diff --git a/armsrc/i2c.c b/armsrc/i2c.c index cd57934fa..d9bac2f7e 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -65,6 +65,13 @@ static bool s_proto_announced = false; // keep the T=0 they had before. static uint8_t s_pps_proto_cmd = 0; +#if SAM_SC_FORCE_T1_TA1_95 +// One-shot request used by the SAM secure-channel path. Keep it separate +// from generic SmartCardRaw PPS selection so the performance policy does not +// alter unrelated contact-card commands. +static bool s_sam_t1_profile_requested = false; +#endif + // 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 @@ -1143,6 +1150,12 @@ uint8_t sc_active_device_cmd(void) { return (s_pps_proto_cmd != 0) ? s_pps_proto_cmd : I2C_DEVICE_CMD_SEND_T0; } +void sc_request_sam_t1_profile(void) { +#if SAM_SC_FORCE_T1_TA1_95 + s_sam_t1_profile_requested = true; +#endif +} + 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; @@ -1217,6 +1230,15 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { s_card_protocols = atr_protocols(card_ptr->atr, card_ptr->atr_len); s_proto_announced = false; +#if SAM_SC_FORCE_T1_TA1_95 + const bool request_sam_t1 = s_sam_t1_profile_requested; + s_sam_t1_profile_requested = false; + if (request_sam_t1) { + // This reset starts a fresh SAM session. Do not restore an older + // cached T=0 PPS entry before the one PPS below selects T=1. + sc_pps_forget(); + } +#endif if (g_dbglevel >= DBG_INFO) { // What the ATR advertises, and which of them the card actually runs // until something negotiates otherwise. Saying only "offers T=0 T=1" @@ -1264,17 +1286,25 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { // once - a refusal is remembered so every later ATR does not retry it. if (s_pps.reapply && (s_pps.tried == false)) { + uint8_t want_proto = atr_first_proto(card_ptr->atr, card_ptr->atr_len); uint8_t want = sc_pps_best_ta1(card_ptr->atr, card_ptr->atr_len); +#if SAM_SC_FORCE_T1_TA1_95 + if (request_sam_t1 && (s_card_protocols & SC_PROTO_T1)) { + want_proto = 1; + want = SAM_SC_T1_TA1; + } +#endif + 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)) { + if (want && sc_pps(want_proto, want)) { s_pps.ta1 = want; - s_pps.proto = atr_first_proto(card_ptr->atr, card_ptr->atr_len); + s_pps.proto = want_proto; if (g_dbglevel >= DBG_INFO) { - Dbprintf("SC: negotiated TA1 %02X", want); + Dbprintf("SC: negotiated T=%u TA1 %02X", want_proto, want); } } } diff --git a/armsrc/i2c.h b/armsrc/i2c.h index c40f94653..828f95440 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -34,6 +34,15 @@ // LRC/CRC - the same way SEND_T0 handles the T=0 procedure bytes. #define I2C_DEVICE_CMD_SEND_T1 0x08 #define I2C_DEVICE_CMD_PPS 0x09 +// SIM module firmware v4.65 and up. A compatibility alias for T=0 in the +// current SIM_C build. Grace response assembly remains PM3-side. +#define I2C_DEVICE_CMD_SEND_T0_AUTORESP 0x0A + +// Disabled: live testing showed that opcode 0x0A itself is not reliable on the +// PM3<->SIM I2C path, even when the SIM handler is an exact SEND_T0 pass-through. +// Keep T=0 traffic on the established 0x07 transport until that low-level issue +// is understood. +#define SAM_T0_AUTORESP 0 // SIM module firmware versions this build knows about. // @@ -46,6 +55,13 @@ #define SIM_MODULE_VERS_T1_HI 4 #define SIM_MODULE_VERS_T1_LO 51 +// SAM secure-channel transport policy for the performance build. Artemis +// offers T=0 first in its ATR; request its advertised T=1 service explicitly +// and use the validated Fi=512/Di=16 rate. This is protocol selection only: +// it does not enable APDU dumps or other bring-up diagnostics. +#define SAM_SC_FORCE_T1_TA1_95 1 +#define SAM_SC_T1_TA1 0x95 + // The SIM module v4 supports up to 384 bytes for the length. #define ISO7816_MAX_FRAME 270 @@ -117,6 +133,11 @@ uint8_t sc_raw_device_cmd(smartcard_command_t flags); // otherwise. uint8_t sc_active_device_cmd(void); +// Request the configured T=1 profile for the next SAM-only GetATR(). The +// request is consumed once, so unrelated SmartCardRaw traffic still follows +// its own ATR/PPS policy. +void sc_request_sam_t1_profile(void); + // 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. diff --git a/armsrc/sam_common.c b/armsrc/sam_common.c index 93804dd27..309ccb655 100644 --- a/armsrc/sam_common.c +++ b/armsrc/sam_common.c @@ -204,7 +204,19 @@ int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) // Whatever protocol GetATR()/PPS left the card on, rather than an assumed // T=0. Resolved once per exchange so the GET RESPONSE round below cannot // end up on a different protocol than the command it belongs to. - const uint8_t dev_cmd = sc_active_device_cmd(); + const uint8_t active_cmd = sc_active_device_cmd(); + const bool t1 = (active_cmd == I2C_DEVICE_CMD_SEND_T1); + // Use the v4.65+ compatibility T=0 opcode when available. Grace responses + // still assemble their 61xx/9Fxx continuations below on the PM3: they may + // carry material response data before the continuation status. T=1 already + // returns its whole APDU response through the module's block layer. +#if SAM_T0_AUTORESP + const uint8_t dev_cmd = (active_cmd == I2C_DEVICE_CMD_SEND_T0) + ? I2C_DEVICE_CMD_SEND_T0_AUTORESP + : active_cmd; +#else + const uint8_t dev_cmd = active_cmd; +#endif bool res = I2C_BufferWrite(data, n, dev_cmd, I2C_DEVICE_ADDRESS_MAIN); if (res == false) { @@ -261,10 +273,21 @@ int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) *resplen = 0; } - uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len}; - sc_log_trace(cmd_getresp, sizeof(cmd_getresp), true); + // Grace T=1 exchanges use extended APDUs. The normal short T=0 GET + // RESPONSE is invalid on that path, so use an extended Le when a T=1 + // response explicitly asks for more data. + uint8_t cmd_getresp_t0[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len}; + uint16_t want = more_len ? more_len : 256; + uint8_t cmd_getresp_t1[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, + 0x00, (uint8_t)(want >> 8), (uint8_t)want}; + const uint8_t *cmd_getresp = t1 ? cmd_getresp_t1 : cmd_getresp_t0; + const uint16_t cmd_getresp_len = t1 ? sizeof(cmd_getresp_t1) : sizeof(cmd_getresp_t0); + sc_log_trace(cmd_getresp, cmd_getresp_len, true); - res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), dev_cmd, I2C_DEVICE_ADDRESS_MAIN); + // Keep response assembly at the PM3, and use the ordinary active protocol + // opcode for the continuation rather than recursively selecting the + // compatibility alias. + res = I2C_BufferWrite(cmd_getresp, cmd_getresp_len, active_cmd, I2C_DEVICE_ADDRESS_MAIN); if (res == false) { DbpString("failed to send to SIM CARD 2"); goto out; @@ -341,29 +364,52 @@ int sam_send_payload_ex( int res = PM3_SUCCESS; uint8_t *buf = response; + const uint16_t inner_len = (uint16_t)(SAM_TX_ASN1_PREFIX_LENGTH + *payload_len); + const bool t1 = (sc_active_device_cmd() == I2C_DEVICE_CMD_SEND_T1); + uint16_t payload_offset = SAM_TX_APDU_PREFIX_LENGTH; + + if ((uint32_t)inner_len + (t1 ? 9u : 5u) > ISO7816_MAX_FRAME) { + return PM3_EINVARG; + } + if (!t1 && inner_len > 0xff) { + return PM3_EINVARG; + } buf[0] = 0xA0; // CLA buf[1] = 0xDA; // INS (PUT DATA) buf[2] = 0x02; // P1 (TLV format?) buf[3] = 0x63; // P2 - buf[4] = SAM_TX_ASN1_PREFIX_LENGTH + (uint8_t) * payload_len; // LEN + if (t1) { + // The Artemis T=1 service accepts Grace in extended APDU form. This + // matches the working ACR39U exchange: 00 ... 0000. + buf[4] = 0x00; + buf[5] = (uint8_t)(inner_len >> 8); + buf[6] = (uint8_t)inner_len; + payload_offset = 7; + } else { + buf[4] = (uint8_t)inner_len; + } // Grace routing header: FROM, TO, REPLY-TO, 0x00, 0x00, scFlag - buf[5] = addr_src; - buf[6] = addr_dest; - buf[7] = addr_reply; + buf[payload_offset] = addr_src; + buf[payload_offset + 1] = addr_dest; + buf[payload_offset + 2] = addr_reply; - buf[8] = 0x00; - buf[9] = 0x00; - buf[10] = scFlag; + buf[payload_offset + 3] = 0x00; + buf[payload_offset + 4] = 0x00; + buf[payload_offset + 5] = scFlag; memcpy( - &buf[11], + &buf[payload_offset + SAM_TX_ASN1_PREFIX_LENGTH], payload, *payload_len ); - uint16_t length = SAM_TX_ASN1_PREFIX_LENGTH + SAM_TX_APDU_PREFIX_LENGTH + (uint8_t) * payload_len; + uint16_t length = (uint16_t)(payload_offset + inner_len); + if (t1) { + buf[length++] = 0x00; // extended Le = 65536 (maximum response) + buf[length++] = 0x00; + } sc_log_trace(buf, length, true); if (g_dbglevel >= DBG_INFO) { @@ -413,7 +459,7 @@ int sam_get_version(bool info) { }; uint16_t payload_len = sizeof(payload); - sam_send_payload( + int exchange = sam_send_payload( 0x44, 0x0a, 0x44, payload, &payload_len, @@ -421,6 +467,22 @@ int sam_get_version(bool info) { &response_len ); + if (exchange != PM3_SUCCESS) { + res = exchange; + goto out; + } + + // The Artemis T=1 endpoint accepts the extended GetVersion warmup with a + // bare 9000 (unlike the T=0 endpoint, it does not return the version TLV). + // It is only a link-settling ping here, so a successful status is enough; + // the following InitAuth exchange performs the actual authentication. + if ((sc_active_device_cmd() == I2C_DEVICE_CMD_SEND_T1) && + (response_len >= 2) && + (response[response_len - 2] == 0x90) && + (response[response_len - 1] == 0x00)) { + goto out; + } + // resp: // c1 64 00 00 00 // bd 11 <- SAM response diff --git a/armsrc/sam_sc.c b/armsrc/sam_sc.c index d73cea345..5ea4f22ab 100644 --- a/armsrc/sam_sc.c +++ b/armsrc/sam_sc.c @@ -522,6 +522,9 @@ void sam_sc_handler(const PacketCommandNG *c) { s_sam_sc_session_active = false; I2C_Reset_EnterMainProgram(); StartTicks(); +#if SAM_SC_FORCE_T1_TA1_95 + sc_request_sam_t1_profile(); +#endif smart_card_atr_t card; if (GetATR(&card, false) == false) { res = PM3_ECARDEXCHANGE; diff --git a/client/resources/sim023.bin b/client/resources/sim023.bin new file mode 100644 index 000000000..a49746fdf Binary files /dev/null and b/client/resources/sim023.bin differ diff --git a/client/resources/sim023.sha512.txt b/client/resources/sim023.sha512.txt new file mode 100644 index 000000000..09451cf3f --- /dev/null +++ b/client/resources/sim023.sha512.txt @@ -0,0 +1 @@ +200F13C2834E4096B56EDD0A1A5A59C7D60141818FEAA9F59821E1E1D074296904CDD2BECA6B408ABF21B36BF4DC20342C347F116F76CD957AF5EBD5AA5617E9 \ No newline at end of file