From a5085bc13d4ef67c1658b96be33be0dafb71913c Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 13 Aug 2026 10:27:23 -0700 Subject: [PATCH] Add TI C2000 AESA hardware AES port and fix CHAR_BIT!=8 AES defects --- .wolfssl_known_macro_extras | 5 + IDE/C2000/README.md | 114 ++++++ IDE/C2000/compile.sh | 31 +- IDE/C2000/user_settings.h | 16 + wolfcrypt/src/aes.c | 88 +++-- wolfcrypt/src/include.am | 1 + wolfcrypt/src/port/ti/ti-c2000-aes.c | 542 +++++++++++++++++++++++++++ wolfssl/wolfcrypt/include.am | 1 + wolfssl/wolfcrypt/port/ti/ti-c2000.h | 97 +++++ 9 files changed, 860 insertions(+), 35 deletions(-) create mode 100644 wolfcrypt/src/port/ti/ti-c2000-aes.c create mode 100644 wolfssl/wolfcrypt/port/ti/ti-c2000.h diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 6ab44d93c9..41124e1738 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -857,6 +857,11 @@ WOLFSSL_BIGINT_TYPES WOLFSSL_BIO_NO_FLOW_STATS WOLFSSL_BUILD_MSG_NO_ZERO_COPY WOLFSSL_BYTESWAP32_ASM +WOLFSSL_C2000_AES +WOLFSSL_C2000_AES_BASE +WOLFSSL_C2000_AES_NO_LOCK +WOLFSSL_C2000_AES_SS_BASE +WOLFSSL_C2000_DEVID WOLFSSL_CAAM_BLACK_KEY_AESCCM WOLFSSL_CAAM_BLACK_KEY_SM WOLFSSL_CAAM_NO_BLACK_KEY diff --git a/IDE/C2000/README.md b/IDE/C2000/README.md index f92a62bb50..dc0ab08661 100644 --- a/IDE/C2000/README.md +++ b/IDE/C2000/README.md @@ -69,6 +69,120 @@ TI `cl2000` (C28x) miscompiles a couple of ML-DSA 32-bit reductions on this With both, ML-DSA builds at full `-O2` on the C28x with no per-file overrides. +## Hardware AES (AESA) + +The F28P55x/F28P65x carry an "AESA" accelerator (a TI EIP-120t instance) at +`0x00042000` supporting ECB/CBC/CTR/CFB/GCM/CCM with 128/192/256-bit keys. +wolfCrypt drives it through the **crypto-callback** framework rather than by +replacing `wolfcrypt/src/aes.c`: + +- `wolfcrypt/src/port/ti/ti-c2000-aes.c` + `wolfssl/wolfcrypt/port/ti/ti-c2000.h`, + gated on `WOLFSSL_C2000_AES` (which also needs `WOLF_CRYPTO_CB`). +- `wc_C2000_Init(devId)` enables/resets the block and registers the callback. + It must be called **after `wolfCrypt_Init()`** -- that is what marks the + device table slots `INVALID_DEVID`, and registration claims one of those. +- A context opts in with `wc_AesInit(&aes, NULL, WOLFSSL_C2000_DEVID)`; one + initialised with `INVALID_DEVID` stays pure software. Software AES remains + compiled in, so a single image can run identical vectors through both paths + and compare -- which is how this port is validated. +- Anything the hardware cannot do returns `CRYPTOCB_UNAVAILABLE` and falls + through to software: non-block-multiple CBC (ciphertext stealing), key + lengths other than 16/24/32, and every mode outside ECB/CBC/CTR. + +Phase 1 covers ECB, CBC and CTR. GCM/CCM/CMAC remain software for now. + +### Measured throughput (LAUNCHXL-F28P55X at 150 MHz) + +`benchmark` with `WC_USE_DEVID` pointing at the AESA device, so it emits paired +`SW`/`HW` rows: + +| Operation | Software | AESA | Speedup | +|---|---|---|---| +| AES-128-ECB encrypt | 471 KiB/s | 2.37 MiB/s | 5.2x | +| AES-256-ECB encrypt | 377 KiB/s | 2.32 MiB/s | 6.3x | +| AES-128-CBC encrypt | 405 KiB/s | 2.36 MiB/s | 6.0x | +| AES-128-CBC decrypt | 388 KiB/s | 2.34 MiB/s | 6.2x | +| AES-256-CBC encrypt | 333 KiB/s | 2.31 MiB/s | 7.1x | +| AES-256-CBC decrypt | 322 KiB/s | 2.29 MiB/s | 7.3x | +| AES-128-CTR | 408 KiB/s | 1.45 MiB/s | 3.6x | +| AES-256-CTR | 335 KiB/s | 1.44 MiB/s | 4.4x | + +Hardware throughput is essentially key-length independent, as expected for a +pipelined block engine -- so the speedup grows with key size, where software +pays for more rounds. CTR lands lower than ECB/CBC because the port does the +counter increment and the keystream XOR in software (see above); it is still +the mode that gains least in relative terms but it is unambiguously worth +offloading. AES-GCM moves only from ~32 to ~34 KiB/s: only its internal ECB +calls reach the accelerator, and the `GCM_SMALL` byte-wise GHASH dominates. +Doing GCM properly means using the block's own GCM mode, which is phase 2. + +### Build overrides + +All `#ifndef`-guarded in `wolfssl/wolfcrypt/port/ti/ti-c2000.h`: + +| Macro | Default | Purpose | +|---|---|---| +| `WOLFSSL_C2000_DEVID` | `0x2000` | devId passed to `wc_AesInit()` and `wc_CryptoCb_RegisterDevice()` | +| `WOLFSSL_C2000_AES_BASE` | `0x00042000` | AESA register base (`AESA_BASE`) | +| `WOLFSSL_C2000_AES_SS_BASE` | `0x00042C00` | AESA wrapper base (`AESA_SS_BASE`) | +| `WOLFSSL_C2000_AES_NO_LOCK` | off | Assert an external lock instead of requiring `SINGLE_THREADED` | + +The two base addresses are defaulted in the port rather than taken from a +device header, so the same source builds against any C2000 part that places +the block elsewhere. + +### Two things the 16-bit byte forces + +**Octet packing.** C2000Ware's AES API is `uint32_t*`-based. On the C28x a +`byte` buffer holds one octet per 16-bit cell and `sizeof(word32)` is 2, so the +`(uint32_t*)in` cast the TivaWare port (`ti-aes.c`) uses is wrong here. Every +transfer is staged through a local `uint32_t` block using the packing driverlib +actually expects, confirmed against its own vectors +(`driverlib/f28p55x/examples/aes/aes_ex1_ecb_encrypt.c` writes the FIPS-197 key +`2b7e1516...` as `{0x16157e2b, ...}`): + +``` +word[i] = b[4i] | (b[4i+1] << 8) | (b[4i+2] << 16) | (b[4i+3] << 24) +``` + +little-endian octets within each word, words in natural order. The +word-index reversal inside `AES_writeDataBlocking()` is internal to driverlib +and must not be compensated for. The port packs and unpacks locally +(`c2000_WordsFromOctets()` / `c2000_OctetsFromWords()`), staging through +`uint32_t` rather than wolfSSL's `word32`: `word32` is only 32-bit under +`WC_16BIT_CPU`, while driverlib writes `uint32_t` either way, so a `word32` +staging array would be half the size the hardware fills. + +**The hardware CTR counter does not match wolfCrypt's.** Measured on a +LAUNCHXL-F28P55X: with `AES_OPMODE_CTR` + `AES_CTR_WIDTH_128BIT` the first +block matches NIST SP800-38A F.5.1, but later blocks diverge from software as +soon as an increment carries across an octet boundary (the F.5 counter starts +at `...fe ff`, so block 2 already does). The block's 128-bit counter increment +therefore disagrees with `IncrementAesCounter()`, which carries through all 16 +octets. The port instead runs the accelerator in **ECB** mode and keeps the +counter in software: identical hardware block-operation count, correct by +construction. + +Both traps are silent -- the first block is right either way, which is exactly +why the KAT harness checks multi-block, split-call and in-place cases. + +### Chaining state + +`aes->reg` is updated in software (last ciphertext block on encrypt, a copy of +the last input block saved *before* processing on decrypt, so in-place calls +work). `AES_readInitializationVector()` is deliberately not used: the +`IV_IN_OUT` registers only hold the saved context when `CTRL.SAVE_CONTEXT` is +set, which `AES_configureModule()` does not set, and driverlib's reader does +not poll `CTRL.SVCTXTRDY` the way `AES_readTag()` does. + +### Threading + +The AESA block is a single shared resource and the port reloads key, IV and +mode on every operation, so it is re-entrant across `Aes` contexts but **not** +across preemption or an ISR. `ti-c2000.h` therefore `#error`s unless +`SINGLE_THREADED` is defined, or `WOLFSSL_C2000_AES_NO_LOCK` asserts that an +external lock provides the guarantee. + ## Enabling on your build Define a user-settings header (see `IDE/C2000/user_settings.h` for a diff --git a/IDE/C2000/compile.sh b/IDE/C2000/compile.sh index 13de96b92f..e0d67e4f50 100755 --- a/IDE/C2000/compile.sh +++ b/IDE/C2000/compile.sh @@ -30,7 +30,10 @@ fi OUT=$(mktemp -d) trap 'rm -rf "$OUT"' EXIT -INCS="-I$CGT_ROOT/include -I$WOLFROOT -I$SELF_DIR" +# $SELF_DIR before $WOLFROOT: wolfSSL's documented user_settings.h workflow +# puts one at the repo root, and if $WOLFROOT came first that copy would shadow +# this guard's config and silently compile a different build. +INCS="-I$CGT_ROOT/include -I$SELF_DIR -I$WOLFROOT" CFLAGS="-v28 --abi=eabi --float_support=fpu32 --tmu_support=tmu1 -O2 \ --define=WOLFSSL_USER_SETTINGS --display_error_number --diag_warning=225" @@ -42,7 +45,7 @@ CFLAGS="-v28 --abi=eabi --float_support=fpu32 --tmu_support=tmu1 -O2 \ # covered by the on-target example build, not by this minimal guard. SRCS="error wc_port memory logging misc coding \ sha sha256 sha512 sha3 wc_mldsa random ecc sp_int sp_c32 \ - aes cmac chacha poly1305 \ + aes cmac chacha poly1305 cryptocb \ curve25519 ed25519 fe_operations ge_operations \ curve448 ed448 fe_448 ge_448" @@ -60,6 +63,30 @@ for s in $SRCS; do fi done +# The AESA hardware-AES port needs C2000Ware driverlib headers, which CI does +# not download, so it is an opt-in extra leg: set C2000WARE to a C2000Ware +# install to include it. +if [ -n "${C2000WARE:-}" ]; then + DRV="$C2000WARE/driverlib/f28p55x/driverlib" + printf 'CC port/ti/ti-c2000-aes.c ... ' + if "$CL" $CFLAGS $INCS -I"$DRV" \ + -I"$C2000WARE/device_support/f28p55x/common/include" \ + -I"$C2000WARE/device_support/f28p55x/headers/include" \ + --define=WOLF_CRYPTO_CB --define=WOLFSSL_C2000_AES \ + --compile_only --skip_assembler \ + --asm_directory="$OUT" --obj_directory="$OUT" \ + "$WOLFROOT/wolfcrypt/src/port/ti/ti-c2000-aes.c" \ + > "$OUT/ti-c2000-aes.log" 2>&1; then + echo "ok" + else + echo "FAIL" + cat "$OUT/ti-c2000-aes.log" + rc=1 + fi +else + echo "SKIP port/ti/ti-c2000-aes.c (set C2000WARE to include it)" +fi + if [ "$rc" -eq 0 ]; then echo "TI C2000 compile-only guard: PASS" else diff --git a/IDE/C2000/user_settings.h b/IDE/C2000/user_settings.h index 01978fcbed..b6c202bb25 100644 --- a/IDE/C2000/user_settings.h +++ b/IDE/C2000/user_settings.h @@ -17,6 +17,13 @@ #define WOLFSSL_NO_ASM #define NO_INLINE #define SINGLE_THREADED + +/* C28x has a 16-bit int. Without this word32 is `unsigned int` (16 bits) and + * every 32-bit crypto value silently truncates. long is 32-bit and long long + * 64-bit on this toolchain. */ +#define WC_16BIT_CPU +#define SIZEOF_LONG 4 +#define SIZEOF_LONG_LONG 8 #define NO_FILESYSTEM #define NO_WOLFSSL_DIR #define NO_MAIN_DRIVER @@ -47,6 +54,15 @@ #define WOLFSSL_AES_SIV #define WOLFSSL_AES_EAX #define WOLFSSL_AES_DIRECT +#define HAVE_AES_ECB + +/* Crypto callbacks, so cryptocb.c is compile-guarded here too. This is what + * the AESA hardware-AES port (WOLFSSL_C2000_AES, wolfcrypt/src/port/ti/ + * ti-c2000-aes.c) plugs into. WOLFSSL_C2000_AES itself is deliberately NOT + * set: it needs C2000Ware driverlib headers, which this hardware-free guard + * does not have. compile.sh builds that file as a separate opt-in leg when + * C2000WARE is set in the environment. */ +#define WOLF_CRYPTO_CB /* ChaCha20-Poly1305 (chunk size, keystream and Poly1305 length octet I/O) */ #define HAVE_CHACHA diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index cd8615e00f..26550bf025 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -8005,7 +8005,10 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) /* in network byte order so start at end and work back */ int i; for (i = WC_AES_BLOCK_SIZE - 1; i >= 0; i--) { - if (++inOutCtr[i]) /* we're done unless we overflow */ + /* WC_OCTET, not a bare ++: where CHAR_BIT != 8 a byte cell + * holds 0x100 and never wraps, so the carry is lost. */ + inOutCtr[i] = WC_OCTET(inOutCtr[i] + 1); + if (inOutCtr[i] != 0) /* we're done unless we overflow */ return; } } @@ -8362,7 +8365,9 @@ static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz) { int i; for (i = (int)ctrSz - 1; i >= 0; i--) { - if (++ctr[i]) + /* See IncrementAesCounter() on why this masks to an octet. */ + ctr[i] = WC_OCTET(ctr[i] + 1); + if (ctr[i] != 0) break; } } @@ -8468,7 +8473,9 @@ static WC_INLINE void IncrementGcmCounter(byte* inOutCtr) /* in network byte order so start at end and work back */ for (i = WC_AES_BLOCK_SIZE - 1; i >= WC_AES_BLOCK_SIZE - CTR_SZ; i--) { - if (++inOutCtr[i]) /* we're done unless we overflow */ + /* See IncrementAesCounter() on why this masks to an octet. */ + inOutCtr[i] = WC_OCTET(inOutCtr[i] + 1); + if (inOutCtr[i] != 0) /* we're done unless we overflow */ return; } } @@ -8492,19 +8499,21 @@ static WC_INLINE void IncrementGcmCounter(byte* inOutCtr) static WC_INLINE void FlattenSzInBits(byte* buf, word32 sz) { - /* Multiply the sz by 8 */ - word32 szHi = (sz >> (8*sizeof(sz) - 3)); + /* Multiply the sz by 8. CHAR_BIT * sizeof, not 8 * sizeof: sizeof counts + * cells, so the latter is a 16-bit width where CHAR_BIT == 16. */ + word32 szHi = (sz >> (CHAR_BIT * sizeof(sz) - 3)); sz <<= 3; - /* copy over the words of the sz into the destination buffer */ - buf[0] = (byte)(szHi >> 24); - buf[1] = (byte)(szHi >> 16); - buf[2] = (byte)(szHi >> 8); - buf[3] = (byte)szHi; - buf[4] = (byte)(sz >> 24); - buf[5] = (byte)(sz >> 16); - buf[6] = (byte)(sz >> 8); - buf[7] = (byte)sz; + /* WC_OCTET, not (byte): the cast keeps the full cell where CHAR_BIT != 8, + * so a 60-octet ciphertext (480 bits) would store 0x1E0 in buf[7]. */ + buf[0] = WC_OCTET(szHi >> 24); + buf[1] = WC_OCTET(szHi >> 16); + buf[2] = WC_OCTET(szHi >> 8); + buf[3] = WC_OCTET(szHi); + buf[4] = WC_OCTET(sz >> 24); + buf[5] = WC_OCTET(sz >> 16); + buf[6] = WC_OCTET(sz >> 8); + buf[7] = WC_OCTET(sz); } @@ -10533,9 +10542,9 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, word32 len[4]; /* Lengths are in bytes. Convert to bits. */ - len[0] = (aSz >> (8*sizeof(aSz) - 3)); + len[0] = (aSz >> (CHAR_BIT*sizeof(aSz) - 3)); len[1] = aSz << 3; - len[2] = (cSz >> (8*sizeof(cSz) - 3)); + len[2] = (cSz >> (CHAR_BIT*sizeof(cSz) - 3)); len[3] = cSz << 3; x[0] ^= len[0]; @@ -10594,9 +10603,9 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, word32 len[4]; \ word32* x = (word32*)AES_TAG(aes); \ word32* h = (word32*)aes->gcm.H; \ - len[0] = (aes->aSz >> (8*sizeof(aes->aSz) - 3)); \ + len[0] = (aes->aSz >> (CHAR_BIT*sizeof(aes->aSz) - 3)); \ len[1] = aes->aSz << 3; \ - len[2] = (aes->cSz >> (8*sizeof(aes->cSz) - 3)); \ + len[2] = (aes->cSz >> (CHAR_BIT*sizeof(aes->cSz) - 3)); \ len[3] = aes->cSz << 3; \ x[0] ^= len[0]; \ x[1] ^= len[1]; \ @@ -10643,9 +10652,9 @@ void GHASH(Gcm* gcm, const byte* a, word32 aSz, const byte* c, word32 len[4]; \ word32* x = (word32*)AES_TAG(aes); \ word32* h = (word32*)aes->gcm.H; \ - len[0] = (aes->aSz >> (8*sizeof(aes->aSz) - 3)); \ + len[0] = (aes->aSz >> (CHAR_BIT*sizeof(aes->aSz) - 3)); \ len[1] = aes->aSz << 3; \ - len[2] = (aes->cSz >> (8*sizeof(aes->cSz) - 3)); \ + len[2] = (aes->cSz >> (CHAR_BIT*sizeof(aes->cSz) - 3)); \ len[3] = aes->cSz << 3; \ x[0] ^= len[0]; \ x[1] ^= len[1]; \ @@ -15497,20 +15506,22 @@ static WARN_UNUSED_RESULT int roll_auth( word32 remainder; int ret; - /* encode the length in */ + /* encode the length in. WC_OCTET, not (byte): the cast keeps the whole + * cell where CHAR_BIT != 8, so any length above 0xFF would XOR stray bits + * into the CBC-MAC input block. */ if (inSz <= 0xFEFF) { authLenSz = 2; - out[0] ^= (byte)(inSz >> 8); - out[1] ^= (byte)inSz; + out[0] ^= WC_OCTET(inSz >> 8); + out[1] ^= WC_OCTET(inSz); } else { authLenSz = 6; out[0] ^= 0xFF; out[1] ^= 0xFE; - out[2] ^= (byte)(inSz >> 24); - out[3] ^= (byte)(inSz >> 16); - out[4] ^= (byte)(inSz >> 8); - out[5] ^= (byte)inSz; + out[2] ^= WC_OCTET(inSz >> 24); + out[3] ^= WC_OCTET(inSz >> 16); + out[4] ^= WC_OCTET(inSz >> 8); + out[5] ^= WC_OCTET(inSz); } /* Note, the protocol handles auth data up to 2^64, but we are * using 32-bit sizes right now, so the bigger data isn't handled @@ -15549,7 +15560,11 @@ static WC_INLINE void AesCcmCtrInc(byte* B, word32 lenSz) word32 i; for (i = 0; i < lenSz; i++) { - if (++B[WC_AES_BLOCK_SIZE - 1 - i] != 0) return; + /* See IncrementAesCounter(): a bare ++byte leaves 0x100 in the cell + * and never carries. */ + B[WC_AES_BLOCK_SIZE - 1 - i] = + WC_OCTET(B[WC_AES_BLOCK_SIZE - 1 - i] + 1); + if (B[WC_AES_BLOCK_SIZE - 1 - i] != 0) return; } } @@ -17104,13 +17119,15 @@ static void shiftLeftArray(byte* ary, byte shift) ary[i] = 0; } else { - /* shifting over by 7 or less bits */ + /* shifting over by 7 or less bits. WC_OCTET on the stores: a (byte) + * cast does not drop bits shifted past bit 7 where CHAR_BIT != 8, so + * cells would exceed 0xFF and corrupt the feedback register. */ for (i = 0; i < WC_AES_BLOCK_SIZE - 1; i++) { byte carry = (byte)(ary[i+1] & (0XFF << (WOLFSSL_BIT_SIZE - shift))); carry = (byte)(carry >> (WOLFSSL_BIT_SIZE - shift)); - ary[i] = (byte)((ary[i] << shift) + carry); + ary[i] = WC_OCTET((ary[i] << shift) + carry); } - ary[i] = (byte)(ary[i] << shift); + ary[i] = WC_OCTET(ary[i] << shift); } } @@ -17490,7 +17507,9 @@ static WC_INLINE void IncrementKeyWrapCounter(byte* inOutCtr) /* in network byte order so start at end and work back */ for (i = KEYWRAP_BLOCK_SIZE - 1; i >= 0; i--) { - if (++inOutCtr[i]) /* we're done unless we overflow */ + /* See IncrementAesCounter() on why this masks to an octet. */ + inOutCtr[i] = WC_OCTET(inOutCtr[i] + 1); + if (inOutCtr[i] != 0) /* we're done unless we overflow */ return; } } @@ -17501,7 +17520,10 @@ static WC_INLINE void DecrementKeyWrapCounter(byte* inOutCtr) int i; for (i = KEYWRAP_BLOCK_SIZE - 1; i >= 0; i--) { - if (--inOutCtr[i] != 0xFF) /* we're done unless we underflow */ + /* Where CHAR_BIT != 8 a bare --byte underflows 0x00 to 0xFFFF, not + * 0xFF, so the borrow is lost. */ + inOutCtr[i] = WC_OCTET(inOutCtr[i] - 1); + if (inOutCtr[i] != 0xFF) /* we're done unless we underflow */ return; } } diff --git a/wolfcrypt/src/include.am b/wolfcrypt/src/include.am index 366c141f2d..6d09af738c 100644 --- a/wolfcrypt/src/include.am +++ b/wolfcrypt/src/include.am @@ -81,6 +81,7 @@ EXTRA_DIST += wolfcrypt/src/port/ti/ti-aes.c \ wolfcrypt/src/port/ti/ti-des3.c \ wolfcrypt/src/port/ti/ti-hash.c \ wolfcrypt/src/port/ti/ti-ccm.c \ + wolfcrypt/src/port/ti/ti-c2000-aes.c \ wolfcrypt/src/port/pic32/pic32mz-crypt.c \ wolfcrypt/src/port/nrf51.c \ wolfcrypt/src/port/aria/aria-crypt.c \ diff --git a/wolfcrypt/src/port/ti/ti-c2000-aes.c b/wolfcrypt/src/port/ti/ti-c2000-aes.c new file mode 100644 index 0000000000..085025ebc5 --- /dev/null +++ b/wolfcrypt/src/port/ti/ti-c2000-aes.c @@ -0,0 +1,542 @@ +/* port/ti/ti-c2000-aes.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 + */ + +/* AES offload to the TI C2000 "AESA" block (EIP-120t) via crypto callbacks. + * Model and build options: wolfssl/wolfcrypt/port/ti/ti-c2000.h. + * + * Octet/word contract, confirmed against driverlib's own vectors in + * driverlib/f28p55x/examples/aes/aes_ex1_ecb_encrypt.c (FIPS-197 key + * 2b7e1516... is written {0x16157e2b, ...}): word j holds octets 4j..4j+3, + * little-endian within the word, index increasing with octet offset. The + * register-index reversal inside AES_writeDataBlocking() is internal to + * driverlib - do not compensate for it. + * + * This matters because CHAR_BIT == 16 here: a byte buffer is one octet per + * 16-bit cell and sizeof(word32) is 2, so the (uint32_t*) casts the TivaWare + * port uses are wrong. Every transfer stages through a local word32 block, + * which also makes alignment and short trailing blocks non-issues. + * + * Build options (all #ifndef-guarded in ti-c2000.h, see IDE/C2000/README.md): + * WOLFSSL_C2000_AES enable this port (needs WOLF_CRYPTO_CB) + * WOLFSSL_C2000_DEVID devId for wc_AesInit()/RegisterDevice (0x2000) + * WOLFSSL_C2000_AES_BASE AESA register base (0x00042000) + * WOLFSSL_C2000_AES_SS_BASE AESA wrapper base (0x00042C00) + * WOLFSSL_C2000_AES_NO_LOCK assert an external lock instead of requiring + * SINGLE_THREADED + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_C2000_AES) && !defined(NO_AES) + +#if !defined(WOLF_CRYPTO_CB) + #error "WOLFSSL_C2000_AES requires WOLF_CRYPTO_CB" +#endif + +/* uint32_t/uint64_t are used directly below to match the driverlib API. Pull + * them in here rather than relying on the C2000Ware headers to provide them, + * so the port does not depend on include order. */ +#include + +#include +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +/* C2000Ware driverlib. No name collisions with wolfssl/wolfcrypt/aes.h: + * driverlib prefixes AES_DIRECTION_/AES_KEY_SIZE_/AES_OPMODE_, wolfCrypt uses + * AES_ENCRYPTION/AES_128_KEY_SIZE. */ +#include "aes.h" +#include "sysctl.h" + +/* Words per AES block. Not sizeof-based: sizeof(word32) is 2 here, so + * WC_AES_BLOCK_SIZE / sizeof(word32) would be 8. */ +#define C2000_BLOCK_WORDS 4 + +/* Largest key in words (AES-256). */ +#define C2000_MAX_KEY_WORDS 8 + + +/* Pack octets into words, little-endian within each word. + * + * Deliberately uint32_t, not wolfSSL's word32: word32 is only 32-bit under + * WC_16BIT_CPU, and driverlib writes uint32_t either way - a word32 staging + * array would be half the size the hardware fills. Accumulates with <<= 8 + * because cl2000 miscompiles a single (uint32_t)octet << 24 as a 16-bit + * shift (see misc.c WordsFromBytesBE32). */ +static void c2000_WordsFromOctets(uint32_t* w, const byte* b, word32 wordCnt) +{ + word32 i; + uint32_t r; + for (i = 0; i < wordCnt; i++) { + r = (uint32_t)(b[(i * 4) + 3] & 0xFF); r <<= 8; + r |= (uint32_t)(b[(i * 4) + 2] & 0xFF); r <<= 8; + r |= (uint32_t)(b[(i * 4) + 1] & 0xFF); r <<= 8; + r |= (uint32_t)(b[(i * 4) + 0] & 0xFF); + w[i] = r; + } +} +static void c2000_OctetsFromWords(byte* b, const uint32_t* w, word32 byteCnt) +{ + word32 i; + for (i = 0; i < byteCnt; i++) { + b[i] = WC_OCTET(w[i >> 2] >> ((i & 0x3) * 8)); + } +} +#define C2000_WORDS_FROM_OCTETS(w, b, n) c2000_WordsFromOctets((w), (b), (n)) +#define C2000_OCTETS_FROM_WORDS(b, w, n) c2000_OctetsFromWords((b), (w), (n)) + + +/* Stage one block, zero-padding a short tail. The zeros are what make the + * unused part of a CTR output block equal the raw keystream. */ +static void c2000_LoadBlock(uint32_t blk[C2000_BLOCK_WORDS], const byte* b, + word32 nOctets) +{ + byte tmp[WC_AES_BLOCK_SIZE]; + + if (nOctets >= WC_AES_BLOCK_SIZE) { + C2000_WORDS_FROM_OCTETS(blk, b, C2000_BLOCK_WORDS); + } + else { + XMEMSET(tmp, 0, WC_AES_BLOCK_SIZE); + XMEMCPY(tmp, b, nOctets); + C2000_WORDS_FROM_OCTETS(blk, tmp, C2000_BLOCK_WORDS); + ForceZero(tmp, WC_AES_BLOCK_SIZE); + } +} + + +/* The context must actually be bound to this device. Under + * WOLF_CRYPTO_CB_FIND wolfCrypt offers contexts of any devId, including ones + * whose devKey the backend never populated - encrypting with that all-zero + * key would be silent and catastrophic. Defined ahead of its callers: an + * implicit declaration here would defeat the guard's own prototype. */ +static int c2000_DevIdOk(int devId, const Aes* aes) +{ + if (aes == NULL) { + return 0; + } + if (aes->devId == INVALID_DEVID) { + return 0; + } + return (aes->devId == devId); +} + + +/* Key length (octets) -> driverlib enum; CRYPTOCB_UNAVAILABLE otherwise so + * software takes the operation. */ +static int c2000_KeySize(const Aes* aes, AES_KeySize* ks) +{ + switch (aes->keylen) { + case 16: + *ks = AES_KEY_SIZE_128BIT; + break; + case 24: + *ks = AES_KEY_SIZE_192BIT; + break; + case 32: + *ks = AES_KEY_SIZE_256BIT; + break; + default: + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + return 0; +} + + +#ifdef WOLFSSL_AES_COUNTER +/* Big-endian 128-bit increment, matching the static IncrementAesCounter() in + * aes.c. aes->reg stays authoritative in software so a caller can interleave + * hardware and software CTR calls on one context. */ +static void c2000_IncrCounter(byte* ctr) +{ + int i; + for (i = WC_AES_BLOCK_SIZE - 1; i >= 0; i--) { + /* WC_OCTET, not a bare ++: a byte cell here is 16 bits, so 0xFF + 1 + * is 0x100 and the carry would never propagate. */ + ctr[i] = WC_OCTET(ctr[i] + 1); + if (ctr[i] != 0) { + return; + } + } +} +#endif /* WOLFSSL_AES_COUNTER */ + + +/* Program the block for one operation. Order is load-bearing: a soft reset + * clears CTRL, KEY1 and IV, so it must be reset -> configure -> IV -> key -> + * length, and the length write starts the engine. dataLen is padded to whole + * blocks so the hardware always emits a complete final block. */ +static int c2000_AesSetup(Aes* aes, AES_Direction dir, AES_OpMode mode, + AES_CounterWidth ctrWidth, const byte* iv16, word32 dataLen) +{ + AES_ConfigParams cfg; + AES_KeySize ks; + uint32_t kw[C2000_MAX_KEY_WORDS]; + uint32_t ivw[C2000_BLOCK_WORDS]; + int ret; + + ret = c2000_KeySize(aes, &ks); + if (ret != 0) { + return ret; + } + + AES_disableGlobalInterrupt(WOLFSSL_C2000_AES_SS_BASE); + AES_performSoftReset(WOLFSSL_C2000_AES_BASE); + + XMEMSET(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + cfg.keySize = ks; + cfg.opMode = mode; + cfg.ctrWidth = ctrWidth; + cfg.ccmLenWidth = AES_CCM_L_1; + cfg.ccmAuthLenWidth = AES_CCM_M_0; + AES_configureModule(WOLFSSL_C2000_AES_BASE, &cfg); + + if (iv16 != NULL) { + C2000_WORDS_FROM_OCTETS(ivw, iv16, C2000_BLOCK_WORDS); + AES_setInitializationVector(WOLFSSL_C2000_AES_BASE, + (const uint32_t*)ivw); + ForceZero(ivw, sizeof(ivw)); + } + + XMEMSET(kw, 0, sizeof(kw)); + C2000_WORDS_FROM_OCTETS(kw, (const byte*)aes->devKey, + (word32)aes->keylen / 4); + AES_setKey1(WOLFSSL_C2000_AES_BASE, (const uint32_t*)kw, ks); + ForceZero(kw, sizeof(kw)); + + AES_setDataLength(WOLFSSL_C2000_AES_BASE, (uint64_t)dataLen); + + return 0; +} + + +/* Straight in -> out loop for the modes the hardware chains itself (ECB, CBC). + * Not AES_processData(): that redoes a 64-bit division every iteration, which + * is costly on a C28x, and we need per-block marshalling anyway. */ +static int c2000_AesProcess(Aes* aes, byte* out, const byte* in, word32 sz, + AES_Direction dir, AES_OpMode mode, const byte* iv16) +{ + uint32_t blk[C2000_BLOCK_WORDS]; + uint32_t outw[C2000_BLOCK_WORDS]; + word32 off; + word32 n; + int ret; + + ret = c2000_AesSetup(aes, dir, mode, AES_CTR_WIDTH_32BIT, iv16, sz); + if (ret != 0) { + return ret; + } + + for (off = 0; off < sz; off += WC_AES_BLOCK_SIZE) { + n = sz - off; + if (n > WC_AES_BLOCK_SIZE) { + n = WC_AES_BLOCK_SIZE; + } + + c2000_LoadBlock(blk, in + off, n); + AES_writeDataBlocking(WOLFSSL_C2000_AES_BASE, (const uint32_t*)blk); + AES_readDataBlocking(WOLFSSL_C2000_AES_BASE, (uint32_t*)outw); + C2000_OCTETS_FROM_WORDS(out + off, outw, n); + } + + ForceZero(blk, sizeof(blk)); + ForceZero(outw, sizeof(outw)); + + return 0; +} + + +#if defined(HAVE_AES_ECB) || defined(WOLFSSL_AES_DIRECT) || \ + defined(WOLF_CRYPTO_CB_ONLY_AES) +static int c2000_Ecb(int devId, struct wc_CryptoInfo* info) +{ + Aes* aes = info->cipher.aesecb.aes; + byte* out = info->cipher.aesecb.out; + const byte* in = info->cipher.aesecb.in; + word32 sz = info->cipher.aesecb.sz; + + if (aes == NULL || out == NULL || in == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (!c2000_DevIdOk(devId, aes)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (sz == 0) { + return 0; + } + if ((sz % WC_AES_BLOCK_SIZE) != 0) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + return c2000_AesProcess(aes, out, in, sz, + info->cipher.enc ? AES_DIRECTION_ENCRYPT : AES_DIRECTION_DECRYPT, + AES_OPMODE_ECB, NULL); +} +#endif /* HAVE_AES_ECB || WOLFSSL_AES_DIRECT || WOLF_CRYPTO_CB_ONLY_AES */ + + +#ifdef HAVE_AES_CBC +static int c2000_Cbc(int devId, struct wc_CryptoInfo* info) +{ + Aes* aes = info->cipher.aescbc.aes; + byte* out = info->cipher.aescbc.out; + const byte* in = info->cipher.aescbc.in; + word32 sz = info->cipher.aescbc.sz; + byte lastIn[WC_AES_BLOCK_SIZE]; + int enc = info->cipher.enc; + int ret; + + if (aes == NULL || out == NULL || in == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (!c2000_DevIdOk(devId, aes)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (sz == 0) { + return 0; + } + /* Non-block-multiple means a ciphertext-stealing caller; leave to SW. */ + if ((sz % WC_AES_BLOCK_SIZE) != 0) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + /* Saved before processing so an in-place call (in == out) still has it. */ + if (!enc) { + XMEMCPY(lastIn, in + sz - WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); + } + + ret = c2000_AesProcess(aes, out, in, sz, + enc ? AES_DIRECTION_ENCRYPT : AES_DIRECTION_DECRYPT, + AES_OPMODE_CBC, (const byte*)aes->reg); + if (ret != 0) { + ForceZero(lastIn, sizeof(lastIn)); + return ret; + } + + /* aes->reg must hold the last ciphertext block so successive calls chain. + * Derived in software, not via AES_readInitializationVector(): IV_IN_OUT + * only holds saved context when CTRL.SAVE_CONTEXT is set (which + * AES_configureModule() does not do), and driverlib's reader does not poll + * CTRL.SVCTXTRDY the way AES_readTag() does. */ + if (enc) { + XMEMCPY(aes->reg, out + sz - WC_AES_BLOCK_SIZE, WC_AES_BLOCK_SIZE); + } + else { + XMEMCPY(aes->reg, lastIn, WC_AES_BLOCK_SIZE); + ForceZero(lastIn, WC_AES_BLOCK_SIZE); + } + + return 0; +} +#endif /* HAVE_AES_CBC */ + + +#ifdef WOLFSSL_AES_COUNTER +static int c2000_Ctr(int devId, struct wc_CryptoInfo* info) +{ + Aes* aes = info->cipher.aesctr.aes; + byte* out = info->cipher.aesctr.out; + const byte* in = info->cipher.aesctr.in; + word32 sz = info->cipher.aesctr.sz; + uint32_t lastOut[C2000_BLOCK_WORDS]; + uint32_t ctrw[C2000_BLOCK_WORDS]; + AES_KeySize ksz; + byte ks[WC_AES_BLOCK_SIZE]; + word32 blocks; + word32 used; + word32 tail; + word32 off; + word32 n; + word32 i; + int ret; + + if (aes == NULL || out == NULL || in == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (!c2000_DevIdOk(devId, aes)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + /* Decide before mutating state: CRYPTOCB_UNAVAILABLE makes wolfCrypt redo + * the operation in software from the original pointers, so consuming + * leftover keystream first would double-consume it. */ + ret = c2000_KeySize(aes, &ksz); + if (ret != 0) { + return ret; + } + (void)ksz; /* only the accept/reject verdict is needed here */ + + /* Consume leftover keystream: the callback fires before wc_AesCtrEncrypt + * does this itself, so the state is ours. */ + if (aes->left > 0) { + used = (aes->left < sz) ? aes->left : sz; + xorbufout(out, in, + (byte*)aes->tmp + WC_AES_BLOCK_SIZE - aes->left, used); + out += used; + in += used; + sz -= used; + aes->left -= used; + } + if (sz == 0) { + return 0; + } + + /* sz is bounded by RAM on this part, so this cannot overflow in practice; + * the division is written to be safe anyway. */ + blocks = (sz / WC_AES_BLOCK_SIZE) + + (((sz % WC_AES_BLOCK_SIZE) != 0) ? 1U : 0U); + + /* Keystream comes from hardware ECB with the counter kept in software, + * not AES_OPMODE_CTR. Measured on a LAUNCHXL-F28P55X: with + * AES_CTR_WIDTH_128BIT the first block matches NIST SP800-38A F.5.1 but + * later blocks diverge once an increment carries across an octet boundary + * (F.5 starts at ...fe ff, so block 2 already does) - the hardware counter + * disagrees with wolfCrypt's IncrementAesCounter(). ECB costs the same + * number of hardware block operations and is correct by construction. */ + ret = c2000_AesSetup(aes, AES_DIRECTION_ENCRYPT, AES_OPMODE_ECB, + AES_CTR_WIDTH_32BIT, NULL, blocks * WC_AES_BLOCK_SIZE); + if (ret != 0) { + return ret; + } + + for (i = 0; i < blocks; i++) { + off = i * WC_AES_BLOCK_SIZE; + n = sz - off; + if (n > WC_AES_BLOCK_SIZE) { + n = WC_AES_BLOCK_SIZE; + } + + /* Encrypt the counter block to get this block's keystream. */ + C2000_WORDS_FROM_OCTETS(ctrw, (const byte*)aes->reg, + C2000_BLOCK_WORDS); + AES_writeDataBlocking(WOLFSSL_C2000_AES_BASE, (const uint32_t*)ctrw); + AES_readDataBlocking(WOLFSSL_C2000_AES_BASE, (uint32_t*)lastOut); + C2000_OCTETS_FROM_WORDS(ks, lastOut, WC_AES_BLOCK_SIZE); + + xorbufout(out + off, in + off, ks, n); + c2000_IncrCounter((byte*)aes->reg); + } + + tail = sz % WC_AES_BLOCK_SIZE; + if (tail != 0) { + /* Octets tail..15 are unconsumed keystream. Store the whole block; + * software reads it from the end (tmp + BLOCK - left). */ + XMEMCPY(aes->tmp, ks, WC_AES_BLOCK_SIZE); + aes->left = WC_AES_BLOCK_SIZE - tail; + } + else { + aes->left = 0; + } + + ForceZero(ks, sizeof(ks)); + ForceZero(ctrw, sizeof(ctrw)); + ForceZero(lastOut, sizeof(lastOut)); + + return 0; +} +#endif /* WOLFSSL_AES_COUNTER */ + + +int wc_C2000_CryptoCb(int devId, struct wc_CryptoInfo* info, void* ctx) +{ + (void)ctx; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + if (info->algo_type != WC_ALGO_TYPE_CIPHER) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + switch (info->cipher.type) { +#ifdef HAVE_AES_CBC + case WC_CIPHER_AES_CBC: + return c2000_Cbc(devId, info); +#endif +#ifdef WOLFSSL_AES_COUNTER + case WC_CIPHER_AES_CTR: + return c2000_Ctr(devId, info); +#endif +#if defined(HAVE_AES_ECB) || defined(WOLFSSL_AES_DIRECT) || \ + defined(WOLF_CRYPTO_CB_ONLY_AES) + case WC_CIPHER_AES_ECB: + return c2000_Ecb(devId, info); +#endif + default: + break; + } + + /* CFB, OFB, XTS, GCM, CCM and DES3 fall through to software. */ + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); +} + + +/* Whether this port ungated the AESA clock, so teardown knows if the + * peripheral is reachable. Mirrors c2000_clkOn in the entropy port. */ +static int c2000_aesOn = 0; + +int wc_C2000_Init(int devId) +{ + /* Device_init() already does this on the LaunchPad BSP; repeated so the + * port works without that BSP. */ + SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_AESA); + SysCtl_delay(10); + SysCtl_resetPeripheral(SYSCTL_PERIPH_RES_AESA); + + AES_disableGlobalInterrupt(WOLFSSL_C2000_AES_SS_BASE); + AES_performSoftReset(WOLFSSL_C2000_AES_BASE); + c2000_aesOn = 1; + + return wc_CryptoCb_RegisterDevice(devId, wc_C2000_CryptoCb, NULL); +} + + +int wc_C2000_Cleanup(int devId) +{ + /* Only touch the peripheral if this port turned its clock on. A Cleanup() + * with no prior Init() - an application error path, say - would otherwise + * write AESA registers while the clock is gated, which raises a system + * access error on these parts rather than being a quiet no-op. + * Unregistering the device is safe either way. */ + if (c2000_aesOn) { + /* A soft reset clears KEY1, so the last key used does not linger in + * the peripheral after the device is unregistered. */ + AES_performSoftReset(WOLFSSL_C2000_AES_BASE); + c2000_aesOn = 0; + } + wc_CryptoCb_UnRegisterDevice(devId); + return 0; +} + +#endif /* WOLFSSL_C2000_AES && !NO_AES */ diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am index c5533eb5dc..49054a7b0b 100644 --- a/wolfssl/wolfcrypt/include.am +++ b/wolfssl/wolfcrypt/include.am @@ -99,6 +99,7 @@ noinst_HEADERS+= \ wolfssl/wolfcrypt/port/pic32/pic32mz-crypt.h \ wolfssl/wolfcrypt/port/ti/ti-hash.h \ wolfssl/wolfcrypt/port/ti/ti-ccm.h \ + wolfssl/wolfcrypt/port/ti/ti-c2000.h \ wolfssl/wolfcrypt/port/nrf51.h \ wolfssl/wolfcrypt/port/nxp/ksdk_port.h \ wolfssl/wolfcrypt/port/nxp/dcp_port.h \ diff --git a/wolfssl/wolfcrypt/port/ti/ti-c2000.h b/wolfssl/wolfcrypt/port/ti/ti-c2000.h new file mode 100644 index 0000000000..9bf44ea950 --- /dev/null +++ b/wolfssl/wolfcrypt/port/ti/ti-c2000.h @@ -0,0 +1,97 @@ +/* ti-c2000.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 + */ + +/* TI C2000 (C28x) on-chip crypto support. + * + * The F28P55x/F28P65x carry an "AESA" accelerator (a TI EIP-120t instance) + * offering ECB/CBC/CTR/CFB/GCM/CCM with 128/192/256-bit keys. wolfCrypt + * reaches it through the crypto-callback framework rather than by replacing + * wolfcrypt/src/aes.c, so software AES stays available: a given Aes context + * opts in by passing WOLFSSL_C2000_DEVID to wc_AesInit(), and a context + * initialised with INVALID_DEVID runs pure software. Anything the hardware + * cannot do returns CRYPTOCB_UNAVAILABLE and falls through to software. + * + * This is a different device from the TivaWare/TM4C block behind + * WOLFSSL_TI_CRYPT (wolfcrypt/src/port/ti/ti-aes.c); the two are not + * interchangeable and must not both be enabled. + */ + +#ifndef WOLF_CRYPT_PORT_TI_C2000_H +#define WOLF_CRYPT_PORT_TI_C2000_H + +#include + +#ifdef WOLFSSL_C2000_AES + +#if defined(WOLFSSL_TI_CRYPT) + #error "WOLFSSL_C2000_AES and WOLFSSL_TI_CRYPT are different devices" +#endif + +/* The AESA block is a single shared resource with no per-context state (key, + * IV and mode are reloaded on every operation), so the port is re-entrant + * across Aes contexts but not across preemption or an ISR. Define + * WOLFSSL_C2000_AES_NO_LOCK to assert that an external lock provides that + * guarantee. */ +#if !defined(SINGLE_THREADED) && !defined(WOLFSSL_C2000_AES_NO_LOCK) + #error "WOLFSSL_C2000_AES needs SINGLE_THREADED or WOLFSSL_C2000_AES_NO_LOCK" +#endif + +/* devId handed to wc_AesInit() and wc_CryptoCb_RegisterDevice(). */ +#ifndef WOLFSSL_C2000_DEVID + #define WOLFSSL_C2000_DEVID 0x2000 +#endif + +/* AESA_BASE / AESA_SS_BASE from C2000Ware inc/hw_memmap.h. Defaulted here so + * the port does not depend on which device header happens to be on the + * include path. */ +#ifndef WOLFSSL_C2000_AES_BASE + #define WOLFSSL_C2000_AES_BASE 0x00042000U +#endif +#ifndef WOLFSSL_C2000_AES_SS_BASE + #define WOLFSSL_C2000_AES_SS_BASE 0x00042C00U +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +struct wc_CryptoInfo; + +/* Enable and reset the AESA block, then register the callback for devId. + * Must be called after wolfCrypt_Init(): wc_CryptoCb_RegisterDevice() looks + * for a slot whose devId is INVALID_DEVID, and the device table is only + * initialised to that value by wolfCrypt_Init(). */ +WOLFSSL_API int wc_C2000_Init(int devId); + +/* Unregister the callback. */ +WOLFSSL_API int wc_C2000_Cleanup(int devId); + +/* The callback itself, exposed so an application can register it by hand. */ +WOLFSSL_API int wc_C2000_CryptoCb(int devId, struct wc_CryptoInfo* info, + void* ctx); + +#ifdef __cplusplus + } +#endif + +#endif /* WOLFSSL_C2000_AES */ + +#endif /* WOLF_CRYPT_PORT_TI_C2000_H */