Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3969fd5136 | ||
|
|
01b197d8ec | ||
|
|
8f7b52a387 | ||
|
|
565ceb7dc4 | ||
|
|
b7590b12d1 | ||
|
|
d8da775218 | ||
|
|
13c7ade20d | ||
|
|
d925e89127 | ||
|
|
7a1131cb1a | ||
|
|
d169f001b6 | ||
|
|
250de29c3c | ||
|
|
7c4a020dc1 | ||
|
|
88063d5d6d | ||
|
|
f43bc9701f | ||
|
|
353d782970 | ||
|
|
cdd2f486aa | ||
|
|
a381e94dda | ||
|
|
e78ec82435 | ||
|
|
584d2f3b33 | ||
|
|
18676990cb | ||
|
|
ed9c46ded0 | ||
|
|
6265992162 | ||
|
|
63b7b9b8d2 | ||
|
|
8db06bf3ac | ||
|
|
77dd1c4b98 | ||
|
|
6a67800057 | ||
|
|
a70e259a90 | ||
|
|
7800056597 | ||
|
|
eeecf513cb | ||
|
|
9b0b584c14 | ||
|
|
1c45685926 | ||
|
|
cff544b485 | ||
|
|
1f805b1df2 | ||
|
|
1d20321d69 | ||
|
|
b42a664ac6 | ||
|
|
2d356a315e | ||
|
|
9bfbc45f84 | ||
|
|
a5a0f3508c | ||
|
|
9c9074c1ef | ||
|
|
022503fdc0 | ||
|
|
dba805dc04 | ||
|
|
bbf474811b | ||
|
|
2eca08161d | ||
|
|
46ada2c1f7 | ||
|
|
5faab169a8 | ||
|
|
3148649f86 | ||
|
|
3c40706aae | ||
|
|
4a64c11740 | ||
|
|
2319abe44e | ||
|
|
a5fe9b5d47 | ||
|
|
d5af2cd8ed | ||
|
|
e994078790 | ||
|
|
d99bcc90ec | ||
|
|
7a59b51849 | ||
|
|
10c58b4be7 | ||
|
|
730e76af75 | ||
|
|
ee80462a4a | ||
|
|
4ecb325e07 | ||
|
|
646b423fe4 | ||
|
|
77c3568885 | ||
|
|
3b43c5112b | ||
|
|
244c18fb51 | ||
|
|
78604f820d | ||
|
|
a68fbd65e9 | ||
|
|
bc0e022d85 | ||
|
|
3fad6baf89 | ||
|
|
df2977e6ad | ||
|
|
1fbf3da4f5 | ||
|
|
4ce6b2df5c | ||
|
|
e5910b1cba | ||
|
|
0df1330f92 | ||
|
|
3ce8496faa | ||
|
|
ef49560d0a | ||
|
|
53ed3a46c4 | ||
|
|
dc07653ae7 | ||
|
|
2d09a5c8e5 | ||
|
|
720c2e45f3 | ||
|
|
aeea3c7183 | ||
|
|
8838ac9e54 | ||
|
|
623db840d3 | ||
|
|
e2b06b908e | ||
|
|
b9e791ca90 | ||
|
|
ed560f10a4 | ||
|
|
1f839c5f99 | ||
|
|
effb8e4063 | ||
|
|
b2e45b0f7f | ||
|
|
24521dff4b | ||
|
|
7bc4a70319 | ||
|
|
cbef14beec | ||
|
|
0e54998d58 | ||
|
|
2e16036bb5 | ||
|
|
f98df743f9 | ||
|
|
4fe1c0804c | ||
|
|
7071949a1f | ||
|
|
e07b5194e3 | ||
|
|
e05115ffac | ||
|
|
38eca2fdd4 | ||
|
|
f276e99342 | ||
|
|
6f517e8fca | ||
|
|
39e2ff40c3 | ||
|
|
ffbe3fcbad | ||
|
|
cf5dbc9ae5 | ||
|
|
2fca44540a | ||
|
|
ec612a451d | ||
|
|
c43006f8c2 |
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
@@ -19,6 +19,7 @@ on:
|
||||
branches: [ "main", "development" ]
|
||||
schedule:
|
||||
- cron: '23 5 * * 4'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -35,6 +36,7 @@ jobs:
|
||||
language: [ 'cpp', 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
mode: [ 'pico', 'esp32', 'local' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -42,7 +44,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -66,7 +68,7 @@ jobs:
|
||||
|
||||
- run: |
|
||||
echo "Run, Build Application using script"
|
||||
./workflows/autobuild.sh
|
||||
./workflows/autobuild.sh ${{ matrix.mode }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
35
.github/workflows/nightly.yml
vendored
Normal file
35
.github/workflows/nightly.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: "Nightly deploy"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
nightly:
|
||||
name: Deploy nightly
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
refs: [main, development]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ matrix.refs }}
|
||||
submodules: 'recursive'
|
||||
- name : Build
|
||||
env:
|
||||
PICO_SDK_PATH: ../pico-sdk
|
||||
run: |
|
||||
./workflows/autobuild.sh pico
|
||||
./build_pico_fido.sh
|
||||
./workflows/autobuild.sh esp32
|
||||
- name: Update nightly release
|
||||
uses: pyTooling/Actions/releaser@main
|
||||
with:
|
||||
tag: nightly-${{ matrix.refs }}
|
||||
rm: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: release/*.*
|
||||
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
@@ -19,6 +19,7 @@ on:
|
||||
branches: [ "main", "development" ]
|
||||
schedule:
|
||||
- cron: '23 5 * * 4'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -18,12 +18,15 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
if(ESP_PLATFORM)
|
||||
set(DEBUG_APDU 1)
|
||||
set(DENABLE_POWER_ON_RESET 0)
|
||||
set(EXTRA_COMPONENT_DIRS src pico-keys-sdk/src)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
else()
|
||||
|
||||
if(ENABLE_EMULATION)
|
||||
else()
|
||||
set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1)
|
||||
include(pico_sdk_import.cmake)
|
||||
endif()
|
||||
|
||||
@@ -39,14 +42,6 @@ endif()
|
||||
|
||||
add_executable(pico_fido)
|
||||
endif()
|
||||
option(ENABLE_UP_BUTTON "Enable/disable user presence button" ON)
|
||||
if(ENABLE_UP_BUTTON)
|
||||
add_definitions(-DENABLE_UP_BUTTON=1)
|
||||
message(STATUS "User presence with button: \t enabled")
|
||||
else()
|
||||
add_definitions(-DENABLE_UP_BUTTON=0)
|
||||
message(STATUS "User presence with button: \t disabled")
|
||||
endif(ENABLE_UP_BUTTON)
|
||||
|
||||
option(ENABLE_POWER_ON_RESET "Enable/disable power cycle on reset" ON)
|
||||
if(ENABLE_POWER_ON_RESET)
|
||||
@@ -77,6 +72,7 @@ endif(ENABLE_OTP_APP)
|
||||
|
||||
if(ENABLE_OTP_APP OR ENABLE_OATH_APP)
|
||||
set(USB_ITF_CCID 1)
|
||||
set(USB_ITF_WCID 1)
|
||||
else()
|
||||
set(USB_ITF_CCID 0)
|
||||
endif()
|
||||
@@ -84,6 +80,7 @@ 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
|
||||
@@ -115,6 +112,7 @@ endif()
|
||||
|
||||
set(USB_ITF_HID 1)
|
||||
include(pico-keys-sdk/pico_keys_sdk_import.cmake)
|
||||
SET_VERSION(ver_major ver_minor "${CMAKE_CURRENT_LIST_DIR}/src/fido/version.h" 1)
|
||||
if(ESP_PLATFORM)
|
||||
project(pico_fido)
|
||||
endif()
|
||||
@@ -160,5 +158,6 @@ if(ENABLE_EMULATION)
|
||||
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()
|
||||
endif()
|
||||
|
||||
51
README.md
51
README.md
@@ -1,5 +1,5 @@
|
||||
# Pico FIDO
|
||||
This project transforms your Raspberry Pi Pico into an integrated FIDO Passkey, functioning like a standard USB Passkey for authentication.
|
||||
This project transforms your Raspberry Pi Pico or ESP32 microcontroller into an integrated FIDO Passkey, functioning like a standard USB Passkey for authentication.
|
||||
|
||||
## Features
|
||||
Pico FIDO includes the following features:
|
||||
@@ -30,51 +30,68 @@ Pico FIDO includes the following features:
|
||||
- Large blobs support (2048 bytes max)
|
||||
- OATH (based on YKOATH protocol specification)
|
||||
- TOTP / HOTP
|
||||
- Yubikey OTP
|
||||
- Yubikey One Time Password
|
||||
- Challenge-response generation
|
||||
- Emulated keyboard interface
|
||||
- Button press generates an OTP that is directly typed
|
||||
- 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.
|
||||
|
||||
All features comply with the specifications. If you encounter unexpected behavior or deviations from the specifications, please open an issue.
|
||||
|
||||
## Security Considerations
|
||||
Microcontrollers RP2350 and ESP32-S3 are designed to support secure environments when Secure Boot is enabled, and optionally, Secure Lock. These features allow a master key encryption key (MKEK) to be stored in a one-time programmable (OTP) memory region, which is inaccessible from outside secure code. This master key is then used to encrypt all private and secret keys on the device, protecting sensitive data from potential flash memory dumps.
|
||||
|
||||
Pico FIDO is an open platform, so exercise caution. The flash memory contents can be easily dumped, potentially revealing private/master keys. It is not feasible to encrypt the content, meaning at least one key (the master key) must be stored in clear text.
|
||||
|
||||
If the Pico is stolen, the private and secret keys can be accessed.
|
||||
**However**, the RP2040 microcontroller lacks this level of security hardware, meaning that it cannot provide the same protection. Data stored on its flash memory, including private or master keys, can be easily accessed or dumped, as encryption of the master key itself is not feasible. Consequently, if an RP2040 device is stolen, any stored private or secret keys may be exposed.
|
||||
|
||||
## Download
|
||||
Please visit the [Release page](https://github.com/polhenarejos/pico-fido/releases "Release page") to download the UF2 file for your board.
|
||||
**If you own an ESP32-S3 board, go to [ESP32 Flasher](https://www.picokeys.com/esp32-flasher/) for flashing your Pico FIDO.**
|
||||
|
||||
Note that UF2 files are shipped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with OpenSC or similar software, you will need to modify the Info.plist of the CCID driver to add these VID/PID values or use the [Pico Patcher tool](https://www.picokeys.com/pico-patcher/).
|
||||
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.
|
||||
|
||||
Alternatively, you can use the legacy VID/PID patcher with the following command:
|
||||
```sh
|
||||
./patch_vidpid.sh VID:PID input_hsm_file.uf2 output_hsm_file.uf2
|
||||
```
|
||||
You can use any VID/PID (e.g., 234b:0000 from FISJ), but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
|
||||
Note that 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").
|
||||
|
||||
For ease of use, the pure-browser option [Pico Patcher tool](https://www.picokeys.com/pico-patcher/) is highly recommended.
|
||||
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.
|
||||
|
||||
## Build
|
||||
Note that the pure-browser option [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner") 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.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/polhenarejos/pico-fido
|
||||
git submodule update --init --recursive
|
||||
cd pico-fido
|
||||
mkdir build
|
||||
cd build
|
||||
PICO_SDK_PATH=/path/to/pico-sdk cmake .. -DPICO_BOARD=board_type -DUSB_VID=0x1234 -DUSB_PID=0x5678
|
||||
make
|
||||
```
|
||||
Note that `PICO_BOARD`, `USB_VID` and `USB_PID` are optional. If not provided, `pico` board and VID/PID `FEFF:FCFD` will be used.
|
||||
|
||||
Note that `PICO_BOARD`, `USB_VID`, and `USB_PID` are optional. If not provided, the default Pico board and VID/PID `FEFF:FCFD` will be used.
|
||||
Additionally, you can pass the `VIDPID=value` parameter to build the firmware with a known VID/PID. The supported values are:
|
||||
|
||||
After `make` finishes, the binary file `pico_fido.uf2` will be generated. Put your Pico board into loading mode by holding the BOOTSEL button while plugging it in, then copy the UF2 file to the new USB mass storage Pico device. Once copied, the Pico mass storage will disconnect automatically, and the Pico board will reset with the new firmware. A blinking LED will indicate that the device is ready to work.
|
||||
- `NitroHSM`
|
||||
- `NitroFIDO2`
|
||||
- `NitroStart`
|
||||
- `NitroPro`
|
||||
- `Nitro3`
|
||||
- `Yubikey5`
|
||||
- `YubikeyNeo`
|
||||
- `YubiHSM`
|
||||
- `Gnuk`
|
||||
- `GnuPG`
|
||||
|
||||
**Remark:** Pico FIDO uses the HID interface, so VID/PID values are irrelevant in terms of operativity. You can safely use any arbitrary values or the default ones. They are only necessary in case you need to use 3rd-party tools from other vendors.
|
||||
After running `make`, the binary file `pico_fido.uf2` will be generated. To load this onto your Pico board:
|
||||
|
||||
1. Put the Pico board into loading mode by holding the `BOOTSEL` button while plugging it in.
|
||||
2. Copy the `pico_fido.uf2` file to the new USB mass storage device that appears.
|
||||
3. Once the file is copied, the Pico mass storage device will automatically disconnect, and the Pico board will reset with the new firmware.
|
||||
4. A blinking LED will indicate that the device is ready to work.
|
||||
|
||||
## Led blink
|
||||
Pico FIDO uses the led to indicate the current status. Four states are available:
|
||||
|
||||
@@ -1,103 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION_MAJOR="5"
|
||||
VERSION_MINOR="12"
|
||||
VERSION_MAJOR="6"
|
||||
VERSION_MINOR="4"
|
||||
SUFFIX="${VERSION_MAJOR}.${VERSION_MINOR}"
|
||||
#if ! [[ -z "${GITHUB_SHA}" ]]; then
|
||||
# SUFFIX="${SUFFIX}.${GITHUB_SHA}"
|
||||
#fi
|
||||
|
||||
rm -rf release/*
|
||||
mkdir -p build_release
|
||||
mkdir -p release
|
||||
cd build_release
|
||||
|
||||
for board in 0xcb_helios \
|
||||
adafruit_feather_rp2040_usb_host \
|
||||
adafruit_feather_rp2040 \
|
||||
adafruit_itsybitsy_rp2040 \
|
||||
adafruit_kb2040 \
|
||||
adafruit_macropad_rp2040 \
|
||||
adafruit_qtpy_rp2040 \
|
||||
adafruit_trinkey_qt2040 \
|
||||
amethyst_fpga \
|
||||
archi \
|
||||
arduino_nano_rp2040_connect \
|
||||
cytron_maker_pi_rp2040 \
|
||||
datanoisetv_rp2040_dsp \
|
||||
eetree_gamekit_rp2040 \
|
||||
garatronic_pybstick26_rp2040 \
|
||||
gen4_rp2350_24 \
|
||||
gen4_rp2350_24ct \
|
||||
gen4_rp2350_24t \
|
||||
gen4_rp2350_28 \
|
||||
gen4_rp2350_28ct \
|
||||
gen4_rp2350_28t \
|
||||
gen4_rp2350_32 \
|
||||
gen4_rp2350_32ct \
|
||||
gen4_rp2350_32t \
|
||||
gen4_rp2350_35 \
|
||||
gen4_rp2350_35ct \
|
||||
gen4_rp2350_35t \
|
||||
hellbender_2350A_devboard \
|
||||
ilabs_challenger_rp2350_bconnect \
|
||||
ilabs_challenger_rp2350_wifi_ble \
|
||||
ilabs_opendec02 \
|
||||
melopero_perpetuo_rp2350_lora \
|
||||
melopero_shake_rp2040 \
|
||||
metrotech_xerxes_rp2040 \
|
||||
net8086_usb_interposer \
|
||||
nullbits_bit_c_pro \
|
||||
phyx_rick_tny_rp2350 \
|
||||
pi-plates_micropi \
|
||||
pico \
|
||||
pico_w \
|
||||
pico2 \
|
||||
pimoroni_badger2040 \
|
||||
pimoroni_interstate75 \
|
||||
pimoroni_keybow2040 \
|
||||
pimoroni_motor2040 \
|
||||
pimoroni_pga2040 \
|
||||
pimoroni_pga2350 \
|
||||
pimoroni_pico_plus2_rp2350 \
|
||||
pimoroni_picolipo_4mb \
|
||||
pimoroni_picolipo_16mb \
|
||||
pimoroni_picosystem \
|
||||
pimoroni_plasma2040 \
|
||||
pimoroni_plasma2350 \
|
||||
pimoroni_servo2040 \
|
||||
pimoroni_tiny2040 \
|
||||
pimoroni_tiny2040_2mb \
|
||||
pimoroni_tiny2350 \
|
||||
pololu_3pi_2040_robot \
|
||||
pololu_zumo_2040_robot \
|
||||
seeed_xiao_rp2040 \
|
||||
seeed_xiao_rp2350 \
|
||||
solderparty_rp2040_stamp \
|
||||
solderparty_rp2040_stamp_carrier \
|
||||
solderparty_rp2040_stamp_round_carrier \
|
||||
solderparty_rp2350_stamp_xl \
|
||||
solderparty_rp2350_stamp \
|
||||
sparkfun_micromod \
|
||||
sparkfun_promicro \
|
||||
sparkfun_promicro_rp2350 \
|
||||
sparkfun_thingplus \
|
||||
switchscience_picossci2_conta_base \
|
||||
switchscience_picossci2_dev_board \
|
||||
switchscience_picossci2_micro \
|
||||
switchscience_picossci2_rp2350_breakout \
|
||||
switchscience_picossci2_tiny \
|
||||
tinycircuits_thumby_color_rp2350 \
|
||||
vgaboard \
|
||||
waveshare_rp2040_lcd_0.96 \
|
||||
waveshare_rp2040_lcd_1.28 \
|
||||
waveshare_rp2040_one \
|
||||
waveshare_rp2040_plus_4mb \
|
||||
waveshare_rp2040_plus_16mb \
|
||||
waveshare_rp2040_zero \
|
||||
weact_studio_rp2040_2mb \
|
||||
weact_studio_rp2040_4mb \
|
||||
weact_studio_rp2040_8mb \
|
||||
weact_studio_rp2040_16mb \
|
||||
wiznet_w5100s_evb_pico
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH:-../../pico-sdk}"
|
||||
board_dir=${PICO_SDK_PATH}/src/boards/include/boards
|
||||
for board in "$board_dir"/*
|
||||
do
|
||||
board_name="$(basename -- $board .h)"
|
||||
rm -rf *
|
||||
PICO_SDK_PATH=../../pico-sdk cmake .. -DPICO_BOARD=$board
|
||||
make -kj20
|
||||
mv pico_fido.uf2 ../release/pico_fido_$board-$VERSION_MAJOR.$VERSION_MINOR.uf2
|
||||
|
||||
PICO_SDK_PATH="${PICO_SDK_PATH}" cmake .. -DPICO_BOARD=$board_name
|
||||
make -j`nproc`
|
||||
mv pico_fido.uf2 ../release/pico_fido_$board_name-$SUFFIX.uf2
|
||||
done
|
||||
|
||||
Submodule pico-keys-sdk updated: 9f65a2cfa0...6e6b524878
@@ -18,9 +18,20 @@ if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_P
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
|
||||
set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
|
||||
endif ()
|
||||
|
||||
if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
|
||||
set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
|
||||
message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
|
||||
endif()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT)
|
||||
@@ -29,11 +40,22 @@ if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
# GIT_SUBMODULES_RECURSE was added in 3.17
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
|
||||
GIT_SUBMODULES_RECURSE FALSE
|
||||
)
|
||||
else ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading Raspberry Pi Pico SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
IGNORE_UNKNOWN_FILES_FOR_MANAGED_COMPONENTS=1
|
||||
|
||||
CONFIG_TINYUSB=y
|
||||
CONFIG_TINYUSB_TASK_STACK_SIZE=16384
|
||||
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="pico-keys-sdk/config/esp32/partitions.csv"
|
||||
@@ -11,6 +12,7 @@ CONFIG_PARTITION_TABLE_FILENAME="pico-keys-sdk/config/esp32/partitions.csv"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_WL_SECTOR_SIZE_512=y
|
||||
CONFIG_WL_SECTOR_MODE_PERF=y
|
||||
COMPILER_OPTIMIZATION="Performance"
|
||||
|
||||
CONFIG_MBEDTLS_CMAC_C=y
|
||||
CONFIG_MBEDTLS_CHACHA20_C=y
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pico_keys.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
|
||||
#include "pico/stdlib.h"
|
||||
#endif
|
||||
@@ -119,8 +120,13 @@ void cbor_thread(void) {
|
||||
DEBUG_DATA(res_APDU + 1, res_APDU_size);
|
||||
}
|
||||
else {
|
||||
res_APDU[0] = apdu.sw;
|
||||
//apdu.sw = 0;
|
||||
if (apdu.sw >= CTAP1_ERR_INVALID_CHANNEL) {
|
||||
res_APDU[-1] = apdu.sw;
|
||||
apdu.sw = 0;
|
||||
}
|
||||
else {
|
||||
res_APDU[0] = apdu.sw;
|
||||
}
|
||||
}
|
||||
|
||||
finished_data_size = res_APDU_size + 1;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "crypto_utils.h"
|
||||
#include "pico_keys.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;
|
||||
@@ -279,6 +280,21 @@ int pinUvAuthTokenUsageTimerObserver() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_mkek_encrypted(const uint8_t *dhash) {
|
||||
if (file_get_size(ef_mkek) == MKEK_IV_SIZE + MKEK_KEY_SIZE) {
|
||||
hash_multi(dhash, 16, session_pin); // Only for storing MKEK
|
||||
uint8_t mkek[MKEK_SIZE] = {0};
|
||||
memcpy(mkek, file_get_data(ef_mkek), MKEK_IV_SIZE + MKEK_KEY_SIZE);
|
||||
int ret = store_mkek(mkek);
|
||||
mbedtls_platform_zeroize(mkek, sizeof(mkek));
|
||||
mbedtls_platform_zeroize(session_pin, sizeof(session_pin));
|
||||
if (ret != PICOKEY_OK) {
|
||||
return CTAP2_ERR_PIN_AUTH_INVALID;
|
||||
}
|
||||
}
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
uint8_t new_pin_mismatches = 0;
|
||||
|
||||
int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
@@ -415,12 +431,20 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
if (pin_len < minPin) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
uint8_t hsh[34];
|
||||
uint8_t hsh[34], dhash[32];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
hsh[1] = pin_len;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 2);
|
||||
file_put_data(ef_pin, hsh, 2 + 16);
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash);
|
||||
double_hash_pin(dhash, 16, hsh + 2);
|
||||
file_put_data(ef_pin, hsh, 2 + 32);
|
||||
low_flash_available();
|
||||
|
||||
ret = check_mkek_encrypted(dhash);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
mbedtls_platform_zeroize(hsh, sizeof(hsh));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
goto err; //No return
|
||||
}
|
||||
else if (subcommand == 0x4) { //changePIN
|
||||
@@ -462,8 +486,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
uint8_t pin_data[34];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 34);
|
||||
pin_data[0] -= 1;
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
@@ -474,7 +498,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin) + 2, 16) != 0) {
|
||||
uint8_t dhash[32];
|
||||
double_hash_pin(paddedNewPin, 16, dhash);
|
||||
if (memcmp(dhash, file_get_data(ef_pin) + 2, 32) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
@@ -488,6 +514,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
}
|
||||
hash_multi(paddedNewPin, 16, session_pin);
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
@@ -515,12 +542,33 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
uint8_t hsh[34];
|
||||
hsh[0] = MAX_PIN_RETRIES;
|
||||
hsh[1] = pin_len;
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 2);
|
||||
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, dhash);
|
||||
double_hash_pin(dhash, 16, hsh + 2);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1 &&
|
||||
memcmp(hsh + 2, file_get_data(ef_pin) + 2, 16) == 0) {
|
||||
memcmp(hsh + 2, file_get_data(ef_pin) + 2, 32) == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||
}
|
||||
file_put_data(ef_pin, hsh, 2 + 16);
|
||||
|
||||
uint8_t mkek[MKEK_SIZE] = {0};
|
||||
ret = load_mkek(mkek);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
file_put_data(ef_pin, hsh, 2 + 32);
|
||||
|
||||
ret = check_mkek_encrypted(dhash);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
|
||||
hash_multi(dhash, 16, session_pin);
|
||||
ret = store_mkek(mkek);
|
||||
mbedtls_platform_zeroize(mkek, sizeof(mkek));
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
mbedtls_platform_zeroize(hsh, sizeof(hsh));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
uint8_t *tmpf = (uint8_t *) calloc(1, file_get_size(ef_minpin));
|
||||
memcpy(tmpf, file_get_data(ef_minpin), file_get_size(ef_minpin));
|
||||
@@ -570,8 +618,8 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
uint8_t pin_data[18];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
uint8_t pin_data[34];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 34);
|
||||
pin_data[0] -= 1;
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
low_flash_available();
|
||||
@@ -582,7 +630,9 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
|
||||
}
|
||||
if (memcmp(paddedNewPin, file_get_data(ef_pin) + 2, 16) != 0) {
|
||||
uint8_t dhash[32];
|
||||
double_hash_pin(paddedNewPin, 16, dhash);
|
||||
if (memcmp(dhash, file_get_data(ef_pin) + 2, 32) != 0) {
|
||||
regenerate();
|
||||
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
|
||||
if (retries == 0) {
|
||||
@@ -596,9 +646,19 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
|
||||
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
ret = check_mkek_encrypted(paddedNewPin);
|
||||
if (ret != PICOKEY_OK) {
|
||||
CBOR_ERROR(ret);
|
||||
}
|
||||
|
||||
hash_multi(paddedNewPin, 16, session_pin);
|
||||
pin_data[0] = MAX_PIN_RETRIES;
|
||||
new_pin_mismatches = 0;
|
||||
file_put_data(ef_pin, pin_data, sizeof(pin_data));
|
||||
mbedtls_platform_zeroize(pin_data, sizeof(pin_data));
|
||||
mbedtls_platform_zeroize(dhash, sizeof(dhash));
|
||||
|
||||
low_flash_available();
|
||||
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
|
||||
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "mbedtls/ecdh.h"
|
||||
#include "mbedtls/chachapoly.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "file.h"
|
||||
|
||||
extern uint8_t keydev_dec[32];
|
||||
extern bool has_keydev_dec;
|
||||
@@ -35,7 +36,7 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CborParser parser;
|
||||
CborValue map;
|
||||
CborError error = CborNoError;
|
||||
uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0, newMinPinLength = 0;
|
||||
uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0, newMinPinLength = 0, vendorParam = 0;
|
||||
CborByteString pinUvAuthParam = { 0 }, vendorAutCt = { 0 };
|
||||
CborCharString minPinLengthRPIDs[32] = { 0 };
|
||||
size_t resp_size = 0, raw_subpara_len = 0, minPinLengthRPIDs_len = 0;
|
||||
@@ -65,7 +66,7 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1);
|
||||
CBOR_PARSE_MAP_START(_f1, 2)
|
||||
{
|
||||
if (subcommand == 0x7f) {
|
||||
if (subcommand == 0x7f) { // Config Aut
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(vendorCommandId, 2);
|
||||
@@ -74,7 +75,7 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CBOR_FIELD_GET_BYTES(vendorAutCt, 2);
|
||||
}
|
||||
}
|
||||
else if (subcommand == 0x03) {
|
||||
else if (subcommand == 0x03) { // Extensions
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(newMinPinLength, 2);
|
||||
@@ -94,6 +95,15 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
CBOR_FIELD_GET_BOOL(forceChangePin, 2);
|
||||
}
|
||||
}
|
||||
else if (subcommand == 0x1B) { // PHY
|
||||
CBOR_FIELD_GET_UINT(subpara, 2);
|
||||
if (subpara == 0x01) {
|
||||
CBOR_FIELD_GET_UINT(vendorCommandId, 2);
|
||||
}
|
||||
else if (subpara == 0x02) {
|
||||
CBOR_FIELD_GET_UINT(vendorParam, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
CBOR_PARSE_MAP_END(_f1, 2);
|
||||
raw_subpara_len = cbor_value_get_next_byte(&_f1) - raw_subpara;
|
||||
@@ -212,6 +222,35 @@ int cbor_config(const uint8_t *data, size_t len) {
|
||||
set_opts(get_opts() | FIDO2_OPT_EA);
|
||||
goto err;
|
||||
}
|
||||
#ifndef ENABLE_EMULATION
|
||||
else if (subcommand == 0x1B) {
|
||||
if (vendorParam == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (vendorCommandId == CTAP_CONFIG_PHY_VIDPID) {
|
||||
phy_data.vid = (vendorParam >> 16) & 0xFFFF;
|
||||
phy_data.pid = vendorParam & 0xFFFF;
|
||||
phy_data.vidpid_present = true;
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_LED_GPIO) {
|
||||
phy_data.led_gpio = (uint8_t)vendorParam;
|
||||
phy_data.led_gpio_present = true;
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_LED_BTNESS) {
|
||||
phy_data.led_brightness = (uint8_t)vendorParam;
|
||||
phy_data.led_brightness_present = true;
|
||||
}
|
||||
else if (vendorCommandId == CTAP_CONFIG_PHY_OPTS) {
|
||||
phy_data.opts = (uint16_t)vendorParam;
|
||||
}
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
if (phy_save() != PICOKEY_OK) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
}
|
||||
|
||||
@@ -279,6 +279,8 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
}
|
||||
|
||||
bool silent = (up == false && uv == false);
|
||||
|
||||
if (allowList_len > 0) {
|
||||
for (size_t e = 0; e < allowList_len; e++) {
|
||||
if (allowList[e].type.present == false || allowList[e].id.present == false) {
|
||||
@@ -288,7 +290,6 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
continue;
|
||||
}
|
||||
if (credential_load(allowList[e].id.data, allowList[e].id.len, rp_id_hash, &creds[creds_len]) != 0) {
|
||||
CBOR_FREE_BYTE_STRING(allowList[e].id);
|
||||
credential_free(&creds[creds_len]);
|
||||
}
|
||||
else {
|
||||
@@ -342,15 +343,32 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
}
|
||||
}
|
||||
if (numberOfCredentials == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
if (silent && allowList_len > 0) {
|
||||
for (size_t e = 0; e < allowList_len; e++) {
|
||||
if (allowList[e].type.present == false || allowList[e].id.present == false) {
|
||||
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
|
||||
}
|
||||
if (strcmp(allowList[e].type.data, "public-key") != 0) {
|
||||
continue;
|
||||
}
|
||||
if (credential_verify(allowList[e].id.data, allowList[e].id.len, rp_id_hash, true) == 0) {
|
||||
numberOfCredentials++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numberOfCredentials == 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
Credential tmp = creds[j];
|
||||
creds[j] = creds[i];
|
||||
creds[i] = tmp;
|
||||
if (!silent) {
|
||||
for (int i = 0; i < numberOfCredentials; i++) {
|
||||
for (int j = i + 1; j < numberOfCredentials; j++) {
|
||||
if (creds[j].creation > creds[i].creation) {
|
||||
Credential tmp = creds[j];
|
||||
creds[j] = creds[i];
|
||||
creds[i] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,8 +398,8 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
|
||||
}
|
||||
|
||||
if (up == false && uv == false) {
|
||||
selcred = &creds[0];
|
||||
if (silent && !resident) {
|
||||
// Silent authentication, do nothing
|
||||
}
|
||||
else {
|
||||
selcred = &creds[0];
|
||||
@@ -410,16 +428,18 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
|
||||
int ret = 0;
|
||||
uint8_t largeBlobKey[32] = {0};
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
if (selcred) {
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
ret = credential_derive_large_blob_key(selcred->id.data, selcred->id.len, largeBlobKey);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_PROCESSING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t ext_len = 0;
|
||||
uint8_t ext[512] = {0};
|
||||
if (extensions.present == true) {
|
||||
if (selcred && extensions.present == true) {
|
||||
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
|
||||
int l = 0;
|
||||
if (options.up == pfalse) {
|
||||
@@ -519,10 +539,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++ = (ctr >> 24) & 0xFF;
|
||||
*pa++ = (ctr >> 16) & 0xFF;
|
||||
*pa++ = (ctr >> 8) & 0xFF;
|
||||
*pa++ = ctr & 0xFF;
|
||||
pa += put_uint32_t_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);
|
||||
@@ -533,32 +550,39 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
mbedtls_ecdsa_context ekey;
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
ret = fido_load_key((int)selcred->curve, selcred->id.data, &ekey);
|
||||
if (ret != 0) {
|
||||
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
}
|
||||
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
ret = mbedtls_md(md, aut_data, aut_data_len + clientDataHash.len, hash);
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
if (selcred) {
|
||||
ret = fido_load_key((int)selcred->curve, selcred->id.data, &ekey);
|
||||
if (ret != 0) {
|
||||
if (derive_key(rp_id_hash, false, selcred->id.data, MBEDTLS_ECP_DP_SECP256R1, &ekey) != 0) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
}
|
||||
if (ekey.grp.id == MBEDTLS_ECP_DP_SECP384R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
|
||||
}
|
||||
else if (ekey.grp.id == MBEDTLS_ECP_DP_SECP521R1) {
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
}
|
||||
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);
|
||||
}
|
||||
else {
|
||||
// Bogus signature
|
||||
olen = 64;
|
||||
memset(sig, 0x0B, olen);
|
||||
}
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
|
||||
uint8_t lfields = 3;
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
if (selcred && selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
lfields++;
|
||||
}
|
||||
if (numberOfCredentials > 1 && next == false) {
|
||||
lfields++;
|
||||
}
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
if (selcred && extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
lfields++;
|
||||
}
|
||||
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
|
||||
@@ -567,7 +591,12 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 2));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->id.data, selcred->id.len));
|
||||
if (selcred) {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->id.data, selcred->id.len));
|
||||
}
|
||||
else {
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, (uint8_t *)"\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01", 16));
|
||||
}
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
|
||||
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
|
||||
@@ -577,7 +606,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, sig, olen));
|
||||
|
||||
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
if (selcred && selcred->opts.present == true && selcred->opts.rk == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
|
||||
uint8_t lu = 1;
|
||||
if (numberOfCredentials > 1 && allowList_len == 0) {
|
||||
@@ -608,7 +637,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, numberOfCredentials));
|
||||
}
|
||||
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
if (selcred && extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
|
||||
}
|
||||
|
||||
@@ -129,10 +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;
|
||||
verify_data[34] = offset & 0xFF;
|
||||
verify_data[35] = (offset >> 8) & 0xFF;
|
||||
verify_data[36] = (offset >> 16) & 0xFF;
|
||||
verify_data[37] = (offset >> 24) & 0xFF;
|
||||
put_uint32_t_le(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);
|
||||
|
||||
@@ -45,7 +45,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CredExtensions extensions = { 0 };
|
||||
//options.present = true;
|
||||
//options.up = ptrue;
|
||||
//options.uv = pfalse;
|
||||
options.uv = pfalse;
|
||||
//options.rk = pfalse;
|
||||
|
||||
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
|
||||
@@ -246,7 +246,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
//else if (options.up == NULL) //5.7
|
||||
//rup = ptrue;
|
||||
}
|
||||
if (pinUvAuthParam.present == false && options.uv != ptrue && file_has_data(ef_pin)) { //8.1
|
||||
if (pinUvAuthParam.present == false && options.uv == pfalse && file_has_data(ef_pin)) { //8.1
|
||||
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
|
||||
}
|
||||
if (enterpriseAttestation > 0) {
|
||||
@@ -286,7 +286,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
if (strcmp(excludeList[e].type.data, (char *)"public-key") != 0) {
|
||||
continue;
|
||||
}
|
||||
Credential ecred;
|
||||
Credential ecred = {0};
|
||||
if (credential_load(excludeList[e].id.data, excludeList[e].id.len, rp_id_hash,
|
||||
&ecred) == 0 &&
|
||||
(ecred.extensions.credProtect != CRED_PROT_UV_REQUIRED ||
|
||||
@@ -364,8 +364,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
|
||||
if (extensions.credBlob.present == true) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder,
|
||||
extensions.credBlob.len < MAX_CREDBLOB_LENGTH));
|
||||
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, extensions.credBlob.len < MAX_CREDBLOB_LENGTH));
|
||||
}
|
||||
if (extensions.credProtect != 0) {
|
||||
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
|
||||
@@ -410,13 +409,9 @@ 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++ = (ctr >> 24) & 0xFF;
|
||||
*pa++ = (ctr >> 16) & 0xFF;
|
||||
*pa++ = (ctr >> 8) & 0xFF;
|
||||
*pa++ = ctr & 0xFF;
|
||||
pa += put_uint32_t_be(ctr, pa);
|
||||
memcpy(pa, aaguid, 16); pa += 16;
|
||||
*pa++ = ((uint16_t)cred_id_len >> 8) & 0xFF;
|
||||
*pa++ = (uint16_t)cred_id_len & 0xFF;
|
||||
pa += put_uint16_t_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;
|
||||
memcpy(pa, ext, ext_len); pa += (uint16_t)ext_len;
|
||||
@@ -440,13 +435,36 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
|
||||
if (enterpriseAttestation == 2 || (ka && ka->use_self_attestation == pfalse)) {
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
mbedtls_ecdsa_init(&ekey);
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), 32);
|
||||
uint8_t key[32] = {0};
|
||||
if (load_keydev(key) != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, key, 32);
|
||||
mbedtls_platform_zeroize(key, sizeof(key));
|
||||
md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
self_attestation = false;
|
||||
}
|
||||
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
|
||||
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, 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);
|
||||
|
||||
@@ -24,13 +24,14 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "esp_compat.h"
|
||||
#endif
|
||||
#include "fs/phy.h"
|
||||
|
||||
extern void scan_all();
|
||||
|
||||
int cbor_reset() {
|
||||
#ifndef ENABLE_EMULATION
|
||||
#if defined(ENABLE_POWER_ON_RESET) && ENABLE_POWER_ON_RESET == 1
|
||||
if (board_millis() > 10000) {
|
||||
if (!(phy_data.opts & PHY_OPT_DISABLE_POWER_RESET) && board_millis() > 10000) {
|
||||
return CTAP2_ERR_NOT_ALLOWED;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -37,14 +37,7 @@ int mse_decrypt_ct(uint8_t *data, size_t len) {
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, mse.key_enc + 12);
|
||||
int ret = mbedtls_chachapoly_auth_decrypt(&chatx,
|
||||
len - 16,
|
||||
mse.key_enc,
|
||||
mse.Qpt,
|
||||
65,
|
||||
data + len - 16,
|
||||
data,
|
||||
data);
|
||||
int ret = mbedtls_chachapoly_auth_decrypt(&chatx, len - 16, mse.key_enc, mse.Qpt, 65, data + len - 16, data, data);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
return ret;
|
||||
}
|
||||
@@ -112,8 +105,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
|
||||
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_keydev_enc),
|
||||
file_get_size(ef_keydev_enc)));
|
||||
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(ef_keydev_enc), file_get_size(ef_keydev_enc)));
|
||||
}
|
||||
else if (vendorCmd == 0x02) {
|
||||
if (vendorParam.present == false) {
|
||||
@@ -140,11 +132,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_gen, NULL);
|
||||
mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
@@ -160,37 +148,19 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
|
||||
uint8_t buf[MBEDTLS_ECP_MAX_BYTES];
|
||||
size_t olen = 0;
|
||||
ret = mbedtls_ecp_point_write_binary(&hkey.ctx.mbed_ecdh.grp,
|
||||
&hkey.ctx.mbed_ecdh.Qp,
|
||||
MBEDTLS_ECP_PF_UNCOMPRESSED,
|
||||
&olen,
|
||||
mse.Qpt,
|
||||
sizeof(mse.Qpt));
|
||||
ret = mbedtls_ecp_point_write_binary(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.Qp, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, mse.Qpt,sizeof(mse.Qpt));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
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_gen, NULL);
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
mbedtls_platform_zeroize(buf, sizeof(buf));
|
||||
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
ret = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
|
||||
NULL,
|
||||
0,
|
||||
buf,
|
||||
olen,
|
||||
mse.Qpt,
|
||||
sizeof(mse.Qpt),
|
||||
mse.key_enc,
|
||||
sizeof(mse.key_enc));
|
||||
ret = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, buf, olen, mse.Qpt, sizeof(mse.Qpt), mse.key_enc, sizeof(mse.key_enc));
|
||||
mbedtls_platform_zeroize(buf, sizeof(buf));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdh_free(&hkey);
|
||||
@@ -248,9 +218,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
}
|
||||
mbedtls_x509write_csr ctx;
|
||||
mbedtls_x509write_csr_init(&ctx);
|
||||
snprintf((char *) buffer,
|
||||
sizeof(buffer),
|
||||
"C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %s", pico_serial_str);
|
||||
snprintf((char *) buffer, sizeof(buffer), "C=ES,O=Pico Keys,OU=Authenticator Attestation,CN=Pico Fido EE Serial %s", pico_serial_str);
|
||||
mbedtls_x509write_csr_set_subject_name(&ctx, (char *) buffer);
|
||||
mbedtls_pk_context key;
|
||||
mbedtls_pk_init(&key);
|
||||
@@ -258,12 +226,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
key.pk_ctx = &ekey;
|
||||
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));
|
||||
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);
|
||||
mbedtls_ecdsa_free(&ekey);
|
||||
if (ret <= 0) {
|
||||
@@ -286,6 +249,41 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
#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 *data = file_get_data(ef_phy);
|
||||
opts = get_uint16_t_be(data + 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);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
int cmd_authenticate() {
|
||||
CTAP_AUTHENTICATE_REQ *req = (CTAP_AUTHENTICATE_REQ *) apdu.data;
|
||||
CTAP_AUTHENTICATE_RESP *resp = (CTAP_AUTHENTICATE_RESP *) res_APDU;
|
||||
//if (scan_files(true) != CCID_OK)
|
||||
//if (scan_files(true) != PICOKEY_OK)
|
||||
// return SW_EXEC_ERROR();
|
||||
if (apdu.nc < CTAP_CHAL_SIZE + CTAP_APPID_SIZE + 1 + 1) {
|
||||
return SW_WRONG_DATA();
|
||||
@@ -43,7 +43,7 @@ int cmd_authenticate() {
|
||||
int ret = 0;
|
||||
uint8_t *tmp_kh = (uint8_t *) calloc(1, req->keyHandleLen);
|
||||
memcpy(tmp_kh, req->keyHandle, req->keyHandleLen);
|
||||
if (credential_verify(tmp_kh, req->keyHandleLen, req->appId) == 0) {
|
||||
if (credential_verify(tmp_kh, req->keyHandleLen, req->appId, false) == 0) {
|
||||
ret = fido_load_key(FIDO2_CURVE_P256, req->keyHandle, &key);
|
||||
}
|
||||
else {
|
||||
@@ -55,7 +55,7 @@ int cmd_authenticate() {
|
||||
}
|
||||
}
|
||||
free(tmp_kh);
|
||||
if (ret != CCID_OK) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
@@ -66,10 +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();
|
||||
resp->ctr[0] = (ctr >> 24) & 0xFF;
|
||||
resp->ctr[1] = (ctr >> 16) & 0xFF;
|
||||
resp->ctr[2] = (ctr >> 8) & 0xFF;
|
||||
resp->ctr[3] = ctr & 0xFF;
|
||||
put_uint32_t_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));
|
||||
|
||||
@@ -37,9 +37,9 @@ int u2f_select(app_t *a, uint8_t force) {
|
||||
if (cap_supported(CAP_U2F)) {
|
||||
a->process_apdu = u2f_process_apdu;
|
||||
a->unload = u2f_unload;
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
return CCID_ERR_FILE_NOT_FOUND;
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
INITIALIZER ( u2f_ctor ) {
|
||||
@@ -47,7 +47,7 @@ INITIALIZER ( u2f_ctor ) {
|
||||
}
|
||||
|
||||
int u2f_unload() {
|
||||
return CCID_OK;
|
||||
return PICOKEY_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";
|
||||
@@ -59,7 +59,7 @@ int cmd_register() {
|
||||
CTAP_REGISTER_RESP *resp = (CTAP_REGISTER_RESP *) res_APDU;
|
||||
resp->registerId = CTAP_REGISTER_ID;
|
||||
resp->keyHandleLen = KEY_HANDLE_LEN;
|
||||
//if (scan_files(true) != CCID_OK)
|
||||
//if (scan_files(true) != PICOKEY_OK)
|
||||
// return SW_EXEC_ERROR();
|
||||
if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE) {
|
||||
return SW_WRONG_LENGTH();
|
||||
@@ -77,7 +77,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 != CCID_OK) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
@@ -100,8 +100,14 @@ int cmd_register() {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
mbedtls_ecdsa_init(&key);
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, file_get_data(ef_keydev), 32);
|
||||
if (ret != CCID_OK) {
|
||||
uint8_t key_dev[32] = {0};
|
||||
ret = load_keydev(key_dev);
|
||||
if (ret != PICOKEY_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) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
|
||||
@@ -27,22 +27,52 @@
|
||||
#include "random.h"
|
||||
#include "files.h"
|
||||
#include "pico_keys.h"
|
||||
#include "otp.h"
|
||||
|
||||
int credential_derive_chacha_key(uint8_t *outk);
|
||||
int credential_derive_chacha_key(uint8_t *outk, const uint8_t *);
|
||||
|
||||
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
|
||||
static int credential_silent_tag(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
|
||||
if (otp_key_1) {
|
||||
memcpy(outk, otp_key_1, 32);
|
||||
}
|
||||
else {
|
||||
mbedtls_sha256(pico_serial.id, PICO_UNIQUE_BOARD_ID_SIZE_BYTES, outk, 0);
|
||||
}
|
||||
return mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, cred_id, cred_id_len - CRED_SILENT_TAG_LEN, outk);
|
||||
}
|
||||
|
||||
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, bool silent) {
|
||||
if (cred_id_len < 4 + 12 + 16) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t key[32], *iv = cred_id + 4, *cipher = cred_id + 4 + 12,
|
||||
*tag = cred_id + cred_id_len - 16;
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
int ret = mbedtls_chachapoly_auth_decrypt(&chatx, cred_id_len - (4 + 12 + 16), iv, rp_id_hash, 32, tag, cipher, cipher);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
uint8_t key[32] = {0}, *iv = cred_id + CRED_PROTO_LEN, *cipher = cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
*tag = cred_id + cred_id_len - CRED_TAG_LEN;
|
||||
cred_proto_t proto = CRED_PROTO_21;
|
||||
if (memcmp(cred_id, CRED_PROTO_22_S, CRED_PROTO_LEN) == 0) { // New format
|
||||
tag = cred_id + cred_id_len - CRED_SILENT_TAG_LEN - CRED_TAG_LEN;
|
||||
proto = CRED_PROTO_22;
|
||||
}
|
||||
int ret = 0;
|
||||
if (!silent) {
|
||||
int hdr_len = CRED_PROTO_LEN + CRED_IV_LEN + CRED_TAG_LEN;
|
||||
if (proto == CRED_PROTO_22) {
|
||||
hdr_len += CRED_SILENT_TAG_LEN;
|
||||
}
|
||||
credential_derive_chacha_key(key, cred_id);
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
ret = mbedtls_chachapoly_auth_decrypt(&chatx, cred_id_len - hdr_len, iv, rp_id_hash, 32, tag, cipher, cipher);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
}
|
||||
else {
|
||||
if (proto <= CRED_PROTO_21) {
|
||||
return -1;
|
||||
}
|
||||
uint8_t outk[32];
|
||||
ret = credential_silent_tag(cred_id, cred_id_len, outk);
|
||||
ret = memcmp(outk, cred_id + cred_id_len - CRED_SILENT_TAG_LEN, CRED_SILENT_TAG_LEN);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -113,25 +143,25 @@ int credential_create(CborCharString *rpId,
|
||||
}
|
||||
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
size_t rs = cbor_encoder_get_buffer_size(&encoder, cred_id);
|
||||
*cred_id_len = 4 + 12 + rs + 16;
|
||||
uint8_t key[32];
|
||||
memset(key, 0, sizeof(key));
|
||||
credential_derive_chacha_key(key);
|
||||
uint8_t iv[12];
|
||||
*cred_id_len = CRED_PROTO_LEN + CRED_IV_LEN + 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));
|
||||
mbedtls_chachapoly_context chatx;
|
||||
mbedtls_chachapoly_init(&chatx);
|
||||
mbedtls_chachapoly_setkey(&chatx, key);
|
||||
int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32,
|
||||
cred_id + 4 + 12,
|
||||
cred_id + 4 + 12,
|
||||
cred_id + 4 + 12 + rs);
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN,
|
||||
cred_id + CRED_PROTO_LEN + CRED_IV_LEN + rs);
|
||||
mbedtls_chachapoly_free(&chatx);
|
||||
if (ret != 0) {
|
||||
CBOR_ERROR(CTAP1_ERR_OTHER);
|
||||
}
|
||||
memcpy(cred_id, CRED_PROTO, 4);
|
||||
memcpy(cred_id + 4, iv, 12);
|
||||
memcpy(cred_id, CRED_PROTO, CRED_PROTO_LEN);
|
||||
memcpy(cred_id + CRED_PROTO_LEN, iv, CRED_IV_LEN);
|
||||
credential_silent_tag(cred_id, *cred_id_len, cred_id + CRED_PROTO_LEN + CRED_IV_LEN + rs + CRED_TAG_LEN);
|
||||
|
||||
err:
|
||||
if (error != CborNoError) {
|
||||
@@ -147,8 +177,12 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
|
||||
int ret = 0;
|
||||
CborError error = CborNoError;
|
||||
uint8_t *copy_cred_id = (uint8_t *) calloc(1, cred_id_len);
|
||||
if (!cred) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
|
||||
}
|
||||
memset(cred, 0, sizeof(Credential));
|
||||
memcpy(copy_cred_id, cred_id, cred_id_len);
|
||||
ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash);
|
||||
ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash, false);
|
||||
if (ret != 0) { // U2F?
|
||||
if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0) {
|
||||
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
|
||||
@@ -236,17 +270,19 @@ err:
|
||||
}
|
||||
|
||||
void credential_free(Credential *cred) {
|
||||
CBOR_FREE_BYTE_STRING(cred->rpId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userName);
|
||||
CBOR_FREE_BYTE_STRING(cred->userDisplayName);
|
||||
CBOR_FREE_BYTE_STRING(cred->id);
|
||||
if (cred->extensions.present) {
|
||||
CBOR_FREE_BYTE_STRING(cred->extensions.credBlob);
|
||||
if (cred) {
|
||||
CBOR_FREE_BYTE_STRING(cred->rpId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userId);
|
||||
CBOR_FREE_BYTE_STRING(cred->userName);
|
||||
CBOR_FREE_BYTE_STRING(cred->userDisplayName);
|
||||
CBOR_FREE_BYTE_STRING(cred->id);
|
||||
if (cred->extensions.present) {
|
||||
CBOR_FREE_BYTE_STRING(cred->extensions.credBlob);
|
||||
}
|
||||
cred->present = false;
|
||||
cred->extensions.present = false;
|
||||
cred->opts.present = false;
|
||||
}
|
||||
cred->present = false;
|
||||
cred->extensions.present = false;
|
||||
cred->opts.present = false;
|
||||
}
|
||||
|
||||
int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
|
||||
@@ -344,13 +380,13 @@ int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
|
||||
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) CRED_PROTO, 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) cred_id, CRED_PROTO_LEN, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "hmac-secret", 11, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int credential_derive_chacha_key(uint8_t *outk) {
|
||||
int credential_derive_chacha_key(uint8_t *outk, const uint8_t *proto) {
|
||||
memset(outk, 0, 32);
|
||||
int r = 0;
|
||||
if ((r = load_keydev(outk)) != 0) {
|
||||
@@ -359,7 +395,7 @@ int credential_derive_chacha_key(uint8_t *outk) {
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) CRED_PROTO, 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) (proto ? proto : (const uint8_t *)CRED_PROTO), CRED_PROTO_LEN, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "Encryption key", 14, outk);
|
||||
return 0;
|
||||
}
|
||||
@@ -373,7 +409,7 @@ int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len,
|
||||
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
|
||||
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "SLIP-0022", 9, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) CRED_PROTO, 4, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) cred_id, CRED_PROTO_LEN, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, (uint8_t *) "largeBlobKey", 12, outk);
|
||||
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
|
||||
return 0;
|
||||
|
||||
@@ -56,9 +56,23 @@ typedef struct Credential {
|
||||
#define CRED_PROT_UV_OPTIONAL_WITH_LIST 0x02
|
||||
#define CRED_PROT_UV_REQUIRED 0x03
|
||||
|
||||
#define CRED_PROTO "\xf1\xd0\x02\x01"
|
||||
#define CRED_PROTO_21_S "\xf1\xd0\x02\x01"
|
||||
#define CRED_PROTO_22_S "\xf1\xd0\x02\x02"
|
||||
|
||||
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash);
|
||||
#define CRED_PROTO CRED_PROTO_22_S
|
||||
|
||||
#define CRED_PROTO_LEN 4
|
||||
#define CRED_IV_LEN 12
|
||||
#define CRED_TAG_LEN 16
|
||||
#define CRED_SILENT_TAG_LEN 16
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CRED_PROTO_21 = 0x01,
|
||||
CRED_PROTO_22 = 0x02,
|
||||
} cred_proto_t;
|
||||
|
||||
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, bool silent);
|
||||
extern int credential_create(CborCharString *rpId,
|
||||
CborByteString *userId,
|
||||
CborCharString *userName,
|
||||
|
||||
@@ -114,6 +114,10 @@ typedef struct {
|
||||
|
||||
#define CTAP_CONFIG_AUT_ENABLE 0x03e43f56b34285e2
|
||||
#define CTAP_CONFIG_AUT_DISABLE 0x1831a40f04a25ed9
|
||||
#define CTAP_CONFIG_PHY_VIDPID 0x6fcb19b0cbe3acfa
|
||||
#define CTAP_CONFIG_PHY_LED_GPIO 0x7b392a394de9f948
|
||||
#define CTAP_CONFIG_PHY_LED_BTNESS 0x76a85945985d02fd
|
||||
#define CTAP_CONFIG_PHY_OPTS 0x969f3b09eceb805f
|
||||
|
||||
#define CTAP_VENDOR_CBOR (CTAPHID_VENDOR_FIRST + 1)
|
||||
|
||||
@@ -121,6 +125,8 @@ 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_PERMISSION_MC 0x01 // MakeCredential
|
||||
#define CTAP_PERMISSION_GA 0x02 // GetAssertion
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "kek.h"
|
||||
#include "pico_keys.h"
|
||||
#include "apdu.h"
|
||||
#include "ctap.h"
|
||||
@@ -34,14 +35,19 @@
|
||||
#include "management.h"
|
||||
#include "hid/ctap_hid.h"
|
||||
#include "version.h"
|
||||
#include "crypto_utils.h"
|
||||
#include "otp.h"
|
||||
|
||||
int fido_process_apdu();
|
||||
int fido_unload();
|
||||
|
||||
uint8_t PICO_PRODUCT = 2; // Pico FIDO
|
||||
|
||||
pinUvAuthToken_t paut = { 0 };
|
||||
|
||||
uint8_t keydev_dec[32];
|
||||
bool has_keydev_dec = false;
|
||||
uint8_t session_pin[32] = { 0 };
|
||||
|
||||
const uint8_t fido_aid[] = {
|
||||
8,
|
||||
@@ -66,9 +72,9 @@ int fido_select(app_t *a, uint8_t force) {
|
||||
if (cap_supported(CAP_FIDO2)) {
|
||||
a->process_apdu = fido_process_apdu;
|
||||
a->unload = fido_unload;
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
return CCID_ERR_FILE_NOT_FOUND;
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
extern uint8_t (*get_version_major)();
|
||||
@@ -84,7 +90,7 @@ INITIALIZER ( fido_ctor ) {
|
||||
}
|
||||
|
||||
int fido_unload() {
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) {
|
||||
@@ -176,16 +182,24 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
|
||||
|
||||
int load_keydev(uint8_t *key) {
|
||||
if (has_keydev_dec == false && !file_has_data(ef_keydev)) {
|
||||
return CCID_ERR_MEMORY_FATAL;
|
||||
return PICOKEY_ERR_MEMORY_FATAL;
|
||||
}
|
||||
|
||||
if (has_keydev_dec == true) {
|
||||
memcpy(key, keydev_dec, sizeof(keydev_dec));
|
||||
}
|
||||
else {
|
||||
memcpy(key, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
//return mkek_decrypt(key, file_get_size(ef_keydev));
|
||||
return CCID_OK;
|
||||
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *key) {
|
||||
@@ -225,7 +239,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)) != CCID_OK) {
|
||||
if ((r = load_keydev(outk)) != PICOKEY_OK) {
|
||||
printf("Error loading keydev: %d\n", r);
|
||||
return r;
|
||||
}
|
||||
@@ -275,6 +289,7 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
|
||||
int scan_files() {
|
||||
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);
|
||||
if (ef_keydev) {
|
||||
if (!file_has_data(ef_keydev) && !file_has_data(ef_keydev_enc)) {
|
||||
printf("KEY DEVICE is empty. Generating SECP256R1 curve...");
|
||||
@@ -289,13 +304,16 @@ int scan_files() {
|
||||
uint8_t kdata[64];
|
||||
size_t key_size = 0;
|
||||
ret = mbedtls_ecp_write_key_ext(&ecdsa, &key_size, kdata, sizeof(kdata));
|
||||
if (ret != CCID_OK) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
return ret;
|
||||
}
|
||||
if (otp_key_1) {
|
||||
ret = aes_encrypt(otp_key_1, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, kdata, 32);
|
||||
}
|
||||
ret = file_put_data(ef_keydev, kdata, (uint16_t)key_size);
|
||||
mbedtls_platform_zeroize(kdata, sizeof(kdata));
|
||||
mbedtls_ecdsa_free(&ecdsa);
|
||||
if (ret != CCID_OK) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
return ret;
|
||||
}
|
||||
printf(" done!\n");
|
||||
@@ -304,13 +322,33 @@ int scan_files() {
|
||||
else {
|
||||
printf("FATAL ERROR: KEY DEV not found in memory!\r\n");
|
||||
}
|
||||
if (ef_mkek) { // No encrypted MKEK
|
||||
if (!file_has_data(ef_mkek)) {
|
||||
uint8_t mkek[MKEK_IV_SIZE + MKEK_KEY_SIZE];
|
||||
random_gen(NULL, mkek, sizeof(mkek));
|
||||
file_put_data(ef_mkek, mkek, sizeof(mkek));
|
||||
int ret = aes_encrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), file_get_data(ef_keydev), 32);
|
||||
mbedtls_platform_zeroize(mkek, sizeof(mkek));
|
||||
if (ret != 0) {
|
||||
printf("FATAL ERROR: MKEK encryption failed!\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("FATAL ERROR: MKEK not found in memory!\r\n");
|
||||
}
|
||||
ef_certdev = search_by_fid(EF_EE_DEV, NULL, SPECIFY_EF);
|
||||
if (ef_certdev) {
|
||||
if (!file_has_data(ef_certdev)) {
|
||||
uint8_t cert[2048];
|
||||
uint8_t cert[2048], outk[32];
|
||||
memset(outk, 0, sizeof(outk));
|
||||
int ret = 0;
|
||||
if ((ret = load_keydev(outk)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
mbedtls_ecdsa_context key;
|
||||
mbedtls_ecdsa_init(&key);
|
||||
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, file_get_data(ef_keydev), file_get_size(ef_keydev));
|
||||
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &key, outk, sizeof(outk));
|
||||
if (ret != 0) {
|
||||
mbedtls_ecdsa_free(&key);
|
||||
return ret;
|
||||
@@ -342,6 +380,13 @@ int scan_files() {
|
||||
printf("FATAL ERROR: Global counter not found in memory!\r\n");
|
||||
}
|
||||
ef_pin = search_by_fid(EF_PIN, NULL, SPECIFY_EF);
|
||||
if (file_get_size(ef_pin) == 18) { // Upgrade PIN storage
|
||||
uint8_t pin_data[34] = { 0 }, dhash[32];
|
||||
memcpy(pin_data, file_get_data(ef_pin), 18);
|
||||
double_hash_pin(pin_data + 2, 16, dhash);
|
||||
memcpy(pin_data + 2, dhash, 32);
|
||||
file_put_data(ef_pin, pin_data, 34);
|
||||
}
|
||||
ef_authtoken = search_by_fid(EF_AUTHTOKEN, NULL, SPECIFY_EF);
|
||||
if (ef_authtoken) {
|
||||
if (!file_has_data(ef_authtoken)) {
|
||||
@@ -359,8 +404,9 @@ int scan_files() {
|
||||
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 CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
void scan_all() {
|
||||
@@ -377,12 +423,10 @@ void init_fido() {
|
||||
bool wait_button_pressed() {
|
||||
uint32_t val = EV_PRESS_BUTTON;
|
||||
#ifndef ENABLE_EMULATION
|
||||
#if defined(ENABLE_UP_BUTTON) && ENABLE_UP_BUTTON == 1
|
||||
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);
|
||||
#endif
|
||||
#endif
|
||||
return val == EV_BUTTON_TIMEOUT;
|
||||
}
|
||||
@@ -390,21 +434,18 @@ bool wait_button_pressed() {
|
||||
uint32_t user_present_time_limit = 0;
|
||||
|
||||
bool check_user_presence() {
|
||||
#if defined(ENABLE_UP_BUTTON) && ENABLE_UP_BUTTON == 1
|
||||
if (user_present_time_limit == 0 ||
|
||||
user_present_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
if (user_present_time_limit == 0 || user_present_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
|
||||
if (wait_button_pressed() == true) { //timeout
|
||||
return false;
|
||||
}
|
||||
//user_present_time_limit = board_millis();
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t get_sign_counter() {
|
||||
uint8_t *caddr = file_get_data(ef_counter);
|
||||
return (*caddr) | (*(caddr + 1) << 8) | (*(caddr + 2) << 16) | (*(caddr + 3) << 24);
|
||||
return get_uint32_t_le(caddr);
|
||||
}
|
||||
|
||||
uint8_t get_opts() {
|
||||
|
||||
@@ -130,4 +130,6 @@ extern uint32_t user_present_time_limit;
|
||||
extern pinUvAuthToken_t paut;
|
||||
extern int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, uint16_t len, uint8_t *sign);
|
||||
|
||||
extern uint8_t session_pin[32];
|
||||
|
||||
#endif //_FIDO_H
|
||||
|
||||
@@ -18,39 +18,20 @@
|
||||
#include "files.h"
|
||||
|
||||
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_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
|
||||
{ .fid = EF_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH,
|
||||
.data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // PIN
|
||||
{ .fid = EF_AUTHTOKEN, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
|
||||
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // AUTH TOKEN
|
||||
{ .fid = EF_MINPINLEN, .parent = 0, .name = NULL,
|
||||
.type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL,
|
||||
.ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // MIN PIN LENGTH
|
||||
{ .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 = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL,
|
||||
.ef_structure = 0, .acl = { 0 } } //end
|
||||
{ .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
|
||||
{ .fid = EF_PIN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // PIN
|
||||
{ .fid = EF_AUTHTOKEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // AUTH TOKEN
|
||||
{ .fid = EF_MINPINLEN, .parent = 0, .name = NULL, .type = FILE_TYPE_INTERNAL_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = { 0xff } }, // MIN PIN LENGTH
|
||||
{ .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 = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_NOT_KNOWN, .data = NULL, .ef_structure = 0, .acl = { 0 } } //end
|
||||
};
|
||||
|
||||
const file_t *MF = &file_entries[0];
|
||||
@@ -62,3 +43,4 @@ file_t *ef_pin = NULL;
|
||||
file_t *ef_authtoken = NULL;
|
||||
file_t *ef_keydev_enc = NULL;
|
||||
file_t *ef_largeblob = NULL;
|
||||
file_t *ef_mkek = NULL;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#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
|
||||
@@ -46,5 +47,6 @@ extern file_t *ef_pin;
|
||||
extern file_t *ef_authtoken;
|
||||
extern file_t *ef_keydev_enc;
|
||||
extern file_t *ef_largeblob;
|
||||
extern file_t *ef_mkek;
|
||||
|
||||
#endif //_FILES_H_
|
||||
|
||||
137
src/fido/kek.c
Normal file
137
src/fido/kek.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "fido.h"
|
||||
#include "pico_keys.h"
|
||||
#include "stdlib.h"
|
||||
#if !defined(ENABLE_EMULATION) && !defined(ESP_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 store_mkek(const uint8_t *mkek) {
|
||||
uint8_t tmp_mkek[MKEK_SIZE];
|
||||
if (mkek == NULL) {
|
||||
const uint8_t *rd = random_bytes_get(MKEK_IV_SIZE + MKEK_KEY_SIZE);
|
||||
memcpy(tmp_mkek, rd, MKEK_IV_SIZE + MKEK_KEY_SIZE);
|
||||
}
|
||||
else {
|
||||
memcpy(tmp_mkek, mkek, MKEK_SIZE);
|
||||
}
|
||||
if (otp_key_1) {
|
||||
mkek_masked(tmp_mkek, otp_key_1);
|
||||
}
|
||||
*(uint32_t *) MKEK_CHECKSUM(tmp_mkek) = crc32c(MKEK_KEY(tmp_mkek), MKEK_KEY_SIZE);
|
||||
uint8_t tmp_mkek_pin[MKEK_SIZE];
|
||||
memcpy(tmp_mkek_pin, tmp_mkek, MKEK_SIZE);
|
||||
file_t *tf = search_file(EF_MKEK);
|
||||
if (!tf) {
|
||||
release_mkek(tmp_mkek);
|
||||
release_mkek(tmp_mkek_pin);
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
aes_encrypt_cfb_256(session_pin, MKEK_IV(tmp_mkek_pin), MKEK_KEY(tmp_mkek_pin), MKEK_KEY_SIZE + MKEK_KEY_CS_SIZE);
|
||||
file_put_data(tf, tmp_mkek_pin, MKEK_SIZE);
|
||||
release_mkek(tmp_mkek_pin);
|
||||
low_flash_available();
|
||||
release_mkek(tmp_mkek);
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
int mkek_encrypt(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_encrypt_cfb_256(MKEK_KEY(mkek), MKEK_IV(mkek), data, len);
|
||||
release_mkek(mkek);
|
||||
return r;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
46
src/fido/kek.h
Normal file
46
src/fido/kek.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -42,7 +42,7 @@ int man_select(app_t *a, uint8_t force) {
|
||||
scan_all();
|
||||
init_otp();
|
||||
}
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
INITIALIZER ( man_ctor ) {
|
||||
@@ -50,7 +50,7 @@ INITIALIZER ( man_ctor ) {
|
||||
}
|
||||
|
||||
int man_unload() {
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
bool cap_supported(uint16_t cap) {
|
||||
@@ -65,7 +65,7 @@ bool cap_supported(uint16_t cap) {
|
||||
if (tag == TAG_USB_ENABLED) {
|
||||
uint16_t ecaps = tag_data[0];
|
||||
if (tag_len == 2) {
|
||||
ecaps = (tag_data[0] << 8) | tag_data[1];
|
||||
ecaps = get_uint16_t_be(tag_data);
|
||||
}
|
||||
return ecaps & cap;
|
||||
}
|
||||
@@ -94,9 +94,6 @@ int man_get_config() {
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = TAG_NFC_SUPPORTED;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
if (!file_has_data(ef)) {
|
||||
res_APDU[res_APDU_size++] = TAG_USB_ENABLED;
|
||||
res_APDU[res_APDU_size++] = 2;
|
||||
@@ -108,9 +105,6 @@ int man_get_config() {
|
||||
res_APDU[res_APDU_size++] = TAG_CONFIG_LOCK;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
res_APDU[res_APDU_size++] = TAG_NFC_ENABLED;
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = 0x00;
|
||||
}
|
||||
else {
|
||||
memcpy(res_APDU + res_APDU_size, file_get_data(ef), file_get_size(ef));
|
||||
@@ -135,12 +129,20 @@ int cmd_write_config() {
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
extern int cbor_reset();
|
||||
int cmd_factory_reset() {
|
||||
cbor_reset();
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_READ_CONFIG 0x1D
|
||||
#define INS_WRITE_CONFIG 0x1C
|
||||
#define INS_RESET 0x1E // Reset device
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_READ_CONFIG, cmd_read_config },
|
||||
{ INS_WRITE_CONFIG, cmd_write_config },
|
||||
{ INS_RESET, cmd_factory_reset },
|
||||
{ 0x00, 0x0 }
|
||||
};
|
||||
|
||||
|
||||
@@ -100,9 +100,9 @@ int oath_select(app_t *a, uint8_t force) {
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
|
||||
apdu.ne = res_APDU_size;
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
return CCID_ERR_FILE_NOT_FOUND;
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
INITIALIZER ( oath_ctor ) {
|
||||
@@ -110,7 +110,7 @@ INITIALIZER ( oath_ctor ) {
|
||||
}
|
||||
|
||||
int oath_unload() {
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
file_t *find_oath_cred(const uint8_t *name, size_t name_len) {
|
||||
@@ -337,7 +337,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 CCID_EXEC_ERROR;
|
||||
return PICOKEY_EXEC_ERROR;
|
||||
}
|
||||
if (truncate == 0x01) {
|
||||
res_APDU[res_APDU_size++] = 4 + 1;
|
||||
@@ -354,7 +354,7 @@ 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 CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
int cmd_calculate() {
|
||||
@@ -391,19 +391,11 @@ 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 != CCID_OK) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
|
||||
uint64_t v =
|
||||
((uint64_t) chal.data[0] << 56) |
|
||||
((uint64_t) chal.data[1] << 48) |
|
||||
((uint64_t) chal.data[2] << 40) |
|
||||
((uint64_t) chal.data[3] << 32) |
|
||||
((uint64_t) chal.data[4] << 24) |
|
||||
((uint64_t) chal.data[5] << 16) |
|
||||
((uint64_t) chal.data[6] << 8) |
|
||||
(uint64_t) chal.data[7];
|
||||
uint64_t v = get_uint64_t_be(chal.data);
|
||||
size_t ef_size = file_get_size(ef);
|
||||
v++;
|
||||
uint8_t *tmp = (uint8_t *) calloc(1, ef_size);
|
||||
@@ -411,14 +403,7 @@ int cmd_calculate() {
|
||||
asn1_ctx_t ctxt;
|
||||
asn1_ctx_init(tmp, (uint16_t)ef_size, &ctxt);
|
||||
asn1_find_tag(&ctxt, TAG_IMF, &chal);
|
||||
chal.data[0] = (v >> 56) & 0xFF;
|
||||
chal.data[1] = (v >> 48) & 0xFF;
|
||||
chal.data[2] = (v >> 40) & 0xFF;
|
||||
chal.data[3] = (v >> 32) & 0xFF;
|
||||
chal.data[4] = (v >> 24) & 0xFF;
|
||||
chal.data[5] = (v >> 16) & 0xFF;
|
||||
chal.data[6] = (v >> 8) & 0xFF;
|
||||
chal.data[7] = v & 0xff;
|
||||
put_uint64_t_be(v, chal.data);
|
||||
file_put_data(ef, tmp, (uint16_t)ef_size);
|
||||
low_flash_available();
|
||||
free(tmp);
|
||||
@@ -466,7 +451,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 != CCID_OK) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
res_APDU[res_APDU_size++] = 1;
|
||||
res_APDU[res_APDU_size++] = key.data[1];
|
||||
}
|
||||
@@ -577,14 +562,14 @@ int cmd_verify_hotp() {
|
||||
return SW_INCORRECT_PARAMS();
|
||||
}
|
||||
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &code) == true) {
|
||||
code_int = (code.data[0] << 24) | (code.data[1] << 16) | (code.data[2] << 8) | code.data[3];
|
||||
code_int = get_uint32_t_be(code.data);
|
||||
}
|
||||
|
||||
int ret = calculate_oath(0x01, key.data, key.len, chal.data, chal.len);
|
||||
if (ret != CCID_OK) {
|
||||
if (ret != PICOKEY_OK) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
uint32_t res_int = (res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
|
||||
uint32_t res_int = get_uint32_t_be(res_APDU + 2);
|
||||
if (res_APDU[1] == 6) {
|
||||
res_int %= (uint32_t) 1e6;
|
||||
}
|
||||
@@ -599,10 +584,52 @@ int cmd_verify_hotp() {
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
int cmd_rename() {
|
||||
asn1_ctx_t ctxi, name = { 0 }, new_name = { 0 };
|
||||
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) {
|
||||
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) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
file_t *ef = find_oath_cred(name.data, name.len);
|
||||
if (file_has_data(ef) == false) {
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
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) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
uint8_t *new_data;
|
||||
if (new_name.len > name.len) {
|
||||
new_data = (uint8_t *) calloc(1, file_get_size(ef) + new_name.len - name.len);
|
||||
}
|
||||
else {
|
||||
new_data = (uint8_t *) calloc(1, file_get_size(ef));
|
||||
}
|
||||
memcpy(new_data, fdata, name.data - fdata);
|
||||
*(new_data + (name.data - fdata) - 1) = new_name.len;
|
||||
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();
|
||||
free(new_data);
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
#define INS_PUT 0x01
|
||||
#define INS_DELETE 0x02
|
||||
#define INS_SET_CODE 0x03
|
||||
#define INS_RESET 0x04
|
||||
#define INS_RENAME 0x05
|
||||
#define INS_LIST 0xa1
|
||||
#define INS_CALCULATE 0xa2
|
||||
#define INS_VALIDATE 0xa3
|
||||
@@ -618,6 +645,7 @@ static const cmd_t cmds[] = {
|
||||
{ INS_DELETE, cmd_delete },
|
||||
{ INS_SET_CODE, cmd_set_code },
|
||||
{ INS_RESET, cmd_reset },
|
||||
{ INS_RENAME, cmd_rename },
|
||||
{ INS_LIST, cmd_list },
|
||||
{ INS_VALIDATE, cmd_validate },
|
||||
{ INS_CALCULATE, cmd_calculate },
|
||||
|
||||
110
src/fido/otp.c
110
src/fido/otp.c
@@ -111,7 +111,7 @@ typedef struct otp_config {
|
||||
}) otp_config_t;
|
||||
|
||||
#define otp_config_size sizeof(otp_config_t)
|
||||
uint16_t otp_status();
|
||||
uint16_t otp_status(bool is_otp);
|
||||
|
||||
int otp_process_apdu();
|
||||
int otp_unload();
|
||||
@@ -140,13 +140,10 @@ int otp_select(app_t *a, uint8_t force) {
|
||||
else {
|
||||
config_seq = 0;
|
||||
}
|
||||
otp_status();
|
||||
memmove(res_APDU, res_APDU + 1, 6);
|
||||
res_APDU_size = 6;
|
||||
apdu.ne = res_APDU_size;
|
||||
return CCID_OK;
|
||||
otp_status(false);
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
return CCID_ERR_FILE_NOT_FOUND;
|
||||
return PICOKEY_ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint8_t modhex_tab[] =
|
||||
@@ -169,12 +166,11 @@ void init_otp() {
|
||||
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 = (data[otp_config_size] << 8) | data[otp_config_size + 1];
|
||||
uint16_t counter = get_uint16_t_be(data + otp_config_size);
|
||||
if (++counter <= 0x7fff) {
|
||||
uint8_t new_data[otp_config_size + 8];
|
||||
memcpy(new_data, data, sizeof(new_data));
|
||||
new_data[otp_config_size] = counter >> 8;
|
||||
new_data[otp_config_size + 1] = counter & 0xff;
|
||||
put_uint16_t_be(counter, new_data + otp_config_size);
|
||||
file_put_data(ef, new_data, sizeof(new_data));
|
||||
}
|
||||
}
|
||||
@@ -228,25 +224,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 |= (uint64_t) *p++ << 56;
|
||||
imf |= (uint64_t) *p++ << 48;
|
||||
imf |= (uint64_t) *p++ << 40;
|
||||
imf |= (uint64_t) *p++ << 32;
|
||||
imf |= *p++ << 24;
|
||||
imf |= *p++ << 16;
|
||||
imf |= *p++ << 8;
|
||||
imf |= *p++;
|
||||
imf = get_uint64_t_be(p);
|
||||
p += 8;
|
||||
if (imf == 0) {
|
||||
imf = ((otp_config->uid[4] << 8) | otp_config->uid[5]) << 4;
|
||||
imf = get_uint16_t_be(otp_config->uid + 4);
|
||||
}
|
||||
uint8_t chal[8] =
|
||||
{ imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8, imf & 0xff };
|
||||
uint8_t chal[8];
|
||||
put_uint64_t_be(imf, chal);
|
||||
res_APDU_size = 0;
|
||||
int ret = calculate_oath(1, tmp_key, sizeof(tmp_key), chal, sizeof(chal));
|
||||
if (ret == CCID_OK) {
|
||||
if (ret == PICOKEY_OK) {
|
||||
uint32_t base = otp_config->cfg_flags & OATH_HOTP8 ? 1e8 : 1e6;
|
||||
uint32_t number =
|
||||
(res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
|
||||
uint32_t number = get_uint16_t_be(res_APDU + 2);
|
||||
number %= base;
|
||||
char number_str[9];
|
||||
if (otp_config->cfg_flags & OATH_HOTP8) {
|
||||
@@ -258,9 +247,8 @@ int otp_button_pressed(uint8_t slot) {
|
||||
add_keyboard_buffer((const uint8_t *) number_str, 6, true);
|
||||
}
|
||||
imf++;
|
||||
uint8_t new_chal[8] =
|
||||
{ imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8,
|
||||
imf & 0xff };
|
||||
uint8_t new_chal[8];
|
||||
put_uint64_t_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));
|
||||
@@ -284,7 +272,7 @@ int otp_button_pressed(uint8_t slot) {
|
||||
else {
|
||||
uint8_t otpk[22], *po = otpk;
|
||||
bool update_counter = false;
|
||||
uint16_t counter = (data[otp_config_size] << 8) | data[otp_config_size + 1], crc = 0;
|
||||
uint16_t counter = get_uint16_t_be(data + otp_config_size), crc = 0;
|
||||
uint32_t ts = board_millis() / 1000;
|
||||
if (counter == 0) {
|
||||
update_counter = true;
|
||||
@@ -294,9 +282,8 @@ int otp_button_pressed(uint8_t slot) {
|
||||
po += 6;
|
||||
memcpy(po, otp_config->uid, UID_SIZE);
|
||||
po += UID_SIZE;
|
||||
*po++ = counter & 0xff;
|
||||
*po++ = counter >> 8;
|
||||
ts >>= 3;
|
||||
po += put_uint16_t_le(counter, po);
|
||||
ts >>= 1;
|
||||
*po++ = ts & 0xff;
|
||||
*po++ = ts >> 8;
|
||||
*po++ = ts >> 16;
|
||||
@@ -304,8 +291,7 @@ int otp_button_pressed(uint8_t slot) {
|
||||
random_gen(NULL, po, 2);
|
||||
po += 2;
|
||||
crc = calculate_crc(otpk + 6, 14);
|
||||
*po++ = ~crc & 0xff;
|
||||
*po++ = ~crc >> 8;
|
||||
po += put_uint16_t_le(~crc, po);
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
|
||||
@@ -326,8 +312,7 @@ 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));
|
||||
new_data[otp_config_size] = counter >> 8;
|
||||
new_data[otp_config_size + 1] = counter & 0xff;
|
||||
put_uint16_t_be(counter, new_data + otp_config_size);
|
||||
file_put_data(ef, new_data, sizeof(new_data));
|
||||
low_flash_available();
|
||||
}
|
||||
@@ -348,25 +333,35 @@ INITIALIZER( otp_ctor ) {
|
||||
}
|
||||
|
||||
int otp_unload() {
|
||||
return CCID_OK;
|
||||
return PICOKEY_OK;
|
||||
}
|
||||
|
||||
uint16_t otp_status() {
|
||||
uint16_t otp_status(bool is_otp) {
|
||||
if (scanned == false) {
|
||||
scan_all();
|
||||
scanned = true;
|
||||
}
|
||||
res_APDU_size = 0;
|
||||
res_APDU[1] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[2] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[3] = 0;
|
||||
res_APDU[4] = config_seq;
|
||||
res_APDU[5] = (CONFIG2_TOUCH | CONFIG1_TOUCH) |
|
||||
if (is_otp) {
|
||||
res_APDU_size++;
|
||||
}
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
|
||||
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
res_APDU[res_APDU_size++] = config_seq;
|
||||
res_APDU[res_APDU_size++] = (CONFIG2_TOUCH | CONFIG1_TOUCH) |
|
||||
(file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ? CONFIG1_VALID :
|
||||
0x00) |
|
||||
(file_has_data(search_dynamic_file(EF_OTP_SLOT2)) ? CONFIG2_VALID :
|
||||
0x00);
|
||||
res_APDU[6] = 0;
|
||||
res_APDU[res_APDU_size++] = 0;
|
||||
if (is_otp) {
|
||||
res_APDU_size = 0;
|
||||
}
|
||||
else {
|
||||
apdu.ne = res_APDU_size;
|
||||
}
|
||||
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
@@ -375,6 +370,7 @@ bool check_crc(const otp_config_t *data) {
|
||||
return crc == 0xF0B8;
|
||||
}
|
||||
|
||||
bool _is_otp = false;
|
||||
int cmd_otp() {
|
||||
uint8_t p1 = P1(apdu), p2 = P2(apdu);
|
||||
if (p2 != 0x00) {
|
||||
@@ -398,16 +394,13 @@ int cmd_otp() {
|
||||
file_put_data(ef, apdu.data, otp_config_size + 8);
|
||||
low_flash_available();
|
||||
config_seq++;
|
||||
return otp_status();
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
}
|
||||
// Delete slot
|
||||
delete_file(ef);
|
||||
if (!file_has_data(search_dynamic_file(EF_OTP_SLOT1)) &&
|
||||
!file_has_data(search_dynamic_file(EF_OTP_SLOT2))) {
|
||||
config_seq = 0;
|
||||
}
|
||||
return otp_status();
|
||||
config_seq++;
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
else if (p1 == 0x04 || p1 == 0x05) {
|
||||
otp_config_t *odata = (otp_config_t *) apdu.data;
|
||||
@@ -431,6 +424,7 @@ int cmd_otp() {
|
||||
file_put_data(ef, apdu.data, otp_config_size);
|
||||
low_flash_available();
|
||||
}
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
else if (p1 == 0x06) {
|
||||
uint8_t tmp[otp_config_size + 8];
|
||||
@@ -454,6 +448,7 @@ int cmd_otp() {
|
||||
delete_file(ef2);
|
||||
}
|
||||
low_flash_available();
|
||||
return otp_status(_is_otp);
|
||||
}
|
||||
else if (p1 == 0x10) {
|
||||
memcpy(res_APDU, pico_serial.id, 4);
|
||||
@@ -471,12 +466,7 @@ int cmd_otp() {
|
||||
}
|
||||
int ret = 0;
|
||||
if (p1 == 0x30 || p1 == 0x38) {
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1),
|
||||
otp_config->aes_key,
|
||||
KEY_SIZE,
|
||||
apdu.data,
|
||||
8,
|
||||
res_APDU);
|
||||
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), otp_config->aes_key, KEY_SIZE, apdu.data, 8, res_APDU);
|
||||
if (ret == 0) {
|
||||
res_APDU_size = 20;
|
||||
}
|
||||
@@ -532,9 +522,7 @@ extern uint16_t *get_send_buffer_size(uint8_t itf);
|
||||
|
||||
int otp_send_frame(uint8_t *frame, size_t frame_len) {
|
||||
uint16_t crc = calculate_crc(frame, frame_len);
|
||||
frame[frame_len] = ~crc & 0xff;
|
||||
frame[frame_len + 1] = ~crc >> 8;
|
||||
frame_len += 2;
|
||||
frame_len += put_uint16_t_le(~crc, frame + frame_len);
|
||||
*get_send_buffer_size(ITF_KEYBOARD) = frame_len;
|
||||
otp_exp_seq = (frame_len / 7);
|
||||
if (frame_len % 7) {
|
||||
@@ -567,7 +555,7 @@ int otp_hid_set_report_cb(uint8_t itf,
|
||||
memcpy(otp_frame_rx + rseq * 7, buffer, 7);
|
||||
if (rseq == 9) {
|
||||
DEBUG_DATA(otp_frame_rx, sizeof(otp_frame_rx));
|
||||
uint16_t residual_crc = calculate_crc(otp_frame_rx, 64), rcrc = (otp_frame_rx[66] << 8 | otp_frame_rx[65]);
|
||||
uint16_t residual_crc = calculate_crc(otp_frame_rx, 64), rcrc = get_uint16_t_le(otp_frame_rx + 65);
|
||||
uint8_t slot_id = otp_frame_rx[64];
|
||||
if (residual_crc == rcrc) {
|
||||
uint8_t hdr[5];
|
||||
@@ -579,10 +567,12 @@ int otp_hid_set_report_cb(uint8_t itf,
|
||||
apdu.header[1] = 0x01;
|
||||
apdu.header[2] = slot_id;
|
||||
apdu.header[3] = 0;
|
||||
_is_otp = true;
|
||||
int ret = otp_process_apdu();
|
||||
if (ret == 0x9000 && res_APDU_size > 0) {
|
||||
otp_send_frame(apdu.rdata, apdu.rlen);
|
||||
}
|
||||
_is_otp = false;
|
||||
}
|
||||
else {
|
||||
printf("[OTP] Bad CRC!\n");
|
||||
@@ -624,7 +614,7 @@ uint16_t otp_hid_get_report_cb(uint8_t itf,
|
||||
}
|
||||
else {
|
||||
res_APDU = buffer;
|
||||
otp_status();
|
||||
otp_status(true);
|
||||
}
|
||||
|
||||
return reqlen;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#ifndef __VERSION_H_
|
||||
#define __VERSION_H_
|
||||
|
||||
#define PICO_FIDO_VERSION 0x050C
|
||||
#define PICO_FIDO_VERSION 0x0604
|
||||
|
||||
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
|
||||
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
|
||||
|
||||
@@ -213,11 +213,19 @@ def test_allow_list_missing_id(device, MCRes):
|
||||
]
|
||||
)
|
||||
|
||||
def test_user_presence_option_false(device, MCRes):
|
||||
def test_silent_ok(device, MCRes):
|
||||
res = device.GA(options={"up": False}, allow_list=[
|
||||
{"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
|
||||
])
|
||||
|
||||
def test_silent_ko(device, MCRes):
|
||||
cred = MCRes['res'].attestation_object.auth_data.credential_data.credential_id + b'\x00'
|
||||
with pytest.raises(CtapError) as e:
|
||||
res = device.GA(options={"up": False}, allow_list=[
|
||||
{"id": cred, "type": "public-key"}
|
||||
])
|
||||
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
|
||||
|
||||
def test_credential_resets(device, MCRes, GARes):
|
||||
device.reset()
|
||||
with pytest.raises(CtapError) as e:
|
||||
|
||||
@@ -255,5 +255,5 @@ def test_returned_credential(device):
|
||||
device.GNA()
|
||||
|
||||
# the returned credential should have user id in it
|
||||
print(ga_res)
|
||||
assert 'id' in ga_res.user and len(ga_res.user["id"]) > 0
|
||||
#print(ga_res)
|
||||
#assert 'id' in ga_res.user and len(ga_res.user["id"]) > 0
|
||||
|
||||
@@ -77,11 +77,17 @@ class VendorConfig(Config):
|
||||
class PARAM(IntEnum):
|
||||
VENDOR_COMMAND_ID = 0x01
|
||||
VENDOR_AUT_CT = 0x02
|
||||
VENDOR_PARAM = 0x02
|
||||
|
||||
class CMD(IntEnum):
|
||||
CONFIG_AUT_ENABLE = 0x03e43f56b34285e2
|
||||
CONFIG_AUT_DISABLE = 0x1831a40f04a25ed9
|
||||
CONFIG_AUT_ENABLE = 0x03e43f56b34285e2
|
||||
CONFIG_AUT_DISABLE = 0x1831a40f04a25ed9
|
||||
CONFIG_VENDOR_PROTOTYPE = 0x7f
|
||||
CONFIG_VENDOR_PHY = 0x1b
|
||||
CONFIG_PHY_VIDPID = 0x6fcb19b0cbe3acfa
|
||||
CONFIG_PHY_OPTS = 0x969f3b09eceb805f
|
||||
CONFIG_PHY_LED_GPIO = 0x7b392a394de9f948
|
||||
CONFIG_PHY_LED_BTNESS = 0x76a85945985d02fd
|
||||
|
||||
class RESP(IntEnum):
|
||||
KEY_AGREEMENT = 0x01
|
||||
@@ -106,6 +112,42 @@ class VendorConfig(Config):
|
||||
},
|
||||
)
|
||||
|
||||
def vidpid(self, vid, pid):
|
||||
self._call(
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PHY,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_VIDPID,
|
||||
VendorConfig.PARAM.VENDOR_PARAM: (vid & 0xFFFF) << 16 | pid
|
||||
},
|
||||
)
|
||||
|
||||
def led_gpio(self, gpio):
|
||||
self._call(
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PHY,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_LED_GPIO,
|
||||
VendorConfig.PARAM.VENDOR_PARAM: gpio
|
||||
},
|
||||
)
|
||||
|
||||
def led_brightness(self, brightness):
|
||||
self._call(
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PHY,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_LED_BTNESS,
|
||||
VendorConfig.PARAM.VENDOR_PARAM: brightness
|
||||
},
|
||||
)
|
||||
|
||||
def phy_opts(self, opts):
|
||||
self._call(
|
||||
VendorConfig.CMD.CONFIG_VENDOR_PHY,
|
||||
{
|
||||
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_PHY_OPTS,
|
||||
VendorConfig.PARAM.VENDOR_PARAM: opts
|
||||
},
|
||||
)
|
||||
|
||||
class Ctap2Vendor(Ctap2):
|
||||
def __init__(self, device: CtapDevice, strict_cbor: bool = True):
|
||||
super().__init__(device=device, strict_cbor=strict_cbor)
|
||||
@@ -190,6 +232,8 @@ class Vendor:
|
||||
VENDOR_MSE = 0x02
|
||||
VENDOR_UNLOCK = 0x03
|
||||
VENDOR_EA = 0x04
|
||||
VENDOR_PHY = 0x05
|
||||
VENDOR_MEMORY = 0x06
|
||||
|
||||
@unique
|
||||
class PARAM(IntEnum):
|
||||
@@ -207,6 +251,10 @@ class Vendor:
|
||||
PARAM = 0x01
|
||||
COSE_KEY = 0x02
|
||||
|
||||
class PHY_OPTS(IntEnum):
|
||||
PHY_OPT_WCID = 0x1
|
||||
PHY_OPT_DIMM = 0x2
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ctap: Ctap2Vendor,
|
||||
@@ -393,6 +441,48 @@ class Vendor:
|
||||
}
|
||||
)
|
||||
|
||||
def vidpid(self, vid, pid):
|
||||
return self.vcfg.vidpid(vid, pid)
|
||||
|
||||
def led_gpio(self, gpio):
|
||||
return self.vcfg.led_gpio(gpio)
|
||||
|
||||
def led_brightness(self, brightness):
|
||||
if (brightness > 15):
|
||||
print('ERROR: Brightness must be between 0 and 15')
|
||||
return
|
||||
return self.vcfg.led_brightness(brightness)
|
||||
|
||||
def led_dimmable(self, onoff):
|
||||
opts = self.phy_opts()
|
||||
if (onoff):
|
||||
opts |= Vendor.PHY_OPTS.PHY_OPT_DIMM
|
||||
else:
|
||||
opts &= ~Vendor.PHY_OPTS.PHY_OPT_DIMM
|
||||
print(f'opts: {opts}')
|
||||
return self.vcfg.phy_opts(opts)
|
||||
|
||||
def wcid(self, onoff):
|
||||
opts = self.phy_opts()
|
||||
if (onoff):
|
||||
opts |= Vendor.PHY_OPTS.PHY_OPT_WCID
|
||||
else:
|
||||
opts &= ~Vendor.PHY_OPTS.PHY_OPT_WCID
|
||||
return self.vcfg.phy_opts(opts)
|
||||
|
||||
def phy_opts(self):
|
||||
return self._call(
|
||||
Vendor.CMD.VENDOR_PHY,
|
||||
Vendor.SUBCMD.ENABLE,
|
||||
)[Vendor.RESP.PARAM]
|
||||
|
||||
def memory(self):
|
||||
resp = self._call(
|
||||
Vendor.CMD.VENDOR_MEMORY,
|
||||
Vendor.SUBCMD.ENABLE,
|
||||
)
|
||||
return { 'free': resp[1], 'used': resp[2], 'total': resp[3], 'files': resp[4], 'size': resp[5] }
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
subparser = parser.add_subparsers(title="commands", dest="command")
|
||||
@@ -408,6 +498,21 @@ def parse_args():
|
||||
parser_attestation.add_argument('subcommand', choices=['csr'])
|
||||
parser_attestation.add_argument('--filename', help='Uploads the certificate filename to the device as enterprise attestation certificate. If not provided, it will generate an enterprise attestation certificate automatically.')
|
||||
|
||||
parser_phy = subparser.add_parser('phy', help='Set PHY options.')
|
||||
subparser_phy = parser_phy.add_subparsers(title='commands', dest='subcommand', required=True)
|
||||
parser_phy_vp = subparser_phy.add_parser('vidpid', help='Sets VID/PID. Use VID:PID format (e.g. 1234:5678)')
|
||||
parser_phy_vp.add_argument('value', help='Value of the PHY option.', metavar='VAL', nargs='?')
|
||||
parser_phy_ledn = subparser_phy.add_parser('led_gpio', help='Sets LED GPIO number.')
|
||||
parser_phy_ledn.add_argument('value', help='Value of the PHY option.', metavar='VAL', nargs='?')
|
||||
parser_phy_optwcid = subparser_phy.add_parser('wcid', help='Enable/Disable Web CCID interface.')
|
||||
parser_phy_optwcid.add_argument('value', choices=['enable', 'disable'], help='Enable/Disable Web CCID interface.', nargs='?')
|
||||
parser_phy_ledbtness = subparser_phy.add_parser('led_brightness', help='Sets LED max. brightness.')
|
||||
parser_phy_ledbtness.add_argument('value', help='Value of the max. brightness.', metavar='VAL', nargs='?')
|
||||
parser_phy_optdimm = subparser_phy.add_parser('led_dimmable', help='Enable/Disable LED dimming.')
|
||||
parser_phy_optdimm.add_argument('value', choices=['enable', 'disable'], help='Enable/Disable LED dimming.', nargs='?')
|
||||
|
||||
parser_mem = subparser.add_parser('memory', help='Get current memory usage.')
|
||||
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
@@ -441,8 +546,41 @@ def attestation(vdr, args):
|
||||
cert = x509.load_pem_x509_certificate(dataf)
|
||||
vdr.upload_ea(cert.public_bytes(Encoding.DER))
|
||||
|
||||
def phy(vdr, args):
|
||||
val = args.value if 'value' in args else None
|
||||
if (val):
|
||||
if (args.subcommand == 'vidpid'):
|
||||
sp = val.split(':')
|
||||
if (len(sp) != 2):
|
||||
print('ERROR: VID/PID have wrong format. Use VID:PID format (e.g. 1234:5678)')
|
||||
ret = vdr.vidpid(int(sp[0],16), int(sp[1],16))
|
||||
elif (args.subcommand == 'led_gpio'):
|
||||
val = int(val)
|
||||
ret = vdr.led_gpio(val)
|
||||
elif (args.subcommand == 'led_brightness'):
|
||||
val = int(val)
|
||||
ret = vdr.led_brightness(val)
|
||||
elif (args.subcommand == 'led_dimmable'):
|
||||
ret = vdr.led_dimmable(val == 'enable')
|
||||
elif (args.subcommand == 'wcid'):
|
||||
ret = vdr.wcid(val == 'enable')
|
||||
|
||||
if (ret):
|
||||
print(f'Current value: {hexlify(ret)}')
|
||||
else:
|
||||
print('Command executed successfully. Please, restart your Pico Key.')
|
||||
|
||||
def memory(vdr, args):
|
||||
mem = vdr.memory()
|
||||
print(f'Memory usage:')
|
||||
print(f'\tFree: {mem["free"]/1024:.2f} kilobytes ({mem["free"]*100/mem["total"]:.2f}%)')
|
||||
print(f'\tUsed: {mem["used"]/1024:.2f} kilobytes ({mem["used"]*100/mem["total"]:.2f}%)')
|
||||
print(f'\tTotal: {mem["total"]/1024:.2f} kilobytes')
|
||||
print(f'\tFlash size: {mem["size"]/1024:.2f} kilobytes')
|
||||
print(f'\tFiles: {mem["files"]}')
|
||||
|
||||
def main(args):
|
||||
print('Pico Fido Tool v1.6')
|
||||
print('Pico Fido Tool v1.10')
|
||||
print('Author: Pol Henarejos')
|
||||
print('Report bugs to https://github.com/polhenarejos/pico-fido/issues')
|
||||
print('')
|
||||
@@ -460,6 +598,10 @@ def main(args):
|
||||
backup(vdr, args)
|
||||
elif (args.command == 'attestation'):
|
||||
attestation(vdr, args)
|
||||
elif (args.command == 'phy'):
|
||||
phy(vdr, args)
|
||||
elif (args.command == 'memory'):
|
||||
memory(vdr, args)
|
||||
|
||||
def run():
|
||||
args = parse_args()
|
||||
|
||||
@@ -2,12 +2,54 @@
|
||||
|
||||
git submodule update --init --recursive
|
||||
sudo apt update
|
||||
|
||||
if [[ $1 == "pico" ]]; then
|
||||
sudo apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
|
||||
git clone https://github.com/raspberrypi/pico-sdk
|
||||
cd pico-sdk
|
||||
git checkout tags/2.1.1
|
||||
git submodule update --init
|
||||
cd ..
|
||||
git clone https://github.com/raspberrypi/picotool
|
||||
cd picotool
|
||||
git submodule update --init
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DPICO_SDK_PATH=../../pico-sdk ..
|
||||
make -j`nproc`
|
||||
sudo make install
|
||||
cd ../..
|
||||
mkdir build_pico
|
||||
cd build_pico
|
||||
cmake -DPICO_SDK_PATH=../pico-sdk ..
|
||||
make
|
||||
cd ..
|
||||
elif [[ $1 == "esp32" ]]; then
|
||||
sudo apt install -y git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
|
||||
git clone --recursive https://github.com/espressif/esp-idf.git
|
||||
cd esp-idf
|
||||
./install.sh esp32s3
|
||||
. ./export.sh
|
||||
cd ..
|
||||
idf.py set-target esp32s3
|
||||
idf.py all
|
||||
mkdir -p release
|
||||
cd build
|
||||
esptool.py --chip ESP32-S3 merge_bin -o ../release/pico_fido_esp32-s3.bin @flash_args
|
||||
cd ..
|
||||
cd esp-idf
|
||||
./install.sh esp32s2
|
||||
. ./export.sh
|
||||
cd ..
|
||||
idf.py set-target esp32s2
|
||||
idf.py all
|
||||
mkdir -p release
|
||||
cd build
|
||||
esptool.py --chip ESP32-S2 merge_bin -o ../release/pico_fido_esp32-s2.bin @flash_args
|
||||
cd ..
|
||||
else
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DENABLE_EMULATION=1 ..
|
||||
make
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user