[FL-1958] U2F prototype (#879)
* U2F implementation prototype * U2F data encryption and store, user confirmation request * remove debug prints * fix notification bug in chrome * split u2f_alloc into u2f_init and u2f_alloc * typo fix, furi-hal-trng -> furi-hal-random * rand/srand redefinition * SubGhz: a little bit of Dante. * u2f_data naming fix Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
parent
9b62b557b4
commit
9e62f08e4d
@ -20,6 +20,7 @@ extern int32_t desktop_srv(void* p);
|
||||
extern int32_t accessor_app(void* p);
|
||||
extern int32_t archive_app(void* p);
|
||||
extern int32_t bad_usb_app(void* p);
|
||||
extern int32_t u2f_app(void* p);
|
||||
extern int32_t uart_echo_app(void* p);
|
||||
extern int32_t blink_test_app(void* p);
|
||||
extern int32_t bt_debug_app(void* p);
|
||||
@ -154,6 +155,11 @@ const FlipperApplication FLIPPER_APPS[] = {
|
||||
#ifdef APP_BAD_USB
|
||||
{.app = bad_usb_app, .name = "Bad USB", .stack_size = 2048, .icon = &A_BadUsb_14},
|
||||
#endif
|
||||
|
||||
#ifdef APP_U2F
|
||||
{.app = u2f_app, .name = "U2F", .stack_size = 2048, .icon = &A_U2F_14},
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
const size_t FLIPPER_APPS_COUNT = sizeof(FLIPPER_APPS) / sizeof(FlipperApplication);
|
||||
|
@ -50,6 +50,7 @@ APP_DISPLAY_TEST = 1
|
||||
APP_BLE_HID = 1
|
||||
APP_USB_MOUSE = 1
|
||||
APP_BAD_USB = 1
|
||||
APP_U2F = 1
|
||||
APP_UART_ECHO = 1
|
||||
endif
|
||||
|
||||
@ -165,7 +166,13 @@ APP_BAD_USB ?= 0
|
||||
ifeq ($(APP_BAD_USB), 1)
|
||||
CFLAGS += -DAPP_BAD_USB
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
APP_U2F ?= 0
|
||||
ifeq ($(APP_U2F), 1)
|
||||
CFLAGS += -DAPP_U2F
|
||||
SRV_GUI = 1
|
||||
endif
|
||||
|
||||
APP_BLE_HID ?=0
|
||||
ifeq ($(APP_BLE_HID), 1)
|
||||
|
@ -35,7 +35,7 @@ void usb_test_submenu_callback(void* context, uint32_t index) {
|
||||
} else if(index == UsbTestSubmenuIndexHid) {
|
||||
furi_hal_usb_set_config(&usb_hid);
|
||||
} else if(index == UsbTestSubmenuIndexHidU2F) {
|
||||
//furi_hal_usb_set_config(UsbModeU2F);
|
||||
furi_hal_usb_set_config(&usb_hid_u2f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ UsbTestApp* usb_test_app_alloc() {
|
||||
submenu_add_item(
|
||||
app->submenu, "HID KB+Mouse", UsbTestSubmenuIndexHid, usb_test_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu, "TODO: HID U2F", UsbTestSubmenuIndexHidU2F, usb_test_submenu_callback, app);
|
||||
app->submenu, "HID U2F", UsbTestSubmenuIndexHidU2F, usb_test_submenu_callback, app);
|
||||
view_set_previous_callback(submenu_get_view(app->submenu), usb_test_exit);
|
||||
view_dispatcher_add_view(app->view_dispatcher, 0, submenu_get_view(app->submenu));
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
/*
|
||||
* Give up hope, everyone who enters here!!!
|
||||
* Оставь надежду, всяк сюда входящий!!!
|
||||
*/
|
||||
/* Abandon hope, all ye who enter here. */
|
||||
|
||||
#include "subghz_i.h"
|
||||
#include <lib/toolbox/path.h>
|
||||
@ -329,4 +326,4 @@ int32_t subghz_app(void* p) {
|
||||
subghz_free(subghz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
30
applications/u2f/scenes/u2f_scene.c
Normal file
30
applications/u2f/scenes/u2f_scene.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include "u2f_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const u2f_scene_on_enter_handlers[])(void*) = {
|
||||
#include "u2f_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 u2f_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "u2f_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 u2f_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "u2f_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers u2f_scene_handlers = {
|
||||
.on_enter_handlers = u2f_scene_on_enter_handlers,
|
||||
.on_event_handlers = u2f_scene_on_event_handlers,
|
||||
.on_exit_handlers = u2f_scene_on_exit_handlers,
|
||||
.scene_num = U2fSceneNum,
|
||||
};
|
29
applications/u2f/scenes/u2f_scene.h
Normal file
29
applications/u2f/scenes/u2f_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) U2fScene##id,
|
||||
typedef enum {
|
||||
#include "u2f_scene_config.h"
|
||||
U2fSceneNum,
|
||||
} U2fScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers u2f_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "u2f_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 "u2f_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 "u2f_scene_config.h"
|
||||
#undef ADD_SCENE
|
1
applications/u2f/scenes/u2f_scene_config.h
Normal file
1
applications/u2f/scenes/u2f_scene_config.h
Normal file
@ -0,0 +1 @@
|
||||
ADD_SCENE(u2f, main, Main)
|
92
applications/u2f/scenes/u2f_scene_main.c
Normal file
92
applications/u2f/scenes/u2f_scene_main.c
Normal file
@ -0,0 +1,92 @@
|
||||
#include "../u2f_app_i.h"
|
||||
#include "../views/u2f_view.h"
|
||||
#include "furi-hal.h"
|
||||
#include "../u2f.h"
|
||||
|
||||
#define U2F_EVENT_TIMEOUT 500
|
||||
|
||||
static void u2f_scene_main_ok_callback(InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
U2fApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventConfirm);
|
||||
}
|
||||
|
||||
static void u2f_scene_main_event_callback(U2fNotifyEvent evt, void* context) {
|
||||
furi_assert(context);
|
||||
U2fApp* app = context;
|
||||
if(evt == U2fNotifyRegister)
|
||||
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 == U2fNotifyWink)
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventWink);
|
||||
}
|
||||
|
||||
static void u2f_scene_main_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
U2fApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, U2fCustomEventTimeout);
|
||||
}
|
||||
|
||||
bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
furi_assert(context);
|
||||
U2fApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if((event.event == U2fCustomEventRegister) || (event.event == U2fCustomEventAuth)) {
|
||||
osTimerStart(app->timer, U2F_EVENT_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_blink_blue_10);
|
||||
} else if(event.event == U2fCustomEventWink) {
|
||||
notification_message(app->notifications, &sequence_blink_green_10);
|
||||
} else if(event.event == U2fCustomEventTimeout) {
|
||||
app->event_cur = U2fCustomEventNone;
|
||||
u2f_view_set_state(app->u2f_view, U2fMsgNone);
|
||||
} else if(event.event == U2fCustomEventConfirm) {
|
||||
if(app->event_cur != U2fCustomEventNone) {
|
||||
u2f_confirm_user_present(app->u2f_instance);
|
||||
}
|
||||
}
|
||||
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void u2f_scene_main_on_enter(void* context) {
|
||||
U2fApp* app = context;
|
||||
|
||||
app->timer = osTimerNew(u2f_scene_main_timer_callback, osTimerOnce, app, NULL);
|
||||
|
||||
app->u2f_instance = u2f_alloc();
|
||||
app->u2f_ready = u2f_init(app->u2f_instance);
|
||||
if(app->u2f_ready == true) {
|
||||
u2f_set_event_callback(app->u2f_instance, u2f_scene_main_event_callback, app);
|
||||
app->u2f_hid = u2f_hid_start(app->u2f_instance);
|
||||
u2f_view_set_ok_callback(app->u2f_view, u2f_scene_main_ok_callback, app);
|
||||
} else {
|
||||
u2f_free(app->u2f_instance);
|
||||
u2f_view_set_state(app->u2f_view, U2fMsgError);
|
||||
}
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, U2fAppViewMain);
|
||||
}
|
||||
|
||||
void u2f_scene_main_on_exit(void* context) {
|
||||
U2fApp* app = context;
|
||||
osTimerStop(app->timer);
|
||||
osTimerDelete(app->timer);
|
||||
if(app->u2f_ready == true) {
|
||||
u2f_hid_stop(app->u2f_hid);
|
||||
u2f_free(app->u2f_instance);
|
||||
}
|
||||
}
|
332
applications/u2f/u2f.c
Normal file
332
applications/u2f/u2f.c
Normal file
@ -0,0 +1,332 @@
|
||||
#include <furi.h>
|
||||
#include "u2f.h"
|
||||
#include "u2f_hid.h"
|
||||
#include "u2f_data.h"
|
||||
#include <furi-hal.h>
|
||||
#include <furi-hal-random.h>
|
||||
|
||||
#include "toolbox/sha256.h"
|
||||
#include "toolbox/hmac_sha256.h"
|
||||
#include "micro-ecc/uECC.h"
|
||||
|
||||
#define TAG "U2F"
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
#define U2F_CMD_REGISTER 0x01
|
||||
#define U2F_CMD_AUTHENTICATE 0x02
|
||||
#define U2F_CMD_VERSION 0x03
|
||||
|
||||
typedef enum {
|
||||
U2fCheckOnly = 0x07, // "check-only" - only check key handle, don't send auth response
|
||||
U2fEnforce =
|
||||
0x03, // "enforce-user-presence-and-sign" - send auth response only if user is present
|
||||
U2fDontEnforce =
|
||||
0x08, // "dont-enforce-user-presence-and-sign" - send auth response even if user is missing
|
||||
} U2fAuthMode;
|
||||
|
||||
typedef struct {
|
||||
uint8_t format;
|
||||
uint8_t xy[64];
|
||||
} __attribute__((packed)) U2fPubKey;
|
||||
|
||||
typedef struct {
|
||||
uint8_t len;
|
||||
uint8_t hash[32];
|
||||
uint8_t nonce[32];
|
||||
} __attribute__((packed)) U2fKeyHandle;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t len[3];
|
||||
uint8_t challenge[32];
|
||||
uint8_t app_id[32];
|
||||
} __attribute__((packed)) U2fRegisterReq;
|
||||
|
||||
typedef struct {
|
||||
uint8_t reserved;
|
||||
U2fPubKey pub_key;
|
||||
U2fKeyHandle key_handle;
|
||||
uint8_t cert[];
|
||||
} __attribute__((packed)) U2fRegisterResp;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t len[3];
|
||||
uint8_t challenge[32];
|
||||
uint8_t app_id[32];
|
||||
U2fKeyHandle key_handle;
|
||||
} __attribute__((packed)) U2fAuthReq;
|
||||
|
||||
typedef struct {
|
||||
uint8_t user_present;
|
||||
uint32_t counter;
|
||||
uint8_t signature[];
|
||||
} __attribute__((packed)) U2fAuthResp;
|
||||
|
||||
static const uint8_t ver_str[] = {"U2F_V2"};
|
||||
|
||||
static const uint8_t state_no_error[] = {0x90, 0x00};
|
||||
static const uint8_t state_not_supported[] = {0x6D, 0x00};
|
||||
static const uint8_t state_user_missing[] = {0x69, 0x85};
|
||||
static const uint8_t state_wrong_data[] = {0x6A, 0x80};
|
||||
|
||||
struct U2fData {
|
||||
uint8_t device_key[32];
|
||||
uint8_t cert_key[32];
|
||||
uint32_t counter;
|
||||
const struct uECC_Curve_t* p_curve;
|
||||
bool ready;
|
||||
bool user_present;
|
||||
U2fEvtCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
static int u2f_uecc_random(uint8_t* dest, unsigned size) {
|
||||
furi_hal_random_fill_buf(dest, size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
U2fData* u2f_alloc() {
|
||||
return furi_alloc(sizeof(U2fData));
|
||||
}
|
||||
|
||||
void u2f_free(U2fData* U2F) {
|
||||
furi_assert(U2F);
|
||||
free(U2F);
|
||||
}
|
||||
|
||||
bool u2f_init(U2fData* U2F) {
|
||||
furi_assert(U2F);
|
||||
|
||||
if(u2f_data_cert_check() == false) {
|
||||
FURI_LOG_E(TAG, "Certificate load error");
|
||||
return false;
|
||||
}
|
||||
if(u2f_data_cert_key_load(U2F->cert_key) == false) {
|
||||
FURI_LOG_E(TAG, "Certificate key load error");
|
||||
return false;
|
||||
}
|
||||
if(u2f_data_key_load(U2F->device_key) == false) {
|
||||
FURI_LOG_W(TAG, "Key loading error, generating new");
|
||||
if(u2f_data_key_generate(U2F->device_key) == false) {
|
||||
FURI_LOG_E(TAG, "Key write failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(u2f_data_cnt_read(&U2F->counter) == false) {
|
||||
FURI_LOG_W(TAG, "Counter loading error, resetting counter");
|
||||
U2F->counter = 0;
|
||||
if(u2f_data_cnt_write(0) == false) {
|
||||
FURI_LOG_E(TAG, "Counter write failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
U2F->p_curve = uECC_secp256r1();
|
||||
uECC_set_rng(u2f_uecc_random);
|
||||
|
||||
U2F->ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void u2f_set_event_callback(U2fData* U2F, U2fEvtCallback callback, void* context) {
|
||||
furi_assert(U2F);
|
||||
furi_assert(callback);
|
||||
U2F->callback = callback;
|
||||
U2F->context = context;
|
||||
}
|
||||
|
||||
void u2f_confirm_user_present(U2fData* U2F) {
|
||||
U2F->user_present = true;
|
||||
}
|
||||
|
||||
static uint8_t u2f_der_encode_int(uint8_t* der, uint8_t* val, uint8_t val_len) {
|
||||
der[0] = 0x02; // Integer
|
||||
|
||||
uint8_t len = 2;
|
||||
// Omit leading zeros
|
||||
while(val[0] == 0 && val_len > 0) {
|
||||
++val;
|
||||
--val_len;
|
||||
}
|
||||
|
||||
// Check if integer is negative
|
||||
if(val[0] > 0x7f) der[len++] = 0;
|
||||
|
||||
memcpy(der + len, val, val_len);
|
||||
len += val_len;
|
||||
|
||||
der[1] = len - 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
static uint8_t u2f_der_encode_signature(uint8_t* der, uint8_t* sig) {
|
||||
der[0] = 0x30;
|
||||
|
||||
uint8_t len = 2;
|
||||
len += u2f_der_encode_int(der + len, sig, 32);
|
||||
len += u2f_der_encode_int(der + len, sig + 32, 32);
|
||||
|
||||
der[1] = len - 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {
|
||||
U2fRegisterReq* req = (U2fRegisterReq*)buf;
|
||||
U2fRegisterResp* resp = (U2fRegisterResp*)buf;
|
||||
U2fKeyHandle handle;
|
||||
uint8_t private[32];
|
||||
U2fPubKey pub_key;
|
||||
uint8_t hash[32];
|
||||
uint8_t signature[64];
|
||||
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyRegister, U2F->context);
|
||||
if(U2F->user_present == false) {
|
||||
memcpy(&buf[0], state_user_missing, 2);
|
||||
return 2;
|
||||
}
|
||||
U2F->user_present = false;
|
||||
|
||||
hmac_sha256_context hmac_ctx;
|
||||
sha256_context sha_ctx;
|
||||
|
||||
handle.len = 32 * 2;
|
||||
// Generate random nonce
|
||||
furi_hal_random_fill_buf(handle.nonce, 32);
|
||||
|
||||
// Generate private key
|
||||
hmac_sha256_init(&hmac_ctx, U2F->device_key);
|
||||
hmac_sha256_update(&hmac_ctx, req->app_id, 32);
|
||||
hmac_sha256_update(&hmac_ctx, handle.nonce, 32);
|
||||
hmac_sha256_finish(&hmac_ctx, U2F->device_key, private);
|
||||
|
||||
// Generate private key handle
|
||||
hmac_sha256_init(&hmac_ctx, U2F->device_key);
|
||||
hmac_sha256_update(&hmac_ctx, private, 32);
|
||||
hmac_sha256_update(&hmac_ctx, req->app_id, 32);
|
||||
hmac_sha256_finish(&hmac_ctx, U2F->device_key, handle.hash);
|
||||
|
||||
// Generate public key
|
||||
pub_key.format = 0x04; // Uncompressed point
|
||||
uECC_compute_public_key(private, pub_key.xy, U2F->p_curve);
|
||||
|
||||
// Generate signature
|
||||
uint8_t reserved_byte = 0;
|
||||
sha256_start(&sha_ctx);
|
||||
sha256_update(&sha_ctx, &reserved_byte, 1);
|
||||
sha256_update(&sha_ctx, req->app_id, 32);
|
||||
sha256_update(&sha_ctx, req->challenge, 32);
|
||||
sha256_update(&sha_ctx, handle.hash, handle.len);
|
||||
sha256_update(&sha_ctx, (uint8_t*)&pub_key, 65);
|
||||
sha256_finish(&sha_ctx, hash);
|
||||
|
||||
uECC_sign(U2F->cert_key, hash, 32, signature, U2F->p_curve);
|
||||
|
||||
// Encode response message
|
||||
resp->reserved = 0x05;
|
||||
memcpy(&(resp->pub_key), &pub_key, sizeof(U2fPubKey));
|
||||
memcpy(&(resp->key_handle), &handle, sizeof(U2fKeyHandle));
|
||||
uint32_t cert_len = u2f_data_cert_load(resp->cert);
|
||||
uint8_t signature_len = u2f_der_encode_signature(resp->cert + cert_len, signature);
|
||||
memcpy(resp->cert + cert_len + signature_len, state_no_error, 2);
|
||||
|
||||
return (sizeof(U2fRegisterResp) + cert_len + signature_len + 2);
|
||||
}
|
||||
|
||||
static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
||||
U2fAuthReq* req = (U2fAuthReq*)buf;
|
||||
U2fAuthResp* resp = (U2fAuthResp*)buf;
|
||||
uint8_t priv_key[32];
|
||||
uint8_t mac_control[32];
|
||||
hmac_sha256_context hmac_ctx;
|
||||
sha256_context sha_ctx;
|
||||
uint8_t flags = 0;
|
||||
uint8_t hash[32];
|
||||
uint8_t signature[64];
|
||||
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyAuth, U2F->context);
|
||||
if(U2F->user_present == true) {
|
||||
flags |= 1;
|
||||
} else {
|
||||
if(req->p1 == U2fEnforce) {
|
||||
memcpy(&buf[0], state_user_missing, 2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
U2F->user_present = false;
|
||||
|
||||
// Generate hash
|
||||
sha256_start(&sha_ctx);
|
||||
sha256_update(&sha_ctx, req->app_id, 32);
|
||||
sha256_update(&sha_ctx, &flags, 1);
|
||||
sha256_update(&sha_ctx, (uint8_t*)&(U2F->counter), 4);
|
||||
sha256_update(&sha_ctx, req->challenge, 32);
|
||||
sha256_finish(&sha_ctx, hash);
|
||||
|
||||
// Recover private key
|
||||
hmac_sha256_init(&hmac_ctx, U2F->device_key);
|
||||
hmac_sha256_update(&hmac_ctx, req->app_id, 32);
|
||||
hmac_sha256_update(&hmac_ctx, req->key_handle.nonce, 32);
|
||||
hmac_sha256_finish(&hmac_ctx, U2F->device_key, priv_key);
|
||||
|
||||
// Generate and verify private key handle
|
||||
hmac_sha256_init(&hmac_ctx, U2F->device_key);
|
||||
hmac_sha256_update(&hmac_ctx, priv_key, 32);
|
||||
hmac_sha256_update(&hmac_ctx, req->app_id, 32);
|
||||
hmac_sha256_finish(&hmac_ctx, U2F->device_key, mac_control);
|
||||
|
||||
if(memcmp(req->key_handle.hash, mac_control, 32) != 0) {
|
||||
FURI_LOG_W(TAG, "Wrong handle!");
|
||||
memcpy(&buf[0], state_wrong_data, 2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(req->p1 == U2fCheckOnly) { // Check-only: don't need to send full response
|
||||
memcpy(&buf[0], state_user_missing, 2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
uECC_sign(priv_key, hash, 32, signature, U2F->p_curve);
|
||||
|
||||
resp->user_present = flags;
|
||||
resp->counter = U2F->counter;
|
||||
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);
|
||||
U2F->counter++;
|
||||
u2f_data_cnt_write(U2F->counter);
|
||||
|
||||
return (sizeof(U2fAuthResp) + signature_len + 2);
|
||||
}
|
||||
|
||||
uint16_t u2f_msg_parse(U2fData* U2F, uint8_t* buf, uint16_t len) {
|
||||
furi_assert(U2F);
|
||||
if(!U2F->ready) return 0;
|
||||
if((buf[0] != 0x00) && (len < 5)) return 0;
|
||||
if(buf[1] == U2F_CMD_REGISTER) { // Register request
|
||||
return u2f_register(U2F, buf);
|
||||
|
||||
} else if(buf[1] == U2F_CMD_AUTHENTICATE) { // Authenticate request
|
||||
return u2f_authenticate(U2F, buf);
|
||||
|
||||
} else if(buf[1] == U2F_CMD_VERSION) { // Get U2F version string
|
||||
memcpy(&buf[0], ver_str, 6);
|
||||
memcpy(&buf[6], state_no_error, 2);
|
||||
return 8;
|
||||
} else {
|
||||
memcpy(&buf[0], state_not_supported, 2);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void u2f_wink(U2fData* U2F) {
|
||||
if(U2F->callback != NULL) U2F->callback(U2fNotifyWink, U2F->context);
|
||||
}
|
35
applications/u2f/u2f.h
Normal file
35
applications/u2f/u2f.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
typedef enum {
|
||||
U2fNotifyRegister,
|
||||
U2fNotifyAuth,
|
||||
U2fNotifyWink,
|
||||
} U2fNotifyEvent;
|
||||
|
||||
typedef struct U2fData U2fData;
|
||||
|
||||
typedef void (*U2fEvtCallback)(U2fNotifyEvent evt, void* context);
|
||||
|
||||
U2fData* u2f_alloc();
|
||||
|
||||
bool u2f_init(U2fData* instance);
|
||||
|
||||
void u2f_free(U2fData* instance);
|
||||
|
||||
void u2f_set_event_callback(U2fData* instance, U2fEvtCallback callback, void* context);
|
||||
|
||||
void u2f_confirm_user_present(U2fData* instance);
|
||||
|
||||
uint16_t u2f_msg_parse(U2fData* instance, uint8_t* buf, uint16_t len);
|
||||
|
||||
void u2f_wink(U2fData* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
77
applications/u2f/u2f_app.c
Normal file
77
applications/u2f/u2f_app.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include "u2f_app_i.h"
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
|
||||
static bool u2f_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
U2fApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool u2f_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
U2fApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void u2f_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
U2fApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
U2fApp* u2f_app_alloc() {
|
||||
U2fApp* app = furi_alloc(sizeof(U2fApp));
|
||||
|
||||
app->gui = furi_record_open("gui");
|
||||
app->notifications = furi_record_open("notification");
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&u2f_scene_handlers, app);
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, u2f_app_tick_event_callback, 500);
|
||||
|
||||
view_dispatcher_set_custom_event_callback(app->view_dispatcher, u2f_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, u2f_app_back_event_callback);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->u2f_view = u2f_view_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_view));
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, U2fAppViewMain);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void u2f_app_free(U2fApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, U2fAppViewMain);
|
||||
u2f_view_free(app->u2f_view);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Close records
|
||||
furi_record_close("gui");
|
||||
furi_record_close("notification");
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t u2f_app(void* p) {
|
||||
U2fApp* u2f_app = u2f_app_alloc();
|
||||
|
||||
view_dispatcher_run(u2f_app->view_dispatcher);
|
||||
|
||||
u2f_app_free(u2f_app);
|
||||
|
||||
return 0;
|
||||
}
|
11
applications/u2f/u2f_app.h
Normal file
11
applications/u2f/u2f_app.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct U2fApp U2fApp;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
45
applications/u2f/u2f_app_i.h
Normal file
45
applications/u2f/u2f_app_i.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "u2f_app.h"
|
||||
#include "scenes/u2f_scene.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <notification/notification-messages.h>
|
||||
#include <gui/modules/variable-item-list.h>
|
||||
#include "views/u2f_view.h"
|
||||
#include "u2f_hid.h"
|
||||
#include "u2f.h"
|
||||
|
||||
typedef enum {
|
||||
U2fCustomEventNone,
|
||||
|
||||
U2fCustomEventRegister,
|
||||
U2fCustomEventAuth,
|
||||
U2fCustomEventWink,
|
||||
|
||||
U2fCustomEventTimeout,
|
||||
|
||||
U2fCustomEventConfirm,
|
||||
|
||||
} GpioCustomEvent;
|
||||
|
||||
typedef enum {
|
||||
U2fAppViewMain,
|
||||
} U2fAppView;
|
||||
|
||||
struct U2fApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
NotificationApp* notifications;
|
||||
osTimerId_t timer;
|
||||
U2fHid* u2f_hid;
|
||||
U2fView* u2f_view;
|
||||
U2fData* u2f_instance;
|
||||
GpioCustomEvent event_cur;
|
||||
bool u2f_ready;
|
||||
};
|
382
applications/u2f/u2f_data.c
Normal file
382
applications/u2f/u2f_data.c
Normal file
@ -0,0 +1,382 @@
|
||||
#include <furi.h>
|
||||
#include "u2f_hid.h"
|
||||
#include <furi-hal.h>
|
||||
#include <storage/storage.h>
|
||||
#include <furi-hal-random.h>
|
||||
#include <flipper_file.h>
|
||||
|
||||
#define TAG "U2F"
|
||||
|
||||
#define U2F_DATA_FOLDER "/any/u2f/"
|
||||
#define U2F_CERT_FILE U2F_DATA_FOLDER "cert.der"
|
||||
#define U2F_CERT_KEY_FILE U2F_DATA_FOLDER "cert_key.u2f"
|
||||
#define U2F_KEY_FILE U2F_DATA_FOLDER "key.u2f"
|
||||
#define U2F_CNT_FILE U2F_DATA_FOLDER "cnt.u2f"
|
||||
|
||||
#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2
|
||||
#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE 11
|
||||
|
||||
#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_KEY_FILE_TYPE "Flipper U2F Certificate Key File"
|
||||
#define U2F_CERT_KEY_VERSION 1
|
||||
|
||||
#define U2F_DEVICE_KEY_FILE_TYPE "Flipper U2F Device Key File"
|
||||
#define U2F_DEVICE_KEY_VERSION 1
|
||||
|
||||
#define U2F_COUNTER_FILE_TYPE "Flipper U2F Counter File"
|
||||
#define U2F_COUNTER_VERSION 1
|
||||
|
||||
#define U2F_COUNTER_CONTROL_VAL 0xAA5500FF
|
||||
|
||||
typedef struct {
|
||||
uint32_t counter;
|
||||
uint8_t random_salt[24];
|
||||
uint32_t control;
|
||||
} __attribute__((packed)) U2fCounterData;
|
||||
|
||||
bool u2f_data_cert_check() {
|
||||
bool state = false;
|
||||
Storage* fs_api = furi_record_open("storage");
|
||||
File* file = storage_file_alloc(fs_api);
|
||||
uint8_t file_buf[8];
|
||||
|
||||
if(storage_file_open(file, U2F_CERT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
do {
|
||||
// Read header to check certificate size
|
||||
size_t file_size = storage_file_size(file);
|
||||
size_t len_cur = storage_file_read(file, file_buf, 4);
|
||||
if(len_cur != 4) break;
|
||||
|
||||
if(file_buf[0] != 0x30) {
|
||||
FURI_LOG_E(TAG, "Wrong certificate header");
|
||||
break;
|
||||
}
|
||||
|
||||
size_t temp_len = ((file_buf[2] << 8) | (file_buf[3])) + 4;
|
||||
if(temp_len != file_size) {
|
||||
FURI_LOG_E(TAG, "Wrong certificate length");
|
||||
break;
|
||||
}
|
||||
state = true;
|
||||
} while(0);
|
||||
}
|
||||
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
|
||||
furi_record_close("storage");
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
uint32_t u2f_data_cert_load(uint8_t* cert) {
|
||||
furi_assert(cert);
|
||||
|
||||
Storage* fs_api = furi_record_open("storage");
|
||||
File* file = storage_file_alloc(fs_api);
|
||||
uint32_t file_size = 0;
|
||||
uint32_t len_cur = 0;
|
||||
|
||||
if(storage_file_open(file, U2F_CERT_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
file_size = storage_file_size(file);
|
||||
len_cur = storage_file_read(file, cert, file_size);
|
||||
if(len_cur != file_size) len_cur = 0;
|
||||
}
|
||||
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
furi_record_close("storage");
|
||||
|
||||
return len_cur;
|
||||
}
|
||||
|
||||
bool u2f_data_cert_key_load(uint8_t* cert_key) {
|
||||
furi_assert(cert_key);
|
||||
|
||||
bool state = false;
|
||||
uint8_t iv[16];
|
||||
uint8_t key[48];
|
||||
uint32_t cert_type = 0;
|
||||
uint8_t key_slot = 0;
|
||||
uint32_t version = 0;
|
||||
|
||||
// Check if unique key exists in secure eclave and generate it if missing
|
||||
if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false;
|
||||
|
||||
string_t filetype;
|
||||
string_init(filetype);
|
||||
|
||||
Storage* storage = furi_record_open("storage");
|
||||
FlipperFile* flipper_file = flipper_file_alloc(storage);
|
||||
|
||||
if(flipper_file_open_existing(flipper_file, U2F_CERT_KEY_FILE)) {
|
||||
do {
|
||||
if(!flipper_file_read_header(flipper_file, filetype, &version)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect header");
|
||||
break;
|
||||
}
|
||||
|
||||
if(strcmp(string_get_cstr(filetype), U2F_CERT_KEY_FILE_TYPE) != 0 ||
|
||||
version != U2F_CERT_KEY_VERSION) {
|
||||
FURI_LOG_E(TAG, "Type or version mismatch");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_file_read_uint32(flipper_file, "Type", &cert_type, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing cert type");
|
||||
break;
|
||||
}
|
||||
|
||||
if(cert_type == U2F_CERT_STOCK) {
|
||||
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 {
|
||||
FURI_LOG_E(TAG, "Unknown cert type");
|
||||
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(!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)) {
|
||||
memset(cert_key, 0, 32);
|
||||
FURI_LOG_E(TAG, "Decryption failed");
|
||||
break;
|
||||
}
|
||||
furi_hal_crypto_store_unload_key(key_slot);
|
||||
state = true;
|
||||
} while(0);
|
||||
}
|
||||
|
||||
flipper_file_close(flipper_file);
|
||||
flipper_file_free(flipper_file);
|
||||
furi_record_close("storage");
|
||||
string_clear(filetype);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
bool u2f_data_key_load(uint8_t* device_key) {
|
||||
furi_assert(device_key);
|
||||
|
||||
bool state = false;
|
||||
uint8_t iv[16];
|
||||
uint8_t key[48];
|
||||
uint32_t version = 0;
|
||||
|
||||
string_t filetype;
|
||||
string_init(filetype);
|
||||
|
||||
Storage* storage = furi_record_open("storage");
|
||||
FlipperFile* flipper_file = flipper_file_alloc(storage);
|
||||
|
||||
if(flipper_file_open_existing(flipper_file, U2F_KEY_FILE)) {
|
||||
do {
|
||||
if(!flipper_file_read_header(flipper_file, filetype, &version)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect header");
|
||||
break;
|
||||
}
|
||||
if(strcmp(string_get_cstr(filetype), U2F_DEVICE_KEY_FILE_TYPE) != 0 ||
|
||||
version != U2F_DEVICE_KEY_VERSION) {
|
||||
FURI_LOG_E(TAG, "Type or version mismatch");
|
||||
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(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {
|
||||
FURI_LOG_E(TAG, "Unable to load encryption key");
|
||||
break;
|
||||
}
|
||||
memset(device_key, 0, 32);
|
||||
if(!furi_hal_crypto_decrypt(key, device_key, 32)) {
|
||||
memset(device_key, 0, 32);
|
||||
FURI_LOG_E(TAG, "Decryption failed");
|
||||
break;
|
||||
}
|
||||
furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);
|
||||
state = true;
|
||||
} while(0);
|
||||
}
|
||||
flipper_file_close(flipper_file);
|
||||
flipper_file_free(flipper_file);
|
||||
furi_record_close("storage");
|
||||
string_clear(filetype);
|
||||
return state;
|
||||
}
|
||||
|
||||
bool u2f_data_key_generate(uint8_t* device_key) {
|
||||
furi_assert(device_key);
|
||||
|
||||
bool state = false;
|
||||
uint8_t iv[16];
|
||||
uint8_t key[32];
|
||||
uint8_t key_encrypted[48];
|
||||
|
||||
// Generate random IV and key
|
||||
furi_hal_random_fill_buf(iv, 16);
|
||||
furi_hal_random_fill_buf(key, 32);
|
||||
|
||||
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(key, key_encrypted, 32)) {
|
||||
FURI_LOG_E(TAG, "Encryption failed");
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
|
||||
if(flipper_file_open_always(flipper_file, U2F_KEY_FILE)) {
|
||||
do {
|
||||
if(!flipper_file_write_header_cstr(
|
||||
flipper_file, U2F_DEVICE_KEY_FILE_TYPE, U2F_DEVICE_KEY_VERSION))
|
||||
break;
|
||||
if(!flipper_file_write_hex(flipper_file, "IV", iv, 16)) break;
|
||||
if(!flipper_file_write_hex(flipper_file, "Data", key_encrypted, 48)) break;
|
||||
state = true;
|
||||
memcpy(device_key, key, 32);
|
||||
} while(0);
|
||||
}
|
||||
|
||||
flipper_file_close(flipper_file);
|
||||
flipper_file_free(flipper_file);
|
||||
furi_record_close("storage");
|
||||
string_clear(filetype);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
bool u2f_data_cnt_read(uint32_t* cnt_val) {
|
||||
furi_assert(cnt_val);
|
||||
|
||||
bool state = false;
|
||||
uint8_t iv[16];
|
||||
U2fCounterData cnt;
|
||||
uint8_t cnt_encr[48];
|
||||
uint32_t version = 0;
|
||||
|
||||
string_t filetype;
|
||||
string_init(filetype);
|
||||
|
||||
Storage* storage = furi_record_open("storage");
|
||||
FlipperFile* flipper_file = flipper_file_alloc(storage);
|
||||
|
||||
if(flipper_file_open_existing(flipper_file, U2F_CNT_FILE)) {
|
||||
do {
|
||||
if(!flipper_file_read_header(flipper_file, filetype, &version)) {
|
||||
FURI_LOG_E(TAG, "Missing or incorrect header");
|
||||
break;
|
||||
}
|
||||
if(strcmp(string_get_cstr(filetype), U2F_COUNTER_FILE_TYPE) != 0 ||
|
||||
version != U2F_COUNTER_VERSION) {
|
||||
FURI_LOG_E(TAG, "Type or version mismatch");
|
||||
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", cnt_encr, 48)) {
|
||||
FURI_LOG_E(TAG, "Missing data");
|
||||
break;
|
||||
}
|
||||
if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) {
|
||||
FURI_LOG_E(TAG, "Unable to load encryption key");
|
||||
break;
|
||||
}
|
||||
memset(&cnt, 0, 32);
|
||||
if(!furi_hal_crypto_decrypt(cnt_encr, (uint8_t*)&cnt, 32)) {
|
||||
memset(&cnt, 0, 32);
|
||||
FURI_LOG_E(TAG, "Decryption failed");
|
||||
break;
|
||||
}
|
||||
furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE);
|
||||
if(cnt.control == U2F_COUNTER_CONTROL_VAL) {
|
||||
*cnt_val = cnt.counter;
|
||||
state = true;
|
||||
}
|
||||
} while(0);
|
||||
}
|
||||
flipper_file_close(flipper_file);
|
||||
flipper_file_free(flipper_file);
|
||||
furi_record_close("storage");
|
||||
string_clear(filetype);
|
||||
return state;
|
||||
}
|
||||
|
||||
bool u2f_data_cnt_write(uint32_t cnt_val) {
|
||||
bool state = false;
|
||||
uint8_t iv[16];
|
||||
U2fCounterData cnt;
|
||||
uint8_t cnt_encr[48];
|
||||
|
||||
// Generate random IV and key
|
||||
furi_hal_random_fill_buf(iv, 16);
|
||||
furi_hal_random_fill_buf(cnt.random_salt, 24);
|
||||
cnt.control = U2F_COUNTER_CONTROL_VAL;
|
||||
cnt.counter = cnt_val;
|
||||
|
||||
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((uint8_t*)&cnt, cnt_encr, 32)) {
|
||||
FURI_LOG_E(TAG, "Encryption failed");
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
|
||||
if(flipper_file_open_always(flipper_file, U2F_CNT_FILE)) {
|
||||
do {
|
||||
if(!flipper_file_write_header_cstr(
|
||||
flipper_file, U2F_COUNTER_FILE_TYPE, U2F_COUNTER_VERSION))
|
||||
break;
|
||||
if(!flipper_file_write_hex(flipper_file, "IV", iv, 16)) break;
|
||||
if(!flipper_file_write_hex(flipper_file, "Data", cnt_encr, 48)) break;
|
||||
state = true;
|
||||
} while(0);
|
||||
}
|
||||
|
||||
flipper_file_close(flipper_file);
|
||||
flipper_file_free(flipper_file);
|
||||
furi_record_close("storage");
|
||||
string_clear(filetype);
|
||||
|
||||
return state;
|
||||
}
|
25
applications/u2f/u2f_data.h
Normal file
25
applications/u2f/u2f_data.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
bool u2f_data_cert_check();
|
||||
|
||||
uint32_t u2f_data_cert_load(uint8_t* cert);
|
||||
|
||||
bool u2f_data_cert_key_load(uint8_t* cert_key);
|
||||
|
||||
bool u2f_data_key_load(uint8_t* device_key);
|
||||
|
||||
bool u2f_data_key_generate(uint8_t* device_key);
|
||||
|
||||
bool u2f_data_cnt_read(uint32_t* cnt);
|
||||
|
||||
bool u2f_data_cnt_write(uint32_t cnt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
293
applications/u2f/u2f_hid.c
Normal file
293
applications/u2f/u2f_hid.c
Normal file
@ -0,0 +1,293 @@
|
||||
#include <furi.h>
|
||||
#include "u2f_hid.h"
|
||||
#include "u2f.h"
|
||||
#include <furi-hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <furi-hal-usb-hid-u2f.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#include <furi-hal-console.h>
|
||||
|
||||
#define TAG "U2FHID"
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
#define U2F_HID_MAX_PAYLOAD_LEN ((HID_U2F_PACKET_LEN - 7) + 128 * (HID_U2F_PACKET_LEN - 5))
|
||||
|
||||
#define U2F_HID_TYPE_MASK 0x80 // Frame type mask
|
||||
#define U2F_HID_TYPE_INIT 0x80 // Initial frame identifier
|
||||
#define U2F_HID_TYPE_CONT 0x00 // Continuation frame identifier
|
||||
|
||||
#define U2F_HID_PING (U2F_HID_TYPE_INIT | 0x01) // Echo data through local processor only
|
||||
#define U2F_HID_MSG (U2F_HID_TYPE_INIT | 0x03) // Send U2F message frame
|
||||
#define U2F_HID_LOCK (U2F_HID_TYPE_INIT | 0x04) // Send lock channel command
|
||||
#define U2F_HID_INIT (U2F_HID_TYPE_INIT | 0x06) // Channel initialization
|
||||
#define U2F_HID_WINK (U2F_HID_TYPE_INIT | 0x08) // Send device identification wink
|
||||
#define U2F_HID_ERROR (U2F_HID_TYPE_INIT | 0x3f) // Error response
|
||||
|
||||
#define U2F_HID_ERR_NONE 0x00 // No error
|
||||
#define U2F_HID_ERR_INVALID_CMD 0x01 // Invalid command
|
||||
#define U2F_HID_ERR_INVALID_PAR 0x02 // Invalid parameter
|
||||
#define U2F_HID_ERR_INVALID_LEN 0x03 // Invalid message length
|
||||
#define U2F_HID_ERR_INVALID_SEQ 0x04 // Invalid message sequencing
|
||||
#define U2F_HID_ERR_MSG_TIMEOUT 0x05 // Message has timed out
|
||||
#define U2F_HID_ERR_CHANNEL_BUSY 0x06 // Channel busy
|
||||
#define U2F_HID_ERR_LOCK_REQUIRED 0x0a // Command requires channel lock
|
||||
#define U2F_HID_ERR_SYNC_FAIL 0x0b // SYNC command failed
|
||||
#define U2F_HID_ERR_OTHER 0x7f // Other unspecified error
|
||||
|
||||
#define U2F_HID_BROADCAST_CID 0xFFFFFFFF
|
||||
|
||||
typedef enum {
|
||||
WorkerEvtReserved = (1 << 0),
|
||||
WorkerEvtStop = (1 << 1),
|
||||
WorkerEvtConnect = (1 << 2),
|
||||
WorkerEvtDisconnect = (1 << 3),
|
||||
WorkerEvtRequest = (1 << 4),
|
||||
WorkerEvtUnlock = (1 << 5),
|
||||
} WorkerEvtFlags;
|
||||
|
||||
struct U2fHid_packet {
|
||||
uint32_t cid;
|
||||
uint16_t len;
|
||||
uint8_t cmd;
|
||||
uint8_t payload[U2F_HID_MAX_PAYLOAD_LEN];
|
||||
};
|
||||
|
||||
struct U2fHid {
|
||||
FuriThread* thread;
|
||||
osTimerId_t lock_timer;
|
||||
struct U2fHid_packet packet;
|
||||
uint8_t seq_id_last;
|
||||
uint16_t req_buf_ptr;
|
||||
uint32_t req_len_left;
|
||||
uint32_t lock_cid;
|
||||
bool lock;
|
||||
U2fData* u2f_instance;
|
||||
};
|
||||
|
||||
static void u2f_hid_event_callback(HidU2fEvent ev, void* context) {
|
||||
furi_assert(context);
|
||||
U2fHid* u2f_hid = context;
|
||||
|
||||
if(ev == HidU2fDisconnected)
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(u2f_hid->thread), WorkerEvtDisconnect);
|
||||
else if(ev == HidU2fConnected)
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(u2f_hid->thread), WorkerEvtConnect);
|
||||
else if(ev == HidU2fRequest)
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(u2f_hid->thread), WorkerEvtRequest);
|
||||
}
|
||||
|
||||
static void u2f_hid_lock_timeout_callback(void* context) {
|
||||
furi_assert(context);
|
||||
U2fHid* u2f_hid = context;
|
||||
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(u2f_hid->thread), WorkerEvtUnlock);
|
||||
}
|
||||
|
||||
static void u2f_hid_send_response(U2fHid* u2f_hid) {
|
||||
uint8_t packet_buf[HID_U2F_PACKET_LEN];
|
||||
uint16_t len_remain = u2f_hid->packet.len;
|
||||
uint8_t len_cur = 0;
|
||||
uint8_t seq_cnt = 0;
|
||||
uint16_t data_ptr = 0;
|
||||
|
||||
memset(packet_buf, 0, HID_U2F_PACKET_LEN);
|
||||
memcpy(packet_buf, &(u2f_hid->packet.cid), 4);
|
||||
|
||||
// Init packet
|
||||
packet_buf[4] = u2f_hid->packet.cmd;
|
||||
packet_buf[5] = u2f_hid->packet.len >> 8;
|
||||
packet_buf[6] = (u2f_hid->packet.len & 0xFF);
|
||||
len_cur = (len_remain < (HID_U2F_PACKET_LEN - 7)) ? (len_remain) : (HID_U2F_PACKET_LEN - 7);
|
||||
if(len_cur > 0) memcpy(&packet_buf[7], u2f_hid->packet.payload, len_cur);
|
||||
furi_hal_hid_u2f_send_response(packet_buf, HID_U2F_PACKET_LEN);
|
||||
data_ptr = len_cur;
|
||||
len_remain -= len_cur;
|
||||
|
||||
// Continuation packets
|
||||
while(len_remain > 0) {
|
||||
memset(&packet_buf[4], 0, HID_U2F_PACKET_LEN - 4);
|
||||
packet_buf[4] = seq_cnt;
|
||||
len_cur = (len_remain < (HID_U2F_PACKET_LEN - 5)) ? (len_remain) :
|
||||
(HID_U2F_PACKET_LEN - 5);
|
||||
memcpy(&packet_buf[5], &(u2f_hid->packet.payload[data_ptr]), len_cur);
|
||||
furi_hal_hid_u2f_send_response(packet_buf, HID_U2F_PACKET_LEN);
|
||||
seq_cnt++;
|
||||
len_remain -= len_cur;
|
||||
data_ptr += len_cur;
|
||||
}
|
||||
}
|
||||
|
||||
static void u2f_hid_send_error(U2fHid* u2f_hid, uint8_t error) {
|
||||
u2f_hid->packet.len = 1;
|
||||
u2f_hid->packet.cmd = U2F_HID_ERROR;
|
||||
u2f_hid->packet.payload[0] = error;
|
||||
u2f_hid_send_response(u2f_hid);
|
||||
}
|
||||
|
||||
static bool u2f_hid_parse_request(U2fHid* u2f_hid) {
|
||||
FURI_LOG_I(
|
||||
WORKER_TAG,
|
||||
"Req cid=%lX cmd=%x len=%u",
|
||||
u2f_hid->packet.cid,
|
||||
u2f_hid->packet.cmd,
|
||||
u2f_hid->packet.len);
|
||||
|
||||
if(u2f_hid->packet.cmd == U2F_HID_PING) { // PING - echo request back
|
||||
u2f_hid_send_response(u2f_hid);
|
||||
|
||||
} else if(u2f_hid->packet.cmd == U2F_HID_MSG) { // MSG - U2F message
|
||||
if((u2f_hid->lock == true) && (u2f_hid->packet.cid != u2f_hid->lock_cid)) return false;
|
||||
uint16_t resp_len =
|
||||
u2f_msg_parse(u2f_hid->u2f_instance, u2f_hid->packet.payload, u2f_hid->packet.len);
|
||||
if(resp_len > 0) {
|
||||
u2f_hid->packet.len = resp_len;
|
||||
u2f_hid_send_response(u2f_hid);
|
||||
} else
|
||||
return false;
|
||||
|
||||
} else if(u2f_hid->packet.cmd == U2F_HID_LOCK) { // LOCK - lock all channels except current
|
||||
if(u2f_hid->packet.len != 1) return false;
|
||||
uint8_t lock_timeout = u2f_hid->packet.payload[0];
|
||||
if(lock_timeout == 0) { // Lock off
|
||||
u2f_hid->lock = false;
|
||||
u2f_hid->lock_cid = 0;
|
||||
} else { // Lock on
|
||||
u2f_hid->lock = true;
|
||||
u2f_hid->lock_cid = u2f_hid->packet.cid;
|
||||
osTimerStart(u2f_hid->lock_timer, lock_timeout * 1000);
|
||||
}
|
||||
|
||||
} else if(u2f_hid->packet.cmd == U2F_HID_INIT) { // INIT - channel initialization request
|
||||
if((u2f_hid->packet.len != 8) || (u2f_hid->packet.cid != U2F_HID_BROADCAST_CID) ||
|
||||
(u2f_hid->lock == true))
|
||||
return false;
|
||||
u2f_hid->packet.len = 17;
|
||||
uint32_t random_cid = furi_hal_random_get();
|
||||
memcpy(&(u2f_hid->packet.payload[8]), &random_cid, 4);
|
||||
u2f_hid->packet.payload[12] = 2; // Protocol version
|
||||
u2f_hid->packet.payload[13] = 1; // Device version major
|
||||
u2f_hid->packet.payload[14] = 0; // Device version minor
|
||||
u2f_hid->packet.payload[15] = 1; // Device build version
|
||||
u2f_hid->packet.payload[16] = 1; // Capabilities: wink
|
||||
u2f_hid_send_response(u2f_hid);
|
||||
|
||||
} else if(u2f_hid->packet.cmd == U2F_HID_WINK) { // WINK - notify user
|
||||
if(u2f_hid->packet.len != 0) return false;
|
||||
u2f_wink(u2f_hid->u2f_instance);
|
||||
u2f_hid->packet.len = 0;
|
||||
u2f_hid_send_response(u2f_hid);
|
||||
} else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
UsbInterface* usb_mode_prev = furi_hal_usb_get_config();
|
||||
furi_hal_usb_set_config(&usb_hid_u2f);
|
||||
|
||||
u2f_hid->lock_timer = osTimerNew(u2f_hid_lock_timeout_callback, osTimerOnce, u2f_hid, NULL);
|
||||
|
||||
furi_hal_hid_u2f_set_callback(u2f_hid_event_callback, u2f_hid);
|
||||
|
||||
while(1) {
|
||||
uint32_t flags = osThreadFlagsWait(
|
||||
WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest,
|
||||
osFlagsWaitAny,
|
||||
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 & WorkerEvtRequest) {
|
||||
uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf);
|
||||
if(len_cur > 0) {
|
||||
if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) {
|
||||
// Init packet
|
||||
u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]);
|
||||
if(u2f_hid->packet.len > (len_cur - 7)) {
|
||||
u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7);
|
||||
len_cur = len_cur - 7;
|
||||
} else {
|
||||
u2f_hid->req_len_left = 0;
|
||||
len_cur = u2f_hid->packet.len;
|
||||
}
|
||||
memcpy(&(u2f_hid->packet.cid), packet_buf, 4);
|
||||
u2f_hid->packet.cmd = packet_buf[4];
|
||||
u2f_hid->seq_id_last = 0;
|
||||
u2f_hid->req_buf_ptr = len_cur;
|
||||
if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur);
|
||||
} else {
|
||||
// Continuation packet
|
||||
if(u2f_hid->req_len_left > 0) {
|
||||
uint32_t cid_temp = 0;
|
||||
memcpy(&cid_temp, packet_buf, 4);
|
||||
uint8_t seq_temp = packet_buf[4];
|
||||
if((cid_temp == u2f_hid->packet.cid) &&
|
||||
(seq_temp == u2f_hid->seq_id_last)) {
|
||||
if(u2f_hid->req_len_left > (len_cur - 5)) {
|
||||
len_cur = len_cur - 5;
|
||||
u2f_hid->req_len_left -= len_cur;
|
||||
} else {
|
||||
len_cur = u2f_hid->req_len_left;
|
||||
u2f_hid->req_len_left = 0;
|
||||
}
|
||||
memcpy(
|
||||
&(u2f_hid->packet.payload[u2f_hid->req_buf_ptr]),
|
||||
&packet_buf[5],
|
||||
len_cur);
|
||||
u2f_hid->req_buf_ptr += len_cur;
|
||||
u2f_hid->seq_id_last++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(u2f_hid->req_len_left == 0) {
|
||||
if(u2f_hid_parse_request(u2f_hid) == false) {
|
||||
u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(flags & WorkerEvtUnlock) {
|
||||
u2f_hid->lock = false;
|
||||
u2f_hid->lock_cid = 0;
|
||||
}
|
||||
}
|
||||
osTimerStop(u2f_hid->lock_timer);
|
||||
osTimerDelete(u2f_hid->lock_timer);
|
||||
|
||||
furi_hal_hid_u2f_set_callback(NULL, NULL);
|
||||
furi_hal_usb_set_config(usb_mode_prev);
|
||||
FURI_LOG_I(WORKER_TAG, "End");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U2fHid* u2f_hid_start(U2fData* u2f_inst) {
|
||||
U2fHid* u2f_hid = furi_alloc(sizeof(U2fHid));
|
||||
|
||||
u2f_hid->u2f_instance = u2f_inst;
|
||||
|
||||
u2f_hid->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(u2f_hid->thread, "U2fHidWorker");
|
||||
furi_thread_set_stack_size(u2f_hid->thread, 2048);
|
||||
furi_thread_set_context(u2f_hid->thread, u2f_hid);
|
||||
furi_thread_set_callback(u2f_hid->thread, u2f_hid_worker);
|
||||
furi_thread_start(u2f_hid->thread);
|
||||
return u2f_hid;
|
||||
}
|
||||
|
||||
void u2f_hid_stop(U2fHid* u2f_hid) {
|
||||
furi_assert(u2f_hid);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(u2f_hid->thread), WorkerEvtStop);
|
||||
furi_thread_join(u2f_hid->thread);
|
||||
furi_thread_free(u2f_hid->thread);
|
||||
free(u2f_hid);
|
||||
}
|
18
applications/u2f/u2f_hid.h
Normal file
18
applications/u2f/u2f_hid.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <furi.h>
|
||||
#include "u2f.h"
|
||||
|
||||
typedef struct U2fHid U2fHid;
|
||||
|
||||
U2fHid* u2f_hid_start(U2fData* u2f_inst);
|
||||
|
||||
void u2f_hid_stop(U2fHid* u2f_hid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
91
applications/u2f/views/u2f_view.c
Normal file
91
applications/u2f/views/u2f_view.c
Normal file
@ -0,0 +1,91 @@
|
||||
#include "u2f_view.h"
|
||||
#include <gui/elements.h>
|
||||
|
||||
struct U2fView {
|
||||
View* view;
|
||||
U2fOkCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
U2fViewMsg display_msg;
|
||||
} U2fModel;
|
||||
|
||||
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");
|
||||
|
||||
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");
|
||||
} 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");
|
||||
} else if(model->display_msg == U2fMsgError) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, "U2F data missing");
|
||||
}
|
||||
}
|
||||
|
||||
static bool u2f_view_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
U2fView* u2f = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
if(event->key == InputKeyOk) {
|
||||
consumed = true;
|
||||
if(u2f->callback != NULL) u2f->callback(InputTypeShort, u2f->context);
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
U2fView* u2f_view_alloc() {
|
||||
U2fView* u2f = furi_alloc(sizeof(U2fView));
|
||||
|
||||
u2f->view = view_alloc();
|
||||
view_allocate_model(u2f->view, ViewModelTypeLocking, sizeof(U2fModel));
|
||||
view_set_context(u2f->view, u2f);
|
||||
view_set_draw_callback(u2f->view, u2f_view_draw_callback);
|
||||
view_set_input_callback(u2f->view, u2f_view_input_callback);
|
||||
|
||||
return u2f;
|
||||
}
|
||||
|
||||
void u2f_view_free(U2fView* u2f) {
|
||||
furi_assert(u2f);
|
||||
view_free(u2f->view);
|
||||
free(u2f);
|
||||
}
|
||||
|
||||
View* u2f_view_get_view(U2fView* u2f) {
|
||||
furi_assert(u2f);
|
||||
return u2f->view;
|
||||
}
|
||||
|
||||
void u2f_view_set_ok_callback(U2fView* u2f, U2fOkCallback callback, void* context) {
|
||||
furi_assert(u2f);
|
||||
furi_assert(callback);
|
||||
with_view_model(
|
||||
u2f->view, (U2fModel * model) {
|
||||
u2f->callback = callback;
|
||||
u2f->context = context;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void u2f_view_set_state(U2fView* u2f, U2fViewMsg msg) {
|
||||
with_view_model(
|
||||
u2f->view, (U2fModel * model) {
|
||||
model->display_msg = msg;
|
||||
return false;
|
||||
});
|
||||
}
|
23
applications/u2f/views/u2f_view.h
Normal file
23
applications/u2f/views/u2f_view.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct U2fView U2fView;
|
||||
typedef void (*U2fOkCallback)(InputType type, void* context);
|
||||
|
||||
typedef enum {
|
||||
U2fMsgNone,
|
||||
U2fMsgRegister,
|
||||
U2fMsgAuth,
|
||||
U2fMsgError,
|
||||
} U2fViewMsg;
|
||||
|
||||
U2fView* u2f_view_alloc();
|
||||
|
||||
void u2f_view_free(U2fView* u2f);
|
||||
|
||||
View* u2f_view_get_view(U2fView* u2f);
|
||||
|
||||
void u2f_view_set_ok_callback(U2fView* u2f, U2fOkCallback callback, void* context);
|
||||
|
||||
void u2f_view_set_state(U2fView* u2f, U2fViewMsg msg);
|
BIN
assets/resources/u2f/cert.der
Normal file
BIN
assets/resources/u2f/cert.der
Normal file
Binary file not shown.
5
assets/resources/u2f/cert_key.u2f
Normal file
5
assets/resources/u2f/cert_key.u2f
Normal file
@ -0,0 +1,5 @@
|
||||
Filetype: Flipper U2F Certificate Key File
|
||||
Version: 1
|
||||
Type: 0
|
||||
IV: E1 56 CE 83 98 FA 59 0D 45 EC 1C EB 34 FC 08 C9
|
||||
Data: E1 8C C9 9A 98 F7 B9 50 1E 85 71 8F A4 CE 76 95 87 4F AC 8B 5E D0 1F 13 BA 3B 2E E7 98 73 54 64 58 0A 00 20 55 B8 00 08 58 0A 00 20 50 4E 01 20
|
@ -1,5 +1,6 @@
|
||||
#include <furi-hal-crypto.h>
|
||||
#include <furi-hal-bt.h>
|
||||
#include <furi-hal-random.h>
|
||||
#include <furi.h>
|
||||
#include <shci.h>
|
||||
|
||||
@ -7,10 +8,119 @@
|
||||
|
||||
CRYP_HandleTypeDef crypt;
|
||||
|
||||
#define ENCLAVE_FACTORY_KEY_SLOTS 10
|
||||
#define ENCLAVE_SIGNATURE_SIZE 16
|
||||
|
||||
static const uint8_t enclave_signature_iv[ENCLAVE_FACTORY_KEY_SLOTS][16] = {
|
||||
{0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a},
|
||||
{0x38, 0xe6, 0x6a, 0x90, 0x5e, 0x5b, 0x8a, 0xa6, 0x70, 0x30, 0x04, 0x72, 0xc2, 0x42, 0xea, 0xaf},
|
||||
{0x73, 0xd5, 0x8e, 0xfb, 0x0f, 0x4b, 0xa9, 0x79, 0x0f, 0xde, 0x0e, 0x53, 0x44, 0x7d, 0xaa, 0xfd},
|
||||
{0x3c, 0x9a, 0xf4, 0x43, 0x2b, 0xfe, 0xea, 0xae, 0x8c, 0xc6, 0xd1, 0x60, 0xd2, 0x96, 0x64, 0xa9},
|
||||
{0x10, 0xac, 0x7b, 0x63, 0x03, 0x7f, 0x43, 0x18, 0xec, 0x9d, 0x9c, 0xc4, 0x01, 0xdc, 0x35, 0xa7},
|
||||
{0x26, 0x21, 0x64, 0xe6, 0xd0, 0xf2, 0x47, 0x49, 0xdc, 0x36, 0xcd, 0x68, 0x0c, 0x91, 0x03, 0x44},
|
||||
{0x7a, 0xbd, 0xce, 0x9c, 0x24, 0x7a, 0x2a, 0xb1, 0x3c, 0x4f, 0x5a, 0x7d, 0x80, 0x3e, 0xfc, 0x0d},
|
||||
{0xcd, 0xdd, 0xd3, 0x02, 0x85, 0x65, 0x43, 0x83, 0xf9, 0xac, 0x75, 0x2f, 0x21, 0xef, 0x28, 0x6b},
|
||||
{0xab, 0x73, 0x70, 0xe8, 0xe2, 0x56, 0x0f, 0x58, 0xab, 0x29, 0xa5, 0xb1, 0x13, 0x47, 0x5e, 0xe8},
|
||||
{0x4f, 0x3c, 0x43, 0x77, 0xde, 0xed, 0x79, 0xa1, 0x8d, 0x4c, 0x1f, 0xfd, 0xdb, 0x96, 0x87, 0x2e},
|
||||
};
|
||||
|
||||
static const uint8_t enclave_signature_input[ENCLAVE_FACTORY_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = {
|
||||
{0x9f, 0x5c, 0xb1, 0x43, 0x17, 0x53, 0x18, 0x8c, 0x66, 0x3d, 0x39, 0x45, 0x90, 0x13, 0xa9, 0xde},
|
||||
{0xc5, 0x98, 0xe9, 0x17, 0xb8, 0x97, 0x9e, 0x03, 0x33, 0x14, 0x13, 0x8f, 0xce, 0x74, 0x0d, 0x54},
|
||||
{0x34, 0xba, 0x99, 0x59, 0x9f, 0x70, 0x67, 0xe9, 0x09, 0xee, 0x64, 0x0e, 0xb3, 0xba, 0xfb, 0x75},
|
||||
{0xdc, 0xfa, 0x6c, 0x9a, 0x6f, 0x0a, 0x3e, 0xdc, 0x42, 0xf6, 0xae, 0x0d, 0x3c, 0xf7, 0x83, 0xaf},
|
||||
{0xea, 0x2d, 0xe3, 0x1f, 0x02, 0x99, 0x1a, 0x7e, 0x6d, 0x93, 0x4c, 0xb5, 0x42, 0xf0, 0x7a, 0x9b},
|
||||
{0x53, 0x5e, 0x04, 0xa2, 0x49, 0xa0, 0x73, 0x49, 0x56, 0xb0, 0x88, 0x8c, 0x12, 0xa0, 0xe4, 0x18},
|
||||
{0x7d, 0xa7, 0xc5, 0x21, 0x7f, 0x12, 0x95, 0xdd, 0x4d, 0x77, 0x01, 0xfa, 0x71, 0x88, 0x2b, 0x7f},
|
||||
{0xdc, 0x9b, 0xc5, 0xa7, 0x6b, 0x84, 0x5c, 0x37, 0x7c, 0xec, 0x05, 0xa1, 0x9f, 0x91, 0x17, 0x3b},
|
||||
{0xea, 0xcf, 0xd9, 0x9b, 0x86, 0xcd, 0x2b, 0x43, 0x54, 0x45, 0x82, 0xc6, 0xfe, 0x73, 0x1a, 0x1a},
|
||||
{0x77, 0xb8, 0x1b, 0x90, 0xb4, 0xb7, 0x32, 0x76, 0x8f, 0x8a, 0x57, 0x06, 0xc7, 0xdd, 0x08, 0x90},
|
||||
};
|
||||
|
||||
static const uint8_t enclave_signature_expected[ENCLAVE_FACTORY_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = {
|
||||
{0xe9, 0x9a, 0xce, 0xe9, 0x4d, 0xe1, 0x7f, 0x55, 0xcb, 0x8a, 0xbf, 0xf2, 0x4d, 0x98, 0x27, 0x67},
|
||||
{0x34, 0x27, 0xa7, 0xea, 0xa8, 0x98, 0x66, 0x9b, 0xed, 0x43, 0xd3, 0x93, 0xb5, 0xa2, 0x87, 0x8e},
|
||||
{0x6c, 0xf3, 0x01, 0x78, 0x53, 0x1b, 0x11, 0x32, 0xf0, 0x27, 0x2f, 0xe3, 0x7d, 0xa6, 0xe2, 0xfd},
|
||||
{0xdf, 0x7f, 0x37, 0x65, 0x2f, 0xdb, 0x7c, 0xcf, 0x5b, 0xb6, 0xe4, 0x9c, 0x63, 0xc5, 0x0f, 0xe0},
|
||||
{0x9b, 0x5c, 0xee, 0x44, 0x0e, 0xd1, 0xcb, 0x5f, 0x28, 0x9f, 0x12, 0x17, 0x59, 0x64, 0x40, 0xbb},
|
||||
{0x94, 0xc2, 0x09, 0x98, 0x62, 0xa7, 0x2b, 0x93, 0xed, 0x36, 0x1f, 0x10, 0xbc, 0x26, 0xbd, 0x41},
|
||||
{0x4d, 0xb2, 0x2b, 0xc5, 0x96, 0x47, 0x61, 0xf4, 0x16, 0xe0, 0x81, 0xc3, 0x8e, 0xb9, 0x9c, 0x9b},
|
||||
{0xc3, 0x6b, 0x83, 0x55, 0x90, 0x38, 0x0f, 0xea, 0xd1, 0x65, 0xbf, 0x32, 0x4f, 0x8e, 0x62, 0x5b},
|
||||
{0x8d, 0x5e, 0x27, 0xbc, 0x14, 0x4f, 0x08, 0xa8, 0x2b, 0x14, 0x89, 0x5e, 0xdf, 0x77, 0x04, 0x31},
|
||||
{0xc9, 0xf7, 0x03, 0xf1, 0x6c, 0x65, 0xad, 0x49, 0x74, 0xbe, 0x00, 0x54, 0xfd, 0xa6, 0x9c, 0x32},
|
||||
};
|
||||
|
||||
void furi_hal_crypto_init() {
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
}
|
||||
|
||||
static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end_slot) {
|
||||
FuriHalCryptoKey key;
|
||||
uint8_t key_data[32];
|
||||
FURI_LOG_I(TAG, "Generating keys %u..%u", start_slot, end_slot);
|
||||
for (uint8_t slot = start_slot; slot <= end_slot; slot++) {
|
||||
key.type = FuriHalCryptoKeyTypeSimple;
|
||||
key.size = FuriHalCryptoKeySize256;
|
||||
key.data = key_data;
|
||||
furi_hal_random_fill_buf(key_data, 32);
|
||||
if (!furi_hal_crypto_store_add_key(&key, &slot)) {
|
||||
FURI_LOG_E(TAG, "Error writing key to slot %u", slot);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool furi_hal_crypto_verify_key(uint8_t key_slot) {
|
||||
uint8_t keys_nb = 0;
|
||||
uint8_t valid_keys_nb = 0;
|
||||
uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS;
|
||||
uint8_t empty_iv[16];
|
||||
furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb);
|
||||
if (key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key
|
||||
if (key_slot > keys_nb)
|
||||
return false;
|
||||
} else { // Unique key
|
||||
if (keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing
|
||||
return false;
|
||||
for (uint8_t i = key_slot; i > ENCLAVE_FACTORY_KEY_SLOTS; i--) {
|
||||
if(furi_hal_crypto_store_load_key(i, empty_iv)) {
|
||||
last_valid_slot = i;
|
||||
furi_hal_crypto_store_unload_key(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (last_valid_slot == key_slot)
|
||||
return true;
|
||||
else // Generate missing unique keys
|
||||
return furi_hal_crypto_generate_unique_keys(last_valid_slot+1, key_slot);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) {
|
||||
furi_assert(keys_nb);
|
||||
furi_assert(valid_keys_nb);
|
||||
uint8_t keys = 0;
|
||||
uint8_t keys_valid = 0;
|
||||
uint8_t buffer[ENCLAVE_SIGNATURE_SIZE];
|
||||
for(size_t key_slot = 0; key_slot < ENCLAVE_FACTORY_KEY_SLOTS; key_slot++) {
|
||||
if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) {
|
||||
keys++;
|
||||
if(furi_hal_crypto_encrypt(enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) {
|
||||
keys_valid += memcmp(buffer, enclave_signature_expected[key_slot], ENCLAVE_SIGNATURE_SIZE) == 0;
|
||||
}
|
||||
furi_hal_crypto_store_unload_key(key_slot + 1);
|
||||
}
|
||||
}
|
||||
*keys_nb = keys;
|
||||
*valid_keys_nb = keys_valid;
|
||||
if (*valid_keys_nb == ENCLAVE_FACTORY_KEY_SLOTS)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) {
|
||||
furi_assert(key);
|
||||
furi_assert(slot);
|
||||
|
@ -2,48 +2,6 @@
|
||||
#include <furi-hal.h>
|
||||
#include <shci.h>
|
||||
|
||||
#define ENCLAVE_SIGNATURE_KEY_SLOTS 10
|
||||
#define ENCLAVE_SIGNATURE_SIZE 16
|
||||
|
||||
static const uint8_t enclave_signature_iv[ENCLAVE_SIGNATURE_KEY_SLOTS][16] = {
|
||||
{0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a},
|
||||
{0x38, 0xe6, 0x6a, 0x90, 0x5e, 0x5b, 0x8a, 0xa6, 0x70, 0x30, 0x04, 0x72, 0xc2, 0x42, 0xea, 0xaf},
|
||||
{0x73, 0xd5, 0x8e, 0xfb, 0x0f, 0x4b, 0xa9, 0x79, 0x0f, 0xde, 0x0e, 0x53, 0x44, 0x7d, 0xaa, 0xfd},
|
||||
{0x3c, 0x9a, 0xf4, 0x43, 0x2b, 0xfe, 0xea, 0xae, 0x8c, 0xc6, 0xd1, 0x60, 0xd2, 0x96, 0x64, 0xa9},
|
||||
{0x10, 0xac, 0x7b, 0x63, 0x03, 0x7f, 0x43, 0x18, 0xec, 0x9d, 0x9c, 0xc4, 0x01, 0xdc, 0x35, 0xa7},
|
||||
{0x26, 0x21, 0x64, 0xe6, 0xd0, 0xf2, 0x47, 0x49, 0xdc, 0x36, 0xcd, 0x68, 0x0c, 0x91, 0x03, 0x44},
|
||||
{0x7a, 0xbd, 0xce, 0x9c, 0x24, 0x7a, 0x2a, 0xb1, 0x3c, 0x4f, 0x5a, 0x7d, 0x80, 0x3e, 0xfc, 0x0d},
|
||||
{0xcd, 0xdd, 0xd3, 0x02, 0x85, 0x65, 0x43, 0x83, 0xf9, 0xac, 0x75, 0x2f, 0x21, 0xef, 0x28, 0x6b},
|
||||
{0xab, 0x73, 0x70, 0xe8, 0xe2, 0x56, 0x0f, 0x58, 0xab, 0x29, 0xa5, 0xb1, 0x13, 0x47, 0x5e, 0xe8},
|
||||
{0x4f, 0x3c, 0x43, 0x77, 0xde, 0xed, 0x79, 0xa1, 0x8d, 0x4c, 0x1f, 0xfd, 0xdb, 0x96, 0x87, 0x2e},
|
||||
};
|
||||
|
||||
static const uint8_t enclave_signature_input[ENCLAVE_SIGNATURE_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = {
|
||||
{0x9f, 0x5c, 0xb1, 0x43, 0x17, 0x53, 0x18, 0x8c, 0x66, 0x3d, 0x39, 0x45, 0x90, 0x13, 0xa9, 0xde},
|
||||
{0xc5, 0x98, 0xe9, 0x17, 0xb8, 0x97, 0x9e, 0x03, 0x33, 0x14, 0x13, 0x8f, 0xce, 0x74, 0x0d, 0x54},
|
||||
{0x34, 0xba, 0x99, 0x59, 0x9f, 0x70, 0x67, 0xe9, 0x09, 0xee, 0x64, 0x0e, 0xb3, 0xba, 0xfb, 0x75},
|
||||
{0xdc, 0xfa, 0x6c, 0x9a, 0x6f, 0x0a, 0x3e, 0xdc, 0x42, 0xf6, 0xae, 0x0d, 0x3c, 0xf7, 0x83, 0xaf},
|
||||
{0xea, 0x2d, 0xe3, 0x1f, 0x02, 0x99, 0x1a, 0x7e, 0x6d, 0x93, 0x4c, 0xb5, 0x42, 0xf0, 0x7a, 0x9b},
|
||||
{0x53, 0x5e, 0x04, 0xa2, 0x49, 0xa0, 0x73, 0x49, 0x56, 0xb0, 0x88, 0x8c, 0x12, 0xa0, 0xe4, 0x18},
|
||||
{0x7d, 0xa7, 0xc5, 0x21, 0x7f, 0x12, 0x95, 0xdd, 0x4d, 0x77, 0x01, 0xfa, 0x71, 0x88, 0x2b, 0x7f},
|
||||
{0xdc, 0x9b, 0xc5, 0xa7, 0x6b, 0x84, 0x5c, 0x37, 0x7c, 0xec, 0x05, 0xa1, 0x9f, 0x91, 0x17, 0x3b},
|
||||
{0xea, 0xcf, 0xd9, 0x9b, 0x86, 0xcd, 0x2b, 0x43, 0x54, 0x45, 0x82, 0xc6, 0xfe, 0x73, 0x1a, 0x1a},
|
||||
{0x77, 0xb8, 0x1b, 0x90, 0xb4, 0xb7, 0x32, 0x76, 0x8f, 0x8a, 0x57, 0x06, 0xc7, 0xdd, 0x08, 0x90},
|
||||
};
|
||||
|
||||
static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = {
|
||||
{0xe9, 0x9a, 0xce, 0xe9, 0x4d, 0xe1, 0x7f, 0x55, 0xcb, 0x8a, 0xbf, 0xf2, 0x4d, 0x98, 0x27, 0x67},
|
||||
{0x34, 0x27, 0xa7, 0xea, 0xa8, 0x98, 0x66, 0x9b, 0xed, 0x43, 0xd3, 0x93, 0xb5, 0xa2, 0x87, 0x8e},
|
||||
{0x6c, 0xf3, 0x01, 0x78, 0x53, 0x1b, 0x11, 0x32, 0xf0, 0x27, 0x2f, 0xe3, 0x7d, 0xa6, 0xe2, 0xfd},
|
||||
{0xdf, 0x7f, 0x37, 0x65, 0x2f, 0xdb, 0x7c, 0xcf, 0x5b, 0xb6, 0xe4, 0x9c, 0x63, 0xc5, 0x0f, 0xe0},
|
||||
{0x9b, 0x5c, 0xee, 0x44, 0x0e, 0xd1, 0xcb, 0x5f, 0x28, 0x9f, 0x12, 0x17, 0x59, 0x64, 0x40, 0xbb},
|
||||
{0x94, 0xc2, 0x09, 0x98, 0x62, 0xa7, 0x2b, 0x93, 0xed, 0x36, 0x1f, 0x10, 0xbc, 0x26, 0xbd, 0x41},
|
||||
{0x4d, 0xb2, 0x2b, 0xc5, 0x96, 0x47, 0x61, 0xf4, 0x16, 0xe0, 0x81, 0xc3, 0x8e, 0xb9, 0x9c, 0x9b},
|
||||
{0xc3, 0x6b, 0x83, 0x55, 0x90, 0x38, 0x0f, 0xea, 0xd1, 0x65, 0xbf, 0x32, 0x4f, 0x8e, 0x62, 0x5b},
|
||||
{0x8d, 0x5e, 0x27, 0xbc, 0x14, 0x4f, 0x08, 0xa8, 0x2b, 0x14, 0x89, 0x5e, 0xdf, 0x77, 0x04, 0x31},
|
||||
{0xc9, 0xf7, 0x03, 0xf1, 0x6c, 0x65, 0xad, 0x49, 0x74, 0xbe, 0x00, 0x54, 0xfd, 0xa6, 0x9c, 0x32},
|
||||
};
|
||||
|
||||
void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) {
|
||||
string_t value;
|
||||
string_init(value);
|
||||
@ -164,23 +122,12 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) {
|
||||
out("radio_ble_mac", string_get_cstr(value), false, context);
|
||||
|
||||
// Signature verification
|
||||
uint8_t buffer[ENCLAVE_SIGNATURE_SIZE];
|
||||
size_t enclave_valid_keys = 0;
|
||||
for(size_t key_slot = 0; key_slot < ENCLAVE_SIGNATURE_KEY_SLOTS; key_slot++) {
|
||||
if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) {
|
||||
if(furi_hal_crypto_encrypt(
|
||||
enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) {
|
||||
enclave_valid_keys += memcmp(
|
||||
buffer,
|
||||
enclave_signature_expected[key_slot],
|
||||
ENCLAVE_SIGNATURE_SIZE) == 0;
|
||||
}
|
||||
furi_hal_crypto_store_unload_key(key_slot + 1);
|
||||
}
|
||||
}
|
||||
uint8_t enclave_keys = 0;
|
||||
uint8_t enclave_valid_keys = 0;
|
||||
bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys);
|
||||
string_printf(value, "%d", enclave_valid_keys);
|
||||
out("enclave_valid_keys", string_get_cstr(value), false, context);
|
||||
out("enclave_valid", (enclave_valid_keys == ENCLAVE_SIGNATURE_KEY_SLOTS) ? "true" : "false", true, context);
|
||||
out("enclave_valid", enclave_valid ? "true" : "false", true, context);
|
||||
} else {
|
||||
out("radio_alive", "false", true, context);
|
||||
}
|
||||
|
56
firmware/targets/f6/furi-hal/furi-hal-random.c
Normal file
56
firmware/targets/f6/furi-hal/furi-hal-random.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include "furi-hal-random.h"
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
|
||||
#include <stm32wbxx_ll_rng.h>
|
||||
#include <stm32wbxx_ll_hsem.h>
|
||||
|
||||
#include <hw_conf.h>
|
||||
|
||||
uint32_t furi_hal_random_get() {
|
||||
|
||||
while( LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID));
|
||||
LL_RNG_Enable(RNG);
|
||||
|
||||
while (!LL_RNG_IsActiveFlag_DRDY(RNG));
|
||||
|
||||
if ((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) {
|
||||
furi_crash("TRNG error");
|
||||
}
|
||||
|
||||
uint32_t random_val = LL_RNG_ReadRandData32(RNG);
|
||||
|
||||
LL_RNG_Disable(RNG);
|
||||
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0);
|
||||
|
||||
return random_val;
|
||||
}
|
||||
|
||||
void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len) {
|
||||
|
||||
while( LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID));
|
||||
LL_RNG_Enable(RNG);
|
||||
|
||||
for (uint32_t i = 0; i < len; i+= 4) {
|
||||
while (!LL_RNG_IsActiveFlag_DRDY(RNG));
|
||||
|
||||
if ((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) {
|
||||
furi_crash("TRNG error");
|
||||
}
|
||||
|
||||
uint32_t random_val = LL_RNG_ReadRandData32(RNG);
|
||||
|
||||
uint8_t len_cur = ((i+4) < len) ? (4) : (len-i);
|
||||
memcpy(&buf[i], &random_val, len_cur);
|
||||
}
|
||||
|
||||
LL_RNG_Disable(RNG);
|
||||
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0);
|
||||
}
|
||||
|
||||
void srand(unsigned seed) {
|
||||
}
|
||||
|
||||
int rand() {
|
||||
return (furi_hal_random_get() & RAND_MAX);
|
||||
}
|
309
firmware/targets/f6/furi-hal/furi-hal-usb-u2f.c
Normal file
309
firmware/targets/f6/furi-hal/furi-hal-usb-u2f.c
Normal file
@ -0,0 +1,309 @@
|
||||
#include "furi-hal-version.h"
|
||||
#include "furi-hal-usb_i.h"
|
||||
#include "furi-hal-usb-hid-u2f.h"
|
||||
#include "furi-hal-usb.h"
|
||||
#include <furi.h>
|
||||
#include "usb.h"
|
||||
#include "usb_hid.h"
|
||||
|
||||
#define HID_PAGE_FIDO 0xF1D0
|
||||
#define HID_FIDO_U2F 0x01
|
||||
#define HID_FIDO_INPUT 0x20
|
||||
#define HID_FIDO_OUTPUT 0x21
|
||||
|
||||
#define HID_EP_IN 0x81
|
||||
#define HID_EP_OUT 0x01
|
||||
|
||||
struct HidIadDescriptor {
|
||||
struct usb_iad_descriptor hid_iad;
|
||||
struct usb_interface_descriptor hid;
|
||||
struct usb_hid_descriptor hid_desc;
|
||||
struct usb_endpoint_descriptor hid_ep_in;
|
||||
struct usb_endpoint_descriptor hid_ep_out;
|
||||
};
|
||||
|
||||
struct HidConfigDescriptor {
|
||||
struct usb_config_descriptor config;
|
||||
struct HidIadDescriptor iad_0;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* HID report: FIDO U2F */
|
||||
static const uint8_t hid_u2f_report_desc[] = {
|
||||
HID_RI_USAGE_PAGE(16, HID_PAGE_FIDO),
|
||||
HID_USAGE(HID_FIDO_U2F),
|
||||
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||
HID_USAGE(HID_FIDO_INPUT),
|
||||
HID_LOGICAL_MINIMUM(0x00),
|
||||
HID_LOGICAL_MAXIMUM(0xFF),
|
||||
HID_REPORT_SIZE(8),
|
||||
HID_REPORT_COUNT(HID_U2F_PACKET_LEN),
|
||||
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||
HID_USAGE(HID_FIDO_OUTPUT),
|
||||
HID_LOGICAL_MINIMUM(0x00),
|
||||
HID_LOGICAL_MAXIMUM(0xFF),
|
||||
HID_REPORT_SIZE(8),
|
||||
HID_REPORT_COUNT(HID_U2F_PACKET_LEN),
|
||||
HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||
HID_END_COLLECTION,
|
||||
};
|
||||
|
||||
static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc.");
|
||||
static const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC("U2F Token test");
|
||||
static const struct usb_string_descriptor dev_serial_desc = USB_STRING_DESC("TODO: serial");
|
||||
|
||||
/* Device descriptor */
|
||||
static const struct usb_device_descriptor hid_u2f_device_desc = {
|
||||
.bLength = sizeof(struct usb_device_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_DEVICE,
|
||||
.bcdUSB = VERSION_BCD(2, 0, 0),
|
||||
.bDeviceClass = USB_CLASS_IAD,
|
||||
.bDeviceSubClass = USB_SUBCLASS_IAD,
|
||||
.bDeviceProtocol = USB_PROTO_IAD,
|
||||
.bMaxPacketSize0 = USB_EP0_SIZE,
|
||||
.idVendor = 0x0483,
|
||||
.idProduct = 0x5741,
|
||||
.bcdDevice = VERSION_BCD(1, 0, 0),
|
||||
.iManufacturer = UsbDevManuf,
|
||||
.iProduct = UsbDevProduct,
|
||||
.iSerialNumber = UsbDevSerial,
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
/* Device configuration descriptor */
|
||||
static const struct HidConfigDescriptor hid_u2f_cfg_desc = {
|
||||
.config =
|
||||
{
|
||||
.bLength = sizeof(struct usb_config_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_CONFIGURATION,
|
||||
.wTotalLength = sizeof(struct HidConfigDescriptor),
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = NO_DESCRIPTOR,
|
||||
.bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED,
|
||||
.bMaxPower = USB_CFG_POWER_MA(100),
|
||||
},
|
||||
.iad_0 =
|
||||
{
|
||||
.hid_iad =
|
||||
{
|
||||
.bLength = sizeof(struct usb_iad_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_INTERFASEASSOC,
|
||||
.bFirstInterface = 0,
|
||||
.bInterfaceCount = 1,
|
||||
.bFunctionClass = USB_CLASS_PER_INTERFACE,
|
||||
.bFunctionSubClass = USB_SUBCLASS_NONE,
|
||||
.bFunctionProtocol = USB_PROTO_NONE,
|
||||
.iFunction = NO_DESCRIPTOR,
|
||||
},
|
||||
.hid =
|
||||
{
|
||||
.bLength = sizeof(struct usb_interface_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT,
|
||||
.bInterfaceProtocol = USB_HID_PROTO_NONBOOT,
|
||||
.iInterface = NO_DESCRIPTOR,
|
||||
},
|
||||
.hid_desc =
|
||||
{
|
||||
.bLength = sizeof(struct usb_hid_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_HID,
|
||||
.bcdHID = VERSION_BCD(1, 0, 0),
|
||||
.bCountryCode = USB_HID_COUNTRY_NONE,
|
||||
.bNumDescriptors = 1,
|
||||
.bDescriptorType0 = USB_DTYPE_HID_REPORT,
|
||||
.wDescriptorLength0 = sizeof(hid_u2f_report_desc),
|
||||
},
|
||||
.hid_ep_in =
|
||||
{
|
||||
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_ENDPOINT,
|
||||
.bEndpointAddress = HID_EP_IN,
|
||||
.bmAttributes = USB_EPTYPE_INTERRUPT,
|
||||
.wMaxPacketSize = HID_U2F_PACKET_LEN,
|
||||
.bInterval = 5,
|
||||
},
|
||||
.hid_ep_out =
|
||||
{
|
||||
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_ENDPOINT,
|
||||
.bEndpointAddress = HID_EP_OUT,
|
||||
.bmAttributes = USB_EPTYPE_INTERRUPT,
|
||||
.wMaxPacketSize = HID_U2F_PACKET_LEN,
|
||||
.bInterval = 5,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static void hid_u2f_init(usbd_device* dev, UsbInterface* intf);
|
||||
static void hid_u2f_deinit(usbd_device* dev);
|
||||
static void hid_u2f_on_wakeup(usbd_device* dev);
|
||||
static void hid_u2f_on_suspend(usbd_device* dev);
|
||||
|
||||
//static bool hid_u2f_send_report(uint8_t report_id);
|
||||
static usbd_respond hid_u2f_ep_config(usbd_device* dev, uint8_t cfg);
|
||||
static usbd_respond
|
||||
hid_u2f_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
|
||||
static usbd_device* usb_dev;
|
||||
static osSemaphoreId_t hid_u2f_semaphore = NULL;
|
||||
static bool hid_u2f_connected = false;
|
||||
|
||||
static HidU2fCallback callback;
|
||||
static void* cb_ctx;
|
||||
|
||||
bool furi_hal_hid_u2f_is_connected() {
|
||||
return hid_u2f_connected;
|
||||
}
|
||||
|
||||
void furi_hal_hid_u2f_set_callback(HidU2fCallback cb, void* ctx) {
|
||||
if (callback != NULL) {
|
||||
if (hid_u2f_connected == true)
|
||||
callback(HidU2fDisconnected, cb_ctx);
|
||||
}
|
||||
|
||||
callback = cb;
|
||||
cb_ctx = ctx;
|
||||
|
||||
if (callback != NULL) {
|
||||
if (hid_u2f_connected == true)
|
||||
callback(HidU2fConnected, cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
UsbInterface usb_hid_u2f = {
|
||||
.init = hid_u2f_init,
|
||||
.deinit = hid_u2f_deinit,
|
||||
.wakeup = hid_u2f_on_wakeup,
|
||||
.suspend = hid_u2f_on_suspend,
|
||||
|
||||
.dev_descr = (struct usb_device_descriptor*)&hid_u2f_device_desc,
|
||||
|
||||
.str_manuf_descr = (void*)&dev_manuf_desc,
|
||||
.str_prod_descr = (void*)&dev_prod_desc,
|
||||
.str_serial_descr = (void*)&dev_serial_desc,
|
||||
|
||||
.cfg_descr = (void*)&hid_u2f_cfg_desc,
|
||||
};
|
||||
|
||||
static void hid_u2f_init(usbd_device* dev, UsbInterface* intf) {
|
||||
if(hid_u2f_semaphore == NULL) hid_u2f_semaphore = osSemaphoreNew(1, 1, NULL);
|
||||
usb_dev = dev;
|
||||
|
||||
usbd_reg_config(dev, hid_u2f_ep_config);
|
||||
usbd_reg_control(dev, hid_u2f_control);
|
||||
|
||||
usbd_connect(dev, true);
|
||||
}
|
||||
|
||||
static void hid_u2f_deinit(usbd_device* dev) {
|
||||
usbd_reg_config(dev, NULL);
|
||||
usbd_reg_control(dev, NULL);
|
||||
}
|
||||
|
||||
static void hid_u2f_on_wakeup(usbd_device* dev) {
|
||||
hid_u2f_connected = true;
|
||||
if (callback != NULL)
|
||||
callback(HidU2fConnected, cb_ctx);
|
||||
}
|
||||
|
||||
static void hid_u2f_on_suspend(usbd_device* dev) {
|
||||
if(hid_u2f_connected == true) {
|
||||
hid_u2f_connected = false;
|
||||
osSemaphoreRelease(hid_u2f_semaphore);
|
||||
if (callback != NULL)
|
||||
callback(HidU2fDisconnected, cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_hid_u2f_send_response(uint8_t* data, uint8_t len) {
|
||||
if ((hid_u2f_semaphore == NULL) || (hid_u2f_connected == false))
|
||||
return;
|
||||
furi_check(osSemaphoreAcquire(hid_u2f_semaphore, osWaitForever) == osOK);
|
||||
if (hid_u2f_connected == true) {
|
||||
usbd_ep_write(usb_dev, HID_EP_OUT, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t furi_hal_hid_u2f_get_request(uint8_t* data) {
|
||||
int32_t len = usbd_ep_read(usb_dev, HID_EP_IN, data, HID_U2F_PACKET_LEN);
|
||||
return ((len < 0) ? 0 : len);
|
||||
}
|
||||
|
||||
static void hid_u2f_rx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
|
||||
if (callback != NULL)
|
||||
callback(HidU2fRequest, cb_ctx);
|
||||
}
|
||||
|
||||
static void hid_u2f_tx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
|
||||
osSemaphoreRelease(hid_u2f_semaphore);
|
||||
}
|
||||
|
||||
static void hid_u2f_txrx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
|
||||
if (event == usbd_evt_eptx) {
|
||||
hid_u2f_tx_ep_callback(dev, event, ep);
|
||||
} else {
|
||||
hid_u2f_rx_ep_callback(dev, event, ep);
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure endpoints */
|
||||
static usbd_respond hid_u2f_ep_config(usbd_device* dev, uint8_t cfg) {
|
||||
switch(cfg) {
|
||||
case 0:
|
||||
/* deconfiguring device */
|
||||
usbd_ep_deconfig(dev, HID_EP_IN);
|
||||
usbd_ep_deconfig(dev, HID_EP_OUT);
|
||||
usbd_reg_endpoint(dev, HID_EP_IN, 0);
|
||||
usbd_reg_endpoint(dev, HID_EP_OUT, 0);
|
||||
return usbd_ack;
|
||||
case 1:
|
||||
/* configuring device */
|
||||
usbd_ep_config(dev, HID_EP_IN, USB_EPTYPE_INTERRUPT, HID_U2F_PACKET_LEN);
|
||||
usbd_ep_config(dev, HID_EP_OUT, USB_EPTYPE_INTERRUPT, HID_U2F_PACKET_LEN);
|
||||
usbd_reg_endpoint(dev, HID_EP_IN, hid_u2f_txrx_ep_callback);
|
||||
usbd_reg_endpoint(dev, HID_EP_OUT, hid_u2f_txrx_ep_callback);
|
||||
usbd_ep_write(dev, HID_U2F_PACKET_LEN, 0, 0);
|
||||
return usbd_ack;
|
||||
default:
|
||||
return usbd_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Control requests handler */
|
||||
static usbd_respond hid_u2f_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
|
||||
/* HID control requests */
|
||||
if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
|
||||
(USB_REQ_INTERFACE | USB_REQ_CLASS) &&
|
||||
req->wIndex == 0) {
|
||||
switch(req->bRequest) {
|
||||
case USB_HID_SETIDLE:
|
||||
return usbd_ack;
|
||||
case USB_HID_GETREPORT:
|
||||
// dev->status.data_ptr = &hid_u2f_report;
|
||||
// dev->status.data_count = sizeof(hid_u2f_report);
|
||||
return usbd_ack;
|
||||
default:
|
||||
return usbd_fail;
|
||||
}
|
||||
}
|
||||
if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
|
||||
(USB_REQ_INTERFACE | USB_REQ_STANDARD) &&
|
||||
req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) {
|
||||
switch(req->wValue >> 8) {
|
||||
case USB_DTYPE_HID:
|
||||
dev->status.data_ptr = (uint8_t*)&(hid_u2f_cfg_desc.iad_0.hid_desc);
|
||||
dev->status.data_count = sizeof(hid_u2f_cfg_desc.iad_0.hid_desc);
|
||||
return usbd_ack;
|
||||
case USB_DTYPE_HID_REPORT:
|
||||
dev->status.data_ptr = (uint8_t*)hid_u2f_report_desc;
|
||||
dev->status.data_count = sizeof(hid_u2f_report_desc);
|
||||
return usbd_ack;
|
||||
default:
|
||||
return usbd_fail;
|
||||
}
|
||||
}
|
||||
return usbd_fail;
|
||||
}
|
@ -71,7 +71,8 @@ C_SOURCES += \
|
||||
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_tim.c \
|
||||
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_usart.c \
|
||||
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_lpuart.c \
|
||||
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c
|
||||
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c \
|
||||
$(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_rng.c
|
||||
|
||||
# FreeRTOS
|
||||
CFLAGS += \
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <furi-hal-crypto.h>
|
||||
#include <furi-hal-bt.h>
|
||||
#include <furi-hal-random.h>
|
||||
#include <furi.h>
|
||||
#include <shci.h>
|
||||
|
||||
@ -7,10 +8,119 @@
|
||||
|
||||
CRYP_HandleTypeDef crypt;
|
||||
|
||||
#define ENCLAVE_FACTORY_KEY_SLOTS 10
|
||||
#define ENCLAVE_SIGNATURE_SIZE 16
|
||||
|
||||
static const uint8_t enclave_signature_iv[ENCLAVE_FACTORY_KEY_SLOTS][16] = {
|
||||
{0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a},
|
||||
{0x38, 0xe6, 0x6a, 0x90, 0x5e, 0x5b, 0x8a, 0xa6, 0x70, 0x30, 0x04, 0x72, 0xc2, 0x42, 0xea, 0xaf},
|
||||
{0x73, 0xd5, 0x8e, 0xfb, 0x0f, 0x4b, 0xa9, 0x79, 0x0f, 0xde, 0x0e, 0x53, 0x44, 0x7d, 0xaa, 0xfd},
|
||||
{0x3c, 0x9a, 0xf4, 0x43, 0x2b, 0xfe, 0xea, 0xae, 0x8c, 0xc6, 0xd1, 0x60, 0xd2, 0x96, 0x64, 0xa9},
|
||||
{0x10, 0xac, 0x7b, 0x63, 0x03, 0x7f, 0x43, 0x18, 0xec, 0x9d, 0x9c, 0xc4, 0x01, 0xdc, 0x35, 0xa7},
|
||||
{0x26, 0x21, 0x64, 0xe6, 0xd0, 0xf2, 0x47, 0x49, 0xdc, 0x36, 0xcd, 0x68, 0x0c, 0x91, 0x03, 0x44},
|
||||
{0x7a, 0xbd, 0xce, 0x9c, 0x24, 0x7a, 0x2a, 0xb1, 0x3c, 0x4f, 0x5a, 0x7d, 0x80, 0x3e, 0xfc, 0x0d},
|
||||
{0xcd, 0xdd, 0xd3, 0x02, 0x85, 0x65, 0x43, 0x83, 0xf9, 0xac, 0x75, 0x2f, 0x21, 0xef, 0x28, 0x6b},
|
||||
{0xab, 0x73, 0x70, 0xe8, 0xe2, 0x56, 0x0f, 0x58, 0xab, 0x29, 0xa5, 0xb1, 0x13, 0x47, 0x5e, 0xe8},
|
||||
{0x4f, 0x3c, 0x43, 0x77, 0xde, 0xed, 0x79, 0xa1, 0x8d, 0x4c, 0x1f, 0xfd, 0xdb, 0x96, 0x87, 0x2e},
|
||||
};
|
||||
|
||||
static const uint8_t enclave_signature_input[ENCLAVE_FACTORY_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = {
|
||||
{0x9f, 0x5c, 0xb1, 0x43, 0x17, 0x53, 0x18, 0x8c, 0x66, 0x3d, 0x39, 0x45, 0x90, 0x13, 0xa9, 0xde},
|
||||
{0xc5, 0x98, 0xe9, 0x17, 0xb8, 0x97, 0x9e, 0x03, 0x33, 0x14, 0x13, 0x8f, 0xce, 0x74, 0x0d, 0x54},
|
||||
{0x34, 0xba, 0x99, 0x59, 0x9f, 0x70, 0x67, 0xe9, 0x09, 0xee, 0x64, 0x0e, 0xb3, 0xba, 0xfb, 0x75},
|
||||
{0xdc, 0xfa, 0x6c, 0x9a, 0x6f, 0x0a, 0x3e, 0xdc, 0x42, 0xf6, 0xae, 0x0d, 0x3c, 0xf7, 0x83, 0xaf},
|
||||
{0xea, 0x2d, 0xe3, 0x1f, 0x02, 0x99, 0x1a, 0x7e, 0x6d, 0x93, 0x4c, 0xb5, 0x42, 0xf0, 0x7a, 0x9b},
|
||||
{0x53, 0x5e, 0x04, 0xa2, 0x49, 0xa0, 0x73, 0x49, 0x56, 0xb0, 0x88, 0x8c, 0x12, 0xa0, 0xe4, 0x18},
|
||||
{0x7d, 0xa7, 0xc5, 0x21, 0x7f, 0x12, 0x95, 0xdd, 0x4d, 0x77, 0x01, 0xfa, 0x71, 0x88, 0x2b, 0x7f},
|
||||
{0xdc, 0x9b, 0xc5, 0xa7, 0x6b, 0x84, 0x5c, 0x37, 0x7c, 0xec, 0x05, 0xa1, 0x9f, 0x91, 0x17, 0x3b},
|
||||
{0xea, 0xcf, 0xd9, 0x9b, 0x86, 0xcd, 0x2b, 0x43, 0x54, 0x45, 0x82, 0xc6, 0xfe, 0x73, 0x1a, 0x1a},
|
||||
{0x77, 0xb8, 0x1b, 0x90, 0xb4, 0xb7, 0x32, 0x76, 0x8f, 0x8a, 0x57, 0x06, 0xc7, 0xdd, 0x08, 0x90},
|
||||
};
|
||||
|
||||
static const uint8_t enclave_signature_expected[ENCLAVE_FACTORY_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = {
|
||||
{0xe9, 0x9a, 0xce, 0xe9, 0x4d, 0xe1, 0x7f, 0x55, 0xcb, 0x8a, 0xbf, 0xf2, 0x4d, 0x98, 0x27, 0x67},
|
||||
{0x34, 0x27, 0xa7, 0xea, 0xa8, 0x98, 0x66, 0x9b, 0xed, 0x43, 0xd3, 0x93, 0xb5, 0xa2, 0x87, 0x8e},
|
||||
{0x6c, 0xf3, 0x01, 0x78, 0x53, 0x1b, 0x11, 0x32, 0xf0, 0x27, 0x2f, 0xe3, 0x7d, 0xa6, 0xe2, 0xfd},
|
||||
{0xdf, 0x7f, 0x37, 0x65, 0x2f, 0xdb, 0x7c, 0xcf, 0x5b, 0xb6, 0xe4, 0x9c, 0x63, 0xc5, 0x0f, 0xe0},
|
||||
{0x9b, 0x5c, 0xee, 0x44, 0x0e, 0xd1, 0xcb, 0x5f, 0x28, 0x9f, 0x12, 0x17, 0x59, 0x64, 0x40, 0xbb},
|
||||
{0x94, 0xc2, 0x09, 0x98, 0x62, 0xa7, 0x2b, 0x93, 0xed, 0x36, 0x1f, 0x10, 0xbc, 0x26, 0xbd, 0x41},
|
||||
{0x4d, 0xb2, 0x2b, 0xc5, 0x96, 0x47, 0x61, 0xf4, 0x16, 0xe0, 0x81, 0xc3, 0x8e, 0xb9, 0x9c, 0x9b},
|
||||
{0xc3, 0x6b, 0x83, 0x55, 0x90, 0x38, 0x0f, 0xea, 0xd1, 0x65, 0xbf, 0x32, 0x4f, 0x8e, 0x62, 0x5b},
|
||||
{0x8d, 0x5e, 0x27, 0xbc, 0x14, 0x4f, 0x08, 0xa8, 0x2b, 0x14, 0x89, 0x5e, 0xdf, 0x77, 0x04, 0x31},
|
||||
{0xc9, 0xf7, 0x03, 0xf1, 0x6c, 0x65, 0xad, 0x49, 0x74, 0xbe, 0x00, 0x54, 0xfd, 0xa6, 0x9c, 0x32},
|
||||
};
|
||||
|
||||
void furi_hal_crypto_init() {
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
}
|
||||
|
||||
static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end_slot) {
|
||||
FuriHalCryptoKey key;
|
||||
uint8_t key_data[32];
|
||||
FURI_LOG_I(TAG, "Generating keys %u..%u", start_slot, end_slot);
|
||||
for (uint8_t slot = start_slot; slot <= end_slot; slot++) {
|
||||
key.type = FuriHalCryptoKeyTypeSimple;
|
||||
key.size = FuriHalCryptoKeySize256;
|
||||
key.data = key_data;
|
||||
furi_hal_random_fill_buf(key_data, 32);
|
||||
if (!furi_hal_crypto_store_add_key(&key, &slot)) {
|
||||
FURI_LOG_E(TAG, "Error writing key to slot %u", slot);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool furi_hal_crypto_verify_key(uint8_t key_slot) {
|
||||
uint8_t keys_nb = 0;
|
||||
uint8_t valid_keys_nb = 0;
|
||||
uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS;
|
||||
uint8_t empty_iv[16];
|
||||
furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb);
|
||||
if (key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key
|
||||
if (key_slot > keys_nb)
|
||||
return false;
|
||||
} else { // Unique key
|
||||
if (keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing
|
||||
return false;
|
||||
for (uint8_t i = key_slot; i > ENCLAVE_FACTORY_KEY_SLOTS; i--) {
|
||||
if(furi_hal_crypto_store_load_key(i, empty_iv)) {
|
||||
last_valid_slot = i;
|
||||
furi_hal_crypto_store_unload_key(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (last_valid_slot == key_slot)
|
||||
return true;
|
||||
else // Generate missing unique keys
|
||||
return furi_hal_crypto_generate_unique_keys(last_valid_slot+1, key_slot);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) {
|
||||
furi_assert(keys_nb);
|
||||
furi_assert(valid_keys_nb);
|
||||
uint8_t keys = 0;
|
||||
uint8_t keys_valid = 0;
|
||||
uint8_t buffer[ENCLAVE_SIGNATURE_SIZE];
|
||||
for(size_t key_slot = 0; key_slot < ENCLAVE_FACTORY_KEY_SLOTS; key_slot++) {
|
||||
if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) {
|
||||
keys++;
|
||||
if(furi_hal_crypto_encrypt(enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) {
|
||||
keys_valid += memcmp(buffer, enclave_signature_expected[key_slot], ENCLAVE_SIGNATURE_SIZE) == 0;
|
||||
}
|
||||
furi_hal_crypto_store_unload_key(key_slot + 1);
|
||||
}
|
||||
}
|
||||
*keys_nb = keys;
|
||||
*valid_keys_nb = keys_valid;
|
||||
if (*valid_keys_nb == ENCLAVE_FACTORY_KEY_SLOTS)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) {
|
||||
furi_assert(key);
|
||||
furi_assert(slot);
|
||||
|
@ -2,48 +2,6 @@
|
||||
#include <furi-hal.h>
|
||||
#include <shci.h>
|
||||
|
||||
#define ENCLAVE_SIGNATURE_KEY_SLOTS 10
|
||||
#define ENCLAVE_SIGNATURE_SIZE 16
|
||||
|
||||
static const uint8_t enclave_signature_iv[ENCLAVE_SIGNATURE_KEY_SLOTS][16] = {
|
||||
{0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a},
|
||||
{0x38, 0xe6, 0x6a, 0x90, 0x5e, 0x5b, 0x8a, 0xa6, 0x70, 0x30, 0x04, 0x72, 0xc2, 0x42, 0xea, 0xaf},
|
||||
{0x73, 0xd5, 0x8e, 0xfb, 0x0f, 0x4b, 0xa9, 0x79, 0x0f, 0xde, 0x0e, 0x53, 0x44, 0x7d, 0xaa, 0xfd},
|
||||
{0x3c, 0x9a, 0xf4, 0x43, 0x2b, 0xfe, 0xea, 0xae, 0x8c, 0xc6, 0xd1, 0x60, 0xd2, 0x96, 0x64, 0xa9},
|
||||
{0x10, 0xac, 0x7b, 0x63, 0x03, 0x7f, 0x43, 0x18, 0xec, 0x9d, 0x9c, 0xc4, 0x01, 0xdc, 0x35, 0xa7},
|
||||
{0x26, 0x21, 0x64, 0xe6, 0xd0, 0xf2, 0x47, 0x49, 0xdc, 0x36, 0xcd, 0x68, 0x0c, 0x91, 0x03, 0x44},
|
||||
{0x7a, 0xbd, 0xce, 0x9c, 0x24, 0x7a, 0x2a, 0xb1, 0x3c, 0x4f, 0x5a, 0x7d, 0x80, 0x3e, 0xfc, 0x0d},
|
||||
{0xcd, 0xdd, 0xd3, 0x02, 0x85, 0x65, 0x43, 0x83, 0xf9, 0xac, 0x75, 0x2f, 0x21, 0xef, 0x28, 0x6b},
|
||||
{0xab, 0x73, 0x70, 0xe8, 0xe2, 0x56, 0x0f, 0x58, 0xab, 0x29, 0xa5, 0xb1, 0x13, 0x47, 0x5e, 0xe8},
|
||||
{0x4f, 0x3c, 0x43, 0x77, 0xde, 0xed, 0x79, 0xa1, 0x8d, 0x4c, 0x1f, 0xfd, 0xdb, 0x96, 0x87, 0x2e},
|
||||
};
|
||||
|
||||
static const uint8_t enclave_signature_input[ENCLAVE_SIGNATURE_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = {
|
||||
{0x9f, 0x5c, 0xb1, 0x43, 0x17, 0x53, 0x18, 0x8c, 0x66, 0x3d, 0x39, 0x45, 0x90, 0x13, 0xa9, 0xde},
|
||||
{0xc5, 0x98, 0xe9, 0x17, 0xb8, 0x97, 0x9e, 0x03, 0x33, 0x14, 0x13, 0x8f, 0xce, 0x74, 0x0d, 0x54},
|
||||
{0x34, 0xba, 0x99, 0x59, 0x9f, 0x70, 0x67, 0xe9, 0x09, 0xee, 0x64, 0x0e, 0xb3, 0xba, 0xfb, 0x75},
|
||||
{0xdc, 0xfa, 0x6c, 0x9a, 0x6f, 0x0a, 0x3e, 0xdc, 0x42, 0xf6, 0xae, 0x0d, 0x3c, 0xf7, 0x83, 0xaf},
|
||||
{0xea, 0x2d, 0xe3, 0x1f, 0x02, 0x99, 0x1a, 0x7e, 0x6d, 0x93, 0x4c, 0xb5, 0x42, 0xf0, 0x7a, 0x9b},
|
||||
{0x53, 0x5e, 0x04, 0xa2, 0x49, 0xa0, 0x73, 0x49, 0x56, 0xb0, 0x88, 0x8c, 0x12, 0xa0, 0xe4, 0x18},
|
||||
{0x7d, 0xa7, 0xc5, 0x21, 0x7f, 0x12, 0x95, 0xdd, 0x4d, 0x77, 0x01, 0xfa, 0x71, 0x88, 0x2b, 0x7f},
|
||||
{0xdc, 0x9b, 0xc5, 0xa7, 0x6b, 0x84, 0x5c, 0x37, 0x7c, 0xec, 0x05, 0xa1, 0x9f, 0x91, 0x17, 0x3b},
|
||||
{0xea, 0xcf, 0xd9, 0x9b, 0x86, 0xcd, 0x2b, 0x43, 0x54, 0x45, 0x82, 0xc6, 0xfe, 0x73, 0x1a, 0x1a},
|
||||
{0x77, 0xb8, 0x1b, 0x90, 0xb4, 0xb7, 0x32, 0x76, 0x8f, 0x8a, 0x57, 0x06, 0xc7, 0xdd, 0x08, 0x90},
|
||||
};
|
||||
|
||||
static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = {
|
||||
{0xe9, 0x9a, 0xce, 0xe9, 0x4d, 0xe1, 0x7f, 0x55, 0xcb, 0x8a, 0xbf, 0xf2, 0x4d, 0x98, 0x27, 0x67},
|
||||
{0x34, 0x27, 0xa7, 0xea, 0xa8, 0x98, 0x66, 0x9b, 0xed, 0x43, 0xd3, 0x93, 0xb5, 0xa2, 0x87, 0x8e},
|
||||
{0x6c, 0xf3, 0x01, 0x78, 0x53, 0x1b, 0x11, 0x32, 0xf0, 0x27, 0x2f, 0xe3, 0x7d, 0xa6, 0xe2, 0xfd},
|
||||
{0xdf, 0x7f, 0x37, 0x65, 0x2f, 0xdb, 0x7c, 0xcf, 0x5b, 0xb6, 0xe4, 0x9c, 0x63, 0xc5, 0x0f, 0xe0},
|
||||
{0x9b, 0x5c, 0xee, 0x44, 0x0e, 0xd1, 0xcb, 0x5f, 0x28, 0x9f, 0x12, 0x17, 0x59, 0x64, 0x40, 0xbb},
|
||||
{0x94, 0xc2, 0x09, 0x98, 0x62, 0xa7, 0x2b, 0x93, 0xed, 0x36, 0x1f, 0x10, 0xbc, 0x26, 0xbd, 0x41},
|
||||
{0x4d, 0xb2, 0x2b, 0xc5, 0x96, 0x47, 0x61, 0xf4, 0x16, 0xe0, 0x81, 0xc3, 0x8e, 0xb9, 0x9c, 0x9b},
|
||||
{0xc3, 0x6b, 0x83, 0x55, 0x90, 0x38, 0x0f, 0xea, 0xd1, 0x65, 0xbf, 0x32, 0x4f, 0x8e, 0x62, 0x5b},
|
||||
{0x8d, 0x5e, 0x27, 0xbc, 0x14, 0x4f, 0x08, 0xa8, 0x2b, 0x14, 0x89, 0x5e, 0xdf, 0x77, 0x04, 0x31},
|
||||
{0xc9, 0xf7, 0x03, 0xf1, 0x6c, 0x65, 0xad, 0x49, 0x74, 0xbe, 0x00, 0x54, 0xfd, 0xa6, 0x9c, 0x32},
|
||||
};
|
||||
|
||||
void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) {
|
||||
string_t value;
|
||||
string_init(value);
|
||||
@ -164,23 +122,12 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) {
|
||||
out("radio_ble_mac", string_get_cstr(value), false, context);
|
||||
|
||||
// Signature verification
|
||||
uint8_t buffer[ENCLAVE_SIGNATURE_SIZE];
|
||||
size_t enclave_valid_keys = 0;
|
||||
for(size_t key_slot = 0; key_slot < ENCLAVE_SIGNATURE_KEY_SLOTS; key_slot++) {
|
||||
if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) {
|
||||
if(furi_hal_crypto_encrypt(
|
||||
enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) {
|
||||
enclave_valid_keys += memcmp(
|
||||
buffer,
|
||||
enclave_signature_expected[key_slot],
|
||||
ENCLAVE_SIGNATURE_SIZE) == 0;
|
||||
}
|
||||
furi_hal_crypto_store_unload_key(key_slot + 1);
|
||||
}
|
||||
}
|
||||
uint8_t enclave_keys = 0;
|
||||
uint8_t enclave_valid_keys = 0;
|
||||
bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys);
|
||||
string_printf(value, "%d", enclave_valid_keys);
|
||||
out("enclave_valid_keys", string_get_cstr(value), false, context);
|
||||
out("enclave_valid", (enclave_valid_keys == ENCLAVE_SIGNATURE_KEY_SLOTS) ? "true" : "false", true, context);
|
||||
out("enclave_valid", enclave_valid ? "true" : "false", true, context);
|
||||
} else {
|
||||
out("radio_alive", "false", true, context);
|
||||
}
|
||||
|
56
firmware/targets/f7/furi-hal/furi-hal-random.c
Normal file
56
firmware/targets/f7/furi-hal/furi-hal-random.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include "furi-hal-random.h"
|
||||
#include <furi.h>
|
||||
#include <furi-hal.h>
|
||||
|
||||
#include <stm32wbxx_ll_rng.h>
|
||||
#include <stm32wbxx_ll_hsem.h>
|
||||
|
||||
#include <hw_conf.h>
|
||||
|
||||
uint32_t furi_hal_random_get() {
|
||||
|
||||
while( LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID));
|
||||
LL_RNG_Enable(RNG);
|
||||
|
||||
while (!LL_RNG_IsActiveFlag_DRDY(RNG));
|
||||
|
||||
if ((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) {
|
||||
furi_crash("TRNG error");
|
||||
}
|
||||
|
||||
uint32_t random_val = LL_RNG_ReadRandData32(RNG);
|
||||
|
||||
LL_RNG_Disable(RNG);
|
||||
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0);
|
||||
|
||||
return random_val;
|
||||
}
|
||||
|
||||
void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len) {
|
||||
|
||||
while( LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID));
|
||||
LL_RNG_Enable(RNG);
|
||||
|
||||
for (uint32_t i = 0; i < len; i+= 4) {
|
||||
while (!LL_RNG_IsActiveFlag_DRDY(RNG));
|
||||
|
||||
if ((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) {
|
||||
furi_crash("TRNG error");
|
||||
}
|
||||
|
||||
uint32_t random_val = LL_RNG_ReadRandData32(RNG);
|
||||
|
||||
uint8_t len_cur = ((i+4) < len) ? (4) : (len-i);
|
||||
memcpy(&buf[i], &random_val, len_cur);
|
||||
}
|
||||
|
||||
LL_RNG_Disable(RNG);
|
||||
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0);
|
||||
}
|
||||
|
||||
void srand(unsigned seed) {
|
||||
}
|
||||
|
||||
int rand() {
|
||||
return (furi_hal_random_get() & RAND_MAX);
|
||||
}
|
309
firmware/targets/f7/furi-hal/furi-hal-usb-u2f.c
Normal file
309
firmware/targets/f7/furi-hal/furi-hal-usb-u2f.c
Normal file
@ -0,0 +1,309 @@
|
||||
#include "furi-hal-version.h"
|
||||
#include "furi-hal-usb_i.h"
|
||||
#include "furi-hal-usb-hid-u2f.h"
|
||||
#include "furi-hal-usb.h"
|
||||
#include <furi.h>
|
||||
#include "usb.h"
|
||||
#include "usb_hid.h"
|
||||
|
||||
#define HID_PAGE_FIDO 0xF1D0
|
||||
#define HID_FIDO_U2F 0x01
|
||||
#define HID_FIDO_INPUT 0x20
|
||||
#define HID_FIDO_OUTPUT 0x21
|
||||
|
||||
#define HID_EP_IN 0x81
|
||||
#define HID_EP_OUT 0x01
|
||||
|
||||
struct HidIadDescriptor {
|
||||
struct usb_iad_descriptor hid_iad;
|
||||
struct usb_interface_descriptor hid;
|
||||
struct usb_hid_descriptor hid_desc;
|
||||
struct usb_endpoint_descriptor hid_ep_in;
|
||||
struct usb_endpoint_descriptor hid_ep_out;
|
||||
};
|
||||
|
||||
struct HidConfigDescriptor {
|
||||
struct usb_config_descriptor config;
|
||||
struct HidIadDescriptor iad_0;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* HID report: FIDO U2F */
|
||||
static const uint8_t hid_u2f_report_desc[] = {
|
||||
HID_RI_USAGE_PAGE(16, HID_PAGE_FIDO),
|
||||
HID_USAGE(HID_FIDO_U2F),
|
||||
HID_COLLECTION(HID_APPLICATION_COLLECTION),
|
||||
HID_USAGE(HID_FIDO_INPUT),
|
||||
HID_LOGICAL_MINIMUM(0x00),
|
||||
HID_LOGICAL_MAXIMUM(0xFF),
|
||||
HID_REPORT_SIZE(8),
|
||||
HID_REPORT_COUNT(HID_U2F_PACKET_LEN),
|
||||
HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||
HID_USAGE(HID_FIDO_OUTPUT),
|
||||
HID_LOGICAL_MINIMUM(0x00),
|
||||
HID_LOGICAL_MAXIMUM(0xFF),
|
||||
HID_REPORT_SIZE(8),
|
||||
HID_REPORT_COUNT(HID_U2F_PACKET_LEN),
|
||||
HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||
HID_END_COLLECTION,
|
||||
};
|
||||
|
||||
static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc.");
|
||||
static const struct usb_string_descriptor dev_prod_desc = USB_STRING_DESC("U2F Token test");
|
||||
static const struct usb_string_descriptor dev_serial_desc = USB_STRING_DESC("TODO: serial");
|
||||
|
||||
/* Device descriptor */
|
||||
static const struct usb_device_descriptor hid_u2f_device_desc = {
|
||||
.bLength = sizeof(struct usb_device_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_DEVICE,
|
||||
.bcdUSB = VERSION_BCD(2, 0, 0),
|
||||
.bDeviceClass = USB_CLASS_IAD,
|
||||
.bDeviceSubClass = USB_SUBCLASS_IAD,
|
||||
.bDeviceProtocol = USB_PROTO_IAD,
|
||||
.bMaxPacketSize0 = USB_EP0_SIZE,
|
||||
.idVendor = 0x0483,
|
||||
.idProduct = 0x5741,
|
||||
.bcdDevice = VERSION_BCD(1, 0, 0),
|
||||
.iManufacturer = UsbDevManuf,
|
||||
.iProduct = UsbDevProduct,
|
||||
.iSerialNumber = UsbDevSerial,
|
||||
.bNumConfigurations = 1,
|
||||
};
|
||||
|
||||
/* Device configuration descriptor */
|
||||
static const struct HidConfigDescriptor hid_u2f_cfg_desc = {
|
||||
.config =
|
||||
{
|
||||
.bLength = sizeof(struct usb_config_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_CONFIGURATION,
|
||||
.wTotalLength = sizeof(struct HidConfigDescriptor),
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = NO_DESCRIPTOR,
|
||||
.bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED,
|
||||
.bMaxPower = USB_CFG_POWER_MA(100),
|
||||
},
|
||||
.iad_0 =
|
||||
{
|
||||
.hid_iad =
|
||||
{
|
||||
.bLength = sizeof(struct usb_iad_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_INTERFASEASSOC,
|
||||
.bFirstInterface = 0,
|
||||
.bInterfaceCount = 1,
|
||||
.bFunctionClass = USB_CLASS_PER_INTERFACE,
|
||||
.bFunctionSubClass = USB_SUBCLASS_NONE,
|
||||
.bFunctionProtocol = USB_PROTO_NONE,
|
||||
.iFunction = NO_DESCRIPTOR,
|
||||
},
|
||||
.hid =
|
||||
{
|
||||
.bLength = sizeof(struct usb_interface_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_INTERFACE,
|
||||
.bInterfaceNumber = 0,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT,
|
||||
.bInterfaceProtocol = USB_HID_PROTO_NONBOOT,
|
||||
.iInterface = NO_DESCRIPTOR,
|
||||
},
|
||||
.hid_desc =
|
||||
{
|
||||
.bLength = sizeof(struct usb_hid_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_HID,
|
||||
.bcdHID = VERSION_BCD(1, 0, 0),
|
||||
.bCountryCode = USB_HID_COUNTRY_NONE,
|
||||
.bNumDescriptors = 1,
|
||||
.bDescriptorType0 = USB_DTYPE_HID_REPORT,
|
||||
.wDescriptorLength0 = sizeof(hid_u2f_report_desc),
|
||||
},
|
||||
.hid_ep_in =
|
||||
{
|
||||
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_ENDPOINT,
|
||||
.bEndpointAddress = HID_EP_IN,
|
||||
.bmAttributes = USB_EPTYPE_INTERRUPT,
|
||||
.wMaxPacketSize = HID_U2F_PACKET_LEN,
|
||||
.bInterval = 5,
|
||||
},
|
||||
.hid_ep_out =
|
||||
{
|
||||
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||
.bDescriptorType = USB_DTYPE_ENDPOINT,
|
||||
.bEndpointAddress = HID_EP_OUT,
|
||||
.bmAttributes = USB_EPTYPE_INTERRUPT,
|
||||
.wMaxPacketSize = HID_U2F_PACKET_LEN,
|
||||
.bInterval = 5,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static void hid_u2f_init(usbd_device* dev, UsbInterface* intf);
|
||||
static void hid_u2f_deinit(usbd_device* dev);
|
||||
static void hid_u2f_on_wakeup(usbd_device* dev);
|
||||
static void hid_u2f_on_suspend(usbd_device* dev);
|
||||
|
||||
//static bool hid_u2f_send_report(uint8_t report_id);
|
||||
static usbd_respond hid_u2f_ep_config(usbd_device* dev, uint8_t cfg);
|
||||
static usbd_respond
|
||||
hid_u2f_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
|
||||
static usbd_device* usb_dev;
|
||||
static osSemaphoreId_t hid_u2f_semaphore = NULL;
|
||||
static bool hid_u2f_connected = false;
|
||||
|
||||
static HidU2fCallback callback;
|
||||
static void* cb_ctx;
|
||||
|
||||
bool furi_hal_hid_u2f_is_connected() {
|
||||
return hid_u2f_connected;
|
||||
}
|
||||
|
||||
void furi_hal_hid_u2f_set_callback(HidU2fCallback cb, void* ctx) {
|
||||
if (callback != NULL) {
|
||||
if (hid_u2f_connected == true)
|
||||
callback(HidU2fDisconnected, cb_ctx);
|
||||
}
|
||||
|
||||
callback = cb;
|
||||
cb_ctx = ctx;
|
||||
|
||||
if (callback != NULL) {
|
||||
if (hid_u2f_connected == true)
|
||||
callback(HidU2fConnected, cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
UsbInterface usb_hid_u2f = {
|
||||
.init = hid_u2f_init,
|
||||
.deinit = hid_u2f_deinit,
|
||||
.wakeup = hid_u2f_on_wakeup,
|
||||
.suspend = hid_u2f_on_suspend,
|
||||
|
||||
.dev_descr = (struct usb_device_descriptor*)&hid_u2f_device_desc,
|
||||
|
||||
.str_manuf_descr = (void*)&dev_manuf_desc,
|
||||
.str_prod_descr = (void*)&dev_prod_desc,
|
||||
.str_serial_descr = (void*)&dev_serial_desc,
|
||||
|
||||
.cfg_descr = (void*)&hid_u2f_cfg_desc,
|
||||
};
|
||||
|
||||
static void hid_u2f_init(usbd_device* dev, UsbInterface* intf) {
|
||||
if(hid_u2f_semaphore == NULL) hid_u2f_semaphore = osSemaphoreNew(1, 1, NULL);
|
||||
usb_dev = dev;
|
||||
|
||||
usbd_reg_config(dev, hid_u2f_ep_config);
|
||||
usbd_reg_control(dev, hid_u2f_control);
|
||||
|
||||
usbd_connect(dev, true);
|
||||
}
|
||||
|
||||
static void hid_u2f_deinit(usbd_device* dev) {
|
||||
usbd_reg_config(dev, NULL);
|
||||
usbd_reg_control(dev, NULL);
|
||||
}
|
||||
|
||||
static void hid_u2f_on_wakeup(usbd_device* dev) {
|
||||
hid_u2f_connected = true;
|
||||
if (callback != NULL)
|
||||
callback(HidU2fConnected, cb_ctx);
|
||||
}
|
||||
|
||||
static void hid_u2f_on_suspend(usbd_device* dev) {
|
||||
if(hid_u2f_connected == true) {
|
||||
hid_u2f_connected = false;
|
||||
osSemaphoreRelease(hid_u2f_semaphore);
|
||||
if (callback != NULL)
|
||||
callback(HidU2fDisconnected, cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_hid_u2f_send_response(uint8_t* data, uint8_t len) {
|
||||
if ((hid_u2f_semaphore == NULL) || (hid_u2f_connected == false))
|
||||
return;
|
||||
furi_check(osSemaphoreAcquire(hid_u2f_semaphore, osWaitForever) == osOK);
|
||||
if (hid_u2f_connected == true) {
|
||||
usbd_ep_write(usb_dev, HID_EP_OUT, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t furi_hal_hid_u2f_get_request(uint8_t* data) {
|
||||
int32_t len = usbd_ep_read(usb_dev, HID_EP_IN, data, HID_U2F_PACKET_LEN);
|
||||
return ((len < 0) ? 0 : len);
|
||||
}
|
||||
|
||||
static void hid_u2f_rx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
|
||||
if (callback != NULL)
|
||||
callback(HidU2fRequest, cb_ctx);
|
||||
}
|
||||
|
||||
static void hid_u2f_tx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
|
||||
osSemaphoreRelease(hid_u2f_semaphore);
|
||||
}
|
||||
|
||||
static void hid_u2f_txrx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) {
|
||||
if (event == usbd_evt_eptx) {
|
||||
hid_u2f_tx_ep_callback(dev, event, ep);
|
||||
} else {
|
||||
hid_u2f_rx_ep_callback(dev, event, ep);
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure endpoints */
|
||||
static usbd_respond hid_u2f_ep_config(usbd_device* dev, uint8_t cfg) {
|
||||
switch(cfg) {
|
||||
case 0:
|
||||
/* deconfiguring device */
|
||||
usbd_ep_deconfig(dev, HID_EP_IN);
|
||||
usbd_ep_deconfig(dev, HID_EP_OUT);
|
||||
usbd_reg_endpoint(dev, HID_EP_IN, 0);
|
||||
usbd_reg_endpoint(dev, HID_EP_OUT, 0);
|
||||
return usbd_ack;
|
||||
case 1:
|
||||
/* configuring device */
|
||||
usbd_ep_config(dev, HID_EP_IN, USB_EPTYPE_INTERRUPT, HID_U2F_PACKET_LEN);
|
||||
usbd_ep_config(dev, HID_EP_OUT, USB_EPTYPE_INTERRUPT, HID_U2F_PACKET_LEN);
|
||||
usbd_reg_endpoint(dev, HID_EP_IN, hid_u2f_txrx_ep_callback);
|
||||
usbd_reg_endpoint(dev, HID_EP_OUT, hid_u2f_txrx_ep_callback);
|
||||
usbd_ep_write(dev, HID_U2F_PACKET_LEN, 0, 0);
|
||||
return usbd_ack;
|
||||
default:
|
||||
return usbd_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Control requests handler */
|
||||
static usbd_respond hid_u2f_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
|
||||
/* HID control requests */
|
||||
if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
|
||||
(USB_REQ_INTERFACE | USB_REQ_CLASS) &&
|
||||
req->wIndex == 0) {
|
||||
switch(req->bRequest) {
|
||||
case USB_HID_SETIDLE:
|
||||
return usbd_ack;
|
||||
case USB_HID_GETREPORT:
|
||||
// dev->status.data_ptr = &hid_u2f_report;
|
||||
// dev->status.data_count = sizeof(hid_u2f_report);
|
||||
return usbd_ack;
|
||||
default:
|
||||
return usbd_fail;
|
||||
}
|
||||
}
|
||||
if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) ==
|
||||
(USB_REQ_INTERFACE | USB_REQ_STANDARD) &&
|
||||
req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) {
|
||||
switch(req->wValue >> 8) {
|
||||
case USB_DTYPE_HID:
|
||||
dev->status.data_ptr = (uint8_t*)&(hid_u2f_cfg_desc.iad_0.hid_desc);
|
||||
dev->status.data_count = sizeof(hid_u2f_cfg_desc.iad_0.hid_desc);
|
||||
return usbd_ack;
|
||||
case USB_DTYPE_HID_REPORT:
|
||||
dev->status.data_ptr = (uint8_t*)hid_u2f_report_desc;
|
||||
dev->status.data_count = sizeof(hid_u2f_report_desc);
|
||||
return usbd_ack;
|
||||
default:
|
||||
return usbd_fail;
|
||||
}
|
||||
}
|
||||
return usbd_fail;
|
||||
}
|
@ -32,6 +32,10 @@ typedef struct {
|
||||
*/
|
||||
void furi_hal_crypto_init();
|
||||
|
||||
bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb);
|
||||
|
||||
bool furi_hal_crypto_verify_key(uint8_t key_slot);
|
||||
|
||||
/** Store key in crypto storage
|
||||
*
|
||||
* @param key FuriHalCryptoKey to store. Only Master, Simple or
|
||||
|
24
firmware/targets/furi-hal-include/furi-hal-random.h
Normal file
24
firmware/targets/furi-hal-include/furi-hal-random.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Get random value
|
||||
*
|
||||
* @return random value
|
||||
*/
|
||||
uint32_t furi_hal_random_get();
|
||||
|
||||
/** Fill buffer with random data
|
||||
*
|
||||
* @param buf buffer pointer
|
||||
* @param data buffer len
|
||||
*/
|
||||
void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
36
firmware/targets/furi-hal-include/furi-hal-usb-hid-u2f.h
Normal file
36
firmware/targets/furi-hal-include/furi-hal-usb-hid-u2f.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#define HID_U2F_PACKET_LEN 64
|
||||
|
||||
typedef enum {
|
||||
HidU2fDisconnected,
|
||||
HidU2fConnected,
|
||||
HidU2fRequest,
|
||||
} HidU2fEvent;
|
||||
|
||||
typedef void (*HidU2fCallback)(HidU2fEvent ev, void* context);
|
||||
|
||||
/** Get HID U2F connection state
|
||||
*
|
||||
* @return true / false
|
||||
*/
|
||||
bool furi_hal_hid_u2f_is_connected();
|
||||
|
||||
/** Set HID U2F event callback
|
||||
*
|
||||
* @param cb callback
|
||||
* @param ctx callback context
|
||||
*/
|
||||
void furi_hal_hid_u2f_set_callback(HidU2fCallback cb, void* ctx);
|
||||
|
||||
/** Get received U2F HID packet
|
||||
*
|
||||
*/
|
||||
uint32_t furi_hal_hid_u2f_get_request(uint8_t* data);
|
||||
|
||||
/** Send U2F HID response packet
|
||||
*
|
||||
* @param data response data
|
||||
* @param len packet length
|
||||
*/
|
||||
void furi_hal_hid_u2f_send_response(uint8_t* data, uint8_t len);
|
@ -23,6 +23,7 @@ struct UsbInterface {
|
||||
extern UsbInterface usb_cdc_single;
|
||||
extern UsbInterface usb_cdc_dual;
|
||||
extern UsbInterface usb_hid;
|
||||
extern UsbInterface usb_hid_u2f;
|
||||
|
||||
/** USB device low-level initialization
|
||||
*/
|
||||
|
@ -40,6 +40,7 @@ template <unsigned int N> struct STOP_EXTERNING_ME {};
|
||||
#include "furi-hal-compress.h"
|
||||
#include "furi-hal-uart.h"
|
||||
#include "furi-hal-info.h"
|
||||
#include "furi-hal-random.h"
|
||||
|
||||
/** Init furi-hal */
|
||||
void furi_hal_init();
|
||||
|
@ -124,3 +124,7 @@ C_SOURCES += $(wildcard $(LIB_DIR)/heatshrink/*.c)
|
||||
# Toolbox
|
||||
CFLAGS += -I$(LIB_DIR)/flipper_file
|
||||
C_SOURCES += $(wildcard $(LIB_DIR)/flipper_file/*.c)
|
||||
|
||||
# Micro-ECC
|
||||
CFLAGS += -I$(LIB_DIR)/micro-ecc
|
||||
C_SOURCES += $(wildcard $(LIB_DIR)/micro-ecc/*.c)
|
||||
|
21
lib/micro-ecc/LICENSE.txt
Normal file
21
lib/micro-ecc/LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
||||
Copyright (c) 2014, Kenneth MacKay
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
41
lib/micro-ecc/README.md
Normal file
41
lib/micro-ecc/README.md
Normal file
@ -0,0 +1,41 @@
|
||||
micro-ecc
|
||||
==========
|
||||
|
||||
A small and fast ECDH and ECDSA implementation for 8-bit, 32-bit, and 64-bit processors.
|
||||
|
||||
The static version of micro-ecc (ie, where the curve was selected at compile-time) can be found in the "static" branch.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Resistant to known side-channel attacks.
|
||||
* Written in C, with optional GCC inline assembly for AVR, ARM and Thumb platforms.
|
||||
* Supports 8, 32, and 64-bit architectures.
|
||||
* Small code size.
|
||||
* No dynamic memory allocation.
|
||||
* Support for 5 standard curves: secp160r1, secp192r1, secp224r1, secp256r1, and secp256k1.
|
||||
* BSD 2-clause license.
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
### Point Representation ###
|
||||
Compressed points are represented in the standard format as defined in http://www.secg.org/sec1-v2.pdf; uncompressed points are represented in standard format, but without the `0x04` prefix. All functions except `uECC_decompress()` only accept uncompressed points; use `uECC_compress()` and `uECC_decompress()` to convert between compressed and uncompressed point representations.
|
||||
|
||||
Private keys are represented in the standard format.
|
||||
|
||||
### Using the Code ###
|
||||
|
||||
I recommend just copying (or symlink) the uECC files into your project. Then just `#include "uECC.h"` to use the micro-ecc functions.
|
||||
|
||||
For use with Arduino, you can use the Library Manager to download micro-ecc (**Sketch**=>**Include Library**=>**Manage Libraries**). You can then use uECC just like any other Arduino library (uECC should show up in the **Sketch**=>**Import Library** submenu).
|
||||
|
||||
See uECC.h for documentation for each function.
|
||||
|
||||
### Compilation Notes ###
|
||||
|
||||
* Should compile with any C/C++ compiler that supports stdint.h (this includes Visual Studio 2013).
|
||||
* If you want to change the defaults for any of the uECC compile-time options (such as `uECC_OPTIMIZATION_LEVEL`), you must change them in your Makefile or similar so that uECC.c is compiled with the desired values (ie, compile uECC.c with `-DuECC_OPTIMIZATION_LEVEL=3` or whatever).
|
||||
* When compiling for a Thumb-1 platform, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher).
|
||||
* When compiling for an ARM/Thumb-2 platform with `uECC_OPTIMIZATION_LEVEL` >= 3, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher).
|
||||
* When compiling for AVR, you must have optimizations enabled (compile with `-O1` or higher).
|
||||
* When building for Windows, you will need to link in the `advapi32.lib` system library.
|
820
lib/micro-ecc/asm_arm.inc
Normal file
820
lib/micro-ecc/asm_arm.inc
Normal file
@ -0,0 +1,820 @@
|
||||
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
||||
|
||||
#ifndef _UECC_ASM_ARM_H_
|
||||
#define _UECC_ASM_ARM_H_
|
||||
|
||||
#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1)
|
||||
#define uECC_MIN_WORDS 8
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp224r1
|
||||
#undef uECC_MIN_WORDS
|
||||
#define uECC_MIN_WORDS 7
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp192r1
|
||||
#undef uECC_MIN_WORDS
|
||||
#define uECC_MIN_WORDS 6
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp160r1
|
||||
#undef uECC_MIN_WORDS
|
||||
#define uECC_MIN_WORDS 5
|
||||
#endif
|
||||
|
||||
#if (uECC_PLATFORM == uECC_arm_thumb)
|
||||
#define REG_RW "+l"
|
||||
#define REG_WRITE "=l"
|
||||
#else
|
||||
#define REG_RW "+r"
|
||||
#define REG_WRITE "=r"
|
||||
#endif
|
||||
|
||||
#if (uECC_PLATFORM == uECC_arm_thumb || uECC_PLATFORM == uECC_arm_thumb2)
|
||||
#define REG_RW_LO "+l"
|
||||
#define REG_WRITE_LO "=l"
|
||||
#else
|
||||
#define REG_RW_LO "+r"
|
||||
#define REG_WRITE_LO "=r"
|
||||
#endif
|
||||
|
||||
#if (uECC_PLATFORM == uECC_arm_thumb2)
|
||||
#define RESUME_SYNTAX
|
||||
#else
|
||||
#define RESUME_SYNTAX ".syntax divided \n\t"
|
||||
#endif
|
||||
|
||||
#if (uECC_OPTIMIZATION_LEVEL >= 2)
|
||||
|
||||
uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
wordcount_t num_words) {
|
||||
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
|
||||
#if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2)
|
||||
uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1;
|
||||
#else /* ARM */
|
||||
uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4;
|
||||
#endif
|
||||
#endif
|
||||
uint32_t carry;
|
||||
uint32_t left_word;
|
||||
uint32_t right_word;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
"movs %[carry], #0 \n\t"
|
||||
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
|
||||
"adr %[left], 1f \n\t"
|
||||
".align 4 \n\t"
|
||||
"adds %[jump], %[left] \n\t"
|
||||
#endif
|
||||
|
||||
"ldmia %[lptr]!, {%[left]} \n\t"
|
||||
"ldmia %[rptr]!, {%[right]} \n\t"
|
||||
"adds %[left], %[right] \n\t"
|
||||
"stmia %[dptr]!, {%[left]} \n\t"
|
||||
|
||||
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
|
||||
"bx %[jump] \n\t"
|
||||
#endif
|
||||
"1: \n\t"
|
||||
REPEAT(DEC(uECC_MAX_WORDS),
|
||||
"ldmia %[lptr]!, {%[left]} \n\t"
|
||||
"ldmia %[rptr]!, {%[right]} \n\t"
|
||||
"adcs %[left], %[right] \n\t"
|
||||
"stmia %[dptr]!, {%[left]} \n\t")
|
||||
|
||||
"adcs %[carry], %[carry] \n\t"
|
||||
RESUME_SYNTAX
|
||||
: [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right),
|
||||
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
|
||||
[jump] REG_RW_LO (jump),
|
||||
#endif
|
||||
[carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word),
|
||||
[right] REG_WRITE_LO (right_word)
|
||||
:
|
||||
: "cc", "memory"
|
||||
);
|
||||
return carry;
|
||||
}
|
||||
#define asm_add 1
|
||||
|
||||
uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
wordcount_t num_words) {
|
||||
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
|
||||
#if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2)
|
||||
uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1;
|
||||
#else /* ARM */
|
||||
uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4;
|
||||
#endif
|
||||
#endif
|
||||
uint32_t carry;
|
||||
uint32_t left_word;
|
||||
uint32_t right_word;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
"movs %[carry], #0 \n\t"
|
||||
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
|
||||
"adr %[left], 1f \n\t"
|
||||
".align 4 \n\t"
|
||||
"adds %[jump], %[left] \n\t"
|
||||
#endif
|
||||
|
||||
"ldmia %[lptr]!, {%[left]} \n\t"
|
||||
"ldmia %[rptr]!, {%[right]} \n\t"
|
||||
"subs %[left], %[right] \n\t"
|
||||
"stmia %[dptr]!, {%[left]} \n\t"
|
||||
|
||||
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
|
||||
"bx %[jump] \n\t"
|
||||
#endif
|
||||
"1: \n\t"
|
||||
REPEAT(DEC(uECC_MAX_WORDS),
|
||||
"ldmia %[lptr]!, {%[left]} \n\t"
|
||||
"ldmia %[rptr]!, {%[right]} \n\t"
|
||||
"sbcs %[left], %[right] \n\t"
|
||||
"stmia %[dptr]!, {%[left]} \n\t")
|
||||
|
||||
"adcs %[carry], %[carry] \n\t"
|
||||
RESUME_SYNTAX
|
||||
: [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right),
|
||||
#if (uECC_MAX_WORDS != uECC_MIN_WORDS)
|
||||
[jump] REG_RW_LO (jump),
|
||||
#endif
|
||||
[carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word),
|
||||
[right] REG_WRITE_LO (right_word)
|
||||
:
|
||||
: "cc", "memory"
|
||||
);
|
||||
return !carry; /* Note that on ARM, carry flag set means "no borrow" when subtracting
|
||||
(for some reason...) */
|
||||
}
|
||||
#define asm_sub 1
|
||||
|
||||
#endif /* (uECC_OPTIMIZATION_LEVEL >= 2) */
|
||||
|
||||
#if (uECC_OPTIMIZATION_LEVEL >= 3)
|
||||
|
||||
#if (uECC_PLATFORM != uECC_arm_thumb)
|
||||
|
||||
#if uECC_ARM_USE_UMAAL
|
||||
#include "asm_arm_mult_square_umaal.inc"
|
||||
#else
|
||||
#include "asm_arm_mult_square.inc"
|
||||
#endif
|
||||
|
||||
#if (uECC_OPTIMIZATION_LEVEL == 3)
|
||||
|
||||
uECC_VLI_API void uECC_vli_mult(uint32_t *result,
|
||||
const uint32_t *left,
|
||||
const uint32_t *right,
|
||||
wordcount_t num_words) {
|
||||
register uint32_t *r0 __asm__("r0") = result;
|
||||
register const uint32_t *r1 __asm__("r1") = left;
|
||||
register const uint32_t *r2 __asm__("r2") = right;
|
||||
register uint32_t r3 __asm__("r3") = num_words;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
#if (uECC_MIN_WORDS == 5)
|
||||
FAST_MULT_ASM_5
|
||||
#if (uECC_MAX_WORDS > 5)
|
||||
FAST_MULT_ASM_5_TO_6
|
||||
#endif
|
||||
#if (uECC_MAX_WORDS > 6)
|
||||
FAST_MULT_ASM_6_TO_7
|
||||
#endif
|
||||
#if (uECC_MAX_WORDS > 7)
|
||||
FAST_MULT_ASM_7_TO_8
|
||||
#endif
|
||||
#elif (uECC_MIN_WORDS == 6)
|
||||
FAST_MULT_ASM_6
|
||||
#if (uECC_MAX_WORDS > 6)
|
||||
FAST_MULT_ASM_6_TO_7
|
||||
#endif
|
||||
#if (uECC_MAX_WORDS > 7)
|
||||
FAST_MULT_ASM_7_TO_8
|
||||
#endif
|
||||
#elif (uECC_MIN_WORDS == 7)
|
||||
FAST_MULT_ASM_7
|
||||
#if (uECC_MAX_WORDS > 7)
|
||||
FAST_MULT_ASM_7_TO_8
|
||||
#endif
|
||||
#elif (uECC_MIN_WORDS == 8)
|
||||
FAST_MULT_ASM_8
|
||||
#endif
|
||||
"1: \n\t"
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1), "+r" (r2)
|
||||
: "r" (r3)
|
||||
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
}
|
||||
#define asm_mult 1
|
||||
|
||||
#if uECC_SQUARE_FUNC
|
||||
uECC_VLI_API void uECC_vli_square(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
wordcount_t num_words) {
|
||||
register uint32_t *r0 __asm__("r0") = result;
|
||||
register const uint32_t *r1 __asm__("r1") = left;
|
||||
register uint32_t r2 __asm__("r2") = num_words;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
#if (uECC_MIN_WORDS == 5)
|
||||
FAST_SQUARE_ASM_5
|
||||
#if (uECC_MAX_WORDS > 5)
|
||||
FAST_SQUARE_ASM_5_TO_6
|
||||
#endif
|
||||
#if (uECC_MAX_WORDS > 6)
|
||||
FAST_SQUARE_ASM_6_TO_7
|
||||
#endif
|
||||
#if (uECC_MAX_WORDS > 7)
|
||||
FAST_SQUARE_ASM_7_TO_8
|
||||
#endif
|
||||
#elif (uECC_MIN_WORDS == 6)
|
||||
FAST_SQUARE_ASM_6
|
||||
#if (uECC_MAX_WORDS > 6)
|
||||
FAST_SQUARE_ASM_6_TO_7
|
||||
#endif
|
||||
#if (uECC_MAX_WORDS > 7)
|
||||
FAST_SQUARE_ASM_7_TO_8
|
||||
#endif
|
||||
#elif (uECC_MIN_WORDS == 7)
|
||||
FAST_SQUARE_ASM_7
|
||||
#if (uECC_MAX_WORDS > 7)
|
||||
FAST_SQUARE_ASM_7_TO_8
|
||||
#endif
|
||||
#elif (uECC_MIN_WORDS == 8)
|
||||
FAST_SQUARE_ASM_8
|
||||
#endif
|
||||
|
||||
"1: \n\t"
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1)
|
||||
: "r" (r2)
|
||||
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
}
|
||||
#define asm_square 1
|
||||
#endif /* uECC_SQUARE_FUNC */
|
||||
|
||||
#else /* (uECC_OPTIMIZATION_LEVEL > 3) */
|
||||
|
||||
uECC_VLI_API void uECC_vli_mult(uint32_t *result,
|
||||
const uint32_t *left,
|
||||
const uint32_t *right,
|
||||
wordcount_t num_words) {
|
||||
register uint32_t *r0 __asm__("r0") = result;
|
||||
register const uint32_t *r1 __asm__("r1") = left;
|
||||
register const uint32_t *r2 __asm__("r2") = right;
|
||||
register uint32_t r3 __asm__("r3") = num_words;
|
||||
|
||||
#if uECC_SUPPORTS_secp160r1
|
||||
if (num_words == 5) {
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
FAST_MULT_ASM_5
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1), "+r" (r2)
|
||||
: "r" (r3)
|
||||
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp192r1
|
||||
if (num_words == 6) {
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
FAST_MULT_ASM_6
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1), "+r" (r2)
|
||||
: "r" (r3)
|
||||
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp224r1
|
||||
if (num_words == 7) {
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
FAST_MULT_ASM_7
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1), "+r" (r2)
|
||||
: "r" (r3)
|
||||
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1)
|
||||
if (num_words == 8) {
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
FAST_MULT_ASM_8
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1), "+r" (r2)
|
||||
: "r" (r3)
|
||||
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#define asm_mult 1
|
||||
|
||||
#if uECC_SQUARE_FUNC
|
||||
uECC_VLI_API void uECC_vli_square(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
wordcount_t num_words) {
|
||||
register uint32_t *r0 __asm__("r0") = result;
|
||||
register const uint32_t *r1 __asm__("r1") = left;
|
||||
register uint32_t r2 __asm__("r2") = num_words;
|
||||
|
||||
#if uECC_SUPPORTS_secp160r1
|
||||
if (num_words == 5) {
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
FAST_SQUARE_ASM_5
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1)
|
||||
: "r" (r2)
|
||||
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp192r1
|
||||
if (num_words == 6) {
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
FAST_SQUARE_ASM_6
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1)
|
||||
: "r" (r2)
|
||||
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp224r1
|
||||
if (num_words == 7) {
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
FAST_SQUARE_ASM_7
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1)
|
||||
: "r" (r2)
|
||||
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1)
|
||||
if (num_words == 8) {
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
FAST_SQUARE_ASM_8
|
||||
RESUME_SYNTAX
|
||||
: "+r" (r0), "+r" (r1)
|
||||
: "r" (r2)
|
||||
: "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#define asm_square 1
|
||||
#endif /* uECC_SQUARE_FUNC */
|
||||
|
||||
#endif /* (uECC_OPTIMIZATION_LEVEL > 3) */
|
||||
|
||||
#endif /* uECC_PLATFORM != uECC_arm_thumb */
|
||||
|
||||
#endif /* (uECC_OPTIMIZATION_LEVEL >= 3) */
|
||||
|
||||
/* ---- "Small" implementations ---- */
|
||||
|
||||
#if !asm_add
|
||||
uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
wordcount_t num_words) {
|
||||
uint32_t carry = 0;
|
||||
uint32_t left_word;
|
||||
uint32_t right_word;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
"1: \n\t"
|
||||
"ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */
|
||||
"ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */
|
||||
"lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */
|
||||
"adcs %[left], %[left], %[right] \n\t" /* Add with carry. */
|
||||
"adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */
|
||||
"stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */
|
||||
"subs %[ctr], #1 \n\t" /* Decrement counter. */
|
||||
"bne 1b \n\t" /* Loop until counter == 0. */
|
||||
RESUME_SYNTAX
|
||||
: [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right),
|
||||
[ctr] REG_RW (num_words), [carry] REG_RW (carry),
|
||||
[left] REG_WRITE (left_word), [right] REG_WRITE (right_word)
|
||||
:
|
||||
: "cc", "memory"
|
||||
);
|
||||
return carry;
|
||||
}
|
||||
#define asm_add 1
|
||||
#endif
|
||||
|
||||
#if !asm_sub
|
||||
uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
wordcount_t num_words) {
|
||||
uint32_t carry = 1; /* carry = 1 initially (means don't borrow) */
|
||||
uint32_t left_word;
|
||||
uint32_t right_word;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
"1: \n\t"
|
||||
"ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */
|
||||
"ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */
|
||||
"lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */
|
||||
"sbcs %[left], %[left], %[right] \n\t" /* Subtract with borrow. */
|
||||
"adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */
|
||||
"stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */
|
||||
"subs %[ctr], #1 \n\t" /* Decrement counter. */
|
||||
"bne 1b \n\t" /* Loop until counter == 0. */
|
||||
RESUME_SYNTAX
|
||||
: [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right),
|
||||
[ctr] REG_RW (num_words), [carry] REG_RW (carry),
|
||||
[left] REG_WRITE (left_word), [right] REG_WRITE (right_word)
|
||||
:
|
||||
: "cc", "memory"
|
||||
);
|
||||
return !carry;
|
||||
}
|
||||
#define asm_sub 1
|
||||
#endif
|
||||
|
||||
#if !asm_mult
|
||||
uECC_VLI_API void uECC_vli_mult(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
wordcount_t num_words) {
|
||||
#if (uECC_PLATFORM != uECC_arm_thumb)
|
||||
uint32_t c0 = 0;
|
||||
uint32_t c1 = 0;
|
||||
uint32_t c2 = 0;
|
||||
uint32_t k = 0;
|
||||
uint32_t i;
|
||||
uint32_t t0, t1;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
|
||||
"1: \n\t" /* outer loop (k < num_words) */
|
||||
"movs %[i], #0 \n\t" /* i = 0 */
|
||||
"b 3f \n\t"
|
||||
|
||||
"2: \n\t" /* outer loop (k >= num_words) */
|
||||
"movs %[i], %[k] \n\t" /* i = k */
|
||||
"subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */
|
||||
|
||||
"3: \n\t" /* inner loop */
|
||||
"subs %[t0], %[k], %[i] \n\t" /* t0 = k-i */
|
||||
|
||||
"ldr %[t1], [%[right], %[t0]] \n\t" /* t1 = right[k - i] */
|
||||
"ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */
|
||||
|
||||
"umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */
|
||||
|
||||
"adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */
|
||||
"adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */
|
||||
"adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */
|
||||
|
||||
"adds %[i], #4 \n\t" /* i += 4 */
|
||||
"cmp %[i], %[last_word] \n\t" /* i > (num_words - 1) (times 4)? */
|
||||
"bgt 4f \n\t" /* if so, exit the loop */
|
||||
"cmp %[i], %[k] \n\t" /* i <= k? */
|
||||
"ble 3b \n\t" /* if so, continue looping */
|
||||
|
||||
"4: \n\t" /* end inner loop */
|
||||
|
||||
"str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */
|
||||
"mov %[c0], %[c1] \n\t" /* c0 = c1 */
|
||||
"mov %[c1], %[c2] \n\t" /* c1 = c2 */
|
||||
"movs %[c2], #0 \n\t" /* c2 = 0 */
|
||||
"adds %[k], #4 \n\t" /* k += 4 */
|
||||
"cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */
|
||||
"ble 1b \n\t" /* if so, loop back, start with i = 0 */
|
||||
"cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */
|
||||
"ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */
|
||||
/* end outer loop */
|
||||
|
||||
"str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */
|
||||
RESUME_SYNTAX
|
||||
: [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2),
|
||||
[k] "+r" (k), [i] "=&r" (i), [t0] "=&r" (t0), [t1] "=&r" (t1)
|
||||
: [result] "r" (result), [left] "r" (left), [right] "r" (right),
|
||||
[last_word] "r" ((num_words - 1) * 4)
|
||||
: "cc", "memory"
|
||||
);
|
||||
|
||||
#else /* Thumb-1 */
|
||||
uint32_t r4, r5, r6, r7;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
"subs %[r3], #1 \n\t" /* r3 = num_words - 1 */
|
||||
"lsls %[r3], #2 \n\t" /* r3 = (num_words - 1) * 4 */
|
||||
"mov r8, %[r3] \n\t" /* r8 = (num_words - 1) * 4 */
|
||||
"lsls %[r3], #1 \n\t" /* r3 = (num_words - 1) * 8 */
|
||||
"mov r9, %[r3] \n\t" /* r9 = (num_words - 1) * 8 */
|
||||
"movs %[r3], #0 \n\t" /* c0 = 0 */
|
||||
"movs %[r4], #0 \n\t" /* c1 = 0 */
|
||||
"movs %[r5], #0 \n\t" /* c2 = 0 */
|
||||
"movs %[r6], #0 \n\t" /* k = 0 */
|
||||
|
||||
"push {%[r0]} \n\t" /* keep result on the stack */
|
||||
|
||||
"1: \n\t" /* outer loop (k < num_words) */
|
||||
"movs %[r7], #0 \n\t" /* r7 = i = 0 */
|
||||
"b 3f \n\t"
|
||||
|
||||
"2: \n\t" /* outer loop (k >= num_words) */
|
||||
"movs %[r7], %[r6] \n\t" /* r7 = k */
|
||||
"mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */
|
||||
"subs %[r7], %[r0] \n\t" /* r7 = i = k - (num_words - 1) (times 4) */
|
||||
|
||||
"3: \n\t" /* inner loop */
|
||||
"mov r10, %[r3] \n\t"
|
||||
"mov r11, %[r4] \n\t"
|
||||
"mov r12, %[r5] \n\t"
|
||||
"mov r14, %[r6] \n\t"
|
||||
"subs %[r0], %[r6], %[r7] \n\t" /* r0 = k - i */
|
||||
|
||||
"ldr %[r4], [%[r2], %[r0]] \n\t" /* r4 = right[k - i] */
|
||||
"ldr %[r0], [%[r1], %[r7]] \n\t" /* r0 = left[i] */
|
||||
|
||||
"lsrs %[r3], %[r0], #16 \n\t" /* r3 = a1 */
|
||||
"uxth %[r0], %[r0] \n\t" /* r0 = a0 */
|
||||
|
||||
"lsrs %[r5], %[r4], #16 \n\t" /* r5 = b1 */
|
||||
"uxth %[r4], %[r4] \n\t" /* r4 = b0 */
|
||||
|
||||
"movs %[r6], %[r3] \n\t" /* r6 = a1 */
|
||||
"muls %[r6], %[r5], %[r6] \n\t" /* r6 = a1 * b1 */
|
||||
"muls %[r3], %[r4], %[r3] \n\t" /* r3 = b0 * a1 */
|
||||
"muls %[r5], %[r0], %[r5] \n\t" /* r5 = a0 * b1 */
|
||||
"muls %[r0], %[r4], %[r0] \n\t" /* r0 = a0 * b0 */
|
||||
|
||||
/* Add middle terms */
|
||||
"lsls %[r4], %[r3], #16 \n\t"
|
||||
"lsrs %[r3], %[r3], #16 \n\t"
|
||||
"adds %[r0], %[r4] \n\t"
|
||||
"adcs %[r6], %[r3] \n\t"
|
||||
|
||||
"lsls %[r4], %[r5], #16 \n\t"
|
||||
"lsrs %[r5], %[r5], #16 \n\t"
|
||||
"adds %[r0], %[r4] \n\t"
|
||||
"adcs %[r6], %[r5] \n\t"
|
||||
|
||||
"mov %[r3], r10\n\t"
|
||||
"mov %[r4], r11\n\t"
|
||||
"mov %[r5], r12\n\t"
|
||||
"adds %[r3], %[r0] \n\t" /* add low word to c0 */
|
||||
"adcs %[r4], %[r6] \n\t" /* add high word to c1, including carry */
|
||||
"movs %[r0], #0 \n\t" /* r0 = 0 (does not affect carry bit) */
|
||||
"adcs %[r5], %[r0] \n\t" /* add carry to c2 */
|
||||
|
||||
"mov %[r6], r14\n\t" /* r6 = k */
|
||||
|
||||
"adds %[r7], #4 \n\t" /* i += 4 */
|
||||
"cmp %[r7], r8 \n\t" /* i > (num_words - 1) (times 4)? */
|
||||
"bgt 4f \n\t" /* if so, exit the loop */
|
||||
"cmp %[r7], %[r6] \n\t" /* i <= k? */
|
||||
"ble 3b \n\t" /* if so, continue looping */
|
||||
|
||||
"4: \n\t" /* end inner loop */
|
||||
|
||||
"ldr %[r0], [sp, #0] \n\t" /* r0 = result */
|
||||
|
||||
"str %[r3], [%[r0], %[r6]] \n\t" /* result[k] = c0 */
|
||||
"mov %[r3], %[r4] \n\t" /* c0 = c1 */
|
||||
"mov %[r4], %[r5] \n\t" /* c1 = c2 */
|
||||
"movs %[r5], #0 \n\t" /* c2 = 0 */
|
||||
"adds %[r6], #4 \n\t" /* k += 4 */
|
||||
"cmp %[r6], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */
|
||||
"ble 1b \n\t" /* if so, loop back, start with i = 0 */
|
||||
"cmp %[r6], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */
|
||||
"ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */
|
||||
/* end outer loop */
|
||||
|
||||
"str %[r3], [%[r0], %[r6]] \n\t" /* result[num_words * 2 - 1] = c0 */
|
||||
"pop {%[r0]} \n\t" /* pop result off the stack */
|
||||
|
||||
".syntax divided \n\t"
|
||||
: [r3] "+l" (num_words), [r4] "=&l" (r4),
|
||||
[r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7)
|
||||
: [r0] "l" (result), [r1] "l" (left), [r2] "l" (right)
|
||||
: "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
#endif
|
||||
}
|
||||
#define asm_mult 1
|
||||
#endif
|
||||
|
||||
#if uECC_SQUARE_FUNC
|
||||
#if !asm_square
|
||||
uECC_VLI_API void uECC_vli_square(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
wordcount_t num_words) {
|
||||
#if (uECC_PLATFORM != uECC_arm_thumb)
|
||||
uint32_t c0 = 0;
|
||||
uint32_t c1 = 0;
|
||||
uint32_t c2 = 0;
|
||||
uint32_t k = 0;
|
||||
uint32_t i, tt;
|
||||
uint32_t t0, t1;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
|
||||
"1: \n\t" /* outer loop (k < num_words) */
|
||||
"movs %[i], #0 \n\t" /* i = 0 */
|
||||
"b 3f \n\t"
|
||||
|
||||
"2: \n\t" /* outer loop (k >= num_words) */
|
||||
"movs %[i], %[k] \n\t" /* i = k */
|
||||
"subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */
|
||||
|
||||
"3: \n\t" /* inner loop */
|
||||
"subs %[tt], %[k], %[i] \n\t" /* tt = k-i */
|
||||
|
||||
"ldr %[t1], [%[left], %[tt]] \n\t" /* t1 = left[k - i] */
|
||||
"ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */
|
||||
|
||||
"umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */
|
||||
|
||||
"cmp %[i], %[tt] \n\t" /* (i < k - i) ? */
|
||||
"bge 4f \n\t" /* if i >= k - i, skip */
|
||||
"adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */
|
||||
"adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */
|
||||
"adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */
|
||||
|
||||
"4: \n\t"
|
||||
"adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */
|
||||
"adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */
|
||||
"adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */
|
||||
|
||||
"adds %[i], #4 \n\t" /* i += 4 */
|
||||
"cmp %[i], %[k] \n\t" /* i >= k? */
|
||||
"bge 5f \n\t" /* if so, exit the loop */
|
||||
"subs %[tt], %[k], %[i] \n\t" /* tt = k - i */
|
||||
"cmp %[i], %[tt] \n\t" /* i <= k - i? */
|
||||
"ble 3b \n\t" /* if so, continue looping */
|
||||
|
||||
"5: \n\t" /* end inner loop */
|
||||
|
||||
"str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */
|
||||
"mov %[c0], %[c1] \n\t" /* c0 = c1 */
|
||||
"mov %[c1], %[c2] \n\t" /* c1 = c2 */
|
||||
"movs %[c2], #0 \n\t" /* c2 = 0 */
|
||||
"adds %[k], #4 \n\t" /* k += 4 */
|
||||
"cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */
|
||||
"ble 1b \n\t" /* if so, loop back, start with i = 0 */
|
||||
"cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */
|
||||
"ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */
|
||||
/* end outer loop */
|
||||
|
||||
"str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */
|
||||
RESUME_SYNTAX
|
||||
: [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2),
|
||||
[k] "+r" (k), [i] "=&r" (i), [tt] "=&r" (tt), [t0] "=&r" (t0), [t1] "=&r" (t1)
|
||||
: [result] "r" (result), [left] "r" (left), [last_word] "r" ((num_words - 1) * 4)
|
||||
: "cc", "memory"
|
||||
);
|
||||
|
||||
#else
|
||||
uint32_t r3, r4, r5, r6, r7;
|
||||
|
||||
__asm__ volatile (
|
||||
".syntax unified \n\t"
|
||||
"subs %[r2], #1 \n\t" /* r2 = num_words - 1 */
|
||||
"lsls %[r2], #2 \n\t" /* r2 = (num_words - 1) * 4 */
|
||||
"mov r8, %[r2] \n\t" /* r8 = (num_words - 1) * 4 */
|
||||
"lsls %[r2], #1 \n\t" /* r2 = (num_words - 1) * 8 */
|
||||
"mov r9, %[r2] \n\t" /* r9 = (num_words - 1) * 8 */
|
||||
"movs %[r2], #0 \n\t" /* c0 = 0 */
|
||||
"movs %[r3], #0 \n\t" /* c1 = 0 */
|
||||
"movs %[r4], #0 \n\t" /* c2 = 0 */
|
||||
"movs %[r5], #0 \n\t" /* k = 0 */
|
||||
|
||||
"push {%[r0]} \n\t" /* keep result on the stack */
|
||||
|
||||
"1: \n\t" /* outer loop (k < num_words) */
|
||||
"movs %[r6], #0 \n\t" /* r6 = i = 0 */
|
||||
"b 3f \n\t"
|
||||
|
||||
"2: \n\t" /* outer loop (k >= num_words) */
|
||||
"movs %[r6], %[r5] \n\t" /* r6 = k */
|
||||
"mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */
|
||||
"subs %[r6], %[r0] \n\t" /* r6 = i = k - (num_words - 1) (times 4) */
|
||||
|
||||
"3: \n\t" /* inner loop */
|
||||
"mov r10, %[r2] \n\t"
|
||||
"mov r11, %[r3] \n\t"
|
||||
"mov r12, %[r4] \n\t"
|
||||
"mov r14, %[r5] \n\t"
|
||||
"subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */
|
||||
|
||||
"ldr %[r3], [%[r1], %[r7]] \n\t" /* r3 = left[k - i] */
|
||||
"ldr %[r0], [%[r1], %[r6]] \n\t" /* r0 = left[i] */
|
||||
|
||||
"lsrs %[r2], %[r0], #16 \n\t" /* r2 = a1 */
|
||||
"uxth %[r0], %[r0] \n\t" /* r0 = a0 */
|
||||
|
||||
"lsrs %[r4], %[r3], #16 \n\t" /* r4 = b1 */
|
||||
"uxth %[r3], %[r3] \n\t" /* r3 = b0 */
|
||||
|
||||
"movs %[r5], %[r2] \n\t" /* r5 = a1 */
|
||||
"muls %[r5], %[r4], %[r5] \n\t" /* r5 = a1 * b1 */
|
||||
"muls %[r2], %[r3], %[r2] \n\t" /* r2 = b0 * a1 */
|
||||
"muls %[r4], %[r0], %[r4] \n\t" /* r4 = a0 * b1 */
|
||||
"muls %[r0], %[r3], %[r0] \n\t" /* r0 = a0 * b0 */
|
||||
|
||||
/* Add middle terms */
|
||||
"lsls %[r3], %[r2], #16 \n\t"
|
||||
"lsrs %[r2], %[r2], #16 \n\t"
|
||||
"adds %[r0], %[r3] \n\t"
|
||||
"adcs %[r5], %[r2] \n\t"
|
||||
|
||||
"lsls %[r3], %[r4], #16 \n\t"
|
||||
"lsrs %[r4], %[r4], #16 \n\t"
|
||||
"adds %[r0], %[r3] \n\t"
|
||||
"adcs %[r5], %[r4] \n\t"
|
||||
|
||||
/* Add to acc, doubling if necessary */
|
||||
"mov %[r2], r10\n\t"
|
||||
"mov %[r3], r11\n\t"
|
||||
"mov %[r4], r12\n\t"
|
||||
|
||||
"cmp %[r6], %[r7] \n\t" /* (i < k - i) ? */
|
||||
"bge 4f \n\t" /* if i >= k - i, skip */
|
||||
"movs %[r7], #0 \n\t" /* r7 = 0 */
|
||||
"adds %[r2], %[r0] \n\t" /* add low word to c0 */
|
||||
"adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */
|
||||
"adcs %[r4], %[r7] \n\t" /* add carry to c2 */
|
||||
"4: \n\t"
|
||||
"movs %[r7], #0 \n\t" /* r7 = 0 */
|
||||
"adds %[r2], %[r0] \n\t" /* add low word to c0 */
|
||||
"adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */
|
||||
"adcs %[r4], %[r7] \n\t" /* add carry to c2 */
|
||||
|
||||
"mov %[r5], r14\n\t" /* r5 = k */
|
||||
|
||||
"adds %[r6], #4 \n\t" /* i += 4 */
|
||||
"cmp %[r6], %[r5] \n\t" /* i >= k? */
|
||||
"bge 5f \n\t" /* if so, exit the loop */
|
||||
"subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */
|
||||
"cmp %[r6], %[r7] \n\t" /* i <= k - i? */
|
||||
"ble 3b \n\t" /* if so, continue looping */
|
||||
|
||||
"5: \n\t" /* end inner loop */
|
||||
|
||||
"ldr %[r0], [sp, #0] \n\t" /* r0 = result */
|
||||
|
||||
"str %[r2], [%[r0], %[r5]] \n\t" /* result[k] = c0 */
|
||||
"mov %[r2], %[r3] \n\t" /* c0 = c1 */
|
||||
"mov %[r3], %[r4] \n\t" /* c1 = c2 */
|
||||
"movs %[r4], #0 \n\t" /* c2 = 0 */
|
||||
"adds %[r5], #4 \n\t" /* k += 4 */
|
||||
"cmp %[r5], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */
|
||||
"ble 1b \n\t" /* if so, loop back, start with i = 0 */
|
||||
"cmp %[r5], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */
|
||||
"ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */
|
||||
/* end outer loop */
|
||||
|
||||
"str %[r2], [%[r0], %[r5]] \n\t" /* result[num_words * 2 - 1] = c0 */
|
||||
"pop {%[r0]} \n\t" /* pop result off the stack */
|
||||
|
||||
".syntax divided \n\t"
|
||||
: [r2] "+l" (num_words), [r3] "=&l" (r3), [r4] "=&l" (r4),
|
||||
[r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7)
|
||||
: [r0] "l" (result), [r1] "l" (left)
|
||||
: "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory"
|
||||
);
|
||||
#endif
|
||||
}
|
||||
#define asm_square 1
|
||||
#endif
|
||||
#endif /* uECC_SQUARE_FUNC */
|
||||
|
||||
#endif /* _UECC_ASM_ARM_H_ */
|
2311
lib/micro-ecc/asm_arm_mult_square.inc
Normal file
2311
lib/micro-ecc/asm_arm_mult_square.inc
Normal file
File diff suppressed because it is too large
Load Diff
1202
lib/micro-ecc/asm_arm_mult_square_umaal.inc
Normal file
1202
lib/micro-ecc/asm_arm_mult_square_umaal.inc
Normal file
File diff suppressed because it is too large
Load Diff
1248
lib/micro-ecc/curve-specific.inc
Normal file
1248
lib/micro-ecc/curve-specific.inc
Normal file
File diff suppressed because it is too large
Load Diff
94
lib/micro-ecc/platform-specific.inc
Normal file
94
lib/micro-ecc/platform-specific.inc
Normal file
@ -0,0 +1,94 @@
|
||||
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
||||
|
||||
#ifndef _UECC_PLATFORM_SPECIFIC_H_
|
||||
#define _UECC_PLATFORM_SPECIFIC_H_
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#if (defined(_WIN32) || defined(_WIN64))
|
||||
/* Windows */
|
||||
|
||||
// use pragma syntax to prevent tweaking the linker script for getting CryptXYZ function
|
||||
#pragma comment(lib, "crypt32.lib")
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
static int default_RNG(uint8_t *dest, unsigned size) {
|
||||
HCRYPTPROV prov;
|
||||
if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
CryptGenRandom(prov, size, (BYTE *)dest);
|
||||
CryptReleaseContext(prov, 0);
|
||||
return 1;
|
||||
}
|
||||
#define default_RNG_defined 1
|
||||
|
||||
#elif defined(unix) || defined(__linux__) || defined(__unix__) || defined(__unix) || \
|
||||
(defined(__APPLE__) && defined(__MACH__)) || defined(uECC_POSIX)
|
||||
|
||||
/* Some POSIX-like system with /dev/urandom or /dev/random. */
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef O_CLOEXEC
|
||||
#define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
static int default_RNG(uint8_t *dest, unsigned size) {
|
||||
int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
fd = open("/dev/random", O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
char *ptr = (char *)dest;
|
||||
size_t left = size;
|
||||
while (left > 0) {
|
||||
ssize_t bytes_read = read(fd, ptr, left);
|
||||
if (bytes_read <= 0) { // read failed
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
left -= bytes_read;
|
||||
ptr += bytes_read;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
#define default_RNG_defined 1
|
||||
|
||||
#elif defined(RIOT_VERSION)
|
||||
|
||||
#include <random.h>
|
||||
|
||||
static int default_RNG(uint8_t *dest, unsigned size) {
|
||||
random_bytes(dest, size);
|
||||
return 1;
|
||||
}
|
||||
#define default_RNG_defined 1
|
||||
|
||||
#elif defined(NRF52_SERIES)
|
||||
|
||||
#include "app_error.h"
|
||||
#include "nrf_crypto_rng.h"
|
||||
|
||||
static int default_RNG(uint8_t *dest, unsigned size)
|
||||
{
|
||||
// make sure to call nrf_crypto_init and nrf_crypto_rng_init first
|
||||
ret_code_t ret_code = nrf_crypto_rng_vector_generate(dest, size);
|
||||
return (ret_code == NRF_SUCCESS) ? 1 : 0;
|
||||
}
|
||||
#define default_RNG_defined 1
|
||||
|
||||
#endif /* platform */
|
||||
|
||||
#endif /* _UECC_PLATFORM_SPECIFIC_H_ */
|
108
lib/micro-ecc/types.h
Normal file
108
lib/micro-ecc/types.h
Normal file
@ -0,0 +1,108 @@
|
||||
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
||||
|
||||
#ifndef _UECC_TYPES_H_
|
||||
#define _UECC_TYPES_H_
|
||||
|
||||
#ifndef uECC_PLATFORM
|
||||
#if __AVR__
|
||||
#define uECC_PLATFORM uECC_avr
|
||||
#elif defined(__thumb2__) || defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */
|
||||
#define uECC_PLATFORM uECC_arm_thumb2
|
||||
#elif defined(__thumb__)
|
||||
#define uECC_PLATFORM uECC_arm_thumb
|
||||
#elif defined(__arm__) || defined(_M_ARM)
|
||||
#define uECC_PLATFORM uECC_arm
|
||||
#elif defined(__aarch64__)
|
||||
#define uECC_PLATFORM uECC_arm64
|
||||
#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__)
|
||||
#define uECC_PLATFORM uECC_x86
|
||||
#elif defined(__amd64__) || defined(_M_X64)
|
||||
#define uECC_PLATFORM uECC_x86_64
|
||||
#else
|
||||
#define uECC_PLATFORM uECC_arch_other
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef uECC_ARM_USE_UMAAL
|
||||
#if (uECC_PLATFORM == uECC_arm) && (__ARM_ARCH >= 6)
|
||||
#define uECC_ARM_USE_UMAAL 1
|
||||
#elif (uECC_PLATFORM == uECC_arm_thumb2) && (__ARM_ARCH >= 6) && !__ARM_ARCH_7M__
|
||||
#define uECC_ARM_USE_UMAAL 1
|
||||
#else
|
||||
#define uECC_ARM_USE_UMAAL 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef uECC_WORD_SIZE
|
||||
#if uECC_PLATFORM == uECC_avr
|
||||
#define uECC_WORD_SIZE 1
|
||||
#elif (uECC_PLATFORM == uECC_x86_64 || uECC_PLATFORM == uECC_arm64)
|
||||
#define uECC_WORD_SIZE 8
|
||||
#else
|
||||
#define uECC_WORD_SIZE 4
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (uECC_WORD_SIZE != 1) && (uECC_WORD_SIZE != 4) && (uECC_WORD_SIZE != 8)
|
||||
#error "Unsupported value for uECC_WORD_SIZE"
|
||||
#endif
|
||||
|
||||
#if ((uECC_PLATFORM == uECC_avr) && (uECC_WORD_SIZE != 1))
|
||||
#pragma message ("uECC_WORD_SIZE must be 1 for AVR")
|
||||
#undef uECC_WORD_SIZE
|
||||
#define uECC_WORD_SIZE 1
|
||||
#endif
|
||||
|
||||
#if ((uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \
|
||||
uECC_PLATFORM == uECC_arm_thumb2) && \
|
||||
(uECC_WORD_SIZE != 4))
|
||||
#pragma message ("uECC_WORD_SIZE must be 4 for ARM")
|
||||
#undef uECC_WORD_SIZE
|
||||
#define uECC_WORD_SIZE 4
|
||||
#endif
|
||||
|
||||
#if defined(__SIZEOF_INT128__) || ((__clang_major__ * 100 + __clang_minor__) >= 302)
|
||||
#define SUPPORTS_INT128 1
|
||||
#else
|
||||
#define SUPPORTS_INT128 0
|
||||
#endif
|
||||
|
||||
typedef int8_t wordcount_t;
|
||||
typedef int16_t bitcount_t;
|
||||
typedef int8_t cmpresult_t;
|
||||
|
||||
#if (uECC_WORD_SIZE == 1)
|
||||
|
||||
typedef uint8_t uECC_word_t;
|
||||
typedef uint16_t uECC_dword_t;
|
||||
|
||||
#define HIGH_BIT_SET 0x80
|
||||
#define uECC_WORD_BITS 8
|
||||
#define uECC_WORD_BITS_SHIFT 3
|
||||
#define uECC_WORD_BITS_MASK 0x07
|
||||
|
||||
#elif (uECC_WORD_SIZE == 4)
|
||||
|
||||
typedef uint32_t uECC_word_t;
|
||||
typedef uint64_t uECC_dword_t;
|
||||
|
||||
#define HIGH_BIT_SET 0x80000000
|
||||
#define uECC_WORD_BITS 32
|
||||
#define uECC_WORD_BITS_SHIFT 5
|
||||
#define uECC_WORD_BITS_MASK 0x01F
|
||||
|
||||
#elif (uECC_WORD_SIZE == 8)
|
||||
|
||||
typedef uint64_t uECC_word_t;
|
||||
#if SUPPORTS_INT128
|
||||
typedef unsigned __int128 uECC_dword_t;
|
||||
#endif
|
||||
|
||||
#define HIGH_BIT_SET 0x8000000000000000ull
|
||||
#define uECC_WORD_BITS 64
|
||||
#define uECC_WORD_BITS_SHIFT 6
|
||||
#define uECC_WORD_BITS_MASK 0x03F
|
||||
|
||||
#endif /* uECC_WORD_SIZE */
|
||||
|
||||
#endif /* _UECC_TYPES_H_ */
|
1669
lib/micro-ecc/uECC.c
Normal file
1669
lib/micro-ecc/uECC.c
Normal file
File diff suppressed because it is too large
Load Diff
367
lib/micro-ecc/uECC.h
Normal file
367
lib/micro-ecc/uECC.h
Normal file
@ -0,0 +1,367 @@
|
||||
/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
||||
|
||||
#ifndef _UECC_H_
|
||||
#define _UECC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Platform selection options.
|
||||
If uECC_PLATFORM is not defined, the code will try to guess it based on compiler macros.
|
||||
Possible values for uECC_PLATFORM are defined below: */
|
||||
#define uECC_arch_other 0
|
||||
#define uECC_x86 1
|
||||
#define uECC_x86_64 2
|
||||
#define uECC_arm 3
|
||||
#define uECC_arm_thumb 4
|
||||
#define uECC_arm_thumb2 5
|
||||
#define uECC_arm64 6
|
||||
#define uECC_avr 7
|
||||
|
||||
/* If desired, you can define uECC_WORD_SIZE as appropriate for your platform (1, 4, or 8 bytes).
|
||||
If uECC_WORD_SIZE is not explicitly defined then it will be automatically set based on your
|
||||
platform. */
|
||||
|
||||
/* Optimization level; trade speed for code size.
|
||||
Larger values produce code that is faster but larger.
|
||||
Currently supported values are 0 - 4; 0 is unusably slow for most applications.
|
||||
Optimization level 4 currently only has an effect ARM platforms where more than one
|
||||
curve is enabled. */
|
||||
#ifndef uECC_OPTIMIZATION_LEVEL
|
||||
#define uECC_OPTIMIZATION_LEVEL 2
|
||||
#endif
|
||||
|
||||
/* uECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a specific function to be
|
||||
used for (scalar) squaring instead of the generic multiplication function. This can make things
|
||||
faster somewhat faster, but increases the code size. */
|
||||
#ifndef uECC_SQUARE_FUNC
|
||||
#define uECC_SQUARE_FUNC 0
|
||||
#endif
|
||||
|
||||
/* uECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will switch to native
|
||||
little-endian format for *all* arrays passed in and out of the public API. This includes public
|
||||
and private keys, shared secrets, signatures and message hashes.
|
||||
Using this switch reduces the amount of call stack memory used by uECC, since less intermediate
|
||||
translations are required.
|
||||
Note that this will *only* work on native little-endian processors and it will treat the uint8_t
|
||||
arrays passed into the public API as word arrays, therefore requiring the provided byte arrays
|
||||
to be word aligned on architectures that do not support unaligned accesses.
|
||||
IMPORTANT: Keys and signatures generated with uECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible
|
||||
with keys and signatures generated with uECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use
|
||||
the same endianness. */
|
||||
#ifndef uECC_VLI_NATIVE_LITTLE_ENDIAN
|
||||
#define uECC_VLI_NATIVE_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
/* Curve support selection. Set to 0 to remove that curve. */
|
||||
#ifndef uECC_SUPPORTS_secp160r1
|
||||
#define uECC_SUPPORTS_secp160r1 1
|
||||
#endif
|
||||
#ifndef uECC_SUPPORTS_secp192r1
|
||||
#define uECC_SUPPORTS_secp192r1 1
|
||||
#endif
|
||||
#ifndef uECC_SUPPORTS_secp224r1
|
||||
#define uECC_SUPPORTS_secp224r1 1
|
||||
#endif
|
||||
#ifndef uECC_SUPPORTS_secp256r1
|
||||
#define uECC_SUPPORTS_secp256r1 1
|
||||
#endif
|
||||
#ifndef uECC_SUPPORTS_secp256k1
|
||||
#define uECC_SUPPORTS_secp256k1 1
|
||||
#endif
|
||||
|
||||
/* Specifies whether compressed point format is supported.
|
||||
Set to 0 to disable point compression/decompression functions. */
|
||||
#ifndef uECC_SUPPORT_COMPRESSED_POINT
|
||||
#define uECC_SUPPORT_COMPRESSED_POINT 1
|
||||
#endif
|
||||
|
||||
struct uECC_Curve_t;
|
||||
typedef const struct uECC_Curve_t * uECC_Curve;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if uECC_SUPPORTS_secp160r1
|
||||
uECC_Curve uECC_secp160r1(void);
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp192r1
|
||||
uECC_Curve uECC_secp192r1(void);
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp224r1
|
||||
uECC_Curve uECC_secp224r1(void);
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp256r1
|
||||
uECC_Curve uECC_secp256r1(void);
|
||||
#endif
|
||||
#if uECC_SUPPORTS_secp256k1
|
||||
uECC_Curve uECC_secp256k1(void);
|
||||
#endif
|
||||
|
||||
/* uECC_RNG_Function type
|
||||
The RNG function should fill 'size' random bytes into 'dest'. It should return 1 if
|
||||
'dest' was filled with random data, or 0 if the random data could not be generated.
|
||||
The filled-in values should be either truly random, or from a cryptographically-secure PRNG.
|
||||
|
||||
A correctly functioning RNG function must be set (using uECC_set_rng()) before calling
|
||||
uECC_make_key() or uECC_sign().
|
||||
|
||||
Setting a correctly functioning RNG function improves the resistance to side-channel attacks
|
||||
for uECC_shared_secret() and uECC_sign_deterministic().
|
||||
|
||||
A correct RNG function is set by default when building for Windows, Linux, or OS X.
|
||||
If you are building on another POSIX-compliant system that supports /dev/random or /dev/urandom,
|
||||
you can define uECC_POSIX to use the predefined RNG. For embedded platforms there is no predefined
|
||||
RNG function; you must provide your own.
|
||||
*/
|
||||
typedef int (*uECC_RNG_Function)(uint8_t *dest, unsigned size);
|
||||
|
||||
/* uECC_set_rng() function.
|
||||
Set the function that will be used to generate random bytes. The RNG function should
|
||||
return 1 if the random data was generated, or 0 if the random data could not be generated.
|
||||
|
||||
On platforms where there is no predefined RNG function (eg embedded platforms), this must
|
||||
be called before uECC_make_key() or uECC_sign() are used.
|
||||
|
||||
Inputs:
|
||||
rng_function - The function that will be used to generate random bytes.
|
||||
*/
|
||||
void uECC_set_rng(uECC_RNG_Function rng_function);
|
||||
|
||||
/* uECC_get_rng() function.
|
||||
|
||||
Returns the function that will be used to generate random bytes.
|
||||
*/
|
||||
uECC_RNG_Function uECC_get_rng(void);
|
||||
|
||||
/* uECC_curve_private_key_size() function.
|
||||
|
||||
Returns the size of a private key for the curve in bytes.
|
||||
*/
|
||||
int uECC_curve_private_key_size(uECC_Curve curve);
|
||||
|
||||
/* uECC_curve_public_key_size() function.
|
||||
|
||||
Returns the size of a public key for the curve in bytes.
|
||||
*/
|
||||
int uECC_curve_public_key_size(uECC_Curve curve);
|
||||
|
||||
/* uECC_make_key() function.
|
||||
Create a public/private key pair.
|
||||
|
||||
Outputs:
|
||||
public_key - Will be filled in with the public key. Must be at least 2 * the curve size
|
||||
(in bytes) long. For example, if the curve is secp256r1, public_key must be 64
|
||||
bytes long.
|
||||
private_key - Will be filled in with the private key. Must be as long as the curve order; this
|
||||
is typically the same as the curve size, except for secp160r1. For example, if the
|
||||
curve is secp256r1, private_key must be 32 bytes long.
|
||||
|
||||
For secp160r1, private_key must be 21 bytes long! Note that the first byte will
|
||||
almost always be 0 (there is about a 1 in 2^80 chance of it being non-zero).
|
||||
|
||||
Returns 1 if the key pair was generated successfully, 0 if an error occurred.
|
||||
*/
|
||||
int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve);
|
||||
|
||||
/* uECC_shared_secret() function.
|
||||
Compute a shared secret given your secret key and someone else's public key. If the public key
|
||||
is not from a trusted source and has not been previously verified, you should verify it first
|
||||
using uECC_valid_public_key().
|
||||
Note: It is recommended that you hash the result of uECC_shared_secret() before using it for
|
||||
symmetric encryption or HMAC.
|
||||
|
||||
Inputs:
|
||||
public_key - The public key of the remote party.
|
||||
private_key - Your private key.
|
||||
|
||||
Outputs:
|
||||
secret - Will be filled in with the shared secret value. Must be the same size as the
|
||||
curve size; for example, if the curve is secp256r1, secret must be 32 bytes long.
|
||||
|
||||
Returns 1 if the shared secret was generated successfully, 0 if an error occurred.
|
||||
*/
|
||||
int uECC_shared_secret(const uint8_t *public_key,
|
||||
const uint8_t *private_key,
|
||||
uint8_t *secret,
|
||||
uECC_Curve curve);
|
||||
|
||||
#if uECC_SUPPORT_COMPRESSED_POINT
|
||||
/* uECC_compress() function.
|
||||
Compress a public key.
|
||||
|
||||
Inputs:
|
||||
public_key - The public key to compress.
|
||||
|
||||
Outputs:
|
||||
compressed - Will be filled in with the compressed public key. Must be at least
|
||||
(curve size + 1) bytes long; for example, if the curve is secp256r1,
|
||||
compressed must be 33 bytes long.
|
||||
*/
|
||||
void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve);
|
||||
|
||||
/* uECC_decompress() function.
|
||||
Decompress a compressed public key.
|
||||
|
||||
Inputs:
|
||||
compressed - The compressed public key.
|
||||
|
||||
Outputs:
|
||||
public_key - Will be filled in with the decompressed public key.
|
||||
*/
|
||||
void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve);
|
||||
#endif /* uECC_SUPPORT_COMPRESSED_POINT */
|
||||
|
||||
/* uECC_valid_public_key() function.
|
||||
Check to see if a public key is valid.
|
||||
|
||||
Note that you are not required to check for a valid public key before using any other uECC
|
||||
functions. However, you may wish to avoid spending CPU time computing a shared secret or
|
||||
verifying a signature using an invalid public key.
|
||||
|
||||
Inputs:
|
||||
public_key - The public key to check.
|
||||
|
||||
Returns 1 if the public key is valid, 0 if it is invalid.
|
||||
*/
|
||||
int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve);
|
||||
|
||||
/* uECC_compute_public_key() function.
|
||||
Compute the corresponding public key for a private key.
|
||||
|
||||
Inputs:
|
||||
private_key - The private key to compute the public key for
|
||||
|
||||
Outputs:
|
||||
public_key - Will be filled in with the corresponding public key
|
||||
|
||||
Returns 1 if the key was computed successfully, 0 if an error occurred.
|
||||
*/
|
||||
int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve);
|
||||
|
||||
/* uECC_sign() function.
|
||||
Generate an ECDSA signature for a given hash value.
|
||||
|
||||
Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it in to
|
||||
this function along with your private key.
|
||||
|
||||
Inputs:
|
||||
private_key - Your private key.
|
||||
message_hash - The hash of the message to sign.
|
||||
hash_size - The size of message_hash in bytes.
|
||||
|
||||
Outputs:
|
||||
signature - Will be filled in with the signature value. Must be at least 2 * curve size long.
|
||||
For example, if the curve is secp256r1, signature must be 64 bytes long.
|
||||
|
||||
Returns 1 if the signature generated successfully, 0 if an error occurred.
|
||||
*/
|
||||
int uECC_sign(const uint8_t *private_key,
|
||||
const uint8_t *message_hash,
|
||||
unsigned hash_size,
|
||||
uint8_t *signature,
|
||||
uECC_Curve curve);
|
||||
|
||||
/* uECC_HashContext structure.
|
||||
This is used to pass in an arbitrary hash function to uECC_sign_deterministic().
|
||||
The structure will be used for multiple hash computations; each time a new hash
|
||||
is computed, init_hash() will be called, followed by one or more calls to
|
||||
update_hash(), and finally a call to finish_hash() to produce the resulting hash.
|
||||
|
||||
The intention is that you will create a structure that includes uECC_HashContext
|
||||
followed by any hash-specific data. For example:
|
||||
|
||||
typedef struct SHA256_HashContext {
|
||||
uECC_HashContext uECC;
|
||||
SHA256_CTX ctx;
|
||||
} SHA256_HashContext;
|
||||
|
||||
void init_SHA256(uECC_HashContext *base) {
|
||||
SHA256_HashContext *context = (SHA256_HashContext *)base;
|
||||
SHA256_Init(&context->ctx);
|
||||
}
|
||||
|
||||
void update_SHA256(uECC_HashContext *base,
|
||||
const uint8_t *message,
|
||||
unsigned message_size) {
|
||||
SHA256_HashContext *context = (SHA256_HashContext *)base;
|
||||
SHA256_Update(&context->ctx, message, message_size);
|
||||
}
|
||||
|
||||
void finish_SHA256(uECC_HashContext *base, uint8_t *hash_result) {
|
||||
SHA256_HashContext *context = (SHA256_HashContext *)base;
|
||||
SHA256_Final(hash_result, &context->ctx);
|
||||
}
|
||||
|
||||
... when signing ...
|
||||
{
|
||||
uint8_t tmp[32 + 32 + 64];
|
||||
SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}};
|
||||
uECC_sign_deterministic(key, message_hash, &ctx.uECC, signature);
|
||||
}
|
||||
*/
|
||||
typedef struct uECC_HashContext {
|
||||
void (*init_hash)(const struct uECC_HashContext *context);
|
||||
void (*update_hash)(const struct uECC_HashContext *context,
|
||||
const uint8_t *message,
|
||||
unsigned message_size);
|
||||
void (*finish_hash)(const struct uECC_HashContext *context, uint8_t *hash_result);
|
||||
unsigned block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */
|
||||
unsigned result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */
|
||||
uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */
|
||||
} uECC_HashContext;
|
||||
|
||||
/* uECC_sign_deterministic() function.
|
||||
Generate an ECDSA signature for a given hash value, using a deterministic algorithm
|
||||
(see RFC 6979). You do not need to set the RNG using uECC_set_rng() before calling
|
||||
this function; however, if the RNG is defined it will improve resistance to side-channel
|
||||
attacks.
|
||||
|
||||
Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it to
|
||||
this function along with your private key and a hash context. Note that the message_hash
|
||||
does not need to be computed with the same hash function used by hash_context.
|
||||
|
||||
Inputs:
|
||||
private_key - Your private key.
|
||||
message_hash - The hash of the message to sign.
|
||||
hash_size - The size of message_hash in bytes.
|
||||
hash_context - A hash context to use.
|
||||
|
||||
Outputs:
|
||||
signature - Will be filled in with the signature value.
|
||||
|
||||
Returns 1 if the signature generated successfully, 0 if an error occurred.
|
||||
*/
|
||||
int uECC_sign_deterministic(const uint8_t *private_key,
|
||||
const uint8_t *message_hash,
|
||||
unsigned hash_size,
|
||||
const uECC_HashContext *hash_context,
|
||||
uint8_t *signature,
|
||||
uECC_Curve curve);
|
||||
|
||||
/* uECC_verify() function.
|
||||
Verify an ECDSA signature.
|
||||
|
||||
Usage: Compute the hash of the signed data using the same hash as the signer and
|
||||
pass it to this function along with the signer's public key and the signature values (r and s).
|
||||
|
||||
Inputs:
|
||||
public_key - The signer's public key.
|
||||
message_hash - The hash of the signed data.
|
||||
hash_size - The size of message_hash in bytes.
|
||||
signature - The signature value.
|
||||
|
||||
Returns 1 if the signature is valid, 0 if it is invalid.
|
||||
*/
|
||||
int uECC_verify(const uint8_t *public_key,
|
||||
const uint8_t *message_hash,
|
||||
unsigned hash_size,
|
||||
const uint8_t *signature,
|
||||
uECC_Curve curve);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* _UECC_H_ */
|
172
lib/micro-ecc/uECC_vli.h
Normal file
172
lib/micro-ecc/uECC_vli.h
Normal file
@ -0,0 +1,172 @@
|
||||
/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */
|
||||
|
||||
#ifndef _UECC_VLI_H_
|
||||
#define _UECC_VLI_H_
|
||||
|
||||
#include "uECC.h"
|
||||
#include "types.h"
|
||||
|
||||
/* Functions for raw large-integer manipulation. These are only available
|
||||
if uECC.c is compiled with uECC_ENABLE_VLI_API defined to 1. */
|
||||
#ifndef uECC_ENABLE_VLI_API
|
||||
#define uECC_ENABLE_VLI_API 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if uECC_ENABLE_VLI_API
|
||||
|
||||
void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words);
|
||||
|
||||
/* Constant-time comparison to zero - secure way to compare long integers */
|
||||
/* Returns 1 if vli == 0, 0 otherwise. */
|
||||
uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words);
|
||||
|
||||
/* Returns nonzero if bit 'bit' of vli is set. */
|
||||
uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit);
|
||||
|
||||
/* Counts the number of bits required to represent vli. */
|
||||
bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words);
|
||||
|
||||
/* Sets dest = src. */
|
||||
void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words);
|
||||
|
||||
/* Constant-time comparison function - secure way to compare long integers */
|
||||
/* Returns one if left == right, zero otherwise */
|
||||
uECC_word_t uECC_vli_equal(const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
wordcount_t num_words);
|
||||
|
||||
/* Constant-time comparison function - secure way to compare long integers */
|
||||
/* Returns sign of left - right, in constant time. */
|
||||
cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, wordcount_t num_words);
|
||||
|
||||
/* Computes vli = vli >> 1. */
|
||||
void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words);
|
||||
|
||||
/* Computes result = left + right, returning carry. Can modify in place. */
|
||||
uECC_word_t uECC_vli_add(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
wordcount_t num_words);
|
||||
|
||||
/* Computes result = left - right, returning borrow. Can modify in place. */
|
||||
uECC_word_t uECC_vli_sub(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
wordcount_t num_words);
|
||||
|
||||
/* Computes result = left * right. Result must be 2 * num_words long. */
|
||||
void uECC_vli_mult(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
wordcount_t num_words);
|
||||
|
||||
/* Computes result = left^2. Result must be 2 * num_words long. */
|
||||
void uECC_vli_square(uECC_word_t *result, const uECC_word_t *left, wordcount_t num_words);
|
||||
|
||||
/* Computes result = (left + right) % mod.
|
||||
Assumes that left < mod and right < mod, and that result does not overlap mod. */
|
||||
void uECC_vli_modAdd(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
const uECC_word_t *mod,
|
||||
wordcount_t num_words);
|
||||
|
||||
/* Computes result = (left - right) % mod.
|
||||
Assumes that left < mod and right < mod, and that result does not overlap mod. */
|
||||
void uECC_vli_modSub(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
const uECC_word_t *mod,
|
||||
wordcount_t num_words);
|
||||
|
||||
/* Computes result = product % mod, where product is 2N words long.
|
||||
Currently only designed to work for mod == curve->p or curve_n. */
|
||||
void uECC_vli_mmod(uECC_word_t *result,
|
||||
uECC_word_t *product,
|
||||
const uECC_word_t *mod,
|
||||
wordcount_t num_words);
|
||||
|
||||
/* Calculates result = product (mod curve->p), where product is up to
|
||||
2 * curve->num_words long. */
|
||||
void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve);
|
||||
|
||||
/* Computes result = (left * right) % mod.
|
||||
Currently only designed to work for mod == curve->p or curve_n. */
|
||||
void uECC_vli_modMult(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
const uECC_word_t *mod,
|
||||
wordcount_t num_words);
|
||||
|
||||
/* Computes result = (left * right) % curve->p. */
|
||||
void uECC_vli_modMult_fast(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *right,
|
||||
uECC_Curve curve);
|
||||
|
||||
/* Computes result = left^2 % mod.
|
||||
Currently only designed to work for mod == curve->p or curve_n. */
|
||||
void uECC_vli_modSquare(uECC_word_t *result,
|
||||
const uECC_word_t *left,
|
||||
const uECC_word_t *mod,
|
||||
wordcount_t num_words);
|
||||
|
||||
/* Computes result = left^2 % curve->p. */
|
||||
void uECC_vli_modSquare_fast(uECC_word_t *result, const uECC_word_t *left, uECC_Curve curve);
|
||||
|
||||
/* Computes result = (1 / input) % mod.*/
|
||||
void uECC_vli_modInv(uECC_word_t *result,
|
||||
const uECC_word_t *input,
|
||||
const uECC_word_t *mod,
|
||||
wordcount_t num_words);
|
||||
|
||||
#if uECC_SUPPORT_COMPRESSED_POINT
|
||||
/* Calculates a = sqrt(a) (mod curve->p) */
|
||||
void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve);
|
||||
#endif
|
||||
|
||||
/* Converts an integer in uECC native format to big-endian bytes. */
|
||||
void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, const uECC_word_t *native);
|
||||
/* Converts big-endian bytes to an integer in uECC native format. */
|
||||
void uECC_vli_bytesToNative(uECC_word_t *native, const uint8_t *bytes, int num_bytes);
|
||||
|
||||
unsigned uECC_curve_num_words(uECC_Curve curve);
|
||||
unsigned uECC_curve_num_bytes(uECC_Curve curve);
|
||||
unsigned uECC_curve_num_bits(uECC_Curve curve);
|
||||
unsigned uECC_curve_num_n_words(uECC_Curve curve);
|
||||
unsigned uECC_curve_num_n_bytes(uECC_Curve curve);
|
||||
unsigned uECC_curve_num_n_bits(uECC_Curve curve);
|
||||
|
||||
const uECC_word_t *uECC_curve_p(uECC_Curve curve);
|
||||
const uECC_word_t *uECC_curve_n(uECC_Curve curve);
|
||||
const uECC_word_t *uECC_curve_G(uECC_Curve curve);
|
||||
const uECC_word_t *uECC_curve_b(uECC_Curve curve);
|
||||
|
||||
int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve);
|
||||
|
||||
/* Multiplies a point by a scalar. Points are represented by the X coordinate followed by
|
||||
the Y coordinate in the same array, both coordinates are curve->num_words long. Note
|
||||
that scalar must be curve->num_n_words long (NOT curve->num_words). */
|
||||
void uECC_point_mult(uECC_word_t *result,
|
||||
const uECC_word_t *point,
|
||||
const uECC_word_t *scalar,
|
||||
uECC_Curve curve);
|
||||
|
||||
/* Generates a random integer in the range 0 < random < top.
|
||||
Both random and top have num_words words. */
|
||||
int uECC_generate_random_int(uECC_word_t *random,
|
||||
const uECC_word_t *top,
|
||||
wordcount_t num_words);
|
||||
|
||||
#endif /* uECC_ENABLE_VLI_API */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* _UECC_VLI_H_ */
|
123
lib/toolbox/hmac_sha256.c
Normal file
123
lib/toolbox/hmac_sha256.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* hmac.c - HMAC
|
||||
*
|
||||
* Copyright (C) 2017 Sergei Glushchenko
|
||||
* Author: Sergei Glushchenko <gl.sergei@gmail.com>
|
||||
*
|
||||
* This file is a part of U2F firmware for STM32
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As additional permission under GNU GPL version 3 section 7, you may
|
||||
* distribute non-source form of the Program without the copy of the
|
||||
* GNU GPL normally required by section 4, provided you inform the
|
||||
* recipients of GNU GPL by a written offer.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sha256.h"
|
||||
#include "hmac_sha256.h"
|
||||
|
||||
static void
|
||||
_hmac_sha256_init (const hmac_context *ctx)
|
||||
{
|
||||
hmac_sha256_context *context = (hmac_sha256_context *)ctx;
|
||||
sha256_start(&context->sha_ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
_hmac_sha256_update (const hmac_context *ctx, const uint8_t *message,
|
||||
unsigned message_size)
|
||||
{
|
||||
hmac_sha256_context *context = (hmac_sha256_context *)ctx;
|
||||
sha256_update(&context->sha_ctx, message, message_size);
|
||||
}
|
||||
|
||||
static void
|
||||
_hmac_sha256_finish (const hmac_context *ctx, uint8_t *hash_result)
|
||||
{
|
||||
hmac_sha256_context *context = (hmac_sha256_context *)ctx;
|
||||
sha256_finish(&context->sha_ctx, hash_result);
|
||||
}
|
||||
|
||||
/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always
|
||||
the same size as the hash result size. */
|
||||
static void
|
||||
hmac_init(const hmac_context *ctx, const uint8_t *K)
|
||||
{
|
||||
uint8_t *pad = ctx->tmp + 2 * ctx->result_size;
|
||||
unsigned i;
|
||||
for (i = 0; i < ctx->result_size; ++i)
|
||||
pad[i] = K[i] ^ 0x36;
|
||||
for (; i < ctx->block_size; ++i)
|
||||
pad[i] = 0x36;
|
||||
|
||||
ctx->init_hash (ctx);
|
||||
ctx->update_hash (ctx, pad, ctx->block_size);
|
||||
}
|
||||
|
||||
static void
|
||||
hmac_update(const hmac_context *ctx,
|
||||
const uint8_t *message,
|
||||
unsigned message_size)
|
||||
{
|
||||
ctx->update_hash (ctx, message, message_size);
|
||||
}
|
||||
|
||||
static void
|
||||
hmac_finish(const hmac_context *ctx,
|
||||
const uint8_t *K,
|
||||
uint8_t *result)
|
||||
{
|
||||
uint8_t *pad = ctx->tmp + 2 * ctx->result_size;
|
||||
unsigned i;
|
||||
for (i = 0; i < ctx->result_size; ++i)
|
||||
pad[i] = K[i] ^ 0x5c;
|
||||
for (; i < ctx->block_size; ++i)
|
||||
pad[i] = 0x5c;
|
||||
|
||||
ctx->finish_hash (ctx, result);
|
||||
|
||||
ctx->init_hash (ctx);
|
||||
ctx->update_hash (ctx, pad, ctx->block_size);
|
||||
ctx->update_hash (ctx, result, ctx->result_size);
|
||||
ctx->finish_hash (ctx, result);
|
||||
}
|
||||
|
||||
void
|
||||
hmac_sha256_init (hmac_sha256_context *ctx, const uint8_t *K)
|
||||
{
|
||||
ctx->hmac_ctx.init_hash = _hmac_sha256_init;
|
||||
ctx->hmac_ctx.update_hash = _hmac_sha256_update;
|
||||
ctx->hmac_ctx.finish_hash = _hmac_sha256_finish;
|
||||
ctx->hmac_ctx.block_size = 64;
|
||||
ctx->hmac_ctx.result_size = 32;
|
||||
ctx->hmac_ctx.tmp = ctx->tmp;
|
||||
hmac_init (&ctx->hmac_ctx, K);
|
||||
}
|
||||
|
||||
void
|
||||
hmac_sha256_update (const hmac_sha256_context *ctx, const uint8_t *message,
|
||||
unsigned message_size)
|
||||
{
|
||||
hmac_update (&ctx->hmac_ctx, message, message_size);
|
||||
}
|
||||
|
||||
void
|
||||
hmac_sha256_finish (const hmac_sha256_context *ctx, const uint8_t *K,
|
||||
uint8_t *hash_result)
|
||||
{
|
||||
hmac_finish (&ctx->hmac_ctx, K, hash_result);
|
||||
}
|
29
lib/toolbox/hmac_sha256.h
Normal file
29
lib/toolbox/hmac_sha256.h
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
typedef struct hmac_context {
|
||||
void (*init_hash)(const struct hmac_context *context);
|
||||
void (*update_hash)(const struct hmac_context *context,
|
||||
const uint8_t *message,
|
||||
unsigned message_size);
|
||||
void (*finish_hash)(const struct hmac_context *context, uint8_t *hash_result);
|
||||
unsigned block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */
|
||||
unsigned result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */
|
||||
uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */
|
||||
} hmac_context;
|
||||
|
||||
|
||||
typedef struct hmac_sha256_context {
|
||||
hmac_context hmac_ctx;
|
||||
sha256_context sha_ctx;
|
||||
uint8_t tmp[32 * 2 + 64];
|
||||
} hmac_sha256_context;
|
||||
|
||||
void
|
||||
hmac_sha256_init (hmac_sha256_context *ctx, const uint8_t *K);
|
||||
|
||||
void
|
||||
hmac_sha256_update (const hmac_sha256_context *ctx, const uint8_t *message,
|
||||
unsigned message_size);
|
||||
|
||||
void
|
||||
hmac_sha256_finish (const hmac_sha256_context *ctx, const uint8_t *K,
|
||||
uint8_t *hash_result);
|
226
lib/toolbox/sha256.c
Normal file
226
lib/toolbox/sha256.c
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* sha256.c -- Compute SHA-256 hash
|
||||
*
|
||||
* Just for little endian architecture.
|
||||
*
|
||||
* Code taken from:
|
||||
* http://gladman.plushost.co.uk/oldsite/cryptography_technology/sha/index.php
|
||||
*
|
||||
* File names are sha2.c, sha2.h, brg_types.h, brg_endian.h
|
||||
* in the archive sha2-07-01-07.zip.
|
||||
*
|
||||
* Code is modified in the style of PolarSSL API.
|
||||
*
|
||||
* See original copyright notice below.
|
||||
*/
|
||||
/*
|
||||
---------------------------------------------------------------------------
|
||||
Copyright (c) 2002, Dr Brian Gladman, Worcester, UK. All rights reserved.
|
||||
|
||||
LICENSE TERMS
|
||||
|
||||
The free distribution and use of this software in both source and binary
|
||||
form is allowed (with or without changes) provided that:
|
||||
|
||||
1. distributions of this source code include the above copyright
|
||||
notice, this list of conditions and the following disclaimer;
|
||||
|
||||
2. distributions in binary form include the above copyright
|
||||
notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other associated materials;
|
||||
|
||||
3. the copyright holder's name is not used to endorse products
|
||||
built using this software without specific written permission.
|
||||
|
||||
ALTERNATIVELY, provided that this notice is retained in full, this product
|
||||
may be distributed under the terms of the GNU General Public License (GPL),
|
||||
in which case the provisions of the GPL apply INSTEAD OF those given above.
|
||||
|
||||
DISCLAIMER
|
||||
|
||||
This software is provided 'as is' with no explicit or implied warranties
|
||||
in respect of its properties, including, but not limited to, correctness
|
||||
and/or fitness for purpose.
|
||||
---------------------------------------------------------------------------
|
||||
Issue Date: 01/08/2005
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "sha256.h"
|
||||
|
||||
#define SHA256_MASK (SHA256_BLOCK_SIZE - 1)
|
||||
|
||||
static void memcpy_output_bswap32 (unsigned char *dst, const uint32_t *p)
|
||||
{
|
||||
int i;
|
||||
uint32_t q = 0;
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
if ((i & 3) == 0)
|
||||
q = __builtin_bswap32 (p[i >> 2]); /* bswap32 is GCC extention */
|
||||
dst[i] = q >> ((i & 3) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
#define rotr32(x,n) (((x) >> n) | ((x) << (32 - n)))
|
||||
|
||||
#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
|
||||
#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) ^ (y))))
|
||||
|
||||
/* round transforms for SHA256 compression functions */
|
||||
#define vf(n,i) v[(n - i) & 7]
|
||||
|
||||
#define hf(i) (p[i & 15] += \
|
||||
g_1(p[(i + 14) & 15]) + p[(i + 9) & 15] + g_0(p[(i + 1) & 15]))
|
||||
|
||||
#define v_cycle0(i) \
|
||||
p[i] = __builtin_bswap32 (p[i]); \
|
||||
vf(7,i) += p[i] + k_0[i] \
|
||||
+ s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \
|
||||
vf(3,i) += vf(7,i); \
|
||||
vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i))
|
||||
|
||||
#define v_cycle(i, j) \
|
||||
vf(7,i) += hf(i) + k_0[i+j] \
|
||||
+ s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \
|
||||
vf(3,i) += vf(7,i); \
|
||||
vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i))
|
||||
|
||||
#define s_0(x) (rotr32((x), 2) ^ rotr32((x), 13) ^ rotr32((x), 22))
|
||||
#define s_1(x) (rotr32((x), 6) ^ rotr32((x), 11) ^ rotr32((x), 25))
|
||||
#define g_0(x) (rotr32((x), 7) ^ rotr32((x), 18) ^ ((x) >> 3))
|
||||
#define g_1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10))
|
||||
#define k_0 k256
|
||||
|
||||
static const uint32_t k256[64] = {
|
||||
0X428A2F98, 0X71374491, 0XB5C0FBCF, 0XE9B5DBA5,
|
||||
0X3956C25B, 0X59F111F1, 0X923F82A4, 0XAB1C5ED5,
|
||||
0XD807AA98, 0X12835B01, 0X243185BE, 0X550C7DC3,
|
||||
0X72BE5D74, 0X80DEB1FE, 0X9BDC06A7, 0XC19BF174,
|
||||
0XE49B69C1, 0XEFBE4786, 0X0FC19DC6, 0X240CA1CC,
|
||||
0X2DE92C6F, 0X4A7484AA, 0X5CB0A9DC, 0X76F988DA,
|
||||
0X983E5152, 0XA831C66D, 0XB00327C8, 0XBF597FC7,
|
||||
0XC6E00BF3, 0XD5A79147, 0X06CA6351, 0X14292967,
|
||||
0X27B70A85, 0X2E1B2138, 0X4D2C6DFC, 0X53380D13,
|
||||
0X650A7354, 0X766A0ABB, 0X81C2C92E, 0X92722C85,
|
||||
0XA2BFE8A1, 0XA81A664B, 0XC24B8B70, 0XC76C51A3,
|
||||
0XD192E819, 0XD6990624, 0XF40E3585, 0X106AA070,
|
||||
0X19A4C116, 0X1E376C08, 0X2748774C, 0X34B0BCB5,
|
||||
0X391C0CB3, 0X4ED8AA4A, 0X5B9CCA4F, 0X682E6FF3,
|
||||
0X748F82EE, 0X78A5636F, 0X84C87814, 0X8CC70208,
|
||||
0X90BEFFFA, 0XA4506CEB, 0XBEF9A3F7, 0XC67178F2,
|
||||
};
|
||||
|
||||
void
|
||||
sha256_process (sha256_context *ctx)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t *p = ctx->wbuf;
|
||||
uint32_t v[8];
|
||||
|
||||
memcpy (v, ctx->state, 8 * sizeof (uint32_t));
|
||||
|
||||
v_cycle0 ( 0); v_cycle0 ( 1); v_cycle0 ( 2); v_cycle0 ( 3);
|
||||
v_cycle0 ( 4); v_cycle0 ( 5); v_cycle0 ( 6); v_cycle0 ( 7);
|
||||
v_cycle0 ( 8); v_cycle0 ( 9); v_cycle0 (10); v_cycle0 (11);
|
||||
v_cycle0 (12); v_cycle0 (13); v_cycle0 (14); v_cycle0 (15);
|
||||
|
||||
for (i = 16; i < 64; i += 16)
|
||||
{
|
||||
v_cycle ( 0, i); v_cycle ( 1, i); v_cycle ( 2, i); v_cycle ( 3, i);
|
||||
v_cycle ( 4, i); v_cycle ( 5, i); v_cycle ( 6, i); v_cycle ( 7, i);
|
||||
v_cycle ( 8, i); v_cycle ( 9, i); v_cycle (10, i); v_cycle (11, i);
|
||||
v_cycle (12, i); v_cycle (13, i); v_cycle (14, i); v_cycle (15, i);
|
||||
}
|
||||
|
||||
ctx->state[0] += v[0];
|
||||
ctx->state[1] += v[1];
|
||||
ctx->state[2] += v[2];
|
||||
ctx->state[3] += v[3];
|
||||
ctx->state[4] += v[4];
|
||||
ctx->state[5] += v[5];
|
||||
ctx->state[6] += v[6];
|
||||
ctx->state[7] += v[7];
|
||||
}
|
||||
|
||||
void
|
||||
sha256_update (sha256_context *ctx, const unsigned char *input,
|
||||
unsigned int ilen)
|
||||
{
|
||||
uint32_t left = (ctx->total[0] & SHA256_MASK);
|
||||
uint32_t fill = SHA256_BLOCK_SIZE - left;
|
||||
|
||||
ctx->total[0] += ilen;
|
||||
if (ctx->total[0] < ilen)
|
||||
ctx->total[1]++;
|
||||
|
||||
while (ilen >= fill)
|
||||
{
|
||||
memcpy (((unsigned char*)ctx->wbuf) + left, input, fill);
|
||||
sha256_process (ctx);
|
||||
input += fill;
|
||||
ilen -= fill;
|
||||
left = 0;
|
||||
fill = SHA256_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
memcpy (((unsigned char*)ctx->wbuf) + left, input, ilen);
|
||||
}
|
||||
|
||||
void
|
||||
sha256_finish (sha256_context *ctx, unsigned char output[32])
|
||||
{
|
||||
uint32_t last = (ctx->total[0] & SHA256_MASK);
|
||||
|
||||
ctx->wbuf[last >> 2] = __builtin_bswap32 (ctx->wbuf[last >> 2]);
|
||||
ctx->wbuf[last >> 2] &= 0xffffff80 << (8 * (~last & 3));
|
||||
ctx->wbuf[last >> 2] |= 0x00000080 << (8 * (~last & 3));
|
||||
ctx->wbuf[last >> 2] = __builtin_bswap32 (ctx->wbuf[last >> 2]);
|
||||
|
||||
if (last > SHA256_BLOCK_SIZE - 9)
|
||||
{
|
||||
if (last < 60)
|
||||
ctx->wbuf[15] = 0;
|
||||
sha256_process (ctx);
|
||||
last = 0;
|
||||
}
|
||||
else
|
||||
last = (last >> 2) + 1;
|
||||
|
||||
while (last < 14)
|
||||
ctx->wbuf[last++] = 0;
|
||||
|
||||
ctx->wbuf[14] = __builtin_bswap32 ((ctx->total[0] >> 29) | (ctx->total[1] << 3));
|
||||
ctx->wbuf[15] = __builtin_bswap32 (ctx->total[0] << 3);
|
||||
sha256_process (ctx);
|
||||
|
||||
memcpy_output_bswap32 (output, ctx->state);
|
||||
memset (ctx, 0, sizeof (sha256_context));
|
||||
}
|
||||
|
||||
static const uint32_t initial_state[8] =
|
||||
{
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
};
|
||||
|
||||
void
|
||||
sha256_start (sha256_context *ctx)
|
||||
{
|
||||
ctx->total[0] = ctx->total[1] = 0;
|
||||
memcpy (ctx->state, initial_state, 8 * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void
|
||||
sha256 (const unsigned char *input, unsigned int ilen,
|
||||
unsigned char output[32])
|
||||
{
|
||||
sha256_context ctx;
|
||||
|
||||
sha256_start (&ctx);
|
||||
sha256_update (&ctx, input, ilen);
|
||||
sha256_finish (&ctx, output);
|
||||
}
|
17
lib/toolbox/sha256.h
Normal file
17
lib/toolbox/sha256.h
Normal file
@ -0,0 +1,17 @@
|
||||
#define SHA256_DIGEST_SIZE 32
|
||||
#define SHA256_BLOCK_SIZE 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t total[2];
|
||||
uint32_t state[8];
|
||||
uint32_t wbuf[16];
|
||||
} sha256_context;
|
||||
|
||||
void sha256 (const unsigned char *input, unsigned int ilen,
|
||||
unsigned char output[32]);
|
||||
void sha256_start (sha256_context *ctx);
|
||||
void sha256_finish (sha256_context *ctx, unsigned char output[32]);
|
||||
void sha256_update (sha256_context *ctx, const unsigned char *input,
|
||||
unsigned int ilen);
|
||||
void sha256_process (sha256_context *ctx);
|
Loading…
Reference in New Issue
Block a user