@@ -33565,6 +33565,178 @@ static int test_wolfSSL_inject(void)
3356533565int testAll = 1;
3356633566int stopOnFail = 0;
3356733567
33568+ /*----------------------------------------------------------------------------*/
33569+ /* LMS tests */
33570+ /*----------------------------------------------------------------------------*/
33571+ int test_wc_LmsKey_sign_verify(void);
33572+ int test_wc_LmsKey_reload_cache(void);
33573+
33574+ #if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS) && \
33575+ !defined(WOLFSSL_LMS_VERIFY_ONLY)
33576+
33577+ #include <wolfssl/wolfcrypt/wc_lms.h>
33578+ #include <wolfssl/wolfcrypt/lms.h>
33579+
33580+ #define LMS_TEST_PRIV_KEY_FILE "/tmp/wolfssl_test_lms.key"
33581+
33582+ static int test_lms_write_key(const byte* priv, word32 privSz, void* context)
33583+ {
33584+ FILE* f = fopen((const char*)context, "wb");
33585+ if (f == NULL)
33586+ return -1;
33587+ fwrite(priv, 1, privSz, f);
33588+ fclose(f);
33589+ return WC_LMS_RC_SAVED_TO_NV_MEMORY;
33590+ }
33591+
33592+ static int test_lms_read_key(byte* priv, word32 privSz, void* context)
33593+ {
33594+ FILE* f = fopen((const char*)context, "rb");
33595+ if (f == NULL)
33596+ return -1;
33597+ if (fread(priv, 1, privSz, f) == 0) {
33598+ fclose(f);
33599+ return -1;
33600+ }
33601+ fclose(f);
33602+ return WC_LMS_RC_READ_TO_MEMORY;
33603+ }
33604+
33605+ /* Helper: init an LMS key with callbacks and L1-H10-W8 params */
33606+ static int test_lms_init_key(LmsKey* key, WC_RNG* rng)
33607+ {
33608+ int ret;
33609+
33610+ ret = wc_LmsKey_Init(key, NULL, INVALID_DEVID);
33611+ if (ret != 0) return ret;
33612+
33613+ ret = wc_LmsKey_SetParameters(key, 1, 10, 8);
33614+ if (ret != 0) return ret;
33615+
33616+ ret = wc_LmsKey_SetWriteCb(key, test_lms_write_key);
33617+ if (ret != 0) return ret;
33618+
33619+ ret = wc_LmsKey_SetReadCb(key, test_lms_read_key);
33620+ if (ret != 0) return ret;
33621+
33622+ ret = wc_LmsKey_SetContext(key, (void*)LMS_TEST_PRIV_KEY_FILE);
33623+ if (ret != 0) return ret;
33624+
33625+ (void)rng;
33626+ return 0;
33627+ }
33628+
33629+ #endif /* WOLFSSL_HAVE_LMS && WOLFSSL_WC_LMS && !WOLFSSL_LMS_VERIFY_ONLY */
33630+
33631+ /*
33632+ * Test basic LMS sign/verify with multiple signings.
33633+ * Uses L1-H10-W8 (1024 total signatures, 32-entry leaf cache).
33634+ */
33635+ int test_wc_LmsKey_sign_verify(void)
33636+ {
33637+ EXPECT_DECLS;
33638+ #if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS) && \
33639+ !defined(WOLFSSL_LMS_VERIFY_ONLY)
33640+ LmsKey key;
33641+ WC_RNG rng;
33642+ byte msg[] = "test message for LMS signing";
33643+ byte sig[2048];
33644+ word32 sigSz;
33645+ int i;
33646+ int numSigs = 5;
33647+
33648+ ExpectIntEQ(wc_InitRng(&rng), 0);
33649+
33650+ remove(LMS_TEST_PRIV_KEY_FILE);
33651+ ExpectIntEQ(test_lms_init_key(&key, &rng), 0);
33652+ ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0);
33653+
33654+ for (i = 0; i < numSigs; i++) {
33655+ sigSz = sizeof(sig);
33656+ ExpectIntEQ(wc_LmsKey_Sign(&key, sig, &sigSz, msg, sizeof(msg)), 0);
33657+ ExpectIntEQ(wc_LmsKey_Verify(&key, sig, sigSz, msg, sizeof(msg)), 0);
33658+ }
33659+
33660+ wc_LmsKey_Free(&key);
33661+ wc_FreeRng(&rng);
33662+ remove(LMS_TEST_PRIV_KEY_FILE);
33663+ #endif
33664+ return EXPECT_RESULT();
33665+ }
33666+
33667+ /*
33668+ * Test LMS key reload after advancing past the leaf cache window.
33669+ *
33670+ * Reproduces a heap-buffer-overflow bug in wc_lms_treehash_init() where the
33671+ * leaf cache write uses (i * hash_len) instead of ((i - leaf->idx) * hash_len).
33672+ * When q > max_cb (default 32), wc_LmsKey_Reload calls wc_hss_init_auth_path
33673+ * which calls wc_lms_treehash_init with q > 0, causing writes past the end of
33674+ * the leaf cache buffer.
33675+ *
33676+ * Reproduction steps:
33677+ * 1. Generate L1-H10-W8 key (cacheBits=5, max_cb=32)
33678+ * 2. Sign 33 times to advance q past the cache window
33679+ * 3. Free the key and reload from persisted state
33680+ * 4. Sign and verify after reload
33681+ *
33682+ * Without the fix: heap-buffer-overflow at wc_lms_impl.c:1965
33683+ * With the fix: all operations succeed, signatures verify
33684+ */
33685+ int test_wc_LmsKey_reload_cache(void)
33686+ {
33687+ EXPECT_DECLS;
33688+ #if defined(WOLFSSL_HAVE_LMS) && defined(WOLFSSL_WC_LMS) && \
33689+ !defined(WOLFSSL_LMS_VERIFY_ONLY)
33690+ LmsKey key;
33691+ LmsKey vkey;
33692+ WC_RNG rng;
33693+ byte msg[] = "test message for LMS signing";
33694+ byte sig[2048];
33695+ word32 sigSz;
33696+ byte pub[64];
33697+ word32 pubSz = sizeof(pub);
33698+ int i;
33699+ /* Sign 33 times to advance q past the 32-entry cache window. */
33700+ int preSigs = 33;
33701+
33702+ ExpectIntEQ(wc_InitRng(&rng), 0);
33703+
33704+ /* Phase 1: Generate key and sign past cache window */
33705+ remove(LMS_TEST_PRIV_KEY_FILE);
33706+ ExpectIntEQ(test_lms_init_key(&key, &rng), 0);
33707+ ExpectIntEQ(wc_LmsKey_MakeKey(&key, &rng), 0);
33708+
33709+ for (i = 0; i < preSigs; i++) {
33710+ sigSz = sizeof(sig);
33711+ ExpectIntEQ(wc_LmsKey_Sign(&key, sig, &sigSz, msg, sizeof(msg)), 0);
33712+ }
33713+
33714+ /* Save public key for verification after reload */
33715+ ExpectIntEQ(wc_LmsKey_ExportPubRaw(&key, pub, &pubSz), 0);
33716+
33717+ wc_LmsKey_Free(&key);
33718+
33719+ /* Phase 2: Reload key. Triggers wc_lms_treehash_init with q=33 */
33720+ ExpectIntEQ(test_lms_init_key(&key, &rng), 0);
33721+ ExpectIntEQ(wc_LmsKey_Reload(&key), 0);
33722+
33723+ /* Phase 3: Sign after reload and verify with separate verify-only key */
33724+ sigSz = sizeof(sig);
33725+ ExpectIntEQ(wc_LmsKey_Sign(&key, sig, &sigSz, msg, sizeof(msg)), 0);
33726+
33727+ ExpectIntEQ(wc_LmsKey_Init(&vkey, NULL, INVALID_DEVID), 0);
33728+ ExpectIntEQ(wc_LmsKey_SetParameters(&vkey, 1, 10, 8), 0);
33729+ ExpectIntEQ(wc_LmsKey_ImportPubRaw(&vkey, pub, pubSz), 0);
33730+ ExpectIntEQ(wc_LmsKey_Verify(&vkey, sig, sigSz, msg, sizeof(msg)), 0);
33731+
33732+ wc_LmsKey_Free(&vkey);
33733+ wc_LmsKey_Free(&key);
33734+ wc_FreeRng(&rng);
33735+ remove(LMS_TEST_PRIV_KEY_FILE);
33736+ #endif
33737+ return EXPECT_RESULT();
33738+ }
33739+
3356833740TEST_CASE testCases[] = {
3356933741 TEST_DECL(test_fileAccess),
3357033742
@@ -33686,6 +33858,10 @@ TEST_CASE testCases[] = {
3368633858 /* ASN */
3368733859 TEST_ASN_DECLS,
3368833860
33861+ /* LMS */
33862+ TEST_DECL_GROUP("lms", test_wc_LmsKey_sign_verify),
33863+ TEST_DECL_GROUP("lms", test_wc_LmsKey_reload_cache),
33864+
3368933865 /* PEM and DER APIs. */
3369033866 TEST_DECL(test_wc_PemToDer),
3369133867 TEST_DECL(test_wc_AllocDer),
0 commit comments