-
Notifications
You must be signed in to change notification settings - Fork 4
fix ATS handling for 14443a-4 cards #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
0575f85
fix ATS handling for 14443a-4 cards
nvx 4eb43d1
Merge remote-tracking branch 'origin/main' into fix-ats
nvx af17050
reconstruct the full ATS rather than just sending the historical bytes
nvx 5f0549a
reintroduce suppression of select seosapp for desfire
nvx 1f71ff9
conditionally handle EV2 inteception based on rAPDU from card
nvx 5e02c73
fix desfire sio sniffing on newer SAM firmware versions
nvx File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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}; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I try to put these at the top of the file, but it's not a blocker |
||
| 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); | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes you wonder how the hell the readers do it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Newer SAM firmware seems to avoid doing the SELECT SeosApp on DESFire cards in the first place, like I can replicate that via Seader even so it's not like we're just talking to the SAM wrong. The question is how it's identifying it, it doesn't have access to the ATQA, and oddly the ATS doesn't seem to matter for the most part either which really just leaves the fact it's a 14443a-4 card, the SAK, and the UID - none of which seems like it would be a reliable indicator, but perhaps the UID is more reliable than expected if you have full NXP datasheets or something.