Skip to content

Commit 00156d7

Browse files
committed
Xilinx Versal Gen2 ASU port: AES ciphers, CMAC, GMAC offload
1 parent acff4d6 commit 00156d7

8 files changed

Lines changed: 1167 additions & 8 deletions

File tree

wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cipher.c

Lines changed: 710 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
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 */

wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_cryptocb.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
#ifdef WOLFSSL_VERSAL_GEN2_ASU_HMAC
4141
#include <wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_hmac.h>
4242
#endif
43+
#ifdef WOLFSSL_VERSAL_GEN2_ASU_CIPHER
44+
#include <wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cipher.h>
45+
#endif
46+
#ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC
47+
#include <wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cmac.h>
48+
#endif
4349

4450
#ifndef WOLF_CRYPTO_CB
4551
#error "WOLFSSL_VERSAL_GEN2_ASU requires WOLF_CRYPTO_CB"
@@ -86,6 +92,11 @@ static int wc_AsuFree(wc_CryptoInfo* info)
8692
case WC_ALGO_TYPE_HMAC:
8793
ret = wc_AsuHmac(info);
8894
break;
95+
#endif
96+
#ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC
97+
case WC_ALGO_TYPE_CMAC:
98+
ret = wc_AsuCmac(info);
99+
break;
89100
#endif
90101
default:
91102
break;
@@ -127,9 +138,15 @@ static int wc_AsuCryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx)
127138
ret = wc_AsuRng(info);
128139
#endif
129140
break;
130-
case WC_ALGO_TYPE_CIPHER: /* M2 asu_aes */
141+
case WC_ALGO_TYPE_CIPHER: /* M2 asu_cipher */
142+
#ifdef WOLFSSL_VERSAL_GEN2_ASU_CIPHER
143+
ret = wc_AsuCipher(info);
144+
#endif
131145
break;
132-
case WC_ALGO_TYPE_CMAC: /* M2 asu_aes */
146+
case WC_ALGO_TYPE_CMAC: /* M2 asu_cmac */
147+
#ifdef WOLFSSL_VERSAL_GEN2_ASU_CMAC
148+
ret = wc_AsuCmac(info);
149+
#endif
133150
break;
134151
case WC_ALGO_TYPE_PK: /* M3 asu_rsa and asu_ecc */
135152
break;

wolfcrypt/src/port/xilinx/versal_gen2_asu/asu_util.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,12 @@ word32 wc_AsuWaitDone(AsuWait* wait)
8181
* asu_settings.h) the data cache is off for the whole application, so buffer
8282
* maintenance is unnecessary and these become no ops. Otherwise the cache is on
8383
* and the port cleans inputs and invalidates outputs around each ASU access. */
84+
8485
void wc_AsuCacheFlush(const void* addr, word32 len)
8586
{
87+
if (addr == NULL || len == 0) {
88+
return;
89+
}
8690
#ifdef WC_ASU_DISABLE_CACHE
8791
(void)addr;
8892
(void)len;
@@ -91,8 +95,13 @@ void wc_AsuCacheFlush(const void* addr, word32 len)
9195
#endif
9296
}
9397

98+
/* Exact extent: callers flush the buffer before the op, so the edge cache lines
99+
* hold nothing stale to write back and neighboring data keeps its new value. */
94100
void wc_AsuCacheInvalidate(void* addr, word32 len)
95101
{
102+
if (addr == NULL || len == 0) {
103+
return;
104+
}
96105
#ifdef WC_ASU_DISABLE_CACHE
97106
(void)addr;
98107
(void)len;

0 commit comments

Comments
 (0)