diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c new file mode 100644 index 000000000..b76f75e53 --- /dev/null +++ b/wolfcrypt/src/wc_mlkem.c @@ -0,0 +1,2107 @@ +/* wc_mlkem.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Implementation based on FIPS 203: + * https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf + * + * Original implementation based on NIST 3rd Round submission package. + * See link at: + * 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. + * + * WOLFSSL_KYBER_NO_MAKE_KEY Default: OFF + * Disable the make key or key generation API. + * Reduces the code size. + * Turn on when only doing encapsulation. + * + * WOLFSSL_KYBER_NO_ENCAPSULATE Default: OFF + * Disable the encapsulation API. + * Reduces the code size. + * Turn on when doing make key/decapsulation. + * + * WOLFSSL_KYBER_NO_DECAPSULATE Default: OFF + * Disable the decapsulation API. + * Reduces the code size. + * Turn on when only doing encapsulation. + * + * WOLFSSL_MLKEM_CACHE_A Default: OFF + * Stores the matrix A during key generation for use in encapsulation when + * performing decapsulation. + * KyberKey is 8KB larger but decapsulation is significantly faster. + * Turn on when performing make key and decapsualtion with same object. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #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 +#if defined(WOLFSSL_MLKEM_CACHE_A) + #if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ + defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) + #error "Can't cache A with small memory code" + #endif +#endif + +#if defined(WOLFSSL_KYBER_NO_MAKE_KEY) && \ + defined(WOLFSSL_KYBER_NO_ENCAPSULATE) && \ + defined(WOLFSSL_KYBER_NO_DECAPSULATE) + #error "No ML-KEM operations to be built." +#endif + +#ifdef WOLFSSL_WC_MLKEM + +/******************************************************************************/ + +/* Use SHA3-256 to generate 32-bytes of hash. */ +#define KYBER_HASH_H kyber_hash256 +/* Use SHA3-512 to generate 64-bytes of hash. */ +#define KYBER_HASH_G kyber_hash512 +/* Use SHAKE-256 as a key derivation function (KDF). */ +#if defined(USE_INTEL_SPEEDUP) || \ + (defined(WOLFSSL_ARMASM) && defined(__aarch64__)) + #define KYBER_KDF kyber_kdf +#else + #define KYBER_KDF wc_Shake256Hash +#endif + +/******************************************************************************/ + +/* Declare variable to make compiler not optimize code in kyber_from_msg(). */ +volatile sword16 kyber_opt_blocker = 0; + +/******************************************************************************/ + +/** + * Initialize the Kyber key. + * + * @param [in] type Type of key: + * WC_ML_KEM_512, WC_ML_KEM_768, WC_ML_KEM_1024, + * KYBER512, KYBER768, KYBER1024. + * @param [out] key Kyber key object to initialize. + * @param [in] heap Dynamic memory hint. + * @param [in] devId Device Id. + * @return 0 on success. + * @return BAD_FUNC_ARG when key is NULL or type is unrecognized. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_MlKemKey_Init(MlKemKey* key, int type, void* heap, int devId) +{ + int ret = 0; + + /* Validate key. */ + if (key == NULL) { + ret = BAD_FUNC_ARG; + } + if (ret == 0) { + /* Validate type. */ + switch (type) { + #ifndef WOLFSSL_NO_ML_KEM + case WC_ML_KEM_512: + #ifndef WOLFSSL_WC_ML_KEM_512 + /* Code not compiled in for Kyber-512. */ + ret = NOT_COMPILED_IN; + #endif + break; + case WC_ML_KEM_768: + #ifndef WOLFSSL_WC_ML_KEM_768 + /* Code not compiled in for Kyber-768. */ + ret = NOT_COMPILED_IN; + #endif + break; + case WC_ML_KEM_1024: + #ifndef WOLFSSL_WC_ML_KEM_1024 + /* Code not compiled in for Kyber-1024. */ + ret = NOT_COMPILED_IN; + #endif + break; + #endif + #ifdef WOLFSSL_KYBER_ORIGINAL + case KYBER512: + #ifndef WOLFSSL_KYBER512 + /* Code not compiled in for Kyber-512. */ + ret = NOT_COMPILED_IN; + #endif + break; + case KYBER768: + #ifndef WOLFSSL_KYBER768 + /* Code not compiled in for Kyber-768. */ + ret = NOT_COMPILED_IN; + #endif + break; + case KYBER1024: + #ifndef WOLFSSL_KYBER1024 + /* Code not compiled in for Kyber-1024. */ + ret = NOT_COMPILED_IN; + #endif + break; + #endif + default: + /* No other values supported. */ + ret = BAD_FUNC_ARG; + break; + } + } + if (ret == 0) { + /* Keep type for parameters. */ + key->type = type; + /* Cache heap pointer. */ + key->heap = heap; + #ifdef WOLF_CRYPTO_CB + /* Cache device id - not used in for this algorithm yet. */ + key->devId = devId; + #endif + key->flags = 0; + + /* Zero out all data. */ + XMEMSET(&key->prf, 0, sizeof(key->prf)); + + /* Initialize the hash algorithm object. */ + ret = kyber_hash_new(&key->hash, heap, devId); + } + if (ret == 0) { + /* Initialize the PRF algorithm object. */ + ret = kyber_prf_new(&key->prf, heap, devId); + } + if (ret == 0) { + kyber_init(); + } + + (void)devId; + + return ret; +} + +/** + * Free the Kyber key object. + * + * @param [in, out] key Kyber key object to dispose of. + */ +void wc_MlKemKey_Free(MlKemKey* key) +{ + if (key != NULL) { + /* Dispose of PRF object. */ + kyber_prf_free(&key->prf); + /* Dispose of hash object. */ + kyber_hash_free(&key->hash); + /* Ensure all private data is zeroed. */ + ForceZero(&key->hash, sizeof(key->hash)); + ForceZero(&key->prf, sizeof(key->prf)); + ForceZero(key->priv, sizeof(key->priv)); + ForceZero(key->z, sizeof(key->z)); + } +} + +/******************************************************************************/ + +#ifndef WOLFSSL_KYBER_NO_MAKE_KEY +/** + * Make a Kyber key object using a random number generator. + * + * FIPS 203 - Algorithm 19: ML-KEM.KeyGen() + * Generates an encapsulation key and a corresponding decapsulation key. + * 1: d <- B_32 > d is 32 random bytes + * 2: z <- B_32 > z is 32 random bytes + * 3: if d == NULL or z == NULL then + * 4: return falsum + * > return an error indication if random bit generation failed + * 5: end if + * 6: (ek,dk) <- ML-KEM.KeyGen_Interal(d, z) + * > run internal key generation algorithm + * &: return (ek,dk) + * + * @param [in, out] key Kyber key object. + * @param [in] rng Random number generator. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or rng is NULL. + * @return MEMORY_E when dynamic memory allocation failed. + * @return MEMORY_E when dynamic memory allocation failed. + * @return RNG_FAILURE_E when generating random numbers failed. + * @return DRBG_CONT_FAILURE when random number generator health check fails. + */ +int wc_MlKemKey_MakeKey(MlKemKey* key, WC_RNG* rng) +{ + int ret = 0; + unsigned char rand[KYBER_MAKEKEY_RAND_SZ]; + + /* Validate parameters. */ + if ((key == NULL) || (rng == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Generate random to use with PRFs. + * Step 1: d is 32 random bytes + * Step 2: z is 32 random bytes + */ + ret = wc_RNG_GenerateBlock(rng, rand, KYBER_SYM_SZ * 2); + /* Step 3: ret is not zero when d == NULL or z == NULL. */ + } + if (ret == 0) { + /* Make a key pair from the random. + * Step 6. run internal key generation algorithm + * Step 7. public and private key are stored in key + */ + ret = wc_KyberKey_MakeKeyWithRandom(key, rand, sizeof(rand)); + } + + /* Ensure seeds are zeroized. */ + ForceZero((void*)rand, (word32)sizeof(rand)); + + /* Step 4: return ret != 0 on falsum or internal key generation failure. */ + return ret; +} + +/** + * Make a Kyber key object using random data. + * + * FIPS 203 - Algorithm 16: ML-KEM.KeyGen_internal(d,z) + * Uses randomness to generate an encapsulation key and a corresponding + * decapsulation key. + * 1: (ek_PKE,dk_PKE) < K-PKE.KeyGen(d) > run key generation for K-PKE + * ... + * + * FIPS 203 - Algorithm 13: K-PKE.KeyGen(d) + * Uses randomness to generate an encryption key and a corresponding decryption + * key. + * 1: (rho,sigma) <- G(d||k)A + * > expand 32+1 bytes to two pseudorandom 32-byte seeds + * 2: N <- 0 + * 3-7: generate matrix A_hat + * 8-11: generate s + * 12-15: generate e + * 16-18: calculate t_hat from A_hat, s and e + * ... + * + * @param [in, out] key Kyber key ovject. + * @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. + * @return NOT_COMPILED_IN when key type is not supported. + * @return MEMORY_E when dynamic memory allocation failed. + */ +int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, WC_RNG* rng, + const unsigned char* rand, int len) +{ + byte buf[2 * KYBER_SYM_SZ + 1]; + byte* rho = buf; + byte* sigma = buf + KYBER_SYM_SZ; +#ifndef WOLFSSL_NO_MALLOC + sword16* e = NULL; +#else +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM +#ifndef WOLFSSL_MLKEM_CACHE_A + sword16 e[(KYBER_MAX_K + 1) * KYBER_MAX_K * KYBER_N]; +#else + sword16 e[KYBER_MAX_K * KYBER_N]; +#endif +#else + sword16 e[KYBER_MAX_K * KYBER_N]; +#endif +#endif +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM + sword16* a = NULL; +#endif + sword16* s = NULL; + sword16* t = NULL; + int ret = 0; + int k = 0; + + /* Validate parameters. */ + if ((key == NULL) || (rand == NULL)) { + ret = BAD_FUNC_ARG; + } + if ((ret == 0) && (len != KYBER_MAKEKEY_RAND_SZ)) { + ret = BUFFER_E; + } + + if (ret == 0) { + key->flags = 0; + + /* Establish parameters based on key type. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + k = WC_ML_KEM_512_K; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + k = WC_ML_KEM_768_K; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + k = WC_ML_KEM_1024_K; + break; + #endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + #ifdef WOLFSSL_KYBER512 + case KYBER512: + k = KYBER512_K; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + k = KYBER768_K; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + k = KYBER1024_K; + break; + #endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } + +#ifndef WOLFSSL_NO_MALLOC + if (ret == 0) { + /* Allocate dynamic memory for matrix and error vector. */ +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM +#ifndef WOLFSSL_MLKEM_CACHE_A + /* e (v) | a (m) */ + e = (sword16*)XMALLOC((k + 1) * k * KYBER_N * sizeof(sword16), + key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#else + /* e (v) */ + e = (sword16*)XMALLOC(k * KYBER_N * sizeof(sword16), + key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif +#else + /* e (v) */ + e = (sword16*)XMALLOC(k * KYBER_N * sizeof(sword16), + key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + if (e == NULL) { + ret = MEMORY_E; + } + } +#endif + if (ret == 0) { + const byte* d = rand; + +#ifdef WOLFSSL_MLKEM_CACHE_A + a = key->a; +#elif !defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) + /* Matrix A allocated at end of error vector. */ + a = e + (k * KYBER_N); +#endif + +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + if (key->type & KYBER_ORIGINAL) +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + { + /* Expand 32 bytes of random to 32. */ + ret = KYBER_HASH_G(&key->hash, d, KYBER_SYM_SZ, NULL, 0, buf); + } +#endif +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + else +#endif +#ifndef WOLFSSL_NO_ML_KEM + { + buf[0] = k; + /* Expand 33 bytes of random to 32. + * Alg 13: Step 1: (rho,sigma) <- G(d||k) + */ + ret = KYBER_HASH_G(&key->hash, d, KYBER_SYM_SZ, buf, 1, buf); + } +#endif + } + if (ret == 0) { + const byte* z = rand + KYBER_SYM_SZ; + s = key->priv; + t = key->pub; + + /* Cache the public seed for use in encapsulation and encoding public + * key. */ + XMEMCPY(key->pubSeed, rho, KYBER_SYM_SZ); + /* Cache the z value for decapsulation and encoding private key. */ + XMEMCPY(key->z, z, sizeof(key->z)); + + /* Initialize PRF for use in noise generation. */ + kyber_prf_init(&key->prf); +#ifndef WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM + /* Generate noise using PRF. + * Alg 13: Steps 8-15: generate s and e + */ + ret = kyber_get_noise(&key->prf, k, s, e, NULL, sigma); + } + if (ret == 0) { + /* Generate the matrix A. + * Alg 13: Steps 3-7 + */ + ret = kyber_gen_matrix(&key->prf, a, k, rho, 0); + } + if (ret == 0) { + /* Generate key pair from random data. + * Alg 13: Steps 16-18. + */ + kyber_keygen(s, t, e, a, k); +#else + /* Generate noise using PRF. + * Alg 13: Steps 8-11: generate s + */ + ret = kyber_get_noise(&key->prf, k, s, NULL, NULL, sigma); + } + if (ret == 0) { + /* Generate key pair from private vector and seeds. + * Alg 13: Steps 3-7: generate matrix A_hat + * Alg 13: 12-15: generate e + * Alg 13: 16-18: calculate t_hat from A_hat, s and e + */ + ret = kyber_keygen_seeds(s, t, &key->prf, e, k, rho, sigma); + } + if (ret == 0) { +#endif + /* Private and public key are set/available. */ + key->flags |= KYBER_FLAG_PRIV_SET | KYBER_FLAG_PUB_SET; +#ifdef WOLFSSL_MLKEM_CACHE_A + key->flags |= KYBER_FLAG_A_SET; +#endif + } + +#ifndef WOLFSSL_NO_MALLOC + /* Free dynamic memory allocated in function. */ + if (key != NULL) { + XFREE(e, key->heap, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif + + return ret; +} +#endif /* !WOLFSSL_KYBER_NO_MAKE_KEY */ + +/******************************************************************************/ + +/** + * Get the size in bytes of cipher text for key. + * + * @param [in] key Kyber key object. + * @param [out] len Length of cipher text in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or len is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_MlKemKey_CipherTextSize(MlKemKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Return in 'len' size of the cipher text for the type of this key. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + *len = WC_ML_KEM_512_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + *len = WC_ML_KEM_768_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + *len = WC_ML_KEM_1024_CIPHER_TEXT_SIZE; + break; + #endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + #ifdef WOLFSSL_KYBER512 + case KYBER512: + *len = KYBER512_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + *len = KYBER768_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + *len = KYBER1024_CIPHER_TEXT_SIZE; + break; + #endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } + + return ret; +} + +/** + * Size of a shared secret in bytes. Always KYBER_SS_SZ. + * + * @param [in] key Kyber key object. Not used. + * @param [out] Size of the shared secret created with a Kyber key. + * @return 0 on success. + * @return 0 to indicate success. + */ +int wc_MlKemKey_SharedSecretSize(MlKemKey* key, word32* len) +{ + (void)key; + + *len = KYBER_SS_SZ; + + return 0; +} + +#if !defined(WOLFSSL_KYBER_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_KYBER_NO_DECAPSULATE) +/* Encapsulate data and derive secret. + * + * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE, m, r) + * Uses the encryption key to encrypt a plaintext message using the randomness + * r. + * 1: N <- 0 + * 2: t_hat <- ByteDecode_12(ek_PKE[0:384k]) + * > run ByteDecode_12 k times to decode t_hat + * 3: rho <- ek_PKE[384k : 384K + 32] + * > extract 32-byte seed from ek_PKE + * 4-8: generate matrix A_hat + * 9-12: generate y + * 13-16: generate e_1 + * 17: generate e_2 + * 18-19: calculate u + * 20: mu <- Decompress_1(ByteDecode_1(m)) + * 21: calculate v + * 22: c_1 <- ByteEncode_d_u(Compress_d_u(u)) + * > run ByteEncode_d_u and Compress_d_u k times + * 23: c_2 <- ByteEncode_d_v(Compress_d_v(v)) + * 24: return c <- (c_1||c_2) + * + * @param [in] key Kyber key object. + * @param [in] m Random bytes. + * @param [in] r Seed to feed to PRF when generating y, e1 and e2. + * @param [out] c Calculated cipher text. + * @return 0 on success. + * @return NOT_COMPILED_IN when key type is not supported. + */ +static int mlkemkey_encapsulate(MlKemKey* key, const byte* m, byte* r, byte* c) +{ + int ret = 0; + sword16* a = NULL; +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM + sword16* mu = NULL; + sword16* e1 = NULL; + sword16* e2 = NULL; +#endif + unsigned int k = 0; + unsigned int compVecSz = 0; +#ifndef WOLFSSL_NO_MALLOC + sword16* y = NULL; +#else +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM + sword16 y[((KYBER_MAX_K + 3) * KYBER_MAX_K + 3) * KYBER_N]; +#else + sword16 y[3 * KYBER_MAX_K * KYBER_N]; +#endif +#endif +#ifdef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM + sword16* u; + sword16* v; +#endif + + /* Establish parameters based on key type. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM +#ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + k = WC_ML_KEM_512_K; + compVecSz = WC_ML_KEM_512_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + k = WC_ML_KEM_768_K; + compVecSz = WC_ML_KEM_768_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + k = WC_ML_KEM_1024_K; + compVecSz = WC_ML_KEM_1024_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL +#ifdef WOLFSSL_KYBER512 + case KYBER512: + k = KYBER512_K; + compVecSz = KYBER512_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#ifdef WOLFSSL_KYBER768 + case KYBER768: + k = KYBER768_K; + compVecSz = KYBER768_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#ifdef WOLFSSL_KYBER1024 + case KYBER1024: + k = KYBER1024_K; + compVecSz = KYBER1024_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + +#ifndef WOLFSSL_NO_MALLOC + if (ret == 0) { + /* Allocate dynamic memory for all matrices, vectors and polynomials. */ +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM + y = (sword16*)XMALLOC(((k + 3) * k + 3) * KYBER_N * sizeof(sword16), + key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#else + y = (sword16*)XMALLOC(3 * k * KYBER_N * sizeof(sword16), key->heap, + DYNAMIC_TYPE_TMP_BUFFER); +#endif + if (y == NULL) { + ret = MEMORY_E; + } + } +#endif + + if (ret == 0) { +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM + /* Assign allocated dynamic memory to pointers. + * y (b) | a (m) | mu (p) | e1 (p) | e2 (v) | u (v) | v (p) */ + a = y + KYBER_N * k; + mu = a + KYBER_N * k * k; + e1 = mu + KYBER_N; + e2 = e1 + KYBER_N * k; +#else + /* Assign allocated dynamic memory to pointers. + * y (v) | a (v) | u (v) */ + a = y + KYBER_N * k; +#endif + +#ifndef WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM + /* Convert msg to a polynomial. + * Step 20: mu <- Decompress_1(ByteDecode_1(m)) */ + kyber_from_msg(mu, m); + + /* Initialize the PRF for use in the noise generation. */ + kyber_prf_init(&key->prf); + /* Generate noise using PRF. + * Steps 9-17: generate y, e_1, e_2 + */ + ret = kyber_get_noise(&key->prf, k, y, e1, e2, r); + } +#ifdef WOLFSSL_MLKEM_CACHE_A + if ((ret == 0) && ((key->flags & KYBER_FLAG_A_SET) != 0)) { + unsigned int i; + /* Transpose matrix. + * Steps 4-8: generate matrix A_hat (from original) */ + for (i = 0; i < k; i++) { + unsigned int j; + for (j = 0; j < k; j++) { + XMEMCPY(&a[(i * k + j) * KYBER_N], + &key->a[(j * k + i) * KYBER_N], + KYBER_N * 2); + } + } + } + else +#endif + if (ret == 0) { + /* Generate the transposed matrix. + * Step 4-8: generate matrix A_hat */ + ret = kyber_gen_matrix(&key->prf, a, k, key->pubSeed, 1); + } + if (ret == 0) { + sword16* u; + sword16* v; + + /* Assign remaining allocated dynamic memory to pointers. + * y (v) | a (m) | mu (p) | e1 (p) | r2 (v) | u (v) | v (p)*/ + u = e2 + KYBER_N; + v = u + KYBER_N * k; + + /* Perform encapsulation maths. + * Steps 18-19, 21: calculate u and v */ + kyber_encapsulate(key->pub, u, v, a, y, e1, e2, mu, k); +#else + /* Initialize the PRF for use in the noise generation. */ + kyber_prf_init(&key->prf); + /* Generate noise using PRF. + * Steps 9-12: generate y */ + ret = kyber_get_noise(&key->prf, k, y, NULL, NULL, r); + } + if (ret == 0) { + /* Assign remaining allocated dynamic memory to pointers. + * y (v) | at (v) | u (v) */ + u = a + KYBER_N * k; + v = a; + + /* Perform encapsulation maths. + * Steps 13-17: generate e_1 and e_2 + * Steps 18-19, 21: calculate u and v */ + ret = kyber_encapsulate_seeds(key->pub, &key->prf, u, a, y, k, m, + key->pubSeed, r); + } + if (ret == 0) { +#endif + byte* c1 = c; + byte* c2 = c + compVecSz; + + #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) + if (k == KYBER512_K) { + /* Step 22: c_1 <- ByteEncode_d_u(Compress_d_u(u)) */ + kyber_vec_compress_10(c1, u, k); + /* Step 23: c_2 <- ByteEncode_d_v(Compress_d_v(v)) */ + kyber_compress_4(c2, v); + /* Step 24: return c <- (c_1||c_2) */ + } + #endif + #if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768) + if (k == KYBER768_K) { + /* Step 22: c_1 <- ByteEncode_d_u(Compress_d_u(u)) */ + kyber_vec_compress_10(c1, u, k); + /* Step 23: c_2 <- ByteEncode_d_v(Compress_d_v(v)) */ + kyber_compress_4(c2, v); + /* Step 24: return c <- (c_1||c_2) */ + } + #endif + #if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024) + if (k == KYBER1024_K) { + /* Step 22: c_1 <- ByteEncode_d_u(Compress_d_u(u)) */ + kyber_vec_compress_11(c1, u); + /* Step 23: c_2 <- ByteEncode_d_v(Compress_d_v(v)) */ + kyber_compress_5(c2, v); + /* Step 24: return c <- (c_1||c_2) */ + } + #endif + } + +#ifndef WOLFSSL_NO_MALLOC + /* Dispose of dynamic memory allocated in function. */ + XFREE(y, key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return ret; +} +#endif + +#ifndef WOLFSSL_KYBER_NO_ENCAPSULATE +/** + * Encapsulate with random number generator and derive secret. + * + * FIPS 203, Algorithm 20: ML-KEM.Encaps(ek) + * Uses the encapsulation key to generate a shared secret key and an associated + * ciphertext. + * 1: m <- B_32 > m is 32 random bytes + * 2: if m == NULL then + * 3: return falsum + * 4: end if + * 5: (K,c) <- ML-KEM.Encaps_internal(ek,m) + * > run internal encapsulation algorithm + * 6: return (K,c) + * + * @param [in] key Kyber key object. + * @param [out] c Cipher text. + * @param [out] k Shared secret generated. + * @param [in] rng Random number generator. + * @return 0 on success. + * @return BAD_FUNC_ARG when key, ct, ss or RNG is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + * @return MEMORY_E when dynamic memory allocation failed. + */ +int wc_MlKemKey_Encapsulate(MlKemKey* key, unsigned char* c, unsigned char* k, + WC_RNG* rng) +{ + int ret = 0; + unsigned char m[KYBER_ENC_RAND_SZ]; + + /* Validate parameters. */ + if ((key == NULL) || (c == NULL) || (k == NULL) || (rng == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Generate seed for use with PRFs. + * Step 1: m is 32 random bytes + */ + ret = wc_RNG_GenerateBlock(rng, m, sizeof(m)); + /* Step 2: ret is not zero when m == NULL. */ + } + if (ret == 0) { + /* Encapsulate with the random. + * Step 5: run internal encapsulation algorithm + */ + ret = wc_KyberKey_EncapsulateWithRandom(key, c, k, m, sizeof(m)); + } + + /* Step 3: return ret != 0 on falsum or internal key generation failure. */ + return ret; +} + +/** + * Encapsulate with random data and derive secret. + * + * FIPS 203, Algorithm 17: ML-KEM.Encaps_internal(ek, m) + * Uses the encapsulation key and randomness to generate a key and an associated + * ciphertext. + * Step 1: (K,r) <- G(m||H(ek)) + * > derive shared secret key K and randomness r + * Step 2: c <- K-PPKE.Encrypt(ek, m, r) + * > encrypt m using K-PKE with randomness r + * Step 3: return (K,c) + * + * @param [out] c Cipher text. + * @param [out] k Shared secret generated. + * @param [in] m Random bytes. + * @param [in] len Length of random bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key, c, k or RNG is NULL. + * @return BUFFER_E when len is not KYBER_ENC_RAND_SZ. + * @return NOT_COMPILED_IN when key type is not supported. + * @return MEMORY_E when dynamic memory allocation failed. + */ +int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* c, + unsigned char* k, const unsigned char* m, int len) +{ +#ifdef WOLFSSL_KYBER_ORIGINAL + byte msg[KYBER_SYM_SZ]; +#endif + byte kr[2 * KYBER_SYM_SZ + 1]; + int ret = 0; +#ifdef WOLFSSL_KYBER_ORIGINAL + unsigned int cSz = 0; +#endif + + /* Validate parameters. */ + if ((key == NULL) || (c == NULL) || (k == NULL) || (m == NULL)) { + ret = BAD_FUNC_ARG; + } + if ((ret == 0) && (len != KYBER_ENC_RAND_SZ)) { + ret = BUFFER_E; + } + +#ifdef WOLFSSL_KYBER_ORIGINAL + if (ret == 0) { + /* Establish parameters based on key type. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + #endif + break; +#endif + #ifdef WOLFSSL_KYBER512 + case KYBER512: + cSz = KYBER512_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + cSz = KYBER768_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + cSz = KYBER1024_CIPHER_TEXT_SIZE; + break; + #endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } +#endif + + /* If public hash (h) is not stored against key, calculate it + * (fields set explicitly instead of using decode). + * Step 1: ... H(ek)... + */ + if ((ret == 0) && ((key->flags & KYBER_FLAG_H_SET) == 0)) { + #ifndef WOLFSSL_NO_MALLOC + byte* pubKey = NULL; + word32 pubKeyLen; + #else + byte pubKey[KYBER_MAX_PUBLIC_KEY_SIZE]; + word32 pubKeyLen = KYBER_MAX_PUBLIC_KEY_SIZE; + #endif + + #ifndef WOLFSSL_NO_MALLOC + /* Determine how big an encoded public key will be. */ + ret = wc_KyberKey_PublicKeySize(key, &pubKeyLen); + if (ret == 0) { + /* Allocate dynamic memory for encoded public key. */ + pubKey = (byte*)XMALLOC(pubKeyLen, key->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (pubKey == NULL) { + ret = MEMORY_E; + } + } + if (ret == 0) { + #endif + /* Encode public key - h is hash of encoded public key. */ + ret = wc_KyberKey_EncodePublicKey(key, pubKey, pubKeyLen); + #ifndef WOLFSSL_NO_MALLOC + } + /* Dispose of encoded public key. */ + XFREE(pubKey, key->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + } + if ((ret == 0) && ((key->flags & KYBER_FLAG_H_SET) == 0)) { + /* Implementation issue if h not cached and flag set. */ + ret = BAD_STATE_E; + } + +#ifdef WOLFSSL_KYBER_ORIGINAL + if (ret == 0) { +#ifndef WOLFSSL_NO_ML_KEM + if (key->type & KYBER_ORIGINAL) +#endif + { + /* Hash random to anonymize as seed data. */ + ret = KYBER_HASH_H(&key->hash, m, KYBER_SYM_SZ, msg); + } + } +#endif + if (ret == 0) { + /* Hash message into seed buffer. */ +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + if (key->type & KYBER_ORIGINAL) +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + { + ret = KYBER_HASH_G(&key->hash, msg, KYBER_SYM_SZ, key->h, + KYBER_SYM_SZ, kr); + } +#endif +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + else +#endif +#ifndef WOLFSSL_NO_ML_KEM + { + /* Step 1: (K,r) <- G(m||H(ek)) */ + ret = KYBER_HASH_G(&key->hash, m, KYBER_SYM_SZ, key->h, + KYBER_SYM_SZ, kr); + } +#endif + } + + if (ret == 0) { + /* Encapsulate the message using the key and the seed. */ +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + if (key->type & KYBER_ORIGINAL) +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + { + ret = mlkemkey_encapsulate(key, msg, kr + KYBER_SYM_SZ, c); + } +#endif +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + else +#endif +#ifndef WOLFSSL_NO_ML_KEM + { + /* Step 2: c <- K-PKE.Encrypt(ek,m,r) */ + ret = mlkemkey_encapsulate(key, m, kr + KYBER_SYM_SZ, c); + } +#endif + } + +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + if (key->type & KYBER_ORIGINAL) +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + { + if (ret == 0) { + /* Hash the cipher text after the seed. */ + ret = KYBER_HASH_H(&key->hash, c, cSz, kr + KYBER_SYM_SZ); + } + if (ret == 0) { + /* Derive the secret from the seed and hash of cipher text. */ + ret = KYBER_KDF(kr, 2 * KYBER_SYM_SZ, k, KYBER_SS_SZ); + } + } +#endif +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + else +#endif +#ifndef WOLFSSL_NO_ML_KEM + { + if (ret == 0) { + /* return (K,c) */ + XMEMCPY(k, kr, KYBER_SS_SZ); + } + } +#endif + + return ret; +} +#endif /* !WOLFSSL_KYBER_NO_ENCAPSULATE */ + +/******************************************************************************/ + +#ifndef WOLFSSL_KYBER_NO_DECAPSULATE +/* Decapsulate cipher text to the message using key. + * + * FIPS 203, Algorithm 15: K-PKE.Decrypt(dk_PKE,c) + * Uses the decryption key to decrypt a ciphertext. + * 1: c1 <- c[0 : 32.d_u.k] + * 2: c2 <= c[32.d_u.k : 32(d_u.k + d_v)] + * 3: u' <= Decompress_d_u(ByteDecode_d_u(c1)) + * 4: v' <= Decompress_d_v(ByteDecode_d_v(c2)) + * ... + * 6: w <- v' - InvNTT(s_hat_trans o NTT(u')) + * 7: m <- ByteEncode_1(Compress_1(w)) + * 8: return m + * + * @param [in] key Kyber key object. + * @param [out] m Message than was encapsulated. + * @param [in] c Cipher text. + * @return 0 on success. + * @return NOT_COMPILED_IN when key type is not supported. + * @return MEMORY_E when dynamic memory allocation failed. + */ +static KYBER_NOINLINE int mlkemkey_decapsulate(MlKemKey* key, byte* m, + const byte* c) +{ + int ret = 0; + sword16* v; + sword16* w; + unsigned int k = 0; + unsigned int compVecSz; +#if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) + sword16* u = NULL; +#else + sword16 u[(KYBER_MAX_K + 1) * KYBER_N]; +#endif + + /* Establish parameters based on key type. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM +#ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + k = WC_ML_KEM_512_K; + compVecSz = WC_ML_KEM_512_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + k = WC_ML_KEM_768_K; + compVecSz = WC_ML_KEM_768_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + k = WC_ML_KEM_1024_K; + compVecSz = WC_ML_KEM_1024_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL +#ifdef WOLFSSL_KYBER512 + case KYBER512: + k = KYBER512_K; + compVecSz = KYBER512_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#ifdef WOLFSSL_KYBER768 + case KYBER768: + k = KYBER768_K; + compVecSz = KYBER768_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#ifdef WOLFSSL_KYBER1024 + case KYBER1024: + k = KYBER1024_K; + compVecSz = KYBER1024_POLY_VEC_COMPRESSED_SZ; + break; +#endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + +#if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) + if (ret == 0) { + /* Allocate dynamic memory for a vector and a polynomial. */ + u = (sword16*)XMALLOC((k + 1) * KYBER_N * sizeof(sword16), key->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (u == NULL) { + ret = MEMORY_E; + } + } +#endif + if (ret == 0) { + /* Step 1: c1 <- c[0 : 32.d_u.k] */ + const byte* c1 = c; + /* Step 2: c2 <= c[32.d_u.k : 32(d_u.k + d_v)] */ + const byte* c2 = c + compVecSz; + + /* Assign allocated dynamic memory to pointers. + * u (v) | v (p) */ + v = u + k * KYBER_N; + w = u; + + #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) + if (k == KYBER512_K) { + /* Step 3: u' <= Decompress_d_u(ByteDecode_d_u(c1)) */ + kyber_vec_decompress_10(u, c1, k); + /* Step 4: v' <= Decompress_d_v(ByteDecode_d_v(c2)) */ + kyber_decompress_4(v, c2); + } + #endif + #if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768) + if (k == KYBER768_K) { + /* Step 3: u' <= Decompress_d_u(ByteDecode_d_u(c1)) */ + kyber_vec_decompress_10(u, c1, k); + /* Step 4: v' <= Decompress_d_v(ByteDecode_d_v(c2)) */ + kyber_decompress_4(v, c2); + } + #endif + #if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024) + if (k == KYBER1024_K) { + /* Step 3: u' <= Decompress_d_u(ByteDecode_d_u(c1)) */ + kyber_vec_decompress_11(u, c1); + /* Step 4: v' <= Decompress_d_v(ByteDecode_d_v(c2)) */ + kyber_decompress_5(v, c2); + } + #endif + + /* Decapsulate the cipher text into polynomial. + * Step 6: w <- v' - InvNTT(s_hat_trans o NTT(u')) */ + kyber_decapsulate(key->priv, w, u, v, k); + + /* Convert the polynomial into a array of bytes (message). + * Step 7: m <- ByteEncode_1(Compress_1(w)) */ + kyber_to_msg(m, w); + /* Step 8: return m */ + } + +#if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) + /* Dispose of dynamically memory allocated in function. */ + XFREE(u, key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return ret; +} + +#ifndef WOLFSSL_NO_ML_KEM +/* Derive the secret from z and cipher text. + * + * @param [in] z Implicit rejection value. + * @param [in] ct Cipher text. + * @param [in] ctSz Length of cipher text in bytes. + * @param [out] ss Shared secret. + * @return 0 on success. + * @return MEMORY_E when dynamic memory allocation failed. + * @return Other negative when a hash error occurred. + */ +static int mlkem_derive_secret(const byte* z, const byte* ct, word32 ctSz, + byte* ss) +{ + int ret; + wc_Shake shake; + + ret = wc_InitShake256(&shake, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Shake256_Update(&shake, z, KYBER_SYM_SZ); + if (ret == 0) { + ret = wc_Shake256_Update(&shake, ct, ctSz); + } + if (ret == 0) { + ret = wc_Shake256_Final(&shake, ss, KYBER_SS_SZ); + } + wc_Shake256_Free(&shake); + } + + return ret; +} +#endif + +/** + * Decapsulate the cipher text to calculate the shared secret. + * + * Validates the cipher text by encapsulating and comparing with data passed in. + * + * FIPS 203, Algorithm 21: ML-KEM.Decaps(dk, c) + * Uses the decapsulation key to produce a shared secret key from a ciphertext. + * 1: K' <- ML-KEM.Decaps_internal(dk,c) + * > run internal decapsulation algorithm + * 2: return K' + * + * FIPS 203, Algorithm 18: ML-KEM.Decaps_internal(dk, c) + * Uses the decapsulation key to produce a shared secret key from a ciphertext. + * ... + * 1: dk_PKE <- dk[0 : 384k] + * > extract (from KEM decaps key) the PKE decryption key + * 2: ek_PKE <- dk[384k : 768l + 32] + * > extract PKE encryption key + * 3: h <- dk[768K + 32 : 768k + 64] + * > extract hash of PKE encryption key + * 4: z <- dk[768K + 64 : 768k + 96] + * > extract implicit rejection value + * 5: m' <- K-PKE.Decrypt(dk_PKE, c) > decrypt ciphertext + * 6: (K', r') <- G(m'||h) + * 7: K_bar <- J(z||c) + * 8: c' <- K-PKE.Encrypt(ek_PKE, m', r') + * > re-encrypt using the derived randomness r' + * 9: if c != c' then + * 10: K' <= K_bar + * > if ciphertexts do not match, "implicitly reject" + * 11: end if + * 12: return K' + * + * @param [in] key Kyber key object. + * @param [out] ss Shared secret. + * @param [in] ct Cipher text. + * @param [in] len Length of cipher text. + * @return 0 on success. + * @return BAD_FUNC_ARG when key, ss or cr are NULL. + * @return NOT_COMPILED_IN when key type is not supported. + * @return BUFFER_E when len is not the length of cipher text for the key type. + * @return MEMORY_E when dynamic memory allocation failed. + */ +int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, + const unsigned char* ct, word32 len) +{ + byte msg[KYBER_SYM_SZ]; + byte kr[2 * KYBER_SYM_SZ + 1]; + int ret = 0; + unsigned int ctSz = 0; + unsigned int i = 0; + int fail = 0; +#if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) + byte* cmp = NULL; +#else + byte cmp[KYBER_MAX_CIPHER_TEXT_SIZE]; +#endif + + /* Validate parameters. */ + if ((key == NULL) || (ss == NULL) || (ct == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Establish cipher text size based on key type. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + ctSz = WC_ML_KEM_512_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + ctSz = WC_ML_KEM_768_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + ctSz = WC_ML_KEM_1024_CIPHER_TEXT_SIZE; + break; + #endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + #ifdef WOLFSSL_KYBER512 + case KYBER512: + ctSz = KYBER512_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + ctSz = KYBER768_CIPHER_TEXT_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + ctSz = KYBER1024_CIPHER_TEXT_SIZE; + break; + #endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } + + /* Ensure the cipher text passed in is the correct size. */ + if ((ret == 0) && (len != ctSz)) { + ret = BUFFER_E; + } + +#if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) + if (ret == 0) { + /* Allocate memory for cipher text that is generated. */ + cmp = (byte*)XMALLOC(ctSz, key->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (cmp == NULL) { + ret = MEMORY_E; + } + } +#endif + + if (ret == 0) { + /* Decapsulate the cipher text. */ + ret = mlkemkey_decapsulate(key, msg, ct); + } + if (ret == 0) { + /* Hash message into seed buffer. */ + ret = KYBER_HASH_G(&key->hash, msg, KYBER_SYM_SZ, key->h, KYBER_SYM_SZ, + kr); + } + if (ret == 0) { + /* Encapsulate the message. */ + ret = mlkemkey_encapsulate(key, msg, kr + KYBER_SYM_SZ, cmp); + } + if (ret == 0) { + /* Compare generated cipher text with that passed in. */ + fail = kyber_cmp(ct, cmp, ctSz); + +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + if (key->type & KYBER_ORIGINAL) +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + { + /* Hash the cipher text after the seed. */ + ret = KYBER_HASH_H(&key->hash, ct, ctSz, kr + KYBER_SYM_SZ); + if (ret == 0) { + /* Change seed to z on comparison failure. */ + for (i = 0; i < KYBER_SYM_SZ; i++) { + kr[i] ^= (kr[i] ^ key->z[i]) & fail; + } + + /* Derive the secret from the seed and hash of cipher text. */ + ret = KYBER_KDF(kr, 2 * KYBER_SYM_SZ, ss, KYBER_SS_SZ); + } + } +#endif +#if defined(WOLFSSL_KYBER_ORIGINAL) && !defined(WOLFSSL_NO_ML_KEM) + else +#endif +#ifndef WOLFSSL_NO_ML_KEM + { + ret = mlkem_derive_secret(key->z, ct, ctSz, msg); + if (ret == 0) { + /* Set secret to kr or fake secret on comparison failure. */ + for (i = 0; i < KYBER_SYM_SZ; i++) { + ss[i] = kr[i] ^ ((kr[i] ^ msg[i]) & fail); + } + } + } +#endif + } + +#if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) + /* Dispose of dynamic memory allocated in function. */ + if (key != NULL) { + XFREE(cmp, key->heap, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif + + return ret; +} +#endif /* WOLFSSL_KYBER_NO_DECAPSULATE */ + +/******************************************************************************/ + +/** + * Get the public key and public seed from bytes. + * + * FIPS 203, Algorithm 14 K-PKE.Encrypt(ek_PKE, m, r) + * ... + * 2: t <- ByteDecode_12(ek_PKE[0 : 384k]) + * 3: rho <- ek_PKE[384k : 384k + 32] + * ... + * + * @param [out] pub Public key - vector. + * @param [out] pubSeed Public seed. + * @param [in] p Public key data. + * @param [in] k Number of polynomials in vector. + */ +static void mlkemkey_decode_public(sword16* pub, byte* pubSeed, const byte* p, + unsigned int k) +{ + unsigned int i; + + /* Decode public key that is vector of polynomials. + * Step 2: t <- ByteDecode_12(ek_PKE[0 : 384k]) */ + kyber_from_bytes(pub, p, k); + p += k * KYBER_POLY_SIZE; + + /* Read public key seed. + * Step 3: rho <- ek_PKE[384k : 384k + 32] */ + for (i = 0; i < KYBER_SYM_SZ; i++) { + pubSeed[i] = p[i]; + } +} + +/** + * Decode the private key. + * + * Private Vector | Public Key | Public Hash | Randomizer + * + * FIPS 203, Algorithm 18: ML-KEM.Decaps_internal(dk, c) + * 1: dk_PKE <- dk[0 : 384k] + * > extract (from KEM decaps key) the PKE decryption key + * 2: ek_PKE <- dk[384k : 768l + 32] + * > extract PKE encryption key + * 3: h <- dk[768K + 32 : 768k + 64] + * > extract hash of PKE encryption key + * 4: z <- dk[768K + 64 : 768k + 96] + * > extract implicit rejection value + * + * FIPS 203, Algorithm 15: K-PKE.Decrypt(dk_PKE, c) + * ... + * 5: s_hat <= ByteDecode_12(dk_PKE) + * ... + * + * @param [in, out] key Kyber key object. + * @param [in] in Buffer holding encoded key. + * @param [in] len Length of data in buffer. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or in is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + * @return BUFFER_E when len is not the correct size. + */ +int wc_MlKemKey_DecodePrivateKey(MlKemKey* key, const unsigned char* in, + word32 len) +{ + int ret = 0; + word32 privLen = 0; + word32 pubLen = 0; + unsigned int k = 0; + const unsigned char* p = in; + + /* Validate parameters. */ + if ((key == NULL) || (in == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Establish parameters based on key type. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + k = WC_ML_KEM_512_K; + privLen = WC_ML_KEM_512_PRIVATE_KEY_SIZE; + pubLen = WC_ML_KEM_512_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + k = WC_ML_KEM_768_K; + privLen = WC_ML_KEM_768_PRIVATE_KEY_SIZE; + pubLen = WC_ML_KEM_768_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + k = WC_ML_KEM_1024_K; + privLen = WC_ML_KEM_1024_PRIVATE_KEY_SIZE; + pubLen = WC_ML_KEM_1024_PUBLIC_KEY_SIZE; + break; + #endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + #ifdef WOLFSSL_KYBER512 + case KYBER512: + k = KYBER512_K; + privLen = KYBER512_PRIVATE_KEY_SIZE; + pubLen = KYBER512_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + k = KYBER768_K; + privLen = KYBER768_PRIVATE_KEY_SIZE; + pubLen = KYBER768_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + k = KYBER1024_K; + privLen = KYBER1024_PRIVATE_KEY_SIZE; + pubLen = KYBER1024_PUBLIC_KEY_SIZE; + break; + #endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } + /* Ensure the data is the correct length for the key type. */ + if ((ret == 0) && (len != privLen)) { + ret = BUFFER_E; + } + + if (ret == 0) { + /* Decode private key that is vector of polynomials. + * Alg 18 Step 1: dk_PKE <- dk[0 : 384k] + * Alg 15 Step 5: s_hat <- ByteDecode_12(dk_PKE) */ + kyber_from_bytes(key->priv, p, k); + p += k * KYBER_POLY_SIZE; + + /* Decode the public key that is after the private key. */ + mlkemkey_decode_public(key->pub, key->pubSeed, p, k); + p += pubLen; + + /* Copy the hash of the encoded public key that is after public key. */ + XMEMCPY(key->h, p, sizeof(key->h)); + p += KYBER_SYM_SZ; + /* Copy the z (randomizer) that is after hash. */ + XMEMCPY(key->z, p, sizeof(key->z)); + + /* Set flags */ + key->flags |= KYBER_FLAG_H_SET | KYBER_FLAG_BOTH_SET; + } + + return ret; +} + +/** + * Decode public key. + * + * Public vector | Public Seed + * + * @param [in, out] key Kyber key object. + * @param [in] in Buffer holding encoded key. + * @param [in] len Length of data in buffer. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or in is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + * @return BUFFER_E when len is not the correct size. + */ +int wc_MlKemKey_DecodePublicKey(MlKemKey* key, const unsigned char* in, + word32 len) +{ + int ret = 0; + word32 pubLen = 0; + unsigned int k = 0; + const unsigned char* p = in; + + if ((key == NULL) || (in == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Establish parameters based on key type. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + k = WC_ML_KEM_512_K; + pubLen = WC_ML_KEM_512_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + k = WC_ML_KEM_768_K; + pubLen = WC_ML_KEM_768_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + k = WC_ML_KEM_1024_K; + pubLen = WC_ML_KEM_1024_PUBLIC_KEY_SIZE; + break; + #endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + #ifdef WOLFSSL_KYBER512 + case KYBER512: + k = KYBER512_K; + pubLen = KYBER512_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + k = KYBER768_K; + pubLen = KYBER768_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + k = KYBER1024_K; + pubLen = KYBER1024_PUBLIC_KEY_SIZE; + break; + #endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } + /* Ensure the data is the correct length for the key type. */ + if ((ret == 0) && (len != pubLen)) { + ret = BUFFER_E; + } + + if (ret == 0) { + mlkemkey_decode_public(key->pub, key->pubSeed, p, k); + + /* Calculate public hash. */ + ret = KYBER_HASH_H(&key->hash, in, len, key->h); + } + if (ret == 0) { + /* Record public key and public hash set. */ + key->flags |= KYBER_FLAG_PUB_SET | KYBER_FLAG_H_SET; + } + + return ret; +} + +/** + * Get the size in bytes of encoded private key for the key. + * + * @param [in] key Kyber key object. + * @param [out] len Length of encoded private key in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or len is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_MlKemKey_PrivateKeySize(MlKemKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Return in 'len' size of the encoded private key for the type of this + * key. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + *len = WC_ML_KEM_512_PRIVATE_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + *len = WC_ML_KEM_768_PRIVATE_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + *len = WC_ML_KEM_1024_PRIVATE_KEY_SIZE; + break; + #endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + #ifdef WOLFSSL_KYBER512 + case KYBER512: + *len = KYBER512_PRIVATE_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + *len = KYBER768_PRIVATE_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + *len = KYBER1024_PRIVATE_KEY_SIZE; + break; + #endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } + + return ret; +} + +/** + * Get the size in bytes of encoded public key for the key. + * + * @param [in] key Kyber key object. + * @param [out] len Length of encoded public key in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or len is NULL. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_MlKemKey_PublicKeySize(MlKemKey* key, word32* len) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (len == NULL)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + /* Return in 'len' size of the encoded public key for the type of this + * key. */ + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + *len = WC_ML_KEM_512_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + *len = WC_ML_KEM_768_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + *len = WC_ML_KEM_1024_PUBLIC_KEY_SIZE; + break; + #endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + #ifdef WOLFSSL_KYBER512 + case KYBER512: + *len = KYBER512_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + *len = KYBER768_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + *len = KYBER1024_PUBLIC_KEY_SIZE; + break; + #endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } + + return ret; +} + +/** + * Encode the private key. + * + * Private Vector | Public Key | Public Hash | Randomizer + * + * FIPS 203, Algorithm 16: ML-KEM.KeyGen_internal(d,z) + * ... + * 3: dk <- (dk_PKE||ek||H(ek)||z) + * ... + * FIPS 203, Algorithm 13: K-PKE.KeyGen(d) + * ... + * 20: dk_PKE <- ByteEncode_12(s_hat) + * ... + * + * @param [in] key Kyber key object. + * @param [out] out Buffer to hold data. + * @param [in] len Size of buffer in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or out is NULL or private/public key not + * available. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_MlKemKey_EncodePrivateKey(MlKemKey* key, unsigned char* out, word32 len) +{ + int ret = 0; + unsigned int k = 0; + unsigned int pubLen = 0; + unsigned int privLen = 0; + unsigned char* p = out; + + if ((key == NULL) || (out == NULL)) { + ret = BAD_FUNC_ARG; + } + if ((ret == 0) && + ((key->flags & KYBER_FLAG_BOTH_SET) != KYBER_FLAG_BOTH_SET)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + k = WC_ML_KEM_512_K; + pubLen = WC_ML_KEM_512_PUBLIC_KEY_SIZE; + privLen = WC_ML_KEM_512_PRIVATE_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + k = WC_ML_KEM_768_K; + pubLen = WC_ML_KEM_768_PUBLIC_KEY_SIZE; + privLen = WC_ML_KEM_768_PRIVATE_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + k = WC_ML_KEM_1024_K; + pubLen = WC_ML_KEM_1024_PUBLIC_KEY_SIZE; + privLen = WC_ML_KEM_1024_PRIVATE_KEY_SIZE; + break; + #endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + #ifdef WOLFSSL_KYBER512 + case KYBER512: + k = KYBER512_K; + pubLen = KYBER512_PUBLIC_KEY_SIZE; + privLen = KYBER512_PRIVATE_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + k = KYBER768_K; + pubLen = KYBER768_PUBLIC_KEY_SIZE; + privLen = KYBER768_PRIVATE_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + k = KYBER1024_K; + pubLen = KYBER1024_PUBLIC_KEY_SIZE; + privLen = KYBER1024_PRIVATE_KEY_SIZE; + break; + #endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } + /* Check buffer is big enough for encoding. */ + if ((ret == 0) && (len != privLen)) { + ret = BUFFER_E; + } + + if (ret == 0) { + /* Encode private key that is vector of polynomials. */ + kyber_to_bytes(p, key->priv, k); + p += KYBER_POLY_SIZE * k; + + /* Encode public key. */ + ret = wc_KyberKey_EncodePublicKey(key, p, pubLen); + p += pubLen; + } + /* Ensure hash of public key is available. */ + if ((ret == 0) && ((key->flags & KYBER_FLAG_H_SET) == 0)) { + ret = KYBER_HASH_H(&key->hash, p - pubLen, pubLen, key->h); + } + if (ret == 0) { + /* Public hash is available. */ + key->flags |= KYBER_FLAG_H_SET; + /* Append public hash. */ + XMEMCPY(p, key->h, sizeof(key->h)); + p += KYBER_SYM_SZ; + /* Append z (randomizer). */ + XMEMCPY(p, key->z, sizeof(key->z)); + } + + return ret; +} + +/** + * Encode the public key. + * + * Public vector | Public Seed + * + * FIPS 203, Algorithm 16: ML-KEM.KeyGen_internal(d,z) + * ... + * 2: ek <- ek_PKE + * ... + * FIPS 203, Algorithm 13: K-PKE.KeyGen(d) + * ... + * 19: ek_PKE <- ByteEncode_12(t_hat)||rho + * ... + * + * @param [in] key Kyber key object. + * @param [out] out Buffer to hold data. + * @param [in] len Size of buffer in bytes. + * @return 0 on success. + * @return BAD_FUNC_ARG when key or out is NULL or public key not available. + * @return NOT_COMPILED_IN when key type is not supported. + */ +int wc_MlKemKey_EncodePublicKey(MlKemKey* key, unsigned char* out, word32 len) +{ + int ret = 0; + unsigned int k = 0; + unsigned int pubLen = 0; + unsigned char* p = out; + + if ((key == NULL) || (out == NULL)) { + ret = BAD_FUNC_ARG; + } + if ((ret == 0) && + ((key->flags & KYBER_FLAG_PUB_SET) != KYBER_FLAG_PUB_SET)) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + switch (key->type) { +#ifndef WOLFSSL_NO_ML_KEM + #ifdef WOLFSSL_WC_ML_KEM_512 + case WC_ML_KEM_512: + k = WC_ML_KEM_512_K; + pubLen = WC_ML_KEM_512_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_768 + case WC_ML_KEM_768: + k = WC_ML_KEM_768_K; + pubLen = WC_ML_KEM_768_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_WC_ML_KEM_1024 + case WC_ML_KEM_1024: + k = WC_ML_KEM_1024_K; + pubLen = WC_ML_KEM_1024_PUBLIC_KEY_SIZE; + break; + #endif +#endif +#ifdef WOLFSSL_KYBER_ORIGINAL + #ifdef WOLFSSL_KYBER512 + case KYBER512: + k = KYBER512_K; + pubLen = KYBER512_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER768 + case KYBER768: + k = KYBER768_K; + pubLen = KYBER768_PUBLIC_KEY_SIZE; + break; + #endif + #ifdef WOLFSSL_KYBER1024 + case KYBER1024: + k = KYBER1024_K; + pubLen = KYBER1024_PUBLIC_KEY_SIZE; + break; + #endif +#endif + default: + /* No other values supported. */ + ret = NOT_COMPILED_IN; + break; + } + } + /* Check buffer is big enough for encoding. */ + if ((ret == 0) && (len != pubLen)) { + ret = BUFFER_E; + } + + if (ret == 0) { + int i; + + /* Encode public key polynomial by polynomial. */ + kyber_to_bytes(p, key->pub, k); + p += k * KYBER_POLY_SIZE; + + /* Append public seed. */ + for (i = 0; i < KYBER_SYM_SZ; i++) { + p[i] = key->pubSeed[i]; + } + + /* Make sure public hash is set. */ + if ((key->flags & KYBER_FLAG_H_SET) == 0) { + ret = KYBER_HASH_H(&key->hash, out, len, key->h); + } + } + if (ret == 0) { + /* Public hash is set. */ + key->flags |= KYBER_FLAG_H_SET; + } + + return ret; +} + +#endif /* WOLFSSL_WC_MLKEM */ diff --git a/wolfssl/wolfcrypt/wc_kyber.h b/wolfssl/wolfcrypt/wc_kyber.h index dab942da3..96d8fa23c 100644 --- a/wolfssl/wolfcrypt/wc_kyber.h +++ b/wolfssl/wolfcrypt/wc_kyber.h @@ -1,4 +1,4 @@ -/* wc_kyber.h +/* wc_mlkem.h * * Copyright (C) 2006-2025 wolfSSL Inc. * @@ -20,12 +20,12 @@ */ /*! - \file wolfssl/wolfcrypt/wc_kyber.h + \file wolfssl/wolfcrypt/wc_mlkem.h */ -#ifndef WOLF_CRYPT_WC_KYBER_H -#define WOLF_CRYPT_WC_KYBER_H +#ifndef WOLF_CRYPT_WC_MLKEM_H +#define WOLF_CRYPT_WC_MLKEM_H #include #include @@ -98,8 +98,8 @@ enum { /* The data type of the pseudo-random function. */ #define KYBER_PRF_T wc_Shake -/* Kyber key. */ -struct KyberKey { +/* ML-KEM key. */ +struct MlKemKey { /* Type of key: KYBER512, KYBER768, KYBER1024 */ int type; /* Dynamic memory allocation hint. */ @@ -136,6 +136,9 @@ struct KyberKey { extern "C" { #endif +/* For backward compatibility */ +typedef struct MlKemKey KyberKey; + WOLFSSL_LOCAL void kyber_init(void); @@ -358,7 +361,53 @@ WOLFSSL_LOCAL unsigned int kyber_arm32_rej_uniform(sword16* p, unsigned int len, } /* extern "C" */ #endif +/* ML-KEM API */ +WOLFSSL_API int wc_MlKemKey_Init(MlKemKey* key, int type); +WOLFSSL_API void wc_MlKemKey_Free(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_PrivateKeySize(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_PublicKeySize(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_CipherTextSize(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_SharedSecretSize(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_MakeKey(MlKemKey* key, WC_RNG* rng); +WOLFSSL_API int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, WC_RNG* rng, + const byte* seed, word32 seedSz); +WOLFSSL_API int wc_MlKemKey_Encapsulate(MlKemKey* key, WC_RNG* rng, + byte* ct, word32 ctSz, + byte* ss, word32 ssSz); +WOLFSSL_API int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, WC_RNG* rng, + byte* ct, word32 ctSz, + byte* ss, word32 ssSz, + const byte* seed, word32 seedSz); +WOLFSSL_API int wc_MlKemKey_Decapsulate(MlKemKey* key, + byte* ss, word32 ssSz, + const byte* ct, word32 ctSz); +WOLFSSL_API int wc_MlKemKey_DecodePrivateKey(MlKemKey* key, + const byte* priv, word32 privSz); +WOLFSSL_API int wc_MlKemKey_DecodePublicKey(MlKemKey* key, + const byte* pub, word32 pubSz); +WOLFSSL_API int wc_MlKemKey_EncodePrivateKey(MlKemKey* key, + byte* priv, word32 privSz); +WOLFSSL_API int wc_MlKemKey_EncodePublicKey(MlKemKey* key, + byte* pub, word32 pubSz); + +/* Backward compatibility defines */ +#define wc_KyberKey_Init(type, key) wc_MlKemKey_Init((key), (type)) +#define wc_KyberKey_Free wc_MlKemKey_Free +#define wc_KyberKey_PrivateKeySize wc_MlKemKey_PrivateKeySize +#define wc_KyberKey_PublicKeySize wc_MlKemKey_PublicKeySize +#define wc_KyberKey_CipherTextSize wc_MlKemKey_CipherTextSize +#define wc_KyberKey_SharedSecretSize wc_MlKemKey_SharedSecretSize +#define wc_KyberKey_MakeKey wc_MlKemKey_MakeKey +#define wc_KyberKey_MakeKeyWithRandom wc_MlKemKey_MakeKeyWithRandom +#define wc_KyberKey_Encapsulate wc_MlKemKey_Encapsulate +#define wc_KyberKey_EncapsulateWithRandom wc_MlKemKey_EncapsulateWithRandom +#define wc_KyberKey_Decapsulate wc_MlKemKey_Decapsulate +#define wc_KyberKey_DecodePrivateKey wc_MlKemKey_DecodePrivateKey +#define wc_KyberKey_DecodePublicKey wc_MlKemKey_DecodePublicKey +#define wc_KyberKey_EncodePrivateKey wc_MlKemKey_EncodePrivateKey +#define wc_KyberKey_EncodePublicKey wc_MlKemKey_EncodePublicKey + #endif /* WOLFSSL_HAVE_KYBER */ -#endif /* WOLF_CRYPT_WC_KYBER_H */ +#endif /* WOLF_CRYPT_WC_MLKEM_H */ diff --git a/wolfssl/wolfcrypt/wc_mlkem.h b/wolfssl/wolfcrypt/wc_mlkem.h new file mode 100644 index 000000000..218b39f19 --- /dev/null +++ b/wolfssl/wolfcrypt/wc_mlkem.h @@ -0,0 +1,412 @@ +/* wc_mlkem.h + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/*! + \file wolfssl/wolfcrypt/wc_mlkem.h +*/ + + +#ifndef WOLF_CRYPT_WC_MLKEM_H +#define WOLF_CRYPT_WC_MLKEM_H + +#include +#include +#include +#include + +#ifdef WOLFSSL_HAVE_KYBER + +#ifdef noinline + #define KYBER_NOINLINE noinline +#elif defined(_MSC_VER) + #define KYBER_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) + #define KYBER_NOINLINE __attribute__((noinline)) +#else + #define KYBER_NOINLINE +#endif + +enum { + /* Flags of Kyber keys. */ + KYBER_FLAG_PRIV_SET = 0x0001, + KYBER_FLAG_PUB_SET = 0x0002, + KYBER_FLAG_BOTH_SET = 0x0003, + KYBER_FLAG_H_SET = 0x0004, + KYBER_FLAG_A_SET = 0x0008, + + /* 2 bits of random used to create noise value. */ + KYBER_CBD_ETA2 = 2, + /* 3 bits of random used to create noise value. */ + KYBER_CBD_ETA3 = 3, + + /* Number of bits to compress to. */ + KYBER_COMP_4BITS = 4, + KYBER_COMP_5BITS = 5, + KYBER_COMP_10BITS = 10, + KYBER_COMP_11BITS = 11, +}; + + +/* SHAKE128 rate. */ +#define XOF_BLOCK_SIZE 168 + +/* Modulus of co-efficients of polynomial. */ +#define KYBER_Q 3329 + + +/* Kyber-512 parameters */ +#ifdef WOLFSSL_KYBER512 +/* Number of bits of random to create noise from. */ +#define KYBER512_ETA1 KYBER_CBD_ETA3 +#endif /* WOLFSSL_KYBER512 */ + +/* Kyber-768 parameters */ +#ifdef WOLFSSL_KYBER768 +/* Number of bits of random to create noise from. */ +#define KYBER768_ETA1 KYBER_CBD_ETA2 +#endif /* WOLFSSL_KYBER768 */ + +/* Kyber-1024 parameters */ +#ifdef WOLFSSL_KYBER1024 +/* Number of bits of random to create noise from. */ +#define KYBER1024_ETA1 KYBER_CBD_ETA2 +#endif /* WOLFSSL_KYBER1024 */ + + + +/* The data type of the hash function. */ +#define KYBER_HASH_T wc_Sha3 + +/* The data type of the pseudo-random function. */ +#define KYBER_PRF_T wc_Shake + +/* ML-KEM key. */ +struct MlKemKey { + /* Type of key: KYBER512, KYBER768, KYBER1024 */ + int type; + /* Dynamic memory allocation hint. */ + void* heap; +#if defined(WOLF_CRYPTO_CB) + /* Device Id. */ + int devId; +#endif + /* Flags indicating what is stored in the key. */ + int flags; + + /* A pseudo-random function object. */ + KYBER_HASH_T hash; + /* A pseudo-random function object. */ + KYBER_PRF_T prf; + + /* Private key as a vector. */ + sword16 priv[KYBER_MAX_K * KYBER_N]; + /* Public key as a vector. */ + sword16 pub[KYBER_MAX_K * KYBER_N]; + /* Public seed. */ + byte pubSeed[KYBER_SYM_SZ]; + /* Public hash - hash of encoded public key. */ + byte h[KYBER_SYM_SZ]; + /* Randomizer for decapsulation. */ + byte z[KYBER_SYM_SZ]; +#ifdef WOLFSSL_MLKEM_CACHE_A + /* A matrix from key generation. */ + sword16 a[KYBER_MAX_K * KYBER_MAX_K * KYBER_N]; +#endif +}; + +#ifdef __cplusplus + extern "C" { +#endif + +/* For backward compatibility */ +typedef struct MlKemKey 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); + +WOLFSSL_LOCAL +int kyber_gen_matrix(KYBER_PRF_T* prf, sword16* a, int kp, byte* seed, + int transposed); +WOLFSSL_LOCAL +int kyber_get_noise(KYBER_PRF_T* prf, int kp, sword16* vec1, sword16* vec2, + sword16* poly, byte* seed); + +#if defined(USE_INTEL_SPEEDUP) || \ + (defined(WOLFSSL_ARMASM) && defined(__aarch64__)) +WOLFSSL_LOCAL +int kyber_kdf(byte* seed, int seedLen, byte* out, int outLen); +#endif +WOLFSSL_LOCAL +void kyber_hash_init(KYBER_HASH_T* hash); +WOLFSSL_LOCAL +int kyber_hash_new(KYBER_HASH_T* hash, void* heap, int devId); +WOLFSSL_LOCAL +void kyber_hash_free(KYBER_HASH_T* hash); +WOLFSSL_LOCAL +int kyber_hash256(wc_Sha3* hash, const byte* data, word32 dataLen, byte* out); +WOLFSSL_LOCAL +int kyber_hash512(wc_Sha3* hash, const byte* data1, word32 data1Len, + const byte* data2, word32 data2Len, byte* out); + +WOLFSSL_LOCAL +void kyber_prf_init(KYBER_PRF_T* prf); +WOLFSSL_LOCAL +int kyber_prf_new(KYBER_PRF_T* prf, void* heap, int devId); +WOLFSSL_LOCAL +void kyber_prf_free(KYBER_PRF_T* prf); + +WOLFSSL_LOCAL +int kyber_cmp(const byte* a, const byte* b, int sz); + +WOLFSSL_LOCAL +void kyber_vec_compress_10(byte* r, sword16* v, unsigned int kp); +WOLFSSL_LOCAL +void kyber_vec_compress_11(byte* r, sword16* v); +WOLFSSL_LOCAL +void kyber_vec_decompress_10(sword16* v, const unsigned char* b, + unsigned int kp); +WOLFSSL_LOCAL +void kyber_vec_decompress_11(sword16* v, const unsigned char* b); + +WOLFSSL_LOCAL +void kyber_compress_4(byte* b, sword16* p); +WOLFSSL_LOCAL +void kyber_compress_5(byte* b, sword16* p); +WOLFSSL_LOCAL +void kyber_decompress_4(sword16* p, const unsigned char* b); +WOLFSSL_LOCAL +void kyber_decompress_5(sword16* p, const unsigned char* b); + +WOLFSSL_LOCAL +void kyber_from_msg(sword16* p, const byte* msg); +WOLFSSL_LOCAL +void kyber_to_msg(byte* msg, sword16* p); +WOLFSSL_LOCAL +void kyber_from_bytes(sword16* p, const byte* b, int k); +WOLFSSL_LOCAL +void kyber_to_bytes(byte* b, sword16* p, int k); + +#ifdef USE_INTEL_SPEEDUP +WOLFSSL_LOCAL +void kyber_keygen_avx2(sword16* priv, sword16* pub, sword16* e, + const sword16* a, int kp); +WOLFSSL_LOCAL +void kyber_encapsulate_avx2(const sword16* pub, sword16* bp, sword16* v, + const sword16* at, sword16* sp, const sword16* ep, const sword16* epp, + const sword16* m, int kp); +WOLFSSL_LOCAL +void kyber_decapsulate_avx2(const sword16* priv, sword16* mp, sword16* bp, + const sword16* v, int kp); + +WOLFSSL_LOCAL +unsigned int kyber_rej_uniform_n_avx2(sword16* p, unsigned int len, + const byte* r, unsigned int rLen); +WOLFSSL_LOCAL +unsigned int kyber_rej_uniform_avx2(sword16* p, unsigned int len, const byte* r, + unsigned int rLen); +WOLFSSL_LOCAL +void kyber_redistribute_21_rand_avx2(const word64* s, byte* r0, byte* r1, + byte* r2, byte* r3); +void kyber_redistribute_17_rand_avx2(const word64* s, byte* r0, byte* r1, + byte* r2, byte* r3); +void kyber_redistribute_16_rand_avx2(const word64* s, byte* r0, byte* r1, + byte* r2, byte* r3); +void kyber_redistribute_8_rand_avx2(const word64* s, byte* r0, byte* r1, + byte* r2, byte* r3); + +WOLFSSL_LOCAL +void kyber_sha3_blocksx4_avx2(word64* s); +WOLFSSL_LOCAL +void kyber_sha3_128_blocksx4_seed_avx2(word64* s, byte* seed); +WOLFSSL_LOCAL +void kyber_sha3_256_blocksx4_seed_avx2(word64* s, byte* seed); + +WOLFSSL_LOCAL +void kyber_cbd_eta2_avx2(sword16* p, const byte* r); +WOLFSSL_LOCAL +void kyber_cbd_eta3_avx2(sword16* p, const byte* r); + +WOLFSSL_LOCAL +void kyber_from_msg_avx2(sword16* p, const byte* msg); +WOLFSSL_LOCAL +void kyber_to_msg_avx2(byte* msg, sword16* p); + +WOLFSSL_LOCAL +void kyber_from_bytes_avx2(sword16* p, const byte* b); +WOLFSSL_LOCAL +void kyber_to_bytes_avx2(byte* b, sword16* p); + +WOLFSSL_LOCAL +void kyber_compress_10_avx2(byte* r, const sword16* p, int n); +WOLFSSL_LOCAL +void kyber_decompress_10_avx2(sword16* p, const byte* r, int n); +WOLFSSL_LOCAL +void kyber_compress_11_avx2(byte* r, const sword16* p, int n); +WOLFSSL_LOCAL +void kyber_decompress_11_avx2(sword16* p, const byte* r, int n); + +WOLFSSL_LOCAL +void kyber_compress_4_avx2(byte* r, const sword16* p); +WOLFSSL_LOCAL +void kyber_decompress_4_avx2(sword16* p, const byte* r); +WOLFSSL_LOCAL +void kyber_compress_5_avx2(byte* r, const sword16* p); +WOLFSSL_LOCAL +void kyber_decompress_5_avx2(sword16* p, const byte* r); + + +WOLFSSL_LOCAL +int kyber_cmp_avx2(const byte* a, const byte* b, int sz); +#elif defined(__aarch64__) && defined(WOLFSSL_ARMASM) +WOLFSSL_LOCAL void kyber_ntt(sword16* r); +WOLFSSL_LOCAL void kyber_invntt(sword16* r); +WOLFSSL_LOCAL void kyber_ntt_sqrdmlsh(sword16* r); +WOLFSSL_LOCAL void kyber_invntt_sqrdmlsh(sword16* r); +WOLFSSL_LOCAL void kyber_basemul_mont(sword16* r, const sword16* a, + const sword16* b); +WOLFSSL_LOCAL void kyber_basemul_mont_add(sword16* r, const sword16* a, + const sword16* b); +WOLFSSL_LOCAL void kyber_add_reduce(sword16* r, const sword16* a); +WOLFSSL_LOCAL void kyber_add3_reduce(sword16* r, const sword16* a, + const sword16* b); +WOLFSSL_LOCAL void kyber_rsub_reduce(sword16* r, const sword16* a); +WOLFSSL_LOCAL void kyber_to_mont(sword16* p); +WOLFSSL_LOCAL void kyber_to_mont_sqrdmlsh(sword16* p); +WOLFSSL_LOCAL void kyber_sha3_blocksx3_neon(word64* state); +WOLFSSL_LOCAL void kyber_shake128_blocksx3_seed_neon(word64* state, byte* seed); +WOLFSSL_LOCAL void kyber_shake256_blocksx3_seed_neon(word64* state, byte* seed); +WOLFSSL_LOCAL unsigned int kyber_rej_uniform_neon(sword16* p, unsigned int len, + const byte* r, unsigned int rLen); +WOLFSSL_LOCAL int kyber_cmp_neon(const byte* a, const byte* b, int sz); +WOLFSSL_LOCAL void kyber_csubq_neon(sword16* p); +WOLFSSL_LOCAL void kyber_from_msg_neon(sword16* p, const byte* msg); +WOLFSSL_LOCAL void kyber_to_msg_neon(byte* msg, sword16* p); +#elif defined(WOLFSSL_ARMASM_THUMB2) && defined(WOLFSSL_ARMASM) +#define kyber_ntt kyber_thumb2_ntt +#define kyber_invntt kyber_thumb2_invntt +#define kyber_basemul_mont kyber_thumb2_basemul_mont +#define kyber_basemul_mont_add kyber_thumb2_basemul_mont_add +#define kyber_rej_uniform_c kyber_thumb2_rej_uniform + +WOLFSSL_LOCAL void kyber_thumb2_ntt(sword16* r); +WOLFSSL_LOCAL void kyber_thumb2_invntt(sword16* r); +WOLFSSL_LOCAL void kyber_thumb2_basemul_mont(sword16* r, const sword16* a, + const sword16* b); +WOLFSSL_LOCAL void kyber_thumb2_basemul_mont_add(sword16* r, const sword16* a, + const sword16* b); +WOLFSSL_LOCAL void kyber_thumb2_csubq(sword16* p); +WOLFSSL_LOCAL unsigned int kyber_thumb2_rej_uniform(sword16* p, + unsigned int len, const byte* r, unsigned int rLen); +#elif defined(WOLFSSL_ARMASM) +#define kyber_ntt kyber_arm32_ntt +#define kyber_invntt kyber_arm32_invntt +#define kyber_basemul_mont kyber_arm32_basemul_mont +#define kyber_basemul_mont_add kyber_arm32_basemul_mont_add +#define kyber_rej_uniform_c kyber_arm32_rej_uniform + +WOLFSSL_LOCAL void kyber_arm32_ntt(sword16* r); +WOLFSSL_LOCAL void kyber_arm32_invntt(sword16* r); +WOLFSSL_LOCAL void kyber_arm32_basemul_mont(sword16* r, const sword16* a, + const sword16* b); +WOLFSSL_LOCAL void kyber_arm32_basemul_mont_add(sword16* r, const sword16* a, + const sword16* b); +WOLFSSL_LOCAL void kyber_arm32_csubq(sword16* p); +WOLFSSL_LOCAL unsigned int kyber_arm32_rej_uniform(sword16* p, unsigned int len, + const byte* r, unsigned int rLen); +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +/* ML-KEM API */ +WOLFSSL_API int wc_MlKemKey_Init(MlKemKey* key, int type); +WOLFSSL_API void wc_MlKemKey_Free(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_PrivateKeySize(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_PublicKeySize(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_CipherTextSize(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_SharedSecretSize(MlKemKey* key); +WOLFSSL_API int wc_MlKemKey_MakeKey(MlKemKey* key, WC_RNG* rng); +WOLFSSL_API int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, WC_RNG* rng, + const byte* seed, word32 seedSz); +WOLFSSL_API int wc_MlKemKey_Encapsulate(MlKemKey* key, WC_RNG* rng, + byte* ct, word32 ctSz, + byte* ss, word32 ssSz); +WOLFSSL_API int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, WC_RNG* rng, + byte* ct, word32 ctSz, + byte* ss, word32 ssSz, + const byte* seed, word32 seedSz); +WOLFSSL_API int wc_MlKemKey_Decapsulate(MlKemKey* key, + byte* ss, word32 ssSz, + const byte* ct, word32 ctSz); +WOLFSSL_API int wc_MlKemKey_DecodePrivateKey(MlKemKey* key, + const byte* priv, word32 privSz); +WOLFSSL_API int wc_MlKemKey_DecodePublicKey(MlKemKey* key, + const byte* pub, word32 pubSz); +WOLFSSL_API int wc_MlKemKey_EncodePrivateKey(MlKemKey* key, + byte* priv, word32 privSz); +WOLFSSL_API int wc_MlKemKey_EncodePublicKey(MlKemKey* key, + byte* pub, word32 pubSz); + +/* Backward compatibility defines */ +#define wc_KyberKey_Init(type, key) wc_MlKemKey_Init((key), (type)) +#define wc_KyberKey_Free wc_MlKemKey_Free +#define wc_KyberKey_PrivateKeySize wc_MlKemKey_PrivateKeySize +#define wc_KyberKey_PublicKeySize wc_MlKemKey_PublicKeySize +#define wc_KyberKey_CipherTextSize wc_MlKemKey_CipherTextSize +#define wc_KyberKey_SharedSecretSize wc_MlKemKey_SharedSecretSize +#define wc_KyberKey_MakeKey wc_MlKemKey_MakeKey +#define wc_KyberKey_MakeKeyWithRandom wc_MlKemKey_MakeKeyWithRandom +#define wc_KyberKey_Encapsulate wc_MlKemKey_Encapsulate +#define wc_KyberKey_EncapsulateWithRandom wc_MlKemKey_EncapsulateWithRandom +#define wc_KyberKey_Decapsulate wc_MlKemKey_Decapsulate +#define wc_KyberKey_DecodePrivateKey wc_MlKemKey_DecodePrivateKey +#define wc_KyberKey_DecodePublicKey wc_MlKemKey_DecodePublicKey +#define wc_KyberKey_EncodePrivateKey wc_MlKemKey_EncodePrivateKey +#define wc_KyberKey_EncodePublicKey wc_MlKemKey_EncodePublicKey + +#endif /* WOLFSSL_HAVE_KYBER */ + +#endif /* WOLF_CRYPT_WC_MLKEM_H */