Skey: a FIDO2 security key on the Raspberry Pi Pico

Firmware for the Pico / Pico 2 that acts as a USB FIDO2 authenticator:
CTAP2 and U2F, passkeys, BOOTSEL as the touch button, and clientPIN with
PIN/UV auth protocols 1 and 2. A host simulator runs the same core so the
python-fido2 test suite exercises it without hardware.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ckochandClaude Opus 5.5 committed 2026-10-14 15:07:38 -04:00
commit 6c7e6ab5f5
41 files changed
+4218

No files matched your search

+82
View File
@@ -0,0 +1,82 @@
cmake_minimum_required(VERSION 3.13)
# Two builds share the authenticator core in src/:
# default Pico firmware (pass -DPICO_BOARD=pico2 for a Pico 2)
# -DSKEY_HOST=ON skey_sim, a desktop simulator driven by the tests in tests/
option(SKEY_HOST "Build the host simulator instead of the Pico firmware" OFF)
if (SKEY_HOST)
project(skey C)
else()
if (NOT PICO_SDK_PATH)
if (DEFINED ENV{PICO_SDK_PATH})
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
else()
set(PICO_SDK_PATH ${CMAKE_CURRENT_LIST_DIR}/deps/pico-sdk)
endif()
endif()
include(${PICO_SDK_PATH}/pico_sdk_init.cmake)
project(skey C CXX ASM)
pico_sdk_init()
endif()
set(CMAKE_C_STANDARD 11)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
set(GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated)
add_custom_command(
OUTPUT ${GENERATED_DIR}/attestation_data.c
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_attestation.py
--key-dir ${CMAKE_CURRENT_SOURCE_DIR}/attestation
--out ${GENERATED_DIR}/attestation_data.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/gen_attestation.py
COMMENT "Generating attestation certificate")
set(CORE_SOURCES
src/cbor.c
src/cred.c
src/crypto.c
src/ctap1.c
src/ctap2.c
src/ctaphid.c
src/pin.c
src/presence.c
src/storage.c)
set(WARNINGS -Wall -Wextra)
if (SKEY_HOST)
option(SKEY_SANITIZE "Build the simulator with ASan and UBSan" ON)
find_path(MBEDTLS_INCLUDE_DIR mbedtls/ecdsa.h)
find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
if (NOT MBEDTLS_INCLUDE_DIR OR NOT MBEDCRYPTO_LIBRARY)
message(FATAL_ERROR "mbedtls not found (apt install libmbedtls-dev)")
endif()
add_executable(skey_sim host/main.c host/platform_host.c ${CORE_SOURCES} ${GENERATED_DIR}/attestation_data.c)
target_include_directories(skey_sim PRIVATE src host ${MBEDTLS_INCLUDE_DIR})
target_link_libraries(skey_sim PRIVATE ${MBEDCRYPTO_LIBRARY})
target_compile_options(skey_sim PRIVATE ${WARNINGS})
if (SKEY_SANITIZE)
target_compile_options(skey_sim PRIVATE -fsanitize=address,undefined -fno-omit-frame-pointer)
target_link_options(skey_sim PRIVATE -fsanitize=address,undefined)
endif()
else()
set(PICO_SOURCES firmware/button.c firmware/main.c firmware/platform_pico.c firmware/usb.c)
add_executable(skey ${PICO_SOURCES} ${CORE_SOURCES} ${GENERATED_DIR}/attestation_data.c)
target_include_directories(skey PRIVATE src firmware)
target_link_libraries(skey PRIVATE
pico_stdlib
pico_rand
pico_unique_id
hardware_flash
tinyusb_device
pico_mbedtls)
# The deepest path (makeCredential -> mbedtls ECDSA) needs ~2.3 KB of stack,
# more than the SDK's 2 KB default. Reserve all of SCRATCH_Y instead.
target_compile_definitions(skey PRIVATE PICO_STACK_SIZE=0x1000)
# Only our own sources: the SDK and mbedtls compile into this target too.
set_source_files_properties(${PICO_SOURCES} ${CORE_SOURCES} PROPERTIES COMPILE_OPTIONS "${WARNINGS}")
pico_enable_stdio_usb(skey 0)
pico_enable_stdio_uart(skey 0)
pico_add_extra_outputs(skey)
endif()