mirror of https://github.com/wolfSSL/wolfssl.git
Merge pull request #11148 from dgarske/fenrir_aes_port_fixes
Fixes for AES in Octeon and TIpull/11169/head
commit
a0a5b56e3c
|
|
@ -52,6 +52,13 @@
|
|||
#include <wolfssl/wolfcrypt/aes.h>
|
||||
#endif
|
||||
|
||||
#ifdef NO_INLINE
|
||||
#include <wolfssl/wolfcrypt/misc.h>
|
||||
#else
|
||||
#define WOLFSSL_MISC_INCLUDED
|
||||
#include <wolfcrypt/src/misc.c>
|
||||
#endif
|
||||
|
||||
#define NOOPT __attribute__((optimize("O0")))
|
||||
|
||||
static int devId = 1234;
|
||||
|
|
@ -441,11 +448,17 @@ static NOOPT void Octeon_GHASH_Init(word16 poly, byte* h)
|
|||
}
|
||||
|
||||
|
||||
/* in may be a caller supplied buffer with no alignment guarantee, so load it
|
||||
* the same unaligned safe way Octeon_AesGcm_SetAAD does. A plain 64-bit
|
||||
* dereference of an odd address traps on MIPS64. */
|
||||
static NOOPT void Octeon_GHASH_Update(byte* in)
|
||||
{
|
||||
word64* bigIn = (word64*)in;
|
||||
CVMX_MT_GFM_XOR0(bigIn[0]);
|
||||
CVMX_MT_GFM_XORMUL1(bigIn[1]);
|
||||
word64 in0, in1;
|
||||
|
||||
CVMX_LOADUNA_INT64(in0, in, 0);
|
||||
CVMX_LOADUNA_INT64(in1, in, 8);
|
||||
CVMX_MT_GFM_XOR0(in0);
|
||||
CVMX_MT_GFM_XORMUL1(in1);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -478,7 +491,7 @@ static NOOPT int Octeon_AesGcm_SetKey(Aes* aes)
|
|||
CVMX_MT_AES_KEYLENGTH((aes->keylen / 8) - 1);
|
||||
|
||||
if (!aes->keySet) {
|
||||
uint64_t* bigH = (uint64_t*)aes->H;
|
||||
uint64_t* bigH = (uint64_t*)aes->gcm.H;
|
||||
CVMX_MT_AES_ENC0(0);
|
||||
CVMX_MT_AES_ENC1(0);
|
||||
CVMX_MF_AES_RESULT(bigH[0], 0);
|
||||
|
|
@ -506,11 +519,17 @@ static NOOPT int Octeon_AesGcm_SetIV(Aes* aes, byte* iv, word32 ivSz)
|
|||
}
|
||||
else {
|
||||
int blocks, remainder, i;
|
||||
byte aesBlock[WC_AES_BLOCK_SIZE];
|
||||
ALIGN16 byte aesBlock[WC_AES_BLOCK_SIZE];
|
||||
|
||||
blocks = ivSz / WC_AES_BLOCK_SIZE;
|
||||
remainder = ivSz % WC_AES_BLOCK_SIZE;
|
||||
|
||||
/* Hash the IV against a freshly loaded H. SetKey only computes
|
||||
* aes->gcm.H in memory, it does not touch the GFM hardware, so
|
||||
* without this J0 would be derived from whatever GFM state the
|
||||
* previous operation left behind. */
|
||||
Octeon_GHASH_Init(0xe100, aes->gcm.H);
|
||||
|
||||
for (i = 0; i < blocks; i++, iv += WC_AES_BLOCK_SIZE)
|
||||
Octeon_GHASH_Update(iv);
|
||||
|
||||
|
|
@ -527,7 +546,7 @@ static NOOPT int Octeon_AesGcm_SetIV(Aes* aes, byte* iv, word32 ivSz)
|
|||
aes->y0 = aes->reg[3];
|
||||
aes->reg[3]++;
|
||||
|
||||
Octeon_GHASH_Init(0xe100, aes->H);
|
||||
Octeon_GHASH_Init(0xe100, aes->gcm.H);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -549,7 +568,7 @@ static NOOPT int Octeon_AesGcm_SetAAD(Aes* aes, byte* aad, word32 aadSz)
|
|||
blocks = aadSz / WC_AES_BLOCK_SIZE;
|
||||
remainder = aadSz % WC_AES_BLOCK_SIZE;
|
||||
|
||||
Octeon_GHASH_Restore(0xe100, aes->H);
|
||||
Octeon_GHASH_Restore(0xe100, aes->gcm.H);
|
||||
|
||||
p = (word64*)aesBlock;
|
||||
|
||||
|
|
@ -672,6 +691,9 @@ static int Octeon_AesGcm_SetEncrypt(Aes* aes, byte* in, byte* out, word32 inSz,
|
|||
}
|
||||
|
||||
|
||||
/* Computes the GCM tag into tag, which must be a private buffer of at least
|
||||
* WC_AES_BLOCK_SIZE bytes. Never pass a caller supplied tag pointer here, the
|
||||
* decrypt path must compare rather than overwrite. */
|
||||
static NOOPT int Octeon_AesGcm_Finalize(Aes* aes, word32 inSz, word32 aadSz,
|
||||
byte* tag)
|
||||
{
|
||||
|
|
@ -716,15 +738,24 @@ static NOOPT int Octeon_AesGcm_Finalize(Aes* aes, word32 inSz, word32 aadSz,
|
|||
|
||||
|
||||
static int Octeon_AesGcm_Encrypt(Aes* aes, byte* in, byte* out, word32 inSz,
|
||||
byte* iv, word32 ivSz, byte* aad, word32 aadSz, byte* tag)
|
||||
byte* iv, word32 ivSz, byte* aad, word32 aadSz, byte* tag,
|
||||
word32 tagSz)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
ALIGN16 byte calcTag[WC_AES_BLOCK_SIZE];
|
||||
|
||||
if (aes == NULL)
|
||||
ret = BAD_FUNC_ARG;
|
||||
/* Return before touching any caller owned buffer. Use the same tag size
|
||||
* policy as the software path rather than a local range check, so an
|
||||
* unsupported size is rejected here too. */
|
||||
if (aes == NULL || tag == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
ret = wc_local_AesGcmCheckTagSz(tagSz);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
ret = Octeon_AesGcm_SetKey(aes);
|
||||
ret = Octeon_AesGcm_SetKey(aes);
|
||||
|
||||
if (ret == 0)
|
||||
ret = Octeon_AesGcm_SetIV(aes, iv, ivSz);
|
||||
|
|
@ -736,22 +767,40 @@ static int Octeon_AesGcm_Encrypt(Aes* aes, byte* in, byte* out, word32 inSz,
|
|||
ret = Octeon_AesGcm_SetEncrypt(aes, in, out, inSz, 1);
|
||||
|
||||
if (ret == 0)
|
||||
ret = Octeon_AesGcm_Finalize(aes, inSz, aadSz, tag);
|
||||
ret = Octeon_AesGcm_Finalize(aes, inSz, aadSz, calcTag);
|
||||
|
||||
/* Only tagSz bytes belong to the caller, the tag buffer may be shorter
|
||||
* than a full block. */
|
||||
if (ret == 0)
|
||||
XMEMCPY(tag, calcTag, tagSz);
|
||||
|
||||
ForceZero(calcTag, sizeof(calcTag));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int Octeon_AesGcm_Decrypt(Aes* aes, byte* in, byte* out, word32 inSz,
|
||||
byte* iv, word32 ivSz, byte* aad, word32 aadSz, byte* tag)
|
||||
byte* iv, word32 ivSz, byte* aad, word32 aadSz, const byte* tag,
|
||||
word32 tagSz)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
ALIGN16 byte calcTag[WC_AES_BLOCK_SIZE];
|
||||
|
||||
if (aes == NULL)
|
||||
ret = BAD_FUNC_ARG;
|
||||
/* Return before touching any caller owned buffer. The output wipe below
|
||||
* is deliberately conservative: it fires on any failure past this point,
|
||||
* including a setup failure that never wrote to out. Use the same tag
|
||||
* size policy as the software path rather than a local range check, so an
|
||||
* unsupported size is rejected here too. */
|
||||
if (aes == NULL || tag == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
ret = wc_local_AesGcmCheckTagSz(tagSz);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
ret = Octeon_AesGcm_SetKey(aes);
|
||||
ret = Octeon_AesGcm_SetKey(aes);
|
||||
|
||||
if (ret == 0)
|
||||
ret = Octeon_AesGcm_SetIV(aes, iv, ivSz);
|
||||
|
|
@ -762,8 +811,18 @@ static int Octeon_AesGcm_Decrypt(Aes* aes, byte* in, byte* out, word32 inSz,
|
|||
if (ret == 0)
|
||||
ret = Octeon_AesGcm_SetEncrypt(aes, in, out, inSz, 0);
|
||||
|
||||
/* Finalize into a private buffer, the caller's tag is the one the peer
|
||||
* sent and must be compared against, never written to. */
|
||||
if (ret == 0)
|
||||
ret = Octeon_AesGcm_Finalize(aes, inSz, aadSz, tag);
|
||||
ret = Octeon_AesGcm_Finalize(aes, inSz, aadSz, calcTag);
|
||||
|
||||
if (ret == 0 && ConstantCompare(tag, calcTag, (int)tagSz) != 0)
|
||||
ret = AES_GCM_AUTH_E;
|
||||
|
||||
if (ret != 0 && out != NULL && inSz > 0)
|
||||
ForceZero(out, inSz);
|
||||
|
||||
ForceZero(calcTag, sizeof(calcTag));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -802,7 +861,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
|||
info->cipher.aesgcm_enc.ivSz,
|
||||
(byte*)info->cipher.aesgcm_enc.authIn,
|
||||
info->cipher.aesgcm_enc.authInSz,
|
||||
(byte*)info->cipher.aesgcm_enc.authTag);
|
||||
(byte*)info->cipher.aesgcm_enc.authTag,
|
||||
info->cipher.aesgcm_enc.authTagSz);
|
||||
}
|
||||
else {
|
||||
ret = Octeon_AesGcm_Decrypt(
|
||||
|
|
@ -814,7 +874,8 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
|||
info->cipher.aesgcm_dec.ivSz,
|
||||
(byte*)info->cipher.aesgcm_dec.authIn,
|
||||
info->cipher.aesgcm_dec.authInSz,
|
||||
(byte*)info->cipher.aesgcm_dec.authTag);
|
||||
info->cipher.aesgcm_dec.authTag,
|
||||
info->cipher.aesgcm_dec.authTagSz);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_AESGCM */
|
||||
|
|
|
|||
|
|
@ -51,8 +51,14 @@
|
|||
#include "driverlib/rom.h"
|
||||
|
||||
#define AES_CFG_MODE_CTR_NOCTR (AES_CFG_MODE_CTR + 100)
|
||||
#define IS_ALIGN16(p) (((unsigned int)(p) & 0xf) == 0)
|
||||
#define ROUNDUP_16(n) ((n+15) & 0xfffffff0)
|
||||
/* IS_ALIGN16 tests an address, IS_MULT16 tests a length. Do not confuse the
|
||||
* two: the ROM AES engine needs both a 16 byte aligned buffer and a whole
|
||||
* number of 16 byte blocks. ALIGN16_SLACK is the extra room needed to
|
||||
* hand-align an allocation up to a block boundary. */
|
||||
#define ALIGN16_SLACK (WC_AES_BLOCK_SIZE - 1)
|
||||
#define IS_ALIGN16(p) ((((wc_ptr_t)(p)) & (wc_ptr_t)ALIGN16_SLACK) == 0)
|
||||
#define IS_MULT16(n) (((n) & (word32)ALIGN16_SLACK) == 0)
|
||||
#define ROUNDUP_16(n) (((n) + ALIGN16_SLACK) & ~(word32)ALIGN16_SLACK)
|
||||
#ifndef TI_BUFFSIZE
|
||||
#define TI_BUFFSIZE 1024
|
||||
#endif
|
||||
|
|
@ -479,6 +485,41 @@ static void AesAuthSetIv(Aes *aes, const byte *nonce, word32 len, word32 L,
|
|||
}
|
||||
}
|
||||
|
||||
/* Allocates a 16 byte aligned, whole block sized temporary for sz bytes and,
|
||||
* when src is not NULL, copies src into it. XMALLOC only guarantees alignment
|
||||
* for fundamental types, which can be as little as 4 bytes, so over-allocate
|
||||
* and align by hand. *save receives the pointer to free, *aligned the pointer
|
||||
* to hand to the ROM engine. */
|
||||
static int AesAuthBounce(const byte* src, word32 sz, void* heap, byte** save,
|
||||
byte** aligned)
|
||||
{
|
||||
byte* p;
|
||||
|
||||
/* ROUNDUP_16 wraps within a block of the word32 maximum, which would
|
||||
* under-allocate. Unreachable with any real buffer, but the size
|
||||
* arithmetic below must not be able to wrap. */
|
||||
if (sz > (0xFFFFFFFFU - (2U * (word32)ALIGN16_SLACK))) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
p = (byte*)XMALLOC(ROUNDUP_16(sz) + ALIGN16_SLACK, heap,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (p == NULL) {
|
||||
return MEMORY_E;
|
||||
}
|
||||
|
||||
*save = p;
|
||||
*aligned = (byte*)(((wc_ptr_t)p + ALIGN16_SLACK) &
|
||||
~(wc_ptr_t)ALIGN16_SLACK);
|
||||
|
||||
XMEMSET(*aligned, 0, ROUNDUP_16(sz));
|
||||
if (src != NULL) {
|
||||
XMEMCPY(*aligned, src, sz);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int AesAuthEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz,
|
||||
const byte* nonce, word32 nonceSz,
|
||||
byte* authTag, word32 authTagSz,
|
||||
|
|
@ -516,33 +557,31 @@ static int AesAuthEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure all pointers are 16 byte aligned */
|
||||
if (IS_ALIGN16(inSz)) {
|
||||
in_save = NULL; in_a = (byte*)in;
|
||||
out_save = NULL; out_a = out;
|
||||
/* Bounce each buffer that is not 16 byte aligned, and each buffer whose
|
||||
* length is not a whole number of blocks. The alignment of in, out and
|
||||
* authIn is independent, so each gets its own temporary. */
|
||||
if (inSz > 0 && (!IS_ALIGN16(in) || !IS_MULT16(inSz))) {
|
||||
ret = AesAuthBounce(in, inSz, aes->heap, &in_save, &in_a);
|
||||
if (ret != 0) { goto exit; }
|
||||
}
|
||||
else {
|
||||
in_save = XMALLOC(ROUNDUP_16(inSz), NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (in_save == NULL) { ret = MEMORY_E; goto exit; }
|
||||
in_a = in_save;
|
||||
XMEMSET(in_a, 0, ROUNDUP_16(inSz));
|
||||
XMEMCPY(in_a, in, inSz);
|
||||
|
||||
out_save = XMALLOC(ROUNDUP_16(inSz), NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (out_save == NULL) { ret = MEMORY_E; goto exit; }
|
||||
out_a = out_save;
|
||||
in_a = (byte*)in;
|
||||
}
|
||||
|
||||
if (IS_ALIGN16(authInSz)) {
|
||||
authIn_save = NULL; authIn_a = (byte*)authIn;
|
||||
if (inSz > 0 && (!IS_ALIGN16(out) || !IS_MULT16(inSz))) {
|
||||
ret = AesAuthBounce(NULL, inSz, aes->heap, &out_save, &out_a);
|
||||
if (ret != 0) { goto exit; }
|
||||
}
|
||||
else {
|
||||
authIn_save = XMALLOC(ROUNDUP_16(authInSz), NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (authIn_save == NULL) { ret = MEMORY_E; goto exit; }
|
||||
out_a = out;
|
||||
}
|
||||
|
||||
authIn_a = authIn_save;
|
||||
XMEMSET(authIn_a, 0, ROUNDUP_16(authInSz));
|
||||
XMEMCPY(authIn_a, authIn, authInSz);
|
||||
if (authInSz > 0 && (!IS_ALIGN16(authIn) || !IS_MULT16(authInSz))) {
|
||||
ret = AesAuthBounce(authIn, authInSz, aes->heap, &authIn_save, &authIn_a);
|
||||
if (ret != 0) { goto exit; }
|
||||
}
|
||||
else {
|
||||
authIn_a = (byte*)authIn;
|
||||
}
|
||||
|
||||
/* Do AES-CCM/GCM Cipher with Auth */
|
||||
|
|
@ -565,20 +604,31 @@ static int AesAuthEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz,
|
|||
wolfSSL_TI_unlockCCM();
|
||||
|
||||
if (ret == false) {
|
||||
XMEMSET(out, 0, inSz);
|
||||
XMEMSET(authTag, 0, authTagSz);
|
||||
/* out is NULL for the GMAC case, where inSz is zero. */
|
||||
if (out != NULL)
|
||||
ForceZero(out, inSz);
|
||||
ForceZero(authTag, authTagSz);
|
||||
ret = AES_GCM_AUTH_E;
|
||||
}
|
||||
else {
|
||||
XMEMCPY(out, out_a, inSz);
|
||||
if (out_save != NULL)
|
||||
XMEMCPY(out, out_a, inSz);
|
||||
XMEMCPY(authTag, tmpTag, authTagSz);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
exit:
|
||||
XFREE(in_save, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(out_save, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(authIn_save, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
/* Whichever of the two data bounce buffers holds plaintext depends on
|
||||
* direction, so both are wiped. On the tag failure path the caller's out
|
||||
* has been cleared but out_save has not. authIn_save is exempt because
|
||||
* AAD is not secret. */
|
||||
if (in_save != NULL)
|
||||
ForceZero(in_save, ROUNDUP_16(inSz) + ALIGN16_SLACK);
|
||||
if (out_save != NULL)
|
||||
ForceZero(out_save, ROUNDUP_16(inSz) + ALIGN16_SLACK);
|
||||
XFREE(in_save, aes->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(out_save, aes->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(authIn_save, aes->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -622,33 +672,31 @@ static int AesAuthDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Make sure all pointers are 16 byte aligned */
|
||||
if (IS_ALIGN16(inSz)) {
|
||||
in_save = NULL; in_a = (byte*)in;
|
||||
out_save = NULL; out_a = out;
|
||||
/* Bounce each buffer that is not 16 byte aligned, and each buffer whose
|
||||
* length is not a whole number of blocks. The alignment of in, out and
|
||||
* authIn is independent, so each gets its own temporary. */
|
||||
if (inSz > 0 && (!IS_ALIGN16(in) || !IS_MULT16(inSz))) {
|
||||
ret = AesAuthBounce(in, inSz, aes->heap, &in_save, &in_a);
|
||||
if (ret != 0) { goto exit; }
|
||||
}
|
||||
else {
|
||||
in_save = XMALLOC(ROUNDUP_16(inSz), NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (in_save == NULL) { ret = MEMORY_E; goto exit; }
|
||||
in_a = in_save;
|
||||
XMEMSET(in_a, 0, ROUNDUP_16(inSz));
|
||||
XMEMCPY(in_a, in, inSz);
|
||||
|
||||
out_save = XMALLOC(ROUNDUP_16(inSz), NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (out_save == NULL) { ret = MEMORY_E; goto exit; }
|
||||
out_a = out_save;
|
||||
in_a = (byte*)in;
|
||||
}
|
||||
|
||||
if (IS_ALIGN16(authInSz)) {
|
||||
authIn_save = NULL; authIn_a = (byte*)authIn;
|
||||
if (inSz > 0 && (!IS_ALIGN16(out) || !IS_MULT16(inSz))) {
|
||||
ret = AesAuthBounce(NULL, inSz, aes->heap, &out_save, &out_a);
|
||||
if (ret != 0) { goto exit; }
|
||||
}
|
||||
else {
|
||||
authIn_save = XMALLOC(ROUNDUP_16(authInSz), NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (authIn_save == NULL) { ret = MEMORY_E; goto exit; }
|
||||
out_a = out;
|
||||
}
|
||||
|
||||
authIn_a = authIn_save;
|
||||
XMEMSET(authIn_a, 0, ROUNDUP_16(authInSz));
|
||||
XMEMCPY(authIn_a, authIn, authInSz);
|
||||
if (authInSz > 0 && (!IS_ALIGN16(authIn) || !IS_MULT16(authInSz))) {
|
||||
ret = AesAuthBounce(authIn, authInSz, aes->heap, &authIn_save, &authIn_a);
|
||||
if (ret != 0) { goto exit; }
|
||||
}
|
||||
else {
|
||||
authIn_a = (byte*)authIn;
|
||||
}
|
||||
|
||||
/* Do AES-CCM/GCM Cipher with Auth */
|
||||
|
|
@ -670,18 +718,29 @@ static int AesAuthDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz,
|
|||
wolfSSL_TI_unlockCCM();
|
||||
|
||||
if ((ret == false) || (ConstantCompare(authTag, tmpTag, authTagSz) != 0)) {
|
||||
XMEMSET(out, 0, inSz);
|
||||
/* out is NULL for the GMAC case, where inSz is zero. */
|
||||
if (out != NULL)
|
||||
ForceZero(out, inSz);
|
||||
ret = AES_GCM_AUTH_E;
|
||||
}
|
||||
else {
|
||||
XMEMCPY(out, out_a, inSz);
|
||||
if (out_save != NULL)
|
||||
XMEMCPY(out, out_a, inSz);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
exit:
|
||||
XFREE(in_save, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(out_save, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(authIn_save, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
/* Whichever of the two data bounce buffers holds plaintext depends on
|
||||
* direction, so both are wiped. On the tag failure path the caller's out
|
||||
* has been cleared but out_save has not. authIn_save is exempt because
|
||||
* AAD is not secret. */
|
||||
if (in_save != NULL)
|
||||
ForceZero(in_save, ROUNDUP_16(inSz) + ALIGN16_SLACK);
|
||||
if (out_save != NULL)
|
||||
ForceZero(out_save, ROUNDUP_16(inSz) + ALIGN16_SLACK);
|
||||
XFREE(in_save, aes->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(out_save, aes->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
XFREE(authIn_save, aes->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue