Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions src/wp_aes_aead.c
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,37 @@ static int wp_aead_get_ctx_params(wp_AeadCtx* ctx, OSSL_PARAM params[])
return ok;
}

/**
* Check whether a tag length is one of the algorithm's allowed sizes.
*
* GCM tags must be 4, 8, 12, 13, 14, 15 or 16 bytes (NIST SP 800-38D).
* CCM tags must be 4, 6, 8, 10, 12, 14 or 16 bytes (NIST SP 800-38C /
* RFC 3610).
*
* @param [in] mode Cipher mode: EVP_CIPH_GCM_MODE or EVP_CIPH_CCM_MODE.
* @param [in] sz Tag length in bytes to check.
* @return 1 if sz is an allowed length for mode.
* @return 0 otherwise.
*/
static int wp_aead_tag_len_valid(int mode, size_t sz)
{
int ok;

if (mode == EVP_CIPH_GCM_MODE) {
ok = (sz == 4) || (sz == 8) || (sz == 12) || (sz == 13) ||
(sz == 14) || (sz == 15) || (sz == 16);
}
else if (mode == EVP_CIPH_CCM_MODE) {
ok = (sz == 4) || (sz == 6) || (sz == 8) || (sz == 10) ||
(sz == 12) || (sz == 14) || (sz == 16);
}
else {
ok = 0;
}

return ok;
}

/**
* Set the AEAD tag from the parameters.
*
Expand Down Expand Up @@ -488,6 +519,9 @@ static int wp_aead_set_param_tag(wp_AeadCtx* ctx,
if (ok && ((sz == 0) || ((p->data != NULL) && ctx->enc))) {
ok = 0;
}
if (ok && !wp_aead_tag_len_valid(ctx->mode, sz)) {
ok = 0;
}
if (ok) {
ctx->tagLen = sz;
}
Expand Down Expand Up @@ -959,9 +993,10 @@ static int wp_aesgcm_tls_iv_set_fixed(wp_AeadCtx* ctx, unsigned char* iv,
}
else {
/* Fixed field must be at least 4 bytes and invocation field at least 8
*/
if ((len < EVP_GCM_TLS_FIXED_IV_LEN) ||
(ctx->ivLen - (int)len) < EVP_GCM_TLS_EXPLICIT_IV_LEN) {
* bytes */
if ((len < EVP_GCM_TLS_FIXED_IV_LEN) || (len > ctx->ivLen) ||
(len > sizeof(ctx->iv)) ||
(ctx->ivLen - len) < EVP_GCM_TLS_EXPLICIT_IV_LEN) {
return 0;
}
if (ctx->enc) {
Expand Down
256 changes: 256 additions & 0 deletions test/test_aestag.c
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,159 @@ int test_aes_gcm_bad_tag(void *data)
return err;
}

static int test_aes_gcm_tls_iv_fixed_oversized_helper(OSSL_LIB_CTX *libCtx,
const char *cipherName, int keyLen)
{
int err;
EVP_CIPHER *cipher = NULL;
EVP_CIPHER_CTX *ctx = NULL;
unsigned char key[32];
/* ivLen (12, the GCM default) + EVP_GCM_TLS_EXPLICIT_IV_LEN (8) = 20: */
unsigned char iv[20];

memset(key, 0xCC, keyLen);
memset(iv, 0xDD, sizeof(iv));

cipher = EVP_CIPHER_fetch(libCtx, cipherName, "");
err = cipher == NULL;

/* A fixed len that leaves exactly the 8-byte
* explicit/invocation field must still be accepted. */
if (err == 0) {
ctx = EVP_CIPHER_CTX_new();
err = ctx == NULL;
}
if (err == 0) {
err = EVP_DecryptInit_ex(ctx, cipher, NULL, key, NULL) != 1;
}
if (err == 0) {
err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED,
EVP_GCM_TLS_FIXED_IV_LEN, iv) != 1;
}
EVP_CIPHER_CTX_free(ctx);
ctx = NULL;

/* Oversized fixed len (> ctx->ivLen, default 12) must be rejected. */
if (err == 0) {
ctx = EVP_CIPHER_CTX_new();
err = ctx == NULL;
}
if (err == 0) {
err = EVP_DecryptInit_ex(ctx, cipher, NULL, key, NULL) != 1;
}
if (err == 0) {
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED,
(int)sizeof(iv), iv) == 1) {
PRINT_ERR_MSG("%s: EVP_CTRL_GCM_SET_IV_FIXED incorrectly "
"accepted a fixed IV length (%d) larger than "
"the IV length", cipherName, (int)sizeof(iv));
err = 1;
}
}

