An overview of the TLV and NDEF formats used to encode NFC data on Mifare Classic cards

Overview

This post's intended audience is those who are familiar with low-level data structures and the basics of NFC and Mifare Classic technologies.

Data are encoded in an NFC-formatted Mifare Classic tag's memory using the TLV (Type-Length-Value) and NDEF (NFC Data Exchange Format) formats. An Adafruit article provides a good overview of NDEF. My overview is below.

Eventually, I will write a Javascript widget that allows one to encode arbitrary data as NDEF and TLV blocks ready to be written to a Mifare Classic card (or the memory of an emulator), but that's out of the scope of this post.

Type-Length-Value

TLV, defined in NXP application note AN1304 is simple: a TLV stream is composed of blocks, each of which has a 1-byte type, a 1- or 3-byte length, and arbitrary-length value. For an NDEF message, the type is 0x03. The length field is the length of the value field. (For example, a value field of 0x12 34 56 has a length field of 0x03.) For lengths of 0x00 to 0xFE, length is encoded as one byte. For lengths of 0xFF to 0xFFFE, length is encoded as three bytes: 0xFF followed by the length as two bytes (big-endian). The value field is data in the NDEF format.

Other TLV values include 0x00, which is a 1-byte null block with no length or value field; 0xFD, which is a proprietary data block; and 0xFE, which is a terminator with no length or value field. All TLV streams must be terminated with an 0xFE byte.

NDEF (NFC Data Exchange Format)

NDEF is defined in the NFC Forum document titled NFC Data Exchange Format Technical Specification or abbreviated as [NFCForum-TS-NDEF_1.0]. An NDEF message is composed of records.

Record header

A record is composed of a header, as follows:

Bit 7 6 5 4 3 2 1 0
Byte
0 Message Begin Message End Chunk Short ID present Type Name Format
1 Type Length
2- Rest of Header

The common values for TNF are 0x00 for an empty payload, 0x01 for an NFC Forum well-known type, and 0x02 for a MIME type.

If the ID Present bit above is set, the next byte is the length of the ID field. Following that is the payload length: 4 bytes (big-endian) if the Short bit is set, 1 byte otherwise. The payload length field does not include the length of the header. Following that are the type, ID (if ID Present is set and the ID Length field is nonzero), and payload.

The Message Begin and End fields indicate whether this record is the first or last of the message, respectively. The Chunk field is set if the payload is chunked (split across multiple records) and the payload is continued beyond this record1.

Text records

The text record type is defined in the NFC Forum document titled Text Record Type Definition Technical Specification or abbreviated as [NFCForum-TS-RTD_Text_1.0]. It has a TNF of 0x01 and a type of 0x54 (ASCII T). Its payload contains a 1-byte header, a variable length IETF BCP 47 language tag, and then the stored text.

The header's MSB is set if the text is encoded in UTF-162 and cleared if the text is encoded in UTF-8; bits 5-0 of the MSB are used to encode (as an unsigned integer) the length of the language tag.

URI records

The URI record type is defined in the NFC Forum document titled URI Record Type Definition Technical Specification or abbreviated as [NFCForum-TS-RTD_URI_1.0]. It has a TNF of 0x01 and a type of 0x55 (ASCII U). Its payload contains a 1-byte prefix3 followed by the rest of the URL. The prefix is decoded as follows:

Prefix byte (hex) Prefix
0x00 Empty
0x01 http://www.
0x02 https://www.
0x03 http://
0x04 https://
0x05 tel:
0x06 mailto:
0x07 ftp://anonymous:anonymous@
0x08 ftp://ftp.
0x09 ftps://
0x0a sftp://
0x0b smb://
0x0c nfs://
0x0d ftp://
0x0e dav://
0x0f news:
0x10 telnet://
0x11 imap:
0x12 rtsp://
0x13 urn:
0x14 pop:
0x15 sip:
0x16 sips:
0x17 tftp:
0x18 btspp://
0x19 btl2cap://
0x1a btgoep://
0x1b tcpobex://
0x1c irdaobex://
0x1d file://
0x1e urn:epc:id:
0x1f urn:epc:tag:
0x20 urn:epc:pat:
0x21 urn:epc:raw:
0x22 urn:epc:
0x23 urn:nfc:
0x24 to 0xFF Reserved

Contact records

