diff --git a/src/wp_aes_aead.c b/src/wp_aes_aead.c index 23d9b1b3..597c0e71 100644 --- a/src/wp_aes_aead.c +++ b/src/wp_aes_aead.c @@ -32,6 +32,16 @@ #if defined(WP_HAVE_AESGCM) || defined(WP_HAVE_AESCCM) +/* For non-streaming AES-GCM, we have to spool all previous updates. + * This is the number of extra bytes we add when allocating the internal + * buffer used to spool the stream input. This way we if there is space + * we can use the existing buffer, reducing the number of full realloc + copy + * operations we need to do. Increase this number for better performance and + * more memory usage, decrease for worse performance but less overhead */ +#ifndef WP_AES_GCM_EXTRA_BUF_LEN +#define WP_AES_GCM_EXTRA_BUF_LEN 128 +#endif + /** * Authenticated Encryption with Associated Data structure. */ @@ -94,10 +104,14 @@ typedef struct wp_AeadCtx { /** CCM is not streaming and needs to cache AAD data. */ unsigned char* aad; #if defined(WP_HAVE_AESGCM) && !defined(WOLFSSL_AESGCM_STREAM) - /** Length of AAD data cached. */ + /** Length of data cached. */ size_t inLen; /** CCM is not streaming and needs to cache AAD data. */ unsigned char* in; + /* Total buffer size */ + size_t bufSize; + /* Original IV */ + unsigned char origIv[AES_BLOCK_SIZE]; #endif } wp_AeadCtx; @@ -310,15 +324,24 @@ static int wp_aead_cache_in(wp_AeadCtx *ctx, const unsigned char *in, unsigned char *p; if (inLen > 0) { - p = (unsigned char*)OPENSSL_realloc(ctx->in, ctx->inLen + inLen); - if (p == NULL) { - ok = 0; - } - if (ok) { - ctx->in = p; + if (inLen < (ctx->bufSize - ctx->inLen)) { + /* We can fit this new data into the extra space, dont realloc */ XMEMCPY(ctx->in + ctx->inLen, in, inLen); ctx->inLen += inLen; } + else { + p = (unsigned char*)OPENSSL_realloc(ctx->in, + ctx->inLen + inLen + WP_AES_GCM_EXTRA_BUF_LEN); + if (p == NULL) { + ok = 0; + } + if (ok) { + ctx->bufSize = ctx->inLen + inLen + WP_AES_GCM_EXTRA_BUF_LEN; + ctx->in = p; + XMEMCPY(ctx->in + ctx->inLen, in, inLen); + ctx->inLen += inLen; + } + } } 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, if (rc != 0) { ok = 0; } - #else - XMEMCPY(ctx->iv, ctx->aes.reg, ctx->ivLen); #endif } if (ok) { @@ -833,6 +854,9 @@ static int wp_aesgcm_get_rand_iv(wp_AeadCtx* ctx, unsigned char* out, olen = ctx->ivLen; } XMEMCPY(out, ctx->iv + ctx->ivLen - olen, olen); +#ifndef WOLFSSL_AESGCM_STREAM + XMEMCPY(ctx->origIv, ctx->iv, ctx->ivLen); +#endif if (inc) { int i; 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, ok = 0; } else { +#ifndef WOLFSSL_AESGCM_STREAM + XMEMCPY(ctx->origIv, ctx->iv, ctx->ivLen); +#endif XMEMCPY(ctx->iv + ctx->ivLen - inLen, in, inLen); ctx->ivState = IV_STATE_COPIED; } @@ -1310,67 +1337,96 @@ static int wp_aesgcm_stream_final(wp_AeadCtx *ctx, unsigned char *out, * @param [in, out] ctx AEAD context object. * @param [out] out Buffer to hold decrypted/encrypted data. * @param [out] outLen Length of data in output buffer. - * @param [in] in Data to be encrypted/decrypted. - * @param [in] inLen Length of data to be encrypted/decrypted. + * @param [in] offset Offset into the spooled data buffer. + * @param [in] done This is a final operation. + * * @return 1 on success. * @return 0 on failure. */ -static int wp_aesgcm_encdec(wp_AeadCtx *ctx, unsigned char *out, size_t* outLen) +static int wp_aesgcm_encdec(wp_AeadCtx *ctx, unsigned char *out, size_t* outLen, + size_t offset, int done) { int ok = 1; int rc; + unsigned char *tmp = NULL; + byte *iv = NULL; if (ctx->tagLen == UNINITIALISED_SIZET) { ctx->tagLen = EVP_GCM_TLS_TAG_LEN; } - if (ctx->enc) { - if (!ctx->ivSet) { - rc = wc_AesGcmSetExtIV(&ctx->aes, ctx->iv, (word32)ctx->ivLen); - if (rc != 0) { + if (ctx->inLen > offset || (ctx->tagAvail == 0 && done)) { + /* Prepare a temp buffer to store all the output */ + if (ctx->inLen > 0) { + tmp = OPENSSL_zalloc(ctx->inLen); + if (tmp == NULL) { ok = 0; } } + /* Once loaded, always use original IV */ + iv = ctx->iv; + if (ctx->ivState == IV_STATE_COPIED) { + iv = ctx->origIv; + } if (ok) { - ctx->ivSet = 1; - /* IV coming out in this call. */ - rc = wc_AesGcmEncrypt_ex(&ctx->aes, out, ctx->in, - (word32)ctx->inLen, ctx->iv, (word32)ctx->ivLen, ctx->buf, - (word32)ctx->tagLen, ctx->aad, (word32)ctx->aadLen); + rc = wc_AesGcmSetExtIV(&ctx->aes, iv, (word32)ctx->ivLen); if (rc != 0) { ok = 0; } - if (ok) { - ctx->tagAvail = 1; + + if (ok && ctx->ivState == IV_STATE_BUFFERED) { + ctx->ivState = IV_STATE_COPIED; + XMEMCPY(ctx->origIv, ctx->iv, ctx->ivLen); } } - } - else { - rc = wc_AesGcmDecrypt(&ctx->aes, out, ctx->in, (word32)ctx->inLen, - ctx->iv, (word32)ctx->ivLen, ctx->buf, (word32)ctx->tagLen, - ctx->aad, (word32)ctx->aadLen); - if (rc == AES_GCM_AUTH_E) { - ctx->authErr = 1; - rc = 0; + if (ctx->enc) { + if (ok) { + ctx->ivSet = 1; + /* IV coming out in this call. */ + rc = wc_AesGcmEncrypt_ex(&ctx->aes, tmp, ctx->in, + (word32)ctx->inLen, iv, (word32)ctx->ivLen, ctx->buf, + (word32)ctx->tagLen, ctx->aad, (word32)ctx->aadLen); + if (rc != 0) { + ok = 0; + } + if (ok) { + ctx->tagAvail = 1; + } + } } - if (rc != 0) { - ok = 0; + else { + /* Only the most recent auth err matters */ + ctx->authErr = 0; + rc = wc_AesGcmDecrypt(&ctx->aes, tmp, ctx->in, (word32)ctx->inLen, + iv, (word32)ctx->ivLen, ctx->buf, (word32)ctx->tagLen, + ctx->aad, (word32)ctx->aadLen); + if (rc == AES_GCM_AUTH_E) { + ctx->authErr = 1; + rc = 0; + } + if (rc != 0) { + ok = 0; + } } + /* Copy out relevant portion of output */ if (ok) { - XMEMCPY(ctx->iv, ctx->aes.reg, ctx->ivLen); + XMEMCPY(out, tmp + offset, (ctx->inLen - offset)); + *outLen = (ctx->inLen - offset); } + OPENSSL_free(tmp); } - if (ok) { - *outLen = ctx->inLen; + else { + *outLen = 0; + } + if (done) { + OPENSSL_free(ctx->aad); + ctx->aad = NULL; + ctx->aadLen = 0; + ctx->aadSet = 0; + OPENSSL_free(ctx->in); + ctx->in = NULL; + ctx->inLen = 0; } - - OPENSSL_free(ctx->aad); - ctx->aad = NULL; - ctx->aadLen = 0; - ctx->aadSet = 0; - OPENSSL_free(ctx->in); - ctx->in = NULL; - ctx->inLen = 0; WOLFPROV_LEAVE(WP_LOG_CIPHER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); return ok; @@ -1392,6 +1448,8 @@ static int wp_aesgcm_stream_update(wp_AeadCtx *ctx, unsigned char *out, size_t *outLen, size_t outSize, const unsigned char *in, size_t inLen) { int ok = 1; + int process = 0; + size_t curLen = 0; if (ctx->tlsAadLen != UNINITIALISED_SIZET) { ok = wp_aesgcm_tls_cipher(ctx, out, outLen, in, inLen); @@ -1414,12 +1472,22 @@ static int wp_aesgcm_stream_update(wp_AeadCtx *ctx, unsigned char *out, ok = 0; } else if (inLen > 0) { + curLen = ctx->inLen; if (!wp_aead_cache_in(ctx, in, inLen)) { ok = 0; } + else { + process = 1; + } } - *outLen = oLen; + /* If there is data to process, do it now */ + if (process) { + ok = wp_aesgcm_encdec(ctx, out, outLen, curLen, 0); + } + else { + *outLen = oLen; + } } WOLFPROV_LEAVE(WP_LOG_CIPHER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); @@ -1449,7 +1517,7 @@ static int wp_aesgcm_stream_final(wp_AeadCtx *ctx, unsigned char *out, ok = 0; } else { - ok = wp_aesgcm_encdec(ctx, out, outLen); + ok = wp_aesgcm_encdec(ctx, out, outLen, ctx->inLen, 1); ctx->ivSet = 0; } diff --git a/test/test_aestag.c b/test/test_aestag.c index 556f8dc7..f9b93ea8 100644 --- a/test/test_aestag.c +++ b/test/test_aestag.c @@ -101,20 +101,16 @@ static int test_aes_tag_enc(const EVP_CIPHER *cipher, if (err == 0 && len > 0) { /* Update with msg, if len > 0 (not GMAC) */ err = EVP_EncryptUpdate(ctx, enc, &encLen, msg, len) != 1; - #ifdef WOLFSSL_AESGCM_STREAM if (encLen != len) { err = 1; } - #endif } if (err == 0) { err = EVP_EncryptFinal_ex(ctx, enc + encLen, &encLen) != 1; - #ifdef WOLFSSL_AESGCM_STREAM if (encLen != 0) { /* should be no more data left */ err = 1; } - #endif } if (err == 0) { err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tagLen, tag) != 1; @@ -200,6 +196,63 @@ static int test_aes_tag_dec(const EVP_CIPHER *cipher, return err; } +static int test_aes_tag_dec_multi(const EVP_CIPHER *cipher, + unsigned char *key, unsigned char *iv, int ivLen, + unsigned char *aad, unsigned char *msg, int len, + unsigned char *enc, unsigned char *tag, + unsigned char *dec) +{ + int err; + EVP_CIPHER_CTX *ctx; + int decLen; + unsigned int tagLen = 16; + + err = (ctx = EVP_CIPHER_CTX_new()) == 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, ivLen, + NULL) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tagLen, + (void *)tag) != 1; + } + if (err == 0) { + err = EVP_DecryptInit(ctx, NULL, key, iv) != 1; + } + if (err == 0) { + err = EVP_DecryptUpdate(ctx, NULL, &decLen, aad, + (int)strlen((char *)aad)) != 1; + if (err == 0 && (decLen != (int)strlen((char *)aad))) { + PRINT_MSG("EVP_DecryptUpdate did not return correct size of AAD"); + err = 1; + } + } + if (err == 0) { + err = EVP_DecryptUpdate(ctx, dec, &decLen, enc, 1) != 1; + } + if (err == 0) { + err = EVP_DecryptUpdate(ctx, dec + 1, &decLen, enc + 1, len - 1) != 1; + } + if (err == 0) { + err = EVP_DecryptFinal_ex(ctx, dec + 1 + decLen, &decLen) != 1; + } + + if (err == 0 && dec != NULL && msg != NULL) { + PRINT_BUFFER("Decrypted", dec, len); + + if (memcmp(dec, msg, len) != 0) { + err = 1; + } + } + + EVP_CIPHER_CTX_free(ctx); + + return err; +} + static int test_aes_tag(void *data, const char *cipher, int keyLen, int ivLen, int ccm, int ccmL) { @@ -238,7 +291,6 @@ static int test_aes_tag(void *data, const char *cipher, err = test_aes_tag_dec(wcipher, key, iv, ivLen, aad, msg, sizeof(msg), enc, tag, dec, ccm, ccmL); } - if (err == 0) { PRINT_MSG("Encrypt with wolfprovider"); err = test_aes_tag_enc(wcipher, key, iv, ivLen, aad, msg, sizeof(msg), @@ -291,7 +343,6 @@ static int test_aes_gcm_gmac(void* data, const char* cipher, err = test_aes_tag_dec(wcipher, key, iv, ivLen, aad, NULL, 0, NULL, tag, NULL, 0, 0); } - if (err == 0) { PRINT_MSG("Encrypt with wolfprovider"); err = test_aes_tag_enc(wcipher, key, iv, ivLen, aad, NULL, 0, @@ -363,6 +414,57 @@ static int test_aes_tag_fixed_enc(const EVP_CIPHER *cipher, return err; } +static int test_aes_tag_fixed_enc_multi(const EVP_CIPHER *cipher, + unsigned char *key, unsigned char *iv, int ivFixedLen, int ivLen, + unsigned char *aad, unsigned char *msg, int len, unsigned char *enc, + unsigned char *tag) +{ + int err; + EVP_CIPHER_CTX *ctx; + int encLen = len; + unsigned int tagLen = 16; + + err = (ctx = EVP_CIPHER_CTX_new()) == NULL; + if (err == 0) { + err = EVP_EncryptInit(ctx, cipher, key, NULL) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IV_FIXED, ivFixedLen, + iv) != 1; + } + if (err == 0) { + memcpy(iv, EVP_CIPHER_CTX_iv(ctx), ivLen); + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_IV_GEN, ivLen, iv) != 1; + } + if (err == 0) { + err = EVP_EncryptUpdate(ctx, NULL, &encLen, aad, + (int)strlen((char *)aad)) != 1; + } + if (err == 0) { + err = EVP_EncryptUpdate(ctx, enc, &encLen, msg, 1) != 1; + } + if (err == 0) { + err = EVP_EncryptUpdate(ctx, enc + 1, &encLen, msg + 1, len - 1) != 1; + } + if (err == 0) { + err = EVP_EncryptFinal_ex(ctx, enc + 1 + encLen, &encLen) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tagLen, tag) != 1; + } + if (err == 0) { + } + + if (err == 0) { + PRINT_BUFFER("Encrypted", enc, len); + PRINT_BUFFER("Tag", tag, 16); + } + + EVP_CIPHER_CTX_free(ctx); + + return err; +} + static int test_aes_tag_enc_ossh(const EVP_CIPHER *cipher, unsigned char *key, unsigned char *iv, unsigned char *aad, unsigned char *msg, int len, unsigned char *enc, @@ -390,10 +492,61 @@ static int test_aes_tag_enc_ossh(const EVP_CIPHER *cipher, lastiv) != 1; } if (err == 0) { - err = EVP_Cipher(encCtx, NULL, aad, (int)strlen((char *)aad)) < 0; + err = EVP_Cipher(encCtx, NULL, aad, (int)strlen((char *)aad)) <= 0; } if (err == 0) { - err = EVP_Cipher(encCtx, enc, msg, len) < 0; + err = EVP_Cipher(encCtx, enc, msg, len) <= 0; + } + if (err == 0) { + err = EVP_Cipher(encCtx, NULL, NULL, 0) < 0; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_GET_TAG, tagLen, + tag) != 1; + } + if (err == 0) { + PRINT_BUFFER("Encrypted", enc, len); + PRINT_BUFFER("Tag", tag, 16); + } + + EVP_CIPHER_CTX_free(encCtx); + return err; +} + +static int test_aes_tag_enc_ossh_multi(const EVP_CIPHER *cipher, + unsigned char *key, unsigned char *iv, + unsigned char *aad, unsigned char *msg, int len, unsigned char *enc, + unsigned char *tag) +{ + int err; + EVP_CIPHER_CTX *encCtx; + unsigned int tagLen = 16; + char lastiv[1]; + + /* Test encryption flow used by openSSH */ + err = (encCtx = EVP_CIPHER_CTX_new()) == NULL; + if (err == 0) { + err = EVP_CipherInit(encCtx, cipher, NULL, iv, 1) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_SET_IV_FIXED, -1, + iv) != 1; + } + if (err == 0) { + err = EVP_CipherInit(encCtx, NULL, key, NULL, -1) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_IV_GEN, 1, + lastiv) != 1; + } + if (err == 0) { + err = EVP_Cipher(encCtx, NULL, aad, (int)strlen((char *)aad)) <= 0; + } + if (err == 0) { + err = EVP_Cipher(encCtx, enc, msg, 1) != 1; + } + if (err == 0) { + err = EVP_Cipher(encCtx, enc + 1, msg + 1, len - 1) != (len - 1); } if (err == 0) { err = EVP_Cipher(encCtx, NULL, NULL, 0) < 0; @@ -442,10 +595,64 @@ static int test_aes_tag_dec_ossh(const EVP_CIPHER *cipher, tag) != 1; } if (err == 0) { - err = EVP_Cipher(decCtx, NULL, aad, (int)strlen((char *)aad)) < 0; + err = EVP_Cipher(decCtx, NULL, aad, (int)strlen((char *)aad)) <= 0; + } + if (err == 0) { + err = EVP_Cipher(decCtx, dec, enc, len) != len; + } + if (err == 0) { + err = EVP_Cipher(decCtx, NULL, NULL, 0) < 0; + } + if (err == 0 && dec != NULL && msg != NULL) { + PRINT_BUFFER("Decrypted", dec, len); + + if (memcmp(dec, msg, len) != 0) { + err = 1; + } + } + + EVP_CIPHER_CTX_free(decCtx); + return err; +} + +static int test_aes_tag_dec_ossh_multi(const EVP_CIPHER *cipher, + unsigned char *key, unsigned char *iv, + unsigned char *aad, unsigned char *msg, int len, unsigned char *enc, + unsigned char *tag, unsigned char *dec) +{ + int err; + EVP_CIPHER_CTX *decCtx; + unsigned int tagLen = 16; + char lastiv[1]; + + /* Test decryption flow used by openSSH */ + err = (decCtx = EVP_CIPHER_CTX_new()) == NULL; + if (err == 0) { + err = EVP_CipherInit(decCtx, cipher, NULL, iv, 0) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(decCtx, EVP_CTRL_GCM_SET_IV_FIXED, -1, + iv) != 1; + } + if (err == 0) { + err = EVP_CipherInit(decCtx, NULL, key, NULL, -1) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(decCtx, EVP_CTRL_GCM_IV_GEN, 1, + lastiv) != 1; + } + if (err == 0) { + err = EVP_CIPHER_CTX_ctrl(decCtx, EVP_CTRL_GCM_SET_TAG, tagLen, + tag) != 1; + } + if (err == 0) { + err = EVP_Cipher(decCtx, NULL, aad, (int)strlen((char *)aad)) <= 0; + } + if (err == 0) { + err = EVP_Cipher(decCtx, dec, enc, 1) != 1; } if (err == 0) { - err = EVP_Cipher(decCtx, dec, enc, len) < 0; + err = EVP_Cipher(decCtx, dec + 1, enc + 1, len - 1) != (len - 1); } if (err == 0) { err = EVP_Cipher(decCtx, NULL, NULL, 0) < 0; @@ -506,7 +713,6 @@ static int test_aes_tag_fixed(void *data, const char *cipher, err = test_aes_tag_dec(wcipher, key, iv, ivLen, aad, msg, sizeof(msg), enc, tag, dec, 0, 0); } - if (err == 0) { PRINT_MSG("Encrypt with wolfprovider"); err = test_aes_tag_fixed_enc(wcipher, key, iv, ivFixedLen, ivLen, aad, @@ -517,24 +723,64 @@ static int test_aes_tag_fixed(void *data, const char *cipher, err = test_aes_tag_dec(ocipher, key, iv, ivLen, aad, msg, sizeof(msg), enc, tag, dec, 0, 0); } + if (err == 0) { + PRINT_MSG("Encrypt with OpenSSL (multiple updates)"); + err = test_aes_tag_fixed_enc_multi(ocipher, key, iv, ivFixedLen, ivLen, + aad, msg, sizeof(msg), enc, tag); + } + if (err == 0) { + PRINT_MSG("Decrypt with wolfprovider (multiple updates)"); + err = test_aes_tag_dec_multi(wcipher, key, iv, ivLen, aad, msg, sizeof(msg), + enc, tag, dec); + } + if (err == 0) { + PRINT_MSG("Encrypt with wolfprovider (multiple updates)"); + err = test_aes_tag_fixed_enc_multi(wcipher, key, iv, ivFixedLen, ivLen, aad, + msg, sizeof(msg), enc, tag); + } + if (err == 0) { + PRINT_MSG("Decrypt with OpenSSL (multiple updates)"); + err = test_aes_tag_dec_multi(ocipher, key, iv, ivLen, aad, msg, + sizeof(msg), enc, tag, dec); + } if (err == 0) { PRINT_MSG("Encrypt with wolfprovider"); - test_aes_tag_enc_ossh(wcipher, key, iv, + err = test_aes_tag_enc_ossh(wcipher, key, iv, aad, msg, sizeof(msg), enc, tag); } if (err == 0) { PRINT_MSG("Decrypt with OpenSSL"); - test_aes_tag_dec_ossh(ocipher, key, iv, + err = test_aes_tag_dec_ossh(ocipher, key, iv, aad, msg, sizeof(msg), enc, tag, dec); } if (err == 0) { PRINT_MSG("Encrypt with OpenSSL"); - test_aes_tag_enc_ossh(ocipher, key, iv, + err = test_aes_tag_enc_ossh(ocipher, key, iv, aad, msg, sizeof(msg), enc, tag); } if (err == 0) { PRINT_MSG("Decrypt with wolfprovider"); - test_aes_tag_dec_ossh(wcipher, key, iv, + err = test_aes_tag_dec_ossh(wcipher, key, iv, + aad, msg, sizeof(msg), enc, tag, dec); + } + if (err == 0) { + PRINT_MSG("Encrypt with wolfprovider (multiple updates)"); + err = test_aes_tag_enc_ossh_multi(wcipher, key, iv, + aad, msg, sizeof(msg), enc, tag); + } + if (err == 0) { + PRINT_MSG("Decrypt with OpenSSL (multiple updates)"); + err = test_aes_tag_dec_ossh_multi(ocipher, key, iv, + aad, msg, sizeof(msg), enc, tag, dec); + } + if (err == 0) { + PRINT_MSG("Encrypt with OpenSSL (multiple updates)"); + err = test_aes_tag_enc_ossh_multi(ocipher, key, iv, + aad, msg, sizeof(msg), enc, tag); + } + if (err == 0) { + PRINT_MSG("Decrypt with wolfprovider (multiple updates)"); + err = test_aes_tag_dec_ossh_multi(wcipher, key, iv, aad, msg, sizeof(msg), enc, tag, dec); }