From fbed338dcef7f98fb2d921fa274a621d70000a40 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 02:39:48 +0300 Subject: [PATCH 01/10] emrtd: don't check for EF_CardAccess, and dump it on hf emrtd dump --- client/src/cmdhfemrtd.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index bdf19bfca..8d0aaecaa 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -937,15 +937,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; - // 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); - PrintAndLogEx(INFO, "Read EF_CardAccess, len: %i.", resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - } else { - PrintAndLogEx(INFO, "PACE unsupported. Will not read EF_CardAccess."); - } - // Select MRTD applet if (emrtd_select_file(EMRTD_P1_SELECT_BY_NAME, EMRTD_AID_MRTD, *use_14b) == false) { PrintAndLogEx(ERR, "Couldn't select the MRTD application."); @@ -1041,8 +1032,12 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab emrtd_dump_file(ks_enc, ks_mac, ssc, file_id, file_name, BAC, use_14b); } - // Dump EF_SOD + // Dump EF_SOD and EF_CardAccess (if available) emrtd_dump_file(ks_enc, ks_mac, ssc, EMRTD_EF_SOD, "EF_SOD", BAC, use_14b); + if (!emrtd_dump_file(ks_enc, ks_mac, ssc, EMRTD_EF_CARDACCESS, "EF_CardAccess", BAC, use_14b)) { + PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE."); + PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about."); + } DropField(); return PM3_SUCCESS; From ffb71118359a6d5a99eb3c5157fdd98aada7095c Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 03:06:42 +0300 Subject: [PATCH 02/10] emrtd: Ensure that DOB and doc expiry lengths are correct --- client/src/cmdhfemrtd.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 8d0aaecaa..2f409d5e4 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1324,10 +1324,22 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { BAC = false; - } + } else { + if (slen != 6) { + PrintAndLogEx(ERR, "Date of Birth length is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + return PM3_ESOFT; + } + } if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { BAC = false; + } else { + if (slen != 6) { + PrintAndLogEx(ERR, "Document expiry length is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + return PM3_ESOFT; + } } CLIParserFree(ctx); @@ -1359,17 +1371,29 @@ static int cmd_hf_emrtd_info(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) { memset(docnum + slen, 0x3c, 9 - slen); } } if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { BAC = false; - } + } else { + if (slen != 6) { + PrintAndLogEx(ERR, "Date of Birth length is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + return PM3_ESOFT; + } + } if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { BAC = false; + } else { + if (slen != 6) { + PrintAndLogEx(ERR, "Document expiry length is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + return PM3_ESOFT; + } } CLIParserFree(ctx); From 45d55106c6c9689ac5e0dc741a7ce50423cb9be7 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 03:13:06 +0300 Subject: [PATCH 03/10] emrtd: Auto-convert document number to uppercase --- client/src/cmdhfemrtd.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 2f409d5e4..ae38ebc73 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1291,6 +1291,15 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab return PM3_SUCCESS; } +static void text_to_upper(uint8_t *data, int datalen) { + // Loop over text to make lowercase text uppercase + for (int i = 0; i < datalen; i++) { + if ('a' <= data[i] && data[i] <= 'z') { + data[i] -= 32; + } + } +} + static int cmd_hf_emrtd_dump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf emrtd dump", @@ -1316,6 +1325,7 @@ 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 { + text_to_upper(docnum, slen); if (slen != 9) { // Pad to 9 with < memset(docnum + slen, 0x3c, 9 - slen); @@ -1371,6 +1381,8 @@ static int cmd_hf_emrtd_info(const char *Cmd) { if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) { BAC = false; } else { + text_to_upper(docnum, slen); + PrintAndLogEx(HINT, "%.*s.", slen, (char *) docnum); if (slen != 9) { memset(docnum + slen, 0x3c, 9 - slen); } From 3312230e80d2d9f952ed234ef854909fcf3582d3 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 03:15:14 +0300 Subject: [PATCH 04/10] emrtd: Remove debug line --- client/src/cmdhfemrtd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index ae38ebc73..e77fc277b 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1382,7 +1382,6 @@ static int cmd_hf_emrtd_info(const char *Cmd) { BAC = false; } else { text_to_upper(docnum, slen); - PrintAndLogEx(HINT, "%.*s.", slen, (char *) docnum); if (slen != 9) { memset(docnum + slen, 0x3c, 9 - slen); } From 027f4e5adc78edbed67694c992c88a63b8c76077 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 03:26:26 +0300 Subject: [PATCH 05/10] emrtd: Use toupper --- client/src/cmdhfemrtd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index e77fc277b..dc07744fe 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1294,9 +1294,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab static void text_to_upper(uint8_t *data, int datalen) { // Loop over text to make lowercase text uppercase for (int i = 0; i < datalen; i++) { - if ('a' <= data[i] && data[i] <= 'z') { - data[i] -= 32; - } + data[i] = toupper(data[i]); } } From 79e9875b43bf893ba818a918ae850a569bb91ce2 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 03:29:21 +0300 Subject: [PATCH 06/10] emrtd: Validate date further --- client/src/cmdhfemrtd.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index dc07744fe..5f6c12891 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1298,6 +1298,22 @@ static void text_to_upper(uint8_t *data, int datalen) { } } +static bool validate_date(uint8_t *data, int datalen) { + // Date has to be 6 chars + if (datalen != 6) { + return false; + } + + // Check for valid date and month numbers + char temp[4] = { 0x00 }; + memcpy(temp, data + 2, 2); + int month = (int) strtol(temp, NULL, 10); + memcpy(temp, data + 4, 2); + int day = (int) strtol(temp, NULL, 10); + + return !(day <= 0 || day > 31 || month <= 0 || month > 12); +} + static int cmd_hf_emrtd_dump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf emrtd dump", @@ -1333,8 +1349,8 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { BAC = false; } else { - if (slen != 6) { - PrintAndLogEx(ERR, "Date of Birth length is incorrect, cannot continue."); + if (!validate_date(dob, slen)) { + PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); PrintAndLogEx(HINT, "Use the format YYMMDD."); return PM3_ESOFT; } @@ -1343,8 +1359,8 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { BAC = false; } else { - if (slen != 6) { - PrintAndLogEx(ERR, "Document expiry length is incorrect, cannot continue."); + if (!validate_date(expiry, slen)) { + PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); PrintAndLogEx(HINT, "Use the format YYMMDD."); return PM3_ESOFT; } @@ -1388,8 +1404,8 @@ static int cmd_hf_emrtd_info(const char *Cmd) { if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { BAC = false; } else { - if (slen != 6) { - PrintAndLogEx(ERR, "Date of Birth length is incorrect, cannot continue."); + if (!validate_date(dob, slen)) { + PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); PrintAndLogEx(HINT, "Use the format YYMMDD."); return PM3_ESOFT; } @@ -1398,8 +1414,8 @@ static int cmd_hf_emrtd_info(const char *Cmd) { if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { BAC = false; } else { - if (slen != 6) { - PrintAndLogEx(ERR, "Document expiry length is incorrect, cannot continue."); + if (!validate_date(expiry, slen)) { + PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); PrintAndLogEx(HINT, "Use the format YYMMDD."); return PM3_ESOFT; } From 9b4e0c212f880275494197f4fb976f465b45e224 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 04:26:39 +0300 Subject: [PATCH 07/10] emrtd: Remove unnecessary memcpys from two dump functions --- client/src/cmdhfemrtd.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 5f6c12891..15983b2f9 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -711,18 +711,17 @@ static bool emrtd_select_and_read(uint8_t *dataout, int *dataoutlen, const char } static bool emrtd_dump_ef_dg2(uint8_t *file_contents, int file_length) { - uint8_t data[EMRTD_MAX_FILE_SIZE]; - int datalen = 0; + int offset, datalen = 0; // This is a hacky impl that just looks for the image header. I'll improve it eventually. // based on mrpkey.py // FF D8 FF E0 -> JPEG // 00 00 00 0C 6A 50 -> JPEG 2000 - for (int i = 0; i < file_length - 6; i++) { - if ((file_contents[i] == 0xFF && file_contents[i + 1] == 0xD8 && file_contents[i + 2] == 0xFF && file_contents[i + 3] == 0xE0) || - (file_contents[i] == 0x00 && file_contents[i + 1] == 0x00 && file_contents[i + 2] == 0x00 && file_contents[i + 3] == 0x0C && file_contents[i + 4] == 0x6A && file_contents[i + 5] == 0x50)) { - datalen = file_length - i; - memcpy(data, file_contents + i, datalen); + // Note: Doing file_length - 6 to account for the longest data we're checking. + for (offset = 0; offset < file_length - 6; offset++) { + if ((file_contents[offset] == 0xFF && file_contents[offset + 1] == 0xD8 && file_contents[offset + 2] == 0xFF && file_contents[offset + 3] == 0xE0) || + (file_contents[offset] == 0x00 && file_contents[offset + 1] == 0x00 && file_contents[offset + 2] == 0x00 && file_contents[offset + 3] == 0x0C && file_contents[offset + 4] == 0x6A && file_contents[offset + 5] == 0x50)) { + datalen = file_length - offset; break; } } @@ -732,7 +731,7 @@ static bool emrtd_dump_ef_dg2(uint8_t *file_contents, int file_length) { return false; } - saveFile("EF_DG2", ".jpg", data, datalen); + saveFile("EF_DG2", ".jpg", file_contents + offset, datalen); return true; } @@ -756,19 +755,15 @@ static bool emrtd_dump_ef_dg5(uint8_t *file_contents, int file_length) { } static bool emrtd_dump_ef_sod(uint8_t *file_contents, int file_length) { - uint8_t data[EMRTD_MAX_FILE_SIZE]; - int fieldlen = emrtd_get_asn1_field_length(file_contents, file_length, 1); int datalen = emrtd_get_asn1_data_length(file_contents, file_length, 1); - if (fieldlen + 1 < EMRTD_MAX_FILE_SIZE) { - memcpy(data, file_contents + fieldlen + 1, datalen); - } else { + if (fieldlen + 1 > EMRTD_MAX_FILE_SIZE) { PrintAndLogEx(ERR, "error (emrtd_dump_ef_sod) fieldlen out-of-bounds"); return false; } - saveFile("EF_SOD", ".p7b", data, datalen); + saveFile("EF_SOD", ".p7b", file_contents + fieldlen + 1, datalen); return true; } From cd21765acc045175cda637227c201df8dc597726 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 04:28:14 +0300 Subject: [PATCH 08/10] emrtd: Move EF_CardAccess dump to before auth to ensure that it is dumped even without auth --- client/src/cmdhfemrtd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 15983b2f9..3dc567d6c 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -988,6 +988,12 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab return PM3_ESOFT; } + // Dump EF_CardAccess (if available) + if (!emrtd_dump_file(ks_enc, ks_mac, ssc, EMRTD_EF_CARDACCESS, "EF_CardAccess", BAC, use_14b)) { + PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE."); + PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about."); + } + // Authenticate with the eMRTD if (!emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac, &use_14b)) { DropField(); @@ -1027,12 +1033,8 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab emrtd_dump_file(ks_enc, ks_mac, ssc, file_id, file_name, BAC, use_14b); } - // Dump EF_SOD and EF_CardAccess (if available) + // Dump EF_SOD emrtd_dump_file(ks_enc, ks_mac, ssc, EMRTD_EF_SOD, "EF_SOD", BAC, use_14b); - if (!emrtd_dump_file(ks_enc, ks_mac, ssc, EMRTD_EF_CARDACCESS, "EF_CardAccess", BAC, use_14b)) { - PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE."); - PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about."); - } DropField(); return PM3_SUCCESS; From 69a124762a01a1414183c1dcf691589474978ef8 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 04:42:09 +0300 Subject: [PATCH 09/10] emrtd: Code cleanup on emrtd_bump_ssc --- client/src/cmdhfemrtd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 3dc567d6c..6cf07b1ae 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -331,11 +331,11 @@ static void emrtd_bump_ssc(uint8_t *ssc) { if ((*(ssc + i)) == 0xFF) { // Set anything already FF to 0, we'll do + 1 on num to left anyways (*(ssc + i)) = 0; - continue; + } else { + (*(ssc + i)) += 1; + PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8)); + return; } - (*(ssc + i)) += 1; - PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8)); - return; } } From 4f3d3f8ac47892f409d8a20a00560f4e3b106346 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 18 Dec 2020 05:03:06 +0300 Subject: [PATCH 10/10] emrtd: Fix insecure read on emrtd_read_file --- client/src/cmdhfemrtd.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 6cf07b1ae..bb89b91b9 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -526,7 +526,7 @@ static int emrtd_read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uin int toread = 4; int offset = 0; - if (use_secure == true) { + if (use_secure) { if (_emrtd_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, response, &resplen, use_14b) == false) { return false; } @@ -546,12 +546,12 @@ static int emrtd_read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uin toread = 118; } - if (kenc == NULL) { - if (_emrtd_read_binary(offset, toread, tempresponse, &tempresplen, use_14b) == false) { + if (use_secure) { + if (_emrtd_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen, use_14b) == false) { return false; } } else { - if (_emrtd_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen, use_14b) == false) { + if (_emrtd_read_binary(offset, toread, tempresponse, &tempresplen, use_14b) == false) { return false; } } @@ -591,8 +591,7 @@ static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int *datainlen, uint8_t * // If the element is what we're looking for, get the data and return true if (*(datain + offset) == tag1 && (!twobytetag || *(datain + offset + 1) == tag2)) { - - if ( *datainlen > e_datalen) { + if (*datainlen > e_datalen) { *dataoutlen = e_datalen; memcpy(dataout, datain + offset + e_idlen + e_fieldlen, e_datalen); return true; @@ -691,7 +690,7 @@ static bool emrtd_file_tag_to_file_id(uint8_t *datain, char *filenameout, char * } static bool emrtd_select_and_read(uint8_t *dataout, int *dataoutlen, const char *file, uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, bool use_secure, bool use_14b) { - if (use_secure == true) { + if (use_secure) { if (emrtd_secure_select_file(ks_enc, ks_mac, ssc, EMRTD_P1_SELECT_BY_EF, file, use_14b) == false) { PrintAndLogEx(ERR, "Failed to secure select %s.", file); return false;