Update Kyber-512 to ML-KEM-768

This commit is contained in:
Zhi Guan
2026-06-24 22:35:01 +08:00
parent 09c96fa2e0
commit d8ce43ffef
8 changed files with 742 additions and 63 deletions

View File

@@ -617,7 +617,7 @@ endif()
if (ENABLE_KYBER) if (ENABLE_KYBER)
message(STATUS "ENABLE_KYBER is ON") message(STATUS "ENABLE_KYBER is ON")
add_definitions(-DENABLE_KYBER) 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 tools tools/kyberkeygen.c tools/kyberencap.c tools/kyberdecap.c)
list(APPEND tests kyber) list(APPEND tests kyber)
endif() endif()
@@ -1016,7 +1016,7 @@ endif()
# #
set(CPACK_PACKAGE_NAME "GmSSL") set(CPACK_PACKAGE_NAME "GmSSL")
set(CPACK_PACKAGE_VENDOR "GmSSL develop team") 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_PACKAGE_DESCRIPTION_FILE ${PROJECT_SOURCE_DIR}/README.md)
set(CPACK_NSIS_MODIFY_PATH ON) set(CPACK_NSIS_MODIFY_PATH ON)
include(CPack) include(CPack)

View File

@@ -42,13 +42,13 @@ extern "C" {
#define KYBER1024_DU 11 #define KYBER1024_DU 11
#define KYBER512_DV 4 #define KYBER512_DV 4
#define KYBER769_DV 4 #define KYBER768_DV 4
#define KYBER1024_DV 5 #define KYBER1024_DV 5
#define KYBER_K KYBER512_K #define KYBER_K KYBER768_K
#define KYBER_ETA1 KYBER512_ETA1 #define KYBER_ETA1 KYBER768_ETA1
#define KYBER_DU KYBER512_DU #define KYBER_DU KYBER768_DU
#define KYBER_DV KYBER512_DV #define KYBER_DV KYBER768_DV
#define KYBER_C1_SIZE ((256 * KYBER_DU)/8) #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_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_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); void kyber_poly_set_zero(kyber_poly_t r);
int kyber_poly_rand(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(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_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); 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); 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) #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_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_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_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); 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_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_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_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]); int kyber_decap(const KYBER_KEY *sk, const KYBER_CIPHERTEXT *c, uint8_t K[32]);

48
include/gmssl/sha3.h Normal file
View File

@@ -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 <stddef.h>
#include <stdint.h>
#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

View File

