From 5f255b48e03ac3e26a1b01c0ac2ad44a5bc22e63 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Fri, 3 Nov 2023 12:14:12 +0000 Subject: [PATCH 01/11] Add new asymmetric key types for SPAKE2+ * Add key encodings * Define public key format * Define key derivation procedure --- doc/ext-pake/api/pake.rst | 204 +++++++++++++++++++ doc/ext-pake/appendix/history.rst | 2 + doc/ext-pake/appendix/specdef_values.rst | 18 ++ doc/ext-pake/figure/spake2p_key.json | 18 ++ doc/ext-pake/figure/spake2p_key.json.license | 2 + doc/ext-pake/figure/spake2p_key.pdf | Bin 0 -> 7438 bytes doc/ext-pake/figure/spake2p_key.pdf.license | 2 + doc/ext-pake/figure/spake2p_key.svg | 2 + doc/ext-pake/figure/spake2p_key.svg.license | 2 + 9 files changed, 250 insertions(+) create mode 100644 doc/ext-pake/figure/spake2p_key.json create mode 100644 doc/ext-pake/figure/spake2p_key.json.license create mode 100644 doc/ext-pake/figure/spake2p_key.pdf create mode 100644 doc/ext-pake/figure/spake2p_key.pdf.license create mode 100644 doc/ext-pake/figure/spake2p_key.svg create mode 100644 doc/ext-pake/figure/spake2p_key.svg.license diff --git a/doc/ext-pake/api/pake.rst b/doc/ext-pake/api/pake.rst index 99886c7b..27e77912 100644 --- a/doc/ext-pake/api/pake.rst +++ b/doc/ext-pake/api/pake.rst @@ -55,6 +55,77 @@ The permitted values of HASH-TYPE depend on the specific KDF algorithm. a. ``hh`` is the HASH-TYPE for the hash algorithm, ``hash``, used to construct the key derivation algorithm. +Key encoding +------------ + +A new type of asymmetric key is added for the SPAKE2+ algorithms. The Asymmetric key sub-type values table in `[PSA-CRYPT]` Appendix B is extended with the information in :numref:`table-spake2p-keys`. + +.. csv-table:: New SPAKE2+ asymmetric key sub-type + :name: table-spake2p-keys + :header-rows: 1 + :align: left + :widths: auto + + Asymmetric key type, ASYM-TYPE, Details + SPAKE2+, 4, See :secref:`spakep2-key-encoding` + +.. rationale:: + + The ASYM-TYPE value 4 is selected as this has the same parity as the ECC sub-type, which have the value 1. The enables the same ECC-FAMILY and P values to be used when encoding a SPAKE2+ key type, as is used in the Elliptic Curve key types. + +.. _spakep2-key-encoding: + +SPAKE2+ key encoding +~~~~~~~~~~~~~~~~~~~~ + +The key type for SPAKE2+ keys defined in this specification are encoded as shown in :numref:`fig-spake2p-key-fields`. + +.. figure:: ../figure/spake2p_key.* + :name: fig-spake2p-key-fields + + SPAKE2+ key encoding + +PAIR is either 0 for a public key, or 3 for a key pair. + +The defined values for ECC-FAMILY and P are shown in :numref:`table-spake2p-type`. + +.. csv-table:: SPAKE2+ key family values + :name: table-spake2p-type + :header-rows: 1 + :align: left + :widths: auto + + SPAKE2+ group, ECC-FAMILY, P, ECC family :sup:`a`, Public key value, Key pair value + SECP R1, 0x09, 0, :code:`PSA_ECC_FAMILY_SECP_R1`, ``0x4412``, ``0x7412`` + Twisted Edwards, 0x21, 0, :code:`PSA_ECC_FAMILY_TWISTED_EDWARDS`, ``0x4442``, ``0x7442`` + +a. The key type value is constructed from the Elliptic Curve family using either :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY(family)` or :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(family)` as required. + +Key formats +----------- + +A SPAKE2+ public key can be exported and imported, to enable use cases that require offline registration. + +The public key consists of the two values w0 and L, which result from the SPAKE2+ registration phase. w0 is a scalar in the same range as a private Elliptic curve key from the group used as the SPAKE2+ primitive group. L is a point on the curve, similar to a public key from the same group. + +The default format for the SPAKE2+ public key is the concatenation of the formatted values for w0 and L, using the the standard formats for Elliptic curve keys. For example, for SPAKE2+ over P-256 (secp256r1), the output from :code:`psa_export_public_key()` would be: + + [ w0 ]\ :sub:`32` || 0x04 || [ x\ :sub:`L` ]\ :sub:`32` || [ y\ :sub:`L` ]\ :sub:`32` + +Where [ v ]\ :sub:`n` is an n-byte, big-endian encoding of the integer value v. + +.. todo:: + In this example, how does using a 'concatenation of elements' depiction compare to the 'bullet list of elements' approach used in the Weierstrass public key format in §9.6.4? + +.. todo:: + In this example, how does the short-hand notation --- [ v ]\ :sub:`n` --- compare with the text description approach used in the Weierstrass public key format in §9.6.4, or the function-based (e.g. ``I2OSP()``) approach used in texts such as SEC1? + +.. todo:: + Would it be better to provide an explicit definition for all of the elliptic curves over which SPAKE2+ is defined, rather than just provide a single example? + +.. todo:: + It might also be time to decide on how to style/format pseudo-mathematical content of the specification. Presently there is a arbitrary mixture of ``monospace code/LaTeX-source-style material a^b = 1, F_q`` (as typical in IETF RFCs) and *emphasized* or regular font .rst material a\ :sup:`b` = 1, *F*\ :sub:`q` (seen in NIST publications, and some IETF RFCs). But we also have the ability to use the ``:math:`` role to :math:`\text{render like LaTeX: } a^b=1, \mathbb{F}_q` (used in SECG and some NIST publications). + Changes and additions to the Programming API -------------------------------------------- @@ -68,6 +139,139 @@ Changes and additions to the Programming API * These definitions must be embedded in, or included by, psa/crypto.h */ +.. _pake-keys: + +SPAKE2+ keys +~~~~~~~~~~~~ + +The SPAKE2+ protocol consists of three phases: + +1. Registration +2. Authenticated key exchange +3. Key confirmation + +The registration phase can be carried out immediately prior to the other phases, or can be carried out offline, and the result of the registration phase transferred to the participants in the protocol for later online authentication. + +The |API| uses an asymmetric key-pair, and public-key, to store the output of the registration, for input to the authentication protocol. The registration is carried out using a key derivation operation, and the key exchange and confirmation is carried out using a PAKE operation. For a SPAKE2+ PAKE operation, the prover, or client, role requires a SPAKE2+ key-pair, while the verifier, or server, role can use either a SPAKE2+ key-pair or SPAKE2+ public key. + +The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is parameterized by a specific Elliptic curve. The Elliptic curve families are used to parameterize the key type, and the key size selects the specific curve. :issue:`Is this overkill? - RFC9383 only specifies cipher-suites that use the SECP R1 curves and the Edwards curves, we could have a custom set of families` + +.. macro:: PSA_KEY_TYPE_SPAKE2P_KEY_PAIR + :definition: /* specification-defined value */ + + .. summary:: + SPAKE2+ key pair: both the prover and verifier key. + + The size of a SPAKE2+ key is the size associated with the Elliptic curve group, that is, ceil(log2(q)) for a curve over a field F\ :sub:`q`. See the documentation of each Elliptic curve family for details. + + .. param:: curve + A value of type :code:`psa_ecc_family_t` that identifies the Elliptic curve family to be used. + + .. subsection:: Compatible algorithms + + SPAEK2+ key pairs can be used in SPAKE2+ PAKE algorithms. + +.. macro:: PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY + :definition: /* specification-defined value */ + + .. summary:: + SPAKE2+ public key: the verifier key. + + .. param:: curve + A value of type :code:`psa_ecc_family_t` that identifies the Elliptic curve family to be used. + + The size of an SPAKE2+ public key is the same as the corresponding private key. See `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()` and the documentation of each Elliptic curve family for details. + + .. subsection:: Compatible algorithms + + SPAEK2+ key pairs can be used in SPAKE2+ PAKE algorithms. + +.. macro:: PSA_KEY_TYPE_IS_SPAKE2P + :definition: /* specification-defined value */ + + .. summary:: + Whether a key type is a SPAKE2+ key, either a key pair or a public key. + + .. param:: type + A key type: a value of type :code:`psa_key_type_t`. + +.. macro:: PSA_KEY_TYPE_IS_SPAKE2P_KEY_PAIR + :definition: /* specification-defined value */ + + .. summary:: + Whether a key type is a SPAKE2+ key pair. + + .. param:: type + A key type: a value of type :code:`psa_key_type_t`. + +.. macro:: PSA_KEY_TYPE_IS_SPAKE2P_PUBLIC_KEY + :definition: /* specification-defined value */ + + .. summary:: + Whether a key type is a SPAKE2+ public key. + + .. param:: type + A key type: a value of type :code:`psa_key_type_t`. + +.. macro:: PSA_KEY_TYPE_SPAKE2P_GET_FAMILY + :definition: /* specification-defined value */ + + .. summary:: + Extract the curve family from a SPAKE2+ key type. + + .. param:: type + A SPAKE2+ key type: a value of type :code:`psa_key_type_t` such that :code:`PSA_KEY_TYPE_IS_SPAKE2P(type)` is true. + + .. return:: psa_ecc_family_t + The elliptic curve family id, if ``type`` is a supported SPAKE2+ key. Unspecified if ``type`` is not a supported SPAKE2+ key. + +Key derivation of SPAKE2+ keys +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The SPAKE2+ key types can be output from a key derivation using :code:`psa_key_derivation_output_key()`. The SPAKE2+ protocol recommends that a key-stretching kdf, such as PBKDF2, is used to hash the SPAKE2+ password. See RFC 9383 for details. + +For example, after setting up the PBKDF2 operation, the following process will derive the SPAKE2+ key pair for use with the P-256 Elliptic curve group :issue:`(This example may be more than necessary in the specification?)`: + +1. Allocate and initialize a key attributes object: + + .. code-block:: xref + + psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT; + +#. Set the key type and size: + + .. code-block:: xref + + psa_set_key_type(&att, PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&att, 256); // for P-256 + +#. Set the key policy: + + .. code-block:: xref + + psa_set_key_usage_flags(&att, PSA_KEY_USAGE_????); + psa_set_key_algorithm(&att, PSA_ALG_SPAKE2P); + + :issue:`Do we need a new usage flag for augmented PAKEs? For example PSA_KEY_USAGE_PROVE/VERIFY. Or do we just use PSA_KEY_USAGE_DERIVE as specified by psa_pake_set_password_key()?` + +#. Derive the key: + + .. code-block:: xref + + psa_key_id_t sp2_key; + psa_key_derivation_output_key(&att, &kdf_op, &sp2_key); + +The key derivation process in :code:`psa_key_derivation_output_key()` follows the recommendations for the registration process in RFC 9383, and matches the specification of this process in the Matter specification. + +For the |API|: + +* The derivation of SPAKE2+ keys extracts ceil(log2(p)/8) + 8 bytes from the PBKDF for each of w0s and w1s, where p is the prime factor of the order of the elliptic curve group. +* The calculation of w0, w1, and L then proceeds as described in the RFC. +* A SPAKE2+ key-pair is the pair (w0, w1). +* A SPAKE2+ public key is the pair (w0, L). + +.. todo:: + Would a table of required w0s/w1s lengths for each of the supported SPAKE2+ elliptic curve groups be useful here? .. _pake-algorithms: diff --git a/doc/ext-pake/appendix/history.rst b/doc/ext-pake/appendix/history.rst index b2166175..cce8bbaa 100644 --- a/doc/ext-pake/appendix/history.rst +++ b/doc/ext-pake/appendix/history.rst @@ -27,6 +27,8 @@ API changes * Replaced :code:`psa_pake_get_implicit_key()` with :code:`psa_pake_get_shared_key()`. This returns a new key containing the shared secret, instead of injecting the shared secret into a key derivation operation. * Added a key confirmation attribute to the PAKE cipher suite. This indicates whether the application wants to extract the shared secret before, or after, key confirmation. See :secref:`pake-cipher-suite`. +* Added asymmetric key types for SPAKE2+ registration, `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()` and `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`. Documented the import/export public key format and key derivation process for these keys. + Clarifications ~~~~~~~~~~~~~~ diff --git a/doc/ext-pake/appendix/specdef_values.rst b/doc/ext-pake/appendix/specdef_values.rst index 470d9e41..23c75b74 100644 --- a/doc/ext-pake/appendix/specdef_values.rst +++ b/doc/ext-pake/appendix/specdef_values.rst @@ -28,3 +28,21 @@ The examples here provide correct results for the valid inputs defined by each A ((pake_bits & 0xFFFF) != pake_bits) ? 0 : \ ((psa_pake_primitive_t) (((pake_type) << 24 | \ (pake_family) << 16) | (pake_bits))) + + #define PSA_KEY_TYPE_SPAKE2P_GET_FAMILY(type) \ + ((psa_ecc_family_t) ((type) & 0x00ff)) + + #define PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(curve) \ + ((psa_key_type_t) (0x7400 | (curve))) + + #define PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY(curve) \ + ((psa_key_type_t) (0x4400 | (curve))) + + #define PSA_KEY_TYPE_IS_SPAKE2P(type) \ + ((PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type) & 0xff00) == 0x4400) + + #define PSA_KEY_TYPE_IS_SPAKE2P_KEY_PAIR(type) \ + (((type) & 0xff00) == 0x7400) + + #define PSA_KEY_TYPE_IS_SPAKE2P_PUBLIC_KEY(type) \ + (((type) & 0xff00) == 0x4400) diff --git a/doc/ext-pake/figure/spake2p_key.json b/doc/ext-pake/figure/spake2p_key.json new file mode 100644 index 00000000..c9c6716f --- /dev/null +++ b/doc/ext-pake/figure/spake2p_key.json @@ -0,0 +1,18 @@ +{ + "reg": [ + { "name": "P", "bits": 1 }, + { "name": "ECC-FAMILY", "bits": 7 }, + { "name": "4", "bits": 4 }, + { "name": "PAIR", "bits": 2 }, + { "name": "1", "bits": 1 }, + { "name": "0", "bits": 1 } + ], + "options": { + "lanes": 1, + "fontfamily": "lato", + "fontsize": 11, + "bits": 16, + "vspace": 52, + "hspace": 300 + } +} diff --git a/doc/ext-pake/figure/spake2p_key.json.license b/doc/ext-pake/figure/spake2p_key.json.license new file mode 100644 index 00000000..9a9052df --- /dev/null +++ b/doc/ext-pake/figure/spake2p_key.json.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/ext-pake/figure/spake2p_key.pdf b/doc/ext-pake/figure/spake2p_key.pdf new file mode 100644 index 0000000000000000000000000000000000000000..550b7a21054cc83b21ec47fb275ede8dddbf90a3 GIT binary patch literal 7438 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 WL10MKnRi7omrB6KrgT%8;Qs*+A%xZd literal 0 HcmV?d00001 diff --git a/doc/ext-pake/figure/spake2p_key.pdf.license b/doc/ext-pake/figure/spake2p_key.pdf.license new file mode 100644 index 00000000..9a9052df --- /dev/null +++ b/doc/ext-pake/figure/spake2p_key.pdf.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/ext-pake/figure/spake2p_key.svg b/doc/ext-pake/figure/spake2p_key.svg new file mode 100644 index 00000000..1dfac6a6 --- /dev/null +++ b/doc/ext-pake/figure/spake2p_key.svg @@ -0,0 +1,2 @@ + +01781112131415PECC-FAMILY4PAIR10 \ No newline at end of file diff --git a/doc/ext-pake/figure/spake2p_key.svg.license b/doc/ext-pake/figure/spake2p_key.svg.license new file mode 100644 index 00000000..9a9052df --- /dev/null +++ b/doc/ext-pake/figure/spake2p_key.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license From 4a6f0af7cc0f52699422443eb779a52366afe2f5 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Fri, 24 Nov 2023 18:00:39 +0000 Subject: [PATCH 02/11] Resolve some open issues * Add macros to crack a PAKE primitive value * Decide on using USAGE_DERIVE for SPAKE2+ keys * Fix typos * Update header file --- doc/ext-pake/api.db/psa/crypto-pake.h | 15 ++++++ doc/ext-pake/api/pake.rst | 69 +++++++++++++++++++----- doc/ext-pake/appendix/specdef_values.rst | 9 ++++ 3 files changed, 80 insertions(+), 13 deletions(-) diff --git a/doc/ext-pake/api.db/psa/crypto-pake.h b/doc/ext-pake/api.db/psa/crypto-pake.h index 21a84e9c..83f10ca7 100644 --- a/doc/ext-pake/api.db/psa/crypto-pake.h +++ b/doc/ext-pake/api.db/psa/crypto-pake.h @@ -11,6 +11,15 @@ typedef uint8_t psa_pake_step_t; #define PSA_ALG_IS_JPAKE(alg) /* specification-defined value */ #define PSA_ALG_IS_PAKE(alg) /* specification-defined value */ #define PSA_ALG_JPAKE(hash_alg) /* specification-defined value */ +#define PSA_KEY_TYPE_IS_SPAKE2P(type) /* specification-defined value */ +#define PSA_KEY_TYPE_IS_SPAKE2P_KEY_PAIR(type) \ + /* specification-defined value */ +#define PSA_KEY_TYPE_IS_SPAKE2P_PUBLIC_KEY(type) \ + /* specification-defined value */ +#define PSA_KEY_TYPE_SPAKE2P_GET_FAMILY(type) /* specification-defined value */ +#define PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(curve) /* specification-defined value */ +#define PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY(curve) \ + /* specification-defined value */ #define PSA_PAKE_CIPHER_SUITE_INIT /* implementation-defined value */ #define PSA_PAKE_CONFIRMED_KEY 0 #define PSA_PAKE_INPUT_MAX_SIZE /* implementation-defined value */ @@ -22,6 +31,12 @@ typedef uint8_t psa_pake_step_t; /* implementation-defined value */ #define PSA_PAKE_PRIMITIVE(pake_type, pake_family, pake_bits) \ /* specification-defined value */ +#define PSA_PAKE_PRIMITIVE_GET_BITS(pake_primitive) \ + /* specification-defined value */ +#define PSA_PAKE_PRIMITIVE_GET_FAMILY(pake_primitive) \ + /* specification-defined value */ +#define PSA_PAKE_PRIMITIVE_GET_TYPE(pake_primitive) \ + /* specification-defined value */ #define PSA_PAKE_PRIMITIVE_TYPE_DH ((psa_pake_primitive_type_t)0x02) #define PSA_PAKE_PRIMITIVE_TYPE_ECC ((psa_pake_primitive_type_t)0x01) #define PSA_PAKE_ROLE_CLIENT ((psa_pake_role_t)0x11) diff --git a/doc/ext-pake/api/pake.rst b/doc/ext-pake/api/pake.rst index 27e77912..00f8d413 100644 --- a/doc/ext-pake/api/pake.rst +++ b/doc/ext-pake/api/pake.rst @@ -150,11 +150,11 @@ The SPAKE2+ protocol consists of three phases: 2. Authenticated key exchange 3. Key confirmation -The registration phase can be carried out immediately prior to the other phases, or can be carried out offline, and the result of the registration phase transferred to the participants in the protocol for later online authentication. +The registration phase can be carried out immediately before the other phases, or can be carried out offline, and the result of the registration phase transferred to the participants in the protocol for later online authentication. -The |API| uses an asymmetric key-pair, and public-key, to store the output of the registration, for input to the authentication protocol. The registration is carried out using a key derivation operation, and the key exchange and confirmation is carried out using a PAKE operation. For a SPAKE2+ PAKE operation, the prover, or client, role requires a SPAKE2+ key-pair, while the verifier, or server, role can use either a SPAKE2+ key-pair or SPAKE2+ public key. +The |API| uses an asymmetric key-pair, and a public-key, to store the output of the registration for input to the authentication protocol. The registration is carried out using a key derivation operation, and the key exchange and confirmation is carried out using a PAKE operation. For a SPAKE2+ PAKE operation, the prover, or client, role requires a SPAKE2+ key-pair, while the verifier, or server, role can use either a SPAKE2+ key-pair or SPAKE2+ public key. -The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is parameterized by a specific Elliptic curve. The Elliptic curve families are used to parameterize the key type, and the key size selects the specific curve. :issue:`Is this overkill? - RFC9383 only specifies cipher-suites that use the SECP R1 curves and the Edwards curves, we could have a custom set of families` +The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is parameterized by a specific Elliptic curve. The Elliptic curve families are used to parameterize the key type, and the key size selects the specific curve. .. macro:: PSA_KEY_TYPE_SPAKE2P_KEY_PAIR :definition: /* specification-defined value */ @@ -169,7 +169,7 @@ The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is .. subsection:: Compatible algorithms - SPAEK2+ key pairs can be used in SPAKE2+ PAKE algorithms. + SPAKE2+ key pairs can be used in SPAKE2+ PAKE algorithms. .. macro:: PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY :definition: /* specification-defined value */ @@ -184,7 +184,7 @@ The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is .. subsection:: Compatible algorithms - SPAEK2+ key pairs can be used in SPAKE2+ PAKE algorithms. + SPAKE2+ key pairs can be used in SPAKE2+ PAKE algorithms. .. macro:: PSA_KEY_TYPE_IS_SPAKE2P :definition: /* specification-defined value */ @@ -249,11 +249,9 @@ For example, after setting up the PBKDF2 operation, the following process will d .. code-block:: xref - psa_set_key_usage_flags(&att, PSA_KEY_USAGE_????); + psa_set_key_usage_flags(&att, PSA_KEY_USAGE_DERIVE); psa_set_key_algorithm(&att, PSA_ALG_SPAKE2P); - :issue:`Do we need a new usage flag for augmented PAKEs? For example PSA_KEY_USAGE_PROVE/VERIFY. Or do we just use PSA_KEY_USAGE_DERIVE as specified by psa_pake_set_password_key()?` - #. Derive the key: .. code-block:: xref @@ -466,6 +464,8 @@ Many PAKE algorithms are designed to allow different cryptographic primitives to The cryptographic primitive for a PAKE operation is specified using a `psa_pake_primitive_t` value, which can be constructed using the `PSA_PAKE_PRIMITIVE()` macro, or can be provided as a numerical constant value. +The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. These can be used to set key attributes for keys used in PAKE algorithms. + A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_pake_cipher_suite_t`, which fully specifies the PAKE operation to be carried out. @@ -486,7 +486,7 @@ A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_ Implementation-defined primitive type. Implementations that define additional primitive types must use an encoding with bit 7 set. - For specification-defined primitive types, see the documentation of individual ``PSA_PAKE_PRIMITIVE_TYPE_XXX`` constants. + For specification-defined primitive types, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. .. macro:: PSA_PAKE_PRIMITIVE_TYPE_ECC :definition: ((psa_pake_primitive_type_t)0x01) @@ -529,7 +529,7 @@ A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_ .. summary:: Encoding of the family of the primitive associated with the PAKE. - For more information see the documentation of individual ``PSA_PAKE_PRIMITIVE_TYPE_XXX`` constants. + For more information on the family values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. .. typedef:: uint32_t psa_pake_primitive_t @@ -553,16 +553,59 @@ A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_ .. param:: pake_family The family of the primitive. The type and interpretation of this parameter depends on ``pake_type``. - For more information, consult the documentation of individual `psa_pake_primitive_type_t` constants. + For more information, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. .. param:: pake_bits The bit-size of the primitive: a value of type ``size_t``. - The interpretation of this parameter depends on ``family``. - For more information, consult the documentation of individual `psa_pake_primitive_type_t` constants. + The interpretation of this parameter depends on ``pake_type`` and ``family``. + For more information, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. .. return:: psa_pake_primitive_t The constructed primitive value. Return ``0`` if the requested primitive can't be encoded as `psa_pake_primitive_t`. + A PAKE primitive value is used to specify a PAKE operation, as part of a PAKE cipher suite. + + The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. + +.. macro:: PSA_PAKE_PRIMITIVE_GET_TYPE + :definition: /* specification-defined value */ + + .. summary:: + Extract the PAKE primitive type from a PAKE primitive. + + .. param:: pake_primitive + A PAKE primitive: a value of type `psa_pake_primitive_t`. + + .. return:: psa_pake_primitive_type_t + The PAKE primitive type, if ``pake_primitive`` is a supported PAKE primitive. Unspecified if ``pake_primitive`` is not a supported PAKE primitive. + +.. macro:: PSA_PAKE_PRIMITIVE_GET_FAMILY + :definition: /* specification-defined value */ + + .. summary:: + Extract the family from a PAKE primitive. + + .. param:: pake_primitive + A PAKE primitive: a value of type `psa_pake_primitive_t`. + + .. return:: psa_pake_family_t + The PAKE primitive family, if ``pake_primitive`` is a supported PAKE primitive. Unspecified if ``pake_primitive`` is not a supported PAKE primitive. + + For more information on the family values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. + +.. macro:: PSA_PAKE_PRIMITIVE_GET_BITS + :definition: /* specification-defined value */ + + .. summary:: + Extract the bit-size from a PAKE primitive. + + .. param:: pake_primitive + A PAKE primitive: a value of type `psa_pake_primitive_t`. + + .. return:: size_t + The PAKE primitive bit-size, if ``pake_primitive`` is a supported PAKE primitive. Unspecified if ``pake_primitive`` is not a supported PAKE primitive. + + For more information on the bit-size values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. .. _pake-cipher-suite: diff --git a/doc/ext-pake/appendix/specdef_values.rst b/doc/ext-pake/appendix/specdef_values.rst index 23c75b74..689077ee 100644 --- a/doc/ext-pake/appendix/specdef_values.rst +++ b/doc/ext-pake/appendix/specdef_values.rst @@ -29,6 +29,15 @@ The examples here provide correct results for the valid inputs defined by each A ((psa_pake_primitive_t) (((pake_type) << 24 | \ (pake_family) << 16) | (pake_bits))) + #define PSA_PAKE_PRIMITIVE_GET_BITS(pake_primitive) \ + ((size_t)(pake_primitive & 0xFFFF)) + + #define PSA_PAKE_PRIMITIVE_GET_FAMILY(pake_primitive) \ + ((psa_pake_family_t)((pake_primitive >> 16) & 0xFF)) + + #define PSA_PAKE_PRIMITIVE_GET_TYPE(pake_primitive) \ + ((psa_pake_primitive_type_t)((pake_primitive >> 24) & 0xFF)) + #define PSA_KEY_TYPE_SPAKE2P_GET_FAMILY(type) \ ((psa_ecc_family_t) ((type) & 0x00ff)) From 999f3b2afcdc97ac9da3ae7cdebcaad72fc2ae6b Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Wed, 29 Nov 2023 16:32:00 +0000 Subject: [PATCH 03/11] Clarify SPAKE2+ key formats and derivation * Constrain key derivation to SPAKE2+ key pairs * Specify the length of data extracted from the KDF during derivation * Use a list format to define the exported key format --- doc/ext-pake/api/pake.rst | 68 +++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/doc/ext-pake/api/pake.rst b/doc/ext-pake/api/pake.rst index 00f8d413..730b696e 100644 --- a/doc/ext-pake/api/pake.rst +++ b/doc/ext-pake/api/pake.rst @@ -106,22 +106,16 @@ Key formats A SPAKE2+ public key can be exported and imported, to enable use cases that require offline registration. -The public key consists of the two values w0 and L, which result from the SPAKE2+ registration phase. w0 is a scalar in the same range as a private Elliptic curve key from the group used as the SPAKE2+ primitive group. L is a point on the curve, similar to a public key from the same group. +The public key consists of the two values w0 and L, which result from the SPAKE2+ registration phase. w0 is a scalar in the same range as a private Elliptic curve key from the group used as the SPAKE2+ primitive group. L is a point on the curve, similar to a public key from the same group, corresponding to the w1 value in the key pair. -The default format for the SPAKE2+ public key is the concatenation of the formatted values for w0 and L, using the the standard formats for Elliptic curve keys. For example, for SPAKE2+ over P-256 (secp256r1), the output from :code:`psa_export_public_key()` would be: +For the |API|, the default format for a SPAKE2+ public key is the concatenation of the formatted values for w0 and L, using the standard formats for Elliptic curve keys used by the |API|. For example, for SPAKE2+ over P-256 (secp256r1), the output from :code:`psa_export_public_key()` would be the concatenation of: - [ w0 ]\ :sub:`32` || 0x04 || [ x\ :sub:`L` ]\ :sub:`32` || [ y\ :sub:`L` ]\ :sub:`32` +* The 32-byte formatted value of the P-256 private key w0. This is a big-endian encoding of the integer w0. +* The 65-byte formatted value of the P-256 public key L. This is itself a concatenation of: -Where [ v ]\ :sub:`n` is an n-byte, big-endian encoding of the integer value v. - -.. todo:: - In this example, how does using a 'concatenation of elements' depiction compare to the 'bullet list of elements' approach used in the Weierstrass public key format in §9.6.4? - -.. todo:: - In this example, how does the short-hand notation --- [ v ]\ :sub:`n` --- compare with the text description approach used in the Weierstrass public key format in §9.6.4, or the function-based (e.g. ``I2OSP()``) approach used in texts such as SEC1? - -.. todo:: - Would it be better to provide an explicit definition for all of the elliptic curves over which SPAKE2+ is defined, rather than just provide a single example? + - The byte ``0x04``. + - The 32-byte big-endian encoding of the x-coordinate of L. + - The 32-byte big-endian encoding of the y-coordinate of L. .. todo:: It might also be time to decide on how to style/format pseudo-mathematical content of the specification. Presently there is a arbitrary mixture of ``monospace code/LaTeX-source-style material a^b = 1, F_q`` (as typical in IETF RFCs) and *emphasized* or regular font .rst material a\ :sup:`b` = 1, *F*\ :sub:`q` (seen in NIST publications, and some IETF RFCs). But we also have the ability to use the ``:math:`` role to :math:`\text{render like LaTeX: } a^b=1, \mathbb{F}_q` (used in SECG and some NIST publications). @@ -162,11 +156,16 @@ The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is .. summary:: SPAKE2+ key pair: both the prover and verifier key. - The size of a SPAKE2+ key is the size associated with the Elliptic curve group, that is, ceil(log2(q)) for a curve over a field F\ :sub:`q`. See the documentation of each Elliptic curve family for details. - .. param:: curve A value of type :code:`psa_ecc_family_t` that identifies the Elliptic curve family to be used. + The size of a SPAKE2+ key is the size associated with the Elliptic curve group, that is, ceil(log2(q)) for a curve over a field F\ :sub:`q`. See the documentation of each Elliptic curve family for details. + + To construct a SPAKE2+ key pair, it must be output from a key derivation operation. See :secref:`spake2p-key-derivation`. + + The corresponding public key can be exported using :code:`psa_export_public_key()`. See also `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`. + + .. subsection:: Compatible algorithms SPAKE2+ key pairs can be used in SPAKE2+ PAKE algorithms. @@ -182,9 +181,11 @@ The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is The size of an SPAKE2+ public key is the same as the corresponding private key. See `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()` and the documentation of each Elliptic curve family for details. + To construct a SPAKE2+ public key, it must be imported. + .. subsection:: Compatible algorithms - SPAKE2+ key pairs can be used in SPAKE2+ PAKE algorithms. + SPAKE2+ public keys can be used in SPAKE2+ PAKE algorithms. .. macro:: PSA_KEY_TYPE_IS_SPAKE2P :definition: /* specification-defined value */ @@ -225,12 +226,14 @@ The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is .. return:: psa_ecc_family_t The elliptic curve family id, if ``type`` is a supported SPAKE2+ key. Unspecified if ``type`` is not a supported SPAKE2+ key. +.. _spake2p-key-derivation: + Key derivation of SPAKE2+ keys ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The SPAKE2+ key types can be output from a key derivation using :code:`psa_key_derivation_output_key()`. The SPAKE2+ protocol recommends that a key-stretching kdf, such as PBKDF2, is used to hash the SPAKE2+ password. See RFC 9383 for details. +A SPAKE2+ key pair can be output from a key derivation using :code:`psa_key_derivation_output_key()`. The SPAKE2+ protocol recommends that a key-stretching key-derivation function, such as PBKDF2, is used to hash the SPAKE2+ password. See RFC 9383 for details. -For example, after setting up the PBKDF2 operation, the following process will derive the SPAKE2+ key pair for use with the P-256 Elliptic curve group :issue:`(This example may be more than necessary in the specification?)`: +For example, after setting up the PBKDF2 operation, the following process will derive the SPAKE2+ key pair for use with the P-256 Elliptic curve group :issue:`(This example can be combined with an illustration of the registration flow in the final specification)`: 1. Allocate and initialize a key attributes object: @@ -256,20 +259,37 @@ For example, after setting up the PBKDF2 operation, the following process will d .. code-block:: xref - psa_key_id_t sp2_key; - psa_key_derivation_output_key(&att, &kdf_op, &sp2_key); + psa_key_id_t sp2p_key; + psa_key_derivation_output_key(&att, &kdf_op, &sp2p_key); The key derivation process in :code:`psa_key_derivation_output_key()` follows the recommendations for the registration process in RFC 9383, and matches the specification of this process in the Matter specification. For the |API|: * The derivation of SPAKE2+ keys extracts ceil(log2(p)/8) + 8 bytes from the PBKDF for each of w0s and w1s, where p is the prime factor of the order of the elliptic curve group. + The following sizes are used for extracting w0s and w1s, depending on the elliptic curve: + + .. csv-table:: + :header-rows: 1 + :widths: auto + :align: left + + Elliptic curve, "Size of w0s and w1s, in bytes" + P-256, 40 + P-384, 56 + P-521, 74 + edwards25519, 40 + edwards448, 64 + + :issue:`I think these values are correct?` + * The calculation of w0, w1, and L then proceeds as described in the RFC. -* A SPAKE2+ key-pair is the pair (w0, w1). -* A SPAKE2+ public key is the pair (w0, L). -.. todo:: - Would a table of required w0s/w1s lengths for each of the supported SPAKE2+ elliptic curve groups be useful here? + .. admonition:: Implementation note + + The values of w0 and w1 are required as part of the SPAKE2+ key pair. + + It is :scterm:`implementation defined` whether L is computed during key derivation, and stored as part of the key pair; or only computed when required from the key pair. .. _pake-algorithms: From 37902048dee00eda564dac8550c8a8d9d1ad718d Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Wed, 29 Nov 2023 16:40:18 +0000 Subject: [PATCH 04/11] Use :math: role and directive for formulae --- doc/ext-pake/api/pake.rst | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/doc/ext-pake/api/pake.rst b/doc/ext-pake/api/pake.rst index 730b696e..66781879 100644 --- a/doc/ext-pake/api/pake.rst +++ b/doc/ext-pake/api/pake.rst @@ -106,19 +106,16 @@ Key formats A SPAKE2+ public key can be exported and imported, to enable use cases that require offline registration. -The public key consists of the two values w0 and L, which result from the SPAKE2+ registration phase. w0 is a scalar in the same range as a private Elliptic curve key from the group used as the SPAKE2+ primitive group. L is a point on the curve, similar to a public key from the same group, corresponding to the w1 value in the key pair. +The public key consists of the two values :math:`w0` and :math:`L`, which result from the SPAKE2+ registration phase. :math:`w0` is a scalar in the same range as a private Elliptic curve key from the group used as the SPAKE2+ primitive group. :math:`L` is a point on the curve, similar to a public key from the same group, corresponding to the :math:`w1` value in the key pair. -For the |API|, the default format for a SPAKE2+ public key is the concatenation of the formatted values for w0 and L, using the standard formats for Elliptic curve keys used by the |API|. For example, for SPAKE2+ over P-256 (secp256r1), the output from :code:`psa_export_public_key()` would be the concatenation of: +For the |API|, the default format for a SPAKE2+ public key is the concatenation of the formatted values for :math:`w0` and :math:`L`, using the standard formats for Elliptic curve keys used by the |API|. For example, for SPAKE2+ over P-256 (secp256r1), the output from :code:`psa_export_public_key()` would be the concatenation of: -* The 32-byte formatted value of the P-256 private key w0. This is a big-endian encoding of the integer w0. -* The 65-byte formatted value of the P-256 public key L. This is itself a concatenation of: +* The 32-byte formatted value of the P-256 private key :math:`w0`. This is a big-endian encoding of the integer :math:`w0`. +* The 65-byte formatted value of the P-256 public key :math:`L`. This is itself a concatenation of: - The byte ``0x04``. - - The 32-byte big-endian encoding of the x-coordinate of L. - - The 32-byte big-endian encoding of the y-coordinate of L. - -.. todo:: - It might also be time to decide on how to style/format pseudo-mathematical content of the specification. Presently there is a arbitrary mixture of ``monospace code/LaTeX-source-style material a^b = 1, F_q`` (as typical in IETF RFCs) and *emphasized* or regular font .rst material a\ :sup:`b` = 1, *F*\ :sub:`q` (seen in NIST publications, and some IETF RFCs). But we also have the ability to use the ``:math:`` role to :math:`\text{render like LaTeX: } a^b=1, \mathbb{F}_q` (used in SECG and some NIST publications). + - The 32-byte big-endian encoding of the x-coordinate of :math:`L`. + - The 32-byte big-endian encoding of the y-coordinate of :math:`L`. Changes and additions to the Programming API -------------------------------------------- @@ -159,7 +156,7 @@ The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is .. param:: curve A value of type :code:`psa_ecc_family_t` that identifies the Elliptic curve family to be used. - The size of a SPAKE2+ key is the size associated with the Elliptic curve group, that is, ceil(log2(q)) for a curve over a field F\ :sub:`q`. See the documentation of each Elliptic curve family for details. + The size of a SPAKE2+ key is the size associated with the Elliptic curve group, that is, :math:`\lceil{log_2(q)}\rceil` for a curve over a field :math:`\mathbb{F}_q`. See the documentation of each Elliptic curve family for details. To construct a SPAKE2+ key pair, it must be output from a key derivation operation. See :secref:`spake2p-key-derivation`. @@ -266,15 +263,15 @@ The key derivation process in :code:`psa_key_derivation_output_key()` follows th For the |API|: -* The derivation of SPAKE2+ keys extracts ceil(log2(p)/8) + 8 bytes from the PBKDF for each of w0s and w1s, where p is the prime factor of the order of the elliptic curve group. - The following sizes are used for extracting w0s and w1s, depending on the elliptic curve: +* The derivation of SPAKE2+ keys extracts :math:`\lceil{log_2(p)/8}\rceil+8` bytes from the PBKDF for each of :math:`w0s` and :math:`w1s`, where :math:`p` is the prime factor of the order of the elliptic curve group. + The following sizes are used for extracting :math:`w0s` and :math:`w1s`, depending on the elliptic curve: .. csv-table:: :header-rows: 1 :widths: auto :align: left - Elliptic curve, "Size of w0s and w1s, in bytes" + Elliptic curve, "Size of :math:`w0s` and :math:`w1s`, in bytes" P-256, 40 P-384, 56 P-521, 74 @@ -283,13 +280,13 @@ For the |API|: :issue:`I think these values are correct?` -* The calculation of w0, w1, and L then proceeds as described in the RFC. +* The calculation of :math:`w0`, :math:`w1`, and :math:`L` then proceeds as described in the RFC. .. admonition:: Implementation note - The values of w0 and w1 are required as part of the SPAKE2+ key pair. + The values of :math:`w0` and :math:`w1` are required as part of the SPAKE2+ key pair. - It is :scterm:`implementation defined` whether L is computed during key derivation, and stored as part of the key pair; or only computed when required from the key pair. + It is :scterm:`implementation defined` whether :math:`L` is computed during key derivation, and stored as part of the key pair; or only computed when required from the key pair. .. _pake-algorithms: From 701c9ecabbb234e3b83b606909d3dbad1c6403d2 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Wed, 15 Nov 2023 21:45:21 +0000 Subject: [PATCH 05/11] Added SPAKE2+ algorithms * Supporting both RFC 9393 and Matter * New APIs: - `PSA_ALG_SPAKE2P_HMAC()` - `PSA_ALG_SPAKE2P_CMAC()` - `PSA_ALG_SPAKE2P_MATTER` - `PSA_ALG_IS_SPAKE2P()` - `PSA_ALG_IS_SPAKE2P_HMAC()` - `PSA_ALG_IS_SPAKE2P_CMAC()` --- doc/ext-pake/api.db/psa/crypto-pake.h | 6 + doc/ext-pake/api/pake.rst | 404 +++++++++++++++++- doc/ext-pake/appendix/history.rst | 9 + doc/ext-pake/appendix/specdef_values.rst | 15 + doc/ext-pake/figure/spake2plus-reg.pdf | Bin 0 -> 41372 bytes .../figure/spake2plus-reg.pdf.license | 2 + doc/ext-pake/figure/spake2plus-reg.puml | 41 ++ doc/ext-pake/figure/spake2plus-reg.svg | 1 + .../figure/spake2plus-reg.svg.license | 2 + doc/ext-pake/figure/spake2plus.pdf | Bin 0 -> 39114 bytes doc/ext-pake/figure/spake2plus.pdf.license | 2 + doc/ext-pake/figure/spake2plus.puml | 54 +++ doc/ext-pake/figure/spake2plus.svg | 1 + doc/ext-pake/figure/spake2plus.svg.license | 2 + doc/ext-pake/references | 30 +- 15 files changed, 550 insertions(+), 19 deletions(-) create mode 100644 doc/ext-pake/figure/spake2plus-reg.pdf create mode 100644 doc/ext-pake/figure/spake2plus-reg.pdf.license create mode 100644 doc/ext-pake/figure/spake2plus-reg.puml create mode 100644 doc/ext-pake/figure/spake2plus-reg.svg create mode 100644 doc/ext-pake/figure/spake2plus-reg.svg.license create mode 100644 doc/ext-pake/figure/spake2plus.pdf create mode 100644 doc/ext-pake/figure/spake2plus.pdf.license create mode 100644 doc/ext-pake/figure/spake2plus.puml create mode 100644 doc/ext-pake/figure/spake2plus.svg create mode 100644 doc/ext-pake/figure/spake2plus.svg.license diff --git a/doc/ext-pake/api.db/psa/crypto-pake.h b/doc/ext-pake/api.db/psa/crypto-pake.h index 83f10ca7..f15dd477 100644 --- a/doc/ext-pake/api.db/psa/crypto-pake.h +++ b/doc/ext-pake/api.db/psa/crypto-pake.h @@ -10,7 +10,13 @@ typedef uint8_t psa_pake_role_t; typedef uint8_t psa_pake_step_t; #define PSA_ALG_IS_JPAKE(alg) /* specification-defined value */ #define PSA_ALG_IS_PAKE(alg) /* specification-defined value */ +#define PSA_ALG_IS_SPAKE2P(alg) /* specification-defined value */ +#define PSA_ALG_IS_SPAKE2P_CMAC(alg) /* specification-defined value */ +#define PSA_ALG_IS_SPAKE2P_HMAC(alg) /* specification-defined value */ #define PSA_ALG_JPAKE(hash_alg) /* specification-defined value */ +#define PSA_ALG_SPAKE2P_CMAC(hash_alg) /* specification-defined value */ +#define PSA_ALG_SPAKE2P_HMAC(hash_alg) /* specification-defined value */ +#define PSA_ALG_SPAKE2P_MATTER ((psa_algoirithm_t)0x0A000609) #define PSA_KEY_TYPE_IS_SPAKE2P(type) /* specification-defined value */ #define PSA_KEY_TYPE_IS_SPAKE2P_KEY_PAIR(type) \ /* specification-defined value */ diff --git a/doc/ext-pake/api/pake.rst b/doc/ext-pake/api/pake.rst index 66781879..b0805093 100644 --- a/doc/ext-pake/api/pake.rst +++ b/doc/ext-pake/api/pake.rst @@ -39,10 +39,10 @@ The algorithm identifier for PAKE algorithms defined in this specification are e The defined values for PAKE-TYPE are shown in :numref:`table-pake-type`. -The permitted values of HASH-TYPE depend on the specific KDF algorithm. +The permitted values of HASH-TYPE depend on the specific PAKE algorithm. .. - The permitted values of HASH-TYPE (see :numref:`table-hash-type`) depend on the specific KDF algorithm. + The permitted values of HASH-TYPE (see :numref:`table-hash-type`) depend on the specific PAKE algorithm. .. csv-table:: PAKE algorithm sub-type values :name: table-pake-type @@ -52,6 +52,9 @@ The permitted values of HASH-TYPE depend on the specific KDF algorithm. PAKE algorithm, PAKE-TYPE, Algorithm identifier, Algorithm value J-PAKE, ``0x01``, :code:`PSA_ALG_JPAKE(hash)`, ``0x0A0001hh`` :sup:`a` + SPAKE2+ with HMAC, ``0x04``, :code:`PSA_ALG_SPAKE2P_HMAC(hash)`, ``0x0A0004hh`` :sup:`a` + SPAKE2+ with CMAC, ``0x05``, :code:`PSA_ALG_SPAKE2P_CMAC(hash)`, ``0x0A0005hh`` :sup:`a` + SPAKE2+ for Matter, ``0x06``, :code:`PSA_ALG_SPAKE2P_MATTER`, ``0x0A000609`` a. ``hh`` is the HASH-TYPE for the hash algorithm, ``hash``, used to construct the key derivation algorithm. @@ -162,10 +165,11 @@ The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is The corresponding public key can be exported using :code:`psa_export_public_key()`. See also `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`. - .. subsection:: Compatible algorithms - SPAKE2+ key pairs can be used in SPAKE2+ PAKE algorithms. + | `PSA_ALG_SPAKE2P_HMAC` + | `PSA_ALG_SPAKE2P_CMAC` + | `PSA_ALG_SPAKE2P_MATTER` .. macro:: PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY :definition: /* specification-defined value */ @@ -182,7 +186,9 @@ The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is .. subsection:: Compatible algorithms - SPAKE2+ public keys can be used in SPAKE2+ PAKE algorithms. + | `PSA_ALG_SPAKE2P_HMAC` (verification only) + | `PSA_ALG_SPAKE2P_CMAC` (verification only) + | `PSA_ALG_SPAKE2P_MATTER` (verification only) .. macro:: PSA_KEY_TYPE_IS_SPAKE2P :definition: /* specification-defined value */ @@ -225,8 +231,8 @@ The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is .. _spake2p-key-derivation: -Key derivation of SPAKE2+ keys -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Derivation of SPAKE2+ keys +^^^^^^^^^^^^^^^^^^^^^^^^^^ A SPAKE2+ key pair can be output from a key derivation using :code:`psa_key_derivation_output_key()`. The SPAKE2+ protocol recommends that a key-stretching key-derivation function, such as PBKDF2, is used to hash the SPAKE2+ password. See RFC 9383 for details. @@ -342,12 +348,18 @@ PAKE algorithms More information on selecting a specific Elliptic curve or Diffie-Hellman field is provided with the `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH` constants. + The PAKE operation for J-PAKE requires a key of type type :code:`PSA_KEY_TYPE_PASSWORD` or :code:`PSA_KEY_TYPE_PASSWORD_HASH`. + The same key value must be provided to the PAKE operation in both participants. + + The key can be the password text itself, in an agreed character encoding, or some value derived from the password as required by a higher level protocol. + For low-entropy passwords, it is recommended that a key-stretching derivation algorithm, such as PBKDF2, is used, and the resulting password hash is used as the PAKE operation key. + The J-PAKE operation follows the protocol shown in :numref:`fig-jpake`. .. figure:: /figure/j-pake.* :name: fig-jpake - The J-PAKE protocol. + The J-PAKE protocol The variable names *x1*, *g1*, and so on, are taken from the finite field implementation of J-PAKE in :RFC:`8236#2`. Details of the computation for the key shares and zero-knowledge proofs are in :RFC:`8236` and :RFC:`8235`. @@ -362,7 +374,7 @@ PAKE algorithms The following steps demonstrate the application code for 'User' in :numref:`fig-jpake`. The input and output steps must be carried out in exactly the same sequence as shown. - 1. To prepare a J-Pake operation, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: + 1. To prepare a J-PAKE operation, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: .. code-block:: xref @@ -470,6 +482,325 @@ PAKE algorithms | :code:`PSA_KEY_TYPE_PASSWORD` | :code:`PSA_KEY_TYPE_PASSWORD_HASH` +.. macro:: PSA_ALG_SPAKE2P_HMAC + :definition: /* specification-defined value */ + + .. summary:: + Macro to build the SPAKE2+ algorithm, using HMAC-based key confirmation. + + .. param:: hash_alg + A hash algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_HASH(hash_alg)` is true. + + .. return:: + A SPAKE2+ algorithm, using HMAC for key confirmation, parameterized by a specific hash. + + Unspecified if ``hash_alg`` is not a supported hash algorithm. + + This is SPAKE2+, as defined by :RFC-title:`9383`, instantiated with the following parameters: + + * An elliptic curve group. + * A cryptographic hash function, ``hash_alg``. + * Key derivation function HKDF, using the same hash function, ``hash_alg``. + * Keyed MAC function HMAC, using the same hash function, ``hash_alg``. + + For SPAKE2+, valid combinations of elliptic curve PAKE primitives and hash algorithms are described in :rfc:`9383#4`. + + SPAKE2+ includes confirmation of the shared secret key that results from the key exchange. + + To select these parameters and set up the cipher suite, initialize a `psa_pake_cipher_suite_t` object, and call the following functions in any order: + + .. code-block:: xref + + psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; + + psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_SPAKE2P_HMAC(hash)); + psa_pake_cs_set_primitive(&cipher_suite, + PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, family, bits)); + + For more information on selecting a specific Elliptic curve, see `PSA_PAKE_PRIMITIVE_TYPE_ECC`. + + .. _spake2p-flow: + + .. subsection:: SPAKE2+ protocol + + There are two participants in the SPAKE2+ protocol: + + * The *Prover* takes the role of client. It uses the protocol to prove that it knows the secret password, and produce a shared secret. + * The *Verifier* takes the role of server. It uses the protocol to verify the client's proof, and produce a shared secret. + + The PAKE operation for SPAKE2+ only accepts a SPAKE2+ key type: + + * The Prover requires a `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`, on the same elliptic curve specified in the PAKE cipher suite. + * The Verifier requires either a `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`, or a `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`, on the same elliptic curve specified in the PAKE cipher suite. + + These keys are derived from the initial SPAKE2+ password prior to starting the PAKE operation. + It is recommended to use a key-stretching derivation algorithm, for example PBKDF2. + This process can take place immediately before the PAKE operation, or derived at some earlier point and persisted in the key store. + Alternatively, the Verifier can be provisioned with the `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()` for the protocol, by the Prover, or some other agent. + :numref:`fig-spake2p-reg` illustrates some example SPAKE2+ key derivation flows. + + It is recommended that the Verifier stores only the public key, because disclosure of the public key does not enable an attacker to impersonate the Prover. + + .. figure:: /figure/spake2plus-reg.* + :name: fig-spake2p-reg + + Examples of SPAKE2+ key derivation procedures + + The variable names *w0*, *w1*, *L* are taken from the description of SPAKE2+ in :RFC:`9383`. + + Details of the computation for the key derivation values are in :RFC:`9383#3.2`. + + Both participants in SPAKE2+ have an optional identity. + If no identity value is provided, then a zero-length string is used for that identity in the protocol. + If the participants do not supply the same identity values to the protocol, the computed secrets will be different, and key confirmation will fail. + + The SPAKE2+ operation follows the protocol shown in :numref:`fig-spake2p`. + + .. figure:: /figure/spake2plus.* + :name: fig-spake2p + + The SPAKE2+ authentication and key confirmation protocol + + The variable names *w0*, *w1*, *L*, and so on, are taken from the description of SPAKE2+ in :RFC:`9383`. + + Details of the computation for the key shares is in :RFC:`9383#3.3` and confirmation values in :RFC:`9383#3.4`. + + The shared secret that is produced by SPAKE2+ is pseudorandom. + Although it can be used directly as an encryption key, it is recommended to use the shared secret as an input to a key derivation operation to produce additional cryptographic keys. + + The following steps demonstrate the application code for both 'Prover' and 'Verifier' in :numref:`fig-spake2p`. + + **Prover** + To prepare a SPAKE2+ operation for the Prover, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: + + .. code-block:: xref + + psa_pake_operation_t spake2p_p = PSA_PAKE_OPERATION_INIT; + + psa_pake_setup(&spake2p_p, pake_key_p, &cipher_suite); + psa_pake_set_role(&spake2p_p, PSA_PAKE_ROLE_CLIENT); + + The key ``pake_key_p`` is a SPAKE2+ key pair, `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`. + + The key must have the :code:`PSA_KEY_USAGE_??????` usage flag. + + **Prover** + Provide any additional, optional, parameters: + + .. code-block:: xref + + psa_pake_set_user(&spake2p_p, ...); // Prover identity + psa_pake_set_peer(&spake2p_p, ...); // Verifier identity + psa_pake_set_context(&spake2p_p, ...); + + **Verifier** + To prepare a SPAKE2+ operation for the Verifier, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: + + .. code-block:: xref + + psa_pake_operation_t spake2p_v = PSA_PAKE_OPERATION_INIT; + + psa_pake_setup(&spake2p_v, pake_key_v, &cipher_suite); + psa_pake_set_role(&spake2p_v, PSA_PAKE_ROLE_SERVER); + + The key ``pake_key_v`` is a SPAKE2+ key pair, `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`, or public key, `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`. + + The key must have the :code:`PSA_KEY_USAGE_??????` usage flag. + + **Verifier** + Provide any additional, optional, parameters: + + .. code-block:: xref + + psa_pake_set_user(&spake2p_v, ...); // Verifier identity + psa_pake_set_peer(&spake2p_v, ...); // Prover identity + psa_pake_set_context(&spake2p_v, ...); + + After setup, the key exchange flow for SPAKE2+ is as follows: + + **Prover** + To get the key share to send to the Verifier, call: + + .. code-block:: xref + + // Get shareP + psa_pake_output(&spake2p_p, PSA_PAKE_STEP_KEY_SHARE, ...); + + **Verifier** + To provide and validate the Prover key share, call: + + .. code-block:: xref + + // Set shareP + psa_pake_input(&spake2p_v, PSA_PAKE_STEP_KEY_SHARE, ...); + + **Verifier** + To get the Verifier key share and confirmation value to send to the Prover, call: + + .. code-block:: xref + + // Get shareV + psa_pake_output(&spake2p_v, PSA_PAKE_STEP_KEY_SHARE, ...); + // Get confirmV + psa_pake_output(&spake2p_v, PSA_PAKE_STEP_CONFIRM, ...); + + **Prover** + To provide and validate the Verifier key share, and confirm the Verifier key, call: + + .. code-block:: xref + + // Set shareV + psa_pake_input(&spake2p_p, PSA_PAKE_STEP_KEY_SHARE, ...); + // Set confirmV + psa_pake_input(&spake2p_p, PSA_PAKE_STEP_KEY_CONFIRM, ...); + + **Prover** + To get the Prover key confirmation value to send to the Verifier, call: + + .. code-block:: xref + + // Get confirmV + psa_pake_output(&spake2p_p, PSA_PAKE_STEP_CONFIRM, ...); + + **Verifier** + To confirm the Prover key, call: + + .. code-block:: xref + + // Set shareP + psa_pake_input(&spake2p_v, PSA_PAKE_STEP_CONFIRM, ...); + + **Prover** + To use the shared secret, extract it as a key-derivation key. + For example, to extract a derivation key for HKDF-SHA-256: + + .. code-block:: xref + + // Set up the key attributes + psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT; + psa_key_set_type(&att, PSA_KEY_TYPE_DERIVE); + psa_key_set_usage_flags(&att, PSA_KEY_USAGE_DERIVE); + psa_key_set_algorithm(&att, PSA_ALG_HKDF(PSA_ALG_SHA256)); + + // Get K_shared + psa_key_id_t shared_key; + psa_pake_get_shared_key(&spake2p_p, &att, &shared_key); + + **Verifier** + To use the shared secret, extract it as a key-derivation key. + The same key attributes can be used as the Prover: + + .. code-block:: xref + + // Get K_shared + psa_key_id_t shared_key; + psa_pake_get_shared_key(&spake2p_v, &att, &shared_key); + + For more information about the format of the values which are passed for each step, see :secref:`pake-steps`. + + If the validation of a key share fails, then the corresponding call to `psa_pake_input()` for the `PSA_PAKE_STEP_KEY_SHARE` step will return :code:`PSA_ERROR_INVALID_ARGUMENT`. + If the verification of a key confirmation value fails, then the corresponding call to `psa_pake_input()` for the `PSA_PAKE_STEP_CONFIRM` step will return :code:`PSA_ERROR_INVALID_SIGNATURE`. + + .. subsection:: Compatible key types + + | :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR` + | :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY` (verification only) + + +.. macro:: PSA_ALG_SPAKE2P_CMAC + :definition: /* specification-defined value */ + + .. summary:: + Macro to build the SPAKE2+ algorithm, using CMAC-based key confirmation. + + .. param:: hash_alg + A hash algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_HASH(hash_alg)` is true. + + .. return:: + A SPAKE2+ algorithm, using CMAC for key confirmation, parameterized by a specific hash. + + Unspecified if ``hash_alg`` is not a supported hash algorithm. + + This is SPAKE2+, as defined by :RFC-title:`9383`, instantiated with the following parameters: + + * An elliptic curve group. + * A cryptographic hash function, ``hash_alg``. + * Key derivation function HKDF, using the same hash function, ``hash_alg``. + * Keyed MAC function CMAC-AES-128. + + For SPAKE2+, valid combinations of elliptic curve PAKE primitives and hash algorithms for use with CMAC-AES-128 are described in :rfc:`9383#4`. + + SPAKE2+ includes confirmation of the shared secret key that results from the key exchange. + + To select these parameters and set up the cipher suite, initialize a `psa_pake_cipher_suite_t` object, and call the following functions in any order: + + .. code-block:: xref + + psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; + + psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_SPAKE2P_CMAC(hash)); + psa_pake_cs_set_primitive(&cipher_suite, + PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, family, bits)); + + For more information on selecting a specific Elliptic curve, see `PSA_PAKE_PRIMITIVE_TYPE_ECC`. + + The SPAKE2+ protocol flow and usage for `PSA_ALG_SPAKE2P_CMAC()` is the same as for `PSA_ALG_SPAKE2P_HMAC()`. See :secref:`spake2p-flow`. + + .. subsection:: Compatible key types + + | :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR` + | :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY` (verification only) + +.. macro:: PSA_ALG_SPAKE2P_MATTER + :definition: ((psa_algoirithm_t)0x0A000609) + + .. summary:: + The SPAKE2+ algorithm, as used by the Matter v1 specification. + + This is the PAKE algorithm specified as MATTER_PAKE in :cite-title:`MATTER`. This is based on draft-02 of the SPAKE2+ protocol, :cite-title:`SPAKE2P-2`. + + :cite:`MATTER` specifies a single cipher suite, as follows: + + * The NIST P-256 elliptic curve (secp256r1). + * The SHA256 hash function. + * Key derivation function HKDF-SHA256. + * Keyed MAC function HMAC-SHA256. + + SPAKE2+ includes confirmation of the shared secret key that results from the key exchange. + + To set up the cipher suite for `PSA_ALG_SPAKE2P_MATTER`, initialize a `psa_pake_cipher_suite_t` object, and call the following functions in any order: + + .. code-block:: xref + + psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; + + psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_SPAKE2P_MATTER); + psa_pake_cs_set_primitive(&cipher_suite, + PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, + PSA_ECC_FAMILY_SECP_R1, 256)); + + This algorithm is compatible with the SPAKE2+ key types, key derivation, protocol flow, and the API usage described in :secref:`pake-keys` and :secref:`spake2p-flow`. However, the following aspects are different: + + * The key schedule is different. This affects the computation of the shared secret and key confirmation values. + * The protocol inputs and outputs have been renamed between draft-02 and the final RFC, as follows: + + .. csv-table:: + :header-rows: 1 + :widths: auto + :align: left + + RFC 9383, Draft-02 + shareP, pA + shareV, pB + confirmP, cA + confirmV, cB + K_shared, Ke + + .. subsection:: Compatible key types + + | :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR` + | :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY` (verification only) + .. _pake-primitive: @@ -1041,8 +1372,11 @@ Multi-part PAKE operations .. param:: psa_key_id_t password_key Identifier of the key holding the password or a value derived from the password. It must remain valid until the operation terminates. - It must be of type :code:`PSA_KEY_TYPE_PASSWORD` or :code:`PSA_KEY_TYPE_PASSWORD_HASH`. - It must permit the usage :code:`PSA_KEY_USAGE_DERIVE`. + + The valid key types depend on the PAKE algorithm, and participant role. + Refer to the documentation of individual PAKE algorithms for more information, see :secref:`pake-algorithms`. + + The key must permit the usage :code:`PSA_KEY_USAGE_DERIVE`. .. param:: const psa_pake_cipher_suite_t *cipher_suite The cipher suite to use. A PAKE cipher suite fully characterizes a PAKE algorithm, including the PAKE algorithm. @@ -1336,7 +1670,7 @@ Multi-part PAKE operations * ``step`` is not compatible with the operation's algorithm. * The input is not valid for the operation's algorithm, cipher suite or ``step``. .. retval:: PSA_ERROR_INVALID_SIGNATURE - The verification fails for a `PSA_PAKE_STEP_ZK_PROOF` input step. + The verification fails for a `PSA_PAKE_STEP_ZK_PROOF` or `PSA_PAKE_STEP_CONFIRM` input step. .. retval:: PSA_ERROR_NOT_SUPPORTED The following conditions can result in this error: @@ -1503,7 +1837,7 @@ Support macros :definition: /* specification-defined value */ .. summary:: - Whether the specified algorithm is a J-PAKE algorithm (:code:`PSA_ALG_JPAKE(hash_alg)`). + Whether the specified algorithm is a J-PAKE algorithm. .. param:: alg An algorithm identifier: a value of type :code:`psa_algorithm_t`. @@ -1511,6 +1845,50 @@ Support macros .. return:: ``1`` if ``alg`` is a J-PAKE algorithm, ``0`` otherwise. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + J-PAKE algorithms are constructed using :code:`PSA_ALG_JPAKE(hash_alg)`. + +.. macro:: PSA_ALG_IS_SPAKE2P + :definition: /* specification-defined value */ + + .. summary:: + Whether the specified algorithm is a SPAKE2+ algorithm. + + .. param:: alg + An algorithm identifier: a value of type :code:`psa_algorithm_t`. + + .. return:: + ``1`` if ``alg`` is a SPAKE2+ algorithm, ``0`` otherwise. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + + SPAKE2+ algorithms are constructed using :code:`PSA_ALG_SPAKE2P_HMAC(hash_alg)`, :code:`PSA_ALG_SPAKE2P_CMAC(hash_alg)`, or :code:`PSA_ALG_SPAKE2P_MATTER`. + +.. macro:: PSA_ALG_IS_SPAKE2P_HMAC + :definition: /* specification-defined value */ + + .. summary:: + Whether the specified algorithm is a SPAKE2+ algorithm that uses a HMAC-based key confirmation. + + .. param:: alg + An algorithm identifier: a value of type :code:`psa_algorithm_t`. + + .. return:: + ``1`` if ``alg`` is a SPAKE2+ algorithm that uses a HMAC-based key confirmation, ``0`` otherwise. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + + SPAKE2+ algorithms, using HMAC-based key confirmation, are constructed using :code:`PSA_ALG_SPAKE2P_HMAC(hash_alg)`. + +.. macro:: PSA_ALG_IS_SPAKE2P_CMAC + :definition: /* specification-defined value */ + + .. summary:: + Whether the specified algorithm is a SPAKE2+ algorithm that uses a CMAC-based key confirmation. + + .. param:: alg + An algorithm identifier: a value of type :code:`psa_algorithm_t`. + + .. return:: + ``1`` if ``alg`` is a SPAKE2+ algorithm that uses a CMAC-based key confirmation, ``0`` otherwise. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + + SPAKE2+ algorithms, using CMAC-based key confirmation, are constructed using :code:`PSA_ALG_SPAKE2P_CMAC(hash_alg)`. + .. macro:: PSA_PAKE_OUTPUT_SIZE :definition: /* implementation-defined value */ diff --git a/doc/ext-pake/appendix/history.rst b/doc/ext-pake/appendix/history.rst index cce8bbaa..b6ebbe32 100644 --- a/doc/ext-pake/appendix/history.rst +++ b/doc/ext-pake/appendix/history.rst @@ -29,6 +29,15 @@ API changes * Added asymmetric key types for SPAKE2+ registration, `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()` and `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`. Documented the import/export public key format and key derivation process for these keys. +* Added SPAKE2+ algorithms, supporting both :rfc-title:`9383` and :cite-title:`MATTER`. Added the following APIs: + + - `PSA_ALG_SPAKE2P_HMAC()` + - `PSA_ALG_SPAKE2P_CMAC()` + - `PSA_ALG_SPAKE2P_MATTER` + - `PSA_ALG_IS_SPAKE2P()` + - `PSA_ALG_IS_SPAKE2P_HMAC()` + - `PSA_ALG_IS_SPAKE2P_CMAC()` + Clarifications ~~~~~~~~~~~~~~ diff --git a/doc/ext-pake/appendix/specdef_values.rst b/doc/ext-pake/appendix/specdef_values.rst index 689077ee..46066667 100644 --- a/doc/ext-pake/appendix/specdef_values.rst +++ b/doc/ext-pake/appendix/specdef_values.rst @@ -21,9 +21,24 @@ The examples here provide correct results for the valid inputs defined by each A #define PSA_ALG_IS_PAKE(alg) \ (((alg) & 0x7f000000) == 0x0a000000) + #define PSA_ALG_IS_SPAKE2P(alg) \ + (((alg) & ~0x000003ff) == 0x0a000400) + + #define PSA_ALG_IS_SPAKE2P_CMAC(alg) \ + (((alg) & ~0x000000ff) == 0x0a000500) + + #define PSA_ALG_IS_SPAKE2P_HMAC(alg) \ + (((alg) & ~0x000000ff) == 0x0a000400) + #define PSA_ALG_JPAKE(hash_alg) \ ((psa_algorithm_t) (0x0a000100 | ((hash_alg) & 0x000000ff))) + #define PSA_ALG_SPAKE2P_CMAC(hash_alg) \ + ((psa_algorithm_t) (0x0a000500 | ((hash_alg) & 0x000000ff))) + + #define PSA_ALG_SPAKE2P_HMAC(hash_alg) \ + ((psa_algorithm_t) (0x0a000400 | ((hash_alg) & 0x000000ff))) + #define PSA_PAKE_PRIMITIVE(pake_type, pake_family, pake_bits) \ ((pake_bits & 0xFFFF) != pake_bits) ? 0 : \ ((psa_pake_primitive_t) (((pake_type) << 24 | \ diff --git a/doc/ext-pake/figure/spake2plus-reg.pdf b/doc/ext-pake/figure/spake2plus-reg.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7140f56066fde4cee1d1a2e8c07c53d1a8b9bf05 GIT binary patch literal 41372 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 b)1Q0F6OX~+w{{c^1Ymd3i;8L;*QWmudKQmB literal 0 HcmV?d00001 diff --git a/doc/ext-pake/figure/spake2plus-reg.pdf.license b/doc/ext-pake/figure/spake2plus-reg.pdf.license new file mode 100644 index 00000000..9a9052df --- /dev/null +++ b/doc/ext-pake/figure/spake2plus-reg.pdf.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/ext-pake/figure/spake2plus-reg.puml b/doc/ext-pake/figure/spake2plus-reg.puml new file mode 100644 index 00000000..a9f1929f --- /dev/null +++ b/doc/ext-pake/figure/spake2plus-reg.puml @@ -0,0 +1,41 @@ +' SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +' SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license + +@startuml + + !include atg-spec.pumh + + participant "Prover //(Client role)//" as Prover + participant "Verifier //(Server role)//" as Verifier + + note over Prover, Verifier: Initial information : cipher suite, //PBKDF-params//, //password// + + Prover -> Prover: ""psa_key_derivation_setup(PBKDF)""\n""psa_key_derivation_input_key(password)""\n""psa_key_derivation_input_xxx()"" for //PBKDF-params// + + Prover -> Prover: ""psa_key_derivation_output_key(SPAKE2P_KEY_PAIR)"" + note left: Compute key-pair (//w0//, //w1//) + + alt Independent registration + + Verifier -> Verifier: ""psa_key_derivation_setup(PBKDF)""\n""psa_key_derivation_input_key(password)""\n""psa_key_derivation_input_xxx()"" for //PBKDF-params// + + Verifier -> Verifier: ""psa_key_derivation_output_key(SPAKE2P_PUBLIC_KEY)"" + note left: Compute public-key (//w0//, //L//) + + else Connected registration + + Prover -> Prover: ""psa_export_public_key()"" + note left: Compute //L// and output //w0// || //L// + + Prover ->> Verifier: Registration record ( //w0// || //L// ) + + Verifier -> Verifier: ""psa_import_key(SPAKE2P_PUBLIC_KEY)"" from //w0// || //L// + note left: Import public-key (//w0//, //L//) + + end + + note over Prover: Use key-pair for authentication flow + / note over Verifier: Use public-key for authentication flow + + +@enduml diff --git a/doc/ext-pake/figure/spake2plus-reg.svg b/doc/ext-pake/figure/spake2plus-reg.svg new file mode 100644 index 00000000..43a0c947 --- /dev/null +++ b/doc/ext-pake/figure/spake2plus-reg.svg @@ -0,0 +1 @@ +Prover(Client role)Verifier(Server role)Initial information : cipher suite,PBKDF-params,passwordpsa_key_derivation_setup(PBKDF)psa_key_derivation_input_key(password)psa_key_derivation_input_xxx()forPBKDF-paramspsa_key_derivation_output_key(SPAKE2P_KEY_PAIR)Compute key-pair (w0,w1)alt[Independent registration]psa_key_derivation_setup(PBKDF)psa_key_derivation_input_key(password)psa_key_derivation_input_xxx()forPBKDF-paramspsa_key_derivation_output_key(SPAKE2P_PUBLIC_KEY)Compute public-key (w0,L)[Connected registration]psa_export_public_key()ComputeLand outputw0||LRegistration record (w0||L)psa_import_key(SPAKE2P_PUBLIC_KEY)fromw0||LImport public-key (w0,L)Use key-pair for authentication flowUse public-key for authentication flow \ No newline at end of file diff --git a/doc/ext-pake/figure/spake2plus-reg.svg.license b/doc/ext-pake/figure/spake2plus-reg.svg.license new file mode 100644 index 00000000..9a9052df --- /dev/null +++ b/doc/ext-pake/figure/spake2plus-reg.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/ext-pake/figure/spake2plus.pdf b/doc/ext-pake/figure/spake2plus.pdf new file mode 100644 index 0000000000000000000000000000000000000000..aba6461a955029b1c3f2fb1e31ada9d8c3c941fd GIT binary patch literal 39114 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 h5f;w~bh|tp +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/ext-pake/figure/spake2plus.puml b/doc/ext-pake/figure/spake2plus.puml new file mode 100644 index 00000000..a226d58f --- /dev/null +++ b/doc/ext-pake/figure/spake2plus.puml @@ -0,0 +1,54 @@ +' SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +' SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license + +@startuml + + !include atg-spec.pumh + + participant "Prover //(Client role)//" as Prover + participant "Verifier //(Server role)//" as Verifier + + note over Prover, Verifier: Shared information : cipher suite, //ProverId//, //VerifierId//, and //Context// + note over Verifier: Registration record (//w0//, //L//) derived from password + / note over Prover: Prover 'key pair' (//w0//, //w1//) derived from password + + ... + + Prover -> Prover: ""psa_pake_setup()"" with key (//w0//, //w1//)\n""psa_pake_set_role(PSA_PAKE_ROLE_CLIENT)""\n""psa_pake_set_user(ProverId)""\n""psa_pake_set_peer(VerifierId)""\n""psa_pake_set_context(Context)"" + + note over Prover: Generate key share //X// + + Prover -> Prover: ""psa_pake_output()"" for //shareP// = //X// + Prover ->> Verifier: (//shareP//) + + Verifier -> Verifier: ""psa_pake_setup()"" with key (//w0//, //L//)\n""psa_pake_set_role(PSA_PAKE_ROLE_SERVER)""\n""psa_pake_set_user(VerifierId)""\n""psa_pake_set_peer(ProverId)""\n""psa_pake_set_context(Context)"" + + note over Verifier: Generate key share //Y// + Verifier -> Verifier: ""psa_pake_input()"" for //shareP// + note over Verifier + Validate //shareP// + Compute //K_shared//, //confirmP'// and //confirmV// + end note + + Verifier -> Verifier: ""psa_pake_output()"" for //shareV// = //Y// and //confirmV// + Verifier ->> Prover: (//shareV//, //confirmV//) + + Prover -> Prover: ""psa_pake_input()"" for //shareV// + + note over Prover + Validate //shareV// + Compute //K_shared//, //confirmP// and //confirmV'// + end note + + Prover -> Prover: ""psa_pake_output()"" for //confirmP// + Prover ->> Verifier: (//confirmP//) + + Prover -> Prover: ""psa_pake_input()"" for //confirmV// + note over Prover: Verify that //confirmV'// = //confirmV// + Prover -> Prover: ""psa_pake_get_shared_key()"" to extract //K_shared// + + Verifier -> Verifier: ""psa_pake_input()"" for //confirmP// + note over Verifier: Verify that //confirmP'// = //confirmP// + Verifier -> Verifier: ""psa_pake_get_shared_key()"" to extract //K_shared// + +@enduml diff --git a/doc/ext-pake/figure/spake2plus.svg b/doc/ext-pake/figure/spake2plus.svg new file mode 100644 index 00000000..11051de7 --- /dev/null +++ b/doc/ext-pake/figure/spake2plus.svg @@ -0,0 +1 @@ +Prover(Client role)Verifier(Server role)Shared information : cipher suite,ProverId,VerifierId, andContextRegistration record (w0,L) derived from passwordProver 'key pair' (w0,w1) derived from passwordpsa_pake_setup()with key (w0,w1)psa_pake_set_role(PSA_PAKE_ROLE_CLIENT)psa_pake_set_user(ProverId)psa_pake_set_peer(VerifierId)psa_pake_set_context(Context)Generate key shareXpsa_pake_output()forshareP=X(shareP)psa_pake_setup()with key (w0,L)psa_pake_set_role(PSA_PAKE_ROLE_SERVER)psa_pake_set_user(VerifierId)psa_pake_set_peer(ProverId)psa_pake_set_context(Context)Generate key shareYpsa_pake_input()forsharePValidatesharePComputeK_shared,confirmP'andconfirmVpsa_pake_output()forshareV=YandconfirmV(shareV,confirmV)psa_pake_input()forshareVValidateshareVComputeK_shared,confirmPandconfirmV'psa_pake_output()forconfirmP(confirmP)psa_pake_input()forconfirmVVerify thatconfirmV'=confirmVpsa_pake_get_shared_key()to extractK_sharedpsa_pake_input()forconfirmPVerify thatconfirmP'=confirmPpsa_pake_get_shared_key()to extractK_shared \ No newline at end of file diff --git a/doc/ext-pake/figure/spake2plus.svg.license b/doc/ext-pake/figure/spake2plus.svg.license new file mode 100644 index 00000000..9a9052df --- /dev/null +++ b/doc/ext-pake/figure/spake2plus.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/ext-pake/references b/doc/ext-pake/references index 5b81c743..e9a16814 100644 --- a/doc/ext-pake/references +++ b/doc/ext-pake/references @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates +.. SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates .. SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license .. reference:: PSA-CRYPT @@ -11,11 +11,11 @@ :author: Arm Ltd :url: github.com/ARMmbed/mbedtls -.. reference:: SEC1 - :title: SEC 1: Elliptic Curve Cryptography - :author: Standards for Efficient Cryptography - :publication: May 2009 - :url: www.secg.org/sec1-v2.pdf +.. reference:: MATTER + :title: Matter Specification, Version 1.2 + :author: CSA + :publication: October 2023 + :url: csa-iot.org/all-solutions/matter/ .. reference:: RFC8235 :title: Schnorr Non-interactive Zero-Knowledge Proof @@ -28,3 +28,21 @@ :author: IETF :publication: September 2017 :url: tools.ietf.org/html/rfc8236.html + +.. reference:: RFC9383 + :title: SPAKE2+, an Augmented Password-Authenticated Key Exchange (PAKE) Protocol + :author: IETF + :publication: September 2023 + :url: tools.ietf.org/html/rfc9383.html + +.. reference:: SEC1 + :title: SEC 1: Elliptic Curve Cryptography + :author: Standards for Efficient Cryptography + :publication: May 2009 + :url: www.secg.org/sec1-v2.pdf + +.. reference:: SPAKE2P-2 + :title: SPAKE2+, an Augmented PAKE (Draft 02) + :author: IETF + :publication: December 2020 + :url: datatracker.ietf.org/doc/draft-bar-cfrg-spake2plus-02 From 3cf983f3e9e2ec98ab76224b92e61c3ddde0b787 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 30 Nov 2023 12:15:16 +0000 Subject: [PATCH 06/11] Move algorithm and key encoding to separate chapter --- doc/ext-pake/api/encodings.rst | 107 +++++++++++++++++++++++++++++++++ doc/ext-pake/api/pake.rst | 93 +--------------------------- doc/ext-pake/index.rst | 3 +- 3 files changed, 110 insertions(+), 93 deletions(-) create mode 100644 doc/ext-pake/api/encodings.rst diff --git a/doc/ext-pake/api/encodings.rst b/doc/ext-pake/api/encodings.rst new file mode 100644 index 00000000..6f573eef --- /dev/null +++ b/doc/ext-pake/api/encodings.rst @@ -0,0 +1,107 @@ +.. SPDX-FileCopyrightText: Copyright 2022-2023 Arm Limited and/or its affiliates +.. SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license + +.. _pake-encodings: + +Algorithm and key type encoding +=============================== + +These are encodings for a proposed PAKE interface for :cite-title:`PSA-CRYPT`. +It is not part of the official |API| yet. + +.. note:: + + The content of this specification is not part of the stable |API| and may change substantially from version to version. + +Algorithm encoding +------------------ + +A new algorithm category is added for PAKE algorithms. The algorithm category table in `[PSA-CRYPT]` Appendix B is extended with the information in :numref:`table-pake-algorithm-category`. + +.. csv-table:: New algorithm identifier categories + :name: table-pake-algorithm-category + :header-rows: 1 + :align: left + :widths: auto + + Algorithm category, CAT, Category details + PAKE, ``0x0A``, See :secref:`pake-encoding` + +.. _pake-encoding: + +PAKE algorithm encoding +~~~~~~~~~~~~~~~~~~~~~~~ + +The algorithm identifier for PAKE algorithms defined in this specification are encoded as shown in :numref:`fig-pake-encoding`. + +.. figure:: /figure/pake_encoding.* + :name: fig-pake-encoding + + PAKE algorithm encoding + +The defined values for PAKE-TYPE are shown in :numref:`table-pake-type`. + +The permitted values of HASH-TYPE depend on the specific PAKE algorithm. + +.. + The permitted values of HASH-TYPE (see :numref:`table-hash-type`) depend on the specific PAKE algorithm. + +.. csv-table:: PAKE algorithm sub-type values + :name: table-pake-type + :header-rows: 1 + :align: left + :widths: auto + + PAKE algorithm, PAKE-TYPE, Algorithm identifier, Algorithm value + J-PAKE, ``0x01``, :code:`PSA_ALG_JPAKE(hash)`, ``0x0A0001hh`` :sup:`a` + SPAKE2+ with HMAC, ``0x04``, :code:`PSA_ALG_SPAKE2P_HMAC(hash)`, ``0x0A0004hh`` :sup:`a` + SPAKE2+ with CMAC, ``0x05``, :code:`PSA_ALG_SPAKE2P_CMAC(hash)`, ``0x0A0005hh`` :sup:`a` + SPAKE2+ for Matter, ``0x06``, :code:`PSA_ALG_SPAKE2P_MATTER`, ``0x0A000609`` + +a. ``hh`` is the HASH-TYPE for the hash algorithm, ``hash``, used to construct the key derivation algorithm. + +Key encoding +------------ + +A new type of asymmetric key is added for the SPAKE2+ algorithms. The Asymmetric key sub-type values table in `[PSA-CRYPT]` Appendix B is extended with the information in :numref:`table-spake2p-keys`. + +.. csv-table:: New SPAKE2+ asymmetric key sub-type + :name: table-spake2p-keys + :header-rows: 1 + :align: left + :widths: auto + + Asymmetric key type, ASYM-TYPE, Details + SPAKE2+, 4, See :secref:`spakep2-key-encoding` + +.. rationale:: + + The ASYM-TYPE value 4 is selected as this has the same parity as the ECC sub-type, which have the value 1. The enables the same ECC-FAMILY and P values to be used when encoding a SPAKE2+ key type, as is used in the Elliptic Curve key types. + +.. _spakep2-key-encoding: + +SPAKE2+ key encoding +~~~~~~~~~~~~~~~~~~~~ + +The key type for SPAKE2+ keys defined in this specification are encoded as shown in :numref:`fig-spake2p-key-fields`. + +.. figure:: ../figure/spake2p_key.* + :name: fig-spake2p-key-fields + + SPAKE2+ key encoding + +PAIR is either 0 for a public key, or 3 for a key pair. + +The defined values for ECC-FAMILY and P are shown in :numref:`table-spake2p-type`. + +.. csv-table:: SPAKE2+ key family values + :name: table-spake2p-type + :header-rows: 1 + :align: left + :widths: auto + + SPAKE2+ group, ECC-FAMILY, P, ECC family :sup:`a`, Public key value, Key pair value + SECP R1, 0x09, 0, :code:`PSA_ECC_FAMILY_SECP_R1`, ``0x4412``, ``0x7412`` + Twisted Edwards, 0x21, 0, :code:`PSA_ECC_FAMILY_TWISTED_EDWARDS`, ``0x4442``, ``0x7442`` + +a. The key type value is constructed from the Elliptic Curve family using either :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY(family)` or :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(family)` as required. diff --git a/doc/ext-pake/api/pake.rst b/doc/ext-pake/api/pake.rst index b0805093..b0656647 100644 --- a/doc/ext-pake/api/pake.rst +++ b/doc/ext-pake/api/pake.rst @@ -11,98 +11,7 @@ It is not part of the official |API| yet. The content of this specification is not part of the stable |API| and may change substantially from version to version. -Algorithm encoding ------------------- - -A new algorithm category is added for PAKE algorithms. The algorithm category table in `[PSA-CRYPT]` Appendix B is extended with the information in :numref:`table-pake-algorithm-category`. - -.. csv-table:: New algorithm identifier categories - :name: table-pake-algorithm-category - :header-rows: 1 - :align: left - :widths: auto - - Algorithm category, CAT, Category details - PAKE, ``0x0A``, See :secref:`pake-encoding` - -.. _pake-encoding: - -PAKE algorithm encoding -~~~~~~~~~~~~~~~~~~~~~~~ - -The algorithm identifier for PAKE algorithms defined in this specification are encoded as shown in :numref:`fig-pake-encoding`. - -.. figure:: /figure/pake_encoding.* - :name: fig-pake-encoding - - PAKE algorithm encoding - -The defined values for PAKE-TYPE are shown in :numref:`table-pake-type`. - -The permitted values of HASH-TYPE depend on the specific PAKE algorithm. - -.. - The permitted values of HASH-TYPE (see :numref:`table-hash-type`) depend on the specific PAKE algorithm. - -.. csv-table:: PAKE algorithm sub-type values - :name: table-pake-type - :header-rows: 1 - :align: left - :widths: auto - - PAKE algorithm, PAKE-TYPE, Algorithm identifier, Algorithm value - J-PAKE, ``0x01``, :code:`PSA_ALG_JPAKE(hash)`, ``0x0A0001hh`` :sup:`a` - SPAKE2+ with HMAC, ``0x04``, :code:`PSA_ALG_SPAKE2P_HMAC(hash)`, ``0x0A0004hh`` :sup:`a` - SPAKE2+ with CMAC, ``0x05``, :code:`PSA_ALG_SPAKE2P_CMAC(hash)`, ``0x0A0005hh`` :sup:`a` - SPAKE2+ for Matter, ``0x06``, :code:`PSA_ALG_SPAKE2P_MATTER`, ``0x0A000609`` - -a. ``hh`` is the HASH-TYPE for the hash algorithm, ``hash``, used to construct the key derivation algorithm. - -Key encoding ------------- - -A new type of asymmetric key is added for the SPAKE2+ algorithms. The Asymmetric key sub-type values table in `[PSA-CRYPT]` Appendix B is extended with the information in :numref:`table-spake2p-keys`. - -.. csv-table:: New SPAKE2+ asymmetric key sub-type - :name: table-spake2p-keys - :header-rows: 1 - :align: left - :widths: auto - - Asymmetric key type, ASYM-TYPE, Details - SPAKE2+, 4, See :secref:`spakep2-key-encoding` - -.. rationale:: - - The ASYM-TYPE value 4 is selected as this has the same parity as the ECC sub-type, which have the value 1. The enables the same ECC-FAMILY and P values to be used when encoding a SPAKE2+ key type, as is used in the Elliptic Curve key types. - -.. _spakep2-key-encoding: - -SPAKE2+ key encoding -~~~~~~~~~~~~~~~~~~~~ - -The key type for SPAKE2+ keys defined in this specification are encoded as shown in :numref:`fig-spake2p-key-fields`. - -.. figure:: ../figure/spake2p_key.* - :name: fig-spake2p-key-fields - - SPAKE2+ key encoding - -PAIR is either 0 for a public key, or 3 for a key pair. - -The defined values for ECC-FAMILY and P are shown in :numref:`table-spake2p-type`. - -.. csv-table:: SPAKE2+ key family values - :name: table-spake2p-type - :header-rows: 1 - :align: left - :widths: auto - - SPAKE2+ group, ECC-FAMILY, P, ECC family :sup:`a`, Public key value, Key pair value - SECP R1, 0x09, 0, :code:`PSA_ECC_FAMILY_SECP_R1`, ``0x4412``, ``0x7412`` - Twisted Edwards, 0x21, 0, :code:`PSA_ECC_FAMILY_TWISTED_EDWARDS`, ``0x4442``, ``0x7442`` - -a. The key type value is constructed from the Elliptic Curve family using either :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY(family)` or :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(family)` as required. +PAKE introduces additional algorithm identifiers and key types. See :secref:`pake-encodings` for the encoding of these values. Key formats ----------- diff --git a/doc/ext-pake/index.rst b/doc/ext-pake/index.rst index d56ad737..5161a5a8 100644 --- a/doc/ext-pake/index.rst +++ b/doc/ext-pake/index.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates +.. SPDX-FileCopyrightText: Copyright 2022-2023 Arm Limited and/or its affiliates .. SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license .. title:: @@ -23,6 +23,7 @@ overview/intro api/pake + api/encodings .. appendix:: From 8a8dd35caeae11df34fb5d16ddfd5e95c01d0cde Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 30 Nov 2023 12:15:38 +0000 Subject: [PATCH 07/11] Provide graphic of PAKE primitive encoding --- doc/ext-pake/api/pake.rst | 32 +++++++++++------- doc/ext-pake/figure/pake_primitive.json | 14 ++++++++ .../figure/pake_primitive.json.license | 2 ++ doc/ext-pake/figure/pake_primitive.pdf | Bin 0 -> 7813 bytes .../figure/pake_primitive.pdf.license | 2 ++ doc/ext-pake/figure/pake_primitive.svg | 2 ++ .../figure/pake_primitive.svg.license | 2 ++ 7 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 doc/ext-pake/figure/pake_primitive.json create mode 100644 doc/ext-pake/figure/pake_primitive.json.license create mode 100644 doc/ext-pake/figure/pake_primitive.pdf create mode 100644 doc/ext-pake/figure/pake_primitive.pdf.license create mode 100644 doc/ext-pake/figure/pake_primitive.svg create mode 100644 doc/ext-pake/figure/pake_primitive.svg.license diff --git a/doc/ext-pake/api/pake.rst b/doc/ext-pake/api/pake.rst index b0656647..1223e0f0 100644 --- a/doc/ext-pake/api/pake.rst +++ b/doc/ext-pake/api/pake.rst @@ -721,10 +721,27 @@ Many PAKE algorithms are designed to allow different cryptographic primitives to The cryptographic primitive for a PAKE operation is specified using a `psa_pake_primitive_t` value, which can be constructed using the `PSA_PAKE_PRIMITIVE()` macro, or can be provided as a numerical constant value. -The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. These can be used to set key attributes for keys used in PAKE algorithms. - A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_pake_cipher_suite_t`, which fully specifies the PAKE operation to be carried out. +.. typedef:: uint32_t psa_pake_primitive_t + + .. summary:: + Encoding of the primitive associated with the PAKE. + + PAKE primitive values are constructed using `PSA_PAKE_PRIMITIVE()`. + + :numref:`fig-pake-primitive` shows how the components of the primitive are encoded into a `psa_pake_primitive_t` value. + + .. figure:: /figure/pake_primitive.* + :name: fig-pake-primitive + + PAKE primitive encoding + + The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. These can be used to set key attributes for keys used in PAKE algorithms. + + .. rationale:: + + An integral type is required for `psa_pake_primitive_t` to enable values of this type to be compile-time-constants. This allows them to be used in ``case`` statements, and used to calculate static buffer sizes with `PSA_PAKE_OUTPUT_SIZE()` and `PSA_PAKE_INPUT_SIZE()`. .. typedef:: uint8_t psa_pake_primitive_type_t @@ -788,17 +805,6 @@ A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_ For more information on the family values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. -.. typedef:: uint32_t psa_pake_primitive_t - - .. summary:: - Encoding of the primitive associated with the PAKE. - - PAKE primitive values are constructed using `PSA_PAKE_PRIMITIVE()`. - - .. rationale:: - - An integral type is required for `psa_pake_primitive_t` to enable values of this type to be compile-time-constants. This allows them to be used in ``case`` statements, and used to calculate static buffer sizes with `PSA_PAKE_OUTPUT_SIZE()` and `PSA_PAKE_INPUT_SIZE()`. - .. macro:: PSA_PAKE_PRIMITIVE :definition: /* specification-defined value */ diff --git a/doc/ext-pake/figure/pake_primitive.json b/doc/ext-pake/figure/pake_primitive.json new file mode 100644 index 00000000..04d93074 --- /dev/null +++ b/doc/ext-pake/figure/pake_primitive.json @@ -0,0 +1,14 @@ +{ + "reg": [ + { "name": "PAKE-BITS", "bits": 16 }, + { "name": "PAKE-FAMILY", "bits": 8 }, + { "name": "PAKE_TYPE", "bits": 8 } + ], + "options": { + "lanes": 1, + "fontfamily": "lato", + "fontsize": 11, + "vspace": 52, + "hspace": 600 + } +} diff --git a/doc/ext-pake/figure/pake_primitive.json.license b/doc/ext-pake/figure/pake_primitive.json.license new file mode 100644 index 00000000..9a9052df --- /dev/null +++ b/doc/ext-pake/figure/pake_primitive.json.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/ext-pake/figure/pake_primitive.pdf b/doc/ext-pake/figure/pake_primitive.pdf new file mode 100644 index 0000000000000000000000000000000000000000..95e98e9f8ad8b91e35f97077cae1ce0fd1018704 GIT binary patch literal 7813 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 MK`$(9?_5c6? literal 0 HcmV?d00001 diff --git a/doc/ext-pake/figure/pake_primitive.pdf.license b/doc/ext-pake/figure/pake_primitive.pdf.license new file mode 100644 index 00000000..9a9052df --- /dev/null +++ b/doc/ext-pake/figure/pake_primitive.pdf.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/ext-pake/figure/pake_primitive.svg b/doc/ext-pake/figure/pake_primitive.svg new file mode 100644 index 00000000..774d59e0 --- /dev/null +++ b/doc/ext-pake/figure/pake_primitive.svg @@ -0,0 +1,2 @@ + +01516232431PAKE-BITSPAKE-FAMILYPAKE_TYPE \ No newline at end of file diff --git a/doc/ext-pake/figure/pake_primitive.svg.license b/doc/ext-pake/figure/pake_primitive.svg.license new file mode 100644 index 00000000..9a9052df --- /dev/null +++ b/doc/ext-pake/figure/pake_primitive.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license From 80f0fac76fa61388da80fa2885167a25ecf5b0e2 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Fri, 1 Dec 2023 16:41:39 +0000 Subject: [PATCH 08/11] Restructure the PAKE API chapter * Split into three sections: Common, J-PAKE, SPAKE2+ * Provide an overview and flow for each protocol, including password handling --- doc/ext-pake/api/pake.rst | 2617 ++++++++++++----------- doc/ext-pake/figure/spake2plus-reg.pdf | Bin 41372 -> 41299 bytes doc/ext-pake/figure/spake2plus-reg.puml | 6 +- doc/ext-pake/figure/spake2plus-reg.svg | 2 +- doc/ext-pake/figure/spake2plus.pdf | Bin 39114 -> 39133 bytes doc/ext-pake/figure/spake2plus.puml | 2 +- doc/ext-pake/figure/spake2plus.svg | 2 +- 7 files changed, 1391 insertions(+), 1238 deletions(-) diff --git a/doc/ext-pake/api/pake.rst b/doc/ext-pake/api/pake.rst index 1223e0f0..22520c44 100644 --- a/doc/ext-pake/api/pake.rst +++ b/doc/ext-pake/api/pake.rst @@ -4,33 +4,33 @@ Password-authenticated key exchange (PAKE) ========================================== -This is a proposed PAKE interface for :cite-title:`PSA-CRYPT`. -It is not part of the official |API| yet. - .. note:: + This is a proposed PAKE interface for :cite-title:`PSA-CRYPT`. + It is not part of the official |API| yet. + The content of this specification is not part of the stable |API| and may change substantially from version to version. -PAKE introduces additional algorithm identifiers and key types. See :secref:`pake-encodings` for the encoding of these values. +This chapter is divided into the following sections: -Key formats ------------ +* :secref:`pake-common-api` --- the common interface elements, including the PAKE operation. +* :secref:`pake-jpake` --- the J-PAKE protocol, and the associated interface elements. +* :secref:`pake-spake2p` --- the SPAKE2+ protocols, and the associated interface elements. -A SPAKE2+ public key can be exported and imported, to enable use cases that require offline registration. +PAKE also introduces additional algorithm identifiers and key types to the |API|. +See :secref:`pake-encodings` for the encoding of these values. -The public key consists of the two values :math:`w0` and :math:`L`, which result from the SPAKE2+ registration phase. :math:`w0` is a scalar in the same range as a private Elliptic curve key from the group used as the SPAKE2+ primitive group. :math:`L` is a point on the curve, similar to a public key from the same group, corresponding to the :math:`w1` value in the key pair. +.. rationale:: -For the |API|, the default format for a SPAKE2+ public key is the concatenation of the formatted values for :math:`w0` and :math:`L`, using the standard formats for Elliptic curve keys used by the |API|. For example, for SPAKE2+ over P-256 (secp256r1), the output from :code:`psa_export_public_key()` would be the concatenation of: + PAKE protocols are more complex operations, when compared with the other types of cryptographic operation in the |API|. + PAKE protocols can also have multiple phases, some of which are carried out prior to the PAKE operation itself, using other parts of the |API|. -* The 32-byte formatted value of the P-256 private key :math:`w0`. This is a big-endian encoding of the integer :math:`w0`. -* The 65-byte formatted value of the P-256 public key :math:`L`. This is itself a concatenation of: + To improve the understanding and correct use of PAKE protocols, it helps to show the protocol flow, and to demonstrate how to implement this with this API. - - The byte ``0x04``. - - The 32-byte big-endian encoding of the x-coordinate of :math:`L`. - - The 32-byte big-endian encoding of the y-coordinate of :math:`L`. +.. _pake-common-api: -Changes and additions to the Programming API --------------------------------------------- +Common API for PAKE +------------------- .. header:: psa/crypto-pake :copyright: Copyright 2018-2023 Arm Limited and/or its affiliates @@ -42,1831 +42,1984 @@ Changes and additions to the Programming API * These definitions must be embedded in, or included by, psa/crypto.h */ -.. _pake-keys: - -SPAKE2+ keys -~~~~~~~~~~~~ - -The SPAKE2+ protocol consists of three phases: +This section defines all of the common interfaces used to carry out a PAKE protocol: -1. Registration -2. Authenticated key exchange -3. Key confirmation +* :secref:`pake-algorithms` +* :secref:`pake-primitive` +* :secref:`pake-cipher-suite` +* :secref:`pake-roles` +* :secref:`pake-steps` +* :secref:`pake-operation` +* :secref:`pake-support` -The registration phase can be carried out immediately before the other phases, or can be carried out offline, and the result of the registration phase transferred to the participants in the protocol for later online authentication. - -The |API| uses an asymmetric key-pair, and a public-key, to store the output of the registration for input to the authentication protocol. The registration is carried out using a key derivation operation, and the key exchange and confirmation is carried out using a PAKE operation. For a SPAKE2+ PAKE operation, the prover, or client, role requires a SPAKE2+ key-pair, while the verifier, or server, role can use either a SPAKE2+ key-pair or SPAKE2+ public key. +.. _pake-algorithms: -The SPAKE2+ algorithms are based on Elliptic curve groups, and a SPAKE2+ key is parameterized by a specific Elliptic curve. The Elliptic curve families are used to parameterize the key type, and the key size selects the specific curve. +PAKE algorithms +~~~~~~~~~~~~~~~ -.. macro:: PSA_KEY_TYPE_SPAKE2P_KEY_PAIR +.. macro:: PSA_ALG_IS_PAKE :definition: /* specification-defined value */ .. summary:: - SPAKE2+ key pair: both the prover and verifier key. - - .. param:: curve - A value of type :code:`psa_ecc_family_t` that identifies the Elliptic curve family to be used. - - The size of a SPAKE2+ key is the size associated with the Elliptic curve group, that is, :math:`\lceil{log_2(q)}\rceil` for a curve over a field :math:`\mathbb{F}_q`. See the documentation of each Elliptic curve family for details. - - To construct a SPAKE2+ key pair, it must be output from a key derivation operation. See :secref:`spake2p-key-derivation`. - - The corresponding public key can be exported using :code:`psa_export_public_key()`. See also `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`. - - .. subsection:: Compatible algorithms - - | `PSA_ALG_SPAKE2P_HMAC` - | `PSA_ALG_SPAKE2P_CMAC` - | `PSA_ALG_SPAKE2P_MATTER` + Whether the specified algorithm is a password-authenticated key exchange. -.. macro:: PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY - :definition: /* specification-defined value */ + .. param:: alg + An algorithm identifier: a value of type :code:`psa_algorithm_t`. - .. summary:: - SPAKE2+ public key: the verifier key. + .. return:: + ``1`` if ``alg`` is a password-authenticated key exchange (PAKE) algorithm, ``0`` otherwise. + This macro can return either ``0`` or ``1`` if ``alg`` is not a supported algorithm identifier. - .. param:: curve - A value of type :code:`psa_ecc_family_t` that identifies the Elliptic curve family to be used. +.. _pake-primitive: - The size of an SPAKE2+ public key is the same as the corresponding private key. See `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()` and the documentation of each Elliptic curve family for details. +PAKE primitives +~~~~~~~~~~~~~~~ - To construct a SPAKE2+ public key, it must be imported. +A PAKE algorithm specifies a sequence of interactions between the participants. +Many PAKE algorithms are designed to allow different cryptographic primitives to be used for the key establishment operation, so long as all the participants are using the same underlying cryptography. - .. subsection:: Compatible algorithms +The cryptographic primitive for a PAKE operation is specified using a `psa_pake_primitive_t` value, which can be constructed using the `PSA_PAKE_PRIMITIVE()` macro, or can be provided as a numerical constant value. - | `PSA_ALG_SPAKE2P_HMAC` (verification only) - | `PSA_ALG_SPAKE2P_CMAC` (verification only) - | `PSA_ALG_SPAKE2P_MATTER` (verification only) +A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_pake_cipher_suite_t`, which fully specifies the PAKE operation to be carried out. -.. macro:: PSA_KEY_TYPE_IS_SPAKE2P - :definition: /* specification-defined value */ +.. typedef:: uint32_t psa_pake_primitive_t .. summary:: - Whether a key type is a SPAKE2+ key, either a key pair or a public key. + Encoding of the primitive associated with the PAKE. - .. param:: type - A key type: a value of type :code:`psa_key_type_t`. + PAKE primitive values are constructed using `PSA_PAKE_PRIMITIVE()`. -.. macro:: PSA_KEY_TYPE_IS_SPAKE2P_KEY_PAIR - :definition: /* specification-defined value */ + :numref:`fig-pake-primitive` shows how the components of the primitive are encoded into a `psa_pake_primitive_t` value. - .. summary:: - Whether a key type is a SPAKE2+ key pair. + .. figure:: /figure/pake_primitive.* + :name: fig-pake-primitive - .. param:: type - A key type: a value of type :code:`psa_key_type_t`. + PAKE primitive encoding -.. macro:: PSA_KEY_TYPE_IS_SPAKE2P_PUBLIC_KEY - :definition: /* specification-defined value */ + The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. + These can be used to set key attributes for keys used in PAKE algorithms. - .. summary:: - Whether a key type is a SPAKE2+ public key. + .. rationale:: - .. param:: type - A key type: a value of type :code:`psa_key_type_t`. + An integral type is required for `psa_pake_primitive_t` to enable values of this type to be compile-time-constants. + This allows them to be used in ``case`` statements, and used to calculate static buffer sizes with `PSA_PAKE_OUTPUT_SIZE()` and `PSA_PAKE_INPUT_SIZE()`. -.. macro:: PSA_KEY_TYPE_SPAKE2P_GET_FAMILY - :definition: /* specification-defined value */ +.. typedef:: uint8_t psa_pake_primitive_type_t .. summary:: - Extract the curve family from a SPAKE2+ key type. - - .. param:: type - A SPAKE2+ key type: a value of type :code:`psa_key_type_t` such that :code:`PSA_KEY_TYPE_IS_SPAKE2P(type)` is true. - - .. return:: psa_ecc_family_t - The elliptic curve family id, if ``type`` is a supported SPAKE2+ key. Unspecified if ``type`` is not a supported SPAKE2+ key. - -.. _spake2p-key-derivation: - -Derivation of SPAKE2+ keys -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -A SPAKE2+ key pair can be output from a key derivation using :code:`psa_key_derivation_output_key()`. The SPAKE2+ protocol recommends that a key-stretching key-derivation function, such as PBKDF2, is used to hash the SPAKE2+ password. See RFC 9383 for details. - -For example, after setting up the PBKDF2 operation, the following process will derive the SPAKE2+ key pair for use with the P-256 Elliptic curve group :issue:`(This example can be combined with an illustration of the registration flow in the final specification)`: + Encoding of the type of the PAKE's primitive. -1. Allocate and initialize a key attributes object: + The range of PAKE primitive type values is divided as follows: - .. code-block:: xref + :code:`0x00` + Reserved as an invalid primitive type. + :code:`0x01 – 0x7f` + Specification-defined primitive type. + Primitive types defined by this standard always have bit 7 clear. + Unallocated primitive type values in this range are reserved for future use. + :code:`0x80 – 0xff` + Implementation-defined primitive type. + Implementations that define additional primitive types must use an encoding with bit 7 set. - psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT; + For specification-defined primitive types, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. -#. Set the key type and size: +.. macro:: PSA_PAKE_PRIMITIVE_TYPE_ECC + :definition: ((psa_pake_primitive_type_t)0x01) - .. code-block:: xref + .. summary:: + The PAKE primitive type indicating the use of elliptic curves. - psa_set_key_type(&att, PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); - psa_set_key_bits(&att, 256); // for P-256 + The values of the ``family`` and ``bits`` components of the PAKE primitive identify a specific elliptic curve, using the same mapping that is used for ECC keys. + See the definition of ``psa_ecc_family_t``. + Here ``family`` and ``bits`` refer to the values used to construct the PAKE primitive using `PSA_PAKE_PRIMITIVE()`. -#. Set the key policy: + Input and output during the operation can involve group elements and scalar values: - .. code-block:: xref + * The format for group elements is the same as that for public keys on the specific Elliptic curve. + For more information, consult the documentation of key formats in `[PSA-CRYPT]`. + * The format for scalars is the same as that for private keys on the specific Elliptic curve. + For more information, consult the documentation of key formats in `[PSA-CRYPT]`. - psa_set_key_usage_flags(&att, PSA_KEY_USAGE_DERIVE); - psa_set_key_algorithm(&att, PSA_ALG_SPAKE2P); -#. Derive the key: +.. macro:: PSA_PAKE_PRIMITIVE_TYPE_DH + :definition: ((psa_pake_primitive_type_t)0x02) - .. code-block:: xref + .. summary:: + The PAKE primitive type indicating the use of Diffie-Hellman groups. - psa_key_id_t sp2p_key; - psa_key_derivation_output_key(&att, &kdf_op, &sp2p_key); + The values of the ``family`` and ``bits`` components of the PAKE primitive identify a specific Diffie-Hellman group, using the same mapping that is used for Diffie-Hellman keys. + See the definition of ``psa_dh_family_t``. + Here ``family`` and ``bits`` refer to the values used to construct the PAKE primitive using `PSA_PAKE_PRIMITIVE()`. -The key derivation process in :code:`psa_key_derivation_output_key()` follows the recommendations for the registration process in RFC 9383, and matches the specification of this process in the Matter specification. + Input and output during the operation can involve group elements and scalar values: -For the |API|: + * The format for group elements is the same as that for public keys in the specific Diffie-Hellman group. + For more information, consult the documentation of key formats in `[PSA-CRYPT]`. + * The format for scalars is the same as that for private keys in the specific Diffie-Hellman group. + For more information, consult the documentation of key formats in `[PSA-CRYPT]`. -* The derivation of SPAKE2+ keys extracts :math:`\lceil{log_2(p)/8}\rceil+8` bytes from the PBKDF for each of :math:`w0s` and :math:`w1s`, where :math:`p` is the prime factor of the order of the elliptic curve group. - The following sizes are used for extracting :math:`w0s` and :math:`w1s`, depending on the elliptic curve: - .. csv-table:: - :header-rows: 1 - :widths: auto - :align: left +.. typedef:: uint8_t psa_pake_family_t - Elliptic curve, "Size of :math:`w0s` and :math:`w1s`, in bytes" - P-256, 40 - P-384, 56 - P-521, 74 - edwards25519, 40 - edwards448, 64 + .. summary:: + Encoding of the family of the primitive associated with the PAKE. - :issue:`I think these values are correct?` + For more information on the family values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. -* The calculation of :math:`w0`, :math:`w1`, and :math:`L` then proceeds as described in the RFC. +.. macro:: PSA_PAKE_PRIMITIVE + :definition: /* specification-defined value */ - .. admonition:: Implementation note + .. summary:: + Construct a PAKE primitive from type, family and bit-size. - The values of :math:`w0` and :math:`w1` are required as part of the SPAKE2+ key pair. + .. param:: pake_type + The type of the primitive: a value of type `psa_pake_primitive_type_t`. + .. param:: pake_family + The family of the primitive. + The type and interpretation of this parameter depends on ``pake_type``. + For more information, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. + .. param:: pake_bits + The bit-size of the primitive: a value of type ``size_t``. + The interpretation of this parameter depends on ``pake_type`` and ``family``. + For more information, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. - It is :scterm:`implementation defined` whether :math:`L` is computed during key derivation, and stored as part of the key pair; or only computed when required from the key pair. + .. return:: psa_pake_primitive_t + The constructed primitive value. + Return ``0`` if the requested primitive can't be encoded as `psa_pake_primitive_t`. -.. _pake-algorithms: + A PAKE primitive value is used to specify a PAKE operation, as part of a PAKE cipher suite. -PAKE algorithms -~~~~~~~~~~~~~~~ + The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. -.. macro:: PSA_ALG_IS_PAKE +.. macro:: PSA_PAKE_PRIMITIVE_GET_TYPE :definition: /* specification-defined value */ .. summary:: - Whether the specified algorithm is a password-authenticated key exchange. + Extract the PAKE primitive type from a PAKE primitive. - .. param:: alg - An algorithm identifier: a value of type :code:`psa_algorithm_t`. + .. param:: pake_primitive + A PAKE primitive: a value of type `psa_pake_primitive_t`. - .. return:: - ``1`` if ``alg`` is a password-authenticated key exchange (PAKE) algorithm, ``0`` otherwise. - This macro can return either ``0`` or ``1`` if ``alg`` is not a supported algorithm identifier. + .. return:: psa_pake_primitive_type_t + The PAKE primitive type, if ``pake_primitive`` is a supported PAKE primitive. + Unspecified if ``pake_primitive`` is not a supported PAKE primitive. -.. macro:: PSA_ALG_JPAKE +.. macro:: PSA_PAKE_PRIMITIVE_GET_FAMILY :definition: /* specification-defined value */ .. summary:: - Macro to build the Password-authenticated key exchange by juggling (J-PAKE) algorithm. - - .. param:: hash_alg - A hash algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_HASH(hash_alg)` is true. + Extract the family from a PAKE primitive. - .. return:: - A J-PAKE algorithm, parameterized by a specific hash. + .. param:: pake_primitive + A PAKE primitive: a value of type `psa_pake_primitive_t`. - Unspecified if ``hash_alg`` is not a supported hash algorithm. + .. return:: psa_pake_family_t + The PAKE primitive family, if ``pake_primitive`` is a supported PAKE primitive. + Unspecified if ``pake_primitive`` is not a supported PAKE primitive. + For more information on the family values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. - This is J-PAKE as defined by :RFC-title:`8236`, instantiated with the following parameters: +.. macro:: PSA_PAKE_PRIMITIVE_GET_BITS + :definition: /* specification-defined value */ - * The group can be either an elliptic curve or defined over a finite field. - * Schnorr Non-Interactive Zero-Knowledge Proof (NIZKP) as defined by :RFC-title:`8235`, using the same group as the J-PAKE algorithm. - * A cryptographic hash function, ``hash_alg``. + .. summary:: + Extract the bit-size from a PAKE primitive. - J-PAKE does not confirm the shared secret key that results from the key exchange. + .. param:: pake_primitive + A PAKE primitive: a value of type `psa_pake_primitive_t`. - To select these parameters and set up the cipher suite, initialize a `psa_pake_cipher_suite_t` object, and call the following functions in any order: + .. return:: size_t + The PAKE primitive bit-size, if ``pake_primitive`` is a supported PAKE primitive. + Unspecified if ``pake_primitive`` is not a supported PAKE primitive. - .. code-block:: xref + For more information on the bit-size values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. - psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; +.. _pake-cipher-suite: - psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_JPAKE(hash)); - psa_pake_cs_set_primitive(&cipher_suite, - PSA_PAKE_PRIMITIVE(type, family, bits)); - psa_pake_cs_set_key_confirmation(&cipher_suite, PSA_PAKE_UNCONFIRMED_KEY); +PAKE cipher suites +~~~~~~~~~~~~~~~~~~ - More information on selecting a specific Elliptic curve or Diffie-Hellman field is provided with the `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH` constants. +Most PAKE algorithms have parameters that must be specified by the application. +These parameters include the following: - The PAKE operation for J-PAKE requires a key of type type :code:`PSA_KEY_TYPE_PASSWORD` or :code:`PSA_KEY_TYPE_PASSWORD_HASH`. - The same key value must be provided to the PAKE operation in both participants. +* The cryptographic primitive used for key establishment, specified using a `PAKE primitive `. +* A cryptographic hash algorithm. +* Whether the application requires the shared secret before, or after, it is confirmed. - The key can be the password text itself, in an agreed character encoding, or some value derived from the password as required by a higher level protocol. - For low-entropy passwords, it is recommended that a key-stretching derivation algorithm, such as PBKDF2, is used, and the resulting password hash is used as the PAKE operation key. +The hash algorithm is encoded into the PAKE algorithm identifier. The `psa_pake_cipher_suite_t` object is used to fully specify a PAKE operation, combining the PAKE protocol with all of the above parameters. - The J-PAKE operation follows the protocol shown in :numref:`fig-jpake`. +A PAKE cipher suite is required when setting up a PAKE operation in `psa_pake_setup()`. - .. figure:: /figure/j-pake.* - :name: fig-jpake - The J-PAKE protocol +.. typedef:: /* implementation-defined type */ psa_pake_cipher_suite_t - The variable names *x1*, *g1*, and so on, are taken from the finite field implementation of J-PAKE in :RFC:`8236#2`. Details of the computation for the key shares and zero-knowledge proofs are in :RFC:`8236` and :RFC:`8235`. + .. summary:: + The type of an object describing a PAKE cipher suite. - J-PAKE does not assign roles to the participants, so it is not necessary to call `psa_pake_set_role()`. + This is the object that represents the cipher suite used for a PAKE algorithm. + The PAKE cipher suite specifies the PAKE algorithm, and the options selected for that algorithm. + The cipher suite includes the following attributes: - J-PAKE requires both an application and a peer identity. - If the peer identity provided to `psa_pake_set_peer()` does not match the data received from the peer, then the call to `psa_pake_input()` for the `PSA_PAKE_STEP_ZK_PROOF` step will fail with :code:`PSA_ERROR_INVALID_SIGNATURE`. + * The PAKE algorithm itself. + * The hash algorithm, encoded within the PAKE algorithm. + * The PAKE primitive, which identifies the prime order group used for the key exchange operation. + See :secref:`pake-primitive`. + * Whether to confirm the shared secret. - The shared secret that is produced by J-PAKE is not suitable for use as an encryption key. - It must be used as an input to a key derivation operation to produce additional cryptographic keys. + This is an implementation-defined type. + Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. - The following steps demonstrate the application code for 'User' in :numref:`fig-jpake`. - The input and output steps must be carried out in exactly the same sequence as shown. + Before calling any function on a PAKE cipher suite object, the application must initialize it by any of the following means: - 1. To prepare a J-PAKE operation, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: + * Set the object to all-bits-zero, for example: .. code-block:: xref - psa_pake_operation_t jpake = PSA_PAKE_OPERATION_INIT; - - psa_pake_setup(&jpake, pake_key, &cipher_suite); - psa_pake_set_user(&jpake, ...); - psa_pake_set_peer(&jpake, ...); - - The password is provided as key ``pake_key``, with type :code:`PSA_KEY_TYPE_PASSWORD` or :code:`PSA_KEY_TYPE_PASSWORD_HASH`. - This can be the password text itself, in an agreed character encoding, or some value derived from the password as required by a higher level protocol. - - The key material is used as an array of bytes, which is converted to an integer as described in :cite-title:`SEC1` §2.3.8, before reducing it modulo *q*. - Here, *q* is the order of the group defined by the cipher-suite primitive. - `psa_pake_setup()` will return an error if the result of the conversion and reduction is ``0``. - - After setup, the key exchange flow for J-PAKE is as follows: + psa_pake_cipher_suite_t cipher_suite; + memset(&cipher_suite, 0, sizeof(cipher_suite)); - 1. To get the first round data that needs to be sent to the peer, call: + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: .. code-block:: xref - // Get g1 - psa_pake_output(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); - // Get V1, the ZKP public key for x1 - psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); - // Get r1, the ZKP proof for x1 - psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); - // Get g2 - psa_pake_output(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); - // Get V2, the ZKP public key for x2 - psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); - // Get r2, the ZKP proof for x2 - psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); - - #. To provide the first round data received from the peer to the operation, call: - - .. code-block:: xref + static psa_pake_cipher_suite_t cipher_suite; - // Set g3 - psa_pake_input(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); - // Set V3, the ZKP public key for x3 - psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); - // Set r3, the ZKP proof for x3 - psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); - // Set g4 - psa_pake_input(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); - // Set V4, the ZKP public key for x4 - psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); - // Set r4, the ZKP proof for x4 - psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); - - #. To get the second round data that needs to be sent to the peer, call: + * Initialize the object to the initializer `PSA_PAKE_CIPHER_SUITE_INIT`, for example: .. code-block:: xref - // Get A - psa_pake_output(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); - // Get V5, the ZKP public key for x2*s - psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); - // Get r5, the ZKP proof for x2*s - psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); + psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; - #. To provide the second round data received from the peer to the operation call: + * Assign the result of the function `psa_pake_cipher_suite_init()` to the object, for example: .. code-block:: xref - // Set B - psa_pake_input(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); - // Set V6, the ZKP public key for x4*s - psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); - // Set r6, the ZKP proof for x4*s - psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); - - #. To use the shared secret, extract it as a key-derivation key. For example, to extract a derivation key for HKDF-SHA-256: + psa_pake_cipher_suite_t cipher_suite; + cipher_suite = psa_pake_cipher_suite_init(); - .. code-block:: xref + Following initialization, the cipher-suite object contains the following values: - // Set up the key attributes - psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT; - psa_key_set_type(&att, PSA_KEY_TYPE_DERIVE); - psa_key_set_usage_flags(&att, PSA_KEY_USAGE_DERIVE); - psa_key_set_algorithm(&att, PSA_ALG_HKDF(PSA_ALG_SHA256)); + .. list-table:: + :header-rows: 1 + :widths: 1 4 + :align: left - // Get Ka=Kb=K - psa_key_id_t shared_key; - psa_pake_get_shared_key(&jpake, &att, &shared_key); + * - Attribute + - Value - For more information about the format of the values which are passed for each step, see :secref:`pake-steps`. + * - algorithm + - :code:`PSA_ALG_NONE` --- an invalid algorithm identifier. + * - primitive + - ``0`` --- an invalid PAKE primitive. + * - key confirmation + - `PSA_PAKE_CONFIRMED_KEY` --- requesting that the secret key is confirmed before it can be returned. - If the verification of a Zero-knowledge proof provided by the peer fails, then the corresponding call to `psa_pake_input()` for the `PSA_PAKE_STEP_ZK_PROOF` step will return :code:`PSA_ERROR_INVALID_SIGNATURE`. + Valid algorithm, primitive, and key confirmation values must be set when using a PAKE cipher suite. - .. warning:: + .. admonition:: Implementation note - At the end of this sequence there is a cryptographic guarantee that only a peer that used the same password is able to compute the same key. - But there is no guarantee that the peer is the participant it claims to be, or that the peer used the same password during the exchange. + Implementations are recommended to define the cipher-suite object as a simple data structure, with fields corresponding to the individual cipher suite attributes. + In such an implementation, each function ``psa_pake_cs_set_xxx()`` sets a field and the corresponding function ``psa_pake_cs_get_xxx()`` retrieves the value of the field. - At this point, authentication is implicit --- material encrypted or authenticated using the computed key can only be decrypted or verified by someone with the same key. - The peer is not authenticated at this point, and no action should be taken by the application which assumes that the peer is authenticated, for example, by accessing restricted files. + An implementations can report attribute values that are equivalent to the original one, but have a different encoding. + For example, an implementation can use a more compact representation for attributes where many bit-patterns are invalid or not supported, and store all values that it does not support as a special marker value. + In such an implementation, after setting an invalid value, the corresponding get function returns an invalid value which might not be the one that was originally stored. - To make the authentication explicit, there are various methods to confirm that both parties have the same key. See :RFC:`8236#5` for two examples. - - .. subsection:: Compatible key types - - | :code:`PSA_KEY_TYPE_PASSWORD` - | :code:`PSA_KEY_TYPE_PASSWORD_HASH` - -.. macro:: PSA_ALG_SPAKE2P_HMAC - :definition: /* specification-defined value */ +.. macro:: PSA_PAKE_CIPHER_SUITE_INIT + :definition: /* implementation-defined value */ .. summary:: - Macro to build the SPAKE2+ algorithm, using HMAC-based key confirmation. + This macro returns a suitable initializer for a PAKE cipher suite object of type `psa_pake_cipher_suite_t`. - .. param:: hash_alg - A hash algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_HASH(hash_alg)` is true. +.. function:: psa_pake_cipher_suite_init - .. return:: - A SPAKE2+ algorithm, using HMAC for key confirmation, parameterized by a specific hash. + .. summary:: + Return an initial value for a PAKE cipher suite object. - Unspecified if ``hash_alg`` is not a supported hash algorithm. + .. return:: psa_pake_cipher_suite_t - This is SPAKE2+, as defined by :RFC-title:`9383`, instantiated with the following parameters: +.. function:: psa_pake_cs_get_algorithm - * An elliptic curve group. - * A cryptographic hash function, ``hash_alg``. - * Key derivation function HKDF, using the same hash function, ``hash_alg``. - * Keyed MAC function HMAC, using the same hash function, ``hash_alg``. + .. summary:: + Retrieve the PAKE algorithm from a PAKE cipher suite. - For SPAKE2+, valid combinations of elliptic curve PAKE primitives and hash algorithms are described in :rfc:`9383#4`. + .. param:: const psa_pake_cipher_suite_t* cipher_suite + The cipher suite object to query. - SPAKE2+ includes confirmation of the shared secret key that results from the key exchange. + .. return:: psa_algorithm_t + The PAKE algorithm stored in the cipher suite object. - To select these parameters and set up the cipher suite, initialize a `psa_pake_cipher_suite_t` object, and call the following functions in any order: + .. admonition:: Implementation note - .. code-block:: xref + This is a simple accessor function that is not required to validate its inputs. + It can be efficiently implemented as a ``static inline`` function or a function-like macro. - psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; +.. function:: psa_pake_cs_set_algorithm - psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_SPAKE2P_HMAC(hash)); - psa_pake_cs_set_primitive(&cipher_suite, - PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, family, bits)); + .. summary:: + Declare the PAKE algorithm for the cipher suite. - For more information on selecting a specific Elliptic curve, see `PSA_PAKE_PRIMITIVE_TYPE_ECC`. + .. param:: psa_pake_cipher_suite_t* cipher_suite + The cipher suite object to write to. + .. param:: psa_algorithm_t alg + The PAKE algorithm to write: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_PAKE(alg)` is true. - .. _spake2p-flow: + .. return:: void - .. subsection:: SPAKE2+ protocol + This function overwrites any PAKE algorithm previously set in ``cipher_suite``. - There are two participants in the SPAKE2+ protocol: + .. admonition:: Implementation note - * The *Prover* takes the role of client. It uses the protocol to prove that it knows the secret password, and produce a shared secret. - * The *Verifier* takes the role of server. It uses the protocol to verify the client's proof, and produce a shared secret. + This is a simple accessor function that is not required to validate its inputs. + It can be efficiently implemented as a ``static inline`` function or a function-like macro. - The PAKE operation for SPAKE2+ only accepts a SPAKE2+ key type: +.. function:: psa_pake_cs_get_primitive - * The Prover requires a `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`, on the same elliptic curve specified in the PAKE cipher suite. - * The Verifier requires either a `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`, or a `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`, on the same elliptic curve specified in the PAKE cipher suite. + .. summary:: + Retrieve the primitive from a PAKE cipher suite. - These keys are derived from the initial SPAKE2+ password prior to starting the PAKE operation. - It is recommended to use a key-stretching derivation algorithm, for example PBKDF2. - This process can take place immediately before the PAKE operation, or derived at some earlier point and persisted in the key store. - Alternatively, the Verifier can be provisioned with the `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()` for the protocol, by the Prover, or some other agent. - :numref:`fig-spake2p-reg` illustrates some example SPAKE2+ key derivation flows. + .. param:: const psa_pake_cipher_suite_t* cipher_suite + The cipher suite object to query. - It is recommended that the Verifier stores only the public key, because disclosure of the public key does not enable an attacker to impersonate the Prover. + .. return:: psa_pake_primitive_t + The primitive stored in the cipher suite object. - .. figure:: /figure/spake2plus-reg.* - :name: fig-spake2p-reg + .. admonition:: Implementation note - Examples of SPAKE2+ key derivation procedures + This is a simple accessor function that is not required to validate its inputs. + It can be efficiently implemented as a ``static inline`` function or a function-like macro. - The variable names *w0*, *w1*, *L* are taken from the description of SPAKE2+ in :RFC:`9383`. +.. function:: psa_pake_cs_set_primitive - Details of the computation for the key derivation values are in :RFC:`9383#3.2`. + .. summary:: + Declare the primitive for a PAKE cipher suite. - Both participants in SPAKE2+ have an optional identity. - If no identity value is provided, then a zero-length string is used for that identity in the protocol. - If the participants do not supply the same identity values to the protocol, the computed secrets will be different, and key confirmation will fail. + .. param:: psa_pake_cipher_suite_t* cipher_suite + The cipher suite object to write to. + .. param:: psa_pake_primitive_t primitive + The PAKE primitive to write: a value of type `psa_pake_primitive_t`. + If this is ``0``, the primitive type in ``cipher_suite`` becomes unspecified. - The SPAKE2+ operation follows the protocol shown in :numref:`fig-spake2p`. + .. return:: void - .. figure:: /figure/spake2plus.* - :name: fig-spake2p + This function overwrites any primitive previously set in ``cipher_suite``. - The SPAKE2+ authentication and key confirmation protocol + .. admonition:: Implementation note - The variable names *w0*, *w1*, *L*, and so on, are taken from the description of SPAKE2+ in :RFC:`9383`. + This is a simple accessor function that is not required to validate its inputs. + It can be efficiently implemented as a ``static inline`` function or a function-like macro. - Details of the computation for the key shares is in :RFC:`9383#3.3` and confirmation values in :RFC:`9383#3.4`. +.. macro:: PSA_PAKE_CONFIRMED_KEY + :definition: 0 - The shared secret that is produced by SPAKE2+ is pseudorandom. - Although it can be used directly as an encryption key, it is recommended to use the shared secret as an input to a key derivation operation to produce additional cryptographic keys. + .. summary:: A key confirmation value that indicates an confirmed key in a PAKE cipher suite. - The following steps demonstrate the application code for both 'Prover' and 'Verifier' in :numref:`fig-spake2p`. + This key confirmation value will result in the PAKE algorithm exchanging data to verify that the shared key is identical for both parties. + This is the default key confirmation value in an initialized PAKE cipher suite object. - **Prover** - To prepare a SPAKE2+ operation for the Prover, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: + Some algorithms do not include confirmation of the shared key. - .. code-block:: xref +.. macro:: PSA_PAKE_UNCONFIRMED_KEY + :definition: 1 - psa_pake_operation_t spake2p_p = PSA_PAKE_OPERATION_INIT; + .. summary:: A key confirmation value that indicates an unconfirmed key in a PAKE cipher suite. - psa_pake_setup(&spake2p_p, pake_key_p, &cipher_suite); - psa_pake_set_role(&spake2p_p, PSA_PAKE_ROLE_CLIENT); + This key confirmation value will result in the PAKE algorithm terminating prior to confirming that the resulting shared key is identical for both parties. - The key ``pake_key_p`` is a SPAKE2+ key pair, `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`. + Some algorithms do not support returning an unconfirmed shared key. - The key must have the :code:`PSA_KEY_USAGE_??????` usage flag. + .. warning:: - **Prover** - Provide any additional, optional, parameters: + When the shared key is not confirmed as part of the PAKE operation, the application is responsible for mitigating risks that arise from the possible mismatch in the output keys. - .. code-block:: xref +.. function:: psa_pake_cs_get_key_confirmation - psa_pake_set_user(&spake2p_p, ...); // Prover identity - psa_pake_set_peer(&spake2p_p, ...); // Verifier identity - psa_pake_set_context(&spake2p_p, ...); + .. summary:: + Retrieve the key confirmation from a PAKE cipher suite. - **Verifier** - To prepare a SPAKE2+ operation for the Verifier, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: + .. param:: const psa_pake_cipher_suite_t* cipher_suite + The cipher suite object to query. - .. code-block:: xref + .. return:: uint32_t + A key confirmation value: either `PSA_PAKE_CONFIRMED_KEY` or `PSA_PAKE_UNCONFIRMED_KEY`. - psa_pake_operation_t spake2p_v = PSA_PAKE_OPERATION_INIT; + .. admonition:: Implementation note - psa_pake_setup(&spake2p_v, pake_key_v, &cipher_suite); - psa_pake_set_role(&spake2p_v, PSA_PAKE_ROLE_SERVER); + This is a simple accessor function that is not required to validate its inputs. + It can be efficiently implemented as a ``static inline`` function or a function-like macro. - The key ``pake_key_v`` is a SPAKE2+ key pair, `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`, or public key, `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`. +.. function:: psa_pake_cs_set_key_confirmation - The key must have the :code:`PSA_KEY_USAGE_??????` usage flag. + .. summary:: + Declare the key confirmation from a PAKE cipher suite. - **Verifier** - Provide any additional, optional, parameters: + .. param:: psa_pake_cipher_suite_t* cipher_suite + The cipher suite object to write to. + .. param:: uint32_t key_confirmation + The key confirmation value to write: either `PSA_PAKE_CONFIRMED_KEY` or `PSA_PAKE_UNCONFIRMED_KEY`. - .. code-block:: xref + .. return:: void - psa_pake_set_user(&spake2p_v, ...); // Verifier identity - psa_pake_set_peer(&spake2p_v, ...); // Prover identity - psa_pake_set_context(&spake2p_v, ...); + This function overwrites any key confirmation previously set in ``cipher_suite``. - After setup, the key exchange flow for SPAKE2+ is as follows: + The documentation of individual PAKE algorithms specifies which key confirmation values are valid for the algorithm. - **Prover** - To get the key share to send to the Verifier, call: + .. admonition:: Implementation note - .. code-block:: xref + This is a simple accessor function that is not required to validate its inputs. + It can be efficiently implemented as a ``static inline`` function or a function-like macro. - // Get shareP - psa_pake_output(&spake2p_p, PSA_PAKE_STEP_KEY_SHARE, ...); +.. _pake-roles: - **Verifier** - To provide and validate the Prover key share, call: +PAKE roles +~~~~~~~~~~ - .. code-block:: xref +Some PAKE algorithms need to know which role each participant is taking in the algorithm. +For example: - // Set shareP - psa_pake_input(&spake2p_v, PSA_PAKE_STEP_KEY_SHARE, ...); +* Augmented PAKE algorithms typically have a client and a server participant. +* Some symmetric PAKE algorithms need to assign an order to the participants. - **Verifier** - To get the Verifier key share and confirmation value to send to the Prover, call: +.. typedef:: uint8_t psa_pake_role_t - .. code-block:: xref + .. summary:: + Encoding of the application role in a PAKE algorithm. - // Get shareV - psa_pake_output(&spake2p_v, PSA_PAKE_STEP_KEY_SHARE, ...); - // Get confirmV - psa_pake_output(&spake2p_v, PSA_PAKE_STEP_CONFIRM, ...); + This type is used to encode the application's role in the algorithm being executed. + For more information see the documentation of individual PAKE role constants. - **Prover** - To provide and validate the Verifier key share, and confirm the Verifier key, call: +.. macro:: PSA_PAKE_ROLE_NONE + :definition: ((psa_pake_role_t)0x00) - .. code-block:: xref + .. summary:: + A value to indicate no role in a PAKE algorithm. - // Set shareV - psa_pake_input(&spake2p_p, PSA_PAKE_STEP_KEY_SHARE, ...); - // Set confirmV - psa_pake_input(&spake2p_p, PSA_PAKE_STEP_KEY_CONFIRM, ...); + This value can be used in a call to `psa_pake_set_role()` for symmetric PAKE algorithms which do not assign roles. - **Prover** - To get the Prover key confirmation value to send to the Verifier, call: +.. macro:: PSA_PAKE_ROLE_FIRST + :definition: ((psa_pake_role_t)0x01) - .. code-block:: xref + .. summary:: + The first peer in a balanced PAKE. - // Get confirmV - psa_pake_output(&spake2p_p, PSA_PAKE_STEP_CONFIRM, ...); + Although balanced PAKE algorithms are symmetric, some of them need the peers to be ordered for the transcript calculations. + If the algorithm does not need a specific ordering, then either do not call `psa_pake_set_role()`, or use `PSA_PAKE_ROLE_NONE` as the role parameter. - **Verifier** - To confirm the Prover key, call: +.. macro:: PSA_PAKE_ROLE_SECOND + :definition: ((psa_pake_role_t)0x02) - .. code-block:: xref + .. summary:: + The second peer in a balanced PAKE. - // Set shareP - psa_pake_input(&spake2p_v, PSA_PAKE_STEP_CONFIRM, ...); + Although balanced PAKE algorithms are symmetric, some of them need the peers to be ordered for the transcript calculations. + If the algorithm does not need a specific ordering, then either do not call `psa_pake_set_role()`, or use `PSA_PAKE_ROLE_NONE` as the role parameter. - **Prover** - To use the shared secret, extract it as a key-derivation key. - For example, to extract a derivation key for HKDF-SHA-256: +.. macro:: PSA_PAKE_ROLE_CLIENT + :definition: ((psa_pake_role_t)0x11) - .. code-block:: xref + .. summary:: + The client in an augmented PAKE. - // Set up the key attributes - psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT; - psa_key_set_type(&att, PSA_KEY_TYPE_DERIVE); - psa_key_set_usage_flags(&att, PSA_KEY_USAGE_DERIVE); - psa_key_set_algorithm(&att, PSA_ALG_HKDF(PSA_ALG_SHA256)); + Augmented PAKE algorithms need to differentiate between client and server. - // Get K_shared - psa_key_id_t shared_key; - psa_pake_get_shared_key(&spake2p_p, &att, &shared_key); +.. macro:: PSA_PAKE_ROLE_SERVER + :definition: ((psa_pake_role_t)0x12) - **Verifier** - To use the shared secret, extract it as a key-derivation key. - The same key attributes can be used as the Prover: + .. summary:: + The server in an augmented PAKE. - .. code-block:: xref + Augmented PAKE algorithms need to differentiate between client and server. - // Get K_shared - psa_key_id_t shared_key; - psa_pake_get_shared_key(&spake2p_v, &att, &shared_key); - For more information about the format of the values which are passed for each step, see :secref:`pake-steps`. +.. _pake-steps: - If the validation of a key share fails, then the corresponding call to `psa_pake_input()` for the `PSA_PAKE_STEP_KEY_SHARE` step will return :code:`PSA_ERROR_INVALID_ARGUMENT`. - If the verification of a key confirmation value fails, then the corresponding call to `psa_pake_input()` for the `PSA_PAKE_STEP_CONFIRM` step will return :code:`PSA_ERROR_INVALID_SIGNATURE`. +PAKE step types +~~~~~~~~~~~~~~~ - .. subsection:: Compatible key types +.. typedef:: uint8_t psa_pake_step_t - | :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR` - | :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY` (verification only) + .. summary:: + Encoding of input and output steps for a PAKE algorithm. + Some PAKE algorithms need to exchange more data than a single key share. + This type encodes additional input and output steps for such algorithms. -.. macro:: PSA_ALG_SPAKE2P_CMAC - :definition: /* specification-defined value */ +.. macro:: PSA_PAKE_STEP_KEY_SHARE + :definition: ((psa_pake_step_t)0x01) .. summary:: - Macro to build the SPAKE2+ algorithm, using CMAC-based key confirmation. + The key share being sent to or received from the peer. - .. param:: hash_alg - A hash algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_HASH(hash_alg)` is true. + The format for both input and output using this step is the same as the format for public keys on the group specified by the PAKE operation's primitive. - .. return:: - A SPAKE2+ algorithm, using CMAC for key confirmation, parameterized by a specific hash. + The public key formats are defined in the documentation for :code:`psa_export_public_key()`. - Unspecified if ``hash_alg`` is not a supported hash algorithm. + For information regarding how the group is determined, consult the documentation `PSA_PAKE_PRIMITIVE()`. - This is SPAKE2+, as defined by :RFC-title:`9383`, instantiated with the following parameters: +.. macro:: PSA_PAKE_STEP_ZK_PUBLIC + :definition: ((psa_pake_step_t)0x02) - * An elliptic curve group. - * A cryptographic hash function, ``hash_alg``. - * Key derivation function HKDF, using the same hash function, ``hash_alg``. - * Keyed MAC function CMAC-AES-128. + .. summary:: + A Schnorr NIZKP public key. + + This is the ephemeral public key in the Schnorr Non-Interactive Zero-Knowledge Proof, this is the value denoted by *V* in :RFC:`8235`. - For SPAKE2+, valid combinations of elliptic curve PAKE primitives and hash algorithms for use with CMAC-AES-128 are described in :rfc:`9383#4`. + The format for both input and output at this step is the same as that for public keys on the group specified by the PAKE operation's primitive. - SPAKE2+ includes confirmation of the shared secret key that results from the key exchange. + For more information on the format, consult the documentation of :code:`psa_export_public_key()`. - To select these parameters and set up the cipher suite, initialize a `psa_pake_cipher_suite_t` object, and call the following functions in any order: + For information regarding how the group is determined, consult the documentation `PSA_PAKE_PRIMITIVE()`. - .. code-block:: xref +.. macro:: PSA_PAKE_STEP_ZK_PROOF + :definition: ((psa_pake_step_t)0x03) - psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; + .. summary:: + A Schnorr NIZKP proof. - psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_SPAKE2P_CMAC(hash)); - psa_pake_cs_set_primitive(&cipher_suite, - PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, family, bits)); + This is the proof in the Schnorr Non-Interactive Zero-Knowledge Proof, this is the value denoted by *r* in :RFC:`8235`. - For more information on selecting a specific Elliptic curve, see `PSA_PAKE_PRIMITIVE_TYPE_ECC`. + Both for input and output, the value at this step is an integer less than the order of the group specified by the PAKE operation's primitive. + The format depends on the group as well: - The SPAKE2+ protocol flow and usage for `PSA_ALG_SPAKE2P_CMAC()` is the same as for `PSA_ALG_SPAKE2P_HMAC()`. See :secref:`spake2p-flow`. + * For Montgomery curves, the encoding is little endian. + * For other Elliptic curves, and for Diffie-Hellman groups, the encoding is big endian. See :cite:`SEC1` §2.3.8. - .. subsection:: Compatible key types + In both cases leading zeroes are permitted as long as the length in bytes does not exceed the byte length of the group order. - | :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR` - | :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY` (verification only) + For information regarding how the group is determined, consult the documentation `PSA_PAKE_PRIMITIVE()`. -.. macro:: PSA_ALG_SPAKE2P_MATTER - :definition: ((psa_algoirithm_t)0x0A000609) +.. macro:: PSA_PAKE_STEP_CONFIRM + :definition: ((psa_pake_step_t)0x04) .. summary:: - The SPAKE2+ algorithm, as used by the Matter v1 specification. + The key confirmation value. - This is the PAKE algorithm specified as MATTER_PAKE in :cite-title:`MATTER`. This is based on draft-02 of the SPAKE2+ protocol, :cite-title:`SPAKE2P-2`. + This value is used during the key confirmation phase of a PAKE protocol. The format of the value depends on the algorithm and cipher suite: - :cite:`MATTER` specifies a single cipher suite, as follows: + * For :code:`PSA_ALG_SPAKE2P`, the format for both input and output at this step is the same as the output of the MAC algorithm specified in the cipher suite. - * The NIST P-256 elliptic curve (secp256r1). - * The SHA256 hash function. - * Key derivation function HKDF-SHA256. - * Keyed MAC function HMAC-SHA256. +.. _pake-operation: - SPAKE2+ includes confirmation of the shared secret key that results from the key exchange. +Multi-part PAKE operations +~~~~~~~~~~~~~~~~~~~~~~~~~~ - To set up the cipher suite for `PSA_ALG_SPAKE2P_MATTER`, initialize a `psa_pake_cipher_suite_t` object, and call the following functions in any order: +.. typedef:: /* implementation-defined type */ psa_pake_operation_t - .. code-block:: xref + .. summary:: + The type of the state object for PAKE operations. - psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; + Before calling any function on a PAKE operation object, the application must initialize it by any of the following means: - psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_SPAKE2P_MATTER); - psa_pake_cs_set_primitive(&cipher_suite, - PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, - PSA_ECC_FAMILY_SECP_R1, 256)); + * Set the object to all-bits-zero, for example: - This algorithm is compatible with the SPAKE2+ key types, key derivation, protocol flow, and the API usage described in :secref:`pake-keys` and :secref:`spake2p-flow`. However, the following aspects are different: + .. code-block:: xref - * The key schedule is different. This affects the computation of the shared secret and key confirmation values. - * The protocol inputs and outputs have been renamed between draft-02 and the final RFC, as follows: + psa_pake_operation_t operation; + memset(&operation, 0, sizeof(operation)); - .. csv-table:: - :header-rows: 1 - :widths: auto - :align: left + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: - RFC 9383, Draft-02 - shareP, pA - shareV, pB - confirmP, cA - confirmV, cB - K_shared, Ke + .. code-block:: xref - .. subsection:: Compatible key types + static psa_pake_operation_t operation; - | :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR` - | :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY` (verification only) + * Initialize the object to the initializer `PSA_PAKE_OPERATION_INIT`, for example: + .. code-block:: xref -.. _pake-primitive: + psa_pake_operation_t operation = PSA_PAKE_OPERATION_INIT; -PAKE primitives -~~~~~~~~~~~~~~~ + * Assign the result of the function `psa_pake_cipher_suite_init()` to the object, for example: -A PAKE algorithm specifies a sequence of interactions between the participants. -Many PAKE algorithms are designed to allow different cryptographic primitives to be used for the key establishment operation, so long as all the participants are using the same underlying cryptography. + .. code-block:: xref -The cryptographic primitive for a PAKE operation is specified using a `psa_pake_primitive_t` value, which can be constructed using the `PSA_PAKE_PRIMITIVE()` macro, or can be provided as a numerical constant value. + psa_pake_operation_t operation; + operation = psa_pake_operation_init(); -A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_pake_cipher_suite_t`, which fully specifies the PAKE operation to be carried out. + This is an implementation-defined type. + Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. -.. typedef:: uint32_t psa_pake_primitive_t +.. macro:: PSA_PAKE_OPERATION_INIT + :definition: /* implementation-defined value */ .. summary:: - Encoding of the primitive associated with the PAKE. - - PAKE primitive values are constructed using `PSA_PAKE_PRIMITIVE()`. + This macro returns a suitable initializer for a PAKE operation object of type `psa_pake_operation_t`. - :numref:`fig-pake-primitive` shows how the components of the primitive are encoded into a `psa_pake_primitive_t` value. +.. function:: psa_pake_operation_init - .. figure:: /figure/pake_primitive.* - :name: fig-pake-primitive + .. summary:: + Return an initial value for a PAKE operation object. - PAKE primitive encoding + .. return:: psa_pake_operation_t - The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. These can be used to set key attributes for keys used in PAKE algorithms. +.. function:: psa_pake_setup - .. rationale:: + .. summary:: + Setup a password-authenticated key exchange. - An integral type is required for `psa_pake_primitive_t` to enable values of this type to be compile-time-constants. This allows them to be used in ``case`` statements, and used to calculate static buffer sizes with `PSA_PAKE_OUTPUT_SIZE()` and `PSA_PAKE_INPUT_SIZE()`. + .. param:: psa_pake_operation_t *operation + The operation object to set up. + It must have been initialized as per the documentation for `psa_pake_operation_t` and not yet in use. + .. param:: psa_key_id_t password_key + Identifier of the key holding the password or a value derived from the password. + It must remain valid until the operation terminates. -.. typedef:: uint8_t psa_pake_primitive_type_t + The valid key types depend on the PAKE algorithm, and participant role. + Refer to the documentation of individual PAKE algorithms for more information, see :secref:`pake-algorithms`. + + The key must permit the usage :code:`PSA_KEY_USAGE_DERIVE`. + .. param:: const psa_pake_cipher_suite_t *cipher_suite + The cipher suite to use. + A PAKE cipher suite fully characterizes a PAKE algorithm, including the PAKE algorithm. + + The cipher suite must be compatible with the key type of ``password_key``. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. The operation is now active. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to :code:`psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_HANDLE + ``password_key`` is not a valid key identifier. + .. retval:: PSA_ERROR_NOT_PERMITTED + ``psssword_key`` does not have the :code:`PSA_KEY_USAGE_DERIVE` flag, or it does not permit the algorithm in ``cipher_suite``. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The algorithm in ``cipher_suite`` is not a PAKE algorithm, or encodes an invalid hash algorithm. + * The PAKE primitive in ``cipher_suite`` is not compatible with the PAKE algorithm. + * The key confirmation value in ``cipher_suite`` is not compatible with the PAKE algorithm and primitive. + * The key type for ``password_key`` is not :code:`PSA_KEY_TYPE_PASSWORD` or :code:`PSA_KEY_TYPE_PASSWORD_HASH`. + * ``password_key`` is not compatible with ``cipher_suite``. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * The algorithm in ``cipher_suite`` is not a supported PAKE algorithm, or encodes an unsupported hash algorithm. + * The PAKE primitive in ``cipher_suite`` is not supported or not compatible with the PAKE algorithm. + * The key confirmation value in ``cipher_suite`` is not supported, or not compatible, with the PAKE algorithm and primitive. + * The key type or key size of ``password_key`` is not supported with ``cipher suite``. + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + The sequence of operations to set up a password-authenticated key exchange operation is as follows: + + 1. Allocate a PAKE operation object which will be passed to all the functions listed here. + #. Initialize the operation object with one of the methods described in the documentation for `psa_pake_operation_t`. + For example, using `PSA_PAKE_OPERATION_INIT`. + #. Call `psa_pake_setup()` to specify the cipher suite. + #. Call ``psa_pake_set_xxx()`` functions on the operation to complete the setup. + The exact sequence of ``psa_pake_set_xxx()`` functions that needs to be called depends on the algorithm in use. + + A typical sequence of calls to perform a password-authenticated key exchange: + + 1. Call :code:`psa_pake_output(operation, PSA_PAKE_STEP_KEY_SHARE, ...)` to get the key share that needs to be sent to the peer. + #. Call :code:`psa_pake_input(operation, PSA_PAKE_STEP_KEY_SHARE, ...)` to provide the key share that was received from the peer. + #. Depending on the algorithm additional calls to `psa_pake_output()` and `psa_pake_input()` might be necessary. + #. Call `psa_pake_get_shared_key()` to access the shared secret. + + Refer to the documentation of individual PAKE algorithms for details on the required set up and operation for each algorithm, and for constraints on the format and content of valid passwords. + See :secref:`pake-algorithms`. + + After a successful call to `psa_pake_setup()`, the operation is active, and the application must eventually terminate the operation. + The following events terminate an operation: + + * A successful call to `psa_pake_get_shared_key()`. + * A call to `psa_pake_abort()`. + + If `psa_pake_setup()` returns an error, the operation object is unchanged. + If a subsequent function call with an active operation returns an error, the operation enters an error state. + + To abandon an active operation, or reset an operation in an error state, call `psa_pake_abort()`. + + .. + See :secref:`multi-part-operations`. :issue:`add this when integrated to main specification` + +.. function:: psa_pake_set_role .. summary:: - Encoding of the type of the PAKE's primitive. + Set the application role for a password-authenticated key exchange. - The range of PAKE primitive type values is divided as follows: + .. param:: psa_pake_operation_t *operation + Active PAKE operation. + .. param:: psa_pake_role_t role + A value of type `psa_pake_role_t` indicating the application role in the PAKE algorithm. + See :secref:`pake-roles`. - :code:`0x00` - Reserved as an invalid primitive type. - :code:`0x01 – 0x7f` - Specification-defined primitive type. - Primitive types defined by this standard always have bit 7 clear. - Unallocated primitive type values in this range are reserved for future use. - :code:`0x80 – 0xff` - Implementation-defined primitive type. - Implementations that define additional primitive types must use an encoding with bit 7 set. + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: - For specification-defined primitive types, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. + * The operation state is not valid: it must be active, and `psa_pake_set_role()`, `psa_pake_input()`, and `psa_pake_output()` must not have been called yet. + * The library requires initializing by a call to :code:`psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + ``role`` is not a valid PAKE role in the operation's algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + ``role`` is not a valid PAKE role, or is not supported for the operation's algorithm. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED -.. macro:: PSA_PAKE_PRIMITIVE_TYPE_ECC - :definition: ((psa_pake_primitive_type_t)0x01) + Not all PAKE algorithms need to differentiate the communicating participants. + For PAKE algorithms that do not require a role to be specified, the application can do either of the following: + + * Not call `psa_pake_set_role()` on the PAKE operation. + * Call `psa_pake_set_role()` with the `PSA_PAKE_ROLE_NONE` role. + + Refer to the documentation of individual PAKE algorithms for more information. + See :secref:`pake-algorithms`. + +.. function:: psa_pake_set_user .. summary:: - The PAKE primitive type indicating the use of elliptic curves. + Set the user ID for a password-authenticated key exchange. - The values of the ``family`` and ``bits`` components of the PAKE primitive identify a specific elliptic curve, using the same mapping that is used for ECC keys. - See the definition of ``psa_ecc_family_t``. - Here ``family`` and ``bits`` refer to the values used to construct the PAKE primitive using `PSA_PAKE_PRIMITIVE()`. + .. param:: psa_pake_operation_t *operation + Active PAKE operation. + .. param:: const uint8_t *user_id + The user ID to authenticate with. + .. param:: size_t user_id_len + Size of the ``user_id`` buffer in bytes. - Input and output during the operation can involve group elements and scalar values: + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: - * The format for group elements is the same as that for public keys on the specific Elliptic curve. - For more information, consult the documentation of key formats in `[PSA-CRYPT]`. - * The format for scalars is the same as that for private keys on the specific Elliptic curve. - For more information, consult the documentation of key formats in `[PSA-CRYPT]`. + * The operation state is not valid: it must be active, and `psa_pake_set_user()`, `psa_pake_input()`, and `psa_pake_output()` must not have been called yet. + * The library requires initializing by a call to :code:`psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + ``user_id`` is not valid for the operation's algorithm and cipher suite. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The value of ``user_id`` is not supported by the implementation. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + Call this function to set the user ID. + For PAKE algorithms that associate a user identifier with both participants in the session, also call `psa_pake_set_peer()` with the peer ID. + For PAKE algorithms that associate a single user identifier with the session, call `psa_pake_set_user()` only. -.. macro:: PSA_PAKE_PRIMITIVE_TYPE_DH - :definition: ((psa_pake_primitive_type_t)0x02) + Refer to the documentation of individual PAKE algorithms for more information. + See :secref:`pake-algorithms`. + +.. function:: psa_pake_set_peer .. summary:: - The PAKE primitive type indicating the use of Diffie-Hellman groups. + Set the peer ID for a password-authenticated key exchange. - The values of the ``family`` and ``bits`` components of the PAKE primitive identify a specific Diffie-Hellman group, using the same mapping that is used for Diffie-Hellman keys. - See the definition of ``psa_dh_family_t``. - Here ``family`` and ``bits`` refer to the values used to construct the PAKE primitive using `PSA_PAKE_PRIMITIVE()`. + .. param:: psa_pake_operation_t *operation + Active PAKE operation. + .. param:: const uint8_t *peer_id + The peer's ID to authenticate. + .. param:: size_t peer_id_len + Size of the ``peer_id`` buffer in bytes. - Input and output during the operation can involve group elements and scalar values: + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: - * The format for group elements is the same as that for public keys in the specific Diffie-Hellman group. - For more information, consult the documentation of key formats in `[PSA-CRYPT]`. - * The format for scalars is the same as that for private keys in the specific Diffie-Hellman group. - For more information, consult the documentation of key formats in `[PSA-CRYPT]`. + * The operation state is not valid: it must be active, and `psa_pake_set_peer()`, `psa_pake_input()`, and `psa_pake_output()` must not have been called yet. + * Calling `psa_pake_set_peer()` is invalid with the operation's algorithm. + * The library requires initializing by a call to :code:`psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + ``peer_id`` is not valid for the operation's algorithm and cipher suite. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The value of ``peer_id`` is not supported by the implementation. + .. retval:: PSA_ERROR_NOT_SUPPORTED + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + Call this function in addition to `psa_pake_set_user()` for PAKE algorithms that associate a user identifier with both participants in the session. + For PAKE algorithms that associate a single user identifier with the session, call `psa_pake_set_user()` only. -.. typedef:: uint8_t psa_pake_family_t + Refer to the documentation of individual PAKE algorithms for more information. + See :secref:`pake-algorithms`. + +.. function:: psa_pake_set_context .. summary:: - Encoding of the family of the primitive associated with the PAKE. + Set the context data for a password-authenticated key exchange. - For more information on the family values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. + .. param:: psa_pake_operation_t *operation + Active PAKE operation. + .. param:: const uint8_t *context + The peer's ID to authenticate. + .. param:: size_t context_len + Size of the ``context`` buffer in bytes. -.. macro:: PSA_PAKE_PRIMITIVE - :definition: /* specification-defined value */ + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active, and `psa_pake_set_context()`, `psa_pake_input()`, and `psa_pake_output()` must not have been called yet. + * Calling `psa_pake_set_context()` is invalid with the operation's algorithm. + * The library requires initializing by a call to :code:`psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + ``context`` is not valid for the operation's algorithm and cipher suite. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The value of ``context`` is not supported by the implementation. + .. retval:: PSA_ERROR_NOT_SUPPORTED + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + + Call this function for PAKE algorithms that accept additional context data as part of the protocol setup. + + Refer to the documentation of individual PAKE algorithms for more information. + See :secref:`pake-algorithms`. + +.. function:: psa_pake_output .. summary:: - Construct a PAKE primitive from type, family and bit-size. + Get output for a step of a password-authenticated key exchange. + + .. param:: psa_pake_operation_t *operation + Active PAKE operation. + .. param:: psa_pake_step_t step + The step of the algorithm for which the output is requested. + .. param:: uint8_t *output + Buffer where the output is to be written. + The format of the output depends on the ``step``, see :secref:`pake-steps`. + .. param:: size_t output_size + Size of the ``output`` buffer in bytes. + This must be appropriate for the cipher suite and output step: + + * A sufficient output size is :code:`PSA_PAKE_OUTPUT_SIZE(alg, primitive, step)` where ``alg`` and ``primitive`` are the PAKE algorithm and primitive in the operation's cipher suite, and ``step`` is the output step. + * `PSA_PAKE_OUTPUT_MAX_SIZE` evaluates to the maximum output size of any supported PAKE algorithm, primitive and step. + .. param:: size_t *output_length + On success, the number of bytes of the returned output. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The first ``(*output_length)`` bytes of ``output`` contain the output. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active and fully set up, and this call must conform to the algorithm's requirements for ordering of input and output steps. + * The library requires initializing by a call to :code:`psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + ``step`` is not compatible with the operation's algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + ``step`` is not supported with the operation's algorithm. + .. retval:: PSA_ERROR_BUFFER_TOO_SMALL + The size of the ``output`` buffer is too small. + `PSA_PAKE_OUTPUT_SIZE()` or `PSA_PAKE_OUTPUT_MAX_SIZE` can be used to determine a sufficient buffer size. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + Depending on the algorithm being executed, you might need to call this function several times or you might not need to call this at all. + + The exact sequence of calls to perform a password-authenticated key exchange depends on the algorithm in use. + Refer to the documentation of individual PAKE algorithms for more information. + See :secref:`pake-algorithms`. + + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_pake_abort()`. + +.. function:: psa_pake_input + + .. summary:: + Provide input for a step of a password-authenticated key exchange. + + .. param:: psa_pake_operation_t *operation + Active PAKE operation. + .. param:: psa_pake_step_t step + The step for which the input is provided. + .. param:: const uint8_t *input + Buffer containing the input. + The format of the input depends on the ``step``, see :secref:`pake-steps`. + .. param:: size_t input_length + Size of the ``input`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active and fully set up, and this call must conform to the algorithm's requirements for ordering of input and output steps. + * The library requires initializing by a call to :code:`psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * ``step`` is not compatible with the operation's algorithm. + * The input is not valid for the operation's algorithm, cipher suite or ``step``. + .. retval:: PSA_ERROR_INVALID_SIGNATURE + The verification fails for a `PSA_PAKE_STEP_ZK_PROOF` or `PSA_PAKE_STEP_CONFIRM` input step. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * ``step`` is not supported with the operation's algorithm. + * The input is not supported for the operation's algorithm, cipher suite or ``step``. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID - .. param:: pake_type - The type of the primitive: a value of type `psa_pake_primitive_type_t`. - .. param:: pake_family - The family of the primitive. - The type and interpretation of this parameter depends on ``pake_type``. - For more information, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. - .. param:: pake_bits - The bit-size of the primitive: a value of type ``size_t``. - The interpretation of this parameter depends on ``pake_type`` and ``family``. - For more information, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. + Depending on the algorithm being executed, you might need to call this function several times or you might not need to call this at all. - .. return:: psa_pake_primitive_t - The constructed primitive value. - Return ``0`` if the requested primitive can't be encoded as `psa_pake_primitive_t`. + The exact sequence of calls to perform a password-authenticated key exchange depends on the algorithm in use. + Refer to the documentation of individual PAKE algorithms for more information. + See :secref:`pake-algorithms`. - A PAKE primitive value is used to specify a PAKE operation, as part of a PAKE cipher suite. + `PSA_PAKE_INPUT_SIZE()` or `PSA_PAKE_INPUT_MAX_SIZE` can be used to allocate buffers of sufficient size to transfer inputs that are received from the peer into the operation. - The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_pake_abort()`. -.. macro:: PSA_PAKE_PRIMITIVE_GET_TYPE - :definition: /* specification-defined value */ +.. function:: psa_pake_get_shared_key .. summary:: - Extract the PAKE primitive type from a PAKE primitive. + Extract the shared secret from the PAKE as a key. - .. param:: pake_primitive - A PAKE primitive: a value of type `psa_pake_primitive_t`. + .. param:: psa_pake_operation_t *operation + Active PAKE operation. + .. param:: const psa_key_attributes_t * attributes + The attributes for the new key. + This function uses the attributes as follows: - .. return:: psa_pake_primitive_type_t - The PAKE primitive type, if ``pake_primitive`` is a supported PAKE primitive. Unspecified if ``pake_primitive`` is not a supported PAKE primitive. + * The key type is required. + All PAKE algorithms can output a key of type :code:`PSA_KEY_TYPE_DERIVE` or :code:`PSA_KEY_TYPE_HMAC`. + PAKE algorithms that produce a pseudo-random shared secret, can also output block-cipher key types, for example :code:`PSA_KEY_TYPE_AES`. + Refer to the documentation of individual PAKE algorithms for more information. + See :secref:`pake-algorithms`. + * The key size in ``attributes`` must be zero. + The returned key size is always determined from the PAKE shared secret. + * The key permitted-algorithm policy is required for keys that will be used for a cryptographic operation. -.. macro:: PSA_PAKE_PRIMITIVE_GET_FAMILY - :definition: /* specification-defined value */ + .. see :secref:`permitted-algorithms`. - .. summary:: - Extract the family from a PAKE primitive. + * The key usage flags define what operations are permitted with the key. - .. param:: pake_primitive - A PAKE primitive: a value of type `psa_pake_primitive_t`. + .. see :secref:`key-usage-flags`. - .. return:: psa_pake_family_t - The PAKE primitive family, if ``pake_primitive`` is a supported PAKE primitive. Unspecified if ``pake_primitive`` is not a supported PAKE primitive. + * The key lifetime and identifier are required for a persistent key. - For more information on the family values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. + .. note:: + This is an input parameter: it is not updated with the final key attributes. + The final attributes of the new key can be queried by calling :code:`psa_get_key_attributes()` with the key's identifier. + .. param:: psa_key_id_t * key + On success, an identifier for the newly created key. :code:`PSA_KEY_ID_NULL` on failure. -.. macro:: PSA_PAKE_PRIMITIVE_GET_BITS - :definition: /* specification-defined value */ - .. summary:: - Extract the bit-size from a PAKE primitive. + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + If the key is persistent, the key material and the key's metadata have been saved to persistent storage. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: - .. param:: pake_primitive - A PAKE primitive: a value of type `psa_pake_primitive_t`. + * The state of PAKE operation ``operation`` is not valid: it must be ready to return the shared secret. - .. return:: size_t - The PAKE primitive bit-size, if ``pake_primitive`` is a supported PAKE primitive. Unspecified if ``pake_primitive`` is not a supported PAKE primitive. + For an unconfirmed key, this will be when the key-exchange output and input steps are complete, but prior to any key-confirmation output and input steps. - For more information on the bit-size values, see `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH`. + For a confirmed key, this will be when all key-exchange and key-confirmation output and input steps are complete. + * The library requires initializing by a call to :code:`psa_crypto_init()`. + .. retval:: PSA_ERROR_ALREADY_EXISTS + This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The key attributes, as a whole, are not supported for creation from a PAKE secret, either by the implementation in general or in the specified storage location. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: -.. _pake-cipher-suite: + * The key type is not valid for output from this operation's algorithm. + * The key size is nonzero. + * The key lifetime is invalid. + * The key identifier is not valid for the key lifetime. + * The key usage flags include invalid values. + * The key's permitted-usage algorithm is invalid. + * The key attributes, as a whole, are invalid. + .. retval:: PSA_ERROR_NOT_PERMITTED + The implementation does not permit creating a key with the specified attributes due to some implementation-specific policy. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID -PAKE cipher suites -~~~~~~~~~~~~~~~~~~ + This is the final call in a PAKE operation, which retrieves the shared secret as a key. + It is recommended that this key is used as an input to a key derivation operation to produce additional cryptographic keys. + For some PAKE algorithms, the shared secret is also suitable for use as a key in cryptographic operations such as encryption. + Refer to the documentation of individual PAKE algorithms for more information, see :secref:`pake-algorithms`. -Most PAKE algorithms have parameters that must be specified by the application. These parameters include the following: + Depending on the key confirmation requested in the cipher suite, `psa_pake_get_shared_key()` must be called either before or after the key-confirmation output and input steps for the PAKE algorithm. + The key confirmation affects the guarantees that can be made about the shared key: -* The cryptographic primitive used for key establishment, specified using a `PAKE primitive `. -* A cryptographic hash algorithm. -* Whether the application requires the shared secret before, or after, it is confirmed. + .. list-table:: + :class: borderless + :widths: 1 4 -The hash algorithm is encoded into the PAKE algorithm identifier. The `psa_pake_cipher_suite_t` object is used to fully specify a PAKE operation, combining the PAKE protocol with all of the above parameters. + * - **Unconfirmed key** + - If the cipher suite used to set up the operation requested an unconfirmed key, the application must call `psa_pake_get_shared_key()` after the key-exchange output and input steps are completed. + The PAKE algorithm provides a cryptographic guarantee that only a peer who used the same password, and identity inputs, is able to compute the same key. + However, there is no guarantee that the peer is the participant it claims to be, and was able to compute the same key. -A PAKE cipher suite is required when setting up a PAKE operation in `psa_pake_setup()`. + Since the peer is not authenticated, no action should be taken that assumes that the peer is who it claims to be. + For example, do not access restricted files on the peer's behalf until an explicit authentication has succeeded. + .. note:: + Some PAKE algorithms do not enable the output of the shared secret until it has been confirmed. -.. typedef:: /* implementation-defined type */ psa_pake_cipher_suite_t + * - **Confirmed key** + - If the cipher suite used to set up the operation requested a confirmed key, the application must call `psa_pake_get_shared_key()` after the key-exchange and key-confirmation output and input steps are completed. - .. summary:: - The type of an object describing a PAKE cipher suite. + Following key confirmation, the PAKE algorithm provides a cryptographic guarantee that the peer used the same password and identity inputs, and has computed the identical shared secret key. - This is the object that represents the cipher suite used for a PAKE algorithm. The PAKE cipher suite specifies the PAKE algorithm, and the options selected for that algorithm. The cipher suite includes the following attributes: + Since the peer is not authenticated, no action should be taken that assumes that the peer is who it claims to be. + For example, do not access restricted files on the peer's behalf until an explicit authentication has succeeded. - * The PAKE algorithm itself. - * The hash algorithm, encoded within the PAKE algorithm. - * The PAKE primitive, which identifies the prime order group used for the key exchange operation. See :secref:`pake-primitive`. - * Whether to confirm the shared secret. + .. note:: + Some PAKE algorithms do not include any key-confirmation steps. - This is an implementation-defined type. Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + The exact sequence of calls to perform a password-authenticated key exchange depends on the algorithm in use. + Refer to the documentation of individual PAKE algorithms for more information. + See :secref:`pake-algorithms`. - Before calling any function on a PAKE cipher suite object, the application must initialize it by any of the following means: + When this function returns successfully, ``operation`` becomes inactive. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_pake_abort()`. - * Set the object to all-bits-zero, for example: +.. function:: psa_pake_abort - .. code-block:: xref + .. summary:: + Abort a PAKE operation. - psa_pake_cipher_suite_t cipher_suite; - memset(&cipher_suite, 0, sizeof(cipher_suite)); + .. param:: psa_pake_operation_t * operation + Initialized PAKE operation. - * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to :code:`psa_crypto_init()`. - .. code-block:: xref + Aborting an operation frees all associated resources except for the ``operation`` object itself. + Once aborted, the operation object can be reused for another operation by calling `psa_pake_setup()` again. - static psa_pake_cipher_suite_t cipher_suite; + This function can be called any time after the operation object has been initialized as described in `psa_pake_operation_t`. - * Initialize the object to the initializer `PSA_PAKE_CIPHER_SUITE_INIT`, for example: + In particular, calling `psa_pake_abort()` after the operation has been terminated by a call to `psa_pake_abort()` or `psa_pake_get_shared_key()` is safe and has no effect. - .. code-block:: xref - psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; +.. _pake-support: - * Assign the result of the function `psa_pake_cipher_suite_init()` to the object, for example: +PAKE Support macros +~~~~~~~~~~~~~~~~~~~ - .. code-block:: xref +.. macro:: PSA_PAKE_OUTPUT_SIZE + :definition: /* implementation-defined value */ - psa_pake_cipher_suite_t cipher_suite; - cipher_suite = psa_pake_cipher_suite_init(); + .. summary:: + Sufficient output buffer size for `psa_pake_output()`, in bytes. - Following initialization, the cipher-suite object contains the following values: + .. param:: alg + A PAKE algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_PAKE(alg)` is true. + .. param:: primitive + A primitive of type `psa_pake_primitive_t` that is compatible with algorithm ``alg``. + .. param:: output_step + A value of type `psa_pake_step_t` that is valid for the algorithm ``alg``. - .. list-table:: - :header-rows: 1 - :widths: 1 4 - :align: left + .. return:: + A sufficient output buffer size for the specified PAKE algorithm, primitive, and output step. + An implementation can return either ``0`` or a correct size for a PAKE algorithm, primitive, and output step that it recognizes, but does not support. + If the parameters are not valid, the return value is unspecified. - * - Attribute - - Value + If the size of the output buffer is at least this large, it is guaranteed that `psa_pake_output()` will not fail due to an insufficient buffer size. + The actual size of the output might be smaller in any given call. - * - algorithm - - :code:`PSA_ALG_NONE` --- an invalid algorithm identifier. - * - primitive - - ``0`` --- an invalid PAKE primitive. - * - key confirmation - - `PSA_PAKE_CONFIRMED_KEY` --- requesting that the secret key is confirmed before it can be returned. + See also `PSA_PAKE_OUTPUT_MAX_SIZE` - Valid algorithm, primitive, and key confirmation values must be set when using a PAKE cipher suite. +.. macro:: PSA_PAKE_OUTPUT_MAX_SIZE + :definition: /* implementation-defined value */ - .. admonition:: Implementation note + .. summary:: + Sufficient output buffer size for `psa_pake_output()` for any of the supported PAKE algorithms, primitives and output steps. - Implementations are recommended to define the cipher-suite object as a simple data structure, with fields corresponding to the individual cipher suite attributes. - In such an implementation, each function ``psa_pake_cs_set_xxx()`` sets a field and the corresponding function ``psa_pake_cs_get_xxx()`` retrieves the value of the field. + If the size of the output buffer is at least this large, it is guaranteed that `psa_pake_output()` will not fail due to an insufficient buffer size. - An implementations can report attribute values that are equivalent to the original one, but have a different encoding. - For example, an implementation can use a more compact representation for attributes where many bit-patterns are invalid or not supported, and store all values that it does not support as a special marker value. - In such an implementation, after setting an invalid value, the corresponding get function returns an invalid value which might not be the one that was originally stored. + See also `PSA_PAKE_OUTPUT_SIZE()`. -.. macro:: PSA_PAKE_CIPHER_SUITE_INIT +.. macro:: PSA_PAKE_INPUT_SIZE :definition: /* implementation-defined value */ .. summary:: - This macro returns a suitable initializer for a PAKE cipher suite object of type `psa_pake_cipher_suite_t`. + Sufficient buffer size for inputs to `psa_pake_input()`. -.. function:: psa_pake_cipher_suite_init + .. param:: alg + A PAKE algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_PAKE(alg)` is true. + .. param:: primitive + A primitive of type `psa_pake_primitive_t` that is compatible with algorithm ``alg``. + .. param:: input_step + A value of type `psa_pake_step_t` that is valid for the algorithm ``alg``. - .. summary:: - Return an initial value for a PAKE cipher suite object. + .. return:: + A sufficient buffer size for the specified PAKE algorithm, primitive, and input step. + An implementation can return either ``0`` or a correct size for a PAKE algorithm, primitive, and output step that it recognizes, but does not support. + If the parameters are not valid, the return value is unspecified. - .. return:: psa_pake_cipher_suite_t + The value returned by this macro is guaranteed to be large enough for any valid input to `psa_pake_input()` in an operation with the specified parameters. -.. function:: psa_pake_cs_get_algorithm + This macro can be useful when transferring inputs from the peer into the PAKE operation. - .. summary:: - Retrieve the PAKE algorithm from a PAKE cipher suite. + See also `PSA_PAKE_INPUT_MAX_SIZE` - .. param:: const psa_pake_cipher_suite_t* cipher_suite - The cipher suite object to query. +.. macro:: PSA_PAKE_INPUT_MAX_SIZE + :definition: /* implementation-defined value */ - .. return:: psa_algorithm_t - The PAKE algorithm stored in the cipher suite object. + .. summary:: + Sufficient buffer size for inputs to `psa_pake_input()` for any of the supported PAKE algorithms, primitives and input steps. - .. admonition:: Implementation note + This macro can be useful when transferring inputs from the peer into the PAKE operation. + + See also `PSA_PAKE_INPUT_SIZE()`. - This is a simple accessor function that is not required to validate its inputs. It can be efficiently implemented as a ``static inline`` function or a function-like macro. -.. function:: psa_pake_cs_set_algorithm +.. _pake-jpake: - .. summary:: - Declare the PAKE algorithm for the cipher suite. +The J-PAKE protocol +------------------- - .. param:: psa_pake_cipher_suite_t* cipher_suite - The cipher suite object to write to. - .. param:: psa_algorithm_t alg - The PAKE algorithm to write: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_PAKE(alg)` is true. +J-PAKE is the password-authenticated key exchange by juggling protocol, defined by :RFC-title:`8236`. +This protocol uses the Schnorr Non-Interactive Zero-Knowledge Proof (NIZKP), as defined by :RFC-title:`8235`. - .. return:: void +J-PAKE is a balanced PAKE, without key confirmation. - This function overwrites any PAKE algorithm previously set in ``cipher_suite``. +.. _jpake-cipher-suites: - .. admonition:: Implementation note +J-PAKE cipher suites +~~~~~~~~~~~~~~~~~~~~ - This is a simple accessor function that is not required to validate its inputs. It can be efficiently implemented as a ``static inline`` function or a function-like macro. +When setting up a PAKE cipher suite to use the J-PAKE protocol: -.. function:: psa_pake_cs_get_primitive +* Use the :code:`PSA_ALG_JPAKE()` algorithm, parameterized by the required hash algorithm. +* Use a PAKE primitive for the required elliptic curve, or finite field group. +* J-PAKE does not confirm the shared secret key that results from the key exchange. - .. summary:: - Retrieve the primitive from a PAKE cipher suite. +For example, the following code creates a cipher suite to select J-PAKE using P-256 with the SHA-256 hash function: - .. param:: const psa_pake_cipher_suite_t* cipher_suite - The cipher suite object to query. +.. code-block:: xref - .. return:: psa_pake_primitive_t - The primitive stored in the cipher suite object. + psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; - .. admonition:: Implementation note + psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_JPAKE(PSA_ALG_SHA256)); + psa_pake_cs_set_primitive(&cipher_suite, + PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, + PSA_ECC_FAMILY_SECP_R1, 256)); + psa_pake_cs_set_key_confirmation(&cipher_suite, PSA_PAKE_UNCONFIRMED_KEY); - This is a simple accessor function that is not required to validate its inputs. It can be efficiently implemented as a ``static inline`` function or a function-like macro. +More information on selecting a specific Elliptic curve or Diffie-Hellman field is provided with the `PSA_PAKE_PRIMITIVE_TYPE_ECC` and `PSA_PAKE_PRIMITIVE_TYPE_DH` constants. -.. function:: psa_pake_cs_set_primitive +.. _jpake-passwords: - .. summary:: - Declare the primitive for a PAKE cipher suite. +J-PAKE password processing +~~~~~~~~~~~~~~~~~~~~~~~~~~ - .. param:: psa_pake_cipher_suite_t* cipher_suite - The cipher suite object to write to. - .. param:: psa_pake_primitive_t primitive - The PAKE primitive to write: a value of type `psa_pake_primitive_t`. - If this is ``0``, the primitive type in ``cipher_suite`` becomes unspecified. +The PAKE operation for J-PAKE expects a key of type type :code:`PSA_KEY_TYPE_PASSWORD` or :code:`PSA_KEY_TYPE_PASSWORD_HASH`. +The same key value must be provided to the PAKE operation in both participants. - .. return:: void +The key can be the password text itself, in an agreed character encoding, or some value derived from the password, as required by a higher level protocol. +For low-entropy passwords, it is recommended that a key-stretching derivation algorithm, such as PBKDF2, is used, and the resulting password hash is used as the key input to the PAKE operation. - This function overwrites any primitive previously set in ``cipher_suite``. +J-PAKE operation +~~~~~~~~~~~~~~~~ - .. admonition:: Implementation note +The J-PAKE operation follows the protocol shown in :numref:`fig-jpake`. - This is a simple accessor function that is not required to validate its inputs. It can be efficiently implemented as a ``static inline`` function or a function-like macro. +.. figure:: /figure/j-pake.* + :name: fig-jpake -.. macro:: PSA_PAKE_CONFIRMED_KEY - :definition: 0 + The J-PAKE protocol - .. summary:: A key confirmation value that indicates an confirmed key in a PAKE cipher suite. + The variable names :math:`x1`, :math:`g1`, and so on, are taken from the finite field implementation of J-PAKE in :RFC:`8236#2`. - This key confirmation value will result in the PAKE algorithm exchanging data to verify that the shared key is identical for both parties. This is the default key confirmation value in an initialized PAKE cipher suite object. + Details of the computation for the key shares and zero-knowledge proofs are in :RFC:`8236` and :RFC:`8235`. - Some algorithms do not include confirmation of the shared key. +Setup +^^^^^ -.. macro:: PSA_PAKE_UNCONFIRMED_KEY - :definition: 1 +J-PAKE does not assign roles to the participants, so it is not necessary to call `psa_pake_set_role()`. - .. summary:: A key confirmation value that indicates an unconfirmed key in a PAKE cipher suite. +J-PAKE requires both an application and a peer identity. +If the peer identity provided to `psa_pake_set_peer()` does not match the data received from the peer, then the call to `psa_pake_input()` for the `PSA_PAKE_STEP_ZK_PROOF` step will fail with :code:`PSA_ERROR_INVALID_SIGNATURE`. - This key confirmation value will result in the PAKE algorithm terminating prior to confirming that the resulting shared key is identical for both parties. +The following steps demonstrate the application code for 'User' in :numref:`fig-jpake`. +The input and output steps must be carried out in exactly the same sequence as shown. - Some algorithms do not support returning an unconfirmed shared key. +1. To prepare a J-PAKE operation, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: - .. warning:: + .. code-block:: xref - When the shared key is not confirmed as part of the PAKE operation, the application is responsible for mitigating risks that arise from the possible mismatch in the output keys. + psa_pake_operation_t jpake = PSA_PAKE_OPERATION_INIT; -.. function:: psa_pake_cs_get_key_confirmation + psa_pake_setup(&jpake, pake_key, &cipher_suite); + psa_pake_set_user(&jpake, ...); + psa_pake_set_peer(&jpake, ...); - .. summary:: - Retrieve the key confirmation from a PAKE cipher suite. + See :secref:`jpake-cipher-suites` and :secref:`jpake-passwords` for details on the requirements for the cipher suite and key. - .. param:: const psa_pake_cipher_suite_t* cipher_suite - The cipher suite object to query. + The key material is used as an array of bytes, which is converted to an integer as described in :cite-title:`SEC1` §2.3.8, before reducing it modulo :math:`q`. + Here, :math:`q`` is the order of the group defined by the cipher-suite primitive. + `psa_pake_setup()` will return an error if the result of the conversion and reduction is ``0``. - .. return:: uint32_t - A key confirmation value: either `PSA_PAKE_CONFIRMED_KEY` or `PSA_PAKE_UNCONFIRMED_KEY`. +Key exchange +^^^^^^^^^^^^ - .. admonition:: Implementation note +After setup, the key exchange flow for J-PAKE is as follows: - This is a simple accessor function that is not required to validate its inputs. - It can be efficiently implemented as a ``static inline`` function or a function-like macro. +2. To get the first round data that needs to be sent to the peer, call: -.. function:: psa_pake_cs_set_key_confirmation + .. code-block:: xref - .. summary:: - Declare the key confirmation from a PAKE cipher suite. + // Get g1 + psa_pake_output(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); + // Get V1, the ZKP public key for x1 + psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); + // Get r1, the ZKP proof for x1 + psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); + // Get g2 + psa_pake_output(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); + // Get V2, the ZKP public key for x2 + psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); + // Get r2, the ZKP proof for x2 + psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); + +#. To provide the first round data received from the peer to the operation, call: - .. param:: psa_pake_cipher_suite_t* cipher_suite - The cipher suite object to write to. - .. param:: uint32_t key_confirmation - The key confirmation value to write: either `PSA_PAKE_CONFIRMED_KEY` or `PSA_PAKE_UNCONFIRMED_KEY`. + .. code-block:: xref - .. return:: void + // Set g3 + psa_pake_input(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); + // Set V3, the ZKP public key for x3 + psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); + // Set r3, the ZKP proof for x3 + psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); + // Set g4 + psa_pake_input(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); + // Set V4, the ZKP public key for x4 + psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); + // Set r4, the ZKP proof for x4 + psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); + +#. To get the second round data that needs to be sent to the peer, call: - This function overwrites any key confirmation previously set in ``cipher_suite``. + .. code-block:: xref - The documentation of individual PAKE algorithms specifies which key confirmation values are valid for the algorithm. + // Get A + psa_pake_output(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); + // Get V5, the ZKP public key for x2*s + psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); + // Get r5, the ZKP proof for x2*s + psa_pake_output(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); - .. admonition:: Implementation note +#. To provide the second round data received from the peer to the operation call: - This is a simple accessor function that is not required to validate its inputs. - It can be efficiently implemented as a ``static inline`` function or a function-like macro. + .. code-block:: xref -.. _pake-roles: + // Set B + psa_pake_input(&jpake, PSA_PAKE_STEP_KEY_SHARE, ...); + // Set V6, the ZKP public key for x4*s + psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PUBLIC, ...); + // Set r6, the ZKP proof for x4*s + psa_pake_input(&jpake, PSA_PAKE_STEP_ZK_PROOF, ...); -PAKE roles -~~~~~~~~~~ +#. To use the shared secret, extract it as a key-derivation key. For example, to extract a derivation key for HKDF-SHA-256: -Some PAKE algorithms need to know which role each participant is taking in the algorithm. For example: + .. code-block:: xref -* Augmented PAKE algorithms typically have a client and a server participant. -* Some symmetric PAKE algorithms need to assign an order to the participants. + // Set up the key attributes + psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT; + psa_key_set_type(&att, PSA_KEY_TYPE_DERIVE); + psa_key_set_usage_flags(&att, PSA_KEY_USAGE_DERIVE); + psa_key_set_algorithm(&att, PSA_ALG_HKDF(PSA_ALG_SHA256)); -.. typedef:: uint8_t psa_pake_role_t + // Get Ka=Kb=K + psa_key_id_t shared_key; + psa_pake_get_shared_key(&jpake, &att, &shared_key); - .. summary:: - Encoding of the application role in a PAKE algorithm. +For more information about the format of the values which are passed for each step, see :secref:`pake-steps`. - This type is used to encode the application's role in the algorithm being executed. - For more information see the documentation of individual PAKE role constants. +If the verification of a Zero-knowledge proof provided by the peer fails, then the corresponding call to `psa_pake_input()` for the `PSA_PAKE_STEP_ZK_PROOF` step will return :code:`PSA_ERROR_INVALID_SIGNATURE`. -.. macro:: PSA_PAKE_ROLE_NONE - :definition: ((psa_pake_role_t)0x00) +The shared secret that is produced by J-PAKE is not suitable for use as an encryption key. +It must be used as an input to a key derivation operation to produce additional cryptographic keys. - .. summary:: - A value to indicate no role in a PAKE algorithm. +.. warning:: - This value can be used in a call to `psa_pake_set_role()` for symmetric PAKE algorithms which do not assign roles. + At the end of this sequence there is a cryptographic guarantee that only a peer that used the same password is able to compute the same key. + But there is no guarantee that the peer is the participant it claims to be, or that the peer used the same password during the exchange. -.. macro:: PSA_PAKE_ROLE_FIRST - :definition: ((psa_pake_role_t)0x01) + At this point, authentication is implicit --- material encrypted or authenticated using the computed key can only be decrypted or verified by someone with the same key. + The peer is not authenticated at this point, and no action should be taken by the application which assumes that the peer is authenticated, for example, by accessing restricted files. - .. summary:: - The first peer in a balanced PAKE. + To make the authentication explicit, there are various methods to confirm that both parties have the same key. See :RFC:`8236#5` for two examples. - Although balanced PAKE algorithms are symmetric, some of them need the peers to be ordered for the transcript calculations. - If the algorithm does not need a specific ordering, then either do not call `psa_pake_set_role()`, or use `PSA_PAKE_ROLE_NONE` as the role parameter. +J-PAKE Algorithms +~~~~~~~~~~~~~~~~~ -.. macro:: PSA_PAKE_ROLE_SECOND - :definition: ((psa_pake_role_t)0x02) +.. macro:: PSA_ALG_JPAKE + :definition: /* specification-defined value */ .. summary:: - The second peer in a balanced PAKE. + Macro to build the Password-authenticated key exchange by juggling (J-PAKE) algorithm. - Although balanced PAKE algorithms are symmetric, some of them need the peers to be ordered for the transcript calculations. - If the algorithm does not need a specific ordering, then either do not call `psa_pake_set_role()`, or use `PSA_PAKE_ROLE_NONE` as the role parameter. + .. param:: hash_alg + A hash algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_HASH(hash_alg)` is true. -.. macro:: PSA_PAKE_ROLE_CLIENT - :definition: ((psa_pake_role_t)0x11) + .. return:: + A J-PAKE algorithm, parameterized by a specific hash. - .. summary:: - The client in an augmented PAKE. + Unspecified if ``hash_alg`` is not a supported hash algorithm. - Augmented PAKE algorithms need to differentiate between client and server. + This is J-PAKE as defined by :RFC:`8236`, instantiated with the following parameters: -.. macro:: PSA_PAKE_ROLE_SERVER - :definition: ((psa_pake_role_t)0x12) + * The primitive group can be either an elliptic curve or defined over a finite field. + * The Schnorr NIZKP, using the same group as the J-PAKE algorithm. + * The cryptographic hash function, ``hash_alg``. - .. summary:: - The server in an augmented PAKE. + J-PAKE does not confirm the shared secret key that results from the key exchange. - Augmented PAKE algorithms need to differentiate between client and server. + The shared secret that is produced by J-PAKE is not suitable for use as an encryption key. + It must be used as an input to a key derivation operation to produce additional cryptographic keys. + See :secref:`pake-jpake` for the J-PAKE protocol flow and how to implement it with the |API|. -.. _pake-steps: + .. subsection:: Compatible key types -PAKE step types -~~~~~~~~~~~~~~~ + | :code:`PSA_KEY_TYPE_PASSWORD` + | :code:`PSA_KEY_TYPE_PASSWORD_HASH` -.. typedef:: uint8_t psa_pake_step_t +.. macro:: PSA_ALG_IS_JPAKE + :definition: /* specification-defined value */ .. summary:: - Encoding of input and output steps for a PAKE algorithm. + Whether the specified algorithm is a J-PAKE algorithm. - Some PAKE algorithms need to exchange more data than a single key share. - This type encodes additional input and output steps for such algorithms. + .. param:: alg + An algorithm identifier: a value of type :code:`psa_algorithm_t`. -.. macro:: PSA_PAKE_STEP_KEY_SHARE - :definition: ((psa_pake_step_t)0x01) + .. return:: + ``1`` if ``alg`` is a J-PAKE algorithm, ``0`` otherwise. + This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. - .. summary:: - The key share being sent to or received from the peer. + J-PAKE algorithms are constructed using :code:`PSA_ALG_JPAKE(hash_alg)`. - The format for both input and output using this step is the same as the format for public keys on the group specified by the PAKE operation's primitive. +.. _pake-spake2p: - The public key formats are defined in the documentation for :code:`psa_export_public_key()`. +The SPAKE2+ protocol +-------------------- - For information regarding how the group is determined, consult the documentation `PSA_PAKE_PRIMITIVE()`. +SPAKE2+ is the augmented password-authenticated key exchange protocol, defined by :rfc-title:`9383`. +SPAKE2+ includes confirmation of the shared secret key that results from the key exchange. -.. macro:: PSA_PAKE_STEP_ZK_PUBLIC - :definition: ((psa_pake_step_t)0x02) +SPAKE2+ is required by :cite-title:`MATTER`, as MATTER_PAKE. +:cite:`MATTER` uses an earlier draft of the SPAKE2+ protocol, :cite-title:`SPAKE2P-2`. - .. summary:: - A Schnorr NIZKP public key. +Although the operation of the PAKE is similar for both of these variants, they have different key schedules for the derivation of the shared secret. - This is the ephemeral public key in the Schnorr Non-Interactive Zero-Knowledge Proof, this is the value denoted by *V* in :RFC:`8235`. +.. _spake2p-cipher-suites: - The format for both input and output at this step is the same as that for public keys on the group specified by the PAKE operation's primitive. +SPAKE2+ cipher suites +~~~~~~~~~~~~~~~~~~~~~ - For more information on the format, consult the documentation of :code:`psa_export_public_key()`. +SPAKE2+ is instantiated with the following parameters: - For information regarding how the group is determined, consult the documentation `PSA_PAKE_PRIMITIVE()`. + * An elliptic curve group. + * A cryptographic hash function. + * A key derivation function. + * A keyed MAC function. -.. macro:: PSA_PAKE_STEP_ZK_PROOF - :definition: ((psa_pake_step_t)0x03) +Valid combinations of these parameters are defined in the table of cipher suites in :rfc:`9383#4`. - .. summary:: - A Schnorr NIZKP proof. +When setting up a PAKE cipher suite to use the SPAKE2+ protocol defined in :rfc:`9383`: - This is the proof in the Schnorr Non-Interactive Zero-Knowledge Proof, this is the value denoted by *r* in :RFC:`8235`. +* For cipher-suites that use HMAC for key confirmation, use the :code:`PSA_ALG_SPAKE2P_HMAC()` algorithm, parameterized by the required hash algorithm. +* For cipher-suites that use CMAC-AES-128 for key confirmation, use the :code:`PSA_ALG_SPAKE2P_CMAC()` algorithm, parameterized by the required hash algorithm. +* Use a PAKE primitive for the required elliptic curve. - Both for input and output, the value at this step is an integer less than the order of the group specified by the PAKE operation's primitive. - The format depends on the group as well: +For example, the following code creates a cipher suite to select SPAKE2+ using edwards25519 with the SHA-256 hash function: - * For Montgomery curves, the encoding is little endian. - * For other Elliptic curves, and for Diffie-Hellman groups, the encoding is big endian. See :cite:`SEC1` §2.3.8. +.. code-block:: xref - In both cases leading zeroes are permitted as long as the length in bytes does not exceed the byte length of the group order. + psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; - For information regarding how the group is determined, consult the documentation `PSA_PAKE_PRIMITIVE()`. + psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_SPAKE2P_HMAC(PSA_ALG_SHA256)); + psa_pake_cs_set_primitive(&cipher_suite, + PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, + PSA_ECC_FAMILY_TWISTED_EDWARDS, 255)); -.. macro:: PSA_PAKE_STEP_CONFIRM - :definition: ((psa_pake_step_t)0x04) +When setting up a PAKE cipher suite to use the SPAKE2+ protocol used by :cite:`MATTER`: - .. summary:: - The key confirmation value. +* Use the :code:`PSA_ALG_SPAKE2P_MATTER` algorithm. +* Use the :code:`PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, PSA_ECC_FAMILY_SECP_R1, 256)` PAKE primitive. - This value is used during the key confirmation phase of a PAKE protocol. The format of the value depends on the algorithm and cipher suite: +The following code creates a cipher suite to select the :cite:`MATTER` variant of SPAKE2+: - * For :code:`PSA_ALG_SPAKE2P`, the format for both input and output at this step is the same as the output of the MAC algorithm specified in the cipher suite. +.. code-block:: xref -.. _pake-operation: + psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT; -Multi-part PAKE operations -~~~~~~~~~~~~~~~~~~~~~~~~~~ + psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_SPAKE2P_MATTER); + psa_pake_cs_set_primitive(&cipher_suite, + PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, + PSA_ECC_FAMILY_SECP_R1, 256)); -.. typedef:: /* implementation-defined type */ psa_pake_operation_t +.. todo:: rework this section - .. summary:: - The type of the state object for PAKE operations. +.. _spake2p-registration: - Before calling any function on a PAKE operation object, the application must initialize it by any of the following means: +SPAKE2+ registration +~~~~~~~~~~~~~~~~~~~~ - * Set the object to all-bits-zero, for example: +The SPAKE2+ protocol has distinct roles for the two participants: - .. code-block:: xref +* The *Prover* takes the role of client. + It uses the protocol to prove that it knows the secret password, and produce a shared secret. +* The *Verifier* takes the role of server. + It uses the protocol to verify the client's proof, and produce a shared secret. - psa_pake_operation_t operation; - memset(&operation, 0, sizeof(operation)); +The registration phase of SPAKE2+ provides the initial password processing, described in :rfc:`9383#3.2`. +The result of registration is two pairs of values --- :math:`(w0, w1)` and :math:`(w0, L)` --- that need to be provided during the authentication phase to the Prover and Verifier, respectively. +The design of SPAKE2+ ensures that knowledge of :math:`(w0, L)` does not enable an attacker to determine the password, or to compute :math:`w1`. - * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: +In the |API|, the registration output values are managed as an asymmetric key-pair: - .. code-block:: xref +* The Prover values, :math:`(w0, w1)`, are stored in a key of type `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`. +* The Verifier values, :math:`(w0, L)`, are stored in a key of type `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`, or derived from the matching `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`. - static psa_pake_operation_t operation; +The SPAKE2+ key types are parameterized by the same elliptic curve as the SPAKE2+ cipher suite. - * Initialize the object to the initializer `PSA_PAKE_OPERATION_INIT`, for example: +The key pair is derived from the initial SPAKE2+ password prior to starting the PAKE operation. +It is recommended to use a key-stretching derivation algorithm, for example PBKDF2. +This process can take place immediately before the PAKE operation, or derived at some earlier point and stored by the participant. +Alternatively, the Verifier can be provisioned with the `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()` for the protocol, by the Prover, or some other agent. +:numref:`fig-spake2p-reg` illustrates some example SPAKE2+ key derivation flows. - .. code-block:: xref +The resulting SPAKE2+ key-pair must be protected at least as well as the password. +The public key, exported from the key pair, does not need to be kept confidential. +It is recommended that the Verifier stores only the public key, because disclosure of the public key does not enable an attacker to impersonate the Prover. - psa_pake_operation_t operation = PSA_PAKE_OPERATION_INIT; +.. figure:: /figure/spake2plus-reg.* + :name: fig-spake2p-reg - * Assign the result of the function `psa_pake_cipher_suite_init()` to the object, for example: + Examples of SPAKE2+ key derivation procedures - .. code-block:: xref + The variable names :math:`w0`, :math:`w1`, and :math:`L` are taken from the description of SPAKE2+ in :RFC:`9383`. - psa_pake_operation_t operation; - operation = psa_pake_operation_init(); + Details of the computation for the key derivation values are in :RFC:`9383#3.2`. - This is an implementation-defined type. Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. +The following steps demonstrate the derivation of a SPAKE2+ key pair for use with the P-256 Elliptic curve group, using PBKDF2-HMAC-SHA256: -.. macro:: PSA_PAKE_OPERATION_INIT - :definition: /* implementation-defined value */ +1. Allocate and initialize a key derivation object: - .. summary:: - This macro returns a suitable initializer for a PAKE operation object of type `psa_pake_operation_t`. + .. code-block:: xref -.. function:: psa_pake_operation_init + psa_key_derivation_operation_t pbkdf = PSA_KEY_DERIVATION_OPERATION_INIT; - .. summary:: - Return an initial value for a PAKE operation object. +#. Setup the key derivation from the SPAKE2+ password, ``password_key``, and parameters ``pbkdf2_params``: - .. return:: psa_pake_operation_t + .. code-block:: xref -.. function:: psa_pake_setup + psa_key_derivation_setup(&pbkdf, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA256)); + psa_key_derivation_input_key(&pbkdf, PSA_KEY_DERIVATION_INPUT_PASSWORD, password_key); + psa_key_derivation_input_integer(&pbkdf, PSA_KEY_DERIVATION_INPUT_COST, pbkdf2_params.cost); + psa_key_derivation_input_bytes(&pbkdf, PSA_KEY_DERIVATION_INPUT_SALT, + &pbkdf2_params.salt, pbkdf2_params.salt_len); - .. summary:: - Setup a password-authenticated key exchange. +#. Allocate and initialize a key attributes object: - .. param:: psa_pake_operation_t *operation - The operation object to set up. - It must have been initialized as per the documentation for `psa_pake_operation_t` and not yet in use. - .. param:: psa_key_id_t password_key - Identifier of the key holding the password or a value derived from the password. - It must remain valid until the operation terminates. + .. code-block:: xref - The valid key types depend on the PAKE algorithm, and participant role. - Refer to the documentation of individual PAKE algorithms for more information, see :secref:`pake-algorithms`. + psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT; - The key must permit the usage :code:`PSA_KEY_USAGE_DERIVE`. - .. param:: const psa_pake_cipher_suite_t *cipher_suite - The cipher suite to use. - A PAKE cipher suite fully characterizes a PAKE algorithm, including the PAKE algorithm. +#. Set the key type, size, and policy: - The cipher suite must be compatible with the key type of ``password_key``. + .. code-block:: xref - .. return:: psa_status_t - .. retval:: PSA_SUCCESS - Success. The operation is now active. - .. retval:: PSA_ERROR_BAD_STATE - The following conditions can result in this error: + psa_set_key_type(&att, PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&att, 256); // for P-256 + psa_set_key_usage_flags(&att, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&att, PSA_ALG_SPAKE2P); - * The operation state is not valid: it must be inactive. - * The library requires initializing by a call to :code:`psa_crypto_init()`. - .. retval:: PSA_ERROR_INVALID_HANDLE - ``password_key`` is not a valid key identifier. - .. retval:: PSA_ERROR_NOT_PERMITTED - ``psssword_key`` does not have the :code:`PSA_KEY_USAGE_DERIVE` flag, or it does not permit the algorithm in ``cipher_suite``. - .. retval:: PSA_ERROR_INVALID_ARGUMENT - The following conditions can result in this error: +#. Derive the key: - * The algorithm in ``cipher_suite`` is not a PAKE algorithm, or encodes an invalid hash algorithm. - * The PAKE primitive in ``cipher_suite`` is not compatible with the PAKE algorithm. - * The key confirmation value in ``cipher_suite`` is not compatible with the PAKE algorithm and primitive. - * The key type for ``password_key`` is not :code:`PSA_KEY_TYPE_PASSWORD` or :code:`PSA_KEY_TYPE_PASSWORD_HASH`. - * ``password_key`` is not compatible with ``cipher_suite``. - .. retval:: PSA_ERROR_NOT_SUPPORTED - The following conditions can result in this error: + .. code-block:: xref - * The algorithm in ``cipher_suite`` is not a supported PAKE algorithm, or encodes an unsupported hash algorithm. - * The PAKE primitive in ``cipher_suite`` is not supported or not compatible with the PAKE algorithm. - * The key confirmation value in ``cipher_suite`` is not supported, or not compatible, with the PAKE algorithm and primitive. - * The key type or key size of ``password_key`` is not supported with ``cipher suite``. - .. retval:: PSA_ERROR_CORRUPTION_DETECTED - .. retval:: PSA_ERROR_COMMUNICATION_FAILURE - .. retval:: PSA_ERROR_STORAGE_FAILURE - .. retval:: PSA_ERROR_DATA_CORRUPT - .. retval:: PSA_ERROR_DATA_INVALID + psa_key_id_t spake2p_key; + psa_key_derivation_output_key(&att, &pbkdf, &spake2p_key); + psa_key_derivation_abort(&pbkdf); - The sequence of operations to set up a password-authenticated key exchange operation is as follows: +See :secref:`spake2p-keys` for details of the key types, key pair derivation, and public key format. + +.. _spake2p-operation: + +SPAKE2+ operation +~~~~~~~~~~~~~~~~~ - 1. Allocate a PAKE operation object which will be passed to all the functions listed here. - #. Initialize the operation object with one of the methods described in the documentation for `psa_pake_operation_t`. - For example, using `PSA_PAKE_OPERATION_INIT`. - #. Call `psa_pake_setup()` to specify the cipher suite. - #. Call ``psa_pake_set_xxx()`` functions on the operation to complete the setup. - The exact sequence of ``psa_pake_set_xxx()`` functions that needs to be called depends on the algorithm in use. +The SPAKE2+ operation follows the protocol shown in :numref:`fig-spake2p`. - A typical sequence of calls to perform a password-authenticated key exchange: +.. figure:: /figure/spake2plus.* + :name: fig-spake2p - 1. Call :code:`psa_pake_output(operation, PSA_PAKE_STEP_KEY_SHARE, ...)` to get the key share that needs to be sent to the peer. - #. Call :code:`psa_pake_input(operation, PSA_PAKE_STEP_KEY_SHARE, ...)` to provide the key share that was received from the peer. - #. Depending on the algorithm additional calls to `psa_pake_output()` and `psa_pake_input()` might be necessary. - #. Call `psa_pake_get_shared_key()` to access the shared secret. + The SPAKE2+ authentication and key confirmation protocol - Refer to the documentation of individual PAKE algorithms for details on the required set up and operation for each algorithm, and for constraints on the format and content of valid passwords. - See :secref:`pake-algorithms`. + The variable names :math:`w0`, :math:`w1`, :math:`L`, and so on, are taken from the description of SPAKE2+ in :RFC:`9383`. - After a successful call to `psa_pake_setup()`, the operation is active, and the application must eventually terminate the operation. The following events terminate an operation: + Details of the computation for the key shares is in :RFC:`9383#3.3` and confirmation values in :RFC:`9383#3.4`. - * A successful call to `psa_pake_get_shared_key()`. - * A call to `psa_pake_abort()`. +Setup +^^^^^ - If `psa_pake_setup()` returns an error, the operation object is unchanged. If a subsequent function call with an active operation returns an error, the operation enters an error state. +In SPAKE2+, the Prover uses the `PSA_PAKE_ROLE_CLIENT` role, and the Verifier uses the `PSA_PAKE_ROLE_SERVER` role. - To abandon an active operation, or reset an operation in an error state, call `psa_pake_abort()`. +The key passed to the Prover must be a SPAKE2+ key-pair, which is derived as recommended in :secref:`spake2p-registration`. +The key passed to the Verifier can either be a SPAKE2+ key-pair, or a SPAKE2+ public key. +A SPAKE2+ public key is imported from data that is output by calling :code:`psa_export_public_key()` on a SPAKE2+ key-pair. - .. - See :secref:`multi-part-operations`. :issue:`add this when integrated to main specification` +Both participants in SPAKE2+ have an optional identity. +If no identity value is provided, then a zero-length string is used for that identity in the protocol. +If the participants do not supply the same identity values to the protocol, the computed secrets will be different, and key confirmation will fail. -.. function:: psa_pake_set_role +The following steps demonstrate the application code for both Prover and Verifier in :numref:`fig-spake2p`. - .. summary:: - Set the application role for a password-authenticated key exchange. +**Prover** + To prepare a SPAKE2+ operation for the Prover, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: - .. param:: psa_pake_operation_t *operation - Active PAKE operation. - .. param:: psa_pake_role_t role - A value of type `psa_pake_role_t` indicating the application role in the PAKE algorithm. - See :secref:`pake-roles`. + .. code-block:: xref - .. return:: psa_status_t - .. retval:: PSA_SUCCESS - Success. - .. retval:: PSA_ERROR_BAD_STATE - The following conditions can result in this error: + psa_pake_operation_t spake2p_p = PSA_PAKE_OPERATION_INIT; - * The operation state is not valid: it must be active, and `psa_pake_set_role()`, `psa_pake_input()`, and `psa_pake_output()` must not have been called yet. - * The library requires initializing by a call to :code:`psa_crypto_init()`. - .. retval:: PSA_ERROR_INVALID_ARGUMENT - ``role`` is not a valid PAKE role in the operation's algorithm. - .. retval:: PSA_ERROR_NOT_SUPPORTED - ``role`` is not a valid PAKE role, or is not supported for the operation's algorithm. - .. retval:: PSA_ERROR_COMMUNICATION_FAILURE - .. retval:: PSA_ERROR_CORRUPTION_DETECTED + psa_pake_setup(&spake2p_p, pake_key_p, &cipher_suite); + psa_pake_set_role(&spake2p_p, PSA_PAKE_ROLE_CLIENT); - Not all PAKE algorithms need to differentiate the communicating participants. - For PAKE algorithms that do not require a role to be specified, the application can do either of the following: + The key ``pake_key_p`` is a SPAKE2+ key pair, `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`. + See :secref:`spake2p-cipher-suites` for details on constructing a suitable cipher suite. - * Not call `psa_pake_set_role()` on the PAKE operation. - * Call `psa_pake_set_role()` with the `PSA_PAKE_ROLE_NONE` role. +**Prover** + Provide any additional, optional, parameters: - Refer to the documentation of individual PAKE algorithms for more information. - See :secref:`pake-algorithms`. + .. code-block:: xref -.. function:: psa_pake_set_user + psa_pake_set_user(&spake2p_p, ...); // Prover identity + psa_pake_set_peer(&spake2p_p, ...); // Verifier identity + psa_pake_set_context(&spake2p_p, ...); - .. summary:: - Set the user ID for a password-authenticated key exchange. +**Verifier** + To prepare a SPAKE2+ operation for the Verifier, initialize and set up a :code:`psa_pake_operation_t` object by calling the following functions: - .. param:: psa_pake_operation_t *operation - Active PAKE operation. - .. param:: const uint8_t *user_id - The user ID to authenticate with. - .. param:: size_t user_id_len - Size of the ``user_id`` buffer in bytes. + .. code-block:: xref - .. return:: psa_status_t - .. retval:: PSA_SUCCESS - Success. - .. retval:: PSA_ERROR_BAD_STATE - The following conditions can result in this error: + psa_pake_operation_t spake2p_v = PSA_PAKE_OPERATION_INIT; - * The operation state is not valid: it must be active, and `psa_pake_set_user()`, `psa_pake_input()`, and `psa_pake_output()` must not have been called yet. - * The library requires initializing by a call to :code:`psa_crypto_init()`. - .. retval:: PSA_ERROR_INVALID_ARGUMENT - ``user_id`` is not valid for the operation's algorithm and cipher suite. - .. retval:: PSA_ERROR_NOT_SUPPORTED - The value of ``user_id`` is not supported by the implementation. - .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY - .. retval:: PSA_ERROR_COMMUNICATION_FAILURE - .. retval:: PSA_ERROR_CORRUPTION_DETECTED + psa_pake_setup(&spake2p_v, pake_key_v, &cipher_suite); + psa_pake_set_role(&spake2p_v, PSA_PAKE_ROLE_SERVER); - Call this function to set the user ID. - For PAKE algorithms that associate a user identifier with both participants in the session, also call `psa_pake_set_peer()` with the peer ID. - For PAKE algorithms that associate a single user identifier with the session, call `psa_pake_set_user()` only. + The key ``pake_key_v`` is a SPAKE2+ key pair, `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()`, or public key, `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`. + See :secref:`spake2p-cipher-suites` for details on constructing a suitable cipher suite. - Refer to the documentation of individual PAKE algorithms for more information. - See :secref:`pake-algorithms`. +**Verifier** + Provide any additional, optional, parameters: -.. function:: psa_pake_set_peer + .. code-block:: xref - .. summary:: - Set the peer ID for a password-authenticated key exchange. + psa_pake_set_user(&spake2p_v, ...); // Verifier identity + psa_pake_set_peer(&spake2p_v, ...); // Prover identity + psa_pake_set_context(&spake2p_v, ...); - .. param:: psa_pake_operation_t *operation - Active PAKE operation. - .. param:: const uint8_t *peer_id - The peer's ID to authenticate. - .. param:: size_t peer_id_len - Size of the ``peer_id`` buffer in bytes. +Key exchange +^^^^^^^^^^^^ - .. return:: psa_status_t - .. retval:: PSA_SUCCESS - Success. - .. retval:: PSA_ERROR_BAD_STATE - The following conditions can result in this error: +After setup, the key exchange and confirmation flow for SPAKE2+ is as follows: - * The operation state is not valid: it must be active, and `psa_pake_set_peer()`, `psa_pake_input()`, and `psa_pake_output()` must not have been called yet. - * Calling `psa_pake_set_peer()` is invalid with the operation's algorithm. - * The library requires initializing by a call to :code:`psa_crypto_init()`. - .. retval:: PSA_ERROR_INVALID_ARGUMENT - ``peer_id`` is not valid for the operation's algorithm and cipher suite. - .. retval:: PSA_ERROR_NOT_SUPPORTED - The value of ``peer_id`` is not supported by the implementation. - .. retval:: PSA_ERROR_NOT_SUPPORTED - .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY - .. retval:: PSA_ERROR_COMMUNICATION_FAILURE - .. retval:: PSA_ERROR_CORRUPTION_DETECTED +**Prover** + To get the key share to send to the Verifier, call: - Call this function in addition to `psa_pake_set_user()` for PAKE algorithms that associate a user identifier with both participants in the session. - For PAKE algorithms that associate a single user identifier with the session, call `psa_pake_set_user()` only. + .. code-block:: xref - Refer to the documentation of individual PAKE algorithms for more information. - See :secref:`pake-algorithms`. + // Get shareP + psa_pake_output(&spake2p_p, PSA_PAKE_STEP_KEY_SHARE, ...); -.. function:: psa_pake_set_context +**Verifier** + To provide and validate the Prover key share, call: - .. summary:: - Set the context data for a password-authenticated key exchange. + .. code-block:: xref - .. param:: psa_pake_operation_t *operation - Active PAKE operation. - .. param:: const uint8_t *context - The peer's ID to authenticate. - .. param:: size_t context_len - Size of the ``context`` buffer in bytes. + // Set shareP + psa_pake_input(&spake2p_v, PSA_PAKE_STEP_KEY_SHARE, ...); - .. return:: psa_status_t - .. retval:: PSA_SUCCESS - Success. - .. retval:: PSA_ERROR_BAD_STATE - The following conditions can result in this error: +**Verifier** + To get the Verifier key share and confirmation value to send to the Prover, call: - * The operation state is not valid: it must be active, and `psa_pake_set_context()`, `psa_pake_input()`, and `psa_pake_output()` must not have been called yet. - * Calling `psa_pake_set_context()` is invalid with the operation's algorithm. - * The library requires initializing by a call to :code:`psa_crypto_init()`. - .. retval:: PSA_ERROR_INVALID_ARGUMENT - ``context`` is not valid for the operation's algorithm and cipher suite. - .. retval:: PSA_ERROR_NOT_SUPPORTED - The value of ``context`` is not supported by the implementation. - .. retval:: PSA_ERROR_NOT_SUPPORTED - .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY - .. retval:: PSA_ERROR_COMMUNICATION_FAILURE - .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. code-block:: xref - Call this function for PAKE algorithms that accept additional context data as part of the protocol setup. + // Get shareV + psa_pake_output(&spake2p_v, PSA_PAKE_STEP_KEY_SHARE, ...); + // Get confirmV + psa_pake_output(&spake2p_v, PSA_PAKE_STEP_CONFIRM, ...); - Refer to the documentation of individual PAKE algorithms for more information. - See :secref:`pake-algorithms`. +**Prover** + To provide and validate the Verifier key share, and confirm the Verifier key, call: -.. function:: psa_pake_output + .. code-block:: xref - .. summary:: - Get output for a step of a password-authenticated key exchange. + // Set shareV + psa_pake_input(&spake2p_p, PSA_PAKE_STEP_KEY_SHARE, ...); + // Set confirmV + psa_pake_input(&spake2p_p, PSA_PAKE_STEP_KEY_CONFIRM, ...); - .. param:: psa_pake_operation_t *operation - Active PAKE operation. - .. param:: psa_pake_step_t step - The step of the algorithm for which the output is requested. - .. param:: uint8_t *output - Buffer where the output is to be written. - The format of the output depends on the ``step``, see :secref:`pake-steps`. - .. param:: size_t output_size - Size of the ``output`` buffer in bytes. - This must be appropriate for the cipher suite and output step: +**Prover** + To get the Prover key confirmation value to send to the Verifier, call: - * A sufficient output size is :code:`PSA_PAKE_OUTPUT_SIZE(alg, primitive, step)` where ``alg`` and ``primitive`` are the PAKE algorithm and primitive in the operation's cipher suite, and ``step`` is the output step. - * `PSA_PAKE_OUTPUT_MAX_SIZE` evaluates to the maximum output size of any supported PAKE algorithm, primitive and step. - .. param:: size_t *output_length - On success, the number of bytes of the returned output. + .. code-block:: xref - .. return:: psa_status_t - .. retval:: PSA_SUCCESS - Success. - The first ``(*output_length)`` bytes of ``output`` contain the output. - .. retval:: PSA_ERROR_BAD_STATE - The following conditions can result in this error: + // Get confirmV + psa_pake_output(&spake2p_p, PSA_PAKE_STEP_CONFIRM, ...); - * The operation state is not valid: it must be active and fully set up, and this call must conform to the algorithm's requirements for ordering of input and output steps. - * The library requires initializing by a call to :code:`psa_crypto_init()`. - .. retval:: PSA_ERROR_INVALID_ARGUMENT - ``step`` is not compatible with the operation's algorithm. - .. retval:: PSA_ERROR_NOT_SUPPORTED - ``step`` is not supported with the operation's algorithm. - .. retval:: PSA_ERROR_BUFFER_TOO_SMALL - The size of the ``output`` buffer is too small. - `PSA_PAKE_OUTPUT_SIZE()` or `PSA_PAKE_OUTPUT_MAX_SIZE` can be used to determine a sufficient buffer size. - .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY - .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY - .. retval:: PSA_ERROR_COMMUNICATION_FAILURE - .. retval:: PSA_ERROR_CORRUPTION_DETECTED - .. retval:: PSA_ERROR_STORAGE_FAILURE - .. retval:: PSA_ERROR_DATA_CORRUPT - .. retval:: PSA_ERROR_DATA_INVALID +**Verifier** + To confirm the Prover key, call: - Depending on the algorithm being executed, you might need to call this function several times or you might not need to call this at all. + .. code-block:: xref - The exact sequence of calls to perform a password-authenticated key exchange depends on the algorithm in use. - Refer to the documentation of individual PAKE algorithms for more information. - See :secref:`pake-algorithms`. + // Set shareP + psa_pake_input(&spake2p_v, PSA_PAKE_STEP_CONFIRM, ...); - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_pake_abort()`. +**Prover** + To use the shared secret, extract it as a key-derivation key. + For example, to extract a derivation key for HKDF-SHA-256: -.. function:: psa_pake_input + .. code-block:: xref - .. summary:: - Provide input for a step of a password-authenticated key exchange. + // Set up the key attributes + psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT; + psa_key_set_type(&att, PSA_KEY_TYPE_DERIVE); + psa_key_set_usage_flags(&att, PSA_KEY_USAGE_DERIVE); + psa_key_set_algorithm(&att, PSA_ALG_HKDF(PSA_ALG_SHA256)); - .. param:: psa_pake_operation_t *operation - Active PAKE operation. - .. param:: psa_pake_step_t step - The step for which the input is provided. - .. param:: const uint8_t *input - Buffer containing the input. - The format of the input depends on the ``step``, see :secref:`pake-steps`. - .. param:: size_t input_length - Size of the ``input`` buffer in bytes. + // Get K_shared + psa_key_id_t shared_key; + psa_pake_get_shared_key(&spake2p_p, &att, &shared_key); - .. return:: psa_status_t - .. retval:: PSA_SUCCESS - Success. - .. retval:: PSA_ERROR_BAD_STATE - The following conditions can result in this error: +**Verifier** + To use the shared secret, extract it as a key-derivation key. + The same key attributes can be used as the Prover: - * The operation state is not valid: it must be active and fully set up, and this call must conform to the algorithm's requirements for ordering of input and output steps. - * The library requires initializing by a call to :code:`psa_crypto_init()`. - .. retval:: PSA_ERROR_INVALID_ARGUMENT - The following conditions can result in this error: + .. code-block:: xref - * ``step`` is not compatible with the operation's algorithm. - * The input is not valid for the operation's algorithm, cipher suite or ``step``. - .. retval:: PSA_ERROR_INVALID_SIGNATURE - The verification fails for a `PSA_PAKE_STEP_ZK_PROOF` or `PSA_PAKE_STEP_CONFIRM` input step. - .. retval:: PSA_ERROR_NOT_SUPPORTED - The following conditions can result in this error: + // Get K_shared + psa_key_id_t shared_key; + psa_pake_get_shared_key(&spake2p_v, &att, &shared_key); - * ``step`` is not supported with the operation's algorithm. - * The input is not supported for the operation's algorithm, cipher suite or ``step``. - .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY - .. retval:: PSA_ERROR_COMMUNICATION_FAILURE - .. retval:: PSA_ERROR_CORRUPTION_DETECTED - .. retval:: PSA_ERROR_STORAGE_FAILURE - .. retval:: PSA_ERROR_DATA_CORRUPT - .. retval:: PSA_ERROR_DATA_INVALID +The shared secret that is produced by SPAKE2+ is pseudorandom. +Although it can be used directly as an encryption key, it is recommended to use the shared secret as an input to a key derivation operation to produce additional cryptographic keys. - Depending on the algorithm being executed, you might need to call this function several times or you might not need to call this at all. +For more information about the format of the values which are passed for each step, see :secref:`pake-steps`. - The exact sequence of calls to perform a password-authenticated key exchange depends on the algorithm in use. - Refer to the documentation of individual PAKE algorithms for more information. - See :secref:`pake-algorithms`. +If the validation of a key share fails, then the corresponding call to `psa_pake_input()` for the `PSA_PAKE_STEP_KEY_SHARE` step will return :code:`PSA_ERROR_INVALID_ARGUMENT`. +If the verification of a key confirmation value fails, then the corresponding call to `psa_pake_input()` for the `PSA_PAKE_STEP_CONFIRM` step will return :code:`PSA_ERROR_INVALID_SIGNATURE`. - `PSA_PAKE_INPUT_SIZE()` or `PSA_PAKE_INPUT_MAX_SIZE` can be used to allocate buffers of sufficient size to transfer inputs that are received from the peer into the operation. +.. _spake2p-keys: - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_pake_abort()`. +SPAKE2+ keys +~~~~~~~~~~~~ -.. function:: psa_pake_get_shared_key +.. macro:: PSA_KEY_TYPE_SPAKE2P_KEY_PAIR + :definition: /* specification-defined value */ .. summary:: - Extract the shared secret from the PAKE as a key. + SPAKE2+ key pair: both the prover and verifier key. - .. param:: psa_pake_operation_t *operation - Active PAKE operation. - .. param:: const psa_key_attributes_t * attributes - The attributes for the new key. - This function uses the attributes as follows: + .. param:: curve + A value of type :code:`psa_ecc_family_t` that identifies the Elliptic curve family to be used. - * The key type is required. - All PAKE algorithms can output a key of type :code:`PSA_KEY_TYPE_DERIVE` or :code:`PSA_KEY_TYPE_HMAC`. - PAKE algorithms that produce a pseudo-random shared secret, can also output block-cipher key types, for example :code:`PSA_KEY_TYPE_AES`. - Refer to the documentation of individual PAKE algorithms for more information. - See :secref:`pake-algorithms`. - * The key size in ``attributes`` must be zero. - The returned key size is always determined from the PAKE shared secret. - * The key permitted-algorithm policy is required for keys that will be used for a cryptographic operation. + The size of a SPAKE2+ key is the size associated with the Elliptic curve group, that is, :math:`\lceil{log_2(q)}\rceil` for a curve over a field :math:`\mathbb{F}_q`. + See the documentation of each Elliptic curve family for details. + + To construct a SPAKE2+ key pair, it must be output from a key derivation operation. + + The corresponding public key can be exported using :code:`psa_export_public_key()`. + See also `PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY()`. + + .. subsection:: Key derivation + + A SPAKE2+ key pair can be output from a key derivation using :code:`psa_key_derivation_output_key()`. + The SPAKE2+ protocol recommends that a key-stretching key-derivation function, such as PBKDF2, is used to hash the SPAKE2+ password. + See :rfc:`9383` for details. + + The key derivation process in :code:`psa_key_derivation_output_key()` follows the recommendations for the registration process in :rfc:`9383`, and matches the specification of this process in :cite:`MATTER`. + + For the |API|: - .. see :secref:`permitted-algorithms`. + * The derivation of SPAKE2+ keys extracts :math:`\lceil{log_2(p)/8}\rceil+8` bytes from the PBKDF for each of :math:`w0s` and :math:`w1s`, where :math:`p` is the prime factor of the order of the elliptic curve group. + The following sizes are used for extracting :math:`w0s` and :math:`w1s`, depending on the elliptic curve: - * The key usage flags define what operations are permitted with the key. + .. csv-table:: + :header-rows: 1 + :widths: auto + :align: left - .. see :secref:`key-usage-flags`. + Elliptic curve, "Size of :math:`w0s` and :math:`w1s`, in bytes" + P-256, 40 + P-384, 56 + P-521, 74 + edwards25519, 40 + edwards448, 64 - * The key lifetime and identifier are required for a persistent key. + :issue:`I think these values are correct?` - .. note:: - This is an input parameter: it is not updated with the final key attributes. - The final attributes of the new key can be queried by calling :code:`psa_get_key_attributes()` with the key's identifier. - .. param:: psa_key_id_t * key - On success, an identifier for the newly created key. :code:`PSA_KEY_ID_NULL` on failure. + * The calculation of :math:`w0`, :math:`w1`, and :math:`L` then proceeds as described in the RFC. + .. admonition:: Implementation note - .. return:: psa_status_t - .. retval:: PSA_SUCCESS - Success. - If the key is persistent, the key material and the key's metadata have been saved to persistent storage. - .. retval:: PSA_ERROR_BAD_STATE - The following conditions can result in this error: + The values of :math:`w0` and :math:`w1` are required as part of the SPAKE2+ key pair. - * The state of PAKE operation ``operation`` is not valid: it must be ready to return the shared secret. + It is :scterm:`implementation defined` whether :math:`L` is computed during key derivation, and stored as part of the key pair; or only computed when required from the key pair. - For an unconfirmed key, this will be when the key-exchange output and input steps are complete, but prior to any key-confirmation output and input steps. + .. subsection:: Compatible algorithms - For a confirmed key, this will be when all key-exchange and key-confirmation output and input steps are complete. - * The library requires initializing by a call to :code:`psa_crypto_init()`. - .. retval:: PSA_ERROR_ALREADY_EXISTS - This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. - .. retval:: PSA_ERROR_NOT_SUPPORTED - The key attributes, as a whole, are not supported for creation from a PAKE secret, either by the implementation in general or in the specified storage location. - .. retval:: PSA_ERROR_INVALID_ARGUMENT - The following conditions can result in this error: + | `PSA_ALG_SPAKE2P_HMAC` + | `PSA_ALG_SPAKE2P_CMAC` + | `PSA_ALG_SPAKE2P_MATTER` - * The key type is not valid for output from this operation's algorithm. - * The key size is nonzero. - * The key lifetime is invalid. - * The key identifier is not valid for the key lifetime. - * The key usage flags include invalid values. - * The key's permitted-usage algorithm is invalid. - * The key attributes, as a whole, are invalid. - .. retval:: PSA_ERROR_NOT_PERMITTED - The implementation does not permit creating a key with the specified attributes due to some implementation-specific policy. - .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY - .. retval:: PSA_ERROR_COMMUNICATION_FAILURE - .. retval:: PSA_ERROR_CORRUPTION_DETECTED - .. retval:: PSA_ERROR_STORAGE_FAILURE - .. retval:: PSA_ERROR_DATA_CORRUPT - .. retval:: PSA_ERROR_DATA_INVALID +.. macro:: PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY + :definition: /* specification-defined value */ - This is the final call in a PAKE operation, which retrieves the shared secret as a key. - It is recommended that this key is used as an input to a key derivation operation to produce additional cryptographic keys. - For some PAKE algorithms, the shared secret is also suitable for use as a key in cryptographic operations such as encryption. - Refer to the documentation of individual PAKE algorithms for more information, see :secref:`pake-algorithms`. + .. summary:: + SPAKE2+ public key: the verifier key. - Depending on the key confirmation requested in the cipher suite, `psa_pake_get_shared_key()` must be called either before or after the key-confirmation output and input steps for the PAKE algorithm. - The key confirmation affects the guarantees that can be made about the shared key: + .. param:: curve + A value of type :code:`psa_ecc_family_t` that identifies the Elliptic curve family to be used. - .. list-table:: - :class: borderless - :widths: 1 4 + The size of an SPAKE2+ public key is the same as the corresponding private key. + See `PSA_KEY_TYPE_SPAKE2P_KEY_PAIR()` and the documentation of each Elliptic curve family for details. - * - **Unconfirmed key** - - If the cipher suite used to set up the operation requested an unconfirmed key, the application must call `psa_pake_get_shared_key()` after the key-exchange output and input steps are completed. - The PAKE algorithm provides a cryptographic guarantee that only a peer who used the same password, and identity inputs, is able to compute the same key. - However, there is no guarantee that the peer is the participant it claims to be, and was able to compute the same key. + To construct a SPAKE2+ public key, it must be imported. - Since the peer is not authenticated, no action should be taken that assumes that the peer is who it claims to be. - For example, do not access restricted files on the peer's behalf until an explicit authentication has succeeded. + .. subsection:: Public key format - .. note:: - Some PAKE algorithms do not enable the output of the shared secret until it has been confirmed. + A SPAKE2+ public key can be exported and imported, to enable use cases that require offline registration. - * - **Confirmed key** - - If the cipher suite used to set up the operation requested a confirmed key, the application must call `psa_pake_get_shared_key()` after the key-exchange and key-confirmation output and input steps are completed. + The public key consists of the two values :math:`w0` and :math:`L`, which result from the SPAKE2+ registration phase. + :math:`w0` is a scalar in the same range as a private Elliptic curve key from the group used as the SPAKE2+ primitive group. + :math:`L` is a point on the curve, similar to a public key from the same group, corresponding to the :math:`w1` value in the key pair. - Following key confirmation, the PAKE algorithm provides a cryptographic guarantee that the peer used the same password and identity inputs, and has computed the identical shared secret key. + For the |API|, the default format for a SPAKE2+ public key is the concatenation of the formatted values for :math:`w0` and :math:`L`, using the standard formats for Elliptic curve keys used by the |API|. + For example, for SPAKE2+ over P-256 (secp256r1), the output from :code:`psa_export_public_key()` would be the concatenation of: - Since the peer is not authenticated, no action should be taken that assumes that the peer is who it claims to be. - For example, do not access restricted files on the peer's behalf until an explicit authentication has succeeded. + * The 32-byte formatted value of the P-256 private key :math:`w0`. + This is a big-endian encoding of the integer :math:`w0`. + * The 65-byte formatted value of the P-256 public key :math:`L`. + This is itself a concatenation of: - .. note:: - Some PAKE algorithms do not include any key-confirmation steps. + - The byte ``0x04``. + - The 32-byte big-endian encoding of the x-coordinate of :math:`L`. + - The 32-byte big-endian encoding of the y-coordinate of :math:`L`. - The exact sequence of calls to perform a password-authenticated key exchange depends on the algorithm in use. - Refer to the documentation of individual PAKE algorithms for more information. - See :secref:`pake-algorithms`. + .. subsection:: Compatible algorithms - When this function returns successfully, ``operation`` becomes inactive. - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_pake_abort()`. + | `PSA_ALG_SPAKE2P_HMAC` (verification only) + | `PSA_ALG_SPAKE2P_CMAC` (verification only) + | `PSA_ALG_SPAKE2P_MATTER` (verification only) -.. function:: psa_pake_abort +.. macro:: PSA_KEY_TYPE_IS_SPAKE2P + :definition: /* specification-defined value */ .. summary:: - Abort a PAKE operation. + Whether a key type is a SPAKE2+ key, either a key pair or a public key. - .. param:: psa_pake_operation_t * operation - Initialized PAKE operation. + .. param:: type + A key type: a value of type :code:`psa_key_type_t`. - .. return:: psa_status_t - .. retval:: PSA_SUCCESS - Success. - The operation object can now be discarded or reused. - .. retval:: PSA_ERROR_COMMUNICATION_FAILURE - .. retval:: PSA_ERROR_CORRUPTION_DETECTED - .. retval:: PSA_ERROR_BAD_STATE - The library requires initializing by a call to :code:`psa_crypto_init()`. +.. macro:: PSA_KEY_TYPE_IS_SPAKE2P_KEY_PAIR + :definition: /* specification-defined value */ - Aborting an operation frees all associated resources except for the ``operation`` object itself. - Once aborted, the operation object can be reused for another operation by calling `psa_pake_setup()` again. + .. summary:: + Whether a key type is a SPAKE2+ key pair. - This function can be called any time after the operation object has been initialized as described in `psa_pake_operation_t`. + .. param:: type + A key type: a value of type :code:`psa_key_type_t`. - In particular, calling `psa_pake_abort()` after the operation has been terminated by a call to `psa_pake_abort()` or `psa_pake_get_shared_key()` is safe and has no effect. +.. macro:: PSA_KEY_TYPE_IS_SPAKE2P_PUBLIC_KEY + :definition: /* specification-defined value */ + .. summary:: + Whether a key type is a SPAKE2+ public key. -Support macros -~~~~~~~~~~~~~~ + .. param:: type + A key type: a value of type :code:`psa_key_type_t`. -.. macro:: PSA_ALG_IS_JPAKE +.. macro:: PSA_KEY_TYPE_SPAKE2P_GET_FAMILY :definition: /* specification-defined value */ .. summary:: - Whether the specified algorithm is a J-PAKE algorithm. + Extract the curve family from a SPAKE2+ key type. - .. param:: alg - An algorithm identifier: a value of type :code:`psa_algorithm_t`. + .. param:: type + A SPAKE2+ key type: a value of type :code:`psa_key_type_t` such that :code:`PSA_KEY_TYPE_IS_SPAKE2P(type)` is true. - .. return:: - ``1`` if ``alg`` is a J-PAKE algorithm, ``0`` otherwise. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + .. return:: psa_ecc_family_t + The elliptic curve family id, if ``type`` is a supported SPAKE2+ key. Unspecified if ``type`` is not a supported SPAKE2+ key. - J-PAKE algorithms are constructed using :code:`PSA_ALG_JPAKE(hash_alg)`. +.. _spake2p-algorithms: -.. macro:: PSA_ALG_IS_SPAKE2P +SPAKE2+ algorithms +~~~~~~~~~~~~~~~~~~ + +.. macro:: PSA_ALG_SPAKE2P_HMAC :definition: /* specification-defined value */ .. summary:: - Whether the specified algorithm is a SPAKE2+ algorithm. + Macro to build the SPAKE2+ algorithm, using HMAC-based key confirmation. - .. param:: alg - An algorithm identifier: a value of type :code:`psa_algorithm_t`. + .. param:: hash_alg + A hash algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_HASH(hash_alg)` is true. .. return:: - ``1`` if ``alg`` is a SPAKE2+ algorithm, ``0`` otherwise. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + A SPAKE2+ algorithm, using HMAC for key confirmation, parameterized by a specific hash. - SPAKE2+ algorithms are constructed using :code:`PSA_ALG_SPAKE2P_HMAC(hash_alg)`, :code:`PSA_ALG_SPAKE2P_CMAC(hash_alg)`, or :code:`PSA_ALG_SPAKE2P_MATTER`. + Unspecified if ``hash_alg`` is not a supported hash algorithm. -.. macro:: PSA_ALG_IS_SPAKE2P_HMAC - :definition: /* specification-defined value */ + This is SPAKE2+, as defined by :RFC-title:`9383`, for cipher suites that use HMAC for key confirmation. + SPAKE2+ cipher suites are specified in :rfc:`9383#4`. + The cipher suite's hash algorithm is used as input to `PSA_ALG_SPAKE2P_HMAC()`. - .. summary:: - Whether the specified algorithm is a SPAKE2+ algorithm that uses a HMAC-based key confirmation. + The shared secret that is produced by SPAKE2+ is pseudorandom. + Although it can be used directly as an encryption key, it is recommended to use the shared secret as an input to a key derivation operation to produce additional cryptographic keys. - .. param:: alg - An algorithm identifier: a value of type :code:`psa_algorithm_t`. + See :secref:`pake-spake2p` for the SPAKE2+ protocol flow and how to implement it with the |API|. - .. return:: - ``1`` if ``alg`` is a SPAKE2+ algorithm that uses a HMAC-based key confirmation, ``0`` otherwise. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + .. subsection:: Compatible key types - SPAKE2+ algorithms, using HMAC-based key confirmation, are constructed using :code:`PSA_ALG_SPAKE2P_HMAC(hash_alg)`. + | :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR` + | :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY` (verification only) -.. macro:: PSA_ALG_IS_SPAKE2P_CMAC + +.. macro:: PSA_ALG_SPAKE2P_CMAC :definition: /* specification-defined value */ .. summary:: - Whether the specified algorithm is a SPAKE2+ algorithm that uses a CMAC-based key confirmation. + Macro to build the SPAKE2+ algorithm, using CMAC-based key confirmation. - .. param:: alg - An algorithm identifier: a value of type :code:`psa_algorithm_t`. + .. param:: hash_alg + A hash algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_HASH(hash_alg)` is true. .. return:: - ``1`` if ``alg`` is a SPAKE2+ algorithm that uses a CMAC-based key confirmation, ``0`` otherwise. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + A SPAKE2+ algorithm, using CMAC for key confirmation, parameterized by a specific hash. - SPAKE2+ algorithms, using CMAC-based key confirmation, are constructed using :code:`PSA_ALG_SPAKE2P_CMAC(hash_alg)`. + Unspecified if ``hash_alg`` is not a supported hash algorithm. -.. macro:: PSA_PAKE_OUTPUT_SIZE - :definition: /* implementation-defined value */ - .. summary:: - Sufficient output buffer size for `psa_pake_output()`, in bytes. + This is SPAKE2+, as defined by :RFC-title:`9383`, for cipher suites that use CMAC-AES-128 for key confirmation. + SPAKE2+ cipher suites are specified in :rfc:`9383#4`. + The cipher suite's hash algorithm is used as input to `PSA_ALG_SPAKE2P_CMAC()`. - .. param:: alg - A PAKE algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_PAKE(alg)` is true. - .. param:: primitive - A primitive of type `psa_pake_primitive_t` that is compatible with algorithm ``alg``. - .. param:: output_step - A value of type `psa_pake_step_t` that is valid for the algorithm ``alg``. + The shared secret that is produced by SPAKE2+ is pseudorandom. + Although it can be used directly as an encryption key, it is recommended to use the shared secret as an input to a key derivation operation to produce additional cryptographic keys. - .. return:: - A sufficient output buffer size for the specified PAKE algorithm, primitive, and output step. - An implementation can return either ``0`` or a correct size for a PAKE algorithm, primitive, and output step that it recognizes, but does not support. - If the parameters are not valid, the return value is unspecified. + See :secref:`pake-spake2p` for the SPAKE2+ protocol flow and how to implement it with the |API|. - If the size of the output buffer is at least this large, it is guaranteed that `psa_pake_output()` will not fail due to an insufficient buffer size. - The actual size of the output might be smaller in any given call. + .. subsection:: Compatible key types - See also `PSA_PAKE_OUTPUT_MAX_SIZE` + | :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR` + | :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY` (verification only) -.. macro:: PSA_PAKE_OUTPUT_MAX_SIZE - :definition: /* implementation-defined value */ +.. macro:: PSA_ALG_SPAKE2P_MATTER + :definition: ((psa_algoirithm_t)0x0A000609) .. summary:: - Sufficient output buffer size for `psa_pake_output()` for any of the supported PAKE algorithms, primitives and output steps. + The SPAKE2+ algorithm, as used by the Matter v1 specification. - If the size of the output buffer is at least this large, it is guaranteed that `psa_pake_output()` will not fail due to an insufficient buffer size. + This is the PAKE algorithm specified as MATTER_PAKE in :cite-title:`MATTER`. + This is based on draft-02 of the SPAKE2+ protocol, :cite-title:`SPAKE2P-2`. + :cite:`MATTER` specifies a single SPAKE2+ cipher suite, P256-SHA256-HKDF-HMAC-SHA256. - See also `PSA_PAKE_OUTPUT_SIZE()`. + The shared secret that is produced by this operation must be processed as directed by the :cite:`MATTER` specification. -.. macro:: PSA_PAKE_INPUT_SIZE - :definition: /* implementation-defined value */ + This algorithm uses the same SPAKE2+ key types, key derivation, protocol flow, and the API usage described in :secref:`pake-spake2p`. + However, the following aspects are different: + + * The key schedule is different. + This affects the computation of the shared secret and key confirmation values. + * The protocol inputs and outputs have been renamed between draft-02 and the final RFC, as follows: + + .. csv-table:: + :header-rows: 1 + :widths: auto + :align: left + + RFC 9383, Draft-02 + shareP, pA + shareV, pB + confirmP, cA + confirmV, cB + K_shared, Ke + + .. subsection:: Compatible key types + + | :code:`PSA_KEY_TYPE_SPAKE2P_KEY_PAIR` + | :code:`PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY` (verification only) + +.. macro:: PSA_ALG_IS_SPAKE2P + :definition: /* specification-defined value */ .. summary:: - Sufficient buffer size for inputs to `psa_pake_input()`. + Whether the specified algorithm is a SPAKE2+ algorithm. .. param:: alg - A PAKE algorithm: a value of type :code:`psa_algorithm_t` such that :code:`PSA_ALG_IS_PAKE(alg)` is true. - .. param:: primitive - A primitive of type `psa_pake_primitive_t` that is compatible with algorithm ``alg``. - .. param:: input_step - A value of type `psa_pake_step_t` that is valid for the algorithm ``alg``. + An algorithm identifier: a value of type :code:`psa_algorithm_t`. .. return:: - A sufficient buffer size for the specified PAKE algorithm, primitive, and input step. - An implementation can return either ``0`` or a correct size for a PAKE algorithm, primitive, and output step that it recognizes, but does not support. - If the parameters are not valid, the return value is unspecified. + ``1`` if ``alg`` is a SPAKE2+ algorithm, ``0`` otherwise. + This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. - The value returned by this macro is guaranteed to be large enough for any valid input to `psa_pake_input()` in an operation with the specified parameters. + SPAKE2+ algorithms are constructed using :code:`PSA_ALG_SPAKE2P_HMAC(hash_alg)`, :code:`PSA_ALG_SPAKE2P_CMAC(hash_alg)`, or :code:`PSA_ALG_SPAKE2P_MATTER`. - This macro can be useful when transferring inputs from the peer into the PAKE operation. +.. macro:: PSA_ALG_IS_SPAKE2P_HMAC + :definition: /* specification-defined value */ - See also `PSA_PAKE_INPUT_MAX_SIZE` + .. summary:: + Whether the specified algorithm is a SPAKE2+ algorithm that uses a HMAC-based key confirmation. -.. macro:: PSA_PAKE_INPUT_MAX_SIZE - :definition: /* implementation-defined value */ + .. param:: alg + An algorithm identifier: a value of type :code:`psa_algorithm_t`. + + .. return:: + ``1`` if ``alg`` is a SPAKE2+ algorithm that uses a HMAC-based key confirmation, ``0`` otherwise. + This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + + SPAKE2+ algorithms, using HMAC-based key confirmation, are constructed using :code:`PSA_ALG_SPAKE2P_HMAC(hash_alg)`. + +.. macro:: PSA_ALG_IS_SPAKE2P_CMAC + :definition: /* specification-defined value */ .. summary:: - Sufficient buffer size for inputs to `psa_pake_input()` for any of the supported PAKE algorithms, primitives and input steps. + Whether the specified algorithm is a SPAKE2+ algorithm that uses a CMAC-based key confirmation. - This macro can be useful when transferring inputs from the peer into the PAKE operation. + .. param:: alg + An algorithm identifier: a value of type :code:`psa_algorithm_t`. - See also `PSA_PAKE_INPUT_SIZE()`. + .. return:: + ``1`` if ``alg`` is a SPAKE2+ algorithm that uses a CMAC-based key confirmation, ``0`` otherwise. + This macro can return either ``0`` or ``1`` if ``alg`` is not a supported PAKE algorithm identifier. + + SPAKE2+ algorithms, using CMAC-based key confirmation, are constructed using :code:`PSA_ALG_SPAKE2P_CMAC(hash_alg)`. diff --git a/doc/ext-pake/figure/spake2plus-reg.pdf b/doc/ext-pake/figure/spake2plus-reg.pdf index 7140f56066fde4cee1d1a2e8c07c53d1a8b9bf05..c2ed27e6e67efe9bcefb249f242f4d1499baba1b 100644 GIT binary patch delta 22114 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 diff --git a/doc/ext-pake/figure/spake2plus-reg.puml b/doc/ext-pake/figure/spake2plus-reg.puml index a9f1929f..d8d61f6f 100644 --- a/doc/ext-pake/figure/spake2plus-reg.puml +++ b/doc/ext-pake/figure/spake2plus-reg.puml @@ -19,8 +19,8 @@ Verifier -> Verifier: ""psa_key_derivation_setup(PBKDF)""\n""psa_key_derivation_input_key(password)""\n""psa_key_derivation_input_xxx()"" for //PBKDF-params// - Verifier -> Verifier: ""psa_key_derivation_output_key(SPAKE2P_PUBLIC_KEY)"" - note left: Compute public-key (//w0//, //L//) + Verifier -> Verifier: ""psa_key_derivation_output_key(SPAKE2P_KEY_PAIR)"" + note left: Compute key-pair (//w0//, //w1//) else Connected registration @@ -35,7 +35,7 @@ end note over Prover: Use key-pair for authentication flow - / note over Verifier: Use public-key for authentication flow + / note over Verifier: Use key for authentication flow @enduml diff --git a/doc/ext-pake/figure/spake2plus-reg.svg b/doc/ext-pake/figure/spake2plus-reg.svg index 43a0c947..204b62a3 100644 --- a/doc/ext-pake/figure/spake2plus-reg.svg +++ b/doc/ext-pake/figure/spake2plus-reg.svg @@ -1 +1 @@ -Prover(Client role)Verifier(Server role)Initial information : cipher suite,PBKDF-params,passwordpsa_key_derivation_setup(PBKDF)psa_key_derivation_input_key(password)psa_key_derivation_input_xxx()forPBKDF-paramspsa_key_derivation_output_key(SPAKE2P_KEY_PAIR)Compute key-pair (w0,w1)alt[Independent registration]psa_key_derivation_setup(PBKDF)psa_key_derivation_input_key(password)psa_key_derivation_input_xxx()forPBKDF-paramspsa_key_derivation_output_key(SPAKE2P_PUBLIC_KEY)Compute public-key (w0,L)[Connected registration]psa_export_public_key()ComputeLand outputw0||LRegistration record (w0||L)psa_import_key(SPAKE2P_PUBLIC_KEY)fromw0||LImport public-key (w0,L)Use key-pair for authentication flowUse public-key for authentication flow \ No newline at end of file +Prover(Client role)Verifier(Server role)Initial information : cipher suite,PBKDF-params,passwordpsa_key_derivation_setup(PBKDF)psa_key_derivation_input_key(password)psa_key_derivation_input_xxx()forPBKDF-paramspsa_key_derivation_output_key(SPAKE2P_KEY_PAIR)Compute key-pair (w0,w1)alt[Independent registration]psa_key_derivation_setup(PBKDF)psa_key_derivation_input_key(password)psa_key_derivation_input_xxx()forPBKDF-paramspsa_key_derivation_output_key(SPAKE2P_KEY_PAIR)Compute key-pair (w0,w1)[Connected registration]psa_export_public_key()ComputeLand outputw0||LRegistration record (w0||L)psa_import_key(SPAKE2P_PUBLIC_KEY)fromw0||LImport public-key (w0,L)Use key-pair for authentication flowUse key for authentication flow \ No newline at end of file diff --git a/doc/ext-pake/figure/spake2plus.pdf b/doc/ext-pake/figure/spake2plus.pdf index aba6461a955029b1c3f2fb1e31ada9d8c3c941fd..3f94c1190c1c4fa62e218c76ea133e8fd335ae54 100644 GIT binary patch delta 3943 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 bW1y5Nz~!MBI1!(TV?v;q#l*CYbeR7GxYL^R delta 3924 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 Prover: ""psa_pake_output()"" for //shareP// = //X// Prover ->> Verifier: (//shareP//) - Verifier -> Verifier: ""psa_pake_setup()"" with key (//w0//, //L//)\n""psa_pake_set_role(PSA_PAKE_ROLE_SERVER)""\n""psa_pake_set_user(VerifierId)""\n""psa_pake_set_peer(ProverId)""\n""psa_pake_set_context(Context)"" + Verifier -> Verifier: ""psa_pake_setup()"" with key (//w0//, //L//) or key (//w0//, //w1//)\n""psa_pake_set_role(PSA_PAKE_ROLE_SERVER)""\n""psa_pake_set_user(VerifierId)""\n""psa_pake_set_peer(ProverId)""\n""psa_pake_set_context(Context)"" note over Verifier: Generate key share //Y// Verifier -> Verifier: ""psa_pake_input()"" for //shareP// diff --git a/doc/ext-pake/figure/spake2plus.svg b/doc/ext-pake/figure/spake2plus.svg index 11051de7..f98855a0 100644 --- a/doc/ext-pake/figure/spake2plus.svg +++ b/doc/ext-pake/figure/spake2plus.svg @@ -1 +1 @@ -Prover(Client role)Verifier(Server role)Shared information : cipher suite,ProverId,VerifierId, andContextRegistration record (w0,L) derived from passwordProver 'key pair' (w0,w1) derived from passwordpsa_pake_setup()with key (w0,w1)psa_pake_set_role(PSA_PAKE_ROLE_CLIENT)psa_pake_set_user(ProverId)psa_pake_set_peer(VerifierId)psa_pake_set_context(Context)Generate key shareXpsa_pake_output()forshareP=X(shareP)psa_pake_setup()with key (w0,L)psa_pake_set_role(PSA_PAKE_ROLE_SERVER)psa_pake_set_user(VerifierId)psa_pake_set_peer(ProverId)psa_pake_set_context(Context)Generate key shareYpsa_pake_input()forsharePValidatesharePComputeK_shared,confirmP'andconfirmVpsa_pake_output()forshareV=YandconfirmV(shareV,confirmV)psa_pake_input()forshareVValidateshareVComputeK_shared,confirmPandconfirmV'psa_pake_output()forconfirmP(confirmP)psa_pake_input()forconfirmVVerify thatconfirmV'=confirmVpsa_pake_get_shared_key()to extractK_sharedpsa_pake_input()forconfirmPVerify thatconfirmP'=confirmPpsa_pake_get_shared_key()to extractK_shared \ No newline at end of file +Prover(Client role)Verifier(Server role)Shared information : cipher suite,ProverId,VerifierId, andContextRegistration record (w0,L) derived from passwordProver 'key pair' (w0,w1) derived from passwordpsa_pake_setup()with key (w0,w1)psa_pake_set_role(PSA_PAKE_ROLE_CLIENT)psa_pake_set_user(ProverId)psa_pake_set_peer(VerifierId)psa_pake_set_context(Context)Generate key shareXpsa_pake_output()forshareP=X(shareP)psa_pake_setup()with key (w0,L) or key (w0,w1)psa_pake_set_role(PSA_PAKE_ROLE_SERVER)psa_pake_set_user(VerifierId)psa_pake_set_peer(ProverId)psa_pake_set_context(Context)Generate key shareYpsa_pake_input()forsharePValidatesharePComputeK_shared,confirmP'andconfirmVpsa_pake_output()forshareV=YandconfirmV(shareV,confirmV)psa_pake_input()forshareVValidateshareVComputeK_shared,confirmPandconfirmV'psa_pake_output()forconfirmP(confirmP)psa_pake_input()forconfirmVVerify thatconfirmV'=confirmVpsa_pake_get_shared_key()to extractK_sharedpsa_pake_input()forconfirmPVerify thatconfirmP'=confirmPpsa_pake_get_shared_key()to extractK_shared \ No newline at end of file From fd46a5a0a969fc884967ad8bd85182cfe78cd1df Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Fri, 1 Dec 2023 17:40:49 +0000 Subject: [PATCH 09/11] Update introduction text --- doc/ext-pake/overview/intro.rst | 12 ++++-------- doc/ext-pake/references | 5 ----- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/doc/ext-pake/overview/intro.rst b/doc/ext-pake/overview/intro.rst index ea82bb83..66e438af 100644 --- a/doc/ext-pake/overview/intro.rst +++ b/doc/ext-pake/overview/intro.rst @@ -1,4 +1,4 @@ -.. SPDX-FileCopyrightText: Copyright 2022 Arm Limited and/or its affiliates +.. SPDX-FileCopyrightText: Copyright 2022-2023 Arm Limited and/or its affiliates .. SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license Introduction @@ -12,16 +12,12 @@ This document is one of a set of resources provided by Arm that can help organiz About the |API| PAKE Extension ------------------------------ -This document introduces an extension to the :cite-title:`PSA-CRYPT` specification, to provide support for :term:`Password-authenticated key exchange` (PAKE) algorithms, and specifically for the J-PAKE algorithm. +This document defines an extension to the :cite-title:`PSA-CRYPT` specification, to provide support for :term:`Password-authenticated key exchange` (PAKE) protocols, and specifically for the J-PAKE and SPAKE2+ protocols. -When the proposed extension is sufficiently stable to be classed as Final, it will be integrated into a future version of `[PSA-CRYPT]`. +When the proposed extension API is sufficiently stable to be classed as Final, it will be integrated into a future version of `[PSA-CRYPT]`. This specification must be read and implemented in conjunction with `[PSA-CRYPT]`. All of the conventions, design considerations, and implementation considerations that are described in `[PSA-CRYPT]` apply to this specification. -.. note:: - - This extension has been developed in conjunction with the :cite-title:`MBED-TLS` project, which is developing an implementation of the |API|. - .. rationale:: Note This version of the document includes *Rationale* commentary that provides background information relating to the design decisions that led to the current proposal. This enables the reader to understand the wider context and alternative approaches that have been considered. @@ -114,7 +110,7 @@ The following PAKE schemes are considered in the |API| design: Scope of this specification ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The current API proposal provides the general interface for PAKE algorithms, and the specific interface for J-PAKE. +The current API proposal provides the general interface for PAKE algorithms, and the specific interface for J-PAKE and SPAKE2+. Out of scope ^^^^^^^^^^^^ diff --git a/doc/ext-pake/references b/doc/ext-pake/references index e9a16814..2d65bf26 100644 --- a/doc/ext-pake/references +++ b/doc/ext-pake/references @@ -6,11 +6,6 @@ :doc_no: IHI 0086 :url: arm-software.github.io/psa-api/crypto -.. reference:: MBED-TLS - :title: Mbed TLS - :author: Arm Ltd - :url: github.com/ARMmbed/mbedtls - .. reference:: MATTER :title: Matter Specification, Version 1.2 :author: CSA From 1a5ed155d515dca7e564b062014ee49bd908a20d Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 18 Dec 2023 16:59:16 +0000 Subject: [PATCH 10/11] Rework SPAKE2+ flow graphic to fit one page --- doc/ext-pake/figure/spake2plus.pdf | Bin 39133 -> 39764 bytes doc/ext-pake/figure/spake2plus.puml | 36 +++++++++++++++------------- doc/ext-pake/figure/spake2plus.svg | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/doc/ext-pake/figure/spake2plus.pdf b/doc/ext-pake/figure/spake2plus.pdf index 3f94c1190c1c4fa62e218c76ea133e8fd335ae54..3ada1f967c2c4e80110dcd8e7643b96e47bd53dc 100644 GIT binary patch delta 15178 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

