/* devcrypto_aes.c * * Copyright (C) 2006-2020 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include #if !defined(NO_AES) && defined(WOLFSSL_DEVCRYPTO) #include #include #include #ifdef NO_INLINE #include #else #define WOLFSSL_MISC_INCLUDED #include #endif #if defined(HAVE_AES_CBC) && defined(WOLFSSL_DEVCRYPTO_CBC) int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { struct crypt_op crt; int ret; if (aes == NULL || out == NULL || in == NULL) { return BAD_FUNC_ARG; } /* encrypt only up to AES block size of date */ sz = sz - (sz % AES_BLOCK_SIZE); if (aes->ctx.cfd == -1) { ret = wc_DevCryptoCreate(&aes->ctx, CRYPTO_AES_CBC, (byte*)aes->devKey, aes->keylen); if (ret != 0) return ret; } wc_SetupCryptSym(&crt, &aes->ctx, (byte*)in, sz, out, (byte*)aes->reg, COP_ENCRYPT); ret = ioctl(aes->ctx.cfd, CIOCCRYPT, &crt); if (ret != 0) { return WC_DEVCRYPTO_E; } /* store iv for next call */ XMEMCPY(aes->reg, out + sz - AES_BLOCK_SIZE, AES_BLOCK_SIZE); return 0; } #ifdef HAVE_AES_DECRYPT int wc_AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { struct crypt_op crt; int ret; if (aes == NULL || out == NULL || in == NULL || sz % AES_BLOCK_SIZE != 0) { return BAD_FUNC_ARG; } XMEMCPY(aes->tmp, in + sz - AES_BLOCK_SIZE, AES_BLOCK_SIZE); if (aes->ctx.cfd == -1) { ret = wc_DevCryptoCreate(&aes->ctx, CRYPTO_AES_CBC, (byte*)aes->devKey, aes->keylen); if (ret != 0) return ret; } wc_SetupCryptSym(&crt, &aes->ctx, (byte*)in, sz, out, (byte*)aes->reg, COP_DECRYPT); ret = ioctl(aes->ctx.cfd, CIOCCRYPT, &crt); if (ret != 0) { return WC_DEVCRYPTO_E; } XMEMCPY(aes->reg, aes->tmp, AES_BLOCK_SIZE); return 0; } #endif /* HAVE_AES_DECRYPT */ #endif /* HAVE_AES_CBC && WOLFSSL_DEVCRYPTO_CBC */ #ifdef WOLFSSL_DEVCRYPTO_AES /* all AES algorithms supported */ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, const byte* iv, int dir) { #if defined(AES_MAX_KEY_SIZE) const word32 max_key_len = (AES_MAX_KEY_SIZE / 8); #endif if (aes == NULL || !((keylen == 16) || (keylen == 24) || (keylen == 32))) { return BAD_FUNC_ARG; } #if defined(AES_MAX_KEY_SIZE) /* Check key length */ if (keylen > max_key_len) { return BAD_FUNC_ARG; } #endif aes->keylen = keylen; aes->rounds = keylen/4 + 6; #ifdef WOLFSSL_AES_COUNTER aes->left = 0; #endif aes->ctx.cfd = -1; XMEMCPY(aes->devKey, userKey, keylen); (void)dir; return wc_AesSetIV(aes, iv); } /* AES-DIRECT */ #if defined(WOLFSSL_AES_DIRECT) || defined(HAVE_AES_ECB) /* common code between ECB encrypt and decrypt * returns 0 on success */ static int wc_DevCrypto_AesDirect(Aes* aes, byte* out, const byte* in, word32 sz, int dir) { int ret; struct crypt_op crt; if (aes == NULL || out == NULL || in == NULL) { return BAD_FUNC_ARG; } if (aes->ctx.cfd == -1) { ret = wc_DevCryptoCreate(&aes->ctx, CRYPTO_AES_ECB, (byte*)aes->devKey, aes->keylen); if (ret != 0) return ret; } wc_SetupCryptSym(&crt, &aes->ctx, (byte*)in, sz, out, NULL, dir); ret = ioctl(aes->ctx.cfd, CIOCCRYPT, &crt); if (ret != 0) { return WC_DEVCRYPTO_E; } return 0; } #endif #if defined(WOLFSSL_AES_DIRECT) || defined(HAVE_AESCCM) void wc_AesEncryptDirect(Aes* aes, byte* out, const byte* in) { wc_DevCrypto_AesDirect(aes, out, in, AES_BLOCK_SIZE, COP_ENCRYPT); } void wc_AesDecryptDirect(Aes* aes, byte* out, const byte* in) { wc_DevCrypto_AesDirect(aes, out, in, AES_BLOCK_SIZE, COP_DECRYPT); } int wc_AesSetKeyDirect(Aes* aes, const byte* userKey, word32 keylen, const byte* iv, int dir) { return wc_AesSetKey(aes, userKey, keylen, iv, dir); } #endif /* AES-CTR */ #if defined(WOLFSSL_AES_COUNTER) /* Increment AES counter */ static WC_INLINE void IncrementAesCounter(byte* inOutCtr) { /* in network byte order so start at end and work back */ int i; for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { if (++inOutCtr[i]) /* we're done unless we overflow */ return; } } int wc_AesCtrEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { int ret; struct crypt_op crt; byte* tmp; if (aes == NULL || out == NULL || in == NULL) { return BAD_FUNC_ARG; } /* consume any unused bytes left in aes->tmp */ tmp = (byte*)aes->tmp + AES_BLOCK_SIZE - aes->left; while (aes->left && sz) { *(out++) = *(in++) ^ *(tmp++); aes->left--; sz--; } if (aes->ctx.cfd == -1) { ret = wc_DevCryptoCreate(&aes->ctx, CRYPTO_AES_CTR, (byte*)aes->devKey, aes->keylen); if (ret != 0) return ret; } if (sz > 0) { /* clear previously leftover data */ tmp = (byte*)aes->tmp; XMEMSET(tmp, 0, AES_BLOCK_SIZE); /* update IV */ wc_SetupCryptSym(&crt, &aes->ctx, (byte*)in, sz, out, (byte*)aes->reg, COP_ENCRYPT); ret = ioctl(aes->ctx.cfd, CIOCCRYPT, &crt); if (ret != 0) { return WC_DEVCRYPTO_E; } /* adjust counter after call to hardware */ while (sz >= AES_BLOCK_SIZE) { IncrementAesCounter((byte*)aes->reg); sz -= AES_BLOCK_SIZE; out += AES_BLOCK_SIZE; in += AES_BLOCK_SIZE; } } /* create key stream for later if needed */ if (sz > 0) { Aes tmpAes; wc_AesSetKey(&tmpAes, (byte*)aes->devKey, aes->keylen, (byte*)aes->reg, AES_ENCRYPTION); wc_AesEncryptDirect(&tmpAes, (byte*)aes->tmp, (const byte*)aes->reg); wc_AesFree(&tmpAes); IncrementAesCounter((byte*)aes->reg); aes->left = AES_BLOCK_SIZE - (sz % AES_BLOCK_SIZE); } return 0; } #endif /* WOLFSSL_AES_COUNTER */ #ifdef HAVE_AESGCM int wc_AesGcmSetKey(Aes* aes, const byte* key, word32 len) { return wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION); } /* common code for AES-GCM encrypt/decrypt */ static int wc_DevCrypto_AesGcm(Aes* aes, byte* out, byte* in, word32 sz, const byte* iv, word32 ivSz, byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz, int dir) { struct crypt_auth_op crt = {0}; int ret; byte scratch[AES_BLOCK_SIZE]; /* argument checks */ if (aes == NULL || authTagSz > AES_BLOCK_SIZE) { return BAD_FUNC_ARG; } /* Account for NULL in/out buffers. Up to tag size is still written into * in/out buffers */ if (out == NULL) out = scratch; if (in == NULL) in = scratch; XMEMSET(scratch, 0, AES_BLOCK_SIZE); if (aes->ctx.cfd == -1) { ret = wc_DevCryptoCreate(&aes->ctx, CRYPTO_AES_GCM, (byte*)aes->devKey, aes->keylen); if (ret != 0) return ret; } /* if decrypting then the tag is expected to be at the end of "in" buffer */ if (dir == COP_DECRYPT) { XMEMCPY(in + sz, authTag, authTagSz); sz += authTagSz; } else{ /* get full tag from hardware */ authTagSz = AES_BLOCK_SIZE; } wc_SetupCryptAead(&crt, &aes->ctx, (byte*)in, sz, out, (byte*)iv, ivSz, dir, (byte*)authIn, authInSz, authTag, authTagSz); ret = ioctl(aes->ctx.cfd, CIOCAUTHCRYPT, &crt); if (ret != 0) { if (dir == COP_DECRYPT) { return AES_GCM_AUTH_E; } else { return WC_DEVCRYPTO_E; } } /* after encryption the tag has been placed at the end of "out" buffer */ if (dir == COP_ENCRYPT) { XMEMCPY(authTag, out + sz, authTagSz); } return 0; } /* it is assumed that "out" buffer has enough room for cipher text + tag */ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, const byte* iv, word32 ivSz, byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz) { if (authTagSz < WOLFSSL_MIN_AUTH_TAG_SZ) { WOLFSSL_MSG("GcmEncrypt authTagSz too small error"); return BAD_FUNC_ARG; } return wc_DevCrypto_AesGcm(aes, out, (byte*)in, sz, iv, ivSz, authTag, authTagSz, authIn, authInSz, COP_ENCRYPT); } #if defined(HAVE_AES_DECRYPT) || defined(HAVE_AESGCM_DECRYPT) /* it is assumed that "in" buffer has enough room for cipher text + tag */ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, const byte* iv, word32 ivSz, const byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz) { return wc_DevCrypto_AesGcm(aes, out, (byte*)in, sz, iv, ivSz, (byte*)authTag, authTagSz, authIn, authInSz, COP_DECRYPT); } #endif /* HAVE_AES_DECRYPT || HAVE_AESGCM_DECRYPT */ #endif /* HAVE_AESGCM */ #ifdef HAVE_AES_ECB int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { return wc_DevCrypto_AesDirect(aes, out, in, sz, COP_ENCRYPT); } int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { return wc_DevCrypto_AesDirect(aes, out, in, sz, COP_DECRYPT); } #endif /* HAVE_AES_ECB */ #endif /* WOLFSSL_DEVCRYPTO_AES */ #endif /* !NO_AES && WOLFSSL_DEVCRYPTO */