From bee075ad09d0abcf0e854a826bc4d8f21bd0f7d1 Mon Sep 17 00:00:00 2001 From: Ave Date: Sun, 6 Dec 2020 03:22:20 +0300 Subject: [PATCH 01/40] emrtd: Push initial code Can only read EF_CardAccess currently, but has abstractions for selecting AIDs and reading files, which was the Hard Part so far Based heavily on mrpkey by rfidiot --- client/CMakeLists.txt | 1 + client/Makefile | 1 + client/src/cmdhf.c | 38 ++++--- client/src/cmdhfemrtd.c | 247 ++++++++++++++++++++++++++++++++++++++++ client/src/cmdhfemrtd.h | 19 ++++ 5 files changed, 288 insertions(+), 18 deletions(-) create mode 100644 client/src/cmdhfemrtd.c create mode 100644 client/src/cmdhfemrtd.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4ea7acf36..c67104158 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -228,6 +228,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhf15.c ${PM3_ROOT}/client/src/cmdhfcryptorf.c ${PM3_ROOT}/client/src/cmdhfepa.c + ${PM3_ROOT}/client/src/cmdhfemrtd.c ${PM3_ROOT}/client/src/cmdhffelica.c ${PM3_ROOT}/client/src/cmdhffido.c ${PM3_ROOT}/client/src/cmdhficlass.c diff --git a/client/Makefile b/client/Makefile index 9c624722c..9cfc4fc4b 100644 --- a/client/Makefile +++ b/client/Makefile @@ -469,6 +469,7 @@ SRCS = aiddesfire.c \ cmdhf15.c \ cmdhfcryptorf.c \ cmdhfepa.c \ + cmdhfemrtd.c \ cmdhffelica.c \ cmdhffido.c \ cmdhficlass.c \ diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index fa100e13d..94985579e 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -23,6 +23,7 @@ #include "cmdhf14b.h" // ISO14443-B #include "cmdhf15.h" // ISO15693 #include "cmdhfepa.h" +#include "cmdhfemrtd.h" // eMRTD #include "cmdhflegic.h" // LEGIC #include "cmdhficlass.h" // ICLASS #include "cmdhfmf.h" // CLASSIC @@ -357,24 +358,25 @@ int CmdHFPlot(const char *Cmd) { static command_t CommandTable[] = { {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("High Frequency") " -----------------------"}, - {"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"}, - {"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"}, - {"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"}, -// {"cryptorf", CmdHFCryptoRF, AlwaysAvailable, "{ CryptoRF RFIDs... }"}, - {"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"}, - {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs... }"}, - {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, - {"iclass", CmdHFiClass, AlwaysAvailable, "{ ICLASS RFIDs... }"}, - {"legic", CmdHFLegic, AlwaysAvailable, "{ LEGIC RFIDs... }"}, - {"lto", CmdHFLTO, AlwaysAvailable, "{ LTO Cartridge Memory RFIDs... }"}, - {"mf", CmdHFMF, AlwaysAvailable, "{ MIFARE RFIDs... }"}, - {"mfp", CmdHFMFP, AlwaysAvailable, "{ MIFARE Plus RFIDs... }"}, - {"mfu", CmdHFMFUltra, AlwaysAvailable, "{ MIFARE Ultralight RFIDs... }"}, - {"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"}, - {"st", CmdHF_ST, AlwaysAvailable, "{ ST Rothult RFIDs... }"}, - {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, - {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, - {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, + {"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"}, + {"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"}, + {"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"}, +// {"cryptorf", CmdHFCryptoRF, AlwaysAvailable, "{ CryptoRF RFIDs... }"}, + {"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"}, + {"emrtd", CmdHFeMRTD, AlwaysAvailable, "{ Machine Readable Travel Document... }"}, + {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs... }"}, + {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, + {"iclass", CmdHFiClass, AlwaysAvailable, "{ ICLASS RFIDs... }"}, + {"legic", CmdHFLegic, AlwaysAvailable, "{ LEGIC RFIDs... }"}, + {"lto", CmdHFLTO, AlwaysAvailable, "{ LTO Cartridge Memory RFIDs... }"}, + {"mf", CmdHFMF, AlwaysAvailable, "{ MIFARE RFIDs... }"}, + {"mfp", CmdHFMFP, AlwaysAvailable, "{ MIFARE Plus RFIDs... }"}, + {"mfu", CmdHFMFUltra, AlwaysAvailable, "{ MIFARE Ultralight RFIDs... }"}, + {"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"}, + {"st", CmdHF_ST, AlwaysAvailable, "{ ST Rothult RFIDs... }"}, + {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, + {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, + {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c new file mode 100644 index 000000000..cc27a86f1 --- /dev/null +++ b/client/src/cmdhfemrtd.c @@ -0,0 +1,247 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2020 A. Ozkal +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Electronic Machine Readable Travel Document commands +//----------------------------------------------------------------------------- + +#include "cmdhfemrtd.h" +#include +#include "fileutils.h" +#include "cmdparser.h" // command_t +#include "comms.h" // clearCommandBuffer +#include "cmdtrace.h" +#include "cliparser.h" +#include "crc16.h" +#include "cmdhf14a.h" +#include "protocols.h" // definitions of ISO14A/7816 protocol +#include "emv/apduinfo.h" // GetAPDUCodeDescription + +#define TIMEOUT 2000 + +static int CmdHelp(const char *Cmd); + +static uint16_t get_sw(uint8_t *d, uint8_t n) { + if (n < 2) + return 0; + + n -= 2; + return d[n] * 0x0100 + d[n + 1]; +} + +static int select_aid(const char *select_by, const char *file_id) { + bool activate_field = true; + bool keep_field_on = true; + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + size_t file_id_len = strlen(file_id) / 2; + + char cmd[50]; + sprintf(cmd, "00A4%s0C%02lu%s", select_by, file_id_len, file_id); + PrintAndLogEx(INFO, "Sending: %s", cmd); + + uint8_t aSELECT_AID[80]; + int aSELECT_AID_n = 0; + param_gethex_to_eol(cmd, 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res) { + DropField(); + return false; + } + + if (resplen < 2) { + DropField(); + return false; + } + PrintAndLogEx(INFO, "Resp: %s", sprint_hex(response, resplen)); + + uint16_t sw = get_sw(response, resplen); + if (sw != 0x9000) { + PrintAndLogEx(ERR, "Selecting Card Access aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return false; + } + return true; +} + +static int asn1datalength(uint8_t *datain, int datainlen) { + char* dataintext = sprint_hex_inrow(datain, datainlen); + + // lazy - https://stackoverflow.com/a/4214350/3286892 + char subbuff[8]; + memcpy(subbuff, &dataintext[2], 2); + subbuff[2] = '\0'; + + int thing = (int)strtol(subbuff, NULL, 16); + if (thing <= 0x7f) { + return thing; + } else if (thing == 0x81) { + memcpy(subbuff, &dataintext[2], 3); + subbuff[3] = '\0'; + return (int)strtol(subbuff, NULL, 16); + } else if (thing == 0x82) { + memcpy(subbuff, &dataintext[2], 5); + subbuff[5] = '\0'; + return (int)strtol(subbuff, NULL, 16); + } else if (thing == 0x83) { + memcpy(subbuff, &dataintext[2], 7); + subbuff[7] = '\0'; + return (int)strtol(subbuff, NULL, 16); + } + return false; +} + +static int asn1fieldlength(uint8_t *datain, int datainlen) { + char* dataintext = sprint_hex_inrow(datain, datainlen); + + // lazy - https://stackoverflow.com/a/4214350/3286892 + char subbuff[8]; + memcpy(subbuff, &dataintext[2], 2); + subbuff[2] = '\0'; + + int thing = (int)strtol(subbuff, NULL, 16); + if (thing <= 0x7f) { + return 2; + } else if (thing == 0x81) { + return 4; + } else if (thing == 0x82) { + return 6; + } else if (thing == 0x83) { + return 8; + } + return false; +} + +static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + bool activate_field = false; + bool keep_field_on = true; + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + char cmd[50]; + sprintf(cmd, "00B0%04i%02i", offset, bytes_to_read); + PrintAndLogEx(INFO, "Sending: %s", cmd); + + uint8_t aREAD_BINARY[80]; + int aREAD_BINARY_n = 0; + param_gethex_to_eol(cmd, 0, aREAD_BINARY, sizeof(aREAD_BINARY), &aREAD_BINARY_n); + int res = ExchangeAPDU14a(aREAD_BINARY, aREAD_BINARY_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res) { + DropField(); + return false; + } + PrintAndLogEx(INFO, "Resp: %s", sprint_hex(response, resplen)); + + // drop sw + memcpy(dataout, &response, resplen - 2); + *dataoutlen = (resplen - 2); + + uint16_t sw = get_sw(response, resplen); + if (sw != 0x9000) { + PrintAndLogEx(ERR, "Reading binary failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return false; + } + return true; +} + +static int read_file(uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + uint8_t tempresponse[PM3_CMD_DATA_SIZE]; + int tempresplen = 0; + + if (!_read_binary(0, 4, response, sizeof(response), &resplen)) { + return false; + } + + int datalen = asn1datalength(response, resplen); + int readlen = datalen - (3 - asn1fieldlength(response, resplen) / 2); + int offset = 4; + int toread; + + while (readlen > 0) { + toread = readlen; + if (readlen > 118) { + toread = 118; + } + + if (!_read_binary(offset, toread, tempresponse, sizeof(tempresponse), &tempresplen)) { + return false; + } + + memcpy(&response[resplen], &tempresponse, tempresplen); + offset += toread; + readlen -= toread; + resplen += tempresplen; + } + + memcpy(dataout, &response, resplen); + *dataoutlen = resplen; + return true; +} + +int infoHF_EMRTD(void) { + // const uint8_t *data + if (select_aid("02", "011c")) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + read_file(response, sizeof(response), &resplen); + + PrintAndLogEx(INFO, "EF_CardAccess: %s", sprint_hex(response, resplen)); + } else { + PrintAndLogEx(INFO, "PACE unsupported. Will not read EF_CardAccess."); + } + + DropField(); + return PM3_SUCCESS; +} + +static int cmd_hf_emrtd_info(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf emrtd info", + "Get info about an eMRTD", + "hf emrtd info" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + return infoHF_EMRTD(); +} + +static int cmd_hf_emrtd_list(const char *Cmd) { + char args[128] = {0}; + if (strlen(Cmd) == 0) { + snprintf(args, sizeof(args), "-t 7816"); + } else { + strncpy(args, Cmd, sizeof(args) - 1); + } + return CmdTraceList(args); +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"info", cmd_hf_emrtd_info, IfPm3Iso14443a, "Tag information"}, + {"list", cmd_hf_emrtd_list, AlwaysAvailable, "List ISO 14443A/7816 history"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFeMRTD(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h new file mode 100644 index 000000000..62b0a5bce --- /dev/null +++ b/client/src/cmdhfemrtd.h @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2020 A. Ozkal +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Electronic Machine Readable Travel Document commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFEMRTD_H__ +#define CMDHFEMRTD_H__ + +#include "common.h" + +int CmdHFeMRTD(const char *Cmd); + +int infoHF_EMRTD(void); +#endif From 2c5ab219482abb5af103d3155aac2572279a3410 Mon Sep 17 00:00:00 2001 From: Ave Date: Sun, 6 Dec 2020 03:39:16 +0300 Subject: [PATCH 02/40] emtrd: Switch magic numbers over to named defines --- client/src/cmdhfemrtd.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index cc27a86f1..e4a10562c 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -22,6 +22,22 @@ #define TIMEOUT 2000 +// ISO7816 commands +#define SELECT "A4" +#define GET_CHALLENGE "84" +#define READ_BINARY "B0" +#define P1_SELECT_BY_EF "02" +#define P1_SELECT_BY_NAME "04" +#define P2_PROPRIETARY "0C" + +// File IDs +#define EF_CARDACCESS "011C" +#define EF_COM "011E" +#define EF_DG1 "0101" + +// App IDs +#define AID_MRTD "A0000002471001" + static int CmdHelp(const char *Cmd); static uint16_t get_sw(uint8_t *d, uint8_t n) { @@ -41,7 +57,7 @@ static int select_aid(const char *select_by, const char *file_id) { size_t file_id_len = strlen(file_id) / 2; char cmd[50]; - sprintf(cmd, "00A4%s0C%02lu%s", select_by, file_id_len, file_id); + sprintf(cmd, "00%s%s0C%02lu%s", SELECT, select_by, file_id_len, file_id); PrintAndLogEx(INFO, "Sending: %s", cmd); uint8_t aSELECT_AID[80]; @@ -57,7 +73,7 @@ static int select_aid(const char *select_by, const char *file_id) { DropField(); return false; } - PrintAndLogEx(INFO, "Resp: %s", sprint_hex(response, resplen)); + PrintAndLogEx(INFO, "Response: %s", sprint_hex(response, resplen)); uint16_t sw = get_sw(response, resplen); if (sw != 0x9000) { @@ -123,7 +139,7 @@ static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int max int resplen = 0; char cmd[50]; - sprintf(cmd, "00B0%04i%02i", offset, bytes_to_read); + sprintf(cmd, "00%s%04i%02i", READ_BINARY, offset, bytes_to_read); PrintAndLogEx(INFO, "Sending: %s", cmd); uint8_t aREAD_BINARY[80]; @@ -134,7 +150,7 @@ static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int max DropField(); return false; } - PrintAndLogEx(INFO, "Resp: %s", sprint_hex(response, resplen)); + PrintAndLogEx(INFO, "Response: %s", sprint_hex(response, resplen)); // drop sw memcpy(dataout, &response, resplen - 2); @@ -187,7 +203,7 @@ static int read_file(uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { int infoHF_EMRTD(void) { // const uint8_t *data - if (select_aid("02", "011c")) { + if (select_aid(P1_SELECT_BY_EF, EF_CARDACCESS)) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; From 885f4b52fd48be8a019f8171251b6615654ea8d8 Mon Sep 17 00:00:00 2001 From: Ave Date: Sun, 6 Dec 2020 03:42:41 +0300 Subject: [PATCH 03/40] emrtd: Rename select_aid to select_file --- client/src/cmdhfemrtd.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index e4a10562c..c1dd246b1 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -48,7 +48,7 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) { return d[n] * 0x0100 + d[n + 1]; } -static int select_aid(const char *select_by, const char *file_id) { +static int select_file(const char *select_by, const char *file_id) { bool activate_field = true; bool keep_field_on = true; uint8_t response[PM3_CMD_DATA_SIZE]; @@ -60,10 +60,10 @@ static int select_aid(const char *select_by, const char *file_id) { sprintf(cmd, "00%s%s0C%02lu%s", SELECT, select_by, file_id_len, file_id); PrintAndLogEx(INFO, "Sending: %s", cmd); - uint8_t aSELECT_AID[80]; - int aSELECT_AID_n = 0; - param_gethex_to_eol(cmd, 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); - int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + uint8_t aSELECT_FILE[80]; + int aSELECT_FILE_n = 0; + param_gethex_to_eol(cmd, 0, aSELECT_FILE, sizeof(aSELECT_FILE), &aSELECT_FILE_n); + int res = ExchangeAPDU14a(aSELECT_FILE, aSELECT_FILE_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res) { DropField(); return false; @@ -77,7 +77,7 @@ static int select_aid(const char *select_by, const char *file_id) { uint16_t sw = get_sw(response, resplen); if (sw != 0x9000) { - PrintAndLogEx(ERR, "Selecting Card Access aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(ERR, "Selecting file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return false; } @@ -203,7 +203,7 @@ static int read_file(uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { int infoHF_EMRTD(void) { // const uint8_t *data - if (select_aid(P1_SELECT_BY_EF, EF_CARDACCESS)) { + if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS)) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; From 84a1059d9318a017f0ba8a0e2bfc620a98c65e1c Mon Sep 17 00:00:00 2001 From: Ave Date: Sun, 6 Dec 2020 04:48:38 +0300 Subject: [PATCH 04/40] emrtd: Add BAC check --- client/src/cmdhfemrtd.c | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index c1dd246b1..c43f614ce 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -48,9 +48,7 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) { return d[n] * 0x0100 + d[n + 1]; } -static int select_file(const char *select_by, const char *file_id) { - bool activate_field = true; - bool keep_field_on = true; +static int select_file(const char *select_by, const char *file_id, bool activate_field, bool keep_field_on) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; @@ -70,7 +68,6 @@ static int select_file(const char *select_by, const char *file_id) { } if (resplen < 2) { - DropField(); return false; } PrintAndLogEx(INFO, "Response: %s", sprint_hex(response, resplen)); @@ -78,7 +75,6 @@ static int select_file(const char *select_by, const char *file_id) { uint16_t sw = get_sw(response, resplen); if (sw != 0x9000) { PrintAndLogEx(ERR, "Selecting file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - DropField(); return false; } return true; @@ -159,7 +155,6 @@ static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int max uint16_t sw = get_sw(response, resplen); if (sw != 0x9000) { PrintAndLogEx(ERR, "Reading binary failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - DropField(); return false; } return true; @@ -202,18 +197,42 @@ static int read_file(uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { } int infoHF_EMRTD(void) { - // const uint8_t *data - if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS)) { - uint8_t response[PM3_CMD_DATA_SIZE]; - int resplen = 0; + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + // bool BAC = true; + // Select and read EF_CardAccess + if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS, true, true)) { read_file(response, sizeof(response), &resplen); - PrintAndLogEx(INFO, "EF_CardAccess: %s", sprint_hex(response, resplen)); } else { PrintAndLogEx(INFO, "PACE unsupported. Will not read EF_CardAccess."); } + // Select MRTD applet + if (select_file(P1_SELECT_BY_NAME, AID_MRTD, false, true) == false) { + PrintAndLogEx(ERR, "Couldn't select the MRTD application."); + return PM3_ESOFT; + } + + // Select EF_COM + if (select_file(P1_SELECT_BY_EF, EF_COM, false, true) == false) { + // BAC = true; + PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt auth."); + } else { + // BAC = false; + // Select EF_DG1 + select_file(P1_SELECT_BY_EF, EF_DG1, false, true); + + if (read_file(response, sizeof(response), &resplen) == false) { + // BAC = true; + PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt auth."); + } else { + // BAC = false; + PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex(response, resplen)); + } + } + DropField(); return PM3_SUCCESS; } From 1149c56696d13e4dfd1473c003829e2daa47bb73 Mon Sep 17 00:00:00 2001 From: Ave Date: Sun, 6 Dec 2020 23:52:08 +0300 Subject: [PATCH 05/40] emrtd: Add kmrz calc --- client/src/cmdhfemrtd.c | 48 +++++++++++++++++++++++++++++++++++++++-- client/src/cmdhfemrtd.h | 2 +- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index c43f614ce..1be269c67 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -80,6 +80,28 @@ static int select_file(const char *select_by, const char *file_id, bool activate return true; } +static char calculate_check_digit(char *data) { + int mrz_weight[] = {7, 3, 1}; + int cd = 0; + int value = 0; + char d; + + for (int i = 0; i < strlen(data); i++) { + d = data[i]; + if ('A' <= d && d <= 'Z') { + value = d - 55; + } else if ('a' <= d && d <= 'z') { + value = d - 87; + } else if (d == '<') { + value = 0; + } else { // Numbers + value = d - 48; + } + cd += value * mrz_weight[i % 3]; + } + return cd % 10; +} + static int asn1datalength(uint8_t *datain, int datainlen) { char* dataintext = sprint_hex_inrow(datain, datainlen); @@ -196,7 +218,7 @@ static int read_file(uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { return true; } -int infoHF_EMRTD(void) { +int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; // bool BAC = true; @@ -233,6 +255,14 @@ int infoHF_EMRTD(void) { } } + char documentnumbercd = calculate_check_digit(documentnumber); + char dobcd = calculate_check_digit(dob); + char expirycd = calculate_check_digit(expiry); + + char kmrz[25]; + sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); + PrintAndLogEx(INFO, "kmrz: %s", kmrz); + DropField(); return PM3_SUCCESS; } @@ -246,11 +276,25 @@ static int cmd_hf_emrtd_info(const char *Cmd) { void *argtable[] = { arg_param_begin, + arg_str1("n", "documentnumber", "", "9 character document number"), + arg_str1("d", "dateofbirth", "", "date of birth in YYMMDD format"), + arg_str1("e", "expiry", "", "expiry in YYMMDD format"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t docnum[9]; + uint8_t dob[6]; + uint8_t expiry[6]; + int docnumlen = sizeof(docnum); + int doblen = sizeof(dob); + int expirylen = sizeof(expiry); + CLIGetStrWithReturn(ctx, 1, docnum, &docnumlen); + CLIGetStrWithReturn(ctx, 2, dob, &doblen); + CLIGetStrWithReturn(ctx, 3, expiry, &expirylen); + CLIParserFree(ctx); - return infoHF_EMRTD(); + return infoHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry); } static int cmd_hf_emrtd_list(const char *Cmd) { diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index 62b0a5bce..ba9e3f2b3 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -15,5 +15,5 @@ int CmdHFeMRTD(const char *Cmd); -int infoHF_EMRTD(void); +int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry); #endif From 9f6309cc23cbf5cca11720f9435871ab2ed203c3 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 7 Dec 2020 00:40:01 +0300 Subject: [PATCH 06/40] emrtd: Add kseed calc, fix kmrz calc --- client/src/cmdhfemrtd.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 1be269c67..91f97d001 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -19,6 +19,7 @@ #include "cmdhf14a.h" #include "protocols.h" // definitions of ISO14A/7816 protocol #include "emv/apduinfo.h" // GetAPDUCodeDescription +#include "sha1.h" // KSeed calculation #define TIMEOUT 2000 @@ -254,6 +255,9 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex(response, resplen)); } } + PrintAndLogEx(INFO, "doc: %s", documentnumber); + PrintAndLogEx(INFO, "dob: %s", dob); + PrintAndLogEx(INFO, "exp: %s", expiry); char documentnumbercd = calculate_check_digit(documentnumber); char dobcd = calculate_check_digit(dob); @@ -263,6 +267,10 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); PrintAndLogEx(INFO, "kmrz: %s", kmrz); + unsigned char kseed[20] = {0x00}; + mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); + PrintAndLogEx(INFO, "kseed: %s", sprint_hex_inrow(kseed, 16)); + DropField(); return PM3_SUCCESS; } @@ -283,9 +291,9 @@ static int cmd_hf_emrtd_info(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t docnum[9]; - uint8_t dob[6]; - uint8_t expiry[6]; + uint8_t docnum[10]; + uint8_t dob[7]; + uint8_t expiry[7]; int docnumlen = sizeof(docnum); int doblen = sizeof(dob); int expirylen = sizeof(expiry); From 49e4321382611d01a303f1c9ad6b78eb2aa39fb6 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 7 Dec 2020 01:17:03 +0300 Subject: [PATCH 07/40] emrtd: Get challenge also some code "cleanup" which makes this whole shit less mem safe but shh --- client/src/cmdhfemrtd.c | 60 ++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 91f97d001..77ce7adc8 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -151,7 +151,39 @@ static int asn1fieldlength(uint8_t *datain, int datainlen) { return false; } -static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { +static int get_challenge(int length, uint8_t *dataout, int *dataoutlen) { + bool activate_field = false; + bool keep_field_on = true; + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + char cmd[50]; + sprintf(cmd, "00%s0000%02X", GET_CHALLENGE, length); + PrintAndLogEx(INFO, "Sending: %s", cmd); + + uint8_t aGET_CHALLENGE[80]; + int aGET_CHALLENGE_n = 0; + param_gethex_to_eol(cmd, 0, aGET_CHALLENGE, sizeof(aGET_CHALLENGE), &aGET_CHALLENGE_n); + int res = ExchangeAPDU14a(aGET_CHALLENGE, aGET_CHALLENGE_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res) { + DropField(); + return false; + } + PrintAndLogEx(INFO, "Response: %s", sprint_hex(response, resplen)); + + // drop sw + memcpy(dataout, &response, resplen - 2); + *dataoutlen = (resplen - 2); + + uint16_t sw = get_sw(response, resplen); + if (sw != 0x9000) { + PrintAndLogEx(ERR, "Getting challenge failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + return false; + } + return true; +} + +static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { bool activate_field = false; bool keep_field_on = true; uint8_t response[PM3_CMD_DATA_SIZE]; @@ -183,13 +215,13 @@ static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int max return true; } -static int read_file(uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { +static int read_file(uint8_t *dataout, int *dataoutlen) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; uint8_t tempresponse[PM3_CMD_DATA_SIZE]; int tempresplen = 0; - if (!_read_binary(0, 4, response, sizeof(response), &resplen)) { + if (!_read_binary(0, 4, response, &resplen)) { return false; } @@ -204,7 +236,7 @@ static int read_file(uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { toread = 118; } - if (!_read_binary(offset, toread, tempresponse, sizeof(tempresponse), &tempresplen)) { + if (!_read_binary(offset, toread, tempresponse, &tempresplen)) { return false; } @@ -221,12 +253,13 @@ static int read_file(uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[PM3_CMD_DATA_SIZE]; + uint8_t challenge[8]; int resplen = 0; // bool BAC = true; // Select and read EF_CardAccess if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS, true, true)) { - read_file(response, sizeof(response), &resplen); + read_file(response, &resplen); PrintAndLogEx(INFO, "EF_CardAccess: %s", sprint_hex(response, resplen)); } else { PrintAndLogEx(INFO, "PACE unsupported. Will not read EF_CardAccess."); @@ -235,6 +268,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select MRTD applet if (select_file(P1_SELECT_BY_NAME, AID_MRTD, false, true) == false) { PrintAndLogEx(ERR, "Couldn't select the MRTD application."); + DropField(); return PM3_ESOFT; } @@ -247,7 +281,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select EF_DG1 select_file(P1_SELECT_BY_EF, EF_DG1, false, true); - if (read_file(response, sizeof(response), &resplen) == false) { + if (read_file(response, &resplen) == false) { // BAC = true; PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt auth."); } else { @@ -271,6 +305,14 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); PrintAndLogEx(INFO, "kseed: %s", sprint_hex_inrow(kseed, 16)); + // Get Challenge + if (get_challenge(8, challenge, &resplen) == false) { + PrintAndLogEx(ERR, "Couldn't get challenge."); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "challenge: %s", sprint_hex_inrow(challenge, 8)); + DropField(); return PM3_SUCCESS; } @@ -294,9 +336,9 @@ static int cmd_hf_emrtd_info(const char *Cmd) { uint8_t docnum[10]; uint8_t dob[7]; uint8_t expiry[7]; - int docnumlen = sizeof(docnum); - int doblen = sizeof(dob); - int expirylen = sizeof(expiry); + int docnumlen = 9; + int doblen = 6; + int expirylen = 6; CLIGetStrWithReturn(ctx, 1, docnum, &docnumlen); CLIGetStrWithReturn(ctx, 2, dob, &doblen); CLIGetStrWithReturn(ctx, 3, expiry, &expirylen); From b0fb5bfff64e95ce00225d0750a78f1ecb46df2f Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 7 Dec 2020 01:38:39 +0300 Subject: [PATCH 08/40] emrtd: Reduce code repetition --- client/src/cmdhfemrtd.c | 83 ++++++++++++----------------------------- 1 file changed, 23 insertions(+), 60 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 77ce7adc8..17d12e08e 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -49,20 +49,16 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) { return d[n] * 0x0100 + d[n + 1]; } -static int select_file(const char *select_by, const char *file_id, bool activate_field, bool keep_field_on) { +static int exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, bool activate_field, bool keep_field_on) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; - size_t file_id_len = strlen(file_id) / 2; - - char cmd[50]; - sprintf(cmd, "00%s%s0C%02lu%s", SELECT, select_by, file_id_len, file_id); PrintAndLogEx(INFO, "Sending: %s", cmd); - uint8_t aSELECT_FILE[80]; - int aSELECT_FILE_n = 0; - param_gethex_to_eol(cmd, 0, aSELECT_FILE, sizeof(aSELECT_FILE), &aSELECT_FILE_n); - int res = ExchangeAPDU14a(aSELECT_FILE, aSELECT_FILE_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + uint8_t aCMD[80]; + int aCMD_n = 0; + param_gethex_to_eol(cmd, 0, aCMD, sizeof(aCMD), &aCMD_n); + int res = ExchangeAPDU14a(aCMD, aCMD_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res) { DropField(); return false; @@ -73,9 +69,13 @@ static int select_file(const char *select_by, const char *file_id, bool activate } PrintAndLogEx(INFO, "Response: %s", sprint_hex(response, resplen)); + // drop sw + memcpy(dataout, &response, resplen - 2); + *dataoutlen = (resplen - 2); + uint16_t sw = get_sw(response, resplen); if (sw != 0x9000) { - PrintAndLogEx(ERR, "Selecting file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(ERR, "Command %s failed (%04x - %s).", cmd, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); return false; } return true; @@ -151,68 +151,31 @@ static int asn1fieldlength(uint8_t *datain, int datainlen) { return false; } -static int get_challenge(int length, uint8_t *dataout, int *dataoutlen) { - bool activate_field = false; - bool keep_field_on = true; +static int select_file(const char *select_by, const char *file_id, bool activate_field, bool keep_field_on) { + size_t file_id_len = strlen(file_id) / 2; + + // Get data even tho we'll not use it uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; + char cmd[50]; + sprintf(cmd, "00%s%s0C%02lu%s", SELECT, select_by, file_id_len, file_id); + + return exchange_commands(cmd, response, &resplen, activate_field, keep_field_on); +} + +static int get_challenge(int length, uint8_t *dataout, int *dataoutlen) { char cmd[50]; sprintf(cmd, "00%s0000%02X", GET_CHALLENGE, length); - PrintAndLogEx(INFO, "Sending: %s", cmd); - uint8_t aGET_CHALLENGE[80]; - int aGET_CHALLENGE_n = 0; - param_gethex_to_eol(cmd, 0, aGET_CHALLENGE, sizeof(aGET_CHALLENGE), &aGET_CHALLENGE_n); - int res = ExchangeAPDU14a(aGET_CHALLENGE, aGET_CHALLENGE_n, activate_field, keep_field_on, response, sizeof(response), &resplen); - if (res) { - DropField(); - return false; - } - PrintAndLogEx(INFO, "Response: %s", sprint_hex(response, resplen)); - - // drop sw - memcpy(dataout, &response, resplen - 2); - *dataoutlen = (resplen - 2); - - uint16_t sw = get_sw(response, resplen); - if (sw != 0x9000) { - PrintAndLogEx(ERR, "Getting challenge failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - return false; - } - return true; + return exchange_commands(cmd, dataout, dataoutlen, false, true); } static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { - bool activate_field = false; - bool keep_field_on = true; - uint8_t response[PM3_CMD_DATA_SIZE]; - int resplen = 0; - char cmd[50]; sprintf(cmd, "00%s%04i%02i", READ_BINARY, offset, bytes_to_read); - PrintAndLogEx(INFO, "Sending: %s", cmd); - uint8_t aREAD_BINARY[80]; - int aREAD_BINARY_n = 0; - param_gethex_to_eol(cmd, 0, aREAD_BINARY, sizeof(aREAD_BINARY), &aREAD_BINARY_n); - int res = ExchangeAPDU14a(aREAD_BINARY, aREAD_BINARY_n, activate_field, keep_field_on, response, sizeof(response), &resplen); - if (res) { - DropField(); - return false; - } - PrintAndLogEx(INFO, "Response: %s", sprint_hex(response, resplen)); - - // drop sw - memcpy(dataout, &response, resplen - 2); - *dataoutlen = (resplen - 2); - - uint16_t sw = get_sw(response, resplen); - if (sw != 0x9000) { - PrintAndLogEx(ERR, "Reading binary failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - return false; - } - return true; + return exchange_commands(cmd, dataout, dataoutlen, false, true); } static int read_file(uint8_t *dataout, int *dataoutlen) { From a7478443b31c6a066528462de76c24a7ed8df901 Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 9 Dec 2020 01:15:09 +0300 Subject: [PATCH 09/40] emrtd: calc kenc and kmac --- client/src/cmdhfemrtd.c | 45 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 17d12e08e..75aafed3b 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -11,15 +11,17 @@ #include "cmdhfemrtd.h" #include #include "fileutils.h" -#include "cmdparser.h" // command_t -#include "comms.h" // clearCommandBuffer +#include "cmdparser.h" // command_t +#include "comms.h" // clearCommandBuffer #include "cmdtrace.h" #include "cliparser.h" #include "crc16.h" #include "cmdhf14a.h" -#include "protocols.h" // definitions of ISO14A/7816 protocol -#include "emv/apduinfo.h" // GetAPDUCodeDescription -#include "sha1.h" // KSeed calculation +#include "protocols.h" // definitions of ISO14A/7816 protocol +#include "emv/apduinfo.h" // GetAPDUCodeDescription +#include "sha1.h" // KSeed calculation etc +#include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt +#include "des.h" // mbedtls_des_key_set_parity #define TIMEOUT 2000 @@ -151,6 +153,29 @@ static int asn1fieldlength(uint8_t *datain, int datainlen) { return false; } + +static void deskey(uint8_t *seed, uint8_t *type, int length, uint8_t *dataout) { + PrintAndLogEx(INFO, "seed: %s", sprint_hex_inrow(seed, 16)); + + // combine seed and type + uint8_t data[50]; + memcpy(data, seed, 16); + memcpy(data + 16, type, 4); + PrintAndLogEx(INFO, "data: %s", sprint_hex_inrow(data, 20)); + + // SHA1 the key + unsigned char key[20]; + mbedtls_sha1(data, 20, key); + PrintAndLogEx(INFO, "key: %s", sprint_hex_inrow(key, 20)); + + // Set parity bits + mbedtls_des_key_set_parity(key); + mbedtls_des_key_set_parity(key + 8); + PrintAndLogEx(INFO, "post-parity key: %s", sprint_hex_inrow(key, 20)); + + memcpy(dataout, &key, length); +} + static int select_file(const char *select_by, const char *file_id, bool activate_field, bool keep_field_on) { size_t file_id_len = strlen(file_id) / 2; @@ -217,6 +242,11 @@ static int read_file(uint8_t *dataout, int *dataoutlen) { int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[PM3_CMD_DATA_SIZE]; uint8_t challenge[8]; + // TODO: get these _types into a better spot + uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; + uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; + uint8_t kenc[50]; + uint8_t kmac[50]; int resplen = 0; // bool BAC = true; @@ -268,6 +298,11 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); PrintAndLogEx(INFO, "kseed: %s", sprint_hex_inrow(kseed, 16)); + deskey(kseed, KENC_type, 16, kenc); + deskey(kseed, KMAC_type, 16, kmac); + PrintAndLogEx(INFO, "kenc: %s", sprint_hex_inrow(kenc, 16)); + PrintAndLogEx(INFO, "kmac: %s", sprint_hex_inrow(kmac, 16)); + // Get Challenge if (get_challenge(8, challenge, &resplen) == false) { PrintAndLogEx(ERR, "Couldn't get challenge."); From 69be5f8b253df22ac4bc40aa26202f710ee57c98 Mon Sep 17 00:00:00 2001 From: Ave Date: Wed, 9 Dec 2020 04:48:17 +0300 Subject: [PATCH 10/40] emrtd: calculate S and e_ifd --- client/src/cmdhfemrtd.c | 43 +++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 75aafed3b..5840c875b 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -8,6 +8,8 @@ // High frequency Electronic Machine Readable Travel Document commands //----------------------------------------------------------------------------- +// This code is heavily based on mrpkey.py of RFIDIOt + #include "cmdhfemrtd.h" #include #include "fileutils.h" @@ -27,6 +29,7 @@ // ISO7816 commands #define SELECT "A4" +#define EXTERNAL_AUTHENTICATE "82" #define GET_CHALLENGE "84" #define READ_BINARY "B0" #define P1_SELECT_BY_EF "02" @@ -241,14 +244,18 @@ static int read_file(uint8_t *dataout, int *dataoutlen) { int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[PM3_CMD_DATA_SIZE]; - uint8_t challenge[8]; - // TODO: get these _types into a better spot - uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; - uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; + uint8_t rnd_ic[8]; uint8_t kenc[50]; uint8_t kmac[50]; int resplen = 0; // bool BAC = true; + uint8_t S[32]; + // TODO: Code sponsored jointly by duracell and sony + uint8_t rnd_ifd[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; + uint8_t k_ifd[16] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; + // TODO: get these _types into a better spot + uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; + uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; // Select and read EF_CardAccess if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS, true, true)) { @@ -304,12 +311,36 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(INFO, "kmac: %s", sprint_hex_inrow(kmac, 16)); // Get Challenge - if (get_challenge(8, challenge, &resplen) == false) { + if (get_challenge(8, rnd_ic, &resplen) == false) { PrintAndLogEx(ERR, "Couldn't get challenge."); DropField(); return PM3_ESOFT; } - PrintAndLogEx(INFO, "challenge: %s", sprint_hex_inrow(challenge, 8)); + PrintAndLogEx(INFO, "rnd_ic: %s", sprint_hex_inrow(rnd_ic, 8)); + + memcpy(S, rnd_ifd, 8); + memcpy(S + 8, rnd_ic, 8); + memcpy(S + 16, k_ifd, 16); + + mbedtls_des3_context ctx; + mbedtls_des3_set2key_enc(&ctx, kenc); + + uint8_t iv[8] = { 0x00 }; + uint8_t e_ifd[8] = { 0x00 }; + + mbedtls_des3_crypt_cbc(&ctx // des3_context + , MBEDTLS_DES_ENCRYPT // int mode + , sizeof(S) // length + , iv // iv[8] + , S // input + , e_ifd // output + ); + + // TODO: get m_ifd by ISO 9797-1 Algo 3(e_ifd, m_mac) + // TODO: get cmd_data by e_ifd + m_ifd + // TODO: iso_7816_external_authenticate(passport.ToHex(cmd_data),Kmac) + + PrintAndLogEx(INFO, "S: %s", sprint_hex_inrow(S, 32)); DropField(); return PM3_SUCCESS; From 6086ede2ed66dff1a4bf9d98fc421eaa3e0be8f3 Mon Sep 17 00:00:00 2001 From: Ave Date: Thu, 10 Dec 2020 22:53:01 +0300 Subject: [PATCH 11/40] emrtd: split 3des encryption to its own func --- client/src/cmdhfemrtd.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 5840c875b..d941ed36c 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -156,6 +156,18 @@ static int asn1fieldlength(uint8_t *datain, int datainlen) { return false; } +static void des3_encrypt(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output) { + mbedtls_des3_context ctx; + mbedtls_des3_set2key_enc(&ctx, key); + + mbedtls_des3_crypt_cbc(&ctx // des3_context + , MBEDTLS_DES_ENCRYPT // int mode + , sizeof(input) // length + , iv // iv[8] + , input // input + , output // output + ); +} static void deskey(uint8_t *seed, uint8_t *type, int length, uint8_t *dataout) { PrintAndLogEx(INFO, "seed: %s", sprint_hex_inrow(seed, 16)); @@ -199,6 +211,13 @@ static int get_challenge(int length, uint8_t *dataout, int *dataoutlen) { return exchange_commands(cmd, dataout, dataoutlen, false, true); } +// static int external_authenticate(const char *response, int length, uint8_t *dataout, int *dataoutlen) { +// char cmd[50]; +// sprintf(cmd, "00%s00%02i%02X%02i", EXTERNAL_AUTHENTICATE, length, sprint_hex_inrow(response, length), length); + +// return exchange_commands(cmd, dataout, dataoutlen, false, true); +// } + static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { char cmd[50]; sprintf(cmd, "00%s%04i%02i", READ_BINARY, offset, bytes_to_read); @@ -322,26 +341,18 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { memcpy(S + 8, rnd_ic, 8); memcpy(S + 16, k_ifd, 16); - mbedtls_des3_context ctx; - mbedtls_des3_set2key_enc(&ctx, kenc); + PrintAndLogEx(INFO, "S: %s", sprint_hex_inrow(S, 32)); uint8_t iv[8] = { 0x00 }; uint8_t e_ifd[8] = { 0x00 }; - mbedtls_des3_crypt_cbc(&ctx // des3_context - , MBEDTLS_DES_ENCRYPT // int mode - , sizeof(S) // length - , iv // iv[8] - , S // input - , e_ifd // output - ); + des3_encrypt(iv, kenc, S, e_ifd); + PrintAndLogEx(INFO, "e_ifd: %s", sprint_hex_inrow(e_ifd, 8)); // TODO: get m_ifd by ISO 9797-1 Algo 3(e_ifd, m_mac) // TODO: get cmd_data by e_ifd + m_ifd // TODO: iso_7816_external_authenticate(passport.ToHex(cmd_data),Kmac) - PrintAndLogEx(INFO, "S: %s", sprint_hex_inrow(S, 32)); - DropField(); return PM3_SUCCESS; } From ca04c44384f827605b1d7b0517be0f22cfda50c3 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 11 Dec 2020 04:52:29 +0300 Subject: [PATCH 12/40] emrtd: Implement retail_mac and external authentication --- client/src/cmdhfemrtd.c | 98 +++++++++++++++++++++++++++++++++++------ 1 file changed, 84 insertions(+), 14 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index d941ed36c..4aa9670de 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -60,7 +60,7 @@ static int exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, PrintAndLogEx(INFO, "Sending: %s", cmd); - uint8_t aCMD[80]; + uint8_t aCMD[100]; int aCMD_n = 0; param_gethex_to_eol(cmd, 0, aCMD, sizeof(aCMD), &aCMD_n); int res = ExchangeAPDU14a(aCMD, aCMD_n, activate_field, keep_field_on, response, sizeof(response), &resplen); @@ -156,19 +156,76 @@ static int asn1fieldlength(uint8_t *datain, int datainlen) { return false; } -static void des3_encrypt(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output) { +static void des_encrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) { + mbedtls_des_context ctx_enc; + mbedtls_des_setkey_enc(&ctx_enc, key); + mbedtls_des_crypt_ecb(&ctx_enc, input, output); + mbedtls_des_free(&ctx_enc); +} + +static void des_decrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) { + mbedtls_des_context ctx_dec; + mbedtls_des_setkey_dec(&ctx_dec, key); + mbedtls_des_crypt_ecb(&ctx_dec, input, output); + mbedtls_des_free(&ctx_dec); +} + +static void des3_encrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) { mbedtls_des3_context ctx; mbedtls_des3_set2key_enc(&ctx, key); mbedtls_des3_crypt_cbc(&ctx // des3_context , MBEDTLS_DES_ENCRYPT // int mode - , sizeof(input) // length + , inputlen // length , iv // iv[8] , input // input , output // output ); + mbedtls_des3_free(&ctx); } +static void retail_mac(uint8_t *key, uint8_t *input, uint8_t *output) { + // This code assumes blocklength (n) = 8, and input len of 32 chars + // This code takes inspirations from https://github.com/devinvenable/iso9797algorithm3 + uint8_t k0[8]; + uint8_t k1[8]; + uint8_t intermediate[8] = {0x00}; + uint8_t intermediate_des[32]; + uint8_t block[8]; + uint8_t message[40]; + uint8_t padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // Populate keys + memcpy(k0, key, 8); + memcpy(k1, key + 8, 8); + + // Prepare message + memcpy(message, input, 32); + // Normally this isn't how it works, but it's what mrpkey does... + memcpy(message + 32, padding, 8); + + // Do chaining and encryption + for (int i = 0; i < (sizeof(message) / 8); i++) { + memcpy(block, message + (i * 8), 8); + + // XOR + for (int x = 0; x < 8; x++) { + intermediate[x] = intermediate[x] ^ block[x]; + } + + des_encrypt_ecb(k0, intermediate, intermediate_des); + memcpy(intermediate, intermediate_des, 8); + } + + + des_decrypt_ecb(k1, intermediate, intermediate_des); + memcpy(intermediate, intermediate_des, 8); + + des_encrypt_ecb(k0, intermediate, intermediate_des); + memcpy(output, intermediate_des, 8); +} + + static void deskey(uint8_t *seed, uint8_t *type, int length, uint8_t *dataout) { PrintAndLogEx(INFO, "seed: %s", sprint_hex_inrow(seed, 16)); @@ -211,12 +268,13 @@ static int get_challenge(int length, uint8_t *dataout, int *dataoutlen) { return exchange_commands(cmd, dataout, dataoutlen, false, true); } -// static int external_authenticate(const char *response, int length, uint8_t *dataout, int *dataoutlen) { -// char cmd[50]; -// sprintf(cmd, "00%s00%02i%02X%02i", EXTERNAL_AUTHENTICATE, length, sprint_hex_inrow(response, length), length); +static int external_authenticate(uint8_t *data, int length, uint8_t *dataout, int *dataoutlen) { + char cmd[100]; -// return exchange_commands(cmd, dataout, dataoutlen, false, true); -// } + sprintf(cmd, "00%s0000%02X%s%02X", EXTERNAL_AUTHENTICATE, length, sprint_hex_inrow(data, length), length); + + return exchange_commands(cmd, dataout, dataoutlen, false, true); +} static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { char cmd[50]; @@ -344,14 +402,26 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(INFO, "S: %s", sprint_hex_inrow(S, 32)); uint8_t iv[8] = { 0x00 }; - uint8_t e_ifd[8] = { 0x00 }; + uint8_t e_ifd[32] = { 0x00 }; - des3_encrypt(iv, kenc, S, e_ifd); - PrintAndLogEx(INFO, "e_ifd: %s", sprint_hex_inrow(e_ifd, 8)); + des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd); + PrintAndLogEx(INFO, "e_ifd: %s", sprint_hex_inrow(e_ifd, 32)); - // TODO: get m_ifd by ISO 9797-1 Algo 3(e_ifd, m_mac) - // TODO: get cmd_data by e_ifd + m_ifd - // TODO: iso_7816_external_authenticate(passport.ToHex(cmd_data),Kmac) + uint8_t m_ifd[8] = { 0x00 }; + + retail_mac(kmac, e_ifd, m_ifd); + PrintAndLogEx(INFO, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8)); + + uint8_t cmd_data[40]; + memcpy(cmd_data, e_ifd, 32); + memcpy(cmd_data + 32, m_ifd, 8); + + // Do external authentication + if (external_authenticate(cmd_data, sizeof(cmd_data), response, &resplen) == false) { + PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?"); + DropField(); + return PM3_ESOFT; + } DropField(); return PM3_SUCCESS; From 332b67655c6aca4ae29054957eb0461fe239f146 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 11 Dec 2020 05:38:14 +0300 Subject: [PATCH 13/40] emrtd: Improve logging --- client/src/cmdhfemrtd.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 4aa9670de..3145dd6e6 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -58,7 +58,7 @@ static int exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; - PrintAndLogEx(INFO, "Sending: %s", cmd); + PrintAndLogEx(DEBUG, "Sending: %s", cmd); uint8_t aCMD[100]; int aCMD_n = 0; @@ -72,7 +72,7 @@ static int exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, if (resplen < 2) { return false; } - PrintAndLogEx(INFO, "Response: %s", sprint_hex(response, resplen)); + PrintAndLogEx(DEBUG, "Response: %s", sprint_hex(response, resplen)); // drop sw memcpy(dataout, &response, resplen - 2); @@ -80,7 +80,7 @@ static int exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, uint16_t sw = get_sw(response, resplen); if (sw != 0x9000) { - PrintAndLogEx(ERR, "Command %s failed (%04x - %s).", cmd, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(DEBUG, "Command %s failed (%04x - %s).", cmd, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); return false; } return true; @@ -227,23 +227,23 @@ static void retail_mac(uint8_t *key, uint8_t *input, uint8_t *output) { static void deskey(uint8_t *seed, uint8_t *type, int length, uint8_t *dataout) { - PrintAndLogEx(INFO, "seed: %s", sprint_hex_inrow(seed, 16)); + PrintAndLogEx(DEBUG, "seed: %s", sprint_hex_inrow(seed, 16)); // combine seed and type uint8_t data[50]; memcpy(data, seed, 16); memcpy(data + 16, type, 4); - PrintAndLogEx(INFO, "data: %s", sprint_hex_inrow(data, 20)); + PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, 20)); // SHA1 the key unsigned char key[20]; mbedtls_sha1(data, 20, key); - PrintAndLogEx(INFO, "key: %s", sprint_hex_inrow(key, 20)); + PrintAndLogEx(DEBUG, "key: %s", sprint_hex_inrow(key, 20)); // Set parity bits mbedtls_des_key_set_parity(key); mbedtls_des_key_set_parity(key + 8); - PrintAndLogEx(INFO, "post-parity key: %s", sprint_hex_inrow(key, 20)); + PrintAndLogEx(DEBUG, "post-parity key: %s", sprint_hex_inrow(key, 20)); memcpy(dataout, &key, length); } @@ -352,7 +352,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select EF_COM if (select_file(P1_SELECT_BY_EF, EF_COM, false, true) == false) { // BAC = true; - PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt auth."); + PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); } else { // BAC = false; // Select EF_DG1 @@ -360,15 +360,15 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { if (read_file(response, &resplen) == false) { // BAC = true; - PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt auth."); + PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); } else { // BAC = false; PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex(response, resplen)); } } - PrintAndLogEx(INFO, "doc: %s", documentnumber); - PrintAndLogEx(INFO, "dob: %s", dob); - PrintAndLogEx(INFO, "exp: %s", expiry); + PrintAndLogEx(DEBUG, "doc: %s", documentnumber); + PrintAndLogEx(DEBUG, "dob: %s", dob); + PrintAndLogEx(DEBUG, "exp: %s", expiry); char documentnumbercd = calculate_check_digit(documentnumber); char dobcd = calculate_check_digit(dob); @@ -376,16 +376,16 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { char kmrz[25]; sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); - PrintAndLogEx(INFO, "kmrz: %s", kmrz); + PrintAndLogEx(DEBUG, "kmrz: %s", kmrz); unsigned char kseed[20] = {0x00}; mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); - PrintAndLogEx(INFO, "kseed: %s", sprint_hex_inrow(kseed, 16)); + PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); deskey(kseed, KENC_type, 16, kenc); deskey(kseed, KMAC_type, 16, kmac); - PrintAndLogEx(INFO, "kenc: %s", sprint_hex_inrow(kenc, 16)); - PrintAndLogEx(INFO, "kmac: %s", sprint_hex_inrow(kmac, 16)); + PrintAndLogEx(DEBUG, "kenc: %s", sprint_hex_inrow(kenc, 16)); + PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 16)); // Get Challenge if (get_challenge(8, rnd_ic, &resplen) == false) { @@ -393,24 +393,24 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { DropField(); return PM3_ESOFT; } - PrintAndLogEx(INFO, "rnd_ic: %s", sprint_hex_inrow(rnd_ic, 8)); + PrintAndLogEx(DEBUG, "rnd_ic: %s", sprint_hex_inrow(rnd_ic, 8)); memcpy(S, rnd_ifd, 8); memcpy(S + 8, rnd_ic, 8); memcpy(S + 16, k_ifd, 16); - PrintAndLogEx(INFO, "S: %s", sprint_hex_inrow(S, 32)); + PrintAndLogEx(DEBUG, "S: %s", sprint_hex_inrow(S, 32)); uint8_t iv[8] = { 0x00 }; uint8_t e_ifd[32] = { 0x00 }; des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd); - PrintAndLogEx(INFO, "e_ifd: %s", sprint_hex_inrow(e_ifd, 32)); + PrintAndLogEx(DEBUG, "e_ifd: %s", sprint_hex_inrow(e_ifd, 32)); uint8_t m_ifd[8] = { 0x00 }; retail_mac(kmac, e_ifd, m_ifd); - PrintAndLogEx(INFO, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8)); + PrintAndLogEx(DEBUG, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8)); uint8_t cmd_data[40]; memcpy(cmd_data, e_ifd, 32); @@ -422,6 +422,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { DropField(); return PM3_ESOFT; } + PrintAndLogEx(INFO, "External authentication successful."); DropField(); return PM3_SUCCESS; From 70ecfdf50295481f1516bd94919c373566b17312 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 11 Dec 2020 06:27:47 +0300 Subject: [PATCH 14/40] emrtd: Verify rnd_ifd --- client/src/cmdhfemrtd.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 3145dd6e6..45f1125bd 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -184,6 +184,20 @@ static void des3_encrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inpu mbedtls_des3_free(&ctx); } +static void des3_decrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) { + mbedtls_des3_context ctx; + mbedtls_des3_set2key_dec(&ctx, key); + + mbedtls_des3_crypt_cbc(&ctx // des3_context + , MBEDTLS_DES_DECRYPT // int mode + , inputlen // length + , iv // iv[8] + , input // input + , output // output + ); + mbedtls_des3_free(&ctx); +} + static void retail_mac(uint8_t *key, uint8_t *input, uint8_t *output) { // This code assumes blocklength (n) = 8, and input len of 32 chars // This code takes inspirations from https://github.com/devinvenable/iso9797algorithm3 @@ -424,6 +438,15 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { } PrintAndLogEx(INFO, "External authentication successful."); + uint8_t dec_output[32] = { 0x00 }; + des3_decrypt_cbc(iv, kenc, response, 32, dec_output); + + if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) { + PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match."); + DropField(); + return PM3_ESOFT; + } + DropField(); return PM3_SUCCESS; } From ca3471ffdf9707d4fc155527284df10b9c55441a Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 11 Dec 2020 06:52:26 +0300 Subject: [PATCH 15/40] emrtd: calculate session keys --- client/src/cmdhfemrtd.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 45f1125bd..49c7b9645 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -392,7 +392,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); PrintAndLogEx(DEBUG, "kmrz: %s", kmrz); - unsigned char kseed[20] = {0x00}; + uint8_t kseed[16] = {0x00}; mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); @@ -440,6 +440,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t dec_output[32] = { 0x00 }; des3_decrypt_cbc(iv, kenc, response, 32, dec_output); + PrintAndLogEx(DEBUG, "dec_output: %s", sprint_hex_inrow(dec_output, 32)); if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) { PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match."); @@ -447,6 +448,33 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { return PM3_ESOFT; } + uint8_t ssc[8] = { 0x00 }; + uint8_t ks_enc[16] = { 0x00 }; + uint8_t ks_mac[16] = { 0x00 }; + uint8_t k_icc[16] = { 0x00 }; + memcpy(k_icc, dec_output + 16, 16); + + // Calculate session keys + for (int x = 0; x < 16; x++) { + kseed[x] = k_ifd[x] ^ k_icc[x]; + } + + PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); + + deskey(kseed, KENC_type, 16, ks_enc); + deskey(kseed, KMAC_type, 16, ks_mac); + + PrintAndLogEx(DEBUG, "ks_enc: %s", sprint_hex_inrow(ks_enc, 16)); + PrintAndLogEx(DEBUG, "ks_mac: %s", sprint_hex_inrow(ks_mac, 16)); + + memcpy(ssc, rnd_ic + 4, 4); + memcpy(ssc + 4, rnd_ifd + 4, 4); + + PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); + + // TODO: Secure select + // TODO: Secure read + DropField(); return PM3_SUCCESS; } From 2f42b875e19ff56f9cdccb6f744bb3c7b771a972 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 11 Dec 2020 21:07:52 +0300 Subject: [PATCH 16/40] emrtd: Impl secure file select --- client/src/cmdhfemrtd.c | 97 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 10 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 49c7b9645..ad3ff69f6 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -198,28 +198,39 @@ static void des3_decrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inpu mbedtls_des3_free(&ctx); } -static void retail_mac(uint8_t *key, uint8_t *input, uint8_t *output) { - // This code assumes blocklength (n) = 8, and input len of 32 chars +static int pad_block(uint8_t *input, int inputlen, uint8_t *output) { + uint8_t padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + memcpy(output, input, inputlen); + + int to_pad = (8 - (inputlen % 8)); + + for (int i = 0; i < to_pad; i++) { + output[inputlen + i] = padding[i]; + } + + return inputlen + to_pad; +} + +static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) { + // This code assumes blocklength (n) = 8, and input len of up to 56 chars // This code takes inspirations from https://github.com/devinvenable/iso9797algorithm3 uint8_t k0[8]; uint8_t k1[8]; uint8_t intermediate[8] = {0x00}; uint8_t intermediate_des[32]; uint8_t block[8]; - uint8_t message[40]; - uint8_t padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t message[64]; // Populate keys memcpy(k0, key, 8); memcpy(k1, key + 8, 8); // Prepare message - memcpy(message, input, 32); - // Normally this isn't how it works, but it's what mrpkey does... - memcpy(message + 32, padding, 8); + int blocksize = pad_block(input, inputlen, message); // Do chaining and encryption - for (int i = 0; i < (sizeof(message) / 8); i++) { + for (int i = 0; i < (blocksize / 8); i++) { memcpy(block, message + (i * 8), 8); // XOR @@ -333,6 +344,69 @@ static int read_file(uint8_t *dataout, int *dataoutlen) { return true; } +static int secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *file) { + // Get data even tho we'll not use it + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + // TODO: fix sizes + uint8_t iv[8] = { 0x00 }; + char command[200]; + uint8_t cmd[200]; + uint8_t data[100]; + uint8_t temp[100] = {0x0c, 0xa4, 0x02, 0x0c}; + uint8_t temp_2[100]; + + PrintAndLogEx(DEBUG, "keyenc: %s", sprint_hex_inrow(kenc, 16)); + PrintAndLogEx(DEBUG, "keymac: %s", sprint_hex_inrow(kmac, 16)); + + int cmdlen = pad_block(temp, 4, cmd); + int datalen = pad_block(file, 2, data); + PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen)); + PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, datalen)); + + des3_encrypt_cbc(iv, kenc, data, datalen, temp_2); + PrintAndLogEx(DEBUG, "temp_2: %s", sprint_hex_inrow(temp_2, datalen)); + uint8_t do87[103] = {0x87, 0x09, 0x01}; + memcpy(do87 + 3, temp_2, datalen); + PrintAndLogEx(DEBUG, "do87: %s", sprint_hex_inrow(do87, datalen + 3)); + + uint8_t m[153]; + memcpy(m, cmd, cmdlen); + memcpy(m + cmdlen, do87, (datalen + 3)); + PrintAndLogEx(DEBUG, "m: %s", sprint_hex_inrow(m, datalen + cmdlen + 3)); + + // this is hacky + PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8)); + (*(ssc + 7)) += 1; + PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8)); + + uint8_t n[161]; + memcpy(n, ssc, 8); + memcpy(n + 8, m, (cmdlen + datalen + 3)); + PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, (cmdlen + datalen + 11))); + + uint8_t cc[8]; + retail_mac(kmac, n, (cmdlen + datalen + 11), cc); + PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8)); + + uint8_t do8e[10] = {0x8E, 0x08}; + memcpy(do8e + 2, cc, 8); + PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10)); + + int lc = datalen + 3 + 10; + PrintAndLogEx(DEBUG, "lc: %i", lc); + + memcpy(data, do87, datalen + 3); + memcpy(data + (datalen + 3), do8e, 10); + PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); + + sprintf(command, "0C%s020C%02X%s00", SELECT, lc, sprint_hex_inrow(data, lc)); + PrintAndLogEx(DEBUG, "command: %s", command); + + return exchange_commands(command, response, &resplen, false, true); +} + int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[PM3_CMD_DATA_SIZE]; uint8_t rnd_ic[8]; @@ -423,7 +497,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t m_ifd[8] = { 0x00 }; - retail_mac(kmac, e_ifd, m_ifd); + retail_mac(kmac, e_ifd, 32, m_ifd); PrintAndLogEx(DEBUG, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8)); uint8_t cmd_data[40]; @@ -472,7 +546,10 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); - // TODO: Secure select + // Select EF_COM + uint8_t file_id[2] = {0x01, 0x1E}; + secure_select_file(ks_enc, ks_mac, ssc, file_id); + // TODO: Secure read DropField(); From 831672be203bde22e23a9a1ff37fc4e65d6549de Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 11 Dec 2020 21:14:47 +0300 Subject: [PATCH 17/40] emrtd: Clean up secure select file --- client/src/cmdhfemrtd.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index ad3ff69f6..f1df87fbb 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -349,13 +349,11 @@ static int secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_ uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; - // TODO: fix sizes uint8_t iv[8] = { 0x00 }; - char command[200]; - uint8_t cmd[200]; - uint8_t data[100]; - uint8_t temp[100] = {0x0c, 0xa4, 0x02, 0x0c}; - uint8_t temp_2[100]; + char command[54]; + uint8_t cmd[8]; + uint8_t data[21]; + uint8_t temp[8] = {0x0c, 0xa4, 0x02, 0x0c}; PrintAndLogEx(DEBUG, "keyenc: %s", sprint_hex_inrow(kenc, 16)); PrintAndLogEx(DEBUG, "keymac: %s", sprint_hex_inrow(kmac, 16)); @@ -365,23 +363,23 @@ static int secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_ PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen)); PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, datalen)); - des3_encrypt_cbc(iv, kenc, data, datalen, temp_2); - PrintAndLogEx(DEBUG, "temp_2: %s", sprint_hex_inrow(temp_2, datalen)); - uint8_t do87[103] = {0x87, 0x09, 0x01}; - memcpy(do87 + 3, temp_2, datalen); + des3_encrypt_cbc(iv, kenc, data, datalen, temp); + PrintAndLogEx(DEBUG, "temp: %s", sprint_hex_inrow(temp, datalen)); + uint8_t do87[11] = {0x87, 0x09, 0x01}; + memcpy(do87 + 3, temp, datalen); PrintAndLogEx(DEBUG, "do87: %s", sprint_hex_inrow(do87, datalen + 3)); - uint8_t m[153]; + uint8_t m[19]; memcpy(m, cmd, cmdlen); memcpy(m + cmdlen, do87, (datalen + 3)); PrintAndLogEx(DEBUG, "m: %s", sprint_hex_inrow(m, datalen + cmdlen + 3)); - // this is hacky + // TODO: this is hacky PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8)); (*(ssc + 7)) += 1; PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8)); - uint8_t n[161]; + uint8_t n[27]; memcpy(n, ssc, 8); memcpy(n + 8, m, (cmdlen + datalen + 3)); PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, (cmdlen + datalen + 11))); From e93a258f8802fb3b5a6ce5dfbc380436e924beb6 Mon Sep 17 00:00:00 2001 From: Ave Date: Fri, 11 Dec 2020 21:17:15 +0300 Subject: [PATCH 18/40] emrtd: make style pass --- client/src/cmdhfemrtd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index f1df87fbb..6ac998864 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -109,7 +109,7 @@ static char calculate_check_digit(char *data) { } static int asn1datalength(uint8_t *datain, int datainlen) { - char* dataintext = sprint_hex_inrow(datain, datainlen); + char *dataintext = sprint_hex_inrow(datain, datainlen); // lazy - https://stackoverflow.com/a/4214350/3286892 char subbuff[8]; @@ -136,7 +136,7 @@ static int asn1datalength(uint8_t *datain, int datainlen) { } static int asn1fieldlength(uint8_t *datain, int datainlen) { - char* dataintext = sprint_hex_inrow(datain, datainlen); + char *dataintext = sprint_hex_inrow(datain, datainlen); // lazy - https://stackoverflow.com/a/4214350/3286892 char subbuff[8]; From 3e2e0299ffe415e67a5404a178b993068918fa0c Mon Sep 17 00:00:00 2001 From: Ave Date: Sat, 12 Dec 2020 01:50:54 +0300 Subject: [PATCH 19/40] emrtd: Impl secure read binary --- client/src/cmdhfemrtd.c | 85 +++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 6ac998864..e9a129f0d 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -256,18 +256,19 @@ static void deskey(uint8_t *seed, uint8_t *type, int length, uint8_t *dataout) { // combine seed and type uint8_t data[50]; - memcpy(data, seed, 16); - memcpy(data + 16, type, 4); - PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, 20)); + memcpy(data, seed, length); + memcpy(data + length, type, 4); + PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, length + 4)); // SHA1 the key - unsigned char key[20]; - mbedtls_sha1(data, 20, key); - PrintAndLogEx(DEBUG, "key: %s", sprint_hex_inrow(key, 20)); + unsigned char key[64]; + mbedtls_sha1(data, length + 4, key); + PrintAndLogEx(DEBUG, "key: %s", sprint_hex_inrow(key, length + 4)); // Set parity bits - mbedtls_des_key_set_parity(key); - mbedtls_des_key_set_parity(key + 8); + for (int i = 0; i < ((length + 4) / 8); i++) { + mbedtls_des_key_set_parity(key + (i * 8)); + } PrintAndLogEx(DEBUG, "post-parity key: %s", sprint_hex_inrow(key, 20)); memcpy(dataout, &key, length); @@ -355,9 +356,6 @@ static int secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_ uint8_t data[21]; uint8_t temp[8] = {0x0c, 0xa4, 0x02, 0x0c}; - PrintAndLogEx(DEBUG, "keyenc: %s", sprint_hex_inrow(kenc, 16)); - PrintAndLogEx(DEBUG, "keymac: %s", sprint_hex_inrow(kmac, 16)); - int cmdlen = pad_block(temp, 4, cmd); int datalen = pad_block(file, 2, data); PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen)); @@ -402,9 +400,71 @@ static int secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_ sprintf(command, "0C%s020C%02X%s00", SELECT, lc, sprint_hex_inrow(data, lc)); PrintAndLogEx(DEBUG, "command: %s", command); + // TODO: Impl CC check, which will handle incrementing itself + (*(ssc + 7)) += 1; + return exchange_commands(command, response, &resplen, false, true); } +static int secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { + char command[54]; + uint8_t cmd[8]; + uint8_t data[21]; + uint8_t temp[8] = {0x0c, 0xb0}; + + PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 20)); + + // TODO: hacky + char offsethex[5]; + sprintf(offsethex, "%04X", offset); + char offsetbuffer[8]; + memcpy(offsetbuffer, offsethex, 2); + int p1 = (int)strtol(offsetbuffer, NULL, 16); + memcpy(offsetbuffer, offsethex + 2, 2); + int p2 = (int)strtol(offsetbuffer, NULL, 16); + temp[2] = p1; + temp[3] = p2; + + int cmdlen = pad_block(temp, 4, cmd); + PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen)); + + uint8_t do97[3] = {0x97, 0x01, bytes_to_read}; + + uint8_t m[11]; + memcpy(m, cmd, 8); + memcpy(m + 8, do97, 3); + + // TODO: this is hacky + PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8)); + (*(ssc + 7)) += 1; + PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8)); + + uint8_t n[19]; + memcpy(n, ssc, 8); + memcpy(n + 8, m, 11); + PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, 19)); + + uint8_t cc[8]; + retail_mac(kmac, n, 19, cc); + PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8)); + + uint8_t do8e[10] = {0x8E, 0x08}; + memcpy(do8e + 2, cc, 8); + PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10)); + + int lc = 13; + PrintAndLogEx(DEBUG, "lc: %i", lc); + + memcpy(data, do97, 3); + memcpy(data + 3, do8e, 10); + PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); + + sprintf(command, "0C%s%02X%02X%02X%s00", READ_BINARY, p1, p2, lc, sprint_hex_inrow(data, lc)); + PrintAndLogEx(DEBUG, "command: %s", command); + + return exchange_commands(command, dataout, dataoutlen, false, true); +} + int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[PM3_CMD_DATA_SIZE]; uint8_t rnd_ic[8]; @@ -548,7 +608,8 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t file_id[2] = {0x01, 0x1E}; secure_select_file(ks_enc, ks_mac, ssc, file_id); - // TODO: Secure read + secure_read_binary(ks_mac, ssc, 0, 4, response, &resplen); + // TODO: impl secure read file DropField(); return PM3_SUCCESS; From cf3b0bcbe1d1c969ad7c3638d853911e84f0cff2 Mon Sep 17 00:00:00 2001 From: Ave Date: Sat, 12 Dec 2020 04:34:16 +0300 Subject: [PATCH 20/40] emrtd: Impl check_cc --- client/src/cmdhfemrtd.c | 59 +++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index e9a129f0d..ae25140e2 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -345,7 +345,41 @@ static int read_file(uint8_t *dataout, int *dataoutlen) { return true; } -static int secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *file) { +static bool check_cc(uint8_t *ssc, uint8_t *key, uint8_t *rapdu, int rapdulength) { + // https://elixi.re/i/clarkson.png + uint8_t k[100]; + uint8_t cc[100]; + + (*(ssc + 7)) += 1; + + memcpy(k, ssc, 8); + int length = 0; + int length2 = 0; + + if (*(rapdu) == 0x87) { + length += 2 + (*(rapdu + 1)); + memcpy(k + 8, rapdu, length); + PrintAndLogEx(DEBUG, "len1: %i", length); + } + + if ((*(rapdu + length)) == 0x99) { + length2 += 2 + (*(rapdu + (length + 1))); + memcpy(k + length + 8, rapdu + length, length2); + PrintAndLogEx(DEBUG, "len2: %i", length2); + } + + int klength = length + length2 + 8; + + retail_mac(key, k, klength, cc); + PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8)); + PrintAndLogEx(DEBUG, "rapdu: %s", sprint_hex_inrow(rapdu, rapdulength)); + PrintAndLogEx(DEBUG, "rapdu cut: %s", sprint_hex_inrow(rapdu + (rapdulength - 8), 8)); + PrintAndLogEx(DEBUG, "k: %s", sprint_hex_inrow(k, klength)); + + return memcmp(cc, rapdu + (rapdulength - 8), 8) == 0; +} + +static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *file) { // Get data even tho we'll not use it uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; @@ -400,13 +434,12 @@ static int secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_ sprintf(command, "0C%s020C%02X%s00", SELECT, lc, sprint_hex_inrow(data, lc)); PrintAndLogEx(DEBUG, "command: %s", command); - // TODO: Impl CC check, which will handle incrementing itself - (*(ssc + 7)) += 1; + exchange_commands(command, response, &resplen, false, true); - return exchange_commands(command, response, &resplen, false, true); + return check_cc(ssc, kmac, response, resplen); } -static int secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { +static bool secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { char command[54]; uint8_t cmd[8]; uint8_t data[21]; @@ -462,7 +495,9 @@ static int secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes sprintf(command, "0C%s%02X%02X%02X%s00", READ_BINARY, p1, p2, lc, sprint_hex_inrow(data, lc)); PrintAndLogEx(DEBUG, "command: %s", command); - return exchange_commands(command, dataout, dataoutlen, false, true); + exchange_commands(command, dataout, dataoutlen, false, true); + + return check_cc(ssc, kmac, dataout, *dataoutlen); } int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { @@ -606,9 +641,17 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select EF_COM uint8_t file_id[2] = {0x01, 0x1E}; - secure_select_file(ks_enc, ks_mac, ssc, file_id); + if (secure_select_file(ks_enc, ks_mac, ssc, file_id) == false) { + PrintAndLogEx(ERR, "Failed to secure select EF_COM, crypto checksum check failed."); + DropField(); + return PM3_ESOFT; + } - secure_read_binary(ks_mac, ssc, 0, 4, response, &resplen); + if (secure_read_binary(ks_mac, ssc, 0, 4, response, &resplen) == false) { + PrintAndLogEx(ERR, "Failed to read EF_COM, crypto checksum check failed."); + DropField(); + return PM3_ESOFT; + } // TODO: impl secure read file DropField(); From a895fbd3c689df3075c363bf395b712ccfaef768 Mon Sep 17 00:00:00 2001 From: Ave Date: Sat, 12 Dec 2020 20:26:54 +0300 Subject: [PATCH 21/40] emrtd: Implement secure_read_file --- client/src/cmdhfemrtd.c | 107 ++++++++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 20 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index ae25140e2..aa0b6c9b8 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -60,7 +60,7 @@ static int exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, PrintAndLogEx(DEBUG, "Sending: %s", cmd); - uint8_t aCMD[100]; + uint8_t aCMD[500]; int aCMD_n = 0; param_gethex_to_eol(cmd, 0, aCMD, sizeof(aCMD), &aCMD_n); int res = ExchangeAPDU14a(aCMD, aCMD_n, activate_field, keep_field_on, response, sizeof(response), &resplen); @@ -113,10 +113,8 @@ static int asn1datalength(uint8_t *datain, int datainlen) { // lazy - https://stackoverflow.com/a/4214350/3286892 char subbuff[8]; - memcpy(subbuff, &dataintext[2], 2); - subbuff[2] = '\0'; - int thing = (int)strtol(subbuff, NULL, 16); + int thing = *(datain + 1); if (thing <= 0x7f) { return thing; } else if (thing == 0x81) { @@ -136,14 +134,7 @@ static int asn1datalength(uint8_t *datain, int datainlen) { } static int asn1fieldlength(uint8_t *datain, int datainlen) { - char *dataintext = sprint_hex_inrow(datain, datainlen); - - // lazy - https://stackoverflow.com/a/4214350/3286892 - char subbuff[8]; - memcpy(subbuff, &dataintext[2], 2); - subbuff[2] = '\0'; - - int thing = (int)strtol(subbuff, NULL, 16); + int thing = *(datain + 1); if (thing <= 0x7f) { return 2; } else if (thing == 0x81) { @@ -213,14 +204,14 @@ static int pad_block(uint8_t *input, int inputlen, uint8_t *output) { } static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) { - // This code assumes blocklength (n) = 8, and input len of up to 56 chars + // This code assumes blocklength (n) = 8, and input len of up to 240 or so chars // This code takes inspirations from https://github.com/devinvenable/iso9797algorithm3 uint8_t k0[8]; uint8_t k1[8]; uint8_t intermediate[8] = {0x00}; - uint8_t intermediate_des[32]; + uint8_t intermediate_des[256]; uint8_t block[8]; - uint8_t message[64]; + uint8_t message[256]; // Populate keys memcpy(k0, key, 8); @@ -439,7 +430,7 @@ static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8 return check_cc(ssc, kmac, response, resplen); } -static bool secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { +static bool _secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { char command[54]; uint8_t cmd[8]; uint8_t data[21]; @@ -500,8 +491,69 @@ static bool secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int byte return check_cc(ssc, kmac, dataout, *dataoutlen); } +static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { + uint8_t response[500]; + uint8_t temp[500]; + int resplen, cutat = 0; + uint8_t iv[8] = { 0x00 }; + + if (_secure_read_binary(kmac, ssc, offset, bytes_to_read, response, &resplen) == false) { + return false; + } + + PrintAndLogEx(DEBUG, "0offset %i on %i: ? (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); + + cutat = ((int) response[1]) - 1; + + PrintAndLogEx(DEBUG, "1offset %i on %i: ? (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); + des3_decrypt_cbc(iv, kenc, response + 3, cutat, temp); + PrintAndLogEx(DEBUG, "2eoffset %i on %i: ? (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); + PrintAndLogEx(DEBUG, "2aoffset %i on %i: %s", offset, bytes_to_read, sprint_hex_inrow(temp, cutat)); + PrintAndLogEx(DEBUG, "2boffset %i on %i: c %s", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); + memcpy(dataout, temp, bytes_to_read); + PrintAndLogEx(DEBUG, "3offset %i on %i: %s (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(temp, cutat), sprint_hex_inrow(response, resplen)); + *dataoutlen = bytes_to_read; + return true; +} + +static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *dataout, int *dataoutlen) { + uint8_t response[500]; + int resplen = 0; + uint8_t tempresponse[500]; + int tempresplen = 0; + + if (!_secure_read_binary_decrypt(kenc, kmac, ssc, 0, 4, response, &resplen)) { + return false; + } + + int datalen = asn1datalength(response, resplen); + int readlen = datalen - (3 - asn1fieldlength(response, resplen) / 2); + int offset = 4; + int toread; + + while (readlen > 0) { + toread = readlen; + if (readlen > 118) { + toread = 118; + } + + if (!_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen)) { + return false; + } + + memcpy(response + resplen, tempresponse, tempresplen); + offset += toread; + readlen -= toread; + resplen += tempresplen; + } + + memcpy(dataout, &response, resplen); + *dataoutlen = resplen; + return true; +} + int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { - uint8_t response[PM3_CMD_DATA_SIZE]; + uint8_t response[500]; uint8_t rnd_ic[8]; uint8_t kenc[50]; uint8_t kmac[50]; @@ -647,12 +699,27 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { return PM3_ESOFT; } - if (secure_read_binary(ks_mac, ssc, 0, 4, response, &resplen) == false) { - PrintAndLogEx(ERR, "Failed to read EF_COM, crypto checksum check failed."); + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + PrintAndLogEx(ERR, "Failed to read EF_COM."); DropField(); return PM3_ESOFT; } - // TODO: impl secure read file + PrintAndLogEx(INFO, "EF_COM: %s", sprint_hex_inrow(response, resplen)); + + // Select EF_DG1 + file_id[1] = 0x01; + if (secure_select_file(ks_enc, ks_mac, ssc, file_id) == false) { + PrintAndLogEx(ERR, "Failed to secure select EF_DG1, crypto checksum check failed."); + DropField(); + return PM3_ESOFT; + } + + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + PrintAndLogEx(ERR, "Failed to read EF_DG1."); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex_inrow(response, resplen)); DropField(); return PM3_SUCCESS; From 42e6763ea1dc139a2ce331e4b2bd0f8fd5684164 Mon Sep 17 00:00:00 2001 From: Ave Date: Sat, 12 Dec 2020 20:55:16 +0300 Subject: [PATCH 22/40] emrtd: Improve and clean asn1datalength and asn1fieldlength --- client/src/cmdhfemrtd.c | 69 ++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index aa0b6c9b8..8a6cf05a8 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -108,41 +108,34 @@ static char calculate_check_digit(char *data) { return cd % 10; } -static int asn1datalength(uint8_t *datain, int datainlen) { - char *dataintext = sprint_hex_inrow(datain, datainlen); - - // lazy - https://stackoverflow.com/a/4214350/3286892 - char subbuff[8]; - - int thing = *(datain + 1); - if (thing <= 0x7f) { - return thing; - } else if (thing == 0x81) { - memcpy(subbuff, &dataintext[2], 3); - subbuff[3] = '\0'; - return (int)strtol(subbuff, NULL, 16); - } else if (thing == 0x82) { - memcpy(subbuff, &dataintext[2], 5); - subbuff[5] = '\0'; - return (int)strtol(subbuff, NULL, 16); - } else if (thing == 0x83) { - memcpy(subbuff, &dataintext[2], 7); - subbuff[7] = '\0'; - return (int)strtol(subbuff, NULL, 16); +static int asn1datalength(uint8_t *datain, int datainlen, int offset) { + PrintAndLogEx(DEBUG, "asn1datalength, datain: %s", sprint_hex_inrow(datain, datainlen)); + int lenfield = (int) *(datain + offset); + PrintAndLogEx(DEBUG, "asn1datalength, lenfield: %i", lenfield); + if (lenfield <= 0x7f) { + return lenfield; + } else if (lenfield == 0x81) { + return ((int) *(datain + offset + 1)); + } else if (lenfield == 0x82) { + return ((int) *(datain + offset + 1) << 8) | ((int) *(datain + offset + 2)); + } else if (lenfield == 0x83) { + return (((int) *(datain + offset + 1) << 16) | ((int) *(datain + offset + 2)) << 8) | ((int) *(datain + offset + 3)); } return false; } -static int asn1fieldlength(uint8_t *datain, int datainlen) { - int thing = *(datain + 1); - if (thing <= 0x7f) { +static int asn1fieldlength(uint8_t *datain, int datainlen, int offset) { + PrintAndLogEx(DEBUG, "asn1fieldlength, datain: %s", sprint_hex_inrow(datain, datainlen)); + int lenfield = (int) *(datain + offset); + PrintAndLogEx(DEBUG, "asn1fieldlength, thing: %i", lenfield); + if (lenfield <= 0x7f) { + return 1; + } else if (lenfield == 0x81) { return 2; - } else if (thing == 0x81) { + } else if (lenfield == 0x82) { + return 3; + } else if (lenfield == 0x83) { return 4; - } else if (thing == 0x82) { - return 6; - } else if (thing == 0x83) { - return 8; } return false; } @@ -310,8 +303,8 @@ static int read_file(uint8_t *dataout, int *dataoutlen) { return false; } - int datalen = asn1datalength(response, resplen); - int readlen = datalen - (3 - asn1fieldlength(response, resplen) / 2); + int datalen = asn1datalength(response, resplen, 1); + int readlen = datalen - (3 - asn1fieldlength(response, resplen, 1)); int offset = 4; int toread; @@ -501,22 +494,20 @@ static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *s return false; } - PrintAndLogEx(DEBUG, "0offset %i on %i: ? (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); + PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: encrypted: %s", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); cutat = ((int) response[1]) - 1; - PrintAndLogEx(DEBUG, "1offset %i on %i: ? (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); des3_decrypt_cbc(iv, kenc, response + 3, cutat, temp); - PrintAndLogEx(DEBUG, "2eoffset %i on %i: ? (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); - PrintAndLogEx(DEBUG, "2aoffset %i on %i: %s", offset, bytes_to_read, sprint_hex_inrow(temp, cutat)); - PrintAndLogEx(DEBUG, "2boffset %i on %i: c %s", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); memcpy(dataout, temp, bytes_to_read); - PrintAndLogEx(DEBUG, "3offset %i on %i: %s (crypt: %s)", offset, bytes_to_read, sprint_hex_inrow(temp, cutat), sprint_hex_inrow(response, resplen)); + PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: decrypted: %s", offset, bytes_to_read, sprint_hex_inrow(temp, cutat)); + PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: decrypted and cut: %s", offset, bytes_to_read, sprint_hex_inrow(dataout, bytes_to_read)); *dataoutlen = bytes_to_read; return true; } static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *dataout, int *dataoutlen) { + // TODO: join this with regular read file uint8_t response[500]; int resplen = 0; uint8_t tempresponse[500]; @@ -526,8 +517,8 @@ static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t return false; } - int datalen = asn1datalength(response, resplen); - int readlen = datalen - (3 - asn1fieldlength(response, resplen) / 2); + int datalen = asn1datalength(response, resplen, 1); + int readlen = datalen - (3 - asn1fieldlength(response, resplen, 1)); int offset = 4; int toread; From 3dd4f580c8a95315e67f4ff5b16c68050542c2ca Mon Sep 17 00:00:00 2001 From: Ave Date: Sat, 12 Dec 2020 21:11:17 +0300 Subject: [PATCH 23/40] emrtd: Have secure_select_file accept string filenames --- client/src/cmdhfemrtd.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 8a6cf05a8..9f3732874 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -363,11 +363,24 @@ static bool check_cc(uint8_t *ssc, uint8_t *key, uint8_t *rapdu, int rapdulength return memcmp(cc, rapdu + (rapdulength - 8), 8) == 0; } -static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *file) { +static void _convert_filename(const char *file, uint8_t *dataout) { + char temp[3]; + memcpy(temp, file, 2); + dataout[0] = (int)strtol(temp, NULL, 16); + memcpy(temp, file + 2, 2); + dataout[1] = (int)strtol(temp, NULL, 16); +} + +static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const char *file) { // Get data even tho we'll not use it + // TODO: make a func to send without receive uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; + // convert filename of string to bytes + uint8_t file_id[2]; + _convert_filename(file, file_id); + uint8_t iv[8] = { 0x00 }; char command[54]; uint8_t cmd[8]; @@ -375,7 +388,7 @@ static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8 uint8_t temp[8] = {0x0c, 0xa4, 0x02, 0x0c}; int cmdlen = pad_block(temp, 4, cmd); - int datalen = pad_block(file, 2, data); + int datalen = pad_block(file_id, 2, data); PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen)); PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, datalen)); @@ -683,8 +696,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); // Select EF_COM - uint8_t file_id[2] = {0x01, 0x1E}; - if (secure_select_file(ks_enc, ks_mac, ssc, file_id) == false) { + if (secure_select_file(ks_enc, ks_mac, ssc, EF_COM) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_COM, crypto checksum check failed."); DropField(); return PM3_ESOFT; @@ -698,8 +710,7 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(INFO, "EF_COM: %s", sprint_hex_inrow(response, resplen)); // Select EF_DG1 - file_id[1] = 0x01; - if (secure_select_file(ks_enc, ks_mac, ssc, file_id) == false) { + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG1) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_DG1, crypto checksum check failed."); DropField(); return PM3_ESOFT; From c2531e7ed0dda3ab1aebc12e4eda67b0558a67e7 Mon Sep 17 00:00:00 2001 From: Ave Date: Sat, 12 Dec 2020 21:19:51 +0300 Subject: [PATCH 24/40] emrtd: Rename from hf emrtd info to dump, code cleanup --- client/src/cmdhfemrtd.c | 32 +++++++++++++++++--------------- client/src/cmdhfemrtd.h | 2 +- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 9f3732874..c2611dc8f 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -54,7 +54,8 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) { return d[n] * 0x0100 + d[n + 1]; } -static int exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, bool activate_field, bool keep_field_on) { +static bool exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, bool activate_field, bool keep_field_on) { + // TODO: Account for 14b too uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; @@ -86,6 +87,13 @@ static int exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, return true; } +static int exchange_commands_noout(const char *cmd, bool activate_field, bool keep_field_on) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + return exchange_commands(cmd, response, &resplen, activate_field, keep_field_on); +} + static char calculate_check_digit(char *data) { int mrz_weight[] = {7, 3, 1}; int cd = 0; @@ -261,14 +269,10 @@ static void deskey(uint8_t *seed, uint8_t *type, int length, uint8_t *dataout) { static int select_file(const char *select_by, const char *file_id, bool activate_field, bool keep_field_on) { size_t file_id_len = strlen(file_id) / 2; - // Get data even tho we'll not use it - uint8_t response[PM3_CMD_DATA_SIZE]; - int resplen = 0; - char cmd[50]; sprintf(cmd, "00%s%s0C%02lu%s", SELECT, select_by, file_id_len, file_id); - return exchange_commands(cmd, response, &resplen, activate_field, keep_field_on); + return exchange_commands_noout(cmd, activate_field, keep_field_on); } static int get_challenge(int length, uint8_t *dataout, int *dataoutlen) { @@ -372,8 +376,6 @@ static void _convert_filename(const char *file, uint8_t *dataout) { } static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const char *file) { - // Get data even tho we'll not use it - // TODO: make a func to send without receive uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; @@ -556,7 +558,7 @@ static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t return true; } -int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { +int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[500]; uint8_t rnd_ic[8]; uint8_t kenc[50]; @@ -727,11 +729,11 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry) { return PM3_SUCCESS; } -static int cmd_hf_emrtd_info(const char *Cmd) { +static int cmd_hf_emrtd_dump(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "hf emrtd info", - "Get info about an eMRTD", - "hf emrtd info" + CLIParserInit(&ctx, "hf emrtd dump", + "Dump all files on an eMRTD", + "hf emrtd dump" ); void *argtable[] = { @@ -754,7 +756,7 @@ static int cmd_hf_emrtd_info(const char *Cmd) { CLIGetStrWithReturn(ctx, 3, expiry, &expirylen); CLIParserFree(ctx); - return infoHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry); + return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry); } static int cmd_hf_emrtd_list(const char *Cmd) { @@ -769,7 +771,7 @@ static int cmd_hf_emrtd_list(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"info", cmd_hf_emrtd_info, IfPm3Iso14443a, "Tag information"}, + {"dump", cmd_hf_emrtd_dump, IfPm3Iso14443, "Dump eMRTD files to binary files"}, {"list", cmd_hf_emrtd_list, AlwaysAvailable, "List ISO 14443A/7816 history"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index ba9e3f2b3..5899ad27a 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -15,5 +15,5 @@ int CmdHFeMRTD(const char *Cmd); -int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry); +int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry); #endif From d1d5d8ee740d2fe9992020c98e235dd2d8e62d03 Mon Sep 17 00:00:00 2001 From: Ave Date: Sat, 12 Dec 2020 21:54:23 +0300 Subject: [PATCH 25/40] emrtd: account for larger file reads, read ef_dg2 --- client/src/cmdhfemrtd.c | 64 ++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index c2611dc8f..c00919835 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -40,6 +40,22 @@ #define EF_CARDACCESS "011C" #define EF_COM "011E" #define EF_DG1 "0101" +#define EF_DG2 "0102" +#define EF_DG3 "0103" +#define EF_DG4 "0104" +#define EF_DG5 "0105" +#define EF_DG6 "0106" +#define EF_DG7 "0107" +#define EF_DG8 "0108" +#define EF_DG9 "0109" +#define EF_DG10 "010A" +#define EF_DG11 "010B" +#define EF_DG12 "010C" +#define EF_DG13 "010D" +#define EF_DG14 "010E" +#define EF_DG15 "010F" +#define EF_DG16 "0110" +#define EF_SOD "011D" // App IDs #define AID_MRTD "A0000002471001" @@ -333,12 +349,26 @@ static int read_file(uint8_t *dataout, int *dataoutlen) { return true; } +static void bump_ssc(uint8_t *ssc) { + PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8)); + for (int i = 7; i > 0; i--) { + if ((*(ssc + i)) == 0xFF) { + // Set anything already FF to 0, we'll do + 1 on num to left anyways + (*(ssc + i)) = 0; + continue; + } + (*(ssc + i)) += 1; + PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8)); + return; + } +} + static bool check_cc(uint8_t *ssc, uint8_t *key, uint8_t *rapdu, int rapdulength) { // https://elixi.re/i/clarkson.png - uint8_t k[100]; - uint8_t cc[100]; + uint8_t k[500]; + uint8_t cc[500]; - (*(ssc + 7)) += 1; + bump_ssc(ssc); memcpy(k, ssc, 8); int length = 0; @@ -405,10 +435,7 @@ static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const memcpy(m + cmdlen, do87, (datalen + 3)); PrintAndLogEx(DEBUG, "m: %s", sprint_hex_inrow(m, datalen + cmdlen + 3)); - // TODO: this is hacky - PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8)); - (*(ssc + 7)) += 1; - PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8)); + bump_ssc(ssc); uint8_t n[27]; memcpy(n, ssc, 8); @@ -466,10 +493,7 @@ static bool _secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int byt memcpy(m, cmd, 8); memcpy(m + 8, do97, 3); - // TODO: this is hacky - PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8)); - (*(ssc + 7)) += 1; - PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8)); + bump_ssc(ssc); uint8_t n[19]; memcpy(n, ssc, 8); @@ -523,7 +547,7 @@ static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *s static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *dataout, int *dataoutlen) { // TODO: join this with regular read file - uint8_t response[500]; + uint8_t response[25000]; int resplen = 0; uint8_t tempresponse[500]; int tempresplen = 0; @@ -559,7 +583,7 @@ static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t } int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { - uint8_t response[500]; + uint8_t response[25000]; uint8_t rnd_ic[8]; uint8_t kenc[50]; uint8_t kmac[50]; @@ -725,6 +749,20 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { } PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex_inrow(response, resplen)); + // Select EF_DG2 + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG2) == false) { + PrintAndLogEx(ERR, "Failed to secure select EF_DG2, crypto checksum check failed."); + DropField(); + return PM3_ESOFT; + } + + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + PrintAndLogEx(ERR, "Failed to read EF_DG2."); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "EF_DG2 (len: %i): %s", resplen, sprint_hex_inrow(response, resplen)); + DropField(); return PM3_SUCCESS; } From ac82a80afa7d4ba8e159beebe1e337fbd73953c1 Mon Sep 17 00:00:00 2001 From: Ave Date: Sat, 12 Dec 2020 22:10:40 +0300 Subject: [PATCH 26/40] emrtd: Read more files, save read files --- client/src/cmdhfemrtd.c | 82 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index c00919835..65dc1c200 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -12,7 +12,7 @@ #include "cmdhfemrtd.h" #include -#include "fileutils.h" +#include "fileutils.h" // saveFile #include "cmdparser.h" // command_t #include "comms.h" // clearCommandBuffer #include "cmdtrace.h" @@ -600,7 +600,8 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select and read EF_CardAccess if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS, true, true)) { read_file(response, &resplen); - PrintAndLogEx(INFO, "EF_CardAccess: %s", sprint_hex(response, resplen)); + PrintAndLogEx(INFO, "Read EF_CardAccess, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); } else { PrintAndLogEx(INFO, "PACE unsupported. Will not read EF_CardAccess."); } @@ -733,8 +734,11 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { DropField(); return PM3_ESOFT; } - PrintAndLogEx(INFO, "EF_COM: %s", sprint_hex_inrow(response, resplen)); + PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile("EF_COM", ".BIN", response, resplen); + // TODO: Don't read a hardcoded list of files, reduce code repetition // Select EF_DG1 if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG1) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_DG1, crypto checksum check failed."); @@ -747,7 +751,9 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { DropField(); return PM3_ESOFT; } - PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex_inrow(response, resplen)); + PrintAndLogEx(INFO, "Read EF_DG1, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile("EF_DG1", ".BIN", response, resplen); // Select EF_DG2 if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG2) == false) { @@ -761,7 +767,73 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { DropField(); return PM3_ESOFT; } - PrintAndLogEx(INFO, "EF_DG2 (len: %i): %s", resplen, sprint_hex_inrow(response, resplen)); + PrintAndLogEx(INFO, "Read EF_DG2, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile("EF_DG2", ".BIN", response, resplen); + + // Select EF_SOD + if (secure_select_file(ks_enc, ks_mac, ssc, EF_SOD) == false) { + PrintAndLogEx(ERR, "Failed to secure select EF_SOD, crypto checksum check failed."); + DropField(); + return PM3_ESOFT; + } + + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + PrintAndLogEx(ERR, "Failed to read EF_SOD."); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "Read EF_SOD, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile("EF_SOD", ".BIN", response, resplen); + + // Select EF_DG11 + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG11) == false) { + PrintAndLogEx(ERR, "Failed to secure select EF_DG11, crypto checksum check failed."); + DropField(); + return PM3_ESOFT; + } + + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + PrintAndLogEx(ERR, "Failed to read EF_DG11."); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "Read EF_DG11, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile("EF_DG11", ".BIN", response, resplen); + + // Select EF_DG12 + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG12) == false) { + PrintAndLogEx(ERR, "Failed to secure select EF_DG12, crypto checksum check failed."); + DropField(); + return PM3_ESOFT; + } + + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + PrintAndLogEx(ERR, "Failed to read EF_DG12."); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "Read EF_DG12, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile("EF_DG12", ".BIN", response, resplen); + + // Select EF_DG14 + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG14) == false) { + PrintAndLogEx(ERR, "Failed to secure select EF_DG14, crypto checksum check failed."); + DropField(); + return PM3_ESOFT; + } + + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + PrintAndLogEx(ERR, "Failed to read EF_DG14."); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "Read EF_DG14, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile("EF_DG14", ".BIN", response, resplen); DropField(); return PM3_SUCCESS; From 21fc1d634f9cb75a86cb4afa855ef8ff5c116b12 Mon Sep 17 00:00:00 2001 From: Ave Date: Sun, 13 Dec 2020 01:49:17 +0300 Subject: [PATCH 27/40] emrtd: Add 14b support and better presence detection --- client/src/cmdhf14b.c | 2 +- client/src/cmdhf14b.h | 2 + client/src/cmdhfemrtd.c | 129 +++++++++++++++++++++++++--------------- 3 files changed, 85 insertions(+), 48 deletions(-) diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index 3f4890581..eb1a4f67a 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -1488,7 +1488,7 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool return PM3_SUCCESS; } -static int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { +int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { *dataoutlen = 0; bool chaining = false; int res; diff --git a/client/src/cmdhf14b.h b/client/src/cmdhf14b.h index 2058ea8ba..d236bb6cf 100644 --- a/client/src/cmdhf14b.h +++ b/client/src/cmdhf14b.h @@ -15,6 +15,8 @@ int CmdHF14B(const char *Cmd); +int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); + int infoHF14B(bool verbose); int readHF14B(bool verbose); #endif diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 65dc1c200..f8f89f557 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -24,6 +24,8 @@ #include "sha1.h" // KSeed calculation etc #include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt #include "des.h" // mbedtls_des_key_set_parity +#include "cmdhf14b.h" // exchange_14b_apdu +#include "iso14b.h" // ISO14B_CONNECT etc #define TIMEOUT 2000 @@ -70,8 +72,7 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) { return d[n] * 0x0100 + d[n + 1]; } -static bool exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, bool activate_field, bool keep_field_on) { - // TODO: Account for 14b too +static bool exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, bool activate_field, bool keep_field_on, bool use_14b) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; @@ -80,7 +81,12 @@ static bool exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen uint8_t aCMD[500]; int aCMD_n = 0; param_gethex_to_eol(cmd, 0, aCMD, sizeof(aCMD), &aCMD_n); - int res = ExchangeAPDU14a(aCMD, aCMD_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + int res; + if (use_14b) { + res = exchange_14b_apdu(aCMD, aCMD_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + } else { + res = ExchangeAPDU14a(aCMD, aCMD_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + } if (res) { DropField(); return false; @@ -103,11 +109,11 @@ static bool exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen return true; } -static int exchange_commands_noout(const char *cmd, bool activate_field, bool keep_field_on) { +static int exchange_commands_noout(const char *cmd, bool activate_field, bool keep_field_on, bool use_14b) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; - return exchange_commands(cmd, response, &resplen, activate_field, keep_field_on); + return exchange_commands(cmd, response, &resplen, activate_field, keep_field_on, use_14b); } static char calculate_check_digit(char *data) { @@ -282,44 +288,44 @@ static void deskey(uint8_t *seed, uint8_t *type, int length, uint8_t *dataout) { memcpy(dataout, &key, length); } -static int select_file(const char *select_by, const char *file_id, bool activate_field, bool keep_field_on) { +static int select_file(const char *select_by, const char *file_id, bool use_14b) { size_t file_id_len = strlen(file_id) / 2; char cmd[50]; sprintf(cmd, "00%s%s0C%02lu%s", SELECT, select_by, file_id_len, file_id); - return exchange_commands_noout(cmd, activate_field, keep_field_on); + return exchange_commands_noout(cmd, false, true, use_14b); } -static int get_challenge(int length, uint8_t *dataout, int *dataoutlen) { +static int get_challenge(int length, uint8_t *dataout, int *dataoutlen, bool use_14b) { char cmd[50]; sprintf(cmd, "00%s0000%02X", GET_CHALLENGE, length); - return exchange_commands(cmd, dataout, dataoutlen, false, true); + return exchange_commands(cmd, dataout, dataoutlen, false, true, use_14b); } -static int external_authenticate(uint8_t *data, int length, uint8_t *dataout, int *dataoutlen) { +static int external_authenticate(uint8_t *data, int length, uint8_t *dataout, int *dataoutlen, bool use_14b) { char cmd[100]; sprintf(cmd, "00%s0000%02X%s%02X", EXTERNAL_AUTHENTICATE, length, sprint_hex_inrow(data, length), length); - return exchange_commands(cmd, dataout, dataoutlen, false, true); + return exchange_commands(cmd, dataout, dataoutlen, false, true, use_14b); } -static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { +static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen, bool use_14b) { char cmd[50]; sprintf(cmd, "00%s%04i%02i", READ_BINARY, offset, bytes_to_read); - return exchange_commands(cmd, dataout, dataoutlen, false, true); + return exchange_commands(cmd, dataout, dataoutlen, false, true, use_14b); } -static int read_file(uint8_t *dataout, int *dataoutlen) { +static int read_file(uint8_t *dataout, int *dataoutlen, bool use_14b) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; uint8_t tempresponse[PM3_CMD_DATA_SIZE]; int tempresplen = 0; - if (!_read_binary(0, 4, response, &resplen)) { + if (!_read_binary(0, 4, response, &resplen, use_14b)) { return false; } @@ -334,7 +340,7 @@ static int read_file(uint8_t *dataout, int *dataoutlen) { toread = 118; } - if (!_read_binary(offset, toread, tempresponse, &tempresplen)) { + if (!_read_binary(offset, toread, tempresponse, &tempresplen, use_14b)) { return false; } @@ -405,7 +411,7 @@ static void _convert_filename(const char *file, uint8_t *dataout) { dataout[1] = (int)strtol(temp, NULL, 16); } -static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const char *file) { +static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const char *file, bool use_14b) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; @@ -460,12 +466,12 @@ static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const sprintf(command, "0C%s020C%02X%s00", SELECT, lc, sprint_hex_inrow(data, lc)); PrintAndLogEx(DEBUG, "command: %s", command); - exchange_commands(command, response, &resplen, false, true); + exchange_commands(command, response, &resplen, false, true, use_14b); return check_cc(ssc, kmac, response, resplen); } -static bool _secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { +static bool _secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen, bool use_14b) { char command[54]; uint8_t cmd[8]; uint8_t data[21]; @@ -518,18 +524,18 @@ static bool _secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int byt sprintf(command, "0C%s%02X%02X%02X%s00", READ_BINARY, p1, p2, lc, sprint_hex_inrow(data, lc)); PrintAndLogEx(DEBUG, "command: %s", command); - exchange_commands(command, dataout, dataoutlen, false, true); + exchange_commands(command, dataout, dataoutlen, false, true, use_14b); return check_cc(ssc, kmac, dataout, *dataoutlen); } -static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen) { +static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen, bool use_14b) { uint8_t response[500]; uint8_t temp[500]; int resplen, cutat = 0; uint8_t iv[8] = { 0x00 }; - if (_secure_read_binary(kmac, ssc, offset, bytes_to_read, response, &resplen) == false) { + if (_secure_read_binary(kmac, ssc, offset, bytes_to_read, response, &resplen, use_14b) == false) { return false; } @@ -545,14 +551,14 @@ static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *s return true; } -static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *dataout, int *dataoutlen) { +static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *dataout, int *dataoutlen, bool use_14b) { // TODO: join this with regular read file uint8_t response[25000]; int resplen = 0; uint8_t tempresponse[500]; int tempresplen = 0; - if (!_secure_read_binary_decrypt(kenc, kmac, ssc, 0, 4, response, &resplen)) { + if (!_secure_read_binary_decrypt(kenc, kmac, ssc, 0, 4, response, &resplen, use_14b)) { return false; } @@ -567,7 +573,7 @@ static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t toread = 118; } - if (!_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen)) { + if (!_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen, use_14b)) { return false; } @@ -597,9 +603,38 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; + bool use_14b = false; + + // Try to 14a + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + bool failed_14a = false; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + DropField(); + failed_14a = true; + } + + if (failed_14a || resp.oldarg[0] == 0) { + PrintAndLogEx(INFO, "No eMRTD spotted with 14a, trying 14b."); + // If not 14a, try to 14b + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD, 0, 0, NULL, 0); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2500)) { + DropField(); + PrintAndLogEx(INFO, "No eMRTD spotted with 14b, exiting."); + return PM3_ESOFT; + } + + if (resp.oldarg[0] != 0) { + DropField(); + PrintAndLogEx(INFO, "No eMRTD spotted with 14b, exiting."); + return PM3_ESOFT; + } + use_14b = true; + } + // Select and read EF_CardAccess - if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS, true, true)) { - read_file(response, &resplen); + if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS, use_14b)) { + read_file(response, &resplen, use_14b); PrintAndLogEx(INFO, "Read EF_CardAccess, len: %i.", resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); } else { @@ -607,22 +642,22 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { } // Select MRTD applet - if (select_file(P1_SELECT_BY_NAME, AID_MRTD, false, true) == false) { + if (select_file(P1_SELECT_BY_NAME, AID_MRTD, use_14b) == false) { PrintAndLogEx(ERR, "Couldn't select the MRTD application."); DropField(); return PM3_ESOFT; } // Select EF_COM - if (select_file(P1_SELECT_BY_EF, EF_COM, false, true) == false) { + if (select_file(P1_SELECT_BY_EF, EF_COM, use_14b) == false) { // BAC = true; PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); } else { // BAC = false; // Select EF_DG1 - select_file(P1_SELECT_BY_EF, EF_DG1, false, true); + select_file(P1_SELECT_BY_EF, EF_DG1, use_14b); - if (read_file(response, &resplen) == false) { + if (read_file(response, &resplen, use_14b) == false) { // BAC = true; PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); } else { @@ -652,7 +687,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 16)); // Get Challenge - if (get_challenge(8, rnd_ic, &resplen) == false) { + if (get_challenge(8, rnd_ic, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Couldn't get challenge."); DropField(); return PM3_ESOFT; @@ -681,7 +716,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { memcpy(cmd_data + 32, m_ifd, 8); // Do external authentication - if (external_authenticate(cmd_data, sizeof(cmd_data), response, &resplen) == false) { + if (external_authenticate(cmd_data, sizeof(cmd_data), response, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?"); DropField(); return PM3_ESOFT; @@ -723,13 +758,13 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); // Select EF_COM - if (secure_select_file(ks_enc, ks_mac, ssc, EF_COM) == false) { + if (secure_select_file(ks_enc, ks_mac, ssc, EF_COM, use_14b) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_COM, crypto checksum check failed."); DropField(); return PM3_ESOFT; } - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Failed to read EF_COM."); DropField(); return PM3_ESOFT; @@ -740,13 +775,13 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // TODO: Don't read a hardcoded list of files, reduce code repetition // Select EF_DG1 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG1) == false) { + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG1, use_14b) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_DG1, crypto checksum check failed."); DropField(); return PM3_ESOFT; } - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Failed to read EF_DG1."); DropField(); return PM3_ESOFT; @@ -756,13 +791,13 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { saveFile("EF_DG1", ".BIN", response, resplen); // Select EF_DG2 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG2) == false) { + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG2, use_14b) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_DG2, crypto checksum check failed."); DropField(); return PM3_ESOFT; } - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Failed to read EF_DG2."); DropField(); return PM3_ESOFT; @@ -772,13 +807,13 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { saveFile("EF_DG2", ".BIN", response, resplen); // Select EF_SOD - if (secure_select_file(ks_enc, ks_mac, ssc, EF_SOD) == false) { + if (secure_select_file(ks_enc, ks_mac, ssc, EF_SOD, use_14b) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_SOD, crypto checksum check failed."); DropField(); return PM3_ESOFT; } - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Failed to read EF_SOD."); DropField(); return PM3_ESOFT; @@ -788,13 +823,13 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { saveFile("EF_SOD", ".BIN", response, resplen); // Select EF_DG11 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG11) == false) { + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG11, use_14b) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_DG11, crypto checksum check failed."); DropField(); return PM3_ESOFT; } - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Failed to read EF_DG11."); DropField(); return PM3_ESOFT; @@ -804,13 +839,13 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { saveFile("EF_DG11", ".BIN", response, resplen); // Select EF_DG12 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG12) == false) { + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG12, use_14b) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_DG12, crypto checksum check failed."); DropField(); return PM3_ESOFT; } - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Failed to read EF_DG12."); DropField(); return PM3_ESOFT; @@ -820,13 +855,13 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { saveFile("EF_DG12", ".BIN", response, resplen); // Select EF_DG14 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG14) == false) { + if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG14, use_14b) == false) { PrintAndLogEx(ERR, "Failed to secure select EF_DG14, crypto checksum check failed."); DropField(); return PM3_ESOFT; } - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen) == false) { + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { PrintAndLogEx(ERR, "Failed to read EF_DG14."); DropField(); return PM3_ESOFT; From d5d5cc1fe65b8c80cca5f7551120cb41ede30e5b Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 00:21:10 +0300 Subject: [PATCH 28/40] emrtd: Add further checks to secure_select and secure_read --- client/src/cmdhfemrtd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index f8f89f557..3dec8accb 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -466,7 +466,9 @@ static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const sprintf(command, "0C%s020C%02X%s00", SELECT, lc, sprint_hex_inrow(data, lc)); PrintAndLogEx(DEBUG, "command: %s", command); - exchange_commands(command, response, &resplen, false, true, use_14b); + if (exchange_commands(command, response, &resplen, false, true, use_14b) == false) { + return false; + } return check_cc(ssc, kmac, response, resplen); } @@ -524,7 +526,9 @@ static bool _secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int byt sprintf(command, "0C%s%02X%02X%02X%s00", READ_BINARY, p1, p2, lc, sprint_hex_inrow(data, lc)); PrintAndLogEx(DEBUG, "command: %s", command); - exchange_commands(command, dataout, dataoutlen, false, true, use_14b); + if (exchange_commands(command, dataout, dataoutlen, false, true, use_14b) == false) { + return false; + } return check_cc(ssc, kmac, dataout, *dataoutlen); } From a85e8a40d4955471e3ac7a68ff86583ac2faaece Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 01:30:11 +0300 Subject: [PATCH 29/40] emrtd: Get file list from ef_com (but not parse yet) --- client/src/cmdhfemrtd.c | 134 ++++++++++++---------------------------- 1 file changed, 38 insertions(+), 96 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 3dec8accb..7dac49477 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -78,7 +78,7 @@ static bool exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen PrintAndLogEx(DEBUG, "Sending: %s", cmd); - uint8_t aCMD[500]; + uint8_t aCMD[PM3_CMD_DATA_SIZE]; int aCMD_n = 0; param_gethex_to_eol(cmd, 0, aCMD, sizeof(aCMD), &aCMD_n); int res; @@ -404,7 +404,7 @@ static bool check_cc(uint8_t *ssc, uint8_t *key, uint8_t *rapdu, int rapdulength } static void _convert_filename(const char *file, uint8_t *dataout) { - char temp[3]; + char temp[3] = {0x00}; memcpy(temp, file, 2); dataout[0] = (int)strtol(temp, NULL, 16); memcpy(temp, file + 2, 2); @@ -592,6 +592,34 @@ static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t return true; } +static bool ef_com_get_file_list(uint8_t *datain, int *datainlen, uint8_t *dataout, int *dataoutlen) { + int offset = 2; + int elementidlen = 0; + int elementlen = 0; + while (offset < *datainlen) { + PrintAndLogEx(DEBUG, "ef_com_get_file_list, offset: %i, data: %X", offset, *(datain + offset)); + // Determine element ID length to set as offset on asn1datalength + if (*(datain + offset) == 0x5f) { + elementidlen = 2; + } else { + elementidlen = 1; + } + + // Get the length of the element + elementlen = asn1datalength(datain + offset, *datainlen - offset, elementidlen); + + // If the element is what we're looking for, get the data and return true + if (*(datain + offset) == 0x5c) { + *dataoutlen = elementlen; + memcpy(dataout, datain + offset + elementidlen + 1, elementlen); + return true; + } + offset += elementidlen + elementlen + 1; + } + // Return false if we can't find the relevant element + return false; +} + int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[25000]; uint8_t rnd_ic[8]; @@ -763,7 +791,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select EF_COM if (secure_select_file(ks_enc, ks_mac, ssc, EF_COM, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select EF_COM, crypto checksum check failed."); + PrintAndLogEx(ERR, "Failed to secure select EF_COM."); DropField(); return PM3_ESOFT; } @@ -775,104 +803,18 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { } PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile("EF_COM", ".BIN", response, resplen); + // saveFile("EF_COM", ".BIN", response, resplen); - // TODO: Don't read a hardcoded list of files, reduce code repetition - // Select EF_DG1 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG1, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select EF_DG1, crypto checksum check failed."); + uint8_t filelist[50]; + int filelistlen = 0; + + if (ef_com_get_file_list(response, &resplen, filelist, &filelistlen) == false) { + PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); DropField(); return PM3_ESOFT; } - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to read EF_DG1."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "Read EF_DG1, len: %i.", resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile("EF_DG1", ".BIN", response, resplen); - - // Select EF_DG2 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG2, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select EF_DG2, crypto checksum check failed."); - DropField(); - return PM3_ESOFT; - } - - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to read EF_DG2."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "Read EF_DG2, len: %i.", resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile("EF_DG2", ".BIN", response, resplen); - - // Select EF_SOD - if (secure_select_file(ks_enc, ks_mac, ssc, EF_SOD, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select EF_SOD, crypto checksum check failed."); - DropField(); - return PM3_ESOFT; - } - - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to read EF_SOD."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "Read EF_SOD, len: %i.", resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile("EF_SOD", ".BIN", response, resplen); - - // Select EF_DG11 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG11, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select EF_DG11, crypto checksum check failed."); - DropField(); - return PM3_ESOFT; - } - - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to read EF_DG11."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "Read EF_DG11, len: %i.", resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile("EF_DG11", ".BIN", response, resplen); - - // Select EF_DG12 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG12, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select EF_DG12, crypto checksum check failed."); - DropField(); - return PM3_ESOFT; - } - - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to read EF_DG12."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "Read EF_DG12, len: %i.", resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile("EF_DG12", ".BIN", response, resplen); - - // Select EF_DG14 - if (secure_select_file(ks_enc, ks_mac, ssc, EF_DG14, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select EF_DG14, crypto checksum check failed."); - DropField(); - return PM3_ESOFT; - } - - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to read EF_DG14."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "Read EF_DG14, len: %i.", resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile("EF_DG14", ".BIN", response, resplen); + PrintAndLogEx(DEBUG, "File List: %s", sprint_hex_inrow(filelist, filelistlen)); DropField(); return PM3_SUCCESS; From 41a7bdef1c3dc8b209b70617272a31dc585367d3 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 02:11:39 +0300 Subject: [PATCH 30/40] emrtd: Detect file list on card and dump what is available --- client/src/cmdhfemrtd.c | 120 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 7dac49477..b0ee1a7b0 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -620,6 +620,113 @@ static bool ef_com_get_file_list(uint8_t *datain, int *datainlen, uint8_t *datao return false; } +static bool file_tag_to_file_id(uint8_t *datain, char *filenameout, char *dataout) { + // imagine bothering with a hashmap or writing good code + // couldn't be me + switch (*datain) { + case 0x60: + memcpy(dataout, EF_COM, 4); + memcpy(filenameout, "EF_COM", 6); + break; + case 0x61: + memcpy(dataout, EF_DG1, 4); + memcpy(filenameout, "EF_DG1", 6); + break; + case 0x75: + memcpy(dataout, EF_DG2, 4); + memcpy(filenameout, "EF_DG2", 6); + break; + // These cases are commented out as they require PACE + // Trying to read a PACE file without doing PACE auth kills the session + // case 0x63: + // memcpy(dataout, EF_DG3, 4); + // memcpy(filenameout, "EF_DG3", 6); + // break; + // case 0x76: + // memcpy(dataout, EF_DG4, 4); + // memcpy(filenameout, "EF_DG4", 6); + // break; + case 0x65: + memcpy(dataout, EF_DG5, 4); + memcpy(filenameout, "EF_DG5", 6); + break; + case 0x66: + memcpy(dataout, EF_DG6, 4); + memcpy(filenameout, "EF_DG6", 6); + break; + case 0x67: + memcpy(dataout, EF_DG7, 4); + memcpy(filenameout, "EF_DG7", 6); + break; + case 0x68: + memcpy(dataout, EF_DG8, 4); + memcpy(filenameout, "EF_DG8", 6); + break; + case 0x69: + memcpy(dataout, EF_DG9, 4); + memcpy(filenameout, "EF_DG9", 6); + break; + case 0x6a: + memcpy(dataout, EF_DG10, 4); + memcpy(filenameout, "EF_DG10", 7); + break; + case 0x6b: + memcpy(dataout, EF_DG11, 4); + memcpy(filenameout, "EF_DG11", 7); + break; + case 0x6c: + memcpy(dataout, EF_DG12, 4); + memcpy(filenameout, "EF_DG12", 7); + break; + case 0x6d: + memcpy(dataout, EF_DG13, 4); + memcpy(filenameout, "EF_DG13", 7); + break; + case 0x6e: + memcpy(dataout, EF_DG14, 4); + memcpy(filenameout, "EF_DG14", 7); + break; + case 0x6f: + memcpy(dataout, EF_DG15, 4); + memcpy(filenameout, "EF_DG15", 7); + break; + case 0x70: + memcpy(dataout, EF_DG16, 4); + memcpy(filenameout, "EF_DG16", 7); + break; + case 0x77: + memcpy(dataout, EF_SOD, 4); + memcpy(filenameout, "EF_SOD", 6); + break; + default: + return false; + } + return true; +} + +static bool dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char *file, const char *name, bool use_14b) { + uint8_t response[35000]; + int resplen = 0; + + if (secure_select_file(ks_enc, ks_mac, ssc, file, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to secure select %s, crypto checksum check failed.", name); + DropField(); + return false; + } + + if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to read %s.", name); + DropField(); + return false; + } + + 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); + + return true; +} + int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[25000]; uint8_t rnd_ic[8]; @@ -803,7 +910,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { } PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - // saveFile("EF_COM", ".BIN", response, resplen); + saveFile("EF_COM", ".BIN", response, resplen); uint8_t filelist[50]; int filelistlen = 0; @@ -816,6 +923,17 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(DEBUG, "File List: %s", sprint_hex_inrow(filelist, filelistlen)); + for (int i = 0; i < filelistlen; i++) { + char file_id[5] = {0x00}; + char file_name[8] = {0x00}; + if (file_tag_to_file_id(&filelist[i], file_name, file_id) == false) { + PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); + continue; + } + PrintAndLogEx(DEBUG, "Current file: %s", file_name); + dump_file(ks_enc, ks_mac, ssc, file_id, file_name, use_14b); + } + DropField(); return PM3_SUCCESS; } From a5aed0dffd558cd35993c2c42503c89327eb42e6 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 02:42:37 +0300 Subject: [PATCH 31/40] emrtd: Force dumping EF_SOD --- client/src/cmdhfemrtd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index b0ee1a7b0..a476352a3 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -804,6 +804,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex(response, resplen)); } } + // TODO: account for the case of no BAC PrintAndLogEx(DEBUG, "doc: %s", documentnumber); PrintAndLogEx(DEBUG, "dob: %s", dob); PrintAndLogEx(DEBUG, "exp: %s", expiry); @@ -934,6 +935,8 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { dump_file(ks_enc, ks_mac, ssc, file_id, file_name, use_14b); } + dump_file(ks_enc, ks_mac, ssc, EF_SOD, "EF_SOD", use_14b); + DropField(); return PM3_SUCCESS; } From 1e16b2d2d9b949a74e5455ccbb7f2f1a0b8c8bba Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 17:26:47 +0300 Subject: [PATCH 32/40] emrtd: Code cleanup, impl PRNG --- client/src/cmdhfemrtd.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index a476352a3..5fdf0e7ca 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -26,6 +26,9 @@ #include "des.h" // mbedtls_des_key_set_parity #include "cmdhf14b.h" // exchange_14b_apdu #include "iso14b.h" // ISO14B_CONNECT etc +#include "crapto1/crapto1.h" // prng_successor +#include "commonutil.h" // num_to_bytes +#include "util_posix.h" // msclock #define TIMEOUT 2000 @@ -62,6 +65,9 @@ // App IDs #define AID_MRTD "A0000002471001" +uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; +uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; + static int CmdHelp(const char *Cmd); static uint16_t get_sw(uint8_t *d, uint8_t n) { @@ -481,16 +487,9 @@ static bool _secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int byt PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 20)); - // TODO: hacky - char offsethex[5]; - sprintf(offsethex, "%04X", offset); - char offsetbuffer[8]; - memcpy(offsetbuffer, offsethex, 2); - int p1 = (int)strtol(offsetbuffer, NULL, 16); - memcpy(offsetbuffer, offsethex + 2, 2); - int p2 = (int)strtol(offsetbuffer, NULL, 16); - temp[2] = p1; - temp[3] = p2; + // Set p1 and p2 + temp[2] = (uint8_t)(offset >> 8); + temp[3] = (uint8_t)(offset >> 0); int cmdlen = pad_block(temp, 4, cmd); PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen)); @@ -523,7 +522,7 @@ static bool _secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int byt memcpy(data + 3, do8e, 10); PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); - sprintf(command, "0C%s%02X%02X%02X%s00", READ_BINARY, p1, p2, lc, sprint_hex_inrow(data, lc)); + sprintf(command, "0C%s%04X%02X%s00", READ_BINARY, offset, lc, sprint_hex_inrow(data, lc)); PrintAndLogEx(DEBUG, "command: %s", command); if (exchange_commands(command, dataout, dataoutlen, false, true, use_14b) == false) { @@ -727,6 +726,13 @@ static bool dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char return true; } +static void rng(int length, uint8_t *dataout) { + // Do very very secure prng operations + for (int i = 0; i < (length / 4); i++) { + num_to_bytes(prng_successor(msclock() + i, 32), 4, &dataout[i * 4]); + } +} + int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t response[25000]; uint8_t rnd_ic[8]; @@ -735,12 +741,9 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { int resplen = 0; // bool BAC = true; uint8_t S[32]; - // TODO: Code sponsored jointly by duracell and sony - uint8_t rnd_ifd[8] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; - uint8_t k_ifd[16] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; - // TODO: get these _types into a better spot - uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; - uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; + uint8_t rnd_ifd[8], k_ifd[16]; + rng(8, rnd_ifd); + rng(16, k_ifd); bool use_14b = false; From 8ed358e3bed27df3d323f437ae5bc5b53258cbd5 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 17:28:54 +0300 Subject: [PATCH 33/40] emrtd: Mark DESKey as const --- client/src/cmdhfemrtd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 5fdf0e7ca..4b658d95e 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -65,8 +65,9 @@ // App IDs #define AID_MRTD "A0000002471001" -uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; -uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; +// DESKey Types +const uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; +const uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; static int CmdHelp(const char *Cmd); @@ -271,7 +272,7 @@ static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *outp } -static void deskey(uint8_t *seed, uint8_t *type, int length, uint8_t *dataout) { +static void deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t *dataout) { PrintAndLogEx(DEBUG, "seed: %s", sprint_hex_inrow(seed, 16)); // combine seed and type From 19922e1d23db9e5e33704a9f182e261baf7d99b1 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 17:30:58 +0300 Subject: [PATCH 34/40] emrtd: Clean up includes, further document used files --- client/src/cmdhfemrtd.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 4b658d95e..0490c783a 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -14,11 +14,9 @@ #include #include "fileutils.h" // saveFile #include "cmdparser.h" // command_t -#include "comms.h" // clearCommandBuffer -#include "cmdtrace.h" -#include "cliparser.h" -#include "crc16.h" -#include "cmdhf14a.h" +#include "cmdtrace.h" // CmdTraceList +#include "cliparser.h" // CLIParserContext etc +#include "cmdhf14a.h" // ExchangeAPDU14a #include "protocols.h" // definitions of ISO14A/7816 protocol #include "emv/apduinfo.h" // GetAPDUCodeDescription #include "sha1.h" // KSeed calculation etc From eaea632eb31c542d8c5cd2538a98102195fe594e Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 17:45:53 +0300 Subject: [PATCH 35/40] emrtd: Join secure and insecure reads --- client/src/cmdhfemrtd.c | 335 +++++++++++++++++++--------------------- 1 file changed, 157 insertions(+), 178 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 0490c783a..43f10ad19 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -269,7 +269,6 @@ static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *outp memcpy(output, intermediate_des, 8); } - static void deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t *dataout) { PrintAndLogEx(DEBUG, "seed: %s", sprint_hex_inrow(seed, 16)); @@ -324,42 +323,6 @@ static int _read_binary(int offset, int bytes_to_read, uint8_t *dataout, int *da return exchange_commands(cmd, dataout, dataoutlen, false, true, use_14b); } -static int read_file(uint8_t *dataout, int *dataoutlen, bool use_14b) { - uint8_t response[PM3_CMD_DATA_SIZE]; - int resplen = 0; - uint8_t tempresponse[PM3_CMD_DATA_SIZE]; - int tempresplen = 0; - - if (!_read_binary(0, 4, response, &resplen, use_14b)) { - return false; - } - - int datalen = asn1datalength(response, resplen, 1); - int readlen = datalen - (3 - asn1fieldlength(response, resplen, 1)); - int offset = 4; - int toread; - - while (readlen > 0) { - toread = readlen; - if (readlen > 118) { - toread = 118; - } - - if (!_read_binary(offset, toread, tempresponse, &tempresplen, use_14b)) { - return false; - } - - memcpy(&response[resplen], &tempresponse, tempresplen); - offset += toread; - readlen -= toread; - resplen += tempresplen; - } - - memcpy(dataout, &response, resplen); - *dataoutlen = resplen; - return true; -} - static void bump_ssc(uint8_t *ssc) { PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8)); for (int i = 7; i > 0; i--) { @@ -553,21 +516,28 @@ static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *s return true; } -static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t *dataout, int *dataoutlen, bool use_14b) { - // TODO: join this with regular read file - uint8_t response[25000]; + +static int read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, bool use_14b) { + uint8_t response[35000]; int resplen = 0; uint8_t tempresponse[500]; int tempresplen = 0; + int toread = 4; + int offset = 0; - if (!_secure_read_binary_decrypt(kenc, kmac, ssc, 0, 4, response, &resplen, use_14b)) { - return false; + if (kenc == NULL) { + if (_read_binary(offset, toread, response, &resplen, use_14b) == false) { + return false; + } + } else { + if (_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, response, &resplen, use_14b) == false) { + return false; + } } int datalen = asn1datalength(response, resplen, 1); int readlen = datalen - (3 - asn1fieldlength(response, resplen, 1)); - int offset = 4; - int toread; + offset = 4; while (readlen > 0) { toread = readlen; @@ -575,8 +545,14 @@ static int secure_read_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint8_t toread = 118; } - if (!_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen, use_14b)) { - return false; + if (kenc == NULL) { + if (_read_binary(offset, toread, tempresponse, &tempresplen, use_14b) == false) { + return false; + } + } else { + if (_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen, use_14b) == false) { + return false; + } } memcpy(response + resplen, tempresponse, tempresplen); @@ -712,7 +688,7 @@ static bool dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char return false; } - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { + if (read_file(response, &resplen, ks_enc, ks_mac, ssc, use_14b) == false) { PrintAndLogEx(ERR, "Failed to read %s.", name); DropField(); return false; @@ -738,7 +714,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { uint8_t kenc[50]; uint8_t kmac[50]; int resplen = 0; - // bool BAC = true; + bool BAC = false; uint8_t S[32]; uint8_t rnd_ifd[8], k_ifd[16]; rng(8, rnd_ifd); @@ -775,7 +751,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select and read EF_CardAccess if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS, use_14b)) { - read_file(response, &resplen, use_14b); + read_file(response, &resplen, NULL, NULL, NULL, use_14b); PrintAndLogEx(INFO, "Read EF_CardAccess, len: %i.", resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); } else { @@ -791,153 +767,156 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select EF_COM if (select_file(P1_SELECT_BY_EF, EF_COM, use_14b) == false) { - // BAC = true; + BAC = true; PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); } else { - // BAC = false; + BAC = false; // Select EF_DG1 select_file(P1_SELECT_BY_EF, EF_DG1, use_14b); - if (read_file(response, &resplen, use_14b) == false) { - // BAC = true; + if (read_file(response, &resplen, NULL, NULL, NULL, use_14b) == false) { + BAC = true; PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); } else { - // BAC = false; + BAC = false; PrintAndLogEx(INFO, "EF_DG1: %s", sprint_hex(response, resplen)); } } - // TODO: account for the case of no BAC - PrintAndLogEx(DEBUG, "doc: %s", documentnumber); - PrintAndLogEx(DEBUG, "dob: %s", dob); - PrintAndLogEx(DEBUG, "exp: %s", expiry); - char documentnumbercd = calculate_check_digit(documentnumber); - char dobcd = calculate_check_digit(dob); - char expirycd = calculate_check_digit(expiry); + if (BAC) { + // TODO: account for the case of no BAC + PrintAndLogEx(DEBUG, "doc: %s", documentnumber); + PrintAndLogEx(DEBUG, "dob: %s", dob); + PrintAndLogEx(DEBUG, "exp: %s", expiry); - char kmrz[25]; - sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); - PrintAndLogEx(DEBUG, "kmrz: %s", kmrz); + char documentnumbercd = calculate_check_digit(documentnumber); + char dobcd = calculate_check_digit(dob); + char expirycd = calculate_check_digit(expiry); - uint8_t kseed[16] = {0x00}; - mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); - PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); + char kmrz[25]; + sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); + PrintAndLogEx(DEBUG, "kmrz: %s", kmrz); - deskey(kseed, KENC_type, 16, kenc); - deskey(kseed, KMAC_type, 16, kmac); - PrintAndLogEx(DEBUG, "kenc: %s", sprint_hex_inrow(kenc, 16)); - PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 16)); + uint8_t kseed[16] = { 0x00 }; + mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); + PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); - // Get Challenge - if (get_challenge(8, rnd_ic, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Couldn't get challenge."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(DEBUG, "rnd_ic: %s", sprint_hex_inrow(rnd_ic, 8)); + deskey(kseed, KENC_type, 16, kenc); + deskey(kseed, KMAC_type, 16, kmac); + PrintAndLogEx(DEBUG, "kenc: %s", sprint_hex_inrow(kenc, 16)); + PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 16)); - memcpy(S, rnd_ifd, 8); - memcpy(S + 8, rnd_ic, 8); - memcpy(S + 16, k_ifd, 16); - - PrintAndLogEx(DEBUG, "S: %s", sprint_hex_inrow(S, 32)); - - uint8_t iv[8] = { 0x00 }; - uint8_t e_ifd[32] = { 0x00 }; - - des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd); - PrintAndLogEx(DEBUG, "e_ifd: %s", sprint_hex_inrow(e_ifd, 32)); - - uint8_t m_ifd[8] = { 0x00 }; - - retail_mac(kmac, e_ifd, 32, m_ifd); - PrintAndLogEx(DEBUG, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8)); - - uint8_t cmd_data[40]; - memcpy(cmd_data, e_ifd, 32); - memcpy(cmd_data + 32, m_ifd, 8); - - // Do external authentication - if (external_authenticate(cmd_data, sizeof(cmd_data), response, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?"); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "External authentication successful."); - - uint8_t dec_output[32] = { 0x00 }; - des3_decrypt_cbc(iv, kenc, response, 32, dec_output); - PrintAndLogEx(DEBUG, "dec_output: %s", sprint_hex_inrow(dec_output, 32)); - - if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) { - PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match."); - DropField(); - return PM3_ESOFT; - } - - uint8_t ssc[8] = { 0x00 }; - uint8_t ks_enc[16] = { 0x00 }; - uint8_t ks_mac[16] = { 0x00 }; - uint8_t k_icc[16] = { 0x00 }; - memcpy(k_icc, dec_output + 16, 16); - - // Calculate session keys - for (int x = 0; x < 16; x++) { - kseed[x] = k_ifd[x] ^ k_icc[x]; - } - - PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); - - deskey(kseed, KENC_type, 16, ks_enc); - deskey(kseed, KMAC_type, 16, ks_mac); - - PrintAndLogEx(DEBUG, "ks_enc: %s", sprint_hex_inrow(ks_enc, 16)); - PrintAndLogEx(DEBUG, "ks_mac: %s", sprint_hex_inrow(ks_mac, 16)); - - memcpy(ssc, rnd_ic + 4, 4); - memcpy(ssc + 4, rnd_ifd + 4, 4); - - PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); - - // Select EF_COM - if (secure_select_file(ks_enc, ks_mac, ssc, EF_COM, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select EF_COM."); - DropField(); - return PM3_ESOFT; - } - - if (secure_read_file(ks_enc, ks_mac, ssc, response, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to read EF_COM."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile("EF_COM", ".BIN", response, resplen); - - uint8_t filelist[50]; - int filelistlen = 0; - - if (ef_com_get_file_list(response, &resplen, filelist, &filelistlen) == false) { - PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); - DropField(); - return PM3_ESOFT; - } - - PrintAndLogEx(DEBUG, "File List: %s", sprint_hex_inrow(filelist, filelistlen)); - - for (int i = 0; i < filelistlen; i++) { - char file_id[5] = {0x00}; - char file_name[8] = {0x00}; - if (file_tag_to_file_id(&filelist[i], file_name, file_id) == false) { - PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); - continue; + // Get Challenge + if (get_challenge(8, rnd_ic, &resplen, use_14b) == false) { + PrintAndLogEx(ERR, "Couldn't get challenge."); + DropField(); + return PM3_ESOFT; } - PrintAndLogEx(DEBUG, "Current file: %s", file_name); - dump_file(ks_enc, ks_mac, ssc, file_id, file_name, use_14b); - } + PrintAndLogEx(DEBUG, "rnd_ic: %s", sprint_hex_inrow(rnd_ic, 8)); - dump_file(ks_enc, ks_mac, ssc, EF_SOD, "EF_SOD", use_14b); + memcpy(S, rnd_ifd, 8); + memcpy(S + 8, rnd_ic, 8); + memcpy(S + 16, k_ifd, 16); + + PrintAndLogEx(DEBUG, "S: %s", sprint_hex_inrow(S, 32)); + + uint8_t iv[8] = { 0x00 }; + uint8_t e_ifd[32] = { 0x00 }; + + des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd); + PrintAndLogEx(DEBUG, "e_ifd: %s", sprint_hex_inrow(e_ifd, 32)); + + uint8_t m_ifd[8] = { 0x00 }; + + retail_mac(kmac, e_ifd, 32, m_ifd); + PrintAndLogEx(DEBUG, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8)); + + uint8_t cmd_data[40]; + memcpy(cmd_data, e_ifd, 32); + memcpy(cmd_data + 32, m_ifd, 8); + + // Do external authentication + if (external_authenticate(cmd_data, sizeof(cmd_data), response, &resplen, use_14b) == false) { + PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?"); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "External authentication successful."); + + uint8_t dec_output[32] = { 0x00 }; + des3_decrypt_cbc(iv, kenc, response, 32, dec_output); + PrintAndLogEx(DEBUG, "dec_output: %s", sprint_hex_inrow(dec_output, 32)); + + if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) { + PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match."); + DropField(); + return PM3_ESOFT; + } + + uint8_t ssc[8] = { 0x00 }; + uint8_t ks_enc[16] = { 0x00 }; + uint8_t ks_mac[16] = { 0x00 }; + uint8_t k_icc[16] = { 0x00 }; + memcpy(k_icc, dec_output + 16, 16); + + // Calculate session keys + for (int x = 0; x < 16; x++) { + kseed[x] = k_ifd[x] ^ k_icc[x]; + } + + PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); + + deskey(kseed, KENC_type, 16, ks_enc); + deskey(kseed, KMAC_type, 16, ks_mac); + + PrintAndLogEx(DEBUG, "ks_enc: %s", sprint_hex_inrow(ks_enc, 16)); + PrintAndLogEx(DEBUG, "ks_mac: %s", sprint_hex_inrow(ks_mac, 16)); + + memcpy(ssc, rnd_ic + 4, 4); + memcpy(ssc + 4, rnd_ifd + 4, 4); + + PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); + + // Select EF_COM + if (secure_select_file(ks_enc, ks_mac, ssc, EF_COM, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to secure select EF_COM."); + DropField(); + return PM3_ESOFT; + } + + if (read_file(response, &resplen, ks_enc, ks_mac, ssc, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to read EF_COM."); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile("EF_COM", ".BIN", response, resplen); + + uint8_t filelist[50]; + int filelistlen = 0; + + if (ef_com_get_file_list(response, &resplen, filelist, &filelistlen) == false) { + PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(DEBUG, "File List: %s", sprint_hex_inrow(filelist, filelistlen)); + + for (int i = 0; i < filelistlen; i++) { + char file_id[5] = {0x00}; + char file_name[8] = {0x00}; + if (file_tag_to_file_id(&filelist[i], file_name, file_id) == false) { + PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); + continue; + } + PrintAndLogEx(DEBUG, "Current file: %s", file_name); + dump_file(ks_enc, ks_mac, ssc, file_id, file_name, use_14b); + } + + dump_file(ks_enc, ks_mac, ssc, EF_SOD, "EF_SOD", use_14b); + } DropField(); return PM3_SUCCESS; From 0bca61aa999a16a581a4e6c30e188d86c47a180e Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 18:05:45 +0300 Subject: [PATCH 36/40] emrtd: Continue work on supporting non-BAC passports --- client/src/cmdhfemrtd.c | 154 +++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 43f10ad19..957620b86 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -379,7 +379,7 @@ static void _convert_filename(const char *file, uint8_t *dataout) { dataout[1] = (int)strtol(temp, NULL, 16); } -static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const char *file, bool use_14b) { +static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const char *select_by, const char *file, bool use_14b) { uint8_t response[PM3_CMD_DATA_SIZE]; int resplen = 0; @@ -391,7 +391,7 @@ static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const char command[54]; uint8_t cmd[8]; uint8_t data[21]; - uint8_t temp[8] = {0x0c, 0xa4, 0x02, 0x0c}; + uint8_t temp[8] = {0x0c, 0xa4, strtol(select_by, NULL, 16), 0x0c}; int cmdlen = pad_block(temp, 4, cmd); int datalen = pad_block(file_id, 2, data); @@ -431,7 +431,7 @@ static bool secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const memcpy(data + (datalen + 3), do8e, 10); PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); - sprintf(command, "0C%s020C%02X%s00", SELECT, lc, sprint_hex_inrow(data, lc)); + sprintf(command, "0C%s%s0C%02X%s00", SELECT, select_by, lc, sprint_hex_inrow(data, lc)); PrintAndLogEx(DEBUG, "command: %s", command); if (exchange_commands(command, response, &resplen, false, true, use_14b) == false) { @@ -517,7 +517,7 @@ static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *s } -static int read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, bool use_14b) { +static int read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, bool use_secure, bool use_14b) { uint8_t response[35000]; int resplen = 0; uint8_t tempresponse[500]; @@ -525,12 +525,12 @@ static int read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uint8_t * int toread = 4; int offset = 0; - if (kenc == NULL) { - if (_read_binary(offset, toread, response, &resplen, use_14b) == false) { + if (use_secure == true) { + if (_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, response, &resplen, use_14b) == false) { return false; } } else { - if (_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, response, &resplen, use_14b) == false) { + if (_read_binary(offset, toread, response, &resplen, use_14b) == false) { return false; } } @@ -678,19 +678,31 @@ static bool file_tag_to_file_id(uint8_t *datain, char *filenameout, char *dataou return true; } -static bool dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char *file, const char *name, bool use_14b) { +static bool select_and_read(uint8_t *dataout, int *dataoutlen, const char *file, uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, bool use_secure, bool use_14b) { + if (use_secure == true) { + if (secure_select_file(ks_enc, ks_mac, ssc, P1_SELECT_BY_EF, file, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to secure select %s.", file); + return false; + } + } else { + if (select_file(P1_SELECT_BY_EF, file, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to select %s.", file); + return false; + } + } + + if (read_file(dataout, dataoutlen, ks_enc, ks_mac, ssc, use_secure, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to read %s.", file); + return false; + } + return true; +} + +static bool dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char *file, const char *name, bool use_secure, bool use_14b) { uint8_t response[35000]; int resplen = 0; - if (secure_select_file(ks_enc, ks_mac, ssc, file, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select %s, crypto checksum check failed.", name); - DropField(); - return false; - } - - if (read_file(response, &resplen, ks_enc, ks_mac, ssc, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to read %s.", name); - DropField(); + if (select_and_read(response, &resplen, file, ks_enc, ks_mac, ssc, use_secure, use_14b) == false) { return false; } @@ -709,17 +721,12 @@ static void rng(int length, uint8_t *dataout) { } int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { - uint8_t response[25000]; - uint8_t rnd_ic[8]; - uint8_t kenc[50]; - uint8_t kmac[50]; + uint8_t response[35000] = { 0x00 }; + uint8_t ssc[8] = { 0x00 }; + uint8_t ks_enc[16] = { 0x00 }; + uint8_t ks_mac[16] = { 0x00 }; int resplen = 0; bool BAC = false; - uint8_t S[32]; - uint8_t rnd_ifd[8], k_ifd[16]; - rng(8, rnd_ifd); - rng(16, k_ifd); - bool use_14b = false; // Try to 14a @@ -751,7 +758,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select and read EF_CardAccess if (select_file(P1_SELECT_BY_EF, EF_CARDACCESS, use_14b)) { - read_file(response, &resplen, NULL, NULL, NULL, use_14b); + read_file(response, &resplen, NULL, NULL, NULL, false, use_14b); PrintAndLogEx(INFO, "Read EF_CardAccess, len: %i.", resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); } else { @@ -774,7 +781,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { // Select EF_DG1 select_file(P1_SELECT_BY_EF, EF_DG1, use_14b); - if (read_file(response, &resplen, NULL, NULL, NULL, use_14b) == false) { + if (read_file(response, &resplen, NULL, NULL, NULL, false, use_14b) == false) { BAC = true; PrintAndLogEx(INFO, "Basic Access Control is enforced. Will attempt external authentication."); } else { @@ -784,7 +791,16 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { } if (BAC) { - // TODO: account for the case of no BAC + uint8_t rnd_ic[8] = { 0x00 }; + uint8_t kenc[50] = { 0x00 }; + uint8_t kmac[50] = { 0x00 }; + uint8_t k_icc[16] = { 0x00 }; + uint8_t S[32] = { 0x00 }; + + uint8_t rnd_ifd[8], k_ifd[16]; + rng(8, rnd_ifd); + rng(16, k_ifd); + PrintAndLogEx(DEBUG, "doc: %s", documentnumber); PrintAndLogEx(DEBUG, "dob: %s", dob); PrintAndLogEx(DEBUG, "exp: %s", expiry); @@ -853,10 +869,6 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { return PM3_ESOFT; } - uint8_t ssc[8] = { 0x00 }; - uint8_t ks_enc[16] = { 0x00 }; - uint8_t ks_mac[16] = { 0x00 }; - uint8_t k_icc[16] = { 0x00 }; memcpy(k_icc, dec_output + 16, 16); // Calculate session keys @@ -876,48 +888,44 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { memcpy(ssc + 4, rnd_ifd + 4, 4); PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); - - // Select EF_COM - if (secure_select_file(ks_enc, ks_mac, ssc, EF_COM, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to secure select EF_COM."); - DropField(); - return PM3_ESOFT; - } - - if (read_file(response, &resplen, ks_enc, ks_mac, ssc, use_14b) == false) { - PrintAndLogEx(ERR, "Failed to read EF_COM."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile("EF_COM", ".BIN", response, resplen); - - uint8_t filelist[50]; - int filelistlen = 0; - - if (ef_com_get_file_list(response, &resplen, filelist, &filelistlen) == false) { - PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); - DropField(); - return PM3_ESOFT; - } - - PrintAndLogEx(DEBUG, "File List: %s", sprint_hex_inrow(filelist, filelistlen)); - - for (int i = 0; i < filelistlen; i++) { - char file_id[5] = {0x00}; - char file_name[8] = {0x00}; - if (file_tag_to_file_id(&filelist[i], file_name, file_id) == false) { - PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); - continue; - } - PrintAndLogEx(DEBUG, "Current file: %s", file_name); - dump_file(ks_enc, ks_mac, ssc, file_id, file_name, use_14b); - } - - dump_file(ks_enc, ks_mac, ssc, EF_SOD, "EF_SOD", use_14b); } + // Select EF_COM + if (select_and_read(response, &resplen, EF_COM, ks_enc, ks_mac, ssc, BAC, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to read EF_COM."); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile("EF_COM", ".BIN", response, resplen); + + uint8_t filelist[50]; + int filelistlen = 0; + + if (ef_com_get_file_list(response, &resplen, filelist, &filelistlen) == false) { + PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(DEBUG, "File List: %s", sprint_hex_inrow(filelist, filelistlen)); + + // Dump all files in the file list + for (int i = 0; i < filelistlen; i++) { + char file_id[5] = { 0x00 }; + char file_name[8] = { 0x00 }; + if (file_tag_to_file_id(&filelist[i], file_name, file_id) == false) { + PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); + continue; + } + PrintAndLogEx(DEBUG, "Current file: %s", file_name); + dump_file(ks_enc, ks_mac, ssc, file_id, file_name, BAC, use_14b); + } + + // Dump EF_SOD + dump_file(ks_enc, ks_mac, ssc, EF_SOD, "EF_SOD", BAC, use_14b); + DropField(); return PM3_SUCCESS; } From d197d5df347478e71f59d6bfe0b3f48e7b64402d Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 18:24:24 +0300 Subject: [PATCH 37/40] emrtd: Complete non-BAC support --- client/src/cmdhfemrtd.c | 39 +++++++++++++++++++++++++-------------- client/src/cmdhfemrtd.h | 2 +- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 957620b86..a1e5406af 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -720,7 +720,7 @@ static void rng(int length, uint8_t *dataout) { } } -int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { +int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { uint8_t response[35000] = { 0x00 }; uint8_t ssc[8] = { 0x00 }; uint8_t ks_enc[16] = { 0x00 }; @@ -790,6 +790,13 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry) { } } + if (BAC == true && BAC_available == false) { + PrintAndLogEx(ERR, "This eMRTD enforces Basic Access Control, but you didn't supplied MRZ data. Cannot proceed."); + PrintAndLogEx(HINT, "Check out hf emrtd dump --help, supply data with -n -d and -e."); + DropField(); + return PM3_ESOFT; + } + if (BAC) { uint8_t rnd_ic[8] = { 0x00 }; uint8_t kenc[50] = { 0x00 }; @@ -939,25 +946,29 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("n", "documentnumber", "", "9 character document number"), - arg_str1("d", "dateofbirth", "", "date of birth in YYMMDD format"), - arg_str1("e", "expiry", "", "expiry in YYMMDD format"), + arg_str0("n", "documentnumber", "", "9 character document number"), + arg_str0("d", "dateofbirth", "", "date of birth in YYMMDD format"), + arg_str0("e", "expiry", "", "expiry in YYMMDD format"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t docnum[10]; - uint8_t dob[7]; - uint8_t expiry[7]; - int docnumlen = 9; - int doblen = 6; - int expirylen = 6; - CLIGetStrWithReturn(ctx, 1, docnum, &docnumlen); - CLIGetStrWithReturn(ctx, 2, dob, &doblen); - CLIGetStrWithReturn(ctx, 3, expiry, &expirylen); + uint8_t docnum[10] = { 0x00 }; + uint8_t dob[7] = { 0x00 }; + uint8_t expiry[7] = { 0x00 }; + bool BAC = true; + int slen = 0; // unused + // Go through all args, if even one isn't supplied, mark BAC as unavailable + if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) { + BAC = false; + } else if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { + BAC = false; + } else if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { + BAC = false; + } CLIParserFree(ctx); - return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry); + return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC); } static int cmd_hf_emrtd_list(const char *Cmd) { diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index 5899ad27a..84500fc8b 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -15,5 +15,5 @@ int CmdHFeMRTD(const char *Cmd); -int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry); +int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); #endif From 457311ffd9a09d2ad8255353f36351fd6e6a3ef8 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 18:25:49 +0300 Subject: [PATCH 38/40] emrtd: make clean pass --- client/src/cmdhfemrtd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index a1e5406af..1ca3b9213 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -145,23 +145,23 @@ static char calculate_check_digit(char *data) { static int asn1datalength(uint8_t *datain, int datainlen, int offset) { PrintAndLogEx(DEBUG, "asn1datalength, datain: %s", sprint_hex_inrow(datain, datainlen)); - int lenfield = (int) *(datain + offset); + int lenfield = (int) * (datain + offset); PrintAndLogEx(DEBUG, "asn1datalength, lenfield: %i", lenfield); if (lenfield <= 0x7f) { return lenfield; } else if (lenfield == 0x81) { - return ((int) *(datain + offset + 1)); + return ((int) * (datain + offset + 1)); } else if (lenfield == 0x82) { - return ((int) *(datain + offset + 1) << 8) | ((int) *(datain + offset + 2)); + return ((int) * (datain + offset + 1) << 8) | ((int) * (datain + offset + 2)); } else if (lenfield == 0x83) { - return (((int) *(datain + offset + 1) << 16) | ((int) *(datain + offset + 2)) << 8) | ((int) *(datain + offset + 3)); + return (((int) * (datain + offset + 1) << 16) | ((int) * (datain + offset + 2)) << 8) | ((int) * (datain + offset + 3)); } return false; } static int asn1fieldlength(uint8_t *datain, int datainlen, int offset) { PrintAndLogEx(DEBUG, "asn1fieldlength, datain: %s", sprint_hex_inrow(datain, datainlen)); - int lenfield = (int) *(datain + offset); + int lenfield = (int) * (datain + offset); PrintAndLogEx(DEBUG, "asn1fieldlength, thing: %i", lenfield); if (lenfield <= 0x7f) { return 1; From bc00e92af012cd43896aa7dc64459cd1da6c0bf0 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 19:08:03 +0300 Subject: [PATCH 39/40] emrtd: Switch from size_t to int to make CI happy --- client/src/cmdhfemrtd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 1ca3b9213..a3d98955e 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -293,10 +293,10 @@ static void deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t *data } static int select_file(const char *select_by, const char *file_id, bool use_14b) { - size_t file_id_len = strlen(file_id) / 2; + int file_id_len = strlen(file_id) / 2; char cmd[50]; - sprintf(cmd, "00%s%s0C%02lu%s", SELECT, select_by, file_id_len, file_id); + sprintf(cmd, "00%s%s0C%02X%s", SELECT, select_by, file_id_len, file_id); return exchange_commands_noout(cmd, false, true, use_14b); } From 3339ba110f91cc24833a814a1248167a7b89da70 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 14 Dec 2020 20:09:20 +0300 Subject: [PATCH 40/40] emrtd: Split BAC into its own function --- client/src/cmdhfemrtd.c | 223 +++++++++++++++++++++------------------- 1 file changed, 119 insertions(+), 104 deletions(-) diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index a3d98955e..d08a789f9 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -28,7 +28,11 @@ #include "commonutil.h" // num_to_bytes #include "util_posix.h" // msclock -#define TIMEOUT 2000 +// Max file size in bytes. Used in several places. +// Average EF_DG2 seems to be around 20-25kB or so, but ICAO doesn't set an upper limit +// Iris data seems to be suggested to be around 35kB per eye (Presumably bumping up the file size to around 70kB) +// but as we cannot read that until we implement PACE, 35k seems to be a safe point. +#define MAX_FILE_SIZE 35000 // ISO7816 commands #define SELECT "A4" @@ -518,7 +522,7 @@ static bool _secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *s static int read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, bool use_secure, bool use_14b) { - uint8_t response[35000]; + uint8_t response[MAX_FILE_SIZE]; int resplen = 0; uint8_t tempresponse[500]; int tempresplen = 0; @@ -699,7 +703,7 @@ static bool select_and_read(uint8_t *dataout, int *dataoutlen, const char *file, } static bool dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char *file, const char *name, bool use_secure, bool use_14b) { - uint8_t response[35000]; + uint8_t response[MAX_FILE_SIZE]; int resplen = 0; if (select_and_read(response, &resplen, file, ks_enc, ks_mac, ssc, use_secure, use_14b) == false) { @@ -720,12 +724,114 @@ static void rng(int length, uint8_t *dataout) { } } +static bool do_bac(char *documentnumber, char *dob, char *expiry, uint8_t *ssc, uint8_t *ks_enc, uint8_t *ks_mac, bool use_14b) { + uint8_t response[MAX_FILE_SIZE] = { 0x00 }; + int resplen = 0; + + uint8_t rnd_ic[8] = { 0x00 }; + uint8_t kenc[50] = { 0x00 }; + uint8_t kmac[50] = { 0x00 }; + uint8_t k_icc[16] = { 0x00 }; + uint8_t S[32] = { 0x00 }; + + uint8_t rnd_ifd[8], k_ifd[16]; + rng(8, rnd_ifd); + rng(16, k_ifd); + + PrintAndLogEx(DEBUG, "doc: %s", documentnumber); + PrintAndLogEx(DEBUG, "dob: %s", dob); + PrintAndLogEx(DEBUG, "exp: %s", expiry); + + char documentnumbercd = calculate_check_digit(documentnumber); + char dobcd = calculate_check_digit(dob); + char expirycd = calculate_check_digit(expiry); + + char kmrz[25]; + sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); + PrintAndLogEx(DEBUG, "kmrz: %s", kmrz); + + uint8_t kseed[16] = { 0x00 }; + mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); + PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); + + deskey(kseed, KENC_type, 16, kenc); + deskey(kseed, KMAC_type, 16, kmac); + PrintAndLogEx(DEBUG, "kenc: %s", sprint_hex_inrow(kenc, 16)); + PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 16)); + + // Get Challenge + if (get_challenge(8, rnd_ic, &resplen, use_14b) == false) { + PrintAndLogEx(ERR, "Couldn't get challenge."); + return false; + } + PrintAndLogEx(DEBUG, "rnd_ic: %s", sprint_hex_inrow(rnd_ic, 8)); + + memcpy(S, rnd_ifd, 8); + memcpy(S + 8, rnd_ic, 8); + memcpy(S + 16, k_ifd, 16); + + PrintAndLogEx(DEBUG, "S: %s", sprint_hex_inrow(S, 32)); + + uint8_t iv[8] = { 0x00 }; + uint8_t e_ifd[32] = { 0x00 }; + + des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd); + PrintAndLogEx(DEBUG, "e_ifd: %s", sprint_hex_inrow(e_ifd, 32)); + + uint8_t m_ifd[8] = { 0x00 }; + + retail_mac(kmac, e_ifd, 32, m_ifd); + PrintAndLogEx(DEBUG, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8)); + + uint8_t cmd_data[40]; + memcpy(cmd_data, e_ifd, 32); + memcpy(cmd_data + 32, m_ifd, 8); + + // Do external authentication + if (external_authenticate(cmd_data, sizeof(cmd_data), response, &resplen, use_14b) == false) { + PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?"); + return false; + } + PrintAndLogEx(INFO, "External authentication successful."); + + uint8_t dec_output[32] = { 0x00 }; + des3_decrypt_cbc(iv, kenc, response, 32, dec_output); + PrintAndLogEx(DEBUG, "dec_output: %s", sprint_hex_inrow(dec_output, 32)); + + if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) { + PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match."); + return false; + } + + memcpy(k_icc, dec_output + 16, 16); + + // Calculate session keys + for (int x = 0; x < 16; x++) { + kseed[x] = k_ifd[x] ^ k_icc[x]; + } + + PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); + + deskey(kseed, KENC_type, 16, ks_enc); + deskey(kseed, KMAC_type, 16, ks_mac); + + PrintAndLogEx(DEBUG, "ks_enc: %s", sprint_hex_inrow(ks_enc, 16)); + PrintAndLogEx(DEBUG, "ks_mac: %s", sprint_hex_inrow(ks_mac, 16)); + + memcpy(ssc, rnd_ic + 4, 4); + memcpy(ssc + 4, rnd_ifd + 4, 4); + + PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); + + return true; +} + int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { - uint8_t response[35000] = { 0x00 }; + uint8_t response[MAX_FILE_SIZE] = { 0x00 }; + int resplen = 0; uint8_t ssc[8] = { 0x00 }; uint8_t ks_enc[16] = { 0x00 }; uint8_t ks_mac[16] = { 0x00 }; - int resplen = 0; bool BAC = false; bool use_14b = false; @@ -790,111 +896,20 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab } } - if (BAC == true && BAC_available == false) { - PrintAndLogEx(ERR, "This eMRTD enforces Basic Access Control, but you didn't supplied MRZ data. Cannot proceed."); - PrintAndLogEx(HINT, "Check out hf emrtd dump --help, supply data with -n -d and -e."); - DropField(); - return PM3_ESOFT; - } - + // Do Basic Access Aontrol if (BAC) { - uint8_t rnd_ic[8] = { 0x00 }; - uint8_t kenc[50] = { 0x00 }; - uint8_t kmac[50] = { 0x00 }; - uint8_t k_icc[16] = { 0x00 }; - uint8_t S[32] = { 0x00 }; - - uint8_t rnd_ifd[8], k_ifd[16]; - rng(8, rnd_ifd); - rng(16, k_ifd); - - PrintAndLogEx(DEBUG, "doc: %s", documentnumber); - PrintAndLogEx(DEBUG, "dob: %s", dob); - PrintAndLogEx(DEBUG, "exp: %s", expiry); - - char documentnumbercd = calculate_check_digit(documentnumber); - char dobcd = calculate_check_digit(dob); - char expirycd = calculate_check_digit(expiry); - - char kmrz[25]; - sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); - PrintAndLogEx(DEBUG, "kmrz: %s", kmrz); - - uint8_t kseed[16] = { 0x00 }; - mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); - PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); - - deskey(kseed, KENC_type, 16, kenc); - deskey(kseed, KMAC_type, 16, kmac); - PrintAndLogEx(DEBUG, "kenc: %s", sprint_hex_inrow(kenc, 16)); - PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 16)); - - // Get Challenge - if (get_challenge(8, rnd_ic, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Couldn't get challenge."); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(DEBUG, "rnd_ic: %s", sprint_hex_inrow(rnd_ic, 8)); - - memcpy(S, rnd_ifd, 8); - memcpy(S + 8, rnd_ic, 8); - memcpy(S + 16, k_ifd, 16); - - PrintAndLogEx(DEBUG, "S: %s", sprint_hex_inrow(S, 32)); - - uint8_t iv[8] = { 0x00 }; - uint8_t e_ifd[32] = { 0x00 }; - - des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd); - PrintAndLogEx(DEBUG, "e_ifd: %s", sprint_hex_inrow(e_ifd, 32)); - - uint8_t m_ifd[8] = { 0x00 }; - - retail_mac(kmac, e_ifd, 32, m_ifd); - PrintAndLogEx(DEBUG, "m_ifd: %s", sprint_hex_inrow(m_ifd, 8)); - - uint8_t cmd_data[40]; - memcpy(cmd_data, e_ifd, 32); - memcpy(cmd_data + 32, m_ifd, 8); - - // Do external authentication - if (external_authenticate(cmd_data, sizeof(cmd_data), response, &resplen, use_14b) == false) { - PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?"); - DropField(); - return PM3_ESOFT; - } - PrintAndLogEx(INFO, "External authentication successful."); - - uint8_t dec_output[32] = { 0x00 }; - des3_decrypt_cbc(iv, kenc, response, 32, dec_output); - PrintAndLogEx(DEBUG, "dec_output: %s", sprint_hex_inrow(dec_output, 32)); - - if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) { - PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match."); + // If BAC isn't available, exit out and warn user. + if (!BAC_available) { + PrintAndLogEx(ERR, "This eMRTD enforces Basic Access Control, but you didn't supplied MRZ data. Cannot proceed."); + PrintAndLogEx(HINT, "Check out hf emrtd dump --help, supply data with -n -d and -e."); DropField(); return PM3_ESOFT; } - memcpy(k_icc, dec_output + 16, 16); - - // Calculate session keys - for (int x = 0; x < 16; x++) { - kseed[x] = k_ifd[x] ^ k_icc[x]; + if (do_bac(documentnumber, dob, expiry, ssc, ks_enc, ks_mac, use_14b) == false) { + DropField(); + return PM3_ESOFT; } - - PrintAndLogEx(DEBUG, "kseed: %s", sprint_hex_inrow(kseed, 16)); - - deskey(kseed, KENC_type, 16, ks_enc); - deskey(kseed, KMAC_type, 16, ks_mac); - - PrintAndLogEx(DEBUG, "ks_enc: %s", sprint_hex_inrow(ks_enc, 16)); - PrintAndLogEx(DEBUG, "ks_mac: %s", sprint_hex_inrow(ks_mac, 16)); - - memcpy(ssc, rnd_ic + 4, 4); - memcpy(ssc + 4, rnd_ifd + 4, 4); - - PrintAndLogEx(DEBUG, "ssc: %s", sprint_hex_inrow(ssc, 8)); } // Select EF_COM