From 7e2f0fcc2201e406c33c584580851c6198a581e0 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Mon, 3 Jan 2022 02:33:28 +0300 Subject: [PATCH] [FL-2121] U2F GUI (#942) * U2F: new gui * U2F: user cert key encryption * FuriCore: move type casting to dangerous_defines * FuriCore: exclude dangerous things from furi.h Co-authored-by: Aleksandr Kutuzov --- .../desktop/animations/animation_storage.c | 3 + applications/u2f/scenes/u2f_scene_main.c | 30 ++++- applications/u2f/u2f.c | 13 ++- applications/u2f/u2f.h | 5 + applications/u2f/u2f_app_i.h | 4 + applications/u2f/u2f_data.c | 106 +++++++++++++----- applications/u2f/u2f_hid.c | 16 ++- applications/u2f/views/u2f_view.c | 36 +++--- applications/u2f/views/u2f_view.h | 4 +- assets/compiled/assets_icons.c | 20 ++++ assets/compiled/assets_icons.h | 5 + assets/icons/U2F/Auth_62x31.png | Bin 0 -> 3761 bytes assets/icons/U2F/Connect_me_62x31.png | Bin 0 -> 3767 bytes assets/icons/U2F/Connected_62x31.png | Bin 0 -> 3765 bytes assets/icons/U2F/Drive_112x35.png | Bin 0 -> 3695 bytes assets/icons/U2F/Error_62x31.png | Bin 0 -> 3751 bytes core/furi/common_defines.h | 24 ---- core/furi/dangerous_defines.h | 38 +++++++ 18 files changed, 227 insertions(+), 77 deletions(-) create mode 100644 assets/icons/U2F/Auth_62x31.png create mode 100644 assets/icons/U2F/Connect_me_62x31.png create mode 100644 assets/icons/U2F/Connected_62x31.png create mode 100644 assets/icons/U2F/Drive_112x35.png create mode 100644 assets/icons/U2F/Error_62x31.png create mode 100644 core/furi/dangerous_defines.h diff --git a/applications/desktop/animations/animation_storage.c b/applications/desktop/animations/animation_storage.c index 623ac68d..80041595 100644 --- a/applications/desktop/animations/animation_storage.c +++ b/applications/desktop/animations/animation_storage.c @@ -15,6 +15,9 @@ #include #include +// Read documentation before using it +#include + #define ANIMATION_META_FILE "meta.txt" #define ANIMATION_DIR "/ext/dolphin/animations" #define ANIMATION_MANIFEST_FILE ANIMATION_DIR "/manifest.txt" diff --git a/applications/u2f/scenes/u2f_scene_main.c b/applications/u2f/scenes/u2f_scene_main.c index 6967a679..12472ce0 100644 --- a/applications/u2f/scenes/u2f_scene_main.c +++ b/applications/u2f/scenes/u2f_scene_main.c @@ -3,7 +3,8 @@ #include "furi-hal.h" #include "../u2f.h" -#define U2F_EVENT_TIMEOUT 500 +#define U2F_REQUEST_TIMEOUT 500 +#define U2F_SUCCESS_TIMEOUT 3000 static void u2f_scene_main_ok_callback(InputType type, void* context) { furi_assert(context); @@ -18,8 +19,14 @@ static void u2f_scene_main_event_callback(U2fNotifyEvent evt, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventRegister); else if(evt == U2fNotifyAuth) view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventAuth); + else if(evt == U2fNotifyAuthSuccess) + view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventAuthSuccess); else if(evt == U2fNotifyWink) view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventWink); + else if(evt == U2fNotifyConnect) + view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventConnect); + else if(evt == U2fNotifyDisconnect) + view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventDisconnect); } static void u2f_scene_main_timer_callback(void* context) { @@ -34,28 +41,39 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if((event.event == U2fCustomEventRegister) || (event.event == U2fCustomEventAuth)) { - osTimerStart(app->timer, U2F_EVENT_TIMEOUT); + if(event.event == U2fCustomEventConnect) { + osTimerStop(app->timer); + u2f_view_set_state(app->u2f_view, U2fMsgIdle); + } else if(event.event == U2fCustomEventDisconnect) { + osTimerStop(app->timer); + app->event_cur = U2fCustomEventNone; + u2f_view_set_state(app->u2f_view, U2fMsgNotConnected); + } else if((event.event == U2fCustomEventRegister) || (event.event == U2fCustomEventAuth)) { + osTimerStart(app->timer, U2F_REQUEST_TIMEOUT); if(app->event_cur == U2fCustomEventNone) { app->event_cur = event.event; if(event.event == U2fCustomEventRegister) u2f_view_set_state(app->u2f_view, U2fMsgRegister); else if(event.event == U2fCustomEventAuth) u2f_view_set_state(app->u2f_view, U2fMsgAuth); - notification_message(app->notifications, &sequence_success); + notification_message(app->notifications, &sequence_display_on); + notification_message(app->notifications, &sequence_single_vibro); } notification_message(app->notifications, &sequence_blink_blue_10); } else if(event.event == U2fCustomEventWink) { notification_message(app->notifications, &sequence_blink_green_10); + } else if(event.event == U2fCustomEventAuthSuccess) { + osTimerStart(app->timer, U2F_SUCCESS_TIMEOUT); + app->event_cur = U2fCustomEventNone; + u2f_view_set_state(app->u2f_view, U2fMsgSuccess); } else if(event.event == U2fCustomEventTimeout) { app->event_cur = U2fCustomEventNone; - u2f_view_set_state(app->u2f_view, U2fMsgNone); + u2f_view_set_state(app->u2f_view, U2fMsgIdle); } else if(event.event == U2fCustomEventConfirm) { if(app->event_cur != U2fCustomEventNone) { u2f_confirm_user_present(app->u2f_instance); } } - consumed = true; } else if(event.type == SceneManagerEventTypeTick) { } diff --git a/applications/u2f/u2f.c b/applications/u2f/u2f.c index 04c8e1df..15ee946e 100644 --- a/applications/u2f/u2f.c +++ b/applications/u2f/u2f.c @@ -299,10 +299,12 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) { uint8_t signature_len = u2f_der_encode_signature(resp->signature, signature); memcpy(resp->signature + signature_len, state_no_error, 2); - FURI_LOG_I(TAG, "Counter: %lu", U2F->counter); + FURI_LOG_D(TAG, "Counter: %lu", U2F->counter); U2F->counter++; u2f_data_cnt_write(U2F->counter); + if(U2F->callback != NULL) U2F->callback(U2fNotifyAuthSuccess, U2F->context); + return (sizeof(U2fAuthResp) + signature_len + 2); } @@ -330,3 +332,12 @@ uint16_t u2f_msg_parse(U2fData* U2F, uint8_t* buf, uint16_t len) { void u2f_wink(U2fData* U2F) { if(U2F->callback != NULL) U2F->callback(U2fNotifyWink, U2F->context); } + +void u2f_set_state(U2fData* U2F, uint8_t state) { + if(state == 0) { + if(U2F->callback != NULL) U2F->callback(U2fNotifyDisconnect, U2F->context); + } else { + if(U2F->callback != NULL) U2F->callback(U2fNotifyConnect, U2F->context); + } + U2F->user_present = false; +} diff --git a/applications/u2f/u2f.h b/applications/u2f/u2f.h index 5e7d7b32..4e10a3ea 100644 --- a/applications/u2f/u2f.h +++ b/applications/u2f/u2f.h @@ -9,7 +9,10 @@ extern "C" { typedef enum { U2fNotifyRegister, U2fNotifyAuth, + U2fNotifyAuthSuccess, U2fNotifyWink, + U2fNotifyConnect, + U2fNotifyDisconnect, } U2fNotifyEvent; typedef struct U2fData U2fData; @@ -30,6 +33,8 @@ uint16_t u2f_msg_parse(U2fData* instance, uint8_t* buf, uint16_t len); void u2f_wink(U2fData* instance); +void u2f_set_state(U2fData* instance, uint8_t state); + #ifdef __cplusplus } #endif diff --git a/applications/u2f/u2f_app_i.h b/applications/u2f/u2f_app_i.h index 6a30286e..2a4a7b59 100644 --- a/applications/u2f/u2f_app_i.h +++ b/applications/u2f/u2f_app_i.h @@ -17,8 +17,12 @@ typedef enum { U2fCustomEventNone, + U2fCustomEventConnect, + U2fCustomEventDisconnect, + U2fCustomEventRegister, U2fCustomEventAuth, + U2fCustomEventAuthSuccess, U2fCustomEventWink, U2fCustomEventTimeout, diff --git a/applications/u2f/u2f_data.c b/applications/u2f/u2f_data.c index 038918a7..c3b316c7 100644 --- a/applications/u2f/u2f_data.c +++ b/applications/u2f/u2f_data.c @@ -18,6 +18,8 @@ #define U2F_CERT_STOCK 0 // Stock certificate, private key is encrypted with factory key #define U2F_CERT_USER 1 // User certificate, private key is encrypted with unique key +#define U2F_CERT_USER_UNENCRYPTED \ + 2 // Unencrypted user certificate, will be encrypted after first load #define U2F_CERT_KEY_FILE_TYPE "Flipper U2F Certificate Key File" #define U2F_CERT_KEY_VERSION 1 @@ -92,6 +94,52 @@ uint32_t u2f_data_cert_load(uint8_t* cert) { return len_cur; } +static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { + furi_assert(cert_key); + + bool state = false; + uint8_t iv[16]; + uint8_t key[48]; + uint32_t cert_type = U2F_CERT_USER; + + FURI_LOG_I(TAG, "Encrypting user cert key"); + + // Generate random IV + furi_hal_random_fill_buf(iv, 16); + + if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + FURI_LOG_E(TAG, "Unable to load encryption key"); + return false; + } + + if(!furi_hal_crypto_encrypt(cert_key, key, 32)) { + FURI_LOG_E(TAG, "Encryption failed"); + return false; + } + furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + + Storage* storage = furi_record_open("storage"); + FlipperFile* flipper_file = flipper_file_alloc(storage); + + if(flipper_file_open_always(flipper_file, U2F_CERT_KEY_FILE)) { + do { + if(!flipper_file_write_header_cstr( + flipper_file, U2F_CERT_KEY_FILE_TYPE, U2F_CERT_KEY_VERSION)) + break; + if(!flipper_file_write_uint32(flipper_file, "Type", &cert_type, 1)) break; + if(!flipper_file_write_hex(flipper_file, "IV", iv, 16)) break; + if(!flipper_file_write_hex(flipper_file, "Data", key, 48)) break; + state = true; + } while(0); + } + + flipper_file_close(flipper_file); + flipper_file_free(flipper_file); + furi_record_close("storage"); + + return state; +} + bool u2f_data_cert_key_load(uint8_t* cert_key) { furi_assert(cert_key); @@ -133,33 +181,41 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { key_slot = U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY; } else if(cert_type == U2F_CERT_USER) { key_slot = U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE; + } else if(cert_type == U2F_CERT_USER_UNENCRYPTED) { + key_slot = 0; } else { FURI_LOG_E(TAG, "Unknown cert type"); break; } + if(key_slot != 0) { + if(!flipper_file_read_hex(flipper_file, "IV", iv, 16)) { + FURI_LOG_E(TAG, "Missing IV"); + break; + } - if(!flipper_file_read_hex(flipper_file, "IV", iv, 16)) { - FURI_LOG_E(TAG, "Missing IV"); - break; - } + if(!flipper_file_read_hex(flipper_file, "Data", key, 48)) { + FURI_LOG_E(TAG, "Missing data"); + break; + } - if(!flipper_file_read_hex(flipper_file, "Data", key, 48)) { - FURI_LOG_E(TAG, "Missing data"); - break; - } - - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { - FURI_LOG_E(TAG, "Unable to load encryption key"); - break; - } - memset(cert_key, 0, 32); - - if(!furi_hal_crypto_decrypt(key, cert_key, 32)) { + if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + FURI_LOG_E(TAG, "Unable to load encryption key"); + break; + } memset(cert_key, 0, 32); - FURI_LOG_E(TAG, "Decryption failed"); - break; + + if(!furi_hal_crypto_decrypt(key, cert_key, 32)) { + memset(cert_key, 0, 32); + FURI_LOG_E(TAG, "Decryption failed"); + break; + } + furi_hal_crypto_store_unload_key(key_slot); + } else { + if(!flipper_file_read_hex(flipper_file, "Data", cert_key, 32)) { + FURI_LOG_E(TAG, "Missing data"); + break; + } } - furi_hal_crypto_store_unload_key(key_slot); state = true; } while(0); } @@ -169,6 +225,10 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { furi_record_close("storage"); string_clear(filetype); + if(cert_type == U2F_CERT_USER_UNENCRYPTED) { + return u2f_data_cert_key_encrypt(cert_key); + } + return state; } @@ -249,9 +309,6 @@ bool u2f_data_key_generate(uint8_t* device_key) { } furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); - string_t filetype; - string_init(filetype); - Storage* storage = furi_record_open("storage"); FlipperFile* flipper_file = flipper_file_alloc(storage); @@ -270,7 +327,6 @@ bool u2f_data_key_generate(uint8_t* device_key) { flipper_file_close(flipper_file); flipper_file_free(flipper_file); furi_record_close("storage"); - string_clear(filetype); return state; } @@ -356,9 +412,6 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { } furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); - string_t filetype; - string_init(filetype); - Storage* storage = furi_record_open("storage"); FlipperFile* flipper_file = flipper_file_alloc(storage); @@ -376,7 +429,6 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { flipper_file_close(flipper_file); flipper_file_free(flipper_file); furi_record_close("storage"); - string_clear(filetype); return state; } diff --git a/applications/u2f/u2f_hid.c b/applications/u2f/u2f_hid.c index 34767c42..766d6ac7 100644 --- a/applications/u2f/u2f_hid.c +++ b/applications/u2f/u2f_hid.c @@ -128,7 +128,7 @@ static void u2f_hid_send_error(U2fHid* u2f_hid, uint8_t error) { } static bool u2f_hid_parse_request(U2fHid* u2f_hid) { - FURI_LOG_I( + FURI_LOG_D( WORKER_TAG, "Req cid=%lX cmd=%x len=%u", u2f_hid->packet.cid, @@ -188,7 +188,7 @@ static int32_t u2f_hid_worker(void* context) { U2fHid* u2f_hid = context; uint8_t packet_buf[HID_U2F_PACKET_LEN]; - FURI_LOG_I(WORKER_TAG, "Init"); + FURI_LOG_D(WORKER_TAG, "Init"); UsbInterface* usb_mode_prev = furi_hal_usb_get_config(); furi_hal_usb_set_config(&usb_hid_u2f); @@ -204,8 +204,14 @@ static int32_t u2f_hid_worker(void* context) { osWaitForever); furi_check((flags & osFlagsError) == 0); if(flags & WorkerEvtStop) break; - if(flags & WorkerEvtConnect) FURI_LOG_I(WORKER_TAG, "Connect"); - if(flags & WorkerEvtDisconnect) FURI_LOG_I(WORKER_TAG, "Disconnect"); + if(flags & WorkerEvtConnect) { + u2f_set_state(u2f_hid->u2f_instance, 1); + FURI_LOG_D(WORKER_TAG, "Connect"); + } + if(flags & WorkerEvtDisconnect) { + u2f_set_state(u2f_hid->u2f_instance, 0); + FURI_LOG_D(WORKER_TAG, "Disconnect"); + } if(flags & WorkerEvtRequest) { uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf); if(len_cur > 0) { @@ -265,7 +271,7 @@ static int32_t u2f_hid_worker(void* context) { furi_hal_hid_u2f_set_callback(NULL, NULL); furi_hal_usb_set_config(usb_mode_prev); - FURI_LOG_I(WORKER_TAG, "End"); + FURI_LOG_D(WORKER_TAG, "End"); return 0; } diff --git a/applications/u2f/views/u2f_view.c b/applications/u2f/views/u2f_view.c index b013638c..20c34e9c 100644 --- a/applications/u2f/views/u2f_view.c +++ b/applications/u2f/views/u2f_view.c @@ -14,22 +14,32 @@ typedef struct { static void u2f_view_draw_callback(Canvas* canvas, void* _model) { U2fModel* model = _model; - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "U2F Demo"); + canvas_draw_icon(canvas, 8, 14, &I_Drive_112x35); + canvas_set_font(canvas, FontSecondary); - if(model->display_msg == U2fMsgRegister) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 45, AlignLeft, AlignBottom, "Registration"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 0, 63, AlignLeft, AlignBottom, "Press [OK] to confirm"); + if(model->display_msg == U2fMsgNotConnected) { + canvas_draw_icon(canvas, 22, 15, &I_Connect_me_62x31); + canvas_draw_str_aligned( + canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connect me to computer"); + } else if(model->display_msg == U2fMsgIdle) { + canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31); + canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!"); + } else if(model->display_msg == U2fMsgRegister) { + elements_button_center(canvas, "OK"); + canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31); + canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register"); } else if(model->display_msg == U2fMsgAuth) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 45, AlignLeft, AlignBottom, "Authentication"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 0, 63, AlignLeft, AlignBottom, "Press [OK] to confirm"); + elements_button_center(canvas, "OK"); + canvas_draw_icon(canvas, 22, 15, &I_Auth_62x31); + canvas_draw_str_aligned( + canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to authenticate"); + } else if(model->display_msg == U2fMsgSuccess) { + canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31); + canvas_draw_str_aligned( + canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successfull!"); } else if(model->display_msg == U2fMsgError) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, "U2F data missing"); + canvas_draw_icon(canvas, 22, 15, &I_Error_62x31); + canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Ceritficate missing"); } } diff --git a/applications/u2f/views/u2f_view.h b/applications/u2f/views/u2f_view.h index a222fbc3..5da3279a 100644 --- a/applications/u2f/views/u2f_view.h +++ b/applications/u2f/views/u2f_view.h @@ -6,9 +6,11 @@ typedef struct U2fView U2fView; typedef void (*U2fOkCallback)(InputType type, void* context); typedef enum { - U2fMsgNone, + U2fMsgNotConnected, + U2fMsgIdle, U2fMsgRegister, U2fMsgAuth, + U2fMsgSuccess, U2fMsgError, } U2fViewMsg; diff --git a/assets/compiled/assets_icons.c b/assets/compiled/assets_icons.c index f136d6c1..f5f7a6d3 100644 --- a/assets/compiled/assets_icons.c +++ b/assets/compiled/assets_icons.c @@ -631,6 +631,21 @@ const uint8_t *_I_Scanning_123x52[] = {_I_Scanning_123x52_0}; const uint8_t _I_Unlock_7x8_0[] = {0x00,0x1C,0x22,0x02,0x4F,0x67,0x73,0x79,0x3C,}; const uint8_t *_I_Unlock_7x8[] = {_I_Unlock_7x8_0}; +const uint8_t _I_Auth_62x31_0[] = {0x01,0x00,0xaf,0x00,0x00,0x47,0xc2,0xfe,0x07,0x58,0x66,0x02,0x02,0x07,0x48,0x1c,0x02,0x0c,0x06,0x3c,0x00,0x08,0x61,0x00,0x73,0xa0,0x00,0x86,0x20,0x02,0x1b,0x04,0x02,0x40,0x04,0x10,0x11,0x01,0xc4,0x18,0x40,0x72,0xf0,0x40,0x40,0xe4,0x1a,0x20,0x38,0xc2,0x3e,0x00,0x71,0xbc,0x05,0xca,0x11,0x08,0x80,0xe0,0x30,0xc0,0x72,0x82,0x7d,0x20,0x44,0x81,0x80,0x81,0xcb,0x75,0x05,0x02,0x08,0x1c,0xe7,0x50,0x58,0xc0,0x94,0x40,0xe5,0xfa,0x82,0xc1,0xbf,0x06,0xc1,0x80,0x40,0x80,0xe3,0x00,0xbe,0x40,0x3f,0x10,0x18,0x17,0xd0,0xd0,0x33,0xf3,0xa0,0xc0,0xe0,0x52,0x88,0x26,0x02,0x3e,0x1d,0x18,0x14,0x08,0xa0,0x3c,0x08,0x78,0x3c,0xc0,0xe3,0xe0,0x83,0x87,0xcd,0x32,0x42,0x11,0x17,0x90,0x04,0x61,0x9f,0xf8,0x06,0x20,0x0e,0x41,0xb1,0x9e,0x1b,0x44,0x2e,0x5f,0x0f,0xfc,0x0c,0x0e,0x80,0x02,0x80,0xc1,0x00,0xe8,0xab,0x11,0xf9,0x01,0xca,0xe0,0x07,0x68,0x60,0xb4,0x40,0xe7,0xfe,0x1f,0x88,0x1d,0x09,0x82,0x28,0x10,0xba,0x01,0xcc,}; +const uint8_t *_I_Auth_62x31[] = {_I_Auth_62x31_0}; + +const uint8_t _I_Connect_me_62x31_0[] = {0x01,0x00,0xb7,0x00,0x00,0x47,0xc2,0xfe,0x07,0x58,0x66,0x02,0x02,0x07,0x48,0x1c,0x02,0x0c,0x06,0x3c,0x00,0x08,0x61,0x00,0x73,0xa0,0x00,0x86,0x20,0x02,0x1b,0xe4,0x02,0x40,0x04,0x10,0x11,0x51,0x01,0x86,0x07,0x2b,0x60,0x1c,0xc3,0x44,0x0f,0x18,0x47,0xc0,0x0e,0x37,0x80,0xf9,0x42,0x21,0x10,0x1c,0x06,0x1a,0x01,0x82,0x80,0x41,0x3e,0x90,0x22,0x40,0xc0,0x40,0xe5,0xba,0x82,0xd8,0x20,0x00,0x73,0x9d,0x41,0x63,0x1e,0x00,0x39,0xfe,0xa0,0xb0,0x6f,0xc0,0x7c,0xa0,0x40,0x71,0x16,0x90,0x1c,0xbe,0x86,0x81,0x9f,0x81,0xce,0x51,0x04,0xc0,0x47,0xe0,0x1f,0xe0,0x38,0x94,0x07,0x81,0x0f,0x80,0x4a,0x00,0xe7,0xe0,0x83,0x81,0xcd,0x32,0x42,0x11,0x03,0x9c,0x0a,0x19,0xff,0x80,0x4e,0x00,0xe5,0x0c,0x81,0xcf,0x20,0x21,0xc1,0x03,0x8f,0xc3,0xff,0x03,0x80,0x92,0x44,0x3e,0x40,0x01,0x80,0xc1,0x20,0x60,0x73,0x55,0x8c,0x0a,0x08,0x07,0x3b,0x80,0x1c,0x61,0x00,0x73,0x86,0x11,0x48,0x0e,0x5f,0xe1,0xf8,0xc3,0x00,0xe7,0xf8,0x6c,0x0c,0x42,0x40,0x17,0x30,0x38,0xcc,0x24,0x00,}; +const uint8_t *_I_Connect_me_62x31[] = {_I_Connect_me_62x31_0}; + +const uint8_t _I_Connected_62x31_0[] = {0x01,0x00,0xaa,0x00,0x00,0x47,0xc2,0xfe,0x07,0x58,0x66,0x02,0x02,0x07,0x48,0x1c,0x02,0x0c,0x06,0x3c,0x00,0x08,0x61,0x00,0x73,0xa0,0x00,0x86,0x20,0x07,0x39,0x00,0x09,0x01,0x88,0x07,0x70,0xd1,0x09,0x0b,0xe0,0x07,0x1b,0xc0,0x1c,0xe1,0x10,0x1c,0x06,0x18,0x0e,0x50,0x4f,0xa4,0x08,0x90,0x24,0x92,0x82,0x6e,0xa0,0xb6,0x08,0x07,0x04,0x10,0x30,0x49,0xd4,0x16,0x31,0xe0,0xa0,0xfc,0x80,0xe3,0xfa,0x82,0xc1,0xbf,0x14,0x08,0x64,0x06,0x04,0x07,0x18,0x05,0xf2,0x81,0x04,0x81,0x40,0xbe,0x86,0x81,0x9f,0xe0,0x20,0x80,0x81,0x94,0x41,0x30,0x11,0xf0,0x39,0x94,0x07,0x81,0x0f,0x03,0xaf,0x82,0x0f,0x00,0x84,0x81,0xc5,0x32,0x42,0x11,0x98,0x89,0xc6,0x01,0x02,0x86,0x7f,0xc9,0x03,0x03,0x03,0x8c,0x32,0x07,0x3c,0x2c,0x08,0x3c,0xbe,0x1f,0xf8,0x18,0x1d,0x00,0x05,0x81,0x0e,0x08,0x1c,0xf0,0x0a,0xc1,0x03,0xa5,0xc0,0x0e,0xd0,0xc4,0xc8,0x81,0xcf,0xfd,0x03,0x03,0xaf,0xe2,0x02,0xb1,0x10,0xba,0x01,0xcc,}; +const uint8_t *_I_Connected_62x31[] = {_I_Connected_62x31_0}; + +const uint8_t _I_Drive_112x35_0[] = {0x01,0x00,0x72,0x00,0xf0,0x7f,0xc0,0x0f,0x1f,0x06,0x94,0x40,0x2f,0x12,0x00,0x19,0x42,0x01,0xb1,0x40,0x01,0x94,0x10,0x1b,0x18,0x00,0x19,0x41,0x81,0xb9,0x07,0x06,0xc9,0x24,0x81,0xb4,0x02,0x20,0x6f,0x83,0x66,0x7c,0x0d,0xc9,0x10,0x6f,0xc1,0xbe,0x0d,0xf2,0x9f,0x83,0x7c,0x14,0x3f,0x8f,0xff,0xe4,0x1b,0x4c,0xe1,0xc4,0x83,0x6a,0x1f,0x00,0xc7,0x8d,0xfc,0xc3,0xe0,0xdf,0x06,0xfa,0xd7,0xc3,0x7e,0x99,0xf0,0x6d,0x7f,0xc0,0x02,0x0d,0xcb,0xf8,0x37,0x27,0xe3,0x7c,0x80,0x2a,0x84,0x00,0xf4,0x00,0x19,0x47,0xc1,0xb1,0x20,0x01,0x97,0xf8,0x92,0x40,0x05,0x0b,0x9f,0xf0,0x1b,0x03,0x33,0x7f,0x08,0x01,0xc9,0xe6,}; +const uint8_t *_I_Drive_112x35[] = {_I_Drive_112x35_0}; + +const uint8_t _I_Error_62x31_0[] = {0x01,0x00,0x9e,0x00,0x00,0x47,0xc2,0xfe,0x07,0x58,0x66,0x02,0x02,0x07,0x48,0x1c,0x02,0x0c,0x06,0x3c,0x00,0x08,0x61,0x00,0x73,0xa0,0x00,0x86,0x20,0x07,0x39,0x00,0x09,0x01,0x88,0x07,0x70,0xd1,0x09,0x0b,0xe0,0x07,0x38,0x1c,0x62,0x11,0x08,0x80,0x8c,0x8a,0x0f,0x1c,0x82,0x7d,0x20,0x58,0x0b,0xe4,0x02,0x1d,0x0e,0x82,0x6e,0xa0,0xb8,0x0c,0x1c,0x02,0x39,0x07,0x82,0x4e,0xa0,0xb7,0x08,0x04,0x07,0x71,0x03,0x82,0x7e,0xa0,0xb0,0xe8,0x04,0x0b,0xe1,0x01,0x81,0x01,0xc6,0x01,0xc0,0x81,0xf8,0x01,0x42,0x27,0x18,0x04,0xc0,0x1e,0x63,0x71,0x3d,0x0c,0x08,0x3e,0x20,0xa1,0x22,0x94,0x08,0x5e,0x21,0x51,0x0f,0x08,0xbc,0x47,0xe2,0x07,0x29,0x81,0x40,0x49,0xe2,0x07,0x28,0x61,0x80,0x4b,0xe2,0x07,0x28,0x19,0xe0,0xc0,0xe2,0x0d,0x18,0xc0,0x1d,0x00,0x02,0xa8,0x30,0x39,0x2e,0x10,0x0e,0x5e,0x00,0x3b,0x7e,0x00,0xec,0x46,0x10,0x3f,0x80,0xc8,}; +const uint8_t *_I_Error_62x31[] = {_I_Error_62x31_0}; + const uint8_t _I_DolphinExcited_64x63_0[] = {0x01,0x00,0x36,0x01,0x00,0x25,0x00,0x0f,0xd2,0x00,0x3b,0xe0,0x00,0xeb,0x10,0x0c,0x34,0x40,0x30,0xd0,0x88,0x80,0x1d,0xa1,0x00,0x42,0xfc,0x7f,0xc0,0x63,0x04,0x01,0x0e,0x02,0x0f,0x00,0x00,0x8c,0x08,0x0e,0x37,0x00,0x10,0xc6,0x20,0x10,0x10,0xd9,0x11,0x92,0x1c,0x1a,0x3e,0x00,0x04,0x42,0x02,0x1a,0x20,0xb0,0xce,0x00,0x64,0x07,0x20,0x59,0x16,0x50,0x36,0x45,0x94,0x84,0x78,0x20,0x60,0x75,0x8e,0x43,0x06,0x63,0x3c,0x33,0x94,0x0c,0xd2,0x5c,0x30,0x38,0xe4,0x08,0x43,0x10,0xc0,0x5e,0x06,0x22,0x53,0x1a,0x02,0x08,0x7f,0xd0,0x32,0xc1,0x50,0x21,0x14,0x0e,0x70,0x1c,0x46,0xe2,0x07,0x19,0x06,0x3c,0xdc,0x20,0x91,0xae,0x01,0xcc,0xbe,0x30,0x09,0xfc,0x12,0x41,0xff,0x83,0xcc,0x0a,0xa3,0x1f,0x03,0x99,0xe8,0x7c,0x10,0xf8,0x25,0xa0,0x5e,0x50,0x0f,0x84,0x1e,0x09,0x54,0x03,0x9f,0xf2,0x07,0x02,0xd5,0x11,0xca,0x01,0xfe,0x80,0xc0,0xaa,0x9f,0xf0,0x39,0x5f,0xd0,0x43,0xaa,0x83,0x41,0x92,0xc3,0x1f,0x03,0x8d,0x52,0x02,0x2e,0x25,0xc9,0x6a,0x99,0x46,0xa6,0x2a,0xa0,0x1c,0xaf,0xca,0x62,0x94,0x28,0xcb,0x7e,0x0f,0x15,0x71,0xf8,0x3c,0x22,0x71,0x03,0x8a,0x84,0x67,0x18,0x0f,0xac,0x1c,0x0e,0x38,0x08,0x0c,0x3e,0x01,0xae,0xbd,0x13,0x0c,0x0e,0x35,0x8e,0xa8,0x1c,0xb0,0x1f,0xf8,0x06,0x83,0xf4,0x27,0x38,0x07,0xff,0xff,0x8f,0x03,0xa0,0x4c,0x80,0xed,0x60,0x03,0xb4,0x60,0x0e,0xd0,0x60,0x3a,0x87,0x84,0x0e,0xb7,0xc2,0xfa,0x18,0x05,0x44,0x20,0x73,0xff,0xf7,0xce,0xe4,0x07,0x2d,0x52,0x2c,0x80,0xe7,0x54,0xea,0x81,0xd7,0x50,0x0f,0x7a,0xaa,0x3d,0x41,0xe2,0x07,0x5a,0x80,0x3c,0xa0,0x40,0x72,0xd0,0x6a,0x80,0xa2,0x07,0x3a,0x05,0x54,0x8e,0x20,0x73,0xc0,0x03,0xd8,0x60,0x30,0x40,0x3a,0xc0,0x00,0xee,0xea,0x10,0x3b,0x80,}; const uint8_t *_I_DolphinExcited_64x63[] = {_I_DolphinExcited_64x63_0}; @@ -829,6 +844,11 @@ const Icon I_MHz_25x11 = {.width=25,.height=11,.frame_count=1,.frame_rate=0,.fra const Icon I_Quest_7x8 = {.width=7,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Quest_7x8}; const Icon I_Scanning_123x52 = {.width=123,.height=52,.frame_count=1,.frame_rate=0,.frames=_I_Scanning_123x52}; const Icon I_Unlock_7x8 = {.width=7,.height=8,.frame_count=1,.frame_rate=0,.frames=_I_Unlock_7x8}; +const Icon I_Auth_62x31 = {.width=62,.height=31,.frame_count=1,.frame_rate=0,.frames=_I_Auth_62x31}; +const Icon I_Connect_me_62x31 = {.width=62,.height=31,.frame_count=1,.frame_rate=0,.frames=_I_Connect_me_62x31}; +const Icon I_Connected_62x31 = {.width=62,.height=31,.frame_count=1,.frame_rate=0,.frames=_I_Connected_62x31}; +const Icon I_Drive_112x35 = {.width=112,.height=35,.frame_count=1,.frame_rate=0,.frames=_I_Drive_112x35}; +const Icon I_Error_62x31 = {.width=62,.height=31,.frame_count=1,.frame_rate=0,.frames=_I_Error_62x31}; const Icon I_DolphinExcited_64x63 = {.width=64,.height=63,.frame_count=1,.frame_rate=0,.frames=_I_DolphinExcited_64x63}; const Icon I_DolphinMafia_115x62 = {.width=115,.height=62,.frame_count=1,.frame_rate=0,.frames=_I_DolphinMafia_115x62}; const Icon I_DolphinNice_96x59 = {.width=96,.height=59,.frame_count=1,.frame_rate=0,.frames=_I_DolphinNice_96x59}; diff --git a/assets/compiled/assets_icons.h b/assets/compiled/assets_icons.h index 0f4c2265..3dac8eb1 100644 --- a/assets/compiled/assets_icons.h +++ b/assets/compiled/assets_icons.h @@ -178,6 +178,11 @@ extern const Icon I_MHz_25x11; extern const Icon I_Quest_7x8; extern const Icon I_Scanning_123x52; extern const Icon I_Unlock_7x8; +extern const Icon I_Auth_62x31; +extern const Icon I_Connect_me_62x31; +extern const Icon I_Connected_62x31; +extern const Icon I_Drive_112x35; +extern const Icon I_Error_62x31; extern const Icon I_DolphinExcited_64x63; extern const Icon I_DolphinMafia_115x62; extern const Icon I_DolphinNice_96x59; diff --git a/assets/icons/U2F/Auth_62x31.png b/assets/icons/U2F/Auth_62x31.png new file mode 100644 index 0000000000000000000000000000000000000000..40f094ac9ba758ea06838c2bb8376cf6f28f8050 GIT binary patch literal 3761 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 literal 0 HcmV?d00001 diff --git a/assets/icons/U2F/Connect_me_62x31.png b/assets/icons/U2F/Connect_me_62x31.png new file mode 100644 index 0000000000000000000000000000000000000000..68c48c0e68142548919d6a4b02e40b48a243b04e GIT binary patch literal 3767 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