mirror of https://github.com/wolfSSL/wolfssl.git
Merge pull request #11202 from dgarske/c2000_hw_aes
TI C2000: hardware AES accelerator and oscillator-jitter entropy sourcepull/11427/head
commit
765d2165e3
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue