From a1c6c8f9370b642c6ad2df24ae12615fe8178ad2 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Wed, 17 Oct 2018 17:52:16 +0300 Subject: [PATCH] communication fido chaining + iso14443-4 chaining. needs refactoring... --- armsrc/epa.c | 2 +- armsrc/iso14443a.c | 27 +++++++++++++++++-------- armsrc/iso14443a.h | 2 +- client/cmdhf14a.c | 44 ++++++++++++++++++++++++++++++++++++----- client/cmdhffido.c | 47 ++++++++++++++++++++++++++++++++++++++++---- client/emv/emvcore.h | 4 ++++ 6 files changed, 107 insertions(+), 19 deletions(-) diff --git a/armsrc/epa.c b/armsrc/epa.c index fd71430b..01aff302 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -116,7 +116,7 @@ int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response) switch(iso_type) { case 'a': - return iso14_apdu(apdu, (uint16_t) length, response); + return iso14_apdu(apdu, (uint16_t) length, response, NULL); break; case 'b': return iso14443b_apdu(apdu, length, response); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 059db71e..c6a65d50 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1935,15 +1935,21 @@ b8 b7 b6 b5 b4 b3 b2 b1 b5,b6 = 00 - DESELECT 11 - WTX */ -int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { +int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data, uint8_t *res) { uint8_t parity[MAX_PARITY_SIZE]; uint8_t real_cmd[cmd_len + 4]; - // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 - real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) - // put block number into the PCB - real_cmd[0] |= iso14_pcb_blocknum; - memcpy(real_cmd + 1, cmd, cmd_len); + if (cmd_len) { + // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 + real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) + // put block number into the PCB + real_cmd[0] |= iso14_pcb_blocknum; + memcpy(real_cmd + 1, cmd, cmd_len); + } else { + // R-block. ACK + real_cmd[0] = 0xA2; // r-block + ACK + real_cmd[0] |= iso14_pcb_blocknum; + } AppendCrc14443a(real_cmd, cmd_len + 1); ReaderTransmit(real_cmd, cmd_len + 3, NULL); @@ -1982,6 +1988,10 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { { iso14_pcb_blocknum ^= 1; } + + // if we received I-block with chaining we need to send ACK and receive another block of data + if (res) + *res = data_bytes[0]; // crc check if (len >=3 && !CheckCrc14443(CRC_14443_A, data_bytes, len)) { @@ -2050,9 +2060,10 @@ void ReaderIso14443a(UsbCommand *c) } if(param & ISO14A_APDU && !cantSELECT) { - arg0 = iso14_apdu(cmd, len, buf); + uint8_t res; + arg0 = iso14_apdu(cmd, len, buf, &res); LED_B_ON(); - cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + cmd_send(CMD_ACK, arg0, res, 0, buf, sizeof(buf)); LED_B_OFF(); } diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 8796edf5..396b2100 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -49,7 +49,7 @@ extern int EmSendPrecompiledCmd(tag_response_info_t *response_info); extern bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *buffer_size); extern void iso14443a_setup(uint8_t fpga_minor_mode); -extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data); +extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data, uint8_t *res); extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); extern void iso14a_set_trigger(bool enable); extern void iso14a_set_timeout(uint32_t timeout); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 63b1cda6..8947d798 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -749,6 +749,8 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { uint16_t cmdc = 0; + int dlen; + *dataoutlen = 0; if (activateField) { cmdc |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE; @@ -760,7 +762,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length USB_CMD_DATA_SIZE=512 // timeout must be authomatically set by "get ATS" - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | cmdc, (datainlen & 0xFFFF), 0}}; + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0xFFFF), 0}}; memcpy(c.d.asBytes, datain, datainlen); SendCommand(&c); @@ -781,10 +783,12 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { recv = resp.d.asBytes; int iLen = resp.arg[0]; + uint8_t res = resp.arg[1]; - *dataoutlen = iLen - 2; - if (*dataoutlen < 0) - *dataoutlen = 0; + dlen = iLen - 2; + if (dlen < 0) + dlen = 0; + *dataoutlen += dlen; if (maxdataoutlen && *dataoutlen > maxdataoutlen) { PrintAndLog("APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); @@ -792,7 +796,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea } memcpy(dataout, recv, *dataoutlen); - + if(!iLen) { PrintAndLog("APDU ERROR: No APDU response."); return 1; @@ -810,6 +814,33 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea return 3; } + if ((res & 0x10) != 0) { + // I-block with chaining + int oldlen = *dataoutlen; + printf("-->%02x oldlen:%d\n", res, oldlen); + UsbCommand c1 = {CMD_READER_ISO_14443a, {ISO14A_APDU | ISO14A_NO_DISCONNECT, 0, 0}}; + SendCommand(&c1); + + uint8_t *recv1; + UsbCommand resp1; + if (WaitForResponseTimeout(CMD_ACK, &resp1, 1500)) { + dlen = resp1.arg[0] - 2; + if (dlen < 0) + dlen = 0; + *dataoutlen += dlen; + + recv1 = resp1.d.asBytes; + printf("vlen:%d dlen:%d\n", resp1.arg[0] & 0xFFFF, dlen); + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLog("APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); + return 2; + } + memcpy(&dataout[oldlen], recv1, dlen); + } + } + + // check apdu length if (iLen < 4) { PrintAndLog("APDU ERROR: Small APDU response. Len=%d", iLen); @@ -821,6 +852,9 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea return 4; } + if (!leaveSignalON) + DropField(); + return 0; } diff --git a/client/cmdhffido.c b/client/cmdhffido.c index 49d57a68..07de6af7 100644 --- a/client/cmdhffido.c +++ b/client/cmdhffido.c @@ -43,6 +43,21 @@ int FIDOSelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t Ma return EMVSelect(ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL); } +int FIDOExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { + int res = EMVExchange(true, apdu, Result, MaxResultLen, ResultLen, sw, NULL); + // software chaining + while ((*sw >> 8) == 0x61) { + size_t oldlen = *ResultLen; + res = EMVExchange(true, (sAPDU){0x00, 0xC0, 0x00, 0x00, 0x00, NULL}, &Result[oldlen], MaxResultLen, ResultLen, sw, NULL); + *ResultLen += oldlen; + } + return res; +} + +int FIDORegister(uint8_t *params, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { + return FIDOExchange((sAPDU){0x00, 0x01, 0x03, 0x00, 64, params}, Result, MaxResultLen, ResultLen, sw); +} + int CmdHFFidoInfo(const char *cmd) { if (cmd && strlen(cmd) > 0) @@ -92,24 +107,48 @@ int CmdHFFidoRegister(const char *cmd) { // challenge parameter [32 bytes] - The challenge parameter is the SHA-256 hash of the Client Data, a stringified JSON data structure that the FIDO Client prepares // application parameter [32 bytes] - The application parameter is the SHA-256 hash of the UTF-8 encoding of the application identity + uint8_t data[64] = {0}; + SetAPDULogging(true); - uint8_t buf[APDU_RES_LEN] = {0}; + uint8_t buf[2048] = {0}; size_t len = 0; uint16_t sw = 0; - int res = FIDOSelect(true, false, buf, sizeof(buf), &len, &sw); + int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw); if (res) { - PrintAndLog("Can't select authenticator. Exit..."); + PrintAndLog("Can't select authenticator. res=%x. Exit...", res); return res; } if (sw != 0x9000) { - PrintAndLog("APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLog("Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); return 2; } + res = FIDORegister(data, buf, sizeof(buf), &len, &sw); +/* if (res) { + PrintAndLog("Can't execute register command. res=%x. Exit...", res); + return res; + } + if (sw != 0x9000) { + PrintAndLog("Can't execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + return 3; + }*/ + + //PrintAndLog("data: %s", sprint_hex(buf, len)); + + if (buf[0] != 0x05) { + PrintAndLog("ERROR: First byte must be 0x05, but it %2x", buf[0]); + return 5; + } + PrintAndLog("User public key: %s", sprint_hex(&buf[1], 65)); + + uint8_t keyHandleLen = buf[66]; + PrintAndLog("Key handle[%d]: %s", keyHandleLen, sprint_hex(&buf[67], keyHandleLen)); + + PrintAndLog("DER certificate[%d]: %s", keyHandleLen, sprint_hex(&buf[67 + keyHandleLen], 20)); return 0; diff --git a/client/emv/emvcore.h b/client/emv/emvcore.h index 94be4fcf..9f8b4f59 100644 --- a/client/emv/emvcore.h +++ b/client/emv/emvcore.h @@ -68,6 +68,10 @@ extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2); extern void SetAPDULogging(bool logging); +// exchange +extern int EMVExchange(bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); + + // search application extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);