Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .github/workflows/ti-c2000-compile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
name: TI C2000 (C28x) compile-only

# Compile-guard for the TI C2000 C28x port (CHAR_BIT == 16). It builds the
# wolfCrypt subset that carries the CHAR_BIT != 8 gated fixes with the TI cl2000
# code generation tools - no linking, no C2000Ware, no hardware. Purpose: catch
# compile regressions in the octet/SP/ML-DSA gated paths. On-target run-tests
# live on a hardware-in-the-loop runner (there is no public C28x simulator).

# START OF COMMON SECTION
on:
# Only build when something that can affect the C28x compile changes, so the
# job (and the CGT it pulls) does not burn runner minutes on unrelated PRs.
push:
branches: [ 'master', 'main', 'release/**' ]
paths:
- 'wolfcrypt/src/**'
- 'wolfssl/wolfcrypt/**'
- 'IDE/C2000/**'
- '.github/workflows/ti-c2000-compile.yml'
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches: [ '*' ]
paths:
- 'wolfcrypt/src/**'
- 'wolfssl/wolfcrypt/**'
- 'IDE/C2000/**'
- '.github/workflows/ti-c2000-compile.yml'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# END OF COMMON SECTION

jobs:
ti_c2000_compile:
name: cl2000 compile-only
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
runs-on: ubuntu-22.04
timeout-minutes: 20
env:
# TI C2000 code generation tools (cl2000). TI now gates the CGT download
# behind a login (login.ti.com), so there is no stable public direct URL.
# Mirror the installer - ti_cgt_c2000_<ver>_linux-x64_installer.bin - to a
# location CI can fetch (a wolfSSL release asset or internal server) and
# set the repo/org variable TI_C2000_CGT_URL to it (optionally
# TI_C2000_CGT_SHA256 to pin its hash). When TI_C2000_CGT_URL is unset the
# compile-only guard is skipped and the job succeeds with a notice, so the
# gate never blocks a PR on this external dependency.
CGT_VER: "22.6.2.LTS"
CGT_URL: "${{ vars.TI_C2000_CGT_URL }}"
CGT_SHA256: "${{ vars.TI_C2000_CGT_SHA256 }}"
CGT_DIR: "${{ github.workspace }}/ti-cgt-c2000"
steps:
- uses: actions/checkout@v4
name: Checkout wolfSSL

- name: Check CGT source is configured
id: gate
run: |
if [ -z "$CGT_URL" ]; then
echo "::notice::TI_C2000_CGT_URL repo variable is not set - skipping the C2000 cl2000 compile-only guard. TI gates the CGT download behind a login; mirror the installer and set TI_C2000_CGT_URL (and optionally TI_C2000_CGT_SHA256) to enable this job."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- name: Cache TI C2000 CGT
if: steps.gate.outputs.skip != 'true'
id: cgt-cache
uses: actions/cache@v4
with:
path: ${{ env.CGT_DIR }}
key: ti-cgt-c2000-${{ env.CGT_VER }}

- name: Download + install TI C2000 CGT
if: steps.gate.outputs.skip != 'true' && steps.cgt-cache.outputs.cache-hit != 'true'
run: |
set -e
curl -fSL "$CGT_URL" -o /tmp/cgt.bin
# A gated/expired link answers with an HTML login or 404 page (often
# HTTP 200), which would be saved as a bogus "installer"; reject it.
if head -c 512 /tmp/cgt.bin | grep -qiE '<!doctype html|<html'; then
echo "::error::TI_C2000_CGT_URL returned an HTML page, not the CGT installer (the link likely needs a login or has moved). Point it at a direct mirror of ti_cgt_c2000_${CGT_VER}_linux-x64_installer.bin."
exit 1
fi
if [ -n "$CGT_SHA256" ]; then
echo "$CGT_SHA256 /tmp/cgt.bin" | sha256sum -c -
else
echo "::warning::TI_C2000_CGT_SHA256 is unset - the installer is not integrity-checked. Pin it with the hash below."
sha256sum /tmp/cgt.bin
fi
chmod +x /tmp/cgt.bin
/tmp/cgt.bin --mode unattended --prefix "$CGT_DIR"

