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

ML-DSA private keys from seeds #2157

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

jakemas
Copy link
Contributor

@jakemas jakemas commented Feb 3, 2025

Issues:

Resolves #CryptoAlg-2889

Description of changes:

The industry has decided upon a few conventions to assist with the usability of ML-DSA key sizes, one of which is to decrease the amount of bytes transferred when encoding ML-DSA private keys. As ML-DSA keys can be deterministically generated from a provided 32-byte seed, many standards bodies have selected to use private keys in seed format. For example, the IETF draft that standardizes the use of ML-DSA in X.509. https://datatracker.ietf.org/doc/draft-ietf-lamps-dilithium-certificates/

This provides:

The functionality that provides key generation from seeds for ML-DSA was added in my previous PR #1999. This PR exposes the internal key generation functions within the PQDSA struct, so that a new function named PQDSA_KEY_set_raw_keypair_from_seed can directly call the method.

The function PQDSA_KEY_set_raw_keypair_from_seed simply performs:

  • a check that the provided seed is of the correct length
  • allocation of public/private key buffers
  • key generation from seed using pqdsa_keygen_internal

As such, pqdsa_priv_decode has been modified to call:

  • PQDSA_KEY_set_raw_private_key when the provided length of the key is pqdsa->private_key_len
  • PQDSA_KEY_set_raw_keypair_from_seed when the provided length of the key is pqdsa->keygen_seed_len

We implement this at the EVP layer by modifying EVP_PKEY_pqdsa_new_raw_private_key to:

  • call PQDSA_KEY_set_raw_private_key when the len provided is private_key_len
  • call PQDSA_KEY_set_raw_keypair_from_seed when the len provided is keygen_seed_len

Call-outs:

  • The API EVP_PKEY_pqdsa_new_raw_private_key will now populate both public and private keys when provided a seed. but only private key when provided the raw private key. (We can make behaviour consistant by calling the pk from sk derivation function ml_dsa_XX_pack_pk_from_sk added in Support for ML-DSA public key generation from private key #2142)
  • The API PQDSA_KEY_set_raw_private_key will only set private key
  • The API PQDSA_KEY_set_raw_public_key will only set public key

Testing:

To provide KAT for keys from seeds I have included the Appendix C. Examples from https://datatracker.ietf.org/doc/draft-ietf-lamps-dilithium-certificates/. For each provided ML-DSA parameter set, we generate the example private key provided from seed, and check that the corresponding public key matches that in the specification. These tests are in PQDSAParameterTest namely, ParsePrivateKey.

These examples show how much shorter ML-DSA-87 private keys are in seed format:

 -----BEGIN PRIVATE KEY-----
   MDICAQAwCwYJYIZIAWUDBAMTBCAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRob
   HB0eHw==
   -----END PRIVATE KEY-----

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.

@jakemas jakemas requested a review from a team as a code owner February 3, 2025 20:07
justsmth
justsmth previously approved these changes Feb 3, 2025
@@ -82,14 +82,69 @@ int PQDSA_KEY_set_raw_public_key(PQDSA_KEY *key, CBS *in) {
}

int PQDSA_KEY_set_raw_private_key(PQDSA_KEY *key, CBS *in) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This implementation feels very much like two functions conflated into one.

Could there instead be a separate function for the seed, like PQDSA_KEY_set_raw_seed? Then have the callers (pqdsa_priv_decode and EVP_PKEY_pqdsa_new_raw_private_key?) make the decision about which to call based on the length of the data?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see! Reworked in e506ba1/

@codecov-commenter
Copy link

codecov-commenter commented Feb 3, 2025

Codecov Report

Attention: Patch coverage is 60.29412% with 27 lines in your changes missing coverage. Please review.

Project coverage is 78.94%. Comparing base (6c613fa) to head (550f0ed).

Files with missing lines Patch % Lines
crypto/evp_extra/p_pqdsa_asn1.c 57.14% 9 Missing ⚠️
crypto/evp_extra/p_pqdsa_test.cc 66.66% 6 Missing and 2 partials ⚠️
crypto/pqdsa/pqdsa.c 60.00% 6 Missing ⚠️
crypto/evp_extra/p_pqdsa.c 50.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2157      +/-   ##
==========================================
- Coverage   78.95%   78.94%   -0.01%     
==========================================
  Files         611      611              
  Lines      105551   105603      +52     
  Branches    14950    14957       +7     
==========================================
+ Hits        83341    83372      +31     
- Misses      21558    21575      +17     
- Partials      652      656       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

key->public_key = public_key;
}

if (key->private_key == NULL || key->public_key == NULL ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this additional check would not be needed if key->private_key was checked at line 126 above. (The other code paths all seem to have NULL checks for both values covered.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed: 2abd022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants