From 8af2198684e92343fdd9bb6c8de2f95efc9d2d05 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Tue, 5 Jul 2022 08:28:27 -0700 Subject: [PATCH 1/3] Iclass UI (#1366) * Move structs to header * roll mbedtls into loclass * Picopass with scene for reading card * Picopass: fix memory leak * Lib: return mbedtls back * Picopass: rename symbols to match naming guide Co-authored-by: Aleksandr Kutuzov --- applications/picopass/application.fam | 2 +- applications/picopass/picopass.c | 478 ++++-------------- applications/picopass/picopass.h | 8 +- applications/picopass/picopass_device.c | 33 ++ applications/picopass/picopass_device.h | 43 ++ applications/picopass/picopass_i.h | 66 +++ applications/picopass/picopass_worker.c | 317 ++++++++++++ applications/picopass/picopass_worker.h | 45 ++ applications/picopass/picopass_worker_i.h | 24 + applications/picopass/scenes/picopass_scene.c | 30 ++ applications/picopass/scenes/picopass_scene.h | 29 ++ .../picopass/scenes/picopass_scene_config.h | 3 + .../scenes/picopass_scene_read_card.c | 55 ++ .../scenes/picopass_scene_read_card_success.c | 78 +++ .../picopass/scenes/picopass_scene_start.c | 45 ++ lib/loclass/optimized_ikeys.c | 2 +- lib/loclass/optimized_ikeys.h | 2 +- 17 files changed, 881 insertions(+), 379 deletions(-) create mode 100644 applications/picopass/picopass_device.c create mode 100644 applications/picopass/picopass_device.h create mode 100644 applications/picopass/picopass_i.h create mode 100644 applications/picopass/picopass_worker.c create mode 100755 applications/picopass/picopass_worker.h create mode 100644 applications/picopass/picopass_worker_i.h create mode 100755 applications/picopass/scenes/picopass_scene.c create mode 100644 applications/picopass/scenes/picopass_scene.h create mode 100755 applications/picopass/scenes/picopass_scene_config.h create mode 100644 applications/picopass/scenes/picopass_scene_read_card.c create mode 100644 applications/picopass/scenes/picopass_scene_read_card_success.c create mode 100644 applications/picopass/scenes/picopass_scene_start.c diff --git a/applications/picopass/application.fam b/applications/picopass/application.fam index 3ad72d274..223094250 100644 --- a/applications/picopass/application.fam +++ b/applications/picopass/application.fam @@ -4,7 +4,7 @@ App( apptype=FlipperAppType.PLUGIN, entry_point="picopass_app", cdefines=["APP_PICOPASS"], - requires=["gui"], + requires=["storage", "gui"], stack_size=1 * 1024, icon="A_Plugins_14", order=30, diff --git a/applications/picopass/picopass.c b/applications/picopass/picopass.c index 2bf0d6f07..fb9e6b0de 100644 --- a/applications/picopass/picopass.c +++ b/applications/picopass/picopass.c @@ -1,397 +1,137 @@ -#include "picopass.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include +#include "picopass_i.h" #define TAG "PicoPass" -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType type; - InputEvent input; -} PluginEvent; - -typedef struct { - bool valid; - uint8_t bitLength; - uint8_t FacilityCode; - uint16_t CardNumber; -} WiegandRecord; - -typedef struct { - bool biometrics; - uint8_t encryption; - uint8_t credential[8]; - uint8_t pin0[8]; - uint8_t pin1[8]; - WiegandRecord record; -} PACS; - -enum State { INIT, READY, RESULT }; -typedef struct { - enum State state; - PACS pacs; -} PluginState; - -uint8_t iclass_key[8] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; -uint8_t iclass_decryptionkey[16] = - {0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36}; -ApplicationArea AA1; - -static void render_callback(Canvas* const canvas, void* ctx) { - const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); - if(plugin_state == NULL) { - return; - } - // border around the edge of the screen - canvas_draw_frame(canvas, 0, 0, 128, 64); - - canvas_set_font(canvas, FontPrimary); - - if(plugin_state->state == INIT) { - canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Loading..."); - } else if(plugin_state->state == READY) { - canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Push center to scan"); - } else if(plugin_state->state == RESULT) { - char raw_credential[25] = {0}; - sprintf( - raw_credential, - "%02x %02x %02x %02x %02x %02x %02x %02x", - plugin_state->pacs.credential[0], - plugin_state->pacs.credential[1], - plugin_state->pacs.credential[2], - plugin_state->pacs.credential[3], - plugin_state->pacs.credential[4], - plugin_state->pacs.credential[5], - plugin_state->pacs.credential[6], - plugin_state->pacs.credential[7]); - canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignTop, raw_credential); - - if(plugin_state->pacs.record.valid) { - char parsed[20] = {0}; - sprintf( - parsed, - "FC: %03u CN: %05u", - plugin_state->pacs.record.FacilityCode, - plugin_state->pacs.record.CardNumber); - canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, parsed); - } - } - - release_mutex((ValueMutex*)ctx, plugin_state); +bool picopass_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + Picopass* picopass = context; + return scene_manager_handle_custom_event(picopass->scene_manager, event); } -static void input_callback(InputEvent* input_event, osMessageQueueId_t event_queue) { - furi_assert(event_queue); - - PluginEvent event = {.type = EventTypeKey, .input = *input_event}; - osMessageQueuePut(event_queue, &event, 0, osWaitForever); +bool picopass_back_event_callback(void* context) { + furi_assert(context); + Picopass* picopass = context; + return scene_manager_handle_back_event(picopass->scene_manager); } -static void picopass_state_init(PluginState* const plugin_state) { - plugin_state->state = READY; +void picopass_tick_event_callback(void* context) { + furi_assert(context); + Picopass* picopass = context; + scene_manager_handle_tick_event(picopass->scene_manager); } -ReturnCode decrypt(uint8_t* enc_data, uint8_t* dec_data) { - uint8_t key[32] = {0}; - memcpy(key, iclass_decryptionkey, sizeof(iclass_decryptionkey)); - mbedtls_des3_context ctx; - mbedtls_des3_init(&ctx); - mbedtls_des3_set2key_dec(&ctx, key); - mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); - mbedtls_des3_free(&ctx); - return ERR_NONE; +Picopass* picopass_alloc() { + Picopass* picopass = malloc(sizeof(Picopass)); + + picopass->worker = picopass_worker_alloc(); + picopass->view_dispatcher = view_dispatcher_alloc(); + picopass->scene_manager = scene_manager_alloc(&picopass_scene_handlers, picopass); + view_dispatcher_enable_queue(picopass->view_dispatcher); + view_dispatcher_set_event_callback_context(picopass->view_dispatcher, picopass); + view_dispatcher_set_custom_event_callback( + picopass->view_dispatcher, picopass_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + picopass->view_dispatcher, picopass_back_event_callback); + view_dispatcher_set_tick_event_callback( + picopass->view_dispatcher, picopass_tick_event_callback, 100); + + // Picopass device + picopass->dev = picopass_device_alloc(); + + // Open GUI record + picopass->gui = furi_record_open("gui"); + view_dispatcher_attach_to_gui( + picopass->view_dispatcher, picopass->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + picopass->notifications = furi_record_open("notification"); + + // Submenu + picopass->submenu = submenu_alloc(); + view_dispatcher_add_view( + picopass->view_dispatcher, PicopassViewMenu, submenu_get_view(picopass->submenu)); + + // Popup + picopass->popup = popup_alloc(); + view_dispatcher_add_view( + picopass->view_dispatcher, PicopassViewPopup, popup_get_view(picopass->popup)); + + // Custom Widget + picopass->widget = widget_alloc(); + view_dispatcher_add_view( + picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget)); + + return picopass; } -ReturnCode parseWiegand(uint8_t* data, WiegandRecord* record) { - uint32_t* halves = (uint32_t*)data; - if(halves[0] == 0) { - uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); - record->bitLength = 31 - leading0s; - } else { - uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); - record->bitLength = 63 - leading0s; - } - FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); +void picopass_free(Picopass* picopass) { + furi_assert(picopass); - if(record->bitLength == 26) { - uint8_t* v4 = data + 4; - v4[0] = 0; + // Submenu + view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewMenu); + submenu_free(picopass->submenu); - uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); + // Popup + view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewPopup); + popup_free(picopass->popup); - record->CardNumber = (bot >> 1) & 0xFFFF; - record->FacilityCode = (bot >> 17) & 0xFF; - record->valid = true; - } else { - record->CardNumber = 0; - record->FacilityCode = 0; - record->valid = false; - } - return ERR_NONE; + // Custom Widget + view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget); + widget_free(picopass->widget); + + // Worker + picopass_worker_stop(picopass->worker); + picopass_worker_free(picopass->worker); + + // View Dispatcher + view_dispatcher_free(picopass->view_dispatcher); + + // Scene Manager + scene_manager_free(picopass->scene_manager); + + // GUI + furi_record_close("gui"); + picopass->gui = NULL; + + // Notifications + furi_record_close("notification"); + picopass->notifications = NULL; + + picopass_device_free(picopass->dev); + picopass->dev = NULL; + + free(picopass); } -ReturnCode disable_field(ReturnCode rc) { - st25r3916TxRxOff(); - rfalLowPowerModeStart(); - return rc; +static const NotificationSequence picopass_sequence_blink_start_blue = { + &message_blink_start_10, + &message_blink_set_color_blue, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence picopass_sequence_blink_stop = { + &message_blink_stop, + NULL, +}; + +void picopass_blink_start(Picopass* picopass) { + notification_message(picopass->notifications, &picopass_sequence_blink_start_blue); } -ReturnCode picopass_read_card(ApplicationArea* AA1) { - rfalPicoPassIdentifyRes idRes; - rfalPicoPassSelectRes selRes; - rfalPicoPassReadCheckRes rcRes; - rfalPicoPassCheckRes chkRes; - - ReturnCode err; - - uint8_t div_key[8] = {0}; - uint8_t mac[4] = {0}; - uint8_t ccnr[12] = {0}; - - st25r3916TxRxOn(); - rfalLowPowerModeStop(); - rfalWorker(); - err = rfalPicoPassPollerInitialize(); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d\n", err); - return disable_field(err); - } - - err = rfalFieldOnAndStartGT(); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d\n", err); - return disable_field(err); - } - - err = rfalPicoPassPollerCheckPresence(); - if(err != ERR_RF_COLLISION) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d\n", err); - return disable_field(err); - } - - err = rfalPicoPassPollerIdentify(&idRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d\n", err); - return disable_field(err); - } - - err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d\n", err); - return disable_field(err); - } - - err = rfalPicoPassPollerReadCheck(&rcRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); - return disable_field(err); - } - memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - - diversifyKey(selRes.CSN, iclass_key, div_key); - opt_doReaderMAC(ccnr, div_key, mac); - - err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); - return disable_field(err); - } - - for(size_t i = 0; i < 4; i++) { - FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i + 6); - err = rfalPicoPassPollerReadBlock(i + 6, &(AA1->block[i])); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err); - return disable_field(err); - } - } - return disable_field(ERR_NONE); +void picopass_blink_stop(Picopass* picopass) { + notification_message(picopass->notifications, &picopass_sequence_blink_stop); } int32_t picopass_app(void* p) { UNUSED(p); - osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(PluginEvent), NULL); + Picopass* picopass = picopass_alloc(); - PluginState* plugin_state = malloc(sizeof(PluginState)); - picopass_state_init(plugin_state); - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { - FURI_LOG_E("Hello_world", "cannot create mutex\r\n"); - free(plugin_state); - return 255; - } + scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart); - // Set system callbacks - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, &state_mutex); - view_port_input_callback_set(view_port, input_callback, event_queue); + view_dispatcher_run(picopass->view_dispatcher); - // Open GUI and register view_port - Gui* gui = furi_record_open("gui"); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - PluginEvent event; - ReturnCode err; - for(bool processing = true; processing;) { - osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 100); - PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); - - if(event_status == osOK) { - // press events - if(event.type == EventTypeKey) { - if(event.input.type == InputTypePress) { - switch(event.input.key) { - case InputKeyUp: - FURI_LOG_D(TAG, "Input Up"); - break; - case InputKeyDown: - FURI_LOG_D(TAG, "Input Down"); - break; - case InputKeyRight: - FURI_LOG_D(TAG, "Input Right"); - break; - case InputKeyLeft: - FURI_LOG_D(TAG, "Input Left"); - break; - case InputKeyOk: - FURI_LOG_D(TAG, "Input OK"); - err = picopass_read_card(&AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_read_card error %d", err); - plugin_state->state = READY; - break; - } - FURI_LOG_D(TAG, "read OK"); - - plugin_state->pacs.biometrics = AA1.block[0].data[4]; - plugin_state->pacs.encryption = AA1.block[0].data[7]; - if(plugin_state->pacs.encryption == 0x17) { - FURI_LOG_D(TAG, "3DES Encrypted"); - err = decrypt(AA1.block[1].data, plugin_state->pacs.credential); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - break; - } - FURI_LOG_D(TAG, "Decrypted 7"); - - err = decrypt(AA1.block[2].data, plugin_state->pacs.pin0); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - break; - } - FURI_LOG_D(TAG, "Decrypted 8"); - - err = decrypt(AA1.block[3].data, plugin_state->pacs.pin1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - break; - } - FURI_LOG_D(TAG, "Decrypted 9"); - } else if(plugin_state->pacs.encryption == 0x14) { - FURI_LOG_D(TAG, "No Encryption"); - memcpy( - plugin_state->pacs.credential, - AA1.block[1].data, - RFAL_PICOPASS_MAX_BLOCK_LEN); - memcpy( - plugin_state->pacs.pin0, - AA1.block[2].data, - RFAL_PICOPASS_MAX_BLOCK_LEN); - memcpy( - plugin_state->pacs.pin1, - AA1.block[3].data, - RFAL_PICOPASS_MAX_BLOCK_LEN); - } else if(plugin_state->pacs.encryption == 0x15) { - FURI_LOG_D(TAG, "DES Encrypted"); - } else { - FURI_LOG_D(TAG, "Unknown encryption"); - break; - } - - FURI_LOG_D( - TAG, - "credential %02x%02x%02x%02x%02x%02x%02x%02x", - plugin_state->pacs.credential[0], - plugin_state->pacs.credential[1], - plugin_state->pacs.credential[2], - plugin_state->pacs.credential[3], - plugin_state->pacs.credential[4], - plugin_state->pacs.credential[5], - plugin_state->pacs.credential[6], - plugin_state->pacs.credential[7]); - FURI_LOG_D( - TAG, - "pin0 %02x%02x%02x%02x%02x%02x%02x%02x", - plugin_state->pacs.pin0[0], - plugin_state->pacs.pin0[1], - plugin_state->pacs.pin0[2], - plugin_state->pacs.pin0[3], - plugin_state->pacs.pin0[4], - plugin_state->pacs.pin0[5], - plugin_state->pacs.pin0[6], - plugin_state->pacs.pin0[7]); - FURI_LOG_D( - TAG, - "pin1 %02x%02x%02x%02x%02x%02x%02x%02x", - plugin_state->pacs.pin1[0], - plugin_state->pacs.pin1[1], - plugin_state->pacs.pin1[2], - plugin_state->pacs.pin1[3], - plugin_state->pacs.pin1[4], - plugin_state->pacs.pin1[5], - plugin_state->pacs.pin1[6], - plugin_state->pacs.pin1[7]); - - err = parseWiegand( - plugin_state->pacs.credential, &plugin_state->pacs.record); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "parse error %d", err); - break; - } - if(plugin_state->pacs.record.valid) { - FURI_LOG_D( - TAG, - "FC: %03d CN: %05d", - plugin_state->pacs.record.FacilityCode, - plugin_state->pacs.record.CardNumber); - } - plugin_state->state = RESULT; - - break; - case InputKeyBack: - FURI_LOG_D(TAG, "Input Back"); - processing = false; - break; - } - } - } - } else { - // FURI_LOG_D(TAG, "osMessageQueue: event timeout"); - // event timeout - } - - view_port_update(view_port); - release_mutex(&state_mutex, plugin_state); - } - - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close("gui"); - view_port_free(view_port); - osMessageQueueDelete(event_queue); + picopass_free(picopass); return 0; } diff --git a/applications/picopass/picopass.h b/applications/picopass/picopass.h index e24d97d7c..a1a87d7f8 100644 --- a/applications/picopass/picopass.h +++ b/applications/picopass/picopass.h @@ -1,9 +1,3 @@ #pragma once -#include -#include -#include -#include - -#define PP_MAX_DUMP_SIZE 1024 -#define FURI_HAL_PICOPASS_UID_MAX_LEN 10 +typedef struct Picopass Picopass; diff --git a/applications/picopass/picopass_device.c b/applications/picopass/picopass_device.c new file mode 100644 index 000000000..802c24e40 --- /dev/null +++ b/applications/picopass/picopass_device.c @@ -0,0 +1,33 @@ +#include "picopass_device.h" + +#include +#include + +#define TAG "PicopassDevice" + +PicopassDevice* picopass_device_alloc() { + PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice)); + picopass_dev->storage = furi_record_open("storage"); + picopass_dev->dialogs = furi_record_open("dialogs"); + return picopass_dev; +} + +void picopass_device_clear(PicopassDevice* dev) { + furi_assert(dev); + + picopass_device_data_clear(&dev->dev_data); + memset(&dev->dev_data, 0, sizeof(dev->dev_data)); +} + +void picopass_device_free(PicopassDevice* picopass_dev) { + furi_assert(picopass_dev); + picopass_device_clear(picopass_dev); + furi_record_close("storage"); + furi_record_close("dialogs"); + free(picopass_dev); +} + +void picopass_device_data_clear(PicopassDeviceData* dev_data) { + FURI_LOG_D(TAG, "picopass_device_data_clear"); + memset(&dev_data->AA1, 0, sizeof(ApplicationArea)); +} diff --git a/applications/picopass/picopass_device.h b/applications/picopass/picopass_device.h new file mode 100644 index 000000000..af4c07b9d --- /dev/null +++ b/applications/picopass/picopass_device.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include + +#include + +typedef struct { + bool valid; + uint8_t bitLength; + uint8_t FacilityCode; + uint16_t CardNumber; +} PicopassWiegandRecord; + +typedef struct { + bool biometrics; + uint8_t encryption; + uint8_t credential[8]; + uint8_t pin0[8]; + uint8_t pin1[8]; + PicopassWiegandRecord record; +} PicopassPacs; + +typedef struct { + ApplicationArea AA1; + PicopassPacs pacs; +} PicopassDeviceData; + +typedef struct { + Storage* storage; + DialogsApp* dialogs; + PicopassDeviceData dev_data; +} PicopassDevice; + +PicopassDevice* picopass_device_alloc(); + +void picopass_device_free(PicopassDevice* picopass_dev); + +void picopass_device_data_clear(PicopassDeviceData* dev_data); + +void picopass_device_clear(PicopassDevice* dev); diff --git a/applications/picopass/picopass_i.h b/applications/picopass/picopass_i.h new file mode 100644 index 000000000..04fd68765 --- /dev/null +++ b/applications/picopass/picopass_i.h @@ -0,0 +1,66 @@ +#pragma once + +#include "picopass.h" +#include "picopass_worker.h" +#include "picopass_device.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +enum PicopassCustomEvent { + // Reserve first 100 events for button types and indexes, starting from 0 + PicopassCustomEventReserved = 100, + + PicopassCustomEventViewExit, + PicopassCustomEventWorkerExit, + PicopassCustomEventByteInputDone, + PicopassCustomEventTextInputDone, + PicopassCustomEventDictAttackDone, +}; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +struct Picopass { + PicopassWorker* worker; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notifications; + SceneManager* scene_manager; + PicopassDevice* dev; + + // Common Views + Submenu* submenu; + Popup* popup; + Widget* widget; +}; + +typedef enum { + PicopassViewMenu, + PicopassViewPopup, + PicopassViewWidget, +} PicopassView; + +Picopass* picopass_alloc(); + +void picopass_blink_start(Picopass* picopass); + +void picopass_blink_stop(Picopass* picopass); diff --git a/applications/picopass/picopass_worker.c b/applications/picopass/picopass_worker.c new file mode 100644 index 000000000..abefcb71a --- /dev/null +++ b/applications/picopass/picopass_worker.c @@ -0,0 +1,317 @@ +#include "picopass_worker_i.h" +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define TAG "PicopassWorker" + +const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; +const uint8_t picopass_iclass_decryptionkey[] = + {0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36}; + +static void picopass_worker_enable_field() { + st25r3916TxRxOn(); + rfalLowPowerModeStop(); + rfalWorker(); +} + +static ReturnCode picopass_worker_disable_field(ReturnCode rc) { + st25r3916TxRxOff(); + rfalLowPowerModeStart(); + return rc; +} + +static ReturnCode picopass_worker_decrypt(uint8_t* enc_data, uint8_t* dec_data) { + uint8_t key[32] = {0}; + memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey)); + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_dec(&ctx, key); + mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); + mbedtls_des3_free(&ctx); + return ERR_NONE; +} + +static ReturnCode picopass_worker_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) { + uint32_t* halves = (uint32_t*)data; + if(halves[0] == 0) { + uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); + record->bitLength = 31 - leading0s; + } else { + uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); + record->bitLength = 63 - leading0s; + } + FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); + + if(record->bitLength == 26) { + uint8_t* v4 = data + 4; + v4[0] = 0; + + uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); + + record->CardNumber = (bot >> 1) & 0xFFFF; + record->FacilityCode = (bot >> 17) & 0xFF; + record->valid = true; + } else { + record->CardNumber = 0; + record->FacilityCode = 0; + record->valid = false; + } + return ERR_NONE; +} + +/***************************** Picopass Worker API *******************************/ + +PicopassWorker* picopass_worker_alloc() { + PicopassWorker* picopass_worker = malloc(sizeof(PicopassWorker)); + + // Worker thread attributes + picopass_worker->thread = furi_thread_alloc(); + furi_thread_set_name(picopass_worker->thread, "PicopassWorker"); + furi_thread_set_stack_size(picopass_worker->thread, 8192); + furi_thread_set_callback(picopass_worker->thread, picopass_worker_task); + furi_thread_set_context(picopass_worker->thread, picopass_worker); + + picopass_worker->callback = NULL; + picopass_worker->context = NULL; + picopass_worker->storage = furi_record_open("storage"); + + picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); + + return picopass_worker; +} + +void picopass_worker_free(PicopassWorker* picopass_worker) { + furi_assert(picopass_worker); + + furi_thread_free(picopass_worker->thread); + + furi_record_close("storage"); + + free(picopass_worker); +} + +PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker) { + return picopass_worker->state; +} + +void picopass_worker_start( + PicopassWorker* picopass_worker, + PicopassWorkerState state, + PicopassDeviceData* dev_data, + PicopassWorkerCallback callback, + void* context) { + furi_assert(picopass_worker); + furi_assert(dev_data); + + FURI_LOG_D(TAG, "picopass_worker_start"); + + picopass_worker->callback = callback; + picopass_worker->context = context; + picopass_worker->dev_data = dev_data; + picopass_worker_change_state(picopass_worker, state); + furi_thread_start(picopass_worker->thread); +} + +void picopass_worker_stop(PicopassWorker* picopass_worker) { + furi_assert(picopass_worker); + if(picopass_worker->state == PicopassWorkerStateBroken || + picopass_worker->state == PicopassWorkerStateReady) { + return; + } + picopass_worker_disable_field(ERR_NONE); + + picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); + furi_thread_join(picopass_worker->thread); +} + +void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) { + picopass_worker->state = state; +} + +/***************************** Picopass Worker Thread *******************************/ + +ReturnCode picopass_detect_card(int timeout) { + UNUSED(timeout); + + ReturnCode err; + + err = rfalPicoPassPollerInitialize(); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d", err); + return err; + } + + err = rfalFieldOnAndStartGT(); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d", err); + return err; + } + + err = rfalPicoPassPollerCheckPresence(); + if(err != ERR_RF_COLLISION) { + FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err); + return err; + } + + return ERR_NONE; +} + +ReturnCode picopass_read_card(ApplicationArea* AA1) { + rfalPicoPassIdentifyRes idRes; + rfalPicoPassSelectRes selRes; + rfalPicoPassReadCheckRes rcRes; + rfalPicoPassCheckRes chkRes; + + ReturnCode err; + + uint8_t div_key[8] = {0}; + uint8_t mac[4] = {0}; + uint8_t ccnr[12] = {0}; + + err = rfalPicoPassPollerIdentify(&idRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); + return err; + } + + err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err); + return err; + } + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return err; + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + diversifyKey(selRes.CSN, picopass_iclass_key, div_key); + opt_doReaderMAC(ccnr, div_key, mac); + + err = rfalPicoPassPollerCheck(mac, &chkRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); + return err; + } + + for(size_t i = 0; i < 4; i++) { + FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i + 6); + rfalPicoPassReadBlockRes block; + err = rfalPicoPassPollerReadBlock(i + 6, &block); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err); + return err; + } + + FURI_LOG_D( + TAG, + "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x", + i + 6, + block.data[0], + block.data[1], + block.data[2], + block.data[3], + block.data[4], + block.data[5], + block.data[6], + block.data[7]); + + memcpy(&(AA1->block[i]), &block, sizeof(block)); + } + + return ERR_NONE; +} + +int32_t picopass_worker_task(void* context) { + PicopassWorker* picopass_worker = context; + + picopass_worker_enable_field(); + if(picopass_worker->state == PicopassWorkerStateDetect) { + picopass_worker_detect(picopass_worker); + } + picopass_worker_disable_field(ERR_NONE); + + picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); + + return 0; +} + +void picopass_worker_detect(PicopassWorker* picopass_worker) { + picopass_device_data_clear(picopass_worker->dev_data); + PicopassDeviceData* dev_data = picopass_worker->dev_data; + + ApplicationArea* AA1 = &dev_data->AA1; + PicopassPacs* pacs = &dev_data->pacs; + ReturnCode err; + + while(picopass_worker->state == PicopassWorkerStateDetect) { + FURI_LOG_D(TAG, "PicopassWorkerStateDetect"); + if(picopass_detect_card(1000) == ERR_NONE) { + // Process first found device + FURI_LOG_D(TAG, "picopass_read_card"); + err = picopass_read_card(AA1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_read_card error %d", err); + } + + pacs->biometrics = AA1->block[0].data[4]; + pacs->encryption = AA1->block[0].data[7]; + + if(pacs->encryption == 0x17) { + FURI_LOG_D(TAG, "3DES Encrypted"); + err = picopass_worker_decrypt(AA1->block[1].data, pacs->credential); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "decrypt error %d", err); + break; + } + FURI_LOG_D(TAG, "Decrypted 7"); + + err = picopass_worker_decrypt(AA1->block[2].data, pacs->pin0); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "decrypt error %d", err); + break; + } + FURI_LOG_D(TAG, "Decrypted 8"); + + err = picopass_worker_decrypt(AA1->block[3].data, pacs->pin1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "decrypt error %d", err); + break; + } + FURI_LOG_D(TAG, "Decrypted 9"); + } else if(pacs->encryption == 0x14) { + FURI_LOG_D(TAG, "No Encryption"); + memcpy(pacs->credential, AA1->block[1].data, RFAL_PICOPASS_MAX_BLOCK_LEN); + memcpy(pacs->pin0, AA1->block[2].data, RFAL_PICOPASS_MAX_BLOCK_LEN); + memcpy(pacs->pin1, AA1->block[3].data, RFAL_PICOPASS_MAX_BLOCK_LEN); + } else if(pacs->encryption == 0x15) { + FURI_LOG_D(TAG, "DES Encrypted"); + } else { + FURI_LOG_D(TAG, "Unknown encryption"); + break; + } + + picopass_worker_parse_wiegand(pacs->credential, &pacs->record); + + // Notify caller and exit + if(picopass_worker->callback) { + picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); + } + break; + } + osDelay(100); + } +} diff --git a/applications/picopass/picopass_worker.h b/applications/picopass/picopass_worker.h new file mode 100755 index 000000000..9035f1c89 --- /dev/null +++ b/applications/picopass/picopass_worker.h @@ -0,0 +1,45 @@ +#pragma once + +#include "picopass_device.h" + +typedef struct PicopassWorker PicopassWorker; + +typedef enum { + // Init states + PicopassWorkerStateNone, + PicopassWorkerStateBroken, + PicopassWorkerStateReady, + // Main worker states + PicopassWorkerStateDetect, + // Transition + PicopassWorkerStateStop, +} PicopassWorkerState; + +typedef enum { + // Reserve first 50 events for application events + PicopassWorkerEventReserved = 50, + + // Picopass worker common events + PicopassWorkerEventSuccess, + PicopassWorkerEventFail, + PicopassWorkerEventNoCardDetected, + + PicopassWorkerEventStartReading, +} PicopassWorkerEvent; + +typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); + +PicopassWorker* picopass_worker_alloc(); + +PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker); + +void picopass_worker_free(PicopassWorker* picopass_worker); + +void picopass_worker_start( + PicopassWorker* picopass_worker, + PicopassWorkerState state, + PicopassDeviceData* dev_data, + PicopassWorkerCallback callback, + void* context); + +void picopass_worker_stop(PicopassWorker* picopass_worker); diff --git a/applications/picopass/picopass_worker_i.h b/applications/picopass/picopass_worker_i.h new file mode 100644 index 000000000..2610d5e7f --- /dev/null +++ b/applications/picopass/picopass_worker_i.h @@ -0,0 +1,24 @@ +#pragma once + +#include "picopass_worker.h" +#include "picopass_i.h" + +#include +#include + +struct PicopassWorker { + FuriThread* thread; + Storage* storage; + + PicopassDeviceData* dev_data; + PicopassWorkerCallback callback; + void* context; + + PicopassWorkerState state; +}; + +void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state); + +int32_t picopass_worker_task(void* context); + +void picopass_worker_detect(PicopassWorker* picopass_worker); diff --git a/applications/picopass/scenes/picopass_scene.c b/applications/picopass/scenes/picopass_scene.c new file mode 100755 index 000000000..61bd5e8fe --- /dev/null +++ b/applications/picopass/scenes/picopass_scene.c @@ -0,0 +1,30 @@ +#include "picopass_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const picopass_on_enter_handlers[])(void*) = { +#include "picopass_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const picopass_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "picopass_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const picopass_on_exit_handlers[])(void* context) = { +#include "picopass_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers picopass_scene_handlers = { + .on_enter_handlers = picopass_on_enter_handlers, + .on_event_handlers = picopass_on_event_handlers, + .on_exit_handlers = picopass_on_exit_handlers, + .scene_num = PicopassSceneNum, +}; diff --git a/applications/picopass/scenes/picopass_scene.h b/applications/picopass/scenes/picopass_scene.h new file mode 100644 index 000000000..2faa80b12 --- /dev/null +++ b/applications/picopass/scenes/picopass_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) PicopassScene##id, +typedef enum { +#include "picopass_scene_config.h" + PicopassSceneNum, +} PicopassScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers picopass_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "picopass_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "picopass_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "picopass_scene_config.h" +#undef ADD_SCENE diff --git a/applications/picopass/scenes/picopass_scene_config.h b/applications/picopass/scenes/picopass_scene_config.h new file mode 100755 index 000000000..7a87737e1 --- /dev/null +++ b/applications/picopass/scenes/picopass_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(picopass, start, Start) +ADD_SCENE(picopass, read_card, ReadCard) +ADD_SCENE(picopass, read_card_success, ReadCardSuccess) diff --git a/applications/picopass/scenes/picopass_scene_read_card.c b/applications/picopass/scenes/picopass_scene_read_card.c new file mode 100644 index 000000000..add05e479 --- /dev/null +++ b/applications/picopass/scenes/picopass_scene_read_card.c @@ -0,0 +1,55 @@ +#include "../picopass_i.h" +#include + +void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) { + UNUSED(event); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); +} + +void picopass_scene_read_card_on_enter(void* context) { + Picopass* picopass = context; + DOLPHIN_DEED(DolphinDeedNfcRead); + + // Setup view + Popup* popup = picopass->popup; + popup_set_header(popup, "Detecting\npicopass card", 70, 34, AlignLeft, AlignTop); + popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); + + // Start worker + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); + picopass_worker_start( + picopass->worker, + PicopassWorkerStateDetect, + &picopass->dev->dev_data, + picopass_read_card_worker_callback, + picopass); + + picopass_blink_start(picopass); +} + +bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassCustomEventWorkerExit) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeTick) { + consumed = true; + } + return consumed; +} + +void picopass_scene_read_card_on_exit(void* context) { + Picopass* picopass = context; + + // Stop worker + picopass_worker_stop(picopass->worker); + // Clear view + popup_reset(picopass->popup); + + picopass_blink_stop(picopass); +} diff --git a/applications/picopass/scenes/picopass_scene_read_card_success.c b/applications/picopass/scenes/picopass_scene_read_card_success.c new file mode 100644 index 000000000..8e65ce003 --- /dev/null +++ b/applications/picopass/scenes/picopass_scene_read_card_success.c @@ -0,0 +1,78 @@ +#include "../picopass_i.h" +#include + +void picopass_scene_read_card_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Picopass* picopass = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(picopass->view_dispatcher, result); + } +} + +void picopass_scene_read_card_success_on_enter(void* context) { + Picopass* picopass = context; + string_t credential_str; + string_t wiegand_str; + string_init(credential_str); + string_init(wiegand_str); + + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Send notification + notification_message(picopass->notifications, &sequence_success); + + // Setup view + PicopassPacs* pacs = &picopass->dev->dev_data.pacs; + Widget* widget = picopass->widget; + + string_set_str(credential_str, ""); + for(uint8_t i = 0; i < RFAL_PICOPASS_MAX_BLOCK_LEN; i++) { + string_cat_printf(credential_str, " %02X", pacs->credential[i]); + } + + if(pacs->record.valid) { + string_cat_printf( + wiegand_str, "FC: %03u CN: %05u", pacs->record.FacilityCode, pacs->record.CardNumber); + } + + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + picopass_scene_read_card_success_widget_callback, + picopass); + if(pacs->record.valid) { + widget_add_string_element( + widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); + } + widget_add_string_element( + widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str)); + + string_clear(credential_str); + string_clear(wiegand_str); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); +} + +bool picopass_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + } + return consumed; +} + +void picopass_scene_read_card_success_on_exit(void* context) { + Picopass* picopass = context; + + // Clear view + widget_reset(picopass->widget); +} diff --git a/applications/picopass/scenes/picopass_scene_start.c b/applications/picopass/scenes/picopass_scene_start.c new file mode 100644 index 000000000..7f42fb133 --- /dev/null +++ b/applications/picopass/scenes/picopass_scene_start.c @@ -0,0 +1,45 @@ +#include "../picopass_i.h" +enum SubmenuIndex { + SubmenuIndexRead, + SubmenuIndexRunScript, + SubmenuIndexSaved, + SubmenuIndexAddManualy, + SubmenuIndexDebug, +}; + +void picopass_scene_start_submenu_callback(void* context, uint32_t index) { + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, index); +} +void picopass_scene_start_on_enter(void* context) { + Picopass* picopass = context; + + Submenu* submenu = picopass->submenu; + submenu_add_item( + submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart)); + picopass_device_clear(picopass->dev); + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); +} + +bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexRead) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard); + consumed = true; + } + scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event); + } + + return consumed; +} + +void picopass_scene_start_on_exit(void* context) { + Picopass* picopass = context; + submenu_reset(picopass->submenu); +} diff --git a/lib/loclass/optimized_ikeys.c b/lib/loclass/optimized_ikeys.c index ec414f1a2..8e093feb3 100644 --- a/lib/loclass/optimized_ikeys.c +++ b/lib/loclass/optimized_ikeys.c @@ -304,7 +304,7 @@ void hash0(uint64_t c, uint8_t k[8]) { * @param key * @param div_key */ -void diversifyKey(uint8_t *csn, uint8_t *key, uint8_t *div_key) { +void diversifyKey(uint8_t *csn, const uint8_t *key, uint8_t *div_key) { // Prepare the DES key mbedtls_des_setkey_enc(&ctx_enc, key); diff --git a/lib/loclass/optimized_ikeys.h b/lib/loclass/optimized_ikeys.h index fd990cac9..e366bb6ee 100644 --- a/lib/loclass/optimized_ikeys.h +++ b/lib/loclass/optimized_ikeys.h @@ -56,7 +56,7 @@ void hash0(uint64_t c, uint8_t k[8]); * @param div_key */ -void diversifyKey(uint8_t *csn, uint8_t *key, uint8_t *div_key); +void diversifyKey(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 From e147b2ceea7e69d637b45c26533662337289a0d1 Mon Sep 17 00:00:00 2001 From: Equip <72751518+equipter@users.noreply.github.com> Date: Tue, 5 Jul 2022 16:34:21 +0100 Subject: [PATCH 2/3] Added Javacard Emulated mifare classic 1K compatibility (#1369) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Mifare classic 1k JC handling Add mifare classic Javacard emulation handling * Adding MIFARE 1K Javacard Emulation Compatibility MIFARE Classic 1K Cards from NXP have the SAK value of 0x08. MIFARE Classic 1K Cards that are emulated via javacard applet have an SAK value of 0x09. Adding the SAK values accordingly so that Javacard emulated mifare classic tags are properly handled. * update mifare_common.c added javacard emulation handling for mifare classic 1k Co-authored-by: gornekich Co-authored-by: あく --- lib/nfc_protocols/mifare_classic.c | 4 ++-- lib/nfc_protocols/mifare_common.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nfc_protocols/mifare_classic.c b/lib/nfc_protocols/mifare_classic.c index b6a1c23ac..28334119d 100644 --- a/lib/nfc_protocols/mifare_classic.c +++ b/lib/nfc_protocols/mifare_classic.c @@ -198,7 +198,7 @@ static bool mf_classic_is_allowed_access( bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); - if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88)) { + if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { return true; } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { return true; @@ -219,7 +219,7 @@ bool mf_classic_get_type( furi_assert(reader); memset(reader, 0, sizeof(MfClassicReader)); - if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88)) { + if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { reader->type = MfClassicType1k; } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { reader->type = MfClassicType4k; diff --git a/lib/nfc_protocols/mifare_common.c b/lib/nfc_protocols/mifare_common.c index 78094cddb..fd622765e 100644 --- a/lib/nfc_protocols/mifare_common.c +++ b/lib/nfc_protocols/mifare_common.c @@ -6,7 +6,7 @@ MifareType mifare_common_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) { type = MifareTypeUltralight; } else if( - ((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88)) || + ((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) || ((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18))) { type = MifareTypeClassic; } else if(ATQA0 == 0x44 && ATQA1 == 0x03 && SAK == 0x20) { From ece142a6677550c9fbc805a72b4f09b052251d45 Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 5 Jul 2022 18:41:19 +0300 Subject: [PATCH 3/3] Fix buffer overflow in mifare classic lib #1374 --- lib/nfc_protocols/mifare_classic.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/nfc_protocols/mifare_classic.c b/lib/nfc_protocols/mifare_classic.c index 28334119d..21d470bca 100644 --- a/lib/nfc_protocols/mifare_classic.c +++ b/lib/nfc_protocols/mifare_classic.c @@ -386,11 +386,25 @@ bool mf_classic_read_block( tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { - if(tx_rx->rx_bits == 8 * 18) { - for(uint8_t i = 0; i < 18; i++) { - block->value[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i]; + if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) { + uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2]; + for(uint8_t i = 0; i < MF_CLASSIC_BLOCK_SIZE + 2; i++) { + block_received[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i]; + } + uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE); + uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) | + block_received[MF_CLASSIC_BLOCK_SIZE]; + if(crc_received != crc_calc) { + FURI_LOG_E( + TAG, + "Incorrect CRC while reading block %d. Expected %04X, Received %04X", + block_num, + crc_received, + crc_calc); + } else { + memcpy(block->value, block_received, MF_CLASSIC_BLOCK_SIZE); + read_block_success = true; } - read_block_success = true; } } return read_block_success;