281 Commits
v3.0 ... v6.4

Author SHA1 Message Date
Pol Henarejos
3969fd5136 Upgrade to v6.4
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 15:15:16 +01:00
Pol Henarejos
01b197d8ec Fix led driver build for Pimoroni.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 15:14:42 +01:00
Pol Henarejos
8f7b52a387 Fix rename board name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 14:34:18 +01:00
Pol Henarejos
565ceb7dc4 Take led_driver on build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 14:33:37 +01:00
Pol Henarejos
b7590b12d1 Enable fastest supported clock.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 13:36:11 +01:00
Pol Henarejos
d8da775218 Add file & line to debug info.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 11:43:56 +01:00
Pol Henarejos
13c7ade20d Add support for older PCSC.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 11:19:28 +01:00
Pol Henarejos
d925e89127 Add support for ESP32-S2 build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 11:17:58 +01:00
Pol Henarejos
7a1131cb1a Modify build script to build all supported boards.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 10:58:59 +01:00
Pol Henarejos
d169f001b6 Upgrade to Pico SDK 2.1.1
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-19 10:58:43 +01:00
Pol Henarejos
250de29c3c Added support for OATH rename.
Fixes #107.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-17 19:54:56 +01:00
Pol Henarejos
7c4a020dc1 Merge PR #7 & #8 from @imkuang.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-09 19:18:31 +01:00
Pol Henarejos
88063d5d6d Added tests for silent authentication.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-08 15:01:25 +01:00
Pol Henarejos
f43bc9701f Added support for silent authentication.
Fixes #91.

It requires FIDO22 credential protocol, meaning that old credentials have to be reissued.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-02-08 15:00:12 +01:00
Pol Henarejos
353d782970 Fix OTP command issues in Linux.
Fixes #96.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-31 12:01:29 +01:00
Pol Henarejos
cdd2f486aa Added phy_save() and phy_load() to save and load PHY.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-29 17:09:47 +01:00
Pol Henarejos
a381e94dda Added phy_save() and phy_load() to save and load PHY.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-29 17:07:03 +01:00
Pol Henarejos
e78ec82435 Do not init PHY on modifying a single value.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-29 16:58:49 +01:00
Pol Henarejos
584d2f3b33 Add option to keep the LED steady.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-29 16:27:45 +01:00
Pol Henarejos
18676990cb Fix USB keyboard descriptor in Windows.
Fixes #97.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-29 13:22:21 +01:00
Pol Henarejos
ed9c46ded0 Fix slot deletion.
Fixes #89.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-19 19:55:16 +01:00
Pol Henarejos
6265992162 Upgrade to v6.2
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-15 15:23:29 +01:00
Pol Henarejos
63b7b9b8d2 Merge branch 'development' 2025-01-15 15:13:14 +01:00
Pol Henarejos
8db06bf3ac Add rollback version to 1.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-15 15:12:28 +01:00
Pol Henarejos
77dd1c4b98 Fix OTP/MKEK secure system.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-08 17:25:04 +01:00
Pol Henarejos
6a67800057 Add support for PIN hash storage and MKEK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2025-01-03 01:20:58 +01:00
Pol Henarejos
a70e259a90 Use partition bounds if available.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-30 21:42:44 +01:00
Pol Henarejos
7800056597 Fix bin name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-27 02:23:24 +01:00
Pol Henarejos
eeecf513cb Fix bin name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-27 02:23:11 +01:00
Pol Henarejos
9b0b584c14 Add nightly build of esp32.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-27 02:11:53 +01:00
Pol Henarejos
1c45685926 Add nightly build of esp32.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-27 02:11:31 +01:00
Pol Henarejos
cff544b485 Fix TX/RX buffers to align them with USB buffers and avoid overflows.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-24 02:06:50 +01:00
Pol Henarejos
1f805b1df2 Use more uint16 funcs.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-23 21:25:46 +01:00
Pol Henarejos
1d20321d69 Add BE/LE functions to pack uint16, uint32 and uint64.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-23 20:51:09 +01:00
Pol Henarejos
b42a664ac6 Add support for displaying memory usage via "pico-fido-tool.py memory" command.
Fixes #82.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-23 19:56:13 +01:00
Pol Henarejos
2d356a315e Increase TinyUSB stack size for ESP32 boards.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-23 19:54:11 +01:00
Pol Henarejos
9bfbc45f84 Add support for variable USB product name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-18 20:18:41 +01:00
Pol Henarejos
a5a0f3508c Remove NFC references.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-17 11:58:39 +01:00
Pol Henarejos
9c9074c1ef Do not debug after write the buffer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-16 18:43:04 +01:00
Pol Henarejos
022503fdc0 In pure U2F mode, no keepalive is sent by authenticator. Instead, client sends commands to know the status. Fixes #72.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-11 22:36:41 +01:00
Pol Henarejos
dba805dc04 Fix potential overflow due to bad initialization. Might fix #72.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-11 21:58:48 +01:00
Pol Henarejos
bbf474811b Add sanity checks.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-11 21:58:25 +01:00
Pol Henarejos
2eca08161d ESP32-S3 only supports 4 IN endpoints. Fixes #77.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-11 12:15:00 +01:00
Pol Henarejos
46ada2c1f7 Add support for tinyusb 0.17 in ESP32.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-01 01:24:01 +01:00
Pol Henarejos
5faab169a8 Add option to disable power cycle on reset via Commissioner.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-12-01 01:07:33 +01:00
Pol Henarejos
3148649f86 Fix RP2350 build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-25 23:48:35 +01:00
Pol Henarejos
3c40706aae Fix ESP32 build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-25 22:59:08 +01:00
Pol Henarejos
4a64c11740 Add support for Pico SDK 2.1.0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-25 22:44:22 +01:00
Pol Henarejos
2319abe44e Merge branch 'main' into development 2024-11-25 13:14:55 +01:00
Pol Henarejos
a5fe9b5d47 Build for Pico SDK 2.0.0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-25 13:14:45 +01:00
Pol Henarejos
d5af2cd8ed Remove ENABLE_UP_BUTTON macro.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-25 12:59:25 +01:00
Pol Henarejos
e994078790 Add UP button timeout to PHY.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-25 12:59:12 +01:00
Pol Henarejos
d99bcc90ec Add CCID SET_CLOCK_AND_FREQUENCY command for latest IFD version.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-25 12:56:29 +01:00
Pol Henarejos
7a59b51849 Upgrade to v6.0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-10 01:21:51 +01:00
Pol Henarejos
10c58b4be7 Update README
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-10 01:20:52 +01:00
Pol Henarejos
730e76af75 Enable OTP master key for ESP32-S3.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-10 01:07:31 +01:00
Pol Henarejos
ee80462a4a Merge branch 'development'
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-10 00:51:52 +01:00
Pol Henarejos
4ecb325e07 Upgrade Pico Keys SDK v7.0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-10 00:50:27 +01:00
Pol Henarejos
646b423fe4 Add compiler flags for optimized builds in ESP32.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-09 00:24:47 +01:00
Pol Henarejos
77c3568885 Add PICO_PRODUCT.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-09 00:23:04 +01:00
Pol Henarejos
3b43c5112b Add command to reset device via management app.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-08 19:33:40 +01:00
Pol Henarejos
244c18fb51 Fix esp32 build with wcid.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-06 17:11:44 +01:00
Pol Henarejos
78604f820d Always enable WCID interface.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-06 17:02:51 +01:00
Pol Henarejos
a68fbd65e9 Compact PHY config.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-05 18:57:28 +01:00
Pol Henarejos
bc0e022d85 Fix version header.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-05 18:37:11 +01:00
Pol Henarejos
3fad6baf89 Rename CCID_ code names to PICOKEY_
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-05 18:21:42 +01:00
Pol Henarejos
df2977e6ad Add rescue app.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-05 18:21:11 +01:00
Pol Henarejos
1fbf3da4f5 Fix usb initialization for emulation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-05 09:43:07 +01:00
Pol Henarejos
4ce6b2df5c Refactor PHY to support more flexible and scalable architecture.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-05 00:29:58 +01:00
Pol Henarejos
e5910b1cba Enable WCID by default.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-05 00:29:32 +01:00
Pol Henarejos
0df1330f92 Add support for commissioning.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-11-04 18:25:42 +01:00
Pol Henarejos
3ce8496faa Update workflows.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-10-04 17:53:59 +02:00
Pol Henarejos
ef49560d0a Fix nightly build
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-10-02 11:55:34 +02:00
Pol Henarejos
53ed3a46c4 Add autobuild for local.
Harmonize with other repos.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-10-01 09:34:22 +02:00
Pol Henarejos
dc07653ae7 Fix emulation build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-27 21:00:39 +02:00
Pol Henarejos
2d09a5c8e5 Added support to configure LED GPIO, LED brightness and LED dimming.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-27 20:56:33 +02:00
Pol Henarejos
720c2e45f3 Add support to LED_GPIO and LED_BTNESS vendor options.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-27 20:21:03 +02:00
Pol Henarejos
aeea3c7183 Fix ESP & emulation build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 19:40:29 +02:00
Pol Henarejos
8838ac9e54 Improve led driver support.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 19:29:08 +02:00
Pol Henarejos
623db840d3 Fix autobuild picotool
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 15:36:45 +02:00
Pol Henarejos
e2b06b908e Do not add SHA to filename, since it not will be able to rm.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 15:16:57 +02:00
Pol Henarejos
b9e791ca90 Fix nightly build
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 13:49:20 +02:00
Pol Henarejos
ed560f10a4 Install picotool
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 13:24:44 +02:00
Pol Henarejos
1f839c5f99 Append sha to nightly builds.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 13:24:29 +02:00
Pol Henarejos
effb8e4063 Fix build for WS2812 boards.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 12:01:55 +02:00
Pol Henarejos
b2e45b0f7f Fix build for boards with WS2812.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 11:33:29 +02:00
Pol Henarejos
24521dff4b Add nightly builds to main
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 11:25:21 +02:00
Pol Henarejos
7bc4a70319 Fix nightly build
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 11:20:43 +02:00
Pol Henarejos
cbef14beec Add manual trigger to workflows
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 11:09:34 +02:00
Pol Henarejos
0e54998d58 Add nightly deploy workflow
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-25 11:09:13 +02:00
Pol Henarejos
2e16036bb5 Update pico_sdk_import
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-24 00:44:58 +02:00
Pol Henarejos
f98df743f9 Upgrade CodeQL to v3
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-19 20:27:00 +02:00
Pol Henarejos
4fe1c0804c Add set target to ESP32-S3
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-19 20:12:52 +02:00
Pol Henarejos
7071949a1f More fixes
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-19 19:55:18 +02:00
Pol Henarejos
e07b5194e3 Fix again...
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-19 19:47:27 +02:00
Pol Henarejos
e05115ffac Fix autobuild for ESP32.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-19 19:37:01 +02:00
Pol Henarejos
38eca2fdd4 Fix permissions.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-19 19:30:03 +02:00
Pol Henarejos
f276e99342 Add autobuild for ESP32
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-19 19:26:04 +02:00
Pol Henarejos
6f517e8fca Fix header in Linux. Fixes #63
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-19 18:26:04 +02:00
Pol Henarejos
39e2ff40c3 Add support for dynamic VIDPID via PHY.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-18 19:44:02 +02:00
Pol Henarejos
ffbe3fcbad Add OTP support and sha256 hardware acceleration.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-18 19:43:54 +02:00
Pol Henarejos
cf5dbc9ae5 Add support for dynamic VIDPID via PHY.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-18 19:42:14 +02:00
Pol Henarejos
2fca44540a Add sha256 hardware accelerator.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-13 21:04:21 +02:00
Pol Henarejos
ec612a451d Fix ssh-keygen creation.
Fixes #59

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-13 21:03:58 +02:00
Pol Henarejos
c43006f8c2 Protect keydev if available (only for RP2350).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-12 19:01:04 +02:00
Pol Henarejos
95cae29206 Upgrade to version 5.12
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-02 17:12:11 +02:00
Pol Henarejos
11c28adbb0 Add more boards with RP2350.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-02 17:11:57 +02:00
Pol Henarejos
661442956d Update readme to add Passkey term.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-02 12:02:42 +02:00
Pol Henarejos
778c6b038a Fix BOOT press with RP2350.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-09-02 09:48:27 +02:00
Pol Henarejos
de1c50db4f Replace sdkconfig
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-30 14:47:49 +02:00
Pol Henarejos
c1e985c9af Use mutex/semaphores for emulation, like in Pico and ESP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-30 12:42:33 +02:00
Pol Henarejos
4f787eaaba Fix otp in Pico
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-30 00:34:14 +02:00
Pol Henarejos
b77277b72e Add RP2350 support.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-29 16:57:59 +02:00
Pol Henarejos
02556fcde1 Fix buffer initialization.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-25 20:21:43 +02:00
Pol Henarejos
f234b0dc26 Fix emulation run
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-25 01:31:19 +02:00
Pol Henarejos
8ba9116454 Fix test
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-25 01:30:54 +02:00
Pol Henarejos
5a31405244 Improving tests
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-25 00:10:23 +02:00
Pol Henarejos
902a988350 Fix memory cleanups.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-24 02:34:15 +02:00
Pol Henarejos
6256a9547d Fix build emulation
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-24 00:11:40 +02:00
Pol Henarejos
5568aa7b69 Fixed thread synchronization.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-23 19:25:20 +02:00
Pol Henarejos
5e86745672 Add missing files for ESP32.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-23 15:23:10 +02:00
Pol Henarejos
cffa8e29ff Fix windows build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-23 14:24:03 +02:00
Pol Henarejos
6c74db9763 Fix warnings.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-23 13:17:51 +02:00
Pol Henarejos
dac6407134 Fix windows build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-23 13:17:29 +02:00
Pol Henarejos
f49833291f Major refactor of USB CCID and USB HID interfaces.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-23 10:04:00 +02:00
Pol Henarejos
8c1e002892 select_app now invokes U2F or FIDO depending on the message.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-20 14:29:25 +02:00
Pol Henarejos
8d49ed5ffc Fix potential crash invoking OTP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-20 14:28:09 +02:00
Pol Henarejos
a0d9ad7a3a Increase vStack depending on the number of interfaces.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-20 12:43:15 +02:00
Pol Henarejos
910fb66f3c Fix keepalive
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-19 16:45:11 +02:00
Pol Henarejos
ed12d6f8e9 Fix emulation build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-19 13:18:03 +02:00
Pol Henarejos
a9799dc77f Fix CBOR error.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-19 13:12:04 +02:00
Pol Henarejos
d7d75caecf Fix OATH selection.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-19 13:11:48 +02:00
Pol Henarejos
af4eb075c7 Add HID/CCID fixes for ESP32.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-19 00:09:05 +02:00
Pol Henarejos
0c5280e12a Add support to ESP32 build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-19 00:08:31 +02:00
Pol Henarejos
163e936231 Fix potential bug in CBOR encoding.
It happen if a keepalive packet is sent in the middle of an encoding.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-18 23:59:52 +02:00
Pol Henarejos
1b4dd9bed0 Fix ESP32 build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-08-18 23:53:18 +02:00
Pol Henarejos
5b95e35ca9 Upgrade to version 5.10
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-07-20 20:29:40 +02:00
Pol Henarejos
69ec242095 Update README.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-07-20 20:28:09 +02:00
Pol Henarejos
6eb6cd35d0 Merge branch 'development'
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-07-20 20:27:01 +02:00
Pol Henarejos
f21e203093 Fix compilation
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-07-20 20:05:00 +02:00
Pol Henarejos
e96da09a84 Fixes for mbedtls 3.6
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-07-20 20:04:48 +02:00
Pol Henarejos
6fe16a63e4 Upgrade Pico Keys SDK
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-07-20 20:04:41 +02:00
Pol Henarejos
d5fe405a87 Fix test bad pub type.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-06-30 00:32:40 +02:00
Pol Henarejos
54bbc0e9ea Fix return value when bad key type is provided. Fixes #47.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-06-30 00:31:29 +02:00
Pol Henarejos
b0b0187919 Fix cleared permissions on make credential when UP is not present.
Following 14.1, flags shall be cleared only when UP == true.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-06-07 20:57:21 +02:00
Pol Henarejos
1f0e1fb8f4 Use latest Pico Keys SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-05-05 00:58:51 +02:00
Pol Henarejos
f3f34cf66b Fix oath crash.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-03-13 22:06:00 +01:00
Pol Henarejos
82ed96b2e2 Fix asn1 struct initialization.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-03-13 21:22:05 +01:00
Pol Henarejos
92d04f9131 Use new asn1 structs.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2024-03-13 18:34:14 +01:00
Pol Henarejos
7a71bf48fc Add -DVIDPID=<VALUE> to build a project with a known VID/PID. Supported values: NitroHSM, NitroFIDO2, NitroStart, NitroPro, Nitro3, Yubikey5, YubikeyNeo, YubiHSM, Gnuk, GnuPG
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-12-11 18:13:32 +01:00
Pol Henarejos
7e2ecdbc56 Upgrade to version 5.8
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
e54df525c4 Removing SHORT_TICKET limitation.
It is not used to return the half of ticket, but to combine with static to produce hex scancodes.

Fixes #29.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
1d9107d4bb OTP callbacks must be initialized on ctor.
Fixes #30.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
a9be759da3 OTP static passwords are 38 bytes length.
A static password uses fixed, uid and key fields (sum 38). However, Yubikey sets short_ticket flag which implies the half of the password is sent.

Fixes #29.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
34bfc3b2ef otp must be initialized when selection fido or management applets.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
d985cf6301 Moving Pico Keys SDK pointer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
0b00e01187 Fix build in emulation mode.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
bef1922c8f Use new names and defines.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
107e5c34db Use new pico-keys-sdk submodule name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
6157a91fdf Rename old pico-hsm-sdk to the new pico-keys-sdk.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
779db90713 Move some functions from HID to fido callbacks.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
a0f1d2334d Use get_version_major and get_version_minor as pointers.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
5c6f87ab8f Update SDK to new otp.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
cf152c1692 Move some OTP functions from HID to OTP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
04238509ee Generate a secure key if it is not found.
Should fix #23.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
85298062cd python-fido2 has a bug which does not allow to use 0xff as ConfigVendorPrototype.
It encodes an uint8_t to int8_t and thus, the command must be <= 0x7f.

Fixes #22.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
0464ad8964 Fixed AUT permission.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
19197e54a8 Added support for --pin flag.
It loads Vendor/Ctap2Vendor with uv_token based on provided --pin.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
01a6c9f77f Added Windows & Linux backend for backup/restore.
Fixes #21

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
ba57cc4527 Fixed OTP read packet through HID interfaces.
Fixes #19.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
68b5614fb9 Fixed potential crash.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
4fd4d75e21 Fixed potential memory leak.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
599fd706ce Fix AID selection.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:31 +01:00
Pol Henarejos
28e979939a Adapted to new selection AID method.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:30 +01:00
Pol Henarejos
849221fd95 Added backfall compatibility.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:30 +01:00
Pol Henarejos
011429a982 Update to latest HSM SDK changes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:30 +01:00
Pol Henarejos
b99181a00c Fix pico_w build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:30 +01:00
Pol Henarejos
041bb788f9 Added support for LED in Pico W.
Fixed #17.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:02:30 +01:00
Pol Henarejos
20a8ef08f0 Upgrade to version 5.8
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 12:01:47 +01:00
Pol Henarejos
e757ad2945 Removing SHORT_TICKET limitation.
It is not used to return the half of ticket, but to combine with static to produce hex scancodes.

Fixes #29.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 11:53:47 +01:00
Pol Henarejos
1ce0d98c34 OTP callbacks must be initialized on ctor.
Fixes #30.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 11:50:42 +01:00
Pol Henarejos
96de6efed6 OTP static passwords are 38 bytes length.
A static password uses fixed, uid and key fields (sum 38). However, Yubikey sets short_ticket flag which implies the half of the password is sent.

Fixes #29.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-16 20:16:23 +01:00
Pol Henarejos
195096ad52 otp must be initialized when selection fido or management applets.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-16 20:12:48 +01:00
Pol Henarejos
1ee86f8634 Moving Pico Keys SDK pointer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-16 20:12:01 +01:00
Pol Henarejos
2b9a5829e5 Merge pull request #26 from sylvainpelissier/patch-1
Update pico-fido-patch-vidpid.sh
2023-11-08 14:17:49 +01:00
Sylvain Pelissier
8056e64cab Update pico-fido-patch-vidpid.sh
Match any previous VID:PID in the image and replace with the new ones.
2023-11-08 13:56:37 +01:00
Pol Henarejos
ffb3beb84a Fix build in emulation mode.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-06 15:32:25 +01:00
Pol Henarejos
d78d9d10aa Use new names and defines.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-06 15:22:28 +01:00
Pol Henarejos
f8d4f1d02e Use new pico-keys-sdk submodule name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-06 14:28:09 +01:00
Pol Henarejos
b493a81ddc Rename old pico-hsm-sdk to the new pico-keys-sdk.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-06 14:27:57 +01:00
Pol Henarejos
5c20909b03 Move some functions from HID to fido callbacks.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-06 13:01:10 +01:00
Pol Henarejos
27b9e3954a Use get_version_major and get_version_minor as pointers.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-06 11:57:08 +01:00
Pol Henarejos
440ec5c854 Update SDK to new otp.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-06 11:49:42 +01:00
Pol Henarejos
cb2744cab3 Move some OTP functions from HID to OTP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-06 11:49:18 +01:00
Pol Henarejos
5db1014850 Generate a secure key if it is not found.
Should fix #23.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-06 11:48:32 +01:00
Pol Henarejos
421bea6421 python-fido2 has a bug which does not allow to use 0xff as ConfigVendorPrototype.
It encodes an uint8_t to int8_t and thus, the command must be <= 0x7f.

