From 01fe1f94ff9bebad7b29293bf66747273b1ce876 Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Fri, 25 Sep 2026 10:17:24 -0700 Subject: [PATCH] Add CB_ONLY mode for LMS+XMSS, add swdev --- .github/workflows/cryptocb-only.yml | 28 +- configure.ac | 6 + tests/api/test_lms_xmss.c | 102 ++++- tests/api/test_lms_xmss.h | 2 + tests/swdev/README.md | 6 + tests/swdev/swdev.c | 568 +++++++++++++++++++++++++++- tests/swdev/user_settings.h | 2 + wolfcrypt/src/cryptocb.c | 14 + wolfcrypt/src/wc_lms.c | 89 ++++- wolfcrypt/src/wc_lms_impl.c | 5 + wolfcrypt/src/wc_xmss.c | 79 +++- wolfcrypt/src/wc_xmss_impl.c | 5 + wolfcrypt/test/test.c | 46 ++- wolfssl/wolfcrypt/settings.h | 18 + wolfssl/wolfcrypt/wc_lms.h | 2 + wolfssl/wolfcrypt/wc_xmss.h | 2 + 16 files changed, 932 insertions(+), 42 deletions(-) diff --git a/.github/workflows/cryptocb-only.yml b/.github/workflows/cryptocb-only.yml index e0bf89a2fc..d27c1664cc 100644 --- a/.github/workflows/cryptocb-only.yml +++ b/.github/workflows/cryptocb-only.yml @@ -163,18 +163,30 @@ jobs: {"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": "lms", "minutes": 3, + "comment": "WOLF_CRYPTO_CB_ONLY_LMS: strips the software LMS/HSS core (wc_lms_impl.c in full); swdev provides the software path via cryptocb. The private key state stays with the caller's read/write callbacks, so the stateful signing path is exercised end to end.", + "configure": ["--enable-lms", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_LMS"]}, + {"name": "xmss", "minutes": 3, + "comment": "WOLF_CRYPTO_CB_ONLY_XMSS: strips the software XMSS/XMSS^MT core (wc_xmss_impl.c in full); swdev provides the software path via cryptocb. Parameter parsing and key sizing stay in wc_xmss.c because the public API needs them with the core gone.", + "configure": ["--enable-xmss", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_XMSS"]}, + {"name": "lms-xmss", "minutes": 4, + "comment": "Both stateful-signature cores stripped together, which is the combination wolfHSM presents: it supports LMS and XMSS on the same device. WOLF_CRYPTO_CB_FREE is on so swdev releases each device-resident key when the caller frees it, rather than only in its cleanup sweep.", + "configure": ["--enable-lms", "--enable-xmss", "--enable-cryptocbutils=free", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_LMS -DWOLF_CRYPTO_CB_ONLY_XMSS"]}, + {"name": "lms-xmss-verify-only", "minutes": 2, + "comment": "Both stripped with signing compiled out (WOLFSSL_LMS_VERIFY_ONLY, WOLFSSL_XMSS_VERIFY_ONLY). swdev has to drop its keygen, sign and signatures-left handlers and keep only verification.", + "configure": ["--enable-lms=verify-only", "--enable-xmss=verify-only", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_LMS -DWOLF_CRYPTO_CB_ONLY_XMSS"]}, {"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 ten 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 -DWOLF_CRYPTO_CB_ONLY_MLKEM"]}, - {"name": "all-mldsa", "minutes": 4.5, + {"name": "all", "minutes": 21, + "comment": "All twelve 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", "--enable-lms", "--enable-xmss", "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 -DWOLF_CRYPTO_CB_ONLY_LMS -DWOLF_CRYPTO_CB_ONLY_XMSS"]}, + {"name": "all-mldsa", "minutes": 6.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_MLKEM -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"]}, + "configure": ["--enable-lms", "--enable-xmss", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ECC -DWOLF_CRYPTO_CB_ONLY_RSA -DWOLF_CRYPTO_CB_ONLY_SHA256 -DWOLF_CRYPTO_CB_ONLY_SHA512 -DWOLF_CRYPTO_CB_ONLY_AES -DWOLF_CRYPTO_CB_ONLY_ED25519 -DWOLF_CRYPTO_CB_ONLY_CURVE25519 -DWOLF_CRYPTO_CB_ONLY_CURVE448 -DWOLF_CRYPTO_CB_ONLY_MLKEM -DWOLF_CRYPTO_CB_ONLY_MLDSA -DWOLF_CRYPTO_CB_ONLY_LMS -DWOLF_CRYPTO_CB_ONLY_XMSS"]}, + {"name": "only", "minutes": 6.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 enabled here. 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-lms", "--enable-xmss", "--enable-cryptocb=only"]}, {"name": "setkey", "minutes": 4.0, "comment": "--enable-cryptocbutils enables all cryptocb utilities (COPY/FREE/SETKEY/EXPORT_KEY/KEYSTORE); WOLF_CRYPTO_CB_SETKEY is what lets test_wc_CryptoCb_TLS_CBC_HMAC run -- a TLS 1.2 CBC MtE handshake whose record HMAC is computed by a key-owning callback, the only config exercising the TLS_hmac update/final routing. No ONLY_* strip, so the device's software HMAC keeps its providers.", "configure": ["--enable-cryptocbutils"]} diff --git a/configure.ac b/configure.ac index 3a24e88e18..5128cf4da5 100644 --- a/configure.ac +++ b/configure.ac @@ -12300,6 +12300,12 @@ then if test "$ENABLED_MLDSA" != "no"; then AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_MLDSA" fi + if test "$ENABLED_LMS" != "no"; then + AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_LMS" + fi + if test "$ENABLED_XMSS" != "no"; then + AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_XMSS" + fi fi if test "$ENABLED_CRYPTOCB_SW_TEST" = "no" diff --git a/tests/api/test_lms_xmss.c b/tests/api/test_lms_xmss.c index c6e170c6cf..1566e70480 100644 --- a/tests/api/test_lms_xmss.c +++ b/tests/api/test_lms_xmss.c @@ -281,8 +281,9 @@ int test_wc_LmsKey_write_fail(void) int test_wc_LmsKey_reload_cache(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ - (!defined(WOLFSSL_LMS_MAX_HEIGHT) || (WOLFSSL_LMS_MAX_HEIGHT >= 10)) +#if !defined(WOLF_CRYPTO_CB_ONLY_LMS) && \ + (defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ + (!defined(WOLFSSL_LMS_MAX_HEIGHT) || (WOLFSSL_LMS_MAX_HEIGHT >= 10))) LmsKey key; LmsKey vkey; WC_RNG rng; @@ -364,9 +365,10 @@ int test_wc_LmsKey_reload_cache(void) * no caller and -Wunused-function fails the build. */ #if defined(WOLF_CRYPTO_CB) && \ ((defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ - !defined(NO_FILESYSTEM)) || \ + !defined(NO_FILESYSTEM) && !defined(WOLF_CRYPTO_CB_ONLY_LMS)) || \ (defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ - !defined(NO_FILESYSTEM) && defined(TEST_XMSS_H10_AVAILABLE))) + !defined(NO_FILESYSTEM) && defined(TEST_XMSS_H10_AVAILABLE) && \ + !defined(WOLF_CRYPTO_CB_ONLY_XMSS))) /* An accelerator with no stateful hash-based signature support: declines * everything, so the software implementation is used. */ @@ -418,8 +420,9 @@ static int test_lms_xmss_verify_cryptocb(int devIdArg, wc_CryptoInfo* info, int test_wc_LmsKey_reload_devid(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ - defined(WOLF_CRYPTO_CB) && !defined(NO_FILESYSTEM) +#if !defined(WOLF_CRYPTO_CB_ONLY_LMS) && \ + (defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ + defined(WOLF_CRYPTO_CB) && !defined(NO_FILESYSTEM)) LmsKey key; LmsKey vkey; LmsKey hsmKey; @@ -497,8 +500,9 @@ int test_wc_LmsKey_reload_devid(void) int test_wc_LmsKey_reload_no_pub(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ - !defined(NO_FILESYSTEM) +#if !defined(WOLF_CRYPTO_CB_ONLY_LMS) && \ + (defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ + !defined(NO_FILESYSTEM)) LmsKey key; LmsKey dst; WC_RNG rng; @@ -595,8 +599,57 @@ int test_wc_LmsKey_reload_devid_verify(void) return EXPECT_RESULT(); } +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ + defined(WOLF_CRYPTO_CB) +/* An accelerator that generates LMS keys but cannot report signatures left. */ +static int test_lms_keygen_cryptocb(int devIdArg, wc_CryptoInfo* info, + void* ctx) +{ + (void)devIdArg; + (void)ctx; + + if ((info != NULL) && (info->algo_type == WC_ALGO_TYPE_PK) && + (info->pk.type == WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN)) { + return 0; + } + return CRYPTOCB_UNAVAILABLE; +} +#endif + +/* + * Test that an LMS key generated on a device without SIGS_LEFT support is + * usable, as the device declining the query does not mean no signatures. + */ +int test_wc_LmsKey_keygen_no_sigsleft(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ + defined(WOLF_CRYPTO_CB) + LmsKey key; + WC_RNG rng; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_LMS_XMSS_CRYPTOCB_DEVID, + test_lms_keygen_cryptocb, NULL), 0); + ExpectIntEQ(wc_InitRng(&rng), 0); + + ExpectIntEQ(wc_LmsKey_Init(&key, NULL, TEST_LMS_XMSS_CRYPTOCB_DEVID), 0); + ExpectIntEQ(test_lms_set_params(&key), 0); + ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0); + ExpectIntEQ(key.state, WC_LMS_STATE_OK); + + wc_LmsKey_Free(&key); + wc_FreeRng(&rng); + wc_CryptoCb_UnRegisterDevice(TEST_LMS_XMSS_CRYPTOCB_DEVID); +#endif + return EXPECT_RESULT(); +} + #if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ - !defined(NO_FILESYSTEM) && defined(TEST_XMSS_H10_AVAILABLE) + !defined(NO_FILESYSTEM) && defined(TEST_XMSS_H10_AVAILABLE) && \ + !defined(WOLF_CRYPTO_CB_ONLY_XMSS) /* Per-process temp file so parallel unit.test runs sharing a working * directory do not clobber each other's stateful XMSS private key. */ static const char* xmss_devid_priv_key_file(void) @@ -667,9 +720,10 @@ static int test_xmss_init_key_ex(XmssKey* key, int devId) int test_wc_XmssKey_reload_devid(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ - defined(WOLF_CRYPTO_CB) && !defined(NO_FILESYSTEM) && \ - defined(TEST_XMSS_H10_AVAILABLE) +#if !defined(WOLF_CRYPTO_CB_ONLY_XMSS) && \ + (defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ + defined(WOLF_CRYPTO_CB) && !defined(NO_FILESYSTEM) && \ + defined(TEST_XMSS_H10_AVAILABLE)) XmssKey key; XmssKey vkey; XmssKey hsmKey; @@ -737,8 +791,9 @@ int test_wc_XmssKey_reload_devid(void) int test_wc_XmssKey_reload_no_pub(void) { EXPECT_DECLS; -#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ - !defined(NO_FILESYSTEM) && defined(TEST_XMSS_H10_AVAILABLE) +#if !defined(WOLF_CRYPTO_CB_ONLY_XMSS) && \ + (defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ + !defined(NO_FILESYSTEM) && defined(TEST_XMSS_H10_AVAILABLE)) XmssKey key; XmssKey dst; WC_RNG rng; @@ -2154,6 +2209,25 @@ int test_wc_LmsFeatureCoverage(void) } #endif + /* Keygen and sign need only the write callback. */ + { + LmsKey key; + byte msg[] = "lms write-only message"; + byte sig[8192]; + word32 sigSz = sizeof(sig); + + XMEMSET(&key, 0, sizeof(key)); + lms_mc_privSz = 0; + ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0); + ExpectIntEQ(wc_LmsKey_SetParameters(&key, 1, 5, 8), 0); + ExpectIntEQ(wc_LmsKey_SetWriteCb(&key, lms_mc_write_key), 0); + ExpectIntEQ(wc_LmsKey_SetContext(&key, (void*)lms_mc_priv), 0); + ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0); + ExpectIntEQ(wc_LmsKey_Sign(&key, sig, &sigSz, msg, sizeof(msg)), 0); + ExpectIntEQ(wc_LmsKey_Verify(&key, sig, sigSz, msg, sizeof(msg)), 0); + wc_LmsKey_Free(&key); + } + wc_FreeRng(&rng); #endif /* WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */ return EXPECT_RESULT(); diff --git a/tests/api/test_lms_xmss.h b/tests/api/test_lms_xmss.h index faee1c3553..4c774c4c86 100644 --- a/tests/api/test_lms_xmss.h +++ b/tests/api/test_lms_xmss.h @@ -30,6 +30,7 @@ int test_wc_LmsKey_reload_cache(void); int test_wc_LmsKey_reload_devid(void); int test_wc_LmsKey_reload_no_pub(void); int test_wc_LmsKey_reload_devid_verify(void); +int test_wc_LmsKey_keygen_no_sigsleft(void); int test_wc_XmssKey_reload_devid(void); int test_wc_XmssKey_reload_no_pub(void); int test_wc_XmssKey_reload_devid_verify(void); @@ -50,6 +51,7 @@ int test_wc_XmssFeatureCoverage(void); TEST_DECL_GROUP("lms", test_wc_LmsKey_reload_devid), \ TEST_DECL_GROUP("lms", test_wc_LmsKey_reload_no_pub), \ TEST_DECL_GROUP("lms", test_wc_LmsKey_reload_devid_verify), \ + TEST_DECL_GROUP("lms", test_wc_LmsKey_keygen_no_sigsleft), \ TEST_DECL_GROUP("xmss", test_wc_XmssKey_reload_devid), \ TEST_DECL_GROUP("xmss", test_wc_XmssKey_reload_no_pub), \ TEST_DECL_GROUP("xmss", test_wc_XmssKey_reload_devid_verify), \ diff --git a/tests/swdev/README.md b/tests/swdev/README.md index 8b0be645ac..a217929c0a 100644 --- a/tests/swdev/README.md +++ b/tests/swdev/README.md @@ -20,6 +20,12 @@ The switches it supports are: | `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 | +| `WOLF_CRYPTO_CB_ONLY_LMS` | software LMS/HSS | LMS via CryptoCb | +| `WOLF_CRYPTO_CB_ONLY_XMSS` | software XMSS/XMSS^MT | XMSS via CryptoCb | + +LMS and XMSS keys are stateful, so swdev keeps each generated key on the +device and signs with it there. The key is released when the caller frees +it (with `WOLF_CRYPTO_CB_FREE`) or at `wc_SwDev_Cleanup()`. 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 0a4eb160c8..e2613a9392 100644 --- a/tests/swdev/swdev.c +++ b/tests/swdev/swdev.c @@ -72,8 +72,15 @@ static int swdev_ensure_init(void) return 0; } +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) +static void swdev_stateful_cleanup(void); +#endif + WC_SWDEV_EXPORT void wc_SwDev_InternalCleanup(void) { +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) + swdev_stateful_cleanup(); +#endif if (swdev_initialized) { wolfCrypt_Cleanup(); swdev_initialized = 0; @@ -793,6 +800,548 @@ static int swdev_pqc_kem(wc_CryptoInfo* info, int type, int pkType) } #endif /* WOLFSSL_HAVE_MLKEM */ +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) + +#if (defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)) || \ + (defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)) + #define SWDEV_STATEFUL_SIGN +#endif + +/* Signing key to model a device-resident key */ +typedef struct swdev_stateful_key { + struct swdev_stateful_key* next; + int type; + byte* priv; + word32 privSz; +#ifdef WOLFSSL_HAVE_LMS + LmsKey lms; +#endif +#ifdef WOLFSSL_HAVE_XMSS + XmssKey xmss; +#endif +} swdev_stateful_key; + +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) +static int swdev_lms_write(const byte* priv, word32 privSz, void* context) +{ + swdev_stateful_key* dev = (swdev_stateful_key*)context; + + if ((dev == NULL) || (priv == NULL)) + return WC_LMS_RC_BAD_ARG; + if (privSz > dev->privSz) + return WC_LMS_RC_WRITE_FAIL; + + XMEMCPY(dev->priv, priv, privSz); + return WC_LMS_RC_SAVED_TO_NV_MEMORY; +} + +static int swdev_lms_read(byte* priv, word32 privSz, void* context) +{ + swdev_stateful_key* dev = (swdev_stateful_key*)context; + + if ((dev == NULL) || (priv == NULL)) + return WC_LMS_RC_BAD_ARG; + if (privSz > dev->privSz) + return WC_LMS_RC_READ_FAIL; + + XMEMCPY(priv, dev->priv, privSz); + return WC_LMS_RC_READ_TO_MEMORY; +} +#endif + +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) +static enum wc_XmssRc swdev_xmss_write(const byte* priv, word32 privSz, + void* context) +{ + swdev_stateful_key* dev = (swdev_stateful_key*)context; + + if ((dev == NULL) || (priv == NULL) || (privSz > dev->privSz)) + return WC_XMSS_RC_WRITE_FAIL; + + XMEMCPY(dev->priv, priv, privSz); + return WC_XMSS_RC_SAVED_TO_NV_MEMORY; +} + +static enum wc_XmssRc swdev_xmss_read(byte* priv, word32 privSz, void* context) +{ + swdev_stateful_key* dev = (swdev_stateful_key*)context; + + if ((dev == NULL) || (priv == NULL) || (privSz > dev->privSz)) + return WC_XMSS_RC_READ_FAIL; + + XMEMCPY(priv, dev->priv, privSz); + return WC_XMSS_RC_READ_TO_MEMORY; +} +#endif + +/* Track freed keys and sweep at cleanup to avoid sanitizer issues. */ +static swdev_stateful_key* swdev_stateful_list = NULL; + +static void swdev_stateful_untrack(swdev_stateful_key* dev) +{ + swdev_stateful_key** p = &swdev_stateful_list; + + while (*p != NULL) { + if (*p == dev) { + *p = dev->next; + return; + } + p = &(*p)->next; + } +} + +static void swdev_stateful_free(swdev_stateful_key* dev); + +static void swdev_stateful_cleanup(void) +{ + while (swdev_stateful_list != NULL) { + swdev_stateful_free(swdev_stateful_list); + } +} + +static void swdev_stateful_free(swdev_stateful_key* dev) +{ + if (dev == NULL) + return; + + swdev_stateful_untrack(dev); + + switch (dev->type) { +#ifdef WOLFSSL_HAVE_LMS + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + wc_LmsKey_Free(&dev->lms); + break; +#endif +#ifdef WOLFSSL_HAVE_XMSS + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + wc_XmssKey_Free(&dev->xmss); + break; +#endif + default: + break; + } + + if (dev->priv != NULL) { + wc_ForceZero(dev->priv, dev->privSz); + XFREE(dev->priv, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + wc_ForceZero(dev, sizeof(*dev)); + XFREE(dev, NULL, DYNAMIC_TYPE_TMP_BUFFER); +} + +#if defined(SWDEV_STATEFUL_SIGN) || defined(WOLF_CRYPTO_CB_FREE) +static swdev_stateful_key* swdev_stateful_get(int type, void* key) +{ + switch (type) { +#ifdef WOLFSSL_HAVE_LMS + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + return (swdev_stateful_key*)((LmsKey*)key)->devCtx; +#endif +#ifdef WOLFSSL_HAVE_XMSS + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + return (swdev_stateful_key*)((XmssKey*)key)->devCtx; +#endif + default: + return NULL; + } +} +#endif /* SWDEV_STATEFUL_SIGN || WOLF_CRYPTO_CB_FREE */ + +/* Clear the key's devId so the public API takes its software path instead of + * dispatching straight back here. */ +static int swdev_stateful_take(int type, void* key, int* devId) +{ + int ret = 0; + + *devId = INVALID_DEVID; + if (key == NULL) + return BAD_FUNC_ARG; + + switch (type) { +#ifdef WOLFSSL_HAVE_LMS + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + *devId = ((LmsKey*)key)->devId; + ((LmsKey*)key)->devId = INVALID_DEVID; + break; +#endif +#ifdef WOLFSSL_HAVE_XMSS + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + *devId = ((XmssKey*)key)->devId; + ((XmssKey*)key)->devId = INVALID_DEVID; + break; +#endif + default: + ret = CRYPTOCB_UNAVAILABLE; + break; + } + + return ret; +} + +static void swdev_stateful_give_back(int type, void* key, int devId) +{ + if (key == NULL) + return; + + switch (type) { +#ifdef WOLFSSL_HAVE_LMS + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + ((LmsKey*)key)->devId = devId; + break; +#endif +#ifdef WOLFSSL_HAVE_XMSS + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + ((XmssKey*)key)->devId = devId; + break; +#endif + default: + break; + } +} + +#ifdef SWDEV_STATEFUL_SIGN +/* Generate a key that stays on the device. Only the public half is copied + * back, so the caller can perform export and verify. */ +static int swdev_stateful_keygen(wc_CryptoInfo* info) +{ + int type = info->pk.pqc_stateful_sig_kg.type; + void* key = info->pk.pqc_stateful_sig_kg.key; + swdev_stateful_key* dev; + word32 privSz = 0; + int ret; + + if (key == NULL) + return BAD_FUNC_ARG; + + dev = (swdev_stateful_key*)XMALLOC(sizeof(*dev), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (dev == NULL) + return MEMORY_E; + XMEMSET(dev, 0, sizeof(*dev)); + dev->type = type; + + switch (type) { +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_LMS: { + const LmsKey* src = (const LmsKey*)key; + int levels = 0, height = 0, winternitz = 0, hash = 0; + + ret = wc_LmsKey_Init(&dev->lms, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_LmsKey_GetParameters_ex(src, &levels, &height, + &winternitz, &hash); + } + if (ret == 0) { + ret = wc_LmsKey_SetParameters_ex(&dev->lms, levels, height, + winternitz, hash); + } + if (ret == 0) { + ret = wc_LmsKey_GetPrivLen(&dev->lms, &privSz); + } + #ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE + /* The write callback receives the traversal state as well. */ + if (ret == 0) { + privSz += LMS_PRIV_DATA_LEN(dev->lms.params->levels, + dev->lms.params->height, dev->lms.params->p, + dev->lms.params->rootLevels, dev->lms.params->cacheBits, + dev->lms.params->hash_len); + } + #endif + break; + } +#endif +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: { + const XmssKey* src = (const XmssKey*)key; + + ret = wc_XmssKey_Init(&dev->xmss, NULL, INVALID_DEVID); + if (ret == 0) { + /* Mirror the caller's parameter set. */ + dev->xmss.oid = src->oid; + dev->xmss.params = src->params; + dev->xmss.sk_len = src->sk_len; + dev->xmss.is_xmssmt = src->is_xmssmt; + dev->xmss.state = WC_XMSS_STATE_PARMSET; + ret = wc_XmssKey_GetPrivLen(&dev->xmss, &privSz); + } + break; + } +#endif + default: + ret = CRYPTOCB_UNAVAILABLE; + break; + } + + /* Stands in for the device's nonvolatile storage. */ + if (ret == 0) { + dev->privSz = privSz; + dev->priv = (byte*)XMALLOC(privSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (dev->priv == NULL) { + ret = MEMORY_E; + } + else { + XMEMSET(dev->priv, 0, privSz); + } + } + + switch ((ret == 0) ? type : WC_PQC_STATEFUL_SIG_TYPE_NONE) { +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + { + const LmsKey* src = (const LmsKey*)key; + /* Persist where the caller asked, or in the device otherwise. */ + if (src->write_private_key != NULL) { + ret = wc_LmsKey_SetWriteCb(&dev->lms, src->write_private_key); + if ((ret == 0) && (src->read_private_key != NULL)) { + ret = wc_LmsKey_SetReadCb(&dev->lms, + src->read_private_key); + } + if (ret == 0) { + ret = wc_LmsKey_SetContext(&dev->lms, src->context); + } + } + else { + ret = wc_LmsKey_SetWriteCb(&dev->lms, swdev_lms_write); + if (ret == 0) { + ret = wc_LmsKey_SetReadCb(&dev->lms, swdev_lms_read); + } + if (ret == 0) { + ret = wc_LmsKey_SetContext(&dev->lms, dev); + } + } + } + if (ret == 0) { + ret = wc_LmsKey_MakeKey(&dev->lms, + info->pk.pqc_stateful_sig_kg.rng); + } + if (ret == 0) { + LmsKey* dst = (LmsKey*)key; + XMEMCPY(dst->pub, dev->lms.pub, sizeof(dst->pub)); + dst->devCtx = dev; + } + break; +#endif +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + { + const XmssKey* src = (const XmssKey*)key; + /* Persist where the caller asked, or in the device otherwise. */ + if (src->write_private_key != NULL) { + ret = wc_XmssKey_SetWriteCb(&dev->xmss, + src->write_private_key); + if ((ret == 0) && (src->read_private_key != NULL)) { + ret = wc_XmssKey_SetReadCb(&dev->xmss, + src->read_private_key); + } + if (ret == 0) { + ret = wc_XmssKey_SetContext(&dev->xmss, src->context); + } + } + else { + ret = wc_XmssKey_SetWriteCb(&dev->xmss, swdev_xmss_write); + if (ret == 0) { + ret = wc_XmssKey_SetReadCb(&dev->xmss, swdev_xmss_read); + } + if (ret == 0) { + ret = wc_XmssKey_SetContext(&dev->xmss, dev); + } + } + } + if (ret == 0) { + ret = wc_XmssKey_MakeKey(&dev->xmss, + info->pk.pqc_stateful_sig_kg.rng); + } + if (ret == 0) { + XmssKey* dst = (XmssKey*)key; + XMEMCPY(dst->pk, dev->xmss.pk, sizeof(dst->pk)); + dst->devCtx = dev; + } + break; +#endif + default: + break; + } + + if (ret != 0) { + swdev_stateful_free(dev); + } + else { + dev->next = swdev_stateful_list; + swdev_stateful_list = dev; + } + + return ret; +} + +static int swdev_stateful_sign(wc_CryptoInfo* info) +{ + int type = info->pk.pqc_stateful_sig_sign.type; + swdev_stateful_key* dev; + int ret; + + dev = swdev_stateful_get(type, info->pk.pqc_stateful_sig_sign.key); + if (dev == NULL) + return CRYPTOCB_UNAVAILABLE; + + switch (type) { +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + ret = wc_LmsKey_Sign(&dev->lms, info->pk.pqc_stateful_sig_sign.out, + info->pk.pqc_stateful_sig_sign.outSz, + info->pk.pqc_stateful_sig_sign.msg, + (int)info->pk.pqc_stateful_sig_sign.msgSz); + break; +#endif +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + ret = wc_XmssKey_Sign(&dev->xmss, info->pk.pqc_stateful_sig_sign.out, + info->pk.pqc_stateful_sig_sign.outSz, + info->pk.pqc_stateful_sig_sign.msg, + (int)info->pk.pqc_stateful_sig_sign.msgSz); + break; +#endif + default: + ret = CRYPTOCB_UNAVAILABLE; + break; + } + + return ret; +} + +static int swdev_stateful_sigsleft(wc_CryptoInfo* info) +{ + int type = info->pk.pqc_stateful_sig_sigs_left.type; + swdev_stateful_key* dev; + int ret = 0; + + if (info->pk.pqc_stateful_sig_sigs_left.sigsLeft == NULL) + return BAD_FUNC_ARG; + + dev = swdev_stateful_get(type, info->pk.pqc_stateful_sig_sigs_left.key); + if (dev == NULL) + return CRYPTOCB_UNAVAILABLE; + + switch (type) { +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + *info->pk.pqc_stateful_sig_sigs_left.sigsLeft = + (word32)wc_LmsKey_SigsLeft(&dev->lms); + break; +#endif +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + *info->pk.pqc_stateful_sig_sigs_left.sigsLeft = + (word32)wc_XmssKey_SigsLeft(&dev->xmss); + break; +#endif + default: + ret = CRYPTOCB_UNAVAILABLE; + break; + } + + return ret; +} +#endif /* SWDEV_STATEFUL_SIGN */ + +static int swdev_stateful_verify(wc_CryptoInfo* info) +{ + int type = info->pk.pqc_stateful_sig_verify.type; + void* key = info->pk.pqc_stateful_sig_verify.key; + int* res = info->pk.pqc_stateful_sig_verify.res; + int devId; + int ret; + + ret = swdev_stateful_take(type, key, &devId); + if (ret == 0) { + switch (type) { + #ifdef WOLFSSL_HAVE_LMS + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + ret = wc_LmsKey_Verify((LmsKey*)key, + info->pk.pqc_stateful_sig_verify.sig, + info->pk.pqc_stateful_sig_verify.sigSz, + info->pk.pqc_stateful_sig_verify.msg, + (int)info->pk.pqc_stateful_sig_verify.msgSz); + break; + #endif + #ifdef WOLFSSL_HAVE_XMSS + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + ret = wc_XmssKey_Verify((XmssKey*)key, + info->pk.pqc_stateful_sig_verify.sig, + info->pk.pqc_stateful_sig_verify.sigSz, + info->pk.pqc_stateful_sig_verify.msg, + (int)info->pk.pqc_stateful_sig_verify.msgSz); + break; + #endif + default: + ret = CRYPTOCB_UNAVAILABLE; + break; + } + swdev_stateful_give_back(type, key, devId); + } + + /* Only a bad signature is a verdict; every other failure propagates. */ + if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E)) { + if (res != NULL) { + *res = 0; + } + ret = 0; + } + else if ((ret == 0) && (res != NULL)) { + *res = 1; + } + + return ret; +} + +#if defined(WOLF_CRYPTO_CB_FREE) +/* Release the device-resident key the caller is done with. */ +static int swdev_stateful_free_cb(wc_CryptoInfo* info) +{ + swdev_stateful_key* dev = swdev_stateful_get(info->free.subType, + info->free.obj); + + if (dev != NULL) { + swdev_stateful_free(dev); + switch (info->free.subType) { + #ifdef WOLFSSL_HAVE_LMS + case WC_PQC_STATEFUL_SIG_TYPE_LMS: + ((LmsKey*)info->free.obj)->devCtx = NULL; + break; + #endif + #ifdef WOLFSSL_HAVE_XMSS + case WC_PQC_STATEFUL_SIG_TYPE_XMSS: + ((XmssKey*)info->free.obj)->devCtx = NULL; + break; + #endif + default: + break; + } + } + + return 0; +} +#endif /* WOLF_CRYPTO_CB_FREE */ + +static int swdev_pqc_stateful_sig(wc_CryptoInfo* info, int pkType) +{ + switch (pkType) { +#ifdef SWDEV_STATEFUL_SIGN + case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN: + return swdev_stateful_keygen(info); + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN: + return swdev_stateful_sign(info); + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT: + return swdev_stateful_sigsleft(info); +#endif + case WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY: + return swdev_stateful_verify(info); + default: + return CRYPTOCB_UNAVAILABLE; + } +} + +#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */ + #if defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256) /* Copy sponge state between the caller's wc_Shake and swdev's shadow */ static void swdev_shake_copy_state(wc_Shake* dst, const wc_Shake* src) @@ -1557,7 +2106,17 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info, #if !defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) || \ defined(HAVE_ED448) || defined(WOLFSSL_HAVE_SLHDSA) || \ - defined(WOLFSSL_HAVE_MLKEM) + defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_HAVE_LMS) || \ + defined(WOLFSSL_HAVE_XMSS) +#if defined(WOLF_CRYPTO_CB_FREE) && \ + (defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)) + case WC_ALGO_TYPE_FREE: + if ((info->free.algo == WC_ALGO_TYPE_PK) && + (info->free.type == WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN)) { + return swdev_stateful_free_cb(info); + } + return CRYPTOCB_UNAVAILABLE; +#endif case WC_ALGO_TYPE_PK: switch (info->pk.type) { #ifndef NO_RSA @@ -1674,6 +2233,13 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info, return swdev_pqc_kem(info, info->pk.pqc_decaps.type, info->pk.type); #endif /* WOLFSSL_HAVE_MLKEM */ + #if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) + case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN: + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN: + case WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY: + case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT: + return swdev_pqc_stateful_sig(info, info->pk.type); + #endif default: return CRYPTOCB_UNAVAILABLE; } diff --git a/tests/swdev/user_settings.h b/tests/swdev/user_settings.h index 2b08ca19ab..3f1be9f127 100644 --- a/tests/swdev/user_settings.h +++ b/tests/swdev/user_settings.h @@ -32,6 +32,8 @@ #undef WOLF_CRYPTO_CB_ONLY_CURVE448 #undef WOLF_CRYPTO_CB_ONLY_SLHDSA #undef WOLF_CRYPTO_CB_ONLY_MLKEM +#undef WOLF_CRYPTO_CB_ONLY_LMS +#undef WOLF_CRYPTO_CB_ONLY_XMSS /* swdev is the software provider, so it needs the software implementations * that these leave out of the library. */ #undef WOLFSSL_NO_RSA_SW diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index 820adb07bf..ebd5e9b46d 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -68,6 +68,8 @@ Crypto Callback Build Options: * 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_ONLY_MLDSA: Use only callbacks for ML-DSA default: off + * WOLF_CRYPTO_CB_ONLY_LMS: Use only callbacks for LMS/HSS default: off + * WOLF_CRYPTO_CB_ONLY_XMSS: Use only callbacks for XMSS 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 @@ -2011,9 +2013,12 @@ int wc_CryptoCb_PqcStatefulSigKeyGen(int type, void* key, WC_RNG* rng) if (key == NULL) return ret; + /* get devId; an unbound key still goes through the find callback */ devId = wc_CryptoCb_PqcStatefulSigGetDevId(type, key); +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); if (dev && dev->cb) { @@ -2041,9 +2046,12 @@ int wc_CryptoCb_PqcStatefulSigSign(const byte* msg, word32 msgSz, byte* out, if (key == NULL) return ret; + /* get devId; an unbound key still goes through the find callback */ devId = wc_CryptoCb_PqcStatefulSigGetDevId(type, key); +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); if (dev && dev->cb) { @@ -2074,9 +2082,12 @@ int wc_CryptoCb_PqcStatefulSigVerify(const byte* sig, word32 sigSz, if (key == NULL) return ret; + /* get devId; an unbound key still goes through the find callback */ devId = wc_CryptoCb_PqcStatefulSigGetDevId(type, key); +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); if (dev && dev->cb) { @@ -2107,9 +2118,12 @@ int wc_CryptoCb_PqcStatefulSigSigsLeft(int type, void* key, word32* sigsLeft) if (key == NULL) return ret; + /* get devId; an unbound key still goes through the find callback */ devId = wc_CryptoCb_PqcStatefulSigGetDevId(type, key); +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); if (dev && dev->cb) { diff --git a/wolfcrypt/src/wc_lms.c b/wolfcrypt/src/wc_lms.c index 7e1e2a427a..c15b8f2005 100644 --- a/wolfcrypt/src/wc_lms.c +++ b/wolfcrypt/src/wc_lms.c @@ -120,6 +120,7 @@ LMS_PARAMS_CACHE(h) } +#ifndef WOLF_CRYPTO_CB_ONLY_LMS /* Initialize the working state for LMS operations. * * @param [in, out] state LMS state. @@ -176,6 +177,7 @@ static void wc_lmskey_state_free(LmsState* state) wc_Sha256Free(LMS_STATE_HASH_K(state)); wc_Sha256Free(LMS_STATE_HASH(state)); } +#endif /* !WOLF_CRYPTO_CB_ONLY_LMS */ /* Supported LMS parameters. */ static const wc_LmsParamsMap wc_lms_map[] = { @@ -654,8 +656,10 @@ int wc_LmsKey_Init(LmsKey* key, void* heap, int devId) /* Start in initialized state. */ key->state = WC_LMS_STATE_INITED; + #ifndef WOLF_CRYPTO_CB_ONLY_LMS /* Ensure the CPU features are known. */ wc_lms_init(); + #endif } return ret; @@ -1012,6 +1016,17 @@ int wc_LmsKey_GetParameters_ex(const LmsKey* key, int* levels, int* height, void wc_LmsKey_Free(LmsKey* key) { if (key != NULL) { +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + (void)wc_CryptoCb_Free(key->devId, WC_ALGO_TYPE_PK, + WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN, + WC_PQC_STATEFUL_SIG_TYPE_LMS, (void*)key); + /* always continue to software cleanup */ + } +#endif #ifndef WOLFSSL_LMS_VERIFY_ONLY if (key->priv_data != NULL) { const LmsParams* params = key->params; @@ -1171,7 +1186,9 @@ int wc_LmsKey_SetContext(LmsKey* key, void* context) int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) { int ret = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_LMS word32 priv_data_len = 0; +#endif /* Validate parameters. */ if ((key == NULL) || (rng == NULL)) { @@ -1187,13 +1204,21 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) /* HSM-backed keys skip the software write/context callbacks because the * device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the * software checks below still run. */ - if ((ret == 0) && (key->devId != INVALID_DEVID)) { + #ifndef WOLF_CRYPTO_CB_FIND + if ((ret == 0) && (key->devId != INVALID_DEVID)) + #else + if (ret == 0) + #endif + { ret = wc_CryptoCb_PqcStatefulSigKeyGen(WC_PQC_STATEFUL_SIG_TYPE_LMS, key, rng); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + word32 sigsLeft = 1; /* This should not happen, but check whether signatures can be - * created. */ - if ((ret == 0) && (wc_LmsKey_SigsLeft(key) == 0)) { + * created. Only a device that reports a count can fail this. */ + if ((ret == 0) && (wc_CryptoCb_PqcStatefulSigSigsLeft( + WC_PQC_STATEFUL_SIG_TYPE_LMS, key, &sigsLeft) == 0) && + (sigsLeft == 0)) { WOLFSSL_MSG("error: generated LMS key signatures exhausted"); key->state = WC_LMS_STATE_NOSIGS; ret = BAD_STATE_E; @@ -1210,6 +1235,11 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_LMS + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else /* Check write callback set. */ if ((ret == 0) && (key->write_private_key == NULL)) { WOLFSSL_MSG("error: LmsKey write callback is not set"); @@ -1287,6 +1317,7 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) key->state = WC_LMS_STATE_OK; key->pubSet = 1; } +#endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; } @@ -1311,7 +1342,9 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) int wc_LmsKey_Reload(LmsKey* key) { int ret = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_LMS word32 priv_data_len = 0; +#endif /* Validate parameter. */ if (key == NULL) { @@ -1336,6 +1369,12 @@ int wc_LmsKey_Reload(LmsKey* key) } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_LMS + /* The device owns the state, so there is nothing to reload. */ + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else /* Check read callback present. */ if ((ret == 0) && (key->read_private_key == NULL)) { WOLFSSL_MSG("error: LmsKey read callback is not set"); @@ -1420,6 +1459,7 @@ int wc_LmsKey_Reload(LmsKey* key) /* Update state. */ key->state = WC_LMS_STATE_OK; } +#endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; } @@ -1513,7 +1553,12 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, /* HSM-backed keys skip the software write/context callbacks because the * device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the * software checks below still run. */ - if ((ret == 0) && (key->devId != INVALID_DEVID)) { + #ifndef WOLF_CRYPTO_CB_FIND + if ((ret == 0) && (key->devId != INVALID_DEVID)) + #else + if (ret == 0) + #endif + { ret = wc_CryptoCb_PqcStatefulSigSign(msg, (word32)msgSz, sig, sigSz, WC_PQC_STATEFUL_SIG_TYPE_LMS, key); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { @@ -1533,6 +1578,11 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_LMS + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else /* Check read and write callbacks available. */ if ((ret == 0) && (key->write_private_key == NULL)) { WOLFSSL_MSG("error: LmsKey write/read callbacks are not set"); @@ -1602,6 +1652,7 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, * to sign with again. */ key->state = WC_LMS_STATE_OK; } +#endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; } @@ -1620,7 +1671,10 @@ int wc_LmsKey_SigsLeft(LmsKey* key) /* NULL keys have no signatures remaining. */ if (key != NULL) { #ifdef WOLF_CRYPTO_CB - if (key->devId != INVALID_DEVID) { + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { word32 sigsLeft = 0; int cbRet = wc_CryptoCb_PqcStatefulSigSigsLeft( WC_PQC_STATEFUL_SIG_TYPE_LMS, key, &sigsLeft); @@ -1639,7 +1693,9 @@ int wc_LmsKey_SigsLeft(LmsKey* key) WOLFSSL_MSG("LMS SigsLeft not supported by device, using software"); } #endif + #ifndef WOLF_CRYPTO_CB_ONLY_LMS ret = wc_hss_sigsleft(key->params, key->priv_raw); + #endif } return ret; @@ -1998,7 +2054,12 @@ int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz, } #ifdef WOLF_CRYPTO_CB - if ((ret == 0) && (key->devId != INVALID_DEVID)) { + #ifndef WOLF_CRYPTO_CB_FIND + if ((ret == 0) && (key->devId != INVALID_DEVID)) + #else + if (ret == 0) + #endif + { int res = 0; ret = wc_CryptoCb_PqcStatefulSigVerify(sig, sigSz, msg, (word32)msgSz, &res, WC_PQC_STATEFUL_SIG_TYPE_LMS, key); @@ -2011,8 +2072,11 @@ int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz, } #endif - /* Only the software verifier needs the public key locally; a device - * holds its own copy. */ +#ifdef WOLF_CRYPTO_CB_ONLY_LMS + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if ((ret == 0) && (!key->pubSet)) { WOLFSSL_MSG("error: LMS key holds no public key"); ret = BAD_STATE_E; @@ -2038,6 +2102,7 @@ int wc_LmsKey_Verify(LmsKey* key, const byte* sig, word32 sigSz, WC_FREE_VAR_EX(state, NULL, DYNAMIC_TYPE_TMP_BUFFER); } } +#endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; } @@ -2066,6 +2131,13 @@ int wc_LmsKey_GetKid(LmsKey* key, const byte** kid, word32* kidSz) ret = BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB_ONLY_LMS + /* The device owns the private key, which contains the key ID. */ + if (ret == 0) { + ret = NOT_COMPILED_IN; + } + (void)offset; +#else #ifdef WOLF_CRYPTO_CB /* priv_raw is not populated for HSM-backed keys where the device owns * the private state. Extend the CryptoCb surface if device-side KID @@ -2082,6 +2154,7 @@ int wc_LmsKey_GetKid(LmsKey* key, const byte** kid, word32* kidSz) *kid = key->priv_raw + offset; *kidSz = HSS_PRIVATE_KEY_LEN(key->params->hash_len) - offset; } +#endif /* WOLF_CRYPTO_CB_ONLY_LMS */ return ret; } diff --git a/wolfcrypt/src/wc_lms_impl.c b/wolfcrypt/src/wc_lms_impl.c index 691aed679a..a99bf86548 100644 --- a/wolfcrypt/src/wc_lms_impl.c +++ b/wolfcrypt/src/wc_lms_impl.c @@ -44,6 +44,9 @@ #ifdef WOLFSSL_HAVE_LMS +/* The whole native core goes when operations are callback-only. */ +#ifndef WOLF_CRYPTO_CB_ONLY_LMS + #if FIPS_VERSION3_GE(7,0,0) #if defined(USE_WINDOWS_API) && defined(_MSC_VER) #pragma code_seg(".fipsA$nf") @@ -5611,5 +5614,7 @@ int wc_hss_verify(LmsState* state, const byte* pub, const byte* msg, return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_LMS */ + #endif /* WOLFSSL_HAVE_LMS */ diff --git a/wolfcrypt/src/wc_xmss.c b/wolfcrypt/src/wc_xmss.c index a99b33f23c..6bdd0999e4 100644 --- a/wolfcrypt/src/wc_xmss.c +++ b/wolfcrypt/src/wc_xmss.c @@ -56,6 +56,7 @@ #include #endif +#ifndef WOLF_CRYPTO_CB_ONLY_XMSS /*************************** * DIGEST init and free. ***************************/ @@ -165,6 +166,7 @@ static WC_INLINE void wc_xmss_state_free(XmssState* state) { wc_xmss_digest_free(state); } +#endif /* !WOLF_CRYPTO_CB_ONLY_XMSS */ /*************************** @@ -685,6 +687,7 @@ static int wc_xmssmt_str_to_params(const char *s, word32* oid, * @return BAD_FUNC_ARG when private key already allocated. * @return MEMORY_E when allocating dynamic memory fails. */ +#ifndef WOLF_CRYPTO_CB_ONLY_XMSS static int wc_xmsskey_alloc_sk(XmssKey* key) { int ret = 0; @@ -823,6 +826,7 @@ static WC_INLINE int wc_xmsskey_signupdate(XmssKey* key, byte* sig, return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_XMSS */ #endif /* !WOLFSSL_XMSS_VERIFY_ONLY */ /*************************** @@ -862,8 +866,10 @@ int wc_XmssKey_Init(XmssKey* key, void* heap, int devId) #endif key->state = WC_XMSS_STATE_INITED; + #ifndef WOLF_CRYPTO_CB_ONLY_XMSS /* Ensure the CPU features are known. */ wc_xmss_init(); + #endif } return ret; @@ -1063,6 +1069,17 @@ void wc_XmssKey_Free(XmssKey* key) { /* Validate parameter. */ if (key != NULL) { +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + (void)wc_CryptoCb_Free(key->devId, WC_ALGO_TYPE_PK, + WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN, + WC_PQC_STATEFUL_SIG_TYPE_XMSS, (void*)key); + /* always continue to software cleanup */ + } +#endif #ifndef WOLFSSL_XMSS_VERIFY_ONLY if (key->sk != NULL) { /* Zeroize private key. */ @@ -1208,12 +1225,14 @@ int wc_XmssKey_SetContext(XmssKey* key, void* context) int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) { int ret = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_XMSS enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE; #ifdef WOLFSSL_SMALL_STACK unsigned char* seed = NULL; #else unsigned char seed[3 * WC_XMSS_MAX_N]; #endif +#endif /* !WOLF_CRYPTO_CB_ONLY_XMSS */ /* Validate parameters */ if ((key == NULL) || (rng == NULL)) { @@ -1228,7 +1247,12 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) /* HSM-backed keys skip the software write/context callbacks because the * device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the * software checks below still run. */ - if ((ret == 0) && (key->devId != INVALID_DEVID)) { + #ifndef WOLF_CRYPTO_CB_FIND + if ((ret == 0) && (key->devId != INVALID_DEVID)) + #else + if (ret == 0) + #endif + { ret = wc_CryptoCb_PqcStatefulSigKeyGen(WC_PQC_STATEFUL_SIG_TYPE_XMSS, key, rng); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { @@ -1244,6 +1268,11 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_XMSS + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else /* Ensure write callback available. */ if ((ret == 0) && (key->write_private_key == NULL)) { WOLFSSL_MSG("error: XmssKey write callback is not set"); @@ -1330,6 +1359,8 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) ForceZero(seed, sizeof(seed)); #endif WC_FREE_VAR_EX(seed, key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif /* WOLF_CRYPTO_CB_ONLY_XMSS */ + return ret; } @@ -1369,7 +1400,9 @@ int wc_XmssKey_MakeKey(XmssKey* key, WC_RNG* rng) int wc_XmssKey_Reload(XmssKey* key) { int ret = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_XMSS enum wc_XmssRc cb_rc = WC_XMSS_RC_NONE; +#endif /* Validate parameter. */ if (key == NULL) { @@ -1394,6 +1427,12 @@ int wc_XmssKey_Reload(XmssKey* key) } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_XMSS + /* The device owns the state, so there is nothing to reload. */ + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else /* Ensure read and write callbacks are available. */ if ((ret == 0) && ((key->write_private_key == NULL) || (key->read_private_key == NULL))) { @@ -1422,6 +1461,7 @@ int wc_XmssKey_Reload(XmssKey* key) if (ret == 0) { key->state = WC_XMSS_STATE_OK; } +#endif /* WOLF_CRYPTO_CB_ONLY_XMSS */ return ret; } @@ -1524,7 +1564,12 @@ int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg, /* HSM-backed keys skip the software write/context callbacks because the * device owns the private state. On CRYPTOCB_UNAVAILABLE fall-through the * software checks below still run. */ - if ((ret == 0) && (key->devId != INVALID_DEVID)) { + #ifndef WOLF_CRYPTO_CB_FIND + if ((ret == 0) && (key->devId != INVALID_DEVID)) + #else + if (ret == 0) + #endif + { ret = wc_CryptoCb_PqcStatefulSigSign(msg, (word32)msgLen, sig, sigLen, WC_PQC_STATEFUL_SIG_TYPE_XMSS, key); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) @@ -1533,6 +1578,11 @@ int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg, } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_XMSS + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else /* Check read and write callbacks available. */ if ((ret == 0) && ((key->write_private_key == NULL) || (key->read_private_key == NULL))) { @@ -1545,6 +1595,7 @@ int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg, /* Finally, sign and update the secret key. */ ret = wc_xmsskey_signupdate(key, sig, msg, msgLen); } +#endif /* WOLF_CRYPTO_CB_ONLY_XMSS */ return ret; } @@ -1564,7 +1615,10 @@ int wc_XmssKey_SigsLeft(XmssKey* key) return 0; #ifdef WOLF_CRYPTO_CB - if (key->devId != INVALID_DEVID) { +#ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) +#endif + { word32 sigsLeft = 0; int cbRet = wc_CryptoCb_PqcStatefulSigSigsLeft( WC_PQC_STATEFUL_SIG_TYPE_XMSS, key, &sigsLeft); @@ -1584,6 +1638,7 @@ int wc_XmssKey_SigsLeft(XmssKey* key) } #endif +#ifndef WOLF_CRYPTO_CB_ONLY_XMSS /* Validate state. */ if (key->state == WC_XMSS_STATE_NOSIGS) { WOLFSSL_MSG("error: XMSS signatures exhausted"); @@ -1603,6 +1658,7 @@ int wc_XmssKey_SigsLeft(XmssKey* key) /* Ask implementation to check index in private key. */ ret = wc_xmss_sigsleft(key->params, key->sk); } +#endif return ret; } @@ -2054,7 +2110,12 @@ int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen, } #ifdef WOLF_CRYPTO_CB - if ((ret == 0) && (key->devId != INVALID_DEVID)) { + #ifndef WOLF_CRYPTO_CB_FIND + if ((ret == 0) && (key->devId != INVALID_DEVID)) + #else + if (ret == 0) + #endif + { int res = 0; ret = wc_CryptoCb_PqcStatefulSigVerify(sig, sigLen, m, (word32)mLen, &res, WC_PQC_STATEFUL_SIG_TYPE_XMSS, key); @@ -2067,10 +2128,13 @@ int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen, } #endif - /* Only the software verifier needs the public key locally; a device - * holds its own copy. */ +#ifdef WOLF_CRYPTO_CB_ONLY_XMSS + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if ((ret == 0) && (!key->pubSet)) { - WOLFSSL_MSG("error: XMSS key holds no public key"); + WOLFSSL_MSG("error: XMSS key contains no public key"); ret = BAD_STATE_E; } @@ -2094,6 +2158,7 @@ int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigLen, WC_FREE_VAR_EX(state, key->heap, DYNAMIC_TYPE_TMP_BUFFER); } } +#endif /* WOLF_CRYPTO_CB_ONLY_XMSS */ return ret; } diff --git a/wolfcrypt/src/wc_xmss_impl.c b/wolfcrypt/src/wc_xmss_impl.c index 5534ade8b5..fe7510d469 100644 --- a/wolfcrypt/src/wc_xmss_impl.c +++ b/wolfcrypt/src/wc_xmss_impl.c @@ -36,6 +36,9 @@ #if defined(WOLFSSL_HAVE_XMSS) +/* The whole native core goes when operations are callback-only. */ +#ifndef WOLF_CRYPTO_CB_ONLY_XMSS + #if FIPS_VERSION3_GE(7,0,0) #if defined(USE_WINDOWS_API) && defined(_MSC_VER) #pragma code_seg(".fipsA$nh") @@ -6017,5 +6020,7 @@ int wc_xmssmt_verify(XmssState* state, const unsigned char* m, word32 mlen, return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_XMSS */ + #endif /* WOLFSSL_HAVE_XMSS */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 84eaa092ee..bf77709db0 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -71180,7 +71180,11 @@ static wc_test_ret_t xmss_test_param(const char* param, word32 exp_pkSz, /* The BDS traversal counters, stored node heights and tree hash entries are * all re-parsed from the persisted private key on every sign. Corrupt them * and the library must stay inside the key's own buffers, and must reject - * a counter that is plainly out of range. */ + * a counter that is plainly out of range. + * + * Needs the software reload of caller-held state, which a callback-only + * build removes: there is no reload operation on the cryptocb surface. */ +#ifndef WOLF_CRYPTO_CB_ONLY_XMSS if (sk_snapshot != NULL) { /* Enough samples to reach every field of the state without making the * test signing-bound. */ @@ -71233,6 +71237,7 @@ static wc_test_ret_t xmss_test_param(const char* param, word32 exp_pkSz, } ret = 0; } +#endif /* !WOLF_CRYPTO_CB_ONLY_XMSS */ #endif /* !WOLFSSL_NO_MALLOC */ out: @@ -71991,10 +71996,16 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void) if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_LmsKey_GetKid(&signingKey, &kid, &kidSz); +#ifdef WOLF_CRYPTO_CB_ONLY_LMS + /* The key ID stays on the device. */ + if (ret != WC_NO_ERR_TRACE(NOT_COMPILED_IN)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); +#else if (ret != 0) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } if (kidSz != WC_LMS_I_LEN) { ERROR_OUT(WC_TEST_RET_ENC_I(kidSz), out); } +#endif ret = wc_LmsKey_ExportPub_ex(&verifyKey, &signingKey, NULL, devId); if (ret != 0) { ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } @@ -72019,10 +72030,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } +/* swdev's find callback routes this key to the device, so skip the check. */ +#if !defined(WOLF_CRYPTO_CB_FIND) || !defined(WOLFSSL_SWDEV) #ifdef WOLF_CRYPTO_CB - /* The NULL-WriteCb -> BAD_FUNC_ARG check in wc_LmsKey_Sign sits after - * the cryptocb dispatch; an HSM-backed Sign succeeds without ever - * reaching it. Only exercise this on the pure software path. */ if (devId == INVALID_DEVID) #endif { @@ -72037,6 +72047,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t lms_test(void) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } } +#endif /* !WOLF_CRYPTO_CB_FIND || !WOLFSSL_SWDEV */ ret = 0; } @@ -91435,6 +91446,10 @@ typedef struct { #ifdef WOLF_CRYPTO_CB_FREE int hashFreeType; /* hash type seen by last hash free dispatch */ #endif +#if defined(WOLF_CRYPTO_CB_FREE) && \ + (defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)) + int statefulFreeType; /* stateful sig type seen by last free dispatch */ +#endif #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) int rsaPssVerifyCount; /* RSA-PSS verify callback invocations */ #endif @@ -95562,6 +95577,13 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } break; } +#endif +#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS) + case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN: + /* record the dispatch and let software do the free */ + myCtx->statefulFreeType = info->free.subType; + ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + break; #endif default: ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN); @@ -99127,12 +99149,28 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) } #endif /* HAVE_FALCON */ #if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) +#ifdef WOLF_CRYPTO_CB_FREE + myCtx.statefulFreeType = WC_PQC_STATEFUL_SIG_TYPE_NONE; +#endif if (ret == 0) ret = xmss_test(); +#ifdef WOLF_CRYPTO_CB_FREE + if ((ret == 0) && + (myCtx.statefulFreeType != WC_PQC_STATEFUL_SIG_TYPE_XMSS)) + ret = WC_TEST_RET_ENC_NC; +#endif #endif #if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) +#ifdef WOLF_CRYPTO_CB_FREE + myCtx.statefulFreeType = WC_PQC_STATEFUL_SIG_TYPE_NONE; +#endif if (ret == 0) ret = lms_test(); +#ifdef WOLF_CRYPTO_CB_FREE + if ((ret == 0) && + (myCtx.statefulFreeType != WC_PQC_STATEFUL_SIG_TYPE_LMS)) + ret = WC_TEST_RET_ENC_NC; +#endif #endif #if defined(HAVE_ED25519) && \ (!defined(WOLF_CRYPTO_CB_ONLY_ED25519) || defined(WOLFSSL_SWDEV)) diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 14f6ab11f4..bd92cabcdf 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -6171,6 +6171,24 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #if defined(WOLF_CRYPTO_CB_ONLY_SLHDSA) && !defined(WOLFSSL_HAVE_SLHDSA) #error "WOLF_CRYPTO_CB_ONLY_SLHDSA requires WOLFSSL_HAVE_SLHDSA" #endif +#if defined(WOLF_CRYPTO_CB_ONLY_LMS) && !defined(WOLF_CRYPTO_CB) + #error "WOLF_CRYPTO_CB_ONLY_LMS requires WOLF_CRYPTO_CB" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_LMS) && !defined(WOLFSSL_HAVE_LMS) + #error "WOLF_CRYPTO_CB_ONLY_LMS requires WOLFSSL_HAVE_LMS" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_LMS) && defined(HAVE_FIPS) + #error "WOLF_CRYPTO_CB_ONLY_LMS is incompatible with FIPS builds" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_XMSS) && !defined(WOLF_CRYPTO_CB) + #error "WOLF_CRYPTO_CB_ONLY_XMSS requires WOLF_CRYPTO_CB" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_XMSS) && !defined(WOLFSSL_HAVE_XMSS) + #error "WOLF_CRYPTO_CB_ONLY_XMSS requires WOLFSSL_HAVE_XMSS" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_XMSS) && defined(HAVE_FIPS) + #error "WOLF_CRYPTO_CB_ONLY_XMSS is incompatible with FIPS builds" +#endif /* * WOLFSSL_NO_RSA_SW omits the wolfCrypt software RSA implementation: the diff --git a/wolfssl/wolfcrypt/wc_lms.h b/wolfssl/wolfcrypt/wc_lms.h index dbfcbbe213..8743d249a0 100644 --- a/wolfssl/wolfcrypt/wc_lms.h +++ b/wolfssl/wolfcrypt/wc_lms.h @@ -949,6 +949,7 @@ WOLFSSL_API const byte* wc_LmsKey_GetKidFromPrivRaw(const byte* priv, word32 privSz); #endif +#ifndef WOLF_CRYPTO_CB_ONLY_LMS /* Work out what the CPU can do; called from wc_LmsKey_Init(). Internal to * the library, not part of the API. */ WOLFSSL_LOCAL void wc_lms_init(void); @@ -963,6 +964,7 @@ int wc_hss_sigsleft(const LmsParams* params, const byte* priv_raw); WOLFSSL_API int wc_hss_verify(LmsState* state, const byte* pub, const byte* msg, word32 msgSz, const byte* sig, word32 sigSz); +#endif /* !WOLF_CRYPTO_CB_ONLY_LMS */ #ifdef __cplusplus } /* extern "C" */ diff --git a/wolfssl/wolfcrypt/wc_xmss.h b/wolfssl/wolfcrypt/wc_xmss.h index 7b5d8e14be..cf2bdd5baf 100644 --- a/wolfssl/wolfcrypt/wc_xmss.h +++ b/wolfssl/wolfcrypt/wc_xmss.h @@ -632,6 +632,7 @@ WOLFSSL_API int wc_XmssKey_ImportPubRaw_ex(XmssKey* key, const byte* in, WOLFSSL_API int wc_XmssKey_Verify(XmssKey* key, const byte* sig, word32 sigSz, const byte* msg, int msgSz); +#ifndef WOLF_CRYPTO_CB_ONLY_XMSS WOLFSSL_LOCAL int wc_xmssmt_keygen(XmssState *state, const unsigned char* seed, unsigned char *sk, unsigned char *pk); /* Work out what the CPU can do; called from wc_XmssKey_Init(). */ @@ -649,6 +650,7 @@ WOLFSSL_LOCAL int wc_xmss_sigsleft(const XmssParams* params, unsigned char* sk); WOLFSSL_LOCAL int wc_xmssmt_verify(XmssState *state, const unsigned char *m, word32 mlen, const unsigned char *sm, const unsigned char *pk); +#endif /* !WOLF_CRYPTO_CB_ONLY_XMSS */ #ifdef __cplusplus } /* extern "C" */