Merge branch 'main' into development

This commit is contained in:
Pol Henarejos
2026-01-07 23:39:08 +01:00
9 changed files with 50 additions and 22 deletions

View File

@@ -34,7 +34,7 @@ jobs:
- name: Delete private key - name: Delete private key
run: rm private.pem run: rm private.pem
- name: Update nightly release - name: Update nightly release
uses: pyTooling/Actions/releaser@main uses: pyTooling/Actions/releaser@v6.7.0
with: with:
tag: nightly-${{ matrix.refs }} tag: nightly-${{ matrix.refs }}
rm: true rm: true

View File

@@ -1,7 +1,7 @@
# Pico FIDO # Pico FIDO
This project transforms your Raspberry Pi Pico or ESP32 microcontroller into an integrated FIDO Passkey, functioning like a standard USB Passkey for authentication. This project transforms your Raspberry Pi Pico or ESP32 microcontroller into an integrated FIDO Passkey, functioning like a standard USB Passkey for authentication.
If you are looking for a Fido + OpenPGP, see: https://github.com/polhenarejos/pico-fido2 If you are looking for a OpenPGP + Fido, see: https://github.com/polhenarejos/pico-fido2. Available through [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App").
## Features ## Features
Pico FIDO includes the following features: Pico FIDO includes the following features:
@@ -36,12 +36,13 @@ Pico FIDO includes the following features:
- Challenge-response generation - Challenge-response generation
- Emulated keyboard interface - Emulated keyboard interface
- Button press generates an OTP that is directly typed - Button press generates an OTP that is directly typed
- Yubico Authenticator app compatible
- Yubico YKMAN compatible - Yubico YKMAN compatible
- Nitrokey nitropy and nitroapp compatible - Nitrokey nitropy and nitroapp compatible
- Secure Boot and Secure Lock in RP2350 and ESP32-S3 MCUs - Secure Boot and Secure Lock in RP2350 and ESP32-S3 MCUs
- One Time Programming to store the master key that encrypts all resident keys and seeds. - One Time Programming to store the master key that encrypts all resident keys and seeds.
- Rescue interface to allow recovery of the device if it becomes unresponsive or undetectable. - Rescue interface to allow recovery of the device if it becomes unresponsive or undetectable.
- LED customization with Pico Commissioner. - LED customization with PicoKey App.
All features comply with the specifications. If you encounter unexpected behavior or deviations from the specifications, please open an issue. All features comply with the specifications. If you encounter unexpected behavior or deviations from the specifications, please open an issue.
@@ -55,11 +56,11 @@ 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. 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 other proprietary tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner"). 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").
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 (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.
Note that the pure-browser option [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner") is the most recommended. Note that the [PicoKey App](https://www.picokeys.com/picokeyapp/ "PicoKey App") is the most recommended.
## Build for Raspberry Pico ## Build for Raspberry Pico
Before building, ensure you have installed the toolchain for the Pico and that the Pico SDK is properly located on your drive. Before building, ensure you have installed the toolchain for the Pico and that the Pico SDK is properly located on your drive.

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
VERSION_MAJOR="7" VERSION_MAJOR="7"
VERSION_MINOR="0" VERSION_MINOR="2"
SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}" SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}"
#if ! [[ -z "${GITHUB_SHA}" ]]; then #if ! [[ -z "${GITHUB_SHA}" ]]; then
# SUFFIX="${SUFFIX}.${GITHUB_SHA}" # SUFFIX="${SUFFIX}.${GITHUB_SHA}"

View File

@@ -41,6 +41,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len);
int cbor_config(const uint8_t *data, size_t len); int cbor_config(const uint8_t *data, size_t len);
int cbor_vendor(const uint8_t *data, size_t len); int cbor_vendor(const uint8_t *data, size_t len);
int cbor_large_blobs(const uint8_t *data, size_t len); int cbor_large_blobs(const uint8_t *data, size_t len);
extern void reset_gna_state();
extern int cmd_read_config(); extern int cmd_read_config();
@@ -59,6 +60,9 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
} }
if (cap_supported(CAP_FIDO2)) { if (cap_supported(CAP_FIDO2)) {
if (cmd == CTAPHID_CBOR) { if (cmd == CTAPHID_CBOR) {
if (data[0] != CTAP_GET_NEXT_ASSERTION) {
reset_gna_state();
}
if (data[0] == CTAP_MAKE_CREDENTIAL) { if (data[0] == CTAP_MAKE_CREDENTIAL) {
return cbor_make_credential(data + 1, len - 1); return cbor_make_credential(data + 1, len - 1);
} }

View File

@@ -42,6 +42,22 @@ uint32_t timerx = 0;
uint8_t *datax = NULL; uint8_t *datax = NULL;
size_t lenx = 0; size_t lenx = 0;
void reset_gna_state() {
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
credential_free(&credsx[i]);
}
if (datax) {
free(datax);
datax = NULL;
}
lenx = 0;
residentx = false;
timerx = 0;
flagsx = 0;
credentialCounter = 0;
numberOfCredentialsx = 0;
}
int cbor_get_next_assertion(const uint8_t *data, size_t len) { int cbor_get_next_assertion(const uint8_t *data, size_t len) {
(void) data; (void) data;
(void) len; (void) len;
@@ -57,19 +73,7 @@ int cbor_get_next_assertion(const uint8_t *data, size_t len) {
credentialCounter++; credentialCounter++;
err: err:
if (error != CborNoError || credentialCounter == numberOfCredentialsx) { if (error != CborNoError || credentialCounter == numberOfCredentialsx) {
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) { reset_gna_state();
credential_free(&credsx[i]);
}
if (datax) {
free(datax);
datax = NULL;
}
lenx = 0;
residentx = false;
timerx = 0;
flagsx = 0;
credentialCounter = 0;
numberOfCredentialsx = 0;
if (error == CborErrorImproperValue) { if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
} }

View File

@@ -319,7 +319,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
credential_free(&rcred); credential_free(&rcred);
continue; continue;
} }
if (memcmp(rcred.userId.data, cred.userId.data, MIN(rcred.userId.len, cred.userId.len)) == 0) { if (rcred.userId.len == cred.userId.len && memcmp(rcred.userId.data, cred.userId.data, rcred.userId.len) == 0) {
sloti = i; sloti = i;
credential_free(&rcred); credential_free(&rcred);
new_record = false; new_record = false;

View File

@@ -18,7 +18,7 @@
#ifndef __VERSION_H_ #ifndef __VERSION_H_
#define __VERSION_H_ #define __VERSION_H_
#define PICO_FIDO_VERSION 0x0700 #define PICO_FIDO_VERSION 0x0702
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff) #define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff) #define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)

View File

@@ -202,10 +202,29 @@ def test_rk_with_allowlist_of_different_rp(resetdevice):
assert e.value.code == CtapError.ERR.NO_CREDENTIALS assert e.value.code == CtapError.ERR.NO_CREDENTIALS
def test_same_prefix_userId(device):
"""
A make credential request with two different UserIds that share the same prefix should NOT overwrite.
"""
rp = {"id": "sameprefix.org", "name": "Example"}
user1 = {"id": b"user_12", "name": "A fixed name", "displayName": "A fixed display name"}
user2 = {"id": b"user_123", "name": "A fixed name", "displayName": "A fixed display name"}
mc_res1 = device.MC(rp = rp, options={"rk":True}, user = user1)
# Should not overwrite the first credential.
mc_res2 = device.MC(rp = rp, options={"rk":True}, user = user2)
ga_res = device.GA(rp_id=rp['id'])['res']
assert ga_res.number_of_credentials == 2
def test_same_userId_overwrites_rk(resetdevice): def test_same_userId_overwrites_rk(resetdevice):
""" """
A make credential request with a UserId & Rp that is the same as an existing one should overwrite. A make credential request with a UserId & Rp that is the same as an existing one should overwrite.
""" """
resetdevice.reset()
rp = {"id": "overwrite.org", "name": "Example"} rp = {"id": "overwrite.org", "name": "Example"}
user = generate_random_user() user = generate_random_user()