Skip to content

Add asymmetric key types for SPAKE2+#119

Merged
athoelke merged 4 commits intoARM-software:mainfrom
athoelke:pake-spake2p-keys
Jan 10, 2024
Merged

Add asymmetric key types for SPAKE2+#119
athoelke merged 4 commits intoARM-software:mainfrom
athoelke:pake-spake2p-keys

Conversation

@athoelke
Copy link
Copy Markdown
Contributor

@athoelke athoelke commented Nov 3, 2023

Define and describe the asymmetric key types for the Prover and Verifier in SPAKE2+, as discussed in #73.

  • Define the key encoding
  • Define the public key format (for import/export)
  • Define the key derivation procedure (this is the 'registration' process)

Open issues (now resolved):

  • Did I get the calculation of the bytes-to-extract for the key derivation right, for the 5 elliptic curves?

  • Should we provide a help API (or APIs) to construct the SPAKE2+ key-pair and public-key attributes from a PAKE cipher-suite, for use during key derivation or key import? See this comment and following.

@athoelke athoelke added enhancement New feature or request API design Related the design of the API Crypto API Issue or PR related to the Cryptography API DO NOT MERGE labels Nov 3, 2023
@athoelke athoelke added this to the Crypto API 1.2 PAKE Extension milestone Nov 3, 2023
@athoelke athoelke self-assigned this Nov 3, 2023
@athoelke athoelke mentioned this pull request Nov 3, 2023
8 tasks
@athoelke
Copy link
Copy Markdown
Contributor Author

athoelke commented Nov 3, 2023

Some feedback would be very welcome from @silabs-Kusumit, @silabs-hannes, @oberon-sk, and @yanesca; given your involvement in the PAKE API development.

@athoelke
Copy link
Copy Markdown
Contributor Author

After further consideration on these open issues, my updated thinking is as follows. I am interested to hear any other ideas an opinions.

  1. Should we parameterize the keys on an ECC-FAMILY? Currently only secp256r1 and Edwards curves are defined in RFC 9383, but the protocol could use other groups with the necessary properties.
    Alternatives would be to define a separate family group for SPAKE2+; or as Draft : SPAKE2PLUS protocol flow proposal #73 suggests, to use the PAKE primitive. The latter might be misleading as the key size is not used to construct the key type, but is needed to set the key attributes.

This seems to make the most sense, and is consistent with the other key APIs.

However, given a PAKE primitive value, there is no API to extract the ECC family or key-bits values. I.e. an application using SPAKE2+ needs to either (a) use the retain/use the ECC-FAMILY and key-bits values to build the PAKE primitive, and to construct the key attributes, or (b) reverse engineer the encoding of the PAKE primitive (e.g. from here) to extract these values. Neither of those follow the pattern/spirit of existing Crypto APIs.

My preferred suggestion is that we provide three macro APIs to extract the components from the PAKE primitive, so that application can construct the key attributes for SPAKE2+ from a PAKE primitive value.

Constructing the key type directly from a PAKE primitive only solves half of the problem, so I would rule this out. An alternative that might address the issue would be a custom key attribute setter for PAKE, or perhaps just SPAKE2+, such as:

void psa_set_pake_key_type(psa_key_attributes_t* att, const psa_pake_cipher_suite* pake_cs);

This can use all of the necessary cipher-suite attributes to determine the correct type and size of the input key for PAKE. Note that for SPAKE2+, this would be the key-pair type, as the cipher-suite does not identify the role.

  1. Should we use the curve size, used to parameterize SPAKE2+, as the key bits value when setting up the key attributes? - This seems natural, even if it does not relate to the actual bit-size of the key material.

This association makes the most sense. In the current API, key_bits is already presented as 'not necessarily the storage size of the key'.

  1. Are the terms 'key pair' and 'public key' acceptable to reuse for SPAKE2+, or should we coin synonyms such as 'prover' and 'verifier' for use with augmented PAKE keys? - This would result in additional/modified macros for use with PAKEs.

I would like to stick with key-pair and public-key terminology/identifier names, and just map these to the algorithm-specific terms.

  1. Do we need a new one (or two) usage flags for asymmetric keys used in PAKE algorithms? - if not, what others should be used.

I think we should stick with just PSA_KEY_USAGE_DERIVE for the SPAKE2+ keys, for both roles. We use this usage flag for both KDF inputs and for key agreement inputs, so it is consistent to continue with that flag for PAKE.

