From eefca9f498ea346128db62d1072010aaf75db17e Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Tue, 7 Mar 2023 02:53:52 -0800 Subject: [PATCH] Support reseting iCx cards (#2451) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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: あく --- .../plugins/picopass/picopass_worker.c | 59 +++++++---- .../plugins/picopass/picopass_worker.h | 2 +- .../plugins/picopass/picopass_worker_i.h | 2 +- .../scenes/picopass_scene_card_menu.c | 13 +++ .../picopass/scenes/picopass_scene_config.h | 1 + .../picopass/scenes/picopass_scene_key_menu.c | 100 ++++++++++++++++++ .../scenes/picopass_scene_read_card.c | 4 +- .../picopass_scene_read_factory_success.c | 3 + .../scenes/picopass_scene_write_key.c | 2 +- 9 files changed, 161 insertions(+), 25 deletions(-) create mode 100644 applications/plugins/picopass/scenes/picopass_scene_key_menu.c diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/plugins/picopass/picopass_worker.c index 6d904478..024c5112 100644 --- a/applications/plugins/picopass/picopass_worker.c +++ b/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; diff --git a/applications/plugins/picopass/picopass_worker.h b/applications/plugins/picopass/picopass_worker.h index 775212c6..02b088b2 100644 --- a/applications/plugins/picopass/picopass_worker.h +++ b/applications/plugins/picopass/picopass_worker.h @@ -12,7 +12,7 @@ typedef enum { // Main worker states PicopassWorkerStateDetect, PicopassWorkerStateWrite, - PicopassWorkerStateWriteStandardKey, + PicopassWorkerStateWriteKey, // Transition PicopassWorkerStateStop, } PicopassWorkerState; diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/plugins/picopass/picopass_worker_i.h index cf55fbdf..f41cfce4 100644 --- a/applications/plugins/picopass/picopass_worker_i.h +++ b/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); diff --git a/applications/plugins/picopass/scenes/picopass_scene_card_menu.c b/applications/plugins/picopass/scenes/picopass_scene_card_menu.c index a424b919..fe63f7c8 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_card_menu.c +++ b/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( diff --git a/applications/plugins/picopass/scenes/picopass_scene_config.h b/applications/plugins/picopass/scenes/picopass_scene_config.h index 95700787..f5a90d46 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_config.h +++ b/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) diff --git a/applications/plugins/picopass/scenes/picopass_scene_key_menu.c b/applications/plugins/picopass/scenes/picopass_scene_key_menu.c new file mode 100644 index 00000000..b1db37f8 --- /dev/null +++ b/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); +} diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card.c b/applications/plugins/picopass/scenes/picopass_scene_read_card.c index 90422a2e..c62cba8e 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card.c @@ -1,7 +1,7 @@ #include "../picopass_i.h" #include -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 { diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c index 8e32d21f..b98951dc 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c @@ -1,6 +1,8 @@ #include "../picopass_i.h" #include +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; } diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_key.c b/applications/plugins/picopass/scenes/picopass_scene_write_key.c index 83d594ca..0f417e1c 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_write_key.c +++ b/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);