Skip to content

Commit e6cd30d

Browse files
committed
Enable dynamic AAD handling for AES-GCM by keeping the existing 4 KiB
static buffer and transparently spilling to a heap buffer that grows as needed. All GCM paths now use the correct buffer and the heap is freed on context teardown. The test suite is extended with large-AAD cases, including a 128 KiB vector and a 2 K + 4 K + 64 K chunk sequence, to exercise spill and realloc during both encryption and decryption.
1 parent f2ae77b commit e6cd30d

2 files changed

Lines changed: 196 additions & 19 deletions

File tree

wolfssl-gnutls-wrapper/src/cipher.c

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ enum {
3131
/** Maximum AES key size. */
3232
#define MAX_AES_KEY_SIZE AES_256_KEY_SIZE
3333
/** Maximum authentication data. */
34-
#define MAX_AUTH_DATA 1024
34+
#define MAX_AUTH_DATA 4096
3535
/** Maximum plaintext to encrypt for GCM */
3636
#define MAX_AES_GCM_PLAINTEXT ((1ULL << 36) - 32)
3737
/** Maximum RSA-PSS signature size */
@@ -82,10 +82,14 @@ struct wolfssl_cipher_ctx {
8282
size_t iv_size;
8383

8484
/* For GCM mode. */
85-
/** Authentication data to use. */
86-
unsigned char auth_data[MAX_AUTH_DATA];
87-
/** Size of authentication data to use. */
85+
/** Authentication data to use (static, maximum 4096 bytes). */
86+
unsigned char auth_data_static[MAX_AUTH_DATA];
87+
/** Authentication data to use (allocated dynamically). */
88+
unsigned char *auth_data_heap;
89+
/** Size of authentication data to use (static). */
8890
size_t auth_data_size;
91+
/** Size of authentication data to use (heap). */
92+
size_t auth_alloc;
8993
/** Calculated authentication tag. */
9094
unsigned char tag[GCM_TAG_SIZE];
9195
/** Size of calculated authentication tag. */
@@ -240,6 +244,7 @@ int wolfssl_cipher_init(gnutls_cipher_algorithm_t algorithm, void **_ctx,
240244
ctx->tag_set_ext = 0;
241245
ctx->algorithm = 0;
242246
ctx->data_size = 0;
247+
ctx->auth_data_heap = NULL;
243248

244249
ctx->algorithm = algorithm;
245250
ctx->mode = get_cipher_mode(algorithm);
@@ -671,12 +676,47 @@ int wolfssl_cipher_auth(void *_ctx, const void *auth_data,
671676
return GNUTLS_E_INVALID_REQUEST;
672677
}
673678

674-
/* Check authentication data will fit in cache. */
675-
if (ctx->auth_data_size + auth_size > sizeof(ctx->auth_data)) {
676-
WGW_ERROR("Auth data too big: %ld + %ld > %ld", ctx->auth_data_size,
677-
auth_size, sizeof(ctx->auth_data));
678-
return GNUTLS_E_SHORT_MEMORY_BUFFER;
679-
}
679+
/* Check authentication data will fit in the static cache,
680+
* switch to heap if it doesn't. */
681+
if (ctx->auth_data_heap == NULL &&
682+
ctx->auth_data_size + auth_size > sizeof(ctx->auth_data_static)) {
683+
WGW_LOG("Auth data too big: %ld + %ld > %ld", ctx->auth_data_size,
684+
auth_size, sizeof(ctx->auth_data_static));
685+
WGW_LOG("Switching to dynamic allocation");
686+
687+
size_t new_sz = sizeof(ctx->auth_data_static);
688+
while (new_sz < ctx->auth_data_size + auth_size)
689+
new_sz *= 2;
690+
691+
ctx->auth_data_heap = gnutls_malloc(new_sz);
692+
if (!ctx->auth_data_heap) {
693+
WGW_ERROR("gnutls_malloc failed for auth_data_heap");
694+
return GNUTLS_E_MEMORY_ERROR;
695+
}
696+
ctx->auth_alloc = new_sz;
697+
XMEMCPY(ctx->auth_data_heap, ctx->auth_data_static,
698+
ctx->auth_data_size);
699+
WGW_LOG("Spilled AAD to heap (%zu bytes)", new_sz);
700+
}
701+
702+
/* We are already storing in the heap, we check
703+
* if we need to grow it. */
704+
if (ctx->auth_data_heap &&
705+
ctx->auth_data_size + auth_size > ctx->auth_alloc) {
706+
WGW_LOG("New auth data too big, reallocating");
707+
size_t new_sz = ctx->auth_alloc;
708+
while (new_sz < ctx->auth_data_size + auth_size)
709+
new_sz *= 2;
710+
711+
unsigned char *p = gnutls_realloc(ctx->auth_data_heap, new_sz);
712+
if (!p) {
713+
WGW_ERROR("gnutls_realloc failed for auth_data_heap");
714+
return GNUTLS_E_MEMORY_ERROR;
715+
}
716+
ctx->auth_data_heap = p;
717+
ctx->auth_alloc = new_sz;
718+
WGW_LOG("Grew AAD heap to %zu bytes", new_sz);
719+
}
680720

681721
/* Streaming must be a multiple of block size except for last. */
682722
if ((ctx->auth_data_size % AES_BLOCK_SIZE) != 0) {
@@ -685,8 +725,11 @@ int wolfssl_cipher_auth(void *_ctx, const void *auth_data,
685725
}
686726

687727
/* Store AAD for later use in encrypt/decrypt operations. */
688-
XMEMCPY(ctx->auth_data + ctx->auth_data_size, auth_data, auth_size);
689-
ctx->auth_data_size += auth_size;
728+
unsigned char *dst = ctx->auth_data_heap ?
729+
ctx->auth_data_heap : ctx->auth_data_static;
730+
731+
XMEMCPY(dst + ctx->auth_data_size, auth_data, auth_size);
732+
ctx->auth_data_size += auth_size;
690733

691734
WGW_LOG("AAD added successfully");
692735
return 0;
@@ -751,7 +794,7 @@ int wolfssl_cipher_encrypt(void *_ctx, const void *src, size_t src_size,
751794
unsigned char* ptr;
752795
unsigned char* encr;
753796

754-
WGW_LOG("Caching platintext");
797+
WGW_LOG("Caching plaintext");
755798

756799
ctx->enc = 1;
757800

@@ -782,11 +825,13 @@ int wolfssl_cipher_encrypt(void *_ctx, const void *src, size_t src_size,
782825
WGW_ERROR("realloc of gmac data failed");
783826
return GNUTLS_E_INVALID_REQUEST;
784827
}
828+
unsigned char *aad = ctx->auth_data_heap ?
829+
ctx->auth_data_heap : ctx->auth_data_static;
785830

786831
/* Do encryption with the data we have. */
787832
ret = wc_AesGcmEncrypt(&ctx->cipher.aes_ctx, encr,
788833
ctx->data, ctx->data_size, ctx->iv, ctx->iv_size,
789-
ctx->tag, ctx->tag_size, ctx->auth_data, ctx->auth_data_size);
834+
ctx->tag, ctx->tag_size, aad, ctx->auth_data_size);
790835
if (ret != 0) {
791836
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
792837
gnutls_free(encr);
@@ -894,7 +939,7 @@ int wolfssl_cipher_decrypt(void *_ctx, const void *src, size_t src_size,
894939
unsigned char* ptr;
895940
unsigned char* decr;
896941

897-
WGW_LOG("Caching platintext");
942+
WGW_LOG("Caching plaintext");
898943

899944
ctx->enc = 1;
900945

@@ -922,14 +967,18 @@ int wolfssl_cipher_decrypt(void *_ctx, const void *src, size_t src_size,
922967
}
923968

924969
WGW_LOG("wc_AesGcmDecrypt");
970+
971+
unsigned char *aad = ctx->auth_data_heap ?
972+
ctx->auth_data_heap : ctx->auth_data_static;
973+
925974
/* If caller hasn't set tag then we are creating it. */
926975
if (!ctx->tag_set_ext) {
927976
/* Encrypt the ciphertext to get the plaintext.
928977
* Tag will have been created on plaintext which is of no use.
929978
*/
930979
ret = wc_AesGcmEncrypt(&ctx->cipher.aes_ctx, decr, ctx->data,
931980
ctx->data_size, ctx->iv, ctx->iv_size,
932-
ctx->tag, ctx->tag_size, ctx->auth_data, ctx->auth_data_size);
981+
ctx->tag, ctx->tag_size, aad, ctx->auth_data_size);
933982
if (ret != 0) {
934983
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
935984
gnutls_free(decr);
@@ -938,7 +987,7 @@ int wolfssl_cipher_decrypt(void *_ctx, const void *src, size_t src_size,
938987
/* Encrypt the plaintext to create the tag. */
939988
ret = wc_AesGcmEncrypt(&ctx->cipher.aes_ctx, decr, decr,
940989
ctx->data_size, ctx->iv, ctx->iv_size,
941-
ctx->tag, ctx->tag_size, ctx->auth_data, ctx->auth_data_size);
990+
ctx->tag, ctx->tag_size, aad, ctx->auth_data_size);
942991
if (ret != 0) {
943992
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
944993
gnutls_free(decr);
@@ -950,7 +999,7 @@ int wolfssl_cipher_decrypt(void *_ctx, const void *src, size_t src_size,
950999
/* Do decryption with cipehtext, IV, authentication data and tag. */
9511000
ret = wc_AesGcmDecrypt(&ctx->cipher.aes_ctx, decr,
9521001
ctx->data, ctx->data_size, ctx->iv, ctx->iv_size,
953-
ctx->tag, ctx->tag_size, ctx->auth_data, ctx->auth_data_size);
1002+
ctx->tag, ctx->tag_size, aad, ctx->auth_data_size);
9541003
if (ret != 0) {
9551004
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
9561005
gnutls_free(decr);
@@ -1044,9 +1093,12 @@ void wolfssl_cipher_tag(void *_ctx, void *tag, size_t tag_size)
10441093
if (ctx->mode == GCM) {
10451094
WGW_LOG("wc_AesGcmEncrypt");
10461095

1096+
unsigned char *aad = ctx->auth_data_heap ?
1097+
ctx->auth_data_heap : ctx->auth_data_static;
1098+
10471099
/* Do authentication with no plaintext. */
10481100
ret = wc_AesGcmEncrypt(&ctx->cipher.aes_ctx, NULL, NULL, 0, ctx->iv,
1049-
ctx->iv_size, ctx->tag, ctx->tag_size, ctx->auth_data,
1101+
ctx->iv_size, ctx->tag, ctx->tag_size, aad,
10501102
ctx->auth_data_size);
10511103
if (ret != 0) {
10521104
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
@@ -1264,6 +1316,9 @@ void wolfssl_cipher_deinit(void *_ctx)
12641316
ctx->initialized = 0;
12651317
ctx->enc_initialized = 0;
12661318
ctx->dec_initialized = 0;
1319+
if (ctx->auth_data_heap) {
1320+
gnutls_free(ctx->auth_data_heap);
1321+
}
12671322
}
12681323

12691324
gnutls_free(ctx);

wolfssl-gnutls-wrapper/tests/test_aesgcm.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,122 @@ static int test_aesgcm_aead(gnutls_cipher_algorithm_t cipher,
266266
return 0;
267267
}
268268

269+
/* ----------- BIG-AAD STRESS TEST (≥ 100 kB) ---------------- */
270+
static int test_aesgcm_big_aad(size_t aad_len)
271+
{
272+
int ret;
273+
gnutls_cipher_hd_t hd;
274+
unsigned char *aad = NULL;
275+
unsigned char pt[16] = {0}; /* 1-block dummy plaintext */
276+
unsigned char tag[16] = {0};
277+
unsigned char iv[12] = {0}; /* 96-bit nonce */
278+
279+
/* 128-bit zero key */
280+
gnutls_datum_t key = { (unsigned char *)key_128, sizeof(key_128) };
281+
282+
/* allocate and fill the big AAD */
283+
aad = gnutls_malloc(aad_len);
284+
if (!aad) { perror("malloc"); return 1; }
285+
for (size_t i = 0; i < aad_len; i++)
286+
aad[i] = (unsigned char)(i & 0xFF); /* deterministic pattern */
287+
288+
/* init AES-128-GCM context */
289+
ret = gnutls_cipher_init(&hd, GNUTLS_CIPHER_AES_128_GCM, &key,
290+
&(gnutls_datum_t){ iv, sizeof(iv) });
291+
if (ret < 0) { print_gnutls_error("cipher_init", ret); goto out; }
292+
293+
/* supply the giant AAD in a single call */
294+
ret = gnutls_cipher_add_auth(hd, aad, aad_len);
295+
if (ret < 0) { print_gnutls_error("add_auth", ret); goto out_free; }
296+
297+
/* encrypt the dummy plaintext (content is irrelevant here) */
298+
ret = gnutls_cipher_encrypt(hd, pt, sizeof(pt));
299+
if (ret < 0) { print_gnutls_error("encrypt", ret); goto out_free; }
300+
301+
/* force tag generation – this calls wc_AesGcmEncrypt/Decrypt under the hood */
302+
ret = gnutls_cipher_tag(hd, tag, sizeof(tag));
303+
if (ret < 0) { print_gnutls_error("tag", ret); goto out_free; }
304+
305+
printf("AES-GCM with %zu-byte AAD succeeded\n", aad_len);
306+
ret = 0;
307+
308+
out_free:
309+
gnutls_cipher_deinit(hd);
310+
out:
311+
gnutls_free(aad);
312+
return ret;
313+
}
314+
315+
/* --- encrypt-then-decrypt with huge AAD (spills + realloc both ways) ---- */
316+
static int test_aesgcm_big_aad_chunks_encdec(void)
317+
{
318+
int ret;
319+
gnutls_cipher_hd_t enc_hd = NULL, dec_hd = NULL;
320+
unsigned char iv[12] = {0};
321+
unsigned char pt[16] = {0}; /* dummy 16-byte plaintext */
322+
unsigned char ct[16] = {0};
323+
unsigned char tag[16] = {0};
324+
gnutls_datum_t key = { (unsigned char *)key_128, sizeof(key_128) };
325+
326+
/* three AAD chunks: 2 KiB (stay static) + 4 KiB (spill) + 64 KiB (realloc) */
327+
const size_t c1 = 2048, c2 = 4096, c3 = 65536;
328+
unsigned char *aad1 = gnutls_malloc(c1);
329+
unsigned char *aad2 = gnutls_malloc(c2);
330+
unsigned char *aad3 = gnutls_malloc(c3);
331+
if (!aad1 || !aad2 || !aad3) { perror("malloc"); return 1; }
332+
memset(aad1, 0x11, c1); memset(aad2, 0x22, c2); memset(aad3, 0x33, c3);
333+
334+
/* ---------- ENCRYPT ---------- */
335+
ret = gnutls_cipher_init(&enc_hd, GNUTLS_CIPHER_AES_128_GCM,
336+
&key, &(gnutls_datum_t){ iv, sizeof(iv) });
337+
if (ret < 0) { print_gnutls_error("enc init", ret); goto out; }
338+
339+
if ((ret = gnutls_cipher_add_auth(enc_hd, aad1, c1)) < 0 ||
340+
(ret = gnutls_cipher_add_auth(enc_hd, aad2, c2)) < 0 ||
341+
(ret = gnutls_cipher_add_auth(enc_hd, aad3, c3)) < 0) {
342+
print_gnutls_error("enc add_auth", ret); goto out;
343+
}
344+
345+
if ((ret = gnutls_cipher_encrypt(enc_hd, pt, sizeof(pt))) < 0) {
346+
print_gnutls_error("encrypt", ret); goto out;
347+
}
348+
memcpy(ct, pt, sizeof(ct)); /* ciphertext now in ct */
349+
350+
if ((ret = gnutls_cipher_tag(enc_hd, tag, sizeof(tag))) < 0) {
351+
print_gnutls_error("get tag", ret); goto out;
352+
}
353+
gnutls_cipher_deinit(enc_hd); enc_hd = NULL;
354+
355+
/* ---------- DECRYPT (same huge AAD pattern) ---------- */
356+
ret = gnutls_cipher_init(&dec_hd, GNUTLS_CIPHER_AES_128_GCM,
357+
&key, &(gnutls_datum_t){ iv, sizeof(iv) });
358+
if (ret < 0) { print_gnutls_error("dec init", ret); goto out; }
359+
360+
if ((ret = gnutls_cipher_add_auth(dec_hd, aad1, c1)) < 0 ||
361+
(ret = gnutls_cipher_add_auth(dec_hd, aad2, c2)) < 0 ||
362+
(ret = gnutls_cipher_add_auth(dec_hd, aad3, c3)) < 0) {
363+
print_gnutls_error("dec add_auth", ret); goto out;
364+
}
365+
366+
if ((ret = gnutls_cipher_tag(dec_hd, tag, sizeof(tag))) < 0) {
367+
print_gnutls_error("set tag", ret); goto out;
368+
}
369+
370+
if ((ret = gnutls_cipher_decrypt(dec_hd, ct, sizeof(ct))) < 0) {
371+
print_gnutls_error("decrypt", ret); goto out;
372+
}
373+
374+
gnutls_cipher_deinit(dec_hd); dec_hd = NULL;
375+
printf("Encrypt + decrypt spill/realloc test succeeded\n");
376+
ret = 0;
377+
378+
out:
379+
if (enc_hd) gnutls_cipher_deinit(enc_hd);
380+
if (dec_hd) gnutls_cipher_deinit(dec_hd);
381+
gnutls_free(aad1); gnutls_free(aad2); gnutls_free(aad3);
382+
return ret;
383+
}
384+
269385
int main(void) {
270386
int ret;
271387

@@ -300,6 +416,12 @@ int main(void) {
300416
ret = test_aesgcm_aead(GNUTLS_CIPHER_AES_256_GCM, key_256,
301417
sizeof(key_256), ciphertext_256_data, sizeof(ciphertext_256_data));
302418
}
419+
if (ret == 0) {
420+
ret = test_aesgcm_big_aad(131072);
421+
}
422+
if (ret == 0) {
423+
ret = test_aesgcm_big_aad_chunks_encdec();
424+
}
303425
if (ret == 0) {
304426
printf("Test completed.\n");
305427
}

0 commit comments

Comments
 (0)