4

I have implemented the code to interact with an NTAG216 by means of low-level communication methods (following NTAG212 Mifare Ultralight with Authentication and the datasheet of NTAG216).

What I have achieved so far:

  • Set password write protection on NTAG216 if not set or if new/blank tag.
  • If password is already set, authenticate using PWD_AUTH and comparing PACK.
  • Read data.
  • Write/overwrite data.

What I haven't been able to do so far:

  • Detect NDEF messages that I write to the tag in other apps. In other words I'm able to write to the tag using writePage() method and also read tags using readPage(). However, on time of writing pages I convert an NdefMessage into a byte array which can be read as well as written. However, this NDEF message is not detected in other applications.

What do I need to do in order to be able to detect NDEF messages I write from other applications?

Community
  • 1
  • 1
phLamBy
  • 185
  • 2
  • 13

1 Answers1

9

NTAG216 is an NFC Forum Type 2 tag. Consequently, you have to follow the NFC Forum Type 2 Tag Operation specification when writing data to this type of NFC tag.

Therefore, you will need to follow a few rules for a tag to be discoverable as NDEF tag (Type 2 tag):

First, the capability container (located in block 3) needs to be configured.

  • Byte 0 must be set to the "magic" value 0xE1.
  • Byte 1 must be set to 0x10 to indicate mapping version 1.0.
  • Byte 2 must be set to 0x6D to indicate the memory size of NTAG216.
  • Byte 3 can be set to 0x00 to indicate read/write access to the NDEF data or to 0x0F to indicate read-only access (note that these are permissions on the application layer).

So you can write the capability container as:

byte[] response = nfc.transceive(new byte[] {
    (byte)0xA2, // WRITE
    (byte)3,    // block address
    (byte)0xE1, (byte)0x10, (byte)0x6D, (byte)0x00
});

NTAG216 already ships with a properly configured capability container so there is no need to do this by hand. Also note that block 3 is one-time-programmable, which means that bits can only be set to one but cannot be cleared to zero again. So if you did already overwrite the capability container with a different value, the tag can most likely no longer be used as an NDEF tag.

The data must be written to the data blocks starting at block 4. NDEF messages must be wrapped into an NDEF message TLV (tag-length-value) structure. The tag for this TLV is 0x03. The length can be either in one-byte format (for NDEF messages with a length between 0 and 254 bytes) or in three-byte format (for NDEF messages with a length of 255 or more bytes). The data of this TLV block is the actual NDEF message (that you can obtain from ndefMessage.toByteArray()).

For example, for the NDEF message D1 01 0D 55 01 65 78 61 6D 70 6C 65 2E 63 6F 6D 2F (which is a URI record with the URL "http://www.example.com/"), you would get the following TLV structure:

03 11 D1010D55016578616D706C652E636F6D2F

If you have alonger NDEF message (e.g. one with 259 bytes), you would use the three-byte length format:

03 FF0103 D101FF5501...

Further, you should mark the end of the data on the tag with a Terminator TLV (tag 0xFE, no length and data fields):

FE

You could then write this data to the tag as:

byte[] ndefMessage = new byte[] {
    (byte)0xD1, (byte)0x01, (byte)0x0D, (byte)0x55, (byte)0x01, (byte)0x65, (byte)0x78, (byte)0x61, (byte)0x6D, (byte)0x70, (byte)0x6C, (byte)0x65, (byte)0x2E, (byte)0x63, (byte)0x6F, (byte)0x6D, (byte)0x2F
};

// wrap into TLV structure
byte[] tlvEncodedData = null;
if (ndefMessage.length < 255) {
    tlvEncodedData = new byte[ndefMessage.length + 3];
    tlvEncodedData[0] = (byte)0x03;  // NDEF TLV tag
    tlvEncodedData[1] = (byte)(ndefMessage.length & 0x0FF);  // NDEF TLV length (1 byte)
    System.arraycopy(ndefMessage, 0, tlvEncodedData, 2, ndefMessage.length);
    tlvEncodedData[2 + ndefMessage.length] = (byte)0xFE;  // Terminator TLV tag
} else {
    tlvEncodedData = new byte[ndefMessage.length + 5];
    tlvEncodedData[0] = (byte)0x03;  // NDEF TLV tag
    tlvEncodedData[1] = (byte)0xFF;  // NDEF TLV length (3 byte, marker)
    tlvEncodedData[2] = (byte)((ndefMessage.length >>> 8) & 0x0FF);  // NDEF TLV length (3 byte, hi)
    tlvEncodedData[3] = (byte)(ndefMessage.length & 0x0FF);          // NDEF TLV length (3 byte, lo)
    System.arraycopy(ndefMessage, 0, tlvEncodedData, 4, ndefMessage.length);
    tlvEncodedData[4 + ndefMessage.length] = (byte)0xFE;  // Terminator TLV tag
}

