|
| 1 | +/* asu_cmac.c |
| 2 | + * |
| 3 | + * Copyright (C) 2006-2026 wolfSSL Inc. |
| 4 | + * |
| 5 | + * This file is part of wolfSSL. |
| 6 | + * |
| 7 | + * wolfSSL is free software; you can redistribute it and/or modify |
| 8 | + * it under the terms of the GNU General Public License as published by |
| 9 | + * the Free Software Foundation; either version 3 of the License, or |
| 10 | + * (at your option) any later version. |
| 11 | + * |
| 12 | + * wolfSSL is distributed in the hope that it will be useful, |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | + * GNU General Public License for more details. |
| 16 | + * |
| 17 | + * You should have received a copy of the GNU General Public License |
| 18 | + * along with this program; if not, write to the Free Software |
| 19 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA |
| 20 | + */ |
| 21 | + |
| 22 | +/* ASU AES-CMAC: the ASU needs the whole message, in 16-byte blocks, in one call, |
| 23 | + * so each Cmac context buffers its message and runs one ASU op at finalize. */ |
| 24 | + |
| 25 | +#ifdef HAVE_CONFIG_H |
| 26 | + #include <config.h> |
| 27 | +#endif |
| 28 | + |
| 29 | +#include <wolfssl/wolfcrypt/settings.h> |
| 30 | + |
| 31 | +#ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC |
| 32 | + |
| 33 | +#include <wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cmac.h> |
| 34 | +#include <wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_util.h> |
| 35 | +#include <wolfssl/wolfcrypt/error-crypt.h> |
| 36 | +#include <wolfssl/wolfcrypt/aes.h> |
| 37 | +#include <wolfssl/wolfcrypt/cmac.h> |
| 38 | +#include <wolfssl/wolfcrypt/hash.h> |
| 39 | + |
| 40 | +#ifdef NO_INLINE |
| 41 | + #include <wolfssl/wolfcrypt/misc.h> |
| 42 | +#else |
| 43 | + #define WOLFSSL_MISC_INCLUDED |
| 44 | + #include <wolfcrypt/src/misc.c> |
| 45 | +#endif |
| 46 | + |
| 47 | +#include "xasu_aes.h" |
| 48 | +#include "xasu_aesinfo.h" |
| 49 | +#include "xasu_def.h" |
| 50 | +#include "xasu_status.h" |
| 51 | +#include "xstatus.h" |
| 52 | + |
| 53 | +#ifndef WOLFSSL_HASH_KEEP |
| 54 | + #error "WOLFSSL_VERSAL_GEN2_ASU_CMAC requires WOLFSSL_HASH_KEEP (_wc_Hash_Grow)" |
| 55 | +#endif |
| 56 | + |
| 57 | +/* Per CMAC context state, held in the wolfSSL Cmac devCtx: the key captured at |
| 58 | + * init and the message accumulated across updates. */ |
| 59 | +typedef struct { |
| 60 | + byte* msg; /* accumulated message */ |
| 61 | + word32 used; /* bytes accumulated */ |
| 62 | + word32 len; /* buffer capacity */ |
| 63 | + byte key[AES_MAX_KEY_SIZE / 8]; |
| 64 | + word32 keyLen; |
| 65 | +} AsuCmacKeep; |
| 66 | + |
| 67 | +/* One ASU AES request: the params block and the key object it points at. */ |
| 68 | +typedef struct { |
| 69 | + XAsu_AesParams params; |
| 70 | + XAsu_AesKeyObject keyObj; |
| 71 | +} AsuCmacReq; |
| 72 | + |
| 73 | +/* Hands one CMAC request to the ASU queue. wc_AsuTransact calls this while it |
| 74 | + * holds the submit lock, so this must only queue the request and return. */ |
| 75 | +static int wc_AsuCmacSubmit(XAsu_ClientParams* params, void* ctx) |
| 76 | +{ |
| 77 | + AsuCmacReq* req = (AsuCmacReq*)ctx; |
| 78 | + |
| 79 | + if (params == NULL || req == NULL) { |
| 80 | + return XST_FAILURE; |
| 81 | + } |
| 82 | + |
| 83 | + return XAsu_AesOperation(params, &req->params); |
| 84 | +} |
| 85 | + |
| 86 | +/* Map a wolfSSL key length to the ASU key size. Returns CRYPTOCB_UNAVAILABLE for |
| 87 | + * AES-192 and any other unsupported length so wolfSSL falls back to software. */ |
| 88 | +static int wc_AsuCmacKeySize(word32 keyLen, u32* keySize) |
| 89 | +{ |
| 90 | + if (keySize == NULL) { |
| 91 | + return BAD_FUNC_ARG; |
| 92 | + } |
| 93 | + if (keyLen == XASU_AES_KEY_SIZE_128BIT_IN_BYTES) { |
| 94 | + *keySize = XASU_AES_KEY_SIZE_128_BITS; |
| 95 | + return 0; |
| 96 | + } |
| 97 | + if (keyLen == XASU_AES_KEY_SIZE_256BIT_IN_BYTES) { |
| 98 | + *keySize = XASU_AES_KEY_SIZE_256_BITS; |
| 99 | + return 0; |
| 100 | + } |
| 101 | + |
| 102 | + return CRYPTOCB_UNAVAILABLE; |
| 103 | +} |
| 104 | + |
| 105 | +/* Free one context's saved key and message. Both are sensitive, so they are |
| 106 | + * zeroed before being freed. */ |
| 107 | +static void wc_AsuCmacKeepFree(AsuCmacKeep* keep) |
| 108 | +{ |
| 109 | + if (keep == NULL) { |
| 110 | + return; |
| 111 | + } |
| 112 | + if (keep->msg != NULL) { |
| 113 | + ForceZero(keep->msg, keep->len); |
| 114 | + XFREE(keep->msg, NULL, DYNAMIC_TYPE_TMP_BUFFER); |
| 115 | + } |
| 116 | + ForceZero(keep, sizeof(*keep)); |
| 117 | + XFREE(keep, NULL, DYNAMIC_TYPE_TMP_BUFFER); |
| 118 | +} |
| 119 | + |
| 120 | +/* Compute the 16-byte CMAC over the whole message in one ASU operation. Caller |
| 121 | + * has checked the key size and that msgLen is non-zero and block aligned. */ |
| 122 | +static int wc_AsuCmacHw(const byte* key, word32 keyLen, u32 keySize, |
| 123 | + const byte* msg, word32 msgLen, byte* tag) |
| 124 | +{ |
| 125 | + AsuCmacReq req; |
| 126 | + word32 addl = 0; |
| 127 | + word32 status; |
| 128 | + byte iv[XASU_AES_IV_SIZE_128BIT_IN_BYTES]; |
| 129 | + |
| 130 | + if (key == NULL || msg == NULL || tag == NULL || msgLen == 0) { |
| 131 | + return BAD_FUNC_ARG; |
| 132 | + } |
| 133 | + |
| 134 | + /* CMAC has no IV but the firmware loads one for every non-ECB mode, so pass |
| 135 | + * the all-zero CBC-MAC start vector; a zero IvLen would make it DMA 0 bytes. */ |
| 136 | + XMEMSET(iv, 0, sizeof(iv)); |
| 137 | + |
| 138 | + XMEMSET(&req, 0, sizeof(req)); |
| 139 | + req.keyObj.KeyAddress = (u64)(UINTPTR)key; |
| 140 | + req.keyObj.KeySize = keySize; |
| 141 | + req.keyObj.KeySrc = XASU_AES_USER_KEY_0; |
| 142 | + |
| 143 | + req.params.KeyObjectAddr = (u64)(UINTPTR)&req.keyObj; |
| 144 | + req.params.AadAddr = (u64)(UINTPTR)msg; |
| 145 | + req.params.AadLen = msgLen; |
| 146 | + req.params.IvAddr = (u64)(UINTPTR)iv; |
| 147 | + req.params.IvLen = XASU_AES_IV_SIZE_128BIT_IN_BYTES; |
| 148 | + req.params.TagAddr = (u64)(UINTPTR)tag; |
| 149 | + req.params.TagLen = XASU_AES_MAX_TAG_LENGTH_IN_BYTES; |
| 150 | + req.params.EngineMode = (u8)XASU_AES_CMAC_MODE; |
| 151 | + req.params.OperationFlags = |
| 152 | + (u8)(XASU_AES_INIT | XASU_AES_UPDATE | XASU_AES_FINAL); |
| 153 | + req.params.IsLast = (u8)XASU_TRUE; |
| 154 | + req.params.OperationType = (u8)XASU_AES_ENCRYPT_OPERATION; |
| 155 | + |
| 156 | + WC_ASU_PRINTF("[ASU] cmac mode=%d keyLen=%u msgLen=%u tag=%u\r\n", |
| 157 | + (int)XASU_AES_CMAC_MODE, (unsigned int)keyLen, (unsigned int)msgLen, |
| 158 | + (unsigned int)XASU_AES_MAX_TAG_LENGTH_IN_BYTES); |
| 159 | + |
| 160 | + /* The ASU DMAs the key object, key, IV and message from memory; invalidate |
| 161 | + * the tag afterwards so the CPU sees the DMA'd result. */ |
| 162 | + wc_AsuCacheFlush(key, keyLen); |
| 163 | + wc_AsuCacheFlush(&req.keyObj, sizeof(req.keyObj)); |
| 164 | + wc_AsuCacheFlush(iv, sizeof(iv)); |
| 165 | + wc_AsuCacheFlush(msg, msgLen); |
| 166 | + |
| 167 | + wc_AsuCacheFlush(tag, XASU_AES_MAX_TAG_LENGTH_IN_BYTES); |
| 168 | + |
| 169 | + status = wc_AsuTransact(wc_AsuCmacSubmit, &req, &addl); |
| 170 | + |
| 171 | + wc_AsuCacheInvalidate(tag, XASU_AES_MAX_TAG_LENGTH_IN_BYTES); |
| 172 | + |
| 173 | + if (status != XST_SUCCESS) { |
| 174 | + return WC_HW_E; |
| 175 | + } |
| 176 | + if (addl != (word32)XASU_AES_TAG_READ) { |
| 177 | + return WC_HW_E; |
| 178 | + } |
| 179 | + |
| 180 | + return 0; |
| 181 | +} |
| 182 | + |
| 183 | +/* Produce the tag: offload a whole-block message to the ASU, else compute in |
| 184 | + * software over the same buffer. outSz is the caller's (maybe truncated) size. */ |
| 185 | +static int wc_AsuCmacProduce(const byte* key, word32 keyLen, const byte* msg, |
| 186 | + word32 msgLen, byte* out, word32* outSz) |
| 187 | +{ |
| 188 | + /* Own a whole cache line: the ASU DMAs the tag here while the response |
| 189 | + * interrupt writes nearby stack, so a shared line could stamp stale bytes. */ |
| 190 | + ALIGN64 byte tag[XASU_AES_MAX_TAG_LENGTH_IN_BYTES]; |
| 191 | + u32 keySize = 0; |
| 192 | + int ret; |
| 193 | + |
| 194 | + if (key == NULL || out == NULL || outSz == NULL || |
| 195 | + (msg == NULL && msgLen > 0)) { |
| 196 | + return BAD_FUNC_ARG; |
| 197 | + } |
| 198 | + if (*outSz < WC_CMAC_TAG_MIN_SZ || *outSz > WC_CMAC_TAG_MAX_SZ) { |
| 199 | + return BUFFER_E; |
| 200 | + } |
| 201 | + |
| 202 | + /* Whole-block, non-empty message with a supported key runs on the ASU. */ |
| 203 | + if ((msgLen != 0) && ((msgLen % XASU_AES_BLOCK_SIZE_IN_BYTES) == 0) && |
| 204 | + (msgLen <= XASU_ASU_DMA_MAX_TRANSFER_LENGTH) && |
| 205 | + (wc_AsuCmacKeySize(keyLen, &keySize) == 0)) { |
| 206 | + ret = wc_AsuCmacHw(key, keyLen, keySize, msg, msgLen, tag); |
| 207 | + if (ret != 0) { |
| 208 | + return ret; |
| 209 | + } |
| 210 | + XMEMCPY(out, tag, *outSz); |
| 211 | + ForceZero(tag, sizeof(tag)); |
| 212 | + return 0; |
| 213 | + } |
| 214 | + |
| 215 | + /* Empty or non whole-block message: wolfSSL skipped its own setup when we |
| 216 | + * accepted at init, so compute the whole CMAC in software right here. */ |
| 217 | + return wc_AesCmacGenerate(out, outSz, msg, msgLen, key, keyLen); |
| 218 | +} |
| 219 | + |
| 220 | +/* Single entry point for the CMAC engine, reached through the crypto callback |
| 221 | + * dispatcher (WC_ALGO_TYPE_CMAC). */ |
| 222 | +int wc_AsuCmac(wc_CryptoInfo* info) |
| 223 | +{ |
| 224 | + Cmac* cmac; |
| 225 | + AsuCmacKeep* keep; |
| 226 | + int ret; |
| 227 | + |
| 228 | + if (info == NULL) { |
| 229 | + return BAD_FUNC_ARG; |
| 230 | + } |
| 231 | + |
| 232 | + /* Context free: release any buffer this context stored in devCtx. Reached |
| 233 | + * via WC_ALGO_TYPE_FREE when a context is freed without being finalized. */ |
| 234 | + if (info->algo_type == WC_ALGO_TYPE_FREE) { |
| 235 | + cmac = (Cmac*)info->free.obj; |
| 236 | + if (cmac != NULL && cmac->devCtx != NULL) { |
| 237 | + wc_AsuCmacKeepFree((AsuCmacKeep*)cmac->devCtx); |
| 238 | + cmac->devCtx = NULL; |
| 239 | + } |
| 240 | + return CRYPTOCB_UNAVAILABLE; |
| 241 | + } |
| 242 | + |
| 243 | + if (info->cmac.cmac == NULL) { |
| 244 | + return BAD_FUNC_ARG; |
| 245 | + } |
| 246 | + if (info->cmac.type != WC_CMAC_AES) { |
| 247 | + return CRYPTOCB_UNAVAILABLE; |
| 248 | + } |
| 249 | + cmac = info->cmac.cmac; |
| 250 | + |
| 251 | + /* Single-call generate (key, message and output all supplied). A whole-block |
| 252 | + * message goes to the ASU; anything else runs in wolfSSL software. */ |
| 253 | + if (info->cmac.key != NULL && info->cmac.out != NULL) { |
| 254 | + u32 keySize = 0; |
| 255 | + if ((info->cmac.inSz == 0) || |
| 256 | + ((info->cmac.inSz % XASU_AES_BLOCK_SIZE_IN_BYTES) != 0) || |
| 257 | + (info->cmac.inSz > XASU_ASU_DMA_MAX_TRANSFER_LENGTH) || |
| 258 | + (wc_AsuCmacKeySize(info->cmac.keySz, &keySize) != 0)) { |
| 259 | + return CRYPTOCB_UNAVAILABLE; |
| 260 | + } |
| 261 | + if ((info->cmac.outSz == NULL) || |
| 262 | + (*info->cmac.outSz < WC_CMAC_TAG_MIN_SZ) || |
| 263 | + (*info->cmac.outSz > WC_CMAC_TAG_MAX_SZ)) { |
| 264 | + return CRYPTOCB_UNAVAILABLE; |
| 265 | + } |
| 266 | + return wc_AsuCmacProduce(info->cmac.key, info->cmac.keySz, |
| 267 | + info->cmac.in, info->cmac.inSz, info->cmac.out, info->cmac.outSz); |
| 268 | + } |
| 269 | + |
| 270 | + /* init(): save the key and start this context's buffer. AES-192 and other |
| 271 | + * unsupported key sizes are refused so wolfSSL does the whole CMAC itself. */ |
| 272 | + if (info->cmac.key != NULL) { |
| 273 | + u32 keySize = 0; |
| 274 | + if (info->cmac.keySz > sizeof(((AsuCmacKeep*)0)->key) || |
| 275 | + (wc_AsuCmacKeySize(info->cmac.keySz, &keySize) != 0)) { |
| 276 | + return CRYPTOCB_UNAVAILABLE; |
| 277 | + } |
| 278 | + keep = (AsuCmacKeep*)XMALLOC(sizeof(AsuCmacKeep), NULL, |
| 279 | + DYNAMIC_TYPE_TMP_BUFFER); |
| 280 | + if (keep == NULL) { |
| 281 | + return MEMORY_E; |
| 282 | + } |
| 283 | + XMEMSET(keep, 0, sizeof(*keep)); |
| 284 | + XMEMCPY(keep->key, info->cmac.key, info->cmac.keySz); |
| 285 | + keep->keyLen = info->cmac.keySz; |
| 286 | + cmac->devCtx = keep; |
| 287 | + /* wc_InitCmac returns early on our success and skips its own type setup, |
| 288 | + * so persist it; later update/final read cmac->type to reach us. */ |
| 289 | + cmac->type = info->cmac.type; |
| 290 | + return 0; |
| 291 | + } |
| 292 | + |
| 293 | + keep = (AsuCmacKeep*)cmac->devCtx; |
| 294 | + |
| 295 | + /* update(): add this chunk to the buffered message. A NULL devCtx means init |
| 296 | + * was refused, so let wolfSSL run this in software. */ |
| 297 | + if (info->cmac.in != NULL && info->cmac.out == NULL) { |
| 298 | + if (keep == NULL) { |
| 299 | + return CRYPTOCB_UNAVAILABLE; |
| 300 | + } |
| 301 | + return _wc_Hash_Grow(&keep->msg, &keep->used, &keep->len, |
| 302 | + info->cmac.in, (int)info->cmac.inSz, NULL); |
| 303 | + } |
| 304 | + |
| 305 | + /* final(): run the CMAC over the whole buffered message, then release the |
| 306 | + * buffer. A NULL devCtx means init was refused; let software handle it. */ |
| 307 | + if (info->cmac.out != NULL) { |
| 308 | + if (keep == NULL) { |
| 309 | + return CRYPTOCB_UNAVAILABLE; |
| 310 | + } |
| 311 | + ret = wc_AsuCmacProduce(keep->key, keep->keyLen, keep->msg, keep->used, |
| 312 | + info->cmac.out, info->cmac.outSz); |
| 313 | + wc_AsuCmacKeepFree(keep); |
| 314 | + cmac->devCtx = NULL; |
| 315 | + return ret; |
| 316 | + } |
| 317 | + |
| 318 | + return CRYPTOCB_UNAVAILABLE; |
| 319 | +} |
| 320 | + |
| 321 | +#endif /* WOLFSSL_VERSAL_GEN2_ASU_CMAC */ |
0 commit comments