From 37dacdae7de4a08b08b99a10843c37fb7babb418 Mon Sep 17 00:00:00 2001 From: Emil Lundberg Date: Tue, 1 Oct 2024 22:56:13 +0200 Subject: [PATCH 1/5] 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}}. + +#### 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=]: + +
+    // 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: {} }
+    
+ +- The {{AuthenticationExtensionsPRFOutputs/results}} output may be present + during [=registration ceremonies=] or [=authentication ceremonies=] + if the {{AuthenticationExtensionsPRFInputs/eval}} or {{AuthenticationExtensionsPRFInputs/evalByCredential}} input is present: + +
+    // 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 } } }
+    
+ +- The {{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/second}} output + is present if and only if the {{AuthenticationExtensionsPRFOutputs/results}} output is present + and the {{AuthenticationExtensionsPRFValues/second}} input was present in the chosen PRF inputs: + +
+    // 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 } } }
+    
+ +- 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: + +
+    // 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]),
+        }
+      }
+    }
+    
+ +Pseudo-random values used in this section were generated as follows: + +- "e02eZ9lPp0UdkF4vGRO4-NxlhWBkL1FCmsmb1tTfRyE" = Base64Url(SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x00)) +- h'c4172e982e9097c39a6c0cb720cb375b92e3fcad154a63e43a93f1096b1e1973' = SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x01) + + +#### 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: + +
+    ; 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'
+    
+ + 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 + {{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/first}} + and {{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/second}} + outputs, respectively. + +- Single input case using PIN protocol 2: + +
+    ; 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'
+    
+ +- Two input case using PIN protocol 2: + +
+    ; 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'
+    
+ +- Single input case using PIN protocol 1: + +
+    ; 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'
+    
+ +Inputs and pseudo-random values used in this section were generated as follows: + +- seed = UTF-8("WebAuthn PRF test vectors") +- prf_eval_first = seed || 0x02 +- prf_eval_second = seed || 0x03 +- platform_key_agreement_private_key = SHA-256(seed || 0x04) +- authenticator_key_agreement_public_key = P256-Public-Key(sk) + where sk = SHA-256(seed || 0x05) +- authenticator_cred_random = SHA-256(seed || 0x06) +- `iv` in single-input `salt_enc` with PIN protocol 2: Truncated SHA-256(seed || 0x07) +- `iv` in two-input `salt_enc` with PIN protocol 2: Truncated SHA-256(seed || 0x08) +- `iv` in single-input `output_enc` with PIN protocol 2: Truncated SHA-256(seed || 0x09) +- `iv` in two-input `output_enc` with PIN protocol 2: Truncated SHA-256(seed || 0x10) + + ### Large blob storage extension (largeBlob) ### {#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 ebfe871f78f5d5be6cefecabf5a723b119dacae2 Mon Sep 17 00:00:00 2001 From: Emil Lundberg Date: Wed, 2 Oct 2024 22:41:04 +0200 Subject: [PATCH 2/5] Use 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> + - The {{AuthenticationExtensionsPRFOutputs/results}} output may be present during [=registration ceremonies=] or [=authentication ceremonies=] if the {{AuthenticationExtensionsPRFInputs/eval}} or {{AuthenticationExtensionsPRFInputs/evalByCredential}} input is present: -
+    
     // 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>
+    
 
 - The {{AuthenticationExtensionsPRFOutputs/results}}.{{AuthenticationExtensionsPRFValues/second}} output
     is present if and only if the {{AuthenticationExtensionsPRFOutputs/results}} output is present
     and the {{AuthenticationExtensionsPRFValues/second}} input was present in the chosen PRF inputs:
 
-    
+    
     // 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>
+    
 
 - 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:
 
-    
+    
     // Example extension inputs:
     {
       prf: {
@@ -7600,7 +7600,7 @@ The examples are not exhaustive.
         }
       }
     }
-    </pre>
+    
 
 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:
 
-    
+    
     ; 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>
+    
 
     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:
 
-    
+    
     ; 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>
+    
 
 - Two input case using PIN protocol 2:
 
-    
+    
     ; 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>
+    
 
 - Single input case using PIN protocol 1:
 
-    
+    
     ; 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>
+    
 
 Inputs and pseudo-random values used in this section were generated as follows:
 

From c2395cbbb6351654ef1a2d40ceba252ca73d4683 Mon Sep 17 00:00:00 2001
From: Emil Lundberg 
Date: Wed, 2 Oct 2024 23:13:17 +0200
Subject: [PATCH 3/5] 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 SHA-256(seed || 0x07)
 - `iv` in two-input `salt_enc` with PIN protocol 2: Truncated SHA-256(seed || 0x08)
 - `iv` in single-input `output_enc` with PIN protocol 2: Truncated SHA-256(seed || 0x09)
-- `iv` in two-input `output_enc` with PIN protocol 2: Truncated SHA-256(seed || 0x10)
+- `iv` in two-input `output_enc` with PIN protocol 2: Truncated SHA-256(seed || 0x0a)
 
 
 ### Large blob storage extension (largeBlob) ### {#sctn-large-blob-extension}

From b953fed1340f3c97096444960c6107c07cebb442 Mon Sep 17 00:00:00 2001
From: Emil Lundberg 
Date: Wed, 2 Oct 2024 23:21:40 +0200
Subject: [PATCH 4/5] 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 
Date: Wed, 2 Oct 2024 23:32:24 +0200
Subject: [PATCH 5/5] Escape single quote in CDDL-style byte string literal in
 

---
 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:
 
 - "e02eZ9lPp0UdkF4vGRO4-NxlhWBkL1FCmsmb1tTfRyE" = Base64Url(SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x00))
-- h'c4172e982e9097c39a6c0cb720cb375b92e3fcad154a63e43a93f1096b1e1973' = SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x01)
+- h'c4172e982e9097c39a6c0cb720cb375b92e3fcad154a63e43a93f1096b1e1973' = SHA-256(UTF-8("WebAuthn PRF test vectors") || 0x01)
 
 
 #### Test vectors: CTAP2 `hmac-secret` extension #### {#prf-extension-test-vectors-ctap}