From 84873aa3cfa3d31951f7a8b8ccb042d02534c8a3 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 15 Dec 2020 17:17:54 +0300 Subject: [PATCH 01/11] emrtd: pad document number --- client/src/cmdhfemrtd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index ea90fa433..7637541d7 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -221,6 +221,12 @@ static void des3_decrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inpu mbedtls_des3_free(&ctx); } +static void emrtd_pad_docnum(char *input) { + for (int i = strlen(input); i < 9; i++) { + input[i] = '<'; + } +} + static int pad_block(uint8_t *input, int inputlen, uint8_t *output) { uint8_t padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -834,6 +840,8 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab bool BAC = false; bool use_14b = false; + emrtd_pad_docnum(documentnumber); + // Try to 14a SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; @@ -960,7 +968,7 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("n", "documentnumber", "", "9 character document number"), + arg_str0("n", "documentnumber", "", "document number, up to 9 chars"), arg_str0("d", "dateofbirth", "", "date of birth in YYMMDD format"), arg_str0("e", "expiry", "", "expiry in YYMMDD format"), arg_param_end From ac3392402fee65bdbdd17588d1b6c5f884031fc3 Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 16 Dec 2020 22:18:14 +0300 Subject: [PATCH 02/11] emrtd: Split auth into a separate function This is done to prepare for info command --- client/src/cmdhfemrtd.c | 68 +++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 7637541d7..2251ddac8 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -797,7 +797,7 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?"); return false; } - PrintAndLogEx(INFO, "External authentication successful."); + PrintAndLogEx(INFO, "External authentication with BAC successful."); uint8_t dec_output[32] = { 0x00 }; des3_decrypt_cbc(iv, kenc, response, 32, dec_output); @@ -831,14 +831,9 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t return true; } -int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { +static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BAC_available, bool *BAC, uint8_t *ssc, uint8_t *ks_enc, uint8_t *ks_mac, bool *use_14b) { uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; int resplen = 0; - uint8_t ssc[8] = { 0x00 }; - uint8_t ks_enc[16] = { 0x00 }; - uint8_t ks_mac[16] = { 0x00 }; - bool BAC = false; - bool use_14b = false; emrtd_pad_docnum(documentnumber); @@ -856,22 +851,20 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab // If not 14a, try to 14b SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD, 0, 0, NULL, 0); if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2500)) { - DropField(); PrintAndLogEx(INFO, "No eMRTD spotted with 14b, exiting."); - return PM3_ESOFT; + return false; } if (resp.oldarg[0] != 0) { - DropField(); PrintAndLogEx(INFO, "No eMRTD spotted with 14b, exiting."); - return PM3_ESOFT; + return false; } - use_14b = true; + *use_14b = true; } // Select and read EF_CardAccess - if (emrtd_select_file(EMRTD_P1_SELECT_BY_EF, EMRTD_EF_CARDACCESS, use_14b)) { - emrtd_read_file(response, &resplen, NULL, NULL, NULL, false, use_14b); + if (emrtd_select_file(EMRTD_P1_SELECT_BY_EF, EMRTD_EF_CARDACCESS, *use_14b)) { + emrtd_read_file(response, &resplen, NULL, NULL, NULL, false, *use_14b); PrintAndLogEx(INFO, "Read EF_CardAccess, len: %i.", resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); } else { @@ -879,46 +872,61 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab } // Select MRTD applet - if (emrtd_select_file(EMRTD_P1_SELECT_BY_NAME, EMRTD_AID_MRTD, use_14b) == false) { + if (emrtd_select_file(EMRTD_P1_SELECT_BY_NAME, EMRTD_AID_MRTD, *use_14b) == false) { PrintAndLogEx(ERR, "Couldn't select the MRTD application."); - DropField(); - return PM3_ESOFT; + return false; } // Select EF_COM - if (emrtd_select_file(EMRTD_P1_SELECT_BY_EF, EMRTD_EF_COM, use_14b) == false) { - BAC = true; + if (emrtd_select_file(EMRTD_P1_SELECT_BY_EF, EMRTD_EF_COM, *use_14b) == false) { + *BAC = true; PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); } else { - BAC = false; + *BAC = false; // Select EF_DG1 - emrtd_select_file(EMRTD_P1_SELECT_BY_EF, EMRTD_EF_DG1, use_14b); + emrtd_select_file(EMRTD_P1_SELECT_BY_EF, EMRTD_EF_DG1, *use_14b); - if (emrtd_read_file(response, &resplen, NULL, NULL, NULL, false, use_14b) == false) { - BAC = true; + if (emrtd_read_file(response, &resplen, NULL, NULL, NULL, false, *use_14b) == false) { + *BAC = true; PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); } else { - BAC = false; + *BAC = false; PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex(response, resplen)); } } // Do Basic Access Aontrol - if (BAC) { + if (*BAC) { // If BAC isn't available, exit out and warn user. if (!BAC_available) { PrintAndLogEx(ERR, "This eMRTD enforces Basic Access Control, but you didn't supplied MRZ data. Cannot proceed."); PrintAndLogEx(HINT, "Check out hf emrtd dump --help, supply data with -n -d and -e."); - DropField(); - return PM3_ESOFT; + return false; } - if (emrtd_do_bac(documentnumber, dob, expiry, ssc, ks_enc, ks_mac, use_14b) == false) { - DropField(); - return PM3_ESOFT; + if (emrtd_do_bac(documentnumber, dob, expiry, ssc, ks_enc, ks_mac, *use_14b) == false) { + return false; } } + return true; +} + +int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { + uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + int resplen = 0; + uint8_t ssc[8] = { 0x00 }; + uint8_t ks_enc[16] = { 0x00 }; + uint8_t ks_mac[16] = { 0x00 }; + bool BAC = false; + bool use_14b = false; + + // Select and authenticate with the eMRTD + if (emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac, &use_14b) == false) { + DropField(); + return PM3_ESOFT; + } + // Select EF_COM if (emrtd_select_and_read(response, &resplen, EMRTD_EF_COM, ks_enc, ks_mac, ssc, BAC, use_14b) == false) { PrintAndLogEx(ERR, "Failed to read EF_COM."); From 503e48d409e8dc353cc65a55f0acdf07d49fa4da Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 16 Dec 2020 23:53:40 +0300 Subject: [PATCH 03/11] emrtd: Start work on the hf emrtd info command --- client/src/cmdhfemrtd.c | 69 ++++++++++++++++++++++++++++++++++++++++- client/src/cmdhfemrtd.h | 1 + 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 2251ddac8..f0f238615 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -899,7 +899,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA if (*BAC) { // If BAC isn't available, exit out and warn user. if (!BAC_available) { - PrintAndLogEx(ERR, "This eMRTD enforces Basic Access Control, but you didn't supplied MRZ data. Cannot proceed."); + PrintAndLogEx(ERR, "This eMRTD enforces Basic Access Control, but you didn't supply MRZ data. Cannot proceed."); PrintAndLogEx(HINT, "Check out hf emrtd dump --help, supply data with -n -d and -e."); return false; } @@ -967,6 +967,38 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab return PM3_SUCCESS; } +int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { + // TODO: support just loading a dump file + uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + int resplen = 0; + uint8_t ssc[8] = { 0x00 }; + uint8_t ks_enc[16] = { 0x00 }; + uint8_t ks_mac[16] = { 0x00 }; + bool BAC = false; + bool use_14b = false; + + // Select and authenticate with the eMRTD + bool auth_result = emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac, &use_14b); + PrintAndLogEx(SUCCESS, "Communication standard: %s", use_14b ? _YELLOW_("ISO/IEC 14443(B)") : _YELLOW_("ISO/IEC 14443(A)")); + PrintAndLogEx(SUCCESS, "BAC: %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced")); + PrintAndLogEx(SUCCESS, "Authentication result: %s", auth_result ? _GREEN_("Successful") : _RED_("Failed")); + + if (!auth_result) { + DropField(); + return PM3_ESOFT; + } + + // Select EF_DG1 + if (emrtd_select_and_read(response, &resplen, EMRTD_EF_DG1, ks_enc, ks_mac, ssc, BAC, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to read EF_DG1."); + DropField(); + return PM3_ESOFT; + } + + DropField(); + return PM3_SUCCESS; +} + static int cmd_hf_emrtd_dump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf emrtd dump", @@ -1001,6 +1033,40 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC); } +static int cmd_hf_emrtd_info(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf emrtd info", + "Display info about an eMRTD", + "hf emrtd info" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("n", "documentnumber", "", "document number, up to 9 chars"), + arg_str0("d", "dateofbirth", "", "date of birth in YYMMDD format"), + arg_str0("e", "expiry", "", "expiry in YYMMDD format"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t docnum[10] = { 0x00 }; + uint8_t dob[7] = { 0x00 }; + uint8_t expiry[7] = { 0x00 }; + bool BAC = true; + int slen = 0; // unused + // Go through all args, if even one isn't supplied, mark BAC as unavailable + if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) { + BAC = false; + } else if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { + BAC = false; + } else if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { + BAC = false; + } + + CLIParserFree(ctx); + return infoHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC); +} + static int cmd_hf_emrtd_list(const char *Cmd) { char args[128] = {0}; if (strlen(Cmd) == 0) { @@ -1014,6 +1080,7 @@ static int cmd_hf_emrtd_list(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"dump", cmd_hf_emrtd_dump, IfPm3Iso14443, "Dump eMRTD files to binary files"}, + {"info", cmd_hf_emrtd_info, IfPm3Iso14443, "Display info about an eMRTD"}, {"list", cmd_hf_emrtd_list, AlwaysAvailable, "List ISO 14443A/7816 history"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index 84500fc8b..f19a71ac0 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -16,4 +16,5 @@ int CmdHFeMRTD(const char *Cmd); int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); +int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); #endif From 76da4ce427c08999640906a99575d3bec41c88f0 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 17 Dec 2020 00:00:26 +0100 Subject: [PATCH 04/11] change 14b apdu - longer timeout, emrt now autopad too short document numbers --- client/src/cmdhf14b.c | 3 +- client/src/cmdhfemrtd.c | 61 +++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index eb1a4f67a..e21509fe8 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -26,6 +26,7 @@ #include "mifare/ndef.h" // NDEFRecordsDecodeAndPrint #define TIMEOUT 2000 +#define APDU_TIMEOUT 4000 // iso14b apdu input frame length static uint16_t apdu_frame_length = 0; @@ -1438,7 +1439,7 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, 0, 0, NULL, 0); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, APDU_TIMEOUT)) { uint8_t *recv = resp.data.asBytes; int rlen = resp.oldarg[0]; uint8_t res = resp.oldarg[1]; diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index f0f238615..1781fbb41 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -280,24 +280,24 @@ static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *outp } static void emrtd_deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t *dataout) { - PrintAndLogEx(DEBUG, "seed: %s", sprint_hex_inrow(seed, 16)); + PrintAndLogEx(DEBUG, "seed.............. %s", sprint_hex_inrow(seed, 16)); // combine seed and type uint8_t data[50]; memcpy(data, seed, length); memcpy(data + length, type, 4); - PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, length + 4)); + PrintAndLogEx(DEBUG, "data.............. %s", sprint_hex_inrow(data, length + 4)); // SHA1 the key unsigned char key[64]; mbedtls_sha1(data, length + 4, key); - PrintAndLogEx(DEBUG, "key: %s", sprint_hex_inrow(key, length + 4)); + PrintAndLogEx(DEBUG, "key............... %s", sprint_hex_inrow(key, length + 4)); // Set parity bits for (int i = 0; i < ((length + 4) / 8); i++) { mbedtls_des_key_set_parity(key + (i * 8)); } - PrintAndLogEx(DEBUG, "post-parity key: %s", sprint_hex_inrow(key, 20)); + PrintAndLogEx(DEBUG, "post-parity key... %s", sprint_hex_inrow(key, 20)); memcpy(dataout, &key, length); } @@ -320,9 +320,7 @@ static int emrtd_get_challenge(int length, uint8_t *dataout, int *dataoutlen, bo static int emrtd_external_authenticate(uint8_t *data, int length, uint8_t *dataout, int *dataoutlen, bool use_14b) { char cmd[100]; - sprintf(cmd, "00%s0000%02X%s%02X", EMRTD_EXTERNAL_AUTHENTICATE, length, sprint_hex_inrow(data, length), length); - return emrtd_exchange_commands(cmd, dataout, dataoutlen, false, true, use_14b); } @@ -724,9 +722,10 @@ static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, cons static void rng(int length, uint8_t *dataout) { // Do very very secure prng operations - for (int i = 0; i < (length / 4); i++) { - num_to_bytes(prng_successor(msclock() + i, 32), 4, &dataout[i * 4]); - } + //for (int i = 0; i < (length / 4); i++) { + // num_to_bytes(prng_successor(msclock() + i, 32), 4, &dataout[i * 4]); + //} + memset(dataout, 0x00, length); } static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t *ssc, uint8_t *ks_enc, uint8_t *ks_mac, bool use_14b) { @@ -743,9 +742,9 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t rng(8, rnd_ifd); rng(16, k_ifd); - PrintAndLogEx(DEBUG, "doc: %s", documentnumber); - PrintAndLogEx(DEBUG, "dob: %s", dob); - PrintAndLogEx(DEBUG, "exp: %s", expiry); + PrintAndLogEx(DEBUG, "doc............... " _GREEN_("%s"), documentnumber); + PrintAndLogEx(DEBUG, "dob............... " _GREEN_("%s"), dob); + PrintAndLogEx(DEBUG, "exp............... " _GREEN_("%s"), expiry); char documentnumbercd = emrtd_calculate_check_digit(documentnumber); char dobcd = emrtd_calculate_check_digit(dob); @@ -753,40 +752,40 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t char kmrz[25]; sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); - PrintAndLogEx(DEBUG, "kmrz: %s", kmrz); + PrintAndLogEx(DEBUG, "kmrz.............. " _GREEN_("%s"), kmrz); uint8_t kseed[16] = { 0x00 }; mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); - PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); + PrintAndLogEx(DEBUG, "kseed (sha1)...... %s ", sprint_hex_inrow(kseed, 16)); emrtd_deskey(kseed, KENC_type, 16, kenc); emrtd_deskey(kseed, KMAC_type, 16, kmac); - PrintAndLogEx(DEBUG, "kenc: %s", sprint_hex_inrow(kenc, 16)); - PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 16)); + PrintAndLogEx(DEBUG, "kenc.............. %s", sprint_hex_inrow(kenc, 16)); + PrintAndLogEx(DEBUG, "kmac.............. %s", sprint_hex_inrow(kmac, 16)); // Get Challenge if (emrtd_get_challenge(8, rnd_ic, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Couldn't get challenge."); return false; } - PrintAndLogEx(DEBUG, "rnd_ic: %s", sprint_hex_inrow(rnd_ic, 8)); + PrintAndLogEx(DEBUG, "rnd_ic............ %s", sprint_hex_inrow(rnd_ic, 8)); memcpy(S, rnd_ifd, 8); memcpy(S + 8, rnd_ic, 8); memcpy(S + 16, k_ifd, 16); - PrintAndLogEx(DEBUG, "S: %s", sprint_hex_inrow(S, 32)); + PrintAndLogEx(DEBUG, "S................. %s", sprint_hex_inrow(S, 32)); uint8_t iv[8] = { 0x00 }; uint8_t e_ifd[32] = { 0x00 }; des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd); - PrintAndLogEx(DEBUG, "e_ifd: %s", sprint_hex_inrow(e_ifd, 32)); + PrintAndLogEx(DEBUG, "e_ifd............. %s", sprint_hex_inrow(e_ifd, 32)); uint8_t m_ifd[8] = { 0x00 }; retail_mac(kmac, e_ifd, 32, m_ifd); - PrintAndLogEx(DEBUG, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8)); + PrintAndLogEx(DEBUG, "m_ifd............. %s", sprint_hex_inrow(m_ifd, 8)); uint8_t cmd_data[40]; memcpy(cmd_data, e_ifd, 32); @@ -801,7 +800,7 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t uint8_t dec_output[32] = { 0x00 }; des3_decrypt_cbc(iv, kenc, response, 32, dec_output); - PrintAndLogEx(DEBUG, "dec_output: %s", sprint_hex_inrow(dec_output, 32)); + PrintAndLogEx(DEBUG, "dec_output........ %s", sprint_hex_inrow(dec_output, 32)); if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) { PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match."); @@ -815,18 +814,18 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t kseed[x] = k_ifd[x] ^ k_icc[x]; } - PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); + PrintAndLogEx(DEBUG, "kseed............ %s", sprint_hex_inrow(kseed, 16)); emrtd_deskey(kseed, KENC_type, 16, ks_enc); emrtd_deskey(kseed, KMAC_type, 16, ks_mac); - PrintAndLogEx(DEBUG, "ks_enc: %s", sprint_hex_inrow(ks_enc, 16)); - PrintAndLogEx(DEBUG, "ks_mac: %s", sprint_hex_inrow(ks_mac, 16)); + PrintAndLogEx(DEBUG, "ks_enc........ %s", sprint_hex_inrow(ks_enc, 16)); + PrintAndLogEx(DEBUG, "ks_mac........ %s", sprint_hex_inrow(ks_mac, 16)); memcpy(ssc, rnd_ic + 4, 4); memcpy(ssc + 4, rnd_ifd + 4, 4); - PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); + PrintAndLogEx(DEBUG, "ssc........... %s", sprint_hex_inrow(ssc, 8)); return true; } @@ -1023,9 +1022,17 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { // Go through all args, if even one isn't supplied, mark BAC as unavailable if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) { BAC = false; - } else if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { + } else { + if ( slen != 9) { + memset(docnum + slen, 0x3c, 9 - slen); + } + } + + if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { BAC = false; - } else if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { + } + + if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { BAC = false; } From 9066aa1d261c6e77d728693a3b465c3be78b0e3d Mon Sep 17 00:00:00 2001 From: Ave Date: Thu, 17 Dec 2020 03:44:39 +0300 Subject: [PATCH 05/11] emrtd: Support passports on hf emrtd info --- client/src/cmdhfemrtd.c | 216 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 1781fbb41..f77cc6676 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -602,6 +602,35 @@ static bool emrtd_ef_com_get_file_list(uint8_t *datain, int *datainlen, uint8_t return false; } +static bool emrtd_ef_dg1_get_mrz(uint8_t *datain, int *datainlen, uint8_t *dataout, int *dataoutlen) { + // TODO: combine with emrtd_ef_com_get_file_list + int offset = 2; + int elementidlen = 0; + int elementlen = 0; + while (offset < *datainlen) { + PrintAndLogEx(DEBUG, "emrtd_ef_dg1_get_mrz, offset: %i, data: %X", offset, *(datain + offset)); + // Determine element ID length to set as offset on asn1datalength + if (*(datain + offset) == 0x5f) { + elementidlen = 2; + } else { + elementidlen = 1; + } + + // Get the length of the element + elementlen = emrtd_get_asn1_data_length(datain + offset, *datainlen - offset, elementidlen); + + // If the element is what we're looking for, get the data and return true + if (*(datain + offset) == 0x5f && *(datain + offset + 1) == 0x1f) { + *dataoutlen = elementlen; + memcpy(dataout, datain + offset + elementidlen + 1, elementlen); + return true; + } + offset += elementidlen + elementlen + 1; + } + // Return false if we can't find the relevant element + return false; +} + static bool emrtd_file_tag_to_file_id(uint8_t *datain, char *filenameout, char *dataout) { // imagine bothering with a hashmap or writing good code // couldn't be me @@ -966,6 +995,135 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab return PM3_SUCCESS; } +static bool emrtd_mrz_verify_check_digit(char *mrz, int offset, int datalen) { + char tempdata[90] = { 0x00 }; + char calculated_check_digit[3]; + + memcpy(tempdata, mrz + offset, datalen); + int check_digit = emrtd_calculate_check_digit(tempdata); + + sprintf(calculated_check_digit, "%i", check_digit); + + PrintAndLogEx(DEBUG, "emrtd_mrz_verify_check_digit, expected: %c", mrz[offset + datalen]); + PrintAndLogEx(DEBUG, "emrtd_mrz_verify_check_digit, calculated: %c", calculated_check_digit[0]); + + return calculated_check_digit[0] == mrz[offset + datalen]; +} + +static void emrtd_print_legal_sex(char *legal_sex) { + char sex[12] = { 0x00 }; + switch (*legal_sex) { + case 'M': + strncpy(sex, "Male", 5); + break; + case 'F': + strncpy(sex, "Female", 7); + break; + case '<': + strncpy(sex, "Unspecified", 12); + break; + } + PrintAndLogEx(SUCCESS, "Legal Sex Marker......: " _YELLOW_("%s"), sex); +} + +static int emrtd_mrz_determine_length(char *mrz, int offset, int max_length) { + int i; + for (i = max_length; i >= 0; i--) { + if (mrz[offset + i - 1] != '<') { + break; + } + } + return i; +} + +static int emrtd_mrz_determine_separator(char *mrz, int offset, int max_length) { + int i; + for (i = max_length; i >= 0; i--) { + if (mrz[offset + i - 1] == '<' && mrz[offset + i] == '<') { + break; + } + } + return i - 1; +} + +static void emrtd_print_optional_elements(char *mrz, int offset) { + int i = emrtd_mrz_determine_length(mrz, offset, 14); + + // Only print optional elements if they're available + if (i != 0) { + PrintAndLogEx(SUCCESS, "Optional elements.....: " _YELLOW_("%.*s"), i, mrz + offset); + } + + if (!emrtd_mrz_verify_check_digit(mrz, offset, 14)) { + PrintAndLogEx(SUCCESS, _RED_("Optional element check digit is invalid.")); + } +} + +static void emrtd_print_passport_number(char *mrz, int offset) { + int i = emrtd_mrz_determine_length(mrz, offset, 9); + + PrintAndLogEx(SUCCESS, "Passport Number.......: " _YELLOW_("%.*s"), i, mrz + offset); + + if (!emrtd_mrz_verify_check_digit(mrz, offset, 9)) { + PrintAndLogEx(SUCCESS, _RED_("Passport number check digit is invalid.")); + } +} + +static void emrtd_print_name(char *mrz, int offset, int max_length) { + char final_name[100] = { 0x00 }; + int i = emrtd_mrz_determine_length(mrz, offset, max_length); + int sep = emrtd_mrz_determine_separator(mrz, offset, i); + int namelen = (i - (sep + 2)); + + memcpy(final_name, mrz + offset + sep + 2, namelen); + final_name[namelen] = ' '; + memcpy(final_name + namelen + 1, mrz + offset, sep); + + PrintAndLogEx(SUCCESS, "Legal Name............: " _YELLOW_("%s"), final_name); +} + +static void emrtd_mrz_convert_date(char *mrz, int offset, char *final_date, bool is_expiry) { + char temp_year[3] = { 0x00 }; + + memcpy(temp_year, mrz + offset, 2); + // If it's > 20, assume 19xx. + if (strtol(temp_year, NULL, 10) < 20 || is_expiry) { + final_date[0] = '2'; + final_date[1] = '0'; + } else { + final_date[0] = '1'; + final_date[1] = '9'; + } + + memcpy(final_date + 2, mrz + offset, 2); + final_date[4] = '-'; + memcpy(final_date + 5, mrz + offset + 2, 2); + final_date[7] = '-'; + memcpy(final_date + 8, mrz + offset + 4, 2); +} + +static void emrtd_print_dob(char *mrz, int offset) { + char final_date[12] = { 0x00 }; + emrtd_mrz_convert_date(mrz, offset, final_date, false); + + PrintAndLogEx(SUCCESS, "Date of birth.........: " _YELLOW_("%s"), final_date); + + if (!emrtd_mrz_verify_check_digit(mrz, offset, 6)) { + PrintAndLogEx(SUCCESS, _RED_("Date of Birth check digit is invalid.")); + } +} + +static void emrtd_print_expiry(char *mrz, int offset) { + char final_date[12] = { 0x00 }; + emrtd_mrz_convert_date(mrz, offset, final_date, true); + + PrintAndLogEx(SUCCESS, "Date of expiry........: " _YELLOW_("%s"), final_date); + + if (!emrtd_mrz_verify_check_digit(mrz, offset, 6)) { + PrintAndLogEx(SUCCESS, _RED_("Date of expiry check digit is invalid.")); + } +} + int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { // TODO: support just loading a dump file uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; @@ -976,11 +1134,13 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab bool BAC = false; bool use_14b = false; + int td_variant = 0; + // Select and authenticate with the eMRTD bool auth_result = emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac, &use_14b); PrintAndLogEx(SUCCESS, "Communication standard: %s", use_14b ? _YELLOW_("ISO/IEC 14443(B)") : _YELLOW_("ISO/IEC 14443(A)")); - PrintAndLogEx(SUCCESS, "BAC: %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced")); - PrintAndLogEx(SUCCESS, "Authentication result: %s", auth_result ? _GREEN_("Successful") : _RED_("Failed")); + PrintAndLogEx(SUCCESS, "BAC...................: %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced")); + PrintAndLogEx(SUCCESS, "Authentication result.: %s", auth_result ? _GREEN_("Successful") : _RED_("Failed")); if (!auth_result) { DropField(); @@ -994,6 +1154,58 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab return PM3_ESOFT; } + // MRZ on TD1 is 90 characters, 30 on each row. + // MRZ on TD3 is 88 characters, 44 on each row. + char mrz[90] = { 0x00 }; + int mrzlen = 0; + + if (emrtd_ef_dg1_get_mrz(response, &resplen, (uint8_t *) mrz, &mrzlen) == false) { + PrintAndLogEx(ERR, "Failed to read MRZ from EF_DG1."); + DropField(); + return PM3_ESOFT; + } + + // Determine and print the document type + if (mrz[0] == 'I' && mrz[1] == 'P') { + td_variant = 1; + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Passport Card")); + } else if (mrz[0] == 'I') { + td_variant = 1; + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("ID Card")); + } else if (mrz[0] == 'P') { + td_variant = 3; + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Passport")); + } else { + td_variant = 1; + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Unknown")); + PrintAndLogEx(INFO, "Assuming ID-style MRZ."); + } + PrintAndLogEx(SUCCESS, "Document Form Factor..: " _YELLOW_("TD%i"), td_variant); + + // // Print the MRZ + // if (td_variant == 1) { + // PrintAndLogEx(SUCCESS, "MRZ Row 1: " _YELLOW_("%.30s"), mrz); + // PrintAndLogEx(SUCCESS, "MRZ Row 2: " _YELLOW_("%.30s"), mrz + 30); + // PrintAndLogEx(SUCCESS, "MRZ Row 3: " _YELLOW_("%.30s"), mrz + 60); + // } else if (td_variant == 3) { + // PrintAndLogEx(SUCCESS, "MRZ Row 1: " _YELLOW_("%.44s"), mrz); + // PrintAndLogEx(SUCCESS, "MRZ Row 2: " _YELLOW_("%.44s"), mrz + 44); + // } + + // Passport form factor + if (td_variant == 3) { + PrintAndLogEx(SUCCESS, "Issuing state.........: " _YELLOW_("%.3s"), mrz + 2); + emrtd_print_name(mrz, 5, 38); + emrtd_print_passport_number(mrz, 44); + PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 44 + 10); + emrtd_print_dob(mrz, 44 + 13); + emrtd_print_legal_sex(&mrz[44 + 20]); + emrtd_print_expiry(mrz, 44 + 21); + emrtd_print_optional_elements(mrz, 44 + 28); + + // TODO: verify composite check digit here + } + DropField(); return PM3_SUCCESS; } From 2e3e4835ed6e77d56eaaa0d42cd4b4d1e21561e0 Mon Sep 17 00:00:00 2001 From: Ave Date: Thu, 17 Dec 2020 04:03:09 +0300 Subject: [PATCH 06/11] emrtd: Support ID cards on hf emrtd info --- client/src/cmdhfemrtd.c | 50 +++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index f77cc6676..2b33aa368 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1046,26 +1046,26 @@ static int emrtd_mrz_determine_separator(char *mrz, int offset, int max_length) return i - 1; } -static void emrtd_print_optional_elements(char *mrz, int offset) { - int i = emrtd_mrz_determine_length(mrz, offset, 14); +static void emrtd_print_optional_elements(char *mrz, int offset, int length, bool verify_check_digit) { + int i = emrtd_mrz_determine_length(mrz, offset, length); // Only print optional elements if they're available if (i != 0) { PrintAndLogEx(SUCCESS, "Optional elements.....: " _YELLOW_("%.*s"), i, mrz + offset); } - if (!emrtd_mrz_verify_check_digit(mrz, offset, 14)) { + if (verify_check_digit && !emrtd_mrz_verify_check_digit(mrz, offset, length)) { PrintAndLogEx(SUCCESS, _RED_("Optional element check digit is invalid.")); } } -static void emrtd_print_passport_number(char *mrz, int offset) { +static void emrtd_print_document_number(char *mrz, int offset) { int i = emrtd_mrz_determine_length(mrz, offset, 9); - PrintAndLogEx(SUCCESS, "Passport Number.......: " _YELLOW_("%.*s"), i, mrz + offset); + PrintAndLogEx(SUCCESS, "Document Number.......: " _YELLOW_("%.*s"), i, mrz + offset); if (!emrtd_mrz_verify_check_digit(mrz, offset, 9)) { - PrintAndLogEx(SUCCESS, _RED_("Passport number check digit is invalid.")); + PrintAndLogEx(SUCCESS, _RED_("Document number check digit is invalid.")); } } @@ -1182,28 +1182,40 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab } PrintAndLogEx(SUCCESS, "Document Form Factor..: " _YELLOW_("TD%i"), td_variant); - // // Print the MRZ - // if (td_variant == 1) { - // PrintAndLogEx(SUCCESS, "MRZ Row 1: " _YELLOW_("%.30s"), mrz); - // PrintAndLogEx(SUCCESS, "MRZ Row 2: " _YELLOW_("%.30s"), mrz + 30); - // PrintAndLogEx(SUCCESS, "MRZ Row 3: " _YELLOW_("%.30s"), mrz + 60); - // } else if (td_variant == 3) { - // PrintAndLogEx(SUCCESS, "MRZ Row 1: " _YELLOW_("%.44s"), mrz); - // PrintAndLogEx(SUCCESS, "MRZ Row 2: " _YELLOW_("%.44s"), mrz + 44); - // } + // Print the MRZ + if (td_variant == 1) { + PrintAndLogEx(DEBUG, "MRZ Row 1: " _YELLOW_("%.30s"), mrz); + PrintAndLogEx(DEBUG, "MRZ Row 2: " _YELLOW_("%.30s"), mrz + 30); + PrintAndLogEx(DEBUG, "MRZ Row 3: " _YELLOW_("%.30s"), mrz + 60); + } else if (td_variant == 3) { + PrintAndLogEx(DEBUG, "MRZ Row 1: " _YELLOW_("%.44s"), mrz); + PrintAndLogEx(DEBUG, "MRZ Row 2: " _YELLOW_("%.44s"), mrz + 44); + } + + PrintAndLogEx(SUCCESS, "Issuing state.........: " _YELLOW_("%.3s"), mrz + 2); - // Passport form factor if (td_variant == 3) { - PrintAndLogEx(SUCCESS, "Issuing state.........: " _YELLOW_("%.3s"), mrz + 2); + // Passport form factor emrtd_print_name(mrz, 5, 38); - emrtd_print_passport_number(mrz, 44); + emrtd_print_document_number(mrz, 44); PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 44 + 10); emrtd_print_dob(mrz, 44 + 13); emrtd_print_legal_sex(&mrz[44 + 20]); emrtd_print_expiry(mrz, 44 + 21); - emrtd_print_optional_elements(mrz, 44 + 28); + emrtd_print_optional_elements(mrz, 44 + 28, 14, true); // TODO: verify composite check digit here + } else if (td_variant == 1) { + // ID form factor + emrtd_print_document_number(mrz, 5); + emrtd_print_optional_elements(mrz, 15, 15, false); + emrtd_print_dob(mrz, 30); + emrtd_print_legal_sex(&mrz[30 + 7]); + emrtd_print_expiry(mrz, 30 + 8); + PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 30 + 15); + emrtd_print_optional_elements(mrz, 30 + 18, 11, false); + // TODO: verify composite check digit here + emrtd_print_name(mrz, 60, 30); } DropField(); From 8e72830686930ba435bee127ede6bff89503ece3 Mon Sep 17 00:00:00 2001 From: Ave Date: Thu, 17 Dec 2020 04:15:00 +0300 Subject: [PATCH 07/11] emrtd: Merge two functions into emrtd_lds_get_data_by_tag --- client/src/cmdhfemrtd.c | 41 ++++++----------------------------------- 1 file changed, 6 insertions(+), 35 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 2b33aa368..2d4d8a8ee 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -574,14 +574,14 @@ static int emrtd_read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uin return true; } -static bool emrtd_ef_com_get_file_list(uint8_t *datain, int *datainlen, uint8_t *dataout, int *dataoutlen) { +static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int *datainlen, uint8_t *dataout, int *dataoutlen, int tag1, int tag2, bool twobytetag) { int offset = 2; int elementidlen = 0; int elementlen = 0; while (offset < *datainlen) { - PrintAndLogEx(DEBUG, "ef_com_get_file_list, offset: %i, data: %X", offset, *(datain + offset)); + PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, offset: %i, data: %X", offset, *(datain + offset)); // Determine element ID length to set as offset on asn1datalength - if (*(datain + offset) == 0x5f) { + if ((*(datain + offset) == 0x5f) || (*(datain + offset) == 0x7f)) { elementidlen = 2; } else { elementidlen = 1; @@ -591,36 +591,7 @@ static bool emrtd_ef_com_get_file_list(uint8_t *datain, int *datainlen, uint8_t elementlen = emrtd_get_asn1_data_length(datain + offset, *datainlen - offset, elementidlen); // If the element is what we're looking for, get the data and return true - if (*(datain + offset) == 0x5c) { - *dataoutlen = elementlen; - memcpy(dataout, datain + offset + elementidlen + 1, elementlen); - return true; - } - offset += elementidlen + elementlen + 1; - } - // Return false if we can't find the relevant element - return false; -} - -static bool emrtd_ef_dg1_get_mrz(uint8_t *datain, int *datainlen, uint8_t *dataout, int *dataoutlen) { - // TODO: combine with emrtd_ef_com_get_file_list - int offset = 2; - int elementidlen = 0; - int elementlen = 0; - while (offset < *datainlen) { - PrintAndLogEx(DEBUG, "emrtd_ef_dg1_get_mrz, offset: %i, data: %X", offset, *(datain + offset)); - // Determine element ID length to set as offset on asn1datalength - if (*(datain + offset) == 0x5f) { - elementidlen = 2; - } else { - elementidlen = 1; - } - - // Get the length of the element - elementlen = emrtd_get_asn1_data_length(datain + offset, *datainlen - offset, elementidlen); - - // If the element is what we're looking for, get the data and return true - if (*(datain + offset) == 0x5f && *(datain + offset + 1) == 0x1f) { + if (*(datain + offset) == tag1 && (!twobytetag || *(datain + offset + 1) == tag2)) { *dataoutlen = elementlen; memcpy(dataout, datain + offset + elementidlen + 1, elementlen); return true; @@ -968,7 +939,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab uint8_t filelist[50]; int filelistlen = 0; - if (emrtd_ef_com_get_file_list(response, &resplen, filelist, &filelistlen) == false) { + if (!emrtd_lds_get_data_by_tag(response, &resplen, filelist, &filelistlen, 0x5c, 0x00, false)) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); DropField(); return PM3_ESOFT; @@ -1159,7 +1130,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab char mrz[90] = { 0x00 }; int mrzlen = 0; - if (emrtd_ef_dg1_get_mrz(response, &resplen, (uint8_t *) mrz, &mrzlen) == false) { + if (!emrtd_lds_get_data_by_tag(response, &resplen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true)) { PrintAndLogEx(ERR, "Failed to read MRZ from EF_DG1."); DropField(); return PM3_ESOFT; From b362101b54b9655dc8db5fddbc6fe23ab4a2cecc Mon Sep 17 00:00:00 2001 From: Ave Date: Thu, 17 Dec 2020 04:19:54 +0300 Subject: [PATCH 08/11] emrtd: Split emrtd_mrz_verify_check_digit into two funcs --- client/src/cmdhfemrtd.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 2d4d8a8ee..747f9b042 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -966,19 +966,27 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab return PM3_SUCCESS; } -static bool emrtd_mrz_verify_check_digit(char *mrz, int offset, int datalen) { +static bool emrtd_compare_check_digit(char *datain, int datalen, char expected_check_digit) { char tempdata[90] = { 0x00 }; char calculated_check_digit[3]; - memcpy(tempdata, mrz + offset, datalen); + memcpy(tempdata, datain, datalen); int check_digit = emrtd_calculate_check_digit(tempdata); sprintf(calculated_check_digit, "%i", check_digit); - PrintAndLogEx(DEBUG, "emrtd_mrz_verify_check_digit, expected: %c", mrz[offset + datalen]); - PrintAndLogEx(DEBUG, "emrtd_mrz_verify_check_digit, calculated: %c", calculated_check_digit[0]); + PrintAndLogEx(DEBUG, "emrtd_compare_check_digit, expected: %c", expected_check_digit); + PrintAndLogEx(DEBUG, "emrtd_compare_check_digit, calculated: %c", calculated_check_digit[0]); - return calculated_check_digit[0] == mrz[offset + datalen]; + return calculated_check_digit[0] == expected_check_digit; +} + +static bool emrtd_mrz_verify_check_digit(char *mrz, int offset, int datalen) { + char tempdata[90] = { 0x00 }; + + memcpy(tempdata, mrz + offset, datalen); + + return emrtd_compare_check_digit(tempdata, datalen, mrz[offset + datalen]); } static void emrtd_print_legal_sex(char *legal_sex) { @@ -1096,7 +1104,6 @@ static void emrtd_print_expiry(char *mrz, int offset) { } int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { - // TODO: support just loading a dump file uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; int resplen = 0; uint8_t ssc[8] = { 0x00 }; From 8e6736edd1b7879b33bd6790960f79c68a9788ef Mon Sep 17 00:00:00 2001 From: Ave Date: Thu, 17 Dec 2020 04:28:26 +0300 Subject: [PATCH 09/11] emrtd: Verify composite check digits --- client/src/cmdhfemrtd.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 747f9b042..244234836 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1182,7 +1182,15 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab emrtd_print_expiry(mrz, 44 + 21); emrtd_print_optional_elements(mrz, 44 + 28, 14, true); - // TODO: verify composite check digit here + // Calculate and verify composite check digit + char composite_check_data[50] = { 0x00 }; + memcpy(composite_check_data, mrz, 9); + memcpy(composite_check_data + 9, mrz + 13, 7); + memcpy(composite_check_data + 7, mrz + 21, 22); + + if (!emrtd_compare_check_digit(composite_check_data, 36, mrz[87])) { + PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid.")); + } } else if (td_variant == 1) { // ID form factor emrtd_print_document_number(mrz, 5); @@ -1192,7 +1200,12 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab emrtd_print_expiry(mrz, 30 + 8); PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 30 + 15); emrtd_print_optional_elements(mrz, 30 + 18, 11, false); - // TODO: verify composite check digit here + + // Calculate and verify composite check digit + if (!emrtd_compare_check_digit(mrz, 59, mrz[59])) { + PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid.")); + } + emrtd_print_name(mrz, 60, 30); } From b31a610566f62b816cc007f94436dee047459a0b Mon Sep 17 00:00:00 2001 From: Ave Date: Thu, 17 Dec 2020 05:08:56 +0300 Subject: [PATCH 10/11] emrtd: Fix passport composite + change print order --- client/src/cmdhfemrtd.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 244234836..5b1fe8981 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1174,9 +1174,9 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab if (td_variant == 3) { // Passport form factor + PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 44 + 10); emrtd_print_name(mrz, 5, 38); emrtd_print_document_number(mrz, 44); - PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 44 + 10); emrtd_print_dob(mrz, 44 + 13); emrtd_print_legal_sex(&mrz[44 + 20]); emrtd_print_expiry(mrz, 44 + 21); @@ -1184,29 +1184,28 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab // Calculate and verify composite check digit char composite_check_data[50] = { 0x00 }; - memcpy(composite_check_data, mrz, 9); - memcpy(composite_check_data + 9, mrz + 13, 7); - memcpy(composite_check_data + 7, mrz + 21, 22); + memcpy(composite_check_data, mrz + 44, 10); + memcpy(composite_check_data + 10, mrz + 44 + 13, 7); + memcpy(composite_check_data + 17, mrz + 44 + 21, 23); - if (!emrtd_compare_check_digit(composite_check_data, 36, mrz[87])) { + if (!emrtd_compare_check_digit(composite_check_data, 39, mrz[87])) { PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid.")); } } else if (td_variant == 1) { // ID form factor + PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 30 + 15); + emrtd_print_name(mrz, 60, 30); emrtd_print_document_number(mrz, 5); - emrtd_print_optional_elements(mrz, 15, 15, false); emrtd_print_dob(mrz, 30); emrtd_print_legal_sex(&mrz[30 + 7]); emrtd_print_expiry(mrz, 30 + 8); - PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 30 + 15); + emrtd_print_optional_elements(mrz, 15, 15, false); emrtd_print_optional_elements(mrz, 30 + 18, 11, false); // Calculate and verify composite check digit if (!emrtd_compare_check_digit(mrz, 59, mrz[59])) { PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid.")); } - - emrtd_print_name(mrz, 60, 30); } DropField(); From 8cb25b742acb49a14b0d7035a732c51cef2575c2 Mon Sep 17 00:00:00 2001 From: Ave Date: Thu, 17 Dec 2020 05:12:54 +0300 Subject: [PATCH 11/11] emrtd: Remove redundant padding code --- client/src/cmdhfemrtd.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 5b1fe8981..50492e3ee 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -221,12 +221,6 @@ static void des3_decrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inpu mbedtls_des3_free(&ctx); } -static void emrtd_pad_docnum(char *input) { - for (int i = strlen(input); i < 9; i++) { - input[i] = '<'; - } -} - static int pad_block(uint8_t *input, int inputlen, uint8_t *output) { uint8_t padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -834,8 +828,6 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; int resplen = 0; - emrtd_pad_docnum(documentnumber); - // Try to 14a SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; @@ -1237,7 +1229,8 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) { BAC = false; } else { - if ( slen != 9) { + if (slen != 9) { + // Pad to 9 with < memset(docnum + slen, 0x3c, 9 - slen); } }