From c3cc3b2a80a12aae82c2a82b57a0ebdcb888f0c9 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 22 Dec 2020 02:22:28 +0300 Subject: [PATCH 01/21] emrtd: split lds tag det to a different func and improve emrtd_lds_get_data_by_tag --- client/src/cmdhfemrtd.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 30eb4667f..1aa6079ad 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -622,8 +622,15 @@ static int emrtd_read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uin return true; } +static int emrtd_lds_determine_tag_length(uint8_t tag) { + if ((tag == 0x5F) || (tag == 0x7F)) { + return 2; + } + return 1; +} + 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 = 1; + int offset = emrtd_lds_determine_tag_length(*datain); offset += emrtd_get_asn1_field_length(datain, datainlen, offset); int e_idlen = 0; @@ -632,11 +639,7 @@ static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *d while (offset < datainlen) { 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) || (*(datain + offset) == 0x7F)) { - e_idlen = 2; - } else { - e_idlen = 1; - } + e_idlen = emrtd_lds_determine_tag_length(*(datain + offset)); // Get the length of the element e_datalen = emrtd_get_asn1_data_length(datain + offset, datainlen - offset, e_idlen); From a05bdecaaf41ba201e571d3ee667123efe3c72d5 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 22 Dec 2020 02:28:37 +0300 Subject: [PATCH 02/21] emrtd: make top tag skipping on emrtd_lds_get_data_by_tag optional --- client/src/cmdhfemrtd.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 1aa6079ad..e31c326fa 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -629,9 +629,13 @@ static int emrtd_lds_determine_tag_length(uint8_t tag) { return 1; } -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 = emrtd_lds_determine_tag_length(*datain); - offset += emrtd_get_asn1_field_length(datain, datainlen, offset); +static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *dataout, int *dataoutlen, int tag1, int tag2, bool twobytetag, bool skiptoptag) { + int offset = 0; + + if (skiptoptag) { + offset += emrtd_lds_determine_tag_length(*datain); + offset += emrtd_get_asn1_field_length(datain, datainlen, offset); + } int e_idlen = 0; int e_datalen = 0; @@ -714,7 +718,7 @@ static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length) { int datalen = 0; // If we can't find image in EF_DG5, return false. - if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true) == false) { + if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true, true) == false) { return PM3_ESOFT; } @@ -732,7 +736,7 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length) { int datalen = 0; // If we can't find image in EF_DG7, return false. - if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true) == false) { + if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true, true) == false) { return PM3_ESOFT; } @@ -998,7 +1002,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab uint8_t filelist[50]; int filelistlen = 0; - if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false)) { + if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true)) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); DropField(); return PM3_ESOFT; @@ -1224,7 +1228,7 @@ static void emrtd_print_unknown_timestamp_5f85(uint8_t *data) { static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) { uint8_t filelist[50]; int filelistlen = 0; - int res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false); + int res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true); if (!res) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); return PM3_ESOFT; @@ -1255,7 +1259,7 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { char mrz[90] = { 0x00 }; int mrzlen = 0; - if (!emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true, true)) { PrintAndLogEx(ERR, "Failed to read MRZ from EF_DG1."); return PM3_ESOFT; } @@ -1337,13 +1341,13 @@ static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG11") " -------------------"); - if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true)) { PrintAndLogEx(ERR, "Failed to read file list from EF_DG11."); return PM3_ESOFT; } for (int i = 0; i < taglistlen; i++) { - emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f); + emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true); // Don't bother with empty tags if (tagdatalen == 0) { continue; @@ -1416,13 +1420,13 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG12") " -------------------"); - if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true)) { PrintAndLogEx(ERR, "Failed to read file list from EF_DG12."); return PM3_ESOFT; } for (int i = 0; i < taglistlen; i++) { - emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f); + emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true); // Don't bother with empty tags if (tagdatalen == 0) { continue; @@ -1524,7 +1528,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab uint8_t filelist[50]; int filelistlen = 0; - if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false)) { + if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true)) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); DropField(); return PM3_ESOFT; @@ -1574,7 +1578,7 @@ int infoHF_EMRTD_offline(const char *path) { uint8_t filelist[50]; int filelistlen = 0; - res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false); + res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true); if (!res) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); free(data); From 8c1e4168cfe2a81c54aa62f93f634751092d9665 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 22 Dec 2020 02:30:26 +0300 Subject: [PATCH 03/21] emrtd: make style pass --- client/src/cmdhfemrtd.c | 43 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index e31c326fa..d0b3527af 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -61,7 +61,7 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen); typedef enum { // list must match dg_table - EF_COM=0, + EF_COM = 0, EF_DG1, EF_DG2, EF_DG3, @@ -109,7 +109,7 @@ static emrtd_dg_t dg_table[] = { }; static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) { - for (int dgi=0; dg_table[dgi].filename != NULL; dgi++) { + for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) { if (dg_table[dgi].tag == tag) { return &dg_table[dgi]; } @@ -117,7 +117,7 @@ static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) { return NULL; } static emrtd_dg_t *emrtd_fileid_to_dg(const char *file_id) { - for (int dgi=0; dg_table[dgi].filename != NULL; dgi++) { + for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) { if (strcmp(dg_table[dgi].fileid, file_id) == 0) { return &dg_table[dgi]; } @@ -698,7 +698,7 @@ static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length) { // 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)) { + (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; } @@ -752,7 +752,7 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length) { static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length) { 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) { PrintAndLogEx(ERR, "error (emrtd_dump_ef_sod) fieldlen out-of-bounds"); return PM3_SUCCESS; @@ -773,7 +773,7 @@ static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, cons PrintAndLogEx(INFO, "Read %s, len: %i.", name, resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); saveFile(name, ".BIN", response, resplen); - emrtd_dg_t * dg = emrtd_fileid_to_dg(file); + emrtd_dg_t *dg = emrtd_fileid_to_dg(file); if ((dg != NULL) && (dg->dumper != NULL)) { dg->dumper(response, resplen); } @@ -785,7 +785,7 @@ static void rng(int length, uint8_t *dataout) { //for (int i = 0; i < (length / 4); i++) { // num_to_bytes(prng_successor(msclock() + i, 32), 4, &dataout[i * 4]); //} - memset(dataout, 0x00, length); + 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) { @@ -1013,7 +1013,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab filelist[filelistlen++] = 0x77; // Dump all files in the file list for (int i = 0; i < filelistlen; i++) { - emrtd_dg_t * dg = emrtd_tag_to_dg(filelist[i]); + emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); if (dg == NULL) { PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); continue; @@ -1034,9 +1034,9 @@ static bool emrtd_compare_check_digit(char *datain, int datalen, char expected_c uint8_t check_digit = emrtd_calculate_check_digit(tempdata) + 0x30; bool res = check_digit == expected_check_digit; PrintAndLogEx(DEBUG, "emrtd_compare_check_digit, expected %c == %c calculated ( %s )" - , expected_check_digit - , check_digit - , (res) ? _GREEN_("ok") : _RED_("fail")); + , expected_check_digit + , check_digit + , (res) ? _GREEN_("ok") : _RED_("fail")); return res; } @@ -1238,7 +1238,7 @@ static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_COM") " --------------------"); for (int i = 0; i < filelistlen; i++) { - emrtd_dg_t * dg = emrtd_tag_to_dg(filelist[i]); + emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); if (dg == NULL) { PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); continue; @@ -1520,7 +1520,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab } int res = emrtd_print_ef_com_info(response, resplen); - if ( res != PM3_SUCCESS) { + if (res != PM3_SUCCESS) { DropField(); return res; } @@ -1537,7 +1537,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab filelist[filelistlen++] = 0x77; // Dump all files in the file list for (int i = 0; i < filelistlen; i++) { - emrtd_dg_t * dg = emrtd_tag_to_dg(filelist[i]); + emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); if (dg == NULL) { PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); continue; @@ -1570,7 +1570,7 @@ int infoHF_EMRTD_offline(const char *path) { } int res = emrtd_print_ef_com_info(data, datalen); - if ( res != PM3_SUCCESS) { + if (res != PM3_SUCCESS) { free(data); free(filepath); return res; @@ -1590,7 +1590,7 @@ int infoHF_EMRTD_offline(const char *path) { filelist[filelistlen++] = 0x77; // Read files in the file list for (int i = 0; i < filelistlen; i++) { - emrtd_dg_t * dg = emrtd_tag_to_dg(filelist[i]); + emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); if (dg == NULL) { PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); continue; @@ -1599,8 +1599,7 @@ int infoHF_EMRTD_offline(const char *path) { strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg->filename); - if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) - { + if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) { // we won't halt on parsing errors if (dg->parser != NULL) dg->parser(data, datalen); @@ -1669,7 +1668,7 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { memset(docnum + slen, '<', 9 - slen); } } - + if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { BAC = false; } else { @@ -1679,7 +1678,7 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { error = true; } } - + if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { BAC = false; } else { @@ -1755,7 +1754,7 @@ static int cmd_hf_emrtd_info(const char *Cmd) { memset(docnum + slen, '<', 9 - slen); } } - + if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { BAC = false; } else { @@ -1765,7 +1764,7 @@ static int cmd_hf_emrtd_info(const char *Cmd) { error = true; } } - + if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { BAC = false; } else { From b505c5f4d8e7823c3a2c474e63e375412ec57505 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 22 Dec 2020 05:01:42 +0300 Subject: [PATCH 04/21] emrtd: Read file hash data from EF_SOD --- client/src/cmdhfemrtd.c | 76 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index d0b3527af..4e3bfc575 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -203,9 +203,9 @@ static char emrtd_calculate_check_digit(char *data) { } static int emrtd_get_asn1_data_length(uint8_t *datain, int datainlen, int offset) { - PrintAndLogEx(DEBUG, "asn1datalength, datain: %s", sprint_hex_inrow(datain, datainlen)); + PrintAndLogEx(DEBUG, "asn1 datalength, datain: %s", sprint_hex_inrow(datain, datainlen)); int lenfield = (int) * (datain + offset); - PrintAndLogEx(DEBUG, "asn1datalength, lenfield: %i", lenfield); + PrintAndLogEx(DEBUG, "asn1 datalength, lenfield: %02X", lenfield); if (lenfield <= 0x7f) { return lenfield; } else if (lenfield == 0x81) { @@ -221,7 +221,7 @@ static int emrtd_get_asn1_data_length(uint8_t *datain, int datainlen, int offset static int emrtd_get_asn1_field_length(uint8_t *datain, int datainlen, int offset) { PrintAndLogEx(DEBUG, "asn1 fieldlength, datain: %s", sprint_hex_inrow(datain, datainlen)); int lenfield = (int) * (datain + offset); - PrintAndLogEx(DEBUG, "asn1 fieldlength, thing: %i", lenfield); + PrintAndLogEx(DEBUG, "asn1 fieldlength, lenfield: %02X", lenfield); if (lenfield <= 0x7F) { return 1; } else if (lenfield == 0x81) { @@ -629,10 +629,10 @@ static int emrtd_lds_determine_tag_length(uint8_t tag) { return 1; } -static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *dataout, int *dataoutlen, int tag1, int tag2, bool twobytetag, bool skiptoptag) { +static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *dataout, int *dataoutlen, int tag1, int tag2, bool twobytetag, bool entertoptag) { int offset = 0; - if (skiptoptag) { + if (entertoptag) { offset += emrtd_lds_determine_tag_length(*datain); offset += emrtd_get_asn1_field_length(datain, datainlen, offset); } @@ -651,6 +651,8 @@ static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *d // Get the length of the element's length e_fieldlen = emrtd_get_asn1_field_length(datain + offset, datainlen - offset, e_idlen); + PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, e_idlen: %02X, e_datalen: %02X, e_fieldlen: %02X", e_idlen, e_datalen, e_fieldlen); + // 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) { @@ -1477,10 +1479,68 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { return PM3_SUCCESS; } +static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) { + // very very very very cursed code. + uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0; + + if (!emrtd_lds_get_data_by_tag(data, (int) datalen, top, (int *) &toplen, 0x30, 0x00, false, true)) { + PrintAndLogEx(ERR, "Failed to read top from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen)); + + if (!emrtd_lds_get_data_by_tag(top, (int) toplen, signeddata, (int *) &signeddatalen, 0xA0, 0x00, false, false)) { + PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen)); + + // Do true on reading into the tag as it's a "sequence" + if (!emrtd_lds_get_data_by_tag(signeddata, (int) signeddatalen, emrtdsigcontainer, (int *) &emrtdsigcontainerlen, 0x30, 0x00, false, true)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen)); + + if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, (int) emrtdsigcontainerlen, emrtdsig, (int *) &emrtdsiglen, 0xA0, 0x00, false, false)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + + // TODO: Not doing memcpy here, it didn't work, fix it somehow + if (!emrtd_lds_get_data_by_tag(emrtdsig, (int) emrtdsiglen, emrtdsigtext, (int *) &emrtdsigtextlen, 0x04, 0x00, false, false)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD."); + return false; + } + memcpy(dataout, emrtdsigtext, emrtdsigtextlen); + *dataoutlen = emrtdsigtextlen; + return PM3_SUCCESS; +} + static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen) { -// PrintAndLogEx(NORMAL, ""); -// PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------"); -// PrintAndLogEx(WARNING, "TODO"); + uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + // size_t emrtdsiglen, e_datalen, e_fieldlen = 0; + size_t emrtdsiglen = 0; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------"); + + if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) { + return false; + } + // TODO: parse this + PrintAndLogEx(INFO, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + return PM3_SUCCESS; } From f1a8345faccca4e18795c7c9e9fab1ca39a1aa70 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 22 Dec 2020 05:29:37 +0300 Subject: [PATCH 05/21] emrtd: Add a skip feature to emrtd_lds_get_data_by_tag --- client/src/cmdhfemrtd.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 4e3bfc575..d92dfb19a 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -629,8 +629,9 @@ static int emrtd_lds_determine_tag_length(uint8_t tag) { return 1; } -static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *dataout, int *dataoutlen, int tag1, int tag2, bool twobytetag, bool entertoptag) { +static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *dataout, int *dataoutlen, int tag1, int tag2, bool twobytetag, bool entertoptag, int skiptagcount) { int offset = 0; + int skipcounter = 0; if (entertoptag) { offset += emrtd_lds_determine_tag_length(*datain); @@ -655,7 +656,9 @@ static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *d // 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 (skipcounter < skiptagcount) { + skipcounter += 1; + } else if (datainlen > e_datalen) { *dataoutlen = e_datalen; memcpy(dataout, datain + offset + e_idlen + e_fieldlen, e_datalen); return true; @@ -720,7 +723,7 @@ static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length) { int datalen = 0; // If we can't find image in EF_DG5, return false. - if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true, true) == false) { + if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true, true, 0) == false) { return PM3_ESOFT; } @@ -738,7 +741,7 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length) { int datalen = 0; // If we can't find image in EF_DG7, return false. - if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true, true) == false) { + if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true, true, 0) == false) { return PM3_ESOFT; } @@ -1004,7 +1007,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab uint8_t filelist[50]; int filelistlen = 0; - if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true)) { + if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); DropField(); return PM3_ESOFT; @@ -1230,7 +1233,7 @@ static void emrtd_print_unknown_timestamp_5f85(uint8_t *data) { static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) { uint8_t filelist[50]; int filelistlen = 0; - int res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true); + int res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0); if (!res) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); return PM3_ESOFT; @@ -1261,7 +1264,7 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { char mrz[90] = { 0x00 }; int mrzlen = 0; - if (!emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true, true)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true, true, 0)) { PrintAndLogEx(ERR, "Failed to read MRZ from EF_DG1."); return PM3_ESOFT; } @@ -1343,13 +1346,13 @@ static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG11") " -------------------"); - if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_DG11."); return PM3_ESOFT; } for (int i = 0; i < taglistlen; i++) { - emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true); + emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0); // Don't bother with empty tags if (tagdatalen == 0) { continue; @@ -1422,13 +1425,13 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG12") " -------------------"); - if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_DG12."); return PM3_ESOFT; } for (int i = 0; i < taglistlen; i++) { - emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true); + emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0); // Don't bother with empty tags if (tagdatalen == 0) { continue; @@ -1488,14 +1491,14 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 }; size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0; - if (!emrtd_lds_get_data_by_tag(data, (int) datalen, top, (int *) &toplen, 0x30, 0x00, false, true)) { + if (!emrtd_lds_get_data_by_tag(data, (int) datalen, top, (int *) &toplen, 0x30, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read top from EF_SOD."); return false; } PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen)); - if (!emrtd_lds_get_data_by_tag(top, (int) toplen, signeddata, (int *) &signeddatalen, 0xA0, 0x00, false, false)) { + if (!emrtd_lds_get_data_by_tag(top, (int) toplen, signeddata, (int *) &signeddatalen, 0xA0, 0x00, false, false, 0)) { PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD."); return false; } @@ -1503,14 +1506,14 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen)); // Do true on reading into the tag as it's a "sequence" - if (!emrtd_lds_get_data_by_tag(signeddata, (int) signeddatalen, emrtdsigcontainer, (int *) &emrtdsigcontainerlen, 0x30, 0x00, false, true)) { + if (!emrtd_lds_get_data_by_tag(signeddata, (int) signeddatalen, emrtdsigcontainer, (int *) &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD."); return false; } PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen)); - if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, (int) emrtdsigcontainerlen, emrtdsig, (int *) &emrtdsiglen, 0xA0, 0x00, false, false)) { + if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, (int) emrtdsigcontainerlen, emrtdsig, (int *) &emrtdsiglen, 0xA0, 0x00, false, false, 0)) { PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD."); return false; } @@ -1518,7 +1521,7 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen)); // TODO: Not doing memcpy here, it didn't work, fix it somehow - if (!emrtd_lds_get_data_by_tag(emrtdsig, (int) emrtdsiglen, emrtdsigtext, (int *) &emrtdsigtextlen, 0x04, 0x00, false, false)) { + if (!emrtd_lds_get_data_by_tag(emrtdsig, (int) emrtdsiglen, emrtdsigtext, (int *) &emrtdsigtextlen, 0x04, 0x00, false, false, 0)) { PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD."); return false; } @@ -1588,7 +1591,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab uint8_t filelist[50]; int filelistlen = 0; - if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true)) { + if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); DropField(); return PM3_ESOFT; @@ -1638,7 +1641,7 @@ int infoHF_EMRTD_offline(const char *path) { uint8_t filelist[50]; int filelistlen = 0; - res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true); + res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0); if (!res) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); free(data); From c3ed99e3883c04a0e0aef09128605cb5adce9a02 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 22 Dec 2020 05:52:48 +0300 Subject: [PATCH 06/21] emrtd: Parse hashes on EF_SOD --- client/src/cmdhfemrtd.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index d92dfb19a..3203b4932 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1532,8 +1532,19 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen) { uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t hash[65] = { 0x00 }; + size_t hashlen = 0; + + uint8_t hashidstr[4] = { 0x00 }; + size_t hashidstrlen = 0; + // size_t emrtdsiglen, e_datalen, e_fieldlen = 0; size_t emrtdsiglen = 0; + size_t hashlistlen = 0; + size_t e_datalen = 0; + size_t e_fieldlen = 0; + size_t offset = 0; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------"); @@ -1541,8 +1552,33 @@ static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen) { if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) { return false; } - // TODO: parse this - PrintAndLogEx(INFO, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + + PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + + if (!emrtd_lds_get_data_by_tag(emrtdsig, (int) emrtdsiglen, hashlist, (int *) &hashlistlen, 0x30, 0x00, false, true, 1)) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "hash list: %s", sprint_hex_inrow(hashlist, hashlistlen)); + + while (offset < hashlistlen) { + // Get the length of the element + e_datalen = emrtd_get_asn1_data_length(hashlist + offset, hashlistlen - offset, 1); + + // Get the length of the element's length + e_fieldlen = emrtd_get_asn1_field_length(hashlist + offset, hashlistlen - offset, 1); + + switch (hashlist[offset]) { + case 0x30: + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, (int) e_datalen, hashidstr, (int *) &hashidstrlen, 0x02, 0x00, false, false, 0); + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, (int) e_datalen, hash, (int *) &hashlen, 0x04, 0x00, false, false, 0); + PrintAndLogEx(SUCCESS, "Hash for EF_DG%i: %s", hashidstr[0], sprint_hex_inrow(hash, hashlen)); + break; + } + // + 1 for length of ID + offset += 1 + e_datalen + e_fieldlen; + } return PM3_SUCCESS; } From 0272a3b63eaca1bde5f5093ebd2bb5c3572142c5 Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 23 Dec 2020 22:27:05 +0300 Subject: [PATCH 07/21] emrtd: Move a lot of size values from int to size_t --- client/src/cmdhfemrtd.c | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 3203b4932..0c9de0cac 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -629,7 +629,7 @@ static int emrtd_lds_determine_tag_length(uint8_t tag) { return 1; } -static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *dataout, int *dataoutlen, int tag1, int tag2, bool twobytetag, bool entertoptag, int skiptagcount) { +static bool emrtd_lds_get_data_by_tag(uint8_t *datain, size_t datainlen, uint8_t *dataout, size_t *dataoutlen, int tag1, int tag2, bool twobytetag, bool entertoptag, size_t skiptagcount) { int offset = 0; int skipcounter = 0; @@ -720,7 +720,7 @@ static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length) { static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length) { uint8_t data[EMRTD_MAX_FILE_SIZE]; - int datalen = 0; + size_t datalen = 0; // If we can't find image in EF_DG5, return false. if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true, true, 0) == false) { @@ -738,7 +738,7 @@ static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length) { static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length) { uint8_t data[EMRTD_MAX_FILE_SIZE]; - int datalen = 0; + size_t datalen = 0; // If we can't find image in EF_DG7, return false. if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true, true, 0) == false) { @@ -1005,7 +1005,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab saveFile(dg_table[EF_COM].filename, ".BIN", response, resplen); uint8_t filelist[50]; - int filelistlen = 0; + size_t filelistlen = 0; if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); @@ -1017,7 +1017,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab // Add EF_SOD to the list filelist[filelistlen++] = 0x77; // Dump all files in the file list - for (int i = 0; i < filelistlen; i++) { + for (size_t i = 0; i < filelistlen; i++) { emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); if (dg == NULL) { PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); @@ -1232,7 +1232,7 @@ static void emrtd_print_unknown_timestamp_5f85(uint8_t *data) { static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) { uint8_t filelist[50]; - int filelistlen = 0; + size_t filelistlen = 0; int res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0); if (!res) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); @@ -1262,7 +1262,7 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { // 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; + size_t mrzlen = 0; if (!emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true, true, 0)) { PrintAndLogEx(ERR, "Failed to read MRZ from EF_DG1."); @@ -1339,9 +1339,9 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { uint8_t taglist[100] = { 0x00 }; - int taglistlen = 0; + size_t taglistlen = 0; uint8_t tagdata[1000] = { 0x00 }; - int tagdatalen = 0; + size_t tagdatalen = 0; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG11") " -------------------"); @@ -1418,9 +1418,9 @@ static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { uint8_t taglist[100] = { 0x00 }; - int taglistlen = 0; + size_t taglistlen = 0; uint8_t tagdata[1000] = { 0x00 }; - int tagdatalen = 0; + size_t tagdatalen = 0; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG12") " -------------------"); @@ -1491,14 +1491,14 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 }; size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0; - if (!emrtd_lds_get_data_by_tag(data, (int) datalen, top, (int *) &toplen, 0x30, 0x00, false, true, 0)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, top, &toplen, 0x30, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read top from EF_SOD."); return false; } PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen)); - if (!emrtd_lds_get_data_by_tag(top, (int) toplen, signeddata, (int *) &signeddatalen, 0xA0, 0x00, false, false, 0)) { + if (!emrtd_lds_get_data_by_tag(top, toplen, signeddata, &signeddatalen, 0xA0, 0x00, false, false, 0)) { PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD."); return false; } @@ -1506,14 +1506,14 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen)); // Do true on reading into the tag as it's a "sequence" - if (!emrtd_lds_get_data_by_tag(signeddata, (int) signeddatalen, emrtdsigcontainer, (int *) &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0)) { + if (!emrtd_lds_get_data_by_tag(signeddata, signeddatalen, emrtdsigcontainer, &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD."); return false; } PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen)); - if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, (int) emrtdsigcontainerlen, emrtdsig, (int *) &emrtdsiglen, 0xA0, 0x00, false, false, 0)) { + if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, emrtdsigcontainerlen, emrtdsig, &emrtdsiglen, 0xA0, 0x00, false, false, 0)) { PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD."); return false; } @@ -1521,7 +1521,7 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen)); // TODO: Not doing memcpy here, it didn't work, fix it somehow - if (!emrtd_lds_get_data_by_tag(emrtdsig, (int) emrtdsiglen, emrtdsigtext, (int *) &emrtdsigtextlen, 0x04, 0x00, false, false, 0)) { + if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, emrtdsigtext, &emrtdsigtextlen, 0x04, 0x00, false, false, 0)) { PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD."); return false; } @@ -1555,7 +1555,7 @@ static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen) { PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); - if (!emrtd_lds_get_data_by_tag(emrtdsig, (int) emrtdsiglen, hashlist, (int *) &hashlistlen, 0x30, 0x00, false, true, 1)) { + if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); return false; } @@ -1571,8 +1571,8 @@ static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen) { switch (hashlist[offset]) { case 0x30: - emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, (int) e_datalen, hashidstr, (int *) &hashidstrlen, 0x02, 0x00, false, false, 0); - emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, (int) e_datalen, hash, (int *) &hashlen, 0x04, 0x00, false, false, 0); + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0); + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0); PrintAndLogEx(SUCCESS, "Hash for EF_DG%i: %s", hashidstr[0], sprint_hex_inrow(hash, hashlen)); break; } @@ -1625,7 +1625,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab } uint8_t filelist[50]; - int filelistlen = 0; + size_t filelistlen = 0; if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); @@ -1676,7 +1676,7 @@ int infoHF_EMRTD_offline(const char *path) { } uint8_t filelist[50]; - int filelistlen = 0; + size_t filelistlen = 0; res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0); if (!res) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); From 4962cb84ecc73bbc6e54a92fa0240fa582b7c7d1 Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 23 Dec 2020 22:37:51 +0300 Subject: [PATCH 08/21] emrtd: Note that MRZ option is for passports (TD3) only --- client/src/cmdhfemrtd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index d90c7e79e..499b06c1a 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1831,7 +1831,7 @@ static int cmd_hf_emrtd_info(const char *Cmd) { 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_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars"), + arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars (passports only)"), arg_str0(NULL, "path", "", "display info from offline dump stored in dirpath"), arg_param_end }; From 9987ebf53493df280d9ef04e4e3efecc201f97b4 Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 23 Dec 2020 23:08:34 +0300 Subject: [PATCH 09/21] emrtd: Use memcmp for JPEG headers --- client/src/cmdhfemrtd.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 499b06c1a..1a51ea427 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -693,17 +693,19 @@ static bool emrtd_select_and_read(uint8_t *dataout, int *dataoutlen, const char return true; } +const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 }; +const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 }; + static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length) { 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 // Note: Doing file_length - 6 to account for the longest data we're checking. + // Checks first byte before the rest to reduce overhead 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)) { + if ((file_contents[offset] == 0xFF && memcmp(jpeg_header, file_contents + offset, 4) != 0) || + (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) != 0)) { datalen = file_length - offset; break; } From f26e027e23259f247c87efc6ffc998ff80b07624 Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 23 Dec 2020 23:13:13 +0300 Subject: [PATCH 10/21] gitignore: Ignore emrtd dumps --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 081c0f8e8..94b4c221b 100644 --- a/.gitignore +++ b/.gitignore @@ -98,6 +98,7 @@ tools/andrew/* tools/jtag_openocd/openocd_configuration ppls patches/* *- Copy.* +/EF_* client/lualibs/mfc_default_keys.lua client/lualibs/pm3_cmd.lua From a1762fa4a30dde35e92d2f0c5164aa3c5d59c948 Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 23 Dec 2020 23:21:54 +0300 Subject: [PATCH 11/21] emrtd: Mark biometrics as EAC-only and introduce the concept --- client/src/cmdhfemrtd.c | 48 ++++++++++++++++++++--------------------- client/src/cmdhfemrtd.h | 1 + 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 1a51ea427..fa669f103 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -84,28 +84,28 @@ typedef enum { // list must match dg_table } emrtd_dg_enum; static emrtd_dg_t dg_table[] = { -// tag fileid filename desc pace req fast parser dumper - {0x60, "011E", "EF_COM", "Header and Data Group Presence Information", false, true, true, emrtd_print_ef_com_info, NULL}, - {0x61, "0101", "EF_DG1", "Details recorded in MRZ", false, true, true, emrtd_print_ef_dg1_info, NULL}, - {0x75, "0102", "EF_DG2", "Encoded Face", false, true, false, NULL, emrtd_dump_ef_dg2}, - {0x63, "0103", "EF_DG3", "Encoded Finger(s)", true, false, false, NULL, NULL}, - {0x76, "0104", "EF_DG4", "Encoded Eye(s)", true, false, false, NULL, NULL}, - {0x65, "0105", "EF_DG5", "Displayed Portrait", false, false, false, NULL, emrtd_dump_ef_dg5}, - {0x66, "0106", "EF_DG6", "Reserved for Future Use", false, false, false, NULL, NULL}, - {0x67, "0107", "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, NULL, emrtd_dump_ef_dg7}, - {0x68, "0108", "EF_DG8", "Data Feature(s)", false, false, true, NULL, NULL}, - {0x69, "0109", "EF_DG9", "Structure Feature(s)", false, false, true, NULL, NULL}, - {0x6a, "010A", "EF_DG10", "Substance Feature(s)", false, false, true, NULL, NULL}, - {0x6b, "010B", "EF_DG11", "Additional Personal Detail(s)", false, false, true, emrtd_print_ef_dg11_info, NULL}, - {0x6c, "010C", "EF_DG12", "Additional Document Detail(s)", false, false, true, emrtd_print_ef_dg12_info, NULL}, - {0x6d, "010D", "EF_DG13", "Optional Detail(s)", false, false, true, NULL, NULL}, - {0x6e, "010E", "EF_DG14", "Security Options", false, false, true, NULL, NULL}, - {0x6f, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, true, NULL, NULL}, - {0x70, "0110", "EF_DG16", "Person(s) to Notify", false, false, true, NULL, NULL}, - {0x77, "011D", "EF_SOD", "Document Security Object", false, false, true, emrtd_print_ef_sod_info, emrtd_dump_ef_sod}, - {0xff, "011C", "EF_CardAccess", "PACE SecurityInfos", true, true, true, NULL, NULL}, - {0xff, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, true, NULL, NULL}, - {0x00, NULL, NULL, NULL, false, false, false, NULL, NULL} +// tag fileid filename desc pace eac req fast parser dumper + {0x60, "011E", "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL}, + {0x61, "0101", "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL}, + {0x75, "0102", "EF_DG2", "Encoded Face", false, false, true, false, NULL, emrtd_dump_ef_dg2}, + {0x63, "0103", "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL}, + {0x76, "0104", "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL}, + {0x65, "0105", "EF_DG5", "Displayed Portrait", false, false, false, false, NULL, emrtd_dump_ef_dg5}, + {0x66, "0106", "EF_DG6", "Reserved for Future Use", false, false, false, false, NULL, NULL}, + {0x67, "0107", "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, false, NULL, emrtd_dump_ef_dg7}, + {0x68, "0108", "EF_DG8", "Data Feature(s)", false, false, false, true, NULL, NULL}, + {0x69, "0109", "EF_DG9", "Structure Feature(s)", false, false, false, true, NULL, NULL}, + {0x6a, "010A", "EF_DG10", "Substance Feature(s)", false, false, false, true, NULL, NULL}, + {0x6b, "010B", "EF_DG11", "Additional Personal Detail(s)", false, false, false, true, emrtd_print_ef_dg11_info, NULL}, + {0x6c, "010C", "EF_DG12", "Additional Document Detail(s)", false, false, false, true, emrtd_print_ef_dg12_info, NULL}, + {0x6d, "010D", "EF_DG13", "Optional Detail(s)", false, false, false, true, NULL, NULL}, + {0x6e, "010E", "EF_DG14", "Security Options", false, false, false, true, NULL, NULL}, + {0x6f, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL}, + {0x70, "0110", "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL}, + {0x77, "011D", "EF_SOD", "Document Security Object", false, false, false, true, emrtd_print_ef_sod_info, emrtd_dump_ef_sod}, + {0xff, "011C", "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, NULL, NULL}, + {0xff, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL}, + {0x00, NULL, NULL, NULL, false, false, false, false, NULL, NULL} }; static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) { @@ -1026,7 +1026,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab continue; } PrintAndLogEx(DEBUG, "Current file: %s", dg->filename); - if (!dg->pace) { + if (!dg->pace && !dg->eac) { emrtd_dump_file(ks_enc, ks_mac, ssc, dg->fileid, dg->filename, BAC, use_14b); } } @@ -1643,7 +1643,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); continue; } - if (dg->fastdump && !dg->pace) { + if (dg->fastdump && !dg->pace && !dg->eac) { if (emrtd_select_and_read(response, &resplen, dg->fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { if (dg->parser != NULL) dg->parser(response, resplen); diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index 9df7846ad..bcae56538 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -19,6 +19,7 @@ typedef struct emrtd_dg_s { const char *filename; const char *desc; bool pace; + bool eac; // EAC only (we can't dump these) bool required; // some are required only if PACE bool fastdump; // fast to dump int (*parser)(uint8_t *data, size_t datalen); From 9e7d515c37d1303de07b635b0e0682ddb4a8303c Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 23 Dec 2020 23:52:31 +0300 Subject: [PATCH 12/21] emrtd info: Don't print EF_SOD info --- client/src/cmdhfemrtd.c | 163 ++++++++++++++++++++-------------------- 1 file changed, 81 insertions(+), 82 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index fa669f103..88a79707a 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -58,7 +58,6 @@ static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen); -static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen); typedef enum { // list must match dg_table EF_COM = 0, @@ -102,7 +101,7 @@ static emrtd_dg_t dg_table[] = { {0x6e, "010E", "EF_DG14", "Security Options", false, false, false, true, NULL, NULL}, {0x6f, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL}, {0x70, "0110", "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL}, - {0x77, "011D", "EF_SOD", "Document Security Object", false, false, false, true, emrtd_print_ef_sod_info, emrtd_dump_ef_sod}, + {0x77, "011D", "EF_SOD", "Document Security Object", false, false, false, false, NULL, emrtd_dump_ef_sod}, {0xff, "011C", "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, NULL, NULL}, {0xff, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL}, {0x00, NULL, NULL, NULL, false, false, false, false, NULL, NULL} @@ -1484,106 +1483,106 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { return PM3_SUCCESS; } -static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) { - // very very very very cursed code. - uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 }; - uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 }; - uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 }; - uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; - uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 }; - size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0; +// static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) { +// // very very very very cursed code. +// uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 }; +// uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 }; +// uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 }; +// uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; +// uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 }; +// size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0; - if (!emrtd_lds_get_data_by_tag(data, datalen, top, &toplen, 0x30, 0x00, false, true, 0)) { - PrintAndLogEx(ERR, "Failed to read top from EF_SOD."); - return false; - } +// if (!emrtd_lds_get_data_by_tag(data, datalen, top, &toplen, 0x30, 0x00, false, true, 0)) { +// PrintAndLogEx(ERR, "Failed to read top from EF_SOD."); +// return false; +// } - PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen)); +// PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen)); - if (!emrtd_lds_get_data_by_tag(top, toplen, signeddata, &signeddatalen, 0xA0, 0x00, false, false, 0)) { - PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD."); - return false; - } +// if (!emrtd_lds_get_data_by_tag(top, toplen, signeddata, &signeddatalen, 0xA0, 0x00, false, false, 0)) { +// PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD."); +// return false; +// } - PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen)); +// PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen)); - // Do true on reading into the tag as it's a "sequence" - if (!emrtd_lds_get_data_by_tag(signeddata, signeddatalen, emrtdsigcontainer, &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0)) { - PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD."); - return false; - } +// // Do true on reading into the tag as it's a "sequence" +// if (!emrtd_lds_get_data_by_tag(signeddata, signeddatalen, emrtdsigcontainer, &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0)) { +// PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD."); +// return false; +// } - PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen)); +// PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen)); - if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, emrtdsigcontainerlen, emrtdsig, &emrtdsiglen, 0xA0, 0x00, false, false, 0)) { - PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD."); - return false; - } +// if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, emrtdsigcontainerlen, emrtdsig, &emrtdsiglen, 0xA0, 0x00, false, false, 0)) { +// PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD."); +// return false; +// } - PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen)); +// PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen)); - // TODO: Not doing memcpy here, it didn't work, fix it somehow - if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, emrtdsigtext, &emrtdsigtextlen, 0x04, 0x00, false, false, 0)) { - PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD."); - return false; - } - memcpy(dataout, emrtdsigtext, emrtdsigtextlen); - *dataoutlen = emrtdsigtextlen; - return PM3_SUCCESS; -} +// // TODO: Not doing memcpy here, it didn't work, fix it somehow +// if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, emrtdsigtext, &emrtdsigtextlen, 0x04, 0x00, false, false, 0)) { +// PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD."); +// return false; +// } +// memcpy(dataout, emrtdsigtext, emrtdsigtextlen); +// *dataoutlen = emrtdsigtextlen; +// return PM3_SUCCESS; +// } -static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen) { - uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; - uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 }; - uint8_t hash[65] = { 0x00 }; - size_t hashlen = 0; +// static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen) { +// uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; +// uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 }; +// uint8_t hash[65] = { 0x00 }; +// size_t hashlen = 0; - uint8_t hashidstr[4] = { 0x00 }; - size_t hashidstrlen = 0; +// uint8_t hashidstr[4] = { 0x00 }; +// size_t hashidstrlen = 0; - // size_t emrtdsiglen, e_datalen, e_fieldlen = 0; - size_t emrtdsiglen = 0; - size_t hashlistlen = 0; - size_t e_datalen = 0; - size_t e_fieldlen = 0; - size_t offset = 0; +// // size_t emrtdsiglen, e_datalen, e_fieldlen = 0; +// size_t emrtdsiglen = 0; +// size_t hashlistlen = 0; +// size_t e_datalen = 0; +// size_t e_fieldlen = 0; +// size_t offset = 0; - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------"); +// PrintAndLogEx(NORMAL, ""); +// PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------"); - if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) { - return false; - } +// if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) { +// return false; +// } - PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); +// PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); - if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { - PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); - return false; - } +// if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { +// PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); +// return false; +// } - PrintAndLogEx(DEBUG, "hash list: %s", sprint_hex_inrow(hashlist, hashlistlen)); +// PrintAndLogEx(DEBUG, "hash list: %s", sprint_hex_inrow(hashlist, hashlistlen)); - while (offset < hashlistlen) { - // Get the length of the element - e_datalen = emrtd_get_asn1_data_length(hashlist + offset, hashlistlen - offset, 1); +// while (offset < hashlistlen) { +// // Get the length of the element +// e_datalen = emrtd_get_asn1_data_length(hashlist + offset, hashlistlen - offset, 1); - // Get the length of the element's length - e_fieldlen = emrtd_get_asn1_field_length(hashlist + offset, hashlistlen - offset, 1); +// // Get the length of the element's length +// e_fieldlen = emrtd_get_asn1_field_length(hashlist + offset, hashlistlen - offset, 1); - switch (hashlist[offset]) { - case 0x30: - emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0); - emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0); - PrintAndLogEx(SUCCESS, "Hash for EF_DG%i: %s", hashidstr[0], sprint_hex_inrow(hash, hashlen)); - break; - } - // + 1 for length of ID - offset += 1 + e_datalen + e_fieldlen; - } +// switch (hashlist[offset]) { +// case 0x30: +// emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0); +// emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0); +// PrintAndLogEx(SUCCESS, "Hash for EF_DG%i: %s", hashidstr[0], sprint_hex_inrow(hash, hashlen)); +// break; +// } +// // + 1 for length of ID +// offset += 1 + e_datalen + e_fieldlen; +// } - return PM3_SUCCESS; -} +// return PM3_SUCCESS; +// } int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; @@ -1696,7 +1695,7 @@ int infoHF_EMRTD_offline(const char *path) { PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); continue; } - if (!dg->pace) { + if (!dg->pace && !dg->eac) { strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg->filename); From 7e2b10d413fb38965359e1bd1a91352bbf251bdf Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 29 Dec 2020 01:55:18 +0300 Subject: [PATCH 13/21] emrtd info (offline): Add basic hash verification support --- client/src/cmdhfemrtd.c | 235 ++++++++++++++++++++++------------------ client/src/cmdhfemrtd.h | 1 + 2 files changed, 131 insertions(+), 105 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 88a79707a..a088bfc7f 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -20,6 +20,7 @@ #include "protocols.h" // definitions of ISO14A/7816 protocol #include "emv/apduinfo.h" // GetAPDUCodeDescription #include "sha1.h" // KSeed calculation etc +#include "crypto/libpcrypto.h" // Hash calculation (sha256), TODO: AES too #include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt #include "des.h" // mbedtls_des_key_set_parity #include "cmdhf14b.h" // exchange_14b_apdu @@ -83,28 +84,28 @@ typedef enum { // list must match dg_table } emrtd_dg_enum; static emrtd_dg_t dg_table[] = { -// tag fileid filename desc pace eac req fast parser dumper - {0x60, "011E", "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL}, - {0x61, "0101", "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL}, - {0x75, "0102", "EF_DG2", "Encoded Face", false, false, true, false, NULL, emrtd_dump_ef_dg2}, - {0x63, "0103", "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL}, - {0x76, "0104", "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL}, - {0x65, "0105", "EF_DG5", "Displayed Portrait", false, false, false, false, NULL, emrtd_dump_ef_dg5}, - {0x66, "0106", "EF_DG6", "Reserved for Future Use", false, false, false, false, NULL, NULL}, - {0x67, "0107", "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, false, NULL, emrtd_dump_ef_dg7}, - {0x68, "0108", "EF_DG8", "Data Feature(s)", false, false, false, true, NULL, NULL}, - {0x69, "0109", "EF_DG9", "Structure Feature(s)", false, false, false, true, NULL, NULL}, - {0x6a, "010A", "EF_DG10", "Substance Feature(s)", false, false, false, true, NULL, NULL}, - {0x6b, "010B", "EF_DG11", "Additional Personal Detail(s)", false, false, false, true, emrtd_print_ef_dg11_info, NULL}, - {0x6c, "010C", "EF_DG12", "Additional Document Detail(s)", false, false, false, true, emrtd_print_ef_dg12_info, NULL}, - {0x6d, "010D", "EF_DG13", "Optional Detail(s)", false, false, false, true, NULL, NULL}, - {0x6e, "010E", "EF_DG14", "Security Options", false, false, false, true, NULL, NULL}, - {0x6f, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL}, - {0x70, "0110", "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL}, - {0x77, "011D", "EF_SOD", "Document Security Object", false, false, false, false, NULL, emrtd_dump_ef_sod}, - {0xff, "011C", "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, NULL, NULL}, - {0xff, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL}, - {0x00, NULL, NULL, NULL, false, false, false, false, NULL, NULL} +// tag dg# fileid filename desc pace eac req fast parser dumper + {0x60, 0, "011E", "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL}, + {0x61, 1, "0101", "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL}, + {0x75, 2, "0102", "EF_DG2", "Encoded Face", false, false, true, false, NULL, emrtd_dump_ef_dg2}, + {0x63, 3, "0103", "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL}, + {0x76, 4, "0104", "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL}, + {0x65, 5, "0105", "EF_DG5", "Displayed Portrait", false, false, false, false, NULL, emrtd_dump_ef_dg5}, + {0x66, 6, "0106", "EF_DG6", "Reserved for Future Use", false, false, false, false, NULL, NULL}, + {0x67, 7, "0107", "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, false, NULL, emrtd_dump_ef_dg7}, + {0x68, 8, "0108", "EF_DG8", "Data Feature(s)", false, false, false, true, NULL, NULL}, + {0x69, 9, "0109", "EF_DG9", "Structure Feature(s)", false, false, false, true, NULL, NULL}, + {0x6a, 10, "010A", "EF_DG10", "Substance Feature(s)", false, false, false, true, NULL, NULL}, + {0x6b, 11, "010B", "EF_DG11", "Additional Personal Detail(s)", false, false, false, true, emrtd_print_ef_dg11_info, NULL}, + {0x6c, 12, "010C", "EF_DG12", "Additional Document Detail(s)", false, false, false, true, emrtd_print_ef_dg12_info, NULL}, + {0x6d, 13, "010D", "EF_DG13", "Optional Detail(s)", false, false, false, true, NULL, NULL}, + {0x6e, 14, "010E", "EF_DG14", "Security Options", false, false, false, true, NULL, NULL}, + {0x6f, 15, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL}, + {0x70, 16, "0110", "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL}, + {0x77, 0, "011D", "EF_SOD", "Document Security Object", false, false, false, false, NULL, emrtd_dump_ef_sod}, + {0xff, 0, "011C", "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, NULL, NULL}, + {0xff, 0, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL}, + {0x00, 0, NULL, NULL, NULL, false, false, false, false, NULL, NULL} }; static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) { @@ -1483,106 +1484,105 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { return PM3_SUCCESS; } -// static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) { -// // very very very very cursed code. -// uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 }; -// uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 }; -// uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 }; -// uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; -// uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 }; -// size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0; +static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) { + // very very very very cursed code. + uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0; -// if (!emrtd_lds_get_data_by_tag(data, datalen, top, &toplen, 0x30, 0x00, false, true, 0)) { -// PrintAndLogEx(ERR, "Failed to read top from EF_SOD."); -// return false; -// } + if (!emrtd_lds_get_data_by_tag(data, datalen, top, &toplen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read top from EF_SOD."); + return false; + } -// PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen)); + PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen)); -// if (!emrtd_lds_get_data_by_tag(top, toplen, signeddata, &signeddatalen, 0xA0, 0x00, false, false, 0)) { -// PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD."); -// return false; -// } + if (!emrtd_lds_get_data_by_tag(top, toplen, signeddata, &signeddatalen, 0xA0, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD."); + return false; + } -// PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen)); + PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen)); -// // Do true on reading into the tag as it's a "sequence" -// if (!emrtd_lds_get_data_by_tag(signeddata, signeddatalen, emrtdsigcontainer, &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0)) { -// PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD."); -// return false; -// } + // Do true on reading into the tag as it's a "sequence" + if (!emrtd_lds_get_data_by_tag(signeddata, signeddatalen, emrtdsigcontainer, &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD."); + return false; + } -// PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen)); + PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen)); -// if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, emrtdsigcontainerlen, emrtdsig, &emrtdsiglen, 0xA0, 0x00, false, false, 0)) { -// PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD."); -// return false; -// } + if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, emrtdsigcontainerlen, emrtdsig, &emrtdsiglen, 0xA0, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD."); + return false; + } -// PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen)); -// // TODO: Not doing memcpy here, it didn't work, fix it somehow -// if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, emrtdsigtext, &emrtdsigtextlen, 0x04, 0x00, false, false, 0)) { -// PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD."); -// return false; -// } -// memcpy(dataout, emrtdsigtext, emrtdsigtextlen); -// *dataoutlen = emrtdsigtextlen; -// return PM3_SUCCESS; -// } + // TODO: Not doing memcpy here, it didn't work, fix it somehow + if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, emrtdsigtext, &emrtdsigtextlen, 0x04, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD."); + return false; + } + memcpy(dataout, emrtdsigtext, emrtdsigtextlen); + *dataoutlen = emrtdsigtextlen; + return PM3_SUCCESS; +} -// static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen) { -// uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; -// uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 }; -// uint8_t hash[65] = { 0x00 }; -// size_t hashlen = 0; +static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes) { + uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t hash[65] = { 0x00 }; + size_t hashlen = 0; -// uint8_t hashidstr[4] = { 0x00 }; -// size_t hashidstrlen = 0; + uint8_t hashidstr[4] = { 0x00 }; + size_t hashidstrlen = 0; -// // size_t emrtdsiglen, e_datalen, e_fieldlen = 0; -// size_t emrtdsiglen = 0; -// size_t hashlistlen = 0; -// size_t e_datalen = 0; -// size_t e_fieldlen = 0; -// size_t offset = 0; + // size_t emrtdsiglen, e_datalen, e_fieldlen = 0; + size_t emrtdsiglen = 0; + size_t hashlistlen = 0; + size_t e_datalen = 0; + size_t e_fieldlen = 0; + size_t offset = 0; -// PrintAndLogEx(NORMAL, ""); -// PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------"); + if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) { + return false; + } -// if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) { -// return false; -// } + PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); -// PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); + return false; + } -// if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { -// PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); -// return false; -// } + PrintAndLogEx(DEBUG, "hash list: %s", sprint_hex_inrow(hashlist, hashlistlen)); -// PrintAndLogEx(DEBUG, "hash list: %s", sprint_hex_inrow(hashlist, hashlistlen)); + while (offset < hashlistlen) { + // Get the length of the element + e_datalen = emrtd_get_asn1_data_length(hashlist + offset, hashlistlen - offset, 1); -// while (offset < hashlistlen) { -// // Get the length of the element -// e_datalen = emrtd_get_asn1_data_length(hashlist + offset, hashlistlen - offset, 1); + // Get the length of the element's length + e_fieldlen = emrtd_get_asn1_field_length(hashlist + offset, hashlistlen - offset, 1); -// // Get the length of the element's length -// e_fieldlen = emrtd_get_asn1_field_length(hashlist + offset, hashlistlen - offset, 1); + switch (hashlist[offset]) { + case 0x30: + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0); + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0); + if (hashlen <= 64) { // TODO: This is for coverity, account for it. + memcpy(hashes + (hashidstr[0] * 64), hash, hashlen); + } + break; + } + // + 1 for length of ID + offset += 1 + e_datalen + e_fieldlen; + } -// switch (hashlist[offset]) { -// case 0x30: -// emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0); -// emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0); -// PrintAndLogEx(SUCCESS, "Hash for EF_DG%i: %s", hashidstr[0], sprint_hex_inrow(hash, hashlen)); -// break; -// } -// // + 1 for length of ID -// offset += 1 + e_datalen + e_fieldlen; -// } - -// return PM3_SUCCESS; -// } + return PM3_SUCCESS; +} int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; @@ -1633,8 +1633,9 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab DropField(); return PM3_ESOFT; } + // TODO: DROP THIS TOO A LA OFFLINE // Add EF_SOD to the list - filelist[filelistlen++] = 0x77; + // filelist[filelistlen++] = 0x77; // Dump all files in the file list for (int i = 0; i < filelistlen; i++) { emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); @@ -1686,8 +1687,26 @@ int infoHF_EMRTD_offline(const char *path) { return PM3_ESOFT; } free(data); - // Add EF_SOD to the list - filelist[filelistlen++] = 0x77; + + uint8_t dg_hashes[20][64]; + uint8_t hash_out[64]; + + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_SOD].filename); + + if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read EF_SOD."); + free(filepath); + return PM3_ESOFT; + } + + res = emrtd_parse_ef_sod_hashes(data, datalen, *dg_hashes); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail."); + } + free(data); + // Read files in the file list for (int i = 0; i < filelistlen; i++) { emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); @@ -1703,6 +1722,12 @@ int infoHF_EMRTD_offline(const char *path) { // we won't halt on parsing errors if (dg->parser != NULL) dg->parser(data, datalen); + sha512hash(data, datalen, hash_out); + if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) { + PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); + } else { + PrintAndLogEx(ERR, _RED_("Hash verification failed for EF_DG%i."), dg->dgnum); + } free(data); } } diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index bcae56538..72f5f44b9 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -15,6 +15,7 @@ typedef struct emrtd_dg_s { uint8_t tag; + uint8_t dgnum; const char *fileid; const char *filename; const char *desc; From dbe5d9ac9a219265738645ec694ee93666441123 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 29 Dec 2020 02:08:30 +0300 Subject: [PATCH 14/21] emrtd info (online): Add basic hash verification support --- client/src/cmdhfemrtd.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index a088bfc7f..2235dc60b 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1633,9 +1633,22 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab DropField(); return PM3_ESOFT; } - // TODO: DROP THIS TOO A LA OFFLINE - // Add EF_SOD to the list - // filelist[filelistlen++] = 0x77; + + // Grab the hash list + uint8_t dg_hashes[16][64]; + uint8_t hash_out[64]; + + if (!emrtd_select_and_read(response, &resplen, dg_table[EF_SOD].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { + PrintAndLogEx(ERR, "Failed to read EF_SOD."); + DropField(); + return PM3_ESOFT; + } + + res = emrtd_parse_ef_sod_hashes(response, resplen, *dg_hashes); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail."); + } + // Dump all files in the file list for (int i = 0; i < filelistlen; i++) { emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); @@ -1647,6 +1660,14 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab if (emrtd_select_and_read(response, &resplen, dg->fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { if (dg->parser != NULL) dg->parser(response, resplen); + + // Check file hash + sha512hash(response, resplen, hash_out); + if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) { + PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); + } else { + PrintAndLogEx(ERR, _RED_("Hash verification failed for EF_DG%i."), dg->dgnum); + } } } } @@ -1688,7 +1709,8 @@ int infoHF_EMRTD_offline(const char *path) { } free(data); - uint8_t dg_hashes[20][64]; + // Grab the hash list + uint8_t dg_hashes[16][64]; uint8_t hash_out[64]; strcpy(filepath, path); @@ -1722,6 +1744,8 @@ int infoHF_EMRTD_offline(const char *path) { // we won't halt on parsing errors if (dg->parser != NULL) dg->parser(data, datalen); + + // Check file hash sha512hash(data, datalen, hash_out); if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) { PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); From 5a9d47476591458e69f9dcfb9c2f6a6c8a0db76d Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 29 Dec 2020 02:15:15 +0300 Subject: [PATCH 15/21] emrtd: Better coverity bits for hash verif --- client/src/cmdhfemrtd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 2235dc60b..770fde28f 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1532,16 +1532,15 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ return PM3_SUCCESS; } -static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes) { +static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes, int *hashalgo) { uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 }; - uint8_t hash[65] = { 0x00 }; + uint8_t hash[64] = { 0x00 }; size_t hashlen = 0; uint8_t hashidstr[4] = { 0x00 }; size_t hashidstrlen = 0; - // size_t emrtdsiglen, e_datalen, e_fieldlen = 0; size_t emrtdsiglen = 0; size_t hashlistlen = 0; size_t e_datalen = 0; @@ -1572,8 +1571,10 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has case 0x30: emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0); emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0); - if (hashlen <= 64) { // TODO: This is for coverity, account for it. + if (hashlen <= 64) { memcpy(hashes + (hashidstr[0] * 64), hash, hashlen); + } else { + PrintAndLogEx(ERR, "error (emrtd_parse_ef_sod_hashes) hashlen out-of-bounds"); } break; } From 637e0179756243477a143c7767ddd175af3bcf77 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 29 Dec 2020 03:39:01 +0300 Subject: [PATCH 16/21] emrtd: Support SHA256 --- client/src/cmdhfemrtd.c | 47 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 770fde28f..e782e43ab 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1532,6 +1532,29 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ return PM3_SUCCESS; } +static const uint8_t emrtd_hashalgo_sha256[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}; +static const uint8_t emrtd_hashalgo_sha512[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}; + +static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hashalgo) { + uint8_t hashalgoset[64] = { 0x00 }; + size_t hashalgosetlen = 0; + + if (!emrtd_lds_get_data_by_tag(data, datalen, hashalgoset, &hashalgosetlen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read hash algo set from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "hash algo set: %s", sprint_hex_inrow(hashalgoset, hashalgosetlen)); + + if (memcmp(emrtd_hashalgo_sha256, hashalgoset, 11) == 0) { + *hashalgo = 1; + } else if (memcmp(emrtd_hashalgo_sha512, hashalgoset, 11) == 0) { + *hashalgo = 3; + } + + return PM3_SUCCESS; +} + static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes, int *hashalgo) { uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 }; @@ -1553,6 +1576,8 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo); + if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); return false; @@ -1638,6 +1663,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab // Grab the hash list uint8_t dg_hashes[16][64]; uint8_t hash_out[64]; + int hash_algo = 0; if (!emrtd_select_and_read(response, &resplen, dg_table[EF_SOD].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { PrintAndLogEx(ERR, "Failed to read EF_SOD."); @@ -1645,7 +1671,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab return PM3_ESOFT; } - res = emrtd_parse_ef_sod_hashes(response, resplen, *dg_hashes); + res = emrtd_parse_ef_sod_hashes(response, resplen, *dg_hashes, &hash_algo); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail."); } @@ -1663,7 +1689,13 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab dg->parser(response, resplen); // Check file hash - sha512hash(response, resplen, hash_out); + memset(hash_out, 0, 64); + if (hash_algo == 1) { + sha256hash(response, resplen, hash_out); + } else if (hash_algo == 3) { + sha512hash(response, resplen, hash_out); + } + if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) { PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); } else { @@ -1713,6 +1745,7 @@ int infoHF_EMRTD_offline(const char *path) { // Grab the hash list uint8_t dg_hashes[16][64]; uint8_t hash_out[64]; + int hash_algo = 0; strcpy(filepath, path); strncat(filepath, PATHSEP, 2); @@ -1724,7 +1757,7 @@ int infoHF_EMRTD_offline(const char *path) { return PM3_ESOFT; } - res = emrtd_parse_ef_sod_hashes(data, datalen, *dg_hashes); + res = emrtd_parse_ef_sod_hashes(data, datalen, *dg_hashes, &hash_algo); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail."); } @@ -1747,7 +1780,13 @@ int infoHF_EMRTD_offline(const char *path) { dg->parser(data, datalen); // Check file hash - sha512hash(data, datalen, hash_out); + memset(hash_out, 0, 64); + if (hash_algo == 1) { + sha256hash(data, datalen, hash_out); + } else if (hash_algo == 3) { + sha512hash(data, datalen, hash_out); + } + if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) { PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); } else { From f47b617a58c6851da9a456c56adb8f5170bae6e6 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 29 Dec 2020 03:51:30 +0300 Subject: [PATCH 17/21] emrtd: Check emrtd_parse_ef_sod_hash_algo output --- client/src/cmdhfemrtd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index e782e43ab..089bb6633 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1550,6 +1550,9 @@ static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hash *hashalgo = 1; } else if (memcmp(emrtd_hashalgo_sha512, hashalgoset, 11) == 0) { *hashalgo = 3; + } else { + *hashalgo = 0; + return PM3_ESOFT; } return PM3_SUCCESS; @@ -1576,7 +1579,9 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); - emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo); + if (emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to parse hash list. Unknown algo?"); + } if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); From bc8c52931bd65816e30109b283f0c70700881777 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 29 Dec 2020 04:01:15 +0300 Subject: [PATCH 18/21] emrtd: Split DG hash calc into emrtd_calc_dg_hash --- client/src/cmdhfemrtd.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 089bb6633..43a2adeb8 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1532,6 +1532,7 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ return PM3_SUCCESS; } +// https://security.stackexchange.com/questions/131241/where-do-magic-constants-for-signature-algorithms-come-from static const uint8_t emrtd_hashalgo_sha256[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}; static const uint8_t emrtd_hashalgo_sha512[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}; @@ -1615,6 +1616,16 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has return PM3_SUCCESS; } +static void emrtd_calc_dg_hash(uint8_t *data, size_t datalen, uint8_t *hash_out, int hash_algo) { + memset(hash_out, 0, 64); + + if (hash_algo == 1) { + sha256hash(data, datalen, hash_out); + } else if (hash_algo == 3) { + sha512hash(data, datalen, hash_out); + } +} + int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; int resplen = 0; @@ -1694,12 +1705,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab dg->parser(response, resplen); // Check file hash - memset(hash_out, 0, 64); - if (hash_algo == 1) { - sha256hash(response, resplen, hash_out); - } else if (hash_algo == 3) { - sha512hash(response, resplen, hash_out); - } + emrtd_calc_dg_hash(response, resplen, hash_out, hash_algo); if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) { PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); @@ -1785,12 +1791,7 @@ int infoHF_EMRTD_offline(const char *path) { dg->parser(data, datalen); // Check file hash - memset(hash_out, 0, 64); - if (hash_algo == 1) { - sha256hash(data, datalen, hash_out); - } else if (hash_algo == 3) { - sha512hash(data, datalen, hash_out); - } + emrtd_calc_dg_hash(data, datalen, hash_out, hash_algo); if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) { PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); From aae3426f8e6f08079ae5648c336c1d5d33c47318 Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 29 Dec 2020 04:09:53 +0300 Subject: [PATCH 19/21] emrtd: Adjust an include comment --- client/src/cmdhfemrtd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 43a2adeb8..32c2c9e23 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -20,7 +20,7 @@ #include "protocols.h" // definitions of ISO14A/7816 protocol #include "emv/apduinfo.h" // GetAPDUCodeDescription #include "sha1.h" // KSeed calculation etc -#include "crypto/libpcrypto.h" // Hash calculation (sha256), TODO: AES too +#include "crypto/libpcrypto.h" // Hash calculation (sha256, sha512) #include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt #include "des.h" // mbedtls_des_key_set_parity #include "cmdhf14b.h" // exchange_14b_apdu From 2c5c58d128e9d7ddc2cdb2a4f7c1a76ffefa807a Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 29 Dec 2020 04:20:48 +0300 Subject: [PATCH 20/21] emrtd: Remove 'very very very very cursed' comment --- client/src/cmdhfemrtd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 32c2c9e23..08c53467b 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -1485,7 +1485,6 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { } static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) { - // very very very very cursed code. uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 }; uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 }; uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 }; From 040520d865486184e0165779a16f7774414277ee Mon Sep 17 00:00:00 2001 From: Ave Date: Tue, 29 Dec 2020 17:56:29 +0300 Subject: [PATCH 21/21] emrtd: Improve hashing logic, support SHA1 --- client/src/cmdhfemrtd.c | 86 +++++++++++++++++++--------------- client/src/cmdhfemrtd.h | 8 ++++ client/src/crypto/libpcrypto.c | 10 ++++ client/src/crypto/libpcrypto.h | 1 + 4 files changed, 68 insertions(+), 37 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 08c53467b..e563ac7fa 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -19,8 +19,7 @@ #include "cmdhf14a.h" // ExchangeAPDU14a #include "protocols.h" // definitions of ISO14A/7816 protocol #include "emv/apduinfo.h" // GetAPDUCodeDescription -#include "sha1.h" // KSeed calculation etc -#include "crypto/libpcrypto.h" // Hash calculation (sha256, sha512) +#include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512) #include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt #include "des.h" // mbedtls_des_key_set_parity #include "cmdhf14b.h" // exchange_14b_apdu @@ -108,6 +107,16 @@ static emrtd_dg_t dg_table[] = { {0x00, 0, NULL, NULL, NULL, false, false, false, false, NULL, NULL} }; +// https://security.stackexchange.com/questions/131241/where-do-magic-constants-for-signature-algorithms-come-from +// https://tools.ietf.org/html/rfc3447#page-43 +static emrtd_hashalg_t hashalg_table[] = { +// name hash func len len descriptor + {"SHA-1", sha1hash, 20, 9, {0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00}}, + {"SHA-256", sha256hash, 32, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}}, + {"SHA-512", sha512hash, 64, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}}, + {NULL, NULL, 0, 0, {}} +}; + static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) { for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) { if (dg_table[dgi].tag == tag) { @@ -339,7 +348,7 @@ static void emrtd_deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t // SHA1 the key unsigned char key[64]; - mbedtls_sha1(data, length + 4, key); + sha1hash(data, length + 4, key); PrintAndLogEx(DEBUG, "key............... %s", sprint_hex_inrow(key, length + 4)); // Set parity bits @@ -822,7 +831,7 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t PrintAndLogEx(DEBUG, "kmrz.............. " _GREEN_("%s"), kmrz); uint8_t kseed[20] = { 0x00 }; - mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); + sha1hash((unsigned char *)kmrz, strlen(kmrz), kseed); PrintAndLogEx(DEBUG, "kseed (sha1)...... %s ", sprint_hex_inrow(kseed, 16)); emrtd_deskey(kseed, KENC_type, 16, kenc); @@ -1531,10 +1540,6 @@ static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_ return PM3_SUCCESS; } -// https://security.stackexchange.com/questions/131241/where-do-magic-constants-for-signature-algorithms-come-from -static const uint8_t emrtd_hashalgo_sha256[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}; -static const uint8_t emrtd_hashalgo_sha512[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}; - static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hashalgo) { uint8_t hashalgoset[64] = { 0x00 }; size_t hashalgosetlen = 0; @@ -1546,16 +1551,23 @@ static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hash PrintAndLogEx(DEBUG, "hash algo set: %s", sprint_hex_inrow(hashalgoset, hashalgosetlen)); - if (memcmp(emrtd_hashalgo_sha256, hashalgoset, 11) == 0) { - *hashalgo = 1; - } else if (memcmp(emrtd_hashalgo_sha512, hashalgoset, 11) == 0) { - *hashalgo = 3; - } else { - *hashalgo = 0; - return PM3_ESOFT; + for (int hashi = 0; hashalg_table[hashi].name != NULL; hashi++) { + PrintAndLogEx(DEBUG, "trying: %s", hashalg_table[hashi].name); + // We're only interested in checking if the length matches to avoid memory shenanigans + if (hashalg_table[hashi].descriptorlen != hashalgosetlen) { + PrintAndLogEx(DEBUG, "len mismatch: %i", hashalgosetlen); + continue; + } + + if (memcmp(hashalg_table[hashi].descriptor, hashalgoset, hashalgosetlen) == 0) { + *hashalgo = hashi; + return PM3_SUCCESS; + } } - return PM3_SUCCESS; + // Return hash algo 0 if we can't find anything + *hashalgo = -1; + return PM3_ESOFT; } static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes, int *hashalgo) { @@ -1580,7 +1592,7 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); if (emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo) != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Failed to parse hash list. Unknown algo?"); + PrintAndLogEx(ERR, "Failed to parse hash list (Unknown algo?). Hash verification won't be available."); } if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { @@ -1615,16 +1627,6 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has return PM3_SUCCESS; } -static void emrtd_calc_dg_hash(uint8_t *data, size_t datalen, uint8_t *hash_out, int hash_algo) { - memset(hash_out, 0, 64); - - if (hash_algo == 1) { - sha256hash(data, datalen, hash_out); - } else if (hash_algo == 3) { - sha512hash(data, datalen, hash_out); - } -} - int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; int resplen = 0; @@ -1703,13 +1705,18 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab if (dg->parser != NULL) dg->parser(response, resplen); + PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo); // Check file hash - emrtd_calc_dg_hash(response, resplen, hash_out, hash_algo); + if (hash_algo != -1) { + PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes[dg->dgnum], hashalg_table[hash_algo].hashlen)); + hashalg_table[hash_algo].hasher(response, resplen, hash_out); + PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(hash_out, hashalg_table[hash_algo].hashlen)); - if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) { - PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); - } else { - PrintAndLogEx(ERR, _RED_("Hash verification failed for EF_DG%i."), dg->dgnum); + if (memcmp(dg_hashes[dg->dgnum], hash_out, hashalg_table[hash_algo].hashlen) == 0) { + PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); + } else { + PrintAndLogEx(ERR, _RED_("Hash verification failed for EF_DG%i."), dg->dgnum); + } } } } @@ -1789,13 +1796,18 @@ int infoHF_EMRTD_offline(const char *path) { if (dg->parser != NULL) dg->parser(data, datalen); + PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo); // Check file hash - emrtd_calc_dg_hash(data, datalen, hash_out, hash_algo); + if (hash_algo != -1) { + PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes[dg->dgnum], hashalg_table[hash_algo].hashlen)); + hashalg_table[hash_algo].hasher(data, datalen, hash_out); + PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(hash_out, hashalg_table[hash_algo].hashlen)); - if (memcmp(dg_hashes[dg->dgnum], hash_out, 64) == 0) { - PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); - } else { - PrintAndLogEx(ERR, _RED_("Hash verification failed for EF_DG%i."), dg->dgnum); + if (memcmp(dg_hashes[dg->dgnum], hash_out, hashalg_table[hash_algo].hashlen) == 0) { + PrintAndLogEx(SUCCESS, _GREEN_("Hash verification passed for EF_DG%i."), dg->dgnum); + } else { + PrintAndLogEx(ERR, _RED_("Hash verification failed for EF_DG%i."), dg->dgnum); + } } free(data); } diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index 72f5f44b9..f7bd64945 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -27,6 +27,14 @@ typedef struct emrtd_dg_s { int (*dumper)(uint8_t *data, size_t datalen); } emrtd_dg_t; +typedef struct emrtd_hashalg_s { + const char *name; + int (*hasher)(uint8_t *datain, int datainlen, uint8_t *dataout); + size_t hashlen; + size_t descriptorlen; + const uint8_t descriptor[15]; +} emrtd_hashalg_t; + int CmdHFeMRTD(const char *Cmd); int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index cb73b3f5c..5c5455bf6 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,15 @@ static int fixed_rand(void *rng_state, unsigned char *output, size_t len) { return 0; } +int sha1hash(uint8_t *input, int length, uint8_t *hash) { + if (!hash || !input) + return 1; + + mbedtls_sha1(input, length, hash); + + return 0; +} + int sha256hash(uint8_t *input, int length, uint8_t *hash) { if (!hash || !input) return 1; diff --git a/client/src/crypto/libpcrypto.h b/client/src/crypto/libpcrypto.h index b4307f454..099fd4423 100644 --- a/client/src/crypto/libpcrypto.h +++ b/client/src/crypto/libpcrypto.h @@ -21,6 +21,7 @@ int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int l int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); +int sha1hash(uint8_t *input, int length, uint8_t *hash); int sha256hash(uint8_t *input, int length, uint8_t *hash); int sha512hash(uint8_t *input, int length, uint8_t *hash);