Skip to content

Commit 26827e9

Browse files
committed
- fix gcm tls fixed-iv length check so it properly rejects sizes larger than the iv buffer
- validate aead tag length against algorithm's allowed sizes (gcm/ccm)
1 parent 08bd5b1 commit 26827e9

4 files changed

Lines changed: 305 additions & 3 deletions

File tree

src/wp_aes_aead.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,47 @@ static int wp_aead_get_ctx_params(wp_AeadCtx* ctx, OSSL_PARAM params[])
458458
return ok;
459459
}
460460

461+
/**
462+
* Check whether a tag length is one of the algorithm's allowed sizes.
463+
*
464+
* GCM tags must be 4, 8, 12, 13, 14, 15 or 16 bytes (NIST SP 800-38D).
465+
* CCM tags must be 4, 6, 8, 10, 12, 14 or 16 bytes (NIST SP 800-38C /
466+
* RFC 3610).
467+
*
468+
* Note: for GCM this is intentionally stricter than OpenSSL's default
469+
* provider, which accepts any non-zero tag length up to 16 on
470+
* EVP_CTRL_AEAD_SET_TAG (including on the decrypt/verify path). wolfProvider
471+
* enforces the NIST SP 800-38D set on both encrypt and decrypt, so tag
472+
* lengths such as 5, 6, 7, 9, 10 or 11 that stock OpenSSL would accept are
473+
* rejected here.
474+
*
475+
* @param [in] mode Cipher mode: EVP_CIPH_GCM_MODE or EVP_CIPH_CCM_MODE.
476+
* @param [in] sz Tag length in bytes to check.
477+
* @return 1 if sz is an allowed length for mode.
478+
* @return 0 otherwise.
479+
*/
480+
static int wp_aead_tag_len_valid(int mode, size_t sz)
481+
{
482+
int ok;
483+
484+
WOLFPROV_ENTER(WP_LOG_COMP_AES, "wp_aead_tag_len_valid");
485+
486+
if (mode == EVP_CIPH_GCM_MODE) {
487+
ok = (sz == 4) || (sz == 8) || (sz == 12) || (sz == 13) ||
488+
(sz == 14) || (sz == 15) || (sz == 16);
489+
}
490+
else if (mode == EVP_CIPH_CCM_MODE) {
491+
ok = (sz == 4) || (sz == 6) || (sz == 8) || (sz == 10) ||
492+
(sz == 12) || (sz == 14) || (sz == 16);
493+
}
494+
else {
495+
ok = 0;
496+
}
497+
498+
WOLFPROV_LEAVE(WP_LOG_COMP_AES, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok);
499+
return ok;
500+
}
501+
461502
/**
462503
* Set the AEAD tag from the parameters.
463504
*
@@ -488,6 +529,9 @@ static int wp_aead_set_param_tag(wp_AeadCtx* ctx,
488529
if (ok && ((sz == 0) || ((p->data != NULL) && ctx->enc))) {
489530
ok = 0;
490531
}
532+
if (ok && !wp_aead_tag_len_valid(ctx->mode, sz)) {
533+
ok = 0;
534+
}
491535
if (ok) {
492536
ctx->tagLen = sz;
493537
}
@@ -959,9 +1003,10 @@ static int wp_aesgcm_tls_iv_set_fixed(wp_AeadCtx* ctx, unsigned char* iv,
9591003
}
9601004
else {
9611005
/* Fixed field must be at least 4 bytes and invocation field at least 8
962-
*/
963-
if ((len < EVP_GCM_TLS_FIXED_IV_LEN) ||
964-
(ctx->ivLen - (int)len) < EVP_GCM_TLS_EXPLICIT_IV_LEN) {
1006+
* bytes */
1007+
if ((len < EVP_GCM_TLS_FIXED_IV_LEN) || (len > ctx->ivLen) ||
1008+
(len > sizeof(ctx->iv)) ||
1009+
(ctx->ivLen - len) < EVP_GCM_TLS_EXPLICIT_IV_LEN) {
9651010
return 0;
9661011
}
9671012
if (ctx->enc) {

test/test_aestag.c

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,173 @@ int test_aes_gcm_bad_tag(void *data)
14161416
return err;
14171417
}
14181418

1419+
static int test_aes_gcm_tls_iv_fixed_oversized_helper(OSSL_LIB_CTX *libCtx,
1420+
const char *cipherName, int keyLen)
1421+
{
1422+
int err;
1423+
EVP_CIPHER *cipher = NULL;
1424+
EVP_CIPHER_CTX *ctx = NULL;
1425+
unsigned char key[32];
1426+
/* ivLen (12, the GCM default) + EVP_GCM_TLS_EXPLICIT_IV_LEN (8) = 20: */
1427+
unsigned char iv[20];
1428+
1429+
memset(key, 0xCC, keyLen);
1430+
memset(iv, 0xDD, sizeof(iv));
1431+
1432+
cipher = EVP_CIPHER_fetch(libCtx, cipherName, "");
1433+
err = cipher == NULL;
1434+
1435+
if (err == 0) {
1436+
ctx = EVP_CIPHER_CTX_new();
1437+
err = ctx == NULL;
1438+
}
1439+
if (err == 0) {
1440+
err = EVP_DecryptInit_ex(ctx, cipher, NULL, key, NULL) != 1;
1441+
}
1442+
if (err == 0) {
1443+
err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED,
1444+
EVP_GCM_TLS_FIXED_IV_LEN, iv) != 1;
1445+
}
1446+
EVP_CIPHER_CTX_free(ctx);
1447+
ctx = NULL;
1448+
1449+
if (err == 0) {
1450+
ctx = EVP_CIPHER_CTX_new();
1451+
err = ctx == NULL;
1452+
}
1453+
if (err == 0) {
1454+
err = EVP_DecryptInit_ex(ctx, cipher, NULL, key, NULL) != 1;
1455+
}
1456+
if (err == 0) {
1457+
int ivLen = EVP_GCM_TLS_FIXED_IV_LEN + EVP_GCM_TLS_EXPLICIT_IV_LEN;
1458+
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED,
1459+
ivLen, iv) == 1) {
1460+
PRINT_ERR_MSG("%s: EVP_CTRL_GCM_SET_IV_FIXED incorrectly "
1461+
"accepted a fixed IV length (%d) equal to the "
1462+
"IV length, leaving no room for the explicit "
1463+
"field", cipherName, ivLen);
1464+
err = 1;
1465+
}
1466+
}
1467+
EVP_CIPHER_CTX_free(ctx);
1468+
ctx = NULL;
1469+
1470+
/* Oversized fixed len (> ctx->ivLen, default 12) must be rejected. */
1471+
if (err == 0) {
1472+
ctx = EVP_CIPHER_CTX_new();
1473+
err = ctx == NULL;
1474+
}
1475+
if (err == 0) {
1476+
err = EVP_DecryptInit_ex(ctx, cipher, NULL, key, NULL) != 1;
1477+
}
1478+
if (err == 0) {
1479+
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED,
1480+
(int)sizeof(iv), iv) == 1) {
1481+
PRINT_ERR_MSG("%s: EVP_CTRL_GCM_SET_IV_FIXED incorrectly "
1482+
"accepted a fixed IV length (%d) larger than "
1483+
"the IV length", cipherName, (int)sizeof(iv));
1484+
err = 1;
1485+
}
1486+
}
1487+
1488+
EVP_CIPHER_CTX_free(ctx);
1489+
EVP_CIPHER_free(cipher);
1490+
return err;
1491+
}
1492+
1493+
int test_aes_gcm_tls_iv_fixed_oversized(void *data)
1494+
{
1495+
int err;
1496+
1497+
(void)data;
1498+
1499+
PRINT_MSG("AES-128-GCM TLS1 fixed-IV length boundary rejection");
1500+
err = test_aes_gcm_tls_iv_fixed_oversized_helper(wpLibCtx,
1501+
"AES-128-GCM", 16);
1502+
if (err == 0) {
1503+
PRINT_MSG("AES-256-GCM TLS1 fixed-IV length boundary rejection");
1504+
err = test_aes_gcm_tls_iv_fixed_oversized_helper(wpLibCtx,
1505+
"AES-256-GCM", 32);
1506+
}
1507+
1508+
return err;
1509+
}
1510+
1511+
/*
1512+
* Test that GCM tag lengths are validated against the NIST SP 800-38D table.
1513+
*/
1514+
static int test_aes_gcm_tag_len_helper(OSSL_LIB_CTX *libCtx,
1515+
const char *cipherName)
1516+
{
1517+
int err;
1518+
int i;
1519+
EVP_CIPHER *cipher = NULL;
1520+
EVP_CIPHER_CTX *ctx = NULL;
1521+
unsigned char tag[16];
1522+
static const int invalidLen[] = { 1, 2, 3, 5, 6, 7 };
1523+
static const int validLen[] = { 4, 8, 12, 13, 14, 15, 16 };
1524+
int numInvalid = (int)(sizeof(invalidLen) / sizeof(invalidLen[0]));
1525+
int numValid = (int)(sizeof(validLen) / sizeof(validLen[0]));
1526+
1527+
memset(tag, 0, sizeof(tag));
1528+
1529+
cipher = EVP_CIPHER_fetch(libCtx, cipherName, "");
1530+
err = cipher == NULL;
1531+
1532+
for (i = 0; (err == 0) && (i < numInvalid); i++) {
1533+
ctx = EVP_CIPHER_CTX_new();
1534+
err = ctx == NULL;
1535+
if (err == 0) {
1536+
err = EVP_DecryptInit(ctx, cipher, NULL, NULL) != 1;
1537+
}
1538+
if ((err == 0) &&
1539+
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, invalidLen[i],
1540+
tag) == 1) {
1541+
PRINT_ERR_MSG("%s: EVP_CTRL_AEAD_SET_TAG incorrectly accepted "
1542+
"tag length %d", cipherName, invalidLen[i]);
1543+
err = 1;
1544+
}
1545+
EVP_CIPHER_CTX_free(ctx);
1546+
ctx = NULL;
1547+
}
1548+
1549+
for (i = 0; (err == 0) && (i < numValid); i++) {
1550+
ctx = EVP_CIPHER_CTX_new();
1551+
err = ctx == NULL;
1552+
if (err == 0) {
1553+
err = EVP_DecryptInit(ctx, cipher, NULL, NULL) != 1;
1554+
}
1555+
if ((err == 0) &&
1556+
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, validLen[i],
1557+
tag) != 1) {
1558+
PRINT_ERR_MSG("%s: EVP_CTRL_AEAD_SET_TAG incorrectly rejected "
1559+
"tag length %d", cipherName, validLen[i]);
1560+
err = 1;
1561+
}
1562+
EVP_CIPHER_CTX_free(ctx);
1563+
ctx = NULL;
1564+
}
1565+
1566+
EVP_CIPHER_free(cipher);
1567+
return err;
1568+
}
1569+
1570+
int test_aes_gcm_tag_len_undersized(void *data)
1571+
{
1572+
int err;
1573+
1574+
(void)data;
1575+
1576+
PRINT_MSG("AES-128-GCM tag length validation");
1577+
err = test_aes_gcm_tag_len_helper(wpLibCtx, "AES-128-GCM");
1578+
if (err == 0) {
1579+
PRINT_MSG("AES-256-GCM tag length validation");
1580+
err = test_aes_gcm_tag_len_helper(wpLibCtx, "AES-256-GCM");
1581+
}
1582+
1583+
return err;
1584+
}
1585+
14191586
#endif /* WP_HAVE_AESGCM */
14201587

