Merge branch 'polhenarejos:main' into main

This commit is contained in:
oxygen
2026-01-26 19:32:32 +08:00
committed by GitHub
13 changed files with 73 additions and 23 deletions

View File

@@ -17,9 +17,12 @@
cmake_minimum_required(VERSION 3.13)
set(USB_VID 0x2E8A)
set(USB_PID 0x10FE)
if(ESP_PLATFORM)
set(DENABLE_POWER_ON_RESET 0)
set(EXTRA_COMPONENT_DIRS src pico-keys-sdk/src)
set(EXTRA_COMPONENT_DIRS pico-keys-sdk/config/esp32/components src/fido)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
else()
@@ -168,7 +171,7 @@ if(ENABLE_EMULATION)
-Wl,--gc-sections
)
endif (APPLE)
target_link_libraries(pico_fido PRIVATE pthread m)
target_link_libraries(pico_fido PRIVATE pico_keys_sdk mbedtls pthread m)
else()
target_link_libraries(pico_fido PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id pico_aon_timer tinyusb_device tinyusb_board)
pico_add_extra_outputs(${CMAKE_PROJECT_NAME})

View File

@@ -56,9 +56,9 @@ Microcontrollers RP2350 and ESP32-S3 are designed to support secure environments
If you own a Raspberry Pico (RP2040 or RP2350), go to [Download page](https://www.picokeys.com/getting-started/), select your vendor and model and download the proper firmware; or go to [Release page](https://www.github.com/polhenarejos/pico-fido/releases/) and download the UF2 file for your board.
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with OpenSC or similar tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App").
UF2 files are shiped with a VID/PID granted by RaspberryPi (2E8A:10FE). If you plan to use it with OpenSC or similar tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App").
You can use whatever VID/PID (i.e., 234b:0000 from FISJ), but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
You can use whatever VID/PID for internal purposes, but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
Note that the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App") is the most recommended.

View File

@@ -1,6 +1,6 @@
idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS . ../../pico-keys-sdk/src ../../pico-keys-sdk/src/fs ../../pico-keys-sdk/src/rng ../../pico-keys-sdk/src/usb ../../pico-keys-sdk/tinycbor/src
REQUIRES esp_tinyusb mbedtls efuse
INCLUDE_DIRS .
REQUIRES esp_tinyusb mbedtls efuse pico-keys-sdk
)
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON)

View File

@@ -419,6 +419,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
hsh[1] = pin_len;
hsh[2] = 1; // New format indicator
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash);
mbedtls_platform_zeroize(paddedNewPin, sizeof(paddedNewPin));
pin_derive_verifier(dhash, 16, hsh + 3);
file_put_data(ef_pin, hsh, sizeof(hsh));
low_flash_available();
@@ -430,6 +431,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
}
mbedtls_platform_zeroize(hsh, sizeof(hsh));
mbedtls_platform_zeroize(dhash, sizeof(dhash));
needs_power_cycle = false;
goto err; //No return
}
else if (subcommand == 0x4) { //changePIN
@@ -458,6 +461,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.Y, kay.data, kay.len) != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (needs_power_cycle) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
}
uint8_t sharedSecret[64];
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
if (ret != 0) {
@@ -587,6 +593,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
low_flash_available();
resetPinUvAuthToken();
resetPersistentPinUvAuthToken();
needs_power_cycle = false;
goto err; // No return
}
else if (subcommand == 0x9 || subcommand == 0x5) { //getPinUvAuthTokenUsingPinWithPermissions
@@ -623,6 +630,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.Y, kay.data, kay.len) != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (needs_power_cycle) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
}
uint8_t sharedSecret[64];
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
if (ret != 0) {
@@ -720,6 +730,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32 + poff));
needs_power_cycle = false;
}
else {
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);

View File

@@ -428,7 +428,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
if (!silent) {
for (int i = 0; i < numberOfCredentials; i++) {
for (int j = i + 1; j < numberOfCredentials; j++) {
if (creds[j].creation > creds[i].creation) {
if (creds[j].board_creation > creds[i].board_creation) {
Credential tmp = creds[j];
creds[j] = creds[i];
creds[i] = tmp;

View File

@@ -206,7 +206,12 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
uint8_t buffer[1024];
mbedtls_ecdsa_context ekey;
mbedtls_ecdsa_init(&ekey);
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), file_get_size(ef_keydev));
uint8_t keydev[32] = {0};
if (load_keydev(keydev) != 0) {
CBOR_ERROR(CTAP1_ERR_OTHER);
}
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, keydev, 32);
mbedtls_platform_zeroize(keydev, sizeof(keydev));
if (ret != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP2_ERR_PROCESSING);

View File

@@ -29,6 +29,7 @@
#include "files.h"
#include "otp.h"
extern bool has_set_rtc();
int credential_derive_chacha_key(uint8_t *outk, const uint8_t *);
static int credential_silent_tag(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, uint8_t *outk) {
@@ -148,6 +149,10 @@ int credential_create(CborCharString *rpId,
}
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
}
if (has_set_rtc()) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, (uint64_t) get_rtc_time()));
}
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id);
*cred_id_len = CRED_PROTO_LEN + CRED_IV_LEN + (uint16_t)rs + CRED_TAG_LEN + CRED_SILENT_TAG_LEN;
@@ -220,7 +225,7 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
CBOR_FIELD_GET_TEXT(cred->userDisplayName, 1);
}
else if (val_u == 0x06) {
CBOR_FIELD_GET_UINT(cred->creation, 1);
CBOR_FIELD_GET_UINT(cred->board_creation, 1);
}
else if (val_u == 0x07) {
cred->extensions.present = true;
@@ -255,6 +260,9 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
}
CBOR_PARSE_MAP_END(_f1, 2);
}
else if (val_u == 0x0C) {
CBOR_FIELD_GET_UINT(cred->rtc_creation, 1);
}
else {
CBOR_ADVANCE(1);
}

