Skip to content

Commit

Permalink
begin reworking devicePubKey extension
Browse files Browse the repository at this point in the history
  • Loading branch information
equalsJeffH committed Jul 1, 2021
1 parent a34b489 commit 2832b5e
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 48 deletions.
65 changes: 30 additions & 35 deletions device-bound-key-pair.pv
Original file line number Diff line number Diff line change
Expand Up @@ -287,30 +287,27 @@ AttObjForDevicePublicKey = { ; Note: This object conveys an attested device publ

extensions: {

devicePublicKeyExtensionResult: {
devicePubKey: {

dpkSignatureValue { // Is a UNIQUE value per response. SIGNED by devicePrivateKey.
sig: { // Is a UNIQUE value per response. SIGNED by devicePrivateKey.
sign((clientDataHash || userCredentialId), devicePrivateKey)
// userCredentialId is second because it is variable length
}

AuthDataForDevicePublicKeyAttestation: {
rpIdHash // i.e., the "audience"
AAGUID // This is in `attestedCredentialData`, but attested cred data
// is not present in authentication assertions, so it needs to be
// here IF we wish this devicePublicKey extension result to be the
// same format whether it is returned as result of a registration
// or authn request.
devicePublicKey // The "attested" device public key.
}
aaguid // This is in `attestedCredentialData`, but attested cred data
// is not present in authentication assertions, so it needs to be
// here IF we wish this devicePublicKey extension result to be the
// same format whether it is returned as result of a registration
// or authn request.
dpk // The "attested" device public key.

fmt: "...attestation statement format identifier..."

attStmt: { // contains various items depending upon the attestation statement format,
// Crucially containing a signature value, generated using
// the device's (effective) AttestationPrivatekey, over this to-be-signed data:

AuthDataForDevicePublicKeyAttestation // SIGNED by AttestationPrivatekey.
( aaguid || dpk ) // SIGNED by AttestationPrivatekey.
// Crucially, this to-be-signed data does not include
// clientDataHash because the latter is specific to these
// current request, e.g., the hashed clientData includes
Expand Down Expand Up @@ -354,36 +351,34 @@ AttObjForDevicePublicKey = { ; Note: This object conveys an attested device publ

extensions: {

devicePublicKeyExtensionResult: {
devicePubKey: {

dpkSignatureValue { // Is a UNIQUE value per response. SIGNED by devicePrivateKey.
sig: { // Is a UNIQUE value per response. SIGNED by devicePrivateKey.
sign((clientDataHash || userCredentialId), devicePrivateKey)
// userCredentialId is second because it is variable length
// userCredentialId is second because it is variable length
}

AuthDataForDevicePublicKeyAttestationToBeSigned: {
rpIdHash // i.e., the "audience"
AAGUID // Needs to be here because if user cred is synced to "new" device
// a registration is not done on that device and the RP will receive
// a new devicePublicKey extension result from a get() call, and
// this will attest to the provenance of the authenticator.
devicePublicKey // The "attested" device public key.
}
aaguid // This is in `attestedCredentialData`, but attested cred data
// is not present in authentication assertions, so it needs to be
// here IF we wish this devicePublicKey extension result to be the
// same format whether it is returned as result of a registration
// or authn request.
dpk // The "attested" device public key.

fmt: "...attestation statement format identifier..."

attStmt: { // Contains various items depending upon the attestation statement format,
// crucially containing a signature value, generated using the device's (effective)
// AttestationPrivatekey, over this to-be-signed data:
AuthDataForDevicePublicKeyAttestation
//
// Crucially, this to-be-signed data DOES NOT INCLUDE
// `clientDataHash` because the latter is specific to this
// current request, e.g., the hashed clientData includes
// the request challenge.
//
// NOTE: This `attStmt` is a CONSTANT value per context per device
// per account per RP.
attStmt: { // contains various items depending upon the attestation statement format,
// Crucially containing a signature value, generated using
// the device's (effective) AttestationPrivatekey, over this to-be-signed data:

( aaguid || dpk ) // SIGNED by AttestationPrivatekey.
// Crucially, this to-be-signed data does not include
// clientDataHash because the latter is specific to these
// current request, e.g., the hashed clientData includes
// the request challenge.
//
// Note: This `attStmt` is a CONSTANT value per context
// per device per account per RP.
}
}

Expand Down
28 changes: 15 additions & 13 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Notes:
* the h1 tag is spec title that is rendered within the document window. Wrapping
may be controlled with break tags. The spec 'Level' value is not appended.
* the Title metadata value is what is rendered in the browser's titlebar, any break
tags will be rendered. It also has the spec 'Level' value (gratuitously) appended.
tags will be rendered. It also has the spec 'Level' value (gratuitously) appended.
-->

<h1>Web Authentication:<br>An API for accessing Public Key Credentials<br>Level 3</h1>
Expand Down Expand Up @@ -1019,7 +1019,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S
: \[DEPRECATED] <dfn>Resident Credential</dfn>
: \[DEPRECATED] <dfn>Resident Key</dfn>
:: Note: Historically, [=client-side discoverable credentials=] have been known as [=resident credentials=] or [=resident keys=].
Due to the phrases `ResidentKey` and `residentKey` being widely used in both the [=web authentication api|WebAuthn
Due to the phrases `ResidentKey` and `residentKey` being widely used in both the [=web authentication api|WebAuthn
API=] and also in the [=Authenticator Model=] (e.g., in dictionary member names, algorithm variable names, and
operation parameters) the usage of `resident` within their
names has not been changed for backwards compatibility purposes. Also, the term [=resident key=] is
Expand Down Expand Up @@ -1090,6 +1090,8 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S
: <dfn>Device-bound Key</dfn>
: <dfn>Device Private Key</dfn>
: <dfn>Device Public Key</dfn>
: <dfn>Device Key</dfn>
: <dfn>Secondary Key</dfn>
:: A [=hardware-bound device key pair=], also known as a [=device-bound key=], is a [=[RP]=]-specific and [=user credential=]-specific public key pair created upon a [=[RP]=]'s request via the [=devicePublicKey=] [=WebAuthn extension=].
The [=[RP]=] may supply this extension during both [=registration=] and [=authentication=].
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. The [=device public key=] is returned only to the [=[RP]=] that created the [=hardware-bound device key pair=]. See [[#sctn-device-publickey-extension]].
Expand Down Expand Up @@ -2354,7 +2356,7 @@ Note: Invoking this method from a [=browsing context=] where the [=Web Authentic
<div dfn-type="attribute" dfn-for="AuthenticatorResponse">
: <dfn>clientDataJSON</dfn>
:: This attribute contains a [[#clientdatajson-serialization|JSON-compatible serialization]] of the [=client data=], the [=hash of the serialized client data|hash of which=] is passed to the
authenticator by the client in its call to either {{CredentialsContainer/create()}} or {{CredentialsContainer/get()}} (i.e., the
authenticator by the client in its call to either {{CredentialsContainer/create()}} or {{CredentialsContainer/get()}} (i.e., the
[=client data=] itself is not sent to the authenticator).
</div>

Expand Down Expand Up @@ -4276,10 +4278,10 @@ Note: This specification does not define any data structures explicitly expressi
calling {{CredentialsContainer/create()|navigator.credentials.create()}} they select an [=attestation conveyance=] other than
{{AttestationConveyancePreference/none}} and verify the received [=attestation statement=] &mdash; will determine the employed
[=attestation type=] as a part of [=verification procedure|verification=]. See the "Verification procedure" subsections of
[[#sctn-defined-attestation-formats]]. See also [[#sctn-attestation-privacy]]. For all [=attestation types=] defined in this
[[#sctn-defined-attestation-formats]]. See also [[#sctn-attestation-privacy]]. For all [=attestation types=] defined in this
section other than [=self attestation|Self=] and [=None=], [=[RP]=] [=verification procedure|verification=] is followed by
matching the [=attestation trust path|trust path=] to an acceptable root certificate per step 21 of [[#sctn-registering-a-new-credential]].
Differentiating these [=attestation types=] becomes useful primarily as a means for determining if the [=attestation=] is acceptable
Differentiating these [=attestation types=] becomes useful primarily as a means for determining if the [=attestation=] is acceptable
under [=[RP]=] policy.

: <dfn>Basic Attestation</dfn> (<dfn>Basic</dfn>)
Expand Down Expand Up @@ -4371,7 +4373,7 @@ the [=authenticator=] MUST:

It is RECOMMENDED that any new attestation formats defined not use ASN.1 encodings,
but instead represent signatures as equivalent fixed-length byte arrays without internal structure,
using the same representations as used by COSE signatures as defined in [[!RFC8152]] and [[!RFC8230]].
using the same representations as used by COSE signatures as defined in [[!RFC8152]] and [[!RFC8230]].

The below signature format definitions satisfy this requirement and serve as examples for deriving the same for other signature algorithms not explicitly mentioned here:

Expand Down Expand Up @@ -4407,7 +4409,7 @@ In order to perform a [=registration ceremony=], the [=[RP]=] MUST proceed as fo
Let |credential| be the result of the successfully resolved promise.
If the promise is rejected, abort the ceremony with a user-visible error, or otherwise guide the user experience as
might be determinable from the context available in the rejected promise. For example if the promise is rejected with
an error code equivalent to "{{InvalidStateError}}", the user might be instructed to use a different [=authenticator=].
an error code equivalent to "{{InvalidStateError}}", the user might be instructed to use a different [=authenticator=].
For information on different error contexts and the circumstances leading to them, see [[#sctn-op-make-cred]].

1. Let |response| be <code>|credential|.{{PublicKeyCredential/response}}</code>.
Expand Down Expand Up @@ -4546,8 +4548,8 @@ In order to perform an [=authentication ceremony=], the [=[RP]=] MUST proceed as
1. Call {{CredentialsContainer/get()|navigator.credentials.get()}} and pass |options|
as the <code>{{CredentialRequestOptions/publicKey}}</code> option.
Let |credential| be the result of the successfully resolved promise.
If the promise is rejected, abort the ceremony with a user-visible error, or otherwise guide the user experience as might
be determinable from the context available in the rejected promise. For information on different error contexts and the
If the promise is rejected, abort the ceremony with a user-visible error, or otherwise guide the user experience as might
be determinable from the context available in the rejected promise. For information on different error contexts and the
circumstances leading to them, see [[#sctn-op-get-assertion]].

1. Let |response| be <code>|credential|.{{PublicKeyCredential/response}}</code>.
Expand Down Expand Up @@ -5393,7 +5395,7 @@ New extensions SHOULD follow the above convention.
publicKey: {
// Other members omitted for brevity
extensions: {
// An "entry key" identifying the "webauthnExample_foobar" extension,
// An "entry key" identifying the "webauthnExample_foobar" extension,
// whose value is a map with two input parameters:
"webauthnExample_foobar": {
foo: 42,
Expand Down Expand Up @@ -5926,15 +5928,15 @@ Note: In order to interoperate, user agents storing large blobs on authenticator
:: [=largeblob|This extension=] directs the user-agent to cause the large blob to be stored on, or retrieved from, the authenticator. It thus does not specify any direct authenticator interaction for [=[RPS]=].


## Device-bound public key extension (<dfn>devicePublicKey</dfn>) ## {#sctn-device-publickey-extension}
## Device-bound public key extension (<dfn>devicePubKey</dfn>) ## {#sctn-device-publickey-extension}

This [=client extension|client=] [=registration extension=] and [=authentication extension=] provides a [=[RP]=] with a "device continuity" signal. This is done by creating a [=[RP]=]-specific [=hardware-bound device key pair=] on the [=client device=], if such a key pair does not already exist for the [=user credential=] being created or exercised, and returning the attested [=device public key=] along with a signature by the [=device private key=] to the [=[RP]=]. This is done each time this [=devicePublicKey=] extension is included with either a <code>{{CredentialsContainer/create()|navigator.credentials.create()}}</code> or <code>{{CredentialsContainer/create()|navigator.credentials.get()}}</code> call.

If the [=client device=] 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 [=devicePublicKey=] extension output generated.


: Extension identifier
:: `devicePublicKey`
:: `devicePubKey`

: Operation applicability
:: [=registration extension|Registration=] and [=authentication extension|authentication=]
Expand Down Expand Up @@ -5972,7 +5974,7 @@ If the [=client device=] is incapable of generating a [=hardware-bound device ke
:: For both [=authenticatorMakeCredential=] and [=authenticatorGetAssertion=] operations:
1. Create or select the [=user credential=] as usual.
1. If a [=hardware-bound device key pair=] does not already exist for this [=user credential=] 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 `devicePublicKey` 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 conveyed in [=attested credential data=].
1. Let `devicePublicKey` 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 `clientDataHash` be the [=hash of the serialized client data=].
1. Let `dpkSignatureValue` be the result of `sign(devicePrivateKey, (clientDataHash || devicePublicKey))` using the signature algorithm appropriate for the `devicePrivateKey`'s public key algorithm.
Expand Down

0 comments on commit 2832b5e

Please sign in to comment.