From 006570f7104f0775187b90801bbb8c775ccf52f1 Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Wed, 2 Sep 2026 20:36:23 -0700 Subject: [PATCH 1/2] Add CB_ONLY mode for ML-KEM --- .github/workflows/cryptocb-only.yml | 8 +- configure.ac | 4 + tests/api/test_mlkem.c | 18 +- wolfcrypt/src/cryptocb.c | 1 + wolfcrypt/src/port/arm/armv8-32-mlkem-asm.S | 4 +- wolfcrypt/src/port/arm/armv8-32-mlkem-asm_c.c | 4 +- wolfcrypt/src/port/arm/armv8-mlkem-asm.S | 4 +- wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c | 4 +- wolfcrypt/src/port/arm/thumb2-mlkem-asm.S | 4 +- wolfcrypt/src/port/arm/thumb2-mlkem-asm_c.c | 4 +- wolfcrypt/src/wc_mlkem.c | 48 ++- wolfcrypt/src/wc_mlkem_asm.S | 12 +- wolfcrypt/src/wc_mlkem_poly.c | 235 +++++++++- wolfcrypt/test/test.c | 402 +++++++++++++++++- wolfssl/wolfcrypt/settings.h | 11 + wolfssl/wolfcrypt/wc_mlkem.h | 13 + 16 files changed, 747 insertions(+), 29 deletions(-) diff --git a/.github/workflows/cryptocb-only.yml b/.github/workflows/cryptocb-only.yml index 12a23a84494..350ac1876e7 100644 --- a/.github/workflows/cryptocb-only.yml +++ b/.github/workflows/cryptocb-only.yml @@ -136,9 +136,15 @@ jobs: {"name": "slhdsa-verify-only", "minutes": 2, "comment": "Same strip with signing compiled out (WOLFSSL_SLHDSA_VERIFY_ONLY). swdev has to build against the verify-only header, which omits key generation, signing and CheckKey, and the dispatcher has to stop offering those operations. Verification alone is quick, so this costs little beside the 'slhdsa' entry above.", "configure": ["--enable-slhdsa=yes,sha2,verify-only", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_SLHDSA"]}, + {"name": "mlkem", "minutes": 3.0, + "comment": "WOLF_CRYPTO_CB_ONLY_MLKEM: strips the ML-KEM lattice math (key generation, encapsulation, decapsulation, the NTT, matrix generation, noise sampling and compression, plus the x86 and ARM assembly). The encode/decode helpers and the hash/PRF object lifecycle stay, because a callback that returns key material needs them. Built with --enable-cryptonly: TLS 1.3 offers ML-KEM key shares by default, and with the software path gone every such handshake needs a registered device, which no test device provides yet.", + "configure": ["--enable-cryptonly", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_MLKEM"]}, {"name": "all", "minutes": 7, - "comment": "All nine ONLY_* macros at once: every supported software primitive is stripped and dispatched through cryptocb. Catches any cross-algorithm call that a single-strip entry would still resolve via the remaining software paths.", + "comment": "All nine TLS-compatible ONLY_* macros at once: every supported software primitive is stripped and dispatched through cryptocb. Catches any cross-algorithm call that a single-strip entry would still resolve via the remaining software paths. MLKEM is not here because it needs --enable-cryptonly; the all-cryptonly entry below covers it alongside the rest.", "configure": ["--enable-slhdsa=yes,sha2", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ECC -DWOLF_CRYPTO_CB_ONLY_RSA -DWOLF_CRYPTO_CB_ONLY_SHA256 -DWOLF_CRYPTO_CB_ONLY_SHA512 -DWOLF_CRYPTO_CB_ONLY_AES -DWOLF_CRYPTO_CB_ONLY_ED25519 -DWOLF_CRYPTO_CB_ONLY_CURVE25519 -DWOLF_CRYPTO_CB_ONLY_CURVE448 -DWOLF_CRYPTO_CB_ONLY_SLHDSA"]}, + {"name": "all-cryptonly", "minutes": 3.2, + "comment": "The 'all' set plus MLKEM, built --enable-cryptonly so ML-KEM can join. This is the only entry where a stripped ML-KEM meets the other stripped primitives; TLS is off because TLS 1.3 offers ML-KEM key shares by default and no test device services them.", + "configure": ["--enable-cryptonly", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ECC -DWOLF_CRYPTO_CB_ONLY_RSA -DWOLF_CRYPTO_CB_ONLY_SHA256 -DWOLF_CRYPTO_CB_ONLY_SHA512 -DWOLF_CRYPTO_CB_ONLY_AES -DWOLF_CRYPTO_CB_ONLY_ED25519 -DWOLF_CRYPTO_CB_ONLY_CURVE25519 -DWOLF_CRYPTO_CB_ONLY_CURVE448 -DWOLF_CRYPTO_CB_ONLY_MLKEM"]}, {"name": "only", "minutes": 4.0, "comment": "Same coverage as the \"all\" entry above, but driven by ./configure --enable-cryptocb=only instead of a hand-written CPPFLAGS list. This is the regression test for the configure option: it must emit exactly the WOLF_CRYPTO_CB_ONLY_* set that \"all\" passes by hand, for the algorithms this base enables. The \"all\" entry deliberately stays on explicit CPPFLAGS so a bug in the configure logic cannot silently weaken both. Note the base already passes --enable-cryptocb; this entry's flags are appended after the base, so --enable-cryptocb=only wins.", "configure": ["--enable-cryptocb=only"]} diff --git a/configure.ac b/configure.ac index c1b4212bc24..0b2be316aec 100644 --- a/configure.ac +++ b/configure.ac @@ -11776,6 +11776,10 @@ then if test "$ENABLED_SLHDSA" != "no"; then AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_SLHDSA" fi + # TLS 1.3 offers ML-KEM key shares by default + if test "$ENABLED_MLKEM" != "no" && test "$ENABLED_TLS" = "no"; then + AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_MLKEM" + fi fi if test "$ENABLED_CRYPTOCB_SW_TEST" = "no" diff --git a/tests/api/test_mlkem.c b/tests/api/test_mlkem.c index 4a8c3c0001c..2e90b989f58 100644 --- a/tests/api/test_mlkem.c +++ b/tests/api/test_mlkem.c @@ -40,6 +40,7 @@ int test_wc_mlkem_make_key_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLKEM) && \ + defined(WC_MLKEM_HAVE_NATIVE) && \ !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) MlKemKey* key; #ifndef WOLFSSL_NO_ML_KEM_512 @@ -1499,6 +1500,7 @@ int test_wc_mlkem_encapsulate_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLKEM) && \ + defined(WC_MLKEM_HAVE_NATIVE) && \ !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) MlKemKey* key; #ifndef WOLFSSL_NO_ML_KEM_512 @@ -2473,6 +2475,7 @@ int test_wc_mlkem_decapsulate_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLKEM) && \ + defined(WC_MLKEM_HAVE_NATIVE) && \ !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) MlKemKey* key; #ifndef WOLFSSL_NO_ML_KEM_512 @@ -3886,7 +3889,8 @@ int test_wc_mlkem_decapsulate_kats(void) int test_wc_mlkem_decapsulate_pubonly_fails(void) { EXPECT_DECLS; -#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) +#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \ + defined(WC_MLKEM_HAVE_NATIVE) #if defined(WOLFSSL_HAVE_MLKEM) && \ !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \ !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \ @@ -3963,7 +3967,8 @@ int test_wc_mlkem_decapsulate_pubonly_fails(void) int test_wc_mlkem_decap_fo_reject(void) { EXPECT_DECLS; -#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0) +#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \ + defined(WC_MLKEM_HAVE_NATIVE) #if defined(WOLFSSL_HAVE_MLKEM) && \ !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \ !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \ @@ -4038,7 +4043,8 @@ int test_wc_mlkem_decap_fo_reject(void) int test_wc_mlkem_decode_privkey_bad_pubhash(void) { EXPECT_DECLS; -#if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) +#if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) && \ + defined(WC_MLKEM_HAVE_NATIVE) #if defined(WOLFSSL_HAVE_MLKEM) && \ !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) MlKemKey* key = NULL; @@ -4105,6 +4111,7 @@ int test_wc_mlkem_decode_privkey_bad_pubhash(void) *****************************************************************************/ #if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) && \ + defined(WC_MLKEM_HAVE_NATIVE) && \ !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) @@ -4209,6 +4216,7 @@ int test_wc_MlkemFeatureCoverage(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) && \ + defined(WC_MLKEM_HAVE_NATIVE) && \ !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) @@ -4228,7 +4236,8 @@ int test_wc_MlkemFeatureCoverage(void) int test_wc_MlkemDecisionCoverage(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) && \ + defined(WC_MLKEM_HAVE_NATIVE) MlKemKey* key = NULL; #ifndef WC_NO_CONSTRUCTORS MlKemKey* newKey = NULL; @@ -4604,6 +4613,7 @@ int test_wc_mlkem_encode_key_len_decision(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) && \ + defined(WC_MLKEM_HAVE_NATIVE) && \ !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) MlKemKey* key; WC_RNG rng; diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index 8783a1684fb..a0b61ce2478 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -66,6 +66,7 @@ Crypto Callback Build Options: * WOLF_CRYPTO_CB_ONLY_AES: Use only callbacks for AES default: off * WOLF_CRYPTO_CB_ONLY_ED25519: Use only callbacks for Ed25519 default: off * WOLF_CRYPTO_CB_ONLY_CURVE25519: Use only callbacks for X25519 default: off + * WOLF_CRYPTO_CB_ONLY_MLKEM: Use only callbacks for ML-KEM default: off */ #include diff --git a/wolfcrypt/src/port/arm/armv8-32-mlkem-asm.S b/wolfcrypt/src/port/arm/armv8-32-mlkem-asm.S index 1cfe370d0e1..0be10942133 100644 --- a/wolfcrypt/src/port/arm/armv8-32-mlkem-asm.S +++ b/wolfcrypt/src/port/arm/armv8-32-mlkem-asm.S @@ -33,7 +33,7 @@ #ifdef WOLFSSL_ARMASM #if !defined(__aarch64__) && !defined(WOLFSSL_ARMASM_THUMB2) #ifndef WOLFSSL_ARMASM_INLINE -#ifdef WOLFSSL_HAVE_MLKEM +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) #ifndef __APPLE__ .text .type L_mlkem_arm32_ntt_zetas, %object @@ -8516,7 +8516,7 @@ L_mlkem_arm32_rej_uniform_done: lsr r0, r12, #1 pop {r4, r5, r6, r7, r8, pc} .size mlkem_arm32_rej_uniform,.-mlkem_arm32_rej_uniform -#endif /* WOLFSSL_HAVE_MLKEM */ +#endif /* WOLFSSL_HAVE_MLKEM && !WOLF_CRYPTO_CB_ONLY_MLKEM */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits diff --git a/wolfcrypt/src/port/arm/armv8-32-mlkem-asm_c.c b/wolfcrypt/src/port/arm/armv8-32-mlkem-asm_c.c index df668bfb45f..51a23ae4d47 100644 --- a/wolfcrypt/src/port/arm/armv8-32-mlkem-asm_c.c +++ b/wolfcrypt/src/port/arm/armv8-32-mlkem-asm_c.c @@ -52,7 +52,7 @@ #include -#ifdef WOLFSSL_HAVE_MLKEM +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) XALIGNED(4) static const word16 L_mlkem_arm32_ntt_zetas[] = { 0x08ed, 0x0a0b, 0x0b9a, 0x0714, 0x05d5, 0x058e, 0x011f, 0x00ca, 0x0c56, 0x026e, 0x0629, 0x00b6, 0x03c2, 0x084f, 0x073f, 0x05bc, @@ -8666,7 +8666,7 @@ WC_OMIT_FRAME_POINTER unsigned int mlkem_arm32_rej_uniform(sword16* p, return (word32)(size_t)p; } -#endif /* WOLFSSL_HAVE_MLKEM */ +#endif /* WOLFSSL_HAVE_MLKEM && !WOLF_CRYPTO_CB_ONLY_MLKEM */ #endif /* WOLFSSL_ARMASM_INLINE */ #endif /* !__aarch64__ && !WOLFSSL_ARMASM_THUMB2 */ diff --git a/wolfcrypt/src/port/arm/armv8-mlkem-asm.S b/wolfcrypt/src/port/arm/armv8-mlkem-asm.S index 14010de9035..ed76d0e8091 100644 --- a/wolfcrypt/src/port/arm/armv8-mlkem-asm.S +++ b/wolfcrypt/src/port/arm/armv8-mlkem-asm.S @@ -49,7 +49,7 @@ #endif /* __APPLE__ */ L_mlkem_aarch64_consts: .short 0x0d01,0xf301,0x4ebf,0x0549,0x5049,0x0000,0x0000,0x0000 -#ifdef WOLFSSL_HAVE_MLKEM +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) #ifndef __APPLE__ .text .section .rodata @@ -12071,7 +12071,7 @@ L_SHA3_shake256_blocksx3_seed_neon_begin: #ifndef __APPLE__ .size mlkem_shake256_blocksx3_seed_neon,.-mlkem_shake256_blocksx3_seed_neon #endif /* __APPLE__ */ -#endif /* WOLFSSL_HAVE_MLKEM */ +#endif /* WOLFSSL_HAVE_MLKEM && !WOLF_CRYPTO_CB_ONLY_MLKEM */ #endif /* __aarch64__ */ #endif /* WOLFSSL_ARMASM */ diff --git a/wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c b/wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c index 2b1f5b40b0f..f9db20e91d4 100644 --- a/wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c +++ b/wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c @@ -40,7 +40,7 @@ XALIGNED(4) static const word16 L_mlkem_aarch64_consts[] = { #include -#ifdef WOLFSSL_HAVE_MLKEM +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) XALIGNED(4) static const word16 L_mlkem_aarch64_zetas[] = { 0x08ed, 0x0a0b, 0x0b9a, 0x0714, 0x05d5, 0x058e, 0x011f, 0x00ca, 0x0c56, 0x026e, 0x0629, 0x00b6, 0x03c2, 0x084f, 0x073f, 0x05bc, @@ -11258,7 +11258,7 @@ void mlkem_shake256_blocksx3_seed_neon(word64* state, byte* seed) ); } -#endif /* WOLFSSL_HAVE_MLKEM */ +#endif /* WOLFSSL_HAVE_MLKEM && !WOLF_CRYPTO_CB_ONLY_MLKEM */ #endif /* __aarch64__ */ #endif /* WOLFSSL_ARMASM */ #endif /* WOLFSSL_ARMASM_INLINE */ diff --git a/wolfcrypt/src/port/arm/thumb2-mlkem-asm.S b/wolfcrypt/src/port/arm/thumb2-mlkem-asm.S index e71b9311ed5..ea12e5f8d5a 100644 --- a/wolfcrypt/src/port/arm/thumb2-mlkem-asm.S +++ b/wolfcrypt/src/port/arm/thumb2-mlkem-asm.S @@ -35,7 +35,7 @@ #ifndef WOLFSSL_ARMASM_INLINE .thumb .syntax unified -#ifdef WOLFSSL_HAVE_MLKEM +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) #ifndef __APPLE__ .text .type L_mlkem_thumb2_ntt_zetas, %object @@ -3591,7 +3591,7 @@ L_mlkem_thumb2_rej_uniform_done: POP {r4, r5, r6, r7, r8, r9, r10, pc} /* Cycle Count = 225 */ .size mlkem_thumb2_rej_uniform,.-mlkem_thumb2_rej_uniform -#endif /* WOLFSSL_HAVE_MLKEM */ +#endif /* WOLFSSL_HAVE_MLKEM && !WOLF_CRYPTO_CB_ONLY_MLKEM */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits diff --git a/wolfcrypt/src/port/arm/thumb2-mlkem-asm_c.c b/wolfcrypt/src/port/arm/thumb2-mlkem-asm_c.c index a0ee89a5fa1..710d33dba60 100644 --- a/wolfcrypt/src/port/arm/thumb2-mlkem-asm_c.c +++ b/wolfcrypt/src/port/arm/thumb2-mlkem-asm_c.c @@ -52,7 +52,7 @@ #include -#ifdef WOLFSSL_HAVE_MLKEM +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) XALIGNED(4) static const word16 L_mlkem_thumb2_ntt_zetas[] = { 0x08ed, 0x0a0b, 0x0b9a, 0x0714, 0x05d5, 0x058e, 0x011f, 0x00ca, 0x0c56, 0x026e, 0x0629, 0x00b6, 0x03c2, 0x084f, 0x073f, 0x05bc, @@ -3889,7 +3889,7 @@ WC_OMIT_FRAME_POINTER unsigned int mlkem_thumb2_rej_uniform(sword16* p, return (word32)(size_t)p; } -#endif /* WOLFSSL_HAVE_MLKEM */ +#endif /* WOLFSSL_HAVE_MLKEM && !WOLF_CRYPTO_CB_ONLY_MLKEM */ #endif /* WOLFSSL_ARMASM_INLINE */ #endif /* WOLFSSL_ARMASM_THUMB2 */ diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index 3fe0409ca55..971510d757c 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -205,6 +205,7 @@ sword16 wc_mlkem_opt_blocker(void) { * @param [in] key ML-KEM key object. * @return k value for the key type, or 0 if not recognized. */ +#ifndef WOLF_CRYPTO_CB_ONLY_MLKEM static int mlkemkey_get_k(const MlKemKey* key) { switch (key->type) { @@ -240,6 +241,7 @@ static int mlkemkey_get_k(const MlKemKey* key) return 0; } } +#endif /* !WOLF_CRYPTO_CB_ONLY_MLKEM */ #endif #ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS @@ -779,6 +781,17 @@ int wc_MlKemKey_MakeKey(MlKemKey* key, WC_RNG* rng) int wc_MlKemKey_MakeKeyWithRandom(MlKemKey* key, const unsigned char* rand, int len) { +#ifdef WOLF_CRYPTO_CB_ONLY_MLKEM + /* Validate as the software path does, so the reported error stays the + * same for a bad call. */ + if ((key == NULL) || (rand == NULL)) { + return BAD_FUNC_ARG; + } + if (len != WC_ML_KEM_MAKEKEY_RAND_SZ) { + return BUFFER_E; + } + return NO_VALID_DEVID; +#else byte buf[2 * WC_ML_KEM_SYM_SZ + 1]; byte* rho = buf; #ifndef WC_MLKEM_FAULT_HARDEN @@ -1097,6 +1110,7 @@ key-pair test required by ISO/IEC 19790:2012 sec 7.10.3.3" #endif /* FIPS v7 or WOLFSSL_VALIDATE_MLKEM_KEYGEN */ return ret; +#endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ } #endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY */ @@ -1192,6 +1206,7 @@ int wc_MlKemKey_SharedSecretSize(MlKemKey* key, word32* len) #if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) +#ifndef WOLF_CRYPTO_CB_ONLY_MLKEM /* Encrypt a message to cipher text with the encryption key. * * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE, m, r) @@ -1440,10 +1455,12 @@ static int mlkemkey_encapsulate(MlKemKey* key, const byte* m, byte* r, byte* c) return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_MLKEM */ #endif #if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) +#ifndef WOLF_CRYPTO_CB_ONLY_MLKEM static int wc_mlkemkey_check_h(MlKemKey* key) { int ret = 0; @@ -1485,6 +1502,7 @@ static int wc_mlkemkey_check_h(MlKemKey* key) return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_MLKEM */ #endif #ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE @@ -1613,6 +1631,20 @@ int wc_MlKemKey_Encapsulate(MlKemKey* key, unsigned char* ct, unsigned char* ss, int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* ct, unsigned char* ss, const unsigned char* rand, int len) { +#ifdef WOLF_CRYPTO_CB_ONLY_MLKEM + /* Validate as the software path does, so the reported error stays the + * same for a bad call. */ + if ((key == NULL) || (ct == NULL) || (ss == NULL) || (rand == NULL)) { + return BAD_FUNC_ARG; + } + if (len != WC_ML_KEM_ENC_RAND_SZ) { + return BUFFER_E; + } + if ((key->flags & MLKEM_FLAG_PUB_SET) == 0) { + return BAD_STATE_E; + } + return NO_VALID_DEVID; +#else #ifdef WOLFSSL_MLKEM_KYBER byte msg[WC_ML_KEM_SYM_SZ]; #endif @@ -1784,12 +1816,14 @@ int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* ct, #endif return ret; +#endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ } #endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE */ /******************************************************************************/ #ifndef WOLFSSL_MLKEM_NO_DECAPSULATE +#ifndef WOLF_CRYPTO_CB_ONLY_MLKEM /* Decapsulate cipher text to the message using key. * * FIPS 203, Algorithm 15: K-PKE.Decrypt(dk_PKE,c) @@ -1954,6 +1988,7 @@ static MLKEM_NOINLINE int mlkemkey_decapsulate(MlKemKey* key, byte* m, return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_MLKEM */ /** * Decapsulate the cipher text to calculate the shared secret. @@ -2002,16 +2037,18 @@ static MLKEM_NOINLINE int mlkemkey_decapsulate(MlKemKey* key, byte* m, int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, const unsigned char* ct, word32 len) { - byte msg[WC_ML_KEM_SYM_SZ]; - byte kr[2 * WC_ML_KEM_SYM_SZ + 1]; int ret = 0; unsigned int ctSz = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_MLKEM + byte msg[WC_ML_KEM_SYM_SZ]; + byte kr[2 * WC_ML_KEM_SYM_SZ + 1]; unsigned int i = 0; int fail = 0; #if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) byte* cmp = NULL; #else byte cmp[WC_ML_KEM_MAX_CIPHER_TEXT_SIZE]; +#endif #endif /* Validate parameters. */ @@ -2086,6 +2123,12 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_MLKEM + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else + #if !defined(USE_INTEL_SPEEDUP) && !defined(WOLFSSL_NO_MALLOC) if (ret == 0) { /* Allocate memory for cipher text that is generated. */ @@ -2180,6 +2223,7 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, wc_MemZero_Check(msg, sizeof(msg)); wc_MemZero_Check(kr, sizeof(kr)); #endif +#endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ return ret; } diff --git a/wolfcrypt/src/wc_mlkem_asm.S b/wolfcrypt/src/wc_mlkem_asm.S index 5c1c9a307f2..6524f8d3405 100644 --- a/wolfcrypt/src/wc_mlkem_asm.S +++ b/wolfcrypt/src/wc_mlkem_asm.S @@ -43,7 +43,7 @@ #endif /* HAVE_INTEL_AVX512 */ #endif /* NO_AVX512_SUPPORT */ -#ifdef WOLFSSL_HAVE_MLKEM +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) #ifdef HAVE_INTEL_AVX2 #ifndef __APPLE__ .data @@ -15918,8 +15918,8 @@ _mlkem_redistribute_8_rand_avx2: .size mlkem_redistribute_8_rand_avx2,.-mlkem_redistribute_8_rand_avx2 #endif /* __APPLE__ */ #endif /* HAVE_INTEL_AVX2 */ -#endif /* WOLFSSL_HAVE_MLKEM */ -#ifdef WOLFSSL_HAVE_MLKEM +#endif /* WOLFSSL_HAVE_MLKEM && !WOLF_CRYPTO_CB_ONLY_MLKEM */ +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) #ifndef NO_AVX512_SUPPORT #ifndef HAVE_INTEL_AVX512 #define HAVE_INTEL_AVX512 @@ -35274,8 +35274,8 @@ L_mlkem_rej_uniform_n_avx512_vbmi2_tail_end: .size mlkem_rej_uniform_n_avx512_vbmi2,.-mlkem_rej_uniform_n_avx512_vbmi2 #endif /* __APPLE__ */ #endif /* HAVE_INTEL_AVX512_VBMI2 */ -#endif /* WOLFSSL_HAVE_MLKEM */ -#ifdef WOLFSSL_HAVE_MLKEM +#endif /* WOLFSSL_HAVE_MLKEM && !WOLF_CRYPTO_CB_ONLY_MLKEM */ +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) #ifndef NO_AVX512_SUPPORT #ifndef NO_AVX512_VBMI_SUPPORT #ifndef HAVE_INTEL_AVX512_VBMI @@ -36969,7 +36969,7 @@ L_mlkem_rej_uniform_n_avx512_vbmi_vbmi2_tail_end: #endif /* __APPLE__ */ #endif /* HAVE_INTEL_AVX512_VBMI2 */ #endif /* HAVE_INTEL_AVX512_VBMI */ -#endif /* WOLFSSL_HAVE_MLKEM */ +#endif /* WOLFSSL_HAVE_MLKEM && !WOLF_CRYPTO_CB_ONLY_MLKEM */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits diff --git a/wolfcrypt/src/wc_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c index eb988c52cba..960cea78975 100644 --- a/wolfcrypt/src/wc_mlkem_poly.c +++ b/wolfcrypt/src/wc_mlkem_poly.c @@ -81,6 +81,26 @@ #endif #endif +#ifdef WOLF_CRYPTO_CB_ONLY_MLKEM +/* Only the encode/decode helpers and the hash/PRF object lifecycle are needed: + * every operation that uses the lattice math is serviced by a crypto callback. + * These are set for this file alone, so the public API in wc_mlkem.c keeps + * every entry point. + * + * This has to sit below the include above: that is what reads settings.h and + * user_settings.h, so a build that sets WOLF_CRYPTO_CB_ONLY_MLKEM there + * rather than on the command line would otherwise not be seen here at all. */ +#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY + #define WOLFSSL_MLKEM_NO_MAKE_KEY +#endif +#ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE + #define WOLFSSL_MLKEM_NO_ENCAPSULATE +#endif +#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE + #define WOLFSSL_MLKEM_NO_DECAPSULATE +#endif +#endif /* WOLF_CRYPTO_CB_ONLY_MLKEM */ + #ifdef WC_MLKEM_NO_ASM #undef USE_INTEL_SPEEDUP #undef WOLFSSL_ARMASM @@ -204,6 +224,9 @@ const sword16 zetas[MLKEM_N / 2] = { #if !defined(WOLFSSL_ARMASM) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Number-Theoretic Transform. * * FIPS 203, Algorithm 9: NTT(f) @@ -556,6 +579,8 @@ static void mlkem_ntt(sword16* r) } #endif } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) @@ -1027,6 +1052,9 @@ static void mlkem_invntt(sword16* r) } #endif +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Multiplication of polynomials in Zq[X]/(X^2-zeta). * * Used for multiplication of elements in Rq in NTT domain. @@ -1222,8 +1250,13 @@ static void mlkem_basemul_mont_add(sword16* r, const sword16* a, } #endif } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Pointwise multiply elements of a and b, into r, and multiply by 2^-16. * * @param [out] r Result polynomial. @@ -1248,6 +1281,8 @@ static void mlkem_pointwise_acc_mont(sword16* r, const sword16* a, mlkem_basemul_mont_add(r, a + (k - 1) * MLKEM_N, b + (k - 1) * MLKEM_N); #endif } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ /******************************************************************************/ @@ -2478,6 +2513,9 @@ static WC_INLINE void mlkem_cbd_eta3_ins(sword16* p, const byte* r) #endif #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * * Seed used with XOF to generate random bytes. @@ -2587,8 +2625,13 @@ static int mlkem_gen_matrix_k2_avx2(sword16* a, byte* seed, int transposed) return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512 +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a 2x2 matrix (or transpose) of uniform integers * mod q using the eight-way AVX-512 SHA3 core. Only four of the eight lanes * are used - the register-resident eight-way permutation is still faster than @@ -2672,10 +2715,15 @@ static int mlkem_gen_matrix_k2_avx512(sword16* a, byte* seed, int transposed) return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif /* WOLFSSL_MLKEM_HAVE_INTEL_AVX512 */ #endif #if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * * Seed used with XOF to generate random bytes. @@ -2832,8 +2880,13 @@ static int mlkem_gen_matrix_k3_avx2(sword16* a, byte* seed, int transposed) return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512 +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a 3x3 matrix (or transpose) of uniform integers * mod q using eight-way AVX-512 SHA3. The first eight polynomials are produced * in one eight-way batch; the ninth uses a single SHA3 state. @@ -2960,9 +3013,14 @@ static int mlkem_gen_matrix_k3_avx512(sword16* a, byte* seed, int transposed) return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif /* WOLFSSL_MLKEM_HAVE_INTEL_AVX512 */ #endif #if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * * Seed used with XOF to generate random bytes. @@ -3073,8 +3131,13 @@ static int mlkem_gen_matrix_k4_avx2(sword16* a, byte* seed, int transposed) return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512 +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a 4x4 matrix (or transpose) of uniform integers * mod q using eight-way AVX-512 SHA3. The 16 polynomials are produced in two * batches of eight. @@ -3163,10 +3226,15 @@ static int mlkem_gen_matrix_k4_avx512(sword16* a, byte* seed, int transposed) return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif /* WOLFSSL_MLKEM_HAVE_INTEL_AVX512 */ #endif /* WOLFSSL_KYBER1024 || WOLFSSL_WC_ML_KEM_1024 */ #elif defined(WOLFSSL_ARMASM) && defined(__aarch64__) #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * * Seed used with XOF to generate random bytes. @@ -3236,9 +3304,14 @@ static int mlkem_gen_matrix_k2_aarch64(sword16* a, byte* seed, int transposed) return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * * Seed used with XOF to generate random bytes. @@ -3300,9 +3373,14 @@ static int mlkem_gen_matrix_k3_aarch64(sword16* a, byte* seed, int transposed) return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * * Seed used with XOF to generate random bytes. @@ -3380,10 +3458,15 @@ static int mlkem_gen_matrix_k4_aarch64(sword16* a, byte* seed, int transposed) return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #endif /* USE_INTEL_SPEEDUP */ #if !(defined(WOLFSSL_ARMASM) && defined(__aarch64__)) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Absorb the seed data for squeezing out pseudo-random data. * * FIPS 203, Section 4.1: @@ -3421,6 +3504,8 @@ static int mlkem_xof_squeezeblocks(wc_Shake* shake128, byte* out, int blocks) { return wc_Shake128_SqueezeBlocks(shake128, out, (word32)blocks); } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif /* New/Initialize SHA-3 object. @@ -3475,6 +3560,9 @@ int mlkem_hash256(wc_Sha3* hash, const byte* data, word32 dataLen, byte* out) return ret; } +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Hash one or two blocks of data using SHA3-512 with SHA-3 object. * * FIPS 203, Section 4.1: @@ -3516,6 +3604,8 @@ void mlkem_prf_init(wc_Shake* prf) { wc_InitShake256(prf, NULL, 0); } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ /* New/Initialize SHAKE-256 object. * @@ -3545,6 +3635,9 @@ void mlkem_prf_free(wc_Shake* prf) } #if !(defined(WOLFSSL_ARMASM) && defined(__aarch64__)) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Create pseudo-random data from the key using SHAKE-256. * * FIPS 203, Section 4.1, 4.3: @@ -3625,6 +3718,8 @@ static int mlkem_prf(wc_Shake* shake256, byte* out, unsigned int outLen, return ret; #endif } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #ifdef WOLFSSL_MLKEM_KYBER @@ -3710,6 +3805,7 @@ int mlkem_kdf(const byte* seed, int seedLen, byte* out, int outLen) #endif #ifndef WOLFSSL_NO_ML_KEM +#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE /* Derive the secret from z and cipher text. * * @param [in, out] prf SHAKE-256 object. @@ -3774,9 +3870,13 @@ int mlkem_derive_secret(wc_Shake* prf, const byte* z, const byte* ct, return ret; } +#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #if !defined(WOLFSSL_ARMASM) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Rejection sampling on uniform random bytes to generate uniform random * integers mod q. * @@ -3932,12 +4032,17 @@ static unsigned int mlkem_rej_uniform_c(sword16* p, unsigned int len, return i; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #if !defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ !defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) #if !(defined(WOLFSSL_ARMASM) && defined(__aarch64__)) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * * Seed used with XOF to generate random bytes. @@ -4064,8 +4169,13 @@ static int mlkem_gen_matrix_c(MLKEM_PRF_T* prf, sword16* a, int k, byte* seed, return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * * Seed used with XOF to generate random bytes. @@ -4174,6 +4284,8 @@ int mlkem_gen_matrix(MLKEM_PRF_T* prf, sword16* a, int k, byte* seed, return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif @@ -4306,6 +4418,9 @@ static int mlkem_gen_matrix_i(MLKEM_PRF_T* prf, sword16* a, int k, byte* seed, (sword16)(((sword16)(((d) >> ((i) * 4 + 0)) & 0x3)) - \ ((sword16)(((d) >> ((i) * 4 + 2)) & 0x3))) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Compute polynomial with coefficients distributed according to a centered * binomial distribution with parameter eta2 from uniform random bytes. * @@ -4406,6 +4521,8 @@ static void mlkem_cbd_eta2(sword16* p, const byte* r) } #endif } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) /* Subtract one 3 bit value from another out of a larger number. @@ -4422,6 +4539,9 @@ static void mlkem_cbd_eta2(sword16* p, const byte* r) (sword16)(((sword16)(((d) >> ((i) * 6 + 0)) & 0x7)) - \ ((sword16)(((d) >> ((i) * 6 + 3)) & 0x7))) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Compute polynomial with coefficients distributed according to a centered * binomial distribution with parameter eta3 from uniform random bytes. * @@ -4568,10 +4688,15 @@ static void mlkem_cbd_eta3(sword16* p, const byte* r) #endif /* WOLFSSL_SMALL_STACK || WOLFSSL_MLKEM_NO_LARGE_CODE || * BIG_ENDIAN_ORDER */ } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #if !(defined(__aarch64__) && defined(WOLFSSL_ARMASM)) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -4679,6 +4804,8 @@ static int mlkem_get_noise_eta2_c(MLKEM_PRF_T* prf, sword16* p, #endif return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif @@ -4687,6 +4814,9 @@ static int mlkem_get_noise_eta2_c(MLKEM_PRF_T* prf, sword16* p, #if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768) || \ defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes. * * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r) @@ -4723,8 +4853,13 @@ static void mlkem_get_noise_x4_eta2_avx2(byte* rand, byte* seed, byte o) wc_MemZero_Check(state, sizeof(state)); #endif } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512 +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get eight lanes of ETA2 random bytes using eight-way AVX-512 SHA3. Lane j * uses seed count j and its output is written to rand + j * ETA2_RAND_SIZE. * @@ -4754,11 +4889,16 @@ static int mlkem_get_noise_x8_eta2_avx512(byte* rand, byte* seed) WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #endif #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) || \ defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get noise/error by calculating random bytes and sampling to a binomial * distribution. Values -2..2 * @@ -4817,9 +4957,14 @@ static int mlkem_get_noise_eta2_avx2(MLKEM_PRF_T* prf, sword16* p, #endif return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes. * * FIPS 203, Algorithm 13: K-PKE.KeyGen(d) @@ -4865,7 +5010,12 @@ static void mlkem_get_noise_x4_eta3_avx2(byte* rand, byte* seed) wc_MemZero_Check(state, sizeof(state)); #endif } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -4908,8 +5058,13 @@ static int mlkem_get_noise_k2_avx2(MLKEM_PRF_T* prf, sword16* vec1, return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512 +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get eight lanes of ETA3-length random bytes using eight-way AVX-512 SHA3. * Lane j uses seed count j; its output (two blocks) is written to * rand + j * PRF_RAND_SZ. ETA2 samplers may read the same lanes (they consume @@ -4944,7 +5099,12 @@ static int mlkem_get_noise_x8_eta3_avx512(byte* rand, byte* seed) WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution, using eight-way AVX-512 SHA3. The ETA3 vector lanes and (for * encapsulation) the extra ETA2 polynomial share one eight-way batch - the @@ -4991,10 +5151,15 @@ static int mlkem_get_noise_k2_avx512(MLKEM_PRF_T* prf, sword16* vec1, WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #endif #if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -5031,8 +5196,13 @@ static int mlkem_get_noise_k3_avx2(sword16* vec1, sword16* vec2, sword16* poly, #endif return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512 +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution, using eight-way AVX-512 SHA3. Eight ETA2 lanes are produced * in one batch; up to seven are consumed (six when poly is NULL). @@ -5072,10 +5242,15 @@ static int mlkem_get_noise_k3_avx512(sword16* vec1, sword16* vec2, WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #endif #if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -5119,8 +5294,13 @@ static int mlkem_get_noise_k4_avx2(MLKEM_PRF_T* prf, sword16* vec1, #endif return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512 +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution, using eight-way AVX-512 SHA3. The eight ETA2 vector lanes are * produced in one batch; the extra polynomial uses a single SHA3 state. @@ -5166,6 +5346,8 @@ static int mlkem_get_noise_k4_avx512(MLKEM_PRF_T* prf, sword16* vec1, WC_FREE_VAR_EX(rand, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #endif #endif /* USE_INTEL_SPEEDUP */ @@ -5174,6 +5356,9 @@ static int mlkem_get_noise_k4_avx512(MLKEM_PRF_T* prf, sword16* vec1, #define PRF_RAND_SZ (2 * SHA3_256_BYTES) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes. * * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r) @@ -5198,8 +5383,13 @@ static void mlkem_get_noise_x3_eta2_aarch64(word64* rand, byte* seed, byte o) mlkem_shake256_blocksx3_seed(rand, seed); } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #if defined(WOLFSSL_KYBER512) || defined(WOLFSSL_WC_ML_KEM_512) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes. * * FIPS 203, Algorithm 13: K-PKE.KeyGen(d) @@ -5249,7 +5439,12 @@ static void mlkem_get_noise_x3_eta3_aarch64(byte* rand, byte* seed, byte o) wc_MemZero_Check(state, sizeof(state)); #endif } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes. * * FIPS 203, Algorithm 13: K-PKE.KeyGen(d) @@ -5285,7 +5480,12 @@ static void mlkem_get_noise_eta3_aarch64(byte* rand, byte* seed, byte o) wc_MemZero_Check(state, sizeof(state)); #endif } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -5326,9 +5526,14 @@ static int mlkem_get_noise_k2_aarch64(sword16* vec1, sword16* vec2, #endif return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #if defined(WOLFSSL_KYBER768) || defined(WOLFSSL_WC_ML_KEM_768) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes. * * FIPS 203, Algorithm 14: K-PKE.Encrypt(ek_PKE,m,r) @@ -5351,7 +5556,12 @@ static void mlkem_get_noise_eta2_aarch64(word64* rand, byte* seed, byte o) rand[16] = W64LIT(0x8000000000000000); BlockSha3(rand); } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -5389,9 +5599,14 @@ static int mlkem_get_noise_k3_aarch64(sword16* vec1, sword16* vec2, #endif return 0; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #if defined(WOLFSSL_KYBER1024) || defined(WOLFSSL_WC_ML_KEM_1024) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -5432,11 +5647,16 @@ static int mlkem_get_noise_k4_aarch64(sword16* vec1, sword16* vec2, #endif return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif #endif /* __aarch64__ && WOLFSSL_ARMASM */ #if !(defined(__aarch64__) && defined(WOLFSSL_ARMASM)) +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -5485,9 +5705,14 @@ static int mlkem_get_noise_c(MLKEM_PRF_T* prf, int k, sword16* vec1, int eta1, return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif /* !(__aarch64__ && WOLFSSL_ARMASM) */ +#if !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -5597,6 +5822,8 @@ int mlkem_get_noise(MLKEM_PRF_T* prf, int k, sword16* vec1, sword16* vec2, return ret; } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || + * !WOLFSSL_MLKEM_NO_DECAPSULATE */ #if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) @@ -5640,6 +5867,7 @@ static int mlkem_get_noise_i(MLKEM_PRF_T* prf, int k, sword16* vec2, /******************************************************************************/ #if !(defined(__aarch64__) && defined(WOLFSSL_ARMASM)) +#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE /* Compare two byte arrays of equal size. * * @param [in] a First array to compare. @@ -5659,8 +5887,10 @@ static int mlkem_cmp_c(const byte* a, const byte* b, int sz) } return (int)(0 - ((-(word32)r) >> 31)); } +#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */ #endif +#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE /* Compare two byte arrays of equal size. * * @param [in] a First array to compare. @@ -5697,6 +5927,7 @@ int mlkem_cmp(const byte* a, const byte* b, int sz) return fail; #endif } +#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */ /******************************************************************************/ @@ -7043,7 +7274,7 @@ static void mlkem_from_bytes_c(sword16* p, const byte* b, int k) */ void mlkem_from_bytes(sword16* p, const byte* b, int k) { -#ifdef USE_INTEL_SPEEDUP +#if defined(USE_INTEL_SPEEDUP) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI if (USE_INTEL_AVX512(cpuid_flags) && IS_INTEL_AVX512_VBMI(cpuid_flags) && @@ -7138,7 +7369,7 @@ static void mlkem_to_bytes_c(byte* b, sword16* p, int k) */ void mlkem_to_bytes(byte* b, sword16* p, int k) { -#ifdef USE_INTEL_SPEEDUP +#if defined(USE_INTEL_SPEEDUP) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) #ifdef WOLFSSL_MLKEM_HAVE_INTEL_AVX512_VBMI if (USE_INTEL_AVX512(cpuid_flags) && IS_INTEL_AVX512_VBMI(cpuid_flags) && diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 33a25729dcc..a7495914296 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -53534,6 +53534,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t ed448_test(void) #endif #ifdef WOLFSSL_HAVE_MLKEM +#ifdef WC_MLKEM_HAVE_NATIVE #if !defined(WOLFSSL_NO_KYBER512) && !defined(WOLFSSL_NO_ML_KEM_512) && \ defined(WOLFSSL_TEST_PQC_SEED_KAT) static wc_test_ret_t mlkem512_kat(void) @@ -57917,10 +57918,35 @@ static wc_test_ret_t mlkem1024_kat(void) return ret; } #endif /* !WOLFSSL_NO_KYBER1024 && !WOLFSSL_NO_ML_KEM_1024 */ +#endif /* WC_MLKEM_HAVE_NATIVE */ + +#ifndef WC_MLKEM_HAVE_NATIVE +/* Any compiled-in parameter set proves the dispatch behaviour; which one is + * irrelevant, so pick the first that is actually built. */ +#ifndef WOLFSSL_NO_ML_KEM + #if defined(WOLFSSL_WC_ML_KEM_512) + #define MLKEM_CB_ONLY_TYPE WC_ML_KEM_512 + #elif defined(WOLFSSL_WC_ML_KEM_768) + #define MLKEM_CB_ONLY_TYPE WC_ML_KEM_768 + #elif defined(WOLFSSL_WC_ML_KEM_1024) + #define MLKEM_CB_ONLY_TYPE WC_ML_KEM_1024 + #endif +#endif +#if !defined(MLKEM_CB_ONLY_TYPE) && defined(WOLFSSL_MLKEM_KYBER) + #if defined(WOLFSSL_KYBER512) + #define MLKEM_CB_ONLY_TYPE KYBER512 + #elif defined(WOLFSSL_KYBER768) + #define MLKEM_CB_ONLY_TYPE KYBER768 + #elif defined(WOLFSSL_KYBER1024) + #define MLKEM_CB_ONLY_TYPE KYBER1024 + #endif +#endif +#endif /* !WC_MLKEM_HAVE_NATIVE */ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mlkem_test(void) { wc_test_ret_t ret; +#ifdef WC_MLKEM_HAVE_NATIVE int i; #ifndef WC_NO_RNG WC_RNG rng; @@ -58218,6 +58244,212 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mlkem_test(void) #endif return ret; +#else /* !WC_MLKEM_HAVE_NATIVE */ + /* Software ML-KEM is compiled out. Walk the public API with a key that + * has no device behind it: every operation must refuse with + * NO_VALID_DEVID rather than silently do nothing. The encode and decode + * paths this build keeps are exercised too, because a callback that + * returns key material relies on them. */ + ret = 0; +#ifdef MLKEM_CB_ONLY_TYPE + { + /* MlKemKey carries maximum-sized polynomial buffers, so keep it off + * the stack as the parameter-set tests do. */ + WC_DECLARE_VAR(key, MlKemKey, 1, HEAP_HINT); + int key_inited = 0; + int r; + word32 pubLen = 0; + word32 privLen = 0; + word32 ctLen = 0; + word32 ssLen = 0; + byte* buf = NULL; + byte* buf2 = NULL; + word32 bufLen = 0; + + WC_ALLOC_VAR(key, MlKemKey, 1, HEAP_HINT); + if (!WC_VAR_OK(key)) + ret = WC_TEST_RET_ENC_EC(MEMORY_E); + if (ret == 0) { + r = wc_MlKemKey_Init(key, MLKEM_CB_ONLY_TYPE, HEAP_HINT, + INVALID_DEVID); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + key_inited = 1; + } +#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY + if (ret == 0) { + byte rand[WC_ML_KEM_MAKEKEY_RAND_SZ]; + + XMEMSET(rand, 0, sizeof(rand)); + /* Argument checks still run ahead of the dispatch report. */ + r = wc_MlKemKey_MakeKeyWithRandom(key, NULL, (int)sizeof(rand)); + if (r != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0) { + r = wc_MlKemKey_MakeKeyWithRandom(key, rand, + (int)sizeof(rand) - 1); + if (r != WC_NO_ERR_TRACE(BUFFER_E)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + r = wc_MlKemKey_MakeKeyWithRandom(key, rand, + (int)sizeof(rand)); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + } +#endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY */ + /* The size queries size every buffer used below. */ + if (ret == 0) { + r = wc_MlKemKey_PublicKeySize(key, &pubLen); + if ((r != 0) || (pubLen == 0)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + r = wc_MlKemKey_PrivateKeySize(key, &privLen); + if ((r != 0) || (privLen == 0)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + r = wc_MlKemKey_CipherTextSize(key, &ctLen); + if ((r != 0) || (ctLen == 0)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + r = wc_MlKemKey_SharedSecretSize(key, &ssLen); + if ((r != 0) || (ssLen != WC_ML_KEM_SS_SZ)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + bufLen = privLen; + if (ctLen > bufLen) + bufLen = ctLen; + buf = (byte*)XMALLOC(bufLen, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + buf2 = (byte*)XMALLOC(bufLen, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if ((buf == NULL) || (buf2 == NULL)) { + ret = WC_TEST_RET_ENC_EC(MEMORY_E); + } + else { + XMEMSET(buf, 0, bufLen); + XMEMSET(buf2, 0, bufLen); + } + } +#ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE + if (ret == 0) { + byte rand[WC_ML_KEM_ENC_RAND_SZ]; + byte ss[WC_ML_KEM_SS_SZ]; + + XMEMSET(rand, 0, sizeof(rand)); + r = wc_MlKemKey_EncapsulateWithRandom(key, NULL, ss, rand, + (int)sizeof(rand)); + if (r != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0) { + r = wc_MlKemKey_EncapsulateWithRandom(key, buf, ss, rand, + (int)sizeof(rand) - 1); + if (r != WC_NO_ERR_TRACE(BUFFER_E)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + /* No public key yet, so this is refused before a device is + * asked for. */ + r = wc_MlKemKey_EncapsulateWithRandom(key, buf, ss, rand, + (int)sizeof(rand)); + if (r != WC_NO_ERR_TRACE(BAD_STATE_E)) + ret = WC_TEST_RET_ENC_NC; + } + } +#endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE */ + /* Coefficients are packed 12 bits little-endian, so 0xff 0x0f makes + * the first one 4095 - above q, and the decode must reject it. */ + if (ret == 0) { + XMEMSET(buf, 0, bufLen); + buf[0] = 0xff; + buf[1] = 0x0f; + r = wc_MlKemKey_DecodePublicKey(key, buf, pubLen); + if (r != WC_NO_ERR_TRACE(PUBLIC_KEY_E)) + ret = WC_TEST_RET_ENC_NC; + } + /* An all-zero encoding decodes cleanly: every coefficient reads as + * zero, which is in reduced range. */ + if (ret == 0) { + XMEMSET(buf, 0, bufLen); + r = wc_MlKemKey_DecodePublicKey(key, buf, pubLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + word32 z; + + /* The public encoding carries no derived field, so re-encoding + * must reproduce the zeros that went in. */ + r = wc_MlKemKey_EncodePublicKey(key, buf, pubLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + for (z = 0; (ret == 0) && (z < pubLen); z++) { + if (buf[z] != 0) + ret = WC_TEST_RET_ENC_NC; + } + } +#ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE + if (ret == 0) { + byte rand[WC_ML_KEM_ENC_RAND_SZ]; + byte ss[WC_ML_KEM_SS_SZ]; + + XMEMSET(rand, 0, sizeof(rand)); + /* With a public key set, the refusal now comes from the + * dispatch. */ + r = wc_MlKemKey_EncapsulateWithRandom(key, buf, ss, rand, + (int)sizeof(rand)); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } +#endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE */ + /* dk is dk_PKE || ek_PKE || H(ek_PKE) || z, and decoding rejects a + * stored hash that does not match the encoded public key. Fill that + * one field in; the rest can stay zero. */ + if (ret == 0) { + word32 hOff = privLen - 2 * WC_ML_KEM_SYM_SZ; + + XMEMSET(buf, 0, bufLen); + r = wc_Sha3_256Hash(buf + hOff - pubLen, pubLen, buf + hOff); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + r = wc_MlKemKey_DecodePrivateKey(key, buf, privLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + /* Every field of that encoding is canonical, so re-encoding must + * reproduce it byte for byte. */ + r = wc_MlKemKey_EncodePrivateKey(key, buf2, privLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (XMEMCMP(buf, buf2, privLen) != 0) + ret = WC_TEST_RET_ENC_NC; + } +#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE + if (ret == 0) { + byte ss[WC_ML_KEM_SS_SZ]; + + XMEMSET(buf, 0, bufLen); + r = wc_MlKemKey_Decapsulate(key, ss, buf, ctLen); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } +#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */ + XFREE(buf, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(buf2, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (key_inited) + wc_MlKemKey_Free(key); + WC_FREE_VAR(key, HEAP_HINT); + } +#endif /* MLKEM_CB_ONLY_TYPE */ + return ret; +#endif /* WC_MLKEM_HAVE_NATIVE */ } #endif /* WOLFSSL_HAVE_MLKEM */ @@ -81381,6 +81613,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t blob_test(void) /* Example custom context for crypto callback */ typedef struct { int exampleVar; /* flag for testing if only crypt is enabled. */ +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WC_MLKEM_HAVE_NATIVE) + int mlkemCount; /* ML-KEM callback invocations */ + int mlkemFail; /* when set, the ML-KEM handler returns this error */ +#endif #ifdef HAVE_ECC int eccMakePubCount; /* EC make-pub callback invocations */ int eccCheckPubCount; /* EC check-pubkey callback invocations */ @@ -83543,7 +83779,7 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) myCtx->exampleVar++; } #endif /* HAVE_FALCON && !WOLF_CRYPTO_CB_ONLY_FALCON */ - #ifdef WOLFSSL_HAVE_MLKEM + #if defined(WOLFSSL_HAVE_MLKEM) && defined(WC_MLKEM_HAVE_NATIVE) if (info->pk.type == WC_PK_TYPE_PQC_KEM_KEYGEN) { if ((info->pk.pqc_kem_kg.type == WC_PQC_KEM_TYPE_MLKEM) && (info->pk.pqc_kem_kg.key != NULL)) { @@ -83610,7 +83846,78 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) key->prf.devId = prfDevId; } } - #endif /* WOLFSSL_HAVE_MLKEM */ + #endif /* WOLFSSL_HAVE_MLKEM && WC_MLKEM_HAVE_NATIVE */ + #if defined(WOLFSSL_HAVE_MLKEM) && !defined(WC_MLKEM_HAVE_NATIVE) + /* The software core is stripped, so this device cannot delegate to the + * public API the way the other handlers do; it would dispatch straight + * back here. It instead answers with its own deterministic material, + * which is enough to prove the dispatch reaches a device, that the + * ciphertext and shared secret travel back to the caller, and that a + * device error is reported as-is. The shared secret is carried in the + * first bytes of the ciphertext, so decapsulate only reproduces it + * when encapsulate's output made the round trip. */ + if ((info->pk.type == WC_PK_TYPE_PQC_KEM_KEYGEN) && + (info->pk.pqc_kem_kg.type == WC_PQC_KEM_TYPE_MLKEM) && + (info->pk.pqc_kem_kg.key != NULL)) { + MlKemKey* key = (MlKemKey*)info->pk.pqc_kem_kg.key; + + myCtx->mlkemCount++; + if (myCtx->mlkemFail != 0) { + ret = myCtx->mlkemFail; + } + else { + XMEMSET(key->z, 0x5a, sizeof(key->z)); + key->flags |= MLKEM_FLAG_BOTH_SET; + ret = 0; + } + } + else if ((info->pk.type == WC_PK_TYPE_PQC_KEM_ENCAPS) && + (info->pk.pqc_encaps.type == WC_PQC_KEM_TYPE_MLKEM) && + (info->pk.pqc_encaps.key != NULL)) { + MlKemKey* key = (MlKemKey*)info->pk.pqc_encaps.key; + word32 ctSz = 0; + + myCtx->mlkemCount++; + if (myCtx->mlkemFail != 0) { + ret = myCtx->mlkemFail; + } + else { + ret = wc_MlKemKey_CipherTextSize(key, &ctSz); + } + if (ret == 0) { + byte* ct = info->pk.pqc_encaps.ciphertext; + byte* ss = info->pk.pqc_encaps.sharedSecret; + word32 i; + + for (i = 0; i < WC_ML_KEM_SS_SZ; i++) { + ss[i] = (byte)(key->z[i] ^ 0xa5); + } + XMEMSET(ct, 0xc7, ctSz); + XMEMCPY(ct, ss, WC_ML_KEM_SS_SZ); + } + } + else if ((info->pk.type == WC_PK_TYPE_PQC_KEM_DECAPS) && + (info->pk.pqc_decaps.type == WC_PQC_KEM_TYPE_MLKEM) && + (info->pk.pqc_decaps.key != NULL)) { + MlKemKey* key = (MlKemKey*)info->pk.pqc_decaps.key; + word32 ctSz = 0; + + myCtx->mlkemCount++; + if (myCtx->mlkemFail != 0) { + ret = myCtx->mlkemFail; + } + else { + ret = wc_MlKemKey_CipherTextSize(key, &ctSz); + } + if ((ret == 0) && (info->pk.pqc_decaps.ciphertextLen != ctSz)) { + ret = BUFFER_E; + } + if (ret == 0) { + XMEMCPY(info->pk.pqc_decaps.sharedSecret, + info->pk.pqc_decaps.ciphertext, WC_ML_KEM_SS_SZ); + } + } + #endif /* WOLFSSL_HAVE_MLKEM && !WC_MLKEM_HAVE_NATIVE */ #ifdef WOLFSSL_HAVE_FRODOKEM if (info->pk.type == WC_PK_TYPE_PQC_KEM_KEYGEN) { if ((info->pk.pqc_kem_kg.type == WC_PQC_KEM_TYPE_FRODOKEM) && @@ -85573,6 +85880,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) /* example data for callback */ myCtx.exampleVar = 1; +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WC_MLKEM_HAVE_NATIVE) + myCtx.mlkemCount = 0; + myCtx.mlkemFail = 0; +#endif #ifdef HAVE_ECC myCtx.eccMakePubCount = 0; myCtx.eccCheckPubCount = 0; @@ -85870,6 +86181,93 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) #ifdef WOLFSSL_HAVE_MLKEM if (ret == 0) ret = mlkem_test(); +#if !defined(WC_MLKEM_HAVE_NATIVE) && defined(MLKEM_CB_ONLY_TYPE) && \ + !defined(WC_NO_RNG) && !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) + /* With the software core stripped, the only way an ML-KEM operation can + * succeed is through a registered device. Drive all three operations that + * way and confirm the results came back, so a dispatch regression cannot + * hide behind the NO_VALID_DEVID checks in mlkem_test(). */ + if (ret == 0) { + WC_DECLARE_VAR(key, MlKemKey, 1, HEAP_HINT); + WC_DECLARE_VAR(ct, byte, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE, HEAP_HINT); + WC_DECLARE_VAR(mlkemRng, WC_RNG, 1, HEAP_HINT); + byte ss[WC_ML_KEM_SS_SZ]; + byte ssDec[WC_ML_KEM_SS_SZ]; + int key_inited = 0; + int rng_inited = 0; + int baseline = myCtx.mlkemCount; + word32 ctSz = 0; + int r; + + WC_ALLOC_VAR(key, MlKemKey, 1, HEAP_HINT); + WC_ALLOC_VAR(ct, byte, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE, HEAP_HINT); + WC_ALLOC_VAR(mlkemRng, WC_RNG, 1, HEAP_HINT); + if ((!WC_VAR_OK(key)) || (!WC_VAR_OK(ct)) || (!WC_VAR_OK(mlkemRng))) + ret = WC_TEST_RET_ENC_EC(MEMORY_E); + if (ret == 0) { + /* The device ignores the RNG; keep it off the callback path. */ + r = wc_InitRng_ex(mlkemRng, HEAP_HINT, INVALID_DEVID); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + rng_inited = 1; + } + if (ret == 0) { + r = wc_MlKemKey_Init(key, MLKEM_CB_ONLY_TYPE, HEAP_HINT, devId); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + key_inited = 1; + } + if (ret == 0) { + r = wc_MlKemKey_MakeKey(key, mlkemRng); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + r = wc_MlKemKey_CipherTextSize(key, &ctSz); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + XMEMSET(ct, 0, WC_ML_KEM_MAX_CIPHER_TEXT_SIZE); + XMEMSET(ss, 0, sizeof(ss)); + r = wc_MlKemKey_Encapsulate(key, ct, ss, mlkemRng); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + XMEMSET(ssDec, 0, sizeof(ssDec)); + r = wc_MlKemKey_Decapsulate(key, ssDec, ct, ctSz); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + /* Shared secrets agree only if the device's ciphertext reached the + * caller and was handed back to decapsulate unchanged. */ + if ((ret == 0) && (XMEMCMP(ss, ssDec, sizeof(ss)) != 0)) + ret = WC_TEST_RET_ENC_NC; + /* Three operations, three callback invocations: no silent bypass. */ + if ((ret == 0) && (myCtx.mlkemCount != baseline + 3)) + ret = WC_TEST_RET_ENC_NC; + /* A device error must reach the caller unchanged. */ + if (ret == 0) { + myCtx.mlkemFail = WC_NO_ERR_TRACE(WC_HW_E); + r = wc_MlKemKey_Encapsulate(key, ct, ss, mlkemRng); + myCtx.mlkemFail = 0; + if (r != WC_NO_ERR_TRACE(WC_HW_E)) + ret = WC_TEST_RET_ENC_NC; + } + if (key_inited) + wc_MlKemKey_Free(key); + if (rng_inited) + wc_FreeRng(mlkemRng); + WC_FREE_VAR(mlkemRng, HEAP_HINT); + WC_FREE_VAR(ct, HEAP_HINT); + WC_FREE_VAR(key, HEAP_HINT); + } +#endif /* callback-only ML-KEM dispatch */ #endif #ifdef WOLFSSL_HAVE_FRODOKEM if (ret == 0) diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 5576b5f8354..ed478a1d7d0 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -5908,6 +5908,17 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #error "WOLF_CRYPTO_CB_ONLY_ED25519 with " \ "WOLFSSL_ED25519_STREAMING_VERIFY is not supported" #endif +#if defined(WOLF_CRYPTO_CB_ONLY_MLKEM) && !defined(WOLF_CRYPTO_CB) + #error "WOLF_CRYPTO_CB_ONLY_MLKEM requires WOLF_CRYPTO_CB" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_MLKEM) && !defined(WOLFSSL_HAVE_MLKEM) + #error "WOLF_CRYPTO_CB_ONLY_MLKEM requires WOLFSSL_HAVE_MLKEM" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_MLKEM) && defined(HAVE_FIPS) + /* Key generation runs a pairwise consistency test that the callback would + * have to service; not validated. */ + #error "WOLF_CRYPTO_CB_ONLY_MLKEM is incompatible with FIPS builds" +#endif #if defined(WOLF_CRYPTO_CB_ONLY_CURVE25519) && !defined(WOLF_CRYPTO_CB) #error "WOLF_CRYPTO_CB_ONLY_CURVE25519 requires WOLF_CRYPTO_CB" #endif diff --git a/wolfssl/wolfcrypt/wc_mlkem.h b/wolfssl/wolfcrypt/wc_mlkem.h index 4c45786525e..ba469b83135 100644 --- a/wolfssl/wolfcrypt/wc_mlkem.h +++ b/wolfssl/wolfcrypt/wc_mlkem.h @@ -870,6 +870,19 @@ WOLFSSL_LOCAL unsigned int mlkem_arm32_rej_uniform(sword16* p, unsigned int len, } /* extern "C" */ #endif +/* Native implementation core (internal). The public wc_MlKemKey_* functions + * in wc_mlkem.c wrap it with cryptocb dispatch and argument checking. With + * WOLF_CRYPTO_CB_ONLY_MLKEM the lattice math is not compiled: key generation, + * encapsulation and decapsulation all go through the crypto callback. The + * encode and decode helpers stay, since a callback that returns key material + * needs them. */ +#ifndef WOLF_CRYPTO_CB_ONLY_MLKEM +/* Signals that native key generation, encapsulation and decapsulation are + * available. Tests gate on this rather than on the build switch, so a test + * says what it needs rather than which configuration removed it. */ +#define WC_MLKEM_HAVE_NATIVE +#endif + #endif /* WOLFSSL_HAVE_MLKEM */ #endif /* WOLF_CRYPT_WC_MLKEM_H */ From ee45af25c3b64f46485724fe07114ff75f116fb5 Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Tue, 8 Sep 2026 09:52:59 -0700 Subject: [PATCH 2/2] PR feedback: add precompiler checks, update test cases --- wolfcrypt/src/wc_mlkem_poly.c | 22 +++++++++++++++------- wolfcrypt/test/test.c | 25 +++++++++++++------------ 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/wolfcrypt/src/wc_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c index 960cea78975..a53992d54eb 100644 --- a/wolfcrypt/src/wc_mlkem_poly.c +++ b/wolfcrypt/src/wc_mlkem_poly.c @@ -122,8 +122,10 @@ #include #endif -#if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ - defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) +#if (defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) && \ + !defined(WOLFSSL_MLKEM_NO_MAKE_KEY)) || \ + (defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) && \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE)) static int mlkem_gen_matrix_i(MLKEM_PRF_T* prf, sword16* a, int k, byte* seed, int i, int transposed); static int mlkem_get_noise_i(MLKEM_PRF_T* prf, int k, sword16* vec2, @@ -4289,8 +4291,10 @@ int mlkem_gen_matrix(MLKEM_PRF_T* prf, sword16* a, int k, byte* seed, #endif -#if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ - defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) +#if (defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) && \ + !defined(WOLFSSL_MLKEM_NO_MAKE_KEY)) || \ + (defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) && \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE)) /* Deterministically generate a matrix (or transpose) of uniform integers mod q. * @@ -5825,8 +5829,10 @@ int mlkem_get_noise(MLKEM_PRF_T* prf, int k, sword16* vec1, sword16* vec2, #endif /* !WOLFSSL_MLKEM_NO_MAKE_KEY || !WOLFSSL_MLKEM_NO_ENCAPSULATE || * !WOLFSSL_MLKEM_NO_DECAPSULATE */ -#if defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) || \ - defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) +#if (defined(WOLFSSL_MLKEM_MAKEKEY_SMALL_MEM) && \ + !defined(WOLFSSL_MLKEM_NO_MAKE_KEY)) || \ + (defined(WOLFSSL_MLKEM_ENCAPSULATE_SMALL_MEM) && \ + !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE)) /* Get the noise/error by calculating random bytes and sampling to a binomial * distribution. * @@ -5931,7 +5937,9 @@ int mlkem_cmp(const byte* a, const byte* b, int sz) /******************************************************************************/ -#if !defined(WOLFSSL_ARMASM) +/* The assembly is compiled out under WOLF_CRYPTO_CB_ONLY_MLKEM, but + * mlkem_to_bytes_c() stays and calls this, so keep the C version there. */ +#if !defined(WOLFSSL_ARMASM) || defined(WOLF_CRYPTO_CB_ONLY_MLKEM) /* Conditional subtraction of q to each coefficient of a polynomial. * diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index a7495914296..42f79246ee2 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -58371,26 +58371,27 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mlkem_test(void) if (r != WC_NO_ERR_TRACE(PUBLIC_KEY_E)) ret = WC_TEST_RET_ENC_NC; } - /* An all-zero encoding decodes cleanly: every coefficient reads as - * zero, which is in reduced range. */ + /* Decode a key with non-zero coefficients: 0x55 bytes make every + * 12 bit coefficient 0x555, which is below q, so the packing is + * exercised rather than a buffer that is zero either way. The seed + * that follows the vector gets its own value. */ if (ret == 0) { - XMEMSET(buf, 0, bufLen); + XMEMSET(buf, 0x55, pubLen - WC_ML_KEM_SYM_SZ); + XMEMSET(buf + pubLen - WC_ML_KEM_SYM_SZ, 0xa5, WC_ML_KEM_SYM_SZ); r = wc_MlKemKey_DecodePublicKey(key, buf, pubLen); if (r != 0) ret = WC_TEST_RET_ENC_EC(r); } if (ret == 0) { - word32 z; - - /* The public encoding carries no derived field, so re-encoding - * must reproduce the zeros that went in. */ - r = wc_MlKemKey_EncodePublicKey(key, buf, pubLen); + /* Re-encode into a second buffer so the comparison cannot be + * satisfied by the input being left in place. The public + * encoding carries no derived field, so it must come back byte + * for byte. */ + r = wc_MlKemKey_EncodePublicKey(key, buf2, pubLen); if (r != 0) ret = WC_TEST_RET_ENC_EC(r); - for (z = 0; (ret == 0) && (z < pubLen); z++) { - if (buf[z] != 0) - ret = WC_TEST_RET_ENC_NC; - } + else if (XMEMCMP(buf, buf2, pubLen) != 0) + ret = WC_TEST_RET_ENC_NC; } #ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE if (ret == 0) {