Merge pull request #11202 from dgarske/c2000_hw_aes

TI C2000: hardware AES accelerator and oscillator-jitter entropy source
pull/11427/head
Sean Parkinson 2026-09-10 09:40:37 +10:00 committed by GitHub
commit 765d2165e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 2642 additions and 41 deletions

View File

@ -250,6 +250,8 @@ CTYPE_USER
CURVED448_SMALL
CUSTOM_ENTROPY_TIMEHIRES
CY_USING_HAL
DCC0_BASE
DCC1_BASE
DCP_USE_DCACHE
DEBUG_FORCE_VECTOR_REGISTER_ACCESS_FUZZING
DEBUG_VECTOR_REGISTER_ACCESS_ALWAYS_OFF
@ -778,6 +780,7 @@ WC_INIT_ERROR_WHEN_CONTENDED
WC_LINUXKM_NO_USE_HEAP_WRAPPERS
WC_MLDSA_NO_ASM
WC_MLKEM_KERNEL_ASM
WC_NOISE_SRC_MAX
WC_NO_ASYNC_SLEEP
WC_NO_RNG_SIMPLE
WC_NO_STATIC_ASSERT
@ -860,6 +863,27 @@ 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_C2000_ENTROPY
WOLFSSL_C2000_ENTROPY_APT_CUTOFF
WOLFSSL_C2000_ENTROPY_APT_WINDOW
WOLFSSL_C2000_ENTROPY_HMIN
WOLFSSL_C2000_ENTROPY_MARGIN
WOLFSSL_C2000_ENTROPY_NO_CLK_INIT
WOLFSSL_C2000_ENTROPY_NO_LOCK
WOLFSSL_C2000_ENTROPY_NUM_SRC
WOLFSSL_C2000_ENTROPY_RCT_CUTOFF
WOLFSSL_C2000_ENTROPY_REF_CLK
WOLFSSL_C2000_ENTROPY_SRC0_CLK
WOLFSSL_C2000_ENTROPY_SRC0_DCC
WOLFSSL_C2000_ENTROPY_SRC1_CLK
WOLFSSL_C2000_ENTROPY_SRC1_DCC
WOLFSSL_C2000_ENTROPY_STARTUP_OCTETS
WOLFSSL_C2000_ENTROPY_WINDOW
WOLFSSL_CAAM_BLACK_KEY_AESCCM
WOLFSSL_CAAM_BLACK_KEY_SM
WOLFSSL_CAAM_NO_BLACK_KEY
@ -967,6 +991,7 @@ WOLFSSL_MP_INVMOD_CONSTANT_TIME
WOLFSSL_MULTICIRCULATE_ALTNAMELIST
WOLFSSL_NETX_DUO
WOLFSSL_NEW_PRIME_CHECK
WOLFSSL_NOISE_SRC
WOLFSSL_NONBLOCK_OCSP
WOLFSSL_NOSHA3_384
WOLFSSL_NOT_WINDOWS_API

View File

@ -118,6 +118,274 @@ 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.39 MiB/s | 5.2x |
| AES-256-ECB encrypt | 377 KiB/s | 2.34 MiB/s | 6.3x |
| AES-128-CBC encrypt | 405 KiB/s | 2.37 MiB/s | 6.0x |
| AES-128-CBC decrypt | 388 KiB/s | 2.36 MiB/s | 6.2x |
| AES-256-CBC encrypt | 333 KiB/s | 2.32 MiB/s | 7.1x |
| AES-256-CBC decrypt | 322 KiB/s | 2.31 MiB/s | 7.3x |
| AES-128-CTR | 408 KiB/s | 1.45 MiB/s | 3.6x |
| AES-256-CTR | 335 KiB/s | 1.45 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.
## Entropy (no TRNG on this part)
The F28P55x has **no hardware TRNG** -- there is no RNG peripheral anywhere in
C2000Ware for this device, and the one "TRNG" string in `hw_asysctl.h`
(`ASYSCTL_PMMCONFIGDFT_VREFTRNG1P225`) is a voltage-reference *trim range*
field. What the part does have is three independent oscillators -- INTOSC1 and
INTOSC2 (on-chip ~10 MHz RC) and the external crystal that SYSCLK/PLLRAWCLK
derive from -- and two Dual-Clock Comparators that can count one against
another.
`wolfcrypt/src/port/ti/ti-c2000-entropy.c` (gate `WOLFSSL_C2000_ENTROPY`)
turns that into an entropy source: a DCC counts PLLRAWCLK edges inside a
window of INTOSC cycles, and the **LSB of that count** is one noise bit,
carrying the relative phase drift of two physically distinct oscillators. Raw
noise is oversampled far past its measured min-entropy, health-tested per
SP800-90B 4.4, conditioned with SHA-256, and handed to the SP800-90A
Hash-DRBG.
The work is split in two. The port file owns only the hardware -- DCC setup,
one measurement, and the noise bit. Everything above that is the generic
`wc_NoiseSrc_*` layer described below, which is not C2000-specific.
### Measured on hardware
Captured with the reference example's `ENTROPY_PROBE=1` build (262144 raw bits
per source, LSB extraction) and analyzed on a host with the example's
`tools/entropy_analyze.py`, so these numbers are reproducible rather than
asserted:
| Source | Hmin/bit | bias | max \|acf\| lag 1..64 | chi-square p |
|---|---|---|---|---|
| INTOSC1 window / PLL counted (DCC1) | **0.932** | -0.0000 | 0.005 | 0.623 |
| INTOSC2 window / PLL counted (DCC0) | 0.843 | -0.0027 | 0.005 | 0.000 |
| ADC LSB, floating input | 0.834 | -0.0086 | 0.073 | 0.000 |
Min-entropy is the SP800-90B 6.3.1 most-common-value estimate at the 99% upper
confidence bound, taken over the 8-bit octet alphabet and divided by 8. The
octet alphabet is used rather than the bit alphabet because it also catches
structure across adjacent bits: on a stream with strong lag-1 correlation but
no marginal bias, a per-bit estimate reports 0.99 and sees nothing while the
octet estimate collapses. **This is an MCV estimate plus bias and correlation
screening, not a full SP800-90B non-IID assessment** (`ea_non_iid` was not
run); MCV assumes IID, so it is an upper bound, and the low measured
correlation on the credited source is what makes it a reasonable one.
Read 0.932 against the estimator's ceiling, not against 1.0: at this sample
count a synthetic uniform stream estimates to 0.930 (`entropy_analyze.py
--selftest` prints it), so the credited source is statistically
indistinguishable from uniform here. These are single-run measurements of a
physical source and move a little between runs -- an earlier capture gave
0.924 / 0.775 / 0.865 for the three rows -- but the pass/fail conclusions have
been identical in every run.
Both DCC sources are gathered and hashed together, but **only INTOSC1 is
credited** with entropy: INTOSC2 estimates lower and fails a chi-square
uniformity check decisively (stat 1972 against 255 degrees of freedom), so it
is defence-in-depth that the budget does not rely on. Hashing extra input can
only add entropy, never remove it. The ADC source is not used by default -- it
also fails chi-square, and it depends on a spare analog pin being left
floating, which is a board property rather than a device one.
The port then assumes **0.5 bits per raw bit** (`WOLFSSL_C2000_ENTROPY_HMIN`)
and oversamples 2x on top of that (`WOLFSSL_C2000_ENTROPY_MARGIN`), i.e. 32
raw bits gathered per output octet -- roughly a 4x cushion over the credited
source's measured 0.924. At a 256-cycle window (~25.6 us per bit) a 32-octet
conditioning chunk costs about 26 ms per source.
### Build overrides
Everything is `#ifndef`-guarded in `wolfssl/wolfcrypt/port/ti/ti-c2000-entropy.h`, so a project can retune it from its own `user_settings.h`:
| Macro | Default | Purpose |
|---|---|---|
| `WOLFSSL_C2000_ENTROPY_NUM_SRC` | 2 | Set to 1 to use the credited source only and leave the second DCC free |
| `WOLFSSL_C2000_ENTROPY_SRC0_DCC` / `_SRC1_DCC` | `DCC1_BASE` / `DCC0_BASE` | Which DCC instance each source uses |
| `WOLFSSL_C2000_ENTROPY_SRC0_CLK` / `_SRC1_CLK` | `INTOSC1` / `INTOSC2` | Slow (window) clock per source |
| `WOLFSSL_C2000_ENTROPY_REF_CLK` | `DCC_COUNT1SRC_PLL` | Fast clock being counted; `SYSCLK` works at coarser quantization |
| `WOLFSSL_C2000_ENTROPY_NO_CLK_INIT` | off | Application manages the DCC peripheral clocks itself |
| `WOLFSSL_C2000_ENTROPY_WINDOW` | 256 | Slow-clock cycles per noise bit |
| `WOLFSSL_C2000_ENTROPY_HMIN` | 50 | Assumed min-entropy per raw bit, in 1/100 bits |
| `WOLFSSL_C2000_ENTROPY_MARGIN` | 2 | Oversample factor on top of `HMIN` |
| `WOLFSSL_C2000_ENTROPY_RCT_CUTOFF` | 9 | SP800-90B 4.4.1 cutoff |
| `WOLFSSL_C2000_ENTROPY_APT_WINDOW` / `_APT_CUTOFF` | 512 / 71 | SP800-90B 4.4.2 window and cutoff |
| `WOLFSSL_C2000_ENTROPY_STARTUP_OCTETS` | 1024 | SP800-90B 4.3 startup test size per source |
| `WOLFSSL_C2000_ENTROPY_NO_LOCK` | off | Assert an external lock instead of requiring `SINGLE_THREADED` |
The hardware-selection group is what to reach for if the board already uses a DCC for clock monitoring, or the part is not an F28P55x. If you change `HMIN`, recompute both health-test cutoffs for the new assumed entropy per octet -- a cutoff that does not match either never trips or trips constantly. A build-time check rejects a startup size smaller than one APT window.
### The generic `wc_NoiseSrc_*` layer
None of the SP800-90B machinery is C2000-specific, so it lives in
`wolfcrypt/src/random.c` behind `WOLFSSL_NOISE_SRC` (declarations in
`wolfssl/wolfcrypt/random.h`) and this port configures it. A port that has a
raw noise source but no TRNG supplies one callback:
```c
int my_sample(void* ctx, int srcIdx, byte* octet);
```
fills a `wc_NoiseSrc` with the callback, a domain-separation tag, a
caller-owned work buffer, the entropy budget (`hmin`, `margin`) and the
health-test cutoffs, and gets back:
| Call | Does |
|---|---|
| `wc_NoiseSrc_Init` | Validates the config, derives the gather size, runs the SP800-90B 4.3 startup test |
| `wc_NoiseSrc_GenerateSeed` | Gathers, health-tests, SHA-256 conditions, wipes the output on any failure |
| `wc_NoiseSrc_GetRaw` | Unconditioned noise for characterization only |
| `wc_NoiseSrc_SelfTest` | Liveness: gathers must differ and must not be a constant octet |
| `wc_NoiseSrc_Free` | Zeroes state and clears the latched failure |
`WC_NOISE_RAW_PER_SRC(hmin, margin)` sizes the work buffer from the entropy
budget so the port does not duplicate the formula. Source 0 is the credited
one; any further source is hashed in as defence in depth and is not budgeted.
The layer takes no locks -- the instance is caller-owned state, so a port that
shares one across threads provides its own mutual exclusion.
`WOLFSSL_C2000_ENTROPY` turns `WOLFSSL_NOISE_SRC` on implicitly.
### Health tests
SP800-90B 4.4.1 Repetition Count and 4.4.2 Adaptive Proportion run
continuously over every octet drawn from each source, with a startup test over
a full gather before anything is released. Cutoffs are derived for 4 bits of
min-entropy per octet at alpha = 2^-30 and are overridable. A failure returns
`ENTROPY_RT_E` / `ENTROPY_APT_E` and **no seed material is produced** -- the
source fails closed rather than degrading silently, and the failure is latched
until `wc_NoiseSrc_Free()`, because a source that trips and then passes is
exactly what continuous testing exists to catch.
Latching applies to the **credited** source only. Source 0 carries the entire
entropy budget, so its failure denies output. Sources 1 and up are unaccounted
extra hash input, and the cutoffs are derived for source 0's assumed
min-entropy rather than theirs, so one of them tripping is not evidence the
seed is weak: it is dropped for the life of the instance and stops
contributing. Output stays fully seeded because the budget never counted it,
and a source the budget ignores cannot deny service. On this part that means a
failing INTOSC2 degrades the source to INTOSC1 alone -- exactly the
`WOLFSSL_C2000_ENTROPY_NUM_SRC 1` configuration -- instead of killing the RNG.
Because those paths only fire on genuinely broken hardware, `noisesrc_test()`
in `wolfcrypt/test/test.c` drives them with synthetic sources -- stuck, biased,
sampler-error, and periodic -- on the host:
```sh
./configure --enable-all CFLAGS="-DWOLFSSL_NOISE_SRC" && make check
```
Note `wolfentropy.c`'s `HAVE_ENTROPY_MEMUSE` is *not* a usable substitute
here: its noise is memory-access timing jitter, which presumes a cache, and
the C28x is in-order and cacheless with deterministic RAM timing. Its default
state array is also ~256 KW on this target against ~100 KW of RAM. Its health
tests are `static` and hardcoded to 1 bit of min-entropy per sample, so they
are not reusable at this source's cutoffs either.
## Enabling on your build
Define a user-settings header (see `IDE/C2000/user_settings.h` for a

View File

@ -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"
@ -40,7 +43,7 @@ CFLAGS="-v28 --abi=eabi --float_support=fpu32 --tmu_support=tmu1 -O2 \
# (wc_MlDsaKey_VerifyCtxHash) calls for the digest size and OID.
SRCS="error wc_port memory logging misc coding hash \
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"
@ -58,6 +61,51 @@ for s in $SRCS; do
fi
done
# random.c again with the entropy gate on: that turns WOLFSSL_NOISE_SRC on and
# pulls in the generic SP800-90B noise-source layer, which is otherwise left
# out of the sweep above. Needs no C2000Ware - only the port .c touches
# driverlib.
printf 'CC random.c (WOLFSSL_C2000_ENTROPY) ... '
if "$CL" $CFLAGS $INCS --define=WOLFSSL_C2000_ENTROPY \
--compile_only --skip_assembler \
--asm_directory="$OUT" --obj_directory="$OUT" \
"$WOLFROOT/wolfcrypt/src/random.c" > "$OUT/random-noise.log" 2>&1; then
echo "ok"
else
echo "FAIL"
cat "$OUT/random-noise.log"
rc=1
fi
# 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"
DRVINCS="-I$DRV \
-I$C2000WARE/device_support/f28p55x/common/include \
-I$C2000WARE/device_support/f28p55x/headers/include"
for p in "ti-c2000-aes:--define=WOLF_CRYPTO_CB --define=WOLFSSL_C2000_AES" \
"ti-c2000-entropy:--define=WOLFSSL_C2000_ENTROPY"; do
f=${p%%:*}
d=${p#*:}
printf 'CC port/ti/%s.c ... ' "$f"
if "$CL" $CFLAGS $INCS $DRVINCS $d \
--compile_only --skip_assembler \
--asm_directory="$OUT" --obj_directory="$OUT" \
"$WOLFROOT/wolfcrypt/src/port/ti/$f.c" \
> "$OUT/$f.log" 2>&1; then
echo "ok"
else
echo "FAIL"
cat "$OUT/$f.log"
rc=1
fi
done
else
echo "SKIP port/ti/ti-c2000-*.c (set C2000WARE to include them)"
fi
if [ "$rc" -eq 0 ]; then
echo "TI C2000 compile-only guard: PASS"
else

View File

@ -26,6 +26,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
@ -56,6 +63,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

View File

@ -7998,7 +7998,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;
}
}
@ -8355,7 +8358,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;
}
}
@ -8461,7 +8466,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;
}
}
@ -8485,19 +8492,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);
}
@ -10526,9 +10535,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];
@ -10587,9 +10596,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]; \
@ -10636,9 +10645,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]; \
@ -15490,20 +15499,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
@ -15542,7 +15553,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;
}
}
@ -17097,13 +17112,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);
}
}
@ -17483,7 +17500,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;
}
}
@ -17494,7 +17513,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;
}
}