- name: Locate cl2000
if: steps.gate.outputs.skip != 'true'
id: find-cl
run: |
CL=$(find "$CGT_DIR" -type f -name cl2000 | head -1)
test -n "$CL" || { echo "cl2000 not found under $CGT_DIR"; exit 1; }
echo "cgt_root=$(dirname "$(dirname "$CL")")" >> "$GITHUB_OUTPUT"

- name: Compile-only guard
if: steps.gate.outputs.skip != 'true'
run: |
CGT_ROOT="${{ steps.find-cl.outputs.cgt_root }}" \
IDE/C2000/compile.sh
7 changes: 7 additions & 0 deletions .wolfssl_known_macro_extras
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ WOLFSSL_CLANG_TIDY
WOLFSSL_CLIENT_EXAMPLE
WOLFSSL_CONTIKI
WOLFSSL_CRL_ALLOW_MISSING_CDP
WOLFSSL_DILITHIUM_VERIFY_SMALLEST_MEM
WOLFSSL_DISABLE_EARLY_SANITY_CHECKS
WOLFSSL_DRBG_SHA256
WOLFSSL_DTLS13_ECHO_LEGACY_SESSION_ID
Expand Down Expand Up @@ -1119,6 +1120,12 @@ __SUNPRO_CC
__SVR4
__TASKING__
__TI_COMPILER_VERSION__
__TMS320C2000__
__TMS320C2800__
__TMS320C28XX__
__TMS320C54X__
__TMS320C5500__
__TMS320C55X__
__TURBOC__
__UNIX__
__USE_GNU
Expand Down
82 changes: 82 additions & 0 deletions IDE/C2000/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# TI C2000 C28x (CHAR_BIT == 16) support

wolfCrypt builds and runs on the TI C2000 C28x DSP family, a word-addressed
architecture where `CHAR_BIT == 16` (a C `char`/`unsigned char` is 16 bits and
is the smallest addressable unit). Support is gated behind `WOLFSSL_WIDE_BYTE`,
which `wolfssl/wolfcrypt/types.h` auto-enables when `CHAR_BIT != 8` or a known
16-bit-char TI toolchain macro is seen (`__TMS320C28XX__`, `__TMS320C2000__`,
etc.). On normal 8-bit-byte targets none of this code changes behavior.

## Validated on hardware (LAUNCHXL-F28P55X, TMS320F28P550SJ, cl2000)

- SHA-1; SHA-224/256, SHA-384/512, SHA-512/224, SHA-512/256
- SHA3-224/256/384/512, SHAKE128/256 (split-64 Keccak permutation auto-enabled
for `WOLFSSL_WIDE_BYTE`, ~53% faster than the generic C path)
- ML-DSA-44/65/87 (Dilithium) verify and full keygen/sign/verify;
ML-KEM-512/768/1024 (FIPS 203)
- AES-128/192/256 CBC/CTR/CFB/OFB/GCM/XTS; AES-CMAC, AES-CCM, AES-GMAC,
AES-SIV, AES-EAX
- HMAC + HKDF; ChaCha20-Poly1305; Poly1305
- X25519 + Ed25519; X448 + Ed448 (CURVE448_SMALL/ED448_SMALL byte backend)
- ECDSA + ECDH (SECP256R1, SP math)
- RSA-2048 PKCS#1 v1.5 sign and verify; DH FFDHE-2048 (SP math)

The on-target acceptance gate is the per-algorithm KAT set the reference example
prints over JTAG (e.g. `ML-DSA-87 verify KAT: PASS`, `X448 a*Bpub: PASS`); the
split-64 Keccak path is additionally validated on a host build with
`-DWC_SHA3_SPLIT64` forced, and the compile-only CI below guards every
`WOLFSSL_WIDE_BYTE` source against build breakage.

## What `WOLFSSL_WIDE_BYTE` fixes

