From 0575f857475754dadce7d8767c483b8155a40506 Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Mon, 18 Aug 2025 22:27:03 +1000 Subject: [PATCH 1/5] fix ATS handling for 14443a-4 cards preivously the ATS was always filled with the ATQA which caused some oddities with DESFire EV2 and newer cards this change populates the ATS which removes the need for the EV2 workaround for Seos cards however the ATS does not appear to be being returned by the flipper firmware (perhaps an upstream bug?) but for Seos the ATS doesn't matter as long as "something" is present, so we retain the previous behaviour of filling in the ATQA for part4 cards --- sam_api.c | 43 +++++++++++++++++++++---------------------- seader_worker.c | 20 +++++++++++++++++--- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/sam_api.c b/sam_api.c index 749df07..b3cda6e 100644 --- a/sam_api.c +++ b/sam_api.c @@ -21,10 +21,6 @@ char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0}; uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02}; -uint8_t ev2_request[] = - {0x00, 0xa4, 0x04, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00}; -uint8_t FILE_NOT_FOUND[] = {0x6a, 0x82}; - void* calloc(size_t count, size_t size) { return malloc(count * size); } @@ -748,20 +744,14 @@ void seader_iso14443a_transmit( BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE); do { - if(credential->isDesfire && memcmp(buffer, ev2_request, len) == 0) { - FURI_LOG_I(TAG, "Intercept Desfire EV2 response and return File Not Found"); - bit_buffer_append_bytes(rx_buffer, FILE_NOT_FOUND, sizeof(FILE_NOT_FOUND)); - - } else { - bit_buffer_append_bytes(tx_buffer, buffer, len); + bit_buffer_append_bytes(tx_buffer, buffer, len); - Iso14443_4aError error = - iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); - if(error != Iso14443_4aErrorNone) { - FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error); - seader_worker->stage = SeaderPollerEventTypeFail; - break; - } + Iso14443_4aError error = + iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); + if(error != Iso14443_4aErrorNone) { + FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error); + seader_worker->stage = SeaderPollerEventTypeFail; + break; } seader_capture_sio(tx_buffer, rx_buffer, credential); @@ -1111,9 +1101,6 @@ NfcCommand seader_worker_card_detect( uint8_t uid_len, uint8_t* ats, uint8_t ats_len) { - UNUSED(ats); - UNUSED(ats_len); - SeaderCredential* credential = seader->credential; CardDetails_t* cardDetails = 0; @@ -1122,7 +1109,19 @@ NfcCommand seader_worker_card_detect( OCTET_STRING_fromBuf(&cardDetails->csn, (const char*)uid, uid_len); OCTET_STRING_t sak_string = {.buf = &sak, .size = 1}; - OCTET_STRING_t atqa_string = {.buf = atqa, .size = 2}; + OCTET_STRING_t ats_string = {0}; + if(ats != NULL) { + ats_string.buf = ats; + ats_string.size = ats_len; + } else { + // technically I think this field is only ever meant to be ATS not ATQA + // for 14443a-4 cards but in practice, Seos cards seem to fail to read the + // ATS but the ATS doesn't matter for Seos as long as it's there otherwise + // the SAM thinks it's a part3 card, and filling it with the ATQA is what + // we did previously and seemed to work so eh + ats_string.buf = atqa; + ats_string.size = 2; + } uint8_t protocol_bytes[] = {0x00, 0x00}; if(sak != 0 && atqa != NULL) { // type 4 @@ -1130,7 +1129,7 @@ NfcCommand seader_worker_card_detect( OCTET_STRING_fromBuf( &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes)); cardDetails->sak = &sak_string; - cardDetails->atqa = &atqa_string; + cardDetails->atqa = &ats_string; credential->isDesfire = seader_mf_df_check_card_type(atqa[0], atqa[1], sak); if(credential->isDesfire) { memcpy(credential->diversifier, uid, uid_len); diff --git a/seader_worker.c b/seader_worker.c index 4b26641..071e99e 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -281,12 +281,26 @@ NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void size_t uid_len; const uint8_t* uid = nfc_device_get_uid(seader->nfc_device, &uid_len); - const Iso14443_3aData* iso14443_3a_data = - nfc_device_get_data(seader->nfc_device, NfcProtocolIso14443_3a); + const Iso14443_4aData* iso14443_4a_data = + nfc_device_get_data(seader->nfc_device, NfcProtocolIso14443_4a); + const Iso14443_3aData* iso14443_3a_data = iso14443_4a_get_base_data(iso14443_4a_data); + uint32_t ats_len = 0; + uint8_t* ats = NULL; + if(iso14443_4a_data->ats_data.t1_tk != NULL) { + // for some reason Seos cards fail to read the ATS + SimpleArray* ats_array = iso14443_4a_data->ats_data.t1_tk; + ats_len = simple_array_get_count(ats_array); + if(ats_len > 0xFF) { + ats_len = 0; + } + if(ats_len > 0) { + ats = simple_array_get_data(ats_array); + } + } uint8_t sak = iso14443_3a_get_sak(iso14443_3a_data); seader_worker_card_detect( - seader, sak, (uint8_t*)iso14443_3a_data->atqa, uid, uid_len, NULL, 0); + seader, sak, (uint8_t*)iso14443_3a_data->atqa, uid, uid_len, ats, ats_len); // nfc_set_fdt_poll_fc(event.instance, SEADER_POLLER_MAX_FWT); furi_thread_set_current_priority(FuriThreadPriorityLowest); From af170501788bfc0ea6ffb84c03d46ba67425e7d6 Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Wed, 20 Aug 2025 18:21:16 +1000 Subject: [PATCH 2/5] reconstruct the full ATS rather than just sending the historical bytes rejig card type detection in seader_worker_card_detect --- sam_api.c | 30 +++++++++--------------------- seader_worker.c | 41 ++++++++++++++++++++++++++++++++--------- seader_worker_i.h | 5 +++++ 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/sam_api.c b/sam_api.c index b3cda6e..8f3e770 100644 --- a/sam_api.c +++ b/sam_api.c @@ -1101,6 +1101,7 @@ NfcCommand seader_worker_card_detect( uint8_t uid_len, uint8_t* ats, uint8_t ats_len) { + UNUSED(atqa); SeaderCredential* credential = seader->credential; CardDetails_t* cardDetails = 0; @@ -1109,37 +1110,21 @@ NfcCommand seader_worker_card_detect( OCTET_STRING_fromBuf(&cardDetails->csn, (const char*)uid, uid_len); OCTET_STRING_t sak_string = {.buf = &sak, .size = 1}; - OCTET_STRING_t ats_string = {0}; - if(ats != NULL) { - ats_string.buf = ats; - ats_string.size = ats_len; - } else { - // technically I think this field is only ever meant to be ATS not ATQA - // for 14443a-4 cards but in practice, Seos cards seem to fail to read the - // ATS but the ATS doesn't matter for Seos as long as it's there otherwise - // the SAM thinks it's a part3 card, and filling it with the ATQA is what - // we did previously and seemed to work so eh - ats_string.buf = atqa; - ats_string.size = 2; - } + OCTET_STRING_t ats_string = {.buf = ats, .size = ats_len}; uint8_t protocol_bytes[] = {0x00, 0x00}; - if(sak != 0 && atqa != NULL) { // type 4 + if(ats != NULL) { // type 4 protocol_bytes[1] = FrameProtocol_nfc; OCTET_STRING_fromBuf( &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes)); cardDetails->sak = &sak_string; + // TODO: Update asn1 to change atqa to ats cardDetails->atqa = &ats_string; credential->isDesfire = seader_mf_df_check_card_type(atqa[0], atqa[1], sak); if(credential->isDesfire) { memcpy(credential->diversifier, uid, uid_len); credential->diversifier_len = uid_len; } - } else if(sak != 0 && atqa == NULL) { // MFC - protocol_bytes[1] = FrameProtocol_nfc; - OCTET_STRING_fromBuf( - &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes)); - cardDetails->sak = &sak_string; } else if(uid_len == 8) { // picopass protocol_bytes[1] = FrameProtocol_iclass; OCTET_STRING_fromBuf( @@ -1147,8 +1132,11 @@ NfcCommand seader_worker_card_detect( memcpy(credential->diversifier, uid, uid_len); credential->diversifier_len = uid_len; credential->isDesfire = false; - } else { - FURI_LOG_D(TAG, "Unknown card type"); + } else { // MFC + protocol_bytes[1] = FrameProtocol_nfc; + OCTET_STRING_fromBuf( + &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes)); + cardDetails->sak = &sak_string; } seader_send_card_detected(seader, cardDetails); diff --git a/seader_worker.c b/seader_worker.c index 071e99e..c13928e 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -284,24 +284,47 @@ NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void const Iso14443_4aData* iso14443_4a_data = nfc_device_get_data(seader->nfc_device, NfcProtocolIso14443_4a); const Iso14443_3aData* iso14443_3a_data = iso14443_4a_get_base_data(iso14443_4a_data); - uint32_t ats_len = 0; - uint8_t* ats = NULL; + + uint32_t t1_tk_size = 0; if(iso14443_4a_data->ats_data.t1_tk != NULL) { - // for some reason Seos cards fail to read the ATS - SimpleArray* ats_array = iso14443_4a_data->ats_data.t1_tk; - ats_len = simple_array_get_count(ats_array); - if(ats_len > 0xFF) { - ats_len = 0; + t1_tk_size = simple_array_get_count(iso14443_4a_data->ats_data.t1_tk); + if(t1_tk_size > 0xFF) { + t1_tk_size = 0; + } + } + + uint8_t ats_len = 0; + uint8_t* ats = malloc(4 + t1_tk_size); + furi_assert(ats); + + if(iso14443_4a_data->ats_data.tl > 1) { + ats[ats_len++] = iso14443_4a_data->ats_data.t0; + if(iso14443_4a_data->ats_data.t0 & ISO14443_4A_ATS_T0_TA1) { + ats[ats_len++] = iso14443_4a_data->ats_data.ta_1; } - if(ats_len > 0) { - ats = simple_array_get_data(ats_array); + if(iso14443_4a_data->ats_data.t0 & ISO14443_4A_ATS_T0_TB1) { + ats[ats_len++] = iso14443_4a_data->ats_data.tb_1; + } + if(iso14443_4a_data->ats_data.t0 & ISO14443_4A_ATS_T0_TC1) { + ats[ats_len++] = iso14443_4a_data->ats_data.tc_1; + } + + if(t1_tk_size != 0) { + memcpy( + ats + ats_len, + simple_array_cget_data(iso14443_4a_data->ats_data.t1_tk), + t1_tk_size); + ats_len += t1_tk_size; } } + uint8_t sak = iso14443_3a_get_sak(iso14443_3a_data); seader_worker_card_detect( seader, sak, (uint8_t*)iso14443_3a_data->atqa, uid, uid_len, ats, ats_len); + free(ats); + // nfc_set_fdt_poll_fc(event.instance, SEADER_POLLER_MAX_FWT); furi_thread_set_current_priority(FuriThreadPriorityLowest); seader_worker->stage = SeaderPollerEventTypeConversation; diff --git a/seader_worker_i.h b/seader_worker_i.h index 4b9e350..b27b5ac 100644 --- a/seader_worker_i.h +++ b/seader_worker_i.h @@ -17,6 +17,11 @@ #define SEADER_POLLER_MAX_FWT (200000U) #define SEADER_POLLER_MAX_BUFFER_SIZE (255U) +// ATS bit definitions +#define ISO14443_4A_ATS_T0_TA1 (1U << 4) +#define ISO14443_4A_ATS_T0_TB1 (1U << 5) +#define ISO14443_4A_ATS_T0_TC1 (1U << 6) + struct SeaderWorker { FuriThread* thread; Storage* storage; From 5f0549aaa923b58b59e85451e78b086074252e0a Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Wed, 20 Aug 2025 20:08:04 +1000 Subject: [PATCH 3/5] reintroduce suppression of select seosapp for desfire --- sam_api.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/sam_api.c b/sam_api.c index 8f3e770..3773f5a 100644 --- a/sam_api.c +++ b/sam_api.c @@ -21,6 +21,10 @@ char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0}; uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02}; +uint8_t select_seos_app[] = + {0x00, 0xa4, 0x04, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00}; +uint8_t FILE_NOT_FOUND[] = {0x6a, 0x82}; + void* calloc(size_t count, size_t size) { return malloc(count * size); } @@ -744,14 +748,20 @@ void seader_iso14443a_transmit( BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE); do { - bit_buffer_append_bytes(tx_buffer, buffer, len); + if(credential->isDesfire && sizeof(select_seos_app) == len && + memcmp(buffer, select_seos_app, len) == 0) { + FURI_LOG_I(TAG, "Intercept SELECT SeosApp to DESFire card and return File Not Found"); + bit_buffer_append_bytes(rx_buffer, FILE_NOT_FOUND, sizeof(FILE_NOT_FOUND)); + } else { + bit_buffer_append_bytes(tx_buffer, buffer, len); - Iso14443_4aError error = - iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); - if(error != Iso14443_4aErrorNone) { - FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error); - seader_worker->stage = SeaderPollerEventTypeFail; - break; + Iso14443_4aError error = + iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); + if(error != Iso14443_4aErrorNone) { + FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error); + seader_worker->stage = SeaderPollerEventTypeFail; + break; + } } seader_capture_sio(tx_buffer, rx_buffer, credential); From 1f71ff9162c30c7631696fc8bbf953015911b53a Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Wed, 20 Aug 2025 21:26:51 +1000 Subject: [PATCH 4/5] conditionally handle EV2 inteception based on rAPDU from card --- sam_api.c | 72 +++++++++++++++++++++++++++++---------------- seader_credential.h | 2 +- seader_worker_i.h | 3 +- 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/sam_api.c b/sam_api.c index 3773f5a..2048a63 100644 --- a/sam_api.c +++ b/sam_api.c @@ -23,6 +23,8 @@ uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02}; uint8_t select_seos_app[] = {0x00, 0xa4, 0x04, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x04, 0x40, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00}; +uint8_t select_desfire_app_no_le[] = + {0x00, 0xA4, 0x04, 0x00, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00}; uint8_t FILE_NOT_FOUND[] = {0x6a, 0x82}; void* calloc(size_t count, size_t size) { @@ -677,7 +679,8 @@ void seader_capture_sio(BitBuffer* tx_buffer, BitBuffer* rx_buffer, SeaderCreden // Desfire EV1 passes SIO in the clear uint8_t desfire_read[] = { 0x90, 0xbd, 0x00, 0x00, 0x07, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if(memcmp(buffer, desfire_read, len) == 0 && rxBuffer[0] == 0x30) { + if(sizeof(desfire_read) == len && memcmp(buffer, desfire_read, len) == 0 && + rxBuffer[0] == 0x30) { credential->sio_len = bit_buffer_get_size_bytes(rx_buffer) - 2; // -2 for the APDU response bytes memcpy(credential->sio, rxBuffer, credential->sio_len); @@ -744,23 +747,47 @@ void seader_iso14443a_transmit( SeaderWorker* seader_worker = seader->worker; SeaderCredential* credential = seader->credential; - BitBuffer* tx_buffer = bit_buffer_alloc(len); + BitBuffer* tx_buffer = + bit_buffer_alloc(len + 1); // extra byte to allow for appending a Le byte sometimes BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE); do { - if(credential->isDesfire && sizeof(select_seos_app) == len && - memcmp(buffer, select_seos_app, len) == 0) { - FURI_LOG_I(TAG, "Intercept SELECT SeosApp to DESFire card and return File Not Found"); - bit_buffer_append_bytes(rx_buffer, FILE_NOT_FOUND, sizeof(FILE_NOT_FOUND)); - } else { - bit_buffer_append_bytes(tx_buffer, buffer, len); + bit_buffer_append_bytes(tx_buffer, buffer, len); - Iso14443_4aError error = - iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); - if(error != Iso14443_4aErrorNone) { - FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error); - seader_worker->stage = SeaderPollerEventTypeFail; - break; + if(seader->credential->isDesfireEV2 && sizeof(select_desfire_app_no_le) == len && + memcmp(buffer, select_desfire_app_no_le, len) == 0) { + // If a DESFire EV2 card has previously sent a dodgy reply to a SELECT SeosApp + // future SELECT DESFire commands with no Le byte (Ne == 0) fail with SW 6C00 (Wrong length Le) + // If it has responded with a file not found (ie non-EV2 cards) to the SELECT SeosApp + // then the SELECT DESFire without the Le byte is accepted fine. + // No clue why this happens, but we have to deal with it annoyingly + // We can't just always add the Le byte as this breaks OG D40 cards, so only do it when needed + bit_buffer_append_byte(tx_buffer, 0x00); // Le byte of 0x00 is Ne 256 + } + + Iso14443_4aError error = + iso14443_4a_poller_send_block(iso14443_4a_poller, tx_buffer, rx_buffer); + if(error != Iso14443_4aErrorNone) { + FURI_LOG_W(TAG, "iso14443_4a_poller_send_block error %d", error); + seader_worker->stage = SeaderPollerEventTypeFail; + break; + } + + // if the cAPDU was select seos app and the response starts with 6F228520 + // then this is almost certainly a dodgy response from a DESFire EV2 card + // not a Seos card which old SAM firmware don't handle very well, so fake + // a FILD_NOT_FOUND response instead of the real response + if(sizeof(select_seos_app) == len && memcmp(buffer, select_seos_app, len) == 0 && + bit_buffer_get_size_bytes(rx_buffer) == 38) { + const uint8_t ev2_select_reply_prefix[] = {0x6F, 0x22, 0x85, 0x20}; + const uint8_t* rapdu = bit_buffer_get_data(rx_buffer); + if(memcmp(ev2_select_reply_prefix, rapdu, sizeof(ev2_select_reply_prefix)) == 0) { + FURI_LOG_I( + TAG, + "Intercept DESFire EV2 reply to SELECT SeosApp and return File Not Found"); + seader->credential->isDesfireEV2 = true; + bit_buffer_reset(rx_buffer); + bit_buffer_append_bytes(rx_buffer, FILE_NOT_FOUND, sizeof(FILE_NOT_FOUND)); } } @@ -1099,10 +1126,6 @@ bool seader_process_success_response_i( return processed; } -bool seader_mf_df_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { - return ATQA0 == 0x44 && ATQA1 == 0x03 && SAK == 0x20; -} - NfcCommand seader_worker_card_detect( Seader* seader, uint8_t sak, @@ -1123,6 +1146,11 @@ NfcCommand seader_worker_card_detect( OCTET_STRING_t ats_string = {.buf = ats, .size = ats_len}; uint8_t protocol_bytes[] = {0x00, 0x00}; + // this won't hold true for Seos cards, but then we won't see the SIO from Seos cards anyway + // so it doesn't really matter + memcpy(credential->diversifier, uid, uid_len); + credential->diversifier_len = uid_len; + if(ats != NULL) { // type 4 protocol_bytes[1] = FrameProtocol_nfc; OCTET_STRING_fromBuf( @@ -1130,18 +1158,10 @@ NfcCommand seader_worker_card_detect( cardDetails->sak = &sak_string; // TODO: Update asn1 to change atqa to ats cardDetails->atqa = &ats_string; - credential->isDesfire = seader_mf_df_check_card_type(atqa[0], atqa[1], sak); - if(credential->isDesfire) { - memcpy(credential->diversifier, uid, uid_len); - credential->diversifier_len = uid_len; - } } else if(uid_len == 8) { // picopass protocol_bytes[1] = FrameProtocol_iclass; OCTET_STRING_fromBuf( &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes)); - memcpy(credential->diversifier, uid, uid_len); - credential->diversifier_len = uid_len; - credential->isDesfire = false; } else { // MFC protocol_bytes[1] = FrameProtocol_nfc; OCTET_STRING_fromBuf( diff --git a/seader_credential.h b/seader_credential.h index d11f74e..b89dd80 100644 --- a/seader_credential.h +++ b/seader_credential.h @@ -42,7 +42,7 @@ typedef struct { uint8_t diversifier[8]; uint8_t diversifier_len; uint8_t sio_start_block; // for iClass SE vs iClass SR - bool isDesfire; + bool isDesfireEV2; SeaderCredentialType type; SeaderCredentialSaveFormat save_format; char name[SEADER_CRED_NAME_MAX_LEN + 1]; diff --git a/seader_worker_i.h b/seader_worker_i.h index b27b5ac..4716f05 100644 --- a/seader_worker_i.h +++ b/seader_worker_i.h @@ -15,7 +15,8 @@ #include #define SEADER_POLLER_MAX_FWT (200000U) -#define SEADER_POLLER_MAX_BUFFER_SIZE (255U) +// Maximum basic rAPDU size is 256 bytes of data + 2 byte SW +#define SEADER_POLLER_MAX_BUFFER_SIZE (258U) // ATS bit definitions #define ISO14443_4A_ATS_T0_TA1 (1U << 4) From 5e02c7387539ff203dcd5ed0497ee48c38ee6575 Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Wed, 20 Aug 2025 21:53:00 +1000 Subject: [PATCH 5/5] fix desfire sio sniffing on newer SAM firmware versions --- sam_api.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sam_api.c b/sam_api.c index 2048a63..6f1839c 100644 --- a/sam_api.c +++ b/sam_api.c @@ -677,12 +677,18 @@ void seader_capture_sio(BitBuffer* tx_buffer, BitBuffer* rx_buffer, SeaderCreden } } else if(credential->type == SeaderCredentialType14A) { // Desfire EV1 passes SIO in the clear - uint8_t desfire_read[] = { - 0x90, 0xbd, 0x00, 0x00, 0x07, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if(sizeof(desfire_read) == len && memcmp(buffer, desfire_read, len) == 0 && + // The desfire_read command is 13 bytes in total, but we deliberately don't check the read length as newer SAM + // firmware versions read 5 bytes first to determine the length of the SIO from the ASN.1 tag length then do a + // second read with just the required length to skip reading any additional bytes at the end of the file + uint8_t desfire_read[] = {0x90, 0xbd, 0x00, 0x00, 0x07, 0x0f, 0x00, 0x00, 0x00}; + if(len == 13 && memcmp(buffer, desfire_read, sizeof(desfire_read)) == 0 && rxBuffer[0] == 0x30) { - credential->sio_len = + size_t sio_len = bit_buffer_get_size_bytes(rx_buffer) - 2; // -2 for the APDU response bytes + if(sio_len > sizeof(credential->sio)) { + return; + } + credential->sio_len = sio_len; memcpy(credential->sio, rxBuffer, credential->sio_len); } }