Преглед на файлове

Virtual processing with Seader credential file

Eric Betts преди 2 години
родител
ревизия
31894f4584
променени са 6 файла, в които са добавени 71 реда и са изтрити 89 реда
  1. 34 68
      sam_api.c
  2. 2 0
      scenes/seader_scene_read_card_success.c
  3. 9 4
      scenes/seader_scene_saved_menu.c
  4. 5 2
      scenes/seader_scene_virtual_credential.c
  5. 4 3
      seader_credential.c
  6. 17 12
      seader_worker.c

+ 34 - 68
sam_api.c

@@ -6,6 +6,9 @@
 #define APDU_HEADER_LEN 5
 #define ASN1_PREFIX 6
 #define ASN1_DEBUG true
+#define SEADER_ICLASS_SR_SIO_BASE_BLOCK 10
+
+const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
 
 static char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0};
 char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0};
@@ -23,63 +26,48 @@ void* calloc(size_t count, size_t size) {
 
 // Forward declarations
 void seader_send_nfc_rx(SeaderUartBridge* seader_uart, uint8_t* buffer, size_t len);
-PicopassError seader_worker_fake_epurse_update(BitBuffer* tx_buffer, BitBuffer* rx_buffer);
 
-void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len) {
-    SeaderWorker* seader_worker = seader->worker;
-    SeaderUartBridge* seader_uart = seader_worker->uart;
+PicopassError seader_worker_fake_epurse_update(BitBuffer* tx_buffer, BitBuffer* rx_buffer) {
+    const uint8_t* buffer = bit_buffer_get_data(tx_buffer);
+    uint8_t fake_response[8];
+    memset(fake_response, 0, sizeof(fake_response));
+    memcpy(fake_response + 0, buffer + 6, 4);
+    memcpy(fake_response + 4, buffer + 2, 4);
+
+    bit_buffer_append_bytes(rx_buffer, fake_response, sizeof(fake_response));
+    iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer);
 
     memset(display, 0, sizeof(display));
-    for(uint8_t i = 0; i < len; i++) {
-        snprintf(display + (i * 2), sizeof(display), "%02x", buffer[i]);
+    for(uint8_t i = 0; i < bit_buffer_get_size_bytes(rx_buffer); i++) {
+        snprintf(display + (i * 2), sizeof(display), "%02x", bit_buffer_get_data(rx_buffer)[i]);
     }
-    FURI_LOG_D(TAG, "Picopass State Macine %d: %s", len, display);
+    FURI_LOG_I(TAG, "Fake update E-Purse response: %s", display);
+
+    return PicopassErrorNone;
+}
+
+void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len) {
+    SeaderWorker* seader_worker = seader->worker;
+    SeaderUartBridge* seader_uart = seader_worker->uart;
 
     BitBuffer* tx_buffer = bit_buffer_alloc(len);
     bit_buffer_append_bytes(tx_buffer, buffer, len);
     BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE);
-    // TODO: have this come from the actual saved card
-    uint8_t csn[PICOPASS_BLOCK_LEN] = {0xf8, 0x7c, 0xd7, 0x12, 0xff, 0xff, 0x12, 0xe0};
+
     uint8_t sr_aia[PICOPASS_BLOCK_LEN] = {0xFF, 0xff, 0xff, 0xff, 0xFF, 0xFf, 0xff, 0xFF};
     uint8_t epurse[PICOPASS_BLOCK_LEN] = {0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff};
     uint8_t pacs_sr_cfg[PICOPASS_BLOCK_LEN] = {0xA3, 0x03, 0x03, 0x03, 0x00, 0x03, 0xe0, 0x14};
 