The `CHAR_BIT != 8` work falls into a few recurring classes, each a no-op on
8-bit targets:

- Byte/word aliasing. Serializing a `word32`/`word64` via a `byte*` cast moves
cells, not octets. Replaced with shift-based octet I/O. Shared helpers live in
`wolfcrypt/src/misc.c`: `WordsFromBytesBE32`/`BytesFromWordsBE32`,
`BytesFromWordsLE32`, the 64-bit variants, and octet-correct
`readUnalignedWord32`/`readUnalignedWord64`. `sp_int.c sp_read_unsigned_bin`
uses the endian-/`CHAR_BIT`-agnostic shift loop for its leftover bytes.
- `(byte)x` not truncating to an octet (it keeps 16 bits). Masked with
`WC_OCTET(x)` = `(byte)((x) & 0xFF)` (types.h). Used across the ML-KEM/ML-DSA
encoders, the SP `*_to_bin` serializers, AES `GETBYTE`, base64, and DRBG.
- Integer-promotion bugs. `1U << n` is 16-bit on C28x (use `1UL`); a bit width
written `sizeof(t) * 8` is wrong when `CHAR_BIT != 8` (use `CHAR_BIT *
sizeof(t)`); a `byte` operand promotes to a 16-bit `int`.
- `sizeof` counting cells, not octets. e.g. `CHACHA_CHUNK_BYTES` is `16 * 4`,
not `16 * sizeof(word32)` (= 32 on C28x, which halves the ChaCha block).

The SP backend file `wolfcrypt/src/sp_c32.c` is generated; the `& 0xFF` octet
masks added to its `sp_*_to_bin_*` serializers are also applied in the SP
generator templates so a regeneration preserves them (tracked separately).

## Enabling on your build

Define a user-settings header (see `IDE/C2000/user_settings.h` for a
minimal CHAR_BIT!=8 config) and build with `WOLFSSL_USER_SETTINGS`. For the SP
math backend on a 16-bit-int target also set `WOLFSSL_SP_MATH`,
`SP_WORD_SIZE 32`, and `WOLFSSL_SP_ALLOW_16BIT_CPU`.

## Reference example

A complete bare-metal example with KATs, benchmark, linker scripts, and per-
algorithm build toggles is in wolfSSL Examples:
`embedded/ti-c2000-f28p55x/` (see its `README.md` for the `make` options:
`ECC`, `MLKEM`, `AES`, `AESEXTRA`, `X25519`, `HKDF`, `CHACHA`, `RSA`, `SIGN`,
`BENCH`).

Representative throughput on the F28P55X at 150 MHz: SHA-256 ~284 KiB/s; SHA3-256
~264 KiB/s; SHAKE128 ~319 KiB/s; RNG Hash-DRBG ~122 KiB/s. ML-DSA-87 verify
~225 ms/op in ~10.7 KB RAM (zero heap, with `WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM`
+ `WOLFSSL_MLDSA_ASSIGN_KEY`).

## Continuous integration

`IDE/C2000/compile.sh` runs `cl2000 --compile_only` over the
`CHAR_BIT != 8` wolfCrypt subset to guard these paths without hardware;
`.github/workflows/ti-c2000-compile.yml` runs it in CI. TI gates the C2000
code generation tools behind a login, so the workflow fetches the installer
from the `TI_C2000_CGT_URL` repo/org variable (mirror it to a wolfSSL release
asset or internal server; optionally pin `TI_C2000_CGT_SHA256`). When that
variable is unset the job is skipped with a notice rather than failing.
68 changes: 68 additions & 0 deletions IDE/C2000/compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/bin/sh
# compile.sh - compile-only guard for the TI C2000 (C28x, CHAR_BIT==16) port.
#
# Builds the wolfCrypt subset that compiles under CHAR_BIT==16 with the TI cl2000
# code generation tools, using IDE/C2000/user_settings.h. No linking, no
# C2000Ware, no hardware: this only catches compile regressions in the
# CHAR_BIT != 8 gated code paths (SHA-2/3/SHAKE, ML-DSA-87 verify, SP-ECC).
#
# Usage:
# CGT_ROOT=/path/to/ti-cgt-c2000_xx.y.z IDE/C2000/compile.sh
#
# CGT_ROOT must point at a TI C2000 codegen install (the dir containing
# bin/cl2000). The CGT is a free download from TI; in CI it is fetched/cached
# by .github/workflows/ti-c2000-compile.yml.

