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,9 @@ 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+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
10984+ int oldPrivKeySize = 0 ;
10985+ #endif
1087710986
1087810987 /* Validate parameters. */
1087910988 if (key == NULL ) {
@@ -10894,6 +11003,17 @@ int wc_dilithium_set_level(dilithium_key* key, byte level)
1089411003 }
1089511004
1089611005 if (ret == 0 ) {
11006+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11007+ /* Get the old private key size. */
11008+ oldPrivKeySize = wc_dilithium_size (key );
11009+ if (oldPrivKeySize < 0 ) {
11010+ /* As the incoming level has already been validated, this error only
11011+ * happens when WOLFSSL_DILITHIUM_FIPS204_DRAFT is enabled and we
11012+ * have not set the level previously. In this case, a private key
11013+ * buffer has not been allocated yet. We can safely set it to 0. */
11014+ oldPrivKeySize = 0 ;
11015+ }
11016+ #endif
1089711017#ifdef WOLFSSL_WC_DILITHIUM
1089811018 /* Get the parameters for level into key. */
1089911019 ret = dilithium_get_params (level , & key -> params );
@@ -10921,6 +11041,24 @@ int wc_dilithium_set_level(dilithium_key* key, byte level)
1092111041#endif
1092211042#endif /* WOLFSSL_WC_DILITHIUM */
1092311043
11044+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11045+ if (key -> k != NULL ) {
11046+ #ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
11047+ #ifdef USE_INTEL_SPEEDUP
11048+ if (oldPrivKeySize > 0 )
11049+ oldPrivKeySize += 8 ;
11050+ #endif
11051+ ForceZero (key -> k , (word32 )oldPrivKeySize );
11052+ #endif
11053+ XFREE (key -> k , key -> heap , DYNAMIC_TYPE_DILITHIUM );
11054+ key -> k = NULL ;
11055+ }
11056+ if (key -> p != NULL ) {
11057+ XFREE (key -> p , key -> heap , DYNAMIC_TYPE_DILITHIUM );
11058+ key -> p = NULL ;
11059+ }
11060+ #endif
11061+
1092411062 /* Store level and indicate public and private key are not set. */
1092511063 key -> level = level % WC_ML_DSA_DRAFT ;
1092611064 key -> pubKeySet = 0 ;
@@ -10991,6 +11129,27 @@ void wc_dilithium_free(dilithium_key* key)
1099111129 /* Free the SHAKE-128/256 object. */
1099211130 wc_Shake256_Free (& key -> shake );
1099311131#endif
11132+ #endif
11133+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11134+ if (key -> k != NULL ) {
11135+ #ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
11136+ int privSz = wc_dilithium_size (key );
11137+ if (privSz < 0 ) {
11138+ /* Should not be possible, as a buffer is only allocated if
11139+ * a valid level has been set before, but defense-in-depth. */
11140+ privSz = 0 ;
11141+ }
11142+ #ifdef USE_INTEL_SPEEDUP
11143+ else
11144+ privSz += 8 ;
11145+ #endif
11146+ ForceZero (key -> k , (word32 )privSz );
11147+ #endif
11148+ XFREE (key -> k , key -> heap , DYNAMIC_TYPE_DILITHIUM );
11149+ }
11150+ if (key -> p != NULL ) {
11151+ XFREE (key -> p , key -> heap , DYNAMIC_TYPE_DILITHIUM );
11152+ }
1099411153#endif
1099511154 /* Ensure all private data is zeroized. */
1099611155 ForceZero (key , sizeof (* key ));
@@ -11553,12 +11712,19 @@ int wc_dilithium_import_public(const byte* in, word32 inLen, dilithium_key* key)
1155311712 }
1155411713 }
1155511714
11715+
11716+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11717+ if (ret == 0 ) {
11718+ ret = dilithium_alloc_pub_buf (key );
11719+ }
11720+ #endif
11721+
1155611722 if (ret == 0 ) {
1155711723 /* Copy the private key data in or copy pointer. */
11558- #ifndef WOLFSSL_DILITHIUM_ASSIGN_KEY
11559- XMEMCPY (key -> p , in , inLen );
11560- #else
11724+ #ifdef WOLFSSL_DILITHIUM_ASSIGN_KEY
1156111725 key -> p = in ;
11726+ #else
11727+ XMEMCPY (key -> p , in , inLen );
1156211728 #endif
1156311729
1156411730#ifdef WC_DILITHIUM_CACHE_PUB_VECTORS
@@ -11630,23 +11796,35 @@ static int dilithium_set_priv_key(const byte* priv, word32 privSz,
1163011796 dilithium_key * key )
1163111797{
1163211798 int ret = 0 ;
11799+ int expPrivSz ;
1163311800#ifdef WC_DILITHIUM_CACHE_MATRIX_A
1163411801 const wc_dilithium_params * params = key -> params ;
1163511802#endif
1163611803
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 )) {
11804+ /* Validate parameters. privSz must match the expected size for the
11805+ * level set on the key. This is required so that subsequent code
11806+ * which reads via key->params stays within the (possibly dynamically
11807+ * sized) buffer. */
11808+ expPrivSz = wc_dilithium_size (key );
11809+ if (expPrivSz < 0 ) {
11810+ ret = BAD_FUNC_ARG ;
11811+ }
11812+ else if (privSz != (word32 )expPrivSz ) {
1164111813 ret = BAD_FUNC_ARG ;
1164211814 }
1164311815
11816+ #ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11817+ if (ret == 0 ) {
11818+ ret = dilithium_alloc_priv_buf (key );
11819+ }
11820+ #endif
11821+
1164411822 if (ret == 0 ) {
1164511823 /* Copy the private key data in or copy pointer. */
11646- #ifndef WOLFSSL_DILITHIUM_ASSIGN_KEY
11647- XMEMCPY (key -> k , priv , privSz );
11648- #else
11824+ #ifdef WOLFSSL_DILITHIUM_ASSIGN_KEY
1164911825 key -> k = priv ;
11826+ #else
11827+ XMEMCPY (key -> k , priv , privSz );
1165011828 #endif
1165111829 }
1165211830
0 commit comments