mirror of
https://github.com/Proxmark/proxmark3.git
synced 2025-08-22 06:13:27 -07:00
communication fido chaining + iso14443-4 chaining. needs refactoring...
This commit is contained in:
parent
fbd3f981c6
commit
a1c6c8f937
6 changed files with 107 additions and 19 deletions
|
@ -116,7 +116,7 @@ int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response)
|
||||||
switch(iso_type)
|
switch(iso_type)
|
||||||
{
|
{
|
||||||
case 'a':
|
case 'a':
|
||||||
return iso14_apdu(apdu, (uint16_t) length, response);
|
return iso14_apdu(apdu, (uint16_t) length, response, NULL);
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
return iso14443b_apdu(apdu, length, response);
|
return iso14443b_apdu(apdu, length, response);
|
||||||
|
|
|
@ -1935,15 +1935,21 @@ b8 b7 b6 b5 b4 b3 b2 b1
|
||||||
b5,b6 = 00 - DESELECT
|
b5,b6 = 00 - DESELECT
|
||||||
11 - WTX
|
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 parity[MAX_PARITY_SIZE];
|
||||||
uint8_t real_cmd[cmd_len + 4];
|
uint8_t real_cmd[cmd_len + 4];
|
||||||
|
|
||||||
// ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02
|
if (cmd_len) {
|
||||||
real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00)
|
// ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02
|
||||||
// put block number into the PCB
|
real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00)
|
||||||
real_cmd[0] |= iso14_pcb_blocknum;
|
// put block number into the PCB
|
||||||
memcpy(real_cmd + 1, cmd, cmd_len);
|
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);
|
AppendCrc14443a(real_cmd, cmd_len + 1);
|
||||||
|
|
||||||
ReaderTransmit(real_cmd, cmd_len + 3, NULL);
|
ReaderTransmit(real_cmd, cmd_len + 3, NULL);
|
||||||
|
@ -1983,6 +1989,10 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) {
|
||||||
iso14_pcb_blocknum ^= 1;
|
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
|
// crc check
|
||||||
if (len >=3 && !CheckCrc14443(CRC_14443_A, data_bytes, len)) {
|
if (len >=3 && !CheckCrc14443(CRC_14443_A, data_bytes, len)) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2050,9 +2060,10 @@ void ReaderIso14443a(UsbCommand *c)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(param & ISO14A_APDU && !cantSELECT) {
|
if(param & ISO14A_APDU && !cantSELECT) {
|
||||||
arg0 = iso14_apdu(cmd, len, buf);
|
uint8_t res;
|
||||||
|
arg0 = iso14_apdu(cmd, len, buf, &res);
|
||||||
LED_B_ON();
|
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();
|
LED_B_OFF();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 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 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 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_trigger(bool enable);
|
||||||
extern void iso14a_set_timeout(uint32_t timeout);
|
extern void iso14a_set_timeout(uint32_t timeout);
|
||||||
|
|
|
@ -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) {
|
int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
|
||||||
uint16_t cmdc = 0;
|
uint16_t cmdc = 0;
|
||||||
|
int dlen;
|
||||||
|
*dataoutlen = 0;
|
||||||
|
|
||||||
if (activateField) {
|
if (activateField) {
|
||||||
cmdc |= ISO14A_CONNECT | ISO14A_CLEAR_TRACE;
|
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
|
// https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size
|
||||||
// here length USB_CMD_DATA_SIZE=512
|
// here length USB_CMD_DATA_SIZE=512
|
||||||
// timeout must be authomatically set by "get ATS"
|
// 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);
|
memcpy(c.d.asBytes, datain, datainlen);
|
||||||
SendCommand(&c);
|
SendCommand(&c);
|
||||||
|
|
||||||
|
@ -781,10 +783,12 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
||||||
recv = resp.d.asBytes;
|
recv = resp.d.asBytes;
|
||||||
int iLen = resp.arg[0];
|
int iLen = resp.arg[0];
|
||||||
|
uint8_t res = resp.arg[1];
|
||||||
|
|
||||||
*dataoutlen = iLen - 2;
|
dlen = iLen - 2;
|
||||||
if (*dataoutlen < 0)
|
if (dlen < 0)
|
||||||
*dataoutlen = 0;
|
dlen = 0;
|
||||||
|
*dataoutlen += dlen;
|
||||||
|
|
||||||
if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
|
if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
|
||||||
PrintAndLog("APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
|
PrintAndLog("APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen);
|
||||||
|
@ -810,6 +814,33 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea
|
||||||
return 3;
|
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
|
// check apdu length
|
||||||
if (iLen < 4) {
|
if (iLen < 4) {
|
||||||
PrintAndLog("APDU ERROR: Small APDU response. Len=%d", iLen);
|
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;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!leaveSignalON)
|
||||||
|
DropField();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
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) {
|
int CmdHFFidoInfo(const char *cmd) {
|
||||||
|
|
||||||
if (cmd && strlen(cmd) > 0)
|
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
|
// 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
|
// 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);
|
SetAPDULogging(true);
|
||||||
|
|
||||||
uint8_t buf[APDU_RES_LEN] = {0};
|
uint8_t buf[2048] = {0};
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
uint16_t sw = 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) {
|
if (res) {
|
||||||
PrintAndLog("Can't select authenticator. Exit...");
|
PrintAndLog("Can't select authenticator. res=%x. Exit...", res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sw != 0x9000) {
|
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;
|
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;
|
return 0;
|
||||||
|
|
|
@ -68,6 +68,10 @@ extern struct tlvdb *GetdCVVRawFromTrack2(const struct tlv *track2);
|
||||||
|
|
||||||
extern void SetAPDULogging(bool logging);
|
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
|
// search application
|
||||||
extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
extern int EMVSearchPSE(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
||||||
extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
extern int EMVSearch(bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue