Parcourir la source

Picopass: handle NR-MAC auth for legacy cards (#95)

Eric Betts il y a 2 ans
Parent
commit
d7175cff16
5 fichiers modifiés avec 23 ajouts et 7 suppressions
  1. 2 0
      .catalog/README.md
  2. 10 1
      picopass_device.c
  3. 1 0
      picopass_device.h
  4. 9 5
      protocol/picopass_poller.c
  5. 1 1
      scenes/picopass_scene_card_menu.c

+ 2 - 0
.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.
 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
 ## Card Part 1
 
 
 1. Place card against Flipper Zero
 1. Place card against Flipper Zero

+ 10 - 1
picopass_device.c

@@ -167,6 +167,11 @@ static bool picopass_device_save_file(
     FuriString* temp_str;
     FuriString* temp_str;
     temp_str = furi_string_alloc();
     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 {
     do {
         if(use_load_path && !furi_string_empty(dev->load_path)) {
         if(use_load_path && !furi_string_empty(dev->load_path)) {
             // Get directory name
             // 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);
             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
             // Open file
             if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
             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) {
     } else if(dev->format == PicopassDeviceSaveFormatSeader) {
         return picopass_device_save_file(
         return picopass_device_save_file(
             dev, dev_name, EXT_PATH("apps_data/seader"), ".credential", true);
             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;
     return false;

+ 1 - 0
picopass_device.h

@@ -71,6 +71,7 @@ typedef enum {
     PicopassDeviceSaveFormatHF,
     PicopassDeviceSaveFormatHF,
     PicopassDeviceSaveFormatLF,
     PicopassDeviceSaveFormatLF,
     PicopassDeviceSaveFormatSeader,
     PicopassDeviceSaveFormatSeader,
+    PicopassDeviceSaveFormatPartial,
 } PicopassDeviceSaveFormat;
 } PicopassDeviceSaveFormat;
 
 
 typedef enum {
 typedef enum {

+ 9 - 5
protocol/picopass_poller.c

@@ -187,10 +187,9 @@ NfcCommand picopass_poller_check_security(PicopassPoller* instance) {
 
 
     if(instance->data->pacs.se_enabled) {
     if(instance->data->pacs.se_enabled) {
         FURI_LOG_D(TAG, "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;
     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));
     FURI_LOG_D(TAG, "Looking for %s", furi_string_get_cstr(temp_str));
     uint8_t nr_mac[PICOPASS_BLOCK_LEN];
     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 {
     do {
         //check for file
         //check for file
         if(!flipper_format_file_open_existing(file, furi_string_get_cstr(temp_str))) break;
         if(!flipper_format_file_open_existing(file, furi_string_get_cstr(temp_str))) break;

+ 1 - 1
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(
             scene_manager_set_scene_state(
                 picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexSave);
                 picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexSave);
             scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName);
             scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName);
-            picopass->dev->format = PicopassDeviceSaveFormatHF;
+            picopass->dev->format = PicopassDeviceSaveFormatPartial;
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexSaveAsSeader) {
         } else if(event.event == SubmenuIndexSaveAsSeader) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(