diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 545c9ac45..bd9b4d2ab 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1440,7 +1440,13 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t if (Handle14443bSamplesFromTag(ci, cq)) { - *eof_time = GetCountSspClkDelta(dma_start_time) - DELAY_TAG_TO_ARM; // end of EOF + // Response timing is measured from DMA start, but trace rows use + // absolute SSP time like reader frames. + uint32_t eof_delta = GetCountSspClkDelta(dma_start_time); + if (eof_delta > DELAY_TAG_TO_ARM) { + eof_delta -= DELAY_TAG_TO_ARM; + } + *eof_time = dma_start_time + eof_delta; // end of EOF if (Demod.len > Demod.max_len) { ret = PM3_EOVFLOW; @@ -1983,6 +1989,20 @@ static int iso14443b_select_prime_card(iso14b_prime_card_select_t *card) { } } + uint8_t attrib[] = { + r_repgen[0], + ISO14443B_PRIME_CMD_ATTRIB, + r_repgen[2], + r_repgen[3], + r_repgen[4], + r_repgen[5], + 0x00, + 0x00, + }; + AddCrc14B(attrib, sizeof(attrib) - 2); + start_time = eof_time + ISO14B_TR2; + CodeAndTransmit14443bAsReader(attrib, sizeof(attrib), &start_time, &eof_time, true); + s_iso14b_pcb_blocknum = 0; return PM3_SUCCESS; } diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index e1d31ccc6..7119029ba 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -61,6 +61,8 @@ // iso14b apdu input frame length static uint16_t apdu_frame_length = 0; +static uint8_t prime_vt_addr = ISO14443B_PRIME_VT_ADDR_DEFAULT; +static uint8_t prime_com_ra_cmd = ISO14443B_PRIME_COM_RA_START; //static uint16_t ats_fsc[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; static bool apdu_in_framing_enable = true; @@ -1339,18 +1341,18 @@ static bool HF14B_ST_Info(bool verbose, bool do_aid_search) { return true; } -static bool get_prime_card(iso14b_prime_card_select_t *card, bool verbose) { - - if (card == NULL) { - return false; - } +int select_card_14443b_prime(bool disconnect, iso14b_prime_card_select_t *card, bool verbose) { iso14b_raw_cmd_t packet = { - .flags = (ISO14B_CONNECT | ISO14B_SELECT_PRIME | ISO14B_DISCONNECT), + .flags = (ISO14B_CONNECT | ISO14B_SELECT_PRIME | ISO14B_CLEARTRACE), .timeout = 0, .rawlen = 0, }; + if (disconnect) { + packet.flags |= ISO14B_DISCONNECT; + } + clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); @@ -1358,26 +1360,34 @@ static bool get_prime_card(iso14b_prime_card_select_t *card, bool verbose) { if (verbose) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); } - return false; + return PM3_ETIMEOUT; } switch (resp.status) { case PM3_SUCCESS: { - if (resp.length < sizeof(*card)) { + if (resp.length < sizeof(iso14b_prime_card_select_t)) { if (verbose) { PrintAndLogEx(FAILED, "ISO 14443-B' card select response too short (%u bytes)", resp.length); } - return false; + return PM3_ELENGTH; } - memcpy(card, resp.data.asBytes, sizeof(*card)); - if (card->repgen_cmd != ISO14443B_PRIME_CMD_REPGEN || card->atr_len > sizeof(card->atr)) { + iso14b_prime_card_select_t selected = {0}; + memcpy(&selected, resp.data.asBytes, sizeof(selected)); + if (selected.repgen_cmd != ISO14443B_PRIME_CMD_REPGEN || selected.atr_len > sizeof(selected.atr)) { if (verbose) { PrintAndLogEx(FAILED, "ISO 14443-B' invalid card select response"); } - return false; + return PM3_EWRONGANSWER; } - return true; + + prime_vt_addr = selected.vt_addr; + prime_com_ra_cmd = ISO14443B_PRIME_COM_RA_START; + SetISODEPState(disconnect ? ISODEP_INACTIVE : ISODEP_NFCB_PRIME); + if (card) { + *card = selected; + } + return PM3_SUCCESS; } case PM3_ELENGTH: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-B' REPGEN wrong length"); @@ -1393,7 +1403,7 @@ static bool get_prime_card(iso14b_prime_card_select_t *card, bool verbose) { break; } - return false; + return resp.status; } // menu command to get and print all info known about any known 14b tag @@ -1656,7 +1666,7 @@ static bool HF14B_picopass_reader(bool verbose) { static bool HF14B_prime_reader(bool verbose) { iso14b_prime_card_select_t card = {0}; - if (get_prime_card(&card, verbose) == false) { + if (select_card_14443b_prime(true, &card, verbose) != PM3_SUCCESS) { return false; } print_prime_general_info(&card); @@ -2597,6 +2607,118 @@ int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, return PM3_SUCCESS; } +static uint8_t next_prime_com_ra_cmd(uint8_t cmd) { + return (cmd + 0x02) & 0x0F; +} + +int exchange_14b_prime_apdu(uint8_t *datain, int datainlen, bool activate_field, + bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, + int *dataoutlen, int user_timeout) { + + if (dataoutlen == NULL || dataout == NULL || datainlen < 0 || datainlen > 0xFE || (datain == NULL && datainlen > 0)) { + return PM3_EINVARG; + } + *dataoutlen = 0; + + if (activate_field) { + int selres = select_card_14443b_prime(false, NULL, false); + if (selres != PM3_SUCCESS) { + return selres; + } + } + + uint8_t frame[PM3_CMD_DATA_SIZE] = {0}; + const uint16_t frame_len = (uint16_t)datainlen + 3; + if (frame_len > sizeof(frame)) { + return PM3_EINVARG; + } + + frame[0] = prime_vt_addr; + frame[1] = prime_com_ra_cmd; + frame[2] = (uint8_t)datainlen + 1; + if (datainlen > 0) { + memcpy(frame + 3, datain, datainlen); + } + + uint32_t flags = ISO14B_RAW | ISO14B_APPEND_CRC; + uint32_t timeout = 0; + if (user_timeout > 0) { + flags |= ISO14B_SET_TIMEOUT; + if (user_timeout > MAX_14B_TIMEOUT_MS) { + user_timeout = MAX_14B_TIMEOUT_MS; + PrintAndLogEx(INFO, "set timeout to 4.9 seconds. The max we can wait for response"); + } + timeout = (uint32_t)((13560 / 128) * user_timeout); + } + + iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + frame_len); + if (packet == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + return PM3_EMALLOC; + } + + packet->flags = flags; + packet->timeout = timeout; + packet->rawlen = frame_len; + memcpy(packet->raw, frame, frame_len); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + packet->rawlen); + free(packet); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, MAX(APDU_TIMEOUT, user_timeout)) == false) { + if (leave_signal_on == false) { + switch_off_field_14b(); + } + PrintAndLogEx(ERR, "APDU: reply timeout"); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + if (leave_signal_on == false) { + switch_off_field_14b(); + } + PrintAndLogEx(ERR, "APDU: no Type B' APDU response"); + return resp.status; + } + + const uint8_t *rx = resp.data.asBytes; + if (resp.length < 5 || check_crc(CRC_14443_B, rx, resp.length) == false) { + if (leave_signal_on == false) { + switch_off_field_14b(); + } + return PM3_ECRC; + } + + const uint8_t rx_len = rx[2]; + if (rx[0] != prime_vt_addr || (rx[1] & 0x01) || rx_len == 0 || resp.length < (uint16_t)rx_len + 4) { + if (leave_signal_on == false) { + switch_off_field_14b(); + } + return PM3_EWRONGANSWER; + } + + const int apdu_len = rx_len - 1; + if (maxdataoutlen && apdu_len > maxdataoutlen) { + if (leave_signal_on == false) { + switch_off_field_14b(); + } + PrintAndLogEx(ERR, "APDU: buffer too small ( " _RED_("%d") " ), needs " _YELLOW_("%d") " bytes", maxdataoutlen, apdu_len); + return PM3_ESOFT; + } + + memcpy(dataout, rx + 3, apdu_len); + *dataoutlen = apdu_len; + prime_com_ra_cmd = next_prime_com_ra_cmd(prime_com_ra_cmd); + + if (leave_signal_on == false) { + switch_off_field_14b(); + } + + return PM3_SUCCESS; +} + // ISO14443-4. 7. Half-duplex block transmission protocol static int CmdHF14BAPDU(const char *Cmd) { CLIParserContext *ctx; diff --git a/client/src/cmdhf14b.h b/client/src/cmdhf14b.h index 065dbc29c..9c5121f90 100644 --- a/client/src/cmdhf14b.h +++ b/client/src/cmdhf14b.h @@ -27,7 +27,9 @@ int CmdHF14BNdefRead(const char *Cmd); uint8_t *get_uid_from_filename(const char *filename); int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, int user_timeout); +int exchange_14b_prime_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, int user_timeout); int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card); +int select_card_14443b_prime(bool disconnect, iso14b_prime_card_select_t *card, bool verbose); int infoHF14B(bool verbose, bool do_aid_search); int readHF14B(bool loop, bool verbose, bool read_plot); diff --git a/client/src/cmdhfcalypso.c b/client/src/cmdhfcalypso.c index 053b4c3de..ef053ef9a 100644 --- a/client/src/cmdhfcalypso.c +++ b/client/src/cmdhfcalypso.c @@ -131,6 +131,8 @@ typedef struct { iso14a_card_select_t card_a; bool has_14b; iso14b_card_select_t card_b; + bool has_prime; + iso14b_prime_card_select_t card_prime; } calypso_rf_info_t; typedef struct { @@ -395,6 +397,9 @@ static const char *calypso_rf_protocol_desc(isodep_state_t protocol) { if (protocol == ISODEP_NFCA) { return "ISO14443-A"; } + if (protocol == ISODEP_NFCB_PRIME) { + return "ISO14443-B' / Innovatron"; + } if (protocol == ISODEP_NFCB) { return "ISO14443-B"; } @@ -430,6 +435,18 @@ static int calypso_connect_contactless(bool verbose, calypso_rf_info_t *rf) { return PM3_SUCCESS; } + iso14b_prime_card_select_t card_prime = {0}; + res = select_card_14443b_prime(false, &card_prime, verbose); + if (res == PM3_SUCCESS) { + if (rf != NULL) { + rf->present = true; + rf->protocol = ISODEP_NFCB_PRIME; + rf->has_prime = true; + rf->card_prime = card_prime; + } + return PM3_SUCCESS; + } + return res; } @@ -2169,6 +2186,10 @@ static void calypso_print_rf_info(const calypso_rf_info_t *rf) { } else if (rf->has_14b) { calypso_print_rf_hex_line(" PUPI : ", rf->card_b.uid, rf->card_b.uidlen); calypso_print_rf_hex_line(" ATQB : ", rf->card_b.atqb, sizeof(rf->card_b.atqb)); + } else if (rf->has_prime) { + PrintAndLogEx(SUCCESS, " V&T Ad : " _GREEN_("%02X"), rf->card_prime.vt_addr); + calypso_print_rf_hex_line(" DIV : ", rf->card_prime.div, sizeof(rf->card_prime.div)); + calypso_print_rf_hex_line(" ATR : ", rf->card_prime.atr, rf->card_prime.atr_len); } } @@ -4494,7 +4515,8 @@ static int calypso_probe_setup(const calypso_select_result_t *selected, bool ver uint8_t select_response[APDU_RES_LEN] = {0}; size_t select_response_len = 0; uint16_t select_sw = 0; - int res = Iso7816Select(CC_CONTACTLESS, false, true, aid, aid_len, select_response, sizeof(select_response), &select_response_len, &select_sw); + sAPDU_t apdu = {0x00, ISO7816_SELECT_FILE, 0x04, 0x00, (uint8_t)aid_len, aid}; + int res = Iso7816ExchangeEx(CC_CONTACTLESS, false, true, apdu, true, 0, select_response, sizeof(select_response), &select_response_len, &select_sw); if (res != PM3_SUCCESS) { return res; } diff --git a/client/src/iso7816/iso7816core.c b/client/src/iso7816/iso7816core.c index 9eafaa599..6699a2d2b 100644 --- a/client/src/iso7816/iso7816core.c +++ b/client/src/iso7816/iso7816core.c @@ -44,10 +44,11 @@ static isodep_state_t isodep_state = ISODEP_INACTIVE; void SetISODEPState(isodep_state_t state) { isodep_state = state; if (APDULogging) { - PrintAndLogEx(SUCCESS, "Setting ISODEP -> %s%s%s%s" + PrintAndLogEx(SUCCESS, "Setting ISODEP -> %s%s%s%s%s" , isodep_state == ISODEP_INACTIVE ? "inactive" : "" , isodep_state == ISODEP_NFCA ? _GREEN_("NFC-A") : "" , isodep_state == ISODEP_NFCB ? _GREEN_("NFC-B") : "" + , isodep_state == ISODEP_NFCB_PRIME ? _GREEN_("NFC-B'") : "" , isodep_state == ISODEP_NFCV ? _GREEN_("NFC-V") : "" ); } @@ -125,6 +126,9 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool l case ISODEP_NFCB: res = exchange_14b_apdu(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len, 4000); break; + case ISODEP_NFCB_PRIME: + res = exchange_14b_prime_apdu(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len, 4000); + break; case ISODEP_NFCV: PrintAndLogEx(INFO, " To be implemented, feel free to contribute!"); break; diff --git a/client/src/iso7816/iso7816core.h b/client/src/iso7816/iso7816core.h index b24be2dab..c209ac9ae 100644 --- a/client/src/iso7816/iso7816core.h +++ b/client/src/iso7816/iso7816core.h @@ -31,6 +31,7 @@ typedef enum { ISODEP_NFCA, ISODEP_NFCB, ISODEP_NFCV, + ISODEP_NFCB_PRIME, } isodep_state_t; typedef enum { diff --git a/client/src/scripting.c b/client/src/scripting.c index f995b6c35..bce0f1149 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -1286,6 +1286,9 @@ static int l_set_iso_dep_state(lua_State *L) { case 3: SetISODEPState(ISODEP_NFCV); break; + case 4: + SetISODEPState(ISODEP_NFCB_PRIME); + break; default: return returnToLuaWithError(L, "Wrong ISODEP STATE value"); } diff --git a/include/protocols.h b/include/protocols.h index e95a9d606..0f343c5d8 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -362,6 +362,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define ISO14443B_PRIME_CMD_ATTRIB 0x0F // APGEN parameter requesting the extended REPGEN response; also called 'APGEN!' #define ISO14443B_PRIME_REQUEST_EXTENDED_REPGEN 0x80 +#define ISO14443B_PRIME_COM_RA_START 0x02 // XEROX Commands #define ISO14443B_XEROX_PWD 0x38