77 Commits
v7.0 ... main

Author SHA1 Message Date
Pol Henarejos
9ffcfb4beb Harden core-shared command/result state
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-06-01 13:38:36 +02:00
Pol Henarejos
3ccd6e827f Add cancel button event.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-06-01 01:43:25 +02:00
Pol Henarejos
a2044d697d Set persistent rpId, userName and userDisplay.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-06-01 00:22:18 +02:00
Pol Henarejos
659c04c837 Add slots for button signals.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-06-01 00:15:22 +02:00
Pol Henarejos
a0437dbfb2 Fix otp first build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-05-29 11:36:50 +02:00
Pol Henarejos
604e7868e2 Fix autobuild.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-05-29 00:20:05 +02:00
Pol Henarejos
0e3d0d9a7d Add libtss2 to runners.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-05-28 23:47:20 +02:00
Pol Henarejos
1f4be2a051 Update PicoKeys SDK
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-05-28 23:43:06 +02:00
Pol Henarejos
dc6007d1b4 Fix pico build
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-05-02 12:04:48 +02:00
Pol Henarejos
6aa986ca06 Use ecp keypair calc public instead.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-05-01 21:01:45 +02:00
Pol Henarejos
98145a0ef4 Use dynamic dependence resolver.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-05-01 19:47:56 +02:00
Pol Henarejos
342ae90df8 Upgrade PicoKeys SDK
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-05-01 18:30:19 +02:00
Pol Henarejos
39b9dbb8d6 Upgrade to v7.6
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-04-08 16:03:56 +02:00
Pol Henarejos
b66a80eb01 Upgrade Pico Keys SDK 8.6 and Mbedtls v3.6.6
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-04-06 20:13:51 +02:00
Pol Henarejos
ca15206e77 Fix includes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-04-06 20:13:40 +02:00
Pol Henarejos
ec10becbef Fix build with cyw43 led.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-29 00:02:20 +01:00
Pol Henarejos
b88e52971f Use device key encryption v2.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-27 17:36:28 +01:00
Pol Henarejos
f658ef6eab Remove unused MKEK system.
Since previous releases, DKEK is double-encrypted with AAD, with OTP and PIN derivation, making not necessary an additional MKEK.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-12 23:41:22 +01:00
Pol Henarejos
4e7c40b2bd Mark submodules as system includes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-08 22:05:40 +01:00
Pol Henarejos
f4487a1ff4 Remove redundant declaration.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-08 21:49:04 +01:00
Pol Henarejos
edf6a697ba Do not use FORTIFY as it causes out of memory panics.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-08 20:25:30 +01:00
Pol Henarejos
24978a5476 Apply strict build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-08 19:27:32 +01:00
Pol Henarejos
1795f5c330 Fixed rare race condition.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-08 17:31:35 +01:00
Pol Henarejos
8d81e2eb24 Fix typos.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-07 17:58:18 +01:00
Pol Henarejos
ab3ddf4e23 Beautify cmake files.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-07 13:30:47 +01:00
Pol Henarejos
d8ccf9bd28 Add vendor Admin PIN.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-07 11:36:52 +01:00
Pol Henarejos
0e1512a135 Fix rename oath when old name starts with the newer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-06 18:55:30 +01:00
Pol Henarejos
ac0462525a Fix curve25519 translation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-03-03 20:16:52 +01:00
Pol Henarejos
bb20a75ef4 Fix secure boot enable.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-16 16:29:53 +01:00
Pol Henarejos
8d709cf745 Add support for HIGH/LOW ESP32 LED
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-04 23:46:41 +01:00
Pol Henarejos
bbbbcadf4c Upgrade to v7.4
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-01 20:37:29 +01:00
Pol Henarejos
fbbf1feb49 Fix phy marker write.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-02-01 20:37:17 +01:00
Pol Henarejos
22de41bfe0 Upgrade to Pico Keys SDK 8.5
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-29 16:22:25 +01:00
Pol Henarejos
31a6315721 Transmit CBOR errors in SW x64 with CCID.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-26 17:22:00 +01:00
Pol Henarejos
9fb8d475b3 Old files. Not used anymore.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-26 15:54:59 +01:00
Pol Henarejos
c0298ece7d Remove PHY and MEMORY vendor commands as they are available through rescue applet.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-26 15:40:08 +01:00
Pol Henarejos
bea7706d63 Fix emulation build
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-26 01:27:12 +01:00
Pol Henarejos
cfd22c2d2c Fix ccid max packet length & interface naming.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-26 01:20:22 +01:00
Pol Henarejos
8ea118fe91 Fix OATH in iOS Authenticator. Fixes #248.
For strange reason, iOS app doesn't follow strictly YKOATH spec. When there are remaining bytes after serial, it assumes there's challenge (and thus, access code), but algorithm 7B is there. Apparently algorithm 7B is only returned when challenge is present but I could not see where it is used.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-26 01:18:51 +01:00
Pol Henarejos
8e6f571b48 Move rtc
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-24 01:15:23 +01:00
Pol Henarejos
3c20800839 Add rtc to credential.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-24 01:14:46 +01:00
Pol Henarejos
f2eef5b839 Use new VID:PID allocated to Pico Fido.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 12:33:08 +01:00
Pol Henarejos
bc6ebdd069 Upgrade to new layout
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 12:09:42 +01:00
Pol Henarejos
3f890757ac Not present
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 01:00:18 +01:00
Pol Henarejos
18d68d7e05 Fix needs power cycle logic.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 00:57:31 +01:00
Pol Henarejos
c8d62de621 Add vendor commands via CCID
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 00:26:51 +01:00
Pol Henarejos
60165c21ca Fix vendor keydev loading
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 00:26:27 +01:00
Pol Henarejos
55a60f8875 Fix power_cycle behavior
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-22 00:26:13 +01:00
Pol Henarejos
7ed90007ef Add support for slots 3 & 4 in OTP.
Both slots are activated by clicking three or four times the BOOTSEL button.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-19 16:37:19 +01:00
Pol Henarejos
c23cc9ffe1 Add set/get RTC.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-19 16:36:49 +01:00
Pol Henarejos
dc4565a8fb Fix LED default parameters in Pimoroni boards.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-15 01:17:18 +01:00
Pol Henarejos
804ee68e86 Remove non-standard MAKE CREDENTIAL step.
It may collide with other userName and the purpose is achieved cleaner via Rescue interface.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-08 10:46:51 +01:00
Pol Henarejos
fe49149d86 Update README with up-to-date info.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-06 21:20:04 +01:00
Pol Henarejos
81d97f1a18 Upgrade to v7.2
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 19:56:09 +01:00
Pol Henarejos
d16016cf1e Upgrade Pico Keys SDK to v8.2
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 19:51:11 +01:00
Pol Henarejos
2e0333677b Fix button logic.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 19:39:46 +01:00
Pol Henarejos
becdc94339 Disable button press by default since LED may not be properly configured until it is commissioned.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 19:37:30 +01:00
Pol Henarejos
bd499ae1d4 Remove print
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 19:37:27 +01:00
Pol Henarejos
5fc84d7097 Reset internal state of GA to avoid phantom requests on GNA.
When a previous GA had more than 1 credential, it stored the full list in the internal state. Later, if a GA had only 1 credential, subsequent GNA returned older state of previous non-related GA.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 12:36:44 +01:00
Pol Henarejos
ac7e34522a Fixed resident credential storage when two userId have the same prefix.
Added a specific test for this case.

Fixes #241.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2026-01-05 12:34:04 +01:00
Pol Henarejos
70dec5596a Fix build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-29 20:36:37 +01:00
Pol Henarejos
2331dcb3ec Blink led three times to acknowledge proper commissioning.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-29 20:16:22 +01:00
Pol Henarejos
42b8b0af5f Fix pimoroni led
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-27 22:04:03 +01:00
Pol Henarejos
ceb54d9a08 Move pointer
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-27 16:03:07 +01:00
Pol Henarejos
527943fbba Releaser is available up to 6.7.0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-26 19:53:46 +01:00
Pol Henarejos
7dddfd971e Build only necessary boards
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-11 20:01:08 +01:00
Pol Henarejos
29f942dab9 Update pointer
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-11 15:42:47 +01:00
Pol Henarejos
aa9df892d3 Revert "Move EDDSA to another branch."
This reverts commit 1867f0330f.
2025-12-11 15:41:47 +01:00
Pol Henarejos
7ac2ce30f0 Revert "Move other curves to another branch."
This reverts commit 46720fb387.
2025-12-11 15:40:16 +01:00
Pol Henarejos
e86862033c Revert "Move enterprise attestation to another branch."
This reverts commit 1d21d93b74.
2025-12-11 15:40:10 +01:00
Pol Henarejos
ae36143498 Revert "Move Secure Boot to another branch."
This reverts commit d90dbb6c5f.
2025-12-11 15:39:57 +01:00
Pol Henarejos
d90dbb6c5f Move Secure Boot to another branch.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-09 21:39:14 +01:00
Pol Henarejos
1d21d93b74 Move enterprise attestation to another branch.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-09 21:39:10 +01:00
Pol Henarejos
8b9be258de Fix applet cmp
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-09 19:15:35 +01:00
Pol Henarejos
46720fb387 Move other curves to another branch.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-09 18:52:13 +01:00
Pol Henarejos
1867f0330f Move EDDSA to another branch.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-09 15:56:31 +01:00
Pol Henarejos
bb542e3b83 Add is_gpg flag for fido2.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-12-07 20:34:42 +01:00
45 changed files with 950 additions and 1080 deletions

View File

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

View File