View File

@ -81,6 +81,8 @@ 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/ti/ti-c2000-entropy.c \
wolfcrypt/src/port/pic32/pic32mz-crypt.c \
wolfcrypt/src/port/nrf51.c \
wolfcrypt/src/port/aria/aria-crypt.c \

View File

@ -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 */

View File

@ -0,0 +1,350 @@
/* port/ti/ti-c2000-entropy.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
*/
/* Oscillator-jitter entropy source for the TI C2000 (C28x). Model and build
* options: wolfssl/wolfcrypt/port/ti/ti-c2000-entropy.h. Characterization:
* IDE/C2000/README.md.
*
* This file owns only the hardware: DCC setup, one measurement, and the noise
* bit. The SP800-90B startup and continuous health tests, the entropy budget,
* the SHA-256 conditioner and the latched failure state are the generic
* wc_NoiseSrc_* layer in wolfcrypt/src/random.c, which this configures.
*
* Noise bit = LSB of a DCC measurement (PLL edges counted inside a window of
* INTOSC cycles), i.e. the relative phase drift of two independent
* oscillators. Both sources are hashed in, but only source 0 is credited with
* entropy: source 1 estimates lower and fails a chi-square uniformity check,
* so it is defence-in-depth only. Extra hash input can never subtract
* entropy, and because source 1 is unbudgeted the generic layer drops it
* rather than failing closed if its health tests trip - see the latch policy
* in wolfcrypt/src/random.c.
*
* Build options (all #ifndef-guarded in ti-c2000-entropy.h, documented with
* defaults in IDE/C2000/README.md):
* WOLFSSL_C2000_ENTROPY enable this source
* ..._NUM_SRC 1 to use source 0 only, freeing a DCC
* ..._SRC0_DCC / ..._SRC1_DCC DCC instance per source
* ..._SRC0_CLK / ..._SRC1_CLK slow (window) clock per source
* ..._REF_CLK fast clock counted (PLL or SYSCLK)
* ..._NO_CLK_INIT application owns the DCC clocks
* ..._WINDOW slow-clock cycles per noise bit
* ..._HMIN / ..._MARGIN assumed min-entropy and oversample
* ..._RCT_CUTOFF SP800-90B 4.4.1 cutoff
* ..._APT_WINDOW / ..._APT_CUTOFF SP800-90B 4.4.2 window and cutoff
* ..._STARTUP_OCTETS SP800-90B 4.3 startup size per source
* ..._NO_LOCK assert an external lock instead of
* requiring SINGLE_THREADED
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#ifdef WOLFSSL_C2000_ENTROPY
/* uint32_t is used directly below to match the driverlib API. Pull it in here
* rather than relying on dcc.h/sysctl.h to provide it, so the port does not
* depend on include order. */
#include <stdint.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/random.h>
#include "dcc.h"
#include "sysctl.h"
#include <wolfssl/wolfcrypt/port/ti/ti-c2000-entropy.h>
/* Counter1 seed; the window must not exhaust it. */
#define C2000_CNT1_SEED 0xFFFFFUL
#define C2000_NUM_SRC WOLFSSL_C2000_ENTROPY_NUM_SRC
/* Raw octets the generic layer gathers per source per chunk, from the entropy
* budget. Sizes the work buffer; wc_NoiseSrc_Init() rederives and checks it. */
#define C2000_RAW_PER_SRC \
WC_NOISE_RAW_PER_SRC(WOLFSSL_C2000_ENTROPY_HMIN, \
WOLFSSL_C2000_ENTROPY_MARGIN)
/* Domain separation from any other SHA-256 use in the system. */
static const char c2000_entropyTag[] = "wolfssl-c2000-osc-entropy-v1";
/* .bss, not stack: the C28x stack is 16 KW below 0x10000. */
static byte c2000_work[C2000_NUM_SRC * C2000_RAW_PER_SRC];
static wc_NoiseSrc c2000_src;
#ifndef WOLFSSL_C2000_ENTROPY_NO_CLK_INIT
static int c2000_clkOn = 0;
#endif
#ifndef WOLFSSL_C2000_ENTROPY_NO_CLK_INIT
/* Map a DCC base to its peripheral-clock enum, so the SRC*_DCC overrides
* decide which clocks are touched instead of hardcoding both instances. */
static void c2000_clkEnable(uint32_t base)
{
/* Explicit rather than "anything that is not DCC0 is DCC1":
* that would quietly accept a mis-set *_SRC{0,1}_DCC override
* and gate the wrong peripheral. */
if (base == DCC0_BASE) {
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_DCC0);
}
else if (base == DCC1_BASE) {
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_DCC1);
}
}
static void c2000_clkDisable(uint32_t base)
{
/* Explicit rather than "anything that is not DCC0 is DCC1":
* that would quietly accept a mis-set *_SRC{0,1}_DCC override
* and gate the wrong peripheral. */
if (base == DCC0_BASE) {
SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_DCC0);
}
else if (base == DCC1_BASE) {
SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_DCC1);
}
}
#endif
/* Per-source DCC setup, done once by wc_c2000_Entropy_Init(). Everything
* here is invariant across measurements; only the counter seeds and the
* done/error flags change per sample, so keeping this out of the sampling
* path saves most of its register writes (that path runs 8 times per octet,
* and STARTUP_OCTETS * 8 times before the source releases anything).
*
* The done/error signal enables belong here rather than in the sample:
* STATUS.DONE does not latch unless they are on. */
static void c2000_dccConfig(uint32_t base, DCC_Count0ClockSource src0)
{
DCC_disableModule(base);
DCC_setCounter0ClkSource(base, src0);
DCC_setCounter1ClkSource(base, WOLFSSL_C2000_ENTROPY_REF_CLK);
DCC_enableSingleShotMode(base, DCC_MODE_COUNTER_ZERO);
DCC_enableErrorSignal(base);
DCC_enableDoneSignal(base);
}
/* End of measurement, which is DONE *or* ERR.
*
* ERR is the ordinary outcome here, not a fault. The DCC exists to check one
* clock against another and raises ERR when counter0 expires while counter1 is
* still far from zero - which is every measurement in this port, because
* counter1 is deliberately seeded at maximum so it never reaches zero. We are
* not clock-monitoring; we are using the DCC as a counter and keeping one bit
* of its residue. TI's own DCC_measureClockFrequency() waits on exactly this
* condition and reads the counter either way.
*
* A genuinely dead counter is not this function's job to catch: it would
* produce a constant bit, which the SP800-90B repetition and adaptive
* proportion tests in the generic layer reject. */
static int c2000_dccComplete(uint32_t base)
{
return DCC_getSingleShotStatus(base) || DCC_getErrorStatus(base);
}
/* One DCC measurement, returning the noise bit. Driven at register level
* rather than through DCC_measureClockFrequency(), which uses float32_t - no
* FP in the RNG path.
*
* Only bit 0 of the residue is used, so the counter is read and masked
* directly. (Subtracting from the seed, as a frequency measurement would,
* cannot change that bit: the seed is odd, so it would only complement it.)
*
* A timeout returns WC_HW_E rather than a bit - folding one in as a zero would
* silently inject a deterministic bit. */
static int c2000_dccSample(uint32_t base, byte* outBit)
{
uint32_t guard;
uint32_t limit;
DCC_clearErrorFlag(base);
DCC_clearDoneFlag(base);
/* Seeds reload only while the module is disabled, and single-shot has to
* be re-armed for every measurement. */
DCC_disableModule(base);
DCC_setCounterSeeds(base, (uint32_t)WOLFSSL_C2000_ENTROPY_WINDOW,
DCC_VALIDSEED_MIN, C2000_CNT1_SEED);
DCC_enableModule(base);
limit = ((uint32_t)WOLFSSL_C2000_ENTROPY_WINDOW * 256UL) + 100000UL;
for (guard = 0; guard < limit; guard++) {
if (c2000_dccComplete(base)) {
*outBit = (byte)(DCC_getCounter1Value(base) & 1U);
return 0;
}
}
return WC_HW_E;
}
/* wc_NoiseSampleCb: one raw octet, one noise bit per measurement, 8 per octet.
*
* Bits accumulate by shifting right and inserting at bit 7, so after eight
* measurements bit k holds the k'th sample - the same little-endian packing the
* characterization in IDE/C2000/README.md was measured against, but with
* constant shifts rather than a variable one per bit. acc starts at zero and
* only ever receives bit 7, so nothing above bit 7 is set (worth stating: byte
* is a 16-bit cell here). */
static int c2000_sampleOctet(void* ctx, int srcIdx, byte* octet)
{
uint32_t base;
byte bit;
int b;
int ret;
word16 acc;
(void)ctx;
base = (srcIdx == 0) ? (uint32_t)WOLFSSL_C2000_ENTROPY_SRC0_DCC
: (uint32_t)WOLFSSL_C2000_ENTROPY_SRC1_DCC;
acc = 0;
for (b = 0; b < 8; b++) {
ret = c2000_dccSample(base, &bit);
if (ret != 0) {
return ret;
}
acc = (word16)((acc >> 1) | (word16)((word16)bit << 7));
}
*octet = WC_OCTET(acc);
return 0;
}
int wc_c2000_Entropy_Init(void)
{
if (c2000_src.inited) {
return 0;
}
#ifndef WOLFSSL_C2000_ENTROPY_NO_CLK_INIT
if (!c2000_clkOn) {
c2000_clkEnable(WOLFSSL_C2000_ENTROPY_SRC0_DCC);
#if C2000_NUM_SRC > 1
c2000_clkEnable(WOLFSSL_C2000_ENTROPY_SRC1_DCC);
#endif
SysCtl_delay(100);
c2000_clkOn = 1;
}
#endif
/* Everything invariant per source, applied once (see c2000_dccConfig). */
c2000_dccConfig(WOLFSSL_C2000_ENTROPY_SRC0_DCC,
WOLFSSL_C2000_ENTROPY_SRC0_CLK);
#if C2000_NUM_SRC > 1
c2000_dccConfig(WOLFSSL_C2000_ENTROPY_SRC1_DCC,
WOLFSSL_C2000_ENTROPY_SRC1_CLK);
#endif
/* Refilled on every attempt: _Free() zeroes the instance. */
c2000_src.sampleCb = c2000_sampleOctet;
c2000_src.ctx = NULL;
c2000_src.tag = c2000_entropyTag;
c2000_src.work = c2000_work;
c2000_src.workSz = (word32)sizeof(c2000_work);
c2000_src.startupOctets = (word32)WOLFSSL_C2000_ENTROPY_STARTUP_OCTETS;
c2000_src.numSrc = (byte)C2000_NUM_SRC;
c2000_src.hmin = (byte)WOLFSSL_C2000_ENTROPY_HMIN;
c2000_src.margin = (byte)WOLFSSL_C2000_ENTROPY_MARGIN;
c2000_src.rctCutoff = (word16)WOLFSSL_C2000_ENTROPY_RCT_CUTOFF;
c2000_src.aptWindow = (word16)WOLFSSL_C2000_ENTROPY_APT_WINDOW;
c2000_src.aptCutoff = (word16)WOLFSSL_C2000_ENTROPY_APT_CUTOFF;
return wc_NoiseSrc_Init(&c2000_src);
}
void wc_c2000_Entropy_Free(void)
{
#ifndef WOLFSSL_C2000_ENTROPY_NO_CLK_INIT
/* Teardown has to be idempotent: a second Free(), or a Free() with no
* Init(), would otherwise write DCC registers with the peripheral clock
* already gated off. Guard on c2000_clkOn rather than inited - a
* startup-test failure returns before inited is set, but the clocks are
* on by then, so inited would skip a teardown that is still owed. */
if (c2000_clkOn)
#endif
{
DCC_disableModule(WOLFSSL_C2000_ENTROPY_SRC0_DCC);
#if C2000_NUM_SRC > 1
DCC_disableModule(WOLFSSL_C2000_ENTROPY_SRC1_DCC);
#endif
#ifndef WOLFSSL_C2000_ENTROPY_NO_CLK_INIT
c2000_clkDisable(WOLFSSL_C2000_ENTROPY_SRC0_DCC);
#if C2000_NUM_SRC > 1
c2000_clkDisable(WOLFSSL_C2000_ENTROPY_SRC1_DCC);
#endif
c2000_clkOn = 0;
#endif
}
wc_NoiseSrc_Free(&c2000_src);
XMEMSET(&c2000_src, 0, sizeof(c2000_src));
}
int wc_c2000_Entropy_GetRaw(byte* out, word32 len, int srcIdx)
{
int ret;
ret = wc_c2000_Entropy_Init();
if (ret != 0) {
return ret;
}
return wc_NoiseSrc_GetRaw(&c2000_src, out, len, srcIdx);
}
int wc_c2000_GenerateSeed(byte* output, word32 sz)
{
int ret;
ret = wc_c2000_Entropy_Init();
if (ret != 0) {
return ret;
}
return wc_NoiseSrc_GenerateSeed(&c2000_src, output, sz);
}
int wc_c2000_Entropy_SelfTest(void)
{
int ret;
ret = wc_c2000_Entropy_Init();
if (ret != 0) {
return ret;
}
return wc_NoiseSrc_SelfTest(&c2000_src);
}
#endif /* WOLFSSL_C2000_ENTROPY */

