Iclass UI (#1366)
* Move structs to header * roll mbedtls into loclass * Picopass with scene for reading card * Picopass: fix memory leak * Lib: return mbedtls back * Picopass: rename symbols to match naming guide Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									34d97ebb4a
								
							
						
					
					
						commit
						8af2198684
					
				| @ -4,7 +4,7 @@ App( | |||||||
|     apptype=FlipperAppType.PLUGIN, |     apptype=FlipperAppType.PLUGIN, | ||||||
|     entry_point="picopass_app", |     entry_point="picopass_app", | ||||||
|     cdefines=["APP_PICOPASS"], |     cdefines=["APP_PICOPASS"], | ||||||
|     requires=["gui"], |     requires=["storage", "gui"], | ||||||
|     stack_size=1 * 1024, |     stack_size=1 * 1024, | ||||||
|     icon="A_Plugins_14", |     icon="A_Plugins_14", | ||||||
|     order=30, |     order=30, | ||||||
|  | |||||||
| @ -1,397 +1,137 @@ | |||||||
| #include "picopass.h" | #include "picopass_i.h" | ||||||
| #include <furi.h> |  | ||||||
| #include <gui/gui.h> |  | ||||||
| #include <input/input.h> |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <st25r3916.h> |  | ||||||
| #include <rfal_analogConfig.h> |  | ||||||
| #include <rfal_rf.h> |  | ||||||
| #include <rfal_nfc.h> |  | ||||||
| 
 |  | ||||||
| #include <storage/storage.h> |  | ||||||
| #include <lib/toolbox/path.h> |  | ||||||
| 
 | 
 | ||||||
| #define TAG "PicoPass" | #define TAG "PicoPass" | ||||||
| 
 | 
 | ||||||
| typedef enum { | bool picopass_custom_event_callback(void* context, uint32_t event) { | ||||||
|     EventTypeTick, |     furi_assert(context); | ||||||
|     EventTypeKey, |     Picopass* picopass = context; | ||||||
| } EventType; |     return scene_manager_handle_custom_event(picopass->scene_manager, event); | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     EventType type; |  | ||||||
|     InputEvent input; |  | ||||||
| } PluginEvent; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     bool valid; |  | ||||||
|     uint8_t bitLength; |  | ||||||
|     uint8_t FacilityCode; |  | ||||||
|     uint16_t CardNumber; |  | ||||||
| } WiegandRecord; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     bool biometrics; |  | ||||||
|     uint8_t encryption; |  | ||||||
|     uint8_t credential[8]; |  | ||||||
|     uint8_t pin0[8]; |  | ||||||
|     uint8_t pin1[8]; |  | ||||||
|     WiegandRecord record; |  | ||||||
| } PACS; |  | ||||||
| 
 |  | ||||||
| enum State { INIT, READY, RESULT }; |  | ||||||
| typedef struct { |  | ||||||
|     enum State state; |  | ||||||
|     PACS pacs; |  | ||||||
| } PluginState; |  | ||||||
| 
 |  | ||||||
| uint8_t iclass_key[8] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; |  | ||||||
| uint8_t iclass_decryptionkey[16] = |  | ||||||
|     {0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36}; |  | ||||||
| ApplicationArea AA1; |  | ||||||
| 
 |  | ||||||
| static void render_callback(Canvas* const canvas, void* ctx) { |  | ||||||
|     const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); |  | ||||||
|     if(plugin_state == NULL) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     // border around the edge of the screen
 |  | ||||||
|     canvas_draw_frame(canvas, 0, 0, 128, 64); |  | ||||||
| 
 |  | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
| 
 |  | ||||||
|     if(plugin_state->state == INIT) { |  | ||||||
|         canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Loading..."); |  | ||||||
|     } else if(plugin_state->state == READY) { |  | ||||||
|         canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Push center to scan"); |  | ||||||
|     } else if(plugin_state->state == RESULT) { |  | ||||||
|         char raw_credential[25] = {0}; |  | ||||||
|         sprintf( |  | ||||||
|             raw_credential, |  | ||||||
|             "%02x %02x %02x %02x %02x %02x %02x %02x", |  | ||||||
|             plugin_state->pacs.credential[0], |  | ||||||
|             plugin_state->pacs.credential[1], |  | ||||||
|             plugin_state->pacs.credential[2], |  | ||||||
|             plugin_state->pacs.credential[3], |  | ||||||
|             plugin_state->pacs.credential[4], |  | ||||||
|             plugin_state->pacs.credential[5], |  | ||||||
|             plugin_state->pacs.credential[6], |  | ||||||
|             plugin_state->pacs.credential[7]); |  | ||||||
|         canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignTop, raw_credential); |  | ||||||
| 
 |  | ||||||
|         if(plugin_state->pacs.record.valid) { |  | ||||||
|             char parsed[20] = {0}; |  | ||||||
|             sprintf( |  | ||||||
|                 parsed, |  | ||||||
|                 "FC: %03u CN: %05u", |  | ||||||
|                 plugin_state->pacs.record.FacilityCode, |  | ||||||
|                 plugin_state->pacs.record.CardNumber); |  | ||||||
|             canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, parsed); |  | ||||||
|         } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     release_mutex((ValueMutex*)ctx, plugin_state); | bool picopass_back_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     return scene_manager_handle_back_event(picopass->scene_manager); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void input_callback(InputEvent* input_event, osMessageQueueId_t event_queue) { | void picopass_tick_event_callback(void* context) { | ||||||
|     furi_assert(event_queue); |     furi_assert(context); | ||||||
| 
 |     Picopass* picopass = context; | ||||||
|     PluginEvent event = {.type = EventTypeKey, .input = *input_event}; |     scene_manager_handle_tick_event(picopass->scene_manager); | ||||||
|     osMessageQueuePut(event_queue, &event, 0, osWaitForever); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void picopass_state_init(PluginState* const plugin_state) { | Picopass* picopass_alloc() { | ||||||
|     plugin_state->state = READY; |     Picopass* picopass = malloc(sizeof(Picopass)); | ||||||
|  | 
 | ||||||
|  |     picopass->worker = picopass_worker_alloc(); | ||||||
|  |     picopass->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     picopass->scene_manager = scene_manager_alloc(&picopass_scene_handlers, picopass); | ||||||
|  |     view_dispatcher_enable_queue(picopass->view_dispatcher); | ||||||
|  |     view_dispatcher_set_event_callback_context(picopass->view_dispatcher, picopass); | ||||||
|  |     view_dispatcher_set_custom_event_callback( | ||||||
|  |         picopass->view_dispatcher, picopass_custom_event_callback); | ||||||
|  |     view_dispatcher_set_navigation_event_callback( | ||||||
|  |         picopass->view_dispatcher, picopass_back_event_callback); | ||||||
|  |     view_dispatcher_set_tick_event_callback( | ||||||
|  |         picopass->view_dispatcher, picopass_tick_event_callback, 100); | ||||||
|  | 
 | ||||||
|  |     // Picopass device
 | ||||||
|  |     picopass->dev = picopass_device_alloc(); | ||||||
|  | 
 | ||||||
|  |     // Open GUI record
 | ||||||
|  |     picopass->gui = furi_record_open("gui"); | ||||||
|  |     view_dispatcher_attach_to_gui( | ||||||
|  |         picopass->view_dispatcher, picopass->gui, ViewDispatcherTypeFullscreen); | ||||||
|  | 
 | ||||||
|  |     // Open Notification record
 | ||||||
|  |     picopass->notifications = furi_record_open("notification"); | ||||||
|  | 
 | ||||||
|  |     // Submenu
 | ||||||
|  |     picopass->submenu = submenu_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         picopass->view_dispatcher, PicopassViewMenu, submenu_get_view(picopass->submenu)); | ||||||
|  | 
 | ||||||
|  |     // Popup
 | ||||||
|  |     picopass->popup = popup_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         picopass->view_dispatcher, PicopassViewPopup, popup_get_view(picopass->popup)); | ||||||
|  | 
 | ||||||
|  |     // Custom Widget
 | ||||||
|  |     picopass->widget = widget_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget)); | ||||||
|  | 
 | ||||||
