diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index bb5bfbd47..04372b10e 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -607,7 +607,8 @@ void Demod14aInit(uint8_t *d, uint16_t n, uint8_t *par) { } // use parameter non_real_time to provide a timestamp. Set to 0 if the decoder should measure real time -RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time) { +static RAMFUNC int ManchesterDecodingEx(uint8_t bit, uint16_t offset, uint32_t non_real_time, bool no_parity) { + const uint8_t frame_bits = no_parity ? 8 : 9; if (Demod.len == Demod.output_len) { // Flush last parity bits @@ -653,8 +654,8 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t } // modulation in first half only - Sequence D = 1 Demod.bitCount++; Demod.shiftReg = (Demod.shiftReg >> 1) | 0x100; // in both cases, add a 1 to the shiftreg - if (Demod.bitCount == 9) { // if we decoded a full byte (including parity) - Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + if (Demod.bitCount == frame_bits) { // if we decoded a full byte (including parity) + Demod.output[Demod.len++] = ((Demod.shiftReg >> (no_parity ? 1 : 0)) & 0xff); Demod.parityBits <<= 1; // make room for the parity bit Demod.parityBits |= ((Demod.shiftReg >> 8) & 0x01); // store parity bit Demod.bitCount = 0; @@ -664,13 +665,13 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t Demod.parityBits = 0; } } - Demod.endTime = Demod.startTime + 8 * (9 * Demod.len + Demod.bitCount + 1) - 4; + Demod.endTime = Demod.startTime + 8 * (frame_bits * Demod.len + Demod.bitCount + 1) - 4; } else { // no modulation in first half if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // and modulation in second half = Sequence E = 0 Demod.bitCount++; Demod.shiftReg = (Demod.shiftReg >> 1); // add a 0 to the shiftreg - if (Demod.bitCount >= 9) { // if we decoded a full byte (including parity) - Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + if (Demod.bitCount >= frame_bits) { // if we decoded a full byte (including parity) + Demod.output[Demod.len++] = ((Demod.shiftReg >> (no_parity ? 1 : 0)) & 0xff); Demod.parityBits <<= 1; // make room for the new parity bit Demod.parityBits |= ((Demod.shiftReg >> 8) & 0x01); // store parity bit Demod.bitCount = 0; @@ -680,7 +681,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t Demod.parityBits = 0; } } - Demod.endTime = Demod.startTime + 8 * (9 * Demod.len + Demod.bitCount + 1); + Demod.endTime = Demod.startTime + 8 * (frame_bits * Demod.len + Demod.bitCount + 1); } else { // no modulation in both halves - End of communication if (Demod.bitCount > 0) { // there are some remaining data bits @@ -707,6 +708,10 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t } +RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time) { + return ManchesterDecodingEx(bit, offset, non_real_time, false); +} + // Thinfilm, Kovio mangles ISO14443A in the way that they don't use start bit nor parity bits. static int ManchesterDecoding_Thinfilm(uint8_t bit) { @@ -3107,7 +3112,7 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint16_t rec_ // If a response is captured return TRUE // If it takes too long return FALSE //----------------------------------------------------------------------------- -static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t rec_maxlen, uint8_t *receivedResponsePar, uint16_t offset) { +static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t rec_maxlen, uint8_t *receivedResponsePar, uint16_t offset, bool no_parity) { if (g_hf_field_active == false) { Dbprintf("Warning: HF field is off"); return false; @@ -3134,7 +3139,7 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t rec_max if (FPGA_SSC_RX_Ready()) { b = (uint8_t)FPGA_SSC_RX_Value(); - if (ManchesterDecoding(b, offset, 0)) { + if (ManchesterDecodingEx(b, offset, 0, no_parity)) { NextTransferTime = MAX(NextTransferTime, Demod.endTime - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / 16 + FRAME_DELAY_TIME_PICC_TO_PCD); return true; } else if (c++ > timeout && Demod.state == DEMOD_14A_UNSYNCD) { @@ -3177,22 +3182,26 @@ void ReaderTransmit(const uint8_t *frame, uint16_t len, uint32_t *timing) { } static uint16_t ReaderReceiveOffset(uint8_t *receivedAnswer, uint16_t answer_len, uint16_t offset, uint8_t *par) { - if (GetIso14443aAnswerFromTag(receivedAnswer, answer_len, par, offset) == false) { + if (GetIso14443aAnswerFromTag(receivedAnswer, answer_len, par, offset, false) == false) { return 0; } LogTrace(receivedAnswer, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, par, false); return Demod.len; } -uint16_t ReaderReceive(uint8_t *receivedAnswer, uint16_t answer_maxlen, uint8_t *par) { - if (GetIso14443aAnswerFromTag(receivedAnswer, answer_maxlen, par, 0) == false) { +static uint16_t ReaderReceiveEx(uint8_t *receivedAnswer, uint16_t answer_maxlen, uint8_t *par, bool no_parity) { + if (GetIso14443aAnswerFromTag(receivedAnswer, answer_maxlen, par, 0, no_parity) == false) { return 0; } - LogTrace(receivedAnswer, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, par, false); + LogTrace(receivedAnswer, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, no_parity ? NULL : par, false); return Demod.len; } +uint16_t ReaderReceive(uint8_t *receivedAnswer, uint16_t answer_maxlen, uint8_t *par) { + return ReaderReceiveEx(receivedAnswer, answer_maxlen, par, false); +} + // This function misstreats the ISO 14443a anticollision procedure. // by fooling the reader there is a collision and forceing the reader to // increase the uid bytes. The might be an overflow, DoS will occur. @@ -4033,7 +4042,9 @@ void ReaderIso14443a(PacketCommandNG *c) { lenbits = len * 8; } - if (lenbits > 0) { + if (param & ISO14A_NO_PARITY) { + ReaderTransmitBitsPar(cmd, lenbits ? lenbits : len * 8, NULL, NULL); + } else if (lenbits > 0) { // want to send a specific number of bits (e.g. short commands) @@ -4110,7 +4121,7 @@ void ReaderIso14443a(PacketCommandNG *c) { FpgaDisableTracing(); reply_iso14a_raw(response, respbuf, 0); } else { - arg0 = ReaderReceive(buf, ISO14A_RESP_MAXLEN, parity_array); + arg0 = ReaderReceiveEx(buf, ISO14A_RESP_MAXLEN, parity_array, (param & ISO14A_NO_PARITY) != 0); if ((param & ISO14A_CRYPTO1MODE) == ISO14A_CRYPTO1MODE) { mf_crypto1_decrypt(&crypto1_state, buf, arg0); diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index f798cbaba..808f39d2c 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -733,6 +733,101 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { return PM3_SUCCESS; } +// Annex C uses CRC-B and continuous frames without parity. +static int hf14a_timeslot_exchange(const uint8_t *data, uint16_t len, bool request, + PacketResponseNG *resp, uint16_t *rlen) { + uint8_t frame[11]; + if (len > sizeof(frame) - 2) { + return PM3_EINVARG; + } + memcpy(frame, data, len); + uint32_t flags = ISO14A_RAW | ISO14A_NO_PARITY | ISO14A_NO_DISCONNECT | + ISO14A_SET_TIMEOUT | ISO14A_SET_WAIT_US; + if (request) { + flags |= ISO14A_CONNECT | ISO14A_NO_SELECT; + } else { + compute_crc(CRC_14443_B, frame, len, &frame[len], &frame[len + 1]); + len += 2; + } + + clearCommandBuffer(); + // Timeouts in 128/fc units; inter-frame delay in microseconds. + SendIso14aReaderEx(flags, frame, len, len, request ? 7 : 0, + request ? 79 : 547, request ? 0 : 1100); + if (WaitForIso14aReply(resp, 1500, rlen, NULL) == false || *rlen == 0) { + return PM3_ETIMEOUT; + } + if (*rlen > resp->length) { + return PM3_ESOFT; + } + if (request == false) { + if (*rlen < 3 || check_crc(CRC_14443_B, resp->data.asBytes, *rlen) == false) { + return PM3_ECRC; + } + *rlen -= 2; + } + return PM3_SUCCESS; +} + +static int hf14a_timeslot_select(bool disconnect_after, bool verbose, bool print_info) { + const uint8_t reqa_t[] = {0x35}; + const uint8_t req_id[] = {0x08, 0x44, 0x00}; + uint8_t sel_t[9] = {0x40}; // CID_t 0 + PacketResponseNG resp; + uint16_t len = 0; + const char *stage = "REQA_t"; + DropField(); + int res = hf14a_timeslot_exchange(reqa_t, sizeof(reqa_t), true, &resp, &len); + if (res != PM3_SUCCESS) { + goto out; + } + uint8_t atqa = resp.data.asBytes[0]; + + stage = "REQ-ID"; + res = hf14a_timeslot_exchange(req_id, sizeof(req_id), false, &resp, &len); + if (res != PM3_SUCCESS) { + goto out; + } + if (len != 9 || resp.data.asBytes[0] != 0x06) { + res = PM3_ESOFT; + goto out; + } + memcpy(sel_t + 1, resp.data.asBytes + 1, 8); + + stage = "SEL_t"; + res = hf14a_timeslot_exchange(sel_t, sizeof(sel_t), false, &resp, &len); + if (res != PM3_SUCCESS) { + goto out; + } + if (len > 33 || (len > 1 && resp.data.asBytes[0] != 0x3b)) { + res = PM3_ESOFT; + goto out; + } + if (print_info) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------- " _CYAN_("ISO14443-A Timeslot Information") " ----------"); + } + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(sel_t + 1, 8)); + if (verbose) { + PrintAndLogEx(SUCCESS, "ATQA_T: " _GREEN_("%02X"), atqa); + if (len == 1) { + PrintAndLogEx(SUCCESS, " SAK_T: " _GREEN_("%02X"), resp.data.asBytes[0]); + } else { + // ProxIC 13.8.1: ATR with CRC-B in place of the contact ATR checksum. + PrintAndLogEx(SUCCESS, " ATR: " _GREEN_("%s"), sprint_hex(resp.data.asBytes, len)); + } + PrintAndLogEx(NORMAL, ""); + } +out: + if (disconnect_after || res != PM3_SUCCESS) { + DropField(); + } + if (res != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "Timeslot %s failed (%d)", stage, res); + } + return res; +} + static int CmdHF14AReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a reader", @@ -815,8 +910,8 @@ static int CmdHF14AReader(const char *Cmd) { found = (select_status != 0); if (select_status == 0) { - DropField(); - res = PM3_ESOFT; + res = hf14a_timeslot_select(disconnectAfter, !(silent && continuous), false); + found = (res == PM3_SUCCESS); goto plot; } @@ -2836,8 +2931,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (select_status == 0) { PrintAndLogEx(DEBUG, "iso14443a card select failed"); - DropField(); - return select_status; + // Status 5 avoids the standard ISO14443-4 application probe in hf search. + return hf14a_timeslot_select(true, true, true) == PM3_SUCCESS ? 5 : 0; } PrintAndLogEx(NORMAL, ""); diff --git a/include/mifare.h b/include/mifare.h index 6e96d56cf..87deba8a8 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -110,6 +110,7 @@ typedef enum ISO14A_COMMAND { ISO14A_SET_WAIT_US = (1 << 15), ISO14A_APPEND_CMAC = (1 << 16), ISO14A_CLEARTRACE = (1 << 17), + ISO14A_NO_PARITY = (1 << 18), } iso14a_command_t; // CMD_HF_ISO14443A_READER payload. @@ -118,7 +119,7 @@ typedef enum ISO14A_COMMAND { // arg1 = (lenbits << 16) | len // arg2 = (wait_us << 32) | timeout typedef struct { - uint32_t flags; // iso14a_command_t bitmask, needs 18 bits today + uint32_t flags; // iso14a_command_t bitmask, needs 19 bits today uint32_t timeout; // in ETUs, only read when ISO14A_SET_TIMEOUT is set uint32_t wait_us; // only read when ISO14A_SET_WAIT_US is set uint16_t len; // bytes in data[]