diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f28fbe9a..6114482c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added `sim020.bin` - v4.60 of sim module firmware, better T=0 handling and clock etu handling (@iceman1001) +- Fixed `hf seos sam` - now have a invalid pacs guard (@iceman1001) +- Changed i2c comms to auto-negotiation and use ATR-keyed rate cache for speedier smart card comms (@iceman1001) - Changed `emv search` - now has better table output (@iceman1001) - Changed client side polling. Should make client more spiffy (@iceman1001) - Fixed `trace list -t 7816` - now handles contact vs contactless annotation better (@iceman1001) diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 0a54b575c..6c9cf1c6e 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -53,6 +53,24 @@ static uint8_t s_card_protocols = 0; // sc_raw_device_cmd() runs per APDU, so report the choice once per card static bool s_proto_announced = false; +// A negotiated rate lives in two places that reset independently: the module's +// UART divisor, which any I2C_Reset_EnterMainProgram() wipes, and the card, +// which only an RST pulse clears. Left alone the two drift apart and every +// exchange fails until something resets the card. +// +// So remember what was negotiated, keyed by the ATR it was negotiated against, +// and put it back after each ATR - the one window where PPS is legal +// (ISO/IEC 7816-3 clause 9). A different card brings a different ATR and drops +// the entry. +static struct { + uint8_t atr[sizeof(((smart_card_atr_t *)0)->atr)]; // what it was negotiated against + uint8_t atr_len; + uint8_t ta1; // 0 = nothing negotiated + uint8_t proto; + bool reapply; // off while SmartCardPPS negotiates + bool tried; // already negotiated against this ATR +} s_pps = { {0}, 0, 0, 0, true, false }; + // try i2c bus recovery at 100kHz = 5us high, 5us low void I2C_recovery(void) { @@ -818,6 +836,21 @@ int I2C_get_version(uint8_t *major, uint8_t *minor) { } // Will read response from smart card module, retries 3 times to get the data. +static uint32_t s_trace_tick = 0; + +void sc_log_trace_reset(void) { + s_trace_tick = GetTicks(); +} + +void sc_log_trace(const uint8_t *d, uint16_t len, bool reader2tag) { + uint32_t now = GetTicks(); + if (s_trace_tick == 0) { + s_trace_tick = now; + } + LogTrace(d, len, s_trace_tick, now, NULL, reader2tag); + s_trace_tick = now; +} + bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen, uint32_t wait) { uint8_t i = 10; @@ -920,6 +953,107 @@ uint8_t sc_raw_device_cmd(smartcard_command_t flags) { return I2C_DEVICE_CMD_SEND; } +// The protocol of TD1, which is what the card runs if nothing is negotiated. +static uint8_t atr_first_proto(const uint8_t *atr, uint8_t len) { + if ((len < 2) || ((atr[1] & 0x80) == 0)) { + return 0; + } + uint8_t i = 2; + if (atr[1] & 0x10) i++; + if (atr[1] & 0x20) i++; + if (atr[1] & 0x40) i++; + return (i < len) ? (uint8_t)(atr[i] & 0x0F) : 0; +} + +// The fastest rate worth proposing to a card, or 0 for none. +// +// Two rules keep this safe, both learned on the bench rather than assumed: +// +// - Keep the Fi the card advertised and only lower Di. Proposing a different +// Fi is refused: a SAM advertising Fi=512 took the whole Fi=512 family and +// rejected every Fi=768/1024/1536/2048 offer. +// - R = Fi / (16 * Di) is the module's UART reload. It has to be a whole +// number or the sampling point drifts - that is the +3.2% which makes +// Fi=372 unusable beyond Di=1 - and it must not fall below the floor: R=8 +// (31250 bit/s) transfers cleanly here, R=4 does not. +// +// A card with no TA1 offers nothing but the default, so nothing is proposed. +#define SC_PPS_MIN_RELOAD 8 + +static uint8_t sc_pps_best_ta1(const uint8_t *atr, uint8_t len) { + + static const uint16_t fi_tab[16] = {372, 372, 558, 744, 1116, 1488, 1860, 0, + 0, 512, 768, 1024, 1536, 2048, 0, 0 + }; + static const uint8_t di_tab[16] = {0, 1, 2, 4, 8, 16, 32, 64, 12, 20, 0, 0, 0, 0, 0, 0}; + + if ((len < 3) || ((atr[1] & 0x10) == 0)) { + return 0; // no TA1 - default only + } + + uint8_t fi_idx = (uint8_t)((atr[2] >> 4) & 0x0F); + uint16_t f = fi_tab[fi_idx]; + if (f == 0) { + return 0; // RFU + } + + uint8_t best = 0; + uint16_t best_clocks = 372; // has to beat the default to be worth it + + for (uint8_t di_idx = 1; di_idx < 16; di_idx++) { + + uint8_t d = di_tab[di_idx]; + if (d == 0) { + continue; + } + if ((f % (uint16_t)(16u * d)) != 0) { + continue; // divisor is not exact, the etu would drift + } + if ((f / (uint16_t)(16u * d)) < SC_PPS_MIN_RELOAD) { + continue; // faster than the module can receive + } + + uint16_t clocks = (uint16_t)(f / d); + if (clocks >= best_clocks) { + continue; + } + best_clocks = clocks; + best = (uint8_t)((fi_idx << 4) | di_idx); + } + + return best; +} + +static bool sc_pps(uint8_t proto, uint8_t ta1) { + uint8_t req[2] = { (uint8_t)(proto & 0x0F), ta1 }; + if (I2C_BufferWrite(req, sizeof(req), I2C_DEVICE_CMD_PPS, I2C_DEVICE_ADDRESS_MAIN) == false) { + return false; + } + uint8_t resp[8] = {0}; + uint16_t len = sizeof(resp); + if ((sc_rx_bytes(resp, &len, SIM_WAIT_DELAY) == false) || (len < 3)) { + return false; + } + // resp is [ok][active protocol][ta1 in force] + return ((resp[0] == 1) && (resp[2] == ta1)); +} + +void sc_pps_remember(const uint8_t *atr, uint8_t atr_len, uint8_t proto, uint8_t ta1) { + if ((atr_len == 0) || (atr_len > sizeof(s_pps.atr))) { + return; + } + memcpy(s_pps.atr, atr, atr_len); + s_pps.atr_len = atr_len; + s_pps.ta1 = ta1; + s_pps.proto = proto; +} + +void sc_pps_forget(void) { + s_pps.atr_len = 0; + s_pps.ta1 = 0; + s_pps.tried = false; +} + bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { if (card_ptr == NULL) { @@ -985,7 +1119,55 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { } if (verbose) { - LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); + sc_log_trace(card_ptr->atr, card_ptr->atr_len, false); + } + + // Same card as the one a rate was negotiated for? Put it back. This is the + // only moment a PPS is legal, and the module has just come up at the + // default, so card and module move together. + if (s_pps.reapply && s_pps.ta1 && (s_pps.atr_len == card_ptr->atr_len) && + (memcmp(s_pps.atr, card_ptr->atr, s_pps.atr_len) == 0)) { + + if (sc_pps(s_pps.proto, s_pps.ta1)) { + if (g_dbglevel >= DBG_INFO) { + Dbprintf("SC: rate restored, TA1 %02X", s_pps.ta1); + } + } else { + // Refused or lost: the card stays at the default per 9.1, so drop + // the entry rather than keep failing on every ATR from now on. + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("SC: could not restore TA1 %02X, back to the default", s_pps.ta1); + } + sc_pps_forget(); + } + + } else { + + bool same_card = (s_pps.atr_len == card_ptr->atr_len) && + (memcmp(s_pps.atr, card_ptr->atr, s_pps.atr_len) == 0); + + if (same_card == false) { + sc_pps_forget(); // different card, start over + } + + // First sight of this card: ask for the best rate its ATR allows. Only + // once - a refusal is remembered so every later ATR does not retry it. + if (s_pps.reapply && (s_pps.tried == false)) { + + uint8_t want = sc_pps_best_ta1(card_ptr->atr, card_ptr->atr_len); + + memcpy(s_pps.atr, card_ptr->atr, card_ptr->atr_len); + s_pps.atr_len = card_ptr->atr_len; + s_pps.tried = true; + + if (want && sc_pps(atr_first_proto(card_ptr->atr, card_ptr->atr_len), want)) { + s_pps.ta1 = want; + s_pps.proto = atr_first_proto(card_ptr->atr, card_ptr->atr_len); + if (g_dbglevel >= DBG_INFO) { + Dbprintf("SC: negotiated TA1 %02X", want); + } + } + } } return true; @@ -1057,7 +1239,7 @@ void SmartCardRaw(const smart_card_raw_t *p) { wait = I2C_ITERS_FOR_MS(ms); } - LogTrace(p->data, p->len, 0, 0, NULL, true); + sc_log_trace(p->data, p->len, true); bool res = I2C_BufferWrite( p->data, @@ -1078,7 +1260,7 @@ void SmartCardRaw(const smart_card_raw_t *p) { len = ISO7816_MAX_FRAME; res = sc_rx_bytes(resp, &len, wait); if (res) { - LogTrace(resp, len, 0, 0, NULL, false); + sc_log_trace(resp, len, false); } else { len = 0; } @@ -1193,7 +1375,10 @@ void SmartCardPPS(const smart_card_pps_t *p) { I2C_Reset_EnterMainProgram(); smart_card_atr_t card; - if (GetATR(&card, true) == false) { + s_pps.reapply = false; // this call is the negotiation + bool got_atr = GetATR(&card, true); + s_pps.reapply = true; + if (got_atr == false) { reply_ng(CMD_SMART_PPS, PM3_ETIMEOUT, NULL, 0); goto out; } @@ -1221,6 +1406,17 @@ void SmartCardPPS(const smart_card_pps_t *p) { goto out; } + // resp is [ok][active protocol][ta1 in force]. Remember a rate that is + // actually faster than the default so later ATRs can put it back; 0x11 is + // the default and means "forget what we had". + if (resp[0] == 1) { + if (resp[2] != 0x11) { + sc_pps_remember(card.atr, card.atr_len, resp[1], resp[2]); + } else { + sc_pps_forget(); + } + } + reply_ng(CMD_SMART_PPS, PM3_SUCCESS, resp, 3); out: diff --git a/armsrc/i2c.h b/armsrc/i2c.h index 30d320704..0795eba8e 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -66,7 +66,7 @@ // these were simply scaled down together. The others are padding - hold after // SCL falls (spec 0.3 us) and SCL high width (spec 4 us) - so they get the // standard mode minimum with margin instead of a full clock. -#define I2C_DELAY_SDA_US I2C_DELAY_1CLK_US +#define I2C_DELAY_SDA_US 15 #define I2C_DELAY_HOLD_US 2 #define I2C_DELAY_HIGH_US 6 @@ -112,6 +112,12 @@ bool I2C_WriteFW(const uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uin // Which SIM module opcode a set of SC_RAW* flags asks for. uint8_t sc_raw_device_cmd(smartcard_command_t flags); +// Log one smartcard frame, timestamped from the tick counter. Start is where +// the previous frame ended, so a Tag frame's span is how long the card took to +// answer and a Rdr frame's is how long the host took to ask. +void sc_log_trace(const uint8_t *d, uint16_t len, bool reader2tag); +void sc_log_trace_reset(void); + bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen, uint32_t wait); // bool GetATR(smart_card_atr_t *card_ptr, bool verbose); diff --git a/armsrc/sam_common.c b/armsrc/sam_common.c index 8afbc64f7..871d75732 100644 --- a/armsrc/sam_common.c +++ b/armsrc/sam_common.c @@ -79,7 +79,19 @@ uint16_t sam_bd_offset(const uint8_t *response, uint16_t response_len) { // what tells a real response node apart from a 0xBD that happens to sit // in the routing tail - without it a SAM whose scFlag were 0xBD would // resolve to the wrong offset. - if ((uint16_t)(ofs + 2 + response[ofs + 1] + 2) == response_len) { + // Length is short form, or long form with one length byte (0x81 ), + // which is what an SNMP shaped reply over 127 bytes uses. + uint16_t hdr = 2; + uint16_t node_len = response[ofs + 1]; + if (node_len == 0x81) { + if ((uint16_t)(ofs + 2) >= response_len) { + continue; + } + hdr = 3; + node_len = response[ofs + 2]; + } + + if ((uint16_t)(ofs + hdr + node_len + 2) == response_len) { return ofs; } if (fallback == 0) { @@ -168,9 +180,10 @@ uint16_t sam_response_payload(const uint8_t *rx, uint16_t rx_len, uint16_t *payl *payload_len = 0; - // SNMP shaped reply carries a long form length: bd 81 8a 81 ... - if (((uint16_t)(ofs + 4) < rx_len) && - (rx[ofs + 1] == 0x81) && (rx[ofs + 3] == 0x8a) && (rx[ofs + 4] == 0x81)) { + // An SNMP shaped reply over 127 bytes carries a long form length, + // bd 81 . Read it from the length byte itself rather than matching one + // known inner tag, or any other node in that form parses as 0x81 + 2. + if (((uint16_t)(ofs + 2) < rx_len) && (rx[ofs + 1] == 0x81)) { *payload_len = (uint16_t)(rx[ofs + 2] + 3); @@ -230,7 +243,7 @@ int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) // The GET RESPONSE round below is ours, not the caller's, so log both // halves of it. Without this the trace shows a case 3 command coming // back with a full data answer, which T=0 cannot do. - LogTrace(resp, *resplen, 0, 0, NULL, false); + sc_log_trace(resp, *resplen, false); } else { // we done, return goto out; @@ -244,7 +257,7 @@ int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) } uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len}; - LogTrace(cmd_getresp, sizeof(cmd_getresp), 0, 0, NULL, true); + sc_log_trace(cmd_getresp, sizeof(cmd_getresp), true); res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); if (res == false) { @@ -347,7 +360,7 @@ int sam_send_payload_ex( uint16_t length = SAM_TX_ASN1_PREFIX_LENGTH + SAM_TX_APDU_PREFIX_LENGTH + (uint8_t) * payload_len; - LogTrace(buf, length, 0, 0, NULL, true); + sc_log_trace(buf, length, true); if (g_dbglevel >= DBG_INFO) { DbpString("SAM REQUEST APDU: "); Dbhexdump(length, buf, false); @@ -360,7 +373,7 @@ int sam_send_payload_ex( goto out; } - LogTrace(response, *response_len, 0, 0, NULL, false); + sc_log_trace(response, *response_len, false); if (g_dbglevel >= DBG_INFO) { DbpString("SAM RESPONSE APDU: "); Dbhexdump(*response_len, response, false); diff --git a/client/resources/sim020.bin b/client/resources/sim020.bin new file mode 100644 index 000000000..ba1ae6366 Binary files /dev/null and b/client/resources/sim020.bin differ diff --git a/client/resources/sim020.sha512.txt b/client/resources/sim020.sha512.txt new file mode 100644 index 000000000..48ae6dfe8 --- /dev/null +++ b/client/resources/sim020.sha512.txt @@ -0,0 +1 @@ +41a0ec1cdd0036e44b28b3462f3de64f04bbf6d7456c7e969490fe6c8c6b5c8feb5746244ba8100d4d223760431cb6d9da5ffe824c1dcd7ef096869f8c0d93ba *client/resources/sim020.bin diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index 104085a86..1abd096c5 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -2370,6 +2370,21 @@ static int CmdHfSeosSAM(const char *Cmd) { // 07 } else if (d[0] == 0xbd && d[2] == 0xb3 && d[4] == 0xa0) { const uint8_t *pacs = d + 6; + // The a0 content element normally starts with 80 . + // Some SAMs / cards return a status-only element with no access-bits + // field, e.g. a0 03 82 01 03 - here the first inner tag is 82, not 80. + // Don't parse the status byte as PACS (that yields a bogus + // "Invalid PACS value"); report that the card has no readable PACS. + if (pacs[0] != 0x80) { + PrintAndLogEx(WARNING, "No PACS/SIO access-bits returned by the SAM"); + if (pacs[0] == 0x82 && pacs[1] == 0x01) { + PrintAndLogEx(INFO, "SAM content status: " _YELLOW_("0x%02X") " (no physicalAccessBits field)", pacs[2]); + } + if (verbose) { + print_hex(d, resp.length); + } + return PM3_ENOPACS; + } const uint8_t pacs_length = pacs[1]; const uint8_t *pacs_data = pacs + 2; int res = HIDDumpPACSBits(pacs_data, pacs_length, verbose); diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index 5b5bd2634..388e475d2 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -622,7 +622,7 @@ static int CmdSmartUpgrade(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "smart upgrade", "Upgrade RDV4 sim module firmware", - "smart upgrade -f sim017.bin" + "smart upgrade -f sim020.bin" ); void *argtable[] = { @@ -927,7 +927,14 @@ static int CmdSmartPPS(const char *Cmd) { "\n" "Note `smart raw -1` already switches a card to T=1 by itself when\n" "the ATR offers it; this is for negotiating Fi/Di explicitly.\n" - "Needs SIM module firmware v4.51 or newer.", + "Needs SIM module firmware v4.51 or newer.\n" + "\n" + "The negotiated rate only holds for the rest of this session, and\n" + "only for commands that do not reset the card. Connecting a client\n" + "reads the module version, which reboots the module back to the\n" + "default rate while the card stays at the negotiated one - the next\n" + "command then fails until something resets the card. `smart info`,\n" + "or any `smart raw -s`, puts both back to the default.", "smart pps -1 -> select T=1\n" "smart pps -0 -> select T=0\n" "smart pps -1 --ta1 96 -> select T=1 and F=512 / D=32" diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 9e23d0f66..02f8f82ed 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -1519,7 +1519,7 @@ int CmdTraceList(const char *Cmd) { } if (protocol == ISO_7816_4) - PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings n/a"); + PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings in ticks (1/1.5MHz == 0.67us)"); if (protocol == PROTO_CALYPSO) PrintAndLogEx(INFO, _YELLOW_("Calypso") " - Timings n/a"); diff --git a/client/src/comms.c b/client/src/comms.c index a4052471e..1db471d24 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -80,6 +80,30 @@ static uint64_t last_packet_time; static bool dl_it(uint8_t *dest, uint32_t bytes, PacketResponseNG *response, size_t ms_timeout, bool show_warning, uint32_t rec_cmd); +// Wait until the comm thread has actually put a queued command on the wire. +// Callers used to sleep a fixed guess instead, which was reasonable when a +// command could sit in the buffer for a whole receive timeout; it is now sent +// within a millisecond or so. +bool WaitForTxIdle(uint32_t ms_timeout) { + + uint64_t start = msclock(); + + for (;;) { + + pthread_mutex_lock(&txBufferMutex); + bool pending = txBuffer_pending; + pthread_mutex_unlock(&txBufferMutex); + + if (pending == false) { + return true; + } + if (msclock() - start >= ms_timeout) { + return false; + } + msleep(1); + } +} + // Simple alias to track usages linked to the Bootloader, these commands must not be migrated. // - commands sent to enter bootloader mode as we might have to talk to old firmwares // - commands sent to the bootloader as it only supports OLD frames (which will always be the case for old BL) diff --git a/client/src/comms.h b/client/src/comms.h index 029e36cfa..0e825f556 100644 --- a/client/src/comms.h +++ b/client/src/comms.h @@ -113,6 +113,7 @@ void StartReconnectProxmark(void); size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, bool show_process, bool keep_raw_mode); bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout, bool show_warning); bool WaitForResponseTimeout(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout); +bool WaitForTxIdle(uint32_t ms_timeout); bool WaitForResponse(uint32_t cmd, PacketResponseNG *response); int SetHfFieldTimeout(uint32_t timeout_sec, bool quiet); diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index bbc65ed4e..7d91cdbb2 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -687,7 +687,7 @@ check_script: if (g_session.pm3_present) { clearCommandBuffer(); SendCommandNG(CMD_QUIT_SESSION, NULL, 0); - msleep(100); // Make sure command is sent before killing client + WaitForTxIdle(100); // make sure it really went out before killing the client } while (current_cmdscriptfile()) {