Merge pull request #11252 from night1rider/asu-2026.1

Asu 2026.1
This commit is contained in:
JacobBarthelmeh
2026-08-26 17:03:46 -06:00
committed by GitHub
14 changed files with 794 additions and 99 deletions
+5 -1
View File
@@ -1129,15 +1129,17 @@ WOLFSSL_VERSAL_GEN2_ASU
WOLFSSL_VERSAL_GEN2_ASU_CCM_ALIGN_DECLINE
WOLFSSL_VERSAL_GEN2_ASU_CTR_WRAP_HW_FIXED
WOLFSSL_VERSAL_GEN2_ASU_DEBUG
WOLFSSL_VERSAL_GEN2_ASU_ECC_P521
WOLFSSL_VERSAL_GEN2_ASU_IPI_BASEADDR
WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT
WOLFSSL_VERSAL_GEN2_ASU_NO_COMP_KEY_WARN
WOLFSSL_VERSAL_GEN2_ASU_NO_ECC_P521
WOLFSSL_VERSAL_GEN2_ASU_NO_ECDH
WOLFSSL_VERSAL_GEN2_ASU_NO_ECIES
WOLFSSL_VERSAL_GEN2_ASU_NO_ED25519
WOLFSSL_VERSAL_GEN2_ASU_NO_ED448
WOLFSSL_VERSAL_GEN2_ASU_NO_RSA_PAD
WOLFSSL_VERSAL_GEN2_ASU_NO_X25519
WOLFSSL_VERSAL_GEN2_ASU_NO_X448
WOLFSSL_VERSAL_GEN2_ASU_RTC
WOLFSSL_VERSAL_GEN2_ASU_TRNG_DIRECT
WOLFSSL_WC_SLHDSA_RECURSIVE
@@ -1169,7 +1171,9 @@ WOLF_CRYPTO_DEV
WOLF_CRYPT_FIPS_H
WOLF_NO_TRAILING_ENUM_COMMAS
WindowsCE
XASU_AES_INIT
XASU_DISABLE_CACHE
XASU_INIT
XGETPASSWD
XMSS_CALL_PRF_KEYGEN
XPAR_VERSAL_CIPS_0_PSPMC_0_PSV_CORTEXA72_0_TIMESTAMP_CLK_FREQ
@@ -34,6 +34,7 @@ The BSP must have the `xilasu` and `xilmailbox` libraries enabled.
| ECDSA | NIST P-192/256/384, Brainpool P-256/320/384/512 |
| EdDSA | plain Ed25519 and Ed448 sign and verify |
| ECDH | the same curves as ECDSA |
| X25519 / X448 | key agreement, Vitis 2026.1 and later |
| ECIES | AES-GCM with HKDF-SHA256 |
| TRNG | seed and random block |
@@ -70,7 +71,10 @@ Other switches:
| `WOLFSSL_VERSAL_GEN2_ASU_IPI_BASEADDR` | IPI channel, default `XPAR_XIPIPSU_0_BASEADDR` |
| `WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT` | the application calls `XAsu_ClientInit` itself |
| `WOLFSSL_VERSAL_GEN2_ASU_NO_RSA_PAD` | RSA on, padding in software |
| `WOLFSSL_VERSAL_GEN2_ASU_ECC_P521` | add P-521, off by default, see below |
| `WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1` | the BSP has Vitis 2026.1 xilasu |
| `WOLFSSL_VERSAL_GEN2_ASU_NO_ECC_P521` | drop P-521, which 2026.1 offloads |
| `WOLFSSL_VERSAL_GEN2_ASU_NO_X25519` | drop X25519, which 2026.1 offloads |
| `WOLFSSL_VERSAL_GEN2_ASU_NO_X448` | drop X448, which 2026.1 offloads |
| `WOLFSSL_VERSAL_GEN2_ASU_DEBUG` | print every ASU operation over the UART |
| `WOLFSSL_VERSAL_GEN2_ASU_RTC` | supply the benchmark time source from the port |
| `XASU_DISABLE_CACHE` | cache is off, so skip all buffer flush and reload work |
@@ -79,11 +83,52 @@ The port sets `WOLF_CRYPTO_CB`, `WOLF_CRYPTO_CB_CMD`, `WOLF_CRYPTO_CB_COPY` and
`WOLF_CRYPTO_CB_FREE` for you, and points `WC_USE_DEVID` at the ASU device so
the unmodified wolfCrypt test and benchmark route through it.
## Which Vitis release
The xilasu client API changed in Vitis 2026.1: the operation flags became
generic, several request structures moved their key fields into key objects,
and RSA gained an output length. The port builds against either release, but it
cannot tell them apart on its own, so name the one the BSP was built with:
```c
#define WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1
```
2025.2 is assumed when neither that nor `..._XILASU_2025_2` is named, so an
existing build keeps working untouched. Naming the wrong one stops the build
with a message rather than failing later.
2026.1 also brings work the older release could not offload at all: P-521,
X25519 and X448, RSA OAEP decrypt, SHAKE output past 64 bytes, and CCM on data
that is not a whole number of blocks. Each turns on by itself there.
The X25519 and X448 offload reads the scalar and the peer point out of the
wolfSSL key, so it needs the curve key export APIs. Naming
`NO_CURVE25519_KEY_EXPORT` or `NO_CURVE448_KEY_EXPORT` alongside it stops the
build with a message from `asu_ecdh.c` rather than failing at link: drop
the macro, or turn the offload off with `WOLFSSL_VERSAL_GEN2_ASU_NO_X25519` /
`..._NO_X448`.
## Known limits
**P-521 is off by default.** Stock ASU firmware pads the digest wrong and the
client caps it at 64 bytes, which is short of the 66 P-521 needs. Turn it on
only with firmware that front-pads.
**P-521 needs Vitis 2026.1 firmware.** The client still caps the digest at 64
bytes, short of the 66 P-521 uses, so the firmware has to front-pad it. 2026.1
does, and `WOLFSSL_VERSAL_GEN2_ASU_ECC_P521` turns on there by itself for both
ECDSA and ECDH; `WOLFSSL_VERSAL_GEN2_ASU_NO_ECC_P521` opts back out. Earlier
firmware padded it wrong, so the curve stays in software on 2025.2.
**CCM block alignment is a 2025.2 limit.** That AES-CCM engine only took data
and AAD in whole 16-byte blocks, so
`WOLFSSL_VERSAL_GEN2_ASU_CCM_ALIGN_DECLINE` sends anything else to software -
the RFC 3610 vectors in the wolfCrypt self-test among them. 2026.1 accepts
unaligned lengths, so leave the macro undefined there and keep the work on
hardware.
The benchmark trips over the same limit. It authenticates `AES_AUTH_ADD_SZ`
bytes of extra data, which defaults to 13, so on 2025.2 every AES-CCM row
declines to software however long the message is. Build the benchmark with
`AES_AUTH_ADD_SZ` set to 16 to keep those rows on hardware; wolfSSL already
does that for the first-generation Versal port for the same reason.
**ECIES needs the KDF context path.** See below.
@@ -175,7 +220,7 @@ asu_cipher.c AES
asu_cmac.c AES-CMAC
asu_rsa.c RSA
asu_ecc.c ECDSA, Ed25519, Ed448
asu_ecdh.c ECDH
asu_ecdh.c ECDH, X25519, X448
asu_ecies.c ECIES
asu_rng.c TRNG
```
@@ -48,10 +48,11 @@
#include "xasu_status.h"
#include "xstatus.h"
/* One ASU AES request: the params block and the key object it points at. */
/* One ASU AES request: the params block and the key object it points at. The
* key object is read by DMA, so keep it on a cache line of its own. */
typedef struct {
XAsu_AesParams params;
XAsu_AesKeyObject keyObj;
XAsu_AesParams params;
WC_ASU_ALIGN64 XAsu_AesKeyObject keyObj;
} AsuCipherReq;
/* Queue one ASU AES operation. The lock is held here, so only queue it. */
@@ -121,7 +122,8 @@ static int wc_AsuCipherOneShot(Aes* aes, byte* out, const byte* in, word32 sz,
req.params.DataLen = sz;
req.params.EngineMode = engineMode;
req.params.OperationFlags =
(u8)(XASU_AES_INIT | XASU_AES_UPDATE | XASU_AES_FINAL);
(u8)(WC_ASU_AES_OP_INIT | WC_ASU_AES_OP_UPDATE |
WC_ASU_AES_OP_FINAL);
req.params.IsLast = (u8)XASU_TRUE;
if (enc) {
req.params.OperationType = (u8)XASU_AES_ENCRYPT_OPERATION;
@@ -463,7 +465,8 @@ static int wc_AsuCipherGcm(wc_CryptoInfo* info)
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);
(u8)(WC_ASU_AES_OP_INIT | WC_ASU_AES_OP_UPDATE |
WC_ASU_AES_OP_FINAL);
req.params.IsLast = (u8)XASU_TRUE;
if (info->cipher.enc) {
req.params.OperationType = (u8)XASU_AES_ENCRYPT_OPERATION;
@@ -620,7 +623,8 @@ static int wc_AsuCipherCcm(wc_CryptoInfo* info)
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);
(u8)(WC_ASU_AES_OP_INIT | WC_ASU_AES_OP_UPDATE |
WC_ASU_AES_OP_FINAL);
req.params.IsLast = (u8)XASU_TRUE;
if (info->cipher.enc) {
req.params.OperationType = (u8)XASU_AES_ENCRYPT_OPERATION;
@@ -64,10 +64,11 @@ typedef struct {
word32 keyLen;
} AsuCmacKeep;
/* One ASU AES request: the params block and the key object it points at. */
/* One ASU AES request: the params block and the key object it points at. The
* key object is read by DMA, so keep it on a cache line of its own. */
typedef struct {
XAsu_AesParams params;
XAsu_AesKeyObject keyObj;
XAsu_AesParams params;
WC_ASU_ALIGN64 XAsu_AesKeyObject keyObj;
} AsuCmacReq;
/* Hands one CMAC request to the ASU queue. wc_AsuTransact calls this while it
@@ -149,7 +150,8 @@ static int wc_AsuCmacHw(const byte* key, word32 keyLen, u32 keySize,
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);
(u8)(WC_ASU_AES_OP_INIT | WC_ASU_AES_OP_UPDATE |
WC_ASU_AES_OP_FINAL);
req.params.IsLast = (u8)XASU_TRUE;
req.params.OperationType = (u8)XASU_AES_ENCRYPT_OPERATION;
@@ -62,7 +62,7 @@
#define WC_ASU_ECC_OP_VERIFY 1 /* XAsu_EccVerifySign */
/* Biggest curve we support, P-521 at 66 bytes. */
#define WC_ASU_ECC_MAX_KEYLEN XASU_ECC_P521_SIZE_IN_BYTES
#define WC_ASU_ECC_MAX_KEYLEN WC_ASU_ECC_P521_LEN
/* One ASU ECC request. The buffers live on the heap so the ASU can reach them. */
typedef struct {
@@ -166,41 +166,41 @@ static int wc_AsuEccCurve(ecc_key* key, u32* curveType, u32* keyLen)
switch (key->dp->id) {
case ECC_SECP192R1:
type = (u32)XASU_ECC_NIST_P192;
len = (u32)XASU_ECC_P192_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P192_LEN;
break;
case ECC_SECP256R1:
type = (u32)XASU_ECC_NIST_P256;
len = (u32)XASU_ECC_P256_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P256_LEN;
break;
case ECC_SECP384R1:
type = (u32)XASU_ECC_NIST_P384;
len = (u32)XASU_ECC_P384_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P384_LEN;
break;
#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECC_P521
/* Off by default. Stock firmware pads the digest wrong and caps it at
* 64 bytes, which is too small for P-521. */
/* On from Vitis 2026.1, whose firmware front-pads the digest. Earlier
* firmware padded it wrong, so the curve stayed in software. */
case ECC_SECP521R1:
type = (u32)XASU_ECC_NIST_P521;
len = (u32)XASU_ECC_P521_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P521_LEN;
break;
#endif
#ifdef HAVE_ECC_BRAINPOOL
/* Brainpool curves work like the NIST ones and all fit in 64 bytes. */
case ECC_BRAINPOOLP256R1:
type = (u32)XASU_ECC_BRAINPOOL_P256;
len = (u32)XASU_ECC_P256_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P256_LEN;
break;
case ECC_BRAINPOOLP320R1:
type = (u32)XASU_ECC_BRAINPOOL_P320;
len = (u32)XASU_ECC_P320_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P320_LEN;
break;
case ECC_BRAINPOOLP384R1:
type = (u32)XASU_ECC_BRAINPOOL_P384;
len = (u32)XASU_ECC_P384_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P384_LEN;
break;
case ECC_BRAINPOOLP512R1:
type = (u32)XASU_ECC_BRAINPOOL_P512;
len = (u32)XASU_ECC_P512_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P512_LEN;
break;
#endif
default:
@@ -322,9 +322,8 @@ static int wc_AsuEccSign(wc_CryptoInfo* info)
mem.req->op = WC_ASU_ECC_OP_SIGN;
mem.req->params.CurveType = curveType;
mem.req->params.KeyLen = keyLen;
mem.req->params.DigestLen = digLen;
mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key;
wc_AsuEccSetKey(&mem.req->params, mem.req->key, keyLen);
mem.req->params.DigestAddr = (u64)(UINTPTR)mem.req->digest;
mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign;
@@ -443,9 +442,8 @@ static int wc_AsuEccVerify(wc_CryptoInfo* info)
mem.req->op = WC_ASU_ECC_OP_VERIFY;
mem.req->params.CurveType = curveType;
mem.req->params.KeyLen = keyLen;
mem.req->params.DigestLen = digLen;
mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key;
wc_AsuEccSetKey(&mem.req->params, mem.req->key, keyLen);
mem.req->params.DigestAddr = (u64)(UINTPTR)mem.req->digest;
mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign;
@@ -538,9 +536,8 @@ static int wc_AsuEd25519Sign(wc_CryptoInfo* info)
mem.req->op = WC_ASU_ECC_OP_SIGN;
mem.req->params.CurveType = (u32)XASU_ECC_NIST_ED25519;
mem.req->params.KeyLen = (u32)ED25519_KEY_SIZE;
mem.req->params.DigestLen = msgLen;
mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key;
wc_AsuEccSetKey(&mem.req->params, mem.req->key, (u32)ED25519_KEY_SIZE);
mem.req->params.DigestAddr = (u64)(UINTPTR)msg;
mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign;
@@ -654,9 +651,8 @@ static int wc_AsuEd25519Verify(wc_CryptoInfo* info)
mem.req->op = WC_ASU_ECC_OP_VERIFY;
mem.req->params.CurveType = (u32)XASU_ECC_NIST_ED25519;
mem.req->params.KeyLen = (u32)ED25519_KEY_SIZE;
mem.req->params.DigestLen = msgLen;
mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key;
wc_AsuEccSetKey(&mem.req->params, mem.req->key, (u32)ED25519_KEY_SIZE);
mem.req->params.DigestAddr = (u64)(UINTPTR)msg;
mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign;
@@ -755,9 +751,8 @@ static int wc_AsuEd448Sign(wc_CryptoInfo* info)
mem.req->op = WC_ASU_ECC_OP_SIGN;
mem.req->params.CurveType = (u32)XASU_ECC_NIST_ED448;
mem.req->params.KeyLen = (u32)ED448_KEY_SIZE;
mem.req->params.DigestLen = msgLen;
mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key;
wc_AsuEccSetKey(&mem.req->params, mem.req->key, (u32)ED448_KEY_SIZE);
mem.req->params.DigestAddr = (u64)(UINTPTR)msg;
mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign;
@@ -875,9 +870,8 @@ static int wc_AsuEd448Verify(wc_CryptoInfo* info)
mem.req->op = WC_ASU_ECC_OP_VERIFY;
mem.req->params.CurveType = (u32)XASU_ECC_NIST_ED448;
mem.req->params.KeyLen = (u32)ED448_KEY_SIZE;
mem.req->params.DigestLen = msgLen;
mem.req->params.KeyAddr = (u64)(UINTPTR)mem.req->key;
wc_AsuEccSetKey(&mem.req->params, mem.req->key, (u32)ED448_KEY_SIZE);
mem.req->params.DigestAddr = (u64)(UINTPTR)msg;
mem.req->params.SignAddr = (u64)(UINTPTR)mem.req->sign;
@@ -49,8 +49,29 @@
#include "xasu_status.h"
#include "xstatus.h"
/* X25519 and X448 ride the same ECDH command with a Montgomery curve id. */
#if defined(WOLFSSL_VERSAL_GEN2_ASU_X25519) && defined(HAVE_CURVE25519)
#include <wolfssl/wolfcrypt/curve25519.h>
#define WC_ASU_X25519_ENABLED
/* The scalar and peer point are read out of the key, so declining the
* export API and asking for the offload is a contradiction, not a
* choice. */
#ifdef NO_CURVE25519_KEY_EXPORT
#error "ASU X25519 needs the curve25519 key export API: drop \
NO_CURVE25519_KEY_EXPORT or name WOLFSSL_VERSAL_GEN2_ASU_NO_X25519"
#endif
#endif
#if defined(WOLFSSL_VERSAL_GEN2_ASU_X448) && defined(HAVE_CURVE448)
#include <wolfssl/wolfcrypt/curve448.h>
#define WC_ASU_X448_ENABLED
#ifdef NO_CURVE448_KEY_EXPORT
#error "ASU X448 needs the curve448 key export API: drop \
NO_CURVE448_KEY_EXPORT or name WOLFSSL_VERSAL_GEN2_ASU_NO_X448"
#endif
#endif
/* Biggest curve we support, P-521 at 66 bytes. */
#define WC_ASU_ECDH_MAX_KEYLEN XASU_ECC_P521_SIZE_IN_BYTES
#define WC_ASU_ECDH_MAX_KEYLEN WC_ASU_ECC_P521_LEN
/* One ASU ECDH request. The buffers live on the heap so the ASU can reach
* them. */
@@ -132,40 +153,39 @@ static int wc_AsuEcdhCurve(ecc_key* key, u32* curveType, u32* keyLen)
switch (key->dp->id) {
case ECC_SECP192R1:
type = (u32)XASU_ECC_NIST_P192;
len = (u32)XASU_ECC_P192_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P192_LEN;
break;
case ECC_SECP256R1:
type = (u32)XASU_ECC_NIST_P256;
len = (u32)XASU_ECC_P256_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P256_LEN;
break;
case ECC_SECP384R1:
type = (u32)XASU_ECC_NIST_P384;
len = (u32)XASU_ECC_P384_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P384_LEN;
break;
#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECC_P521
/* Uses the same P-521 switch as ECDSA. ECDH has no digest, so the
* firmware padding bug does not apply, but it stays off until tested. */
/* Uses the same P-521 switch as ECDSA, on from Vitis 2026.1. */
case ECC_SECP521R1:
type = (u32)XASU_ECC_NIST_P521;
len = (u32)XASU_ECC_P521_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P521_LEN;
break;
#endif
#ifdef HAVE_ECC_BRAINPOOL
case ECC_BRAINPOOLP256R1:
type = (u32)XASU_ECC_BRAINPOOL_P256;
len = (u32)XASU_ECC_P256_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P256_LEN;
break;
case ECC_BRAINPOOLP320R1:
type = (u32)XASU_ECC_BRAINPOOL_P320;
len = (u32)XASU_ECC_P320_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P320_LEN;
break;
case ECC_BRAINPOOLP384R1:
type = (u32)XASU_ECC_BRAINPOOL_P384;
len = (u32)XASU_ECC_P384_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P384_LEN;
break;
case ECC_BRAINPOOLP512R1:
type = (u32)XASU_ECC_BRAINPOOL_P512;
len = (u32)XASU_ECC_P512_SIZE_IN_BYTES;
len = (u32)WC_ASU_ECC_P512_LEN;
break;
#endif
default:
@@ -180,6 +200,13 @@ static int wc_AsuEcdhCurve(ecc_key* key, u32* curveType, u32* keyLen)
}
/* ECDH shared secret. Both keys must be on the same supported curve. */
#ifdef WC_ASU_X25519_ENABLED
static int wc_AsuX25519(wc_CryptoInfo* info);
#endif
#ifdef WC_ASU_X448_ENABLED
static int wc_AsuX448(wc_CryptoInfo* info);
#endif
int wc_AsuEcdh(wc_CryptoInfo* info)
{
AsuEcdhMem mem;
@@ -194,8 +221,20 @@ int wc_AsuEcdh(wc_CryptoInfo* info)
if (info == NULL) {
return BAD_FUNC_ARG;
}
if (info->algo_type != WC_ALGO_TYPE_PK ||
info->pk.type != WC_PK_TYPE_ECDH) {
if (info->algo_type != WC_ALGO_TYPE_PK) {
return CRYPTOCB_UNAVAILABLE;
}
#ifdef WC_ASU_X25519_ENABLED
if (info->pk.type == WC_PK_TYPE_CURVE25519) {
return wc_AsuX25519(info);
}
#endif
#ifdef WC_ASU_X448_ENABLED
if (info->pk.type == WC_PK_TYPE_CURVE448) {
return wc_AsuX448(info);
}
#endif
if (info->pk.type != WC_PK_TYPE_ECDH) {
return CRYPTOCB_UNAVAILABLE;
}
@@ -254,9 +293,8 @@ int wc_AsuEcdh(wc_CryptoInfo* info)
}
mem.req->params.CurveType = curveType;
mem.req->params.KeyLen = keyLen;
mem.req->params.PvtKeyAddr = (u64)(UINTPTR)mem.req->privKey;
mem.req->params.PubKeyAddr = (u64)(UINTPTR)mem.req->pubKey;
wc_AsuEcdhSetKeys(&mem.req->params, mem.req->privKey, mem.req->pubKey,
keyLen);
mem.req->params.SharedSecretAddr = (u64)(UINTPTR)mem.req->secret;
mem.req->params.SharedSecretObjIdAddr = 0;
@@ -287,4 +325,157 @@ int wc_AsuEcdh(wc_CryptoInfo* info)
return 0;
}
#if defined(WC_ASU_X25519_ENABLED) || defined(WC_ASU_X448_ENABLED)
/* One X25519 or X448 agreement. Both are a scalar, a u coordinate and a shared
* secret of one curve width, so only the curve id and the length differ. */
static int wc_AsuMontgomeryEcdh(u32 curveType, u32 keyLen, const byte* pvt,
const byte* peer, byte* out, word32* outLen, int bigEndian)
{
AsuEcdhMem mem;
word32 status;
word32 addl = 0;
int ret;
ret = wc_AsuEcdhReqNew(&mem);
if (ret != 0) {
return ret;
}
XMEMSET(mem.req, 0, sizeof(*mem.req));
XMEMCPY(mem.req->privKey, pvt, keyLen);
XMEMCPY(mem.req->pubKey, peer, keyLen);
mem.req->params.CurveType = curveType;
wc_AsuEcdhSetKeys(&mem.req->params, mem.req->privKey, mem.req->pubKey,
keyLen);
mem.req->params.SharedSecretAddr = (u64)(UINTPTR)mem.req->secret;
mem.req->params.SharedSecretObjIdAddr = 0;
WC_ASU_PRINTF("[ASU] ecdh curve=%u keyLen=%u montgomery\r\n",
(unsigned int)curveType, (unsigned int)keyLen);
wc_AsuCacheFlush(mem.req->privKey, keyLen);
/* A Montgomery public key is only the u coordinate, so one key length
* here, not the two a Weierstrass x||y point needs. */
wc_AsuCacheFlush(mem.req->pubKey, keyLen);
/* Flush the output first, or old cache lines could overwrite the secret
* the ASU writes and we would read zeros. */
wc_AsuCacheFlush(mem.req->secret, keyLen);
status = wc_AsuTransact(wc_AsuEcdhSubmit, mem.req, &addl);
wc_AsuCacheInvalidate(mem.req->secret, keyLen);
WC_ASU_PRINTF("[ASU] ecdh st=%u addl=0x%x\r\n",
(unsigned int)status, (unsigned int)addl);
if (status != XST_SUCCESS) {
wc_AsuEcdhReqFree(&mem);
return WC_HW_E;
}
/* No zero shared secret test needed: the ASU refuses a low order point
* itself (measured, XASUFW_ECDH_GEN_SECRET_OPERATION_FAIL). */
/* The curve works little endian and so does the ASU; a caller that asked
* for big endian gets the same reversal software would have done. */
if (bigEndian) {
u32 i;
for (i = 0; i < keyLen; i++) {
out[i] = mem.req->secret[keyLen - i - 1U];
}
}
else {
XMEMCPY(out, mem.req->secret, keyLen);
}
*outLen = keyLen;
wc_AsuEcdhReqFree(&mem);
return 0;
}
#endif /* WC_ASU_X25519_ENABLED || WC_ASU_X448_ENABLED */
#ifdef WC_ASU_X25519_ENABLED
/* X25519 shared secret on the ASU. */
static int wc_AsuX25519(wc_CryptoInfo* info)
{
byte pvt[CURVE25519_KEYSIZE];
byte peer[CURVE25519_KEYSIZE];
word32 pvtLen = (word32)sizeof(pvt);
word32 peerLen = (word32)sizeof(peer);
int ret;
if (info->pk.curve25519.private_key == NULL ||
info->pk.curve25519.public_key == NULL ||
info->pk.curve25519.out == NULL ||
info->pk.curve25519.outlen == NULL) {
return BAD_FUNC_ARG;
}
if (*info->pk.curve25519.outlen < (word32)CURVE25519_KEYSIZE) {
return BUFFER_E;
}
ret = wc_curve25519_export_private_raw_ex(info->pk.curve25519.private_key,
pvt, &pvtLen, EC25519_LITTLE_ENDIAN);
if (ret == 0) {
/* Exported as stored, not masked: the ASU clears the RFC 7748 reserved
* high bit itself, measured to match what software returns. */
ret = wc_curve25519_export_public_ex(info->pk.curve25519.public_key,
peer, &peerLen, EC25519_LITTLE_ENDIAN);
}
/* No usable key material is a reason to use software, not an error. */
if (ret != 0 || pvtLen != (word32)CURVE25519_KEYSIZE ||
peerLen != (word32)CURVE25519_KEYSIZE) {
ForceZero(pvt, sizeof(pvt));
return CRYPTOCB_UNAVAILABLE;
}
ret = wc_AsuMontgomeryEcdh((u32)XASU_ECC_CURVE25519,
(u32)CURVE25519_KEYSIZE, pvt, peer, info->pk.curve25519.out,
info->pk.curve25519.outlen,
info->pk.curve25519.endian == EC25519_BIG_ENDIAN);
ForceZero(pvt, sizeof(pvt));
return ret;
}
#endif /* WC_ASU_X25519_ENABLED */
#ifdef WC_ASU_X448_ENABLED
/* X448 shared secret on the ASU. */
static int wc_AsuX448(wc_CryptoInfo* info)
{
byte pvt[CURVE448_KEY_SIZE];
byte peer[CURVE448_KEY_SIZE];
word32 pvtLen = (word32)sizeof(pvt);
word32 peerLen = (word32)sizeof(peer);
int ret;
if (info->pk.curve448.private_key == NULL ||
info->pk.curve448.public_key == NULL ||
info->pk.curve448.out == NULL ||
info->pk.curve448.outlen == NULL) {
return BAD_FUNC_ARG;
}
if (*info->pk.curve448.outlen < (word32)CURVE448_KEY_SIZE) {
return BUFFER_E;
}
ret = wc_curve448_export_private_raw_ex(info->pk.curve448.private_key,
pvt, &pvtLen, EC448_LITTLE_ENDIAN);
if (ret == 0) {
ret = wc_curve448_export_public_ex(info->pk.curve448.public_key,
peer, &peerLen, EC448_LITTLE_ENDIAN);
}
if (ret != 0 || pvtLen != (word32)CURVE448_KEY_SIZE ||
peerLen != (word32)CURVE448_KEY_SIZE) {
ForceZero(pvt, sizeof(pvt));
return CRYPTOCB_UNAVAILABLE;
}
ret = wc_AsuMontgomeryEcdh((u32)XASU_ECC_CURVE448,
(u32)CURVE448_KEY_SIZE, pvt, peer, info->pk.curve448.out,
info->pk.curve448.outlen,
info->pk.curve448.endian == EC448_BIG_ENDIAN);
ForceZero(pvt, sizeof(pvt));
return ret;
}
#endif /* WC_ASU_X448_ENABLED */
#endif /* WC_ASU_ECDH_ENABLED */
@@ -66,8 +66,13 @@
#define WC_ASU_ECIES_NONCE_SZ 12
#define WC_ASU_ECIES_TAG_SZ 16
/* Biggest curve we support here, Brainpool P-512 at 64 bytes. */
#define WC_ASU_ECIES_MAX_KEYLEN XASU_ECC_P512_SIZE_IN_BYTES
/* Biggest curve we support here. P-521 joins only where the firmware handles
* it, so the buffers follow the same switch. */
#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECC_P521
#define WC_ASU_ECIES_MAX_KEYLEN WC_ASU_ECC_P521_LEN
#else
#define WC_ASU_ECIES_MAX_KEYLEN WC_ASU_ECC_P512_LEN
#endif
/* One ASU ECIES request. The fixed size fields live on the heap so the ASU
* can reach them. The message stays in the caller buffers. */
@@ -158,32 +163,39 @@ static int wc_AsuEciesCurve(ecc_key* key, u8* curveType, u8* keyLen)
switch (key->dp->id) {
case ECC_SECP192R1:
*curveType = (u8)XASU_ECC_NIST_P192;
*keyLen = (u8)XASU_ECC_P192_SIZE_IN_BYTES;
*keyLen = (u8)WC_ASU_ECC_P192_LEN;
break;
case ECC_SECP256R1:
*curveType = (u8)XASU_ECC_NIST_P256;
*keyLen = (u8)XASU_ECC_P256_SIZE_IN_BYTES;
*keyLen = (u8)WC_ASU_ECC_P256_LEN;
break;
case ECC_SECP384R1:
*curveType = (u8)XASU_ECC_NIST_P384;
*keyLen = (u8)XASU_ECC_P384_SIZE_IN_BYTES;
*keyLen = (u8)WC_ASU_ECC_P384_LEN;
break;
#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECC_P521
/* Same P-521 switch as ECDSA and ECDH, on from Vitis 2026.1. */
case ECC_SECP521R1:
*curveType = (u8)XASU_ECC_NIST_P521;
*keyLen = (u8)WC_ASU_ECC_P521_LEN;
break;
#endif
#ifdef HAVE_ECC_BRAINPOOL
case ECC_BRAINPOOLP256R1:
*curveType = (u8)XASU_ECC_BRAINPOOL_P256;
*keyLen = (u8)XASU_ECC_P256_SIZE_IN_BYTES;
*keyLen = (u8)WC_ASU_ECC_P256_LEN;
break;
case ECC_BRAINPOOLP320R1:
*curveType = (u8)XASU_ECC_BRAINPOOL_P320;
*keyLen = (u8)XASU_ECC_P320_SIZE_IN_BYTES;
*keyLen = (u8)WC_ASU_ECC_P320_LEN;
break;
case ECC_BRAINPOOLP384R1:
*curveType = (u8)XASU_ECC_BRAINPOOL_P384;
*keyLen = (u8)XASU_ECC_P384_SIZE_IN_BYTES;
*keyLen = (u8)WC_ASU_ECC_P384_LEN;
break;
case ECC_BRAINPOOLP512R1:
*curveType = (u8)XASU_ECC_BRAINPOOL_P512;
*keyLen = (u8)XASU_ECC_P512_SIZE_IN_BYTES;
*keyLen = (u8)WC_ASU_ECC_P512_LEN;
break;
#endif
default:
@@ -70,6 +70,7 @@ typedef struct {
typedef struct {
XAsu_ShaOperationCmd cmd;
int isSha3;
WC_ASU_ALIGN64 byte hash[WC_ASU_SHAKE_MAX_HASH_LEN]; /* DMA out */
} AsuHashReq;
/* Free a saved message. It holds the data we hashed, so wipe it first. */
@@ -260,35 +261,38 @@ static int wc_AsuHashOneShot(u8 shaType, u8 shaMode, const byte* data,
{
AsuHashReq req;
word32 status;
byte* outAddr = digest;
word32 outLen = hashLen;
byte xofTmp[XASU_SHAKE_256_MAX_HASH_LEN];
word32 outLen = hashLen;
if (digest == NULL || (data == NULL && dataLen > 0)) {
return BAD_FUNC_ARG;
}
if (hashLen > (word32)sizeof(req.hash)) {
return BAD_FUNC_ARG;
}
/* The ASU reads the digest 4 bytes at a time, so round the length up into
* a temporary buffer and copy back only what was asked for. */
if ((shaMode == XASU_SHA_MODE_SHAKE256) && ((hashLen % 4u) != 0u) &&
(hashLen <= XASU_SHAKE_256_MAX_HASH_LEN)) {
outLen = (hashLen + 3u) & ~3u;
outAddr = xofTmp;
/* The ASU writes the digest 4 bytes at a time, so round the length up. */
outLen = (hashLen + 3u) & ~3u;
if (outLen > (word32)sizeof(req.hash)) {
return BAD_FUNC_ARG;
}
XMEMSET(&req, 0, sizeof(req));
req.cmd.DataAddr = (u64)(UINTPTR)data;
req.cmd.DataSize = dataLen;
req.cmd.HashAddr = (u64)(UINTPTR)outAddr;
/* Always land the digest in our own aligned buffer, never the caller's:
* the ASU writes it by DMA and the cache maintenance below is by line. */
req.cmd.HashAddr = (u64)(UINTPTR)req.hash;
req.cmd.HashBufSize = outLen;
req.cmd.ShaMode = shaMode;
req.cmd.IsLast = (u8)XASU_TRUE;
if (dataLen > 0) {
req.cmd.OperationFlags =
(u8)(XASU_SHA_START | XASU_SHA_UPDATE | XASU_SHA_FINISH);
(u8)(WC_ASU_SHA_OP_START | WC_ASU_SHA_OP_UPDATE |
WC_ASU_SHA_OP_FINISH);
}
else {
req.cmd.OperationFlags = (u8)(XASU_SHA_START | XASU_SHA_FINISH);
req.cmd.OperationFlags =
(u8)(WC_ASU_SHA_OP_START | WC_ASU_SHA_OP_FINISH);
}
if (shaType == XASU_SHA3_TYPE) {
req.isSha3 = 1;
@@ -300,29 +304,31 @@ static int wc_AsuHashOneShot(u8 shaType, u8 shaMode, const byte* data,
WC_ASU_PRINTF("[ASU] hash type=%d mode=%d dataLen=%u hashLen=%u\r\n",
(int)shaType, (int)shaMode, (unsigned int)dataLen, (unsigned int)hashLen);
/* The ASU reads the message from memory, so push it out first. The digest
* comes back another way and needs nothing here. */
/* The ASU reads the message from memory, so push it out first. */
if (dataLen > 0) {
wc_AsuCacheFlush(data, dataLen);
}
#ifdef WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1
/* 2026.1 returns the digest by DMA; 2025.2 sent it back in the mailbox. */
wc_AsuCacheFlush(req.hash, outLen);
#endif
status = wc_AsuTransact(wc_AsuHashSubmit, &req, NULL);
if (status != XST_SUCCESS) {
return WC_HW_E;
}
/* Copy back only the bytes asked for when a temp buffer was used. */
if (outAddr != digest) {
XMEMCPY(digest, xofTmp, hashLen);
}
#ifdef WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1
/* Reload the digest the ASU wrote, or the read returns stale data. */
wc_AsuCacheInvalidate(req.hash, outLen);
#endif
/* Hand back only the bytes asked for, dropping any round-up padding. */
XMEMCPY(digest, req.hash, hashLen);
return 0;
}
/* The ASU sends the hash back in a 64 byte slot. A SHAKE output that fits goes
* to the ASU, and anything longer is done in software. */
#define WC_ASU_SHAKE_HW_MAX_BYTES 64
#ifdef WOLFSSL_SHAKE256
/* Do SHAKE256 in software for long outputs. The private context uses an
* invalid device id so this does not come back through the callback. */
@@ -407,7 +413,7 @@ static int wc_AsuHashCompute(wc_CryptoInfo* info)
* Everything else is one ASU operation. */
#ifdef WOLFSSL_SHAKE256
if ((shaMode == XASU_SHA_MODE_SHAKE256) &&
(hashLen > WC_ASU_SHAKE_HW_MAX_BYTES)) {
(hashLen > WC_ASU_SHAKE_MAX_HASH_LEN)) {
ret = wc_AsuShakeSoftware(data, dataLen, info->hash.digest, hashLen);
}
else
@@ -162,18 +162,19 @@ static int wc_AsuHmacOneShot(u8 shaType, u8 shaMode, const byte* key,
req.params.ShaType = shaType;
req.params.ShaMode = shaMode;
req.params.IsLast = (u8)XASU_TRUE;
req.params.KeyLen = keyLen;
req.params.MsgLen = msgLen;
req.params.HmacLen = macLen;
req.params.KeyAddr = (u64)(UINTPTR)key;
req.params.MsgBufferAddr = (u64)(UINTPTR)msg;
req.params.HmacAddr = (u64)(UINTPTR)mac;
wc_AsuHmacSetKey(&req.params, key, keyLen);
if (msgLen > 0) {
req.params.OperationFlags =
(u8)(XASU_HMAC_INIT | XASU_HMAC_UPDATE | XASU_HMAC_FINAL);
(u8)(WC_ASU_HMAC_OP_INIT | WC_ASU_HMAC_OP_UPDATE |
WC_ASU_HMAC_OP_FINAL);
}
else {
req.params.OperationFlags = (u8)(XASU_HMAC_INIT | XASU_HMAC_FINAL);
req.params.OperationFlags =
(u8)(WC_ASU_HMAC_OP_INIT | WC_ASU_HMAC_OP_FINAL);
}
WC_ASU_PRINTF("[ASU] hmac type=%d mode=%d keyLen=%u msgLen=%u macLen=%u\r\n",
@@ -181,7 +182,7 @@ static int wc_AsuHmacOneShot(u8 shaType, u8 shaMode, const byte* key,
(unsigned int)macLen);
/* The ASU reads the key and message from memory, so push them out first.
* The MAC comes back another way and needs nothing here. */
* The MAC returns in the mailbox response, so it needs no cache work. */
wc_AsuCacheFlush(key, keyLen);
if (msgLen > 0) {
wc_AsuCacheFlush(msg, msgLen);
@@ -216,11 +217,16 @@ static int wc_AsuHmacCompute(wc_CryptoInfo* info)
ret = wc_AsuHmacResolve(info->hmac.macType, &shaType, &shaMode, &hmacLen);
if (ret != 0) {
WC_ASU_PRINTF("[ASU] hmac decline: macType=%d unsupported\r\n",
(int)info->hmac.macType);
return ret;
}
/* The ASU needs the raw key. If wolfSSL did not keep one, use software. */
if ((hmac->keyRaw == NULL) || (hmac->keyLen == 0)) {
WC_ASU_PRINTF("[ASU] hmac decline: no raw key "
"(keyRaw=%p keyLen=%u)\r\n",
(void*)hmac->keyRaw, (unsigned int)hmac->keyLen);
return CRYPTOCB_UNAVAILABLE;
}
@@ -32,10 +32,10 @@
* example).
*
* OAEP encrypt is full hardware (ASU does the SHA/MGF encode and the RSA math).
* OAEP decrypt is NOT offloaded here: the ASU OAEP-decode command returns no
* recovered-message length, which wolfSSL requires, so it declines to software
* (the private RSA math is still offloaded through the raw WC_PK_TYPE_RSA
* path).
* OAEP decrypt is full hardware from Vitis 2026.1, which returns the recovered
* message length the decode needs; before that the length was lost, so it
* declined to software with only the private RSA math offloaded through the raw
* WC_PK_TYPE_RSA path, and it still does on 2025.2.
*/
#ifdef HAVE_CONFIG_H
@@ -74,12 +74,24 @@
#define WC_ASU_RSA_PAD
#endif
/* OAEP decrypt needs the recovered-message length back, which only the 2026.1
* client returns (through OutputLenAddr). Before that the decode ran on the
* ASU but the length was lost, so wolfSSL could not use the result. */
/* It also needs the private exponent, which RsaKey does not carry under
* WOLFSSL_RSA_PUBLIC_ONLY, so the offload stays out of that build. */
#if defined(WC_ASU_RSA_PAD) && \
defined(WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1) && \
!defined(WOLFSSL_RSA_PUBLIC_ONLY)
#define WC_ASU_RSA_OAEP_DEC
#endif
/* Submit-thunk op selector. */
#define WC_ASU_RSA_OP_PUB 0 /* XAsu_RsaEnc: public m^e mod n */
#define WC_ASU_RSA_OP_PVT 1 /* XAsu_RsaDec: private c^d mod n */
#define WC_ASU_RSA_OP_PSS_SIGN 2 /* XAsu_RsaPssSignGen */
#define WC_ASU_RSA_OP_PSS_VERIFY 3 /* XAsu_RsaPssSignVer */
#define WC_ASU_RSA_OP_OAEP_ENC 4 /* XAsu_RsaOaepEnc */
#define WC_ASU_RSA_OP_OAEP_DEC 5 /* XAsu_RsaOaepDec */
/* Everything one RSA request needs: the operation/padding info plus the key.
* Public-key operations (encrypt, verify) use only the public part. */
@@ -93,6 +105,9 @@ typedef struct {
* CPU-owned data and stamps stale bytes over the DMA result. */
WC_ASU_ALIGN64 byte out[XRSA_4096_KEY_SIZE]; /* DMA result, copied out */
WC_ASU_ALIGN64 byte scratch[XRSA_4096_KEY_SIZE]; /* PSS sig/OAEP label */
/* 2026.1 returns the produced length here through the mailbox, and the
* client refuses a request that does not offer somewhere to put it. */
u32 outLen;
int op;
} AsuRsaReq;
@@ -159,6 +174,10 @@ static int wc_AsuRsaSubmit(XAsu_ClientParams* params, void* ctx)
return XAsu_RsaPssSignVer(params, &req->pad);
case WC_ASU_RSA_OP_OAEP_ENC:
return XAsu_RsaOaepEnc(params, &req->oaep);
#ifdef WC_ASU_RSA_OAEP_DEC
case WC_ASU_RSA_OP_OAEP_DEC:
return XAsu_RsaOaepDec(params, &req->oaep);
#endif
default:
return XST_FAILURE;
}
@@ -224,7 +243,7 @@ static int wc_AsuRsaShaMap(enum wc_HashType hash, int mgf, u8* shaType,
case WC_HASH_TYPE_SHA256:
*shaType = (u8)XASU_SHA2_TYPE;
*shaMode = (u8)XASU_SHA_MODE_256;
*hashLen = XASU_SHA_256_HASH_LEN;
*hashLen = WC_ASU_SHA_256_HASH_LEN;
if (mgf != WC_MGF1SHA256) {
return CRYPTOCB_UNAVAILABLE;
}
@@ -248,7 +267,7 @@ static int wc_AsuRsaShaMap(enum wc_HashType hash, int mgf, u8* shaType,
case WC_HASH_TYPE_SHA3_256:
*shaType = (u8)XASU_SHA3_TYPE;
*shaMode = (u8)XASU_SHA_MODE_256;
*hashLen = XASU_SHA_256_HASH_LEN;
*hashLen = WC_ASU_SHA_256_HASH_LEN;
if (mgf != WC_MGF1SHA3_256) {
return CRYPTOCB_UNAVAILABLE;
}
@@ -323,6 +342,8 @@ static int wc_AsuRsaRaw(wc_CryptoInfo* info, RsaKey* key, u32 keySize, int op)
mem.req->pad.XAsu_RsaOpComp.ExpoCompAddr = 0U;
mem.req->pad.XAsu_RsaOpComp.Len = keySize;
mem.req->pad.XAsu_RsaOpComp.KeySize = keySize;
wc_AsuRsaSetOutLen(&mem.req->pad.XAsu_RsaOpComp, keySize,
&mem.req->outLen);
WC_ASU_PRINTF("[ASU] rsa raw op=%d keySize=%u\r\n",
op, (unsigned int)keySize);
@@ -492,6 +513,8 @@ static int wc_AsuRsaPssSign(wc_CryptoInfo* info)
mem.req->pad.XAsu_RsaOpComp.KeyCompAddr = (u64)(UINTPTR)&mem.req->key;
mem.req->pad.XAsu_RsaOpComp.Len = hashLen;
mem.req->pad.XAsu_RsaOpComp.KeySize = keySize;
wc_AsuRsaSetOutLen(&mem.req->pad.XAsu_RsaOpComp, keySize,
&mem.req->outLen);
mem.req->pad.SignatureDataAddr = (u64)(UINTPTR)mem.req->scratch;
mem.req->pad.SignatureLen = keySize;
mem.req->pad.SaltLen = (u32)saltLen;
@@ -544,6 +567,25 @@ static int wc_AsuRsaPssIsReject(word32 status)
(second >= 0xC4U && second <= 0xCAU));
}
#ifdef WC_ASU_RSA_OAEP_DEC
/* Same packing. Measured 0x4002F0BC, 0xBC OAEP_DECODE_ERROR, on a tampered
* ciphertext; 0xBD is its hash compare. Anything else is a fault, not a
* verdict. */
static int wc_AsuRsaOaepStatusIsPadding(word32 status)
{
word32 first = status & 0x3FFU;
word32 second = (status >> 10) & 0x3FFU;
if (first == 0xBCU || first == 0xBDU) {
return 1;
}
if (second == 0xBCU || second == 0xBDU) {
return 1;
}
return 0;
}
#endif /* WC_ASU_RSA_OAEP_DEC */
/* Decode an ASU RSA verify into a verdict: *res = 1 verified / 0 rejected,
* return 0 when the ASU gave a verdict, WC_HW_E only on transport fault. */
static int wc_AsuRsaVerifyResult(word32 status, word32 addl,
@@ -632,6 +674,8 @@ static int wc_AsuRsaPssVerify(wc_CryptoInfo* info)
mem.req->pad.XAsu_RsaOpComp.KeyCompAddr = (u64)(UINTPTR)&mem.req->key;
mem.req->pad.XAsu_RsaOpComp.Len = hashLen;
mem.req->pad.XAsu_RsaOpComp.KeySize = keySize;
wc_AsuRsaSetOutLen(&mem.req->pad.XAsu_RsaOpComp, keySize,
&mem.req->outLen);
mem.req->pad.SignatureDataAddr =
(u64)(UINTPTR)info->pk.rsa_pss_verify.sig;
mem.req->pad.SignatureLen = keySize;
@@ -685,7 +729,6 @@ static int wc_AsuRsaOaepEnc(wc_CryptoInfo* info)
info->pk.rsa.out == NULL) {
return BAD_FUNC_ARG;
}
/* Only encrypt is offloaded; ASU OAEP-decode returns no message length. */
if (info->pk.rsa.type != RSA_PUBLIC_ENCRYPT) {
return CRYPTOCB_UNAVAILABLE;
}
@@ -744,6 +787,8 @@ static int wc_AsuRsaOaepEnc(wc_CryptoInfo* info)
mem.req->oaep.XAsu_RsaOpComp.KeyCompAddr = (u64)(UINTPTR)&mem.req->key;
mem.req->oaep.XAsu_RsaOpComp.Len = info->pk.rsa.inLen;
mem.req->oaep.XAsu_RsaOpComp.KeySize = keySize;
wc_AsuRsaSetOutLen(&mem.req->oaep.XAsu_RsaOpComp, keySize,
&mem.req->outLen);
mem.req->oaep.OptionalLabelAddr = (u64)(UINTPTR)label;
mem.req->oaep.OptionalLabelSize = labelSz;
mem.req->oaep.ShaType = shaType;
@@ -773,6 +818,139 @@ static int wc_AsuRsaOaepEnc(wc_CryptoInfo* info)
wc_AsuRsaReqFree(&mem);
return 0;
}
#ifdef WC_ASU_RSA_OAEP_DEC
/* OAEP decrypt: the ASU does the private math and the OAEP decode, and reports
* how many message bytes it recovered. */
static int wc_AsuRsaOaepDec(wc_CryptoInfo* info)
{
AsuRsaMem mem;
RsaKey* key = info->pk.rsa.key;
RsaPadding* padding = info->pk.rsa.padding;
u32 keySize = 0;
u8 shaType = 0;
u8 shaMode = 0;
word32 hashLen = 0;
word32 recovered;
const byte* label;
word32 labelSz;
word32 status;
word32 addl = 0;
int ret = 0;
if (key == NULL || padding == NULL || info->pk.rsa.in == NULL ||
info->pk.rsa.out == NULL) {
return BAD_FUNC_ARG;
}
if (info->pk.rsa.type != RSA_PRIVATE_DECRYPT) {
return CRYPTOCB_UNAVAILABLE;
}
/* Private op needs the private exponent, same as the raw and PSS paths. */
if (mp_unsigned_bin_size(&key->d) == 0) {
return CRYPTOCB_UNAVAILABLE;
}
ret = wc_AsuRsaKeySize(key, &keySize);
if (ret != 0) {
return ret;
}
/* The ciphertext is one modulus wide; anything else is not ours. */
if (info->pk.rsa.inLen != keySize) {
return CRYPTOCB_UNAVAILABLE;
}
if (info->pk.rsa.outLen == NULL) {
return BAD_FUNC_ARG;
}
ret = wc_AsuRsaShaMap(padding->hash, padding->mgf, &shaType, &shaMode,
&hashLen);
if (ret != 0) {
return ret;
}
ret = wc_AsuRsaReqNew(&mem);
if (ret != 0) {
return ret;
}
XMEMSET(mem.req, 0, sizeof(*mem.req));
ret = wc_AsuRsaPubComp(key, keySize, &mem.req->key.PubKeyComp);
if (ret != 0) {
wc_AsuRsaReqFree(&mem);
return ret;
}
mem.req->key.PrimeCompOrTotientPrsnt = 0U;
if (mp_to_unsigned_bin_len_ct(&key->d, (byte*)mem.req->key.PvtExp,
(int)keySize) < 0) {
wc_AsuRsaReqFree(&mem);
return WC_HW_E;
}
/* Same empty-label rule as encrypt: a valid address with size 0. */
label = padding->label;
labelSz = padding->labelSz;
if (label == NULL && labelSz > 0) {
wc_AsuRsaReqFree(&mem);
return BUFFER_E;
}
if (label == NULL) {
label = mem.req->scratch;
labelSz = 0;
}
mem.req->op = WC_ASU_RSA_OP_OAEP_DEC;
mem.req->oaep.XAsu_RsaOpComp.InputDataAddr = (u64)(UINTPTR)info->pk.rsa.in;
/* Recover into our own buffer: the message is shorter than the modulus and
* the caller's buffer may be smaller than what the ASU is told it has. */
mem.req->oaep.XAsu_RsaOpComp.OutputDataAddr = (u64)(UINTPTR)mem.req->out;
mem.req->oaep.XAsu_RsaOpComp.KeyCompAddr = (u64)(UINTPTR)&mem.req->key;
mem.req->oaep.XAsu_RsaOpComp.Len = info->pk.rsa.inLen;
mem.req->oaep.XAsu_RsaOpComp.KeySize = keySize;
wc_AsuRsaSetOutLen(&mem.req->oaep.XAsu_RsaOpComp,
(u32)sizeof(mem.req->out), &mem.req->outLen);
mem.req->oaep.OptionalLabelAddr = (u64)(UINTPTR)label;
mem.req->oaep.OptionalLabelSize = labelSz;
mem.req->oaep.ShaType = shaType;
mem.req->oaep.ShaMode = shaMode;
WC_ASU_PRINTF("[ASU] rsa oaep-dec keySize=%u shaMode=%u labelSz=%u\r\n",
(unsigned int)keySize, (unsigned int)shaMode, (unsigned int)labelSz);
wc_AsuCacheFlush(info->pk.rsa.in, info->pk.rsa.inLen);
if (labelSz > 0) {
wc_AsuCacheFlush(label, labelSz);
}
wc_AsuCacheFlush(&mem.req->key, sizeof(mem.req->key));
/* out is a DMA target so it is flushed and invalidated; outLen is not one,
* it arrives in the mailbox response - see wc_AsuRsaSetOutLen. */
wc_AsuCacheFlush(mem.req->out, sizeof(mem.req->out));
status = wc_AsuTransact(wc_AsuRsaSubmit, mem.req, &addl);
wc_AsuCacheInvalidate(mem.req->out, sizeof(mem.req->out));
/* A decode failure is an answer, the padding is valid for this key or it
* is not. A transport or argument fault is not, so keep the two apart. */
if (status != XST_SUCCESS) {
int isPadding = wc_AsuRsaOaepStatusIsPadding((word32)status);
wc_AsuRsaReqFree(&mem);
if (isPadding) {
return RSA_PAD_E;
}
return WC_HW_E;
}
/* Trust the reported length only as far as both buffers allow. */
recovered = mem.req->outLen;
if (recovered > sizeof(mem.req->out) || recovered > *info->pk.rsa.outLen) {
wc_AsuRsaReqFree(&mem);
return RSA_BUFFER_E;
}
XMEMCPY(info->pk.rsa.out, mem.req->out, recovered);
*info->pk.rsa.outLen = recovered;
wc_AsuRsaReqFree(&mem);
return 0;
}
#endif /* WC_ASU_RSA_OAEP_DEC */
#endif /* WC_ASU_RSA_PAD (OAEP encrypt) */
/* WC_ALGO_TYPE_PK entry: dispatch on RSA pk sub-type. Raw always offloaded;
@@ -797,6 +975,11 @@ int wc_AsuRsa(wc_CryptoInfo* info)
case WC_PK_TYPE_RSA_PSS_VERIFY:
return wc_AsuRsaPssVerify(info);
case WC_PK_TYPE_RSA_OAEP:
#ifdef WC_ASU_RSA_OAEP_DEC
if (info->pk.rsa.type == RSA_PRIVATE_DECRYPT) {
return wc_AsuRsaOaepDec(info);
}
#endif
return wc_AsuRsaOaepEnc(info);
#endif /* WC_ASU_RSA_PAD */
default:
+1
View File
@@ -107,6 +107,7 @@ noinst_HEADERS+= \
wolfssl/wolfcrypt/port/xilinx/xil-versal-trng.h \
wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cipher.h \
wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cmac.h \
wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_compat.h \
wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_cryptocb.h \
wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecc.h \
wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_ecdh.h \
@@ -0,0 +1,203 @@
/* asu_compat.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
*/
/* Names that moved between the Vitis 2025.2 and 2026.1 xilasu client APIs.
* The rest of the port uses the WC_ASU_ names from here so the engine files
* read the same against either release.
*
* Set WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1 in user_settings.h for a 2026.1
* BSP; asu_settings.h assumes 2025.2 otherwise.
*
* The xilasu names are only read here, never redefined: AMD is free to bring
* an old spelling back, and a #define of theirs would then collide.
*/
#ifndef WOLFSSL_VERSAL_GEN2_ASU_COMPAT_H
#define WOLFSSL_VERSAL_GEN2_ASU_COMPAT_H
#include <wolfssl/wolfcrypt/settings.h>
#ifdef WOLFSSL_VERSAL_GEN2_ASU
#include <wolfssl/wolfcrypt/types.h> /* WC_INLINE */
/* Every header the names below come from, so this one stands on its own and
* does not depend on what an engine file happened to include first. 2026.1
* moved the operation flags into xasu_def.h; before that each engine carried
* its own set. All five ship in both releases. */
#include "xasu_def.h"
#include "xasu_aesinfo.h"
#include "xasu_hmacinfo.h"
#include "xasu_shainfo.h"
#include "xasu_eccinfo.h"
#include "xasu_rsainfo.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Catch a BSP that does not match what the build was told to expect. Without
* this the mismatch surfaces as a wall of undeclared-identifier errors from
* whichever engine file happens to compile first. */
#if defined(WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1) && !defined(XASU_INIT)
#error "WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1 named but the BSP xilasu \
predates 2026.1: name WOLFSSL_VERSAL_GEN2_ASU_XILASU_2025_2 instead"
#endif
#if defined(WOLFSSL_VERSAL_GEN2_ASU_XILASU_2025_2) && !defined(XASU_AES_INIT)
#error "WOLFSSL_VERSAL_GEN2_ASU_XILASU_2025_2 named but the BSP xilasu \
looks newer than 2025.2: name WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1 instead"
#endif
#ifdef WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1
/* 2026.1 drives every engine from one set of operation flags. */
#define WC_ASU_AES_OP_INIT XASU_INIT
#define WC_ASU_AES_OP_UPDATE XASU_UPDATE
#define WC_ASU_AES_OP_FINAL XASU_FINISH
#define WC_ASU_HMAC_OP_INIT XASU_INIT
#define WC_ASU_HMAC_OP_UPDATE XASU_UPDATE
#define WC_ASU_HMAC_OP_FINAL XASU_FINISH
#define WC_ASU_SHA_OP_START XASU_INIT
#define WC_ASU_SHA_OP_UPDATE XASU_UPDATE
#define WC_ASU_SHA_OP_FINISH XASU_FINISH
/* Curve sizes gained a private and a public form. The public one is twice the
* private, and every use in the port is a curve length, so they all map to the
* private form. The values themselves did not change. */
#define WC_ASU_ECC_P192_LEN XASU_ECC_P192_PVT_KEY_SIZE_IN_BYTES
#define WC_ASU_ECC_P256_LEN XASU_ECC_P256_PVT_KEY_SIZE_IN_BYTES
#define WC_ASU_ECC_P320_LEN XASU_ECC_P320_PVT_KEY_SIZE_IN_BYTES
#define WC_ASU_ECC_P384_LEN XASU_ECC_P384_PVT_KEY_SIZE_IN_BYTES
#define WC_ASU_ECC_P512_LEN XASU_ECC_P512_PVT_KEY_SIZE_IN_BYTES
#define WC_ASU_ECC_P521_LEN XASU_ECC_P521_PVT_KEY_SIZE_IN_BYTES
/* SHA-256 and SHAKE-256 share one length define now. Both are 32. */
#define WC_ASU_SHA_256_HASH_LEN XASU_SHA_SHAKE_256_HASH_LEN
/* The digest comes back by DMA, so the engine's own limit is the only one. */
#define WC_ASU_SHAKE_MAX_HASH_LEN XASU_SHAKE_256_MAX_HASH_LEN
#else /* Vitis 2025.2 */
#define WC_ASU_AES_OP_INIT XASU_AES_INIT
#define WC_ASU_AES_OP_UPDATE XASU_AES_UPDATE
#define WC_ASU_AES_OP_FINAL XASU_AES_FINAL
#define WC_ASU_HMAC_OP_INIT XASU_HMAC_INIT
#define WC_ASU_HMAC_OP_UPDATE XASU_HMAC_UPDATE
#define WC_ASU_HMAC_OP_FINAL XASU_HMAC_FINAL
#define WC_ASU_SHA_OP_START XASU_SHA_START
#define WC_ASU_SHA_OP_UPDATE XASU_SHA_UPDATE
#define WC_ASU_SHA_OP_FINISH XASU_SHA_FINISH
#define WC_ASU_ECC_P192_LEN XASU_ECC_P192_SIZE_IN_BYTES
#define WC_ASU_ECC_P256_LEN XASU_ECC_P256_SIZE_IN_BYTES
#define WC_ASU_ECC_P320_LEN XASU_ECC_P320_SIZE_IN_BYTES
#define WC_ASU_ECC_P384_LEN XASU_ECC_P384_SIZE_IN_BYTES
#define WC_ASU_ECC_P512_LEN XASU_ECC_P512_SIZE_IN_BYTES
#define WC_ASU_ECC_P521_LEN XASU_ECC_P521_SIZE_IN_BYTES
#define WC_ASU_SHA_256_HASH_LEN XASU_SHA_256_HASH_LEN
/* The digest came back in the 64 byte mailbox response slot, so that is the
* longest SHAKE the ASU could return however much the engine itself allows. */
#define WC_ASU_SHAKE_MAX_HASH_LEN 64
#endif /* WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1 */
/* 2026.1 moved the HMAC key into a key object so it can name a key vault
* entry instead. A zero KeyId keeps the key coming from KeyInAddr. */
static WC_INLINE void wc_AsuHmacSetKey(XAsu_HmacParams* p, const void* key,
u32 keyLen)
{
#ifdef WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1
p->KeyObject.KeyInAddr = (u64)(UINTPTR)key;
p->KeyObject.KeyInLen = keyLen;
p->KeyObject.KeyId = 0U;
#else
p->KeyAddr = (u64)(UINTPTR)key;
p->KeyLen = keyLen;
#endif
}
/* 2026.1 moved the ECC key into a key object so it can name a key vault entry
* instead. KeyLen stays the curve length for both a private scalar and a public
* point; the firmware knows a point is twice that. */
static WC_INLINE void wc_AsuEccSetKey(XAsu_EccParams* p, const void* key,
u32 keyLen)
{
#ifdef WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1
p->Key.KeyAddr = (u64)(UINTPTR)key;
p->Key.KeyLen = keyLen;
p->Key.KeyId = 0U;
#else
p->KeyAddr = (u64)(UINTPTR)key;
p->KeyLen = keyLen;
#endif
}
/* Same move for ECDH, which gains a key object per side. 2025.2 carried one
* KeyLen for both keys, so the single length feeds both objects here. */
static WC_INLINE void wc_AsuEcdhSetKeys(XAsu_EcdhParams* p, const void* pvt,
const void* pub, u32 keyLen)
{
#ifdef WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1
p->PvtKey.KeyAddr = (u64)(UINTPTR)pvt;
p->PvtKey.KeyLen = keyLen;
p->PvtKey.KeyId = 0U;
p->PubKey.KeyAddr = (u64)(UINTPTR)pub;
p->PubKey.KeyLen = keyLen;
p->PubKey.KeyId = 0U;
#else
p->PvtKeyAddr = (u64)(UINTPTR)pvt;
p->PubKeyAddr = (u64)(UINTPTR)pub;
p->KeyLen = keyLen;
#endif
}
/* 2026.1 wants the size of the caller's output buffer up front, and somewhere
* to report how much was produced. The client rejects the request when either
* is missing, so both have to be set even though 2025.2 had neither. outLen
* points at a 4-byte slot the mailbox response fills in.
* Not a DMA target, so it takes no cache maintenance: the client CPU-copies
* the value in, and invalidating the line would throw that write away. */
static WC_INLINE void wc_AsuRsaSetOutLen(XAsu_RsaParams* op, u32 bufSize,
u32* outLen)
{
#ifdef WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1
op->OutputDataLen = bufSize;
op->OutputLenAddr = (u64)(UINTPTR)outLen;
op->KeyId = 0U;
#else
(void)op; (void)bufSize; (void)outLen;
#endif
}
#ifdef __cplusplus
}
#endif
#endif /* WOLFSSL_VERSAL_GEN2_ASU */
#endif /* WOLFSSL_VERSAL_GEN2_ASU_COMPAT_H */
@@ -37,6 +37,11 @@
* WOLFSSL_VERSAL_GEN2_ASU_NO_RSA_PAD - RSA on, padding in software
* WOLFSSL_VERSAL_GEN2_ASU_IPI_BASEADDR - IPI channel to use
* WOLFSSL_VERSAL_GEN2_ASU_NO_CLIENT_INIT - app starts the client itself
*
* The xilasu client API changed in Vitis 2026.1. Name the release the BSP was
* built with; 2025.2 is assumed when neither is given:
* WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1
* WOLFSSL_VERSAL_GEN2_ASU_XILASU_2025_2
*/
#ifndef WOLFSSL_VERSAL_GEN2_ASU_SETTINGS_H
@@ -54,6 +59,22 @@
#define WOLF_CRYPTO_CB_CMD
#endif
/* Which xilasu the BSP carries. 2026.1 renamed the operation flags and moved
* the key fields of several request structures into key objects, so the port
* has to be told which one it is building against. Unlike the engine switches
* there is no software fallback for guessing wrong: the two APIs are not
* source compatible, so a conflict is an error rather than an #undef. */
#if defined(WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1) && \
defined(WOLFSSL_VERSAL_GEN2_ASU_XILASU_2025_2)
#error "Name only one of WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1 or _2025_2"
#endif
/* Assume 2025.2, so a build that predates this switch keeps working. */
#if !defined(WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1) && \
!defined(WOLFSSL_VERSAL_GEN2_ASU_XILASU_2025_2)
#define WOLFSSL_VERSAL_GEN2_ASU_XILASU_2025_2
#endif
/* No engine was named, so turn them all on. */
#if !defined(WOLFSSL_VERSAL_GEN2_ASU_TRNG) && \
!defined(WOLFSSL_VERSAL_GEN2_ASU_HASH) && \
@@ -85,6 +106,28 @@
#define WOLF_CRYPTO_CB_RSA_PAD
#endif
/* P-521 needs the firmware to front-pad the digest, which it does from Vitis
* 2026.1. Before that the curve stayed off because the padding was wrong. */
#if defined(WOLFSSL_VERSAL_GEN2_ASU_ECC) && \
defined(WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1) && \
!defined(WOLFSSL_VERSAL_GEN2_ASU_NO_ECC_P521)
#define WOLFSSL_VERSAL_GEN2_ASU_ECC_P521
#endif
/* X25519 and X448 key agreement: Curve25519 and Curve448 joined the ASU curve
* list in Vitis 2026.1, so there is nothing to offload before that. */
#if defined(WOLFSSL_VERSAL_GEN2_ASU_ECC) && \
defined(WOLFSSL_VERSAL_GEN2_ASU_XILASU_2026_1)
#ifndef WOLFSSL_VERSAL_GEN2_ASU_NO_X25519
#define WOLFSSL_VERSAL_GEN2_ASU_X25519
#endif
#ifndef WOLFSSL_VERSAL_GEN2_ASU_NO_X448
#define WOLFSSL_VERSAL_GEN2_ASU_X448
#endif
/* The key export APIs these need are checked in asu_ecdh.c, where the
* offload actually compiles: this macro alone does not mean it is built. */
#endif
/* ECDH and ECIES come along with ECC when their features are built. */
#ifdef WOLFSSL_VERSAL_GEN2_ASU_ECC
/* Those feature macros are decided later, so check user macros here. */
@@ -30,6 +30,7 @@
#include <wolfssl/wolfcrypt/types.h>
#include "xasu_client.h"
#include <wolfssl/wolfcrypt/port/xilinx/versal_gen2_asu/asu_compat.h>
#ifdef __cplusplus
extern "C" {