|  |     return picopass; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ReturnCode decrypt(uint8_t* enc_data, uint8_t* dec_data) { | void picopass_free(Picopass* picopass) { | ||||||
|     uint8_t key[32] = {0}; |     furi_assert(picopass); | ||||||
|     memcpy(key, iclass_decryptionkey, sizeof(iclass_decryptionkey)); | 
 | ||||||
|     mbedtls_des3_context ctx; |     // Submenu
 | ||||||
|     mbedtls_des3_init(&ctx); |     view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewMenu); | ||||||
|     mbedtls_des3_set2key_dec(&ctx, key); |     submenu_free(picopass->submenu); | ||||||
|     mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); | 
 | ||||||
|     mbedtls_des3_free(&ctx); |     // Popup
 | ||||||
|     return ERR_NONE; |     view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewPopup); | ||||||
|  |     popup_free(picopass->popup); | ||||||
|  | 
 | ||||||
|  |     // Custom Widget
 | ||||||
|  |     view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget); | ||||||
|  |     widget_free(picopass->widget); | ||||||
|  | 
 | ||||||
|  |     // Worker
 | ||||||
|  |     picopass_worker_stop(picopass->worker); | ||||||
|  |     picopass_worker_free(picopass->worker); | ||||||
|  | 
 | ||||||
|  |     // View Dispatcher
 | ||||||
|  |     view_dispatcher_free(picopass->view_dispatcher); | ||||||
|  | 
 | ||||||
|  |     // Scene Manager
 | ||||||
|  |     scene_manager_free(picopass->scene_manager); | ||||||
|  | 
 | ||||||
|  |     // GUI
 | ||||||
|  |     furi_record_close("gui"); | ||||||
|  |     picopass->gui = NULL; | ||||||
|  | 
 | ||||||
|  |     // Notifications
 | ||||||
|  |     furi_record_close("notification"); | ||||||
|  |     picopass->notifications = NULL; | ||||||
|  | 
 | ||||||
|  |     picopass_device_free(picopass->dev); | ||||||
|  |     picopass->dev = NULL; | ||||||
|  | 
 | ||||||
|  |     free(picopass); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ReturnCode parseWiegand(uint8_t* data, WiegandRecord* record) { | static const NotificationSequence picopass_sequence_blink_start_blue = { | ||||||
|     uint32_t* halves = (uint32_t*)data; |     &message_blink_start_10, | ||||||
|     if(halves[0] == 0) { |     &message_blink_set_color_blue, | ||||||
|         uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); |     &message_do_not_reset, | ||||||
|         record->bitLength = 31 - leading0s; |     NULL, | ||||||
|     } else { | }; | ||||||
|         uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); |  | ||||||
|         record->bitLength = 63 - leading0s; |  | ||||||
|     } |  | ||||||
|     FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); |  | ||||||
| 
 | 
 | ||||||
