ARMv8: fix configurations

Poly1305: poly1305_block() is void for ARMv8 ASM
SHA512: explicitly include cryptocb.h.
AES: set keylen for EVP API
AES: Implement streaming AES-GCM.
pull/4434/head
Sean Parkinson 2021-09-30 10:33:08 +10:00
parent f4be011b91
commit 38cfec89e8
3 changed files with 650 additions and 1 deletions

View File

@ -786,11 +786,15 @@ int wc_Poly1305Update(Poly1305* ctx, const byte* m, word32 bytes)
/* process full blocks */
if (bytes >= POLY1305_BLOCK_SIZE) {
int ret;
size_t want = (bytes & ~(POLY1305_BLOCK_SIZE - 1));
#if !defined(WOLFSSL_ARMASM) || !defined(__aarch64__)
int ret;
ret = poly1305_blocks(ctx, m, want);
if (ret != 0)
return ret;
#else
poly1305_blocks(ctx, m, want);
#endif
m += want;
bytes -= (word32)want;
}

View File

@ -176,6 +176,7 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen,
aes->left = 0;
#endif /* WOLFSSL_AES_COUNTER */
aes->keylen = keylen;
aes->rounds = keylen/4 + 6;
XMEMCPY(rk, userKey, keylen);
@ -4381,6 +4382,643 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz,
#endif /* aarch64 */
#ifdef HAVE_AESGCM
#ifdef WOLFSSL_AESGCM_STREAM
/* Access initialization counter data. */
#define AES_INITCTR(aes) ((aes)->streamData + 0 * AES_BLOCK_SIZE)
/* Access counter data. */
#define AES_COUNTER(aes) ((aes)->streamData + 1 * AES_BLOCK_SIZE)
/* Access tag data. */
#define AES_TAG(aes) ((aes)->streamData + 2 * AES_BLOCK_SIZE)
/* Access last GHASH block. */
#define AES_LASTGBLOCK(aes) ((aes)->streamData + 3 * AES_BLOCK_SIZE)
/* Access last encrypted block. */
#define AES_LASTBLOCK(aes) ((aes)->streamData + 4 * AES_BLOCK_SIZE)
/* GHASH one block of data.
*
* XOR block into tag and GMULT with H.
*
* @param [in, out] aes AES GCM object.
* @param [in] block Block of AAD or cipher text.
*/
#define GHASH_ONE_BLOCK(aes, block) \
do { \
xorbuf(AES_TAG(aes), block, AES_BLOCK_SIZE); \
GMULT(AES_TAG(aes), aes->H); \
} \
while (0)
/* Hash in the lengths of the AAD and cipher text in bits.
*
* Default implementation.
*
* @param [in, out] aes AES GCM object.
*/
#define GHASH_LEN_BLOCK(aes) \
do { \
byte scratch[AES_BLOCK_SIZE]; \
FlattenSzInBits(&scratch[0], aes->aSz); \
FlattenSzInBits(&scratch[8], aes->cSz); \
GHASH_ONE_BLOCK(aes, scratch); \
} \
while (0)
static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz)
{
int i;
for (i = ctrSz-1; i >= 0; i--) {
if (++ctr[i])
break;
}
}
/* Initialize a GHASH for streaming operations.
*
* @param [in, out] aes AES GCM object.
*/
static void GHASH_INIT(Aes* aes) {
/* Set tag to all zeros as initial value. */
XMEMSET(AES_TAG(aes), 0, AES_BLOCK_SIZE);
/* Reset counts of AAD and cipher text. */
aes->aOver = 0;
aes->cOver = 0;
}
/* Update the GHASH with AAD and/or cipher text.
*
* @param [in,out] aes AES GCM object.
* @param [in] a Additional authentication data buffer.
* @param [in] aSz Size of data in AAD buffer.
* @param [in] c Cipher text buffer.
* @param [in] cSz Size of data in cipher text buffer.
*/
static void GHASH_UPDATE(Aes* aes, const byte* a, word32 aSz, const byte* c,
word32 cSz)
{
word32 blocks;
word32 partial;
/* Hash in A, the Additional Authentication Data */
if (aSz != 0 && a != NULL) {
/* Update count of AAD we have hashed. */
aes->aSz += aSz;
/* Check if we have unprocessed data. */
if (aes->aOver > 0) {
/* Calculate amount we can use - fill up the block. */
byte sz = AES_BLOCK_SIZE - aes->aOver;
if (sz > aSz) {
sz = aSz;
}
/* Copy extra into last GHASH block array and update count. */
XMEMCPY(AES_LASTGBLOCK(aes) + aes->aOver, a, sz);
aes->aOver += sz;
if (aes->aOver == AES_BLOCK_SIZE) {
/* We have filled up the block and can process. */
GHASH_ONE_BLOCK(aes, AES_LASTGBLOCK(aes));
/* Reset count. */
aes->aOver = 0;
}
/* Used up some data. */
aSz -= sz;
a += sz;
}
/* Calculate number of blocks of AAD and the leftover. */
blocks = aSz / AES_BLOCK_SIZE;
partial = aSz % AES_BLOCK_SIZE;
/* GHASH full blocks now. */
while (blocks--) {
GHASH_ONE_BLOCK(aes, a);
a += AES_BLOCK_SIZE;
}
if (partial != 0) {
/* Cache the partial block. */
XMEMCPY(AES_LASTGBLOCK(aes), a, partial);
aes->aOver = (byte)partial;
}
}
if (aes->aOver > 0 && cSz > 0 && c != NULL) {
/* No more AAD coming and we have a partial block. */
/* Fill the rest of the block with zeros. */
byte sz = AES_BLOCK_SIZE - aes->aOver;
XMEMSET(AES_LASTGBLOCK(aes) + aes->aOver, 0, sz);
/* GHASH last AAD block. */
GHASH_ONE_BLOCK(aes, AES_LASTGBLOCK(aes));
/* Clear partial count for next time through. */
aes->aOver = 0;
}
/* Hash in C, the Ciphertext */
if (cSz != 0 && c != NULL) {
/* Update count of cipher text we have hashed. */
aes->cSz += cSz;
if (aes->cOver > 0) {
/* Calculate amount we can use - fill up the block. */
byte sz = AES_BLOCK_SIZE - aes->cOver;
if (sz > cSz) {
sz = cSz;
}
XMEMCPY(AES_LASTGBLOCK(aes) + aes->cOver, c, sz);
/* Update count of unsed encrypted counter. */
aes->cOver += sz;
if (aes->cOver == AES_BLOCK_SIZE) {
/* We have filled up the block and can process. */
GHASH_ONE_BLOCK(aes, AES_LASTGBLOCK(aes));
/* Reset count. */
aes->cOver = 0;
}
/* Used up some data. */
cSz -= sz;
c += sz;
}
/* Calculate number of blocks of cipher text and the leftover. */
blocks = cSz / AES_BLOCK_SIZE;
partial = cSz % AES_BLOCK_SIZE;
/* GHASH full blocks now. */
while (blocks--) {
GHASH_ONE_BLOCK(aes, c);
c += AES_BLOCK_SIZE;
}
if (partial != 0) {
/* Cache the partial block. */
XMEMCPY(AES_LASTGBLOCK(aes), c, partial);
aes->cOver = (byte)partial;
}
}
}
/* Finalize the GHASH calculation.
*
* Complete hashing cipher text and hash the AAD and cipher text lengths.
*
* @param [in, out] aes AES GCM object.
* @param [out] s Authentication tag.
* @param [in] sSz Size of authentication tag required.
*/
static void GHASH_FINAL(Aes* aes, byte* s, word32 sSz)
{
/* AAD block incomplete when > 0 */
byte over = aes->aOver;
if (aes->cOver > 0) {
/* Cipher text block incomplete. */
over = aes->cOver;
}
if (over > 0) {
/* Zeroize the unused part of the block. */
XMEMSET(AES_LASTGBLOCK(aes) + over, 0, AES_BLOCK_SIZE - over);
/* Hash the last block of cipher text. */
GHASH_ONE_BLOCK(aes, AES_LASTGBLOCK(aes));
}
/* Hash in the lengths of AAD and cipher text in bits */
GHASH_LEN_BLOCK(aes);
/* Copy the result into s. */
XMEMCPY(s, AES_TAG(aes), sSz);
}
/* Initialize the AES GCM cipher with an IV. C implementation.
*
* @param [in, out] aes AES object.
* @param [in] iv IV/nonce buffer.
* @param [in] ivSz Length of IV/nonce data.
*/
static void AesGcmInit_C(Aes* aes, const byte* iv, word32 ivSz)
{
ALIGN32 byte counter[AES_BLOCK_SIZE];
if (ivSz == GCM_NONCE_MID_SZ) {
/* Counter is IV with bottom 4 bytes set to: 0x00,0x00,0x00,0x01. */
XMEMCPY(counter, iv, ivSz);
XMEMSET(counter + GCM_NONCE_MID_SZ, 0,
AES_BLOCK_SIZE - GCM_NONCE_MID_SZ - 1);
counter[AES_BLOCK_SIZE - 1] = 1;
}
else {
/* Counter is GHASH of IV. */
#ifdef OPENSSL_EXTRA
word32 aadTemp = aes->aadLen;
aes->aadLen = 0;
#endif
GHASH(aes, NULL, 0, iv, ivSz, counter, AES_BLOCK_SIZE);
GMULT(counter, aes->H);
#ifdef OPENSSL_EXTRA
aes->aadLen = aadTemp;
#endif
}
/* Copy in the counter for use with cipher. */
XMEMCPY(AES_COUNTER(aes), counter, AES_BLOCK_SIZE);
/* Encrypt initial counter into a buffer for GCM. */
wc_AesEncrypt(aes, counter, AES_INITCTR(aes));
/* Reset state fields. */
aes->over = 0;
aes->aSz = 0;
aes->cSz = 0;
/* Initialization for GHASH. */
GHASH_INIT(aes);
}
/* Update the AES GCM cipher with data. C implementation.
*
* Only enciphers data.
*
* @param [in, out] aes AES object.
* @param [in] out Cipher text or plaintext buffer.
* @param [in] in Plaintext or cipher text buffer.
* @param [in] sz Length of data.
*/
static void AesGcmCryptUpdate_C(Aes* aes, byte* out, const byte* in, word32 sz)
{
word32 blocks;
word32 partial;
/* Check if previous encrypted block was not used up. */
if (aes->over > 0) {
byte pSz = AES_BLOCK_SIZE - aes->over;
if (pSz > sz) pSz = sz;
/* Use some/all of last encrypted block. */
xorbufout(out, AES_LASTBLOCK(aes) + aes->over, in, pSz);
aes->over = (aes->over + pSz) & (AES_BLOCK_SIZE - 1);
/* Some data used. */
sz -= pSz;
in += pSz;
out += pSz;
}
/* Calculate the number of blocks needing to be encrypted and any leftover.
*/
blocks = sz / AES_BLOCK_SIZE;
partial = sz & (AES_BLOCK_SIZE - 1);
/* Encrypt block by block. */
while (blocks--) {
ALIGN32 byte scratch[AES_BLOCK_SIZE];
IncrementGcmCounter(AES_COUNTER(aes));
/* Encrypt counter into a buffer. */
wc_AesEncrypt(aes, AES_COUNTER(aes), scratch);
/* XOR plain text into encrypted counter into cipher text buffer. */
xorbufout(out, scratch, in, AES_BLOCK_SIZE);
/* Data complete. */
in += AES_BLOCK_SIZE;
out += AES_BLOCK_SIZE;
}
if (partial != 0) {
/* Generate an extra block and use up as much as needed. */
IncrementGcmCounter(AES_COUNTER(aes));
/* Encrypt counter into cache. */
wc_AesEncrypt(aes, AES_COUNTER(aes), AES_LASTBLOCK(aes));
/* XOR plain text into encrypted counter into cipher text buffer. */
xorbufout(out, AES_LASTBLOCK(aes), in, partial);
/* Keep amount of encrypted block used. */
aes->over = partial;
}
}
/* Calculates authentication tag for AES GCM. C implementation.
*
* @param [in, out] aes AES object.
* @param [out] authTag Buffer to store authentication tag in.
* @param [in] authTagSz Length of tag to create.
*/
static void AesGcmFinal_C(Aes* aes, byte* authTag, word32 authTagSz)
{
/* Calculate authentication tag. */
GHASH_FINAL(aes, authTag, authTagSz);
/* XOR in as much of encrypted counter as is required. */
xorbuf(authTag, AES_INITCTR(aes), authTagSz);
#ifdef OPENSSL_EXTRA
/* store AAD size for next call */
aes->aadLen = aes->aSz;
#endif
/* Zeroize last block to protect sensitive data. */
ForceZero(AES_LASTBLOCK(aes), AES_BLOCK_SIZE);
}
/* Initialize an AES GCM cipher for encryption or decryption.
*
* Must call wc_AesInit() before calling this function.
*
* @param [in, out] aes AES object.
* @param [in] key Buffer holding key.
* @param [in] len Length of key in bytes.
* @param [in] iv Buffer holding IV/nonce.
* @param [in] ivSz Length of IV/nonce in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when aes is NULL, or a length is non-zero but buffer
* is NULL, or the IV is NULL and no previous IV has been set.
* @return MEMORY_E when dynamic memory allocation fails. (WOLFSSL_SMALL_STACK)
*/
int wc_AesGcmInit(Aes* aes, const byte* key, word32 len, const byte* iv,
word32 ivSz)
{
int ret = 0;
/* Check validity of parameters. */
if ((aes == NULL) || ((len > 0) && (key == NULL)) ||
((ivSz == 0) && (iv != NULL)) || (ivSz > AES_BLOCK_SIZE) ||
((ivSz > 0) && (iv == NULL))) {
ret = BAD_FUNC_ARG;
}
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_AESNI)
if ((ret == 0) && (aes->streamData == NULL)) {
/* Allocate buffers for streaming. */
aes->streamData = (byte*)XMALLOC(5 * AES_BLOCK_SIZE, aes->heap,
DYNAMIC_TYPE_AES);
if (aes->streamData == NULL) {
ret = MEMORY_E;
}
}
#endif
/* Set the key if passed in. */
if ((ret == 0) && (key != NULL)) {
ret = wc_AesGcmSetKey(aes, key, len);
}
if (ret == 0) {
/* Setup with IV if needed. */
if (iv != NULL) {
/* Cache the IV in AES GCM object. */
XMEMCPY((byte*)aes->reg, iv, ivSz);
aes->nonceSz = ivSz;
}
else if (aes->nonceSz != 0) {
/* Copy out the cached copy. */
iv = (byte*)aes->reg;
ivSz = aes->nonceSz;
}
if (iv != NULL) {
/* Initialize with the IV. */
AesGcmInit_C(aes, iv, ivSz);
aes->nonceSet = 1;
}
}
return ret;
}
/* Initialize an AES GCM cipher for encryption.
*
* Must call wc_AesInit() before calling this function.
*
* @param [in, out] aes AES object.
* @param [in] key Buffer holding key.
* @param [in] len Length of key in bytes.
* @param [in] iv Buffer holding IV/nonce.
* @param [in] ivSz Length of IV/nonce in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when aes is NULL, or a length is non-zero but buffer
* is NULL, or the IV is NULL and no previous IV has been set.
*/
int wc_AesGcmEncryptInit(Aes* aes, const byte* key, word32 len, const byte* iv,
word32 ivSz)
{
return wc_AesGcmInit(aes, key, len, iv, ivSz);
}
/* Initialize an AES GCM cipher for encryption or decryption. Get IV.
*
* Must call wc_AesInit() before calling this function.
*
* @param [in, out] aes AES object.
* @param [in] key Buffer holding key.
* @param [in] len Length of key in bytes.
* @param [in] iv Buffer holding IV/nonce.
* @param [in] ivSz Length of IV/nonce in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when aes is NULL, or a length is non-zero but buffer
* is NULL, or the IV is NULL and no previous IV has been set.
*/
int wc_AesGcmEncryptInit_ex(Aes* aes, const byte* key, word32 len, byte* ivOut,
word32 ivOutSz)
{
XMEMCPY(ivOut, aes->reg, ivOutSz);
return wc_AesGcmInit(aes, key, len, NULL, 0);
}
/* Update the AES GCM for encryption with data and/or authentication data.
*
* All the AAD must be passed to update before the plaintext.
* Last part of AAD can be passed with first part of plaintext.
*
* Must set key and IV before calling this function.
* Must call wc_AesGcmInit() before calling this function.
*
* @param [in, out] aes AES object.
* @param [out] out Buffer to hold cipher text.
* @param [in] in Buffer holding plaintext.
* @param [in] sz Length of plaintext in bytes.
* @param [in] authIn Buffer holding authentication data.
* @param [in] authInSz Length of authentication data in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when aes is NULL, or a length is non-zero but buffer
* is NULL.
*/
int wc_AesGcmEncryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz,
const byte* authIn, word32 authInSz)
{
int ret = 0;
/* Check validity of parameters. */
if ((aes == NULL) || ((authInSz > 0) && (authIn == NULL)) || ((sz > 0) &&
((out == NULL) || (in == NULL)))) {
ret = BAD_FUNC_ARG;
}
/* Check key has been set. */
if ((ret == 0) && (!aes->gcmKeySet)) {
ret = MISSING_KEY;
}
/* Check IV has been set. */
if ((ret == 0) && (!aes->nonceSet)) {
ret = MISSING_IV;
}
if ((ret == 0) && aes->ctrSet && (aes->aSz == 0) && (aes->cSz == 0)) {
aes->invokeCtr[0]++;
if (aes->invokeCtr[0] == 0) {
aes->invokeCtr[1]++;
if (aes->invokeCtr[1] == 0)
ret = AES_GCM_OVERFLOW_E;
}
}
if (ret == 0) {
/* Encrypt the plaintext. */
AesGcmCryptUpdate_C(aes, out, in, sz);
/* Update the authenication tag with any authentication data and the
* new cipher text. */
GHASH_UPDATE(aes, authIn, authInSz, out, sz);
}
return ret;
}
/* Finalize the AES GCM for encryption and return the authentication tag.
*
* Must set key and IV before calling this function.
* Must call wc_AesGcmInit() before calling this function.
*
* @param [in, out] aes AES object.
* @param [out] authTag Buffer to hold authentication tag.
* @param [in] authTagSz Length of authentication tag in bytes.
* @return 0 on success.
*/
int wc_AesGcmEncryptFinal(Aes* aes, byte* authTag, word32 authTagSz)
{
int ret = 0;
/* Check validity of parameters. */
if ((aes == NULL) || (authTag == NULL) || (authTagSz > AES_BLOCK_SIZE) ||
(authTagSz == 0)) {
ret = BAD_FUNC_ARG;
}
/* Check key has been set. */
if ((ret == 0) && (!aes->gcmKeySet)) {
ret = MISSING_KEY;
}
/* Check IV has been set. */
if ((ret == 0) && (!aes->nonceSet)) {
ret = MISSING_IV;
}
if (ret == 0) {
/* Calculate authentication tag. */
AesGcmFinal_C(aes, authTag, authTagSz);
}
if ((ret == 0) && aes->ctrSet) {
IncCtr((byte*)aes->reg, aes->nonceSz);
}
return ret;
}
#if defined(HAVE_AES_DECRYPT) || defined(HAVE_AESGCM_DECRYPT)
/* Initialize an AES GCM cipher for decryption.
*
* Must call wc_AesInit() before calling this function.
*
* @param [in, out] aes AES object.
* @param [in] key Buffer holding key.
* @param [in] len Length of key in bytes.
* @param [in] iv Buffer holding IV/nonce.
* @param [in] ivSz Length of IV/nonce in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when aes is NULL, or a length is non-zero but buffer
* is NULL, or the IV is NULL and no previous IV has been set.
*/
int wc_AesGcmDecryptInit(Aes* aes, const byte* key, word32 len, const byte* iv,
word32 ivSz)
{
return wc_AesGcmInit(aes, key, len, iv, ivSz);
}
/* Update the AES GCM for decryption with data and/or authentication data.
*
* All the AAD must be passed to update before the cipher text.
* Last part of AAD can be passed with first part of cipher text.
*
* Must set key and IV before calling this function.
* Must call wc_AesGcmInit() before calling this function.
*
* @param [in, out] aes AES object.
* @param [out] out Buffer to hold plaintext.
* @param [in] in Buffer holding cipher text.
* @param [in] sz Length of cipher text in bytes.
* @param [in] authIn Buffer holding authentication data.
* @param [in] authInSz Length of authentication data in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when aes is NULL, or a length is non-zero but buffer
* is NULL.
*/
int wc_AesGcmDecryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz,
const byte* authIn, word32 authInSz)
{
int ret = 0;
/* Check validity of parameters. */
if ((aes == NULL) || ((authInSz > 0) && (authIn == NULL)) || ((sz > 0) &&
((out == NULL) || (in == NULL)))) {
ret = BAD_FUNC_ARG;
}
/* Check key has been set. */
if ((ret == 0) && (!aes->gcmKeySet)) {
ret = MISSING_KEY;
}
/* Check IV has been set. */
if ((ret == 0) && (!aes->nonceSet)) {
ret = MISSING_IV;
}
if (ret == 0) {
/* Decrypt with AAD and/or cipher text. */
/* Update the authenication tag with any authentication data and
* cipher text. */
GHASH_UPDATE(aes, authIn, authInSz, in, sz);
/* Decrypt the cipher text. */
AesGcmCryptUpdate_C(aes, out, in, sz);
}
return ret;
}
/* Finalize the AES GCM for decryption and check the authentication tag.
*
* Must set key and IV before calling this function.
* Must call wc_AesGcmInit() before calling this function.
*
* @param [in, out] aes AES object.
* @param [in] authTag Buffer holding authentication tag.
* @param [in] authTagSz Length of authentication tag in bytes.
* @return 0 on success.
*/
int wc_AesGcmDecryptFinal(Aes* aes, const byte* authTag, word32 authTagSz)
{
int ret = 0;
/* Check validity of parameters. */
if ((aes == NULL) || (authTag == NULL) || (authTagSz > AES_BLOCK_SIZE) ||
(authTagSz == 0)) {
ret = BAD_FUNC_ARG;
}
/* Check key has been set. */
if ((ret == 0) && (!aes->gcmKeySet)) {
ret = MISSING_KEY;
}
/* Check IV has been set. */
if ((ret == 0) && (!aes->nonceSet)) {
ret = MISSING_IV;
}
if (ret == 0) {
/* Calculate authentication tag and compare with one passed in.. */
ALIGN32 byte calcTag[AES_BLOCK_SIZE];
/* Calculate authentication tag. */
AesGcmFinal_C(aes, calcTag, authTagSz);
/* Check calculated tag matches the one passed in. */
if (ConstantCompare(authTag, calcTag, authTagSz) != 0) {
ret = AES_GCM_AUTH_E;
}
}
return ret;
}
#endif /* HAVE_AES_DECRYPT || HAVE_AESGCM_DECRYPT */
#endif /* WOLFSSL_AESGCM_STREAM */
#endif /* HAVE_AESGCM */
#ifdef HAVE_AESCCM
/* Software version of AES-CCM from wolfcrypt/src/aes.c
@ -4641,6 +5279,10 @@ int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len)
ret = wc_AesSetKey(aes, key, len, iv, AES_ENCRYPTION);
if (ret == 0) {
#ifdef WOLFSSL_AESGCM_STREAM
aes->gcmKeySet = 1;
#endif
wc_AesEncrypt(aes, iv, aes->H);
#if defined(__aarch64__)
{

View File

@ -44,6 +44,9 @@
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
#ifdef WOLF_CRYPTO_CB
#include <wolfssl/wolfcrypt/cryptocb.h>
#endif
#ifdef WOLFSSL_SHA512