Reputation: 317
I'm working on an NFC tag project where I'm encoding a public ID onto the tags. A key requirement is to protect this ID with a password, preventing unauthorized rewrites. I've built a helper class to manage passwords, but I'm facing a few issues:
Password Reset: I can set and remove passwords using my helper class. However, after setting a password, I can't reset it using an external NFC tool (like NFC Tools), even when I provide the correct password. I can remove the password using my own helper class.
Password Detection: My helper function for checking if a password is already set on a tag always returns false, regardless of whether a password actually exists.
I've included my code below and need help troubleshooting these problems so I can reliably manage passwords on my NFC tags, both within my application and using standard NFC tools.
fun isPasswordProtected(tag: Tag): Boolean {
val nfcA = NfcA.get(tag)
try {
nfcA.connect()
// Read page 41 (0x29)
val response = nfcA.transceive(byteArrayOf(0x30, 0x29))
// Get the AUTH0 byte (first byte of the response)
val auth0 = response[0]
// Password protection is enabled if AUTH0 is less than 0xFF
return auth0 < 0xFF.toByte()
} catch (e: IOException) {
LogUtil.log(LogUtil.TAG_NFC, "IOException while reading NFC-A tag -> ${e.message}")
return false // Or handle the exception as needed
} finally {
try {
nfcA.close()
} catch (e: IOException) {
LogUtil.log(LogUtil.TAG_NFC,"IOException while closing NFC-A tag -> ${e.message}")
}
}
}
fun writeProtectWithPassword(tag: Tag, password: String, pack: String) {
val nfcA = NfcA.get(tag)
try {
nfcA.connect()
val passwordBytes = password.toByteArray(Charset.forName("US-ASCII"))
val packBytes = pack.toByteArray(Charset.forName("US-ASCII"))
// Write password to page 43 (0x2B)
nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x2B, passwordBytes[0], passwordBytes[1], passwordBytes[2], passwordBytes[3]))
// Write PACK to page 44 (0x2C)
nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x2C, packBytes[0], packBytes[1], 0x00, 0x00))
// Set AUTH0 to page 9 (0x09) to protect from page 9 onwards
nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x29, 0x09, 0x00, 0x00, 0x00))
// (Optional) Set PROT bit in page 42 (0x2A) to 1 for read/write protection
nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x2A, 0x80.toByte(), 0x00, 0x00, 0x00))
} catch (e: IOException) {
LogUtil.log(LogUtil.TAG_NFC,"IOException while writing to NFC-A tag -> ${e.message}")
} finally {
try {
nfcA.close()
} catch (e: IOException) {
LogUtil.log(LogUtil.TAG_NFC,"IOException while closing NFC-A tag -> ${e.message}")
}
}
}
fun removePasswordProtection(tag: Tag, password: String) {
val nfcA = NfcA.get(tag)
try {
nfcA.connect()
val passwordBytes = password.toByteArray(Charset.forName("US-ASCII"))
// Authenticate with the current password
nfcA.transceive(byteArrayOf(0x1B, passwordBytes[0], passwordBytes[1], passwordBytes[2], passwordBytes[3]))
val result = nfcA.transceive(byteArrayOf(0x30, 0x29))
// Set AUTH0 to 0xFF to disable password protection
nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x29, result[0], result[1], result[2], 0xFF.toByte()))
} catch (e: IOException) {
LogUtil.log(LogUtil.TAG_NFC, "IOException while writing to NFC-A tag -> ${e.message}")
} finally {
try {
nfcA.close()
} catch (e: IOException) {
LogUtil.log(LogUtil.TAG_NFC,"IOException while closing NFC-A tag -> ${e.message}")
}
}
}
Upvotes: 0
Views: 45
Reputation: 10232
Update
There are multiple reasons:-
First Reason
I missed that you are also seem to setting read and write protection to be on (not just the default write protection)
Thus you've made it logically impossible to check "If password protection is on or not" with this type of test as you cannot read the config page because first you will need to authenticate first to read it. So you will already know that it is password protected because you would have successfully authenticated first to find that out.
You can only check the negative without first authenticating to see if the value of page 41 byte 3 is 0xFF
The reason why you thought it should be byte 0 to check, is your read operation was probably failing (because the tag was read protected) and probably giving you a 1 byte response which contained a "Not Acknowledged" response.
As mentioned in the first answer version, Always check for a NACK response on any transceive
operation
if (response == null) {
// either NACK or some other error
// Handle problem
} else if ((response.length == 1)
&& ((response[0] & 0x00A) != 0x00A)) {
// NACK response according to Digital Protocol/T2TOP
// Handle problem
} else {
// success (reads return greater than 1 bytes and writes returned an ACK)
}
So you could probably do:-
Second Reason
Why isPasswordProtected
does not work (once you have successfully authenticated)
// Get the AUTH0 byte (first byte of the response)
val auth0 = response[0]
Is wrong, when you read you get 4 pages of 4 bytes each (16 bytes in total)
Auth0 is byte number 3 on the first page.
val auth0 = response[3]
Is the correct byte to check.
See this example of setting Auth0
And in your own code to reset Auth0 you change byte 3 to 0xFF
// Set AUTH0 to 0xFF to disable password protection
nfcA.transceive(byteArrayOf(0xA2.toByte(), 0x29, result[0], result[1], result[2], 0xFF.toByte()))
Second question of "Why Password Reset can be done by NFC Tools"
I've seen another question with the same problem, the conclusion seemed to be that NFC Tools set password function converts the text entered to bytes in a "funny way".
You could use the "Advanced" method in NFC Tools to transceive the correct byte array bypassing it's "funny" text encoding.
Upvotes: 2