mirror of
https://github.com/polhenarejos/pico-fido
synced 2026-06-04 11:59:06 +02:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ffcfb4beb | ||
|
|
3ccd6e827f | ||
|
|
a2044d697d | ||
|
|
659c04c837 | ||
|
|
a0437dbfb2 | ||
|
|
604e7868e2 | ||
|
|
0e3d0d9a7d | ||
|
|
1f4be2a051 | ||
|
|
dc6007d1b4 | ||
|
|
6aa986ca06 | ||
|
|
98145a0ef4 | ||
|
|
342ae90df8 | ||
|
|
39b9dbb8d6 | ||
|
|
b66a80eb01 | ||
|
|
ca15206e77 | ||
|
|
ec10becbef | ||
|
|
b88e52971f | ||
|
|
f658ef6eab | ||
|
|
4e7c40b2bd | ||
|
|
f4487a1ff4 | ||
|
|
edf6a697ba | ||
|
|
24978a5476 | ||
|
|
1795f5c330 | ||
|
|
8d81e2eb24 | ||
|
|
ab3ddf4e23 | ||
|
|
d8ccf9bd28 | ||
|
|
0e1512a135 | ||
|
|
ac0462525a | ||
|
|
bb20a75ef4 | ||
|
|
8d709cf745 | ||
|
|
bbbbcadf4c | ||
|
|
fbbf1feb49 | ||
|
|
22de41bfe0 | ||
|
|
31a6315721 | ||
|
|
9fb8d475b3 | ||
|
|
c0298ece7d | ||
|
|
bea7706d63 | ||
|
|
cfd22c2d2c | ||
|
|
8ea118fe91 | ||
|
|
8e6f571b48 | ||
|
|
3c20800839 | ||
|
|
f2eef5b839 | ||
|
|
bc6ebdd069 | ||
|
|
3f890757ac | ||
|
|
18d68d7e05 | ||
|
|
c8d62de621 | ||
|
|
60165c21ca | ||
|
|
55a60f8875 | ||
|
|
7ed90007ef | ||
|
|
c23cc9ffe1 | ||
|
|
dc4565a8fb | ||
|
|
804ee68e86 | ||
|
|
fe49149d86 | ||
|
|
81d97f1a18 | ||
|
|
d16016cf1e | ||
|
|
2e0333677b | ||
|
|
becdc94339 | ||
|
|
bd499ae1d4 | ||
|
|
5fc84d7097 | ||
|
|
ac7e34522a | ||
|
|
70dec5596a | ||
|
|
2331dcb3ec | ||
|
|
42b8b0af5f | ||
|
|
ceb54d9a08 | ||
|
|
527943fbba | ||
|
|
7dddfd971e | ||
|
|
29f942dab9 | ||
|
|
aa9df892d3 | ||
|
|
7ac2ce30f0 | ||
|
|
e86862033c | ||
|
|
ae36143498 | ||
|
|
d90dbb6c5f | ||
|
|
1d21d93b74 | ||
|
|
8b9be258de | ||
|
|
46720fb387 | ||
|
|
1867f0330f | ||
|
|
bb542e3b83 |
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Delete private key
|
||||
run: rm private.pem
|
||||
- name: Update nightly release
|
||||
uses: pyTooling/Actions/releaser@main
|
||||
uses: pyTooling/Actions/releaser@v6.7.0
|
||||
with:
|
||||
tag: nightly-${{ matrix.refs }}
|
||||
rm: true
|
||||
|
||||
295
CMakeLists.txt
295
CMakeLists.txt
@@ -1,68 +1,84 @@
|
||||
#
|
||||
# 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 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
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
# 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 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
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
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)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
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(NOT ENABLE_EMULATION)
|
||||
set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1)
|
||||
include(pico_sdk_import.cmake)
|
||||
endif()
|
||||
|
||||
if(ENABLE_EMULATION)
|
||||
else()
|
||||
set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1)
|
||||
include(pico_sdk_import.cmake)
|
||||
project(pico_fido C CXX ASM)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(pico_fido)
|
||||
endif()
|
||||
|
||||
project(pico_fido C CXX ASM)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
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,98 +88,127 @@ 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}
|
||||
${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
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_reset.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_get_info.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_make_credential.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/known_apps.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_client_pin.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/credential.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_get_assertion.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_selection.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_cred_mgmt.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_config.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_vendor.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_large_blobs.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/management.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/defs.c
|
||||
)
|
||||
if (${ENABLE_OATH_APP})
|
||||
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/cmd_register.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_authenticate.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cmd_version.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_reset.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_get_info.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_make_credential.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/known_apps.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_client_pin.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/credential.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_get_assertion.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_selection.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_cred_mgmt.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_config.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_vendor.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_large_blobs.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/management.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/defs.c
|
||||
)
|
||||
|
||||
if(ENABLE_OATH_APP)
|
||||
list(APPEND SOURCES
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido/oath.c
|
||||
)
|
||||
endif()
|
||||
if (${ENABLE_OTP_APP})
|
||||
set(SOURCES ${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)
|
||||
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")
|
||||
if(ESP_PLATFORM)
|
||||
project(pico_fido)
|
||||
endif()
|
||||
|
||||
set(INCLUDES ${INCLUDES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido
|
||||
)
|
||||
${CMAKE_CURRENT_LIST_DIR}/src/fido
|
||||
)
|
||||
|
||||
if(NOT ESP_PLATFORM)
|
||||
target_sources(pico_fido PUBLIC ${SOURCES})
|
||||
target_include_directories(pico_fido PUBLIC ${INCLUDES})
|
||||
target_sources(pico_fido PUBLIC ${SOURCES})
|
||||
target_include_directories(pico_fido PUBLIC ${INCLUDES})
|
||||
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-Wall
|
||||
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/"
|
||||
)
|
||||
|
||||
string(FIND ${CMAKE_C_COMPILER} ":" COMPILER_COLON)
|
||||
if (${COMPILER_COLON} GREATER_EQUAL 0)
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-Wno-error=use-after-free
|
||||
)
|
||||
endif()
|
||||
endif(NOT MSVC)
|
||||
|
||||
if(ENABLE_EMULATION)
|
||||
if(NOT MSVC)
|
||||
target_compile_options(pico_fido PUBLIC
|
||||
-fdata-sections
|
||||
-ffunction-sections
|
||||
string(FIND ${CMAKE_C_COMPILER} ":" COMPILER_COLON)
|
||||
if(${COMPILER_COLON} GREATER_EQUAL 0)
|
||||
target_compile_options(pico_fido PRIVATE
|
||||
-Wno-error=use-after-free
|
||||
)
|
||||
endif(NOT MSVC)
|
||||
if(APPLE)
|
||||
target_link_options(pico_fido PUBLIC
|
||||
-Wl,-dead_strip
|
||||
)
|
||||
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)
|
||||
endif()
|
||||
else()
|
||||
target_link_options(pico_fido PUBLIC
|
||||
-Wl,--gc-sections
|
||||
endif()
|
||||
|
||||
if(ENABLE_EMULATION)
|
||||
if(NOT MSVC)
|
||||
set(EMULATION_NON_MSVC_COMPILE_OPTIONS
|
||||
-fdata-sections
|
||||
-ffunction-sections
|
||||
)
|
||||
endif (APPLE)
|
||||
target_link_libraries(pico_fido PRIVATE pthread m)
|
||||
else()
|
||||
target_link_libraries(pico_fido PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id pico_aon_timer tinyusb_device tinyusb_board)
|
||||
pico_add_extra_outputs(${CMAKE_PROJECT_NAME})
|
||||
endif()
|
||||
target_compile_options(pico_fido PRIVATE ${EMULATION_NON_MSVC_COMPILE_OPTIONS})
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(EMULATION_APPLE_LINK_OPTIONS
|
||||
-Wl,-dead_strip
|
||||
)
|
||||
target_link_options(pico_fido PRIVATE ${EMULATION_APPLE_LINK_OPTIONS})
|
||||
|
||||
if(DEBUG_APDU)
|
||||
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()
|
||||
set(EMULATION_NON_APPLE_LINK_OPTIONS
|
||||
-Wl,--gc-sections
|
||||
)
|
||||
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
|
||||
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()
|
||||
|
||||
11
README.md
11
README.md
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Submodule pico-keys-sdk updated: d0dea3d0c5...13d2e84595
@@ -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)
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
214
src/fido/fido.c
214
src/fido/fido.c
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
308
src/fido/oath.c
308
src/fido/oath.c
@@ -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);
|
||||
}
|
||||
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_otp_pin)) {
|
||||
const uint8_t *pin_data = file_get_data(ef_otp_pin);
|
||||
res_APDU[res_APDU_size++] = TAG_PIN_COUNTER;
|
||||
res_APDU[res_APDU_size++] = TAG_ALGO;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = *pin_data;
|
||||
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = TAG_ALGO;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
|
||||
if (is_nk) {
|
||||
res_APDU[res_APDU_size++] = TAG_SERIAL_NUMBER;
|
||||
res_APDU[res_APDU_size++] = 8;
|
||||
memcpy(res_APDU + res_APDU_size, pico_serial_str, 8);
|
||||
res_APDU_size += 8;
|
||||
file_t *ef_otp_pin = 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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
222
src/fido/otp.c
222
src/fido/otp.c
@@ -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,11 +691,11 @@ 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,
|
||||
uint8_t report_id,
|
||||
hid_report_type_t report_type,
|
||||
uint8_t *buffer,
|
||||
uint16_t reqlen) {
|
||||
static uint16_t otp_hid_get_report_cb(uint8_t itf,
|
||||
uint8_t report_id,
|
||||
hid_report_type_t report_type,
|
||||
uint8_t *buffer,
|
||||
uint16_t reqlen) {
|
||||
// TODO not Implemented
|
||||
(void) itf;
|
||||
(void) report_id;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 /
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
@@ -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
@@ -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 ..
|
||||
|
||||
Reference in New Issue
Block a user