diff --git a/.github/workflows/cryptocb-only.yml b/.github/workflows/cryptocb-only.yml index c31f875d76f..2b3699e7ec7 100644 --- a/.github/workflows/cryptocb-only.yml +++ b/.github/workflows/cryptocb-only.yml @@ -151,12 +151,24 @@ 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": "mldsa", "minutes": 4.0, + "comment": "WOLF_CRYPTO_CB_ONLY_MLDSA: strips the software ML-DSA core (NTT, matrix expansion, rejection sampling, the sign/verify workers and the x86 assembly); key import/export, the size queries and the DER paths stay, and the cached matrix/vector fields stay in the key struct so its layout is unchanged. The in-tree cryptocb test registers a callback-only ML-DSA device and drives key generation, signing, verifying and the private-key check through it, so the successful dispatch path is covered and not just the no-device refusal.", + "configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_MLDSA"]}, + {"name": "mldsa-verify-only", "minutes": 4.0, + "comment": "WOLF_CRYPTO_CB_ONLY_MLDSA against a verify-only ML-DSA build. That combination drops WOLFSSL_MLDSA_CHECK_KEY and the signing entry points, so it catches a callback-only path that assumes an API which this configuration does not compile.", + "configure": ["--enable-mldsa=verify-only", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_MLDSA"]}, + {"name": "mldsa-no-verify", "minutes": 4.0, + "comment": "WOLF_CRYPTO_CB_ONLY_MLDSA against a sign-only ML-DSA build (WOLFSSL_MLDSA_NO_VERIFY), the mirror of the verify-only entry.", + "configure": ["--enable-mldsa=sign", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_MLDSA"]}, {"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.", + "comment": "All nine ONLY_* macros that can share a build: 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. MLDSA is not here because this entry also enables SLH-DSA, and the TLS 1.3 suite then runs an SLH-DSA root with an ML-DSA-44 entity certificate: with the ML-DSA software path gone that handshake needs a registered device, which swdev does not provide yet. The all-mldsa entry below covers MLDSA alongside the rest.", "configure": ["--enable-slhdsa=yes,sha2", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ECC -DWOLF_CRYPTO_CB_ONLY_RSA -DWOLF_CRYPTO_CB_ONLY_SHA256 -DWOLF_CRYPTO_CB_ONLY_SHA512 -DWOLF_CRYPTO_CB_ONLY_AES -DWOLF_CRYPTO_CB_ONLY_ED25519 -DWOLF_CRYPTO_CB_ONLY_CURVE25519 -DWOLF_CRYPTO_CB_ONLY_CURVE448 -DWOLF_CRYPTO_CB_ONLY_SLHDSA"]}, + {"name": "all-mldsa", "minutes": 4.5, + "comment": "The 'all' set with MLDSA in place of SLHDSA, so a stripped ML-DSA still meets the other stripped primitives. SLH-DSA is left out because only that combination pulls in the ML-DSA-44 entity certificate test described above.", + "configure": ["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_MLDSA"]}, {"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..ec519e9b43b 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_MLDSA" != "no"; then + AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_MLDSA" + fi fi if test "$ENABLED_CRYPTOCB_SW_TEST" = "no" diff --git a/tests/api.c b/tests/api.c index 8a09b804278..d1a65a62baa 100644 --- a/tests/api.c +++ b/tests/api.c @@ -29908,6 +29908,7 @@ static int test_wc_SignCRL_mldsa(void) EXPECT_DECLS; #if defined(WOLFSSL_CERT_GEN) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ !defined(NO_ASN) && defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ defined(WOLFSSL_PEM_TO_DER) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \ !defined(WOLFSSL_MLDSA_NO_ASN1) static const struct { diff --git a/tests/api/test_mldsa.c b/tests/api/test_mldsa.c index f3749d684b4..f722308c16c 100644 --- a/tests/api/test_mldsa.c +++ b/tests/api/test_mldsa.c @@ -711,7 +711,8 @@ int test_mldsa(void) int test_mldsa_sign_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_MLDSA_HAVE_NATIVE) #if defined(WOLFSSL_HAVE_MLDSA) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ @@ -804,6 +805,7 @@ int test_mldsa_make_key(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) wc_MlDsaKey* key; WC_RNG rng; @@ -844,7 +846,7 @@ int test_mldsa_make_key(void) int test_mldsa_sign(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_MLDSA) && \ +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) && defined(WOLFSSL_MLDSA_NO_CTX) wc_MlDsaKey* key; wc_MlDsaKey* importKey = NULL; @@ -1031,7 +1033,7 @@ int test_mldsa_sign(void) int test_mldsa_verify(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_MLDSA) && \ +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_VERIFY) && defined(WOLFSSL_MLDSA_NO_CTX) && \ (!defined(WOLFSSL_NO_ML_DSA_44) || !defined(WOLFSSL_MLDSA_NO_SIGN)) wc_MlDsaKey* key; @@ -1264,6 +1266,7 @@ int test_mldsa_sign_vfy(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) && !defined(WOLFSSL_MLDSA_NO_VERIFY) wc_MlDsaKey* key; @@ -1352,6 +1355,7 @@ int test_mldsa_check_key(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ defined(WOLFSSL_MLDSA_CHECK_KEY) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) wc_MlDsaKey* checkKey; @@ -3024,6 +3028,7 @@ int test_mldsa_der(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_ASN1) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) wc_MlDsaKey* key; @@ -3228,6 +3233,7 @@ int test_mldsa_der(void) } #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_ASN1) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) /* Decode, re-export, byte-compare. Asserts version=1 on the bundled form and @@ -3296,6 +3302,7 @@ int test_mldsa_oneasymkey_version(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_ASN1) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) #ifndef WOLFSSL_NO_ML_DSA_44 ExpectIntEQ(mldsa_oneasymkey_version_check(WC_ML_DSA_44), @@ -3317,6 +3324,7 @@ int test_mldsa_make_key_from_seed(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) wc_MlDsaKey* key; #ifndef WOLFSSL_NO_ML_DSA_44 @@ -7784,7 +7792,7 @@ int test_mldsa_make_key_from_seed(void) int test_mldsa_sig_kats(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_MLDSA) && \ +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) && defined(WOLFSSL_MLDSA_NO_CTX) wc_MlDsaKey* key; #ifndef WOLFSSL_NO_ML_DSA_44 @@ -12604,6 +12612,7 @@ int test_mldsa_sign_ctx_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) wc_MlDsaKey* key; word32 sigLen; @@ -16842,6 +16851,7 @@ int test_mldsa_verify_ctx_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_VERIFY) wc_MlDsaKey* key; int res; @@ -20363,7 +20373,7 @@ int test_mldsa_verify_ctx_kats(void) int test_mldsa_verify_kats(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_MLDSA) && \ +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_VERIFY) && defined(WOLFSSL_MLDSA_NO_CTX) wc_MlDsaKey* key; int res; @@ -24635,6 +24645,7 @@ int test_mldsa_sign_mu_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) wc_MlDsaKey* key = NULL; word32 sigLen; @@ -27499,6 +27510,7 @@ int test_mldsa_verify_mu_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_VERIFY) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) wc_MlDsaKey* key = NULL; byte* sigBuf = NULL; @@ -29695,6 +29707,7 @@ int test_mldsa_verify_mu_kats(void) } #if !defined(NO_ASN) && defined(HAVE_PKCS8) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ defined(WOLFSSL_HAVE_MLDSA) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLDSA_NO_ASN1) && defined(WOLFSSL_ASN_TEMPLATE) @@ -29763,6 +29776,7 @@ int test_mldsa_PrivateKeyDecode_OpenSSL_form(void) EXPECT_DECLS; #if !defined(NO_ASN) && defined(HAVE_PKCS8) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ defined(WOLFSSL_HAVE_MLDSA) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLDSA_NO_ASN1) && defined(WOLFSSL_ASN_TEMPLATE) @@ -29856,6 +29870,7 @@ int test_mldsa_pkcs8_import_OpenSSL_form(void) { EXPECT_DECLS; #if !defined(NO_ASN) && defined(HAVE_PKCS8) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ defined(WOLFSSL_HAVE_MLDSA) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) && \ @@ -29930,6 +29945,7 @@ int test_mldsa_pkcs8_export_import_wolfSSL_form(void) { EXPECT_DECLS; #if !defined(NO_ASN) && defined(HAVE_PKCS8) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ defined(WOLFSSL_HAVE_MLDSA) && !defined(NO_TLS) && \ (!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER)) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ @@ -30044,7 +30060,7 @@ int test_mldsa_pkcs8_export_import_wolfSSL_form(void) int test_mldsa_encode_w1_large_values(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_MLDSA) && \ +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ (!defined(WOLFSSL_MLDSA_NO_SIGN) || \ !defined(WOLFSSL_MLDSA_NO_VERIFY)) @@ -30177,7 +30193,8 @@ int test_mldsa_pkcs12(void) { EXPECT_DECLS; #if !defined(NO_ASN) && defined(HAVE_PKCS12) && \ - defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \ + defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ + defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \ !defined(WOLFSSL_MLDSA_NO_ASN1) && \ !defined(NO_TLS) && !defined(NO_PWDBASED) && !defined(NO_HMAC) && \ !defined(NO_CERTS) && !defined(NO_DES3) && \ @@ -30562,6 +30579,7 @@ int test_mldsa_verify_hash(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLDSA_NO_VERIFY) wc_MlDsaKey key; @@ -30602,6 +30620,7 @@ int test_dilithium_hash(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLDSA_NO_VERIFY) wc_MlDsaKey key; @@ -30815,6 +30834,7 @@ int test_wc_MldsaFeatureCoverage(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ !defined(WOLFSSL_MLDSA_VERIFY_ONLY) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) && \ @@ -30904,7 +30924,8 @@ int test_wc_MldsaFeatureCoverage(void) int test_wc_MldsaDecisionCoverage2(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_MLDSA) +#if defined(WOLFSSL_HAVE_MLDSA) && \ + defined(WC_MLDSA_HAVE_NATIVE) wc_MlDsaKey key; int inited = 0; @@ -31757,6 +31778,7 @@ int test_wc_MlDsaKey_seed_service_indicator(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ + defined(WC_MLDSA_HAVE_NATIVE) && \ !defined(WOLFSSL_MLDSA_VERIFY_ONLY) wc_MlDsaKey key; byte seed[MLDSA_SEED_SZ]; diff --git a/tests/api/test_mldsa_legacy.c b/tests/api/test_mldsa_legacy.c index a8f1c131f47..6de7cd0db52 100644 --- a/tests/api/test_mldsa_legacy.c +++ b/tests/api/test_mldsa_legacy.c @@ -373,7 +373,8 @@ int test_mldsa_legacy_shim(void) #if !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \ !defined(WOLFSSL_MLDSA_NO_VERIFY) && !defined(WOLFSSL_NO_ML_DSA_44) && \ defined(WOLFSSL_MLDSA_PUBLIC_KEY) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \ - !defined(WC_NO_RNG) + !defined(WC_NO_RNG) && \ + defined(WC_MLDSA_HAVE_NATIVE) { dilithium_key key; /* legacy typedef */ WC_RNG rng; diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index d706d8a608f..3e1e586aed8 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_MLDSA: Use only callbacks for ML-DSA 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 diff --git a/wolfcrypt/src/wc_mldsa.c b/wolfcrypt/src/wc_mldsa.c index 8fade7cfb5f..0b978ac7311 100644 --- a/wolfcrypt/src/wc_mldsa.c +++ b/wolfcrypt/src/wc_mldsa.c @@ -493,6 +493,7 @@ static int mldsa_alloc_pub_buf(wc_MlDsaKey* key) } #endif +#ifndef WOLF_CRYPTO_CB_ONLY_MLDSA /****************************************************************************** * Hash operations ******************************************************************************/ @@ -9221,6 +9222,11 @@ static int mldsa_pct(wc_MlDsaKey* key) * @return Other negative when an error occurs. */ +/* Forward declaration: the software path below calls this directly so it + * does not re-enter the crypto callback. */ +static int mldsa_key_from_seed_checked(wc_MlDsaKey* key, const byte* seed, + int runPct); + static int mldsa_make_key(wc_MlDsaKey* key, WC_RNG* rng) { int ret; @@ -9235,8 +9241,10 @@ static int mldsa_make_key(wc_MlDsaKey* key, WC_RNG* rng) #endif /* Step 2: Check for error. */ if (ret == 0) { - /* Step 5: Make key with random seed. */ - ret = wc_MlDsaKey_MakeKeyFromSeed(key, seed); + /* Step 5: Make key with random seed. Straight to the helper: + * wc_MlDsaKey_MakeKeyFromSeed() would offer the seed to the + * device that already declined this generation. */ + ret = mldsa_key_from_seed_checked(key, seed, 1); } ForceZero(seed, sizeof(seed)); @@ -11362,6 +11370,7 @@ static int mldsa_verify_ctx_hash(wc_MlDsaKey* key, const byte* ctx, return ret; } #endif /* WOLFSSL_MLDSA_NO_VERIFY */ +#endif /* !WOLF_CRYPTO_CB_ONLY_MLDSA */ #ifndef WOLFSSL_MLDSA_NO_MAKE_KEY int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng) @@ -11389,6 +11398,11 @@ int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng) } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { /* Check the level or parameters have been set. */ if (key->params == NULL) { @@ -11399,8 +11413,9 @@ int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng) ret = mldsa_make_key(key, rng); } } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ - /* No key-pair test here: wc_MlDsaKey_MakeKeyFromSeed(), reached from + /* No key-pair test here: mldsa_key_from_seed_checked(), reached from * mldsa_make_key() above, already runs it on every generation path. * Guarded on the version, not HAVE_FIPS: src/include.am only compiles * this file under BUILD_FIPS_V7_PLUS, so the two are equivalent here. */ @@ -11408,6 +11423,7 @@ int wc_MlDsaKey_MakeKey(wc_MlDsaKey* key, WC_RNG* rng) return ret; } +#ifndef WOLF_CRYPTO_CB_ONLY_MLDSA /* Expand a seed into an ML-DSA key pair and test it. * * @param [in, out] key ML-DSA key to fill in. @@ -11449,10 +11465,58 @@ static int mldsa_key_from_seed_checked(wc_MlDsaKey* key, const byte* seed, return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_MLDSA */ + +/* Expand a seed into a key pair, offering it to a device first. + * + * A device that generates ML-DSA keys already expands a seed of its own + * choosing, so it can take this one. runPct is 0 for the ASN.1 decode path. + * The software key generation path calls mldsa_key_from_seed_checked() + * directly rather than coming back through here. + */ +static int mldsa_key_from_seed_dev(wc_MlDsaKey* key, const byte* seed, + int runPct) +{ + int ret = 0; + + /* Validate parameters. */ + if ((key == NULL) || (seed == NULL)) { + ret = BAD_FUNC_ARG; + } + +#ifdef WOLF_CRYPTO_CB + if (ret == 0) { + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + ret = wc_CryptoCb_MakePqcSignatureKeyEx(NULL, + WC_PQC_SIG_TYPE_MLDSA, key->level, seed, MLDSA_SEED_SZ, key); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + ret = 0; + } + } +#endif + +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NO_VALID_DEVID; + } + (void)runPct; +#else + if (ret == 0) { + ret = mldsa_key_from_seed_checked(key, seed, runPct); + } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ + + return ret; +} int wc_MlDsaKey_MakeKeyFromSeed(wc_MlDsaKey* key, const byte* seed) { - return mldsa_key_from_seed_checked(key, seed, 1); + return mldsa_key_from_seed_dev(key, seed, 1); } #endif @@ -11515,11 +11579,17 @@ int wc_MlDsaKey_SignCtx(wc_MlDsaKey* key, const byte* ctx, byte ctxLen, ret = BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { /* Sign message. */ ret = mldsa_sign_ctx_msg(key, rng, ctx, ctxLen, msg, msgLen, sig, sigLen); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -11577,10 +11647,16 @@ int wc_MlDsaKey_Sign(wc_MlDsaKey* key, byte* sig, word32 *sigLen, ret = BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { /* Sign message. */ ret = mldsa_sign_msg(key, rng, msg, msgLen, sig, sigLen); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -11636,11 +11712,17 @@ int wc_MlDsaKey_SignCtxHash(wc_MlDsaKey* key, const byte* ctx, byte ctxLen, ret = BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { /* Sign message. */ ret = mldsa_sign_ctx_hash(key, rng, ctx, ctxLen, hashAlg, hash, hashLen, sig, sigLen); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -11685,11 +11767,18 @@ int wc_MlDsaKey_SignCtxWithSeed(wc_MlDsaKey* key, const byte* ctx, byte ctxLen, ret = BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NOT_COMPILED_IN; + } + (void)msgLen; +#else if (ret == 0) { /* Sign message. */ ret = mldsa_sign_ctx_msg_with_seed(key, seed, ctx, ctxLen, msg, msgLen, sig, sigLen); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -11730,10 +11819,17 @@ int wc_MlDsaKey_SignWithSeed(wc_MlDsaKey* key, byte* sig, word32 *sigLen, ret = BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + (void)msgLen; + if (ret == 0) { + ret = NOT_COMPILED_IN; + } +#else if (ret == 0) { /* Sign message. */ ret = mldsa_sign_msg_with_seed(key, seed, msg, msgLen, sig, sigLen); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -11773,11 +11869,19 @@ int wc_MlDsaKey_SignCtxHashWithSeed(wc_MlDsaKey* key, const byte* ctx, ret = BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NOT_COMPILED_IN; + } + (void)hashLen; + (void)hashAlg; +#else if (ret == 0) { /* Sign message. */ ret = mldsa_sign_ctx_hash_with_seed(key, seed, ctx, ctxLen, hashAlg, hash, hashLen, sig, sigLen); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -11817,6 +11921,11 @@ int wc_MlDsaKey_SignMuWithSeed(wc_MlDsaKey* key, byte* sig, word32 *sigLen, ret = BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NOT_COMPILED_IN; + } +#else if (ret == 0) { /* Build [seed||mu] buffer and call internal sign function. */ byte seedMu[MLDSA_RND_SZ + MLDSA_MU_SZ]; @@ -11832,6 +11941,7 @@ int wc_MlDsaKey_SignMuWithSeed(wc_MlDsaKey* key, byte* sig, word32 *sigLen, wc_MemZero_Check(seedMu, sizeof(seedMu)); #endif } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -11894,11 +12004,17 @@ int wc_MlDsaKey_VerifyCtx(wc_MlDsaKey* key, const byte* sig, word32 sigLen, } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { /* Verify message with signature. */ ret = mldsa_verify_ctx_msg(key, ctx, ctxLen, msg, msgLen, sig, sigLen, res); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -11952,10 +12068,16 @@ int wc_MlDsaKey_Verify(wc_MlDsaKey* key, const byte* sig, word32 sigLen, } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { /* Verify message with signature. */ ret = mldsa_verify_msg(key, msg, msgLen, sig, sigLen, res); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -12007,11 +12129,17 @@ int wc_MlDsaKey_VerifyCtxHash(wc_MlDsaKey* key, const byte* sig, word32 sigLen, } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { /* Verify message with signature. */ ret = mldsa_verify_ctx_hash(key, ctx, ctxLen, hashAlg, hash, hashLen, sig, sigLen, res); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -12081,9 +12209,16 @@ int wc_MlDsaKey_VerifyMu(wc_MlDsaKey* key, const byte* sig, word32 sigLen, ret = BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + if (ret == 0) { + ret = NOT_COMPILED_IN; + } + (void)sigLen; +#else if (ret == 0) { ret = mldsa_verify_with_mu(key, mu, sig, sigLen, res); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ return ret; } @@ -12682,6 +12817,7 @@ int wc_MlDsaKey_GetSigLen(wc_MlDsaKey* key, int* len) int wc_MlDsaKey_CheckKey(wc_MlDsaKey* key) { int ret = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_MLDSA const wc_MlDsaParams* params = NULL; sword32* a = NULL; sword32* s1 = NULL; @@ -12689,11 +12825,58 @@ int wc_MlDsaKey_CheckKey(wc_MlDsaKey* key) sword32* t = NULL; sword32* t0 = NULL; sword32* t1 = NULL; +#endif /* Validate parameter. */ if (key == NULL) { ret = BAD_FUNC_ARG; } + +#ifdef WOLF_CRYPTO_CB + /* A device-backed key holds no local private material, so dispatch before + * the prvKeySet check the software path makes. */ + if (ret == 0) { + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + const byte* pub = NULL; + word32 pubSz = 0; + + /* pubKeySet means this object holds the public key bytes. A key + * the device generated has none here, so nothing is sent and the + * device works from its own copy. */ + if (key->pubKeySet) { + int sz = wc_MlDsaKey_PubSize(key); + int have = (sz > 0); + + #if defined(WOLFSSL_MLDSA_DYNAMIC_KEYS) || \ + defined(WOLFSSL_MLDSA_ASSIGN_KEY) + /* p is a pointer in these layouts and may be unset. */ + have = have && (key->p != NULL); + #endif + if (have) { + pub = key->p; + pubSz = (word32)sz; + } + } + ret = wc_CryptoCb_PqcSignatureCheckPrivKey(key, + WC_PQC_SIG_TYPE_MLDSA, pub, pubSz); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + ret = 0; + } + } +#endif /* WOLF_CRYPTO_CB */ + +#ifdef WOLF_CRYPTO_CB_ONLY_MLDSA + /* No software fallback: the check recomputes the public key from the + * private key, which only the device holding it can do. */ + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if ((ret == 0) && (!key->prvKeySet)) { ret = BAD_FUNC_ARG; } @@ -12830,6 +13013,8 @@ int wc_MlDsaKey_CheckKey(wc_MlDsaKey* key) /* Dispose of allocated memory. */ XFREE(s1, key->heap, DYNAMIC_TYPE_MLDSA); } +#endif /* WOLF_CRYPTO_CB_ONLY_MLDSA */ + return ret; } #endif /* WOLFSSL_MLDSA_CHECK_KEY */ @@ -13013,7 +13198,8 @@ int wc_MlDsaKey_ImportPubRaw(wc_MlDsaKey* key, const byte* in, word32 inLen) XMEMCPY(key->p, in, inLen); #endif -#ifdef WC_MLDSA_CACHE_PUB_VECTORS +#if defined(WC_MLDSA_CACHE_PUB_VECTORS) && !defined(WOLF_CRYPTO_CB_ONLY_MLDSA) + /* The caches only feed the native signer and verifier. */ #ifndef WC_MLDSA_FIXED_ARRAY /* Allocate t1 if required. */ if (key->t1 == NULL) { @@ -13032,7 +13218,7 @@ int wc_MlDsaKey_ImportPubRaw(wc_MlDsaKey* key, const byte* in, word32 inLen) /* Compute t1 from public key data. */ mldsa_make_pub_vec(key, key->t1); #endif -#ifdef WC_MLDSA_CACHE_MATRIX_A +#if defined(WC_MLDSA_CACHE_MATRIX_A) && !defined(WOLF_CRYPTO_CB_ONLY_MLDSA) #ifndef WC_MLDSA_FIXED_ARRAY /* Allocate matrix a if required. */ if (key->a == NULL) { @@ -13133,7 +13319,9 @@ static int mldsa_set_priv_key(const byte* priv, word32 privSz, { int ret = 0; int expPrivSz; -#ifdef WC_MLDSA_CACHE_MATRIX_A +#if (defined(WC_MLDSA_CACHE_MATRIX_A) || \ + defined(WC_MLDSA_CACHE_PRIV_VECTORS)) && \ + !defined(WOLF_CRYPTO_CB_ONLY_MLDSA) const wc_MlDsaParams* params = key->params; #endif @@ -13173,8 +13361,9 @@ static int mldsa_set_priv_key(const byte* priv, word32 privSz, #endif } - /* Allocate and create cached values. */ -#ifdef WC_MLDSA_CACHE_MATRIX_A + /* Allocate and create cached values. The caches only feed the + * native signer and verifier. */ +#if defined(WC_MLDSA_CACHE_MATRIX_A) && !defined(WOLF_CRYPTO_CB_ONLY_MLDSA) #ifndef WC_MLDSA_FIXED_ARRAY if (ret == 0) { /* Allocate matrix a if required. */ @@ -13199,7 +13388,7 @@ static int mldsa_set_priv_key(const byte* priv, word32 privSz, } } #endif -#ifdef WC_MLDSA_CACHE_PRIV_VECTORS +#if defined(WC_MLDSA_CACHE_PRIV_VECTORS) && !defined(WOLF_CRYPTO_CB_ONLY_MLDSA) #ifndef WC_MLDSA_FIXED_ARRAY if ((ret == 0) && (key->s1 == NULL)) { /* Allocate L vector s1, K vector s2 and K vector t0 if required. */ @@ -13650,10 +13839,12 @@ int wc_MlDsaKey_PrivateKeyDecode(wc_MlDsaKey* key, const byte* input, if (ret == 0) { /* Generate a key pair if seed exists and decoded key pair is ignored */ if (seedLen != 0) { -#if !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) +#ifndef WOLFSSL_MLDSA_NO_MAKE_KEY + /* The seed is the source of truth and any expanded key alongside + * it is discarded, so both build modes derive the same key. */ if (seedLen == MLDSA_SEED_SZ) { /* runPct 0: this is an import, not a generation. */ - ret = mldsa_key_from_seed_checked(key, seed, 0); + ret = mldsa_key_from_seed_dev(key, seed, 0); } else { ret = ASN_PARSE_E; diff --git a/wolfcrypt/src/wc_mldsa_asm.S b/wolfcrypt/src/wc_mldsa_asm.S index 26669947a60..072ecd02744 100644 --- a/wolfcrypt/src/wc_mldsa_asm.S +++ b/wolfcrypt/src/wc_mldsa_asm.S @@ -43,7 +43,7 @@ #endif /* HAVE_INTEL_AVX512 */ #endif /* NO_AVX512_SUPPORT */ -#ifdef WOLFSSL_HAVE_MLDSA +#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLF_CRYPTO_CB_ONLY_MLDSA) #ifdef HAVE_INTEL_AVX2 #ifndef __APPLE__ .data @@ -39712,8 +39712,8 @@ _wc_mldsa_poly_make_pos_avx2: .size wc_mldsa_poly_make_pos_avx2,.-wc_mldsa_poly_make_pos_avx2 #endif /* __APPLE__ */ #endif /* HAVE_INTEL_AVX2 */ -#endif /* WOLFSSL_HAVE_MLDSA */ -#ifdef WOLFSSL_HAVE_MLDSA +#endif /* WOLFSSL_HAVE_MLDSA && !WOLF_CRYPTO_CB_ONLY_MLDSA */ +#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLF_CRYPTO_CB_ONLY_MLDSA) #ifndef NO_AVX512_SUPPORT #ifndef HAVE_INTEL_AVX512 #define HAVE_INTEL_AVX512 @@ -63818,8 +63818,8 @@ _wc_mldsa_poly_make_pos_avx512: .size wc_mldsa_poly_make_pos_avx512,.-wc_mldsa_poly_make_pos_avx512 #endif /* __APPLE__ */ #endif /* HAVE_INTEL_AVX512 */ -#endif /* WOLFSSL_HAVE_MLDSA */ -#ifdef WOLFSSL_HAVE_MLDSA +#endif /* WOLFSSL_HAVE_MLDSA && !WOLF_CRYPTO_CB_ONLY_MLDSA */ +#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLF_CRYPTO_CB_ONLY_MLDSA) #ifndef NO_AVX512_SUPPORT #ifndef NO_AVX512_VBMI_SUPPORT #ifndef HAVE_INTEL_AVX512_VBMI @@ -64425,7 +64425,7 @@ _wc_mldsa_encode_t0_t1_avx512_vbmi: .size wc_mldsa_encode_t0_t1_avx512_vbmi,.-wc_mldsa_encode_t0_t1_avx512_vbmi #endif /* __APPLE__ */ #endif /* HAVE_INTEL_AVX512_VBMI */ -#endif /* WOLFSSL_HAVE_MLDSA */ +#endif /* WOLFSSL_HAVE_MLDSA && !WOLF_CRYPTO_CB_ONLY_MLDSA */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 260b6e94d22..4a820555a14 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -65324,6 +65324,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t frodokem_test(void) #endif /* WOLFSSL_HAVE_FRODOKEM */ #ifdef WOLFSSL_HAVE_MLDSA +#ifdef WC_MLDSA_HAVE_NATIVE #ifndef WOLFSSL_MLDSA_NO_VERIFY static wc_test_ret_t mldsa_param_vfy_test(int param, const byte* pubKey, word32 pubKeyLen, const byte* sig, word32 sigLen) @@ -69018,6 +69019,7 @@ static wc_test_ret_t mldsa_decode_test(void) } #endif /* (WOLFSSL_MLDSA_PUBLIC_KEY && !WOLFSSL_MLDSA_NO_VERIFY) || * (WOLFSSL_MLDSA_PRIVATE_KEY && !WOLFSSL_MLDSA_NO_SIGN) */ +#endif /* WC_MLDSA_HAVE_NATIVE */ #endif /* WOLFSSL_HAVE_MLDSA - Falcon test below is independent of ML-DSA */ @@ -69687,9 +69689,20 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t falcon_test(void) #if defined(WOLFSSL_HAVE_MLDSA) +/* Any compiled-in level proves the dispatch behaviour; which one is + * irrelevant, so pick the first that is actually built. */ +#ifndef WOLFSSL_NO_ML_DSA_44 + #define MLDSA_CB_ONLY_LEVEL WC_ML_DSA_44 +#elif !defined(WOLFSSL_NO_ML_DSA_65) + #define MLDSA_CB_ONLY_LEVEL WC_ML_DSA_65 +#elif !defined(WOLFSSL_NO_ML_DSA_87) + #define MLDSA_CB_ONLY_LEVEL WC_ML_DSA_87 +#endif + WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mldsa_test(void) { wc_test_ret_t ret; +#ifdef WC_MLDSA_HAVE_NATIVE WC_RNG rng; #ifndef HAVE_FIPS @@ -69779,6 +69792,283 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mldsa_test(void) #endif wc_FreeRng(&rng); return ret; +#else /* !WC_MLDSA_HAVE_NATIVE */ + /* Software ML-DSA is compiled out. Walk the public API with a key that + * has no device behind it and confirm every entry point refuses rather + * than silently doing nothing: NO_VALID_DEVID where a registered device + * could have serviced the call, NOT_COMPILED_IN where the callback + * protocol cannot express the operation at all. */ + ret = 0; +#ifdef MLDSA_CB_ONLY_LEVEL + { + /* wc_MlDsaKey embeds the key buffers, so keep it off the stack as the + * parameter-set helpers in this file do. */ + WC_DECLARE_VAR(key, wc_MlDsaKey, 1, HEAP_HINT); + int key_inited = 0; + int r; +#if !defined(WOLFSSL_MLDSA_NO_SIGN) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) + byte* priv = NULL; +#endif + + WC_ALLOC_VAR(key, wc_MlDsaKey, 1, HEAP_HINT); + if (!WC_VAR_OK(key)) + ret = WC_TEST_RET_ENC_EC(MEMORY_E); + if (ret == 0) { + r = wc_MlDsaKey_Init(key, HEAP_HINT, INVALID_DEVID); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + key_inited = 1; + } + if (ret == 0) { + r = wc_MlDsaKey_SetParams(key, MLDSA_CB_ONLY_LEVEL); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } +#ifndef WOLFSSL_MLDSA_NO_MAKE_KEY + if (ret == 0) { + WC_RNG kgRng; + + r = wc_InitRng_ex(&kgRng, HEAP_HINT, devId); + if (r != 0) { + ret = WC_TEST_RET_ENC_EC(r); + } + else { + r = wc_MlDsaKey_MakeKey(key, &kgRng); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + wc_FreeRng(&kgRng); + } + } + if (ret == 0) { + byte seed[MLDSA_SEED_SZ]; + + XMEMSET(seed, 0, sizeof(seed)); + /* The seed goes to a device now, so with none registered this + * reports a missing device rather than missing code. */ + r = wc_MlDsaKey_MakeKeyFromSeed(key, seed); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } +#endif /* !WOLFSSL_MLDSA_NO_MAKE_KEY */ +#ifndef WOLFSSL_MLDSA_NO_SIGN + if (ret == 0) { + byte sig[4]; + word32 sigLen = (word32)sizeof(sig); + + /* Argument checks still run ahead of the dispatch report: no + * private key is set, so this is refused before the question of + * a device even arises. */ + r = wc_MlDsaKey_SignCtx(key, NULL, 0, sig, &sigLen, + (const byte*)"m", 1, NULL); + if (r != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ret = WC_TEST_RET_ENC_NC; + } +#ifdef WOLFSSL_MLDSA_PRIVATE_KEY + /* Give the key private material so the refusals below come from the + * dispatch rather than from the argument check above. An all-zero + * private key imports cleanly: every s1 and s2 coefficient of that + * encoding reads as in range. */ + if (ret == 0) { + int privSz = wc_MlDsaKey_Size(key); + + if (privSz <= 0) { + ret = WC_TEST_RET_ENC_NC; + } + else { + priv = (byte*)XMALLOC((size_t)privSz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + if (priv == NULL) + ret = WC_TEST_RET_ENC_EC(MEMORY_E); + else + XMEMSET(priv, 0, (size_t)privSz); + } + if (ret == 0) { + r = wc_MlDsaKey_ImportPrivRaw(key, priv, (word32)privSz); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + } + if (ret == 0) { + byte sig[4]; + word32 sigLen = (word32)sizeof(sig); + + r = wc_MlDsaKey_SignCtx(key, NULL, 0, sig, &sigLen, + (const byte*)"m", 1, NULL); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + byte sig[4]; + byte hash[32]; + word32 sigLen = (word32)sizeof(sig); + + XMEMSET(hash, 0, sizeof(hash)); + r = wc_MlDsaKey_SignCtxHash(key, NULL, 0, sig, &sigLen, hash, + (word32)sizeof(hash), WC_HASH_TYPE_SHA256, NULL); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + /* The deterministic entry points take a caller-supplied seed that + * the callback has no field for, so they report NOT_COMPILED_IN. */ + if (ret == 0) { + byte sig[4]; + byte seed[MLDSA_SEED_SZ]; + word32 sigLen = (word32)sizeof(sig); + + XMEMSET(seed, 0, sizeof(seed)); + r = wc_MlDsaKey_SignCtxWithSeed(key, NULL, 0, sig, &sigLen, + (const byte*)"m", 1, seed); + if (r != WC_NO_ERR_TRACE(NOT_COMPILED_IN)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + byte sig[4]; + byte hash[32]; + byte seed[MLDSA_SEED_SZ]; + word32 sigLen = (word32)sizeof(sig); + + XMEMSET(hash, 0, sizeof(hash)); + XMEMSET(seed, 0, sizeof(seed)); + r = wc_MlDsaKey_SignCtxHashWithSeed(key, NULL, 0, sig, &sigLen, + hash, (word32)sizeof(hash), WC_HASH_TYPE_SHA256, seed); + if (r != WC_NO_ERR_TRACE(NOT_COMPILED_IN)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + byte sig[4]; + byte mu[MLDSA_MU_SZ]; + byte seed[MLDSA_SEED_SZ]; + word32 sigLen = (word32)sizeof(sig); + + XMEMSET(mu, 0, sizeof(mu)); + XMEMSET(seed, 0, sizeof(seed)); + r = wc_MlDsaKey_SignMuWithSeed(key, sig, &sigLen, mu, + (word32)sizeof(mu), seed); + if (r != WC_NO_ERR_TRACE(NOT_COMPILED_IN)) + ret = WC_TEST_RET_ENC_NC; + } +#ifdef WOLFSSL_MLDSA_NO_CTX + if (ret == 0) { + byte sig[4]; + word32 sigLen = (word32)sizeof(sig); + + r = wc_MlDsaKey_Sign(key, sig, &sigLen, (const byte*)"m", 1, NULL); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + byte sig[4]; + byte seed[MLDSA_SEED_SZ]; + word32 sigLen = (word32)sizeof(sig); + + XMEMSET(seed, 0, sizeof(seed)); + r = wc_MlDsaKey_SignWithSeed(key, sig, &sigLen, (const byte*)"m", + 1, seed); + if (r != WC_NO_ERR_TRACE(NOT_COMPILED_IN)) + ret = WC_TEST_RET_ENC_NC; + } +#endif /* WOLFSSL_MLDSA_NO_CTX */ +#endif /* WOLFSSL_MLDSA_PRIVATE_KEY */ +#endif /* !WOLFSSL_MLDSA_NO_SIGN */ +#ifndef WOLFSSL_MLDSA_NO_VERIFY + if (ret == 0) { + byte sig[4]; + word32 sigLen = (word32)sizeof(sig); + int res = 0; + + r = wc_MlDsaKey_VerifyCtx(key, sig, sigLen, NULL, 0, + (const byte*)"m", 1, &res); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + byte sig[4]; + byte hash[32]; + word32 sigLen = (word32)sizeof(sig); + int res = 0; + + XMEMSET(hash, 0, sizeof(hash)); + r = wc_MlDsaKey_VerifyCtxHash(key, sig, sigLen, NULL, 0, hash, + (word32)sizeof(hash), WC_HASH_TYPE_SHA256, &res); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + byte sig[4]; + byte mu[MLDSA_MU_SZ]; + word32 sigLen = (word32)sizeof(sig); + int res = 0; + + XMEMSET(mu, 0, sizeof(mu)); + /* The callback has no way to say the input is mu rather than a + * message, so no device could take this one either. */ + r = wc_MlDsaKey_VerifyMu(key, sig, sigLen, mu, + (word32)sizeof(mu), &res); + if (r != WC_NO_ERR_TRACE(NOT_COMPILED_IN)) + ret = WC_TEST_RET_ENC_NC; + } +#ifdef WOLFSSL_MLDSA_NO_CTX + if (ret == 0) { + byte sig[4]; + word32 sigLen = (word32)sizeof(sig); + int res = 0; + + r = wc_MlDsaKey_Verify(key, sig, sigLen, (const byte*)"m", 1, + &res); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } +#endif /* WOLFSSL_MLDSA_NO_CTX */ +#endif /* !WOLFSSL_MLDSA_NO_VERIFY */ +#ifdef WOLFSSL_MLDSA_CHECK_KEY + if (ret == 0) { + r = wc_MlDsaKey_CheckKey(key); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } +#endif +#if !defined(WOLFSSL_MLDSA_NO_ASN1) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \ + !defined(WOLFSSL_NO_ML_DSA_44) + if (ret == 0) { + /* A OneAsymmetricKey holding only the 32 byte seed for + * ML-DSA-44. Decoding it means expanding the seed: a device is + * asked to do that, so with none registered the report is a + * missing device -- unless key generation is compiled out, when + * there is no seed path at all. */ + static const byte seedOnlyKey[] = { + 0x30, 0x34, 0x02, 0x01, 0x00, 0x30, 0x0b, 0x06, + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, + 0x03, 0x11, 0x04, 0x22, 0x80, 0x20, 0x67, 0x1b, + 0x9a, 0x54, 0xb4, 0x72, 0x81, 0xbd, 0x1d, 0xb1, + 0x97, 0xab, 0x6d, 0x64, 0x38, 0x0b, 0x0c, 0x1b, + 0x17, 0x9d, 0xcb, 0x46, 0x20, 0xc9, 0x46, 0x3d, + 0xd4, 0x4f, 0x80, 0x87, 0xa2, 0x16 + }; + word32 idx = 0; + + r = wc_MlDsaKey_PrivateKeyDecode(key, seedOnlyKey, + (word32)sizeof(seedOnlyKey), &idx); +#ifndef WOLFSSL_MLDSA_NO_MAKE_KEY + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) +#else + if (r != WC_NO_ERR_TRACE(NOT_COMPILED_IN)) +#endif + ret = WC_TEST_RET_ENC_NC; + } +#endif + if (key_inited) + wc_MlDsaKey_Free(key); +#if !defined(WOLFSSL_MLDSA_NO_SIGN) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) + /* Freed after the key: WOLFSSL_MLDSA_ASSIGN_KEY builds keep the + * pointer instead of copying the bytes. */ + XFREE(priv, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); +#endif + WC_FREE_VAR(key, HEAP_HINT); + } +#endif /* MLDSA_CB_ONLY_LEVEL */ + return ret; +#endif /* WC_MLDSA_HAVE_NATIVE */ } #endif /* WOLFSSL_HAVE_MLDSA */ @@ -82724,7 +83014,8 @@ static wc_test_ret_t pkcs7_signed_no_content_test(byte* cert, word32 certSz, #endif /* !NO_RSA && !NO_SHA256 */ -#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_ASN1) && \ +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ + !defined(WOLFSSL_MLDSA_NO_ASN1) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) && !defined(WOLFSSL_MLDSA_NO_VERIFY) && \ !defined(NO_FILESYSTEM) && !defined(NO_ASN) @@ -83296,7 +83587,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs7signed_test(void) rsaCaCertBuf, (word32)rsaCaCertBufSz); #endif -#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_ASN1) && \ +#if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) && \ + !defined(WOLFSSL_MLDSA_NO_ASN1) && \ !defined(WOLFSSL_MLDSA_NO_SIGN) && !defined(WOLFSSL_MLDSA_NO_VERIFY) && \ !defined(NO_FILESYSTEM) && !defined(NO_ASN) if (ret >= 0) @@ -88593,6 +88885,12 @@ 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_MLDSA) + int mldsaCount; /* ML-DSA callback invocations */ + int mldsaFail; /* when set, the ML-DSA handler returns this error */ + int mldsaCbActive; /* when clear, the handler declines so a native build + * still runs the real ML-DSA test */ +#endif #ifdef HAVE_ECC int eccMakePubCount; /* EC make-pub callback invocations */ int eccCheckPubCount; /* EC check-pubkey callback invocations */ @@ -89754,6 +90052,37 @@ static int myCryptoCbExportPointX963(const ecc_set_type* dp, ecc_point* pub, #endif /* HAVE_ECC && !WOLFSSL_NO_MALLOC && HAVE_ECC_KEY_EXPORT */ /* Example crypto dev callback function that calls software version */ +#if defined(WOLFSSL_HAVE_MLDSA) +#define MLDSA_CB_SIG_LEN 32 + +/* Seed the device is asked to generate from, shared with the test below so + * the handler can confirm it arrived unchanged. */ +static const byte mldsa_cb_seed[MLDSA_SEED_SZ] = { + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf +}; +/* Deterministic stand-in signature for the callback-only ML-DSA device: it + * covers the message, the context and the pre-hash selector, so a dispatch + * that loses any of them fails the matching verify. */ +static void mldsa_cb_sign(const byte* msg, word32 msgLen, const byte* ctx, + byte ctxLen, word32 preHashType, byte* out) +{ + word32 i; + + for (i = 0; i < MLDSA_CB_SIG_LEN; i++) { + byte b = (byte)(0x5a ^ (byte)i ^ (byte)ctxLen ^ (byte)preHashType); + + if (msgLen > 0) + b ^= msg[i % msgLen]; + if ((ctx != NULL) && (ctxLen > 0)) + b ^= ctx[i % ctxLen]; + out[i] = b; + } +} +#endif + static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) { int ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); /* return this to bypass HW and @@ -90927,9 +91256,10 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) myCtx->exampleVar++; } #endif /* HAVE_FALCON && !WOLF_CRYPTO_CB_ONLY_FALCON */ - #ifdef WOLFSSL_HAVE_MLDSA + #if defined(WOLFSSL_HAVE_MLDSA) && defined(WC_MLDSA_HAVE_NATIVE) #ifndef WOLFSSL_MLDSA_NO_MAKE_KEY - if (info->pk.type == WC_PK_TYPE_PQC_SIG_KEYGEN) { + if ((info->pk.type == WC_PK_TYPE_PQC_SIG_KEYGEN) && + !myCtx->mldsaCbActive) { if ((info->pk.pqc_sig_kg.type == WC_PQC_SIG_TYPE_MLDSA) && (info->pk.pqc_sig_kg.key != NULL)) { wc_MlDsaKey* key = (wc_MlDsaKey*)info->pk.pqc_sig_kg.key; @@ -90951,7 +91281,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) #if !defined(WOLFSSL_MLDSA_NO_SIGN) && !defined(WOLFSSL_MLDSA_NO_CTX) /* WOLFSSL_MLDSA_NO_CTX makes Sign() and SignCtx() with an empty * context indistinguishable here, so leave both to software. */ - if (info->pk.type == WC_PK_TYPE_PQC_SIG_SIGN) { + if ((info->pk.type == WC_PK_TYPE_PQC_SIG_SIGN) && + !myCtx->mldsaCbActive) { if ((info->pk.pqc_sign.type == WC_PQC_SIG_TYPE_MLDSA) && (info->pk.pqc_sign.key != NULL)) { wc_MlDsaKey* key = (wc_MlDsaKey*)info->pk.pqc_sign.key; @@ -90990,7 +91321,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) #endif #if !defined(WOLFSSL_MLDSA_NO_VERIFY) && !defined(WOLFSSL_MLDSA_NO_CTX) /* Omitted under WOLFSSL_MLDSA_NO_CTX; see the sign branch. */ - if (info->pk.type == WC_PK_TYPE_PQC_SIG_VERIFY) { + if ((info->pk.type == WC_PK_TYPE_PQC_SIG_VERIFY) && + !myCtx->mldsaCbActive) { if ((info->pk.pqc_verify.type == WC_PQC_SIG_TYPE_MLDSA) && (info->pk.pqc_verify.key != NULL)) { wc_MlDsaKey* key = (wc_MlDsaKey*)info->pk.pqc_verify.key; @@ -91028,6 +91360,116 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } } #endif + #endif /* WOLFSSL_HAVE_MLDSA && WC_MLDSA_HAVE_NATIVE */ + #if defined(WOLFSSL_HAVE_MLDSA) + /* This device cannot delegate to the public API the way the other + * handlers do; it would dispatch straight back here, and under + * CB_ONLY there is no software core to reach. It answers with its + * own deterministic signature instead, which proves the dispatch + * reaches a device for all four ML-DSA operations, that the signature + * and the verify result travel back, and that a device error is + * reported as-is. The signature covers the message and the context, + * so a call site that drops either is caught by the verify step. */ + if (!myCtx->mldsaCbActive) { + /* Leave ret unchanged */ + } + else if (((info->pk.type == WC_PK_TYPE_PQC_SIG_KEYGEN) || + (info->pk.type == WC_PK_TYPE_PQC_SIG_KEYGEN_SEED)) && + (info->pk.pqc_sig_kg.type == WC_PQC_SIG_TYPE_MLDSA)) { + myCtx->mldsaCount++; + if (myCtx->mldsaFail != 0) { + ret = myCtx->mldsaFail; + } + else if (info->pk.type == WC_PK_TYPE_PQC_SIG_KEYGEN_SEED) { + /* Seeded generation: the seed must arrive whole. A real + * device would expand it; this one only checks it. */ + ret = ((info->pk.pqc_sig_kg.seedSz == + (word32)sizeof(mldsa_cb_seed)) && + (XMEMCMP(info->pk.pqc_sig_kg.seed, mldsa_cb_seed, + sizeof(mldsa_cb_seed)) == 0)) ? + 0 : WC_NO_ERR_TRACE(BAD_STATE_E); + } + else { + /* The device keeps the key material. The key flags say + * whether this object holds the bytes, and it holds neither, + * so both stay clear. Signing and verifying dispatch before + * those flags are looked at. */ + /* A plain keygen carrying a seed means a seeded call reached + * the device as a request for an unrelated random key. */ + ret = (info->pk.pqc_sig_kg.seed == NULL) ? 0 : + WC_NO_ERR_TRACE(BAD_STATE_E); + } + } + else if ((info->pk.type == WC_PK_TYPE_PQC_SIG_SIGN) && + (info->pk.pqc_sign.type == WC_PQC_SIG_TYPE_MLDSA)) { + myCtx->mldsaCount++; + if (myCtx->mldsaFail != 0) { + ret = myCtx->mldsaFail; + } + else if (*info->pk.pqc_sign.outlen < MLDSA_CB_SIG_LEN) { + ret = BUFFER_E; + } + else { + mldsa_cb_sign(info->pk.pqc_sign.in, info->pk.pqc_sign.inlen, + info->pk.pqc_sign.context, info->pk.pqc_sign.contextLen, + info->pk.pqc_sign.preHashType, info->pk.pqc_sign.out); + *info->pk.pqc_sign.outlen = MLDSA_CB_SIG_LEN; + ret = 0; + } + } + else if ((info->pk.type == WC_PK_TYPE_PQC_SIG_VERIFY) && + (info->pk.pqc_verify.type == WC_PQC_SIG_TYPE_MLDSA)) { + myCtx->mldsaCount++; + if (myCtx->mldsaFail != 0) { + ret = myCtx->mldsaFail; + } + else { + byte expected[MLDSA_CB_SIG_LEN]; + + mldsa_cb_sign(info->pk.pqc_verify.msg, + info->pk.pqc_verify.msglen, info->pk.pqc_verify.context, + info->pk.pqc_verify.contextLen, + info->pk.pqc_verify.preHashType, expected); + if (info->pk.pqc_verify.res != NULL) { + *info->pk.pqc_verify.res = + ((info->pk.pqc_verify.siglen == MLDSA_CB_SIG_LEN) && + (XMEMCMP(info->pk.pqc_verify.sig, expected, + MLDSA_CB_SIG_LEN) == 0)) ? 1 : 0; + } + ret = 0; + } + } + #ifdef WOLFSSL_MLDSA_CHECK_KEY + else if ((info->pk.type == WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY) && + (info->pk.pqc_sig_check.type == WC_PQC_SIG_TYPE_MLDSA)) { + wc_MlDsaKey* ck = (wc_MlDsaKey*)info->pk.pqc_sig_check.key; + + myCtx->mldsaCount++; + if (myCtx->mldsaFail != 0) { + ret = myCtx->mldsaFail; + } + else if (ck == NULL) { + ret = BAD_FUNC_ARG; + } + else if (!ck->pubKeySet) { + /* No local public key: a real device compares against its own + * copy, so anything sent here would be material the caller + * never had. */ + ret = ((info->pk.pqc_sig_check.pubKey == NULL) && + (info->pk.pqc_sig_check.pubKeySz == 0)) ? + 0 : WC_NO_ERR_TRACE(BAD_STATE_E); + } + else { + /* Public key present: it must arrive whole. */ + int ckSz = wc_MlDsaKey_PubSize(ck); + + ret = ((ckSz > 0) && + (info->pk.pqc_sig_check.pubKey != NULL) && + (info->pk.pqc_sig_check.pubKeySz == (word32)ckSz)) ? + 0 : WC_NO_ERR_TRACE(BAD_STATE_E); + } + } + #endif /* WOLFSSL_MLDSA_CHECK_KEY */ #endif /* WOLFSSL_HAVE_MLDSA */ #ifdef WOLFSSL_HAVE_MLKEM #ifndef WOLFSSL_MLKEM_NO_MAKE_KEY @@ -93957,6 +94399,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) /* example data for callback */ myCtx.exampleVar = 1; +#if defined(WOLFSSL_HAVE_MLDSA) + myCtx.mldsaCount = 0; + myCtx.mldsaFail = 0; + myCtx.mldsaCbActive = 0; +#endif #ifdef HAVE_ECC myCtx.eccMakePubCount = 0; myCtx.eccCheckPubCount = 0; @@ -94639,6 +95086,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) ret = mldsa_test(); + #ifdef WC_MLDSA_HAVE_NATIVE #if !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && !defined(HAVE_FIPS) if ((ret == 0) && (myCtx.mldsaKeyGenCount == 0)) ret = WC_TEST_RET_ENC_NC; @@ -94666,7 +95114,165 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) if ((ret == 0) && (myCtx.mldsaVerifyHashCount == 0)) ret = WC_TEST_RET_ENC_NC; #endif + #endif /* WC_MLDSA_HAVE_NATIVE */ } +#if !defined(WOLFSSL_MLDSA_NO_SIGN) && \ + !defined(WOLFSSL_MLDSA_NO_VERIFY) && \ + !defined(WOLFSSL_MLDSA_NO_MAKE_KEY) && \ + !defined(WC_NO_RNG) && defined(MLDSA_CB_ONLY_LEVEL) + /* Drive key generation, seeded generation, signing, verifying and the + * private-key check through a registered device and confirm the results + * came back. Runs in native builds too, so the seeded dispatch is covered + * where a software path exists to hide a regression. */ + myCtx.mldsaCbActive = 1; + if (ret == 0) { + WC_DECLARE_VAR(key, wc_MlDsaKey, 1, HEAP_HINT); + WC_DECLARE_VAR(mldsaRng, WC_RNG, 1, HEAP_HINT); + byte sig[MLDSA_CB_SIG_LEN]; + word32 sigLen = (word32)sizeof(sig); + static const byte msg[] = "wolfSSL ML-DSA callback-only dispatch"; + static const byte sigCtx[] = { 0x01, 0x02, 0x03 }; + int key_inited = 0; + int rng_inited = 0; + int res = 0; + int r; + + WC_ALLOC_VAR(key, wc_MlDsaKey, 1, HEAP_HINT); + WC_ALLOC_VAR(mldsaRng, WC_RNG, 1, HEAP_HINT); + if ((!WC_VAR_OK(key)) || (!WC_VAR_OK(mldsaRng))) + ret = WC_TEST_RET_ENC_EC(MEMORY_E); + if (ret == 0) { + r = wc_InitRng_ex(mldsaRng, HEAP_HINT, devId); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + rng_inited = 1; + } + if (ret == 0) { + r = wc_MlDsaKey_Init(key, HEAP_HINT, devId); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + key_inited = 1; + } + if (ret == 0) { + r = wc_MlDsaKey_SetParams(key, MLDSA_CB_ONLY_LEVEL); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + /* Key generation has a software fallback in native builds, so require + * that each call reached the device. */ + if (ret == 0) { + int before = myCtx.mldsaCount; + + r = wc_MlDsaKey_MakeKey(key, mldsaRng); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (myCtx.mldsaCount != before + 1) + ret = WC_TEST_RET_ENC_NC; + } +#ifndef WOLFSSL_MLDSA_NO_MAKE_KEY + /* Generating from a caller supplied seed is the device's job too: + * the handler fails the call if the seed does not arrive whole. */ + if (ret == 0) { + int before = myCtx.mldsaCount; + + r = wc_MlDsaKey_MakeKeyFromSeed(key, mldsa_cb_seed); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (myCtx.mldsaCount != before + 1) + ret = WC_TEST_RET_ENC_NC; + } +#endif + if (ret == 0) { + r = wc_MlDsaKey_SignCtx(key, sigCtx, (byte)sizeof(sigCtx), sig, + &sigLen, msg, (word32)sizeof(msg), mldsaRng); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (sigLen != MLDSA_CB_SIG_LEN) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + r = wc_MlDsaKey_VerifyCtx(key, sig, sigLen, sigCtx, + (byte)sizeof(sigCtx), msg, (word32)sizeof(msg), &res); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (res != 1) + ret = WC_TEST_RET_ENC_NC; + } + /* A different context must not verify: proves the context reached the + * device rather than being dropped on the way. */ + if (ret == 0) { + static const byte otherCtx[] = { 0x09, 0x09, 0x09 }; + + res = 1; + r = wc_MlDsaKey_VerifyCtx(key, sig, sigLen, otherCtx, + (byte)sizeof(otherCtx), msg, (word32)sizeof(msg), &res); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (res != 0) + ret = WC_TEST_RET_ENC_NC; + } +#ifdef WOLFSSL_MLDSA_CHECK_KEY + /* This is the operation a no-device test cannot cover: before the key + * check dispatched, it could only ever fail. The handler asserts that + * a key generated on the device sends no public key with it. */ + if (ret == 0) { + r = wc_MlDsaKey_CheckKey(key); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } +#ifdef WOLFSSL_MLDSA_PUBLIC_KEY + /* Import a public key and check again: now the bytes are local, so + * the handler asserts they arrive whole. */ + if (ret == 0) { + int pubSz = wc_MlDsaKey_PubSize(key); + byte* pubRaw = NULL; + + if (pubSz <= 0) { + ret = WC_TEST_RET_ENC_NC; + } + else { + pubRaw = (byte*)XMALLOC((word32)pubSz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + if (pubRaw == NULL) + ret = WC_TEST_RET_ENC_EC(MEMORY_E); + else + XMEMSET(pubRaw, 0x5a, (word32)pubSz); + } + if (ret == 0) { + r = wc_MlDsaKey_ImportPubRaw(key, pubRaw, (word32)pubSz); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + r = wc_MlDsaKey_CheckKey(key); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + XFREE(pubRaw, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif /* WOLFSSL_MLDSA_PUBLIC_KEY */ +#endif + /* A device error must reach the caller unchanged. */ + if (ret == 0) { + myCtx.mldsaFail = WC_NO_ERR_TRACE(WC_HW_E); + sigLen = (word32)sizeof(sig); + r = wc_MlDsaKey_SignCtx(key, sigCtx, (byte)sizeof(sigCtx), sig, + &sigLen, msg, (word32)sizeof(msg), mldsaRng); + myCtx.mldsaFail = 0; + if (r != WC_NO_ERR_TRACE(WC_HW_E)) + ret = WC_TEST_RET_ENC_NC; + } + if (key_inited) + wc_MlDsaKey_Free(key); + if (rng_inited) + wc_FreeRng(mldsaRng); + WC_FREE_VAR(mldsaRng, HEAP_HINT); + WC_FREE_VAR(key, HEAP_HINT); + } + myCtx.mldsaCbActive = 0; +#endif /* sign && verify && make key && !WC_NO_RNG */ #endif #ifdef WOLFSSL_HAVE_SLHDSA if (ret == 0) { diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 85f98891574..1a88d9c1969 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_MLDSA) && !defined(WOLF_CRYPTO_CB) + #error "WOLF_CRYPTO_CB_ONLY_MLDSA requires WOLF_CRYPTO_CB" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_MLDSA) && !defined(WOLFSSL_HAVE_MLDSA) + #error "WOLF_CRYPTO_CB_ONLY_MLDSA requires WOLFSSL_HAVE_MLDSA" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_MLDSA) && defined(HAVE_FIPS) + /* Key generation runs a pairwise consistency test that the callback would + * have to service; not validated. */ + #error "WOLF_CRYPTO_CB_ONLY_MLDSA 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_mldsa.h b/wolfssl/wolfcrypt/wc_mldsa.h index 34ba33efc26..e93c40f16be 100644 --- a/wolfssl/wolfcrypt/wc_mldsa.h +++ b/wolfssl/wolfcrypt/wc_mldsa.h @@ -592,6 +592,8 @@ typedef struct wc_MlDsaParams MlDsaParams; #endif struct wc_MlDsaKey { + /* Set when this object holds the public key bytes in p. A key generated + * on a crypto-callback device has none here and leaves it clear. */ byte pubKeySet; byte prvKeySet; byte level; /* 2,3 or 5 */ @@ -1114,5 +1116,16 @@ WOLFSSL_TEST_VIS void wc_mldsa_encode_w1_32(const sword32* w1, byte* w1e); } /* extern "C" */ #endif +/* Native implementation core (internal). The public wc_MlDsaKey_* functions + * in wc_mldsa.c wrap it with cryptocb dispatch and argument checking. With + * WOLF_CRYPTO_CB_ONLY_MLDSA the native core is not compiled: all operations go + * through the crypto callback. */ +#ifndef WOLF_CRYPTO_CB_ONLY_MLDSA +/* Signals that native key generation, signing and verifying 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_MLDSA_HAVE_NATIVE +#endif + #endif /* WOLFSSL_HAVE_MLDSA */ #endif /* WOLF_CRYPT_WC_MLDSA_H */