Search code examples
nfcmifarendefacr122smartcardio

Trouble writing NDEF record to NTAG213 using external NFC reader (but writing to memory works)


I am using the sample provided by Michael Roland in this answer and modified the bytes command structure to match this answer.

After I scan the tag, I receive 90 00 responses from the reader. When I then scan the tag using NFC Tools though, I don't see that it has an NDEF record (photo). If I examine the memory I can see my data written starting at block 4 as follows here.

Meanwhile, if I use the Write Tag feature of NFC Tools to write an NDEF message and then scan the tag again, it does work. The memory in the blocks other than those starting at block 4 appear to be identical (photo).

I don't believe it's a capability container issue as the memory is identical in block 3 after writing to the tag from my reader vs. NFC Tools.

Do I need to do any other kind of NDEF read / check command prior to writing to block 4?

My code below:

                         byte[] ndefMessage = new byte[] {
                                (byte)0xD1, (byte)0x01, (byte)0x0C, (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;
                        Log.e("length",String.valueOf(ndefMessage.length));
                        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)0xFF, // WRITE
                                    (byte)0xD6,
                                    (byte)0x00,
                                    (byte)((4 + i / 4) & 0x0FF), // block address
                                    (byte)0x04,
                                    0, 0, 0, 0
                            };

                            System.arraycopy(tlvEncodedData, i, command, 5, 4);

                            ResponseAPDU answer = cardChannel.transmit(new CommandAPDU(command));
                            byte[] response = answer.getBytes();
                            writeLogWindow("response: "+ byteArrayToHexString(response));
                        }

Solution

  • I believe that the problem is that Michael Roland's answer has a bug in it.

    D1 01 0C 55 01 65 78 61 6D 70 6C 65 2E 63 6F 6D 2F is not a valid Ndef message.

    If you look at the various specs for Ndef at https://github.com/haldean/ndef/tree/master/docs (specifically the NFCForum-TS-RTD_URI_1.0.pdf and NFCForum-TS-NDEF_1.0.pdf) his example of "http://www.example.com/" is actually made up of "http://www." which has a type code of 01 and 12 characters or the rest of the URL.

    Thus the payload length is 13 (1 + 12) bytes so OD where as his message:-

    D1 01 0C 55 01 65 78 61 6D 70 6C 65 2E 63 6F 6D 2F

    only specifies the length of the second part of the URL and not the prefix, so is one byte too short.

    This is confirmed if you try and write a record for that URL using the NFC Tools App or NXP's TagWriter App both generate a message of

    D1 01 0D 55 01 65 78 61 6D 70 6C 65 2E 63 6F 6D 2F

    So try using in your code

    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
    };