PnOUyA_ZxN_MOP8-JXWJDRh$%4wvL~IgsGEI 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�#z4nQBm|`O>{R!U>KD9 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 G^M3%3NYMuX delta 14564 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 NbHY Prover: ""psa_pake_setup()"" with key (//w0//, //w1//)\n""psa_pake_set_role(PSA_PAKE_ROLE_CLIENT)""\n""psa_pake_set_user(ProverId)""\n""psa_pake_set_peer(VerifierId)""\n""psa_pake_set_context(Context)"" - note over Prover: Generate key share //X// - Prover -> Prover: ""psa_pake_output()"" for //shareP// = //X// + note left: Generate key share //X// Prover ->> Verifier: (//shareP//) Verifier -> Verifier: ""psa_pake_setup()"" with key (//w0//, //L//) or key (//w0//, //w1//)\n""psa_pake_set_role(PSA_PAKE_ROLE_SERVER)""\n""psa_pake_set_user(VerifierId)""\n""psa_pake_set_peer(ProverId)""\n""psa_pake_set_context(Context)"" - note over Verifier: Generate key share //Y// Verifier -> Verifier: ""psa_pake_input()"" for //shareP// - note over Verifier - Validate //shareP// - Compute //K_shared//, //confirmP'// and //confirmV// + note left: Validate //shareP// + Verifier -> Verifier: ""psa_pake_output()"" for //shareV// = //Y// and //confirmV// + note left + Generate key share //Y// + Compute //K_shared//, + //confirmP'// and //confirmV// end note - Verifier -> Verifier: ""psa_pake_output()"" for //shareV// = //Y// and //confirmV// Verifier ->> Prover: (//shareV//, //confirmV//) Prover -> Prover: ""psa_pake_input()"" for //shareV// - - note over Prover - Validate //shareV// - Compute //K_shared//, //confirmP// and //confirmV'// - end note + note left: Validate //shareV// Prover -> Prover: ""psa_pake_output()"" for //confirmP// + note left + Compute //K_shared//, + //confirmP// and //confirmV'// + end note Prover ->> Verifier: (//confirmP//) Prover -> Prover: ""psa_pake_input()"" for //confirmV// - note over Prover: Verify that //confirmV'// = //confirmV// + note left + Verify that + //confirmV'// = //confirmV// + end note Prover -> Prover: ""psa_pake_get_shared_key()"" to extract //K_shared// Verifier -> Verifier: ""psa_pake_input()"" for //confirmP// - note over Verifier: Verify that //confirmP'// = //confirmP// + note left + Verify that + //confirmP'// = //confirmP// + end note Verifier -> Verifier: ""psa_pake_get_shared_key()"" to extract //K_shared// @enduml diff --git a/doc/ext-pake/figure/spake2plus.svg b/doc/ext-pake/figure/spake2plus.svg index f98855a0..ac1bfc49 100644 --- a/doc/ext-pake/figure/spake2plus.svg +++ b/doc/ext-pake/figure/spake2plus.svg @@ -1 +1 @@ -Prover(Client role)Verifier(Server role)Shared information : cipher suite,ProverId,VerifierId, andContextRegistration record (w0,L) derived from passwordProver 'key pair' (w0,w1) derived from passwordpsa_pake_setup()with key (w0,w1)psa_pake_set_role(PSA_PAKE_ROLE_CLIENT)psa_pake_set_user(ProverId)psa_pake_set_peer(VerifierId)psa_pake_set_context(Context)Generate key shareXpsa_pake_output()forshareP=X(shareP)psa_pake_setup()with key (w0,L) or key (w0,w1)psa_pake_set_role(PSA_PAKE_ROLE_SERVER)psa_pake_set_user(VerifierId)psa_pake_set_peer(ProverId)psa_pake_set_context(Context)Generate key shareYpsa_pake_input()forsharePValidatesharePComputeK_shared,confirmP'andconfirmVpsa_pake_output()forshareV=YandconfirmV(shareV,confirmV)psa_pake_input()forshareVValidateshareVComputeK_shared,confirmPandconfirmV'psa_pake_output()forconfirmP(confirmP)psa_pake_input()forconfirmVVerify thatconfirmV'=confirmVpsa_pake_get_shared_key()to extractK_sharedpsa_pake_input()forconfirmPVerify thatconfirmP'=confirmPpsa_pake_get_shared_key()to extractK_shared \ No newline at end of file +Prover(Client role)Verifier(Server role)Shared information : cipher suite,ProverId,VerifierId, andContextRegistration record (w0,L) derived from passwordProver 'key pair' (w0,w1) derived from passwordpsa_pake_setup()with key (w0,w1)psa_pake_set_role(PSA_PAKE_ROLE_CLIENT)psa_pake_set_user(ProverId)psa_pake_set_peer(VerifierId)psa_pake_set_context(Context)psa_pake_output()forshareP=XGenerate key shareX(shareP)psa_pake_setup()with key (w0,L) or key (w0,w1)psa_pake_set_role(PSA_PAKE_ROLE_SERVER)psa_pake_set_user(VerifierId)psa_pake_set_peer(ProverId)psa_pake_set_context(Context)psa_pake_input()forsharePValidatesharePpsa_pake_output()forshareV=YandconfirmVGenerate key shareYComputeK_shared,confirmP'andconfirmV(shareV,confirmV)psa_pake_input()forshareVValidateshareVpsa_pake_output()forconfirmPComputeK_shared,confirmPandconfirmV'(confirmP)psa_pake_input()forconfirmVVerify thatconfirmV'=confirmVpsa_pake_get_shared_key()to extractK_sharedpsa_pake_input()forconfirmPVerify thatconfirmP'=confirmPpsa_pake_get_shared_key()to extractK_shared \ No newline at end of file From 74e44c599dc230fa3f84ad2126ad02aa457d76bb Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 18 Dec 2023 16:59:37 +0000 Subject: [PATCH 11/11] Fixes following review --- doc/ext-pake/api/pake.rst | 34 ++++++++++++++++----------------- doc/ext-pake/overview/intro.rst | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/ext-pake/api/pake.rst b/doc/ext-pake/api/pake.rst index 22520c44..d66bdf4f 100644 --- a/doc/ext-pake/api/pake.rst +++ b/doc/ext-pake/api/pake.rst @@ -96,14 +96,15 @@ A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_ PAKE primitive encoding - The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. - These can be used to set key attributes for keys used in PAKE algorithms. - .. rationale:: An integral type is required for `psa_pake_primitive_t` to enable values of this type to be compile-time-constants. This allows them to be used in ``case`` statements, and used to calculate static buffer sizes with `PSA_PAKE_OUTPUT_SIZE()` and `PSA_PAKE_INPUT_SIZE()`. + The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. + These can be used to set key attributes for keys used in PAKE algorithms. + :secref:`spake2p-registration` provides an example of this usage. + .. typedef:: uint8_t psa_pake_primitive_type_t .. summary:: @@ -189,8 +190,6 @@ A PAKE primitive is required when constructing a PAKE cipher-suite object, `psa_ A PAKE primitive value is used to specify a PAKE operation, as part of a PAKE cipher suite. - The components of a PAKE primitive value can be extracted using the `PSA_PAKE_PRIMITIVE_GET_TYPE()`, `PSA_PAKE_PRIMITIVE_GET_FAMILY()`, and `PSA_PAKE_PRIMITIVE_GET_BITS()`. - .. macro:: PSA_PAKE_PRIMITIVE_GET_TYPE :definition: /* specification-defined value */ @@ -321,7 +320,7 @@ A PAKE cipher suite is required when setting up a PAKE operation in `psa_pake_se Implementations are recommended to define the cipher-suite object as a simple data structure, with fields corresponding to the individual cipher suite attributes. In such an implementation, each function ``psa_pake_cs_set_xxx()`` sets a field and the corresponding function ``psa_pake_cs_get_xxx()`` retrieves the value of the field. - An implementations can report attribute values that are equivalent to the original one, but have a different encoding. + An implementation can report attribute values that are equivalent to the original one, but have a different encoding. For example, an implementation can use a more compact representation for attributes where many bit-patterns are invalid or not supported, and store all values that it does not support as a special marker value. In such an implementation, after setting an invalid value, the corresponding get function returns an invalid value which might not be the one that was originally stored. @@ -478,7 +477,7 @@ Some PAKE algorithms need to know which role each participant is taking in the a For example: * Augmented PAKE algorithms typically have a client and a server participant. -* Some symmetric PAKE algorithms need to assign an order to the participants. +* Some symmetric PAKE algorithms assign an order to the two participants. .. typedef:: uint8_t psa_pake_role_t @@ -1075,7 +1074,7 @@ Multi-part PAKE operations However, there is no guarantee that the peer is the participant it claims to be, and was able to compute the same key. Since the peer is not authenticated, no action should be taken that assumes that the peer is who it claims to be. - For example, do not access restricted files on the peer's behalf until an explicit authentication has succeeded. + For example, do not access restricted resources on the peer's behalf until an explicit authentication has succeeded. .. note:: Some PAKE algorithms do not enable the output of the shared secret until it has been confirmed. @@ -1086,7 +1085,7 @@ Multi-part PAKE operations Following key confirmation, the PAKE algorithm provides a cryptographic guarantee that the peer used the same password and identity inputs, and has computed the identical shared secret key. Since the peer is not authenticated, no action should be taken that assumes that the peer is who it claims to be. - For example, do not access restricted files on the peer's behalf until an explicit authentication has succeeded. + For example, do not access restricted resources on the peer's behalf until an explicit authentication has succeeded. .. note:: Some PAKE algorithms do not include any key-confirmation steps. @@ -1371,7 +1370,7 @@ It must be used as an input to a key derivation operation to produce additional But there is no guarantee that the peer is the participant it claims to be, or that the peer used the same password during the exchange. At this point, authentication is implicit --- material encrypted or authenticated using the computed key can only be decrypted or verified by someone with the same key. - The peer is not authenticated at this point, and no action should be taken by the application which assumes that the peer is authenticated, for example, by accessing restricted files. + The peer is not authenticated at this point, and no action should be taken by the application which assumes that the peer is authenticated, for example, by accessing restricted resources. To make the authentication explicit, there are various methods to confirm that both parties have the same key. See :RFC:`8236#5` for two examples. @@ -1485,8 +1484,6 @@ The following code creates a cipher suite to select the :cite:`MATTER` variant o PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, PSA_ECC_FAMILY_SECP_R1, 256)); -.. todo:: rework this section - .. _spake2p-registration: SPAKE2+ registration @@ -1529,7 +1526,7 @@ It is recommended that the Verifier stores only the public key, because disclosu Details of the computation for the key derivation values are in :RFC:`9383#3.2`. -The following steps demonstrate the derivation of a SPAKE2+ key pair for use with the P-256 Elliptic curve group, using PBKDF2-HMAC-SHA256: +The following steps demonstrate the derivation of a SPAKE2+ key pair using PBKDF2-HMAC-SHA256, for use with a SPAKE2+ cipher suite, ``cipher_suite``. See :secref:`spake2p-cipher-suites` for an example of how to construct the cipher suite object. 1. Allocate and initialize a key derivation object: @@ -1553,14 +1550,17 @@ The following steps demonstrate the derivation of a SPAKE2+ key pair for use wit psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT; -#. Set the key type, size, and policy: +#. Set the key type, size, and policy from the ``cipher_suite`` object: .. code-block:: xref - psa_set_key_type(&att, PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); - psa_set_key_bits(&att, 256); // for P-256 + const psa_pake_primitive_t primitive = psa_pake_cs_get_primitive(&cipher_suite); + + psa_set_key_type(&att, + PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(PSA_PAKE_PRIMITIVE_GET_FAMILY(primitive))); + psa_set_key_bits(&att, PSA_PAKE_PRIMITIVE_GET_BITS(primitive)); psa_set_key_usage_flags(&att, PSA_KEY_USAGE_DERIVE); - psa_set_key_algorithm(&att, PSA_ALG_SPAKE2P); + psa_set_key_algorithm(&att, psa_pake_cs_get_algorithm(&cipher_suite)); #. Derive the key: diff --git a/doc/ext-pake/overview/intro.rst b/doc/ext-pake/overview/intro.rst index 66e438af..cc5adf3d 100644 --- a/doc/ext-pake/overview/intro.rst +++ b/doc/ext-pake/overview/intro.rst @@ -116,7 +116,7 @@ Out of scope ^^^^^^^^^^^^ PAKE protocols that do not fit into any of the above categories are not taken into consideration in the proposed API. -Some schemes like that are: +Such schemes include: .. list-table:: :header-rows: 1