14211588
/******************************************************************************/
@@ -1601,5 +1768,89 @@ int test_aes_ccm_bad_tag(void *data)
16011768
return err;
16021769
}
16031770

1771+
/*
1772+
* Test that CCM tag lengths are validated against the NIST SP 800-38C.
1773+
*/
1774+
static int test_aes_ccm_tag_len_helper(OSSL_LIB_CTX *libCtx,
1775+
const char *cipherName)
1776+
{
1777+
int err;
1778+
int i;
1779+
EVP_CIPHER *cipher = NULL;
1780+
EVP_CIPHER_CTX *ctx = NULL;
1781+
unsigned char iv[13];
1782+
unsigned char tag[16];
1783+
static const int invalidLen[] = { 1, 2, 3, 5, 7, 9 };
1784+
static const int validLen[] = { 4, 6, 8, 10, 12, 14, 16 };
1785+
int numInvalid = (int)(sizeof(invalidLen) / sizeof(invalidLen[0]));
1786+
int numValid = (int)(sizeof(validLen) / sizeof(validLen[0]));
1787+
1788+
memset(tag, 0, sizeof(tag));
1789+
1790+
cipher = EVP_CIPHER_fetch(libCtx, cipherName, "");
1791+
err = cipher == NULL;
1792+
1793+
for (i = 0; (err == 0) && (i < numInvalid); i++) {
1794+
ctx = EVP_CIPHER_CTX_new();
1795+
err = ctx == NULL;
1796+
if (err == 0) {
1797+
err = EVP_DecryptInit(ctx, cipher, NULL, NULL) != 1;
1798+
}
1799+
if (err == 0) {
1800+
err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
1801+
(int)sizeof(iv), NULL) != 1;
1802+
}
1803+
if ((err == 0) &&
1804+
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, invalidLen[i],
1805+
tag) == 1) {
1806+
PRINT_ERR_MSG("%s: EVP_CTRL_AEAD_SET_TAG incorrectly accepted "
1807+
"tag length %d", cipherName, invalidLen[i]);
1808+
err = 1;
1809+
}
1810+
EVP_CIPHER_CTX_free(ctx);
1811+
ctx = NULL;
1812+
}
1813+
1814+
for (i = 0; (err == 0) && (i < numValid); i++) {
1815+
ctx = EVP_CIPHER_CTX_new();
1816+
err = ctx == NULL;
1817+
if (err == 0) {
1818+
err = EVP_DecryptInit(ctx, cipher, NULL, NULL) != 1;
1819+
}
1820+
if (err == 0) {
1821+
err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
1822+
(int)sizeof(iv), NULL) != 1;
1823+
}
1824+
if ((err == 0) &&
1825+
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, validLen[i],
1826+
tag) != 1) {
1827+
PRINT_ERR_MSG("%s: EVP_CTRL_AEAD_SET_TAG incorrectly rejected "
1828+
"tag length %d", cipherName, validLen[i]);
1829+
err = 1;
1830+
}
1831+
EVP_CIPHER_CTX_free(ctx);
1832+
ctx = NULL;
1833+
}
1834+
1835+
EVP_CIPHER_free(cipher);
1836+
return err;
1837+
}
1838+
1839+
int test_aes_ccm_tag_len_undersized(void *data)
1840+
{
1841+
int err;
1842+
1843+
(void)data;
1844+
1845+
PRINT_MSG("AES-128-CCM tag length validation");
1846+
err = test_aes_ccm_tag_len_helper(wpLibCtx, "AES-128-CCM");
1847+
if (err == 0) {
1848+
PRINT_MSG("AES-256-CCM tag length validation");
1849+
err = test_aes_ccm_tag_len_helper(wpLibCtx, "AES-256-CCM");
1850+
}
1851+
1852+
return err;
1853+
}
1854+
16041855
#endif /* WP_HAVE_AESCCM */
16051856