@@ -18,7 +18,7 @@ extern "C" {
#define GMSSL_VERSION_NUM 30300 #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); int gmssl_version_num(void);
const char *gmssl_version_str(void); const char *gmssl_version_str(void);

View File

@@ -14,7 +14,9 @@
#include <gmssl/hex.h> #include <gmssl/hex.h>
#include <gmssl/mem.h> #include <gmssl/mem.h>
#include <gmssl/rand.h> #include <gmssl/rand.h>
#include <gmssl/sm3.h>
#include <gmssl/hkdf.h> #include <gmssl/hkdf.h>
#include <gmssl/sha3.h>
#include <gmssl/error.h> #include <gmssl/error.h>
#include <gmssl/endian.h> #include <gmssl/endian.h>
#include <gmssl/kyber.h> #include <gmssl/kyber.h>
@@ -22,13 +24,29 @@
void kyber_h_hash(const uint8_t *in, size_t inlen, uint8_t out[32]) void kyber_h_hash(const uint8_t *in, size_t inlen, uint8_t out[32])
{ {
SM3_CTX ctx; sha3_256(in, inlen, out);
sm3_init(&ctx);
sm3_update(&ctx, in, inlen);
sm3_finish(&ctx, out);
} }
void kyber_g_hash(const uint8_t *in, size_t inlen, uint8_t out[64]) 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; SM3_CTX ctx;
uint8_t ctr[4] = {0}; 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, in, inlen);
sm3_update(&ctx, ctr, 4); sm3_update(&ctx, ctr, 4);
sm3_finish(&ctx, out + 32); 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) static int kyber_prf(const uint8_t seed[32], uint8_t N, size_t outlen, uint8_t *out)
{ {
uint8_t salt[1]; uint8_t in[33];
uint8_t key[32];
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) { if (sm3_hkdf_extract(NULL, 0, seed, 32, key) != 1) {
error_print(); error_print();
return -1; 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; 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]; uint8_t key[32];
sm3_hkdf_extract(NULL, 0, in, 64, key);
sm3_hkdf_expand(key, NULL, 0, 32, out); if (sm3_hkdf_extract(NULL, 0, in, 64, key) != 1) {
gmssl_secure_clear(key, 32); 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; 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) 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; SM3_CTX ctx;
uint8_t seed[32 + 2 + 4]; 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: end:
gmssl_secure_clear(&ctx, sizeof(ctx));
gmssl_secure_clear(seed, sizeof(seed));
gmssl_secure_clear(rand, sizeof(rand));
return 1; 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)); gmssl_secure_clear(bytes, sizeof(bytes));
} else {
error_print();
return -1;
} }
for (i = 0; i < 256; i++) { 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 e[KYBER_K];
kyber_poly_t t[KYBER_K]; kyber_poly_t t[KYBER_K];
uint8_t d[64]; uint8_t d[64];
uint8_t g_in[33];
uint8_t *rho = d; uint8_t *rho = d;
uint8_t *sigma = d + 32; uint8_t *sigma = d + 32;
uint8_t N = 0; uint8_t N = 0;
int i,j; int i,j;
if (random) { if (random) {
memcpy(d, random, 32); memcpy(g_in, random, 32);
} else { } else {
if (rand_bytes(d, 32) != 1) { if (rand_bytes(g_in, 32) != 1) {
error_print(); error_print();
return -1; 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)) // AHat[i][j] = Parse(XOR(rho, j, i))
for (i = 0; i < KYBER_K; 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); memcpy(key->public_key.rho, rho, 32);
gmssl_secure_clear(d, sizeof(d)); gmssl_secure_clear(d, sizeof(d));
gmssl_secure_clear(g_in, sizeof(g_in));
gmssl_secure_clear(s, sizeof(s)); gmssl_secure_clear(s, sizeof(s));
gmssl_secure_clear(e, sizeof(e)); 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) 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) { if (!key || !in || !(*in) || !inlen) {
error_print(); error_print();
return -1; return -1;
@@ -685,13 +783,19 @@ int kyber_cpa_public_key_from_bytes(KYBER_CPA_KEY *key, const uint8_t **in, size
error_print(); error_print();
return -1; return -1;
} }
memset(key, 0, sizeof(*key));
memcpy(key->public_key.t, *in, sizeof(key->public_key.t)); memcpy(key->public_key.t, *in, sizeof(key->public_key.t));
*in += sizeof(key->public_key.t); *in += sizeof(key->public_key.t);
*inlen -= sizeof(key->public_key.t); *inlen -= sizeof(key->public_key.t);
memcpy(key->public_key.rho, *in, sizeof(key->public_key.rho)); memcpy(key->public_key.rho, *in, sizeof(key->public_key.rho));
*in += sizeof(key->public_key.rho); *in += sizeof(key->public_key.rho);
*inlen -= 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; return 1;
} }
@@ -701,15 +805,15 @@ int kyber_cpa_private_key_to_bytes(const KYBER_CPA_KEY *key, uint8_t **out, size
error_print(); error_print();
return -1; return -1;
} }
if (kyber_cpa_public_key_to_bytes(key, out, outlen) != 1) {
error_print();
return -1;
}
if (out && *out) { if (out && *out) {
memcpy(*out, key->s, sizeof(key->s)); memcpy(*out, key->s, sizeof(key->s));
*out += sizeof(key->s); *out += sizeof(key->s);
} }
*outlen += sizeof(key->s); *outlen += sizeof(key->s);
if (kyber_cpa_public_key_to_bytes(key, out, outlen) != 1) {
error_print();
return -1;
}
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; return -1;
} }
memset(key, 0, sizeof(*key)); 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)) { if (*inlen < sizeof(key->s)) {
error_print(); error_print();
return -1; 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)); memcpy(key->s, *in, sizeof(key->s));
*in += sizeof(key->s); *in += sizeof(key->s);
*inlen -= sizeof(key->s); *inlen -= sizeof(key->s);
if (kyber_cpa_public_key_from_bytes(key, in, inlen) != 1) {
error_print();
return -1;
}
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]); kyber_poly_encode10(u[i], out->c1[i]);
} }
// c2 = Encode4(Compress(v, 4)) // c2 = Encode4(Compress(v, KYBER_DV))
kyber_poly_compress(v, 4, v); kyber_poly_compress(v, KYBER_DV, v);
kyber_poly_encode4(v, out->c2); kyber_poly_encode4(v, out->c2);
gmssl_secure_clear(m, sizeof(m)); 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) // v = Decompress(Decode_dv(c2), dv)
kyber_poly_decode4(v, in->c2); kyber_poly_decode4(v, in->c2);
kyber_poly_decompress(v, 4, v); kyber_poly_decompress(v, KYBER_DV, v);
// s = Decode_12(sk) // 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]) int kyber_key_generate_ex(KYBER_KEY *key, const uint8_t random[32])
{ {
uint8_t seed[64];
if (!key) { if (!key) {
error_print(); error_print();
return -1; 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(); error_print();
return -1; return -1;
} }
kyber_h_hash((uint8_t *)key, sizeof(KYBER_CPA_PUBLIC_KEY), key->pk_hash); if (rand_bytes(seed + 32, 32) != 1) {
if (rand_bytes(key->z, 32) != 1) { gmssl_secure_clear(seed, sizeof(seed));
gmssl_secure_clear(&key->cpa_key, sizeof(KYBER_CPA_KEY));
error_print(); error_print();
return -1; 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; 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) 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) { if (!key || !in || !(*in) || !inlen) {
error_print(); error_print();
return -1; 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)); memcpy(key->z, *in, sizeof(key->z));
*in += sizeof(key->z); *in += sizeof(key->z);
*inlen -= 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; return 1;
} }
@@ -1101,60 +1241,86 @@ int kyber_private_key_print(FILE *fp, int fmt, int ind, const char *label, const
return 1; 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 m_h[64];
uint8_t K_r[64]; uint8_t K_r[64];
uint8_t *m = m_h;
uint8_t *h = m_h + 32; uint8_t *h = m_h + 32;
uint8_t *K_ = K_r; uint8_t *K_ = K_r;
uint8_t *r = K_r + 32; uint8_t *r = K_r + 32;
// m = rand(32) if (!key || !m || !c || !K) {
if (rand_bytes(m, 32) != 1) {
error_print(); error_print();
return -1; return -1;
} }
// m = H(m) memcpy(m_h, m, 32);
kyber_h_hash(m, 32, m);
// h = H(pk) // 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)) // (K_, r) = G(m || H(pk))
kyber_g_hash(m_h, 64, K_r); kyber_g_hash(m_h, 64, K_r);
// c = Kyber.CPA.Enc(pk, m, r) // c = Kyber.CPA.Enc(pk, m, r)
if (kyber_cpa_encrypt(&key->cpa_key, m, r, c) != 1) { 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(); error_print();
return -1; return -1;
} }
// H(c) memcpy(K, K_, 32);
kyber_h_hash((uint8_t *)c, sizeof(KYBER_CIPHERTEXT), r);
// K = KDF(K_ || H(c))
kyber_kdf(K_r, K);
gmssl_secure_clear(m_h, sizeof(m_h)); gmssl_secure_clear(m_h, sizeof(m_h));
gmssl_secure_clear(K_r, sizeof(K_r)); gmssl_secure_clear(K_r, sizeof(K_r));
return 1; 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]) int kyber_decap(const KYBER_KEY *key, const KYBER_CIPHERTEXT *c, uint8_t K[32])
{ {
uint8_t m_h[64]; uint8_t m_h[64];
uint8_t K_r[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 *m = m_h;
uint8_t *h = m_h + 32; uint8_t *h = m_h + 32;
uint8_t *K_ = K_r;
uint8_t *r = K_r + 32; uint8_t *r = K_r + 32;
KYBER_CIPHERTEXT c_; KYBER_CIPHERTEXT c_;
uint8_t mask;
int diff;
int i;
if (!key || !c || !K) {
error_print();
return -1;
}
// m' = Dec(sk, c) // m' = Dec(sk, c)
if (kyber_cpa_decrypt(&key->cpa_key, c, m) != 1) { 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(); error_print();
return -1; 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) { if (kyber_cpa_encrypt(&key->cpa_key, m, r, &c_) != 1) {
gmssl_secure_clear(m_h, sizeof(m_h)); gmssl_secure_clear(m_h, sizeof(m_h));
gmssl_secure_clear(K_r, sizeof(K_r)); 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(); error_print();
return -1; return -1;
} }
// H(c) memcpy(z_c, key->z, 32);
kyber_h_hash((uint8_t *)c, sizeof(KYBER_CIPHERTEXT), r); 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) { memcpy(K, K_r, 32);
// K = KDF(K_||H(c)) diff = gmssl_secure_memcmp(c, &c_, sizeof(KYBER_CIPHERTEXT));
kyber_kdf(K_r, K); mask = (uint8_t)(0 - (uint8_t)(diff == 0));
} else { for (i = 0; i < 32; i++) {
error_print(); K[i] = (K[i] & mask) | (Kbar[i] & ~mask);
memcpy(K_r, key->z, 32); // TODO: const time
kyber_kdf(K_r, K);
} }
gmssl_secure_clear(m_h, sizeof(m_h)); gmssl_secure_clear(m_h, sizeof(m_h));
gmssl_secure_clear(K_r, sizeof(K_r)); 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; return 1;
} }

