mirror of https://github.com/wolfSSL/wolfssl.git
Add TI C2000 AESA hardware AES port and fix CHAR_BIT!=8 AES defects
parent
01426d0fdb
commit
a5085bc13d
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <wolfssl/wolfcrypt/settings.h>
|
||||
|
||||
#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 <stdint.h>
|
||||
|
||||
#include <wolfssl/wolfcrypt/aes.h>
|
||||
#include <wolfssl/wolfcrypt/cryptocb.h>
|
||||
#include <wolfssl/wolfcrypt/error-crypt.h>
|
||||
#include <wolfssl/wolfcrypt/port/ti/ti-c2000.h>
|
||||
|
||||
#ifdef NO_INLINE
|
||||
#include <wolfssl/wolfcrypt/misc.h>
|
||||
#else
|
||||
#define WOLFSSL_MISC_INCLUDED
|
||||
#include <wolfcrypt/src/misc.c>
|
||||
#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 */
|
||||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 <wolfssl/wolfcrypt/types.h>
|
||||
|
||||
#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 */
|
||||
Loading…
Reference in New Issue