18 Commits
v1.0 ... v1.2

Author SHA1 Message Date
Pol Henarejos
7ae80ab688 Upgrade to v1.2
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-08 16:54:52 +02:00
Pol Henarejos
610bb33cce Upgrading flash tool to latest HSM SDK version.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-08 16:54:41 +02:00
Pol Henarejos
bdcca8a913 ADded clarification.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-08 16:50:55 +02:00
Pol Henarejos
858b9c42ee HSM SDK fixes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-08 16:49:49 +02:00
Pol Henarejos
573cb15e69 Replace some constants with defines.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-08 14:00:05 +02:00
Pol Henarejos
a7b8fb829f Fix for HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-08 13:51:26 +02:00
Pol Henarejos
a1db7ec1ea Add length check.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-08 13:51:15 +02:00
Pol Henarejos
6025030d58 Moving from HSM SDK to here.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-08 13:50:47 +02:00
Pol Henarejos
4a4911617d Move HSM SDK to v3.2.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-08 13:45:53 +02:00
Pol Henarejos
b178b139fb Added user presence flag and global counter on authentication.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-07 20:03:34 +02:00
Pol Henarejos
d6c9077b02 More fixes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-06 22:23:40 +02:00
Pol Henarejos
8a139e70b7 Fix verifying key handle.
Now it works in Firefox!

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-06 21:47:13 +02:00
Pol Henarejos
dda5c25e85 Fix computing HMAC of key path.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-06 21:35:23 +02:00
Pol Henarejos
046706058d Added support for user enforcement and key check (P1 0x07, 0x03 and 0x08).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-06 16:54:12 +02:00
Pol Henarejos
694ab2cf87 Fix authentication key_path.
Also adding key parameter for key derivation as optional.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-06 16:53:39 +02:00
Pol Henarejos
6e56874d3e Adding test user presence on authentication.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-06 16:30:01 +02:00
Pol Henarejos
19dce60d76 It requires user to press the button for confirming registration.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-06 16:24:38 +02:00
Pol Henarejos
9bf20175be Adding routine for pressing button to test required user presence.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-09-06 16:24:21 +02:00
11 changed files with 210 additions and 26 deletions

View File

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

View File

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

View File

@@ -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();
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
View 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__

View File

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