diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index daccfc4d144..e0c9a731c21 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1091,6 +1091,8 @@ WOLFSSL_VA416X0_TRNG WOLFSSL_VALIDATE_DH_KEYGEN WOLFSSL_VAULTIC_DEBUG WOLFSSL_VERSAL_GEN2_ASU +WOLFSSL_VERSAL_GEN2_ASU_CCM_ALIGN_DECLINE +WOLFSSL_VERSAL_GEN2_ASU_CTR_WRAP_HW_FIXED WOLFSSL_VERSAL_GEN2_ASU_RTC WOLFSSL_VERSAL_GEN2_ASU_TRNG_DIRECT WOLFSSL_WC_SLHDSA_RECURSIVE diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c new file mode 100644 index 00000000000..5119c451f14 --- /dev/null +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c @@ -0,0 +1,740 @@ +/* asu_cipher.c + * + * Copyright (C) 2006-2026 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 3 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 + */ + +/* ASU AES engine (CBC/ECB/CTR/CFB/OFB/GCM/CCM). Each call runs as one complete + * ASU operation; AES-192 and partial blocks fall back to software. */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef WOLFSSL_VERSAL_GEN2_ASU_CIPHER + +#include +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#include "xasu_aes.h" +#include "xasu_aesinfo.h" +#include "xasu_def.h" +#include "xasu_status.h" +#include "xstatus.h" + +/* One ASU AES request: the params block and the key object it points at. */ +typedef struct { + XAsu_AesParams params; + XAsu_AesKeyObject keyObj; +} AsuCipherReq; + +/* Hands one AES request to the ASU queue. wc_AsuTransact calls this while it + * holds the submit lock, so this must only queue the request and return. */ +static int wc_AsuCipherSubmit(XAsu_ClientParams* params, void* ctx) +{ + AsuCipherReq* req = (AsuCipherReq*)ctx; + + if (params == NULL || req == NULL) { + return XST_FAILURE; + } + + return XAsu_AesOperation(params, &req->params); +} + +/* Map a wolfSSL key length to the ASU key size. Returns CRYPTOCB_UNAVAILABLE for + * AES-192 and any other unsupported length so wolfSSL falls back to software. */ +static int wc_AsuCipherKeySize(word32 keyLen, u32* keySize) +{ + if (keySize == NULL) { + return BAD_FUNC_ARG; + } + if (keyLen == XASU_AES_KEY_SIZE_128BIT_IN_BYTES) { + *keySize = XASU_AES_KEY_SIZE_128_BITS; + return 0; + } + if (keyLen == XASU_AES_KEY_SIZE_256BIT_IN_BYTES) { + *keySize = XASU_AES_KEY_SIZE_256_BITS; + return 0; + } + + return CRYPTOCB_UNAVAILABLE; +} + +/* Run one AES operation on the ASU. iv is NULL for ECB. Empty input, sizes that + * are not whole blocks, and unsupported keys fall back to software. */ +static int wc_AsuCipherOneShot(Aes* aes, byte* out, const byte* in, word32 sz, + int enc, u8 engineMode, const byte* iv) +{ + AsuCipherReq req; + u32 keySize = 0; + word32 status; + int ret; + + if (aes == NULL || out == NULL || in == NULL) { + return BAD_FUNC_ARG; + } + /* The ASU only takes whole 16-byte blocks up to the DMA limit; any other + * length runs in software. */ + if (sz == 0 || (sz % XASU_AES_BLOCK_SIZE_IN_BYTES) != 0 || + sz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH) { + return CRYPTOCB_UNAVAILABLE; + } + + ret = wc_AsuCipherKeySize(aes->keylen, &keySize); + if (ret != 0) { + return ret; + } + + XMEMSET(&req, 0, sizeof(req)); + req.keyObj.KeyAddress = (u64)(UINTPTR)aes->devKey; + req.keyObj.KeySize = keySize; + req.keyObj.KeySrc = XASU_AES_USER_KEY_0; + + req.params.InputDataAddr = (u64)(UINTPTR)in; + req.params.OutputDataAddr = (u64)(UINTPTR)out; + req.params.KeyObjectAddr = (u64)(UINTPTR)&req.keyObj; + req.params.DataLen = sz; + req.params.EngineMode = engineMode; + req.params.OperationFlags = + (u8)(XASU_AES_INIT | XASU_AES_UPDATE | XASU_AES_FINAL); + req.params.IsLast = (u8)XASU_TRUE; + if (enc) { + req.params.OperationType = (u8)XASU_AES_ENCRYPT_OPERATION; + } + else { + req.params.OperationType = (u8)XASU_AES_DECRYPT_OPERATION; + } + if (iv != NULL) { + req.params.IvAddr = (u64)(UINTPTR)iv; + req.params.IvLen = XASU_AES_IV_SIZE_128BIT_IN_BYTES; + } + + WC_ASU_PRINTF("[ASU] cipher mode=%d enc=%d keyLen=%u sz=%u\r\n", + (int)engineMode, enc, (unsigned int)aes->keylen, (unsigned int)sz); + + /* The ASU reads the key object, key, IV and input straight from RAM, so + * flush our cached copies out first; drop the cached output after the op. */ + wc_AsuCacheFlush(aes->devKey, aes->keylen); + wc_AsuCacheFlush(&req.keyObj, sizeof(req.keyObj)); + if (iv != NULL) { + wc_AsuCacheFlush(iv, XASU_AES_IV_SIZE_128BIT_IN_BYTES); + } + wc_AsuCacheFlush(in, sz); + wc_AsuCacheFlush(out, sz); + + status = wc_AsuTransact(wc_AsuCipherSubmit, &req, NULL); + if (status != XST_SUCCESS) { + return WC_HW_E; + } + + wc_AsuCacheInvalidate(out, sz); + + return 0; +} + +/* AES-CBC. The IV comes from aes->reg and, on success, is updated to the last + * ciphertext block so a chained call continues correctly. */ +static int wc_AsuCipherCbc(wc_CryptoInfo* info) +{ + byte* ctr; + int ret; + byte lastBlock[WC_AES_BLOCK_SIZE]; + + /* Reference info->cipher.aescbc fields directly; no aliasing locals. */ + if (info == NULL || info->cipher.aescbc.aes == NULL || + info->cipher.aescbc.out == NULL || info->cipher.aescbc.in == NULL) { + return BAD_FUNC_ARG; + } + if (info->cipher.aescbc.sz == 0 || + (info->cipher.aescbc.sz % WC_AES_BLOCK_SIZE) != 0) { + return CRYPTOCB_UNAVAILABLE; + } + + /* The CBC chaining IV lives in aes->reg. For decrypt the next IV is the last + * input block; save it now because out may be the same buffer as in. */ + ctr = (byte*)info->cipher.aescbc.aes->reg; + if (!info->cipher.enc) { + XMEMCPY(lastBlock, info->cipher.aescbc.in + + (info->cipher.aescbc.sz - WC_AES_BLOCK_SIZE), WC_AES_BLOCK_SIZE); + } + + ret = wc_AsuCipherOneShot(info->cipher.aescbc.aes, info->cipher.aescbc.out, + info->cipher.aescbc.in, info->cipher.aescbc.sz, info->cipher.enc, + (u8)XASU_AES_CBC_MODE, ctr); + if (ret != 0) { + return ret; + } + + /* Update aes->reg to the last ciphertext block for a chained call. */ + if (info->cipher.enc) { + XMEMCPY(ctr, info->cipher.aescbc.out + + (info->cipher.aescbc.sz - WC_AES_BLOCK_SIZE), WC_AES_BLOCK_SIZE); + } + else { + XMEMCPY(ctr, lastBlock, WC_AES_BLOCK_SIZE); + } + + return 0; +} + +/* AES-ECB. No IV and no chaining state. */ +static int wc_AsuCipherEcb(wc_CryptoInfo* info) +{ + if (info == NULL || info->cipher.aesecb.aes == NULL || + info->cipher.aesecb.out == NULL || info->cipher.aesecb.in == NULL) { + return BAD_FUNC_ARG; + } + + return wc_AsuCipherOneShot(info->cipher.aesecb.aes, info->cipher.aesecb.out, + info->cipher.aesecb.in, info->cipher.aesecb.sz, info->cipher.enc, + (u8)XASU_AES_ECB_MODE, NULL); +} + +#ifdef WOLFSSL_AES_COUNTER +/* Add n to the 16-byte counter, starting at the last byte and carrying toward + * the first, so it matches wolfSSL's software counter. */ +static void wc_AsuCtrAdd(byte* ctr, word32 n) +{ + word32 carry = n; + int i; + for (i = WC_AES_BLOCK_SIZE - 1; (i >= 0) && (carry != 0); i--) { + carry += (word32)ctr[i]; + ctr[i] = (byte)carry; + carry >>= 8; + } +} + +/* AES-CTR. The ASU only counts the last 4 bytes of the counter and cannot carry + * past them, so we split the work there and add the carry in software. */ +static int wc_AsuCipherCtr(wc_CryptoInfo* info) +{ + byte* ctr; + word32 blocks; + int ret; + + if (info == NULL || info->cipher.aesctr.aes == NULL || + info->cipher.aesctr.out == NULL || info->cipher.aesctr.in == NULL) { + return BAD_FUNC_ARG; + } + /* Partly-used keystream from an earlier call lives in the Aes context and + * the ASU always starts fresh, so those calls run in software. */ + if (info->cipher.aesctr.aes->left != 0 || info->cipher.aesctr.sz == 0 || + (info->cipher.aesctr.sz % WC_AES_BLOCK_SIZE) != 0 || + info->cipher.aesctr.sz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH) { + return CRYPTOCB_UNAVAILABLE; + } + + ctr = (byte*)info->cipher.aesctr.aes->reg; + blocks = info->cipher.aesctr.sz / WC_AES_BLOCK_SIZE; + + /* Default: do the split. Define this macro on a firmware that carries the + * whole 128-bit counter to skip the split and use one hardware call. */ +#ifndef WOLFSSL_VERSAL_GEN2_ASU_CTR_WRAP_HW_FIXED + { + word32 off = 0; + word32 remaining = blocks; + while (remaining != 0) { + word32 low = ((word32)ctr[12] << 24) | ((word32)ctr[13] << 16) | + ((word32)ctr[14] << 8) | (word32)ctr[15]; + /* How many blocks fit before the last 4 bytes roll over. */ + word64 toWrap = (word64)0x100000000ULL - (word64)low; + word32 chunk = ((word64)remaining < toWrap) ? remaining + : (word32)toWrap; + /* CTR encrypt and decrypt are the same op, so always run encrypt. */ + ret = wc_AsuCipherOneShot(info->cipher.aesctr.aes, + info->cipher.aesctr.out + off, info->cipher.aesctr.in + off, + chunk * WC_AES_BLOCK_SIZE, 1, (u8)XASU_AES_CTR_MODE, ctr); + if (ret != 0) { + return ret; + } + wc_AsuCtrAdd(ctr, chunk); + off += chunk * WC_AES_BLOCK_SIZE; + remaining -= chunk; + } + } +#else + ret = wc_AsuCipherOneShot(info->cipher.aesctr.aes, info->cipher.aesctr.out, + info->cipher.aesctr.in, info->cipher.aesctr.sz, 1, + (u8)XASU_AES_CTR_MODE, ctr); + if (ret != 0) { + return ret; + } + wc_AsuCtrAdd(ctr, blocks); +#endif + + return 0; +} +#endif /* WOLFSSL_AES_COUNTER */ + +#ifdef WOLFSSL_AES_CFB +/* AES-CFB: the feedback register is the last ciphertext block, so the aes->reg + * update matches CBC. Decline leftover keystream and non block-aligned sizes. */ +static int wc_AsuCipherCfb(wc_CryptoInfo* info) +{ + byte* ctr; + int ret; + byte lastBlock[WC_AES_BLOCK_SIZE]; + + if (info == NULL || info->cipher.aescfb.aes == NULL || + info->cipher.aescfb.out == NULL || info->cipher.aescfb.in == NULL) { + return BAD_FUNC_ARG; + } + /* Partly-used keystream from an earlier call lives in the Aes context and + * the ASU always starts fresh, so those calls run in software. */ + if (info->cipher.aescfb.aes->left != 0 || info->cipher.aescfb.sz == 0 || + (info->cipher.aescfb.sz % WC_AES_BLOCK_SIZE) != 0) { + return CRYPTOCB_UNAVAILABLE; + } + + /* The feedback IV lives in aes->reg. For decrypt the next IV is the last + * input block; save it now because out may be the same buffer as in. */ + ctr = (byte*)info->cipher.aescfb.aes->reg; + if (!info->cipher.enc) { + XMEMCPY(lastBlock, info->cipher.aescfb.in + + (info->cipher.aescfb.sz - WC_AES_BLOCK_SIZE), WC_AES_BLOCK_SIZE); + } + + ret = wc_AsuCipherOneShot(info->cipher.aescfb.aes, info->cipher.aescfb.out, + info->cipher.aescfb.in, info->cipher.aescfb.sz, info->cipher.enc, + (u8)XASU_AES_CFB_MODE, ctr); + if (ret != 0) { + return ret; + } + + /* Update aes->reg to the last ciphertext block for a chained call. */ + if (info->cipher.enc) { + XMEMCPY(ctr, info->cipher.aescfb.out + + (info->cipher.aescfb.sz - WC_AES_BLOCK_SIZE), WC_AES_BLOCK_SIZE); + } + else { + XMEMCPY(ctr, lastBlock, WC_AES_BLOCK_SIZE); + } + + return 0; +} +#endif /* WOLFSSL_AES_CFB */ + +#ifdef WOLFSSL_AES_OFB +/* AES-OFB: the feedback register is the keystream block, recovered after the op + * as out XOR in. Symmetric, so always driven as encrypt; stream limits as CTR. */ +static int wc_AsuCipherOfb(wc_CryptoInfo* info) +{ + byte* ctr; + byte* lastOut; + int ret; + word32 i; + byte lastIn[WC_AES_BLOCK_SIZE]; + + if (info == NULL || info->cipher.aesofb.aes == NULL || + info->cipher.aesofb.out == NULL || info->cipher.aesofb.in == NULL) { + return BAD_FUNC_ARG; + } + /* Partly-used keystream from an earlier call lives in the Aes context and + * the ASU always starts fresh, so those calls run in software. */ + if (info->cipher.aesofb.aes->left != 0 || info->cipher.aesofb.sz == 0 || + (info->cipher.aesofb.sz % WC_AES_BLOCK_SIZE) != 0) { + return CRYPTOCB_UNAVAILABLE; + } + + /* The keystream is out XOR in; save the last input block now because out may + * be the same buffer as in, then recover the keystream after the op. */ + ctr = (byte*)info->cipher.aesofb.aes->reg; + XMEMCPY(lastIn, info->cipher.aesofb.in + + (info->cipher.aesofb.sz - WC_AES_BLOCK_SIZE), WC_AES_BLOCK_SIZE); + + ret = wc_AsuCipherOneShot(info->cipher.aesofb.aes, info->cipher.aesofb.out, + info->cipher.aesofb.in, info->cipher.aesofb.sz, 1, + (u8)XASU_AES_OFB_MODE, ctr); + if (ret != 0) { + /* lastIn holds a plaintext block on encrypt; scrub before returning. */ + ForceZero(lastIn, sizeof(lastIn)); + return ret; + } + + /* Update aes->reg to the last keystream block (out XOR in) for a chained call. */ + lastOut = info->cipher.aesofb.out + (info->cipher.aesofb.sz - WC_AES_BLOCK_SIZE); + for (i = 0; i < WC_AES_BLOCK_SIZE; i++) { + ctr[i] = (byte)(lastOut[i] ^ lastIn[i]); + } + ForceZero(lastIn, sizeof(lastIn)); + + return 0; +} +#endif /* WOLFSSL_AES_OFB */ + +#ifdef HAVE_AESGCM +/* AES-GCM in a single call (key, IV, AAD and tag together). Encrypt and decrypt + * share one struct layout, so aesgcm_enc reads either direction's fields. */ +static int wc_AsuCipherGcm(wc_CryptoInfo* info) +{ + AsuCipherReq req; + u32 keySize = 0; + word32 addl = 0; + word32 status; + int ret; + + if (info == NULL || info->cipher.aesgcm_enc.aes == NULL || + info->cipher.aesgcm_enc.iv == NULL || + info->cipher.aesgcm_enc.ivSz == 0 || + info->cipher.aesgcm_enc.authTag == NULL) { + return BAD_FUNC_ARG; + } + /* Data/AAD buffers must be valid when their length is non-zero. */ + if ((info->cipher.aesgcm_enc.sz != 0) && + ((info->cipher.aesgcm_enc.in == NULL) || + (info->cipher.aesgcm_enc.out == NULL))) { + return BAD_FUNC_ARG; + } + if ((info->cipher.aesgcm_enc.authInSz != 0) && + (info->cipher.aesgcm_enc.authIn == NULL)) { + return BAD_FUNC_ARG; + } + + /* The ASU GCM engine needs at least one byte of data or AAD; the empty + * message with empty AAD (tag-only) case runs in software. */ + if (info->cipher.aesgcm_enc.sz == 0 && info->cipher.aesgcm_enc.authInSz == 0) { + return CRYPTOCB_UNAVAILABLE; + } + + /* The ASU GCM engine takes a 96-bit or 128-bit IV, whole 16-byte data/AAD + * within the DMA limit, and an 8..16 byte tag; anything else runs in software. */ + if ((info->cipher.aesgcm_enc.sz % XASU_AES_BLOCK_SIZE_IN_BYTES) != 0 || + (info->cipher.aesgcm_enc.authInSz % XASU_AES_BLOCK_SIZE_IN_BYTES) != 0 || + info->cipher.aesgcm_enc.sz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH || + info->cipher.aesgcm_enc.authInSz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH || + (info->cipher.aesgcm_enc.ivSz != XASU_AES_IV_SIZE_96BIT_IN_BYTES && + info->cipher.aesgcm_enc.ivSz != XASU_AES_IV_SIZE_128BIT_IN_BYTES) || + info->cipher.aesgcm_enc.authTagSz < XASU_AES_RECOMMENDED_TAG_LENGTH_IN_BYTES || + info->cipher.aesgcm_enc.authTagSz > XASU_AES_MAX_TAG_LENGTH_IN_BYTES) { + return CRYPTOCB_UNAVAILABLE; + } + ret = wc_AsuCipherKeySize(info->cipher.aesgcm_enc.aes->keylen, &keySize); + if (ret != 0) { + return ret; + } + + XMEMSET(&req, 0, sizeof(req)); + req.keyObj.KeyAddress = (u64)(UINTPTR)info->cipher.aesgcm_enc.aes->devKey; + req.keyObj.KeySize = keySize; + req.keyObj.KeySrc = XASU_AES_USER_KEY_0; + + /* The ASU client rejects a non-zero buffer address paired with a zero + * length, so leave these at the memset-zero default when the length is 0. */ + if (info->cipher.aesgcm_enc.sz != 0) { + req.params.InputDataAddr = (u64)(UINTPTR)info->cipher.aesgcm_enc.in; + req.params.OutputDataAddr = (u64)(UINTPTR)info->cipher.aesgcm_enc.out; + } + if (info->cipher.aesgcm_enc.authInSz != 0) { + req.params.AadAddr = (u64)(UINTPTR)info->cipher.aesgcm_enc.authIn; + } + req.params.KeyObjectAddr = (u64)(UINTPTR)&req.keyObj; + req.params.IvAddr = (u64)(UINTPTR)info->cipher.aesgcm_enc.iv; + req.params.TagAddr = (u64)(UINTPTR)info->cipher.aesgcm_enc.authTag; + req.params.DataLen = info->cipher.aesgcm_enc.sz; + req.params.AadLen = info->cipher.aesgcm_enc.authInSz; + req.params.IvLen = info->cipher.aesgcm_enc.ivSz; + req.params.TagLen = info->cipher.aesgcm_enc.authTagSz; + req.params.EngineMode = (u8)XASU_AES_GCM_MODE; + req.params.OperationFlags = + (u8)(XASU_AES_INIT | XASU_AES_UPDATE | XASU_AES_FINAL); + req.params.IsLast = (u8)XASU_TRUE; + if (info->cipher.enc) { + req.params.OperationType = (u8)XASU_AES_ENCRYPT_OPERATION; + } + else { + req.params.OperationType = (u8)XASU_AES_DECRYPT_OPERATION; + } + + WC_ASU_PRINTF("[ASU] cipher mode=%d enc=%d keyLen=%u sz=%u aad=%u tag=%u\r\n", + (int)XASU_AES_GCM_MODE, info->cipher.enc, + (unsigned int)info->cipher.aesgcm_enc.aes->keylen, + (unsigned int)info->cipher.aesgcm_enc.sz, + (unsigned int)info->cipher.aesgcm_enc.authInSz, + (unsigned int)info->cipher.aesgcm_enc.authTagSz); + + /* The ASU DMAs key, IV, AAD, input (and the tag on decrypt) from memory. */ + wc_AsuCacheFlush(info->cipher.aesgcm_enc.aes->devKey, + info->cipher.aesgcm_enc.aes->keylen); + wc_AsuCacheFlush(&req.keyObj, sizeof(req.keyObj)); + wc_AsuCacheFlush(info->cipher.aesgcm_enc.iv, info->cipher.aesgcm_enc.ivSz); + if (info->cipher.aesgcm_enc.authInSz != 0) { + wc_AsuCacheFlush(info->cipher.aesgcm_enc.authIn, + info->cipher.aesgcm_enc.authInSz); + } + if (info->cipher.aesgcm_enc.sz != 0) { + wc_AsuCacheFlush(info->cipher.aesgcm_enc.in, info->cipher.aesgcm_enc.sz); + } + if (!info->cipher.enc) { + wc_AsuCacheFlush(info->cipher.aesgcm_enc.authTag, + info->cipher.aesgcm_enc.authTagSz); + } + if (info->cipher.aesgcm_enc.sz != 0) { + wc_AsuCacheFlush(info->cipher.aesgcm_enc.out, info->cipher.aesgcm_enc.sz); + } + if (info->cipher.enc) { + wc_AsuCacheFlush(info->cipher.aesgcm_enc.authTag, + info->cipher.aesgcm_enc.authTagSz); + } + + status = wc_AsuTransact(wc_AsuCipherSubmit, &req, &addl); + + /* Invalidate the CPU's view of the ASU-written output (and the tag on encrypt). */ + if (info->cipher.aesgcm_enc.sz != 0) { + wc_AsuCacheInvalidate(info->cipher.aesgcm_enc.out, + info->cipher.aesgcm_enc.sz); + } + if (info->cipher.enc) { + wc_AsuCacheInvalidate(info->cipher.aesgcm_enc.authTag, + info->cipher.aesgcm_enc.authTagSz); + } + + /* Decrypt success is confirmed by TAG_MATCHED; on a mismatch zero the + * unauthenticated plaintext the ASU already wrote to out. */ + if (!info->cipher.enc) { + if ((status == XST_SUCCESS) && + (addl == (word32)XASU_AES_TAG_MATCHED)) { + return 0; + } + if (info->cipher.aesgcm_enc.sz != 0) { + ForceZero(info->cipher.aesgcm_enc.out, info->cipher.aesgcm_enc.sz); + } + return AES_GCM_AUTH_E; + } + /* Encrypt success is confirmed by TAG_READ. */ + if ((status != XST_SUCCESS) || (addl != (word32)XASU_AES_TAG_READ)) { + return WC_HW_E; + } + + return 0; +} +#endif /* HAVE_AESGCM */ + +#ifdef HAVE_AESCCM +/* AES-CCM: the ASU takes the raw 7..13 byte nonce and even 4..16 byte tag and + * formats the B0/counter blocks itself; enc/dec share the aesccm_enc layout. */ +static int wc_AsuCipherCcm(wc_CryptoInfo* info) +{ + AsuCipherReq req; + u32 keySize = 0; + word32 addl = 0; + word32 status; + int ret; + + if (info == NULL || info->cipher.aesccm_enc.aes == NULL || + info->cipher.aesccm_enc.nonce == NULL || + info->cipher.aesccm_enc.authTag == NULL) { + return BAD_FUNC_ARG; + } + /* Data/AAD buffers must be valid when their length is non-zero. */ + if ((info->cipher.aesccm_enc.sz != 0) && + ((info->cipher.aesccm_enc.in == NULL) || + (info->cipher.aesccm_enc.out == NULL))) { + return BAD_FUNC_ARG; + } + if ((info->cipher.aesccm_enc.authInSz != 0) && + (info->cipher.aesccm_enc.authIn == NULL)) { + return BAD_FUNC_ARG; + } + /* CCM requires a 7..13 byte nonce and an even 4..16 byte tag; outside this + * the call is malformed and software rejects it identically. */ + if (info->cipher.aesccm_enc.nonceSz < XASU_AES_CCM_MIN_NONCE_LEN || + info->cipher.aesccm_enc.nonceSz > XASU_AES_CCM_MAX_NONCE_LEN || + (info->cipher.aesccm_enc.authTagSz % XASU_AES_EVEN_MODULUS) != 0 || + info->cipher.aesccm_enc.authTagSz < XASU_AES_MIN_TAG_LENGTH_IN_BYTES || + info->cipher.aesccm_enc.authTagSz > XASU_AES_MAX_TAG_LENGTH_IN_BYTES) { + return BAD_FUNC_ARG; + } + + /* Tag-only (no data, no AAD) is not something the engine accepts; software. */ + if (info->cipher.aesccm_enc.sz == 0 && info->cipher.aesccm_enc.authInSz == 0) { + return CRYPTOCB_UNAVAILABLE; + } + + /* Oversized transfers exceed the ASU DMA limit; software handles them. */ + if (info->cipher.aesccm_enc.sz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH || + info->cipher.aesccm_enc.authInSz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH) { + return CRYPTOCB_UNAVAILABLE; + } + + /* Optional: when this macro is defined, data or AAD that is not a multiple + * of 16 bytes runs in software instead of going to the ASU. */ +#ifdef WOLFSSL_VERSAL_GEN2_ASU_CCM_ALIGN_DECLINE + if ((info->cipher.aesccm_enc.sz % XASU_AES_BLOCK_SIZE_IN_BYTES) != 0 || + (info->cipher.aesccm_enc.authInSz % XASU_AES_BLOCK_SIZE_IN_BYTES) != 0) { + return CRYPTOCB_UNAVAILABLE; + } +#endif + + ret = wc_AsuCipherKeySize(info->cipher.aesccm_enc.aes->keylen, &keySize); + if (ret != 0) { + return ret; + } + + XMEMSET(&req, 0, sizeof(req)); + req.keyObj.KeyAddress = (u64)(UINTPTR)info->cipher.aesccm_enc.aes->devKey; + req.keyObj.KeySize = keySize; + req.keyObj.KeySrc = XASU_AES_USER_KEY_0; + + /* The ASU client rejects a non-zero buffer address paired with a zero + * length, so leave these at the memset-zero default when the length is 0. */ + if (info->cipher.aesccm_enc.sz != 0) { + req.params.InputDataAddr = (u64)(UINTPTR)info->cipher.aesccm_enc.in; + req.params.OutputDataAddr = (u64)(UINTPTR)info->cipher.aesccm_enc.out; + } + if (info->cipher.aesccm_enc.authInSz != 0) { + req.params.AadAddr = (u64)(UINTPTR)info->cipher.aesccm_enc.authIn; + } + req.params.KeyObjectAddr = (u64)(UINTPTR)&req.keyObj; + req.params.IvAddr = (u64)(UINTPTR)info->cipher.aesccm_enc.nonce; + req.params.TagAddr = (u64)(UINTPTR)info->cipher.aesccm_enc.authTag; + req.params.DataLen = info->cipher.aesccm_enc.sz; + req.params.AadLen = info->cipher.aesccm_enc.authInSz; + req.params.IvLen = info->cipher.aesccm_enc.nonceSz; + req.params.TagLen = info->cipher.aesccm_enc.authTagSz; + req.params.EngineMode = (u8)XASU_AES_CCM_MODE; + req.params.OperationFlags = + (u8)(XASU_AES_INIT | XASU_AES_UPDATE | XASU_AES_FINAL); + req.params.IsLast = (u8)XASU_TRUE; + if (info->cipher.enc) { + req.params.OperationType = (u8)XASU_AES_ENCRYPT_OPERATION; + } + else { + req.params.OperationType = (u8)XASU_AES_DECRYPT_OPERATION; + } + + WC_ASU_PRINTF("[ASU] cipher mode=%d enc=%d keyLen=%u sz=%u aad=%u tag=%u\r\n", + (int)XASU_AES_CCM_MODE, info->cipher.enc, + (unsigned int)info->cipher.aesccm_enc.aes->keylen, + (unsigned int)info->cipher.aesccm_enc.sz, + (unsigned int)info->cipher.aesccm_enc.authInSz, + (unsigned int)info->cipher.aesccm_enc.authTagSz); + + /* The ASU DMAs key, nonce, AAD, input (and the tag on decrypt) from memory. */ + wc_AsuCacheFlush(info->cipher.aesccm_enc.aes->devKey, + info->cipher.aesccm_enc.aes->keylen); + wc_AsuCacheFlush(&req.keyObj, sizeof(req.keyObj)); + wc_AsuCacheFlush(info->cipher.aesccm_enc.nonce, info->cipher.aesccm_enc.nonceSz); + if (info->cipher.aesccm_enc.authInSz != 0) { + wc_AsuCacheFlush(info->cipher.aesccm_enc.authIn, + info->cipher.aesccm_enc.authInSz); + } + if (info->cipher.aesccm_enc.sz != 0) { + wc_AsuCacheFlush(info->cipher.aesccm_enc.in, info->cipher.aesccm_enc.sz); + } + if (!info->cipher.enc) { + wc_AsuCacheFlush(info->cipher.aesccm_enc.authTag, + info->cipher.aesccm_enc.authTagSz); + } + if (info->cipher.aesccm_enc.sz != 0) { + wc_AsuCacheFlush(info->cipher.aesccm_enc.out, info->cipher.aesccm_enc.sz); + } + if (info->cipher.enc) { + wc_AsuCacheFlush(info->cipher.aesccm_enc.authTag, + info->cipher.aesccm_enc.authTagSz); + } + + status = wc_AsuTransact(wc_AsuCipherSubmit, &req, &addl); + + /* Invalidate the CPU's view of the ASU-written output (and the tag on encrypt). */ + if (info->cipher.aesccm_enc.sz != 0) { + wc_AsuCacheInvalidate(info->cipher.aesccm_enc.out, + info->cipher.aesccm_enc.sz); + } + if (info->cipher.enc) { + wc_AsuCacheInvalidate(info->cipher.aesccm_enc.authTag, + info->cipher.aesccm_enc.authTagSz); + } + + /* Decrypt success is confirmed by TAG_MATCHED; on a mismatch zero the + * unauthenticated plaintext the ASU already wrote to out. */ + if (!info->cipher.enc) { + if ((status == XST_SUCCESS) && + (addl == (word32)XASU_AES_TAG_MATCHED)) { + return 0; + } + if (info->cipher.aesccm_enc.sz != 0) { + ForceZero(info->cipher.aesccm_enc.out, info->cipher.aesccm_enc.sz); + } + return AES_CCM_AUTH_E; + } + /* Encrypt success is confirmed by TAG_READ. */ + if ((status != XST_SUCCESS) || (addl != (word32)XASU_AES_TAG_READ)) { + return WC_HW_E; + } + + return 0; +} +#endif /* HAVE_AESCCM */ + +/* Single entry point for the cipher engine, reached through the crypto callback + * dispatcher. */ +int wc_AsuCipher(wc_CryptoInfo* info) +{ + if (info == NULL) { + return BAD_FUNC_ARG; + } + if (info->algo_type != WC_ALGO_TYPE_CIPHER) { + return CRYPTOCB_UNAVAILABLE; + } + + switch (info->cipher.type) { + #ifdef HAVE_AES_CBC + case WC_CIPHER_AES_CBC: + return wc_AsuCipherCbc(info); + #endif + #ifdef HAVE_AES_ECB + case WC_CIPHER_AES_ECB: + return wc_AsuCipherEcb(info); + #endif + #ifdef WOLFSSL_AES_COUNTER + case WC_CIPHER_AES_CTR: + return wc_AsuCipherCtr(info); + #endif + #ifdef WOLFSSL_AES_CFB + case WC_CIPHER_AES_CFB: + return wc_AsuCipherCfb(info); + #endif + #ifdef WOLFSSL_AES_OFB + case WC_CIPHER_AES_OFB: + return wc_AsuCipherOfb(info); + #endif + #ifdef HAVE_AESGCM + case WC_CIPHER_AES_GCM: + return wc_AsuCipherGcm(info); + #endif + #ifdef HAVE_AESCCM + case WC_CIPHER_AES_CCM: + return wc_AsuCipherCcm(info); + #endif + default: + return CRYPTOCB_UNAVAILABLE; + } +} + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_CIPHER */ diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cmac.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cmac.c new file mode 100644 index 00000000000..c4ea778c231 --- /dev/null +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cmac.c @@ -0,0 +1,330 @@ +/* asu_cmac.c + * + * Copyright (C) 2006-2026 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 3 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 + */ + +/* ASU AES-CMAC: the ASU needs the whole message, in 16-byte blocks, in one call, + * so each Cmac context buffers its message and runs one ASU op at finalize. */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC + +#include +#include +#include +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#include "xasu_aes.h" +#include "xasu_aesinfo.h" +#include "xasu_def.h" +#include "xasu_status.h" +#include "xstatus.h" + +#ifndef WOLFSSL_HASH_KEEP + #error "WOLFSSL_VERSAL_GEN2_ASU_CMAC requires WOLFSSL_HASH_KEEP (_wc_Hash_Grow)" +#endif + +/* Per CMAC context state, held in the wolfSSL Cmac devCtx: the key captured at + * init and the message accumulated across updates. */ +typedef struct { + byte* msg; /* accumulated message */ + word32 used; /* bytes accumulated */ + word32 len; /* buffer capacity */ + byte key[AES_MAX_KEY_SIZE / 8]; + word32 keyLen; +} AsuCmacKeep; + +/* One ASU AES request: the params block and the key object it points at. */ +typedef struct { + XAsu_AesParams params; + XAsu_AesKeyObject keyObj; +} AsuCmacReq; + +/* Hands one CMAC request to the ASU queue. wc_AsuTransact calls this while it + * holds the submit lock, so this must only queue the request and return. */ +static int wc_AsuCmacSubmit(XAsu_ClientParams* params, void* ctx) +{ + AsuCmacReq* req = (AsuCmacReq*)ctx; + + if (params == NULL || req == NULL) { + return XST_FAILURE; + } + + return XAsu_AesOperation(params, &req->params); +} + +/* Map a wolfSSL key length to the ASU key size. Returns CRYPTOCB_UNAVAILABLE for + * AES-192 and any other unsupported length so wolfSSL falls back to software. */ +static int wc_AsuCmacKeySize(word32 keyLen, u32* keySize) +{ + if (keySize == NULL) { + return BAD_FUNC_ARG; + } + if (keyLen == XASU_AES_KEY_SIZE_128BIT_IN_BYTES) { + *keySize = XASU_AES_KEY_SIZE_128_BITS; + return 0; + } + if (keyLen == XASU_AES_KEY_SIZE_256BIT_IN_BYTES) { + *keySize = XASU_AES_KEY_SIZE_256_BITS; + return 0; + } + + return CRYPTOCB_UNAVAILABLE; +} + +/* Free one context's saved key and message. Both are sensitive, so they are + * zeroed before being freed. */ +static void wc_AsuCmacKeepFree(AsuCmacKeep* keep) +{ + if (keep == NULL) { + return; + } + if (keep->msg != NULL) { + ForceZero(keep->msg, keep->len); + XFREE(keep->msg, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + ForceZero(keep, sizeof(*keep)); + XFREE(keep, NULL, DYNAMIC_TYPE_TMP_BUFFER); +} + +/* Compute the 16-byte CMAC over the whole message in one ASU operation. Caller + * has checked the key size and that msgLen is non-zero and block aligned. */ +static int wc_AsuCmacHw(const byte* key, word32 keyLen, u32 keySize, + const byte* msg, word32 msgLen, byte* tag) +{ + AsuCmacReq req; + word32 addl = 0; + word32 status; + byte iv[XASU_AES_IV_SIZE_128BIT_IN_BYTES]; + + if (key == NULL || msg == NULL || tag == NULL || msgLen == 0) { + return BAD_FUNC_ARG; + } + + /* CMAC has no IV but the firmware loads one for every non-ECB mode, so pass + * the all-zero CBC-MAC start vector; a zero IvLen would make it DMA 0 bytes. */ + XMEMSET(iv, 0, sizeof(iv)); + + XMEMSET(&req, 0, sizeof(req)); + req.keyObj.KeyAddress = (u64)(UINTPTR)key; + req.keyObj.KeySize = keySize; + req.keyObj.KeySrc = XASU_AES_USER_KEY_0; + + req.params.KeyObjectAddr = (u64)(UINTPTR)&req.keyObj; + req.params.AadAddr = (u64)(UINTPTR)msg; + req.params.AadLen = msgLen; + req.params.IvAddr = (u64)(UINTPTR)iv; + req.params.IvLen = XASU_AES_IV_SIZE_128BIT_IN_BYTES; + req.params.TagAddr = (u64)(UINTPTR)tag; + req.params.TagLen = XASU_AES_MAX_TAG_LENGTH_IN_BYTES; + req.params.EngineMode = (u8)XASU_AES_CMAC_MODE; + req.params.OperationFlags = + (u8)(XASU_AES_INIT | XASU_AES_UPDATE | XASU_AES_FINAL); + req.params.IsLast = (u8)XASU_TRUE; + req.params.OperationType = (u8)XASU_AES_ENCRYPT_OPERATION; + + WC_ASU_PRINTF("[ASU] cmac mode=%d keyLen=%u msgLen=%u tag=%u\r\n", + (int)XASU_AES_CMAC_MODE, (unsigned int)keyLen, (unsigned int)msgLen, + (unsigned int)XASU_AES_MAX_TAG_LENGTH_IN_BYTES); + + /* The ASU DMAs the key object, key, IV and message from memory; invalidate + * the tag afterwards so the CPU sees the DMA'd result. */ + wc_AsuCacheFlush(key, keyLen); + wc_AsuCacheFlush(&req.keyObj, sizeof(req.keyObj)); + wc_AsuCacheFlush(iv, sizeof(iv)); + wc_AsuCacheFlush(msg, msgLen); + + wc_AsuCacheFlush(tag, XASU_AES_MAX_TAG_LENGTH_IN_BYTES); + + status = wc_AsuTransact(wc_AsuCmacSubmit, &req, &addl); + + wc_AsuCacheInvalidate(tag, XASU_AES_MAX_TAG_LENGTH_IN_BYTES); + + if (status != XST_SUCCESS) { + return WC_HW_E; + } + if (addl != (word32)XASU_AES_TAG_READ) { + return WC_HW_E; + } + + return 0; +} + +/* Produce the tag: offload a whole-block message to the ASU, else compute in + * software over the same buffer. outSz is the caller's (maybe truncated) size. */ +static int wc_AsuCmacProduce(const byte* key, word32 keyLen, const byte* msg, + word32 msgLen, byte* out, word32* outSz) +{ + /* Own a whole cache line: the ASU DMAs the tag here while the response + * interrupt writes nearby stack, so a shared line could stamp stale bytes. */ + ALIGN64 byte tag[XASU_AES_MAX_TAG_LENGTH_IN_BYTES]; + u32 keySize = 0; + int ret; + + if (key == NULL || out == NULL || outSz == NULL || + (msg == NULL && msgLen > 0)) { + return BAD_FUNC_ARG; + } + if (*outSz < WC_CMAC_TAG_MIN_SZ || *outSz > WC_CMAC_TAG_MAX_SZ) { + return BUFFER_E; + } + + /* Whole-block, non-empty message with a supported key runs on the ASU. */ + if ((msgLen != 0) && ((msgLen % XASU_AES_BLOCK_SIZE_IN_BYTES) == 0) && + (msgLen <= XASU_ASU_DMA_MAX_TRANSFER_LENGTH) && + (wc_AsuCmacKeySize(keyLen, &keySize) == 0)) { + ret = wc_AsuCmacHw(key, keyLen, keySize, msg, msgLen, tag); + if (ret != 0) { + return ret; + } + XMEMCPY(out, tag, *outSz); + ForceZero(tag, sizeof(tag)); + return 0; + } + + /* Empty or non whole-block message: wolfSSL skipped its own setup when we + * accepted at init, so compute the whole CMAC in software right here. */ + return wc_AesCmacGenerate(out, outSz, msg, msgLen, key, keyLen); +} + +/* Single entry point for the CMAC engine, reached through the crypto callback + * dispatcher (WC_ALGO_TYPE_CMAC). */ +int wc_AsuCmac(wc_CryptoInfo* info) +{ + Cmac* cmac; + AsuCmacKeep* keep; + int ret; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + /* Context free: release any buffer this context stored in devCtx. Reached + * via WC_ALGO_TYPE_FREE when a context is freed without being finalized. */ + if (info->algo_type == WC_ALGO_TYPE_FREE) { + cmac = (Cmac*)info->free.obj; + if (cmac != NULL && cmac->devCtx != NULL) { + wc_AsuCmacKeepFree((AsuCmacKeep*)cmac->devCtx); + cmac->devCtx = NULL; + } + return CRYPTOCB_UNAVAILABLE; + } + + if (info->cmac.cmac == NULL) { + return BAD_FUNC_ARG; + } + if (info->cmac.type != WC_CMAC_AES) { + return CRYPTOCB_UNAVAILABLE; + } + cmac = info->cmac.cmac; + + /* Single-call generate (key, message and output all supplied). A whole-block + * message goes to the ASU; anything else runs in wolfSSL software. */ + if (info->cmac.key != NULL && info->cmac.out != NULL) { + u32 keySize = 0; + if ((info->cmac.inSz == 0) || + ((info->cmac.inSz % XASU_AES_BLOCK_SIZE_IN_BYTES) != 0) || + (info->cmac.inSz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH) || + (wc_AsuCmacKeySize(info->cmac.keySz, &keySize) != 0)) { + return CRYPTOCB_UNAVAILABLE; + } + if ((info->cmac.outSz == NULL) || + (*info->cmac.outSz < WC_CMAC_TAG_MIN_SZ) || + (*info->cmac.outSz > WC_CMAC_TAG_MAX_SZ)) { + return CRYPTOCB_UNAVAILABLE; + } + return wc_AsuCmacProduce(info->cmac.key, info->cmac.keySz, + info->cmac.in, info->cmac.inSz, info->cmac.out, info->cmac.outSz); + } + + /* init(): save the key and start this context's buffer. AES-192 and other + * unsupported key sizes are refused so wolfSSL does the whole CMAC itself. */ + if (info->cmac.key != NULL) { + u32 keySize = 0; + if (info->cmac.keySz > sizeof(((AsuCmacKeep*)0)->key) || + (wc_AsuCmacKeySize(info->cmac.keySz, &keySize) != 0)) { + return CRYPTOCB_UNAVAILABLE; + } + keep = (AsuCmacKeep*)XMALLOC(sizeof(AsuCmacKeep), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (keep == NULL) { + return MEMORY_E; + } + XMEMSET(keep, 0, sizeof(*keep)); + XMEMCPY(keep->key, info->cmac.key, info->cmac.keySz); + keep->keyLen = info->cmac.keySz; + cmac->devCtx = keep; + /* wc_InitCmac returns early on our success and skips its own type setup, + * so persist it; later update/final read cmac->type to reach us. */ + cmac->type = info->cmac.type; + return 0; + } + + keep = (AsuCmacKeep*)cmac->devCtx; + + /* update(): add this chunk to the buffered message. A NULL devCtx means init + * was refused, so let wolfSSL run this in software. */ + if (info->cmac.in != NULL && info->cmac.out == NULL) { + if (keep == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + return _wc_Hash_Grow(&keep->msg, &keep->used, &keep->len, + info->cmac.in, (int)info->cmac.inSz, NULL); + } + + /* final(): run the CMAC over the whole buffered message, then release the + * buffer. A NULL devCtx means init was refused; let software handle it. */ + if (info->cmac.out != NULL) { + if (keep == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + /* A one-shot generate whose key was set at init sends the message as + * info->cmac.in; add it to the buffer here so it is not dropped. */ + if (info->cmac.in != NULL && info->cmac.inSz > 0) { + ret = _wc_Hash_Grow(&keep->msg, &keep->used, &keep->len, + info->cmac.in, (int)info->cmac.inSz, NULL); + if (ret != 0) { + return ret; + } + } + ret = wc_AsuCmacProduce(keep->key, keep->keyLen, keep->msg, keep->used, + info->cmac.out, info->cmac.outSz); + wc_AsuCmacKeepFree(keep); + cmac->devCtx = NULL; + return ret; + } + + return CRYPTOCB_UNAVAILABLE; +} + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_CMAC */ diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c index 6722e07a79d..6cc7d6d8e0e 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c @@ -40,6 +40,12 @@ #ifdef WOLFSSL_VERSAL_GEN2_ASU_HMAC #include #endif +#ifdef WOLFSSL_VERSAL_GEN2_ASU_CIPHER + #include +#endif +#ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC + #include +#endif #ifndef WOLF_CRYPTO_CB #error "WOLFSSL_VERSAL_GEN2_ASU requires WOLF_CRYPTO_CB" @@ -86,6 +92,11 @@ static int wc_AsuFree(wc_CryptoInfo* info) case WC_ALGO_TYPE_HMAC: ret = wc_AsuHmac(info); break; + #endif + #ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC + case WC_ALGO_TYPE_CMAC: + ret = wc_AsuCmac(info); + break; #endif default: break; @@ -127,9 +138,15 @@ static int wc_AsuCryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) ret = wc_AsuRng(info); #endif break; - case WC_ALGO_TYPE_CIPHER: /* M2 asu_aes */ + case WC_ALGO_TYPE_CIPHER: /* M2 asu_cipher */ + #ifdef WOLFSSL_VERSAL_GEN2_ASU_CIPHER + ret = wc_AsuCipher(info); + #endif break; - case WC_ALGO_TYPE_CMAC: /* M2 asu_aes */ + case WC_ALGO_TYPE_CMAC: /* M2 asu_cmac */ + #ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC + ret = wc_AsuCmac(info); + #endif break; case WC_ALGO_TYPE_PK: /* M3 asu_rsa and asu_ecc */ break; diff --git a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c index f2b2cba1d92..4c3a0705bcd 100644 --- a/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c +++ b/wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c @@ -96,8 +96,12 @@ word32 wc_AsuWaitDone(AsuWait* wait) * asu_settings.h) the data cache is off for the whole application, so buffer * maintenance is unnecessary and these become no ops. Otherwise the cache is on * and the port cleans inputs and invalidates outputs around each ASU access. */ + void wc_AsuCacheFlush(const void* addr, word32 len) { + if (addr == NULL || len == 0) { + return; + } #ifdef WC_ASU_DISABLE_CACHE (void)addr; (void)len; @@ -106,8 +110,13 @@ void wc_AsuCacheFlush(const void* addr, word32 len) #endif } +/* Exact extent: callers flush the buffer before the op, so the edge cache lines + * hold nothing stale to write back and neighboring data keeps its new value. */ void wc_AsuCacheInvalidate(void* addr, word32 len) { + if (addr == NULL || len == 0) { + return; + } #ifdef WC_ASU_DISABLE_CACHE (void)addr; (void)len; diff --git a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cipher.h b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cipher.h new file mode 100644 index 00000000000..1d9abcd91b0 --- /dev/null +++ b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cipher.h @@ -0,0 +1,48 @@ +/* asu_cipher.h + * + * Copyright (C) 2006-2026 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 3 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 + */ + +/* ASU symmetric cipher engine for the wolfSSL crypto callback: AES-CBC, ECB, + * CTR, CFB, OFB, GCM and CCM offload (128/256 bit keys). AES-192 uses software. */ + +#ifndef WOLFSSL_VERSAL_GEN2_ASU_CIPHER_H +#define WOLFSSL_VERSAL_GEN2_ASU_CIPHER_H + +#include + +#ifdef WOLFSSL_VERSAL_GEN2_ASU_CIPHER + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Single entry point for the cipher engine. Returns 0 on success, + * CRYPTOCB_UNAVAILABLE for software fallback, or a negative error. */ +WOLFSSL_LOCAL int wc_AsuCipher(wc_CryptoInfo* info); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_CIPHER */ + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_CIPHER_H */ diff --git a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cmac.h b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cmac.h new file mode 100644 index 00000000000..ca4908d96b7 --- /dev/null +++ b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cmac.h @@ -0,0 +1,48 @@ +/* asu_cmac.h + * + * Copyright (C) 2006-2026 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 3 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 + */ + +/* ASU AES-CMAC for the wolfSSL crypto callback: the message is accumulated per + * Cmac context and the whole CMAC is produced in one ASU operation at finalize. */ + +#ifndef WOLFSSL_VERSAL_GEN2_ASU_CMAC_H +#define WOLFSSL_VERSAL_GEN2_ASU_CMAC_H + +#include + +#ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Single entry point for the CMAC engine (WC_ALGO_TYPE_CMAC): single-call and + * init/update/final. Returns 0, CRYPTOCB_UNAVAILABLE for software, or an error. */ +WOLFSSL_LOCAL int wc_AsuCmac(wc_CryptoInfo* info); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_CMAC */ + +#endif /* WOLFSSL_VERSAL_GEN2_ASU_CMAC_H */ diff --git a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_settings.h b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_settings.h index c4eaa86770c..d7c0c8f9148 100644 --- a/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_settings.h +++ b/wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_settings.h @@ -32,7 +32,7 @@ * WOLFSSL_VERSAL_GEN2_ASU_TRNG * WOLFSSL_VERSAL_GEN2_ASU_HASH * WOLFSSL_VERSAL_GEN2_ASU_HMAC - * WOLFSSL_VERSAL_GEN2_ASU_AES + * WOLFSSL_VERSAL_GEN2_ASU_CIPHER * WOLFSSL_VERSAL_GEN2_ASU_CMAC * WOLFSSL_VERSAL_GEN2_ASU_RSA * WOLFSSL_VERSAL_GEN2_ASU_ECC @@ -54,14 +54,14 @@ #if !defined(WOLFSSL_VERSAL_GEN2_ASU_TRNG) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_HASH) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_HMAC) && \ - !defined(WOLFSSL_VERSAL_GEN2_ASU_AES) && \ + !defined(WOLFSSL_VERSAL_GEN2_ASU_CIPHER) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_CMAC) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_RSA) && \ !defined(WOLFSSL_VERSAL_GEN2_ASU_ECC) #define WOLFSSL_VERSAL_GEN2_ASU_TRNG #define WOLFSSL_VERSAL_GEN2_ASU_HASH #define WOLFSSL_VERSAL_GEN2_ASU_HMAC - #define WOLFSSL_VERSAL_GEN2_ASU_AES + #define WOLFSSL_VERSAL_GEN2_ASU_CIPHER #define WOLFSSL_VERSAL_GEN2_ASU_CMAC #define WOLFSSL_VERSAL_GEN2_ASU_RSA #define WOLFSSL_VERSAL_GEN2_ASU_ECC