View File

@ -194,6 +194,8 @@ This library contains implementation for the random number generator.
#include "wolfssl/wolfcrypt/port/xilinx/xil-versal-trng.h"
#elif defined(WOLFSSL_RPIPICO)
#include "wolfssl/wolfcrypt/port/rpi_pico/pico.h"
#elif defined(WOLFSSL_C2000_ENTROPY)
#include "wolfssl/wolfcrypt/port/ti/ti-c2000-entropy.h"
#elif defined(NO_DEV_RANDOM)
#elif defined(CUSTOM_RAND_GENERATE)
#elif defined(CUSTOM_RAND_GENERATE_BLOCK)
@ -1766,9 +1768,11 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz)
#endif
XMEMSET(byteCounts, 0, MAX_ENTROPY_BITS * sizeof(word16));
/* Initialize counts for first window */
/* Indices are WC_OCTET-masked: byteCounts has 256 entries, but a
* byte cell can exceed 255 where CHAR_BIT != 8, so an unmasked seed
* value would index out of bounds. */
for (i = 0; i < windowSize; i++) {
byteCounts[seed[i]]++;
byteCounts[WC_OCTET(seed[i])]++;
}
/* Check first window - scan all 256 counts */
@ -1779,15 +1783,16 @@ int wc_RNG_TestSeed(const byte* seed, word32 seedSz)
/* Slide window through remaining seed data */
while ((windowStart + windowSize) < seedSz) {
/* Remove byte leaving the window */
byteCounts[seed[windowStart]]--;
byteCounts[WC_OCTET(seed[windowStart])]--;
windowStart++;
/* Add byte entering the window */
newIdx = windowStart + windowSize - 1;
byteCounts[seed[newIdx]]++;
byteCounts[WC_OCTET(seed[newIdx])]++;
/* Accumulate failure flag for new byte's count */
aptFailed |= (byteCounts[seed[newIdx]] >= WC_RNG_SEED_APT_CUTOFF);
aptFailed |= (byteCounts[WC_OCTET(seed[newIdx])] >=
WC_RNG_SEED_APT_CUTOFF);
}
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_SMALL_STACK_CACHE)
@ -4039,6 +4044,475 @@ static int wc_GenerateRand_IntelRD(OS_Seed* os, byte* output, word32 sz)
#endif /* HAVE_INTEL_RDRAND || HAVE_INTEL_RDSEED || HAVE_AMD_RDSEED */
#ifdef WOLFSSL_NOISE_SRC
/* Generic SP800-90B noise source. Configuration struct and API:
* wolfssl/wolfcrypt/random.h.
*
* Entropy model. A port supplies raw, unconditioned octets through one
* callback. Each raw bit is credited hmin/100 bits of min-entropy, so a
* full-entropy output octet needs 8 / (hmin/100) raw bits, and margin
* oversamples on top of that; WC_NOISE_RAW_PER_SRC() turns the two into the
* raw octets gathered per conditioner chunk. Only source 0 is budgeted - any
* further source is hashed in as defence in depth, and extra hash input can
* never subtract entropy.
*
* Every gathered octet passes the 4.4.1 Repetition Count Test and the 4.4.2
* Adaptive Proportion Test before reaching the conditioner, and _Init() runs
* the 4.3 startup test over startupOctets per source first. A failure is
* latched: 4.3/4.4 want a persistent failure state, not a transparent retry,
* because a source that trips and then passes is exactly what continuous
* testing exists to catch. Only _Free() clears it.
*
* Only test verdicts latch - the health tests and the _SelfTest() liveness
* checks. An error from the sample callback propagates unlatched and stays
* retryable: it says the hardware did not answer, not that the noise is bad.
* Either way no output is produced.
*
* Latching is also limited to the credited source. Source 0 carries the whole
* budget, so its failure fails closed. Sources 1.. are unaccounted extra hash
* input, and the cutoffs are derived for source 0's assumed min-entropy rather
* than theirs, so one of them tripping is not evidence the seed is weak - it is
* dropped for the life of the instance (recorded in src->degraded) and stops
* contributing. Output stays fully seeded because the budget never counted
* it, and a source the budget ignores cannot deny service.
*
* Cutoffs belong to the caller and must match the assumed hmin, or they either
* never trip or trip constantly. For H bits of min-entropy per octet at
* alpha = 2^-30: RCT C = 1 + ceil(30/H), APT C = 1 + CRITBINOM(W, 2^-H,
* 1-alpha).
*
* No locking. The instance is caller-owned state, so a port sharing one
* across threads provides its own mutual exclusion. */
#ifdef NO_SHA256
#error "WOLFSSL_NOISE_SRC conditions with SHA-256; do not set NO_SHA256"
#endif
#if WC_NOISE_CHUNK_SZ != WC_SHA256_DIGEST_SIZE
#error "WC_NOISE_CHUNK_SZ must match WC_SHA256_DIGEST_SIZE"
#endif
/* len raw octets from one source. No health testing - callers that keep the
* data run NoiseSrc_HealthTest() over it. */
static int NoiseSrc_Gather(wc_NoiseSrc* src, byte* out, word32 len, int srcIdx)
{
word32 i;
int ret;
for (i = 0; i < len; i++) {
ret = src->sampleCb(src->ctx, srcIdx, &out[i]);
if (ret != 0) {
return ret;
}
/* The contract is raw octets, but where CHAR_BIT != 8 a callback can
* legally leave bits above 0xFF in the cell. Mask here so the
* conditioner sees exactly what the health tests scored - they mask
* already - and no high bits leak into the hash. */
out[i] = WC_OCTET(out[i]);
}
return 0;
}
/* SP800-90B 4.4.1 RCT and 4.4.2 APT over every octet drawn from a source.
* Returns at the offending sample so the rest of the buffer cannot scrub the
* state that detected it, and latches the failure. */
static int NoiseSrc_HealthTest(wc_NoiseSrc* src, const byte* buf, word32 len,
int srcIdx)
{
wc_NoiseHealth* st;
word32 i;
word16 s;
if (srcIdx < 0 || srcIdx >= (int)src->numSrc) {
return BAD_FUNC_ARG;
}
st = &src->health[srcIdx];
for (i = 0; i < len; i++) {
s = (word16)WC_OCTET(buf[i]);
if (!st->started) {
st->started = 1;
st->rctLast = s;
st->rctCount = 1;
st->aptRef = s;
st->aptCount = 1;
st->aptPos = 1;
continue;
}
if (s == st->rctLast) {
st->rctCount++;
if (st->rctCount >= src->rctCutoff) {
if (srcIdx == 0) {
src->failed = ENTROPY_RT_E;
}
return ENTROPY_RT_E;
}
}
else {
st->rctLast = s;
st->rctCount = 1;
}
if (st->aptPos >= src->aptWindow) {
st->aptRef = s;
st->aptCount = 1;
st->aptPos = 1;
}
else {
if (s == st->aptRef) {
st->aptCount++;
if (st->aptCount >= src->aptCutoff) {
if (srcIdx == 0) {
src->failed = ENTROPY_APT_E;
}
return ENTROPY_APT_E;
}
}
st->aptPos++;
}
}
return 0;
}
int wc_NoiseSrc_Init(wc_NoiseSrc* src)
{
word32 done;
word32 take;
word32 need;
int i;
int ret;
if (src == NULL) {
return BAD_FUNC_ARG;
}
if (src->failed != 0) {
return src->failed;
}
if (src->inited) {
return 0;
}
if (src->sampleCb == NULL || src->tag == NULL || src->work == NULL) {
return BAD_FUNC_ARG;
}
if (src->numSrc < 1 || src->numSrc > WC_NOISE_SRC_MAX) {
return BAD_FUNC_ARG;
}
if (src->hmin < 1 || src->hmin > 100 || src->margin < 1) {
return BAD_FUNC_ARG;
}
if (src->rctCutoff < 2 || src->aptCutoff < 2 || src->aptWindow < 2) {
return BAD_FUNC_ARG;
}
/* Below one APT window the startup pass never exercises that test. */
if (src->startupOctets < (word32)src->aptWindow) {
return BAD_FUNC_ARG;
}
src->rawPerSrc = WC_NOISE_RAW_PER_SRC(src->hmin, src->margin);
need = src->rawPerSrc * (word32)src->numSrc;
if (src->rawPerSrc == 0 || src->workSz < need) {
return BUFFER_E;
}
XMEMSET(src->health, 0, sizeof(src->health));
src->chunkCtr = 0;
src->degraded = 0;
/* SP800-90B 4.3: push startupOctets per source through the continuous
* tests - more than one APT window - before releasing anything. */
for (i = 0; i < (int)src->numSrc; i++) {
for (done = 0; done < src->startupOctets; done += take) {
take = src->startupOctets - done;
if (take > src->rawPerSrc) {
take = src->rawPerSrc;
}
/* Sampler errors and test verdicts are handled differently, so
* keep them apart: a callback error means the hardware did not
* answer and stays retryable for every source, while only a test
* verdict drops or latches. */
ret = NoiseSrc_Gather(src, src->work, take, i);
if (ret != 0) {
ForceZero(src->work, src->workSz);
return ret;
}
ret = NoiseSrc_HealthTest(src, src->work, take, i);
if (ret != 0) {
if (i == 0) {
ForceZero(src->work, src->workSz);
return ret; /* credited source: fail closed */
}
/* Uncredited: drop it and keep going - see the latch policy
* note at the top of this module. */
src->degraded |= (word16)(1U << i);
ret = 0;
break;
}
}
}
ForceZero(src->work, src->workSz);
src->inited = 1;
return 0;
}
void wc_NoiseSrc_Free(wc_NoiseSrc* src)
{
if (src == NULL) {
return;
}
if (src->work != NULL && src->workSz > 0) {
ForceZero(src->work, src->workSz);
}
XMEMSET(src->health, 0, sizeof(src->health));
src->chunkCtr = 0;
src->failed = 0;
src->degraded = 0;
src->inited = 0;
}
int wc_NoiseSrc_GetRaw(wc_NoiseSrc* src, byte* output, word32 len, int srcIdx)
{
int ret;
if (src == NULL || output == NULL) {
return BAD_FUNC_ARG;
}
/* Before _Init(): numSrc is caller configuration, so a bad index need not
* pay for the startup test or burn hardware entropy first. */
if (srcIdx < 0 || srcIdx >= (int)src->numSrc) {
return BAD_FUNC_ARG;
}
if (src->failed != 0) {
return src->failed;
}
ret = wc_NoiseSrc_Init(src);
if (ret != 0) {
return ret;
}
ret = NoiseSrc_Gather(src, output, len, srcIdx);
if (ret != 0) {
ForceZero(output, len);
}
return ret;
}
int wc_NoiseSrc_GenerateSeed(wc_NoiseSrc* src, byte* output, word32 sz)
{
#ifdef WOLFSSL_SMALL_STACK
wc_Sha256* sha = NULL;
#else
wc_Sha256 sha[1];
#endif
byte digest[WC_NOISE_CHUNK_SZ];
byte ctrBuf[4];
byte* raw;
byte* outStart;
word32 outLen;
word32 take;
word16 contributed;
int i;
int ret;
if (src == NULL || output == NULL) {
return BAD_FUNC_ARG;
}
if (sz == 0) {
return 0;
}
if (src->failed != 0) {
return src->failed;
}
/* Kept so a mid-way failure can wipe what already landed: no caller
* should ever see a partially-filled seed buffer. */
outStart = output;
outLen = sz;
XMEMSET(digest, 0, sizeof(digest));
ret = wc_NoiseSrc_Init(src);
if (ret != 0) {
return ret;
}
#ifdef WOLFSSL_SMALL_STACK
sha = (wc_Sha256*)XMALLOC(sizeof(wc_Sha256), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (sha == NULL) {
return MEMORY_E;
}
#endif
while (sz > 0) {
contributed = 0;
for (i = 0; i < (int)src->numSrc; i++) {
if ((src->degraded & (word16)(1U << i)) != 0) {
continue; /* uncredited source already dropped */
}
raw = src->work + ((word32)i * src->rawPerSrc);
/* A sampler error propagates for any source and is retryable;
* only a health-test verdict drops or latches. */
ret = NoiseSrc_Gather(src, raw, src->rawPerSrc, i);
if (ret != 0) {
goto out;
}
ret = NoiseSrc_HealthTest(src, raw, src->rawPerSrc, i);
if (ret != 0) {
if (i == 0) {
goto out; /* fail closed; HealthTest latched src->failed */
}
src->degraded |= (word16)(1U << i);
ret = 0;
continue;
}
contributed |= (word16)(1U << i);
}
ret = wc_InitSha256(sha);
if (ret != 0) {
ForceZero(sha, sizeof(*sha));
goto out;
}
src->chunkCtr++;
ctrBuf[0] = WC_OCTET(src->chunkCtr >> 24);
ctrBuf[1] = WC_OCTET(src->chunkCtr >> 16);
ctrBuf[2] = WC_OCTET(src->chunkCtr >> 8);
ctrBuf[3] = WC_OCTET(src->chunkCtr);
ret = wc_Sha256Update(sha, (const byte*)src->tag,
(word32)XSTRLEN(src->tag));
if (ret == 0) {
ret = wc_Sha256Update(sha, ctrBuf, (word32)sizeof(ctrBuf));
}
for (i = 0; (ret == 0) && (i < (int)src->numSrc); i++) {
if ((contributed & (word16)(1U << i)) == 0) {
continue; /* not gathered this chunk - never hash stale data */
}
raw = src->work + ((word32)i * src->rawPerSrc);
ret = wc_Sha256Update(sha, raw, src->rawPerSrc);
}
if (ret == 0) {
ret = wc_Sha256Final(sha, digest);
}
wc_Sha256Free(sha);
ForceZero(sha, sizeof(*sha));
if (ret != 0) {
goto out;
}
take = (sz < (word32)WC_NOISE_CHUNK_SZ) ? sz
: (word32)WC_NOISE_CHUNK_SZ;
XMEMCPY(output, digest, take);
output += take;
sz -= take;
}
out:
if (ret != 0) {
ForceZero(outStart, outLen);
}
ForceZero(digest, sizeof(digest));
ForceZero(src->work, src->workSz);
#ifdef WOLFSSL_SMALL_STACK
XFREE(sha, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return ret;
}
int wc_NoiseSrc_SelfTest(wc_NoiseSrc* src)
{
/* Tests RAW noise, not conditioned seeds: the hashed chunk counter makes
* two GenerateSeed() outputs differ even with every source dead. */
byte* a;
byte* b;
word32 len;
word32 i;
byte diff;
int s;
int ret;
if (src == NULL) {
return BAD_FUNC_ARG;
}
ret = wc_NoiseSrc_Init(src);
if (ret != 0) {
return ret;
}
/* Two gathers side by side in the work buffer. */
len = src->workSz / 2U;
if (len > 64U) {
len = 64U;
}
if (len == 0U) {
return BUFFER_E;
}
a = src->work;
b = src->work + len;
for (s = 0; s < (int)src->numSrc; s++) {
if ((src->degraded & (word16)(1U << s)) != 0) {
continue; /* already dropped - no point re-testing it */
}
ret = NoiseSrc_Gather(src, a, len, s);
if (ret == 0) {
ret = NoiseSrc_Gather(src, b, len, s);
}
if (ret != 0) {
break;
}
/* A source stuck at any constant - including one whose clock was
* never enabled - produces identical gathers. */
/* ConstantCompare, not XMEMCMP: raw pre-conditioning samples. */
if (ConstantCompare(a, b, (int)len) == 0) {
ret = ENTROPY_RT_E;
if (s == 0) {
src->failed = ret;
break;
}
src->degraded |= (word16)(1U << s);
ret = 0;
continue;
}
/* Constant-octet check, accumulated rather than early-exit. */
diff = 0;
for (i = 1; i < len; i++) {
diff |= (byte)(a[i] ^ a[0]);
}
if (diff == 0) {
ret = ENTROPY_RT_E;
if (s == 0) {
src->failed = ret;
break;
}
src->degraded |= (word16)(1U << s);
ret = 0;
continue;
}
}
/* A gather error above is deliberately not latched - see the policy note
* at the top of this module. */
ForceZero(src->work, src->workSz);
return ret;
}
#endif /* WOLFSSL_NOISE_SRC */
/* Begin wc_GenerateSeed Implementations */
#if defined(CUSTOM_RAND_GENERATE_SEED)
@ -5806,6 +6280,38 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
return 0;
}
#elif defined(WOLFSSL_C2000_ENTROPY)
/* TI C2000 (C28x) oscillator-jitter entropy source. The part has no
* TRNG; the noise bit is the LSB of a Dual-Clock Comparator measurement,
* oversampled past its measured min-entropy, health-tested per SP800-90B
* 4.4 and SHA-256 conditioned before feeding the Hash-DRBG.
*
* Build: define WOLFSSL_C2000_ENTROPY and add
* wolfcrypt/src/port/ti/ti-c2000-entropy.c with the C2000Ware driverlib
* headers on the include path. Tuning macros, hardware overrides and the
* characterization: wolfssl/wolfcrypt/port/ti/ti-c2000-entropy.h and
* IDE/C2000/README.md.
*
* Blocking by design: ~26 ms per 32-octet chunk per source at the default
* window, ~100 ms for a typical seed, ~420 ms for the one-time startup
* test. Fine at boot, not for a control loop. */
#if !defined(HAVE_HASHDRBG)
#error "WOLFSSL_C2000_ENTROPY expects HAVE_HASHDRBG to expand the seed"
#endif
#include <wolfssl/wolfcrypt/port/ti/ti-c2000-entropy.h>
int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
{
(void)os;
if (output == NULL) {
return BUFFER_E;
}
return wc_c2000_GenerateSeed(output, sz);
}
#elif defined(DOLPHIN_EMULATOR) || defined (WOLFSSL_NDS)
int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
@ -6197,8 +6703,10 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz)
{
word32 i;
/* WC_OCTET, not (byte): the cast does not truncate where
* CHAR_BIT != 8, so sz > 256 would emit values above 0xFF. */
for (i = 0; i < sz; i++ )
output[i] = (byte)i;
output[i] = WC_OCTET(i);
(void)os;

View File

@ -938,6 +938,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void);
#ifdef WC_RNG_BANK_SUPPORT
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void);
#endif
#ifdef WOLFSSL_NOISE_SRC
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t noisesrc_test(void);
#endif
#endif /* WC_NO_RNG */
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pwdbased_test(void);
#if defined(USE_CERT_BUFFERS_2048) && \
@ -2582,6 +2585,12 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\
else
TEST_PASS("RNGBANK test passed!\n");
#endif
#ifdef WOLFSSL_NOISE_SRC
if ((ret = noisesrc_test()) != 0)
TEST_FAIL("NOISESRC test failed!\n", ret);
else
TEST_PASS("NOISESRC test passed!\n");
#endif
#endif /* WC_NO_RNG */
#ifdef WOLFSSL_SHAKE128
@ -27405,6 +27414,430 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_test(void)
#endif /* !HAVE_HASHDRBG || CUSTOM_RAND_GENERATE_BLOCK || HAVE_INTEL_RDRAND */
#if defined(WOLFSSL_NOISE_SRC) && !defined(WC_NO_RNG)
/* Synthetic noise sources for the generic wc_NoiseSrc_* layer. A physical
* source only trips SP800-90B on genuinely broken hardware, so these drive the
* failure paths - RCT, APT, sampler error, the latch and the self-test - on
* the host instead. */
#define NOISE_TEST_GOOD 0
#define NOISE_TEST_STUCK 1
#define NOISE_TEST_BIASED 2
#define NOISE_TEST_HWFAIL 3
#define NOISE_TEST_PERIODIC 4
#define NOISE_TEST_SRC1_DEAD 5
#define NOISE_TEST_SRC1_HWFAIL 6
/* Healthy through the startup tests, then stuck. The failure therefore lands
* inside wc_NoiseSrc_GenerateSeed() rather than wc_NoiseSrc_Init(), which is
* the only way to reach the continuous-test branch, the seed wipe and the
* mid-flight drop of an uncredited source. */
#define NOISE_TEST_LATE_STUCK 7
#define NOISE_TEST_SRC1_LATE_STUCK 8
/* Health-test settings the synthetic sources are built against. */
#define NOISE_TEST_HMIN 50
#define NOISE_TEST_MARGIN 2
#define NOISE_TEST_RCT 9
#define NOISE_TEST_APT_W 512
#define NOISE_TEST_APT_C 71
#define NOISE_TEST_STARTUP 512
#define NOISE_TEST_PERIOD 64
typedef struct NoiseTestCtx {
word32 pos;
word32 drawn; /* samples served, so a source can turn bad after startup */
int mode;
} NoiseTestCtx;
static int noise_test_sample(void* ctx, int srcIdx, byte* octet)
{
NoiseTestCtx* c = (NoiseTestCtx*)ctx;
word32 p;
c->drawn++;
switch (c->mode) {
case NOISE_TEST_STUCK:
*octet = 0xA5;
break;
/* Runs of seven keep the RCT clear (cutoff 9) while one value takes
* 448 of every 512 slots, well past the APT cutoff. */
case NOISE_TEST_BIASED:
p = c->pos++;
*octet = ((p % 8U) == 7U) ? (byte)(p & 0xFFU) : (byte)0xAA;
break;
case NOISE_TEST_HWFAIL:
return WC_HW_E;
/* Repeats every NOISE_TEST_PERIOD octets: passes both health tests,
* but successive equal-length gathers come out identical. */
case NOISE_TEST_PERIODIC:
p = c->pos++;
*octet = (byte)(p % (word32)NOISE_TEST_PERIOD);
break;
/* Credited source healthy, uncredited source's sampler erroring: the
* error must propagate rather than silently dropping the source. */
case NOISE_TEST_SRC1_HWFAIL:
if (srcIdx != 0) {
return WC_HW_E;
}
c->pos = (c->pos * 1103515245U) + 12345U;
*octet = (byte)((c->pos >> 16) & 0xFFU);
break;
/* Credited source healthy, uncredited source stuck: the uncredited
* one must be dropped rather than denying service. */
case NOISE_TEST_SRC1_DEAD:
if (srcIdx != 0) {
*octet = 0x5A;
break;
}
c->pos = (c->pos * 1103515245U) + 12345U;
*octet = (byte)((c->pos >> 16) & 0xFFU);
break;
/* One source, so startup draws exactly startupOctets; anything after
* that is inside GenerateSeed. */
case NOISE_TEST_LATE_STUCK:
if (c->drawn > (word32)NOISE_TEST_STARTUP) {
*octet = 0x5A;
break;
}
c->pos = (c->pos * 1103515245U) + 12345U;
*octet = (byte)((c->pos >> 16) & 0xFFU);
break;
/* Two sources, so startup draws startupOctets twice. Only the
* uncredited source goes stuck: seeding must continue. */
case NOISE_TEST_SRC1_LATE_STUCK:
if ((srcIdx != 0) &&
(c->drawn > (word32)(2 * NOISE_TEST_STARTUP))) {
*octet = 0x5A;
break;
}
c->pos = (c->pos * 1103515245U) + 12345U;
*octet = (byte)(((c->pos >> 16) & 0xFFU) ^
(byte)((unsigned int)srcIdx * 0x5AU));
break;
case NOISE_TEST_GOOD:
default:
/* Cheap LCG - only has to be well distributed, not secure. The
* srcIdx term keeps two sources from producing the same stream. */
c->pos = (c->pos * 1103515245U) + 12345U;
*octet = (byte)(((c->pos >> 16) & 0xFFU) ^
(byte)((unsigned int)srcIdx * 0x5AU));
break;
}
return 0;
}
static void noise_test_cfg(wc_NoiseSrc* src, NoiseTestCtx* ctx, byte* work,
word32 workSz, int mode)
{
XMEMSET(src, 0, sizeof(*src));
XMEMSET(ctx, 0, sizeof(*ctx));
ctx->mode = mode;
src->sampleCb = noise_test_sample;
src->ctx = ctx;
src->tag = "wolfssl-noisesrc-test";
src->work = work;
src->workSz = workSz;
src->startupOctets = (word32)NOISE_TEST_STARTUP;
src->numSrc = 1;
src->hmin = (byte)NOISE_TEST_HMIN;
src->margin = (byte)NOISE_TEST_MARGIN;
src->rctCutoff = (word16)NOISE_TEST_RCT;
src->aptWindow = (word16)NOISE_TEST_APT_W;
src->aptCutoff = (word16)NOISE_TEST_APT_C;
}
/* Conditioned-output KATs for the deterministic NOISE_TEST_GOOD sampler.
*
* The chunk counter is hashed into every chunk, so "seed1 != seed2" and the
* chunk-inequality check below hold even if no noise were gathered at all -
* wc_NoiseSrc_SelfTest() says as much. Those checks therefore prove nothing
* about the conditioner. Pinning the actual output covers what they cannot:
* that noise really reaches the hash, that the contributed-mask selects the
* right sources, and that the per-source buffers are hashed in the right
* order. Values are octet streams, so a CHAR_BIT != 8 target must reproduce
* them exactly. */
static const byte noise_kat_1src[WC_NOISE_CHUNK_SZ] = {
0xc4, 0xea, 0x79, 0x58, 0x9b, 0xe4, 0x09, 0x34,
0xa2, 0x1a, 0x90, 0xb0, 0xd0, 0xac, 0x04, 0x83,
0xc0, 0xaf, 0x12, 0x08, 0x68, 0xcc, 0xf9, 0x2a,
0x8b, 0xef, 0x77, 0xce, 0xc6, 0xd5, 0xba, 0xaa,
};
#if WC_NOISE_SRC_MAX >= 2
static const byte noise_kat_2src[WC_NOISE_CHUNK_SZ * 3] = {
0xbf, 0x52, 0x8d, 0xfa, 0x85, 0x08, 0x11, 0xb7,
0x5b, 0xb6, 0xdd, 0x55, 0x88, 0x75, 0xb3, 0x70,
0xd4, 0x41, 0x5d, 0x36, 0xac, 0x33, 0x1b, 0xa2,
0x1c, 0x6a, 0x43, 0x37, 0x72, 0xa5, 0x6b, 0x0d,
0x0b, 0xd8, 0x7c, 0x1b, 0x6d, 0x99, 0xe4, 0x10,
0xc2, 0x2e, 0x1b, 0xf2, 0x37, 0x44, 0xca, 0x5e,
0x6b, 0xc0, 0x72, 0xdb, 0x4e, 0x75, 0xc9, 0x1f,
0x6a, 0x67, 0xf0, 0x4d, 0xd0, 0x64, 0x50, 0xcc,
0x6f, 0x43, 0xbf, 0xe9, 0xea, 0x38, 0x97, 0xee,
0xab, 0xa3, 0xe2, 0xfe, 0xe9, 0x8d, 0x11, 0xbe,
0x7d, 0x06, 0xba, 0xf9, 0xea, 0xce, 0xda, 0xb5,
0xc4, 0xd1, 0xcf, 0x57, 0x5c, 0x54, 0x44, 0xa4,
};
#endif
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t noisesrc_test(void)
{
wc_NoiseSrc src;
NoiseTestCtx ctx;
byte work[2 * WC_NOISE_RAW_PER_SRC(NOISE_TEST_HMIN, NOISE_TEST_MARGIN)];
byte seed1[WC_NOISE_CHUNK_SZ];
byte seed2[WC_NOISE_CHUNK_SZ];
/* Spans several conditioner chunks. Declared unconditionally: the
* post-startup fail-closed case below needs only one source. */
byte seedLong[WC_NOISE_CHUNK_SZ * 3];
wc_test_ret_t ret;
int i;
WOLFSSL_ENTER("noisesrc_test");
/* Well-distributed source: startup passes, seeds differ, self-test OK. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_GOOD);
ret = wc_NoiseSrc_Init(&src);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (src.rawPerSrc !=
WC_NOISE_RAW_PER_SRC(NOISE_TEST_HMIN, NOISE_TEST_MARGIN))
return WC_TEST_RET_ENC_NC;
ret = wc_NoiseSrc_GenerateSeed(&src, seed1, (word32)sizeof(seed1));
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
ret = wc_NoiseSrc_GenerateSeed(&src, seed2, (word32)sizeof(seed2));
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (XMEMCMP(seed1, seed2, sizeof(seed1)) == 0)
return WC_TEST_RET_ENC_NC;
if (XMEMCMP(seed1, noise_kat_1src, sizeof(noise_kat_1src)) != 0)
return WC_TEST_RET_ENC_NC;
ret = wc_NoiseSrc_SelfTest(&src);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
wc_NoiseSrc_Free(&src);
/* Two sources: exercises the per-source work-buffer slicing, per-source
* health state and the uncredited hash-in - the shipping C2000 config.
* The seed spans several conditioner chunks. */
/* A source that only goes bad AFTER the startup tests: the failure must be
* caught by the continuous tests inside wc_NoiseSrc_GenerateSeed(), which
* is the module's fail-closed guarantee. Assert the error is returned AND
* that the caller's buffer is wiped rather than left holding partial
* output. Needs one source only, so it must sit outside the two-source
* region below. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work),
NOISE_TEST_LATE_STUCK);
ret = wc_NoiseSrc_Init(&src);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
XMEMSET(seedLong, 0xA5, sizeof(seedLong));
ret = wc_NoiseSrc_GenerateSeed(&src, seedLong, (word32)sizeof(seedLong));
if (ret != WC_NO_ERR_TRACE(ENTROPY_RT_E))
return WC_TEST_RET_ENC_NC;
for (i = 0; i < (int)sizeof(seedLong); i++) {
if (seedLong[i] != 0)
return WC_TEST_RET_ENC_NC;
}
/* and the instance must stay failed */
if (wc_NoiseSrc_GenerateSeed(&src, seed1, (word32)sizeof(seed1)) !=
WC_NO_ERR_TRACE(ENTROPY_RT_E))
return WC_TEST_RET_ENC_NC;
wc_NoiseSrc_Free(&src);
#if WC_NOISE_SRC_MAX >= 2
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_GOOD);
src.numSrc = 2;
ret = wc_NoiseSrc_Init(&src);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
ret = wc_NoiseSrc_GenerateSeed(&src, seedLong, (word32)sizeof(seedLong));
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
/* Chunk boundaries must not repeat: the counter is hashed in. */
if (XMEMCMP(seedLong, seedLong + WC_NOISE_CHUNK_SZ,
WC_NOISE_CHUNK_SZ) == 0)
return WC_TEST_RET_ENC_NC;
if (XMEMCMP(seedLong, noise_kat_2src, sizeof(noise_kat_2src)) != 0)
return WC_TEST_RET_ENC_NC;
/* Raw access per source, and a rejected index. */
ret = wc_NoiseSrc_GetRaw(&src, seed1, (word32)sizeof(seed1), 0);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
ret = wc_NoiseSrc_GetRaw(&src, seed2, (word32)sizeof(seed2), 1);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (XMEMCMP(seed1, seed2, sizeof(seed1)) == 0)
return WC_TEST_RET_ENC_NC;
if (wc_NoiseSrc_GetRaw(&src, seed1, (word32)sizeof(seed1), 2) !=
WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_NC;
if (wc_NoiseSrc_GetRaw(&src, seed1, (word32)sizeof(seed1), -1) !=
WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_NC;
ret = wc_NoiseSrc_SelfTest(&src);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
wc_NoiseSrc_Free(&src);
/* An uncredited source that trips must not deny service: source 0 carries
* the whole entropy budget, so a dead source 1 is dropped and seeding
* continues. The reverse (source 0 dead) must still fail closed. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work),
NOISE_TEST_SRC1_DEAD);
src.numSrc = 2;
ret = wc_NoiseSrc_Init(&src);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (src.failed != 0)
return WC_TEST_RET_ENC_NC;
if ((src.degraded & 0x2) == 0) /* source 1 dropped */
return WC_TEST_RET_ENC_NC;
if ((src.degraded & 0x1) != 0) /* source 0 untouched */
return WC_TEST_RET_ENC_NC;
ret = wc_NoiseSrc_GenerateSeed(&src, seed1, (word32)sizeof(seed1));
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
ret = wc_NoiseSrc_GenerateSeed(&src, seed2, (word32)sizeof(seed2));
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (XMEMCMP(seed1, seed2, sizeof(seed1)) == 0)
return WC_TEST_RET_ENC_NC;
wc_NoiseSrc_Free(&src);
/* Same stuck pattern on the credited source still fails closed. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_STUCK);
src.numSrc = 2;
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(ENTROPY_RT_E))
return WC_TEST_RET_ENC_NC;
wc_NoiseSrc_Free(&src);
/* A sampler error is not a test verdict: it must propagate unlatched and
* stay retryable, never silently drop an uncredited source. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work),
NOISE_TEST_SRC1_HWFAIL);
src.numSrc = 2;
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(WC_HW_E))
return WC_TEST_RET_ENC_NC;
if (src.degraded != 0) /* not a verdict - nothing dropped */
return WC_TEST_RET_ENC_NC;
if (src.failed != 0) /* retryable - nothing latched */
return WC_TEST_RET_ENC_NC;
wc_NoiseSrc_Free(&src);
/* Only the uncredited source goes bad after startup: it must be dropped
* mid-flight and seeding must continue, not fail. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work),
NOISE_TEST_SRC1_LATE_STUCK);
src.numSrc = 2;
ret = wc_NoiseSrc_Init(&src);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
ret = wc_NoiseSrc_GenerateSeed(&src, seedLong, (word32)sizeof(seedLong));
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if ((src.degraded & 0x2) == 0) /* source 1 dropped */
return WC_TEST_RET_ENC_NC;
if (src.failed != 0) /* but the instance is still usable */
return WC_TEST_RET_ENC_NC;
wc_NoiseSrc_Free(&src);
#endif
/* Stuck source: RCT trips inside the startup test, and the failure is
* latched for every later call until _Free() clears it. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_STUCK);
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(ENTROPY_RT_E))
return WC_TEST_RET_ENC_NC;
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(ENTROPY_RT_E))
return WC_TEST_RET_ENC_NC;
if (wc_NoiseSrc_GenerateSeed(&src, seed1, (word32)sizeof(seed1)) !=
ENTROPY_RT_E)
return WC_TEST_RET_ENC_NC;
if (wc_NoiseSrc_SelfTest(&src) != WC_NO_ERR_TRACE(ENTROPY_RT_E))
return WC_TEST_RET_ENC_NC;
wc_NoiseSrc_Free(&src);
if (src.failed != 0)
return WC_TEST_RET_ENC_NC;
/* Biased source: RCT stays clear, APT catches it. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_BIASED);
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(ENTROPY_APT_E))
return WC_TEST_RET_ENC_NC;
wc_NoiseSrc_Free(&src);
/* A sampler failure propagates instead of becoming a deterministic bit. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_HWFAIL);
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(WC_HW_E))
return WC_TEST_RET_ENC_NC;
wc_NoiseSrc_Free(&src);
/* Periodic source: passes both health tests, so only the self-test's
* identical-gather check catches it. Its constant-octet arm is not
* reachable from here - a constant source trips the RCT first - and stays
* defence in depth. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work),
NOISE_TEST_PERIODIC);
ret = wc_NoiseSrc_Init(&src);
if (ret != 0)
return WC_TEST_RET_ENC_EC(ret);
if (wc_NoiseSrc_SelfTest(&src) != WC_NO_ERR_TRACE(ENTROPY_RT_E))
return WC_TEST_RET_ENC_NC;
wc_NoiseSrc_Free(&src);
/* Configuration validation. */
if (wc_NoiseSrc_Init(NULL) != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_NC;
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_GOOD);
src.sampleCb = NULL;
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_NC;
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_GOOD);
src.tag = NULL;
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_NC;
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_GOOD);
src.hmin = 0;
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_NC;
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_GOOD);
src.numSrc = 0;
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_NC;
/* A startup pass shorter than one APT window never exercises the APT. */
noise_test_cfg(&src, &ctx, work, (word32)sizeof(work), NOISE_TEST_GOOD);
src.startupOctets = 8;
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return WC_TEST_RET_ENC_NC;
/* Work buffer too small for the entropy budget. */
noise_test_cfg(&src, &ctx, work, 16, NOISE_TEST_GOOD);
if (wc_NoiseSrc_Init(&src) != WC_NO_ERR_TRACE(BUFFER_E))
return WC_TEST_RET_ENC_NC;
return 0;
}
#endif /* WOLFSSL_NOISE_SRC && !WC_NO_RNG */
#ifdef WC_RNG_BANK_SUPPORT
static char *rng_bank_affinity_lock_lock;

