From 4c914e67e5b6dee6634b95f5d2438802873a5c1a Mon Sep 17 00:00:00 2001 From: Adam Laurie Date: Mon, 17 Sep 2018 14:37:09 +0100 Subject: [PATCH 1/4] Fix handling of NTAG OTP and LOCK bytes --- utils/nfc-mfultralight.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/utils/nfc-mfultralight.c b/utils/nfc-mfultralight.c index 018af51..e75fe6e 100644 --- a/utils/nfc-mfultralight.c +++ b/utils/nfc-mfultralight.c @@ -61,13 +61,18 @@ #define MAX_TARGET_COUNT 16 #define MAX_UID_LEN 10 -#define EV1_NONE 0 -#define EV1_UL11 1 -#define EV1_UL21 2 +#define EV1_NONE 0 +#define EV1_UL11 1 +#define EV1_UL21 2 #define EV1_NTAG213 3 #define EV1_NTAG215 4 #define EV1_NTAG216 5 +#define NTAG_NONE 0 +#define NTAG_213 1 +#define NTAG_215 2 +#define NTAG_216 3 + static nfc_device *pnd; static nfc_target nt; static mifare_param mp; @@ -77,6 +82,7 @@ static uint32_t uiReadPages = 0; static uint8_t iPWD[4] = { 0x0 }; static uint8_t iPACK[2] = { 0x0 }; static uint8_t iEV1Type = EV1_NONE; +static uint8_t iNTAGType = NTAG_NONE; // special unlock command uint8_t abtUnlock1[1] = { 0x40 }; @@ -341,7 +347,10 @@ write_card(bool write_otp, bool write_lock, bool write_uid) char buffer[BUFSIZ]; if (!write_otp) { - printf("Write OTP bytes ? [yN] "); + if (iNTAGType == NTAG_NONE) + printf("Write OTP bytes ? [yN] "); + else + printf("Write OTP Static Lock bytes ? [yN] "); if (!fgets(buffer, BUFSIZ, stdin)) { ERR("Unable to read standard input."); } @@ -349,7 +358,10 @@ write_card(bool write_otp, bool write_lock, bool write_uid) } if (!write_lock) { - printf("Write Lock bytes ? [yN] "); + if (iNTAGType == NTAG_NONE) + printf("Write Lock bytes ? [yN] "); + else + printf("Write Dynamic Lock bytes ? [yN] "); if (!fgets(buffer, BUFSIZ, stdin)) { ERR("Unable to read standard input."); } @@ -376,13 +388,19 @@ write_card(bool write_otp, bool write_lock, bool write_uid) } } + // NTAG Dynamic Lock Bytes are in different locations for each type for (uint32_t page = uiSkippedPages; page < uiBlocks; page++) { - if ((page == 0x2) && (!write_lock)) { + if (((iNTAGType == NTAG_NONE && page == 0x2) || \ + (iNTAGType == NTAG_213 && page == 0x28) || \ + (iNTAGType == NTAG_215 && page == 0x82) || \ + (iNTAGType == NTAG_216 && page == 0xe2)) && (!write_lock)) { printf("s"); uiSkippedPages++; continue; } - if ((page == 0x3) && (!write_otp)) { + // NTAG doesn't have OTP blocks but Static Lock Bytes are OTP + if (((iNTAGType == NTAG_NONE && page == 0x3) || \ + (iNTAGType && page == 0x2)) && (!write_otp)) { printf("s"); uiSkippedPages++; continue; @@ -631,16 +649,19 @@ main(int argc, const char *argv[]) printf("NTAG213 (144 user bytes)\n"); uiBlocks = 45; iEV1Type = EV1_NTAG213; + iNTAGType = NTAG_213; iDumpSize = sizeof(mifarentag_213_tag); } else if (abtRx[6] == 0x11) { printf("NTAG215 (504 user bytes)\n"); uiBlocks = 135; iEV1Type = EV1_NTAG215; + iNTAGType = NTAG_215; iDumpSize = sizeof(mifarentag_215_tag); } else if (abtRx[6] == 0x13) { printf("NTAG216 (888 user bytes)\n"); uiBlocks = 231; iEV1Type = EV1_NTAG216; + iNTAGType = NTAG_216; iDumpSize = sizeof(mifarentag_216_tag); } else { printf("unknown! (0x%02x)\n", abtRx[6]); From 40b54a10d756563eed02cd6f17b81be5ccc3d492 Mon Sep 17 00:00:00 2001 From: Adam Laurie Date: Mon, 17 Sep 2018 16:58:49 +0100 Subject: [PATCH 2/4] Fix NTAG OTP and LOCK BYTES (Dynamic LOCK also applies to MF0UL21). Fix MF0UL21 sizing issue. --- utils/mifare.h | 1 + utils/nfc-mfultralight.c | 70 ++++++++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/utils/mifare.h b/utils/mifare.h index 4a354a7..75d65de 100644 --- a/utils/mifare.h +++ b/utils/mifare.h @@ -242,6 +242,7 @@ typedef struct { } mifareul_ev1_mf0ul11_tag; // UL EV1 MF0UL21 tag - 1 manuf block + 8 user blocks + 1/4 lock block + 1 config block +// (note that tag is actually 3 bytes smaller due to 1/4 block, so don't rely on this for sizing!) typedef struct { mifareul_block amb[11]; } mifareul_ev1_mf0ul21_tag; diff --git a/utils/nfc-mfultralight.c b/utils/nfc-mfultralight.c index e75fe6e..c54dcd9 100644 --- a/utils/nfc-mfultralight.c +++ b/utils/nfc-mfultralight.c @@ -336,7 +336,7 @@ static bool check_magic() } static bool -write_card(bool write_otp, bool write_lock, bool write_uid) +write_card(bool write_otp, bool write_lock, bool write_dyn_lock, bool write_uid) { uint32_t uiBlock = 0; bool bFailure = false; @@ -346,28 +346,33 @@ write_card(bool write_otp, bool write_lock, bool write_uid) char buffer[BUFSIZ]; - if (!write_otp) { - if (iNTAGType == NTAG_NONE) - printf("Write OTP bytes ? [yN] "); - else - printf("Write OTP Static Lock bytes ? [yN] "); + // NTAG does not have explicit OTP bytes + if (!write_otp && iNTAGType == NTAG_NONE) { + printf("Write OTP Bytes ? [yN] "); if (!fgets(buffer, BUFSIZ, stdin)) { ERR("Unable to read standard input."); } write_otp = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } + // Lock Bytes are OTP if set, so warn if (!write_lock) { - if (iNTAGType == NTAG_NONE) - printf("Write Lock bytes ? [yN] "); - else - printf("Write Dynamic Lock bytes ? [yN] "); + printf("Write Lock Bytes (Warning: OTP if set) ? [yN] "); if (!fgets(buffer, BUFSIZ, stdin)) { ERR("Unable to read standard input."); } write_lock = ((buffer[0] == 'y') || (buffer[0] == 'Y')); } + // NTAG and MF0UL21 have additional lock bytes + if (!write_dyn_lock && (iNTAGType != NTAG_NONE || iEV1Type == EV1_UL21)) { + printf("Write Dynamic Lock Bytes ? [yN] "); + if (!fgets(buffer, BUFSIZ, stdin)) { + ERR("Unable to read standard input."); + } + write_dyn_lock = ((buffer[0] == 'y') || (buffer[0] == 'Y')); + } + if (!write_uid) { printf("Write UID bytes (only for special writeable UID cards) ? [yN] "); if (!fgets(buffer, BUFSIZ, stdin)) { @@ -388,19 +393,23 @@ write_card(bool write_otp, bool write_lock, bool write_uid) } } - // NTAG Dynamic Lock Bytes are in different locations for each type for (uint32_t page = uiSkippedPages; page < uiBlocks; page++) { - if (((iNTAGType == NTAG_NONE && page == 0x2) || \ - (iNTAGType == NTAG_213 && page == 0x28) || \ - (iNTAGType == NTAG_215 && page == 0x82) || \ - (iNTAGType == NTAG_216 && page == 0xe2)) && (!write_lock)) { + if ((!write_lock) && page == 0x2) { printf("s"); uiSkippedPages++; continue; } - // NTAG doesn't have OTP blocks but Static Lock Bytes are OTP - if (((iNTAGType == NTAG_NONE && page == 0x3) || \ - (iNTAGType && page == 0x2)) && (!write_otp)) { + // NTAG doesn't have OTP blocks + if ((iNTAGType == NTAG_NONE && page == 0x3) && (!write_otp)) { + printf("s"); + uiSkippedPages++; + continue; + } + // NTAG and MF0UL21 have Dynamic Lock Bytes + if (((iEV1Type == EV1_UL21 && page == 0x24) || \ + (iNTAGType == NTAG_213 && page == 0x28) || \ + (iNTAGType == NTAG_215 && page == 0x82) || \ + (iNTAGType == NTAG_216 && page == 0xe2)) && (!write_dyn_lock)) { printf("s"); uiSkippedPages++; continue; @@ -490,10 +499,11 @@ print_usage(const char *argv[]) printf("\tr|w - Perform read or write\n"); printf("\t - MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)\n"); printf("Options:\n"); - printf("\t--otp - Don't prompt for OTP writing (Assume yes)\n"); - printf("\t--lock - Don't prompt for Lockbit writing (Assume yes)\n"); + printf("\t--otp - Don't prompt for OTP Bytes writing (Assume yes)\n"); + printf("\t--lock - Don't prompt for Lock Bytes (OTP) writing (Assume yes)\n"); + printf("\t--dynlock - Don't prompt for Dynamic Lock Bytes writing (Assume yes)\n"); printf("\t--uid - Don't prompt for UID writing (Assume yes)\n"); - printf("\t--full - Assume full card write (UID + OTP + Lockbit)\n"); + printf("\t--full - Assume full card write (UID + OTP + Lockbytes + Dynamic Lockbytes)\n"); printf("\t--with-uid - Specify UID to read/write from\n"); printf("\t--pw - Specify 8 HEX digit PASSWORD for EV1\n"); printf("\t--partial - Allow source data size to be other than tag capacity\n"); @@ -508,6 +518,7 @@ main(int argc, const char *argv[]) size_t szUID = 0; bool bOTP = false; bool bLock = false; + bool bDynLock = false; bool bUID = false; bool bPWD = false; bool bPart = false; @@ -536,11 +547,14 @@ main(int argc, const char *argv[]) } else if (0 == strcmp(argv[arg], "--full")) { bOTP = true; bLock = true; + bDynLock = true; bUID = true; } else if (0 == strcmp(argv[arg], "--otp")) { bOTP = true; } else if (0 == strcmp(argv[arg], "--lock")) { bLock = true; + } else if (0 == strcmp(argv[arg], "--dynlock")) { + bDynLock = true; } else if (0 == strcmp(argv[arg], "--uid")) { bUID = true; } else if (0 == strcmp(argv[arg], "--check-magic")) { @@ -638,31 +652,31 @@ main(int argc, const char *argv[]) if (abtRx[6] == 0x0b) { printf("MF0UL11 (48 bytes)\n"); uiBlocks = 20; // total number of 4 byte 'pages' + iDumpSize = uiBlocks * 4; iEV1Type = EV1_UL11; - iDumpSize = sizeof(mifareul_ev1_mf0ul11_tag); } else if (abtRx[6] == 0x0e) { printf("MF0UL21 (128 user bytes)\n"); uiBlocks = 41; + iDumpSize = uiBlocks * 4; iEV1Type = EV1_UL21; - iDumpSize = sizeof(mifareul_ev1_mf0ul21_tag); } else if (abtRx[6] == 0x0f) { printf("NTAG213 (144 user bytes)\n"); uiBlocks = 45; + iDumpSize = uiBlocks * 4; iEV1Type = EV1_NTAG213; iNTAGType = NTAG_213; - iDumpSize = sizeof(mifarentag_213_tag); } else if (abtRx[6] == 0x11) { printf("NTAG215 (504 user bytes)\n"); uiBlocks = 135; + iDumpSize = uiBlocks * 4; iEV1Type = EV1_NTAG215; iNTAGType = NTAG_215; - iDumpSize = sizeof(mifarentag_215_tag); } else if (abtRx[6] == 0x13) { printf("NTAG216 (888 user bytes)\n"); uiBlocks = 231; + iDumpSize = uiBlocks * 4; iEV1Type = EV1_NTAG216; iNTAGType = NTAG_216; - iDumpSize = sizeof(mifarentag_216_tag); } else { printf("unknown! (0x%02x)\n", abtRx[6]); exit(EXIT_FAILURE); @@ -702,7 +716,7 @@ main(int argc, const char *argv[]) size_t szDump; if (((szDump = fread(&mtDump, 1, sizeof(mtDump), pfDump)) != iDumpSize && !bPart) || szDump <= 0) { - ERR("Could not read from dump file or size mismatch: %s\n", argv[2]); + ERR("Could not read from dump file or size mismatch: %s (read %lu, expected %lu)\n", argv[2], szDump, iDumpSize); fclose(pfDump); exit(EXIT_FAILURE); } @@ -740,7 +754,7 @@ main(int argc, const char *argv[]) if (!bRF) printf("Warning! Read failed - partial data written to file!\n"); } else if (iAction == 2) { - write_card(bOTP, bLock, bUID); + write_card(bOTP, bLock, bDynLock, bUID); } else if (iAction == 3) { if (!check_magic()) { printf("Card is not magic\n"); From 3ba065f00bc87255ae53d8d1b2ccc077b4d72033 Mon Sep 17 00:00:00 2001 From: Adam Laurie Date: Mon, 17 Sep 2018 21:40:48 +0100 Subject: [PATCH 3/4] Remove redundant EV1 types and rely on NTAG types instead --- utils/nfc-mfultralight.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/utils/nfc-mfultralight.c b/utils/nfc-mfultralight.c index c54dcd9..99916a2 100644 --- a/utils/nfc-mfultralight.c +++ b/utils/nfc-mfultralight.c @@ -64,9 +64,6 @@ #define EV1_NONE 0 #define EV1_UL11 1 #define EV1_UL21 2 -#define EV1_NTAG213 3 -#define EV1_NTAG215 4 -#define EV1_NTAG216 5 #define NTAG_NONE 0 #define NTAG_213 1 @@ -150,19 +147,25 @@ read_card(void) memcpy(mtDump.ul[9].mbc21a.pwd, iPWD, 4); memcpy(mtDump.ul[9].mbc21b.pack, iPACK, 2); break; - case EV1_NTAG213: + case EV1_NONE: + default: + break; + } + // copy NTAG secrets to dump data + switch (iNTAGType) { + case NTAG_213: memcpy(mtDump.nt[43].mbc21356d.pwd, iPWD, 4); memcpy(mtDump.nt[44].mbc21356e.pack, iPACK, 2); break; - case EV1_NTAG215: + case NTAG_215: memcpy(mtDump.nt[133].mbc21356d.pwd, iPWD, 4); memcpy(mtDump.nt[134].mbc21356e.pack, iPACK, 2); break; - case EV1_NTAG216: + case NTAG_216: memcpy(mtDump.nt[229].mbc21356d.pwd, iPWD, 4); memcpy(mtDump.nt[230].mbc21356e.pack, iPACK, 2); break; - case EV1_NONE: + case NTAG_NONE: default: break; } @@ -644,7 +647,7 @@ main(int argc, const char *argv[]) } printf("\n"); - // test if tag is EV1 + // test if tag is EV1 or NTAG if (get_ev1_version()) { if (!bPWD) printf("Tag is EV1 - PASSWORD may be required\n"); @@ -663,19 +666,16 @@ main(int argc, const char *argv[]) printf("NTAG213 (144 user bytes)\n"); uiBlocks = 45; iDumpSize = uiBlocks * 4; - iEV1Type = EV1_NTAG213; iNTAGType = NTAG_213; } else if (abtRx[6] == 0x11) { printf("NTAG215 (504 user bytes)\n"); uiBlocks = 135; iDumpSize = uiBlocks * 4; - iEV1Type = EV1_NTAG215; iNTAGType = NTAG_215; } else if (abtRx[6] == 0x13) { printf("NTAG216 (888 user bytes)\n"); uiBlocks = 231; iDumpSize = uiBlocks * 4; - iEV1Type = EV1_NTAG216; iNTAGType = NTAG_216; } else { printf("unknown! (0x%02x)\n", abtRx[6]); From 25ee3a2f762433b15d1a9008fe9e2240c31c8222 Mon Sep 17 00:00:00 2001 From: Adam Laurie Date: Tue, 18 Sep 2018 11:14:08 +0100 Subject: [PATCH 4/4] Add OTP/Capability Bytes handling to NTAG --- utils/mifare.h | 2 +- utils/nfc-mfultralight.c | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/utils/mifare.h b/utils/mifare.h index 75d65de..627cc59 100644 --- a/utils/mifare.h +++ b/utils/mifare.h @@ -247,7 +247,7 @@ typedef struct { mifareul_block amb[11]; } mifareul_ev1_mf0ul21_tag; -// NOT really UL but so similar we can re-use this code +// NTAG is a range of NXP tags some of which are essentially Ultralights so we can support them here // if Edwin van Andel doesn't distract us... // https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf diff --git a/utils/nfc-mfultralight.c b/utils/nfc-mfultralight.c index 99916a2..3fe2100 100644 --- a/utils/nfc-mfultralight.c +++ b/utils/nfc-mfultralight.c @@ -349,9 +349,8 @@ write_card(bool write_otp, bool write_lock, bool write_dyn_lock, bool write_uid) char buffer[BUFSIZ]; - // NTAG does not have explicit OTP bytes - if (!write_otp && iNTAGType == NTAG_NONE) { - printf("Write OTP Bytes ? [yN] "); + if (!write_otp) { + printf("Write OTP/Capability Bytes ? [yN] "); if (!fgets(buffer, BUFSIZ, stdin)) { ERR("Unable to read standard input."); } @@ -402,8 +401,8 @@ write_card(bool write_otp, bool write_lock, bool write_dyn_lock, bool write_uid) uiSkippedPages++; continue; } - // NTAG doesn't have OTP blocks - if ((iNTAGType == NTAG_NONE && page == 0x3) && (!write_otp)) { + // OTP/Capability blocks + if ((page == 0x3) && (!write_otp)) { printf("s"); uiSkippedPages++; continue; @@ -650,30 +649,29 @@ main(int argc, const char *argv[]) // test if tag is EV1 or NTAG if (get_ev1_version()) { if (!bPWD) - printf("Tag is EV1 - PASSWORD may be required\n"); - printf("EV1 type: "); + printf("WARNING: Tag is EV1 or NTAG - PASSWORD may be required\n"); if (abtRx[6] == 0x0b) { - printf("MF0UL11 (48 bytes)\n"); + printf("EV1 type: MF0UL11 (48 bytes)\n"); uiBlocks = 20; // total number of 4 byte 'pages' iDumpSize = uiBlocks * 4; iEV1Type = EV1_UL11; } else if (abtRx[6] == 0x0e) { - printf("MF0UL21 (128 user bytes)\n"); + printf("EV1 type: MF0UL21 (128 user bytes)\n"); uiBlocks = 41; iDumpSize = uiBlocks * 4; iEV1Type = EV1_UL21; } else if (abtRx[6] == 0x0f) { - printf("NTAG213 (144 user bytes)\n"); + printf("NTAG Type: NTAG213 (144 user bytes)\n"); uiBlocks = 45; iDumpSize = uiBlocks * 4; iNTAGType = NTAG_213; } else if (abtRx[6] == 0x11) { - printf("NTAG215 (504 user bytes)\n"); + printf("NTAG Type: NTAG215 (504 user bytes)\n"); uiBlocks = 135; iDumpSize = uiBlocks * 4; iNTAGType = NTAG_215; } else if (abtRx[6] == 0x13) { - printf("NTAG216 (888 user bytes)\n"); + printf("NTAG Type: NTAG216 (888 user bytes)\n"); uiBlocks = 231; iDumpSize = uiBlocks * 4; iNTAGType = NTAG_216;