Skip to content

Commit 207c324

Browse files
committed
Add dynamic key allocation support for Dilithium
This update introduces the WOLFSSL_DILITHIUM_DYNAMIC_KEYS option, allowing for dynamic memory allocation of public and private key buffers. This change reduces memory usage by allocating buffers only when needed.
1 parent ae0a387 commit 207c324

File tree

4 files changed

+209
-14
lines changed

4 files changed

+209
-14
lines changed

.github/workflows/pq-all.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ jobs:
3434
'--enable-intelasm --enable-sp-asm --enable-all --enable-testcert --enable-dtls13 --enable-dtls-mtu --enable-dtls-frag-ch --enable-dtlscid --enable-mlkem=make,enc,dec,1024 --enable-tls-mlkem-standalone --disable-qt CPPFLAGS="-pedantic -Wdeclaration-after-statement -DWOLFCRYPT_TEST_LINT -DNO_WOLFSSL_CIPHER_SUITE_TEST -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"',
3535
'--enable-intelasm --enable-sp-asm --enable-all --enable-testcert --enable-dtls13 --enable-dtls-mtu --enable-dtls-frag-ch --enable-dtlscid --enable-mlkem=make,enc,dec,1024 --enable-tls-mlkem-standalone --disable-pqc-hybrids --disable-qt CPPFLAGS="-pedantic -Wdeclaration-after-statement -DWOLFCRYPT_TEST_LINT -DNO_WOLFSSL_CIPHER_SUITE_TEST -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"',
3636
'--enable-intelasm --enable-sp-asm --enable-all --enable-testcert --enable-acert --enable-dtls13 --enable-dtls-mtu --enable-dtls-frag-ch --enable-dtlscid --enable-quic --with-sys-crypto-policy --enable-experimental --enable-mlkem=yes,kyber,ml-kem --enable-lms --enable-xmss --enable-slhdsa --enable-dilithium=yes,no-ctx --enable-dual-alg-certs --disable-qt CPPFLAGS="-pedantic -Wdeclaration-after-statement -DWOLFCRYPT_TEST_LINT -DNO_WOLFSSL_CIPHER_SUITE_TEST -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"',
37+
'--enable-intelasm --enable-sp-asm --enable-dilithium=yes CPPFLAGS="-DWOLFSSL_DILITHIUM_DYNAMIC_KEYS"',
38+
'--disable-intelasm --enable-dilithium=yes,small CPPFLAGS="-DWOLFSSL_DILITHIUM_DYNAMIC_KEYS"',
39+
'--disable-intelasm --enable-dilithium=44,65,87,verify-only CPPFLAGS="-DWOLFSSL_DILITHIUM_DYNAMIC_KEYS"',
3740
]
3841
name: make check
3942
if: github.repository_owner == 'wolfssl'

wolfcrypt/src/dilithium.c

Lines changed: 190 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@
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,
1087410980
int 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

wolfcrypt/src/wc_pkcs11.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2247,10 +2247,21 @@ int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear, void* key)
22472247
session.func->C_DestroyObject(session.handle, privKey);
22482248
}
22492249
}
2250-
#ifndef WOLFSSL_DILITHIUM_ASSIGN_KEY
2250+
#if !defined(WOLFSSL_DILITHIUM_ASSIGN_KEY) && \
2251+
!defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS)
22512252
if (ret == 0 && clear) {
22522253
ForceZero(mldsaKey->k, sizeof(mldsaKey->k));
22532254
}
2255+
#elif defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS)
2256+
if (ret == 0 && clear && mldsaKey->k != NULL) {
2257+
int zSz = wc_dilithium_size(mldsaKey);
2258+
if (zSz > 0) {
2259+
#ifdef USE_INTEL_SPEEDUP
2260+
zSz += 8;
2261+
#endif
2262+
ForceZero(mldsaKey->k, (word32)zSz);
2263+
}
2264+
}
22542265
#endif
22552266
break;
22562267
}

wolfssl/wolfcrypt/dilithium.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,10 @@ struct dilithium_key {
734734
int labelLen;
735735
#endif
736736

737-
#ifndef WOLFSSL_DILITHIUM_ASSIGN_KEY
737+
#if defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS)
738+
byte* p; /* heap-allocated, right-sized public key */
739+
byte* k; /* heap-allocated, right-sized secret key */
740+
#elif !defined(WOLFSSL_DILITHIUM_ASSIGN_KEY)
738741
#ifdef USE_INTEL_SPEEDUP
739742
byte p[DILITHIUM_MAX_PUB_KEY_SIZE+8];
740743
byte k[DILITHIUM_MAX_KEY_SIZE+8];

0 commit comments

Comments
 (0)