Add support for Ultralight NTAG213/215/216

This commit is contained in:
Adam Laurie 2018-09-15 16:19:35 +01:00
parent 216145fba5
commit 62f27c1c58
2 changed files with 138 additions and 17 deletions

View file

@ -9,7 +9,7 @@
* Copyright (C) 2012-2013 Ludovic Rousseau
* See AUTHORS file for a more comprehensive list of contributors.
* Additional contributors of this file:
* Copyright (C) 2017 Adam Laurie
* Copyright (C) 2017-2018 Adam Laurie
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -167,10 +167,62 @@ typedef struct {
uint8_t dummy[12];
} mifareul_block_config21B;
// MIFARE NTAG21[3/5/6] Manufacturer Pages
typedef struct {
uint8_t sn0[4];
} mifarentag_block_manuf21356A;
typedef struct {
uint8_t sn1[4];
} mifarentag_block_manuf21356B;
typedef struct {
uint8_t sn2;
uint8_t internal;
uint8_t lock[2];
} mifarentag_block_manuf21356C;
typedef struct {
uint8_t cc[4];
} mifarentag_block_manuf21356D;
// MIFARE NTAG21[3/5/6] Config Pages
typedef struct {
uint8_t dynlock[3];
uint8_t rfui0;
} mifarentag_block_config21356A;
typedef struct {
uint8_t cfg0[4];
} mifarentag_block_config21356B;
typedef struct {
uint8_t cfg1[4];
} mifarentag_block_config21356C;
typedef struct {
uint8_t pwd[4];
} mifarentag_block_config21356D;
typedef struct {
uint8_t pack[2];
uint8_t rfui1[2];
} mifarentag_block_config21356E;
typedef struct {
uint8_t abtData[16];
} mifareul_block_data;
typedef struct {
uint8_t abtData[4];
} mifarentag_block_data;
typedef union {
mifarentag_block_manuf21356A mbm21356a;
mifarentag_block_manuf21356B mbm21356b;
mifarentag_block_manuf21356C mbm21356c;
mifarentag_block_manuf21356D mbm21356d;
mifarentag_block_data mbd;
mifarentag_block_config21356A mbc21356a;
mifarentag_block_config21356B mbc21356b;
mifarentag_block_config21356C mbc21356c;
mifarentag_block_config21356D mbc21356d;
mifarentag_block_config21356E mbc21356e;
} mifarentag_block;
typedef union {
mifareul_block_manufacturer mbm;
mifareul_block_data mbd;
@ -194,6 +246,43 @@ typedef struct {
mifareul_block amb[11];
} mifareul_ev1_mf0ul21_tag;
// NOT really UL but so similar we can re-use this code
// if Edwin van Andel doesn't distract us...
// https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf
// NTAG213 EEPROM: 180 bytes, organized in 45 pages of 4 byte per page.
// 26 bytes reserved for manufacturer and configuration data
// 34 bits used for the read-only locking mechanism
// 4 bytes available as capability container
// 144 bytes user programmable read/write memory
typedef struct {
mifarentag_block amb[45];
} mifarentag_213_tag;
// NTAG215 EEPROM: 540 bytes, organized in 135 pages of 4 byte per page.
// 26 bytes reserved for manufacturer and configuration data
// 28 bits used for the read-only locking mechanism
// 4 bytes available as capability container
// 504 bytes user programmable read/write memory
typedef struct {
mifarentag_block amb[135];
} mifarentag_215_tag;
// NTAG216 EEPROM: 924 bytes, organized in 231 pages of 4 byte per page.
// 26 bytes reserved for manufacturer and configuration data
// 37 bits used for the read-only locking mechanism
// 4 bytes available as capability container
// 888 bytes user programmable read/write memory
typedef struct {
mifarentag_block amb[231];
} mifarentag_216_tag;
// dummy max size with all structures in it for reading, rounded up to a multiple of 16 bytes
typedef union {
mifareul_block ul[58];
mifarentag_block nt[232];
} maxtag;
// Reset struct alignment to default
# pragma pack()

View file

@ -9,7 +9,7 @@
* Copyright (C) 2012-2013 Ludovic Rousseau
* See AUTHORS file for a more comprehensive list of contributors.
* Additional contributors of this file:
* Copyright (C) 2013-2017 Adam Laurie
* Copyright (C) 2013-2018 Adam Laurie
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -64,11 +64,14 @@
#define EV1_NONE 0
#define EV1_UL11 1
#define EV1_UL21 2
#define EV1_NTAG213 3
#define EV1_NTAG215 4
#define EV1_NTAG216 5
static nfc_device *pnd;
static nfc_target nt;
static mifare_param mp;
static mifareul_ev1_mf0ul21_tag mtDump; // use the largest tag type for internal storage
static maxtag mtDump; // use the largest tag type for internal storage
static uint32_t uiBlocks = 0x10;
static uint32_t uiReadPages = 0;
static uint8_t iPWD[4] = { 0x0 };
@ -119,7 +122,7 @@ read_card(void)
for (page = 0; page < uiBlocks; page += 4) {
// Try to read out the data block
if (nfc_initiator_mifare_cmd(pnd, MC_READ, page, &mp)) {
memcpy(mtDump.amb[page / 4].mbd.abtData, mp.mpd.abtData, uiBlocks - page < 4 ? (uiBlocks - page) * 4 : 16);
memcpy(mtDump.ul[page / 4].mbd.abtData, mp.mpd.abtData, uiBlocks - page < 4 ? (uiBlocks - page) * 4 : 16);
} else {
bFailure = true;
}
@ -134,12 +137,24 @@ read_card(void)
// copy EV1 secrets to dump data
switch (iEV1Type) {
case EV1_UL11:
memcpy(mtDump.amb[4].mbc11.pwd, iPWD, 4);
memcpy(mtDump.amb[4].mbc11.pack, iPACK, 2);
memcpy(mtDump.ul[4].mbc11.pwd, iPWD, 4);
memcpy(mtDump.ul[4].mbc11.pack, iPACK, 2);
break;
case EV1_UL21:
memcpy(mtDump.amb[9].mbc21a.pwd, iPWD, 4);
memcpy(mtDump.amb[9].mbc21b.pack, iPACK, 2);
memcpy(mtDump.ul[9].mbc21a.pwd, iPWD, 4);
memcpy(mtDump.ul[9].mbc21b.pack, iPACK, 2);
break;
case EV1_NTAG213:
memcpy(mtDump.nt[43].mbc21356d.pwd, iPWD, 4);
memcpy(mtDump.nt[44].mbc21356e.pack, iPACK, 2);
break;
case EV1_NTAG215:
memcpy(mtDump.nt[133].mbc21356d.pwd, iPWD, 4);
memcpy(mtDump.nt[134].mbc21356e.pack, iPACK, 2);
break;
case EV1_NTAG216:
memcpy(mtDump.nt[229].mbc21356d.pwd, iPWD, 4);
memcpy(mtDump.nt[230].mbc21356e.pack, iPACK, 2);
break;
case EV1_NONE:
default:
@ -386,7 +401,7 @@ write_card(bool write_otp, bool write_lock, bool write_uid)
// page (4 bytes). The Ultralight-specific Write command only
// writes one page at a time.
uiBlock = page / 4;
memcpy(mp.mpd.abtData, mtDump.amb[uiBlock].mbd.abtData + ((page % 4) * 4), 4);
memcpy(mp.mpd.abtData, mtDump.ul[uiBlock].mbd.abtData + ((page % 4) * 4), 4);
memset(mp.mpd.abtData + 4, 0, 12);
if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, page, &mp))
bFailure = true;
@ -470,7 +485,7 @@ int
main(int argc, const char *argv[])
{
int iAction = 0;
uint8_t iDumpSize = sizeof(mifareul_tag);
size_t iDumpSize = sizeof(mifareul_tag);
uint8_t iUID[MAX_UID_LEN] = { 0x0 };
size_t szUID = 0;
bool bOTP = false;
@ -601,19 +616,36 @@ main(int argc, const char *argv[])
if (get_ev1_version()) {
if (!bPWD)
printf("Tag is EV1 - PASSWORD may be required\n");
printf("EV1 storage size: ");
printf("EV1 type: ");
if (abtRx[6] == 0x0b) {
printf("48 bytes\n");
uiBlocks = 0x14;
printf("MF0UL11 (48 bytes)\n");
uiBlocks = 20; // total number of 4 byte 'pages'
iEV1Type = EV1_UL11;
iDumpSize = sizeof(mifareul_ev1_mf0ul11_tag);
} else if (abtRx[6] == 0x0e) {
printf("128 bytes\n");
uiBlocks = 0x29;
printf("MF0UL21 (128 user bytes)\n");
uiBlocks = 41;
iEV1Type = EV1_UL21;
iDumpSize = sizeof(mifareul_ev1_mf0ul21_tag);
} else
printf("unknown!\n");
} else if (abtRx[6] == 0x0f) {
printf("NTAG213 (144 user bytes)\n");
uiBlocks = 45;
iEV1Type = EV1_NTAG213;
iDumpSize = sizeof(mifarentag_213_tag);
} else if (abtRx[6] == 0x11) {
printf("NTAG215 (504 user bytes)\n");
uiBlocks = 135;
iEV1Type = EV1_NTAG215;
iDumpSize = sizeof(mifarentag_215_tag);
} else if (abtRx[6] == 0x13) {
printf("NTAG216 (888 user bytes)\n");
uiBlocks = 231;
iEV1Type = EV1_NTAG216;
iDumpSize = sizeof(mifarentag_216_tag);
} else {
printf("unknown! (0x%02x)\n", abtRx[6]);
exit(EXIT_FAILURE);
}
} else {
// re-init non EV1 tag
if (nfc_initiator_select_passive_target(pnd, nmMifare, (szUID) ? iUID : NULL, szUID, &nt) <= 0) {