From bcd428d84e3f0094fc75a77aa45985bd4e0ff9f9 Mon Sep 17 00:00:00 2001 From: Adam Langley <agl@chromium.org> Date: Wed, 25 Sep 2024 10:05:32 -0700 Subject: [PATCH 01/26] Mark Android SafetyNet attestation as deprecated. Google have [announced](https://developer.android.com/privacy-and-security/safetynet/deprecation-timeline) the deprecation of SafetyNet in general, and [specifically for](https://android-developers.googleblog.com/2024/09/attestation-format-change-for-android-fido2-api.html) WebAuthn. This change adds a note in the SafetyNet section that it may be removed in a future revision of the spec. --- index.bs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.bs b/index.bs index 0db3e9d0a..2ff1e861f 100644 --- a/index.bs +++ b/index.bs @@ -6659,6 +6659,8 @@ data</dfn> is identified by the OID `1.3.6.1.4.1.11129.2.1.17`, and its schema i ## Android SafetyNet Attestation Statement Format ## {#sctn-android-safetynet-attestation} +Note: This format is deprecated and is expected to be removed in a future revision of this document. + When the [=authenticator=] is a [=platform authenticator=] on certain Android platforms, the attestation statement may be based on the [SafetyNet API](https://developer.android.com/training/safetynet/attestation#compat-check-response). In this case the [=authenticator data=] is completely controlled by the caller of the SafetyNet API (typically an application From 2e2e3c6dc421a89a6801233f724cbd33bc4f0ef5 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Fri, 27 Sep 2024 16:12:28 +0200 Subject: [PATCH 02/26] Deprecate rp.name --- index.bs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/index.bs b/index.bs index 0db3e9d0a..1c747ae51 100644 --- a/index.bs +++ b/index.bs @@ -3593,14 +3593,20 @@ associated with or [=scoped=] to, respectively. : <dfn>name</dfn> :: A [=human palatability|human-palatable=] name for the entity. Its function depends on what the {{PublicKeyCredentialEntity}} represents: - - When inherited by {{PublicKeyCredentialRpEntity}} it is a [=human palatability|human-palatable=] identifier for the [=[RP]=], intended only + - \[DEPRECATED] When inherited by {{PublicKeyCredentialRpEntity}} it is a [=human palatability|human-palatable=] identifier for the [=[RP]=], intended only for display. For example, "ACME Corporation", "Wonderful Widgets, Inc." or "ОАО Примертех". + This member is deprecated because many [=clients=] do not display it, + but it remains a required dictionary member for backwards compatibility. + [=[RPS]=] MAY as a safe default set this equal to the [=RP ID=]. + - [=[RPS]=] SHOULD perform enforcement, as prescribed in Section 2.3 of [[!RFC8266]] for the Nickname Profile of the PRECIS FreeformClass [[!RFC8264]], when setting {{PublicKeyCredentialEntity/name}}'s value, or displaying the value to the user. - - This string MAY contain language and direction metadata. [=[RPS]=] SHOULD consider providing this information. See [[#sctn-strings-langdir]] about how this metadata is encoded. + - This string MAY contain language and direction metadata. + [=[RPS]=] SHOULD consider providing this information if setting the member to a value other than the [=RP ID=]. + See [[#sctn-strings-langdir]] about how this metadata is encoded. - [=Clients=] SHOULD perform enforcement, as prescribed in Section 2.3 of [[!RFC8266]] for the Nickname Profile of the PRECIS FreeformClass [[!RFC8264]], From ae49b8200c5fadbbf60be748afee0f96813353d2 Mon Sep 17 00:00:00 2001 From: philomathic_life <15947783+zacknewman@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:00:43 +0000 Subject: [PATCH 03/26] Fix CredentialRequestOptions hyperlink --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 0db3e9d0a..bb369eee3 100644 --- a/index.bs +++ b/index.bs @@ -6099,7 +6099,7 @@ provide this chain in the attestation information. In order to perform an [=authentication ceremony=], the [=[RP]=] MUST proceed as follows: 1. Let |options| be a new {{CredentialRequestOptions}} structure configured to the [=[RP]=]'s needs for the ceremony. - Let |pkOptions| be <code>|options|.{{CredentialCreationOptions/publicKey}}</code>. + Let |pkOptions| be <code>|options|.{{CredentialRequestOptions/publicKey}}</code>. 1. Call {{CredentialsContainer/get()|navigator.credentials.get()}} and pass |options| as the argument. Let |credential| be the result of the successfully resolved promise. From 434a77fb9578bc2c5d70a31c60b6588343063e28 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Tue, 1 Oct 2024 14:54:34 +0200 Subject: [PATCH 04/26] Fix Unicode example syntax --- index.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.bs b/index.bs index 0db3e9d0a..9920c5a4c 100644 --- a/index.bs +++ b/index.bs @@ -5540,8 +5540,8 @@ The second consists of a single code point which is either U+200E (“LEFT-TO-RI So the string “حبیب الرحمان” could have two different DOMString values, depending on whether the language was encoded or not. (Since the direction is unambiguous a directionality marker is not needed in this example.) - * Unadorned string: U+FEA2, U+FE92, U+FBFF, U+FE91, U+20, U+FE8E, U+FEDF, U+FEAE, U+FEA4, U+FEE3, U+FE8E, U+FEE7 - * With language “ar-SA” encoded: U+FEA2, U+FE92, U+FBFF, U+FE91, U+20, U+FE8E, U+FEDF, U+FEAE, U+FEA4, U+FEE3, U+FE8E, U+FEE7, U+E0001, U+E0061, U+E0072, U+E002D, U+E0053, U+E0041, U+E007F + * Unadorned string: U+062D, U+0628, U+06CC, U+0628, U+0020, U+0627, U+0644, U+0631, U+062D, U+0645, U+0627, U+0646 + * With language “ar-SA” encoded: U+062D, U+0628, U+06CC, U+0628, U+0020, U+0627, U+0644, U+0631, U+062D, U+0645, U+0627, U+0646, U+E0001, U+E0061, U+E0072, U+E002D, U+E0053, U+E0041, U+E007F Consumers of strings that may have language and direction encoded should be aware that truncation could truncate a [=language tag=] into a different, but still valid, language. The final directionality marker or CANCEL TAG code point provide an unambigous indication of truncation. From bdcb938b242ff8b7a9ec74f1afdfcc54a14cc3c1 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Tue, 1 Oct 2024 16:26:16 +0200 Subject: [PATCH 05/26] Don't return an algorithm from [[DiscoverFromExternalSource]] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This initialization of |settings| and |global| is copied from the equivalent steps of [§2.5.4. Create a Credential][1] in CredMan, which sets the arguments used to invoke the |constructCredentialAlg| in WebAuthn's [[Create]]: >Let |settings| be the [current settings object][2]. > >Assert: |settings| is a [secure context][3]. > >Let |global| be |settings|’ [global object][4]. [1]: https://w3c.github.io/webappsec-credential-management/#algorithm-create [2]: https://html.spec.whatwg.org/multipage/webappapis.html#current-settings-object [3]: https://html.spec.whatwg.org/multipage/webappapis.html#secure-context [4]: https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-global --- index.bs | 57 +++++++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/index.bs b/index.bs index 0db3e9d0a..e9d51768a 100644 --- a/index.bs +++ b/index.bs @@ -2636,49 +2636,46 @@ When this method is invoked, the user agent MUST execute the following algorithm 1. If |credentialIdFilter| [=list/is empty=] and [=userHandleResult=] is null, [=continue=]. - 1. Let |constructAssertionAlg| be an algorithm that takes a [=global object=] - |global|, and whose steps are: + 1. Let |settings| be the [=current settings object=]. Let |global| be |settings|’ [=global object=]. - 1. Let |pubKeyCred| be a new {{PublicKeyCredential}} object associated with |global| whose fields are: + 1. Let |pubKeyCred| be a new {{PublicKeyCredential}} object associated with |global| whose fields are: - : {{PublicKeyCredential/[[identifier]]}} - :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of - <code>|assertionCreationData|.[=credentialIdResult=]</code>. - - : {{PublicKeyCredential/authenticatorAttachment}} - :: The {{AuthenticatorAttachment}} value matching the current [=authenticator attachment modality=] of |authenticator|. + : {{PublicKeyCredential/[[identifier]]}} + :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of + <code>|assertionCreationData|.[=credentialIdResult=]</code>. - : {{PublicKeyCredential/response}} - :: A new {{AuthenticatorAssertionResponse}} object associated with |global| whose fields are: + : {{PublicKeyCredential/authenticatorAttachment}} + :: The {{AuthenticatorAttachment}} value matching the current [=authenticator attachment modality=] of |authenticator|. - : {{AuthenticatorResponse/clientDataJSON}} - :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of - <code>|assertionCreationData|.[=assertionCreationData/clientDataJSONResult=]</code>. - - : {{AuthenticatorAssertionResponse/authenticatorData}} - :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of - <code>|assertionCreationData|.[=assertionCreationData/authenticatorDataResult=]</code>. + : {{PublicKeyCredential/response}} + :: A new {{AuthenticatorAssertionResponse}} object associated with |global| whose fields are: - : {{AuthenticatorAssertionResponse/signature}} - :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of - <code>|assertionCreationData|.[=assertionCreationData/signatureResult=]</code>. + : {{AuthenticatorResponse/clientDataJSON}} + :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of + <code>|assertionCreationData|.[=assertionCreationData/clientDataJSONResult=]</code>. - : {{AuthenticatorAssertionResponse/userHandle}} - :: If <code>|assertionCreationData|.[=assertionCreationData/userHandleResult=]</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/userHandleResult=]</code>. + : {{AuthenticatorAssertionResponse/authenticatorData}} + :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of + <code>|assertionCreationData|.[=assertionCreationData/authenticatorDataResult=]</code>. - : {{PublicKeyCredential/[[clientExtensionsResults]]}} + : {{AuthenticatorAssertionResponse/signature}} :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of - <code>|assertionCreationData|.[=assertionCreationData/clientExtensionResults=]</code>. + <code>|assertionCreationData|.[=assertionCreationData/signatureResult=]</code>. + + : {{AuthenticatorAssertionResponse/userHandle}} + :: If <code>|assertionCreationData|.[=assertionCreationData/userHandleResult=]</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/userHandleResult=]</code>. - 1. Return |pubKeyCred|. + : {{PublicKeyCredential/[[clientExtensionsResults]]}} + :: A new {{ArrayBuffer}}, created using |global|'s [=%ArrayBuffer%=], containing the bytes of + <code>|assertionCreationData|.[=assertionCreationData/clientExtensionResults=]</code>. 1. [=set/For each=] remaining |authenticator| in |issuedRequests| invoke the [=authenticatorCancel=] operation on |authenticator| and [=set/remove=] it from |issuedRequests|. - 1. Return |constructAssertionAlg| and terminate this algorithm. + 1. Return |pubKeyCred| and terminate this algorithm. </dl> 1. Throw a "{{NotAllowedError}}" {{DOMException}}. From 9e0fe6ae6f8484007d8df50fe45f6b26d88a205f Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Tue, 1 Oct 2024 20:19:18 +0200 Subject: [PATCH 06/26] =?UTF-8?q?Acknowledge=20Simone=20Onofri=20and=20Phi?= =?UTF-8?q?lippe=20Le=20H=C3=A9garet=20as=20W3C=20Team=20Contacts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.bs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.bs b/index.bs index 0db3e9d0a..7a824bdd2 100644 --- a/index.bs +++ b/index.bs @@ -9041,6 +9041,8 @@ Richard Barnes for their contributions as co-chairs of the <a href="https://www.w3.org/Webauthn/">Web Authentication Working Group</a>. We also thank +Simone Onofri, +Philippe Le Hégaret, Wendy Seltzer, Samuel Weiler, and From 3b5a8d1e253f0eff76f26ee5d20d472d1c710520 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Tue, 1 Oct 2024 22:38:44 +0200 Subject: [PATCH 07/26] Acknowledge Zack Newman for reviews and contributions --- index.bs | 1 + 1 file changed, 1 insertion(+) diff --git a/index.bs b/index.bs index 0db3e9d0a..125bcb071 100644 --- a/index.bs +++ b/index.bs @@ -9021,6 +9021,7 @@ Jing Jin, Wally Jones, Ian Kilpatrick, Axel Nennker, +Zack Newman, Yoshikazu Nojima, Kimberly Paulhamus, Adam Powers, From 37dacdae7de4a08b08b99a10843c37fb7babb418 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Tue, 1 Oct 2024 22:56:13 +0200 Subject: [PATCH 08/26] Add test vectors for PRF extension --- index.bs | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/index.bs b/index.bs index 0db3e9d0a..ac6ff10e4 100644 --- a/index.bs +++ b/index.bs @@ -7504,6 +7504,218 @@ Note: this extension may be implemented for [=authenticators=] that do not use [ :: The results of evaluating the PRF for the inputs given in {{AuthenticationExtensionsPRFInputs/eval}} or {{AuthenticationExtensionsPRFInputs/evalByCredential}}. Outputs may not be available during [=registration=]; see comments in {{AuthenticationExtensionsPRFInputs/eval}}. </div> + +#### Test vectors: Web Authentication API #### {#prf-extension-test-vectors-webauthn} + +[INFORMATIVE] + +The following examples may be used to test [=[WAC]=] implementations of and [=[WRP]=] usage of the [=prf=] extension. +The examples are not exhaustive. + +- The {{AuthenticationExtensionsPRFOutputs/enabled}} output is always present during [=registration ceremonies=], + and never present during [=authentication ceremonies=]: + + <pre class="example" highlight="js"> + // Example extension inputs: + { prf: {} } + + // Example client extension outputs from navigator.credentials.create(): + { prf: { enabled: true } } + { prf: { enabled: false } } + + // Example client extension outputs from navigator.credentials.get(): + { prf: {} } + </pre> + +- The {{AuthenticationExtensionsPRFOutputs/results}} output may be present + during [=registration ceremonies=] or [=authentication ceremonies=] + if the {{AuthenticationExtensionsPRFInputs/eval}} or {{AuthenticationExtensionsPRFInputs/evalByCredential}} input is present: + + <pre class="example" highlight="js"> + // Example extension inputs: + { prf: { eval: { first: new Uint8Array([1, 2, 3, 4]) } } } + + // Example client extension outputs from navigator.credentials.create(): + { prf: { enabled: true } } + { prf: { enabled: false } } + { prf: { enabled: true, results: { first: ArrayBuffer } } } + + // Example client extension outputs from navigator.credentials.get(): + { prf: {} } + { prf: { results: { first: ArrayBuffer } } } + </pre> + +- The <code>{{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/second}}</code> output + is present if and only if the {{AuthenticationExtensionsPRFOutputs/results}} output is present + and the <code>{{AuthenticationExtensionsPRFValues/second}}</code> input was present in the chosen PRF inputs: + + <pre class="example" highlight="js"> + // Example extension inputs: + { + prf: { + eval: { + first: new Uint8Array([1, 2, 3, 4]), + second: new Uint8Array([5, 6, 7, 8]), + }, + evalByCredential: { + "e02eZ9lPp0UdkF4vGRO4-NxlhWBkL1FCmsmb1tTfRyE": { + first: new Uint8Array([9, 10, 11, 12]), + } + } + } + } + + // Example client extension outputs from navigator.credentials.get() if credential "e02eZ9lP..." was used: + { prf: { results: { first: ArrayBuffer } } } + + // Example client extension outputs from navigator.credentials.get() if a different credential was used: + { prf: {} } + { prf: { results: { first: ArrayBuffer, second: ArrayBuffer } } } + </pre> + +- The {{AuthenticationExtensionsPRFValues/first}} and {{AuthenticationExtensionsPRFValues/second}} outputs + may be any {{BufferSource}} type. + Equal {{AuthenticationExtensionsPRFValues/first}} and {{AuthenticationExtensionsPRFValues/second}} inputs + result in equal {{AuthenticationExtensionsPRFValues/first}} and {{AuthenticationExtensionsPRFValues/second}} outputs: + + <pre class="example" highlight="js"> + // Example extension inputs: + { + prf: { + evalByCredential: { + "e02eZ9lPp0UdkF4vGRO4-NxlhWBkL1FCmsmb1tTfRyE": { + first: new Uint8Array([9, 10, 11, 12]), + second: new Uint8Array([9, 10, 11, 12]) + } + } + } + } + + // Example client extension outputs from navigator.credentials.get(): + { + prf: { + results: { + first: new Uint8Array([0xc4, 0x17, 0x2e, 0x98, 0x2e, 0x90, 0x97, 0xc3, 0x9a, 0x6c, 0x0c, 0xb7, 0x20, 0xcb, 0x37, 0x5b, 0x92, 0xe3, 0xfc, 0xad, 0x15, 0x4a, 0x63, 0xe4, 0x3a, 0x93, 0xf1, 0x09, 0x6b, 0x1e, 0x19, 0x73]), + second: new Uint32Array([0x98172ec4, 0xc390972e, 0xb76c0c9a, 0x5bcb3720, 0xade3fc92, 0xe44a6315, 0x0993f13a, 0x731e196b]), + } + } + } + </pre> + +Pseudo-random values used in this section were generated as follows: + +- <code>"e02eZ9lPp0UdkF4vGRO4-NxlhWBkL1FCmsmb1tTfRyE" = Base64Url(SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x00))</code> +- <code>h'c4172e982e9097c39a6c0cb720cb375b92e3fcad154a63e43a93f1096b1e1973' = SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x01)</code> + + +#### Test vectors: CTAP2 `hmac-secret` extension #### {#prf-extension-test-vectors-ctap} + +[INFORMATIVE] + +The following examples may be used to test [=[WAC]=] implementations +of how the [=prf=] extension uses the [[FIDO-CTAP]] `hmac-secret` extension. +The examples are given in CDDL [[RFC8610]] notation. +The examples are not exhaustive. + +- The following shared definitions are used in all subsequent examples: + + <pre class="example"> + ; Given input parameters: + platform_key_agreement_private_key = 0x0971bc7fb1be48270adcd3d9a5fc15d5fb0f335b3071ff36a54c007fa6c76514 + authenticator_key_agreement_public_key = { + 1: 2, + 3: -25, + -1: 1, + -2: h'a30522c2de402b561965c3cf949a1cab020c6f6ea36fcf7e911ac1a0f1515300', + -3: h'9961a929abdb2f42e6566771887d41484d889e735e3248518a53112d2b915f00', + } + authenticator_cred_random = h'437e065e723a98b2f08f39d8baf7c53ecb3c363c5e5104bdaaf5d5ca2e028154' + </pre> + + The {{AuthenticationExtensionsPRFValues/first}} and {{AuthenticationExtensionsPRFValues/second}} inputs + are mapped in the examples as `prf_eval_first` and `prf_eval_second`, respectively. + The `prf_results_first` and `prf_results_second` values in the examples + are mapped to the + <code>{{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/first}}</code> + and <code>{{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/second}}</code> + outputs, respectively. + +- Single input case using PIN protocol 2: + + <pre class="example"> + ; Inputs from Relying Party: + prf_eval_first = h'576562417574686e20505246207465737420766563746f727302' + + ; Client computes: + shared_secret = h'0c63083de8170101d38bcf8bd72309568ddb4550867e23404b35d85712f7c20d8bc911ee23c06034cbc14290b9669bec07739053c5a416e313ef905c79955876' + salt1 = h'527413ebb48293772df30f031c5ac4650c7de14bf9498671ae163447b6a772b3' + salt_enc = h'437e065e723a98b2f08f39d8baf7c53ebbb2ed3e746b87576fd81f95def5757cad24be18eaef892e97692e684e07da53' + + ; Authenticator computes: + output1 = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' + output_enc = h'3bfaa48f7952330d63e35ff8cd5bca48d2a12823828915749287256ab146272f9fb437bf65691243c3f504bd7ea6d5e6' + + ; Client decrypts: + prf_results_first = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' + </pre> + +- Two input case using PIN protocol 2: + + <pre class="example"> + ; Inputs from Relying Party: + prf_eval_first = h'576562417574686e20505246207465737420766563746f727302' + prf_eval_second = h'576562417574686e20505246207465737420766563746f727303' + + ; Client computes: + shared_secret = h'0c63083de8170101d38bcf8bd72309568ddb4550867e23404b35d85712f7c20d8bc911ee23c06034cbc14290b9669bec07739053c5a416e313ef905c79955876' + salt1 = h'527413ebb48293772df30f031c5ac4650c7de14bf9498671ae163447b6a772b3' + salt2 = h'd68ac03329a10ee5e0ec834492bb9a96a0e547baf563bf78ccbe8789b22e776b' + salt_enc = h'23dde5e3462daf36559b85c4ac5f9656aa9bfd81c1dc2bf8533c8b9f3882854786b4f500e25b4e3d81f7fc7c742362294d92926c883b3fae1a3673246464bf730446e1fa4698c432a9092477c5dde5e3' + + ; Authenticator computes: + output1 = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' + output2 = h'a62a8773b19cda90d7ed4ef72a80a804320dbd3997e2f663805ad1fd3293d50b' + output_enc = h'27cc2400050f7ef4d0d37a916518dd33acd741e3a70bb5a91921c8a80ac73d656536843f593b86771246daabf32e1527616955b4981f5e6d30c13329bf460bacd6b4049b8bbea823438078f6224c58a8' + + ; Client decrypts: + prf_results_first = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' + prf_results_second = h'a62a8773b19cda90d7ed4ef72a80a804320dbd3997e2f663805ad1fd3293d50b' + </pre> + +- Single input case using PIN protocol 1: + + <pre class="example"> + ; Inputs from Relying Party: + prf_eval_first = h'576562417574686e20505246207465737420766563746f727302' + + ; Client computes: + shared_secret = h'23e5ed7157c25892b77732fb9c8a107e3518800db2af4142f9f4adfacb771d39' + salt1 = h'527413ebb48293772df30f031c5ac4650c7de14bf9498671ae163447b6a772b3' + salt_enc = h'ab8c878bb05d04700f077ed91845ec9c503c925cb12b327ddbeb4243c397f913' + + ; Authenticator computes: + output1 = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' + output_enc = h'15d4e4f3f04109b492b575c1b38c28585b6719cf8d61304215108d939f37ccfb' + + ; Client decrypts: + prf_results_first = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' + </pre> + +Inputs and pseudo-random values used in this section were generated as follows: + +- <code>seed = UTF-8("WebAuthn PRF test vectors")</code> +- <code>prf_eval_first = seed || 0x02</code> +- <code>prf_eval_second = seed || 0x03</code> +- <code>platform_key_agreement_private_key = SHA-256(seed || 0x04)</code> +- <code>authenticator_key_agreement_public_key = P256-Public-Key(sk)</code> + where <code>sk = SHA-256(seed || 0x05)</code> +- <code>authenticator_cred_random = SHA-256(seed || 0x06)</code> +- `iv` in single-input `salt_enc` with PIN protocol 2: Truncated <code>SHA-256(seed || 0x07)</code> +- `iv` in two-input `salt_enc` with PIN protocol 2: Truncated <code>SHA-256(seed || 0x08)</code> +- `iv` in single-input `output_enc` with PIN protocol 2: Truncated <code>SHA-256(seed || 0x09)</code> +- `iv` in two-input `output_enc` with PIN protocol 2: Truncated <code>SHA-256(seed || 0x10)</code> + + ### Large blob storage extension (<dfn>largeBlob</dfn>) ### {#sctn-large-blob-extension} This [=client extension|client=] [=registration extension=] and [=authentication extension=] allows a [=[RP]=] to store opaque data associated with a credential. Since [=authenticators=] can only store small amounts of data, and most [=[RPS]=] are online services that can store arbitrary amounts of state for a user, this is only useful in specific cases. For example, the [=[RP]=] might wish to issue certificates rather than run a centralised authentication service. From fe68eaec8f4402d98c329deb737a8766d90035b9 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Wed, 2 Oct 2024 21:09:48 +0200 Subject: [PATCH 09/26] Apply review suggestion Co-authored-by: Adam Langley <agl@google.com> --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 1c747ae51..86c3adbc3 100644 --- a/index.bs +++ b/index.bs @@ -3598,7 +3598,7 @@ associated with or [=scoped=] to, respectively. This member is deprecated because many [=clients=] do not display it, but it remains a required dictionary member for backwards compatibility. - [=[RPS]=] MAY as a safe default set this equal to the [=RP ID=]. + [=[RPS]=] MAY, as a safe default, set this equal to the [=RP ID=]. - [=[RPS]=] SHOULD perform enforcement, as prescribed in Section 2.3 of [[!RFC8266]] for the Nickname Profile of the PRECIS FreeformClass [[!RFC8264]], From 96ed2bd5dbbc6aa66e9ea7b9adf3a8c66ec260e0 Mon Sep 17 00:00:00 2001 From: Nina Satragno <nsatragno@gmail.com> Date: Wed, 18 Sep 2024 12:16:36 -0400 Subject: [PATCH 10/26] Add userName and userDisplayName to webdriver This PR adds the userName and userDisplayName properties to the webdriver's credential parameters. These properties are useful to test the new signalCurrentUserDetails method, both on WPTs and for web developers. Closes #2143 --- index.bs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/index.bs b/index.bs index 0db3e9d0a..89ea812ba 100644 --- a/index.bs +++ b/index.bs @@ -8013,6 +8013,22 @@ The <dfn>Credential Parameters</dfn> is a JSON [=Object=] passed to the [=remote </td> <td>boolean</td> </tr> + <tr> + <td>|userName|</td> + <td> + The {{PublicKeyCredentialUserEntity|user}}'s {{PublicKeyCredentialEntity/name}} associated to the credential. + If unset, the value will default to the empty string. + </td> + <td>string</td> + </tr> + <tr> + <td>|userDisplayName|</td> + <td> + The {{PublicKeyCredentialUserEntity|user}}'s {{PublicKeyCredentialUserEntity/displayName}} associated to the credential. + If unset, the value will default to the empty string. + </td> + <td>string</td> + </tr> </tbody> </table> </figure> @@ -8053,6 +8069,10 @@ The [=remote end steps=] are: 1. If |backupEligibility| is not defined, set |backupEligibility| to the value of the |authenticator|'s |defaultBackupEligibility|. 1. Let |backupState| be the |parameters|' |backupState| property. 1. If |backupState| is not defined, set |backupState| to the value of the |authenticator|'s |defaultBackupState|. + 1. Let |userName| be the |parameters|' |userName| property. + 1. If |userName| is not defined, set |userName| to the empty string. + 1. Let |userDisplayName| be the |parameters|' |userDisplayName| property. + 1. If |userDisplayName| is not defined, set |userDisplayName| to the empty string. 1. Let |credential| be a new [=Client-side discoverable Public Key Credential Source=] if |isResidentCredential| is [TRUE] or a [=Server-side Public Key Credential Source=] otherwise whose items are: : [=public key credential source/type=] @@ -8065,6 +8085,8 @@ The [=remote end steps=] are: :: |rpId| : [=public key credential source/userHandle=] :: |userHandle| + : [=public key credential source/otherUI=] + :: Construct from |userName| and |userDisplayName|. 1. Set the |credential|'s [=backup eligibility=] [=credential property=] to |backupEligibility|. 1. Set the |credential|'s [=backup state=] [=credential property=] to |backupState|. 1. Associate a [=signature counter=] |counter| to the |credential| with a starting value equal to the |parameters|' From ebfe871f78f5d5be6cefecabf5a723b119dacae2 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Wed, 2 Oct 2024 22:41:04 +0200 Subject: [PATCH 11/26] Use <xmp> instead of <pre>, fixing CDDL highlighting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using <pre> causes some single quotes in the CDDL examples to be converted into "’" (U+2019) instead of "'" (U+0027), which is incorrect CDDL and also breaks the CDDL syntax highlighting. See the [Bikeshed documentation][1] for more on using `<xmp>`. [1]: https://speced.github.io/bikeshed/#xmp --- index.bs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/index.bs b/index.bs index ac6ff10e4..b53633d0f 100644 --- a/index.bs +++ b/index.bs @@ -7515,7 +7515,7 @@ The examples are not exhaustive. - The {{AuthenticationExtensionsPRFOutputs/enabled}} output is always present during [=registration ceremonies=], and never present during [=authentication ceremonies=]: - <pre class="example" highlight="js"> + <xmp class="example" highlight="js"> // Example extension inputs: { prf: {} } @@ -7525,13 +7525,13 @@ The examples are not exhaustive. // Example client extension outputs from navigator.credentials.get(): { prf: {} } - </pre> + </xmp> - The {{AuthenticationExtensionsPRFOutputs/results}} output may be present during [=registration ceremonies=] or [=authentication ceremonies=] if the {{AuthenticationExtensionsPRFInputs/eval}} or {{AuthenticationExtensionsPRFInputs/evalByCredential}} input is present: - <pre class="example" highlight="js"> + <xmp class="example" highlight="js"> // Example extension inputs: { prf: { eval: { first: new Uint8Array([1, 2, 3, 4]) } } } @@ -7543,13 +7543,13 @@ The examples are not exhaustive. // Example client extension outputs from navigator.credentials.get(): { prf: {} } { prf: { results: { first: ArrayBuffer } } } - </pre> + </xmp> - The <code>{{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/second}}</code> output is present if and only if the {{AuthenticationExtensionsPRFOutputs/results}} output is present and the <code>{{AuthenticationExtensionsPRFValues/second}}</code> input was present in the chosen PRF inputs: - <pre class="example" highlight="js"> + <xmp class="example" highlight="js"> // Example extension inputs: { prf: { @@ -7571,14 +7571,14 @@ The examples are not exhaustive. // Example client extension outputs from navigator.credentials.get() if a different credential was used: { prf: {} } { prf: { results: { first: ArrayBuffer, second: ArrayBuffer } } } - </pre> + </xmp> - The {{AuthenticationExtensionsPRFValues/first}} and {{AuthenticationExtensionsPRFValues/second}} outputs may be any {{BufferSource}} type. Equal {{AuthenticationExtensionsPRFValues/first}} and {{AuthenticationExtensionsPRFValues/second}} inputs result in equal {{AuthenticationExtensionsPRFValues/first}} and {{AuthenticationExtensionsPRFValues/second}} outputs: - <pre class="example" highlight="js"> + <xmp class="example" highlight="js"> // Example extension inputs: { prf: { @@ -7600,7 +7600,7 @@ The examples are not exhaustive. } } } - </pre> + </xmp> Pseudo-random values used in this section were generated as follows: @@ -7619,7 +7619,7 @@ The examples are not exhaustive. - The following shared definitions are used in all subsequent examples: - <pre class="example"> + <xmp class="example" highlight="cddl"> ; Given input parameters: platform_key_agreement_private_key = 0x0971bc7fb1be48270adcd3d9a5fc15d5fb0f335b3071ff36a54c007fa6c76514 authenticator_key_agreement_public_key = { @@ -7630,7 +7630,7 @@ The examples are not exhaustive. -3: h'9961a929abdb2f42e6566771887d41484d889e735e3248518a53112d2b915f00', } authenticator_cred_random = h'437e065e723a98b2f08f39d8baf7c53ecb3c363c5e5104bdaaf5d5ca2e028154' - </pre> + </xmp> The {{AuthenticationExtensionsPRFValues/first}} and {{AuthenticationExtensionsPRFValues/second}} inputs are mapped in the examples as `prf_eval_first` and `prf_eval_second`, respectively. @@ -7642,7 +7642,7 @@ The examples are not exhaustive. - Single input case using PIN protocol 2: - <pre class="example"> + <xmp class="example" highlight="cddl"> ; Inputs from Relying Party: prf_eval_first = h'576562417574686e20505246207465737420766563746f727302' @@ -7657,11 +7657,11 @@ The examples are not exhaustive. ; Client decrypts: prf_results_first = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' - </pre> + </xmp> - Two input case using PIN protocol 2: - <pre class="example"> + <xmp class="example" highlight="cddl"> ; Inputs from Relying Party: prf_eval_first = h'576562417574686e20505246207465737420766563746f727302' prf_eval_second = h'576562417574686e20505246207465737420766563746f727303' @@ -7680,11 +7680,11 @@ The examples are not exhaustive. ; Client decrypts: prf_results_first = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' prf_results_second = h'a62a8773b19cda90d7ed4ef72a80a804320dbd3997e2f663805ad1fd3293d50b' - </pre> + </xmp> - Single input case using PIN protocol 1: - <pre class="example"> + <xmp class="example" highlight="cddl"> ; Inputs from Relying Party: prf_eval_first = h'576562417574686e20505246207465737420766563746f727302' @@ -7699,7 +7699,7 @@ The examples are not exhaustive. ; Client decrypts: prf_results_first = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' - </pre> + </xmp> Inputs and pseudo-random values used in this section were generated as follows: From 41c514f6e1db4b4aaba77c3145990f0f99c5bd7b Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Wed, 2 Oct 2024 22:35:41 +0200 Subject: [PATCH 12/26] Consistently use <xmp> instead of <pre> for code examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using `<pre>` sometimes causes some characters to be converted into others. This is especially apparent in CDDL examples, where for example the first single quote in `foo = h'001122'` gets converted into "’" (U+2019) instead of "'" (U+0027), which is incorrect and also breaks CDDL syntax highlighting. See the [Bikeshed documentation][1] for more on using `<xmp>`. [1]: https://speced.github.io/bikeshed/#xmp --- index.bs | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/index.bs b/index.bs index f537878e3..260976e74 100644 --- a/index.bs +++ b/index.bs @@ -536,7 +536,7 @@ In this flow, the [=[WRP]=] does not have a preference for [=platform authentica The sample code for generating and registering a new key follows: -<pre class="example" highlight="js"> +<xmp class="example" highlight="js"> if (!window.PublicKeyCredential) { /* Client not capable. Handle error. */ } var publicKey = { @@ -591,7 +591,7 @@ The sample code for generating and registering a new key follows: }).catch(function (err) { // No acceptable authenticator or user refused consent. Handle appropriately. }); -</pre> +</xmp> ### Registration Specifically with User-Verifying Platform Authenticator ### {#sctn-sample-registration-with-platform-authenticator} @@ -613,7 +613,7 @@ a [=user-verifying platform authenticator=]. 1. Upon successful credential creation, the [=[RP]=] script conveys the new credential to the server. -<pre class="example" highlight="js"> +<xmp class="example" highlight="js"> if (!window.PublicKeyCredential) { /* Client not capable of the API. Handle error. */ } PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() @@ -637,7 +637,7 @@ a [=user-verifying platform authenticator=]. }).catch(function (err) { // Something went wrong. Handle appropriately. }); -</pre> +</xmp> ### Authentication ### {#sctn-sample-authentication} @@ -679,7 +679,7 @@ credential. If the [=[RP]=] script does not have any hints available (e.g., from locally stored data) to help it narrow the list of credentials, then the sample code for performing such an authentication might look like this: -<pre class="example" highlight="js"> +<xmp class="example" highlight="js"> if (!window.PublicKeyCredential) { /* Client not capable. Handle error. */ } // credentialId is generated by the authenticator and is an opaque random byte array @@ -697,12 +697,12 @@ credentials, then the sample code for performing such an authentication might lo }).catch(function (err) { // No acceptable credential or user refused consent. Handle appropriately. }); -</pre> +</xmp> On the other hand, if the [=[RP]=] script has some hints to help it narrow the list of credentials, then the sample code for performing such an authentication might look like the following. Note that this sample also demonstrates how to use the [=credProps|Credential Properties Extension=]. -<pre class="example" highlight="js"> +<xmp class="example" highlight="js"> if (!window.PublicKeyCredential) { /* Client not capable. Handle error. */ } var encoder = new TextEncoder(); @@ -729,14 +729,14 @@ performing such an authentication might look like the following. Note that this }).catch(function (err) { // No acceptable credential or user refused consent. Handle appropriately. }); -</pre> +</xmp> ### Aborting Authentication Operations ### {#sctn-sample-aborting} The below example shows how a developer may use the AbortSignal parameter to abort a credential registration operation. A similar procedure applies to an authentication operation. -<pre class="example" highlight="js"> +<xmp class="example" highlight="js"> const authAbortController = new AbortController(); const authAbortSignal = authAbortController.signal; @@ -764,7 +764,7 @@ credential registration operation. A similar procedure applies to an authenticat if (widget == "disappear") { authAbortController.abort(); } -</pre> +</xmp> ### Decommissioning ### {#sctn-sample-decommissioning} @@ -5669,7 +5669,7 @@ Below is an example of a COSE_Key-encoded Elliptic Curve public key in EC2 forma on the P-256 curve, to be used with the ES256 signature algorithm (ECDSA w/ SHA-256, see [=Section 2.1=] of [[!RFC9053]]): -<pre class="example" highlight="json"> +<xmp class="example" highlight="json"> { 1: 2, ; kty: EC2 key type 3: -7, ; alg: ES256 signature algorithm @@ -5679,12 +5679,12 @@ algorithm (ECDSA w/ SHA-256, see [=Section 2.1=] of [[!RFC9053]]): -3: y ; y-coordinate as byte string 32 bytes in length ; e.g., in hex: 1e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c } -</pre> +</xmp> Below is the above Elliptic Curve public key encoded in the [=CTAP2 canonical CBOR encoding form=], whitespace and line breaks are included here for clarity and to match the CDDL [[!RFC8610]] presentation above: -<pre class="example" highlight="json"> +<xmp class="example" highlight="json"> A5 01 02 @@ -5695,14 +5695,14 @@ are included here for clarity and to match the CDDL [[!RFC8610]] presentation ab 21 58 20 65eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c08551d 22 58 20 1e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd0084d19c -</pre> +</xmp> Below is an example of a COSE_Key-encoded 2048-bit RSA public key (see [[RFC8230]] [=Section 4=], to be used with the PS256 signature algorithm (RSASSA-PSS with SHA-256, see [=RFC8230/Section 2=] of [[RFC8230]]: -<pre class="example" highlight="json"> +<xmp class="example" highlight="json"> { 1: 3, ; kty: RSA key type 3: -37, ; alg: PS256 @@ -5711,12 +5711,12 @@ to be used with the PS256 signature algorithm -2: e ; e: RSA public exponent e byte string 3 bytes in length ; e.g., in hex: 010001 } -</pre> +</xmp> Below is an example of the same COSE_Key-encoded RSA public key as above, to be used with the RS256 signature algorithm (RSASSA-PKCS1-v1_5 with SHA-256): -<pre class="example" highlight="json"> +<xmp class="example" highlight="json"> { 1: 3, ; kty: RSA key type 3:-257, ; alg: RS256 @@ -5725,7 +5725,7 @@ to be used with the RS256 signature algorithm (RSASSA-PKCS1-v1_5 with SHA-256): -2: e ; e: RSA public exponent e byte string 3 bytes in length ; e.g., in hex: 010001 } -</pre> +</xmp> ### Attestation Statement Formats ### {#sctn-attestation-formats} @@ -7040,7 +7040,7 @@ Note: Other documents have specified extensions where the extension input does n as the entry key. New extensions SHOULD follow the above convention. -<pre class="example" highlight="js"> +<xmp class="example" highlight="js"> var assertionPromise = navigator.credentials.get({ publicKey: { // Other members omitted for brevity @@ -7054,7 +7054,7 @@ New extensions SHOULD follow the above convention. } } }); -</pre> +</xmp> Extension definitions MUST specify the valid values for their [=client extension input=]. Clients SHOULD ignore extensions with an invalid [=client extension input=]. If an extension does not require any parameters from the [=[RP]=], it SHOULD be defined @@ -7077,14 +7077,14 @@ The following example defines that an extension with [=extension identifier|iden takes an unsigned integer as [=authenticator extension input=], and returns an array of at least one byte string as [=authenticator extension output=]: -<pre class="example"> +<xmp class="example"> $$extensionInput //= ( webauthnExample_foobar: uint ) $$extensionOutput //= ( webauthnExample_foobar: [+ bytes] ) -</pre> +</xmp> Note: Extensions should aim to define authenticator arguments that are as small as possible. Some authenticators communicate over low-bandwidth links such as Bluetooth Low-Energy or NFC. From ef54852bf773d82174798bfff3e87469380db325 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Wed, 2 Oct 2024 22:39:49 +0200 Subject: [PATCH 13/26] Fix syntax highlighting tags --- index.bs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.bs b/index.bs index 260976e74..ea23ad1f2 100644 --- a/index.bs +++ b/index.bs @@ -5669,7 +5669,7 @@ Below is an example of a COSE_Key-encoded Elliptic Curve public key in EC2 forma on the P-256 curve, to be used with the ES256 signature algorithm (ECDSA w/ SHA-256, see [=Section 2.1=] of [[!RFC9053]]): -<xmp class="example" highlight="json"> +<xmp class="example" highlight="cddl"> { 1: 2, ; kty: EC2 key type 3: -7, ; alg: ES256 signature algorithm @@ -5684,7 +5684,7 @@ algorithm (ECDSA w/ SHA-256, see [=Section 2.1=] of [[!RFC9053]]): Below is the above Elliptic Curve public key encoded in the [=CTAP2 canonical CBOR encoding form=], whitespace and line breaks are included here for clarity and to match the CDDL [[!RFC8610]] presentation above: -<xmp class="example" highlight="json"> +<xmp class="example"> A5 01 02 @@ -5702,7 +5702,7 @@ Below is an example of a COSE_Key-encoded 2048-bit RSA public key (see [[RFC8230 to be used with the PS256 signature algorithm (RSASSA-PSS with SHA-256, see [=RFC8230/Section 2=] of [[RFC8230]]: -<xmp class="example" highlight="json"> +<xmp class="example" highlight="cddl"> { 1: 3, ; kty: RSA key type 3: -37, ; alg: PS256 @@ -5716,7 +5716,7 @@ to be used with the PS256 signature algorithm Below is an example of the same COSE_Key-encoded RSA public key as above, to be used with the RS256 signature algorithm (RSASSA-PKCS1-v1_5 with SHA-256): -<xmp class="example" highlight="json"> +<xmp class="example" highlight="cddl"> { 1: 3, ; kty: RSA key type 3:-257, ; alg: RS256 @@ -7077,7 +7077,7 @@ The following example defines that an extension with [=extension identifier|iden takes an unsigned integer as [=authenticator extension input=], and returns an array of at least one byte string as [=authenticator extension output=]: -<xmp class="example"> +<xmp class="example" highlight="cddl"> $$extensionInput //= ( webauthnExample_foobar: uint ) From c2395cbbb6351654ef1a2d40ceba252ca73d4683 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Wed, 2 Oct 2024 23:13:17 +0200 Subject: [PATCH 14/26] Use 0x0a instead of 0x10 as 11th test vector PRNG index --- index.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.bs b/index.bs index b53633d0f..a0597be27 100644 --- a/index.bs +++ b/index.bs @@ -7675,7 +7675,7 @@ The examples are not exhaustive. ; Authenticator computes: output1 = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' output2 = h'a62a8773b19cda90d7ed4ef72a80a804320dbd3997e2f663805ad1fd3293d50b' - output_enc = h'27cc2400050f7ef4d0d37a916518dd33acd741e3a70bb5a91921c8a80ac73d656536843f593b86771246daabf32e1527616955b4981f5e6d30c13329bf460bacd6b4049b8bbea823438078f6224c58a8' + output_enc = h'90ee52f739043bc17b3488a74306d7801debb5b61f18662c648a25b5b5678ede482cdaff99a537a44f064fcb10ce6e04dfd27619dc96a0daff8507e499296b1eecf0981f7c8518b277a7a3018f5ec6fb' ; Client decrypts: prf_results_first = h'3c33e07d202c3b029cc21f1722767021bf27d595933b3d2b6a1b9d5dddc77fae' @@ -7713,7 +7713,7 @@ Inputs and pseudo-random values used in this section were generated as follows: - `iv` in single-input `salt_enc` with PIN protocol 2: Truncated <code>SHA-256(seed || 0x07)</code> - `iv` in two-input `salt_enc` with PIN protocol 2: Truncated <code>SHA-256(seed || 0x08)</code> - `iv` in single-input `output_enc` with PIN protocol 2: Truncated <code>SHA-256(seed || 0x09)</code> -- `iv` in two-input `output_enc` with PIN protocol 2: Truncated <code>SHA-256(seed || 0x10)</code> +- `iv` in two-input `output_enc` with PIN protocol 2: Truncated <code>SHA-256(seed || 0x0a)</code> ### Large blob storage extension (<dfn>largeBlob</dfn>) ### {#sctn-large-blob-extension} From b953fed1340f3c97096444960c6107c07cebb442 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Wed, 2 Oct 2024 23:21:40 +0200 Subject: [PATCH 15/26] Fix order of middle bytes in Uint32Array example --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index a0597be27..49d95ad51 100644 --- a/index.bs +++ b/index.bs @@ -7596,7 +7596,7 @@ The examples are not exhaustive. prf: { results: { first: new Uint8Array([0xc4, 0x17, 0x2e, 0x98, 0x2e, 0x90, 0x97, 0xc3, 0x9a, 0x6c, 0x0c, 0xb7, 0x20, 0xcb, 0x37, 0x5b, 0x92, 0xe3, 0xfc, 0xad, 0x15, 0x4a, 0x63, 0xe4, 0x3a, 0x93, 0xf1, 0x09, 0x6b, 0x1e, 0x19, 0x73]), - second: new Uint32Array([0x98172ec4, 0xc390972e, 0xb76c0c9a, 0x5bcb3720, 0xade3fc92, 0xe44a6315, 0x0993f13a, 0x731e196b]), + second: new Uint32Array([0x982e17c4, 0xc397902e, 0xb70c6c9a, 0x5b37cb20, 0xadfce392, 0xe4634a15, 0x09f1933a, 0x73191e6b]), } } } From a23151753ccc9857a00831250b196828c84907e3 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Wed, 2 Oct 2024 23:32:24 +0200 Subject: [PATCH 16/26] Escape single quote in CDDL-style byte string literal in <code> --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 49d95ad51..ff1bb6a1a 100644 --- a/index.bs +++ b/index.bs @@ -7605,7 +7605,7 @@ The examples are not exhaustive. Pseudo-random values used in this section were generated as follows: - <code>"e02eZ9lPp0UdkF4vGRO4-NxlhWBkL1FCmsmb1tTfRyE" = Base64Url(SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x00))</code> -- <code>h'c4172e982e9097c39a6c0cb720cb375b92e3fcad154a63e43a93f1096b1e1973' = SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x01)</code> +- <code>h'c4172e982e9097c39a6c0cb720cb375b92e3fcad154a63e43a93f1096b1e1973' = SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x01)</code> #### Test vectors: CTAP2 `hmac-secret` extension #### {#prf-extension-test-vectors-ctap} From 85717cce29586ec9fec3bf75bbb00da8ee39e4c6 Mon Sep 17 00:00:00 2001 From: Shane Weeden <sweeden@au1.ibm.com> Date: Thu, 3 Oct 2024 10:55:01 +1000 Subject: [PATCH 17/26] Address #2172 --- index.bs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/index.bs b/index.bs index f537878e3..b1d55a2fa 100644 --- a/index.bs +++ b/index.bs @@ -4811,9 +4811,9 @@ leave the <code>[=authData/signCount=]</code> in the [=authenticator data=] cons A [=[RP]=] stores the [=signature counter=] of the most recent [=authenticatorGetAssertion=] operation. (Or the counter from the [=authenticatorMakeCredential=] operation if no [=authenticatorGetAssertion=] has ever been performed on a credential.) In subsequent [=authenticatorGetAssertion=] operations, the [=[RP]=] compares the stored [=signature counter=] value with the new -<code>[=authData/signCount=]</code> value returned in the assertion's [=authenticator data=]. If either is non-zero, and the new <code>[=authData/signCount=]</code> value is less than or equal to the stored value, a cloned authenticator may exist, or the authenticator may be malfunctioning. +<code>[=authData/signCount=]</code> value returned in the assertion's [=authenticator data=]. If either is non-zero, and the new <code>[=authData/signCount=]</code> value is less than or equal to the stored value, a cloned authenticator may exist, or the authenticator may be malfunctioning, or a race condition might exist where the relying party is receiving and processing assertions in an order other than the order they were generated at the authenticator. -Detecting a [=signature counter=] mismatch does not indicate whether the current operation was performed by a cloned authenticator or the original authenticator. [=[RPS]=] should address this situation appropriately relative to their individual situations, i.e., their risk tolerance. +Detecting a [=signature counter=] mismatch does not indicate whether the current operation was performed by a cloned authenticator or the original authenticator. [=[RPS]=] should address this situation appropriately relative to their individual situations, i.e., their risk tolerance or operational factors that might result in an acceptable reason for non-increasing values. Authenticators: - SHOULD implement per credential [=signature counters=]. This prevents the @@ -6240,15 +6240,17 @@ a numbered step. If outdented, it (today) is rendered as a bullet in the midst o <dt>greater than <code>|credentialRecord|.[$credential record/signCount$]</code>:</dt> <dd>The signature counter is valid.</dd> <dt>less than or equal to <code>|credentialRecord|.[$credential record/signCount$]</code>:</dt> - <dd>This is a signal that - the authenticator may be cloned, i.e. at least - two copies of the [=credential private key=] may exist and are - being used in parallel. [=[RPS]=] should incorporate this information - into their risk scoring. + <dd>This is a signal, but not proof, that the authenticator may be cloned. For example it might mean that: + - Two or more copies of the [=credential private key=] may exist and are being used in parallel. + - An authenticator is malfunctioning. + - A race condition exists where the [=[RP]=] is processing assertion responses in an order other than the order they were generated at the authenticator. + + [=[RPS]=] should evaluate their own operational characteristics and incorporate this information into their risk scoring. Whether the [=[RP]=] updates <code>|credentialRecord|.[$credential record/signCount$]</code> - below in this case, or not, or fails the - [=authentication ceremony=] or not, is - [=[RP]=]-specific. </dd> + below in this case, or not, or fails the [=authentication ceremony=] or not, is [=[RP]=]-specific. + + For more information on signature counter considerations, see [[#sctn-sign-counter]]. + </dd> </dl> <li id='authn-ceremony-update-credential-record'> From 70fb37a378e50943f1652195d7420452f061807c Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Mon, 7 Oct 2024 13:27:32 +0200 Subject: [PATCH 18/26] Move <dfn> of [[Create]] to heading like [[DiscoverFromExternalSource]] --- index.bs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/index.bs b/index.bs index 47da4a1d9..ebc143a37 100644 --- a/index.bs +++ b/index.bs @@ -1733,11 +1733,10 @@ To support obtaining assertions via {{CredentialsContainer/get()|navigator.crede </xmp> -### Create a New Credential - PublicKeyCredential's `[[Create]](origin, options, sameOriginWithAncestors)` Method ### {#sctn-createCredential} +### Create a New Credential - PublicKeyCredential's <dfn for="PublicKeyCredential" method>\[[Create]](origin, options, sameOriginWithAncestors)</dfn> Method ### {#sctn-createCredential} <div link-for-hint="PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)"> -{{PublicKeyCredential}}'s [=interface object=]'s implementation of the <dfn for="PublicKeyCredential" method>\[[Create]](origin, -options, sameOriginWithAncestors)</dfn> [=internal method=] [[!CREDENTIAL-MANAGEMENT-1]] allows +{{PublicKeyCredential}}'s [=interface object=]'s implementation of the {{Credential/[[Create]](origin, options, sameOriginWithAncestors)}} [=internal method=] [[!CREDENTIAL-MANAGEMENT-1]] allows [=[WRP]=] scripts to call {{CredentialsContainer/create()|navigator.credentials.create()}} to request the creation of a new [=public key credential source=], [=bound credential|bound=] to an [=authenticator=]. From 6744192e08e90fd90f2636e0ad076d19daf8e132 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Mon, 7 Oct 2024 13:29:14 +0200 Subject: [PATCH 19/26] Extract macros for referring to [[Create]] and [[DiscoverFromExternalSource]] This also fixes some inconsistencies in parameter lists between references to these methods. --- index.bs | 79 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/index.bs b/index.bs index ebc143a37..a2b8e1785 100644 --- a/index.bs +++ b/index.bs @@ -58,6 +58,12 @@ Text Macro: WAA WebAuthn Authenticator Text Macro: WAC WebAuthn Client Text Macro: WRP WebAuthn Relying Party Text Macro: WRPS WebAuthn Relying Parties +Text Macro: CREATE-METHOD-ARGS (origin, options, sameOriginWithAncestors) +Text Macro: CREATE-METHOD [[Create]][CREATE-METHOD-ARGS] +Text Macro: CREATE-METHOD-DEF \[[Create]][CREATE-METHOD-ARGS] +Text Macro: DISCOVER-METHOD-ARGS (origin, options, sameOriginWithAncestors) +Text Macro: DISCOVER-METHOD [[DiscoverFromExternalSource]][DISCOVER-METHOD-ARGS] +Text Macro: DISCOVER-METHOD-DEF \[[DiscoverFromExternalSource]][DISCOVER-METHOD-ARGS] Ignored Vars: op, alg, type, algorithm Abstract: This specification defines an API enabling the creation and use of strong, attested, [=scoped=], public key-based credentials by [=web applications=], for the purpose of strongly authenticating users. Conceptually, one or more [=public key @@ -1009,7 +1015,7 @@ BCP 14 [[!RFC2119]] [[!RFC8174]] when, and only when, they appear in all capital : <dfn>Client</dfn> : <dfn>[WAC]</dfn> -:: Also referred to herein as simply a [=client=]. See also [=Conforming User Agent=]. A [=[WAC]=] is an intermediary entity typically implemented in the user agent (in whole, or in part). Conceptually, it underlies the [=Web Authentication API=] and embodies the implementation of the {{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}} and {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} [=internal methods=]. It is responsible for both marshalling the inputs for the underlying [=authenticator operations=], and for returning the results of the latter operations to the [=Web Authentication API=]'s callers. +:: Also referred to herein as simply a [=client=]. See also [=Conforming User Agent=]. A [=[WAC]=] is an intermediary entity typically implemented in the user agent (in whole, or in part). Conceptually, it underlies the [=Web Authentication API=] and embodies the implementation of the {{PublicKeyCredential/[CREATE-METHOD]}} and {{PublicKeyCredential/[DISCOVER-METHOD]}} [=internal methods=]. It is responsible for both marshalling the inputs for the underlying [=authenticator operations=], and for returning the results of the latter operations to the [=Web Authentication API=]'s callers. The [=[WAC]=] runs on, and is distinct from, a [=[WAC] Device=]. @@ -1703,7 +1709,7 @@ that are returned to the caller when a new credential is created, or a new asser {{PublicKeyCredential}}'s [=interface object=] inherits {{Credential}}'s implementation of {{Credential/[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)}}, and defines its own -implementation of each of {{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}}, {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}}, and +implementation of each of {{PublicKeyCredential/[CREATE-METHOD]}}, {{PublicKeyCredential/[DISCOVER-METHOD]}}, and {{PublicKeyCredential/[[Store]](credential, sameOriginWithAncestors)}}. Calling {{CredentialsContainer}}'s {{CredentialsContainer/preventSilentAccess()}} method @@ -1733,10 +1739,10 @@ To support obtaining assertions via {{CredentialsContainer/get()|navigator.crede </xmp> -### Create a New Credential - PublicKeyCredential's <dfn for="PublicKeyCredential" method>\[[Create]](origin, options, sameOriginWithAncestors)</dfn> Method ### {#sctn-createCredential} +### Create a New Credential - PublicKeyCredential's <dfn for="PublicKeyCredential" method>[CREATE-METHOD-DEF]</dfn> Method ### {#sctn-createCredential} -<div link-for-hint="PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)"> -{{PublicKeyCredential}}'s [=interface object=]'s implementation of the {{Credential/[[Create]](origin, options, sameOriginWithAncestors)}} [=internal method=] [[!CREDENTIAL-MANAGEMENT-1]] allows +<div link-for-hint="PublicKeyCredential/[CREATE-METHOD]"> +{{PublicKeyCredential}}'s [=interface object=]'s implementation of the {{Credential/[CREATE-METHOD]}} [=internal method=] [[!CREDENTIAL-MANAGEMENT-1]] allows [=[WRP]=] scripts to call {{CredentialsContainer/create()|navigator.credentials.create()}} to request the creation of a new [=public key credential source=], [=bound credential|bound=] to an [=authenticator=]. @@ -1754,7 +1760,7 @@ see [[dom#abortcontroller-api-integration]] for detailed instructions. This [=internal method=] accepts three arguments: -<dl dfn-type="argument" dfn-for="PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)"> +<dl dfn-type="argument" dfn-for="PublicKeyCredential/[CREATE-METHOD]"> : <dfn>origin</dfn> :: This argument is the [=relevant settings object=]'s [=environment settings object/origin=], as determined by the @@ -1819,7 +1825,7 @@ When this method is invoked, the user agent MUST execute the following algorithm 1. If the length of <code>|pkOptions|.{{PublicKeyCredentialCreationOptions/user}}.{{PublicKeyCredentialUserEntity/id}}</code> is not between 1 and 64 bytes (inclusive) then throw a {{TypeError}}. -1. Let |callerOrigin| be {{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)/origin}}. If |callerOrigin| is an [=opaque origin=], throw a "{{NotAllowedError}}" {{DOMException}}. +1. Let |callerOrigin| be {{PublicKeyCredential/[CREATE-METHOD]/origin}}. If |callerOrigin| is an [=opaque origin=], throw a "{{NotAllowedError}}" {{DOMException}}. 1. Let |effectiveDomain| be the |callerOrigin|'s [=effective domain=]. If [=effective domain=] is not a [=valid domain=], then throw a @@ -1915,11 +1921,11 @@ a numbered step. If outdented, it (today) is rendered as a bullet in the midst o :: The [=ascii serialization of an origin|serialization of=] |callerOrigin|. : {{CollectedClientData/crossOrigin}} :: The inverse of the value of the - {{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)/sameOriginWithAncestors}} + {{PublicKeyCredential/[CREATE-METHOD]/sameOriginWithAncestors}} argument passed to this [=internal method=]. : {{CollectedClientData/topOrigin}} :: The [=ascii serialization of an origin|serialization of=] |callerOrigin|'s [=top-level origin=] if - the {{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)/sameOriginWithAncestors}} + the {{PublicKeyCredential/[CREATE-METHOD]/sameOriginWithAncestors}} argument passed to this [=internal method=] is [FALSE], else `undefined`. 1. Let |clientDataJSON| be the [=JSON-compatible serialization of client data=] constructed from |collectedClientData|. @@ -2309,7 +2315,7 @@ decline the entire interaction even if a [=public key credential source=] is pre The {{CredentialsContainer/get()|navigator.credentials.get()}} implementation [[!CREDENTIAL-MANAGEMENT-1]] calls <code>PublicKeyCredential.{{PublicKeyCredential/[[CollectFromCredentialStore]]()}}</code> to collect any [=credentials=] that should be available without [=user mediation=] (roughly, this specification's [=authorization gesture=]), and if it does not find -exactly one of those, it then calls <code>PublicKeyCredential.{{PublicKeyCredential/[[DiscoverFromExternalSource]]()}}</code> to have +exactly one of those, it then calls <code>PublicKeyCredential.{{PublicKeyCredential/[DISCOVER-METHOD]}}</code> to have the user select a [=public key credential source=]. Since this specification requires an [=authorization gesture=] to create any [=assertions=], the <code>PublicKeyCredential.<dfn @@ -2327,13 +2333,13 @@ Any {{CredentialsContainer/get()|navigator.credentials.get()}} operation can be aborted by leveraging the {{AbortController}}; see [[dom#abortcontroller-api-integration]] for detailed instructions. -#### PublicKeyCredential's <code><dfn for="PublicKeyCredential" method>\[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)</dfn></code> Method #### {#sctn-discover-from-external-source} +#### PublicKeyCredential's <code><dfn for="PublicKeyCredential" method>[DISCOVER-METHOD-DEF]</dfn></code> Method #### {#sctn-discover-from-external-source} -<div link-for-hint="PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)"> +<div link-for-hint="PublicKeyCredential/[DISCOVER-METHOD]"> This [=internal method=] accepts three arguments: -<dl dfn-type="argument" dfn-for="PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)"> +<dl dfn-type="argument" dfn-for="PublicKeyCredential/[DISCOVER-METHOD]"> : <dfn>origin</dfn> :: This argument is the [=relevant settings object=]'s [=environment settings object/origin=], as determined by the @@ -2394,7 +2400,7 @@ When this method is invoked, the user agent MUST execute the following algorithm Note: The user agent should take cognitive guidelines into considerations regarding timeout for users with special needs. -1. Let |callerOrigin| be {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)/origin}}. If |callerOrigin| is +1. Let |callerOrigin| be {{PublicKeyCredential/[DISCOVER-METHOD]/origin}}. If |callerOrigin| is an [=opaque origin=], throw a "{{NotAllowedError}}" {{DOMException}}. 1. Let |effectiveDomain| be the |callerOrigin|'s [=effective domain=]. @@ -2459,11 +2465,11 @@ When this method is invoked, the user agent MUST execute the following algorithm :: The [=ascii serialization of an origin|serialization of=] |callerOrigin|. : {{CollectedClientData/crossOrigin}} :: The inverse of the value of the - {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)/sameOriginWithAncestors}} + {{PublicKeyCredential/[DISCOVER-METHOD]/sameOriginWithAncestors}} argument passed to this [=internal method=]. : {{CollectedClientData/topOrigin}} :: The [=ascii serialization of an origin|serialization of=] |callerOrigin|'s [=top-level origin=] if - the {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)/sameOriginWithAncestors}} + the {{PublicKeyCredential/[DISCOVER-METHOD]/sameOriginWithAncestors}} argument passed to this [=internal method=] is [FALSE], else `undefined`. 1. Let |clientDataJSON| be the [=JSON-compatible serialization of client data=] constructed from |collectedClientData|. @@ -2686,9 +2692,9 @@ When this method is invoked, the user agent MUST execute the following algorithm #### <dfn for="PublicKeyCredential" algorithm="Issuing a credential request to an authenticator">Issuing a Credential Request to an Authenticator</dfn> #### {#sctn-issuing-cred-request-to-authenticator} -This sub-algorithm of {{PublicKeyCredential/[[DiscoverFromExternalSource]]()}} encompasses the specific UI context-independent +This sub-algorithm of {{PublicKeyCredential/[DISCOVER-METHOD]}} encompasses the specific UI context-independent steps necessary for requesting a [=credential=] from a given [=authenticator=], using given {{PublicKeyCredentialRequestOptions}}. -It is called by {{PublicKeyCredential/[[DiscoverFromExternalSource]]()}} from various points depending on which [=user mediation=] +It is called by {{PublicKeyCredential/[DISCOVER-METHOD]}} from various points depending on which [=user mediation=] the present [=authentication ceremony=] is subject to (e.g.: {{CredentialMediationRequirement/conditional}} mediation). This algorithm accepts the following arguments: @@ -3391,7 +3397,7 @@ during registration. #### Easily accessing credential data #### {#sctn-public-key-easy} -Every user of the {{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}} method will need to parse and store the returned [=credential public key=] in order to verify future [=authentication assertions=]. However, the [=credential public key=] is in COSE format [[!RFC9052]], inside the [=credentialPublicKey=] member of the [=attestedCredentialData=], inside the [=authenticator data=], inside the [=attestation object=] conveyed by {{AuthenticatorAttestationResponse}}.{{AuthenticatorAttestationResponse/attestationObject}}. [=[RPS]=] wishing to use [=attestation=] are obliged to do the work of parsing the {{AuthenticatorAttestationResponse/attestationObject}} and obtaining the [=credential public key=] because that public key copy is the one the [=authenticator=] [signed](#signing-procedure). However, many valid WebAuthn use cases do not require [=attestation=]. For those uses, user agents can do the work of parsing, expose the [=authenticator data=] directly, and translate the [=credential public key=] into a more convenient format. +Every user of the {{PublicKeyCredential/[CREATE-METHOD]}} method will need to parse and store the returned [=credential public key=] in order to verify future [=authentication assertions=]. However, the [=credential public key=] is in COSE format [[!RFC9052]], inside the [=credentialPublicKey=] member of the [=attestedCredentialData=], inside the [=authenticator data=], inside the [=attestation object=] conveyed by {{AuthenticatorAttestationResponse}}.{{AuthenticatorAttestationResponse/attestationObject}}. [=[RPS]=] wishing to use [=attestation=] are obliged to do the work of parsing the {{AuthenticatorAttestationResponse/attestationObject}} and obtaining the [=credential public key=] because that public key copy is the one the [=authenticator=] [signed](#signing-procedure). However, many valid WebAuthn use cases do not require [=attestation=]. For those uses, user agents can do the work of parsing, expose the [=authenticator data=] directly, and translate the [=credential public key=] into a more convenient format. The {{AuthenticatorAttestationResponse/getPublicKey()}} operation thus returns the [=credential public key=] as a [=SubjectPublicKeyInfo=]. This {{ArrayBuffer}} can, for example, be passed to Java's `java.security.spec.X509EncodedKeySpec`, .NET's `System.Security.Cryptography.ECDsa.ImportSubjectPublicKeyInfo`, or Go's `crypto/x509.ParsePKIXPublicKey`. @@ -3784,10 +3790,9 @@ Note: The {{AuthenticatorAttachment}} enumeration is deliberately not referenced :: This value indicates [=cross-platform attachment=]. </div> -Note: An [=authenticator attachment modality=] selection option is available only in the {{PublicKeyCredential/[[Create]](origin, options, -sameOriginWithAncestors)}} operation. The [=[RP]=] may use it to, for example, ensure the user has a [=roaming credential=] for +Note: An [=authenticator attachment modality=] selection option is available only in the {{PublicKeyCredential/[CREATE-METHOD]}} operation. The [=[RP]=] may use it to, for example, ensure the user has a [=roaming credential=] for authenticating on another [=client device=]; or to specifically register a [=platform credential=] for easier reauthentication using a -particular [=client device=]. The {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} +particular [=client device=]. The {{PublicKeyCredential/[DISCOVER-METHOD]}} operation has no [=authenticator attachment modality=] selection option, so the [=[RP]=] SHOULD accept any of the user's registered [=public key credential|credentials=]. The [=client=] and user will then use whichever is available and convenient at the time. @@ -3969,24 +3974,24 @@ an assertion. Its {{PublicKeyCredentialRequestOptions/challenge}} member MUST be ## Abort Operations with `AbortSignal` ## {#sctn-abortoperation} Developers are encouraged to leverage the {{AbortController}} to manage the -{{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}} and {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} operations. +{{PublicKeyCredential/[CREATE-METHOD]}} and {{PublicKeyCredential/[DISCOVER-METHOD]}} operations. See [[dom#abortcontroller-api-integration]] section for detailed instructions. Note: [[dom#abortcontroller-api-integration]] section specifies that web platform APIs integrating with the {{AbortController}} must reject the promise immediately once the {{AbortSignal}} is [=AbortSignal/aborted=]. - Given the complex inheritance and parallelization structure of the {{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}} - and {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} methods, the algorithms for the two APIs fulfills this + Given the complex inheritance and parallelization structure of the {{PublicKeyCredential/[CREATE-METHOD]}} + and {{PublicKeyCredential/[DISCOVER-METHOD]}} methods, the algorithms for the two APIs fulfills this requirement by checking the [=AbortSignal/aborted=] property in three places. In the case of - {{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}}, the [=AbortSignal/aborted=] property is checked first in - [[credential-management-1#algorithm-create]] immediately before calling {{Credential/[[Create]](origin, options, sameOriginWithAncestors)}}, + {{PublicKeyCredential/[CREATE-METHOD]}}, the [=AbortSignal/aborted=] property is checked first in + [[credential-management-1#algorithm-create]] immediately before calling {{Credential/[CREATE-METHOD]}}, then in [[#sctn-createCredential]] right before [=authenticator sessions=] start, and finally during [=authenticator sessions=]. The same goes for - {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}}. + {{PublicKeyCredential/[DISCOVER-METHOD]}}. The [=visibility states|visibility=] and [=focus=] state of the {{Window}} object determines whether the -{{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}} and {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} operations +{{PublicKeyCredential/[CREATE-METHOD]}} and {{PublicKeyCredential/[DISCOVER-METHOD]}} operations should continue. When the {{Window}} object associated with the [=Document=] loses focus, -{{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}} and {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} operations +{{PublicKeyCredential/[CREATE-METHOD]}} and {{PublicKeyCredential/[DISCOVER-METHOD]}} operations SHOULD be aborted. Issue: The WHATWG HTML WG is discussing whether to provide a hook when a browsing context gains or @@ -4522,14 +4527,14 @@ A {{Document}}'s [=Document/permissions policy=] determines whether any content If disabled in any document, no content in the document will be [=allowed to use=] the foregoing methods: attempting to do so will [return an error](https://www.w3.org/2001/tag/doc/promises-guide#errors). -Note: Algorithms specified in [[!CREDENTIAL-MANAGEMENT-1]] perform the actual permissions policy evaluation. This is because such policy evaluation needs to occur when there is access to the [=current settings object=]. The {{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}} and {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} [=internal methods=] does not have such access since they are invoked [=in parallel=] by {{CredentialsContainer}}'s <a abstract-op>Create a `Credential`</a> and <a abstract-op>Request a `Credential`</a> abstract operations [[!CREDENTIAL-MANAGEMENT-1]]. +Note: Algorithms specified in [[!CREDENTIAL-MANAGEMENT-1]] perform the actual permissions policy evaluation. This is because such policy evaluation needs to occur when there is access to the [=current settings object=]. The {{PublicKeyCredential/[CREATE-METHOD]}} and {{PublicKeyCredential/[DISCOVER-METHOD]}} [=internal methods=] does not have such access since they are invoked [=in parallel=] by {{CredentialsContainer}}'s <a abstract-op>Create a `Credential`</a> and <a abstract-op>Request a `Credential`</a> abstract operations [[!CREDENTIAL-MANAGEMENT-1]]. ## Using Web Authentication within <code>iframe</code> elements ## {#sctn-iframe-guidance} The [=Web Authentication API=] is disabled by default in cross-origin <{iframe}>s. -To override this default policy and indicate that a cross-origin <{iframe}> is allowed to invoke the [=Web Authentication API=]'s {{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} method, specify the <{iframe/allow}> attribute on the <{iframe}> element and include the <code>[=publickey-credentials-get-feature|publickey-credentials-get=]</code> feature-identifier token in the <{iframe/allow}> attribute's value. +To override this default policy and indicate that a cross-origin <{iframe}> is allowed to invoke the [=Web Authentication API=]'s {{PublicKeyCredential/[DISCOVER-METHOD]}} method, specify the <{iframe/allow}> attribute on the <{iframe}> element and include the <code>[=publickey-credentials-get-feature|publickey-credentials-get=]</code> feature-identifier token in the <{iframe/allow}> attribute's value. [=[RPS]=] utilizing the WebAuthn API in an embedded context should review [[#sctn-seccons-visibility]] regarding [=UI redressing=] and its possible mitigations. @@ -7572,10 +7577,10 @@ However, [=authenticators=] that do not utilize [[!FIDO-CTAP]] do not necessaril 1. Set {{AuthenticationExtensionsLargeBlobOutputs/supported}} to [TRUE]. Note: This is in anticipation of an authenticator capable of storing large blobs becoming available. - It occurs during extension processing in [step 12](#CreateCred-process-extensions) of {{PublicKeyCredential/[[Create]]()}}. + It occurs during extension processing in [step 12](#CreateCred-process-extensions) of {{PublicKeyCredential/[CREATE-METHOD]}}. The {{AuthenticationExtensionsLargeBlobOutputs}} will be abandoned if no satisfactory authenticator becomes available. - 1. If a [=create/candidate authenticator=] becomes available ([step 21](#CreateCred-async-loop) of {{PublicKeyCredential/[[Create]]()}}) then, + 1. If a [=create/candidate authenticator=] becomes available ([step 21](#CreateCred-async-loop) of {{PublicKeyCredential/[CREATE-METHOD]}}) then, before evaluating any <code>|options|</code>, [=iteration/continue=] (i.e. ignore the [=create/candidate authenticator=]) if the [=create/candidate authenticator=] is not capable of storing large blobs. 1. Otherwise (i.e. {{AuthenticationExtensionsLargeBlobInputs/support}} is absent or has the value {{LargeBlobSupport/preferred}}): @@ -7589,7 +7594,7 @@ However, [=authenticators=] that do not utilize [[!FIDO-CTAP]] do not necessaril 1. Return a {{DOMException}} whose name is “{{NotSupportedError}}”. 1. If {{AuthenticationExtensionsLargeBlobInputs/read}} is present and has the value [TRUE]: 1. Initialize the [=client extension output=], {{AuthenticationExtensionsClientOutputs/largeBlob}}. - 1. If any authenticator indicates success (in {{PublicKeyCredential/[[DiscoverFromExternalSource]]()}}), attempt to read any largeBlob data associated with the asserted credential. + 1. If any authenticator indicates success (in {{PublicKeyCredential/[DISCOVER-METHOD]}}), attempt to read any largeBlob data associated with the asserted credential. 1. If successful, set {{AuthenticationExtensionsLargeBlobOutputs/blob}} to the result. Note: if the read is not successful, {{AuthenticationExtensionsClientOutputs/largeBlob}} will be present in {{AuthenticationExtensionsClientOutputs}} but the {{AuthenticationExtensionsLargeBlobOutputs/blob}} member will not be present. @@ -8852,7 +8857,7 @@ These recommendations serve to prevent an adversary with physical access to an [ ### Registration Ceremony Privacy ### {#sctn-make-credential-privacy} In order to protect users from being identified without [=user consent|consent=], implementations of the -{{PublicKeyCredential/[[Create]](origin, options, sameOriginWithAncestors)}} method need to take care to not leak information that +{{PublicKeyCredential/[CREATE-METHOD]}} method need to take care to not leak information that could enable a malicious [=[WRP]=] to distinguish between these cases, where "excluded" means that at least one of the [=public key credential|credentials=] listed by the [=[RP]=] in {{PublicKeyCredentialCreationOptions/excludeCredentials}} is [=bound credential|bound=] to the [=authenticator=]: @@ -8875,7 +8880,7 @@ leaked. ### Authentication Ceremony Privacy ### {#sctn-assertion-privacy} In order to protect users from being identified without [=user consent|consent=], implementations of the -{{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} method need to take care to not +{{PublicKeyCredential/[DISCOVER-METHOD]}} method need to take care to not leak information that could enable a malicious [=[WRP]=] to distinguish between these cases, where "named" means that the [=public key credential|credential=] is listed by the [=[RP]=] in {{PublicKeyCredentialRequestOptions/allowCredentials}}: From 5887b9f253fcc738db8a5c66818bb3ad954bc84f Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Mon, 7 Oct 2024 15:52:46 +0200 Subject: [PATCH 20/26] Drop definition "User Credential" unused since 2ec45f8b34638b0c62bb4208507bc4a76cd0ef4f --- index.bs | 1 - 1 file changed, 1 deletion(-) diff --git a/index.bs b/index.bs index 060d1b51e..1ca58aa2c 100644 --- a/index.bs +++ b/index.bs @@ -1087,7 +1087,6 @@ 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=]. From 1fcb7aad5898035244ecc26a96da3074e8d6a516 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Mon, 7 Oct 2024 15:56:44 +0200 Subject: [PATCH 21/26] Fix typo in reference to variable |effectiveDomain| Fixes this Bikeshed lint: ``` LINE ~3100: The var 'effective domain' (in global scope) is only used once. If this is not a typo, please add an ignore='' attribute to the <var>. ``` --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index 060d1b51e..71d43d753 100644 --- a/index.bs +++ b/index.bs @@ -3098,7 +3098,7 @@ The [$Asynchronous RP ID validation algorithm$] lets [=signal methods=] validate and returns a promise that rejects if the validation fails. The steps are: 1. Let |effectiveDomain| be the [=relevant settings object=]'s [=environment - settings object/origin=]'s [=effective domain=]. If |effective domain| is + settings object/origin=]'s [=effective domain=]. If |effectiveDomain| is not a [=valid domain=], then return [=a promise rejected with=] "{{SecurityError}}" {{DOMException}}. 1. If |rpId| [=is a registrable domain suffix of or is equal to=] From 39da7b119eabee3b75529586712f28be22cf51de Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Mon, 7 Oct 2024 12:50:45 +0200 Subject: [PATCH 22/26] Remove apparent reference to non-existent [[Get]] internal method --- index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.bs b/index.bs index a2b8e1785..5a7b98507 100644 --- a/index.bs +++ b/index.bs @@ -2301,7 +2301,7 @@ The following [=simple exceptions=] can be raised: </dl> -### Use an Existing Credential to Make an Assertion - PublicKeyCredential's `[[Get]](options)` Method ### {#sctn-getAssertion} +### Use an Existing Credential to Make an Assertion ### {#sctn-getAssertion} [=[WRPS]=] call <code><a idl for="CredentialsContainer" lt="get()">navigator.credentials.get({publicKey:..., ...})</a></code> to discover and use an existing [=public key credential=], with the [=user consent|user's consent=]. [=[RP]=] script optionally specifies some criteria From c258674eceafbb05545f596cb37b3703b3fbc931 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Mon, 7 Oct 2024 17:05:08 +0200 Subject: [PATCH 23/26] Change "Method" to "Internal Method" in headings where appropriate --- index.bs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.bs b/index.bs index 5a7b98507..02c042cb3 100644 --- a/index.bs +++ b/index.bs @@ -1739,7 +1739,7 @@ To support obtaining assertions via {{CredentialsContainer/get()|navigator.crede </xmp> -### Create a New Credential - PublicKeyCredential's <dfn for="PublicKeyCredential" method>[CREATE-METHOD-DEF]</dfn> Method ### {#sctn-createCredential} +### Create a New Credential - PublicKeyCredential's <dfn for="PublicKeyCredential" method>[CREATE-METHOD-DEF]</dfn> Internal Method ### {#sctn-createCredential} <div link-for-hint="PublicKeyCredential/[CREATE-METHOD]"> {{PublicKeyCredential}}'s [=interface object=]'s implementation of the {{Credential/[CREATE-METHOD]}} [=internal method=] [[!CREDENTIAL-MANAGEMENT-1]] allows @@ -2333,7 +2333,7 @@ Any {{CredentialsContainer/get()|navigator.credentials.get()}} operation can be aborted by leveraging the {{AbortController}}; see [[dom#abortcontroller-api-integration]] for detailed instructions. -#### PublicKeyCredential's <code><dfn for="PublicKeyCredential" method>[DISCOVER-METHOD-DEF]</dfn></code> Method #### {#sctn-discover-from-external-source} +#### PublicKeyCredential's <code><dfn for="PublicKeyCredential" method>[DISCOVER-METHOD-DEF]</dfn></code> Internal Method #### {#sctn-discover-from-external-source} <div link-for-hint="PublicKeyCredential/[DISCOVER-METHOD]"> @@ -2855,7 +2855,7 @@ The following [=simple exceptions=] can be raised: </dl> -### Store an Existing Credential - PublicKeyCredential's `[[Store]](credential, sameOriginWithAncestors)` Method ### {#sctn-storeCredential} +### Store an Existing Credential - PublicKeyCredential's `[[Store]](credential, sameOriginWithAncestors)` Internal Method ### {#sctn-storeCredential} <div link-for-hint="PublicKeyCredential/[[Store]](credential, sameOriginWithAncestors)"> From 8d76185ae7b8c0a3069872fbd1f8d3789b5bfb94 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Mon, 7 Oct 2024 17:07:14 +0200 Subject: [PATCH 24/26] Simplify reference to default [[CollectFromCredentialStore]] --- index.bs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.bs b/index.bs index 02c042cb3..6ef5bd5fa 100644 --- a/index.bs +++ b/index.bs @@ -2318,9 +2318,9 @@ should be available without [=user mediation=] (roughly, this specification's [= exactly one of those, it then calls <code>PublicKeyCredential.{{PublicKeyCredential/[DISCOVER-METHOD]}}</code> to have the user select a [=public key credential source=]. -Since this specification requires an [=authorization gesture=] to create any [=assertions=], the <code>PublicKeyCredential.<dfn -for="PublicKeyCredential" method>\[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)</dfn></code> [=internal method=] inherits the default behavior of -{{Credential/[[CollectFromCredentialStore]]()|Credential.[[CollectFromCredentialStore]]()}}, of returning an empty set. +Since this specification requires an [=authorization gesture=] to create any [=assertions=], +{{PublicKeyCredential}} inherits the default behavior of +{{Credential/[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)}}, of returning an empty set. In general, the user agent SHOULD show some UI to the user to guide them in selecting and authorizing an authenticator with which to complete the operation. By setting <code>|options|.{{CredentialRequestOptions/mediation}}</code> to {{CredentialMediationRequirement/conditional}}, [=[RPS]=] can indicate that a prominent modal UI should <i>not</i> be shown <i>unless</i> credentials are discovered. From 068d7f56764c6a48d7de74f7fcf99267a2b726b1 Mon Sep 17 00:00:00 2001 From: Emil Lundberg <emil@yubico.com> Date: Mon, 7 Oct 2024 17:10:53 +0200 Subject: [PATCH 25/26] Add reference to #sctn-discover-from-external-source from discussion of get() internals --- index.bs | 1 + 1 file changed, 1 insertion(+) diff --git a/index.bs b/index.bs index 6ef5bd5fa..af9ab45be 100644 --- a/index.bs +++ b/index.bs @@ -2321,6 +2321,7 @@ the user select a [=public key credential source=]. Since this specification requires an [=authorization gesture=] to create any [=assertions=], {{PublicKeyCredential}} inherits the default behavior of {{Credential/[[CollectFromCredentialStore]](origin, options, sameOriginWithAncestors)}}, of returning an empty set. +{{PublicKeyCredential}}'s implementation of {{PublicKeyCredential/[DISCOVER-METHOD]}} is specified in the next section. In general, the user agent SHOULD show some UI to the user to guide them in selecting and authorizing an authenticator with which to complete the operation. By setting <code>|options|.{{CredentialRequestOptions/mediation}}</code> to {{CredentialMediationRequirement/conditional}}, [=[RPS]=] can indicate that a prominent modal UI should <i>not</i> be shown <i>unless</i> credentials are discovered. From 8c2cfdd4a7e6fec4eef91c31f2b0fc0ef972eef6 Mon Sep 17 00:00:00 2001 From: Shane Weeden <sbweeden@users.noreply.github.com> Date: Thu, 24 Oct 2024 04:28:58 +1000 Subject: [PATCH 26/26] Update index.bs accepting line breaks as elum suggested Co-authored-by: Emil Lundberg <emil@emlun.se> --- index.bs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.bs b/index.bs index b1d55a2fa..19975144b 100644 --- a/index.bs +++ b/index.bs @@ -4811,7 +4811,9 @@ leave the <code>[=authData/signCount=]</code> in the [=authenticator data=] cons A [=[RP]=] stores the [=signature counter=] of the most recent [=authenticatorGetAssertion=] operation. (Or the counter from the [=authenticatorMakeCredential=] operation if no [=authenticatorGetAssertion=] has ever been performed on a credential.) In subsequent [=authenticatorGetAssertion=] operations, the [=[RP]=] compares the stored [=signature counter=] value with the new -<code>[=authData/signCount=]</code> value returned in the assertion's [=authenticator data=]. If either is non-zero, and the new <code>[=authData/signCount=]</code> value is less than or equal to the stored value, a cloned authenticator may exist, or the authenticator may be malfunctioning, or a race condition might exist where the relying party is receiving and processing assertions in an order other than the order they were generated at the authenticator. +<code>[=authData/signCount=]</code> value returned in the assertion's [=authenticator data=]. If either is non-zero, and the new <code>[=authData/signCount=]</code> value is less than or equal to the stored value, a cloned authenticator may exist, or the authenticator may be malfunctioning, +or a race condition might exist where the relying party is receiving and processing assertions +in an order other than the order they were generated at the authenticator. Detecting a [=signature counter=] mismatch does not indicate whether the current operation was performed by a cloned authenticator or the original authenticator. [=[RPS]=] should address this situation appropriately relative to their individual situations, i.e., their risk tolerance or operational factors that might result in an acceptable reason for non-increasing values.