mozilla/authenticator-rs

FIDO_2_0 devices with no pin set - experiencing unexpected make credential behavior

elukewalker opened this issue · 7 comments

I would expect to able to create a credential on both my FIDO_2_0 security key and my FIDO2_2_1_PRE key when no pin is set on either device. I can successfully create a credential on my FIDO2_2_1_PRE device. However, the FIDO2_2_0 make credential request, where uv=preferred or uv is undefined fails with a PinError(PinNotSet) error.

Here is the FIDO2_2_0 output

$ RUST_LOG=debug cargo run --no-default-features --features crypto_openssl --example ctap2
...
[2023-07-28T21:24:49Z DEBUG authenticator::ctap2] Getting pinUvAuthParam
[2023-07-28T21:24:49Z ERROR authenticator::ctap2] Error when determining pinAuth: PinError(PinNotSet)
thread '<unnamed>' panicked at 'Unexpected error: PinNotSet', examples/ctap2.rs:135:17
...

What is confusing is that I can successfully make credentials on my FIDO2_2_0 device with no pin set using Chrome and Safari. But on FireFox 117.0a1 I get the message "User verification failed... You may need to set a PIN on your device." Note: uv was undefined during testing.
Screenshot 2023-07-28 at 2 30 42 PM

I can always make credentials on my FIDO2_2_1_PRE device with no pin set using Chrome, Safari, and Firefox when uv is undefined or set to preferred.

When a make credential request has uv=preferred or undefined, shouldn't the library allow skipping user verification when no pin is set on the device and the max supported version is FIDO2_2_0?

It looks like the the issue may be how always_uv is set at

|| info.max_supported_version() == AuthenticatorVersion::FIDO_2_0;

AlwaysUV is a feature to accommodate devices with built-in user verification (like a fingerprint sensor or a physical PIN input), and was added in CTAP 2.1, I don't think it should be enabled for CTAP2.0 (FIDO2_2_0) devices: see
https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html#sctn-feature-descriptions-alwaysUv

The CTAP 2.1 spec says

platforms must be aware that FIDO_2_0 (aka CTAP2.0) authenticators always require some form of user verification for authenticatorMakeCredential operations.

Although I don't see anything so explicit when I look at the CTAP 2.0 spec. It looks to me like FIDO_2_0 authenticators only require some form of user verification once a PIN has been set.

This is working in Chrome because they downgrade requests to CTAP1 when the arguments are compatible with doing so. We should probably do the same, as our current behavior is causing some confusion.

The CTAP 2.0 spec is very unclear here..

  1. If pinAuth parameter is not present and clientPin been set on the authenticator, return CTAP2_ERR_PIN_REQUIRED error.

I initially read "clientPin been set" as "a client PIN has been set" but I see now that it links to the clientPin field of the authenticator info. So that's consistent with what's written in the CTAP 2.1 spec and with what we implemented.

I'm fairly confident I've been able to observe other platforms (Safari, Chrome) and authenticators (YubiKey & others) create credentials using CTAP2, not U2F fallback when no PIN is set. In any case, I've also asked for clarification about the spec, particularly the backwards compatibility note you referenced.

For reference this is the behavior for a FIDO_2_0 device once the logic on line 256 is commented out:

$ cargo run --example ctap2

warning: unused import: AuthenticatorVersion

 --> src/ctap2/commands/make_credentials.rs:1:42

  |

1 | use super::get_info::{AuthenticatorInfo, AuthenticatorVersion};

  |                                          ^^^^^^^^^^^^^^^^^^^^

  |

  = note: #[warn(unused_imports)] on by default

warning: authenticator (lib) generated 1 warning (run cargo fix --lib -p authenticator to apply 1 suggestion)

    Finished dev [unoptimized + debuginfo] target(s) in 0.12s

     Running target/debug/examples/ctap2

Using 25s as the timeout

Asking a security key to register now...

STATUS: device available: Vendor: HS, Device: HyperFIDO, Interface: 2, Firmware: v1.0.1, Capabilities: 07

STATUS: Continuing with device: Vendor: HS, Device: HyperFIDO, Interface: 2, Firmware: v1.0.1, Capabilities: 07

STATUS: success using device: Vendor: HS, Device: HyperFIDO, Interface: 2, Firmware: v1.0.1, Capabilities: 07

Ok!

Register result: AttestationObject { auth_data: AuthenticatorData { rp_id_hash: RpIdHash(o3mm9u6vuaVeN4wRgDTidR5oL6ufLTCrE9ISVYbOGUc), flags: USER_PRESENT | ATTESTED, counter: 581, credential_data: Some(AttestedCredentialData { aaguid: AAGuid(9f77e279-a6e2-4d58-b700-31e5943c6a98), credential_id: [223, 159, 14, 192, 57, 146, 198, 53, 26, 48, 39, 97, 227, 171, 154, 32, 94, 148, 141, 42, 119, 159, 57, 76, 59, 173, 193, 102, 239, 4, 123, 23, 89, 160, 88, 65, 250, 175, 218, 207, 100, 221, 43, 63, 72, 184, 184, 129, 187, 60, 245, 2, 188, 134, 208, 243, 173, 40, 118, 202, 23, 215, 136, 135, 253, 102, 100, 29, 62, 149, 17, 240, 234, 247, 100, 33, 243, 5, 191, 7, 19, 167, 211, 18, 119, 192, 140, 123, 7, 204, 82, 198, 165, 192, 97, 159], credential_public_key: COSEKey { alg: ES256, key: EC2(COSEEC2Key { curve: SECP256R1, x: [127, 85, 93, 5, 82, 194, 30, 95, 71, 112, 77, 200, 177, 111, 190, 44, 138, 135, 137, 162, 96, 247, 79, 52, 6, 53, 29, 236, 57, 249, 248, 132], y: [13, 179, 102, 170, 8, 81, 202, 223, 62, 244, 195, 31, 222, 216, 91, 75, 62, 40, 60, 206, 189, 72, 56, 211, 22, 27, 24, 107, 115, 43, 37, 24] }) } }), extensions: Extension { pin_min_length: None, hmac_secret: None } }, att_statement: Packed(AttestationStatementPacked { alg: ES256, sig: Signature(MEQCIEPRHPMXLW-xV-XgYaFxmm2G1avG3O6Y1GUuz8rwhfLSAiADx8r-LGQCrpVV4XG_vjTdtTqyqqOffEt1OSNw0mP4bA), attestation_cert: [AttestationCertificate([48, 130, 2, 45, 48, 130, 1, 212, 160, 3, 2, 1, 2, 2, 16, 51, 147, 79, 65, 234, 163, 89, 101, 66, 153, 217, 82, 75, 169, 162, 99, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, 48, 65, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 67, 65, 49, 18, 48, 16, 6, 3, 85, 4, 10, 12, 9, 72, 89, 80, 69, 82, 83, 69, 67, 85, 49, 30, 48, 28, 6, 3, 85, 4, 3, 12, 21, 72, 121, 112, 101, 114, 115, 101, 99, 117, 32, 70, 73, 68, 79, 50, 32, 67, 65, 32, 48, 49, 48, 32, 23, 13, 49, 56, 49, 49, 48, 56, 48, 48, 48, 48, 48, 48, 90, 24, 15, 50, 48, 51, 51, 49, 49, 48, 55, 50, 51, 53, 57, 53, 57, 90, 48, 99, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 67, 65, 49, 18, 48, 16, 6, 3, 85, 4, 10, 12, 9, 72, 89, 80, 69, 82, 83, 69, 67, 85, 49, 34, 48, 32, 6, 3, 85, 4, 11, 12, 25, 65, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 111, 114, 32, 65, 116, 116, 101, 115, 116, 97, 116, 105, 111, 110, 49, 28, 48, 26, 6, 3, 85, 4, 3, 12, 19, 72, 121, 112, 101, 114, 115, 101, 99, 117, 32, 70, 73, 68, 79, 50, 32, 75, 49, 56, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 223, 160, 193, 223, 71, 60, 157, 236, 228, 253, 179, 183, 161, 120, 53, 126, 194, 108, 100, 78, 42, 44, 176, 206, 47, 162, 187, 96, 134, 50, 71, 119, 170, 194, 177, 236, 105, 52, 199, 167, 250, 66, 50, 41, 147, 203, 117, 167, 38, 224, 170, 199, 229, 170, 108, 82, 254, 124, 49, 58, 31, 87, 79, 158, 163, 129, 137, 48, 129, 134, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, 163, 119, 7, 218, 141, 164, 178, 2, 239, 23, 226, 252, 96, 102, 134, 94, 110, 176, 125, 152, 48, 31, 6, 3, 85, 29, 35, 4, 24, 48, 22, 128, 20, 171, 204, 28, 191, 222, 49, 129, 29, 42, 119, 245, 152, 140, 74, 226, 180, 44, 71, 246, 80, 48, 12, 6, 3, 85, 29, 19, 1, 1, 255, 4, 2, 48, 0, 48, 19, 6, 11, 43, 6, 1, 4, 1, 130, 229, 28, 2, 1, 1, 4, 4, 3, 2, 5, 32, 48, 33, 6, 11, 43, 6, 1, 4, 1, 130, 229, 28, 1, 1, 4, 4, 18, 4, 16, 159, 119, 226, 121, 166, 226, 77, 88, 183, 0, 49, 229, 148, 60, 106, 152, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, 3, 71, 0, 48, 68, 2, 32, 127, 218, 178, 218, 94, 170, 213, 67, 194, 167, 189, 188, 240, 39, 207, 129, 32, 47, 8, 90, 241, 233, 136, 111, 236, 186, 108, 101, 185, 153, 139, 100, 2, 32, 95, 250, 108, 28, 137, 170, 109, 174, 154, 85, 85, 173, 172, 253, 163, 164, 12, 4, 164, 131, 11, 148, 241, 17, 163, 56, 132, 188, 36, 230, 136, 175]), AttestationCertificate([48, 130, 1, 224, 48, 130, 1, 135, 160, 3, 2, 1, 2, 2, 16, 51, 147, 79, 65, 234, 163, 89, 101, 66, 153, 217, 82, 75, 169, 162, 97, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, 48, 58, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 67, 65, 49, 18, 48, 16, 6, 3, 85, 4, 10, 12, 9, 72, 89, 80, 69, 82, 83, 69, 67, 85, 49, 23, 48, 21, 6, 3, 85, 4, 3, 12, 14, 72, 89, 80, 69, 82, 70, 73, 68, 79, 32, 48, 50, 48, 48, 48, 32, 23, 13, 49, 56, 49, 49, 48, 49, 48, 48, 48, 48, 48, 48, 90, 24, 15, 50, 48, 51, 56, 49, 48, 51, 49, 50, 51, 53, 57, 53, 57, 90, 48, 65, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 67, 65, 49, 18, 48, 16, 6, 3, 85, 4, 10, 12, 9, 72, 89, 80, 69, 82, 83, 69, 67, 85, 49, 30, 48, 28, 6, 3, 85, 4, 3, 12, 21, 72, 121, 112, 101, 114, 115, 101, 99, 117, 32, 70, 73, 68, 79, 50, 32, 67, 65, 32, 48, 49, 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4, 239, 151, 215, 52, 106, 143, 125, 149, 241, 150, 196, 104, 228, 24, 33, 210, 45, 15, 195, 118, 50, 48, 152, 163, 252, 65, 72, 104, 207, 248, 140, 18, 101, 219, 70, 3, 183, 11, 166, 209, 23, 117, 199, 25, 82, 211, 87, 18, 109, 132, 18, 2, 219, 12, 47, 56, 123, 184, 120, 64, 68, 86, 128, 225, 163, 102, 48, 100, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, 171, 204, 28, 191, 222, 49, 129, 29, 42, 119, 245, 152, 140, 74, 226, 180, 44, 71, 246, 80, 48, 31, 6, 3, 85, 29, 35, 4, 24, 48, 22, 128, 20, 182, 88, 113, 243, 12, 194, 68, 0, 25, 187, 119, 175, 44, 217, 20, 241, 114, 166, 107, 8, 48, 18, 6, 3, 85, 29, 19, 1, 1, 255, 4, 8, 48, 6, 1, 1, 255, 2, 1, 0, 48, 14, 6, 3, 85, 29, 15, 1, 1, 255, 4, 4, 3, 2, 1, 6, 48, 10, 6, 8, 42, 134, 72, 206, 61, 4, 3, 2, 3, 71, 0, 48, 68, 2, 32, 3, 138, 5, 206, 64, 69, 127, 134, 196, 158, 27, 10, 42, 23, 191, 63, 250, 233, 5, 215, 98, 168, 206, 88, 78, 255, 52, 174, 255, 212, 242, 53, 2, 32, 81, 109, 242, 247, 28, 247, 193, 143, 201, 75, 19, 39, 178, 244, 203, 173, 249, 9, 86, 239, 141, 152, 166, 28, 219, 93, 178, 147, 85, 70, 6, 189])] }) }

