Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/cryptocb-only.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
name: cryptocb-only Tests

# START OF COMMON SECTION
Expand Down Expand Up @@ -151,12 +151,15 @@
{"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"]},
Expand Down
3 changes: 3 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 5 additions & 5 deletions doc/dox_comments/header_files/wc_mlkem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
9 changes: 6 additions & 3 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
88 changes: 76 additions & 12 deletions tests/api/test_mlkem.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) && \
Expand Down Expand Up @@ -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) && \
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Comment thread
padelsbach marked this conversation as resolved.
MlKemKey* key;
WC_RNG rng;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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()) {
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions tests/swdev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading
Loading