Picopass: detect and show SE / SIO (#1701)
* detect and show SE / SIO * fix fault * remove bad read check Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									fb476c29e6
								
							
						
					
					
						commit
						c7cd5721ed
					
				| @ -309,10 +309,6 @@ ReturnCode picopass_device_decrypt(uint8_t* enc_data, uint8_t* dec_data) { | |||||||
| ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs) { | ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs) { | ||||||
|     ReturnCode err; |     ReturnCode err; | ||||||
| 
 | 
 | ||||||
|     // Thank you proxmark!
 |  | ||||||
|     pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); |  | ||||||
|     pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); |  | ||||||
| 
 |  | ||||||
|     pacs->biometrics = AA1[6].data[4]; |     pacs->biometrics = AA1[6].data[4]; | ||||||
|     pacs->pin_length = AA1[6].data[6] & 0x0F; |     pacs->pin_length = AA1[6].data[6] & 0x0F; | ||||||
|     pacs->encryption = AA1[6].data[7]; |     pacs->encryption = AA1[6].data[7]; | ||||||
| @ -347,6 +343,8 @@ ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pa | |||||||
|         FURI_LOG_D(TAG, "Unknown encryption"); |         FURI_LOG_D(TAG, "Unknown encryption"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     pacs->sio = (AA1[10].data[0] == 0x30); // rough check
 | ||||||
|  | 
 | ||||||
|     return ERR_NONE; |     return ERR_NONE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -47,6 +47,7 @@ typedef struct { | |||||||
| typedef struct { | typedef struct { | ||||||
|     bool legacy; |     bool legacy; | ||||||
|     bool se_enabled; |     bool se_enabled; | ||||||
|  |     bool sio; | ||||||
|     bool biometrics; |     bool biometrics; | ||||||
|     uint8_t pin_length; |     uint8_t pin_length; | ||||||
|     PicopassEncryption encryption; |     PicopassEncryption encryption; | ||||||
|  | |||||||
| @ -112,18 +112,12 @@ ReturnCode picopass_detect_card(int timeout) { | |||||||
|     return ERR_NONE; |     return ERR_NONE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ReturnCode picopass_read_card(PicopassBlock* AA1) { | ReturnCode picopass_read_preauth(PicopassBlock* AA1) { | ||||||
|     rfalPicoPassIdentifyRes idRes; |     rfalPicoPassIdentifyRes idRes; | ||||||
|     rfalPicoPassSelectRes selRes; |     rfalPicoPassSelectRes selRes; | ||||||
|     rfalPicoPassReadCheckRes rcRes; |  | ||||||
|     rfalPicoPassCheckRes chkRes; |  | ||||||
| 
 | 
 | ||||||
|     ReturnCode err; |     ReturnCode err; | ||||||
| 
 | 
 | ||||||
|     uint8_t div_key[8] = {0}; |  | ||||||
|     uint8_t mac[4] = {0}; |  | ||||||
|     uint8_t ccnr[12] = {0}; |  | ||||||
| 
 |  | ||||||
|     err = rfalPicoPassPollerIdentify(&idRes); |     err = rfalPicoPassPollerIdentify(&idRes); | ||||||
|     if(err != ERR_NONE) { |     if(err != ERR_NONE) { | ||||||
|         FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); |         FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); | ||||||
| @ -136,6 +130,62 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, selRes.CSN, sizeof(selRes.CSN)); | ||||||
|  |     FURI_LOG_D( | ||||||
|  |         TAG, | ||||||
|  |         "csn %02x%02x%02x%02x%02x%02x%02x%02x", | ||||||
|  |         AA1[PICOPASS_CSN_BLOCK_INDEX].data[0], | ||||||
|  |         AA1[PICOPASS_CSN_BLOCK_INDEX].data[1], | ||||||
|  |         AA1[PICOPASS_CSN_BLOCK_INDEX].data[2], | ||||||
|  |         AA1[PICOPASS_CSN_BLOCK_INDEX].data[3], | ||||||
|  |         AA1[PICOPASS_CSN_BLOCK_INDEX].data[4], | ||||||
|  |         AA1[PICOPASS_CSN_BLOCK_INDEX].data[5], | ||||||
|  |         AA1[PICOPASS_CSN_BLOCK_INDEX].data[6], | ||||||
|  |         AA1[PICOPASS_CSN_BLOCK_INDEX].data[7]); | ||||||
|  | 
 | ||||||
|  |     rfalPicoPassReadBlockRes cfg = {0}; | ||||||
|  |     err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg); | ||||||
|  |     memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data)); | ||||||
|  |     FURI_LOG_D( | ||||||
|  |         TAG, | ||||||
|  |         "config %02x%02x%02x%02x%02x%02x%02x%02x", | ||||||
|  |         AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0], | ||||||
|  |         AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[1], | ||||||
|  |         AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[2], | ||||||
|  |         AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[3], | ||||||
|  |         AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[4], | ||||||
|  |         AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[5], | ||||||
|  |         AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[6], | ||||||
|  |         AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7]); | ||||||
|  | 
 | ||||||
|  |     rfalPicoPassReadBlockRes aia; | ||||||
|  |     err = rfalPicoPassPollerReadBlock(PICOPASS_AIA_BLOCK_INDEX, &aia); | ||||||
|  |     memcpy(AA1[PICOPASS_AIA_BLOCK_INDEX].data, aia.data, sizeof(aia.data)); | ||||||
|  |     FURI_LOG_D( | ||||||
|  |         TAG, | ||||||
|  |         "aia %02x%02x%02x%02x%02x%02x%02x%02x", | ||||||
|  |         AA1[PICOPASS_AIA_BLOCK_INDEX].data[0], | ||||||
|  |         AA1[PICOPASS_AIA_BLOCK_INDEX].data[1], | ||||||
|  |         AA1[PICOPASS_AIA_BLOCK_INDEX].data[2], | ||||||
|  |         AA1[PICOPASS_AIA_BLOCK_INDEX].data[3], | ||||||
|  |         AA1[PICOPASS_AIA_BLOCK_INDEX].data[4], | ||||||
|  |         AA1[PICOPASS_AIA_BLOCK_INDEX].data[5], | ||||||
|  |         AA1[PICOPASS_AIA_BLOCK_INDEX].data[6], | ||||||
|  |         AA1[PICOPASS_AIA_BLOCK_INDEX].data[7]); | ||||||
|  | 
 | ||||||
|  |     return ERR_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ReturnCode picopass_read_card(PicopassBlock* AA1) { | ||||||
|  |     rfalPicoPassReadCheckRes rcRes; | ||||||
|  |     rfalPicoPassCheckRes chkRes; | ||||||
|  | 
 | ||||||
|  |     ReturnCode err; | ||||||
|  | 
 | ||||||
|  |     uint8_t div_key[8] = {0}; | ||||||
|  |     uint8_t mac[4] = {0}; | ||||||
|  |     uint8_t ccnr[12] = {0}; | ||||||
|  | 
 | ||||||
|     err = rfalPicoPassPollerReadCheck(&rcRes); |     err = rfalPicoPassPollerReadCheck(&rcRes); | ||||||
|     if(err != ERR_NONE) { |     if(err != ERR_NONE) { | ||||||
|         FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); |         FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); | ||||||
| @ -143,7 +193,7 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { | |||||||
|     } |     } | ||||||
|     memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 |     memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 | ||||||
| 
 | 
 | ||||||
|     loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key); |     loclass_diversifyKey(AA1[PICOPASS_CSN_BLOCK_INDEX].data, picopass_iclass_key, div_key); | ||||||
|     loclass_opt_doReaderMAC(ccnr, div_key, mac); |     loclass_opt_doReaderMAC(ccnr, div_key, mac); | ||||||
| 
 | 
 | ||||||
