Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add definitions required by SRP-6. #178

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions headers/crypto/1.2/psa/crypto-srp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/* This file contains reference definitions for implementations of
* SRP-6 in addition to the PSA Certified Crypto API v1.2 PAKE Extension
*
* 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.

#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 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+)

* 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.

* Diffie-Hellman group to be used.
*/
#define PSA_KEY_TYPE_SRP_KEY_PAIR(group) \
((psa_key_type_t) (PSA_KEY_TYPE_SRP_KEY_PAIR_BASE | (group)))

/** SRP public key. The server key (password verifier).
*
* The size of an SRP public key is the same as the corresponding private
* key. See #PSA_KEY_TYPE_SRP_KEY_PAIR() and the documentation of each
* Diffie-Hellman group for details.
* To construct a SRP public key, it must be imported. The key size
* in attributes must not be zero.
*
* \param group A value of type ::psa_dh_family_t that identifies the
* Diffie-Hellman group to be used.
*/
#define PSA_KEY_TYPE_SRP_PUBLIC_KEY(group) \
((psa_key_type_t) (PSA_KEY_TYPE_SRP_PUBLIC_KEY_BASE | (group)))

/** Whether a key type is a SRP key (pair or public-only). */
#define PSA_KEY_TYPE_IS_SRP(type) \
((PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type) & \
~PSA_KEY_TYPE_SRP_GROUP_MASK) == \
PSA_KEY_TYPE_SRP_PUBLIC_KEY_BASE)
/** Whether a key type is a SRP key pair. */
#define PSA_KEY_TYPE_IS_SRP_KEY_PAIR(type) \
(((type) & ~PSA_KEY_TYPE_SRP_GROUP_MASK) == \
PSA_KEY_TYPE_SRP_KEY_PAIR_BASE)
/** Whether a key type is a SRP public key. */
#define PSA_KEY_TYPE_IS_SRP_PUBLIC_KEY(type) \
(((type) & ~PSA_KEY_TYPE_SRP_GROUP_MASK) == \
PSA_KEY_TYPE_SRP_PUBLIC_KEY_BASE)
/** Extract the curve from a SRP key type. */
#define PSA_KEY_TYPE_SRP_GET_FAMILY(type) \
((psa_ecc_family_t) (PSA_KEY_TYPE_IS_SRP(type) ? \
((type) & PSA_KEY_TYPE_SRP_GROUP_MASK) : \
0))

