From 31894f4584c416aee4f41b99568698bb6f62a85e Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 3 Dec 2023 13:54:15 -0800 Subject: [PATCH] Virtual processing with Seader credential file --- sam_api.c | 102 ++++++++--------------- scenes/seader_scene_read_card_success.c | 2 + scenes/seader_scene_saved_menu.c | 13 ++- scenes/seader_scene_virtual_credential.c | 7 +- seader_credential.c | 7 +- seader_worker.c | 29 ++++--- 6 files changed, 71 insertions(+), 89 deletions(-) diff --git a/sam_api.c b/sam_api.c index 0c2127a..1cd84c3 100644 --- a/sam_api.c +++ b/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); + +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_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len) { SeaderWorker* seader_worker = seader->worker; SeaderUartBridge* seader_uart = seader_worker->uart; - memset(display, 0, sizeof(display)); - for(uint8_t i = 0; i < len; i++) { - snprintf(display + (i * 2), sizeof(display), "%02x", buffer[i]); - } - FURI_LOG_D(TAG, "Picopass State Macine %d: %s", len, display); - 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, diff --git a/scenes/seader_scene_read_card_success.c b/scenes/seader_scene_read_card_success.c index f28cfb0..8942a0f 100644 --- a/scenes/seader_scene_read_card_success.c +++ b/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) { diff --git a/scenes/seader_scene_saved_menu.c b/scenes/seader_scene_saved_menu.c index 9c5b671..acea600 100644 --- a/scenes/seader_scene_saved_menu.c +++ b/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, diff --git a/scenes/seader_scene_virtual_credential.c b/scenes/seader_scene_virtual_credential.c index f16146d..d675939 100644 --- a/scenes/seader_scene_virtual_credential.c +++ b/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; } diff --git a/seader_credential.c b/seader_credential.c index 9e42cae..16ec0e0 100644 --- a/seader_credential.c +++ b/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); diff --git a/seader_worker.c b/seader_worker.c index cf27fd3..72d288f 100644 --- a/seader_worker.c +++ b/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); } }