Explorar o código

Support reseting iCx cards (#2451)

* Support reseting iCx cards
* add submenu
* Fix auth
* switch key derivation to use same method
* test system keys using both elite and standard kdf

Co-authored-by: あく <alleteam@gmail.com>
Eric Betts %!s(int64=2) %!d(string=hai) anos
pai
achega
eefca9f498

+ 39 - 20
applications/plugins/picopass/picopass_worker.c

@@ -7,6 +7,9 @@
 const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
 const uint8_t picopass_factory_credit_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00};
 const uint8_t picopass_factory_debit_key[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87};
+const uint8_t picopass_xice_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88};
+const uint8_t picopass_xicl_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88};
+const uint8_t picopass_xics_key[] = {0x66, 0x66, 0x20, 0x20, 0x66, 0x66, 0x88, 0x88};
 
 static void picopass_worker_enable_field() {
     furi_hal_nfc_ll_txrx_on();
@@ -192,7 +195,7 @@ static ReturnCode picopass_auth_standard(uint8_t* csn, uint8_t* div_key) {
     }
     memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 
-    loclass_diversifyKey(csn, picopass_iclass_key, div_key);
+    loclass_iclass_calc_div_key(csn, (uint8_t*)picopass_iclass_key, div_key, false);
     loclass_opt_doReaderMAC(ccnr, div_key, mac);
 
     return rfalPicoPassPollerCheck(mac, &chkRes);
@@ -214,7 +217,7 @@ static ReturnCode picopass_auth_factory(uint8_t* csn, uint8_t* div_key) {
     }
     memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 
-    loclass_diversifyKey(csn, picopass_factory_debit_key, div_key);
+    loclass_iclass_calc_div_key(csn, (uint8_t*)picopass_factory_debit_key, div_key, false);
     loclass_opt_doReaderMAC(ccnr, div_key, mac);
 
     return rfalPicoPassPollerCheck(mac, &chkRes);
@@ -224,7 +227,8 @@ static ReturnCode picopass_auth_dict(
     uint8_t* csn,
     PicopassPacs* pacs,
     uint8_t* div_key,
-    IclassEliteDictType dict_type) {
+    IclassEliteDictType dict_type,
+    bool elite) {
     rfalPicoPassReadCheckRes rcRes;
     rfalPicoPassCheckRes chkRes;
 
@@ -269,7 +273,7 @@ static ReturnCode picopass_auth_dict(
         }
         memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 
-        loclass_iclass_calc_div_key(csn, key, div_key, true);
+        loclass_iclass_calc_div_key(csn, key, div_key, elite);
         loclass_opt_doReaderMAC(ccnr, div_key, mac);
 
         err = rfalPicoPassPollerCheck(mac, &chkRes);
@@ -303,22 +307,35 @@ ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) {
         return ERR_NONE;
     }
 
-    FURI_LOG_I(TAG, "Starting user dictionary attack");
+    FURI_LOG_I(TAG, "Starting user dictionary attack [Elite KDF]");
     err = picopass_auth_dict(
         AA1[PICOPASS_CSN_BLOCK_INDEX].data,
         pacs,
         AA1[PICOPASS_KD_BLOCK_INDEX].data,
-        IclassEliteDictTypeUser);
+        IclassEliteDictTypeUser,
+        true);
     if(err == ERR_NONE) {
         return ERR_NONE;
     }
 
-    FURI_LOG_I(TAG, "Starting system dictionary attack");
+    FURI_LOG_I(TAG, "Starting system dictionary attack [Elite KDF]");
     err = picopass_auth_dict(
         AA1[PICOPASS_CSN_BLOCK_INDEX].data,
         pacs,
         AA1[PICOPASS_KD_BLOCK_INDEX].data,
-        IclassEliteDictTypeFlipper);
+        IclassEliteDictTypeFlipper,
+        true);
+    if(err == ERR_NONE) {
+        return ERR_NONE;
+    }
+
+    FURI_LOG_I(TAG, "Starting system dictionary attack [Standard KDF]");
+    err = picopass_auth_dict(
+        AA1[PICOPASS_CSN_BLOCK_INDEX].data,
+        pacs,
+        AA1[PICOPASS_KD_BLOCK_INDEX].data,
+        IclassEliteDictTypeFlipper,
+        false);
     if(err == ERR_NONE) {
         return ERR_NONE;
     }