@silabs-Kusumit
Copy link
Copy Markdown

3. Should we use the curve size, used to parameterize SPAKE2+, as the key bits value when setting up the key attributes? - This seems natural, even if it does not relate to the actual bit-size of the key material.

This association makes the most sense. In the current API, key_bits is already presented as 'not necessarily the storage size of the key'.

  1. Are the terms 'key pair' and 'public key' acceptable to reuse for SPAKE2+, or should we coin synonyms such as 'prover' and 'verifier' for use with augmented PAKE keys? - This would result in additional/modified macros for use with PAKEs.

I would like to stick with key-pair and public-key terminology/identifier names, and just map these to the algorithm-specific terms.

  1. Do we need a new one (or two) usage flags for asymmetric keys used in PAKE algorithms? - if not, what others should be used.

I think we should stick with just PSA_KEY_USAGE_DERIVE for the SPAKE2+ keys, for both roles. We use this usage flag for both KDF inputs and for key agreement inputs, so it is consistent to continue with that flag for PAKE.

LGTM

@yanesca
Copy link
Copy Markdown
Contributor

yanesca commented Nov 27, 2023

  1. [...]

This can use all of the necessary cipher-suite attributes to determine the correct type and size of the input key for PAKE. Note that for SPAKE2+, this would be the key-pair type, as the cipher-suite does not identify the role.

I agree that the most convenient way (for both implementation and application) to do this is with a dedicated API that constructs the appropriate attributes. (I mean psa_set_pake_key_type().)

  1. Should we use the curve size, used to parameterize SPAKE2+, as the key bits value when setting up the key attributes? - This seems natural, even if it does not relate to the actual bit-size of the key material.

This association makes the most sense. In the current API, key_bits is already presented as 'not necessarily the storage size of the key'.

The only situation when the application needs to know the size of the key is when exporting and then they need to use the PSA_EXPORT_KEY_OUTPUT_SIZE and not key_bits, so it should be safe to deviate. Also, it looks like we will be using psa_ecc_family_t which needs additional information to fully characterise a curve. We normally use key_bits for that purpose and I can't see any good reason why we should do it differently here. In summary, I too think we should the curve size here.

Are the terms 'key pair' and 'public key' acceptable to reuse for SPAKE2+, or should we coin synonyms such as 'prover' and 'verifier' for use with augmented PAKE keys? - This would result in additional/modified macros for use with PAKEs.

I would like to stick with key-pair and public-key terminology/identifier names, and just map these to the algorithm-specific terms.

My first thought for this was the key-pair and public-key terminology as well. Thinking about it a bit more it doesn't feel very precise and prone to confusion. That said, I can't think of anything better as the standards don't really use any terminology here. The closest thing they mention that these are stored in the registration record, but that doesn't fit very well what we need here. The key-pair and public-key terminology is probably the best we can do and it should work as long as we document the mappings as you suggested.

  1. Do we need a new one (or two) usage flags for asymmetric keys used in PAKE algorithms? - if not, what others should be used.

I think we should stick with just PSA_KEY_USAGE_DERIVE for the SPAKE2+ keys, for both roles. We use this usage flag for both KDF inputs and for key agreement inputs, so it is consistent to continue with that flag for PAKE.

Yep, as you say PSA_KEY_USAGE_DERIVE covers this properly, there is no ambiguity here. In theory we could allow PSA_KEY_USAGE_VERIFY_DERIVATION as well, but I can't think of a meaningful use case.

  1. When describing a data format that is a concatenation of elements: is there a preference for the 'bullet list of elements' approach used in the Weierstrass public key format in §9.6.4 Key formats, or the more technical 'concatenation of elements' depiction using the || operator?

I think the list of elements approach is easier to read, but can be ambiguous. Which is not the end of the world as we usually reference a standard with detailed description.

  1. When detailing a byte-string representation of an integer: is there a preference for the narrative style used in the Weierstrass public key format in §9.6.4 Key formats, the [ v ]n style used in some NIST texts, or the function-based (e.g. I2OSP()) approach used in texts such as SEC1?

