Picopass: Read Elite (#1888)
* working elite dict * add csn to display Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									68009c6230
								
							
						
					
					
						commit
						56f760aa07
					
				
							
								
								
									
										151
									
								
								applications/plugins/picopass/helpers/iclass_elite_dict.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								applications/plugins/picopass/helpers/iclass_elite_dict.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | |||||||
|  | #include "iclass_elite_dict.h" | ||||||
|  | 
 | ||||||
|  | #include <lib/toolbox/args.h> | ||||||
|  | #include <lib/flipper_format/flipper_format.h> | ||||||
|  | 
 | ||||||
|  | #define ICLASS_ELITE_DICT_FLIPPER_PATH EXT_PATH("picopass/assets/iclass_elite_dict.txt") | ||||||
|  | #define ICLASS_ELITE_DICT_USER_PATH EXT_PATH("picopass/assets/iclass_elite_dict_user.txt") | ||||||
|  | 
 | ||||||
|  | #define TAG "IclassEliteDict" | ||||||
|  | 
 | ||||||
|  | #define ICLASS_ELITE_KEY_LINE_LEN (17) | ||||||
|  | #define ICLASS_ELITE_KEY_LEN (8) | ||||||
|  | 
 | ||||||
|  | struct IclassEliteDict { | ||||||
|  |     Stream* stream; | ||||||
|  |     uint32_t total_keys; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) { | ||||||
|  |     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     bool dict_present = false; | ||||||
|  |     if(dict_type == IclassEliteDictTypeFlipper) { | ||||||
|  |         dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_PATH, NULL) == | ||||||
|  |                        FSE_OK; | ||||||
|  |     } else if(dict_type == IclassEliteDictTypeUser) { | ||||||
|  |         dict_present = storage_common_stat(storage, ICLASS_ELITE_DICT_USER_PATH, NULL) == FSE_OK; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  | 
 | ||||||
|  |     return dict_present; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) { | ||||||
|  |     IclassEliteDict* dict = malloc(sizeof(IclassEliteDict)); | ||||||
|  |     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||||
|  |     dict->stream = buffered_file_stream_alloc(storage); | ||||||
|  |     furi_record_close(RECORD_STORAGE); | ||||||
|  |     FuriString* next_line = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     bool dict_loaded = false; | ||||||
|  |     do { | ||||||
|  |         if(dict_type == IclassEliteDictTypeFlipper) { | ||||||
|  |             if(!buffered_file_stream_open( | ||||||
|  |                    dict->stream, ICLASS_ELITE_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||||
|  |                 buffered_file_stream_close(dict->stream); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } else if(dict_type == IclassEliteDictTypeUser) { | ||||||
|  |             if(!buffered_file_stream_open( | ||||||
|  |                    dict->stream, ICLASS_ELITE_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { | ||||||
|  |                 buffered_file_stream_close(dict->stream); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Read total amount of keys
 | ||||||
|  |         while(true) { | ||||||
|  |             if(!stream_read_line(dict->stream, next_line)) break; | ||||||
|  |             if(furi_string_get_char(next_line, 0) == '#') continue; | ||||||
|  |             if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; | ||||||
|  |             dict->total_keys++; | ||||||
|  |         } | ||||||
|  |         furi_string_reset(next_line); | ||||||
|  |         stream_rewind(dict->stream); | ||||||
|  | 
 | ||||||
|  |         dict_loaded = true; | ||||||
|  |         FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys); | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     if(!dict_loaded) { | ||||||
|  |         buffered_file_stream_close(dict->stream); | ||||||
|  |         free(dict); | ||||||
|  |         dict = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_string_free(next_line); | ||||||
|  | 
 | ||||||
|  |     return dict; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void iclass_elite_dict_free(IclassEliteDict* dict) { | ||||||
|  |     furi_assert(dict); | ||||||
|  |     furi_assert(dict->stream); | ||||||
|  | 
 | ||||||
|  |     buffered_file_stream_close(dict->stream); | ||||||
|  |     stream_free(dict->stream); | ||||||
|  |     free(dict); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict) { | ||||||
|  |     furi_assert(dict); | ||||||
|  | 
 | ||||||
|  |     return dict->total_keys; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key) { | ||||||
|  |     furi_assert(dict); | ||||||
|  |     furi_assert(dict->stream); | ||||||
|  | 
 | ||||||
|  |     uint8_t key_byte_tmp = 0; | ||||||
|  |     FuriString* next_line = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     bool key_read = false; | ||||||
|  |     *key = 0ULL; | ||||||
|  |     while(!key_read) { | ||||||
|  |         if(!stream_read_line(dict->stream, next_line)) break; | ||||||
|  |         if(furi_string_get_char(next_line, 0) == '#') continue; | ||||||
|  |         if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; | ||||||
|  |         for(uint8_t i = 0; i < ICLASS_ELITE_KEY_LEN * 2; i += 2) { | ||||||
|  |             args_char_to_hex( | ||||||
|  |                 furi_string_get_char(next_line, i), | ||||||
|  |                 furi_string_get_char(next_line, i + 1), | ||||||
|  |                 &key_byte_tmp); | ||||||
|  |             key[i / 2] = key_byte_tmp; | ||||||
|  |         } | ||||||
|  |         key_read = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_string_free(next_line); | ||||||
|  |     return key_read; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iclass_elite_dict_rewind(IclassEliteDict* dict) { | ||||||
|  |     furi_assert(dict); | ||||||
|  |     furi_assert(dict->stream); | ||||||
|  | 
 | ||||||
|  |     return stream_rewind(dict->stream); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) { | ||||||
|  |     furi_assert(dict); | ||||||
|  |     furi_assert(dict->stream); | ||||||
|  | 
 | ||||||
|  |     FuriString* key_str = furi_string_alloc(); | ||||||
|  |     for(size_t i = 0; i < 6; i++) { | ||||||
|  |         furi_string_cat_printf(key_str, "%02X", key[i]); | ||||||
|  |     } | ||||||
|  |     furi_string_cat_printf(key_str, "\n"); | ||||||
|  | 
 | ||||||
|  |     bool key_added = false; | ||||||
|  |     do { | ||||||
|  |         if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; | ||||||
|  |         if(!stream_insert_string(dict->stream, key_str)) break; | ||||||
|  |         key_added = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     furi_string_free(key_str); | ||||||
|  |     return key_added; | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								applications/plugins/picopass/helpers/iclass_elite_dict.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								applications/plugins/picopass/helpers/iclass_elite_dict.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <lib/flipper_format/flipper_format.h> | ||||||
|  | #include <lib/toolbox/stream/file_stream.h> | ||||||
|  | #include <lib/toolbox/stream/buffered_file_stream.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     IclassEliteDictTypeUser, | ||||||
|  |     IclassEliteDictTypeFlipper, | ||||||
|  | } IclassEliteDictType; | ||||||
|  | 
 | ||||||
|  | typedef struct IclassEliteDict IclassEliteDict; | ||||||
|  | 
 | ||||||
|  | bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type); | ||||||
|  | 
 | ||||||
|  | IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type); | ||||||
|  | 
 | ||||||
|  | void iclass_elite_dict_free(IclassEliteDict* dict); | ||||||
|  | 
 | ||||||
|  | uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict); | ||||||
|  | 
 | ||||||
|  | bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key); | ||||||
|  | 
 | ||||||
|  | bool iclass_elite_dict_rewind(IclassEliteDict* dict); | ||||||
|  | 
 | ||||||
|  | bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key); | ||||||
| @ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8 | |||||||
|  * @param loclass_hash1 loclass_hash1 |  * @param loclass_hash1 loclass_hash1 | ||||||
|  * @param key_sel output key_sel=h[loclass_hash1[i]] |  * @param key_sel output key_sel=h[loclass_hash1[i]] | ||||||
|  */ |  */ | ||||||
| void hash2(uint8_t* key64, uint8_t* outp_keytable) { | void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) { | ||||||
|     /**
 |     /**
 | ||||||
|      *Expected: |      *Expected: | ||||||
|      * High Security Key Table |      * High Security Key Table | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ | |||||||
| #include "rfal_picopass.h" | #include "rfal_picopass.h" | ||||||
| #include <optimized_ikeys.h> | #include <optimized_ikeys.h> | ||||||
| #include <optimized_cipher.h> | #include <optimized_cipher.h> | ||||||
|  | #include "helpers/iclass_elite_dict.h" | ||||||
| 
 | 
 | ||||||
| #define PICOPASS_DEV_NAME_MAX_LEN 22 | #define PICOPASS_DEV_NAME_MAX_LEN 22 | ||||||
| #define PICOPASS_READER_DATA_MAX_SIZE 64 | #define PICOPASS_READER_DATA_MAX_SIZE 64 | ||||||
| @ -49,6 +50,7 @@ typedef struct { | |||||||
|     bool se_enabled; |     bool se_enabled; | ||||||
|     bool sio; |     bool sio; | ||||||
|     bool biometrics; |     bool biometrics; | ||||||
|  |     uint8_t key[8]; | ||||||
|     uint8_t pin_length; |     uint8_t pin_length; | ||||||
|     PicopassEncryption encryption; |     PicopassEncryption encryption; | ||||||
|     uint8_t credential[8]; |     uint8_t credential[8]; | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| #include "picopass_worker_i.h" | #include "picopass_worker_i.h" | ||||||
| 
 | 
 | ||||||
|  | #include <flipper_format/flipper_format.h> | ||||||
|  | 
 | ||||||
| #define TAG "PicopassWorker" | #define TAG "PicopassWorker" | ||||||
| 
 | 
 | ||||||
| const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; | const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; | ||||||
| @ -176,7 +178,7 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { | |||||||
|     return ERR_NONE; |     return ERR_NONE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ReturnCode picopass_read_card(PicopassBlock* AA1) { | ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { | ||||||
|     rfalPicoPassReadCheckRes rcRes; |     rfalPicoPassReadCheckRes rcRes; | ||||||
|     rfalPicoPassCheckRes chkRes; |     rfalPicoPassCheckRes chkRes; | ||||||
| 
 | 
 | ||||||
| @ -197,10 +199,68 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { | |||||||
|     loclass_opt_doReaderMAC(ccnr, div_key, mac); |     loclass_opt_doReaderMAC(ccnr, div_key, mac); | ||||||
| 
 | 
 | ||||||
|     err = rfalPicoPassPollerCheck(mac, &chkRes); |     err = rfalPicoPassPollerCheck(mac, &chkRes); | ||||||
|     if(err != ERR_NONE) { |     if(err == ERR_NONE) { | ||||||
|         FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); |         return ERR_NONE; | ||||||
|         return err; |  | ||||||
|     } |     } | ||||||
|  |     FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_E(TAG, "Starting dictionary attack"); | ||||||
|  | 
 | ||||||
|  |     size_t index = 0; | ||||||
|  |     uint8_t key[PICOPASS_BLOCK_LEN] = {0}; | ||||||
|  | 
 | ||||||
|  |     if(!iclass_elite_dict_check_presence(IclassEliteDictTypeFlipper)) { | ||||||
|  |         FURI_LOG_E(TAG, "Dictionary not found"); | ||||||
|  |         return ERR_PARAM; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     IclassEliteDict* dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper); | ||||||
|  |     if(!dict) { | ||||||
|  |         FURI_LOG_E(TAG, "Dictionary not allocated"); | ||||||
|  |         return ERR_PARAM; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Loaded %lu keys", iclass_elite_dict_get_total_keys(dict)); | ||||||
|  |     while(iclass_elite_dict_get_next_key(dict, key)) { | ||||||
|  |         FURI_LOG_D( | ||||||
|  |             TAG, | ||||||
|  |             "Try to auth with key %d %02x%02x%02x%02x%02x%02x%02x%02x", | ||||||
|  |             index++, | ||||||
|  |             key[0], | ||||||
|  |             key[1], | ||||||
|  |             key[2], | ||||||
|  |             key[3], | ||||||
|  |             key[4], | ||||||
|  |             key[5], | ||||||
|  |             key[6], | ||||||
|  |             key[7]); | ||||||
|  | 
 | ||||||
|  |         err = rfalPicoPassPollerReadCheck(&rcRes); | ||||||
|  |         if(err != ERR_NONE) { | ||||||
|  |             FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |         memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 | ||||||
|  | 
 | ||||||
|  |         loclass_iclass_calc_div_key(AA1[PICOPASS_CSN_BLOCK_INDEX].data, key, div_key, true); | ||||||
|  |         loclass_opt_doReaderMAC(ccnr, div_key, mac); | ||||||
|  | 
 | ||||||
|  |         err = rfalPicoPassPollerCheck(mac, &chkRes); | ||||||
|  |         if(err == ERR_NONE) { | ||||||
|  |             memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(dict) { | ||||||
|  |         iclass_elite_dict_free(dict); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ReturnCode picopass_read_card(PicopassBlock* AA1) { | ||||||
|  |     ReturnCode err; | ||||||
| 
 | 
 | ||||||
|     size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? |     size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? | ||||||
|                            AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : |                            AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : | ||||||
| @ -352,28 +412,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { | |||||||
|             pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\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) { |             if(pacs->se_enabled) { | ||||||
|                 FURI_LOG_D(TAG, "SE enabled"); |                 FURI_LOG_D(TAG, "SE enabled"); | ||||||
|  |                 nextState = PicopassWorkerEventFail; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             err = picopass_read_card(AA1); |             if(nextState == PicopassWorkerEventSuccess) { | ||||||
|             if(err != ERR_NONE) { |                 err = picopass_auth(AA1, pacs); | ||||||
|                 FURI_LOG_E(TAG, "picopass_read_card error %d", err); |                 if(err != ERR_NONE) { | ||||||
|                 nextState = PicopassWorkerEventFail; |                     FURI_LOG_E(TAG, "picopass_try_auth error %d", err); | ||||||
|  |                     nextState = PicopassWorkerEventFail; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(nextState == PicopassWorkerEventSuccess) { | ||||||
|  |                 err = picopass_read_card(AA1); | ||||||
|  |                 if(err != ERR_NONE) { | ||||||
|  |                     FURI_LOG_E(TAG, "picopass_read_card error %d", err); | ||||||
|  |                     nextState = PicopassWorkerEventFail; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(nextState == PicopassWorkerEventSuccess) { |             if(nextState == PicopassWorkerEventSuccess) { | ||||||
|                 err = picopass_device_parse_credential(AA1, pacs); |                 err = picopass_device_parse_credential(AA1, pacs); | ||||||
|             } |                 if(err != ERR_NONE) { | ||||||
|             if(err != ERR_NONE) { |                     FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); | ||||||
|                 FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); |                     nextState = PicopassWorkerEventFail; | ||||||
|                 nextState = PicopassWorkerEventFail; |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(nextState == PicopassWorkerEventSuccess) { |             if(nextState == PicopassWorkerEventSuccess) { | ||||||
|                 err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); |                 err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); | ||||||
|             } |                 if(err != ERR_NONE) { | ||||||
|             if(err != ERR_NONE) { |                     FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); | ||||||
|                 FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); |                     nextState = PicopassWorkerEventFail; | ||||||
|                 nextState = PicopassWorkerEventFail; |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Notify caller and exit
 |             // Notify caller and exit
 | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ | |||||||
| struct PicopassWorker { | struct PicopassWorker { | ||||||
|     FuriThread* thread; |     FuriThread* thread; | ||||||
|     Storage* storage; |     Storage* storage; | ||||||
|  |     Stream* dict_stream; | ||||||
| 
 | 
 | ||||||
|     PicopassDeviceData* dev_data; |     PicopassDeviceData* dev_data; | ||||||
|     PicopassWorkerCallback callback; |     PicopassWorkerCallback callback; | ||||||
|  | |||||||
| @ -15,12 +15,10 @@ void picopass_scene_read_card_success_widget_callback( | |||||||
| 
 | 
 | ||||||
| void picopass_scene_read_card_success_on_enter(void* context) { | void picopass_scene_read_card_success_on_enter(void* context) { | ||||||
|     Picopass* picopass = context; |     Picopass* picopass = context; | ||||||
|     FuriString* credential_str; |     FuriString* csn_str = furi_string_alloc_set("CSN:"); | ||||||
|     FuriString* wiegand_str; |     FuriString* credential_str = furi_string_alloc(); | ||||||
|     FuriString* sio_str; |     FuriString* wiegand_str = furi_string_alloc(); | ||||||
|     credential_str = furi_string_alloc(); |     FuriString* sio_str = furi_string_alloc(); | ||||||
|     wiegand_str = furi_string_alloc(); |  | ||||||
|     sio_str = furi_string_alloc(); |  | ||||||
| 
 | 
 | ||||||
|     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); |     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||||
| 
 | 
 | ||||||
| @ -28,10 +26,18 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|     notification_message(picopass->notifications, &sequence_success); |     notification_message(picopass->notifications, &sequence_success); | ||||||
| 
 | 
 | ||||||
|     // Setup view
 |     // Setup view
 | ||||||
|  |     PicopassBlock* AA1 = picopass->dev->dev_data.AA1; | ||||||
|     PicopassPacs* pacs = &picopass->dev->dev_data.pacs; |     PicopassPacs* pacs = &picopass->dev->dev_data.pacs; | ||||||
|     Widget* widget = picopass->widget; |     Widget* widget = picopass->widget; | ||||||
| 
 | 
 | ||||||
|     if(pacs->record.bitLength == 0) { |     uint8_t csn[PICOPASS_BLOCK_LEN]; | ||||||
|  |     memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); | ||||||
|  |     for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { | ||||||
|  |         furi_string_cat_printf(csn_str, " %02X", csn[i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Neither of these are valid.  Indicates the block was all 0x00 or all 0xff
 | ||||||
|  |     if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { | ||||||
|         furi_string_cat_printf(wiegand_str, "Read Failed"); |         furi_string_cat_printf(wiegand_str, "Read Failed"); | ||||||
| 
 | 
 | ||||||
|         if(pacs->se_enabled) { |         if(pacs->se_enabled) { | ||||||
| @ -79,18 +85,21 @@ void picopass_scene_read_card_success_on_enter(void* context) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); |         widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); | ||||||
|  |     widget_add_string_element( | ||||||
|  |         widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         widget, |         widget, | ||||||
|         64, |         64, | ||||||
|         32, |         36, | ||||||
|         AlignCenter, |         AlignCenter, | ||||||
|         AlignCenter, |         AlignCenter, | ||||||
|         FontSecondary, |         FontSecondary, | ||||||
|         furi_string_get_cstr(credential_str)); |         furi_string_get_cstr(credential_str)); | ||||||
|     widget_add_string_element( |     widget_add_string_element( | ||||||
|         widget, 64, 42, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); |         widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); | ||||||
| 
 | 
 | ||||||
|  |     furi_string_free(csn_str); | ||||||
|     furi_string_free(credential_str); |     furi_string_free(credential_str); | ||||||
|     furi_string_free(wiegand_str); |     furi_string_free(wiegand_str); | ||||||
|     furi_string_free(sio_str); |     furi_string_free(sio_str); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Patrick Cunningham
						Patrick Cunningham