-
Notifications
You must be signed in to change notification settings - Fork 172
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
Add importCryptoKey input to PRF extension #1945
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6754,6 +6754,14 @@ Note: this extension may be implemented for [=authenticators=] that do not use [ | |
dictionary AuthenticationExtensionsPRFInputs { | ||
AuthenticationExtensionsPRFValues eval; | ||
record<USVString, AuthenticationExtensionsPRFValues> evalByCredential; | ||
AuthenticationExtensionsPRFImportCryptoKeyParams importCryptoKey; | ||
}; | ||
|
||
dictionary AuthenticationExtensionsPRFImportCryptoKeyParams { | ||
required KeyFormat format; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nothing but Raw can work as a format, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, at least not without preprocessing by the client. For example, I did one experiment where my application code wrapped the 32 raw bytes in a PKCS#8 header and fed it into |
||
required AlgorithmIdentifier algorithm; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to subset algorithms? HMAC is useless, right? Because nobody else could have the HMAC key. (And there's HKDF if someone wants a PRF.) RSA is out. PBKDF2 doesn't make sense? AES-* work. ECDSA and ECDH? We could define things to work with P-256 if we wanted. Are asymmetric algorithms interesting? The ability to sign things as an identity without throwing UI? The ability to public-key decrypt? I guess this would imply bringing in CryptoKeyPair. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Maybe not? See: #1946
Mostly yes, unless you import it as extractable and use
Agreed.
Signing is a bit awkward due to the lack of UI as you note, but asymmetric encryption certainly is interesting. For example, it would allow for encrypting to multiple PRF-backed keys for redundancy. This can be done today via key wrapping, but not with never-extractable keys.
Right, we could. We can't naively use the PRF directly as a private key, because the P-256 key space is slightly smaller than the full 232-1 range, so we'd need to specify some way to either fail out or recalculate some input salt in case the key falls outside the valid range. I would prefer if we could do something like that generically in WebCrypto, for example extend HKDF with asymmetric keys as output keys, but I presume it would take far more effort to get something like that accepted and rolled out in implementations. The more I've thought about this, though, the more I've come to think that non-extractability in WebCrypto perhaps isn't that impressive after all. A malicious actor that has the ability to access I still think there is value in the ability to import PRF outputs directly to On the other hand, I guess this line of reasoning also comes back around in favour of importing PRF outputs directly to asymmetric private keys - since the domain separation here actually can make truly non-extractable keys. It's still true that malicious code could abuse the resulting So okay, do you think it would make sense to specify a procedure for the client to transform the PRF output into an asymmetric key? I'm thinking in that case we should probably base it on HKDF or the like, rather than restricting ourselves to key sizes around 32 bits only. |
||
required boolean extractable; | ||
required sequence<KeyUsage> keyUsages; | ||
}; | ||
|
||
partial dictionary AuthenticationExtensionsClientInputs { | ||
|
@@ -6767,17 +6775,56 @@ Note: this extension may be implemented for [=authenticators=] that do not use [ | |
|
||
: <dfn>evalByCredential</dfn> | ||
:: A record mapping [=base64url encoding|base64url encoded=] [=credential IDs=] to PRF inputs to evaluate for that credential. Only applicable during [=assertions=] when {{PublicKeyCredentialRequestOptions/allowCredentials}} is not empty. | ||
|
||
: <dfn>importCryptoKey</dfn> | ||
:: Arguments for the [=client=] to invoke {{SubtleCrypto/importKey}}, along with the PRF result as the `keyData` argument. | ||
If present, the extension outputs will be {{Promise}}s resolving to {{CryptoKey}} values; | ||
if not present, the extension outputs will be {{BufferSource}} values. | ||
|
||
The client ensures domain separation between {{BufferSource}} and {{CryptoKey}} results, | ||
so that an extension input requesting unextractable {{CryptoKey}} values cannot be "downgraded" | ||
to requesting the same results as extractable {{BufferSource}} values. | ||
For the same reason, the client also ensures domain separation between the [TRUE] and [FALSE] values | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect the domain separation should cover all the parameters? Here's a silly example, but perhaps there are better ones: A service uses this facility to get an opaque AES-GCM key and claims that, even if the Javascript is compromised, the compromise is limited to existing ciphertexts and ciphertexts that are encrypted in the future, after the compromise is eliminated, are safe again because the key couldn't be extracted. But if they use a counter for a nonce, which is safe in general, an attacker could tweak the import parameters to get an opaque AES-ECB key and precompute Ek(x) for the x that will be used by future AES-GCM ciphertexts. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds fair. Note that WebCrypto doesn't support AES-ECB, but I suppose you could probably emulate it by feeding single blocks at a time into AES-CBC. I suppose for that we would need something like a hash of a canonical representation of the arguments - one invariant in order of |
||
of <code>{{importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/extractable}}</code>. | ||
|
||
Note: The {{KeyFormat}}, {{AlgorithmIdentifier}} and {{KeyUsage}} types are defined in [[!WebCryptoApi]]. | ||
</div> | ||
|
||
: Client extension processing ([=registration extension|registration=]) | ||
:: | ||
1. If {{AuthenticationExtensionsPRFInputs/evalByCredential}} is present, return a {{DOMException}} whose name is “{{NotSupportedError}}”. | ||
1. Set `hmac-secret` to [TRUE] in the authenticator extensions input. | ||
1. If {{AuthenticationExtensionsPRFInputs/eval}} is present and a future extension to [[FIDO-CTAP]] permits evaluation of the PRF at creation time, configure `hmac-secret` inputs accordingly: | ||
* Let `salt1` be the value of <code>SHA-256(UTF8Encode("WebAuthn PRF") || 0x00 || {{AuthenticationExtensionsPRFInputs/eval}}.{{AuthenticationExtensionsPRFValues/first}})</code>. | ||
* If <code>{{AuthenticationExtensionsPRFInputs/eval}}.{{AuthenticationExtensionsPRFValues/second}}</code> is present, let `salt2` be the value of <code>SHA-256(UTF8Encode("WebAuthn PRF") || 0x00 || {{AuthenticationExtensionsPRFInputs/eval}}.{{AuthenticationExtensionsPRFValues/second}})</code>. | ||
|
||
* Let |prefix| be a byte string as follows: | ||
* If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is not present, let |prefix| be <code>UTF8Encode("WebAuthn PRF")</code>. | ||
* If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is present and <code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/extractable}}</code> is [TRUE], let |prefix| be <code>UTF8Encode("WebAuthn PRF:CryptoKey")</code>. | ||
* If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is present and <code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/extractable}}</code> is [FALSE], let |prefix| be <code>UTF8Encode("WebAuthn PRF:CryptoKey:Extractable")</code>. | ||
* Let `salt1` be the value of <code>SHA-256(|prefix| || 0x00 || {{AuthenticationExtensionsPRFInputs/eval}}.{{AuthenticationExtensionsPRFValues/first}})</code>. | ||
* If <code>{{AuthenticationExtensionsPRFInputs/eval}}.{{AuthenticationExtensionsPRFValues/second}}</code> is present, let `salt2` be the value of <code>SHA-256(|prefix| || 0x00 || {{AuthenticationExtensionsPRFInputs/eval}}.{{AuthenticationExtensionsPRFValues/second}})</code>. | ||
1. Set {{AuthenticationExtensionsPRFOutputs/enabled}} to the value of `hmac-secret` in the authenticator extensions output. If not present, set {{AuthenticationExtensionsPRFOutputs/enabled}} to [FALSE]. | ||
1. Set {{AuthenticationExtensionsPRFOutputs/results}} to the decrypted PRF result(s), if any. | ||
1. If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is not present: | ||
1. Set {{AuthenticationExtensionsPRFOutputs/results}} to the decrypted PRF result(s), if any. | ||
|
||
1. If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is present: | ||
1. Let |firstOutput| and |secondOutput| be {{BufferSource}} values containing the respective decrypted PRF result(s), if any. | ||
1. Let |subtleCrypto| be an instance of {{SubtleCrypto}}. | ||
1. If |firstOutput| is present, set | ||
<code>{{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/first}}</code> | ||
to the result of invoking <code>|subtleCrypto|.{{SubtleCrypto/importKey}}</code> with the arguments | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/format}}</code>, | ||
|firstOutput|, | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/algorithm}}</code>, | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/extractable}}</code> and | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/keyUsages}}</code>. | ||
1. If |secondOutput| is present, set | ||
<code>{{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/second}}</code> | ||
to the result of invoking <code>|subtleCrypto|.{{SubtleCrypto/importKey}}</code> with the arguments | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/format}}</code>, | ||
|secondOutput|, | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/algorithm}}</code>, | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/extractable}}</code> and | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/keyUsages}}</code>. | ||
|
||
Note: If PRF results are obtained during [=registration=] then the [=[RP]=] MUST inspect the [=UV=] bit in the [=flags=] of the response in order to determine the correct value of {{PublicKeyCredentialRequestOptions/userVerification}} for future [=assertions=]. Otherwise results from [=assertions=] may be inconsistent with those from the [=registration=]. | ||
|
||
|
@@ -6790,10 +6837,36 @@ Note: If PRF results are obtained during [=registration=] then the [=[RP]=] MUST | |
1. If {{AuthenticationExtensionsPRFInputs/evalByCredential}} is present and [=map/exists|contains=] an [=map/entry=] whose [=map/key=] is the [=base64url encoding=] of the [=credential ID=] that will be returned, let |ev| be the [=map/value=] of that entry. | ||
1. If |ev| is null and {{AuthenticationExtensionsPRFInputs/eval}} is present, then let |ev| be the value of {{AuthenticationExtensionsPRFInputs/eval}}. | ||
1. If |ev| is not null: | ||
1. Let `salt1` be the value of <code>SHA-256(UTF8Encode("WebAuthn PRF") || 0x00 || |ev|.{{AuthenticationExtensionsPRFValues/first}})</code>. | ||
1. If <code>|ev|.{{AuthenticationExtensionsPRFValues/second}}</code> is present, let `salt2` be the value of <code>SHA-256(UTF8Encode("WebAuthn PRF") || 0x00 || |ev|.{{AuthenticationExtensionsPRFValues/second}})</code>. | ||
|
||
1. Let |prefix| be a byte string as follows: | ||
- If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is not present, let |prefix| be <code>UTF8Encode("WebAuthn PRF")</code>. | ||
- If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is present and <code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/extractable}}</code> is [TRUE], let |prefix| be <code>UTF8Encode("WebAuthn PRF:CryptoKey")</code>. | ||
- If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is present and <code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/extractable}}</code> is [FALSE], let |prefix| be <code>UTF8Encode("WebAuthn PRF:CryptoKey:Extractable")</code>. | ||
1. Let `salt1` be the value of <code>SHA-256(|prefix| || 0x00 || |ev|.{{AuthenticationExtensionsPRFValues/first}})</code>. | ||
1. If <code>|ev|.{{AuthenticationExtensionsPRFValues/second}}</code> is present, let `salt2` be the value of <code>SHA-256(|prefix| || 0x00 || |ev|.{{AuthenticationExtensionsPRFValues/second}})</code>. | ||
1. Send an `hmac-secret` extension to the [=authenticator=] using the values of `salt1` and, if set, `salt2` as the parameters of the same name in that process. | ||
1. Decrypt the extension result and set {{AuthenticationExtensionsPRFOutputs/results}} to the PRF result(s), if any. | ||
1. If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is not present: | ||
1. Set {{AuthenticationExtensionsPRFOutputs/results}} to the decrypted PRF result(s), if any. | ||
|
||
1. If {{AuthenticationExtensionsPRFInputs/importCryptoKey}} is present: | ||
1. Let |firstOutput| and |secondOutput| be {{BufferSource}} values containing the respective decrypted PRF result(s), if any. | ||
1. Let |subtleCrypto| be an instance of {{SubtleCrypto}}. | ||
1. If |firstOutput| is present, set | ||
<code>{{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/first}}</code> | ||
to the result of invoking <code>|subtleCrypto|.{{SubtleCrypto/importKey}}</code> with the arguments | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/format}}</code>, | ||
|firstOutput|, | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/algorithm}}</code>, | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/extractable}}</code> and | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/keyUsages}}</code>. | ||
1. If |secondOutput| is present, set | ||
<code>{{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/second}}</code> | ||
to the result of invoking <code>|subtleCrypto|.{{SubtleCrypto/importKey}}</code> with the arguments | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/format}}</code>, | ||
|secondOutput|, | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/algorithm}}</code>, | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/extractable}}</code> and | ||
<code>{{AuthenticationExtensionsPRFInputs/importCryptoKey}}.{{AuthenticationExtensionsPRFImportCryptoKeyParams/keyUsages}}</code>. | ||
|
||
: Authenticator extension input / processing / output | ||
:: [=prf|This extension=] uses the [[FIDO-CTAP]] `hmac-secret` extension when communicating with the authenticator. It thus does not specify any direct authenticator interaction for [=[RPS]=]. | ||
|
@@ -6802,7 +6875,12 @@ Note: If PRF results are obtained during [=registration=] then the [=[RP]=] MUST | |
:: <xmp class="idl"> | ||
dictionary AuthenticationExtensionsPRFOutputs { | ||
boolean enabled; | ||
AuthenticationExtensionsPRFValues results; | ||
(AuthenticationExtensionsPRFValues or AuthenticationExtensionsPRFCryptoKeyValues) results; | ||
}; | ||
|
||
dictionary AuthenticationExtensionsPRFCryptoKeyValues { | ||
required Promise<CryptoKey> first; | ||
Promise<CryptoKey> second; | ||
}; | ||
|
||
partial dictionary AuthenticationExtensionsClientOutputs { | ||
|
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
Sorry, something went wrong.