From 316177a7f176c077bb643390fb5e7942688ab676 Mon Sep 17 00:00:00 2001 From: Sean Parkinson Date: Mon, 3 Feb 2025 12:35:23 +1000 Subject: [PATCH] ML-KEM/Kyber: small memory usage Options to compile ML-KEM/Kyber to use less dynamic memory. Only available with C code and has small performance trade-off. --- .wolfssl_known_macro_extras | 2 + wolfcrypt/src/wc_kyber.c | 140 +++++++++++---- wolfcrypt/src/wc_kyber_poly.c | 314 ++++++++++++++++++++++++++++++++-- wolfssl/wolfcrypt/wc_kyber.h | 14 ++ 4 files changed, 431 insertions(+), 39 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index e69f11d5d..a29ead2ab 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -676,6 +676,8 @@ WOLFSSL_MAKE_SYSTEM_NAME_LINUX WOLFSSL_MAKE_SYSTEM_NAME_WSL WOLFSSL_MDK5 WOLFSSL_MEM_FAIL_COUNT +WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM +WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM WOLFSSL_MONT_RED_CT WOLFSSL_MP_COND_COPY WOLFSSL_MP_INVMOD_CONSTANT_TIME diff --git a/wolfcrypt/src/wc_kyber.c b/wolfcrypt/src/wc_kyber.c index c3f6680a5..658d73a8d 100644 --- a/wolfcrypt/src/wc_kyber.c +++ b/wolfcrypt/src/wc_kyber.c @@ -24,6 +24,20 @@ * https://csrc.nist.gov/Projects/post-quantum-cryptography/post-quantum-cryptography-standardization/round-3-submissions */ +/* Possible Kyber options: + * + * WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM Default: OFF + * Uses less dynamic memory to perform key generation. + * Has a small performance trade-off. + * Only usable with C implementation. + * + * WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM Default: OFF + * Uses less dynamic memory to perform encapsulation. + * Affects decapsulation too as encapsulation called. + * Has a small performance trade-off. + * Only usable with C implementation. + */ + #ifdef HAVE_CONFIG_H #include #endif @@ -42,6 +56,14 @@ #include #endif +#if defined(USE_INTEL_SPEEDUP) || \ + (defined(__aarch64__) && defined(WOLFSSL_ARMASM)) + #if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ + defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) + #error "Can't use small memory with assembly optimized code" + #endif +#endif + #ifdef WOLFSSL_WC_KYBER /******************************************************************************/ @@ -225,7 +247,8 @@ int wc_KyberKey_MakeKey(KyberKey* key, WC_RNG* rng) * Make a Kyber key object using random data. * * @param [in, out] key Kyber key ovject. - * @param [in] rng Random number generator. + * @param [in] rand Random data. + * @param [in] len Length of random data in bytes. * @return 0 on success. * @return BAD_FUNC_ARG when key or rand is NULL. * @return BUFFER_E when length is not KYBER_MAKEKEY_RAND_SZ. @@ -239,11 +262,17 @@ int wc_KyberKey_MakeKeyWithRandom(KyberKey* key, const unsigned char* rand, byte* pubSeed = buf; byte* noiseSeed = buf + KYBER_SYM_SZ; #ifndef WOLFSSL_NO_MALLOC - sword16* a = NULL; -#else - sword16 a[(KYBER_MAX_K + 1) * KYBER_MAX_K * KYBER_N]; -#endif sword16* e = NULL; +#else +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM + sword16 e[(KYBER_MAX_K + 1) * KYBER_MAX_K * KYBER_N]; +#else + sword16 e[KYBER_MAX_K * KYBER_N]; +#endif +#endif +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM + sword16* a = NULL; +#endif int ret = 0; int kp = 0; @@ -302,9 +331,14 @@ int wc_KyberKey_MakeKeyWithRandom(KyberKey* key, const unsigned char* rand, #ifndef WOLFSSL_NO_MALLOC if (ret == 0) { /* Allocate dynamic memory for matrix and error vector. */ - a = (sword16*)XMALLOC((kp + 1) * kp * KYBER_N * sizeof(sword16), +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM + e = (sword16*)XMALLOC((kp + 1) * kp * KYBER_N * sizeof(sword16), key->heap, DYNAMIC_TYPE_TMP_BUFFER); - if (a == NULL) { +#else + e = (sword16*)XMALLOC(kp * KYBER_N * sizeof(sword16), + key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + if (e == NULL) { ret = MEMORY_E; } } @@ -312,8 +346,10 @@ int wc_KyberKey_MakeKeyWithRandom(KyberKey* key, const unsigned char* rand, if (ret == 0) { const byte* d = rand; +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM /* Error vector allocated at end of a. */ - e = a + (kp * kp * KYBER_N); + a = e + (kp * KYBER_N); +#endif #if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) if (key->type & KYBER_ORIGINAL) @@ -344,20 +380,29 @@ int wc_KyberKey_MakeKeyWithRandom(KyberKey* key, const unsigned char* rand, /* Cache the z value for decapsulation and encoding private key. */ XMEMCPY(key->z, z, sizeof(key->z)); - /* Generate the matrix A. */ - ret = kyber_gen_matrix(&key->prf, a, kp, pubSeed, 0); - } - - if (ret == 0) { /* Initialize PRF for use in noise generation. */ kyber_prf_init(&key->prf); +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM /* Generate noise using PRF. */ ret = kyber_get_noise(&key->prf, kp, key->priv, e, NULL, noiseSeed); } + if (ret == 0) { + /* Generate the matrix A. */ + ret = kyber_gen_matrix(&key->prf, a, kp, pubSeed, 0); + } if (ret == 0) { /* Generate key pair from random data. */ kyber_keygen(key->priv, key->pub, e, a, kp); - +#else + /* Generate noise using PRF. */ + ret = kyber_get_noise(&key->prf, kp, key->priv, NULL, NULL, noiseSeed); + } + if (ret == 0) { + ret = kyber_keygen_seeds(key->priv, key->pub, &key->prf, e, kp, + pubSeed, noiseSeed); + } + if (ret == 0) { +#endif /* Private and public key are set/available. */ key->flags |= KYBER_FLAG_PRIV_SET | KYBER_FLAG_PUB_SET; } @@ -365,7 +410,7 @@ int wc_KyberKey_MakeKeyWithRandom(KyberKey* key, const unsigned char* rand, #ifndef WOLFSSL_NO_MALLOC /* Free dynamic memory allocated in function. */ if (key != NULL) { - XFREE(a, key->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(e, key->heap, DYNAMIC_TYPE_TMP_BUFFER); } #endif @@ -470,15 +515,25 @@ static int kyberkey_encapsulate(KyberKey* key, const byte* msg, byte* coins, { int ret = 0; sword16* sp = NULL; - sword16* ep = NULL; +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM sword16* k = NULL; + sword16* ep = NULL; sword16* epp = NULL; +#endif unsigned int kp = 0; unsigned int compVecSz = 0; #ifndef WOLFSSL_NO_MALLOC sword16* at = NULL; #else +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM sword16 at[((KYBER_MAX_K + 3) * KYBER_MAX_K + 3) * KYBER_N]; +#else + sword16 at[3 * KYBER_MAX_K * KYBER_N]; +#endif +#endif +#ifdef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM + sword16* bp; + sword16* v; #endif /* Establish parameters based on key type. */ @@ -532,8 +587,13 @@ static int kyberkey_encapsulate(KyberKey* key, const byte* msg, byte* coins, #ifndef WOLFSSL_NO_MALLOC if (ret == 0) { /* Allocate dynamic memory for all matrices, vectors and polynomials. */ +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM at = (sword16*)XMALLOC(((kp + 3) * kp + 3) * KYBER_N * sizeof(sword16), key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#else + at = (sword16*)XMALLOC(3 * kp * KYBER_N * sizeof(sword16), key->heap, + DYNAMIC_TYPE_TMP_BUFFER); +#endif if (at == NULL) { ret = MEMORY_E; } @@ -541,36 +601,58 @@ static int kyberkey_encapsulate(KyberKey* key, const byte* msg, byte* coins, #endif if (ret == 0) { +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM /* Assign allocated dynamic memory to pointers. - * at (m) | k (p) | sp (v) | sp (v) | epp (v) | bp (p) | v (v) */ + * at (m) | k (p) | sp (v) | ep (p) | epp (v) | bp (v) | v (p) */ k = at + KYBER_N * kp * kp; sp = k + KYBER_N; ep = sp + KYBER_N * kp; epp = ep + KYBER_N * kp; +#else + /* Assign allocated dynamic memory to pointers. + * at (v) | sp (v) | bp (v) */ + sp = at + KYBER_N * kp; +#endif + /* Initialize the PRF for use in the noise generation. */ + kyber_prf_init(&key->prf); +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM /* Convert msg to a polynomial. */ kyber_from_msg(k, msg); - /* Generate the transposed matrix. */ - ret = kyber_gen_matrix(&key->prf, at, kp, key->pubSeed, 1); - } - if (ret == 0) { - /* Initialize the PRF for use in the noise generation. */ - kyber_prf_init(&key->prf); /* Generate noise using PRF. */ ret = kyber_get_noise(&key->prf, kp, sp, ep, epp, coins); } + if (ret == 0) { + /* Generate the transposed matrix. */ + ret = kyber_gen_matrix(&key->prf, at, kp, key->pubSeed, 1); + } if (ret == 0) { sword16* bp; sword16* v; /* Assign remaining allocated dynamic memory to pointers. - * at (m) | k (p) | sp (v) | sp (v) | epp (v) | bp (p) | v (v)*/ + * at (m) | k (p) | sp (v) | ep (p) | epp (v) | bp (v) | v (p)*/ bp = epp + KYBER_N; v = bp + KYBER_N * kp; /* Perform encapsulation maths. */ kyber_encapsulate(key->pub, bp, v, at, sp, ep, epp, k, kp); +#else + /* Generate noise using PRF. */ + ret = kyber_get_noise(&key->prf, kp, sp, NULL, NULL, coins); + } + if (ret == 0) { + /* Assign remaining allocated dynamic memory to pointers. + * at (v) | sp (v) | bp (v) */ + bp = sp + KYBER_N * kp; + v = at; + + ret = kyber_encapsulate_seeds(key->pub, &key->prf, bp, at, sp, kp, msg, + key->pubSeed, coins); + } + if (ret == 0) { +#endif #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) if (kp == KYBER512_K) { @@ -848,7 +930,7 @@ static KYBER_NOINLINE int kyberkey_decapsulate(KyberKey* key, #if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) sword16* bp = NULL; #else - sword16 bp[(KYBER_MAX_K + 2) * KYBER_N]; + sword16 bp[(KYBER_MAX_K + 1) * KYBER_N]; #endif /* Establish parameters based on key type. */ @@ -901,8 +983,8 @@ static KYBER_NOINLINE int kyberkey_decapsulate(KyberKey* key, #if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) if (ret == 0) { - /* Allocate dynamic memory for a vector and two polynomials. */ - bp = (sword16*)XMALLOC((kp + 2) * KYBER_N * sizeof(sword16), key->heap, + /* Allocate dynamic memory for a vector and a polynomial. */ + bp = (sword16*)XMALLOC((kp + 1) * KYBER_N * sizeof(sword16), key->heap, DYNAMIC_TYPE_TMP_BUFFER); if (bp == NULL) { ret = MEMORY_E; @@ -911,9 +993,9 @@ static KYBER_NOINLINE int kyberkey_decapsulate(KyberKey* key, #endif if (ret == 0) { /* Assign allocated dynamic memory to pointers. - * bp (v) | v (p) | mp (p) */ + * bp (v) | v (p) */ v = bp + kp * KYBER_N; - mp = v + KYBER_N; + mp = bp; #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) if (kp == KYBER512_K) { diff --git a/wolfcrypt/src/wc_kyber_poly.c b/wolfcrypt/src/wc_kyber_poly.c index 35873edb2..7886c82b2 100644 --- a/wolfcrypt/src/wc_kyber_poly.c +++ b/wolfcrypt/src/wc_kyber_poly.c @@ -81,6 +81,16 @@ #include #endif +#if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ + defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) +static int kyber_gen_matrix_i(KYBER_PRF_T* prf, sword16* a, int kp, byte* seed, + int i, int transposed); +static int kyber_get_noise_i(KYBER_PRF_T* prf, int kp, sword16* vec2, + byte* seed, int i, int make); +static int kyber_get_noise_eta2_c(KYBER_PRF_T* prf, sword16* p, + const byte* seed); +#endif + /* Declared in wc_kyber.c to stop compiler optimizer from simplifying. */ extern volatile sword16 kyber_opt_blocker; @@ -1088,10 +1098,16 @@ static void kyber_pointwise_acc_mont(sword16* r, const sword16* a, unsigned int i; kyber_basemul_mont(r, a, b); +#ifdef WOLFSSL_KYBER_SMALL + for (i = 1; i < kp; ++i) { + kyber_basemul_mont_add(r, a + i * KYBER_N, b + i * KYBER_N); + } +#else for (i = 1; i < kp - 1; ++i) { kyber_basemul_mont_add(r, a + i * KYBER_N, b + i * KYBER_N); } kyber_basemul_mont_add(r, a + (kp - 1) * KYBER_N, b + (kp - 1) * KYBER_N); +#endif } /******************************************************************************/ @@ -1281,6 +1297,7 @@ void kyber_decapsulate(const sword16* priv, sword16* mp, sword16* bp, #else +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM /* Generate a public-private key pair from randomly generated data. * * @param [in, out] priv Private key vector of polynomials. @@ -1344,6 +1361,67 @@ void kyber_keygen(sword16* priv, sword16* pub, sword16* e, const sword16* a, } } +#else + +/* Generate a public-private key pair from randomly generated data. + * + * @param [in, out] priv Private key vector of polynomials. + * @param [out] pub Public key vector of polynomials. + * @param [in] prf XOF object. + * @param [in] tv Temporary vector of polynomials. + * @param [in] kp Number of polynomials in vector. + * @param [in] seed Random seed to generate matrix A from. + * @param [in] noiseSeed Random seed to generate noise from. + */ +int kyber_keygen_seeds(sword16* priv, sword16* pub, KYBER_PRF_T* prf, + sword16* tv, int kp, byte* seed, byte* noiseSeed) +{ + int i; + int ret = 0; + + /* Transform private key. All of result used in public key calculation */ + for (i = 0; i < kp; ++i) { + kyber_ntt(priv + i * KYBER_N); + } + + /* For each polynomial in the vectors. */ + for (i = 0; i < kp; ++i) { + unsigned int j; + + /* Generate a vector of matrix A. */ + ret = kyber_gen_matrix_i(prf, tv, kp, seed, i, 0); + if (ret != 0) { + break; + } + + /* Multiply a by private into public polynomial. */ + kyber_pointwise_acc_mont(pub + i * KYBER_N, tv, priv, kp); + /* Convert public polynomial to Montgomery form. */ + for (j = 0; j < KYBER_N; ++j) { + sword32 t = pub[i * KYBER_N + j] * (sword32)KYBER_F; + pub[i * KYBER_N + j] = KYBER_MONT_RED(t); + } + + /* Generate noise using PRF. */ + ret = kyber_get_noise_i(prf, kp, tv, noiseSeed, i, 1); + if (ret != 0) { + break; + } + /* Transform error values polynomial. */ + kyber_ntt(tv); + /* Add errors to public key and reduce. */ + for (j = 0; j < KYBER_N; ++j) { + sword16 t = pub[i * KYBER_N + j] + tv[j]; + pub[i * KYBER_N + j] = KYBER_BARRETT_RED(t); + } + } + + return ret; +} + +#endif + +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM /* Encapsulate message. * * @param [in] pub Public key vector of polynomials. @@ -1394,7 +1472,6 @@ static void kyber_encapsulate_c(const sword16* pub, sword16* bp, sword16* v, } } - /* Encapsulate message. * * @param [in] pub Public key vector of polynomials. @@ -1423,6 +1500,85 @@ void kyber_encapsulate(const sword16* pub, sword16* bp, sword16* v, } } +#else + +/* Encapsulate message. + * + * @param [in] pub Public key vector of polynomials. + * @param [in] prf XOF object. + * @param [out] bp Vector of polynomials. + * @param [in, out] tp Polynomial. + * @param [in] sp Vector of polynomials. + * @param [in] kp Number of polynomials in vector. + * @param [in] msg Message to encapsulate. + * @param [in] seed Random seed to generate matrix A from. + * @param [in] coins Random seed to generate noise from. + */ +int kyber_encapsulate_seeds(const sword16* pub, KYBER_PRF_T* prf, sword16* bp, + sword16* tp, sword16* sp, int kp, const byte* msg, byte* seed, byte* coins) +{ + int ret = 0; + int i; + sword16* at = tp; + sword16* ep = tp; + sword16* v = tp; + sword16* epp = tp + KYBER_N; + sword16* m = sp; + + /* Transform sp. All of result used in calculation of bp and v. */ + for (i = 0; i < kp; ++i) { + kyber_ntt(sp + i * KYBER_N); + } + + /* For each polynomial in the vectors. */ + for (i = 0; i < kp; ++i) { + unsigned int j; + + /* Generate a vector of matrix A. */ + ret = kyber_gen_matrix_i(prf, at, kp, seed, i, 1); + if (ret != 0) { + break; + } + + /* Multiply at by sp into bp polynomial. */ + kyber_pointwise_acc_mont(bp + i * KYBER_N, at, sp, kp); + /* Inverse transform bp polynomial. */ + kyber_invntt(bp + i * KYBER_N); + + /* Generate noise using PRF. */ + ret = kyber_get_noise_i(prf, kp, ep, coins, i, 0); + if (ret != 0) { + break; + } + /* Add errors to bp and reduce. */ + for (j = 0; j < KYBER_N; ++j) { + sword16 t = bp[i * KYBER_N + j] + ep[j]; + bp[i * KYBER_N + j] = KYBER_BARRETT_RED(t); + } + } + + /* Multiply public key by sp into v polynomial. */ + kyber_pointwise_acc_mont(v, pub, sp, kp); + /* Inverse transform v. */ + kyber_invntt(v); + + kyber_from_msg(m, msg); + + /* Generate noise using PRF. */ + coins[KYBER_SYM_SZ] = 2 * kp; + ret = kyber_get_noise_eta2_c(prf, epp, coins); + if (ret == 0) { + /* Add errors and message to v and reduce. */ + for (i = 0; i < KYBER_N; ++i) { + sword16 t = v[i] + epp[i] + m[i]; + tp[i] = KYBER_BARRETT_RED(t); + } + } + + return ret; +} +#endif + /* Decapsulate message. * * @param [in] priv Private key vector of polynomials. @@ -2362,6 +2518,9 @@ static unsigned int kyber_rej_uniform_c(sword16* p, unsigned int len, } #endif +#if !defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ + !defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) + #if !(defined(WOLFSSL_ARMASM) && defined(__aarch64__)) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * @@ -2379,7 +2538,7 @@ static unsigned int kyber_rej_uniform_c(sword16* p, unsigned int len, static int kyber_gen_matrix_c(KYBER_PRF_T* prf, sword16* a, int kp, byte* seed, int transposed) { -#ifdef WOLFSSL_SMALL_STACK +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) byte* rand; #else byte rand[GEN_MATRIX_SIZE + 2]; @@ -2390,7 +2549,7 @@ static int kyber_gen_matrix_c(KYBER_PRF_T* prf, sword16* a, int kp, byte* seed, XMEMCPY(extSeed, seed, KYBER_SYM_SZ); -#ifdef WOLFSSL_SMALL_STACK +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) /* Allocate large amount of memory to hold random bytes to be samples. */ rand = (byte*)XMALLOC(GEN_MATRIX_SIZE + 2, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (rand == NULL) { @@ -2441,7 +2600,7 @@ static int kyber_gen_matrix_c(KYBER_PRF_T* prf, sword16* a, int kp, byte* seed, } } -#ifdef WOLFSSL_SMALL_STACK +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) /* Dispose of temporary buffer. */ XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif @@ -2534,6 +2693,97 @@ int kyber_gen_matrix(KYBER_PRF_T* prf, sword16* a, int kp, byte* seed, return ret; } +#endif + +#if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ + defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) + +/* Deterministically generate a matrix (or transpose) of uniform integers mod q. + * + * Seed used with XOF to generate random bytes. + * + * @param [in] prf XOF object. + * @param [out] a Matrix of uniform integers. + * @param [in] kp Number of dimensions. kp x kp polynomials. + * @param [in] seed Bytes to seed XOF generation. + * @param [in] i Index of vector to generate. + * @param [in] transposed Whether A or A^T is generated. + * @return 0 on success. + * @return MEMORY_E when dynamic memory allocation fails. Only possible when + * WOLFSSL_SMALL_STACK is defined. + */ +static int kyber_gen_matrix_i(KYBER_PRF_T* prf, sword16* a, int kp, byte* seed, + int i, int transposed) +{ +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + byte* rand; +#else + byte rand[GEN_MATRIX_SIZE + 2]; +#endif + byte extSeed[KYBER_SYM_SZ + 2]; + int ret = 0; + int j; + + XMEMCPY(extSeed, seed, KYBER_SYM_SZ); + +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + /* Allocate large amount of memory to hold random bytes to be samples. */ + rand = (byte*)XMALLOC(GEN_MATRIX_SIZE + 2, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (rand == NULL) { + ret = MEMORY_E; + } +#endif + +#if !defined(WOLFSSL_KYBER_SMALL) && defined(WC_64BIT_CPU) + /* Loading 64 bits, only using 48 bits. Loading 2 bytes more than used. */ + if (ret == 0) { + rand[GEN_MATRIX_SIZE+0] = 0xff; + rand[GEN_MATRIX_SIZE+1] = 0xff; + } +#endif + + /* Generate each polynomial in vector from seed with indices. */ + for (j = 0; (ret == 0) && (j < kp); j++) { + if (transposed) { + extSeed[KYBER_SYM_SZ + 0] = i; + extSeed[KYBER_SYM_SZ + 1] = j; + } + else { + extSeed[KYBER_SYM_SZ + 0] = j; + extSeed[KYBER_SYM_SZ + 1] = i; + } + /* Absorb the index specific seed. */ + ret = kyber_xof_absorb(prf, extSeed, sizeof(extSeed)); + if (ret == 0) { + /* Create out based on the seed. */ + ret = kyber_xof_squeezeblocks(prf, rand, GEN_MATRIX_NBLOCKS); + } + if (ret == 0) { + unsigned int ctr; + + /* Sample random bytes to create a polynomial. */ + ctr = kyber_rej_uniform_c(a + j * KYBER_N, KYBER_N, rand, + GEN_MATRIX_SIZE); + /* Create more blocks if too many rejected. */ + while (ctr < KYBER_N) { + kyber_xof_squeezeblocks(prf, rand, 1); + ctr += kyber_rej_uniform_c(a + j * KYBER_N + ctr, + KYBER_N - ctr, rand, XOF_BLOCK_SIZE); + } + } + } + +#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) + /* Dispose of temporary buffer. */ + XFREE(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return ret; +} + +#endif + + /******************************************************************************/ /* Subtract one 2 bit value from another out of a larger number. @@ -3276,12 +3526,17 @@ static int kyber_get_noise_c(KYBER_PRF_T* prf, int kp, sword16* vec1, int eta1, /* Increment value of appended byte. */ seed[KYBER_SYM_SZ]++; } - /* Generate noise for error. */ - for (i = 0; (ret == 0) && (i < kp); i++) { - /* Generate noise for each dimension of vector. */ - ret = kyber_get_noise_eta1_c(prf, vec2 + i * KYBER_N, seed, eta2); - /* Increment value of appended byte. */ - seed[KYBER_SYM_SZ]++; + if ((ret == 0) && (vec2 != NULL)) { + /* Generate noise for error. */ + for (i = 0; (ret == 0) && (i < kp); i++) { + /* Generate noise for each dimension of vector. */ + ret = kyber_get_noise_eta1_c(prf, vec2 + i * KYBER_N, seed, eta2); + /* Increment value of appended byte. */ + seed[KYBER_SYM_SZ]++; + } + } + else { + seed[KYBER_SYM_SZ] = 2 * kp; } if ((ret == 0) && (poly != NULL)) { /* Generating random error polynomial. */ @@ -3382,6 +3637,45 @@ int kyber_get_noise(KYBER_PRF_T* prf, int kp, sword16* vec1, return ret; } +#if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ + defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) +/* Get the noise/error by calculating random bytes and sampling to a binomial + * distribution. + * + * @param [in, out] prf Pseudo-random function object. + * @param [in] kp Number of polynomials in vector. + * @param [out] vec2 Second Vector of polynomials. + * @param [in] seed Seed to use when calculating random. + * @param [in] i Index of vector to generate. + * @param [in] make Indicates generation is for making a key. + * @return 0 on success. + */ +static int kyber_get_noise_i(KYBER_PRF_T* prf, int kp, sword16* vec2, + byte* seed, int i, int make) +{ + int ret; + + /* Initialize the PRF (generating matrix A leaves it in uninitialized + * state). */ + kyber_prf_init(prf); + + /* Set index of polynomial of second vector into seed. */ + seed[KYBER_SYM_SZ] = kp + i; +#if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) + if ((kp == KYBER512_K) && make) { + ret = kyber_get_noise_eta1_c(prf, vec2, seed, KYBER_CBD_ETA3); + } + else +#endif + { + ret = kyber_get_noise_eta1_c(prf, vec2, seed, KYBER_CBD_ETA2); + } + + (void)make; + return ret; +} +#endif + /******************************************************************************/ #if !(defined(__aarch64__) && defined(WOLFSSL_ARMASM)) diff --git a/wolfssl/wolfcrypt/wc_kyber.h b/wolfssl/wolfcrypt/wc_kyber.h index 84f4a2e7c..4909f7b73 100644 --- a/wolfssl/wolfcrypt/wc_kyber.h +++ b/wolfssl/wolfcrypt/wc_kyber.h @@ -145,13 +145,27 @@ struct KyberKey { WOLFSSL_LOCAL void kyber_init(void); + +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM WOLFSSL_LOCAL void kyber_keygen(sword16* priv, sword16* pub, sword16* e, const sword16* a, int kp); +#else +WOLFSSL_LOCAL +int kyber_keygen_seeds(sword16* priv, sword16* pub, KYBER_PRF_T* prf, + sword16* e, int kp, byte* seed, byte* noiseSeed); +#endif +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM WOLFSSL_LOCAL void kyber_encapsulate(const sword16* pub, sword16* bp, sword16* v, const sword16* at, sword16* sp, const sword16* ep, const sword16* epp, const sword16* m, int kp); +#else +WOLFSSL_LOCAL +int kyber_encapsulate_seeds(const sword16* pub, KYBER_PRF_T* prf, sword16* bp, + sword16* tp, sword16* sp, int kp, const byte* msg, byte* seed, + byte* coins); +#endif WOLFSSL_LOCAL void kyber_decapsulate(const sword16* priv, sword16* mp, sword16* bp, const sword16* v, int kp);