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

devicePubKey → supplementalPubKeys #1957

Merged
merged 11 commits into from
Nov 15, 2023
Merged

devicePubKey → supplementalPubKeys #1957

merged 11 commits into from
Nov 15, 2023

Conversation

agl
Copy link
Contributor

@agl agl commented Sep 3, 2023

This change removes the devicePubKey extension but adds the very similar supplementalPubKeys extension. The major difference between the two is that the latter allows for one or two supplemental keys, and while supplemental keys can be device bound, they can also have "provider" scope, which is defined by their attestation statement.

Added by @timcappalli:

Here is a visual that (hopefully) helps to depict how this all comes together. This diagram assumes that the RP has requested both a provider SPK and device SPK, and that the passkey provider supports.

WebAuthn-SPK-Visual-Oct23


Preview | Diff

@maxhata
Copy link

maxhata commented Sep 4, 2023

What kind of "supplemental keys" will this be used for ?

@agl
Copy link
Contributor Author

agl commented Sep 4, 2023

What kind of "supplemental keys" will this be used for ?

The PR is rather extensive, but it does have updated examples if you'll permit me just to quote them:

A usage example is thus:

A sign-in request is received by a website that, by regulation, must require certain authentication standards. The sign-in is done with a [=multi-device credential=], but also includes a supplemental key with an attestation that states that the supplemental key is only synced after a user has met or exceeded those standards. Since that supplemental key has been seen before, and was initially verified to meet the site's authentication standards, additional sign-in challenges are not required.

Another example of supplemental keys:

Say that a sign-in request appears at a website along with some geolocation signal that has not been seen for this [=user account=] before, and is outside of the typical usage hours observed for the account. The risk may be deemed high enough not to allow the request, even with an assertion by a [=multi-device credential=] on its own. But if a signature from a supplimental key that is device-bound, and that is well established for this user can also be presented, then that may tip the balance.

@maxhata
Copy link

maxhata commented Sep 5, 2023

Thanks for the explanation!

@arianvp
Copy link

arianvp commented Sep 5, 2023

A sign-in request is received by a website that, by regulation, must require certain authentication standards. The sign-in is done with a [=multi-device credential=], but also includes a supplemental key with an attestation that states that the supplemental key is only synced after a user has met or exceeded those standards. Since that supplemental key has been seen before, and was initially verified to meet the site's authentication standards, additional sign-in challenges are not required.

Couldn't this be solved by assertion-time attestation? "Hey I didn't get attestation during registration; please prove this key is hardware bound; my policy changed" sounds simpler from an implementation-POV

@agl
Copy link
Contributor Author

agl commented Sep 5, 2023

Couldn't this be solved by assertion-time attestation? "Hey I didn't get attestation during registration; please prove this key is hardware bound; my policy changed" sounds simpler from an implementation-POV

Assertion-time attestation is for the primary key, but the primary key might not be hardware bound, nor have the other properties that sites are interested in.

Also, assertion-time attestation closes over the challenge, meaning that it has to be calculated for every assertion. The assertion for a supplemental key can be fixed and cached. (Doing an online assertion for every sign-in is a little fraught from both privacy and reliability perspectives. If a site wants a remote assertion for every sign in, they might want federation.)

@ve7jtb
Copy link
Contributor

ve7jtb commented Sep 5, 2023

This raises the question of if we want to keep the added complexity of attestation over the challenge for the primary key on get?
The key is either hardware bound, so it won't change from makeCredential, or effectively unbound and can come from anyone's device or account making attestation duplicative with the proposed supplementalPubKeys extension. On getAssertion it is the supplementalPubKeys extension that carries the useful info. I think I would rather roll back the attestation on get, and allow all authenticators to return the supplementalPubKeys extension. If the primary key is hardware bound a security key could return values in the extension that would help the RP understand that or at least not throw up risk signals.

@arianvp
Copy link

