Skip to content

Commit

Permalink
Add initial support for ST25TN and its signature verification
Browse files Browse the repository at this point in the history
  • Loading branch information
doegox committed Dec 15, 2024
1 parent 67ee460 commit be79654
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 35 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 6 additions & 2 deletions client/src/cmdhf14a.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
121 changes: 94 additions & 27 deletions client/src/cmdhfmfu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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)] = {
Expand All @@ -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[] = {
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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: {
/*
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 |
Expand Down
2 changes: 2 additions & 0 deletions client/src/cmdhfmfu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion client/src/cmdhfst25ta.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
11 changes: 7 additions & 4 deletions client/src/crypto/originality.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion client/src/crypto/originality.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#include <mbedtls/pk.h>
#include <mbedtls/ecp.h>

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;
Expand Down

0 comments on commit be79654

Please sign in to comment.