From 31ecbbbbf7c965fc5786e2dab19d9b6e8ce5a7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Wed, 21 Oct 2026 14:27:57 +0200 Subject: [PATCH] Report no signatures left for an LMS/XMSS key held by a device When the device declines a SigsLeft query, wc_LmsKey_SigsLeft and wc_XmssKey_SigsLeft fall back to software. That is right for a key whose state software holds, such as one made or reloaded through a devId that only routes other algorithms. But a key whose state lives on the device, named by id or reloaded without a read callback, has none in software. XMSS then called the unset read_private_key and crashed. LMS counted from the all-zero priv_raw and reported a signature left. Fall back to software only when it holds the private key: privSet for LMS, which only a software MakeKey or Reload sets, and sk plus the read callback for XMSS. Otherwise report no signatures left, as before d56831c90d. The doc comments list this as a reason for returning 0. wc_LmsKey_Sign and wc_XmssKey_Sign had the same fallback. When the device declined to sign with such a key, software signed with the unset priv_data or sk and crashed. A key generated on the device has no sk either, so XMSS crashed there even with read and write callbacks set. Return BAD_STATE_E when the LMS privSet or the XMSS sk is not set. wc_LmsKey_MakeKey and wc_LmsKey_Reload left priv_raw and priv_data in place when generating or reading the key, or a later step such as writing out a new key, failed, so a key that was never saved or loaded still counted from priv_raw. On any failure after the state and callback checks, wipe priv_data, priv_raw and the expanded key state and clear privSet, which is now set as soon as priv_raw holds the key. The priv_data buffer is kept rather than freed, so a caller that supplied its own, as WOLFSSL_NO_MALLOC builds must, can retry. A call refused by those checks leaves a loaded key alone. test_wc_LmsKey_id_sigsleft and test_wc_XmssKey_id_sigsleft query a key from InitId on a device that declines, and the LMS test also signs with it. test_wc_XmssKey_keygen_sign_declined signs with a key generated on a device that then declines. test_wc_LmsKey_reload_fail_sigsleft and test_wc_LmsKey_makekey_fail_sigsleft query a key whose Reload failed to read or whose MakeKey failed to write, and check that a priv_data buffer the caller supplied is still in place. The reload_devid tests now also check that a software-held key on a declining devId still reports signatures left. --- tests/api/test_lms_xmss.c | 238 ++++++++++++++++++++++++++++++++++++++ tests/api/test_lms_xmss.h | 10 ++ wolfcrypt/src/wc_lms.c | 73 ++++++++---- wolfcrypt/src/wc_xmss.c | 13 ++- 4 files changed, 308 insertions(+), 26 deletions(-) diff --git a/tests/api/test_lms_xmss.c b/tests/api/test_lms_xmss.c index 3e74534f5e..f12545992c 100644 --- a/tests/api/test_lms_xmss.c +++ b/tests/api/test_lms_xmss.c @@ -470,6 +470,7 @@ int test_wc_LmsKey_reload_devid(void) ExpectIntEQ(wc_LmsKey_Reload(&key), 0); /* The reload must have expanded the private key, not been skipped. */ ExpectNotNull(key.priv_data); + ExpectIntEQ(wc_LmsKey_SigsLeft(&key), 1); ExpectIntEQ(wc_LmsKey_GetKid(&key, &kid, &kidSz), 0); ExpectIntEQ(kidSz, WC_LMS_I_LEN); @@ -617,10 +618,72 @@ 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) && defined(WOLF_PRIVATE_KEY_ID) && \ + !defined(WOLF_CRYPTO_CB_ONLY_LMS) +static int test_lms_write_key_ok(const byte* priv, word32 privSz, + void* context) +{ + (void)priv; + (void)privSz; + (void)context; + return WC_LMS_RC_SAVED_TO_NV_MEMORY; +} +#endif + +/* + * Test that an LMS key named by id reports no signatures left, and is not + * signed with in software, when the device declines. + */ +int test_wc_LmsKey_id_sigsleft(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ + defined(WOLF_CRYPTO_CB) && defined(WOLF_PRIVATE_KEY_ID) + LmsKey key; + const byte id[] = { 0x01, 0x02, 0x03, 0x04 }; +#ifndef WOLF_CRYPTO_CB_ONLY_LMS + byte msg[] = "test message for LMS signing"; + byte* sig = NULL; + word32 sigSz = 0; +#endif + + XMEMSET(&key, 0, sizeof(key)); + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_LMS_XMSS_CRYPTOCB_DEVID, + test_lms_xmss_verify_cryptocb, NULL), 0); + ExpectIntEQ(wc_LmsKey_InitId(&key, id, (int)sizeof(id), NULL, + TEST_LMS_XMSS_CRYPTOCB_DEVID), 0); + ExpectIntEQ(test_lms_set_params(&key), 0); +#ifndef WOLF_CRYPTO_CB_ONLY_LMS + ExpectIntEQ(wc_LmsKey_SetWriteCb(&key, test_lms_write_key_ok), 0); +#endif + ExpectIntEQ(wc_LmsKey_Reload(&key), 0); + ExpectIntEQ(wc_LmsKey_SigsLeft(&key), 0); + +#ifndef WOLF_CRYPTO_CB_ONLY_LMS + ExpectIntEQ(wc_LmsKey_GetSigLen(&key, &sigSz), 0); + ExpectNotNull(sig = (byte*)XMALLOC(sigSz, NULL, DYNAMIC_TYPE_TMP_BUFFER)); + if (sig != NULL) { + ExpectIntEQ(wc_LmsKey_Sign(&key, sig, &sigSz, msg, sizeof(msg)), + WC_NO_ERR_TRACE(BAD_STATE_E)); + } + XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + wc_LmsKey_Free(&key); + wc_CryptoCb_UnRegisterDevice(TEST_LMS_XMSS_CRYPTOCB_DEVID); +#endif + return EXPECT_RESULT(); +} + /* Must be the exact union of the guards of the tests that register it. */ #if defined(WOLF_CRYPTO_CB) && \ ((defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)) || \ (defined(WOLF_CRYPTO_CB_ONLY_XMSS) && !defined(WOLFSSL_SWDEV) && \ + !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ + defined(TEST_XMSS_H10_AVAILABLE)) || \ + (defined(WOLFSSL_HAVE_XMSS) && !defined(WOLF_CRYPTO_CB_ONLY_XMSS) && \ !defined(WOLFSSL_XMSS_VERIFY_ONLY) && defined(TEST_XMSS_H10_AVAILABLE))) /* An accelerator that generates keys and declines everything else. */ static int test_lms_xmss_keygen_cryptocb(int devIdArg, wc_CryptoInfo* info, @@ -673,6 +736,86 @@ int test_wc_LmsKey_keygen_no_sigsleft(void) return EXPECT_RESULT(); } +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ + !defined(WOLF_CRYPTO_CB_ONLY_LMS) +static int test_lms_read_key_fail(byte* priv, word32 privSz, void* context) +{ + (void)priv; + (void)privSz; + (void)context; + return WC_LMS_RC_READ_FAIL; +} + +static int test_lms_write_key_fail(const byte* priv, word32 privSz, + void* context) +{ + (void)priv; + (void)privSz; + (void)context; + return WC_LMS_RC_WRITE_FAIL; +} + +/* Upper bound on priv_data for the parameters test_lms_set_params uses. */ +#define TEST_LMS_PRIV_DATA_LEN \ + (LMS_PRIV_DATA_LEN(1, 10, 8, LMS_ROOT_LEVELS, LMS_CACHE_BITS, \ + LMS_MAX_NODE_LEN) + HSS_MAX_PRIVATE_KEY_LEN) +#endif + +/* An LMS key whose MakeKey failed to save has no private key to count from. */ +int test_wc_LmsKey_makekey_fail_sigsleft(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ + !defined(WOLF_CRYPTO_CB_ONLY_LMS) + LmsKey key; + WC_RNG rng; + static byte privData[TEST_LMS_PRIV_DATA_LEN]; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0); + key.priv_data = privData; + ExpectIntEQ(test_lms_set_params(&key), 0); + ExpectIntEQ(wc_LmsKey_SetWriteCb(&key, test_lms_write_key_fail), 0); + ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), WC_NO_ERR_TRACE(IO_FAILED_E)); + ExpectIntEQ(wc_LmsKey_SigsLeft(&key), 0); + /* A caller-supplied buffer stays with the key for a retry. */ + ExpectPtrEq(key.priv_data, privData); + + key.priv_data = NULL; + wc_LmsKey_Free(&key); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + +/* An LMS key whose Reload failed has no private key to count from. */ +int test_wc_LmsKey_reload_fail_sigsleft(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY) && \ + !defined(WOLF_CRYPTO_CB_ONLY_LMS) + LmsKey key; + static byte privData[TEST_LMS_PRIV_DATA_LEN]; + + XMEMSET(&key, 0, sizeof(key)); + + ExpectIntEQ(wc_LmsKey_Init(&key, NULL, INVALID_DEVID), 0); + key.priv_data = privData; + ExpectIntEQ(test_lms_set_params(&key), 0); + ExpectIntEQ(wc_LmsKey_SetReadCb(&key, test_lms_read_key_fail), 0); + ExpectIntNE(wc_LmsKey_Reload(&key), 0); + ExpectIntEQ(wc_LmsKey_SigsLeft(&key), 0); + ExpectPtrEq(key.priv_data, privData); + + key.priv_data = NULL; + wc_LmsKey_Free(&key); +#endif + return EXPECT_RESULT(); +} + #if defined(WOLF_CRYPTO_CB_ONLY_LMS) && !defined(WOLFSSL_SWDEV) && \ !defined(WOLFSSL_LMS_VERIFY_ONLY) static int test_lms_read_fail(byte* priv, word32 privSz, void* context) @@ -859,6 +1002,7 @@ int test_wc_XmssKey_reload_devid(void) ExpectIntEQ(wc_XmssKey_Reload(&key), 0); /* The reload must have allocated the secret key, not been skipped. */ ExpectNotNull(key.sk); + ExpectIntEQ(wc_XmssKey_SigsLeft(&key), 1); /* Sign with the reloaded key and verify with a software-only key. */ sigSz = sizeof(sig); @@ -987,6 +1131,100 @@ int test_wc_XmssKey_reload_devid_verify(void) return EXPECT_RESULT(); } +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ + defined(WOLF_CRYPTO_CB) && defined(TEST_XMSS_H10_AVAILABLE) && \ + !defined(WOLF_CRYPTO_CB_ONLY_XMSS) +static enum wc_XmssRc test_xmss_write_key_ok(const byte* priv, word32 privSz, + void* context) +{ + (void)priv; + (void)privSz; + (void)context; + return WC_XMSS_RC_SAVED_TO_NV_MEMORY; +} + +static enum wc_XmssRc test_xmss_read_key_ok(byte* priv, word32 privSz, + void* context) +{ + (void)priv; + (void)privSz; + (void)context; + return WC_XMSS_RC_READ_TO_MEMORY; +} +#endif + +/* + * Test that an XMSS key generated on a device is not signed with in software + * when the device declines. + */ +int test_wc_XmssKey_keygen_sign_declined(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ + defined(WOLF_CRYPTO_CB) && defined(TEST_XMSS_H10_AVAILABLE) && \ + !defined(WOLF_CRYPTO_CB_ONLY_XMSS) + XmssKey key; + WC_RNG rng; + byte msg[] = "test message for XMSS signing"; + byte* sig = NULL; + word32 sigSz = 0; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_LMS_XMSS_CRYPTOCB_DEVID, + test_lms_xmss_keygen_cryptocb, NULL), 0); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_XmssKey_Init(&key, NULL, TEST_LMS_XMSS_CRYPTOCB_DEVID), 0); + ExpectIntEQ(wc_XmssKey_SetParamStr(&key, "XMSS-SHA2_10_256"), 0); + ExpectIntEQ(wc_XmssKey_SetWriteCb(&key, test_xmss_write_key_ok), 0); + ExpectIntEQ(wc_XmssKey_SetReadCb(&key, test_xmss_read_key_ok), 0); + ExpectIntEQ(wc_XmssKey_MakeKey(&key, &rng), 0); + ExpectNull(key.sk); + + ExpectIntEQ(wc_XmssKey_GetSigLen(&key, &sigSz), 0); + ExpectNotNull(sig = (byte*)XMALLOC(sigSz, NULL, DYNAMIC_TYPE_TMP_BUFFER)); + if (sig != NULL) { + ExpectIntEQ(wc_XmssKey_Sign(&key, sig, &sigSz, msg, sizeof(msg)), + WC_NO_ERR_TRACE(BAD_STATE_E)); + } + + XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wc_XmssKey_Free(&key); + wc_FreeRng(&rng); + wc_CryptoCb_UnRegisterDevice(TEST_LMS_XMSS_CRYPTOCB_DEVID); +#endif + return EXPECT_RESULT(); +} + +/* + * Same scenario as test_wc_LmsKey_id_sigsleft, for XMSS. + */ +int test_wc_XmssKey_id_sigsleft(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY) && \ + defined(WOLF_CRYPTO_CB) && defined(TEST_XMSS_H10_AVAILABLE) && \ + defined(WOLF_PRIVATE_KEY_ID) + XmssKey key; + const byte id[] = { 0x01, 0x02, 0x03, 0x04 }; + + XMEMSET(&key, 0, sizeof(key)); + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_LMS_XMSS_CRYPTOCB_DEVID, + test_lms_xmss_verify_cryptocb, NULL), 0); + ExpectIntEQ(wc_XmssKey_InitId(&key, id, (int)sizeof(id), NULL, + TEST_LMS_XMSS_CRYPTOCB_DEVID), 0); + ExpectIntEQ(wc_XmssKey_SetParamStr(&key, "XMSS-SHA2_10_256"), 0); + ExpectIntEQ(wc_XmssKey_Reload(&key), 0); + ExpectIntEQ(wc_XmssKey_SigsLeft(&key), 0); + + wc_XmssKey_Free(&key); + wc_CryptoCb_UnRegisterDevice(TEST_LMS_XMSS_CRYPTOCB_DEVID); +#endif + return EXPECT_RESULT(); +} + #if defined(WOLF_CRYPTO_CB_ONLY_XMSS) && !defined(WOLFSSL_SWDEV) && \ !defined(WOLFSSL_XMSS_VERIFY_ONLY) && defined(TEST_XMSS_H10_AVAILABLE) static enum wc_XmssRc test_xmss_read_fail(byte* priv, word32 privSz, diff --git a/tests/api/test_lms_xmss.h b/tests/api/test_lms_xmss.h index 119dc8fae7..2e40e51d4c 100644 --- a/tests/api/test_lms_xmss.h +++ b/tests/api/test_lms_xmss.h @@ -30,11 +30,16 @@ 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_reload_fail_sigsleft(void); +int test_wc_LmsKey_makekey_fail_sigsleft(void); int test_wc_LmsKey_keygen_no_sigsleft(void); +int test_wc_LmsKey_id_sigsleft(void); int test_wc_LmsKey_cb_only_no_device(void); int test_wc_XmssKey_reload_devid(void); int test_wc_XmssKey_reload_no_pub(void); int test_wc_XmssKey_reload_devid_verify(void); +int test_wc_XmssKey_keygen_sign_declined(void); +int test_wc_XmssKey_id_sigsleft(void); int test_wc_XmssKey_cb_only_no_device(void); int test_rfc9802_lms_x509_verify(void); int test_rfc9802_xmss_x509_verify(void); @@ -53,11 +58,16 @@ 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_reload_fail_sigsleft), \ + TEST_DECL_GROUP("lms", test_wc_LmsKey_makekey_fail_sigsleft), \ TEST_DECL_GROUP("lms", test_wc_LmsKey_keygen_no_sigsleft), \ + TEST_DECL_GROUP("lms", test_wc_LmsKey_id_sigsleft), \ TEST_DECL_GROUP("lms", test_wc_LmsKey_cb_only_no_device), \ 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), \ + TEST_DECL_GROUP("xmss", test_wc_XmssKey_keygen_sign_declined), \ + TEST_DECL_GROUP("xmss", test_wc_XmssKey_id_sigsleft), \ TEST_DECL_GROUP("xmss", test_wc_XmssKey_cb_only_no_device), \ TEST_DECL_GROUP("lms", test_rfc9802_lms_x509_verify), \ TEST_DECL_GROUP("xmss", test_rfc9802_xmss_x509_verify), \ diff --git a/wolfcrypt/src/wc_lms.c b/wolfcrypt/src/wc_lms.c index 678ed36ff8..1e335962d7 100644 --- a/wolfcrypt/src/wc_lms.c +++ b/wolfcrypt/src/wc_lms.c @@ -1007,6 +1007,30 @@ int wc_LmsKey_GetParameters_ex(const LmsKey* key, int* levels, int* height, return ret; } +#ifndef WOLFSSL_LMS_VERIFY_ONLY +/* Wipe the private key held in software, keeping the priv_data buffer. + * + * @param [in, out] key LMS key. + */ +static void wc_lmskey_wipe_priv(LmsKey* key) +{ + if (key->priv_data != NULL) { + const LmsParams* params = key->params; + word32 priv_data_len = LMS_PRIV_DATA_LEN(params->levels, + params->height, params->p, params->rootLevels, + params->cacheBits, params->hash_len); + + #ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE + priv_data_len += HSS_PRIVATE_KEY_LEN(params->hash_len); + #endif + ForceZero(key->priv_data, priv_data_len); + } + ForceZero(key->priv_raw, sizeof(key->priv_raw)); + ForceZero(&key->priv, sizeof(HssPrivKey)); + key->privSet = 0; +} +#endif + /* Frees the LMS key from memory. * * This does not affect the private key saved to non-volatile storage. @@ -1028,23 +1052,9 @@ void wc_LmsKey_Free(LmsKey* key) } #endif #ifndef WOLFSSL_LMS_VERIFY_ONLY - if (key->priv_data != NULL) { - const LmsParams* params = key->params; - word32 priv_data_len = LMS_PRIV_DATA_LEN(params->levels, - params->height, params->p, params->rootLevels, - params->cacheBits, params->hash_len); - - #ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE - priv_data_len += HSS_PRIVATE_KEY_LEN(key->params->hash_len); - #endif - ForceZero(key->priv_data, priv_data_len); - XFREE(key->priv_data, key->heap, DYNAMIC_TYPE_LMS); - key->priv_data = NULL; - } - - ForceZero(key->priv_raw, HSS_MAX_PRIVATE_KEY_LEN); - key->privSet = 0; - ForceZero(&key->priv, sizeof(HssPrivKey)); + wc_lmskey_wipe_priv(key); + XFREE(key->priv_data, key->heap, DYNAMIC_TYPE_LMS); + key->priv_data = NULL; key->write_private_key = NULL; key->read_private_key = NULL; @@ -1304,6 +1314,9 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) if (rv != WC_LMS_RC_SAVED_TO_NV_MEMORY) { ret = IO_FAILED_E; } + else { + key->privSet = 1; + } } /* This should not happen, but check whether signatures can be created. */ @@ -1313,11 +1326,14 @@ int wc_LmsKey_MakeKey(LmsKey* key, WC_RNG* rng) ret = BAD_STATE_E; } + if ((ret != 0) && (priv_data_len > 0)) { + wc_lmskey_wipe_priv(key); + } + if (ret == 0) { /* Update state. */ key->state = WC_LMS_STATE_OK; key->pubSet = 1; - key->privSet = 1; } #endif /* WOLF_CRYPTO_CB_ONLY_LMS */ @@ -1431,6 +1447,9 @@ int wc_LmsKey_Reload(LmsKey* key) HSS_PRIVATE_KEY_LEN(params->hash_len)); } #endif + if (ret == 0) { + key->privSet = 1; + } } /* Double check the key actually has signatures left. */ @@ -1461,10 +1480,13 @@ int wc_LmsKey_Reload(LmsKey* key) } } + if ((ret != 0) && (priv_data_len > 0)) { + wc_lmskey_wipe_priv(key); + } + if (ret == 0) { /* Update state. */ key->state = WC_LMS_STATE_OK; - key->privSet = 1; } #endif /* WOLF_CRYPTO_CB_ONLY_LMS */ @@ -1595,6 +1617,10 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, WOLFSSL_MSG("error: LmsKey write/read callbacks are not set"); ret = BAD_FUNC_ARG; } + if ((ret == 0) && (!key->privSet)) { + WOLFSSL_MSG("error: LMS private key not held in software"); + ret = BAD_STATE_E; + } if (ret == 0) { WC_DECLARE_VAR(state, LmsState, 1, 0); @@ -1671,6 +1697,7 @@ int wc_LmsKey_Sign(LmsKey* key, byte* sig, word32* sigSz, const byte* msg, * @return 1 if there are signatures remaining. * @return 0 if available signatures are exhausted, or the key is NULL, has * no parameters set or is verify-only. + * @return 0 when the device declines and software holds no private key. */ int wc_LmsKey_SigsLeft(LmsKey* key) { @@ -1697,14 +1724,14 @@ int wc_LmsKey_SigsLeft(LmsKey* key) WOLFSSL_MSG("PqcStatefulSigSigsLeft returned an error"); return 0; } - /* Cryptocb declined. priv_raw reflects software state from the - * CRYPTOCB_UNAVAILABLE fall-through in MakeKey/Reload, so the - * software check below is valid. */ 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); + /* Only a software MakeKey or Reload fills priv_raw. */ + if (key->privSet) { + ret = wc_hss_sigsleft(key->params, key->priv_raw); + } #endif } diff --git a/wolfcrypt/src/wc_xmss.c b/wolfcrypt/src/wc_xmss.c index 906ba03dec..e2dcbb9966 100644 --- a/wolfcrypt/src/wc_xmss.c +++ b/wolfcrypt/src/wc_xmss.c @@ -1593,6 +1593,10 @@ int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg, WOLFSSL_MSG("error: XmssKey write/read callbacks are not set"); ret = BAD_FUNC_ARG; } + if ((ret == 0) && (key->sk == NULL)) { + WOLFSSL_MSG("error: XMSS secret key not held in software"); + ret = BAD_STATE_E; + } if (ret == 0) { *sigLen = key->params->sig_len; @@ -1609,6 +1613,7 @@ int wc_XmssKey_Sign(XmssKey* key, byte* sig, word32* sigLen, const byte* msg, * @param [in] key XMSS key to check. * @return 1 when signatures possible. * @return 0 when key exhausted. + * @return 0 when the device declines and software holds no private key. */ int wc_XmssKey_SigsLeft(XmssKey* key) { @@ -1635,9 +1640,6 @@ int wc_XmssKey_SigsLeft(XmssKey* key) WOLFSSL_MSG("PqcStatefulSigSigsLeft returned an error"); return 0; } - /* Cryptocb declined. Fall through to the software path: state - * reflects the CRYPTOCB_UNAVAILABLE fall-through in MakeKey/Reload, - * and read_private_key can repopulate sk from NV storage. */ WOLFSSL_MSG("XMSS SigsLeft not supported by device, using software"); } #endif @@ -1652,6 +1654,11 @@ int wc_XmssKey_SigsLeft(XmssKey* key) WOLFSSL_MSG("error: can't sign, XMSS key not in good state"); ret = 0; } + /* A key held by a device has no secret key in software. */ + else if ((key->sk == NULL) || (key->read_private_key == NULL)) { + WOLFSSL_MSG("error: XMSS secret key not held in software"); + ret = 0; + } /* Read the current secret key from NV storage.*/ else if (key->read_private_key(key->sk, key->sk_len, key->context) != WC_XMSS_RC_READ_TO_MEMORY) {