From d8ce43ffef7d00738467f2a4287025339842c11c Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Wed, 24 Jun 2026 22:35:01 +0800 Subject: [PATCH] Update Kyber-512 to ML-KEM-768 --- CMakeLists.txt | 4 +- include/gmssl/kyber.h | 15 +- include/gmssl/sha3.h | 48 +++++++ include/gmssl/version.h | 2 +- src/kyber.c | 279 ++++++++++++++++++++++++++++++------- src/sha3.c | 247 ++++++++++++++++++++++++++++++++ tests/kybertest.c | 102 +++++++++++++- tests/kybertest_mlkem768.h | 108 ++++++++++++++ 8 files changed, 742 insertions(+), 63 deletions(-) create mode 100644 include/gmssl/sha3.h create mode 100644 src/sha3.c create mode 100644 tests/kybertest_mlkem768.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a0e17f64..ad1ecabe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -617,7 +617,7 @@ endif() if (ENABLE_KYBER) message(STATUS "ENABLE_KYBER is ON") add_definitions(-DENABLE_KYBER) - list(APPEND src src/kyber.c) + list(APPEND src src/sha3.c src/kyber.c) list(APPEND tools tools/kyberkeygen.c tools/kyberencap.c tools/kyberdecap.c) list(APPEND tests kyber) endif() @@ -1016,7 +1016,7 @@ endif() # set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_VENDOR "GmSSL develop team") -set(CPACK_PACKAGE_VERSION "3.3.0-dev.1169") +set(CPACK_PACKAGE_VERSION "3.3.0-dev.1170") set(CPACK_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md) set(CPACK_NSIS_MODIFY_PATH ON) include(CPack) diff --git a/include/gmssl/kyber.h b/include/gmssl/kyber.h index b8811534..a93e3079 100644 --- a/include/gmssl/kyber.h +++ b/include/gmssl/kyber.h @@ -42,13 +42,13 @@ extern "C" { #define KYBER1024_DU 11 #define KYBER512_DV 4 -#define KYBER769_DV 4 +#define KYBER768_DV 4 #define KYBER1024_DV 5 -#define KYBER_K KYBER512_K -#define KYBER_ETA1 KYBER512_ETA1 -#define KYBER_DU KYBER512_DU -#define KYBER_DV KYBER512_DV +#define KYBER_K KYBER768_K +#define KYBER_ETA1 KYBER768_ETA1 +#define KYBER_DU KYBER768_DU +#define KYBER_DV KYBER768_DV #define KYBER_C1_SIZE ((256 * KYBER_DU)/8) @@ -77,6 +77,8 @@ CRYSTALS-Kyber Algorithm Specifications and Supporing Documentation (version 3.0 void kyber_h_hash(const uint8_t *in, size_t inlen, uint8_t out[32]); void kyber_g_hash(const uint8_t *in, size_t inlen, uint8_t out[64]); +void kyber_h_hash_sm3(const uint8_t *in, size_t inlen, uint8_t out[32]); +void kyber_g_hash_sm3(const uint8_t *in, size_t inlen, uint8_t out[64]); @@ -88,6 +90,7 @@ int kyber_poly_print(FILE *fp, int fmt, int ind, const char *label, const kyber void kyber_poly_set_zero(kyber_poly_t r); int kyber_poly_rand(kyber_poly_t r); int kyber_poly_uniform_sample(kyber_poly_t r, const uint8_t rho[32], uint8_t j, uint8_t i); +int kyber_poly_uniform_sample_sm3(kyber_poly_t r, const uint8_t rho[32], uint8_t j, uint8_t i); int kyber_poly_cbd_sample(kyber_poly_t r, int eta, const uint8_t secret[32], uint8_t n); int kyber_poly_equ(const kyber_poly_t a, const kyber_poly_t b); void kyber_poly_add(kyber_poly_t r, const kyber_poly_t a, const kyber_poly_t b); @@ -165,6 +168,7 @@ typedef struct { #define KYBER_PRIVATE_KEY_SIZE sizeof(KYBER_KEY) int kyber_key_generate_ex(KYBER_KEY *key, const uint8_t random[32]); +int kyber_key_generate_from_seed(KYBER_KEY *key, const uint8_t seed[64]); int kyber_public_key_to_bytes(const KYBER_KEY *key, uint8_t **out, size_t *outlen); int kyber_public_key_from_bytes(KYBER_KEY *key, const uint8_t **in, size_t *inlen); int kyber_public_key_print(FILE *fp, int fmt, int ind, const char *label, const KYBER_KEY *pk); @@ -178,6 +182,7 @@ int kyber_ciphertext_to_bytes(const KYBER_CIPHERTEXT *ciphertext, uint8_t **out, int kyber_ciphertext_from_bytes(KYBER_CIPHERTEXT *ciphertext, const uint8_t **in, size_t *inlen); int kyber_ciphertext_print(FILE *fp, int fmt, int ind, const char *label, const KYBER_CIPHERTEXT *c); +int kyber_encap_ex(const KYBER_KEY *pk, const uint8_t m[32], KYBER_CIPHERTEXT *c, uint8_t K[32]); int kyber_encap(const KYBER_KEY *pk, KYBER_CIPHERTEXT *c, uint8_t K[32]); int kyber_decap(const KYBER_KEY *sk, const KYBER_CIPHERTEXT *c, uint8_t K[32]); diff --git a/include/gmssl/sha3.h b/include/gmssl/sha3.h new file mode 100644 index 00000000..5bcd9c89 --- /dev/null +++ b/include/gmssl/sha3.h @@ -0,0 +1,48 @@ +/* + * Copyright 2014-2026 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#ifndef GMSSL_SHA3_H +#define GMSSL_SHA3_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define SHA3_256_DIGEST_SIZE 32 +#define SHA3_512_DIGEST_SIZE 64 + +typedef struct { + uint64_t state[25]; + uint8_t block[168]; + size_t rate; + size_t num; + int squeezing; +} SHAKE_CTX; + +void sha3_256(const uint8_t *in, size_t inlen, uint8_t out[SHA3_256_DIGEST_SIZE]); +void sha3_512(const uint8_t *in, size_t inlen, uint8_t out[SHA3_512_DIGEST_SIZE]); + +void shake128_init(SHAKE_CTX *ctx); +void shake256_init(SHAKE_CTX *ctx); +void shake_update(SHAKE_CTX *ctx, const uint8_t *in, size_t inlen); +void shake_finish(SHAKE_CTX *ctx); +void shake_squeeze(SHAKE_CTX *ctx, uint8_t *out, size_t outlen); + +void shake128(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen); +void shake256(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/gmssl/version.h b/include/gmssl/version.h index e02cceea..c61a20bf 100644 --- a/include/gmssl/version.h +++ b/include/gmssl/version.h @@ -18,7 +18,7 @@ extern "C" { #define GMSSL_VERSION_NUM 30300 -#define GMSSL_VERSION_STR "GmSSL 3.3.0-dev.1169" +#define GMSSL_VERSION_STR "GmSSL 3.3.0-dev.1170" int gmssl_version_num(void); const char *gmssl_version_str(void); diff --git a/src/kyber.c b/src/kyber.c index d0ea1b27..91a9f1b3 100644 --- a/src/kyber.c +++ b/src/kyber.c @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -22,13 +24,29 @@ void kyber_h_hash(const uint8_t *in, size_t inlen, uint8_t out[32]) { - SM3_CTX ctx; - sm3_init(&ctx); - sm3_update(&ctx, in, inlen); - sm3_finish(&ctx, out); + sha3_256(in, inlen, out); } void kyber_g_hash(const uint8_t *in, size_t inlen, uint8_t out[64]) +{ + sha3_512(in, inlen, out); +} + +/* + * Legacy SM3-based Kyber variants kept for compatibility/reference only. + * The ML-KEM path above uses FIPS 203 SHA3/SHAKE functions. + */ +void kyber_h_hash_sm3(const uint8_t *in, size_t inlen, uint8_t out[32]) +{ + SM3_CTX ctx; + + sm3_init(&ctx); + sm3_update(&ctx, in, inlen); + sm3_finish(&ctx, out); + gmssl_secure_clear(&ctx, sizeof(ctx)); +} + +void kyber_g_hash_sm3(const uint8_t *in, size_t inlen, uint8_t out[64]) { SM3_CTX ctx; uint8_t ctr[4] = {0}; @@ -43,30 +61,53 @@ void kyber_g_hash(const uint8_t *in, size_t inlen, uint8_t out[64]) sm3_update(&ctx, in, inlen); sm3_update(&ctx, ctr, 4); sm3_finish(&ctx, out + 32); + + gmssl_secure_clear(&ctx, sizeof(ctx)); + gmssl_secure_clear(ctr, sizeof(ctr)); } -// https://www.cryptosys.net/pki/manpki/pki_prfxof.html static int kyber_prf(const uint8_t seed[32], uint8_t N, size_t outlen, uint8_t *out) { - uint8_t salt[1]; - uint8_t key[32]; + uint8_t in[33]; - salt[0] = (uint8_t)N; + memcpy(in, seed, 32); + in[32] = N; + shake256(in, sizeof(in), out, outlen); + gmssl_secure_clear(in, sizeof(in)); + return 1; +} + +static int kyber_prf_sm3(const uint8_t seed[32], uint8_t N, size_t outlen, uint8_t *out) +{ + uint8_t key[32]; if (sm3_hkdf_extract(NULL, 0, seed, 32, key) != 1) { error_print(); return -1; } - sm3_hkdf_expand(key, &N, 1, outlen, out); + if (sm3_hkdf_expand(key, &N, 1, outlen, out) != 1) { + gmssl_secure_clear(key, sizeof(key)); + error_print(); + return -1; + } + gmssl_secure_clear(key, sizeof(key)); return 1; } -static int kyber_kdf(const uint8_t in[64], uint8_t out[32]) +static int kyber_kdf_sm3(const uint8_t in[64], uint8_t out[32]) { uint8_t key[32]; - sm3_hkdf_extract(NULL, 0, in, 64, key); - sm3_hkdf_expand(key, NULL, 0, 32, out); - gmssl_secure_clear(key, 32); + + if (sm3_hkdf_extract(NULL, 0, in, 64, key) != 1) { + error_print(); + return -1; + } + if (sm3_hkdf_expand(key, NULL, 0, 32, out) != 1) { + gmssl_secure_clear(key, sizeof(key)); + error_print(); + return -1; + } + gmssl_secure_clear(key, sizeof(key)); return 1; } @@ -112,6 +153,51 @@ int kyber_poly_rand(kyber_poly_t r) } int kyber_poly_uniform_sample(kyber_poly_t r, const uint8_t rho[32], uint8_t j, uint8_t i) +{ + SHAKE_CTX ctx; + uint8_t seed[32 + 2]; + uint8_t rand[168]; + size_t n; + int16_t *out = r; + int16_t *end = r + 256; + + memcpy(seed, rho, 32); + seed[32] = j; + seed[33] = i; + shake128_init(&ctx); + shake_update(&ctx, seed, sizeof(seed)); + shake_finish(&ctx); + + for (;;) { + shake_squeeze(&ctx, rand, sizeof(rand)); + for (n = 0; n < sizeof(rand); n += 3) { + int16_t a0 = rand[n] | ((int16_t)(rand[n + 1] & 0xf) << 8); + int16_t a1 = (rand[n + 1] >> 4) | ((int16_t)rand[n + 2] << 4); + + if (a0 < KYBER_Q) { + + *out++ = a0; + if (out >= end) { + goto end; + } + } + if (a1 < KYBER_Q) { + *out++ = a1; + if (out >= end) { + goto end; + } + } + } + } + +end: + gmssl_secure_clear(&ctx, sizeof(ctx)); + gmssl_secure_clear(seed, sizeof(seed)); + gmssl_secure_clear(rand, sizeof(rand)); + return 1; +} + +int kyber_poly_uniform_sample_sm3(kyber_poly_t r, const uint8_t rho[32], uint8_t j, uint8_t i) { SM3_CTX ctx; uint8_t seed[32 + 2 + 4]; @@ -154,6 +240,9 @@ int kyber_poly_uniform_sample(kyber_poly_t r, const uint8_t rho[32], uint8_t j, } end: + gmssl_secure_clear(&ctx, sizeof(ctx)); + gmssl_secure_clear(seed, sizeof(seed)); + gmssl_secure_clear(rand, sizeof(rand)); return 1; } @@ -198,6 +287,9 @@ int kyber_poly_cbd_sample(kyber_poly_t r, int eta, const uint8_t secret[32], uin } gmssl_secure_clear(bytes, sizeof(bytes)); + } else { + error_print(); + return -1; } for (i = 0; i < 256; i++) { @@ -586,21 +678,23 @@ int kyber_cpa_key_generate_ex(KYBER_CPA_KEY *key, const uint8_t random[32]) kyber_poly_t e[KYBER_K]; kyber_poly_t t[KYBER_K]; uint8_t d[64]; + uint8_t g_in[33]; uint8_t *rho = d; uint8_t *sigma = d + 32; uint8_t N = 0; int i,j; if (random) { - memcpy(d, random, 32); + memcpy(g_in, random, 32); } else { - if (rand_bytes(d, 32) != 1) { + if (rand_bytes(g_in, 32) != 1) { error_print(); return -1; } } + g_in[32] = KYBER_K; - kyber_g_hash(d, 32, d); + kyber_g_hash(g_in, sizeof(g_in), d); // AHat[i][j] = Parse(XOR(rho, j, i)) for (i = 0; i < KYBER_K; i++) { @@ -652,6 +746,7 @@ int kyber_cpa_key_generate_ex(KYBER_CPA_KEY *key, const uint8_t random[32]) memcpy(key->public_key.rho, rho, 32); gmssl_secure_clear(d, sizeof(d)); + gmssl_secure_clear(g_in, sizeof(g_in)); gmssl_secure_clear(s, sizeof(s)); gmssl_secure_clear(e, sizeof(e)); @@ -677,6 +772,9 @@ int kyber_cpa_public_key_to_bytes(const KYBER_CPA_KEY *key, uint8_t **out, size_ int kyber_cpa_public_key_from_bytes(KYBER_CPA_KEY *key, const uint8_t **in, size_t *inlen) { + kyber_poly_t t; + int i; + if (!key || !in || !(*in) || !inlen) { error_print(); return -1; @@ -685,13 +783,19 @@ int kyber_cpa_public_key_from_bytes(KYBER_CPA_KEY *key, const uint8_t **in, size error_print(); return -1; } - memset(key, 0, sizeof(*key)); memcpy(key->public_key.t, *in, sizeof(key->public_key.t)); *in += sizeof(key->public_key.t); *inlen -= sizeof(key->public_key.t); memcpy(key->public_key.rho, *in, sizeof(key->public_key.rho)); *in += sizeof(key->public_key.rho); *inlen -= sizeof(key->public_key.rho); + + for (i = 0; i < KYBER_K; i++) { + if (kyber_poly_decode12(t, key->public_key.t[i]) != 1) { + error_print(); + return -1; + } + } return 1; } @@ -701,15 +805,15 @@ int kyber_cpa_private_key_to_bytes(const KYBER_CPA_KEY *key, uint8_t **out, size error_print(); return -1; } - if (kyber_cpa_public_key_to_bytes(key, out, outlen) != 1) { - error_print(); - return -1; - } if (out && *out) { memcpy(*out, key->s, sizeof(key->s)); *out += sizeof(key->s); } *outlen += sizeof(key->s); + if (kyber_cpa_public_key_to_bytes(key, out, outlen) != 1) { + error_print(); + return -1; + } return 1; } @@ -720,10 +824,6 @@ int kyber_cpa_private_key_from_bytes(KYBER_CPA_KEY *key, const uint8_t **in, siz return -1; } memset(key, 0, sizeof(*key)); - if (kyber_cpa_public_key_from_bytes(key, in, inlen) != 1) { - error_print(); - return -1; - } if (*inlen < sizeof(key->s)) { error_print(); return -1; @@ -731,6 +831,10 @@ int kyber_cpa_private_key_from_bytes(KYBER_CPA_KEY *key, const uint8_t **in, siz memcpy(key->s, *in, sizeof(key->s)); *in += sizeof(key->s); *inlen -= sizeof(key->s); + if (kyber_cpa_public_key_from_bytes(key, in, inlen) != 1) { + error_print(); + return -1; + } return 1; } @@ -932,8 +1036,8 @@ int kyber_cpa_encrypt(const KYBER_CPA_KEY *key, const uint8_t in[32], kyber_poly_encode10(u[i], out->c1[i]); } - // c2 = Encode4(Compress(v, 4)) - kyber_poly_compress(v, 4, v); + // c2 = Encode4(Compress(v, KYBER_DV)) + kyber_poly_compress(v, KYBER_DV, v); kyber_poly_encode4(v, out->c2); gmssl_secure_clear(m, sizeof(m)); @@ -962,7 +1066,7 @@ int kyber_cpa_decrypt(const KYBER_CPA_KEY *key, const KYBER_CPA_CIPHERTEXT *in, // v = Decompress(Decode_dv(c2), dv) kyber_poly_decode4(v, in->c2); - kyber_poly_decompress(v, 4, v); + kyber_poly_decompress(v, KYBER_DV, v); // s = Decode_12(sk) @@ -997,20 +1101,46 @@ int kyber_cpa_decrypt(const KYBER_CPA_KEY *key, const KYBER_CPA_CIPHERTEXT *in, int kyber_key_generate_ex(KYBER_KEY *key, const uint8_t random[32]) { + uint8_t seed[64]; + if (!key) { error_print(); return -1; } - if (kyber_cpa_key_generate_ex(&key->cpa_key, random) != 1) { + if (random) { + memcpy(seed, random, 32); + } else if (rand_bytes(seed, 32) != 1) { error_print(); return -1; } - kyber_h_hash((uint8_t *)key, sizeof(KYBER_CPA_PUBLIC_KEY), key->pk_hash); - if (rand_bytes(key->z, 32) != 1) { - gmssl_secure_clear(&key->cpa_key, sizeof(KYBER_CPA_KEY)); + if (rand_bytes(seed + 32, 32) != 1) { + gmssl_secure_clear(seed, sizeof(seed)); error_print(); return -1; } + if (kyber_key_generate_from_seed(key, seed) != 1) { + gmssl_secure_clear(seed, sizeof(seed)); + error_print(); + return -1; + } + gmssl_secure_clear(seed, sizeof(seed)); + return 1; +} + +int kyber_key_generate_from_seed(KYBER_KEY *key, const uint8_t seed[64]) +{ + if (!key || !seed) { + error_print(); + return -1; + } + memset(key, 0, sizeof(*key)); + if (kyber_cpa_key_generate_ex(&key->cpa_key, seed) != 1) { + gmssl_secure_clear(key, sizeof(*key)); + error_print(); + return -1; + } + kyber_h_hash((const uint8_t *)&key->cpa_key.public_key, sizeof(KYBER_CPA_PUBLIC_KEY), key->pk_hash); + memcpy(key->z, seed + 32, 32); return 1; } @@ -1064,6 +1194,8 @@ int kyber_private_key_to_bytes(const KYBER_KEY *key, uint8_t **out, size_t *outl int kyber_private_key_from_bytes(KYBER_KEY *key, const uint8_t **in, size_t *inlen) { + uint8_t pk_hash[32]; + if (!key || !in || !(*in) || !inlen) { error_print(); return -1; @@ -1084,6 +1216,14 @@ int kyber_private_key_from_bytes(KYBER_KEY *key, const uint8_t **in, size_t *inl memcpy(key->z, *in, sizeof(key->z)); *in += sizeof(key->z); *inlen -= sizeof(key->z); + kyber_h_hash((const uint8_t *)&key->cpa_key.public_key, sizeof(KYBER_CPA_PUBLIC_KEY), pk_hash); + if (gmssl_secure_memcmp(key->pk_hash, pk_hash, sizeof(pk_hash)) != 0) { + gmssl_secure_clear(key, sizeof(*key)); + gmssl_secure_clear(pk_hash, sizeof(pk_hash)); + error_print(); + return -1; + } + gmssl_secure_clear(pk_hash, sizeof(pk_hash)); return 1; } @@ -1101,60 +1241,86 @@ int kyber_private_key_print(FILE *fp, int fmt, int ind, const char *label, const return 1; } -int kyber_encap(const KYBER_KEY *key, KYBER_CIPHERTEXT *c, uint8_t K[32]) +int kyber_encap_ex(const KYBER_KEY *key, const uint8_t m[32], KYBER_CIPHERTEXT *c, uint8_t K[32]) { uint8_t m_h[64]; uint8_t K_r[64]; - uint8_t *m = m_h; uint8_t *h = m_h + 32; uint8_t *K_ = K_r; uint8_t *r = K_r + 32; - // m = rand(32) - if (rand_bytes(m, 32) != 1) { + if (!key || !m || !c || !K) { error_print(); return -1; } - // m = H(m) - kyber_h_hash(m, 32, m); + memcpy(m_h, m, 32); // h = H(pk) - kyber_h_hash((const uint8_t *)key, sizeof(KYBER_PUBLIC_KEY), h); + kyber_h_hash((const uint8_t *)&key->cpa_key.public_key, sizeof(KYBER_PUBLIC_KEY), h); // (K_, r) = G(m || H(pk)) kyber_g_hash(m_h, 64, K_r); // c = Kyber.CPA.Enc(pk, m, r) if (kyber_cpa_encrypt(&key->cpa_key, m, r, c) != 1) { + gmssl_secure_clear(m_h, sizeof(m_h)); + gmssl_secure_clear(K_r, sizeof(K_r)); error_print(); return -1; } - // H(c) - kyber_h_hash((uint8_t *)c, sizeof(KYBER_CIPHERTEXT), r); - - // K = KDF(K_ || H(c)) - kyber_kdf(K_r, K); + memcpy(K, K_, 32); gmssl_secure_clear(m_h, sizeof(m_h)); gmssl_secure_clear(K_r, sizeof(K_r)); return 1; } +int kyber_encap(const KYBER_KEY *key, KYBER_CIPHERTEXT *c, uint8_t K[32]) +{ + uint8_t m[32]; + + if (rand_bytes(m, sizeof(m)) != 1) { + error_print(); + return -1; + } + if (kyber_encap_ex(key, m, c, K) != 1) { + gmssl_secure_clear(m, sizeof(m)); + error_print(); + return -1; + } + gmssl_secure_clear(m, sizeof(m)); + return 1; +} + int kyber_decap(const KYBER_KEY *key, const KYBER_CIPHERTEXT *c, uint8_t K[32]) { uint8_t m_h[64]; uint8_t K_r[64]; + uint8_t Kbar[32]; + uint8_t z_c[32 + sizeof(KYBER_CIPHERTEXT)]; uint8_t *m = m_h; uint8_t *h = m_h + 32; - uint8_t *K_ = K_r; uint8_t *r = K_r + 32; KYBER_CIPHERTEXT c_; + uint8_t mask; + int diff; + int i; + if (!key || !c || !K) { + error_print(); + return -1; + } + // m' = Dec(sk, c) if (kyber_cpa_decrypt(&key->cpa_key, c, m) != 1) { + gmssl_secure_clear(m_h, sizeof(m_h)); + gmssl_secure_clear(K_r, sizeof(K_r)); + gmssl_secure_clear(Kbar, sizeof(Kbar)); + gmssl_secure_clear(z_c, sizeof(z_c)); + gmssl_secure_clear(&c_, sizeof(c_)); error_print(); return -1; } @@ -1169,24 +1335,29 @@ int kyber_decap(const KYBER_KEY *key, const KYBER_CIPHERTEXT *c, uint8_t K[32]) if (kyber_cpa_encrypt(&key->cpa_key, m, r, &c_) != 1) { gmssl_secure_clear(m_h, sizeof(m_h)); gmssl_secure_clear(K_r, sizeof(K_r)); + gmssl_secure_clear(Kbar, sizeof(Kbar)); + gmssl_secure_clear(z_c, sizeof(z_c)); + gmssl_secure_clear(&c_, sizeof(c_)); error_print(); return -1; } - // H(c) - kyber_h_hash((uint8_t *)c, sizeof(KYBER_CIPHERTEXT), r); + memcpy(z_c, key->z, 32); + memcpy(z_c + 32, c, sizeof(KYBER_CIPHERTEXT)); + shake256(z_c, sizeof(z_c), Kbar, sizeof(Kbar)); - if (memcmp(c, &c_, sizeof(KYBER_CIPHERTEXT)) == 0) { - // K = KDF(K_||H(c)) - kyber_kdf(K_r, K); - } else { - error_print(); - memcpy(K_r, key->z, 32); // TODO: const time - kyber_kdf(K_r, K); + memcpy(K, K_r, 32); + diff = gmssl_secure_memcmp(c, &c_, sizeof(KYBER_CIPHERTEXT)); + mask = (uint8_t)(0 - (uint8_t)(diff == 0)); + for (i = 0; i < 32; i++) { + K[i] = (K[i] & mask) | (Kbar[i] & ~mask); } gmssl_secure_clear(m_h, sizeof(m_h)); gmssl_secure_clear(K_r, sizeof(K_r)); + gmssl_secure_clear(Kbar, sizeof(Kbar)); + gmssl_secure_clear(z_c, sizeof(z_c)); + gmssl_secure_clear(&c_, sizeof(c_)); return 1; } diff --git a/src/sha3.c b/src/sha3.c new file mode 100644 index 00000000..34347b9f --- /dev/null +++ b/src/sha3.c @@ -0,0 +1,247 @@ +/* + * Copyright 2014-2026 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include + + +static uint64_t load64_le(const uint8_t in[8]) +{ + return ((uint64_t)in[0]) + | ((uint64_t)in[1] << 8) + | ((uint64_t)in[2] << 16) + | ((uint64_t)in[3] << 24) + | ((uint64_t)in[4] << 32) + | ((uint64_t)in[5] << 40) + | ((uint64_t)in[6] << 48) + | ((uint64_t)in[7] << 56); +} + +static void store64_le(uint8_t out[8], uint64_t a) +{ + out[0] = (uint8_t)a; + out[1] = (uint8_t)(a >> 8); + out[2] = (uint8_t)(a >> 16); + out[3] = (uint8_t)(a >> 24); + out[4] = (uint8_t)(a >> 32); + out[5] = (uint8_t)(a >> 40); + out[6] = (uint8_t)(a >> 48); + out[7] = (uint8_t)(a >> 56); +} + +static uint64_t rol64(uint64_t a, int n) +{ + return n ? ((a << n) | (a >> (64 - n))) : a; +} + +static void keccak_f1600(uint64_t a[25]) +{ + static const uint64_t rc[24] = { + 0x0000000000000001ULL, 0x0000000000008082ULL, + 0x800000000000808aULL, 0x8000000080008000ULL, + 0x000000000000808bULL, 0x0000000080000001ULL, + 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x000000000000008aULL, 0x0000000000000088ULL, + 0x0000000080008009ULL, 0x000000008000000aULL, + 0x000000008000808bULL, 0x800000000000008bULL, + 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, + 0x000000000000800aULL, 0x800000008000000aULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, + 0x0000000080000001ULL, 0x8000000080008008ULL, + }; + static const int rho[25] = { + 0, 1, 62, 28, 27, + 36, 44, 6, 55, 20, + 3, 10, 43, 25, 39, + 41, 45, 15, 21, 8, + 18, 2, 61, 56, 14, + }; + uint64_t b[25]; + uint64_t c[5]; + uint64_t d; + int round; + int x; + int y; + + for (round = 0; round < 24; round++) { + for (x = 0; x < 5; x++) { + c[x] = a[x] ^ a[x + 5] ^ a[x + 10] ^ a[x + 15] ^ a[x + 20]; + } + for (x = 0; x < 5; x++) { + d = c[(x + 4) % 5] ^ rol64(c[(x + 1) % 5], 1); + for (y = 0; y < 5; y++) { + a[x + 5 * y] ^= d; + } + } + for (x = 0; x < 5; x++) { + for (y = 0; y < 5; y++) { + b[y + 5 * ((2 * x + 3 * y) % 5)] = + rol64(a[x + 5 * y], rho[x + 5 * y]); + } + } + for (x = 0; x < 5; x++) { + for (y = 0; y < 5; y++) { + a[x + 5 * y] = b[x + 5 * y] + ^ ((~b[((x + 1) % 5) + 5 * y]) + & b[((x + 2) % 5) + 5 * y]); + } + } + a[0] ^= rc[round]; + } + + gmssl_secure_clear(b, sizeof(b)); + gmssl_secure_clear(c, sizeof(c)); + gmssl_secure_clear(&d, sizeof(d)); +} + +static void keccak_xor_block(uint64_t state[25], const uint8_t *block, size_t rate) +{ + size_t i; + + for (i = 0; i < rate / 8; i++) { + state[i] ^= load64_le(block + 8 * i); + } +} + +static void keccak_store_block(uint8_t *block, const uint64_t state[25], size_t rate) +{ + size_t i; + + for (i = 0; i < rate / 8; i++) { + store64_le(block + 8 * i, state[i]); + } +} + +static void keccak_init(SHAKE_CTX *ctx, size_t rate) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->rate = rate; +} + +static void keccak_update(SHAKE_CTX *ctx, const uint8_t *in, size_t inlen) +{ + size_t len; + + while (inlen) { + len = ctx->rate - ctx->num; + if (len > inlen) { + len = inlen; + } + memcpy(ctx->block + ctx->num, in, len); + ctx->num += len; + in += len; + inlen -= len; + + if (ctx->num == ctx->rate) { + keccak_xor_block(ctx->state, ctx->block, ctx->rate); + keccak_f1600(ctx->state); + gmssl_secure_clear(ctx->block, ctx->rate); + ctx->num = 0; + } + } +} + +static void keccak_finish(SHAKE_CTX *ctx, uint8_t suffix) +{ + memset(ctx->block + ctx->num, 0, ctx->rate - ctx->num); + ctx->block[ctx->num] ^= suffix; + ctx->block[ctx->rate - 1] ^= 0x80; + keccak_xor_block(ctx->state, ctx->block, ctx->rate); + keccak_f1600(ctx->state); + keccak_store_block(ctx->block, ctx->state, ctx->rate); + ctx->num = 0; + ctx->squeezing = 1; +} + +static void keccak_squeeze(SHAKE_CTX *ctx, uint8_t *out, size_t outlen) +{ + size_t len; + + while (outlen) { + if (ctx->num == ctx->rate) { + keccak_f1600(ctx->state); + keccak_store_block(ctx->block, ctx->state, ctx->rate); + ctx->num = 0; + } + len = ctx->rate - ctx->num; + if (len > outlen) { + len = outlen; + } + memcpy(out, ctx->block + ctx->num, len); + ctx->num += len; + out += len; + outlen -= len; + } +} + +static void keccak_digest(size_t rate, uint8_t suffix, + const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen) +{ + SHAKE_CTX ctx; + + keccak_init(&ctx, rate); + keccak_update(&ctx, in, inlen); + keccak_finish(&ctx, suffix); + keccak_squeeze(&ctx, out, outlen); + gmssl_secure_clear(&ctx, sizeof(ctx)); +} + +void sha3_256(const uint8_t *in, size_t inlen, uint8_t out[SHA3_256_DIGEST_SIZE]) +{ + keccak_digest(136, 0x06, in, inlen, out, SHA3_256_DIGEST_SIZE); +} + +void sha3_512(const uint8_t *in, size_t inlen, uint8_t out[SHA3_512_DIGEST_SIZE]) +{ + keccak_digest(72, 0x06, in, inlen, out, SHA3_512_DIGEST_SIZE); +} + +void shake128_init(SHAKE_CTX *ctx) +{ + keccak_init(ctx, 168); +} + +void shake256_init(SHAKE_CTX *ctx) +{ + keccak_init(ctx, 136); +} + +void shake_update(SHAKE_CTX *ctx, const uint8_t *in, size_t inlen) +{ + if (!ctx->squeezing) { + keccak_update(ctx, in, inlen); + } +} + +void shake_finish(SHAKE_CTX *ctx) +{ + if (!ctx->squeezing) { + keccak_finish(ctx, 0x1f); + } +} + +void shake_squeeze(SHAKE_CTX *ctx, uint8_t *out, size_t outlen) +{ + if (!ctx->squeezing) { + keccak_finish(ctx, 0x1f); + } + keccak_squeeze(ctx, out, outlen); +} + +void shake128(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen) +{ + keccak_digest(168, 0x1f, in, inlen, out, outlen); +} + +void shake256(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen) +{ + keccak_digest(136, 0x1f, in, inlen, out, outlen); +} diff --git a/tests/kybertest.c b/tests/kybertest.c index 874ce94f..459b3e65 100644 --- a/tests/kybertest.c +++ b/tests/kybertest.c @@ -15,6 +15,21 @@ #include #include +#include "kybertest_mlkem768.h" + + +static int test_hex_to_bytes(const char *hex, uint8_t *out, size_t outlen) +{ + size_t len = outlen; + + if (hex_to_bytes(hex, strlen(hex), out, &len) != 1 || len != outlen) { + error_print(); + return -1; + } + + return 1; +} + static int test_kyber_poly_uniform_sample(void) { @@ -506,6 +521,91 @@ static int test_kyber_kem(void) return 1; } +static int test_mlkem768_vector(void) +{ + uint8_t seed[64]; + uint8_t m[32]; + KYBER_KEY key; + KYBER_CIPHERTEXT c; + uint8_t K[32]; + uint8_t K_[32]; + uint8_t expected_ek[KYBER_PUBLIC_KEY_SIZE]; + uint8_t expected_dk[KYBER_PRIVATE_KEY_SIZE]; + uint8_t expected_ct[sizeof(KYBER_CIPHERTEXT)]; + uint8_t expected_ss[32]; + uint8_t ek[KYBER_PUBLIC_KEY_SIZE]; + uint8_t dk[KYBER_PRIVATE_KEY_SIZE]; + uint8_t ct[sizeof(KYBER_CIPHERTEXT)]; + uint8_t *p; + size_t len; + size_t i; + + for (i = 0; i < sizeof(seed); i++) { + seed[i] = (uint8_t)i; + } + for (i = 0; i < sizeof(m); i++) { + m[i] = (uint8_t)(0x40 + i); + } + + if (test_hex_to_bytes(mlkem768_ek_hex, expected_ek, sizeof(expected_ek)) != 1 + || test_hex_to_bytes(mlkem768_dk_hex, expected_dk, sizeof(expected_dk)) != 1 + || test_hex_to_bytes(mlkem768_ct_hex, expected_ct, sizeof(expected_ct)) != 1 + || test_hex_to_bytes(mlkem768_ss_hex, expected_ss, sizeof(expected_ss)) != 1) { + error_print(); + return -1; + } + + if (kyber_key_generate_from_seed(&key, seed) != 1) { + error_print(); + return -1; + } + p = ek; + len = 0; + if (kyber_public_key_to_bytes(&key, &p, &len) != 1 || len != sizeof(ek)) { + error_print(); + return -1; + } + p = dk; + len = 0; + if (kyber_private_key_to_bytes(&key, &p, &len) != 1 || len != sizeof(dk)) { + error_print(); + return -1; + } + if (memcmp(ek, expected_ek, sizeof(ek)) != 0 + || memcmp(dk, expected_dk, sizeof(dk)) != 0) { + error_print(); + return -1; + } + + if (kyber_encap_ex(&key, m, &c, K) != 1) { + error_print(); + return -1; + } + p = ct; + len = 0; + if (kyber_ciphertext_to_bytes(&c, &p, &len) != 1 || len != sizeof(ct)) { + error_print(); + return -1; + } + if (memcmp(ct, expected_ct, sizeof(ct)) != 0 + || memcmp(K, expected_ss, sizeof(K)) != 0) { + error_print(); + return -1; + } + + if (kyber_decap(&key, &c, K_) != 1) { + error_print(); + return -1; + } + if (memcmp(K_, expected_ss, sizeof(K_)) != 0) { + error_print(); + return -1; + } + + printf("%s() ok\n", __FUNCTION__); + return 1; +} + static int test_kyber_cpa_key_to_bytes(void) { KYBER_CPA_KEY key; @@ -652,6 +752,7 @@ int main(void) if (test_kyber_key_to_bytes() != 1) goto err; if (test_kyber_cpa_ciphertext_to_bytes() != 1) goto err; if (test_kyber_kem() != 1) goto err; + if (test_mlkem768_vector() != 1) goto err; printf("%s all tests passed\n", __FILE__); return 0; @@ -659,4 +760,3 @@ err: error_print(); return 1; } - diff --git a/tests/kybertest_mlkem768.h b/tests/kybertest_mlkem768.h new file mode 100644 index 00000000..4d703129 --- /dev/null +++ b/tests/kybertest_mlkem768.h @@ -0,0 +1,108 @@ +/* Generated from OpenSSL 3.6.2 ML-KEM-768 with seed 00..3f and ikme 40..5f. */ + +static const char mlkem768_ek_hex[] = + "298aa10d423c8dda069d02bc59e6cdf03a096b8b3da4cab9b80ca4a14907672ccef1ec4faf234a0bc5b7e9d473f2b313" + "3b3b26a1d175cb67a7805919699c02f76531b99c5f89180704bb4ca4535c5b8972679c660a07c5e514b87009c862eb8f" + "5157695efb3fc40a9def6b81c1cc02a249ae4f094ad0d9bd3485c1c1c68080520a7c8c632032cee738154e5c5176c07d" + "a56024776a430fe76eacf665a3f7b832102215bc82f10939c8355704336a8fac1d81e4bb0485aa5d7c74d6b59bbe5c5e" + "972a0d8bac411b55b5d5557cd680a1a8f71b4eb86bc48c9a0509731a54bd9d7290b27963e4372dc9b199cfdcac0b01ac" + "d28a62395112e4c43648d622c48c8234d01440e8cc376c927f23a5afc9ac0474c662274e424525c8552ece3b3fe26516" + "de901bc7d515bde89558e626c95c80b93342f8010004f39e6c6c94871c5e344cab3966c835f9a96a59afd31c40286b38" + "b1c1a78470bab947518934453ce86736a919f1f5a6d510a86f5454fc3980cb5c765bd2bd5f7b36b1410d6635c8ceb47c" + "4dda0d76a28eac939c71c3024804866c71626658442163c2c22117e50acefce6378a985652302a4ef0c2ce0cc716b779" + "6e2b6b2e3777dfa1ac3da259a31b5a9b530f8cb638a81a62ac301849abaf95a7301bda30068909bfdb7e67dbccbb38a5" + "551a25b1a3a0f685748ad5753d8880f0016c627486166384c5571fe2365900364d038311e2d875db366686932b5ec602" + "430a369e87a6ef5c338786657825bd4c057aceb923eb0935e6905e63b4ced7f80857a773dd64b150d26612ea9ac12052" + "db2017bf1843ccb4b3281b690dc728adfa85c00281b8e3c09287335f856b4fc2892f69a2f57921ada01914c40988662d" + "57769662a786351b9b66493dab79594d986de2100d65ba0ff4ea58b81538d24a4435a258fac25404aa7f41f658b13850" + "65e158dcb60115732720f40459aaac15e406953a90ac52997d1ccd070060efc65db9e653354467fad56ec713c86e7540" + "c423acf2669f52fa6f4ac6888d871ef3e847c029a8aafbb92e17b24aa079b1f419ba6175b442afb11909d4a56b70a033" + "5b28739218aa7c9348e2c3c2f3eb3d15a41e6417c0dd94bfeb21419b311a7bb13a180bbe833218a9a6b17447cc85f225" + "859587a73077049acbcfd44d0f025438e15d1538270d586e1bf83192a9459cf63c0e972f85297679831ecf121509851c" + "b8340f6f107b0fa1a0efd1b36a8189bc085c4f5cb784e553f41b918f80397ce1956f785bee377ca9aa8be6998ada30c2" + "6b7c3d8c6b55254cc96203b20c42aee0ac4e1ebb408e49a9e3f879d0ab0785eb7025425d1305a2299c015e120d163b0e" + "19494ce57253d0246d182745cb8197ab7438b3c1bb7972bec5a306eba3567855c014699fef65ae54c770a0d85c18400c" + "f642aedc660777ba4b138502bd5a7812f621f84a48296b98dd4322b6f15828b8a8f0e00a8ba44a53c3a8b143571b0740" + "abd567daf1cde9c79c204b6d5e259d1766a31bbbcb4e6a05cf4502176b301c1c2f41247750157bcec85e809b30a4d60d" + "7747cdd0f5b99aa8c826987517793aaa8080a0b124a8558df72bbe37b75f4edbb6be8216d6c633fb2b2280e25113d869" + "5e43481c3eeb397eb192505229b67a201ea893c3e2cb32da8bc342fa4dea0578"; + +static const char mlkem768_dk_hex[] = + "27d2a77f33756f61208ef113abe82595873d4abc730e5b5d679529bf6a4ceb6383427231a8612f41550515acba52e48e" + "ad8b942833bbe6865d13d14a79d2c5c3e07f0a056d8de7aadfcaba058c493c80b37cab8c562753bb3ba6b6ec8297f885" + "eaa7540d530015a84406e55b1366b577e236ce58a26d8a1eb5a44d542323c2167d9bf4a47f985699ca05bae43b8dec61" + "7f02380a3890afd4b8c7ec7ede26553a025f3ce5bc5d7a62130304235cb1ad4836b566b5b863bd9bdb45a2844a7047b6" + "c8d383e448525e040b4dc8a2b48c6c37c96d62d43f3fd88e2881c40a205c9e248f652b592781a779f86880f2a147b678" + "63f391cc1a5a908c0095e07212291e2ef8a36eb9a9c0c6073225b34703a4af049382c47573da68fde9245ad444e31b1f" + "bdb521f1f61f37bc0cef292067e670d28a1ffd904f6f1190a996918a13037a6cabf3c373bf8296cd37ab33ba7746809c" + "c3f8ade1b3639bd57bfcc69650aaaf1de198fc4c0463299e52c461780cc428fc5d04a5c51850cba6c2a5274340675793" + "dda09be44c29e6395c65f85d2a0a7c6df411e6911b1f2cb6c351cd2e875f51b638be776097e93e2f2b2f83da0beef4aa" + "85ba9e763ab64502a0ca5222e9eab5b3b7088ed52060e8c8269b943a71ab0ae1c5b1b687d2e019cf8036bcf9bf6e7bac" + "3aaa36e41660faa4540f2648cd93a189ec5c2dea70bacaaa4ffc906f90810ea1b67bf24f2c78cf6ba881aaea61c0652b" + "ff95b1bae4426d1773b9cc2ca82c21e38c636e3b1c523244986b0be8a83f5dd5cf2d54762fb3c5ebf59b8e885302b1ce" + "47033edf760f4e029be40b6d566b19dd758acd5c7412878131244f90172c53f26663c21d905301d48baf91c917cc7779" + "e9d8802cc10d89a3705099a2ad3a3a8896743c1144698093be257dacb66dc785228b912c8d965d14aa28342c3ac4a93f" + "efa532b20945ddc1020139c14d638b908c4ddde9a0645b95b2e4414d40bb79f04413830f15a873c28bb7059c27410020" + "15f20408f058e715b0bf995b5380b7dd325a056ab97e659a2be0cdf6c33731c683a634b771e8c92a139aee4bb0e49c70" + "77321d42fc199f7c1f298ca625d223a5c263a03cc48159b7812665b78637e4e18720b2c29a6b99f42766a4cbc4dc508b" + "a94ba83b89c3a5c78f8bb26bbd9b79beb8c8182490f5793ee5b96013b74b7e169e29d162f1315464ea7d72436d89b755" + "161192c81cc2dd1c8b8bba795ef426ee1cc01c37aaa37b2cff8b0a378b47cbd0b4d49398cfc2712959699fa0bd8cd846" + "66acc61f541b84fa96b9c854e4e75e9144addb44b8566a57dfbb545ce423c03346f2b2c1a91780d152a8de1a4d4c9cac" + "de7392c996888cc2399c02c38b3353adf8acab283924da00a05b76e738c72c930d6cba09ae168990faa1fef2226e7808" + "61d416eff402f4f759fc648ab1f97100109087f96e4b148d2cb31e4805314ea0cd95fb023eac0d989474ba4201d7b41d" + "26f5394b217eea5b34b71a8b37931c0e594271e0b7c733257240233e7ba735603e425a87dee77079e37cb28a21764594" + "ce5350d8da2b62a07174943032ec89c98809c73b6423d30c1d283a766a64d89703c3d629b497828d48320c346210797a" + "298aa10d423c8dda069d02bc59e6cdf03a096b8b3da4cab9b80ca4a14907672ccef1ec4faf234a0bc5b7e9d473f2b313" + "3b3b26a1d175cb67a7805919699c02f76531b99c5f89180704bb4ca4535c5b8972679c660a07c5e514b87009c862eb8f" + "5157695efb3fc40a9def6b81c1cc02a249ae4f094ad0d9bd3485c1c1c68080520a7c8c632032cee738154e5c5176c07d" + "a56024776a430fe76eacf665a3f7b832102215bc82f10939c8355704336a8fac1d81e4bb0485aa5d7c74d6b59bbe5c5e" + "972a0d8bac411b55b5d5557cd680a1a8f71b4eb86bc48c9a0509731a54bd9d7290b27963e4372dc9b199cfdcac0b01ac" + "d28a62395112e4c43648d622c48c8234d01440e8cc376c927f23a5afc9ac0474c662274e424525c8552ece3b3fe26516" + "de901bc7d515bde89558e626c95c80b93342f8010004f39e6c6c94871c5e344cab3966c835f9a96a59afd31c40286b38" + "b1c1a78470bab947518934453ce86736a919f1f5a6d510a86f5454fc3980cb5c765bd2bd5f7b36b1410d6635c8ceb47c" + "4dda0d76a28eac939c71c3024804866c71626658442163c2c22117e50acefce6378a985652302a4ef0c2ce0cc716b779" + "6e2b6b2e3777dfa1ac3da259a31b5a9b530f8cb638a81a62ac301849abaf95a7301bda30068909bfdb7e67dbccbb38a5" + "551a25b1a3a0f685748ad5753d8880f0016c627486166384c5571fe2365900364d038311e2d875db366686932b5ec602" + "430a369e87a6ef5c338786657825bd4c057aceb923eb0935e6905e63b4ced7f80857a773dd64b150d26612ea9ac12052" + "db2017bf1843ccb4b3281b690dc728adfa85c00281b8e3c09287335f856b4fc2892f69a2f57921ada01914c40988662d" + "57769662a786351b9b66493dab79594d986de2100d65ba0ff4ea58b81538d24a4435a258fac25404aa7f41f658b13850" + "65e158dcb60115732720f40459aaac15e406953a90ac52997d1ccd070060efc65db9e653354467fad56ec713c86e7540" + "c423acf2669f52fa6f4ac6888d871ef3e847c029a8aafbb92e17b24aa079b1f419ba6175b442afb11909d4a56b70a033" + "5b28739218aa7c9348e2c3c2f3eb3d15a41e6417c0dd94bfeb21419b311a7bb13a180bbe833218a9a6b17447cc85f225" + "859587a73077049acbcfd44d0f025438e15d1538270d586e1bf83192a9459cf63c0e972f85297679831ecf121509851c" + "b8340f6f107b0fa1a0efd1b36a8189bc085c4f5cb784e553f41b918f80397ce1956f785bee377ca9aa8be6998ada30c2" + "6b7c3d8c6b55254cc96203b20c42aee0ac4e1ebb408e49a9e3f879d0ab0785eb7025425d1305a2299c015e120d163b0e" + "19494ce57253d0246d182745cb8197ab7438b3c1bb7972bec5a306eba3567855c014699fef65ae54c770a0d85c18400c" + "f642aedc660777ba4b138502bd5a7812f621f84a48296b98dd4322b6f15828b8a8f0e00a8ba44a53c3a8b143571b0740" + "abd567daf1cde9c79c204b6d5e259d1766a31bbbcb4e6a05cf4502176b301c1c2f41247750157bcec85e809b30a4d60d" + "7747cdd0f5b99aa8c826987517793aaa8080a0b124a8558df72bbe37b75f4edbb6be8216d6c633fb2b2280e25113d869" + "5e43481c3eeb397eb192505229b67a201ea893c3e2cb32da8bc342fa4dea0578a24e16d8f8f9383a95b77050f4d9fd2f" + "5733eec1d63ef3c23ebf9918173669a7202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"; + +static const char mlkem768_ct_hex[] = + "695a60d9c79f08343ed9ff5802582063c2ca3a648e543d924affbb39ef4de656591f0d7689e6626be7ea7fedaf134e2c" + "27c6797c73a5edaf16808f141c8afcf31614e8ab665379573e4d0a2037cbf776048167ba53576001a2596402cf24b5d4" + "5362bc893ceaef3599f76b10812e626002e66db5c5b0f2b9a7080e32db68dcc8d04c24f8461a58bb7e47efe670d740ad" + "8af9820033845ef5f880f26f0e00adb2abef876f5270477ebbb02de6787ce72ca8785fb181f46c3ff7ae3787c25c68cc" + "ceefb3551875b9d77c4d439b6050eb382aacf9e744227e8c46e0a9a55838ea7034f5b4bcb61f1023a80186e795f4b3d8" + "ae93988994224fa2d83e21711670da01e2b3e272f81616c0bc88cc46f641d16e0d0c0924cf4a4a5c1a9128c226d4918a" + "a39bef94199dfffa33876ef0bfa0d9560d25f5ba08068d5271f32d2f9d88bcf53c7dcf811a8d5efe617f5e05700d3478" + "d3cb7932528d1bceb240198a4cf8752caea3d387f00759a1356b7a5bf1838d26c3573e92e69f0f57c06e8c25459eb83e" + "12cdd75f541a81ce710eafce2984783f30e37b327ff93b72297c6cd8c78c185ad53864952069d7d6c3bc633ae5e1a592" + "5855df0b7e714bbde245f68822e0950c23c96d6111753a6ed0c46cce437f53b6bb708c1a3e25979733198d9879e3237e" + "769471f922e579f37cfd641d29bdcfdbaa81edae09aeb046366e0376d04282d17778a8d54774e8c9be3c822b1e90cd88" + "95abc1db8951b7687f63fee50ec43faf23730b15189e7c982b22d896a972da3c2ee529bb5fe63630c9c2ddfb9d1e4263" + "a3d49af2832053d97efa2bd1782f25d7b864d6fb3708bfb9d4bc6c2cc6458d4f1459995db387e8b503825a4496c73525" + "2aa630a1bcaa7a2674727396dcaf67030b53473951651dc26c22476bfd11d33206af0ff035ed035e34716c905e8ddf04" + "3a4cdae145238d8f612dbcb75e879653bb9e2657dab58b944ff34f977fe15ce907f6814a5f92338774e6f2ab5257d249" + "17decdd158c6d4594189f42a9b7fa9159a8af6aa825ba904654e08c894901298ffb27239ddea8283dd45b876036c0aec" + "f03583ba444529757444c857fff6e4f8ed48f8a180adea54979a678f16dc6ac8edcc8e72ed08e96082f0ff4520dc635d" + "4a846a3026fd86a48b1297e0cdfc06008793e783bde1c3fc6a71871e66b1feb560495817aabbdc59f0149f3e76add9b5" + "bd6ce34734de7593ed607efb84c6e732960c744c908a9cb8947375a55b55fa2f0cd6742b75c10f65522d3844bed9b05b" + "d441bbbea17cfbabdaef9847a0edd9c8329a762e34e5396014d88b4d344f250aaddefd917bb2120d1169c79cb09f59ba" + "d21850752c1099fff98b71bcdaab76f7063323e78faa521cd243f74ddc7f7775aa79960622e13580a6831e69bb7f2321" + "d141d35da88317719078d4db319f308594c26836503f62362c40005022937c1298a928c040879661349a7b5362d0a75f" + "2893b97a2600d5337239a70a6b64a457e6dfd5c74d462e7e790bb9ef3cee1461"; + +static const char mlkem768_ss_hex[] = + "9cddd089ffe70e3996e76f7c8d06746df34d07e8657bc0fcf2bb0e1c3084aea1";