set -e

: "${CGT_ROOT:?set CGT_ROOT to the ti-cgt-c2000 install (dir with bin/cl2000)}"

# Repo root = two levels up from this script.
SELF_DIR=$(cd "$(dirname "$0")" && pwd)
WOLFROOT=$(cd "$SELF_DIR/../.." && pwd)
CL="$CGT_ROOT/bin/cl2000"

if [ ! -x "$CL" ]; then
echo "ERROR: cl2000 not found/executable at $CL" >&2
exit 2
fi

OUT=$(mktemp -d)
trap 'rm -rf "$OUT"' EXIT

INCS="-I$CGT_ROOT/include -I$WOLFROOT -I$SELF_DIR"
CFLAGS="-v28 --abi=eabi --float_support=fpu32 --tmu_support=tmu1 -O2 \
--define=WOLFSSL_USER_SETTINGS --display_error_number --diag_warning=225"

# wolfCrypt sources to compile-guard under CHAR_BIT==16. This is the set that
# carries the CHAR_BIT != 8 gated fixes (plus their direct deps) - the
# regression surface for this port. hash.c (an unmodified dispatch wrapper) is
# intentionally omitted: its wc_OidGetHash() OID switch needs the fuller ASN/OID
# config of a real build to avoid a 16-bit-int case-label fold, and it is
# covered by the on-target example build, not by this minimal guard.
SRCS="error wc_port memory logging misc coding \
sha sha256 sha512 sha3 wc_mldsa random ecc sp_int sp_c32 \
aes cmac chacha poly1305 \
curve25519 ed25519 fe_operations ge_operations \
curve448 ed448 fe_448 ge_448"

rc=0
for s in $SRCS; do
printf 'CC %s.c ... ' "$s"
if "$CL" $CFLAGS $INCS --compile_only --skip_assembler \
--asm_directory="$OUT" --obj_directory="$OUT" \
"$WOLFROOT/wolfcrypt/src/$s.c" > "$OUT/$s.log" 2>&1; then
echo "ok"
else
echo "FAIL"
cat "$OUT/$s.log"
rc=1
fi
done

if [ "$rc" -eq 0 ]; then
echo "TI C2000 compile-only guard: PASS"
else
echo "TI C2000 compile-only guard: FAIL" >&2
fi
exit "$rc"
111 changes: 111 additions & 0 deletions IDE/C2000/user_settings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* user_settings.h - minimal wolfCrypt config for the TI C2000 (C28x,
* CHAR_BIT==16) compile-only CI guard.
*
* This is NOT a board config: it has no BSP/device dependencies. Its only job
* is to enable the wolfCrypt subset that carries the CHAR_BIT != 8 gated code
* (SHA-1/2/3/SHAKE, ML-DSA-87 verify, ECDSA/ECDH P-256 via SP math, AES and its
* modes, ChaCha20-Poly1305, X25519/Ed25519, X448/Ed448) so that
* IDE/C2000/compile.sh can compile them with cl2000 and catch regressions.
* cl2000 predefines __TMS320C28XX__, so types.h auto-enables WOLFSSL_WIDE_BYTE;
* we do not set it here. */
#ifndef TI_C2000_CI_USER_SETTINGS_H
#define TI_C2000_CI_USER_SETTINGS_H

