MX 2 лет назад
Родитель
Сommit
aa0a38eee7

+ 1 - 1
application.fam

@@ -17,7 +17,7 @@ App(
     ],
     ],
     fap_icon="icons/logo.png",
     fap_icon="icons/logo.png",
     fap_category="NFC",
     fap_category="NFC",
-    fap_version="2.1",
+    fap_version="2.2",
     fap_author="bettse",
     fap_author="bettse",
 #    fap_extbuild=(
 #    fap_extbuild=(
 #        ExtFile(
 #        ExtFile(

+ 62 - 17
sam_api.c

@@ -22,6 +22,10 @@ uint8_t read4Block13[] = {RFAL_PICOPASS_CMD_READ4, 0x0D, 0x96, 0xE8};
 
 
 uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02};
 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) {
 void* calloc(size_t count, size_t size) {
     return malloc(count * size);
     return malloc(count * size);
 }
 }
@@ -321,7 +325,7 @@ bool seader_unpack_pacs(Seader* seader, uint8_t* buf, size_t size) {
 
 
             memset(display, 0, sizeof(display));
             memset(display, 0, sizeof(display));
             if(seader_credential->sio[0] == 0x30) {
             if(seader_credential->sio[0] == 0x30) {
-                for(uint8_t i = 0; i < sizeof(seader_credential->sio); i++) {
+                for(uint8_t i = 0; i < seader_credential->sio_len; i++) {
                     snprintf(
                     snprintf(
                         display + (i * 2), sizeof(display), "%02x", seader_credential->sio[i]);
                         display + (i * 2), sizeof(display), "%02x", seader_credential->sio[i]);
                 }
                 }
@@ -475,14 +479,29 @@ void seader_capture_sio(BitBuffer* tx_buffer, BitBuffer* rx_buffer, SeaderCreden
     size_t len = bit_buffer_get_size_bytes(tx_buffer);
     size_t len = bit_buffer_get_size_bytes(tx_buffer);
     const uint8_t* rxBuffer = bit_buffer_get_data(rx_buffer);
     const uint8_t* rxBuffer = bit_buffer_get_data(rx_buffer);
 
 
-    if(memcmp(buffer, read4Block6, len) == 0 && rxBuffer[0] == 0x30) {
-        memcpy(credential->sio, rxBuffer, 32);
-    } else if(memcmp(buffer, read4Block10, len) == 0 && rxBuffer[0] == 0x30) {
-        memcpy(credential->sio, rxBuffer, 32);
-    } else if(memcmp(buffer, read4Block9, len) == 0) {
-        memcpy(credential->sio + 32, rxBuffer + 8, 24);
-    } else if(memcmp(buffer, read4Block13, len) == 0) {
-        memcpy(credential->sio + 32, rxBuffer + 8, 24);
+    if(credential->type == SeaderCredentialTypePicopass) {
+        if(memcmp(buffer, read4Block6, len) == 0 && rxBuffer[0] == 0x30) {
+            memcpy(credential->sio, rxBuffer, 32);
+            credential->sio_len += 32;
+        } else if(memcmp(buffer, read4Block10, len) == 0 && rxBuffer[0] == 0x30) {
+            memcpy(credential->sio, rxBuffer, 32);
+            credential->sio_len += 32;
+        } else if(memcmp(buffer, read4Block9, len) == 0) {
+            memcpy(credential->sio + 32, rxBuffer + 8, 24);
+            credential->sio_len += 24;
+        } else if(memcmp(buffer, read4Block13, len) == 0) {
+            memcpy(credential->sio + 32, rxBuffer + 8, 24);
+            credential->sio_len += 24;
+        }
+    } 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 =
+                bit_buffer_get_size_bytes(rx_buffer) - 2; // -2 for the APDU response bytes
+            memcpy(credential->sio, rxBuffer, credential->sio_len);
+        }
     }
     }
 }
 }
 
 
