diff --git a/.github/workflows/cryptocb-only.yml b/.github/workflows/cryptocb-only.yml index c31f875d76f..fecf4bafebe 100644 --- a/.github/workflows/cryptocb-only.yml +++ b/.github/workflows/cryptocb-only.yml @@ -151,12 +151,15 @@ jobs: {"name": "falcon-onlycb-no-swdev", "minutes": 1.0, "comment": "WOLF_CRYPTO_CB_ONLY_FALCON without swdev, which has no Falcon handlers: builds the Falcon key API that a callback-only build keeps, including its TLS and ASN callers, and runs the tests that need no device.", "configure": ["--disable-swdev", "--enable-falcon", "--enable-experimental", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_FALCON"]}, + {"name": "mlkem", "minutes": 4.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); swdev provides the software path via cryptocb, including the ML-KEM key shares TLS 1.3 offers by default. The encode/decode helpers and the hash/PRF object lifecycle stay, because a callback that returns key material needs them. Key generation and encapsulation from caller-supplied randomness have no callback, so the KAT tests are skipped.", + "configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_MLKEM"]}, {"name": "shake-xof", "minutes": 4.0, "comment": "WOLF_CRYPTO_CB_SHAKE_XOF: swdev handles SHAKE absorb and squeeze. No ONLY_* strip exists for SHAKE, so software SHAKE stays in. ML-KEM and ML-DSA reach swdev_shake through WOLF_CRYPTO_CB_FIND, and cryptocb_test runs shake_cb_xof_test.", "configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_SHAKE_XOF"]}, {"name": "all", "minutes": 19, - "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.", - "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"]}, + "comment": "All ten 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.", + "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 -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 ffb92d5e842..43bcf0eb1a0 100644 --- a/configure.ac +++ b/configure.ac @@ -12118,6 +12118,9 @@ then if test "$ENABLED_SLHDSA" != "no"; then AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_SLHDSA" fi + if test "$ENABLED_MLKEM" != "no"; then + AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_MLKEM" + fi fi if test "$ENABLED_CRYPTOCB_SW_TEST" = "no" diff --git a/doc/dox_comments/header_files/wc_mlkem.h b/doc/dox_comments/header_files/wc_mlkem.h index 0c9c2bd4846..47fea233f7c 100644 --- a/doc/dox_comments/header_files/wc_mlkem.h +++ b/doc/dox_comments/header_files/wc_mlkem.h @@ -558,10 +558,10 @@ int wc_MlKemKey_PublicKeyDecode(MlKemKey* key, const byte* input, word32 inSz, a key initialized with WC_ML_KEM_TYPE_UNSET takes it from the algorithm OID in the DER instead. - A build defining WOLFSSL_MLKEM_NO_MAKE_KEY cannot expand a seed, and so - cannot perform the Section 8 comparison either. Such a build rejects every - key that carries a seed, the "both" form included, rather than accepting - its expanded half unchecked. + A build defining WOLFSSL_MLKEM_NO_MAKE_KEY or WOLF_CRYPTO_CB_ONLY_MLKEM + cannot expand a seed, and so cannot perform the Section 8 comparison + either. Such a build rejects every key that carries a seed, the "both" + form included, rather than accepting its expanded half unchecked. \return 0 on success. \return BAD_FUNC_ARG if any required pointer is NULL. @@ -570,7 +570,7 @@ int wc_MlKemKey_PublicKeyDecode(MlKemKey* key, const byte* input, word32 inSz, carries a seed of the wrong length, or pairs a seed with an expanded key that does not match it. \return NOT_COMPILED_IN if the key carries a seed and the build defines - WOLFSSL_MLKEM_NO_MAKE_KEY. + WOLFSSL_MLKEM_NO_MAKE_KEY or WOLF_CRYPTO_CB_ONLY_MLKEM. \param [in,out] key Pointer to an initialized MlKemKey. \param [in] input Buffer holding the DER. diff --git a/tests/api.c b/tests/api.c index 8a09b804278..89a059f7a54 100644 --- a/tests/api.c +++ b/tests/api.c @@ -34508,7 +34508,8 @@ static int test_SSL_CIPHER_get_current_kx(void) !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_RSA) && \ !defined(WOLF_CRYPTO_CB_ONLY_SHA512) && \ !defined(WOLF_CRYPTO_CB_ONLY_ED25519) && \ - !defined(WOLF_CRYPTO_CB_ONLY_CURVE25519)) + !defined(WOLF_CRYPTO_CB_ONLY_CURVE25519) && \ + !defined(WOLF_CRYPTO_CB_ONLY_MLKEM)) static int load_pem_key_file_as_der(const char* privKeyFile, DerBuffer** pDer, int* keyFormat) @@ -36110,7 +36111,8 @@ static int test_wc_CryptoCb(void) !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_RSA) && \ !defined(WOLF_CRYPTO_CB_ONLY_SHA512) && \ !defined(WOLF_CRYPTO_CB_ONLY_ED25519) && \ - !defined(WOLF_CRYPTO_CB_ONLY_CURVE25519)) + !defined(WOLF_CRYPTO_CB_ONLY_CURVE25519) && \ + !defined(WOLF_CRYPTO_CB_ONLY_MLKEM)) #if defined(HAVE_IO_TESTS_DEPENDENCIES) && \ (!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519)) int tlsVer; @@ -44078,7 +44080,8 @@ TEST_CASE testCases[] = { !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_RSA) && \ !defined(WOLF_CRYPTO_CB_ONLY_SHA512) && \ !defined(WOLF_CRYPTO_CB_ONLY_ED25519) && \ - !defined(WOLF_CRYPTO_CB_ONLY_CURVE25519)) + !defined(WOLF_CRYPTO_CB_ONLY_CURVE25519) && \ + !defined(WOLF_CRYPTO_CB_ONLY_MLKEM)) /* Can't memory test as client/server hangs. */ TEST_DECL(test_wc_CryptoCb_registry), #endif diff --git a/tests/api/test_mlkem.c b/tests/api/test_mlkem.c index 7f06dfc5564..bdb74d434e9 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) && \ @@ -4065,7 +4070,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; @@ -4132,6 +4138,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) @@ -4236,6 +4243,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) @@ -4255,7 +4263,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; @@ -4631,6 +4640,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; @@ -4682,8 +4692,8 @@ int test_wc_mlkem_encode_key_len_decision(void) int test_wc_MlKemKey_seed_service_indicator(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \ - !defined(WOLFSSL_NO_ML_KEM) +#if defined(WOLFSSL_HAVE_MLKEM) && defined(WC_MLKEM_HAVE_NATIVE) && \ + !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && !defined(WOLFSSL_NO_ML_KEM) MlKemKey* key = NULL; byte rand[WC_ML_KEM_MAKEKEY_RAND_SZ]; #ifndef WOLFSSL_NO_ML_KEM_768 @@ -4862,6 +4872,14 @@ int test_wc_mlkem_cb_free(void) * filled with it proves nothing wrote to it. */ #define TEST_MLKEM_CB_FILL 0xA5 +/* What an operation the device declines returns: the software result, or + * no device when there is no software to fall back to. */ +#ifdef WC_MLKEM_HAVE_NATIVE + #define TEST_MLKEM_CB_DECLINED 0 +#else + #define TEST_MLKEM_CB_DECLINED WC_NO_ERR_TRACE(NO_VALID_DEVID) +#endif + typedef struct { int calls; /* KEM callbacks seen */ int ret; /* what the callback returns */ @@ -4900,6 +4918,34 @@ static int mlkem_cb_untouched(const byte* buf, word32 len) } return 1; } + +#ifndef WC_MLKEM_HAVE_NATIVE +/* Set a private key without key generation. All-zero key material decodes + * once the stored H(ek) matches, since dk is dk_PKE || ek || H(ek) || z. */ +static int mlkem_cb_load_key(MlKemKey* key) +{ + byte dk[WC_ML_KEM_MAX_PRIVATE_KEY_SIZE]; + word32 dkLen = 0; + word32 ekLen = 0; + word32 hOff; + int ret; + + XMEMSET(dk, 0, sizeof(dk)); + ret = wc_MlKemKey_PrivateKeySize(key, &dkLen); + if (ret == 0) { + ret = wc_MlKemKey_PublicKeySize(key, &ekLen); + } + if (ret == 0) { + hOff = dkLen - 2 * WC_ML_KEM_SYM_SZ; + ret = wc_Sha3_256Hash(dk + hOff - ekLen, ekLen, dk + hOff); + } + if (ret == 0) { + ret = wc_MlKemKey_DecodePrivateKey(key, dk, dkLen); + } + + return ret; +} +#endif /* !WC_MLKEM_HAVE_NATIVE */ #endif /* TEST_MLKEM_CB_PENDING */ /* A crypto callback that returns WC_PENDING_E for a KEM operation is asking @@ -4919,7 +4965,9 @@ int test_wc_mlkem_cb_pending_rejected(void) WC_RNG rng; byte* ctGood = NULL; byte* ct = NULL; +#ifdef WC_MLKEM_HAVE_NATIVE byte ssGood[WC_ML_KEM_SS_SZ]; +#endif byte ss[WC_ML_KEM_SS_SZ]; word32 ctLen = 0; int rngInit = 0; @@ -4935,8 +4983,8 @@ int test_wc_mlkem_cb_pending_rejected(void) ExpectNotNull(key = (MlKemKey*)XMALLOC(sizeof(MlKemKey), NULL, DYNAMIC_TYPE_TMP_BUFFER)); - /* A software key pair and a valid ciphertext to decapsulate, made before - * any callback is registered. */ + /* A key pair and a ciphertext to decapsulate, made before any callback + * is registered. */ ExpectIntEQ(wc_MlKemKey_Init(key, TEST_MLKEM_CB_PENDING_TYPE, NULL, INVALID_DEVID), 0); if (EXPECT_SUCCESS()) { @@ -4946,8 +4994,17 @@ int test_wc_mlkem_cb_pending_rejected(void) ExpectNotNull(ctGood = (byte*)XMALLOC(ctLen, NULL, DYNAMIC_TYPE_TMP_BUFFER)); ExpectNotNull(ct = (byte*)XMALLOC(ctLen, NULL, DYNAMIC_TYPE_TMP_BUFFER)); +#ifdef WC_MLKEM_HAVE_NATIVE ExpectIntEQ(wc_MlKemKey_MakeKey(key, &rng), 0); ExpectIntEQ(wc_MlKemKey_Encapsulate(key, ctGood, ssGood, &rng), 0); +#else + /* Pending is refused before the key or ciphertext is read, so a decoded + * key and a zero ciphertext do. */ + ExpectIntEQ(mlkem_cb_load_key(key), 0); + if (ctGood != NULL) { + XMEMSET(ctGood, 0, ctLen); + } +#endif ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_MLKEM_CB_PENDING_DEVID, mlkem_cb_pending_cb, &seen), 0); @@ -4986,10 +5043,15 @@ int test_wc_mlkem_cb_pending_rejected(void) /* Declining still falls through to software, and the software result is * the one the key pair was built with. */ seen.ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); - ExpectIntEQ(wc_MlKemKey_Encapsulate(key, ct, ss, &rng), 0); - ExpectIntEQ(wc_MlKemKey_Decapsulate(key, ss, ct, ctLen), 0); - ExpectIntEQ(wc_MlKemKey_Decapsulate(key, ss, ctGood, ctLen), 0); + ExpectIntEQ(wc_MlKemKey_Encapsulate(key, ct, ss, &rng), + TEST_MLKEM_CB_DECLINED); + ExpectIntEQ(wc_MlKemKey_Decapsulate(key, ss, ct, ctLen), + TEST_MLKEM_CB_DECLINED); + ExpectIntEQ(wc_MlKemKey_Decapsulate(key, ss, ctGood, ctLen), + TEST_MLKEM_CB_DECLINED); +#ifdef WC_MLKEM_HAVE_NATIVE ExpectIntEQ(XMEMCMP(ss, ssGood, sizeof(ss)), 0); +#endif ExpectIntEQ(seen.calls, 7); if (keyInit) { @@ -5018,11 +5080,13 @@ int test_wc_mlkem_cb_pending_rejected(void) /* Declining key generation still reaches the software path. */ seen.ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); - ExpectIntEQ(wc_MlKemKey_MakeKey(key, &rng), 0); + ExpectIntEQ(wc_MlKemKey_MakeKey(key, &rng), TEST_MLKEM_CB_DECLINED); ExpectIntEQ(seen.calls, 9); +#ifdef WC_MLKEM_HAVE_NATIVE if (key != NULL) { ExpectIntEQ(key->flags & MLKEM_FLAG_BOTH_SET, MLKEM_FLAG_BOTH_SET); } +#endif if (keyInit) { wc_MlKemKey_Free(key); diff --git a/tests/swdev/README.md b/tests/swdev/README.md index 5fb34fa26d2..8b0be645acd 100644 --- a/tests/swdev/README.md +++ b/tests/swdev/README.md @@ -19,6 +19,7 @@ The switches it supports are: | `WOLF_CRYPTO_CB_ONLY_CURVE25519` | software X25519 | X25519 via CryptoCb | | `WOLF_CRYPTO_CB_ONLY_CURVE448` | software X448 | X448 via CryptoCb | | `WOLF_CRYPTO_CB_ONLY_SLHDSA` | software SLH-DSA | SLH-DSA via CryptoCb | +| `WOLF_CRYPTO_CB_ONLY_MLKEM` | software ML-KEM | ML-KEM via CryptoCb | When a test program calls e.g. `wc_AesCbcEncrypt()` against a libwolfssl built with `-DWOLF_CRYPTO_CB_ONLY_AES`, the software AES path is gone; diff --git a/tests/swdev/swdev.c b/tests/swdev/swdev.c index 3872140c736..4db11465718 100644 --- a/tests/swdev/swdev.c +++ b/tests/swdev/swdev.c @@ -52,6 +52,9 @@ #ifdef WOLFSSL_HAVE_SLHDSA #include #endif +#ifdef WOLFSSL_HAVE_MLKEM +#include +#endif static int swdev_initialized = 0; @@ -650,6 +653,72 @@ static int swdev_pqc_sig(wc_CryptoInfo* info, int type, int pkType) } #endif /* WOLFSSL_HAVE_SLHDSA */ +#ifdef WOLFSSL_HAVE_MLKEM +#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY +static int swdev_mlkem_keygen(wc_CryptoInfo* info) +{ + return wc_MlKemKey_MakeKey((MlKemKey*)info->pk.pqc_kem_kg.key, + info->pk.pqc_kem_kg.rng); +} +#endif + +#ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE +static int swdev_mlkem_encaps(wc_CryptoInfo* info) +{ + MlKemKey* key = (MlKemKey*)info->pk.pqc_encaps.key; + word32 ctSz = 0; + int ret; + + ret = wc_MlKemKey_CipherTextSize(key, &ctSz); + if ((ret == 0) && ((info->pk.pqc_encaps.ciphertextLen != ctSz) || + (info->pk.pqc_encaps.sharedSecretLen != WC_ML_KEM_SS_SZ))) { + ret = BUFFER_E; + } + if (ret == 0) { + ret = wc_MlKemKey_Encapsulate(key, info->pk.pqc_encaps.ciphertext, + info->pk.pqc_encaps.sharedSecret, info->pk.pqc_encaps.rng); + } + + return ret; +} +#endif + +#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE +static int swdev_mlkem_decaps(wc_CryptoInfo* info) +{ + if (info->pk.pqc_decaps.sharedSecretLen != WC_ML_KEM_SS_SZ) + return BUFFER_E; + return wc_MlKemKey_Decapsulate((MlKemKey*)info->pk.pqc_decaps.key, + info->pk.pqc_decaps.sharedSecret, info->pk.pqc_decaps.ciphertext, + info->pk.pqc_decaps.ciphertextLen); +} +#endif + +/* Dispatch a PQC KEM operation, declining the families swdev has no handler + * for so the caller can fall back. */ +static int swdev_pqc_kem(wc_CryptoInfo* info, int type, int pkType) +{ + if (type != WC_PQC_KEM_TYPE_MLKEM) + return CRYPTOCB_UNAVAILABLE; + + switch (pkType) { +#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY + case WC_PK_TYPE_PQC_KEM_KEYGEN: + return swdev_mlkem_keygen(info); +#endif +#ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE + case WC_PK_TYPE_PQC_KEM_ENCAPS: + return swdev_mlkem_encaps(info); +#endif +#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE + case WC_PK_TYPE_PQC_KEM_DECAPS: + return swdev_mlkem_decaps(info); +#endif + default: + return CRYPTOCB_UNAVAILABLE; + } +} +#endif /* WOLFSSL_HAVE_MLKEM */ #if defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256) /* Copy sponge state between the caller's wc_Shake and swdev's shadow */ @@ -1414,7 +1483,7 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info, switch (info->algo_type) { #if !defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) || \ - defined(WOLFSSL_HAVE_SLHDSA) + defined(WOLFSSL_HAVE_SLHDSA) || defined(WOLFSSL_HAVE_MLKEM) case WC_ALGO_TYPE_PK: switch (info->pk.type) { #ifndef NO_RSA @@ -1506,6 +1575,17 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info, case WC_PK_TYPE_PQC_SIG_VERIFY_MSG: return swdev_pqc_sig(info, info->pk.pqc_verify.type, info->pk.type); #endif /* WOLFSSL_HAVE_SLHDSA */ + #ifdef WOLFSSL_HAVE_MLKEM + case WC_PK_TYPE_PQC_KEM_KEYGEN: + return swdev_pqc_kem(info, info->pk.pqc_kem_kg.type, + info->pk.type); + case WC_PK_TYPE_PQC_KEM_ENCAPS: + return swdev_pqc_kem(info, info->pk.pqc_encaps.type, + info->pk.type); + case WC_PK_TYPE_PQC_KEM_DECAPS: + return swdev_pqc_kem(info, info->pk.pqc_decaps.type, + info->pk.type); + #endif /* WOLFSSL_HAVE_MLKEM */ default: return CRYPTOCB_UNAVAILABLE; } diff --git a/tests/swdev/user_settings.h b/tests/swdev/user_settings.h index be125c11c78..fa032ad5295 100644 --- a/tests/swdev/user_settings.h +++ b/tests/swdev/user_settings.h @@ -31,6 +31,7 @@ #undef WOLF_CRYPTO_CB_ONLY_CURVE25519 #undef WOLF_CRYPTO_CB_ONLY_CURVE448 #undef WOLF_CRYPTO_CB_ONLY_SLHDSA +#undef WOLF_CRYPTO_CB_ONLY_MLKEM #ifndef WOLF_CRYPTO_CB #error "wc_swdev requires the main build to define WOLF_CRYPTO_CB" diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index d706d8a608f..31370e97d22 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 * WOLF_CRYPTO_CB_SHAKE_XOF: Dispatch SHAKE absorb and squeeze default: off * as well as update and final. Off by * default because a callback that predates @@ -2073,8 +2074,10 @@ int wc_CryptoCb_MakePqcKemKey(WC_RNG* rng, int type, int keySize, void* key) /* get devId */ devId = wc_CryptoCb_PqcKemGetDevId(type, key); +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif /* locate registered callback */ dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); @@ -2107,8 +2110,10 @@ int wc_CryptoCb_PqcEncapsulate(byte* ciphertext, word32 ciphertextLen, /* get devId */ devId = wc_CryptoCb_PqcKemGetDevId(type, key); +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif /* locate registered callback */ dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); @@ -2143,8 +2148,10 @@ int wc_CryptoCb_PqcDecapsulate(const byte* ciphertext, word32 ciphertextLen, /* get devId */ devId = wc_CryptoCb_PqcKemGetDevId(type, key); +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif /* locate registered callback */ dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); 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 df007042449..b4882629a63 100644 --- a/wolfcrypt/src/port/arm/armv8-mlkem-asm.S +++ b/wolfcrypt/src/port/arm/armv8-mlkem-asm.S @@ -33,7 +33,7 @@ #ifdef WOLFSSL_ARMASM #ifdef __aarch64__ #ifndef WOLFSSL_ARMASM_INLINE -#ifdef WOLFSSL_HAVE_MLKEM +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) #ifndef __APPLE__ .text .section .rodata @@ -12072,7 +12072,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.asm b/wolfcrypt/src/port/arm/armv8-mlkem-asm.asm index d9b0a64d21a..ecdb7999a42 100644 --- a/wolfcrypt/src/port/arm/armv8-mlkem-asm.asm +++ b/wolfcrypt/src/port/arm/armv8-mlkem-asm.asm @@ -23,7 +23,7 @@ ; cd ../scripts ; ruby ./kyber/kyber.rb arm64 \ ; ../wolfssl/wolfcrypt/src/port/arm/armv8-mlkem-asm.asm - IF :DEF:WOLFSSL_HAVE_MLKEM + IF :DEF:WOLFSSL_HAVE_MLKEM :LAND: :LNOT::DEF:WOLF_CRYPTO_CB_ONLY_MLKEM AREA |.rodata|, DATA, READONLY, ALIGN=4 ALIGN 8 L_mlkem_aarch64_consts diff --git a/wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c b/wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c index eb1e38fdd84..e060cc83f82 100644 --- a/wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c +++ b/wolfcrypt/src/port/arm/armv8-mlkem-asm_c.c @@ -36,7 +36,7 @@ #ifdef WOLFSSL_ARMASM_INLINE #include -#ifdef WOLFSSL_HAVE_MLKEM +#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLF_CRYPTO_CB_ONLY_MLKEM) XALIGNED(4) static const word16 L_mlkem_aarch64_consts[] = { 0x0d01, 0xf301, 0x4ebf, 0x0549, 0x5049, 0x0000, 0x0000, 0x0000, }; @@ -11261,7 +11261,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 24215d7d05c..96ce82c81b3 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 7111a6151c6..4386c2ad88a 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, @@ -3985,7 +3985,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 e9e582a88b5..deabef81263 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -224,6 +224,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) { @@ -259,6 +260,7 @@ static int mlkemkey_get_k(const MlKemKey* key) return 0; } } +#endif /* !WOLF_CRYPTO_CB_ONLY_MLKEM */ #endif #ifdef WOLFSSL_MLKEM_DYNAMIC_KEYS @@ -805,6 +807,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 @@ -1126,6 +1139,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 */ @@ -1221,6 +1235,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) @@ -1476,10 +1491,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; @@ -1521,6 +1538,7 @@ static int wc_mlkemkey_check_h(MlKemKey* key) return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_MLKEM */ #endif #ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE @@ -1651,6 +1669,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 @@ -1822,12 +1854,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) @@ -1994,6 +2028,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. @@ -2042,16 +2077,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 = -1; /* mismatch until mlkem_cmp() says otherwise */ #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. */ @@ -2128,6 +2165,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. */ @@ -2223,6 +2266,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; } @@ -3185,6 +3229,10 @@ int wc_MlKemKey_PublicKeyDecode(MlKemKey* key, const byte* input, word32 inSz, return ret; } +#if defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || defined(WOLF_CRYPTO_CB_ONLY_MLKEM) + #define MLKEM_NO_SEED_EXPAND +#endif + /* Decode a DER PKCS#8 OneAsymmetricKey into an ML-KEM private key. * * All three RFC 9935 Section 6 CHOICE shapes are accepted: the 64 byte seed @@ -3195,9 +3243,9 @@ int wc_MlKemKey_PublicKeyDecode(MlKemKey* key, const byte* input, word32 inSz, * Section 8. The parameter set must match the initialized key object, as * described for wc_MlKemKey_PublicKeyDecode. * - * A WOLFSSL_MLKEM_NO_MAKE_KEY build cannot expand a seed, so it cannot run - * the Section 8 check either and rejects any key carrying one, including the - * "both" shape. + * A WOLFSSL_MLKEM_NO_MAKE_KEY or WOLF_CRYPTO_CB_ONLY_MLKEM build cannot + * expand a seed, so it cannot run the Section 8 check either and rejects any + * key carrying one, including the "both" shape. * * @param [in, out] key ML-KEM key object. * @param [in] input DER buffer. @@ -3210,7 +3258,7 @@ int wc_MlKemKey_PublicKeyDecode(MlKemKey* key, const byte* input, word32 inSz, * expanded key that disagree. * @return MEMORY_E when dynamic memory allocation fails. * @return NOT_COMPILED_IN when a key carrying a seed is decoded in a - * WOLFSSL_MLKEM_NO_MAKE_KEY build. + * WOLFSSL_MLKEM_NO_MAKE_KEY or WOLF_CRYPTO_CB_ONLY_MLKEM build. */ int wc_MlKemKey_PrivateKeyDecode(MlKemKey* key, const byte* input, word32 inSz, word32* inOutIdx) @@ -3223,7 +3271,7 @@ int wc_MlKemKey_PrivateKeyDecode(MlKemKey* key, const byte* input, word32 inSz, word32 privKeyLen = 0; const byte* pubKey = NULL; word32 pubKeyLen = 0; -#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY +#ifndef MLKEM_NO_SEED_EXPAND int keyExpanded = 0; #endif @@ -3244,7 +3292,7 @@ int wc_MlKemKey_PrivateKeyDecode(MlKemKey* key, const byte* input, word32 inSz, if ((ret == 0) && ((key->flags & MLKEM_FLAG_TYPE_SET) == 0)) { ret = mlkem_key_adopt_type(key, keyType); } -#ifdef WOLFSSL_MLKEM_NO_MAKE_KEY +#ifdef MLKEM_NO_SEED_EXPAND /* Expanding a seed needs key generation, which this build lacks. That * rules out the "both" shape too: without the RFC 9935 Section 8 * comparison, trusting the expanded half would accept a tampered file. */ @@ -3310,12 +3358,7 @@ int wc_MlKemKey_PrivateKeyDecode(MlKemKey* key, const byte* input, word32 inSz, ForceZero(key->priv, key->privAllocSz); } #else - int scrubK = mlkemkey_get_k(key); - - if (scrubK != 0) { - ForceZero(key->priv, - (size_t)scrubK * MLKEM_N * sizeof(sword16)); - } + ForceZero(key->priv, sizeof(key->priv)); #endif ForceZero(key->z, WC_ML_KEM_SYM_SZ); key->flags &= MLKEM_FLAG_TYPE_SET; @@ -3323,7 +3366,7 @@ int wc_MlKemKey_PrivateKeyDecode(MlKemKey* key, const byte* input, word32 inSz, else if ((ret == 0) && (seed == NULL)) { ret = wc_MlKemKey_DecodePrivateKey(key, privKey, privKeyLen); } -#endif /* WOLFSSL_MLKEM_NO_MAKE_KEY */ +#endif /* MLKEM_NO_SEED_EXPAND */ return ret; } diff --git a/wolfcrypt/src/wc_mlkem_asm.S b/wolfcrypt/src/wc_mlkem_asm.S index fa0e8833a24..5af0f2a7b06 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_asm.asm b/wolfcrypt/src/wc_mlkem_asm.asm index 98d956063f0..be6b1c78cd5 100644 --- a/wolfcrypt/src/wc_mlkem_asm.asm +++ b/wolfcrypt/src/wc_mlkem_asm.asm @@ -42,6 +42,7 @@ _WIN64 = 1 ENDIF IFDEF WOLFSSL_HAVE_MLKEM +IFNDEF WOLF_CRYPTO_CB_ONLY_MLKEM IFDEF HAVE_INTEL_AVX2 _DATA SEGMENT ALIGN 16 @@ -15432,7 +15433,9 @@ mlkem_redistribute_8_rand_avx2 ENDP _TEXT ENDS ENDIF ENDIF +ENDIF IFDEF WOLFSSL_HAVE_MLKEM +IFNDEF WOLF_CRYPTO_CB_ONLY_MLKEM IFNDEF NO_AVX512_SUPPORT IFNDEF HAVE_INTEL_AVX512 HAVE_INTEL_AVX512 = 1 @@ -34154,7 +34157,9 @@ mlkem_rej_uniform_n_avx512_vbmi2 ENDP _TEXT ENDS ENDIF ENDIF +ENDIF IFDEF WOLFSSL_HAVE_MLKEM +IFNDEF WOLF_CRYPTO_CB_ONLY_MLKEM IFNDEF NO_AVX512_SUPPORT IFNDEF NO_AVX512_VBMI_SUPPORT IFNDEF HAVE_INTEL_AVX512_VBMI @@ -35508,4 +35513,5 @@ _TEXT ENDS ENDIF ENDIF ENDIF +ENDIF END diff --git a/wolfcrypt/src/wc_mlkem_poly.c b/wolfcrypt/src/wc_mlkem_poly.c index 616da2c3843..249a6a7dc2b 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 @@ -102,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, @@ -204,6 +226,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 +581,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 +1054,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 +1252,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 +1283,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 */ /******************************************************************************/ @@ -2505,6 +2542,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. @@ -2614,8 +2654,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 @@ -2699,10 +2744,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. @@ -2859,8 +2909,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. @@ -2987,9 +3042,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. @@ -3100,8 +3160,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. @@ -3190,10 +3255,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. @@ -3263,9 +3333,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. @@ -3327,9 +3402,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. @@ -3407,10 +3487,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: @@ -3448,6 +3533,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. @@ -3502,6 +3589,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: @@ -3543,6 +3633,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. * @@ -3572,6 +3664,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: @@ -3652,6 +3747,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 @@ -3737,6 +3834,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. @@ -3783,9 +3881,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. * @@ -3941,12 +4043,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. @@ -4073,8 +4180,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. @@ -4198,11 +4310,15 @@ 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 -#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. * @@ -4330,6 +4446,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. * @@ -4430,6 +4549,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. @@ -4446,6 +4567,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. * @@ -4592,10 +4716,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. * @@ -4703,6 +4832,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 @@ -4711,6 +4842,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) @@ -4747,8 +4881,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. * @@ -4778,11 +4917,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 * @@ -4841,9 +4985,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) @@ -4889,7 +5038,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. * @@ -4932,8 +5086,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 @@ -4968,7 +5127,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 @@ -5015,10 +5179,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. * @@ -5055,8 +5224,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). @@ -5096,10 +5270,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. * @@ -5143,8 +5322,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. @@ -5190,6 +5374,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 */ @@ -5198,6 +5384,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) @@ -5222,8 +5411,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) @@ -5273,7 +5467,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) @@ -5309,7 +5508,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. * @@ -5350,9 +5554,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) @@ -5375,7 +5584,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. * @@ -5413,9 +5627,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. * @@ -5456,11 +5675,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. * @@ -5509,9 +5733,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. * @@ -5636,9 +5865,13 @@ 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) +#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. * @@ -5679,6 +5912,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. @@ -5698,8 +5932,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. @@ -5736,10 +5972,13 @@ int mlkem_cmp(const byte* a, const byte* b, int sz) return fail; #endif } +#endif /* !WOLFSSL_MLKEM_NO_DECAPSULATE */ /******************************************************************************/ -#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. * @@ -7082,7 +7321,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) && @@ -7177,7 +7416,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 260b6e94d22..4f68e621666 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -59517,6 +59517,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) @@ -63900,6 +63901,30 @@ 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 */ #if !defined(WOLFSSL_MLKEM_NO_ASN1) && \ !defined(WOLFSSL_NO_MALLOC) && \ @@ -64742,6 +64767,7 @@ static wc_test_ret_t mlkem_certgen_test(void) 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; @@ -65075,6 +65101,218 @@ 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; + } + /* 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, 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) { + /* 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); + else if (XMEMCMP(buf, buf2, pubLen) != 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); + #ifdef WOLFSSL_SWDEV + /* swdev serves keys that have no device of their own. */ + if (r != 0) + #else + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + #endif + 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 */ @@ -88593,6 +88831,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 */ @@ -91029,7 +91271,7 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } #endif #endif /* WOLFSSL_HAVE_MLDSA */ - #ifdef WOLFSSL_HAVE_MLKEM + #if defined(WOLFSSL_HAVE_MLKEM) && defined(WC_MLKEM_HAVE_NATIVE) #ifndef WOLFSSL_MLKEM_NO_MAKE_KEY if (info->pk.type == WC_PK_TYPE_PQC_KEM_KEYGEN) { if ((info->pk.pqc_kem_kg.type == WC_PQC_KEM_TYPE_MLKEM) && @@ -91102,7 +91344,78 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } } #endif - #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) && @@ -93957,6 +94270,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; @@ -94621,6 +94938,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 85f98891574..91f278f0b21 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -6047,6 +6047,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 b1ba69a4100..4a1cbfdcc81 100644 --- a/wolfssl/wolfcrypt/wc_mlkem.h +++ b/wolfssl/wolfcrypt/wc_mlkem.h @@ -913,6 +913,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 */