#pragma author WerWolv #pragma description NTAG213/NTAG215/NTAG216, NFC Forum Type 2 Tag compliant IC import std.core; import std.io; using BitfieldOrder = std::core::BitfieldOrder; bitfield AccessCapability { Read : 4; Write : 4; } [[bitfield_order(BitfieldOrder::MostToLeastSignificant, 8)]]; struct CapabilityContainer { u8 magic; u8 version; u8 memorySize; be AccessCapability accessCapability; }; bitfield NDEFFlags { MB : 1; ME : 1; CF : 1; SR : 1; IL : 1; TNF : 3; } [[bitfield_order(BitfieldOrder::MostToLeastSignificant, 8)]]; enum TNFType : u8 { Empty = 0x00, NFCForumWellKnownType = 0x01, MediaType = 0x02, AbsoluteURI = 0x03, NFCForumExternalType = 0x04, Unknown = 0x05, Unchanged = 0x06, Reserved = 0x07 }; struct NDEF { NDEFFlags flags; u8 typeLength; if (flags.SR) u8 payloadLength; else u32 payloadLength; if (flags.IL) u8 idLength; char type[typeLength]; if (flags.IL) u8 id[idLength]; u8 payload[payloadLength]; if (flags.ME) break; }; struct LockControl { u8 dynamicLockByteOffset; u8 numBits; u8 pageControlInfo; }; struct MemoryControl { u8 reservedBytesOffset; u8 numBytes; u8 pageSize; }; struct Length { u8 byte [[hidden, no_unique_address]]; if (byte == 0xFF) u24 length; else u8 length; } [[sealed, transform("transform_length"), format("transform_length")]]; fn transform_length(ref auto length) { return length.length; }; enum Tag : u8 { NULL = 0x00, LockControl = 0x01, MemoryControl = 0x02, NDEFMessage = 0x03, Proprietary = 0xFD, TerminatorTLV = 0xFE }; struct TLV { Tag tag; if (tag == Tag::TerminatorTLV) { break; } else if (tag == Tag::NULL) { // Empty } else { Length length; if (length > 0) { if (tag == Tag::LockControl) { LockControl lockControl; } else if (tag == Tag::MemoryControl) { LockControl lockControl; } else if (tag == Tag::NDEFMessage) { NDEF ndef[while(true)]; } else { u8 value[length]; } } } }; struct ManufacturerData { u8 serial1[3]; u8 checkByte0; u8 serial2[4]; u8 checkByte1; u8 internal; u16 lockBytes; }; struct DynamicLockBytes { u8 bytes[3]; padding[1]; }; bitfield MIRROR { MIRROR_CONF : 2; MIRROR_BYTE : 2; padding : 1; STRG_MOD_EN : 1; padding : 2; } [[bitfield_order(BitfieldOrder::MostToLeastSignificant, 8)]]; bitfield ACCESS { PROT : 1; CFGLCK : 1; padding : 1; NFC_CNT_EN : 1; NFC_CNT_PWD_PROT : 1; AUTHLIM : 3; }; fn validate_ntag_checksum(ManufacturerData mdata) { /* 0x88 is the CT/cascade digit. This is XOR'd with the first 3 bytes of the serial to calculate BCC0. */ u8 bcc0 = 0x88 ^ mdata.serial1[0] ^ mdata.serial1[1] ^ mdata.serial1[2]; if (bcc0 == mdata.checkByte0) { std::print("NTAG BCC0 checksum OK. Value: {}", bcc0); } else { std::warning(std::format("NTAG BCC0 checksum failed! Value: {}", bcc0)); } // BCC1 is the XOR of the last 4 serial bytes. u8 bcc1 = mdata.serial2[0] ^ mdata.serial2[1] ^ mdata.serial2[2] ^ mdata.serial2[3]; if (bcc1 == mdata.checkByte1) { std::print("NTAG BCC1 checksum OK. Value: {}", bcc1); } else { std::warning(std::format("NTAG BCC1 checksum failed! Value: {}", bcc1)); } }; struct Config { MIRROR MIRROR; u8 MIRROR_PAGE; u8 AUTH0; ACCESS ACCESS; u32 PWD; u16 PACK; }; struct NTAG { ManufacturerData manufacturerData; CapabilityContainer cc; TLV tlv[while(true)]; padding[addressof(tlv) + cc.memorySize * 8 - sizeof(tlv)]; DynamicLockBytes dynamicLockBytes; Config config; }; NTAG ntag @ 0x00; validate_ntag_checksum(ntag.manufacturerData);