261 Commits

Author SHA1 Message Date
Pol Henarejos
21765a6f10 Move pico-keys-sdk pointer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 13:10:58 +01:00
Pol Henarejos
eb2c92bc5c Merge branch 'development' into eddsa
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-11-21 13:01:10 +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
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
233c5a7c7d Merge branch 'development' into eddsa 2023-09-18 09:33:56 +02:00
Pol Henarejos
3b4ac12d0f Merge branch 'development' into eddsa 2023-09-18 09:02:26 +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
7c5bab8b05 Merge branch 'development' into eddsa 2023-09-18 01:38:39 +02:00
Pol Henarejos
21035d649d Upgrade to version 5.7
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-18 01:38:31 +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
abe91823c0 Build firmwares with -eddsa1 suffix.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-17 19:29:54 +02:00
Pol Henarejos
91e049b997 Merge branch 'development' into eddsa
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-09-17 19:28:41 +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
8836902dc1 Merge branch 'development' into eddsa
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-22 15:32:10 +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
a019b54d69 Merge branch 'development' into eddsa
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-22 13:27:35 +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
3adb1a8422 Merge branch 'development' into eddsa
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-21 19:12:51 +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
95a9fe4214 Added flow triggering for eddsa branch.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 16:49:58 +02:00
Pol Henarejos
8af7cac57a Added authentication tests with EdDSA.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 16:48:12 +02:00
Pol Henarejos
7997eefdc8 Fixed EdDSA signature encapsulation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 16:46:55 +02:00
Pol Henarejos
e18f841a34 Fix Edwards load key.
It did not compute the correct public point.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 16:46:37 +02:00
Pol Henarejos
73b51cabfc Merge branch 'development' into eddsa 2023-08-18 14:10:58 +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
ad3b2bbe4b Added EdDSA credential creation test.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 13:07:52 +02:00
Pol Henarejos
b9ad8f4745 Merge branch 'development' into eddsa 2023-08-18 13:07:13 +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
8242dc8d80 Merge branch 'development' into eddsa
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-18 12:44:52 +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
2f6e4d5568 Upgraded COSE key functions to accept EDDSA.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-17 01:40:22 +02:00
Pol Henarejos
911dab031e Merge branch 'development' into eddsa
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-17 01:36:35 +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
3a71275bc8 Add EDDSA algorithm in get_info.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 18:06:29 +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
9f1e879efe Fix OTP applet selection.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 17:32:17 +02:00
Pol Henarejos
57bf97196d Updated readme.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 14:48:44 +02:00
Pol Henarejos
e8c8ce4d15 Adding support for EdDSA with Ed25519 curve.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 14:47:34 +02:00
Pol Henarejos
69d618cc6b Point to proper EdDSA branch.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 13:13:58 +02:00
Pol Henarejos
e057f17180 Using Pico HSM SDK EdDSA branch.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-08-16 13:07:01 +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
Pol Henarejos
d4ed55b5a5 Upgrade to version 3.0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-26 19:55:13 +02:00
Pol Henarejos
cfb0b8f3f2 Upgrade to version 3.0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-26 19:42:49 +02:00
Pol Henarejos
eca8656bd9 Added support for newer waveshare boards.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-26 19:01:40 +02:00
Pol Henarejos
5b5a9fc0fe Upgrade HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-26 18:58:10 +02:00
Pol Henarejos
59ec9b75fc Increase validity up to 50 years.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-20 09:55:57 +01:00
Pol Henarejos
8b2be54ede Update code style.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-04 14:05:30 +01:00
Pol Henarejos
483073ebb8 Fix tests for CI
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-03 20:31:38 +01:00
Pol Henarejos
12250a1c31 Small fixes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-03 17:54:00 +01:00
Pol Henarejos
3f8aed1ecf Moving pointer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-03 17:36:19 +01:00
Pol Henarejos
707af84292 Add test CI/CD
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-03 01:16:29 +01:00
Pol Henarejos
717e7135d5 Adding test scripts
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-03 01:15:01 +01:00
Pol Henarejos
fc909fa93d Reset device on test_cid_0
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-03 00:50:30 +01:00
Pol Henarejos
03f29f5be6 Fix cbor processing when unknown command is used.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-03 00:23:12 +01:00
Pol Henarejos
267e66eaee Add input with timeout and fix fixtures' scopes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-03 00:04:40 +01:00
Pol Henarejos
22317d4322 Numbering tests
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-03 00:04:12 +01:00
Pol Henarejos
376b49db95 Fix encoding map on credmgmt listing credentials for specific RP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-02 22:07:45 +01:00
Pol Henarejos
9756b451bb Fix test for credmgmt without credProtect.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-02 22:07:12 +01:00
Pol Henarejos
dcdf605a5e Fix crash when missing PubKey type.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-02 22:06:50 +01:00
Pol Henarejos
6d9208f434 Added support for Fido emulation to automatize tests.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-03-02 22:05:04 +01:00
Pol Henarejos
9a493b9972 Fix for non apple builds
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-23 07:57:11 +01:00
Pol Henarejos
ef993d0f7b Using byte serial rpiid.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-23 07:56:48 +01:00
Pol Henarejos
5360d62062 Fix increasing counter on make credential.
Closes #6

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-20 10:23:56 +01:00
Pol Henarejos
6d123e2a0f Moving pointer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-20 10:20:01 +01:00
Pol Henarejos
379f136699 Fix increasing counter on make credential.
Closes #6

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-20 10:19:25 +01:00
Pol Henarejos
5e0b0bfe38 Upgrading to Version 2.10.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-17 12:01:08 +01:00
Pol Henarejos
f3decd8649 Fixes for Pico SDK 1.5
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-02-17 11:51:10 +01:00
Pol Henarejos
4f33d999e3 Adjusting code to work with the emulated interface.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2023-01-09 18:07:41 +01:00
Pol Henarejos
46661ee808 Adding first commit of OTP.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-30 21:34:33 +01:00
Pol Henarejos
b1fdb9b1d1 Cleaning unused includes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-30 19:39:01 +01:00
Pol Henarejos
b3b2e98ec1 Adding OATH conditional compilation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-30 01:55:02 +01:00
Pol Henarejos
cdf96e3564 Fix ifdefs.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-30 01:36:53 +01:00
Pol Henarejos
4fe29750f2 Add some ifdefs for ccid.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-29 02:07:42 +01:00
Pol Henarejos
87bdea7e28 Fix uninitialized var.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-28 17:04:13 +01:00
Pol Henarejos
cecb5da3a0 Added auth check in test_auth.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-27 00:49:21 +01:00
Pol Henarejos
49cf031037 Added test noauth.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-27 00:47:17 +01:00
Pol Henarejos
3a2ab27567 Fix raising APDU
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-27 00:47:00 +01:00
Pol Henarejos
601e1bcda7 Added test_delete.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-27 00:33:32 +01:00
Pol Henarejos
dc6f25caf3 Send RESET with proper P1/P2
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-27 00:33:19 +01:00
Pol Henarejos
20345ebd10 Added P1/P2 check on RESET.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-27 00:21:29 +01:00
Pol Henarejos
6674a0bb1e Add IMF tests.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-27 00:21:17 +01:00
Pol Henarejos
c45c70d95d Added support to overwrite keys with the same name.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-27 00:16:43 +01:00
Pol Henarejos
061b5e919e Fix when IMF is not 8 bytes.
It must be prepended with 0 up to 8 bytes.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-27 00:11:13 +01:00
Pol Henarejos
0dc547dbe5 Add test_bothath for TOTP and HOTP calculation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-26 21:38:30 +01:00
Pol Henarejos
c383f6c446 Fix HOTP CALCULATE.
It is not clear which is the role of IMF, which is 4-bytes length but counter is 8 bytes.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-26 21:38:15 +01:00
Pol Henarejos
c3d6d4c657 Add test_overwrite and test_auth.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-26 20:45:25 +01:00
Pol Henarejos
e387033266 Fix returning ID in VERSION.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-26 20:45:15 +01:00
Pol Henarejos
97336bf8d4 Added first tests for OATH.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-26 19:22:56 +01:00
Pol Henarejos
6ebaa05523 Fix CALCULATE result.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-26 19:21:07 +01:00
Pol Henarejos
00f30ba00c Adding disclaimer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-26 19:20:56 +01:00
Pol Henarejos
363ad1c9e2 No need to call distinguished functions on core0/core1.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-24 01:38:38 +01:00
Pol Henarejos
94806f9bf0 Digits shall be returned in all cases.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-24 00:49:50 +01:00
Pol Henarejos
03b35cfe88 Added OATH calculations (CALCULATE and CALCULATE_ALL).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-23 12:41:45 +01:00
Pol Henarejos
bc9bbaf292 Add VALIDATE instruction.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-23 01:50:05 +01:00
Pol Henarejos
e5ca759dea Add OATH app through CCID interface.
It coexists with FIDO app via HID interface.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-23 01:40:30 +01:00
Pol Henarejos
f780b2a0b4 Fix HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-23 01:39:44 +01:00
Pol Henarejos
b9f1adf211 Fix selecting FIDO with AID.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-22 19:32:25 +01:00
Pol Henarejos
880a1b003d Some fixes in HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-20 23:52:31 +01:00
Pol Henarejos
545860ccbc Update some functions to the newer Pico HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-20 23:42:06 +01:00
Pol Henarejos
9ac5200792 Moving Pico HSM SDK pointer.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-19 22:56:35 +01:00
Pol Henarejos
7542fad31c Upgrade to Version 2.10
Update README also

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-19 22:50:56 +01:00
Pol Henarejos
fe95093484 Upgrading to Version 2.10.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:42:38 +01:00
Pol Henarejos
483a0931a6 Update README with new enhancements and features
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:42:13 +01:00
Pol Henarejos
6644b500fa Add write and read large blob test.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:31:21 +01:00
Pol Henarejos
c8775ec69f Fix computing sha256 of large blob array.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:00:29 +01:00
Pol Henarejos
9160bbb8fe Write default large blob array.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-15 00:00:07 +01:00
Pol Henarejos
19dd52f944 Fix with required parameters.
LB required parameters are not at the begining of map.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 23:59:54 +01:00
Pol Henarejos
4c724d0e8b Fix offset parameter.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 23:51:30 +01:00
Pol Henarejos
81d3da2645 Activating LBW permission.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 23:51:17 +01:00
Pol Henarejos
9bcfacfe08 Fix calling large blobs.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 23:51:03 +01:00
Pol Henarejos
641c2fb880 Minor indent changes.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:42:24 +01:00
Pol Henarejos
cb24927a80 Update get info command to add max large blob array length.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:42:09 +01:00
Pol Henarejos
4ddd45f16e Add ef to large blob array.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:41:48 +01:00
Pol Henarejos
f39a51afca Add macro for large blob size.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:41:38 +01:00
Pol Henarejos
aa4255b875 Add large blob command to cbor.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:41:19 +01:00
Pol Henarejos
a4d82136c2 Compile large blob command.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:40:14 +01:00
Pol Henarejos
4a3f957fdf Add initial large blob command.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-14 19:40:02 +01:00
Pol Henarejos
24b66dcffc Added some largeBlobKey tests.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:48:17 +01:00
Pol Henarejos
703e4697ec Fix loading large blob key from a credential id.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:39:49 +01:00
Pol Henarejos
839562130a Zeroize large blob key.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:37:56 +01:00
Pol Henarejos
e87ae34ab5 Adde largeBlobs to get info.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:31:05 +01:00
Pol Henarejos
2431812a18 Return largeBlobKey on cred management.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-12 00:16:17 +01:00
Pol Henarejos
81717135f5 Add test for credBlob extension.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-11 21:04:55 +01:00
Pol Henarejos
1d1d8ce5c3 Adding info test.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-11 21:04:35 +01:00
Pol Henarejos
6030f33977 Added more tests
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-11 00:07:22 +01:00
Pol Henarejos
360b8eadaa Add minimal test for minPinLength extension.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-10 21:36:17 +01:00
Pol Henarejos
0d51d3c727 Number of credentials is always returned in GA, as Pico Fido does not have any display.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-10 20:49:09 +01:00
Pol Henarejos
d0924f5ecc Some optimizations to speed up tests.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 19:02:23 +01:00
Pol Henarejos
866d69a82d CredMgmt must be redeclared everytime, since PinToken might be changed due to underlaying doMC.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 18:42:59 +01:00
Pol Henarejos
00ba0db87a Test fixes for credMgmt.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 16:34:12 +01:00
Pol Henarejos
c5644d14b0 Fix token precedence
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 16:28:03 +01:00
Pol Henarejos
2cf211cbd0 Fix clearing token rp link.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 16:27:41 +01:00
Pol Henarejos
50418113a9 Authenticator may return 1 number of credentials (not None).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 14:21:04 +01:00
Pol Henarejos
3a92238c0c Fix returning numberOfCredentials based on up and uv flags in the request (not in the response).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 14:20:42 +01:00
Pol Henarejos
270a54f3b7 Adding parenthesis for clearer statement
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 14:19:28 +01:00
Pol Henarejos
a8364c281b When doing GA, GET permission is necessary.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 13:33:51 +01:00
Pol Henarejos
5dcf89cd66 Fix critical bug caused by double free().
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-09 13:13:47 +01:00
Pol Henarejos
5c7be811e8 Return largeBlobKey on getAssertion if credential has largeKeyBlob and if requested.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 21:03:30 +01:00
Pol Henarejos
1707430593 Return largeBlobKey on makeCredential if requested.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 21:02:52 +01:00
Pol Henarejos
a151dc72e4 Embed largeBlobKey presence in credId.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 21:02:23 +01:00
Pol Henarejos
315f01372e Adding largeBlobKey in getInfo.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 21:02:08 +01:00
Pol Henarejos
860cca53e0 Added key derivation for large blob.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 17:07:16 +01:00
Pol Henarejos
d7016f6065 Add MAX_MSG_SIZE in getInfo.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 17:01:01 +01:00
Pol Henarejos
8e9eafaec5 Fix important potential buffer overflow deriving the credential key.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 16:35:19 +01:00
Pol Henarejos
1376c51528 Fix credProtect should not be returned on getAssertion.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:57:41 +01:00
Pol Henarejos
fa5926a3cc credBlob is returned on getAssertion if requested.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:55:27 +01:00
Pol Henarejos
710e03f5a6 Process credBlob on makeCredential.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:54:57 +01:00
Pol Henarejos
9d79505c5a Embed credBlob onto credId.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:54:10 +01:00
Pol Henarejos
4cb0af5045 Defining max length for credBlobs
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:48:46 +01:00
Pol Henarejos
196430517f Added credBlob in getInfo.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:07:02 +01:00
Pol Henarejos
083f9bc787 Moving HSM pointer to support EA.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 15:06:29 +01:00
Pol Henarejos
eb66ec3064 Upgrade to v2.8
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 11:15:44 +01:00
Pol Henarejos
e5834ff7c4 Upgrading to v2.6
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-07 11:07:43 +01:00
Pol Henarejos
3f1aba889e Adding algorithms to get info.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 20:13:28 +01:00
Pol Henarejos
58fbea8929 Added a flag (--filename) to upload an enterprise attestation certificate.
If this flag is not provided, an enteprise attestation certificate is automatically requested and uploaded.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 19:30:00 +01:00
Pol Henarejos
8bf53a6497 Return EA certificate if present.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 19:22:05 +01:00
Pol Henarejos
c89b044825 Added a subcommand to upload an enterprise certificate for enterprise attestation.
If present, when requested enterpriseAttestation==2 for MC, this certificate is returned in the x5c field. If not present, a batch attestation is returned.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 19:21:45 +01:00
Pol Henarejos
004073c3dd Adding FID for Enterprise certificate.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-12-01 19:20:19 +01:00
Pol Henarejos
bae8450a8d Added first step to Enterprise Attestation.
Once enabled, it allows to generate a CSR in the device, which is sent to our PKI. If valid, it returns a signed certificate by an intermediate CA that will be used for attestation.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-30 23:41:05 +01:00
Pol Henarejos
a355f87f82 Fix freeing memory on x509.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-30 23:37:34 +01:00
Pol Henarejos
b023668788 Moving pointer of HSM SDK (again).
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:55:51 +01:00
Pol Henarejos
1b6d1e4b7f Moving pointer of HSM SDK.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:52:05 +01:00
Pol Henarejos
3bea6adf7a Fix requesting CM permission in credMgmt preview.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:50:23 +01:00
Pol Henarejos
54c2df3570 Fix cred RP enumeration return value.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:41:51 +01:00
Pol Henarejos
ae42e28384 Added support for credMgmt preview, despite this info is not broadcasted.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:27:52 +01:00
Pol Henarejos
71c0e865dc Fixed RP attachment to token.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:27:12 +01:00
Pol Henarejos
052ff2d60a Fix requesting a UV token.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 18:02:53 +01:00
Pol Henarejos
8b70c864a4 Added support for enterprise attestation.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-28 17:39:21 +01:00
Pol Henarejos
765db0e98b Update README.md
Added new features for version 2.6.
2022-11-24 15:35:34 +01:00
Pol Henarejos
6b2e95deb0 Adding support for minPinLength extension.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-23 19:11:03 +01:00
Pol Henarejos
d45fa9aae0 Added support for setMinPinLength.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-23 17:01:18 +01:00
Pol Henarejos
23c7e16e6e Fix counting PIN retries.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-23 16:42:49 +01:00
Pol Henarejos
5923f435fe Add support for authenticatorConfig verification.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-23 15:24:09 +01:00
Pol Henarejos
04868f2d7b Added permissions support.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
2022-11-23 13:00:28 +01:00
66 changed files with 5868 additions and 1129 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