|     err = rfalPicoPassPollerCheck(mac, &chkRes); |     err = rfalPicoPassPollerCheck(mac, &chkRes); | ||||||
| @ -152,18 +202,11 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     rfalPicoPassReadBlockRes csn; |     size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? | ||||||
|     err = rfalPicoPassPollerReadBlock(PICOPASS_CSN_BLOCK_INDEX, &csn); |                            AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : | ||||||
|     memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, csn.data, sizeof(csn.data)); |                            PICOPASS_MAX_APP_LIMIT; | ||||||
| 
 |  | ||||||
|     rfalPicoPassReadBlockRes cfg; |  | ||||||
|     err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg); |  | ||||||
|     memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data)); |  | ||||||
| 
 |  | ||||||
|     size_t app_limit = cfg.data[0] < PICOPASS_MAX_APP_LIMIT ? cfg.data[0] : PICOPASS_MAX_APP_LIMIT; |  | ||||||
| 
 | 
 | ||||||
|     for(size_t i = 2; i < app_limit; i++) { |     for(size_t i = 2; i < app_limit; i++) { | ||||||
|         FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i); |  | ||||||
|         rfalPicoPassReadBlockRes block; |         rfalPicoPassReadBlockRes block; | ||||||
|         err = rfalPicoPassPollerReadBlock(i, &block); |         err = rfalPicoPassPollerReadBlock(i, &block); | ||||||
|         if(err != ERR_NONE) { |         if(err != ERR_NONE) { | ||||||
| @ -287,11 +330,30 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { | |||||||
|     PicopassPacs* pacs = &dev_data->pacs; |     PicopassPacs* pacs = &dev_data->pacs; | ||||||
|     ReturnCode err; |     ReturnCode err; | ||||||
| 
 | 
 | ||||||
|  |     // reset device data
 | ||||||
|  |     for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { | ||||||
|  |         memset(AA1[i].data, 0, sizeof(AA1[i].data)); | ||||||
|  |     } | ||||||
|  |     memset(pacs, 0, sizeof(PicopassPacs)); | ||||||
|  | 
 | ||||||
|     PicopassWorkerEvent nextState = PicopassWorkerEventSuccess; |     PicopassWorkerEvent nextState = PicopassWorkerEventSuccess; | ||||||
| 
 | 
 | ||||||
|     while(picopass_worker->state == PicopassWorkerStateDetect) { |     while(picopass_worker->state == PicopassWorkerStateDetect) { | ||||||
|         if(picopass_detect_card(1000) == ERR_NONE) { |         if(picopass_detect_card(1000) == ERR_NONE) { | ||||||
|             // Process first found device
 |             // Process first found device
 | ||||||
|  |             err = picopass_read_preauth(AA1); | ||||||
|  |             if(err != ERR_NONE) { | ||||||
|  |                 FURI_LOG_E(TAG, "picopass_read_preauth error %d", err); | ||||||
|  |                 nextState = PicopassWorkerEventFail; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Thank you proxmark!
 | ||||||
|  |             pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); | ||||||
|  |             pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); | ||||||
|  |             if(pacs->se_enabled) { | ||||||
|  |                 FURI_LOG_D(TAG, "SE enabled"); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             err = picopass_read_card(AA1); |             err = picopass_read_card(AA1); | ||||||
|             if(err != ERR_NONE) { |             if(err != ERR_NONE) { | ||||||
|                 FURI_LOG_E(TAG, "picopass_read_card error %d", err); |                 FURI_LOG_E(TAG, "picopass_read_card error %d", err); | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ typedef enum { | |||||||
|     PicopassWorkerEventSuccess, |     PicopassWorkerEventSuccess, | ||||||
|     PicopassWorkerEventFail, |     PicopassWorkerEventFail, | ||||||
|     PicopassWorkerEventNoCardDetected, |     PicopassWorkerEventNoCardDetected, | ||||||
|  |     PicopassWorkerEventSeEnabled, | ||||||
| 
 | 
 | ||||||
|     PicopassWorkerEventStartReading, |     PicopassWorkerEventStartReading, | ||||||
| } PicopassWorkerEvent; | } PicopassWorkerEvent; | ||||||
|  | |||||||
| @ -17,8 +17,10 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|     Picopass* picopass = context; |     Picopass* picopass = context; | ||||||
|     string_t credential_str; |     string_t credential_str; | ||||||
|     string_t wiegand_str; |     string_t wiegand_str; | ||||||
|  |     string_t sio_str; | ||||||
|     string_init(credential_str); |     string_init(credential_str); | ||||||
|     string_init(wiegand_str); |     string_init(wiegand_str); | ||||||
|  |     string_init(sio_str); | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||||
| 
 | 
 | ||||||
| @ -32,6 +34,10 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|     if(pacs->record.bitLength == 0) { |     if(pacs->record.bitLength == 0) { | ||||||
|         string_cat_printf(wiegand_str, "Read Failed"); |         string_cat_printf(wiegand_str, "Read Failed"); | ||||||
| 
 | 
 | ||||||
|  |         if(pacs->se_enabled) { | ||||||
|  |             string_cat_printf(credential_str, "SE enabled"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         widget_add_button_element( |         widget_add_button_element( | ||||||
|             widget, |             widget, | ||||||
|             GuiButtonTypeLeft, |             GuiButtonTypeLeft, | ||||||
| @ -39,8 +45,6 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|             picopass_scene_read_card_success_widget_callback, |             picopass_scene_read_card_success_widget_callback, | ||||||
|             picopass); |             picopass); | ||||||
| 
 | 
 | ||||||
|         widget_add_string_element( |  | ||||||
|             widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); |  | ||||||
|     } else { |     } else { | ||||||
|         size_t bytesLength = 1 + pacs->record.bitLength / 8; |         size_t bytesLength = 1 + pacs->record.bitLength / 8; | ||||||
|         string_set_str(credential_str, ""); |         string_set_str(credential_str, ""); | ||||||
| @ -55,6 +59,10 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|             string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); |             string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if(pacs->sio) { | ||||||
|  |             string_cat_printf(sio_str, "+SIO"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         widget_add_button_element( |         widget_add_button_element( | ||||||
|             widget, |             widget, | ||||||
|             GuiButtonTypeLeft, |             GuiButtonTypeLeft, | ||||||
| @ -68,20 +76,18 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|             "More", |             "More", | ||||||
|             picopass_scene_read_card_success_widget_callback, |             picopass_scene_read_card_success_widget_callback, | ||||||
|             picopass); |             picopass); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); |         widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|             widget, |         widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str)); | ||||||
|             64, |     widget_add_string_element( | ||||||
|             32, |         widget, 64, 42, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(sio_str)); | ||||||
|             AlignCenter, | 
 | ||||||
|             AlignCenter, |  | ||||||
|             FontSecondary, |  | ||||||
|             string_get_cstr(credential_str)); |  | ||||||
|     } |  | ||||||
|     string_clear(credential_str); |     string_clear(credential_str); | ||||||
|     string_clear(wiegand_str); |     string_clear(wiegand_str); | ||||||
|  |     string_clear(sio_str); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); |     view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Patrick Cunningham
						Patrick Cunningham