247
src/sha3.c Normal file
View File

@@ -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 <string.h>
#include <gmssl/mem.h>
#include <gmssl/sha3.h>
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);
}

View File

@@ -15,6 +15,21 @@
#include <gmssl/error.h> #include <gmssl/error.h>
#include <gmssl/kyber.h> #include <gmssl/kyber.h>
#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) static int test_kyber_poly_uniform_sample(void)
{ {
@@ -506,6 +521,91 @@ static int test_kyber_kem(void)
return 1; 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) static int test_kyber_cpa_key_to_bytes(void)
{ {
KYBER_CPA_KEY key; KYBER_CPA_KEY key;
@@ -652,6 +752,7 @@ int main(void)
if (test_kyber_key_to_bytes() != 1) goto err; if (test_kyber_key_to_bytes() != 1) goto err;
if (test_kyber_cpa_ciphertext_to_bytes() != 1) goto err; if (test_kyber_cpa_ciphertext_to_bytes() != 1) goto err;
if (test_kyber_kem() != 1) goto err; if (test_kyber_kem() != 1) goto err;
if (test_mlkem768_vector() != 1) goto err;
printf("%s all tests passed\n", __FILE__); printf("%s all tests passed\n", __FILE__);
return 0; return 0;
@@ -659,4 +760,3 @@ err:
error_print(); error_print();
return 1; return 1;
} }

108
tests/kybertest_mlkem768.h Normal file
View File

@@ -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";