Skip to content

Conversation

oberon-sk
Copy link

The PSA Certified Crypto API v1.2 PAKE Extension requires some additional definitions to support SRP-6 as defined by RFC 2945 and RFC 5054 to be embedded in, or included by, psa/crypto.h.

This PR provides a proposal.

Signed-off-by: Oberon microsystems

@athoelke athoelke marked this pull request as draft March 4, 2024 14:49
@athoelke athoelke added enhancement New feature or request Crypto API Issue or PR related to the Cryptography API proposal An RFC, or proposal for discussion labels Mar 4, 2024
@athoelke athoelke added this to the Crypto API 1.x milestone Mar 4, 2024
Copy link
Contributor

@athoelke athoelke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the contribution!

Some further clarification of the definition of the protocol, as the public documentation has various definitions for some of the computational steps. This is necessary to ensure compatible implementations of algorithms in the Crypto API.

If we need additional flexibility for different uses of SRP-6, then we will need additional algorithm identifiers, or other parameterisation of the PAKE cipher-suite.

#define PSA_KEY_TYPE_SRP_PUBLIC_KEY_BASE ((psa_key_type_t) 0x4700)
#define PSA_KEY_TYPE_SRP_GROUP_MASK ((psa_key_type_t) 0x00ff)

/** SRP key pair. Both the client and server key.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition of these SRP-6 keys is technically equivalent to FFDH keys, in terms of key content and calculation of the public key from the private key.

However, there may be a good reason for maintaining a separate key type definition for SRP-6 keys instead of reusing the existing PSA_KEY_TYPE_DH_KEY_PAIR(): The derivation of an SRP-6 key is different to the default method for FFDH keys in the Crypto API.

  • An FFDH key-pair is derived by repeated sampling of the KDF output to find a value in the required range [1, N-1].
  • A SRP-6 key-pair is constructed by zero-padding the fixed-size output of the password-hash. The original SRP hash outputs only 160 bits (for SHA1).

Note to self: this may be worth a rationale comment when defined in the specification.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some descriptions of SRP-6 show the client private key being the output of a simple hash - so we need to ensure the correct construction of an SRP-6 key from an imported data buffer (the hash output), as well as construction via a KDF (such as the original SRP specification double-hash algorithm).

For a general KDF, how does psa_key_derivation_output_key() know how much data to extract when deriving the SRP-6 key? The KDF presented in the SRP definition only outputs the hash output size. What about KDFs that do not have limited output? - we can either define the behavior explicitly, or recommend the application extract the required number of KDF bytes, and then import the SRP key?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a general problem for the derivation of structured keys. The PSA core has to ‘know’ somehow how much data the driver needs to construct the key. The “psa-driver-interface.md” document mentions this under “Open questions around cooked key derivation”. The idea of an additional entry point to determine the expected length mentioned there is possibly the best solution.
For trial-and-error type key derivation like FFDH the driver simply returns PSA_ERROR_INSUFFICIENT_DATA to signal the core to call it again with the next chunk of data.

* The corresponding public key (password verifier) can be exported using
* psa_export_public_key(). See also #PSA_KEY_TYPE_SRP_PUBLIC_KEY().
*
* \param group A value of type ::psa_dh_family_t that identifies the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there specific DH groups that could/should be used here?

  • Crypto API currently just defines the RFC 7919 family
  • This header defines another RFC 3526 family
  • The SRP-for-TLS reference (RFC 5054) defines another set of FFDH groups

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RFC 5054 might be a better choice.

* These definitions must be embedded in, or included by, psa/crypto.h
*/

#define PSA_KEY_TYPE_SRP_KEY_PAIR_BASE ((psa_key_type_t) 0x7700)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This value works well with the current set of DH key values: it has the same parity as the PSA_KEY_TYPE_DH_KEY_PAIR() base value, so existing DH family values can be used.


