-
Notifications
You must be signed in to change notification settings - Fork 218
device public key extension #1663
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 74 commits
6719e05
4a6b8fe
5d1662d
1622df2
5e684aa
71afdbe
8040d13
38131e6
ad71ff1
094d385
e66eb2d
618b2de
1e97952
8f0d66d
43e03c8
503a027
87340d7
cbb066f
1e72a00
99a6b79
e9db523
68ebaa2
8b5702c
da82c2e
f84069b
f6663cb
22e325d
a3ed05b
6382444
768d900
fd9ea00
a34b489
2832b5e
e47c5f8
75c8f25
4515d63
c208e19
591cded
59260f0
c3487a2
0e8d3b3
6b216db
25b07e6
2da4504
f943bbc
e23ccfe
7a1e2ee
8a420eb
d2b529b
666718a
f9e861c
e1a4383
9182fa1
89e2660
54eb767
f6fcee8
1747dff
12ec079
59f2909
8b4d51c
c5f3b2d
4ebd028
4f18790
66e67bd
9ac274a
fcc6a68
73cc7ff
7c5393c
3d16662
aee534c
7c3e2e8
90593b9
d0bef33
89cec45
db63d69
9a78683
d52342c
e23c4b9
b8ec5b8
0bb9aaa
3237896
d652787
55e64c9
41ffcbf
f131d68
e1e6d94
23ea3ef
683ad4d
17f3aa2
b4e8d0e
619ebb9
2730294
f0fe8f2
f145234
b8d8567
d92bad2
6d45aba
eb598ff
b7289e1
dcfb392
cbb6b5d
ccfd0b4
f3315b5
27ef223
bfce0cf
20dd35c
27d0895
e30cdb1
38fb4e1
0c7fad0
844cff7
6fbfccf
4e67faa
832c2e8
6940a43
7b531a8
04ddb48
3cba94c
47017e4
16a846a
2ec8861
5c1cd98
5c6c23d
a026a5b
ec03d4d
88be1a6
3430c95
5af393d
fe333fe
ece61f0
4279e6e
d25fd53
8966fe6
d671894
ca1b0c6
6112877
ed0b779
9bd0e3d
759ce04
8aa160c
bff403d
fba2725
f780870
6ae32a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1068,8 +1068,9 @@ 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=]. | ||
and [=scoped=] to a specific [=[WRP]=]. It thus forms the central part of a [=public key credential source=]. | ||
|
||
A [=credential public key=] is the public key portion of a [=credential key pair=]. | ||
The [=credential public key=] is returned to the [=[RP]=] during a [=registration ceremony=]. | ||
|
@@ -1078,17 +1079,26 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S | |
The [=credential private key=] is bound to a particular [=authenticator=] - its [=managing authenticator=] - | ||
and is expected to never be exposed to any other party, not even to the owner of the [=authenticator=]. | ||
|
||
Note that in the case of [=self | ||
attestation=], the [=credential key pair=] is also used as the [=attestation key pair=], see [=self attestation=] | ||
for details. | ||
Note: In the case of [=self attestation=], the [=credential key pair=] is also used as the [=attestation key pair=], see [=self attestation=] for details. | ||
|
||
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. | ||
[[FIDO-U2F-Message-Formats]] and some parts of this specification that relate to it. Also, the term [=user credential=] is occasionally used to synonymously refer to [=credential public key=] and [=user public key=]. | ||
|
||
: <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=]. | ||
|
||
: <dfn>Hardware-bound Device Key Pair</dfn> | ||
: <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 [=devicePubKey=] [=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]]. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it worth calling out that this is for authenticators where the [=credential public key=] might not be hardware-bound? I think in the minds of readers for some time they're probably coming to WebAuthn with that assumption and might be unmoored by this description without that context. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, good catch, I've submitted #1665 re explicitly defining the "synced | hardware-bound" credential notions. |
||
|
||
|
||
: <dfn>Human Palatability</dfn> | ||
:: An identifier that is [=human palatability|human-palatable=] is intended to be rememberable and reproducible by typical human | ||
users, in contrast to identifiers that are, for example, randomly generated sequences of bits [[EduPersonObjectClassSpec]]. | ||
|
@@ -5952,6 +5962,142 @@ However, [=authenticators=] that do not utilize [[!FIDO-CTAP]] do not necessaril | |
:: [=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>devicePubKey</dfn>) ## {#sctn-device-publickey-extension} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it fair to say this extension is only useful in cases where the credential public key registration is performed with direct attestation, and further that this not be a self attestation? Really, what's the point in declaring a device public key if there is no real verifiable root of trust of the authenticator attestation of the associated credential public key? In the case of "none" attestation, there is no AAGUID anyway, so you can't even provide a meaningful value for aaguid in devicePubKey extension output. I would argue that for "self" attestation it's meaningless as well since self attestation can be spoofed by any client. If the above assertions are agreed, I think this needs to be made more explicit, and that there should be guidance to suggest that this extension only be used with authenticators and in contexts providing a direct attestation using a verifiable root of trust. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The DPK is providing a device continuity signal whose usefulness is arguably independent of what sort of attestation of the user credential may have been supplied at the time the user credential public key was originally registered. The devicePubKey extension output in conjunction with create() or get() output demonstrates authenticator's possession of both the (potentially synced) user credential private key and the device private key, simultaneously, by employing a nested set of signatures beginning with the authenticator's attestation private key.
"none" is an *attestation conveyance" preference (also having a corresponding "attestation statement format"), not an attestation type, and signals the client platform to modify the attestation returned by the authenticator before returning the attstn object to the RP. Thus the authnr may indeed have an AAGUID to employ with the device public key extension. "self attestation" is an "attestation type" and (offhand) seems not applicable/appropriate for use in attesting to device public keys. "Basic" attestation type (aka "batch attestation") is the primarily applicable attestation type. The AttCA and AnonCA types require further thinking. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As an RP, I can certainly receive a "none" attestation statement format, and that can and does happen at times regardless of the RP-requested attestation conveyance preference. One such example is the current use of Apple passkey credentials. I can't see any point in accepting values of this extension containing a "none" attestation statement format. I think when the TODO part of this PR is being done ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, though, the attestation you're referring to there is that of the user credential. As @agl notes in his above comment: "So the two attestations (user cred's, and DPK's) could happen to be equal, but that wouldn't generally be true.", and so the DPK attestation in this case could be tied to hardware roots of trust even though the user credential's attestation is not. It will also depend on what DPK attestation types/formats/conveyances we determine are appropriate for DPKs spec-wise (i.e., on a technical basis), and then what a given DPK-supporting authenticator actually implements.
Is it not up to RPs to decide what to trust? E.g., presently in Step 20 of section 7.1. Registering a New Credential assessing "trustworthiness" is done by the RP according to the RP's policies, and the webauthn spec does not attempt to stipulate such policies. Nominally, if an RP receives a "none" DPK attestation from an authenticator (which I suspect could potentially happen regardless of what the spec ends up saying), then it seems it is up to the RP to decide what to do, which could vary, e.g., based on how sophisticated (or not) the RP's session risk scoring is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes it is ultimately up to the RP to decide what to do, however practical guidance can and should be provided as to the verification rules for an RP. I do not find the use of the DPK extension intuitive, and the stronger the guardrails here the better. For example I cannot think of any practical scenario in which an RP, having requested the DPK extension, should then accept a DPK attestation that cannot be verified back to a hardware root of trust. After all, that's the entire point of the extension in the first place - provide a device-bound continuity check. A DPK attestation containing attestation statement format of "none" or "self" provides no such assurance. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardware may not be available all the time on every device user has. Which means that there may not be hardware root of trust all the time. Then it is up to the RP to decide what to do in its risk analysis. What DPK is telling that it is a new device in that case, which is still a requirement to figure out even when there may not be a hardware root of trust. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can logically "infer" or fallback to the posture of it being a new device if no DPK is present. For example, how does DPK provides any better or more reliable a trust signal over a persistent cookie if it isn't attested? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The DPK lives in the authenticator, so it can be involved in a sign-in request on a new device as long as a known authenticator is used. It's also likely longer lived than a cookie.
agl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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 [=devicePubKey=] extension is included with either a <code>{{CredentialsContainer/create()|navigator.credentials.create()}}</code> or <code>{{CredentialsContainer/create()|navigator.credentials.get()}}</code> call. | ||
emlun marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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 [=devicePubKey=] extension output generated. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seem feeling here about giving context. Most authenticators are capable of creating a hardware-bound key pair but won't honour this extension :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. To be addressed along with issue #1665.
equalsJeffH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
<div class="note"> | ||
Note 1: 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 when used consistently with both <code>{{CredentialsContainer/get()|navigator.credentials.create()}}</code> and <code>{{CredentialsContainer/get()|navigator.credentials.get()}}</code> operations. When a user's credential is synced to a new device, the [=[RP]=] will receive a new [=device public key=] on the initial <code>{{CredentialsContainer/get()|navigator.credentials.get()}}</code> invocation on the new device. Note that a synced credential will be exercised for authentication on a new device without a <code>{{CredentialsContainer/get()|navigator.credentials.create()}}</code> having been performed on that new device). | ||
equalsJeffH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
A usage example is thus: | ||
|
||
Say that a sign-in request appears along with some geolocation signal that has not been seen for this [=user account=] before, and is outside of the typical working hours observed for the account. The risk may be deemed high enough not to allow the request, even with a synced credential. But if a device-bound signature can also be presented and it's a [=device-bound key=] that is well established for this user, then that may tip the balance. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. “synced credential” is mentioned here but is probably the first mention of such a thing in the spec? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. To be addressed along with issue #1665. |
||
|
||
Note 2: A [=[RP]=] utilizing this extension will only need to perform thorough verification of the [=device public key=]'s `attObjForDevicePublicKey` once: i.e., the first time a new [=device public key=] is received. "Thorough verification" means verifying the attestation statement `$$attStmtType` (per the [= attestation statement format=]'s signing procedure, see [[#sctn-generating-an-attestation-object]]) as well as the `sig` value. Upon successful verification, the [=[RP]=] SHOULD cache the `aaguid`, `dpk`, `scope`, and `$$attStmtType` values. Upon receiving subsequent `devicePubKey` extension output values, the [=[RP]=] can, along with verifying the `sig` value of each extension output occurrance, do binary equality checks against the cached `aaguid`, `dpk`, `scope`, and `$$attStmtType` values against those returned in the extension output. | ||
</div> | ||
|
||
Issue: TODO: write an explicit subsection wrt "how to verify a `attObjForDevicePublicKey`" ? (i.e., analogous to the "verification procedue" steps provided in [[#sctn-defined-attestation-formats|attestation statement format definitions]], and also steps 7 through 19 in [[#sctn-verifying-assertion]]) | ||
|
||
: Extension identifier | ||
:: `devicePubKey` | ||
|
||
: 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 devicePubKey; // Explicitly set this to \`true\`! | ||
}; | ||
</xmp> | ||
|
||
: Client extension processing | ||
:: If {{AuthenticationExtensionsClientInputs/devicePubKey}} is [TRUE], the client creates the authenticator extension input from the client extension input. If {{AuthenticationExtensionsClientInputs/devicePubKey}} is [FALSE], the client ignores this extension. | ||
|
||
: Client extension output | ||
:: An ArrayBuffer containing the [=device public key attestation object=] that was returned in the authenticator extension output. | ||
<xmp class="idl"> | ||
partial dictionary AuthenticationExtensionsClientOutputs { | ||
ArrayBuffer devicePubKey; | ||
}; | ||
</xmp> | ||
|
||
|
||
: Authenticator extension input | ||
:: The Boolean value [TRUE], encoded in CBOR (major type 7, value 21). | ||
|
||
``` | ||
$$extensionInput //= ( | ||
devicePubKey: true | ||
) | ||
``` | ||
|
||
: Authenticator extension output | ||
:: The <dfn>device public key attestation object</dfn>, defined by the `attObjForDevicePublicKey` type: | ||
|
||
``` | ||
$$extensionOutput //= ( | ||
devicePubKey: attObjForDevicePublicKey, | ||
) | ||
agl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
attObjForDevicePublicKey = { ; Note: This object conveys an attested | ||
; device public key and is analogous to \`attObj\`. | ||
|
||
sig: bstr, ; Result of sign((clientDataHash || userCredentialId), devicePrivateKey) | ||
; Note that this signature value is unique per-response | ||
; because the client data contains the per-request challenge. | ||
|
||
aaguid: bstr, ; Authenticator's AAGUID (16 bytes fixed-length) | ||
sbweeden marked this conversation as resolved.
Show resolved
Hide resolved
|
||
; 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. | ||
|
||
; See https://www.w3.org/TR/webauthn/#sctn-generating-an-attestation-object | ||
; | ||
; Attestation statement formats define the \`fmt\` and \`attStmt\` members of | ||
; $$attStmtType. | ||
agl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
; | ||
; In summary, the \`attStmt\` will (typically) contain: | ||
; (1) a SIGNATURE value calculated (using the attestation private key) | ||
; over (aaguid || dpk). | ||
; (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, | ||
} | ||
|
||
``` | ||
|
||
: Authenticator extension processing | ||
:: For both [=authenticatorMakeCredential=] and [=authenticatorGetAssertion=] operations: | ||
1. Create or select the [=user credential=] as usual, and let `userCredentialId` be its <code>[=credentialId=]</code>. | ||
|
||
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 `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 `clientDataHash` be the [=hash of the serialized client data=]. | ||
|
||
1. Let `sig` be the result of signing the concatenation of `clientDataHash` and `userCredentialId` using the `devicePrivateKey` and the signature algorithm appropriate for the `devicePrivateKey`'s public key algorithm: `sign((clientDataHash || userCredentialId), devicePrivateKey)`. | ||
|
||
1. Let `scope` have the value zero (0x00) if this is an "entrie-device" [=device public key=]. Otherwise, let `scope` have the value one (0x01), indicating a more narrow per-app scope. | ||
equalsJeffH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
1. Let the `$$attStmtType` values be the result of generating an attestation statement in the [=attestation statement format=] appropriate for the [=user credential=], although substituting the authenticator's <code>[=aaguid=]</code> for |authenticatorData|, and substituting `userCredentialId` for |clientDataHash| in the [=attestation statement format=]'s signing procedure (see [[#sctn-generating-an-attestation-object]]). Attestation statement formats define the `fmt` and `attStmt` members of the `$$attStmtType` expansion. | ||
|
||
In summary, the `$$attStmtType` values typically contain a signature value calculated over the bytes of `(clientDataHash || userCredentialId)`, the attestation certificate or public key, and supporting certificates, if any. | ||
|
||
Note: The details of the `$$attStmtType` values are dependent upon the particular [=attestation statement=] format. See [[#sctn-attestation-formats]]. | ||
|
||
1. Let the `devicePubKey` [=authenticator extension output=] be a [=CBOR=] map as defined by `attObjForDevicePublicKey` above, whose values for its `sig`, `aaguid`, `dpk`, `scope`, and `$$attStmtType` members are as calculated in the prior steps. | ||
|
||
|
||
|
||
# User Agent Automation # {#sctn-automation} | ||
|
||
For the purposes of user agent automation and [=web application=] testing, this document defines a number of [[WebDriver]] [=extension commands=]. | ||
|
Uh oh!
There was an error while loading. Please reload this page.