EVP_CIPHER_CTX_free(ctx);
EVP_CIPHER_free(cipher);
return err;
}

int test_aes_gcm_tls_iv_fixed_oversized(void *data)
{
int err;

(void)data;

PRINT_MSG("AES-128-GCM TLS1 fixed-IV oversized length rejection");
err = test_aes_gcm_tls_iv_fixed_oversized_helper(wpLibCtx,
"AES-128-GCM", 16);

return err;
}

/*
* Test that GCM tags smaller than the 32-bit minimum are rejected.
*/
static int test_aes_gcm_tag_len_undersized_helper(OSSL_LIB_CTX *libCtx,
const char *cipherName, int keyLen)
{
int err;
EVP_CIPHER *cipher = NULL;
EVP_CIPHER_CTX *ctx = NULL;
unsigned char key[32];
unsigned char iv[12];
unsigned char aad[] = "additional data";
unsigned char pt[] = "GCM plaintext for tag length test";
int ptLen = (int)(sizeof(pt) - 1);
unsigned char ct[64];
unsigned char tag[16];
int outLen = 0, fLen = 0;

memset(key, 0xAA, keyLen);
memset(iv, 0xBB, sizeof(iv));

cipher = EVP_CIPHER_fetch(libCtx, cipherName, "");
err = cipher == NULL;

/* Encrypt normally to get a valid ciphertext/full-size tag pair. */
if (err == 0) {
ctx = EVP_CIPHER_CTX_new();
err = ctx == NULL;
}
if (err == 0) {
err = EVP_EncryptInit(ctx, cipher, key, iv) != 1;
}
if (err == 0) {
err = EVP_EncryptUpdate(ctx, NULL, &outLen, aad,
(int)(sizeof(aad) - 1)) != 1;
}
if (err == 0) {
err = EVP_EncryptUpdate(ctx, ct, &outLen, pt, ptLen) != 1;
}
if (err == 0) {
err = EVP_EncryptFinal_ex(ctx, ct + outLen, &fLen) != 1;
outLen += fLen;
}
if (err == 0) {
err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, sizeof(tag),
tag) != 1;
}
EVP_CIPHER_CTX_free(ctx);
ctx = NULL;

/* A 1-byte tag length is below the NIST-mandated minimum (4 bytes)
* and must be rejected here. */
if (err == 0) {
ctx = EVP_CIPHER_CTX_new();
err = ctx == NULL;
}
if (err == 0) {
err = EVP_DecryptInit(ctx, cipher, NULL, NULL) != 1;
}
if (err == 0) {
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 1, tag) == 1) {
PRINT_ERR_MSG("%s: EVP_CTRL_AEAD_SET_TAG incorrectly accepted "
"a 1-byte tag length", cipherName);
err = 1;
}
}

EVP_CIPHER_CTX_free(ctx);
EVP_CIPHER_free(cipher);
return err;
}

int test_aes_gcm_tag_len_undersized(void *data)
{
int err;

(void)data;

PRINT_MSG("AES-128-GCM undersized tag length rejection");
err = test_aes_gcm_tag_len_undersized_helper(wpLibCtx, "AES-128-GCM",
16);

return err;
}

#endif /* WP_HAVE_AESGCM */

/******************************************************************************/
Expand Down Expand Up @@ -1601,5 +1754,108 @@ int test_aes_ccm_bad_tag(void *data)
return err;
}