@@ -396,7 +413,7 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) {
     }
     memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 
-    loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key);
+    loclass_iclass_calc_div_key(selRes.CSN, (uint8_t*)picopass_iclass_key, div_key, false);
     loclass_opt_doReaderMAC(ccnr, div_key, mac);
 
     err = rfalPicoPassPollerCheck(mac, &chkRes);
@@ -438,7 +455,7 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) {
     return ERR_NONE;
 }
 
-ReturnCode picopass_write_block(PicopassPacs* pacs, uint8_t blockNo, uint8_t* newBlock) {
+ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* newBlock) {
     rfalPicoPassIdentifyRes idRes;
     rfalPicoPassSelectRes selRes;
     rfalPicoPassReadCheckRes rcRes;
@@ -446,7 +463,6 @@ ReturnCode picopass_write_block(PicopassPacs* pacs, uint8_t blockNo, uint8_t* ne
 
     ReturnCode err;
 
-    uint8_t div_key[8] = {0};
     uint8_t mac[4] = {0};
     uint8_t ccnr[12] = {0};
 
@@ -469,9 +485,12 @@ ReturnCode picopass_write_block(PicopassPacs* pacs, uint8_t blockNo, uint8_t* ne
     }
     memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 
-    loclass_diversifyKey(selRes.CSN, pacs->key, div_key);
-    loclass_opt_doReaderMAC(ccnr, div_key, mac);
+    if(memcmp(selRes.CSN, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN) != 0) {
+        FURI_LOG_E(TAG, "Wrong CSN for write");
+        return ERR_REQUEST;
+    }
 
+    loclass_opt_doReaderMAC(ccnr, AA1[PICOPASS_KD_BLOCK_INDEX].data, mac);
     err = rfalPicoPassPollerCheck(mac, &chkRes);
     if(err != ERR_NONE) {
         FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err);
@@ -489,7 +508,7 @@ ReturnCode picopass_write_block(PicopassPacs* pacs, uint8_t blockNo, uint8_t* ne
         newBlock[5],
         newBlock[6],
         newBlock[7]};
-    loclass_doMAC_N(data, sizeof(data), div_key, mac);
+    loclass_doMAC_N(data, sizeof(data), AA1[PICOPASS_KD_BLOCK_INDEX].data, mac);
     FURI_LOG_D(
         TAG,
         "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
@@ -524,8 +543,8 @@ int32_t picopass_worker_task(void* context) {
         picopass_worker_detect(picopass_worker);
     } else if(picopass_worker->state == PicopassWorkerStateWrite) {
         picopass_worker_write(picopass_worker);
-    } else if(picopass_worker->state == PicopassWorkerStateWriteStandardKey) {
-        picopass_worker_write_standard_key(picopass_worker);
+    } else if(picopass_worker->state == PicopassWorkerStateWriteKey) {
+        picopass_worker_write_key(picopass_worker);
     }
     picopass_worker_disable_field(ERR_NONE);
 
@@ -633,7 +652,7 @@ void picopass_worker_write(PicopassWorker* picopass_worker) {
     }
 }
 
-void picopass_worker_write_standard_key(PicopassWorker* picopass_worker) {
+void picopass_worker_write_key(PicopassWorker* picopass_worker) {
     PicopassDeviceData* dev_data = picopass_worker->dev_data;
     PicopassBlock* AA1 = dev_data->AA1;
     PicopassPacs* pacs = &dev_data->pacs;
@@ -646,7 +665,7 @@ void picopass_worker_write_standard_key(PicopassWorker* picopass_worker) {
     uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data;
 
     uint8_t newKey[PICOPASS_BLOCK_LEN] = {0};
-    loclass_diversifyKey(csn, picopass_iclass_key, newKey);
+    loclass_iclass_calc_div_key(csn, pacs->key, newKey, false);
 
     if((fuses & 0x80) == 0x80) {
         FURI_LOG_D(TAG, "Plain write for personalized mode key change");
@@ -658,9 +677,9 @@ void picopass_worker_write_standard_key(PicopassWorker* picopass_worker) {
         }
     }
 
-    while(picopass_worker->state == PicopassWorkerStateWriteStandardKey) {
+    while(picopass_worker->state == PicopassWorkerStateWriteKey) {
         if(picopass_detect_card(1000) == ERR_NONE) {
-            err = picopass_write_block(pacs, PICOPASS_KD_BLOCK_INDEX, newKey);
+            err = picopass_write_block(AA1, PICOPASS_KD_BLOCK_INDEX, newKey);
             if(err != ERR_NONE) {
                 FURI_LOG_E(TAG, "picopass_write_block error %d", err);
                 nextState = PicopassWorkerEventFail;

+ 1 - 1
applications/plugins/picopass/picopass_worker.h

@@ -12,7 +12,7 @@ typedef enum {
     // Main worker states
     PicopassWorkerStateDetect,
     PicopassWorkerStateWrite,
-    PicopassWorkerStateWriteStandardKey,
+    PicopassWorkerStateWriteKey,
     // Transition
     PicopassWorkerStateStop,
 } PicopassWorkerState;

+ 1 - 1
applications/plugins/picopass/picopass_worker_i.h

@@ -31,4 +31,4 @@ int32_t picopass_worker_task(void* context);
 
 void picopass_worker_detect(PicopassWorker* picopass_worker);
 void picopass_worker_write(PicopassWorker* picopass_worker);
-void picopass_worker_write_standard_key(PicopassWorker* picopass_worker);
+void picopass_worker_write_key(PicopassWorker* picopass_worker);

+ 13 - 0
applications/plugins/picopass/scenes/picopass_scene_card_menu.c

@@ -3,6 +3,7 @@
 enum SubmenuIndex {
     SubmenuIndexSave,
     SubmenuIndexSaveAsLF,
+    SubmenuIndexChangeKey,
 };
 
 void picopass_scene_card_menu_submenu_callback(void* context, uint32_t index) {
@@ -25,6 +26,13 @@ void picopass_scene_card_menu_on_enter(void* context) {
             picopass_scene_card_menu_submenu_callback,
             picopass);
     }
+    submenu_add_item(
+        submenu,
+        "Change Key",
+        SubmenuIndexChangeKey,
+        picopass_scene_card_menu_submenu_callback,
+        picopass);
+
     submenu_set_selected_item(
         picopass->submenu,
         scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneCardMenu));
@@ -49,6 +57,11 @@ bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
             picopass->dev->format = PicopassDeviceSaveFormatLF;
             scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName);
             consumed = true;
+        } else if(event.event == SubmenuIndexChangeKey) {
+            scene_manager_set_scene_state(
+                picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexChangeKey);
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneKeyMenu);
+            consumed = true;
         }
     } else if(event.type == SceneManagerEventTypeBack) {
         consumed = scene_manager_search_and_switch_to_previous_scene(

+ 1 - 0
applications/plugins/picopass/scenes/picopass_scene_config.h

@@ -13,3 +13,4 @@ ADD_SCENE(picopass, write_card, WriteCard)
 ADD_SCENE(picopass, write_card_success, WriteCardSuccess)
 ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess)
 ADD_SCENE(picopass, write_key, WriteKey)
+ADD_SCENE(picopass, key_menu, KeyMenu)

+ 100 - 0
applications/plugins/picopass/scenes/picopass_scene_key_menu.c

@@ -0,0 +1,100 @@
+#include "../picopass_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexWriteStandard,
+    SubmenuIndexWriteiCE,
+    SubmenuIndexWriteiCL,
+    SubmenuIndexWriteiCS,
+    SubmenuIndexWriteCustom, //TODO: user input of key
+};
+
+extern const uint8_t picopass_xice_key[];
+extern const uint8_t picopass_xicl_key[];
+extern const uint8_t picopass_xics_key[];
+extern const uint8_t picopass_iclass_key[];
+
+void picopass_scene_key_menu_submenu_callback(void* context, uint32_t index) {
+    Picopass* picopass = context;
+
+    view_dispatcher_send_custom_event(picopass->view_dispatcher, index);
+}
+
+void picopass_scene_key_menu_on_enter(void* context) {
+    Picopass* picopass = context;
+    Submenu* submenu = picopass->submenu;
+
+    submenu_add_item(
+        submenu,
+        "Write Standard",
+        SubmenuIndexWriteStandard,
+        picopass_scene_key_menu_submenu_callback,
+        picopass);
+    submenu_add_item(
+        submenu,
+        "Write iCE",
+        SubmenuIndexWriteiCE,
+        picopass_scene_key_menu_submenu_callback,
+        picopass);
+    submenu_add_item(
+        submenu,
+        "Write iCL",
+        SubmenuIndexWriteiCL,
+        picopass_scene_key_menu_submenu_callback,
+        picopass);
+    submenu_add_item(
+        submenu,
+        "Write iCS",
+        SubmenuIndexWriteiCS,
+        picopass_scene_key_menu_submenu_callback,
+        picopass);
+
+    submenu_set_selected_item(
+        picopass->submenu,
+        scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneKeyMenu));
+
+    view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu);
+}
+
+bool picopass_scene_key_menu_on_event(void* context, SceneManagerEvent event) {
+    Picopass* picopass = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexWriteStandard) {
+            scene_manager_set_scene_state(
+                picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteStandard);
+            memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN);
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
+            consumed = true;
+        } else if(event.event == SubmenuIndexWriteiCE) {
+            scene_manager_set_scene_state(
+                picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE);
+            memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, PICOPASS_BLOCK_LEN);
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
+            consumed = true;
+        } else if(event.event == SubmenuIndexWriteiCL) {
+            scene_manager_set_scene_state(
+                picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE);
+            memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, PICOPASS_BLOCK_LEN);
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
+            consumed = true;
+        } else if(event.event == SubmenuIndexWriteiCS) {
+            scene_manager_set_scene_state(
+                picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE);
+            memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, PICOPASS_BLOCK_LEN);
+            scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
+            consumed = true;
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_search_and_switch_to_previous_scene(
+            picopass->scene_manager, PicopassSceneStart);
+    }
+
+    return consumed;
+}
+
+void picopass_scene_key_menu_on_exit(void* context) {
+    Picopass* picopass = context;
+
+    submenu_reset(picopass->submenu);
+}

