Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .cspell/acronyms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ VCHAR
attestn
RSASSA
PKCS1
bytestrings
bytestrings
cmtg
231 changes: 229 additions & 2 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,7 @@ BCP 14 [[!RFC2119]] [[!RFC8174]] when, and only when, they appear in all capital
: <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=].

Expand All @@ -1060,6 +1061,20 @@ BCP 14 [[!RFC2119]] [[!RFC8174]] when, and only when, they appear in all capital
Note: The [=credential public key=] is referred to as the [=user public key=] in FIDO UAF [[UAFProtocol]], and in FIDO U2F
[[FIDO-U2F-Message-Formats]] and some parts of this specification that relate to it.

: <dfn>Credential Manager Trust Group Key</dfn>
: <dfn>Credential Manager Trust Group Private Key</dfn>
: <dfn>Credential Manager Trust Group Public Key</dfn>
:: A [=Credential Manager Trust Group Key=], is a [=authenticator=]- / [=credential manager=]-, [=[RP]=]-, and [=user credential=]-specific
Copy link
Member

Choose a reason for hiding this comment

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

nit: having "/[=credential manager=]" everywhere is awkward. Do we really need to introduce this term to the spec? I'd say drop the "credential manager". It's cleaner.

public key pair created upon a [=[RP]=]'s request via the [=cmtgKey=] [=WebAuthn extension=].

The [=Credential Manager Trust Group Public Key=] is conveyed to the [=[RP]=] during a
[=registration ceremony|registration=] or [=authentication ceremony|authentication=] ceremony
as part of the [=cmtgKey=] extension output.

The [=Credential Manager Trust Group Private Key=] is stored by the [=authenticator=] / [=credential manager=].

See [[#sctn-cmtg-key-extension]].

: <dfn>Credential Properties</dfn>
:: A [=credential property=] is some characteristic property of a [=public key credential source=], such as whether it is a
[=client-side discoverable credential=] or a [=server-side credential=].
Expand Down Expand Up @@ -7738,15 +7753,227 @@ However, [=authenticators=] that do not utilize [[!FIDO-CTAP]] do not necessaril


: Authenticator extension processing
:: [=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]=].
:: [=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]=].


## Authenticator Extensions ## {#sctn-defined-authenticator-extensions}

This section defines extensions that are both [=client extensions=] and [=authenticator extensions=].

This section is currently empty.
### Credential Manager Trust Group Key extension (<dfn>cmtgKey</dfn>) ### {#sctn-cmtg-key-extension}

This [=authenticator extension|authenticator=] [=registration extension=] and [=authentication extension=]
enables an [=authenticator=]/[=credential manager=] to provide a signal to a [=[RP]=]
that two devices possessing the same [=backed up=] credential
have established a trust relationship through a non-remote interaction,
such as a local setup or physical proximity.

This is done by creating an additional [=public key credential source=]-specific [=Credential Manager Trust Group key pair=]
in the [=authenticator=], if such a key pair needs to be created for the
[=public key credential source=] being created or exercised on the specific device,
and returning one of the [=Credential Manager Trust Group public keys=]
along with a signature by the [=Credential Manager Trust Group private key=] to the [=[RP]=].

This is done each time this [=cmtgKey=] extension is included with either a
{{CredentialsContainer/create()|navigator.credentials.create()}} or {{CredentialsContainer/get()|navigator.credentials.get()}} call.

#### Relying Party Usage #### {#sctn-cmtg-key-extension-usage}
Copy link
Member

Choose a reason for hiding this comment

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

nit: the details for when a new key pair is minted should not be under "relying party usage" imo. Paragraphs 3 onwards are all about authenticator actions, with no mention of what RPs should do with this information. Those can go under the previous heading instead.


This extension is intended for use by those [=[RPS]=] employing risk-analysis systems informing their sign-in decisions.

This extension provides a "trust group" signal <i>when used consistently</i> with both
{{CredentialsContainer/create()|navigator.credentials.create()}} and {{CredentialsContainer/get()|navigator.credentials.get()}} operations.
Copy link
Member

Choose a reason for hiding this comment

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

optional: consider adding details on how to use the signal and what it means. Something like:

When a relying party observes a new CMTG public key during a create or assert operation, the CMTG public key should be added to the credential record. On subsequent assertions, after validating the CMTG signature, if an existing CMTG public key is observed, the relying party should use this as a signal that there is a strong relationship between the device that produced an assertion and previously used devices. Otherwise, if the CMTG public key is new, the device producing the assertion may not have a strong relationship with previously used devices. The new CMTG public key should be added to the credential record, signaling the start of a separate trust group.

An invalid CMTG signature is not expected and should invalidate the entire request.


Copy link
Member

Choose a reason for hiding this comment

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

Let's also add some javascript examples, both on the spec and the explainer. The explainer doesn't have any javascript for processing a response.

When a [=[RP]=] uses the `cmtgKey` extension during a {{CredentialsContainer/create()|create()}} option on the first device (`dev1`),
the selected [=authenticator=]/[=credential manager=] (`cm1`) generates a new [=Credential Manager Trust Group Key=] pair.
The extension output includes the public key and a signature generated using the
[=Credential Manager Trust Group private key=] (`cmtgKey1`).

A subsequent {{CredentialsContainer/get()|get()}} operation (with the `cmtgKey` extension) on `dev1`
using the same credential and the same authenticator/credential manager (`cm1`),
will include a signature from the same [=Credential Manager Trust Group private key=], `cmtgKey1`.

`cmtgKey1` is only synced to an authenticator/credential manager on another device (`dev2`)
if a non-remote relationship exists between the source (`dev1`) and destination (`dev2`) device.

If a third device (`dev3`) is introduced (with `cm1` configured),
which does not share a non-remote relationship with `dev1`or `dev2`
(as evaluated by the authenticator/credential manager, `cm1`),
a new [=Credential Manager Trust Group private key=] (`cmtgKey2`) is generated and its public key
is returned to the [=[RP]=] along with it's public key, signaling the start of a separate trust group.

#### Extension Definition #### {#sctn-cmtg-key-extension-definition}

: Extension identifier
:: `cmtgKey`

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

: Client extension input
:: The Boolean value [TRUE] to indicate that this extension is requested by the [=[RP]=].
<xmp class="idl">
partial dictionary AuthenticationExtensionsClientInputs {
boolean cmtgKey;
};
</xmp>

: Client extension processing
:: None, except creating the authenticator extension input from the client extension input.
Copy link
Member

Choose a reason for hiding this comment

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

Somewhere we need a discussion of what happens when the UA understands the extension but the authenticator does not. In that case, no CMTG will be returned. Perhaps this is the right place for it.


: Client extension output
:: An ArrayBuffer containing the COSE_Key-encoded [=Credential Manager Trust Group public key=]
that was returned in the authenticator extension output
and the signature returned as the [=unsigned extension output=].
<xmp class="idl">
dictionary AuthenticationExtensionsCmtgKeyOutputs {
ArrayBuffer cmtgKey;
ArrayBuffer signature;
};

partial dictionary AuthenticationExtensionsClientOutputs {
AuthenticationExtensionsCmtgKeyOutputs cmtgKey;
};
</xmp>

: Authenticator extension input
:: The Boolean value [TRUE], encoded in CBOR (major type 7, value 21).

```
$$extensionInput //= (
cmtgKey: true,
)
```

: Authenticator extension output
:: A byte string containing the COSE_Key-encoded [=Credential Manager Trust Group public key=]:

```
$$extensionOutput //= (
cmtgKey: bstr,
Copy link
Member

Choose a reason for hiding this comment

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

And the signature as well?

)
```

: Unsigned extension output
:: A CBOR byte string containing a signature generated with the Credential Manager Trust Group Key's 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=],
terminate these processing steps as this extension only applies to [=multi-device credentials=].

1. If a [=Credential Manager Trust Group Key=] 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 in the [=authenticator=]/[=credential manager=],
Comment on lines +7869 to +7870
Copy link
Member

Choose a reason for hiding this comment

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

As written, CMTG keys seem to have a 1:1 relationship with public key credentials. Even if we don't define the mechanism that determines which devices belong to a trust group, we should incorporate the concept of trust groups to the execution steps:

  1. If a [=Credential Manager Trust Group Key=] does not already exist for this [=public key credential source=] and [=credential manager trust group=] tuple,

We should have a definition for "credential manager trust group" as well.

create it using the same public key algorithm as that used by the [=user credential=]'s [=credential key pair=],
otherwise locate the existing [=Credential Manager Trust Group Key=].

