Skip to content

Commit 385c42d

Browse files
committed
Rewrite AES-GCM stream handling for FIPS
1 parent c9279b6 commit 385c42d

2 files changed

Lines changed: 375 additions & 59 deletions

File tree

src/wp_aes_aead.c

Lines changed: 114 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@
3232

3333
#if defined(WP_HAVE_AESGCM) || defined(WP_HAVE_AESCCM)
3434

35+
/* For non-streaming AES-GCM, we have to spool all previous updates.
36+
* This is the number of extra bytes we add when allocating the internal
37+
* buffer used to spool the stream input. This way we if there is space
38+
* we can use the existing buffer, reducing the number of full realloc + copy
39+
* operations we need to do. Increase this number for better performance and
40+
* more memory usage, decrease for worse performance but less overhead */
41+
#ifndef WP_AES_GCM_EXTRA_BUF_LEN
42+
#define WP_AES_GCM_EXTRA_BUF_LEN 128
43+
#endif
44+
3545
/**
3646
* Authenticated Encryption with Associated Data structure.
3747
*/
@@ -94,10 +104,14 @@ typedef struct wp_AeadCtx {
94104
/** CCM is not streaming and needs to cache AAD data. */
95105
unsigned char* aad;
96106
#if defined(WP_HAVE_AESGCM) && !defined(WOLFSSL_AESGCM_STREAM)
97-
/** Length of AAD data cached. */
107+
/** Length of data cached. */
98108
size_t inLen;
99109
/** CCM is not streaming and needs to cache AAD data. */
100110
unsigned char* in;
111+
/* Total buffer size */
112+
size_t bufSize;
113+
/* Original IV */
114+
unsigned char origIv[AES_BLOCK_SIZE];
101115
#endif
102116
} wp_AeadCtx;
103117

