From 37dacdae7de4a08b08b99a10843c37fb7babb418 Mon Sep 17 00:00:00 2001 From: Emil Lundberg Date: Tue, 1 Oct 2024 22:56:13 +0200 Subject: [PATCH] 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.