@@ -13,10 +13,10 @@ name: "CodeQL"
on:
push:
branches: [ "main" ]
branches: [ "main", "development", "eddsa" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
branches: [ "main", "development", "eddsa" ]
schedule:
- cron: '23 5 * * 4'
@@ -48,11 +48,11 @@ jobs:
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
# - name: Autobuild
@@ -61,7 +61,7 @@ jobs:
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
- run: |

36
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "Emulation and test"
on:
push:
branches: [ "main", "development", "eddsa" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main", "development", "eddsa" ]
schedule:
- cron: '23 5 * * 4'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository and submodules
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build in container
run: ./tests/build-in-docker.sh
- name: Start emulation and test
run: ./tests/run-test-in-docker.sh

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,36 +17,66 @@
cmake_minimum_required(VERSION 3.13)
if(ENABLE_EMULATION)
else()
include(pico_sdk_import.cmake)
endif()
project(pico_fido C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if(ENABLE_EMULATION)
else()
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("Enabling user presence with button")
message(STATUS "User presence with button: \t enabled")
else()
add_definitions(-DENABLE_UP_BUTTON=0)
message("Disabling user presence with button")
message(STATUS "User presence with button: \t disabled")
endif(ENABLE_UP_BUTTON)
option(ENABLE_POWER_ON_RESET "Enable/disable power cycle on reset" ON)
if(ENABLE_POWER_ON_RESET)
add_definitions(-DENABLE_POWER_ON_RESET=1)
message("Enabling power cycle on reset")
message(STATUS "Power cycle on reset: \t enabled")
else()
add_definitions(-DENABLE_POWER_ON_RESET=0)
message("Disabling power cycle on reset")
message(STATUS "Power cycle on reset: \t disabled")
endif(ENABLE_POWER_ON_RESET)
target_sources(pico_fido PUBLIC
option(ENABLE_OATH_APP "Enable/disable OATH application" ON)
if(ENABLE_OATH_APP)
add_definitions(-DENABLE_OATH_APP=1)
message(STATUS "OATH Application: \t\t enabled")
else()
add_definitions(-DENABLE_OATH_APP=0)
message(STATUS "OATH Application: \t\t disabled")
endif(ENABLE_OATH_APP)
option(ENABLE_OTP_APP "Enable/disable OTP application" ON)
if(ENABLE_OTP_APP)
add_definitions(-DENABLE_OTP_APP=1)
message(STATUS "OTP Application: \t\t enabled")
else()
add_definitions(-DENABLE_OTP_APP=0)
message(STATUS "OTP Application: \t\t disabled")
endif(ENABLE_OTP_APP)
if(ENABLE_OTP_APP OR ENABLE_OATH_APP)
set(USB_ITF_CCID 1)
else()
set(USB_ITF_CCID 0)
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/cmd_register.c
@@ -64,14 +94,30 @@ target_sources(pico_fido PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_cred_mgmt.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_config.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_vendor.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/cbor_large_blobs.c
${CMAKE_CURRENT_LIST_DIR}/src/fido/management.c
)
set(HSM_DRIVER "hid")
include(pico-hsm-sdk/pico_hsm_sdk_import.cmake)
if (${ENABLE_OATH_APP})
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/src/fido/oath.c
)
endif()
if (${ENABLE_OTP_APP})
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/src/fido/otp.c
)
endif()
target_include_directories(pico_fido PUBLIC
set(USB_ITF_HID 1)
include(pico-keys-sdk/pico_keys_sdk_import.cmake)
set(INCLUDES ${INCLUDES}
${CMAKE_CURRENT_LIST_DIR}/src/fido
)
target_sources(pico_fido PUBLIC ${SOURCES})
target_include_directories(pico_fido PUBLIC ${INCLUDES})
target_compile_options(pico_fido PUBLIC
-Wall
-Werror
@@ -83,6 +129,23 @@ if (${COMPILER_COLON} GREATER_EQUAL 0)
)
endif()
pico_add_extra_outputs(pico_fido)
if(ENABLE_EMULATION)
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_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)
else()
pico_add_extra_outputs(pico_fido)
target_link_libraries(pico_fido PRIVATE pico_keys_sdk pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board)
endif()

View File

@@ -13,12 +13,29 @@ Pico FIDO has implemented the following features:
- User Verification with PIN
- Discoverable credentials
- Credential management
- ECDSA authentication
- ECDSA and EDDSA authentication
- Authentication with SECP256R1, SECP384R1, SECP521R1, SECP256K1 and Ed25519 curves
- App registration and login
- Device selection
- Support for vendor Config
- Backup with 24 words
- Secure lock to protect the device from flash dumpings
- Permissions support (MC, GA, CM, ACFG, LBW)
- Authenticator configuration
- minPinLength extension
- Self attestation
- Enterprise attestation
- credBlobs extension
- largeBlobKey extension
- largeBlobs support (2048 bytes máx.)
- OATH (based on YKOATH protocol specification)
- TOTP / HOTP
- Yubikey OTP
- Challenge-response generation
- Emulated keyboard interface
- Button press generates an OTP that is written directly is it was typed
- Yubico YKMAN compatible
- Nitrokey nitropy and nitroapp compatible
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.
@@ -30,9 +47,14 @@ If the Pico is stolen the contents of private and secret keys can be read.
## Download
Please, go to the [Release page](https://github.com/polhenarejos/pico-fido/releases "Release page") and download the UF2 file for your board.
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you are planning to use it with OpenSC or similar, you should modify Info.plist of CCID driver to add these VID/PID or use the VID/PID patcher as follows: `./pico-fido-patch-vidpid.sh VID:PID input_fido_file.uf2 output_fido_file.uf2`
Note that UF2 files are shiped with a dummy VID/PID to avoid license issues (FEFF:FCFD). If you are planning to use it with OpenSC or similar, you should modify Info.plist of CCID driver to add these VID/PID or use the [Pico Patcher tool](https://www.picokeys.com/pico-patcher/).
You can use whatever VID/PID, but remember that you are not authorized to distribute the binary with a VID/PID that you do not own.
Alternatively you can use the legacy VID/PID patcher as follows:
`./patch_vidpid.sh VID:PID input_hsm_file.uf2 output_hsm_file.uf2`
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.
Note that the pure-browser option [Pico Patcher tool](https://www.picokeys.com/pico-patcher/) is the most recommended.
## Build
Before building, ensure you have installed the toolchain for the Pico and the Pico SDK is properly located in your drive.
@@ -48,6 +70,8 @@ Note that PICO_BOARD, USB_VID and USB_PID are optional. If not provided, pico bo
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.
**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.
## Led blink
Pico FIDO uses the led to indicate the current status. Four states are available:
### Press to confirm

View File

@@ -1,7 +1,7 @@
#!/bin/bash
VERSION_MAJOR="2"
VERSION_MINOR="4"
VERSION_MAJOR="5"
VERSION_MINOR="8-eddsa1"
rm -rf release/*
cd build_release
@@ -17,6 +17,7 @@ for board in adafruit_feather_rp2040 \
eetree_gamekit_rp2040 \
garatronic_pybstick26_rp2040 \
melopero_shake_rp2040 \
nullbits_bit_c_pro \
pico \
pico_w \
pimoroni_badger2040 \
@@ -31,6 +32,7 @@ for board in adafruit_feather_rp2040 \
pimoroni_servo2040 \
pimoroni_tiny2040 \
pimoroni_tiny2040_2mb \
pololu_3pi_2040_robot \
seeed_xiao_rp2040 \
solderparty_rp2040_stamp \
solderparty_rp2040_stamp_carrier \
@@ -40,6 +42,8 @@ for board in adafruit_feather_rp2040 \
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 \

View File

@@ -17,8 +17,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
VERSION_MAJOR="3" #Version of Pico CCID Core
VERSION_MINOR="4"
VERSION_MAJOR="4" #Version of Pico CCID Core
VERSION_MINOR="0"
echo "----------------------------"
echo "VID/PID patcher for Pico FIDO"

Submodule pico-hsm-sdk deleted from 4a8a6728c7

1
pico-keys-sdk Submodule

Submodule pico-keys-sdk added at e5e2169a47

View File

@@ -15,15 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#ifndef ENABLE_EMULATION
#include "pico/stdlib.h"
#include "ctap2_cbor.h"
#endif
#include "hid/ctap_hid.h"
#include "ctap.h"
#include "ctap_hid.h"
#include "fido.h"
#include "hsm.h"
#include "usb.h"
#include "apdu.h"
#include "management.h"
#include "ctap2_cbor.h"
#include "version.h"
const bool _btrue = true, _bfalse = false;
@@ -37,44 +39,72 @@ int cbor_selection();
int cbor_cred_mgmt(const uint8_t *data, size_t len);
int cbor_config(const uint8_t *data, size_t len);
int cbor_vendor(const uint8_t *data, size_t len);
int cbor_large_blobs(const uint8_t *data, size_t len);
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();
static const uint8_t *cbor_data = NULL;
static size_t cbor_len = 0;
static uint8_t cmd = 0;
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;
int cbor_parse(uint8_t cmd, const uint8_t *data, size_t len) {
if (len == 0)
if (len == 0 && cmd == CTAPHID_CBOR) {
return CTAP1_ERR_INVALID_LEN;
DEBUG_DATA(data+1,len-1);
driver_prepare_response();
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)
return cbor_cred_mgmt(data + 1, len - 1);
else if (data[0] == CTAP_CONFIG)
return cbor_config(data + 1, len - 1);
}
else if (cmd == CTAP_VENDOR_CBOR) {
return cbor_vendor(data, len);
if (len > 0) {
DEBUG_DATA(data + 1, len - 1);
}
return CTAP2_ERR_INVALID_CBOR;
if (cap_supported(CAP_FIDO2)) {
driver_prepare_response_hid();
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);
}
}
else if (cmd == CTAP_VENDOR_CBOR) {
return cbor_vendor(data, len);
}
else if (cmd == 0xC2) {
if (cmd_read_config() == 0x9000) {
res_APDU_size -= 1;
return 0;
}
}
}
return CTAP1_ERR_INVALID_CMD;
}
#ifndef ENABLE_EMULATION
void cbor_thread() {
card_init_core1();
@@ -84,18 +114,24 @@ void cbor_thread() {
if (m == EV_EXIT) {
break;
}
break;
}
apdu.sw = cbor_parse(cmd, cbor_data, cbor_len);
if (apdu.sw == 0)
if (apdu.sw == 0) {
DEBUG_DATA(res_APDU + 1, res_APDU_size);
}
else {
res_APDU[0] = apdu.sw;
apdu.sw = 0;
}
finished_data_size = res_APDU_size + 1;
finished_data_size = res_APDU_size+1;
uint32_t flag = EV_EXEC_FINISHED;
queue_add_blocking(&card_to_usb_q, &flag);
}
}
#endif
int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len) {
cbor_data = data;
@@ -105,3 +141,131 @@ int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len) {
res_APDU_size = 0;
return 1;
}
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;
}
else if (key->grp.id == MBEDTLS_ECP_DP_ED25519) {
alg = FIDO2_ALG_EDDSA;
}
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

@@ -23,17 +23,19 @@
#include "cbor.h"
#include "ctap.h"
#include "ctap2_cbor.h"
#ifndef ENABLE_EMULATION
#include "bsp/board.h"
#endif
#include "hid/ctap_hid.h"
#include "fido.h"
#include "files.h"
#include "random.h"
#include "crypto_utils.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
uint8_t permissions_rp_id = 0, permission_set = 0;
uint32_t usage_timer = 0, initial_usage_time_limit = 0;
uint32_t max_usage_time_period = 600*1000;
uint32_t max_usage_time_period = 600 * 1000;
bool needs_power_cycle = false;
static mbedtls_ecdh_context hkey;
static bool hkey_init = false;
@@ -48,54 +50,65 @@ int beginUsingPinUvAuthToken(bool userIsPresent) {
}
void clearUserPresentFlag() {
if (paut.in_use == true)
if (paut.in_use == true) {
paut.user_present = false;
}
}
void clearUserVerifiedFlag() {
if (paut.in_use == true)
if (paut.in_use == true) {
paut.user_verified = false;
}
}
void clearPinUvAuthTokenPermissionsExceptLbw() {
if (paut.in_use == true)
paut.permissions = FIDO2_PERMISSION_LBW;
if (paut.in_use == true) {
paut.permissions = CTAP_PERMISSION_LBW;
}
}
void stopUsingPinUvAuthToken() {
permissions_rp_id = 0;
paut.permissions = 0;
usage_timer = 0;
paut.in_use = false;
memset(paut.rp_id_hash, 0, sizeof(paut.rp_id_hash));
paut.has_rp_id = false;
initial_usage_time_limit = 0;
paut.user_present = paut.user_verified = false;
user_present_time_limit = 0;
}
bool getUserPresentFlagValue() {
if (paut.in_use != true)
if (paut.in_use != true) {
paut.user_present = false;
}
return paut.user_present;
}
bool getUserVerifiedFlagValue() {
if (paut.in_use != true)
bool getUserVerifiedFlagValue() {
if (paut.in_use != true) {
paut.user_verified = false;
}
return paut.user_verified;
}
}
int regenerate() {
if (hkey_init == true)
if (hkey_init == true) {
mbedtls_ecdh_free(&hkey);
}
mbedtls_ecdh_init(&hkey);
hkey_init = true;
mbedtls_ecdh_setup(&hkey, MBEDTLS_ECP_DP_SECP256R1);
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_gen, NULL);
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp,
&hkey.ctx.mbed_ecdh.d,
&hkey.ctx.mbed_ecdh.Q,
random_gen,
NULL);
mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1);
if (ret != 0)
if (ret != 0) {
return ret;
}
return 0;
}
@@ -103,17 +116,38 @@ int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) {
int ret = 0;
uint8_t buf[32];
ret = mbedtls_mpi_write_binary(z, buf, sizeof(buf));
if (ret != 0)
if (ret != 0) {
return ret;
}
if (protocol == 1) {
return mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), buf, sizeof(buf), sharedSecret);
return mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
buf,
sizeof(buf),
sharedSecret);
}
else if (protocol == 2) {
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
ret = mbedtls_hkdf(md_info, NULL, 0, buf, sizeof(buf), (uint8_t *)"CTAP2 HMAC key", 14, sharedSecret, 32);
if (ret != 0)
ret = mbedtls_hkdf(md_info,
NULL,
0,
buf,
sizeof(buf),
(uint8_t *) "CTAP2 HMAC key",
14,
sharedSecret,
32);
if (ret != 0) {
return ret;
return mbedtls_hkdf(md_info, NULL, 0, buf, sizeof(buf), (uint8_t *)"CTAP2 AES key", 13, sharedSecret+32, 32);
}
return mbedtls_hkdf(md_info,
NULL,
0,
buf,
sizeof(buf),
(uint8_t *) "CTAP2 AES key",
13,
sharedSecret + 32,
32);
}
return -1;
}
@@ -121,7 +155,12 @@ int kdf(uint8_t protocol, const mbedtls_mpi *z, uint8_t *sharedSecret) {
int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret) {
mbedtls_mpi z;
mbedtls_mpi_init(&z);
int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp, &z, Q, &hkey.ctx.mbed_ecdh.d, random_gen, NULL);
int ret = mbedtls_ecdh_compute_shared(&hkey.ctx.mbed_ecdh.grp,
&z,
Q,
&hkey.ctx.mbed_ecdh.d,
random_gen,
NULL);
ret = kdf(protocol, &z, sharedSecret);
mbedtls_mpi_free(&z);
return ret;
@@ -142,12 +181,12 @@ int resetPinUvAuthToken() {
int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_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;
@@ -156,29 +195,36 @@ int encrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_t in_l
int decrypt(uint8_t protocol, const uint8_t *key, const uint8_t *in, size_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);
return aes_decrypt(key + 32, in, 32 * 8, PICO_KEYS_AES_MODE_CBC, out, in_len - IV_SIZE);
}
return -1;
}
int authenticate(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len, uint8_t *sign) {
int authenticate(uint8_t protocol,
const uint8_t *key,
const uint8_t *data,
size_t len,
uint8_t *sign) {
uint8_t hmac[32];
int ret = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
if (ret != 0)
int ret =
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
if (ret != 0) {
return ret;
}
if (protocol == 1) {
memcpy(sign, hmac, 16);
}
else if (protocol == 2) {
memcpy(sign, hmac, 32);
}
else
else {
return -1;
}
return 0;
}
@@ -186,13 +232,17 @@ int verify(uint8_t protocol, const uint8_t *key, const uint8_t *data, size_t len
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);
if (ret != 0)
int ret =
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, 32, data, len, hmac);
if (ret != 0) {
return ret;
if (protocol == 1)
}
if (protocol == 1) {
return memcmp(sign, hmac, 16);
else if (protocol == 2)
}
else if (protocol == 2) {
return memcmp(sign, hmac, 32);
}
return -1;
}
@@ -206,13 +256,17 @@ int getPublicKey() {
}
int pinUvAuthTokenUsageTimerObserver() {
if (usage_timer == 0)
if (usage_timer == 0) {
return -1;
if (usage_timer+max_usage_time_period > board_millis()) {
if (user_present_time_limit == 0 || user_present_time_limit+TRANSPORT_TIME_LIMIT < board_millis())
}
if (usage_timer + max_usage_time_period > board_millis()) {
if (user_present_time_limit == 0 ||
user_present_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
clearUserPresentFlag();
}
if (paut.in_use == true) {
if (initial_usage_time_limit == 0 || initial_usage_time_limit+TRANSPORT_TIME_LIMIT < board_millis()) {
if (initial_usage_time_limit == 0 ||
initial_usage_time_limit + TRANSPORT_TIME_LIMIT < board_millis()) {
stopUsingPinUvAuthToken();
return 1;
}
@@ -232,20 +286,24 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
CborEncoder encoder, mapEncoder;
CborValue map;
CborError error = CborNoError;
CborByteString pinUvAuthParam = {0}, newPinEnc = {0}, pinHashEnc = {0}, kax = {0}, kay = {0};
CborCharString rpId = {0};
CborByteString pinUvAuthParam = { 0 }, newPinEnc = { 0 }, pinHashEnc = { 0 }, kax = { 0 },
kay = { 0 };
CborCharString rpId = { 0 };
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
if (hkey_init == false)
if (hkey_init == false) {
initialize();
}
CBOR_PARSE_MAP_START(map, 1)
{
uint64_t val_u = 0;
CBOR_FIELD_GET_UINT(val_u, 1);
if (val_c <= 2 && val_c != val_u)
if (val_c <= 2 && val_c != val_u) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (val_u < val_c)
}
if (val_u < val_c) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
val_c = val_u + 1;
if (val_u == 0x01) {
CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1);
@@ -254,28 +312,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);
@@ -296,12 +333,13 @@ 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);
if (subcommand == 0x0)
if (subcommand == 0x0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
else if (subcommand == 0x1) { //getPINRetries
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, needs_power_cycle ? 2 : 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, (uint64_t)*file_get_data(ef_pin)));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, (uint64_t) *file_get_data(ef_pin)));
if (needs_power_cycle) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
@@ -313,36 +351,30 @@ 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)
else if (pinUvAuthProtocol == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
else
}
else {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
}
else if (subcommand == 0x3) { //setPIN
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0)
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 ||
newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
}
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if (file_has_data(ef_pin))
}
if (file_has_data(ef_pin)) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
if ((pinUvAuthProtocol == 1 && newPinEnc.len != 64) || (pinUvAuthProtocol == 2 && newPinEnc.len != 64+IV_SIZE))
}
if ((pinUvAuthProtocol == 1 && newPinEnc.len != 64) ||
(pinUvAuthProtocol == 2 && newPinEnc.len != 64 + IV_SIZE)) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.X, kax.data, kax.len) != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
@@ -355,40 +387,60 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (verify(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, pinUvAuthParam.data) != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (verify(pinUvAuthProtocol, sharedSecret, newPinEnc.data, 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);
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
if (ret != 0)
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (paddedNewPin[63] != 0)
}
if (paddedNewPin[63] != 0) {
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
}
uint8_t pin_len = 0;
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin))
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin)) {
pin_len++;
if (pin_len < 4)
}
uint8_t minPin = 4;
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
if (file_has_data(ef_minpin)) {
minPin = *file_get_data(ef_minpin);
}
if (pin_len < minPin) {
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
uint8_t hsh[33];
}
uint8_t hsh[34];
hsh[0] = MAX_PIN_RETRIES;
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 1);
flash_write_data_to_file(ef_pin, hsh, 1+16);
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);
low_flash_available();
goto err; //No return
}
else if (subcommand == 0x4) { //changePIN
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0 || pinHashEnc.present == false)
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 ||
newPinEnc.present == false || pinUvAuthParam.present == false || alg == 0 ||
pinHashEnc.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
}
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if (!file_has_data(ef_pin))
}
if (!file_has_data(ef_pin)) {
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
if (*file_get_data(ef_pin) == 0)
}
if (*file_get_data(ef_pin) == 0) {
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
if ((pinUvAuthProtocol == 1 && (newPinEnc.len != 64 || pinHashEnc.len != 16)) || (pinUvAuthProtocol == 2 && (newPinEnc.len != 64+IV_SIZE || pinHashEnc.len != 16+IV_SIZE)))
}
if ((pinUvAuthProtocol == 1 && (newPinEnc.len != 64 || pinHashEnc.len != 16)) ||
(pinUvAuthProtocol == 2 &&
(newPinEnc.len != 64 + IV_SIZE || pinHashEnc.len != 16 + IV_SIZE))) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.X, kax.data, kax.len) != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
@@ -404,66 +456,108 @@ 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(pinUvAuthProtocol, sharedSecret, tmp, newPinEnc.len + pinHashEnc.len,
pinUvAuthParam.data) != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
uint8_t retries = *file_get_data(ef_pin) - 1;
flash_write_data_to_file(ef_pin, &retries, 1);
uint8_t pin_data[18];
memcpy(pin_data, file_get_data(ef_pin), 18);
pin_data[0] -= 1;
flash_write_data_to_file(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(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
if (ret != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
low_flash_available();
if (memcmp(paddedNewPin, file_get_data(ef_pin)+1, 16) != 0) {
if (memcmp(paddedNewPin, file_get_data(ef_pin) + 2, 16) != 0) {
regenerate();
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
if (retries == 0) {
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
}
if (++new_pin_mismatches >= 3) {
if (++new_pin_mismatches >= 3) {
needs_power_cycle = true;
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
}
else
else {
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
}
}
retries = MAX_PIN_RETRIES;
pin_data[0] = MAX_PIN_RETRIES;
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
low_flash_available();
new_pin_mismatches = 0;
flash_write_data_to_file(ef_pin, &retries, 1);
ret = decrypt(pinUvAuthProtocol, sharedSecret, newPinEnc.data, newPinEnc.len, paddedNewPin);
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (paddedNewPin[63] != 0)
if (paddedNewPin[63] != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
uint8_t pin_len = 0;
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin))
while (paddedNewPin[pin_len] != 0 && pin_len < sizeof(paddedNewPin)) {
pin_len++;
if (pin_len < 4)
}
uint8_t minPin = 4;
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
if (file_has_data(ef_minpin)) {
minPin = *file_get_data(ef_minpin);
}
if (pin_len < minPin) {
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
}
uint8_t hsh[33];
hsh[0] = MAX_PIN_RETRIES;
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, hsh + 1);
flash_write_data_to_file(ef_pin, hsh, 1+16);
hsh[1] = pin_len;
mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), paddedNewPin, pin_len, 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) {
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
}
flash_write_data_to_file(ef_pin, hsh, 2 + 16);
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);
}
low_flash_available();
resetPinUvAuthToken();
goto err; // No return
}
else if (subcommand == 0x9 || subcommand == 0x5) { //getUVRgetPinUvAuthTokenUsingPinWithPermissionsetries
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || alg == 0 || pinHashEnc.present == false)
else if (subcommand == 0x9 || subcommand == 0x5) { //getPinUvAuthTokenUsingPinWithPermissions
if (kax.present == false || kay.present == false || pinUvAuthProtocol == 0 || alg == 0 ||
pinHashEnc.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
}
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if ((subcommand == 0x9 && permissions == 0) || (subcommand == 0x5 && (permissions != 0 || rpId.present == true)))
}
if (subcommand == 0x5 && (permissions != 0 || rpId.present == true)) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
if (!file_has_data(ef_pin))
}
if (subcommand == 0x9) {
if (permissions == 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if ((permissions & CTAP_PERMISSION_BE)) { // Not supported yet
CBOR_ERROR(CTAP2_ERR_UNAUTHORIZED_PERMISSION);
}
}
if (!file_has_data(ef_pin)) {
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
if (*file_get_data(ef_pin) == 0)
}
if (*file_get_data(ef_pin) == 0) {
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
}
if (mbedtls_mpi_read_binary(&hkey.ctx.mbed_ecdh.Qp.X, kax.data, kax.len) != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
@@ -476,46 +570,63 @@ int cbor_client_pin(const uint8_t *data, size_t len) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
uint8_t retries = *file_get_data(ef_pin) - 1;
flash_write_data_to_file(ef_pin, &retries, 1);
uint8_t paddedNewPin[64], poff = (pinUvAuthProtocol-1)*IV_SIZE;
ret = decrypt(pinUvAuthProtocol, sharedSecret, pinHashEnc.data, pinHashEnc.len, paddedNewPin);
uint8_t pin_data[18];
memcpy(pin_data, file_get_data(ef_pin), 18);
pin_data[0] -= 1;
flash_write_data_to_file(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);
if (ret != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
low_flash_available();
if (memcmp(paddedNewPin, file_get_data(ef_pin)+1, 16) != 0) {
if (memcmp(paddedNewPin, file_get_data(ef_pin) + 2, 16) != 0) {
regenerate();
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
if (retries == 0) {
CBOR_ERROR(CTAP2_ERR_PIN_BLOCKED);
}
if (++new_pin_mismatches >= 3) {
if (++new_pin_mismatches >= 3) {
needs_power_cycle = true;
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_BLOCKED);
}
else
else {
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
}
}
retries = MAX_PIN_RETRIES;
pin_data[0] = MAX_PIN_RETRIES;
new_pin_mismatches = 0;
flash_write_data_to_file(ef_pin, &retries, 1);
flash_write_data_to_file(ef_pin, pin_data, sizeof(pin_data));
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) {
CBOR_ERROR(CTAP2_ERR_PIN_INVALID);
}
resetPinUvAuthToken();
beginUsingPinUvAuthToken(false);
if (subcommand == 0x05) {
permissions = CTAP_PERMISSION_MC | CTAP_PERMISSION_GA;
}
paut.permissions = permissions;
if (rpId.present == true)
memcpy(paut.rp_id_hash, rpId.data, 32);
else
memset(paut.rp_id_hash, 0, sizeof(paut.rp_id_hash));
uint8_t pinUvAuthToken_enc[32+IV_SIZE];
if (rpId.present == true) {
mbedtls_sha256((uint8_t *) rpId.data, rpId.len, paut.rp_id_hash, 0);
paut.has_rp_id = true;
}
else {
paut.has_rp_id = false;
}
uint8_t pinUvAuthToken_enc[32 + IV_SIZE];
encrypt(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));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, pinUvAuthToken_enc, 32 + poff));
}
else
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);
err:
@@ -526,8 +637,9 @@ err:
CBOR_FREE_BYTE_STRING(kay);
CBOR_FREE_BYTE_STRING(rpId);
if (error != CborNoError) {
if (error == CborErrorImproperValue)
if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
return error;
}
res_APDU_size = resp_size;

View File

@@ -15,19 +15,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "ctap2_cbor.h"
#include "fido.h"
#include "ctap.h"
#include "bsp/board.h"
#include "hid/ctap_hid.h"
#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/hkdf.h"
#include "mbedtls/sha256.h"
extern uint8_t keydev_dec[32];
extern bool has_keydev_dec;
@@ -36,28 +35,36 @@ 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;
CborByteString pinUvAuthParam = {0}, vendorAutCt = {0};
size_t resp_size = 0;
uint64_t subcommand = 0, pinUvAuthProtocol = 0, vendorCommandId = 0, newMinPinLength = 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;
uint8_t *raw_subpara = NULL;
const bool *forceChangePin = NULL;
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
CBOR_PARSE_MAP_START(map, 1) {
CBOR_PARSE_MAP_START(map, 1)
{
uint64_t val_u = 0;
CBOR_FIELD_GET_UINT(val_u, 1);
if (val_c <= 1 && val_c != val_u)
if (val_c <= 1 && val_c != val_u) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (val_u < val_c)
}
if (val_u < val_c) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
val_c = val_u + 1;
if (val_u == 0x01) {
CBOR_FIELD_GET_UINT(subcommand, 1);
}
else if (val_u == 0x02) {
uint64_t subpara = 0;
CBOR_PARSE_MAP_START(_f1, 2) {
if (subcommand == 0xff) {
raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1);
CBOR_PARSE_MAP_START(_f1, 2)
{
if (subcommand == 0x7f) {
CBOR_FIELD_GET_UINT(subpara, 2);
if (subpara == 0x01) {
CBOR_FIELD_GET_UINT(vendorCommandId, 2);
@@ -66,13 +73,34 @@ int cbor_config(const uint8_t *data, size_t len) {
CBOR_FIELD_GET_BYTES(vendorAutCt, 2);
}
}
else if (subcommand == 0x03) {
CBOR_FIELD_GET_UINT(subpara, 2);
if (subpara == 0x01) {
CBOR_FIELD_GET_UINT(newMinPinLength, 2);
}
else if (subpara == 0x02) {
CBOR_PARSE_ARRAY_START(_f2, 3)
{
CBOR_FIELD_GET_TEXT(minPinLengthRPIDs[minPinLengthRPIDs_len], 3);
minPinLengthRPIDs_len++;
if (minPinLengthRPIDs_len >= 32) {
CBOR_ERROR(CTAP2_ERR_KEY_STORE_FULL);
}
}
CBOR_PARSE_ARRAY_END(_f2, 3);
}
else if (subpara == 0x03) {
CBOR_FIELD_GET_BOOL(forceChangePin, 2);
}
}
}
CBOR_PARSE_MAP_END(_f1, 2);
raw_subpara_len = cbor_value_get_next_byte(&_f1) - raw_subpara;
}
else if (val_u == 0x03) {
CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1);
}
else if (val_u == 0x04) { // pubKeyCredParams
else if (val_u == 0x04) {
CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1);
}
}
@@ -80,22 +108,52 @@ int cbor_config(const uint8_t *data, size_t len) {
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
if (subcommand == 0xff) {
if (pinUvAuthParam.present == false) {
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
}
if (pinUvAuthProtocol == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
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;
memcpy(verify_payload + 34, raw_subpara, raw_subpara_len);
error = verify(pinUvAuthProtocol,
paut.data,
verify_payload,
32 + 1 + 1 + raw_subpara_len,
pinUvAuthParam.data);
free(verify_payload);
if (error != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (!(paut.permissions & CTAP_PERMISSION_ACFG)) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (subcommand == 0x7f) {
if (vendorCommandId == CTAP_CONFIG_AUT_DISABLE) {
if (!file_has_data(ef_keydev_enc))
if (!file_has_data(ef_keydev_enc)) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
if (has_keydev_dec == false)
}
if (has_keydev_dec == false) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
flash_write_data_to_file(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
low_flash_available();
}
else if (vendorCommandId == CTAP_CONFIG_AUT_ENABLE) {
if (!file_has_data(ef_keydev))
if (!file_has_data(ef_keydev)) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
if (mse.init == false)
}
if (mse.init == false) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
mbedtls_chachapoly_context chatx;
int ret = mse_decrypt_ct(vendorAutCt.data, vendorAutCt.len);
@@ -103,13 +161,20 @@ int cbor_config(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
uint8_t key_dev_enc[12+32+16];
uint8_t key_dev_enc[12 + 32 + 16];
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){
if (ret != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
@@ -124,18 +189,58 @@ int cbor_config(const uint8_t *data, size_t len) {
}
goto err;
}
else
else if (subcommand == 0x03) {
uint8_t currentMinPinLen = 4;
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
if (file_has_data(ef_minpin)) {
currentMinPinLen = *file_get_data(ef_minpin);
}
if (newMinPinLength == 0) {
newMinPinLength = currentMinPinLen;
}
else if (newMinPinLength > 0 && newMinPinLength < currentMinPinLen) {
CBOR_ERROR(CTAP2_ERR_PIN_POLICY_VIOLATION);
}
if (forceChangePin == ptrue && !file_has_data(ef_pin)) {
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
}
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);
}
flash_write_data_to_file(ef_minpin, data, 2 + minPinLengthRPIDs_len * 32);
low_flash_available();
goto err; //No return
}
else if (subcommand == 0x01) {
set_opts(get_opts() | FIDO2_OPT_EA);
goto err;
}
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);
err:
err:
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
CBOR_FREE_BYTE_STRING(vendorAutCt);
for (int i = 0; i < minPinLengthRPIDs_len; i++) {
CBOR_FREE_BYTE_STRING(minPinLengthRPIDs[i]);
}
if (error != CborNoError) {
if (error == CborErrorImproperValue)
if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
return error;
}
res_APDU_size = resp_size;

View File

@@ -15,66 +15,72 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ctap2_cbor.h"
#include "fido.h"
#include "ctap.h"
#include "bsp/board.h"
#include "hid/ctap_hid.h"
#include "cbor_make_credential.h"
#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;
uint8_t cred_counter = 1;
uint8_t cred_total = 0;
CborByteString rpIdHashx = {0};
CborByteString rpIdHashx = { 0 };
int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CborParser parser;
CborValue map;
CborError error = CborNoError;
uint64_t subcommand = 0, pinUvAuthProtocol = 0;
CborByteString pinUvAuthParam = {0}, rpIdHash = {0};
PublicKeyCredentialDescriptor credentialId = {0};
PublicKeyCredentialUserEntity user = {0};
CborByteString pinUvAuthParam = { 0 }, rpIdHash = { 0 };
PublicKeyCredentialDescriptor credentialId = { 0 };
PublicKeyCredentialUserEntity user = { 0 };
size_t resp_size = 0;
CborEncoder encoder, mapEncoder, mapEncoder2;
uint8_t *raw_subpara = NULL;
size_t raw_subpara_len = 0;
bool asserted = false;
bool asserted = false, is_preview = *(data - 1) == 0x41; // Backwards compatibility
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
CBOR_PARSE_MAP_START(map, 1) {
CBOR_PARSE_MAP_START(map, 1)
{
uint64_t val_u = 0;
CBOR_FIELD_GET_UINT(val_u, 1);
if (val_c <= 1 && val_c != val_u)
if (val_c <= 1 && val_c != val_u) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (val_u < val_c)
}
if (val_u < val_c) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
val_c = val_u + 1;
if (val_u == 0x01) {
CBOR_FIELD_GET_UINT(subcommand, 1);
}
else if (val_u == 0x02) {
uint64_t subpara = 0;
raw_subpara = (uint8_t *)cbor_value_get_next_byte(&_f1);
CBOR_PARSE_MAP_START(_f1, 2) {
raw_subpara = (uint8_t *) cbor_value_get_next_byte(&_f1);
CBOR_PARSE_MAP_START(_f1, 2)
{
CBOR_FIELD_GET_UINT(subpara, 2);
if (subpara == 0x01) {
CBOR_FIELD_GET_BYTES(rpIdHash, 2);
}
else if (subpara == 0x02) {
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", credentialId.id);
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "type", credentialId.type);
if (strcmp(_fd3, "transports") == 0) {
CBOR_PARSE_ARRAY_START(_f3, 4) {
CBOR_FIELD_GET_TEXT(credentialId.transports[credentialId.transports_len], 4);
CBOR_PARSE_ARRAY_START(_f3, 4)
{
CBOR_FIELD_GET_TEXT(credentialId.transports[credentialId.
transports_len], 4);
credentialId.transports_len++;
}
CBOR_PARSE_ARRAY_END(_f3, 4);
@@ -83,7 +89,8 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_PARSE_MAP_END(_f2, 3);
}
else if (subpara == 0x03) {
CBOR_PARSE_MAP_START(_f1, 3) {
CBOR_PARSE_MAP_START(_f1, 3)
{
CBOR_FIELD_GET_KEY_TEXT(3);
CBOR_FIELD_KEY_TEXT_VAL_BYTES(3, "id", user.id);
CBOR_FIELD_KEY_TEXT_VAL_TEXT(3, "name", user.parent.name);
@@ -105,76 +112,105 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_PARSE_MAP_END(map, 1);
if (subcommand != 0x03 && subcommand != 0x05) {
if (pinUvAuthParam.present == false)
if (pinUvAuthParam.present == false) {
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
}
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
}
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
if(subcommand == 0x01) {
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *)"\x01", 1, pinUvAuthParam.data) != CborNoError)
if (subcommand == 0x01) {
if (verify(pinUvAuthProtocol, paut.data, (const uint8_t *) "\x01", 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)) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
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(EF_CRED + i))) {
existing++;
}
}
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, existing));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_RESIDENT_CREDENTIALS-existing));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_RESIDENT_CREDENTIALS - existing));
}
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(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)) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
rp_counter = 1;
rp_total = 0;
}
else {
if (rp_counter > rp_total)
if (rp_counter > rp_total) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
}
uint8_t skip = 0;
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *tef = search_dynamic_file(EF_RP + i);
if (file_has_data(tef) && *file_get_data(tef) > 0) {
if (++skip == rp_counter) {
if (rp_ef == NULL)
if (rp_ef == NULL) {
rp_ef = tef;
if (subcommand == 0x03)
}
if (subcommand == 0x03) {
break;
}
}
if (subcommand == 0x02)
if (subcommand == 0x02) {
rp_total++;
}
}
}
if (rp_ef == NULL) // should not happen
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
if (rp_ef == NULL) {
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
}
rp_counter++;
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, subcommand == 0x02 ? 3 : 2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x03));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, 1));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, file_get_data(rp_ef)+33, file_get_size(rp_ef)-33));
CBOR_CHECK(cbor_encode_text_string(&mapEncoder2, (char *) file_get_data(rp_ef) + 33,
file_get_size(rp_ef) - 33));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(rp_ef)+1, 32));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, file_get_data(rp_ef) + 1, 32));
if (subcommand == 0x02) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, rp_total));
}
}
else if (subcommand == 0x04 || subcommand == 0x05) {
if (subcommand == 0x04 && rpIdHash.present == false)
if (subcommand == 0x04 && rpIdHash.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
if (subcommand == 0x04) {
*(raw_subpara-1) = 0x04;
if (verify(pinUvAuthProtocol, paut.data, raw_subpara-1, raw_subpara_len+1, pinUvAuthParam.data) != CborNoError)
*(raw_subpara - 1) = 0x04;
if (verify(pinUvAuthProtocol, paut.data, raw_subpara - 1, raw_subpara_len + 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 && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
cred_counter = 1;
cred_total = 0;
}
@@ -190,21 +226,27 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
file_t *tef = search_dynamic_file(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)
if (cred_ef == NULL) {
cred_ef = tef;
if (subcommand == 0x05)
}
if (subcommand == 0x05) {
break;
}
}
if (subcommand == 0x04)
if (subcommand == 0x04) {
cred_total++;
}
}
}
if (!file_has_data(cred_ef))
if (!file_has_data(cred_ef)) {
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
}
Credential cred = {0};
if (credential_load(file_get_data(cred_ef)+32, file_get_size(cred_ef)-32, rpIdHash.data, &cred) != 0)
Credential 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);
@@ -215,16 +257,32 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
cred_counter++;
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, subcommand == 0x04 ? 5 : 4));
uint8_t l = 4;
if (subcommand == 0x04) {
l++;
}
if (cred.extensions.present == true) {
if (cred.extensions.credProtect > 0) {
l++;
}
if (cred.extensions.largeBlobKey == ptrue) {
l++;
}
}
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x06));
uint8_t l = 0;
if (cred.userId.present == true)
l = 0;
if (cred.userId.present == true) {
l++;
if (cred.userName.present == true)
}
if (cred.userName.present == true) {
l++;
if (cred.userDisplayName.present == true)
}
if (cred.userDisplayName.present == true) {
l++;
}
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, l));
if (cred.userId.present == true) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
@@ -232,11 +290,13 @@ 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));
@@ -249,21 +309,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));
@@ -273,33 +319,69 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
asserted = true;
rpIdHashx = rpIdHash;
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, cred.extensions.credProtect));
if (cred.extensions.present == true) {
if (cred.extensions.credProtect > 0) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, cred.extensions.credProtect));
}
if (cred.extensions.largeBlobKey == ptrue) {
uint8_t largeBlobKey[32];
int ret = credential_derive_large_blob_key(cred.id.data, cred.id.len, largeBlobKey);
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0B));
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);
}
else if (subcommand == 0x06) {
if (credentialId.id.present == false)
if (credentialId.id.present == false) {
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(pinUvAuthProtocol, paut.data, raw_subpara - 1, raw_subpara_len + 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 && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
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) {
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)
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) {
uint8_t *rp_data = (uint8_t *)calloc(1, file_get_size(rp_ef));
if (file_has_data(rp_ef) &&
memcmp(file_get_data(rp_ef) + 1, rp_id_hash, 32) == 0) {
uint8_t *rp_data = (uint8_t *) calloc(1, file_get_size(rp_ef));
memcpy(rp_data, file_get_data(rp_ef), file_get_size(rp_ef));
rp_data[0] -= 1;
if (rp_data[0] == 0)
if (rp_data[0] == 0) {
delete_file(rp_ef);
else
}
else {
flash_write_data_to_file(rp_ef, rp_data, file_get_size(rp_ef));
}
free(rp_data);
break;
}
@@ -311,25 +393,41 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
}
else if (subcommand == 0x07) {
if (credentialId.id.present == false || user.id.present == false)
if (credentialId.id.present == false || user.id.present == false) {
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(pinUvAuthProtocol, paut.data, raw_subpara - 1, raw_subpara_len + 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 && memcmp(paut.rp_id_hash, rpIdHash.data, 32) != 0))) {
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) {
Credential cred = {0};
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);
}
uint8_t newcred[MAX_CRED_ID_LENGTH];
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) {
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) {
credential_free(&cred);
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
@@ -345,7 +443,7 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
}
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
err:
err:
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
if (asserted == false) {
@@ -356,11 +454,12 @@ int cbor_cred_mgmt(const uint8_t *data, size_t len) {
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.transports[n]);
}
CBOR_FREE_BYTE_STRING(credentialId.transports[n]);
}
if (error != CborNoError) {
if (error == CborErrorImproperValue)
if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
return error;
}
res_APDU_size = resp_size;

View File

@@ -17,22 +17,24 @@
#include "cbor.h"
#include "ctap.h"
#include "ctap2_cbor.h"
#ifndef ENABLE_EMULATION
#include "bsp/board.h"
#endif
#include "hid/ctap_hid.h"
#include "fido.h"
#include "files.h"
#include "random.h"
#include "crypto_utils.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
#include "cbor_make_credential.h"
#include "credential.h"
#include <math.h>
#include "mbedtls/sha256.h"
#include "random.h"
int cbor_get_assertion(const uint8_t *data, size_t len, bool next);
bool residentx = false;
Credential credsx[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
Credential credsx[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
uint8_t credentialCounter = 1;
uint8_t numberOfCredentialsx = 0;
uint8_t flagsx = 0;
@@ -42,17 +44,20 @@ size_t lenx = 0;
int cbor_get_next_assertion(const uint8_t *data, size_t len) {
CborError error = CborNoError;
if (credentialCounter >= numberOfCredentialsx)
if (credentialCounter >= numberOfCredentialsx) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
if (timerx+30*1000 < board_millis())
}
if (timerx + 30 * 1000 < board_millis()) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
CBOR_CHECK(cbor_get_assertion(datax, lenx, true));
timerx = board_millis();
credentialCounter++;
err:
if (error != CborNoError || credentialCounter == numberOfCredentialsx) {
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
credential_free(&credsx[i]);
}
if (datax) {
free(datax);
datax = NULL;
@@ -63,8 +68,9 @@ err:
flagsx = 0;
credentialCounter = 0;
numberOfCredentialsx = 0;
if (error == CborErrorImproperValue)
if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
return error;
}
return 0;
@@ -73,31 +79,35 @@ err:
int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
size_t resp_size = 0;
uint64_t pinUvAuthProtocol = 0, hmacSecretPinUvAuthProtocol = 1;
CredOptions options = {0};
CredExtensions extensions = {0};
CredOptions options = { 0 };
CredExtensions extensions = { 0 };
CborParser parser;
CborEncoder encoder, mapEncoder, mapEncoder2;
CborValue map;
CborError error = CborNoError;
CborByteString pinUvAuthParam = {0}, clientDataHash = {0};
CborCharString rpId = {0};
PublicKeyCredentialDescriptor allowList[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
Credential creds[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
CborByteString pinUvAuthParam = { 0 }, clientDataHash = { 0 };
CborCharString rpId = { 0 };
PublicKeyCredentialDescriptor allowList[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
Credential creds[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
size_t allowList_len = 0, creds_len = 0;
uint8_t *aut_data = NULL;
bool asserted = false;
bool asserted = false, up = true, uv = false;
int64_t kty = 2, alg = 0, crv = 0;
CborByteString kax = {0}, kay = {0}, salt_enc = {0}, salt_auth = {0};
CborByteString kax = { 0 }, kay = { 0 }, salt_enc = { 0 }, salt_auth = { 0 };
const bool *credBlob = NULL;
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
CBOR_PARSE_MAP_START(map, 1) {
CBOR_PARSE_MAP_START(map, 1)
{
uint64_t val_u = 0;
CBOR_FIELD_GET_UINT(val_u, 1);
if (val_c <= 2 && val_c != val_u)
if (val_c <= 2 && val_c != val_u) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (val_u < val_c)
}
if (val_u < val_c) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
val_c = val_u + 1;
if (val_u == 0x01) {
CBOR_FIELD_GET_TEXT(rpId, 1);
@@ -106,14 +116,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_FIELD_GET_BYTES(clientDataHash, 1);
}
else if (val_u == 0x03) { // excludeList
CBOR_PARSE_ARRAY_START(_f1, 2) {
PublicKeyCredentialDescriptor *pc = &allowList[allowList_len];
CBOR_PARSE_MAP_START(_f2, 3) {
CBOR_PARSE_ARRAY_START(_f1, 2)
{
PublicKeyCredentialDescriptor *pc = &allowList[allowList_len];
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++;
}
@@ -127,36 +140,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
else if (val_u == 0x04) { // extensions
extensions.present = true;
CBOR_PARSE_MAP_START(_f1, 2) {
CBOR_PARSE_MAP_START(_f1, 2)
{
CBOR_FIELD_GET_KEY_TEXT(2);
if (strcmp(_fd2, "hmac-secret") == 0) {
extensions.hmac_secret = ptrue;
uint64_t ukey = 0;
CBOR_PARSE_MAP_START(_f2, 3) {
CBOR_PARSE_MAP_START(_f2, 3)
{
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);
@@ -167,20 +161,24 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
else if (ukey == 0x04) {
CBOR_FIELD_GET_UINT(hmacSecretPinUvAuthProtocol, 3);
}
else
else {
CBOR_ADVANCE(3);
}
}
CBOR_PARSE_MAP_END(_f2, 3);
continue;
}
CBOR_FIELD_KEY_TEXT_VAL_UINT(2, "credProtect", extensions.credProtect);
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);
}
else if (val_u == 0x05) { // 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);
@@ -198,12 +196,13 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
CBOR_PARSE_MAP_END(map, 1);
if (rpId.present == false || clientDataHash.present == false)
if (rpId.present == false || clientDataHash.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
uint8_t flags = 0;
uint8_t rp_id_hash[32];
mbedtls_sha256((uint8_t *)rpId.data, rpId.len, rp_id_hash, 0);
mbedtls_sha256((uint8_t *) rpId.data, rpId.len, rp_id_hash, 0);
bool resident = false;
uint8_t numberOfCredentials = 0;
@@ -211,18 +210,23 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
if (next == false) {
if (pinUvAuthParam.present == true) {
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
if (check_user_presence() == false)
if (check_user_presence() == false) {
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
if (!file_has_data(ef_pin))
}
if (!file_has_data(ef_pin)) {
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
else
}
else {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
}
else {
if (pinUvAuthProtocol == 0)
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
if (pinUvAuthProtocol == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
}
}
if (options.present) {
@@ -236,68 +240,109 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_ERROR(CTAP2_ERR_UNSUPPORTED_OPTION);
}
//else if (options.up == NULL) //5.7
//rup = ptrue;
//rup = ptrue;
if (options.uv != NULL) {
uv = *options.uv;
}
if (options.up != NULL) {
up = *options.up;
}
}
if (pinUvAuthParam.present == true) { //6.1
int ret = verify(pinUvAuthProtocol, paut.data, clientDataHash.data, clientDataHash.len, pinUvAuthParam.data);
if (ret != CborNoError)
int ret = verify(pinUvAuthProtocol,
paut.data,
clientDataHash.data,
clientDataHash.len,
pinUvAuthParam.data);
if (ret != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (getUserVerifiedFlagValue() == false)
}
if (getUserVerifiedFlagValue() == false) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (!(paut.permissions & CTAP_PERMISSION_GA)) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
flags |= FIDO2_AUT_FLAG_UV;
// Check pinUvAuthToken permissions. See 6.2.2.4
}
if (extensions.present == true && extensions.hmac_secret == ptrue) {
if (kax.present == false || kay.present == false || crv == 0 || alg == 0 || salt_enc.present == false || salt_auth.present == false)
if (kax.present == false || kay.present == false || crv == 0 || alg == 0 ||
salt_enc.present == false || salt_auth.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (salt_enc.len != 32+(hmacSecretPinUvAuthProtocol-1)*IV_SIZE && salt_enc.len != 64+(hmacSecretPinUvAuthProtocol-1)*IV_SIZE)
}
if (salt_enc.len != 32 + (hmacSecretPinUvAuthProtocol - 1) * IV_SIZE &&
salt_enc.len != 64 + (hmacSecretPinUvAuthProtocol - 1) * IV_SIZE) {
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
}
}
if (allowList_len > 0) {
for (int e = 0; e < allowList_len; e++) {
if (allowList[e].type.present == false || allowList[e].id.present == false)
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)
}
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) {
}
if (credential_load(allowList[e].id.data, allowList[e].id.len, rp_id_hash,
&creds[creds_len]) != 0) {
CBOR_FREE_BYTE_STRING(allowList[e].id);
credential_free(&creds[creds_len]);
}
else
else {
creds_len++;
}
}
}
else {
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS && creds_len < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
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);
if (!file_has_data(ef) || memcmp(file_get_data(ef), rp_id_hash, 32) != 0)
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]);
if (ret != 0)
}
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]);
else
}
else {
creds_len++;
}
}
resident = true;
}
for (int 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 && resident == true && !(flags & FIDO2_AUT_FLAG_UV))
}
else if (creds[i].extensions.credProtect == CRED_PROT_UV_OPTIONAL_WITH_LIST &&
resident == true && !(flags & FIDO2_AUT_FLAG_UV)) {
credential_free(&creds[i]);
else
}
else {
creds[numberOfCredentials++] = creds[i];
}
}
else
else {
creds[numberOfCredentials++] = creds[i];
}
}
}
if (numberOfCredentials == 0)
if (numberOfCredentials == 0) {
CBOR_ERROR(CTAP2_ERR_NO_CREDENTIALS);
}
for (int i = 0; i < numberOfCredentials; i++) {
for (int j = i + 1; j < numberOfCredentials; j++) {
@@ -312,14 +357,16 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
if (options.up == ptrue || options.present == false || options.up == NULL) { //9.1
if (pinUvAuthParam.present == true) {
if (getUserPresentFlagValue() == false) {
if (check_user_presence() == false)
if (check_user_presence() == false) {
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
}
}
}
else {
if (!(flags & FIDO2_AUT_FLAG_UP)) {
if (check_user_presence() == false)
if (check_user_presence() == false) {
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
}
}
}
flags |= FIDO2_AUT_FLAG_UP;
@@ -328,7 +375,11 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
clearPinUvAuthTokenPermissionsExceptLbw();
}
if (!(flags & FIDO2_AUT_FLAG_UP) && !(flags & FIDO2_AUT_FLAG_UV)) {
if (extensions.largeBlobKey == pfalse) {
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
}
if (up == false && uv == false) {
selcred = &creds[0];
}
else {
@@ -336,10 +387,11 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
if (numberOfCredentials > 1) {
asserted = true;
residentx = resident;
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
credsx[i] = creds[i];
}
numberOfCredentialsx = numberOfCredentials;
datax = (uint8_t *)calloc(1, len);
datax = (uint8_t *) calloc(1, len);
memcpy(datax, data, len);
lenx = len;
flagsx = flags;
@@ -354,31 +406,51 @@ 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);
mbedtls_ecp_keypair ekey;
mbedtls_ecp_keypair_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);
mbedtls_ecp_keypair_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);
}
}
size_t ext_len = 0;
uint8_t ext [512];
uint8_t ext[512];
if (extensions.present == true) {
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
int l = 0;
if (options.up == pfalse)
if (options.up == pfalse) {
extensions.hmac_secret = NULL;
if (extensions.hmac_secret != NULL)
}
if (extensions.hmac_secret != NULL) {
l++;
if (extensions.credProtect != 0)
}
if (credBlob == ptrue) {
l++;
}
if (extensions.thirdPartyPayment != NULL) {
l++;
}
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, l));
if (extensions.credProtect != 0) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, extensions.credProtect));
if (credBlob == ptrue) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credBlob"));
if (selcred->extensions.credBlob.present == true) {
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, selcred->extensions.credBlob.data,
selcred->extensions.credBlob.len));
}
else {
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, NULL, 0));
}
}
if (extensions.hmac_secret != NULL) {
@@ -402,12 +474,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
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(hmacSecretPinUvAuthProtocol, sharedSecret, salt_enc.data, 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], poff = (hmacSecretPinUvAuthProtocol - 1) * IV_SIZE;
ret = decrypt(hmacSecretPinUvAuthProtocol,
sharedSecret,
salt_enc.data,
salt_enc.len,
salt_dec);
if (ret != 0) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
@@ -418,17 +495,39 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
mbedtls_platform_zeroize(sharedSecret, sizeof(sharedSecret));
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (flags & FIDO2_AUT_FLAG_UV)
if (flags & FIDO2_AUT_FLAG_UV) {
crd = cred_random + 32;
else
}
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);
encrypt(hmacSecretPinUvAuthProtocol, sharedSecret, out1, salt_enc.len-poff, hmac_res);
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);
}
encrypt(hmacSecretPinUvAuthProtocol, sharedSecret, out1, 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);
@@ -438,7 +537,7 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
uint32_t ctr = get_sign_counter();
size_t aut_data_len = 32 + 1 + 4 + ext_len;
aut_data = (uint8_t *)calloc(1, aut_data_len + clientDataHash.len);
aut_data = (uint8_t *) calloc(1, aut_data_len + clientDataHash.len);
uint8_t *pa = aut_data;
memcpy(pa, rp_id_hash, 32); pa += 32;
*pa++ = flags;
@@ -447,21 +546,66 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
*pa++ = ctr >> 8;
*pa++ = ctr & 0xff;
memcpy(pa, ext, ext_len); pa += ext_len;
if (pa-aut_data != aut_data_len)
if (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], sig[MBEDTLS_ECDSA_MAX_LEN];
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);
}
else if (ekey.grp.id == MBEDTLS_ECP_DP_ED25519) {
md = NULL;
}
size_t olen = 0;
ret = mbedtls_ecdsa_write_signature(&ekey, MBEDTLS_MD_SHA256, hash, 32, sig, sizeof(sig), &olen, random_gen, NULL);
mbedtls_ecdsa_free(&ekey);
if (md != NULL) {
ret = mbedtls_md(md,
aut_data,
aut_data_len + clientDataHash.len,
hash);
ret = mbedtls_ecdsa_write_signature(&ekey,
mbedtls_md_get_type(md),
hash,
mbedtls_md_get_size(md),
sig,
sizeof(sig),
&olen,
random_gen,
NULL);
}
else {
ret = mbedtls_eddsa_write_signature(&ekey,
aut_data,
aut_data_len + clientDataHash.len,
sig,
sizeof(sig),
&olen,
MBEDTLS_EDDSA_PURE,
NULL,
0,
random_gen,
NULL);
}
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
mbedtls_ecp_keypair_free(&ekey);
uint8_t lfields = 3;
if (selcred->opts.present == true && selcred->opts.rk == ptrue)
if (selcred->opts.present == true && selcred->opts.rk == ptrue) {
lfields++;
if (numberOfCredentials > 1 && next == false && !(flags & FIDO2_AUT_FLAG_UP) && !(flags & FIDO2_AUT_FLAG_UV))
}
if (numberOfCredentials > 1 && next == false) {
lfields++;
}
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
lfields++;
}
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, lfields));
@@ -482,14 +626,17 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
uint8_t lu = 1;
if (numberOfCredentials > 1 && allowList_len == 0) {
if (selcred->userName.present == true)
if (selcred->userName.present == true) {
lu++;
if (selcred->userDisplayName.present == true)
}
if (selcred->userDisplayName.present == true) {
lu++;
}
}
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, lu));
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "id"));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->userId.data, selcred->userId.len));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder2, selcred->userId.data,
selcred->userId.len));
if (numberOfCredentials > 1 && allowList_len == 0) {
if (selcred->userName.present == true) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "name"));
@@ -502,22 +649,28 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
}
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &mapEncoder2));
}
if (numberOfCredentials > 1 && next == false && !(flags & FIDO2_AUT_FLAG_UP) && !(flags & FIDO2_AUT_FLAG_UV)) {
if (numberOfCredentials > 1 && next == false) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, numberOfCredentials));
}
if (extensions.largeBlobKey == ptrue && selcred->extensions.largeBlobKey == ptrue) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
}
mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey));
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));
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
low_flash_available();
err:
err:
CBOR_FREE_BYTE_STRING(clientDataHash);
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
CBOR_FREE_BYTE_STRING(rpId);
if (asserted == false) {
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++)
for (int i = 0; i < MAX_CREDENTIAL_COUNT_IN_LIST; i++) {
credential_free(&creds[i]);
}
}
for (int m = 0; m < allowList_len; m++) {
@@ -527,11 +680,13 @@ int cbor_get_assertion(const uint8_t *data, size_t len, bool next) {
CBOR_FREE_BYTE_STRING(allowList[m].transports[n]);
}
}
if (aut_data)
if (aut_data) {
free(aut_data);
}
if (error != CborNoError) {
if (error == CborErrorImproperValue)
if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
return error;
}
res_APDU_size = resp_size;

View File

@@ -16,6 +16,7 @@
*/
#include "ctap2_cbor.h"
#include "hid/ctap_hid.h"
#include "fido.h"
#include "ctap.h"
#include "files.h"
@@ -23,10 +24,10 @@
#include "version.h"
int cbor_get_info() {
CborEncoder encoder, mapEncoder, arrayEncoder;
CborEncoder encoder, mapEncoder, arrayEncoder, mapEncoder2;
CborError error = CborNoError;
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 10));
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 15));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 3));
@@ -36,16 +37,22 @@ 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, 2));
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));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, aaguid, sizeof(aaguid)));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 5));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &arrayEncoder, 8));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "ep"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, get_opts() & FIDO2_OPT_EA));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "rk"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "credMgmt"));
@@ -53,14 +60,23 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "authnrCfg"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "clientPin"));
if (file_has_data(ef_pin))
if (file_has_data(ef_pin)) {
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
else
}
else {
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, false));
}
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "largeBlobs"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "pinUvAuthToken"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
CBOR_CHECK(cbor_encode_text_stringz(&arrayEncoder, "setMinPINLength"));
CBOR_CHECK(cbor_encode_boolean(&arrayEncoder, true));
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_MSG_SIZE));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x06));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 2));
CBOR_CHECK(cbor_encode_uint(&arrayEncoder, 1)); // PIN protocols
@@ -73,12 +89,42 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x08));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CRED_ID_LENGTH)); // MAX_CRED_ID_MAX_LENGTH
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0D));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 4)); // minPINLength
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0A));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 5));
CBOR_CHECK(COSE_public_key(FIDO2_ALG_ES256, &arrayEncoder, &mapEncoder2));
CBOR_CHECK(COSE_public_key(FIDO2_ALG_EDDSA, &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));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_LARGE_BLOB_SIZE)); // maxSerializedLargeBlobArray
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0C));
if (file_has_data(ef_minpin) && file_get_data(ef_minpin)[1] == 1) {
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, true));
}
else {
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, false));
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0D));
if (file_has_data(ef_minpin)) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, *file_get_data(ef_minpin))); // minPINLength
}
else {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 4)); // minPINLength
}
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0E));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, PICO_FIDO_VERSION)); // firmwareVersion
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x0F));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, MAX_CREDBLOB_LENGTH)); // maxCredBlobLength
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x15));
CBOR_CHECK(cbor_encoder_create_array(&mapEncoder, &arrayEncoder, 2));
CBOR_CHECK(cbor_encode_uint(&arrayEncoder, CTAP_CONFIG_AUT_ENABLE));
@@ -86,9 +132,10 @@ int cbor_get_info() {
CBOR_CHECK(cbor_encoder_close_container(&mapEncoder, &arrayEncoder));
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
err:
if (error != CborNoError)
err:
if (error != CborNoError) {
return -CTAP2_ERR_INVALID_CBOR;
}
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
return 0;
}

