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 &ldquo;none&rdquo;
+
+                    </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 &ldquo;none&rdquo;
+
+            </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 &mdash; {{AuthenticationExtensionsClientInputs}} and {{AuthenticationExtensionsClientOutputs}} &mdash; 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&mdash;along with the [=devicePubKey=] extension&mdash;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&nbsp;1") is returned along with the new [=device public key=]. Even if the [=user credential=] is [=backed up=], "dpk&nbsp;1" never leaves the [=generating authenticator=] ("authenticator&nbsp;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&nbsp;1"). This behavior on "authenticator&nbsp;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&nbsp;2"), the [=[RP]=]'s first {{CredentialsContainer/get()|get()}} call on "authenticator&nbsp;2" (that includes the `devicePubKey` extension) will produce an [=assertion=] including a signature by a <i>new</i> [=device-bound key=] ("dpk&nbsp;2"). Note that such a [=multi-device credential=] can be exercised on "authenticator&nbsp;2" without a {{CredentialsContainer/create()|create()}} having been performed on "authenticator&nbsp;2". The [=[RP]=]'s subsequent {{CredentialsContainer/get()|get()}} calls on "authenticator&nbsp;2", using the `devicePubKey` extension and the same user credential, yield further signatures by "dpk&nbsp;2".
+
+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=]&mdash;and thus device&mdash;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}