1. Let |cmtgPublicKey| be the newly created or existing [=Credential Manager Trust Group Key=],
in COSE_Key format [=credentialPublicKey|in the same fashion as for the user credential's credentialPublicKey=].

1. Let |cmtgPrivateKey| be the newly created or existing [=Credential Manager Trust Group private key=].

1. Let |cmtgKeySig| be the result of signing the [=assertion signature=] [input](#fig-signature) with |cmtgPrivateKey|,
using the same signature algorithm as that used by the [=user credential=]'s [=credential key pair=].

Note: the [=assertion signature=] [input](#fig-signature) and |cmtgKeySig| covers the [=[RP]=]'s {{PublicKeyCredentialCreationOptions/challenge}}
because it includes the [=hash of the serialized client data=]. Thus the [=[RP]=] knows that |cmtgKeySig| is a fresh signature.

1. Output |cmtgKeySig| as the extension's [=unsigned extension output=].

Note: |cmtgKeySig| 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.

#### `cmtgKey` Extension Output Verification Procedures #### {#sctn-cmtg-key-extension-verification}

Verifying the <code>[=cmtgKey=]</code> extension output is performed by the [=[RP]=] whenever a [=Credential Manager Trust Group Key=] is returned within the extension output.

##### Registration (`create()`) ##### {#sctn-cmtg-key-extension-verification-create}

If the `cmtgKey` extension was included on a {{CredentialsContainer/create()|navigator.credentials.create()}} call
and a [=Credential Manager Trust Group Key=] is returned by an [=authenticator=],
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|.

1. Let |attObjForCmtgKey| be the value of the {{AuthenticationExtensionsClientOutputs/cmtgKey}} member of |clientExtensionResults|.
Copy link
Member

Choose a reason for hiding this comment

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

I believe from what you wrote above, clientExtensionResults["cmtgKey"] is not CBOR, but a javascript AuthenticationExtensionsCmtgKeyOutputs object.

So I think you want something like

1. Let |cmtgKey| be the value of the `cmtgKey` member of |clientExtensionResults|.
2. Let |attObjForCmtgKey| be the value of the `cmtgKey` member of |cmtgKey|
<-- this leads me to believe you might want to name that member something else
    to avoid confusion. Your call.
3. Verify that |attObjForCmtgKey| is valid CBOR COSE...
4. Verify that |cmtgKey|.{AuthenticationExtensionsCmtgKeyOutputs/signature}} is a valid signature over...


1. Verify that |attObjForCmtgKey| is valid CBOR conforming to the syntax defined above
Copy link
Member

Choose a reason for hiding this comment

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

Related to the comment above, |signature| and |cmtgKey| are not CBOR members, they're javascript members. Perhaps you wanted to have the RP verify the integrity of the CBOR encoded COSE public key?

and perform CBOR decoding on it to extract the contained fields: |signature|, |cmtgKey|.

Note: The latter |attObjForCmtgKey| 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 the algorithm used for the signature in {{AuthenticationExtensionsCmtgKeyOutputs}}
matches the COSE algorithm identifier of the [=credentialPublicKey=] within the [=attestedCredentialData=]
of |authData|.

1. Verify that {{AuthenticationExtensionsCmtgKeyOutputs/signature}} is a valid signature over the [=assertion signature=] [input](#fig-signature)
(i.e. `authData` and `hash`) by the [=Credential Manager Trust Group Key=] |cmtgKey|.

1. Complete the steps from [[#sctn-registering-a-new-credential]] and, if those steps are successful,
store the |cmtgKey| value indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=].
Copy link
Member

Choose a reason for hiding this comment

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

You should refer to the credential record object here instead.

In fact, the credential record object should be updated to add CMTG public keys.


See also [[#sctn-cmtg-key-extension-usage]] for further details.

##### Authentication (`get()`) ##### {#sctn-cmtg-key-extension-verification-get}
Copy link
Member

Choose a reason for hiding this comment

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

(Same comments apply as above)


If the `cmtgKey` 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 `cmtgKey` is acceptable.

1. Let |attObjForCmtgKey| be the value of the `cmtgKey` member of |clientExtensionResults|.

1. Verify that |attObjForCmtgKey| is valid CBOR conforming to the syntax defined above
and perform CBOR decoding on it to extract the contained fields: |signature|, |cmtgKey|.

Note: The latter |attObjForCmtgKey| 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 the algorithm used for the signature in {{AuthenticationExtensionsCmtgKeyOutputs}}
matches the algorithm of the public key previously stored for the credential
identified by the [=credentialId=].

1. Verify that {{AuthenticationExtensionsCmtgKeyOutputs/signature}} is a valid signature over the [=assertion signature=] [input](#fig-signature)
(i.e. `authData` and `hash`) by the [=Credential Manager Trust Group public key=] |cmtgKey|.
(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)
Copy link
Member

Choose a reason for hiding this comment

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

You should refer to the credential record object here as well.

hold a `cmtgKey` value corresponding to the extracted |attObjForCmtgKey| fields,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
hold a `cmtgKey` value corresponding to the extracted |attObjForCmtgKey| fields,
holds a `cmtgKey` value corresponding to the extracted |attObjForCmtgKey| fields,

then perform binary equality checks between the corresponding stored value and the extracted field value.
The [=[RP]=] MAY have more than one `cmtgKey` value mapped to the [=user account=] and <code>|credential|.{{Credential/id}}</code> pair
Copy link
Member

Choose a reason for hiding this comment

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

Instead of this note why not make L7937 a loop per stored CMTG key.

and each value 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.
Terminate these verification steps.

: exactly one match
:: The [=authenticator=]/[=credential manager=] is part of a previously seen trust group:

Terminate these verification steps.

: zero matches
:: This is possibly a new [=Credential Manager Trust Group Key=], signifying a new trust group:

Store the extracted |cmtgKey| value indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=].
Terminate these verification steps.

</dl>

1. Otherwise, the [=[RP]=] does not have |attObjForCmtgKey| fields presently mapped to this [=user account=]
and <code>|credential|.{{Credential/id}}</code> pair:

1. Store the extracted |cmtgKey| value indexed to the <code>|credential|.{{Credential/id}}</code> in the [=user account=].
Terminate these verification steps.

See also [[#sctn-cmtg-key-extension-usage]].
Copy link
Member

Choose a reason for hiding this comment

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

Would it be possible to add virtual authenticator support for this feature? It feels it should be pretty simple, and it'll be required to write WPTs.


# User Agent Automation # {#sctn-automation}

Expand Down