Fixes #22.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-02 22:29:28 +01:00
Pol Henarejos
65039c0959 Fixed AUT permission.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-02 22:13:45 +01:00
Pol Henarejos
8e36b4c379 Added support for --pin flag.
It loads Vendor/Ctap2Vendor with uv_token based on provided --pin.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-02 22:08:49 +01:00
Pol Henarejos
3652368542 Added Windows & Linux backend for backup/restore.
Fixes #21

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-02 09:32:19 +01:00
Pol Henarejos
e5d1ef29a4 Fixed OTP read packet through HID interfaces.
Fixes #19.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-10-31 17:35:59 +01:00
Pol Henarejos
0fd36806cc Fixed potential crash.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-10-31 00:40:56 +01:00
Pol Henarejos
7bf26b28fc Fixed potential memory leak.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-10-30 16:51:56 +01:00
Pol Henarejos
da94a82487 Fix AID selection.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-10-28 20:57:53 +02:00
Pol Henarejos
c24be5a631 Adapted to new selection AID method.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-10-28 20:53:06 +02:00
Pol Henarejos
46ce9390bf Added backfall compatibility.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-10-28 20:52:07 +02:00
Pol Henarejos
c1fd5736f9 Update to latest HSM SDK changes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-10-28 20:51:36 +02:00
Pol Henarejos
b1c4ff877e Fix pico_w build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-18 10:39:21 +02:00
Pol Henarejos
6c85d57412 Added support for LED in Pico W.
Fixed #17.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-18 10:14:11 +02:00
Pol Henarejos
2e5b8f4c71 Upgrade to version 5.6 2023-09-18 09:01:14 +02:00
Pol Henarejos
a9697ba4e0 Upgrade to version 5.6
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-18 08:57:31 +02:00
Pol Henarejos
aec488f070 Revert "Upgrade to version 5.6"
This reverts commit 45c2cf65fe.
2023-09-18 08:56:43 +02:00
Pol Henarejos
9c90095e96 CBOR errors are not sent through CTAPHID_ERROR command, but in CBOR response instead. Fixes #16
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-18 01:36:47 +02:00
Pol Henarejos
7c5f2cee4b Do not throw error if not supported but valid algorithm is provided. Just ignore it.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-18 01:35:13 +02:00
Pol Henarejos
5e0c42a9f9 Use hexa representation for error displaying
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-18 01:34:47 +02:00
Pol Henarejos
da7b918dc4 Added RS algorithms though are not supported.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-18 01:34:34 +02:00
Pol Henarejos
cfcfb941e0 Merge 5.6 changes. 2023-09-17 19:13:43 +02:00
Pol Henarejos
45c2cf65fe Upgrade to version 5.6
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-17 19:13:07 +02:00
Pol Henarejos
1217d82361 Add support for newer boards.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-17 19:12:57 +02:00
Pol Henarejos
332debea6d Code style.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-17 19:11:39 +02:00
Pol Henarejos
bafede2ae5 Add supported curves to README.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-22 15:31:30 +02:00
Pol Henarejos
539420b996 Added ES256K tests.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-22 13:23:56 +02:00
Pol Henarejos
0c08590dcc Added support for ES256K tests.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-22 13:23:42 +02:00
Pol Henarejos
cac4ae1751 Adapted test errors to specs.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-22 13:23:22 +02:00
Pol Henarejos
974868d8e4 FIDO2 Server only uses supported algorithms.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-22 13:22:38 +02:00
Pol Henarejos
2bbaf7c274 Adapted pubKeyCredParams verification and return error messages to specs.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-22 13:22:16 +02:00
Pol Henarejos
df26040838 Fix loading SECP521R1 key.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-21 19:11:44 +02:00
Pol Henarejos
539ea61436 Add get assertion test with different algorithms.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 14:10:49 +02:00
Pol Henarejos
75771e5e46 Not used.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 14:10:24 +02:00
Pol Henarejos
8e26ec8bcd Use python-fido2 from my repo, which contains some fixes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 14:10:17 +02:00
Pol Henarejos
26148282e6 Fix credential creation for ES512.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 13:07:06 +02:00
Pol Henarejos
05044b498d Added test for testing algorithms on make credential.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 13:06:51 +02:00
Pol Henarejos
be44947475 Fix writing COSE key when for curves with kty=1.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 12:43:49 +02:00
Pol Henarejos
0d280ca252 Moving pointer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 12:33:30 +02:00
Pol Henarejos
4c3042a8bf Added function for reading COSE keys.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-17 01:30:49 +02:00
Pol Henarejos
b7ceec8d49 Using COSE keys write functions.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-17 01:19:45 +02:00
Pol Henarejos
63e15b19bb Added functions for writing COSE keys.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-17 01:19:27 +02:00
Pol Henarejos
b2c4e0e1c1 Added curve to fido.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-17 01:19:13 +02:00
Pol Henarejos
b72c596aa6 Fix chained response.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 19:20:11 +02:00
Pol Henarejos
2d81a3c472 Update to pyfido2 1.1.2
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 19:17:13 +02:00
Pol Henarejos
bb20dd7a53 First attempt to include CBOR as CCID.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 19:16:54 +02:00
Pol Henarejos
c258dad8e6 Fix OTP applet selection.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 17:32:52 +02:00
Pol Henarejos
ce040a79f5 Fix signature computation for algorithms ES384 and ES512.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 12:39:53 +02:00
Pol Henarejos
8ffd1bfe38 Added support for ES256K algorithm.
It uses secp256k1 curve with SHA-256.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 12:18:42 +02:00
Pol Henarejos
5105545df0 Added thirdPartyPayment to supported extensions.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-14 21:44:34 +02:00
Pol Henarejos
d011314500 Add thirdPartyPayment extension to credential manager response.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-14 21:44:15 +02:00
Pol Henarejos
51cbfe5fe9 Fix enabled cap detection when applet is already selected.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-14 21:09:54 +02:00
Pol Henarejos
aa7362f88f Fix enabled capabilities detection.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-14 20:49:29 +02:00
Pol Henarejos
2b1227b105 Added support for management via Yubikey Manager to enable/disable specific interfaces individually.
All interfaces are enabled by default.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-14 19:55:17 +02:00
Pol Henarejos
a79842b33f Fix OTP slot deletion.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-14 18:45:36 +02:00
Pol Henarejos
30f51b8453 Add Nitrokey readme support.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-14 18:44:48 +02:00
Pol Henarejos
c00c83dfe6 Added support for thirdPartyPayment extension.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-13 21:12:49 +02:00
Pol Henarejos
cda97259b3 Create FUNDING.yml 2023-05-17 10:22:35 +02:00
Pol Henarejos
c883083a75 Fix for mbedtls 3.4 build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-05-16 09:41:11 +02:00
Pol Henarejos
016780b3de Update pointer
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-05-16 09:23:16 +02:00
Pol Henarejos
24224b78dd Added support to Nitrokey's nitropy tool.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-05-15 19:11:02 +02:00
Pol Henarejos
07729f807b Upgrade to version 5.4
This passes from previous version 3.0 to 5.4 due to compatibility issues with Yubico software, which expects +5.4

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-05-15 19:02:32 +02:00
Pol Henarejos
e0c793dd0a Fix empty challenge.
Now a new fresh challenge is generated on every select command.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-05-12 16:19:22 +02:00
Pol Henarejos
9d6003d1e5 Add more features to README
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-05-11 20:10:44 +02:00
Pol Henarejos
147a93d7fb Update README.md
Added Pico Patcher link.
2023-05-11 20:08:39 +02:00
Pol Henarejos
f12c55805c Put again commands to FIDO app for interoperability.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 01:19:23 +02:00
Pol Henarejos
7e10e25f96 Added management application.
Used for Yubico clients.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 01:05:19 +02:00
Pol Henarejos
9052c66a7f Fix returning otp status over ccid.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 00:29:22 +02:00
Pol Henarejos
443ca69547 Added get config capabilities command.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 00:17:39 +02:00
Pol Henarejos
415c1b2e9c Enable U2F applet selection.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-02 00:00:09 +02:00
Pol Henarejos
d87c1530c7 Return otp_status if selected applet OTP id.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 23:48:47 +02:00
Pol Henarejos
f90baaf095 Do not respond a challenge-response command if no challenge-response app is configured.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 23:37:51 +02:00
Pol Henarejos
1d7bdb0861 Added support for swap.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 23:35:19 +02:00
Pol Henarejos
fa811e2a0f If slot is configured with a challenge-response app, do nothing when pressed.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 23:25:57 +02:00
Pol Henarejos
ff498ebfdf Added support for update config.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 19:11:00 +02:00
Pol Henarejos
cceb735cc0 Fix order of fields of Yubico OTP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 01:15:59 +02:00
Pol Henarejos
5a9de32e02 Added support for challenge-response for Yubico OTP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 01:13:52 +02:00
Pol Henarejos
c9eacc4a3d Added support for challenge-response HMAC SHA1.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-04-01 01:05:09 +02:00
Pol Henarejos
c23d92ea28 Added support for OTP YubiOTP.
It generates a 44 byte string, modhex encoded, following the specification of Yubikey for OTP YubiOTP. When button is pressed, it sends the 44-byte OTP to the host machine, as if it was typed.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-31 20:03:02 +02:00
Pol Henarejos
da04fbb824 Add crc check.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 23:44:37 +02:00
Pol Henarejos
0bfa760903 Undo previous commit.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 01:14:06 +02:00
Pol Henarejos
bd9d4286d5 Added fix for emulation conditional build.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 01:04:22 +02:00
Pol Henarejos
3d1c68fa40 Added support for APPEND_CR.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 00:52:08 +02:00
Pol Henarejos
26ac66e813 Added support for OTP HOTP and OTP Static.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 00:37:51 +02:00
Pol Henarejos
05afcd706e Fix OATH calculation result when called multiple times.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-30 00:37:31 +02:00
Pol Henarejos
8c90dd55bd Added support for button pressed callback.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-28 23:33:14 +02:00
Pol Henarejos
c6c1d0c6eb Added features to README
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-27 00:19:11 +02:00
55 changed files with 2984 additions and 1255 deletions

4
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
# These are supported funding model platforms
github: polhenarejos
custom: ["https://www.paypal.me/polhenarejos"]

View File