#define WOLFCRYPT_ONLY /* crypto only - no TLS (no MD5/SHA1 dep) */
#define WOLFSSL_GENERAL_ALIGNMENT 2
#define HAVE_LIMITS_H
#define WOLFSSL_NO_ASM
#define NO_INLINE
#define SINGLE_THREADED
#define NO_FILESYSTEM
#define NO_WOLFSSL_DIR
#define NO_MAIN_DRIVER
#define NO_DEV_RANDOM
#define WOLFSSL_IGNORE_FILE_WARN
#define BENCH_EMBEDDED
#define NO_WOLFSSL_MEMORY
#define WOLFSSL_GENSEED_FORTEST /* dev-only seed; no TRNG on this part */

/* Hashes (SHA-1 is on by default - NO_SHA is not set) */
#define WOLFSSL_SHA512
#define WOLFSSL_SHA384
#define WOLFSSL_SHA3
#define WOLFSSL_SHAKE128
#define WOLFSSL_SHAKE256

/* AES + modes (block/key/keystream octet I/O and the XTS tweak carry) */
#define HAVE_AES_CBC
#define HAVE_AES_DECRYPT
#define WOLFSSL_AES_COUNTER
#define WOLFSSL_AES_CFB
#define WOLFSSL_AES_OFB
#define HAVE_AESGCM
#define GCM_SMALL
#define HAVE_AESCCM
#define WOLFSSL_CMAC
#define WOLFSSL_AES_XTS
#define WOLFSSL_AES_SIV
#define WOLFSSL_AES_EAX
#define WOLFSSL_AES_DIRECT

/* ChaCha20-Poly1305 (chunk size, keystream and Poly1305 length octet I/O) */
#define HAVE_CHACHA
#define HAVE_POLY1305

/* Curve25519/Ed25519 + Curve448/Ed448 (field serialization octet I/O). 448
* uses the SMALL byte-array backend (no __uint128_t on this toolchain). */
#define HAVE_CURVE25519
#define HAVE_ED25519
#define HAVE_CURVE448
#define CURVE448_SMALL
#define HAVE_ED448
#define ED448_SMALL

/* ML-DSA-87 verify (smallest-mem streaming verifier) */
#define WOLFSSL_HAVE_MLDSA
#define WOLFSSL_NO_ML_DSA_44
#define WOLFSSL_NO_ML_DSA_65
#define WOLFSSL_MLDSA_NO_ASN1
#define WOLFSSL_MLDSA_VERIFY_ONLY
#define WOLFSSL_MLDSA_VERIFY_SMALL_MEM
#define WOLFSSL_MLDSA_VERIFY_NO_MALLOC
#define WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM
#undef WOLFSSL_MLDSA_ALIGNMENT
#define WOLFSSL_MLDSA_ALIGNMENT 16
#define WOLFSSL_SMALL_STACK

/* ECDSA / ECDH P-256 via SP single-precision math (sp_c32.c) */
#define HAVE_ECC
#define ECC_USER_CURVES
#define HAVE_ECC256
#define HAVE_ECC_VERIFY
#define HAVE_ECC_SIGN
#define HAVE_ECC_DHE
#define ECC_TIMING_RESISTANT
#define WOLFSSL_SP_MATH
#define WOLFSSL_HAVE_SP_ECC
#define WOLFSSL_SP_NO_MALLOC
#define WOLFSSL_SP_SMALL
#define SP_WORD_SIZE 32
#define WOLFSSL_SP_ALLOW_16BIT_CPU

/* Off: big-int/ASN and legacy algorithms not part of the CHAR_BIT != 8 surface.
* (RSA/DH are validated on hardware but their CHAR_BIT != 8 fixes live in
* sp_int.c/sp_c32.c, already compiled here via ECC.) */
#define NO_RSA
#define NO_DH
#define NO_DSA
#define NO_ASN
#define NO_CERTS
#define NO_PWDBASED
#define NO_PKCS7
#define NO_PKCS12
#define NO_SIG_WRAPPER
#define NO_DES3
#define NO_RC4
#define NO_MD4
#define NO_MD5
#define NO_ASN_TIME
#define WOLFSSL_USER_CURRTIME

#endif /* TI_C2000_CI_USER_SETTINGS_H */
Loading
Loading