@@ -310,15 +324,24 @@ static int wp_aead_cache_in(wp_AeadCtx *ctx, const unsigned char *in,
310324
unsigned char *p;
311325

312326
if (inLen > 0) {
313-
p = (unsigned char*)OPENSSL_realloc(ctx->in, ctx->inLen + inLen);
314-
if (p == NULL) {
315-
ok = 0;
316-
}
317-
if (ok) {
318-
ctx->in = p;
327+
if (inLen < (ctx->bufSize - ctx->inLen)) {
328+
/* We can fit this new data into the extra space, dont realloc */
319329
XMEMCPY(ctx->in + ctx->inLen, in, inLen);
320330
ctx->inLen += inLen;
321331
}
332+
else {
333+
p = (unsigned char*)OPENSSL_realloc(ctx->in,
334+
ctx->inLen + inLen + WP_AES_GCM_EXTRA_BUF_LEN);
335+
if (p == NULL) {
336+
ok = 0;
337+
}
338+
if (ok) {
339+
ctx->bufSize = ctx->inLen + inLen + WP_AES_GCM_EXTRA_BUF_LEN;
340+
ctx->in = p;
341+
XMEMCPY(ctx->in + ctx->inLen, in, inLen);
342+
ctx->inLen += inLen;
343+
}
344+
}
322345
}
323346

324347
WOLFPROV_LEAVE(WP_LOG_CIPHER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok);
@@ -823,8 +846,6 @@ static int wp_aesgcm_get_rand_iv(wp_AeadCtx* ctx, unsigned char* out,
823846
if (rc != 0) {
824847
ok = 0;
825848
}
826-
#else
827-
XMEMCPY(ctx->iv, ctx->aes.reg, ctx->ivLen);
828849
#endif
829850
}
830851
if (ok) {
@@ -833,6 +854,9 @@ static int wp_aesgcm_get_rand_iv(wp_AeadCtx* ctx, unsigned char* out,
833854
olen = ctx->ivLen;
834855
}
835856
XMEMCPY(out, ctx->iv + ctx->ivLen - olen, olen);
857+
#ifndef WOLFSSL_AESGCM_STREAM
858+
XMEMCPY(ctx->origIv, ctx->iv, ctx->ivLen);
859+
#endif
836860
if (inc) {
837861
int i;
838862
unsigned char* p = ctx->iv + ctx->ivLen - 8;
@@ -869,6 +893,9 @@ static int wp_aesgcm_set_rand_iv(wp_AeadCtx *ctx, unsigned char *in,
869893
ok = 0;
870894
}
871895
else {
896+
#ifndef WOLFSSL_AESGCM_STREAM
897+
XMEMCPY(ctx->origIv, ctx->iv, ctx->ivLen);
898+
#endif
872899
XMEMCPY(ctx->iv + ctx->ivLen - inLen, in, inLen);
873900
ctx->ivState = IV_STATE_COPIED;
874901
}
@@ -1312,65 +1339,96 @@ static int wp_aesgcm_stream_final(wp_AeadCtx *ctx, unsigned char *out,
13121339
* @param [out] outLen Length of data in output buffer.
13131340
* @param [in] in Data to be encrypted/decrypted.
13141341
* @param [in] inLen Length of data to be encrypted/decrypted.
1342+
* @param [in] offset Offset into the spooled data buffer.
1343+
* @param [in] done This is a final operation.
1344+
*
13151345
* @return 1 on success.
13161346
* @return 0 on failure.
13171347
*/
1318-
static int wp_aesgcm_encdec(wp_AeadCtx *ctx, unsigned char *out, size_t* outLen)
1348+
static int wp_aesgcm_encdec(wp_AeadCtx *ctx, unsigned char *out, size_t* outLen,
1349+
size_t offset, int done)
13191350
{
13201351
int ok = 1;
13211352
int rc;
1353+
unsigned char *tmp = NULL;
1354+
byte *iv = NULL;
13221355

13231356
if (ctx->tagLen == UNINITIALISED_SIZET) {
13241357
ctx->tagLen = EVP_GCM_TLS_TAG_LEN;
13251358
}
13261359

1327-
if (ctx->enc) {
1328-
if (!ctx->ivSet) {
1329-
rc = wc_AesGcmSetExtIV(&ctx->aes, ctx->iv, (word32)ctx->ivLen);
1330-
if (rc != 0) {
1360+
if (ctx->inLen > offset || (ctx->tagAvail == 0 && done)) {
1361+
/* Prepare a temp buffer to store all the output */
1362+
if (ctx->inLen > 0) {
1363+
tmp = OPENSSL_zalloc(ctx->inLen);
1364+
if (tmp == NULL) {
13311365
ok = 0;
13321366
}
13331367
}
1368+
/* Once loaded, always use original IV */
1369+
iv = ctx->iv;
1370+
if (ctx->ivState == IV_STATE_COPIED) {
1371+
iv = ctx->origIv;
1372+
}
13341373
if (ok) {
1335-
ctx->ivSet = 1;
1336-
/* IV coming out in this call. */
1337-
rc = wc_AesGcmEncrypt_ex(&ctx->aes, out, ctx->in,
1338-
(word32)ctx->inLen, ctx->iv, (word32)ctx->ivLen, ctx->buf,
1339-
(word32)ctx->tagLen, ctx->aad, (word32)ctx->aadLen);
1374+
rc = wc_AesGcmSetExtIV(&ctx->aes, iv, (word32)ctx->ivLen);
13401375
if (rc != 0) {
13411376
ok = 0;
13421377
}
1343-
if (ok) {
1344-
ctx->tagAvail = 1;
1378+
1379+
if (ok && ctx->ivState == IV_STATE_BUFFERED) {
1380+
ctx->ivState = IV_STATE_COPIED;
1381+
XMEMCPY(ctx->origIv, ctx->iv, ctx->ivLen);
13451382
}
13461383
}
1347-
}
1348-
else {
1349-
rc = wc_AesGcmDecrypt(&ctx->aes, out, ctx->in, (word32)ctx->inLen,
1350-
ctx->iv, (word32)ctx->ivLen, ctx->buf, (word32)ctx->tagLen,
1351-
ctx->aad, (word32)ctx->aadLen);
1352-
if (rc == AES_GCM_AUTH_E) {
1353-
ctx->authErr = 1;
1354-
rc = 0;
1384+
if (ctx->enc) {
1385+
if (ok) {
1386+
ctx->ivSet = 1;
1387+
/* IV coming out in this call. */
1388+
rc = wc_AesGcmEncrypt_ex(&ctx->aes, tmp, ctx->in,
1389+
(word32)ctx->inLen, iv, (word32)ctx->ivLen, ctx->buf,
1390+
(word32)ctx->tagLen, ctx->aad, (word32)ctx->aadLen);
1391+
if (rc != 0) {
1392+
ok = 0;
1393+
}
1394+
if (ok) {
1395+
ctx->tagAvail = 1;
1396+
}
1397+
}
13551398
}
1356-
if (rc != 0) {
1357-
ok = 0;
1399+
else {
1400+
/* Only the most recent auth err matters */
1401+
ctx->authErr = 0;
1402+
rc = wc_AesGcmDecrypt(&ctx->aes, tmp, ctx->in, (word32)ctx->inLen,
1403+
iv, (word32)ctx->ivLen, ctx->buf, (word32)ctx->tagLen,
1404+
ctx->aad, (word32)ctx->aadLen);
1405+
if (rc == AES_GCM_AUTH_E) {
1406+
ctx->authErr = 1;
1407+
rc = 0;
1408+
}
1409+
if (rc != 0) {
1410+
ok = 0;
1411+
}
13581412
}
1413+
/* Copy out relevant portion of output */
13591414
if (ok) {
1360-
XMEMCPY(ctx->iv, ctx->aes.reg, ctx->ivLen);
1415+
XMEMCPY(out, tmp + offset, (ctx->inLen - offset));
1416+
*outLen = (ctx->inLen - offset);
13611417
}
1418+
OPENSSL_free(tmp);
13621419
}
1363-
if (ok) {
1364-
*outLen = ctx->inLen;
1420+
else {
1421+
*outLen = 0;
1422+
}
1423+
if (done) {
1424+
OPENSSL_free(ctx->aad);
1425+
ctx->aad = NULL;
1426+
ctx->aadLen = 0;
1427+
ctx->aadSet = 0;
1428+
OPENSSL_free(ctx->in);
1429+
ctx->in = NULL;
1430+
ctx->inLen = 0;
13651431
}
1366-
1367-
OPENSSL_free(ctx->aad);
1368-
ctx->aad = NULL;
1369-
ctx->aadLen = 0;
1370-
ctx->aadSet = 0;
1371-
OPENSSL_free(ctx->in);
1372-
ctx->in = NULL;
1373-
ctx->inLen = 0;
13741432

13751433
WOLFPROV_LEAVE(WP_LOG_CIPHER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok);
13761434
return ok;
@@ -1392,6 +1450,8 @@ static int wp_aesgcm_stream_update(wp_AeadCtx *ctx, unsigned char *out,
13921450
size_t *outLen, size_t outSize, const unsigned char *in, size_t inLen)
13931451
{
13941452
int ok = 1;
1453+
int process = 0;
1454+
size_t curLen = 0;
13951455

13961456
if (ctx->tlsAadLen != UNINITIALISED_SIZET) {
13971457
ok = wp_aesgcm_tls_cipher(ctx, out, outLen, in, inLen);
@@ -1414,12 +1474,22 @@ static int wp_aesgcm_stream_update(wp_AeadCtx *ctx, unsigned char *out,
14141474
ok = 0;
14151475
}
14161476
else if (inLen > 0) {
1477+
curLen = ctx->inLen;
14171478
if (!wp_aead_cache_in(ctx, in, inLen)) {
14181479
ok = 0;
14191480
}
1481+
else {
1482+
process = 1;
1483+
}
14201484
}
14211485

1422-
*outLen = oLen;
1486+
/* If there is data to process, do it now */
1487+
if (process) {
1488+
ok = wp_aesgcm_encdec(ctx, out, outLen, curLen, 0);
1489+
}
1490+
else {
1491+
*outLen = oLen;
1492+
}
14231493
}
14241494

14251495
WOLFPROV_LEAVE(WP_LOG_CIPHER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok);
@@ -1449,7 +1519,7 @@ static int wp_aesgcm_stream_final(wp_AeadCtx *ctx, unsigned char *out,
14491519
ok = 0;
14501520
}
14511521
else {
1452-
ok = wp_aesgcm_encdec(ctx, out, outLen);
1522+
ok = wp_aesgcm_encdec(ctx, out, outLen, ctx->inLen, 1);
14531523
ctx->ivSet = 0;
14541524
}
14551525

0 commit comments

Comments
 (0)