@@ -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
View 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/*.*

View File

@@ -19,6 +19,7 @@ on:
branches: [ "main", "development" ]
schedule:
- cron: '23 5 * * 4'
workflow_dispatch:
jobs:
build:

6
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "pico-hsm-sdk"]
path = pico-hsm-sdk
url = ../pico-hsm-sdk
[submodule "pico-keys-sdk"]
path = pico-keys-sdk
url = https://github.com/polhenarejos/pico-keys-sdk

View File

@@ -17,8 +17,16 @@
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()
@@ -33,15 +41,7 @@ pico_sdk_init()
endif()
add_executable(pico_fido)
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)
endif()
option(ENABLE_POWER_ON_RESET "Enable/disable power cycle on reset" ON)
if(ENABLE_POWER_ON_RESET)
@@ -72,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()
@@ -79,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
@@ -95,6 +97,7 @@ set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_config.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_vendor.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_large_blobs.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/management.c
)
if (${ENABLE_OATH_APP})
set(SOURCES ${SOURCES}
@@ -108,43 +111,53 @@ set(SOURCES ${SOURCES}
endif()
set(USB_ITF_HID 1)
include(pico-hsm-sdk/pico_hsm_sdk_import.cmake)
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()
set(INCLUDES ${INCLUDES}
${CMAKE_CURRENT_LIST_DIR}/src/fido
)
if(NOT ESP_PLATFORM)
target_sources(pico_fido PUBLIC ${SOURCES})
target_include_directories(pico_fido PUBLIC ${INCLUDES})
target_compile_options(pico_fido PUBLIC
-Wall
)
if (NOT MSVC)
target_compile_options(pico_fido PUBLIC
-Werror
)
string(FIND ${CMAKE_C_COMPILER} ":" COMPILER_COLON)
if (${COMPILER_COLON} GREATER_EQUAL 0)
target_compile_options(pico_fido PUBLIC
-Wno-error=use-after-free
)
endif()
endif(NOT MSVC)
if(ENABLE_EMULATION)
target_compile_options(pico_fido PUBLIC
-fdata-sections
-ffunction-sections
)
if(APPLE)
target_link_options(pico_fido PUBLIC
-Wl,-dead_strip
)
else()
target_link_options(pico_fido PUBLIC
-Wl,--gc-sections
)
target_link_libraries(pico_fido PRIVATE m)
endif (APPLE)
if(NOT MSVC)
target_compile_options(pico_fido PUBLIC
-fdata-sections
-ffunction-sections
)
endif(NOT MSVC)
if(APPLE)
target_link_options(pico_fido PUBLIC
-Wl,-dead_strip
)
else()
target_link_options(pico_fido PUBLIC
-Wl,--gc-sections
)
endif (APPLE)
target_link_libraries(pico_fido PRIVATE pthread m)
else()
pico_add_extra_outputs(pico_fido)
target_link_libraries(pico_fido PRIVATE pico_hsm_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board)
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()

View File

@@ -1,8 +1,8 @@
# Pico FIDO
This project aims at transforming your Raspberry Pico into a FIDO key integrated. The Pico works as a FIDO key, like a normal USB key 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 has implemented the following features:
Pico FIDO includes the following features:
- CTAP 2.1 / CTAP 1
- WebAuthn
@@ -10,15 +10,16 @@ Pico FIDO has implemented the following features:
- HMAC-Secret extension
- CredProtect extension
- User presence enforcement through physical button
- User Verification with PIN
- Discoverable credentials
- User verification with PIN
- Discoverable credentials (resident keys)
- Credential management
- ECDSA authentication
- Support for SECP256R1, SECP384R1, SECP521R1, and SECP256K1 curves
- App registration and login
- Device selection
- Support for vendor Config
- Support for vendor configuration
- Backup with 24 words
- Secure lock to protect the device from flash dumpings
- Secure lock to protect the device from flash dumps
- Permissions support (MC, GA, CM, ACFG, LBW)
- Authenticator configuration
- minPinLength extension
@@ -26,33 +27,71 @@ Pico FIDO has implemented the following features:
- Enterprise attestation
- credBlobs extension
- largeBlobKey extension
- largeBlobs support (2048 bytes máx.)
- Large blobs support (2048 bytes max)
- OATH (based on YKOATH protocol specification)
- TOTP / HOTP
- 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 these features are compliant with the specification. Therefore, if you detect some behaviour that is not expected or it does not follow the rules of specs, please open an issue.
All features comply with the specifications. If you encounter unexpected behavior or deviations from the specifications, please open an issue.
## Security considerations
Pico FIDO is an open platform so be careful. The contents in the flash memory may be easily dumpled and obtain the private/master keys. Therefore, it is not possible to encrypt the content. At least, one key (the master, the supreme key) must be stored in clear text.
## 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.
If the Pico is stolen the contents of private and secret keys can be read.
**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, go to the [Release page](https://github.com/polhenarejos/pico-fido/releases "Release page") and 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.**
## Build
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.
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.
git clone https://github.com/polhenarejos/pico-fido
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 UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you plan to use it with other proprietary tools, you should modify Info.plist of CCID driver to add these VID/PID or use the [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner").
Note that PICO_BOARD, USB_VID and USB_PID are optional. If not provided, pico board and VID/PID FEFF:FCFD will be used.
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.
After make ends, the binary file pico_fido.uf2 will be generated. Put your pico board into loading mode, by pushing BOOTSEL button while pluging on, and copy the UF2 to the new fresh usb mass storage Pico device. Once copied, the pico mass storage will be disconnected automatically and the pico board will reset with the new firmware. A blinking led will indicate the device is ready to work.
Note that the pure-browser option [Pico Commissioner](https://www.picokeys.com/pico-commissioner/ "Pico Commissioner") is the most recommended.
**Remark:** Pico Fido uses HID interface and thus, VID/PID values are irrelevant in terms of operativity. You can safely use any arbitrary value or the default ones.
## 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.
Additionally, you can pass the `VIDPID=value` parameter to build the firmware with a known VID/PID. The supported values are:
- `NitroHSM`
- `NitroFIDO2`
- `NitroStart`
- `NitroPro`
- `Nitro3`
- `Yubikey5`
- `YubikeyNeo`
- `YubiHSM`
- `Gnuk`
- `GnuPG`
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:
@@ -78,20 +117,21 @@ While processing, the Pico FIDO is busy and cannot receive additional commands u
## Driver
Pico FIDO uses the `HID` driver, present in all OS. It should be detected by all OS and browser/applications, like normal USB FIDO keys.
Pico FIDO uses the `HID` driver, which is present in all operating systems. It should be detected by all OS and browser/applications just like normal USB FIDO keys.
## Tests
Tests can be found at `tests` folder. It is based on [FIDO2 tests](https://github.com/solokeys/fido2-tests "FIDO2 tests") from Solokeys, but adapted to [python-fido2](https://github.com/Yubico/python-fido2 "python-fido2") v1.0 package, which is a major refactor from previous 0.8 version and includes latests improvements from CTAP 2.1.
Tests can be found in the `tests` folder. They are based on [FIDO2 tests](https://github.com/solokeys/fido2-tests "FIDO2 tests") from Solokeys but adapted to the [python-fido2](https://github.com/Yubico/python-fido2 "python-fido2") v1.0 package, which is a major refactor from the previous 0.8 version and includes the latest improvements from CTAP 2.1.
All tests can be run by
To run all tests, use:
```
```sh
pytest
```
or by selecting a subset with `-k <test>` flag:
```
To run a subset of tests, use the `-k <test>` flag:
```sh
pytest -k test_credprotect
```

View File

@@ -1,55 +1,23 @@
#!/bin/bash
VERSION_MAJOR="3"
VERSION_MINOR="0"
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 adafruit_feather_rp2040 \
adafruit_itsybitsy_rp2040 \
adafruit_kb2040 \
adafruit_macropad_rp2040 \
adafruit_qtpy_rp2040 \
adafruit_trinkey_qt2040 \
arduino_nano_rp2040_connect \
datanoisetv_rp2040_dsp \
eetree_gamekit_rp2040 \
garatronic_pybstick26_rp2040 \
melopero_shake_rp2040 \
pico \
pico_w \
pimoroni_badger2040 \
pimoroni_interstate75 \
pimoroni_keybow2040 \
pimoroni_motor2040 \
pimoroni_pga2040 \
pimoroni_picolipo_4mb \
pimoroni_picolipo_16mb \
pimoroni_picosystem \
pimoroni_plasma2040 \
pimoroni_servo2040 \
pimoroni_tiny2040 \
pimoroni_tiny2040_2mb \
seeed_xiao_rp2040 \
solderparty_rp2040_stamp \
solderparty_rp2040_stamp_carrier \
solderparty_rp2040_stamp_round_carrier \
sparkfun_micromod \
sparkfun_promicro \
sparkfun_thingplus \
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 \
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

View File

@@ -87,7 +87,7 @@ fi
LITTLE_VID="\x${VID:2:2}\x${VID:0:2}"
LITTLE_PID="\x${PID:2:2}\x${PID:0:2}"
perl -pi -e "s/\xfe\xca\x31\x42\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/$LITTLE_VID$LITTLE_PID\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01/" $UF2_FILE_OF
perl -pi -e "s/[\x00-\xff]{4}\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01\x00\x00/$LITTLE_VID$LITTLE_PID\x$VERSION_MINOR\x$VERSION_MAJOR\x01\x02\x03\x01\x00\x00/" $UF2_FILE_OF
echo "Done!"
echo ""

Submodule pico-hsm-sdk deleted from b12e66a057

1
pico-keys-sdk Submodule

Submodule pico-keys-sdk added at 6e6b524878

View File

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

55
sdkconfig.defaults Normal file
View File

@@ -0,0 +1,55 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
#
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"
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
CONFIG_MBEDTLS_POLY1305_C=y
CONFIG_MBEDTLS_CHACHAPOLY_C=y
CONFIG_MBEDTLS_HKDF_C=y
CONFIG_MBEDTLS_HARDWARE_ECC=y
CONFIG_MBEDTLS_HARDWARE_GCM=y
# CONFIG_MBEDTLS_HARDWARE_MPI is not set
CONFIG_MBEDTLS_HARDWARE_SHA=y
CONFIG_MBEDTLS_HARDWARE_AES=y
# CONFIG_MBEDTLS_ROM_MD5 is not set
CONFIG_MBEDTLS_SHA512_C=y
CONFIG_MBEDTLS_TLS_DISABLED=y
# CONFIG_MBEDTLS_TLS_ENABLED is not set
# CONFIG_ESP_TLS_USE_DS_PERIPHERAL is not set
# CONFIG_ESP_WIFI_ENABLED is not set
# CONFIG_ESP_WIFI_MBEDTLS_CRYPTO is not set
# CONFIG_ESP_WIFI_MBEDTLS_TLS_CLIENT is not set
# CONFIG_WPA_MBEDTLS_CRYPTO is not set
# CONFIG_MBEDTLS_PSK_MODES is not set
# CONFIG_MBEDTLS_KEY_EXCHANGE_RSA is not set
# CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE is not set
# CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA is not set
# CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA is not set
# CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA is not set
# CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA is not set
# CONFIG_MBEDTLS_SSL_RENEGOTIATION is not set
# CONFIG_MBEDTLS_SSL_PROTO_TLS1_2 is not set
# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set
# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set
# CONFIG_MBEDTLS_SSL_ALPN is not set
# CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS is not set
# CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS is not set
# CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE is not set
# CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA is not set
# CONFIG_ESP_WIFI_ENABLE_WPA3_SAE is not set
# CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA is not set
CONFIG_ESP_COREDUMP_ENABLE_TO_UART=y

6
src/fido/CMakeLists.txt Normal file
View File

@@ -0,0 +1,6 @@
idf_component_register(
SRCS ${SOURCES}
INCLUDE_DIRS . ../../pico-keys-sdk/src ../../pico-keys-sdk/src/fs ../../pico-keys-sdk/src/rng ../../pico-keys-sdk/src/usb ../../pico-keys-sdk/tinycbor/src
REQUIRES bootloader_support esp_partition esp_tinyusb zorxx__neopixel mbedtls efuse
)
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON)

View File

@@ -15,7 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ENABLE_EMULATION
#include "pico_keys.h"
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include "pico/stdlib.h"
#endif
#include "hid/ctap_hid.h"
@@ -23,6 +24,9 @@
#include "fido.h"
#include "usb.h"
#include "apdu.h"
#include "management.h"
#include "ctap2_cbor.h"
#include "version.h"
const bool _btrue = true, _bfalse = false;
@@ -38,12 +42,13 @@ int cbor_config(const uint8_t *data, size_t len);
int cbor_vendor(const uint8_t *data, size_t len);
int cbor_large_blobs(const uint8_t *data, size_t len);
const uint8_t aaguid[16] =
{ 0x89, 0xFB, 0x94, 0xB7, 0x06, 0xC9, 0x36, 0x73, 0x9B, 0x7E, 0x30, 0x52, 0x6D, 0x96, 0x81, 0x45 }; // First 16 bytes of SHA256("Pico FIDO2")
extern int cmd_read_config();
const uint8_t aaguid[16] = { 0x89, 0xFB, 0x94, 0xB7, 0x06, 0xC9, 0x36, 0x73, 0x9B, 0x7E, 0x30, 0x52, 0x6D, 0x96, 0x81, 0x45 }; // First 16 bytes of SHA256("Pico FIDO2")
const uint8_t *cbor_data = NULL;
size_t cbor_len = 0;
uint8_t cmd = 0;
uint8_t cbor_cmd = 0;
int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
if (len == 0 && cmd == CTAPHID_CBOR) {
@@ -52,75 +57,204 @@ int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
if (len > 0) {
DEBUG_DATA(data + 1, len - 1);
}
driver_prepare_response_hid();
if (cmd == CTAPHID_CBOR) {
if (data[0] == CTAP_MAKE_CREDENTIAL) {
return cbor_make_credential(data + 1, len - 1);
if (cap_supported(CAP_FIDO2)) {
if (cmd == CTAPHID_CBOR) {
if (data[0] == CTAP_MAKE_CREDENTIAL) {
return cbor_make_credential(data + 1, len - 1);
}
if (data[0] == CTAP_GET_INFO) {
return cbor_get_info();
}
else if (data[0] == CTAP_RESET) {
return cbor_reset();
}
else if (data[0] == CTAP_CLIENT_PIN) {
return cbor_client_pin(data + 1, len - 1);
}
else if (data[0] == CTAP_GET_ASSERTION) {
return cbor_get_assertion(data + 1, len - 1, false);
}
else if (data[0] == CTAP_GET_NEXT_ASSERTION) {
return cbor_get_next_assertion(data + 1, len - 1);
}
else if (data[0] == CTAP_SELECTION) {
return cbor_selection();
}
else if (data[0] == CTAP_CREDENTIAL_MGMT || data[0] == 0x41) {
return cbor_cred_mgmt(data + 1, len - 1);
}
else if (data[0] == CTAP_CONFIG) {
return cbor_config(data + 1, len - 1);
}
else if (data[0] == CTAP_LARGE_BLOBS) {
return cbor_large_blobs(data + 1, len - 1);
}
}
if (data[0] == CTAP_GET_INFO) {
return cbor_get_info();
else if (cmd == CTAP_VENDOR_CBOR) {
return cbor_vendor(data, len);
}
else if (data[0] == CTAP_RESET) {
return cbor_reset();
else if (cmd == 0xC2) {
if (cmd_read_config() == 0x9000) {
memmove(res_APDU-1, res_APDU, res_APDU_size);
res_APDU_size -= 1;
return 0;
}
}
else if (data[0] == CTAP_CLIENT_PIN) {
return cbor_client_pin(data + 1, len - 1);
}
else if (data[0] == CTAP_GET_ASSERTION) {
return cbor_get_assertion(data + 1, len - 1, false);
}
else if (data[0] == CTAP_GET_NEXT_ASSERTION) {
return cbor_get_next_assertion(data + 1, len - 1);
}
else if (data[0] == CTAP_SELECTION) {
return cbor_selection();
}
else if (data[0] == CTAP_CREDENTIAL_MGMT || data[0] == 0x41) {
return cbor_cred_mgmt(data + 1, len - 1);
}
else if (data[0] == CTAP_CONFIG) {
return cbor_config(data + 1, len - 1);
}
else if (data[0] == CTAP_LARGE_BLOBS) {
return cbor_large_blobs(data + 1, len - 1);
}
}
else if (cmd == CTAP_VENDOR_CBOR) {
return cbor_vendor(data, len);
}
return CTAP1_ERR_INVALID_CMD;
}
#ifndef ENABLE_EMULATION
void cbor_thread() {
void cbor_thread(void) {
card_init_core1();
while (1) {
uint32_t m;
queue_remove_blocking(&usb_to_card_q, &m);
uint32_t flag = m + 1;
queue_add_blocking(&card_to_usb_q, &flag);
if (m == EV_EXIT) {
break;
}
apdu.sw = cbor_parse(cmd, cbor_data, cbor_len);
apdu.sw = cbor_parse(cbor_cmd, cbor_data, cbor_len);
if (apdu.sw == 0) {
DEBUG_DATA(res_APDU + 1, res_APDU_size);
}
else {
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;
uint32_t flag = EV_EXEC_FINISHED;
flag = EV_EXEC_FINISHED;
queue_add_blocking(&card_to_usb_q, &flag);
}
}
#ifdef ESP_PLATFORM
vTaskDelete(NULL);
#endif
}
int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len) {
cbor_data = data;
cbor_len = len;
cmd = last_cmd;
cbor_cmd = last_cmd;
ctap_resp->init.data[0] = 0;
res_APDU = ctap_resp->init.data + 1;
res_APDU_size = 0;
return 1;
return 2; // CBOR processing
}
CborError COSE_key_params(int crv, int alg, mbedtls_ecp_group *grp, mbedtls_ecp_point *Q, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder) {
CborError error = CborNoError;
int kty = 1;
if (crv == FIDO2_CURVE_P256 || crv == FIDO2_CURVE_P384 || crv == FIDO2_CURVE_P521 ||
crv == FIDO2_CURVE_P256K1) {
kty = 2;
}
CBOR_CHECK(cbor_encoder_create_map(mapEncoderParent, mapEncoder, kty == 2 ? 5 : 4));
CBOR_CHECK(cbor_encode_uint(mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(mapEncoder, kty));
CBOR_CHECK(cbor_encode_uint(mapEncoder, 3));
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, -alg));
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(mapEncoder, crv));
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, 2));
uint8_t pkey[67];
if (kty == 2) {
size_t plen = mbedtls_mpi_size(&grp->P);
CBOR_CHECK(mbedtls_mpi_write_binary(&Q->X, pkey, plen));
CBOR_CHECK(cbor_encode_byte_string(mapEncoder, pkey, plen));
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, 3));
CBOR_CHECK(mbedtls_mpi_write_binary(&Q->Y, pkey, plen));
CBOR_CHECK(cbor_encode_byte_string(mapEncoder, pkey, plen));
}
else {
size_t olen = 0;
CBOR_CHECK(mbedtls_ecp_point_write_binary(grp, Q, MBEDTLS_ECP_PF_COMPRESSED, &olen, pkey,
sizeof(pkey)));
CBOR_CHECK(cbor_encode_byte_string(mapEncoder, pkey, olen));
}
CBOR_CHECK(cbor_encoder_close_container(mapEncoderParent, mapEncoder));
err:
return error;
}
CborError COSE_key(mbedtls_ecp_keypair *key, CborEncoder *mapEncoderParent,
CborEncoder *mapEncoder) {
int crv = mbedtls_curve_to_fido(key->grp.id), alg = 0;
if (key->grp.id == MBEDTLS_ECP_DP_SECP256R1) {
alg = FIDO2_ALG_ES256;
}
else if (key->grp.id == MBEDTLS_ECP_DP_SECP384R1) {
alg = FIDO2_ALG_ES384;
}
else if (key->grp.id == MBEDTLS_ECP_DP_SECP521R1) {
alg = FIDO2_ALG_ES512;
}
else if (key->grp.id == MBEDTLS_ECP_DP_SECP256K1) {
alg = FIDO2_ALG_ES256K;
}
else if (key->grp.id == MBEDTLS_ECP_DP_CURVE25519) {
alg = FIDO2_ALG_ECDH_ES_HKDF_256;
}
return COSE_key_params(crv, alg, &key->grp, &key->Q, mapEncoderParent, mapEncoder);
}
CborError COSE_key_shared(mbedtls_ecdh_context *key,
CborEncoder *mapEncoderParent,
CborEncoder *mapEncoder) {
int crv = mbedtls_curve_to_fido(key->ctx.mbed_ecdh.grp.id), alg = FIDO2_ALG_ECDH_ES_HKDF_256;
return COSE_key_params(crv, alg, &key->ctx.mbed_ecdh.grp, &key->ctx.mbed_ecdh.Q, mapEncoderParent, mapEncoder);
}
CborError COSE_public_key(int alg, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder) {
CborError error = CborNoError;
CBOR_CHECK(cbor_encoder_create_map(mapEncoderParent, mapEncoder, 2));
CBOR_CHECK(cbor_encode_text_stringz(mapEncoder, "alg"));
CBOR_CHECK(cbor_encode_negative_int(mapEncoder, -alg));
CBOR_CHECK(cbor_encode_text_stringz(mapEncoder, "type"));
CBOR_CHECK(cbor_encode_text_stringz(mapEncoder, "public-key"));
CBOR_CHECK(cbor_encoder_close_container(mapEncoderParent, mapEncoder));
err:
return error;
}
CborError COSE_read_key(CborValue *f, int64_t *kty, int64_t *alg, int64_t *crv, CborByteString *kax, CborByteString *kay) {
int64_t kkey = 0;
CborError error = CborNoError;
CBOR_PARSE_MAP_START(*f, 0)
{
CBOR_FIELD_GET_INT(kkey, 0);
if (kkey == 1) {
CBOR_FIELD_GET_INT(*kty, 0);
}
else if (kkey == 3) {
CBOR_FIELD_GET_INT(*alg, 0);
}
else if (kkey == -1) {
CBOR_FIELD_GET_INT(*crv, 0);
}
else if (kkey == -2) {
CBOR_FIELD_GET_BYTES(*kax, 0);
}
else if (kkey == -3) {
CBOR_FIELD_GET_BYTES(*kay, 0);
}
else {
CBOR_ADVANCE(0);
}
}
CBOR_PARSE_MAP_END(*f, 0);
err:
return error;
}

View File

@@ -15,7 +15,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ESP_PLATFORM
#include "common.h"
#else
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#endif
#include "mbedtls/ecp.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/sha256.h"
@@ -23,7 +27,7 @@
#include "cbor.h"
#include "ctap.h"
#include "ctap2_cbor.h"
#ifndef ENABLE_EMULATION
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include "bsp/board.h"
#endif
#include "hid/ctap_hid.h"
@@ -31,8 +35,9 @@
#include "files.h"
#include "random.h"
#include "crypto_utils.h"
#include "hsm.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;
@@ -169,7 +174,7 @@ int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret) {
int resetPinUvAuthToken() {
uint8_t t[32];
random_gen(NULL, t, sizeof(t));
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
file_put_data(ef_authtoken, t, sizeof(t));
paut.permissions = 0;
paut.data = file_get_data(ef_authtoken);
paut.len = file_get_size(ef_authtoken);
@@ -178,28 +183,28 @@ int resetPinUvAuthToken() {
return 0;
}
int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) {
int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out) {
if (protocol == 1) {
memcpy(out, in, in_len);
return aes_encrypt(key, NULL, 32 * 8, HSM_AES_MODE_CBC, out, in_len);
return aes_encrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len);
}
else if (protocol == 2) {
random_gen(NULL, out, IV_SIZE);
memcpy(out + IV_SIZE, in, in_len);
return aes_encrypt(key + 32, out, 32 * 8, HSM_AES_MODE_CBC, out + IV_SIZE, in_len);
return aes_encrypt(key + 32, out, 32 * 8, PICO_KEYS_AES_MODE_CBC, out + IV_SIZE, in_len);
}
return -1;
}
int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_len, uint8_t *out) {
int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out) {
if (protocol == 1) {
memcpy(out, in, in_len);
return aes_decrypt(key, NULL, 32 * 8, HSM_AES_MODE_CBC, out, in_len);
return aes_decrypt(key, NULL, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len);
}
else if (protocol == 2) {
memcpy(out, in + IV_SIZE, in_len);
return aes_decrypt(key + 32, in, 32 * 8, HSM_AES_MODE_CBC, out, in_len - IV_SIZE);
memcpy(out, in + IV_SIZE, in_len - IV_SIZE);
return aes_decrypt(key + 32, in, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len - IV_SIZE);
}
return -1;
@@ -228,12 +233,11 @@ int authenticate(uint8_t protocol,
return 0;
}
int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign) {
int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, uint16_t len, uint8_t *sign) {
uint8_t hmac[32];
//if (paut.in_use == false)
// return -2;
int ret =
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
int ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
if (ret != 0) {
return ret;
}
@@ -276,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) {
@@ -312,30 +331,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
CBOR_FIELD_GET_UINT(subcommand, 1);
}
else if (val_u == 0x03) {
int64_t key = 0;
CBOR_PARSE_MAP_START(_f1, 2)
{
CBOR_FIELD_GET_INT(key, 2);
if (key == 1) {
CBOR_FIELD_GET_INT(kty, 2);
}
else if (key == 3) {
CBOR_FIELD_GET_INT(alg, 2);
}
else if (key == -1) {
CBOR_FIELD_GET_INT(crv, 2);
}
else if (key == -2) {
CBOR_FIELD_GET_BYTES(kax, 2);
}
else if (key == -3) {
CBOR_FIELD_GET_BYTES(kay, 2);
}
else {
CBOR_ADVANCE(2);
}
}
CBOR_PARSE_MAP_END(_f1, 2);
CBOR_CHECK(COSE_read_key(&_f1, &kty, &alg, &crv, &kax, &kay));
}
else if (val_u == 0x04) {
CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1);
@@ -355,7 +351,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
}
CBOR_PARSE_MAP_END(map, 1);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
if (subcommand == 0x0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
@@ -374,21 +370,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ECDH_ES_HKDF_256));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, FIDO2_CURVE_P256));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
uint8_t pkey[32];
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.X, pkey, 32);
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.Y, pkey, 32);
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
CBOR_CHECK(COSE_key_shared(&hkey, &mapEncoder, &mapEncoder2));
}
else if (pinUvAuthProtocol == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
@@ -419,18 +401,17 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
uint8_t sharedSecret[64];
int ret = ecdh(pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
if (ret != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (verify(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len,
pinUvAuthParam.data) != 0) {
if (verify((uint8_t)pinUvAuthProtocol, sharedSecret, newPinEnc.data, (uint16_t)newPinEnc.len, pinUvAuthParam.data) != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
uint8_t paddedNewPin[64];
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, newPinEnc.data, (uint16_t)newPinEnc.len, paddedNewPin);
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
@@ -450,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);
flash_write_data_to_file(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
@@ -485,7 +474,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
uint8_t sharedSecret[64];
int ret = ecdh(pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
if (ret != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
@@ -493,25 +482,25 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
uint8_t tmp[80 + 32];
memcpy(tmp, newPinEnc.data, newPinEnc.len);
memcpy(tmp + newPinEnc.len, pinHashEnc.data, pinHashEnc.len);
if (verify(pinUvAuthProtocol, sharedSecret, tmp, newPinEnc.len + pinHashEnc.len,
pinUvAuthParam.data) != 0) {
if (verify((uint8_t)pinUvAuthProtocol, sharedSecret, tmp, (uint16_t)(newPinEnc.len + pinHashEnc.len), pinUvAuthParam.data) != 0) {
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;
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
file_put_data(ef_pin, pin_data, sizeof(pin_data));
low_flash_available();
uint8_t retries = pin_data[0];
uint8_t paddedNewPin[64];
ret =
decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, pinHashEnc.data, (uint16_t)pinHashEnc.len, paddedNewPin);
if (ret != 0) {
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) {
@@ -525,11 +514,12 @@ 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;
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
file_put_data(ef_pin, pin_data, sizeof(pin_data));
low_flash_available();
new_pin_mismatches = 0;
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, newPinEnc.data, (uint16_t)newPinEnc.len, paddedNewPin);
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
@@ -549,21 +539,42 @@ 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[33];
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);
}
flash_write_data_to_file(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 *tmp = (uint8_t *) calloc(1, file_get_size(ef_minpin));
memcpy(tmp, file_get_data(ef_minpin), file_get_size(ef_minpin));
tmp[1] = 0;
flash_write_data_to_file(ef_minpin, tmp, file_get_size(ef_minpin));
free(tmp);
uint8_t *tmpf = (uint8_t *) calloc(1, file_get_size(ef_minpin));
memcpy(tmpf, file_get_data(ef_minpin), file_get_size(ef_minpin));
tmpf[1] = 0;
file_put_data(ef_minpin, tmpf, file_get_size(ef_minpin));
free(tmpf);
}
low_flash_available();
resetPinUvAuthToken();
@@ -602,25 +613,26 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
uint8_t sharedSecret[64];
int ret = ecdh(pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
int ret = ecdh((uint8_t)pinUvAuthProtocol, &hkey.ctx.mbed_ecdh.Qp, sharedSecret);
if (ret != 0) {
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;
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
file_put_data(ef_pin, pin_data, sizeof(pin_data));
low_flash_available();
uint8_t retries = pin_data[0];
uint8_t paddedNewPin[64], poff = (pinUvAuthProtocol - 1) * IV_SIZE;
ret =
decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
uint8_t paddedNewPin[64], poff = ((uint8_t)pinUvAuthProtocol - 1) * IV_SIZE;
ret = decrypt((uint8_t)pinUvAuthProtocol, sharedSecret, pinHashEnc.data, (uint16_t)pinHashEnc.len, paddedNewPin);
if (ret != 0) {
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) {
@@ -634,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;
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
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) {
@@ -647,7 +669,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
if (subcommand == 0x05) {
permissions = CTAP_PERMISSION_MC | CTAP_PERMISSION_GA;
}
paut.permissions = permissions;
paut.permissions = (uint8_t)permissions;
if (rpId.present == true) {
mbedtls_sha256((uint8_t *) rpId.data, rpId.len, paut.rp_id_hash, 0);
paut.has_rp_id = true;
@@ -656,7 +678,7 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
paut.has_rp_id = false;
}
uint8_t pinUvAuthToken_enc[32 + IV_SIZE];
encrypt(pinUvAuthProtocol, sharedSecret, paut.data, 32, pinUvAuthToken_enc);
encrypt((uint8_t)pinUvAuthProtocol, sharedSecret, paut.data, 32, pinUvAuthToken_enc);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32 + poff));
@@ -679,6 +701,6 @@ err:
}
return error;
}
res_APDU_size = resp_size;
res_APDU_size = (uint16_t)resp_size;
return 0;
}

View File

@@ -22,11 +22,12 @@
#include "files.h"
#include "apdu.h"
#include "credential.h"
#include "hsm.h"
#include "pico_keys.h"
#include "random.h"
#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,11 +36,12 @@ 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;
CborEncoder encoder, mapEncoder;
CborEncoder encoder;
//CborEncoder mapEncoder;
uint8_t *raw_subpara = NULL;
const bool *forceChangePin = NULL;
@@ -64,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 == 0xff) {
if (subcommand == 0x7f) { // Config Aut
CBOR_FIELD_GET_UINT(subpara, 2);
if (subpara == 0x01) {
CBOR_FIELD_GET_UINT(vendorCommandId, 2);
@@ -73,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);
@@ -93,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;
@@ -106,7 +117,7 @@ int cbor_config(const uint8_t *data, size_t len) {
}
CBOR_PARSE_MAP_END(map, 1);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
if (pinUvAuthParam.present == false) {
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
@@ -118,13 +129,9 @@ int cbor_config(const uint8_t *data, size_t len) {
uint8_t *verify_payload = (uint8_t *) calloc(1, 32 + 1 + 1 + raw_subpara_len);
memset(verify_payload, 0xff, 32);
verify_payload[32] = 0x0d;
verify_payload[33] = subcommand;
verify_payload[33] = (uint8_t)subcommand;
memcpy(verify_payload + 34, raw_subpara, raw_subpara_len);
error = verify(pinUvAuthProtocol,
paut.data,
verify_payload,
32 + 1 + 1 + raw_subpara_len,
pinUvAuthParam.data);
error = verify((uint8_t)pinUvAuthProtocol, paut.data, verify_payload, (uint16_t)(32 + 1 + 1 + raw_subpara_len), pinUvAuthParam.data);
free(verify_payload);
if (error != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
@@ -134,7 +141,7 @@ int cbor_config(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (subcommand == 0xff) {
if (subcommand == 0x7f) {
if (vendorCommandId == CTAP_CONFIG_AUT_DISABLE) {
if (!file_has_data(ef_keydev_enc)) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
@@ -142,9 +149,9 @@ int cbor_config(const uint8_t *data, size_t len) {
if (has_keydev_dec == false) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
flash_write_data_to_file(ef_keydev, keydev_dec, sizeof(keydev_dec));
file_put_data(ef_keydev, keydev_dec, sizeof(keydev_dec));
mbedtls_platform_zeroize(keydev_dec, sizeof(keydev_dec));
flash_write_data_to_file(ef_keydev_enc, NULL, 0); // Set ef to 0 bytes
file_put_data(ef_keydev_enc, NULL, 0); // Set ef to 0 bytes
low_flash_available();
}
else if (vendorCommandId == CTAP_CONFIG_AUT_ENABLE) {
@@ -165,23 +172,16 @@ int cbor_config(const uint8_t *data, size_t len) {
random_gen(NULL, key_dev_enc, 12);
mbedtls_chachapoly_init(&chatx);
mbedtls_chachapoly_setkey(&chatx, vendorAutCt.data);
ret = mbedtls_chachapoly_encrypt_and_tag(&chatx,
file_get_size(ef_keydev),
key_dev_enc,
NULL,
0,
file_get_data(ef_keydev),
key_dev_enc + 12,
key_dev_enc + 12 + file_get_size(ef_keydev));
ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, file_get_size(ef_keydev), key_dev_enc, NULL, 0, file_get_data(ef_keydev), key_dev_enc + 12, key_dev_enc + 12 + file_get_size(ef_keydev));
mbedtls_chachapoly_free(&chatx);
if (ret != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
flash_write_data_to_file(ef_keydev_enc, key_dev_enc, sizeof(key_dev_enc));
file_put_data(ef_keydev_enc, key_dev_enc, sizeof(key_dev_enc));
mbedtls_platform_zeroize(key_dev_enc, sizeof(key_dev_enc));
flash_write_data_to_file(ef_keydev, key_dev_enc, file_get_size(ef_keydev)); // Overwrite ef with 0
flash_write_data_to_file(ef_keydev, NULL, 0); // Set ef to 0 bytes
file_put_data(ef_keydev, key_dev_enc, file_get_size(ef_keydev)); // Overwrite ef with 0
file_put_data(ef_keydev, NULL, 0); // Set ef to 0 bytes
low_flash_available();
}
else {
@@ -207,33 +207,60 @@ int cbor_config(const uint8_t *data, size_t len) {
if (file_has_data(ef_pin) && file_get_data(ef_pin)[1] < newMinPinLength) {
forceChangePin = ptrue;
}
uint8_t *data = (uint8_t *) calloc(1, 2 + minPinLengthRPIDs_len * 32);
data[0] = newMinPinLength;
data[1] = forceChangePin == ptrue ? 1 : 0;
for (int m = 0; m < minPinLengthRPIDs_len; m++) {
mbedtls_sha256((uint8_t *) minPinLengthRPIDs[m].data,
minPinLengthRPIDs[m].len,
data + 2 + m * 32,
0);
uint8_t *dataf = (uint8_t *) calloc(1, 2 + minPinLengthRPIDs_len * 32);
dataf[0] = (uint8_t)newMinPinLength;
dataf[1] = forceChangePin == ptrue ? 1 : 0;
for (size_t m = 0; m < minPinLengthRPIDs_len; m++) {
mbedtls_sha256((uint8_t *) minPinLengthRPIDs[m].data, minPinLengthRPIDs[m].len, dataf + 2 + m * 32, 0);
}
flash_write_data_to_file(ef_minpin, data, 2 + minPinLengthRPIDs_len * 32);
file_put_data(ef_minpin, dataf, (uint16_t)(2 + minPinLengthRPIDs_len * 32));
low_flash_available();
free(dataf);
goto err; //No return
}
else if (subcommand == 0x01) {
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);
}
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
//CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
//resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
err:
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
CBOR_FREE_BYTE_STRING(vendorAutCt);
for (int i = 0; i < minPinLengthRPIDs_len; i++) {
for (size_t i = 0; i < minPinLengthRPIDs_len; i++) {
CBOR_FREE_BYTE_STRING(minPinLengthRPIDs[i]);
}
@@ -243,6 +270,6 @@ err:
}
return error;
}
res_APDU_size = resp_size;
res_APDU_size = (uint16_t)resp_size;
return 0;
}

View File

@@ -22,7 +22,7 @@
#include "files.h"
#include "apdu.h"
#include "credential.h"
#include "hsm.h"
#include "pico_keys.h"
uint8_t rp_counter = 1;
uint8_t rp_total = 0;
@@ -120,10 +120,9 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
}
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
if (subcommand == 0x01) {
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *) "\x01", 1,
pinUvAuthParam.data) != CborNoError) {
if (verify((uint8_t)pinUvAuthProtocol, paut.data, (const uint8_t *) "\x01", 1, pinUvAuthParam.data) != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (is_preview == false &&
@@ -132,7 +131,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
uint8_t existing = 0;
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
if (file_has_data(search_dynamic_file(EF_CRED + i))) {
if (file_has_data(search_dynamic_file((uint16_t)(EF_CRED + i)))) {
existing++;
}
}
@@ -145,12 +144,10 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
else if (subcommand == 0x02 || subcommand == 0x03) {
file_t *rp_ef = NULL;
if (subcommand == 0x02) {
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *) "\x02", 1,
pinUvAuthParam.data) != CborNoError) {
if (verify((uint8_t)pinUvAuthProtocol, paut.data, (const uint8_t *) "\x02", 1, pinUvAuthParam.data) != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (is_preview == false &&
(!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) {
if (is_preview == false && (!(paut.permissions & CTAP_PERMISSION_CM) || paut.has_rp_id == true)) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
rp_counter = 1;
@@ -163,7 +160,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
uint8_t skip = 0;
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *tef = search_dynamic_file(EF_RP + i);
file_t *tef = search_dynamic_file((uint16_t)(EF_RP + i));
if (file_has_data(tef) && *file_get_data(tef) > 0) {
if (++skip == rp_counter) {
if (rp_ef == NULL) {
@@ -202,8 +199,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
if (subcommand == 0x04) {
*(raw_subpara - 1) = 0x04;
if (verify(pinUvAuthProtocol, paut.data, raw_subpara - 1, raw_subpara_len + 1,
pinUvAuthParam.data) != CborNoError) {
if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (is_preview == false &&
@@ -223,7 +219,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
file_t *cred_ef = NULL;
uint8_t skip = 0;
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *tef = search_dynamic_file(EF_CRED + i);
file_t *tef = search_dynamic_file((uint16_t)(EF_CRED + i));
if (file_has_data(tef) && memcmp(file_get_data(tef), rpIdHash.data, 32) == 0) {
if (++skip == cred_counter) {
if (cred_ef == NULL) {
@@ -243,14 +239,13 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
Credential cred = { 0 };
if (credential_load(file_get_data(cred_ef) + 32, file_get_size(cred_ef) - 32, rpIdHash.data,
&cred) != 0) {
if (credential_load(file_get_data(cred_ef) + 32, file_get_size(cred_ef) - 32, rpIdHash.data, &cred) != 0) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
mbedtls_ecdsa_context key;
mbedtls_ecdsa_init(&key);
if (fido_load_key(cred.curve, cred.id.data, &key) != 0) {
if (fido_load_key((int)cred.curve, cred.id.data, &key) != 0) {
credential_free(&cred);
mbedtls_ecdsa_free(&key);
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
@@ -258,7 +253,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
cred_counter++;
uint8_t l = 3;
uint8_t l = 4;
if (subcommand == 0x04) {
l++;
}
@@ -290,13 +285,11 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
if (cred.userName.present == true) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "name"));
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, cred.userName.data,
cred.userName.len));
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, cred.userName.data, cred.userName.len));
}
if (cred.userDisplayName.present == true) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "displayName"));
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, cred.userDisplayName.data,
cred.userDisplayName.len));
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, cred.userDisplayName.data, cred.userDisplayName.len));
}
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
@@ -309,21 +302,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -cred.alg));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, cred.curve));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
uint8_t pkey[66];
mbedtls_mpi_write_binary(&key.Q.X, pkey, mbedtls_mpi_size(&key.Q.X));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, mbedtls_mpi_size(&key.Q.X)));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
mbedtls_mpi_write_binary(&key.Q.Y, pkey, mbedtls_mpi_size(&key.Q.Y));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, mbedtls_mpi_size(&key.Q.Y)));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
CBOR_CHECK(COSE_key(&key, &mapEncoder, &mapEncoder2));
if (subcommand == 0x04) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x09));
@@ -345,10 +324,15 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey,
sizeof(largeBlobKey)));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey));
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, cred.extensions.thirdPartyPayment == ptrue));
}
else {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false));
}
credential_free(&cred);
mbedtls_ecdsa_free(&key);
@@ -358,8 +342,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
*(raw_subpara - 1) = 0x06;
if (verify(pinUvAuthProtocol, paut.data, raw_subpara - 1, raw_subpara_len + 1,
pinUvAuthParam.data) != CborNoError) {
if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (is_preview == false &&
@@ -368,18 +351,15 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef = search_dynamic_file(EF_CRED + i);
if (file_has_data(ef) &&
memcmp(file_get_data(ef) + 32, credentialId.id.data,
MIN(file_get_size(ef) - 32, credentialId.id.len)) == 0) {
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
if (file_has_data(ef) && memcmp(file_get_data(ef) + 32, credentialId.id.data, MIN(file_get_size(ef) - 32, credentialId.id.len)) == 0) {
uint8_t *rp_id_hash = file_get_data(ef);
if (delete_file(ef) != 0) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
for (int j = 0; j < MAX_RESIDENT_CREDENTIALS; j++) {
file_t *rp_ef = search_dynamic_file(EF_RP + j);
if (file_has_data(rp_ef) &&
memcmp(file_get_data(rp_ef) + 1, rp_id_hash, 32) == 0) {
file_t *rp_ef = search_dynamic_file((uint16_t)(EF_RP + j));
if (file_has_data(rp_ef) && memcmp(file_get_data(rp_ef) + 1, rp_id_hash, 32) == 0) {
uint8_t *rp_data = (uint8_t *) calloc(1, file_get_size(rp_ef));
memcpy(rp_data, file_get_data(rp_ef), file_get_size(rp_ef));
rp_data[0] -= 1;
@@ -387,7 +367,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
delete_file(rp_ef);
}
else {
flash_write_data_to_file(rp_ef, rp_data, file_get_size(rp_ef));
file_put_data(rp_ef, rp_data, file_get_size(rp_ef));
}
free(rp_data);
break;
@@ -404,8 +384,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
*(raw_subpara - 1) = 0x07;
if (verify(pinUvAuthProtocol, paut.data, raw_subpara - 1, raw_subpara_len + 1,
pinUvAuthParam.data) != CborNoError) {
if (verify((uint8_t)pinUvAuthProtocol, paut.data, raw_subpara - 1, (uint16_t)(raw_subpara_len + 1), pinUvAuthParam.data) != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (is_preview == false &&
@@ -414,18 +393,14 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef = search_dynamic_file(EF_CRED + i);
if (file_has_data(ef) &&
memcmp(file_get_data(ef) + 32, credentialId.id.data,
MIN(file_get_size(ef) - 32, credentialId.id.len)) == 0) {
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
if (file_has_data(ef) && memcmp(file_get_data(ef) + 32, credentialId.id.data, MIN(file_get_size(ef) - 32, credentialId.id.len)) == 0) {
Credential cred = { 0 };
uint8_t *rp_id_hash = file_get_data(ef);
if (credential_load(rp_id_hash + 32, file_get_size(ef) - 32, rp_id_hash,
&cred) != 0) {
if (credential_load(rp_id_hash + 32, file_get_size(ef) - 32, rp_id_hash, &cred) != 0) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
if (memcmp(user.id.data, cred.userId.data,
MIN(user.id.len, cred.userId.len)) != 0) {
if (memcmp(user.id.data, cred.userId.data, MIN(user.id.len, cred.userId.len)) != 0) {
credential_free(&cred);
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
@@ -433,8 +408,8 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
size_t newcred_len = 0;
if (credential_create(&cred.rpId, &cred.userId, &user.parent.name,
&user.displayName, &cred.opts, &cred.extensions,
cred.use_sign_count, cred.alg,
cred.curve, newcred, &newcred_len) != 0) {
cred.use_sign_count, (int)cred.alg,
(int)cred.curve, newcred, &newcred_len) != 0) {
credential_free(&cred);
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
@@ -460,7 +435,8 @@ err:
CBOR_FREE_BYTE_STRING(user.displayName);
CBOR_FREE_BYTE_STRING(user.parent.name);
CBOR_FREE_BYTE_STRING(credentialId.type);
for (int n = 0; n < credentialId.transports_len; n++) {
CBOR_FREE_BYTE_STRING(credentialId.id);
for (size_t n = 0; n < credentialId.transports_len; n++) {
CBOR_FREE_BYTE_STRING(credentialId.transports[n]);
}
if (error != CborNoError) {
@@ -469,6 +445,6 @@ err:
}
return error;
}
res_APDU_size = resp_size;
res_APDU_size = (uint16_t)resp_size;
return 0;
}

View File

@@ -17,14 +17,14 @@
#include "cbor.h"
#include "ctap.h"
#ifndef ENABLE_EMULATION
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include "bsp/board.h"
#endif
#include "hid/ctap_hid.h"
#include "fido.h"
#include "files.h"
#include "crypto_utils.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
#include "cbor_make_credential.h"
#include "credential.h"
@@ -43,6 +43,8 @@ uint8_t *datax = NULL;
size_t lenx = 0;
int cbor_get_next_assertion(const uint8_t *data, size_t len) {
(void) data;
(void) len;
CborError error = CborNoError;
if (credentialCounter >= numberOfCredentialsx) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
@@ -150,30 +152,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
{
CBOR_FIELD_GET_UINT(ukey, 3);
if (ukey == 0x01) {
int64_t kkey = 0;
CBOR_PARSE_MAP_START(_f3, 4)
{
CBOR_FIELD_GET_INT(kkey, 4);
if (kkey == 1) {
CBOR_FIELD_GET_INT(kty, 4);
}
else if (kkey == 3) {
CBOR_FIELD_GET_INT(alg, 4);
}
else if (kkey == -1) {
CBOR_FIELD_GET_INT(crv, 4);
}
else if (kkey == -2) {
CBOR_FIELD_GET_BYTES(kax, 4);
}
else if (kkey == -3) {
CBOR_FIELD_GET_BYTES(kay, 4);
}
else {
CBOR_ADVANCE(4);
}
}
CBOR_PARSE_MAP_END(_f3, 4);
CBOR_CHECK(COSE_read_key(&_f3, &kty, &alg, &crv, &kax, &kay));
}
else if (ukey == 0x02) {
CBOR_FIELD_GET_BYTES(salt_enc, 3);
@@ -193,6 +172,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "credBlob", credBlob);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", extensions.largeBlobKey);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "thirdPartyPayment", extensions.thirdPartyPayment);
CBOR_ADVANCE(2);
}
CBOR_PARSE_MAP_END(_f1, 2);
@@ -223,7 +203,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
uint8_t flags = 0;
uint8_t rp_id_hash[32];
uint8_t rp_id_hash[32] = {0};
mbedtls_sha256((uint8_t *) rpId.data, rpId.len, rp_id_hash, 0);
bool resident = false;
@@ -272,11 +252,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
if (pinUvAuthParam.present == true) { //6.1
int ret = verify(pinUvAuthProtocol,
paut.data,
clientDataHash.data,
clientDataHash.len,
pinUvAuthParam.data);
int ret = verify((uint8_t)pinUvAuthProtocol, paut.data, clientDataHash.data, (uint16_t)clientDataHash.len, pinUvAuthParam.data);
if (ret != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
@@ -303,17 +279,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
}
bool silent = (up == false && uv == false);
if (allowList_len > 0) {
for (int e = 0; e < allowList_len; e++) {
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_load(allowList[e].id.data, allowList[e].id.len, rp_id_hash,
&creds[creds_len]) != 0) {
CBOR_FREE_BYTE_STRING(allowList[e].id);
if (credential_load(allowList[e].id.data, allowList[e].id.len, rp_id_hash, &creds[creds_len]) != 0) {
credential_free(&creds[creds_len]);
}
else {
@@ -322,17 +298,12 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
}
else {
for (int i = 0;
i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST;
i++) {
file_t *ef = search_dynamic_file(EF_CRED + i);
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
file_t *ef = search_dynamic_file((uint16_t)(EF_CRED + i));
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
continue;
}
int ret = credential_load(file_get_data(ef) + 32,
file_get_size(ef) - 32,
rp_id_hash,
&creds[creds_len]);
int ret = credential_load(file_get_data(ef) + 32, file_get_size(ef) - 32, rp_id_hash, &creds[creds_len]);
if (ret != 0) {
credential_free(&creds[creds_len]);
}
@@ -342,11 +313,10 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
resident = true;
}
for (int i = 0; i < creds_len; i++) {
for (size_t i = 0; i < creds_len; i++) {
if (creds[i].present == true) {
if (creds[i].extensions.present == true) {
if (creds[i].extensions.credProtect == CRED_PROT_UV_REQUIRED &&
!(flags & FIDO2_AUT_FLAG_UV)) {
if (creds[i].extensions.credProtect == CRED_PROT_UV_REQUIRED && !(flags & FIDO2_AUT_FLAG_UV)) {
credential_free(&creds[i]);
}
else if (creds[i].extensions.credProtect == CRED_PROT_UV_OPTIONAL_WITH_LIST &&
@@ -354,24 +324,51 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
credential_free(&creds[i]);
}
else {
creds[numberOfCredentials++] = creds[i];
if (numberOfCredentials != i) {
creds[numberOfCredentials++] = creds[i];
}
else {
numberOfCredentials++;
}
}
}
else {
creds[numberOfCredentials++] = creds[i];
if (numberOfCredentials != i) {
creds[numberOfCredentials++] = creds[i];
}
else {
numberOfCredentials++;
}
}
}
}
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;
}
}
}
}
@@ -401,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];
@@ -428,27 +425,21 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
flags = flagsx;
selcred = &credsx[credentialCounter];
}
mbedtls_ecdsa_context ekey;
mbedtls_ecdsa_init(&ekey);
int ret = fido_load_key(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);
}
}
uint8_t largeBlobKey[32];
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);
int ret = 0;
uint8_t largeBlobKey[32] = {0};
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];
if (extensions.present == true) {
uint8_t ext[512] = {0};
if (selcred && extensions.present == true) {
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
int l = 0;
if (options.up == pfalse) {
@@ -460,6 +451,9 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
if (credBlob == ptrue) {
l++;
}
if (extensions.thirdPartyPayment != NULL) {
l++;
}
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
if (credBlob == ptrue) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
@@ -475,7 +469,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "hmac-secret"));
uint8_t sharedSecret[64];
uint8_t sharedSecret[64] = {0};
mbedtls_ecp_point Qp;
mbedtls_ecp_point_init(&Qp);
mbedtls_mpi_lset(&Qp.Z, 1);
@@ -487,28 +481,23 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
mbedtls_ecp_point_free(&Qp);
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
int ret = ecdh(hmacSecretPinUvAuthProtocol, &Qp, sharedSecret);
ret = ecdh((uint8_t)hmacSecretPinUvAuthProtocol, &Qp, sharedSecret);
mbedtls_ecp_point_free(&Qp);
if (ret != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (verify(hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, salt_enc.len,
salt_auth.data) != 0) {
if (verify((uint8_t)hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, (uint16_t)salt_enc.len, salt_auth.data) != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP2_ERR_EXTENSION_FIRST);
}
uint8_t salt_dec[64], poff = (hmacSecretPinUvAuthProtocol - 1) * IV_SIZE;
ret = decrypt(hmacSecretPinUvAuthProtocol,
sharedSecret,
salt_enc.data,
salt_enc.len,
salt_dec);
uint8_t salt_dec[64] = {0}, poff = ((uint8_t)hmacSecretPinUvAuthProtocol - 1) * IV_SIZE;
ret = decrypt((uint8_t)hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, (uint16_t)salt_enc.len, salt_dec);
if (ret != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
uint8_t cred_random[64], *crd = NULL;
uint8_t cred_random[64] = {0}, *crd = NULL;
ret = credential_derive_hmac_key(selcred->id.data, selcred->id.len, cred_random);
if (ret != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
@@ -520,24 +509,23 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
else {
crd = cred_random;
}
uint8_t out1[64], hmac_res[80];
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
crd,
32,
salt_dec,
32,
out1);
if (salt_enc.len == 64 + poff) {
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
crd,
32,
salt_dec + 32,
32,
out1 + 32);
uint8_t out1[64] = {0}, hmac_res[80] = {0};
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), crd, 32, salt_dec, 32, out1);
if ((uint8_t)salt_enc.len == 64 + poff) {
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), crd, 32, salt_dec + 32, 32, out1 + 32);
}
encrypt(hmacSecretPinUvAuthProtocol, sharedSecret, out1, salt_enc.len - poff, hmac_res);
encrypt((uint8_t)hmacSecretPinUvAuthProtocol, sharedSecret, out1, (uint16_t)(salt_enc.len - poff), hmac_res);
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, hmac_res, salt_enc.len));
}
if (extensions.thirdPartyPayment != NULL) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "thirdPartyPayment"));
if (selcred->extensions.thirdPartyPayment == ptrue) {
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
}
else {
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false));
}
}
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
@@ -551,50 +539,64 @@ 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;
*pa++ = ctr >> 16;
*pa++ = ctr >> 8;
*pa++ = ctr & 0xff;
pa += put_uint32_t_be(ctr, pa);
memcpy(pa, ext, ext_len); pa += ext_len;
if (pa - aut_data != aut_data_len) {
if ((size_t)(pa - aut_data) != aut_data_len) {
CBOR_ERROR(CTAP1_ERR_OTHER);
}
memcpy(pa, clientDataHash.data, clientDataHash.len);
uint8_t hash[32], sig[MBEDTLS_ECDSA_MAX_LEN];
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
aut_data,
aut_data_len + clientDataHash.len,
hash);
uint8_t hash[64] = {0}, sig[MBEDTLS_ECDSA_MAX_LEN] = {0};
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_ecdsa_context ekey;
mbedtls_ecdsa_init(&ekey);
size_t olen = 0;
ret = mbedtls_ecdsa_write_signature(&ekey,
MBEDTLS_MD_SHA256,
hash,
32,
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_PACKET_SIZE, 0);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, lfields));
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));
@@ -604,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) {
@@ -635,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)));
}
@@ -643,22 +645,26 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
ctr++;
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
low_flash_available();
err:
CBOR_FREE_BYTE_STRING(clientDataHash);
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
CBOR_FREE_BYTE_STRING(rpId);
CBOR_FREE_BYTE_STRING(kax);
CBOR_FREE_BYTE_STRING(kay);
CBOR_FREE_BYTE_STRING(salt_enc);
CBOR_FREE_BYTE_STRING(salt_auth);
if (asserted == false) {
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
credential_free(&creds[i]);
}
}
for (int m = 0; m < allowList_len; m++) {
for (size_t m = 0; m < MAX_CREDENTIAL_COUNT_IN_LIST; m++) {
CBOR_FREE_BYTE_STRING(allowList[m].type);
CBOR_FREE_BYTE_STRING(allowList[m].id);
for (int n = 0; n < allowList[m].transports_len; n++) {
for (size_t n = 0; n < 8; n++) {
CBOR_FREE_BYTE_STRING(allowList[m].transports[n]);
}
}
@@ -671,6 +677,6 @@ err:
}
return error;
}
res_APDU_size = resp_size;
res_APDU_size = (uint16_t)resp_size;
return 0;
}

View File

@@ -26,7 +26,7 @@
int cbor_get_info() {
CborEncoder encoder, mapEncoder, arrayEncoder, mapEncoder2;
CborError error = CborNoError;
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 15));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
@@ -37,12 +37,13 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 5));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 6));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credBlob"));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credProtect"));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "hmac-secret"));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "largeBlobKey"));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "minPinLength"));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "thirdPartyPayment"));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
@@ -89,25 +90,11 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CRED_ID_LENGTH)); // MAX_CRED_ID_MAX_LENGTH
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3));
CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES256));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2));
CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES384));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2));
CBOR_CHECK(cbor_encoder_create_map(&arrayEncoder, &mapEncoder2, 2));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ES512));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "type"));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "public-key"));
CBOR_CHECK(cbor_encoder_close_container(&arrayEncoder, &mapEncoder2));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 4));
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES256, &arrayEncoder, &mapEncoder2));
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES384, &arrayEncoder, &mapEncoder2));
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES512, &arrayEncoder, &mapEncoder2));
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES256K, &arrayEncoder, &mapEncoder2));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
@@ -146,6 +133,6 @@ err:
if (error != CborNoError) {
return -CTAP2_ERR_INVALID_CBOR;
}
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
res_APDU_size = (uint16_t)cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
return 0;
}

View File

@@ -21,7 +21,7 @@
#include "hid/ctap_hid.h"
#include "files.h"
#include "apdu.h"
#include "hsm.h"
#include "pico_keys.h"
#include "mbedtls/sha256.h"
static uint64_t expectedLength = 0, expectedNextOffset = 0;
@@ -79,7 +79,7 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
if (get > 0) {
if (length != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
@@ -129,13 +129,9 @@ 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;
verify_data[36] = offset >> 16;
verify_data[37] = offset >> 24;
put_uint32_t_le(offset, verify_data + 34);
mbedtls_sha256(set.data, set.len, verify_data + 38, 0);
if (verify(pinUvAuthProtocol, paut.data, verify_data, sizeof(verify_data),
pinUvAuthParam.data) != 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);
}
if (!(paut.permissions & CTAP_PERMISSION_LBW)) {
@@ -155,7 +151,7 @@ int cbor_large_blobs(const uint8_t *data, size_t len) {
if (expectedLength > 17 && memcmp(sha, temp_lba + expectedLength - 16, 16) != 0) {
CBOR_ERROR(CTAP2_ERR_INTEGRITY_FAILURE);
}
flash_write_data_to_file(ef_largeblob, temp_lba, expectedLength);
file_put_data(ef_largeblob, temp_lba, (uint16_t)expectedLength);
low_flash_available();
}
goto err;
@@ -168,6 +164,6 @@ err:
if (error != CborNoError) {
return -CTAP2_ERR_INVALID_CBOR;
}
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
res_APDU_size = (uint16_t)cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
return 0;
}

View File

@@ -25,7 +25,7 @@
#include "credential.h"
#include "mbedtls/sha256.h"
#include "random.h"
#include "hsm.h"
#include "pico_keys.h"
int cbor_make_credential(const uint8_t *data, size_t len) {
CborParser parser;
@@ -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));
@@ -65,7 +65,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_FIELD_GET_BYTES(clientDataHash, 1);
}
else if (val_u == 0x02) { // rp
CBOR_PARSE_MAP_START(_f1, 2) {
CBOR_PARSE_MAP_START(_f1, 2)
{
CBOR_FIELD_GET_KEY_TEXT(2);
CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "id", rp.id);
CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "name", rp.parent.name);
@@ -73,7 +74,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_PARSE_MAP_END(_f1, 2);
}
else if (val_u == 0x03) { // user
CBOR_PARSE_MAP_START(_f1, 2) {
CBOR_PARSE_MAP_START(_f1, 2)
{
CBOR_FIELD_GET_KEY_TEXT(2);
CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "id", user.id);
CBOR_FIELD_KEY_TEXT_VAL_TEXT(2, "name", user.parent.name);
@@ -83,9 +85,11 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_PARSE_MAP_END(_f1, 2);
}
else if (val_u == 0x04) { // pubKeyCredParams
CBOR_PARSE_ARRAY_START(_f1, 2) {
CBOR_PARSE_ARRAY_START(_f1, 2)
{
PublicKeyCredentialParameters *pk = &pubKeyCredParams[pubKeyCredParams_len];
CBOR_PARSE_MAP_START(_f2, 3) {
CBOR_PARSE_MAP_START(_f2, 3)
{
CBOR_FIELD_GET_KEY_TEXT(3);
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", pk->type);
CBOR_FIELD_KEY_TEXT_VAL_INT(3, "alg", pk->alg);
@@ -96,14 +100,17 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_PARSE_ARRAY_END(_f1, 2);
}
else if (val_u == 0x05) { // excludeList
CBOR_PARSE_ARRAY_START(_f1, 2) {
CBOR_PARSE_ARRAY_START(_f1, 2)
{
PublicKeyCredentialDescriptor *pc = &excludeList[excludeList_len];
CBOR_PARSE_MAP_START(_f2, 3) {
CBOR_PARSE_MAP_START(_f2, 3)
{
CBOR_FIELD_GET_KEY_TEXT(3);
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", pc->id);
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", pc->type);
if (strcmp(_fd3, "transports") == 0) {
CBOR_PARSE_ARRAY_START(_f3, 4) {
CBOR_PARSE_ARRAY_START(_f3, 4)
{
CBOR_FIELD_GET_TEXT(pc->transports[pc->transports_len], 4);
pc->transports_len++;
}
@@ -117,20 +124,23 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
else if (val_u == 0x06) { // extensions
extensions.present = true;
CBOR_PARSE_MAP_START(_f1, 2) {
CBOR_PARSE_MAP_START(_f1, 2)
{
CBOR_FIELD_GET_KEY_TEXT(2);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "hmac-secret", extensions.hmac_secret);
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", extensions.credProtect);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "minPinLength", extensions.minPinLength);
CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "credBlob", extensions.credBlob);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", extensions.largeBlobKey);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "thirdPartyPayment", extensions.thirdPartyPayment);
CBOR_ADVANCE(2);
}
CBOR_PARSE_MAP_END(_f1, 2);
}
else if (val_u == 0x07) { // options
options.present = true;
CBOR_PARSE_MAP_START(_f1, 2) {
CBOR_PARSE_MAP_START(_f1, 2)
{
CBOR_FIELD_GET_KEY_TEXT(2);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "rk", options.rk);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "up", options.up);
@@ -152,48 +162,9 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_PARSE_MAP_END(map, 1);
uint8_t flags = FIDO2_AUT_FLAG_AT;
uint8_t rp_id_hash[32];
uint8_t rp_id_hash[32] = {0};
mbedtls_sha256((uint8_t *) rp.id.data, rp.id.len, rp_id_hash, 0);
int curve = -1, alg = 0;
if (pubKeyCredParams_len == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
for (int i = 0; i < pubKeyCredParams_len; i++) {
if (pubKeyCredParams[i].type.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0) {
continue;
}
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256) {
curve = FIDO2_CURVE_P256;
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384) {
curve = FIDO2_CURVE_P384;
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512) {
curve = FIDO2_CURVE_P521;
}
else if (pubKeyCredParams[i].alg == 0) { // no present
curve = -1;
}
else {
curve = 0;
}
if (curve > 0) {
alg = pubKeyCredParams[i].alg;
break;
}
}
if (curve == 0) {
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
else if (curve == -1) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
if (pinUvAuthParam.present == true) {
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
if (check_user_presence() == false) {
@@ -215,6 +186,56 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
}
}
int curve = -1, alg = 0;
if (pubKeyCredParams_len == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
for (unsigned int i = 0; i < pubKeyCredParams_len; i++) {
if (pubKeyCredParams[i].type.present == false) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
if (pubKeyCredParams[i].alg == 0) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
if (strcmp(pubKeyCredParams[i].type.data, "public-key") != 0) {
CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
}
if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256) {
if (curve <= 0) {
curve = FIDO2_CURVE_P256;
}
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES384) {
if (curve <= 0) {
curve = FIDO2_CURVE_P384;
}
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES512) {
if (curve <= 0) {
curve = FIDO2_CURVE_P521;
}
}
else if (pubKeyCredParams[i].alg == FIDO2_ALG_ES256K) {
if (curve <= 0) {
curve = FIDO2_CURVE_P256K1;
}
}
else if (pubKeyCredParams[i].alg <= FIDO2_ALG_RS256 && pubKeyCredParams[i].alg >= FIDO2_ALG_RS512) {
// pass
}
//else {
// CBOR_ERROR(CTAP2_ERR_CBOR_UNEXPECTED_TYPE);
//}
if (curve > 0 && alg == 0) {
alg = (int)pubKeyCredParams[i].alg;
}
}
if (curve <= 0) {
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_ALGORITHM);
}
if (options.present) {
if (options.uv == ptrue) { //5.3
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
@@ -225,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) {
@@ -238,11 +259,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
//Unfinished. See 6.1.2.9
}
if (pinUvAuthParam.present == true) { //11.1
int ret = verify(pinUvAuthProtocol,
paut.data,
clientDataHash.data,
clientDataHash.len,
pinUvAuthParam.data);
int ret = verify((uint8_t)pinUvAuthProtocol, paut.data, clientDataHash.data, (uint16_t)clientDataHash.len, pinUvAuthParam.data);
if (ret != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
@@ -262,20 +279,22 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
}
for (int e = 0; e < excludeList_len; e++) { //12.1
for (size_t e = 0; e < excludeList_len; e++) { //12.1
if (excludeList[e].type.present == false || excludeList[e].id.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
if (strcmp(excludeList[e].type.data, "public-key") != 0) {
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 ||
(flags & FIDO2_AUT_FLAG_UV))) {
credential_free(&ecred);
CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED);
}
credential_free(&ecred);
}
if (extensions.largeBlobKey == pfalse ||
@@ -292,14 +311,16 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
}
flags |= FIDO2_AUT_FLAG_UP;
clearUserPresentFlag();
clearUserVerifiedFlag();
clearPinUvAuthTokenPermissionsExceptLbw();
if (options.up == ptrue) {
clearUserPresentFlag();
clearUserVerifiedFlag();
clearPinUvAuthTokenPermissionsExceptLbw();
}
}
const known_app_t *ka = find_app_by_rp_id_hash(rp_id_hash);
uint8_t cred_id[MAX_CRED_ID_LENGTH];
uint8_t cred_id[MAX_CRED_ID_LENGTH] = {0};
size_t cred_id_len = 0;
CBOR_CHECK(credential_create(&rp.id, &user.id, &user.parent.name, &user.displayName, &options,
@@ -310,7 +331,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
flags |= FIDO2_AUT_FLAG_UV;
}
size_t ext_len = 0;
uint8_t ext[512];
uint8_t ext[512] = {0};
CborEncoder encoder, mapEncoder, mapEncoder2;
if (extensions.present == true) {
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
@@ -343,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"));
@@ -365,7 +385,6 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
flags |= FIDO2_AUT_FLAG_ED;
}
uint8_t pkey[66];
mbedtls_ecdsa_context ekey;
mbedtls_ecdsa_init(&ekey);
int ret = fido_load_key(curve, cred_id, &ekey);
@@ -380,23 +399,9 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
size_t olen = 0;
uint32_t ctr = get_sign_counter();
uint8_t cbor_buf[1024];
uint8_t cbor_buf[1024] = {0};
cbor_encoder_init(&encoder, cbor_buf, sizeof(cbor_buf), 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 5));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 3));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, -alg));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, curve));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 2));
mbedtls_mpi_write_binary(&ekey.Q.X, pkey, mbedtls_mpi_size(&ekey.Q.X));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pkey, mbedtls_mpi_size(&ekey.Q.X)));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder, 3));
mbedtls_mpi_write_binary(&ekey.Q.Y, pkey, mbedtls_mpi_size(&ekey.Q.Y));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pkey, mbedtls_mpi_size(&ekey.Q.Y)));
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
CBOR_CHECK(COSE_key(&ekey, &encoder, &mapEncoder));
size_t rs = cbor_encoder_get_buffer_size(&encoder, cbor_buf);
size_t aut_data_len = 32 + 1 + 4 + (16 + 2 + cred_id_len + rs) + ext_len;
@@ -404,47 +409,63 @@ 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;
*pa++ = ctr >> 16;
*pa++ = ctr >> 8;
*pa++ = ctr & 0xff;
pa += put_uint32_t_be(ctr, pa);
memcpy(pa, aaguid, 16); pa += 16;
*pa++ = cred_id_len >> 8;
*pa++ = cred_id_len & 0xff;
memcpy(pa, cred_id, cred_id_len); pa += cred_id_len;
memcpy(pa, cbor_buf, rs); pa += rs;
memcpy(pa, ext, ext_len); pa += ext_len;
if (pa - aut_data != aut_data_len) {
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;
if ((size_t)(pa - aut_data) != aut_data_len) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP1_ERR_OTHER);
}
memcpy(pa, clientDataHash.data, clientDataHash.len);
uint8_t hash[32], sig[MBEDTLS_ECDSA_MAX_LEN];
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
aut_data,
aut_data_len + clientDataHash.len,
hash);
uint8_t hash[64] = {0}, sig[MBEDTLS_ECDSA_MAX_LEN] = {0};
const mbedtls_md_info_t *md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
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);
bool self_attestation = true;
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_SHA256,
hash,
32,
sig,
sizeof(sig),
&olen,
random_gen,
NULL);
ret = mbedtls_ecdsa_write_signature(&ekey, mbedtls_md_get_type(md), hash, mbedtls_md_get_size(md), sig, sizeof(sig), &olen, random_gen, NULL);
mbedtls_ecdsa_free(&ekey);
uint8_t largeBlobKey[32];
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);
if (ret != 0) {
@@ -452,10 +473,8 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
}
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder,
extensions.largeBlobKey == ptrue &&
options.rk == ptrue ? 5 : 4));
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, extensions.largeBlobKey == ptrue && options.rk == ptrue ? 5 : 4));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "packed"));
@@ -463,13 +482,12 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aut_data, aut_data_len));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2,
self_attestation == false ? 3 : 2));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, self_attestation == false || is_nitrokey ? 3 : 2));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "alg"));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, self_attestation ? -alg : -FIDO2_ALG_ES256));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, self_attestation || is_nitrokey ? -alg : -FIDO2_ALG_ES256));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "sig"));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, sig, olen));
if (self_attestation == false) {
if (self_attestation == false || is_nitrokey) {
CborEncoder arrEncoder;
file_t *ef_cert = NULL;
if (enterpriseAttestation == 2) {
@@ -480,8 +498,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "x5c"));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder2, &arrEncoder, 1));
CBOR_CHECK(cbor_encode_byte_string(&arrEncoder, file_get_data(ef_cert),
file_get_size(ef_cert)));
CBOR_CHECK(cbor_encode_byte_string(&arrEncoder, file_get_data(ef_cert), file_get_size(ef_cert)));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder2, &arrEncoder));
}
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
@@ -503,7 +520,7 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
}
}
ctr++;
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
low_flash_available();
err:
CBOR_FREE_BYTE_STRING(clientDataHash);
@@ -513,14 +530,17 @@ err:
CBOR_FREE_BYTE_STRING(user.id);
CBOR_FREE_BYTE_STRING(user.displayName);
CBOR_FREE_BYTE_STRING(user.parent.name);
for (int n = 0; n < pubKeyCredParams_len; n++) {
if (extensions.present == true) {
CBOR_FREE_BYTE_STRING(extensions.credBlob);
}
for (size_t n = 0; n < MAX_CREDENTIAL_COUNT_IN_LIST; n++) {
CBOR_FREE_BYTE_STRING(pubKeyCredParams[n].type);
}
for (int m = 0; m < excludeList_len; m++) {
for (size_t m = 0; m < MAX_CREDENTIAL_COUNT_IN_LIST; m++) {
CBOR_FREE_BYTE_STRING(excludeList[m].type);
CBOR_FREE_BYTE_STRING(excludeList[m].id);
for (int n = 0; n < excludeList[m].transports_len; n++) {
for (size_t n = 0; n < excludeList[m].transports_len; n++) {
CBOR_FREE_BYTE_STRING(excludeList[m].transports[n]);
}
}
@@ -533,6 +553,6 @@ err:
}
return error;
}
res_APDU_size = resp_size;
res_APDU_size = (uint16_t)resp_size;
return 0;
}

View File

@@ -18,16 +18,20 @@
#include "file.h"
#include "fido.h"
#include "ctap.h"
#ifndef ENABLE_EMULATION
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include "bsp/board.h"
#endif
#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

View File

@@ -21,7 +21,7 @@
#include "hid/ctap_hid.h"
#include "files.h"
#include "apdu.h"
#include "hsm.h"
#include "pico_keys.h"
#include "random.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/chachapoly.h"
@@ -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;
}
@@ -84,30 +77,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
CBOR_FIELD_GET_BYTES(vendorParam, 2);
}
else if (subpara == 0x02) {
int64_t key = 0;
CBOR_PARSE_MAP_START(_f2, 3)
{
CBOR_FIELD_GET_INT(key, 3);
if (key == 1) {
CBOR_FIELD_GET_INT(kty, 3);
}
else if (key == 3) {
CBOR_FIELD_GET_INT(alg, 3);
}
else if (key == -1) {
CBOR_FIELD_GET_INT(crv, 3);
}
else if (key == -2) {
CBOR_FIELD_GET_BYTES(kax, 3);
}
else if (key == -3) {
CBOR_FIELD_GET_BYTES(kay, 3);
}
else {
CBOR_ADVANCE(3);
}
}
CBOR_PARSE_MAP_END(_f2, 3);
CBOR_CHECK(COSE_read_key(&_f2, &kty, &alg, &crv, &kax, &kay));
}
else {
CBOR_ADVANCE(2);
@@ -124,7 +94,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
}
CBOR_PARSE_MAP_END(map, 1);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_CBOR_PAYLOAD, 0);
if (cmd == CTAP_VENDOR_BACKUP) {
if (vendorCmd == 0x01) {
@@ -135,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) {
@@ -144,9 +113,9 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
}
uint8_t zeros[32];
memset(zeros, 0, sizeof(zeros));
flash_write_data_to_file(ef_keydev_enc, vendorParam.data, vendorParam.len);
flash_write_data_to_file(ef_keydev, zeros, file_get_size(ef_keydev)); // Overwrite ef with 0
flash_write_data_to_file(ef_keydev, NULL, 0); // Set ef to 0 bytes
file_put_data(ef_keydev_enc, vendorParam.data, (uint16_t)vendorParam.len);
file_put_data(ef_keydev, zeros, file_get_size(ef_keydev)); // Overwrite ef with 0
file_put_data(ef_keydev, NULL, 0); // Set ef to 0 bytes
low_flash_available();
goto err;
}
@@ -163,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);
@@ -183,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);
@@ -223,22 +170,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_encoder_create_map(&mapEncoder, &mapEncoder2, 5));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, 3));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, -FIDO2_ALG_ECDH_ES_HKDF_256));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, FIDO2_CURVE_P256));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 2));
uint8_t pkey[32];
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.X, pkey, 32);
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
CBOR_CHECK(cbor_encode_negative_int(&mapEncoder2, 3));
mbedtls_mpi_write_binary(&hkey.ctx.mbed_ecdh.Q.Y, pkey, 32);
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, pkey, 32));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
CBOR_CHECK(COSE_key_shared(&hkey, &mapEncoder, &mapEncoder2));
mbedtls_ecdh_free(&hkey);
}
}
@@ -261,14 +193,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
size_t keyenc_len = file_get_size(ef_keydev_enc);
mbedtls_chachapoly_init(&chatx);
mbedtls_chachapoly_setkey(&chatx, vendorParam.data);
ret = mbedtls_chachapoly_auth_decrypt(&chatx,
sizeof(keydev_dec),
keyenc,
NULL,
0,
keyenc + keyenc_len - 16,
keyenc + 12,
keydev_dec);
ret = mbedtls_chachapoly_auth_decrypt(&chatx, sizeof(keydev_dec), keyenc, NULL, 0, keyenc + keyenc_len - 16, keyenc + 12, keydev_dec);
mbedtls_chachapoly_free(&chatx);
if (ret != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
@@ -281,10 +206,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
uint8_t buffer[1024];
mbedtls_ecdsa_context ekey;
mbedtls_ecdsa_init(&ekey);
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1,
&ekey,
file_get_data(ef_keydev),
file_get_size(ef_keydev));
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), file_get_size(ef_keydev));
if (ret != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
@@ -294,27 +216,9 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
#ifndef ENABLE_EMULATION
pico_unique_board_id_t rpiid;
pico_get_unique_board_id(&rpiid);
#else
struct {
uint8_t id[8];
} rpiid = { 0 };
#endif
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 %02x%02x%02x%02x%02x%02x%02x%02x",
rpiid.id[0],
rpiid.id[1],
rpiid.id[2],
rpiid.id[3],
rpiid.id[4],
rpiid.id[5],
rpiid.id[6],
rpiid.id[7]);
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);
@@ -322,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) {
@@ -344,12 +243,47 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
}
file_t *ef_ee_ea = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
if (ef_ee_ea) {
flash_write_data_to_file(ef_ee_ea, vendorParam.data, vendorParam.len);
file_put_data(ef_ee_ea, vendorParam.data, (uint16_t)vendorParam.len);
}
low_flash_available();
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);
}
@@ -366,7 +300,7 @@ err:
}
return error;
}
res_APDU_size = resp_size;
res_APDU_size = (uint16_t)resp_size;
return 0;
}

View File

@@ -16,7 +16,7 @@
*/
#include "fido.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
#include "ctap.h"
#include "random.h"
@@ -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,18 +43,19 @@ 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 {
ret = derive_key(req->appId, false, req->keyHandle, MBEDTLS_ECP_DP_SECP256R1, &key);
if (verify_key(req->appId, req->keyHandle, &key) != 0) {
mbedtls_ecdsa_free(&key);
free(tmp_kh);
return SW_INCORRECT_PARAMS();
}
}
free(tmp_kh);
if (ret != CCID_OK) {
if (ret != PICOKEY_OK) {
mbedtls_ecdsa_free(&key);
return SW_EXEC_ERROR();
}
@@ -65,39 +66,27 @@ 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;
resp->ctr[1] = ctr >> 16;
resp->ctr[2] = ctr >> 8;
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));
memcpy(sig_base + CTAP_APPID_SIZE + 1, resp->ctr, 4);
memcpy(sig_base + CTAP_APPID_SIZE + 1 + 4, req->chal, CTAP_CHAL_SIZE);
ret =
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sig_base, sizeof(sig_base), hash);
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sig_base, sizeof(sig_base), hash);
if (ret != 0) {
mbedtls_ecdsa_free(&key);
return SW_EXEC_ERROR();
}
size_t olen = 0;
ret = mbedtls_ecdsa_write_signature(&key,
MBEDTLS_MD_SHA256,
hash,
32,
(uint8_t *) resp->sig,
CTAP_MAX_EC_SIG_SIZE,
&olen,
random_gen,
NULL);
ret = mbedtls_ecdsa_write_signature(&key, MBEDTLS_MD_SHA256, hash, 32, (uint8_t *) resp->sig, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
mbedtls_ecdsa_free(&key);
if (ret != 0) {
return SW_EXEC_ERROR();
}
res_APDU_size = 1 + 4 + olen;
res_APDU_size = 1 + 4 + (uint16_t)olen;
ctr++;
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
file_put_data(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
low_flash_available();
return SW_OK();
}

View File

@@ -16,16 +16,41 @@
*/
#include "fido.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
#include "ctap.h"
#include "random.h"
#include "files.h"
#include "hid/ctap_hid.h"
#include "management.h"
const uint8_t *bogus_firefox =
(const uint8_t *)
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
const uint8_t u2f_aid[] = {
7,
0xA0, 0x00, 0x00, 0x05, 0x27, 0x10, 0x02
};
int u2f_unload();
int u2f_process_apdu();
int u2f_select(app_t *a, uint8_t force) {
(void) force;
if (cap_supported(CAP_U2F)) {
a->process_apdu = u2f_process_apdu;
a->unload = u2f_unload;
return PICOKEY_OK;
}
return PICOKEY_ERR_FILE_NOT_FOUND;
}
INITIALIZER ( u2f_ctor ) {
register_app(u2f_select, u2f_aid);
}
int u2f_unload() {
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";
const uint8_t *bogus_chrome = (const uint8_t *) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
extern int ctap_error(uint8_t error);
@@ -34,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();
@@ -52,61 +77,71 @@ 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();
}
size_t olen = 0;
ret =
mbedtls_ecp_point_write_binary(&key.grp,
&key.Q,
MBEDTLS_ECP_PF_UNCOMPRESSED,
&olen,
(uint8_t *) &resp->pubKey,
CTAP_EC_POINT_SIZE);
ret = mbedtls_ecp_point_write_binary(&key.grp, &key.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, (uint8_t *) &resp->pubKey, CTAP_EC_POINT_SIZE);
mbedtls_ecdsa_free(&key);
if (ret != 0) {
return SW_EXEC_ERROR();
}
size_t ef_certdev_size = file_get_size(ef_certdev);
uint16_t ef_certdev_size = file_get_size(ef_certdev);
memcpy(resp->keyHandleCertSig + KEY_HANDLE_LEN, file_get_data(ef_certdev), ef_certdev_size);
uint8_t hash[32],
sign_base[1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN + CTAP_EC_POINT_SIZE];
uint8_t hash[32], sign_base[1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN + CTAP_EC_POINT_SIZE];
sign_base[0] = CTAP_REGISTER_HASH_ID;
memcpy(sign_base + 1, req->appId, CTAP_APPID_SIZE);
memcpy(sign_base + 1 + CTAP_APPID_SIZE, req->chal, CTAP_CHAL_SIZE);
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE, resp->keyHandleCertSig,
KEY_HANDLE_LEN);
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN,
(uint8_t *) &resp->pubKey,
CTAP_EC_POINT_SIZE);
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
sign_base,
sizeof(sign_base),
hash);
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE, resp->keyHandleCertSig, KEY_HANDLE_LEN);
memcpy(sign_base + 1 + CTAP_APPID_SIZE + CTAP_CHAL_SIZE + KEY_HANDLE_LEN, (uint8_t *) &resp->pubKey, CTAP_EC_POINT_SIZE);
ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sign_base, sizeof(sign_base), hash);
if (ret != 0) {
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();
}
ret = mbedtls_ecdsa_write_signature(&key,
MBEDTLS_MD_SHA256,
hash,
32,
(uint8_t *) resp->keyHandleCertSig + KEY_HANDLE_LEN + ef_certdev_size,
CTAP_MAX_EC_SIG_SIZE,
&olen,
random_gen,
NULL);
ret = mbedtls_ecdsa_write_signature(&key,MBEDTLS_MD_SHA256, hash, 32, (uint8_t *) resp->keyHandleCertSig + KEY_HANDLE_LEN + ef_certdev_size, CTAP_MAX_EC_SIG_SIZE, &olen, random_gen, NULL);
mbedtls_ecdsa_free(&key);
if (ret != 0) {
return SW_EXEC_ERROR();
}
res_APDU_size = sizeof(CTAP_REGISTER_RESP) - sizeof(resp->keyHandleCertSig) + KEY_HANDLE_LEN +
ef_certdev_size + olen;
res_APDU_size = sizeof(CTAP_REGISTER_RESP) - sizeof(resp->keyHandleCertSig) + KEY_HANDLE_LEN + ef_certdev_size + (uint16_t)olen;
return SW_OK();
}
extern int cmd_register();
extern int cmd_authenticate();
extern int cmd_version();
static const cmd_t cmds[] = {
{ CTAP_REGISTER, cmd_register },
{ CTAP_AUTHENTICATE, cmd_authenticate },
{ CTAP_VERSION, cmd_version },
{ 0x00, 0x0 }
};
int u2f_process_apdu() {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}
if (cap_supported(CAP_U2F)) {
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
}
}
}
return SW_INS_NOT_SUPPORTED();
}