test/unit.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ TEST_CASE test_case[] = {
288288
TEST_DECL(test_aes128_gcm_set_iv_inv, NULL),
289289
TEST_DECL(test_aes128_gcm_key_then_iv, NULL),
290290
TEST_DECL(test_aes_gcm_bad_tag, NULL),
291+
TEST_DECL(test_aes_gcm_tls_iv_fixed_oversized, NULL),
292+
TEST_DECL(test_aes_gcm_tag_len_undersized, NULL),
291293
#endif
292294
#ifdef WP_HAVE_AESCCM
293295
TEST_DECL(test_aes128_ccm, NULL),
@@ -296,6 +298,7 @@ TEST_CASE test_case[] = {
296298
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
297299
TEST_DECL(test_aes128_ccm_tls, NULL),
298300
TEST_DECL(test_aes_ccm_bad_tag, NULL),
301+
TEST_DECL(test_aes_ccm_tag_len_undersized, NULL),
299302
#endif
300303
#endif
301304
#ifdef WP_HAVE_RANDOM

test/unit.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ int test_aes128_gcm_tls(void *data);
219219
int test_aes128_gcm_set_iv_inv(void *data);
220220
int test_aes128_gcm_key_then_iv(void *data);
221221
int test_aes_gcm_bad_tag(void *data);
222+
int test_aes_gcm_tls_iv_fixed_oversized(void *data);
223+
int test_aes_gcm_tag_len_undersized(void *data);
222224

223225
#endif /* WP_HAVE_AESGCM */
224226

@@ -229,6 +231,7 @@ int test_aes192_ccm(void *data);
229231
int test_aes256_ccm(void *data);
230232
int test_aes128_ccm_tls(void *data);
231233
int test_aes_ccm_bad_tag(void *data);
234+
int test_aes_ccm_tag_len_undersized(void *data);
232235

233236
#endif /* WP_HAVE_AESCCM */
234237

0 commit comments

Comments
 (0)