Search code examples
androidtagsnfcmifarelockbits

Android: Error when resetting Dynamic locks on NFC NTAG213 (28h)


I am using NFCA.transceive to write to NTAG213 and can successfully write to location 28h that holds the dynamic locks without any problems when the tag is empty. But when I try to write it back to its default state I get a TAG_LOST exception. All other fields reset fine such as the password, AUTH0, etc.

In the specification is says NTAG213 features "tearing" protected write operations to specific memory content and mentions 28h. Is that something to do with it? I did not understand the term "tearing".

I should mention that before I update I used the authentication and this must be working OK as everything apart from this page/location changes back. I have played around with order of writes to no effect.

Relevant code:

public String resetBDtag(Tag tag) {
    NfcA nfca = NfcA.get(tag);

    String resultString = "";

    byte[] result;
    try {

        nfca.connect();
        try {
        result = nfca.transceive(new byte[]{
                (byte)0x1B,  // Command: password Auth
                (byte)0x93, (byte)0xD0, (byte)0x55, (byte)0xB7
        });} catch (IOException e) {
            Log.e(TAG, "IOException while authenticating :" + resultString, e );
            resultString = "Error authenticating"+ e.toString();
        }



        try {
            result = nfca.transceive(new byte[]{
                    (byte) 0xA2,  // Command: WRITE
                    (byte) 0x29,  // Address: page 0x29 (2)

                    (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0xFF  // CF

            });
        } catch (IOException e) {
            Log.e(TAG, "IOException while resting Auth0 requirement :" + resultString, e );
            resultString = "Error removing Authentication requirement"+ e.toString();;
        }

        try {
            result = nfca.transceive(new byte[]{
                    (byte) 0xA2,  // Command: WRITE
                    (byte) 0x2B,  // Address: page 0x2B (2)

                    (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF  // Password
            });

        } catch (IOException e) {
            Log.e(TAG, "IOException while clearing password :" + resultString, e );
            resultString = "Error clearing password"+ e.toString();;
        }
        try {
            result = nfca.transceive(new byte[]{
                    (byte) 0xA2,  // Command: WRITE
                    (byte) 0x10,  // Address: page 0x10 (2)

                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00  // Status / Count
            });
        } catch (IOException e) {
            Log.e(TAG, "IOException while clearing the status data :" + resultString, e );
            resultString = "Error clearing status data"+ e.toString();;
        }
        try {
            // This does not work!
            result = nfca.transceive(new byte[]{
                    (byte) 0xA2,  // Command: WRITE
                    (byte) 0x28,  // CFG1

                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xBD  // reset CFG1

            });
        } catch (IOException e) {
            Log.e(TAG, "IOException while removing locked pages 18-19 :" + resultString, e );
            resultString = "Error removing locks"+ e.toString();;
        }


    } catch (IOException e) {
        Log.e(TAG, "IOException while writing MifareUltralight :" + resultString, e );
        resultString = "Can not speak to the tag!";
    } finally {
        if (nfca != null) {
            try {
                nfca.close();
                Log.d("finally","isoDep closed");
            }
            catch (IOException e) {
                Log.e(TAG, "Error closing tag...", e);
                resultString = "Can not close the connection to the  tag!";
            }
        }
    }
    return resultString;
}

Solution

  • Regarding your write command for the dynamic lock bits

    You try to write to byte 3 (i.e. byte 3 has a value other than 0x00 in your write command). You should not do that. The datasheet clearly states that you should set all RFUI bits to 0 (which applies to byte 3, as well as to some bits in bytes 1 and 2).

    How the write command works for one-time programmable memory areas like the dynamic lock bits

    The write command will do a logical OR between the current value of the page and the new value sent in the write command parameter. For instance, if the page is currently set to FF 0F 00 BD and you try to write 00 00 3F 00, the new value for the page will be FF 0F 3F BD. Hence, you can only set bits (= set to 1) in the dynamic lock bits but you can never reset them to zero.

    Why your write command fails

    You set the RFUI byte (byte 3) to 0xBD in your write command. This is not allowed.

    What is "tearing" protection?

    Tearing is when you remove (accidental or intentional) the tag from the reader during a write operation. This removal during a write could result in a partial write of a page, which would leave the memory in an undefined state. Tearing protection means that the tag will either fully complete the write or will not perform the write at all. Hence, the memory won't get into any unexpected state.