diff --git a/index.bs b/index.bs index 2d7344966..421f96b7f 100644 --- a/index.bs +++ b/index.bs @@ -1097,6 +1097,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S : <dfn>Credential Private Key</dfn> : <dfn>Credential Public Key</dfn> : <dfn>User Public Key</dfn> +: <dfn>User Credential</dfn> :: A [=credential key pair=] is a pair of asymmetric cryptographic keys generated by an [=authenticator=] and [=scoped=] to a specific [=[WRP]=]. It is the central part of a [=public key credential=]. @@ -1187,6 +1188,14 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S : {{PublicKeyCredentialDescriptor/transports}} :: The [$credential record/transports$] of the [=credential record=]. +: <dfn>Hardware-bound Device Key Pair</dfn> +: <dfn>Device-bound Key</dfn> +: <dfn>Device Private Key</dfn> +: <dfn>Device Public Key</dfn> +:: A [=hardware-bound device key pair=], also known as a [=device-bound key=], is an [=authenticator=]-, [=[RP]=]-, and [=user credential=]-specific public key pair created upon a [=[RP]=]'s request via the [=devicePubKey=] [=WebAuthn extension=]. + The [=authenticator=] that a [=hardware-bound device key pair=] is created upon guarantees that the [=device private key=] is securely stored in hardware, i.e., it is unextractable. See also [[#sctn-device-publickey-extension]]. + + Note: All guarantees about the operation of an [=authenticator=] operation rely on [=attestation=]. In particular, [=[RPS]=] MUST NOT rely on the above guarantee of unextractability unless supported by a valid, trusted [=attestation statement=]. : <dfn>Generating Authenticator</dfn> :: The Generating Authenticator is the authenticator involved in the [=authenticatorMakeCredential=] operation resulting @@ -1958,6 +1967,17 @@ a numbered step. If outdented, it (today) is rendered as a bullet in the midst o </dl> + 1. Let |attestationFormats| be a list of strings, initialized to the value of <code>|options|.{{PublicKeyCredentialCreationOptions/attestationFormats}}</code>. + + 1. If <code>|options|.{{PublicKeyCredentialCreationOptions/attestation}}</code> + + <dl class="switch"> + + : is set to {{AttestationConveyancePreference/none}} + :: Set |attestationFormats| be the single-element list containing the string “none” + + </dl> + 1. Let |excludeCredentialDescriptorList| be a new [=list=]. 1. [=list/For each=] credential descriptor |C| in <code>|pkOptions|.{{PublicKeyCredentialCreationOptions/excludeCredentials}}</code>: @@ -1977,7 +1997,7 @@ a numbered step. If outdented, it (today) is rendered either as a bullet in the --> <li id='CreateCred-InvokeAuthnrMakeCred'> - <!-- @@EDITOR-ANCHOR-01A: KEEP THIS LIST SYNC'D WITH THE LIST UP AT @@EDITOR-ANCHOR-01B --> + <!-- @@EDITOR-ANCHOR-01: KEEP THIS LIST SYNC'D WITH OTHER LOCATIONS WITH THIS TAG --> Invoke the [=authenticatorMakeCredential=] operation on |authenticator| with |clientDataHash|, <code>|pkOptions|.{{PublicKeyCredentialCreationOptions/rp}}</code>, @@ -1987,6 +2007,7 @@ a numbered step. If outdented, it (today) is rendered either as a bullet in the |credTypesAndPubKeyAlgs|, |excludeCredentialDescriptorList|, |enterpriseAttestationPossible|, + |attestationFormats|, and |authenticatorExtensions| as parameters. </li> @@ -2430,6 +2451,10 @@ When this method is invoked, the user agent MUST execute the following algorithm :: If the [=authenticator=] returned a [=user handle=], set the value of [=userHandleResult=] to be the bytes of the returned [=user handle=]. Otherwise, set the value of [=userHandleResult=] to null. + : <code><dfn for="assertionCreationData">assertionAttestation</dfn></code> + :: If the [=authenticator=] returned an [=attestation=], set the value of [=assertionAttestation=] to be the bytes of + the [=attestation statement=]. Otherwise set it to null. + : <code><dfn for="assertionCreationData">clientExtensionResults</dfn></code> :: whose value is an {{AuthenticationExtensionsClientOutputs}} object containing [=extension identifier=] → [=client extension output=] entries. The entries are created by running each extension's @@ -2472,6 +2497,12 @@ When this method is invoked, the user agent MUST execute the following algorithm [=%ArrayBuffer%=], containing the bytes of <code>|assertionCreationData|.[=assertionCreationData/userHandleResult=]</code>. + : {{AuthenticatorAssertionResponse/attestationObject}} + :: If <code>|assertionCreationData|.[=assertionCreationData/assertionAttestation=]</code> is null, set this + field to null. Otherwise, set this field to a new {{ArrayBuffer}}, created using |global|'s + [=%ArrayBuffer%=], containing the bytes of + <code>|assertionCreationData|.[=assertionCreationData/assertionAttestation=]</code>. + : {{PublicKeyCredential/[[clientExtensionsResults]]}} :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of <code>|assertionCreationData|.[=assertionCreationData/clientExtensionResults=]</code>. @@ -2554,6 +2585,30 @@ The steps for [=issuing a credential request to an authenticator=] are as follow </dl> + 1. Let |enterpriseAttestationPossible| be a Boolean value, as follows. If + <code>|options|.{{PublicKeyCredentialRequestOptions/attestation}}</code> + + <dl class="switch"> + + : is set to {{AttestationConveyancePreference/enterprise}} + :: Let |enterpriseAttestationPossible| be [TRUE] if the user agent wishes to support enterprise attestation for |rpId| (see [Step 7](#GetAssn-DetermineRpId) of [[#sctn-discover-from-external-source]]). Otherwise [FALSE]. + + : otherwise + :: Let |enterpriseAttestationPossible| be [FALSE]. + + </dl> + + 1. Let |attestationFormats| be a list of strings, initialized to the value of <code>|options|.{{PublicKeyCredentialRequestOptions/attestationFormats}}</code>. + + 1. If <code>|options|.{{PublicKeyCredentialRequestOptions/attestation}}</code> + + <dl class="switch"> + + : is set to {{AttestationConveyancePreference/none}} + :: Set |attestationFormats| be the single-element list containing the string “none” + + </dl> + 1. <span id="allowCredentialDescriptorListCreation"></span> If <code>|pkOptions|.{{PublicKeyCredentialRequestOptions/allowCredentials}}</code> <dl class="switch"> @@ -2590,19 +2645,25 @@ The steps for [=issuing a credential request to an authenticator=] are as follow Then, using |transport|, invoke the [=authenticatorGetAssertion=] operation on |authenticator|, with |rpId|, |clientDataHash|, |allowCredentialDescriptorList|, - |userVerification|, and |authenticatorExtensions| as parameters. + |userVerification|, + |enterpriseAttestationPossible|, + |attestationFormats|, + and |authenticatorExtensions| as parameters. : [=list/is empty=] :: Using local configuration knowledge of the appropriate transport to use with |authenticator|, invoke the [=authenticatorGetAssertion=] operation on |authenticator| with |rpId|, - |clientDataHash|, |allowCredentialDescriptorList|, |userVerification|, and - |authenticatorExtensions| as parameters. + |clientDataHash|, |allowCredentialDescriptorList|, |userVerification|, + |enterpriseAttestationPossible|, |attestationFormats|, and |authenticatorExtensions| as parameters. </dl> : [=list/is empty=] :: Using local configuration knowledge of the appropriate transport to use with |authenticator|, invoke the [=authenticatorGetAssertion=] operation on |authenticator| with |rpId|, |clientDataHash|, - |userVerification| and |authenticatorExtensions| as parameters. + |userVerification|, + |enterpriseAttestationPossible|, + |attestationFormats|, + and |authenticatorExtensions| as parameters. Note: In this case, the [=[RP]=] did not supply a list of acceptable credential descriptors. Thus, the authenticator is being asked to exercise any credential it may possess that is [=scoped=] to @@ -2871,6 +2932,7 @@ optionally evidence of [=user consent=] to a specific transaction. [SameObject] readonly attribute ArrayBuffer authenticatorData; [SameObject] readonly attribute ArrayBuffer signature; [SameObject] readonly attribute ArrayBuffer? userHandle; + [SameObject] readonly attribute ArrayBuffer? attestationObject; }; </xmp> <div dfn-type="attribute" dfn-for="AuthenticatorAssertionResponse"> @@ -2889,6 +2951,9 @@ optionally evidence of [=user consent=] to a specific transaction. : <dfn>userHandle</dfn> :: This attribute contains the [=user handle=] returned from the authenticator, or null if the authenticator did not return a [=user handle=]. See [[#sctn-op-get-assertion]]. + + : <dfn>attestationObject</dfn> + :: This OPTIONAL attribute contains an [=attestation object=], if the [=authenticator=] supports attestation in assertions. The [=attestation object=], if present, includes an [=attestation statement=]. Unlike the {{AuthenticatorAttestationResponse/attestationObject}} in an {{AuthenticatorAttestationResponse}}, it does not contain an `authData` key because the [=authenticator data=] is provided directly in an {{AuthenticatorAssertionResponse}} structure. For more details on attestation, see [[#sctn-attestation]], [[#sctn-attestation-in-assertions]], [[#sctn-generating-an-attestation-object]], and [Figure 6](#fig-attStructs). </div> ## Parameters for Credential Generation (dictionary <dfn dictionary>PublicKeyCredentialParameters</dfn>) ## {#dictionary-credential-params} @@ -2928,6 +2993,7 @@ optionally evidence of [=user consent=] to a specific transaction. sequence<PublicKeyCredentialDescriptor> excludeCredentials = []; AuthenticatorSelectionCriteria authenticatorSelection; DOMString attestation = "none"; + sequence<DOMString> attestationFormats = []; AuthenticationExtensionsClientInputs extensions; }; </xmp> @@ -2994,6 +3060,14 @@ optionally evidence of [=user consent=] to a specific transaction. The default value is {{AttestationConveyancePreference/none}}. + : <dfn>attestationFormats</dfn> + :: The [=[RP]=] MAY use this OPTIONAL member to specify a preference regarding the [=attestation=] statement format used by the [=authenticator=]. + Values SHOULD be taken from the IANA "WebAuthn Attestation Statement Format Identifiers" registry [[!IANA-WebAuthn-Registries]] established by [[!RFC8809]]. + Values are ordered from most preferable to least preferable. + This parameter is advisory and the [=authenticator=] MAY use an attestation statement not enumerated in this parameter. + + The default value is the empty list, which indicates no preference. + : <dfn>extensions</dfn> :: The [=[RP]=] MAY use this OPTIONAL member to provide [=client extension inputs=] requesting additional processing by the [=client=] and [=authenticator=]. @@ -3299,6 +3373,8 @@ an assertion. Its {{PublicKeyCredentialRequestOptions/challenge}} member MUST be USVString rpId; sequence<PublicKeyCredentialDescriptor> allowCredentials = []; DOMString userVerification = "preferred"; + DOMString attestation = "none"; + sequence<DOMString> attestationFormats = []; AuthenticationExtensionsClientInputs extensions; }; </xmp> @@ -3359,6 +3435,21 @@ an assertion. Its {{PublicKeyCredentialRequestOptions/challenge}} member MUST be See {{UserVerificationRequirement}} for the description of {{AuthenticatorSelectionCriteria/userVerification}}'s values and semantics. + : <dfn>attestation</dfn> + :: The [=[RP]=] MAY use this OPTIONAL member to specify a preference regarding [=attestation conveyance=]. + Its value SHOULD be a member of {{AttestationConveyancePreference}}. + [=Client platforms=] MUST ignore unknown values, treating an unknown value as if the [=map/exist|member does not exist=]. + + The default value is {{AttestationConveyancePreference/none}}. + + : <dfn>attestationFormats</dfn> + :: The [=[RP]=] MAY use this OPTIONAL member to specify a preference regarding the [=attestation=] statement format used by the [=authenticator=]. + Values SHOULD be taken from the IANA "WebAuthn Attestation Statement Format Identifiers" registry [[!IANA-WebAuthn-Registries]] established by [[!RFC8809]]. + Values are ordered from most preferable to least preferable. + This parameter is advisory and the [=authenticator=] MAY use an attestation statement not enumerated in this parameter. + + The default value is the empty list, which indicates no preference. + : <dfn>extensions</dfn> :: The [=[RP]=] MAY use this OPTIONAL member to provide [=client extension inputs=] requesting additional processing by the [=client=] and [=authenticator=]. @@ -3402,7 +3493,7 @@ SHOULD be aborted. The subsections below define the data types used for conveying [=WebAuthn extension=] inputs and outputs. -Note: [=Authenticator extension outputs=] are conveyed as a part of [=Authenticator data=] (see [Table 1](#table-authData)). +Note: [=Authenticator extension outputs=] are conveyed as a part of [=authenticator data=] (see [Table 1](#table-authData)). Note: The types defined below — {{AuthenticationExtensionsClientInputs}} and {{AuthenticationExtensionsClientOutputs}} — are applicable to both [=registration extensions=] and [=authentication extensions=]. The "Authentication..." portion of their names should be regarded as meaning "WebAuthentication..." @@ -4360,7 +4451,7 @@ The result of <dfn for="credential id">looking up</dfn> a [=credential id=] |cre It takes the following input parameters: -<!-- @@EDITOR-ANCHOR-01B: KEEP THIS LIST SYNC'D WITH THE LIST UP AT @@EDITOR-ANCHOR-01A --> +<!-- @@EDITOR-ANCHOR-01: KEEP THIS LIST SYNC'D WITH OTHER LOCATIONS WITH THIS TAG --> : |hash| :: The [=hash of the serialized client data=], provided by the client. : |rpEntity| @@ -4385,6 +4476,8 @@ It takes the following input parameters: list of known credentials. : |enterpriseAttestationPossible| :: A Boolean value that indicates that individually-identifying attestation MAY be returned by the authenticator. +: |attestationFormats| +:: A sequence of strings that expresses the [=[RP]=]'s preference for attestation statement formats, from most to least preferable. If the [=authenticator=] returns [=attestation=], then it makes a best-effort attempt to use the most preferable format that it supports. : |extensions| :: A [=CBOR=] [=map=] from [=extension identifiers=] to their [=authenticator extension inputs=], created by the [=client=] based on the extensions requested by the [=[RP]=], if any. @@ -4496,11 +4589,13 @@ a numbered step. If outdented, it (today) is rendered as a bullet in the midst o </dl> 1. Let |attestedCredentialData| be the [=attested credential data=] byte array including the |credentialId| and |publicKey|. +1. Let |attestationFormat| be the first supported [=attestation statement format identifier=] from |attestationFormats|, taking into account |enterpriseAttestationPossible|. + If |attestationFormats| contains no supported value, then let |attestationFormat| be the [=attestation statement format identifier=] most preferred by this authenticator. 1. Let |authenticatorData| [=perform the following steps to generate an authenticator data structure|be the byte array=] specified in [[#sctn-authenticator-data]], including |attestedCredentialData| as the <code>[=attestedCredentialData=]</code> and |processedExtensions|, if any, as the <code>[=authData/extensions=]</code>. 1. Create an [=attestation object=] for the new credential using the procedure specified in - [[#sctn-generating-an-attestation-object]], using an authenticator-chosen [=attestation statement format=], |authenticatorData|, + [[#sctn-generating-an-attestation-object]], the [=attestation statement format=] |attestationFormat|, and the values |authenticatorData| and |hash|, as well as {{enterprise|taking into account}} the value of |enterpriseAttestationPossible|. For more details on attestation, see [[#sctn-attestation]]. On successful completion of this operation, the authenticator returns the [=attestation object=] to the client. @@ -4523,6 +4618,10 @@ It takes the following input parameters: wish to make a [=test of user presence=] optional although WebAuthn does not. : |requireUserVerification| :: The [=effective user verification requirement for assertion=], a Boolean value provided by the client. +: |enterpriseAttestationPossible| +:: A Boolean value that indicates that individually-identifying attestation MAY be returned by the authenticator. +: |attestationFormats| +:: A sequence of strings that expresses the [=[RP]=]'s preference for attestation statement formats, from most to least preferable. If the [=authenticator=] returns [=attestation=], then it makes a best-effort attempt to use the most preferable format that it supports. : |extensions| :: A [=CBOR=] [=map=] from [=extension identifiers=] to their [=authenticator extension inputs=], created by the client based on the extensions requested by the [=[RP]=], if any. @@ -4564,9 +4663,17 @@ When this method is invoked, the [=authenticator=] MUST perform the following pr which approach is implemented by the [=authenticator=], by some positive value. If the [=authenticator=] does not implement a [=signature counter=], let the [=signature counter=] value remain constant at zero. +1. If |attestationFormats|: + <dl class="switch"> + : is [=list/is not empty|not empty=] + :: let |attestationFormat| be the first supported [=attestation statement format=] from |attestationFormats|, taking into account |enterpriseAttestationPossible|. If none are supported, fallthrough to: + + : is [=list/is empty|empty=] + :: let |attestationFormat| be the [=attestation statement format=] most preferred by this authenticator. If it does not support attestation during assertion then let this be `none`. + </dl> 1. Let |authenticatorData| [=perform the following steps to generate an authenticator data structure|be the byte array=] specified in [[#sctn-authenticator-data]] including |processedExtensions|, if any, as - the <code>[=authData/extensions=]</code> and excluding <code>[=attestedCredentialData=]</code>. + the <code>[=authData/extensions=]</code> and excluding <code>[=attestedCredentialData=]</code>. This |authenticatorData| MUST include [=attested credential data=] if, and only if, |attestationFormat| is not `none`. 1. Let |signature| be the [=assertion signature=] of the concatenation <code>|authenticatorData| || |hash|</code> using the [=public key credential source/privateKey=] of |selectedCredential| as shown in <a href="#fig-signature">Figure <span class="figure-num-following"/></a>, below. A simple, undelimited @@ -4578,8 +4685,11 @@ When this method is invoked, the [=authenticator=] MUST perform the following pr <figcaption>Generating an [=assertion signature=].</figcaption> </figure> -1. If any error occurred while generating the [=assertion signature=], return an error code equivalent to "{{UnknownError}}" and - terminate the operation. +1. The |attestationFormat| is not `none` then create an [=attestation object=] for the new credential using the procedure specified in + [[#sctn-generating-an-attestation-object]], the [=attestation statement format=] |attestationFormat|, and the values |authenticatorData| + and |hash|, as well as {{enterprise|taking into account}} the value of |enterpriseAttestationPossible|. For more details on attestation, see [[#sctn-attestation]]. + +1. If any error occurred then return an error code equivalent to "{{UnknownError}}" and terminate the operation. <!-- Note: this next step is actually a top-level step, but bikeshed wanted it indented this much in order to render it as a numbered step. If outdented, it (today) is rendered as a bullet in the midst of a numbered list :-/ @@ -4596,6 +4706,7 @@ a numbered step. If outdented, it (today) is rendered as a bullet in the midst o - |authenticatorData| - |signature| + - The attestation object, if an [=attestation object=] was created for this assertion. - |selectedCredential|.[=public key credential source/userHandle=] Note: the returned [=public key credential source/userHandle=] value may be `null`, see: @@ -4717,8 +4828,9 @@ produce, for each [=credential public key=], an [=attestation statement=] verifi a challenge, as well as a certificate or similar data providing provenance information for the [=attestation public key=], enabling the [=[RP]=] to make a trust decision. However, if an [=attestation key pair=] is not available, then the authenticator MAY either perform [=self attestation=] of the [=credential public key=] with the corresponding [=credential private key=], -or otherwise perform [=None|no attestation=]. All this -information is returned by [=authenticators=] any time a new [=public key credential=] is generated, in the overall form of an +or otherwise perform [=None|no attestation=]. + +All this information is returned by [=authenticators=] any time a new [=public key credential=] is generated, and optionally when exercised, in the overall form of an <dfn>attestation object</dfn>. The relationship of the [=attestation object=] with [=authenticator data=] (containing [=attested credential data=]) and the [=attestation statement=] is illustrated in <a href="#fig-attStructs">figure <span class="figure-num-following"/></a>, below. @@ -4728,7 +4840,7 @@ In these cases, the [=authenticator=] provides no guarantees about its operation <figure id="fig-attStructs"> <img src="images/fido-attestation-structures.svg"/> - <figcaption>[=Attestation object=] layout illustrating the included [=authenticator data=] (containing [=attested credential + <figcaption>[=Attestation object=] layout illustrating the included [=authenticator data=] from a {{CredentialsContainer/create()|create()}} operation (containing [=attested credential data=]) and the [=attestation statement=].</figcaption> </figure> <div class="note"> @@ -4765,17 +4877,28 @@ The privacy, security and operational characteristics of [=attestation=] depend operating environment, and so on. The [=attestation type=] and [=attestation statement format=] is chosen by the [=authenticator=]; -[=[RPS]=] can only signal limited [=attestation conveyance=] preferences during [=registration=]. +[=[RPS]=] can only signal their preferences by setting the {{PublicKeyCredentialCreationOptions/attestation}} and {{PublicKeyCredentialCreationOptions/attestationFormats}} parameters, or those with the same names in {{PublicKeyCredentialRequestOptions}}. + It is expected that most [=authenticators=] will support a small number of [=attestation types=] and [=attestation statement formats=], while [=[RPS]=] will decide what [=attestation types=] are acceptable to them by policy. [=[RPS]=] will also need to understand the characteristics of the [=authenticators=] that they trust, based on information they have about these [=authenticators=]. For example, the FIDO Metadata Service [[FIDOMetadataService]] provides one way to access such information. +### Attestation in assertions ### {#sctn-attestation-in-assertions} + +Attestation is most commonly provided during credential creation. However, if supported by the authenticator and requested by the [=[RP]=] using the {{PublicKeyCredentialRequestOptions/attestation}} parameter, attestation MAY be provided in [=assertions=]. + +Attestations in [=assertions=] could be helpful in at least the following situations: + +1. For [=multi-device credentials=], the [=generating authenticator=] may have returned a meaningfully different attestation than the authenticator currently exercising the credential. Thus returning an attestation for each use of the credential allows the [=[RP]=] to observe these changes. +1. If the [=attestation statement format=] involves a 3rd-party attesting to the state of the authenticator, then returning an attestation with each use of the credential allows for the continued good health of the authenticator to be attested. + +[=Attestation objects=] provided in an {{AuthenticatorAttestationResponse}} structure (i.e. as the result of a {{CredentialsContainer/create()|create()}} operation) contain at least the three keys shown in [the previous figure](#fig-attStructs): `fmt`, `attStmt`, and `authData`. The `authData` key is not included when an [=attestation object=] is provided in an {{AuthenticatorAssertionResponse}} (i.e. as the result of a {{CredentialsContainer/get()|get()}} operation). That is because the [=authenticator data=] is provided directly in the {{AuthenticatorAssertionResponse/authenticatorData}} member of the {{AuthenticatorAssertionResponse}}. Otherwise, processing of the [=attestation object=] is identical. ### Attested Credential Data ### {#sctn-attested-credential-data} <dfn>Attested credential data</dfn> is a variable-length byte array added to the [=authenticator data=] when generating an [=attestation -object=] for a given credential. Its format is shown in <a href="#table-attestedCredentialData">Table <span class="table-ref-following"/></a>. +object=] for a credential. Its format is shown in <a href="#table-attestedCredentialData">Table <span class="table-ref-following"/></a>. <figure id="table-attestedCredentialData" class="table"> <table class="complex data longlastcol" dfn-for="authData/attestedCredentialData"> @@ -4819,6 +4942,8 @@ object=] for a given credential. Its format is shown in <a href="#table-attested </figcaption> </figure> +Attested credential data is always present in any [=authenticator data=] that results from a {{CredentialsContainer/create()|create()}} operation. It MUST be present in an [=authenticator data=] resulting from a {{CredentialsContainer/get()|get()}} operation if, and only if, the {{AuthenticatorAssertionResponse/attestationObject}} attribute is present in the assertion result. + #### Examples of `credentialPublicKey` Values Encoded in COSE_Key Format #### {#sctn-encoded-credPubKey-examples} This section provides examples of COSE_Key-encoded Elliptic Curve and RSA public keys for the ES256, PS256, and RS256 @@ -5135,6 +5260,8 @@ In order to perform a [=registration ceremony=], the [=[RP]=] MUST proceed as fo Note: Since all extensions are OPTIONAL for both the [=client=] and the [=authenticator=], the [=[RP]=] MUST also be prepared to handle cases where none or not all of the requested extensions were acted upon. + Note: The [=devicePubKey=] extension has explicit verification procedures, see [[#sctn-device-publickey-extension-verification-create]]. + 1. Determine the attestation statement format by performing a USASCII case-sensitive match on |fmt| against the set of supported WebAuthn Attestation Statement Format Identifier values. An up-to-date list of registered WebAuthn Attestation Statement Format Identifier values @@ -5324,6 +5451,8 @@ a numbered step. If outdented, it (today) is rendered as a bullet in the midst o Note: Since all extensions are OPTIONAL for both the [=client=] and the [=authenticator=], the [=[RP]=] MUST also be prepared to handle cases where none or not all of the requested extensions were acted upon. + Note: The [=devicePubKey=] extension has explicit verification procedures, see [[#sctn-device-publickey-extension-verification-get]]. + 1. Let |hash| be the result of computing a hash over the |cData| using SHA-256. 1. Using <code>|credentialRecord|.[$credential record/publicKey$]</code>, @@ -5351,6 +5480,20 @@ a numbered step. If outdented, it (today) is rendered as a bullet in the midst o [=[RP]=]-specific. </dd> </dl> +1. If <code>|response|.{{AuthenticatorAssertionResponse/attestationObject}}</code> is present and the [=[RP]=] wishes to verify the attestation then perform CBOR decoding on {{AuthenticatorAssertionResponse/attestationObject}} to obtain the attestation statement format |fmt|, and the attestation statement |attStmt|. + + 1. Verify that the `AT` bit in the [=flags=] field of |authData| is set, indicating that [=attested credential data=] is included. + + 1. Verify that the [=credentialPublicKey=] and [=credentialId=] fields of the [=attested credential data=] in |authData| match <code>|credentialRecord|.[$credential record/publicKey$]</code> and <code>|credentialRecord|.[$credential record/id$]</code>, respectively. + + 1. Determine the attestation statement format by performing a USASCII case-sensitive match on |fmt| against the set of supported WebAuthn Attestation Statement Format Identifier values. An up-to-date list of registered WebAuthn Attestation Statement Format Identifier values is maintained in the IANA "WebAuthn Attestation Statement Format Identifiers" registry [[!IANA-WebAuthn-Registries]] established by [[!RFC8809]]. + + 1. Verify that |attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |fmt|'s [=verification procedure=] given |attStmt|, |authData| and |hash|. + + Note: Each [=attestation statement format=] specifies its own [=verification procedure=]. See [[#sctn-defined-attestation-formats]] for the initially-defined formats, and [[!IANA-WebAuthn-Registries]] for the up-to-date list. + + 1. If validation is successful, obtain a list of acceptable trust anchors (i.e. attestation root certificates) for that attestation type and attestation statement format |fmt|, from a trusted source or from policy. The [=aaguid=] in the [=attested credential data=] can be used to guide this lookup. + 1. Update |credentialRecord| with new state values: 1. Update <code>|credentialRecord|.[$credential record/signCount$]</code> to the value of |authData|.<code>[=authData/signCount=]</code>. @@ -5973,9 +6116,11 @@ and passes them to the authenticator in the {{CredentialsContainer/create()}} ca {{CredentialsContainer/get()}} call (for [=authentication extensions=]). These [=authenticator extension input=] values are represented in [=CBOR=] and passed as name-value pairs, with the [=extension identifier=] as the name, and the corresponding [=authenticator extension input=] as the value. The authenticator, in turn, performs additional processing for the extensions -that it supports, and returns the [=CBOR=] [=authenticator extension output=] for each as specified by the extension. Part of -the [=client extension processing=] for [=authenticator extensions=] is to use the [=authenticator extension output=] as an -input to creating the [=client extension output=]. +that it supports, and returns the [=CBOR=] [=authenticator extension output=] for each as specified by the extension. +Since [=authenticator extension output=] is returned as part of the signed [=authenticator data=], authenticator extensions +MAY also specify an [=unsigned extension output=], e.g. for cases where an output itself depends on [=authenticator data=]. +Part of the [=client extension processing=] for [=authenticator extensions=] is to use the [=authenticator extension output=] +and [=unsigned extension output=] as an input to creating the [=client extension output=]. All [=WebAuthn Extensions=] are OPTIONAL for both clients and authenticators. Thus, any extensions requested by a [=[RP]=] MAY be ignored by the client browser or OS and not passed to the authenticator at all, or they MAY be ignored by the authenticator. @@ -6025,6 +6170,7 @@ If the extension communicates with the authenticator (meaning it is an [=authent it MUST also specify the [=CBOR=] [=authenticator extension input=] argument sent via the [=authenticatorGetAssertion=] or [=authenticatorMakeCredential=] call, the [=authenticator extension processing=] rules, and the [=CBOR=] [=authenticator extension output=] value. +Extensions MAY specify [=unsigned extension outputs=]. Any [=client extension=] that is processed by the client MUST return a [=client extension output=] value so that the [=[WRP]=] knows that the extension was honored by the client. Similarly, any extension that requires authenticator processing MUST return @@ -6115,7 +6261,8 @@ There MUST NOT be any values returned for ignored extensions. Extensions that require authenticator processing MUST define the process by which the [=client extension input=] can be used to determine the [=CBOR=] [=authenticator extension input=] and -the process by which the [=CBOR=] [=authenticator extension output=] can be used to determine the [=client extension output=]. +the process by which the [=CBOR=] [=authenticator extension output=], and the [=unsigned extension output=] if used, can be +used to determine the [=client extension output=]. ## <dfn>Authenticator Extension Processing</dfn> ## {#sctn-authenticator-extension-processing} @@ -6128,8 +6275,16 @@ Likewise, the extension output is represented in the [=authData/extensions=] par [=authData/extensions=] part of the [=authenticator data=] is a CBOR map where each key is an [=extension identifier=] and the corresponding value is the <dfn>authenticator extension output</dfn> for that extension. +<dfn>Unsigned extension outputs</dfn> are represented independently from [=authenticator data=] and returned by authenticators +as a separate map, keyed with the same [=extension identifier=]. This map only contains entries for authenticator +extensions that make use of unsigned outputs. + +Note: In [[!FIDO-CTAP]] [=unsigned extension outputs=] are returned as a CBOR map in a top-level field named +`unsignedExtensionOutputs` from both [=authenticatorMakeCredential=] and [=authenticatorGetAssertion=]. + For each supported extension, the [=authenticator extension processing=] rule for that extension is used create the -[=authenticator extension output=] from the [=authenticator extension input=] and possibly also other inputs. +[=authenticator extension output=], and [=unsigned extension output=] if used, from the [=authenticator extension input=] +and possibly also other inputs. There MUST NOT be any values returned for ignored extensions. @@ -6693,6 +6848,364 @@ This extension enables use of a user verification method. </pre> +### Device-bound public key extension (<dfn>devicePubKey</dfn>) ### {#sctn-device-publickey-extension} + +This [=authenticator extension|authenticator=] [=registration extension=] and [=authentication extension=] provides a [=[RP]=] with a "device continuity" signal for [=backup eligible=] credentials. This is done by creating a [=user credential=]-specific [=hardware-bound device key pair=] on the [=authenticator=], if such a key pair does not already exist for the [=user credential=] being created or exercised, and returning the [=device public key=] along with a signature by the [=device private key=] to the [=[RP]=]. This is done each time this [=devicePubKey=] extension is included with either a {{CredentialsContainer/create()|navigator.credentials.create()}} or {{CredentialsContainer/get()|navigator.credentials.get()}} call. + +If the [=authenticator=] is incapable of generating a [=hardware-bound device key pair=], or the registration or authentication operation fails for any reason, this extension is ignored and no [=hardware-bound device key pair=] is created. In this case, there is no [=devicePubKey=] extension output generated. + +The [=hardware-bound device key pair=] is not on its own a [=user credential=] and does not have its own [=credential ID=]. Instead, the returned [=device public key=] is a device-specific contextual attribute of its associated [=user credential=]. That is, when that [=user credential=] is used—along with the [=devicePubKey=] extension—on a particular [=authenticator=], a particular [=device public key=] is returned by the extension, along with a signature demonstrating proof-of-possession of the [=device private key=] by that device. + + +#### Relying Party Usage #### {#sctn-device-publickey-extension-usage} + +This extension is intended for use by those [=[RPS]=] employing risk-analysis systems informing their sign-in decisions. This extension provides a "device continuity" signal <i>when used consistently</i> with both {{CredentialsContainer/create()|navigator.credentials.create()}} and {{CredentialsContainer/get()|navigator.credentials.get()}} operations: + +When a [=[RP]=] uses the `devicePubKey` extension with a {{CredentialsContainer/create()|create()}} call to create a new [=user credential=], a signature by a new [=device-bound key=] ("dpk 1") is returned along with the new [=device public key=]. Even if the [=user credential=] is [=backed up=], "dpk 1" never leaves the [=generating authenticator=] ("authenticator 1"). The [=[RP]=]'s subsequent {{CredentialsContainer/get()|get()}} operations using the same user credential with that <i>same [=authenticator=]</i> generate [=assertions=] including further signatures by the <i>same</i> [=device-bound key=] ("dpk 1"). This behavior on "authenticator 1" is independent of whether this user credential has been copied to any other [=authenticator=]. + +Then, if this same user credential is copied to a different [=authenticator=] ("authenticator 2"), the [=[RP]=]'s first {{CredentialsContainer/get()|get()}} call on "authenticator 2" (that includes the `devicePubKey` extension) will produce an [=assertion=] including a signature by a <i>new</i> [=device-bound key=] ("dpk 2"). Note that such a [=multi-device credential=] can be exercised on "authenticator 2" without a {{CredentialsContainer/create()|create()}} having been performed on "authenticator 2". The [=[RP]=]'s subsequent {{CredentialsContainer/get()|get()}} calls on "authenticator 2", using the `devicePubKey` extension and the same user credential, yield further signatures by "dpk 2". + +A usage example is thus: + +> 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 by a [=device-bound key=] that is <i>well established</i> for this user can also be presented, then that may tip the balance. + +Note: [=[RPS]=] need to take care to verify [=device-bound key=] signatures before associating and storing extension output value fields in conjunction with the [=user account=]. See [[#sctn-device-publickey-extension-verification]]. + +The weight that [=[RPS]=] give to the presence of a signature from a [=device-bound key=] may be based on information learned from its optional attestation. An attestation can indicate the level of protection that the hardware offers the private key, certifications for the hardware, etc. + +#### Extension Definition #### {#sctn-device-publickey-extension-definition} + +: Extension identifier +:: `devicePubKey` + +: Operation applicability +:: [=registration extension|Registration=] and [=authentication extension|authentication=] + +: Client extension input +:: <xmp class="idl"> + dictionary AuthenticationExtensionsDevicePublicKeyInputs { + DOMString attestation = "none"; + sequence<DOMString> attestationFormats = []; + }; + + partial dictionary AuthenticationExtensionsClientInputs { + AuthenticationExtensionsDevicePublicKeyInputs devicePubKey; + }; + </xmp> + <div dfn-type="dict-member" dfn-for="AuthenticationExtensionsDevicePublicKeyInputs"> + : <dfn>attestation</dfn> + :: The [=[RP]=] MAY use this OPTIONAL member to specify a preference regarding [=attestation conveyance=]. + Its value SHOULD be a member of {{AttestationConveyancePreference}}. + [=Client platforms=] MUST ignore unknown values, treating an unknown value as if the [=map/exist|member does not exist=]. + + The default value is {{AttestationConveyancePreference/none}}. + + : <dfn>attestationFormats</dfn> + :: The [=[RP]=] MAY use this OPTIONAL member to specify a preference regarding the [=attestation=] statement format used by the [=authenticator=]. + Values SHOULD be taken from the IANA "WebAuthn Attestation Statement Format Identifiers" registry [[!IANA-WebAuthn-Registries]] established by [[!RFC8809]]. + Values are ordered from most preferable to least preferable. + This parameter is advisory and the [=authenticator=] MAY use an attestation statement not enumerated in this parameter. + + The default value is the empty list, which indicates no preference. + </div> + + Note: To request the `devicePubKey` extension processing with default options, pass an empty dictionary as the input. + +: Client extension processing +:: If {{AuthenticationExtensionsClientInputs/devicePubKey}} is present, the client creates the authenticator extension input from the client extension input. + +: Client extension output +:: An ArrayBuffer containing the [=device public key attestation object=] that was returned in the authenticator extension output and the signature returned as the [=unsigned extension output=]. + <xmp class="idl"> + dictionary AuthenticationExtensionsDevicePublicKeyOutputs { + ArrayBuffer authenticatorOutput; + ArrayBuffer signature; + }; + + partial dictionary AuthenticationExtensionsClientOutputs { + AuthenticationExtensionsDevicePublicKeyOutputs devicePubKey; + }; + </xmp> + + +: Authenticator extension input +:: A CBOR expression of the client extension input + + ``` + devicePublicKeyInputs = { + attestation: "none" / "indirect" / "direct" / "enterprise", + attestationFormats: [tstr], + } + $$extensionInput //= ( + devicePubKey: devicePublicKeyInputs, + ) + ``` + +: Authenticator extension output +:: A byte string containing the CBOR encoding of the <dfn>device public key attestation object</dfn>, defined by the `attObjForDevicePublicKey` type: + + ``` + $$extensionOutput //= ( + devicePubKey: bstr, + ) + + attObjForDevicePublicKey = { ; Note: This object conveys an attested + ; device public key and is analogous to \`attObj\`. + + aaguid: bstr, ; Authenticator's AAGUID (16 bytes fixed-length) + ; https://www.w3.org/TR/webauthn/#aaguid + + dpk: bstr, ; The Device Public Key (self-describing variable length, + ; COSE_Key format, CBOR-encoded)). + + ; Whether this key is scoped to the entire device, or a loosely-defined, + ; narrower scope called "app". For example, a "device"-scoped key is expected + ; to be the same between an app and a browser on the same device, while + ; an "app"-scoped key would probably not be. + ; + ; Whatever the scope, a device key is still specific to a given credential + ; and does not provide any ability to link credentials. + ; + ; Whether device-scoped or not, keys are still device-bound. I.e. an + ; app-scoped key does not enjoy lesser protection from extraction. + + scope: uint .size 1, ; A value of 0x00 means "entire device" ("all apps") + ; scope. 0x01 means "per-app" scope. + ; Values other than 0x00 or 0x01 are reserved for future + ; use. + + ; An authenticator-generated random nonce for inclusion in the attestation + ; signature. If the authenticator chooses to not generate a nonce, it sets this + ; to a zero-length byte string. See the note below about "randomNonce" for a + ; discussion on the nonce's purpose. + + nonce: bstr .size (0..32), + + ; See https://www.w3.org/TR/webauthn/#sctn-generating-an-attestation-object + ; + ; Attestation statement formats define the \`fmt\` and \`attStmt\` members of + ; $$attStmtType. + ; Note that \`fmt\` and \`attStmt\` are top-level members of + ; \`attObjForDevicePublicKey\`. + ; + ; In summary, the \`attStmt\` will (typically) contain: + ; (1) a SIGNATURE value calculated (using the attestation private key) + ; over (prefix || aaguid || dpk || nonce) where \`prefix\` is + ; h'64657669636520626f756e64206b6579206174746573746174696f6e20736967 + ; 00ffffffff'. + ; (See the attestation calculations section, below, for a discussion + ; about the purpose of this \`prefix\` value.) + ; (2) the attestation certificate or public key, and supporting certificates, + ; if any. + ; + ; Note that there are details dependent upon the particular attestation + ; statement format. + ; See https://www.w3.org/TR/webauthn/#sctn-defined-attestation-formats. + + $$attStmtType, + + ; An optional boolean that indicates whether the attestation statement + ; contains uniquely identifying information. This can only be true + ; when the \`attestation\` field of the extension input is "enterprise" + ; and either the user-agent or the authenticator permits uniquely + ; identifying attestation for the requested RP ID. + + ? epAtt: bool .default false, + } + + ``` + +: Unsigned extension output +:: A CBOR byte string containing a signature generated with the device private key. + +: Authenticator extension processing +:: 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. If a [=hardware-bound device key pair=] does not already exist for this {[=public key credential source/id|Credential ID=], [=public key credential source/rpId|RP ID=], [=public key credential source/rpId|userHandle=]} tuple on the [=authenticator=], create it using the same public key algorithm as that used by the [=user credential=]'s [=credential key pair=], otherwise locate the existing [=device-bound key=]. + + 1. Let |attFormat| be the chosen [=attestation statement format=], and |attAaguid| be a 16-byte value, based on the value of {{AuthenticationExtensionsDevicePublicKeyInputs/attestation}} in the extension input: + + <dl class="switch"> + : none + :: |attFormat| is "none" or "self", at the authenticator's discretion, and |attAaguid| is 16 zero bytes. (Note that, since the [=device-bound key=] is already exercised during {{CredentialsContainer/get()|navigator.credentials.get()}} calls, the proof-of-possession property provided by "self" attestation is superfluous in that context.) + + : indirect, direct + :: |attFormat| is an [=attestation statement format=] appropriate for this [=authenticator=] based on {{AuthenticationExtensionsDevicePublicKeyInputs/attestationFormats}}, and |attAaguid| is the [=authenticator's=] [=AAGUID=]. (Since the [=hardware-bound device key pair=] is specific to a particular authenticator, its attestation can be tied to hardware roots of trust, although they do not have to be. This is in contrast to the associated [=user credential=]'s attestation, if it is a [=multi-device credential=].) + + : enterprise + :: The [=[RP]=] wants to receive an [=attestation statement=] that may include uniquely identifying information. This is intended for controlled deployments within an enterprise where the organization wishes to tie registrations to specific authenticators. [=Authenticators=] MUST NOT provide such an attestation unless the user agent or authenticator configuration expressly permits it for the requested [=RP ID=]. If <i>not</i> permitted, then |attFormat| is "none" and |attAaguid| is 16 zero bytes. Otherwise |attFormat| is an [=attestation statement format=] appropriate for this [=authenticator=] based on {{AuthenticationExtensionsDevicePublicKeyInputs/attestationFormats}}, and |attAaguid| is the [=authenticator's=] [=AAGUID=]. (Again, since the [=hardware-bound device key pair=] is specific to a particular authenticator, the attestation may be tied to hardware roots of trust.) + + Note: CTAP2 does not currently provide for an <a href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#makecred-enterpriseattestation">enterpriseAttestation</a> signal during an <a href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetAssertion">authenticatorGetAssertion</a> call. Until that is changed, <a href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#platform-managed-enterprise-attestation">platform-managed enterprise attestation</a> will not work in that context with CTAP2 [=authenticators=]. + </dl> + + 1. Let |dpk| be the newly created or existing [=device public key=], in COSE_Key format [=credentialPublicKey|in the same fashion as for the user credential's credentialPublicKey=] when the latter is conveyed in [=attested credential data=]. + + 1. Let |devicePrivateKey| be the newly created or existing [=device private key=]. + + 1. Let |randomNonce| be a fresh randomly-generated byte string of 32 bytes maximum length, or a zero length byte string if the authenticator chooses to not generate a nonce. + + Note: |randomNonce|'s purpose is to randomize the `devicePubKey` extension's [=attestation signature=] value. If this is not done, then the `devicePubKey` extension's [=attestation signature=] value remains constant for all such signatures issued on behalf of this [=user credential=], possibly exposing the [=authenticator=]'s [=attestation private key=] to side-channel attacks. The randomness-generation mechanism should be carefully chosen by the authenticator implementer. + + 1. Let the `devicePubKey` [=authenticator extension output=] value be a byte string that encodes a [=CBOR=] map as defined by `attObjForDevicePublicKey` above, with keys and values as follows: + + Note: as with all CBOR structures used in this specification, the [=CTAP2 canonical CBOR encoding form=] MUST be used. + + 1. Let the `aaguid` key's value be |attAaguid|. + + 1. Let the `dpk` key's value be |dpk|. + + 1. Let the `nonce` key's value be |randomNonce|. + + 1. Let the `scope` key have the value zero (0x00) if this is an "entire device" [=device public key=]. Otherwise, let `scope` have the value one (0x01), indicating a more narrow per-app scope. + + 1. Let the values of the `$$attStmtType` [=group socket=] be the result of generating an [=attestation statement=] with the [=attestation statement format=], |attFormat|. See [[#sctn-device-publickey-attestation-calculations]]. + + Note: The details of the `$$attStmtType` values are dependent upon the particular [=attestation statement=] format. See [[#sctn-attestation-formats]]. Since the [=hardware-bound device key pair=] is specific to a particular authenticator, its attestation can be tied to hardware roots of trust (although they do not have to be). This is in contrast to the associated [=user credential=]'s attestation, if it is a [=multi-device credential=]. + + 1. If the `$$attStmtType` [=group socket=] contains uniquely identifying information then let `epAtt` be [TRUE]. Otherwise omit the `epAtt` field. (This field can only be [TRUE] when the {{AuthenticationExtensionsDevicePublicKeyInputs/attestation}} field of the extension input is "enterprise" and either the user-agent or the authenticator permits uniquely identifying attestation for the requested RP ID.) + + 1. Let |dpkSig| be the result of signing the [=assertion signature=] [input](#fig-signature) with |devicePrivateKey|. + + Note: the [=assertion signature=] [input](#fig-signature), and thus |dpkSig|, covers the [=[RP]=]'s {{PublicKeyCredentialCreationOptions/challenge}} because it includes the [=hash of the serialized client data=]. Thus the [=[RP]=] knows that |dpkSig| is a fresh signature. + + 1. Output |dpkSig| as the extension's [=unsigned extension output=]. + + Note: |dpkSig| cannot be included in the [=authenticator extension output=] because it is returned inside the [=authenticator data=] and that would imply that the signature signs over itself. + +##### AAGUIDs ##### {#sctn-device-publickey-attestation-aaguid} + +The [=AAGUID=] included in the <code>[=devicePubKey=]</code> extension output, if non-zero, identifies the make or model of hardware that is storing the [=device-bound key=]. This is distinct from the [=AAGUID=] in the [=attested credential data=] of a [=multi-device credential=], which likely identifies something broader since such credentials are not bound to a single device. Thus the two AAGUIDs MAY be different in a single response and either, or both, may be zero depending on the options requested and authenticator behaviour. + +##### Attestation calculations ##### {#sctn-device-publickey-attestation-calculations} + +When computing attestations, the process in [[#sctn-generating-an-attestation-object]] takes two inputs: `authData` and `hash`. When calculating an attestation for a [=device-bound key=], the typical value for `hash` hashes over the attestation signature itself, which is impossible. Also the attestation of a [=device-bound key=] is potentially used repeatedly, thus may want to be cached. But signing over values that include [=[RP]=]-chosen nonces, like the [=hash of the serialized client data=], makes that impossible. + +Therefore when calculating an attestation for a [=device-bound key=], the inputs are: + + * For `authData`, substitute the concatenation of the byte string h'64657669636520626f756e64206b6579206174746573746174696f6e2073696700ffffffff' and the value of |aaguid| from the extension output. + * For `hash`, substitute the concatenation of the |dpk| and |nonce| fields from the extension output. (The nonce MAY be empty.) + +The attestation signature is thus typically calculated over the bytes of <code>(h'64657669636520626f756e64206b6579206174746573746174696f6e2073696700ffffffff' || |aaguid| || |dpk| || |nonce|)</code>. The 37-byte prefix ensures domain separation: it takes the place of the RP ID hash, flags, and signature counter fields in those messages and ensures that no attestation signature for a [=device-bound key=] can be confused with a signature for a [=user credential=]. + +Note that when |nonce| is empty, then the (signed) authenticator extension output MAY be constant. However, the (unsigned) |dpkSig| output is always unique and prevents replay of the (signed) extension output without knowledge of the [=device private key=]. + +#### `devicePubKey` Extension Output Verification Procedures #### {#sctn-device-publickey-extension-verification} + +Verifying the <code>[=devicePubKey=]</code> extension output is performed by the [=[RP]=] whenever a [=device public key=] is returned within the extension output. + +##### Registration (`create()`) ##### {#sctn-device-publickey-extension-verification-create} + +If the `devicePubKey` extension was included on a {{CredentialsContainer/create()|navigator.credentials.create()}} call, then the below verification steps are performed in the context of <a href=#reg-ceremony-verify-extension-outputs>this step</a> of [[#sctn-registering-a-new-credential]] using these variables established therein: |credential|, |clientExtensionResults|, |authData|, and |hash|. [=[RP]=] policy may specify whether a response without a `devicePubKey` is acceptable. + +1. Let |attObjForDevicePublicKey| be the value of the {{AuthenticationExtensionsClientOutputs/devicePubKey}} member of |clientExtensionResults|. + +1. Verify that |attObjForDevicePublicKey| is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields: |aaguid|, |dpk|, |scope|, |nonce|, |fmt|, |attStmt|. + + Note: The latter |attObjForDevicePublicKey| fields are referenced exclusively in the below steps and are not to be confused with other fields with the same names in other portions of the top-level [=attestation object=]. + +1. Verify that {{AuthenticationExtensionsDevicePublicKeyOutputs/signature}} is a valid signature over the [=assertion signature=] [input](#fig-signature) (i.e. `authData` and `hash`) by the [=device public key=] |dpk|. (The signature algorithm is the same as for the [=user credential=].) + +1. Optionally, if attestation was requested and the [=[RP]=] wishes to verify it, verify that |attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |fmt|'s [=verification procedure=] given |attStmt|. See [[#sctn-device-publickey-attestation-calculations]]. [=[RP]=] policy may specifiy which attestations are acceptable. + + Note: If |fmt|'s value is "[=none=]" there is no attestation signature to verify. + +1. Complete the steps from [[#sctn-registering-a-new-credential]] and, if those steps are successful, store the |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=]. + +See also [[#sctn-device-publickey-extension-usage]] for further details. + +##### Authentication (`get()`) ##### {#sctn-device-publickey-extension-verification-get} + +If the `devicePubKey` extension was included on a {{CredentialsContainer/get()|navigator.credentials.get()}} call, then the below verification steps are performed in the context of <a href=#authn-ceremony-verify-extension-outputs>this step</a> of [[#sctn-verifying-assertion]] using these variables established therein: |credential|, |clientExtensionResults|, |authData|, and |hash|. [=[RP]=] policy may specify whether a response without a `devicePubKey` is acceptable. + +1. Let |attObjForDevicePublicKey| be the value of the `devicePubKey` member of |clientExtensionResults|. + +1. Verify that |attObjForDevicePublicKey| is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to extract the contained fields: |aaguid|, |dpk|, |scope|, |nonce|, |fmt|, |attStmt|. + + Note: The latter |attObjForDevicePublicKey| fields are referenced exclusively in the below steps and are not to be confused with other fields with the same names in other portions of [=authenticator data=]. + +1. Verify that {{AuthenticationExtensionsDevicePublicKeyOutputs/signature}} is a valid signature over the [=assertion signature=] [input](#fig-signature) (i.e. `authData` and `hash`) by the [=device public key=] |dpk|. (The signature algorithm is the same as for the [=user credential=].) + +1. If the [=[RP]=]'s [=user account=] mapped to the <code>|credential|.{{Credential/id}}</code> in play (i.e., for the user being authenticated) holds `aaguid`, `dpk` and `scope` values corresponding to the extracted |attObjForDevicePublicKey| fields, then perform binary equality checks between the corresponding stored values and the extracted field values. The [=[RP]=] MAY have more than one set of {`aaguid`, `dpk`, `scope`} values mapped to the [=user account=] and <code>|credential|.{{Credential/id}}</code> pair and each set MUST be checked. + + If the above set of binary equality checks resulted in: + + <dl class="switch"> + : more than one match + :: Some form of error has occurred. It is indeterminate whether this is a known device. Terminate these verification steps. + + : exactly one match + :: This is likely a known device. + + If |fmt|'s value is "none" then there is no attestation signature to verify and this is a known [=device public key=] with a valid signature and thus a known device. Terminate these verification steps. + + Otherwise, check |attObjForDevicePublicKey|'s |attStmt| by performing a binary equality check between the corresponding stored and extracted |attStmt| values. If the result is: + <dl class="switch"> + : successful + :: This is a known [=device public key=] with a valid signature and valid attestation and thus a known device. Terminate these verification steps. + + Note: This authenticator is not generating a fresh per-response random nonce. + + : unsuccessful + :: Optionally, if attestation was requested and the RP wishes to verify it, verify that |attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |fmt|'s [=verification procedure=] given |attStmt|. See [[#sctn-device-publickey-attestation-calculations]]. [=[RP]=] policy may specifiy which attestations are acceptable. + + If the result is: + + <dl class="switch"> + : successful + :: This is a known [=device public key=] with a valid signature and valid attestation and thus a known device. Terminate these verification steps. + + : unsuccessful + :: Some form of error has occurred. It is indeterminate whether this is a known device. Terminate these verification steps. + </dl> + </dl> + + : zero matches + :: This is possibly a new [=device public key=] signifying a new device: + + 1. If |attObjForDevicePublicKey|.|dpk| did not match any of the [=[RP]=]'s stored |dpk| values for this [=user account=] and <code>|credential|.{{Credential/id}}</code> pair then: + + <dl class="switch"> + : If |fmt|'s value is "none": + :: There is no attestation signature to verify and this is a new device. Unless [=[RP]=] policy specifies that this attestation is unacceptable, store the extracted |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=]. Terminate these verification steps. + + : Otherwise: + :: Optionally, if attestation was requested and the RP wishes to verify it, verify that |attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |fmt|'s [=verification procedure=] given |attStmt|. See [[#sctn-device-publickey-attestation-calculations]]. [=[RP]=] policy may specifiy which attestations are acceptable. + + If the result is: + + <dl class="switch"> + : successful + :: This is a new [=device public key=] signifying a new device. Complete the steps in [[#sctn-verifying-assertion]] and, if those steps are successful, store the extracted |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=]. Terminate these verification steps. + + : unsuccessful + :: Some form of error has occurred. It is indeterminate whether this is a valid new device. Terminate these verification steps. + </dl> + </dl> + + 1. Otherwise there is some form of error: we recieved a known |dpk| value, but one or more of the accompanying |aaguid|, |scope|, or |fmt| values did not match what the [=[RP]=] has stored along with that |dpk| value. Terminate these verification steps. + </dl> + +1. Otherwise, the [=[RP]=] does not have |attObjForDevicePublicKey| fields presently mapped to this [=user account=] and <code>|credential|.{{Credential/id}}</code> pair: + + 1. If |fmt|'s value is "none" there is no attestation signature to verify. Complete the steps in [[#sctn-verifying-assertion]] and, if those steps are successful, store the extracted |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=]. Terminate these verification steps. + + 1. Otherwise, verify that |attStmt| is a correct [=attestation statement=], conveying a valid [=attestation signature=], by using the [=attestation statement format=] |fmt|'s [=verification procedure=] given |attStmt|. See [[#sctn-device-publickey-attestation-calculations]]. [=[RP]=] policy may specifiy which attestations are acceptable. + + If the result is: + + <dl class="switch"> + : successful + :: This is the first [=device public key=]—and thus device—mapped to this [=user account=] and <code>|credential|.{{Credential/id}}</code> pair. Complete the steps in [[#sctn-verifying-assertion]] and, if those steps are successful, store the extracted |aaguid|, |dpk|, |scope|, |fmt|, |attStmt| values indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=]. Terminate these verification steps. + + : unsuccessful + :: Some form of error has occurred. It is indeterminate whether this is a valid new device. Terminate these verification steps. + </dl> + +See also [[#sctn-device-publickey-extension-usage]]. + # User Agent Automation # {#sctn-automation}