View File

@@ -43,7 +43,7 @@ typedef struct Credential {
CborByteString userId;
CborCharString userName;
CborCharString userDisplayName;
uint64_t creation;
uint64_t board_creation;
CredExtensions extensions;
const bool *use_sign_count;
int64_t alg;
@@ -51,6 +51,7 @@ typedef struct Credential {
CborByteString id;
CredOptions opts;
bool present;
uint64_t rtc_creation;
} Credential;
#define CRED_PROT_UV_OPTIONAL 0x01

View File

@@ -53,6 +53,11 @@ const uint8_t fido_aid[] = {
0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01
};
const uint8_t fido_aid_backup[] = {
8,
0xB0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01
};
const uint8_t atr_fido[] = {
23,
0x3b, 0xfd, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe, 0x15, 0x80, 0x73, 0xc0, 0x21, 0xc0, 0x57, 0x59,
@@ -86,6 +91,7 @@ INITIALIZER ( fido_ctor ) {
get_version_major = fido_get_version_major;
get_version_minor = fido_get_version_minor;
register_app(fido_select, fido_aid);
register_app(fido_select, fido_aid_backup);
}
int fido_unload() {
@@ -477,11 +483,13 @@ void scan_all() {
}
extern void init_otp();
extern bool needs_power_cycle;
void init_fido() {
scan_all();
#ifdef ENABLE_OTP_APP
init_otp();
#endif
needs_power_cycle = false;
}
bool wait_button_pressed() {
@@ -530,18 +538,32 @@ extern int cmd_register();
extern int cmd_authenticate();
extern int cmd_version();
extern int cbor_parse(int, uint8_t *, size_t);
extern int cbor_vendor(const uint8_t *data, size_t len);
extern void driver_init_hid();
#define CTAP_CBOR 0x10
int cmd_vendor() {
uint8_t *old_buf = res_APDU;
driver_init_hid();
int ret = cbor_vendor(apdu.data, apdu.nc);
res_APDU = old_buf;
if (ret != 0) {
return SW_EXEC_ERROR();
}
res_APDU_size += 1;
memcpy(res_APDU, ctap_resp->init.data, res_APDU_size);
return SW_OK();
}
int cmd_cbor() {
uint8_t *old_buf = res_APDU;
driver_init_hid();
int ret = cbor_parse(0x90, apdu.data, apdu.nc);
res_APDU = old_buf;
if (ret != 0) {
return SW_EXEC_ERROR();
}
res_APDU = old_buf;
res_APDU_size += 1;
memcpy(res_APDU, ctap_resp->init.data, res_APDU_size);
return SW_OK();
@@ -552,6 +574,7 @@ static const cmd_t cmds[] = {
{ CTAP_AUTHENTICATE, cmd_authenticate },
{ CTAP_VERSION, cmd_version },
{ CTAP_CBOR, cmd_cbor },
{ 0x41, cmd_vendor },
{ 0x00, 0x0 }
};

View File

@@ -94,22 +94,22 @@ int oath_select(app_t *a, uint8_t force) {
res_APDU[res_APDU_size++] = sizeof(challenge);
memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge));
res_APDU_size += sizeof(challenge);
}
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (file_has_data(ef_otp_pin)) {
const uint8_t *pin_data = file_get_data(ef_otp_pin);
res_APDU[res_APDU_size++] = TAG_PIN_COUNTER;
res_APDU[res_APDU_size++] = TAG_ALGO;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = *pin_data;
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
}
res_APDU[res_APDU_size++] = TAG_ALGO;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
if (is_nk) {
res_APDU[res_APDU_size++] = TAG_SERIAL_NUMBER;
res_APDU[res_APDU_size++] = 8;
memcpy(res_APDU + res_APDU_size, pico_serial_str, 8);
res_APDU_size += 8;
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (file_has_data(ef_otp_pin)) {
const uint8_t *pin_data = file_get_data(ef_otp_pin);
res_APDU[res_APDU_size++] = TAG_PIN_COUNTER;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = *pin_data;
}
}
apdu.ne = res_APDU_size;
return PICOKEY_OK;

View File

@@ -51,7 +51,7 @@ def test_lockout(device, resetdevice, client_pin):
res = client_pin.get_pin_retries()
assert res[0] == attempts
if err == CtapError.ERR.PIN_AUTH_BLOCKED:
if e.value.code == CtapError.ERR.PIN_AUTH_BLOCKED:
device.reboot()
client_pin = ClientPin(resetdevice.client()._backend.ctap2)

File diff suppressed because one or more lines are too long