View File

@ -99,6 +99,8 @@ 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/ti/ti-c2000-entropy.h \
wolfssl/wolfcrypt/port/nrf51.h \
wolfssl/wolfcrypt/port/nxp/ksdk_port.h \
wolfssl/wolfcrypt/port/nxp/dcp_port.h \

View File

@ -0,0 +1,190 @@
/* ti-c2000-entropy.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
*/
/* Oscillator-jitter entropy source for the TI C2000 (C28x).
*
* The F28P55x has no TRNG, but it has three independent oscillators (INTOSC1,
* INTOSC2 - on-chip ~10 MHz RC - and the crystal behind SYSCLK/PLLRAWCLK) and
* two Dual-Clock Comparators that can count one against another. A DCC
* counts PLL edges inside a window of RC-clock cycles; the LSB of that count
* is the noise bit, carrying the relative phase drift of two physically
* distinct oscillators.
*
* The port supplies only that measurement. The SP800-90B startup and
* continuous health tests, the entropy budget, the SHA-256 conditioner and the
* latched failure state come from the generic wc_NoiseSrc_* layer declared in
* wolfssl/wolfcrypt/random.h, which the tuning macros below configure.
*
* Characterization behind the defaults is in IDE/C2000/README.md.
*/
#ifndef WOLF_CRYPT_PORT_TI_C2000_ENTROPY_H
#define WOLF_CRYPT_PORT_TI_C2000_ENTROPY_H
#include <wolfssl/wolfcrypt/types.h>
#include <wolfssl/wolfcrypt/random.h>
#ifdef WOLFSSL_C2000_ENTROPY
/* random.h turns this on for us; catch an include order that defeats it. */
#ifndef WOLFSSL_NOISE_SRC
#error "WOLFSSL_C2000_ENTROPY needs random.h included first"
#endif
/* Owns the DCCs and static buffers with no locking. */
#if !defined(SINGLE_THREADED) && !defined(WOLFSSL_C2000_ENTROPY_NO_LOCK)
#error "WOLFSSL_C2000_ENTROPY needs SINGLE_THREADED or WOLFSSL_C2000_ENTROPY_NO_LOCK"
#endif
/* ---- Hardware selection -------------------------------------------------
* Override these if the board needs a DCC for clock monitoring, targets a
* different C2000 part, or wants a different oscillator pairing. Values are
* C2000Ware driverlib enums, so set them to e.g. DCC_COUNT0SRC_XTAL.
*
* Set NUM_SRC to 1 to use source 0 only and leave DCC0 free. Source 0 is the
* credited one; source 1 is unaccounted defence-in-depth, so dropping it
* costs no budgeted entropy. */
#ifndef WOLFSSL_C2000_ENTROPY_NUM_SRC
#define WOLFSSL_C2000_ENTROPY_NUM_SRC 2
#endif
#if (WOLFSSL_C2000_ENTROPY_NUM_SRC < 1) || (WOLFSSL_C2000_ENTROPY_NUM_SRC > 2)
#error "WOLFSSL_C2000_ENTROPY_NUM_SRC must be 1 or 2"
#endif
#ifndef WOLFSSL_C2000_ENTROPY_SRC0_DCC
#define WOLFSSL_C2000_ENTROPY_SRC0_DCC DCC1_BASE
#endif
#ifndef WOLFSSL_C2000_ENTROPY_SRC0_CLK
#define WOLFSSL_C2000_ENTROPY_SRC0_CLK DCC_COUNT0SRC_INTOSC1
#endif
#ifndef WOLFSSL_C2000_ENTROPY_SRC1_DCC
#define WOLFSSL_C2000_ENTROPY_SRC1_DCC DCC0_BASE
#endif
#ifndef WOLFSSL_C2000_ENTROPY_SRC1_CLK
#define WOLFSSL_C2000_ENTROPY_SRC1_CLK DCC_COUNT0SRC_INTOSC2
#endif
/* Two sources sharing one DCC instance would contend for the peripheral and
* defeat the point of gathering from two of them. Only checkable once the
* driverlib base addresses are known: random.c includes this header without
* C2000Ware (see IDE/C2000/compile.sh), where both names would preprocess to
* 0 and compare equal. */
#if (WOLFSSL_C2000_ENTROPY_NUM_SRC > 1) && \
defined(DCC0_BASE) && defined(DCC1_BASE) && \
(WOLFSSL_C2000_ENTROPY_SRC0_DCC == WOLFSSL_C2000_ENTROPY_SRC1_DCC)
#error "WOLFSSL_C2000_ENTROPY_SRC0_DCC and _SRC1_DCC must be different \
DCC instances when WOLFSSL_C2000_ENTROPY_NUM_SRC > 1"
#endif
/* The fast clock both sources count. SYSCLK works if PLLRAWCLK is unusable,
* at coarser quantization. */
#ifndef WOLFSSL_C2000_ENTROPY_REF_CLK
#define WOLFSSL_C2000_ENTROPY_REF_CLK DCC_COUNT1SRC_PLL
#endif
/* Define if the application already manages the DCC peripheral clocks; the
* port then neither enables nor disables them. */
/* #define WOLFSSL_C2000_ENTROPY_NO_CLK_INIT */
/* ---- Sampling -----------------------------------------------------------
* Window is in slow-clock cycles per noise bit. Counter1 seeds at 0xFFFFF
* and counts down, so the window must stay well under 2^20 PLL cycles; 256
* (~25.6 us) measured as well as any larger window and is the fastest. */
#ifndef WOLFSSL_C2000_ENTROPY_WINDOW
#define WOLFSSL_C2000_ENTROPY_WINDOW 256U
#endif
/* ---- Entropy budget -----------------------------------------------------
* HMIN is assumed min-entropy per raw bit in 1/100 bits; MARGIN oversamples
* on top. Measured ~0.92 bits/bit for the credited source, so the 0.5
* default plus 2x is roughly a 4x cushion. Raising HMIN gathers less. */
#ifndef WOLFSSL_C2000_ENTROPY_HMIN
#define WOLFSSL_C2000_ENTROPY_HMIN 50
#endif
#ifndef WOLFSSL_C2000_ENTROPY_MARGIN
#define WOLFSSL_C2000_ENTROPY_MARGIN 2
#endif
#if (WOLFSSL_C2000_ENTROPY_MARGIN) < 1
#error "WOLFSSL_C2000_ENTROPY_MARGIN must be >= 1"
#endif
#if (WOLFSSL_C2000_ENTROPY_HMIN) < 1 || (WOLFSSL_C2000_ENTROPY_HMIN) > 100
#error "WOLFSSL_C2000_ENTROPY_HMIN is 1/100 bits per raw bit: use 1..100"
#endif
/* ---- SP800-90B 4.4 health tests -----------------------------------------
* Cutoffs assume 4 bits of min-entropy per octet (8 raw bits x HMIN 0.5) at
* alpha = 2^-30, from the exact binomial:
* RCT 4.4.1: C = 1 + ceil(30/H) = 9
* APT 4.4.2: C = 1 + CRITBINOM(W, 2^-H, 1-alpha) = 71 at W = 512
* Recompute both if HMIN changes: a cutoff that does not match the assumed H
* either never trips or trips constantly. */
#ifndef WOLFSSL_C2000_ENTROPY_RCT_CUTOFF
#define WOLFSSL_C2000_ENTROPY_RCT_CUTOFF 9
#endif
#ifndef WOLFSSL_C2000_ENTROPY_APT_WINDOW
#define WOLFSSL_C2000_ENTROPY_APT_WINDOW 512
#endif
#ifndef WOLFSSL_C2000_ENTROPY_APT_CUTOFF
#define WOLFSSL_C2000_ENTROPY_APT_CUTOFF 71
#endif
/* SP800-90B 4.3 startup test, octets per source. Must exceed one APT window
* or the startup pass never exercises that test. */
#ifndef WOLFSSL_C2000_ENTROPY_STARTUP_OCTETS
#define WOLFSSL_C2000_ENTROPY_STARTUP_OCTETS 1024
#endif
#if WOLFSSL_C2000_ENTROPY_STARTUP_OCTETS < WOLFSSL_C2000_ENTROPY_APT_WINDOW
#error "WOLFSSL_C2000_ENTROPY_STARTUP_OCTETS must be >= APT window"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* These wrap a wc_NoiseSrc instance configured from the macros above; the
* generic wc_NoiseSrc_* API in random.h is usable directly too. */
/* Enable the DCCs and run the startup health test. Called automatically on
* first use. Claims the configured DCCs for the life of the source; costs
* ~420 ms at the defaults. */
WOLFSSL_API int wc_c2000_Entropy_Init(void);
/* Release the DCCs and clear any latched failure. */
WOLFSSL_API void wc_c2000_Entropy_Free(void);
/* Unconditioned noise, for characterization and self-test only - runs no
* health tests, so never use it for keying material. srcIdx 0 or 1. */
WOLFSSL_API int wc_c2000_Entropy_GetRaw(byte* out, word32 len, int srcIdx);
/* Conditioned seed material, called by the wc_GenerateSeed() branch in
* random.c. After a health-test failure this returns the latched error on
* every later call until wc_c2000_Entropy_Free(). */
WOLFSSL_API int wc_c2000_GenerateSeed(byte* output, word32 sz);
/* Liveness check on the raw noise: each source must produce differing
* gathers and never a constant octet. */
WOLFSSL_API int wc_c2000_Entropy_SelfTest(void);
#ifdef __cplusplus
}
#endif
#endif /* WOLFSSL_C2000_ENTROPY */
#endif /* WOLF_CRYPT_PORT_TI_C2000_ENTROPY_H */