173
src/fido/cbor_large_blobs.c Normal file
View File

@@ -0,0 +1,173 @@
/*
* 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 "ctap2_cbor.h"
#include "fido.h"
#include "ctap.h"
#include "hid/ctap_hid.h"
#include "files.h"
#include "apdu.h"
#include "pico_keys.h"
#include "mbedtls/sha256.h"
static uint64_t expectedLength = 0, expectedNextOffset = 0;
uint8_t temp_lba[MAX_LARGE_BLOB_SIZE];
int cbor_large_blobs(const uint8_t *data, size_t len) {
CborParser parser;
CborValue map;
CborEncoder encoder, mapEncoder;
CborError error = CborNoError;
uint64_t get = 0, offset = UINT64_MAX, length = 0, pinUvAuthProtocol = 0;
CborByteString set = { 0 }, pinUvAuthParam = { 0 };
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
CBOR_PARSE_MAP_START(map, 1)
{
uint64_t val_u = 0;
CBOR_FIELD_GET_UINT(val_u, 1);
if (val_c <= 0 && val_c != val_u) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
if (val_u < val_c) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
val_c = val_u + 1;
if (val_u == 0x01) {
CBOR_FIELD_GET_UINT(get, 1);
}
else if (val_u == 0x02) {
CBOR_FIELD_GET_BYTES(set, 1);
}
else if (val_u == 0x03) {
CBOR_FIELD_GET_UINT(offset, 1);
}
else if (val_u == 0x04) {
CBOR_FIELD_GET_UINT(length, 1);
}
else if (val_u == 0x05) {
CBOR_FIELD_GET_BYTES(pinUvAuthParam, 1);
}
else if (val_u == 0x06) {
CBOR_FIELD_GET_UINT(pinUvAuthProtocol, 1);
}
}
CBOR_PARSE_MAP_END(map, 1);
if (offset == UINT64_MAX) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (get == 0 && set.present == false) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (get != 0 && set.present == true) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
if (get > 0) {
if (length != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (length > MAX_FRAGMENT_LENGTH) {
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
}
if (offset > file_get_size(ef_largeblob)) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
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_largeblob) + offset,
MIN(get, file_get_size(ef_largeblob) - offset)));
}
else {
if (set.len > MAX_FRAGMENT_LENGTH) {
CBOR_ERROR(CTAP1_ERR_INVALID_LEN);
}
if (offset == 0) {
if (length == 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (length > MAX_LARGE_BLOB_SIZE) {
CBOR_ERROR(CTAP2_ERR_LARGE_BLOB_STORAGE_FULL);
}
if (length < 17) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
expectedLength = length;
expectedNextOffset = 0;
}
else {
if (length != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
}
if (offset != expectedNextOffset) {
CBOR_ERROR(CTAP1_ERR_INVALID_SEQ);
}
if (pinUvAuthParam.present == false) {
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
}
if (pinUvAuthProtocol == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
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;
mbedtls_sha256(set.data, set.len, verify_data + 38, 0);
if (verify(pinUvAuthProtocol, paut.data, verify_data, sizeof(verify_data),
pinUvAuthParam.data) != 0) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (!(paut.permissions & CTAP_PERMISSION_LBW)) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (offset + set.len > expectedLength) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (offset == 0) {
memset(temp_lba, 0, sizeof(temp_lba));
}
memcpy(temp_lba + expectedNextOffset, set.data, set.len);
expectedNextOffset += set.len;
if (expectedNextOffset == expectedLength) {
uint8_t sha[32];
mbedtls_sha256(temp_lba, expectedLength - 16, sha, 0);
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);
low_flash_available();
}
goto err;
}
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
err:
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
CBOR_FREE_BYTE_STRING(set);
if (error != CborNoError) {
return -CTAP2_ERR_INVALID_CBOR;
}
res_APDU_size = cbor_encoder_get_buffer_size(&encoder, res_APDU + 1);
return 0;
}

View File

@@ -15,34 +15,34 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "ctap2_cbor.h"
#include "cbor_make_credential.h"
#include "ctap2_cbor.h"
#include "hid/ctap_hid.h"
#include "fido.h"
#include "ctap.h"
#include "files.h"
#include "random.h"
#include "hsm.h"
#include <math.h>
#include "apdu.h"
#include "credential.h"
#include "mbedtls/sha256.h"
#include "random.h"
#include "pico_keys.h"
int cbor_make_credential(const uint8_t *data, size_t len) {
CborParser parser;
CborValue map;
CborError error = CborNoError;
CborByteString clientDataHash = {0}, pinUvAuthParam = {0};
PublicKeyCredentialRpEntity rp = {0};
PublicKeyCredentialUserEntity user = {0};
PublicKeyCredentialParameters pubKeyCredParams[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
CborByteString clientDataHash = { 0 }, pinUvAuthParam = { 0 };
PublicKeyCredentialRpEntity rp = { 0 };
PublicKeyCredentialUserEntity user = { 0 };
PublicKeyCredentialParameters pubKeyCredParams[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
size_t pubKeyCredParams_len = 0;
PublicKeyCredentialDescriptor excludeList[MAX_CREDENTIAL_COUNT_IN_LIST] = {0};
PublicKeyCredentialDescriptor excludeList[MAX_CREDENTIAL_COUNT_IN_LIST] = { 0 };
size_t excludeList_len = 0;
CredOptions options = {0};
CredOptions options = { 0 };
uint64_t pinUvAuthProtocol = 0, enterpriseAttestation = 0;
uint8_t *aut_data = NULL;
size_t resp_size = 0;
CredExtensions extensions = {0};
CredExtensions extensions = { 0 };
//options.present = true;
//options.up = ptrue;
//options.uv = pfalse;
@@ -50,19 +50,23 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
CBOR_PARSE_MAP_START(map, 1) {
CBOR_PARSE_MAP_START(map, 1)
{
uint64_t val_u = 0;
CBOR_FIELD_GET_UINT(val_u, 1);
if (val_c <= 4 && val_c != val_u)
if (val_c <= 4 && val_c != val_u) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (val_u < val_c)
}
if (val_u < val_c) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
val_c = val_u + 1;
if (val_u == 0x01) { // clientDataHash
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);
@@ -70,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);
@@ -80,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);
@@ -93,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) {
PublicKeyCredentialDescriptor *pc = &excludeList[excludeList_len];
CBOR_PARSE_MAP_START(_f2, 3) {
CBOR_PARSE_ARRAY_START(_f1, 2)
{
PublicKeyCredentialDescriptor *pc = &excludeList[excludeList_len];
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++;
}
@@ -114,17 +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);
@@ -147,51 +163,84 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
uint8_t flags = FIDO2_AUT_FLAG_AT;
uint8_t rp_id_hash[32];
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 (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);
mbedtls_sha256((uint8_t *) rp.id.data, rp.id.len, rp_id_hash, 0);
if (pinUvAuthParam.present == true) {
if (pinUvAuthParam.len == 0 || pinUvAuthParam.data == NULL) {
if (check_user_presence() == false)
if (check_user_presence() == false) {
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
if (!file_has_data(ef_pin))
}
if (!file_has_data(ef_pin)) {
CBOR_ERROR(CTAP2_ERR_PIN_NOT_SET);
else
}
else {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
}
else {
if (pinUvAuthProtocol == 0)
if (pinUvAuthProtocol == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2)
}
if (pinUvAuthProtocol != 1 && pinUvAuthProtocol != 2) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
}
}
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_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_EDDSA) {
if (curve <= 0) {
curve = FIDO2_CURVE_ED25519;
}
}
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 = 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);
@@ -200,42 +249,72 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
}
//else if (options.up == NULL) //5.7
//rup = ptrue;
//rup = ptrue;
}
if (pinUvAuthParam.present == false && options.uv != ptrue && file_has_data(ef_pin)) { //8.1
CBOR_ERROR(CTAP2_ERR_PUAT_REQUIRED);
}
if (enterpriseAttestation > 0) {
if (!(get_opts() & FIDO2_OPT_EA)) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (enterpriseAttestation != 1 && enterpriseAttestation != 2) { //9.2.1
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
}
//Unfinished. See 6.1.2.9
}
if (pinUvAuthParam.present == true) { //11.1
int ret = verify(pinUvAuthProtocol, paut.data, clientDataHash.data, clientDataHash.len, pinUvAuthParam.data);
if (ret != CborNoError)
int ret = verify(pinUvAuthProtocol,
paut.data,
clientDataHash.data,
clientDataHash.len,
pinUvAuthParam.data);
if (ret != CborNoError) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
if (getUserVerifiedFlagValue() == false)
}
if (!(paut.permissions & CTAP_PERMISSION_MC)) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (paut.has_rp_id == true && memcmp(paut.rp_id_hash, rp_id_hash, 32) != 0) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
if (getUserVerifiedFlagValue() == false) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
flags |= FIDO2_AUT_FLAG_UV;
// Check pinUvAuthToken permissions. See 6.1.2.11
if (paut.has_rp_id == false) {
memcpy(paut.rp_id_hash, rp_id_hash, 32);
paut.has_rp_id = true;
}
}
for (int e = 0; e < excludeList_len; e++) { //12.1
if (excludeList[e].type.present == false || excludeList[e].id.present == false)
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, "public-key") != 0) {
continue;
}
Credential ecred;
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))
CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED);
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))) {
CBOR_ERROR(CTAP2_ERR_CREDENTIAL_EXCLUDED);
}
}
if (extensions.largeBlobKey == pfalse ||
(extensions.largeBlobKey == ptrue && options.rk != ptrue)) {
CBOR_ERROR(CTAP2_ERR_INVALID_OPTION);
}
if (options.up == ptrue || options.up == NULL) { //14.1
if (pinUvAuthParam.present == true) {
if (getUserPresentFlagValue() == false) {
if (check_user_presence() == false)
if (check_user_presence() == false) {
CBOR_ERROR(CTAP2_ERR_OPERATION_DENIED);
}
}
}
flags |= FIDO2_AUT_FLAG_UP;
@@ -249,29 +328,50 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
uint8_t cred_id[MAX_CRED_ID_LENGTH];
size_t cred_id_len = 0;
CBOR_CHECK(credential_create(&rp.id, &user.id, &user.parent.name, &user.displayName, &options, &extensions, (!ka || ka->use_sign_count == ptrue), alg, curve, cred_id, &cred_id_len));
CBOR_CHECK(credential_create(&rp.id, &user.id, &user.parent.name, &user.displayName, &options,
&extensions, (!ka || ka->use_sign_count == ptrue), alg, curve,
cred_id, &cred_id_len));
mbedtls_ecdsa_context ekey;
mbedtls_ecdsa_init(&ekey);
int ret = fido_load_key(curve, cred_id, &ekey);
if (ret != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP1_ERR_OTHER);
}
if (getUserVerifiedFlagValue())
if (getUserVerifiedFlagValue()) {
flags |= FIDO2_AUT_FLAG_UV;
}
size_t ext_len = 0;
uint8_t ext [512];
uint8_t ext[512];
CborEncoder encoder, mapEncoder, mapEncoder2;
if (extensions.present == true) {
cbor_encoder_init(&encoder, ext, sizeof(ext), 0);
int l = 0;
if (extensions.hmac_secret != NULL)
uint8_t minPinLen = 0;
if (extensions.hmac_secret != NULL) {
l++;
if (extensions.credProtect != 0)
}
if (extensions.credProtect != 0) {
l++;
}
if (extensions.minPinLength != NULL) {
file_t *ef_minpin = search_by_fid(EF_MINPINLEN, NULL, SPECIFY_EF);
if (file_has_data(ef_minpin)) {
uint8_t *minpin_data = file_get_data(ef_minpin);
for (int o = 2; o < file_get_size(ef_minpin); o += 32) {
if (memcmp(minpin_data + o, rp_id_hash, 32) == 0) {
minPinLen = minpin_data[0];
if (minPinLen > 0) {
l++;
}
break;
}
}
}
}
if (extensions.credBlob.present == true) {
l++;
}
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));
}
if (extensions.credProtect != 0) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "credProtect"));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, extensions.credProtect));
@@ -281,38 +381,37 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "hmac-secret"));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, *extensions.hmac_secret));
}
if (minPinLen > 0) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder, "minPinLength"));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, minPinLen));
}
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
ext_len = cbor_encoder_get_buffer_size(&encoder, ext);
flags |= FIDO2_AUT_FLAG_ED;
}
uint8_t pkey[66];
const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(ekey.grp.id);
if (cinfo == NULL)
mbedtls_ecp_keypair ekey;
mbedtls_ecp_keypair_init(&ekey);
int ret = fido_load_key(curve, cred_id, &ekey);
if (ret != 0) {
mbedtls_ecp_keypair_free(&ekey);
CBOR_ERROR(CTAP1_ERR_OTHER);
}
const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(ekey.grp.id);
if (cinfo == NULL) {
mbedtls_ecp_keypair_free(&ekey);
CBOR_ERROR(CTAP1_ERR_OTHER);
}
size_t olen = 0;
uint32_t ctr = get_sign_counter();
uint8_t cbor_buf[1024];
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;
aut_data = (uint8_t *)calloc(1, aut_data_len + clientDataHash.len);
aut_data = (uint8_t *) calloc(1, aut_data_len + clientDataHash.len);
uint8_t *pa = aut_data;
memcpy(pa, rp_id_hash, 32); pa += 32;
*pa++ = flags;
@@ -326,25 +425,78 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
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)
if (pa - aut_data != aut_data_len) {
mbedtls_ecp_keypair_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], sig[MBEDTLS_ECDSA_MAX_LEN];
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);
}
else if (ekey.grp.id == MBEDTLS_ECP_DP_ED25519) {
md = NULL;
}
if (md != NULL) {
ret = mbedtls_md(md,
aut_data,
aut_data_len + clientDataHash.len,
hash);
}
bool self_attestation = true;
if (ka && ka->use_self_attestation == pfalse) {
mbedtls_ecdsa_free(&ekey);
mbedtls_ecdsa_init(&ekey);
if (enterpriseAttestation == 2 || (ka && ka->use_self_attestation == pfalse)) {
mbedtls_ecp_keypair_free(&ekey);
mbedtls_ecp_keypair_init(&ekey);
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &ekey, file_get_data(ef_keydev), 32);
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);
mbedtls_ecdsa_free(&ekey);
if (md != NULL) {
ret = mbedtls_ecdsa_write_signature(&ekey,
mbedtls_md_get_type(md),
hash,
mbedtls_md_get_size(md),
sig,
sizeof(sig),
&olen,
random_gen,
NULL);
}
else {
ret = mbedtls_eddsa_write_signature(&ekey,
aut_data,
aut_data_len + clientDataHash.len,
sig,
sizeof(sig),
&olen,
MBEDTLS_EDDSA_PURE,
NULL,
0,
random_gen,
NULL);
}
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
mbedtls_ecp_keypair_free(&ekey);
uint8_t largeBlobKey[32];
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
ret = credential_derive_large_blob_key(cred_id, cred_id_len, largeBlobKey);
if (ret != 0) {
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
}
cbor_encoder_init(&encoder, ctap_resp->init.data + 1, CTAP_MAX_PACKET_SIZE, 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 3));
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"));
@@ -352,29 +504,51 @@ 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) {
ef_cert = search_by_fid(EF_EE_DEV_EA, NULL, SPECIFY_EF);
}
if (!file_has_data(ef_cert)) {
ef_cert = ef_certdev;
}
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_certdev), file_get_size(ef_certdev)));
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));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x04));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder, enterpriseAttestation == 2));
if (extensions.largeBlobKey == ptrue && options.rk == ptrue) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x05));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, largeBlobKey, sizeof(largeBlobKey)));
}
mbedtls_platform_zeroize(largeBlobKey, sizeof(largeBlobKey));
CBOR_CHECK(cbor_encoder_close_container(&encoder, &mapEncoder));
resp_size = cbor_encoder_get_buffer_size(&encoder, ctap_resp->init.data + 1);
if (options.rk == ptrue) {
if (credential_store(cred_id, cred_id_len, rp_id_hash) != 0)
if (credential_store(cred_id, cred_id_len, rp_id_hash) != 0) {
CBOR_ERROR(CTAP2_ERR_KEY_STORE_FULL);
}
}
err:
CBOR_FREE_BYTE_STRING(clientDataHash);
ctr++;
flash_write_data_to_file(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(rp.id);
CBOR_FREE_BYTE_STRING(rp.parent.name);
@@ -392,14 +566,15 @@ int cbor_make_credential(const uint8_t *data, size_t len) {
CBOR_FREE_BYTE_STRING(excludeList[m].transports[n]);
}
}
if (aut_data)
if (aut_data) {
free(aut_data);
}
if (error != CborNoError) {
if (error == CborErrorImproperValue)
if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
return error;
}
res_APDU_size = resp_size;
return 0;
}

View File

@@ -18,27 +18,18 @@
#ifndef _CBOR_MAKE_CREDENTIAL_H_
#define _CBOR_MAKE_CREDENTIAL_H_
#include "common.h"
#include "mbedtls/chachapoly.h"
#include <stdlib.h>
#include "pico/stdlib.h"
#include "ctap2_cbor.h"
#include "random.h"
#include "mbedtls/sha256.h"
typedef struct PublicKeyCredentialEntity
{
typedef struct PublicKeyCredentialEntity {
CborCharString name;
} PublicKeyCredentialEntity;
typedef struct PublicKeyCredentialRpEntity
{
typedef struct PublicKeyCredentialRpEntity {
PublicKeyCredentialEntity parent;
CborCharString id;
} PublicKeyCredentialRpEntity;
typedef struct PublicKeyCredentialUserEntity
{
typedef struct PublicKeyCredentialUserEntity {
PublicKeyCredentialEntity parent;
CborByteString id;
CborCharString displayName;

View File

@@ -1,4 +1,3 @@
/*
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
* Copyright (c) 2022 Pol Henarejos.
@@ -16,23 +15,27 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ctap2_cbor.h"
#include "file.h"
#include "fido.h"
#include "apdu.h"
#include "ctap.h"
#ifndef ENABLE_EMULATION
#include "bsp/board.h"
#endif
extern void scan_all();
int cbor_reset() {
#if defined(ENABLE_POWER_ON_RESET) && ENABLE_POWER_ON_RESET==1
if (board_millis() > 10000)
#ifndef ENABLE_EMULATION
#if defined(ENABLE_POWER_ON_RESET) && ENABLE_POWER_ON_RESET == 1
if (board_millis() > 10000) {
return CTAP2_ERR_NOT_ALLOWED;
}
#endif
if (wait_button_pressed() == true)
if (wait_button_pressed() == true) {
return CTAP2_ERR_USER_ACTION_TIMEOUT;
}
#endif
initialize_flash(true);
init_fido(true);
init_fido();
return 0;
}

View File

@@ -1,4 +1,3 @@
/*
* This file is part of the Pico FIDO distribution (https://github.com/polhenarejos/pico-fido).
* Copyright (c) 2022 Pol Henarejos.
@@ -16,13 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ctap2_cbor.h"
#include "fido.h"
#include "ctap.h"
#include "bsp/board.h"
int cbor_selection() {
if (wait_button_pressed() == true)
if (wait_button_pressed() == true) {
return CTAP2_ERR_USER_ACTION_TIMEOUT;
}
return CTAP2_OK;
}

View File

@@ -15,28 +15,36 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "ctap2_cbor.h"
#include "fido.h"
#include "ctap.h"
#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"
#include "mbedtls/hkdf.h"
#include "mbedtls/x509_csr.h"
extern uint8_t keydev_dec[32];
extern bool has_keydev_dec;
mse_t mse = {.init = false};
mse_t mse = { .init = false };
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;
}
@@ -45,7 +53,7 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
CborParser parser;
CborValue map;
CborError error = CborNoError;
CborByteString pinUvAuthParam = {0}, vendorParam = {0}, kax = {0}, kay = {0};
CborByteString pinUvAuthParam = { 0 }, vendorParam = { 0 }, kax = { 0 }, kay = { 0 };
size_t resp_size = 0;
uint64_t vendorCmd = 0, pinUvAuthProtocol = 0;
int64_t kty = 0, alg = 0, crv = 0;
@@ -53,50 +61,34 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
CBOR_CHECK(cbor_parser_init(data, len, 0, &parser, &map));
uint64_t val_c = 1;
CBOR_PARSE_MAP_START(map, 1) {
CBOR_PARSE_MAP_START(map, 1)
{
uint64_t val_u = 0;
CBOR_FIELD_GET_UINT(val_u, 1);
if (val_c <= 1 && val_c != val_u)
if (val_c <= 1 && val_c != val_u) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (val_u < val_c)
}
if (val_u < val_c) {
CBOR_ERROR(CTAP2_ERR_INVALID_CBOR);
}
val_c = val_u + 1;
if (val_u == 0x01) {
CBOR_FIELD_GET_UINT(vendorCmd, 1);
}
else if (val_u == 0x02) {
uint64_t subpara = 0;
CBOR_PARSE_MAP_START(_f1, 2) {
CBOR_PARSE_MAP_START(_f1, 2)
{
CBOR_FIELD_GET_UINT(subpara, 2);
if (subpara == 0x01) {
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
else {
CBOR_ADVANCE(2);
}
}
CBOR_PARSE_MAP_END(_f1, 2);
}
@@ -113,17 +105,20 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
if (cmd == CTAP_VENDOR_BACKUP) {
if (vendorCmd == 0x01) {
if (has_keydev_dec == false)
if (has_keydev_dec == false) {
CBOR_ERROR(CTAP2_ERR_PIN_AUTH_INVALID);
}
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)
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
if (vendorParam.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
uint8_t zeros[32];
memset(zeros, 0, sizeof(zeros));
flash_write_data_to_file(ef_keydev_enc, vendorParam.data, vendorParam.len);
@@ -138,13 +133,18 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
}
else if (cmd == CTAP_VENDOR_MSE) {
if (vendorCmd == 0x01) { // KeyAgreement
if (kax.present == false || kay.present == false || alg == 0)
if (kax.present == false || kay.present == false || alg == 0) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
mbedtls_ecdh_context hkey;
mbedtls_ecdh_init(&hkey);
mbedtls_ecdh_setup(&hkey, MBEDTLS_ECP_DP_SECP256R1);
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp, &hkey.ctx.mbed_ecdh.d, &hkey.ctx.mbed_ecdh.Q, random_gen, NULL);
int ret = mbedtls_ecdh_gen_public(&hkey.ctx.mbed_ecdh.grp,
&hkey.ctx.mbed_ecdh.d,
&hkey.ctx.mbed_ecdh.Q,
random_gen,
NULL);
mbedtls_mpi_lset(&hkey.ctx.mbed_ecdh.Qp.Z, 1);
if (ret != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
@@ -160,19 +160,37 @@ 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);
@@ -182,28 +200,14 @@ 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);
}
}
else if (cmd == CTAP_VENDOR_UNLOCK) {
if (mse.init == false)
if (mse.init == false) {
CBOR_ERROR(CTAP2_ERR_NOT_ALLOWED);
}
mbedtls_chachapoly_context chatx;
int ret = mse_decrypt_ct(vendorParam.data, vendorParam.len);
@@ -211,33 +215,117 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
if (!file_has_data(ef_keydev_enc))
if (!file_has_data(ef_keydev_enc)) {
CBOR_ERROR(CTAP2_ERR_INTEGRITY_FAILURE);
}
uint8_t *keyenc = file_get_data(ef_keydev_enc);
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){
if (ret != 0) {
CBOR_ERROR(CTAP1_ERR_INVALID_PARAMETER);
}
has_keydev_dec = true;
goto err;
}
else
else if (cmd == CTAP_VENDOR_EA) {
if (vendorCmd == 0x01) {
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));
if (ret != 0) {
mbedtls_ecdsa_free(&ekey);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
ret = mbedtls_ecp_mul(&ekey.grp, &ekey.Q, &ekey.d, &ekey.grp.G, random_gen, NULL);
if (ret != 0) {
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]);
mbedtls_x509write_csr_set_subject_name(&ctx, (char *) buffer);
mbedtls_pk_context key;
mbedtls_pk_init(&key);
mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
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));
ret = mbedtls_x509write_csr_der(&ctx, buffer, sizeof(buffer), random_gen, NULL);
mbedtls_ecdsa_free(&ekey);
if (ret <= 0) {
mbedtls_x509write_csr_free(&ctx);
CBOR_ERROR(CTAP2_ERR_PROCESSING);
}
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x01));
CBOR_CHECK(cbor_encode_byte_string(&mapEncoder, buffer + sizeof(buffer) - ret, ret));
}
else if (vendorCmd == 0x02) {
if (vendorParam.present == false) {
CBOR_ERROR(CTAP2_ERR_MISSING_PARAMETER);
}
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);
}
low_flash_available();
goto err;
}
}
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);
err:
err:
CBOR_FREE_BYTE_STRING(pinUvAuthParam);
CBOR_FREE_BYTE_STRING(vendorParam);
if (error != CborNoError) {
if (error == CborErrorImproperValue)
if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
return error;
}
res_APDU_size = resp_size;
@@ -245,9 +333,11 @@ int cbor_vendor_generic(uint8_t cmd, const uint8_t *data, size_t len) {
}
int cbor_vendor(const uint8_t *data, size_t len) {
if (len == 0)
if (len == 0) {
return CTAP1_ERR_INVALID_LEN;
if (data[0] >= CTAP_VENDOR_BACKUP)
}
if (data[0] >= CTAP_VENDOR_BACKUP) {
return cbor_vendor_generic(data[0], data + 1, len - 1);
}
return CTAP2_ERR_INVALID_CBOR;
}

View File

@@ -16,30 +16,32 @@
*/
#include "fido.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
#include "ctap.h"
#include "mbedtls/ecdsa.h"
#include "random.h"
#include "files.h"
#include "credential.h"
int cmd_authenticate() {
CTAP_AUTHENTICATE_REQ *req = (CTAP_AUTHENTICATE_REQ *)apdu.data;
CTAP_AUTHENTICATE_RESP *resp = (CTAP_AUTHENTICATE_RESP *)res_APDU;
CTAP_AUTHENTICATE_REQ *req = (CTAP_AUTHENTICATE_REQ *) apdu.data;
CTAP_AUTHENTICATE_RESP *resp = (CTAP_AUTHENTICATE_RESP *) res_APDU;
//if (scan_files(true) != CCID_OK)
// return SW_EXEC_ERROR();
if (apdu.nc < CTAP_CHAL_SIZE+CTAP_APPID_SIZE+1+1)
if (apdu.nc < CTAP_CHAL_SIZE + CTAP_APPID_SIZE + 1 + 1) {
return SW_WRONG_DATA();
if (req->keyHandleLen < KEY_HANDLE_LEN)
}
if (req->keyHandleLen < KEY_HANDLE_LEN) {
return SW_INCORRECT_PARAMS();
if (P1(apdu) == CTAP_AUTH_ENFORCE && wait_button_pressed() == true)
}
if (P1(apdu) == CTAP_AUTH_ENFORCE && wait_button_pressed() == true) {
return SW_CONDITIONS_NOT_SATISFIED();
}
mbedtls_ecdsa_context key;
mbedtls_ecdsa_init(&key);
int ret = 0;
uint8_t *tmp_kh = (uint8_t *)calloc(1, req->keyHandleLen);
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) {
ret = fido_load_key(FIDO2_CURVE_P256, req->keyHandle, &key);
@@ -69,23 +71,33 @@ int cmd_authenticate() {
resp->ctr[3] = ctr & 0xff;
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, &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)
if (ret != 0) {
return SW_EXEC_ERROR();
}
res_APDU_size = 1 + 4 + olen;
ctr++;
flash_write_data_to_file(ef_counter, (uint8_t *)&ctr, sizeof(ctr));
flash_write_data_to_file(ef_counter, (uint8_t *) &ctr, sizeof(ctr));
low_flash_available();
return SW_OK();
}