The NFC read/write app on my phone (NFC Tools, which is fairly useful for NFC debugging) supports a "Contact" record type, encoded using an NDEF record with TLV of 0x02 (MIME type) and a type field of text/vcard. As indicated by the MIME type, the payload is encoded using the vCard format (Wikipedia article, RFC 6350).

Other records

Among others, the Smart Poster and (Digital) Signature record types are defined in NFC Forum standards [NFCForum-SmartPoster_RTD_1.0] and [RTD_SIG] , respectively; the latter also has a Wikipedia article

Example

Consider a card with sectors

 1-----+-----+-------------------------------------------------+-----------------
 2 sec | blk | data                                            | ascii
 3-----+-----+-------------------------------------------------+-----------------
 4   0 |   0 | E6 84 87 F3 16 88 04 00 46 8E 45 55 4D 70 41 04 | ........F.EUMpA.
 5     |   1 | 73 00 03 E1 03 E1 03 E1 03 E1 03 E1 03 E1 03 E1 | s...............
 6     |   2 | 03 E1 03 E1 03 E1 03 E1 03 E1 03 E1 03 E1 03 E1 | ................
 7     |   3 | A0 A1 A2 A3 A4 A5 FF 07 80 C1 FF FF FF FF FF FF | ................
 8   1 |   4 | 03 2F D1 01 2B 55 04 74 75 63 6B 65 72 2E 74 68 | ./..+U.tucker.th
 9     |   5 | 65 2D 74 77 6F 6D 65 79 73 2E 63 6F 6D 2F 62 6C | e-twomeys.com/bl
10     |   6 | 6F 67 2F 70 6F 73 74 73 2F 6E 64 65 66 2D 74 6C | og/posts/ndef-tl
11     |   7 | D3 F7 D3 F7 D3 F7 FF 07 80 40 FF FF FF FF FF FF | .........@......
12   2 |   8 | 76 FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | v...............
13     |   9 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
14     |  10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
15     |  11 | D3 F7 D3 F7 D3 F7 FF 07 80 40 FF FF FF FF FF FF | .........@......

Sector 0 contains a MAD indicating the presence of NDEF data in all blocks. Blocks 3, 7, and 11 contain key material and can be ignored. Block 4 contains the start of TLV data. The first block is of type 0x03 (NDEF) with length 0x2F. What follows is a NDEF message 0x2F = 47 bytes long. The NDEF message contains one record, with record header 0xD1 01 2B 55 The first byte is 0xD1 = 0b1101 0001, indicating that this is the first and last record (by bits 7 and 6 being set), no chunking is used (by bit 5 being cleared), the length field being one byte (by bit 4 being set), no ID field is present (by bit 3 being cleared), and the record type type field containing an NFC Forum Well-Known type (by bits 2 to 0 being 0x1). The next byte is the type length (0x01), then the payload length (0x2B). There is then a 0x01-byte type of 0x55 (ASCII U), which indicates that what follows is a URL, then 0x2B bytes of data. The first data byte is 0x04, which indicates that https:// is to be prepended to the URL field. Next is the URL (the-twomeys.com/blog/posts/ndef-tlv, which is the URL for this post). Finally, a TLV terminator block (type 0xFE) is present.

Starting with a blank card in emulator memory, the data above could be written to emulator memory (or card memory if hf mf wrbl -a --key D3F7D3F7D3F7 were substituted for hf mf esetblk) by the below commands.

1hf mf esetblk -b 4 -d 032FD1012B55047475636b65722e7468
2hf mf esetblk -b 5 -d 652d74776f6d6579732e636f6d2f626c
3hf mf esetblk -b 6 -d 6f672f706f7374732f6e6465662d746c
4hf mf esetblk -b 8 -d 76FE0000000000000000000000000000

1

When chunking, the payload length of each chunk is the length of that chunk only. The TNF, type length, and type of the first chunk is that of the full record, while the TNF, type length, ID Present, ID, and type fields of subsequent records are 0x06, 0x00, empty, and empty, respectively.

2

Because it seems that the NFC Forum enjoys using its juicy membership fees and technical wizardry to prop up obsolete text encodings.

3

Because apparently the NFC Forum has never considered the phrase "penny wise, pound foolish". That, or they seem to think that NFC tags are too small to fit the text https:// but are big enough to fit the rest of a URL.