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

Please provide / document a method for controlling whether a PIN is requested #246

Open
tmo1 opened this issue Jan 24, 2025 · 4 comments
Open

Comments

@tmo1
Copy link

tmo1 commented Jan 24, 2025

I've been experimenting with the provided example code (credential.py, hmac-secret.py) with a Yubico Security Key running firmware 5.4.3 (with support for 'U2F_V2', 'FIDO_2_0', and 'FIDO_2_1_PRE') and a PIN configured, and no matter what I try, I can't control when I'm asked for a PIN and when I'm not - make_credential always asks for a PIN, and get_assertion never does, no matter what options I try to set.

@dainnilsson
Copy link
Member

This behavior is defined by the WebAuthn and CTAP2 specification, and thoroughly documented there. A 5.4 YubiKey with a PIN set will always require PIN verification when creating a new credential. Whether or not you are prompted for PIN when going an assertion depends on the value of userVerification parameter as described here https://www.w3.org/TR/webauthn-2/#dictdef-authenticatorselectioncriteria

Note that the UserInteraction implementation in the example code caches the PIN, so you will not be prompted for it more than once.

@tmo1
Copy link
Author

tmo1 commented Jan 24, 2025

Thank you for the help.

This behavior is defined by the WebAuthn and CTAP2 specification, and thoroughly documented there.

Can you be more specific?

A 5.4 YubiKey with a PIN set will always require PIN verification when creating a new credential

Is this per FIDO spec, or a Yubico 5.4 implementation detail? If the latter, is it documented somewhere?

Whether or not you are prompted for PIN when going an assertion depends on the value of userVerification parameter as described here https://www.w3.org/TR/webauthn-2/#dictdef-authenticatorselectioncriteria

That's Webauthn - we're talking about pure CTAP.

The fido2-assert CLI tool, for example, has the -v option to "prompt the user for a PIN and request user verification from the authenticator." I'm trying to understand whether there's any similar option that can be set / unset when using the Python bindings.

@dainnilsson
Copy link
Member

If you're using the Fido2Client class then we're not just talking CTAP, we're also talking WebAuthn. The client implements a WebAuthn-like client and is intended to for the most part act as a WebAuthn-capable browser would. If you want to use pure CTAP2, then you'll need to use the Ctap2 class directly instead. The Fido2Client takes the Options-objects defined in WebAuthn, and will respect the userVerification parameter.

The specific requirement of PIN for YK 5.4 comes from CTAP2 where make credential always requires UV, if configured up until FIDO_2_1 versioned authenticators where makeCredUvNotRqd was introduced. It's not tied the the 5.4 version number per se, it's the versions of the CTAP spec it implements.

@tmo1
Copy link
Author

tmo1 commented Jan 28, 2025

Thanks much for the help.

If you're using the Fido2Client class then we're not just talking CTAP, we're also talking WebAuthn. The client implements a WebAuthn-like client and is intended to for the most part act as a WebAuthn-capable browser would. If you want to use pure CTAP2, then you'll need to use the Ctap2 class directly instead.

Ah, okay. So I suppose that that's why Fido2Client seems to have no option to suppress user presence checking, since that's not allowed in WebAuthn. I've actually already implemented a largely working local encryption application using python-fido2 and the Fido2Client class, but I suppose that if I want to suppress the UP checks, I'll have to rewrite it using the Ctap2 class directly.

The Fido2Client takes the Options-objects defined in WebAuthn, and will respect the userVerification parameter

I was making some embarassing mistakes in passing the parameter properly, but I have it working now. For anyone who comes across this, here's an example:

result = client.get_assertion({"rpId": "example.com", "challenge": os.urandom(12), "allowCredentials": allow_list,
                                   "userVerification": UserVerificationRequirement.DISCOURAGED,
                                   "extensions": {"hmacGetSecret": salts}}).get_response(0)

I still haven't been able to figure out the proper format of a Ctap2.get_assertion call to get an hmac-secret. I'm doing:

result = client.get_assertion("example.com", ccd.hash, allow_list, {"hmacGetSecret": salts}, {"up": False})

which executes without error, but result.auth_data.extensions is None. Am I doing something obviously wrong?

Edit: I realized that the spec requires UP for use of the hmac-secret extension ("If "up" is set to false, authenticator returns CTAP2_ERR_UNSUPPORTED_OPTION."), so I guess there's no reason not to just stick with Fido2Client.

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

2 participants