*********************************************************************

Asking a security key to sign now, with the data from the register...

*********************************************************************

STATUS: device available: Vendor: HS, Device: HyperFIDO, Interface: 2, Firmware: v1.0.1, Capabilities: 07

STATUS: Continuing with device: Vendor: HS, Device: HyperFIDO, Interface: 2, Firmware: v1.0.1, Capabilities: 07

STATUS: success using device: Vendor: HS, Device: HyperFIDO, Interface: 2, Firmware: v1.0.1, Capabilities: 07

Assertion Object: GetAssertionResult([Assertion { credentials: Some(PublicKeyCredentialDescriptor { id: [223, 159, 14, 192, 57, 146, 198, 53, 26, 48, 39, 97, 227, 171, 154, 32, 94, 148, 141, 42, 119, 159, 57, 76, 59, 173, 193, 102, 239, 4, 123, 23, 89, 160, 88, 65, 250, 175, 218, 207, 100, 221, 43, 63, 72, 184, 184, 129, 187, 60, 245, 2, 188, 134, 208, 243, 173, 40, 118, 202, 23, 215, 136, 135, 253, 102, 100, 29, 62, 149, 17, 240, 234, 247, 100, 33, 243, 5, 191, 7, 19, 167, 211, 18, 119, 192, 140, 123, 7, 204, 82, 198, 165, 192, 97, 159], transports: [] }), auth_data: AuthenticatorData { rp_id_hash: RpIdHash(o3mm9u6vuaVeN4wRgDTidR5oL6ufLTCrE9ISVYbOGUc), flags: USER_PRESENT, counter: 640, credential_data: None, extensions: Extension { pin_min_length: None, hmac_secret: None } }, signature: [48, 70, 2, 33, 0, 210, 9, 183, 16, 122, 200, 20, 20, 154, 217, 38, 1, 130, 201, 228, 250, 127, 165, 104, 239, 122, 40, 160, 35, 23, 11, 224, 21, 142, 127, 23, 197, 2, 33, 0, 194, 127, 115, 214, 101, 102, 69, 57, 45, 88, 41, 38, 101, 173, 81, 147, 221, 120, 152, 136, 172, 4, 76, 192, 173, 113, 45, 236, 182, 165, 29, 188], user: None }])

