From 9d21f38b8ea04a70a753626708e3de21d8d0b733 Mon Sep 17 00:00:00 2001 From: Jake Massimo Date: Mon, 16 Sep 2024 07:49:54 -0700 Subject: [PATCH] Migrated ML-KEM SHA3/SHAKE usage to fipsmodule (#1851) Following the inclusion of SHAKE as an extensible-output-function (XOF) in #1839, we are now able to fully support ML-KEM with SHA3/SHAKE usage within crypto/fipsmodule. As such, all references to the internal implementation of SHA3 (within crypto/kyber/pqcrystals_kyber_ref_common/fips202.{h|c}) have been removed. --- crypto/fipsmodule/ml_kem/README.md | 2 +- crypto/fipsmodule/ml_kem/ml_kem_ref/indcpa.c | 10 ++--- .../ml_kem/ml_kem_ref/symmetric-shake.c | 38 +++++++++++++------ .../fipsmodule/ml_kem/ml_kem_ref/symmetric.h | 15 ++++---- crypto/fipsmodule/sha/internal.h | 2 + 5 files changed, 42 insertions(+), 25 deletions(-) diff --git a/crypto/fipsmodule/ml_kem/README.md b/crypto/fipsmodule/ml_kem/README.md index 07502e80a6..69dc369d80 100644 --- a/crypto/fipsmodule/ml_kem/README.md +++ b/crypto/fipsmodule/ml_kem/README.md @@ -8,7 +8,7 @@ The code was refactored in [this PR](https://github.com/aws/aws-lc/pull/1763) 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 `ml_kem_ref` 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 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. +- `fips202.{h|c}` are deleted as all SHA3/SHAKE functionality is provided instead by AWS-LC fipsmodule/sha rather than the reference implementation. - `symmetric-shake.c`: unnecessary include of fips202.h is removed. - `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). diff --git a/crypto/fipsmodule/ml_kem/ml_kem_ref/indcpa.c b/crypto/fipsmodule/ml_kem/ml_kem_ref/indcpa.c index 3b01cd65ad..dcc5412d6c 100644 --- a/crypto/fipsmodule/ml_kem/ml_kem_ref/indcpa.c +++ b/crypto/fipsmodule/ml_kem/ml_kem_ref/indcpa.c @@ -167,16 +167,16 @@ void gen_matrix(ml_kem_params *params, polyvec *a, const uint8_t seed[KYBER_SYMB unsigned int ctr, i, j, k; unsigned int buflen, off; uint8_t buf[GEN_MATRIX_NBLOCKS*XOF_BLOCKBYTES+2]; - xof_state state; + KECCAK1600_CTX ctx; for(i=0;ik;i++) { for(j=0;jk;j++) { if(transposed) - xof_absorb(&state, seed, i, j); + xof_absorb(&ctx, seed, i, j); else - xof_absorb(&state, seed, j, i); + xof_absorb(&ctx, seed, j, i); - xof_squeezeblocks(buf, GEN_MATRIX_NBLOCKS, &state); + xof_squeezeblocks(buf, GEN_MATRIX_NBLOCKS, &ctx); buflen = GEN_MATRIX_NBLOCKS*XOF_BLOCKBYTES; ctr = rej_uniform(a[i].vec[j].coeffs, KYBER_N, buf, buflen); @@ -184,7 +184,7 @@ void gen_matrix(ml_kem_params *params, polyvec *a, const uint8_t seed[KYBER_SYMB off = buflen % 3; for(k = 0; k < off; k++) buf[k] = buf[buflen - off + k]; - xof_squeezeblocks(buf + off, 1, &state); + xof_squeezeblocks(buf + off, 1, &ctx); buflen = off + XOF_BLOCKBYTES; ctr += rej_uniform(a[i].vec[j].coeffs + ctr, KYBER_N - ctr, buf, buflen); } diff --git a/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c index 5855451c68..859e92fd98 100644 --- a/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c +++ b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric-shake.c @@ -9,12 +9,12 @@ * * Description: Absorb step of the SHAKE128 specialized for the Kyber context. * -* Arguments: - keccak_state *state: pointer to (uninitialized) output Keccak state +* Arguments: - KECCAK1600_CTX *ctx: pointer to (uninitialized) output Keccak state * - const uint8_t *seed: pointer to KYBER_SYMBYTES input to be absorbed into state * - uint8_t i: additional byte of input * - uint8_t j: additional byte of input **************************************************/ -void kyber_shake128_absorb(keccak_state *state, +void kyber_shake128_absorb(KECCAK1600_CTX *ctx, const uint8_t seed[KYBER_SYMBYTES], uint8_t x, uint8_t y) @@ -25,7 +25,25 @@ void kyber_shake128_absorb(keccak_state *state, extseed[KYBER_SYMBYTES+0] = x; extseed[KYBER_SYMBYTES+1] = y; - shake128_absorb_once(state, extseed, sizeof(extseed)); + SHAKE_Init(ctx, SHAKE128_BLOCKSIZE); + SHA3_Update(ctx, extseed, sizeof(extseed)); +} + +/************************************************* +* Name: kyber_shake128_squeeze +* +* Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of +* SHAKE128_RATE bytes each. Can be called multiple times +* to keep squeezing. Assumes new block has not yet been +* started. +* +* Arguments: - uint8_t *out: pointer to output blocks +* - size_t nblocks: number of blocks to be squeezed (written to output) +* - KECCAK1600_CTX *ctx: pointer to input/output Keccak state +**************************************************/ +void kyber_shake128_squeeze(KECCAK1600_CTX *ctx, uint8_t *out, int nblocks) +{ + SHAKE_Final(out, ctx, nblocks * SHAKE128_BLOCKSIZE); } /************************************************* @@ -46,7 +64,7 @@ void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYM memcpy(extkey, key, KYBER_SYMBYTES); extkey[KYBER_SYMBYTES] = nonce; - shake256(out, outlen, extkey, sizeof(extkey)); + SHAKE256(extkey, sizeof(extkey), out, outlen); } /************************************************* @@ -62,11 +80,9 @@ void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYM **************************************************/ void kyber_shake256_rkprf(ml_kem_params *params, uint8_t out[KYBER_SSBYTES], const uint8_t key[KYBER_SYMBYTES], const uint8_t *input) { - keccak_state s; - - shake256_init(&s); - shake256_absorb(&s, key, KYBER_SYMBYTES); - shake256_absorb(&s, input, params->ciphertext_bytes); - shake256_finalize(&s); - shake256_squeeze(out, KYBER_SSBYTES, &s); + KECCAK1600_CTX ctx; + SHAKE_Init(&ctx, SHAKE256_BLOCKSIZE); + SHA3_Update(&ctx, key, KYBER_SYMBYTES); + SHA3_Update(&ctx, input, params->ciphertext_bytes); + SHAKE_Final(out, &ctx, KYBER_SSBYTES); } diff --git a/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric.h b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric.h index f28fe6a04b..0f5678e51a 100644 --- a/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric.h +++ b/crypto/fipsmodule/ml_kem/ml_kem_ref/symmetric.h @@ -5,12 +5,10 @@ #include #include "params.h" -#include "../../../kyber/pqcrystals_kyber_ref_common/fips202.h" - -typedef keccak_state xof_state; +#include "../../sha/internal.h" #define kyber_shake128_absorb KYBER_NAMESPACE(kyber_shake128_absorb) -void kyber_shake128_absorb(keccak_state *s, +void kyber_shake128_absorb(KECCAK1600_CTX *ctx, const uint8_t seed[KYBER_SYMBYTES], uint8_t x, uint8_t y); @@ -21,12 +19,13 @@ void kyber_shake256_prf(uint8_t *out, size_t outlen, const uint8_t key[KYBER_SYM #define kyber_shake256_rkprf KYBER_NAMESPACE(kyber_shake256_rkprf) void kyber_shake256_rkprf(ml_kem_params *params, uint8_t out[KYBER_SSBYTES], const uint8_t key[KYBER_SYMBYTES], const uint8_t *input); -#define XOF_BLOCKBYTES SHAKE128_RATE +#define kyber_shake128_squeeze KYBER_NAMESPACE(kyber_shake128_squeeze) +void kyber_shake128_squeeze(KECCAK1600_CTX *ctx, uint8_t *out, int nblocks); -#define hash_h(OUT, IN, INBYTES) sha3_256(OUT, IN, INBYTES) -#define hash_g(OUT, IN, INBYTES) sha3_512(OUT, IN, INBYTES) +#define hash_h(OUT, IN, INBYTES) SHA3_256(IN, INBYTES, OUT) +#define hash_g(OUT, IN, INBYTES) SHA3_512(IN, INBYTES, OUT) #define xof_absorb(STATE, SEED, X, Y) kyber_shake128_absorb(STATE, SEED, X, Y) -#define xof_squeezeblocks(OUT, OUTBLOCKS, STATE) shake128_squeezeblocks(OUT, OUTBLOCKS, STATE) +#define xof_squeezeblocks(OUT, OUTBLOCKS, STATE) kyber_shake128_squeeze(STATE, OUT, OUTBLOCKS) #define prf(OUT, OUTBYTES, KEY, NONCE) kyber_shake256_prf(OUT, OUTBYTES, KEY, NONCE) #define rkprf(PARAMS, OUT, KEY, INPUT) kyber_shake256_rkprf(PARAMS, OUT, KEY, INPUT) diff --git a/crypto/fipsmodule/sha/internal.h b/crypto/fipsmodule/sha/internal.h index 7094dd6c1f..29970940bb 100644 --- a/crypto/fipsmodule/sha/internal.h +++ b/crypto/fipsmodule/sha/internal.h @@ -66,6 +66,8 @@ extern "C" { #define SHAKE_PAD_CHAR 0x1F #define SHAKE128_BLOCKSIZE (KECCAK1600_WIDTH - 128 * 2) / 8 #define SHAKE256_BLOCKSIZE (KECCAK1600_WIDTH - 256 * 2) / 8 +#define SHAKE128_RATE 168 +#define XOF_BLOCKBYTES SHAKE128_RATE // SHAKE128 has the maximum block size among the SHA3/SHAKE algorithms. #define SHA3_MAX_BLOCKSIZE SHAKE128_BLOCKSIZE