Like above, I prefer the narrative style, I can always consult the standard for details if needed.

  1. Semi-mathematical content of the specification is currently an 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 ab = 1, Fq (seen in NIST publications, and some IETF RFCs). It would be good to pick one approach and use it everywhere. However, we also have the ability to use the :math: role, which will
    (used in SECG and some NIST publications).

I think the :math: role is convenient to use and provides the best results.

@athoelke
Copy link
Copy Markdown
Contributor Author

This can use all of the necessary cipher-suite attributes to determine the correct type and size of the input key for PAKE. Note that for SPAKE2+, this would be the key-pair type, as the cipher-suite does not identify the role.

I agree that the most convenient way (for both implementation and application) to do this is with a dedicated API that constructs the appropriate attributes. (I mean psa_set_pake_key_type().)

If this approach is agreeable, then is there any reason to limit this to setting the key type and size? The key policy can be uniquely determined from the cipher suite: a matching permitted algorithm and the PSA_KEY_USAGE_DERIVE flag. So we instead define:

void psa_set_pake_key_attributes(psa_key_attributes_t * attributes, const psa_pake_cipher_suite_t * cipher_suite);

Its implementation could be something along the lines of:

void psa_set_pake_key_attributes(psa_key_attributes_t * attributes, const psa_pake_cipher_suite_t * cipher_suite) {
    const psa_algorithm_t alg = psa_pake_cs_get_algorithm(cipher_suite);
    const psa_pake_primitive_t primitive = psa_pake_cs_get_primitive(cipher_suite);
    if (PSA_ALG_IS_SPAKE2P(alg)) {
        psa_set_key_type(attributes,
                         PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(PSA_PAKE_PRIMITIVE_GET_FAMILY(primitive)));
        psa_set_key_bits(attributes, PSA_PAKE_PRIMITIVE_GET_BITS(primitive);
        psa_set_key_algorithm(attributes, alg);
        psa_set_key_usage_flags(attributes, PSA_KEY_USAGE_DERIVE);
    }
}

This would be used by applications during SPAKE2+ when constructing the SPAKE2+ 'keys' from the password:

...
psa_key_id_t spake_key;
psa_key_attributes_t att = PSA_KEY_ATTRIBUTES_INIT;
psa_set_pake_key_attributes(&att, &cipher_suite);
psa_key_derivation_output_key(&pbkdf, &att, &spake_key);
psa_key_derivation_abort(&pbkdf);

psa_pake_operation_t pake = PSA_PAKE_OPERATION_INIT;
psa_pake_setup(&pake, &cipher_suite, spake_key);

@athoelke
Copy link
Copy Markdown
Contributor Author

So we instead define:

void psa_set_pake_key_attributes(psa_key_attributes_t * attributes, const psa_pake_cipher_suite_t * cipher_suite);

I've realised that this is only helpful for the derivation of the key pair. A parallel solution is needed when importing a SPAKE2+ public key...

@athoelke
Copy link
Copy Markdown
Contributor Author

Rebased this PR, and followed up on the feedback. Resolved most outstanding issues.

* Add key encodings
* Define public key format
* Define key derivation procedure
* Add macros to crack a PAKE primitive value
* Decide on using USAGE_DERIVE for SPAKE2+ keys
* Fix typos
* Update header file
* 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
@athoelke
Copy link
Copy Markdown
Contributor Author

[Rebased on top of merged SPAKE2+ PRs]

@yanesca
Copy link
Copy Markdown
Contributor

yanesca commented Dec 4, 2023

I've realised that this is only helpful for the derivation of the key pair. A parallel solution is needed when importing a SPAKE2+ public key...

I can see that you went with the getter macros, which makes sense: adding 3 simple getter macros is probably better than adding 2 potentially more complicated API functions.

@athoelke athoelke changed the title DRAFT: Add asymmetric key types for SPAKE2+ Add asymmetric key types for SPAKE2+ Dec 12, 2023
@athoelke
Copy link
Copy Markdown
Contributor Author

Open issues resolved:

  • Key derivation extraction sizes confirmed here
  • No concerns raised over the approach to initializing key attributes from a pake primitive

@athoelke athoelke merged commit 237ece2 into ARM-software:main Jan 10, 2024
@athoelke athoelke deleted the pake-spake2p-keys branch January 10, 2024 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API design Related the design of the API Crypto API Issue or PR related to the Cryptography API enhancement New feature or request

Projects

Development

Successfully merging this pull request may close these issues.

3 participants