arianvp commented Sep 5, 2023

I'm not really understanding the usecase why you would have multiple keys per credential. It sounds complicated. Why not ask the user to register a new credential that does satisfy the assurance levels if their old one doesn't satisfy the new assurance level anymore?

@agl
Copy link
Contributor Author

agl commented Sep 5, 2023

I think I would rather roll back the attestation on get, and allow all authenticators to return the supplementalPubKeys extension.

I don't think there are any implementations of assertion-time attestation at the moment so I'm fine with that. But I think some enterprise-focused passkey providers might want to put device integrity signals in there and thus do want to close over the challenge.

I'm not really understanding the usecase why you would have multiple keys per credential. It sounds complicated.

Multi-device credentials currently don't have any attestation and, for the vast majority of cases, that is sufficient. If you're worrying about supplemental keys you should probably be a bank with a large fraud and compliance department, otherwise you're probably making a mistake. So, in some sense, the complexity might be a useful guide for people.

But, in that context, the thinking roughly goes that, as a bank, there's only so much that you can depend on a passkey for: you may be subject to inflexible requirements about authentication in particular regions. So while you might accept a passkey for sign-in, an additional signal is needed before allowing, say, money transfers. So if we can staple a device-bound signal to a passkey perhaps you can avoid bothering the user with additional factors quite so often if you have built up trust in the device.

This was the motivation for devicePubKey.

But that leaves a gap when the user is on a new device. A passkey provider likely has useful information about how the user authenticated themselves on that new device but we don't want to tie that to the primary key because you'll probably see import/export interoperability start to appear between passkey providers in the coming year.

Thus we add another key, a provider-scoped supplemental key. The basic idea is the same as with a devicePubKey, but it might signal something like “this supplemental key is only synced after a specified level of 2FA authentication has been performed”. (Where the definition and evaluation of that claim is probably deferred to FIDO in many cases.)

So it's possible that an assertion comes with three keys (primary, provider-scoped, device-bound). Some providers will likely skip one of the latter two even if they support supplemental public keys at all. That's a lot and the vast majority of RPs should never bother with any of this. But sign-ins to regulated sectors account for a lot of sign-ins because they make people sign-in so often.

@arianvp
Copy link

arianvp commented Sep 6, 2023

I understand the use-case now.

I have some thoughts on solving this another way though. Maybe this doesn't belong as a discussion on this PR. We can move this discusison elsewhere.

The central problem here is that "multi-device credentials do not have attestation because they're not device-bound".
I'm curious if it can be solved without touching the spec and moving the whole extra key business to the sync fabric of the passkey provider.

I think device-bound keys are compatible with moving between secure elements. HSMs and TPMs have supported this usecase for ages using key-wrapping.

For example:

  1. User Create a passkey on HooliPhone A and stores it in the secure element.
  2. User buys a HooliPad B and logs into it with their Hooli account. user needs to confirm the sign in with their HooliPhone A. HooliPad generates an assymetric key-wrapping key and uploads the public part to Hooli servers.
  3. HooliPhone A downloads HooliPad B's key-wrapping key and encrypts the users' Passkeys with them in the secure element.
  4. HooliPad B downloads the key-wrapped passkeys that HooliPhone A uploaded and unwraps them in the secure element. Because the key-wrapping key's private key never leaves HooliPad B's secure element we're sure that the Passkey never ends up unencrypted on Hooli's servers and is only present on the user's device's secure elements
  5. The passkey is now in the Hoolipad's secure element.
  6. We can attest that the passkey is on the Hoolipad using an assertion-time attestation.

The key-wrapping and unwrapping is part of the sync-fabric between hooli devices and hooli servers. The RP doesn't have to care. It also has the added benefit that we cryptographically know that Hooli can not access the Passkeys in unencrypted form.

Reason why I want to move this logic into the Passkey sync fabric is to keep implementation for RPs easy. If we drop the notion of "multi-device credentials aren't device-bound" I think this simplifies things for RPs.

