1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-25 23:01:02 +00:00
trezor-firmware/firmware/u2f.c
Jochen Hoenicke 1cceec0ae2 Check that U2F key only uses hardened derivation ()
We generate only U2F keys with hardened derivation.  However, we
didn't check incoming keys if they used hardened derivation.
This patch fixes this.
2017-01-10 21:51:57 +01:00

771 lines
19 KiB
C

/*
* This file is part of the TREZOR project.
*
* Copyright (C) 2015 Mark Bryars <mbryars@google.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <ecdsa.h>
#include "debug.h"
#include "storage.h"
#include "bip32.h"
#include "layout2.h"
#include "usb.h"
#include "buttons.h"
#include "trezor.h"
#include "curves.h"
#include "nist256p1.h"
#include "rng.h"
#include "hmac.h"
#include "util.h"
#include "macros.h"
#include "u2f/u2f.h"
#include "u2f/u2f_hid.h"
#include "u2f/u2f_keys.h"
#include "u2f_knownapps.h"
#include "u2f.h"
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
// About 1/2 Second according to values used in protect.c
#define U2F_TIMEOUT (800000/2)
#define U2F_OUT_PKT_BUFFER_LEN 128
// Initialise without a cid
static uint32_t cid = 0;
// Circular Output buffer
static uint32_t u2f_out_start = 0;
static uint32_t u2f_out_end = 0;
static uint8_t u2f_out_packets[U2F_OUT_PKT_BUFFER_LEN][HID_RPT_SIZE];
#define U2F_PUBKEY_LEN 65
#define KEY_PATH_LEN 32
#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH)
// Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r'
#define KEY_PATH_ENTRIES (1 + KEY_PATH_LEN / sizeof(uint32_t))
// Defined as UsbSignHandler.BOGUS_APP_ID_HASH
// in https://github.com/google/u2f-ref-code/blob/master/u2f-chrome-extension/usbsignhandler.js#L118
#define BOGUS_APPID "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
// Auth/Register request state machine
typedef enum {
INIT = 0,
AUTH = 10,
AUTH_PASS = 11,
REG = 20,
REG_PASS = 21
} U2F_STATE;
static U2F_STATE last_req_state = INIT;
typedef struct {
uint8_t reserved;
uint8_t appId[U2F_APPID_SIZE];
uint8_t chal[U2F_CHAL_SIZE];
uint8_t keyHandle[KEY_HANDLE_LEN];
uint8_t pubKey[U2F_PUBKEY_LEN];
} U2F_REGISTER_SIG_STR;
typedef struct {
uint8_t appId[U2F_APPID_SIZE];
uint8_t flags;
uint8_t ctr[4];
uint8_t chal[U2F_CHAL_SIZE];
} U2F_AUTHENTICATE_SIG_STR;
static uint32_t dialog_timeout = 0;
uint32_t next_cid(void)
{
// extremely unlikely but hey
do {
cid = random32();
} while (cid == 0 || cid == CID_BROADCAST);
return cid;
}
typedef struct {
uint8_t buf[57+127*59];
uint8_t *buf_ptr;
uint32_t len;
uint8_t seq;
uint8_t cmd;
} U2F_ReadBuffer;
U2F_ReadBuffer *reader;
void u2fhid_read(char tiny, const U2FHID_FRAME *f)
{
// Always handle init packets directly
if (f->init.cmd == U2FHID_INIT) {
u2fhid_init(f);
if (tiny && reader && f->cid == cid) {
// abort current channel
reader->cmd = 0;
reader->len = 0;
reader->seq = 255;
}
return;
}
if (tiny) {
// read continue packet
if (reader == 0 || cid != f->cid) {
send_u2fhid_error(f->cid, ERR_CHANNEL_BUSY);
return;
}
if ((f->type & TYPE_INIT) && reader->seq == 255) {
u2fhid_init_cmd(f);
return;
}
if (reader->seq != f->cont.seq) {
send_u2fhid_error(f->cid, ERR_INVALID_SEQ);
reader->cmd = 0;
reader->len = 0;
reader->seq = 255;
return;
}
// check out of bounds
if ((reader->buf_ptr - reader->buf) >= (signed) reader->len
|| (reader->buf_ptr + sizeof(f->cont.data) - reader->buf) > (signed) sizeof(reader->buf))
return;
reader->seq++;
memcpy(reader->buf_ptr, f->cont.data, sizeof(f->cont.data));
reader->buf_ptr += sizeof(f->cont.data);
return;
}
u2fhid_read_start(f);
}
void u2fhid_init_cmd(const U2FHID_FRAME *f) {
reader->seq = 0;
reader->buf_ptr = reader->buf;
reader->len = MSG_LEN(*f);
reader->cmd = f->type;
memcpy(reader->buf_ptr, f->init.data, sizeof(f->init.data));
reader->buf_ptr += sizeof(f->init.data);
cid = f->cid;
}
void u2fhid_read_start(const U2FHID_FRAME *f) {
U2F_ReadBuffer readbuffer;
if (!(f->type & TYPE_INIT)) {
return;
}
// Broadcast is reserved for init
if (f->cid == CID_BROADCAST || f->cid == 0) {
send_u2fhid_error(f->cid, ERR_INVALID_CID);
return;
}
if ((unsigned)MSG_LEN(*f) > sizeof(reader->buf)) {
send_u2fhid_error(f->cid, ERR_INVALID_LEN);
return;
}
reader = &readbuffer;
u2fhid_init_cmd(f);
usbTiny(1);
for(;;) {
// Do we need to wait for more data
while ((reader->buf_ptr - reader->buf) < (signed)reader->len) {
uint8_t lastseq = reader->seq;
uint8_t lastcmd = reader->cmd;
int counter = U2F_TIMEOUT;
while (reader->seq == lastseq && reader->cmd == lastcmd) {
if (counter-- == 0) {
// timeout
send_u2fhid_error(cid, ERR_MSG_TIMEOUT);
cid = 0;
reader = 0;
usbTiny(0);
layoutHome();
return;
}
usbPoll();
}
}
// We have all the data
switch (reader->cmd) {
case 0:
// message was aborted by init
break;
case U2FHID_PING:
u2fhid_ping(reader->buf, reader->len);
break;
case U2FHID_MSG:
u2fhid_msg((APDU *)reader->buf, reader->len);
break;
case U2FHID_WINK:
u2fhid_wink(reader->buf, reader->len);
break;
default:
send_u2fhid_error(cid, ERR_INVALID_CMD);
break;
}
// wait for next commmand/ button press
reader->cmd = 0;
reader->seq = 255;
while (dialog_timeout > 0 && reader->cmd == 0) {
dialog_timeout--;
usbPoll(); // may trigger new request
buttonUpdate();
if (button.YesUp &&
(last_req_state == AUTH || last_req_state == REG)) {
last_req_state++;
// standard requires to remember button press for 10 seconds.
dialog_timeout = 10 * U2F_TIMEOUT;
}
}
if (reader->cmd == 0) {
last_req_state = INIT;
cid = 0;
reader = 0;
usbTiny(0);
layoutHome();
return;
}
}
}
void u2fhid_ping(const uint8_t *buf, uint32_t len)
{
debugLog(0, "", "u2fhid_ping");
send_u2fhid_msg(U2FHID_PING, buf, len);
}
void u2fhid_wink(const uint8_t *buf, uint32_t len)
{
debugLog(0, "", "u2fhid_wink");
(void)buf;
if (len > 0)
return send_u2fhid_error(cid, ERR_INVALID_LEN);
if (dialog_timeout > 0)
dialog_timeout = U2F_TIMEOUT;
U2FHID_FRAME f;
MEMSET_BZERO(&f, sizeof(f));
f.cid = cid;
f.init.cmd = U2FHID_WINK;
f.init.bcntl = 0;
queue_u2f_pkt(&f);
}
void u2fhid_init(const U2FHID_FRAME *in)
{
const U2FHID_INIT_REQ *init_req = (const U2FHID_INIT_REQ *)&in->init.data;
U2FHID_FRAME f;
U2FHID_INIT_RESP resp;
debugLog(0, "", "u2fhid_init");
if (in->cid == 0) {
send_u2fhid_error(in->cid, ERR_INVALID_CID);
return;
}
MEMSET_BZERO(&f, sizeof(f));
f.cid = in->cid;
f.init.cmd = U2FHID_INIT;
f.init.bcnth = 0;
f.init.bcntl = U2FHID_INIT_RESP_SIZE;
memcpy(resp.nonce, init_req->nonce, sizeof(init_req->nonce));
resp.cid = in->cid == CID_BROADCAST ? next_cid() : in->cid;
resp.versionInterface = U2FHID_IF_VERSION;
resp.versionMajor = VERSION_MAJOR;
resp.versionMinor = VERSION_MINOR;
resp.versionBuild = VERSION_PATCH;
resp.capFlags = CAPFLAG_WINK;
memcpy(&f.init.data, &resp, sizeof(resp));
queue_u2f_pkt(&f);
}
void queue_u2f_pkt(const U2FHID_FRAME *u2f_pkt)
{
// debugLog(0, "", "u2f_write_pkt");
uint32_t next = (u2f_out_end + 1) % U2F_OUT_PKT_BUFFER_LEN;
if (u2f_out_start == next) {
debugLog(0, "", "u2f_write_pkt full");
return; // Buffer full :(
}
memcpy(u2f_out_packets[u2f_out_end], u2f_pkt, HID_RPT_SIZE);
u2f_out_end = next;
}
uint8_t *u2f_out_data(void)
{
if (u2f_out_start == u2f_out_end)
return NULL; // No data
// debugLog(0, "", "u2f_out_data");
uint32_t t = u2f_out_start;
u2f_out_start = (u2f_out_start + 1) % U2F_OUT_PKT_BUFFER_LEN;
return u2f_out_packets[t];
}
void u2fhid_msg(const APDU *a, uint32_t len)
{
if ((APDU_LEN(*a) + sizeof(APDU)) > len) {
debugLog(0, "", "BAD APDU LENGTH");
debugInt(APDU_LEN(*a));
debugInt(len);
return;
}
if (a->cla != 0) {
send_u2f_error(U2F_SW_CLA_NOT_SUPPORTED);
return;
}
switch (a->ins) {
case U2F_REGISTER:
u2f_register(a);
break;
case U2F_AUTHENTICATE:
u2f_authenticate(a);
break;
case U2F_VERSION:
u2f_version(a);
break;
default:
debugLog(0, "", "u2f unknown cmd");
send_u2f_error(U2F_SW_INS_NOT_SUPPORTED);
}
}
void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, const uint32_t len)
{
U2FHID_FRAME f;
uint8_t *p = (uint8_t *)data;
uint32_t l = len;
uint32_t psz;
uint8_t seq = 0;
// debugLog(0, "", "send_u2fhid_msg");
MEMSET_BZERO(&f, sizeof(f));
f.cid = cid;
f.init.cmd = cmd;
f.init.bcnth = len >> 8;
f.init.bcntl = len & 0xff;
// Init packet
psz = MIN(sizeof(f.init.data), l);
memcpy(f.init.data, p, psz);
queue_u2f_pkt(&f);
l -= psz;
p += psz;
// Cont packet(s)
for (; l > 0; l -= psz, p += psz) {
// debugLog(0, "", "send_u2fhid_msg con");
MEMSET_BZERO(&f.cont.data, sizeof(f.cont.data));
f.cont.seq = seq++;
psz = MIN(sizeof(f.cont.data), l);
memcpy(f.cont.data, p, psz);
queue_u2f_pkt(&f);
}
if (data + len != p) {
debugLog(0, "", "send_u2fhid_msg is bad");
debugInt(data + len - p);
}
}
void send_u2fhid_error(uint32_t fcid, uint8_t err)
{
U2FHID_FRAME f;
MEMSET_BZERO(&f, sizeof(f));
f.cid = fcid;
f.init.cmd = U2FHID_ERROR;
f.init.bcntl = 1;
f.init.data[0] = err;
queue_u2f_pkt(&f);
}
void u2f_version(const APDU *a)
{
if (APDU_LEN(*a) != 0) {
debugLog(0, "", "u2f version - badlen");
send_u2f_error(U2F_SW_WRONG_LENGTH);
return;
}
// INCLUDES SW_NO_ERROR
static const uint8_t version_response[] = {'U', '2', 'F', '_',
'V', '2', 0x90, 0x00};
debugLog(0, "", "u2f version");
send_u2f_msg(version_response, sizeof(version_response));
}
void getReadableAppId(const uint8_t appid[U2F_APPID_SIZE], const char **appname, const BITMAP **appicon) {
unsigned int i;
static char buf[8+2+8+1];
for (i = 0; i < sizeof(u2f_well_known)/sizeof(U2FWellKnown); i++) {
if (memcmp(appid, u2f_well_known[i].appid, U2F_APPID_SIZE) == 0) {
*appname = u2f_well_known[i].appname;
*appicon = u2f_well_known[i].appicon;
return;
}
}
data2hex(appid, 4, &buf[0]);
buf[8] = buf[9] = '.';
data2hex(appid + (U2F_APPID_SIZE - 4), 4, &buf[10]);
*appname = buf;
*appicon = NULL;
}
const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count)
{
static HDNode node;
if (!storage_getRootNode(&node, NIST256P1_NAME, false)) {
layoutHome();
debugLog(0, "", "ERR: Device not init");
return 0;
}
if (!address_n || address_n_count == 0) {
return &node;
}
if (hdnode_private_ckd_cached(&node, address_n, address_n_count, NULL) == 0) {
layoutHome();
debugLog(0, "", "ERR: Derive private failed");
return 0;
}
return &node;
}
const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[])
{
uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN];
// Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r'
uint32_t i, key_path[KEY_PATH_ENTRIES];
key_path[0] = U2F_KEY_PATH;
for (i = 1; i < KEY_PATH_ENTRIES; i++) {
// high bit for hardened keys
key_path[i]= 0x80000000 | random32();
}
// First half of keyhandle is key_path
memcpy(key_handle, &key_path[1], KEY_PATH_LEN);
// prepare keypair from /random data
const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES);
if (!node)
return NULL;
// For second half of keyhandle
// Signature of app_id and random data
memcpy(&keybase[0], app_id, U2F_APPID_SIZE);
memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN);
hmac_sha256(node->private_key, sizeof(node->private_key),
keybase, sizeof(keybase), &key_handle[KEY_PATH_LEN]);
// Done!
return node;
}
const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle[])
{
uint32_t key_path[KEY_PATH_ENTRIES];
key_path[0] = U2F_KEY_PATH;
memcpy(&key_path[1], key_handle, KEY_PATH_LEN);
for (unsigned int i = 1; i < KEY_PATH_ENTRIES; i++) {
// check high bit for hardened keys
if (! (key_path[i] & 0x80000000)) {
return NULL;
}
}
const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES);
if (!node)
return NULL;
uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN];
memcpy(&keybase[0], app_id, U2F_APPID_SIZE);
memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN);
uint8_t hmac[SHA256_DIGEST_LENGTH];
hmac_sha256(node->private_key, sizeof(node->private_key),
keybase, sizeof(keybase), hmac);
if (memcmp(&key_handle[KEY_PATH_LEN], hmac, SHA256_DIGEST_LENGTH) != 0)
return NULL;
// Done!
return node;
}
void u2f_register(const APDU *a)
{
static U2F_REGISTER_REQ last_req;
const U2F_REGISTER_REQ *req = (U2F_REGISTER_REQ *)a->data;
if (!storage_isInitialized()) {
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
return;
}
// Validate basic request parameters
debugLog(0, "", "u2f register");
if (APDU_LEN(*a) != sizeof(U2F_REGISTER_REQ)) {
debugLog(0, "", "u2f register - badlen");
send_u2f_error(U2F_SW_WRONG_LENGTH);
return;
}
// If this request is different from last request, reset state machine
if (memcmp(&last_req, req, sizeof(last_req)) != 0) {
memcpy(&last_req, req, sizeof(last_req));
last_req_state = INIT;
}
// First Time request, return not present and display request dialog
if (last_req_state == INIT) {
// wake up crypto system to be ready for signing
getDerivedNode(NULL, 0);
// error: testof-user-presence is required
buttonUpdate(); // Clear button state
if (0 == memcmp(req->appId, BOGUS_APPID, U2F_APPID_SIZE)) {
layoutDialog(&bmp_icon_warning, NULL, "OK", NULL, "Another U2F device", "was used to register", "in this application.", NULL, NULL, NULL);
} else {
const char *appname;
const BITMAP *appicon;
getReadableAppId(req->appId, &appname, &appicon);
layoutU2FDialog("Register", appname, appicon);
}
last_req_state = REG;
}
// Still awaiting Keypress
if (last_req_state == REG) {
// error: testof-user-presence is required
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
dialog_timeout = U2F_TIMEOUT;
return;
}
// Buttons said yes
if (last_req_state == REG_PASS) {
uint8_t data[sizeof(U2F_REGISTER_RESP) + 2];
U2F_REGISTER_RESP *resp = (U2F_REGISTER_RESP *)&data;
MEMSET_BZERO(data, sizeof(data));
resp->registerId = U2F_REGISTER_ID;
resp->keyHandleLen = KEY_HANDLE_LEN;
// Generate keypair for this appId
const HDNode *node =
generateKeyHandle(req->appId, (uint8_t*)&resp->keyHandleCertSig);
if (!node) {
debugLog(0, "", "getDerivedNode Fail");
send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle
return;
}
ecdsa_get_public_key65(node->curve->params, node->private_key,
(uint8_t *)&resp->pubKey);
memcpy(resp->keyHandleCertSig + resp->keyHandleLen,
U2F_ATT_CERT, sizeof(U2F_ATT_CERT));
uint8_t sig[64];
U2F_REGISTER_SIG_STR sig_base;
sig_base.reserved = 0;
memcpy(sig_base.appId, req->appId, U2F_APPID_SIZE);
memcpy(sig_base.chal, req->chal, U2F_CHAL_SIZE);
memcpy(sig_base.keyHandle, &resp->keyHandleCertSig, KEY_HANDLE_LEN);
memcpy(sig_base.pubKey, &resp->pubKey, U2F_PUBKEY_LEN);
if (ecdsa_sign(&nist256p1, U2F_ATT_PRIV_KEY, (uint8_t *)&sig_base, sizeof(sig_base), sig, NULL, NULL) != 0) {
send_u2f_error(U2F_SW_WRONG_DATA);
return;
}
// Where to write the signature in the response
uint8_t *resp_sig = resp->keyHandleCertSig +
resp->keyHandleLen + sizeof(U2F_ATT_CERT);
// Convert to der for the response
const uint8_t sig_len = ecdsa_sig_to_der(sig, resp_sig);
// Append success bytes
memcpy(resp->keyHandleCertSig + resp->keyHandleLen +
sizeof(U2F_ATT_CERT) + sig_len,
"\x90\x00", 2);
int l = 1 /* registerId */ + U2F_PUBKEY_LEN +
1 /* keyhandleLen */ + resp->keyHandleLen +
sizeof(U2F_ATT_CERT) + sig_len + 2;
last_req_state = INIT;
dialog_timeout = 0;
send_u2f_msg(data, l);
return;
}
// Didnt expect to get here
dialog_timeout = 0;
}
void u2f_authenticate(const APDU *a)
{
const U2F_AUTHENTICATE_REQ *req = (U2F_AUTHENTICATE_REQ *)a->data;
static U2F_AUTHENTICATE_REQ last_req;
if (!storage_isInitialized()) {
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
return;
}
if (APDU_LEN(*a) < 64) { /// FIXME: decent value
debugLog(0, "", "u2f authenticate - badlen");
send_u2f_error(U2F_SW_WRONG_LENGTH);
return;
}
if (req->keyHandleLen != KEY_HANDLE_LEN) {
debugLog(0, "", "u2f auth - bad keyhandle len");
send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle
return;
}
const HDNode *node =
validateKeyHandle(req->appId, req->keyHandle);
if (!node) {
debugLog(0, "", "u2f auth - bad keyhandle len");
send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle
return;
}
if (a->p1 == U2F_AUTH_CHECK_ONLY) {
debugLog(0, "", "u2f authenticate check");
// This is a success for a good keyhandle
// A failed check would have happened earlier
// error: testof-user-presence is required
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
return;
}
if (a->p1 != U2F_AUTH_ENFORCE) {
debugLog(0, "", "u2f authenticate unknown");
// error:bad key handle
send_u2f_error(U2F_SW_WRONG_DATA);
return;
}
debugLog(0, "", "u2f authenticate enforce");
if (memcmp(&last_req, req, sizeof(last_req)) != 0) {
memcpy(&last_req, req, sizeof(last_req));
last_req_state = INIT;
}
if (last_req_state == INIT) {
// error: testof-user-presence is required
buttonUpdate(); // Clear button state
const char *appname;
const BITMAP *appicon;
getReadableAppId(req->appId, &appname, &appicon);
layoutU2FDialog("Authenticate", appname, appicon);
last_req_state = AUTH;
}
// Awaiting Keypress
if (last_req_state == AUTH) {
// error: testof-user-presence is required
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
dialog_timeout = U2F_TIMEOUT;
return;
}
// Buttons said yes
if (last_req_state == AUTH_PASS) {
uint8_t buf[sizeof(U2F_AUTHENTICATE_RESP) + 2];
U2F_AUTHENTICATE_RESP *resp =
(U2F_AUTHENTICATE_RESP *)&buf;
const uint32_t ctr = storage_nextU2FCounter();
resp->flags = U2F_AUTH_FLAG_TUP;
resp->ctr[0] = ctr >> 24 & 0xff;
resp->ctr[1] = ctr >> 16 & 0xff;
resp->ctr[2] = ctr >> 8 & 0xff;
resp->ctr[3] = ctr & 0xff;
// Build and sign response
U2F_AUTHENTICATE_SIG_STR sig_base;
uint8_t sig[64];
memcpy(sig_base.appId, req->appId, U2F_APPID_SIZE);
sig_base.flags = resp->flags;
memcpy(sig_base.ctr, resp->ctr, 4);
memcpy(sig_base.chal, req->chal, U2F_CHAL_SIZE);
if (ecdsa_sign(&nist256p1, node->private_key, (uint8_t *)&sig_base, sizeof(sig_base), sig, NULL, NULL) != 0) {
send_u2f_error(U2F_SW_WRONG_DATA);
return;
}
// Copy DER encoded signature into response
const uint8_t sig_len = ecdsa_sig_to_der(sig, resp->sig);
// Append OK
memcpy(buf + sizeof(U2F_AUTHENTICATE_RESP) -
U2F_MAX_EC_SIG_SIZE + sig_len,
"\x90\x00", 2);
last_req_state = INIT;
dialog_timeout = 0;
send_u2f_msg(buf, sizeof(U2F_AUTHENTICATE_RESP) -
U2F_MAX_EC_SIG_SIZE + sig_len +
2);
}
}
void send_u2f_error(const uint16_t err)
{
uint8_t data[2];
data[0] = err >> 8 & 0xFF;
data[1] = err & 0xFF;
send_u2f_msg(data, 2);
}
void send_u2f_msg(const uint8_t *data, const uint32_t len)
{
send_u2fhid_msg(U2FHID_MSG, data, len);
}