Done.

STATUS: end

and here are the capabilities of the device I used to test:

PS > fido2-token.exe -I "\\?\hid#vid_2ccf&pid_0854&mi_01#7&bdf9234&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
proto: 0x02
major: 0x01
minor: 0x00
build: 0x01
caps: 0x07 (wink, cbor, msg)
version strings: U2F_V2, FIDO_2_0
extension strings: hmac-secret
aaguid: 9f77e279a6e24d58b70031e5943c6a98
options: rk, up, nouv, noplat, noclientPin
fwversion: 0x0
maxmsgsiz: 2048
maxcredcntlst: 0
maxcredlen: 0
maxlargeblob: 0
pin protocols: 1
pin retries: 8
pin change required: false
uv retries: undefined

OK, I'm convinced that (our read on) the comment in CTAP 2.1 is incorrect and that CTAP 2.0 allows MakeCredentials without UV when the device is not protected.

ve7jtb commented

The CTAP 2.0 specification requires the authenticator to return an error if the pin is sent and no pinAuth parameter is sent. The logic allowed the authenticator to make the credential id no pin was set.

In CTAP2.1 we changed the logic to allow non-resident credentials to always be made with no pin. All credentials can still be created if no pin is set and alwaysuv is off.

We added a warning for platforms that CTAP2.0 and 2.1_pre keys would still require UV for make credentials with non resident.

However, I may have used too strong wording on the warning
platforms must be aware that FIDO_2_0 (aka CTAP2.0) authenticators always require [some form of user verification] for [authenticatorMakeCredential] operations.

Should have been
platforms must be aware that FIDO_2_0 (aka CTAP2.0) authenticators always require [some form of user verification] for [authenticatorMakeCredential] operations, if the authenticator is [Protected by some form of User Verification]

I will update that in CTAP2.2