View File

@ -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 */

View File

@ -438,6 +438,104 @@ struct WC_RNG {
WOLFSSL_API int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz);
/* Ports layered on the generic noise source turn it on implicitly, so a
* user_settings.h only has to name the port. */
#if defined(WOLFSSL_C2000_ENTROPY) && !defined(WOLFSSL_NOISE_SRC)
#define WOLFSSL_NOISE_SRC
#endif
#ifdef WOLFSSL_NOISE_SRC
/* Generic SP800-90B noise source, for parts with a raw physical noise source
* but no TRNG. The port supplies one callback returning an unconditioned
* octet; this layer adds the 4.3 startup test, the 4.4.1 RCT and 4.4.2 APT
* continuous tests, a latched fail-closed state, entropy-budget oversampling
* and a SHA-256 conditioner suitable for feeding wc_GenerateSeed().
* Implementation and entropy model: wolfcrypt/src/random.c. Worked example:
* wolfcrypt/src/port/ti/ti-c2000-entropy.c. */
/* Noise sources one instance can combine. */
#ifndef WC_NOISE_SRC_MAX
#define WC_NOISE_SRC_MAX 2
#endif
#if (WC_NOISE_SRC_MAX) < 1 || (WC_NOISE_SRC_MAX) > 16
/* wc_NoiseSrc.degraded is a word16 bitmask of dropped sources. */
#error "WC_NOISE_SRC_MAX must be 1..16"
#endif
/* Conditioner output per chunk in octets (SHA-256). random.c asserts this
* against WC_SHA256_DIGEST_SIZE so the two cannot drift; kept as a literal
* here to avoid pulling sha256.h into random.h. */
#define WC_NOISE_CHUNK_SZ 32
/* Raw octets drawn per source per chunk, from the assumed min-entropy hmin
* (hundredths of a bit per raw bit) and the oversample factor margin, rounded
* up so an hmin that does not divide evenly never under-gathers. Sizes the
* gather of the credited source (index 0); any further source is extra hash
* input and is not budgeted. Exposed so a port can size its work buffer
* without duplicating the formula. */
#define WC_NOISE_RAW_PER_SRC(hmin, margin) \
((word32)((WC_NOISE_CHUNK_SZ * \
(((8UL * 100UL * (unsigned long)(margin)) + \
((unsigned long)(hmin) - 1UL)) / (unsigned long)(hmin))) / 8U))
/* Fill *octet with one raw, unconditioned noise octet from source srcIdx.
* Returns 0 on success, negative on hardware failure. */
typedef int (*wc_NoiseSampleCb)(void* ctx, int srcIdx, byte* octet);
/* SP800-90B 4.4 state, one per source. Persists across calls by design: the
* tests are continuous, not per-request. */
typedef struct wc_NoiseHealth {
word16 rctCount;
word16 rctLast;
word16 aptCount;
word16 aptRef;
word16 aptPos;
byte started;
} wc_NoiseHealth;
/* Caller-owned instance. Fill the configuration members, then call
* wc_NoiseSrc_Init(); it derives rawPerSrc and owns everything after it. */
typedef struct wc_NoiseSrc {
wc_NoiseSampleCb sampleCb; /* required */
void* ctx; /* opaque, passed to sampleCb */
const char* tag; /* domain separation string, required */
byte* work; /* caller owned, >= numSrc * rawPerSrc */
word32 workSz;
word32 startupOctets; /* SP800-90B 4.3, per source */
word32 chunkCtr; /* hashed in so chunks cannot repeat */
word32 rawPerSrc; /* derived by wc_NoiseSrc_Init */
wc_NoiseHealth health[WC_NOISE_SRC_MAX];
int failed; /* latched, cleared only by _Free */
word16 rctCutoff; /* SP800-90B 4.4.1 */
word16 aptWindow; /* SP800-90B 4.4.2 */
word16 aptCutoff;
word16 degraded; /* bitmask of uncredited sources dropped */
byte numSrc; /* 1 .. WC_NOISE_SRC_MAX */
byte hmin; /* 1..100, hundredths of a bit per raw bit */
byte margin; /* oversample factor, >= 1 */
byte inited;
} wc_NoiseSrc;
WOLFSSL_API int wc_NoiseSrc_Init(wc_NoiseSrc* src);
WOLFSSL_API void wc_NoiseSrc_Free(wc_NoiseSrc* src);
WOLFSSL_API int wc_NoiseSrc_GenerateSeed(wc_NoiseSrc* src, byte* output,
word32 sz);
/* Raw, unconditioned octets for characterization and self-test only: these run
* no health tests, so the output is not fit for keying material.
*
* They also do not advance the continuous-test state, which has a consequence
* worth knowing: SP800-90B 4.4 scores a repetition run across consecutive
* samples, and the octets drawn here are invisible to that accounting. A run
* that straddles one of these calls is therefore under-counted. Draw
* characterization data before seeding starts, or accept that a stuck source
* spanning the call may take longer to be caught. */
WOLFSSL_API int wc_NoiseSrc_GetRaw(wc_NoiseSrc* src, byte* output, word32 len,
int srcIdx);
WOLFSSL_API int wc_NoiseSrc_SelfTest(wc_NoiseSrc* src);
#endif /* WOLFSSL_NOISE_SRC */
#ifdef HAVE_WNR
/* Whitewood netRandom client library */