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 internal APIs for ML-DSA #1999

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

jakemas
Copy link
Contributor

@jakemas jakemas commented Nov 18, 2024

Issues:

Resolves #CryptoAlg-2723

Description of changes:

This PR adds the internal functions from FIPS 204: Module-Lattice-Based Digital Signature Standard. We base this implementation on the upstream reference implementation of ML-DSA pq-crystals/dilithium@444cdcc. However, the upstream commit only includes implementation of ML-DSA.Sign_internal and ML-DSA.Verify_internal, so we also include ML-DSA.KeyGen_internal to complete the implementation.

Changes:

Call-outs:

We can remove the testing mechanism for the KATs pq_custom_randombytes as we now support KATs that use the internal functions that provide randomness via an input seed.

Testing:

The KATs have been migrated to use the internal functions.

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 November 18, 2024 20:07
@codecov-commenter
Copy link

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 78.90%. Comparing base (ab8953b) to head (33dee33).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1999      +/-   ##
==========================================
+ Coverage   78.89%   78.90%   +0.01%     
==========================================
  Files         595      594       -1     
  Lines      102451   102434      -17     
  Branches    14527    14524       -3     
==========================================
- Hits        80827    80825       -2     
+ Misses      20976    20958      -18     
- Partials      648      651       +3     

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


🚨 Try these New Features:

uint8_t *private_key);
uint8_t *secret_key);

int ml_dsa_65_keypair_internal(uint8_t *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.

Why internal? We used deterministic for ML-KEM.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We used deterministic for ML-KEM as we wrote these functions before the final release of the standard came out, and we were working from the IPD and naming conventions from the PQC mailing list. Since the release of FIPS 203, 204 final, we now see these deterministic methods described as internal, so to make the implementation of the standard clearer I named them internal and added descriptors to the standard itself, e.g.:

/*************************************************
 * Name:        crypto_sign_keypair_internal
 *
 * Description: FIPS 204: Algorithm 6 ML-DSA.KeyGen_internal.
 *              Generates public and private key. Internal API.
 ...


To refer to:
Screenshot 2024-11-18 at 4 56 52 PM

- `ntt.c`, `poly.c`, `reduce.c`, `reduce.h`: have been modified with a code refactor. The function `fqmul` has been added to bring mode code consistency with Kyber/ML-KEM. See https://github.com/aws/aws-lc/pull/1748 for more details on this change.
- `reduce.c`: a small fix to documentation has been made on the bounds of `reduce32`.
- `poly.c`: a small fix to documentation has been made on the bounds of `poly_reduce`.
- `polyvec.c`: a small fix to documentation has been made on the bounds of `polyveck_reduce`.

**Testing**

The KATs were obtained from https://github.com/pq-crystals/dilithium/tree/master/ref/nistkat.
To compile the KAT programs on Linux or macOS, go to the `ref/` directory and run `make nistkat`. This will produce executables within `nistkat` which once executed will produce the KATs: `PQCsignKAT_Dilithium2.rsp`, `PQCsignKAT_Dilithium3.rsp`,`PQCsignKAT_Dilithium5.rsp`.
The KATs were obtained from https://github.com/post-quantum-cryptography/KAT.
Copy link
Contributor

Choose a reason for hiding this comment

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

I know why, but please explain why our file MLDSA_65_hedged_pure.txt doesn't match https://github.com/post-quantum-cryptography/KAT/blob/main/MLDSA/kat_MLDSA_65_hedged_pure.rsp

uint8_t seedbuf[2*SEEDBYTES + CRHBYTES];
uint8_t tr[TRBYTES];
const uint8_t *rho, *rhoprime, *key;
polyvecl mat[DILITHIUM_K_MAX];
polyvecl s1, s1hat;
polyveck s2, t1, t0;

/* Get randomness for rho, rhoprime and key */
pq_custom_randombytes(seedbuf, SEEDBYTES);
OPENSSL_memcpy(seedbuf, seed, SEEDBYTES);
Copy link
Contributor

Choose a reason for hiding this comment

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

At first I thought this looked weird and was wondering why not just use seed directly, but I see they put more stuff into seedbuf down below. Seems a bit weird and a fragile construction.

Comment on lines 39 to 40
seedbuf[SEEDBYTES+0] = params->k;
seedbuf[SEEDBYTES+1] = params->l;
Copy link
Contributor

Choose a reason for hiding this comment

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

How does this work? seedbuf is an array of uint8_t, so each index in the array can only hold 8 bits and k/l are both size_t which could be 64 bits each.

int crypto_sign_keypair(ml_dsa_params *params, uint8_t *pk, uint8_t *sk) {
uint8_t seed[SEEDBYTES];
/* Get randomness for rho, rhoprime and key */
RAND_bytes(seed, SEEDBYTES);
Copy link
Contributor

Choose a reason for hiding this comment

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

RAND_bytes can never fail, but you should still check the return code so static analysis tools don't complain about not checking the return value.

Comment on lines +260 to +266
#ifdef DILITHIUM_RANDOMIZED_SIGNING
if (!RAND_bytes(rnd, RNDBYTES))
return -1;
#else
for(i=0;i<RNDBYTES;i++)
rnd[i] = 0;
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this a compile time option and not two normal APIs for sign and sign_deterministic?

Comment on lines +428 to +429
if(ctxlen > 255)
return -1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Here and in other spots in this PR please update the code to always add the brackets even for single line statements.

Suggested change
if(ctxlen > 255)
return -1;
if(ctxlen > 255) {
return -1;
}

@@ -19,7 +18,6 @@

#include "../test/file_test.h"
#include "../test/test_util.h"
#include "../rand_extra/pq_custom_randombytes.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉

Comment on lines +411 to +413
for(i = 0; i < ctxstr.size(); i++) {
m_prime[2 + i] = ctxstr[i];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this just use memcpy to be simpler and maybe faster?

Comment on lines +257 to +258
for(i = 0; i < ctxlen; i++)
pre[2 + i] = ctx[i];
Copy link
Contributor

Choose a reason for hiding this comment

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

memcpy here too? Either way needs {}

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