Skip to content

Commit

Permalink
FIPS 203 IPD update: ML-KEM-IPD-768 and ML-KEM-IPD-1024 (#1724)
Browse files Browse the repository at this point in the history
NIST have made the following public statements regarding the planned
changes to FIPS 203:
- NIST will specify lower-level de-randomized API to enable CAVP testing
with seeds as keys.

As such, this PR includes the following:
- The addition of ML-KEM-IPD-768 and ML-KEM-IPD-1024 to AWS-LC. *Note*:
as common functionality is already added to aws-lc, this lift is
extremely light, as we need only to define
`crypto/ml_kem/ml_kem_768_ipd.c` and `crypto/ml_kem/ml_kem_1024_ipd.c`.
- The addition of de-randomized testing API for ML-KEM-IPD-768 and
ML-KEM-IPD-1024
- KATs for ML-KEM-IPD-768 and ML-KEM-IPD-1024 that use seeds as keys (as
per CAVP requirement)
- An update to the file that captures the divergence from the upstream
reference listed at
https://github.com/aws/aws-lc/tree/main/crypto/ml_kem#readme. This
removes the outdated information regarding `pq_custom_randombytes` and
updates with information regarding the de-randomized API.

The new KEM algorithms have been added to the `built_in_kems` list, and
as such, are included within the complete `PerKEMTest` suite. This
includes testing of the de-randomized APIs added in
#1578.
  • Loading branch information
jakemas authored Jul 30, 2024
1 parent b862b16 commit 5cc9456
Show file tree
Hide file tree
Showing 18 changed files with 2,885 additions and 709 deletions.
2 changes: 2 additions & 0 deletions crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,8 @@ add_library(
lhash/lhash.c
mem.c
ml_kem/ml_kem_512_ipd.c
ml_kem/ml_kem_768_ipd.c
ml_kem/ml_kem_1024_ipd.c
ml_kem/ml_kem.c
obj/obj.c
obj/obj_xref.c
Expand Down
2 changes: 2 additions & 0 deletions crypto/evp_extra/evp_extra_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2073,6 +2073,8 @@ static const struct KnownKEM kKEMs[] = {
{"Kyber768r3", NID_KYBER768_R3, 1184, 2400, 1088, 32, 64, 32, "kyber/kat/kyber768r3.txt"},
{"Kyber1024r3", NID_KYBER1024_R3, 1568, 3168, 1568, 32, 64, 32, "kyber/kat/kyber1024r3.txt"},
{"MLKEM512IPD", NID_MLKEM512IPD, 800, 1632, 768, 32, 64, 32, "ml_kem/kat/mlkem512ipd.txt"},
{"MLKEM768IPD", NID_MLKEM768IPD, 1184, 2400, 1088, 32, 64, 32, "ml_kem/kat/mlkem768ipd.txt"},
{"MLKEM1024IPD", NID_MLKEM1024IPD, 1568, 3168, 1568, 32, 64, 32, "ml_kem/kat/mlkem1024ipd.txt"},
};

class PerKEMTest : public testing::TestWithParam<KnownKEM> {};
Expand Down
2 changes: 2 additions & 0 deletions crypto/kem/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ extern const KEM_METHOD kem_kyber512r3_method;
extern const KEM_METHOD kem_kyber768r3_method;
extern const KEM_METHOD kem_kyber1024r3_method;
extern const KEM_METHOD kem_ml_kem_512_ipd_method;
extern const KEM_METHOD kem_ml_kem_768_ipd_method;
extern const KEM_METHOD kem_ml_kem_1024_ipd_method;

// KEM structure and helper functions.
typedef struct {
Expand Down
38 changes: 33 additions & 5 deletions crypto/kem/kem.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
// - Kyber is not standardized yet, so we use the latest specification
// from Round 3 of NIST PQC project.

#define AWSLC_NUM_BUILT_IN_KEMS 4
#define AWSLC_NUM_BUILT_IN_KEMS 6

// TODO(awslc): placeholder OIDs, replace with the real ones when available.
static const uint8_t kOIDKyber512r3[] = {0xff, 0xff, 0xff, 0xff};
static const uint8_t kOIDKyber768r3[] = {0xff, 0xff, 0xff, 0xff};
static const uint8_t kOIDKyber1024r3[] = {0xff, 0xff, 0xff, 0xff};
static const uint8_t kOIDMLKEM512IPD[] = {0xff, 0xff, 0xff, 0xff};
static const uint8_t kOIDKyber512r3[] = {0xff, 0xff, 0xff, 0xff};
static const uint8_t kOIDKyber768r3[] = {0xff, 0xff, 0xff, 0xff};
static const uint8_t kOIDKyber1024r3[] = {0xff, 0xff, 0xff, 0xff};
static const uint8_t kOIDMLKEM512IPD[] = {0xff, 0xff, 0xff, 0xff};
static const uint8_t kOIDMLKEM768IPD[] = {0xff, 0xff, 0xff, 0xff};
static const uint8_t kOIDMLKEM1024IPD[] = {0xff, 0xff, 0xff, 0xff};

static const KEM built_in_kems[AWSLC_NUM_BUILT_IN_KEMS] = {
{
Expand Down Expand Up @@ -82,6 +84,32 @@ static const KEM built_in_kems[AWSLC_NUM_BUILT_IN_KEMS] = {
MLKEM512IPD_ENCAPS_SEED_LEN, // kem.encaps_seed_len
&kem_ml_kem_512_ipd_method, // kem.method
},
{
NID_MLKEM768IPD, // kem.nid
kOIDMLKEM768IPD, // kem.oid
sizeof(kOIDMLKEM768IPD), // kem.oid_len
"MLKEM768 IPD", // kem.comment
MLKEM768IPD_PUBLIC_KEY_BYTES, // kem.public_key_len
MLKEM768IPD_SECRET_KEY_BYTES, // kem.secret_key_len
MLKEM768IPD_CIPHERTEXT_BYTES, // kem.ciphertext_len
MLKEM768IPD_SHARED_SECRET_LEN, // kem.shared_secret_len
MLKEM768IPD_KEYGEN_SEED_LEN, // kem.keygen_seed_len
MLKEM768IPD_ENCAPS_SEED_LEN, // kem.encaps_seed_len
&kem_ml_kem_768_ipd_method, // kem.method
},
{
NID_MLKEM1024IPD, // kem.nid
kOIDMLKEM1024IPD, // kem.oid
sizeof(kOIDMLKEM1024IPD), // kem.oid_len
"MLKEM1024 IPD", // kem.comment
MLKEM1024IPD_PUBLIC_KEY_BYTES, // kem.public_key_len
MLKEM1024IPD_SECRET_KEY_BYTES, // kem.secret_key_len
MLKEM1024IPD_CIPHERTEXT_BYTES, // kem.ciphertext_len
MLKEM1024IPD_SHARED_SECRET_LEN, // kem.shared_secret_len
MLKEM1024IPD_KEYGEN_SEED_LEN, // kem.keygen_seed_len
MLKEM1024IPD_ENCAPS_SEED_LEN, // kem.encaps_seed_len
&kem_ml_kem_1024_ipd_method, // kem.method
},
};

const KEM *KEM_find_kem_by_nid(int nid) {
Expand Down
76 changes: 76 additions & 0 deletions crypto/kem/kem_methods.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,79 @@ const KEM_METHOD kem_ml_kem_512_ipd_method = {
ml_kem_512_ipd_encaps,
ml_kem_512_ipd_decaps,
};

static int ml_kem_768_ipd_keygen_deterministic(uint8_t *public_key,
uint8_t *secret_key,
const uint8_t *seed) {
return ml_kem_768_ipd_keypair_deterministic(public_key, secret_key, seed) == 0;
}

static int ml_kem_768_ipd_keygen(uint8_t *public_key,
uint8_t *secret_key) {
return ml_kem_768_ipd_keypair(public_key, secret_key) == 0;
}

static int ml_kem_768_ipd_encaps_deterministic(uint8_t *ciphertext,
uint8_t *shared_secret,
const uint8_t *public_key,
const uint8_t *seed) {
return ml_kem_768_ipd_encapsulate_deterministic(ciphertext, shared_secret, public_key, seed) == 0;
}

static int ml_kem_768_ipd_encaps(uint8_t *ciphertext,
uint8_t *shared_secret,
const uint8_t *public_key) {
return ml_kem_768_ipd_encapsulate(ciphertext, shared_secret, public_key) == 0;
}

static int ml_kem_768_ipd_decaps(uint8_t *shared_secret,
const uint8_t *ciphertext,
const uint8_t *secret_key) {
return ml_kem_768_ipd_decapsulate(shared_secret, ciphertext, secret_key) == 0;
}

const KEM_METHOD kem_ml_kem_768_ipd_method = {
ml_kem_768_ipd_keygen_deterministic,
ml_kem_768_ipd_keygen,
ml_kem_768_ipd_encaps_deterministic,
ml_kem_768_ipd_encaps,
ml_kem_768_ipd_decaps,
};

static int ml_kem_1024_ipd_keygen_deterministic(uint8_t *public_key,
uint8_t *secret_key,
const uint8_t *seed) {
return ml_kem_1024_ipd_keypair_deterministic(public_key, secret_key, seed) == 0;
}

static int ml_kem_1024_ipd_keygen(uint8_t *public_key,
uint8_t *secret_key) {
return ml_kem_1024_ipd_keypair(public_key, secret_key) == 0;
}

static int ml_kem_1024_ipd_encaps_deterministic(uint8_t *ciphertext,
uint8_t *shared_secret,
const uint8_t *public_key,
const uint8_t *seed) {
return ml_kem_1024_ipd_encapsulate_deterministic(ciphertext, shared_secret, public_key, seed) == 0;
}

static int ml_kem_1024_ipd_encaps(uint8_t *ciphertext,
uint8_t *shared_secret,
const uint8_t *public_key) {
return ml_kem_1024_ipd_encapsulate(ciphertext, shared_secret, public_key) == 0;
}

static int ml_kem_1024_ipd_decaps(uint8_t *shared_secret,
const uint8_t *ciphertext,
const uint8_t *secret_key) {
return ml_kem_1024_ipd_decapsulate(shared_secret, ciphertext, secret_key) == 0;
}

const KEM_METHOD kem_ml_kem_1024_ipd_method = {
ml_kem_1024_ipd_keygen_deterministic,
ml_kem_1024_ipd_keygen,
ml_kem_1024_ipd_encaps_deterministic,
ml_kem_1024_ipd_encaps,
ml_kem_1024_ipd_decaps,
};
8 changes: 4 additions & 4 deletions crypto/ml_kem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ NOTE: THIS IS AN IMPLEMENTATION OF THE DRAFT VERSION OF FIPS 203, NOT THE FINAL

The following changes were made to the source code in `ml_kem_ipd_ref_common` directory:
- `randombytes.{h|c}` are deleted because we are using the randomness generation functions provided by AWS-LC.
- `kem.c`: call to randombytes function is replaced with a call to pq_custom_randombytes and the appropriate header file is included (crypto/rand_extra/pq_custom_randombytes.h).
- `kem.c`: call to randombytes function is replaced with a call to RAND_bytes and the appropriate header file is included (openssl/rand.h).
- `fips202.{h|c}` are deleted and the ones from `crypto/kyber/pqcrystals_kyber_ref_common` directory are used.
- `symmetric-shake.c`: unnecessary include of fips202.h is removed.
- `api.h`: `pqcrystals` prefix substituted with `ml_kem` (to be able build alongside `crypto/kyber`).
* `poly.c`: the `poly_frommsg` function was modified to address the constant-time issue described [here](https://github.com/pq-crystals/kyber/commit/9b8d30698a3e7449aeb34e62339d4176f11e3c6c).
- `api.h`: `pqcrystals` prefix substituted with `ml_kem` (to be able to build alongside `crypto/kyber`).
- `poly.c`: the `poly_frommsg` function was modified to address the constant-time issue described [here](https://github.com/pq-crystals/kyber/commit/9b8d30698a3e7449aeb34e62339d4176f11e3c6c).

The KATs were generated by compiling and running the KAT generator tests from the official repository. Specifically, running `make` in the `ref` folder produces `nistkat/PQCgenKAT_kem512` binary that can generates the test vectors.
The KATs were generated by compiling and running the KAT generator tests from the official repository. Specifically, running `make` in the `ref` folder produces `nistkat/PQCgenKAT_kem512` binary that can generates the test vectors. The files in `ml_kem/kat/mlkem{512|768|1024}ipd.txt` utilize the deterministic API for testing, and instead use the intermediate seed values (`keypair_coins` and `encap_coins`) instead of a call to randomness generation. The same intermediate values can be extracted from the official repository from the `coins` generated during keypair, and encapsulate.
Loading

0 comments on commit 5cc9456

Please sign in to comment.