Compare commits
26 Commits
v7.0-eddsa
...
v7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
804ee68e86 | ||
|
|
fe49149d86 | ||
|
|
81d97f1a18 | ||
|
|
d16016cf1e | ||
|
|
2e0333677b | ||
|
|
becdc94339 | ||
|
|
bd499ae1d4 | ||
|
|
5fc84d7097 | ||
|
|
ac7e34522a | ||
|
|
70dec5596a | ||
|
|
2331dcb3ec | ||
|
|
42b8b0af5f | ||
|
|
ceb54d9a08 | ||
|
|
527943fbba | ||
|
|
7dddfd971e | ||
|
|
29f942dab9 | ||
|
|
aa9df892d3 | ||
|
|
7ac2ce30f0 | ||
|
|
e86862033c | ||
|
|
ae36143498 | ||
|
|
d90dbb6c5f | ||
|
|
1d21d93b74 | ||
|
|
8b9be258de | ||
|
|
46720fb387 | ||
|
|
1867f0330f | ||
|
|
bb542e3b83 |
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Delete private key
|
||||
run: rm private.pem
|
||||
- name: Update nightly release
|
||||
uses: pyTooling/Actions/releaser@main
|
||||
uses: pyTooling/Actions/releaser@v6.7.0
|
||||
with:
|
||||
tag: nightly-${{ matrix.refs }}
|
||||
rm: true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 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.
|
||||
|
||||
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
|
||||
Pico FIDO includes the following features:
|
||||
@@ -36,12 +36,13 @@ Pico FIDO includes the following features:
|
||||
- Challenge-response generation
|
||||
- Emulated keyboard interface
|
||||
- Button press generates an OTP that is directly typed
|
||||
- Yubico Authenticator app compatible
|
||||
- Yubico YKMAN compatible
|
||||
- Nitrokey nitropy and nitroapp compatible
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
Before building, ensure you have installed the toolchain for the Pico and that the Pico SDK is properly located on your drive.
|
||||
|
||||
@@ -1,48 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION_MAJOR="7"
|
||||
VERSION_MINOR="0"
|
||||
NO_EDDSA=0
|
||||
VERSION_MINOR="2"
|
||||
SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}"
|
||||
#if ! [[ -z "${GITHUB_SHA}" ]]; then
|
||||
# SUFFIX="${SUFFIX}.${GITHUB_SHA}"
|
||||
#fi
|
||||
|
||||
if [[ $1 == "--no-eddsa" ]]; then
|
||||
NO_EDDSA=1
|
||||
echo "Skipping EDDSA build"
|
||||
fi
|
||||
|
||||
mkdir -p build_release
|
||||
mkdir -p release
|
||||
mkdir -p release_eddsa
|
||||
rm -rf -- release/*
|
||||
if [[ $NO_EDDSA -eq 0 ]]; then
|
||||
rm -rf -- release_eddsa/*
|
||||
fi
|
||||
cd build_release
|
||||
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH:-../../pico-sdk}"
|
||||
SECURE_BOOT_PKEY="${SECURE_BOOT_PKEY:-../../ec_private_key.pem}"
|
||||
board_dir=${PICO_SDK_PATH}/src/boards/include/boards
|
||||
for board in "$board_dir"/*
|
||||
boards=("pico" "pico2")
|
||||
|
||||
for board_name in "${boards[@]}"
|
||||
do
|
||||
board_name="$(basename -- "$board" .h)"
|
||||
rm -rf -- ./*
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name -DSECURE_BOOT_PKEY=${SECURE_BOOT_PKEY}
|
||||
make -j`nproc`
|
||||
mv pico_fido.uf2 ../release/pico_fido_$board_name-$SUFFIX.uf2
|
||||
done
|
||||
|
||||
# Build with EDDSA
|
||||
|
||||
if [[ $NO_EDDSA -eq 0 ]]; then
|
||||
for board in "$board_dir"/*
|
||||
do
|
||||
board_name="$(basename -- "$board" .h)"
|
||||
rm -rf -- ./*
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name -DSECURE_BOOT_PKEY=${SECURE_BOOT_PKEY} -DENABLE_EDDSA=1
|
||||
make -j`nproc`
|
||||
mv pico_fido.uf2 ../release_eddsa/pico_fido_$board_name-$SUFFIX-eddsa1.uf2
|
||||
done
|
||||
fi
|
||||
|
||||
Submodule pico-keys-sdk updated: d0dea3d0c5...263e554cc6
@@ -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_vendor(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();
|
||||
|
||||
@@ -59,6 +60,9 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
if (cap_supported(CAP_FIDO2)) {
|
||||
if (cmd == CTAPHID_CBOR) {
|
||||
if (data[0] != CTAP_GET_NEXT_ASSERTION) {
|
||||
reset_gna_state();
|
||||
}
|
||||
if (data[0] == CTAP_MAKE_CREDENTIAL) {
|
||||
return cbor_make_credential(data + 1, len - 1);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,22 @@ uint32_t timerx = 0;
|
||||
uint8_t *datax = NULL;
|
||||
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) {
|
||||
(void) data;
|
||||
(void) len;
|
||||
@@ -57,19 +73,7 @@ int cbor_get_next_assertion(const uint8_t *data, size_t len) {
|
||||
credentialCounter++;
|
||||
err:
|
||||
if (error != CborNoError || credentialCounter == numberOfCredentialsx) {
|
||||
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;
|
||||
reset_gna_state();
|
||||
if (error == CborErrorImproperValue) {
|
||||
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
|
||||
}
|
||||
|
||||
@@ -613,24 +613,6 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
|
||||
if (user.id.len > 0 && user.parent.name.len > 0 && user.displayName.len > 0) {
|
||||
if (memcmp(user.parent.name.data, "+pico", 5) == 0) {
|
||||
options.rk = pfalse;
|
||||
#ifndef ENABLE_EMULATION
|
||||
uint8_t *p = (uint8_t *)user.parent.name.data + 5;
|
||||
if (memcmp(p, "CommissionProfile", 17) == 0) {
|
||||
ret = phy_unserialize_data(user.id.data, (uint16_t)user.id.len, &phy_data);
|
||||
if (ret == PICOKEY_OK) {
|
||||
ret = phy_save();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t largeBlobKey[32] = {0};
|
||||
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
|
||||
ret = credential_derive_large_blob_key(cred_id, cred_id_len, largeBlobKey);
|
||||
|
||||
@@ -319,7 +319,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
|
||||
credential_free(&rcred);
|
||||
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;
|
||||
credential_free(&rcred);
|
||||
new_record = false;
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "asn1.h"
|
||||
#include "management.h"
|
||||
|
||||
bool is_gpg = true;
|
||||
|
||||
int man_process_apdu();
|
||||
int man_unload();
|
||||
|
||||
@@ -44,6 +46,7 @@ int man_select(app_t *a, uint8_t force) {
|
||||
init_otp();
|
||||
#endif
|
||||
}
|
||||
is_gpg = false;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#ifndef __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_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
||||
@@ -202,10 +202,29 @@ def test_rk_with_allowlist_of_different_rp(resetdevice):
|
||||
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):
|
||||
"""
|
||||
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"}
|
||||
user = generate_random_user()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user