commit
5e57a1ceaf
@ -0,0 +1,768 @@
|
||||
/*
|
||||
* 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))
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
#if DEBUG_LOG
|
||||
char *debugInt(const uint32_t i)
|
||||
{
|
||||
static uint8_t n = 0;
|
||||
static char id[8][9];
|
||||
uint32hex(i, id[n]);
|
||||
debugLog(0, "", id[n]);
|
||||
char *ret = (char *)id[n];
|
||||
n = (n + 1) % 8;
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#define debugInt(I) do{}while(0)
|
||||
#endif
|
||||
|
||||
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 = (U2FHID_INIT_RESP *)f.init.data;
|
||||
|
||||
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;
|
||||
|
||||
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) == 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);
|
||||
|
||||
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
|
||||
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);
|
||||
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;
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#define U2F_KEY_PATH 0x80553246
|
||||
|
||||
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(char tiny, const U2FHID_FRAME *buf);
|
||||
void u2fhid_init_cmd(const U2FHID_FRAME *f);
|
||||
void u2fhid_read_start(const U2FHID_FRAME *f);
|
||||
bool u2fhid_write(uint8_t *buf);
|
||||
void u2fhid_init(const U2FHID_FRAME *in);
|
||||
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, uint32_t len);
|
||||
void send_u2f_error(uint16_t err);
|
||||
|
||||
void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data,
|
||||
const uint32_t len);
|
||||
void send_u2fhid_error(uint32_t fcid, uint8_t err);
|
||||
|
||||
#endif
|
@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
cat > u2f_keys.h <<EOF
|
||||
#ifndef __U2F_KEYS_H_INCLUDED__
|
||||
#define __U2F_KEYS_H_INCLUDED__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
const uint8_t U2F_ATT_PRIV_KEY[] = {
|
||||
EOF
|
||||
|
||||
if [ \! -e trezordevkey.pem ]; then
|
||||
openssl ecparam -genkey -out trezordevkey.pem -name prime256v1
|
||||
fi
|
||||
openssl ec -in trezordevkey.pem -text |
|
||||
perl -e '$key = "\t"; while (<>) {
|
||||
if (/priv:/) { $priv = 1 }
|
||||
elsif (/pub:/) { $priv = 0 }
|
||||
elsif ($priv) {
|
||||
while ($_ =~ s/.*?([0-9a-f]{2})//) {
|
||||
$key .= "0x$1,";
|
||||
if ($num++ % 8 == 7) { $key .= "\n\t"; }
|
||||
else {$key .= " ";}
|
||||
}
|
||||
}
|
||||
}
|
||||
$key =~ s/,\s*$/\n/s;
|
||||
print $key;' >> u2f_keys.h
|
||||
cat >> u2f_keys.h <<EOF
|
||||
};
|
||||
|
||||
const uint8_t U2F_ATT_CERT[] = {
|
||||
EOF
|
||||
|
||||
openssl req -new -key trezordevkey.pem -out trezordevcert.req -subj "/CN=Trezor U2F"
|
||||
openssl x509 -req -in trezordevcert.req -signkey trezordevkey.pem -days 3650 -out trezordevcert.pem
|
||||
openssl x509 -in trezordevcert.pem -outform der | od -tx1 -w12 -Anone | perl -pe 's/ ([0-9a-f]{2})/ 0x$1,/g; $_ =~ s/^ /\t/;' >> u2f_keys.h
|
||||
|
||||
cat >> u2f_keys.h <<EOF
|
||||
};
|
||||
|
||||
#endif // __U2F_KEYS_H_INCLUDED__
|
||||
EOF
|
||||
|
||||
rm trezordevcert.req trezordevcert.pem
|
@ -0,0 +1,5 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIHEmrCv2RNxhhq2D7x/N8SpXtc+iAAuK0CfpVuhUxQqLoAoGCCqGSM49
|
||||
AwEHoUQDQgAE2Ri9+opUrJLpDakfynqiZFTA0XM2MU3eg6VLhrXfTvBSZZodb/y3
|
||||
Rn8azduKMwgLXu2RiRP0Q6UmG8d7aGBvwQ==
|
||||
-----END EC PRIVATE KEY-----
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
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_LENGTH 0x6700 // SW_WRONG_LENGTH
|
||||
#define U2F_SW_DATA_INVALID 0x6984 // SW_WRONG_DATA
|
||||
#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED
|
||||
#define U2F_SW_WRONG_DATA 0x6a80 // SW_WRONG_DATA
|
||||
#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__
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
#define U2FHID_INIT_RESP_SIZE 17
|
||||
|
||||
// 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__
|
@ -0,0 +1,40 @@
|
||||
#ifndef __U2F_KEYS_H_INCLUDED__
|
||||
#define __U2F_KEYS_H_INCLUDED__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
const 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
|
||||
};
|
||||
|
||||
const uint8_t U2F_ATT_CERT[] = {
|
||||
0x30, 0x82, 0x01, 0x18, 0x30, 0x81, 0xc0, 0x02, 0x09, 0x00, 0xb1, 0xd9,
|
||||
0x8f, 0x42, 0x64, 0x72, 0xd3, 0x2c, 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, 0x36, 0x30,
|
||||
0x34, 0x32, 0x39, 0x31, 0x33, 0x33, 0x31, 0x35, 0x33, 0x5a, 0x17, 0x0d,
|
||||
0x32, 0x36, 0x30, 0x34, 0x32, 0x37, 0x31, 0x33, 0x33, 0x31, 0x35, 0x33,
|
||||
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, 0x47, 0x00, 0x30, 0x44,
|
||||
0x02, 0x20, 0x24, 0x1e, 0x81, 0xff, 0xd2, 0xe5, 0xe6, 0x15, 0x36, 0x94,
|
||||
0xc3, 0x55, 0x2e, 0x8f, 0xeb, 0xd7, 0x1e, 0x89, 0x35, 0x92, 0x1c, 0xb4,
|
||||
0x83, 0x41, 0x43, 0x71, 0x1c, 0x76, 0xea, 0xee, 0xf3, 0x95, 0x02, 0x20,
|
||||
0x5f, 0x80, 0xeb, 0x10, 0xf2, 0x5c, 0xcc, 0x39, 0x8b, 0x3c, 0xa8, 0xa9,
|
||||
0xad, 0xa4, 0x02, 0x7f, 0x93, 0x13, 0x20, 0x77, 0xb7, 0xab, 0xce, 0x77,
|
||||
0x46, 0x5a, 0x27, 0xf5, 0x3d, 0x33, 0xa1, 0x1d,
|
||||
};
|
||||
|
||||
#endif // __U2F_KEYS_H_INCLUDED__
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project.
|
||||
*
|
||||
* Copyright (C) 2016 Jochen Hoenicke <hoenicke@gmail.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_KNOWNAPPS_H_INCLUDED__
|
||||
#define __U2F_KNOWNAPPS_H_INCLUDED__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "u2f/u2f.h"
|
||||
#include "bitmaps.h"
|
||||
|
||||
typedef struct {
|
||||
const uint8_t appid[U2F_APPID_SIZE];
|
||||
const char *appname;
|
||||
const BITMAP *appicon;
|
||||
} U2FWellKnown;
|
||||
|
||||
static const U2FWellKnown u2f_well_known[4] = {
|
||||
{
|
||||
// https://www.gstatic.com/securitykey/origins.json
|
||||
{ 0xa5,0x46,0x72,0xb2,0x22,0xc4,0xcf,0x95,
|
||||
0xe1,0x51,0xed,0x8d,0x4d,0x3c,0x76,0x7a,
|
||||
0x6c,0xc3,0x49,0x43,0x59,0x43,0x79,0x4e,
|
||||
0x88,0x4f,0x3d,0x02,0x3a,0x82,0x29,0xfd },
|
||||
"Google",
|
||||
&bmp_u2f_google
|
||||
},
|
||||
{
|
||||
// https://github.com/u2f/trusted_facets
|
||||
{ 0x70,0x61,0x7d,0xfe,0xd0,0x65,0x86,0x3a,
|
||||
0xf4,0x7c,0x15,0x55,0x6c,0x91,0x79,0x88,
|
||||
0x80,0x82,0x8c,0xc4,0x07,0xfd,0xf7,0x0a,
|
||||
0xe8,0x50,0x11,0x56,0x94,0x65,0xa0,0x75 },
|
||||
"Github",
|
||||
&bmp_u2f_github
|
||||
},
|
||||
{
|
||||
// https://www.dropbox.com/u2f-app-id.json
|
||||
{ 0xc5,0x0f,0x8a,0x7b,0x70,0x8e,0x92,0xf8,
|
||||
0x2e,0x7a,0x50,0xe2,0xbd,0xc5,0x5d,0x8f,
|
||||
0xd9,0x1a,0x22,0xfe,0x6b,0x29,0xc0,0xcd,
|
||||
0xf7,0x80,0x55,0x30,0x84,0x2a,0xf5,0x81 },
|
||||
"Dropbox",
|
||||
&bmp_u2f_dropbox
|
||||
},
|
||||
{
|
||||
// https://slushpool.com/static/security/u2f.json
|
||||
{ 0x08,0xb2,0xa3,0xd4,0x19,0x39,0xaa,0x31,
|
||||
0x66,0x84,0x93,0xcb,0x36,0xcd,0xcc,0x4f,
|
||||
0x16,0xc4,0xd9,0xb4,0xc8,0x23,0x8b,0x73,
|
||||
0xc2,0xf6,0x72,0xc0,0x33,0x00,0x71,0x97 },
|
||||
"Slush Pool",
|
||||
&bmp_u2f_slushpool
|
||||
}
|
||||
};
|
||||
|
||||
#endif // U2F_KNOWNAPPS_INCLUDED
|
After Width: | Height: | Size: 263 B |
After Width: | Height: | Size: 236 B |
After Width: | Height: | Size: 240 B |
After Width: | Height: | Size: 214 B |
@ -1 +1 @@
|
||||
Subproject commit e6295a33cda4f0541311c9098569531c2d84220b
|
||||
Subproject commit 70d8ac8f85f16a0a16019749402f26dcc635d841
|
Loading…
Reference in new issue