5555 * Key data is assigned into Dilithium key rather than copied.
5656 * Life of key data passed in is tightly coupled to life of Dilithium key.
5757 * Cannot be used when make key is enabled.
58+ * WOLFSSL_DILITHIUM_DYNAMIC_KEYS Default: OFF
59+ * Key buffers (public and private) are dynamically allocated on the heap
60+ * instead of being static arrays in the key struct. Buffers are right-sized
61+ * for the key's ML-DSA level and only allocated when needed (e.g. no private
62+ * key buffer for verify-only keys). Reduces memory footprint significantly.
63+ * Cannot be used with WOLFSSL_DILITHIUM_ASSIGN_KEY.
5864 * WOLFSSL_DILITHIUM_SIGN_SMALL_MEM Default: OFF
5965 * Compiles signature implementation that uses smaller amounts of memory but
6066 * is considerably slower.
@@ -218,6 +224,11 @@ void print_data(const char* name, const byte* d, int len)
218224 #error "Cannot use assign key when making keys"
219225#endif
220226
227+ #if defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS ) && \
228+ defined(WOLFSSL_DILITHIUM_ASSIGN_KEY )
229+ #error "Cannot use both WOLFSSL_DILITHIUM_DYNAMIC_KEYS and WOLFSSL_DILITHIUM_ASSIGN_KEY"
230+ #endif
231+
221232
222233/* Number of bytes from first block to use for sign. */
223234#define DILITHIUM_SIGN_BYTES 8
@@ -358,6 +369,69 @@ static int dilithium_get_params(int level, const wc_dilithium_params** params)
358369 return ret ;
359370}
360371
372+ #if defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS ) && \
373+ defined(WOLFSSL_DILITHIUM_PRIVATE_KEY )
374+ /* Allocate the private key buffer for the current level if not already
375+ * allocated. Buffer is sized via wc_dilithium_size(key) so that any later
376+ * code reading via key->params stays within bounds. On failure key->k may
377+ * remain NULL; callers must not inspect it. */
378+ static int dilithium_alloc_priv_buf (dilithium_key * key )
379+ {
380+ int ret = 0 ;
381+
382+ if (key -> k == NULL ) {
383+ int secSz = wc_dilithium_size (key );
384+ if (secSz < 0 ) {
385+ /* Should not happen, as the level checks have already been
386+ * performed, but defense-in-depth. */
387+ ret = BAD_STATE_E ;
388+ }
389+ else {
390+ #ifdef USE_INTEL_SPEEDUP
391+ secSz += 8 ;
392+ #endif
393+ key -> k = (byte * )XMALLOC ((word32 )secSz , key -> heap ,
394+ DYNAMIC_TYPE_DILITHIUM );
395+ if (key -> k == NULL ) {
396+ ret = MEMORY_E ;
397+ }
398+ }
399+ }
400+ return ret ;
401+ }
402+ #endif
403+
404+ #if defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS ) && \
405+ defined(WOLFSSL_DILITHIUM_PUBLIC_KEY )
406+ /* Allocate the public key buffer for the current level if not already
407+ * allocated. Buffer is sized via wc_dilithium_pub_size(key). On failure
408+ * key->p may remain NULL; callers must not inspect it. */
409+ static int dilithium_alloc_pub_buf (dilithium_key * key )
410+ {
411+ int ret = 0 ;
412+
413+ if (key -> p == NULL ) {
414+ int pubSz = wc_dilithium_pub_size (key );
415+ if (pubSz < 0 ) {
416+ /* Should not happen, as the level checks have already been
417+ * performed, but defense-in-depth. */
418+ ret = BAD_STATE_E ;
419+ }
420+ else {
421+ #ifdef USE_INTEL_SPEEDUP
422+ pubSz += 8 ;
423+ #endif
424+ key -> p = (byte * )XMALLOC ((word32 )pubSz , key -> heap ,
425+ DYNAMIC_TYPE_DILITHIUM );
426+ if (key -> p == NULL ) {
427+ ret = MEMORY_E ;
428+ }
429+ }
430+ }
431+ return ret ;
432+ }
433+ #endif
434+
361435/******************************************************************************
362436 * Hash operations
363437 ******************************************************************************/
@@ -7654,9 +7728,20 @@ static int dilithium_make_key_from_seed(dilithium_key* key, const byte* seed)
76547728 sword32 * s1 = NULL ;
76557729 sword32 * s2 = NULL ;
76567730 sword32 * t = NULL ;
7657- byte * pub_seed = key -> k ;
7731+ byte * pub_seed = NULL ;
76587732 byte kl [2 ];
76597733
7734+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
7735+ ret = dilithium_alloc_priv_buf (key );
7736+ if (ret == 0 ) {
7737+ ret = dilithium_alloc_pub_buf (key );
7738+ }
7739+ #endif
7740+
7741+ if (ret == 0 ) {
7742+ pub_seed = key -> k ;
7743+ }
7744+
76607745 /* Allocate memory for large intermediates. */
76617746#ifdef WC_DILITHIUM_CACHE_MATRIX_A
76627747#ifndef WC_DILITHIUM_FIXED_ARRAY
@@ -7818,11 +7903,22 @@ static int dilithium_make_key_from_seed(dilithium_key* key, const byte* seed)
78187903 sword64 * t64 = NULL ;
78197904#endif
78207905 byte * h = NULL ;
7821- byte * pub_seed = key -> k ;
7906+ byte * pub_seed = NULL ;
78227907 unsigned int r ;
78237908 unsigned int s ;
78247909 byte kl [2 ];
78257910
7911+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
7912+ ret = dilithium_alloc_priv_buf (key );
7913+ if (ret == 0 ) {
7914+ ret = dilithium_alloc_pub_buf (key );
7915+ }
7916+ #endif
7917+
7918+ if (ret == 0 ) {
7919+ pub_seed = key -> k ;
7920+ }
7921+
78267922 /* Allocate memory for large intermediates. */
78277923 if (ret == 0 ) {
78287924 unsigned int allocSz ;
@@ -10000,6 +10096,16 @@ static int oqs_dilithium_make_key(dilithium_key* key, WC_RNG* rng)
1000010096 ret = SIG_TYPE_E ;
1000110097 }
1000210098
10099+
10100+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
10101+ if (ret == 0 ) {
10102+ ret = dilithium_alloc_priv_buf (key );
10103+ }
10104+ if (ret == 0 ) {
10105+ ret = dilithium_alloc_pub_buf (key );
10106+ }
10107+ #endif
10108+
1000310109 if (ret == 0 ) {
1000410110 ret = wolfSSL_liboqsRngMutexLock (rng );
1000510111 if (ret == 0 ) {
@@ -10874,6 +10980,10 @@ int wc_dilithium_init_label(dilithium_key* key, const char* label, void* heap,
1087410980int wc_dilithium_set_level (dilithium_key * key , byte level )
1087510981{
1087610982 int ret = 0 ;
10983+ #if defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS ) && \
10984+ defined(WOLFSSL_DILITHIUM_PRIVATE_KEY )
10985+ int oldPrivKeySize = 0 ;
10986+ #endif
1087710987
1087810988 /* Validate parameters. */
1087910989 if (key == NULL ) {
@@ -10894,6 +11004,18 @@ int wc_dilithium_set_level(dilithium_key* key, byte level)
1089411004 }
1089511005
1089611006 if (ret == 0 ) {
11007+ #if defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS ) && \
11008+ defined(WOLFSSL_DILITHIUM_PRIVATE_KEY )
11009+ /* Get the old private key size. */
11010+ oldPrivKeySize = wc_dilithium_size (key );
11011+ if (oldPrivKeySize < 0 ) {
11012+ /* As the incoming level has already been validated, this error only
11013+ * happens when WOLFSSL_DILITHIUM_FIPS204_DRAFT is enabled and we
11014+ * have not set the level previously. In this case, a private key
11015+ * buffer has not been allocated yet. We can safely set it to 0. */
11016+ oldPrivKeySize = 0 ;
11017+ }
11018+ #endif
1089711019#ifdef WOLFSSL_WC_DILITHIUM
1089811020 /* Get the parameters for level into key. */
1089911021 ret = dilithium_get_params (level , & key -> params );
@@ -10921,6 +11043,25 @@ int wc_dilithium_set_level(dilithium_key* key, byte level)
1092111043#endif
1092211044#endif /* WOLFSSL_WC_DILITHIUM */
1092311045
11046+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11047+ if (key -> k != NULL ) {
11048+ #if defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS ) && \
11049+ defined(WOLFSSL_DILITHIUM_PRIVATE_KEY )
11050+ #ifdef USE_INTEL_SPEEDUP
11051+ if (oldPrivKeySize > 0 )
11052+ oldPrivKeySize += 8 ;
11053+ #endif
11054+ ForceZero (key -> k , (word32 )oldPrivKeySize );
11055+ #endif
11056+ XFREE (key -> k , key -> heap , DYNAMIC_TYPE_DILITHIUM );
11057+ key -> k = NULL ;
11058+ }
11059+ if (key -> p != NULL ) {
11060+ XFREE (key -> p , key -> heap , DYNAMIC_TYPE_DILITHIUM );
11061+ key -> p = NULL ;
11062+ }
11063+ #endif
11064+
1092411065 /* Store level and indicate public and private key are not set. */
1092511066 key -> level = level % WC_ML_DSA_DRAFT ;
1092611067 key -> pubKeySet = 0 ;
@@ -10991,6 +11132,27 @@ void wc_dilithium_free(dilithium_key* key)
1099111132 /* Free the SHAKE-128/256 object. */
1099211133 wc_Shake256_Free (& key -> shake );
1099311134#endif
11135+ #endif
11136+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11137+ if (key -> k != NULL ) {
11138+ #ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
11139+ int privSz = wc_dilithium_size (key );
11140+ if (privSz < 0 ) {
11141+ /* Should not be possible, as a buffer is only allocated if
11142+ * a valid level has been set before, but defense-in-depth. */
11143+ privSz = 0 ;
11144+ }
11145+ #ifdef USE_INTEL_SPEEDUP
11146+ else
11147+ privSz += 8 ;
11148+ #endif
11149+ ForceZero (key -> k , (word32 )privSz );
11150+ #endif
11151+ XFREE (key -> k , key -> heap , DYNAMIC_TYPE_DILITHIUM );
11152+ }
11153+ if (key -> p != NULL ) {
11154+ XFREE (key -> p , key -> heap , DYNAMIC_TYPE_DILITHIUM );
11155+ }
1099411156#endif
1099511157 /* Ensure all private data is zeroized. */
1099611158 ForceZero (key , sizeof (* key ));
@@ -11553,12 +11715,19 @@ int wc_dilithium_import_public(const byte* in, word32 inLen, dilithium_key* key)
1155311715 }
1155411716 }
1155511717
11718+
11719+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11720+ if (ret == 0 ) {
11721+ ret = dilithium_alloc_pub_buf (key );
11722+ }
11723+ #endif
11724+
1155611725 if (ret == 0 ) {
1155711726 /* Copy the private key data in or copy pointer. */
11558- #ifndef WOLFSSL_DILITHIUM_ASSIGN_KEY
11559- XMEMCPY (key -> p , in , inLen );
11560- #else
11727+ #ifdef WOLFSSL_DILITHIUM_ASSIGN_KEY
1156111728 key -> p = in ;
11729+ #else
11730+ XMEMCPY (key -> p , in , inLen );
1156211731 #endif
1156311732
1156411733#ifdef WC_DILITHIUM_CACHE_PUB_VECTORS
@@ -11630,23 +11799,35 @@ static int dilithium_set_priv_key(const byte* priv, word32 privSz,
1163011799 dilithium_key * key )
1163111800{
1163211801 int ret = 0 ;
11802+ int expPrivSz ;
1163311803#ifdef WC_DILITHIUM_CACHE_MATRIX_A
1163411804 const wc_dilithium_params * params = key -> params ;
1163511805#endif
1163611806
11637- /* Validate parameters. */
11638- if ((privSz != ML_DSA_LEVEL2_KEY_SIZE ) &&
11639- (privSz != ML_DSA_LEVEL3_KEY_SIZE ) &&
11640- (privSz != ML_DSA_LEVEL5_KEY_SIZE )) {
11807+ /* Validate parameters. privSz must match the expected size for the
11808+ * level set on the key. This is required so that subsequent code
11809+ * which reads via key->params stays within the (possibly dynamically
11810+ * sized) buffer. */
11811+ expPrivSz = wc_dilithium_size (key );
11812+ if (expPrivSz < 0 ) {
11813+ ret = BAD_FUNC_ARG ;
11814+ }
11815+ else if (privSz != (word32 )expPrivSz ) {
1164111816 ret = BAD_FUNC_ARG ;
1164211817 }
1164311818
11819+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11820+ if (ret == 0 ) {
11821+ ret = dilithium_alloc_priv_buf (key );
11822+ }
11823+ #endif
11824+
1164411825 if (ret == 0 ) {
1164511826 /* Copy the private key data in or copy pointer. */
11646- #ifndef WOLFSSL_DILITHIUM_ASSIGN_KEY
11647- XMEMCPY (key -> k , priv , privSz );
11648- #else
11827+ #ifdef WOLFSSL_DILITHIUM_ASSIGN_KEY
1164911828 key -> k = priv ;
11829+ #else
11830+ XMEMCPY (key -> k , priv , privSz );
1165011831 #endif
1165111832 }
1165211833
0 commit comments