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

PRF extension. #1424

Merged
merged 12 commits into from
Jul 1, 2020
87 changes: 87 additions & 0 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -5638,6 +5638,93 @@ At this time, one [=credential property=] is defined: the [=resident key credent
: Authenticator extension output
:: None.

## Pseudo-random function extension (<dfn>prf</dfn>) ## {#prf-extension}

This [=client extension|client=] [=registration extension=] and [=authentication extension=] allows a [=[RP]=] to evaluate outputs from one of two secret, pseudo-random functions (PRFs) associated with a [=credential=]. A pseudo-random function, F, is such that, given x<sub>1</sub>, x<sub>2</sub>, &hellip; x<sub>n</sub>, and F(x<sub>1</sub>), F(x<sub>2</sub>), &hellip; F(x<sub>n</sub>), no adversary can predict any bit of F(x<sub>m</sub>) for any m &gt; n. The PRFs provided by this extension map from {{DOMString}}s of any length to 32-byte {{ArrayBuffer}}s.
agl marked this conversation as resolved.
Show resolved Hide resolved

If a [=credential=] is created on an [=authenticator=] that supports this extension then two fresh PRFs are associated with it. One is evaluated when [=user verification=] is performed and the other is evaluated when [=user verification=] is not performed. It is the responsibility of the [=[RP]=] to set the {{PublicKeyCredentialRequestOptions/userVerification}} parameter accordingly.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
If a [=credential=] is created on an [=authenticator=] that supports this extension then two fresh PRFs are associated with it. One is evaluated when [=user verification=] is performed and the other is evaluated when [=user verification=] is not performed. It is the responsibility of the [=[RP]=] to set the {{PublicKeyCredentialRequestOptions/userVerification}} parameter accordingly.
If a [=credential=] is created on an [=authenticator=] that supports this extension then two freshly-seeded PRFs are associated with it. Upon subsequent invocations of {{CredentialsContainer/get()|navigator.credentials.get()}}, one PRF is evaluated if [=user verification=] is performed and the other is evaluated if only a [=test of user presence|user presence test=] is performed. It is the responsibility of the [=[RP]=] to set the {{PublicKeyCredentialRequestOptions/userVerification}} parameter accordingly: [=[RPS]=] SHOULD _consistently_ use either of the "Required" or "Discouraged" values of {{PublicKeyCredentialRequestOptions/userVerification}} when evaluating the PRF, and SHOULD NOT use "Preferred". Regarding the prior "consistently" remark: if an RP uses "Required" to obtain a PRF output that is subsequently used as a key to encrypt some data, and then later obtains a PRF output using "Discouraged" (using the same salt value as before), the PRF output will be different and they will be unable to decrypt the data.

(am rushing a bit here, so the last sentence of the above is not as polished as I'd otherwise like...)

see also https://github.com/fido-alliance/fido-2-specs/issues/873

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Integrated, but have changed some wording. PTAL.

And thanks for https://github.com/fido-alliance/fido-2-specs/issues/873. I didn't notice that CTAP2.0 was different and I've updated the wording here accordingly.


As a motivating example, PRF outputs could be used as symmetric keys to encrypt user data. Such encrypted data would be inaccessible without the ability to get assertions from the associated [=credential=]. By using the provision below to evaluate the PRF at two points in a single [=assertion=] operation, the encryption key could be periodically rotated during [=assertions=] without additional user interaction. If the evaluation inputs were made unpredictable then even an attacker who could satisfy [=user verification=], and who had time-limited access to the authenticator, could not learn the encryption key without also knowing the correct PRF input.
agl marked this conversation as resolved.
Show resolved Hide resolved

This extension is implemented on top of the [[FIDO-CTAP]] `hmac-secret` extension. It is a separate [=client extension=] because `hmac-secret` requires that inputs and outputs be encrypted in a manner that only the user agent can perform, and to provide domain separation between uses by WebAuthn and any uses by the underlying platform. The latter is achieved by hashing the provided PRF inputs with a context string to prevent evaluation of the PRFs for arbitrary inputs.
agl marked this conversation as resolved.
Show resolved Hide resolved

Note: this extension may be implemented for [=authenticators=] that do not use [[FIDO-CTAP]] so long as the behavior observed by a [=[RP]=] is identical.

: Extension identifier
:: `prf`

: Operation applicability
:: [=registration extension|Registration=] and [=authentication extension|authentication=]

: Client extension input ([=registration extension|registration=])
agl marked this conversation as resolved.
Show resolved Hide resolved
:: <xmp class="idl">
partial dictionary AuthenticationExtensionsClientInputs {
boolean prfEnable;
};
</xmp>

<div dfn-type="dict-member" dfn-for="AuthenticationExtensionsClientInputs">
: <dfn>prfEnable</dfn>
:: If true, specifies that two PRFs should be associated with the created credential.
agl marked this conversation as resolved.
Show resolved Hide resolved
</div>

: Client extension processing ([=registration extension|registration=])
::
1. If {{AuthenticationExtensionsClientInputs/prfEnable}} is present and has the value `true`:
1. Set `hmac-secret` to `true` in the authenticator extensions input.
1. Set {{AuthenticationExtensionsClientOutputs/prfEnabled}} to the value of `hmac-secret` in the authenticator extensions output. If not present, set {{AuthenticationExtensionsClientOutputs/prfEnabled}} to false.
1. Else:
1. Set {{AuthenticationExtensionsClientOutputs/prfEnabled}} to false.
agl marked this conversation as resolved.
Show resolved Hide resolved

: Client extension output ([=registration extension|registration=])
agl marked this conversation as resolved.
Show resolved Hide resolved
:: <xmp class="idl">
partial dictionary AuthenticationExtensionsClientOutputs {
boolean prfEnabled;
};
</xmp>

<div dfn-type="dict-member" dfn-for="AuthenticationExtensionsClientOutputs">
: <dfn>prfEnabled</dfn>
:: True iff the two PRFs are available for use with the created credential.
agl marked this conversation as resolved.
Show resolved Hide resolved
</div>

Note: it is not possible to evaluate a PRF during [=registration=] due to the design of the [[FIDO-CTAP]] `hmac-secret` extension.

: Client extension input ([=authentication extension|authentication=])
:: <xmp class="idl">
partial dictionary AuthenticationExtensionsClientInputs {
sequence<DOMString> prfEval;
};
</xmp>

<div dfn-type="dict-member" dfn-for="AuthenticationExtensionsClientInputs">
: <dfn>prfEnable</dfn>
:: A sequence of one or two strings that are inputs on which to evaluate the PRF.
agl marked this conversation as resolved.
Show resolved Hide resolved
</div>

: Client extension processing ([=authentication extension|authentication=])
::
1. If {{AuthenticationExtensionsClientInputs/prfEval}} is present and non-empty:
1. If the length of {{AuthenticationExtensionsClientInputs/prfEval}} is greater than two, return a {{DOMException}} whose name is “{{NotSupportedError}}”.
1. Let `salt1` be the value of <kbd>SHA-256(UTF8Encode("WebAuthn PRF\x00" + {{AuthenticationExtensionsClientInputs/prfEval}}[0]))</kbd>.
agl marked this conversation as resolved.
Show resolved Hide resolved
agl marked this conversation as resolved.
Show resolved Hide resolved
1. If the length of {{AuthenticationExtensionsClientInputs/prfEval}} is two, let `salt2` be the value of <kbd>SHA-256(UTF8Encode("WebAuthn PRF\x00" + {{AuthenticationExtensionsClientInputs/prfEval}}[1]))</kbd>.
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 {{AuthenticationExtensionsClientOutputs/prfOutputs}} to be a one- or two-element sequence containing the result(s).

: Client extension output ([=authentication extension|authentication=])
:: <xmp class="idl">
partial dictionary AuthenticationExtensionsClientOutputs {
sequence<ArrayBuffer> prfOutputs;
agl marked this conversation as resolved.
Show resolved Hide resolved
};
</xmp>

<div dfn-type="dict-member" dfn-for="AuthenticationExtensionsClientOutputs">
: <dfn>prfOutputs</dfn>
:: A sequence, which is the same length as {{AuthenticationExtensionsClientInputs/prfEval}}, containing the 32-byte output(s) of the PRF.
</div>

: Authenticator extension input / processing / output
:: 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]=].

# User Agent Automation # {#sctn-automation}

Expand Down