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
145 changes: 141 additions & 4 deletions src/wp_aes_aead.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ typedef struct wp_AeadCtx {

/** Length of key. */
size_t keyLen;
/** Key data. */
unsigned char key[AES_256_KEY_SIZE];
/** Length of iv/nonce. */
size_t ivLen;
/** Authentication tag length. */
Expand All @@ -83,7 +85,7 @@ typedef struct wp_AeadCtx {
unsigned int ivGen:1;
/** CCM needs to set IV specially. */
unsigned int ivSet:1;
/** GCM needs to know if key has been set. */
/** Key has been set. */
unsigned int keySet:1;
/** Cache of authentication status. */
unsigned int authErr:1;
Expand Down Expand Up @@ -132,6 +134,106 @@ typedef struct wp_AeadCtx {
/** Uninitialized value for a field of type size_t. */
#define UNINITIALISED_SIZET ((size_t)-1)

/**
* Save the key for dupctx.
*
* @param [in, out] ctx AEAD context object.
* @param [in] key Key data.
* @param [in] keyLen Length of key in bytes.
*/
static void wp_aead_save_key(wp_AeadCtx* ctx, const unsigned char* key,
size_t keyLen)
{
XMEMCPY(ctx->key, key, keyLen);
ctx->keySet = 1;
}

/**
* Duplicate the AEAD context object.
*
* @param [in] src AEAD context object to copy.
* @return NULL on failure.
* @return AEAD context object.
*/
static void *wp_aead_dupctx(wp_AeadCtx* src)
{
wp_AeadCtx* dst = NULL;
int ok = 1;

if (!wolfssl_prov_is_running()) {
ok = 0;
}
#if defined(WP_HAVE_AESGCM) && defined(WOLFSSL_AESGCM_STREAM)
/* wolfCrypt's GCM stream state can't be copied. */
if (ok && (src->mode == EVP_CIPH_GCM_MODE) &&
(src->ivState == IV_STATE_COPIED)) {
ok = 0;
}
#endif
if (ok) {
dst = OPENSSL_malloc(sizeof(*dst));
if (dst == NULL) {
ok = 0;
}
}
if (ok) {
XMEMCPY(dst, src, sizeof(*dst));
dst->aad = NULL;
dst->aadLen = 0;
#if defined(WP_HAVE_AESGCM) && !defined(WOLFSSL_AESGCM_STREAM)
dst->in = NULL;
dst->inLen = 0;
dst->bufSize = 0;
#endif
XMEMSET(&dst->aes, 0, sizeof(dst->aes));
if (wc_AesInit(&dst->aes, NULL, INVALID_DEVID) != 0) {
ok = 0;
}
}
#ifdef WP_HAVE_AESGCM
if (ok && src->keySet && (src->mode == EVP_CIPH_GCM_MODE) &&
(wc_AesGcmSetKey(&dst->aes, src->key, (word32)src->keyLen) != 0)) {
ok = 0;
}
#endif
#ifdef WP_HAVE_AESCCM
if (ok && src->keySet && (src->mode == EVP_CIPH_CCM_MODE) &&
(wc_AesCcmSetKey(&dst->aes, src->key, (word32)src->keyLen) != 0)) {
ok = 0;
}
#endif
if (ok && (src->aad != NULL) && (src->aadLen > 0)) {
dst->aad = OPENSSL_memdup(src->aad, src->aadLen);
if (dst->aad == NULL) {
ok = 0;
}
else {
dst->aadLen = src->aadLen;
}
}
#if defined(WP_HAVE_AESGCM) && !defined(WOLFSSL_AESGCM_STREAM)
if (ok && (src->in != NULL)) {
dst->in = OPENSSL_malloc(src->bufSize);
if (dst->in == NULL) {
ok = 0;
}
else {
XMEMCPY(dst->in, src->in, src->inLen);
dst->inLen = src->inLen;
dst->bufSize = src->bufSize;
}
}
#endif
if ((!ok) && (dst != NULL)) {
OPENSSL_free(dst->aad);
wc_AesFree(&dst->aes);
OPENSSL_clear_free(dst, sizeof(*dst));
dst = NULL;
}

return dst;
}


/** AEAD cipher flags. */
#define AEAD_FLAGS (WP_CIPHER_FLAG_AEAD | WP_CIPHER_FLAG_CUSTOM_IV)
Expand Down Expand Up @@ -171,6 +273,7 @@ const OSSL_DISPATCH wp_aes##kbits##lc##_functions[] = { \
{ OSSL_FUNC_CIPHER_NEWCTX, \
(DFUNC)wp_aes_##lc##_##kbits##lc##_newctx }, \
{ OSSL_FUNC_CIPHER_FREECTX, (DFUNC)wp_aes_##lc##_freectx }, \
{ OSSL_FUNC_CIPHER_DUPCTX, (DFUNC)wp_aead_dupctx }, \
{ OSSL_FUNC_CIPHER_ENCRYPT_INIT, (DFUNC)wp_aes##lc##_einit }, \
{ OSSL_FUNC_CIPHER_DECRYPT_INIT, (DFUNC)wp_aes##lc##_dinit }, \
{ OSSL_FUNC_CIPHER_UPDATE, (DFUNC)wp_aes##lc##_stream_update }, \
Expand Down Expand Up @@ -1145,6 +1248,9 @@ static int wp_aesgcm_einit(wp_AeadCtx* ctx, const unsigned char *key,
if (ok) {
WP_CHECK_FIPS_ALGO(WP_CAST_ALGO_AES);
}
if (ok && (key != NULL) && (keyLen != ctx->keyLen)) {
ok = 0;
}
#ifdef WOLFSSL_AESGCM_STREAM
if (ok) {
int rc;
Expand All @@ -1166,13 +1272,19 @@ static int wp_aesgcm_einit(wp_AeadCtx* ctx, const unsigned char *key,
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_AesGcmSetKey", rc);
ok = 0;
}
else {
wp_aead_save_key(ctx, key, keyLen);
}
}
else if (key != NULL) {
rc = wc_AesGcmEncryptInit(aes, key, (word32)keyLen, iv, (word32)ivLen);
if (rc != 0) {
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_AesGcmEncryptInit", rc);
ok = 0;
}
else {
wp_aead_save_key(ctx, key, keyLen);
}
}
}
#else
Expand All @@ -1182,6 +1294,9 @@ static int wp_aesgcm_einit(wp_AeadCtx* ctx, const unsigned char *key,
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_AesGcmSetKey", rc);
ok = 0;
}
else {
wp_aead_save_key(ctx, key, keyLen);
}
}
if (ok && (iv != NULL)) {
if (ivLen != ctx->ivLen) {
Expand Down Expand Up @@ -1249,13 +1364,19 @@ static int wp_aesgcm_dinit(wp_AeadCtx *ctx, const unsigned char *key,
if (ok) {
WP_CHECK_FIPS_ALGO(WP_CAST_ALGO_AES);
}
if (ok && (key != NULL) && (keyLen != ctx->keyLen)) {
ok = 0;
}
#ifdef WOLFSSL_AESGCM_STREAM
if (ok && key != NULL) {
int rc = wc_AesGcmDecryptInit(aes, key, (word32)keyLen, iv, (word32)ivLen);
if (rc != 0) {
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_AesGcmDecryptInit", rc);
ok = 0;
}
else {
wp_aead_save_key(ctx, key, keyLen);
}
}
if (ok && (iv != NULL)) {
XMEMCPY(ctx->iv, iv, ivLen);
Expand All @@ -1269,6 +1390,9 @@ static int wp_aesgcm_dinit(wp_AeadCtx *ctx, const unsigned char *key,
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_AesGcmSetKey", rc);
ok = 0;
}
else {
wp_aead_save_key(ctx, key, keyLen);
}
}
if (ok && (iv != NULL)) {
if (ivLen != ctx->ivLen) {
Expand Down Expand Up @@ -1535,6 +1659,13 @@ static int wp_aesgcm_stream_final(wp_AeadCtx *ctx, unsigned char *out,
if ((!done) && ok && (ctx->ivState == IV_STATE_UNINITIALISED)) {
ok = 0;
}
if ((!done) && ok && (ctx->ivState == IV_STATE_BUFFERED)) {
rc = wc_AesGcmInit(aes, NULL, 0, ctx->iv, (word32)ctx->ivLen);
if (rc != 0) {
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_AesGcmInit", rc);
ok = 0;
}
}

if ((!done) && ok) {
if (outSize == 0) {
Expand Down Expand Up @@ -1892,10 +2023,10 @@ static void wp_aes_gcm_freectx(wp_AeadCtx* ctx)
{
OPENSSL_free(ctx->aad);
#if defined(WP_HAVE_AESGCM) && !defined(WOLFSSL_AESGCM_STREAM)
OPENSSL_free(ctx->in);
OPENSSL_clear_free(ctx->in, ctx->inLen);
#endif
wc_AesFree(&ctx->aes);
OPENSSL_free(ctx);
OPENSSL_clear_free(ctx, sizeof(*ctx));
}


Expand Down Expand Up @@ -1970,12 +2101,18 @@ static int wp_aesccm_init(wp_AeadCtx* ctx, const unsigned char *key,
if (ok) {
WP_CHECK_FIPS_ALGO(WP_CAST_ALGO_AES);
}
if (ok && (key != NULL) && (keyLen != ctx->keyLen)) {
ok = 0;
}
if (ok && (key != NULL)) {
rc = wc_AesCcmSetKey(&ctx->aes, key, (word32)keyLen);
if (rc != 0) {
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_AesCcmSetKey", rc);
ok = 0;
}
else {
wp_aead_save_key(ctx, key, keyLen);
}
}
if (ok && (iv != NULL)) {
if (ivLen != ctx->ivLen) {
Expand Down Expand Up @@ -2411,7 +2548,7 @@ static void wp_aes_ccm_freectx(wp_AeadCtx* ctx)
{
OPENSSL_free(ctx->aad);
wc_AesFree(&ctx->aes);
OPENSSL_free(ctx);
OPENSSL_clear_free(ctx, sizeof(*ctx));
}

/* Implement AES CCM for key sizes: 128, 192 and 256 bits. */
Expand Down
84 changes: 84 additions & 0 deletions test/test_aestag.c
Original file line number Diff line number Diff line change
Expand Up @@ -2510,6 +2510,90 @@ int test_aes_gcm_final_no_update_no_iv(void *data)
return err;
}

static EVP_CIPHER_CTX *aead_base(EVP_CIPHER *cipher, int enc, int ccm,
const unsigned char *key)
{
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();

if (ctx != NULL && (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL,
enc) != 1 ||
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL) != 1 ||
(ccm && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16,
NULL) != 1) ||
EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, enc) != 1)) {
EVP_CIPHER_CTX_free(ctx);
ctx = NULL;
}
return ctx;
}

/* pyca/cryptography copies a keyed base context for every operation. */
static int aead_copy_op(EVP_CIPHER_CTX *base, int copy, int enc, int ccm,
int len, const unsigned char *in, unsigned char *out, unsigned char *tag)
{
static const unsigned char iv[12] = { 1, 2, 3 };
static const unsigned char aad[] = "aad";
EVP_CIPHER_CTX *ctx = copy ? EVP_CIPHER_CTX_new() : base;
int n = 0, fin, err;

err = ctx == NULL || (copy && EVP_CIPHER_CTX_copy(ctx, base) != 1) ||
EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv, enc) != 1 ||
(!enc && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16,
tag) != 1) ||
(ccm && EVP_CipherUpdate(ctx, NULL, &n, NULL, len) != 1) ||
(len > 0 && (EVP_CipherUpdate(ctx, NULL, &n, aad, sizeof(aad)) != 1 ||
EVP_CipherUpdate(ctx, out, &n, in, len) != 1 || n != len)) ||
EVP_CipherFinal_ex(ctx, out + n, &fin) != 1 ||
(enc && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, tag) != 1);
if (copy) {
EVP_CIPHER_CTX_free(ctx);
}
return err;
}

int test_aes_aead_ctx_copy(void *data)
{
static const char *names[] = { "AES-128-GCM", "AES-192-GCM", "AES-256-GCM",
#ifdef WP_HAVE_AESCCM
"AES-128-CCM", "AES-192-CCM", "AES-256-CCM",
#endif
};
unsigned char key[32] = { 4, 5, 6 }, pt[32] = { 7, 8, 9 };
unsigned char ct[2][32], tag[2][16], dec[32];
EVP_CIPHER *cipher[2];
EVP_CIPHER_CTX *base[2], *ref;
int i, j, ccm, len, err = 0;

(void)data;

for (i = 0; i < (int)(sizeof(names) / sizeof(*names)) && !err; i++) {
PRINT_MSG("%s: copy keyed context", names[i]);
ccm = strstr(names[i], "CCM") != NULL;
cipher[0] = EVP_CIPHER_fetch(osslLibCtx, names[i], "");
cipher[1] = EVP_CIPHER_fetch(wpLibCtx, names[i], "");
base[0] = aead_base(cipher[1], 1, ccm, key);
base[1] = aead_base(cipher[1], 0, ccm, key);
Comment on lines +2574 to +2575
/* Round two copies the bases again; GCM goes straight to Final. */
for (j = 0; j < 2 && !err; j++) {
len = (ccm || j == 0) ? 32 : 0;
/* Default can't copy GCM/CCM before 3.0.13/3.1.5. */
ref = aead_base(cipher[0], 1, ccm, key);
err = aead_copy_op(ref, 0, 1, ccm, len, pt, ct[0], tag[0]) ||
aead_copy_op(base[0], 1, 1, ccm, len, pt, ct[1], tag[1]) ||
memcmp(ct[0], ct[1], len) != 0 ||
memcmp(tag[0], tag[1], 16) != 0 ||
aead_copy_op(base[1], 1, 0, ccm, len, ct[1], dec, tag[1]) ||
memcmp(dec, pt, len) != 0;
EVP_CIPHER_CTX_free(ref);
}
EVP_CIPHER_CTX_free(base[0]);
EVP_CIPHER_CTX_free(base[1]);
EVP_CIPHER_free(cipher[0]);
EVP_CIPHER_free(cipher[1]);
}
return err;
}

#endif /* WP_HAVE_AESGCM */

/******************************************************************************/
Expand Down
1 change: 1 addition & 0 deletions test/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ TEST_CASE test_case[] = {
TEST_DECL(test_aes_gcm_key_no_iv, NULL),
TEST_DECL(test_aes_gcm_stream_key_no_iv, NULL),
TEST_DECL(test_aes_gcm_final_no_update_no_iv, NULL),
TEST_DECL(test_aes_aead_ctx_copy, NULL),
#endif
#ifdef WP_HAVE_AESCCM
TEST_DECL(test_aes128_ccm, NULL),
Expand Down
1 change: 1 addition & 0 deletions test/unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ int test_aes_gcm_tag_len_undersized(void *data);
int test_aes_gcm_key_no_iv(void *data);
int test_aes_gcm_stream_key_no_iv(void *data);
int test_aes_gcm_final_no_update_no_iv(void *data);
int test_aes_aead_ctx_copy(void *data);

#endif /* WP_HAVE_AESGCM */

Expand Down
Loading