diff --git a/Makefile b/Makefile index ba1760bb..f0741492 100644 --- a/Makefile +++ b/Makefile @@ -133,6 +133,8 @@ CPPCHECK_FLAGS=--enable=warning,performance,portability,missingInclude \ --suppress=constParameterCallback \ --suppress=toomanyconfigs \ --suppress=unmatchedSuppression --inconclusive \ + --suppress=comparePointers:src/port/stm32c5a3/startup.c \ + --suppress=comparePointers:src/port/stm32c5a3/syscalls.c \ --suppress=comparePointers:src/port/stm32h563/startup.c \ --suppress=comparePointers:src/port/stm32h563/syscalls.c \ --suppress=comparePointers:src/port/stm32h753/startup.c \ @@ -149,6 +151,7 @@ CPPCHECK_FLAGS=--enable=warning,performance,portability,missingInclude \ --suppress=comparePointers:src/port/rp2350_cyw43439/startup_hazard3.c \ --suppress=comparePointers:src/port/rp2350_cyw43439/syscalls.c \ --suppress=unknownMacro:src/port/stm32h563/dot1x_client.c \ + --suppress=unknownMacro:src/port/stm32c5a3/startup.c \ --suppress=preprocessorErrorDirective:src/supplicant/supplicant_features.h \ --disable=style \ --std=c99 --language=c \ diff --git a/src/port/stm32/PORTING.md b/src/port/stm32/PORTING.md new file mode 100644 index 00000000..5a0e0a6d --- /dev/null +++ b/src/port/stm32/PORTING.md @@ -0,0 +1,65 @@ +# Porting wolfIP to a new STM32 + +This tree is layered so a new STM32 wolfIP port is a small set of per-chip +files plus shared infrastructure. Existing ports: `stm32c5a3` (M33, TLS 1.3 + +DHUK), `stm32h753` (M7, TLS + HTTPS), `stm32h563` (M33, TLS + SSH + MQTT + +dot1x + FreeRTOS), `stm32f439` (M4, network only), `stm32n6` (M55, network +only). + +## Shared -- reuse as-is + +| File | What it gives you | +|------|-------------------| +| `src/port/stm32/stm32_eth.c` / `.h` | Synopsys DWC GMAC MAC+DMA+PHY driver for H5/H7/N6 (and C5, via a one-line `STM32C5 -> STM32H5` alias). ETH1 at `0x40028000` on H5/C5. | +| `src/port/stm32/wolfssl.mk` | `WOLFSSL_ROOT`/`WOLFMQTT_ROOT` defaults, the `WOLFSSL_SRCS` TLS-1.3 source list, the optional `WOLFSSL_LOG_SRCS` (logging/error/wc_encrypt) group, and the `$(WOLFSSL_ROOT)/%.o` relaxed-warning rule. `include` it from a port Makefile. | +| `src/port/wolfssl_io.c` | wolfIP <-> wolfSSL I/O callbacks (EAGAIN -> WANT_READ/WRITE, `-1` -> CONN_CLOSE). | +| `src/port/tls_client.c` / `.h` | Shared TLS 1.3 client state machine. Build knobs (per-port `-D`): `TLS_CLIENT_MUTUAL_AUTH` (present a client cert from the port's `tls_certs.h`), `ENABLE_DHUK_KEY` (sign CertificateVerify via the DHUK PK callback), `TLS_CLIENT_CIPHER` (force one suite), `TLS_CLIENT_SETTLE_POLLS` (post-connect settle, default 10). Without `MUTUAL_AUTH` it is a plain one-way client (SNI via `tls_client_set_sni()`). | +| `src/port/certs.h` | Embedded test certificates (for the HTTPS/server ports). | +| `src/wolfip.c` | The stack itself. | + +## Per-chip -- supply these + +| File | Notes | +|------|-------| +| `startup.c` (+ `ivt.c`) | Vendor vector table + reset. Genuinely per-silicon (different peripheral IRQ set). C5A3 folds the full M33 vector table into `startup.c` (no separate `ivt.c`). | +| `target.ld` | Memory map (flash/RAM addresses + sizes). Add an `.eth_buffers` output section in RAM for the GMAC descriptors + RX/TX buffers. | +| `main.c` | The per-chip register sequences: `clock_init`, `uart_init`, `eth_gpio_init` (RMII pinmux + SYSCFG/SBS PHY-interface select + RCC ETH gates), `rng_init`. Plus the wolfIP init + poll loop (mostly copyable between ports). | +| `config.h` | wolfIP sizing (sockets, buffers, static IP). | +| `user_settings.h` | wolfSSL config: the shared TLS-1.3 base + a per-chip HW-crypto arm (`STM32_HASH` / `STM32_CRYPTO` / `WOLFSSL_STM32_PKA` / RNG, gated on the chip define). | +| `Makefile` | Thin: CPU flags, device `-D`, includes, `include ../stm32/wolfssl.mk`, board sources, feature source lists, flash command. | + +## Makefile pattern + +```make +CFLAGS := -mcpu=cortex-mXX ... -D +CFLAGS_WOLFSSL := $(filter-out -Werror,$(CFLAGS)) -Wno-unused-variable -Wno-unused-function +include $(ROOT)/src/port/stm32/wolfssl.mk # WOLFSSL_SRCS + %.o rule + defaults +... +ifeq ($(ENABLE_TLS),1) + WOLFSSL_SRCS += $(WOLFSSL_LOG_SRCS) # unless you strip logging/error to save flash + SRCS += $(WOLFSSL_SRCS) +endif +``` + +- A port that needs feature defines on the wolfSSL objects (e.g. SSH/dot1x) sets `WOLFSSL_EXTRA_DEFS` -- the shared `%.o` rule appends it (see `stm32h563/Makefile`). +- Keep `all:`/`clean:`/`size:`/`flash:` in the port; `wolfssl.mk` intentionally defines only variables and the `%.o` pattern rule so it never hijacks the default goal. + +## New-port checklist (things that bite) + +- **`_sbrk` must use the linker `.heap` region**, not `_ebss.._estack`. On a port whose linker places `.eth_buffers` after `.bss`, an `_ebss`-based heap hands out malloc memory that overlaps the Ethernet DMA buffers, and packet DMA silently corrupts heap objects (seen on C5A3: garbage `WOLFSSL_CTX->method` -> bus fault deep in TLS). +- **Add the port to the root `Makefile` cppcheck suppression list** (`comparePointers` for `startup.c`/`syscalls.c`, and `unknownMacro` for a CMSIS vendor `startup.c`). A cleaner alternative for new code: cast the `_sbrk` bounds compare to `uintptr_t` so no `comparePointers` suppression is needed. +- **RMII alternate-function numbers are not uniform** -- check each pin against the CubeMX/HAL board example (on C5A3, RXD0/RXD1 are AF12/AF13 while the rest are AF10). A wrong AF on an RX pin leaves the link up (MDIO works) but drops all RX. +- **DMA buffers must be in DMA-reachable RAM** (not DTCM on H7); place them via the `.eth_buffers` section. +- **Wire MSPLIM** to the linker's real stack-limit symbol (`PROVIDE(__STACK_LIMIT = __StackLimit)` if the CMSIS startup uses `__STACK_LIMIT`) so a stack overflow faults cleanly instead of corrupting the heap. +- The `../wolfssl` object tree is shared: ports at different FP ABIs (hard vs soft float) clobber each other's `.o` files. `make clean` (or delete `$(WOLFSSL_ROOT)/**/*.o`) between such builds. + +## Shared TLS client + +`tls_client.c`/`.h` moved to `src/port/` (see the shared table above). The three +TLS ports now compile the single `$(ROOT)/src/port/tls_client.c` and select +behavior with `-D` knobs: `stm32c5a3` sets `TLS_CLIENT_MUTUAL_AUTH` + +`TLS_CLIENT_CIPHER` (+ `ENABLE_DHUK_KEY`) for its mutual-auth DHUK demo, while +`stm32h753`/`stm32h563` build it as a plain one-way client (H753 keeps its +historical `TLS_CLIENT_SETTLE_POLLS=100`). C5A3's mutual-auth + DHUK build is +verified on hardware; H753/H563 are build-verified (boards not testable here -- +no H753 on the bench, H563 is provisioned TZEN=1). diff --git a/src/port/stm32/stm32_eth.c b/src/port/stm32/stm32_eth.c index 88004c4c..2b668bef 100644 --- a/src/port/stm32/stm32_eth.c +++ b/src/port/stm32/stm32_eth.c @@ -11,8 +11,16 @@ #include "config.h" #include "stm32_eth.h" +/* STM32C5 (Cortex-M33) uses a DWC GMAC that is byte-for-byte identical to + * the STM32H5: same ETH_BASE (0x40028000), same register layout, same NS + * access model, and the same MDIO clock-range divider (CR=4) at HCLK 144MHz. + * Alias it onto the H5 path so it reuses every H5 value and code arm. */ +#if defined(STM32C5) && !defined(STM32H5) +#define STM32H5 +#endif + #if !defined(STM32H5) && !defined(STM32H7) && !defined(STM32N6) -#error "Define STM32H5, STM32H7 or STM32N6 for stm32_eth.c" +#error "Define STM32C5, STM32H5, STM32H7 or STM32N6 for stm32_eth.c" #endif #if defined(STM32H5) diff --git a/src/port/stm32/wolfssl.mk b/src/port/stm32/wolfssl.mk new file mode 100644 index 00000000..7fa1f2ee --- /dev/null +++ b/src/port/stm32/wolfssl.mk @@ -0,0 +1,74 @@ +# wolfssl.mk -- shared wolfSSL/wolfCrypt build plumbing for the STM32 bare-metal +# wolfIP ports (stm32c5a3, stm32h753, stm32h563, ...). +# +# Copyright (C) 2026 wolfSSL Inc. +# +# `include $(ROOT)/src/port/stm32/wolfssl.mk` from a port Makefile. Provides: +# - WOLFSSL_ROOT / WOLFMQTT_ROOT sibling defaults (a port may set WOLFSSL_ROOT +# earlier with ?= to point at a worktree; that still wins). +# - WOLFSSL_SRCS: the minimal wolfSSL/wolfCrypt source set for a TLS 1.3 client +# (ECC + AES-GCM) plus the ChaCha20-Poly1305 / RSA-verify / logging groups +# the ports share. Add to SRCS from inside the port's ENABLE_TLS block. +# - the $(WOLFSSL_ROOT)/%.o relaxed-warning compile rule (uses the port's +# CFLAGS_WOLFSSL, which the port defines and appends its own -D flags to). +# +# Only variables and a pattern rule live here -- no explicit targets -- so the +# including Makefile keeps ownership of the default goal and of all/clean/size/ +# flash. Include it AFTER the port has set CFLAGS (and, if used, its own +# WOLFSSL_ROOT ?= override); the port defines CFLAGS_WOLFSSL itself. + +WOLFSSL_ROOT ?= $(ROOT)/../wolfssl +WOLFMQTT_ROOT ?= $(ROOT)/../wolfmqtt + +# Minimal wolfSSL/wolfCrypt source set for a TLS 1.3 client with ECC + AES-GCM. +WOLFSSL_SRCS := \ + $(WOLFSSL_ROOT)/wolfcrypt/src/aes.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/sha.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/sha256.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/sha512.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/hmac.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/hash.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/kdf.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/random.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/ecc.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/asn.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/coding.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/wc_port.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/memory.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/wolfmath.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/sp_int.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/sp_c32.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/sp_cortexm.c \ + $(WOLFSSL_ROOT)/src/ssl.c \ + $(WOLFSSL_ROOT)/src/tls.c \ + $(WOLFSSL_ROOT)/src/tls13.c \ + $(WOLFSSL_ROOT)/src/internal.c \ + $(WOLFSSL_ROOT)/src/keys.c \ + $(WOLFSSL_ROOT)/src/wolfio.c + +# ChaCha20-Poly1305 (fallback suite). +WOLFSSL_SRCS += \ + $(WOLFSSL_ROOT)/wolfcrypt/src/chacha.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/chacha20_poly1305.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/poly1305.c + +# RSA for peer certificate verification. +WOLFSSL_SRCS += \ + $(WOLFSSL_ROOT)/wolfcrypt/src/rsa.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/signature.c + +# Logging / error-strings / encrypt-helpers group. Kept separate because a +# size-tight port may omit it (e.g. with NO_ERROR_STRINGS + logging off). Ports +# that want it do: WOLFSSL_SRCS += $(WOLFSSL_LOG_SRCS) +WOLFSSL_LOG_SRCS := \ + $(WOLFSSL_ROOT)/wolfcrypt/src/logging.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/error.c \ + $(WOLFSSL_ROOT)/wolfcrypt/src/wc_encrypt.c + +# wolfSSL objects build with relaxed warnings (third-party, heavily #ifdef'd). +# CFLAGS_WOLFSSL is defined by the including port Makefile. WOLFSSL_EXTRA_DEFS +# lets a port add feature defines that the wolfSSL objects need (e.g. H563's +# -DWOLFSSL_WOLFSSH / -DHAVE_PBKDF2 for its SSH / dot1x builds); it is empty by +# default. +$(WOLFSSL_ROOT)/%.o: $(WOLFSSL_ROOT)/%.c + $(CC) $(CFLAGS_WOLFSSL) -DWOLFSSL_USER_SETTINGS $(WOLFSSL_EXTRA_DEFS) -I$(WOLFSSL_ROOT) -c $< -o $@ diff --git a/src/port/stm32c5a3/.gitignore b/src/port/stm32c5a3/.gitignore new file mode 100644 index 00000000..cbc41116 --- /dev/null +++ b/src/port/stm32c5a3/.gitignore @@ -0,0 +1,8 @@ +# Build artifacts +*.o +app.elf +app.bin + +# Generated TLS test host material (contains a throwaway test private key). +# Regenerate with: python3 gen_certs.py (also rewrites tls_certs.h) +tls-certs/ diff --git a/src/port/stm32c5a3/Makefile b/src/port/stm32c5a3/Makefile new file mode 100644 index 00000000..ccb213a3 --- /dev/null +++ b/src/port/stm32c5a3/Makefile @@ -0,0 +1,289 @@ +CC ?= arm-none-eabi-gcc +OBJCOPY ?= arm-none-eabi-objcopy + +ROOT := ../../.. + +# TLS support: set ENABLE_TLS=1 to include wolfSSL +# Requires wolfSSL cloned alongside wolfip (or set WOLFSSL_ROOT) +ENABLE_TLS ?= 0 + +# TLS client test: set ENABLE_TLS_CLIENT=1 to include TLS client (Google test) +# Automatically enables TLS if needed +ENABLE_TLS_CLIENT ?= 0 + +# HTTPS web server: set ENABLE_HTTPS=1 to include HTTPS web server +# Automatically enables TLS if needed +ENABLE_HTTPS ?= 0 + +# MQTT Broker: set ENABLE_MQTT_BROKER=1 to include wolfMQTT broker (requires TLS) +ENABLE_MQTT_BROKER ?= 0 + +# Auto-enable TLS when any feature that requires it is enabled +ifeq ($(ENABLE_TLS_CLIENT),1) + ENABLE_TLS = 1 +endif +ifeq ($(ENABLE_HTTPS),1) + ENABLE_TLS = 1 +endif +ifeq ($(ENABLE_MQTT_BROKER),1) + ENABLE_TLS = 1 +endif + +# Library paths - default to sibling directories. The TLS client uses the +# STM32C5 HW-crypto arm, so it needs the wolfSSL worktree that carries the +# STM32C5 DHUK/BARE port. (?= means an explicit WOLFSSL_ROOT= still wins.) +ifeq ($(ENABLE_TLS_CLIENT),1) + WOLFSSL_ROOT ?= $(ROOT)/../wolfssl-c5tls +endif +# Sibling WOLFSSL_ROOT/WOLFMQTT_ROOT fallbacks + the shared wolfSSL source list +# and $(WOLFSSL_ROOT)/%.o rule come from the shared fragment (included below). + +# ----------------------------------------------------------------------------- +# Device headers / CMSIS +# ----------------------------------------------------------------------------- +# STM32C5 device header (stm32c5a3xx.h) from the CubeC5 DFP. +CUBEC5_INC ?= $(HOME)/GitHub/STM32CubeC5/stm32c5xx_dfp/Include + +# Cortex-M33 CMSIS-Core. No C5 pack ships it, so borrow from the U5 pack +# (overridable, matching the STM32_Bare_Test board.mk convention). +STM32CUBE_FW_U5 ?= $(HOME)/STM32Cube/Repository/STM32Cube_FW_U5_V1.8.0 +CMSIS_CORE ?= $(STM32CUBE_FW_U5)/Drivers/CMSIS/Core/Include + +# STM32C5A3ZG - Cortex-M33F with single-precision FPU +CFLAGS := -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard +CFLAGS += -Os -ffreestanding -fdata-sections -ffunction-sections +CFLAGS += -g -ggdb -Wall -Wextra -Werror +CFLAGS += -I. -I$(ROOT) -I$(ROOT)/src -I$(ROOT)/src/port -I$(ROOT)/src/port/stm32 +CFLAGS += -I$(CUBEC5_INC) -I$(CMSIS_CORE) +CFLAGS += -DSTM32C5 -DSTM32C5A3xx + +# Relaxed warnings for external libraries +CFLAGS_WOLFSSL := $(CFLAGS) +CFLAGS_WOLFSSL := $(filter-out -Werror,$(CFLAGS_WOLFSSL)) +CFLAGS_WOLFSSL += -Wno-unused-variable -Wno-unused-function + +# Shared wolfSSL build plumbing: WOLFSSL_ROOT/WOLFMQTT_ROOT defaults, the base +# WOLFSSL_SRCS list, and the $(WOLFSSL_ROOT)/%.o relaxed-warning rule. +include $(ROOT)/src/port/stm32/wolfssl.mk + +LDSCRIPT := target.ld +# The vendor startup (startup.c) uses the CMSIS __PROGRAM_START flow: +# __cmsis_start runs the .copy.table/.zero.table init then calls newlib's +# _start (crt0), which runs __libc_init_array and main. So we link the +# newlib crt0 via nano/nosys specs rather than -nostartfiles/-nostdlib. +LDFLAGS := -T $(LDSCRIPT) --specs=nano.specs --specs=nosys.specs -Wl,-gc-sections + +# Base source files. The vector table lives in startup.c (vendor startup +# with full M33 IVT), so there is no separate ivt.c. +SRCS := startup.c syscalls.c main.c $(ROOT)/src/port/stm32/stm32_eth.c $(ROOT)/src/wolfip.c + +# ----------------------------------------------------------------------------- +# TLS Support (wolfSSL) +# ----------------------------------------------------------------------------- +ifeq ($(ENABLE_TLS),1) + +# Validate wolfSSL exists +ifeq ($(wildcard $(WOLFSSL_ROOT)/wolfssl/ssl.h),) + $(error wolfSSL not found at $(WOLFSSL_ROOT). Clone it: git clone https://github.com/wolfSSL/wolfssl.git) +endif + +# ENABLE_TLS is on for the whole build (drives the -DENABLE_TLS #ifdef in +# main.c), but WOLFSSL_WOLFIP / WOLFSSL_STM32C5 / the wolfSSL include must NOT +# reach the plain port sources (stm32_eth.c, wolfip.c, startup.c, ...). Those +# files include wolfip.h, and wolfip.h pulls in wolfssl settings.h when +# WOLFSSL_WOLFIP is set; with WOLFSSL_STM32C5 that in turn pulls the CubeC5 +# device header (stm32c5xx.h) whose ETH_* defines collide with stm32_eth.c's +# private fallback macros. So the wolfSSL-facing defines live only in the +# wolfSSL/glue compile flags below, not in the global CFLAGS. +CFLAGS += -DENABLE_TLS + +# Defines needed by every object that actually consumes wolfSSL headers: +# the wolfcrypt/ssl sources and the two wolfIP<->wolfSSL glue files. +TLS_WOLFSSL_DEFS := -DWOLFSSL_USER_SETTINGS -DWOLFSSL_WOLFIP -I$(WOLFSSL_ROOT) + +# wolfIP-wolfSSL glue (compiled with the wolfSSL-facing flags, see rule below) +SRCS += $(ROOT)/src/port/wolfssl_io.c + +# TLS client (3A.0 mutual-auth against openssl s_server) +ifeq ($(ENABLE_TLS_CLIENT),1) +CFLAGS += -DENABLE_TLS_CLIENT +SRCS += $(ROOT)/src/port/tls_client.c + +# Milestone 3A.1: set ENABLE_DHUK_KEY=1 to sign CertificateVerify with the +# DHUK-wrapped identity key via a PK callback (identity scalar never in SW). +ENABLE_DHUK_KEY ?= 0 +ifeq ($(ENABLE_DHUK_KEY),1) +TLS_WOLFSSL_DEFS += -DENABLE_DHUK_KEY +endif + +# STM32C5 HW-crypto arm for the TLS client. WOLFSSL_STM32C5 also gates the C5 +# arm in user_settings.h. These join the wolfSSL-facing flags so only the +# wolfSSL/glue objects see them. +TLS_WOLFSSL_DEFS += -DWOLFSSL_STM32C5 -DWOLFSSL_DHUK -DWOLF_CRYPTO_CB + +# STM32 DHUK/BARE port + crypto-callback dispatcher for the C5 arm. +SRCS += $(WOLFSSL_ROOT)/wolfcrypt/src/port/st/stm32.c +SRCS += $(WOLFSSL_ROOT)/wolfcrypt/src/cryptocb.c +endif + +# CFLAGS_WOLFSSL was captured (:=) before this block; append the wolfSSL-facing +# defines so the wolfcrypt/ssl objects build against the C5 arm and user +# settings. The wolfSSL objects use the $(WOLFSSL_ROOT)/%.o rule (relaxed +# warnings) further down. The two glue files (tls_client.c, wolfssl_io.c) get +# explicit compile rules near the build rules at the bottom -- placing target +# rules here (before "all:") would hijack the default goal. +CFLAGS_WOLFSSL += $(TLS_WOLFSSL_DEFS) + +# wolfSSL/wolfCrypt source set comes from the shared wolfssl.mk (included above), +# plus the logging/error/encrypt group. +WOLFSSL_SRCS += $(WOLFSSL_LOG_SRCS) +SRCS += $(WOLFSSL_SRCS) + +endif # ENABLE_TLS + +# ----------------------------------------------------------------------------- +# HTTPS web server (requires TLS) - uses existing wolfIP httpd +# ----------------------------------------------------------------------------- +ifeq ($(ENABLE_HTTPS),1) +CFLAGS += -DENABLE_HTTPS +SRCS += $(ROOT)/src/http/httpd.c +endif + +# ----------------------------------------------------------------------------- +# MQTT Broker Support (wolfMQTT broker) - requires TLS +# ----------------------------------------------------------------------------- +ifeq ($(ENABLE_MQTT_BROKER),1) + +# MQTT Broker requires TLS +ifeq ($(ENABLE_TLS),0) + $(error ENABLE_MQTT_BROKER=1 requires ENABLE_TLS=1) +endif + +# Validate wolfMQTT exists +ifeq ($(wildcard $(WOLFMQTT_ROOT)/wolfmqtt/mqtt_broker.h),) + $(error wolfMQTT (with broker) not found at $(WOLFMQTT_ROOT). Clone it: git clone https://github.com/wolfSSL/wolfMQTT.git) +endif + +CFLAGS += -DENABLE_MQTT_BROKER +CFLAGS += -DWOLFMQTT_USER_SETTINGS +CFLAGS += -I$(WOLFMQTT_ROOT) + +# MQTT broker wrapper +SRCS += mqtt_broker.c + +# wolfIP-wolfMQTT glue +SRCS += $(ROOT)/src/port/wolfmqtt_io.c + +# wolfMQTT broker source files +WOLFMQTT_BROKER_SRCS := \ + $(WOLFMQTT_ROOT)/src/mqtt_broker.c \ + $(WOLFMQTT_ROOT)/src/mqtt_client.c \ + $(WOLFMQTT_ROOT)/src/mqtt_packet.c \ + $(WOLFMQTT_ROOT)/src/mqtt_socket.c + +SRCS += $(WOLFMQTT_BROKER_SRCS) + +# wolfMQTT objects use relaxed warnings + include paths + user_settings.h +$(WOLFMQTT_ROOT)/%.o: $(WOLFMQTT_ROOT)/%.c + $(CC) $(CFLAGS_WOLFSSL) -DENABLE_MQTT_BROKER -DWOLFSSL_USER_SETTINGS -DWOLFMQTT_USER_SETTINGS -I$(WOLFMQTT_ROOT) -I$(WOLFSSL_ROOT) -I$(ROOT)/src -c $< -o $@ +endif # ENABLE_MQTT_BROKER + +# ----------------------------------------------------------------------------- +# Build rules +# ----------------------------------------------------------------------------- +OBJS := $(patsubst %.c,%.o,$(SRCS)) + +all: app.bin + @echo "Built with ENABLE_TLS=$(ENABLE_TLS) ENABLE_TLS_CLIENT=$(ENABLE_TLS_CLIENT) ENABLE_HTTPS=$(ENABLE_HTTPS) ENABLE_MQTT_BROKER=$(ENABLE_MQTT_BROKER)" +ifeq ($(ENABLE_TLS),1) + @echo " wolfSSL: $(WOLFSSL_ROOT)" +endif +ifeq ($(ENABLE_MQTT_BROKER),1) + @echo " wolfMQTT (broker): $(WOLFMQTT_ROOT)" +endif + +app.elf: $(OBJS) $(LDSCRIPT) + $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -Wl,--start-group -lc -lm -lgcc -lnosys -Wl,--end-group -o $@ + +app.bin: app.elf + $(OBJCOPY) -O binary $< $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# The $(WOLFSSL_ROOT)/%.o relaxed-warning rule comes from wolfssl.mk. + +# The two wolfIP<->wolfSSL glue files live in the app/port tree but include +# wolfssl/ssl.h, so they need the wolfSSL-facing defines (WOLFSSL_WOLFIP / +# WOLFSSL_STM32C5 / -I$(WOLFSSL_ROOT)). Keep -Werror (our code). These are +# defined only when ENABLE_TLS is on, and sit after "all:" so they do not +# hijack the default goal. +ifeq ($(ENABLE_TLS),1) +# Shared TLS client: mutual auth (client cert from tls_certs.h) + forced suite. +$(ROOT)/src/port/tls_client.o: $(ROOT)/src/port/tls_client.c + $(CC) $(CFLAGS) $(TLS_WOLFSSL_DEFS) -DTLS_CLIENT_MUTUAL_AUTH -DTLS_CLIENT_CIPHER='"TLS_AES_128_GCM_SHA256"' -c $< -o $@ + +$(ROOT)/src/port/wolfssl_io.o: $(ROOT)/src/port/wolfssl_io.c + $(CC) $(CFLAGS) $(TLS_WOLFSSL_DEFS) -c $< -o $@ +endif + +clean: + rm -f *.o app.elf app.bin + rm -f $(ROOT)/src/*.o + rm -f $(ROOT)/src/port/*.o + rm -f $(ROOT)/src/port/stm32/*.o + rm -f $(ROOT)/src/http/*.o +ifeq ($(ENABLE_TLS),1) + rm -f $(WOLFSSL_ROOT)/wolfcrypt/src/*.o + rm -f $(WOLFSSL_ROOT)/wolfcrypt/src/port/st/*.o + rm -f $(WOLFSSL_ROOT)/wolfcrypt/test/*.o + rm -f $(WOLFSSL_ROOT)/src/*.o +endif +ifeq ($(ENABLE_MQTT_BROKER),1) + rm -f $(WOLFMQTT_ROOT)/src/*.o +endif + +# Show memory usage +size: app.elf + @echo "=== Memory Usage ===" + @arm-none-eabi-size app.elf + @echo "" + @echo "Flash usage: $$(arm-none-eabi-size app.elf | awk 'NR==2{printf "%.1f%% (%d / %d bytes)", ($$1+$$2)*100/1048576, $$1+$$2, 1048576}')" + @echo "RAM usage (static): $$(arm-none-eabi-size app.elf | awk 'NR==2{printf "%.1f%% (%d / %d bytes)", ($$2+$$3)*100/262144, $$2+$$3, 262144}')" + +# Flash using STM32CubeProgrammer over SWD (ST-LINK V3 on NUCLEO-C5A3ZG) +flash: app.bin + STM32_Programmer_CLI -c port=swd sn=003D00203235510F37333439 mode=UR -e all -d app.bin 0x08000000 -v -rst + +.PHONY: all clean size flash + +# ----------------------------------------------------------------------------- +# Help +# ----------------------------------------------------------------------------- +help: + @echo "STM32C5A3ZG wolfIP Build System" + @echo "" + @echo "Usage: make [target] [options]" + @echo "" + @echo "Targets:" + @echo " all Build app.bin (default)" + @echo " clean Remove build artifacts" + @echo " size Show memory usage statistics" + @echo " flash Flash to device using STM32_Programmer_CLI" + @echo " help Show this help" + @echo "" + @echo "Options:" + @echo " ENABLE_TLS=1 Enable wolfSSL TLS support" + @echo " ENABLE_TLS_CLIENT=1 Enable TLS client test (Google)" + @echo " ENABLE_HTTPS=1 Enable HTTPS web server (port 443)" + @echo " ENABLE_MQTT_BROKER=1 Enable MQTT broker (port 8883 with TLS)" + @echo " WOLFSSL_ROOT= Path to wolfSSL (default: ../wolfssl)" + @echo " WOLFMQTT_ROOT= Path to wolfMQTT (default: ../wolfmqtt)" + @echo " CUBEC5_INC= Path to CubeC5 DFP Include (stm32c5a3xx.h)" + @echo " CMSIS_CORE= Path to Cortex-M33 CMSIS-Core" + @echo " CC= C compiler (default: arm-none-eabi-gcc)" + @echo "" + @echo "Testing:" + @echo " nc 7 # TCP echo test" + +.PHONY: help diff --git a/src/port/stm32c5a3/README.md b/src/port/stm32c5a3/README.md new file mode 100644 index 00000000..831c254f --- /dev/null +++ b/src/port/stm32c5a3/README.md @@ -0,0 +1,107 @@ +# wolfIP on STM32C5A3ZG (NUCLEO-C5A3ZG) + +Bare-metal wolfIP TCP/IP port for the STMicro STM32C5A3ZG (Cortex-M33F, no +TrustZone), with a TLS 1.3 mutual-auth client whose long-term ECDSA identity +key is protected by the STM32 DHUK (Device Hardware Unique Key) crypto +callback: the private scalar is unwrapped inside the SAES block and signed on +the hardware PKA, so it never appears in software. + +## Status + +All configurations validated on hardware (NUCLEO-C5A3ZG): + +- Network: DHCP + ICMP ping + TCP echo on port 7. +- TLS 1.3 mutual-auth client: `TLS_AES_128_GCM_SHA256`, peer verifies the device client certificate. +- DHUK identity key in TLS: CertificateVerify signed through the callback (SAES unwrap -> HW PKA). + +## Hardware + +- MCU: Cortex-M33F @ 144 MHz (HSE 48 MHz -> PSI -> PSIS), 1 MB flash, 256 KB SRAM. No TrustZone (single image). +- Ethernet: on-chip Synopsys DWC GMAC (RMII), shared driver `../stm32/stm32_eth.c`. +- Crypto: SAES, PKA (V2, sign-only), HASH, RNG. + +RMII pin map (all AF10 unless noted): + +| Signal | Pin | Note | +|-----------|------|------------------------------| +| REF_CLK | PA1 | | +| MDC | PC1 | | +| MDIO | PE12 | not PA2 (PA2 = USART2 TX) | +| CRS_DV | PD1 | | +| RXD0 | PC4 | **AF12** (not AF10) | +| RXD1 | PC5 | **AF13** (not AF10) | +| TX_EN | PG11 | | +| TXD0 | PG13 | | +| TXD1 | PG12 | | + +Console: USART2 PA2(TX)/PA3(RX) AF7, 115200 8N1 over the ST-LINK VCP. + +## Build + +``` +make # network only (DHCP + ping + TCP echo) +make ENABLE_TLS_CLIENT=1 # + TLS 1.3 mutual-auth client (software identity key) +make ENABLE_TLS_CLIENT=1 ENABLE_DHUK_KEY=1 # + identity key signed via the DHUK callback +``` + +The TLS builds need the wolfSSL branch that carries the STM32C5 bare-crypto + +DHUK port; point `WOLFSSL_ROOT` at it (defaults to the sibling `../wolfssl-c5tls` +for those builds). The wolfSSL/wolfCrypt source list and build rules are shared +from `../stm32/wolfssl.mk`. + +If your shell presets `CC` to the host compiler, pass `CC=arm-none-eabi-gcc`. + +Approx flash usage (1 MB): network 3.0%, TLS 21.2%, TLS+DHUK 21.9%. + +## Flash + +STM32CubeProgrammer over SWD (pyocd / st-flash do not support C5; the debug AP +is AP2, so use under-reset connect): + +``` +make flash +# = STM32_Programmer_CLI -c port=swd sn= mode=UR -e all -d app.bin 0x08000000 -v -rst +``` + +## TLS test + +The device is the client; run an OpenSSL server on the host that requires and +verifies the client certificate: + +``` +cd tls-certs +openssl s_server -accept 11111 -tls1_3 -cert server.pem -key server.key.pem \ + -Verify 1 -CAfile ca.pem -www +``` + +Set the target in `config.h` (`TLS_SERVER_IP` / `TLS_SERVER_PORT`). On success +the device logs `TLS Client: Passed!` and the server logs `verify return:1` for +`CN = stm32c5a3-client`. + +## Test certificates + +`tls_certs.h` (embedded in the firmware, all public material) is committed; the +host server material `tls-certs/` is git-ignored (it holds a throwaway test +key). Both come from `gen_certs.py`. The client identity keypair is the public +NIST P-256 CAVP vector in `identity_key.h`; the CA and server are fixed-scalar +test keys. To (re)generate before running the TLS test: + +``` +python3 gen_certs.py # writes tls_certs.h AND tls-certs/{ca,server,server.key}.pem +make ENABLE_TLS_CLIENT=1 ENABLE_DHUK_KEY=1 && make flash +``` + +ECDSA cert DER is not byte-reproducible, so regenerating rewrites `tls_certs.h` +too -- rebuild + reflash so the device cert matches the fresh host CA. + +## Hardware gotchas (see the inline comments) + +- RXD0/RXD1 use AF12/AF13, not AF10 -- AF10 there silently kills RX (link stays up via MDIO, no frames received). +- The RNG clock-error detector (`CR.CED`) false-trips on the 48 MHz CK48 clock; it is disabled (`WC_STM32_RNG_CED_DISABLE`). +- `_sbrk` uses the linker `.heap` region -- an `_ebss.._estack` heap would overlap the Ethernet DMA buffers and be corrupted by packet DMA. +- C5 PKA is sign-only: ECDSA verify and ephemeral ECDHE run in software (Config A). + +## Out of scope + +- Config B (`WOLF_CRYPTO_CB_ONLY_ECC`, route ECDHE through the callback to strip software ECC) -- needs wolfSSL-side ECDH callback work. +- wolfBoot secure-boot chainload (this board is supported by wolfBoot `config/examples/stm32c5.config`). diff --git a/src/port/stm32c5a3/config.h b/src/port/stm32c5a3/config.h new file mode 100644 index 00000000..e26ad658 --- /dev/null +++ b/src/port/stm32c5a3/config.h @@ -0,0 +1,86 @@ +/* config.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP 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 + */ +#ifndef WOLF_CONFIG_H +#define WOLF_CONFIG_H + +#ifndef CONFIG_IPFILTER +#define CONFIG_IPFILTER 0 +#endif + +#define ETHERNET +#define LINK_MTU 1536 + +/* Socket configuration - STM32C5A3ZG has only 256KB SRAM. Each tsocket + * embeds rxmem[RXBUF_SIZE] + txmem[TXBUF_SIZE], so the static pool is + * (MAX_*SOCKETS) * (RXBUF_SIZE + TXBUF_SIZE). Keep these small enough that + * the pool + heap + stack fit in 256KB (see target.ld for heap/stack). */ +#define MAX_TCPSOCKETS 4 +#define MAX_UDPSOCKETS 2 +#define MAX_ICMPSOCKETS 1 +#define RXBUF_SIZE (LINK_MTU * 2) /* 3KB */ +#define TXBUF_SIZE (LINK_MTU * 2) /* 3KB */ + +#define MAX_NEIGHBORS 16 + +#ifndef WOLFIP_MAX_INTERFACES +#define WOLFIP_MAX_INTERFACES 1 +#endif + +#ifndef WOLFIP_ENABLE_FORWARDING +#define WOLFIP_ENABLE_FORWARDING 0 +#endif + +#ifndef WOLFIP_ENABLE_LOOPBACK +#define WOLFIP_ENABLE_LOOPBACK 0 +#endif + +#ifndef WOLFIP_ENABLE_DHCP +#define WOLFIP_ENABLE_DHCP 1 +#endif + +/* Static IP fallback (used when DHCP is disabled or times out). + * Bench segment is 10.0.4.0/24 (host enp5s0 = 10.0.4.24, GW 10.0.4.1). */ +#define WOLFIP_IP "10.0.4.123" +#define WOLFIP_NETMASK "255.255.255.0" +#define WOLFIP_GW "10.0.4.1" +#define WOLFIP_STATIC_DNS_IP "8.8.8.8" + +#if WOLFIP_ENABLE_DHCP +#define DHCP +/* Reduce DHCP retries for faster fallback to static IP on demo boards */ +#define DHCP_DISCOVER_RETRIES 1 +#define DHCP_REQUEST_RETRIES 1 +#endif + +/* TLS 1.3 mutual-auth client target (ENABLE_TLS_CLIENT build, milestone 3A.0). + * The device connects to an openssl s_server here after the network is up. + * Change these to point at your test server. */ +#ifndef TLS_SERVER_IP +#define TLS_SERVER_IP "10.0.4.24" +#endif +#ifndef TLS_SERVER_PORT +#define TLS_SERVER_PORT 11111 +#endif + +/* Hardware debug: uncomment to enable verbose GPIO/ETH/MDIO/DHCP logging */ +/* #define DEBUG_HW */ + +#endif /* WOLF_CONFIG_H */ diff --git a/src/port/stm32c5a3/gen_certs.py b/src/port/stm32c5a3/gen_certs.py new file mode 100644 index 00000000..e163f309 --- /dev/null +++ b/src/port/stm32c5a3/gen_certs.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# gen_certs.py -- generate the TLS test PKI for the STM32C5A3ZG wolfIP TLS client. +# +# Copyright (C) 2026 wolfSSL Inc. +# +# The CA and server EC keys are fixed scalars and the client key is the NIST +# P-256 CAVP SigGen vector from identity_key.h. ECDSA cert DER is not byte- +# reproducible (random nonce), so re-running this script produces a fresh but +# internally-matched PKI: rebuild + reflash the firmware after regenerating. +# +# Outputs: +# tls_certs.h (committed) -- client cert + client key (public CAVP scalar) + +# CA cert, embedded into the firmware. All public material. +# tls-certs/ (git-ignored) -- ca.pem, server.pem, server.key.pem for the +# test server (server.key.pem is a throwaway TEST key, hence not +# committed), plus client.pem/client.key.pem for reference. +# +# Test server the device connects to (see README.md): +# openssl s_server -accept 11111 -tls1_3 -cert server.pem -key server.key.pem \ +# -Verify 1 -CAfile ca.pem -www +# +# Usage: cd src/port/stm32c5a3 && python3 gen_certs.py +# Requires: python3-cryptography. TEST ONLY -- never use for production. + +import os +import datetime +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives import hashes, serialization +from cryptography import x509 +from cryptography.x509.oid import NameOID + +HERE = os.path.dirname(os.path.abspath(__file__)) +OUT = os.path.join(HERE, "tls-certs") +os.makedirs(OUT, exist_ok=True) + +# Client identity keypair = NIST P-256 CAVP SigGen vector (identity_key.h). +# The scalar is public (a published KAT vector); it is the identity key whose +# private form the device holds only in DHUK-wrapped shape at runtime. +D = bytes.fromhex("708309a7449e156b0db70e5b52e606c7e094ed676ce8953bf6c14757c826f590") +QX = bytes.fromhex("29578c7ab6ce0d11493c95d5ea05d299d536801ca9cbd50e9924e43b733b83ab") +QY = bytes.fromhex("08c8049879c6278b2273348474158515accaa38344106ef96803c5a05adc4800") + +# CA and server use P-256 keys from fixed scalars (< the P-256 order n; the +# high 0x0f byte keeps them in range). ECDSA is used throughout so the device's +# wolfSSL (no Ed25519) can parse the server certificate it receives. TEST-ONLY +# throwaway keys. NOTE: ECDSA signing uses a random nonce, so the cert DER is +# NOT byte-reproducible across runs -- this is why the whole matched PKI (this +# header AND the tls-certs/ host material) is committed together rather than +# regenerated on demand; regenerating replaces the entire set at once. +CA_D = 0x0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0 +SERVER_D = 0x0e0d0c0b0a09080706050403020100ff0e0d0c0b0a09080706050403020100ff + +client_key = ec.derive_private_key(int.from_bytes(D, "big"), ec.SECP256R1()) +pub = client_key.public_key().public_numbers() +assert pub.x == int.from_bytes(QX, "big") and pub.y == int.from_bytes(QY, "big"), \ + "derived public key does not match identity_key_qx/qy in identity_key.h" +print("client identity pubkey matches identity_key_qx/qy: OK") + +ca_key = ec.derive_private_key(CA_D, ec.SECP256R1()) +server_key = ec.derive_private_key(SERVER_D, ec.SECP256R1()) + + +def name(cn): + return x509.Name([x509.NameAttribute(NameOID.ORGANIZATION_NAME, "wolfSSL Test"), + x509.NameAttribute(NameOID.COMMON_NAME, cn)]) + + +# Fixed validity window so the certs (and thus the DER bytes) are deterministic. +t0 = datetime.datetime(2026, 1, 1) +t1 = datetime.datetime(2036, 1, 1) + +ca = (x509.CertificateBuilder() + .subject_name(name("wolfIP Test CA")).issuer_name(name("wolfIP Test CA")) + .public_key(ca_key.public_key()).serial_number(1) + .not_valid_before(t0).not_valid_after(t1) + .add_extension(x509.BasicConstraints(ca=True, path_length=None), True) + .sign(ca_key, hashes.SHA256())) + +client_cert = (x509.CertificateBuilder() + .subject_name(name("stm32c5a3-client")).issuer_name(ca.subject) + .public_key(client_key.public_key()).serial_number(2) + .not_valid_before(t0).not_valid_after(t1) + .add_extension(x509.BasicConstraints(ca=False, path_length=None), True) + .add_extension(x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH]), False) + .sign(ca_key, hashes.SHA256())) + +server_cert = (x509.CertificateBuilder() + .subject_name(name("stm32c5a3-server")).issuer_name(ca.subject) + .public_key(server_key.public_key()).serial_number(3) + .not_valid_before(t0).not_valid_after(t1) + .add_extension(x509.BasicConstraints(ca=False, path_length=None), True) + .add_extension(x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.SERVER_AUTH]), False) + .sign(ca_key, hashes.SHA256())) + +PEM = serialization.Encoding.PEM +DER = serialization.Encoding.DER +NOENC = serialization.NoEncryption() +SEC1 = serialization.PrivateFormat.TraditionalOpenSSL # P-256 client/server keys + + +def w(fn, data): + with open(os.path.join(OUT, fn), "wb") as f: + f.write(data) + + +w("ca.pem", ca.public_bytes(PEM)) +w("client.pem", client_cert.public_bytes(PEM)) +w("client.key.pem", client_key.private_bytes(PEM, SEC1, NOENC)) +w("server.pem", server_cert.public_bytes(PEM)) +w("server.key.pem", server_key.private_bytes(PEM, SEC1, NOENC)) + +client_der = client_cert.public_bytes(DER) +client_key_der = client_key.private_bytes(DER, SEC1, NOENC) +ca_der = ca.public_bytes(DER) + + +def carr(nm, b): + out = ["static const unsigned char %s[] = {" % nm] + for i in range(0, len(b), 12): + out.append(" " + "".join("0x%02x, " % c for c in b[i:i + 12]).rstrip()) + out.append("};") + out.append("static const unsigned int %s_len = %d;" % (nm, len(b))) + return "\n".join(out) + + +hdr = """/* tls_certs.h -- GENERATED by gen_certs.py; do not edit by hand. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * TLS 1.3 mutual-auth test PKI for the STM32C5A3ZG wolfIP client. TEST ONLY. + * + * Provenance / regeneration: + * cd src/port/stm32c5a3 && python3 gen_certs.py + * Fixed CA/server scalars; client key is the NIST P-256 CAVP SigGen vector from + * identity_key.h. The host material (tls-certs/, git-ignored -- it holds a + * throwaway test key) is regenerated on demand. ECDSA cert DER is not byte- + * reproducible, so regenerating also rewrites this header: rebuild + reflash + * after regenerating. The host material feeds: + * openssl s_server -accept 11111 -tls1_3 -cert server.pem \\ + * -key server.key.pem -Verify 1 -CAfile ca.pem -www + * + * Contents below (all public material -- no live secret): + * client_cert_der -- the device client certificate (binds the P-256 + * identity public key identity_key_qx/qy). + * client_key_der -- SEC1 EC private key = the RAW identity scalar, used + * only by the software bring-up build (ENABLE_TLS_CLIENT + * without ENABLE_DHUK_KEY). The DHUK build signs through + * the crypto callback and never touches this. + * ca_cert_der -- the test CA (to verify the server, if enabled). + */ +#ifndef TLS_CERTS_H +#define TLS_CERTS_H + +%s + +%s + +%s + +#endif /* TLS_CERTS_H */ +""" % (carr("client_cert_der", client_der), + carr("client_key_der", client_key_der), + carr("ca_cert_der", ca_der)) + +with open(os.path.join(HERE, "tls_certs.h"), "w") as f: + f.write(hdr) + +print("wrote tls_certs.h and", sorted(os.listdir(OUT))) +print("client_der=%d client_key_der=%d ca_der=%d bytes" % + (len(client_der), len(client_key_der), len(ca_der))) diff --git a/src/port/stm32c5a3/identity_key.h b/src/port/stm32c5a3/identity_key.h new file mode 100644 index 00000000..8f2365d0 --- /dev/null +++ b/src/port/stm32c5a3/identity_key.h @@ -0,0 +1,61 @@ +/* identity_key.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP 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 + */ + +/* Device P-256 (secp256r1) identity keypair used by the TLS 1.3 mutual-auth + * DHUK demo (ENABLE_DHUK_KEY): the private scalar is delivered to the device + * only in DHUK-wrapped form (unwrapped inside SAES, signed on the HW PKA), and + * the public point is bound into the client certificate in tls_certs.h. + * + * For this demo the keypair is a PUBLIC NIST P-256 CAVP (FIPS 186) known-answer + * vector (US Government public domain, http://csrc.nist.gov/groups/STM/cavp/), + * so it is not a real secret. In production the wrapped identity blob is + * provisioned off-device and the plaintext scalar never exists here. If this + * keypair changes, re-run gen_certs.py so the client certificate matches. + */ +#ifndef IDENTITY_KEY_H +#define IDENTITY_KEY_H +#include + +/* Private scalar d (kept only for the software bring-up path; the DHUK build + * wraps it and signs through the callback instead). */ +const uint8_t identity_key_d[] = +{ + 0x70, 0x83, 0x09, 0xa7, 0x44, 0x9e, 0x15, 0x6b, 0x0d, 0xb7, 0x0e, 0x5b, + 0x52, 0xe6, 0x06, 0xc7, 0xe0, 0x94, 0xed, 0x67, 0x6c, 0xe8, 0x95, 0x3b, + 0xf6, 0xc1, 0x47, 0x57, 0xc8, 0x26, 0xf5, 0x90 +}; + +/* Public point Q = d*G, uncompressed (qx || qy). */ +const uint8_t identity_key_qx[] = +{ + 0x29, 0x57, 0x8c, 0x7a, 0xb6, 0xce, 0x0d, 0x11, 0x49, 0x3c, 0x95, 0xd5, + 0xea, 0x05, 0xd2, 0x99, 0xd5, 0x36, 0x80, 0x1c, 0xa9, 0xcb, 0xd5, 0x0e, + 0x99, 0x24, 0xe4, 0x3b, 0x73, 0x3b, 0x83, 0xab +}; + +const uint8_t identity_key_qy[] = +{ + 0x08, 0xc8, 0x04, 0x98, 0x79, 0xc6, 0x27, 0x8b, 0x22, 0x73, 0x34, 0x84, + 0x74, 0x15, 0x85, 0x15, 0xac, 0xca, 0xa3, 0x83, 0x44, 0x10, 0x6e, 0xf9, + 0x68, 0x03, 0xc5, 0xa0, 0x5a, 0xdc, 0x48, 0x00 +}; + +#endif /* IDENTITY_KEY_H */ diff --git a/src/port/stm32c5a3/main.c b/src/port/stm32c5a3/main.c new file mode 100644 index 00000000..f4c53b49 --- /dev/null +++ b/src/port/stm32c5a3/main.c @@ -0,0 +1,693 @@ +/* main.c + * + * STM32C5A3ZG (NUCLEO-C5A3ZG) wolfIP Echo Server + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP 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 + */ +#include +#include +#include "stm32c5a3xx.h" +#include "config.h" +#include "wolfip.h" +#include "stm32_eth.h" +#ifdef ENABLE_TLS_CLIENT +#include "tls_client.h" +#endif + +#define ECHO_PORT 7 +#define RX_BUF_SIZE 1024 + +#ifdef ENABLE_TLS_CLIENT +/* Milestone 3A.0 TLS mutual-auth client test state. */ +static int tls_client_test_started = 0; +static int tls_client_test_done = 0; +static int tls_request_sent = 0; +#endif + +/* NUCLEO-C5A3ZG: 144 MHz SYSCLK/HCLK (HSE 48 MHz -> PSI -> PSIS, prescalers /1). */ +#define BOARD_SYSCLK_HZ 144000000u + +#define HSE_TIMEOUT 0x100000u +#define PSI_TIMEOUT 0x100000u +#define SW_TIMEOUT 0x010000u + +/* ========================================================================= + * Global Variables + * ========================================================================= */ +static struct wolfIP *IPStack; +static int listen_fd = -1; +static int client_fd = -1; +static uint8_t rx_buf[RX_BUF_SIZE]; + +/* CMSIS-Core hooks called by the vendor startup. SystemInit is a no-op (clocks + * are set in clock_init); Default_IRQHandler_Hook parks on an unexpected IRQ. */ +void SystemInit(void) { /* no-op */ } + +__attribute__((weak)) void Default_IRQHandler_Hook(void) +{ + while (1) { } +} + +/* Simple busy-wait delay. */ +static void delay(uint32_t count) +{ + volatile uint32_t i; + for (i = 0; i < count; i++) { } +} + +/* ========================================================================= + * Clock init: HSE 48 MHz -> PSI -> PSIS = 144 MHz SYSCLK + * + * Lifted verbatim from the proven wolfBoot/STM32_Bare_Test C5A3 port: + * HSE on, PSIREFSRC=HSE, PSIREF=48 MHz, PSIFREQ=144 MHz, enable PSIS, all + * bus prescalers /1, flash 4 WS + prefetch + WRHIGHFREQ delay 2, switch + * SYSCLK to PSIS. CK48 (the RNG kernel clock) is sourced from HSIDIV3 = + * HSI/3 = 48 MHz (needed for the HW RNG later; harmless now). On any + * timeout we leave the chip on its reset clock so boot still proceeds. + * ========================================================================= */ +static void clock_init(void) +{ + uint32_t reg; + volatile uint32_t timeout; + + /* RNG kernel clock = CK48 (RCC_CCIPR2.CK48SEL), sourced from HSIDIV3 + * (HSI / 3 = 48 MHz). Validated on silicon. */ + RCC->CR1 |= RCC_CR1_HSIDIV3ON; + timeout = HSE_TIMEOUT; + while (((RCC->CR1 & RCC_CR1_HSIDIV3RDY) == 0u) && (--timeout != 0u)) { + /* spin */ + } + reg = RCC->CCIPR2 & ~RCC_CCIPR2_CK48SEL_Msk; + reg |= RCC_CCIPR2_CK48SEL_1; /* CK48 source = HSIDIV3 (HSI/3 = 48 MHz) */ + RCC->CCIPR2 = reg; + + /* 1. Enable HSE (48 MHz on NUCLEO-C5A3ZG soldered crystal). */ + if ((RCC->CR1 & RCC_CR1_HSEON) == 0u) { + RCC->CR1 |= RCC_CR1_HSEON; + } + timeout = HSE_TIMEOUT; + while (((RCC->CR1 & RCC_CR1_HSERDY) == 0u) && (--timeout != 0u)) { + /* spin */ + } + if (timeout == 0u) { + return; + } + + /* 2. Configure PSI: ref source = HSE (PSIREFSRC=00), ref = 48 MHz + * (PSIREF = 110b), output = 144 MHz (PSIFREQ = 01b). */ + reg = RCC->CR2 & ~(RCC_CR2_PSIREFSRC | RCC_CR2_PSIREF | + RCC_CR2_PSIFREQ); + reg |= (0u) /* PSIREFSRC = HSE */ + | (RCC_CR2_PSIREF_2 | RCC_CR2_PSIREF_1) /* PSIREF = 48 MHz */ + | (RCC_CR2_PSIFREQ_0); /* PSIFREQ = 144 MHz */ + RCC->CR2 = reg; + + /* 3. Enable PSIS, wait for ready. */ + RCC->CR1 |= RCC_CR1_PSISON; + timeout = PSI_TIMEOUT; + while (((RCC->CR1 & RCC_CR1_PSISRDY) == 0u) && (--timeout != 0u)) { + /* spin */ + } + if (timeout == 0u) { + return; + } + + /* 4. All bus prescalers /1. Reset value already 0; explicit for intent. */ + RCC->CFGR2 = 0u; + + /* 5. Flash 4 WS + prefetch BEFORE switching SYSCLK to 144 MHz. */ + reg = FLASH->ACR & ~FLASH_ACR_LATENCY; + reg |= (4u << FLASH_ACR_LATENCY_Pos) | FLASH_ACR_PRFTEN; + FLASH->ACR = reg; + while ((FLASH->ACR & FLASH_ACR_LATENCY) != + (4u << FLASH_ACR_LATENCY_Pos)) { + /* spin */ + } + + /* 6. Switch SYSCLK to PSIS (SW = 11b). */ + reg = RCC->CFGR1 & ~RCC_CFGR1_SW; + RCC->CFGR1 = reg | (RCC_CFGR1_SW_0 | RCC_CFGR1_SW_1); + timeout = SW_TIMEOUT; + while (((RCC->CFGR1 & RCC_CFGR1_SWS) != + (RCC_CFGR1_SWS_0 | RCC_CFGR1_SWS_1)) && (--timeout != 0u)) { + /* spin */ + } + if (timeout == 0u) { + return; + } + + /* 7. Programming delay 2 (required at HCLK >= 136 MHz). */ + reg = FLASH->ACR & ~FLASH_ACR_WRHIGHFREQ; + FLASH->ACR = reg | (2u << FLASH_ACR_WRHIGHFREQ_Pos); +} + +/* ========================================================================= + * USART2 init: PA2 (TX) / PA3 (RX), AF7 (ST-LINK V3 VCP) + * + * Lifted verbatim from the proven C5A3 port. PCLK1 = SYSCLK = 144 MHz + * after clock_init(), so BRR = 144000000 / 115200. + * ========================================================================= */ +static void uart_init(void) +{ + /* Enable GPIOA on AHB2 */ + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; + (void)RCC->AHB2ENR; + + /* PA2 (TX), PA3 (RX): MODER=AF (10b), AF7 (USART2) */ + GPIOA->MODER &= ~(GPIO_MODER_MODE2_Msk | GPIO_MODER_MODE3_Msk); + GPIOA->MODER |= (2u << GPIO_MODER_MODE2_Pos) | + (2u << GPIO_MODER_MODE3_Pos); + GPIOA->OSPEEDR |= (3u << GPIO_OSPEEDR_OSPEED2_Pos) | + (3u << GPIO_OSPEEDR_OSPEED3_Pos); + GPIOA->AFR[0] &= ~((0xFu << GPIO_AFRL_AFSEL2_Pos) | + (0xFu << GPIO_AFRL_AFSEL3_Pos)); + GPIOA->AFR[0] |= (7u << GPIO_AFRL_AFSEL2_Pos) | + (7u << GPIO_AFRL_AFSEL3_Pos); + GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPD2_Msk | GPIO_PUPDR_PUPD3_Msk); + + /* Enable USART2 clock (APB1L bit USART2EN) */ + RCC->APB1LENR |= RCC_APB1LENR_USART2EN; + (void)RCC->APB1LENR; + + /* USART2: 8N1, oversample 16. */ + USART2->CR1 = 0; + USART2->BRR = BOARD_SYSCLK_HZ / 115200u; + USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; + + while ((USART2->ISR & (USART_ISR_TEACK | USART_ISR_REACK)) != + (USART_ISR_TEACK | USART_ISR_REACK)) { + /* spin */ + } +} + +/* ========================================================================= + * UART helpers + * ========================================================================= */ +static void uart_putc(char c) +{ + while ((USART2->ISR & USART_ISR_TXE_TXFNF) == 0u) { } + USART2->TDR = (uint32_t)c & 0xFFu; +} + +static void uart_puts(const char *s) +{ + while (*s) { + if (*s == '\n') uart_putc('\r'); + uart_putc(*s++); + } +} + +static void uart_puthex(uint32_t val) +{ + const char hex[] = "0123456789ABCDEF"; + int i; + uart_puts("0x"); + for (i = 28; i >= 0; i -= 4) { + uart_putc(hex[(val >> i) & 0xF]); + } +} + +static void uart_putdec(uint32_t val) +{ + char buf[12]; + int i = 0; + if (val == 0) { + uart_putc('0'); + return; + } + while (val > 0 && i < 11) { + buf[i++] = (char)('0' + (val % 10)); + val /= 10; + } + while (i > 0) { + uart_putc(buf[--i]); + } +} + +static void uart_putip4(ip4 ip) +{ + uart_putdec((ip >> 24) & 0xFF); + uart_putc('.'); + uart_putdec((ip >> 16) & 0xFF); + uart_putc('.'); + uart_putdec((ip >> 8) & 0xFF); + uart_putc('.'); + uart_putdec(ip & 0xFF); +} + +/* ========================================================================= + * Fault reporting (override the weak while(1) HardFault in startup.c) so a + * crash prints the fault registers + stacked PC/LR instead of hanging + * silently. __StackTop/__StackLimit come from target.ld. + * ========================================================================= */ +extern uint32_t __StackLimit; +void hardfault_report(uint32_t *sp) +{ + uart_puts("\n!! HARDFAULT !!\n"); + uart_puts(" stacked PC = "); uart_puthex(sp[6]); uart_puts("\n"); + uart_puts(" stacked LR = "); uart_puthex(sp[5]); uart_puts("\n"); + uart_puts(" CFSR = "); uart_puthex(*(volatile uint32_t *)0xE000ED28u); uart_puts("\n"); + uart_puts(" HFSR = "); uart_puthex(*(volatile uint32_t *)0xE000ED2Cu); uart_puts("\n"); + uart_puts(" MMFAR= "); uart_puthex(*(volatile uint32_t *)0xE000ED34u); uart_puts("\n"); + uart_puts(" SP = "); uart_puthex((uint32_t)sp); uart_puts("\n"); + uart_puts(" StackLimit = "); uart_puthex((uint32_t)&__StackLimit); uart_puts("\n"); + while (1) { } +} + +__attribute__((naked)) void HardFault_Handler(void) +{ + __asm volatile( + "tst lr, #4 \n" + "ite eq \n" + "mrseq r0, msp \n" + "mrsne r0, psp \n" + "b hardfault_report \n" + ); +} + +/* ========================================================================= + * RNG (required by wolfIP for TCP ISNs) + * + * The HW RNG kernel clock (CK48) was set up in clock_init(). Enable the + * RNG clock + peripheral here. wolfIP_getrandom falls back to a runtime- + * seeded LFSR if the HW RNG is unavailable. + * ========================================================================= */ +static void rng_init(void) +{ + /* Enable RNG peripheral clock (AHB2). */ + RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; + (void)RCC->AHB2ENR; + delay(100); + /* CR.CED=1 disables the clock-error detector, which on C5 silicon + * false-trips on the 48 MHz CK48 clock and stalls DRDY. */ + RNG->CR = RNG_CR_RNGEN | RNG_CR_CED; +} + +static int rng_get_word(uint32_t *out) +{ + uint32_t timeout = 100000u; + while ((RNG->SR & RNG_SR_DRDY) == 0u) { + if ((RNG->SR & (RNG_SR_CECS | RNG_SR_SECS)) != 0u) { + RNG->CR = 0; + delay(100); + RNG->CR = RNG_CR_RNGEN | RNG_CR_CED; + timeout = 100000u; + } + if (--timeout == 0u) { + return -1; + } + } + *out = RNG->DR; + return 0; +} + +uint32_t wolfIP_getrandom(void) +{ + uint32_t val; + if (rng_get_word(&val) == 0) { + return val; + } + /* HW RNG failed: fall back to a runtime-seeded xorshift LFSR so a + * degraded device does not emit a globally-identical sequence. Not a + * cryptographic RNG. */ + { + static uint32_t lfsr = 0u; + if (lfsr == 0u) { + lfsr = RNG->DR ^ 0x1A2B3C4DU; + if (lfsr == 0u) { + lfsr = 0x1A2B3C4DU; + } + } + lfsr ^= lfsr << 13; + lfsr ^= lfsr >> 17; + lfsr ^= lfsr << 5; + return lfsr; + } +} + +/* ========================================================================= + * Ethernet GPIO / SYSCFG (SBS) / RMII configuration + * + * NUCLEO-C5A3ZG RMII pinout (all AF10 = ETH1): + * PA1 - ETH_REF_CLK + * PC1 - ETH_MDC + * PE12 - ETH_MDIO (board MDIO route; PA2 is the USART2 console TX) + * PD1 - ETH_CRS_DV + * PC4 - ETH_RXD0 + * PC5 - ETH_RXD1 + * PG11 - ETH_TX_EN + * PG13 - ETH_TXD0 + * PG12 - ETH_TXD1 + * ========================================================================= */ + +/* Configure one GPIO as an Ethernet alternate-function pin. The ETH AF + * number is NOT uniform on the C5A3: most RMII signals are AF10, but + * RXD0 (PC4) is AF12 and RXD1 (PC5) is AF13 (per the NUCLEO-C5A3ZG + * CubeC5 ETH example). Passing the wrong AF leaves the pin disconnected + * from the MAC -- e.g. AF10 on the RXD pins silently kills the RX path + * (link stays up via MDIO, but no frames are received). */ +static void gpio_eth_pin(GPIO_TypeDef *port, uint32_t pin, uint32_t af) +{ + uint32_t pos2 = pin * 2u; + + /* Mode = alternate function (0b10) */ + port->MODER &= ~(3u << pos2); + port->MODER |= (2u << pos2); + + /* Very-high speed (0b11) */ + port->OSPEEDR |= (3u << pos2); + + if (pin < 8u) { + port->AFR[0] &= ~(0xFu << (pin * 4u)); + port->AFR[0] |= (af << (pin * 4u)); + } else { + port->AFR[1] &= ~(0xFu << ((pin - 8u) * 4u)); + port->AFR[1] |= (af << ((pin - 8u) * 4u)); + } +} + +static void eth_gpio_init(void) +{ + uint32_t val; + + /* Enable GPIO port clocks: A, C, D, E, G (AHB2ENR). */ + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOCEN | + RCC_AHB2ENR_GPIODEN | RCC_AHB2ENR_GPIOEEN | + RCC_AHB2ENR_GPIOGEN; + + /* Enable SBS (System Bus / SYSCFG) clock for RMII select. */ + RCC->APB3ENR |= RCC_APB3ENR_SBSEN; + (void)RCC->APB3ENR; + delay(1000); + + /* Select RMII mode: SBS_PMCR.ETH1_SEL_PHY = 100b (RMII). + * This is the C5 analog of the H5 SBS EPIS field and the H7 + * SYSCFG_PMCR EPIS field. */ + val = SBS->PMCR; + val &= ~SBS_PMCR_ETH1_SEL_PHY_Msk; + val |= SBS_PMCR_ETH1_SEL_PHY_2; /* 0x04 << 24 = RMII */ + SBS->PMCR = val; + (void)SBS->PMCR; + __asm volatile ("dsb sy" ::: "memory"); + delay(1000); + + /* Configure RMII pins. Most are AF10; RXD0/RXD1 are AF12/AF13. */ + gpio_eth_pin(GPIOA, 1, 10); /* REF_CLK */ + gpio_eth_pin(GPIOC, 1, 10); /* MDC */ + gpio_eth_pin(GPIOE, 12, 10); /* MDIO */ + gpio_eth_pin(GPIOD, 1, 10); /* CRS_DV */ + gpio_eth_pin(GPIOC, 4, 12); /* RXD0 (AF12) */ + gpio_eth_pin(GPIOC, 5, 13); /* RXD1 (AF13) */ + gpio_eth_pin(GPIOG, 11, 10); /* TX_EN */ + gpio_eth_pin(GPIOG, 13, 10); /* TXD0 */ + gpio_eth_pin(GPIOG, 12, 10); /* TXD1 */ +} + +/* ========================================================================= + * TLS Client Callback (milestone 3A.0) + * ========================================================================= */ +#ifdef ENABLE_TLS_CLIENT +static void tls_response_cb(const char *data, int len, void *ctx) +{ + int i; + (void)ctx; + uart_puts("TLS Client received "); + uart_putdec((uint32_t)len); + uart_puts(" bytes:\n"); + + /* Print up to the first 200 bytes of the server response. */ + for (i = 0; (i < len) && (i < 200); i++) { + uart_putc(data[i]); + } + if (len > 200) { + uart_puts("\n... (truncated)\n"); + } + uart_puts("\n"); + tls_client_test_done = 1; +} +#endif + +/* ========================================================================= + * TCP Echo Server Callback + * ========================================================================= */ +static void echo_cb(int fd, uint16_t event, void *arg) +{ + struct wolfIP *s = (struct wolfIP *)arg; + int ret; + + if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { + client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd > 0) { + wolfIP_register_callback(s, client_fd, echo_cb, s); + } + return; + } + + if ((fd == client_fd) && (event & CB_EVENT_READABLE)) { + ret = wolfIP_sock_recvfrom(s, client_fd, rx_buf, sizeof(rx_buf), 0, NULL, NULL); + if (ret > 0) { + (void)wolfIP_sock_sendto(s, client_fd, rx_buf, (uint32_t)ret, 0, NULL, 0); + } else if (ret == 0) { + wolfIP_sock_close(s, client_fd); + client_fd = -1; + } + } + + if ((fd == client_fd) && (event & CB_EVENT_CLOSED)) { + wolfIP_sock_close(s, client_fd); + client_fd = -1; + } +} + +/* ========================================================================= + * Main + * ========================================================================= */ +int main(void) +{ + struct wolfIP_ll_dev *ll; + struct wolfIP_sockaddr_in addr; + uint64_t tick = 0; + int ret; + + /* Enable FPU CP10/CP11 full access (Cortex-M33F). */ + SCB->CPACR |= (0xFu << 20); + __DSB(); + __ISB(); + + /* System clock: HSE + PSI -> 144 MHz SYSCLK / HCLK. */ + clock_init(); + + /* USART2 console (ST-LINK V3 VCP). */ + uart_init(); + + uart_puts("\n\n=== wolfIP STM32C5A3ZG Echo Server ===\n"); + uart_puts("Clock: 144 MHz SYSCLK (HSE 48 MHz -> PSI)\n"); + + /* Hardware RNG (kernel clock CK48 set up in clock_init). */ + rng_init(); + { + uint32_t rng_test; + int rng_ok = rng_get_word(&rng_test); + uart_puts("RNG init: "); + uart_puts(rng_ok == 0 ? "OK" : "FAILED"); + if (rng_ok == 0) { + uart_puts(" val="); + uart_puthex(rng_test); + } + uart_puts("\n"); + } + + uart_puts("Initializing wolfIP stack...\n"); + wolfIP_init_static(&IPStack); + + /* Step 1: Configure GPIO + SBS RMII mode BEFORE enabling ETH clocks. */ + uart_puts("Configuring GPIO for RMII...\n"); + eth_gpio_init(); + + /* Step 2: Wait for the PHY REF_CLK to stabilize after board reset. + * The MAC needs REF_CLK for register access. */ + delay(2500000); /* ~200ms */ + + /* Step 3: Enable ETH clocks: ETH1EN + ETH1TXEN + ETH1RXEN + ETH1CKEN. */ + uart_puts("Enabling Ethernet clocks...\n"); + RCC->AHB1ENR |= RCC_AHB1ENR_ETH1EN | RCC_AHB1ENR_ETH1TXEN | + RCC_AHB1ENR_ETH1RXEN | RCC_AHB1ENR_ETH1CKEN; + __asm volatile ("dsb sy" ::: "memory"); + delay(12500); /* ~1ms */ + + /* Step 4: RCC reset of the ETH MAC (AHB1RSTR.ETH1RST). */ + uart_puts("Resetting Ethernet MAC...\n"); + RCC->AHB1RSTR |= RCC_AHB1RSTR_ETH1RST; + __asm volatile ("dsb sy" ::: "memory"); + delay(12500); + RCC->AHB1RSTR &= ~RCC_AHB1RSTR_ETH1RST; + __asm volatile ("dsb sy" ::: "memory"); + delay(125000); /* ~10ms post-reset stabilization */ + + uart_puts("Initializing Ethernet MAC...\n"); + ll = wolfIP_getdev(IPStack); + ret = stm32_eth_init(ll, NULL); + if (ret < 0) { + uart_puts(" ERROR: stm32_eth_init failed ("); + uart_puthex((uint32_t)ret); + uart_puts(")\n"); + } else { + uart_puts(" PHY link: "); + uart_puts((ret & 0x100) ? "UP" : "DOWN"); + uart_puts(", PHY addr: "); + uart_puthex(ret & 0xFF); + uart_puts("\n"); + } + +#ifdef DHCP + { + uint32_t dhcp_start_tick; + uint32_t dhcp_timeout = 5000; + + /* Prime the stack's last_tick before starting the DHCP client, so + * the first DHCP timeout delta is sane (a zero last_tick makes the + * client mis-time its DISCOVER retransmits). */ + (void)wolfIP_poll(IPStack, tick++); + + ret = dhcp_client_init(IPStack); + if (ret >= 0) { + uart_puts("Waiting for DHCP...\n"); + dhcp_start_tick = (uint32_t)tick; + while (!dhcp_bound(IPStack) && dhcp_client_is_running(IPStack)) { + uint32_t elapsed = (uint32_t)tick - dhcp_start_tick; + (void)wolfIP_poll(IPStack, tick); + tick++; + delay(100000); /* ~8ms per poll */ + if (elapsed > dhcp_timeout) + break; + } + if (dhcp_bound(IPStack)) { + ip4 ip = 0, nm = 0, gw = 0; + wolfIP_ipconfig_get(IPStack, &ip, &nm, &gw); + uart_puts("DHCP configuration received:\n"); + uart_puts(" IP: "); + uart_putip4(ip); + uart_puts("\n Mask: "); + uart_putip4(nm); + uart_puts("\n GW: "); + uart_putip4(gw); + uart_puts("\n"); + } else { + ip4 ip = atoip4(WOLFIP_IP); + ip4 nm = atoip4(WOLFIP_NETMASK); + ip4 gw = atoip4(WOLFIP_GW); + uart_puts(" DHCP timeout - falling back to static IP\n"); + uart_puts(" IP: "); + uart_putip4(ip); + uart_puts("\n Mask: "); + uart_putip4(nm); + uart_puts("\n GW: "); + uart_putip4(gw); + uart_puts("\n"); + wolfIP_ipconfig_set(IPStack, ip, nm, gw); + } + } + } +#else + { + ip4 ip = atoip4(WOLFIP_IP); + ip4 nm = atoip4(WOLFIP_NETMASK); + ip4 gw = atoip4(WOLFIP_GW); + uart_puts("Using static IP configuration:\n"); + uart_puts(" IP: "); + uart_putip4(ip); + uart_puts("\n Mask: "); + uart_putip4(nm); + uart_puts("\n GW: "); + uart_putip4(gw); + uart_puts("\n"); + wolfIP_ipconfig_set(IPStack, ip, nm, gw); + } +#endif + + /* Create TCP echo server on port 7 */ + uart_puts("Creating TCP echo server on port 7...\n"); + listen_fd = wolfIP_sock_socket(IPStack, AF_INET, IPSTACK_SOCK_STREAM, 0); + wolfIP_register_callback(IPStack, listen_fd, echo_cb, IPStack); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(ECHO_PORT); + addr.sin_addr.s_addr = 0; + (void)wolfIP_sock_bind(IPStack, listen_fd, (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + (void)wolfIP_sock_listen(IPStack, listen_fd, 1); + +#ifdef ENABLE_TLS_CLIENT + /* Milestone 3A.0: bring up the TLS 1.3 mutual-auth client. The device + * loads its client cert + SEC1 identity key inside tls_client_init(). */ + uart_puts("Initializing TLS client (mutual auth)...\n"); + if (tls_client_init(IPStack, uart_puts) < 0) { + uart_puts("ERROR: TLS client init failed\n"); + } +#endif + + uart_puts("Entering main loop. Ready for connections!\n"); + uart_puts(" TCP Echo: port 7\n"); +#ifdef ENABLE_TLS_CLIENT + uart_puts(" TLS Client: will connect after network settles\n"); +#endif + + for (;;) { + (void)wolfIP_poll(IPStack, tick++); + delay(100000); /* ~8ms per tick */ + +#ifdef ENABLE_TLS_CLIENT + /* Kick off the TLS connection once the stack has settled. */ + if (!tls_client_test_started && tick > 250) { + uart_puts("\n--- TLS Client Test (3A.0 mutual auth) ---\n"); + uart_puts("Target: "); + uart_puts(TLS_SERVER_IP); + uart_puts(":"); + uart_putdec(TLS_SERVER_PORT); + uart_puts("\n"); + + if (tls_client_connect(TLS_SERVER_IP, TLS_SERVER_PORT, + tls_response_cb, NULL) == 0) { + uart_puts("TLS Client: Connection initiated\n"); + } else { + uart_puts("TLS Client: Failed to start connection\n"); + } + tls_client_test_started = 1; + } + + /* Drive the TLS client state machine. */ + tls_client_poll(); + + /* Send a short request once the handshake completes. */ + if (tls_client_is_connected() && !tls_client_test_done && + !tls_request_sent) { + const char *req = "GET / \r\n"; + uart_puts("TLS Client: Sending request...\n"); + if (tls_client_send(req, (int)strlen(req)) > 0) { + uart_puts("TLS Client: Request sent\n"); + } else { + uart_puts("TLS Client: Send failed\n"); + } + tls_request_sent = 1; + } +#endif + } + + return 0; +} diff --git a/src/port/stm32c5a3/startup.c b/src/port/stm32c5a3/startup.c new file mode 100644 index 00000000..dc1e374a --- /dev/null +++ b/src/port/stm32c5a3/startup.c @@ -0,0 +1,340 @@ +/** + ****************************************************************************** + * @file startup_stm32c5a3xx.c + * @brief Startup File for STM32C5A3xx Devices + ****************************************************************************** + * @attention + * + * Copyright (c) 2026 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32c5a3xx.h" + +#ifdef __cplusplus +#error "startup_stm32c5a3xx.c file cannot be compiled with C++ compiler" +#endif /* __cplusplus */ + +/* External References -------------------------------------------------------*/ +extern void SystemInit(void); +extern uint32_t __INITIAL_SP; +extern uint32_t __STACK_LIMIT; + +extern __NO_RETURN void __PROGRAM_START(void); + +/* Private function prototypes -----------------------------------------------*/ +/* ISR headers */ +__NO_RETURN void Reset_Handler(void) __attribute__((weak)); + void Default_IRQHandler(void); +__NO_RETURN void Default_IRQHandler_Hook(void) __attribute__((weak)); + +/* Cortex-M interrupts */ +void NMI_Handler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void HardFault_Handler (void) __attribute__((weak)); +void MemManage_Handler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void BusFault_Handler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void UsageFault_Handler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void SVC_Handler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void DebugMon_Handler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void PendSV_Handler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void SysTick_Handler (void) __attribute__((weak, alias("Default_IRQHandler"))); + +/* Device interrupts */ +void WWDG_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void PWR_PVD_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void RTC_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TAMP_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void RAMCFG_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void FLASH_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void RCC_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI0_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI2_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI3_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI4_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI5_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI6_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI7_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI8_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI9_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI10_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI11_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI12_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI13_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI14_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void EXTI15_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA1_CH0_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA1_CH1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA1_CH2_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA1_CH3_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA1_CH4_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA1_CH5_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA1_CH6_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA1_CH7_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void IWDG_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void ADC1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void ADC2_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void FDCAN1_IT0_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void FDCAN1_IT1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM1_BRK_TERR_IERR_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM1_UPD_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM1_TRGI_COM_DIR_IDX_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM1_CC_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM2_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM5_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM6_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM7_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void I2C1_EV_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void I2C1_ERR_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void I3C1_EV_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void I3C1_ERR_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void SPI1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void SPI2_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void SPI3_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void USART1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void USART2_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void USART3_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void UART4_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void UART5_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPUART1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPTIM1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM12_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM15_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM16_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM17_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void USB_DRD_FS_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void CRS_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void RNG_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void FPU_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void ICACHE_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void CORDIC_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void AES_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void HASH_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void I2C2_EV_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void I2C2_ERR_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM8_BRK_TERR_IERR_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM8_UPD_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM8_TRGI_COM_DIR_IDX_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM8_CC_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void COMP1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void DAC1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA2_CH0_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA2_CH1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA2_CH2_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA2_CH3_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA2_CH4_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA2_CH5_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA2_CH6_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void LPDMA2_CH7_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void FDCAN2_IT0_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void FDCAN2_IT1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM3_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void TIM4_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void XSPI1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void SAES_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void PKA_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void ETH1_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void ETH1_WKUP_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void USART6_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void UART7_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); +void ADC3_IRQHandler (void) __attribute__((weak, alias("Default_IRQHandler"))); + +/* Private variables ---------------------------------------------------------*/ +/** + Vector table + */ + +#if defined ( __GNUC__ ) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#endif /* __GNUC__ */ + +extern const VECTOR_TABLE_Type __VECTOR_TABLE[]; +const VECTOR_TABLE_Type __VECTOR_TABLE[] __VECTOR_TABLE_ATTRIBUTE = +{ + (VECTOR_TABLE_Type)(&__INITIAL_SP), /* The initial stack pointer */ + Reset_Handler, + NMI_Handler, + HardFault_Handler, + MemManage_Handler, + BusFault_Handler, + UsageFault_Handler, + 0, + 0, + 0, + 0, + SVC_Handler, + DebugMon_Handler, + 0, + PendSV_Handler, + SysTick_Handler, + WWDG_IRQHandler, + PWR_PVD_IRQHandler, + RTC_IRQHandler, + TAMP_IRQHandler, + RAMCFG_IRQHandler, + FLASH_IRQHandler, + RCC_IRQHandler, + EXTI0_IRQHandler, + EXTI1_IRQHandler, + EXTI2_IRQHandler, + EXTI3_IRQHandler, + EXTI4_IRQHandler, + EXTI5_IRQHandler, + EXTI6_IRQHandler, + EXTI7_IRQHandler, + EXTI8_IRQHandler, + EXTI9_IRQHandler, + EXTI10_IRQHandler, + EXTI11_IRQHandler, + EXTI12_IRQHandler, + EXTI13_IRQHandler, + EXTI14_IRQHandler, + EXTI15_IRQHandler, + LPDMA1_CH0_IRQHandler, + LPDMA1_CH1_IRQHandler, + LPDMA1_CH2_IRQHandler, + LPDMA1_CH3_IRQHandler, + LPDMA1_CH4_IRQHandler, + LPDMA1_CH5_IRQHandler, + LPDMA1_CH6_IRQHandler, + LPDMA1_CH7_IRQHandler, + IWDG_IRQHandler, + ADC1_IRQHandler, + ADC2_IRQHandler, + FDCAN1_IT0_IRQHandler, + FDCAN1_IT1_IRQHandler, + TIM1_BRK_TERR_IERR_IRQHandler, + TIM1_UPD_IRQHandler, + TIM1_TRGI_COM_DIR_IDX_IRQHandler, + TIM1_CC_IRQHandler, + TIM2_IRQHandler, + TIM5_IRQHandler, + TIM6_IRQHandler, + TIM7_IRQHandler, + I2C1_EV_IRQHandler, + I2C1_ERR_IRQHandler, + I3C1_EV_IRQHandler, + I3C1_ERR_IRQHandler, + SPI1_IRQHandler, + SPI2_IRQHandler, + SPI3_IRQHandler, + USART1_IRQHandler, + USART2_IRQHandler, + USART3_IRQHandler, + UART4_IRQHandler, + UART5_IRQHandler, + LPUART1_IRQHandler, + LPTIM1_IRQHandler, + TIM12_IRQHandler, + TIM15_IRQHandler, + TIM16_IRQHandler, + TIM17_IRQHandler, + USB_DRD_FS_IRQHandler, + CRS_IRQHandler, + RNG_IRQHandler, + FPU_IRQHandler, + ICACHE_IRQHandler, + CORDIC_IRQHandler, + AES_IRQHandler, + HASH_IRQHandler, + I2C2_EV_IRQHandler, + I2C2_ERR_IRQHandler, + TIM8_BRK_TERR_IERR_IRQHandler, + TIM8_UPD_IRQHandler, + TIM8_TRGI_COM_DIR_IDX_IRQHandler, + TIM8_CC_IRQHandler, + COMP1_IRQHandler, + DAC1_IRQHandler, + LPDMA2_CH0_IRQHandler, + LPDMA2_CH1_IRQHandler, + LPDMA2_CH2_IRQHandler, + LPDMA2_CH3_IRQHandler, + LPDMA2_CH4_IRQHandler, + LPDMA2_CH5_IRQHandler, + LPDMA2_CH6_IRQHandler, + LPDMA2_CH7_IRQHandler, + FDCAN2_IT0_IRQHandler, + FDCAN2_IT1_IRQHandler, + 0, + TIM3_IRQHandler, + TIM4_IRQHandler, + XSPI1_IRQHandler, + SAES_IRQHandler, + PKA_IRQHandler, + ETH1_IRQHandler, + ETH1_WKUP_IRQHandler, + USART6_IRQHandler, + UART7_IRQHandler, + ADC3_IRQHandler +}; + +#if defined ( __GNUC__ ) +#pragma GCC diagnostic pop +#endif /* __GNUC__ */ + +/* Private functions ---------------------------------------------------------*/ + +/** + * @brief This function is the Reset Handler called on controller reset. + * @param None + * @retval None + */ +__WEAK __NO_RETURN void Reset_Handler(void) +{ + __set_PSP((uint32_t)(&__INITIAL_SP)); + + __set_MSPLIM((uint32_t)(&__STACK_LIMIT)); + __set_PSPLIM((uint32_t)(&__STACK_LIMIT)); + + SystemInit(); /* CMSIS System Initialization */ + __PROGRAM_START(); /* Enter PreMain (C library entry point) */ +} + +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" +#endif /* __ARMCC_VERSION */ + +/** + * @brief Hard Fault Handler. + * @param None + * @retval None + */ +__WEAK void HardFault_Handler(void) +{ + while (1); +} + +/** + * @brief This function is the default IRQ handler + * when the IRQ line is not used by the application. + * @param None + * @retval None + */ +void Default_IRQHandler(void) +{ + Default_IRQHandler_Hook(); +} + +/** + * @brief This function is the default IRQHandler implementation hook + * could be overridden by the application. + * @param None + * @retval None + */ +__WEAK void Default_IRQHandler_Hook(void) +{ + while (1); +} + +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) +#pragma clang diagnostic pop +#endif /* __ARMCC_VERSION */ diff --git a/src/port/stm32c5a3/syscalls.c b/src/port/stm32c5a3/syscalls.c new file mode 100644 index 00000000..ab6af8cc --- /dev/null +++ b/src/port/stm32c5a3/syscalls.c @@ -0,0 +1,144 @@ +/* syscalls.c + * + * Newlib system call stubs for STM32C5A3ZG bare-metal + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + */ +#include +#include +#include +#include +#include +#include + +/* Use the linker's dedicated .heap region. Do NOT grow the newlib heap from + * _ebss toward _estack: the linker places the .eth_buffers (GMAC DMA + * descriptors + RX/TX buffers), .heap and .stack sections right after _ebss, + * so an _ebss..._estack heap would hand out malloc() memory that overlaps the + * Ethernet DMA buffers -- the MAC then writes received packets on top of + * malloc'd objects (e.g. the WOLFSSL_CTX), corrupting them. */ +extern char _heap_start; +extern char _heap_end; + +static char *heap_end; + +/* USART2 TX register for printf output (ST-LINK VCP on NUCLEO-C5A3ZG) */ +#define USART2_BASE_ADDR 0x40004400UL +#define USART2_ISR_REG (*(volatile uint32_t *)(USART2_BASE_ADDR + 0x1C)) +#define USART2_TDR_REG (*(volatile uint32_t *)(USART2_BASE_ADDR + 0x28)) +#define USART_ISR_TXFNF (1u << 7) /* TXE / TX FIFO not full */ + +int _write(int file, const char *ptr, int len) +{ + int i; + (void)file; + for (i = 0; i < len; i++) { + if (ptr[i] == '\n') { + while ((USART2_ISR_REG & USART_ISR_TXFNF) == 0) { } + USART2_TDR_REG = '\r'; + } + while ((USART2_ISR_REG & USART_ISR_TXFNF) == 0) { } + USART2_TDR_REG = (uint32_t)ptr[i]; + } + return len; +} + +int _close(int file) +{ + (void)file; + return -1; +} + +int _fstat(int file, struct stat *st) +{ + (void)file; + if (st == 0) { + errno = EINVAL; + return -1; + } + st->st_mode = S_IFCHR; + return 0; +} + +int _isatty(int file) +{ + (void)file; + return 1; +} + +int _lseek(int file, int ptr, int dir) +{ + (void)file; + (void)ptr; + (void)dir; + return 0; +} + +int _read(int file, char *ptr, int len) +{ + (void)file; + (void)ptr; + (void)len; + return 0; +} + +void *_sbrk(ptrdiff_t incr) +{ + char *prev; + if (heap_end == 0) { + heap_end = &_heap_start; + } + prev = heap_end; + if ((heap_end + incr) > &_heap_end) { + errno = ENOMEM; + return (void *)-1; + } + heap_end += incr; + return prev; +} + +int _gettimeofday(struct timeval *tv, void *tzvp) +{ + (void)tzvp; + if (tv == 0) { + errno = EINVAL; + return -1; + } + tv->tv_sec = 0; + tv->tv_usec = 0; + return 0; +} + +time_t time(time_t *t) +{ + /* Fixed time for certificate validation. Without a real RTC, this + * ensures ASN time checks pass once TLS is added. */ + time_t now = 1771200000; /* approx 2026-02-16 */ + if (t != 0) { + *t = now; + } + return now; +} + +void _exit(int status) +{ + (void)status; + while (1) { + __asm volatile("wfi"); + } +} + +int _kill(int pid, int sig) +{ + (void)pid; + (void)sig; + errno = EINVAL; + return -1; +} + +int _getpid(void) +{ + return 1; +} diff --git a/src/port/stm32c5a3/target.ld b/src/port/stm32c5a3/target.ld new file mode 100644 index 00000000..9ace9741 --- /dev/null +++ b/src/port/stm32c5a3/target.ld @@ -0,0 +1,210 @@ +/** +****************************************************************************** +* @file stm32c5a3xg_flash.ld +* @brief Linker File +****************************************************************************** +* @attention +* +* Copyright (c) 2026 STMicroelectronics. +* All rights reserved. +* +* This software is licensed under terms that can be found in the LICENSE file +* in the root directory of this software component. +* If no LICENSE file comes with this software, it is provided AS-IS. +* +****************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +HEAP_SIZE = 0x10000; /* 64K heap - TLS 1.3 + WOLFSSL_SMALL_STACK temporaries */ +STACK_SIZE = 0x10000; /* 64K stack - TLS 1.3 / SP-math ECC call depth */ + +/* Aliases for STM32_Bare_Test STACK=1 adapter -- see board_common.c. */ +_Min_Heap_Size = HEAP_SIZE; +_Min_Stack_Size = STACK_SIZE; + + +MEMORY +{ + ROM (rx) : org = 0x8000000, len = 0x100000 + RAM (xrw) : org = 0x20000000, len = 0x40000 +} + +SECTIONS +{ + .vectors : + { + . = ALIGN(8); + KEEP(*(.vectors)); + . = ALIGN(8); + } > ROM + + .text : + { + . = ALIGN(8); + *(.text); + *(.text*); + *(.glue_7); + *(.glue_7t); + *(.eh_frame); + KEEP (*(.init)); + KEEP (*(.fini)); + . = ALIGN(8); + _etext = .; + } > ROM + + .rodata : + { + . = ALIGN(8); + *(.rodata); + *(.rodata*); + . = ALIGN(8); + } > ROM + + .ARM.extab (READONLY) : + { + . = ALIGN(8); + *(.ARM.extab* .gnu.linkonce.armextab.*) + . = ALIGN(8); + } > ROM + + .ARM (READONLY) : + { + . = ALIGN(8); + __exidx_start = .; + *(.ARM.exidx*); + __exidx_end = .; + . = ALIGN(8); + } > ROM + + .preinit_array (READONLY) : + { + . = ALIGN(8); + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)); + PROVIDE_HIDDEN (__preinit_array_end = .); + . = ALIGN(8); + } > ROM + + .init_array (READONLY) : + { + . = ALIGN(8); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))); + KEEP (*(.init_array*)); + PROVIDE_HIDDEN (__init_array_end = .); + . = ALIGN(8); + } > ROM + + .fini_array (READONLY) : + { + . = ALIGN(8); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))); + KEEP (*(.fini_array*)); + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(8); + } > ROM + + .copy.table (READONLY) : + { + . = ALIGN(8); + __copy_table_start__ = .; + LONG(LOADADDR(.data)); + LONG (ADDR(.data)); + LONG (SIZEOF(.data) / 4); + __copy_table_end__ = .; + } > ROM + + .zero.table (READONLY) : + { + . = ALIGN(8); + __zero_table_start__ = .; + LONG (ADDR(.bss)); + LONG (SIZEOF(.bss) / 4); + __zero_table_end__ = .; + } > ROM + + .data : + { + . = ALIGN(8); + _sidata = LOADADDR(.data); + __data_start__ = .; + _sdata = .; + *(.data); + *(.data*); + . = ALIGN(8); + _edata = .; + } > RAM AT> ROM + + .bss : + { + . = ALIGN(8); + _sbss = .; + __bss_start__ = _sbss; + *(.bss); + *(.bss*); + *(COMMON); + . = ALIGN(8); + _ebss = .; + __bss_end__ = _ebss; + } > RAM + + /* DWC GMAC DMA descriptors + RX/TX buffers. The stm32_eth.c driver + * places all descriptor rings and packet buffers into .eth_buffers. + * On STM32C5 all SRAM is DMA-reachable, so a normal 32-byte aligned + * section in RAM suffices. NOLOAD: uninitialized, zeroed by the driver. */ + .eth_buffers (NOLOAD) : + { + . = ALIGN(32); + *(.eth_buffers); + *(.eth_buffers*); + . = ALIGN(32); + } > RAM + + .heap (NOLOAD) : + { + . = ALIGN(8); + __end__ = .; + PROVIDE (end = .); + PROVIDE (_end = .); + _heap_start = .; + . += HEAP_SIZE; + . = ALIGN(8); + _heap_end = .; + PROVIDE (_heap_limit = .); + __HeapLimit = .; + } > RAM + + .stack (NOLOAD) : + { + . = ALIGN(8); + __StackLimit = .; + . += STACK_SIZE; + . = ALIGN(8); + __StackTop = .; + _estack = .; + __stack = .; + } > RAM + + /* CMSIS startup.c uses these names for the initial SP and MSPLIM; alias + * them to the linker's stack symbols so MSPLIM actually protects against + * a stack overflow (otherwise MSPLIM=0 and an overflow silently corrupts + * the heap below the stack instead of faulting). */ + PROVIDE(__INITIAL_SP = __StackTop); + PROVIDE(__STACK_LIMIT = __StackLimit); + + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : + { + *(.ARM.attributes) + } +} diff --git a/src/port/stm32c5a3/tls_certs.h b/src/port/stm32c5a3/tls_certs.h new file mode 100644 index 00000000..7493acfe --- /dev/null +++ b/src/port/stm32c5a3/tls_certs.h @@ -0,0 +1,114 @@ +/* tls_certs.h -- GENERATED by gen_certs.py; do not edit by hand. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * TLS 1.3 mutual-auth test PKI for the STM32C5A3ZG wolfIP client. TEST ONLY. + * + * Provenance / regeneration: + * cd src/port/stm32c5a3 && python3 gen_certs.py + * Fixed CA/server scalars; client key is the NIST P-256 CAVP SigGen vector from + * identity_key.h. The host material (tls-certs/, git-ignored -- it holds a + * throwaway test key) is regenerated on demand. ECDSA cert DER is not byte- + * reproducible, so regenerating also rewrites this header: rebuild + reflash + * after regenerating. The host material feeds: + * openssl s_server -accept 11111 -tls1_3 -cert server.pem \ + * -key server.key.pem -Verify 1 -CAfile ca.pem -www + * + * Contents below (all public material -- no live secret): + * client_cert_der -- the device client certificate (binds the P-256 + * identity public key identity_key_qx/qy). + * client_key_der -- SEC1 EC private key = the RAW identity scalar, used + * only by the software bring-up build (ENABLE_TLS_CLIENT + * without ENABLE_DHUK_KEY). The DHUK build signs through + * the crypto callback and never touches this. + * ca_cert_der -- the test CA (to verify the server, if enabled). + */ +#ifndef TLS_CERTS_H +#define TLS_CERTS_H + +static const unsigned char client_cert_der[] = { + 0x30, 0x82, 0x01, 0x75, 0x30, 0x82, 0x01, 0x1c, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x01, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x04, 0x03, 0x02, 0x30, 0x30, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x77, 0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, + 0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x0e, 0x77, 0x6f, 0x6c, 0x66, 0x49, 0x50, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x36, + 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, + 0x0d, 0x33, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x30, 0x32, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x0c, 0x0c, 0x77, 0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0c, 0x10, 0x73, 0x74, 0x6d, 0x33, 0x32, 0x63, 0x35, 0x61, 0x33, 0x2d, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x29, 0x57, 0x8c, + 0x7a, 0xb6, 0xce, 0x0d, 0x11, 0x49, 0x3c, 0x95, 0xd5, 0xea, 0x05, 0xd2, + 0x99, 0xd5, 0x36, 0x80, 0x1c, 0xa9, 0xcb, 0xd5, 0x0e, 0x99, 0x24, 0xe4, + 0x3b, 0x73, 0x3b, 0x83, 0xab, 0x08, 0xc8, 0x04, 0x98, 0x79, 0xc6, 0x27, + 0x8b, 0x22, 0x73, 0x34, 0x84, 0x74, 0x15, 0x85, 0x15, 0xac, 0xca, 0xa3, + 0x83, 0x44, 0x10, 0x6e, 0xf9, 0x68, 0x03, 0xc5, 0xa0, 0x5a, 0xdc, 0x48, + 0x00, 0xa3, 0x25, 0x30, 0x23, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x13, 0x06, 0x03, 0x55, + 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x3c, + 0xaa, 0x07, 0xa6, 0xd3, 0xe4, 0xd3, 0x0e, 0x6d, 0xa0, 0x20, 0x8c, 0x8a, + 0x86, 0x6b, 0x60, 0xab, 0xf2, 0x55, 0x55, 0x3f, 0x05, 0xfe, 0x71, 0xe2, + 0x5f, 0xfa, 0x1f, 0xa7, 0x54, 0xbf, 0xd7, 0x02, 0x20, 0x25, 0x48, 0x9b, + 0xb5, 0x93, 0xe3, 0xab, 0x16, 0x9a, 0xc4, 0xd7, 0x49, 0xec, 0x4d, 0x88, + 0xa9, 0x43, 0x94, 0x70, 0x39, 0x97, 0xcc, 0x08, 0xd4, 0xe4, 0xb1, 0xb7, + 0x23, 0x21, 0x1b, 0x1d, 0xfd, +}; +static const unsigned int client_cert_der_len = 377; + +static const unsigned char client_key_der[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x70, 0x83, 0x09, 0xa7, 0x44, + 0x9e, 0x15, 0x6b, 0x0d, 0xb7, 0x0e, 0x5b, 0x52, 0xe6, 0x06, 0xc7, 0xe0, + 0x94, 0xed, 0x67, 0x6c, 0xe8, 0x95, 0x3b, 0xf6, 0xc1, 0x47, 0x57, 0xc8, + 0x26, 0xf5, 0x90, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0x29, 0x57, 0x8c, + 0x7a, 0xb6, 0xce, 0x0d, 0x11, 0x49, 0x3c, 0x95, 0xd5, 0xea, 0x05, 0xd2, + 0x99, 0xd5, 0x36, 0x80, 0x1c, 0xa9, 0xcb, 0xd5, 0x0e, 0x99, 0x24, 0xe4, + 0x3b, 0x73, 0x3b, 0x83, 0xab, 0x08, 0xc8, 0x04, 0x98, 0x79, 0xc6, 0x27, + 0x8b, 0x22, 0x73, 0x34, 0x84, 0x74, 0x15, 0x85, 0x15, 0xac, 0xca, 0xa3, + 0x83, 0x44, 0x10, 0x6e, 0xf9, 0x68, 0x03, 0xc5, 0xa0, 0x5a, 0xdc, 0x48, + 0x00, +}; +static const unsigned int client_key_der_len = 121; + +static const unsigned char ca_cert_der[] = { + 0x30, 0x82, 0x01, 0x61, 0x30, 0x82, 0x01, 0x08, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x04, 0x03, 0x02, 0x30, 0x30, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x77, 0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, + 0x20, 0x54, 0x65, 0x73, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x0e, 0x77, 0x6f, 0x6c, 0x66, 0x49, 0x50, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x36, + 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, + 0x0d, 0x33, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x30, 0x30, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x0c, 0x0c, 0x77, 0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0c, 0x0e, 0x77, 0x6f, 0x6c, 0x66, 0x49, 0x50, 0x20, 0x54, 0x65, 0x73, + 0x74, 0x20, 0x43, 0x41, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xbd, 0x62, 0x98, 0x35, 0xde, + 0xfd, 0x5c, 0xb0, 0xf5, 0x47, 0x0b, 0x7c, 0x8e, 0x34, 0x14, 0x7b, 0x2b, + 0xaa, 0x6f, 0x2f, 0xdb, 0x61, 0xce, 0x57, 0x8f, 0x27, 0x1c, 0x92, 0x1b, + 0x9f, 0x48, 0xf7, 0x8a, 0x61, 0x1c, 0xcc, 0x4a, 0x76, 0x2e, 0x8c, 0xb5, + 0xbf, 0x40, 0xb9, 0xde, 0x8a, 0x8d, 0x76, 0xf8, 0x0d, 0x45, 0xa6, 0xb9, + 0xb7, 0x1b, 0x75, 0x7e, 0x60, 0x34, 0xc2, 0xab, 0xe5, 0x9b, 0x23, 0xa3, + 0x13, 0x30, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0a, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, + 0x44, 0x02, 0x20, 0x51, 0x41, 0xe6, 0x43, 0x31, 0x55, 0x91, 0x6d, 0xc3, + 0x51, 0xc9, 0x9a, 0xc6, 0x36, 0xbe, 0x25, 0x6b, 0xfa, 0xd3, 0x95, 0xf7, + 0x02, 0x0c, 0xca, 0x9d, 0xa1, 0x87, 0x0c, 0x0d, 0x50, 0x7d, 0x6e, 0x02, + 0x20, 0x5b, 0xcb, 0x98, 0x18, 0x07, 0x9c, 0x74, 0x14, 0x3b, 0x64, 0xee, + 0x2b, 0xe4, 0x9b, 0xd2, 0x9f, 0x7b, 0x3e, 0xd6, 0xa4, 0xf6, 0x19, 0xb9, + 0x31, 0xe8, 0x1a, 0x96, 0x25, 0x71, 0xd0, 0x0f, 0x65, +}; +static const unsigned int ca_cert_der_len = 357; + +#endif /* TLS_CERTS_H */ diff --git a/src/port/stm32c5a3/user_settings.h b/src/port/stm32c5a3/user_settings.h new file mode 100644 index 00000000..3830b053 --- /dev/null +++ b/src/port/stm32c5a3/user_settings.h @@ -0,0 +1,216 @@ +/* user_settings.h + * + * wolfSSL configuration for STM32C5A3ZG bare-metal + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP 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. + * + * wolfIP 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 + */ + +#ifndef USER_SETTINGS_H +#define USER_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------------------------------------------------------------- */ +/* Build-mode shorthand + * + * WOLFSSL_STM32C5_CRYPTO_ARM gates the shared STM32C5 hardware-crypto config + * for the TLS 1.3 mutual-auth client (ENABLE_TLS_CLIENT). TLS-only feature + * toggles are added on top under the ENABLE_TLS gate further down. + * ------------------------------------------------------------------------- */ +#if defined(ENABLE_TLS_CLIENT) || defined(WOLFSSL_STM32C5) + #define WOLFSSL_STM32C5_CRYPTO_ARM +#endif + +/* ------------------------------------------------------------------------- */ +/* Platform / OS + * + * Network-only baseline: this file is only compiled when ENABLE_TLS is set. + * ------------------------------------------------------------------------- */ +#define WOLFSSL_GENERAL_ALIGNMENT 4 +#define SINGLE_THREADED +#define WOLFSSL_SMALL_STACK +#define WOLFSSL_USER_IO /* Use custom I/O callbacks (wolfssl_io.c) */ +#define NO_FILESYSTEM +#define NO_WRITEV +#define NO_MAIN_DRIVER + +/* Bare-metal: no system headers */ +#define WOLFSSL_NO_SOCK +#define NO_WOLFSSL_DIR + +/* ------------------------------------------------------------------------- */ +/* STM32C5A3 hardware crypto (DHUK crypto-callback TLS build) + * + * The STM32C5A3 has TinyAES + HASH + RNG + SAES + PKA (V2 layout, all on + * AHB2). We drive them via the direct-register WOLFSSL_STM32_BARE path (the + * C5 new-generation HAL has no classic + * CRYP/PKA driver APIs), mirroring the STM32_Bare_Test c5a3 crypto arm. The + * network-only (ENABLE_TLS) baseline stays on the software crypto path -- the + * HW enables below are additive and only activate for the self-test. + * + * Enabling WOLFSSL_STM32_PKA on the C5 auto-selects WC_STM32_PKA_SIGN_ONLY + * (see wolfcrypt/port/st/stm32.h): HW signs via the protected PKA, software + * verifies. WOLFSSL_STM32_BARE + SAES + PKA + WOLFSSL_DHUK together auto- + * derive WC_STM32_HAS_DHUK inside stm32.h -- do not define it by hand. + * ------------------------------------------------------------------------- */ +#ifdef WOLFSSL_STM32C5_CRYPTO_ARM + #ifndef WOLFSSL_STM32C5 /* also passed as -DWOLFSSL_STM32C5 */ + #define WOLFSSL_STM32C5 + #endif + #define WOLFSSL_STM32_BARE /* direct-register HW crypto (no HAL) */ + #define STM32_CRYPTO /* TinyAES HW */ + #define STM32_HASH /* HW HASH (SHA-1/224/256/384/512) */ + #define NO_STM32_HMAC /* HW HASH state machine does not survive HKDF */ + #define STM32_RNG /* HW RNG (CK48 <- HSIDIV3, set up in main.c) */ + /* C5 silicon: the RNG clock-error detector (CR.CED) trips on the perfectly + * valid 48 MHz CK48 kernel clock and stalls DRDY, which hangs the multi-word + * Hash-DRBG seed pull inside wolfSSL_new. Suppress it (matches the Linux + * STM32 RNG driver). main.c sets CR.CED when it enables the RNG too. */ + #define WC_STM32_RNG_CED_DISABLE + #define NO_AES_192 /* TinyAES does not support 192-bit keys */ + #define WOLFSSL_STM32_PKA /* auto-selects WC_STM32_PKA_SIGN_ONLY on C5 */ + #define WOLFSSL_SMALL_STACK /* move large crypto temporaries to the heap */ + + /* Crypto-callback + DHUK are harmless in 3A.0 (no session/key devId is + * set anywhere), but wiring them here now means the later DHUK step only + * has to register the callback and stamp a devId -- no user_settings churn. + * Passed on the command line too (-DWOLF_CRYPTO_CB -DWOLFSSL_DHUK). */ + #ifndef WOLF_CRYPTO_CB + #define WOLF_CRYPTO_CB + #endif + #ifndef WOLFSSL_DHUK + #define WOLFSSL_DHUK + #endif + /* PK (EccSign) callback: lets the TLS client sign CertificateVerify with + * the DHUK-wrapped identity key held by the app (ENABLE_DHUK_KEY build). */ + #ifndef HAVE_PK_CALLBACKS + #define HAVE_PK_CALLBACKS + #endif +#endif + +/* ------------------------------------------------------------------------- */ +/* Math - SP math with Cortex-M assembly optimizations + * ------------------------------------------------------------------------- */ +#define WOLFSSL_SP_MATH_ALL /* Use SP math for all operations */ +#define WOLFSSL_SP_SMALL /* Smaller code size */ +#define SP_WORD_SIZE 32 /* 32-bit platform */ +#define WOLFSSL_SP_ARM_CORTEX_M_ASM +#define WOLFSSL_HAVE_SP_ECC +#define WOLFSSL_HAVE_SP_RSA +#define TFM_NO_ASM + +/* ------------------------------------------------------------------------- */ +/* TLS Configuration */ +/* ------------------------------------------------------------------------- */ +#define WOLFSSL_TLS13 +#define HAVE_TLS_EXTENSIONS +#define HAVE_SUPPORTED_CURVES +#define HAVE_ENCRYPT_THEN_MAC +#define HAVE_SNI +#define NO_SESSION_CACHE + +/* ------------------------------------------------------------------------- */ +/* Cipher Suites */ +/* ------------------------------------------------------------------------- */ +#define HAVE_AESGCM +#define GCM_SMALL +#define WOLFSSL_AES_SMALL_TABLES +#define WOLFSSL_AES_DIRECT +#ifdef WOLFSSL_STM32C5_CRYPTO_ARM + /* The DHUK ECDSA leg AES-ECB-wraps the P-256 scalar with the device key, + * and the GCM/HMAC/RNG legs need the DRBG + ECB primitives. The TLS arm + * uses the same C5 HW-RNG-seeded DRBG (HAVE_HASHDRBG + NO_DEV_RANDOM): + * wc_GenerateSeed pulls from the STM32 HW RNG (STM32_RNG), no software + * CUSTOM_RAND block. AES-ECB/DECRYPT are pulled in by the TinyAES BARE + * path as well. */ + #define HAVE_AES_ECB + #define HAVE_AES_DECRYPT + #define HAVE_HASHDRBG + #define NO_DEV_RANDOM +#endif +#define HAVE_CHACHA +#define HAVE_POLY1305 +#define WOLFSSL_SHA384 +#define WOLFSSL_SHA512 +#define HAVE_HKDF + +/* ------------------------------------------------------------------------- */ +/* Key Exchange / Certificates */ +/* ------------------------------------------------------------------------- */ +#define HAVE_ECC +#define ECC_USER_CURVES +#define HAVE_ECC256 +#define ECC_SHAMIR +#define ECC_TIMING_RESISTANT +#define TFM_TIMING_RESISTANT +#define WC_RSA_BLINDING +#define WC_RSA_PSS +#define WOLFSSL_ASN_TEMPLATE +#define WOLFSSL_BASE64_ENCODE +#define WOLFSSL_CERT_GEN +#define WOLFSSL_CERT_EXT + +/* ------------------------------------------------------------------------- */ +/* Disable Unused Features */ +/* ------------------------------------------------------------------------- */ +#define NO_DSA +#define NO_RC4 +#define NO_MD4 +#define NO_MD5 +#define NO_DES3 +#define NO_RABBIT +#define NO_HC128 +#define NO_PSK +#define NO_PWDBASED +#define NO_OLD_TLS +#define NO_CHECK_PRIVATE_KEY +#define NO_DH + +/* ------------------------------------------------------------------------- */ +/* Memory */ +/* ------------------------------------------------------------------------- */ +#define ALT_ECC_SIZE +#define WOLFSSL_SMALL_CERT_VERIFY +#define BENCH_EMBEDDED + +/* ------------------------------------------------------------------------- */ +/* RNG + * + * Network baseline: seed from the wolfIP LFSR/HW-RNG shim (custom_rand_gen_block + * in main.c). Any build that enables the STM32C5 crypto arm (self-test OR the + * TLS 1.3 client) drives the real HW RNG (STM32_RNG) to seed the Hash-DRBG, so + * the software custom block is compiled out there. + * ------------------------------------------------------------------------- */ +#ifndef WOLFSSL_STM32C5_CRYPTO_ARM + #define CUSTOM_RAND_GENERATE_BLOCK custom_rand_gen_block + int custom_rand_gen_block(unsigned char* output, unsigned int sz); +#endif + +/* ------------------------------------------------------------------------- */ +/* Debug (uncomment for troubleshooting) */ +/* ------------------------------------------------------------------------- */ +/* #define DEBUG_WOLFSSL */ + +#ifdef __cplusplus +} +#endif + +#endif /* USER_SETTINGS_H */ diff --git a/src/port/stm32h563/Makefile b/src/port/stm32h563/Makefile index 41003368..753314a3 100644 --- a/src/port/stm32h563/Makefile +++ b/src/port/stm32h563/Makefile @@ -86,10 +86,9 @@ endif # certain T32 instructions generated by sp_cortexm.c; real hardware uses 0) WOLFSSL_SP_NO_ASM ?= 0 -# Library paths - default to sibling directories (clone alongside pattern) -WOLFSSL_ROOT ?= $(ROOT)/../wolfssl +# Library paths - default to sibling directories (clone alongside pattern). +# WOLFSSL_ROOT / WOLFMQTT_ROOT defaults come from the shared wolfssl.mk below. WOLFSSH_ROOT ?= $(ROOT)/../wolfssh -WOLFMQTT_ROOT ?= $(ROOT)/../wolfmqtt # wolfBoot tree, used for TZEN=1 to sign the NS image and to provide # the secure stub binary that gets concatenated into factory.bin. # Override on the command line for local clones, e.g.: @@ -122,6 +121,11 @@ CFLAGS_WOLFSSL := $(filter-out -Werror,$(CFLAGS_WOLFSSL)) CFLAGS_WOLFSSL += -Wno-unused-variable -Wno-unused-function CFLAGS_WOLFSSL += $(EXTRA_CFLAGS_WOLFSSL) +# Shared wolfSSL build plumbing: WOLFSSL_ROOT/WOLFMQTT_ROOT defaults, the base +# WOLFSSL_SRCS list (both sp_c32 + sp_cortexm, internally guarded), and the +# $(WOLFSSL_ROOT)/%.o relaxed-warning rule. +include $(ROOT)/src/port/stm32/wolfssl.mk + # Select linker script based on TZEN setting ifeq ($(TZEN),1) LDSCRIPT := target_tzen.ld @@ -197,10 +201,10 @@ SRCS += tls_server.c SRCS += $(ROOT)/src/port/wolfssl_io.c endif -# TLS client (Google test) +# TLS client (Google test) -- shared state machine, plain one-way client. ifeq ($(ENABLE_TLS_CLIENT),1) CFLAGS += -DENABLE_TLS_CLIENT -SRCS += tls_client.c +SRCS += $(ROOT)/src/port/tls_client.c endif @@ -213,52 +217,16 @@ SRCS += $(ROOT)/src/http/httpd.c endif endif -# wolfSSL source files (minimal set for TLS 1.3 server with ECC) -WOLFSSL_SRCS := \ - $(WOLFSSL_ROOT)/wolfcrypt/src/aes.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sha.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sha256.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sha512.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/hmac.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/hash.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/kdf.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/random.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/ecc.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/asn.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/coding.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/wc_port.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/memory.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/wolfmath.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sp_int.c - +# wolfSSL/wolfCrypt base set comes from wolfssl.mk (both sp_c32 + sp_cortexm are +# listed; the one not matching the ASM config compiles to nothing). H563 +# deliberately omits the logging/error/wc_encrypt group (NO_ERROR_STRINGS + +# logging off) to save flash, so it does NOT add $(WOLFSSL_LOG_SRCS). +# WOLFSSL_SP_NO_ASM forces the portable C SP math (needed under some emulators). ifeq ($(WOLFSSL_SP_NO_ASM),1) CFLAGS += -DWOLFSSL_SP_NO_ASM CFLAGS_WOLFSSL += -DWOLFSSL_SP_NO_ASM -WOLFSSL_SRCS += $(WOLFSSL_ROOT)/wolfcrypt/src/sp_c32.c -else -WOLFSSL_SRCS += $(WOLFSSL_ROOT)/wolfcrypt/src/sp_cortexm.c endif -WOLFSSL_SRCS += \ - $(WOLFSSL_ROOT)/src/ssl.c \ - $(WOLFSSL_ROOT)/src/tls.c \ - $(WOLFSSL_ROOT)/src/tls13.c \ - $(WOLFSSL_ROOT)/src/internal.c \ - $(WOLFSSL_ROOT)/src/keys.c \ - $(WOLFSSL_ROOT)/src/wolfio.c - -# ChaCha20-Poly1305 (optional) -WOLFSSL_SRCS += \ - $(WOLFSSL_ROOT)/wolfcrypt/src/chacha.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/chacha20_poly1305.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/poly1305.c - -# RSA for certificate verification (most servers use RSA certs) -WOLFSSL_SRCS += \ - $(WOLFSSL_ROOT)/wolfcrypt/src/rsa.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/signature.c - - SRCS += $(WOLFSSL_SRCS) endif # ENABLE_TLS @@ -572,9 +540,9 @@ factory: factory.bin %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ -# wolfSSL objects use relaxed warnings + user_settings.h + include paths -$(WOLFSSL_ROOT)/%.o: $(WOLFSSL_ROOT)/%.c - $(CC) $(CFLAGS_WOLFSSL) -DWOLFSSL_USER_SETTINGS $(if $(filter 1,$(ENABLE_SSH)),-DENABLE_SSH -DWOLFSSL_WOLFSSH) $(if $(filter 1,$(ENABLE_MQTT_BROKER)),-DENABLE_MQTT_BROKER) $(if $(filter 1,$(ENABLE_DOT1X)),-DHAVE_PBKDF2 -DHAVE_AES_KEYWRAP) -I$(WOLFSSL_ROOT) -c $< -o $@ +# Feature defines the wolfSSL objects need (SSH / MQTT / dot1x). Consumed by the +# shared $(WOLFSSL_ROOT)/%.o rule in wolfssl.mk via WOLFSSL_EXTRA_DEFS. +WOLFSSL_EXTRA_DEFS = $(if $(filter 1,$(ENABLE_SSH)),-DENABLE_SSH -DWOLFSSL_WOLFSSH) $(if $(filter 1,$(ENABLE_MQTT_BROKER)),-DENABLE_MQTT_BROKER) $(if $(filter 1,$(ENABLE_DOT1X)),-DHAVE_PBKDF2 -DHAVE_AES_KEYWRAP) clean: rm -f *.o app.elf app.bin diff --git a/src/port/stm32h563/README.md b/src/port/stm32h563/README.md index dec9d8eb..b1eae376 100644 --- a/src/port/stm32h563/README.md +++ b/src/port/stm32h563/README.md @@ -481,7 +481,7 @@ The TLS configuration is in `user_settings.h`: | `user_settings.h` | wolfSSL compile-time configuration | | `../certs.h` | Embedded ECC P-256 test certificate (shared) | | `tls_server.c/h` | TLS echo server implementation | -| `tls_client.c/h` | TLS client (for outbound connections) | +| `../tls_client.c/h` | TLS client state machine (shared, for outbound connections) | ### Generating Custom Certificates @@ -1087,7 +1087,7 @@ reset wolfBoot reads the trailer and swaps in the staged image. | `user_settings.h` | wolfSSL/wolfSSH/wolfMQTT configuration | | `../certs.h` | Embedded TLS certificates, shared (TLS builds only) | | `tls_server.c/h` | TLS echo server (TLS builds only) | -| `tls_client.c/h` | TLS client for outbound connections (TLS builds only) | +| `../tls_client.c/h` | TLS client (shared; outbound connections, TLS builds only) | | `../http/httpd.c` | HTTPS web server - wolfIP httpd (HTTPS builds only) | | `ssh_server.c/h` | SSH shell server (SSH builds only) | | `ssh_keys.h` | Embedded SSH host key (SSH builds only) | diff --git a/src/port/stm32h563/tls_client.c b/src/port/stm32h563/tls_client.c deleted file mode 100644 index 8fe2576c..00000000 --- a/src/port/stm32h563/tls_client.c +++ /dev/null @@ -1,321 +0,0 @@ -/* tls_client.c - * - * Copyright (C) 2026 wolfSSL Inc. - * - * This file is part of wolfIP TCP/IP stack. - * - * wolfIP 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. - * - * wolfIP 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 - */ - -#include "tls_client.h" -#include "wolfip.h" - -#include -#include -#include - -/* Configuration */ -#ifndef TLS_CLIENT_BUF_SIZE -#define TLS_CLIENT_BUF_SIZE 2048 -#endif - -/* Client state */ -typedef enum { - TLS_CLIENT_STATE_IDLE = 0, - TLS_CLIENT_STATE_DNS_LOOKUP, - TLS_CLIENT_STATE_CONNECTING, - TLS_CLIENT_STATE_HANDSHAKE, - TLS_CLIENT_STATE_CONNECTED, - TLS_CLIENT_STATE_DONE, - TLS_CLIENT_STATE_ERROR -} tls_client_state_t; - -/* Client context */ -static struct { - struct wolfIP *stack; - WOLFSSL_CTX *ctx; - WOLFSSL *ssl; - int fd; - tls_client_state_t state; - tls_client_debug_cb debug_cb; - tls_client_response_cb response_cb; - void *user_ctx; - uint8_t rx_buf[TLS_CLIENT_BUF_SIZE]; - ip4 server_ip; - uint16_t server_port; - int got_response; -} client; - -/* External functions from wolfssl_io.c */ -extern int wolfSSL_SetIO_wolfIP_CTX(WOLFSSL_CTX *ctx, struct wolfIP *s); -extern int wolfSSL_SetIO_wolfIP(WOLFSSL *ssl, int fd); - -/* Debug output helper */ -static void debug_print(const char *msg) -{ - if (client.debug_cb) { - client.debug_cb(msg); - } -} - -int tls_client_init(struct wolfIP *stack, tls_client_debug_cb debug) -{ - int ret; - - memset(&client, 0, sizeof(client)); - client.stack = stack; - client.debug_cb = debug; - client.fd = -1; - client.state = TLS_CLIENT_STATE_IDLE; - - debug_print("TLS Client: Initializing wolfSSL\n"); - - /* Initialize wolfSSL library (may already be done by server) */ - ret = wolfSSL_Init(); - if (ret != WOLFSSL_SUCCESS) { - debug_print("TLS Client: wolfSSL_Init failed\n"); - return -1; - } - - /* Create TLS 1.3 client context */ - client.ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); - if (client.ctx == NULL) { - debug_print("TLS Client: CTX_new failed\n"); - return -1; - } - - /* Don't verify server certificate (for testing without root CAs) */ - wolfSSL_CTX_set_verify(client.ctx, WOLFSSL_VERIFY_NONE, NULL); - - /* Register wolfIP I/O callbacks */ - wolfSSL_SetIO_wolfIP_CTX(client.ctx, stack); - - debug_print("TLS Client: Initialized\n"); - return 0; -} - -int tls_client_connect(const char *host, uint16_t port, - tls_client_response_cb response_cb, void *user_ctx) -{ - struct wolfIP_sockaddr_in addr; - int ret; - - if (client.state != TLS_CLIENT_STATE_IDLE) { - debug_print("TLS Client: Already busy\n"); - return -1; - } - - client.response_cb = response_cb; - client.user_ctx = user_ctx; - client.server_port = port; - - /* Try to parse as IP address first */ - client.server_ip = atoip4(host); - if (client.server_ip == 0) { - /* TODO: DNS lookup - for now require IP address */ - debug_print("TLS Client: DNS not implemented, use IP address\n"); - return -1; - } - - /* Create socket */ - client.fd = wolfIP_sock_socket(client.stack, AF_INET, IPSTACK_SOCK_STREAM, 0); - if (client.fd < 0) { - debug_print("TLS Client: Failed! socket() error\n"); - return -1; - } - - /* Connect to server */ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = ee16(port); - addr.sin_addr.s_addr = ee32(client.server_ip); - - ret = wolfIP_sock_connect(client.stack, client.fd, - (struct wolfIP_sockaddr *)&addr, sizeof(addr)); - if (ret < 0 && ret != -WOLFIP_EAGAIN) { - debug_print("TLS Client: Failed! connect() error\n"); - wolfIP_sock_close(client.stack, client.fd); - client.fd = -1; - return -1; - } - - client.state = TLS_CLIENT_STATE_CONNECTING; - debug_print("TLS Client: Connecting...\n"); - return 0; -} - -/* Call this from main loop to drive the TLS client state machine */ -int tls_client_poll(void) -{ - int ret; - int err; - - switch (client.state) { - case TLS_CLIENT_STATE_IDLE: - case TLS_CLIENT_STATE_DONE: - case TLS_CLIENT_STATE_ERROR: - return 0; - - case TLS_CLIENT_STATE_CONNECTING: - /* Check if TCP connection is established by calling connect again */ - { - struct wolfIP_sockaddr_in addr; - static int connect_ready_count = 0; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = ee16(client.server_port); - addr.sin_addr.s_addr = ee32(client.server_ip); - - ret = wolfIP_sock_connect(client.stack, client.fd, - (struct wolfIP_sockaddr *)&addr, sizeof(addr)); - if (ret == -WOLFIP_EAGAIN) { - /* Still connecting, keep polling */ - connect_ready_count = 0; - return 0; - } - if (ret < 0) { - debug_print("TLS Client: Failed! TCP connect error\n"); - client.state = TLS_CLIENT_STATE_ERROR; - return -1; - } - /* Connection established - wait a few poll cycles to let stack settle */ - connect_ready_count++; - if (connect_ready_count < 100) { - return 0; - } - connect_ready_count = 0; - } - debug_print("TLS Client: TLS handshake...\n"); - - /* Create SSL object */ - client.ssl = wolfSSL_new(client.ctx); - if (client.ssl == NULL) { - debug_print("TLS Client: Failed! SSL context error\n"); - client.state = TLS_CLIENT_STATE_ERROR; - return -1; - } - - /* Set SNI (Server Name Indication) - required by most servers */ - wolfSSL_UseSNI(client.ssl, WOLFSSL_SNI_HOST_NAME, "google.com", 10); - - /* Associate SSL with socket */ - ret = wolfSSL_SetIO_wolfIP(client.ssl, client.fd); - if (ret != 0) { - debug_print("TLS Client: Failed! I/O setup error\n"); - client.state = TLS_CLIENT_STATE_ERROR; - return -1; - } - client.state = TLS_CLIENT_STATE_HANDSHAKE; - __attribute__((fallthrough)); - - case TLS_CLIENT_STATE_HANDSHAKE: - ret = wolfSSL_connect(client.ssl); - if (ret == WOLFSSL_SUCCESS) { - debug_print("TLS Client: Connected!\n"); - client.state = TLS_CLIENT_STATE_CONNECTED; - } else { - err = wolfSSL_get_error(client.ssl, ret); - if (err == WOLFSSL_ERROR_WANT_READ || - err == WOLFSSL_ERROR_WANT_WRITE) { - /* Handshake in progress, continue polling */ - return 0; - } - (void)err; - debug_print("TLS Client: Failed! Handshake error\n"); - client.state = TLS_CLIENT_STATE_ERROR; - return -1; - } - break; - - case TLS_CLIENT_STATE_CONNECTED: - /* Try to read any response */ - ret = wolfSSL_read(client.ssl, client.rx_buf, - sizeof(client.rx_buf) - 1); - if (ret > 0) { - client.rx_buf[ret] = '\0'; - client.got_response = 1; - if (client.response_cb) { - client.response_cb((char *)client.rx_buf, ret, client.user_ctx); - } - } else { - err = wolfSSL_get_error(client.ssl, ret); - if (err == WOLFSSL_ERROR_ZERO_RETURN) { - /* Server closed connection - normal after sending response */ - if (client.got_response) { - debug_print("TLS Client: Passed! Received response from server\n"); - } else { - debug_print("TLS Client: Failed! Server closed connection (no data received)\n"); - } - client.state = TLS_CLIENT_STATE_DONE; - } else if (err != WOLFSSL_ERROR_WANT_READ) { - /* Connection closed/reset - check if we got data first */ - if (client.got_response) { - debug_print("TLS Client: Passed! Connection closed after response\n"); - client.state = TLS_CLIENT_STATE_DONE; - } else { - debug_print("TLS Client: Failed! Read error (no response received)\n"); - client.state = TLS_CLIENT_STATE_ERROR; - } - } - } - break; - - default: - break; - } - - return 0; -} - -int tls_client_send(const void *data, int len) -{ - int ret; - int err; - - if (client.state != TLS_CLIENT_STATE_CONNECTED) { - return -1; - } - - ret = wolfSSL_write(client.ssl, data, len); - if (ret <= 0) { - err = wolfSSL_get_error(client.ssl, ret); - if (err != WOLFSSL_ERROR_WANT_WRITE) { - debug_print("TLS Client: Write failed\n"); - return -1; - } - } - - return ret; -} - -void tls_client_close(void) -{ - if (client.ssl) { - wolfSSL_shutdown(client.ssl); - wolfSSL_free(client.ssl); - client.ssl = NULL; - } - if (client.fd >= 0 && client.stack) { - wolfIP_sock_close(client.stack, client.fd); - client.fd = -1; - } - client.state = TLS_CLIENT_STATE_IDLE; -} - -int tls_client_is_connected(void) -{ - return (client.state == TLS_CLIENT_STATE_CONNECTED); -} diff --git a/src/port/stm32h563/tls_client.h b/src/port/stm32h563/tls_client.h deleted file mode 100644 index cf7862a6..00000000 --- a/src/port/stm32h563/tls_client.h +++ /dev/null @@ -1,101 +0,0 @@ -/* tls_client.h - * - * Copyright (C) 2026 wolfSSL Inc. - * - * This file is part of wolfIP TCP/IP stack. - * - * wolfIP 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. - * - * wolfIP 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 - */ - -#ifndef TLS_CLIENT_H -#define TLS_CLIENT_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Forward declaration */ -struct wolfIP; - -/* Debug callback - receives status messages */ -typedef void (*tls_client_debug_cb)(const char *msg); - -/* Response callback - receives data from server */ -typedef void (*tls_client_response_cb)(const char *data, int len, void *ctx); - -/** - * Initialize the TLS client - * - * @param stack wolfIP stack instance - * @param debug Optional debug callback (can be NULL) - * - * @return 0 on success, negative on error - */ -int tls_client_init(struct wolfIP *stack, tls_client_debug_cb debug); - -/** - * Start TLS connection to a server - * - * @param host Server IP address (DNS not yet supported) - * @param port Server port (e.g., 443 for HTTPS) - * @param response_cb Callback for received data - * @param user_ctx User context passed to callback - * - * @return 0 on success, negative on error - * - * Example: - * tls_client_connect("142.250.80.46", 443, my_response_cb, NULL); - */ -int tls_client_connect(const char *host, uint16_t port, - tls_client_response_cb response_cb, void *user_ctx); - -/** - * Poll the TLS client state machine - * - * Call this regularly from main loop to drive handshake and receive data. - * - * @return 0 on success, negative on error - */ -int tls_client_poll(void); - -/** - * Send data to the server - * - * @param data Data to send - * @param len Length of data - * - * @return bytes sent, or negative on error - */ -int tls_client_send(const void *data, int len); - -/** - * Close the TLS connection - */ -void tls_client_close(void); - -/** - * Check if client is connected - * - * @return 1 if connected, 0 otherwise - */ -int tls_client_is_connected(void); - -#ifdef __cplusplus -} -#endif - -#endif /* TLS_CLIENT_H */ diff --git a/src/port/stm32h753/Makefile b/src/port/stm32h753/Makefile index 824779a6..ccc4ea78 100644 --- a/src/port/stm32h753/Makefile +++ b/src/port/stm32h753/Makefile @@ -29,9 +29,8 @@ ifeq ($(ENABLE_MQTT_BROKER),1) ENABLE_TLS = 1 endif -# Library paths - default to sibling directories -WOLFSSL_ROOT ?= $(ROOT)/../wolfssl -WOLFMQTT_ROOT ?= $(ROOT)/../wolfmqtt +# WOLFSSL_ROOT/WOLFMQTT_ROOT defaults + the shared wolfSSL source list and +# $(WOLFSSL_ROOT)/%.o rule come from the shared fragment (included below). # STM32H753ZI - Cortex-M7 with FPU CFLAGS := -mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard @@ -45,6 +44,10 @@ CFLAGS_WOLFSSL := $(CFLAGS) CFLAGS_WOLFSSL := $(filter-out -Werror,$(CFLAGS_WOLFSSL)) CFLAGS_WOLFSSL += -Wno-unused-variable -Wno-unused-function +# Shared wolfSSL build plumbing: WOLFSSL_ROOT/WOLFMQTT_ROOT defaults, the base +# WOLFSSL_SRCS list, and the $(WOLFSSL_ROOT)/%.o relaxed-warning rule. +include $(ROOT)/src/port/stm32/wolfssl.mk + LDSCRIPT := target.ld LDFLAGS := -nostdlib -T $(LDSCRIPT) -Wl,-gc-sections @@ -69,56 +72,19 @@ CFLAGS += -I$(WOLFSSL_ROOT) # wolfIP-wolfSSL glue SRCS += $(ROOT)/src/port/wolfssl_io.c -# TLS client (Google test) +# TLS client (Google test) -- shared state machine, plain one-way client. +# Keep the historical 100-poll settle before the handshake. ifeq ($(ENABLE_TLS_CLIENT),1) -CFLAGS += -DENABLE_TLS_CLIENT -SRCS += tls_client.c +CFLAGS += -DENABLE_TLS_CLIENT -DTLS_CLIENT_SETTLE_POLLS=100 +SRCS += $(ROOT)/src/port/tls_client.c endif -# wolfSSL source files (minimal set for TLS 1.3 client with ECC) -WOLFSSL_SRCS := \ - $(WOLFSSL_ROOT)/wolfcrypt/src/aes.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sha.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sha256.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sha512.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/hmac.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/hash.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/kdf.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/random.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/ecc.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/asn.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/coding.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/wc_port.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/memory.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/wolfmath.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sp_int.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sp_c32.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/sp_cortexm.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/port/st/stm32.c \ - $(WOLFSSL_ROOT)/src/ssl.c \ - $(WOLFSSL_ROOT)/src/tls.c \ - $(WOLFSSL_ROOT)/src/tls13.c \ - $(WOLFSSL_ROOT)/src/internal.c \ - $(WOLFSSL_ROOT)/src/keys.c \ - $(WOLFSSL_ROOT)/src/wolfio.c - -# ChaCha20-Poly1305 (optional) -WOLFSSL_SRCS += \ - $(WOLFSSL_ROOT)/wolfcrypt/src/chacha.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/chacha20_poly1305.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/poly1305.c - -# RSA for certificate verification +# Base wolfSSL/wolfCrypt set comes from wolfssl.mk. H753 adds the logging/error/ +# encrypt group, the STM32 HW HASH/HMAC port, and the wolfcrypt self-test. +WOLFSSL_SRCS += $(WOLFSSL_LOG_SRCS) WOLFSSL_SRCS += \ - $(WOLFSSL_ROOT)/wolfcrypt/src/rsa.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/signature.c - -# wolfCrypt test (validates all crypto algorithms including HW HASH/HMAC) -WOLFSSL_SRCS += \ - $(WOLFSSL_ROOT)/wolfcrypt/test/test.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/logging.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/error.c \ - $(WOLFSSL_ROOT)/wolfcrypt/src/wc_encrypt.c + $(WOLFSSL_ROOT)/wolfcrypt/src/port/st/stm32.c \ + $(WOLFSSL_ROOT)/wolfcrypt/test/test.c SRCS += $(WOLFSSL_SRCS) @@ -194,9 +160,7 @@ app.bin: app.elf %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ -# wolfSSL objects use relaxed warnings -$(WOLFSSL_ROOT)/%.o: $(WOLFSSL_ROOT)/%.c - $(CC) $(CFLAGS_WOLFSSL) -DWOLFSSL_USER_SETTINGS -I$(WOLFSSL_ROOT) -c $< -o $@ +# The $(WOLFSSL_ROOT)/%.o relaxed-warning rule comes from wolfssl.mk. clean: rm -f *.o app.elf app.bin diff --git a/src/port/stm32h753/README.md b/src/port/stm32h753/README.md index b2e6dde6..25eb99fa 100644 --- a/src/port/stm32h753/README.md +++ b/src/port/stm32h753/README.md @@ -181,8 +181,8 @@ stm32h753/ ├── syscalls.c # Newlib stubs ├── main.c # Application entry point ├── stm32_hash_register.h # STM32 HASH peripheral register definitions -├── tls_client.c # TLS 1.3 client (ENABLE_TLS_CLIENT) -├── tls_client.h # TLS client header +├── ../tls_client.c # TLS 1.3 client state machine (shared, ENABLE_TLS_CLIENT) +├── ../tls_client.h # TLS client header (shared) ├── ../stm32/stm32_eth.c # Ethernet MAC/PHY driver (shared) ├── ../stm32/stm32_eth.h # Ethernet driver header (shared) └── ../certs.h # Embedded TLS test certificates (shared) diff --git a/src/port/stm32h753/tls_client.c b/src/port/tls_client.c similarity index 52% rename from src/port/stm32h753/tls_client.c rename to src/port/tls_client.c index fd2ee5aa..35a4876b 100644 --- a/src/port/stm32h753/tls_client.c +++ b/src/port/tls_client.c @@ -19,18 +19,72 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ +/* Shared TLS 1.3 client state machine for the STM32 bare-metal wolfIP ports + * (compiled from each port as $(ROOT)/src/port/tls_client.c). Build-time knobs: + * + * TLS_CLIENT_MUTUAL_AUTH present a client certificate (mutual auth); loads + * client_cert_der / client_key_der from the port's + * tls_certs.h. (STM32C5A3ZG DHUK demo.) + * ENABLE_DHUK_KEY (implies MUTUAL_AUTH) sign CertificateVerify via a + * PK callback backed by the DHUK-wrapped identity + * key -- the scalar is unwrapped in SAES and signed + * on the HW PKA, never in software. + * TLS_CLIENT_CIPHER if defined, force this single TLS 1.3 suite. + * TLS_CLIENT_SETTLE_POLLS poll cycles to wait after TCP connect before the + * handshake (default 10). + * TLS_CLIENT_BUF_SIZE receive buffer size (default 2048). + * + * Without MUTUAL_AUTH this is a plain one-way client (H753/H563 -> HTTPS host); + * SNI is optional via tls_client_set_sni(). + */ + #include "tls_client.h" #include "wolfip.h" +#ifdef TLS_CLIENT_MUTUAL_AUTH +#include "tls_certs.h" +#endif #include #include #include +#ifdef ENABLE_DHUK_KEY +/* Milestone 3A.1: the identity private key is a DHUK-wrapped scalar. The + * CertificateVerify signature is produced by a PK (EccSign) callback that + * routes wc_ecc_sign_hash through the STM32 crypto callback: the scalar is + * unwrapped inside SAES and signed on the HW PKA, so it never appears in + * software. */ +#include +#include +#include +#include +#include "identity_key.h" + +static ecc_key g_dhuk_key; +static WC_RNG g_dhuk_rng; +static int g_dhuk_ready; + +/* DHUK derivation seed used to wrap the identity scalar (matches the crypto + * self-test provisioning). In production the wrapped blob is provisioned + * off-device and the plaintext scalar never exists here. */ +static const byte g_dhuk_seed[32] = { + 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, + 0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff, + 0x10,0x32,0x54,0x76,0x98,0xba,0xdc,0xfe, + 0xef,0xcd,0xab,0x89,0x67,0x45,0x23,0x01 +}; +#endif /* ENABLE_DHUK_KEY */ + /* Configuration */ #ifndef TLS_CLIENT_BUF_SIZE #define TLS_CLIENT_BUF_SIZE 2048 #endif +/* Poll cycles to let the TCP stack settle after connect before the handshake. */ +#ifndef TLS_CLIENT_SETTLE_POLLS +#define TLS_CLIENT_SETTLE_POLLS 10 +#endif + /* Client state */ typedef enum { TLS_CLIENT_STATE_IDLE = 0, @@ -72,6 +126,133 @@ static void debug_print(const char *msg) } } +#ifdef DEBUG_WOLFSSL +/* wolfSSL internal logging -> UART. */ +static void tls_wolfssl_log(const int logLevel, const char *const logMessage) +{ + (void)logLevel; + if (client.debug_cb && logMessage) { + client.debug_cb("[ssl] "); + client.debug_cb(logMessage); + client.debug_cb("\n"); + } +} +#endif + +/* Print " = \n" (val in decimal, handles negatives). */ +static void debug_print_kv(const char *key, int val) +{ + char buf[16]; + int i = 0; + unsigned int u; + int neg = 0; + + if (!client.debug_cb) { + return; + } + client.debug_cb(key); + client.debug_cb(" = "); + if (val < 0) { + neg = 1; + u = (unsigned int)(-val); + } else { + u = (unsigned int)val; + } + if (u == 0u) { + buf[i++] = '0'; + } + while (u > 0u && i < (int)sizeof(buf) - 1) { + buf[i++] = (char)('0' + (u % 10u)); + u /= 10u; + } + if (neg && i < (int)sizeof(buf) - 1) { + buf[i++] = '-'; + } + { + char rev[16]; + int j; + for (j = 0; j < i; j++) { + rev[j] = buf[i - 1 - j]; + } + rev[i] = '\0'; + client.debug_cb(rev); + } + client.debug_cb("\n"); +} + +#ifdef ENABLE_DHUK_KEY +/* PK (EccSign) callback: sign the CertificateVerify hash with the DHUK-wrapped + * identity key. wc_ecc_sign_hash on a WC_DHUK_DEVID key dispatches to the STM32 + * crypto callback (SAES unwrap -> HW PKA). The loaded placeholder key (keyDer) + * is intentionally ignored. */ +static int dhuk_ecc_sign_cb(WOLFSSL *ssl, const byte *in, word32 inSz, + byte *out, word32 *outSz, const byte *keyDer, word32 keySz, void *ctx) +{ + int ret; + (void)ssl; + (void)keyDer; + (void)keySz; + (void)ctx; + debug_print("TLS Client: CertificateVerify -> DHUK sign callback (SAES unwrap -> HW PKA)\n"); + ret = wc_ecc_sign_hash(in, inSz, out, outSz, &g_dhuk_rng, &g_dhuk_key); + debug_print_kv("TLS Client: DHUK sign ret", ret); + return ret; +} + +/* Register the DHUK device and provision the wrapped identity key. */ +static int dhuk_provision_key(void) +{ + Aes aes; + byte wrapped[32]; + int ret; + + ret = wc_Stm32_DhukRegister(WC_DHUK_DEVID); + if (ret != 0) { + debug_print_kv("TLS Client: DHUK register err", ret); + return ret; + } + ret = wc_InitRng(&g_dhuk_rng); + if (ret != 0) { + debug_print_kv("TLS Client: DHUK rng err", ret); + return ret; + } + ret = wc_ecc_init_ex(&g_dhuk_key, NULL, WC_DHUK_DEVID); + if (ret != 0) { + debug_print_kv("TLS Client: DHUK ecc init err", ret); + return ret; + } + /* Curve + public point from the identity vector (public, for verify). */ + ret = wc_ecc_import_unsigned(&g_dhuk_key, (byte *)identity_key_qx, + (byte *)identity_key_qy, NULL, ECC_SECP256R1); + if (ret != 0) { + debug_print_kv("TLS Client: DHUK import pub err", ret); + return ret; + } + /* Wrap the identity scalar with the DHUK-derived AES key (SAES). */ + ret = wc_AesInit(&aes, NULL, WC_DHUK_DEVID); + if (ret == 0) { + ret = wc_AesSetKey(&aes, g_dhuk_seed, 32, NULL, AES_ENCRYPTION); + } + if (ret == 0) { + ret = wc_AesEcbEncrypt(&aes, wrapped, (byte *)identity_key_d, 32); + } + wc_AesFree(&aes); + if (ret != 0) { + debug_print_kv("TLS Client: DHUK wrap err", ret); + return ret; + } + ret = wc_ecc_import_wrapped_private(&g_dhuk_key, g_dhuk_seed, 32, + wrapped, 32, 32); + if (ret != 0) { + debug_print_kv("TLS Client: DHUK import wrapped err", ret); + return ret; + } + g_dhuk_ready = 1; + debug_print("TLS Client: DHUK identity key provisioned (devId)\n"); + return 0; +} +#endif /* ENABLE_DHUK_KEY */ + int tls_client_init(struct wolfIP *stack, tls_client_debug_cb debug) { int ret; @@ -84,7 +265,14 @@ int tls_client_init(struct wolfIP *stack, tls_client_debug_cb debug) debug_print("TLS Client: Initializing wolfSSL\n"); - /* Initialize wolfSSL library (may already be done by server) */ +#ifdef DEBUG_WOLFSSL + /* Route wolfSSL's internal trace to the UART so we can see exactly + * where a hang occurs during wolfSSL_new / wolfSSL_connect. */ + wolfSSL_SetLoggingCb(tls_wolfssl_log); + wolfSSL_Debugging_ON(); +#endif + + /* Initialize wolfSSL library (may already be done elsewhere) */ ret = wolfSSL_Init(); if (ret != WOLFSSL_SUCCESS) { debug_print("TLS Client: wolfSSL_Init failed\n"); @@ -98,13 +286,61 @@ int tls_client_init(struct wolfIP *stack, tls_client_debug_cb debug) return -1; } - /* Don't verify server certificate (for testing without root CAs) */ +#ifdef TLS_CLIENT_MUTUAL_AUTH + /* Load the device client certificate (binds the identity public key) and + * its private key. DER / ASN.1 encoded in the port's tls_certs.h. The DHUK + * build overrides the actual signing via a PK callback (see below), so the + * loaded key there is only a placeholder for wolfSSL's cert/key association. + * A SEC1 EC key is ASN.1 (FILETYPE_ASN1). */ + ret = wolfSSL_CTX_use_certificate_buffer(client.ctx, + client_cert_der, (long)client_cert_der_len, WOLFSSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS) { + debug_print("TLS Client: Failed! load client certificate\n"); + return -1; + } + ret = wolfSSL_CTX_use_PrivateKey_buffer(client.ctx, + client_key_der, (long)client_key_der_len, WOLFSSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS) { + debug_print("TLS Client: Failed! load client private key\n"); + return -1; + } +#endif /* TLS_CLIENT_MUTUAL_AUTH */ + + /* Do not verify the server certificate (test against hosts without a + * bundled root CA). For mutual auth, the SERVER verifies US. */ wolfSSL_CTX_set_verify(client.ctx, WOLFSSL_VERIFY_NONE, NULL); +#ifdef TLS_CLIENT_CIPHER + /* Force a single TLS 1.3 suite. */ + ret = wolfSSL_CTX_set_cipher_list(client.ctx, TLS_CLIENT_CIPHER); + if (ret != WOLFSSL_SUCCESS) { + debug_print("TLS Client: Failed! set cipher list\n"); + return -1; + } +#endif + +#ifdef ENABLE_DHUK_KEY + /* 3A.1: provision the DHUK-wrapped identity key and route the + * CertificateVerify signature through the crypto callback (HW PKA). + * The placeholder key loaded above satisfies wolfSSL's cert/key + * association; the PK callback overrides the actual signing so the + * identity scalar is only ever unwrapped inside SAES. */ + ret = dhuk_provision_key(); + if (ret != 0) { + debug_print("TLS Client: Failed! DHUK key provisioning\n"); + return -1; + } + wolfSSL_CTX_SetEccSignCb(client.ctx, dhuk_ecc_sign_cb); +#endif + /* Register wolfIP I/O callbacks */ wolfSSL_SetIO_wolfIP_CTX(client.ctx, stack); +#ifdef TLS_CLIENT_MUTUAL_AUTH + debug_print("TLS Client: Initialized (mutual auth)\n"); +#else debug_print("TLS Client: Initialized\n"); +#endif return 0; } @@ -136,7 +372,7 @@ int tls_client_connect(const char *host, uint16_t port, /* Try to parse as IP address first */ client.server_ip = atoip4(host); if (client.server_ip == 0) { - /* TODO: DNS lookup - for now require IP address */ + /* DNS lookup not implemented - require IP address */ debug_print("TLS Client: DNS not implemented, use IP address\n"); return -1; } @@ -168,6 +404,23 @@ int tls_client_connect(const char *host, uint16_t port, return 0; } +/* Print negotiated protocol version and cipher after a successful handshake. */ +static void tls_client_report_handshake(void) +{ + const char *ver; + const char *cipher; + + ver = wolfSSL_get_version(client.ssl); + cipher = wolfSSL_get_cipher(client.ssl); + + debug_print("TLS Client: version = "); + debug_print(ver ? ver : "(null)"); + debug_print("\n"); + debug_print("TLS Client: cipher = "); + debug_print(cipher ? cipher : "(null)"); + debug_print("\n"); +} + /* Call this from main loop to drive the TLS client state machine */ int tls_client_poll(void) { @@ -202,9 +455,9 @@ int tls_client_poll(void) client.state = TLS_CLIENT_STATE_ERROR; return -1; } - /* Connection established - wait a few poll cycles to let stack settle */ + /* Connection established - wait a few poll cycles to settle */ client.connect_ready_count++; - if (client.connect_ready_count < 10) { + if (client.connect_ready_count < TLS_CLIENT_SETTLE_POLLS) { return 0; } client.connect_ready_count = 0; @@ -219,7 +472,7 @@ int tls_client_poll(void) return -1; } - /* Set SNI (Server Name Indication) - required by most servers */ + /* Set SNI (Server Name Indication) if configured */ if (client.sni_host[0]) { wolfSSL_UseSNI(client.ssl, WOLFSSL_SNI_HOST_NAME, client.sni_host, (word16)strlen(client.sni_host)); @@ -239,6 +492,7 @@ int tls_client_poll(void) ret = wolfSSL_connect(client.ssl); if (ret == WOLFSSL_SUCCESS) { debug_print("TLS Client: Connected!\n"); + tls_client_report_handshake(); client.state = TLS_CLIENT_STATE_CONNECTED; } else { err = wolfSSL_get_error(client.ssl, ret); @@ -247,8 +501,7 @@ int tls_client_poll(void) /* Handshake in progress, continue polling */ return 0; } - (void)err; - debug_print("TLS Client: Failed! Handshake error\n"); + debug_print_kv("TLS Client: Failed! Handshake err", err); client.state = TLS_CLIENT_STATE_ERROR; return -1; } @@ -267,7 +520,7 @@ int tls_client_poll(void) } else { err = wolfSSL_get_error(client.ssl, ret); if (err == WOLFSSL_ERROR_ZERO_RETURN) { - /* Server closed connection - normal after sending response */ + /* Server closed connection - normal after response */ if (client.got_response) { debug_print("TLS Client: Passed! Received response from server\n"); } else { diff --git a/src/port/stm32h753/tls_client.h b/src/port/tls_client.h similarity index 100% rename from src/port/stm32h753/tls_client.h rename to src/port/tls_client.h