@@ -549,21 +568,29 @@ void seader_iso14443a_transmit(
     furi_assert(iso14443_4a_poller);
     furi_assert(iso14443_4a_poller);
     SeaderWorker* seader_worker = seader->worker;
     SeaderWorker* seader_worker = seader->worker;
     SeaderUartBridge* seader_uart = seader_worker->uart;
     SeaderUartBridge* seader_uart = seader_worker->uart;
+    SeaderCredential* credential = seader->credential;
 
 
     BitBuffer* tx_buffer = bit_buffer_alloc(len);
     BitBuffer* tx_buffer = bit_buffer_alloc(len);
     BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE);
     BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE);
 
 
     do {
     do {
-        bit_buffer_append_bytes(tx_buffer, buffer, len);
+        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));
 
 
-        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;
+        } 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;
+            }
         }
         }
 
 
+        seader_capture_sio(tx_buffer, rx_buffer, credential);
         seader_send_nfc_rx(
         seader_send_nfc_rx(
             seader_uart,
             seader_uart,
             (uint8_t*)bit_buffer_get_data(rx_buffer),
             (uint8_t*)bit_buffer_get_data(rx_buffer),
@@ -714,13 +741,21 @@ bool seader_process_success_response_i(
 
 
         processed = seader_worker_state_machine(seader, payload, online, spc);
         processed = seader_worker_state_machine(seader, payload, online, spc);
     } else {
     } else {
-        FURI_LOG_D(TAG, "Failed to decode APDU payload");
+        memset(display, 0, sizeof(display));
+        for(uint8_t i = 0; i < len; i++) {
+            snprintf(display + (i * 2), sizeof(display), "%02x", apdu[i]);
+        }
+        FURI_LOG_D(TAG, "Failed to decode APDU payload: [%s]", display);
     }
     }
 
 
     ASN_STRUCT_FREE(asn_DEF_Payload, payload);
     ASN_STRUCT_FREE(asn_DEF_Payload, payload);
     return processed;
     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(
 NfcCommand seader_worker_card_detect(
     Seader* seader,
     Seader* seader,
     uint8_t sak,
     uint8_t sak,
@@ -737,6 +772,8 @@ NfcCommand seader_worker_card_detect(
 
 
     SeaderWorker* seader_worker = seader->worker;
     SeaderWorker* seader_worker = seader->worker;
     SeaderUartBridge* seader_uart = seader_worker->uart;
     SeaderUartBridge* seader_uart = seader_worker->uart;
+    SeaderCredential* credential = seader->credential;
+
     CardDetails_t* cardDetails = 0;
     CardDetails_t* cardDetails = 0;
     cardDetails = calloc(1, sizeof *cardDetails);
     cardDetails = calloc(1, sizeof *cardDetails);
     assert(cardDetails);
     assert(cardDetails);
@@ -750,6 +787,9 @@ NfcCommand seader_worker_card_detect(
         protocol_bytes[1] = FrameProtocol_iclass;
         protocol_bytes[1] = FrameProtocol_iclass;
         OCTET_STRING_fromBuf(
         OCTET_STRING_fromBuf(
             &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes));
             &cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes));
+        memcpy(credential->diversifier, uid, uid_len);
+        credential->diversifier_len = uid_len;
+        credential->isDesfire = false;
     } else {
     } else {
         protocol_bytes[1] = FrameProtocol_nfc;
         protocol_bytes[1] = FrameProtocol_nfc;
         OCTET_STRING_fromBuf(
         OCTET_STRING_fromBuf(
@@ -757,6 +797,11 @@ NfcCommand seader_worker_card_detect(
 
 
         cardDetails->sak = &sak_string;
         cardDetails->sak = &sak_string;
         cardDetails->atqa = &atqa_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;
+        }
     }
     }
 
 
     seader_send_card_detected(seader_uart, cardDetails);
     seader_send_card_detected(seader_uart, cardDetails);

+ 1 - 1
scenes/seader_scene_card_menu.c

@@ -33,7 +33,7 @@ void seader_scene_card_menu_on_enter(void* context) {
         SubmenuIndexSaveRFID,
         SubmenuIndexSaveRFID,
         seader_scene_card_menu_submenu_callback,
         seader_scene_card_menu_submenu_callback,
         seader);
         seader);
-    if(credential->sio[0] == 0x30) {
+    if(credential->sio[0] == 0x30 && credential->diversifier_len == RFAL_PICOPASS_UID_LEN) {
         submenu_add_item(
         submenu_add_item(
             submenu,
             submenu,
             "Save SR",
             "Save SR",

+ 1 - 0
scenes/seader_scene_read_14a.c

@@ -17,6 +17,7 @@ void seader_scene_read_14a_on_enter(void* context) {
 
 
     seader->worker->stage = SeaderPollerEventTypeCardDetect;
     seader->worker->stage = SeaderPollerEventTypeCardDetect;
     seader_credential_clear(seader->credential);
     seader_credential_clear(seader->credential);
+    seader->credential->type = SeaderCredentialType14A;
     nfc_poller_start(seader->poller, seader_worker_poller_callback_iso14443_4a, seader);
     nfc_poller_start(seader->poller, seader_worker_poller_callback_iso14443_4a, seader);
 
 
     seader_blink_start(seader);
     seader_blink_start(seader);

+ 1 - 0
scenes/seader_scene_read_picopass.c

@@ -15,6 +15,7 @@ void seader_scene_read_picopass_on_enter(void* context) {
 
 
     seader->worker->stage = SeaderPollerEventTypeCardDetect;
     seader->worker->stage = SeaderPollerEventTypeCardDetect;
     seader_credential_clear(seader->credential);
     seader_credential_clear(seader->credential);
+    seader->credential->type = SeaderCredentialTypePicopass;
     seader->picopass_poller = picopass_poller_alloc(seader->nfc);
     seader->picopass_poller = picopass_poller_alloc(seader->nfc);
     picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader);
     picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader);
 
 

+ 1 - 1
scenes/seader_scene_sam_missing.c

@@ -21,7 +21,7 @@ void seader_scene_sam_missing_on_enter(void* context) {
         seader_scene_sam_missing_submenu_callback,
         seader_scene_sam_missing_submenu_callback,
         seader);
         seader);
     submenu_add_item(
     submenu_add_item(
-        submenu, "Load", SubmenuIndexSaved, seader_scene_sam_missing_submenu_callback, seader);
+        submenu, "Saved", SubmenuIndexSaved, seader_scene_sam_missing_submenu_callback, seader);
 
 
     submenu_set_selected_item(
     submenu_set_selected_item(
         submenu, scene_manager_get_scene_state(seader->scene_manager, SeaderSceneSamPresent));
         submenu, scene_manager_get_scene_state(seader->scene_manager, SeaderSceneSamPresent));

+ 1 - 1
scenes/seader_scene_sam_present.c

@@ -34,7 +34,7 @@ void seader_scene_sam_present_on_update(void* context) {
         seader_scene_sam_present_submenu_callback,
         seader_scene_sam_present_submenu_callback,
         seader);
         seader);
     submenu_add_item(
     submenu_add_item(
-        submenu, "Load", SubmenuIndexSaved, seader_scene_sam_present_submenu_callback, seader);
+        submenu, "Saved", SubmenuIndexSaved, seader_scene_sam_present_submenu_callback, seader);
 
 
     if(seader_worker->sam_version[0] != 0 && seader_worker->sam_version[1] != 0) {
     if(seader_worker->sam_version[0] != 0 && seader_worker->sam_version[1] != 0) {
         FuriString* fw_str = furi_string_alloc();
         FuriString* fw_str = furi_string_alloc();

+ 1 - 1
scenes/seader_scene_sam_wrong.c

@@ -21,7 +21,7 @@ void seader_scene_sam_wrong_on_enter(void* context) {
         seader_scene_sam_wrong_submenu_callback,
         seader_scene_sam_wrong_submenu_callback,
         seader);
         seader);
     submenu_add_item(
     submenu_add_item(
-        submenu, "Load", SubmenuIndexSaved, seader_scene_sam_wrong_submenu_callback, seader);
+        submenu, "Saved", SubmenuIndexSaved, seader_scene_sam_wrong_submenu_callback, seader);
 
 
     submenu_set_selected_item(
     submenu_set_selected_item(
         submenu, scene_manager_get_scene_state(seader->scene_manager, SeaderSceneSamPresent));
         submenu, scene_manager_get_scene_state(seader->scene_manager, SeaderSceneSamPresent));

+ 7 - 1
seader_credential.c

@@ -71,7 +71,9 @@ static bool seader_credential_load(SeaderCredential* cred, FuriString* path, boo
 
 
         // Optional SIO/Diversifier
         // Optional SIO/Diversifier
         flipper_format_read_hex(file, "SIO", cred->sio, sizeof(cred->sio));
         flipper_format_read_hex(file, "SIO", cred->sio, sizeof(cred->sio));
+        cred->sio_len = sizeof(cred->sio); // No way to know real length;
         flipper_format_read_hex(file, "Diversifier", cred->diversifier, sizeof(cred->diversifier));
         flipper_format_read_hex(file, "Diversifier", cred->diversifier, sizeof(cred->diversifier));
+        cred->diversifier_len = sizeof(cred->diversifier); // No way to know real length;
 
 
         parsed = true;
         parsed = true;
     } while(false);
     } while(false);
@@ -362,9 +364,10 @@ bool seader_credential_save_agnostic(SeaderCredential* cred, const char* name) {
                file, "Credential", (uint8_t*)&swapped, sizeof(cred->credential)))
                file, "Credential", (uint8_t*)&swapped, sizeof(cred->credential)))
             break;
             break;
         if(cred->sio[0] == 0x30) {
         if(cred->sio[0] == 0x30) {
+            // TODO: update to writing sio_len bytes, when that value has been seen to work well
             if(!flipper_format_write_hex(file, "SIO", cred->sio, sizeof(cred->sio))) break;
             if(!flipper_format_write_hex(file, "SIO", cred->sio, sizeof(cred->sio))) break;
             if(!flipper_format_write_hex(
             if(!flipper_format_write_hex(
-                   file, "Diversifier", cred->diversifier, sizeof(cred->diversifier)))
+                   file, "Diversifier", cred->diversifier, cred->diversifier_len))
                 break;
                 break;
         }
         }
 
 
@@ -418,6 +421,7 @@ bool seader_credential_save_picopass(SeaderCredential* cred, const char* name) {
             furi_string_printf(temp_str, "Block %d", i);
             furi_string_printf(temp_str, "Block %d", i);
             switch(i) {
             switch(i) {
             case CSN_INDEX:
             case CSN_INDEX:
+                // TODO: Is there any practical difference here?  If so, document.
                 if(withSIO) {
                 if(withSIO) {
                     if(!flipper_format_write_hex(
                     if(!flipper_format_write_hex(
                            file,
                            file,
@@ -641,7 +645,9 @@ void seader_credential_clear(SeaderCredential* cred) {
     cred->bit_length = 0;
     cred->bit_length = 0;
     cred->type = SeaderCredentialTypeNone;
     cred->type = SeaderCredentialTypeNone;
     memset(cred->sio, 0, sizeof(cred->sio));
     memset(cred->sio, 0, sizeof(cred->sio));
+    cred->sio_len = 0;
     memset(cred->diversifier, 0, sizeof(cred->diversifier));
     memset(cred->diversifier, 0, sizeof(cred->diversifier));
+    cred->diversifier_len = 0;
     furi_string_reset(cred->load_path);
     furi_string_reset(cred->load_path);
 }
 }
 
 

+ 3 - 0
seader_credential.h

@@ -36,7 +36,10 @@ typedef struct {
     uint64_t credential;
     uint64_t credential;
     size_t bit_length;
     size_t bit_length;
     uint8_t sio[128];
     uint8_t sio[128];
+    uint8_t sio_len;
     uint8_t diversifier[8];
     uint8_t diversifier[8];
+    uint8_t diversifier_len;
+    bool isDesfire;
     SeaderCredentialType type;
     SeaderCredentialType type;
     SeaderCredentialSaveFormat save_format;
     SeaderCredentialSaveFormat save_format;
     char name[SEADER_CRED_NAME_MAX_LEN + 1];
     char name[SEADER_CRED_NAME_MAX_LEN + 1];

+ 2 - 0
seader_worker.c

@@ -242,6 +242,8 @@ void seader_worker_poller_conversation(Seader* seader, SeaderPollerContainer* sp
                 // no-op
                 // no-op
             } else {
             } else {
                 FURI_LOG_I(TAG, "Response false");
                 FURI_LOG_I(TAG, "Response false");
+                view_dispatcher_send_custom_event(
+                    seader->view_dispatcher, SeaderCustomEventWorkerExit);
                 seader_worker->stage = SeaderPollerEventTypeComplete;
                 seader_worker->stage = SeaderPollerEventTypeComplete;
             }
             }
         }
         }