From afe9ef78f3c947de05466b05a8a3b154f41eb2b3 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Mon, 1 Jan 2024 20:20:16 -0800 Subject: [PATCH] Picopass: handle NR-MAC auth for legacy cards (#95) --- picopass/.catalog/README.md | 2 ++ picopass/picopass_device.c | 11 ++++++++++- picopass/picopass_device.h | 1 + picopass/protocol/picopass_poller.c | 14 +++++++++----- picopass/scenes/picopass_scene_card_menu.c | 2 +- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/picopass/.catalog/README.md b/picopass/.catalog/README.md index 0c7ce258..0136740e 100644 --- a/picopass/.catalog/README.md +++ b/picopass/.catalog/README.md @@ -35,6 +35,8 @@ There are some situations when the offline loclass may not find a key, such as: Due to the nature of how secure picopass works, it is possible to emulate some public fields from a card and capture the reader's response, which can be used to authenticate. Two of the pieces involved in this are the NR and MAC. +These instructions are intended to be performed all at the same time. If you use the card with the reader between Card Part 1 and Card Part 2, then Card Part 2 will fail. + ## Card Part 1 1. Place card against Flipper Zero diff --git a/picopass/picopass_device.c b/picopass/picopass_device.c index c090a27a..568727e0 100644 --- a/picopass/picopass_device.c +++ b/picopass/picopass_device.c @@ -167,6 +167,11 @@ static bool picopass_device_save_file( FuriString* temp_str; temp_str = furi_string_alloc(); + if(dev->format == PicopassDeviceSaveFormatPartial) { + // Clear key that may have been set when doing key tests for legacy + memset(AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data, 0, PICOPASS_BLOCK_LEN); + } + do { if(use_load_path && !furi_string_empty(dev->load_path)) { // Get directory name @@ -178,7 +183,8 @@ static bool picopass_device_save_file( furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); } - if(dev->format == PicopassDeviceSaveFormatHF) { + if(dev->format == PicopassDeviceSaveFormatHF || + dev->format == PicopassDeviceSaveFormatPartial) { // Open file if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; @@ -229,6 +235,9 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) { } else if(dev->format == PicopassDeviceSaveFormatSeader) { return picopass_device_save_file( dev, dev_name, EXT_PATH("apps_data/seader"), ".credential", true); + } else if(dev->format == PicopassDeviceSaveFormatPartial) { + return picopass_device_save_file( + dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, PICOPASS_APP_EXTENSION, true); } return false; diff --git a/picopass/picopass_device.h b/picopass/picopass_device.h index 75a295c1..bbe6efa6 100644 --- a/picopass/picopass_device.h +++ b/picopass/picopass_device.h @@ -71,6 +71,7 @@ typedef enum { PicopassDeviceSaveFormatHF, PicopassDeviceSaveFormatLF, PicopassDeviceSaveFormatSeader, + PicopassDeviceSaveFormatPartial, } PicopassDeviceSaveFormat; typedef enum { diff --git a/picopass/protocol/picopass_poller.c b/picopass/protocol/picopass_poller.c index da6a6188..461cdc00 100644 --- a/picopass/protocol/picopass_poller.c +++ b/picopass/protocol/picopass_poller.c @@ -187,10 +187,9 @@ NfcCommand picopass_poller_check_security(PicopassPoller* instance) { if(instance->data->pacs.se_enabled) { FURI_LOG_D(TAG, "SE enabled"); - instance->state = PicopassPollerStateNrMacAuth; - } else { - instance->state = PicopassPollerStateAuth; } + // Always try the NR-MAC auth in case we have the file. + instance->state = PicopassPollerStateNrMacAuth; return command; } @@ -221,8 +220,13 @@ NfcCommand picopass_poller_nr_mac_auth(PicopassPoller* instance) { FURI_LOG_D(TAG, "Looking for %s", furi_string_get_cstr(temp_str)); uint8_t nr_mac[PICOPASS_BLOCK_LEN]; - // Presume failure unless all steps are successful and the state is made "read block" - instance->state = PicopassPollerStateFail; + // Set next state so breaking do/while will jump to it. If successful, do/while will set to ReadBlock + if(instance->data->pacs.se_enabled) { + instance->state = PicopassPollerStateFail; + } else { + // For non-SE, run through normal key check + instance->state = PicopassPollerStateAuth; + } do { //check for file if(!flipper_format_file_open_existing(file, furi_string_get_cstr(temp_str))) break; diff --git a/picopass/scenes/picopass_scene_card_menu.c b/picopass/scenes/picopass_scene_card_menu.c index ce9e48f2..5eae180d 100644 --- a/picopass/scenes/picopass_scene_card_menu.c +++ b/picopass/scenes/picopass_scene_card_menu.c @@ -106,7 +106,7 @@ bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexSave); scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); - picopass->dev->format = PicopassDeviceSaveFormatHF; + picopass->dev->format = PicopassDeviceSaveFormatPartial; consumed = true; } else if(event.event == SubmenuIndexSaveAsSeader) { scene_manager_set_scene_state(