From 4a276dfcbc43fa1d91df0c19edf7a2d0e93868c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=90=E6=AD=AA?= Date: Sun, 30 Aug 2026 03:39:19 +0800 Subject: [PATCH] docs: content clean up --- client/src/cmdhfmfu.c | 220 ++++------------------------------- doc/mfu_ndef_format_notes.md | 102 ++++++++++++++++ 2 files changed, 127 insertions(+), 195 deletions(-) create mode 100644 doc/mfu_ndef_format_notes.md diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index a2eee5ba4..2c1a443cd 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -7829,16 +7829,9 @@ int CmdHF14MfuNDEFWrite(const char *Cmd) { return PM3_SUCCESS; } -// --------------------------------------------------------------------------- -// NDEF formatting -// -// Page 03h is the OTP Capability Container (CC): once written, it cannot be -// changed. Refuse unknown types to avoid a permanent wrong CC. -// Restore NXP's delivery content for pages 03h-05h. NTAG212/213/213F/213TT -// include a Lock Control TLV before the NDEF TLV; other types do not, so -// content is copied per type. -// Only NTAG21x ships with a CC. MF0ICU1/2, MF0ULx1 and NTAG203 leave 03h -// blank; derive their CC from the user memory range and use an empty NDEF. +// NDEF formatting - restores the NXP factory delivery content (Capability +// Container + empty NDEF message) for the detected tag type. Block 3 is OTP. +// Per-type sources and rationale: doc/mfu_ndef_format_notes.md typedef struct { uint64_t tagtype; const char *name; @@ -7846,167 +7839,21 @@ typedef struct { } mfu_ndef_format_t; static const mfu_ndef_format_t mfu_ndef_format_table[] = { - - // MIFARE Ultralight MF0ICU1 MLEN 06h = 48 bytes, user memory pages 04h-0Fh - // https://www.nxp.com/docs/en/data-sheet/MF0ICU1.pdf rev 3.9 - 23 July 2014 - // memory organization ........... section 7.5, Table 5, page 10 of 31 - // data pages .................... section 7.5, Table 5, page 10 of 31 - // page 03h is OTP, no CC at delivery - CC derived from the page range - { - MFU_TT_UL, "MIFARE Ultralight", - {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, - - // MIFARE Ultralight C MF0ICU2 MLEN 12h = 144 bytes, user memory pages 04h-27h - // https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf rev 3.5 - 30 January 2026 - // memory organization ........... section 7.5, Table 5, page 8 of 35 - // data pages .................... section 7.5, Table 5, page 8 of 35 - // OTP preset to all 0 ........... section 7.5.4, page 11 of 35 - // CC derived from the page range. The data area ends at page 27h, right before - // the lock bytes at page 28h, so no Lock Control TLV is needed. - { - MFU_TT_UL_C, "MIFARE Ultralight C", - {{0xE1, 0x10, 0x12, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, - - // MIFARE Ultralight EV1 48 MF0UL11 MLEN 06h = 48 bytes, user memory pages 04h-0Fh - // https://www.nxp.com/docs/en/data-sheet/MF0ULX1.pdf rev 3.3 - 9 April 2019 - // memory organization ........... section 8.5, Fig 5, page 10 of 45 - // data pages .................... section 8.5.5, page 14 of 45 - // OTP default 00 00 00 00h ...... section 8.5.4, page 13-14 of 45 - // CC derived from the page range. - { - MFU_TT_UL_EV1_48, "MIFARE Ultralight EV1 48", - {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, - - // MIFARE Ultralight EV1 128 MF0UL21 MLEN 10h = 128 bytes, user memory pages 04h-23h - // https://www.nxp.com/docs/en/data-sheet/MF0ULX1.pdf rev 3.3 - 9 April 2019 - // memory organization ........... section 8.5, Fig 6, page 11 of 45 - // data pages .................... section 8.5.5, page 14 of 45 - // OTP default 00 00 00 00h ...... section 8.5.4, page 13-14 of 45 - // CC derived from the page range. - { - MFU_TT_UL_EV1_128, "MIFARE Ultralight EV1 128", - {{0xE1, 0x10, 0x10, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, - - // NTAG203 NT2H0301 MLEN 12h = 144 bytes, user memory pages 04h-27h - // https://www.nxp.com/docs/en/data-sheet/NTAG203.pdf rev 3.0 - 17 October 2011 - // memory organization ........... section 8.5, Table 5, page 10 of 30 - // data pages .................... section 8.5.4, page 13 of 30 - // OTP preset to all 0 ........... section 8.5.3, page 13 of 30 - // CC derived from the page range: page 03h is a plain OTP page here, not a - // Capability Container, so there is no delivery value to copy. Its dynamic - // lock bytes at page 28h use 4 page granularity (section 8.5.2, Fig 7, page - // 12 of 30) against 2 for NTAG213, so that Lock Control TLV would misdescribe - // this part - the plain empty NDEF message is written instead. - { - MFU_TT_NTAG_203, "NTAG203", - {{0xE1, 0x10, 0x12, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, - - // NTAG210 NT2H1011 MLEN 06h = 48 bytes, user memory pages 04h-0Fh - // https://www.nxp.com/docs/en/data-sheet/NTAG210_212.pdf rev 3.0 - 14 March 2013 - // memory organization ........... section 8.5, Fig 4, page 10 of 46 - // data pages .................... section 8.5.5, page 13 of 46 - // content at delivery ........... section 8.5.6, Table 4, page 14 of 46 - { - MFU_TT_NTAG_210, "NTAG210", - {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, - - // NTAG210u NT2L1001 / NT2H1001 MLEN 06h = 48 bytes, user memory blocks 04h-0Fh - // https://www.nxp.com/docs/en/data-sheet/NT2L1001_NT2H1001.pdf rev 3.0 - 7 September 2016 - // data blocks ................... section 9.5.4, page 11 of 32 - // content at delivery ........... section 9.5.5, Table 4, page 11 of 32 - { - MFU_TT_NTAG_210u, "NTAG210u", - {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, - - // NTAG212 NT2L1211 MLEN 10h = 128 bytes, user memory pages 04h-23h - // https://www.nxp.com/docs/en/data-sheet/NTAG210_212.pdf rev 3.0 - 14 March 2013 - // memory organization ........... section 8.5, Fig 5, page 10 of 46 - // data pages .................... section 8.5.5, page 13 of 46 - // content at delivery ........... section 8.5.6, Table 5, page 14 of 46 - { - MFU_TT_NTAG_212, "NTAG212", - {{0xE1, 0x10, 0x10, 0x00}, {0x01, 0x03, 0x90, 0x0A}, {0x34, 0x03, 0x00, 0xFE}} - }, - - // NTAG213 NT2H1311 MLEN 12h = 144 bytes, user memory pages 04h-27h - // https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf rev 3.2 - 2 June 2015 - // memory organization ........... section 8.5, Fig 5, page 11 of 60 - // data pages .................... section 8.5.5, page 16 of 60 - // content at delivery ........... section 8.5.6, Table 5, page 17 of 60 - { - MFU_TT_NTAG_213, "NTAG213", - {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} - }, - - // NTAG213F NT2H1311F MLEN 12h = 144 bytes, user memory pages 04h-27h - // https://www.nxp.com/docs/en/data-sheet/NTAG213F_216F.pdf rev 3.6 - 28 September 2015 - // memory organization ........... section 8.5, Fig 6, page 12 of 55 - // data pages .................... section 8.5.5, page 16 of 55 - // content at delivery ........... section 8.5.6, Table 5, page 17 of 55 - { - MFU_TT_NTAG_213_F, "NTAG213F", - {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} - }, - - // NTAG213TT NT2H1311TT MLEN 12h = 144 bytes, user memory pages 04h-27h - // https://www.nxp.com/docs/en/data-sheet/NT2H1311TT.pdf rev 1.1 - 28 March 2017 - // memory organization ........... section 8.5, Fig 4, page 11 of 57 - // data pages .................... section 8.5.5, page 14 of 57 - // content at delivery ........... section 8.5.6, Table 5, page 15 of 57 - { - MFU_TT_NTAG_213_TT, "NTAG213TT", - {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} - }, - - // NTAG213C NT2H1311C1DTL MLEN 12h = 144 bytes, user memory pages 04h-27h - // NO DATA SHEET. NXP publishes nothing for this part number and no other - // public documentation could be found. - // Most data come from commit ad19f8384 (2020-09-26, "add accurate detection for - // NT2H1311C1DTL") - { - MFU_TT_NTAG_213_C, "NTAG213C", - {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} - }, - - // NTAG215 NT2H1511 MLEN 3Eh = 496 bytes, user memory pages 04h-81h - // https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf rev 3.2 - 2 June 2015 - // memory organization ........... section 8.5, Fig 6, page 11 of 60 - // data pages .................... section 8.5.5, page 16 of 60 - // content at delivery ........... section 8.5.6, Table 6, page 17 of 60 - // The factory MLEN announces 496 bytes while the user memory holds 504. The - // data sheet gives no reason for the 8 byte difference - { - MFU_TT_NTAG_215, "NTAG215", - {{0xE1, 0x10, 0x3E, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, - - // NTAG216 NT2H1611 MLEN 6Dh = 872 bytes, user memory pages 04h-E1h - // https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf rev 3.2 - 2 June 2015 - // memory organization ........... section 8.5, Fig 7, page 12 of 60 - // data pages .................... section 8.5.5, page 16 of 60 - // content at delivery ........... section 8.5.6, Table 7, page 17 of 60 - // Announces 872 bytes against 888 of user memory. - { - MFU_TT_NTAG_216, "NTAG216", - {{0xE1, 0x10, 0x6D, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, - - // NTAG216F NT2H1611F MLEN 6Dh = 872 bytes, user memory pages 04h-E1h - // https://www.nxp.com/docs/en/data-sheet/NTAG213F_216F.pdf rev 3.6 - 28 September 2015 - // memory organization ........... section 8.5, Fig 7, page 12 of 55 - // data pages .................... section 8.5.5, page 16 of 55 - // content at delivery ........... section 8.5.6, Table 6, page 17 of 55 - { - MFU_TT_NTAG_216_F, "NTAG216F", - {{0xE1, 0x10, 0x6D, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} - }, + { MFU_TT_UL, "MIFARE Ultralight", {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, + { MFU_TT_UL_C, "MIFARE Ultralight C", {{0xE1, 0x10, 0x12, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, + { MFU_TT_UL_EV1_48, "MIFARE Ultralight EV1 48", {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, + { MFU_TT_UL_EV1_128, "MIFARE Ultralight EV1 128", {{0xE1, 0x10, 0x10, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, + { MFU_TT_NTAG_203, "NTAG203", {{0xE1, 0x10, 0x12, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, + { MFU_TT_NTAG_210, "NTAG210", {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, + { MFU_TT_NTAG_210u, "NTAG210u", {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, + { MFU_TT_NTAG_212, "NTAG212", {{0xE1, 0x10, 0x10, 0x00}, {0x01, 0x03, 0x90, 0x0A}, {0x34, 0x03, 0x00, 0xFE}} }, + { MFU_TT_NTAG_213, "NTAG213", {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} }, + { MFU_TT_NTAG_213_F, "NTAG213F", {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} }, + { MFU_TT_NTAG_213_TT, "NTAG213TT", {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} }, + { MFU_TT_NTAG_213_C, "NTAG213C", {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} }, + { MFU_TT_NTAG_215, "NTAG215", {{0xE1, 0x10, 0x3E, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, + { MFU_TT_NTAG_216, "NTAG216", {{0xE1, 0x10, 0x6D, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, + { MFU_TT_NTAG_216_F, "NTAG216F", {{0xE1, 0x10, 0x6D, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} }, }; static const mfu_ndef_format_t *mfu_get_ndef_format(uint64_t tagtype) { @@ -8036,7 +7883,7 @@ int CmdHF14AMfUFormat(const char *Cmd) { "hf mfu format -k FFFFFFFF\n" "hf mfu format -k 49454D4B41455242214E4143554F5946\n" "hf mfu format -d E1101200 --force" - ); + ); void *argtable[] = { arg_param_begin, @@ -8134,10 +7981,7 @@ int CmdHF14AMfUFormat(const char *Cmd) { if (cc_len == MFU_BLOCK_SIZE) { - // A hand written Capability Container still must not announce more NDEF - // area than the tag physically holds. Block 3 is OTP, so an over reporting - // MLEN can never be taken back, and ndefwrite would then clamp against the - // whole chip - lock bytes, configuration and key pages included. + // -d must not announce more memory than this tag type actually has if ((fmt != NULL) && (cc_override[2] > fmt->page[0][2]) && (force == false)) { PrintAndLogEx(FAILED, "Capability Container announces more memory than this tag has"); PrintAndLogEx(INFO, " requested... %d bytes ( MLEN %02X )", cc_override[2] * 8, cc_override[2]); @@ -8194,9 +8038,7 @@ int CmdHF14AMfUFormat(const char *Cmd) { return PM3_ESOFT; } - // Block 3 is OTP: a WRITE is OR'ed with what is already there and a bit that - // is set can never be cleared. Anything the OR can not produce is refused - // before a single byte goes to the tag. + // block 3 is OTP: refuse a target the current content can't reach via OR uint8_t *cur = data + (MFU_NDEF_CC_BLOCK * MFU_BLOCK_SIZE); bool blank = true; bool reachable = true; @@ -8239,20 +8081,13 @@ int CmdHF14AMfUFormat(const char *Cmd) { } } - // The erase range is derived from the MLEN in the table, never from -d, so a - // hand written Capability Container can not push it past the user memory. - // Every table entry announces at most the user memory of that type, which - // keeps the last block below the lock bytes and configuration pages. + // erase range from the table MLEN, never from -d - can't run past user memory uint16_t last_block = MFU_NDEF_CC_BLOCK + 2; if (erase && (fmt != NULL)) { last_block = (uint16_t)(MFU_NDEF_CC_BLOCK + (fmt->page[0][2] * 2)); } - // WRITE addresses blocks with a single byte, so block 255 is the last one - // reachable - the same limit MFU_NDEF_MAX_BYTES encodes for ndefread/ndefwrite. - // No current table entry comes close, but mfu_write_block takes a uint8_t and - // the cast below would silently wrap a larger block number back onto the UID - // and lock pages, so clamp here rather than rely on the table staying small. + // block 255 is the last one WRITE can reach with its single address byte if (last_block > 0xFF) { PrintAndLogEx(INFO, "Data area runs past block 255, stopping at the last addressable block"); last_block = 0xFF; @@ -8314,10 +8149,7 @@ int CmdHF14AMfUFormat(const char *Cmd) { DropField(); PrintAndLogEx(NORMAL, ""); - // Block 3 is one time programmable, so there is exactly one chance to get it - // right. A tag can ACK a WRITE and still not commit the page - a weak field, a - // tear, or a block locking bit that was already set - and the write status - // alone would not show it. Read the formatted blocks back rather than trust it. + // read the formatted blocks back rather than trust the write status alone if (ul_auth_select(&card, tagtype, (has_auth_key || has_pwd), auth_key_ptr, pack, sizeof(pack), use_schann) == PM3_ESOFT) { PrintAndLogEx(WARNING, "Wrote the tag but could not re-select it to verify"); PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu ndefread") "` to check it yourself"); @@ -8345,9 +8177,7 @@ int CmdHF14AMfUFormat(const char *Cmd) { if (memcmp(verify + MFU_BLOCK_SIZE, pages[1], 2 * MFU_BLOCK_SIZE) != 0) { PrintAndLogEx(FAILED, "Capability Container is correct but the empty NDEF message is not"); - // pages[1] and pages[2] are contiguous, and so are the two blocks in - // verify, so each side prints in a single call - sprint_hex_inrow hands - // back one shared static buffer, so two calls per line would alias. + // one sprint_hex_inrow() call per line: it returns a shared static buffer PrintAndLogEx(INFO, " wanted...... " _GREEN_("%s"), sprint_hex_inrow(pages[1], 2 * MFU_BLOCK_SIZE)); PrintAndLogEx(INFO, " on tag now.. " _RED_("%s"), sprint_hex_inrow(verify + MFU_BLOCK_SIZE, 2 * MFU_BLOCK_SIZE)); PrintAndLogEx(HINT, "Hint: these blocks are ordinary user memory, check the lock bytes"); diff --git a/doc/mfu_ndef_format_notes.md b/doc/mfu_ndef_format_notes.md new file mode 100644 index 000000000..d4a79a86e --- /dev/null +++ b/doc/mfu_ndef_format_notes.md @@ -0,0 +1,102 @@ +# Notes on `hf mfu format` + + +# Table of Contents +- [Notes on hf mfu format](#notes-on-hf-mfu-format) +- [Table of Contents](#table-of-contents) + - [Why the command exists](#why-the-command-exists) + - [Capability Container basics](#capability-container-basics) + - [Per-type delivery content](#per-type-delivery-content) + - [Lock Control TLV](#lock-control-tlv) + - [NTAG215 / NTAG216 under-reporting](#ntag215--ntag216-under-reporting) + - [NTAG213C](#ntag213c) + +## Why the command exists +^[Top](#top) + +`hf mfu ndefwrite` refuses a tag with no Capability Container (CC). Before this command the only +way to add one was a hand computed `hf mfu wrbl -b 3 -d ` — block 3 is One Time Programmable +(OTP), so a wrong value is permanent. + +`hf mfu format` writes the NXP factory delivery content (CC + an empty NDEF message) for the +detected tag type, restoring what the tag looked like before anything was written to it. + +## Capability Container basics +^[Top](#top) + +Page 3 (`E1 10 00`) is OTP on every type below: a WRITE is bit-wise OR'ed with the current +content, so a bit already set to 1 can never be cleared again. `MLEN * 8` is the size of the NDEF +data area in bytes. + +Consequences for the implementation, in `mfu_get_ndef_format()` / `CmdHF14AMfUFormat()` in +`client/src/cmdhfmfu.c`: + +- an unknown tag type is refused rather than guessed at +- a target CC that the current OTP content cannot reach (checked with the same bit-wise OR) is + refused before anything is written +- `-d` on a *known* type is capped at that type's own MLEN unless `--force` is given, so a typo + cannot silently announce more memory than the tag holds +- `--erase`'s end block is derived from the table's MLEN, never from `-d`, so it cannot run past + the user memory into the lock bytes or configuration pages +- after writing, the command re-selects and reads blocks 3-5 back to confirm the OTP write + actually took — a tag can ACK a WRITE and still not commit the page (weak field, tearing, a lock + bit already set) + +## Per-type delivery content +^[Top](#top) + +Only the NTAG21x family ships with a CC at all. UL / UL-C / UL EV1 and NTAG203 leave page 3 blank +at delivery, so their CC is derived from the user memory range instead of copied from a data +sheet. + +| Type | Part | MLEN | User memory | Data sheet | +|---|---|---|---|---| +| MIFARE Ultralight | MF0ICU1 | 06h (48B) | pages 04h-0Fh | [MF0ICU1.pdf](https://www.nxp.com/docs/en/data-sheet/MF0ICU1.pdf) rev 3.9, §7.5 Table 5 (p.10/31, OTP blank) | +| MIFARE Ultralight C | MF0ICU2 | 12h (144B) | pages 04h-27h | [MF0ICU2.pdf](https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf) rev 3.5, §7.5 Table 5 (p.8/35), §7.5.4 (p.11/35, OTP blank) | +| MIFARE Ultralight EV1 48 | MF0UL11 | 06h (48B) | pages 04h-0Fh | [MF0ULX1.pdf](https://www.nxp.com/docs/en/data-sheet/MF0ULX1.pdf) rev 3.3, §8.5 Fig 5 (p.10/45), §8.5.4 (p.13-14/45, OTP blank) | +| MIFARE Ultralight EV1 128 | MF0UL21 | 10h (128B) | pages 04h-23h | [MF0ULX1.pdf](https://www.nxp.com/docs/en/data-sheet/MF0ULX1.pdf) rev 3.3, §8.5 Fig 6 (p.11/45), §8.5.4 (p.13-14/45, OTP blank) | +| NTAG203 | NT2H0301 | 12h (144B) | pages 04h-27h | [NTAG203.pdf](https://www.nxp.com/docs/en/data-sheet/NTAG203.pdf) rev 3.0, §8.5 Table 5 (p.10/30), §8.5.3 (p.13/30, OTP blank) | +| NTAG210 | NT2H1011 | 06h (48B) | pages 04h-0Fh | [NTAG210_212.pdf](https://www.nxp.com/docs/en/data-sheet/NTAG210_212.pdf) rev 3.0, §8.5.6 Table 4 (p.14/46) | +| NTAG210u | NT2L1001 / NT2H1001 | 06h (48B) | pages 04h-0Fh | [NT2L1001_NT2H1001.pdf](https://www.nxp.com/docs/en/data-sheet/NT2L1001_NT2H1001.pdf) rev 3.0, §9.5.5 Table 4 (p.11/32) | +| NTAG212 | NT2L1211 | 10h (128B) | pages 04h-23h | [NTAG210_212.pdf](https://www.nxp.com/docs/en/data-sheet/NTAG210_212.pdf) rev 3.0, §8.5.6 Table 5 (p.14/46) | +| NTAG213 | NT2H1311 | 12h (144B) | pages 04h-27h | [NTAG213_215_216.pdf](https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf) rev 3.2, §8.5.6 Table 5 (p.17/60) | +| NTAG213F | NT2H1311F | 12h (144B) | pages 04h-27h | [NTAG213F_216F.pdf](https://www.nxp.com/docs/en/data-sheet/NTAG213F_216F.pdf) rev 3.6, §8.5.6 Table 5 (p.17/55) | +| NTAG213TT | NT2H1311TT | 12h (144B) | pages 04h-27h | [NT2H1311TT.pdf](https://www.nxp.com/docs/en/data-sheet/NT2H1311TT.pdf) rev 1.1, §8.5.6 Table 5 (p.15/57) | +| NTAG213C | NT2H1311C1DTL | 12h (144B) | pages 04h-27h | none — see [NTAG213C](#ntag213c) | +| NTAG215 | NT2H1511 | 3Eh (496B) | pages 04h-81h | [NTAG213_215_216.pdf](https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf) rev 3.2, §8.5.6 Table 6 (p.17/60) | +| NTAG216 | NT2H1611 | 6Dh (872B) | pages 04h-E1h | [NTAG213_215_216.pdf](https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf) rev 3.2, §8.5.6 Table 7 (p.17/60) | +| NTAG216F | NT2H1611F | 6Dh (872B) | pages 04h-E1h | [NTAG213F_216F.pdf](https://www.nxp.com/docs/en/data-sheet/NTAG213F_216F.pdf) rev 3.6, §8.5.6 Table 6 (p.17/55) | + +## Lock Control TLV +^[Top](#top) + +Standards ref: NFC Forum Type 2 Tag Operation, and the Capability Container layout in the data +sheets cited above. + +NTAG212, NTAG213, NTAG213F and NTAG213TT are delivered with a 5 byte Lock Control TLV ahead of the +NDEF TLV (`01 03 ...`). The other types in the table +are not. This TLV tells an NFC device where the *dynamic* lock bytes live so it can lock the tag +read-only — on these parts the dynamic lock bytes sit just past the user memory (e.g. NTAG213 at +page 40h, right after the page 04h-27h data area), so the TLV exists purely for that use case, not +because a writer needs to avoid overwriting anything. + +`hf mfu format` copies this TLV verbatim from the factory content; it does not construct one. +`hf mfu ndefwrite` preserves any control TLV (type `01` or `02`) it finds ahead of the NDEF TLV +before overwriting the data area — see the code for the exact scan. + +## NTAG215 / NTAG216 under-reporting +^[Top](#top) + +The factory MLEN announces less than the physical user memory: NTAG215 announces 496 bytes of a +504 byte area, NTAG216 announces 872 of 888. The data sheet does not explain the 8/16 byte gap. +The NXP value is used as-is rather than corrected upward, since under-reporting can never let a +write run past the user memory while a larger value could. + +## NTAG213C +^[Top](#top) + +NXP publishes no data sheet for this part number (NT2H1311C1DTL) and none could be found anywhere +else. It was added to the client in commit `ad19f8384` (2020-09-26, "add accurate detection for +NT2H1311C1DTL") from an observed tag's `GET_VERSION` response, which differs from a plain NTAG213 +only in the minor product version byte (`01h` vs `00h`); the storage size byte — the one that +encodes the 144 byte user memory — is identical. The NTAG213 content is used on that basis.