Skip to content

Commit

Permalink
added internal APIs for ml-dsa
Browse files Browse the repository at this point in the history
  • Loading branch information
jakemas committed Nov 18, 2024
1 parent ab8953b commit 33dee33
Show file tree
Hide file tree
Showing 13 changed files with 1,779 additions and 1,434 deletions.
1 change: 0 additions & 1 deletion crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,6 @@ add_library(
rand_extra/forkunsafe.c
rand_extra/fuchsia.c
rand_extra/rand_extra.c
rand_extra/pq_custom_randombytes.c
rand_extra/trusty.c
rand_extra/windows.c
rc4/rc4.c
Expand Down
1,199 changes: 1,199 additions & 0 deletions crypto/dilithium/kat/MLDSA_65_hedged_pure.txt

Large diffs are not rendered by default.

902 changes: 0 additions & 902 deletions crypto/dilithium/kat/mldsa65.txt

This file was deleted.

35 changes: 35 additions & 0 deletions crypto/dilithium/ml_dsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ int ml_dsa_65_keypair(uint8_t *public_key /* OUT */,
return (crypto_sign_keypair(&params, public_key, private_key) == 0);
}

int ml_dsa_65_keypair_internal(uint8_t *public_key /* OUT */,
uint8_t *private_key /* OUT */,
const uint8_t *seed /* IN */) {
ml_dsa_params params;
ml_dsa_65_params_init(&params);
return crypto_sign_keypair_internal(&params, public_key, private_key, seed) == 0;
}

int ml_dsa_65_sign(const uint8_t *private_key /* IN */,
uint8_t *sig /* OUT */,
size_t *sig_len /* OUT */,
Expand All @@ -45,6 +53,20 @@ int ml_dsa_65_sign(const uint8_t *private_key /* IN */,
ctx_string, ctx_string_len, private_key) == 0;
}

int ml_dsa_65_sign_internal(const uint8_t *private_key /* IN */,
uint8_t *sig /* OUT */,
size_t *sig_len /* OUT */,
const uint8_t *message /* IN */,
size_t message_len /* IN */,
const uint8_t *pre /* IN */,
size_t pre_len /* IN */,
uint8_t *rnd /* IN */) {
ml_dsa_params params;
ml_dsa_65_params_init(&params);
return crypto_sign_signature_internal(&params, sig, sig_len, message, message_len,
pre, pre_len, rnd, private_key) == 0;
}

int ml_dsa_65_verify(const uint8_t *public_key /* IN */,
const uint8_t *sig /* IN */,
size_t sig_len /* IN */,
Expand All @@ -57,3 +79,16 @@ int ml_dsa_65_verify(const uint8_t *public_key /* IN */,
return crypto_sign_verify(&params, sig, sig_len, message, message_len,
ctx_string, ctx_string_len, public_key) == 0;
}

int ml_dsa_65_verify_internal(const uint8_t *public_key /* IN */,
const uint8_t *sig /* IN */,
size_t sig_len /* IN */,
const uint8_t *message /* IN */,
size_t message_len /* IN */,
const uint8_t *pre /* IN */,
size_t pre_len /* IN */) {
ml_dsa_params params;
ml_dsa_65_params_init(&params);
return crypto_sign_verify_internal(&params, sig, sig_len, message, message_len,
pre, pre_len, public_key) == 0;
}
43 changes: 30 additions & 13 deletions crypto/dilithium/ml_dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,39 @@
#define MLDSA65_KEYGEN_SEED_BYTES 32
#define MLDSA65_SIGNATURE_SEED_BYTES 32

#if defined(__cplusplus)
extern "C" {
#endif

int ml_dsa_65_keypair(uint8_t *public_key,
uint8_t *private_key);
uint8_t *secret_key);

int ml_dsa_65_keypair_internal(uint8_t *public_key,
uint8_t *private_key,
const uint8_t *seed);

int ml_dsa_65_sign(const uint8_t *private_key,
uint8_t *sig,
size_t *sig_len,
const uint8_t *message,
size_t message_len,
const uint8_t *ctx_string,
size_t ctx_string_len);
uint8_t *sig, size_t *sig_len,
const uint8_t *message, size_t message_len,
const uint8_t *ctx_string, size_t ctx_len);

int ml_dsa_65_sign_internal(const uint8_t *private_key,
uint8_t *sig, size_t *sig_len,
const uint8_t *message, size_t message_len,
const uint8_t *pre, size_t pre_len,
uint8_t *rnd);

int ml_dsa_65_verify(const uint8_t *public_key,
const uint8_t *sig,
size_t sig_len,
const uint8_t *message,
size_t message_len,
const uint8_t *ctx_string,
size_t ctx_string_len);
const uint8_t *sig, size_t sig_len,
const uint8_t *message, size_t message_len,
const uint8_t *ctx_string, size_t ctx_string_len);

