diff --git a/armsrc/sam_common.c b/armsrc/sam_common.c index 307af7402..2bfb06fe8 100644 --- a/armsrc/sam_common.c +++ b/armsrc/sam_common.c @@ -105,6 +105,60 @@ uint16_t sam_bd_offset(const uint8_t *response, uint16_t response_len) { * that offset, clamped to what actually arrived, or 0 if the frame is too short * to hold anything. */ +// How many bytes of routing tail this SAM puts in front of the ASN.1 payload. +// A Grace SAM uses 6 where SAM_RX_ASN1_PREFIX_LENGTH says 5, so pick the one +// whose node length accounts for the frame exactly: tag, length, contents, +// SW1 SW2. +uint16_t sam_rx_prefix_len(const uint8_t *rx, uint16_t rx_len) { + + uint16_t fallback = 0; + + for (uint16_t ofs = SAM_RX_ASN1_PREFIX_LENGTH; + ofs <= (uint16_t)(SAM_RX_ASN1_PREFIX_LENGTH + 1); + ofs++) { + + if ((uint16_t)(ofs + 1) >= rx_len) { + break; + } + if ((rx[ofs] != 0xa1) && (rx[ofs] != 0xbd)) { + continue; + } + if ((uint16_t)(ofs + 2 + rx[ofs + 1] + 2) == rx_len) { + return ofs; + } + if (fallback == 0) { + fallback = ofs; + } + } + + return (fallback != 0) ? fallback : (uint16_t)SAM_RX_ASN1_PREFIX_LENGTH; +} + +// The SAM asks for a card exchange with an a1 node holding an 80 APDU. +// Older SAMs flagged it with 0x61 in the routing tail, which is where the +// fixed sam_rx_buf[1] test came from - a Grace SAM puts 0x14 there instead, so +// key off the ASN.1 node, which both generations agree on. +bool sam_relay_pending(const uint8_t *rx, uint16_t rx_len) { + + uint16_t p = sam_rx_prefix_len(rx, rx_len); + if ((uint16_t)(p + 4) >= rx_len) { + return false; + } + return ((rx[p] == 0xa1) && (rx[p + 2] == 0xa1) && (rx[p + 4] == 0x80)); +} + +// The tag <-> SAM relay ends on an a1 02 82 00 node. The routing tail is 5 or +// 6 bytes depending on the SAM - the same reason sam_bd_offset() searches - so +// anchor on the node rather than indexing a fixed offset 7. +bool sam_relay_complete(const uint8_t *rx, uint16_t rx_len) { + + uint16_t ofs = sam_rx_prefix_len(rx, rx_len); + if ((uint16_t)(ofs + 2) >= rx_len) { + return false; + } + return ((rx[ofs] == 0xa1) && (rx[ofs + 2] == 0x82)); +} + uint16_t sam_response_payload(const uint8_t *rx, uint16_t rx_len, uint16_t *payload_len) { uint16_t ofs = sam_bd_offset(rx, rx_len); @@ -609,7 +663,7 @@ int sam_relay_iso15_loop( // Nothing to relay - the SAM answered directly (final response already in // sam_rx_buf). This is the normal case for SAM-internal commands. - if (sam_rx_buf[1] != 0x61) { + if (sam_relay_pending(sam_rx_buf, *sam_rx_len) == false) { return PM3_SUCCESS; } @@ -624,11 +678,11 @@ int sam_relay_iso15_loop( switch_clock_to_countsspclk(); // tag <-> SAM exchange starts here - while (sam_rx_buf[1] == 0x61) { + while (sam_relay_pending(sam_rx_buf, *sam_rx_len)) { uint32_t start_time = GetCountSspClk(); uint32_t eof_time = start_time + DELAY_ICLASS_VICC_TO_VCD_READER; - nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); + nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf, *sam_rx_len); // PAGESEL (0x84) substitution for 2K PicoPass cards. A 2K card has a // single book/page and does not answer PAGESEL, but the encode-side SAM @@ -720,7 +774,7 @@ int sam_relay_iso15_loop( // last SAM->TAG // c1 61 c1 00 00 a1 02 >>82<< 00 90 00 - if (sam_rx_buf[7] == 0x82) { + if (sam_relay_complete(sam_rx_buf, *sam_rx_len)) { // tag <-> SAM exchange ends here break; } @@ -738,7 +792,7 @@ int sam_relay_iso15_loop( // interpreter command). That response is ALREADY in sam_rx_buf - // sending the ack now would overwrite it with a bare 90 00. // So only ack in case (a). - if (sam_rx_buf[1] == 0x61) { + if (sam_relay_pending(sam_rx_buf, *sam_rx_len)) { static const uint8_t hfack[] = { 0xbd, 0x04, 0xa0, 0x02, 0x82, 0x00 }; @@ -763,7 +817,7 @@ int sam_relay_iso15_loop( return PM3_SUCCESS; } -uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) { +uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf, uint16_t sam_rx_len) { // SAM resp: // c1 61 c1 00 00 // a1 10 <- nfc command @@ -779,8 +833,18 @@ uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) { // NFC req: // 0C 05 DE 64 - // copy data out of c1->a1>->a1->80 node - uint16_t nfc_tx_len = (uint8_t) * (sam_rx_buf + 10); - memcpy(nfc_tx_buf, sam_rx_buf + 11, nfc_tx_len); + // copy data out of the a1->a1->80 node, which sits after a routing tail + // that is 5 bytes on some SAMs and 6 on others + uint16_t p = sam_rx_prefix_len(sam_rx_buf, sam_rx_len); + if ((uint16_t)(p + 5) >= sam_rx_len) { + return 0; + } + + uint16_t nfc_tx_len = sam_rx_buf[p + 5]; + if ((uint16_t)(p + 6 + nfc_tx_len) > sam_rx_len) { + return 0; + } + + memcpy(nfc_tx_buf, sam_rx_buf + p + 6, nfc_tx_len); return nfc_tx_len; } diff --git a/armsrc/sam_common.h b/armsrc/sam_common.h index 5096535e1..69e5d8c7f 100644 --- a/armsrc/sam_common.h +++ b/armsrc/sam_common.h @@ -26,6 +26,10 @@ static const uint8_t SAM_RX_ASN1_PREFIX_LENGTH = 5; uint16_t sam_bd_offset(const uint8_t *response, uint16_t response_len); // Offset of the response node plus how much of it to forward. See the .c file. +uint16_t sam_rx_prefix_len(const uint8_t *rx, uint16_t rx_len); +bool sam_relay_pending(const uint8_t *rx, uint16_t rx_len); +bool sam_relay_complete(const uint8_t *rx, uint16_t rx_len); + uint16_t sam_response_payload(const uint8_t *rx, uint16_t rx_len, uint16_t *payload_len); int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen); @@ -77,7 +81,7 @@ void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type void sam_send_ack(void); uint16_t sam_copy_payload_nfc2sam(uint8_t *sam_tx, uint8_t *nfc_rx, uint8_t nfc_len); -uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf); +uint16_t sam_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf, uint16_t sam_rx_len); // NOTE: derived from sam_picopass.c::sam_send_request_iso15's relay loop. int sam_relay_iso15_loop( diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index a4859e1c4..c79d2329f 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -94,15 +94,15 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re sam_rx_buf, &sam_rx_len ); - if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61 + if (sam_relay_pending(sam_rx_buf, sam_rx_len)) { // commands to be relayed to card starts with 0x61 switch_clock_to_countsspclk(); // tag <-> SAM exchange starts here - while (sam_rx_buf[1] == 0x61) { + while (sam_relay_pending(sam_rx_buf, sam_rx_len)) { uint32_t start_time = GetCountSspClk(); uint32_t eof_time = start_time + DELAY_ICLASS_VICC_TO_VCD_READER; - nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); + nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf, sam_rx_len); bool is_cmd_check = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK); @@ -184,7 +184,7 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re // last SAM->TAG // c1 61 c1 00 00 a1 02 >>82<< 00 90 00 - if (sam_rx_buf[7] == 0x82) { + if (sam_relay_complete(sam_rx_buf, sam_rx_len)) { // tag <-> SAM exchange ends here break; } @@ -344,9 +344,9 @@ static int sam_send_request_emulated(const uint8_t *const request, const uint8_t Dbprintf("Emulate: initial SAM resp[1]=%02x rx_len=%u", sam_rx_buf[1], sam_rx_len); } - if (sam_rx_buf[1] == 0x61) { - while (sam_rx_buf[1] == 0x61) { - nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); + if (sam_relay_pending(sam_rx_buf, sam_rx_len)) { + while (sam_relay_pending(sam_rx_buf, sam_rx_len)) { + nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf, sam_rx_len); if (g_dbglevel >= DBG_INFO) { Dbprintf("Emulate: SAM NFC cmd [%u]: %02x %02x ...", nfc_tx_len, @@ -473,7 +473,7 @@ static int sam_send_request_emulated(const uint8_t *const request, const uint8_t Dbprintf("Emulate: SAM rx[1]=%02x rx[7]=%02x", sam_rx_buf[1], sam_rx_buf[7]); } - if (sam_rx_buf[7] == 0x82) { + if (sam_relay_complete(sam_rx_buf, sam_rx_len)) { break; } } diff --git a/armsrc/sam_seos.c b/armsrc/sam_seos.c index 3cbc5983f..c1ddfcc8b 100644 --- a/armsrc/sam_seos.c +++ b/armsrc/sam_seos.c @@ -177,23 +177,34 @@ static int sam_send_request_iso14a(const uint8_t *const request, const uint8_t r sam_rx_buf, &sam_rx_len ); - if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61 + if (sam_relay_pending(sam_rx_buf, sam_rx_len)) { // commands to be relayed to card starts with 0x61 // tag <-> SAM exchange starts here - while (sam_rx_buf[1] == 0x61) { + while (sam_relay_pending(sam_rx_buf, sam_rx_len)) { switch_clock_to_countsspclk(); - nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); + nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf, sam_rx_len); - nfc_rx_len = iso14_apdu(nfc_tx_buf, nfc_tx_len, false, nfc_rx_buf, ISO7816_MAX_FRAME, NULL); - // iceman: should check nfc_rx_len , if negative something went wrong... + int nfc_res = iso14_apdu(nfc_tx_buf, nfc_tx_len, false, nfc_rx_buf, ISO7816_MAX_FRAME, NULL); switch_clock_to_ticks(); + + // A card that went away returns negative, which wrapped through + // uint16_t and was relayed on as a 253 byte answer. + if (nfc_res < 2) { + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("SEOS relay: card exchange failed (%d)", nfc_res); + } + res = PM3_ECARDEXCHANGE; + goto out; + } + + nfc_rx_len = (uint16_t)nfc_res; sam_tx_len = sam_copy_payload_nfc2sam(sam_tx_buf, nfc_rx_buf, nfc_rx_len - 2); sam_send_payload(0x14, 0x0a, 0x14, sam_tx_buf, &sam_tx_len, sam_rx_buf, &sam_rx_len); // last SAM->TAG // c1 61 c1 00 00 a1 02 >>82<< 00 90 00 - if (sam_rx_buf[7] == 0x82) { + if (sam_relay_complete(sam_rx_buf, sam_rx_len)) { // tag <-> SAM exchange ends here break; } diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 9daeffd60..8319ede72 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -8131,14 +8131,31 @@ static int CmdHFiClassSAMExtract(const char *Cmd) { return res; } - const uint8_t *oid = pacs + 2 + pacs_length; - const uint8_t oid_length = oid[1]; - const uint8_t *oid_data = oid + 2; - PrintAndLogEx(SUCCESS, "SIO OID.......... " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length)); + // The a0 element holds 80 (PACS) and optionally 81 (SIO OID) and 82 + // (media type). An iCLASS SE credential often omits 81, so walk the + // nodes rather than assuming all three are present in order. + const uint8_t *p = pacs + 2 + pacs_length; + const uint8_t *end = d + 6 + d[5]; + if (end > d + resp.length) { + end = d + resp.length; + } - const uint8_t *mediaType = oid + 2 + oid_length; - const uint8_t mediaType_data = mediaType[2]; - PrintAndLogEx(SUCCESS, "SIO Media Type... " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data)); + while (p + 1 < end) { + + uint8_t tag = p[0]; + uint8_t len = p[1]; + if (p + 2 + len > end) { + break; + } + + if (tag == 0x81) { + PrintAndLogEx(SUCCESS, "SIO OID.......... " _GREEN_("%s"), sprint_hex_inrow(p + 2, len)); + } else if ((tag == 0x82) && (len >= 1)) { + PrintAndLogEx(SUCCESS, "SIO Media Type... " _GREEN_("%s"), getSioMediaTypeInfo(p[2])); + } + + p += 2 + len; + } } else if (break_nrmac && d[0] == 0x05) { PrintAndLogEx(SUCCESS, "Nr-MAC........... " _GREEN_("%s"), sprint_hex_inrow(d + 1, 8)); if (verbose) { diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index 8458819bd..910194445 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -2373,14 +2373,31 @@ static int CmdHfSeosSAM(const char *Cmd) { return res; } - const uint8_t *oid = pacs + 2 + pacs_length; - const uint8_t oid_length = oid[1]; - const uint8_t *oid_data = oid + 2; - PrintAndLogEx(SUCCESS, "SIO OID.......: " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length)); + // The a0 element holds 80 (PACS) and optionally 81 (SIO OID) and 82 + // (media type). An iCLASS SE credential often omits 81, so walk the + // nodes rather than assuming all three are present in order. + const uint8_t *p = pacs + 2 + pacs_length; + const uint8_t *end = d + 6 + d[5]; + if (end > d + resp.length) { + end = d + resp.length; + } - const uint8_t *mediaType = oid + 2 + oid_length; - const uint8_t mediaType_data = mediaType[2]; - PrintAndLogEx(SUCCESS, "SIO Media Type: " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data)); + while (p + 1 < end) { + + uint8_t tag = p[0]; + uint8_t len = p[1]; + if (p + 2 + len > end) { + break; + } + + if (tag == 0x81) { + PrintAndLogEx(SUCCESS, "SIO OID.......: " _GREEN_("%s"), sprint_hex_inrow(p + 2, len)); + } else if ((tag == 0x82) && (len >= 1)) { + PrintAndLogEx(SUCCESS, "SIO Media Type: " _GREEN_("%s"), getSioMediaTypeInfo(p[2])); + } + + p += 2 + len; + } } else { print_hex(d, resp.length);