-    /*
-30 33 81 05 01 87 BC 0F
-45 A5 02 05 00 A6 08 81
-01 01 04 03 03 00 08 A7
-18 85 16 E0 8C 96 D4 1E
-26 55 12 79 6A 65 00 21
-C1 7D 19 27 CA 9F 80 35
-98 A9 02 05 00 05 00 00
- */
-
-    uint8_t sio_first[PICOPASS_BLOCK_LEN * 4] = {
-  0x30, 0x33, 0x81, 0x05, 0x01, 0x87, 0xbc, 0x0f,
-  0x45, 0xa5, 0x02, 0x05, 0x00, 0xa6, 0x08, 0x81,
-  0x01, 0x01, 0x04, 0x03, 0x03, 0x00, 0x08, 0xa7,
-  0x18, 0x85, 0x16, 0xe0, 0x8c, 0x96, 0xd4, 0x1e
-    };
-    // NOTE: 8 byte overlap
-    uint8_t sio_second[PICOPASS_BLOCK_LEN * 4] = {
-  0x18, 0x85, 0x16, 0xe0, 0x8c, 0x96, 0xd4, 0x1e,
-  0x26, 0x55, 0x12, 0x79, 0x6a, 0x65, 0x00, 0x21,
-  0xc1, 0x7d, 0x19, 0x27, 0xca, 0x9f, 0x80, 0x35,
-  0x98, 0xa9, 0x02, 0x05, 0x00, 0x05, 0x00, 0x00
-    };
-
-    const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
-
     uint8_t tmac[4] = {};
     uint8_t div_key[PICOPASS_BLOCK_LEN] = {};
+    uint8_t offset; // for READ4
 
     do {
         switch(buffer[0]) {
         case RFAL_PICOPASS_CMD_READ_OR_IDENTIFY:
-            // append_bytes(rx, seader->[picopass]->AA1[buffer[1]].data, PICOPASS_BLOCK_LEN);
-            if(buffer[1] == AIA_INDEX) { // TODO: _INDEX
+            if(buffer[1] == AIA_INDEX) {
                 bit_buffer_append_bytes(rx_buffer, sr_aia, sizeof(sr_aia));
-            } else if (buffer[1] == PACS_CFG_INDEX) {
+            } else if(buffer[1] == PACS_CFG_INDEX) {
                 bit_buffer_append_bytes(rx_buffer, pacs_sr_cfg, sizeof(pacs_sr_cfg));
             }
             iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer);
@@ -88,26 +76,23 @@ C1 7D 19 27 CA 9F 80 35
             seader_worker_fake_epurse_update(tx_buffer, rx_buffer);
             break;
         case RFAL_PICOPASS_CMD_READCHECK_KD:
-            if(buffer[1] == 2) { // TODO: _INDEX
+            if(buffer[1] == EPURSE_INDEX) {
                 bit_buffer_append_bytes(rx_buffer, epurse, sizeof(epurse));
             }
             break;
         case RFAL_PICOPASS_CMD_CHECK:
-            //memcpy(cc, data->AA1[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data, sizeof(PicopassBlock));
-            loclass_iclass_calc_div_key(csn, picopass_iclass_key, div_key, false);
+            loclass_iclass_calc_div_key(
+                seader->credential->diversifier, picopass_iclass_key, div_key, false);
             LoclassState_t cipher_state = loclass_opt_doTagMAC_1(epurse, div_key);
-            // loclass_opt_doBothMAC_2(cipher_state, rx_buffer+1, rmac, tmac, div_key);
-
             loclass_opt_doTagMAC_2(cipher_state, buffer + 1, tmac, div_key);
             bit_buffer_append_bytes(rx_buffer, tmac, sizeof(tmac));
             break;
         case RFAL_PICOPASS_CMD_READ4:
-            if(buffer[1] == 10) {
-                bit_buffer_append_bytes(rx_buffer, sio_first, sizeof(sio_first));
-            }
-            if(buffer[1] == 13) {
-                bit_buffer_append_bytes(rx_buffer, sio_second, sizeof(sio_second));
-            }
+            offset = buffer[1] - SEADER_ICLASS_SR_SIO_BASE_BLOCK;
+            bit_buffer_append_bytes(
+                rx_buffer,
+                seader->credential->sio + (PICOPASS_BLOCK_LEN * offset),
+                PICOPASS_BLOCK_LEN * 4);
             iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer);
             break;
         }
@@ -462,25 +447,6 @@ void seader_capture_sio(BitBuffer* tx_buffer, BitBuffer* rx_buffer, SeaderCreden
     }
 }
 
