mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 15:38:11 +00:00
Add U2F support
This commit is contained in:
parent
7cd63ac53d
commit
9328cad7f1
@ -3,6 +3,7 @@ APPVER = 1.0.0
|
||||
NAME = trezor
|
||||
|
||||
OBJS += usb.o
|
||||
OBJS += u2f.o
|
||||
OBJS += messages.o
|
||||
OBJS += storage.o
|
||||
OBJS += trezor.o
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
|
||||
const pb_field_t Storage_fields[11] = {
|
||||
const pb_field_t Storage_fields[12] = {
|
||||
PB_FIELD2( 1, UINT32 , REQUIRED, STATIC , FIRST, Storage, version, version, 0),
|
||||
PB_FIELD2( 2, MESSAGE , OPTIONAL, STATIC , OTHER, Storage, node, version, &HDNodeType_fields),
|
||||
PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, Storage, mnemonic, node, 0),
|
||||
@ -16,6 +16,7 @@ const pb_field_t Storage_fields[11] = {
|
||||
PB_FIELD2( 8, STRING , OPTIONAL, STATIC , OTHER, Storage, label, language, 0),
|
||||
PB_FIELD2( 9, BOOL , OPTIONAL, STATIC , OTHER, Storage, imported, label, 0),
|
||||
PB_FIELD2( 10, BYTES , OPTIONAL, STATIC , OTHER, Storage, homescreen, imported, 0),
|
||||
PB_FIELD2( 11, UINT32 , OPTIONAL, STATIC , OTHER, Storage, u2f_counter, homescreen, 0),
|
||||
PB_LAST_FIELD
|
||||
};
|
||||
|
||||
|
@ -37,13 +37,15 @@ typedef struct _Storage {
|
||||
bool imported;
|
||||
bool has_homescreen;
|
||||
Storage_homescreen_t homescreen;
|
||||
bool has_u2f_counter;
|
||||
uint32_t u2f_counter;
|
||||
} Storage;
|
||||
|
||||
/* Default values for struct fields */
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define Storage_init_default {0, false, HDNodeType_init_default, false, "", false, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}}
|
||||
#define Storage_init_zero {0, false, HDNodeType_init_zero, false, "", false, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}}
|
||||
#define Storage_init_default {0, false, HDNodeType_init_default, false, "", false, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0}
|
||||
#define Storage_init_zero {0, false, HDNodeType_init_zero, false, "", false, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define Storage_version_tag 1
|
||||
@ -56,12 +58,13 @@ typedef struct _Storage {
|
||||
#define Storage_label_tag 8
|
||||
#define Storage_imported_tag 9
|
||||
#define Storage_homescreen_tag 10
|
||||
#define Storage_u2f_counter_tag 11
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
extern const pb_field_t Storage_fields[11];
|
||||
extern const pb_field_t Storage_fields[12];
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define Storage_size (1359 + HDNodeType_size)
|
||||
#define Storage_size (1365 + HDNodeType_size)
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
@ -401,3 +401,15 @@ bool storage_isInitialized(void)
|
||||
{
|
||||
return storage.has_node || storage.has_mnemonic;
|
||||
}
|
||||
|
||||
uint32_t storage_nextU2FCounter(void)
|
||||
{
|
||||
if(!storage.has_u2f_counter) {
|
||||
storage.has_u2f_counter = true;
|
||||
storage.u2f_counter = 1;
|
||||
} else {
|
||||
storage.u2f_counter++;
|
||||
}
|
||||
storage_commit();
|
||||
return storage.u2f_counter;
|
||||
}
|
||||
|
@ -58,6 +58,8 @@ void storage_resetPinFails(void);
|
||||
void storage_increasePinFails(void);
|
||||
uint32_t storage_getPinFails(void);
|
||||
|
||||
uint32_t storage_nextU2FCounter(void);
|
||||
|
||||
bool storage_isInitialized(void);
|
||||
|
||||
extern Storage storage;
|
||||
|
725
firmware/u2f.c
Normal file
725
firmware/u2f.c
Normal file
@ -0,0 +1,725 @@
|
||||
/*
|
||||
* 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 "nist256p1.h"
|
||||
#include "rng.h"
|
||||
|
||||
#include "u2f/u2f.h"
|
||||
#include "u2f/u2f_hid.h"
|
||||
#include "u2f/u2f_keys.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 840000/2
|
||||
#define U2F_OUT_PKT_BUFFER_LEN 128
|
||||
|
||||
// Initialise without a cid
|
||||
static uint32_t cid = CID_BROADCAST;
|
||||
|
||||
// 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_HANDLE_LEN 64
|
||||
|
||||
// Auth/Register request state machine
|
||||
typedef enum {
|
||||
INIT = 0,
|
||||
BTN_NO = 1,
|
||||
BTN_YES = 2,
|
||||
AUTH = 10,
|
||||
AUTH_FAIL = 11,
|
||||
AUTH_PASS = 12,
|
||||
REG = 20,
|
||||
REG_FAIL = 21,
|
||||
REG_PASS = 22
|
||||
} 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;
|
||||
|
||||
uint8_t buttonState(void)
|
||||
{
|
||||
buttonUpdate();
|
||||
|
||||
if ((button.NoDown > 10) || button.NoUp)
|
||||
return BTN_NO;
|
||||
if ((button.YesDown > 10) || button.YesUp)
|
||||
return BTN_YES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void int2hex(uint8_t *dst, const uint32_t i)
|
||||
{
|
||||
dst[0] = '0' + ((i >> 28) & 0x0F);
|
||||
dst[1] = '0' + ((i >> 24) & 0x0F);
|
||||
dst[2] = '0' + ((i >> 20) & 0x0F);
|
||||
dst[3] = '0' + ((i >> 16) & 0x0F);
|
||||
dst[4] = '0' + ((i >> 12) & 0x0F);
|
||||
dst[5] = '0' + ((i >> 8) & 0x0F);
|
||||
dst[6] = '0' + ((i >> 4) & 0x0F);
|
||||
dst[7] = '0' + (i & 0x0F);
|
||||
dst[8] = '\0';
|
||||
|
||||
int t = 0;
|
||||
for (; t < 8; t++) {
|
||||
if (dst[t] > '9')
|
||||
dst[t] += 7; // 'A'-'9'+1
|
||||
}
|
||||
}
|
||||
|
||||
char *debugInt(const uint32_t i)
|
||||
{
|
||||
static uint8_t n = 0;
|
||||
static uint8_t id[8][9];
|
||||
int2hex(id[n], i);
|
||||
debugLog(0, "", (const char *)id[n]);
|
||||
char *ret = (char *)id[n];
|
||||
n = (n + 1) % 8;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t dialog_timeout = 0;
|
||||
|
||||
void LayoutHomeAfterTimeout(void)
|
||||
{
|
||||
static bool timeoutLock = false;
|
||||
|
||||
if (timeoutLock || dialog_timeout == 0)
|
||||
return; // Dialog has cleared or already in loop
|
||||
|
||||
timeoutLock = true;
|
||||
U2F_STATE rs = last_req_state;
|
||||
U2F_STATE bs = INIT;
|
||||
while (dialog_timeout-- && rs == last_req_state && bs == 0) {
|
||||
usbPoll(); // may trigger new request
|
||||
bs = buttonState();
|
||||
}
|
||||
timeoutLock = false;
|
||||
|
||||
if (rs != last_req_state)
|
||||
return; // Reset by new request don't clear screen
|
||||
|
||||
if (dialog_timeout == 0) {
|
||||
last_req_state += BTN_NO; // Timeout is like button no
|
||||
}
|
||||
else {
|
||||
last_req_state += bs;
|
||||
dialog_timeout = 0;
|
||||
}
|
||||
|
||||
layoutHome();
|
||||
}
|
||||
|
||||
uint32_t next_cid(void)
|
||||
{
|
||||
// extremely unlikely but hey
|
||||
do {
|
||||
cid = random32();
|
||||
} while (cid == 0 || cid == CID_BROADCAST);
|
||||
return cid;
|
||||
}
|
||||
|
||||
void u2fhid_read(const U2FHID_FRAME *f)
|
||||
{
|
||||
static uint8_t seq, cmd;
|
||||
static uint32_t len;
|
||||
static uint8_t *buf_ptr;
|
||||
static uint8_t buf[7609];
|
||||
|
||||
if ((f->cid != CID_BROADCAST) && (f->cid != cid)) {
|
||||
return; // Not for us
|
||||
}
|
||||
|
||||
if (f->type & TYPE_INIT) {
|
||||
seq = 0;
|
||||
buf_ptr = buf;
|
||||
len = MSG_LEN(*f);
|
||||
cmd = f->type;
|
||||
memcpy(buf_ptr, f->init.data, sizeof(f->init.data));
|
||||
buf_ptr += sizeof(f->init.data);
|
||||
}
|
||||
else {
|
||||
if (f->cont.seq == seq) {
|
||||
seq++;
|
||||
memcpy(buf_ptr, f->cont.data, sizeof(f->cont.data));
|
||||
buf_ptr += sizeof(f->cont.data);
|
||||
}
|
||||
else {
|
||||
return send_u2fhid_error(ERR_INVALID_SEQ);
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast is reserved for init
|
||||
if (cid == CID_BROADCAST && cmd != U2FHID_INIT)
|
||||
return;
|
||||
|
||||
// Check length isnt bigger than spec max
|
||||
if (len > sizeof(buf))
|
||||
return send_u2fhid_error(ERR_INVALID_LEN);
|
||||
|
||||
// Do we need to wait for more data
|
||||
if ((buf_ptr - buf) < (signed)len) {
|
||||
// debugLog(0, "", "u2fhid_read wait");
|
||||
return;
|
||||
}
|
||||
|
||||
// We have all the data
|
||||
switch (cmd) {
|
||||
case U2FHID_PING:
|
||||
u2fhid_ping(buf, len);
|
||||
break;
|
||||
case U2FHID_MSG:
|
||||
u2fhid_msg((APDU *)buf, len);
|
||||
break;
|
||||
case U2FHID_LOCK:
|
||||
u2fhid_lock(buf, len);
|
||||
break;
|
||||
case U2FHID_INIT:
|
||||
u2fhid_init((const U2FHID_INIT_REQ *)buf);
|
||||
break;
|
||||
case U2FHID_WINK:
|
||||
u2fhid_wink(buf, len);
|
||||
break;
|
||||
// case U2FHID_SYNC:
|
||||
// u2fhid_sync(buf, len);
|
||||
break;
|
||||
default:
|
||||
send_u2fhid_error(ERR_INVALID_CMD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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(ERR_INVALID_LEN);
|
||||
|
||||
if (dialog_timeout > 0)
|
||||
dialog_timeout = U2F_TIMEOUT;
|
||||
|
||||
U2FHID_FRAME f;
|
||||
bzero(&f, sizeof(f));
|
||||
f.cid = cid;
|
||||
f.init.cmd = U2FHID_WINK;
|
||||
f.init.bcntl = 0;
|
||||
queue_u2f_pkt(&f);
|
||||
}
|
||||
|
||||
void u2fhid_sync(const uint8_t *buf, uint32_t len)
|
||||
{
|
||||
debugLog(0, "", "u2fhid_sync");
|
||||
(void)buf;
|
||||
|
||||
if (len > 0)
|
||||
return send_u2fhid_error(ERR_INVALID_LEN);
|
||||
|
||||
// Abort things.
|
||||
dialog_timeout = 0;
|
||||
}
|
||||
|
||||
void u2fhid_lock(const uint8_t *buf, uint32_t len)
|
||||
{
|
||||
debugLog(0, "", "u2fhid_lock");
|
||||
(void)buf;
|
||||
(void)len;
|
||||
send_u2fhid_error(ERR_INVALID_CMD);
|
||||
}
|
||||
|
||||
void u2fhid_init(const U2FHID_INIT_REQ *init_req)
|
||||
{
|
||||
debugLog(0, "", "u2fhid_init");
|
||||
U2FHID_FRAME f;
|
||||
U2FHID_INIT_RESP *resp = (U2FHID_INIT_RESP *)f.init.data;
|
||||
|
||||
bzero(&f, sizeof(f));
|
||||
f.cid = CID_BROADCAST;
|
||||
f.init.cmd = U2FHID_INIT;
|
||||
f.init.bcnth = 0;
|
||||
f.init.bcntl = sizeof(U2FHID_INIT_RESP);
|
||||
|
||||
memcpy(resp->nonce, init_req->nonce, sizeof(init_req->nonce));
|
||||
resp->cid = next_cid();
|
||||
resp->versionInterface = U2FHID_IF_VERSION;
|
||||
resp->versionMajor = VERSION_MAJOR;
|
||||
resp->versionMinor = VERSION_MINOR;
|
||||
resp->versionBuild = VERSION_PATCH;
|
||||
resp->capFlags = CAPFLAG_WINK;
|
||||
|
||||
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)
|
||||
{
|
||||
static bool lock = false;
|
||||
|
||||
if ((APDU_LEN(*a) + sizeof(APDU)) > len) {
|
||||
debugLog(0, "", "BAD APDU LENGTH");
|
||||
debugInt(APDU_LEN(*a));
|
||||
debugInt(len);
|
||||
return;
|
||||
}
|
||||
|
||||
// Very crude locking, incase another message comes in while we wait. This
|
||||
// actually can probably be removed as no code inside calls usbPoll anymore
|
||||
if (lock)
|
||||
return send_u2fhid_error(ERR_CHANNEL_BUSY);
|
||||
|
||||
lock = true;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
lock = false;
|
||||
|
||||
LayoutHomeAfterTimeout();
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
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");
|
||||
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(uint8_t err)
|
||||
{
|
||||
U2FHID_FRAME f;
|
||||
|
||||
bzero(&f, sizeof(f));
|
||||
f.cid = cid;
|
||||
f.init.cmd = U2FHID_ERROR;
|
||||
f.init.bcntl = 1;
|
||||
f.init.data[0] = err;
|
||||
queue_u2f_pkt(&f);
|
||||
}
|
||||
|
||||
void u2f_version(const APDU *a)
|
||||
{
|
||||
// INCLUDES SW_NO_ERROR
|
||||
static const uint8_t version_response[] = {'U', '2', 'F', '_',
|
||||
'V', '2', 0x90, 0x00};
|
||||
(void)a;
|
||||
debugLog(0, "", "u2f version");
|
||||
send_u2f_msg(version_response, sizeof(version_response));
|
||||
}
|
||||
|
||||
const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count)
|
||||
{
|
||||
static HDNode node;
|
||||
if (!storage_getRootNode(&node)) {
|
||||
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) == 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[64];
|
||||
|
||||
// First half of keyhandle is random
|
||||
random_buffer(key_handle, 32);
|
||||
|
||||
// prepare keypair from /random data
|
||||
const HDNode *node =
|
||||
getDerivedNode((uint32_t*)key_handle, 32/sizeof(uint32_t));
|
||||
|
||||
// For second half of keyhandle
|
||||
// Signature of app_id and random data
|
||||
memcpy(&keybase[0], app_id, 32);
|
||||
memcpy(&keybase[32], key_handle, 32);
|
||||
uint8_t sig[64];
|
||||
ecdsa_sign(&nist256p1, node->private_key,
|
||||
(uint8_t *)&keybase, sizeof(keybase), sig,
|
||||
NULL);
|
||||
|
||||
// Copy 32 bytes of signature into keyhandle
|
||||
memcpy(&key_handle[32], sig, 32);
|
||||
|
||||
// Done!
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle[])
|
||||
{
|
||||
uint8_t keybase[64];
|
||||
memcpy(&keybase[0], app_id, 32);
|
||||
memcpy(&keybase[32], key_handle, 32);
|
||||
|
||||
const HDNode *node =
|
||||
getDerivedNode((uint32_t*)key_handle, 32/sizeof(uint32_t));
|
||||
|
||||
uint8_t sig[64];
|
||||
ecdsa_sign(&nist256p1, node->private_key,
|
||||
(uint8_t *)&keybase, sizeof(keybase), sig,
|
||||
NULL);
|
||||
|
||||
if (memcmp(&key_handle[32], sig, 32) !=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;
|
||||
|
||||
// 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_DATA);
|
||||
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 == 0) {
|
||||
// wake up crypto system to be ready for signing
|
||||
getDerivedNode(NULL, 0);
|
||||
// error: testof-user-presence is required
|
||||
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
|
||||
buttonUpdate(); // Clear button state
|
||||
layoutDialog(DIALOG_ICON_QUESTION, "Cancel", "Register",
|
||||
NULL, "Register U2F", "security key", "", "", "", NULL);
|
||||
dialog_timeout = U2F_TIMEOUT;
|
||||
last_req_state = REG;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 no!
|
||||
if (last_req_state == REG_FAIL) {
|
||||
send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle
|
||||
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;
|
||||
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(&nist256p1, 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);
|
||||
ecdsa_sign(&nist256p1, U2F_ATT_PRIV_KEY, (uint8_t *)&sig_base,
|
||||
sizeof(sig_base), sig, NULL);
|
||||
|
||||
// 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;
|
||||
|
||||
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 (APDU_LEN(*a) < 64) { /// FIXME: decent value
|
||||
debugLog(0, "", "u2f authenticate - badlen");
|
||||
send_u2f_error(U2F_SW_WRONG_DATA);
|
||||
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
|
||||
send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED);
|
||||
buttonUpdate(); // Clear button state
|
||||
layoutDialog(DIALOG_ICON_QUESTION, "Cancel", "Authenticate", NULL,
|
||||
"Authenticate U2F", "security key", "", "", "", NULL);
|
||||
dialog_timeout = U2F_TIMEOUT;
|
||||
last_req_state = AUTH;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 no!
|
||||
if (last_req_state == AUTH_FAIL) {
|
||||
send_u2f_error(
|
||||
U2F_SW_WRONG_DATA); // error:bad key handle
|
||||
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);
|
||||
ecdsa_sign(&nist256p1, node->private_key,
|
||||
(uint8_t *)&sig_base, sizeof(sig_base), sig,
|
||||
NULL);
|
||||
|
||||
// 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);
|
||||
send_u2f_msg(buf, sizeof(U2F_AUTHENTICATE_RESP) -
|
||||
U2F_MAX_EC_SIG_SIZE + sig_len +
|
||||
2);
|
||||
last_req_state = INIT;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
58
firmware/u2f.h
Normal file
58
firmware/u2f.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __U2F_H__
|
||||
#define __U2F_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "u2f/u2f_hid.h"
|
||||
#include "trezor.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t cla, ins, p1, p2;
|
||||
uint8_t lc1, lc2, lc3;
|
||||
uint8_t data[];
|
||||
} APDU;
|
||||
|
||||
#define APDU_LEN(A) (uint32_t)(((A).lc1 << 16) + ((A).lc2 << 8) + ((A).lc3))
|
||||
|
||||
void u2fhid_read(const U2FHID_FRAME *buf);
|
||||
bool u2fhid_write(uint8_t *buf);
|
||||
void u2fhid_init(const U2FHID_INIT_REQ *init_req);
|
||||
void u2fhid_ping(const uint8_t *buf, uint32_t len);
|
||||
void u2fhid_wink(const uint8_t *buf, uint32_t len);
|
||||
void u2fhid_sync(const uint8_t *buf, uint32_t len);
|
||||
void u2fhid_lock(const uint8_t *buf, uint32_t len);
|
||||
void u2fhid_msg(const APDU *a, uint32_t len);
|
||||
void queue_u2f_pkt(const U2FHID_FRAME *u2f_pkt);
|
||||
|
||||
uint8_t *u2f_out_data(void);
|
||||
void u2f_register(const APDU *a);
|
||||
void u2f_version(const APDU *a);
|
||||
void u2f_authenticate(const APDU *a);
|
||||
|
||||
void send_u2f_msg(const uint8_t *data, const uint32_t len);
|
||||
void send_u2f_error(const uint16_t err);
|
||||
|
||||
void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data,
|
||||
const uint32_t len);
|
||||
void send_u2fhid_error(const uint8_t err);
|
||||
|
||||
#endif
|
141
firmware/u2f/u2f.h
Normal file
141
firmware/u2f/u2f.h
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright (C) 2013-2015 Yubico AB
|
||||
|
||||
This program 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 2.1, 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 Lesser
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Common U2F raw message format header.
|
||||
// 2014-08-14 J Ehrensvard, Yubico, Inc.
|
||||
|
||||
#ifndef __U2F_H_INCLUDED__
|
||||
#define __U2F_H_INCLUDED__
|
||||
|
||||
#ifdef _MSC_VER // Windows
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long int uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// General constants
|
||||
|
||||
#define U2F_EC_KEY_SIZE 32 // EC key size in bytes
|
||||
#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) // Size of EC point
|
||||
#define U2F_MAX_KH_SIZE 128 // Max size of key handle
|
||||
#define U2F_MAX_ATT_CERT_SIZE 1024 // Max size of attestation certificate
|
||||
#define U2F_MAX_EC_SIG_SIZE 72 // Max size of DER coded EC signature
|
||||
#define U2F_CTR_SIZE 4 // Size of counter field
|
||||
#define U2F_APPID_SIZE 32 // Size of application id
|
||||
#define U2F_CHAL_SIZE 32 // Size of challenge
|
||||
|
||||
#define ENC_SIZE(x) ((x + 7) & 0xfff8)
|
||||
|
||||
// EC (uncompressed) point
|
||||
|
||||
#define U2F_POINT_UNCOMPRESSED 0x04 // Uncompressed point format
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t pointFormat; // Point type
|
||||
uint8_t x[U2F_EC_KEY_SIZE]; // X-value
|
||||
uint8_t y[U2F_EC_KEY_SIZE]; // Y-value
|
||||
} U2F_EC_POINT;
|
||||
|
||||
// U2F native commands
|
||||
|
||||
#define U2F_REGISTER 0x01 // Registration command
|
||||
#define U2F_AUTHENTICATE 0x02 // Authenticate/sign command
|
||||
#define U2F_VERSION 0x03 // Read version string command
|
||||
|
||||
#define U2F_VENDOR_FIRST 0x40 // First vendor defined command
|
||||
#define U2F_VENDOR_LAST 0x7f // Last vendor defined command
|
||||
|
||||
// U2F_CMD_REGISTER command defines
|
||||
|
||||
#define U2F_REGISTER_ID 0x05 // Version 2 registration identifier
|
||||
#define U2F_REGISTER_HASH_ID 0x00 // Version 2 hash identintifier
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t chal[U2F_CHAL_SIZE]; // Challenge
|
||||
uint8_t appId[U2F_APPID_SIZE]; // Application id
|
||||
} U2F_REGISTER_REQ;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t registerId; // Registration identifier (U2F_REGISTER_ID_V2)
|
||||
U2F_EC_POINT pubKey; // Generated public key
|
||||
uint8_t keyHandleLen; // Length of key handle
|
||||
uint8_t keyHandleCertSig[U2F_MAX_KH_SIZE + // Key handle
|
||||
U2F_MAX_ATT_CERT_SIZE + // Attestation certificate
|
||||
U2F_MAX_EC_SIG_SIZE]; // Registration signature
|
||||
} U2F_REGISTER_RESP;
|
||||
|
||||
// U2F_CMD_AUTHENTICATE command defines
|
||||
|
||||
// Authentication control byte
|
||||
|
||||
#define U2F_AUTH_ENFORCE 0x03 // Enforce user presence and sign
|
||||
#define U2F_AUTH_CHECK_ONLY 0x07 // Check only
|
||||
#define U2F_AUTH_FLAG_TUP 0x01 // Test of user presence set
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t chal[U2F_CHAL_SIZE]; // Challenge
|
||||
uint8_t appId[U2F_APPID_SIZE]; // Application id
|
||||
uint8_t keyHandleLen; // Length of key handle
|
||||
uint8_t keyHandle[U2F_MAX_KH_SIZE]; // Key handle
|
||||
} U2F_AUTHENTICATE_REQ;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t flags; // U2F_AUTH_FLAG_ values
|
||||
uint8_t ctr[U2F_CTR_SIZE]; // Counter field (big-endian)
|
||||
uint8_t sig[U2F_MAX_EC_SIG_SIZE]; // Signature
|
||||
} U2F_AUTHENTICATE_RESP;
|
||||
|
||||
// Common raw message format (ISO7816-4:2005 mapping)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t cla; // Class - reserved
|
||||
uint8_t ins; // U2F instruction
|
||||
uint8_t p1; // U2F parameter 1
|
||||
uint8_t p2; // U2F parameter 2
|
||||
uint8_t lc1; // Length field, set to zero
|
||||
uint8_t lc2; // Length field, MSB
|
||||
uint8_t lc3; // Length field, LSB
|
||||
uint8_t data[1]; // Data field
|
||||
} U2F_MSG;
|
||||
|
||||
// Command status responses
|
||||
|
||||
#define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR
|
||||
#define U2F_SW_WRONG_DATA 0x6984 // SW_WRONG_DATA
|
||||
#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED
|
||||
#define U2F_SW_INS_NOT_SUPPORTED 0x6d00 // SW_INS_NOT_SUPPORTED
|
||||
#define U2F_SW_CLA_NOT_SUPPORTED 0x6e00 // SW_CLA_NOT_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __U2F_H_INCLUDED__
|
140
firmware/u2f/u2f_hid.h
Normal file
140
firmware/u2f/u2f_hid.h
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright (C) 2013-2015 Yubico AB
|
||||
|
||||
This program 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 2.1, 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 Lesser
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Common U2F HID transport header.
|
||||
// 2014-08-21 J Ehrensvard, Yubico, Inc.
|
||||
|
||||
#ifndef __U2FHID_H_INCLUDED__
|
||||
#define __U2FHID_H_INCLUDED__
|
||||
|
||||
#ifdef _MSC_VER // Windows
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long int uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// Size of HID reports
|
||||
|
||||
#define HID_RPT_SIZE 64 // Default size of raw HID report
|
||||
|
||||
// Frame layout - command- and continuation frames
|
||||
|
||||
#define CID_BROADCAST 0xffffffff // Broadcast channel id
|
||||
|
||||
#define TYPE_MASK 0x80 // Frame type mask
|
||||
#define TYPE_INIT 0x80 // Initial frame identifier
|
||||
#define TYPE_CONT 0x00 // Continuation frame identifier
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t cid; // Channel identifier
|
||||
union
|
||||
{
|
||||
uint8_t type; // Frame type - b7 defines type
|
||||
struct
|
||||
{
|
||||
uint8_t cmd; // Command - b7 set
|
||||
uint8_t bcnth; // Message byte count - high part
|
||||
uint8_t bcntl; // Message byte count - low part
|
||||
uint8_t data[HID_RPT_SIZE - 7]; // Data payload
|
||||
} init;
|
||||
struct
|
||||
{
|
||||
uint8_t seq; // Sequence number - b7 cleared
|
||||
uint8_t data[HID_RPT_SIZE - 5]; // Data payload
|
||||
} cont;
|
||||
};
|
||||
} U2FHID_FRAME;
|
||||
|
||||
#define FRAME_TYPE(f) ((f).type & TYPE_MASK)
|
||||
#define FRAME_CMD(f) ((f).init.cmd & ~TYPE_MASK)
|
||||
#define MSG_LEN(f) (((f).init.bcnth << 8) + (f).init.bcntl)
|
||||
#define FRAME_SEQ(f) ((f).cont.seq & ~TYPE_MASK)
|
||||
|
||||
// HID usage- and usage-page definitions
|
||||
|
||||
#define FIDO_USAGE_PAGE 0xf1d0 // FIDO alliance HID usage page
|
||||
#define FIDO_USAGE_U2FHID 0x01 // U2FHID usage for top-level collection
|
||||
#define FIDO_USAGE_DATA_IN 0x20 // Raw IN data report
|
||||
#define FIDO_USAGE_DATA_OUT 0x21 // Raw OUT data report
|
||||
|
||||
// General constants
|
||||
|
||||
#define U2FHID_IF_VERSION 2 // Current interface implementation version
|
||||
#define U2FHID_FRAME_TIMEOUT 500 // Default frame timeout in ms
|
||||
#define U2FHID_TRANS_TIMEOUT 3000 // Default message timeout in ms
|
||||
|
||||
// U2FHID native commands
|
||||
|
||||
#define U2FHID_PING (TYPE_INIT | 0x01) // Echo data through local processor only
|
||||
#define U2FHID_MSG (TYPE_INIT | 0x03) // Send U2F message frame
|
||||
#define U2FHID_LOCK (TYPE_INIT | 0x04) // Send lock channel command
|
||||
#define U2FHID_INIT (TYPE_INIT | 0x06) // Channel initialization
|
||||
#define U2FHID_WINK (TYPE_INIT | 0x08) // Send device identification wink
|
||||
#define U2FHID_ERROR (TYPE_INIT | 0x3f) // Error response
|
||||
|
||||
#define U2FHID_VENDOR_FIRST (TYPE_INIT | 0x40) // First vendor defined command
|
||||
#define U2FHID_VENDOR_LAST (TYPE_INIT | 0x7f) // Last vendor defined command
|
||||
|
||||
// U2FHID_INIT command defines
|
||||
|
||||
#define INIT_NONCE_SIZE 8 // Size of channel initialization challenge
|
||||
#define CAPFLAG_WINK 0x01 // Device supports WINK command
|
||||
#define CAPFLAG_LOCK 0x02 // Device supports LOCK command
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce
|
||||
} U2FHID_INIT_REQ;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce
|
||||
uint32_t cid; // Channel identifier
|
||||
uint8_t versionInterface; // Interface version
|
||||
uint8_t versionMajor; // Major version number
|
||||
uint8_t versionMinor; // Minor version number
|
||||
uint8_t versionBuild; // Build version number
|
||||
uint8_t capFlags; // Capabilities flags
|
||||
} U2FHID_INIT_RESP;
|
||||
|
||||
// Low-level error codes. Return as negatives.
|
||||
|
||||
#define ERR_NONE 0x00 // No error
|
||||
#define ERR_INVALID_CMD 0x01 // Invalid command
|
||||
#define ERR_INVALID_PAR 0x02 // Invalid parameter
|
||||
#define ERR_INVALID_LEN 0x03 // Invalid message length
|
||||
#define ERR_INVALID_SEQ 0x04 // Invalid message sequencing
|
||||
#define ERR_MSG_TIMEOUT 0x05 // Message has timed out
|
||||
#define ERR_CHANNEL_BUSY 0x06 // Channel busy
|
||||
#define ERR_LOCK_REQUIRED 0x0a // Command requires channel lock
|
||||
#define ERR_INVALID_CID 0x0b // Command not allowed on this cid
|
||||
#define ERR_OTHER 0x7f // Other unspecified error
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __U2FHID_H_INCLUDED__
|
46
firmware/u2f/u2f_keys.h
Normal file
46
firmware/u2f/u2f_keys.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef __U2F_KEYS_H_INCLUDED__
|
||||
#define __U2F_KEYS_H_INCLUDED__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint8_t U2F_ATT_PRIV_KEY[] = {0x71, 0x26, 0xac, 0x2b, 0xf6, 0x44, 0xdc, 0x61,
|
||||
0x86, 0xad, 0x83, 0xef, 0x1f, 0xcd, 0xf1, 0x2a,
|
||||
0x57, 0xb5, 0xcf, 0xa2, 0x00, 0x0b, 0x8a, 0xd0,
|
||||
0x27, 0xe9, 0x56, 0xe8, 0x54, 0xc5, 0x0a, 0x8b};
|
||||
|
||||
uint8_t U2F_ATT_PUB_KEY[] = {
|
||||
0x04, 0xd9, 0x18, 0xbd, 0xfa, 0x8a, 0x54, 0xac, 0x92, 0xe9, 0x0d,
|
||||
0xa9, 0x1f, 0xca, 0x7a, 0xa2, 0x64, 0x54, 0xc0, 0xd1, 0x73, 0x36,
|
||||
0x31, 0x4d, 0xde, 0x83, 0xa5, 0x4b, 0x86, 0xb5, 0xdf, 0x4e, 0xf0,
|
||||
0x52, 0x65, 0x9a, 0x1d, 0x6f, 0xfc, 0xb7, 0x46, 0x7f, 0x1a, 0xcd,
|
||||
0xdb, 0x8a, 0x33, 0x08, 0x0b, 0x5e, 0xed, 0x91, 0x89, 0x13, 0xf4,
|
||||
0x43, 0xa5, 0x26, 0x1b, 0xc7, 0x7b, 0x68, 0x60, 0x6f, 0xc1};
|
||||
|
||||
uint8_t U2F_ATT_CERT[] = {
|
||||
0x30, 0x82, 0x01, 0x19, 0x30, 0x81, 0xC0, 0x02, 0x09, 0x00, 0x8B, 0x3F,
|
||||
0xA6, 0x46, 0xDE, 0x01, 0xCB, 0xB8, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86,
|
||||
0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11,
|
||||
0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0A, 0x54, 0x72, 0x65, 0x7A, 0x6F,
|
||||
0x72, 0x20, 0x55, 0x32, 0x46, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x35, 0x31,
|
||||
0x30, 0x31, 0x31, 0x32, 0x33, 0x32, 0x33, 0x34, 0x36, 0x5A, 0x17, 0x0D,
|
||||
0x32, 0x35, 0x31, 0x30, 0x30, 0x38, 0x32, 0x33, 0x32, 0x33, 0x34, 0x36,
|
||||
0x5A, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03,
|
||||
0x0C, 0x0A, 0x54, 0x72, 0x65, 0x7A, 0x6F, 0x72, 0x20, 0x55, 0x32, 0x46,
|
||||
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
|
||||
0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03,
|
||||
0x42, 0x00, 0x04, 0xD9, 0x18, 0xBD, 0xFA, 0x8A, 0x54, 0xAC, 0x92, 0xE9,
|
||||
0x0D, 0xA9, 0x1F, 0xCA, 0x7A, 0xA2, 0x64, 0x54, 0xC0, 0xD1, 0x73, 0x36,
|
||||
0x31, 0x4D, 0xDE, 0x83, 0xA5, 0x4B, 0x86, 0xB5, 0xDF, 0x4E, 0xF0, 0x52,
|
||||
0x65, 0x9A, 0x1D, 0x6F, 0xFC, 0xB7, 0x46, 0x7F, 0x1A, 0xCD, 0xDB, 0x8A,
|
||||
0x33, 0x08, 0x0B, 0x5E, 0xED, 0x91, 0x89, 0x13, 0xF4, 0x43, 0xA5, 0x26,
|
||||
0x1B, 0xC7, 0x7B, 0x68, 0x60, 0x6F, 0xC1, 0x30, 0x0A, 0x06, 0x08, 0x2A,
|
||||
0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45,
|
||||
0x02, 0x21, 0x00, 0x89, 0x38, 0x50, 0x13, 0xBB, 0x13, 0xF1, 0xE9, 0xEF,
|
||||
0x7A, 0x1B, 0x47, 0x59, 0x19, 0xBA, 0x18, 0x55, 0x32, 0xE6, 0x76, 0xD1,
|
||||
0xAD, 0x69, 0x9D, 0x97, 0x07, 0x89, 0x80, 0x08, 0x4F, 0xAA, 0xE8, 0x02,
|
||||
0x20, 0x35, 0x9A, 0x5F, 0x1D, 0xE5, 0x0A, 0x12, 0xA0, 0x9C, 0x96, 0x3D,
|
||||
0x5A, 0x39, 0xA8, 0x83, 0x3C, 0xBA, 0x2E, 0xAD, 0xD5, 0x90, 0x23, 0x85,
|
||||
0x81, 0x7D, 0xF0, 0xC7, 0x72, 0x8E, 0x79, 0x09, 0xD0,
|
||||
};
|
||||
|
||||
#endif // __U2F_KEYS_H_INCLUDED__
|
116
firmware/usb.c
116
firmware/usb.c
@ -24,6 +24,7 @@
|
||||
#include "usb.h"
|
||||
#include "debug.h"
|
||||
#include "messages.h"
|
||||
#include "u2f.h"
|
||||
#include "storage.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -31,6 +32,8 @@
|
||||
#define ENDPOINT_ADDRESS_OUT (0x01)
|
||||
#define ENDPOINT_ADDRESS_DEBUG_IN (0x82)
|
||||
#define ENDPOINT_ADDRESS_DEBUG_OUT (0x02)
|
||||
#define ENDPOINT_ADDRESS_U2F_IN (0x83)
|
||||
#define ENDPOINT_ADDRESS_U2F_OUT (0x03)
|
||||
|
||||
static const struct usb_device_descriptor dev_descr = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
@ -111,6 +114,25 @@ static const uint8_t hid_report_descriptor[] = {
|
||||
0x95, 0x13, 0x09, 0x01, 0xB1, 0x02, 0xC0,
|
||||
};
|
||||
|
||||
static const uint8_t hid_report_descriptor_u2f[] = {
|
||||
0x06, 0xd0, 0xf1, // USAGE_PAGE (FIDO Alliance)
|
||||
0x09, 0x01, // USAGE (U2F HID Authenticator Device)
|
||||
0xa1, 0x01, // COLLECTION (Application)
|
||||
0x09, 0x20, // USAGE (Input Report Data)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x26,0xff, 0x00, // LOGICAL_MAXIMUM (255)
|
||||
0x75,0x08, // REPORT_SIZE (8)
|
||||
0x95,0x40, // REPORT_COUNT (64)
|
||||
0x81,0x02, // INPUT (Data,Var,Abs)
|
||||
0x09,0x21, // USAGE (Output Report Data)
|
||||
0x15,0x00, // LOGICAL_MINIMUM (0)
|
||||
0x26,0xff, 0x00, // LOGICAL_MAXIMUM (255)
|
||||
0x75,0x08, // REPORT_SIZE (8)
|
||||
0x95,0x40, // REPORT_COUNT (64)
|
||||
0x91,0x02, // OUTPUT (Data,Var,Abs)
|
||||
0xc0 // END_COLLECTION
|
||||
};
|
||||
|
||||
static const struct {
|
||||
struct usb_hid_descriptor hid_descriptor;
|
||||
struct {
|
||||
@ -131,6 +153,27 @@ static const struct {
|
||||
}
|
||||
};
|
||||
|
||||
static const struct {
|
||||
struct usb_hid_descriptor hid_descriptor_u2f;
|
||||
struct {
|
||||
uint8_t bReportDescriptorType;
|
||||
uint16_t wDescriptorLength;
|
||||
} __attribute__((packed)) hid_report_u2f;
|
||||
} __attribute__((packed)) hid_function_u2f = {
|
||||
.hid_descriptor_u2f = {
|
||||
.bLength = sizeof(hid_function_u2f),
|
||||
.bDescriptorType = USB_DT_HID,
|
||||
.bcdHID = 0x0111,
|
||||
.bCountryCode = 0,
|
||||
.bNumDescriptors = 1,
|
||||
},
|
||||
.hid_report_u2f = {
|
||||
.bReportDescriptorType = USB_DT_REPORT,
|
||||
.wDescriptorLength = sizeof(hid_report_descriptor_u2f),
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static const struct usb_endpoint_descriptor hid_endpoints[2] = {{
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
@ -162,6 +205,41 @@ static const struct usb_interface_descriptor hid_iface[] = {{
|
||||
.extralen = sizeof(hid_function),
|
||||
}};
|
||||
|
||||
static const struct usb_endpoint_descriptor hid_endpoints_u2f[2] = {{
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = ENDPOINT_ADDRESS_U2F_IN,
|
||||
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 1,
|
||||
}, {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = ENDPOINT_ADDRESS_U2F_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 1,
|
||||
}};
|
||||
|
||||
static const struct usb_interface_descriptor hid_iface_u2f[] = {{
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
#if DEBUG_LINK
|
||||
.bInterfaceNumber = 2,
|
||||
#else
|
||||
.bInterfaceNumber = 1,
|
||||
#endif
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceSubClass = 0,
|
||||
.bInterfaceProtocol = 0,
|
||||
.iInterface = 0,
|
||||
.endpoint = hid_endpoints_u2f,
|
||||
.extra = &hid_function_u2f,
|
||||
.extralen = sizeof(hid_function_u2f),
|
||||
}};
|
||||
|
||||
#if DEBUG_LINK
|
||||
static const struct usb_endpoint_descriptor hid_endpoints_debug[2] = {{
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
@ -198,6 +276,9 @@ static const struct usb_interface_descriptor hid_iface_debug[] = {{
|
||||
static const struct usb_interface ifaces[] = {{
|
||||
.num_altsetting = 1,
|
||||
.altsetting = hid_iface,
|
||||
}, {
|
||||
.num_altsetting = 1,
|
||||
.altsetting = hid_iface_u2f,
|
||||
#if DEBUG_LINK
|
||||
}, {
|
||||
.num_altsetting = 1,
|
||||
@ -210,9 +291,9 @@ static const struct usb_config_descriptor config = {
|
||||
.bDescriptorType = USB_DT_CONFIGURATION,
|
||||
.wTotalLength = 0,
|
||||
#if DEBUG_LINK
|
||||
.bNumInterfaces = 2,
|
||||
.bNumInterfaces = 3,
|
||||
#else
|
||||
.bNumInterfaces = 1,
|
||||
.bNumInterfaces = 2,
|
||||
#endif
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = 0,
|
||||
@ -237,11 +318,18 @@ static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uin
|
||||
(req->wValue != 0x2200))
|
||||
return 0;
|
||||
|
||||
/* Handle the HID report descriptor. */
|
||||
*buf = (uint8_t *)hid_report_descriptor;
|
||||
*len = sizeof(hid_report_descriptor);
|
||||
if (req->wIndex==1) {
|
||||
debugLog(0, "", "hid_control_request u2f");
|
||||
*buf = (uint8_t *)hid_report_descriptor_u2f;
|
||||
*len = sizeof(hid_report_descriptor_u2f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
debugLog(0, "", "hid_control_request trezor");
|
||||
/* Handle the HID report descriptor. */
|
||||
*buf = (uint8_t *)hid_report_descriptor;
|
||||
*len = sizeof(hid_report_descriptor);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static volatile char tiny = 0;
|
||||
@ -259,6 +347,16 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep)
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_u2f_rx_callback(usbd_device *dev, uint8_t ep)
|
||||
{
|
||||
(void)ep;
|
||||
static uint8_t buf[64] __attribute__ ((aligned(4)));
|
||||
|
||||
debugLog(0, "", "hid_u2f_rx_callback");
|
||||
if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_U2F_OUT, buf, 64) != 64) return;
|
||||
u2fhid_read((const U2FHID_FRAME *)buf);
|
||||
}
|
||||
|
||||
#if DEBUG_LINK
|
||||
static void hid_debug_rx_callback(usbd_device *dev, uint8_t ep)
|
||||
{
|
||||
@ -280,6 +378,8 @@ static void hid_set_config(usbd_device *dev, uint16_t wValue)
|
||||
|
||||
usbd_ep_setup(dev, ENDPOINT_ADDRESS_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0);
|
||||
usbd_ep_setup(dev, ENDPOINT_ADDRESS_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_rx_callback);
|
||||
usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0);
|
||||
usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_u2f_rx_callback);
|
||||
#if DEBUG_LINK
|
||||
usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0);
|
||||
usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_debug_rx_callback);
|
||||
@ -311,6 +411,10 @@ void usbPoll(void)
|
||||
if (data) {
|
||||
while ( usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_IN, data, 64) != 64 ) {}
|
||||
}
|
||||
data = u2f_out_data();
|
||||
if (data) {
|
||||
while ( usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_U2F_IN, data, 64) != 64 ) {}
|
||||
}
|
||||
#if DEBUG_LINK
|
||||
// write pending debug data
|
||||
data = msg_debug_out_data();
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 3fc31bfe9c42ebf3c0a63c48f44537994787f9c9
|
||||
Subproject commit 72268e816b8e8e06f698b3729223a255c7c74167
|
Loading…
Reference in New Issue
Block a user