wolfCrypt on TI C2000 C28x (LAUNCHXL-F28P55X)#10724
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds and CI-guards a bare-metal wolfCrypt port for TI C2000 C28x targets where CHAR_BIT == 16, introducing gated fixes so hashing, DRBG, ML-DSA verify, and SP-math ECC work correctly when a C “byte” is wider than 8 bits.
Changes:
- Introduces
WOLFSSL_NO_OCTET_BYTEdetection and uses octet-wise load/store paths to avoid invalid byte/word aliasing onCHAR_BIT != 8targets (SHA-256/512 family, SHA-3/SHAKE, Base64 CT decode, DRBG helpers, rotate helpers). - Adds “smallest memory” ML-DSA verify mode that streams
zper polynomial to reduce pinned RAM inwc_MlDsaKey. - Adds TI C2000 compile-only guard scripts plus a GitHub Actions workflow that downloads the TI CGT and compiles a scoped subset.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| wolfssl/wolfcrypt/wc_port.h | Makes atomic arg type selection robust for 16-bit int by also checking UINT_MAX. |
| wolfssl/wolfcrypt/wc_mldsa.h | Adds WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM struct layout variant for reduced verify RAM. |
| wolfssl/wolfcrypt/types.h | Adds WOLFSSL_NO_OCTET_BYTE auto-detection; adjusts WC_16BIT_CPU 64-bit availability behavior. |
| wolfssl/wolfcrypt/sp_int.h | Adds support for unsigned char being 16-bit (no native 8-bit type). |
| wolfssl/wolfcrypt/settings.h | Requires explicit opt-in for SP math on 16-bit-int CPUs via WOLFSSL_SP_ALLOW_16BIT_CPU. |
| wolfssl/wolfcrypt/dilithium.h | Adds smallest-mem verify gating and defaults slow Montgomery reduction macros on WC_16BIT_CPU. |
| wolfcrypt/test/test.c | Switches large-digest constants from C strings to byte[] to avoid CHAR_BIT!=8 pitfalls. |
| wolfcrypt/src/wc_port.c | Fixes init-state static assert to use CHAR_BIT instead of hardcoded 8. |
| wolfcrypt/src/wc_mldsa.c | Adds octet-masking for packed bytes and fixes integer-promotion/sign issues on 16-bit int; adds streaming z verify path. |
| wolfcrypt/src/sha512.c | Adds octet-wise word load/store and corrects length carry/length placement for CHAR_BIT!=8. |
| wolfcrypt/src/sha3.c | Forces bytewise Keccak absorb/squeeze for WOLFSSL_NO_OCTET_BYTE and adds squeeze helper. |
| wolfcrypt/src/sha256.c | Adds octet-wise word load/store and corrects length carry/length placement for CHAR_BIT!=8. |
| wolfcrypt/src/random.c | Fixes DRBG serialization/addition helpers for non-8-bit “byte” targets. |
| wolfcrypt/src/misc.c | Fixes rotate helpers to use CHAR_BIT-based bit width when needed. |
| wolfcrypt/src/coding.c | Ensures Base64 CT decode returns 0xFF for invalid chars even when byte is wider than 8 bits. |
| wolfcrypt/benchmark/benchmark.c | Adds static buffers for WOLFSSL_NO_MALLOC benchmarking and adjusts frees/allocations accordingly. |
| scripts/ti-c2000/user_settings.h | Adds minimal CI-only config for cl2000 compile-guard. |
| scripts/ti-c2000/compile.sh | Adds compile-only script to build a scoped source set with TI cl2000. |
| .github/workflows/ti-c2000-compile.yml | Adds CI workflow to download/cache TI CGT and run the compile-only guard. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
39c343a to
afaf660
Compare
853641c to
0f8a445
Compare
…I C2000 C28x) - core types, misc octet helpers, base64, DRBG
…tation for CHAR_BIT != 8
…MLDSA_VERIFY_SMALLEST_MEM
…<->mp conversion for CHAR_BIT != 8 Curve448/Ed448 build with the CURVE448_SMALL / ED448_SMALL byte-array field backend (the default fe_448 backend needs __uint128_t for the sc448 mod-order arithmetic, which the C28x toolchain lacks). The SMALL fe448 carry-stores wrote each limb through a (word8) cast that does not truncate to an octet when a C byte is wider than 8 bits, so the next carry re-read saw a corrupted limb; mask each carry-store with WC_OCTET (a no-op on the usual 8-bit-byte targets).
|
retest this please |
|
…I C2000 compile CI and docs
| word32 i; | ||
|
|
||
| XMEMCPY(x, state, CHACHA_CHUNK_BYTES); | ||
| XMEMCPY(x, state, CHACHA_CHUNK_WORDS * sizeof(word32)); |
There was a problem hiding this comment.
A word32 should be a 32-bit type.
I guess this is saying that there is no 32-bit type?
| WC_MISC_STATIC WC_INLINE word32 rotlFixed(word32 x, word32 y) | ||
| { | ||
| return (x << y) | (x >> (sizeof(x) * 8 - y)); | ||
| return (x << y) | (x >> (sizeof(x) * CHAR_BIT - y)); |
There was a problem hiding this comment.
This looks weird.
The size of a 32-bit word should be 32 bits not 64 bits I would have thought.
There was a problem hiding this comment.
Every variable on the DSP is 16-bits. Its an odd one!
wolfCrypt: CHAR_BIT != 8 (16-bit byte) support for TI C2000 C28x
Companion example PR (wolfssl-examples): wolfSSL/wolfssl-examples#576
Summary
Adds
WOLFSSL_WIDE_BYTEsupport so wolfCrypt builds and runs correctly on word-addressed targets whereCHAR_BIT != 8- specifically the TI C2000 C28x DSP family, where a Cchar/unsigned char(wolfSSL'sbyte) is 16 bits and is the smallest addressable unit. All changes are gated and are a no-op on normal 8-bit-byte targets.The work was validated end-to-end on a TI LAUNCHXL-F28P55X (TMS320F28P550SJ, C28x, 150 MHz) using the bare-metal example added in the companion wolfssl-examples PR. Every algorithm below passes known-answer tests on hardware, and the standard host
wolfcrypt_testcontinues to pass (no 8-bit regression).Validated algorithms (on C28x hardware)
WC_16BIT_CPUthat emits native instructions instead of compiler 64-bit helper calls - ~53% faster SHAKE/SHA3 on this target)What the
CHAR_BIT != 8fixes addressAll behind
WOLFSSL_WIDE_BYTE(auto-enabled forCHAR_BIT != 8and known 16-bit-char TI toolchain macros), each a no-op on 8-bit targets:word32/word64by casting tobyte*moves addressable cells, not octets. Replaced with explicit shift-based octet I/O via shared helpers inmisc.c(WordsFromBytesBE32/BytesFromWordsBE32,BytesFromWordsLE32, the 64-bit variants, octet-correctreadUnalignedWord32/readUnalignedWord64).sp_int.csp_read_unsigned_binuses an endian-/CHAR_BIT-agnostic shift loop for its leftover bytes (a 3-byte RSA exponent previously loaded as 1 instead of 65537).(byte)xnot truncating to an octet (it keeps 16 bits). Masked withWC_OCTET(x)=(byte)((x) & 0xFF). Used across the ML-KEM/ML-DSA encoders, the SP*_to_binserializers, AESGETBYTE, base64, the DRBG, and the Curve448/Ed448CURVE448_SMALLbyte-array field backend (whose carry-store(word8)casts must mask before the next limb re-reads them).1U << nis 16-bit on C28x (use1UL); a bit width writtensizeof(t) * 8is wrong whenCHAR_BIT != 8(useCHAR_BIT * sizeof(t));byteoperands promote to a 16-bitint.sizeofcounting cells, not octets. e.g.CHACHA_CHUNK_BYTESmust be16 * 4, not16 * sizeof(word32)(= 32 on C28x, which halves the ChaCha block and desyncs the counter).xorbufword stride.WOLFSSL_WORD_SIZE_LOG2vssizeof(word)mismatch left half of each buffer un-XORed on a 16-bit-cell target; corrected for theWC_16BIT_CPUword16path.It also adds
WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM(streams the signaturezvector per-row), which combined withWOLFSSL_MLDSA_ASSIGN_KEYbrings ML-DSA-87 verify to ~10.8 KB RAM with zero heap.Commit layout
wolfcrypt: add WOLFSSL_WIDE_BYTE support for CHAR_BIT != 8 targets (TI C2000 C28x) - core types, misc octet helpers, base64, DRBGsha: octet-correct SHA-1/SHA-2 byte I/O and 32-bit split Keccak permutation for CHAR_BIT != 8aes/chacha: octet-correct block, key, keystream and XTS-tweak I/O for CHAR_BIT != 8mldsa/mlkem: correct ML-DSA and ML-KEM on CHAR_BIT != 8; add WOLFSSL_MLDSA_VERIFY_SMALLEST_MEMecc/25519/448/sp: octet-correct X25519/Ed25519/X448/Ed448 and SP byte<->mp conversion for CHAR_BIT != 8test/benchmark/ci: CHAR_BIT != 8 test vectors, NO_MALLOC benchmark, TI C2000 compile CI and docsFootprint (measured on F28P55X, cl2000 25.11.0; octets, KB = 1024 octets)
Code size is per-object
.textfrom the linker map (16-bit words x2). Builds are single-parameter: ML-DSA-87 only, ML-KEM-1024 only.wc_mldsa.obj)wc_mlkem+wc_mlkem_poly)WOLFSSL_SHA3_SMALL/ split-64 fast path)RAM per operation, measured on hardware (heap high-water via
wolfSSL_SetAllocators, stack via paint/scan):WOLFSSL_MLDSA_ASSIGN_KEY(zero heap); ~15.9 KB copying the public key into the key structTesting
./configure --enable-dilithium --enable-experimental --enable-shake256 --enable-shake128 && make && ./wolfcrypt/test/testwolfcrypt- passes (RSA, ECC, ML-DSA, ML-KEM, SHA-2/3, all crypto). No behavior change on 8-bit-byte targets.wolfcrypt_testcrypto passes.IDE/C2000/compile.shrunscl2000 --compile_onlyover theCHAR_BIT != 8wolfCrypt subset (SHA-1/2/3, AES + modes, ChaCha/Poly1305, X25519/Ed25519, X448/Ed448, ML-DSA verify, SP-ECC);.github/workflows/ti-c2000-compile.ymlruns it on PRs (fetches/caches the TI C2000 code generation tools, with optional SHA-256 pinning of the installer).Benchmarks (F28P55X @ 150 MHz)
ML-DSA-87: verify ~225 ms/op (~10.8 KB RAM, zero heap); keygen and signing also run (SIGN=1).
Notes
wolfcrypt/src/sp_c32.cis generated. The& 0xFFoctet masks added to itssp_*_to_bin_*serializers are also applied in the SP generator templates (kept in sync so a regeneration preserves them).IDE/C2000/README.mddescribes the support, the build options, and the benchmark/footprint results; the full bare-metal example (with KATs, benchmark, linker scripts, and per-algorithmmaketoggles) is in wolfssl-examples atembedded/ti-c2000-f28p55x/.