@nadalin nadalin added this to the L3-WD-01 milestone Sep 6, 2023
@agl
Copy link
Contributor Author

agl commented Sep 6, 2023

Reason why I want to move this logic into the Passkey sync fabric is to keep implementation for RPs easy.

We continue to strongly recommend that regular sites use passkeys as is and don't bother with any supplemental key stuff.

Given that, your suggestion to do imports into enclaves is a good one in general(*) for security. (Although with the caveat that, taking two major platforms as examples, Android Keystore doesn't support the import of asymmetric keys at all, and Apple still supports devices that don't have a secure enclave.)

In your example, the provider is the one that seemingly blesses the import/export operation. The provider will need to authenticate the new device before instructing the old one to move keys into it and the details of that level of authentication are what needs to be included in the attestation. E.g. if the provider always does two-factor authentication, that's the sort of fact that sites may be able to use.

Now, to motivate using multiple keys:

We can't call keys that can be exported from a device "device bound", because they aren't! Storing them in the enclave protects against local attacks, which is great, but a site that wants device bound properties for compliance reasons isn't satisfied by any key that the provider can cause to be exported, even if it can only be moved to another hardware enclave.

So, to meet any device-bound requirements we need a second key.

And, if the provider is going to build a system that only moves keys between devices based on its attested two-factor authentication, then that precludes ever supporting moving passkeys between providers. But we don't want to preclude that because we don't want to exclude 3rd-party providers. And thus we split that key off and we end up with the supplementalPubKeys design.

Adam Langley added 2 commits September 6, 2023 16:01
This extension is very similar the previous `devicePubKeys` extension.
The difference is that `supplementalPubKey` allows for one _or two_
supplemental keys, and while supplemental keys can be device bound, they
can also have "provider" scope, which is defined by their attestation
statement.
@agl
Copy link
Contributor Author

agl commented Sep 6, 2023

From the call of 2023-09-06, there was a request to split this change into a) a change that removed devicePubKey and b) a change that added supplementalPubKeys. I've done so and so there are two commits. By fiddling with the diff settings, you can see the either the change from devicePubKey to supplementalPubKeys, or else a wall of green text that adds supplementalPubKeys.

@timcappalli timcappalli requested review from akshayku and removed request for ve7jtb September 11, 2023 07:54
@nadalin nadalin requested review from ve7jtb and akshayku and removed request for akshayku September 11, 2023 07:54
@sbweeden
Copy link
Contributor

Are there requirements for an RP to know if the user in control of the credential has changed? I know that in native mobile apps its typically possible to get signals when enrolled biometrics changes, and in some cases apps require reauthentication (i.e. re-identity-proofing) when such a thing happens. If so, is it practical or possible for a provider to signal whether or not, from that provider's perspective, the user account (belonging to the passkey provider) exercising the credential has changed?

The point here is, are we satisfying real RP policy requirements with the proposals in this extension.

@nadalin nadalin requested review from MasterKale and removed request for MasterKale September 11, 2023 08:48
@ve7jtb
Copy link
Contributor

ve7jtb commented Sep 11, 2023

@sbweeden I wasn't thinking of signaling a change of control based on device biometrics.
I was thinking that the provider key would not be exported to a different subscriber account within the same provider.

I think that explicitly moving the credential into another subscribers account should not carry with it the provider scope key. Or if it is carried arcoss we need a user scope where the supplemental key is unique to the combination of Account, RP, Provider.

I think that a lot of RP will be forced via regulation to do some additional step up each login if they can't detect a change of subscriber account.