+ 2 - 2
applications/plugins/picopass/scenes/picopass_scene_read_card.c

@@ -1,7 +1,7 @@
 #include "../picopass_i.h"
 #include <dolphin/dolphin.h>
 
-const uint8_t picopass_factory_key_check[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87};
+extern const uint8_t picopass_factory_debit_key[];
 
 void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) {
     UNUSED(event);
@@ -38,7 +38,7 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) {
         if(event.event == PicopassCustomEventWorkerExit) {
             if(memcmp(
                    picopass->dev->dev_data.pacs.key,
-                   picopass_factory_key_check,
+                   picopass_factory_debit_key,
                    PICOPASS_BLOCK_LEN) == 0) {
                 scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess);
             } else {

+ 3 - 0
applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c

@@ -1,6 +1,8 @@
 #include "../picopass_i.h"
 #include <dolphin/dolphin.h>
 
+extern const uint8_t picopass_iclass_key[];
+
 void picopass_scene_read_factory_success_widget_callback(
     GuiButtonType result,
     InputType type,
@@ -63,6 +65,7 @@ bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEve
         if(event.event == GuiButtonTypeLeft) {
             consumed = scene_manager_previous_scene(picopass->scene_manager);
         } else if(event.event == GuiButtonTypeCenter) {
+            memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN);
             scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
             consumed = true;
         }

+ 1 - 1
applications/plugins/picopass/scenes/picopass_scene_write_key.c

@@ -20,7 +20,7 @@ void picopass_scene_write_key_on_enter(void* context) {
     view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup);
     picopass_worker_start(
         picopass->worker,
-        PicopassWorkerStateWriteStandardKey,
+        PicopassWorkerStateWriteKey,
         &picopass->dev->dev_data,
         picopass_write_key_worker_callback,
         picopass);