View File

@@ -16,30 +16,65 @@
*/
#include "fido.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
#include "ctap.h"
#include "mbedtls/ecdsa.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 *bogus_chrome = (const uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
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) {
if (cap_supported(CAP_U2F)) {
a->process_apdu = u2f_process_apdu;
a->unload = u2f_unload;
return CCID_OK;
}
return CCID_ERR_FILE_NOT_FOUND;
}
void __attribute__((constructor)) u2f_ctor() {
register_app(u2f_select, u2f_aid);
}
int u2f_unload() {
return CCID_OK;
}
const uint8_t *bogus_firefox =
(const uint8_t *)
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
const uint8_t *bogus_chrome = (const uint8_t *) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
extern int ctap_error(uint8_t error);
int cmd_register() {
CTAP_REGISTER_REQ *req = (CTAP_REGISTER_REQ *)apdu.data;
CTAP_REGISTER_RESP *resp = (CTAP_REGISTER_RESP *)res_APDU;
CTAP_REGISTER_REQ *req = (CTAP_REGISTER_REQ *) apdu.data;
CTAP_REGISTER_RESP *resp = (CTAP_REGISTER_RESP *) res_APDU;
resp->registerId = CTAP_REGISTER_ID;
resp->keyHandleLen = KEY_HANDLE_LEN;
//if (scan_files(true) != CCID_OK)
// return SW_EXEC_ERROR();
if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE)
if (apdu.nc != CTAP_APPID_SIZE + CTAP_CHAL_SIZE) {
return SW_WRONG_LENGTH();
if (wait_button_pressed() == true)
}
if (wait_button_pressed() == true) {
return SW_CONDITIONS_NOT_SATISFIED();
if (memcmp(req->appId, bogus_firefox, CTAP_APPID_SIZE) == 0 || memcmp(req->appId, bogus_chrome, CTAP_APPID_SIZE) == 0)
return ctap_error(CTAP1_ERR_CHANNEL_BUSY);
}
if (memcmp(req->appId, bogus_firefox,
CTAP_APPID_SIZE) == 0 || memcmp(req->appId, bogus_chrome, CTAP_APPID_SIZE) == 0)
#ifndef ENABLE_EMULATION
{ return ctap_error(CTAP1_ERR_CHANNEL_BUSY); }
#else
{ return SW_DATA_INVALID(); }
#endif
mbedtls_ecdsa_context key;
mbedtls_ecdsa_init(&key);
int ret = derive_key(req->appId, true, resp->keyHandleCertSig, MBEDTLS_ECP_DP_SECP256R1, &key);
@@ -48,32 +83,82 @@ int cmd_register() {
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);
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);
if (ret != 0)
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) {
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)
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 + 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

@@ -15,9 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "apdu.h"
#include "hsm.h"
#include "pico_keys.h"
int cmd_version() {
memcpy(res_APDU, "U2F_V2", strlen("U2F_V2"));

View File

@@ -15,40 +15,60 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "mbedtls/chachapoly.h"
#include "mbedtls/sha256.h"
#include "credential.h"
#ifndef ENABLE_EMULATION
#include "bsp/board.h"
#endif
#include "hid/ctap_hid.h"
#include "fido.h"
#include "ctap.h"
#include "random.h"
#include "files.h"
#include "file.h"
#include "hsm.h"
#include "pico_keys.h"
int credential_derive_chacha_key(uint8_t *outk);
int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
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;
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);
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);
return ret;
}
int credential_create(CborCharString *rpId, CborByteString *userId, CborCharString *userName, CborCharString *userDisplayName, CredOptions *opts, CredExtensions *extensions, bool use_sign_count, int alg, int curve, uint8_t *cred_id, size_t *cred_id_len) {
int credential_create(CborCharString *rpId,
CborByteString *userId,
CborCharString *userName,
CborCharString *userDisplayName,
CredOptions *opts,
CredExtensions *extensions,
bool use_sign_count,
int alg,
int curve,
uint8_t *cred_id,
size_t *cred_id_len) {
CborEncoder encoder, mapEncoder, mapEncoder2;
CborError error = CborNoError;
uint8_t rp_id_hash[32];
mbedtls_sha256((uint8_t *)rpId->data, rpId->len, rp_id_hash, 0);
cbor_encoder_init(&encoder, cred_id+4+12, MAX_CRED_ID_LENGTH-(4+12+16), 0);
mbedtls_sha256((uint8_t *) rpId->data, rpId->len, rp_id_hash, 0);
cbor_encoder_init(&encoder, cred_id + 4 + 12, MAX_CRED_ID_LENGTH - (4 + 12 + 16), 0);
CBOR_CHECK(cbor_encoder_create_map(&encoder, &mapEncoder, CborIndefiniteLength));
CBOR_APPEND_KEY_UINT_VAL_STRING(mapEncoder, 0x01, *rpId);
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x02));
@@ -60,6 +80,12 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
if (extensions->present == true) {
CBOR_CHECK(cbor_encode_uint(&mapEncoder, 0x07));
CBOR_CHECK(cbor_encoder_create_map(&mapEncoder, &mapEncoder2, CborIndefiniteLength));
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));
}
if (extensions->credProtect != 0) {
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "credProtect"));
CBOR_CHECK(cbor_encode_uint(&mapEncoder2, extensions->credProtect));
@@ -68,6 +94,14 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
CBOR_CHECK(cbor_encode_text_stringz(&mapEncoder2, "hmac-secret"));
CBOR_CHECK(cbor_encode_boolean(&mapEncoder2, *extensions->hmac_secret));
}
if (extensions->largeBlobKey == ptrue) {
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));
@@ -96,7 +130,14 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
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 + 4 + 12,
cred_id + 4 + 12,
cred_id + 4 + 12 + rs);
mbedtls_chachapoly_free(&chatx);
if (ret != 0) {
CBOR_ERROR(CTAP1_ERR_OTHER);
@@ -104,24 +145,29 @@ int credential_create(CborCharString *rpId, CborByteString *userId, CborCharStri
memcpy(cred_id, CRED_PROTO, 4);
memcpy(cred_id + 4, iv, 12);
err:
err:
if (error != CborNoError) {
if (error == CborErrorImproperValue)
if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
return error;
}
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;
uint8_t *copy_cred_id = (uint8_t *)calloc(1, cred_id_len);
CborError error = CborNoError;
uint8_t *copy_cred_id = (uint8_t *) calloc(1, cred_id_len);
memcpy(copy_cred_id, cred_id, cred_id_len);
ret = credential_verify(copy_cred_id, cred_id_len, rp_id_hash);
if (ret != 0) { // U2F?
if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0)
if (cred_id_len != KEY_HANDLE_LEN || verify_key(rp_id_hash, cred_id, NULL) != 0) {
CBOR_ERROR(CTAP2_ERR_INVALID_CREDENTIAL);
}
}
else {
CborParser parser;
@@ -129,8 +175,10 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
memset(cred, 0, sizeof(Credential));
cred->curve = FIDO2_CURVE_P256;
cred->alg = FIDO2_ALG_ES256;
CBOR_CHECK(cbor_parser_init(copy_cred_id + 4 + 12, cred_id_len - (4 + 12 + 16), 0, &parser, &map));
CBOR_PARSE_MAP_START(map, 1) {
CBOR_CHECK(cbor_parser_init(copy_cred_id + 4 + 12, cred_id_len - (4 + 12 + 16), 0, &parser,
&map));
CBOR_PARSE_MAP_START(map, 1)
{
uint64_t val_u = 0;
CBOR_FIELD_GET_UINT(val_u, 1);
if (val_u == 0x01) {
@@ -155,6 +203,11 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
CBOR_FIELD_GET_KEY_TEXT(2);
CBOR_FIELD_KEY_TEXT_VAL_BOOL(2, "hmac-secret", cred->extensions.hmac_secret);
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);
@@ -184,15 +237,16 @@ int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *r
}
}
cred->id.present = true;
cred->id.data = (uint8_t *)calloc(1, cred_id_len);
cred->id.data = (uint8_t *) calloc(1, cred_id_len);
memcpy(cred->id.data, cred_id, cred_id_len);
cred->id.len = cred_id_len;
cred->present = true;
err:
err:
free(copy_cred_id);
if (error != CborNoError) {
if (error == CborErrorImproperValue)
if (error == CborErrorImproperValue) {
return CTAP2_ERR_CBOR_UNEXPECTED_TYPE;
}
return error;
}
return 0;
@@ -211,7 +265,7 @@ void credential_free(Credential *cred) {
int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash) {
int sloti = -1;
Credential cred = {0};
Credential cred = { 0 };
int ret = 0;
bool new_record = true;
ret = credential_load(cred_id, cred_id_len, rp_id_hash, &cred);
@@ -221,20 +275,23 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
}
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
file_t *ef = search_dynamic_file(EF_CRED + i);
Credential rcred = {0};
Credential rcred = { 0 };
if (!file_has_data(ef)) {
if (sloti == -1)
if (sloti == -1) {
sloti = i;
}
continue;
}
if (memcmp(file_get_data(ef), rp_id_hash, 32) != 0)
if (memcmp(file_get_data(ef), rp_id_hash, 32) != 0) {
continue;
ret = credential_load(file_get_data(ef) + 32, file_get_size(ef)-32, rp_id_hash, &rcred);
}
ret = credential_load(file_get_data(ef) + 32, file_get_size(ef) - 32, rp_id_hash, &rcred);
if (ret != 0) {
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;
@@ -242,12 +299,13 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
}
credential_free(&rcred);
}
if (sloti == -1)
if (sloti == -1) {
return -1;
uint8_t *data = (uint8_t *)calloc(1, cred_id_len+32);
}
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);
file_t *ef = file_new(EF_CRED + sloti);
flash_write_data_to_file(ef, data, cred_id_len + 32);
free(data);
@@ -256,30 +314,32 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
for (int i = 0; i < MAX_RESIDENT_CREDENTIALS; i++) {
ef = search_dynamic_file(EF_RP + i);
if (!file_has_data(ef)) {
if (sloti == -1)
if (sloti == -1) {
sloti = i;
}
continue;
}
if (memcmp(file_get_data(ef)+1, rp_id_hash, 32) == 0) {
if (memcmp(file_get_data(ef) + 1, rp_id_hash, 32) == 0) {
sloti = i;
break;
}
}
if (sloti == -1)
if (sloti == -1) {
return -1;
}
ef = search_dynamic_file(EF_RP + sloti);
if (file_has_data(ef)) {
data = (uint8_t *)calloc(1, file_get_size(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));
free(data);
}
else {
ef = file_new(EF_RP+sloti);
data = (uint8_t *)calloc(1, 1 + 32 + cred.rpId.len);
ef = file_new(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, 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);
free(data);
@@ -293,13 +353,14 @@ int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *
int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
memset(outk, 0, 64);
int r = 0;
if ((r = load_keydev(outk)) != 0)
if ((r = load_keydev(outk)) != 0) {
return r;
}
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 *)"hmac-secret", 11, outk);
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 *) "hmac-secret", 11, outk);
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
return 0;
}
@@ -307,12 +368,28 @@ int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8
int credential_derive_chacha_key(uint8_t *outk) {
memset(outk, 0, 32);
int r = 0;
if ((r = load_keydev(outk)) != 0)
if ((r = load_keydev(outk)) != 0) {
return r;
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
}
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 *)"Encryption key", 14, outk);
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 *) "Encryption key", 14, outk);
return 0;
}
int credential_derive_large_blob_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk) {
memset(outk, 0, 32);
int r = 0;
if ((r = load_keydev(outk)) != 0) {
return r;
}
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 *) "largeBlobKey", 12, outk);
mbedtls_md_hmac(md_info, outk, 32, cred_id, cred_id_len, outk);
return 0;
}

View File