|     if(record->bitLength == 26) { | static const NotificationSequence picopass_sequence_blink_stop = { | ||||||
|         uint8_t* v4 = data + 4; |     &message_blink_stop, | ||||||
|         v4[0] = 0; |     NULL, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
|         uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); | void picopass_blink_start(Picopass* picopass) { | ||||||
| 
 |     notification_message(picopass->notifications, &picopass_sequence_blink_start_blue); | ||||||
|         record->CardNumber = (bot >> 1) & 0xFFFF; |  | ||||||
|         record->FacilityCode = (bot >> 17) & 0xFF; |  | ||||||
|         record->valid = true; |  | ||||||
|     } else { |  | ||||||
|         record->CardNumber = 0; |  | ||||||
|         record->FacilityCode = 0; |  | ||||||
|         record->valid = false; |  | ||||||
|     } |  | ||||||
|     return ERR_NONE; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ReturnCode disable_field(ReturnCode rc) { | void picopass_blink_stop(Picopass* picopass) { | ||||||
|     st25r3916TxRxOff(); |     notification_message(picopass->notifications, &picopass_sequence_blink_stop); | ||||||
|     rfalLowPowerModeStart(); |  | ||||||
|     return rc; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ReturnCode picopass_read_card(ApplicationArea* AA1) { |  | ||||||
|     rfalPicoPassIdentifyRes idRes; |  | ||||||
|     rfalPicoPassSelectRes selRes; |  | ||||||
|     rfalPicoPassReadCheckRes rcRes; |  | ||||||
|     rfalPicoPassCheckRes chkRes; |  | ||||||
| 
 |  | ||||||
|     ReturnCode err; |  | ||||||
| 
 |  | ||||||
|     uint8_t div_key[8] = {0}; |  | ||||||
|     uint8_t mac[4] = {0}; |  | ||||||
|     uint8_t ccnr[12] = {0}; |  | ||||||
| 
 |  | ||||||
|     st25r3916TxRxOn(); |  | ||||||
|     rfalLowPowerModeStop(); |  | ||||||
|     rfalWorker(); |  | ||||||
|     err = rfalPicoPassPollerInitialize(); |  | ||||||
|     if(err != ERR_NONE) { |  | ||||||
|         FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d\n", err); |  | ||||||
|         return disable_field(err); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     err = rfalFieldOnAndStartGT(); |  | ||||||
|     if(err != ERR_NONE) { |  | ||||||
|         FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d\n", err); |  | ||||||
|         return disable_field(err); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     err = rfalPicoPassPollerCheckPresence(); |  | ||||||
|     if(err != ERR_RF_COLLISION) { |  | ||||||
|         FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d\n", err); |  | ||||||
|         return disable_field(err); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     err = rfalPicoPassPollerIdentify(&idRes); |  | ||||||
|     if(err != ERR_NONE) { |  | ||||||
|         FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d\n", err); |  | ||||||
|         return disable_field(err); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); |  | ||||||
|     if(err != ERR_NONE) { |  | ||||||
|         FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d\n", err); |  | ||||||
|         return disable_field(err); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     err = rfalPicoPassPollerReadCheck(&rcRes); |  | ||||||
|     if(err != ERR_NONE) { |  | ||||||
|         FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); |  | ||||||
|         return disable_field(err); |  | ||||||
|     } |  | ||||||
|     memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
 |  | ||||||
| 
 |  | ||||||
|     diversifyKey(selRes.CSN, iclass_key, div_key); |  | ||||||
|     opt_doReaderMAC(ccnr, div_key, mac); |  | ||||||
| 
 |  | ||||||
|     err = rfalPicoPassPollerCheck(mac, &chkRes); |  | ||||||
|     if(err != ERR_NONE) { |  | ||||||
|         FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); |  | ||||||
|         return disable_field(err); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     for(size_t i = 0; i < 4; i++) { |  | ||||||
|         FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i + 6); |  | ||||||
|         err = rfalPicoPassPollerReadBlock(i + 6, &(AA1->block[i])); |  | ||||||
|         if(err != ERR_NONE) { |  | ||||||
|             FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err); |  | ||||||
|             return disable_field(err); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return disable_field(ERR_NONE); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int32_t picopass_app(void* p) { | int32_t picopass_app(void* p) { | ||||||
|     UNUSED(p); |     UNUSED(p); | ||||||
|     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(PluginEvent), NULL); |     Picopass* picopass = picopass_alloc(); | ||||||
| 
 | 
 | ||||||
|     PluginState* plugin_state = malloc(sizeof(PluginState)); |     scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart); | ||||||
|     picopass_state_init(plugin_state); |  | ||||||
|     ValueMutex state_mutex; |  | ||||||
|     if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { |  | ||||||
|         FURI_LOG_E("Hello_world", "cannot create mutex\r\n"); |  | ||||||
|         free(plugin_state); |  | ||||||
|         return 255; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     // Set system callbacks
 |     view_dispatcher_run(picopass->view_dispatcher); | ||||||
|     ViewPort* view_port = view_port_alloc(); |  | ||||||
|     view_port_draw_callback_set(view_port, render_callback, &state_mutex); |  | ||||||
|     view_port_input_callback_set(view_port, input_callback, event_queue); |  | ||||||
| 
 | 
 | ||||||
|     // Open GUI and register view_port
 |     picopass_free(picopass); | ||||||
|     Gui* gui = furi_record_open("gui"); |  | ||||||
|     gui_add_view_port(gui, view_port, GuiLayerFullscreen); |  | ||||||
| 
 |  | ||||||
|     PluginEvent event; |  | ||||||
|     ReturnCode err; |  | ||||||
|     for(bool processing = true; processing;) { |  | ||||||
|         osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 100); |  | ||||||
|         PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); |  | ||||||
| 
 |  | ||||||
|         if(event_status == osOK) { |  | ||||||
|             // press events
 |  | ||||||
|             if(event.type == EventTypeKey) { |  | ||||||
|                 if(event.input.type == InputTypePress) { |  | ||||||
|                     switch(event.input.key) { |  | ||||||
|                     case InputKeyUp: |  | ||||||
|                         FURI_LOG_D(TAG, "Input Up"); |  | ||||||
|                         break; |  | ||||||
|                     case InputKeyDown: |  | ||||||
|                         FURI_LOG_D(TAG, "Input Down"); |  | ||||||
|                         break; |  | ||||||
|                     case InputKeyRight: |  | ||||||
|                         FURI_LOG_D(TAG, "Input Right"); |  | ||||||
|                         break; |  | ||||||
|                     case InputKeyLeft: |  | ||||||
|                         FURI_LOG_D(TAG, "Input Left"); |  | ||||||
|                         break; |  | ||||||
|                     case InputKeyOk: |  | ||||||
|                         FURI_LOG_D(TAG, "Input OK"); |  | ||||||
|                         err = picopass_read_card(&AA1); |  | ||||||
|                         if(err != ERR_NONE) { |  | ||||||
|                             FURI_LOG_E(TAG, "picopass_read_card error %d", err); |  | ||||||
|                             plugin_state->state = READY; |  | ||||||
|                             break; |  | ||||||
|                         } |  | ||||||
|                         FURI_LOG_D(TAG, "read OK"); |  | ||||||
| 
 |  | ||||||
|                         plugin_state->pacs.biometrics = AA1.block[0].data[4]; |  | ||||||
|                         plugin_state->pacs.encryption = AA1.block[0].data[7]; |  | ||||||
|                         if(plugin_state->pacs.encryption == 0x17) { |  | ||||||
|                             FURI_LOG_D(TAG, "3DES Encrypted"); |  | ||||||
|                             err = decrypt(AA1.block[1].data, plugin_state->pacs.credential); |  | ||||||
|                             if(err != ERR_NONE) { |  | ||||||
|                                 FURI_LOG_E(TAG, "decrypt error %d", err); |  | ||||||
|                                 break; |  | ||||||
|                             } |  | ||||||
|                             FURI_LOG_D(TAG, "Decrypted 7"); |  | ||||||
| 
 |  | ||||||
|                             err = decrypt(AA1.block[2].data, plugin_state->pacs.pin0); |  | ||||||
|                             if(err != ERR_NONE) { |  | ||||||
|                                 FURI_LOG_E(TAG, "decrypt error %d", err); |  | ||||||
|                                 break; |  | ||||||
|                             } |  | ||||||
|                             FURI_LOG_D(TAG, "Decrypted 8"); |  | ||||||
| 
 |  | ||||||
|                             err = decrypt(AA1.block[3].data, plugin_state->pacs.pin1); |  | ||||||
|                             if(err != ERR_NONE) { |  | ||||||
|                                 FURI_LOG_E(TAG, "decrypt error %d", err); |  | ||||||
|                                 break; |  | ||||||
|                             } |  | ||||||
|                             FURI_LOG_D(TAG, "Decrypted 9"); |  | ||||||
|                         } else if(plugin_state->pacs.encryption == 0x14) { |  | ||||||
|                             FURI_LOG_D(TAG, "No Encryption"); |  | ||||||
|                             memcpy( |  | ||||||
|                                 plugin_state->pacs.credential, |  | ||||||
|                                 AA1.block[1].data, |  | ||||||
|                                 RFAL_PICOPASS_MAX_BLOCK_LEN); |  | ||||||
|                             memcpy( |  | ||||||
|                                 plugin_state->pacs.pin0, |  | ||||||
|                                 AA1.block[2].data, |  | ||||||
|                                 RFAL_PICOPASS_MAX_BLOCK_LEN); |  | ||||||
|                             memcpy( |  | ||||||
|                                 plugin_state->pacs.pin1, |  | ||||||
|                                 AA1.block[3].data, |  | ||||||
|                                 RFAL_PICOPASS_MAX_BLOCK_LEN); |  | ||||||
|                         } else if(plugin_state->pacs.encryption == 0x15) { |  | ||||||
|                             FURI_LOG_D(TAG, "DES Encrypted"); |  | ||||||
|                         } else { |  | ||||||
|                             FURI_LOG_D(TAG, "Unknown encryption"); |  | ||||||
|                             break; |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         FURI_LOG_D( |  | ||||||
|                             TAG, |  | ||||||
|                             "credential %02x%02x%02x%02x%02x%02x%02x%02x", |  | ||||||
|                             plugin_state->pacs.credential[0], |  | ||||||
|                             plugin_state->pacs.credential[1], |  | ||||||
|                             plugin_state->pacs.credential[2], |  | ||||||
|                             plugin_state->pacs.credential[3], |  | ||||||
|                             plugin_state->pacs.credential[4], |  | ||||||
|                             plugin_state->pacs.credential[5], |  | ||||||
|                             plugin_state->pacs.credential[6], |  | ||||||
|                             plugin_state->pacs.credential[7]); |  | ||||||
|                         FURI_LOG_D( |  | ||||||
|                             TAG, |  | ||||||
|                             "pin0 %02x%02x%02x%02x%02x%02x%02x%02x", |  | ||||||
|                             plugin_state->pacs.pin0[0], |  | ||||||
|                             plugin_state->pacs.pin0[1], |  | ||||||
|                             plugin_state->pacs.pin0[2], |  | ||||||
|                             plugin_state->pacs.pin0[3], |  | ||||||
|                             plugin_state->pacs.pin0[4], |  | ||||||
|                             plugin_state->pacs.pin0[5], |  | ||||||
|                             plugin_state->pacs.pin0[6], |  | ||||||
|                             plugin_state->pacs.pin0[7]); |  | ||||||
|                         FURI_LOG_D( |  | ||||||
|                             TAG, |  | ||||||
|                             "pin1 %02x%02x%02x%02x%02x%02x%02x%02x", |  | ||||||
|                             plugin_state->pacs.pin1[0], |  | ||||||
|                             plugin_state->pacs.pin1[1], |  | ||||||
|                             plugin_state->pacs.pin1[2], |  | ||||||
|                             plugin_state->pacs.pin1[3], |  | ||||||
|                             plugin_state->pacs.pin1[4], |  | ||||||
|                             plugin_state->pacs.pin1[5], |  | ||||||
|                             plugin_state->pacs.pin1[6], |  | ||||||
|                             plugin_state->pacs.pin1[7]); |  | ||||||
| 
 |  | ||||||
|                         err = parseWiegand( |  | ||||||
|                             plugin_state->pacs.credential, &plugin_state->pacs.record); |  | ||||||
|                         if(err != ERR_NONE) { |  | ||||||
|                             FURI_LOG_E(TAG, "parse error %d", err); |  | ||||||
|                             break; |  | ||||||
|                         } |  | ||||||
|                         if(plugin_state->pacs.record.valid) { |  | ||||||
|                             FURI_LOG_D( |  | ||||||
|                                 TAG, |  | ||||||
|                                 "FC: %03d CN: %05d", |  | ||||||
|                                 plugin_state->pacs.record.FacilityCode, |  | ||||||
|                                 plugin_state->pacs.record.CardNumber); |  | ||||||
|                         } |  | ||||||
|                         plugin_state->state = RESULT; |  | ||||||
| 
 |  | ||||||
|                         break; |  | ||||||
|                     case InputKeyBack: |  | ||||||
|                         FURI_LOG_D(TAG, "Input Back"); |  | ||||||
|                         processing = false; |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             // FURI_LOG_D(TAG, "osMessageQueue: event timeout");
 |  | ||||||
|             // event timeout
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         view_port_update(view_port); |  | ||||||
|         release_mutex(&state_mutex, plugin_state); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     view_port_enabled_set(view_port, false); |  | ||||||
|     gui_remove_view_port(gui, view_port); |  | ||||||
|     furi_record_close("gui"); |  | ||||||
|     view_port_free(view_port); |  | ||||||
|     osMessageQueueDelete(event_queue); |  | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,9 +1,3 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <rfal_picopass.h> | typedef struct Picopass Picopass; | ||||||
| #include <loclass/optimized_ikeys.h> |  | ||||||
| #include <loclass/optimized_cipher.h> |  | ||||||
| #include <mbedtls/des.h> |  | ||||||
| 
 |  | ||||||
| #define PP_MAX_DUMP_SIZE 1024 |  | ||||||
| #define FURI_HAL_PICOPASS_UID_MAX_LEN 10 |  | ||||||
|  | |||||||
							
								
								
									
										33
									
								
								applications/picopass/picopass_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								applications/picopass/picopass_device.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | #include "picopass_device.h" | ||||||
|  | 
 | ||||||
|  | #include <toolbox/path.h> | ||||||
|  | #include <flipper_format/flipper_format.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "PicopassDevice" | ||||||
|  | 
 | ||||||
|  | PicopassDevice* picopass_device_alloc() { | ||||||
|  |     PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice)); | ||||||
|  |     picopass_dev->storage = furi_record_open("storage"); | ||||||
|  |     picopass_dev->dialogs = furi_record_open("dialogs"); | ||||||
|  |     return picopass_dev; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_device_clear(PicopassDevice* dev) { | ||||||
|  |     furi_assert(dev); | ||||||
|  | 
 | ||||||
|  |     picopass_device_data_clear(&dev->dev_data); | ||||||
|  |     memset(&dev->dev_data, 0, sizeof(dev->dev_data)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_device_free(PicopassDevice* picopass_dev) { | ||||||
|  |     furi_assert(picopass_dev); | ||||||
|  |     picopass_device_clear(picopass_dev); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     furi_record_close("dialogs"); | ||||||
|  |     free(picopass_dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_device_data_clear(PicopassDeviceData* dev_data) { | ||||||
|  |     FURI_LOG_D(TAG, "picopass_device_data_clear"); | ||||||
|  |     memset(&dev_data->AA1, 0, sizeof(ApplicationArea)); | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								applications/picopass/picopass_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								applications/picopass/picopass_device.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <dialogs/dialogs.h> | ||||||
|  | 
 | ||||||
|  | #include <rfal_picopass.h> | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool valid; | ||||||
|  |     uint8_t bitLength; | ||||||
|  |     uint8_t FacilityCode; | ||||||
|  |     uint16_t CardNumber; | ||||||
|  | } PicopassWiegandRecord; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool biometrics; | ||||||
|  |     uint8_t encryption; | ||||||
|  |     uint8_t credential[8]; | ||||||
|  |     uint8_t pin0[8]; | ||||||
|  |     uint8_t pin1[8]; | ||||||
|  |     PicopassWiegandRecord record; | ||||||
|  | } PicopassPacs; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     ApplicationArea AA1; | ||||||
|  |     PicopassPacs pacs; | ||||||
|  | } PicopassDeviceData; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     Storage* storage; | ||||||
|  |     DialogsApp* dialogs; | ||||||
|  |     PicopassDeviceData dev_data; | ||||||
|  | } PicopassDevice; | ||||||
|  | 
 | ||||||
|  | PicopassDevice* picopass_device_alloc(); | ||||||
|  | 
 | ||||||
|  | void picopass_device_free(PicopassDevice* picopass_dev); | ||||||
|  | 
 | ||||||
|  | void picopass_device_data_clear(PicopassDeviceData* dev_data); | ||||||
|  | 
 | ||||||
|  | void picopass_device_clear(PicopassDevice* dev); | ||||||
							
								
								
									
										66
									
								
								applications/picopass/picopass_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								applications/picopass/picopass_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "picopass.h" | ||||||
|  | #include "picopass_worker.h" | ||||||
|  | #include "picopass_device.h" | ||||||
|  | 
 | ||||||
|  | #include <rfal_picopass.h> | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | #include <notification/notification_messages.h> | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <gui/modules/popup.h> | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | 
 | ||||||
|  | #include <input/input.h> | ||||||
|  | 
 | ||||||
|  | #include <picopass/scenes/picopass_scene.h> | ||||||
|  | 
 | ||||||
|  | #include <storage/storage.h> | ||||||
|  | #include <lib/toolbox/path.h> | ||||||
|  | 
 | ||||||
|  | enum PicopassCustomEvent { | ||||||
|  |     // Reserve first 100 events for button types and indexes, starting from 0
 | ||||||
|  |     PicopassCustomEventReserved = 100, | ||||||
|  | 
 | ||||||
|  |     PicopassCustomEventViewExit, | ||||||
|  |     PicopassCustomEventWorkerExit, | ||||||
|  |     PicopassCustomEventByteInputDone, | ||||||
|  |     PicopassCustomEventTextInputDone, | ||||||
|  |     PicopassCustomEventDictAttackDone, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     EventTypeTick, | ||||||
|  |     EventTypeKey, | ||||||
|  | } EventType; | ||||||
|  | 
 | ||||||
|  | struct Picopass { | ||||||
|  |     PicopassWorker* worker; | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     Gui* gui; | ||||||
|  |     NotificationApp* notifications; | ||||||
|  |     SceneManager* scene_manager; | ||||||
|  |     PicopassDevice* dev; | ||||||
|  | 
 | ||||||
|  |     // Common Views
 | ||||||
|  |     Submenu* submenu; | ||||||
|  |     Popup* popup; | ||||||
|  |     Widget* widget; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     PicopassViewMenu, | ||||||
|  |     PicopassViewPopup, | ||||||
|  |     PicopassViewWidget, | ||||||
|  | } PicopassView; | ||||||
|  | 
 | ||||||
|  | Picopass* picopass_alloc(); | ||||||
|  | 
 | ||||||
|  | void picopass_blink_start(Picopass* picopass); | ||||||
|  | 
 | ||||||
|  | void picopass_blink_stop(Picopass* picopass); | ||||||
							
								
								
									
										317
									
								
								applications/picopass/picopass_worker.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								applications/picopass/picopass_worker.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,317 @@ | |||||||
|  | #include "picopass_worker_i.h" | ||||||
|  | #include <furi_hal.h> | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <st25r3916.h> | ||||||
|  | #include <rfal_analogConfig.h> | ||||||
|  | #include <rfal_rf.h> | ||||||
|  | #include <rfal_nfc.h> | ||||||
|  | 
 | ||||||
|  | #include <mbedtls/des.h> | ||||||
|  | #include <loclass/optimized_ikeys.h> | ||||||
|  | #include <loclass/optimized_cipher.h> | ||||||
|  | 
 | ||||||
|  | #include <platform.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "PicopassWorker" | ||||||
|  | 
 | ||||||
|  | const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; | ||||||
|  | const uint8_t picopass_iclass_decryptionkey[] = | ||||||
|  |     {0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36}; | ||||||
|  | 
 | ||||||
|  | static void picopass_worker_enable_field() { | ||||||
|  |     st25r3916TxRxOn(); | ||||||
|  |     rfalLowPowerModeStop(); | ||||||
|  |     rfalWorker(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ReturnCode picopass_worker_disable_field(ReturnCode rc) { | ||||||
|  |     st25r3916TxRxOff(); | ||||||
|  |     rfalLowPowerModeStart(); | ||||||
|  |     return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ReturnCode picopass_worker_decrypt(uint8_t* enc_data, uint8_t* dec_data) { | ||||||
|  |     uint8_t key[32] = {0}; | ||||||
|  |     memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey)); | ||||||
|  |     mbedtls_des3_context ctx; | ||||||
|  |     mbedtls_des3_init(&ctx); | ||||||
|  |     mbedtls_des3_set2key_dec(&ctx, key); | ||||||
|  |     mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); | ||||||
|  |     mbedtls_des3_free(&ctx); | ||||||
|  |     return ERR_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ReturnCode picopass_worker_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) { | ||||||
|  |     uint32_t* halves = (uint32_t*)data; | ||||||
|  |     if(halves[0] == 0) { | ||||||
|  |         uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); | ||||||
|  |         record->bitLength = 31 - leading0s; | ||||||
|  |     } else { | ||||||
|  |         uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); | ||||||
|  |         record->bitLength = 63 - leading0s; | ||||||
|  |     } | ||||||
|  |     FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); | ||||||
|  | 
 | ||||||
|  |     if(record->bitLength == 26) { | ||||||
|  |         uint8_t* v4 = data + 4; | ||||||
|  |         v4[0] = 0; | ||||||
|  | 
 | ||||||
|  |         uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); | ||||||
|  | 
 | ||||||
|  |         record->CardNumber = (bot >> 1) & 0xFFFF; | ||||||
|  |         record->FacilityCode = (bot >> 17) & 0xFF; | ||||||
|  |         record->valid = true; | ||||||
|  |     } else { | ||||||
|  |         record->CardNumber = 0; | ||||||
|  |         record->FacilityCode = 0; | ||||||
|  |         record->valid = false; | ||||||
|  |     } | ||||||
|  |     return ERR_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /***************************** Picopass Worker API *******************************/ | ||||||
|  | 
 | ||||||
|  | PicopassWorker* picopass_worker_alloc() { | ||||||
|  |     PicopassWorker* picopass_worker = malloc(sizeof(PicopassWorker)); | ||||||
|  | 
 | ||||||
|  |     // Worker thread attributes
 | ||||||
|  |     picopass_worker->thread = furi_thread_alloc(); | ||||||
|  |     furi_thread_set_name(picopass_worker->thread, "PicopassWorker"); | ||||||
|  |     furi_thread_set_stack_size(picopass_worker->thread, 8192); | ||||||
|  |     furi_thread_set_callback(picopass_worker->thread, picopass_worker_task); | ||||||
|  |     furi_thread_set_context(picopass_worker->thread, picopass_worker); | ||||||
|  | 
 | ||||||
|  |     picopass_worker->callback = NULL; | ||||||
|  |     picopass_worker->context = NULL; | ||||||
|  |     picopass_worker->storage = furi_record_open("storage"); | ||||||
|  | 
 | ||||||
|  |     picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); | ||||||
|  | 
 | ||||||
|  |     return picopass_worker; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_worker_free(PicopassWorker* picopass_worker) { | ||||||
|  |     furi_assert(picopass_worker); | ||||||
|  | 
 | ||||||
|  |     furi_thread_free(picopass_worker->thread); | ||||||
|  | 
 | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | 
 | ||||||
|  |     free(picopass_worker); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker) { | ||||||
|  |     return picopass_worker->state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_worker_start( | ||||||
|  |     PicopassWorker* picopass_worker, | ||||||
|  |     PicopassWorkerState state, | ||||||
|  |     PicopassDeviceData* dev_data, | ||||||
|  |     PicopassWorkerCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(picopass_worker); | ||||||
|  |     furi_assert(dev_data); | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_D(TAG, "picopass_worker_start"); | ||||||
|  | 
 | ||||||
|  |     picopass_worker->callback = callback; | ||||||
|  |     picopass_worker->context = context; | ||||||
|  |     picopass_worker->dev_data = dev_data; | ||||||
|  |     picopass_worker_change_state(picopass_worker, state); | ||||||
|  |     furi_thread_start(picopass_worker->thread); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_worker_stop(PicopassWorker* picopass_worker) { | ||||||
|  |     furi_assert(picopass_worker); | ||||||
|  |     if(picopass_worker->state == PicopassWorkerStateBroken || | ||||||
|  |        picopass_worker->state == PicopassWorkerStateReady) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     picopass_worker_disable_field(ERR_NONE); | ||||||
|  | 
 | ||||||
|  |     picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); | ||||||
|  |     furi_thread_join(picopass_worker->thread); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) { | ||||||
|  |     picopass_worker->state = state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /***************************** Picopass Worker Thread *******************************/ | ||||||
|  | 
 | ||||||
|  | ReturnCode picopass_detect_card(int timeout) { | ||||||
|  |     UNUSED(timeout); | ||||||
|  | 
 | ||||||
|  |     ReturnCode err; | ||||||
|  | 
 | ||||||
|  |     err = rfalPicoPassPollerInitialize(); | ||||||
|  |     if(err != ERR_NONE) { | ||||||
|  |         FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     err = rfalFieldOnAndStartGT(); | ||||||
|  |     if(err != ERR_NONE) { | ||||||
|  |         FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     err = rfalPicoPassPollerCheckPresence(); | ||||||
|  |     if(err != ERR_RF_COLLISION) { | ||||||
|  |         FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ERR_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ReturnCode picopass_read_card(ApplicationArea* AA1) { | ||||||
|  |     rfalPicoPassIdentifyRes idRes; | ||||||
|  |     rfalPicoPassSelectRes selRes; | ||||||
|  |     rfalPicoPassReadCheckRes rcRes; | ||||||
|  |     rfalPicoPassCheckRes chkRes; | ||||||
|  | 
 | ||||||
|  |     ReturnCode err; | ||||||
|  | 
 | ||||||
|  |     uint8_t div_key[8] = {0}; | ||||||
|  |     uint8_t mac[4] = {0}; | ||||||
|  |     uint8_t ccnr[12] = {0}; | ||||||
|  | 
 | ||||||
|  |     err = rfalPicoPassPollerIdentify(&idRes); | ||||||
|  |     if(err != ERR_NONE) { | ||||||
|  |         FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); | ||||||
|  |     if(err != ERR_NONE) { | ||||||
|  |         FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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
 | ||||||
|  | 
 | ||||||
|  |     diversifyKey(selRes.CSN, picopass_iclass_key, div_key); | ||||||
|  |     opt_doReaderMAC(ccnr, div_key, mac); | ||||||
|  | 
 | ||||||
|  |     err = rfalPicoPassPollerCheck(mac, &chkRes); | ||||||
|  |     if(err != ERR_NONE) { | ||||||
|  |         FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < 4; i++) { | ||||||
|  |         FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i + 6); | ||||||
|  |         rfalPicoPassReadBlockRes block; | ||||||
|  |         err = rfalPicoPassPollerReadBlock(i + 6, &block); | ||||||
|  |         if(err != ERR_NONE) { | ||||||
|  |             FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err); | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         FURI_LOG_D( | ||||||
|  |             TAG, | ||||||
|  |             "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x", | ||||||
|  |             i + 6, | ||||||
|  |             block.data[0], | ||||||
|  |             block.data[1], | ||||||
|  |             block.data[2], | ||||||
|  |             block.data[3], | ||||||
|  |             block.data[4], | ||||||
|  |             block.data[5], | ||||||
|  |             block.data[6], | ||||||
|  |             block.data[7]); | ||||||
|  | 
 | ||||||
|  |         memcpy(&(AA1->block[i]), &block, sizeof(block)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ERR_NONE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t picopass_worker_task(void* context) { | ||||||
|  |     PicopassWorker* picopass_worker = context; | ||||||
|  | 
 | ||||||
|  |     picopass_worker_enable_field(); | ||||||
|  |     if(picopass_worker->state == PicopassWorkerStateDetect) { | ||||||
|  |         picopass_worker_detect(picopass_worker); | ||||||
|  |     } | ||||||
|  |     picopass_worker_disable_field(ERR_NONE); | ||||||
|  | 
 | ||||||
|  |     picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_worker_detect(PicopassWorker* picopass_worker) { | ||||||
|  |     picopass_device_data_clear(picopass_worker->dev_data); | ||||||
|  |     PicopassDeviceData* dev_data = picopass_worker->dev_data; | ||||||
|  | 
 | ||||||
|  |     ApplicationArea* AA1 = &dev_data->AA1; | ||||||
|  |     PicopassPacs* pacs = &dev_data->pacs; | ||||||
|  |     ReturnCode err; | ||||||
|  | 
 | ||||||
|  |     while(picopass_worker->state == PicopassWorkerStateDetect) { | ||||||
|  |         FURI_LOG_D(TAG, "PicopassWorkerStateDetect"); | ||||||
|  |         if(picopass_detect_card(1000) == ERR_NONE) { | ||||||
|  |             // Process first found device
 | ||||||
|  |             FURI_LOG_D(TAG, "picopass_read_card"); | ||||||
|  |             err = picopass_read_card(AA1); | ||||||
|  |             if(err != ERR_NONE) { | ||||||
|  |                 FURI_LOG_E(TAG, "picopass_read_card error %d", err); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             pacs->biometrics = AA1->block[0].data[4]; | ||||||
|  |             pacs->encryption = AA1->block[0].data[7]; | ||||||
|  | 
 | ||||||
|  |             if(pacs->encryption == 0x17) { | ||||||
|  |                 FURI_LOG_D(TAG, "3DES Encrypted"); | ||||||
|  |                 err = picopass_worker_decrypt(AA1->block[1].data, pacs->credential); | ||||||
|  |                 if(err != ERR_NONE) { | ||||||
|  |                     FURI_LOG_E(TAG, "decrypt error %d", err); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 FURI_LOG_D(TAG, "Decrypted 7"); | ||||||
|  | 
 | ||||||
|  |                 err = picopass_worker_decrypt(AA1->block[2].data, pacs->pin0); | ||||||
|  |                 if(err != ERR_NONE) { | ||||||
|  |                     FURI_LOG_E(TAG, "decrypt error %d", err); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 FURI_LOG_D(TAG, "Decrypted 8"); | ||||||
|  | 
 | ||||||
|  |                 err = picopass_worker_decrypt(AA1->block[3].data, pacs->pin1); | ||||||
|  |                 if(err != ERR_NONE) { | ||||||
|  |                     FURI_LOG_E(TAG, "decrypt error %d", err); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 FURI_LOG_D(TAG, "Decrypted 9"); | ||||||
|  |             } else if(pacs->encryption == 0x14) { | ||||||
|  |                 FURI_LOG_D(TAG, "No Encryption"); | ||||||
|  |                 memcpy(pacs->credential, AA1->block[1].data, RFAL_PICOPASS_MAX_BLOCK_LEN); | ||||||
|  |                 memcpy(pacs->pin0, AA1->block[2].data, RFAL_PICOPASS_MAX_BLOCK_LEN); | ||||||
|  |                 memcpy(pacs->pin1, AA1->block[3].data, RFAL_PICOPASS_MAX_BLOCK_LEN); | ||||||
|  |             } else if(pacs->encryption == 0x15) { | ||||||
|  |                 FURI_LOG_D(TAG, "DES Encrypted"); | ||||||
|  |             } else { | ||||||
|  |                 FURI_LOG_D(TAG, "Unknown encryption"); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             picopass_worker_parse_wiegand(pacs->credential, &pacs->record); | ||||||
|  | 
 | ||||||
|  |             // Notify caller and exit
 | ||||||
|  |             if(picopass_worker->callback) { | ||||||
|  |                 picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         osDelay(100); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								applications/picopass/picopass_worker.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										45
									
								
								applications/picopass/picopass_worker.h
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "picopass_device.h" | ||||||
|  | 
 | ||||||
|  | typedef struct PicopassWorker PicopassWorker; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     // Init states
 | ||||||
|  |     PicopassWorkerStateNone, | ||||||
|  |     PicopassWorkerStateBroken, | ||||||
|  |     PicopassWorkerStateReady, | ||||||
|  |     // Main worker states
 | ||||||
|  |     PicopassWorkerStateDetect, | ||||||
|  |     // Transition
 | ||||||
|  |     PicopassWorkerStateStop, | ||||||
|  | } PicopassWorkerState; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     // Reserve first 50 events for application events
 | ||||||
|  |     PicopassWorkerEventReserved = 50, | ||||||
|  | 
 | ||||||
|  |     // Picopass worker common events
 | ||||||
|  |     PicopassWorkerEventSuccess, | ||||||
|  |     PicopassWorkerEventFail, | ||||||
|  |     PicopassWorkerEventNoCardDetected, | ||||||
|  | 
 | ||||||
|  |     PicopassWorkerEventStartReading, | ||||||
|  | } PicopassWorkerEvent; | ||||||
|  | 
 | ||||||
|  | typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); | ||||||
|  | 
 | ||||||
|  | PicopassWorker* picopass_worker_alloc(); | ||||||
|  | 
 | ||||||
|  | PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker); | ||||||
|  | 
 | ||||||
|  | void picopass_worker_free(PicopassWorker* picopass_worker); | ||||||
|  | 
 | ||||||
|  | void picopass_worker_start( | ||||||
|  |     PicopassWorker* picopass_worker, | ||||||
|  |     PicopassWorkerState state, | ||||||
|  |     PicopassDeviceData* dev_data, | ||||||
|  |     PicopassWorkerCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | void picopass_worker_stop(PicopassWorker* picopass_worker); | ||||||
							
								
								
									
										24
									
								
								applications/picopass/picopass_worker_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								applications/picopass/picopass_worker_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "picopass_worker.h" | ||||||
|  | #include "picopass_i.h" | ||||||
|  | 
 | ||||||
|  | #include <furi.h> | ||||||
|  | #include <lib/toolbox/stream/file_stream.h> | ||||||
|  | 
 | ||||||
|  | struct PicopassWorker { | ||||||
|  |     FuriThread* thread; | ||||||
|  |     Storage* storage; | ||||||
|  | 
 | ||||||
|  |     PicopassDeviceData* dev_data; | ||||||
|  |     PicopassWorkerCallback callback; | ||||||
|  |     void* context; | ||||||
|  | 
 | ||||||
|  |     PicopassWorkerState state; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state); | ||||||
|  | 
 | ||||||
|  | int32_t picopass_worker_task(void* context); | ||||||
|  | 
 | ||||||
|  | void picopass_worker_detect(PicopassWorker* picopass_worker); | ||||||
							
								
								
									
										30
									
								
								applications/picopass/scenes/picopass_scene.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										30
									
								
								applications/picopass/scenes/picopass_scene.c
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | #include "picopass_scene.h" | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, | ||||||
|  | void (*const picopass_on_enter_handlers[])(void*) = { | ||||||
|  | #include "picopass_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, | ||||||
|  | bool (*const picopass_on_event_handlers[])(void* context, SceneManagerEvent event) = { | ||||||
|  | #include "picopass_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, | ||||||
|  | void (*const picopass_on_exit_handlers[])(void* context) = { | ||||||
|  | #include "picopass_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Initialize scene handlers configuration structure
 | ||||||
|  | const SceneManagerHandlers picopass_scene_handlers = { | ||||||
|  |     .on_enter_handlers = picopass_on_enter_handlers, | ||||||
|  |     .on_event_handlers = picopass_on_event_handlers, | ||||||
|  |     .on_exit_handlers = picopass_on_exit_handlers, | ||||||
|  |     .scene_num = PicopassSceneNum, | ||||||
|  | }; | ||||||
							
								
								
									
										29
									
								
								applications/picopass/scenes/picopass_scene.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								applications/picopass/scenes/picopass_scene.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | // Generate scene id and total number
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) PicopassScene##id, | ||||||
|  | typedef enum { | ||||||
|  | #include "picopass_scene_config.h" | ||||||
|  |     PicopassSceneNum, | ||||||
|  | } PicopassScene; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | extern const SceneManagerHandlers picopass_scene_handlers; | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); | ||||||
|  | #include "picopass_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) \ | ||||||
|  |     bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); | ||||||
|  | #include "picopass_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); | ||||||
|  | #include "picopass_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
							
								
								
									
										3
									
								
								applications/picopass/scenes/picopass_scene_config.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								applications/picopass/scenes/picopass_scene_config.h
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | ADD_SCENE(picopass, start, Start) | ||||||
|  | ADD_SCENE(picopass, read_card, ReadCard) | ||||||
|  | ADD_SCENE(picopass, read_card_success, ReadCardSuccess) | ||||||
							
								
								
									
										55
									
								
								applications/picopass/scenes/picopass_scene_read_card.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								applications/picopass/scenes/picopass_scene_read_card.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | #include "../picopass_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) { | ||||||
|  |     UNUSED(event); | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_scene_read_card_on_enter(void* context) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     DOLPHIN_DEED(DolphinDeedNfcRead); | ||||||
|  | 
 | ||||||
|  |     // Setup view
 | ||||||
|  |     Popup* popup = picopass->popup; | ||||||
|  |     popup_set_header(popup, "Detecting\npicopass card", 70, 34, AlignLeft, AlignTop); | ||||||
|  |     popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); | ||||||
|  | 
 | ||||||
|  |     // Start worker
 | ||||||
|  |     view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); | ||||||
|  |     picopass_worker_start( | ||||||
|  |         picopass->worker, | ||||||
|  |         PicopassWorkerStateDetect, | ||||||
|  |         &picopass->dev->dev_data, | ||||||
|  |         picopass_read_card_worker_callback, | ||||||
|  |         picopass); | ||||||
|  | 
 | ||||||
|  |     picopass_blink_start(picopass); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == PicopassCustomEventWorkerExit) { | ||||||
|  |             scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_scene_read_card_on_exit(void* context) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  | 
 | ||||||
|  |     // Stop worker
 | ||||||
|  |     picopass_worker_stop(picopass->worker); | ||||||
|  |     // Clear view
 | ||||||
|  |     popup_reset(picopass->popup); | ||||||
|  | 
 | ||||||
|  |     picopass_blink_stop(picopass); | ||||||
|  | } | ||||||
| @ -0,0 +1,78 @@ | |||||||
|  | #include "../picopass_i.h" | ||||||
|  | #include <dolphin/dolphin.h> | ||||||
|  | 
 | ||||||
|  | void picopass_scene_read_card_success_widget_callback( | ||||||
|  |     GuiButtonType result, | ||||||
|  |     InputType type, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Picopass* picopass = context; | ||||||
|  | 
 | ||||||
|  |     if(type == InputTypeShort) { | ||||||
|  |         view_dispatcher_send_custom_event(picopass->view_dispatcher, result); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_scene_read_card_success_on_enter(void* context) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     string_t credential_str; | ||||||
|  |     string_t wiegand_str; | ||||||
|  |     string_init(credential_str); | ||||||
|  |     string_init(wiegand_str); | ||||||
|  | 
 | ||||||
|  |     DOLPHIN_DEED(DolphinDeedNfcReadSuccess); | ||||||
|  | 
 | ||||||
|  |     // Send notification
 | ||||||
|  |     notification_message(picopass->notifications, &sequence_success); | ||||||
|  | 
 | ||||||
|  |     // Setup view
 | ||||||
|  |     PicopassPacs* pacs = &picopass->dev->dev_data.pacs; | ||||||
|  |     Widget* widget = picopass->widget; | ||||||
|  | 
 | ||||||
|  |     string_set_str(credential_str, ""); | ||||||
|  |     for(uint8_t i = 0; i < RFAL_PICOPASS_MAX_BLOCK_LEN; i++) { | ||||||
|  |         string_cat_printf(credential_str, " %02X", pacs->credential[i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(pacs->record.valid) { | ||||||
|  |         string_cat_printf( | ||||||
|  |             wiegand_str, "FC: %03u CN: %05u", pacs->record.FacilityCode, pacs->record.CardNumber); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     widget_add_button_element( | ||||||
|  |         widget, | ||||||
|  |         GuiButtonTypeLeft, | ||||||
|  |         "Retry", | ||||||
|  |         picopass_scene_read_card_success_widget_callback, | ||||||
|  |         picopass); | ||||||
|  |     if(pacs->record.valid) { | ||||||
|  |         widget_add_string_element( | ||||||
|  |             widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); | ||||||
|  |     } | ||||||
|  |     widget_add_string_element( | ||||||
|  |         widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str)); | ||||||
|  | 
 | ||||||
|  |     string_clear(credential_str); | ||||||
|  |     string_clear(wiegand_str); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool picopass_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == GuiButtonTypeLeft) { | ||||||
|  |             consumed = scene_manager_previous_scene(picopass->scene_manager); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_scene_read_card_success_on_exit(void* context) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  | 
 | ||||||
|  |     // Clear view
 | ||||||
|  |     widget_reset(picopass->widget); | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								applications/picopass/scenes/picopass_scene_start.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								applications/picopass/scenes/picopass_scene_start.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | #include "../picopass_i.h" | ||||||
|  | enum SubmenuIndex { | ||||||
|  |     SubmenuIndexRead, | ||||||
|  |     SubmenuIndexRunScript, | ||||||
|  |     SubmenuIndexSaved, | ||||||
|  |     SubmenuIndexAddManualy, | ||||||
|  |     SubmenuIndexDebug, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void picopass_scene_start_submenu_callback(void* context, uint32_t index) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     view_dispatcher_send_custom_event(picopass->view_dispatcher, index); | ||||||
|  | } | ||||||
|  | void picopass_scene_start_on_enter(void* context) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  | 
 | ||||||
|  |     Submenu* submenu = picopass->submenu; | ||||||
|  |     submenu_add_item( | ||||||
|  |         submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass); | ||||||
|  | 
 | ||||||
|  |     submenu_set_selected_item( | ||||||
|  |         submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart)); | ||||||
|  |     picopass_device_clear(picopass->dev); | ||||||
|  |     view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == SubmenuIndexRead) { | ||||||
|  |             scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard); | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |         scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void picopass_scene_start_on_exit(void* context) { | ||||||
|  |     Picopass* picopass = context; | ||||||
|  |     submenu_reset(picopass->submenu); | ||||||
|  | } | ||||||
| @ -304,7 +304,7 @@ void hash0(uint64_t c, uint8_t k[8]) { | |||||||
|  * @param key |  * @param key | ||||||
|  * @param div_key |  * @param div_key | ||||||
|  */ |  */ | ||||||
| void diversifyKey(uint8_t *csn, uint8_t *key, uint8_t *div_key) { | void diversifyKey(uint8_t *csn, const uint8_t *key, uint8_t *div_key) { | ||||||
|     // Prepare the DES key
 |     // Prepare the DES key
 | ||||||
|     mbedtls_des_setkey_enc(&ctx_enc, key); |     mbedtls_des_setkey_enc(&ctx_enc, key); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -56,7 +56,7 @@ void hash0(uint64_t c, uint8_t k[8]); | |||||||
|  * @param div_key |  * @param div_key | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| void diversifyKey(uint8_t *csn, uint8_t *key, uint8_t *div_key); | void diversifyKey(uint8_t *csn, const uint8_t *key, uint8_t *div_key); | ||||||
| /**
 | /**
 | ||||||
|  * @brief Permutes a key from standard NIST format to Iclass specific format |  * @brief Permutes a key from standard NIST format to Iclass specific format | ||||||
|  * @param key |  * @param key | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Eric Betts
						Eric Betts