/** The Secure Remote Passwort key exchange (SRP) algorithm.
*
* This is SRP-6 as defined by RFC 2945 and RFC 5054, instantiated with the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this SRP-6 or SRP-6a? the latter calculates the multiplier value k using Hash(N || PAD(g)), instead of using the fixed value 3. This distinction is not always correctly defined in references to this algorithm.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is SRP-6a as defined by RFC 5054 including errata (k = Hash(N || PAD(g)).

* psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT;
* psa_pake_cs_set_algorithm(cipher_suite, PSA_ALG_SRP_6(hash));
* psa_pake_cs_set_primitive(&cipher_suite,
* PSA_PAKE_PRIMITIVE(type, family, bits));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameters here could be more fully specified - something to fix in the specification itself.

* // send M1
* psa_pake_input(operation, #PSA_PAKE_STEP_CONFIRM, ...);
* // receive M2
* psa_pake_output(operation, #PSA_PAKE_STEP_CONFIRM, ...);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition of M2 needs to be clarified. I can find more than 3 different formulations across different SRP-6 documentation.

The definition of M1 and M2 may result in not requiring some of the inputs to the operation (client identity and salt).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variant where M1 = H(H(N) XOR H(g) | H(U) | s | A | B | K) and M2 = H(A | M1 | K) seems to be the clear standard.
It is defined or used by:

  • RFC2945
  • RFC5054 (by referring to RFC2945)
  • srp.stanford.edu/design (for SRP-3, SRP-6, and SRP-6a)
  • The Stanford reference implementation
  • The reference implementation on Wikipedia
  • The SRP test vectors on github.com/secure-remote-password/test-vectors
  • HomeKit (by referring to the Stanford reference implementation)
  • The Wolfcrypt library

(see also the comment for the server proof phase call)

* For the server proof phase call the following functions in this order:
* \code
* // receive M1
* psa_pake_output(operation, #PSA_PAKE_STEP_CONFIRM, ...);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition of M1 needs to be clarified. I can find different formulations across different SRP-6 documentation.

The definition of M1 and M2 may result in not requiring some of the inputs to the operation (client identity and salt).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variant where M1 = H(H(N) XOR H(g) | H(U) | s | A | B | K) and M2 = H(A | M1 | K) seems to be the clear standard.
It is defined or used by:

  • RFC2945
  • RFC5054 (by referring to RFC2945)
  • srp.stanford.edu/design (for SRP-3, SRP-6, and SRP-6a)
  • The Stanford reference implementation
  • The reference implementation on Wikipedia
  • The SRP test vectors on github.com/secure-remote-password/test-vectors
  • HomeKit (by referring to the Stanford reference implementation)
  • The Wolfcrypt library

(see also the comment for the client proof phase call)

* // receive M2
* psa_pake_output(operation, #PSA_PAKE_STEP_CONFIRM, ...);
* // Get secret
* psa_pake_get_shared_key()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formulation of the shared key needs to be clarified.

  • In RFC 2945, it uses an interleaved SHA1 to construct an unbiased pseudo-random 320-bit value from the pre-master secret (biased output of a function on the DH group).
  • In RFC 5054, the description stops at the pre-mater secret (S), a biased output of a function on the DH group.
  • Elsewhere it just uses Hash(S), for some value of 'Hash'.

Any of these might be appropriate...

* - #PSA_KEY_DERIVATION_INPUT_INFO is the user id.
* - #PSA_KEY_DERIVATION_INPUT_PASSWORD is the password.
* - #PSA_KEY_DERIVATION_INPUT_SALT is the salt.
* The output has to be read as a key of type PSA_KEY_TYPE_SRP_KEY_PAIR.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume this means that the intended use is to call psa_key_derivation_output_key() requesting a key of that type, with a key size that matches the bit-size of the DH group used for the primitive in the PAKE cipher-suite?

*
* The size of a SRP key is the size associated with the Diffie-Hellman
* group. See the documentation of each Diffie-Hellman group for details.
* To construct a SRP key pair, the password hash must be imported.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later, the documentation talks about constructing an SRP-6 key as output from a password-hash type of KDF, rather than by using psa_import_key() on some data.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, it should be:

"To construct an SRP key pair, it must either be output from a key derivation operation, or imported."

(similar to SPAKE2+)

@athoelke
Copy link
Contributor

athoelke commented Sep 4, 2025

I am dusting off my 75%-complete PR for adding this to the specification, and have a small number of things to resolve:

  1. Key formats. The Password token key is expected to be stored and used - there is no WPA3-related use case to export or import these items. (This key is constructed via derivation from the shared password, for later use in the PAKE operation). I plan to define the import/export format as implementation-specific.

    We could instead make export/import invalid, or define the format (as being the same as the public key of the cyclic group (ECC or FFDH) for which the password token is computed.

  2. Key exchange (commit) phase inputs and outputs. WPA3-SAE exchanges a scalar (integer modulo group prime) and a group element during the first part of the exchange. These are packaged in separate fields within the commit messages.

    To align with J-PAKE, the commit-scalar and COMMIT-ELEMENT values should require two calls to the pake output and input functions. The scalar would need a new step type value, e.g. PSA_PAKE_STEP_COMMIT_VALUE, but the ELEMENT could reuse the existing PSA_PAKE_STEP_KEY_SHARE, with a small caveat.

    The group element in the WPA3-SAE commit message is formatted very similarly to the cyclic group public key format. For FFDH it is the same. For ECC the element format is the concatenation of the x and y coordinates encoded as non-truncated big-endian integers, which is the same as the uncompressed Weierstrass public key in SEC1, but without the `0x04` prefix octet. The documentation for PSA_PAKE_STEP_KEY_SHARE currently says that the input/output format is the same as the public key format. We could leave as is (and force applications to add/remove the `0x04` prefix); make the format of this step dependent on the PAKE algorithm (and remove the prefix for WPA3-SAE); or add a new PAKE step for the WPA3-SAE COMMIT-ELEMENT.

    An entirely different approach would be to encode both commit-scalar and COMMIT-ELEMENT in a single input/output step, and have the application split these on output, or concatenate on input when processing the WPA3-SAE 802.11 frames.

  3. Ordering: it seems that the commitment outputs and inputs could occur in any order (e.g. the peer values can be input before extracting the commitment outputs); likewise for the confirmation values (although the counter has to be set prior to output of the confirmation value); the salt could in theory be input at any time before the confirmation phase starts; and the key id can be output at any time after the commitment phase is complete. However, requiring flexibility might be costly for an implementation, but demanding a rigid ordering may also add costs for an application and unnecessary validation overheads in an implementation. Is there a sweet spot?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Crypto API Issue or PR related to the Cryptography API enhancement New feature or request proposal An RFC, or proposal for discussion
Projects
Development

Successfully merging this pull request may close these issues.

2 participants