int ml_dsa_65_verify_internal(const uint8_t *public_key,
const uint8_t *sig, size_t sig_len,
const uint8_t *message, size_t message_len,
const uint8_t *pre, size_t pre_len);
#if defined(__cplusplus)
}
#endif

#endif
86 changes: 39 additions & 47 deletions crypto/dilithium/p_pqdsa_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "../test/test_util.h"

#include <vector>
#include "../crypto/evp_extra/internal.h"
#include "../fipsmodule/evp/internal.h"
#include "../internal.h"
#include "internal.h"
Expand All @@ -19,7 +18,6 @@

#include "../test/file_test.h"
#include "../test/test_util.h"
#include "../rand_extra/pq_custom_randombytes.h"
#include "ml_dsa.h"

// mldsa65kPublicKey is an example ML-DSA-65 public key
Expand Down Expand Up @@ -357,7 +355,7 @@ CMP_VEC_AND_PTR(vec, pkey->pkey.pqdsa_key->public_key, len)
CMP_VEC_AND_PTR(vec, pkey->pkey.pqdsa_key->private_key, len)

static const struct PQDSATestVector parameterSet[] = {
{"MLDSA65", NID_MLDSA65, 1952, 4032, 3309, "dilithium/kat/mldsa65.txt", mldsa65kPublicKey, mldsa65kPublicKeySPKI, 1974},
{"MLDSA65", NID_MLDSA65, 1952, 4032, 3309, "dilithium/kat/MLDSA_65_hedged_pure.txt", mldsa65kPublicKey, mldsa65kPublicKeySPKI, 1974},
};

class PQDSAParameterTest : public testing::TestWithParam<PQDSATestVector> {};
Expand All @@ -373,65 +371,59 @@ TEST_P(PQDSAParameterTest, KAT) {

FileTestGTest(kat_filepath.c_str(), [&](FileTest *t) {
std::string count, mlen, smlen;
std::vector<uint8_t> seed, msg, pk, sk, sm;
std::vector<uint8_t> xi, rng, seed, msg, pk, sk, sm, ctxstr;

ASSERT_TRUE(t->GetAttribute(&count, "count"));
ASSERT_TRUE(t->GetBytes(&xi, "xi"));
ASSERT_TRUE(t->GetBytes(&rng, "rng"));
ASSERT_TRUE(t->GetBytes(&seed, "seed"));
ASSERT_TRUE(t->GetAttribute(&mlen, "mlen"));
ASSERT_TRUE(t->GetBytes(&msg, "msg"));
ASSERT_TRUE(t->GetBytes(&pk, "pk"));
ASSERT_TRUE(t->GetBytes(&sk, "sk"));
ASSERT_TRUE(t->GetAttribute(&smlen, "smlen"));
ASSERT_TRUE(t->GetBytes(&msg, "msg"));
ASSERT_TRUE(t->GetAttribute(&mlen, "mlen"));
ASSERT_TRUE(t->GetBytes(&sm, "sm"));
ASSERT_TRUE(t->GetAttribute(&smlen, "smlen"));
ASSERT_TRUE(t->GetBytes(&ctxstr, "ctx"));

size_t pk_len = GetParam().public_key_len;
size_t sk_len = GetParam().private_key_len;
size_t sig_len = GetParam().signature_len;

// The KAT files generated by the dilithium team use the optional APIs that
// create a signature for a message m and append the message to the end of
// the signature. We only want to bring the APIs that create and verify just
// the signature, therefore each signature is a constant
// DILITHIUM3_SIGNATURE_BYTES and we truncate the KAT's signed message down,
// "sm" down to a constant DILITHIUM3_SIGNATURE_BYTES.

std::vector<uint8_t> pub(pk_len);
std::vector<uint8_t> priv(sk_len);
std::vector<uint8_t> signature(sig_len);
sm.resize(sig_len);

// Convert string read from KAT to int
size_t mlen_int = std::stoi(mlen);
sm.resize(sig_len);

// Here we fix the DRBG (AES-CTR) so that we are able to seed it with the
// seed from the KAT (testing only)
pq_custom_randombytes_use_deterministic_for_testing();
pq_custom_randombytes_init_for_testing(seed.data());

// Generate our dilithium public and private key pair
bssl::UniquePtr<EVP_PKEY_CTX> pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_PQDSA, nullptr));
ASSERT_TRUE(pctx);
ASSERT_TRUE(EVP_PKEY_CTX_pqdsa_set_params(pctx.get(),GetParam().nid));
ASSERT_TRUE(EVP_PKEY_keygen_init(pctx.get()));
EVP_PKEY *raw = nullptr;
ASSERT_TRUE(EVP_PKEY_keygen(pctx.get(), &raw));
bssl::UniquePtr<EVP_PKEY> pkey(raw);

// Compare the expected public/secret key from KATs with generated values
CMP_VEC_AND_PKEY_PUBLIC(pk, pkey, pk_len);
CMP_VEC_AND_PKEY_SECRET(sk, pkey, sk_len);

// Generate a signature for the message
// We use EVP_DigestSign because dilithium supports the use of
// non-hash-then-sign (just like ed25519) so we first init EVP_DigestSign
// WITHOUT a hash function.
bssl::ScopedEVP_MD_CTX ctx;
ASSERT_TRUE(EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get()));
ASSERT_TRUE(EVP_DigestSign(ctx.get(), signature.data(), &sig_len, msg.data(), mlen_int));
EXPECT_EQ(Bytes(sm), Bytes(signature.data(), sig_len));
ctx.Reset();

