Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ae80ab688 | ||
|
|
610bb33cce | ||
|
|
bdcca8a913 | ||
|
|
858b9c42ee | ||
|
|
573cb15e69 | ||
|
|
a7b8fb829f | ||
|
|
a1db7ec1ea | ||
|
|
6025030d58 | ||
|
|
4a4911617d | ||
|
|
b178b139fb | ||
|
|
d6c9077b02 | ||
|
|
8a139e70b7 | ||
|
|
dda5c25e85 | ||
|
|
046706058d | ||
|
|
694ab2cf87 | ||
|
|
6e56874d3e | ||
|
|
19dce60d76 | ||
|
|
9bf20175be |
@@ -6,6 +6,7 @@ Pico FIDO has implemented the following features:
|
||||
|
||||
- ECDSA authentication.
|
||||
- App registration and login.
|
||||
- User presence enforcement through physical button.
|
||||
|
||||
All these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#
|
||||
|
||||
VERSION_MAJOR="3" #Version of Pico CCID Core
|
||||
VERSION_MINOR="0"
|
||||
VERSION_MINOR="2"
|
||||
|
||||
echo "----------------------------"
|
||||
echo "VID/PID patcher for Pico FIDO"
|
||||
@@ -87,7 +87,7 @@ fi
|
||||
LITTLE_VID="\x${VID:2:2}\x${VID:0:2}"
|
||||
LITTLE_PID="\x${PID:2:2}\x${PID:0:2}"
|
||||
|
||||
perl -pi -e "s/\xfe\xff\xfc\xfd\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/$LITTLE_VID$LITTLE_PID\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/" $UF2_FILE_OF
|
||||
perl -pi -e "s/\xfe\xca\x31\x42\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/$LITTLE_VID$LITTLE_PID\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/" $UF2_FILE_OF
|
||||
|
||||
echo "Done!"
|
||||
echo ""
|
||||
|
||||
Submodule pico-hsm-sdk updated: 43dfb0cde5...7491021102
@@ -28,20 +28,54 @@ int cmd_authenticate() {
|
||||
U2F_AUTHENTICATE_RESP *resp = (U2F_AUTHENTICATE_RESP *)res_APDU;
|
||||
if (scan_files() != CCID_OK)
|
||||
return SW_EXEC_ERROR();
|
||||
resp->flags = 0x1;
|
||||
resp->ctr[0] = 0;
|
||||
uint8_t hash[32], sig_base[U2F_APPID_SIZE+1+4+U2F_CHAL_SIZE];
|
||||
if (req->keyHandleLen != KEY_HANDLE_LEN)
|
||||
return SW_WRONG_DATA();
|
||||
if (P1(apdu) == U2F_AUTH_ENFORCE && wait_button_pressed() == true)
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
int ret = derive_key(req->appId, false, req->keyHandle, &key);
|
||||
if (ret != CCID_OK) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if (P1(apdu) == U2F_AUTH_CHECK_ONLY) {
|
||||
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
|
||||
uint32_t k = *(uint32_t *)&req->keyHandle[i*sizeof(uint32_t)];
|
||||
if (!(k & 0x80000000)) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
}
|
||||
uint8_t hmac[32], d[32];
|
||||
ret = mbedtls_ecp_write_key(&key, d, sizeof(d));
|
||||
mbedtls_ecdsa_free(&key);
|
||||
if (ret != 0)
|
||||
return SW_WRONG_DATA();
|
||||
uint8_t key_base[U2F_APPID_SIZE + KEY_PATH_LEN];
|
||||
memcpy(key_base, req->appId, U2F_APPID_SIZE);
|
||||
memcpy(key_base + U2F_APPID_SIZE, req->keyHandle, KEY_PATH_LEN);
|
||||
ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), d, 32, key_base, sizeof(key_base), hmac);
|
||||
mbedtls_platform_zeroize(d, sizeof(d));
|
||||
if (memcmp(req->keyHandle + KEY_PATH_LEN, hmac, sizeof(hmac)) != 0)
|
||||
return SW_WRONG_DATA();
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
}
|
||||
resp->flags = 0;
|
||||
resp->flags |= P1(apdu) == U2F_AUTH_ENFORCE ? U2F_AUTH_FLAG_TUP : 0x0;
|
||||
uint32_t ctr = *(uint32_t *)file_get_data(ef_counter);
|
||||
resp->ctr[0] = ctr >> 24;
|
||||
resp->ctr[1] = ctr >> 16;
|
||||
resp->ctr[2] = ctr >> 8;
|
||||
resp->ctr[3] = ctr & 0xff;
|
||||
uint8_t hash[32], sig_base[U2F_APPID_SIZE + 1 + 4 + U2F_CHAL_SIZE];
|
||||
memcpy(sig_base, req->appId, U2F_APPID_SIZE);
|
||||
memcpy(sig_base+U2F_APPID_SIZE, &resp->flags, sizeof(uint8_t));
|
||||
memcpy(sig_base + U2F_APPID_SIZE + 1, resp->ctr, 4);
|
||||
memcpy(sig_base + U2F_APPID_SIZE + 1 + 4, req->chal, U2F_CHAL_SIZE);
|
||||
int ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sig_base, sizeof(sig_base), hash);
|
||||
if (ret != 0)
|
||||
return SW_EXEC_ERROR();
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
ret = derive_key(req->appId, false, req->keyHandle, &key);
|
||||
if (ret != CCID_OK) {
|
||||
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sig_base, sizeof(sig_base), hash);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
@@ -51,5 +85,9 @@ int cmd_authenticate() {
|
||||
if (ret != 0)
|
||||
return SW_EXEC_ERROR();
|
||||
res_APDU_size = 1 + 4 + olen;
|
||||
|
||||
ctr++;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *)&ctr, sizeof(ctr));
|
||||
low_flash_available();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@ int cmd_register() {
|
||||
resp->keyHandleLen = KEY_HANDLE_LEN;
|
||||
if (scan_files() != CCID_OK)
|
||||
return SW_EXEC_ERROR();
|
||||
if (apdu.nc != U2F_APPID_SIZE + U2F_CHAL_SIZE)
|
||||
return SW_WRONG_LENGTH();
|
||||
if (wait_button_pressed() == true)
|
||||
return SW_CONDITIONS_NOT_SATISFIED();
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
int ret = derive_key(req->appId, true, resp->keyHandleCertSig, &key);
|
||||
@@ -46,7 +50,7 @@ int cmd_register() {
|
||||
size_t ef_certdev_size = file_get_size(ef_certdev);
|
||||
memcpy(resp->keyHandleCertSig + KEY_HANDLE_LEN, file_get_data(ef_certdev), ef_certdev_size);
|
||||
uint8_t hash[32], sign_base[1 + U2F_APPID_SIZE + U2F_CHAL_SIZE + KEY_HANDLE_LEN + U2F_EC_POINT_SIZE];
|
||||
sign_base[0] = 0x00;
|
||||
sign_base[0] = U2F_REGISTER_HASH_ID;
|
||||
memcpy(sign_base + 1, req->appId, U2F_APPID_SIZE);
|
||||
memcpy(sign_base + 1 + U2F_APPID_SIZE, req->chal, U2F_CHAL_SIZE);
|
||||
memcpy(sign_base + 1 + U2F_APPID_SIZE + U2F_CHAL_SIZE, resp->keyHandleCertSig, KEY_HANDLE_LEN);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "u2f.h"
|
||||
#include "files.h"
|
||||
#include "file.h"
|
||||
#include "usb.h"
|
||||
#include "random.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "mbedtls/x509_crt.h"
|
||||
@@ -92,14 +93,13 @@ int load_keydev(uint8_t *key) {
|
||||
}
|
||||
|
||||
int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, mbedtls_ecdsa_context *key) {
|
||||
const int entries = KEY_PATH_LEN / sizeof(uint32_t);
|
||||
uint8_t outk[64] = {0};
|
||||
int r = 0;
|
||||
memset(outk, 0, sizeof(outk));
|
||||
if ((r = load_keydev(outk)) != CCID_OK)
|
||||
return r;
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
for (int i = 0; i < entries; i++)
|
||||
for (int i = 0; i < KEY_PATH_ENTRIES; i++)
|
||||
{
|
||||
if (new_key == true) {
|
||||
uint32_t val = 0x80000000 | *((uint32_t *)random_bytes_get(sizeof(uint32_t)));
|
||||
@@ -111,17 +111,26 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, mbedtls
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if ((r = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, app_id, 32, key_handle + 32)) != 0)
|
||||
{
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
return r;
|
||||
if (new_key == true) {
|
||||
uint8_t key_base[U2F_APPID_SIZE + KEY_PATH_LEN];
|
||||
memcpy(key_base, app_id, U2F_APPID_SIZE);
|
||||
memcpy(key_base + U2F_APPID_SIZE, key_handle, KEY_PATH_LEN);
|
||||
if ((r = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, key_base, sizeof(key_base), key_handle + 32)) != 0)
|
||||
{
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if (key != NULL) {
|
||||
mbedtls_ecp_group_load(&key->grp, MBEDTLS_ECP_DP_SECP256R1);
|
||||
r = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, key, outk, 32);
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
if (r != 0)
|
||||
return r;
|
||||
return mbedtls_ecp_mul(&key->grp, &key->Q, &key->d, &key->grp.G, random_gen, NULL );
|
||||
}
|
||||
mbedtls_ecp_group_load(&key->grp, MBEDTLS_ECP_DP_SECP256R1);
|
||||
r = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, key, outk, 32);
|
||||
mbedtls_platform_zeroize(outk, sizeof(outk));
|
||||
if (r != 0)
|
||||
return r;
|
||||
return mbedtls_ecp_mul(&key->grp, &key->Q, &key->d, &key->grp.G, random_gen, NULL );
|
||||
return r;
|
||||
}
|
||||
|
||||
int scan_files() {
|
||||
@@ -174,6 +183,16 @@ int scan_files() {
|
||||
else {
|
||||
printf("FATAL ERROR: CERT DEV not found in memory!\r\n");
|
||||
}
|
||||
ef_counter = search_by_fid(EF_COUNTER, NULL, SPECIFY_EF);
|
||||
if (ef_counter) {
|
||||
if (file_get_size(ef_counter) == 0 || !ef_counter->data) {
|
||||
uint32_t v = 0;
|
||||
flash_write_data_to_file(ef_counter, (uint8_t *)&v, sizeof(v));
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("FATAL ERROR: Global counter not found in memory!\r\n");
|
||||
}
|
||||
low_flash_available();
|
||||
return CCID_OK;
|
||||
}
|
||||
@@ -186,6 +205,15 @@ void init_fido() {
|
||||
scan_all();
|
||||
}
|
||||
|
||||
bool wait_button_pressed() {
|
||||
uint32_t val = EV_PRESS_BUTTON;
|
||||
queue_try_add(&card_to_usb_q, &val);
|
||||
do {
|
||||
queue_remove_blocking(&usb_to_card_q, &val);
|
||||
} while (val != EV_BUTTON_PRESSED && val != EV_BUTTON_TIMEOUT);
|
||||
return val == EV_BUTTON_TIMEOUT;
|
||||
}
|
||||
|
||||
typedef struct cmd
|
||||
{
|
||||
uint8_t ins;
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
|
||||
#define U2F_PUBKEY_LEN (65)
|
||||
#define KEY_PATH_LEN (32)
|
||||
#define KEY_PATH_ENTRIES (KEY_PATH_LEN / sizeof(uint32_t))
|
||||
#define SHA256_DIGEST_LENGTH (32)
|
||||
#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH)
|
||||
|
||||
extern int scan_files();
|
||||
extern int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, mbedtls_ecdsa_context *key);
|
||||
extern bool wait_button_pressed();
|
||||
|
||||
#endif //_FIDO_H
|
||||
|
||||
@@ -22,6 +22,7 @@ file_t file_entries[] = {
|
||||
{.fid = 0x3f00, .parent = 0xff, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, .ef_structure = 0, .acl = {0}}, // MF
|
||||
{.fid = EF_KEY_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH | FILE_PERSISTENT, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Device Key
|
||||
{.fid = EF_EE_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH | FILE_PERSISTENT, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // End Entity Certificate Device
|
||||
{.fid = EF_COUNTER, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0xff}}, // Global counter
|
||||
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end
|
||||
};
|
||||
|
||||
@@ -29,3 +30,4 @@ const file_t *MF = &file_entries[0];
|
||||
const file_t *file_last = &file_entries[sizeof(file_entries)/sizeof(file_t)-1];
|
||||
file_t *ef_keydev = NULL;
|
||||
file_t *ef_certdev = NULL;
|
||||
file_t *ef_counter = NULL;
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
|
||||
#define EF_KEY_DEV 0xCC00
|
||||
#define EF_EE_DEV 0xCE00
|
||||
#define EF_COUNTER 0xC000
|
||||
|
||||
extern file_t *ef_keydev;
|
||||
extern file_t *ef_certdev;
|
||||
extern file_t *ef_counter;
|
||||
|
||||
#endif //_FILES_H_
|
||||
|
||||
107
src/fido/u2f.h
Normal file
107
src/fido/u2f.h
Normal file
@@ -0,0 +1,107 @@
|
||||
// Common U2F raw message format header - Review Draft
|
||||
// 2014-10-08
|
||||
// Editor: Jakob Ehrensvard, Yubico, jakob@yubico.com
|
||||
|
||||
#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 2048 // 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 0xbf // 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;
|
||||
|
||||
// Command status responses
|
||||
|
||||
#define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR
|
||||
#define U2F_SW_WRONG_DATA 0x6A80 // SW_WRONG_DATA
|
||||
#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED
|
||||
#define U2F_SW_COMMAND_NOT_ALLOWED 0x6986 // SW_COMMAND_NOT_ALLOWED
|
||||
#define U2F_SW_INS_NOT_SUPPORTED 0x6D00 // SW_INS_NOT_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __U2F_H_INCLUDED__
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define PICO_FIDO_VERSION 0x0100
|
||||
#define PICO_FIDO_VERSION 0x0102
|
||||
|
||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
||||
Reference in New Issue
Block a user