From 2e4f5abead82c712f652bef472b3788eb0390b54 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 2 Dec 2023 20:12:46 -0800 Subject: [PATCH 01/19] replace ccitt stuff with iso13239_crc_append --- sam_api.c | 36 ++++-------------------------------- sam_api.h | 2 ++ 2 files changed, 6 insertions(+), 32 deletions(-) diff --git a/sam_api.c b/sam_api.c index 498d02b..c94f419 100644 --- a/sam_api.c +++ b/sam_api.c @@ -339,32 +339,6 @@ void seader_send_nfc_rx(SeaderUartBridge* seader_uart, uint8_t* buffer, size_t l ASN_STRUCT_FREE(asn_DEF_Response, response); } -static uint16_t seader_worker_picopass_update_ccitt(uint16_t crcSeed, uint8_t dataByte) { - uint16_t crc = crcSeed; - uint8_t dat = dataByte; - - dat ^= (uint8_t)(crc & 0xFFU); - dat ^= (dat << 4); - - crc = (crc >> 8) ^ (((uint16_t)dat) << 8) ^ (((uint16_t)dat) << 3) ^ (((uint16_t)dat) >> 4); - - return crc; -} - -static uint16_t seader_worker_picopass_calculate_ccitt( - uint16_t preloadValue, - const uint8_t* buf, - uint16_t length) { - uint16_t crc = preloadValue; - uint16_t index; - - for(index = 0; index < length; index++) { - crc = seader_worker_picopass_update_ccitt(crc, buf[index]); - } - - return crc; -} - uint8_t read4Block6[] = {0x06, 0x06, 0x45, 0x56}; uint8_t read4Block9[] = {0x06, 0x09, 0xB2, 0xAE}; uint8_t read4Block10[] = {0x06, 0x0A, 0x29, 0x9C}; @@ -389,19 +363,17 @@ 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[10]; + 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); - uint16_t crc = seader_worker_picopass_calculate_ccitt(0xE012, fake_response, 8); - memcpy(fake_response + 8, &crc, sizeof(uint16_t)); - 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 < sizeof(fake_response); i++) { - snprintf(display + (i * 2), sizeof(display), "%02x", fake_response[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_I(TAG, "Fake update E-Purse response: %s", display); diff --git a/sam_api.h b/sam_api.h index ad63350..ca22baa 100644 --- a/sam_api.h +++ b/sam_api.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "seader_i.h" #include "seader_credential.h" #include "seader_bridge.h" From faef7b56f49522696f7e3c754fee288daf937b5d Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 2 Dec 2023 17:46:24 -0800 Subject: [PATCH 02/19] UI for virtual card --- scenes/seader_scene_config.h | 1 + scenes/seader_scene_saved_menu.c | 8 ++++ scenes/seader_scene_virtual_credential.c | 59 ++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 scenes/seader_scene_virtual_credential.c diff --git a/scenes/seader_scene_config.h b/scenes/seader_scene_config.h index 2d8dcb5..58914c1 100644 --- a/scenes/seader_scene_config.h +++ b/scenes/seader_scene_config.h @@ -14,3 +14,4 @@ ADD_SCENE(seader, delete, Delete) ADD_SCENE(seader, delete_success, DeleteSuccess) ADD_SCENE(seader, credential_info, CredentialInfo) ADD_SCENE(seader, sam_info, SamInfo) +ADD_SCENE(seader, virtual_credential, VirtualCredential) diff --git a/scenes/seader_scene_saved_menu.c b/scenes/seader_scene_saved_menu.c index d749ae9..9c5b671 100644 --- a/scenes/seader_scene_saved_menu.c +++ b/scenes/seader_scene_saved_menu.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexDelete, SubmenuIndexInfo, + SubmenuIndexVirtual, }; void seader_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -21,6 +22,10 @@ void seader_scene_saved_menu_on_enter(void* context) { 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); + submenu_set_selected_item( seader->submenu, scene_manager_get_scene_state(seader->scene_manager, SeaderSceneSavedMenu)); @@ -41,6 +46,9 @@ bool seader_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(seader->scene_manager, SeaderSceneCredentialInfo); consumed = true; + } else if(event.event == SubmenuIndexVirtual) { + scene_manager_next_scene(seader->scene_manager, SeaderSceneVirtualCredential); + consumed = true; } } diff --git a/scenes/seader_scene_virtual_credential.c b/scenes/seader_scene_virtual_credential.c new file mode 100644 index 0000000..c51fc79 --- /dev/null +++ b/scenes/seader_scene_virtual_credential.c @@ -0,0 +1,59 @@ +#include "../seader_i.h" +#include + +void seader_scene_virtual_credential_on_enter(void* context) { + Seader* seader = context; + dolphin_deed(DolphinDeedNfcRead); + + // Setup view + Popup* popup = seader->popup; + popup_set_header(popup, "Detecting\npicopass\ncard", 68, 30, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); + + // Start worker + view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); + + seader->worker->stage = SeaderPollerEventTypeCardDetect; + seader_credential_clear(seader->credential); + seader->picopass_poller = picopass_poller_alloc(seader->nfc); + picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader); + + seader_blink_start(seader); +} + +bool seader_scene_virtual_credential_on_event(void* context, SceneManagerEvent event) { + Seader* seader = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SeaderCustomEventWorkerExit) { + seader->credential->type = SeaderCredentialTypePicopass; + scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); + consumed = true; + } else if(event.event == SeaderCustomEventPollerSuccess) { + seader->credential->type = SeaderCredentialTypePicopass; + scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); + consumed = true; + } + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_search_and_switch_to_previous_scene( + seader->scene_manager, SeaderSceneSamPresent); + consumed = true; + } + return consumed; +} + +void seader_scene_virtual_credential_on_exit(void* context) { + Seader* seader = context; + + if(seader->picopass_poller) { + picopass_poller_stop(seader->picopass_poller); + picopass_poller_free(seader->picopass_poller); + } + + // Clear view + popup_reset(seader->popup); + + seader_blink_stop(seader); +} From acb848d98efb2e0ce4cac0fcdae4e8fbd1d21eb1 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 2 Dec 2023 18:03:10 -0800 Subject: [PATCH 03/19] WIP: detection --- scenes/seader_scene_virtual_credential.c | 37 +++++++---------- seader_credential.h | 1 + seader_worker.c | 51 ++++++++++++++++++++++++ seader_worker.h | 1 + 4 files changed, 68 insertions(+), 22 deletions(-) diff --git a/scenes/seader_scene_virtual_credential.c b/scenes/seader_scene_virtual_credential.c index c51fc79..f16146d 100644 --- a/scenes/seader_scene_virtual_credential.c +++ b/scenes/seader_scene_virtual_credential.c @@ -1,24 +1,29 @@ #include "../seader_i.h" #include +void seader_virtual_credential_worker_callback(SeaderWorkerEvent event, void* context) { + Seader* seader = context; + view_dispatcher_send_custom_event(seader->view_dispatcher, event); +} + void seader_scene_virtual_credential_on_enter(void* context) { Seader* seader = context; - dolphin_deed(DolphinDeedNfcRead); // Setup view Popup* popup = seader->popup; - popup_set_header(popup, "Detecting\npicopass\ncard", 68, 30, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); + popup_set_header(popup, "Processing\nvirtual\npicopass", 68, 30, AlignLeft, AlignTop); // Start worker - view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); - - seader->worker->stage = SeaderPollerEventTypeCardDetect; seader_credential_clear(seader->credential); - seader->picopass_poller = picopass_poller_alloc(seader->nfc); - picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader); + seader->credential->type = SeaderCredentialTypeVirtual; + seader_worker_start( + seader->worker, + SeaderWorkerStateVirtualCredential, + seader->uart, + seader_virtual_credential_worker_callback, + seader); - seader_blink_start(seader); + view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); } bool seader_scene_virtual_credential_on_event(void* context, SceneManagerEvent event) { @@ -30,15 +35,10 @@ bool seader_scene_virtual_credential_on_event(void* context, SceneManagerEvent e seader->credential->type = SeaderCredentialTypePicopass; scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); consumed = true; - } else if(event.event == SeaderCustomEventPollerSuccess) { - seader->credential->type = SeaderCredentialTypePicopass; - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); - consumed = true; } - } else if(event.type == SceneManagerEventTypeBack) { scene_manager_search_and_switch_to_previous_scene( - seader->scene_manager, SeaderSceneSamPresent); + seader->scene_manager, SeaderSceneSavedMenu); consumed = true; } return consumed; @@ -47,13 +47,6 @@ bool seader_scene_virtual_credential_on_event(void* context, SceneManagerEvent e void seader_scene_virtual_credential_on_exit(void* context) { Seader* seader = context; - if(seader->picopass_poller) { - picopass_poller_stop(seader->picopass_poller); - picopass_poller_free(seader->picopass_poller); - } - // Clear view popup_reset(seader->popup); - - seader_blink_stop(seader); } diff --git a/seader_credential.h b/seader_credential.h index ab9ffbf..ec8865d 100644 --- a/seader_credential.h +++ b/seader_credential.h @@ -18,6 +18,7 @@ typedef enum { SeaderCredentialType14A, // Might need to make 14a into "javacard" and add Desfire SeaderCredentialTypeMifareClassic, + SeaderCredentialTypeVirtual, } SeaderCredentialType; typedef enum { diff --git a/seader_worker.c b/seader_worker.c index 9449298..875a1e3 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -151,13 +151,64 @@ bool seader_worker_process_sam_message(Seader* seader, CCID_Message* message) { return false; } +void seader_worker_virtual_credential(Seader* seader) { + SeaderWorker* seader_worker = seader->worker; + + uint8_t csn[8] = {0xf8, 0x7c, 0xd7, 0x12, 0xff, 0xff, 0x12, 0xe0}; + + // Detect card + seader_worker_card_detect(seader, 0, NULL, csn, sizeof(PicopassSerialNum), NULL, 0); + + bool running = true; + uint8_t loops = 0; + + while(running) { + if(furi_mutex_acquire(seader_worker->mq_mutex, 0) == FuriStatusOk) { + uint32_t count = furi_message_queue_get_count(seader_worker->messages); + if(count > 0) { + FURI_LOG_D(TAG, "MessageQueue: %ld messages", count); + + SeaderAPDU seaderApdu = {}; + FuriStatus status = + 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); + } + if(seader_process_success_response_i( + seader, seaderApdu.buf, seaderApdu.len, true, NULL)) { + // no-op + } else { + FURI_LOG_I(TAG, "Response false"); + running = false; + } + + + // fake being SR + // picopass commands + // pacs + } + furi_mutex_release(seader_worker->mq_mutex); + } + furi_delay_ms(1000); + loops++; + running = (loops < 10); + } +} + int32_t seader_worker_task(void* context) { SeaderWorker* seader_worker = context; + Seader* seader = seader_worker->context; SeaderUartBridge* seader_uart = seader_worker->uart; if(seader_worker->state == SeaderWorkerStateCheckSam) { FURI_LOG_D(TAG, "Check for SAM"); seader_ccid_check_for_sam(seader_uart); + } else if(seader_worker->state == SeaderWorkerStateVirtualCredential) { + FURI_LOG_D(TAG, "Virtual Credential"); + seader_worker_virtual_credential(seader); } seader_worker_change_state(seader_worker, SeaderWorkerStateReady); diff --git a/seader_worker.h b/seader_worker.h index 92b9301..9868b20 100644 --- a/seader_worker.h +++ b/seader_worker.h @@ -17,6 +17,7 @@ typedef enum { SeaderWorkerStateReady, // Main worker states SeaderWorkerStateCheckSam, + SeaderWorkerStateVirtualCredential, // Transition SeaderWorkerStateStop, } SeaderWorkerState; From be889102c1520adc8e3c87ee8e452d653ee658f7 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 2 Dec 2023 19:39:28 -0800 Subject: [PATCH 04/19] picopass state machine --- sam_api.c | 68 ++++++++++++++++++++++++++++++++++++++++++++----- sam_api.h | 1 + seader_worker.c | 15 +++++------ 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/sam_api.c b/sam_api.c index c94f419..76d42e6 100644 --- a/sam_api.c +++ b/sam_api.c @@ -11,10 +11,68 @@ static char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0}; char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0}; bool requestPacs = true; +uint8_t read4Block6[] = {0x06, 0x06, 0x45, 0x56}; +uint8_t read4Block9[] = {0x06, 0x09, 0xB2, 0xAE}; +uint8_t read4Block10[] = {0x06, 0x0A, 0x29, 0x9C}; +uint8_t read4Block13[] = {0x06, 0x0D, 0x96, 0xE8}; +uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02}; + void* calloc(size_t count, size_t size) { return malloc(count * 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); +static uint16_t seader_worker_picopass_calculate_ccitt( + uint16_t preloadValue, + const uint8_t* buf, + uint16_t length); + +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); + uint8_t sr_aia[PICOPASS_BLOCK_LEN + 2] = { + 0xFF, 0xff, 0xff, 0xff, 0xFF, 0xFf, 0xff, 0xFF, 0x00, 0x00}; + uint8_t epurse[PICOPASS_BLOCK_LEN] = {0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff}; + + 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] == 5) { // TODO: _INDEX + uint16_t crc = seader_worker_picopass_calculate_ccitt(0xE012, sr_aia, 8); + memcpy(sr_aia + 8, &crc, sizeof(uint16_t)); + bit_buffer_append_bytes(rx_buffer, sr_aia, sizeof(sr_aia)); + } + break; + case RFAL_PICOPASS_CMD_UPDATE: + seader_worker_fake_epurse_update(tx_buffer, rx_buffer); + break; + case RFAL_PICOPASS_CMD_READCHECK_KD: + if(buffer[1] == 2) { // TODO: _INDEX + bit_buffer_append_bytes(rx_buffer, epurse, sizeof(epurse)); + } + break; + case RFAL_PICOPASS_CMD_CHECK: + + break; + } + + seader_send_nfc_rx( + seader_uart, + (uint8_t*)bit_buffer_get_data(rx_buffer), + bit_buffer_get_size_bytes(rx_buffer)); + + } while(false); + bit_buffer_free(tx_buffer); + bit_buffer_free(rx_buffer); +} + bool seader_send_apdu( SeaderUartBridge* seader_uart, uint8_t CLA, @@ -339,12 +397,6 @@ void seader_send_nfc_rx(SeaderUartBridge* seader_uart, uint8_t* buffer, size_t l ASN_STRUCT_FREE(asn_DEF_Response, response); } -uint8_t read4Block6[] = {0x06, 0x06, 0x45, 0x56}; -uint8_t read4Block9[] = {0x06, 0x09, 0xB2, 0xAE}; -uint8_t read4Block10[] = {0x06, 0x0A, 0x29, 0x9C}; -uint8_t read4Block13[] = {0x06, 0x0D, 0x96, 0xE8}; -uint8_t updateBlock2[] = {0x87, 0x02}; // TODO - void seader_capture_sio(BitBuffer* tx_buffer, BitBuffer* rx_buffer, SeaderCredential* credential) { const uint8_t* buffer = bit_buffer_get_data(tx_buffer); size_t len = bit_buffer_get_size_bytes(tx_buffer); @@ -491,7 +543,9 @@ void seader_parse_nfc_command_transmit( frameProtocol); #endif - if(frameProtocol == FrameProtocol_iclass) { + if(seader->credential->type == SeaderCredentialTypeVirtual) { + seader_picopass_state_machine(seader, nfcSend->data.buf, nfcSend->data.size); + } else if(frameProtocol == FrameProtocol_iclass) { seader_iso15693_transmit( seader, spc->picopass_poller, nfcSend->data.buf, nfcSend->data.size); } else if(frameProtocol == FrameProtocol_nfc) { diff --git a/sam_api.h b/sam_api.h index ca22baa..6681d1e 100644 --- a/sam_api.h +++ b/sam_api.h @@ -6,6 +6,7 @@ #include "seader_credential.h" #include "seader_bridge.h" #include "seader_worker.h" +#include "protocol/rfal_picopass.h" #include diff --git a/seader_worker.c b/seader_worker.c index 875a1e3..36d6888 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -177,14 +177,13 @@ void seader_worker_virtual_credential(Seader* seader) { view_dispatcher_send_custom_event( seader->view_dispatcher, SeaderCustomEventWorkerExit); } - if(seader_process_success_response_i( - seader, seaderApdu.buf, seaderApdu.len, true, NULL)) { - // no-op - } else { - FURI_LOG_I(TAG, "Response false"); - running = false; - } - + if(seader_process_success_response_i( + seader, seaderApdu.buf, seaderApdu.len, true, NULL)) { + // no-op + } else { + FURI_LOG_I(TAG, "Response false"); + running = false; + } // fake being SR // picopass commands From b5290e7b15219435a83214eee417f70fcfa69116 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 2 Dec 2023 20:09:39 -0800 Subject: [PATCH 05/19] add loclass --- application.fam | 4 + lib/loclass/optimized_cipher.c | 319 +++++++++++++++++++++++++++ lib/loclass/optimized_cipher.h | 117 ++++++++++ lib/loclass/optimized_cipherutils.c | 136 ++++++++++++ lib/loclass/optimized_cipherutils.h | 64 ++++++ lib/loclass/optimized_elite.c | 232 ++++++++++++++++++++ lib/loclass/optimized_elite.h | 58 +++++ lib/loclass/optimized_ikeys.c | 320 ++++++++++++++++++++++++++++ lib/loclass/optimized_ikeys.h | 66 ++++++ 9 files changed, 1316 insertions(+) create mode 100644 lib/loclass/optimized_cipher.c create mode 100644 lib/loclass/optimized_cipher.h create mode 100644 lib/loclass/optimized_cipherutils.c create mode 100644 lib/loclass/optimized_cipherutils.h create mode 100644 lib/loclass/optimized_elite.c create mode 100644 lib/loclass/optimized_elite.h create mode 100644 lib/loclass/optimized_ikeys.c create mode 100644 lib/loclass/optimized_ikeys.h diff --git a/application.fam b/application.fam index d47dd9e..a78c28a 100644 --- a/application.fam +++ b/application.fam @@ -30,6 +30,10 @@ App( name="asn1", cflags=["-Wno-error"], ), + Lib( + name="loclass", + cflags=["-O3"], + ), ], fap_weburl="https://seader.ericbetts.dev", fap_icon_assets="icons", diff --git a/lib/loclass/optimized_cipher.c b/lib/loclass/optimized_cipher.c new file mode 100644 index 0000000..e42dc08 --- /dev/null +++ b/lib/loclass/optimized_cipher.c @@ -0,0 +1,319 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +/* + This file contains an optimized version of the MAC-calculation algorithm. Some measurements on + a std laptop showed it runs in about 1/3 of the time: + + Std: 0.428962 + Opt: 0.151609 + + Additionally, it is self-reliant, not requiring e.g. bitstreams from the cipherutils, thus can + be easily dropped into a code base. + + The optimizations have been performed in the following steps: + * Parameters passed by reference instead of by value. + * Iteration instead of recursion, un-nesting recursive loops into for-loops. + * Handling of bytes instead of individual bits, for less shuffling and masking + * Less creation of "objects", structs, and instead reuse of alloc:ed memory + * Inlining some functions via #define:s + + As a consequence, this implementation is less generic. Also, I haven't bothered documenting this. + For a thorough documentation, check out the MAC-calculation within cipher.c instead. + + -- MHS 2015 +**/ + +/** + + The runtime of opt_doTagMAC_2() with the MHS optimized version was 403 microseconds on Proxmark3. + This was still to slow for some newer readers which didn't want to wait that long. + + Further optimizations to speedup the MAC calculations: + * Optimized opt_Tt logic + * Look up table for opt_select + * Removing many unnecessary bit maskings (& 0x1) + * updating state in place instead of alternating use of a second state structure + * remove the necessity to reverse bits of input and output bytes + + opt_doTagMAC_2() now completes in 270 microseconds. + + -- piwi 2019 +**/ + +/** + add the possibility to do iCLASS on device only + -- iceman 2020 +**/ + +#include "optimized_cipher.h" +#include "optimized_elite.h" +#include "optimized_ikeys.h" +#include "optimized_cipherutils.h" + +static const uint8_t loclass_opt_select_LUT[256] = { + 00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04, 01, 02, 03, 00, 02, 03, 00, 01, + 05, 06, 06, 05, 06, 07, 05, 04, 06, 05, 04, 07, 04, 05, 06, 07, 06, 05, 05, 06, 04, 05, 07, 06, + 07, 04, 05, 06, 04, 05, 06, 07, 07, 04, 04, 07, 04, 05, 07, 06, 06, 05, 04, 07, 04, 05, 06, 07, + 02, 01, 01, 02, 00, 01, 03, 02, 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, + 00, 03, 02, 01, 02, 03, 00, 01, 00, 03, 03, 00, 02, 03, 01, 00, 05, 06, 07, 04, 06, 07, 04, 05, + 05, 06, 06, 05, 06, 07, 05, 04, 02, 01, 00, 03, 00, 01, 02, 03, 06, 05, 05, 06, 04, 05, 07, 06, + 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, 02, 01, 00, 03, 00, 01, 02, 03, + 02, 01, 01, 02, 00, 01, 03, 02, 03, 00, 01, 02, 00, 01, 02, 03, 03, 00, 00, 03, 00, 01, 03, 02, + 04, 07, 06, 05, 06, 07, 04, 05, 00, 03, 03, 00, 02, 03, 01, 00, 01, 02, 03, 00, 02, 03, 00, 01, + 05, 06, 06, 05, 06, 07, 05, 04, 04, 07, 06, 05, 06, 07, 04, 05, 04, 07, 07, 04, 06, 07, 05, 04, + 01, 02, 03, 00, 02, 03, 00, 01, 01, 02, 02, 01, 02, 03, 01, 00}; + +/********************** the table above has been generated with this code: ******** +#include "util.h" +static void init_opt_select_LUT(void) { + for (int r = 0; r < 256; r++) { + uint8_t r_ls2 = r << 2; + uint8_t r_and_ls2 = r & r_ls2; + uint8_t r_or_ls2 = r | r_ls2; + uint8_t z0 = (r_and_ls2 >> 5) ^ ((r & ~r_ls2) >> 4) ^ ( r_or_ls2 >> 3); + uint8_t z1 = (r_or_ls2 >> 6) ^ ( r_or_ls2 >> 1) ^ (r >> 5) ^ r; + uint8_t z2 = ((r & ~r_ls2) >> 4) ^ (r_and_ls2 >> 3) ^ r; + loclass_opt_select_LUT[r] = (z0 & 4) | (z1 & 2) | (z2 & 1); + } + print_result("", loclass_opt_select_LUT, 256); +} +***********************************************************************************/ + +static inline void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) { + uint16_t Tt = s->t & 0xc533; + Tt = Tt ^ (Tt >> 1); + Tt = Tt ^ (Tt >> 4); + Tt = Tt ^ (Tt >> 10); + Tt = Tt ^ (Tt >> 8); + + s->t = (s->t >> 1); + s->t |= (Tt ^ (s->r >> 7) ^ (s->r >> 3)) << 15; + + uint8_t opt_B = s->b; + opt_B ^= s->b >> 6; + opt_B ^= s->b >> 5; + opt_B ^= s->b >> 4; + + s->b = s->b >> 1; + s->b |= (opt_B ^ s->r) << 7; + + uint8_t Tt1 = Tt & 0x01; + uint8_t opt_select = loclass_opt_select_LUT[s->r] ^ Tt1 ^ ((Tt1 ^ (y & 0x01)) << 1); + + uint8_t r = s->r; + s->r = (k[opt_select] ^ s->b) + s->l; + s->l = s->r + r; +} + +static inline void loclass_opt_suc( + const uint8_t* k, + LoclassState_t* s, + const uint8_t* in, + uint8_t length, + bool add32Zeroes) { + for(int i = 0; i < length; i++) { + uint8_t head = in[i]; +#pragma GCC unroll 8 + for(int j = 0; j < 8; j++) { + loclass_opt_successor(k, s, head); + head >>= 1; + } + } + // For tag MAC, an additional 32 zeroes + if(add32Zeroes) { + for(int i = 0; i < 32; i++) { + loclass_opt_successor(k, s, 0); + } + } +} + +static inline void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) { +#pragma GCC unroll 4 + for(uint8_t times = 0; times < 4; times++) { + uint8_t bout = 0; + bout |= (s->r & 0x4) >> 2; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) >> 1; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4); + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 1; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 2; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 3; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 4; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 5; + loclass_opt_successor(k, s, 0); + buffer[times] = bout; + } +} + +static void loclass_opt_MAC(uint8_t* k, uint8_t* input, uint8_t* out) { + LoclassState_t _init = { + ((k[0] ^ 0x4c) + 0xEC) & 0xFF, // l + ((k[0] ^ 0x4c) + 0x21) & 0xFF, // r + 0x4c, // b + 0xE012 // t + }; + + loclass_opt_suc(k, &_init, input, 12, false); + loclass_opt_output(k, &_init, out); +} + +static void loclass_opt_MAC_N(uint8_t* k, uint8_t* input, uint8_t in_size, uint8_t* out) { + LoclassState_t _init = { + ((k[0] ^ 0x4c) + 0xEC) & 0xFF, // l + ((k[0] ^ 0x4c) + 0x21) & 0xFF, // r + 0x4c, // b + 0xE012 // t + }; + + loclass_opt_suc(k, &_init, input, in_size, false); + loclass_opt_output(k, &_init, out); +} + +void loclass_opt_doReaderMAC(uint8_t* cc_nr_p, uint8_t* div_key_p, uint8_t mac[4]) { + uint8_t dest[] = {0, 0, 0, 0, 0, 0, 0, 0}; + loclass_opt_MAC(div_key_p, cc_nr_p, dest); + memcpy(mac, dest, 4); +} + +void loclass_opt_doReaderMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t mac[4], + const uint8_t* div_key_p) { + loclass_opt_suc(div_key_p, &_init, nr, 4, false); + loclass_opt_output(div_key_p, &_init, mac); +} + +void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]) { + uint8_t dest[] = {0, 0, 0, 0, 0, 0, 0, 0}; + loclass_opt_MAC_N(div_key_p, in_p, in_size, dest); + memcpy(mac, dest, 4); +} + +void loclass_opt_doTagMAC(uint8_t* cc_p, const uint8_t* div_key_p, uint8_t mac[4]) { + LoclassState_t _init = { + ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF, // l + ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF, // r + 0x4c, // b + 0xE012 // t + }; + loclass_opt_suc(div_key_p, &_init, cc_p, 12, true); + loclass_opt_output(div_key_p, &_init, mac); +} + +/** + * The tag MAC can be divided (both can, but no point in dividing the reader mac) into + * two functions, since the first 8 bytes are known, we can pre-calculate the state + * reached after feeding CC to the cipher. + * @param cc_p + * @param div_key_p + * @return the cipher state + */ +LoclassState_t loclass_opt_doTagMAC_1(uint8_t* cc_p, const uint8_t* div_key_p) { + LoclassState_t _init = { + ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF, // l + ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF, // r + 0x4c, // b + 0xE012 // t + }; + loclass_opt_suc(div_key_p, &_init, cc_p, 8, false); + return _init; +} + +/** + * The second part of the tag MAC calculation, since the CC is already calculated into the state, + * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag + * MAC response. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param mac - where to store the MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doTagMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t mac[4], + const uint8_t* div_key_p) { + loclass_opt_suc(div_key_p, &_init, nr, 4, true); + loclass_opt_output(div_key_p, &_init, mac); +} + +/** + * The second part of the tag MAC calculation, since the CC is already calculated into the state, + * this function is fed only the NR, and generates both the reader and tag MACs. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param rmac - where to store the reader MAC + * @param tmac - where to store the tag MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doBothMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t rmac[4], + uint8_t tmac[4], + const uint8_t* div_key_p) { + LoclassState_t* s = &_init; + loclass_opt_suc(div_key_p, s, nr, 4, false); + loclass_opt_output(div_key_p, s, rmac); + loclass_opt_output(div_key_p, s, tmac); +} + +void loclass_iclass_calc_div_key( + const uint8_t* csn, + const uint8_t* key, + uint8_t* div_key, + bool elite) { + if(elite) { + uint8_t keytable[128] = {0}; + uint8_t key_index[8] = {0}; + uint8_t key_sel[8] = {0}; + uint8_t key_sel_p[8] = {0}; + loclass_hash2(key, keytable); + loclass_hash1(csn, key_index); + for(uint8_t i = 0; i < 8; i++) key_sel[i] = keytable[key_index[i]]; + + //Permute from iclass format to standard format + loclass_permutekey_rev(key_sel, key_sel_p); + loclass_diversifyKey(csn, key_sel_p, div_key); + } else { + loclass_diversifyKey(csn, key, div_key); + } +} diff --git a/lib/loclass/optimized_cipher.h b/lib/loclass/optimized_cipher.h new file mode 100644 index 0000000..5830bec --- /dev/null +++ b/lib/loclass/optimized_cipher.h @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// More recently from https://github.com/RfidResearchGroup/proxmark3 +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#ifndef OPTIMIZED_CIPHER_H +#define OPTIMIZED_CIPHER_H +#include +#include +#include +#include + +/** +* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 +* consisting of the following four components: +* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ; +* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ; +* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 . +* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 . +**/ +typedef struct { + uint8_t l; + uint8_t r; + uint8_t b; + uint16_t t; +} LoclassState_t; + +/** The reader MAC is MAC(key, CC * NR ) + **/ +void loclass_opt_doReaderMAC(uint8_t* cc_nr_p, uint8_t* div_key_p, uint8_t mac[4]); + +void loclass_opt_doReaderMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t mac[4], + const uint8_t* div_key_p); + +/** + * The tag MAC is MAC(key, CC * NR * 32x0)) + */ +void loclass_opt_doTagMAC(uint8_t* cc_p, const uint8_t* div_key_p, uint8_t mac[4]); + +/** + * The tag MAC can be divided (both can, but no point in dividing the reader mac) into + * two functions, since the first 8 bytes are known, we can pre-calculate the state + * reached after feeding CC to the cipher. + * @param cc_p + * @param div_key_p + * @return the cipher state + */ +LoclassState_t loclass_opt_doTagMAC_1(uint8_t* cc_p, const uint8_t* div_key_p); +/** + * The second part of the tag MAC calculation, since the CC is already calculated into the state, + * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag + * MAC response. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param mac - where to store the MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doTagMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t mac[4], + const uint8_t* div_key_p); + +/** + * The same as loclass_opt_doTagMAC_2, but calculates both the reader and tag MACs at the same time + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param rmac - where to store the reader MAC + * @param tmac - where to store the tag MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doBothMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t rmac[4], + uint8_t tmac[4], + const uint8_t* div_key_p); + +void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]); +void loclass_iclass_calc_div_key( + const uint8_t* csn, + const uint8_t* key, + uint8_t* div_key, + bool elite); +#endif // OPTIMIZED_CIPHER_H diff --git a/lib/loclass/optimized_cipherutils.c b/lib/loclass/optimized_cipherutils.c new file mode 100644 index 0000000..e6a87c4 --- /dev/null +++ b/lib/loclass/optimized_cipherutils.c @@ -0,0 +1,136 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#include "optimized_cipherutils.h" +#include + +/** + * + * @brief Return and remove the first bit (x0) in the stream : + * @param stream + * @return + */ +bool loclass_headBit(LoclassBitstreamIn_t* stream) { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = (stream->position++) & 7; // mask out 00000111 + return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; +} +/** + * @brief Return and remove the last bit (xn) in the stream: + * @param stream + * @return + */ +bool loclass_tailBit(LoclassBitstreamIn_t* stream) { + int bitpos = stream->numbits - 1 - (stream->position++); + + int bytepos = bitpos >> 3; + bitpos &= 7; + return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; +} +/** + * @brief Pushes bit onto the stream + * @param stream + * @param bit + */ +void loclass_pushBit(LoclassBitstreamOut_t* stream, bool bit) { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = stream->position & 7; + *(stream->buffer + bytepos) |= (bit) << (7 - bitpos); + stream->position++; + stream->numbits++; +} + +/** + * @brief Pushes the lower six bits onto the stream + * as b0 b1 b2 b3 b4 b5 b6 + * @param stream + * @param bits + */ +void loclass_push6bits(LoclassBitstreamOut_t* stream, uint8_t bits) { + loclass_pushBit(stream, bits & 0x20); + loclass_pushBit(stream, bits & 0x10); + loclass_pushBit(stream, bits & 0x08); + loclass_pushBit(stream, bits & 0x04); + loclass_pushBit(stream, bits & 0x02); + loclass_pushBit(stream, bits & 0x01); +} + +/** + * @brief loclass_bitsLeft + * @param stream + * @return number of bits left in stream + */ +int loclass_bitsLeft(LoclassBitstreamIn_t* stream) { + return stream->numbits - stream->position; +} +/** + * @brief numBits + * @param stream + * @return Number of bits stored in stream + */ +void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t* dest) { + while(len--) { + dest[len] = (uint8_t)n; + n >>= 8; + } +} + +uint64_t loclass_x_bytes_to_num(uint8_t* src, size_t len) { + uint64_t num = 0; + while(len--) { + num = (num << 8) | (*src); + src++; + } + return num; +} + +uint8_t loclass_reversebytes(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + +void loclass_reverse_arraybytes(uint8_t* arr, size_t len) { + uint8_t i; + for(i = 0; i < len; i++) { + arr[i] = loclass_reversebytes(arr[i]); + } +} + +void loclass_reverse_arraycopy(uint8_t* arr, uint8_t* dest, size_t len) { + uint8_t i; + for(i = 0; i < len; i++) { + dest[i] = loclass_reversebytes(arr[i]); + } +} diff --git a/lib/loclass/optimized_cipherutils.h b/lib/loclass/optimized_cipherutils.h new file mode 100644 index 0000000..05b6820 --- /dev/null +++ b/lib/loclass/optimized_cipherutils.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// More recently from https://github.com/RfidResearchGroup/proxmark3 +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#ifndef CIPHERUTILS_H +#define CIPHERUTILS_H +#include +#include +#include + +typedef struct { + uint8_t* buffer; + uint8_t numbits; + uint8_t position; +} LoclassBitstreamIn_t; + +typedef struct { + uint8_t* buffer; + uint8_t numbits; + uint8_t position; +} LoclassBitstreamOut_t; + +bool loclass_headBit(LoclassBitstreamIn_t* stream); +bool loclass_tailBit(LoclassBitstreamIn_t* stream); +void loclass_pushBit(LoclassBitstreamOut_t* stream, bool bit); +int loclass_bitsLeft(LoclassBitstreamIn_t* stream); + +void loclass_push6bits(LoclassBitstreamOut_t* stream, uint8_t bits); +void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t* dest); +uint64_t loclass_x_bytes_to_num(uint8_t* src, size_t len); +uint8_t loclass_reversebytes(uint8_t b); +void loclass_reverse_arraybytes(uint8_t* arr, size_t len); +void loclass_reverse_arraycopy(uint8_t* arr, uint8_t* dest, size_t len); +#endif // CIPHERUTILS_H diff --git a/lib/loclass/optimized_elite.c b/lib/loclass/optimized_elite.c new file mode 100644 index 0000000..e198a41 --- /dev/null +++ b/lib/loclass/optimized_elite.c @@ -0,0 +1,232 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#include "optimized_elite.h" + +#include +#include +#include +#include +#include "optimized_ikeys.h" + +/** + * @brief Permutes a key from standard NIST format to Iclass specific format + * from http://www.proxmark.org/forum/viewtopic.php?pid=11220#p11220 + * + * If you loclass_permute [6c 8d 44 f9 2a 2d 01 bf] you get [8a 0d b9 88 bb a7 90 ea] as shown below. + * + * 1 0 1 1 1 1 1 1 bf + * 0 0 0 0 0 0 0 1 01 + * 0 0 1 0 1 1 0 1 2d + * 0 0 1 0 1 0 1 0 2a + * 1 1 1 1 1 0 0 1 f9 + * 0 1 0 0 0 1 0 0 44 + * 1 0 0 0 1 1 0 1 8d + * 0 1 1 0 1 1 0 0 6c + * + * 8 0 b 8 b a 9 e + * a d 9 8 b 7 0 a + * + * @param key + * @param dest + */ +void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]) { + int i; + for(i = 0; i < 8; i++) { + dest[i] = (((key[7] & (0x80 >> i)) >> (7 - i)) << 7) | + (((key[6] & (0x80 >> i)) >> (7 - i)) << 6) | + (((key[5] & (0x80 >> i)) >> (7 - i)) << 5) | + (((key[4] & (0x80 >> i)) >> (7 - i)) << 4) | + (((key[3] & (0x80 >> i)) >> (7 - i)) << 3) | + (((key[2] & (0x80 >> i)) >> (7 - i)) << 2) | + (((key[1] & (0x80 >> i)) >> (7 - i)) << 1) | + (((key[0] & (0x80 >> i)) >> (7 - i)) << 0); + } +} +/** + * Permutes a key from iclass specific format to NIST format + * @brief loclass_permutekey_rev + * @param key + * @param dest + */ +void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]) { + int i; + for(i = 0; i < 8; i++) { + dest[7 - i] = (((key[0] & (0x80 >> i)) >> (7 - i)) << 7) | + (((key[1] & (0x80 >> i)) >> (7 - i)) << 6) | + (((key[2] & (0x80 >> i)) >> (7 - i)) << 5) | + (((key[3] & (0x80 >> i)) >> (7 - i)) << 4) | + (((key[4] & (0x80 >> i)) >> (7 - i)) << 3) | + (((key[5] & (0x80 >> i)) >> (7 - i)) << 2) | + (((key[6] & (0x80 >> i)) >> (7 - i)) << 1) | + (((key[7] & (0x80 >> i)) >> (7 - i)) << 0); + } +} + +/** + * Helper function for loclass_hash1 + * @brief loclass_rr + * @param val + * @return + */ +static uint8_t loclass_rr(uint8_t val) { + return val >> 1 | ((val & 1) << 7); +} + +/** + * Helper function for loclass_hash1 + * @brief rl + * @param val + * @return + */ +static uint8_t loclass_rl(uint8_t val) { + return val << 1 | ((val & 0x80) >> 7); +} + +/** + * Helper function for loclass_hash1 + * @brief loclass_swap + * @param val + * @return + */ +static uint8_t loclass_swap(uint8_t val) { + return ((val >> 4) & 0xFF) | ((val & 0xFF) << 4); +} + +/** + * Hash1 takes CSN as input, and determines what bytes in the keytable will be used + * when constructing the K_sel. + * @param csn the CSN used + * @param k output + */ +void loclass_hash1(const uint8_t csn[], uint8_t k[]) { + k[0] = csn[0] ^ csn[1] ^ csn[2] ^ csn[3] ^ csn[4] ^ csn[5] ^ csn[6] ^ csn[7]; + k[1] = csn[0] + csn[1] + csn[2] + csn[3] + csn[4] + csn[5] + csn[6] + csn[7]; + k[2] = loclass_rr(loclass_swap(csn[2] + k[1])); + k[3] = loclass_rl(loclass_swap(csn[3] + k[0])); + k[4] = ~loclass_rr(csn[4] + k[2]) + 1; + k[5] = ~loclass_rl(csn[5] + k[3]) + 1; + k[6] = loclass_rr(csn[6] + (k[4] ^ 0x3c)); + k[7] = loclass_rl(csn[7] + (k[5] ^ 0xc3)); + + k[7] &= 0x7F; + k[6] &= 0x7F; + k[5] &= 0x7F; + k[4] &= 0x7F; + k[3] &= 0x7F; + k[2] &= 0x7F; + k[1] &= 0x7F; + k[0] &= 0x7F; +} +/** +Definition 14. Define the rotate key function loclass_rk : (F 82 ) 8 × N → (F 82 ) 8 as +loclass_rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] +loclass_rk(x [0] . . . x [7] , n + 1) = loclass_rk(loclass_rl(x [0] ) . . . loclass_rl(x [7] ), n) +**/ +static void loclass_rk(const uint8_t* key, uint8_t n, uint8_t* outp_key) { + memcpy(outp_key, key, 8); + uint8_t j; + while(n-- > 0) { + for(j = 0; j < 8; j++) outp_key[j] = loclass_rl(outp_key[j]); + } + return; +} + +static mbedtls_des_context loclass_ctx_enc; +static mbedtls_des_context loclass_ctx_dec; + +static void loclass_desdecrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8_t* output) { + uint8_t key_std_format[8] = {0}; + loclass_permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_dec(&loclass_ctx_dec, key_std_format); + mbedtls_des_crypt_ecb(&loclass_ctx_dec, input, output); +} + +static void loclass_desencrypt_iclass(const uint8_t* iclass_key, uint8_t* input, uint8_t* output) { + uint8_t key_std_format[8] = {0}; + loclass_permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_enc(&loclass_ctx_enc, key_std_format); + mbedtls_des_crypt_ecb(&loclass_ctx_enc, input, output); +} + +/** + * @brief Insert uint8_t[8] custom master key to calculate hash2 and return key_select. + * @param key unpermuted custom key + * @param loclass_hash1 loclass_hash1 + * @param key_sel output key_sel=h[loclass_hash1[i]] + */ +void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable) { + /** + *Expected: + * High Security Key Table + + 00 F1 35 59 A1 0D 5A 26 7F 18 60 0B 96 8A C0 25 C1 + 10 BF A1 3B B0 FF 85 28 75 F2 1F C6 8F 0E 74 8F 21 + 20 14 7A 55 16 C8 A9 7D B3 13 0C 5D C9 31 8D A9 B2 + 30 A3 56 83 0F 55 7E DE 45 71 21 D2 6D C1 57 1C 9C + 40 78 2F 64 51 42 7B 64 30 FA 26 51 76 D3 E0 FB B6 + 50 31 9F BF 2F 7E 4F 94 B4 BD 4F 75 91 E3 1B EB 42 + 60 3F 88 6F B8 6C 2C 93 0D 69 2C D5 20 3C C1 61 95 + 70 43 08 A0 2F FE B3 26 D7 98 0B 34 7B 47 70 A0 AB + + **** The 64-bit HS Custom Key Value = 5B7C62C491C11B39 ******/ + uint8_t key64_negated[8] = {0}; + uint8_t z[8][8] = {{0}, {0}}; + uint8_t temp_output[8] = {0}; + + //calculate complement of key + int i; + for(i = 0; i < 8; i++) key64_negated[i] = ~key64[i]; + + // Once again, key is on iclass-format + loclass_desencrypt_iclass(key64, key64_negated, z[0]); + + uint8_t y[8][8] = {{0}, {0}}; + + // y[0]=DES_dec(z[0],~key) + // Once again, key is on iclass-format + loclass_desdecrypt_iclass(z[0], key64_negated, y[0]); + + for(i = 1; i < 8; i++) { + loclass_rk(key64, i, temp_output); + loclass_desdecrypt_iclass(temp_output, z[i - 1], z[i]); + loclass_desencrypt_iclass(temp_output, y[i - 1], y[i]); + } + + if(outp_keytable != NULL) { + for(i = 0; i < 8; i++) { + memcpy(outp_keytable + i * 16, y[i], 8); + memcpy(outp_keytable + 8 + i * 16, z[i], 8); + } + } +} diff --git a/lib/loclass/optimized_elite.h b/lib/loclass/optimized_elite.h new file mode 100644 index 0000000..fba512a --- /dev/null +++ b/lib/loclass/optimized_elite.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// More recently from https://github.com/RfidResearchGroup/proxmark3 +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#ifndef ELITE_CRACK_H +#define ELITE_CRACK_H + +#include +#include + +void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]); +/** + * Permutes a key from iclass specific format to NIST format + * @brief loclass_permutekey_rev + * @param key + * @param dest + */ +void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]); +/** + * Hash1 takes CSN as input, and determines what bytes in the keytable will be used + * when constructing the K_sel. + * @param csn the CSN used + * @param k output + */ +void loclass_hash1(const uint8_t* csn, uint8_t* k); +void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable); + +#endif diff --git a/lib/loclass/optimized_ikeys.c b/lib/loclass/optimized_ikeys.c new file mode 100644 index 0000000..d19f560 --- /dev/null +++ b/lib/loclass/optimized_ikeys.c @@ -0,0 +1,320 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- + +/** +From "Dismantling iclass": + This section describes in detail the built-in key diversification algorithm of iClass. + Besides the obvious purpose of deriving a card key from a master key, this + algorithm intends to circumvent weaknesses in the cipher by preventing the + usage of certain ‘weak’ keys. In order to compute a diversified key, the iClass + reader first encrypts the card identity id with the master key K, using single + DES. The resulting ciphertext is then input to a function called loclass_hash0 which + outputs the diversified key k. + + k = loclass_hash0(DES enc (id, K)) + + Here the DES encryption of id with master key K outputs a cryptogram c + of 64 bits. These 64 bits are divided as c = x, y, z [0] , . . . , z [7] ∈ F 82 × F 82 × (F 62 ) 8 + which is used as input to the loclass_hash0 function. This function introduces some + obfuscation by performing a number of permutations, complement and modulo + operations, see Figure 2.5. Besides that, it checks for and removes patterns like + similar key bytes, which could produce a strong bias in the cipher. Finally, the + output of loclass_hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 . + +**/ +#include "optimized_ikeys.h" + +#include +#include +#include +#include +#include "optimized_cipherutils.h" + +static const uint8_t loclass_pi[35] = {0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, + 0x33, 0x35, 0x39, 0x36, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, + 0x4E, 0x53, 0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, + 0x66, 0x69, 0x6A, 0x6C, 0x71, 0x72, 0x74, 0x78}; + +/** + * @brief The key diversification algorithm uses 6-bit bytes. + * This implementation uses 64 bit uint to pack seven of them into one + * variable. When they are there, they are placed as follows: + * XXXX XXXX N0 .... N7, occupying the last 48 bits. + * + * This function picks out one from such a collection + * @param all + * @param n bitnumber + * @return + */ +static uint8_t loclass_getSixBitByte(uint64_t c, int n) { + return (c >> (42 - 6 * n)) & 0x3F; +} + +/** + * @brief Puts back a six-bit 'byte' into a uint64_t. + * @param c buffer + * @param z the value to place there + * @param n bitnumber. + */ +static void loclass_pushbackSixBitByte(uint64_t* c, uint8_t z, int n) { + //0x XXXX YYYY ZZZZ ZZZZ ZZZZ + // ^z0 ^z7 + //z0: 1111 1100 0000 0000 + + uint64_t masked = z & 0x3F; + uint64_t eraser = 0x3F; + masked <<= 42 - 6 * n; + eraser <<= 42 - 6 * n; + + //masked <<= 6*n; + //eraser <<= 6*n; + + eraser = ~eraser; + (*c) &= eraser; + (*c) |= masked; +} +/** + * @brief Swaps the z-values. + * If the input value has format XYZ0Z1...Z7, the output will have the format + * XYZ7Z6...Z0 instead + * @param c + * @return + */ +static uint64_t loclass_swapZvalues(uint64_t c) { + uint64_t newz = 0; + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 0), 7); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 1), 6); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 2), 5); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 3), 4); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 4), 3); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 5), 2); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 6), 1); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 7), 0); + newz |= (c & 0xFFFF000000000000); + return newz; +} + +/** +* @return 4 six-bit bytes chunked into a uint64_t,as 00..00a0a1a2a3 +*/ +static uint64_t loclass_ck(int i, int j, uint64_t z) { + if(i == 1 && j == -1) { + // loclass_ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + return z; + } else if(j == -1) { + // loclass_ck(i, −1, z [0] . . . z [3] ) = loclass_ck(i − 1, i − 2, z [0] . . . z [3] ) + return loclass_ck(i - 1, i - 2, z); + } + + if(loclass_getSixBitByte(z, i) == loclass_getSixBitByte(z, j)) { + //loclass_ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ) + uint64_t newz = 0; + int c; + for(c = 0; c < 4; c++) { + uint8_t val = loclass_getSixBitByte(z, c); + if(c == i) + loclass_pushbackSixBitByte(&newz, j, c); + else + loclass_pushbackSixBitByte(&newz, val, c); + } + return loclass_ck(i, j - 1, newz); + } else { + return loclass_ck(i, j - 1, z); + } +} +/** + + Definition 8. + Let the function check : (F 62 ) 8 → (F 62 ) 8 be defined as + check(z [0] . . . z [7] ) = loclass_ck(3, 2, z [0] . . . z [3] ) · loclass_ck(3, 2, z [4] . . . z [7] ) + + where loclass_ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as + + loclass_ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + loclass_ck(i, −1, z [0] . . . z [3] ) = loclass_ck(i − 1, i − 2, z [0] . . . z [3] ) + loclass_ck(i, j, z [0] . . . z [3] ) = + loclass_ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ; + loclass_ck(i, j − 1, z [0] . . . z [3] ), otherwise + + otherwise. +**/ + +static uint64_t loclass_check(uint64_t z) { + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + + // loclass_ck(3, 2, z [0] . . . z [3] ) + uint64_t ck1 = loclass_ck(3, 2, z); + + // loclass_ck(3, 2, z [4] . . . z [7] ) + uint64_t ck2 = loclass_ck(3, 2, z << 24); + + //The loclass_ck function will place the values + // in the middle of z. + ck1 &= 0x00000000FFFFFF000000; + ck2 &= 0x00000000FFFFFF000000; + + return ck1 | ck2 >> 24; +} + +static void loclass_permute( + LoclassBitstreamIn_t* p_in, + uint64_t z, + int l, + int r, + LoclassBitstreamOut_t* out) { + if(loclass_bitsLeft(p_in) == 0) return; + + bool pn = loclass_tailBit(p_in); + if(pn) { // pn = 1 + uint8_t zl = loclass_getSixBitByte(z, l); + + loclass_push6bits(out, zl + 1); + loclass_permute(p_in, z, l + 1, r, out); + } else { // otherwise + uint8_t zr = loclass_getSixBitByte(z, r); + + loclass_push6bits(out, zr); + loclass_permute(p_in, z, l, r + 1, out); + } +} + +/** + * @brief + *Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void loclass_hash0(uint64_t c, uint8_t k[8]) { + c = loclass_swapZvalues(c); + + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + // x = 8 bits + // y = 8 bits + // z0-z7 6 bits each : 48 bits + uint8_t x = (c & 0xFF00000000000000) >> 56; + uint8_t y = (c & 0x00FF000000000000) >> 48; + uint64_t zP = 0; + + for(int n = 0; n < 4; n++) { + uint8_t zn = loclass_getSixBitByte(c, n); + uint8_t zn4 = loclass_getSixBitByte(c, n + 4); + uint8_t _zn = (zn % (63 - n)) + n; + uint8_t _zn4 = (zn4 % (64 - n)) + n; + loclass_pushbackSixBitByte(&zP, _zn, n); + loclass_pushbackSixBitByte(&zP, _zn4, n + 4); + } + + uint64_t zCaret = loclass_check(zP); + uint8_t p = loclass_pi[x % 35]; + + if(x & 1) //Check if x7 is 1 + p = ~p; + + LoclassBitstreamIn_t p_in = {&p, 8, 0}; + uint8_t outbuffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; + LoclassBitstreamOut_t out = {outbuffer, 0, 0}; + loclass_permute(&p_in, zCaret, 0, 4, &out); //returns 48 bits? or 6 8-bytes + + //Out is now a buffer containing six-bit bytes, should be 48 bits + // if all went well + //Shift z-values down onto the lower segment + + uint64_t zTilde = loclass_x_bytes_to_num(outbuffer, sizeof(outbuffer)); + + zTilde >>= 16; + + for(int i = 0; i < 8; i++) { + // the key on index i is first a bit from y + // then six bits from z, + // then a bit from p + + // Init with zeroes + k[i] = 0; + // First, place yi leftmost in k + //k[i] |= (y << i) & 0x80 ; + + // First, place y(7-i) leftmost in k + k[i] |= (y << (7 - i)) & 0x80; + + uint8_t zTilde_i = loclass_getSixBitByte(zTilde, i); + // zTildeI is now on the form 00XXXXXX + // with one leftshift, it'll be + // 0XXXXXX0 + // So after leftshift, we can OR it into k + // However, when doing complement, we need to + // again MASK 0XXXXXX0 (0x7E) + zTilde_i <<= 1; + + //Finally, add bit from p or p-mod + //Shift bit i into rightmost location (mask only after complement) + uint8_t p_i = p >> i & 0x1; + + if(k[i]) { // yi = 1 + k[i] |= ~zTilde_i & 0x7E; + k[i] |= p_i & 1; + k[i] += 1; + + } else { // otherwise + k[i] |= zTilde_i & 0x7E; + k[i] |= (~p_i) & 1; + } + } +} +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ +void loclass_diversifyKey(const uint8_t* csn, const uint8_t* key, uint8_t* div_key) { + mbedtls_des_context loclass_ctx_enc; + + // Prepare the DES key + mbedtls_des_setkey_enc(&loclass_ctx_enc, key); + + uint8_t crypted_csn[8] = {0}; + + // Calculate DES(CSN, KEY) + mbedtls_des_crypt_ecb(&loclass_ctx_enc, csn, crypted_csn); + + //Calculate HASH0(DES)) + uint64_t c_csn = loclass_x_bytes_to_num(crypted_csn, sizeof(crypted_csn)); + + loclass_hash0(c_csn, div_key); +} diff --git a/lib/loclass/optimized_ikeys.h b/lib/loclass/optimized_ikeys.h new file mode 100644 index 0000000..f40e87b --- /dev/null +++ b/lib/loclass/optimized_ikeys.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// More recently from https://github.com/RfidResearchGroup/proxmark3 +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#ifndef IKEYS_H +#define IKEYS_H + +#include + +/** + * @brief + *Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void loclass_hash0(uint64_t c, uint8_t k[8]); +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ + +void loclass_diversifyKey(const uint8_t* csn, const uint8_t* key, uint8_t* div_key); +/** + * @brief Permutes a key from standard NIST format to Iclass specific format + * @param key + * @param dest + */ + +#endif // IKEYS_H From d83fa2e41ff40a407166f65a975120f4ce5ca3f0 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 2 Dec 2023 21:05:03 -0800 Subject: [PATCH 06/19] add mbedtls lib --- application.fam | 1 + 1 file changed, 1 insertion(+) diff --git a/application.fam b/application.fam index a78c28a..29a6ba2 100644 --- a/application.fam +++ b/application.fam @@ -25,6 +25,7 @@ App( # command="asn1c -D ${FAP_SRC_DIR}/lib/asn1 -no-gen-example -pdu=all ${FAP_SRC_DIR}/seader.asn1" # ), # ), + fap_libs=["mbedtls"], fap_private_libs=[ Lib( name="asn1", From 5e1c5a893ac16cafe6dc9700d1636c5fc83b6481 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 2 Dec 2023 21:05:46 -0800 Subject: [PATCH 07/19] Consolidate some defines --- protocol/picopass_protocol.h | 10 ++++++++++ seader_credential.c | 11 ----------- seader_credential.h | 1 + 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/protocol/picopass_protocol.h b/protocol/picopass_protocol.h index 6c4dd44..73c549c 100644 --- a/protocol/picopass_protocol.h +++ b/protocol/picopass_protocol.h @@ -8,6 +8,16 @@ #define PICOPASS_MAC_LEN 4 #define PICOPASS_KEY_LEN 8 +#define CSN_INDEX 0 +#define CFG_INDEX 1 +#define EPURSE_INDEX 2 +#define KD_INDEX 3 +#define KC_INDEX 4 +#define AIA_INDEX 5 +#define PACS_CFG_INDEX 6 +#define PACS_INDEX 7 +#define SR_SIO_INDEX 10 + #define PICOPASS_FDT_LISTEN_FC (1000) #ifdef __cplusplus diff --git a/seader_credential.c b/seader_credential.c index e6da596..51f8f1f 100644 --- a/seader_credential.c +++ b/seader_credential.c @@ -9,17 +9,6 @@ #include #define TAG "SeaderCredential" -#define PICOPASS_BLOCK_LEN 8 - -#define CSN_INDEX 0 -#define CFG_INDEX 1 -#define EPURSE_INDEX 2 -#define KD_INDEX 3 -#define KC_INDEX 4 -#define AIA_INDEX 5 -#define PACS_CFG_INDEX 6 -#define PACS_INDEX 7 -#define SR_SIO_INDEX 10 static const char* seader_file_header = "Flipper Seader Credential"; static const uint32_t seader_file_version = 1; diff --git a/seader_credential.h b/seader_credential.h index ec8865d..038cc33 100644 --- a/seader_credential.h +++ b/seader_credential.h @@ -4,6 +4,7 @@ #include #include #include +#include "protocol/picopass_protocol.h" #define SEADER_CRED_NAME_MAX_LEN 22 #define SEADER_APP_EXTENSION ".credential" From 27786723fe48435dd8257f2ee0fa5817162d1a64 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 2 Dec 2023 21:06:20 -0800 Subject: [PATCH 08/19] use define for array size --- seader_worker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seader_worker.c b/seader_worker.c index 36d6888..53b619e 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -154,7 +154,7 @@ 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[8] = {0xf8, 0x7c, 0xd7, 0x12, 0xff, 0xff, 0x12, 0xe0}; + 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); From ec3ded124f5f5874a86f28eef194d7844f3e919b Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sat, 2 Dec 2023 21:06:37 -0800 Subject: [PATCH 09/19] continue work on picopass state machine --- sam_api.c | 75 +++++++++++++++++++++++++++++++++++++++++++++---------- sam_api.h | 2 ++ 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/sam_api.c b/sam_api.c index 76d42e6..0c2127a 100644 --- a/sam_api.c +++ b/sam_api.c @@ -11,10 +11,10 @@ static char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0}; char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0}; bool requestPacs = true; -uint8_t read4Block6[] = {0x06, 0x06, 0x45, 0x56}; -uint8_t read4Block9[] = {0x06, 0x09, 0xB2, 0xAE}; -uint8_t read4Block10[] = {0x06, 0x0A, 0x29, 0x9C}; -uint8_t read4Block13[] = {0x06, 0x0D, 0x96, 0xE8}; +uint8_t read4Block6[] = {RFAL_PICOPASS_CMD_READ4, 0x06, 0x45, 0x56}; +uint8_t read4Block9[] = {RFAL_PICOPASS_CMD_READ4, 0x09, 0xB2, 0xAE}; +uint8_t read4Block10[] = {RFAL_PICOPASS_CMD_READ4, 0x0A, 0x29, 0x9C}; +uint8_t read4Block13[] = {RFAL_PICOPASS_CMD_READ4, 0x0D, 0x96, 0xE8}; uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02}; void* calloc(size_t count, size_t size) { @@ -24,31 +24,65 @@ 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); -static uint16_t seader_worker_picopass_calculate_ccitt( - uint16_t preloadValue, - const uint8_t* buf, - uint16_t length); 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); - uint8_t sr_aia[PICOPASS_BLOCK_LEN + 2] = { - 0xFF, 0xff, 0xff, 0xff, 0xFF, 0xFf, 0xff, 0xFF, 0x00, 0x00}; + // 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] = {}; 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] == 5) { // TODO: _INDEX - uint16_t crc = seader_worker_picopass_calculate_ccitt(0xE012, sr_aia, 8); - memcpy(sr_aia + 8, &crc, sizeof(uint16_t)); + if(buffer[1] == AIA_INDEX) { // TODO: _INDEX bit_buffer_append_bytes(rx_buffer, sr_aia, sizeof(sr_aia)); + } 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); break; case RFAL_PICOPASS_CMD_UPDATE: seader_worker_fake_epurse_update(tx_buffer, rx_buffer); @@ -59,7 +93,22 @@ void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len) } 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); + 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)); + } + iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer); break; } diff --git a/sam_api.h b/sam_api.h index 6681d1e..271d4b3 100644 --- a/sam_api.h +++ b/sam_api.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include "seader_i.h" #include "seader_credential.h" From 7dcf6450c322cc855cb8d57f7a8a972cd11e0698 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 3 Dec 2023 12:30:05 -0800 Subject: [PATCH 10/19] Show 'unknown' for None type --- scenes/seader_scene_credential_info.c | 2 +- scenes/seader_scene_read_card_success.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scenes/seader_scene_credential_info.c b/scenes/seader_scene_credential_info.c index d8cb084..07e9071 100644 --- a/scenes/seader_scene_credential_info.c +++ b/scenes/seader_scene_credential_info.c @@ -36,7 +36,7 @@ void seader_scene_credential_info_on_enter(void* context) { furi_string_cat_printf(credential_str, "0x%llX", credential->credential); if(credential->type == SeaderCredentialTypeNone) { - furi_string_set(type_str, "None"); + furi_string_set(type_str, "Unknown"); } else if(credential->type == SeaderCredentialType14A) { furi_string_set(type_str, "14443A"); } else if(credential->type == SeaderCredentialTypePicopass) { diff --git a/scenes/seader_scene_read_card_success.c b/scenes/seader_scene_read_card_success.c index cc498ba..f28cfb0 100644 --- a/scenes/seader_scene_read_card_success.c +++ b/scenes/seader_scene_read_card_success.c @@ -36,7 +36,7 @@ void seader_scene_read_card_success_on_enter(void* context) { furi_string_cat_printf(credential_str, "0x%llX", credential->credential); if(credential->type == SeaderCredentialTypeNone) { - furi_string_set(type_str, "None"); + furi_string_set(type_str, "Unknown"); } else if(credential->type == SeaderCredentialType14A) { furi_string_set(type_str, "14443A"); } else if(credential->type == SeaderCredentialTypePicopass) { From 31c3ccd21b766bb2096963820c4526f25b094deb Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 3 Dec 2023 12:30:19 -0800 Subject: [PATCH 11/19] Reducate delay in virtual card loop --- seader_worker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seader_worker.c b/seader_worker.c index 53b619e..cf27fd3 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -191,7 +191,7 @@ void seader_worker_virtual_credential(Seader* seader) { } furi_mutex_release(seader_worker->mq_mutex); } - furi_delay_ms(1000); + furi_delay_ms(100); loops++; running = (loops < 10); } From b1c0f1e05db74d0196379c40c5bb233f7a3fc227 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 3 Dec 2023 12:30:37 -0800 Subject: [PATCH 12/19] Only show pacs in log for successfully loaded cards --- seader_credential.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/seader_credential.c b/seader_credential.c index 51f8f1f..9e42cae 100644 --- a/seader_credential.c +++ b/seader_credential.c @@ -70,6 +70,7 @@ static bool seader_credential_load(SeaderCredential* cred, FuriString* path, boo // 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); @@ -84,7 +85,9 @@ static bool seader_credential_load(SeaderCredential* cred, FuriString* path, boo dialog_message_show_storage_error(cred->dialogs, "Can not parse\nfile"); } } - 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); flipper_format_free(file); From 31894f4584c416aee4f41b99568698bb6f62a85e Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 3 Dec 2023 13:54:15 -0800 Subject: [PATCH 13/19] 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); } } From 5dfd2cd8c2d53605dfff01466a183ca9f821e26b Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 3 Dec 2023 14:10:44 -0800 Subject: [PATCH 14/19] Remove delay in virtual processing --- seader_worker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seader_worker.c b/seader_worker.c index 72d288f..da93742 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -187,7 +187,6 @@ void seader_worker_virtual_credential(Seader* seader) { 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"); @@ -196,6 +195,7 @@ void seader_worker_virtual_credential(Seader* seader) { } if(dead_loops > 0) { + FURI_LOG_D(TAG, "Final dead loops: %d", dead_loops); view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventPollerSuccess); } else { view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit); From 40885c5c633e22d6e1ea9839cd0f6a6aebaa043b Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Mon, 4 Dec 2023 20:09:09 -0800 Subject: [PATCH 15/19] use loclass_opt_doTagMAC --- sam_api.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sam_api.c b/sam_api.c index 1cd84c3..5dbd6fc 100644 --- a/sam_api.c +++ b/sam_api.c @@ -59,6 +59,7 @@ void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len) uint8_t pacs_sr_cfg[PICOPASS_BLOCK_LEN] = {0xA3, 0x03, 0x03, 0x03, 0x00, 0x03, 0xe0, 0x14}; uint8_t tmac[4] = {}; + uint8_t cc_p[12] = {}; uint8_t div_key[PICOPASS_BLOCK_LEN] = {}; uint8_t offset; // for READ4 @@ -83,8 +84,9 @@ void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len) case RFAL_PICOPASS_CMD_CHECK: 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_doTagMAC_2(cipher_state, buffer + 1, tmac, div_key); + memcpy(cc_p, epurse, PICOPASS_BLOCK_LEN); + memcpy(cc_p+8, buffer+1, PICOPASS_MAC_LEN); + loclass_opt_doTagMAC(cc_p, div_key, tmac); bit_buffer_append_bytes(rx_buffer, tmac, sizeof(tmac)); break; case RFAL_PICOPASS_CMD_READ4: From a580c9372d064b80f27e3aa32af3b56157c8a7a6 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Mon, 4 Dec 2023 20:10:31 -0800 Subject: [PATCH 16/19] format --- sam_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sam_api.c b/sam_api.c index 5dbd6fc..967e77d 100644 --- a/sam_api.c +++ b/sam_api.c @@ -85,7 +85,7 @@ void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len) loclass_iclass_calc_div_key( seader->credential->diversifier, picopass_iclass_key, div_key, false); memcpy(cc_p, epurse, PICOPASS_BLOCK_LEN); - memcpy(cc_p+8, buffer+1, PICOPASS_MAC_LEN); + memcpy(cc_p + 8, buffer + 1, PICOPASS_MAC_LEN); loclass_opt_doTagMAC(cc_p, div_key, tmac); bit_buffer_append_bytes(rx_buffer, tmac, sizeof(tmac)); break; From 0e0af8300bb66a02e97060b9b728e33c4cd054ac Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Wed, 6 Dec 2023 10:50:21 -0800 Subject: [PATCH 17/19] more logging --- sam_api.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sam_api.c b/sam_api.c index 967e77d..eed1114 100644 --- a/sam_api.c +++ b/sam_api.c @@ -130,6 +130,12 @@ bool seader_send_apdu( apdu[4] = length; memcpy(apdu + APDU_HEADER_LEN, payload, length); + memset(display, 0, sizeof(display)); + for(uint8_t i = 0; i < APDU_HEADER_LEN + length; i++) { + snprintf(display + (i * 2), sizeof(display), "%02x", apdu[i]); + } + FURI_LOG_D(TAG, "seader_send_apdu %s", display); + seader_ccid_XfrBlock(seader_uart, apdu, APDU_HEADER_LEN + length); return true; } From b35531d7e0e06a4c1b6c72cab713ebd5d88990c4 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Wed, 6 Dec 2023 10:50:28 -0800 Subject: [PATCH 18/19] typo --- seader_credential.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seader_credential.c b/seader_credential.c index 16ec0e0..1e9a449 100644 --- a/seader_credential.c +++ b/seader_credential.c @@ -157,7 +157,7 @@ bool seader_credential_save_mfc(SeaderCredential* cred, const char* name) { 0x45, 0x41, 0x54}; - uint8_t sectorn_trailer[16] = { + uint8_t section_trailer[16] = { 0xff, 0xff, 0xff, @@ -305,8 +305,8 @@ bool seader_credential_save_mfc(SeaderCredential* cred, const char* name) { if(!flipper_format_write_hex( file, furi_string_get_cstr(temp_str), - sectorn_trailer, - sizeof(sectorn_trailer))) { + section_trailer, + sizeof(section_trailer))) { block_saved = false; } break; From b20353d4e17bd01e6d01efb2e0c555803e354a76 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Wed, 6 Dec 2023 13:02:08 -0800 Subject: [PATCH 19/19] Save picopass to picopass folder --- seader_credential.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seader_credential.c b/seader_credential.c index 1e9a449..c303391 100644 --- a/seader_credential.c +++ b/seader_credential.c @@ -407,7 +407,7 @@ bool seader_credential_save(SeaderCredential* cred, const char* name) { furi_string_cat_printf(temp_str, "/%s%s", name, ".picopass"); } else { furi_string_printf( - temp_str, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, name, ".picopass"); + temp_str, "%s/%s%s", EXT_PATH("apps_data/picopass"), name, ".picopass"); } FURI_LOG_D(TAG, "Save as Picopass [%s]", furi_string_get_cstr(temp_str));