@@ -17,14 +17,31 @@
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)
if(NOT DEFINED ENABLE_POWER_ON_RESET)
set(ENABLE_POWER_ON_RESET 0)
endif()
if(NOT DEFINED ENABLE_PQC)
set(ENABLE_PQC 0)
endif()
set(EXTRA_COMPONENT_DIRS
src/fido
pico-keys-sdk/config/esp32/components/pico-keys-sdk
pico-keys-sdk/config/esp32/components/tinycbor
)
if(ENABLE_PQC)
list(APPEND EXTRA_COMPONENT_DIRS
pico-keys-sdk/config/esp32/components/mlkem512
pico-keys-sdk/config/esp32/components/mlkem768
pico-keys-sdk/config/esp32/components/mlkem1024
)
endif()
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
else()
if(ENABLE_EMULATION)
else()
if(NOT ENABLE_EMULATION)
set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1)
include(pico_sdk_import.cmake)
endif()
@@ -37,32 +54,31 @@ set(CMAKE_CXX_STANDARD 17)
add_executable(pico_fido)
endif()
include(pico-keys-sdk/cmake/options.cmake)
option(ENABLE_POWER_ON_RESET "Enable/disable power cycle on reset" ON)
if(ENABLE_POWER_ON_RESET)
add_definitions(-DENABLE_POWER_ON_RESET=1)
message(STATUS "Power cycle on reset: \t enabled")
else()
add_definitions(-DENABLE_POWER_ON_RESET=0)
message(STATUS "Power cycle on reset: \t disabled")
endif(ENABLE_POWER_ON_RESET)
configure_bool_option(
ENABLE_POWER_ON_RESET
ENABLE_POWER_ON_RESET
"Power cycle on reset: \t enabled"
"Power cycle on reset: \t disabled"
)
option(ENABLE_OATH_APP "Enable/disable OATH application" ON)
if(ENABLE_OATH_APP)
add_definitions(-DENABLE_OATH_APP=1)
message(STATUS "OATH Application: \t\t enabled")
else()
add_definitions(-DENABLE_OATH_APP=0)
message(STATUS "OATH Application: \t\t disabled")
endif(ENABLE_OATH_APP)
configure_bool_option(
ENABLE_OATH_APP
ENABLE_OATH_APP
"OATH Application: \t\t enabled"
"OATH Application: \t\t disabled"
)
option(ENABLE_OTP_APP "Enable/disable OTP application" ON)
if(ENABLE_OTP_APP)
add_definitions(-DENABLE_OTP_APP=1)
message(STATUS "OTP Application: \t\t enabled")
else()
add_definitions(-DENABLE_OTP_APP=0)
message(STATUS "OTP Application: \t\t disabled")
endif(ENABLE_OTP_APP)
configure_bool_option(
ENABLE_OTP_APP
ENABLE_OTP_APP
"OTP Application: \t\t enabled"
"OTP Application: \t\t disabled"
)
if(ENABLE_OTP_APP OR ENABLE_OATH_APP)
set(USB_ITF_CCID 1)
@@ -72,16 +88,15 @@ else()
endif()
set(USB_ITF_HID 1)
include(pico-keys-sdk/pico_keys_sdk_import.cmake)
include(pico-keys-sdk/picokeys_sdk_import.cmake)
if(NOT ESP_PLATFORM)
set(SOURCES ${PICO_KEYS_SOURCES})
set(SOURCES ${PICOKEYS_SOURCES})
endif()
set(SOURCES ${SOURCES}
list(APPEND SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/fido/fido.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/files.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/kek.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_register.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_authenticate.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_version.c
@@ -101,69 +116,99 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/src/fido/management.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/defs.c
)
if (${ENABLE_OATH_APP})
set(SOURCES ${SOURCES}
if(ENABLE_OATH_APP)
list(APPEND SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/fido/oath.c
)
endif()
if (${ENABLE_OTP_APP})
set(SOURCES ${SOURCES}
if(ENABLE_OTP_APP)
list(APPEND SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/fido/otp.c
)
endif()
SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/fido/version.h" 2)
SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/fido/version.h")
if(ESP_PLATFORM)
project(pico_fido)
endif()
set(INCLUDES ${INCLUDES}
${CMAKE_CURRENT_LIST_DIR}/src/fido
)
if(NOT ESP_PLATFORM)
target_sources(pico_fido PUBLIC ${SOURCES})
target_include_directories(pico_fido PUBLIC ${INCLUDES})
target_compile_options(pico_fido PUBLIC
set(COMMON_COMPILE_OPTIONS
-Wall
)
if (NOT MSVC)
target_compile_options(pico_fido PUBLIC
-Werror
target_compile_options(pico_fido PRIVATE ${COMMON_COMPILE_OPTIONS})
picokeys_apply_strict_flags(
SOURCES ${SOURCES}
FILTER_REGEX "/src/fido/|/pico-keys-sdk/src/|/pico-keys-sdk/config/"
)
if(NOT MSVC)
string(FIND ${CMAKE_C_COMPILER} ":" COMPILER_COLON)
if(${COMPILER_COLON} GREATER_EQUAL 0)
target_compile_options(pico_fido PUBLIC
target_compile_options(pico_fido PRIVATE
-Wno-error=use-after-free
)
endif()
endif(NOT MSVC)
endif()
if(ENABLE_EMULATION)
if(NOT MSVC)
target_compile_options(pico_fido PUBLIC
set(EMULATION_NON_MSVC_COMPILE_OPTIONS
-fdata-sections
-ffunction-sections
)
endif(NOT MSVC)
target_compile_options(pico_fido PRIVATE ${EMULATION_NON_MSVC_COMPILE_OPTIONS})
endif()
if(APPLE)
target_link_options(pico_fido PUBLIC
set(EMULATION_APPLE_LINK_OPTIONS
-Wl,-dead_strip
)
target_link_options(pico_fido PRIVATE ${EMULATION_APPLE_LINK_OPTIONS})
if(DEBUG_APDU)
target_compile_options(pico_fido PUBLIC
-fsanitize=address -g -O1 -fno-omit-frame-pointer)
target_link_options(pico_fido PUBLIC
-fsanitize=address -g -O1 -fno-omit-frame-pointer)
set(DEBUG_APDU_SANITIZER_OPTIONS
-fsanitize=address
-g
-O1
-fno-omit-frame-pointer
)
target_compile_options(pico_fido PRIVATE ${DEBUG_APDU_SANITIZER_OPTIONS})
target_link_options(pico_fido PRIVATE ${DEBUG_APDU_SANITIZER_OPTIONS})
endif()
else()
target_link_options(pico_fido PUBLIC
set(EMULATION_NON_APPLE_LINK_OPTIONS
-Wl,--gc-sections
)
endif (APPLE)
target_link_libraries(pico_fido PRIVATE pthread m)
target_link_options(pico_fido PRIVATE ${EMULATION_NON_APPLE_LINK_OPTIONS})
endif()
target_link_libraries(pico_fido PRIVATE picokeys_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)
target_link_libraries(
pico_fido
PRIVATE
picokeys_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})
endif()
endif()

View File

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

View File

@@ -1,48 +1,25 @@
#!/bin/bash
VERSION_MAJOR="7"
VERSION_MINOR="0"
NO_EDDSA=0
VERSION_MINOR="6"
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

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

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#if defined(PICO_PLATFORM)
#include "pico/stdlib.h"
#endif
@@ -30,25 +30,13 @@
const bool _btrue = true, _bfalse = false;
int cbor_reset();
int cbor_get_info();
int cbor_make_credential(const uint8_t *data, size_t len);
int cbor_client_pin(const uint8_t *data, size_t len);
int cbor_get_assertion(const uint8_t *data, size_t len, bool next);
int cbor_get_next_assertion(const uint8_t *data, size_t len);
int cbor_selection();
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 int cmd_read_config();
const uint8_t aaguid[16] = { 0x89, 0xFB, 0x94, 0xB7, 0x06, 0xC9, 0x36, 0x73, 0x9B, 0x7E, 0x30, 0x52, 0x6D, 0x96, 0x81, 0x45 }; // First 16 bytes of SHA256("Pico FIDO2")
const uint8_t *cbor_data = NULL;
size_t cbor_len = 0;
uint8_t cbor_cmd = 0;
static const uint8_t *volatile cbor_data = NULL;
static volatile size_t cbor_len = 0;
static volatile uint8_t cbor_cmd = 0;
int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
if (len == 0 && cmd == CTAPHID_CBOR) {
@@ -59,6 +47,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);
}
@@ -94,7 +85,7 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
return cbor_vendor(data, len);
}
else if (cmd == 0xC2) {
if (cmd_read_config() == 0x9000) {
if (man_get_config() == 0) {
memmove(res_APDU-1, res_APDU, res_APDU_size);
res_APDU_size -= 1;
return 0;
@@ -104,6 +95,7 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
return CTAP1_ERR_INVALID_CMD;
}
void *cbor_thread(void *arg) __attribute__((unused));
void *cbor_thread(void *arg) {
(void)arg;
card_init_core1();
@@ -116,7 +108,10 @@ void *cbor_thread(void *arg) {
if (m == EV_EXIT) {
break;
}
apdu.sw = (uint16_t)cbor_parse(cbor_cmd, cbor_data, cbor_len);
const uint8_t *data = (const uint8_t *)cbor_data;
size_t len = cbor_len;
uint8_t cmd = cbor_cmd;
apdu.sw = (uint16_t)cbor_parse(cmd, data, len);
if (apdu.sw == 0) {
DEBUG_DATA(res_APDU, res_APDU_size);
}
@@ -148,7 +143,7 @@ int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len) {
return 2; // CBOR processing
}
CborError COSE_key_params(int crv, int alg, mbedtls_ecp_group *grp, mbedtls_ecp_point *Q, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder) {
static CborError COSE_key_params(int crv, int alg, mbedtls_ecp_group *grp, mbedtls_ecp_point *Q, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder) {
CborError error = CborNoError;
int kty = 1;
if (crv == FIDO2_CURVE_P256 || crv == FIDO2_CURVE_P384 || crv == FIDO2_CURVE_P521 ||

View File

@@ -15,16 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#ifndef ESP_PLATFORM
#include "common.h"
#else
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#endif
#include "picokeys.h"
#include "mbedtls/ecp.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/sha256.h"
#include "mbedtls/hkdf.h"
#include "mbedtls/constant_time.h"
#include "cbor.h"
#include "ctap.h"
#include "ctap2_cbor.h"
@@ -37,16 +33,14 @@
#include "random.h"
#include "crypto_utils.h"
#include "apdu.h"
#include "kek.h"
uint32_t usage_timer = 0, initial_usage_time_limit = 0;
uint32_t max_usage_time_period = 600 * 1000;
bool needs_power_cycle = false;
static mbedtls_ecdh_context hkey;
static bool hkey_init = false;
extern int encrypt_keydev_f1(const uint8_t keydev[32]);
int beginUsingPinUvAuthToken(bool userIsPresent) {
static int beginUsingPinUvAuthToken(bool userIsPresent) {
paut.user_present = userIsPresent;
paut.user_verified = true;
initial_usage_time_limit = board_millis();
@@ -55,25 +49,25 @@ int beginUsingPinUvAuthToken(bool userIsPresent) {
return 0;
}
void clearUserPresentFlag() {
void clearUserPresentFlag(void) {
if (paut.in_use == true) {
paut.user_present = false;
}
}
void clearUserVerifiedFlag() {
void clearUserVerifiedFlag(void) {
if (paut.in_use == true) {
paut.user_verified = false;
}
}
void clearPinUvAuthTokenPermissionsExceptLbw() {
void clearPinUvAuthTokenPermissionsExceptLbw(void) {
if (paut.in_use == true) {
paut.permissions = CTAP_PERMISSION_LBW;
}
}
void stopUsingPinUvAuthToken() {
static void stopUsingPinUvAuthToken(void) {
paut.permissions = 0;
usage_timer = 0;
paut.in_use = false;
@@ -84,21 +78,21 @@ void stopUsingPinUvAuthToken() {
user_present_time_limit = 0;
}
bool getUserPresentFlagValue() {
bool getUserPresentFlagValue(void) {
if (paut.in_use != true) {
paut.user_present = false;
}
return paut.user_present;
}
bool getUserVerifiedFlagValue() {
bool getUserVerifiedFlagValue(void) {
if (paut.in_use != true) {
paut.user_verified = false;
}
return paut.user_verified;
}
int regenerate() {
static int regenerate(void) {
if (hkey_init == true) {
mbedtls_ecdh_free(&hkey);
}
@@ -106,7 +100,7 @@ int regenerate() {
mbedtls_ecdh_init(&hkey);
hkey_init = true;
mbedtls_ecdh_setup(&hkey, MBEDTLS_ECP_DP_SECP256R1);
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_gen, NULL);
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_fill_iterator, NULL);
mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1);
if (ret != 0) {
return ret;
@@ -114,7 +108,7 @@ int regenerate() {
return 0;
}
int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) {
static int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) {
int ret = 0;
uint8_t buf[32];
ret = mbedtls_mpi_write_binary(z, buf, sizeof(buf));
@@ -138,25 +132,25 @@ int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) {
int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret) {
mbedtls_mpi z;
mbedtls_mpi_init(&z);
int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp, &z, Q, &hkey.ctx.mbed_ecdh.d, random_gen, NULL);
int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp, &z, Q, &hkey.ctx.mbed_ecdh.d, random_fill_iterator, NULL);
ret = kdf(protocol, &z, sharedSecret);
mbedtls_mpi_free(&z);
return ret;
}
void resetAuthToken(bool persistent) {
static void resetAuthToken(bool persistent) {
uint16_t fid = EF_AUTHTOKEN;
if (persistent) {
fid = EF_PAUTHTOKEN;
}
file_t *ef = search_by_fid(fid, NULL, SPECIFY_EF);
file_t *ef = file_search_by_fid(fid, NULL, SPECIFY_EF);
uint8_t t[32];
random_gen(NULL, t, sizeof(t));
random_fill_buffer(t, sizeof(t));
file_put_data(ef, t, sizeof(t));
low_flash_available();
flash_commit();
}
int resetPinUvAuthToken() {
int resetPinUvAuthToken(void) {
resetAuthToken(false);
paut.permissions = 0;
paut.data = file_get_data(ef_authtoken);
@@ -164,9 +158,9 @@ int resetPinUvAuthToken() {
return 0;
}
int resetPersistentPinUvAuthToken() {
int resetPersistentPinUvAuthToken(void) {
resetAuthToken(true);
file_t *ef_pauthtoken = search_by_fid(EF_PAUTHTOKEN, NULL, SPECIFY_EF);
file_t *ef_pauthtoken = file_search_by_fid(EF_PAUTHTOKEN, NULL, SPECIFY_EF);
ppaut.permissions = 0;
ppaut.data = file_get_data(ef_pauthtoken);
ppaut.len = file_get_size(ef_pauthtoken);
@@ -176,12 +170,12 @@ int resetPersistentPinUvAuthToken() {
int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out) {
if (protocol == 1) {
memcpy(out, in, in_len);
return aes_encrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len);
return aes_encrypt(key, NULL, 32 * 8, PICOKEYS_AES_MODE_CBC, out, in_len);
}
else if (protocol == 2) {
random_gen(NULL, out, IV_SIZE);
random_fill_buffer(out, IV_SIZE);
memcpy(out + IV_SIZE, in, in_len);
return aes_encrypt(key + 32, out, 32 * 8, PICO_KEYS_AES_MODE_CBC, out + IV_SIZE, in_len);
return aes_encrypt(key + 32, out, 32 * 8, PICOKEYS_AES_MODE_CBC, out + IV_SIZE, in_len);
}
return -1;
@@ -190,17 +184,17 @@ int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in
int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out) {
if (protocol == 1) {
memcpy(out, in, in_len);
return aes_decrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len);
return aes_decrypt(key, NULL, 32 * 8, PICOKEYS_AES_MODE_CBC, out, in_len);
}
else if (protocol == 2) {
memcpy(out, in + IV_SIZE, in_len - IV_SIZE);
return aes_decrypt(key + 32, in, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len - IV_SIZE);
return aes_decrypt(key + 32, in, 32 * 8, PICOKEYS_AES_MODE_CBC, out, in_len - IV_SIZE);
}
return -1;
}
int authenticate(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign) {
static int __attribute__((unused)) authenticate(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign) {
uint8_t hmac[32];
int ret =
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
@@ -228,24 +222,24 @@ int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, uint16_t l
return ret;
}
if (protocol == 1) {
return ct_memcmp(sign, hmac, 16);
return mbedtls_ct_memcmp(sign, hmac, 16);
}
else if (protocol == 2) {
return ct_memcmp(sign, hmac, 32);
return mbedtls_ct_memcmp(sign, hmac, 32);
}
return -1;
}
int initialize() {
static int initialize(void) {
regenerate();
return resetPinUvAuthToken();
}
int getPublicKey() {
static int __attribute__((unused)) getPublicKey(void) {
return 0;
}
int pinUvAuthTokenUsageTimerObserver() {
static int __attribute__((unused)) pinUvAuthTokenUsageTimerObserver(void) {
if (usage_timer == 0) {
return -1;
}
@@ -266,16 +260,16 @@ int pinUvAuthTokenUsageTimerObserver() {
return 0;
}
int check_keydev_encrypted(const uint8_t pin_token[32]) {
static int check_keydev_encrypted(const uint8_t pin_token[32]) {
if (file_get_data(ef_keydev) && *file_get_data(ef_keydev) == 0x01) {
uint8_t tmp_keydev[61];
tmp_keydev[0] = 0x02; // Change format to encrypted
encrypt_with_aad(pin_token, file_get_data(ef_keydev) + 1, 32, tmp_keydev + 1);
tmp_keydev[0] = 0x03; // Change format to encrypted
encrypt_with_aad(pin_token, file_get_data(ef_keydev) + 1, 32, 2, tmp_keydev + 1);
file_put_data(ef_keydev, tmp_keydev, sizeof(tmp_keydev));
mbedtls_platform_zeroize(tmp_keydev, sizeof(tmp_keydev));
low_flash_available();
flash_commit();
}
return PICOKEY_OK;
return PICOKEYS_OK;
}
uint8_t new_pin_mismatches = 0;
@@ -407,7 +401,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
pin_len++;
}
uint8_t minPin = 4;
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
file_t *ef_minpin = file_search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
if (file_has_data(ef_minpin)) {
minPin = *file_get_data(ef_minpin);
}
@@ -419,17 +413,20 @@ 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();
flash_commit();
pin_derive_session(dhash, 16, session_pin);
ret = check_keydev_encrypted(session_pin);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
CBOR_ERROR(ret);
}
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 +455,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) {
@@ -475,7 +475,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
memcpy(pin_data, file_get_data(ef_pin), file_get_size(ef_pin));
pin_data[0] -= 1;
file_put_data(ef_pin, pin_data, file_get_size(ef_pin));
low_flash_available();
flash_commit();
uint8_t retries = pin_data[0];
uint8_t paddedNewPin[64];
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, pinHashEnc.data, (uint16_t)pinHashEnc.len, paddedNewPin);
@@ -492,7 +492,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
pin_derive_verifier(paddedNewPin, 16, dhash);
}
if (ct_memcmp(dhash, file_get_data(ef_pin) + off, 32) != 0) {
if (mbedtls_ct_memcmp(dhash, file_get_data(ef_pin) + off, 32) != 0) {
regenerate();
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
if (retries == 0) {
@@ -513,7 +513,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
hash_multi(paddedNewPin, 16, session_pin);
ret = load_keydev(keydev);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
}
encrypt_keydev_f1(keydev);
@@ -521,10 +521,10 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
pin_derive_session(paddedNewPin, 16, session_pin);
pin_data[0] = MAX_PIN_RETRIES;
file_put_data(ef_pin, pin_data, sizeof(pin_data));
low_flash_available();
flash_commit();
ret = check_keydev_encrypted(session_pin);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
CBOR_ERROR(ret);
}
@@ -542,7 +542,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
pin_len++;
}
uint8_t minPin = 4;
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
file_t *ef_minpin = file_search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
if (file_has_data(ef_minpin)) {
minPin = *file_get_data(ef_minpin);
}
@@ -552,7 +552,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
// New PIN is valid and verified
ret = load_keydev(keydev);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
}
encrypt_keydev_f1(keydev);
@@ -560,17 +560,17 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash);
pin_derive_session(dhash, 16, session_pin);
ret = check_keydev_encrypted(session_pin);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
CBOR_ERROR(ret);
}
low_flash_available();
flash_commit();
pin_data[0] = MAX_PIN_RETRIES;
pin_data[1] = pin_len;
pin_data[2] = 1; // New format indicator
pin_derive_verifier(dhash, 16, pin_data + 3);
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 && ct_memcmp(pin_data + 3, file_get_data(ef_pin) + 3, 32) == 0) {
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 && mbedtls_ct_memcmp(pin_data + 3, file_get_data(ef_pin) + 3, 32) == 0) {
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
}
file_put_data(ef_pin, pin_data, sizeof(pin_data));
@@ -584,9 +584,10 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
file_put_data(ef_minpin, tmpf, file_get_size(ef_minpin));
free(tmpf);
}
low_flash_available();
flash_commit();
resetPinUvAuthToken();
resetPersistentPinUvAuthToken();
needs_power_cycle = false;
goto err; // No return
}
else if (subcommand == 0x9 || subcommand == 0x5) { //getPinUvAuthTokenUsingPinWithPermissions
@@ -623,6 +624,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) {
@@ -633,7 +637,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
memcpy(pin_data, file_get_data(ef_pin), file_get_size(ef_pin));
pin_data[0] -= 1;
file_put_data(ef_pin, pin_data, file_get_size(ef_pin));
low_flash_available();
flash_commit();
uint8_t retries = pin_data[0];
uint8_t paddedNewPin[64], poff = ((uint8_t)pinUvAuthProtocol - 1) * IV_SIZE;
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, pinHashEnc.data, (uint16_t)pinHashEnc.len, paddedNewPin);
@@ -649,7 +653,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
else {
pin_derive_verifier(paddedNewPin, 16, dhash);
}
if (ct_memcmp(dhash, file_get_data(ef_pin) + off, 32) != 0) {
if (mbedtls_ct_memcmp(dhash, file_get_data(ef_pin) + off, 32) != 0) {
regenerate();
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
mbedtls_platform_zeroize(dhash, sizeof(dhash));
@@ -672,7 +676,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
pin_derive_verifier(paddedNewPin, 16, pin_data + 3);
hash_multi(paddedNewPin, 16, session_pin);
ret = load_keydev(keydev);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
}
encrypt_keydev_f1(keydev);
@@ -680,7 +684,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
pin_derive_session(paddedNewPin, 16, session_pin);
ret = check_keydev_encrypted(session_pin);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
CBOR_ERROR(ret);
}
@@ -690,8 +694,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
file_put_data(ef_pin, pin_data, sizeof(pin_data));
mbedtls_platform_zeroize(pin_data, sizeof(pin_data));
low_flash_available();
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
flash_commit();
file_t *ef_minpin = file_search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
}
@@ -720,6 +724,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

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "ctap2_cbor.h"
#include "fido.h"
#include "ctap.h"
@@ -31,9 +31,6 @@
extern uint8_t keydev_dec[32];
extern bool has_keydev_dec;
extern void resetPersistentPinUvAuthToken();
extern void resetPinUvAuthToken();
int cbor_config(const uint8_t *data, size_t len) {
CborParser parser;
CborValue map;
@@ -144,12 +141,6 @@ int cbor_config(const uint8_t *data, size_t len) {
}
if (subcommand == 0xFF) {
#ifndef ENABLE_EMULATION
const bool is_phy = (vendorCommandId == CTAP_CONFIG_PHY_VIDPID ||
vendorCommandId == CTAP_CONFIG_PHY_LED_GPIO ||
vendorCommandId == CTAP_CONFIG_PHY_LED_BTNESS ||
vendorCommandId == CTAP_CONFIG_PHY_OPTS);
#endif
if (vendorCommandId == CTAP_CONFIG_AUT_DISABLE){
if (!file_has_data(ef_keydev_enc)) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
@@ -160,7 +151,7 @@ int cbor_config(const uint8_t *data, size_t len) {
file_put_data(ef_keydev, keydev_dec, sizeof(keydev_dec));
mbedtls_platform_zeroize(keydev_dec, sizeof(keydev_dec));
file_put_data(ef_keydev_enc, NULL, 0); // Set ef to 0 bytes
low_flash_available();
flash_commit();
}
else if (vendorCommandId == CTAP_CONFIG_AUT_ENABLE) {
if (!file_has_data(ef_keydev)) {
@@ -177,7 +168,7 @@ int cbor_config(const uint8_t *data, size_t len) {
}
uint8_t key_dev_enc[12 + 32 + 16];
random_gen(NULL, key_dev_enc, 12);
random_fill_buffer(key_dev_enc, 12);
mbedtls_chachapoly_init(&chatx);
mbedtls_chachapoly_setkey(&chatx, vendorParamByteString.data);
ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, file_get_size(ef_keydev), key_dev_enc, NULL, 0, file_get_data(ef_keydev), key_dev_enc + 12, key_dev_enc + 12 + file_get_size(ef_keydev));
@@ -190,36 +181,17 @@ int cbor_config(const uint8_t *data, size_t len) {
mbedtls_platform_zeroize(key_dev_enc, sizeof(key_dev_enc));
file_put_data(ef_keydev, key_dev_enc, file_get_size(ef_keydev)); // Overwrite ef with 0
file_put_data(ef_keydev, NULL, 0); // Set ef to 0 bytes
low_flash_available();
flash_commit();
}
#ifndef ENABLE_EMULATION
else if (vendorCommandId == CTAP_CONFIG_PHY_VIDPID) {
phy_data.vid = (vendorParamInt >> 16) & 0xFFFF;
phy_data.pid = vendorParamInt & 0xFFFF;
phy_data.vidpid_present = true;
}
else if (vendorCommandId == CTAP_CONFIG_PHY_LED_GPIO) {
phy_data.led_gpio = (uint8_t)vendorParamInt;
phy_data.led_gpio_present = true;
}
else if (vendorCommandId == CTAP_CONFIG_PHY_LED_BTNESS) {
phy_data.led_brightness = (uint8_t)vendorParamInt;
phy_data.led_brightness_present = true;
}
else if (vendorCommandId == CTAP_CONFIG_PHY_OPTS) {
phy_data.opts = (uint16_t)vendorParamInt;
}
#endif
else if (vendorCommandId == CTAP_CONFIG_EA_UPLOAD) {
if (vendorParamByteString.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
file_t *ef_ee_ea = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
file_t *ef_ee_ea = file_search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
if (ef_ee_ea) {
file_put_data(ef_ee_ea, vendorParamByteString.data, (uint16_t)vendorParamByteString.len);
}
low_flash_available();
flash_commit();
}
else if (vendorCommandId == CTAP_CONFIG_PIN_POLICY) {
file_t *ef_pin_policy = file_new(EF_PIN_COMPLEXITY_POLICY);
@@ -234,21 +206,16 @@ int cbor_config(const uint8_t *data, size_t len) {
free(val);
}
}
low_flash_available();
flash_commit();
}
else {
CBOR_ERROR(CTAP2_ERR_INVALID_SUBCOMMAND);
}
#ifndef ENABLE_EMULATION
if (is_phy && phy_save() != PICOKEY_OK) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
#endif
goto err;
}
else if (subcommand == 0x03) {
uint8_t currentMinPinLen = 4;
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
file_t *ef_minpin = file_search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
if (file_has_data(ef_minpin)) {
currentMinPinLen = *file_get_data(ef_minpin);
}
@@ -275,7 +242,7 @@ int cbor_config(const uint8_t *data, size_t len) {
mbedtls_sha256((uint8_t *) minPinLengthRPIDs[m].data, minPinLengthRPIDs[m].len, dataf + 2 + m * 32, 0);
}
file_put_data(ef_minpin, dataf, (uint16_t)(2 + minPinLengthRPIDs_len * 32));
low_flash_available();
flash_commit();
free(dataf);
goto err; //No return
}

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "fido.h"
#include "ctap.h"
#include "hid/ctap_hid.h"
@@ -137,7 +137,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
uint8_t existing = 0;
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
if (file_has_data(search_dynamic_file((uint16_t)(EF_CRED + i)))) {
if (file_has_data(file_search((uint16_t)(EF_CRED + i)))) {
existing++;
}
}
@@ -173,7 +173,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
uint8_t skip = 0;
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *tef = search_dynamic_file((uint16_t)(EF_RP + i));
file_t *tef = file_search((uint16_t)(EF_RP + i));
if (file_has_data(tef) && *file_get_data(tef) > 0) {
if (++skip == rp_counter) {
if (rp_ef == NULL) {
@@ -239,7 +239,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
file_t *cred_ef = NULL;
uint8_t skip = 0;
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *tef = search_dynamic_file((uint16_t)(EF_CRED + i));
file_t *tef = file_search((uint16_t)(EF_CRED + i));
if (file_has_data(tef) && memcmp(file_get_data(tef), rpIdHash.data, 32) == 0) {
if (++skip == cred_counter) {
if (cred_ef == NULL) {
@@ -373,20 +373,20 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
file_t *ef = file_search((uint16_t)(EF_CRED + i));
if (file_has_data(ef) && memcmp(file_get_data(ef) + 32, credentialId.id.data, CRED_RESIDENT_LEN) == 0) {
uint8_t *rp_id_hash = file_get_data(ef);
if (delete_file(ef) != 0) {
if (file_delete(ef) != 0) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
for (int j = 0; j < MAX_RESIDENT_CREDENTIALS; j++) {
file_t *rp_ef = search_dynamic_file((uint16_t)(EF_RP + j));
file_t *rp_ef = file_search((uint16_t)(EF_RP + j));
if (file_has_data(rp_ef) && memcmp(file_get_data(rp_ef) + 1, rp_id_hash, 32) == 0) {
uint8_t *rp_data = (uint8_t *) calloc(1, file_get_size(rp_ef));
memcpy(rp_data, file_get_data(rp_ef), file_get_size(rp_ef));
rp_data[0] -= 1;
if (rp_data[0] == 0) {
delete_file(rp_ef);
file_delete(rp_ef);
}
else {
file_put_data(rp_ef, rp_data, file_get_size(rp_ef));
@@ -395,7 +395,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
break;
}
}
low_flash_available();
flash_commit();
goto err; //no error
}
}
@@ -415,7 +415,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
file_t *ef = file_search((uint16_t)(EF_CRED + i));
if (file_has_data(ef) && memcmp(file_get_data(ef) + 32, credentialId.id.data, CRED_RESIDENT_LEN) == 0) {
Credential cred = { 0 };
uint8_t *rp_id_hash = file_get_data(ef);
@@ -439,7 +439,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
if (credential_store(newcred, newcred_len, rp_id_hash) != 0) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
low_flash_available();
flash_commit();
goto err; //no error
}
}

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "cbor.h"
#include "ctap.h"
#if defined(PICO_PLATFORM)
@@ -32,6 +32,7 @@
#include "random.h"
int cbor_get_assertion(const uint8_t *data, size_t len, bool next);
extern char *rp_id, *user_name, *display_name;
bool residentx = false;
Credential credsx[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
@@ -42,6 +43,22 @@ uint32_t timerx = 0;
uint8_t *datax = NULL;
size_t lenx = 0;
void reset_gna_state(void) {
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 +74,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;
}
@@ -201,6 +206,9 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
if (rpId.present == false || clientDataHash.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
rp_id = rpId.data;
user_name = NULL;
display_name = NULL;
uint8_t flags = 0;
uint8_t rp_id_hash[32] = {0};
@@ -297,7 +305,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
if (credential_is_resident(allowList[e].id.data, allowList[e].id.len)) {
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
file_t *ef = file_search((uint16_t)(EF_CRED + i));
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
continue;
}
@@ -324,7 +332,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
// Even we provide allowList, we need to check if the credential is resident
if (!resident) {
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
file_t *ef = file_search((uint16_t)(EF_CRED + i));
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
continue;
}
@@ -343,7 +351,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
else {
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
file_t *ef = file_search((uint16_t)(EF_CRED + i));
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
continue;
}
@@ -397,7 +405,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
if (credential_is_resident(allowList[e].id.data, allowList[e].id.len)) {
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
file_t *ef = file_search((uint16_t)(EF_CRED + i));
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
continue;
}
@@ -424,7 +432,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;
@@ -607,7 +615,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
uint8_t *pa = aut_data;
memcpy(pa, rp_id_hash, 32); pa += 32;
*pa++ = flags;
pa += put_uint32_t_be(ctr, pa);
pa += put_uint32_be(ctr, pa);
memcpy(pa, ext, ext_len); pa += ext_len;
if ((size_t)(pa - aut_data) != aut_data_len) {
CBOR_ERROR(CTAP1_ERR_OTHER);
@@ -640,11 +648,11 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
#endif
if (md != NULL) {
ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash);
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_fill_iterator, NULL);
}
#ifdef MBEDTLS_EDDSA_C
else {
ret = mbedtls_eddsa_write_signature(&ekey, aut_data, aut_data_len + clientDataHash.len, sig, sizeof(sig), &olen, MBEDTLS_EDDSA_PURE, NULL, 0, random_gen, NULL);
ret = mbedtls_eddsa_write_signature(&ekey, aut_data, aut_data_len + clientDataHash.len, sig, sizeof(sig), &olen, MBEDTLS_EDDSA_PURE, NULL, 0, random_fill_iterator, NULL);
}
#endif
}
@@ -735,7 +743,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
ctr++;
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
low_flash_available();
flash_commit();
err:
CBOR_FREE_BYTE_STRING(clientDataHash);
CBOR_FREE_BYTE_STRING(pinUvAuthParam);

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "ctap2_cbor.h"
#include "hid/ctap_hid.h"
#include "fido.h"
@@ -24,7 +24,7 @@
#include "apdu.h"
#include "version.h"
int cbor_get_info() {
int cbor_get_info(void) {
CborEncoder encoder, mapEncoder, arrayEncoder, mapEncoder2;
CborError error = CborNoError;
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
@@ -36,7 +36,7 @@ int cbor_get_info() {
#else
lfields++;
#endif
file_t *ef_pin_policy = search_by_fid(EF_PIN_COMPLEXITY_POLICY, NULL, SPECIFY_EF);
file_t *ef_pin_policy = file_search_by_fid(EF_PIN_COMPLEXITY_POLICY, NULL, SPECIFY_EF);
if (file_has_data(ef_pin_policy)) {
lfields += 2;
}
@@ -144,7 +144,7 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_LARGE_BLOB_SIZE)); // maxSerializedLargeBlobArray
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
file_t *ef_minpin = file_search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "ctap2_cbor.h"
#include "fido.h"
#include "ctap.h"
@@ -129,7 +129,7 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
uint8_t verify_data[70] = { 0 };
memset(verify_data, 0xff, 32);
verify_data[32] = 0x0C;
put_uint32_t_le((uint32_t)offset, verify_data + 34);
put_uint32_le((uint32_t)offset, verify_data + 34);
mbedtls_sha256(set.data, set.len, verify_data + 38, 0);
if (verify((uint8_t)pinUvAuthProtocol, paut.data, verify_data, (uint16_t)sizeof(verify_data), pinUvAuthParam.data) != 0) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
@@ -152,7 +152,7 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_INTEGRITY_FAILURE);
}
file_put_data(ef_largeblob, temp_lba, (uint16_t)expectedLength);
low_flash_available();
flash_commit();
}
goto err;
}

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "cbor_make_credential.h"
#include "ctap2_cbor.h"
#include "hid/ctap_hid.h"
@@ -28,6 +28,8 @@
#include "random.h"
#include "crypto_utils.h"
char *rp_id = NULL, *user_name = NULL, *display_name = NULL;
int cbor_make_credential(const uint8_t *data, size_t len) {
CborParser parser;
CborValue map;
@@ -192,6 +194,9 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
}
CBOR_PARSE_MAP_END(map, 1);
rp_id = rp.id.data;
user_name = user.parent.name.data;
display_name = user.displayName.data;
uint8_t flags = FIDO2_AUT_FLAG_AT;
uint8_t rp_id_hash[32] = {0};
@@ -352,7 +357,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
Credential ecred = {0};
if (credential_is_resident(excludeList[e].id.data, excludeList[e].id.len)) {
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef_cred = search_dynamic_file((uint16_t)(EF_CRED + i));
file_t *ef_cred = file_search((uint16_t)(EF_CRED + i));
if (!file_has_data(ef_cred) || memcmp(file_get_data(ef_cred), rp_id_hash, 32) != 0) {
continue;
}
@@ -423,7 +428,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
l++;
}
if (extensions.minPinLength == ptrue) {
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
file_t *ef_minpin = file_search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
if (file_has_data(ef_minpin)) {
uint8_t *minpin_data = file_get_data(ef_minpin);
for (int o = 2; o < file_get_size(ef_minpin); o += 32) {
@@ -517,7 +522,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
if (pin_complexity_policy == ptrue) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "pinComplexityPolicy"));
file_t *ef_pin_complexity_policy = search_by_fid(EF_PIN_COMPLEXITY_POLICY, NULL, SPECIFY_EF);
file_t *ef_pin_complexity_policy = file_search_by_fid(EF_PIN_COMPLEXITY_POLICY, NULL, SPECIFY_EF);
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, file_has_data(ef_pin_complexity_policy)));
}
@@ -550,16 +555,16 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
uint8_t *pa = aut_data;
memcpy(pa, rp_id_hash, 32); pa += 32;
*pa++ = flags;
pa += put_uint32_t_be(ctr, pa);
pa += put_uint32_be(ctr, pa);
memcpy(pa, aaguid, 16); pa += 16;
if (options.rk == ptrue) {
uint8_t cred_idr[CRED_RESIDENT_LEN] = {0};
pa += put_uint16_t_be(sizeof(cred_idr), pa);
pa += put_uint16_be(sizeof(cred_idr), pa);
credential_derive_resident(cred_id, cred_id_len, cred_idr);
memcpy(pa, cred_idr, sizeof(cred_idr)); pa += sizeof(cred_idr);
}
else {
pa += put_uint16_t_be(cred_id_len, pa);
pa += put_uint16_be(cred_id_len, pa);
memcpy(pa, cred_id, cred_id_len); pa += (uint16_t)cred_id_len;
}
memcpy(pa, cbor_buf, rs); pa += (uint16_t)rs;
@@ -601,11 +606,11 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
self_attestation = false;
}
if (md != NULL) {
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_fill_iterator, NULL);
}
#ifdef MBEDTLS_EDDSA_C
else {
ret = mbedtls_eddsa_write_signature(&ekey, aut_data, aut_data_len + clientDataHash.len, sig, sizeof(sig), &olen, MBEDTLS_EDDSA_PURE, NULL, 0, random_gen, NULL);
ret = mbedtls_eddsa_write_signature(&ekey, aut_data, aut_data_len + clientDataHash.len, sig, sizeof(sig), &olen, MBEDTLS_EDDSA_PURE, NULL, 0, random_fill_iterator, NULL);
}
#endif
mbedtls_ecp_keypair_free(&ekey);
@@ -613,24 +618,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);
@@ -664,7 +651,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CborEncoder arrEncoder;
file_t *ef_cert = NULL;
if (enterpriseAttestation == 2) {
ef_cert = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
ef_cert = file_search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
}
if (!file_has_data(ef_cert)) {
ef_cert = ef_certdev;
@@ -696,7 +683,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
ctr++;
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
low_flash_available();
flash_commit();
err:
CBOR_FREE_BYTE_STRING(clientDataHash);
CBOR_FREE_BYTE_STRING(pinUvAuthParam);

View File

@@ -15,9 +15,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "file.h"
#include "fido.h"
#include "ctap2_cbor.h"
#include "ctap.h"
#if defined(PICO_PLATFORM)
#include "bsp/board.h"
@@ -27,20 +28,18 @@
#endif
#include "fs/phy.h"
extern void scan_all();
int cbor_reset() {
int cbor_reset(void) {
#ifndef ENABLE_EMULATION
#if defined(ENABLE_POWER_ON_RESET) && ENABLE_POWER_ON_RESET == 1
if (!(phy_data.opts & PHY_OPT_DISABLE_POWER_RESET) && board_millis() > 10000) {
return CTAP2_ERR_NOT_ALLOWED;
}
#endif
if (wait_button_pressed() == true) {
if (wait_button_pressed() > 0) {
return CTAP2_ERR_USER_ACTION_TIMEOUT;
}
#endif
initialize_flash(true);
file_initialize_flash(true);
init_fido();
return 0;
}

View File

@@ -15,13 +15,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "fido.h"
#include "ctap2_cbor.h"
#include "ctap.h"
int cbor_selection() {
if (wait_button_pressed() == true) {
extern char *rp_id, *user_name, *display_name;
int cbor_selection(void) {
rp_id = user_name = display_name = NULL;
int ret = wait_button_pressed() ;
if (ret == 1) {
return CTAP2_ERR_USER_ACTION_TIMEOUT;
}
else if (ret == 2) {
return CTAP2_ERR_OPERATION_DENIED;
}
return CTAP2_OK;
}

View File

@@ -15,7 +15,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "serial.h"
#include "ctap2_cbor.h"
#include "fido.h"
#include "ctap.h"
@@ -42,7 +43,7 @@ int mse_decrypt_ct(uint8_t *data, size_t len) {
return ret;
}
int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
static int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
CborParser parser;
CborValue map;
CborError error = CborNoError;
@@ -116,7 +117,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
file_put_data(ef_keydev_enc, vendorParam.data, (uint16_t)vendorParam.len);
file_put_data(ef_keydev, zeros, file_get_size(ef_keydev)); // Overwrite ef with 0
file_put_data(ef_keydev, NULL, 0); // Set ef to 0 bytes
low_flash_available();
flash_commit();
goto err;
}
else {
@@ -132,7 +133,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
mbedtls_ecdh_context hkey;
mbedtls_ecdh_init(&hkey);
mbedtls_ecdh_setup(&hkey, MBEDTLS_ECP_DP_SECP256R1);
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_gen, NULL);
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_fill_iterator, NULL);
mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1);
if (ret != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
@@ -154,7 +155,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
ret = mbedtls_ecdh_calc_secret(&hkey, &olen, buf, MBEDTLS_ECP_MAX_BYTES, random_gen, NULL);
ret = mbedtls_ecdh_calc_secret(&hkey, &olen, buf, MBEDTLS_ECP_MAX_BYTES, random_fill_iterator, NULL);
if (ret != 0) {
mbedtls_ecdh_free(&hkey);
mbedtls_platform_zeroize(buf, sizeof(buf));
@@ -206,12 +207,17 @@ 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);
}
ret = mbedtls_ecp_mul(&ekey.grp, &ekey.Q, &ekey.d, &ekey.grp.G, random_gen, NULL);
ret = mbedtls_ecp_keypair_calc_public(&ekey, random_fill_iterator, NULL);
if (ret != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
@@ -227,7 +233,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
mbedtls_x509write_csr_set_key(&ctx, &key);
mbedtls_x509write_csr_set_md_alg(&ctx, MBEDTLS_MD_SHA256);
mbedtls_x509write_csr_set_extension(&ctx, "\x2B\x06\x01\x04\x01\x82\xE5\x1C\x01\x01\x04", 0xB, 0, aaguid, sizeof(aaguid));
ret = mbedtls_x509write_csr_der(&ctx, buffer, sizeof(buffer), random_gen, NULL);
ret = mbedtls_x509write_csr_der(&ctx, buffer, sizeof(buffer), random_fill_iterator, NULL);
mbedtls_ecdsa_free(&ekey);
if (ret <= 0) {
mbedtls_x509write_csr_free(&ctx);
@@ -238,41 +244,6 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, buffer + sizeof(buffer) - ret, ret));
}
}
#ifndef ENABLE_EMULATION
else if (cmd == CTAP_VENDOR_PHY_OPTS) {
if (vendorCmd == 0x01) {
uint16_t opts = 0;
if (file_has_data(ef_phy)) {
uint8_t *pdata = file_get_data(ef_phy);
opts = get_uint16_t_be(pdata + PHY_OPTS);
}
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, opts));
}
else {
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
}
}
#endif
else if (cmd == CTAP_VENDOR_MEMORY) {
if (vendorCmd == 0x01) {
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 5));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_free_space()));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_used_space()));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_total_space()));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_num_files()));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, flash_size()));
}
else {
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
}
}
else {
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
}

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "fido.h"
#include "apdu.h"
#include "ctap.h"
@@ -23,10 +23,10 @@
#include "files.h"
#include "credential.h"
int cmd_authenticate() {
int cmd_authenticate(void) {
CTAP_AUTHENTICATE_REQ *req = (CTAP_AUTHENTICATE_REQ *) apdu.data;
CTAP_AUTHENTICATE_RESP *resp = (CTAP_AUTHENTICATE_RESP *) res_APDU;
//if (scan_files_fido(true) != PICOKEY_OK)
//if (scan_files_fido(true) != PICOKEYS_OK)
// return SW_EXEC_ERROR();
if (apdu.nc < CTAP_CHAL_SIZE + CTAP_APPID_SIZE + 1 + 1) {
return SW_WRONG_DATA();
@@ -34,7 +34,7 @@ int cmd_authenticate() {
if (req->keyHandleLen < KEY_HANDLE_LEN) {
return SW_INCORRECT_PARAMS();
}
if (P1(apdu) == CTAP_AUTH_ENFORCE && wait_button_pressed() == true) {
if (P1(apdu) == CTAP_AUTH_ENFORCE && wait_button_pressed() > 0) {
return SW_CONDITIONS_NOT_SATISFIED();
}
@@ -55,7 +55,7 @@ int cmd_authenticate() {
}
}
free(tmp_kh);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
mbedtls_ecp_keypair_free(&key);
return SW_EXEC_ERROR();
}
@@ -66,7 +66,7 @@ int cmd_authenticate() {
resp->flags = 0;
resp->flags |= P1(apdu) == CTAP_AUTH_ENFORCE ? CTAP_AUTH_FLAG_TUP : 0x0;
uint32_t ctr = get_sign_counter();
put_uint32_t_be(ctr, resp->ctr);
put_uint32_be(ctr, resp->ctr);
uint8_t hash[32], sig_base[CTAP_APPID_SIZE + 1 + 4 + CTAP_CHAL_SIZE];
memcpy(sig_base, req->appId, CTAP_APPID_SIZE);
memcpy(sig_base + CTAP_APPID_SIZE, &resp->flags, sizeof(uint8_t));
@@ -78,7 +78,7 @@ int cmd_authenticate() {
return SW_EXEC_ERROR();
}
size_t olen = 0;
ret = mbedtls_ecdsa_write_signature(&key, MBEDTLS_MD_SHA256, hash, 32, (uint8_t *) resp->sig, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
ret = mbedtls_ecdsa_write_signature(&key, MBEDTLS_MD_SHA256, hash, 32, (uint8_t *) resp->sig, CTAP_MAX_EC_SIG_SIZE, &olen, random_fill_iterator, NULL);
mbedtls_ecp_keypair_free(&key);
if (ret != 0) {
return SW_EXEC_ERROR();
@@ -87,6 +87,6 @@ int cmd_authenticate() {
ctr++;
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
low_flash_available();
flash_commit();
return SW_OK();
}

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "fido.h"
#include "apdu.h"
#include "ctap.h"
@@ -29,42 +29,41 @@ const uint8_t u2f_aid[] = {
0xA0, 0x00, 0x00, 0x05, 0x27, 0x10, 0x02
};
int u2f_unload();
int u2f_process_apdu();
static int u2f_unload(void);
static int u2f_process_apdu(void);
int u2f_select(app_t *a, uint8_t force) {
static int u2f_select(app_t *a, uint8_t force) {
(void) force;
if (cap_supported(CAP_U2F)) {
a->process_apdu = u2f_process_apdu;
a->unload = u2f_unload;
return PICOKEY_OK;
return PICOKEYS_OK;
}
return PICOKEY_ERR_FILE_NOT_FOUND;
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
INITIALIZER ( u2f_ctor ) {
register_app(u2f_select, u2f_aid);
}
int u2f_unload() {
return PICOKEY_OK;
int u2f_unload(void) {
return PICOKEYS_OK;
}
const uint8_t *bogus_firefox = (const uint8_t *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
const uint8_t *bogus_chrome = (const uint8_t *) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
extern int ctap_error(uint8_t error);
int cmd_register() {
int cmd_register(void) {
CTAP_REGISTER_REQ *req = (CTAP_REGISTER_REQ *) apdu.data;
CTAP_REGISTER_RESP *resp = (CTAP_REGISTER_RESP *) res_APDU;
resp->registerId = CTAP_REGISTER_ID;
resp->keyHandleLen = KEY_HANDLE_LEN;
//if (scan_files_fido(true) != PICOKEY_OK)
//if (scan_files_fido(true) != PICOKEYS_OK)
// return SW_EXEC_ERROR();
if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE) {
return SW_WRONG_LENGTH();
}
if (wait_button_pressed() == true) {
if (wait_button_pressed() > 0) {
return SW_CONDITIONS_NOT_SATISFIED();
}
if (memcmp(req->appId, bogus_firefox,
@@ -73,7 +72,7 @@ int cmd_register() {
mbedtls_ecdsa_context key;
mbedtls_ecdsa_init(&key);
int ret = derive_key(req->appId, true, resp->keyHandleCertSig, MBEDTLS_ECP_DP_SECP256R1, &key);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
mbedtls_ecdsa_free(&key);
return SW_EXEC_ERROR();
}
@@ -98,16 +97,16 @@ int cmd_register() {
mbedtls_ecdsa_init(&key);
uint8_t key_dev[32] = {0};
ret = load_keydev(key_dev);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
return SW_EXEC_ERROR();
}
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, key_dev, 32);
mbedtls_platform_zeroize(key_dev, sizeof(key_dev));
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
mbedtls_ecdsa_free(&key);
return SW_EXEC_ERROR();
}
ret = mbedtls_ecdsa_write_signature(&key,MBEDTLS_MD_SHA256, hash, 32, (uint8_t *) resp->keyHandleCertSig + KEY_HANDLE_LEN + ef_certdev_size, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
ret = mbedtls_ecdsa_write_signature(&key,MBEDTLS_MD_SHA256, hash, 32, (uint8_t *) resp->keyHandleCertSig + KEY_HANDLE_LEN + ef_certdev_size, CTAP_MAX_EC_SIG_SIZE, &olen, random_fill_iterator, NULL);
mbedtls_ecdsa_free(&key);
if (ret != 0) {
return SW_EXEC_ERROR();
@@ -116,10 +115,6 @@ int cmd_register() {
return SW_OK();
}
extern int cmd_register();
extern int cmd_authenticate();
extern int cmd_version();
static const cmd_t cmds[] = {
{ CTAP_REGISTER, cmd_register },
{ CTAP_AUTHENTICATE, cmd_authenticate },
@@ -127,7 +122,7 @@ static const cmd_t cmds[] = {
{ 0x00, 0x0 }
};
int u2f_process_apdu() {
int u2f_process_apdu(void) {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}

View File

@@ -16,9 +16,10 @@
*/
#include "apdu.h"
#include "pico_keys.h"
#include "picokeys.h"
#include "fido.h"
int cmd_version() {
int cmd_version(void) {
memcpy(res_APDU, "U2F_V2", strlen("U2F_V2"));
res_APDU_size = (uint16_t)strlen("U2F_V2");
return SW_OK();

View File

@@ -15,7 +15,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "serial.h"
#include "pico_time.h"
#include "mbedtls/chachapoly.h"
#include "mbedtls/sha256.h"
#include "credential.h"
@@ -148,13 +150,17 @@ 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;
uint8_t key[32] = {0};
credential_derive_chacha_key(key, (const uint8_t *)CRED_PROTO);
uint8_t iv[CRED_IV_LEN] = {0};
random_gen(NULL, iv, sizeof(iv));
random_fill_buffer(iv, sizeof(iv));
mbedtls_chachapoly_context chatx;
mbedtls_chachapoly_init(&chatx);
mbedtls_chachapoly_setkey(&chatx, key);
@@ -220,7 +226,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 +261,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);
}
@@ -303,7 +312,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
return ret;
}
for (uint16_t i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef = search_dynamic_file(EF_CRED + i);
file_t *ef = file_search(EF_CRED + i);
Credential rcred = { 0 };
if (!file_has_data(ef)) {
if (sloti == -1) {
@@ -319,7 +328,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;
@@ -343,7 +352,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
if (new_record == true) { //increase rps
sloti = -1;
for (uint16_t i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
ef = search_dynamic_file(EF_RP + i);
ef = file_search(EF_RP + i);
if (!file_has_data(ef)) {
if (sloti == -1) {
sloti = i;
@@ -358,7 +367,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
if (sloti == -1) {
return -1;
}
ef = search_dynamic_file((uint16_t)(EF_RP + sloti));
ef = file_search((uint16_t)(EF_RP + sloti));
if (file_has_data(ef)) {
data = (uint8_t *) calloc(1, file_get_size(ef));
memcpy(data, file_get_data(ef), file_get_size(ef));
@@ -377,7 +386,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
}
}
credential_free(&cred);
low_flash_available();
flash_commit();
return 0;
}

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

@@ -129,8 +129,7 @@ typedef struct {
#define CTAP_VENDOR_MSE 0x02
#define CTAP_VENDOR_UNLOCK 0x03
#define CTAP_VENDOR_EA 0x04
#define CTAP_VENDOR_PHY_OPTS 0x05
#define CTAP_VENDOR_MEMORY 0x06
#define CTAP_VENDOR_ADMIN_PIN 0x08
#define CTAP_PERMISSION_MC 0x01 // MakeCredential
#define CTAP_PERMISSION_GA 0x02 // GetAssertion

View File

@@ -19,16 +19,23 @@
#define _CTAP2_CBOR_H_
#include "cbor.h"
#ifndef ESP_PLATFORM
#include "common.h"
#else
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#endif
#include "mbedtls/ecp.h"
#include "mbedtls/ecdh.h"
extern uint8_t *driver_prepare_response();
extern uint8_t *driver_prepare_response(void);
extern void driver_exec_finished(size_t size_next);
extern int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len);
extern int cbor_get_info(void);
extern int cbor_reset(void);
extern int cbor_make_credential(const uint8_t *data, size_t len);
extern int cbor_client_pin(const uint8_t *data, size_t len);
extern int cbor_selection(void);
extern int cbor_get_next_assertion(const uint8_t *data, size_t len);
extern int cbor_cred_mgmt(const uint8_t *data, size_t len);
extern int cbor_config(const uint8_t *data, size_t len);
extern int cbor_large_blobs(const uint8_t *data, size_t len);
extern int cbor_vendor(const uint8_t *data, size_t len);
extern void reset_gna_state(void);
extern int cbor_process(uint8_t, const uint8_t *data, size_t len);
extern const uint8_t aaguid[16];

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "fido.h"
#include "version.h"

View File

@@ -15,9 +15,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "fido.h"
#include "kek.h"
#include "serial.h"
#include "apdu.h"
#include "ctap.h"
#include "files.h"
@@ -34,12 +34,12 @@
#include <math.h>
#include "management.h"
#include "hid/ctap_hid.h"
#include "ctap2_cbor.h"
#include "version.h"
#include "crypto_utils.h"
#include "otp.h"
int fido_process_apdu();
int fido_unload();
static int fido_unload(void);
pinUvAuthToken_t paut = { 0 };
persistentPinUvAuthToken_t ppaut = { 0 };
@@ -53,31 +53,36 @@ 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,
0x75, 0x62, 0x69, 0x4b, 0x65, 0x79, 0x40
};
uint8_t fido_get_version_major() {
static uint8_t fido_get_version_major(void) {
return PICO_FIDO_VERSION_MAJOR;
}
uint8_t fido_get_version_minor() {
static uint8_t fido_get_version_minor(void) {
return PICO_FIDO_VERSION_MINOR;
}
int fido_select(app_t *a, uint8_t force) {
static int fido_select(app_t *a, uint8_t force) {
(void) force;
if (cap_supported(CAP_FIDO2)) {
a->process_apdu = fido_process_apdu;
a->unload = fido_unload;
return PICOKEY_OK;
return PICOKEYS_OK;
}
return PICOKEY_ERR_FILE_NOT_FOUND;
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
extern uint8_t (*get_version_major)();
extern uint8_t (*get_version_minor)();
extern uint8_t (*get_version_major)(void);
extern uint8_t (*get_version_minor)(void);
INITIALIZER ( fido_ctor ) {
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
@@ -86,10 +91,11 @@ 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() {
return PICOKEY_OK;
static int fido_unload(void) {
return PICOKEYS_OK;
}
mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) {
@@ -144,7 +150,7 @@ int mbedtls_curve_to_fido(mbedtls_ecp_group_id id) {
return FIDO2_CURVE_P256K1;
}
else if (id == MBEDTLS_ECP_DP_CURVE25519) {
return MBEDTLS_ECP_DP_CURVE25519;
return FIDO2_CURVE_X25519;
}
else if (id == MBEDTLS_ECP_DP_CURVE448) {
return FIDO2_CURVE_X448;
@@ -167,14 +173,18 @@ int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecp_keypair *key) {
}
uint8_t key_path[KEY_PATH_LEN];
memcpy(key_path, cred_id, KEY_PATH_LEN);
*(uint32_t *) key_path = 0x80000000 | 10022;
uint32_t key_path_first = 0x80000000u | 10022u;
memcpy(key_path, &key_path_first, sizeof(key_path_first));
for (size_t i = 1; i < KEY_PATH_ENTRIES; i++) {
*(uint32_t *) (key_path + i * sizeof(uint32_t)) |= 0x80000000;
uint32_t part = 0;
memcpy(&part, key_path + i * sizeof(uint32_t), sizeof(part));
part |= 0x80000000u;
memcpy(key_path + i * sizeof(uint32_t), &part, sizeof(part));
}
return derive_key(NULL, false, key_path, mbedtls_curve, key);
}
int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffer_size) {
static int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffer_size) {
mbedtls_x509write_cert ctx;
mbedtls_x509write_crt_init(&ctx);
mbedtls_x509write_crt_set_version(&ctx, MBEDTLS_X509_CRT_VERSION_3);
@@ -182,7 +192,7 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
mbedtls_x509write_crt_set_issuer_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
mbedtls_x509write_crt_set_subject_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
uint8_t serial[16];
random_gen(NULL, serial, sizeof(serial));
random_fill_buffer(serial, sizeof(serial));
mbedtls_x509write_crt_set_serial_raw(&ctx, serial, sizeof(serial));
mbedtls_pk_context key;
mbedtls_pk_init(&key);
@@ -197,7 +207,7 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
mbedtls_x509write_crt_set_key_usage(&ctx,
MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
MBEDTLS_X509_KU_KEY_CERT_SIGN);
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL);
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_fill_iterator, NULL);
mbedtls_x509write_crt_free(&ctx);
/* pk cannot be freed, as it is freed later */
//mbedtls_pk_free(&key);
@@ -206,7 +216,7 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
int load_keydev(uint8_t key[32]) {
if (has_keydev_dec == false && !file_has_data(ef_keydev)) {
return PICOKEY_ERR_MEMORY_FATAL;
return PICOKEYS_ERR_MEMORY_FATAL;
}
if (has_keydev_dec == true) {
@@ -216,45 +226,54 @@ int load_keydev(uint8_t key[32]) {
uint16_t fid_size = file_get_size(ef_keydev);
if (fid_size == 32) {
memcpy(key, file_get_data(ef_keydev), 32);
if (mkek_decrypt(key, 32) != PICOKEY_OK) {
return PICOKEY_EXEC_ERROR;
}
if (otp_key_1 && aes_decrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, key, 32) != PICOKEY_OK) {
return PICOKEY_EXEC_ERROR;
if (otp_key_1 && aes_decrypt(otp_key_1, NULL, 32 * 8, PICOKEYS_AES_MODE_CBC, key, 32) != PICOKEYS_OK) {
return PICOKEYS_EXEC_ERROR;
}
}
else if (fid_size == 33 || fid_size == 61) {
uint8_t format = *file_get_data(ef_keydev);
if (format == 0x01 || format == 0x02) { // Format indicator
if (format == 0x02) {
uint8_t tmp_key[61];
if (format == 0x01 || format == 0x02 || format == 0x03) { // Format indicator
if (format == 0x02 || format == 0x03) {
uint8_t tmp_key[61], version = format == 0x03 ? 2 : 1;
memcpy(tmp_key, file_get_data(ef_keydev), sizeof(tmp_key));
int ret = decrypt_with_aad(session_pin, tmp_key + 1, 60, key);
if (ret != PICOKEY_OK) {
return PICOKEY_EXEC_ERROR;
int ret = decrypt_with_aad(session_pin, tmp_key + 1, 60, version, key);
if (ret != PICOKEYS_OK) {
return PICOKEYS_EXEC_ERROR;
}
if (format == 0x02) {
tmp_key[0] = 0x03;
ret = encrypt_with_aad(session_pin, key, 32, 2, tmp_key + 1);
if (ret != PICOKEYS_OK) {
mbedtls_platform_zeroize(tmp_key, sizeof(tmp_key));
return PICOKEYS_EXEC_ERROR;
}
file_put_data(ef_keydev, tmp_key, sizeof(tmp_key));
flash_commit();
}
mbedtls_platform_zeroize(tmp_key, sizeof(tmp_key));
}
else {
memcpy(key, file_get_data(ef_keydev) + 1, 32);
}
uint8_t kbase[32];
derive_kbase(kbase);
int ret = aes_decrypt(kbase, pico_serial_hash, 32 * 8, PICO_KEYS_AES_MODE_CBC, key, 32);
if (ret != PICOKEY_OK) {
int ret = aes_decrypt(kbase, pico_serial_hash, 32 * 8, PICOKEYS_AES_MODE_CBC, key, 32);
if (ret != PICOKEYS_OK) {
mbedtls_platform_zeroize(kbase, sizeof(kbase));
return PICOKEY_EXEC_ERROR;
return PICOKEYS_EXEC_ERROR;
}
mbedtls_platform_zeroize(kbase, sizeof(kbase));
}
}
}
return PICOKEY_OK;
return PICOKEYS_OK;
}
int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecp_keypair *key) {
for (size_t i = 0; i < KEY_PATH_ENTRIES; i++) {
uint32_t k = *(uint32_t *) &keyHandle[i * sizeof(uint32_t)];
uint32_t k = 0;
memcpy(&k, &keyHandle[i * sizeof(uint32_t)], sizeof(k));
if (!(k & 0x80000000)) {
return -1;
}
@@ -289,7 +308,7 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
uint8_t outk[67] = { 0 }; //SECP521R1 key is 66 bytes length
int r = 0;
memset(outk, 0, sizeof(outk));
if ((r = load_keydev(outk)) != PICOKEY_OK) {
if ((r = load_keydev(outk)) != PICOKEYS_OK) {
printf("Error loading keydev: %d\n", r);
return r;
}
@@ -297,7 +316,7 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
for (size_t i = 0; i < KEY_PATH_ENTRIES; i++) {
if (new_key == true) {
uint32_t val = 0;
random_gen(NULL, (uint8_t *) &val, sizeof(val));
random_fill_buffer((uint8_t *) &val, sizeof(val));
val |= 0x80000000;
memcpy(&key_handle[i * sizeof(uint32_t)], &val, sizeof(uint32_t));
}
@@ -325,17 +344,12 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
if (cinfo->bit_size % 8 != 0) {
outk[0] >>= 8 - (cinfo->bit_size % 8);
}
r = mbedtls_ecp_read_key(curve, key, outk, (size_t)ceil((float) cinfo->bit_size / 8));
r = mbedtls_ecp_read_key(curve, key, outk, (size_t)((cinfo->bit_size + 7) / 8));
mbedtls_platform_zeroize(outk, sizeof(outk));
if (r != 0) {
return r;
}
#ifdef MBEDTLS_EDDSA_C
if (curve == MBEDTLS_ECP_DP_ED25519) {
return mbedtls_ecp_point_edwards(&key->grp, &key->Q, &key->d, random_gen, NULL);
}
#endif
return mbedtls_ecp_mul(&key->grp, &key->Q, &key->d, &key->grp.G, random_gen, NULL);
return mbedtls_ecp_keypair_calc_public(key, random_fill_iterator, NULL);
}
mbedtls_platform_zeroize(outk, sizeof(outk));
return r;
@@ -347,28 +361,26 @@ int encrypt_keydev_f1(const uint8_t keydev[32]) {
memcpy(kdata + 1, keydev, 32);
uint8_t kbase[32];
derive_kbase(kbase);
int ret = aes_encrypt(kbase, pico_serial_hash, 32 * 8, PICO_KEYS_AES_MODE_CBC, kdata + 1, 32);
int ret = aes_encrypt(kbase, pico_serial_hash, 32 * 8, PICOKEYS_AES_MODE_CBC, kdata + 1, 32);
mbedtls_platform_zeroize(kbase, sizeof(kbase));
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
return ret;
}
ret = file_put_data(ef_keydev, kdata, 33);
mbedtls_platform_zeroize(kdata, sizeof(kdata));
low_flash_available();
flash_commit();
return ret;
}
int scan_files_fido() {
ef_keydev = search_by_fid(EF_KEY_DEV, NULL, SPECIFY_EF);
ef_keydev_enc = search_by_fid(EF_KEY_DEV_ENC, NULL, SPECIFY_EF);
ef_mkek = search_by_fid(EF_MKEK, NULL, SPECIFY_EF);
int scan_files_fido(void) {
ef_keydev = file_search_by_fid(EF_KEY_DEV, NULL, SPECIFY_EF);
ef_keydev_enc = file_search_by_fid(EF_KEY_DEV_ENC, NULL, SPECIFY_EF);
if (ef_keydev) {
if (!file_has_data(ef_keydev) && !file_has_data(ef_keydev_enc)) {
printf("KEY DEVICE is empty. Generating SECP256R1 curve...");
mbedtls_ecdsa_context ecdsa;
mbedtls_ecdsa_init(&ecdsa);
uint8_t index = 0;
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, random_gen, &index);
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, random_fill_iterator, NULL);
if (ret != 0) {
mbedtls_ecdsa_free(&ecdsa);
return ret;
@@ -379,12 +391,12 @@ int scan_files_fido() {
if (ret != 0 || key_size != 32) {
mbedtls_platform_zeroize(keydev, sizeof(keydev));
mbedtls_ecdsa_free(&ecdsa);
return ret != 0 ? ret : PICOKEY_EXEC_ERROR;
return ret != 0 ? ret : PICOKEYS_EXEC_ERROR;
}
encrypt_keydev_f1(keydev);
mbedtls_platform_zeroize(keydev, sizeof(keydev));
mbedtls_ecdsa_free(&ecdsa);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
return ret;
}
printf(" done!\n");
@@ -393,7 +405,7 @@ int scan_files_fido() {
else {
printf("FATAL ERROR: KEY DEV not found in memory!\r\n");
}
ef_certdev = search_by_fid(EF_EE_DEV, NULL, SPECIFY_EF);
ef_certdev = file_search_by_fid(EF_EE_DEV, NULL, SPECIFY_EF);
if (ef_certdev) {
if (!file_has_data(ef_certdev)) {
uint8_t cert[2048], outk[32];
@@ -409,7 +421,7 @@ int scan_files_fido() {
mbedtls_ecdsa_free(&key);
return ret;
}
ret = mbedtls_ecp_mul(&key.grp, &key.Q, &key.d, &key.grp.G, random_gen, NULL);
ret = mbedtls_ecp_keypair_calc_public(&key, random_fill_iterator, NULL);
if (ret != 0) {
mbedtls_ecdsa_free(&key);
return ret;
@@ -425,7 +437,7 @@ int scan_files_fido() {
else {
printf("FATAL ERROR: CERT DEV not found in memory!\r\n");
}
ef_counter = search_by_fid(EF_COUNTER, NULL, SPECIFY_EF);
ef_counter = file_search_by_fid(EF_COUNTER, NULL, SPECIFY_EF);
if (ef_counter) {
if (!file_has_data(ef_counter)) {
uint32_t v = 0;
@@ -435,12 +447,13 @@ int scan_files_fido() {
else {
printf("FATAL ERROR: Global counter not found in memory!\r\n");
}
ef_pin = search_by_fid(EF_PIN, NULL, SPECIFY_EF);
ef_authtoken = search_by_fid(EF_AUTHTOKEN, NULL, SPECIFY_EF);
ef_pin = file_search_by_fid(EF_PIN, NULL, SPECIFY_EF);
ef_pin_admin = file_search_by_fid(EF_PIN_ADMIN, NULL, SPECIFY_EF);
ef_authtoken = file_search_by_fid(EF_AUTHTOKEN, NULL, SPECIFY_EF);
if (ef_authtoken) {
if (!file_has_data(ef_authtoken)) {
uint8_t t[32];
random_gen(NULL, t, sizeof(t));
random_fill_buffer(t, sizeof(t));
file_put_data(ef_authtoken, t, sizeof(t));
}
paut.data = file_get_data(ef_authtoken);
@@ -449,11 +462,11 @@ int scan_files_fido() {
else {
printf("FATAL ERROR: Auth Token not found in memory!\r\n");
}
file_t *ef_pauthtoken = search_by_fid(EF_PAUTHTOKEN, NULL, SPECIFY_EF);
file_t *ef_pauthtoken = file_search_by_fid(EF_PAUTHTOKEN, NULL, SPECIFY_EF);
if (ef_pauthtoken) {
if (!file_has_data(ef_pauthtoken)) {
uint8_t t[32];
random_gen(NULL, t, sizeof(t));
random_fill_buffer(t, sizeof(t));
file_put_data(ef_pauthtoken, t, sizeof(t));
}
ppaut.data = file_get_data(ef_pauthtoken);
@@ -462,44 +475,51 @@ int scan_files_fido() {
else {
printf("FATAL ERROR: Persistent Auth Token not found in memory!\r\n");
}
ef_largeblob = search_by_fid(EF_LARGEBLOB, NULL, SPECIFY_EF);
ef_largeblob = file_search_by_fid(EF_LARGEBLOB, NULL, SPECIFY_EF);
if (!file_has_data(ef_largeblob)) {
file_put_data(ef_largeblob, (const uint8_t *) "\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c", 17);
}
low_flash_available();
return PICOKEY_OK;
flash_commit();
return PICOKEYS_OK;
}
void scan_all() {
scan_flash();
void scan_all(void) {
file_scan_flash();
scan_files_fido();
}
extern void init_otp();
void init_fido() {
extern bool needs_power_cycle;
void init_fido(void) {
scan_all();
#ifdef ENABLE_OTP_APP
init_otp();
#endif
needs_power_cycle = false;
}
bool wait_button_pressed() {
int wait_button_pressed(void) {
uint32_t val = EV_PRESS_BUTTON;
#if defined(PICO_PLATFORM) || defined(ESP_PLATFORM)
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);
} while (val != EV_BUTTON_PRESSED && val != EV_BUTTON_TIMEOUT && val != EV_BUTTON_CANCELLED);
#endif
return val == EV_BUTTON_TIMEOUT;
if (val == EV_BUTTON_TIMEOUT) {
return 1;
}
else if (val == EV_BUTTON_CANCELLED) {
return 2;
}
return 0;
}
uint32_t user_present_time_limit = 0;
bool check_user_presence() {
bool check_user_presence(void) {
if (user_present_time_limit == 0 || user_present_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
if (wait_button_pressed() == true) { //timeout
if (wait_button_pressed() > 0) { //timeout
return false;
}
//user_present_time_limit = board_millis();
@@ -507,13 +527,13 @@ bool check_user_presence() {
return true;
}
uint32_t get_sign_counter() {
uint32_t get_sign_counter(void) {
uint8_t *caddr = file_get_data(ef_counter);
return get_uint32_t_le(caddr);
return get_uint32_le(caddr);
}
uint8_t get_opts() {
file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
uint8_t get_opts(void) {
file_t *ef = file_search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
if (file_has_data(ef)) {
return *file_get_data(ef);
}
@@ -521,27 +541,34 @@ uint8_t get_opts() {
}
void set_opts(uint8_t opts) {
file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
file_t *ef = file_search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
file_put_data(ef, &opts, sizeof(uint8_t));
low_flash_available();
flash_commit();
}
extern int cmd_register();
extern int cmd_authenticate();
extern int cmd_version();
extern int cbor_parse(int, uint8_t *, size_t);
extern void driver_init_hid();
#define CTAP_CBOR 0x10
int cmd_cbor() {
static int cmd_vendor(void) {
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 set_res_sw(0x64, ret);
}
res_APDU_size += 1;
memcpy(res_APDU, ctap_resp->init.data, res_APDU_size);
return SW_OK();
}
static int cmd_cbor(void) {
uint8_t *old_buf = res_APDU;
driver_init_hid();
int ret = cbor_parse(0x90, apdu.data, apdu.nc);
if (ret != 0) {
return SW_EXEC_ERROR();
}
res_APDU = old_buf;
if (ret != 0) {
return set_res_sw(0x64, ret);
}
res_APDU_size += 1;
memcpy(res_APDU, ctap_resp->init.data, res_APDU_size);
return SW_OK();
@@ -552,10 +579,11 @@ static const cmd_t cmds[] = {
{ CTAP_AUTHENTICATE, cmd_authenticate },
{ CTAP_VERSION, cmd_version },
{ CTAP_CBOR, cmd_cbor },
{ 0x41, cmd_vendor },
{ 0x00, 0x0 }
};
int fido_process_apdu() {
int fido_process_apdu(void) {
if (CLA(apdu) != 0x00 && CLA(apdu) != 0x80) {
return SW_CLA_NOT_SUPPORTED();
}

View File

@@ -21,11 +21,6 @@
#if defined(PICO_PLATFORM)
#include "pico/stdlib.h"
#endif
#ifndef ESP_PLATFORM
#include "common.h"
#else
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#endif
#include "mbedtls/ecdsa.h"
#ifdef MBEDTLS_EDDSA_C
@@ -39,15 +34,17 @@
#define SHA256_DIGEST_LENGTH (32)
#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH)
extern int scan_files_fido();
extern int scan_files_fido(void);
extern int derive_key(const uint8_t *app_id,
bool new_key,
uint8_t *key_handle,
int,
mbedtls_ecp_keypair *key);
extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecp_keypair *);
extern bool wait_button_pressed();
extern void init_fido();
extern int wait_button_pressed(void);
extern void init_fido(void);
extern void init_otp(void);
extern void scan_all(void);
extern mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve);
extern int mbedtls_curve_to_fido(mbedtls_ecp_group_id id);
extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecp_keypair *key);
@@ -95,14 +92,14 @@ extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSec
#define FIDO2_OPT_AUV 0x02 // User Verification
#define MAX_PIN_RETRIES 8
extern bool getUserPresentFlagValue();
extern bool getUserVerifiedFlagValue();
extern void clearUserPresentFlag();
extern void clearUserVerifiedFlag();
extern void clearPinUvAuthTokenPermissionsExceptLbw();
extern void send_keepalive();
extern uint32_t get_sign_counter();
extern uint8_t get_opts();
extern bool getUserPresentFlagValue(void);
extern bool getUserVerifiedFlagValue(void);
extern void clearUserPresentFlag(void);
extern void clearUserVerifiedFlag(void);
extern void clearPinUvAuthTokenPermissionsExceptLbw(void);
extern void send_keepalive(void);
extern uint32_t get_sign_counter(void);
extern uint8_t get_opts(void);
extern void set_opts(uint8_t);
#define MAX_CREDENTIAL_COUNT_IN_LIST 16
#define MAX_CRED_ID_LENGTH 1024
@@ -123,7 +120,19 @@ extern const known_app_t *find_app_by_rp_id_hash(const uint8_t *rp_id_hash);
#define TRANSPORT_TIME_LIMIT (30 * 1000) //USB
bool check_user_presence();
bool check_user_presence(void);
int fido_process_apdu(void);
int cmd_register(void);
int cmd_authenticate(void);
int cmd_version(void);
int calculate_oath(uint8_t truncate,
const uint8_t *key,
size_t key_len,
const uint8_t *chal,
size_t chal_len);
int encrypt_keydev_f1(const uint8_t keydev[32]);
int resetPinUvAuthToken(void);
int resetPersistentPinUvAuthToken(void);
typedef struct pinUvAuthToken {
uint8_t *data;

View File

@@ -21,7 +21,6 @@ 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, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Device Key
{ .fid = EF_KEY_DEV_ENC, .parent = 0, .name = NULL,.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Device Key Enc
{ .fid = EF_MKEK, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // MKEK
{ .fid = EF_EE_DEV, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // End Entity Certificate Device
{ .fid = EF_EE_DEV_EA, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // End Entity Enterprise Attestation Certificate
{ .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
@@ -32,6 +31,7 @@ file_t file_entries[] = {
{ .fid = EF_OPTS, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Global options
{ .fid = EF_LARGEBLOB, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // Large Blob
{ .fid = EF_OTP_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } },
{ .fid = EF_PIN_ADMIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // ADMIN PIN
{ .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL, .ef_structure = 0, .acl = { 0 } } //end
};
@@ -41,7 +41,7 @@ file_t *ef_keydev = NULL;
file_t *ef_certdev = NULL;
file_t *ef_counter = NULL;
file_t *ef_pin = NULL;
file_t *ef_pin_admin = NULL;
file_t *ef_authtoken = NULL;
file_t *ef_keydev_enc = NULL;
file_t *ef_largeblob = NULL;
file_t *ef_mkek = NULL;

View File

@@ -22,12 +22,12 @@
#define EF_KEY_DEV 0xCC00
#define EF_KEY_DEV_ENC 0xCC01
#define EF_MKEK 0xCC0F
#define EF_EE_DEV 0xCE00
#define EF_EE_DEV_EA 0xCE01
#define EF_COUNTER 0xC000
#define EF_OPTS 0xC001
#define EF_PIN 0x1080
#define EF_PIN_ADMIN 0x1084
#define EF_AUTHTOKEN 0x1090
#define EF_PAUTHTOKEN 0x1091
#define EF_MINPINLEN 0x1100
@@ -40,15 +40,17 @@
#define EF_OATH_CODE 0xBAFF
#define EF_OTP_SLOT1 0xBB00
#define EF_OTP_SLOT2 0xBB01
#define EF_OTP_SLOT3 0xBB02
#define EF_OTP_SLOT4 0xBB03
#define EF_OTP_PIN 0x10A0 // Nitrokey OTP PIN
extern file_t *ef_keydev;
extern file_t *ef_certdev;
extern file_t *ef_counter;
extern file_t *ef_pin;
extern file_t *ef_pin_admin;
extern file_t *ef_authtoken;
extern file_t *ef_keydev_enc;
extern file_t *ef_largeblob;
extern file_t *ef_mkek;
#endif //_FILES_H_

View File

@@ -1,97 +0,0 @@
/*
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "fido.h"
#include "stdlib.h"
#if defined(PICO_PLATFORM)
#include "pico/stdlib.h"
#endif
#include "kek.h"
#include "crypto_utils.h"
#include "random.h"
#include "mbedtls/md.h"
#include "mbedtls/cmac.h"
#include "mbedtls/rsa.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/chachapoly.h"
#include "files.h"
#include "otp.h"
extern uint8_t session_pin[32];
uint8_t mkek_mask[MKEK_KEY_SIZE];
bool has_mkek_mask = false;
#define POLY 0xedb88320
uint32_t crc32c(const uint8_t *buf, size_t len) {
uint32_t crc = 0xffffffff;
while (len--) {
crc ^= *buf++;
for (int k = 0; k < 8; k++) {
crc = (crc >> 1) ^ (POLY & (0 - (crc & 1)));
}
}
return ~crc;
}
void mkek_masked(uint8_t *mkek, const uint8_t *mask) {
if (mask) {
for (int i = 0; i < MKEK_KEY_SIZE; i++) {
MKEK_KEY(mkek)[i] ^= mask[i];
}
}
}
int load_mkek(uint8_t *mkek) {
file_t *tf = search_file(EF_MKEK);
if (file_has_data(tf)) {
memcpy(mkek, file_get_data(tf), MKEK_SIZE);
}
if (has_mkek_mask) {
mkek_masked(mkek, mkek_mask);
}
if (file_get_size(tf) == MKEK_SIZE) {
int ret = aes_decrypt_cfb_256(session_pin, MKEK_IV(mkek), MKEK_KEY(mkek), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
if (ret != 0) {
return PICOKEY_EXEC_ERROR;
}
if (crc32c(MKEK_KEY(mkek), MKEK_KEY_SIZE) != *(uint32_t *) MKEK_CHECKSUM(mkek)) {
return PICOKEY_WRONG_DKEK;
}
if (otp_key_1) {
mkek_masked(mkek, otp_key_1);
}
}
return PICOKEY_OK;
}
void release_mkek(uint8_t *mkek) {
mbedtls_platform_zeroize(mkek, MKEK_SIZE);
}
int mkek_decrypt(uint8_t *data, uint16_t len) {
int r;
uint8_t mkek[MKEK_SIZE + 4];
if ((r = load_mkek(mkek)) != PICOKEY_OK) {
return r;
}
r = aes_decrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), data, len);
release_mkek(mkek);
return r;
}

View File

@@ -1,46 +0,0 @@
/*
* This file is part of the Pico Fido distribution (https://github.com/polhenarejos/pico-fido).
* Copyright (c) 2022 Pol Henarejos.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _KEK_H_
#define _KEK_H_
#include "crypto_utils.h"
#if defined(ENABLE_EMULATION) || defined(ESP_PLATFORM)
#include <stdbool.h>
#endif
extern int load_mkek(uint8_t *);
extern int store_mkek(const uint8_t *);
extern void init_mkek();
extern void release_mkek(uint8_t *);
extern int mkek_encrypt(uint8_t *data, uint16_t len);
extern int mkek_decrypt(uint8_t *data, uint16_t len);
#define MKEK_IV_SIZE (IV_SIZE)
#define MKEK_KEY_SIZE (32)
#define MKEK_KEY_CS_SIZE (4)
#define MKEK_SIZE (MKEK_IV_SIZE + MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE)
#define MKEK_IV(p) (p)
#define MKEK_KEY(p) (MKEK_IV(p) + MKEK_IV_SIZE)
#define MKEK_CHECKSUM(p) (MKEK_KEY(p) + MKEK_KEY_SIZE)
#define DKEK_KEY_SIZE (32)
extern uint8_t mkek_mask[MKEK_KEY_SIZE];
extern bool has_mkek_mask;
#endif

View File

@@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "fido.h"
#include "ctap2_cbor.h"

View File

@@ -15,24 +15,26 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include <stdio.h>
#include "picokeys.h"
#include "serial.h"
#include "fido.h"
#include "apdu.h"
#include "version.h"
#include "files.h"
#include "asn1.h"
#include "tlv.h"
#include "management.h"
int man_process_apdu();
int man_unload();
bool is_gpg = true;
static int man_process_apdu(void);
static int man_unload(void);
const uint8_t man_aid[] = {
8,
0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17
};
extern void scan_all();
extern void init_otp();
int man_select(app_t *a, uint8_t force) {
static int man_select(app_t *a, uint8_t force) {
a->process_apdu = man_process_apdu;
a->unload = man_unload;
sprintf((char *) res_APDU, "%d.%d.0", PICO_FIDO_VERSION_MAJOR, PICO_FIDO_VERSION_MINOR);
@@ -44,30 +46,31 @@ int man_select(app_t *a, uint8_t force) {
init_otp();
#endif
}
return PICOKEY_OK;
is_gpg = false;
return PICOKEYS_OK;
}
INITIALIZER ( man_ctor ) {
register_app(man_select, man_aid);
}
int man_unload() {
return PICOKEY_OK;
static int man_unload(void) {
return PICOKEYS_OK;
}
bool cap_supported(uint16_t cap) {
file_t *ef = search_dynamic_file(EF_DEV_CONF);
file_t *ef = file_search(EF_DEV_CONF);
if (file_has_data(ef)) {
uint16_t tag = 0x0;
uint8_t *tag_data = NULL, *p = NULL;
uint16_t tag_len = 0;
asn1_ctx_t ctxi;
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
while (walk_tlv(&ctxi, &p, &tag, &tag_len, &tag_data)) {
tlv_ctx_t ctxi;
tlv_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
while (tlv_walk(&ctxi, &p, &tag, &tag_len, &tag_data)) {
if (tag == TAG_USB_ENABLED) {
uint16_t ecaps = tag_data[0];
if (tag_len == 2) {
ecaps = get_uint16_t_be(tag_data);
ecaps = get_uint16_be(tag_data);
}
return ecaps & cap;
}
@@ -85,8 +88,8 @@ static uint8_t _piv_aid[] = {
0xA0, 0x00, 0x00, 0x03, 0x8,
};
int man_get_config() {
file_t *ef = search_dynamic_file(EF_DEV_CONF);
int man_get_config(void) {
file_t *ef = file_search(EF_DEV_CONF);
res_APDU_size = 0;
res_APDU[res_APDU_size++] = 0; // Overall length. Filled later
res_APDU[res_APDU_size++] = TAG_USB_SUPPORTED;
@@ -152,18 +155,18 @@ int man_get_config() {
return 0;
}
int cmd_read_config() {
static int cmd_read_config(void) {
man_get_config();
return SW_OK();
}
int cmd_write_config() {
static int cmd_write_config(void) {
if (apdu.data[0] != apdu.nc - 1) {
return SW_WRONG_DATA();
}
file_t *ef = file_new(EF_DEV_CONF);
file_put_data(ef, apdu.data + 1, (uint16_t)(apdu.nc - 1));
low_flash_available();
flash_commit();
#ifndef ENABLE_EMULATION
if (cap_supported(CAP_OTP)) {
phy_data.enabled_usb_itf |= PHY_USB_ITF_KB;
@@ -176,8 +179,8 @@ int cmd_write_config() {
return SW_OK();
}
extern int cbor_reset();
int cmd_factory_reset() {
extern int cbor_reset(void);
static int cmd_factory_reset(void) {
cbor_reset();
return SW_OK();
}
@@ -193,7 +196,7 @@ static const cmd_t cmds[] = {
{ 0x00, 0x0 }
};
int man_process_apdu() {
static int man_process_apdu(void) {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}

View File

@@ -50,6 +50,6 @@
#define FLAG_EJECT 0x80
extern bool cap_supported(uint16_t cap);
extern int man_get_config();
extern int man_get_config(void);
#endif //_MANAGEMENT_H

View File

@@ -15,16 +15,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include "picokeys.h"
#include "serial.h"
#include "fido.h"
#include "apdu.h"
#include "files.h"
#include "random.h"
#include "version.h"
#include "asn1.h"
#include "tlv.h"
#include "crypto_utils.h"
#include "management.h"
extern bool is_nk;
#define MAX_OATH_CRED 255
#define CHALLENGE_LEN 8
@@ -63,8 +63,8 @@ extern bool is_nk;
#define PROP_TOUCH 0x02
#define PROP_PIN 0x03
int oath_process_apdu();
int oath_unload();
static int oath_process_apdu(void);
static int oath_unload(void);
static bool validated = true;
static uint8_t challenge[CHALLENGE_LEN] = { 0 };
@@ -74,7 +74,7 @@ const uint8_t oath_aid[] = {
0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01
};
int oath_select(app_t *a, uint8_t force) {
static int oath_select(app_t *a, uint8_t force) {
(void) force;
if (cap_supported(CAP_OATH)) {
a->process_apdu = oath_process_apdu;
@@ -88,69 +88,69 @@ int oath_select(app_t *a, uint8_t force) {
res_APDU[res_APDU_size++] = TAG_NAME;
res_APDU[res_APDU_size++] = 8;
memcpy(res_APDU + res_APDU_size, pico_serial_str, 8); res_APDU_size += 8;
if (file_has_data(search_dynamic_file(EF_OATH_CODE)) == true) {
random_gen(NULL, challenge, sizeof(challenge));
if (file_has_data(file_search(EF_OATH_CODE)) == true) {
random_fill_buffer(challenge, sizeof(challenge));
res_APDU[res_APDU_size++] = TAG_CHALLENGE;
res_APDU[res_APDU_size++] = sizeof(challenge);
memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge));
res_APDU_size += sizeof(challenge);
res_APDU[res_APDU_size++] = TAG_ALGO;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
}
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
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 = file_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;
}
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;
}
apdu.ne = res_APDU_size;
return PICOKEY_OK;
return PICOKEYS_OK;
}
return PICOKEY_ERR_FILE_NOT_FOUND;
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
INITIALIZER ( oath_ctor ) {
register_app(oath_select, oath_aid);
}
int oath_unload() {
return PICOKEY_OK;
static int oath_unload(void) {
return PICOKEYS_OK;
}
file_t *find_oath_cred(const uint8_t *name, size_t name_len) {
static file_t *find_oath_cred(const uint8_t *name, size_t name_len) {
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
asn1_ctx_t ctxi, ef_tag = { 0 };
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
if (file_has_data(ef) && asn1_find_tag(&ctxi, TAG_NAME, &ef_tag) == true && ef_tag.len == name_len && memcmp(ef_tag.data, name, name_len) == 0) {
file_t *ef = file_search((uint16_t)(EF_OATH_CRED + i));
tlv_ctx_t ctxi, ef_tag = { 0 };
tlv_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
if (file_has_data(ef) && tlv_find_tag(&ctxi, TAG_NAME, &ef_tag) == true && ef_tag.len == name_len && memcmp(ef_tag.data, name, name_len) == 0) {
return ef;
}
}
return NULL;
}
int cmd_put() {
static int cmd_put(void) {
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
asn1_ctx_t ctxi, key = { 0 }, name = { 0 }, imf = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_KEY, &key) == false) {
tlv_ctx_t ctxi, key = { 0 }, name = { 0 }, imf = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_KEY, &key) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
if (tlv_find_tag(&ctxi, TAG_NAME, &name) == false) {
return SW_INCORRECT_PARAMS();
}
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
if (asn1_find_tag(&ctxi, TAG_IMF, &imf) == false) {
if (tlv_find_tag(&ctxi, TAG_IMF, &imf) == false) {
memcpy(apdu.data + apdu.nc, "\x7a\x08\x00\x00\x00\x00\x00\x00\x00\x00", 10);
apdu.nc += 10;
}
@@ -167,15 +167,15 @@ int cmd_put() {
file_t *ef = find_oath_cred(name.data, name.len);
if (file_has_data(ef)) {
file_put_data(ef, apdu.data, (uint16_t)apdu.nc);
low_flash_available();
flash_commit();
}
else {
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *tef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
file_t *tef = file_search((uint16_t)(EF_OATH_CRED + i));
if (!file_has_data(tef)) {
tef = file_new((uint16_t)(EF_OATH_CRED + i));
file_put_data(tef, apdu.data, (uint16_t)apdu.nc);
low_flash_available();
flash_commit();
return SW_OK();
}
}
@@ -185,16 +185,16 @@ int cmd_put() {
}
int cmd_delete() {
static int cmd_delete(void) {
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
asn1_ctx_t ctxi, ctxo = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_NAME, &ctxo) == true) {
tlv_ctx_t ctxi, ctxo = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_NAME, &ctxo) == true) {
file_t *ef = find_oath_cred(ctxo.data, ctxo.len);
if (ef) {
delete_file(ef);
file_delete(ef);
return SW_OK();
}
return SW_DATA_INVALID();
@@ -202,7 +202,7 @@ int cmd_delete() {
return SW_INCORRECT_PARAMS();
}
const mbedtls_md_info_t *get_oath_md_info(uint8_t alg) {
static const mbedtls_md_info_t *get_oath_md_info(uint8_t alg) {
if ((alg & ALG_MASK) == ALG_HMAC_SHA1) {
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
}
@@ -215,29 +215,29 @@ const mbedtls_md_info_t *get_oath_md_info(uint8_t alg) {
return NULL;
}
int cmd_set_code() {
static int cmd_set_code(void) {
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (apdu.nc == 0) {
delete_file(search_dynamic_file(EF_OATH_CODE));
file_delete(file_search(EF_OATH_CODE));
validated = true;
return SW_OK();
}
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_KEY, &key) == false) {
tlv_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_KEY, &key) == false) {
return SW_INCORRECT_PARAMS();
}
if (key.len == 0) {
delete_file(search_dynamic_file(EF_OATH_CODE));
file_delete(file_search(EF_OATH_CODE));
validated = true;
return SW_OK();
}
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
if (tlv_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
if (tlv_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
return SW_INCORRECT_PARAMS();
}
@@ -253,52 +253,52 @@ int cmd_set_code() {
if (memcmp(hmac, resp.data, resp.len) != 0) {
return SW_DATA_INVALID();
}
random_gen(NULL, challenge, sizeof(challenge));
random_fill_buffer(challenge, sizeof(challenge));
file_t *ef = file_new(EF_OATH_CODE);
file_put_data(ef, key.data, key.len);
low_flash_available();
flash_commit();
validated = false;
return SW_OK();
}
int cmd_reset() {
static int cmd_reset(void) {
if (P1(apdu) != 0xde || P2(apdu) != 0xad) {
return SW_INCORRECT_P1P2();
}
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
file_t *ef = file_search((uint16_t)(EF_OATH_CRED + i));
if (file_has_data(ef)) {
delete_file(ef);
file_delete(ef);
}
}
delete_file(search_dynamic_file(EF_OATH_CODE));
flash_clear_file(search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF));
low_flash_available();
file_delete(file_search(EF_OATH_CODE));
flash_clear_file(file_search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF));
flash_commit();
validated = true;
return SW_OK();
}
int cmd_list() {
static int cmd_list(void) {
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
bool ext = (apdu.nc == 1 && apdu.data[0] == 0x01);
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
file_t *ef = file_search((uint16_t)(EF_OATH_CRED + i));
if (file_has_data(ef)) {
asn1_ctx_t ctxi, key = { 0 }, name = { 0 }, pws = { 0 };
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == true && asn1_find_tag(&ctxi, TAG_KEY, &key) == true) {
tlv_ctx_t ctxi, key = { 0 }, name = { 0 }, pws = { 0 };
tlv_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
if (tlv_find_tag(&ctxi, TAG_NAME, &name) == true && tlv_find_tag(&ctxi, TAG_KEY, &key) == true) {
res_APDU[res_APDU_size++] = TAG_NAME_LIST;
res_APDU[res_APDU_size++] = (uint8_t)(name.len + 1 + (ext ? 1 : 0));
res_APDU[res_APDU_size++] = key.data[0];
memcpy(res_APDU + res_APDU_size, name.data, name.len); res_APDU_size += name.len;
if (ext) {
uint8_t props = 0x0;
if (asn1_find_tag(&ctxi, TAG_PWS_LOGIN, &pws) == true || asn1_find_tag(&ctxi, TAG_PWS_PASSWORD, &pws) == true || asn1_find_tag(&ctxi, TAG_PWS_METADATA, &pws) == true) {
if (tlv_find_tag(&ctxi, TAG_PWS_LOGIN, &pws) == true || tlv_find_tag(&ctxi, TAG_PWS_PASSWORD, &pws) == true || tlv_find_tag(&ctxi, TAG_PWS_METADATA, &pws) == true) {
props |= 0x4;
}
if (asn1_find_tag(&ctxi, TAG_PROPERTY, &pws) == true && (pws.data[0] & PROP_TOUCH)) {
if (tlv_find_tag(&ctxi, TAG_PROPERTY, &pws) == true && (pws.data[0] & PROP_TOUCH)) {
props |= 0x1;
}
res_APDU[res_APDU_size++] = props;
@@ -310,16 +310,16 @@ int cmd_list() {
return SW_OK();
}
int cmd_validate() {
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
static int cmd_validate(void) {
tlv_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
if (tlv_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = search_dynamic_file(EF_OATH_CODE);
file_t *ef = file_search(EF_OATH_CODE);
if (file_has_data(ef) == false) {
validated = true;
return SW_DATA_INVALID();
@@ -360,7 +360,7 @@ int calculate_oath(uint8_t truncate, const uint8_t *key, size_t key_len, const u
int r = mbedtls_md_hmac(md_info, key + 2, key_len - 2, chal, chal_len, hmac);
size_t hmac_size = mbedtls_md_get_size(md_info);
if (r != 0) {
return PICOKEY_EXEC_ERROR;
return PICOKEYS_EXEC_ERROR;
}
if (truncate == 0x01) {
res_APDU[res_APDU_size++] = 4 + 1;
@@ -377,36 +377,36 @@ int calculate_oath(uint8_t truncate, const uint8_t *key, size_t key_len, const u
memcpy(res_APDU + res_APDU_size, hmac, hmac_size); res_APDU_size += (uint16_t)hmac_size;
}
apdu.ne = res_APDU_size;
return PICOKEY_OK;
return PICOKEYS_OK;
}
int cmd_calculate() {
static int cmd_calculate(void) {
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
return SW_INCORRECT_P1P2();
}
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
tlv_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
if (tlv_find_tag(&ctxi, TAG_NAME, &name) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = find_oath_cred(name.data, name.len);
if (file_has_data(ef) == false) {
return SW_DATA_INVALID();
}
asn1_ctx_t ctxe;
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
if (asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
tlv_ctx_t ctxe;
tlv_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
if (tlv_find_tag(&ctxe, TAG_KEY, &key) == false) {
return SW_INCORRECT_PARAMS();
}
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
if (asn1_find_tag(&ctxe, TAG_IMF, &chal) == false) {
if (tlv_find_tag(&ctxe, TAG_IMF, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
}
@@ -414,48 +414,48 @@ int cmd_calculate() {
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
return SW_EXEC_ERROR();
}
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
uint64_t v = get_uint64_t_be(chal.data);
uint64_t v = get_uint64_be(chal.data);
size_t ef_size = file_get_size(ef);
v++;
uint8_t *tmp = (uint8_t *) calloc(1, ef_size);
memcpy(tmp, file_get_data(ef), ef_size);
asn1_ctx_t ctxt;
asn1_ctx_init(tmp, (uint16_t)ef_size, &ctxt);
asn1_find_tag(&ctxt, TAG_IMF, &chal);
put_uint64_t_be(v, chal.data);
tlv_ctx_t ctxt;
tlv_ctx_init(tmp, (uint16_t)ef_size, &ctxt);
tlv_find_tag(&ctxt, TAG_IMF, &chal);
put_uint64_be(v, chal.data);
file_put_data(ef, tmp, (uint16_t)ef_size);
low_flash_available();
flash_commit();
free(tmp);
}
apdu.ne = res_APDU_size;
return SW_OK();
}
int cmd_calculate_all() {
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, prop = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
static int cmd_calculate_all(void) {
tlv_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, prop = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
return SW_INCORRECT_P1P2();
}
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
if (tlv_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
res_APDU_size = 0;
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
file_t *ef = file_search((uint16_t)(EF_OATH_CRED + i));
if (file_has_data(ef)) {
const uint8_t *ef_data = file_get_data(ef);
size_t ef_len = file_get_size(ef);
asn1_ctx_t ctxe;
asn1_ctx_init((uint8_t *)ef_data, (uint16_t)ef_len, &ctxe);
if (asn1_find_tag(&ctxe, TAG_NAME, &name) == false || asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
tlv_ctx_t ctxe;
tlv_ctx_init((uint8_t *)ef_data, (uint16_t)ef_len, &ctxe);
if (tlv_find_tag(&ctxe, TAG_NAME, &name) == false || tlv_find_tag(&ctxe, TAG_KEY, &key) == false) {
continue;
}
res_APDU[res_APDU_size++] = TAG_NAME;
@@ -466,7 +466,7 @@ int cmd_calculate_all() {
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = key.data[1];
}
else if (asn1_find_tag(&ctxe, TAG_PROPERTY, &prop) == true && (prop.data[0] & PROP_TOUCH)) {
else if (tlv_find_tag(&ctxe, TAG_PROPERTY, &prop) == true && (prop.data[0] & PROP_TOUCH)) {
res_APDU[res_APDU_size++] = TAG_TOUCH_RESPONSE;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = key.data[1];
@@ -474,7 +474,7 @@ int cmd_calculate_all() {
else {
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = key.data[1];
}
@@ -485,62 +485,62 @@ int cmd_calculate_all() {
return SW_OK();
}
int cmd_send_remaining() {
static int cmd_send_remaining(void) {
return SW_OK();
}
int cmd_set_otp_pin() {
static int cmd_set_otp_pin(void) {
uint8_t hsh[33] = { 0 };
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
file_t *ef_otp_pin = file_search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (file_has_data(ef_otp_pin)) {
return SW_CONDITIONS_NOT_SATISFIED();
}
asn1_ctx_t ctxi, pw = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
tlv_ctx_t ctxi, pw = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
hsh[0] = MAX_OTP_COUNTER;
double_hash_pin(pw.data, pw.len, hsh + 1);
file_put_data(ef_otp_pin, hsh, sizeof(hsh));
low_flash_available();
flash_commit();
return SW_OK();
}
int cmd_change_otp_pin() {
static int cmd_change_otp_pin(void) {
uint8_t hsh[33] = { 0 };
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
file_t *ef_otp_pin = file_search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (!file_has_data(ef_otp_pin)) {
return SW_CONDITIONS_NOT_SATISFIED();
}
asn1_ctx_t ctxi, pw = { 0 }, new_pw = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
tlv_ctx_t ctxi, pw = { 0 }, new_pw = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
double_hash_pin(pw.data, pw.len, hsh + 1);
if (memcmp(file_get_data(ef_otp_pin) + 1, hsh + 1, 32) != 0) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (asn1_find_tag(&ctxi, TAG_NEW_PASSWORD, &new_pw) == false) {
if (tlv_find_tag(&ctxi, TAG_NEW_PASSWORD, &new_pw) == false) {
return SW_INCORRECT_PARAMS();
}
hsh[0] = MAX_OTP_COUNTER;
double_hash_pin(new_pw.data, new_pw.len, hsh + 1);
file_put_data(ef_otp_pin, hsh, sizeof(hsh));
low_flash_available();
flash_commit();
return SW_OK();
}
int cmd_verify_otp_pin() {
static int cmd_verify_otp_pin(void) {
uint8_t hsh[33] = { 0 }, data_hsh[33];
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
file_t *ef_otp_pin = file_search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (!file_has_data(ef_otp_pin)) {
return SW_CONDITIONS_NOT_SATISFIED();
}
asn1_ctx_t ctxi, pw = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
tlv_ctx_t ctxi, pw = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
double_hash_pin(pw.data, pw.len, hsh + 1);
@@ -550,49 +550,49 @@ int cmd_verify_otp_pin() {
data_hsh[0] -= 1;
}
file_put_data(ef_otp_pin, data_hsh, sizeof(data_hsh));
low_flash_available();
flash_commit();
validated = false;
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
data_hsh[0] = MAX_OTP_COUNTER;
file_put_data(ef_otp_pin, data_hsh, sizeof(data_hsh));
low_flash_available();
flash_commit();
validated = true;
return SW_OK();
}
int cmd_verify_hotp() {
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, code = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
static int cmd_verify_hotp(void) {
tlv_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, code = { 0 };
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
uint32_t code_int = 0;
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
if (tlv_find_tag(&ctxi, TAG_NAME, &name) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = find_oath_cred(name.data, name.len);
file_t *ef = file_search_by_fid(EF_OATH_CRED, NULL, SPECIFY_EF);
if (file_has_data(ef) == false) {
return SW_DATA_INVALID();
}
asn1_ctx_t ctxe;
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
if (asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
tlv_ctx_t ctxe;
tlv_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
if (tlv_find_tag(&ctxe, TAG_KEY, &key) == false) {
return SW_INCORRECT_PARAMS();
}
if ((key.data[0] & OATH_TYPE_MASK) != OATH_TYPE_HOTP) {
return SW_DATA_INVALID();
}
if (asn1_find_tag(&ctxe, TAG_IMF, &chal) == false) {
if (tlv_find_tag(&ctxe, TAG_IMF, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &code) == true) {
code_int = get_uint32_t_be(code.data);
if (tlv_find_tag(&ctxi, TAG_RESPONSE, &code) == true) {
code_int = get_uint32_be(code.data);
}
int ret = calculate_oath(0x01, key.data, key.len, chal.data, chal.len);
if (ret != PICOKEY_OK) {
if (ret != PICOKEYS_OK) {
return SW_EXEC_ERROR();
}
uint32_t res_int = get_uint32_t_be(res_APDU + 2);
uint32_t res_int = get_uint32_be(res_APDU + 2);
if (res_APDU[1] == 6) {
res_int %= (uint32_t) 1e6;
}
@@ -607,8 +607,8 @@ int cmd_verify_hotp() {
return SW_OK();
}
int cmd_rename() {
asn1_ctx_t ctxi, name = { 0 }, new_name = { 0 };
static int cmd_rename(void) {
tlv_ctx_t ctxi, name = { 0 }, new_name = { 0 };
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
@@ -616,16 +616,16 @@ int cmd_rename() {
if (apdu.data[0] != TAG_NAME) {
return SW_WRONG_DATA();
}
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_NAME, &name) == false) {
return SW_WRONG_DATA();
}
asn1_ctx_init(name.data + name.len, (uint16_t)(apdu.nc - (name.data + name.len - apdu.data)), &ctxi);
if (asn1_find_tag(&ctxi, TAG_NAME, &new_name) == false) {
tlv_ctx_init(name.data + name.len, (uint16_t)(apdu.nc - (name.data + name.len - apdu.data)), &ctxi);
if (tlv_find_tag(&ctxi, TAG_NAME, &new_name) == false) {
return SW_WRONG_DATA();
}
if (memcmp(name.data, new_name.data, name.len) == 0) {
if (name.len == new_name.len && memcmp(name.data, new_name.data, name.len) == 0) {
return SW_WRONG_DATA();
}
file_t *ef = find_oath_cred(name.data, name.len);
@@ -634,8 +634,8 @@ int cmd_rename() {
}
uint8_t *fdata = file_get_data(ef);
uint16_t fsize = file_get_size(ef);
asn1_ctx_init(fdata, fsize, &ctxi);
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
tlv_ctx_init(fdata, fsize, &ctxi);
if (tlv_find_tag(&ctxi, TAG_NAME, &name) == false) {
return SW_WRONG_DATA();
}
uint8_t *new_data = (uint8_t *) calloc(fsize + new_name.len - name.len, sizeof(uint8_t));
@@ -644,50 +644,50 @@ int cmd_rename() {
memcpy(new_data + (name.data - fdata), new_name.data, new_name.len);
memcpy(new_data + (name.data - fdata) + new_name.len, name.data + name.len, fsize - (name.data + name.len - fdata));
file_put_data(ef, new_data, fsize + new_name.len - name.len);
low_flash_available();
flash_commit();
free(new_data);
return SW_OK();
}
int cmd_get_credential() {
asn1_ctx_t ctxi, name = { 0 };
static int cmd_get_credential(void) {
tlv_ctx_t ctxi, name = { 0 };
if (apdu.nc < 3) {
return SW_INCORRECT_PARAMS();
}
if (apdu.data[0] != TAG_NAME) {
return SW_WRONG_DATA();
}
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
tlv_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (tlv_find_tag(&ctxi, TAG_NAME, &name) == false) {
return SW_WRONG_DATA();
}
file_t *ef = find_oath_cred(name.data, name.len);
if (file_has_data(ef) == false) {
return SW_DATA_INVALID();
}
asn1_ctx_t login = { 0 }, pw = { 0 }, meta = { 0 }, prop = { 0 };
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == true) {
tlv_ctx_t login = { 0 }, pw = { 0 }, meta = { 0 }, prop = { 0 };
tlv_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
if (tlv_find_tag(&ctxi, TAG_NAME, &name) == true) {
res_APDU[res_APDU_size++] = TAG_NAME;
res_APDU[res_APDU_size++] = (uint8_t)(name.len);
memcpy(res_APDU + res_APDU_size, name.data, name.len); res_APDU_size += name.len;
}
if (asn1_find_tag(&ctxi, TAG_PWS_LOGIN, &login) == true) {
if (tlv_find_tag(&ctxi, TAG_PWS_LOGIN, &login) == true) {
res_APDU[res_APDU_size++] = TAG_PWS_LOGIN;
res_APDU[res_APDU_size++] = (uint8_t)(login.len);
memcpy(res_APDU + res_APDU_size, login.data, login.len); res_APDU_size += login.len;
}
if (asn1_find_tag(&ctxi, TAG_PWS_PASSWORD, &pw) == true) {
if (tlv_find_tag(&ctxi, TAG_PWS_PASSWORD, &pw) == true) {
res_APDU[res_APDU_size++] = TAG_PWS_PASSWORD;
res_APDU[res_APDU_size++] = (uint8_t)(pw.len);
memcpy(res_APDU + res_APDU_size, pw.data, pw.len); res_APDU_size += pw.len;
}
if (asn1_find_tag(&ctxi, TAG_PWS_METADATA, &meta) == true) {
if (tlv_find_tag(&ctxi, TAG_PWS_METADATA, &meta) == true) {
res_APDU[res_APDU_size++] = TAG_PWS_METADATA;
res_APDU[res_APDU_size++] = (uint8_t)(meta.len);
memcpy(res_APDU + res_APDU_size, meta.data, meta.len); res_APDU_size += meta.len;
}
if (asn1_find_tag(&ctxi, TAG_PROPERTY, &prop) == true) {
if (tlv_find_tag(&ctxi, TAG_PROPERTY, &prop) == true) {
res_APDU[res_APDU_size++] = TAG_PROPERTY;
res_APDU[res_APDU_size++] = (uint8_t)(prop.len);
memcpy(res_APDU + res_APDU_size, prop.data, prop.len); res_APDU_size += prop.len;
@@ -731,7 +731,7 @@ static const cmd_t cmds[] = {
{ 0x00, 0x0 }
};
int oath_process_apdu() {
static int oath_process_apdu(void) {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}

View File

@@ -15,21 +15,30 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "pico_keys.h"
#include <stdio.h>
#include "picokeys.h"
#include "serial.h"
#include "button.h"
#include "fido.h"
#include "apdu.h"
#include "files.h"
#include "random.h"
#include "version.h"
#include "asn1.h"
#include "hid/ctap_hid.h"
#include "usb.h"
#if defined(PICO_PLATFORM)
#include "bsp/board.h"
#endif
#ifdef ENABLE_EMULATION
void add_keyboard_buffer(const uint8_t *buf, size_t len, bool press_enter) {}
void append_keyboard_buffer(const uint8_t *buf, size_t len) {}
void add_keyboard_buffer(const uint8_t *buf, size_t len, bool press_enter) {
(void)buf;
(void)len;
(void)press_enter;
}
void append_keyboard_buffer(const uint8_t *buf, size_t len) {
(void)buf;
(void)len;
}
#else
#include "tusb.h"
#endif
@@ -49,6 +58,11 @@ void append_keyboard_buffer(const uint8_t *buf, size_t len) {}
#define CONFIG_LED_INV 0x10
#define CONFIG_STATUS_MASK 0x1f
#define CONFIG3_VALID 0x01
#define CONFIG4_VALID 0x02
#define CONFIG3_TOUCH 0x04
#define CONFIG4_TOUCH 0x08
/* EXT Flags */
#define SERIAL_BTN_VISIBLE 0x01 // Serial number visible at startup (button press)
#define SERIAL_USB_VISIBLE 0x02 // Serial number visible in USB iSerial field
@@ -114,42 +128,40 @@ typedef struct otp_config {
}) otp_config_t;
#define otp_config_size sizeof(otp_config_t)
uint16_t otp_status(bool is_otp);
int otp_process_apdu();
int otp_unload();
static uint16_t otp_status(bool is_otp);
static int otp_process_apdu(void);
static int otp_unload(void);
extern int (*hid_set_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t);
extern uint16_t (*hid_get_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t);
int otp_hid_set_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t);
uint16_t otp_hid_get_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t);
static int otp_hid_set_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t);
static uint16_t otp_hid_get_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t);
const uint8_t otp_aid[] = {
7,
0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
};
int otp_select(app_t *a, uint8_t force) {
static int otp_select(app_t *a, uint8_t force) {
(void) force;
if (cap_supported(CAP_OTP)) {
a->process_apdu = otp_process_apdu;
a->unload = otp_unload;
if (file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ||
file_has_data(search_dynamic_file(EF_OTP_SLOT2))) {
if (file_has_data(file_search(EF_OTP_SLOT1)) || file_has_data(file_search(EF_OTP_SLOT2))) {
config_seq = 1;
}
else {
config_seq = 0;
}
otp_status(false);
return PICOKEY_OK;
return PICOKEYS_OK;
}
return PICOKEY_ERR_FILE_NOT_FOUND;
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
uint8_t modhex_tab[] =
{ 'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v' };
int encode_modhex(const uint8_t *in, size_t len, uint8_t *out) {
static int encode_modhex(const uint8_t *in, size_t len, uint8_t *out) {
for (size_t l = 0; l < len; l++) {
*out++ = modhex_tab[in[l] >> 4];
*out++ = modhex_tab[in[l] & 0xf];
@@ -157,36 +169,29 @@ int encode_modhex(const uint8_t *in, size_t len, uint8_t *out) {
return 0;
}
static bool scanned = false;
extern void scan_all();
void init_otp() {
void init_otp(void) {
if (scanned == false) {
scan_all();
for (uint8_t i = 0; i < 2; i++) {
file_t *ef = search_dynamic_file(EF_OTP_SLOT1 + i);
for (uint8_t i = 0; i < 4; i++) {
file_t *ef = file_search(EF_OTP_SLOT1 + i);
uint8_t *data = file_get_data(ef);
otp_config_t *otp_config = (otp_config_t *) data;
if (file_has_data(ef) && !(otp_config->tkt_flags & OATH_HOTP) &&
!(otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET)) {
uint16_t counter = get_uint16_t_be(data + otp_config_size);
uint16_t counter = get_uint16_be(data + otp_config_size);
if (++counter <= 0x7fff) {
uint8_t new_data[otp_config_size + 8];
memcpy(new_data, data, sizeof(new_data));
put_uint16_t_be(counter, new_data + otp_config_size);
put_uint16_be(counter, new_data + otp_config_size);
file_put_data(ef, new_data, sizeof(new_data));
}
}
}
scanned = true;
low_flash_available();
flash_commit();
}
}
extern int calculate_oath(uint8_t truncate,
const uint8_t *key,
size_t key_len,
const uint8_t *chal,
size_t chal_len);
uint16_t calculate_crc(const uint8_t *data, size_t data_len) {
static uint16_t calculate_crc(const uint8_t *data, size_t data_len) {
uint16_t crc = 0xFFFF;
for (size_t idx = 0; idx < data_len; idx++) {
crc ^= data[idx];
@@ -202,12 +207,13 @@ uint16_t calculate_crc(const uint8_t *data, size_t data_len) {
}
static uint8_t session_counter[2] = { 0 };
int otp_button_pressed(uint8_t slot) {
static int otp_button_pressed(uint8_t slot) {
init_otp();
if (!cap_supported(CAP_OTP)) {
return 3;
}
file_t *ef = search_dynamic_file(slot == 1 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
uint16_t slot_ef = EF_OTP_SLOT1 + slot - 1;
file_t *ef = file_search(slot_ef);
const uint8_t *data = file_get_data(ef);
otp_config_t *otp_config = (otp_config_t *) data;
if (file_has_data(ef) == false) {
@@ -223,18 +229,18 @@ int otp_button_pressed(uint8_t slot) {
memcpy(tmp_key + 2, otp_config->aes_key, KEY_SIZE);
uint64_t imf = 0;
const uint8_t *p = data + otp_config_size;
imf = get_uint64_t_be(p);
imf = get_uint64_be(p);
p += 8;
if (imf == 0) {
imf = get_uint16_t_be(otp_config->uid + 4);
imf = get_uint16_be(otp_config->uid + 4);
}
uint8_t chal[8];
put_uint64_t_be(imf, chal);
put_uint64_be(imf, chal);
res_APDU_size = 0;
int ret = calculate_oath(1, tmp_key, sizeof(tmp_key), chal, sizeof(chal));
if (ret == PICOKEY_OK) {
if (ret == PICOKEYS_OK) {
uint32_t base = otp_config->cfg_flags & OATH_HOTP8 ? 1e8 : 1e6;
uint32_t number = get_uint16_t_be(res_APDU + 2);
uint32_t number = get_uint16_be(res_APDU + 2);
number %= base;
char number_str[9];
if (otp_config->cfg_flags & OATH_HOTP8) {
@@ -247,12 +253,12 @@ int otp_button_pressed(uint8_t slot) {
}
imf++;
uint8_t new_chal[8];
put_uint64_t_be(imf, new_chal);
put_uint64_be(imf, new_chal);
uint8_t new_otp_config[otp_config_size + sizeof(new_chal)];
memcpy(new_otp_config, otp_config, otp_config_size);
memcpy(new_otp_config + otp_config_size, new_chal, sizeof(new_chal));
file_put_data(ef, new_otp_config, sizeof(new_otp_config));
low_flash_available();
flash_commit();
}
if (otp_config->tkt_flags & APPEND_CR) {
append_keyboard_buffer((const uint8_t *) "\r", 1);
@@ -272,7 +278,7 @@ int otp_button_pressed(uint8_t slot) {
else {
uint8_t otpk[22], *po = otpk;
bool update_counter = false;
uint16_t counter = get_uint16_t_be(data + otp_config_size), crc = 0;
uint16_t counter = get_uint16_be(data + otp_config_size), crc = 0;
uint32_t ts = board_millis() / 1000;
if (counter == 0) {
update_counter = true;
@@ -282,16 +288,16 @@ int otp_button_pressed(uint8_t slot) {
po += 6;
memcpy(po, otp_config->uid, UID_SIZE);
po += UID_SIZE;
po += put_uint16_t_le(counter, po);
po += put_uint16_le(counter, po);
ts >>= 1;
*po++ = ts & 0xff;
*po++ = ts >> 8;
*po++ = ts >> 16;
*po++ = session_counter[slot - 1];
random_gen(NULL, po, 2);
random_fill_buffer(po, 2);
po += 2;
crc = calculate_crc(otpk + 6, 14);
po += put_uint16_t_le(~crc, po);
po += put_uint16_le(~crc, po);
mbedtls_aes_context ctx;
mbedtls_aes_init(&ctx);
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
@@ -312,9 +318,9 @@ int otp_button_pressed(uint8_t slot) {
if (update_counter == true) {
uint8_t new_data[otp_config_size + 8];
memcpy(new_data, data, sizeof(new_data));
put_uint16_t_be(counter, new_data + otp_config_size);
put_uint16_be(counter, new_data + otp_config_size);
file_put_data(ef, new_data, sizeof(new_data));
low_flash_available();
flash_commit();
}
}
@@ -328,12 +334,45 @@ INITIALIZER( otp_ctor ) {
hid_get_report_cb = otp_hid_get_report_cb;
}
int otp_unload() {
return PICOKEY_OK;
static int otp_unload(void) {
return PICOKEYS_OK;
}
uint8_t status_byte = 0x0;
uint16_t otp_status(bool is_otp) {
static uint16_t otp_status_ext(void) {
for (int i = 0; i < 4; i++) {
file_t *ef = file_search(EF_OTP_SLOT1 + i);
if (file_has_data(ef)) {
res_APDU[res_APDU_size++] = 0xB0 + i;
res_APDU[res_APDU_size++] = 0; // Filled later
uint8_t *p = res_APDU + res_APDU_size;
otp_config_t *otp_config = (otp_config_t *)file_get_data(ef);
*p++ = 0xA0;
*p++ = 2;
*p++ = otp_config->tkt_flags;
*p++ = otp_config->cfg_flags;
if (otp_config->cfg_flags & CHAL_YUBICO && otp_config->tkt_flags & CHAL_RESP) {
}
else if (otp_config->tkt_flags & OATH_HOTP) {
}
else if (otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET) {
}
else {
*p++ = 0xC0;
*p++ = 6;
memcpy(p, otp_config->fixed_data, 6);
p += 6;
}
uint8_t len = p - (res_APDU + res_APDU_size);
res_APDU[res_APDU_size - 1] = len;
res_APDU_size += len;
}
}
return SW_OK();
}
static uint16_t otp_status(bool is_otp) {
if (scanned == false) {
scan_all();
scanned = true;
@@ -347,7 +386,7 @@ uint16_t otp_status(bool is_otp) {
res_APDU[res_APDU_size++] = 0;
res_APDU[res_APDU_size++] = config_seq;
uint8_t opts = 0;
file_t *ef = search_dynamic_file(EF_OTP_SLOT1);
file_t *ef = file_search(EF_OTP_SLOT1);
if (file_has_data(ef)) {
opts |= CONFIG1_VALID;
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
@@ -355,7 +394,7 @@ uint16_t otp_status(bool is_otp) {
opts |= CONFIG1_TOUCH;
}
}
ef = search_dynamic_file(EF_OTP_SLOT2);
ef = file_search(EF_OTP_SLOT2);
if (file_has_data(ef)) {
opts |= CONFIG2_VALID;
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
@@ -376,49 +415,54 @@ uint16_t otp_status(bool is_otp) {
return SW_OK();
}
bool check_crc(const otp_config_t *data) {
static bool check_crc(const otp_config_t *data) {
uint16_t crc = calculate_crc((const uint8_t *) data, otp_config_size);
return crc == 0xF0B8;
}
bool _is_otp = false;
int cmd_otp() {
static int cmd_otp(void) {
uint8_t p1 = P1(apdu), p2 = P2(apdu);
if (p2 != 0x00) {
return SW_INCORRECT_P1P2();
}
if (p1 == 0x01 || p1 == 0x03) { // Configure slot
otp_config_t *odata = (otp_config_t *) apdu.data;
file_t *ef = file_new(p1 == 0x01 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
if (p1 == 0x03 && p2 != 0x0) {
return SW_INCORRECT_P1P2();
}
uint16_t slot = (p1 == 0x01 ? EF_OTP_SLOT1 : EF_OTP_SLOT2) + p2;
file_t *ef = file_new(slot);
if (file_has_data(ef)) {
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
}
for (int c = 0; c < otp_config_size; c++) {
for (size_t c = 0; c < otp_config_size; c++) {
if (apdu.data[c] != 0) {
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
return SW_WRONG_DATA();
}
memset(apdu.data + otp_config_size, 0, 8); // Add 8 bytes extra
file_put_data(ef, apdu.data, otp_config_size + 8);
low_flash_available();
flash_commit();
config_seq++;
return otp_status(_is_otp);
}
}
// Delete slot
delete_file(ef);
file_delete(ef);
config_seq++;
return otp_status(_is_otp);
}
else if (p1 == 0x04 || p1 == 0x05) { // Update slot
otp_config_t *odata = (otp_config_t *) apdu.data;
if (p1 == 0x05 && p2 != 0x0) {
return SW_INCORRECT_P1P2();
}
uint16_t slot = (p1 == 0x04 ? EF_OTP_SLOT1 : EF_OTP_SLOT2) + p2;
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
return SW_WRONG_DATA();
}
file_t *ef = search_dynamic_file(p1 == 0x04 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
file_t *ef = file_search(slot);
if (file_has_data(ef)) {
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
@@ -438,7 +482,7 @@ int cmd_otp() {
odata->cfg_flags = otpc->cfg_flags;
}
file_put_data(ef, apdu.data, otp_config_size);
low_flash_available();
flash_commit();
config_seq++;
}
return otp_status(_is_otp);
@@ -446,8 +490,16 @@ int cmd_otp() {
else if (p1 == 0x06) { // Swap slots
uint8_t tmp[otp_config_size + 8];
bool ef1_data = false;
file_t *ef1 = file_new(EF_OTP_SLOT1);
file_t *ef2 = file_new(EF_OTP_SLOT2);
uint16_t slot1 = EF_OTP_SLOT1, slot2 = EF_OTP_SLOT2;
if (apdu.ne > 0) {
if (apdu.ne != 2) {
return SW_WRONG_LENGTH();
}
slot1 += apdu.data[0];
slot2 += apdu.data[1];
}
file_t *ef1 = file_new(slot1);
file_t *ef2 = file_new(slot2);
if (file_has_data(ef1)) {
memcpy(tmp, file_get_data(ef1), file_get_size(ef1));
ef1_data = true;
@@ -456,17 +508,17 @@ int cmd_otp() {
file_put_data(ef1, file_get_data(ef2), file_get_size(ef2));
}
else {
delete_file(ef1);
file_delete(ef1);
// When a dynamic file is deleted, existing referenes are invalidated
ef2 = file_new(EF_OTP_SLOT2);
ef2 = file_new(slot2);
}
if (ef1_data) {
file_put_data(ef2, tmp, sizeof(tmp));
}
else {
delete_file(ef2);
file_delete(ef2);
}
low_flash_available();
flash_commit();
config_seq++;
return otp_status(_is_otp);
}
@@ -478,8 +530,15 @@ int cmd_otp() {
else if (p1 == 0x13) { // Get config
man_get_config();
}
else if (p1 == 0x14) {
otp_status_ext();
}
else if (p1 == 0x30 || p1 == 0x38 || p1 == 0x20 || p1 == 0x28) { // Calculate OTP
file_t *ef = search_dynamic_file(p1 == 0x30 || p1 == 0x20 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
if ((p1 == 0x38 || p1 == 0x28) && p2 != 0x0) {
return SW_INCORRECT_P1P2();
}
uint16_t slot = (p1 == 0x30 || p1 == 0x20 ? EF_OTP_SLOT1 : EF_OTP_SLOT2) + p2;
file_t *ef = file_search(slot);
if (file_has_data(ef)) {
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
if (!(otp_config->tkt_flags & CHAL_RESP)) {
@@ -491,7 +550,7 @@ int cmd_otp() {
status_byte = 0x20;
otp_status(_is_otp);
#ifndef ENABLE_EMULATION
if (wait_button() == true) {
if (button_wait()) {
status_byte = 0x00;
otp_status(_is_otp);
return SW_CONDITIONS_NOT_SATISFIED();
@@ -549,7 +608,7 @@ static const cmd_t cmds[] = {
{ 0x00, 0x0 }
};
int otp_process_apdu() {
static int otp_process_apdu(void) {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}
@@ -569,11 +628,9 @@ uint8_t otp_frame_tx[70] = {0};
uint8_t otp_exp_seq = 0, otp_curr_seq = 0;
uint8_t otp_header[4] = {0};
extern uint16_t *get_send_buffer_size(uint8_t itf);
int otp_send_frame(uint8_t *frame, size_t frame_len) {
static int otp_send_frame(uint8_t *frame, size_t frame_len) {
uint16_t crc = calculate_crc(frame, frame_len);
frame_len += put_uint16_t_le(~crc, frame + frame_len);
frame_len += put_uint16_le(~crc, frame + frame_len);
*get_send_buffer_size(ITF_KEYBOARD) = frame_len;
otp_exp_seq = (frame_len / 7);
if (frame_len % 7) {
@@ -583,7 +640,10 @@ int otp_send_frame(uint8_t *frame, size_t frame_len) {
return 0;
}
int otp_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
static int otp_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
(void)itf;
(void)report_id;
(void)bufsize;
if (report_type == 3) {
DEBUG_PAYLOAD(buffer, bufsize);
if (buffer[7] == 0xFF) { // reset
@@ -601,7 +661,7 @@ int otp_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t repo
if (rseq == 9) {
DEBUG_DATA(otp_frame_rx, sizeof(otp_frame_rx));
DEBUG_PAYLOAD(otp_frame_rx, sizeof(otp_frame_rx));
uint16_t residual_crc = calculate_crc(otp_frame_rx, 64), rcrc = get_uint16_t_le(otp_frame_rx + 65);
uint16_t residual_crc = calculate_crc(otp_frame_rx, 64), rcrc = get_uint16_le(otp_frame_rx + 65);
uint8_t slot_id = otp_frame_rx[64];
if (residual_crc == rcrc) {
uint8_t hdr[5];
@@ -631,7 +691,7 @@ int otp_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t repo
return 0;
}
uint16_t otp_hid_get_report_cb(uint8_t itf,
static uint16_t otp_hid_get_report_cb(uint8_t itf,
uint8_t report_id,
hid_report_type_t report_type,
uint8_t *buffer,

View File

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

View File

@@ -22,6 +22,9 @@ RUN apt install -y libccid \
cmake \
libfuse-dev \
python3-pyscard \
libtss2-dev \
tpm2-tools \
swtpm \
&& rm -rf /var/lib/apt/lists/*
RUN pip3 install pytest pycvc cryptography inputimeout fido2==2.0.0 --break-system-packages
WORKDIR /

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)

View File

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

View File

@@ -25,6 +25,7 @@ INS_PUT = 0x01
INS_DELETE = 0x02
INS_SET_CODE = 0x03
INS_RESET = 0x04
INS_RENAME = 0x05
INS_LIST = 0xa1
INS_CALCULATE = 0xa2
INS_VALIDATE = 0xa3
@@ -89,6 +90,24 @@ def test_life(reset_oath):
resp = list_apdu(reset_oath)
assert(len(resp) == 0)
def test_rename_prefix_extension(reset_oath):
old_name = b"30/test"
new_name = b"30/test2"
key = list(bytes(b"foo bar"))
put_data = [TAG_NAME, len(old_name)] + list(old_name)
put_data += [TAG_KEY, len(key) + 2, TYPE_TOTP | ALG_SHA1, 6] + key
send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=put_data)
rename_data = [TAG_NAME, len(old_name)] + list(old_name)
rename_data += [TAG_NAME, len(new_name)] + list(new_name)
send_apdu(reset_oath, INS_RENAME, p1=0, p2=0, data=rename_data)
resp = list_apdu(reset_oath)
exp = [TAG_NAME_LIST, len(new_name) + 1, TYPE_TOTP | ALG_SHA1] + list(new_name)
assert resp == exp
def test_overwrite(reset_oath):
data = data_name + data_key
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=list(data))

View File

@@ -1,61 +0,0 @@
import sys
import keyring
DOMAIN = "PicoKeys.com"
USERNAME = "Pico-Fido"
try:
import keyring
from keyrings.osx_keychain_keys.backend import OSXKeychainKeysBackend, OSXKeychainKeyType, OSXKeyChainKeyClassType
except:
print('ERROR: keyring module not found! Install keyring package.\nTry with `pip install keyrings.osx-keychain-keys`')
sys.exit(-1)
try:
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
except:
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
sys.exit(-1)
def get_backend(use_secure_enclave=False):
backend = OSXKeychainKeysBackend(
key_type=OSXKeychainKeyType.EC, # Key type, e.g. RSA, RC, DSA, ...
key_class_type=OSXKeyChainKeyClassType.Private, # Private key, Public key, Symmetric-key
key_size_in_bits=256,
is_permanent=True, # If set, saves the key in keychain; else, returns a transient key
use_secure_enclave=use_secure_enclave, # Saves the key in the T2 (TPM) chip, requires a code-signed interpreter
access_group=None, # Limits key management and retrieval to set group, requires a code-signed interpreter
is_extractable=True # If set, private key is extractable; else, it can't be retrieved, but only operated against
)
return backend
def generate_secure_key(use_secure_enclave=False):
backend = get_backend(use_secure_enclave)
backend.set_password(DOMAIN, USERNAME, password=None)
return backend.get_password(DOMAIN, USERNAME)
def get_d(key):
return key.private_numbers().private_value.to_bytes(32, 'big')
def set_secure_key(pk):
backend = get_backend(False)
try:
backend.delete_password(DOMAIN, USERNAME)
except:
pass
backend.set_password(DOMAIN, USERNAME, pk.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption()))
def get_secure_key():
key = None
try:
backend = get_backend(False)
key = backend.get_password(DOMAIN, USERNAME)[0]
if (key is None):
raise TypeError
except (keyring.errors.KeyringError, TypeError):
try:
key = generate_secure_key(False)[0] # It should be True, but secure enclave causes python segfault
except keyring.errors.PasswordSetError:
key = generate_secure_key(False)[0]
return get_d(key)

View File

@@ -1,44 +0,0 @@
import sys
DOMAIN = "PicoKeys.com"
USERNAME = "Pico-Fido"
try:
import keyring
except:
print('ERROR: keyring module not found! Install keyring package.\nTry with `pip install keyring`')
sys.exit(-1)
try:
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption, load_pem_private_key
from cryptography.hazmat.primitives.asymmetric import ec
except:
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
sys.exit(-1)
def generate_secure_key():
pkey = ec.generate_private_key(ec.SECP256R1())
set_secure_key(pkey)
return keyring.get_password(DOMAIN, USERNAME)
def get_d(key):
return load_pem_private_key(key, password=None).private_numbers().private_value.to_bytes(32, 'big')
def set_secure_key(pk):
try:
keyring.delete_password(DOMAIN, USERNAME)
except:
pass
keyring.set_password(DOMAIN, USERNAME, pk.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()).decode())
def get_secure_key():
key = None
try:
key = keyring.get_password(DOMAIN, USERNAME)
if (key is None):
raise TypeError
except (keyring.errors.KeyringError, TypeError):
key = generate_secure_key()
return get_d(key.encode())

File diff suppressed because one or more lines are too long

View File

@@ -49,6 +49,7 @@ cd build
esptool.py --chip ESP32-S2 merge_bin -o ../release/pico_fido_esp32-s2.bin @flash_args
cd ..
else
sudo apt install -y libtss2-dev tpm2-tools swtpm cmake
mkdir build
cd build
cmake -DENABLE_EMULATION=1 ..