The pseudo-random function extension does not belong in PublicKeyCredential or in Web Authentication’s specification, and should be moved into another document with its own methods extending the Web Cryptography API.
The mechanism is useful, but I don’t see a reason to make it carry the baggage of WebAuthn simply because it’s relies on the CTAP2 wire protocol. As we always postulated in the early days of this working group, WebAuthn could use additional authenticator interface mechanisms some day. Similarly, a mechanism for obtaining a hardware-backed origin-bound, rotatable secret key could be extended in the future independent of CTAP2.
Let’s first look at the extension as defined in the current working draft. My password manager, “Honest J.C.’s Recycled Passwords”, wants to do client-side encryption of its password database. Let’s assume that WebAuthn is already used for server-side enrollment and authentication.
The developer of the password database now seeks to add client-side hardware-backed encryption. Regardless of whether or not the user has already obtained a WebAuthn credential for their account, the developer, seeking a secret key, needs to construct a publicKey-type create request:
navigator.credentials.create({ publicKey: createRequest })
In that createRequest they must decide what public key type they want to use.
"pubKeyCredParams": [
{
"alg": -7,
"type": "public-key"
}
],
What does this have to do with the secret key? Will the signature be important for the symmetric encryption later? The developer of “Honest J.C.’s Recycled Passwords” is not a cryptographer. They want to get a 256-bit key they can pass into WebCrypto, but what are they supposed to do with the public key after the create? Perhaps they store it in the database alongside the hmac salt inputs because maybe it’s important?
Now they encrypt the database with the key they obtained. But they have this signature operation, too. Always good to have more crypto, they think.
But now we’re into bespoke verification mechanisms rather than something more like an AEAD, and in our best case the excess data leads the developer of “Honest J.C.’s Recycled Passwords” towards passing the ciphertext into subsequent calls to navigator.credentials.get() that seek to re-derive the secret key.
In the worst case, they design something too clever, perhaps utilizing the current database ciphertext as the challenge field for all login authentication, so they only have to call navigator.credentials.get once. That’s more efficient, right? The key won’t decrypt if the user isn’t authentic. What does it matter if the database didn’t change between invocations?
I think the WebAuthn pieces of this get very in-the-way, and we’ll probably be dealing with confusion around the extension for the foreseeable future if we continue down this path.
Let’s imagine this more focused on the application developer. The wants are:
hmac-secret commandLet’s imagine a document that defines a new WebCrypto Algorithm, “Hardware-Backed Secret”, and some new methods:
partial interface SubtleCrypto {
Promise<HardwareBackedSecretAttestationResponse> createHardwareSecret(...)
Promise<HardwareBackedSecret> getHardwareSecret(...)
}
The resulting objects can omit the signature but still return the attestation and the attested client data:
[SecureContext, Exposed=Window]
interface HardwareBackedSecret {
required ArrayBuffer first;
ArrayBuffer second;
[SameObject] readonly attribute ArrayBuffer rawSecretKeyId;
[SameObject] readonly attribute ArrayBuffer clientDataJSON;
SecretKeyExtensionsClientOutputs getClientExtensionResults();
// Probably others, when we get to the full analysis... but the point is that
// it is doable, and this capability is worthwhile enough to be worth doing
// properly.
};
[SecureContext, Exposed=Window]
interface HardwareBackedSecretAttestationResponse : HardwareBackedSecret {
[SameObject] readonly attribute ArrayBuffer attestationObject;
sequence<DOMString> getTransports();
ArrayBuffer getAuthenticatorData();
};
The input parameters would follow the same pattern, shaving off anything unnecessary to the secrets management. We can also craft them to inherit from Algorithm, to allow use via the existing deriveBits and deriveKey methods. For those methods, one might not be able to handle the optional Second buffer for key rollover operations, but the new get/create hardware methods would be compatible with credentials produced via the derive... methods, in the event a developer needed to get more sophisticated with time.
dictionary HardwareBackedSecretParams : Algorithm {
required SecretKeyRpEntity rp;
unsigned long timeout;
AuthenticatorSelectionCriteria authenticatorSelection;
SecretKeyExtensionsClientInputs extensions;
};
Creation of new secret key types uses a dictionary that includes the necessary WebAuthn types, but can forego things irrelevant to its use case, perhaps even the user account information:
dictionary HardwareBackedSecretCreateParams : HardwareBackedSecretParams {
DOMString attestation = "none";
sequence<SecretKeyDescriptor> excludeCredentials = [];
}
dictionary HardwareBackedSecretGetParams : HardwareBackedSecretParams {
sequence<SecretKeyDescriptor> includeCredentials = [];
};
dictionary SecretKeyDescriptor {
required BufferSource secretKeyId;
sequence<DOMString> transports;
};
At the User Agent level, utilizing the hmac-secret CTAP2 feature would ingest data not supplied here (challenge, notably!), and similarly would produce signatures that would not be emitted. The specification would define how the user agent behaves with that data to make it safe.
But the resulting methods become more apparent to the application developer, and the extraneous bits won’t be there to confuse them into misuse, directly.
A powerful reason to make the hardware-backed encryption key mechanism stand on its own is letting it be further extended without dragging along the baggage of WebAuthn’s extensions mechanism.
Just from brief conversations about using authenticators as encryption sources, there’s desire for mechanisms like M-of-N secret sharing/splits, which would be very cumbersome to try and implement atop of an extension atop a WebAuthn extension, but could be added as their _own_ extensions into calls of crypto.subtle.getHardwareSecret(...).
I know we all want to get WebAuthn Level 2 finalized. I think we should remove this extension from the draft and introduce it as its own document, either inside the WebAuthn WG if we believe the charter supports it (and if not, perhaps that’s another argument against the extension!), in a new Web Crypto Community Group, or perhaps at the WICG. Then taking the above as a starting point, we construct a document that incorporates the Authenticator Model, Attestation Formats, fingerprinting and tracking mitigations, and other necessary sections by reference, and introduce that on its own publication schedule. And then we can do this right.
I'm going to condense your points quite considerably here when referencing them but I'm not intending to strawman them, so if you believe that I've missed the mark please take that as an honest misunderstanding and let me know. (I'm also going to reorder them to put what I think are more minor points later.)
This should be done in WebCrypto instead.
The user-presence and user-verification properties of the protection offered by the PRF extension are valuable, but they're also expensive because they require the human at the computer to touch, enter a PIN, or do a fingerprint read. If getting hardware-backed keys is a separate step then we force sites to choose between strong sign-in or strong key protection, because to get both you have the terrible experience of making people do all that twice in a row.
If a site is happy to do make people do an extra ceremony to get encryption keys then they can already get hardware-backed keys from WebAuthn, although I fear that you'll target that if I point out how. But the PRF extension is largely a convenience to merge two ceremonies into one.
Moving this to WebCrypto also means importing half of WebAuthn into WebCrypto. WebAuthn is problematically complicated already, forking half of it and putting it into WebCrypto would be very sad. It would also then diverge and I expect sites that wanted to use both would end up in situations where only WebAuthn worked for them because WebCrypto hadn't caught up in some respect.
People will be confused that they need to use
publicKeymembers and that they get signatures back when all they want is the symmetric key.
I see this is a _positive_. People should feel a little odd about all the extra machinery if they're ignoring it. What should they use the signature for? They should use it for _logging in_! I have no problem nudging people, who are using the backend machinery anyway for key protection, to also protect sign-ins. If there are people in the set of those who want hardware-backed key protection, but love phishing, then they're hardly over-burdened: they have to ignore some asymmetric cryptography terms and some extra struct members. Seems like a weak nudge, honestly.
“It relies on the CTAP2 wire protocol”
Obviously it was designed to be able to work _with_ CTAP2, as a reflection of the amount of compatible hardware. But the PRF extension already includes provision for evaluation at credential-creation time that cannot be implemented on CTAP2, but which can be implemented over other transports, and which we hope(d) to do.
It'll be easier to extend in WebCrypto
I think the PRF extension provides a flexible building block, and I'm not sure what such extensions could look like. M-of-N shares? Encrypt the shares as you would any other data, using PRF-supplied keys, and combine using Shamir's scheme.
The extension is already in CTAP2.0 this allows extending existing functionality to a RP. While we could perhaps look at extending web crypto, I think this logically should be part of CTAP/WebAuthn.
I know the SIOP (Self Issued OP) used in SSI have been waiting for this.
It also needs to be extended to native apps over the windows/Android API.
Perhaps if this was limited only to platform authenticators then having as part of WebCrypto would make more sense.
JC I would also note that your examples are in the browser. I think the main use for this would be for SaaS service RP who are also authenticating the user, or native apps. The key provided from a roaming authenticator can be used to bootstrap new devices securely.
If we are focusing only on JS apps in the browser then yes WebCrypto would be the way to go I suspect.
This work is already done and in the WebAuthn spec. Let's leave it there.
I will be producing a PR adding WebCrypto accessor methods to the current extension, but such a PR does not necessarily satisfy Mozilla's whole concerns regarding the extension. I'm working to better articulate those.
Excited to learn of this work.... I've been waiting for crypto.subtle.getHardwareSecret(...) and crypto.subtle.getHardwareSignature(...)... I would have preferred to see a vanilla hardware backed crypto API before seeing an authentication API, but sounds like raw encryption will be supported before raw signatures.... a use case for this api would be support for "encrypted data vaults" or "personal data stores" in the SSI space.
There are many valuable use cases, especially for Password Managers.
I think it's also a positive to have this part of the authentication protocol, instead of a separate one. In our use cases, it would not be user friendly if the users need to tap twice their keys due to the separations of authentication and encryption, there should be a way to combine those.
We support Adam's work with the PRF extension 100%.
@jcjones, the accessor method approach could be also interesting later on. What are you proposing?
In our use cases, it would not be user friendly if the users need to tap twice their keys due to the separations of authentication and encryption, there should be a way to combine those.
For what it's worth: although WebAuthn always requires user presence, CTAP does allow for authentication operations without user presence. So there still is the possibility for the new API to not require a second tap (but I cannot promise that will be the case).
The problem is currently that authenticators don't respond with the HMAC extension without UP.
In principal we should change that so that it always replies but has different seeds for "uv=1" , uv=0 and up=1, and uv=0 and up=0. That probably makes sense for iOT applications as well.
I personally see this fitting with the WebAuthn flow for SSI and SIOP applications as well as password managers. I don't think that is mutually exclusive with allowing webCrypto access. I would like to see both.
The extension was removed in #1478, and we now plan to work this into a Working Group Note.
closed by merging pr #1481
On 2020-09-16 call: re-opening this because it's now focusing on reminding us to create a "PRF extension WG Note"
On 2020-09-16 call: re-opening this because it's now focusing on reminding us to create a "PRF extension WG Note"
has this wg note been created yet?