/** 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)).

* following parameters:
*
* - The group is defined over a finite field using a secure prime.
* - A cryptographic hash function.
*
* To select these parameters and set up the cipher suite, call these functions:
*
* \code
* 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.

* \endcode
*
* After initializing a SRP operation, call:
*
* \code
* psa_pake_setup(operation, password, cipher_suite);
* psa_pake_set_role(operation, ...);
* psa_pake_set_user(operation, ...);
Copy link
Contributor

Choose a reason for hiding this comment

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

psa_pake_set_user() sets the local participant identity. So for the server role, psa_pake_set_peer() would need to be called. However, unless it is used in the key confirmation computation (see other comments), the pake operation does not require this value.

There is no server identity in this PAKE, setting a value should probably return an error?

Copy link
Author

Choose a reason for hiding this comment

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

We would suggest that both roles call psa_pake_set_user (psa_pake_set_peer is not required).

* \endcode
*
* The password provided to the client side must be of type
* #PSA_KEY_TYPE_SRP_KEY_PAIR.
* The password provided to the server side must be of type
* #PSA_KEY_TYPE_SRP_PUBLIC_KEY.
Copy link
Contributor

Choose a reason for hiding this comment

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

In SPAKE2+ we permitted the use of the key-pair here as well. Should we permit the same here, or was that a mistake?

Copy link
Author

Choose a reason for hiding this comment

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

Agreed.

*
* The role set by \c psa_pake_set_role() must be either
* \c PSA_PAKE_ROLE_CLIENT or \c PSA_PAKE_ROLE_SERVER.
*
* For the SRP client key exchange call the following functions in any order:
Copy link
Contributor

Choose a reason for hiding this comment

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

We were rather more strict about the ordering in the SPAKE2+ definition. Is the flexibility here important? - e.g. to support different protocol flows regarding transmission of User Id and receipt of salt from the server?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, flexibility is important for that reason.

* \code
* // get salt
* psa_pake_input(operation, #PSA_PAKE_STEP_SALT, ...);
Copy link
Contributor

Choose a reason for hiding this comment

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

The Client only requires the salt for password hashing (done outside the PAKE operation, to prepare the SRP-6 key-pair), and for some definitions of the key confirmation values. i.e. this might be unnecessary depending on the definition of the key confirmation values (see other comments).

* // get server key
* psa_pake_input(operation, #PSA_PAKE_STEP_KEY_SHARE, ...);
* // write client key
* psa_pake_output(operation, #PSA_PAKE_STEP_KEY_SHARE, ...);
* \endcode
*
* For the SRP server key exchange call the following functions in any order:
* \code
* // get salt
* psa_pake_input(operation, #PSA_PAKE_STEP_SALT, ...);
Copy link
Contributor

Choose a reason for hiding this comment

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

The Server only requires the salt for some definitions of the key confirmation values. i.e. this might be unnecessary depending on the definition of the key confirmation values (see other comments).

* // get client key
* psa_pake_input(operation, #PSA_PAKE_STEP_KEY_SHARE, ...);
* // write server key
* psa_pake_output(operation, #PSA_PAKE_STEP_KEY_SHARE, ...);
* \endcode
*
* For the client proof phase call the following functions in this order:
* \code
* // 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)

* // 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...

* \endcode
*
* 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)

* // send M2
* psa_pake_input(operation, #PSA_PAKE_STEP_CONFIRM, ...);
* // Get secret
* psa_pake_get_shared_key()
* \endcode
*
* The shared secret that is produced by SRP 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.
*/
#define PSA_ALG_SRP_6_BASE ((psa_algorithm_t) 0x0a000300)
#define PSA_ALG_SRP_6(hash_alg) (PSA_ALG_SRP_6_BASE | ((hash_alg) & PSA_ALG_HASH_MASK))
#define PSA_ALG_IS_SRP_6(alg) (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_SRP_6_BASE)

/** The salt.
*
* The format for both input and output at this step is plain binary data.
*/
#define PSA_PAKE_STEP_SALT ((psa_pake_step_t)0x05)

/** Diffie-Hellman groups defined in RFC 3526.
*
* This family includes groups with the following key sizes (in bits):
* 1536, 2048, 3072, 4096, 6144, 8192. A given implementation may support
* all of these sizes or only a subset.
*/
#define PSA_DH_FAMILY_RFC3526 ((psa_dh_family_t) 0x05)

#define PSA_ALG_SRP_PASSWORD_HASH_BASE ((psa_algorithm_t) 0x08800300)
/** The SRP password to password-hash KDF.
* It takes the password p, the salt s, and the user id u.
* It calculates the password hash h as
* h = H(salt || H(u || ":" || p)
* where H is the given hash algorithm.
*
* This key derivation algorithm uses the following inputs, which must be
* provided in the following order:
* - #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?

*/
#define PSA_ALG_SRP_PASSWORD_HASH(hash_alg) \
(PSA_ALG_SRP_PASSWORD_HASH_BASE | ((hash_alg) & PSA_ALG_HASH_MASK))

/** Whether the specified algorithm is a key derivation algorithm constructed
* using #PSA_ALG_SRP_PASSWORD_HASH(\p hash_alg).
*
* \param alg An algorithm identifier (value of type #psa_algorithm_t).
*
* \return 1 if \p alg is a key derivation algorithm constructed using #PSA_ALG_SRP_PASSWORD_HASH(),
* 0 otherwise. This macro may return either 0 or 1 if \c alg is not a supported
* key derivation algorithm identifier.
*/
#define PSA_ALG_IS_SRP_PASSWORD_HASH(alg) \
(((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_SRP_PASSWORD_HASH_BASE)