[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 <alleteam@gmail.com>
This commit is contained in:
@@ -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) {
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -17,8 +17,12 @@
|
||||
typedef enum {
|
||||
U2fCustomEventNone,
|
||||
|
||||
U2fCustomEventConnect,
|
||||
U2fCustomEventDisconnect,
|
||||
|
||||
U2fCustomEventRegister,
|
||||
U2fCustomEventAuth,
|
||||
U2fCustomEventAuthSuccess,
|
||||
U2fCustomEventWink,
|
||||
|
||||
U2fCustomEventTimeout,
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user