// Verify the signature
ASSERT_TRUE(EVP_DigestVerifyInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get()));
ASSERT_TRUE(EVP_DigestVerify(ctx.get(), signature.data(), sig_len, msg.data(), mlen_int));
// Generate key pair from seed xi and assert that public and private keys
// are equal to expected values from KAT
ASSERT_TRUE(ml_dsa_65_keypair_internal(pub.data(),priv.data(),xi.data()));
EXPECT_EQ(Bytes(pub), Bytes(pk));
EXPECT_EQ(Bytes(priv), Bytes(sk));

// Prepare m_prime = (0 || ctxlen || ctx) as in FIPS 204: Algorithm 2 line 10
size_t i;
uint8_t m_prime[257];
size_t m_prime_len = ctxstr.size() + 2;
m_prime[0] = 0;
m_prime[1] = ctxstr.size();
for(i = 0; i < ctxstr.size(); i++) {
m_prime[2 + i] = ctxstr[i];
}

// Generate signature by signing |msg|, assert that signature is equal
// to expected value from KAT, then verify signature.
ASSERT_TRUE(ml_dsa_65_sign_internal(priv.data(),
signature.data(), &sig_len,
msg.data(), mlen_int,
m_prime,m_prime_len,
rng.data()));
ASSERT_EQ(Bytes(signature), Bytes(sm));
ASSERT_TRUE(ml_dsa_65_verify_internal(pub.data(),
signature.data(), sig_len,
msg.data(), mlen_int,
m_prime, m_prime_len));
});
}

Expand Down
5 changes: 2 additions & 3 deletions crypto/dilithium/pqcrystals_dilithium_ref_common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ The code was refactored in [this PR](https://github.com/aws/aws-lc/pull/1910) by
that initialize a given structure with values corresponding to a parameter set. This structure is then passed to every function that requires it as a function argument. In addition, the following changes were made to the source code in `pqcrystals_dilithium_ref_common` directory:

- `randombytes.{h|c}` are deleted because we are using the randomness generation functions provided by AWS-LC.
- `sign.c`: calls to `randombytes` function is replaced with calls to `pq_custom_randombytes` and the appropriate header file is included (`crypto/rand_extra/pq_custom_randombytes.h`).
- `sign.c`: calls to `randombytes` function is replaced with calls to `RAND_bytes` and the appropriate header file is included (`openssl/rand.h`).
- `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.
Loading

0 comments on commit 33dee33

Please sign in to comment.