diff --git a/CHANGELOG.md b/CHANGELOG.md index b6d3ca1da..eee8ddc9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added initial support for ST25TN and its signature verification (@doegox) - Changed originality checks handling to refactor code and pk data (@doegox) - Changed `uniq.yaml` workflow to be case-insensitive (@iceman1001) - Fixed `mem load --mfc` not erasing all SPI flash blocks after extending to 4095 keys (@piotrva) diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 08d723d8d..cd1db3e08 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -2680,8 +2680,12 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfdes info") "`"); } - if ((isST) && (card.ats_len >= 0)) { - PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf st25ta info") "`"); + if (isST) { + if (card.ats_len > 0) { + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf st25ta info") "`"); + } else { + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfu info") "`"); + } } if (isEMV) { diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index fafcf2b78..5094d40a3 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -54,6 +54,8 @@ #define MAX_MY_D_MOVE_LEAN 0x0F #define MAX_UL_NANO_40 0x0A #define MAX_UL_AES 0x37 +#define MAX_ST25TN512 0x3F +#define MAX_ST25TN01K 0x3F static int CmdHelp(const char *Cmd); @@ -104,7 +106,9 @@ static uint64_t UL_TYPES_ARRAY[] = { MFU_TT_MAGIC_1A, MFU_TT_MAGIC_1B, MFU_TT_MAGIC_NTAG, MFU_TT_NTAG_210u, MFU_TT_UL_MAGIC, MFU_TT_UL_C_MAGIC, - MFU_TT_UL_AES + MFU_TT_UL_AES, + MFU_TT_ST25TN512, MFU_TT_ST25TN01K, + }; static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { @@ -125,7 +129,9 @@ static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { // MAGIC_1A, MAGIC_1B, MAGIC_NTAG, MAX_UL_BLOCKS, MAX_UL_BLOCKS, MAX_NTAG_216, // NTAG_210u, UL_MAGIC, UL_C_MAGIC - MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_UL_AES + MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_UL_AES, +// ST25TN512, ST25TN01K, + MAX_ST25TN512, MAX_ST25TN01K, }; static const ul_family_t ul_family[] = { @@ -790,7 +796,13 @@ static int ul_print_default(uint8_t *data, uint8_t *real_uid) { PrintAndLogEx(SUCCESS, " BCC1: %02X ( " _GREEN_("ok") " )", data[8]); else PrintAndLogEx(NORMAL, " BCC1: %02X, crc should be %02X", data[8], crc1); - PrintAndLogEx(SUCCESS, " Internal: %02X ( %s )", data[9], (data[9] == 0x48) ? _GREEN_("default") : _RED_("not default")); + if (uid[0] == 0x04) { + PrintAndLogEx(SUCCESS, " Internal: %02X ( %s )", data[9], (data[9] == 0x48) ? _GREEN_("default") : _RED_("not default")); + } else if (uid[0] == 0x02) { + PrintAndLogEx(SUCCESS, " Sysblock: %02X ( %s )", data[9], (data[9] == 0x2C) ? _GREEN_("default") : _RED_("not default")); + } else { + PrintAndLogEx(SUCCESS, " Internal: %02X", data[9]); + } } else { PrintAndLogEx(SUCCESS, "Blocks 0-2: %s", sprint_hex(data + 0, 12)); } @@ -1012,6 +1024,10 @@ int ul_print_type(uint64_t tagtype, uint8_t spaces) { snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move lean (SLE 66R01L)"), spaces, ""); else if (tagtype & MFU_TT_FUDAN_UL) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("FUDAN Ultralight Compatible (or other compatible)"), spaces, ""); + else if (tagtype & MFU_TT_ST25TN512) + snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("ST ST25TN512 64bytes"), spaces, ""); + else if (tagtype & MFU_TT_ST25TN01K) + snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("ST ST25TN01K 160bytes"), spaces, ""); else snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("Unknown %06" PRIx64), spaces, "", tagtype); @@ -1998,17 +2014,55 @@ uint64_t GetHF14AMfU_Type(void) { // Ultralight - ATQA / SAK if (card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00) { - //PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); + //PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D |ST25TN [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); DropField(); return MFU_TT_UL_ERROR; } - - if (card.uid[0] != 0x05) { + if (card.uid[0] == 0x02) { + // ST25TN + // read SYSBLOCK + uint8_t data[4] = {0x00}; + int status = ul_read(0x02, data, sizeof(data)); + if (status <= 1) { + tagtype = MFU_TT_UL; + } else { + status = ul_read(data[1] + 1, data, sizeof(data)); + if (status <= 1) { + tagtype = MFU_TT_UL; + } else { + // data[3] == KID == 0x05 Key ID + // data[2] == REV == 0x13 Product version + if ((data[1]==0x90) && (data[0]==0x90)) { + tagtype = MFU_TT_ST25TN01K; + } else if ((data[1]==0x90) && (data[0]==0x91)) { + tagtype = MFU_TT_ST25TN512; + } + } + } + } else if (card.uid[0] == 0x05) { + // Infineon MY-D tests Exam high nibble + DropField(); + uint8_t nib = (card.uid[1] & 0xf0) >> 4; + switch (nib) { + // case 0: tagtype = SLE66R35E7; break; //or SLE 66R35E7 - mifare compat... should have different sak/atqa for mf 1k + case 1: + tagtype = MFU_TT_MY_D; + break; // or SLE 66RxxS ... up to 512 pages of 8 user bytes... + case 2: + tagtype = MFU_TT_MY_D_NFC; + break; // or SLE 66RxxP ... up to 512 pages of 8 user bytes... (or in nfc mode FF pages of 4 bytes) + case 3: + tagtype = (MFU_TT_MY_D_MOVE | MFU_TT_MY_D_MOVE_NFC); + break; // or SLE 66R01P // 38 pages of 4 bytes //notice: we can not currently distinguish between these two + case 7: + tagtype = MFU_TT_MY_D_MOVE_LEAN; + break; // or SLE 66R01L // 16 pages of 4 bytes + } + } else { uint8_t version[10] = {0x00}; int len = ulev1_getVersion(version, sizeof(version)); DropField(); - switch (len) { case 0x0A: { /* @@ -2096,7 +2150,6 @@ uint64_t GetHF14AMfU_Type(void) { tagtype = MFU_TT_UNKNOWN; break; } - // This is a test from cards that doesn't answer to GET_VERSION command // UL vs UL-C vs NTAG203 vs FUDAN FM11NT021 (which is NTAG213 compatiable) if (tagtype & (MFU_TT_UL | MFU_TT_UL_C | MFU_TT_NTAG_203)) { @@ -2150,25 +2203,6 @@ uint64_t GetHF14AMfU_Type(void) { tagtype = ul_fudan_check(); DropField(); } - } else { - DropField(); - // Infinition MY-D tests Exam high nibble - uint8_t nib = (card.uid[1] & 0xf0) >> 4; - switch (nib) { - // case 0: tagtype = SLE66R35E7; break; //or SLE 66R35E7 - mifare compat... should have different sak/atqa for mf 1k - case 1: - tagtype = MFU_TT_MY_D; - break; // or SLE 66RxxS ... up to 512 pages of 8 user bytes... - case 2: - tagtype = MFU_TT_MY_D_NFC; - break; // or SLE 66RxxP ... up to 512 pages of 8 user bytes... (or in nfc mode FF pages of 4 bytes) - case 3: - tagtype = (MFU_TT_MY_D_MOVE | MFU_TT_MY_D_MOVE_NFC); - break; // or SLE 66R01P // 38 pages of 4 bytes //notice: we can not currently distinguish between these two - case 7: - tagtype = MFU_TT_MY_D_MOVE_LEAN; - break; // or SLE 66R01L // 16 pages of 4 bytes - } } tagtype |= ul_magic_test(); @@ -2378,6 +2412,39 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } } + // ST25TN info & signature + if (tagtype & (MFU_TT_ST25TN512 | MFU_TT_ST25TN01K)) { + status = ul_read(0x02, data, sizeof(data)); + if (status <= 1) { + PrintAndLogEx(ERR, "Error: tag didn't answer to READ SYSBLOCK"); + DropField(); + return PM3_ESOFT; + } + status = ul_read(data[1] + 1, data, sizeof(data)); + if (status <= 1) { + PrintAndLogEx(ERR, "Error: tag didn't answer to READ SYSBLOCK"); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "--- " _CYAN_("Tag System Information")); + PrintAndLogEx(INFO, " Key ID: %02x", data[3]); + PrintAndLogEx(INFO, " Product Version: %02x", data[2]); + PrintAndLogEx(INFO, " Product Code: %02x%02x", data[1], data[0]); + uint8_t signature[32] = {0}; + for (int blkoff=0; blkoff<8; blkoff++) { + status = ul_read(0x34 + blkoff, signature + (blkoff * 4), 4); + if (status <= 1) { + PrintAndLogEx(ERR, "Error: tag didn't answer to READ SYSBLOCK"); + DropField(); + return PM3_ESOFT; + } + } + // check signature + int index = originality_check_verify_ex(card.uid, 7, signature, sizeof(signature), PK_ST25TN, false, true); + PrintAndLogEx(NORMAL, ""); + originality_check_print(signature, sizeof(signature), index); + } + // Read signature if ((tagtype & (MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1 | MFU_TT_UL_NANO_40 | MFU_TT_NTAG_210u | MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | diff --git a/client/src/cmdhfmfu.h b/client/src/cmdhfmfu.h index 37dbff558..a365e76d3 100644 --- a/client/src/cmdhfmfu.h +++ b/client/src/cmdhfmfu.h @@ -96,6 +96,8 @@ int CmdHF14MfUTamper(const char *Cmd); #define MFU_TT_MAGIC_4 0x400000000ULL #define MFU_TT_MAGIC_4_GDM 0x800000000ULL #define MFU_TT_MAGIC_NTAG21X 0x1000000000ULL +#define MFU_TT_ST25TN512 0x2000000000ULL +#define MFU_TT_ST25TN01K 0x4000000000ULL #define MFU_TT_UL_MAGIC (MFU_TT_UL | MFU_TT_MAGIC) #define MFU_TT_UL_C_MAGIC (MFU_TT_UL_C | MFU_TT_MAGIC) // Don't forget to fill UL_TYPES_ARRAY and UL_MEMORY_ARRAY if new types are added diff --git a/client/src/cmdhfst25ta.c b/client/src/cmdhfst25ta.c index 9656d8146..7dff65067 100644 --- a/client/src/cmdhfst25ta.c +++ b/client/src/cmdhfst25ta.c @@ -149,7 +149,7 @@ static void print_st25ta_system_info(uint8_t *d, uint8_t n) { } static int print_st25ta_signature(uint8_t *uid, uint8_t *signature) { - int index = originality_check_verify_ex(uid, 7, signature, 32, PK_ST25, false, true); + int index = originality_check_verify_ex(uid, 7, signature, 32, PK_ST25TA, false, true); PrintAndLogEx(NORMAL, ""); return originality_check_print(signature, 32, index); } diff --git a/client/src/crypto/originality.c b/client/src/crypto/originality.c index 0fe7c9676..2dbda1471 100644 --- a/client/src/crypto/originality.c +++ b/client/src/crypto/originality.c @@ -69,13 +69,16 @@ const ecdsa_publickey_ng_t manufacturer_public_keys[] = { {PK_MFDES, MBEDTLS_ECP_DP_SECP224R1, 57, "DESFire EV3", "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"}, - {PK_ST25, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TA TruST25 (ST) key 01?", + // ref: AN5101 TruST25 digital signature for ST25TA512B, ST25TA02KB, ST25TA02KB-D and ST25TA02KB-P devices + {PK_ST25TA, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TA TruST25 (ST) key 01?", "041D92163650161A2548D33881C235D0FB2315C2C31A442F23C87ACF14497C0CBA"}, -// FIXME: need to implement support for ST25TN signature check. hash=sha256 - from block 52, followed by ascii UID - {PK_ST25, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TN TruST25 (ST) key 05?", + // ref: AN5660 TruST25 digital signature for ST25TN512 and ST25TN01K devices + {PK_ST25TN, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TN TruST25 (ST) KeyID 05", "0440004F974F7C76BC8718E523D85FA7B354A9A992BFA966CB8219242F9D274FD6"}, // FIXME: need to implement support for ST25TV signature check. hash=sha256 - from block 63, starting with KeyID ? - {PK_ST25, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TV TruST25 (ST) key 04?", + // ref: AN5104 TruST25 digital signature for ST25TV512 and ST25TV02K devices ? + // ref: AN5580 TruST25 digital signature for ST25TV512C and ST25TV02KC devices + {PK_ST25TV, MBEDTLS_ECP_DP_SECP128R1, 33, "ST25TV TruST25 (ST) key 04?", "04101E188A8B4CDDBC62D5BC3E0E6850F0C2730E744B79765A0E079907FBDB01BC"}, {PK_15, MBEDTLS_ECP_DP_SECP128R1, 33, "NXP ICODE DNA, ICODE SLIX2", diff --git a/client/src/crypto/originality.h b/client/src/crypto/originality.h index 25c95f6eb..c6aab57fb 100644 --- a/client/src/crypto/originality.h +++ b/client/src/crypto/originality.h @@ -25,7 +25,7 @@ #include #include -typedef enum {PK_MFC, PK_MFUL, PK_MFULAES, PK_MFP, PK_MFDES, PK_ST25, PK_15, PK_MIK, PK_ALL} pk_type_t; +typedef enum {PK_MFC, PK_MFUL, PK_MFULAES, PK_MFP, PK_MFDES, PK_ST25TA, PK_ST25TN, PK_ST25TV, PK_15, PK_MIK, PK_ALL} pk_type_t; typedef struct { const pk_type_t type;