Merge branch 'ofw_dev' into dev

This commit is contained in:
MX
2023-12-01 20:29:16 +03:00
88 changed files with 1576 additions and 10502 deletions

View File

@@ -13,7 +13,7 @@ App(
"!plugins",
"!nfc_cli.c",
],
fap_libs=["assets"],
fap_libs=["assets", "mbedtls"],
fap_icon="icon.png",
fap_category="NFC",
)

View File

@@ -29,7 +29,9 @@ static NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, v
NfcApp* instance = context;
const St25tbPollerEvent* st25tb_event = event.event_data;
if(st25tb_event->type == St25tbPollerEventTypeReady) {
if(st25tb_event->type == St25tbPollerEventTypeRequestMode) {
st25tb_event->data->mode_request.mode = St25tbPollerModeRead;
} else if(st25tb_event->type == St25tbPollerEventTypeSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);

View File

@@ -7,7 +7,7 @@ App(
icon="A_U2F_14",
order=80,
resources="resources",
fap_libs=["assets"],
fap_libs=["assets", "mbedtls"],
fap_category="USB",
fap_icon="icon.png",
)

View File

@@ -1,98 +0,0 @@
/*
* 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);
}

View File

@@ -1,38 +0,0 @@
#pragma once
#include "sha256.h"
#ifdef __cplusplus
extern "C" {
#endif
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);
#ifdef __cplusplus
}
#endif

View File

@@ -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;

View File

@@ -37,7 +37,7 @@ typedef struct {
uint32_t counter;
uint8_t random_salt[24];
uint32_t control;
} __attribute__((packed)) U2fCounterData;
} FURI_PACKED U2fCounterData;
bool u2f_data_check(bool cert_only) {
bool state = false;