nfcmifare

Write to NXP NTAG215 protected


On an NTAG215 card I need to store a decimal value for example 5432.50 and a JWT of approximately 350 characters. Initially I saved both data with NdefMessage but I implemented the authentication mechanism on the card by enabling a password for write operations, using MifareUltralight

The jwt value does not change once initially established, but the numerical value does, in order to decrease monetary balance.

According to the MIFARE Ultralight protocol documentation always writes 1 page (4 bytes). at a time, for example for 5432.50, you have to write on page 4 “5432” and on page 5 “.500” and for the jwt, from page 6 to 94, but there are some pages in that range that they are not for data.

What is the correct way to do this logic?

fun writeAndProtectTag(intent: Intent) {
    Thread(object : Runnable {

        var pwd = "-_bA".toByteArray() //Password has to be 4 characters
        var pack = "cC".toByteArray()  //Password Acknowledge has to be 2 characters

        override fun run() {
            val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
            var mifare: MifareUltralight? = null

            try {
                mifare = MifareUltralight.get(tag)
                mifare.connect()

                while (!mifare.isConnected) {
                }

                var response: ByteArray?

                // Authenticate with the tag first
                try {
                    response = mifare.transceive(
                        byteArrayOf(
                            0x1B.toByte(),  // PWD_AUTH
                            pwd[0], pwd[1], pwd[2], pwd[3]
                        )
                    )
                    // Check if PACK is matching expected PACK
                    if (response != null && response.size >= 2) {
                        val packResponse = Arrays.copyOf(response, 2)
                        if (!(pack[0] == packResponse[0] && pack[1] == packResponse[1])) {
                            Log.d("NFC", "Tag could not be authenticated:\n$packResponse≠$pack")
                        } else {
                            try {
                                mifare.writePage(4, "5432".encodeToByteArray())
                                mifare.writePage(5, ".500".encodeToByteArray())

                                //TODO write jwt
                            } catch (e: UnsupportedEncodingException) {
                                e.printStackTrace()
                            } catch (e: IOException) {
                                e.printStackTrace()
                            }
                        }
                    }
                } catch (e: IOException) {
                    e.printStackTrace()
                }

            } catch (e: IOException) {
                e.printStackTrace()
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }

            try {
                mifare!!.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }).start()
}

Solution

  • I don't quite understand your question but I think you are miss understanding the page numbers.

    An NTAG215 data pages are "04h to 81h for NTAG215" or 4 to 129 in decimal but as the pages start at 0 and it includes the start page, then that give you 126 pages (126 x 4 = 504).

    You jwt needs 350 bytes (88 pages) thus uses up to 94 in decimal or 5Eh which is much lower than the max page you can use of 81h thus you won't come anywhere near the configuration pages starting at 82h.

    I think that you have confused 81h in hex as 81 in decimal and thus you think your jwt data would override the config pages, which it will not.

    Otherwise at a glance the code logic looks OK, just split you jwt bytes in to groups of 4 and write them to incrementing pages numbers from 06h