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