// fill up with zeros to block boundary:
tlvEncodedData = Arrays.copyOf(tlvEncodedData, (tlvEncodedData.length / 4 + 1) * 4);
for (int i = 0; i < tlvEncodedData.length; i += 4) {
    byte[] command = new byte[] {
        (byte)0xA2, // WRITE
        (byte)((4 + i / 4) & 0x0FF), // block address
        0, 0, 0, 0
    };
    System.arraycopy(tlvEncodedData, i, command, 2, 4);
    byte[] response = nfc.transceive(command);
}

Finally, be aware that you can't use the tag as NDEF tag if you have read-password set on the NDEF data area since the NFC Forum Type 2 Tag Operation specification requires the tag to be freely readable.

Andrew
  • 8,198
  • 2
  • 15
  • 35
Michael Roland
  • 39,663
  • 10
  • 99
  • 206
  • I'm on travel till the end of February. As soon as I get back to work I'll implement and test it I'll accept your answer . But theoretically you have solved my problem. Thanks. – phLamBy Feb 10 '17 at 08:53
  • Thanks for the patience and efforts to answer my query. I have integrated your code with mine . 1) I authenticated card first 2) written capability container and then wrote my data using the given code. But still i'm not able to detect the Ndef Tag on other applications. my NdefMessage contains two tags First is MIME (package name of application) Second is text record (JSON MESSAGE). – phLamBy Mar 08 '17 at 11:26
  • Part1: -110 34 0 97 112 112 108 105 99 97 116 105 111 110 47 111 114 103 46 111 99 46 111 115 111 46 110 102 99 97 99 116 105 118 105 116 121 81 1 -58 84 2 101 110 47 82 75 103 69 111 80 81 43 104 111 70 70 119 99 74 108 43 82 43 75 84 52 53 98 121 118 55 48 48 69 89 108 56 110 107 87 102 107 48 67 89 56 83 53 86 55 110 99 65 80 89 107 105 51 74 117 101 55 48 73 110 97 47 83 113 105 104 101 53 87 67 52 78 83 43 10 72 75 55 53 77 47 77 120 80 83 78 112 121 105 78 121 71 121 69 103 80 106 56 54 52 74 87 87 78 70 88 85 72 82 83 115 54 78 79 78 53 115 109 66 79 106 67 53 90 78 73 80 107 71 108 – phLamBy Mar 08 '17 at 12:12
  • Part2: 119 97 54 106 43 111 53 65 57 88 120 89 120 86 112 51 51 113 52 70 68 10 66 53 88 106 76 105 109 112 116 56 71 97 99 85 101 89 82 51 65 114 82 79 67 90 76 82 103 56 77 100 48 98 72 68 88 114 72 121 71 72 10 This is the NdefMessage i used. – phLamBy Mar 08 '17 at 12:13
  • @KailashKumar Did you properly wrap the NDEF message into the TLV structure? Did you write the data to the correct blocks? – Michael Roland Mar 11 '17 at 20:15
  • I exactly followed your code as data is less than 255 bytes after writing data i found CC as FF FF FF FF, before writing it showed E1 10 6D 00 and now i'm not even able to write CC as it was before. – phLamBy Mar 14 '17 at 04:25
  • @KailashKumar I'm very sorry for this. Unfortunately, I, indeed, had a bug in the code which caused all the data to be written to the CC block. This, in turn, resulted in the CC block to become FF FF FF FF (which means the tag is no longer usable for NDEF; unfortunately, this condition is irreversible). I've updated the code to properly increase the block counter now. – Michael Roland Mar 14 '17 at 06:42
  • I already had identified and solved that bug but your piece of code is better than mine so i have used your code. and also wasted 4 NFC cards because of that bug .. :). Anyway it solved my problem. Thanks for your time (Y) – phLamBy Mar 14 '17 at 11:40
  • 1
    As found out in this question https://stackoverflow.com/a/74283529/2373819 `D1 01 0C 55 01 65 78 61 6D 70 6C 65 2E 63 6F 6D 2F` is not a valid Ndef Message, the payload length is out by 1, so adjusted answer to message to be correct by change the third byte from `0C` to `0D` e.g. `D1 01 0D 55 01 65 78 61 6D 70 6C 65 2E 63 6F 6D 2F` – Andrew Nov 02 '22 at 10:28