Skip to content

Commit bac0563

Browse files
authored
Merge pull request #9919 from anhu/lms-leaf-idx
Fix buffer-overflow in LMS leaf cache indexing
2 parents 424af6e + 00d0b09 commit bac0563

2 files changed

Lines changed: 178 additions & 1 deletion

File tree

tests/api.c

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33565,6 +33565,178 @@ static int test_wolfSSL_inject(void)
3356533565
int testAll = 1;
3356633566
int 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+
3356833740
TEST_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),

wolfcrypt/src/wc_lms_impl.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1962,7 +1962,8 @@ static int wc_lms_treehash_init(LmsState* state, LmsPrivState* privState,
19621962

19631963
/* Cache leaf node if in range. */
19641964
if ((ret == 0) && (i >= leaf->idx) && (i < leaf->idx + max_cb)) {
1965-
XMEMCPY(leaf->cache + i * params->hash_len, temp, params->hash_len);
1965+
XMEMCPY(leaf->cache + (i - leaf->idx) * params->hash_len, temp,
1966+
params->hash_len);
19661967
}
19671968

19681969
/* Store the node if on the authentication path. */

0 commit comments

Comments
 (0)