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>
This commit is contained in:
		
							parent
							
								
									9dd1fb64b7
								
							
						
					
					
						commit
						eefca9f498
					
				| @ -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; | ||||
|  | ||||
| @ -12,7 +12,7 @@ typedef enum { | ||||
|     // Main worker states
 | ||||
|     PicopassWorkerStateDetect, | ||||
|     PicopassWorkerStateWrite, | ||||
|     PicopassWorkerStateWriteStandardKey, | ||||
|     PicopassWorkerStateWriteKey, | ||||
|     // Transition
 | ||||
|     PicopassWorkerStateStop, | ||||
| } PicopassWorkerState; | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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
									
								
								applications/plugins/picopass/scenes/picopass_scene_key_menu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								applications/plugins/picopass/scenes/picopass_scene_key_menu.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
| } | ||||
| @ -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 { | ||||
|  | ||||
| @ -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; | ||||
|         } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Eric Betts
						Eric Betts