@@ -32856,6 +32856,178 @@ static int test_wolfSSL_inject(void)
3285632856int testAll = 1;
3285732857int stopOnFail = 0;
3285832858
32859+ /*----------------------------------------------------------------------------*/
32860+ /* LMS tests */
32861+ /*----------------------------------------------------------------------------*/
32862+ int test_wc_LmsKey_sign_verify(void);
32863+ int test_wc_LmsKey_reload_cache(void);
32864+
32865+ #if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS) && \
32866+ !defined(WOLFSSL_LMS_VERIFY_ONLY)
32867+
32868+ #include <wolfssl/wolfcrypt/wc_lms.h>
32869+ #include <wolfssl/wolfcrypt/lms.h>
32870+
32871+ #define LMS_TEST_PRIV_KEY_FILE "/tmp/wolfssl_test_lms.key"
32872+
32873+ static int test_lms_write_key(const byte* priv, word32 privSz, void* context)
32874+ {
32875+ FILE* f = fopen((const char*)context, "wb");
32876+ if (f == NULL)
32877+ return -1;
32878+ fwrite(priv, 1, privSz, f);
32879+ fclose(f);
32880+ return WC_LMS_RC_SAVED_TO_NV_MEMORY;
32881+ }
32882+
32883+ static int test_lms_read_key(byte* priv, word32 privSz, void* context)
32884+ {
32885+ FILE* f = fopen((const char*)context, "rb");
32886+ if (f == NULL)
32887+ return -1;
32888+ if (fread(priv, 1, privSz, f) == 0) {
32889+ fclose(f);
32890+ return -1;
32891+ }
32892+ fclose(f);
32893+ return WC_LMS_RC_READ_TO_MEMORY;
32894+ }
32895+
32896+ /* Helper: init an LMS key with callbacks and L1-H10-W8 params */
32897+ static int test_lms_init_key(LmsKey* key, WC_RNG* rng)
32898+ {
32899+ int ret;
32900+
32901+ ret = wc_LmsKey_Init(key, NULL, INVALID_DEVID);
32902+ if (ret != 0) return ret;
32903+
32904+ ret = wc_LmsKey_SetParameters(key, 1, 10, 8);
32905+ if (ret != 0) return ret;
32906+
32907+ ret = wc_LmsKey_SetWriteCb(key, test_lms_write_key);
32908+ if (ret != 0) return ret;
32909+
32910+ ret = wc_LmsKey_SetReadCb(key, test_lms_read_key);
32911+ if (ret != 0) return ret;
32912+
32913+ ret = wc_LmsKey_SetContext(key, (void*)LMS_TEST_PRIV_KEY_FILE);
32914+ if (ret != 0) return ret;
32915+
32916+ (void)rng;
32917+ return 0;
32918+ }
32919+
32920+ #endif /* WOLFSSL_HAVE_LMS && WOLFSSL_WC_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
32921+
32922+ /*
32923+ * Test basic LMS sign/verify with multiple signings.
32924+ * Uses L1-H10-W8 (1024 total signatures, 32-entry leaf cache).
32925+ */
32926+ int test_wc_LmsKey_sign_verify(void)
32927+ {
32928+ EXPECT_DECLS;
32929+ #if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS) && \
32930+ !defined(WOLFSSL_LMS_VERIFY_ONLY)
32931+ LmsKey key;
32932+ WC_RNG rng;
32933+ byte msg[] = "test message for LMS signing";
32934+ byte sig[2048];
32935+ word32 sigSz;
32936+ int i;
32937+ int numSigs = 5;
32938+
32939+ ExpectIntEQ(wc_InitRng(&rng), 0);
32940+
32941+ remove(LMS_TEST_PRIV_KEY_FILE);
32942+ ExpectIntEQ(test_lms_init_key(&key, &rng), 0);
32943+ ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0);
32944+
32945+ for (i = 0; i < numSigs; i++) {
32946+ sigSz = sizeof(sig);
32947+ ExpectIntEQ(wc_LmsKey_Sign(&key, sig, &sigSz, msg, sizeof(msg)), 0);
32948+ ExpectIntEQ(wc_LmsKey_Verify(&key, sig, sigSz, msg, sizeof(msg)), 0);
32949+ }
32950+
32951+ wc_LmsKey_Free(&key);
32952+ wc_FreeRng(&rng);
32953+ remove(LMS_TEST_PRIV_KEY_FILE);
32954+ #endif
32955+ return EXPECT_RESULT();
32956+ }
32957+
32958+ /*
32959+ * Test LMS key reload after advancing past the leaf cache window.
32960+ *
32961+ * Reproduces a heap-buffer-overflow bug in wc_lms_treehash_init() where the
32962+ * leaf cache write uses (i * hash_len) instead of ((i - leaf->idx) * hash_len).
32963+ * When q > max_cb (default 32), wc_LmsKey_Reload calls wc_hss_init_auth_path
32964+ * which calls wc_lms_treehash_init with q > 0, causing writes past the end of
32965+ * the leaf cache buffer.
32966+ *
32967+ * Reproduction steps:
32968+ * 1. Generate L1-H10-W8 key (cacheBits=5, max_cb=32)
32969+ * 2. Sign 33 times to advance q past the cache window
32970+ * 3. Free the key and reload from persisted state
32971+ * 4. Sign and verify after reload
32972+ *
32973+ * Without the fix: heap-buffer-overflow at wc_lms_impl.c:1965
32974+ * With the fix: all operations succeed, signatures verify
32975+ */
32976+ int test_wc_LmsKey_reload_cache(void)
32977+ {
32978+ EXPECT_DECLS;
32979+ #if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS) && \
32980+ !defined(WOLFSSL_LMS_VERIFY_ONLY)
32981+ LmsKey key;
32982+ LmsKey vkey;
32983+ WC_RNG rng;
32984+ byte msg[] = "test message for LMS signing";
32985+ byte sig[2048];
32986+ word32 sigSz;
32987+ byte pub[64];
32988+ word32 pubSz = sizeof(pub);
32989+ int i;
32990+ /* Sign 33 times to advance q past the 32-entry cache window. */
32991+ int preSigs = 33;
32992+
32993+ ExpectIntEQ(wc_InitRng(&rng), 0);
32994+
32995+ /* Phase 1: Generate key and sign past cache window */
32996+ remove(LMS_TEST_PRIV_KEY_FILE);
32997+ ExpectIntEQ(test_lms_init_key(&key, &rng), 0);
32998+ ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0);
32999+
33000+ for (i = 0; i < preSigs; i++) {
33001+ sigSz = sizeof(sig);
33002+ ExpectIntEQ(wc_LmsKey_Sign(&key, sig, &sigSz, msg, sizeof(msg)), 0);
33003+ }
33004+
33005+ /* Save public key for verification after reload */
33006+ ExpectIntEQ(wc_LmsKey_ExportPubRaw(&key, pub, &pubSz), 0);
33007+
33008+ wc_LmsKey_Free(&key);
33009+
33010+ /* Phase 2: Reload key. Triggers wc_lms_treehash_init with q=33 */
33011+ ExpectIntEQ(test_lms_init_key(&key, &rng), 0);
33012+ ExpectIntEQ(wc_LmsKey_Reload(&key), 0);
33013+
33014+ /* Phase 3: Sign after reload and verify with separate verify-only key */
33015+ sigSz = sizeof(sig);
33016+ ExpectIntEQ(wc_LmsKey_Sign(&key, sig, &sigSz, msg, sizeof(msg)), 0);
33017+
33018+ ExpectIntEQ(wc_LmsKey_Init(&vkey, NULL, INVALID_DEVID), 0);
33019+ ExpectIntEQ(wc_LmsKey_SetParameters(&vkey, 1, 10, 8), 0);
33020+ ExpectIntEQ(wc_LmsKey_ImportPubRaw(&vkey, pub, pubSz), 0);
33021+ ExpectIntEQ(wc_LmsKey_Verify(&vkey, sig, sigSz, msg, sizeof(msg)), 0);
33022+
33023+ wc_LmsKey_Free(&vkey);
33024+ wc_LmsKey_Free(&key);
33025+ wc_FreeRng(&rng);
33026+ remove(LMS_TEST_PRIV_KEY_FILE);
33027+ #endif
33028+ return EXPECT_RESULT();
33029+ }
33030+
3285933031TEST_CASE testCases[] = {
3286033032 TEST_DECL(test_fileAccess),
3286133033
@@ -32977,6 +33149,10 @@ TEST_CASE testCases[] = {
3297733149 /* ASN */
3297833150 TEST_ASN_DECLS,
3297933151
33152+ /* LMS */
33153+ TEST_DECL_GROUP("lms", test_wc_LmsKey_sign_verify),
33154+ TEST_DECL_GROUP("lms", test_wc_LmsKey_reload_cache),
33155+
3298033156 /* PEM and DER APIs. */
3298133157 TEST_DECL(test_wc_PemToDer),
3298233158 TEST_DECL(test_wc_AllocDer),
0 commit comments