mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
[FL-3690] Libraries cleanup; u2f crypto rework to use mbedtls (#3234)
* examples: plugins: utilize fal_embedded * libs: removed fnv1a_hash * furi: added FURI_PACKED; apps, libs: changed to use FURI_PACKED * lib: mbedtls: using custom config * lib: toolbox: removed md5, switched to mbedtls * targets: f18: link fix * lib: added mbedtls_cfg.h * apps: nfc: explicit dependency on libmbedtls * u2f: reworking to mbedtls * u2f: replaced sha256 & hmac with mbedtls * u2f: functional rework using mbedtls * libs: dropped micro-ecc * u2f: dropped old implementation * toolbox: removed sha256 impl * mcheck() for mbedtls * libs: removed libmisc; split into smaller libs * apps: debug: fixed display_test * apps: include cleanups * fbt: fixed VERSIONCOMSTR * furi: added FURI_CHECK_RETURN * lib: removed qrcode * cleanup * fbt: lint_py+format_py: fixed excessive command length * api: Removed bzero from f7 * api: Removed bzero from f18 * Bump API Symbols Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
@@ -1,18 +1,22 @@
|
||||
#include <furi.h>
|
||||
#include "u2f.h"
|
||||
#include "u2f_hid.h"
|
||||
#include "u2f_data.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_random.h>
|
||||
#include <littlefs/lfs_util.h> // for lfs_tobe32
|
||||
|
||||
#include "toolbox/sha256.h"
|
||||
#include "hmac_sha256.h"
|
||||
#include "micro-ecc/uECC.h"
|
||||
#include <mbedtls/sha256.h>
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/error.h>
|
||||
|
||||
#define TAG "U2f"
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
#define MCHECK(expr) furi_check((expr) == 0)
|
||||
|
||||
#define U2F_CMD_REGISTER 0x01
|
||||
#define U2F_CMD_AUTHENTICATE 0x02
|
||||
#define U2F_CMD_VERSION 0x03
|
||||
@@ -25,16 +29,26 @@ typedef enum {
|
||||
0x08, // "dont-enforce-user-presence-and-sign" - send auth response even if user is missing
|
||||
} U2fAuthMode;
|
||||
|
||||
#define U2F_HASH_SIZE 32
|
||||
#define U2F_NONCE_SIZE 32
|
||||
#define U2F_CHALLENGE_SIZE 32
|
||||
#define U2F_APP_ID_SIZE 32
|
||||
|
||||
#define U2F_EC_KEY_SIZE 32
|
||||
#define U2F_EC_BIGNUM_SIZE 32
|
||||
#define U2F_EC_POINT_SIZE 65
|
||||
|
||||
typedef struct {
|
||||
uint8_t format;
|
||||
uint8_t xy[64];
|
||||
} __attribute__((packed)) U2fPubKey;
|
||||
} FURI_PACKED U2fPubKey;
|
||||
_Static_assert(sizeof(U2fPubKey) == U2F_EC_POINT_SIZE, "U2fPubKey size mismatch");
|
||||
|
||||
typedef struct {
|
||||
uint8_t len;
|
||||
uint8_t hash[32];
|
||||
uint8_t nonce[32];
|
||||
} __attribute__((packed)) U2fKeyHandle;
|
||||
uint8_t hash[U2F_HASH_SIZE];
|
||||
uint8_t nonce[U2F_NONCE_SIZE];
|
||||
} FURI_PACKED U2fKeyHandle;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla;
|
||||
@@ -42,16 +56,16 @@ typedef struct {
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t len[3];
|
||||
uint8_t challenge[32];
|
||||
uint8_t app_id[32];
|
||||
} __attribute__((packed)) U2fRegisterReq;
|
||||
uint8_t challenge[U2F_CHALLENGE_SIZE];
|
||||
uint8_t app_id[U2F_APP_ID_SIZE];
|
||||
} FURI_PACKED U2fRegisterReq;
|
||||
|
||||
typedef struct {
|
||||
uint8_t reserved;
|
||||
U2fPubKey pub_key;
|
||||
U2fKeyHandle key_handle;
|
||||
uint8_t cert[];
|
||||
} __attribute__((packed)) U2fRegisterResp;
|
||||
} FURI_PACKED U2fRegisterResp;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla;
|
||||
@@ -59,16 +73,16 @@ typedef struct {
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t len[3];
|
||||
uint8_t challenge[32];
|
||||
uint8_t app_id[32];
|
||||
uint8_t challenge[U2F_CHALLENGE_SIZE];
|
||||
uint8_t app_id[U2F_APP_ID_SIZE];
|
||||
U2fKeyHandle key_handle;
|
||||
} __attribute__((packed)) U2fAuthReq;
|
||||
} FURI_PACKED U2fAuthReq;
|
||||
|
||||
typedef struct {
|
||||
uint8_t user_present;
|
||||
uint32_t counter;
|
||||
uint8_t signature[];
|
||||
} __attribute__((packed)) U2fAuthResp;
|
||||
} FURI_PACKED U2fAuthResp;
|
||||
|
||||
static const uint8_t ver_str[] = {"U2F_V2"};
|
||||
|
||||
@@ -78,19 +92,20 @@ 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];
|
||||
uint8_t device_key[U2F_EC_KEY_SIZE];
|
||||
uint8_t cert_key[U2F_EC_KEY_SIZE];
|
||||
uint32_t counter;
|
||||
const struct uECC_Curve_t* p_curve;
|
||||
bool ready;
|
||||
bool user_present;
|
||||
U2fEvtCallback callback;
|
||||
void* context;
|
||||
mbedtls_ecp_group group;
|
||||
};
|
||||
|
||||
static int u2f_uecc_random(uint8_t* dest, unsigned size) {
|
||||
static int u2f_uecc_random_cb(void* context, uint8_t* dest, unsigned size) {
|
||||
UNUSED(context);
|
||||
furi_hal_random_fill_buf(dest, size);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
U2fData* u2f_alloc() {
|
||||
@@ -99,6 +114,7 @@ U2fData* u2f_alloc() {
|
||||
|
||||
void u2f_free(U2fData* U2F) {
|
||||
furi_assert(U2F);
|
||||
mbedtls_ecp_group_free(&U2F->group);
|
||||
free(U2F);
|
||||
}
|
||||
|
||||
@@ -129,8 +145,8 @@ bool u2f_init(U2fData* U2F) {
|
||||
}
|
||||
}
|
||||
|
||||
U2F->p_curve = uECC_secp256r1();
|
||||
uECC_set_rng(u2f_uecc_random);
|
||||
mbedtls_ecp_group_init(&U2F->group);
|
||||
mbedtls_ecp_group_load(&U2F->group, MBEDTLS_ECP_DP_SECP256R1);
|
||||
|
||||
U2F->ready = true;
|
||||
return true;
|
||||
@@ -171,21 +187,63 @@ 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);
|
||||
len += u2f_der_encode_int(der + len, sig, U2F_HASH_SIZE);
|
||||
len += u2f_der_encode_int(der + len, sig + U2F_HASH_SIZE, U2F_HASH_SIZE);
|
||||
|
||||
der[1] = len - 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
static void
|
||||
u2f_ecc_sign(mbedtls_ecp_group* grp, const uint8_t* key, uint8_t* hash, uint8_t* signature) {
|
||||
mbedtls_mpi r, s, d;
|
||||
|
||||
mbedtls_mpi_init(&r);
|
||||
mbedtls_mpi_init(&s);
|
||||
mbedtls_mpi_init(&d);
|
||||
|
||||
MCHECK(mbedtls_mpi_read_binary(&d, key, U2F_EC_KEY_SIZE));
|
||||
MCHECK(mbedtls_ecdsa_sign(grp, &r, &s, &d, hash, U2F_HASH_SIZE, u2f_uecc_random_cb, NULL));
|
||||
MCHECK(mbedtls_mpi_write_binary(&r, signature, U2F_EC_BIGNUM_SIZE));
|
||||
MCHECK(mbedtls_mpi_write_binary(&s, signature + U2F_EC_BIGNUM_SIZE, U2F_EC_BIGNUM_SIZE));
|
||||
|
||||
mbedtls_mpi_free(&r);
|
||||
mbedtls_mpi_free(&s);
|
||||
mbedtls_mpi_free(&d);
|
||||
}
|
||||
|
||||
static void u2f_ecc_compute_public_key(
|
||||
mbedtls_ecp_group* grp,
|
||||
const uint8_t* private_key,
|
||||
U2fPubKey* public_key) {
|
||||
mbedtls_ecp_point Q;
|
||||
mbedtls_mpi d;
|
||||
size_t olen;
|
||||
|
||||
mbedtls_ecp_point_init(&Q);
|
||||
mbedtls_mpi_init(&d);
|
||||
|
||||
MCHECK(mbedtls_mpi_read_binary(&d, private_key, U2F_EC_KEY_SIZE));
|
||||
MCHECK(mbedtls_ecp_mul(grp, &Q, &d, &grp->G, u2f_uecc_random_cb, NULL));
|
||||
MCHECK(mbedtls_ecp_check_privkey(grp, &d));
|
||||
|
||||
MCHECK(mbedtls_ecp_point_write_binary(
|
||||
grp, &Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, (unsigned char*)public_key, sizeof(U2fPubKey)));
|
||||
|
||||
mbedtls_ecp_point_free(&Q);
|
||||
mbedtls_mpi_free(&d);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
|
||||
static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {
|
||||
U2fRegisterReq* req = (U2fRegisterReq*)buf;
|
||||
U2fRegisterResp* resp = (U2fRegisterResp*)buf;
|
||||
U2fKeyHandle handle;
|
||||
uint8_t private[32];
|
||||
uint8_t private[U2F_EC_KEY_SIZE];
|
||||
U2fPubKey pub_key;
|
||||
uint8_t hash[32];
|
||||
uint8_t signature[64];
|
||||
uint8_t hash[U2F_HASH_SIZE];
|
||||
uint8_t signature[U2F_EC_BIGNUM_SIZE * 2];
|
||||
|
||||
if(u2f_data_check(false) == false) {
|
||||
U2F->ready = false;
|
||||
@@ -201,40 +259,54 @@ static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {
|
||||
}
|
||||
U2F->user_present = false;
|
||||
|
||||
hmac_sha256_context hmac_ctx;
|
||||
sha256_context sha_ctx;
|
||||
handle.len = U2F_HASH_SIZE * 2;
|
||||
|
||||
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);
|
||||
{
|
||||
mbedtls_md_context_t hmac_ctx;
|
||||
mbedtls_md_init(&hmac_ctx);
|
||||
MCHECK(mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1));
|
||||
MCHECK(mbedtls_md_hmac_starts(&hmac_ctx, U2F->device_key, sizeof(U2F->device_key)));
|
||||
|
||||
// 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 private key
|
||||
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));
|
||||
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, handle.nonce, sizeof(handle.nonce)));
|
||||
MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, private));
|
||||
|
||||
MCHECK(mbedtls_md_hmac_reset(&hmac_ctx));
|
||||
|
||||
// Generate private key handle
|
||||
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, private, sizeof(private)));
|
||||
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));
|
||||
MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, handle.hash));
|
||||
}
|
||||
|
||||
// Generate public key
|
||||
pub_key.format = 0x04; // Uncompressed point
|
||||
uECC_compute_public_key(private, pub_key.xy, U2F->p_curve);
|
||||
u2f_ecc_compute_public_key(&U2F->group, private, &pub_key);
|
||||
|
||||
// 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);
|
||||
{
|
||||
uint8_t reserved_byte = 0;
|
||||
|
||||
uECC_sign(U2F->cert_key, hash, 32, signature, U2F->p_curve);
|
||||
mbedtls_sha256_context sha_ctx;
|
||||
|
||||
mbedtls_sha256_init(&sha_ctx);
|
||||
mbedtls_sha256_starts(&sha_ctx, 0);
|
||||
|
||||
mbedtls_sha256_update(&sha_ctx, &reserved_byte, 1);
|
||||
mbedtls_sha256_update(&sha_ctx, req->app_id, sizeof(req->app_id));
|
||||
mbedtls_sha256_update(&sha_ctx, req->challenge, sizeof(req->challenge));
|
||||
mbedtls_sha256_update(&sha_ctx, handle.hash, handle.len);
|
||||
mbedtls_sha256_update(&sha_ctx, (uint8_t*)&pub_key, sizeof(U2fPubKey));
|
||||
|
||||
mbedtls_sha256_finish(&sha_ctx, hash);
|
||||
mbedtls_sha256_free(&sha_ctx);
|
||||
}
|
||||
|
||||
// Sign hash
|
||||
u2f_ecc_sign(&U2F->group, U2F->cert_key, hash, signature);
|
||||
|
||||
// Encode response message
|
||||
resp->reserved = 0x05;
|
||||
@@ -250,13 +322,11 @@ static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) {
|
||||
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 priv_key[U2F_EC_KEY_SIZE];
|
||||
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];
|
||||
uint8_t hash[U2F_HASH_SIZE];
|
||||
uint8_t signature[U2F_HASH_SIZE * 2];
|
||||
uint32_t be_u2f_counter;
|
||||
|
||||
if(u2f_data_check(false) == false) {
|
||||
@@ -281,26 +351,42 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
||||
be_u2f_counter = lfs_tobe32(U2F->counter + 1);
|
||||
|
||||
// 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*)&(be_u2f_counter), 4);
|
||||
sha256_update(&sha_ctx, req->challenge, 32);
|
||||
sha256_finish(&sha_ctx, hash);
|
||||
{
|
||||
mbedtls_sha256_context sha_ctx;
|
||||
|
||||
// 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);
|
||||
mbedtls_sha256_init(&sha_ctx);
|
||||
mbedtls_sha256_starts(&sha_ctx, 0);
|
||||
|
||||
// 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);
|
||||
mbedtls_sha256_update(&sha_ctx, req->app_id, sizeof(req->app_id));
|
||||
mbedtls_sha256_update(&sha_ctx, &flags, 1);
|
||||
mbedtls_sha256_update(&sha_ctx, (uint8_t*)&(be_u2f_counter), sizeof(be_u2f_counter));
|
||||
mbedtls_sha256_update(&sha_ctx, req->challenge, sizeof(req->challenge));
|
||||
|
||||
if(memcmp(req->key_handle.hash, mac_control, 32) != 0) {
|
||||
mbedtls_sha256_finish(&sha_ctx, hash);
|
||||
mbedtls_sha256_free(&sha_ctx);
|
||||
}
|
||||
|
||||
{
|
||||
mbedtls_md_context_t hmac_ctx;
|
||||
mbedtls_md_init(&hmac_ctx);
|
||||
MCHECK(mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1));
|
||||
MCHECK(mbedtls_md_hmac_starts(&hmac_ctx, U2F->device_key, sizeof(U2F->device_key)));
|
||||
|
||||
// Recover private key
|
||||
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));
|
||||
MCHECK(mbedtls_md_hmac_update(
|
||||
&hmac_ctx, req->key_handle.nonce, sizeof(req->key_handle.nonce)));
|
||||
MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, priv_key));
|
||||
|
||||
MCHECK(mbedtls_md_hmac_reset(&hmac_ctx));
|
||||
|
||||
// Generate and verify private key handle
|
||||
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, priv_key, sizeof(priv_key)));
|
||||
MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id)));
|
||||
MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, mac_control));
|
||||
}
|
||||
|
||||
if(memcmp(req->key_handle.hash, mac_control, sizeof(mac_control)) != 0) {
|
||||
FURI_LOG_W(TAG, "Wrong handle!");
|
||||
memcpy(&buf[0], state_wrong_data, 2);
|
||||
return 2;
|
||||
@@ -311,7 +397,8 @@ static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
uECC_sign(priv_key, hash, 32, signature, U2F->p_curve);
|
||||
// Sign hash
|
||||
u2f_ecc_sign(&U2F->group, priv_key, hash, signature);
|
||||
|
||||
resp->user_present = flags;
|
||||
resp->counter = be_u2f_counter;
|
||||
|
||||
Reference in New Issue
Block a user