Merge pull request #10400 from embhorn/gh10383

Fix Dilithium signing when WC_DILITHIUM_CACHE_MATRIX_A is enabled
This commit is contained in:
David Garske
2026-05-07 14:30:46 -07:00
committed by GitHub
2 changed files with 128 additions and 2 deletions
+6 -2
View File
@@ -8333,10 +8333,14 @@ static int dilithium_sign_with_seed_mu(dilithium_key* key,
#ifdef WC_DILITHIUM_CACHE_MATRIX_A
#ifndef WC_DILITHIUM_FIXED_ARRAY
if ((ret == 0) && (key->a == NULL)) {
a = (sword32*)XMALLOC(params->aSz, key->heap, DYNAMIC_TYPE_DILITHIUM);
if (a == NULL) {
key->a = (sword32*)XMALLOC((size_t)params->aSz, key->heap,
DYNAMIC_TYPE_DILITHIUM);
if (key->a == NULL) {
ret = MEMORY_E;
}
else {
XMEMSET(key->a, 0, (size_t)params->aSz);
}
}
#endif
if (ret == 0) {
+122
View File
@@ -52545,6 +52545,106 @@ out:
}
#endif
#if defined(WC_DILITHIUM_CACHE_MATRIX_A) && \
!defined(WC_DILITHIUM_FIXED_ARRAY) && \
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
!defined(WOLFSSL_DILITHIUM_NO_SIGN) && \
!defined(WOLFSSL_DILITHIUM_NO_VERIFY)
/* Regression test for sign path matrix A cache allocation.
*
* dilithium_sign_with_seed_mu() previously stored the result of XMALLOC for
* the matrix A cache into a local variable instead of key->a. The local was
* then immediately overwritten by `a = key->a` (still NULL), so the just-
* allocated buffer was leaked and a NULL pointer was passed to
* dilithium_expand_a().
*
* This test exercises that exact code path by clearing the cache state on a
* key after make_key, then signing. The post-condition asserts that key->a
* was populated (proving the allocation made it into the key, not the local)
* and that signing produces a verifiable signature.
*/
static wc_test_ret_t dilithium_sign_cache_alloc_test(int param, WC_RNG* rng)
{
wc_test_ret_t ret;
dilithium_key* key = NULL;
byte* sig = NULL;
word32 sigLen;
byte msg[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
int res = 0;
key = (dilithium_key*)XMALLOC(sizeof(*key), HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER);
if (key == NULL) {
ERROR_OUT(WC_TEST_RET_ENC_ERRNO, out);
}
/* Init before further allocations so wc_dilithium_free() in the cleanup
* path operates on a zeroed struct, not garbage cached-pointer fields. */
ret = wc_dilithium_init_ex(key, NULL, devId);
if (ret != 0)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
sig = (byte*)XMALLOC(DILITHIUM_MAX_SIG_SIZE, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER);
if (sig == NULL) {
ERROR_OUT(WC_TEST_RET_ENC_ERRNO, out);
}
ret = wc_dilithium_set_level(key, param);
if (ret != 0)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
ret = wc_dilithium_make_key(key, rng);
if (ret != 0)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
/* Drop the cached matrix A so the next sign exercises the allocation
* branch in dilithium_sign_with_seed_mu(). */
XFREE(key->a, key->heap, DYNAMIC_TYPE_DILITHIUM);
key->a = NULL;
key->aSet = 0;
#ifdef WC_DILITHIUM_CACHE_PRIV_VECTORS
XFREE(key->s1, key->heap, DYNAMIC_TYPE_DILITHIUM);
key->s1 = NULL;
key->s2 = NULL;
key->t0 = NULL;
key->privVecsSet = 0;
#endif
sigLen = wc_dilithium_sig_size(key);
if (sigLen <= 0)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
ret = wc_dilithium_sign_ctx_msg(NULL, 0, msg, (word32)sizeof(msg), sig,
&sigLen, key, rng);
if (ret != 0)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
/* With the fix, signing must populate key->a (allocated buffer is owned
* by the key, not leaked to a local). Without the fix, key->a remains
* NULL because the XMALLOC result was assigned to a local variable. */
if (key->a == NULL)
ERROR_OUT(WC_TEST_RET_ENC_NC, out);
if (key->aSet != 1)
ERROR_OUT(WC_TEST_RET_ENC_NC, out);
ret = wc_dilithium_verify_ctx_msg(sig, sigLen, NULL, 0, msg,
(word32)sizeof(msg), &res, key);
if (ret != 0)
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
if (res != 1)
ERROR_OUT(WC_TEST_RET_ENC_EC(res), out);
out:
if (key != NULL)
wc_dilithium_free(key);
XFREE(sig, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
#endif /* WC_DILITHIUM_CACHE_MATRIX_A && !WC_DILITHIUM_FIXED_ARRAY &&
* !WOLFSSL_DILITHIUM_NO_MAKE_KEY && !WOLFSSL_DILITHIUM_NO_SIGN &&
* !WOLFSSL_DILITHIUM_NO_VERIFY */
#if (defined(WOLFSSL_DILITHIUM_PRIVATE_KEY) && \
!defined(WOLFSSL_DILITHIUM_NO_SIGN)) || \
@@ -52835,6 +52935,28 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t dilithium_test(void)
#endif
#endif
#if defined(WC_DILITHIUM_CACHE_MATRIX_A) && \
!defined(WC_DILITHIUM_FIXED_ARRAY) && \
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
!defined(WOLFSSL_DILITHIUM_NO_SIGN) && \
!defined(WOLFSSL_DILITHIUM_NO_VERIFY)
#ifndef WOLFSSL_NO_ML_DSA_44
ret = dilithium_sign_cache_alloc_test(WC_ML_DSA_44, &rng);
if (ret != 0)
ERROR_OUT(ret, out);
#endif
#ifndef WOLFSSL_NO_ML_DSA_65
ret = dilithium_sign_cache_alloc_test(WC_ML_DSA_65, &rng);
if (ret != 0)
ERROR_OUT(ret, out);
#endif
#ifndef WOLFSSL_NO_ML_DSA_87
ret = dilithium_sign_cache_alloc_test(WC_ML_DSA_87, &rng);
if (ret != 0)
ERROR_OUT(ret, out);
#endif
#endif
#if (defined(WOLFSSL_DILITHIUM_PRIVATE_KEY) && \
!defined(WOLFSSL_DILITHIUM_NO_SIGN)) || \
(defined(WOLFSSL_DILITHIUM_PUBLIC_KEY) && \