-PicopassError seader_worker_fake_epurse_update(BitBuffer* tx_buffer, BitBuffer* rx_buffer) {
-    const uint8_t* buffer = bit_buffer_get_data(tx_buffer);
-    uint8_t fake_response[8];
-    memset(fake_response, 0, sizeof(fake_response));
-    memcpy(fake_response + 0, buffer + 6, 4);
-    memcpy(fake_response + 4, buffer + 2, 4);
-
-    bit_buffer_append_bytes(rx_buffer, fake_response, sizeof(fake_response));
-    iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer);
-
-    memset(display, 0, sizeof(display));
-    for(uint8_t i = 0; i < bit_buffer_get_size_bytes(rx_buffer); i++) {
-        snprintf(display + (i * 2), sizeof(display), "%02x", bit_buffer_get_data(rx_buffer)[i]);
-    }
-    FURI_LOG_I(TAG, "Fake update E-Purse response: %s", display);
-
-    return PicopassErrorNone;
-}
-
 void seader_iso15693_transmit(
     Seader* seader,
     PicopassPoller* picopass_poller,

+ 2 - 0
scenes/seader_scene_read_card_success.c

@@ -37,6 +37,8 @@ void seader_scene_read_card_success_on_enter(void* context) {
 
         if(credential->type == SeaderCredentialTypeNone) {
             furi_string_set(type_str, "Unknown");
+        } else if(credential->type == SeaderCredentialTypeVirtual) {
+            furi_string_set(type_str, "Virtual");
         } else if(credential->type == SeaderCredentialType14A) {
             furi_string_set(type_str, "14443A");
         } else if(credential->type == SeaderCredentialTypePicopass) {

+ 9 - 4
scenes/seader_scene_saved_menu.c

@@ -14,17 +14,22 @@ void seader_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
 
 void seader_scene_saved_menu_on_enter(void* context) {
     Seader* seader = context;
+    SeaderCredential* credential = seader->credential;
     Submenu* submenu = seader->submenu;
-    UNUSED(submenu);
 
     submenu_add_item(
         submenu, "Delete", SubmenuIndexDelete, seader_scene_saved_menu_submenu_callback, seader);
     submenu_add_item(
         submenu, "Info", SubmenuIndexInfo, seader_scene_saved_menu_submenu_callback, seader);
 
-    // TODO: if iClass
-    submenu_add_item(
-        submenu, "Virtual", SubmenuIndexVirtual, seader_scene_saved_menu_submenu_callback, seader);
+    if(credential->sio[0] == 0x30) {
+        submenu_add_item(
+            submenu,
+            "Virtual",
+            SubmenuIndexVirtual,
+            seader_scene_saved_menu_submenu_callback,
+            seader);
+    }
 
     submenu_set_selected_item(
         seader->submenu,

+ 5 - 2
scenes/seader_scene_virtual_credential.c

@@ -14,7 +14,6 @@ void seader_scene_virtual_credential_on_enter(void* context) {
     popup_set_header(popup, "Processing\nvirtual\npicopass", 68, 30, AlignLeft, AlignTop);
 
     // Start worker
-    seader_credential_clear(seader->credential);
     seader->credential->type = SeaderCredentialTypeVirtual;
     seader_worker_start(
         seader->worker,
@@ -32,7 +31,11 @@ bool seader_scene_virtual_credential_on_event(void* context, SceneManagerEvent e
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SeaderCustomEventWorkerExit) {
-            seader->credential->type = SeaderCredentialTypePicopass;
+            seader->credential->type = SeaderCredentialTypeVirtual;
+            scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
+            consumed = true;
+        } else if(event.event == SeaderCustomEventPollerSuccess) {
+            seader->credential->type = SeaderCredentialTypeVirtual;
             scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
             consumed = true;
         }

+ 4 - 3
seader_credential.c

@@ -67,10 +67,11 @@ static bool seader_credential_load(SeaderCredential* cred, FuriString* path, boo
         // The order is reversed for storage and for the user opening the file
         uint64_t swapped = __builtin_bswap64(cred->credential);
         cred->credential = swapped;
+
         // Optional SIO/Diversifier
         flipper_format_read_hex(file, "SIO", cred->sio, sizeof(cred->sio));
         flipper_format_read_hex(file, "Diversifier", cred->diversifier, sizeof(cred->diversifier));
-        // seader->credential->type = SeaderCredentialTypeVirtual;
+
         parsed = true;
     } while(false);
 
@@ -85,8 +86,8 @@ static bool seader_credential_load(SeaderCredential* cred, FuriString* path, boo
             dialog_message_show_storage_error(cred->dialogs, "Can not parse\nfile");
         }
     }
-    if (parsed) {
-      FURI_LOG_I(TAG, "PACS: (%d) %016llx", cred->bit_length, cred->credential);
+    if(parsed) {
+        FURI_LOG_I(TAG, "PACS: (%d) %016llx", cred->bit_length, cred->credential);
     }
 
     furi_string_free(temp_str);

+ 17 - 12
seader_worker.c

@@ -154,13 +154,13 @@ bool seader_worker_process_sam_message(Seader* seader, CCID_Message* message) {
 void seader_worker_virtual_credential(Seader* seader) {
     SeaderWorker* seader_worker = seader->worker;
 
-    uint8_t csn[PICOPASS_BLOCK_LEN] = {0xf8, 0x7c, 0xd7, 0x12, 0xff, 0xff, 0x12, 0xe0};
-
     // Detect card
-    seader_worker_card_detect(seader, 0, NULL, csn, sizeof(PicopassSerialNum), NULL, 0);
+    seader_worker_card_detect(
+        seader, 0, NULL, seader->credential->diversifier, sizeof(PicopassSerialNum), NULL, 0);
 
     bool running = true;
-    uint8_t loops = 0;
+    // Max times the loop will run with no message to process
+    uint8_t dead_loops = 20;
 
     while(running) {
         if(furi_mutex_acquire(seader_worker->mq_mutex, 0) == FuriStatusOk) {
@@ -173,7 +173,6 @@ void seader_worker_virtual_credential(Seader* seader) {
                     furi_message_queue_get(seader_worker->messages, &seaderApdu, FuriWaitForever);
                 if(status != FuriStatusOk) {
                     FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status);
-                    seader_worker->stage = SeaderPollerEventTypeComplete;
                     view_dispatcher_send_custom_event(
                         seader->view_dispatcher, SeaderCustomEventWorkerExit);
                 }
@@ -184,16 +183,22 @@ void seader_worker_virtual_credential(Seader* seader) {
                     FURI_LOG_I(TAG, "Response false");
                     running = false;
                 }
-
-                // fake being SR
-                // picopass commands
-                // pacs
             }
             furi_mutex_release(seader_worker->mq_mutex);
+        } else {
+            dead_loops--;
+            furi_delay_ms(100);
+            running = (dead_loops > 0);
+            FURI_LOG_D(
+                TAG, "Dead loops: %d -> Running: %s", dead_loops, running ? "true" : "false");
         }
-        furi_delay_ms(100);
-        loops++;
-        running = (loops < 10);
+        running = (seader_worker->stage != SeaderPollerEventTypeComplete);
+    }
+
+    if(dead_loops > 0) {
+        view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventPollerSuccess);
+    } else {
+        view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
     }
 }