diff --git a/sam_api.c b/sam_api.c index 749df07..6f1839c 100644 --- a/sam_api.c +++ b/sam_api.c @@ -21,8 +21,10 @@ char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0}; uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02}; -uint8_t ev2_request[] = +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) { @@ -675,11 +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(memcmp(buffer, desfire_read, len) == 0 && rxBuffer[0] == 0x30) { - credential->sio_len = + // 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) { + 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); } } @@ -744,23 +753,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 && 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)); + bit_buffer_append_bytes(tx_buffer, buffer, len); - } else { - bit_buffer_append_bytes(tx_buffer, buffer, len); + 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; + 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 +1132,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, @@ -1111,9 +1140,7 @@ NfcCommand seader_worker_card_detect( uint8_t uid_len, uint8_t* ats, uint8_t ats_len) { - UNUSED(ats); - UNUSED(ats_len); - + UNUSED(atqa); SeaderCredential* credential = seader->credential; CardDetails_t* cardDetails = 0; @@ -1122,34 +1149,30 @@ 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 = {.buf = ats, .size = ats_len}; uint8_t protocol_bytes[] = {0x00, 0x00}; - if(sak != 0 && atqa != NULL) { // type 4 - protocol_bytes[1] = FrameProtocol_nfc; - OCTET_STRING_fromBuf( - &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes)); - cardDetails->sak = &sak_string; - cardDetails->atqa = &atqa_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 + // 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( &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; } 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 { - 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_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.c b/seader_worker.c index 4b26641..c13928e 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -281,12 +281,49 @@ 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 t1_tk_size = 0; + if(iso14443_4a_data->ats_data.t1_tk != NULL) { + 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(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, NULL, 0); + 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); diff --git a/seader_worker_i.h b/seader_worker_i.h index 4b9e350..4716f05 100644 --- a/seader_worker_i.h +++ b/seader_worker_i.h @@ -15,7 +15,13 @@ #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) +#define ISO14443_4A_ATS_T0_TB1 (1U << 5) +#define ISO14443_4A_ATS_T0_TC1 (1U << 6) struct SeaderWorker { FuriThread* thread;