/*
* Test that CCM tags are at least 32 bits.
*/
static int test_aes_ccm_tag_len_undersized_helper(OSSL_LIB_CTX *libCtx,
const char *cipherName, int keyLen)
{
int err;
EVP_CIPHER *cipher = NULL;
EVP_CIPHER_CTX *ctx = NULL;
unsigned char key[32];
unsigned char iv[13];
unsigned char aad[] = "additional data";
unsigned char pt[] = "CCM plaintext for tag length test";
int ptLen = (int)(sizeof(pt) - 1);
unsigned char ct[64];
unsigned char tag[16];
int outLen = 0, fLen = 0;

memset(key, 0xAA, keyLen);
memset(iv, 0xBB, sizeof(iv));

cipher = EVP_CIPHER_fetch(libCtx, cipherName, "");
err = cipher == NULL;

/* Encrypt normally to get a valid ciphertext/full-size tag pair. */
if (err == 0) {
ctx = EVP_CIPHER_CTX_new();
err = ctx == NULL;
}
if (err == 0) {
err = EVP_EncryptInit(ctx, cipher, NULL, NULL) != 1;
}
if (err == 0) {
err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
(int)sizeof(iv), NULL) != 1;
}
if (err == 0) {
err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, sizeof(tag),
NULL) != 1;
}
if (err == 0) {
err = EVP_EncryptInit(ctx, NULL, key, iv) != 1;
}
if (err == 0) {
err = EVP_EncryptUpdate(ctx, NULL, &outLen, NULL, ptLen) != 1;
}
if (err == 0) {
err = EVP_EncryptUpdate(ctx, NULL, &outLen, aad,
(int)(sizeof(aad) - 1)) != 1;
}
if (err == 0) {
err = EVP_EncryptUpdate(ctx, ct, &outLen, pt, ptLen) != 1;
}
if (err == 0) {
err = EVP_EncryptFinal_ex(ctx, ct + outLen, &fLen) != 1;
outLen += fLen;
}
if (err == 0) {
err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, sizeof(tag),
tag) != 1;
}
EVP_CIPHER_CTX_free(ctx);
ctx = NULL;

/* A 1-byte tag length is below the NIST-mandated minimum (4 bytes)
* and must be rejected here. */
if (err == 0) {
ctx = EVP_CIPHER_CTX_new();
err = ctx == NULL;
}
if (err == 0) {
err = EVP_DecryptInit(ctx, cipher, NULL, NULL) != 1;
}
if (err == 0) {
err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
(int)sizeof(iv), NULL) != 1;
}
if (err == 0) {
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 1, tag) == 1) {
PRINT_ERR_MSG("%s: EVP_CTRL_AEAD_SET_TAG incorrectly accepted "
"a 1-byte tag length", cipherName);
err = 1;
}
}

EVP_CIPHER_CTX_free(ctx);
EVP_CIPHER_free(cipher);
return err;
}

int test_aes_ccm_tag_len_undersized(void *data)
{
int err;

(void)data;

PRINT_MSG("AES-128-CCM undersized tag length rejection");
err = test_aes_ccm_tag_len_undersized_helper(wpLibCtx, "AES-128-CCM",
16);

return err;
}

#endif /* WP_HAVE_AESCCM */

3 changes: 3 additions & 0 deletions test/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ TEST_CASE test_case[] = {
TEST_DECL(test_aes128_gcm_set_iv_inv, NULL),
TEST_DECL(test_aes128_gcm_key_then_iv, NULL),
TEST_DECL(test_aes_gcm_bad_tag, NULL),
TEST_DECL(test_aes_gcm_tls_iv_fixed_oversized, NULL),
TEST_DECL(test_aes_gcm_tag_len_undersized, NULL),
#endif
#ifdef WP_HAVE_AESCCM
TEST_DECL(test_aes128_ccm, NULL),
Expand All @@ -296,6 +298,7 @@ TEST_CASE test_case[] = {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
TEST_DECL(test_aes128_ccm_tls, NULL),
TEST_DECL(test_aes_ccm_bad_tag, NULL),
TEST_DECL(test_aes_ccm_tag_len_undersized, NULL),
#endif
#endif
#ifdef WP_HAVE_RANDOM
Expand Down
3 changes: 3 additions & 0 deletions test/unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ int test_aes128_gcm_tls(void *data);
int test_aes128_gcm_set_iv_inv(void *data);
int test_aes128_gcm_key_then_iv(void *data);
int test_aes_gcm_bad_tag(void *data);
int test_aes_gcm_tls_iv_fixed_oversized(void *data);
int test_aes_gcm_tag_len_undersized(void *data);

#endif /* WP_HAVE_AESGCM */

Expand All @@ -229,6 +231,7 @@ int test_aes192_ccm(void *data);
int test_aes256_ccm(void *data);
int test_aes128_ccm_tls(void *data);
int test_aes_ccm_bad_tag(void *data);
int test_aes_ccm_tag_len_undersized(void *data);

#endif /* WP_HAVE_AESCCM */

Expand Down
Loading