Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect handling of 'preferred' user verification. #162

Open
zelch opened this issue Oct 28, 2022 · 3 comments
Open

Incorrect handling of 'preferred' user verification. #162

zelch opened this issue Oct 28, 2022 · 3 comments

Comments

@zelch
Copy link

zelch commented Oct 28, 2022

Specifically, it is handling 'preferred' as 'required'.

By my reading of the Webauthn spec, the handling of 'preferred' depends of the definition of 'Protected by some form of User Verification'.

Per the spec, this requires that the support be both supported and enabled.

In 6.1.1, the text at the beginning describes the case where user verification is 'required' and not present.

It then describes the process flow, I draw some attention to 6.1.1, 1.1 where things start to go wrong in the described flow, though I will definitely note that the text at the beginning says 'In other words, this is only a brief sketch of plausible platform behavior.'

If the authenticator is protected by some form of user verification, or the Relying Party prefers enforcing user verification (e.g., by setting options.authenticatorSelection.userVerification to "required", or "preferred" in the WebAuthn API

In 6.1.1, 1.1.2.2 we get to the point where the code is following the described flow... But the described flow simply does not handle this case.

The request says that user verification is 'preferred', the device has support for clientpin, however clientpin is not configured on the device.

This is currently handled in fido2/client.py, class _Ctap2ClientBackend, function _should_use_uv, on line 470.

In this code, it looks at both if uv is supported, and if uv is configured.

If user verification is REQUIRED, or if user verification is preferred and supported, or if 'alwaysUv' is set by the device, then it fails if uv is not configured for the device.

I propose that the uv_supported check be removed entirely, and in the if statement we replace:

            or (
                user_verification == UserVerificationRequirement.PREFERRED
                and uv_supported
            )

with

            or (
                user_verification == UserVerificationRequirement.PREFERRED
                and uv_configured
            )

This would, I believe, be both compliant to the spec, and would work for the fairly common case where user verification is preferred, but not required, and a user with a Yubikey 5 has not configured a PIN.

@dainnilsson
Copy link
Member

See my comment here: #148 (comment)

IMO the spec isn't completely clear as to the correct behavior, but the behavior we have implemented is (I believe) in line with what the major platforms are doing.

@zelch
Copy link
Author

zelch commented Oct 28, 2022

@dainnilsson

I agree that the spec could be more clear as to the correct behavior here.

However I disagree that the current behavior is sensible, preferred should use UV if it is readily available, and still work regardless.

So, I did a little digging.

And the current behavior is definitely not how either Firefox or Chrome behave.

I did some quick hunting for a for Webauthn demos that allow you to specify the user verification setting, and found:

https://webauthn.singularkey.com/

This works on both Chrome and Firefox.

With User Verification set to Unspecified, Discouraged, or Preferred, my Yubikey 5 with no PIN set is accepted by both Firefox and Chrome.

With User Verification set to Required, Firefox simply ignores the Yubikey as not being a qualified device, while Chrome prompts for me to setup a PIN.

@emlun
Copy link
Member

emlun commented Nov 1, 2022

@zelch Thanks - you may be right, but the CTAP2 spec is currently underspecified in regards to this.

Some background: We had misremembered the WebAuthn spec saying this about the "preferred" case:

[...] user agents SHOULD guide the user through setting up user verification if needed to create a client-side discoverable credential in this case.

...and that that is how Chrome behaves.

However, that is only true for residentKey: "preferred", not userVerification: "preferred". The latter only says:

This value indicates that the Relying Party prefers user verification for the operation if possible, but will not fail the operation if the response does not have the UV flag set.

and Chrome indeed does not prompt the user to set a PIN if one is not currently configured.

So yes, from a WebAuthn Relying Party perspective, the ceremony should indeed proceed without requiring the user to set up a PIN.

However...

The python-fido2 feature discussed here is part of the client-facing library, not the RP-facing library. It's quite reasonable that a client implementation should be able to detect whether a PIN is set or not, and decide whether or not to prompt the user to set one up. Unlike an RP, a client can catch the exception, decide how to proceed, and adjust the make_credential() arguments as necessary, without having to cause a user interaction during that process.

There is support for this interpretation in the CTAP2 spec you linked, too. If the RP has set userVerification: "preferred", the procedure enters step 1.1., and there is no path out of that branch that results in an authenticator response without UV. That might require the user to set up a PIN, so the client implementation should be able to do that, but it's not appropriate for the library to try to do it internally. The calling application should be in control of any user interaction, so the library raises an exception to return control to the caller.

However...

There is a logical inconsistency in the CTAP2 spec. Step 1.1. reads:

  1. If the authenticator is protected by some form of user verification, or the Relying Party prefers enforcing user verification (e.g., by setting options.authenticatorSelection.userVerification to "required", or "preferred" in the WebAuthn API):

and step 1.2., which should be its logical complement, reads:

  1. Otherwise, implying the authenticator is not presently protected by some form of user verification, or the Relying Party wants to create a non-discoverable credential and not require user verification (e.g., by setting options.authenticatorSelection.userVerification to "discouraged" in the WebAuthn API), [...]

But these two statements are not logically complementary - for that, the latter should rather read "[...] AND the Relying Party [...]". Perhaps the "preferred" case should also be moved from the example in 1.1. to the example in 1.2. Because strictly speaking, there is currently a gray area where both conditions apply: when the authenticator does not have a PIN set, but the RP has set userVerification: "preferred" or "required".

I'll raise an issue in the CTAP spec about clarifying this. But for now, I don't think we can conclude that the current implementation in python-fido2 is incorrect.

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants