Skip to content

Commit dd80dd2

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 a50a540 commit dd80dd2

4 files changed

Lines changed: 182 additions & 14 deletions

File tree

.github/workflows/pq-all.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ jobs:
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"',
3737
'--enable-intelasm --enable-sp-asm --enable-mlkem=yes,kyber,ml-kem,cache-a CPPFLAGS="-DWOLFSSL_MLKEM_DYNAMIC_KEYS"',
38+
'--enable-intelasm --enable-sp-asm --enable-dilithium=yes CPPFLAGS="-DWOLFSSL_DILITHIUM_DYNAMIC_KEYS"',
39+
'--disable-intelasm --enable-dilithium=yes,small CPPFLAGS="-DWOLFSSL_DILITHIUM_DYNAMIC_KEYS"',
40+
'--disable-intelasm --enable-dilithium=44,65,87,verify-only CPPFLAGS="-DWOLFSSL_DILITHIUM_DYNAMIC_KEYS"',
3841
]
3942
name: make check
4043
if: github.repository_owner == 'wolfssl'

wolfcrypt/src/dilithium.c

Lines changed: 167 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,76 @@ 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) and the allocated size
376+
* is stored in key->kSz for later use (ForceZero, free). 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+
else {
399+
key->kSz = (word32)secSz;
400+
}
401+
}
402+
}
403+
return ret;
404+
}
405+
#endif
406+
407+
#if defined(WOLFSSL_DILITHIUM_DYNAMIC_KEYS) && \
408+
defined(WOLFSSL_DILITHIUM_PUBLIC_KEY)
409+
/* Allocate the public key buffer for the current level if not already
410+
* allocated. Buffer is sized via wc_dilithium_pub_size(key) and the allocated
411+
* size is stored in key->pSz for later use. On failure key->p may remain NULL;
412+
* callers must not inspect it. */
413+
static int dilithium_alloc_pub_buf(dilithium_key* key)
414+
{
415+
int ret = 0;
416+
417+
if (key->p == NULL) {
418+
int pubSz = wc_dilithium_pub_size(key);
419+
if (pubSz < 0) {
420+
/* Should not happen, as the level checks have already been
421+
* performed, but defense-in-depth. */
422+
ret = BAD_STATE_E;
423+
}
424+
else {
425+
#ifdef USE_INTEL_SPEEDUP
426+
pubSz += 8;
427+
#endif
428+
key->p = (byte*)XMALLOC((word32)pubSz, key->heap,
429+
DYNAMIC_TYPE_DILITHIUM);
430+
if (key->p == NULL) {
431+
ret = MEMORY_E;
432+
}
433+
else {
434+
key->pSz = (word32)pubSz;
435+
}
436+
}
437+
}
438+
return ret;
439+
}
440+
#endif
441+
361442
/******************************************************************************
362443
* Hash operations
363444
******************************************************************************/
@@ -7654,9 +7735,20 @@ static int dilithium_make_key_from_seed(dilithium_key* key, const byte* seed)
76547735
sword32* s1 = NULL;
76557736
sword32* s2 = NULL;
76567737
sword32* t = NULL;
7657-
byte* pub_seed = key->k;
7738+
byte* pub_seed = NULL;
76587739
byte kl[2];
76597740

7741+
#ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
7742+
ret = dilithium_alloc_priv_buf(key);
7743+
if (ret == 0) {
7744+
ret = dilithium_alloc_pub_buf(key);
7745+
}
7746+
#endif
7747+
7748+
if (ret == 0) {
7749+
pub_seed = key->k;
7750+
}
7751+
76607752
/* Allocate memory for large intermediates. */
76617753
#ifdef WC_DILITHIUM_CACHE_MATRIX_A
76627754
#ifndef WC_DILITHIUM_FIXED_ARRAY
@@ -7818,11 +7910,22 @@ static int dilithium_make_key_from_seed(dilithium_key* key, const byte* seed)
78187910
sword64* t64 = NULL;
78197911
#endif
78207912
byte* h = NULL;
7821-
byte* pub_seed = key->k;
7913+
byte* pub_seed = NULL;
78227914
unsigned int r;
78237915
unsigned int s;
78247916
byte kl[2];
78257917

7918+
#ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
7919+
ret = dilithium_alloc_priv_buf(key);
7920+
if (ret == 0) {
7921+
ret = dilithium_alloc_pub_buf(key);
7922+
}
7923+
#endif
7924+
7925+
if (ret == 0) {
7926+
pub_seed = key->k;
7927+
}
7928+
78267929
/* Allocate memory for large intermediates. */
78277930
if (ret == 0) {
78287931
unsigned int allocSz;
@@ -10000,6 +10103,16 @@ static int oqs_dilithium_make_key(dilithium_key* key, WC_RNG* rng)
1000010103
ret = SIG_TYPE_E;
1000110104
}
1000210105

10106+
10107+
#ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
10108+
if (ret == 0) {
10109+
ret = dilithium_alloc_priv_buf(key);
10110+
}
10111+
if (ret == 0) {
10112+
ret = dilithium_alloc_pub_buf(key);
10113+
}
10114+
#endif
10115+
1000310116
if (ret == 0) {
1000410117
ret = wolfSSL_liboqsRngMutexLock(rng);
1000510118
if (ret == 0) {
@@ -10921,6 +11034,20 @@ int wc_dilithium_set_level(dilithium_key* key, byte level)
1092111034
#endif
1092211035
#endif /* WOLFSSL_WC_DILITHIUM */
1092311036

11037+
#ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11038+
if (key->k != NULL) {
11039+
ForceZero(key->k, key->kSz);
11040+
XFREE(key->k, key->heap, DYNAMIC_TYPE_DILITHIUM);
11041+
key->k = NULL;
11042+
key->kSz = 0;
11043+
}
11044+
if (key->p != NULL) {
11045+
XFREE(key->p, key->heap, DYNAMIC_TYPE_DILITHIUM);
11046+
key->p = NULL;
11047+
key->pSz = 0;
11048+
}
11049+
#endif
11050+
1092411051
/* Store level and indicate public and private key are not set. */
1092511052
key->level = level % WC_ML_DSA_DRAFT;
1092611053
key->pubKeySet = 0;
@@ -10991,6 +11118,15 @@ void wc_dilithium_free(dilithium_key* key)
1099111118
/* Free the SHAKE-128/256 object. */
1099211119
wc_Shake256_Free(&key->shake);
1099311120
#endif
11121+
#endif
11122+
#ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11123+
if (key->k != NULL) {
11124+
ForceZero(key->k, key->kSz);
11125+
XFREE(key->k, key->heap, DYNAMIC_TYPE_DILITHIUM);
11126+
}
11127+
if (key->p != NULL) {
11128+
XFREE(key->p, key->heap, DYNAMIC_TYPE_DILITHIUM);
11129+
}
1099411130
#endif
1099511131
/* Ensure all private data is zeroized. */
1099611132
ForceZero(key, sizeof(*key));
@@ -11553,12 +11689,19 @@ int wc_dilithium_import_public(const byte* in, word32 inLen, dilithium_key* key)
1155311689
}
1155411690
}
1155511691

11692+
11693+
#ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11694+
if (ret == 0) {
11695+
ret = dilithium_alloc_pub_buf(key);
11696+
}
11697+
#endif
11698+
1155611699
if (ret == 0) {
1155711700
/* Copy the private key data in or copy pointer. */
11558-
#ifndef WOLFSSL_DILITHIUM_ASSIGN_KEY
11559-
XMEMCPY(key->p, in, inLen);
11560-
#else
11701+
#ifdef WOLFSSL_DILITHIUM_ASSIGN_KEY
1156111702
key->p = in;
11703+
#else
11704+
XMEMCPY(key->p, in, inLen);
1156211705
#endif
1156311706

1156411707
#ifdef WC_DILITHIUM_CACHE_PUB_VECTORS
@@ -11630,23 +11773,35 @@ static int dilithium_set_priv_key(const byte* priv, word32 privSz,
1163011773
dilithium_key* key)
1163111774
{
1163211775
int ret = 0;
11776+
int expPrivSz;
1163311777
#ifdef WC_DILITHIUM_CACHE_MATRIX_A
1163411778
const wc_dilithium_params* params = key->params;
1163511779
#endif
1163611780

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)) {
11781+
/* Validate parameters. privSz must match the expected size for the
11782+
* level set on the key. This is required so that subsequent code
11783+
* which reads via key->params stays within the (possibly dynamically
11784+
* sized) buffer. */
11785+
expPrivSz = wc_dilithium_size(key);
11786+
if (expPrivSz < 0) {
1164111787
ret = BAD_FUNC_ARG;
1164211788
}
11789+
else if (privSz != (word32)expPrivSz) {
11790+
ret = BAD_FUNC_ARG;
11791+
}
11792+
11793+
#ifdef WOLFSSL_DILITHIUM_DYNAMIC_KEYS
11794+
if (ret == 0) {
11795+
ret = dilithium_alloc_priv_buf(key);
11796+
}
11797+
#endif
1164311798

1164411799
if (ret == 0) {
1164511800
/* Copy the private key data in or copy pointer. */
11646-
#ifndef WOLFSSL_DILITHIUM_ASSIGN_KEY
11647-
XMEMCPY(key->k, priv, privSz);
11648-
#else
11801+
#ifdef WOLFSSL_DILITHIUM_ASSIGN_KEY
1164911802
key->k = priv;
11803+
#else
11804+
XMEMCPY(key->k, priv, privSz);
1165011805
#endif
1165111806
}
1165211807

wolfcrypt/src/wc_pkcs11.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2247,10 +2247,15 @@ 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+
ForceZero(mldsaKey->k, mldsaKey->kSz);
2258+
}
22542259
#endif
22552260
break;
22562261
}

wolfssl/wolfcrypt/dilithium.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,12 @@ 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+
word32 pSz; /* allocated size of p buffer */
741+
word32 kSz; /* allocated size of k buffer */
742+
#elif !defined(WOLFSSL_DILITHIUM_ASSIGN_KEY)
738743
#ifdef USE_INTEL_SPEEDUP
739744
byte p[DILITHIUM_MAX_PUB_KEY_SIZE+8];
740745
byte k[DILITHIUM_MAX_KEY_SIZE+8];

0 commit comments

Comments
 (0)