@@ -7274,8 +7274,6 @@ The weight that [=[RPS]=] give to the presence of a signature from a supplementa
:: For both [=authenticatorMakeCredential=] and [=authenticatorGetAssertion=] operations:
1. Create or select the [=public key credential source=] as usual (see [[#sctn-op-make-cred]], or [[#sctn-op-get-assertion]] as appropriate).

1. If the [=public key credential source=] is not [=backup eligible=] then terminate these processing steps: this extension only applies to [=multi-device credentials=].

1. Let |scopes| be the [=set=] of all supplemental public key scopes that the [=authenticator=] supports. Updates |scopes| to be the [=set/intersection=] of itself and {{AuthenticationExtensionsSupplementalPublicKeysInputs/scopes}}. If |scopes| is empty, terminate these processing steps with no extension output.

1. Let |spks| and |spkSigs| be empty arrays.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit looks good

index.bs Outdated

Then, if this same user credential is copied to a different [=authenticator=] ("authenticator&nbsp;2"), the [=[RP]=]'s first {{CredentialsContainer/get()|get()}} call on "authenticator&nbsp;2" (that includes the `devicePubKey` extension) will produce an [=assertion=] including a signature by a <i>new</i> [=device-bound key=] ("dpk&nbsp;2"). Note that such a [=multi-device credential=] can be exercised on "authenticator&nbsp;2" without a {{CredentialsContainer/create()|create()}} having been performed on "authenticator&nbsp;2". The [=[RP]=]'s subsequent {{CredentialsContainer/get()|get()}} calls on "authenticator&nbsp;2", using the `devicePubKey` extension and the same user credential, yield further signatures by "dpk&nbsp;2".
When a [=[RP]=] uses the `supplementalPubKeys` extension with a {{CredentialsContainer/create()|create()}} call to create a new [=user credential=], a signature by one or more new supplemental keys may be returned along with the new supplemental public keys themselves. For the sake of example, assume that a single supplemental public key is returned, called &ldquo;SPK1&rdquo;. For as long as the [=user credential=] is exercised within the scope of the supplemental key, the [=[RP]=]'s subsequent {{CredentialsContainer/get()|get()}} operations for that credential will generate [=assertions=] including further signatures by &ldquo;SPK1&rdquo;.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be "may generate [=assertions=]"? instead of will?

There is no guarantee that the authenticator will continue generating an SPK, is there? Or this is something we should explicitly define?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with "will likely", how does that feel? I thought that "may" was too soft because we want to communicate that sites should generally expect that an SPK will persist, although you're correct that there will be mechanisms to reset them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WFM!

(Addressing Tim's comment.)
Copy link
Member

@timcappalli timcappalli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@emlun emlun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay!

index.bs Outdated Show resolved Hide resolved
index.bs Outdated Show resolved Hide resolved
index.bs Outdated Show resolved Hide resolved
index.bs Outdated Show resolved Hide resolved
index.bs Outdated Show resolved Hide resolved
index.bs Outdated Show resolved Hide resolved
index.bs Outdated Show resolved Hide resolved
index.bs Outdated Show resolved Hide resolved
index.bs Outdated Show resolved Hide resolved
index.bs Show resolved Hide resolved
agl and others added 3 commits November 15, 2023 10:20
Co-authored-by: Emil Lundberg <emil@yubico.com>
The extension name uses "PubKeys" but some of the structures said
"PublicKeys". Be consistent.
index.bs Outdated Show resolved Hide resolved
Copy link
Member

@emlun emlun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @agl for your patience!

@agl agl merged commit f8163ea into main Nov 15, 2023
2 checks passed
@agl agl deleted the spk branch November 15, 2023 22:20
github-actions bot added a commit that referenced this pull request Nov 15, 2023
SHA: f8163ea
Reason: push, by agl

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actions bot added a commit to VoltrexKeyva/webauthn that referenced this pull request Nov 17, 2023
SHA: f8163ea
Reason: push, by VoltrexKeyva

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
github-actions bot added a commit to nsatragno/webauthn that referenced this pull request Nov 20, 2023
SHA: f8163ea
Reason: push, by nsatragno

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.