@@ -30,11 +30,14 @@ typedef struct CredOptions {
typedef struct CredExtensions {
const bool *hmac_secret;
uint64_t credProtect;
const bool *minPinLength;
CborByteString credBlob;
const bool *largeBlobKey;
const bool *thirdPartyPayment;
bool present;
} CredExtensions;
typedef struct Credential
{
typedef struct Credential {
CborCharString rpId;
CborByteString userId;
CborCharString userName;
@@ -56,10 +59,26 @@ typedef struct Credential
#define CRED_PROTO "\xf1\xd0\x02\x01"
extern int credential_verify(uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash);
extern int credential_create(CborCharString *rpId, CborByteString *userId, CborCharString *userName, CborCharString *userDisplayName, CredOptions *opts, CredExtensions *extensions, bool use_sign_count, int alg, int curve, uint8_t *cred_id, size_t *cred_id_len);
extern int credential_create(CborCharString *rpId,
CborByteString *userId,
CborCharString *userName,
CborCharString *userDisplayName,
CredOptions *opts,
CredExtensions *extensions,
bool use_sign_count,
int alg,
int curve,
uint8_t *cred_id,
size_t *cred_id_len);
extern void credential_free(Credential *cred);
extern int credential_store(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash);
extern int credential_load(const uint8_t *cred_id, size_t cred_id_len, const uint8_t *rp_id_hash, Credential *cred);
extern int credential_load(const uint8_t *cred_id,
size_t cred_id_len,
const uint8_t *rp_id_hash,
Credential *cred);
extern int credential_derive_hmac_key(const uint8_t *cred_id, size_t cred_id_len, uint8_t *outk);
extern int credential_derive_large_blob_key(const uint8_t *cred_id,
size_t cred_id_len,
uint8_t *outk);
#endif // _CREDENTIAL_H_

View File

@@ -24,7 +24,9 @@ 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
@@ -114,6 +116,7 @@ typedef struct {
#define CTAP_GET_NEXT_ASSERTION 0x08
#define CTAP_CREDENTIAL_MGMT 0x0A
#define CTAP_SELECTION 0x0B
#define CTAP_LARGE_BLOBS 0x0C
#define CTAP_CONFIG 0x0D
#define CTAP_CONFIG_AUT_ENABLE 0x03e43f56b34285e2
@@ -124,6 +127,14 @@ typedef struct {
#define CTAP_VENDOR_BACKUP 0x01
#define CTAP_VENDOR_MSE 0x02
#define CTAP_VENDOR_UNLOCK 0x03
#define CTAP_VENDOR_EA 0x04
#define CTAP_PERMISSION_MC 0x01 // MakeCredential
#define CTAP_PERMISSION_GA 0x02 // GetAssertion
#define CTAP_PERMISSION_CM 0x04 // CredentialManagement
#define CTAP_PERMISSION_BE 0x08 // BioEnrollment
#define CTAP_PERMISSION_LBW 0x10 // LargeBlobWrite
#define CTAP_PERMISSION_ACFG 0x20 // AuthenticatorConfiguration
typedef struct mse {
uint8_t Qpt[65];

View File

@@ -18,10 +18,10 @@
#ifndef _CTAP2_CBOR_H_
#define _CTAP2_CBOR_H_
#include <stdlib.h>
#include "pico/stdlib.h"
#include <stdio.h>
#include "cbor.h"
#include "common.h"
#include "mbedtls/ecp.h"
#include "mbedtls/ecdh.h"
extern uint8_t *driver_prepare_response();
extern void driver_exec_finished(size_t size_next);
@@ -49,17 +49,17 @@ extern const bool _btrue, _bfalse;
if (x) \
{ \
free(x); \
x = NULL;\
x = NULL; \
} \
} while(0)
} while (0)
#define CBOR_ERROR(e) \
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)
} while (0)
#define CBOR_ASSERT(c) \
do \
@@ -70,7 +70,7 @@ extern const bool _btrue, _bfalse;
printf("Cbor ASSERT [%s:%d]: %s\n", __FILE__, __LINE__, #c); \
goto err; \
} \
} while(0)
} while (0)
#define PINUVAUTHTOKEN_MC 0x1
#define PINUVAUTHTOKEN_GA 0x2
@@ -97,20 +97,20 @@ typedef struct CborCharString {
do \
{ \
if ((v).nofree != true) \
CBOR_FREE((v).data); \
CBOR_FREE((v).data); \
else \
(v).data = NULL; \
(v).data = NULL; \
(v).len = 0; \
(v).present = false; \
} while(0)
} while (0)
#define CBOR_PARSE_MAP_START(_p,_n) \
#define CBOR_PARSE_MAP_START(_p, _n) \
CBOR_ASSERT(cbor_value_is_map(&(_p)) == true); \
CborValue _f##_n; \
CBOR_CHECK(cbor_value_enter_container(&(_p), &(_f##_n))); \
while (cbor_value_at_end(&(_f##_n)) == false)
#define CBOR_PARSE_ARRAY_START(_p,_n) \
#define CBOR_PARSE_ARRAY_START(_p, _n) \
CBOR_ASSERT(cbor_value_is_array(&(_p)) == true); \
CborValue _f##_n; \
CBOR_CHECK(cbor_value_enter_container(&(_p), &(_f##_n))); \
@@ -121,14 +121,14 @@ typedef struct CborCharString {
CBOR_ASSERT(cbor_value_is_unsigned_integer(&(_f##_n)) == true); \
CBOR_CHECK(cbor_value_get_uint64(&(_f##_n), &(v))); \
CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \
} while(0)
} while (0)
#define CBOR_FIELD_GET_INT(v, _n) \
do { \
CBOR_ASSERT(cbor_value_is_integer(&(_f##_n)) == true); \
CBOR_CHECK(cbor_value_get_int64(&(_f##_n), &(v))); \
CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \
} while(0)
} while (0)
#define CBOR_FIELD_GET_BYTES(v, _n) \
do { \
@@ -151,7 +151,7 @@ typedef struct CborCharString {
CBOR_CHECK(cbor_value_get_boolean(&(_f##_n), &val)); \
v = (val == true ? ptrue : pfalse); \
CBOR_CHECK(cbor_value_advance_fixed(&(_f##_n))); \
} while(0)
} while (0)
#define CBOR_FIELD_GET_KEY_TEXT(_n) \
CBOR_ASSERT(cbor_value_is_text_string(&(_f##_n)) == true); \
@@ -177,26 +177,26 @@ typedef struct CborCharString {
#define CBOR_FIELD_KEY_TEXT_VAL_INT(_n, _t, _v) \
if (strcmp(_fd##_n, _t) == 0) { \
CBOR_FIELD_GET_INT(_v, _n);\
CBOR_FIELD_GET_INT(_v, _n); \
continue; \
}
#define CBOR_FIELD_KEY_TEXT_VAL_UINT(_n, _t, _v) \
if (strcmp(_fd##_n, _t) == 0) { \
CBOR_FIELD_GET_UINT(_v, _n);\
CBOR_FIELD_GET_UINT(_v, _n); \
continue; \
}
#define CBOR_FIELD_KEY_TEXT_VAL_BOOL(_n, _t, _v) \
if (strcmp(_fd##_n, _t) == 0) { \
CBOR_FIELD_GET_BOOL(_v, _n);\
CBOR_FIELD_GET_BOOL(_v, _n); \
continue; \
}
#define CBOR_PARSE_MAP_END(_p,_n) \
#define CBOR_PARSE_MAP_END(_p, _n) \
CBOR_CHECK(cbor_value_leave_container(&(_p), &(_f##_n)))
#define CBOR_PARSE_ARRAY_END(_p,_n) CBOR_PARSE_MAP_END(_p, _n)
#define CBOR_PARSE_ARRAY_END(_p, _n) CBOR_PARSE_MAP_END(_p, _n)
#define CBOR_ADVANCE(_n) CBOR_CHECK(cbor_value_advance(&_f##_n));
@@ -205,39 +205,51 @@ typedef struct CborCharString {
if ((v).data && (v).len > 0) { \
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
CBOR_CHECK(cbor_encode_byte_string(&(p), (v).data, (v).len)); \
} } while(0)
} } while (0)
#define CBOR_APPEND_KEY_UINT_VAL_STRING(p, k, v) \
do { \
if ((v).data && (v).len > 0) { \
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
CBOR_CHECK(cbor_encode_text_stringz(&(p), (v).data)); \
} } while(0)
} } while (0)
#define CBOR_APPEND_KEY_UINT_VAL_UINT(p, k, v) \
do { \
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
CBOR_CHECK(cbor_encode_uint(&(p), (v))); \
} while(0)
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
CBOR_CHECK(cbor_encode_uint(&(p), (v))); \
} while (0)
#define CBOR_APPEND_KEY_UINT_VAL_INT(p, k, v) \
do { \
CBOR_CHECK(cbor_encode_int(&(p), (k))); \
CBOR_CHECK(cbor_encode_int(&(p), (v))); \
} while(0)
CBOR_CHECK(cbor_encode_int(&(p), (k))); \
CBOR_CHECK(cbor_encode_int(&(p), (v))); \
} while (0)
#define CBOR_APPEND_KEY_UINT_VAL_BOOL(p, k, v) \
do { \
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
CBOR_CHECK(cbor_encode_boolean(&(p), (v))); \
} while(0)
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
CBOR_CHECK(cbor_encode_boolean(&(p), (v))); \
} while (0)
#define CBOR_APPEND_KEY_UINT_VAL_PBOOL(p, k, v) \
do { \
if (v != NULL) {\
if (v != NULL) { \
CBOR_CHECK(cbor_encode_uint(&(p), (k))); \
CBOR_CHECK(cbor_encode_boolean(&(p), v == ptrue ? true : false)); \
} } while(0)
} } 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

@@ -15,50 +15,83 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "fido.h"
#include "hsm.h"
#include "pico_keys.h"
#include "apdu.h"
#include "ctap.h"
#include "files.h"
#include "file.h"
#include "usb.h"
#include "random.h"
#include "bsp/board.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/hkdf.h"
#include "pk_wrap.h"
#include "crypto_utils.h"
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
#include "ccid/ccid.h"
#endif
#ifndef ENABLE_EMULATION
#include "bsp/board.h"
#endif
#include <math.h>
#include <stdio.h>
#include "management.h"
#include "ctap_hid.h"
#include "version.h"
void init_fido(bool);
int fido_process_apdu();
int fido_unload();
pinUvAuthToken_t paut = {0};
pinUvAuthToken_t paut = { 0 };
uint8_t keydev_dec[32];
bool has_keydev_dec = false;
const uint8_t fido_aid[] = {
const uint8_t _fido_aid[] = {
8,
0xA0, 0x00, 0x00, 0x06, 0x47, 0x2F, 0x00, 0x01
};
app_t *fido_select(app_t *a) {
a->aid = fido_aid;
a->process_apdu = fido_process_apdu;
a->unload = fido_unload;
current_app = a;
//init_fido(false);
return a;
const uint8_t atr_fido[] = {
23,
0x3b, 0xfd, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe, 0x15, 0x80, 0x73, 0xc0, 0x21, 0xc0, 0x57, 0x59,
0x75, 0x62, 0x69, 0x4b, 0x65, 0x79, 0x40
};
uint8_t fido_get_version_major() {
return PICO_FIDO_VERSION_MAJOR;
}
uint8_t fido_get_version_minor() {
return PICO_FIDO_VERSION_MINOR;
}
void __attribute__ ((constructor)) fido_ctor() {
register_app(fido_select);
//fido_select(&apps[0]);
int fido_select(app_t *a) {
if (cap_supported(CAP_FIDO2)) {
a->process_apdu = fido_process_apdu;
a->unload = fido_unload;
return CCID_OK;
}
return CCID_ERR_FILE_NOT_FOUND;
}
extern uint8_t (*get_version_major)();
extern uint8_t (*get_version_minor)();
extern const uint8_t *fido_aid;
extern void (*init_fido_cb)();
extern void (*cbor_thread_func)();
extern int (*cbor_process_cb)(uint8_t, const uint8_t *, size_t);
extern void cbor_thread();
extern int cbor_process(uint8_t last_cmd, const uint8_t *data, size_t len);
void __attribute__((constructor)) fido_ctor() {
#if defined(USB_ITF_CCID) || defined(ENABLE_EMULATION)
ccid_atr = atr_fido;
#endif
get_version_major = fido_get_version_major;
get_version_minor = fido_get_version_minor;
fido_aid = _fido_aid;
init_fido_cb = init_fido;
#ifndef ENABLE_EMULATION
cbor_thread_func = cbor_thread;
#endif
cbor_process_cb = cbor_process;
register_app(fido_select, fido_aid);
}
int fido_unload() {
@@ -66,44 +99,84 @@ int fido_unload() {
}
mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve) {
if (curve == FIDO2_CURVE_P256)
if (curve == FIDO2_CURVE_P256) {
return MBEDTLS_ECP_DP_SECP256R1;
else if (curve == FIDO2_CURVE_P384)
}
else if (curve == FIDO2_CURVE_P384) {
return MBEDTLS_ECP_DP_SECP384R1;
else if (curve == FIDO2_CURVE_P521)
}
else if (curve == FIDO2_CURVE_P521) {
return MBEDTLS_ECP_DP_SECP521R1;
else if (curve == FIDO2_CURVE_P256K1)
}
else if (curve == FIDO2_CURVE_P256K1) {
return MBEDTLS_ECP_DP_SECP256K1;
else if (curve == FIDO2_CURVE_X25519)
}
else if (curve == FIDO2_CURVE_X25519) {
return MBEDTLS_ECP_DP_CURVE25519;
else if (curve == FIDO2_CURVE_X448)
}
else if (curve == FIDO2_CURVE_X448) {
return MBEDTLS_ECP_DP_CURVE448;
}
else if (curve == FIDO2_CURVE_ED25519) {
return MBEDTLS_ECP_DP_ED25519;
}
else if (curve == FIDO2_CURVE_ED448) {
return MBEDTLS_ECP_DP_ED448;
}
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;
}
else if (id == MBEDTLS_ECP_DP_ED25519) {
return FIDO2_CURVE_ED25519;
}
else if (id == MBEDTLS_ECP_DP_ED448) {
return FIDO2_CURVE_ED448;
}
return 0;
}
int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key) {
int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecp_keypair *key) {
mbedtls_ecp_group_id mbedtls_curve = fido_curve_to_mbedtls(curve);
if (mbedtls_curve == MBEDTLS_ECP_DP_NONE)
if (mbedtls_curve == MBEDTLS_ECP_DP_NONE) {
return CTAP2_ERR_UNSUPPORTED_ALGORITHM;
}
uint8_t key_path[KEY_PATH_LEN];
memcpy(key_path, cred_id, KEY_PATH_LEN);
*(uint32_t *)key_path = 0x80000000 | 10022;
for (int i = 1; i < KEY_PATH_ENTRIES; i++)
*(uint32_t *)(key_path+i*sizeof(uint32_t)) |= 0x80000000;
*(uint32_t *) key_path = 0x80000000 | 10022;
for (int i = 1; i < KEY_PATH_ENTRIES; i++) {
*(uint32_t *) (key_path + i * sizeof(uint32_t)) |= 0x80000000;
}
return derive_key(NULL, false, key_path, mbedtls_curve, key);
}
int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffer_size, bool core1) {
int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffer_size) {
mbedtls_x509write_cert ctx;
mbedtls_x509write_crt_init(&ctx);
mbedtls_x509write_crt_set_version(&ctx, MBEDTLS_X509_CRT_VERSION_3);
mbedtls_x509write_crt_set_validity(&ctx, "20220901000000", "20320831235959" );
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, core1 ? random_gen : random_gen_core0, NULL);
mbedtls_x509write_crt_set_serial(&ctx, &serial);
uint8_t serial[20];
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));
@@ -114,25 +187,32 @@ int x509_create_cert(mbedtls_ecdsa_context *ecdsa, uint8_t *buffer, size_t buffe
mbedtls_x509write_crt_set_basic_constraints(&ctx, 0, 0);
mbedtls_x509write_crt_set_subject_key_identifier(&ctx);
mbedtls_x509write_crt_set_authority_key_identifier(&ctx);
mbedtls_x509write_crt_set_key_usage(&ctx, MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN);
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, core1 ? random_gen : random_gen_core0, NULL);
mbedtls_x509write_crt_set_key_usage(&ctx,
MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
MBEDTLS_X509_KU_KEY_CERT_SIGN);
int ret = mbedtls_x509write_crt_der(&ctx, buffer, buffer_size, random_gen, NULL);
/* pk cannot be freed, as it is freed later */
//mbedtls_pk_free(&key);
return ret;
}
int load_keydev(uint8_t *key) {
if (has_keydev_dec == false && !file_has_data(ef_keydev))
if (has_keydev_dec == false && !file_has_data(ef_keydev)) {
return CCID_ERR_MEMORY_FATAL;
if (has_keydev_dec == true)
}
if (has_keydev_dec == true) {
memcpy(key, keydev_dec, sizeof(keydev_dec));
else
}
else {
memcpy(key, file_get_data(ef_keydev), file_get_size(ef_keydev));
}
//return mkek_decrypt(key, file_get_size(ef_keydev));
return CCID_OK;
}
int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *key) {
int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecp_keypair *key) {
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
uint32_t k = *(uint32_t *)&keyHandle[i*sizeof(uint32_t)];
uint32_t k = *(uint32_t *) &keyHandle[i * sizeof(uint32_t)];
if (!(k & 0x80000000)) {
return -1;
}
@@ -141,40 +221,61 @@ int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_con
if (key == NULL) {
mbedtls_ecdsa_init(&ctx);
key = &ctx;
if (derive_key(appId, false, (uint8_t *)keyHandle, MBEDTLS_ECP_DP_SECP256R1, &ctx) != 0) {
if (derive_key(appId, false, (uint8_t *) keyHandle, MBEDTLS_ECP_DP_SECP256R1, &ctx) != 0) {
mbedtls_ecdsa_free(&ctx);
return -3;
}
}
uint8_t hmac[32], d[32];
int ret = mbedtls_ecp_write_key(key, d, sizeof(d));
if (key == NULL)
if (key == NULL) {
mbedtls_ecdsa_free(&ctx);
if (ret != 0)
}
if (ret != 0) {
return -2;
}
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_ecp_keypair *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)) != CCID_OK) {
return r;
}
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
for (int i = 0; i < KEY_PATH_ENTRIES; i++) {
if (new_key == true) {
uint32_t val = 0;
random_gen(NULL, (uint8_t *) &val, sizeof(val));
val |= 0x80000000;
memcpy(&key_handle[i*sizeof(uint32_t)], &val, sizeof(uint32_t));
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;
@@ -184,8 +285,9 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
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;
}
@@ -193,19 +295,27 @@ int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int cur
if (key != NULL) {
mbedtls_ecp_group_load(&key->grp, curve);
const mbedtls_ecp_curve_info *cinfo = mbedtls_ecp_curve_info_from_grp_id(curve);
if (cinfo == NULL)
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, ceil((float) cinfo->bit_size / 8));
mbedtls_platform_zeroize(outk, sizeof(outk));
if (r != 0)
if (r != 0) {
return r;
}
if (curve == MBEDTLS_ECP_DP_ED25519) {
return mbedtls_ecp_point_edwards(&key->grp, &key->Q, &key->d, random_gen, NULL);
}
return mbedtls_ecp_mul(&key->grp, &key->Q, &key->d, &key->grp.G, random_gen, NULL);
}
mbedtls_platform_zeroize(outk, sizeof(outk));
return r;
}
int scan_files(bool core1) {
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);
if (ef_keydev) {
@@ -214,7 +324,7 @@ int scan_files(bool core1) {
mbedtls_ecdsa_context ecdsa;
mbedtls_ecdsa_init(&ecdsa);
uint8_t index = 0;
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, core1 ? random_gen : random_gen_core0, &index);
int ret = mbedtls_ecdsa_genkey(&ecdsa, MBEDTLS_ECP_DP_SECP256R1, random_gen, &index);
if (ret != 0) {
mbedtls_ecdsa_free(&ecdsa);
return ret;
@@ -240,16 +350,24 @@ int scan_files(bool core1) {
uint8_t cert[4096];
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));
if (ret != 0)
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1,
&key,
file_get_data(ef_keydev),
file_get_size(ef_keydev));
if (ret != 0) {
mbedtls_ecdsa_free(&key);
return ret;
ret = mbedtls_ecp_mul(&key.grp, &key.Q, &key.d, &key.grp.G, core1 ? random_gen : random_gen_core0, NULL);
if (ret != 0)
}
ret = mbedtls_ecp_mul(&key.grp, &key.Q, &key.d, &key.grp.G, random_gen, NULL);
if (ret != 0) {
mbedtls_ecdsa_free(&key);
return ret;
ret = x509_create_cert(&key, cert, sizeof(cert), core1);
}
ret = x509_create_cert(&key, cert, sizeof(cert));
mbedtls_ecdsa_free(&key);
if (ret <= 0)
if (ret <= 0) {
return ret;
}
flash_write_data_to_file(ef_certdev, cert + sizeof(cert) - ret, ret);
}
}
@@ -260,7 +378,7 @@ int scan_files(bool core1) {
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));
flash_write_data_to_file(ef_counter, (uint8_t *) &v, sizeof(v));
}
}
else {
@@ -271,10 +389,7 @@ int scan_files(bool core1) {
if (ef_authtoken) {
if (!file_has_data(ef_authtoken)) {
uint8_t t[32];
if (core1)
random_gen(NULL, t, sizeof(t));
else
random_gen_core0(NULL, t, sizeof(t));
random_gen(NULL, t, sizeof(t));
flash_write_data_to_file(ef_authtoken, t, sizeof(t));
}
paut.data = file_get_data(ef_authtoken);
@@ -283,37 +398,49 @@ int scan_files(bool core1) {
else {
printf("FATAL ERROR: Auth Token not found in memory!\r\n");
}
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);
}
low_flash_available();
return CCID_OK;
}
void scan_all(bool core1) {
void scan_all() {
scan_flash();
scan_files(core1);
scan_files();
}
void init_fido(bool core1) {
scan_all(core1);
extern void init_otp();
void init_fido() {
scan_all();
init_otp();
}
bool wait_button_pressed() {
uint32_t val = EV_PRESS_BUTTON;
#if defined(ENABLE_UP_BUTTON) && ENABLE_UP_BUTTON==1
#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
return (val == EV_BUTTON_TIMEOUT);
#endif
return val == EV_BUTTON_TIMEOUT;
}
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 (wait_button_pressed() == true) //timeout
#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 (wait_button_pressed() == true) { //timeout
return false;
}
//user_present_time_limit = board_millis();
}
#endif
@@ -325,31 +452,57 @@ uint32_t get_sign_counter() {
return (*caddr) | (*(caddr + 1) << 8) | (*(caddr + 2) << 16) | (*(caddr + 3) << 24);
}
typedef struct cmd
{
uint8_t ins;
int (*cmd_handler)();
} cmd_t;
uint8_t get_opts() {
file_t *ef = search_by_fid(EF_OPTS, NULL, SPECIFY_EF);
if (file_has_data(ef)) {
return *file_get_data(ef);
}
return 0;
}
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));
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 },
{ 0x00, 0x0}
{ 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,11 +18,17 @@
#ifndef _FIDO_H_
#define _FIDO_H_
#include <stdlib.h>
#ifndef ENABLE_EMULATION
#include "pico/stdlib.h"
#endif
#include "common.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/eddsa.h"
#ifndef ENABLE_EMULATION
#include "ctap_hid.h"
#else
#include <stdbool.h>
#endif
#define CTAP_PUBKEY_LEN (65)
#define KEY_PATH_LEN (32)
@@ -30,17 +36,29 @@
#define SHA256_DIGEST_LENGTH (32)
#define KEY_HANDLE_LEN (KEY_PATH_LEN + SHA256_DIGEST_LENGTH)
extern int scan_files(bool);
extern int derive_key(const uint8_t *app_id, bool new_key, uint8_t *key_handle, int, mbedtls_ecdsa_context *key);
extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecdsa_context *);
extern int scan_files();
extern int derive_key(const uint8_t *app_id,
bool new_key,
uint8_t *key_handle,
int,
mbedtls_ecp_keypair *key);
extern int verify_key(const uint8_t *appId, const uint8_t *keyHandle, mbedtls_ecp_keypair *);
extern bool wait_button_pressed();
extern CTAPHID_FRAME *ctap_req, *ctap_resp;
extern void init_fido(bool);
extern void init_fido();
extern mbedtls_ecp_group_id fido_curve_to_mbedtls(int curve);
extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecdsa_context *key);
extern int mbedtls_curve_to_fido(mbedtls_ecp_group_id id);
extern int fido_load_key(int curve, const uint8_t *cred_id, mbedtls_ecp_keypair *key);
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,
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 ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSecret);
#define FIDO2_ALG_ES256 -7 //ECDSA-SHA256 P256
@@ -48,6 +66,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
@@ -63,12 +85,7 @@ extern int ecdh(uint8_t protocol, const mbedtls_ecp_point *Q, uint8_t *sharedSec
#define FIDO2_AUT_FLAG_AT 0x40
#define FIDO2_AUT_FLAG_ED 0x80
#define FIDO2_PERMISSION_MC 0x1
#define FIDO2_PERMISSION_GA 0x2
#define FIDO2_PERMISSION_CM 0x4
#define FIDO2_PERMISSION_BE 0x8
#define FIDO2_PERMISSION_LBW 0x10
#define FIDO2_PERMISSION_ACFG 0x20
#define FIDO2_OPT_EA 0x01 // Enterprise Attestation
#define MAX_PIN_RETRIES 8
extern bool getUserPresentFlagValue();
@@ -78,9 +95,15 @@ extern void clearUserVerifiedFlag();
extern void clearPinUvAuthTokenPermissionsExceptLbw();
extern void send_keepalive();
extern uint32_t get_sign_counter();
extern uint8_t get_opts();
extern void set_opts(uint8_t);
#define MAX_CREDENTIAL_COUNT_IN_LIST 16
#define MAX_CRED_ID_LENGTH 1024
#define MAX_RESIDENT_CREDENTIALS 256
#define MAX_CREDBLOB_LENGTH 128
#define MAX_MSG_SIZE 1024
#define MAX_FRAGMENT_LENGTH (MAX_MSG_SIZE - 64)
#define MAX_LARGE_BLOB_SIZE 2048
typedef struct known_app {
const uint8_t *rp_id_hash;
@@ -91,7 +114,7 @@ typedef struct known_app {
extern const known_app_t *find_app_by_rp_id_hash(const uint8_t *rp_id_hash);
#define TRANSPORT_TIME_LIMIT (30*1000) //USB
#define TRANSPORT_TIME_LIMIT (30 * 1000) //USB
bool check_user_presence();
@@ -101,6 +124,7 @@ typedef struct pinUvAuthToken {
bool in_use;
uint8_t permissions;
uint8_t rp_id_hash[32];
bool has_rp_id;
bool user_present;
bool user_verified;
} pinUvAuthToken_t;
@@ -108,6 +132,10 @@ 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,
size_t len,
uint8_t *sign);
#endif //_FIDO_H

View File

@@ -15,25 +15,50 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "file.h"
#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_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 = 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_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_UNKNOWN, .data = NULL,
.ef_structure = 0, .acl = { 0 } } //end
};
const file_t *MF = &file_entries[0];
const file_t *file_last = &file_entries[sizeof(file_entries)/sizeof(file_t)-1];
const file_t *file_last = &file_entries[sizeof(file_entries) / sizeof(file_t) - 1];
file_t *ef_keydev = NULL;
file_t *ef_certdev = NULL;
file_t *ef_counter = NULL;
file_t *ef_pin = NULL;
file_t *ef_authtoken = NULL;
file_t *ef_keydev_enc = NULL;
file_t *ef_largeblob = NULL;

View File

@@ -23,11 +23,21 @@
#define EF_KEY_DEV 0xCC00
#define EF_KEY_DEV_ENC 0xCC01
#define EF_EE_DEV 0xCE00
#define EF_EE_DEV_EA 0xCE01
#define EF_COUNTER 0xC000
#define EF_OPTS 0xC001
#define EF_PIN 0x1080
#define EF_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
#define EF_OATH_CRED 0xBA00 // OATH Creds at 0xBA00 - 0xBAFE
#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;
@@ -35,5 +45,6 @@ extern file_t *ef_counter;
extern file_t *ef_pin;
extern file_t *ef_authtoken;
extern file_t *ef_keydev_enc;
extern file_t *ef_largeblob;
#endif //_FILES_H_

View File

@@ -15,280 +15,359 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "fido.h"
#include "ctap2_cbor.h"
static const known_app_t kapps[] = {
{
.rp_id_hash = (const uint8_t *)"\x96\x89\x78\xa2\x99\x53\xde\x52\xd3\xef\x0f\x0c\x71\xb7\xb7\xb6\xb1\xaf\x9f\x08\xe2\x57\x89\x6a\x8d\x81\x26\x91\x85\x30\x29\x3b",
.rp_id_hash =
(const uint8_t *)
"\x96\x89\x78\xa2\x99\x53\xde\x52\xd3\xef\x0f\x0c\x71\xb7\xb7\xb6\xb1\xaf\x9f\x08\xe2\x57\x89\x6a\x8d\x81\x26\x91\x85\x30\x29\x3b",
.label = "aws.amazon.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xc3\x40\x8c\x04\x47\x88\xae\xa5\xb3\xdf\x30\x89\x52\xfd\x8c\xa3\xc7\x0e\x21\xfe\xf4\xf6\xc1\xc2\x37\x4c\xaa\x1d\xf9\xb2\x8d\xdd",
.rp_id_hash =
(const uint8_t *)
"\xc3\x40\x8c\x04\x47\x88\xae\xa5\xb3\xdf\x30\x89\x52\xfd\x8c\xa3\xc7\x0e\x21\xfe\xf4\xf6\xc1\xc2\x37\x4c\xaa\x1d\xf9\xb2\x8d\xdd",
.label = "www.binance.com",
.use_sign_count = pfalse,
.use_self_attestation = ptrue,
},
{
.rp_id_hash = (const uint8_t *)"\x20\xf6\x61\xb1\x94\x0c\x34\x70\xac\x54\xfa\x2e\xb4\x99\x90\xfd\x33\xb5\x6d\xe8\xde\x60\x18\x70\xff\x02\xa8\x06\x0f\x3b\x7c\x58",
.rp_id_hash =
(const uint8_t *)
"\x20\xf6\x61\xb1\x94\x0c\x34\x70\xac\x54\xfa\x2e\xb4\x99\x90\xfd\x33\xb5\x6d\xe8\xde\x60\x18\x70\xff\x02\xa8\x06\x0f\x3b\x7c\x58",
.label = "binance.com",
.use_sign_count = pfalse,
.use_self_attestation = ptrue,
},
{
.rp_id_hash = (const uint8_t *)"\x12\x74\x3b\x92\x12\x97\xb7\x7f\x11\x35\xe4\x1f\xde\xdd\x4a\x84\x6a\xfe\x82\xe1\xf3\x69\x32\xa9\x91\x2f\x3b\x0d\x8d\xfb\x7d\x0e",
.rp_id_hash =
(const uint8_t *)
"\x12\x74\x3b\x92\x12\x97\xb7\x7f\x11\x35\xe4\x1f\xde\xdd\x4a\x84\x6a\xfe\x82\xe1\xf3\x69\x32\xa9\x91\x2f\x3b\x0d\x8d\xfb\x7d\x0e",
//U2F key for Bitbucket
.label = "bitbucket.org",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x30\x2f\xd5\xb4\x49\x2a\x07\xb9\xfe\xbb\x30\xe7\x32\x69\xec\xa5\x01\x20\x5c\xcf\xe0\xc2\x0b\xf7\xb4\x72\xfa\x2d\x31\xe2\x1e\x63",
.rp_id_hash =
(const uint8_t *)
"\x30\x2f\xd5\xb4\x49\x2a\x07\xb9\xfe\xbb\x30\xe7\x32\x69\xec\xa5\x01\x20\x5c\xcf\xe0\xc2\x0b\xf7\xb4\x72\xfa\x2d\x31\xe2\x1e\x63",
//U2F key for Bitfinex
.label = "www.bitfinex.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xa3\x4d\x30\x9f\xfa\x28\xc1\x24\x14\xb8\xba\x6c\x07\xee\x1e\xfa\xe1\xa8\x5e\x8a\x04\x61\x48\x59\xa6\x7c\x04\x93\xb6\x95\x61\x90",
.rp_id_hash =
(const uint8_t *)
"\xa3\x4d\x30\x9f\xfa\x28\xc1\x24\x14\xb8\xba\x6c\x07\xee\x1e\xfa\xe1\xa8\x5e\x8a\x04\x61\x48\x59\xa6\x7c\x04\x93\xb6\x95\x61\x90",
//U2F key for Bitwarden
.label = "vault.bitwarden.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x19\x81\x5c\xb9\xa5\xfb\x25\xd8\x05\xde\xbd\x7b\x32\x53\x7e\xd5\x78\x63\x9b\x3e\xd1\x08\xec\x7c\x5b\xb9\xe8\xf0\xdf\xb1\x68\x73",
.rp_id_hash =
(const uint8_t *)
"\x19\x81\x5c\xb9\xa5\xfb\x25\xd8\x05\xde\xbd\x7b\x32\x53\x7e\xd5\x78\x63\x9b\x3e\xd1\x08\xec\x7c\x5b\xb9\xe8\xf0\xdf\xb1\x68\x73",
//WebAuthn key for Cloudflare
.label = "dash.cloudflare.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xe2\x7d\x61\xb4\xe9\x9d\xe0\xed\x98\x16\x3c\xb3\x8b\x7a\xf9\x33\xc6\x66\x5e\x55\x09\xe8\x49\x08\x37\x05\x58\x13\x77\x8e\x23\x6a",
.rp_id_hash =
(const uint8_t *)
"\xe2\x7d\x61\xb4\xe9\x9d\xe0\xed\x98\x16\x3c\xb3\x8b\x7a\xf9\x33\xc6\x66\x5e\x55\x09\xe8\x49\x08\x37\x05\x58\x13\x77\x8e\x23\x6a",
//WebAuthn key for Coinbase
.label = "coinbase.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x68\x20\x19\x15\xd7\x4c\xb4\x2a\xf5\xb3\xcc\x5c\x95\xb9\x55\x3e\x3e\x3a\x83\xb4\xd2\xa9\x3b\x45\xfb\xad\xaa\x84\x69\xff\x8e\x6e",
.rp_id_hash =
(const uint8_t *)
"\x68\x20\x19\x15\xd7\x4c\xb4\x2a\xf5\xb3\xcc\x5c\x95\xb9\x55\x3e\x3e\x3a\x83\xb4\xd2\xa9\x3b\x45\xfb\xad\xaa\x84\x69\xff\x8e\x6e",
//U2F key for Dashlane
.label = "www.dashlane.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xc5\x0f\x8a\x7b\x70\x8e\x92\xf8\x2e\x7a\x50\xe2\xbd\xc5\x5d\x8f\xd9\x1a\x22\xfe\x6b\x29\xc0\xcd\xf7\x80\x55\x30\x84\x2a\xf5\x81",
.rp_id_hash =
(const uint8_t *)
"\xc5\x0f\x8a\x7b\x70\x8e\x92\xf8\x2e\x7a\x50\xe2\xbd\xc5\x5d\x8f\xd9\x1a\x22\xfe\x6b\x29\xc0\xcd\xf7\x80\x55\x30\x84\x2a\xf5\x81",
//U2F key for Dropbox
.label = "www.dropbox.com",
.use_sign_count = pfalse,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x82\xf4\xa8\xc9\x5f\xec\x94\xb2\x6b\xaf\x9e\x37\x25\x0e\x95\x63\xd9\xa3\x66\xc7\xbe\x26\x1c\xa4\xdd\x01\x01\xf4\xd5\xef\xcb\x83",
.rp_id_hash =
(const uint8_t *)
"\x82\xf4\xa8\xc9\x5f\xec\x94\xb2\x6b\xaf\x9e\x37\x25\x0e\x95\x63\xd9\xa3\x66\xc7\xbe\x26\x1c\xa4\xdd\x01\x01\xf4\xd5\xef\xcb\x83",
//WebAuthn key for Dropbox
.label = "www.dropbox.com",
.use_sign_count = pfalse,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xf3\xe2\x04\x2f\x94\x60\x7d\xa0\xa9\xc1\xf3\xb9\x5e\x0d\x2f\x2b\xb2\xe0\x69\xc5\xbb\x4f\xa7\x64\xaf\xfa\x64\x7d\x84\x7b\x7e\xd6",
.rp_id_hash =
(const uint8_t *)
"\xf3\xe2\x04\x2f\x94\x60\x7d\xa0\xa9\xc1\xf3\xb9\x5e\x0d\x2f\x2b\xb2\xe0\x69\xc5\xbb\x4f\xa7\x64\xaf\xfa\x64\x7d\x84\x7b\x7e\xd6",
//U2F key for Duo
.label = "duosecurity.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x31\x19\x33\x28\xf8\xe2\x1d\xfb\x6c\x99\xf3\x22\xd2\x2d\x7b\x0b\x50\x87\x78\xe6\x4f\xfb\xba\x86\xe5\x22\x93\x37\x90\x31\xb8\x74",
.rp_id_hash =
(const uint8_t *)
"\x31\x19\x33\x28\xf8\xe2\x1d\xfb\x6c\x99\xf3\x22\xd2\x2d\x7b\x0b\x50\x87\x78\xe6\x4f\xfb\xba\x86\xe5\x22\x93\x37\x90\x31\xb8\x74",
//WebAuthn key for Facebook
.label = "facebook.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x69\x66\xab\xe3\x67\x4e\xa2\xf5\x30\x79\xeb\x71\x01\x97\x84\x8c\x9b\xe6\xf3\x63\x99\x2f\xd0\x29\xe9\x89\x84\x47\xcb\x9f\x00\x84",
.rp_id_hash =
(const uint8_t *)
"\x69\x66\xab\xe3\x67\x4e\xa2\xf5\x30\x79\xeb\x71\x01\x97\x84\x8c\x9b\xe6\xf3\x63\x99\x2f\xd0\x29\xe9\x89\x84\x47\xcb\x9f\x00\x84",
//U2F key for FastMail
.label = "www.fastmail.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x3f\xcb\x82\x82\xb8\x46\x76\xeb\xee\x71\x40\xe3\x9e\xca\xe1\x6e\xeb\x19\x90\x64\xc7\xc7\xe4\x43\x2e\x28\xc9\xb5\x7e\x4b\x60\x39",
.rp_id_hash =
(const uint8_t *)
"\x3f\xcb\x82\x82\xb8\x46\x76\xeb\xee\x71\x40\xe3\x9e\xca\xe1\x6e\xeb\x19\x90\x64\xc7\xc7\xe4\x43\x2e\x28\xc9\xb5\x7e\x4b\x60\x39",
.label = "fastmail.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x9d\x61\x44\x2f\x5c\xe1\x33\xbd\x46\x54\x4f\xc4\x2f\x0a\x6d\x54\xc0\xde\xb8\x88\x40\xca\xc2\xb6\xae\xfa\x65\x14\xf8\x93\x49\xe9",
.rp_id_hash =
(const uint8_t *)
"\x9d\x61\x44\x2f\x5c\xe1\x33\xbd\x46\x54\x4f\xc4\x2f\x0a\x6d\x54\xc0\xde\xb8\x88\x40\xca\xc2\xb6\xae\xfa\x65\x14\xf8\x93\x49\xe9",
.label = "fedoraproject.org",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xa4\xe2\x2d\xca\xfe\xa7\xe9\x0e\x12\x89\x50\x11\x39\x89\xfc\x45\x97\x8d\xc9\xfb\x87\x76\x75\x60\x51\x6c\x1c\x69\xdf\xdf\xd1\x96",
.rp_id_hash =
(const uint8_t *)
"\xa4\xe2\x2d\xca\xfe\xa7\xe9\x0e\x12\x89\x50\x11\x39\x89\xfc\x45\x97\x8d\xc9\xfb\x87\x76\x75\x60\x51\x6c\x1c\x69\xdf\xdf\xd1\x96",
.label = "gandi.net",
.use_sign_count = pfalse,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x54\xce\x65\x1e\xd7\x15\xb4\xaa\xa7\x55\xee\xce\xbd\x4e\xa0\x95\x08\x15\xb3\x34\xbd\x07\xd1\x09\x89\x3e\x96\x30\x18\xcd\xdb\xd9",
.rp_id_hash =
(const uint8_t *)
"\x54\xce\x65\x1e\xd7\x15\xb4\xaa\xa7\x55\xee\xce\xbd\x4e\xa0\x95\x08\x15\xb3\x34\xbd\x07\xd1\x09\x89\x3e\x96\x30\x18\xcd\xdb\xd9",
//WebAuthn key for Gandi
.label = "gandi.net",
.use_sign_count = pfalse,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x86\x06\xc1\x68\xe5\x1f\xc1\x31\xe5\x46\xad\x57\xa1\x9f\x32\x97\xb1\x1e\x0e\x5c\xe8\x3e\x8e\x89\x31\xb2\x85\x08\x11\xcf\xa8\x81",
.rp_id_hash =
(const uint8_t *)
"\x86\x06\xc1\x68\xe5\x1f\xc1\x31\xe5\x46\xad\x57\xa1\x9f\x32\x97\xb1\x1e\x0e\x5c\xe8\x3e\x8e\x89\x31\xb2\x85\x08\x11\xcf\xa8\x81",
//WebAuthn key for Gemini
.label = "gemini.com",
.use_sign_count = pfalse,
.use_self_attestation = ptrue,
},
{
.rp_id_hash = (const uint8_t *)"\x70\x61\x7d\xfe\xd0\x65\x86\x3a\xf4\x7c\x15\x55\x6c\x91\x79\x88\x80\x82\x8c\xc4\x07\xfd\xf7\x0a\xe8\x50\x11\x56\x94\x65\xa0\x75",
.rp_id_hash =
(const uint8_t *)
"\x70\x61\x7d\xfe\xd0\x65\x86\x3a\xf4\x7c\x15\x55\x6c\x91\x79\x88\x80\x82\x8c\xc4\x07\xfd\xf7\x0a\xe8\x50\x11\x56\x94\x65\xa0\x75",
//U2F key for GitHub
.label = "github.com",
.use_sign_count = ptrue,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x3a\xeb\x00\x24\x60\x38\x1c\x6f\x25\x8e\x83\x95\xd3\x02\x6f\x57\x1f\x0d\x9a\x76\x48\x8d\xcd\x83\x76\x39\xb1\x3a\xed\x31\x65\x60",
.rp_id_hash =
(const uint8_t *)
"\x3a\xeb\x00\x24\x60\x38\x1c\x6f\x25\x8e\x83\x95\xd3\x02\x6f\x57\x1f\x0d\x9a\x76\x48\x8d\xcd\x83\x76\x39\xb1\x3a\xed\x31\x65\x60",
//WebAuthn key for GitHub
.label = "github.com",
.use_sign_count = ptrue,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xe7\xbe\x96\xa5\x1b\xd0\x19\x2a\x72\x84\x0d\x2e\x59\x09\xf7\x2b\xa8\x2a\x2f\xe9\x3f\xaa\x62\x4f\x03\x39\x6b\x30\xe4\x94\xc8\x04",
.rp_id_hash =
(const uint8_t *)
"\xe7\xbe\x96\xa5\x1b\xd0\x19\x2a\x72\x84\x0d\x2e\x59\x09\xf7\x2b\xa8\x2a\x2f\xe9\x3f\xaa\x62\x4f\x03\x39\x6b\x30\xe4\x94\xc8\x04",
//U2F key for GitLab
.label = "gitlab.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xa5\x46\x72\xb2\x22\xc4\xcf\x95\xe1\x51\xed\x8d\x4d\x3c\x76\x7a\x6c\xc3\x49\x43\x59\x43\x79\x4e\x88\x4f\x3d\x02\x3a\x82\x29\xfd",
.rp_id_hash =
(const uint8_t *)
"\xa5\x46\x72\xb2\x22\xc4\xcf\x95\xe1\x51\xed\x8d\x4d\x3c\x76\x7a\x6c\xc3\x49\x43\x59\x43\x79\x4e\x88\x4f\x3d\x02\x3a\x82\x29\xfd",
//U2F key for Google
.label = "google.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xd4\xc9\xd9\x02\x73\x26\x27\x1a\x89\xce\x51\xfc\xaf\x32\x8e\xd6\x73\xf1\x7b\xe3\x34\x69\xff\x97\x9e\x8a\xb8\xdd\x50\x1e\x66\x4f",
.rp_id_hash =
(const uint8_t *)
"\xd4\xc9\xd9\x02\x73\x26\x27\x1a\x89\xce\x51\xfc\xaf\x32\x8e\xd6\x73\xf1\x7b\xe3\x34\x69\xff\x97\x9e\x8a\xb8\xdd\x50\x1e\x66\x4f",
//WebAuthn key for Google
.label = "google.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x53\xa1\x5b\xa4\x2a\x7c\x03\x25\xb8\xdb\xee\x28\x96\x34\xa4\x8f\x58\xae\xa3\x24\x66\x45\xd5\xff\x41\x8f\x9b\xb8\x81\x98\x85\xa9",
.rp_id_hash =
(const uint8_t *)
"\x53\xa1\x5b\xa4\x2a\x7c\x03\x25\xb8\xdb\xee\x28\x96\x34\xa4\x8f\x58\xae\xa3\x24\x66\x45\xd5\xff\x41\x8f\x9b\xb8\x81\x98\x85\xa9",
//U2F key for Keeper
.label = "keepersecurity.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xd6\x5f\x00\x5e\xf4\xde\xa9\x32\x0c\x99\x73\x05\x3c\x95\xff\x60\x20\x11\x5d\x5f\xec\x1b\x7f\xee\x41\xa5\x78\xe1\x8d\xf9\xca\x8c",
.rp_id_hash =
(const uint8_t *)
"\xd6\x5f\x00\x5e\xf4\xde\xa9\x32\x0c\x99\x73\x05\x3c\x95\xff\x60\x20\x11\x5d\x5f\xec\x1b\x7f\xee\x41\xa5\x78\xe1\x8d\xf9\xca\x8c",
//U2F key for Keeper
.label = "keepersecurity.eu",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x3f\x37\x50\x85\x33\x2c\xac\x4f\xad\xf9\xe5\xdd\x28\xcd\x54\x69\x8f\xab\x98\x4b\x75\xd9\xc3\x6a\x07\x2c\xb1\x60\x77\x3f\x91\x52",
.rp_id_hash =
(const uint8_t *)
"\x3f\x37\x50\x85\x33\x2c\xac\x4f\xad\xf9\xe5\xdd\x28\xcd\x54\x69\x8f\xab\x98\x4b\x75\xd9\xc3\x6a\x07\x2c\xb1\x60\x77\x3f\x91\x52",
//WebAuthn key for Kraken
.label = "kraken.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xf8\x3f\xc3\xa1\xb2\x89\xa0\xde\xc5\xc1\xc8\xaa\x07\xe9\xb5\xdd\x9c\xbb\x76\xf6\xb2\xf5\x60\x60\x17\x66\x72\x68\xe5\xb9\xc4\x5e",
.rp_id_hash =
(const uint8_t *)
"\xf8\x3f\xc3\xa1\xb2\x89\xa0\xde\xc5\xc1\xc8\xaa\x07\xe9\xb5\xdd\x9c\xbb\x76\xf6\xb2\xf5\x60\x60\x17\x66\x72\x68\xe5\xb9\xc4\x5e",
//WebAuthn key for login.gov
.label = "secure.login.gov",
.use_sign_count = pfalse,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x35\x6c\x9e\xd4\xa0\x93\x21\xb9\x69\x5f\x1e\xaf\x91\x82\x03\xf1\xb5\x5f\x68\x9d\xa6\x1f\xbc\x96\x18\x4c\x15\x7d\xda\x68\x0c\x81",
.rp_id_hash =
(const uint8_t *)
"\x35\x6c\x9e\xd4\xa0\x93\x21\xb9\x69\x5f\x1e\xaf\x91\x82\x03\xf1\xb5\x5f\x68\x9d\xa6\x1f\xbc\x96\x18\x4c\x15\x7d\xda\x68\x0c\x81",
//WebAuthn key for Microsoft
.label = "login.microsoft.com",
.use_sign_count = pfalse,
.use_self_attestation = pfalse,
},
{
.rp_id_hash = (const uint8_t *)"\xab\x2d\xaf\x07\x43\xde\x78\x2a\x70\x18\x9a\x0f\x5e\xfc\x30\x90\x2f\x92\x5b\x9f\x9a\x18\xc5\xd7\x14\x1b\x7b\x12\xf8\xa0\x10\x0c",
.rp_id_hash =
(const uint8_t *)
"\xab\x2d\xaf\x07\x43\xde\x78\x2a\x70\x18\x9a\x0f\x5e\xfc\x30\x90\x2f\x92\x5b\x9f\x9a\x18\xc5\xd7\x14\x1b\x7b\x12\xf8\xa0\x10\x0c",
//WebAuthn key for mojeID
.label = "mojeid.cz",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x85\x71\x01\x36\x1b\x20\xa9\x54\x4c\xdb\x9b\xef\x65\x85\x8b\x6b\xac\x70\x13\x55\x0d\x8f\x84\xf7\xef\xee\x25\x2b\x96\xfa\x7c\x1e",
.rp_id_hash =
(const uint8_t *)
"\x85\x71\x01\x36\x1b\x20\xa9\x54\x4c\xdb\x9b\xef\x65\x85\x8b\x6b\xac\x70\x13\x55\x0d\x8f\x84\xf7\xef\xee\x25\x2b\x96\xfa\x7c\x1e",
//WebAuthn key for Namecheap
.label = "www.namecheap.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x08\xb2\xa3\xd4\x19\x39\xaa\x31\x66\x84\x93\xcb\x36\xcd\xcc\x4f\x16\xc4\xd9\xb4\xc8\x23\x8b\x73\xc2\xf6\x72\xc0\x33\x00\x71\x97",
.rp_id_hash =
(const uint8_t *)
"\x08\xb2\xa3\xd4\x19\x39\xaa\x31\x66\x84\x93\xcb\x36\xcd\xcc\x4f\x16\xc4\xd9\xb4\xc8\x23\x8b\x73\xc2\xf6\x72\xc0\x33\x00\x71\x97",
//U2F key for Slush Pool
.label = "slushpool.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x38\x80\x4f\x2e\xff\x74\xf2\x28\xb7\x41\x51\xc2\x01\xaa\x82\xe7\xe8\xee\xfc\xac\xfe\xcf\x23\xfa\x14\x6b\x13\xa3\x76\x66\x31\x4f",
.rp_id_hash =
(const uint8_t *)
"\x38\x80\x4f\x2e\xff\x74\xf2\x28\xb7\x41\x51\xc2\x01\xaa\x82\xe7\xe8\xee\xfc\xac\xfe\xcf\x23\xfa\x14\x6b\x13\xa3\x76\x66\x31\x4f",
//U2F key for Slush Pool
.label = "slushpool.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x2a\xc6\xad\x09\xa6\xd0\x77\x2c\x44\xda\x73\xa6\x07\x2f\x9d\x24\x0f\xc6\x85\x4a\x70\xd7\x9c\x10\x24\xff\x7c\x75\x59\x59\x32\x92",
.rp_id_hash =
(const uint8_t *)
"\x2a\xc6\xad\x09\xa6\xd0\x77\x2c\x44\xda\x73\xa6\x07\x2f\x9d\x24\x0f\xc6\x85\x4a\x70\xd7\x9c\x10\x24\xff\x7c\x75\x59\x59\x32\x92",
//U2F key for Stripe
.label = "stripe.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xfa\xbe\xec\xe3\x98\x2f\xad\x9d\xdc\xc9\x8f\x91\xbd\x2e\x75\xaf\xc7\xd1\xf4\xca\x54\x49\x29\xb2\xd0\xd0\x42\x12\xdf\xfa\x30\xfa",
.rp_id_hash =
(const uint8_t *)
"\xfa\xbe\xec\xe3\x98\x2f\xad\x9d\xdc\xc9\x8f\x91\xbd\x2e\x75\xaf\xc7\xd1\xf4\xca\x54\x49\x29\xb2\xd0\xd0\x42\x12\xdf\xfa\x30\xfa",
//U2F key for Tutanota
.label = "tutanota.com",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x1b\x3c\x16\xdd\x2f\x7c\x46\xe2\xb4\xc2\x89\xdc\x16\x74\x6b\xcc\x60\xdf\xcf\x0f\xb8\x18\xe1\x32\x15\x52\x6e\x14\x08\xe7\xf4\x68",
.rp_id_hash =
(const uint8_t *)
"\x1b\x3c\x16\xdd\x2f\x7c\x46\xe2\xb4\xc2\x89\xdc\x16\x74\x6b\xcc\x60\xdf\xcf\x0f\xb8\x18\xe1\x32\x15\x52\x6e\x14\x08\xe7\xf4\x68",
//U2F key for u2f.bin.coffee
.label = "u2f.bin.coffee",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xa6\x42\xd2\x1b\x7c\x6d\x55\xe1\xce\x23\xc5\x39\x98\x28\xd2\xc7\x49\xbf\x6a\x6e\xf2\xfe\x03\xcc\x9e\x10\xcd\xf4\xed\x53\x08\x8b",
.rp_id_hash =
(const uint8_t *)
"\xa6\x42\xd2\x1b\x7c\x6d\x55\xe1\xce\x23\xc5\x39\x98\x28\xd2\xc7\x49\xbf\x6a\x6e\xf2\xfe\x03\xcc\x9e\x10\xcd\xf4\xed\x53\x08\x8b",
//WebAuthn key for webauthn.bin.coffee
.label = "webauthn.bin.coffee",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\x74\xa6\xea\x92\x13\xc9\x9c\x2f\x74\xb2\x24\x92\xb3\x20\xcf\x40\x26\x2a\x94\xc1\xa9\x50\xa0\x39\x7f\x29\x25\x0b\x60\x84\x1e\xf0",
.rp_id_hash =
(const uint8_t *)
"\x74\xa6\xea\x92\x13\xc9\x9c\x2f\x74\xb2\x24\x92\xb3\x20\xcf\x40\x26\x2a\x94\xc1\xa9\x50\xa0\x39\x7f\x29\x25\x0b\x60\x84\x1e\xf0",
//WebAuthn key for WebAuthn.io
.label = "webauthn.io",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xf9\x5b\xc7\x38\x28\xee\x21\x0f\x9f\xd3\xbb\xe7\x2d\x97\x90\x80\x13\xb0\xa3\x75\x9e\x9a\xea\x3d\x0a\xe3\x18\x76\x6c\xd2\xe1\xad",
.rp_id_hash =
(const uint8_t *)
"\xf9\x5b\xc7\x38\x28\xee\x21\x0f\x9f\xd3\xbb\xe7\x2d\x97\x90\x80\x13\xb0\xa3\x75\x9e\x9a\xea\x3d\x0a\xe3\x18\x76\x6c\xd2\xe1\xad",
//WebAuthn key for WebAuthn.me
.label = "webauthn.me",
.use_sign_count = NULL,
.use_self_attestation = NULL,
},
{
.rp_id_hash = (const uint8_t *)"\xc4\x6c\xef\x82\xad\x1b\x54\x64\x77\x59\x1d\x00\x8b\x08\x75\x9e\xc3\xe6\xd2\xec\xb4\xf3\x94\x74\xbf\xea\x69\x69\x92\x5d\x03\xb7",
.rp_id_hash =
(const uint8_t *)
"\xc4\x6c\xef\x82\xad\x1b\x54\x64\x77\x59\x1d\x00\x8b\x08\x75\x9e\xc3\xe6\xd2\xec\xb4\xf3\x94\x74\xbf\xea\x69\x69\x92\x5d\x03\xb7",
//WebAuthn key for demo.yubico.com
.label = "demo.yubico.com",
.use_sign_count = NULL,
@@ -304,8 +383,9 @@ static const known_app_t kapps[] = {
const known_app_t *find_app_by_rp_id_hash(const uint8_t *rp_id_hash) {
for (const known_app_t *ka = &kapps[0]; ka->rp_id_hash != NULL; ka++) {
if (memcmp(rp_id_hash, ka->rp_id_hash, 32) == 0)
if (memcmp(rp_id_hash, ka->rp_id_hash, 32) == 0) {
return ka;
}
}
return NULL;
}

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

@@ -0,0 +1,156 @@
/*
* 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) {
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 = strlen((char *) res_APDU);
apdu.ne = res_APDU_size;
scan_all();
init_otp();
return CCID_OK;
}
void __attribute__((constructor)) man_ctor() {
register_app(man_select, man_aid);
}
int man_unload() {
return CCID_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, data_len = file_get_size(ef);
uint8_t *tag_data = NULL, *p = NULL, *data = file_get_data(ef);
size_t tag_len = 0;
while (walk_tlv(data, data_len, &p, &tag, &tag_len, &tag_data)) {
if (tag == TAG_USB_ENABLED) {
uint16_t ecaps = tag_data[0];
if (tag_len == 2) {
ecaps = (tag_data[0] << 8) | tag_data[1];
}
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;
#ifndef ENABLE_EMULATION
pico_get_unique_board_id_string((char *) res_APDU + res_APDU_size, 4);
#endif
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;
res_APDU[res_APDU_size++] = TAG_NFC_SUPPORTED;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = 0x00;
if (!file_has_data(ef)) {
res_APDU[res_APDU_size++] = TAG_USB_ENABLED;
res_APDU[res_APDU_size++] = 2;
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;
res_APDU[res_APDU_size++] = TAG_NFC_ENABLED;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = 0x00;
}
else {
memcpy(res_APDU + res_APDU_size, file_get_data(ef), file_get_size(ef));
res_APDU_size += file_get_size(ef);
}
res_APDU[0] = 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);
flash_write_data_to_file(ef, apdu.data + 1, apdu.nc - 1);
low_flash_available();
return SW_OK();
}
#define INS_READ_CONFIG 0x1D
#define INS_WRITE_CONFIG 0x1C
static const cmd_t cmds[] = {
{ INS_READ_CONFIG, cmd_read_config },
{ INS_WRITE_CONFIG, cmd_write_config },
{ 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>
#ifndef ENABLE_EMULATION
#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

657
src/fido/oath.c Normal file
View File

@@ -0,0 +1,657 @@
/*
* 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 "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
#define TAG_KEY 0x73
#define TAG_CHALLENGE 0x74
#define TAG_RESPONSE 0x75
#define TAG_T_RESPONSE 0x76
#define TAG_NO_RESPONSE 0x77
#define TAG_PROPERTY 0x78
#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
#define ALG_HMAC_SHA512 0x03
#define ALG_MASK 0x0f
#define OATH_TYPE_HOTP 0x10
#define OATH_TYPE_TOTP 0x20
#define OATH_TYPE_MASK 0xf0
#define PROP_INC 0x01
#define PROP_TOUCH 0x02
int oath_process_apdu();
int oath_unload();
static bool validated = true;
static uint8_t challenge[CHALLENGE_LEN] = { 0 };
const uint8_t oath_aid[] = {
7,
0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01
};
int oath_select(app_t *a) {
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_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
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 CCID_OK;
}
return CCID_ERR_FILE_NOT_FOUND;
}
void __attribute__((constructor)) oath_ctor() {
register_app(oath_select, oath_aid);
}
int oath_unload() {
return CCID_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) {
return ef;
}
}
return NULL;
}
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) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &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) {
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);
}
}
}
file_t *ef = find_oath_cred(name, name_len);
if (file_has_data(ef)) {
flash_write_data_to_file(ef, apdu.data, 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);
low_flash_available();
return SW_OK();
}
}
return SW_FILE_FULL();
}
return SW_OK();
}
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);
if (ef) {
delete_file(ef);
return SW_OK();
}
return SW_DATA_INVALID();
}
return SW_INCORRECT_PARAMS();
}
const mbedtls_md_info_t *get_oath_md_info(uint8_t alg) {
if ((alg & ALG_MASK) == ALG_HMAC_SHA1) {
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
}
else if ((alg & ALG_MASK) == ALG_HMAC_SHA256) {
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
}
else if ((alg & ALG_MASK) == ALG_HMAC_SHA512) {
return mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
}
return NULL;
}
int cmd_set_code() {
if (validated == false) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
if (apdu.nc == 0) {
delete_file(search_dynamic_file(EF_OATH_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) {
return SW_INCORRECT_PARAMS();
}
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) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &resp_len, &resp) == false) {
return SW_INCORRECT_PARAMS();
}
const mbedtls_md_info_t *md_info = get_oath_md_info(key[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);
if (r != 0) {
return SW_EXEC_ERROR();
}
if (memcmp(hmac, resp, 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);
low_flash_available();
validated = false;
return SW_OK();
}
int cmd_reset() {
if (P1(apdu) != 0xde || P2(apdu) != 0xad) {
return SW_INCORRECT_P1P2();
}
for (int i = 0; i < MAX_OATH_CRED; i++) {
file_t *ef = search_dynamic_file(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);
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) {
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;
}
}
}
apdu.ne = res_APDU_size;
return SW_OK();
}
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) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &resp_len, &resp) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = search_dynamic_file(EF_OATH_CODE);
if (file_has_data(ef) == false) {
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]);
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);
if (ret != 0) {
return SW_EXEC_ERROR();
}
if (memcmp(hmac, resp, resp_len) != 0) {
return SW_DATA_INVALID();
}
ret = mbedtls_md_hmac(md_info, key + 1, key_len - 1, chal, chal_len, hmac);
if (ret != 0) {
return SW_EXEC_ERROR();
}
validated = true;
res_APDU[res_APDU_size++] = TAG_RESPONSE;
res_APDU[res_APDU_size++] = mbedtls_md_get_size(md_info);
memcpy(res_APDU + res_APDU_size, hmac, mbedtls_md_get_size(md_info));
res_APDU_size += mbedtls_md_get_size(md_info);
apdu.ne = res_APDU_size;
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) {
const mbedtls_md_info_t *md_info = get_oath_md_info(key[0]);
if (md_info == NULL) {
return SW_INCORRECT_PARAMS();
}
uint8_t hmac[64];
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;
}
if (truncate == 0x01) {
res_APDU[res_APDU_size++] = 4 + 1;
res_APDU[res_APDU_size++] = key[1];
uint8_t offset = hmac[hmac_size - 1] & 0x0f;
res_APDU[res_APDU_size++] = hmac[offset] & 0x7f;
res_APDU[res_APDU_size++] = hmac[offset + 1];
res_APDU[res_APDU_size++] = hmac[offset + 2];
res_APDU[res_APDU_size++] = hmac[offset + 3];
}
else {
res_APDU[res_APDU_size++] = hmac_size + 1;
res_APDU[res_APDU_size++] = key[1];
memcpy(res_APDU + res_APDU_size, hmac, hmac_size); res_APDU_size += hmac_size;
}
apdu.ne = res_APDU_size;
return CCID_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) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = find_oath_cred(name, 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) {
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) {
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) {
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];
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);
low_flash_available();
free(tmp);
}
apdu.ne = res_APDU_size;
return SW_OK();
}
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;
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) {
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);
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) {
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++] = TAG_NO_RESPONSE;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = key[1];
}
else if (asn1_find_tag(ef_data, ef_len, TAG_PROPERTY, &prop_len,
&prop) == true && (prop[0] & PROP_TOUCH)) {
res_APDU[res_APDU_size++] = TAG_TOUCH_RESPONSE;
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = key[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) {
res_APDU[res_APDU_size++] = 1;
res_APDU[res_APDU_size++] = key[1];
}
}
}
}
apdu.ne = res_APDU_size;
return SW_OK();
}
int cmd_send_remaining() {
return SW_OK();
}
int cmd_set_otp_pin() {
size_t pw_len = 0;
uint8_t *pw = NULL, 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();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
hsh[0] = MAX_OTP_COUNTER;
double_hash_pin(pw, pw_len, hsh + 1);
flash_write_data_to_file(ef_otp_pin, hsh, sizeof(hsh));
low_flash_available();
return SW_OK();
}
int cmd_change_otp_pin() {
size_t pw_len = 0, new_pw_len = 0;
uint8_t *pw = NULL, *new_pw = NULL, 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();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
double_hash_pin(pw, 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(apdu.data, apdu.nc, TAG_NEW_PASSWORD, &new_pw_len, &new_pw) == false) {
return SW_INCORRECT_PARAMS();
}
hsh[0] = MAX_OTP_COUNTER;
double_hash_pin(new_pw, new_pw_len, hsh + 1);
flash_write_data_to_file(ef_otp_pin, hsh, sizeof(hsh));
low_flash_available();
return SW_OK();
}
int cmd_verify_otp_pin() {
size_t pw_len = 0;
uint8_t *pw = NULL, 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();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_PASSWORD, &pw_len, &pw) == false) {
return SW_INCORRECT_PARAMS();
}
double_hash_pin(pw, 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;
}
flash_write_data_to_file(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;
flash_write_data_to_file(ef_otp_pin, data_hsh, sizeof(data_hsh));
low_flash_available();
validated = true;
return SW_OK();
}
int cmd_verify_hotp() {
size_t key_len = 0, chal_len = 0, name_len = 0, code_len = 0;
uint8_t *key = NULL, *chal = NULL, *name = NULL, *code = NULL;
uint32_t code_int = 0;
if (asn1_find_tag(apdu.data, apdu.nc, TAG_NAME, &name_len, &name) == false) {
return SW_INCORRECT_PARAMS();
}
file_t *ef = find_oath_cred(name, 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) {
return SW_INCORRECT_PARAMS();
}
if ((key[0] & OATH_TYPE_MASK) != OATH_TYPE_HOTP) {
return SW_DATA_INVALID();
}
if (asn1_find_tag(file_get_data(ef), file_get_size(ef), TAG_IMF, &chal_len,
&chal) == false) {
return SW_INCORRECT_PARAMS();
}
if (asn1_find_tag(apdu.data, apdu.nc, TAG_RESPONSE, &code_len, &code) == true) {
code_int = (code[0] << 24) | (code[1] << 16) | (code[2] << 8) | code[3];
}
int ret = calculate_oath(0x01, key, key_len, chal, chal_len);
if (ret != CCID_OK) {
return SW_EXEC_ERROR();
}
uint32_t res_int = (res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
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 = apdu.ne = 0;
return SW_OK();
}
#define INS_PUT 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
#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_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 }
};
int oath_process_apdu() {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}
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();
}

625
src/fido/otp.c Normal file
View File

@@ -0,0 +1,625 @@
/*
* 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 "files.h"
#include "random.h"
#include "version.h"
#include "asn1.h"
#include "hid/ctap_hid.h"
#ifndef ENABLE_EMULATION
#include "bsp/board.h"
#endif
#include "mbedtls/aes.h"
#include "management.h"
#define FIXED_SIZE 16
#define KEY_SIZE 16
#define UID_SIZE 6
#define KEY_SIZE_OATH 20
#define ACC_CODE_SIZE 6
#define CONFIG1_VALID 0x01
#define CONFIG2_VALID 0x02
#define CONFIG1_TOUCH 0x04
#define CONFIG2_TOUCH 0x08
#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 };
typedef struct otp_config {
uint8_t fixed_data[FIXED_SIZE];
uint8_t uid[UID_SIZE];
uint8_t aes_key[KEY_SIZE];
uint8_t acc_code[ACC_CODE_SIZE];
uint8_t fixed_size;
uint8_t ext_flags;
uint8_t tkt_flags;
uint8_t cfg_flags;
uint8_t rfu[2];
uint16_t crc;
} __attribute__((packed)) otp_config_t;
static const size_t otp_config_size = sizeof(otp_config_t);
uint16_t otp_status();
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
};
int otp_select(app_t *a) {
if (cap_supported(CAP_OTP)) {
a->process_apdu = otp_process_apdu;
a->unload = otp_unload;
if (file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ||
file_has_data(search_dynamic_file(EF_OTP_SLOT2))) {
config_seq = 1;
}
else {
config_seq = 0;
}
otp_status();
memmove(res_APDU, res_APDU + 1, 6);
res_APDU_size = 6;
apdu.ne = res_APDU_size;
return CCID_OK;
}
return CCID_ERR_FILE_NOT_FOUND;
}
uint8_t modhex_tab[] =
{ 'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v' };
int encode_modhex(const uint8_t *in, size_t len, uint8_t *out) {
for (int 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 (int 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 = (data[otp_config_size] << 8) | data[otp_config_size + 1];
if (++counter <= 0x7fff) {
uint8_t new_data[otp_config_size + 8];
memcpy(new_data, data, sizeof(new_data));
new_data[otp_config_size] = counter >> 8;
new_data[otp_config_size + 1] = counter & 0xff;
flash_write_data_to_file(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 |= (uint64_t) *p++ << 56;
imf |= (uint64_t) *p++ << 48;
imf |= (uint64_t) *p++ << 40;
imf |= (uint64_t) *p++ << 32;
imf |= *p++ << 24;
imf |= *p++ << 16;
imf |= *p++ << 8;
imf |= *p++;
if (imf == 0) {
imf = ((otp_config->uid[4] << 8) | otp_config->uid[5]) << 4;
}
uint8_t chal[8] =
{ imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8, imf & 0xff };
res_APDU_size = 0;
int ret = calculate_oath(1, tmp_key, sizeof(tmp_key), chal, sizeof(chal));
if (ret == CCID_OK) {
uint32_t base = otp_config->cfg_flags & OATH_HOTP8 ? 1e8 : 1e6;
uint32_t number =
(res_APDU[2] << 24) | (res_APDU[3] << 16) | (res_APDU[4] << 8) | res_APDU[5];
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] =
{ imf >> 56, imf >> 48, imf >> 40, imf >> 32, imf >> 24, imf >> 16, imf >> 8,
imf & 0xff };
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));
flash_write_data_to_file(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 = (data[otp_config_size] << 8) | data[otp_config_size + 1], 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++ = counter & 0xff;
*po++ = counter >> 8;
ts >>= 3;
*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++ = ~crc & 0xff;
*po++ = ~crc >> 8;
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));
new_data[otp_config_size] = counter >> 8;
new_data[otp_config_size + 1] = counter & 0xff;
flash_write_data_to_file(ef, new_data, sizeof(new_data));
low_flash_available();
}
}
#endif
return 0;
}
void __attribute__((constructor)) 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;
}
uint16_t otp_status() {
if (scanned == false) {
scan_all();
scanned = true;
}
res_APDU_size = 0;
res_APDU[1] = PICO_FIDO_VERSION_MAJOR;
res_APDU[2] = PICO_FIDO_VERSION_MINOR;
res_APDU[3] = 0;
res_APDU[4] = config_seq;
res_APDU[5] = (CONFIG2_TOUCH | CONFIG1_TOUCH) |
(file_has_data(search_dynamic_file(EF_OTP_SLOT1)) ? CONFIG1_VALID :
0x00) |
(file_has_data(search_dynamic_file(EF_OTP_SLOT2)) ? CONFIG2_VALID :
0x00);
res_APDU[6] = 0;
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;
}
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
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);
if (memcmp(otpc->acc_code, apdu.data + otp_config_size, ACC_CODE_SIZE) != 0) {
return SW_SECURITY_STATUS_NOT_SATISFIED();
}
}
for (int c = 0; c < otp_config_size; c++) {
if (apdu.data[c] != 0) {
if (odata->rfu[0] != 0 || odata->rfu[1] != 0 || check_crc(odata) == false) {
return SW_WRONG_DATA();
}
memset(apdu.data + otp_config_size, 0, 8); // Add 8 bytes extra
flash_write_data_to_file(ef, apdu.data, otp_config_size + 8);
low_flash_available();
config_seq++;
return otp_status();
}
}
// Delete slot
delete_file(ef);
if (!file_has_data(search_dynamic_file(EF_OTP_SLOT1)) &&
!file_has_data(search_dynamic_file(EF_OTP_SLOT2))) {
config_seq = 0;
}
return otp_status();
}
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();
}
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);
flash_write_data_to_file(ef, apdu.data, otp_config_size);
low_flash_available();
}
}
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)) {
flash_write_data_to_file(ef1, file_get_data(ef2), file_get_size(ef2));
}
else {
delete_file(ef1);
}
if (ef1_data) {
flash_write_data_to_file(ef2, tmp, sizeof(tmp));
}
else {
delete_file(ef2);
}
low_flash_available();
}
else if (p1 == 0x10) {
#ifndef ENABLE_EMULATION
pico_get_unique_board_id_string((char *) res_APDU, 4);
#endif
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);
#ifndef ENABLE_EMULATION
pico_get_unique_board_id_string((char *) challenge + 6, 10);
#endif
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
static const cmd_t cmds[] = {
{ INS_OTP, cmd_otp },
{ 0x00, 0x0 }
};
int otp_process_apdu() {
if (CLA(apdu) != 0x00) {
return SW_CLA_NOT_SUPPORTED();
}
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[frame_len] = ~crc & 0xff;
frame[frame_len + 1] = ~crc >> 8;
frame_len += 2;
*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 (report_type == 3) {
DEBUG_PAYLOAD(buffer, bufsize);
if (itf == ITF_KEYBOARD && 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 = (otp_frame_rx[66] << 8 | otp_frame_rx[65]);
uint8_t slot_id = otp_frame_rx[64];
if (residual_crc == rcrc) {
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;
int ret = otp_process_apdu();
if (ret == 0x9000 && res_APDU_size > 0) {
otp_send_frame(apdu.rdata, apdu.rlen);
}
}
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 {
otp_status();
memcpy(buffer, res_APDU, 7);
}
return reqlen;
}
#endif

View File

@@ -18,10 +18,9 @@
#ifndef __VERSION_H_
#define __VERSION_H_
#define PICO_FIDO_VERSION 0x0204
#define PICO_FIDO_VERSION 0x0508
#define PICO_FIDO_VERSION_MAJOR ((PICO_FIDO_VERSION >> 8) & 0xff)
#define PICO_FIDO_VERSION_MINOR (PICO_FIDO_VERSION & 0xff)
#endif

7
tests/build-in-docker.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash -eu
source tests/docker_env.sh
#run_in_docker rm -rf CMakeFiles
run_in_docker mkdir -p build_in_docker
run_in_docker -w "$PWD/build_in_docker" cmake -DENABLE_EMULATION=1 ..
run_in_docker -w "$PWD/build_in_docker" make -j ${NUM_PROC}

View File

@@ -1,3 +1,23 @@
"""
/*
* 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/>.
*/
"""
from http import client
from fido2.hid import CtapHidDevice
from fido2.client import Fido2Client, WindowsClient, UserInteraction, ClientError, _Ctap1ClientBackend
@@ -5,13 +25,15 @@ 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
import pytest
import os
import struct
from inputimeout import inputimeout
DEFAULT_PIN='12345678'
@@ -94,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
@@ -159,7 +185,11 @@ class Device():
def reboot(self):
print("Please reboot authenticator and hit enter")
input()
try:
inputimeout(prompt='>>', timeout=5)
except Exception:
pass
self.__set_client(self.__origin, self.__user_interaction, self.__uv)
self.__set_server(rp=self.__rp, attestation=self.__attestation)
@@ -296,7 +326,7 @@ class Device():
def doGA(self, client_data=Ellipsis, rp_id=Ellipsis, allow_list=None, extensions=None, user_verification=None, event=None, ctap1=False, check_only=False):
client_data = client_data if client_data is not Ellipsis else CollectedClientData.create(
type=CollectedClientData.TYPE.CREATE, origin=self.__origin, challenge=os.urandom(32)
type=CollectedClientData.TYPE.GET, origin=self.__origin, challenge=os.urandom(32)
)
rp_id = rp_id if rp_id is not Ellipsis else self.__rp['id']
if (ctap1 is True):
@@ -335,20 +365,20 @@ def device():
dev = Device()
return dev
@pytest.fixture(scope="session")
@pytest.fixture(scope="module")
def info(device):
return device.client()._backend.info
@pytest.fixture(scope="session")
@pytest.fixture(scope="module")
def MCRes(device, *args):
return device.doMC(*args)
@pytest.fixture(scope="session")
@pytest.fixture(scope="module")
def resetdevice(device):
device.reset()
return device
@pytest.fixture(scope="session")
@pytest.fixture(scope="module")
def GARes(device, MCRes, *args):
res = device.doGA(allow_list=[
{"id": MCRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
@@ -359,11 +389,11 @@ def GARes(device, MCRes, *args):
verify(MCRes['res'].attestation_object, a, res['req']['client_data'].hash)
return res
@pytest.fixture(scope="session")
@pytest.fixture(scope="module")
def MCRes_DC(device, *args):
return device.doMC(rk=True, *args)
@pytest.fixture(scope="session")
@pytest.fixture(scope="module")
def GARes_DC(device, MCRes_DC, *args):
res = device.GA(allow_list=[
{"id": MCRes_DC['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
@@ -380,7 +410,7 @@ def RegRes(resetdevice, *args):
return res
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def AuthRes(device, RegRes, *args):
res = device.doGA(ctap1=True, allow_list=[
{"id": RegRes['res'].attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
@@ -393,3 +423,30 @@ def AuthRes(device, RegRes, *args):
@pytest.fixture(scope="class")
def client_pin(resetdevice):
return ClientPin(resetdevice.client()._backend.ctap2)
@pytest.fixture(scope="class")
def ccid_card():
cardtype = AnyCardType()
try:
# request card insertion
cardrequest = CardRequest(timeout=10, cardType=cardtype)
card = cardrequest.waitforcard()
# connect to the card and perform a few transmits
card.connection.connect()
return card
except CardRequestTimeoutException:
print('time-out: no card inserted during last 10s')
return None
@pytest.fixture(scope="class")
def select_oath(ccid_card):
aid = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01, 0x01]
resp = send_apdu(ccid_card, 0xA4, 0x04, 0x00, aid)
return ccid_card
@pytest.fixture(scope="class")
def reset_oath(select_oath):
send_apdu(select_oath, 0x04, p1=0xde, p2=0xad)
return select_oath

View File

@@ -0,0 +1,36 @@
FROM debian:bullseye
ARG DEBIAN_FRONTEND=noninteractive
RUN apt update && apt upgrade -y
RUN apt install -y apt-utils
RUN apt install -y libccid \
libpcsclite-dev \
git \
autoconf \
pkg-config \
libtool \
help2man \
automake \
gcc \
make \
build-essential \
opensc \
python3 \
python3-pip \
swig \
cmake \
libfuse-dev \
&& rm -rf /var/lib/apt/lists/*
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
RUN ./configure --sysconfdir=/etc
RUN make && make install
WORKDIR /

View File

@@ -0,0 +1,270 @@
# Copyright (c) 2020 Yubico AB
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
from __future__ import annotations
from .base import HidDescriptor
from ..ctap import CtapDevice, CtapError, STATUS
from ..utils import LOG_LEVEL_TRAFFIC
from threading import Event
from enum import IntEnum, IntFlag, unique
from typing import Tuple, Optional, Callable, Iterator
import struct
import sys
import os
import logging
logger = logging.getLogger(__name__)
if sys.platform.startswith("linux"):
from . import linux as backend
elif sys.platform.startswith("win32"):
from . import windows as backend
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
@unique
class CTAPHID(IntEnum):
PING = 0x01
MSG = 0x03
LOCK = 0x04
INIT = 0x06
WINK = 0x08
CBOR = 0x10
CANCEL = 0x11
ERROR = 0x3F
KEEPALIVE = 0x3B
VENDOR_FIRST = 0x40
@unique
class CAPABILITY(IntFlag):
WINK = 0x01
LOCK = 0x02 # Not used
CBOR = 0x04
NMSG = 0x08
def supported(self, flags: CAPABILITY) -> bool:
return bool(flags & self)
TYPE_INIT = 0x80
class CtapHidDevice(CtapDevice):
"""
CtapDevice implementation using the HID transport.
:cvar descriptor: Device descriptor.
"""
def __init__(self, descriptor: HidDescriptor, connection):
self.descriptor = descriptor
self._packet_size = descriptor.report_size_out
self._connection = connection
nonce = os.urandom(8)
self._channel_id = 0xFFFFFFFF
response = self.call(CTAPHID.INIT, nonce)
r_nonce, response = response[:8], response[8:]
if r_nonce != nonce:
raise Exception("Wrong nonce")
(
self._channel_id,
self._u2fhid_version,
v1,
v2,
v3,
self._capabilities,
) = struct.unpack_from(">IBBBBB", response)
self._device_version = (v1, v2, v3)
def __repr__(self):
return f"CtapHidDevice({self.descriptor.path!r})"
@property
def version(self) -> int:
"""CTAP HID protocol version."""
return self._u2fhid_version
@property
def device_version(self) -> Tuple[int, int, int]:
"""Device version number."""
return self._device_version
@property
def capabilities(self) -> int:
"""Capabilities supported by the device."""
return self._capabilities
@property
def product_name(self) -> Optional[str]:
"""Product name of device."""
return self.descriptor.product_name
@property
def serial_number(self) -> Optional[str]:
"""Serial number of device."""
return self.descriptor.serial_number
def _send_cancel(self):
packet = struct.pack(">IB", self._channel_id, TYPE_INIT | CTAPHID.CANCEL).ljust(
self._packet_size, b"\0"
)
logger.log(LOG_LEVEL_TRAFFIC, "SEND: %s", packet.hex())
self._connection.write_packet(packet)
def call(
self,
cmd: int,
data: bytes = b"",
event: Optional[Event] = None,
on_keepalive: Optional[Callable[[int], None]] = None,
) -> bytes:
event = event or Event()
remaining = data
seq = 0
# Send request
header = struct.pack(">IBH", self._channel_id, TYPE_INIT | cmd, len(remaining))
while remaining or seq == 0:
size = min(len(remaining), self._packet_size - len(header))
body, remaining = remaining[:size], remaining[size:]
packet = header + body
logger.log(LOG_LEVEL_TRAFFIC, "SEND: %s", packet.hex())
self._connection.write_packet(packet.ljust(self._packet_size, b"\0"))
header = struct.pack(">IB", self._channel_id, 0x7F & seq)
seq += 1
try:
# Read response
seq = 0
response = b""
last_ka = None
while True:
if event.is_set():
# Cancel
logger.debug("Sending cancel...")
self._send_cancel()
recv = self._connection.read_packet()
logger.log(LOG_LEVEL_TRAFFIC, "RECV: %s", recv.hex())
r_channel = struct.unpack_from(">I", recv)[0]
recv = recv[4:]
if r_channel != self._channel_id:
raise Exception("Wrong channel")
if not response: # Initialization packet
r_cmd, r_len = struct.unpack_from(">BH", recv)
recv = recv[3:]
if r_cmd == TYPE_INIT | cmd:
pass # first data packet
elif r_cmd == TYPE_INIT | CTAPHID.KEEPALIVE:
ka_status = struct.unpack_from(">B", recv)[0]
logger.debug(f"Got keepalive status: {ka_status:02x}")
if on_keepalive and ka_status != last_ka:
try:
ka_status = STATUS(ka_status)
except ValueError:
pass # Unknown status value
last_ka = ka_status
on_keepalive(ka_status)
continue
elif r_cmd == TYPE_INIT | CTAPHID.ERROR:
raise CtapError(struct.unpack_from(">B", recv)[0])
else:
raise CtapError(CtapError.ERR.INVALID_COMMAND)
else: # Continuation packet
r_seq = struct.unpack_from(">B", recv)[0]
recv = recv[1:]
if r_seq != seq:
raise Exception("Wrong sequence number")
seq += 1
response += recv
if len(response) >= r_len:
break
return response[:r_len]
except KeyboardInterrupt:
logger.debug("Keyboard interrupt, cancelling...")
self._send_cancel()
raise
def wink(self) -> None:
"""Causes the authenticator to blink."""
self.call(CTAPHID.WINK)
def ping(self, msg: bytes = b"Hello FIDO") -> bytes:
"""Sends data to the authenticator, which echoes it back.
:param msg: The data to send.
:return: The response from the authenticator.
"""
return self.call(CTAPHID.PING, msg)
def lock(self, lock_time: int = 10) -> None:
"""Locks the channel."""
self.call(CTAPHID.LOCK, struct.pack(">B", lock_time))
def close(self) -> None:
if self._connection:
self._connection.close()
self._connection = None
@classmethod
def list_devices(cls) -> Iterator[CtapHidDevice]:
for d in list_descriptors():
yield cls(d, open_connection(d))
def list_devices() -> Iterator[CtapHidDevice]:
return CtapHidDevice.list_devices()
def open_device(path) -> CtapHidDevice:
descriptor = get_descriptor(path)
return CtapHidDevice(descriptor, open_connection(descriptor))

View File

@@ -0,0 +1,79 @@
# Original work Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Modified work Copyright 2020 Yubico AB. All Rights Reserved.
# This file, with modifications, is licensed under the above Apache License.
from __future__ import annotations
from .base import HidDescriptor, CtapHidConnection
import socket
from typing import Set
import logging
import sys
HOST = '127.0.0.1'
PORT = 35962
# Don't typecheck this file on Windows
assert sys.platform != "win32" # nosec
logger = logging.getLogger(__name__)
class EmulationCtapHidConnection(CtapHidConnection):
def __init__(self, descriptor):
self.descriptor = descriptor
self.handle = descriptor.path
self.handle.connect((HOST, PORT))
def write_packet(self, packet):
if (self.handle.send(len(packet).to_bytes(2, 'big')) != 2):
raise OSError("write_packet sending size failed")
if (self.handle.send(packet) != len(packet)):
raise OSError("write_packet sending packet failed")
def read_packet(self):
bts = self.handle.recv(2)
if (len(bts) != 2):
raise OSError("read_packet failed reading size")
size = int.from_bytes(bts, 'big')
data = self.handle.recv(size)
if (len(data) != size):
raise OSError("read_packet failed reading packet")
return data
def close(self) -> None:
return self.handle.close()
def open_connection(descriptor):
return EmulationCtapHidConnection(descriptor)
def get_descriptor(_):
HOST = 'localhost' # The remote host
PORT = 35962 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
return HidDescriptor(s, 0x00, 0x00, 64, 64, "Pico-Fido", "AAAAAA")
def list_descriptors():
devices = []
try:
devices.append(get_descriptor(None))
except ValueError:
pass # Not a CTAP device, ignore.
return devices

107
tests/docker_env.sh Normal file
View File

@@ -0,0 +1,107 @@
#!/bin/bash -eu
# Taken from Mbed-TLS project
# https://github.com/Mbed-TLS/mbedtls/blob/master/tests/scripts/docker_env.sh
#
# docker_env.sh
#
# Purpose
# -------
#
# This is a helper script to enable running tests under a Docker container,
# thus making it easier to get set up as well as isolating test dependencies
# (which include legacy/insecure configurations of openssl and gnutls).
#
# WARNING: the Dockerfile used by this script is no longer maintained! See
# https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start
# for the set of Docker images we use on the CI.
#
# Notes for users
# ---------------
# This script expects a Linux x86_64 system with a recent version of Docker
# installed and available for use, as well as http/https access. If a proxy
# server must be used, invoke this script with the usual environment variables
# (http_proxy and https_proxy) set appropriately. If an alternate Docker
# registry is needed, specify MBEDTLS_DOCKER_REGISTRY to point at the
# host name.
#
#
# Running this script directly will check for Docker availability and set up
# the Docker image.
# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# default values, can be overridden by the environment
: ${MBEDTLS_DOCKER_GUEST:=bullseye}
DOCKER_IMAGE_TAG="pico-hsm-test:${MBEDTLS_DOCKER_GUEST}"
# Make sure docker is available
if ! which docker > /dev/null; then
echo "Docker is required but doesn't seem to be installed. See https://www.docker.com/ to get started"
exit 1
fi
# Figure out if we need to 'sudo docker'
if groups | grep docker > /dev/null; then
DOCKER="docker"
else
echo "Using sudo to invoke docker since you're not a member of the docker group..."
DOCKER="docker"
fi
# Figure out the number of processors available
if [ "$(uname)" == "Darwin" ]; then
NUM_PROC="$(sysctl -n hw.logicalcpu)"
else
NUM_PROC="$(nproc)"
fi
# Build the Docker image
echo "Getting docker image up to date (this may take a few minutes)..."
${DOCKER} image build \
-t ${DOCKER_IMAGE_TAG} \
--cache-from=${DOCKER_IMAGE_TAG} \
--network host \
--build-arg MAKEFLAGS_PARALLEL="-j ${NUM_PROC}" \
tests/docker/${MBEDTLS_DOCKER_GUEST}
run_in_docker()
{
ENV_ARGS=""
while [ "$1" == "-e" ]; do
ENV_ARGS="${ENV_ARGS} $1 $2"
shift 2
done
WORKDIR="${PWD}"
if [ "$1" == '-w' ]; then
WORKDIR="$2"
shift 2
fi
${DOCKER} container run --rm \
--cap-add ALL \
--privileged \
--volume $PWD:$PWD \
--workdir ${WORKDIR} \
-e MAKEFLAGS \
${ENV_ARGS} \
${DOCKER_IMAGE_TAG} \
$@
}

View File

@@ -0,0 +1,47 @@
"""
/*
* 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/>.
*/
"""
import pytest
from fido2.client import CtapError
def test_getinfo(device):
pass
def test_get_info_version(info):
assert "FIDO_2_0" in info.versions
def test_Check_pin_protocols_field(info):
if len(info.pin_uv_protocols):
assert sum(info.pin_uv_protocols) > 0
def test_Check_options_field(info):
for x in info.options:
assert info.options[x] in [True, False]
def test_Check_up_option(device, info):
if "up" not in info.options or info.options["up"]:
with pytest.raises(CtapError) as e:
device.MC(options={"up": True})
assert e.value.code == CtapError.ERR.INVALID_OPTION

View File

@@ -1,3 +1,23 @@
"""
/*
* 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/>.
*/
"""
import os
import pytest
from fido2.ctap import CtapError
@@ -14,9 +34,9 @@ def test_lockout(device, resetdevice, client_pin):
pin_token = client_pin.get_pin_token(pin)
for i in range(1, 10):
err = CtapError.ERR.PIN_INVALID
if i in (3, 6):
err = CtapError.ERR.PIN_AUTH_BLOCKED
err = [CtapError.ERR.PIN_INVALID]
if 3 <= i <= 7:
err = [CtapError.ERR.PIN_AUTH_BLOCKED]
elif i >= 8:
err = [CtapError.ERR.PIN_BLOCKED, CtapError.ERR.PIN_INVALID]

View File

@@ -1,5 +1,26 @@
"""
/*
* 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/>.
*/
"""
from fido2.client import CtapError
from fido2.cose import ES256
from fido2.cose import ES256, ES384, ES512, EdDSA
from utils import ES256K
import pytest
@@ -11,7 +32,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
@@ -100,18 +121,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, EdDSA.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,
]
@@ -123,7 +151,7 @@ def test_unsupported_algorithm(device):
with pytest.raises(CtapError) as e:
device.doMC(key_params=[{"alg": 1337, "type": "public-key"}])
assert e.value.code == CtapError.ERR.UNSUPPORTED_ALGORITHM
assert e.value.code == CtapError.ERR.CBOR_UNEXPECTED_TYPE
def test_exclude_list(resetdevice):
resetdevice.doMC(exclude_list=[{"id": b"1234", "type": "rot13"}])
@@ -160,5 +188,6 @@ def test_exclude_list_excluded(device):
assert e.value.code == CtapError.ERR.CREDENTIAL_EXCLUDED
def test_unknown_option(resetdevice):
resetdevice.MC(options={"unknown": False})
def test_unknown_option(device):
device.reset()
device.MC(options={"unknown": False})

View File

@@ -1,5 +1,26 @@
from fido2.utils import sha256
"""
/*
* 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/>.
*/
"""
from fido2.client import CtapError
from fido2.cose import ES256, ES384, ES512, EdDSA
from utils import verify, ES256K
import pytest
def test_authenticate(device):
@@ -27,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, EdDSA.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 = []
@@ -76,7 +108,7 @@ def test_get_assertion_allow_list_filtering_and_buffering(device):
len(rp2_assertions)
)
assert counts in [(None, None), (l1, l2)]
assert counts in [(1, 1), (l1, l2)]
def test_corrupt_credId(device, MCRes):
# apply bit flip
@@ -104,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"}})

View File

@@ -1,3 +1,23 @@
"""
/*
* 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/>.
*/
"""
from fido2.client import CtapError
import pytest
import random

View File

@@ -0,0 +1,150 @@
"""
/*
* 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/>.
*/
"""
import pytest
from fido2.ctap import CtapError
from fido2.ctap2.pin import PinProtocolV2, ClientPin
from utils import verify
import os
PIN='12345678'
SMALL_BLOB=b"A"*32
LARGE_BLOB=b"B"*1024
@pytest.fixture(scope="function")
def MCCredBlob(device):
res = device.doMC(extensions={'credBlob': SMALL_BLOB})['res'].attestation_object
return res
@pytest.fixture(scope="function")
def GACredBlob(device, MCCredBlob):
res = device.doGA(allow_list=[
{"id": MCCredBlob.auth_data.credential_data.credential_id, "type": "public-key"}
], extensions={'getCredBlob': True})
assertions = res['res'].get_assertions()
for a in assertions:
verify(MCCredBlob, a, res['req']['client_data'].hash)
return assertions[0]
@pytest.fixture(scope="function")
def MCLBK(device):
res = device.doMC(
rk=True,
extensions={'largeBlob':{'support':'required'}}
)['res']
return res
@pytest.fixture(scope="function")
def GALBRead(device, MCLBK):
res = device.doGA(
allow_list=[
{"id": MCLBK.attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
],extensions={'largeBlob':{'read': True}}
)
assertions = res['res'].get_assertions()
for a in assertions:
verify(MCLBK.attestation_object, a, res['req']['client_data'].hash)
return res['res']
@pytest.fixture(scope="function")
def GALBReadLBK(GALBRead):
return GALBRead.get_assertions()[0]
@pytest.fixture(scope="function")
def GALBReadLB(GALBRead):
return GALBRead.get_response(0)
@pytest.fixture(scope="function")
def GALBWrite(device, MCLBK):
res = device.doGA(
allow_list=[
{"id": MCLBK.attestation_object.auth_data.credential_data.credential_id, "type": "public-key"}
],extensions={'largeBlob':{'write': LARGE_BLOB}}
)
assertions = res['res'].get_assertions()
for a in assertions:
verify(MCLBK.attestation_object, a, res['req']['client_data'].hash)
return res['res'].get_response(0)
def test_supports_credblob(info):
assert info.extensions
assert 'credBlob' in info.extensions
assert info.max_cred_blob_length
assert info.max_cred_blob_length > 0
def test_mc_credblob(MCCredBlob):
assert MCCredBlob.auth_data.extensions
assert "credBlob" in MCCredBlob.auth_data.extensions
assert MCCredBlob.auth_data.extensions['credBlob'] is True
def test_ga_credblob(GACredBlob):
assert GACredBlob.auth_data.extensions
assert "credBlob" in GACredBlob.auth_data.extensions
assert GACredBlob.auth_data.extensions['credBlob'] == SMALL_BLOB
def test_wrong_credblob(device, info):
device.reset()
cdh = os.urandom(32)
ClientPin(device.client()._backend.ctap2).set_pin(PIN)
pin_token = ClientPin(device.client()._backend.ctap2).get_pin_token(PIN, permissions=ClientPin.PERMISSION.MAKE_CREDENTIAL | ClientPin.PERMISSION.AUTHENTICATOR_CFG)
protocol = PinProtocolV2()
MC = device.MC(
client_data_hash=cdh,
extensions={'credBlob': b'A'*(info.max_cred_blob_length+1)},
pin_uv_protocol=protocol.VERSION,
pin_uv_param=protocol.authenticate(pin_token, cdh)
)['res']
assert MC.auth_data.extensions
assert "credBlob" in MC.auth_data.extensions
assert MC.auth_data.extensions['credBlob'] is False
res = device.doGA(allow_list=[
{"id": MC.auth_data.credential_data.credential_id, "type": "public-key"}
], extensions={'getCredBlob': True})
assertions = res['res'].get_assertions()
for a in assertions:
verify(MC, a, res['req']['client_data'].hash)
assert assertions[0].auth_data.extensions
assert "credBlob" in assertions[0].auth_data.extensions
assert len(assertions[0].auth_data.extensions['credBlob']) == 0
def test_supports_largeblobs(info):
assert info.extensions
assert 'largeBlobKey' in info.extensions
assert 'largeBlobs' in info.options
assert info.max_large_blob is None or (info.max_large_blob > 1024)
def test_get_largeblobkey_mc(MCLBK):
assert 'supported' in MCLBK.extension_results
assert MCLBK.extension_results['supported'] is True
def test_get_largeblobkey_ga(GALBReadLBK):
assert GALBReadLBK.large_blob_key is not None
def test_get_largeblob_rw(GALBWrite, GALBReadLB):
assert 'written' in GALBWrite.extension_results
assert GALBWrite.extension_results['written'] is True
assert 'blob' in GALBReadLB.extension_results
assert GALBReadLB.extension_results['blob'] == LARGE_BLOB

View File

@@ -1,3 +1,23 @@
"""
/*
* 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/>.
*/
"""
import pytest
from fido2.ctap2.extensions import CredProtectExtension
from fido2.webauthn import UserVerificationRequirement
@@ -106,10 +126,7 @@ def test_credprotect_optional_and_list_works_no_uv(device, MCCredProtectOptional
res1 = device.doGA(allow_list=allow_list)['res'].get_assertions()[0]
assert res1.number_of_credentials in (None, 2)
results = [res1]
if res1.number_of_credentials == 2:
res2 = device.GNA()['res']
results.append(res2)
results = device.doGA(allow_list=allow_list)['res'].get_assertions()
# the required credProtect is not returned.
for res in results:

View File

@@ -1,3 +1,23 @@
"""
/*
* 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/>.
*/
"""
import pytest
from fido2.ctap import CtapError
from fido2.ctap2.extensions import HmacSecretExtension

View File

@@ -0,0 +1,101 @@
"""
/*
* 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/>.
*/
"""
import pytest
from fido2.ctap2.extensions import CredProtectExtension
from fido2.webauthn import UserVerificationRequirement
from fido2.ctap import CtapError
from fido2.ctap2.pin import PinProtocolV2, ClientPin
from fido2.ctap2 import Config
PIN='12345678'
MINPINLENGTH=6
@pytest.fixture(scope="function")
def MCMinPin(device):
res = device.doMC(rk=True, extensions={'minPinLength': True})['res'].attestation_object
return res
@pytest.fixture(scope="function")
def SetMinPin(device):
device.reset()
ClientPin(device.client()._backend.ctap2).set_pin(PIN)
cfg = FidoConfig(device)
cfg.set_min_pin_length(MINPINLENGTH,rp_ids=['example.com'])
@pytest.fixture(scope="function")
def SetMinPinWrongRpid(device):
device.reset()
ClientPin(device.client()._backend.ctap2).set_pin(PIN)
cfg = FidoConfig(device)
cfg.set_min_pin_length(MINPINLENGTH,rp_ids=['notanexample.com'])
def PinToken(device):
return ClientPin(device.client()._backend.ctap2).get_pin_token(PIN, permissions=ClientPin.PERMISSION.MAKE_CREDENTIAL | ClientPin.PERMISSION.AUTHENTICATOR_CFG)
def FidoConfig(device):
pt = PinToken(device)
pin_protocol = PinProtocolV2()
return Config(device.client()._backend.ctap2, pin_protocol, pt)
def test_supports_minpin(info):
assert info.extensions
assert 'minPinLength' in info.extensions
assert info.options
assert 'setMinPINLength' in info.options
assert info.options['setMinPINLength'] is True
def test_minpin(SetMinPin, MCMinPin):
assert MCMinPin.auth_data.extensions
assert "minPinLength" in MCMinPin.auth_data.extensions
assert MCMinPin.auth_data.extensions['minPinLength'] == MINPINLENGTH
def test_minpin_bad_rpid(SetMinPinWrongRpid, MCMinPin):
assert not MCMinPin.auth_data.extensions
assert "minPinLength" not in MCMinPin.auth_data.extensions
def test_setminpin(device, SetMinPin, MCMinPin):
cfg = FidoConfig(device)
cfg.set_min_pin_length(MINPINLENGTH+2,rp_ids=['example.com'])
res = device.doMC(rk=True, extensions={'minPinLength': True})['res'].attestation_object
assert res.auth_data.extensions
assert "minPinLength" in res.auth_data.extensions
assert res.auth_data.extensions['minPinLength'] == MINPINLENGTH+2
def test_no_setminpin(device, SetMinPin, MCMinPin):
cfg = FidoConfig(device)
with pytest.raises(CtapError) as e:
cfg.set_min_pin_length(MINPINLENGTH-2,rp_ids=['example.com'])
assert e.value.code == CtapError.ERR.PIN_POLICY_VIOLATION
def test_setminpin_check_force(device, SetMinPin, MCMinPin):
cfg = FidoConfig(device)
cfg.set_min_pin_length(len(PIN)+1,rp_ids=['example.com'])
info = device.client()._backend.ctap2.get_info()
assert info.force_pin_change == True
@pytest.mark.parametrize(
"force", [True, False]
)
def test_setminpin_set_forcee(device, SetMinPin, MCMinPin, force):
cfg = FidoConfig(device)
cfg.set_min_pin_length(MINPINLENGTH,rp_ids=['example.com'],force_change_pin=force)
info = device.client()._backend.ctap2.get_info()
assert info.force_pin_change == force

View File

@@ -1,78 +1,100 @@
"""
/*
* 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/>.
*/
"""
import pytest
import time
import random
from fido2.ctap import CtapError
from fido2.ctap2 import CredentialManagement
from fido2.utils import sha256, hmac_sha256
from fido2.ctap2.pin import PinProtocolV2
from fido2.utils import sha256
from fido2.ctap2.pin import PinProtocolV2, ClientPin
from binascii import hexlify
from utils import generate_random_user
PIN = "12345678"
@pytest.fixture(params=[PIN], scope = 'function')
def PinToken(request, device, client_pin):
def client_pin_set(request, device, client_pin):
#device.reboot()
device.reset()
pin = request.param
client_pin.set_pin(pin)
return client_pin.get_pin_token(pin)
@pytest.fixture(scope = 'function')
def MC_RK_Res(device, PinToken):
def MC_RK_Res(device, client_pin_set):
rp = {"id": "ssh:", "name": "Bate Goiko"}
device.doMC(rp=rp, rk=True)
rp = {"id": "xakcop.com", "name": "John Doe"}
device.doMC(rp=rp, rk=True)
def PinToken(device):
return ClientPin(device.client()._backend.ctap2).get_pin_token(PIN, permissions=ClientPin.PERMISSION.MAKE_CREDENTIAL | ClientPin.PERMISSION.CREDENTIAL_MGMT)
@pytest.fixture(scope = 'function')
def CredMgmt(device, PinToken):
def CredMgmt(device):
pt = PinToken(device)
pin_protocol = PinProtocolV2()
return CredentialManagement(device.client()._backend.ctap2, pin_protocol, PinToken)
return CredentialManagement(device.client()._backend.ctap2, pin_protocol, pt)
def _test_enumeration(CredMgmt, rp_map):
def _test_enumeration(device, rp_map):
"Enumerate credentials using BFS"
res = CredMgmt.enumerate_rps()
credMgmt = CredMgmt(device)
res = credMgmt.enumerate_rps()
assert len(rp_map.keys()) == len(res)
for rp in res:
creds = CredMgmt.enumerate_creds(sha256(rp[3]["id"]))
assert len(creds) == rp_map[rp[3]["id"].decode()]
creds = credMgmt.enumerate_creds(sha256(rp[3]["id"].encode()))
assert len(creds) == rp_map[rp[3]["id"]]
def _test_enumeration_interleaved(CredMgmt, rp_map):
def _test_enumeration_interleaved(device, rp_map):
"Enumerate credentials using DFS"
first_rp = CredMgmt.enumerate_rps_begin()
credMgmt = CredMgmt(device)
first_rp = credMgmt.enumerate_rps_begin()
assert len(rp_map.keys()) == first_rp[CredentialManagement.RESULT.TOTAL_RPS]
rk_count = 1
first_rk = CredMgmt.enumerate_creds_begin(sha256(first_rp[3]["id"]))
first_rk = credMgmt.enumerate_creds_begin(sha256(first_rp[3]["id"].encode()))
for i in range(1, first_rk[CredentialManagement.RESULT.TOTAL_CREDENTIALS]):
c = CredMgmt.enumerate_creds_next()
c = credMgmt.enumerate_creds_next()
rk_count += 1
assert rk_count == rp_map[first_rp[3]["id"].decode()]
assert rk_count == rp_map[first_rp[3]["id"]]
for i in range(1, first_rp[CredentialManagement.RESULT.TOTAL_RPS]):
next_rp = CredMgmt.enumerate_rps_next()
next_rp = credMgmt.enumerate_rps_next()
rk_count = 1
first_rk = CredMgmt.enumerate_creds_begin(
sha256(next_rp[3]["id"])
first_rk =credMgmt.enumerate_creds_begin(
sha256(next_rp[3]["id"].encode())
)
for i in range(1, first_rk[CredentialManagement.RESULT.TOTAL_CREDENTIALS]):
c = CredMgmt.enumerate_creds_next()
c = credMgmt.enumerate_creds_next()
rk_count += 1
assert rk_count == rp_map[next_rp[3]["id"].decode()]
assert rk_count == rp_map[next_rp[3]["id"]]
def CredMgmtWrongPinAuth(device, pin_token):
def CredMgmtWrongPinAuth(device):
pin_token = PinToken(device)
pin_protocol = PinProtocolV2()
wrong_pt = bytearray(pin_token)
wrong_pt[0] = (wrong_pt[0] + 1) % 256
@@ -85,7 +107,7 @@ def assert_cred_response_has_all_fields(cred_res):
CredentialManagement.RESULT.CREDENTIAL_ID,
CredentialManagement.RESULT.PUBLIC_KEY,
CredentialManagement.RESULT.TOTAL_CREDENTIALS,
CredentialManagement.RESULT.CRED_PROTECT,
#CredentialManagement.RESULT.CRED_PROTECT,
):
assert i in cred_res
@@ -98,55 +120,58 @@ def test_get_info(info):
assert 0x8 in info
assert info[0x8] > 1
def test_get_metadata(CredMgmt, MC_RK_Res):
metadata = CredMgmt.get_metadata()
def test_get_metadata_ok(MC_RK_Res, device):
metadata = CredMgmt(device).get_metadata()
assert metadata[CredentialManagement.RESULT.EXISTING_CRED_COUNT] == 2
assert metadata[CredentialManagement.RESULT.MAX_REMAINING_COUNT] >= 48
def test_enumerate_rps(CredMgmt, MC_RK_Res):
res = CredMgmt.enumerate_rps()
def test_enumerate_rps(MC_RK_Res, device):
res = CredMgmt(device).enumerate_rps()
assert len(res) == 2
assert res[0][CredentialManagement.RESULT.RP]["id"] == b"ssh:"
assert res[0][CredentialManagement.RESULT.RP]["id"] == "ssh:"
assert res[0][CredentialManagement.RESULT.RP_ID_HASH] == sha256(b"ssh:")
# Solo doesn't store rpId with the exception of "ssh:"
assert res[1][CredentialManagement.RESULT.RP]["id"] == b"xakcop.com"
assert res[1][CredentialManagement.RESULT.RP]["id"] == "xakcop.com"
assert res[1][CredentialManagement.RESULT.RP_ID_HASH] == sha256(b"xakcop.com")
def test_enumarate_creds(CredMgmt, MC_RK_Res):
res = CredMgmt.enumerate_creds(sha256(b"ssh:"))
def test_enumarate_creds(MC_RK_Res, device):
credMgmt = CredMgmt(device)
res = credMgmt.enumerate_creds(sha256(b"ssh:"))
assert len(res) == 1
assert_cred_response_has_all_fields(res[0])
res = CredMgmt.enumerate_creds(sha256(b"xakcop.com"))
res = credMgmt.enumerate_creds(sha256(b"xakcop.com"))
assert len(res) == 1
assert_cred_response_has_all_fields(res[0])
res = CredMgmt.enumerate_creds(sha256(b"missing.com"))
res = credMgmt.enumerate_creds(sha256(b"missing.com"))
assert not res
def test_get_metadata_wrong_pinauth(device, MC_RK_Res, PinToken):
def test_get_metadata_wrong_pinauth(device, MC_RK_Res):
cmd = lambda credMgmt: credMgmt.get_metadata()
_test_wrong_pinauth(device, cmd, PinToken)
_test_wrong_pinauth(device, cmd)
def test_rpbegin_wrong_pinauth(device, MC_RK_Res, PinToken):
def test_rpbegin_wrong_pinauth(device, MC_RK_Res):
cmd = lambda credMgmt: credMgmt.enumerate_rps_begin()
_test_wrong_pinauth(device, cmd, PinToken)
_test_wrong_pinauth(device, cmd)
def test_rkbegin_wrong_pinauth(device, MC_RK_Res, PinToken):
def test_rkbegin_wrong_pinauth(device, MC_RK_Res):
cmd = lambda credMgmt: credMgmt.enumerate_creds_begin(sha256(b"ssh:"))
_test_wrong_pinauth(device, cmd, PinToken)
_test_wrong_pinauth(device, cmd)
def test_rpnext_without_rpbegin(device, CredMgmt, MC_RK_Res):
CredMgmt.enumerate_creds_begin(sha256(b"ssh:"))
def test_rpnext_without_rpbegin(device, MC_RK_Res):
credMgmt = CredMgmt(device)
credMgmt.enumerate_creds_begin(sha256(b"ssh:"))
with pytest.raises(CtapError) as e:
CredMgmt.enumerate_rps_next()
credMgmt.enumerate_rps_next()
assert e.value.code == CtapError.ERR.NOT_ALLOWED
def test_rknext_without_rkbegin(device, CredMgmt, MC_RK_Res):
CredMgmt.enumerate_rps_begin()
def test_rknext_without_rkbegin(device, MC_RK_Res):
credMgmt = CredMgmt(device)
credMgmt.enumerate_rps_begin()
with pytest.raises(CtapError) as e:
CredMgmt.enumerate_creds_next()
credMgmt.enumerate_creds_next()
assert e.value.code == CtapError.ERR.NOT_ALLOWED
def test_delete(device, PinToken, CredMgmt):
def test_delete(device):
# create a new RK
rp = {"id": "example_3.com", "name": "John Doe 2"}
@@ -156,21 +181,22 @@ def test_delete(device, PinToken, CredMgmt):
auth = device.doGA(rp_id=rp['id'])
# get the ID from enumeration
creds = CredMgmt.enumerate_creds(reg.auth_data.rp_id_hash)
credMgmt = CredMgmt(device)
creds = credMgmt.enumerate_creds(reg.auth_data.rp_id_hash)
for cred in creds:
if cred[7]["id"] == reg.auth_data.credential_data.credential_id:
break
# delete it
cred = {"id": cred[7]["id"], "type": "public-key"}
CredMgmt.delete_cred(cred)
credMgmt.delete_cred(cred)
# make sure it doesn't work
with pytest.raises(CtapError) as e:
auth = device.doGA(rp_id=rp['id'])
assert e.value.code == CtapError.ERR.NO_CREDENTIALS
def test_add_delete(device, PinToken, CredMgmt):
def test_add_delete(device):
""" Delete a credential in the 'middle' and ensure other credentials are not affected. """
rp = {"id": "example_4.com", "name": "John Doe 3"}
@@ -182,11 +208,12 @@ def test_add_delete(device, PinToken, CredMgmt):
regs.append(reg)
# Check they all enumerate
res = CredMgmt.enumerate_creds(regs[1].auth_data.rp_id_hash)
credMgmt = CredMgmt(device)
res = credMgmt.enumerate_creds(regs[1].auth_data.rp_id_hash)
assert len(res) == 3
# delete the middle one
creds = CredMgmt.enumerate_creds(reg.auth_data.rp_id_hash)
creds = credMgmt.enumerate_creds(reg.auth_data.rp_id_hash)
for cred in creds:
if cred[7]["id"] == regs[1].auth_data.credential_data.credential_id:
break
@@ -194,16 +221,16 @@ def test_add_delete(device, PinToken, CredMgmt):
assert cred[7]["id"] == regs[1].auth_data.credential_data.credential_id
cred = {"id": cred[7]["id"], "type": "public-key"}
CredMgmt.delete_cred(cred)
credMgmt.delete_cred(cred)
# Check one less enumerates
res = CredMgmt.enumerate_creds(regs[0].auth_data.rp_id_hash)
res = credMgmt.enumerate_creds(regs[0].auth_data.rp_id_hash)
assert len(res) == 2
def test_multiple_creds_per_multiple_rps(
device, PinToken, CredMgmt, MC_RK_Res
device, MC_RK_Res
):
res = CredMgmt.enumerate_rps()
res = CredMgmt(device).enumerate_rps()
assert len(res) == 2
new_rps = [
@@ -217,27 +244,26 @@ def test_multiple_creds_per_multiple_rps(
for i in range(0, 3):
reg = device.doMC(rp=rp, rk=True, user=generate_random_user())
res = CredMgmt.enumerate_rps()
credMgmt = CredMgmt(device)
res = credMgmt.enumerate_rps()
assert len(res) == 5
for rp in res:
if rp[3]["id"][:12] == "new_example_":
creds = CredMgmt.enumerate_creds(sha256(rp[3]["id"].encode("utf8")))
creds = credMgmt.enumerate_creds(sha256(rp[3]["id"].encode("utf8")))
assert len(creds) == 3
@pytest.mark.parametrize(
"enumeration_test", [_test_enumeration, _test_enumeration_interleaved]
)
def test_multiple_enumeration(
device, PinToken, MC_RK_Res, CredMgmt, enumeration_test
device, MC_RK_Res, enumeration_test
):
""" Test enumerate still works after different commands """
res = CredMgmt.enumerate_rps()
expected_enumeration = {"xakcop.com": 1, "ssh:": 1}
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
new_rps = [
{"id": "example-2.com", "name": "Example-2-creds", "count": 2},
@@ -253,27 +279,19 @@ def test_multiple_enumeration(
# Now expect creds from this RP
expected_enumeration[rp["id"]] = rp["count"]
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(CredMgmt, expected_enumeration)
metadata = CredMgmt.get_metadata()
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
@pytest.mark.parametrize(
"enumeration_test", [_test_enumeration, _test_enumeration_interleaved]
)
def test_multiple_enumeration_with_deletions(
device, PinToken, MC_RK_Res, CredMgmt, enumeration_test
device, MC_RK_Res, enumeration_test
):
""" Create each credential in random order. Test enumerate still works after randomly deleting each credential"""
res = CredMgmt.enumerate_rps()
expected_enumeration = {"xakcop.com": 1, "ssh:": 1}
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
new_rps = [
{"id": "example-1.com", "name": "Example-1-creds"},
@@ -294,7 +312,7 @@ def test_multiple_enumeration_with_deletions(
else:
expected_enumeration[rp["id"]] += 1
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
total_creds = len(new_rps)
@@ -304,10 +322,11 @@ def test_multiple_enumeration_with_deletions(
num = expected_enumeration[rp]
index = 0 if num == 1 else random.randint(0, num - 1)
cred = CredMgmt.enumerate_creds(sha256(rp.encode("utf8")))[index]
credMgmt = CredMgmt(device)
cred = credMgmt.enumerate_creds(sha256(rp.encode("utf8")))[index]
# print('Delete %d index (%d total) cred of %s' % (index, expected_enumeration[rp], rp))
CredMgmt.delete_cred({"id": cred[7]["id"], "type": "public-key"})
credMgmt.delete_cred({"id": cred[7]["id"], "type": "public-key"})
expected_enumeration[rp] -= 1
if expected_enumeration[rp] == 0:
@@ -316,43 +335,13 @@ def test_multiple_enumeration_with_deletions(
if len(list(expected_enumeration.keys())) == 0:
break
enumeration_test(CredMgmt, expected_enumeration)
enumeration_test(device, expected_enumeration)
def _test_wrong_pinauth(device, cmd, PinToken):
def _test_wrong_pinauth(device, cmd):
credMgmt = CredMgmtWrongPinAuth(device, PinToken)
credMgmt = CredMgmtWrongPinAuth(device)
for i in range(2):
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_INVALID
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_BLOCKED
#device.reboot()
credMgmt = CredMgmtWrongPinAuth(device, PinToken)
for i in range(2):
time.sleep(0.2)
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_INVALID
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_BLOCKED
#device.reboot()
credMgmt = CredMgmtWrongPinAuth(device, PinToken)
for i in range(1):
time.sleep(0.2)
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_AUTH_INVALID
with pytest.raises(CtapError) as e:
cmd(credMgmt)
assert e.value.code == CtapError.ERR.PIN_BLOCKED

View File

@@ -1,3 +1,23 @@
"""
/*
* 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/>.
*/
"""
# Test U2F register works with FIDO2 auth
def test_ctap1_register(RegRes):
pass

View File

@@ -1,3 +1,23 @@
"""
/*
* 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/>.
*/
"""
import pytest
import os
from fido2.ctap1 import APDU, ApduError, Ctap1

View File

@@ -1,3 +1,23 @@
"""
/*
* 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/>.
*/
"""
import os
import socket
import time
@@ -154,22 +174,17 @@ class TestHID(object):
def test_check_busy(self, device):
t1 = time.time() * 1000
device.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
oldcid = device.cid()
#oldcid = device.cid().to_bytes(4, 'big')
newcid = b"\x11\x22\x33\x44"
device.send_raw("\x81\x04\x00")
device.set_cid(newcid)
device.send_raw("\x81\x04\x00")
cmd, r = device.recv_raw() # busy response
t2 = time.time() * 1000
assert t2 - t1 < 100
#t2 = time.time() * 1000
#assert t2 - t1 < 100
assert cmd == 0xBF
assert r[0] == CtapError.ERR.CHANNEL_BUSY
device.set_cid(oldcid)
cmd, r = device.recv_raw() # timeout response
assert cmd == 0xBF
assert r[0] == CtapError.ERR.TIMEOUT
def test_check_busy_interleaved(self, device):
cid1 = b"\x11\x22\x33\x44"
cid2 = b"\x01\x22\x33\x44"
@@ -200,25 +215,26 @@ class TestHID(object):
assert len(r) == 0x39
def test_cid_0(self, device):
device.set_cid("\x00\x00\x00\x00")
device.reset()
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"
)
cmd, r = device.recv_raw() # timeout
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_CHANNEL
device.set_cid("\x05\x04\x03\x02")
device.set_cid(b"\x05\x04\x03\x02")
def test_cid_ffffffff(self, device):
device.set_cid("\xff\xff\xff\xff")
device.set_cid(b"\xff\xff\xff\xff")
device.send_raw(
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
)
cmd, r = device.recv_raw() # timeout
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_CHANNEL
device.set_cid("\x05\x04\x03\x02")
device.set_cid(b"\x05\x04\x03\x02")
def test_keep_alive(self, device, check_timeouts=False):

View File

@@ -0,0 +1,258 @@
"""
/*
* 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/>.
*/
"""
import pytest
from utils import *
import hmac, hashlib
INS_PUT = 0x01
INS_DELETE = 0x02
INS_SET_CODE = 0x03
INS_RESET = 0x04
INS_LIST = 0xa1
INS_CALCULATE = 0xa2
INS_VALIDATE = 0xa3
INS_CALC_ALL = 0xa4
INS_SEND_REMAINING = 0xa5
RESP_MORE_DATA = 0x61
TAG_NAME = 0x71
TAG_NAME_LIST = 0x72
TAG_KEY = 0x73
TAG_CHALLENGE = 0x74
TAG_RESPONSE = 0x75
TAG_T_RESPONSE = 0x76
TAG_NO_RESPONSE = 0x77
TAG_PROPERTY = 0x78
TAG_VERSION = 0x79
TAG_IMF = 0x7a
TAG_ALGO = 0x7b
TAG_TOUCH_RESPONSE = 0x7c
TYPE_MASK = 0xf0
TYPE_HOTP = 0x10
TYPE_TOTP = 0x20
ALG_MASK = 0x0f
ALG_SHA1 = 0x01
ALG_SHA256 = 0x02
PROP_ALWAYS_INC = 0x01
PROP_REQUIRE_TOUCH = 0x02
## Based on tests on https://github.com/Yubico/ykneo-oath/blob/master/test/test/pkgYkneoOathTest/YkneoOathTest.java
def test_select_oath(select_oath):
pass
def list_apdu(ccid_card):
resp = send_apdu(ccid_card, INS_LIST, p1=0, p2=0)
return resp
name_kaka = [ord('k'), ord('a'), ord('k'), ord('a')]
data_name = [TAG_NAME] + [len(name_kaka)] + name_kaka
data_key = [TAG_KEY, 0x16, 0x21, 0x06, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b]
data_chal = [TAG_CHALLENGE, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]
def test_life(reset_oath):
data = data_name + data_key
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=list(data))
assert(len(resp) == 0)
resp = list_apdu(reset_oath)
exp = [TAG_NAME_LIST, 5, 0x21] + name_kaka
assert(resp == exp)
data = data_name + data_chal
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=data)
exp = [TAG_RESPONSE, 0x15, 0x06, 0xb3, 0x99, 0xbd, 0xfc, 0x9d, 0x05, 0xd1, 0x2a, 0xc4, 0x35, 0xc4, 0xc8, 0xd6, 0xcb, 0xd2, 0x47, 0xc4, 0x0a, 0x30, 0xf1]
assert(resp == exp)
data = data_name
resp = send_apdu(reset_oath, INS_DELETE, p1=0, p2=0, data=data)
resp = list_apdu(reset_oath)
assert(len(resp) == 0)
def test_overwrite(reset_oath):
data = data_name + data_key
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=list(data))
assert(len(resp) == 0)
resp = list_apdu(reset_oath)
exp = [TAG_NAME_LIST, 5, 0x21] + name_kaka
assert(resp == exp)
data = data_name + [TAG_CHALLENGE, 0x8] + list(bytes(b'\xff'*8))
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=data)
exp = [TAG_RESPONSE, 0x15, 0x06, 0x79, 0x3e, 0x1b, 0xbd, 0xbf, 0xa7, 0x75, 0xa8, 0x63,0xcc, 0x80, 0x02, 0xce, 0xe4, 0xbd, 0x6c, 0xd7, 0xce, 0xb8, 0xcd]
assert(resp == exp)
resp = list_apdu(reset_oath)
exp = [TAG_NAME_LIST, 5, 0x21] + name_kaka
assert(resp == exp)
data = data_name + [TAG_CHALLENGE, 0x8] + list(bytes(b'\xff\x00'*4))
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=data)
exp = [TAG_RESPONSE, 0x15, 0x06, 0x3b, 0x0e, 0x3c, 0x63, 0x1c, 0x01, 0x67, 0xb0, 0x93, 0xa5, 0xec, 0xb9, 0x09, 0x7d, 0x0b, 0x8e, 0x9a, 0xcc, 0x2f, 0x7f]
assert(resp == exp)
def test_auth(reset_oath):
key = list(bytes(b'kaka blahonga'))
chal = [1,2,3,4,5,6,7,8]
resp = [0x0c, 0x42, 0x8e, 0x9c, 0xba, 0xa3, 0xb3, 0xab, 0x18, 0x53, 0xd8, 0x79, 0xb9, 0xd2, 0x26, 0xf7, 0xce, 0xcc, 0x4a, 0x7a]
data = [TAG_KEY, len(key)+1, ALG_SHA1 | TYPE_TOTP] + key + [TAG_CHALLENGE, len(chal)] + chal + [TAG_RESPONSE, len(resp)] + resp
resp = send_apdu(reset_oath, INS_SET_CODE, p1=0, p2=0, data=data)
reset_oath.connection.reconnect()
with pytest.raises(APDUResponse) as e:
resp = list_apdu(reset_oath)
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
aid = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x21, 0x01, 0x01]
resp = send_apdu(reset_oath, 0xA4, 0x04, 0x00, aid)
assert(resp[15] == TAG_CHALLENGE)
assert(resp[16] == 8)
resp2 = hmac.digest(bytes(key), bytes(resp[17:17+8]), 'sha1')
data = [TAG_RESPONSE, len(resp2)] + list(resp2) + [TAG_CHALLENGE, len(chal)] + chal
resp = send_apdu(reset_oath, INS_VALIDATE, p1=0, p2=0, data=data)
exp = [TAG_RESPONSE, 20] + list(hmac.digest(bytes(key), bytes(chal), 'sha1'))
assert(exp == resp)
resp = list_apdu(reset_oath)
def test_bothoath(reset_oath):
digits = 6
tname = list(bytes(b'totp'))
data = [TAG_NAME, len(tname)] + tname + [TAG_KEY, 9, TYPE_TOTP | ALG_SHA1, digits] + list(bytes(b'foo bar'))
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
data[2] = ord('h')
data[8] = TYPE_HOTP | ALG_SHA1
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
hname = tname[:]
hname[0] = ord('h')
data = [TAG_CHALLENGE, 8, 0, 0, 0, 0, 0x02, 0xbc, 0xad, 0xc8]
resp = send_apdu(reset_oath, INS_CALC_ALL, p1=0, p2=1, data=data)
exp = [TAG_NAME, len(tname)] + tname + [TAG_T_RESPONSE, 5, digits, 0x3d, 0xc6, 0xbf, 0x3d] + [TAG_NAME, len(hname)] + hname + [TAG_NO_RESPONSE, 0x01, digits]
assert(exp == resp)
data = [TAG_NAME, len(hname)] + hname + [TAG_CHALLENGE]
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
exp = [TAG_T_RESPONSE, 5, digits, 0x17, 0xfa, 0x2d, 0x40]
assert(resp == exp)
def test_imf_overwrite(reset_oath):
key = list(bytes(b'kaka'))
imf = [0xff, 0x00, 0xff, 0xff]
name = list(bytes(b'kaka'))
data = [TAG_NAME, len(name)] + name + [TAG_KEY, len(key)+2, ALG_SHA1 | TYPE_HOTP, 6] + key + [TAG_IMF, len(imf)] + imf
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
data = [TAG_NAME, len(name)] + name + [TAG_CHALLENGE]
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
exp = [TAG_T_RESPONSE, 5, 6, 0x45, 0xd9, 0x0f, 0x25]
assert(exp == resp)
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
exp = [TAG_T_RESPONSE, 5, 6, 0x1b, 0xc5, 0x4a, 0x85]
assert(exp == resp)
data = [TAG_NAME, len(name)] + name + [TAG_KEY, len(key)+2, ALG_SHA1 | TYPE_HOTP, 6] + key
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
data = [TAG_NAME, len(name)] + name + [TAG_CHALLENGE]
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
exp = [TAG_T_RESPONSE, 5, 6, 0x16, 0x53, 0x24, 0xdb]
assert(exp == resp)
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
exp = [TAG_T_RESPONSE, 5, 6, 0x53, 0xed, 0x5e, 0xb2]
assert(exp == resp)
def test_imf_more(reset_oath):
key = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30]
imf = [0, 0, 0, 1]
name = list(bytes(b'kaka'))
data = [TAG_NAME, len(name)] + name + [TAG_KEY, len(key)+2, ALG_SHA1 | TYPE_HOTP, 6] + key + [TAG_IMF, len(imf)] + imf
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
data = [TAG_NAME, len(name)] + name + [TAG_CHALLENGE]
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=1, data=data)
exp = [TAG_T_RESPONSE, 5, 6, 0x41, 0x39, 0x7e, 0xea]
assert(exp == resp)
def test_delete(reset_oath):
key = list(bytes(b'blahonga!'))
firstname = list(bytes(b'one'))
secondname = list(bytes(b'two'))
thirdname = list(bytes(b'three'))
type = ALG_SHA1 | TYPE_TOTP
data = [TAG_NAME, len(firstname)] + firstname + [TAG_KEY, len(key)+2, type, 6] + key
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
data = [TAG_NAME, len(secondname)] + secondname + [TAG_KEY, len(key)+2, type, 6] + key
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
resp = list_apdu(reset_oath)
exp = [TAG_NAME_LIST, len(firstname)+1, type] + firstname + [TAG_NAME_LIST, len(secondname)+1, type] + secondname
assert(exp == resp)
data = [TAG_NAME, len(firstname)] + firstname
resp = send_apdu(reset_oath, INS_DELETE, p1=0, p2=0, data=data)
resp = list_apdu(reset_oath)
exp = [TAG_NAME_LIST, len(secondname)+1, type] + secondname
assert(exp == resp)
data = [TAG_NAME, len(thirdname)] + thirdname + [TAG_KEY, len(key)+2, type, 6] + key
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=data)
resp = list_apdu(reset_oath)
exp = [TAG_NAME_LIST, len(thirdname)+1, type] + thirdname + [TAG_NAME_LIST, len(secondname)+1, type] + secondname
assert(exp == resp)
def test_noauth(reset_oath):
key = list(bytes(b'kaka blahonga'))
chal = [1,2,3,4,5,6,7,8]
resp = [0x0c, 0x42, 0x8e, 0x9c, 0xba, 0xa3, 0xb3, 0xab, 0x18, 0x53, 0xd8, 0x79, 0xb9, 0xd2, 0x26, 0xf7, 0xce, 0xcc, 0x4a, 0x7a]
data = [TAG_KEY, len(key)+1, ALG_SHA1 | TYPE_TOTP] + key + [TAG_CHALLENGE, len(chal)] + chal + [TAG_RESPONSE, len(resp)] + resp
resp = send_apdu(reset_oath, INS_SET_CODE, p1=0, p2=0, data=data)
reset_oath.connection.reconnect()
with pytest.raises(APDUResponse) as e:
resp = list_apdu(reset_oath)
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
with pytest.raises(APDUResponse) as e:
resp = send_apdu(reset_oath, INS_PUT, p1=0, p2=0, data=None)
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
with pytest.raises(APDUResponse) as e:
resp = send_apdu(reset_oath, INS_DELETE, p1=0, p2=0, data=None)
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
with pytest.raises(APDUResponse) as e:
resp = send_apdu(reset_oath, INS_SET_CODE, p1=0, p2=0, data=None)
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
with pytest.raises(APDUResponse) as e:
resp = send_apdu(reset_oath, INS_CALCULATE, p1=0, p2=0, data=None)
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
with pytest.raises(APDUResponse) as e:
resp = send_apdu(reset_oath, INS_CALC_ALL, p1=0, p2=0, data=None)
assert([e.value.sw1, e.value.sw2] == [0x69, 0x82])
with pytest.raises(APDUResponse) as e:
resp = send_apdu(reset_oath, INS_RESET, p1=0, p2=0, data=None)
assert([e.value.sw1, e.value.sw2] == [0x6A, 0x86])
resp = send_apdu(reset_oath, INS_RESET, p1=0xde, p2=0xad, data=None)

View File

@@ -1,27 +0,0 @@
import pytest
from fido2.client import CtapError
def test_getinfo(device):
pass
def test_get_info_version(info):
assert "FIDO_2_0" in info.versions
def test_Check_pin_protocols_field(info):
if len(info.pin_uv_protocols):
assert sum(info.pin_uv_protocols) > 0
def test_Check_options_field(info):
for x in info.options:
assert info.options[x] in [True, False]
def test_Check_up_option(device, info):
if "up" not in info.options or info.options["up"]:
with pytest.raises(CtapError) as e:
device.MC(options={"up": True})
assert e.value.code == CtapError.ERR.INVALID_OPTION

5
tests/run-test-in-docker.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash -eu
source tests/docker_env.sh
run_in_docker ./tests/start-up-and-test.sh

8
tests/start-up-and-test.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash -eu
/usr/sbin/pcscd &
sleep 2
rm -f memory.flash
cp -R tests/docker/fido2/* /usr/local/lib/python3.9/dist-packages/fido2/hid
./build_in_docker/pico_fido > /dev/null &
pytest tests

View File

@@ -1,4 +1,29 @@
"""
/*
* 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/>.
*/
"""
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
@@ -6,6 +31,46 @@ import math
from threading import Event, Timer
from numbers import Number
import sys
try:
from smartcard.CardType import AnyCardType
from smartcard.CardRequest import CardRequest
from smartcard.Exceptions import CardRequestTimeoutException, CardConnectionException
except ModuleNotFoundError:
print('ERROR: smarctard module not found! Install pyscard package.\nTry with `pip install pyscard`')
sys.exit(-1)
class APDUResponse(Exception):
def __init__(self, sw1, sw2):
self.sw1 = sw1
self.sw2 = sw2
super().__init__(f'SW:{sw1:02X}{sw2:02X}')
def send_apdu(card, command, p1, p2, data=None, ne=None):
lc = []
dataf = []
if (data):
lc = [0x00] + list(len(data).to_bytes(2, 'big'))
dataf = data
if (ne is None):
le = [0x00, 0x00]
else:
le = list(ne.to_bytes(2, 'big'))
if (isinstance(command, list) and len(command) > 1):
apdu = command
else:
apdu = [0x00, command]
apdu = apdu + [p1, p2] + lc + dataf + le
try:
response, sw1, sw2 = card.connection.transmit(apdu)
except CardConnectionException:
card.connection.reconnect()
response, sw1, sw2 = card.connection.transmit(apdu)
if (sw1 != 0x90):
raise APDUResponse(sw1, sw2)
return response
def verify(MC, GA, client_data_hash):
credential_data = AttestedCredentialData(MC.auth_data.credential_data)
@@ -115,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,15 +23,16 @@ 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
import urllib.request
import json
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
@@ -48,6 +49,7 @@ try:
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
from cryptography.hazmat.primitives import hashes
from cryptography import x509
except:
print('ERROR: cryptography module not found! Install cryptography package.\nTry with `pip install cryptography`')
sys.exit(-1)
@@ -55,13 +57,20 @@ 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'
method = 'GET'
if (data is not None):
method = 'POST'
req = urllib.request.Request(f"https://www.picokeys.com/pico/pico-fido/{url}/",
method=method,
data=data,
headers={'User-Agent': user_agent, })
response = urllib.request.urlopen(req)
resp = response.read().decode('utf-8')
j = json.loads(resp)
return j
class VendorConfig(Config):
@@ -72,6 +81,7 @@ class VendorConfig(Config):
class CMD(IntEnum):
CONFIG_AUT_ENABLE = 0x03e43f56b34285e2
CONFIG_AUT_DISABLE = 0x1831a40f04a25ed9
CONFIG_VENDOR_PROTOTYPE = 0x7f
class RESP(IntEnum):
KEY_AGREEMENT = 0x01
@@ -81,7 +91,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
@@ -90,7 +100,7 @@ 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
},
@@ -179,6 +189,7 @@ class Vendor:
VENDOR_BACKUP = 0x01
VENDOR_MSE = 0x02
VENDOR_UNLOCK = 0x03
VENDOR_EA = 0x04
@unique
class PARAM(IntEnum):
@@ -189,6 +200,8 @@ class Vendor:
ENABLE = 0x01
DISABLE = 0x02
KEY_AGREEMENT = 0x01
EA_CSR = 0x01
EA_UPLOAD = 0x02
class RESP(IntEnum):
PARAM = 0x01
@@ -209,7 +222,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:
@@ -231,6 +244,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,
@@ -249,6 +270,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):
@@ -328,6 +357,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):
@@ -342,9 +378,25 @@ class Vendor:
def disable_device_aut(self):
self.vcfg.disable_device_aut()
def csr(self):
return self._call(
Vendor.CMD.VENDOR_EA,
Vendor.SUBCMD.EA_CSR,
)[Vendor.RESP.PARAM]
def upload_ea(self, der):
self._call(
Vendor.CMD.VENDOR_EA,
Vendor.SUBCMD.EA_UPLOAD,
{
Vendor.PARAM.PARAM: der
}
)
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.')
@@ -352,6 +404,10 @@ def parse_args():
parser_backup.add_argument('subcommand', choices=['save', 'load'], help='Saves or loads a backup.')
parser_backup.add_argument('filename', help='File to save or load the backup.')
parser_attestation = subparser.add_parser('attestation', help='Manages Enterprise Attestation')
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.')
args = parser.parse_args()
return args
@@ -369,22 +425,41 @@ def backup(vdr, args):
elif (args.subcommand == 'load'):
vdr.backup_load(args.filename)
def attestation(vdr, args):
if (args.subcommand == 'csr'):
if (args.filename is None):
csr = x509.load_der_x509_csr(vdr.csr())
data = urllib.parse.urlencode({'csr': csr.public_bytes(Encoding.PEM)}).encode()
j = get_pki_data('csr', data=data)
cert = x509.load_pem_x509_certificate(j['x509'].encode())
else:
with open(args.filename, 'rb') as f:
dataf = f.read()
try:
cert = x509.load_der_x509_certificate(dataf)
except ValueError:
cert = x509.load_pem_x509_certificate(dataf)
vdr.upload_ea(cert.public_bytes(Encoding.DER))
def main(args):
print('Pico Fido Tool v1.2')
print('Pico Fido Tool v1.6')
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)
elif (args.command == 'backup'):
backup(vdr, args)
elif (args.command == 'attestation'):
attestation(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())