Граф коммитов

24 Коммитов

Автор SHA1 Сообщение Дата
Emil Lundberg c3defd344c
Add support for WebAuthn PRF extension (#337)
* Add support for WebAuthn PRF extension

Original context: https://bugzilla.mozilla.org/show_bug.cgi?id=1863819

* Send correct PIN protocol ID in hmac-secret

Before this change, OpenSK (tag 2.1, commit
893faa5113f47457337ddb826b1a58870f00bc78) returns CTAP2_ERR_INVALID_PARAMETER in
response to attempts to use the WebAuthn PRF extension.

Original context: https://bugzilla.mozilla.org/show_bug.cgi?id=1863819

* Extract function HmacSecretResponse::decrypt_secrets

* Clarify and correct hmac-secret and PRF client outputs in makeCredential

* Delete unnecessary impl Default

* Rename HmacSecretFromHmacSecretOrPrf to HmacCreateSecretOrPrf

* Use HmacGetSecretOrPrf data model in getAssertion too

* Add examples/prf.rs

* Construct channels outside loop

* Remove unused loop

* Add tests for HmacSecretResponse::decrypt_secrets

* Extract function AuthenticationExtensionsPRFInputs::eval_to_salt

* Extract AuthenticationExtensionsPRFInputs::select_eval and ::select_credential

* Add doc comment to AuthenticationExtensionsPRFInputs::calculate

* Fix clippy lint

* Return empty prf output if no eval or evalByCredential entry matched

* Extract function HmacGetSecretOrPrf::calculate

* Add tests of calculating hmac-secret/PRF inputs

* Fix outdated error messages

* Separate hmac_secret tests that require a crypto backend

* Add debug output to error paths of HmacSecretResponse::decrypt_secrets

* Fix a typo and a cryptic comment

* Eliminate unnecessary sha256 function

* Simplify to Sha256::digest where possible

* Derive PartialEq always, not just in cfg(test)

* Document generation of hmac_secret test data

* Remove unnecessary comma

* Tweak imports per review

* Take PinUvAuthToken as reference in HmacSecretExtension::calculate

* Deduplicate decrypt_pin_token code in tests

* Extract function GetAssertion::process_hmac_secret_and_prf_extension

* Move allow_list assignment to top level scope

* Add tests of hmac-secret and prf processing in GetAssertion::finalize_result

* Fail hmac-secret salt calculation if input salts are too long

This is prescribed by the [CTAP spec][ctap]:

>**Client extension processing**
>1. [...]
>2. If present in a get():
>  1. Verify that salt1 is a 32-byte ArrayBuffer.
>  2. If salt2 is present, verify that it is a 32-byte ArrayBuffer.
>  [...]

[ctap]: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-hmac-secret-extension

* Add tests of GetAssertion::process_hmac_secret_and_prf_extension

* Propagate WrongSaltLength as InvalidRelyingPartyInput in GetAssertion::process_hmac_secret_and_prf_extension

* Return PrfUnmatched instead of None when shared secret is not available

This is needed because the PRF extension should return an empty extension output
`prf: {}` when the extension is processed but no eligible authenticator is
found. Thus we need to differentiate these cases so that
`GetAssertion::finalize_result` can match on `PrfUnmatched` and generate the
empty output.

* Add debug logging when no shared secret is available

* Add debug logging when hmac-secret output decryption fails

* Add test of serializing uninitialized and unmatched PRF inputs

* Add missing test of serializing hmac-secret with PIN protocol 2
2024-07-25 10:06:59 -07:00
John M. Schanck d3a0d09f48 Add StatusUpdate::SelectResultNotice 2023-09-19 15:39:26 -07:00
John M. Schanck 5d20800714 Rename User to PublicKeyCredentialUserEntity 2023-09-19 15:39:26 -07:00
John M. Schanck d512ad9b5d Rework support for AppId extension 2023-09-11 09:39:09 -07:00
John M. Schanck a00fd64edc Add support for the credProps extension 2023-09-11 09:39:09 -07:00
John M. Schanck 0a00a250ae Remove webdriver feature now that it has been moved to Firefox 2023-08-23 10:54:34 -07:00
John M. Schanck d160f94e53 Remove icon field from WebAuthn PublicKeyCredentialEntitys 2023-08-21 13:10:03 -07:00
John M. Schanck bda39d6a10 Separate serialization routines for AttestationObject and MakeCredentialsResult 2023-08-15 13:26:00 -07:00
John M. Schanck 8947b15140 Type visibility changes for virtual authenticators 2023-06-14 12:13:28 -07:00
John M. Schanck c19a3ea625 Move get/set_device_info to HIDDevice and remove U2FDeviceInfo status updates 2023-06-02 10:39:09 -07:00
John M. Schanck 1aaf0540ff Add StatusUpdate::PresenceRequired 2023-05-15 11:59:31 -07:00
Martin Sirringhaus cb44fd3918 Add interactive token management functionality 2023-05-01 09:24:51 -07:00
John M. Schanck 4cfd0668e7 Make Required/Preferred/Discouraged arguments explicit 2023-04-26 10:13:09 -07:00
John M. Schanck 36de5573da Make MC and GA take client data hash instead of client data 2023-04-18 13:56:18 -07:00
John M. Schanck 91e185e24c Remove redundant CTAP1 interface 2023-04-18 13:56:18 -07:00
John M. Schanck fef2653602 Copy StateMachine::{register,sign} to StateMachineCtap2::legacy_{register,sign} 2023-04-18 13:56:18 -07:00
Martin Sirringhaus 8fe23f3536 Implement UV-handling for CTAP2.1 2023-04-11 11:14:42 -07:00
Martin Sirringhaus d79e7f2d68 Rework CTAP2.0 GetPinToken-workflow 2023-03-27 10:19:20 -07:00
John M. Schanck a396f70350 Fix clippy lints 2023-02-09 11:52:11 -08:00
John M. Schanck 27de241f60 Add RP ID retry mechanism for FIDO AppID extension 2023-02-09 11:52:11 -08:00
Martin Sirringhaus 5e9db7bd9d Fix clippy warnings and run clippy in CI 2023-01-12 13:01:46 -08:00
msirringhaus 78a8cd56aa
Bump sha2-version (#168)
Co-authored-by: Martin Sirringhaus <>
2022-05-23 14:19:23 +02:00
msirringhaus ab20df2e8c
Device selection (#163)
* Add cancel-function to HIDs, which can cancel blocking reads

* Add function to clone a (Linux) device in write-only mode

* First implementation of DeviceSelector

For selecting multiple devices, if those are present.
A few lints need to be fixed and it doesn't work with PIN-tokens yet.

* Fix stupid typo in function name

* Move PIN into StatusUpdate as a callback (discoverable_creds not yet working)

* Move workaround for unsetting uv to accomodate the new PIN-retrieval process

* Fix warnings

* Send DeviceSelectionNotice on Status channel, when devices blink

Also remove the unneccessary Mutex around the status-sender, since
we have to clone it anyways into Transaction. Then we can just clone
and hand it into each device-thread.

* Trying to add latest features to mac and win

Untested, so compilation might be broken.
netbsd is only partly done and broken anyways at the moment

* Simplify HIDDevice vs. FidoDevice traits

Instead of implementing FidoDevice for each HIDDevice and implementing a bunch
of duplicate functions, that call through to HIDDevice, just extend HIDDevice
with FidoDevice.
This _might_ become a problem once we introduce NFC or such, but it is unclear
at the moment, how this is going to look like, anyways.

* Add impl FidoDevice for Windows and Mac

* Reduce number of arguments to new-device callback function

Also move functions needed by statemachine or device_selector into the
HIDDevice trait

* Make tests compile again

* Add authenticatorSelection command (CTAP2.1 only)

* Blink only when at least one token has a PIN (or UV)

With this, we can skip the "blinking"-phase if multiple tokens are there,
because we only need to blink, if at least one of them has a PIN (otherwise
we get a PIN-prompt, even though no token has been selected yet).
For tokens with no PIN (or CTAP1 tokens) we can send the normal request right
away. That will blink anyways.

* Make only tokens with some UV blink

Only let tokens with some UV (e.g. Password) blink, the others
can get the request straight away. This way, they will also blink,
but execute the request right away and cancel all other devices.
Only UV-devices would trigger callbacks, so they have to be sent a
selection-command.

* Make the is_u2f-function part of the HIDDevice trait as every device needs to have that function currently

* Move the TestDevice into its own platform and implement tests for DeviceSelector

Mostly copy&paste, plus minor adjustments to existing tests.
For the DeviceSelector-Tests, some extensions to the device have been made
(adding Sender and Receiver, giving it a function for it).

* Some AuthenticatorOptions need a default value according to spec

* Revert f3718a3: Blink for all devices, not just those with PIN

We can't let the non-UV devices continue with the regular request, because in the error-case
we can't differentiate if it is due to a bad request or something the user did (e.g. pull the
token out, or decline the request on the token, etc.).
Some errors should lead to cancelling the request altogether, the other just means "not with
this device". With our own Blink-command, we know that it works in principle. If there is an
error then it must be because of user-actions and should lead to skipping that device.

* Add status callbacks to C-API (for ctap2)

* Don't just borrow the status result, but 'forget' it on the Rust side

If we don't do this, the callback-function on C++ side can't keep a
reference to the result, which it needs for later sending a Pin back.

* Add "DeviceSelected" status update plus tests

Also moved the "DeviceUnavailable" to DeviceSelector (and send it upon removal of the token)

* First test of multi-platform CI (excluding crypto)

* Fix builds on Mac and Windows

* Add option for PinError-callback to 'say' the error is not recoverable

We use the property of channel() here, that recv will error out if the Sender is dropped.
So the status-callback can now drop the Sender without replying first, to trigger
the code to error out the 'normal' way.
This helps us to show a popup-dialog, but also cancel the current transaction cleanly.
For this, NSS-errors needed to be made cloneable, so we replaced them with their String-representation.

* Fix typo

* Implement serializing of crypto backend errors

* Add 2 more CI pipelines for building with all crypto-backends on Linux

* Change status update C-API to combined buffer+length-interface

* Revert "Change status update C-API to combined buffer+length-interface"

This reverts commit b4342e90b6.

Co-authored-by: Martin Sirringhaus <>
2022-05-17 17:35:20 +02:00
msirringhaus f310a5b826
Ctap2 continued (#157)
* Moving platform specific modules into new module 'transport'

* Replace capability-defines with bitflags (and add ctap2 capabilities)

* Make HID commands its own type

* Run cargo fmt and clippy

* Add test for U2F_VERSION

* CTAP2: Implement first ctap2-command GetInfo as well as ctap1-fallback GetVersion, plus tests

* WIP: Add part MakeCredentials

* Add test for (and do some fixes) for MakeCredential CTAP1 mapping

* Adjust tests with RelyingParty and name-field

* Fix serialization of MakeCredentialOptions (individually serialize members)

* Temporarily parse credential_public_key just as a byte-vector, until COSE-crate is added and able to actually parse it

* Add Option Unparsed to AttestationStatement, to currently skip parsing it (which would need a DER-parser)

* Add commands GetAssertion and GetAssertionNext. Still missing tests.

* WIP: Add clientpin command with all the crypto functionality commented out

* Remove duplicate Pin-definition

* MakeCredentials and GetAssertion now do not store Pin but PinAuth, which has to be calculated before hand

* Sort CTAPHID const values according to value

* WIP: New Manager and StateMachine-slots for CTAP2. A lot of functions are still missing.

* MakeCredentials: Remove pointless alias for HIDError

* GetInfo: Fix wrong parsing of response. Forgot to strip away the status-byte

* Linux/Device: Fix bug where file was not saved and prematurely closed, leaving a dangling filedescriptor

* WIP: Add seperate statemachine for ctap2, add example which can already call MakeCredential

* Prepare for sign-functions: Make SignResult CTAP2-aware (similar to RegisterResult)

* Add some (not all) CTAP2.1 info options to GetInfo for testing.

* Implement generic Request marker trait for GetAssertion

* Fix non-conformance with spec for GetAssertion: Only RelyingParty-ID should be send, not the entire rp

* First draft implementation of manager.sign()

- SignArgs now enum for ctap1/2 like RegisterArgs
- Same for SignResult
- Add exclude_list and allow_list to RegisterArgs and SignArgs resp.

* Remove transports-option of PublicKeyCredentialDescriptor for now as older tokens dont understand it and return an error

* Fix wrongly used default of GetAssertionOptions (which has its own Default-implementation)

* Fix test for PublicKeyCredentialDescriptor because of removed transport-serialization

* Remove some unneeded dead_code cfgs

* Remove outdated TODO-comments

* Add some comments/hints/todos

* Expose Pin at a higher level to be easily found and used by users

* First draft of the framework of crypto-module, with some dummy-content for testing

* GetInfo: When deserializing key algorithms don't abort when an unknown alg is encountered. Just mark it as unknown

* Activate openssl crypto backend for PIN usage in MakeCredentials

Moved src/ctap2/crypto.rs to src/crypto/mod.rs
Fleshed out authenticate, encrypt, decrypt, etc. functions in openssl
Use them in client_pin.rs

* openssl: Clarify IV comments. Spec says IV=0 for all use cases

* GetAssertion: Implement pin_auth and use it to get user_verification in GetAssertionResponse

* PIN: Implement automated GetRetires if pin was wrong. Re-wrap several other pin-related Errors

* Implement Ring backend as far as possible for now (ring doesn't support AES-CBC yet)

* Redirect PinRequired error and update some comments

* Implement first draft of NSS backend

* Add one more testcase: Hash and encrypt PIN

* Fix GetAssertion for CTAP1-only devices

- Defaults of GetAssertionOptions were wrong
- Skip PIN-related function for CTAP1

* Fix downward compatiblity (to use CTAP2-Manager for CTAP1 requests on CTAP1 tokens)

- Repackage CTAP1 requests for "register()" and "sign()" into CTAP2 data structure
- Repackage MakeCredential result only for CTAP2 requests into CTAP2 result values
- Repackage GetAssertion result into CTAP1-result if needed
- Add "is_ctap2_request()" function and only send cbor-messages if Request AND Device are CTAP2
- Introduce RelyingPartyWrapper (CTAP1 requests provide the RpIdHash only, not the complete rp)
- Copy existing ctap1-example (main.rs), but use new CTAP2 Manager

* Fix build with feature webdriver

* Fix parsing of GetAssertionResponse: Parse PublicKeyCredentialDescriptor instead of serde::Value

* First draft of capi-additions: sign-result repackaging not yet done

* For testing purposes, switch to own repo of rc_crypto (to test vendoring)

* Undo last change, switch to application-services directly

* Expose rust_ctap2_mgr_new()

* Fix various CTAP1-only bugs

- When doing CTAP1 only, use challenge directly in the requests (not hashing the whole client data)
- Package GetAssertionResult similar to MakeCredential in CTAP1 and CTAP2
- Append flags and counter before AssertionObject.u2f_sign_data() to make it identical to the CTAP1 response

* openssl: Add incomplete verify()-function to test if signature is valid. Add some commented out code on how to eventually use it

* Make Challenge a base64-encoded String

* Make RegisterResult-matching agnostic wrt number of result arguments

* Add to_vec() function to AuthenticatorData, to be able to easily serialize it again for the answer back to the RelyingParty

* Add function to serialize Alg for passing the answer to C

* Add rust_u2f_resbuf_contains to capi, to check whether certain keys are set in the result-hashmap or not (for determining if its a ctap2 or ctap1 response)

* Expose ctap2-register function to C and repackage the result accordingly

* Remove User from Ctap2-SignArgs, as it is not needed

* Expose rust_ctap2_mgr_sign() to C

* Fix bug: sign() should use WebauthnType::Get, not Create

* C-API: Introduce CTAP2.0 indicator to check whether a response is CTAP1 or 2 (used 2.0, as 2.1 will be coming soon)

* Make ctap2-sign() work

- Add client-data to CTAP2 result
- C-API: Set CTAP2 indicator in the sign-result
- C-API: Add logic to determine credential-id (if none is returned from the token, but allow_list is length 1, use that)

* Also remap PinRequired-error

* GetInfo: Remove double definition of AAGuid

* Implement Deserialize for CollectedClientData

- Implement Deserialize for WebauthnType
- Implement Deserialize for TokenBinding
- Make TokenBinding contain String, not Vec<u8> (as we get it base64-encoded from the server)
- Add some tests for it
- Fix remaining test by fixing the client_hash (reference calculated via commandline)

* Make crossOrigin not an Option anymore, as we need it when serializing. Set a default for deserializing instead.

* test: implement tests for ctap2 get_assertion

This also fixes an issue in the test frame work. Due to the drop
implementation for the test client, a panic-while-panic could occur
which prevents the developer from seeing the actual error

* Update test_get_assertion_ctap2() to new codebase

* Start to add macos support for ctap2

* Expose PIN-related errors via C-api

* Move PinErrors to toplevel error. Enhance example to ask for pin, if required.

* Avoid multiple remapping of PinErrors and map them once directly to AuthenticatorError

* Define (most) C-API constants like hashmap-IDs only once, to avoid possible mismatch between C and Rust.

* Finish up macos

* Revert "Define (most) C-API constants like hashmap-IDs only once, to avoid possible mismatch between C and Rust."

With this change, it is not possible to use the const-values in switch-case statements in C/C++.
This reverts commit d5b400252b.

* Implement HIDDevice trait for device stub / dummy, so it (hopefully) compiles on non-supported archs

* Add some explanatory comments to the HIDDevice trait

* Rename user_validation to user_verification, which is the correct term

* Wrap various RegisterArgs in structs for smaller function API and also expose MakeCredentialOptions

* Also shrink C-API for GetAssertion and expose GetAssertionOptions. Fix bug where PIN+user verification could be provided, which is not allowed

* Make User.name optional, as it is no longer required by Webauthn

* Use correct credential-id for return-value in GetAssertion. Use the old path as a Fallback in case nothing is found

* Fix broken prev. commit.

* Fix tests because User.name is now an option

* Make naming of CommandError variants consistent and remove redundant 'Parsing'-variant

* Replace custom error with invalid_length (even though the usage is a bit weird)

* Use From<CommandError>-trait of HIDError to avoid verbose map_err-calls, by using the shorter .into()

* Add another test with discoverable credentials

* Splitting c-api into u2f and ctap2-parts

- Add a bunch of functions to access ctap2 results
- Attestation statement is now returned as "raw cbor data", instead of individual pieces, that are re-assembled
- Including possibility to have multiple credentials upon signing in ("discoverable creds" might return more, which the user has to choose from)

* Fix bug in parsing ctap1-response (wrongly set flags), which was not according to spec

* Fix missing credential_ids in GetAssertion responses (most notably when using a CTAP1-only token)

* Only try to determine PIN-Auth, if the request is actually CTAP2

* Return the actual Adpu-Error instead of generic DeviceError

* Adjust MakeCredentialsOptions::default, as uv=true is problematic for ctap1. Let the webauthn-server specify it

* Rework crypto module, with better COSE defintions and being able to parse keys as structs instead of Vec<u8>

* Follow-up to 'Only try to determine PIN-Auth, if the request is actually CTAP2': Same for GetAssertion

* Fix bug where Challenge for CTAP1 requests was base64-encoded, which is wrong

* Challenge only has to be 32 bytes in case of CTAP1-mapping, not in general

* C-API: Add functions to copy username from GetAssertions

* Implement hmac-secret extension

* Remove old, commented out code

Co-authored-by: Martin Sirringhaus <>
Co-authored-by: Dominik Süß <dominik@suess.wtf>
Co-authored-by: William Brown <william@blackhats.net.au>
2022-01-25 17:42:30 +01:00