View File

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

View File

@@ -18,7 +18,7 @@
#include "mbedtls/chachapoly.h"
#include "mbedtls/sha256.h"
#include "credential.h"
#ifndef ENABLE_EMULATION
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include "bsp/board.h"
#endif
#include "hid/ctap_hid.h"
@@ -26,30 +26,53 @@
#include "ctap.h"
#include "random.h"
#include "files.h"
#include "hsm.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;
}
@@ -83,8 +106,7 @@ int credential_create(CborCharString *rpId,
if (extensions->credBlob.present == true &&
extensions->credBlob.len < MAX_CREDBLOB_LENGTH) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "credBlob"));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, extensions->credBlob.data,
extensions->credBlob.len));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, extensions->credBlob.data, extensions->credBlob.len));
}
if (extensions->credProtect != 0) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "credProtect"));
@@ -98,6 +120,10 @@ int credential_create(CborCharString *rpId,
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "largeBlobKey"));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, true));
}
if (extensions->thirdPartyPayment == ptrue) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "thirdPartyPayment"));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, true));
}
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
@@ -117,29 +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);
int ret = mbedtls_chachapoly_encrypt_and_tag(&chatx, rs, iv, rp_id_hash, 32,
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) {
@@ -151,15 +173,16 @@ err:
return 0;
}
int credential_load(const uint8_t *cred_id,
size_t cred_id_len,
const uint8_t *rp_id_hash,
Credential *cred) {
int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, Credential *cred) {
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);
@@ -201,6 +224,7 @@ int credential_load(const uint8_t *cred_id,
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", cred->extensions.credProtect);
CBOR_FIELD_KEY_TEXT_VAL_BYTES(2, "credBlob", cred->extensions.credBlob);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "largeBlobKey", cred->extensions.largeBlobKey);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "thirdPartyPayment", cred->extensions.thirdPartyPayment);
CBOR_ADVANCE(2);
}
CBOR_PARSE_MAP_END(_f1, 2);
@@ -246,14 +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);
cred->present = false;
cred->extensions.present = false;
cred->opts.present = false;
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;
}
}
int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
@@ -266,7 +295,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
credential_free(&cred);
return ret;
}
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
for (uint16_t i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef = search_dynamic_file(EF_CRED + i);
Credential rcred = { 0 };
if (!file_has_data(ef)) {
@@ -283,8 +312,7 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
credential_free(&rcred);
continue;
}
if (memcmp(rcred.userId.data, cred.userId.data,
MIN(rcred.userId.len, cred.userId.len)) == 0) {
if (memcmp(rcred.userId.data, cred.userId.data, MIN(rcred.userId.len, cred.userId.len)) == 0) {
sloti = i;
credential_free(&rcred);
new_record = false;
@@ -298,13 +326,13 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
uint8_t *data = (uint8_t *) calloc(1, cred_id_len + 32);
memcpy(data, rp_id_hash, 32);
memcpy(data + 32, cred_id, cred_id_len);
file_t *ef = file_new(EF_CRED + sloti);
flash_write_data_to_file(ef, data, cred_id_len + 32);
file_t *ef = file_new((uint16_t)(EF_CRED + sloti));
file_put_data(ef, data, (uint16_t)cred_id_len + 32);
free(data);
if (new_record == true) { //increase rps
sloti = -1;
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
for (uint16_t i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
ef = search_dynamic_file(EF_RP + i);
if (!file_has_data(ef)) {
if (sloti == -1) {
@@ -320,21 +348,21 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
if (sloti == -1) {
return -1;
}
ef = search_dynamic_file(EF_RP + sloti);
ef = search_dynamic_file((uint16_t)(EF_RP + sloti));
if (file_has_data(ef)) {
data = (uint8_t *) calloc(1, file_get_size(ef));
memcpy(data, file_get_data(ef), file_get_size(ef));
data[0] += 1;
flash_write_data_to_file(ef, data, file_get_size(ef));
file_put_data(ef, data, file_get_size(ef));
free(data);
}
else {
ef = file_new(EF_RP + sloti);
ef = file_new((uint16_t)(EF_RP + sloti));
data = (uint8_t *) calloc(1, 1 + 32 + cred.rpId.len);
data[0] = 1;
memcpy(data + 1, rp_id_hash, 32);
memcpy(data + 1 + 32, cred.rpId.data, cred.rpId.len);
flash_write_data_to_file(ef, data, 1 + 32 + cred.rpId.len);
file_put_data(ef, data, (uint16_t)(1 + 32 + cred.rpId.len));
free(data);
}
}
@@ -352,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) {
@@ -367,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;
}
@@ -381,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;

View File

@@ -33,6 +33,7 @@ typedef struct CredExtensions {
const bool *minPinLength;
CborByteString credBlob;
const bool *largeBlobKey;
const bool *thirdPartyPayment;
bool present;
} CredExtensions;
@@ -55,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,

View File

@@ -18,16 +18,9 @@
#ifndef _CTAP_H_
#define _CTAP_H_
#ifdef _MSC_VER // Windows
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long int uint64_t;
#else
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#endif
#ifdef __cplusplus
extern "C" {
@@ -121,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)
@@ -128,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

View File

@@ -19,6 +19,13 @@
#define _CTAP2_CBOR_H_
#include "cbor.h"
#ifndef ESP_PLATFORM
#include "common.h"
#else
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#endif
#include "mbedtls/ecp.h"
#include "mbedtls/ecdh.h"
extern uint8_t *driver_prepare_response();
extern void driver_exec_finished(size_t size_next);
@@ -54,7 +61,7 @@ extern const bool _btrue, _bfalse;
do \
{ \
error = e; \
printf("Cbor ERROR [%s:%d]: %d\n", __FILE__, __LINE__, e); \
printf("Cbor ERROR [%s:%d]: %x\n", __FILE__, __LINE__, e); \
goto err; \
} while (0)
@@ -152,7 +159,7 @@ typedef struct CborCharString {
#define CBOR_FIELD_GET_KEY_TEXT(_n) \
CBOR_ASSERT(cbor_value_is_text_string(&(_f##_n)) == true); \
char _fd##_n[64]; \
char _fd##_n[64] = {0}; \
size_t _fdl##_n = sizeof(_fd##_n); \
CBOR_CHECK(cbor_value_copy_text_string(&(_f##_n), _fd##_n, &_fdl##_n, &(_f##_n)))
@@ -237,4 +244,16 @@ typedef struct CborCharString {
CBOR_CHECK(cbor_encode_boolean(&(p), v == ptrue ? true : false)); \
} } while (0)
extern CborError COSE_key(mbedtls_ecp_keypair *, CborEncoder *, CborEncoder *);
extern CborError COSE_key_shared(mbedtls_ecdh_context *key,
CborEncoder *mapEncoderParent,
CborEncoder *mapEncoder);
extern CborError COSE_public_key(int alg, CborEncoder *mapEncoderParent, CborEncoder *mapEncoder);
extern CborError COSE_read_key(CborValue *f,
int64_t *kty,
int64_t *alg,
int64_t *crv,
CborByteString *kax,
CborByteString *kay);
#endif //_CTAP2_CBOR_H_

View File

@@ -16,7 +16,8 @@
*/
#include "fido.h"
#include "hsm.h"
#include "kek.h"
#include "pico_keys.h"
#include "apdu.h"
#include "ctap.h"
#include "files.h"
@@ -27,18 +28,26 @@
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
#include "ccid/ccid.h"
#endif
#ifndef ENABLE_EMULATION
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include "bsp/board.h"
#endif
#include <math.h>
#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,
@@ -51,25 +60,37 @@ const uint8_t atr_fido[] = {
0x75, 0x62, 0x69, 0x4b, 0x65, 0x79, 0x40
};
app_t *fido_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
if (!memcmp(aid, fido_aid + 1, MIN(aid_len, fido_aid[0]))) {
a->aid = fido_aid;
a->process_apdu = fido_process_apdu;
a->unload = fido_unload;
return a;
}
return NULL;
uint8_t fido_get_version_major() {
return PICO_FIDO_VERSION_MAJOR;
}
uint8_t fido_get_version_minor() {
return PICO_FIDO_VERSION_MINOR;
}
void __attribute__((constructor)) fido_ctor() {
int fido_select(app_t *a, uint8_t force) {
(void) force;
if (cap_supported(CAP_FIDO2)) {
a->process_apdu = fido_process_apdu;
a->unload = fido_unload;
return PICOKEY_OK;
}
return PICOKEY_ERR_FILE_NOT_FOUND;
}
extern uint8_t (*get_version_major)();
extern uint8_t (*get_version_minor)();
INITIALIZER ( fido_ctor ) {
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
ccid_atr = atr_fido;
#endif
register_app(fido_select);
get_version_major = fido_get_version_major;
get_version_minor = fido_get_version_minor;
register_app(fido_select, fido_aid);
}
int fido_unload() {
return CCID_OK;
return PICOKEY_OK;
}
mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) {
@@ -93,6 +114,27 @@ mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) {
}
return MBEDTLS_ECP_DP_NONE;
}
int mbedtls_curve_to_fido(mbedtls_ecp_group_id id) {
if (id == MBEDTLS_ECP_DP_SECP256R1) {
return FIDO2_CURVE_P256;
}
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
return FIDO2_CURVE_P384;
}
else if (id == MBEDTLS_ECP_DP_SECP521R1) {
return FIDO2_CURVE_P521;
}
else if (id == MBEDTLS_ECP_DP_SECP256K1) {
return FIDO2_CURVE_P256K1;
}
else if (id == MBEDTLS_ECP_DP_CURVE25519) {
return MBEDTLS_ECP_DP_CURVE25519;
}
else if (id == MBEDTLS_ECP_DP_CURVE448) {
return FIDO2_CURVE_X448;
}
return 0;
}
int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key) {
mbedtls_ecp_group_id mbedtls_curve = fido_curve_to_mbedtls(curve);
@@ -115,10 +157,9 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
mbedtls_x509write_crt_set_validity(&ctx, "20220901000000", "20720831235959");
mbedtls_x509write_crt_set_issuer_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
mbedtls_x509write_crt_set_subject_name(&ctx, "C=ES,O=Pico HSM,CN=Pico FIDO");
mbedtls_mpi serial;
mbedtls_mpi_init(&serial);
mbedtls_mpi_fill_random(&serial, 32, random_gen, NULL);
mbedtls_x509write_crt_set_serial(&ctx, &serial);
uint8_t serial[16];
random_gen(NULL, serial, sizeof(serial));
mbedtls_x509write_crt_set_serial_raw(&ctx, serial, sizeof(serial));
mbedtls_pk_context key;
mbedtls_pk_init(&key);
mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
@@ -133,6 +174,7 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
MBEDTLS_X509_KU_KEY_CERT_SIGN);
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL);
mbedtls_x509write_crt_free(&ctx);
/* pk cannot be freed, as it is freed later */
//mbedtls_pk_free(&key);
return ret;
@@ -140,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) {
@@ -169,8 +219,9 @@ int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_con
}
}
uint8_t hmac[32], d[32];
int ret = mbedtls_ecp_write_key(key, d, sizeof(d));
if (key == NULL) {
size_t olen = 0;
int ret = mbedtls_ecp_write_key_ext(key, &olen, d, sizeof(d));
if (key == &ctx) {
mbedtls_ecdsa_free(&ctx);
}
if (ret != 0) {
@@ -179,26 +230,17 @@ int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_con
uint8_t key_base[CTAP_APPID_SIZE + KEY_PATH_LEN];
memcpy(key_base, appId, CTAP_APPID_SIZE);
memcpy(key_base + CTAP_APPID_SIZE, keyHandle, KEY_PATH_LEN);
ret =
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
d,
32,
key_base,
sizeof(key_base),
hmac);
ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), d, 32, key_base, sizeof(key_base), hmac);
mbedtls_platform_zeroize(d, sizeof(d));
return memcmp(keyHandle + KEY_PATH_LEN, hmac, sizeof(hmac));
}
int derive_key(const uint8_t *app_id,
bool new_key,
uint8_t *key_handle,
int curve,
mbedtls_ecdsa_context *key) {
uint8_t outk[64] = { 0 };
int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int curve, mbedtls_ecdsa_context *key) {
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;
}
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
@@ -209,15 +251,7 @@ int derive_key(const uint8_t *app_id,
val |= 0x80000000;
memcpy(&key_handle[i * sizeof(uint32_t)], &val, sizeof(uint32_t));
}
r = mbedtls_hkdf(md_info,
&key_handle[i * sizeof(uint32_t)],
sizeof(uint32_t),
outk,
32,
outk + 32,
32,
outk,
sizeof(outk));
r = mbedtls_hkdf(md_info, &key_handle[i * sizeof(uint32_t)], sizeof(uint32_t), outk, 32, outk + 32, 32, outk, sizeof(outk));
if (r != 0) {
mbedtls_platform_zeroize(outk, sizeof(outk));
return r;
@@ -227,9 +261,7 @@ int derive_key(const uint8_t *app_id,
uint8_t key_base[CTAP_APPID_SIZE + KEY_PATH_LEN];
memcpy(key_base, app_id, CTAP_APPID_SIZE);
memcpy(key_base + CTAP_APPID_SIZE, key_handle, KEY_PATH_LEN);
if ((r =
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, key_base,
sizeof(key_base), key_handle + 32)) != 0) {
if ((r = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), outk, 32, key_base, sizeof(key_base), key_handle + 32)) != 0) {
mbedtls_platform_zeroize(outk, sizeof(outk));
return r;
}
@@ -240,7 +272,10 @@ int derive_key(const uint8_t *app_id,
if (cinfo == NULL) {
return 1;
}
r = mbedtls_ecp_read_key(curve, key, outk, ceil((float) cinfo->bit_size / 8));
if (cinfo->bit_size % 8 != 0) {
outk[0] >>= 8 - (cinfo->bit_size % 8);
}
r = mbedtls_ecp_read_key(curve, key, outk, (size_t)ceil((float) cinfo->bit_size / 8));
mbedtls_platform_zeroize(outk, sizeof(outk));
if (r != 0) {
return r;
@@ -254,6 +289,7 @@ int derive_key(const uint8_t *app_id,
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...");
@@ -265,13 +301,19 @@ int scan_files() {
mbedtls_ecdsa_free(&ecdsa);
return ret;
}
uint8_t kdata[32];
int key_size = mbedtls_mpi_size(&ecdsa.d);
mbedtls_mpi_write_binary(&ecdsa.d, kdata, key_size);
ret = flash_write_data_to_file(ef_keydev, kdata, key_size);
uint8_t kdata[64];
size_t key_size = 0;
ret = mbedtls_ecp_write_key_ext(&ecdsa, &key_size, kdata, sizeof(kdata));
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");
@@ -280,16 +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[4096];
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;
@@ -304,7 +363,7 @@ int scan_files() {
if (ret <= 0) {
return ret;
}
flash_write_data_to_file(ef_certdev, cert + sizeof(cert) - ret, ret);
file_put_data(ef_certdev, cert + sizeof(cert) - ret, (uint16_t)ret);
}
}
else {
@@ -314,19 +373,26 @@ int scan_files() {
if (ef_counter) {
if (!file_has_data(ef_counter)) {
uint32_t v = 0;
flash_write_data_to_file(ef_counter, (uint8_t *) &v, sizeof(v));
file_put_data(ef_counter, (uint8_t *) &v, sizeof(v));
}
}
else {
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)) {
uint8_t t[32];
random_gen(NULL, t, sizeof(t));
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
file_put_data(ef_authtoken, t, sizeof(t));
}
paut.data = file_get_data(ef_authtoken);
paut.len = file_get_size(ef_authtoken);
@@ -336,12 +402,11 @@ int scan_files() {
}
ef_largeblob = search_by_fid(EF_LARGEBLOB, NULL, SPECIFY_EF);
if (!file_has_data(ef_largeblob)) {
flash_write_data_to_file(ef_largeblob,
(const uint8_t *) "\x80\x76\xbe\x8b\x52\x8d\x00\x75\xf7\xaa\xe9\x8d\x6f\xa5\x7a\x6d\x3c",
17);
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() {
@@ -349,19 +414,19 @@ void scan_all() {
scan_files();
}
extern void init_otp();
void init_fido() {
scan_all();
init_otp();
}
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;
}
@@ -369,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() {
@@ -396,29 +458,47 @@ uint8_t get_opts() {
void set_opts(uint8_t opts) {
file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
flash_write_data_to_file(ef, &opts, sizeof(uint8_t));
file_put_data(ef, &opts, sizeof(uint8_t));
low_flash_available();
}
extern int cmd_register();
extern int cmd_authenticate();
extern int cmd_version();
extern int cbor_parse(int, uint8_t *, size_t);
#define CTAP_CBOR 0x10
int cmd_cbor() {
uint8_t *old_buf = res_APDU;
int ret = cbor_parse(0x90, apdu.data, apdu.nc);
if (ret != 0) {
return SW_EXEC_ERROR();
}
res_APDU = old_buf;
res_APDU_size += 1;
memcpy(res_APDU, ctap_resp->init.data, res_APDU_size);
return SW_OK();
}
static const cmd_t cmds[] = {
{ CTAP_REGISTER, cmd_register },
{ CTAP_AUTHENTICATE, cmd_authenticate },
{ CTAP_VERSION, cmd_version },
{ CTAP_CBOR, cmd_cbor },
{ 0x00, 0x0 }
};
int fido_process_apdu() {
if (CLA(apdu) != 0x00) {
if (CLA(apdu) != 0x00 && CLA(apdu) != 0x80) {
return SW_CLA_NOT_SUPPORTED();
}
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
if (cap_supported(CAP_U2F)) {
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
}
}
}
return SW_INS_NOT_SUPPORTED();

View File

@@ -18,13 +18,18 @@
#ifndef _FIDO_H_
#define _FIDO_H_
#ifndef ENABLE_EMULATION
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include "pico/stdlib.h"
#endif
#ifndef ESP_PLATFORM
#include "common.h"
#else
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#endif
#include "mbedtls/ecdsa.h"
#ifndef ENABLE_EMULATION
#include "ctap_hid.h"
#include "hid/ctap_hid.h"
#else
#include <stdbool.h>
#endif
@@ -45,18 +50,11 @@ extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ec
extern bool wait_button_pressed();
extern void init_fido();
extern mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve);
extern int mbedtls_curve_to_fido(mbedtls_ecp_group_id id);
extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key);
extern int load_keydev(uint8_t *key);
extern int encrypt(uint8_t protocol,
const uint8_t *key,
const uint8_t *in,
size_t in_len,
uint8_t *out);
extern int decrypt(uint8_t protocol,
const uint8_t *key,
const uint8_t *in,
size_t in_len,
uint8_t *out);
extern int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out);
extern int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, uint16_t in_len, uint8_t *out);
extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret);
#define FIDO2_ALG_ES256 -7 //ECDSA-SHA256 P256
@@ -64,6 +62,10 @@ extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSec
#define FIDO2_ALG_ES384 -35 //ECDSA-SHA384 P384
#define FIDO2_ALG_ES512 -36 //ECDSA-SHA512 P521
#define FIDO2_ALG_ECDH_ES_HKDF_256 -25 //ECDH-ES + HKDF-256
#define FIDO2_ALG_ES256K -47
#define FIDO2_ALG_RS256 -257
#define FIDO2_ALG_RS384 -258
#define FIDO2_ALG_RS512 -259
#define FIDO2_CURVE_P256 1
#define FIDO2_CURVE_P384 2
@@ -126,10 +128,8 @@ typedef struct pinUvAuthToken {
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,
size_t len,
uint8_t *sign);
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

View File

@@ -18,36 +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 = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .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];
@@ -59,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;

View File

@@ -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
@@ -29,6 +30,7 @@
#define EF_PIN 0x1080
#define EF_AUTHTOKEN 0x1090
#define EF_MINPINLEN 0x1100
#define EF_DEV_CONF 0x1122
#define EF_CRED 0xCF00 // Creds at 0xCF00 - 0xCFFF
#define EF_RP 0xD000 // RPs at 0xD000 - 0xD0FF
#define EF_LARGEBLOB 0x1101 // Large Blob Array
@@ -36,6 +38,7 @@
#define EF_OATH_CODE 0xBAFF
#define EF_OTP_SLOT1 0xBB00
#define EF_OTP_SLOT2 0xBB01
#define EF_OTP_PIN 0x10A0 // Nitrokey OTP PIN
extern file_t *ef_keydev;
extern file_t *ef_certdev;
@@ -44,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
View 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
View 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

160
src/fido/management.c Normal file
View File

@@ -0,0 +1,160 @@
/*
* 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 "apdu.h"
#include "version.h"
#include "files.h"
#include "asn1.h"
#include "management.h"
int man_process_apdu();
int man_unload();
const uint8_t man_aid[] = {
8,
0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17
};
extern void scan_all();
extern void init_otp();
int man_select(app_t *a, uint8_t force) {
a->process_apdu = man_process_apdu;
a->unload = man_unload;
sprintf((char *) res_APDU, "%d.%d.0", PICO_FIDO_VERSION_MAJOR, PICO_FIDO_VERSION_MINOR);
res_APDU_size = (uint16_t)strlen((char *) res_APDU);
apdu.ne = res_APDU_size;
if (force) {
scan_all();
init_otp();
}
return PICOKEY_OK;
}
INITIALIZER ( man_ctor ) {
register_app(man_select, man_aid);
}
int man_unload() {
return PICOKEY_OK;
}
bool cap_supported(uint16_t cap) {
file_t *ef = search_dynamic_file(EF_DEV_CONF);
if (file_has_data(ef)) {
uint16_t tag = 0x0;
uint8_t *tag_data = NULL, *p = NULL;
uint16_t tag_len = 0;
asn1_ctx_t ctxi;
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
while (walk_tlv(&ctxi, &p, &tag, &tag_len, &tag_data)) {
if (tag == TAG_USB_ENABLED) {
uint16_t ecaps = tag_data[0];
if (tag_len == 2) {
ecaps = get_uint16_t_be(tag_data);
}
return ecaps & cap;
}
}
}
return true;
}
int man_get_config() {
file_t *ef = search_dynamic_file(EF_DEV_CONF);
res_APDU_size = 0;
res_APDU[res_APDU_size++] = 0; // Overall length. Filled later
res_APDU[res_APDU_size++] = TAG_USB_SUPPORTED;
res_APDU[res_APDU_size++] = 2;
res_APDU[res_APDU_size++] = CAP_FIDO2 >> 8;
res_APDU[res_APDU_size++] = CAP_OTP | CAP_U2F | CAP_OATH;
res_APDU[res_APDU_size++] = TAG_SERIAL;
res_APDU[res_APDU_size++] = 4;
memcpy(res_APDU + res_APDU_size, pico_serial.id, 4);
res_APDU_size += 4;
res_APDU[res_APDU_size++] = TAG_FORM_FACTOR;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = 0x01;
res_APDU[res_APDU_size++] = TAG_VERSION;
res_APDU[res_APDU_size++] = 3;
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MAJOR;
res_APDU[res_APDU_size++] = PICO_FIDO_VERSION_MINOR;
res_APDU[res_APDU_size++] = 0;
if (!file_has_data(ef)) {
res_APDU[res_APDU_size++] = TAG_USB_ENABLED;
res_APDU[res_APDU_size++] = 2;
res_APDU[res_APDU_size++] = CAP_FIDO2 >> 8;
res_APDU[res_APDU_size++] = CAP_OTP | CAP_U2F | CAP_OATH;
res_APDU[res_APDU_size++] = TAG_DEVICE_FLAGS;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = FLAG_EJECT;
res_APDU[res_APDU_size++] = TAG_CONFIG_LOCK;
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));
res_APDU_size += file_get_size(ef);
}
res_APDU[0] = (uint8_t)(res_APDU_size - 1);
return 0;
}
int cmd_read_config() {
man_get_config();
return SW_OK();
}
int cmd_write_config() {
if (apdu.data[0] != apdu.nc - 1) {
return SW_WRONG_DATA();
}
file_t *ef = file_new(EF_DEV_CONF);
file_put_data(ef, apdu.data + 1, (uint16_t)(apdu.nc - 1));
low_flash_available();
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 }
};
int man_process_apdu() {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
}
}
return SW_INS_NOT_SUPPORTED();
}

55
src/fido/management.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* 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 _MANAGEMENT_H_
#define _MANAGEMENT_H_
#include <stdlib.h>
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include "pico/stdlib.h"
#endif
#define TAG_USB_SUPPORTED 0x01
#define TAG_SERIAL 0x02
#define TAG_USB_ENABLED 0x03
#define TAG_FORM_FACTOR 0x04
#define TAG_VERSION 0x05
#define TAG_AUTO_EJECT_TIMEOUT 0x06
#define TAG_CHALRESP_TIMEOUT 0x07
#define TAG_DEVICE_FLAGS 0x08
#define TAG_APP_VERSIONS 0x09
#define TAG_CONFIG_LOCK 0x0A
#define TAG_UNLOCK 0x0B
#define TAG_REBOOT 0x0C
#define TAG_NFC_SUPPORTED 0x0D
#define TAG_NFC_ENABLED 0x0E
#define CAP_OTP 0x01
#define CAP_U2F 0x02
#define CAP_FIDO2 0x200
#define CAP_OATH 0x20
#define CAP_PIV 0x10
#define CAP_OPENPGP 0x08
#define CAP_HSMAUTH 0x100
#define FLAG_REMOTE_WAKEUP 0x40
#define FLAG_EJECT 0x80
extern bool cap_supported(uint16_t cap);
extern int man_get_config();
#endif //_MANAGEMENT_H

View File

@@ -16,15 +16,18 @@
*/
#include "fido.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
#include "files.h"
#include "random.h"
#include "version.h"
#include "asn1.h"
#include "crypto_utils.h"
#include "management.h"
#define MAX_OATH_CRED 255
#define CHALLENGE_LEN 8
#define MAX_OTP_COUNTER 3
#define TAG_NAME 0x71
#define TAG_NAME_LIST 0x72
@@ -34,10 +37,13 @@
#define TAG_T_RESPONSE 0x76
#define TAG_NO_RESPONSE 0x77
#define TAG_PROPERTY 0x78
#define TAG_VERSION 0x79
#define TAG_T_VERSION 0x79
#define TAG_IMF 0x7a
#define TAG_ALGO 0x7b
#define TAG_TOUCH_RESPONSE 0x7c
#define TAG_PASSWORD 0x80
#define TAG_NEW_PASSWORD 0x81
#define TAG_PIN_COUNTER 0x82
#define ALG_HMAC_SHA1 0x01
#define ALG_HMAC_SHA256 0x02
@@ -62,54 +68,57 @@ const uint8_t oath_aid[] = {
0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01
};
app_t *oath_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
if (!memcmp(aid, oath_aid + 1, MIN(aid_len, oath_aid[0]))) {
a->aid = oath_aid;
int oath_select(app_t *a, uint8_t force) {
(void) force;
if (cap_supported(CAP_OATH)) {
a->process_apdu = oath_process_apdu;
a->unload = oath_unload;
res_APDU_size = 0;
res_APDU[res_APDU_size++] = TAG_VERSION;
res_APDU[res_APDU_size++] = TAG_T_VERSION;
res_APDU[res_APDU_size++] = 3;
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_NAME;
res_APDU[res_APDU_size++] = 8;
#ifndef ENABLE_EMULATION
pico_get_unique_board_id((pico_unique_board_id_t *) (res_APDU + res_APDU_size));
res_APDU_size += 8;
#else
memset(res_APDU + res_APDU_size, 0, 8); res_APDU_size += 8;
#endif
memcpy(res_APDU + res_APDU_size, pico_serial_str, 8); res_APDU_size += 8;
if (file_has_data(search_dynamic_file(EF_OATH_CODE)) == true) {
random_gen(NULL, challenge, sizeof(challenge));
res_APDU[res_APDU_size++] = TAG_CHALLENGE;
res_APDU[res_APDU_size++] = sizeof(challenge);
memcpy(res_APDU + res_APDU_size, challenge, sizeof(challenge));
res_APDU_size += sizeof(challenge);
}
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (file_has_data(ef_otp_pin)) {
const uint8_t *pin_data = file_get_data(ef_otp_pin);
res_APDU[res_APDU_size++] = TAG_PIN_COUNTER;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = *pin_data;
}
res_APDU[res_APDU_size++] = TAG_ALGO;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = ALG_HMAC_SHA1;
apdu.ne = res_APDU_size;
return a;
return PICOKEY_OK;
}
return NULL;
return PICOKEY_ERR_FILE_NOT_FOUND;
}
void __attribute__((constructor)) oath_ctor() {
register_app(oath_select);
INITIALIZER ( oath_ctor ) {
register_app(oath_select, oath_aid);
}
int oath_unload() {
return CCID_OK;
return PICOKEY_OK;
}
file_t *find_oath_cred(const uint8_t *name, size_t name_len) {
size_t ef_tag_len = 0;
uint8_t *ef_tag_data = NULL;
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
if (file_has_data(ef) &&
asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_NAME, &ef_tag_len,
&ef_tag_data) == true && ef_tag_len == name_len &&
memcmp(ef_tag_data, name, name_len) == 0) {
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
asn1_ctx_t ctxi, ef_tag = { 0 };
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
if (file_has_data(ef) && asn1_find_tag(&ctxi, TAG_NAME, &ef_tag) == true && ef_tag.len == name_len && memcmp(ef_tag.data, name, name_len) == 0) {
return ef;
}
}
@@ -120,40 +129,40 @@ int cmd_put() {
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
size_t key_len = 0, imf_len = 0, name_len = 0;
uint8_t *key = NULL, *imf = NULL, *name = NULL;
if (asn1_find_tag(apdu.data, apdu.nc, TAG_KEY, &key_len, &key) == false) {
asn1_ctx_t ctxi, key = { 0 }, name = { 0 }, imf = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_KEY, &key) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
return SW_INCORRECT_PARAMS();
}
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
if (asn1_find_tag(apdu.data, apdu.nc, TAG_IMF, &imf_len, &imf) == false) {
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
if (asn1_find_tag(&ctxi, TAG_IMF, &imf) == false) {
memcpy(apdu.data + apdu.nc, "\x7a\x08\x00\x00\x00\x00\x00\x00\x00\x00", 10);
apdu.nc += 10;
}
else { //prepend zero-valued bytes
if (imf_len < 8) {
memmove(imf + (8 - imf_len), imf, imf_len);
memset(imf, 0, 8 - imf_len);
*(imf - 1) = 8;
apdu.nc += (8 - imf_len);
if (imf.len < 8) {
memmove(imf.data + (8 - imf.len), imf.data, imf.len);
memset(imf.data, 0, 8 - imf.len);
*(imf.data - 1) = 8;
apdu.nc += (8 - imf.len);
}
}
}
file_t *ef = find_oath_cred(name, name_len);
file_t *ef = find_oath_cred(name.data, name.len);
if (file_has_data(ef)) {
flash_write_data_to_file(ef, apdu.data, apdu.nc);
file_put_data(ef, apdu.data, (uint16_t)apdu.nc);
low_flash_available();
}
else {
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
if (!file_has_data(ef)) {
ef = file_new(EF_OATH_CRED + i);
flash_write_data_to_file(ef, apdu.data, apdu.nc);
file_t *tef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
if (!file_has_data(tef)) {
tef = file_new((uint16_t)(EF_OATH_CRED + i));
file_put_data(tef, apdu.data, (uint16_t)apdu.nc);
low_flash_available();
return SW_OK();
}
@@ -165,13 +174,13 @@ int cmd_put() {
int cmd_delete() {
size_t tag_len = 0;
uint8_t *tag_data = NULL;
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &tag_len, &tag_data) == true) {
file_t *ef = find_oath_cred(tag_data, tag_len);
asn1_ctx_t ctxi, ctxo = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_NAME, &ctxo) == true) {
file_t *ef = find_oath_cred(ctxo.data, ctxo.len);
if (ef) {
delete_file(ef);
return SW_OK();
@@ -203,38 +212,38 @@ int cmd_set_code() {
validated = true;
return SW_OK();
}
size_t key_len = 0, chal_len = 0, resp_len = 0;
uint8_t *key = NULL, *chal = NULL, *resp = NULL;
if (asn1_find_tag(apdu.data, apdu.nc, TAG_KEY, &key_len, &key) == false) {
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_KEY, &key) == false) {
return SW_INCORRECT_PARAMS();
}
if (key_len == 0) {
if (key.len == 0) {
delete_file(search_dynamic_file(EF_OATH_CODE));
validated = true;
return SW_OK();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &resp_len, &resp) == false) {
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
return SW_INCORRECT_PARAMS();
}
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
const mbedtls_md_info_t *md_info = get_oath_md_info(key.data[0]);
if (md_info == NULL) {
return SW_INCORRECT_PARAMS();
}
uint8_t hmac[64];
int r = mbedtls_md_hmac(md_info, key + 1, key_len - 1, chal, chal_len, hmac);
int r = mbedtls_md_hmac(md_info, key.data + 1, key.len - 1, chal.data, chal.len, hmac);
if (r != 0) {
return SW_EXEC_ERROR();
}
if (memcmp(hmac, resp, resp_len) != 0) {
if (memcmp(hmac, resp.data, resp.len) != 0) {
return SW_DATA_INVALID();
}
random_gen(NULL, challenge, sizeof(challenge));
file_t *ef = file_new(EF_OATH_CODE);
flash_write_data_to_file(ef, key, key_len);
file_put_data(ef, key.data, key.len);
low_flash_available();
validated = false;
return SW_OK();
@@ -245,34 +254,32 @@ int cmd_reset() {
return SW_INCORRECT_P1P2();
}
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
if (file_has_data(ef)) {
delete_file(ef);
}
}
delete_file(search_dynamic_file(EF_OATH_CODE));
flash_clear_file(search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF));
low_flash_available();
validated = true;
return SW_OK();
}
int cmd_list() {
size_t name_len = 0, key_len = 0;
uint8_t *name = NULL, *key = NULL;
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
if (file_has_data(ef)) {
uint8_t *data = file_get_data(ef);
size_t data_len = file_get_size(ef);
if (asn1_find_tag(data, data_len, TAG_NAME, &name_len,
&name) == true &&
asn1_find_tag(data, data_len, TAG_KEY, &key_len, &key) == true) {
asn1_ctx_t ctxi, key = { 0 }, name = { 0 };
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxi);
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == true && asn1_find_tag(&ctxi, TAG_KEY, &key) == true) {
res_APDU[res_APDU_size++] = TAG_NAME_LIST;
res_APDU[res_APDU_size++] = name_len + 1;
res_APDU[res_APDU_size++] = key[0];
memcpy(res_APDU + res_APDU_size, name, name_len); res_APDU_size += name_len;
res_APDU[res_APDU_size++] = (uint8_t)(name.len + 1);
res_APDU[res_APDU_size++] = key.data[0];
memcpy(res_APDU + res_APDU_size, name.data, name.len); res_APDU_size += name.len;
}
}
}
@@ -281,12 +288,12 @@ int cmd_list() {
}
int cmd_validate() {
size_t chal_len = 0, resp_len = 0, key_len = 0;
uint8_t *chal = NULL, *resp = NULL, *key = NULL;
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, resp = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &resp_len, &resp) == false) {
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &resp) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = search_dynamic_file(EF_OATH_CODE);
@@ -294,21 +301,21 @@ int cmd_validate() {
validated = true;
return SW_DATA_INVALID();
}
key = file_get_data(ef);
key_len = file_get_size(ef);
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
key.data = file_get_data(ef);
key.len = file_get_size(ef);
const mbedtls_md_info_t *md_info = get_oath_md_info(key.data[0]);
if (md_info == NULL) {
return SW_INCORRECT_PARAMS();
}
uint8_t hmac[64];
int ret = mbedtls_md_hmac(md_info, key + 1, key_len - 1, challenge, sizeof(challenge), hmac);
int ret = mbedtls_md_hmac(md_info, key.data + 1, key.len - 1, challenge, sizeof(challenge), hmac);
if (ret != 0) {
return SW_EXEC_ERROR();
}
if (memcmp(hmac, resp, resp_len) != 0) {
if (memcmp(hmac, resp.data, resp.len) != 0) {
return SW_DATA_INVALID();
}
ret = mbedtls_md_hmac(md_info, key + 1, key_len - 1, chal, chal_len, hmac);
ret = mbedtls_md_hmac(md_info, key.data + 1, key.len - 1, chal.data, chal.len, hmac);
if (ret != 0) {
return SW_EXEC_ERROR();
}
@@ -321,11 +328,7 @@ int cmd_validate() {
return SW_OK();
}
int calculate_oath(uint8_t truncate,
const uint8_t *key,
size_t key_len,
const uint8_t *chal,
size_t chal_len) {
int calculate_oath(uint8_t truncate, const uint8_t *key, size_t key_len, const uint8_t *chal, size_t chal_len) {
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
if (md_info == NULL) {
return SW_INCORRECT_PARAMS();
@@ -334,7 +337,7 @@ int calculate_oath(uint8_t truncate,
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;
@@ -346,77 +349,62 @@ int calculate_oath(uint8_t truncate,
res_APDU[res_APDU_size++] = hmac[offset + 3];
}
else {
res_APDU[res_APDU_size++] = hmac_size + 1;
res_APDU[res_APDU_size++] = (uint8_t)(hmac_size + 1);
res_APDU[res_APDU_size++] = key[1];
memcpy(res_APDU + res_APDU_size, hmac, hmac_size); res_APDU_size += hmac_size;
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() {
size_t chal_len = 0, name_len = 0, key_len = 0;
uint8_t *chal = NULL, *name = NULL, *key = NULL;
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
return SW_INCORRECT_P1P2();
}
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = find_oath_cred(name, name_len);
file_t *ef = find_oath_cred(name.data, name.len);
if (file_has_data(ef) == false) {
return SW_DATA_INVALID();
}
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_KEY, &key_len, &key) == false) {
asn1_ctx_t ctxe;
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
if (asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
return SW_INCORRECT_PARAMS();
}
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_IMF, &chal_len,
&chal) == false) {
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
if (asn1_find_tag(&ctxe, TAG_IMF, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
}
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
int ret = calculate_oath(P2(apdu), key, key_len, chal, chal_len);
if (ret != CCID_OK) {
int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len);
if (ret != PICOKEY_OK) {
return SW_EXEC_ERROR();
}
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
uint64_t v =
((uint64_t) chal[0] <<
56) |
((uint64_t) chal[1] <<
48) |
((uint64_t) chal[2] <<
40) |
((uint64_t) chal[3] <<
32) |
((uint64_t) chal[4] <<
24) | ((uint64_t) chal[5] << 16) | ((uint64_t) chal[6] << 8) | (uint64_t) chal[7];
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
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);
memcpy(tmp, file_get_data(ef), ef_size);
asn1_find_tag(tmp, ef_size, TAG_IMF, &chal_len, &chal);
chal[0] = v >> 56;
chal[1] = v >> 48;
chal[2] = v >> 40;
chal[3] = v >> 32;
chal[4] = v >> 24;
chal[5] = v >> 16;
chal[6] = v >> 8;
chal[7] = v & 0xff;
flash_write_data_to_file(ef, tmp, ef_size);
asn1_ctx_t ctxt;
asn1_ctx_init(tmp, (uint16_t)ef_size, &ctxt);
asn1_find_tag(&ctxt, TAG_IMF, &chal);
put_uint64_t_be(v, chal.data);
file_put_data(ef, tmp, (uint16_t)ef_size);
low_flash_available();
free(tmp);
}
@@ -425,47 +413,47 @@ int cmd_calculate() {
}
int cmd_calculate_all() {
size_t chal_len = 0, name_len = 0, key_len = 0, prop_len = 0;
uint8_t *chal = NULL, *name = NULL, *key = NULL, *prop = NULL;
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, prop = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (P2(apdu) != 0x0 && P2(apdu) != 0x1) {
return SW_INCORRECT_P1P2();
}
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_CHALLENGE, &chal_len, &chal) == false) {
if (asn1_find_tag(&ctxi, TAG_CHALLENGE, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
res_APDU_size = 0;
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file(EF_OATH_CRED + i);
file_t *ef = search_dynamic_file((uint16_t)(EF_OATH_CRED + i));
if (file_has_data(ef)) {
const uint8_t *ef_data = file_get_data(ef);
size_t ef_len = file_get_size(ef);
if (asn1_find_tag(ef_data, ef_len, TAG_NAME, &name_len,
&name) == false ||
asn1_find_tag(ef_data, ef_len, TAG_KEY, &key_len, &key) == false) {
asn1_ctx_t ctxe;
asn1_ctx_init((uint8_t *)ef_data, (uint16_t)ef_len, &ctxe);
if (asn1_find_tag(&ctxe, TAG_NAME, &name) == false || asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
continue;
}
res_APDU[res_APDU_size++] = TAG_NAME;
res_APDU[res_APDU_size++] = name_len;
memcpy(res_APDU + res_APDU_size, name, name_len); res_APDU_size += name_len;
if ((key[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
res_APDU[res_APDU_size++] = (uint8_t)name.len;
memcpy(res_APDU + res_APDU_size, name.data, name.len); res_APDU_size += name.len;
if ((key.data[0] & OATH_TYPE_MASK) == OATH_TYPE_HOTP) {
res_APDU[res_APDU_size++] = TAG_NO_RESPONSE;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = key[1];
res_APDU[res_APDU_size++] = key.data[1];
}
else if (asn1_find_tag(ef_data, ef_len, TAG_PROPERTY, &prop_len,
&prop) == true && (prop[0] & PROP_TOUCH)) {
else if (asn1_find_tag(&ctxe, TAG_PROPERTY, &prop) == true && (prop.data[0] & PROP_TOUCH)) {
res_APDU[res_APDU_size++] = TAG_TOUCH_RESPONSE;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = key[1];
res_APDU[res_APDU_size++] = key.data[1];
}
else {
res_APDU[res_APDU_size++] = TAG_RESPONSE + P2(apdu);
int ret = calculate_oath(P2(apdu), key, key_len, chal, chal_len);
if (ret != CCID_OK) {
int ret = calculate_oath(P2(apdu), key.data, key.len, chal.data, chal.len);
if (ret != PICOKEY_OK) {
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = key[1];
res_APDU[res_APDU_size++] = key.data[1];
}
}
}
@@ -478,26 +466,195 @@ int cmd_send_remaining() {
return SW_OK();
}
int cmd_set_otp_pin() {
uint8_t hsh[33] = { 0 };
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (file_has_data(ef_otp_pin)) {
return SW_CONDITIONS_NOT_SATISFIED();
}
asn1_ctx_t ctxi, pw = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
hsh[0] = MAX_OTP_COUNTER;
double_hash_pin(pw.data, pw.len, hsh + 1);
file_put_data(ef_otp_pin, hsh, sizeof(hsh));
low_flash_available();
return SW_OK();
}
int cmd_change_otp_pin() {
uint8_t hsh[33] = { 0 };
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (!file_has_data(ef_otp_pin)) {
return SW_CONDITIONS_NOT_SATISFIED();
}
asn1_ctx_t ctxi, pw = { 0 }, new_pw = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
double_hash_pin(pw.data, pw.len, hsh + 1);
if (memcmp(file_get_data(ef_otp_pin) + 1, hsh + 1, 32) != 0) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (asn1_find_tag(&ctxi, TAG_NEW_PASSWORD, &new_pw) == false) {
return SW_INCORRECT_PARAMS();
}
hsh[0] = MAX_OTP_COUNTER;
double_hash_pin(new_pw.data, new_pw.len, hsh + 1);
file_put_data(ef_otp_pin, hsh, sizeof(hsh));
low_flash_available();
return SW_OK();
}
int cmd_verify_otp_pin() {
uint8_t hsh[33] = { 0 }, data_hsh[33];
file_t *ef_otp_pin = search_by_fid(EF_OTP_PIN, NULL, SPECIFY_EF);
if (!file_has_data(ef_otp_pin)) {
return SW_CONDITIONS_NOT_SATISFIED();
}
asn1_ctx_t ctxi, pw = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
if (asn1_find_tag(&ctxi, TAG_PASSWORD, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
double_hash_pin(pw.data, pw.len, hsh + 1);
memcpy(data_hsh, file_get_data(ef_otp_pin), sizeof(data_hsh));
if (data_hsh[0] == 0 || memcmp(data_hsh + 1, hsh + 1, 32) != 0) {
if (data_hsh[0] > 0) {
data_hsh[0] -= 1;
}
file_put_data(ef_otp_pin, data_hsh, sizeof(data_hsh));
low_flash_available();
validated = false;
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
data_hsh[0] = MAX_OTP_COUNTER;
file_put_data(ef_otp_pin, data_hsh, sizeof(data_hsh));
low_flash_available();
validated = true;
return SW_OK();
}
int cmd_verify_hotp() {
asn1_ctx_t ctxi, key = { 0 }, chal = { 0 }, name = { 0 }, code = { 0 };
asn1_ctx_init(apdu.data, (uint16_t)apdu.nc, &ctxi);
uint32_t code_int = 0;
if (asn1_find_tag(&ctxi, TAG_NAME, &name) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = find_oath_cred(name.data, name.len);
if (file_has_data(ef) == false) {
return SW_DATA_INVALID();
}
asn1_ctx_t ctxe;
asn1_ctx_init(file_get_data(ef), file_get_size(ef), &ctxe);
if (asn1_find_tag(&ctxe, TAG_KEY, &key) == false) {
return SW_INCORRECT_PARAMS();
}
if ((key.data[0] & OATH_TYPE_MASK) != OATH_TYPE_HOTP) {
return SW_DATA_INVALID();
}
if (asn1_find_tag(&ctxe, TAG_IMF, &chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(&ctxi, TAG_RESPONSE, &code) == true) {
code_int = get_uint32_t_be(code.data);
}
int ret = calculate_oath(0x01, key.data, key.len, chal.data, chal.len);
if (ret != PICOKEY_OK) {
return SW_EXEC_ERROR();
}
uint32_t res_int = get_uint32_t_be(res_APDU + 2);
if (res_APDU[1] == 6) {
res_int %= (uint32_t) 1e6;
}
else {
res_int %= (uint32_t) 1e8;
}
if (res_int != code_int) {
return SW_WRONG_DATA();
}
res_APDU_size = 0;
apdu.ne = 0;
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
#define INS_CALC_ALL 0xa4
#define INS_SEND_REMAINING 0xa5
#define INS_VERIFY_CODE 0xb1
#define INS_VERIFY_PIN 0xb2
#define INS_CHANGE_PIN 0xb3
#define INS_SET_PIN 0xb4
static const cmd_t cmds[] = {
{ INS_PUT, cmd_put },
{ 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 },
{ INS_CALC_ALL, cmd_calculate_all },
{ INS_SEND_REMAINING, cmd_send_remaining },
{ INS_SET_PIN, cmd_set_otp_pin },
{ INS_CHANGE_PIN, cmd_change_otp_pin },
{ INS_VERIFY_PIN, cmd_verify_otp_pin },
{ INS_VERIFY_CODE, cmd_verify_hotp },
{ 0x00, 0x0 }
};
@@ -505,10 +662,12 @@ int oath_process_apdu() {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
if (cap_supported(CAP_OATH)) {
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
}
}
}
return SW_INS_NOT_SUPPORTED();

View File

@@ -16,12 +16,22 @@
*/
#include "fido.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
#include "files.h"
#include "random.h"
#include "version.h"
#include "asn1.h"
#include "hid/ctap_hid.h"
#include "usb.h"
#if !defined(ENABLE_EMULATION) && !defined(ESP_PLATFORM)
#include "bsp/board.h"
#endif
#include "mbedtls/aes.h"
#include "management.h"
#ifndef ENABLE_EMULATION
#include "tusb.h"
#endif
#define FIXED_SIZE 16
#define KEY_SIZE 16
@@ -36,8 +46,57 @@
#define CONFIG_LED_INV 0x10
#define CONFIG_STATUS_MASK 0x1f
/* EXT Flags */
#define SERIAL_BTN_VISIBLE 0x01 // Serial number visible at startup (button press)
#define SERIAL_USB_VISIBLE 0x02 // Serial number visible in USB iSerial field
#define SERIAL_API_VISIBLE 0x04 // Serial number visible via API call
#define USE_NUMERIC_KEYPAD 0x08 // Use numeric keypad for digits
#define FAST_TRIG 0x10 // Use fast trig if only cfg1 set
#define ALLOW_UPDATE 0x20 // Allow update of existing configuration (selected flags + access code)
#define DORMANT 0x40 // Dormant config (woken up, flag removed, requires update flag)
#define LED_INV 0x80 // LED idle state is off rather than on
#define EXTFLAG_UPDATE_MASK (SERIAL_BTN_VISIBLE | SERIAL_USB_VISIBLE | SERIAL_API_VISIBLE | \
USE_NUMERIC_KEYPAD | FAST_TRIG | ALLOW_UPDATE | DORMANT | LED_INV)
/* TKT Flags */
#define TAB_FIRST 0x01 // Send TAB before first part
#define APPEND_TAB1 0x02 // Send TAB after first part
#define APPEND_TAB2 0x04 // Send TAB after second part
#define APPEND_DELAY1 0x08 // Add 0.5s delay after first part
#define APPEND_DELAY2 0x10 // Add 0.5s delay after second part
#define APPEND_CR 0x20 // Append CR as final character
#define OATH_HOTP 0x40 // OATH HOTP mode
#define CHAL_RESP 0x40 // Challenge-response enabled (both must be set)
#define PROTECT_CFG2 0x80 // Block update of config 2 unless config 2 is configured and has this bit set
#define TKTFLAG_UPDATE_MASK (TAB_FIRST | APPEND_TAB1 | APPEND_TAB2 | APPEND_DELAY1 | APPEND_DELAY2 | \
APPEND_CR)
/* CFG Flags */
#define SEND_REF 0x01 // Send reference string (0..F) before data
#define PACING_10MS 0x04 // Add 10ms intra-key pacing
#define PACING_20MS 0x08 // Add 20ms intra-key pacing
#define STATIC_TICKET 0x20 // Static ticket generation
// Static
#define SHORT_TICKET 0x02 // Send truncated ticket (half length)
#define STRONG_PW1 0x10 // Strong password policy flag #1 (mixed case)
#define STRONG_PW2 0x40 // Strong password policy flag #2 (subtitute 0..7 to digits)
#define MAN_UPDATE 0x80 // Allow manual (local) update of static OTP
// Challenge (no keyboard)
#define HMAC_LT64 0x04 // Set when HMAC message is less than 64 bytes
#define CHAL_BTN_TRIG 0x08 // Challenge-response operation requires button press
#define CHAL_YUBICO 0x20 // Challenge-response enabled - Yubico OTP mode
#define CHAL_HMAC 0x22 // Challenge-response enabled - HMAC-SHA1
// OATH
#define OATH_HOTP8 0x02 // Generate 8 digits HOTP rather than 6 digits
#define OATH_FIXED_MODHEX1 0x10 // First byte in fixed part sent as modhex
#define OATH_FIXED_MODHEX2 0x40 // First two bytes in fixed part sent as modhex
#define OATH_FIXED_MODHEX 0x50 // Fixed part sent as modhex
#define OATH_FIXED_MASK 0x50 // Mask to get out fixed flags
#define CFGFLAG_UPDATE_MASK (PACING_10MS | PACING_20MS)
static uint8_t config_seq = { 1 };
PACK(
typedef struct otp_config {
uint8_t fixed_data[FIXED_SIZE];
uint8_t uid[UID_SIZE];
@@ -49,22 +108,29 @@ typedef struct otp_config {
uint8_t cfg_flags;
uint8_t rfu[2];
uint16_t crc;
} __attribute__((packed)) otp_config_t;
}) otp_config_t;
static const size_t otp_config_size = sizeof(otp_config_t);
uint16_t otp_status();
#define otp_config_size sizeof(otp_config_t)
uint16_t otp_status(bool is_otp);
int otp_process_apdu();
int otp_unload();
#ifndef ENABLE_EMULATION
extern int (*hid_set_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t);
extern uint16_t (*hid_get_report_cb)(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t);
int otp_hid_set_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t const *, uint16_t);
uint16_t otp_hid_get_report_cb(uint8_t, uint8_t, hid_report_type_t, uint8_t *, uint16_t);
#endif
const uint8_t otp_aid[] = {
7,
0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01
};
app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
if (!memcmp(aid, otp_aid + 1, MIN(aid_len, otp_aid[0]))) {
a->aid = otp_aid;
int otp_select(app_t *a, uint8_t force) {
(void) force;
if (cap_supported(CAP_OTP)) {
a->process_apdu = otp_process_apdu;
a->unload = otp_unload;
if (file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ||
@@ -74,47 +140,244 @@ app_t *otp_select(app_t *a, const uint8_t *aid, uint8_t aid_len) {
else {
config_seq = 0;
}
otp_status();
apdu.ne = res_APDU_size;
return a;
otp_status(false);
return PICOKEY_OK;
}
return NULL;
return PICOKEY_ERR_FILE_NOT_FOUND;
}
void __attribute__((constructor)) otp_ctor() {
register_app(otp_select);
uint8_t modhex_tab[] =
{ 'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v' };
int encode_modhex(const uint8_t *in, size_t len, uint8_t *out) {
for (size_t l = 0; l < len; l++) {
*out++ = modhex_tab[in[l] >> 4];
*out++ = modhex_tab[in[l] & 0xf];
}
return 0;
}
static bool scanned = false;
extern void scan_all();
void init_otp() {
if (scanned == false) {
scan_all();
for (uint8_t i = 0; i < 2; i++) {
file_t *ef = search_dynamic_file(EF_OTP_SLOT1 + i);
uint8_t *data = file_get_data(ef);
otp_config_t *otp_config = (otp_config_t *) data;
if (file_has_data(ef) && !(otp_config->tkt_flags & OATH_HOTP) &&
!(otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET)) {
uint16_t counter = get_uint16_t_be(data + otp_config_size);
if (++counter <= 0x7fff) {
uint8_t new_data[otp_config_size + 8];
memcpy(new_data, data, sizeof(new_data));
put_uint16_t_be(counter, new_data + otp_config_size);
file_put_data(ef, new_data, sizeof(new_data));
}
}
}
scanned = true;
low_flash_available();
}
}
extern int calculate_oath(uint8_t truncate,
const uint8_t *key,
size_t key_len,
const uint8_t *chal,
size_t chal_len);
uint16_t calculate_crc(const uint8_t *data, size_t data_len) {
uint16_t crc = 0xFFFF;
for (size_t idx = 0; idx < data_len; idx++) {
crc ^= data[idx];
for (uint8_t i = 0; i < 8; i++) {
uint16_t j = crc & 0x1;
crc >>= 1;
if (j == 1) {
crc ^= 0x8408;
}
}
}
return crc & 0xFFFF;
}
#ifndef ENABLE_EMULATION
static uint8_t session_counter[2] = { 0 };
#endif
int otp_button_pressed(uint8_t slot) {
init_otp();
if (!cap_supported(CAP_OTP)) {
return 3;
}
#ifndef ENABLE_EMULATION
file_t *ef = search_dynamic_file(slot == 1 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
const uint8_t *data = file_get_data(ef);
otp_config_t *otp_config = (otp_config_t *) data;
if (file_has_data(ef) == false) {
return 1;
}
if (otp_config->cfg_flags & CHAL_YUBICO && otp_config->tkt_flags & CHAL_RESP) {
return 2;
}
if (otp_config->tkt_flags & OATH_HOTP) {
uint8_t tmp_key[KEY_SIZE + 2];
tmp_key[0] = 0x01;
memcpy(tmp_key + 2, otp_config->aes_key, KEY_SIZE);
uint64_t imf = 0;
const uint8_t *p = data + otp_config_size;
imf = get_uint64_t_be(p);
p += 8;
if (imf == 0) {
imf = get_uint16_t_be(otp_config->uid + 4);
}
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 == PICOKEY_OK) {
uint32_t base = otp_config->cfg_flags & OATH_HOTP8 ? 1e8 : 1e6;
uint32_t number = get_uint16_t_be(res_APDU + 2);
number %= base;
char number_str[9];
if (otp_config->cfg_flags & OATH_HOTP8) {
sprintf(number_str, "%08lu", (long unsigned int) number);
add_keyboard_buffer((const uint8_t *) number_str, 8, true);
}
else {
sprintf(number_str, "%06lu", (long unsigned int) number);
add_keyboard_buffer((const uint8_t *) number_str, 6, true);
}
imf++;
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));
file_put_data(ef, new_otp_config, sizeof(new_otp_config));
low_flash_available();
}
if (otp_config->tkt_flags & APPEND_CR) {
append_keyboard_buffer((const uint8_t *) "\r", 1);
}
}
else if (otp_config->cfg_flags & SHORT_TICKET || otp_config->cfg_flags & STATIC_TICKET) {
uint8_t fixed_size = FIXED_SIZE + UID_SIZE + KEY_SIZE;
if (otp_config->cfg_flags & SHORT_TICKET) { // Not clear which is the purpose of SHORT_TICKET
//fixed_size /= 2;
}
add_keyboard_buffer(otp_config->fixed_data, fixed_size, false);
if (otp_config->tkt_flags & APPEND_CR) {
append_keyboard_buffer((const uint8_t *) "\x28", 1);
}
}
else {
uint8_t otpk[22], *po = otpk;
bool update_counter = false;
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;
counter = 1;
}
memcpy(po, otp_config->fixed_data, 6);
po += 6;
memcpy(po, otp_config->uid, UID_SIZE);
po += UID_SIZE;
po += put_uint16_t_le(counter, po);
ts >>= 1;
*po++ = ts & 0xff;
*po++ = ts >> 8;
*po++ = ts >> 16;
*po++ = session_counter[slot - 1];
random_gen(NULL, po, 2);
po += 2;
crc = calculate_crc(otpk + 6, 14);
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);
mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, otpk + 6, otpk + 6);
mbedtls_aes_free(&ctx);
uint8_t otp_out[44];
encode_modhex(otpk, sizeof(otpk), otp_out);
add_keyboard_buffer((const uint8_t *) otp_out, sizeof(otp_out), true);
if (otp_config->tkt_flags & APPEND_CR) {
append_keyboard_buffer((const uint8_t *) "\r", 1);
}
if (++session_counter[slot - 1] == 0) {
if (++counter <= 0x7fff) {
update_counter = true;
}
}
if (update_counter == true) {
uint8_t new_data[otp_config_size + 8];
memcpy(new_data, data, sizeof(new_data));
put_uint16_t_be(counter, new_data + otp_config_size);
file_put_data(ef, new_data, sizeof(new_data));
low_flash_available();
}
}
#else
(void) slot;
#endif
return 0;
}
INITIALIZER( otp_ctor ) {
register_app(otp_select, otp_aid);
button_pressed_cb = otp_button_pressed;
#ifndef ENABLE_EMULATION
hid_set_report_cb = otp_hid_set_report_cb;
hid_get_report_cb = otp_hid_get_report_cb;
#endif
}
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;
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++] = 0;
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);
(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[res_APDU_size++] = 0;
if (is_otp) {
res_APDU_size = 0;
}
else {
apdu.ne = res_APDU_size;
}
return SW_OK();
}
bool check_crc(const otp_config_t *data) {
uint16_t crc = calculate_crc((const uint8_t *) data, otp_config_size);
return crc == 0xF0B8;
}
bool _is_otp = false;
int cmd_otp() {
uint8_t p1 = P1(apdu), p2 = P2(apdu);
if (p2 != 0x00) {
return SW_INCORRECT_P1P2();
}
if (p1 == 0x01 || p1 == 0x03) { // Configure slot
if (apdu.nc != otp_config_size + ACC_CODE_SIZE) {
return SW_WRONG_LENGTH();
}
if (apdu.data[48] != 0 || apdu.data[49] != 0) {
return SW_WRONG_DATA();
}
otp_config_t *odata = (otp_config_t *) apdu.data;
file_t *ef = file_new(p1 == 0x01 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
if (file_has_data(ef)) {
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
@@ -124,38 +387,109 @@ int cmd_otp() {
}
for (int c = 0; c < otp_config_size; c++) {
if (apdu.data[c] != 0) {
flash_write_data_to_file(ef, apdu.data, otp_config_size);
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
return SW_WRONG_DATA();
}
memset(apdu.data + otp_config_size, 0, 8); // Add 8 bytes extra
file_put_data(ef, apdu.data, otp_config_size + 8);
low_flash_available();
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;
config_seq++;
return otp_status(_is_otp);
}
else if (p1 == 0x04 || p1 == 0x05) {
otp_config_t *odata = (otp_config_t *) apdu.data;
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
return SW_WRONG_DATA();
}
return otp_status();
file_t *ef = search_dynamic_file(p1 == 0x04 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
if (file_has_data(ef)) {
otp_config_t *otpc = (otp_config_t *) file_get_data(ef);
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
memcpy(apdu.data, file_get_data(ef), FIXED_SIZE + UID_SIZE + KEY_SIZE);
odata->fixed_size = otpc->fixed_size;
odata->ext_flags = (otpc->ext_flags & ~EXTFLAG_UPDATE_MASK) |
(odata->ext_flags & EXTFLAG_UPDATE_MASK);
odata->tkt_flags = (otpc->tkt_flags & ~TKTFLAG_UPDATE_MASK) |
(odata->tkt_flags & TKTFLAG_UPDATE_MASK);
odata->cfg_flags = (otpc->cfg_flags & ~CFGFLAG_UPDATE_MASK) |
(odata->cfg_flags & CFGFLAG_UPDATE_MASK);
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];
bool ef1_data = false;
file_t *ef1 = file_new(EF_OTP_SLOT1);
file_t *ef2 = file_new(EF_OTP_SLOT2);
if (file_has_data(ef1)) {
memcpy(tmp, file_get_data(ef1), file_get_size(ef1));
ef1_data = true;
}
if (file_has_data(ef2)) {
file_put_data(ef1, file_get_data(ef2), file_get_size(ef2));
}
else {
delete_file(ef1);
}
if (ef1_data) {
file_put_data(ef2, tmp, sizeof(tmp));
}
else {
delete_file(ef2);
}
low_flash_available();
return otp_status(_is_otp);
}
else if (p1 == 0x10) {
#ifndef ENABLE_EMULATION
pico_get_unique_board_id_string((char *) res_APDU, 4);
#endif
memcpy(res_APDU, pico_serial.id, 4);
res_APDU_size = 4;
}
else if (p1 == 0x13) {
man_get_config();
}
else if (p1 == 0x30 || p1 == 0x38 || p1 == 0x20 || p1 == 0x28) {
file_t *ef = search_dynamic_file(p1 == 0x30 || p1 == 0x20 ? EF_OTP_SLOT1 : EF_OTP_SLOT2);
if (file_has_data(ef)) {
otp_config_t *otp_config = (otp_config_t *) file_get_data(ef);
if (!(otp_config->cfg_flags & CHAL_YUBICO && otp_config->tkt_flags & CHAL_RESP)) {
return SW_WRONG_DATA();
}
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);
if (ret == 0) {
res_APDU_size = 20;
}
}
else if (p1 == 0x20 || p1 == 0x28) {
uint8_t challenge[16];
memcpy(challenge, apdu.data, 6);
memcpy(challenge + 6, pico_serial_str, 10);
mbedtls_aes_context ctx;
mbedtls_aes_init(&ctx);
mbedtls_aes_setkey_enc(&ctx, otp_config->aes_key, 128);
ret = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, challenge, res_APDU);
mbedtls_aes_free(&ctx);
if (ret == 0) {
res_APDU_size = 16;
}
}
}
}
return SW_OK();
}
#define INS_OTP 0x01
#define INS_DELETE 0x02
#define INS_SET_CODE 0x03
#define INS_RESET 0x04
#define INS_LIST 0xa1
#define INS_CALCULATE 0xa2
#define INS_VALIDATE 0xa3
#define INS_CALC_ALL 0xa4
#define INS_SEND_REMAINING 0xa5
static const cmd_t cmds[] = {
{ INS_OTP, cmd_otp },
@@ -166,11 +500,124 @@ int otp_process_apdu() {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
if (cap_supported(CAP_OTP)) {
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
if (cmd->ins == INS(apdu)) {
int r = cmd->cmd_handler();
return r;
}
}
}
return SW_INS_NOT_SUPPORTED();
}
#ifndef ENABLE_EMULATION
uint8_t otp_frame_rx[70] = {0};
uint8_t otp_frame_tx[70] = {0};
uint8_t otp_exp_seq = 0, otp_curr_seq = 0;
uint8_t otp_header[4] = {0};
extern uint16_t *get_send_buffer_size(uint8_t itf);
int otp_send_frame(uint8_t *frame, size_t frame_len) {
uint16_t crc = calculate_crc(frame, frame_len);
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) {
otp_exp_seq++;
}
otp_curr_seq = 0;
return 0;
}
int otp_hid_set_report_cb(uint8_t itf,
uint8_t report_id,
hid_report_type_t report_type,
uint8_t const *buffer,
uint16_t bufsize)
{
if (itf == ITF_KEYBOARD) {
if (report_type == 3) {
DEBUG_PAYLOAD(buffer, bufsize);
if (buffer[7] == 0xFF) { // reset
*get_send_buffer_size(ITF_KEYBOARD) = 0;
otp_curr_seq = otp_exp_seq = 0;
memset(otp_frame_tx, 0, sizeof(otp_frame_tx));
}
else if (buffer[7] & 0x80) { // a frame
uint8_t rseq = buffer[7] & 0x1F;
if (rseq < 10) {
if (rseq == 0) {
memset(otp_frame_rx, 0, sizeof(otp_frame_rx));
}
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 = get_uint16_t_le(otp_frame_rx + 65);
uint8_t slot_id = otp_frame_rx[64];
if (residual_crc == rcrc) {
uint8_t hdr[5];
apdu.header = hdr;
apdu.data = otp_frame_rx;
apdu.nc = 64;
apdu.rdata = otp_frame_tx;
apdu.header[0] = 0;
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");
}
}
}
}
}
return 1;
}
return 0;
}
uint16_t otp_hid_get_report_cb(uint8_t itf,
uint8_t report_id,
hid_report_type_t report_type,
uint8_t *buffer,
uint16_t reqlen) {
// TODO not Implemented
(void) itf;
(void) report_id;
(void) report_type;
(void) buffer;
(void) reqlen;
uint16_t send_buffer_size = *get_send_buffer_size(ITF_KEYBOARD);
if (send_buffer_size > 0) {
uint8_t seq = otp_curr_seq++;
memset(buffer, 0, 8);
memcpy(buffer, otp_frame_tx + 7 * seq, MIN(7, send_buffer_size));
buffer[7] = 0x40 | seq;
DEBUG_DATA(buffer, 8);
*get_send_buffer_size(ITF_KEYBOARD) -= MIN(7, send_buffer_size);
}
else if (otp_curr_seq == otp_exp_seq && otp_exp_seq > 0) {
memset(buffer, 0, 7);
buffer[7] = 0x40;
DEBUG_DATA(buffer,8);
otp_curr_seq = otp_exp_seq = 0;
}
else {
res_APDU = buffer;
otp_status(true);
}
return reqlen;
}
#endif

View File

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

View File

@@ -25,7 +25,7 @@ from fido2.attestation import FidoU2FAttestation
from fido2.ctap2.pin import ClientPin
from fido2.server import Fido2Server
from fido2.ctap import CtapError
from fido2.webauthn import CollectedClientData, AttestedCredentialData
from fido2.webauthn import CollectedClientData, PublicKeyCredentialParameters, PublicKeyCredentialType
from utils import *
from fido2.cose import ES256
import sys
@@ -116,6 +116,10 @@ class Device():
self.__rp = rp
self.__attestation = attestation
self.__server = Fido2Server(self.__rp, attestation=self.__attestation)
self.__server.allowed_algorithms = [
PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, p['alg'])
for p in self.__client._backend.info.algorithms
]
def client(self):
return self.__client

View File

@@ -22,7 +22,12 @@ RUN apt install -y libccid \
cmake \
libfuse-dev \
&& rm -rf /var/lib/apt/lists/*
RUN pip3 install pytest pycvc cryptography pyscard fido2 inputimeout
RUN pip3 install pytest pycvc cryptography pyscard inputimeout
RUN git clone https://github.com/polhenarejos/python-fido2.git
WORKDIR /python-fido2
RUN git checkout development
RUN pip3 install .
WORKDIR /
RUN git clone https://github.com/frankmorgner/vsmartcard.git
WORKDIR /vsmartcard/virtualsmartcard
RUN autoreconf --verbose --install

View File

@@ -49,13 +49,14 @@ elif sys.platform.startswith("darwin"):
from . import macos as backend
elif sys.platform.startswith("freebsd"):
from . import freebsd as backend
elif sys.platform.startswith("netbsd"):
from . import netbsd as backend
elif sys.platform.startswith("openbsd"):
from . import openbsd as backend
else:
raise Exception("Unsupported platform")
from . import emulation as backend
list_descriptors = backend.list_descriptors
get_descriptor = backend.get_descriptor
open_connection = backend.open_connection

View File

@@ -19,7 +19,10 @@
from fido2.client import CtapError
from fido2.cose import ES256
from fido2.cose import ES256, ES384, ES512
import fido2.features
fido2.features.webauthn_json_mapping.enabled = False
from utils import ES256K
import pytest
@@ -31,7 +34,7 @@ def test_make_credential():
pass
def test_attestation_format(MCRes):
assert MCRes['res'].attestation_object.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"]
assert MCRes['res'].attestation_object.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"]
def test_authdata_length(MCRes):
assert len(MCRes['res'].attestation_object.auth_data) >= 77
@@ -120,18 +123,25 @@ def test_bad_type_pubKeyCredParams(device):
with pytest.raises(CtapError) as e:
device.doMC(key_params=["wrong"])
@pytest.mark.parametrize(
"alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM]
)
def test_algorithms(device, info, alg):
if ({'alg': alg, 'type': 'public-key'} in info.algorithms):
device.doMC(key_params=[{"alg": alg, "type": "public-key"}])
def test_missing_pubKeyCredParams_type(device):
with pytest.raises(CtapError) as e:
device.doMC(key_params=[{"alg": ES256.ALGORITHM}])
assert e.value.code == CtapError.ERR.MISSING_PARAMETER
assert e.value.code == CtapError.ERR.INVALID_CBOR
def test_missing_pubKeyCredParams_alg(device):
with pytest.raises(CtapError) as e:
device.doMC(key_params=[{"type": "public-key"}])
assert e.value.code in [
CtapError.ERR.MISSING_PARAMETER,
CtapError.ERR.INVALID_CBOR,
CtapError.ERR.UNSUPPORTED_ALGORITHM,
]
@@ -139,6 +149,8 @@ def test_bad_type_pubKeyCredParams_alg(device):
with pytest.raises(CtapError) as e:
device.doMC(key_params=[{"alg": "7", "type": "public-key"}])
assert e.value.code == CtapError.ERR.CBOR_UNEXPECTED_TYPE
def test_unsupported_algorithm(device):
with pytest.raises(CtapError) as e:
device.doMC(key_params=[{"alg": 1337, "type": "public-key"}])

View File

@@ -18,8 +18,9 @@
"""
from fido2.utils import sha256
from fido2.client import CtapError
from fido2.cose import ES256, ES384, ES512
from utils import verify, ES256K
import pytest
def test_authenticate(device):
@@ -47,6 +48,17 @@ def test_empty_allowList(device):
device.doGA(allow_list=[])
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
@pytest.mark.parametrize(
"alg", [ES256.ALGORITHM, ES384.ALGORITHM, ES512.ALGORITHM, ES256K.ALGORITHM]
)
def test_algorithms(device, info, alg):
if ({'alg': alg, 'type': 'public-key'} in info.algorithms):
MCRes = device.doMC(key_params=[{"alg": alg, "type": "public-key"}])
res = device.GA(allow_list=[
{"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
])
verify(MCRes['res'].attestation_object, res['res'], res['req']['client_data_hash'])
def test_get_assertion_allow_list_filtering_and_buffering(device):
""" Check that authenticator filters and stores items in allow list correctly """
allow_list = []
@@ -124,7 +136,6 @@ def test_missing_rp(device):
assert e.value.code == CtapError.ERR.MISSING_PARAMETER
def test_bad_rp(device):
with pytest.raises(CtapError) as e:
device.doGA(rp_id={"id": {"type": "wrong"}})
@@ -202,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:

View File

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

View File

@@ -69,12 +69,12 @@ def test_hmac_secret_entropy(device, MCHmacSecret, hmac, salts
#print(shannon_entropy(auth.authenticator_data.extensions['hmac-secret']))
if len(salts) == 1:
assert shannon_entropy(auth.authenticator_data.extensions['hmac-secret']) > 4.6
assert shannon_entropy(ext["hmacGetSecret"]['output1']) > 4.6
assert shannon_entropy(auth.authenticator_data.extensions['hmac-secret']) > 4.5
assert shannon_entropy(ext["hmacGetSecret"]['output1']) > 4.5
if len(salts) == 2:
assert shannon_entropy(auth.authenticator_data.extensions['hmac-secret']) > 5.4
assert shannon_entropy(ext["hmacGetSecret"]['output1']) > 4.6
assert shannon_entropy(ext["hmacGetSecret"]['output2']) > 4.6
assert shannon_entropy(ext["hmacGetSecret"]['output1']) > 4.5
assert shannon_entropy(ext["hmacGetSecret"]['output2']) > 4.5
def get_output(device, MCHmacSecret, hmac, salts):
hout = {'salt1':salts[0]}

View File

@@ -196,16 +196,13 @@ class TestHID(object):
device.set_cid(cid2) # send ping on 2nd channel
device.send_raw("\x81\x00\x39")
time.sleep(0.1)
device.send_raw("\x00")
cmd, r = device.recv_raw() # busy response
time.sleep(0.1)
device.set_cid(cid1) # finish 1st channel ping
device.send_raw("\x00")
device.set_cid(cid2)
assert cmd == 0xBF
assert r[0] == CtapError.ERR.CHANNEL_BUSY
@@ -213,9 +210,11 @@ class TestHID(object):
cmd, r = device.recv_raw() # ping response
assert cmd == 0x81
assert len(r) == 0x39
cmd, r = device.recv_raw() # ping response
def test_cid_0(self, device):
device.reset()
time.sleep(0.1)
device.set_cid(b"\x00\x00\x00\x00")
device.send_raw(
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"

View File

@@ -19,6 +19,11 @@
from fido2.webauthn import AttestedCredentialData
from fido2.cose import CoseKey
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from fido2.utils import bytes2int, int2bytes
from cryptography.hazmat.backends import default_backend
import random
import string
import secrets
@@ -175,3 +180,29 @@ class Timeout(object):
if self.timer:
self.timer.cancel()
self.timer.join()
class ES256K(CoseKey):
ALGORITHM = -47
_HASH_ALG = hashes.SHA256()
def verify(self, message, signature):
if self[-1] != 8:
raise ValueError("Unsupported elliptic curve")
ec.EllipticCurvePublicNumbers(
bytes2int(self[-2]), bytes2int(self[-3]), ec.SECP256K1()
).public_key(default_backend()).verify(
signature, message, ec.ECDSA(self._HASH_ALG)
)
@classmethod
def from_cryptography_key(cls, public_key):
pn = public_key.public_numbers()
return cls(
{
1: 2,
3: cls.ALGORITHM,
-1: 8,
-2: int2bytes(pn.x, 32),
-3: int2bytes(pn.y, 32),
}
)

View File

@@ -23,7 +23,6 @@ import sys
import argparse
import platform
from binascii import hexlify
from words import words
from threading import Event
from typing import Mapping, Any, Optional, Callable
import struct
@@ -33,7 +32,7 @@ from enum import IntEnum, unique
try:
from fido2.ctap2.config import Config
from fido2.ctap2 import Ctap2
from fido2.ctap2 import Ctap2, ClientPin, PinProtocolV2
from fido2.hid import CtapHidDevice, CTAPHID
from fido2.utils import bytes2int, int2bytes
from fido2 import cbor
@@ -58,14 +57,6 @@ except:
from enum import IntEnum
from binascii import hexlify
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
from secure_key import windows as skey
elif (platform.system() == 'Darwin'):
from secure_key import macos as skey
else:
print('ERROR: platform not supported')
sys.exit(-1)
def get_pki_data(url, data=None, method='GET'):
user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; '
'rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7'
@@ -86,10 +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
@@ -99,7 +97,7 @@ class VendorConfig(Config):
def enable_device_aut(self, ct):
self._call(
Config.CMD.VENDOR_PROTOTYPE,
VendorConfig.CMD.CONFIG_VENDOR_PROTOTYPE,
{
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_AUT_ENABLE,
VendorConfig.PARAM.VENDOR_AUT_CT: ct
@@ -108,12 +106,48 @@ class VendorConfig(Config):
def disable_device_aut(self):
self._call(
Config.CMD.VENDOR_PROTOTYPE,
VendorConfig.CMD.CONFIG_VENDOR_PROTOTYPE,
{
VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_AUT_DISABLE
},
)
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)
@@ -198,6 +232,8 @@ class Vendor:
VENDOR_MSE = 0x02
VENDOR_UNLOCK = 0x03
VENDOR_EA = 0x04
VENDOR_PHY = 0x05
VENDOR_MEMORY = 0x06
@unique
class PARAM(IntEnum):
@@ -215,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,
@@ -230,7 +270,7 @@ class Vendor:
self.__key_enc = None
self.__iv = None
self.vcfg = VendorConfig(ctap)
self.vcfg = VendorConfig(ctap, pin_uv_protocol=pin_uv_protocol, pin_uv_token=pin_uv_token)
def _call(self, cmd, sub_cmd, params=None):
if params:
@@ -252,6 +292,14 @@ class Vendor:
return self.ctap.vendor(cmd, sub_cmd, params, pin_uv_protocol, pin_uv_param)
def backup_save(self, filename):
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
from secure_key import windows as skey
elif (platform.system() == 'Darwin'):
from secure_key import macos as skey
else:
print('ERROR: platform not supported')
sys.exit(-1)
from words import words
ret = self._call(
Vendor.CMD.VENDOR_BACKUP,
Vendor.SUBCMD.ENABLE,
@@ -270,6 +318,14 @@ class Vendor:
print(f'{(c+1):02d} - {words[coef]}')
def backup_load(self, filename):
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
from secure_key import windows as skey
elif (platform.system() == 'Darwin'):
from secure_key import macos as skey
else:
print('ERROR: platform not supported')
sys.exit(-1)
from words import words
d = 0
if (d == 0):
for c in range(24):
@@ -349,6 +405,13 @@ class Vendor:
)
def _get_key_device(self):
if (platform.system() == 'Windows' or platform.system() == 'Linux'):
from secure_key import windows as skey
elif (platform.system() == 'Darwin'):
from secure_key import macos as skey
else:
print('ERROR: platform not supported')
sys.exit(-1)
return skey.get_secure_key()
def get_skey(self):
@@ -378,9 +441,52 @@ 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")
parser.add_argument('-p','--pin', help='Specify the PIN of the device.', required=True)
parser_secure = subparser.add_parser('secure', help='Manages security of Pico Fido.')
parser_secure.add_argument('subcommand', choices=['enable', 'disable', 'unlock'], help='Enables, disables or unlocks the security.')
@@ -392,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
@@ -425,16 +546,51 @@ 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.4')
print('Pico Fido Tool v1.10')
print('Author: Pol Henarejos')
print('Report bugs to https://github.com/polhenarejos/pico-fido/issues')
print('')
print('')
dev = next(CtapHidDevice.list_devices(), None)
vdr = Vendor(Ctap2Vendor(dev))
ctap = Ctap2Vendor(dev)
client_pin = ClientPin(ctap)
token = client_pin.get_pin_token(args.pin, permissions=ClientPin.PERMISSION.AUTHENTICATOR_CFG)
vdr = Vendor(ctap, pin_uv_protocol=PinProtocolV2(), pin_uv_token=token)
if (args.command == 'secure'):
secure(vdr, args)
@@ -442,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()

View File

@@ -51,7 +51,9 @@ def get_secure_key():
try:
backend = get_backend(False)
key = backend.get_password(DOMAIN, USERNAME)[0]
except keyring.errors.KeyringError:
if (key is None):
raise TypeError
except (keyring.errors.KeyringError, TypeError):
try:
key = generate_secure_key(False)[0] # It should be True, but secure enclave causes python segfault
except keyring.errors.PasswordSetError:

View File

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

View File

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