diff --git a/src/http/httpd.c b/src/http/httpd.c index 2ca7ca1..cd3ca63 100644 --- a/src/http/httpd.c +++ b/src/http/httpd.c @@ -501,14 +501,14 @@ int httpd_init(struct httpd *httpd, struct wolfIP *s, uint16_t port, void *ssl_c struct wolfIP_sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; - addr.sin_port = htons(port); + addr.sin_port = ee16(port); if (!httpd) { return -1; } memset(httpd, 0, sizeof(struct httpd)); httpd->ipstack = s; httpd->port = port; - httpd->listen_sd = wolfIP_sock_socket(s, AF_INET, SOCK_STREAM, 0); + httpd->listen_sd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0); if (httpd->listen_sd < 0) { return -1; } diff --git a/src/port/stm32h563/Makefile b/src/port/stm32h563/Makefile index a16c4e0..0ab8105 100644 --- a/src/port/stm32h563/Makefile +++ b/src/port/stm32h563/Makefile @@ -1,4 +1,4 @@ -CC=arm-none-eabi-gcc +CC ?= arm-none-eabi-gcc OBJCOPY ?= arm-none-eabi-objcopy ROOT := ../../.. @@ -7,10 +7,34 @@ ROOT := ../../.. # Default is TZEN=0 (TrustZone disabled) TZEN ?= 0 +# TLS support: set ENABLE_TLS=1 to include wolfSSL TLS server +# Requires wolfSSL cloned alongside wolfip (or set WOLFSSL_ROOT) +ENABLE_TLS ?= 0 + +# HTTPS web server: set ENABLE_HTTPS=1 to include HTTPS web server (requires TLS) +ENABLE_HTTPS ?= 0 + +# SSH support: set ENABLE_SSH=1 to include wolfSSH server (requires TLS) +ENABLE_SSH ?= 0 + +# MQTT support: set ENABLE_MQTT=1 to include wolfMQTT client (requires TLS) +ENABLE_MQTT ?= 0 + +# Library paths - default to sibling directories (clone alongside pattern) +WOLFSSL_ROOT ?= $(ROOT)/../wolfssl +WOLFSSH_ROOT ?= $(ROOT)/../wolfssh +WOLFMQTT_ROOT ?= $(ROOT)/../wolfmqtt + +# Base compiler flags CFLAGS := -mcpu=cortex-m33 -mthumb -mcmse -Os -ffreestanding -fdata-sections -ffunction-sections -CFLAGS += -g -ggdb -Wall -Wextra -Werror -Wdeclaration-after-statement +CFLAGS += -g -ggdb -Wall -Wextra -Werror CFLAGS += -I. -I$(ROOT) -I$(ROOT)/src +# Relaxed warnings for external libraries (wolfSSL has many unused var warnings) +CFLAGS_WOLFSSL := $(CFLAGS) +CFLAGS_WOLFSSL := $(filter-out -Werror,$(CFLAGS_WOLFSSL)) +CFLAGS_WOLFSSL += -Wno-unused-variable -Wno-unused-function + # Select linker script based on TZEN setting ifeq ($(TZEN),1) LDSCRIPT := target_tzen.ld @@ -22,11 +46,178 @@ endif LDFLAGS := -nostdlib -T $(LDSCRIPT) -Wl,-gc-sections +# Base source files SRCS := startup.c ivt.c syscalls.c main.c stm32h5_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 + +CFLAGS += -DENABLE_TLS +CFLAGS += -DWOLFSSL_USER_SETTINGS +CFLAGS += -DWOLFSSL_WOLFIP +CFLAGS += -I$(WOLFSSL_ROOT) + +# TLS server, client and wolfIP-wolfSSL glue +SRCS += tls_server.c +SRCS += tls_client.c +SRCS += $(ROOT)/src/port/wolfssl_io.c + +# HTTPS web server (requires TLS) - uses existing wolfIP httpd +ifeq ($(ENABLE_HTTPS),1) + +# HTTPS requires TLS +ifeq ($(ENABLE_TLS),0) + $(error ENABLE_HTTPS=1 requires ENABLE_TLS=1) +endif + +CFLAGS += -DENABLE_HTTPS +SRCS += $(ROOT)/src/http/httpd.c +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_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, comment out to save space) +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 + +# Signature verification (required for wolfSSH) +ifeq ($(ENABLE_SSH),1) +WOLFSSL_SRCS += \ + $(WOLFSSL_ROOT)/wolfcrypt/src/signature.c +endif + +SRCS += $(WOLFSSL_SRCS) + +endif # ENABLE_TLS + +# ----------------------------------------------------------------------------- +# SSH Support (wolfSSH) - requires TLS +# ----------------------------------------------------------------------------- +ifeq ($(ENABLE_SSH),1) + +# SSH requires TLS +ifeq ($(ENABLE_TLS),0) + $(error ENABLE_SSH=1 requires ENABLE_TLS=1) +endif + +# Validate wolfSSH exists +ifeq ($(wildcard $(WOLFSSH_ROOT)/wolfssh/ssh.h),) + $(error wolfSSH not found at $(WOLFSSH_ROOT). Clone it: git clone https://github.com/wolfSSL/wolfssh.git) +endif + +CFLAGS += -DENABLE_SSH +CFLAGS += -DWOLFSSH_USER_SETTINGS +CFLAGS += -I$(WOLFSSH_ROOT) + +# SSH server and wolfSSH-wolfIP glue +SRCS += ssh_server.c +SRCS += $(ROOT)/src/port/wolfssh_io.c + +# wolfSSH source files (minimal set for SSH server) +WOLFSSH_SRCS := \ + $(WOLFSSH_ROOT)/src/ssh.c \ + $(WOLFSSH_ROOT)/src/internal.c \ + $(WOLFSSH_ROOT)/src/io.c \ + $(WOLFSSH_ROOT)/src/keygen.c \ + $(WOLFSSH_ROOT)/src/log.c \ + $(WOLFSSH_ROOT)/src/port.c + +SRCS += $(WOLFSSH_SRCS) + +# wolfSSH objects use relaxed warnings + SSH/SSL include paths + user_settings.h +$(WOLFSSH_ROOT)/%.o: $(WOLFSSH_ROOT)/%.c + $(CC) $(CFLAGS_WOLFSSL) -DENABLE_SSH -DWOLFSSL_USER_SETTINGS -DWOLFSSH_USER_SETTINGS -I$(WOLFSSH_ROOT) -I$(WOLFSSL_ROOT) -c $< -o $@ + +endif # ENABLE_SSH + +# ----------------------------------------------------------------------------- +# MQTT Support (wolfMQTT) - requires TLS +# ----------------------------------------------------------------------------- +ifeq ($(ENABLE_MQTT),1) + +# MQTT requires TLS +ifeq ($(ENABLE_TLS),0) + $(error ENABLE_MQTT=1 requires ENABLE_TLS=1) +endif + +# Validate wolfMQTT exists +ifeq ($(wildcard $(WOLFMQTT_ROOT)/wolfmqtt/mqtt_client.h),) + $(error wolfMQTT not found at $(WOLFMQTT_ROOT). Clone it: git clone https://github.com/wolfSSL/wolfMQTT.git) +endif + +CFLAGS += -DENABLE_MQTT +CFLAGS += -DWOLFMQTT_USER_SETTINGS +CFLAGS += -I$(WOLFMQTT_ROOT) + +# MQTT client and wolfMQTT-wolfIP glue +SRCS += mqtt_client.c +SRCS += $(ROOT)/src/port/wolfmqtt_io.c + +# wolfMQTT source files (minimal set for MQTT client) +WOLFMQTT_SRCS := \ + $(WOLFMQTT_ROOT)/src/mqtt_client.c \ + $(WOLFMQTT_ROOT)/src/mqtt_packet.c \ + $(WOLFMQTT_ROOT)/src/mqtt_socket.c + +SRCS += $(WOLFMQTT_SRCS) + +# wolfMQTT objects use relaxed warnings + MQTT/SSL include paths + user_settings.h +$(WOLFMQTT_ROOT)/%.o: $(WOLFMQTT_ROOT)/%.c + $(CC) $(CFLAGS_WOLFSSL) -DENABLE_MQTT -DWOLFSSL_USER_SETTINGS -DWOLFMQTT_USER_SETTINGS -I$(WOLFMQTT_ROOT) -I$(WOLFSSL_ROOT) -c $< -o $@ + +endif # ENABLE_MQTT + +# ----------------------------------------------------------------------------- +# Build rules +# ----------------------------------------------------------------------------- OBJS := $(patsubst %.c,%.o,$(SRCS)) all: app.bin - @echo "Built with TZEN=$(TZEN) using $(LDSCRIPT)" + @echo "Built with TZEN=$(TZEN) ENABLE_TLS=$(ENABLE_TLS) ENABLE_HTTPS=$(ENABLE_HTTPS) ENABLE_SSH=$(ENABLE_SSH) ENABLE_MQTT=$(ENABLE_MQTT)" +ifeq ($(ENABLE_TLS),1) + @echo " wolfSSL: $(WOLFSSL_ROOT)" +endif +ifeq ($(ENABLE_SSH),1) + @echo " wolfSSH: $(WOLFSSH_ROOT)" +endif +ifeq ($(ENABLE_MQTT),1) + @echo " wolfMQTT: $(WOLFMQTT_ROOT)" +endif app.elf: $(OBJS) $(LDSCRIPT) $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -Wl,--start-group -lc -lm -lgcc -lnosys -Wl,--end-group -o $@ @@ -37,7 +228,91 @@ app.bin: app.elf %.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) -I$(WOLFSSL_ROOT) -c $< -o $@ + clean: - rm -f $(OBJS) app.elf app.bin + rm -f *.o app.elf app.bin + rm -f $(ROOT)/src/*.o + rm -f $(ROOT)/src/port/*.o +ifeq ($(ENABLE_TLS),1) + rm -f $(WOLFSSL_ROOT)/wolfcrypt/src/*.o + rm -f $(WOLFSSL_ROOT)/src/*.o +endif +ifeq ($(ENABLE_SSH),1) + rm -f $(WOLFSSH_ROOT)/src/*.o +endif +ifeq ($(ENABLE_MQTT),1) + rm -f $(WOLFMQTT_ROOT)/src/*.o +endif + +# Verify what features are compiled into the binary +verify: app.bin + @echo "=== Build Verification ===" + @echo "Checking compiled features in app.bin..." + @strings app.bin | grep -q "Initializing TLS server" && echo " ✓ TLS server enabled" || echo " ✗ TLS server disabled" + @strings app.bin | grep -q "Initializing HTTPS server" && echo " ✓ HTTPS server enabled" || echo " ✗ HTTPS server disabled" + @strings app.bin | grep -q "Initializing SSH server" && echo " ✓ SSH server enabled" || echo " ✗ SSH server disabled" + @strings app.bin | grep -q "Initializing MQTT client" && echo " ✓ MQTT client enabled" || echo " ✗ MQTT client disabled" + @echo "" + @echo "Binary size: $$(ls -lh app.bin | awk '{print $$5}')" + @echo "Build flags: TZEN=$(TZEN) ENABLE_TLS=$(ENABLE_TLS) ENABLE_HTTPS=$(ENABLE_HTTPS) ENABLE_SSH=$(ENABLE_SSH) ENABLE_MQTT=$(ENABLE_MQTT)" + +# 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/2097152, $$1+$$2, 2097152}')" + @echo "RAM usage (static): $$(arm-none-eabi-size app.elf | awk 'NR==2{printf "%.1f%% (%d / %d bytes)", ($$2+$$3)*100/655360, $$2+$$3, 655360}')" + +.PHONY: all clean verify size + +# ----------------------------------------------------------------------------- +# Help +# ----------------------------------------------------------------------------- +help: + @echo "STM32H563 wolfIP Build System" + @echo "" + @echo "Usage: make [target] [options]" + @echo "" + @echo "Targets:" + @echo " all Build app.bin (default)" + @echo " clean Remove build artifacts" + @echo " verify Check which features are compiled in" + @echo " size Show memory usage statistics" + @echo " help Show this help" + @echo "" + @echo "Options:" + @echo " TZEN=1 Enable TrustZone support" + @echo " ENABLE_TLS=1 Enable TLS server (requires wolfSSL)" + @echo " ENABLE_HTTPS=1 Enable HTTPS web server (requires TLS)" + @echo " ENABLE_SSH=1 Enable SSH server (requires TLS + wolfSSH)" + @echo " ENABLE_MQTT=1 Enable MQTT client (requires TLS + wolfMQTT)" + @echo " WOLFSSL_ROOT= Path to wolfSSL (default: ../wolfssl)" + @echo " WOLFSSH_ROOT= Path to wolfSSH (default: ../wolfssh)" + @echo " WOLFMQTT_ROOT= Path to wolfMQTT (default: ../wolfmqtt)" + @echo " CC= C compiler (default: arm-none-eabi-gcc)" + @echo " OBJCOPY= Objcopy tool (default: arm-none-eabi-objcopy)" + @echo "" + @echo "Examples:" + @echo " make # Basic TCP echo (port 7)" + @echo " make ENABLE_TLS=1 # TLS echo server (port 8443)" + @echo " make ENABLE_TLS=1 ENABLE_HTTPS=1 # TLS + HTTPS web (port 443)" + @echo " make ENABLE_TLS=1 ENABLE_SSH=1 # TLS + SSH shell (port 22)" + @echo " make ENABLE_TLS=1 ENABLE_MQTT=1 # TLS + MQTT client" + @echo " make ENABLE_TLS=1 ENABLE_HTTPS=1 ENABLE_SSH=1 ENABLE_MQTT=1 # Full featured" + @echo "" + @echo "Full Build Command (recommended):" + @echo " CC=arm-none-eabi-gcc OBJCOPY=arm-none-eabi-objcopy \\" + @echo " make ENABLE_TLS=1 ENABLE_HTTPS=1 ENABLE_SSH=1 ENABLE_MQTT=1" + @echo "" + @echo "Testing:" + @echo " nc 7 # TCP echo" + @echo " echo 'Hello' | openssl s_client -connect :8443 -quiet # TLS echo" + @echo " curl -k https:/// # HTTPS web server" + @echo " ssh admin@ # SSH (password: wolfip)" + @echo " mosquitto_sub -h test.mosquitto.org -t 'wolfip/status' -v # MQTT subscribe" -.PHONY: all clean +.PHONY: help diff --git a/src/port/stm32h563/README.md b/src/port/stm32h563/README.md index 7add454..07e03f7 100644 --- a/src/port/stm32h563/README.md +++ b/src/port/stm32h563/README.md @@ -2,6 +2,39 @@ This directory contains a bare-metal port of wolfIP for the STM32H563 microcontroller, featuring an Ethernet driver and TCP/IP echo server example. +## Quick Start + +1. **Build with all features:** + ```bash + cd src/port/stm32h563 + CC=arm-none-eabi-gcc OBJCOPY=arm-none-eabi-objcopy \ + make ENABLE_TLS=1 ENABLE_HTTPS=1 ENABLE_SSH=1 ENABLE_MQTT=1 + ``` + +2. **Flash to board:** + ```bash + openocd -f interface/stlink-dap.cfg -f target/stm32h5x.cfg \ + -c "program app.bin 0x08000000 verify reset exit" + ``` + +3. **Monitor UART output** (115200 baud on /dev/ttyACM0): + ```bash + screen /dev/ttyACM0 115200 + ``` + Get the device IP address from the DHCP output. + +4. **Test features** (replace `` with IP from step 3): + ```bash + # TCP Echo + echo "Hello" | nc 7 + + # HTTPS Web + curl -k https:/// + + # SSH (password: wolfip) + ssh admin@ + ``` + ## Hardware Requirements - STM32H563 development board (e.g., NUCLEO-H563ZI) @@ -27,18 +60,54 @@ sudo apt install gcc-arm-none-eabi openocd ```bash cd src/port/stm32h563 -make +CC=arm-none-eabi-gcc OBJCOPY=arm-none-eabi-objcopy make ``` This produces `app.elf` and `app.bin` for use with TZEN=0 (TrustZone disabled). -### TrustZone Enabled Build (Experimental) +### Verifying Build + +After building, verify which features are compiled in: + +```bash +make verify +``` + +Output example: +``` +=== Build Verification === +Checking compiled features in app.bin... + ✓ TLS server enabled + ✓ HTTPS server enabled + ✓ SSH server enabled + ✓ MQTT client enabled + +Binary size: 262K +``` + +### Checking Memory Usage + +```bash +make size +``` + +Output example: +``` +=== Memory Usage === + text data bss dec hex filename + 265560 1804 400676 668040 a3188 app.elf + +Flash usage: 12.7% (267364 / 2097152 bytes) +RAM usage (static): 61.4% (402480 / 655360 bytes) +``` + +### TrustZone Enabled Build ```bash make TZEN=1 ``` -> **Note:** TZEN=1 support is experimental. The Ethernet driver currently has issues receiving packets when TrustZone is enabled. +This builds firmware for execution in TrustZone secure mode with proper SAU, GTZC, and MPCBB configuration for Ethernet DMA access. ## Disabling TrustZone (Option Bytes) @@ -53,6 +122,27 @@ If your board has TrustZone enabled, you must disable it via option bytes before 5. Set TZEN to **0xC3** (disabled) 6. Click **Apply** +## TrustZone Support (TZEN=1) + +The TZEN=1 build provides full TrustZone support for running wolfIP in secure mode: + +- **SAU Configuration:** Enables ALLNS mode for non-secure DMA access to all undefined regions +- **GTZC/MPCBB:** Configures SRAM3 blocks (registers 36-39) as non-secure for Ethernet DMA buffers +- **TZSC:** Marks Ethernet MAC peripheral as non-secure for DMA operation +- **Secure Aliases:** Uses secure peripheral addresses (0x5xxxxxxx) for RCC, GPIO, GTZC +- **Separate ETHMEM:** Places Ethernet TX/RX buffers in dedicated non-secure SRAM region + +### Enabling TrustZone (Option Bytes) + +To use the TZEN=1 build, TrustZone must be enabled in the option bytes: + +1. Open STM32CubeProgrammer +2. Connect to the target +3. Go to **Option Bytes** tab +4. Find **TZEN** under "User Configuration" +5. Set TZEN to **0xB4** (enabled) +6. Click **Apply** + ### Using OpenOCD ```bash @@ -158,18 +248,506 @@ sudo ip link set up Replace `` with your Ethernet interface name (e.g., `eth0`, `enp5s0`). -## Testing +## Testing TCP Echo Server + +The default build provides a TCP echo server on port 7. + +### Building default build + +```bash +make +``` + +### Expected Serial Output + +``` +=== wolfIP STM32H563 Echo Server === +Initializing wolfIP stack... +Configuring GPIO for RMII... +Enabling Ethernet clocks... +Resetting Ethernet MAC... +Initializing Ethernet MAC... + PHY link: UP, PHY addr: 0x00000000 +DHCP configuration received: + IP: 192.168.0.197 + Mask: 255.255.255.0 + GW: 192.168.0.1 +Creating TCP socket on port 7... +Entering main loop. Ready for connections! +``` + +### Testing Echo Server + +```bash +# Get the device IP from serial output (DHCP) or use static IP + +# Test ICMP connectivity +ping + +# Test TCP echo server (port 7) +echo "Hello wolfIP!" | nc 7 +# Expected: "Hello wolfIP!" echoed back + +# Interactive TCP test +nc 7 +# Type messages and see them echoed back +``` + +## TLS + +The port includes optional TLS 1.3 support using wolfSSL. This enables secure encrypted communication. + +### Prerequisites + +Clone wolfSSL alongside wolfip: + +```bash +cd /path/to/parent +git clone https://github.com/wolfSSL/wolfssl.git +# wolfip should be at /path/to/parent/wolfip +``` + +### Building with TLS + +```bash +make ENABLE_TLS=1 +``` + +Or specify a custom wolfSSL path: -Once running, the echo server listens on TCP port 7: +```bash +make ENABLE_TLS=1 WOLFSSL_ROOT=/path/to/wolfssl +``` + +### Building with HTTPS Web Server + +The HTTPS web server provides a status page accessible via browser: + +```bash +make ENABLE_TLS=1 ENABLE_HTTPS=1 +``` + +### Building with SSH Server + +SSH server requires both wolfSSL and wolfSSH: + +```bash +# Clone wolfSSH alongside wolfip +cd /path/to/parent +git clone https://github.com/wolfSSL/wolfssh.git + +# Build with SSH support +make ENABLE_TLS=1 ENABLE_SSH=1 +``` + +Or specify a custom wolfSSH path: + +```bash +make ENABLE_TLS=1 ENABLE_SSH=1 WOLFSSH_ROOT=/path/to/wolfssh +``` + +### Full Featured Build + +Build with all features (TLS echo, HTTPS web server, and SSH shell): + +```bash +make ENABLE_TLS=1 ENABLE_HTTPS=1 ENABLE_SSH=1 +``` + +This provides: +- TCP echo server on port 7 +- TLS echo server on port 8443 +- HTTPS web server on port 443 +- SSH shell on port 22 + +### TLS Example Output + +With TLS enabled, you'll see additional output including the TLS server startup and TLS client test: + +``` +=== wolfIP STM32H563 Echo Server === +Initializing wolfIP stack... +... +DHCP configuration received: + IP: 192.168.0.197 + Mask: 255.255.255.0 + GW: 192.168.0.1 +Creating TCP socket on port 7... +Initializing TLS server on port 8443... +TLS: Initializing wolfSSL +TLS: Loading certificate +TLS: Loading private key +TLS: Server ready on port 8443 +Initializing HTTPS server on port 443... +HTTPS: Initializing wolfSSL +HTTPS: Loading certificate +HTTPS: Loading private key +HTTPS: Server ready +Initializing TLS client... +TLS Client: Initializing wolfSSL +TLS Client: Initialized +Entering main loop. Ready for connections! +Loop starting... + +--- TLS Client Test: Connecting to Google --- +Target: 142.250.189.174:443 +TLS Client: Connecting... +TLS Client: TLS handshake... +TLS Client: Connected! +TLS Client: Sending HTTP GET request... +TLS Client received 851 bytes: +HTTP/1.1 301 Moved Permanently +... +TLS Client: Passed! Connection closed after response +``` + +### Building TLS Mode + +```bash +make ENABLE_TLS=1 +``` + +### Testing the TLS Server + +The TLS echo server listens on port 8443. Test with OpenSSL: + +```bash +# Basic TLS connection test +(echo "Hello TLS!"; sleep 2) | openssl s_client -connect :8443 -quiet + +# View full handshake details +openssl s_client -connect :8443 -tls1_3 + +# View TLS session details +openssl s_client -connect :8443 -tls1_3 -brief +``` + +### Expected TLS Test Output + +``` +depth=0 CN = wolfIP-STM32H563, O = wolfSSL, C = US +verify error:num=18:self-signed certificate +Hello TLS! +``` + +The self-signed certificate warning is expected for development. Replace with a valid certificate for production. + +### TLS Client (Google Test) + +The TLS build includes a client example that connects to Google over HTTPS to verify outbound TLS connectivity. This runs automatically ~5 seconds after boot. + +**Example Output:** +``` +--- TLS Client Test: Connecting to Google --- +Target: 142.250.189.174:443 +TLS Client: Connecting... +TLS Client: Connection initiated +TLS Client: TLS handshake... +TLS Client: Connected! +TLS Client: Sending HTTP GET request... +TLS Client: Request sent +TLS Client received 851 bytes: +HTTP/1.1 301 Moved Permanently +Location: https://www.google.com/ +... +TLS Client: Passed! Connection closed after response +``` + +The 301 redirect is expected - Google redirects `google.com` to `www.google.com`. The "Passed!" message confirms the full TLS 1.3 handshake completed successfully. + +### TLS Configuration + +The TLS configuration is in `user_settings.h`: + +| Setting | Description | +|---------|-------------| +| TLS 1.3 only | `WOLFSSL_TLS13`, `NO_OLD_TLS` | +| Key Exchange | ECDHE with P-256 (secp256r1) | +| Cert Verify | RSA (most servers use RSA certs) | +| Ciphers | AES-GCM, ChaCha20-Poly1305 | +| SNI | Server Name Indication enabled | + +### TLS Files + +| File | Description | +|------|-------------| +| `user_settings.h` | wolfSSL compile-time configuration | +| `certs.h` | Embedded ECC P-256 test certificate | +| `tls_server.c/h` | TLS echo server implementation | +| `tls_client.c/h` | TLS client (for outbound connections) | + +### Generating Custom Certificates + +The included test certificate is for development only. Generate your own: ```bash -# Test with netcat -echo "Hello wolfIP!" | nc 192.168.12.11 7 +# Generate ECC P-256 key and self-signed certificate +openssl ecparam -genkey -name prime256v1 -out server_key.pem +openssl req -new -x509 -key server_key.pem -out server_cert.pem \ + -days 3650 -subj "/CN=my-device/O=my-org/C=US" -# Test with ping -ping 192.168.12.11 +# Convert to C header (update certs.h) +# Copy PEM content into certs.h as string literals ``` +## HTTPS Web Server + +When built with `ENABLE_HTTPS=1`, the device serves a status web page on port 443. + +### Building HTTPS Mode + +```bash +make ENABLE_TLS=1 ENABLE_HTTPS=1 +``` + +### Expected Serial Output (HTTPS Mode) + +``` +=== wolfIP STM32H563 Echo Server === +... +DHCP configuration received: + IP: 192.168.0.197 + Mask: 255.255.255.0 + GW: 192.168.0.1 +Creating TCP socket on port 7... +Initializing TLS server on port 8443... +TLS: Server ready on port 8443 +Initializing HTTPS server on port 443... +HTTPS: Initializing wolfSSL +HTTPS: Loading certificate +HTTPS: Loading private key +HTTPS: Server ready +Entering main loop. Ready for connections! +``` + +### Testing HTTPS + +```bash +# Using curl (skip certificate verification for self-signed cert) +curl -k https:/// + +# Using openssl to see TLS details +openssl s_client -connect :443 -tls1_3 + +# Using a browser +# Navigate to https:/// and accept the self-signed certificate warning +``` + +### Expected HTTPS Response + +```html +wolfIP STM32H563 + +

wolfIP Status

+ + + + +
DeviceSTM32H563
IP Address10.0.4.117
Uptime1234 sec
TLSTLS 1.3
+``` + +### HTTPS Status Page + +The status page displays: +- Device type (STM32H563) +- IP address (dynamically updated from DHCP or static config) +- Uptime in seconds (continuously updated) +- TLS version (TLS 1.3) + +## SSH Server + +When built with `ENABLE_SSH=1`, the device provides an SSH shell on port 22. + +### Building SSH Mode + +```bash +# First, clone wolfSSH alongside wolfip +cd /path/to/parent +git clone https://github.com/wolfSSL/wolfssh.git + +# Build with SSH support (requires TLS) +cd wolfip/src/port/stm32h563 +make ENABLE_TLS=1 ENABLE_SSH=1 +``` + +### Expected Serial Output (SSH Mode) + +``` +=== wolfIP STM32H563 Echo Server === +... +Initializing TLS server on port 8443... +TLS: Server ready on port 8443 +Initializing SSH server on port 22... +SSH: Initializing wolfSSH +SSH: Loading host key +SSH: Server ready on port 22 +Entering main loop. Ready for connections! +``` + +### SSH Credentials + +| Username | Password | +|----------|----------| +| admin | wolfip | + +**Warning:** These are default credentials for testing. Change them in `ssh_server.c` for production use. + +### Testing SSH + +```bash +# Connect via SSH +ssh admin@ + +# Enter password: wolfip +``` + +### SSH Shell Commands + +Once connected, the following commands are available: + +| Command | Description | +|---------|-------------| +| help | Show available commands | +| info | Show device information | +| uptime | Show uptime in seconds | +| exit | Close SSH session | + +### SSH Example Session + +``` +$ ssh admin@192.168.0.197 +admin@192.168.0.197's password: +=== wolfIP SSH Shell === +Type 'help' for available commands. + +wolfip> info + +Device: STM32H563 +Stack: wolfIP +SSH: wolfSSH +TLS: wolfSSL TLS 1.3 + +wolfip> uptime + +Uptime: 1234 seconds + +wolfip> exit + +Goodbye! +Connection to 192.168.0.197 closed. +``` + +### Generating Custom SSH Host Key + +The included test host key is for development only. Generate your own: + +```bash +# Generate ECC P-256 key +openssl ecparam -genkey -name prime256v1 -out ssh_host_key.pem +openssl ec -in ssh_host_key.pem -outform DER -out ssh_host_key.der + +# Convert to C header +xxd -i ssh_host_key.der > ssh_keys.h + +# Update the array name in ssh_keys.h to ssh_host_key_der +``` + +## MQTT Client + +When built with `ENABLE_MQTT=1`, the device includes an MQTT client that publishes status messages to a broker. + +### Prerequisites + +Clone wolfMQTT alongside wolfip: + +```bash +cd /path/to/parent +git clone https://github.com/wolfSSL/wolfMQTT.git +# wolfip should be at /path/to/parent/wolfip +``` + +### Building MQTT Mode + +```bash +# MQTT requires TLS +make ENABLE_TLS=1 ENABLE_MQTT=1 +``` + +Or specify a custom wolfMQTT path: + +```bash +make ENABLE_TLS=1 ENABLE_MQTT=1 WOLFMQTT_ROOT=/path/to/wolfmqtt +``` + +### Expected Serial Output (MQTT Mode) + +``` +=== wolfIP STM32H563 Echo Server === +... +Initializing TLS server on port 8443... +TLS: Server ready on port 8443 +Initializing MQTT client... +MQTT: Initializing client +MQTT: Initialized, connecting to 54.36.178.49:8883 +Entering main loop. Ready for connections! +Loop starting... +MQTT: Connecting... +MQTT: TCP connected +MQTT: TLS handshake... +MQTT: TLS connected +MQTT: Sending CONNECT... +MQTT: Connected to broker +``` + +### MQTT Configuration + +| Setting | Default Value | +|---------|--------------| +| Broker IP | 54.36.178.49 (test.mosquitto.org) | +| Broker Port | 8883 (TLS) | +| Client ID | wolfip-stm32h563 | +| Publish Topic | wolfip/status | +| Keep-alive | 60 seconds | +| QoS | 0 (fire and forget) | + +### Subscribing to Device Messages + +To see messages published by the device: + +```bash +# Subscribe to the status topic +mosquitto_sub -h test.mosquitto.org -t "wolfip/status" -v + +# Expected output (every 60 seconds): +# wolfip/status STM32H563 online, uptime: 60s +# wolfip/status STM32H563 online, uptime: 120s +``` + +### Full Featured Build + +Build with all features (TLS, HTTPS, SSH, and MQTT): + +```bash +make ENABLE_TLS=1 ENABLE_HTTPS=1 ENABLE_SSH=1 ENABLE_MQTT=1 +``` + +This provides: +- TCP echo server on port 7 +- TLS echo server on port 8443 +- HTTPS web server on port 443 +- SSH shell on port 22 +- MQTT client publishing to test.mosquitto.org + +### MQTT Files + +| File | Description | +|------|-------------| +| `mqtt_client.c/h` | MQTT client state machine and API | +| `../wolfmqtt_io.c` | wolfMQTT I/O glue layer for wolfIP sockets | +| `user_settings.h` | wolfMQTT compile-time configuration | + ## Files | File | Description | @@ -184,19 +762,17 @@ ping 192.168.12.11 | `target_tzen.ld` | Linker script for TZEN=1 | | `config.h` | Build configuration | | `Makefile` | Build system | - -## TrustZone Support (TZEN=1) - Experimental - -The TZEN=1 build adds TrustZone support: - -- **SAU Configuration:** Marks memory regions for non-secure DMA access -- **GTZC/MPCBB:** Configures SRAM3 blocks for Ethernet DMA -- **Secure Aliases:** Uses secure peripheral addresses (0x5xxxxxxx) -- **Separate ETHMEM:** Places Ethernet buffers in dedicated non-secure SRAM - -### Current Limitations - -The TZEN=1 build compiles and runs, but the Ethernet driver experiences RBU (Receive Buffer Unavailable) errors. This appears to be a DMA access issue that requires further investigation. +| `user_settings.h` | wolfSSL/wolfSSH/wolfMQTT configuration | +| `certs.h` | Embedded TLS certificates (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) | +| `../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) | +| `mqtt_client.c/h` | MQTT client state machine (MQTT builds only) | +| `../wolfssl_io.c` | wolfSSL I/O callbacks for wolfIP (TLS builds only) | +| `../wolfssh_io.c` | wolfSSH I/O callbacks for wolfIP (SSH builds only) | +| `../wolfmqtt_io.c` | wolfMQTT I/O callbacks for wolfIP (MQTT builds only) | ## Troubleshooting @@ -205,18 +781,74 @@ The TZEN=1 build compiles and runs, but the Ethernet driver experiences RBU (Rec - Check USB connection and correct serial port - Verify baud rate is 115200 - Try resetting the board +- Check that you're monitoring the correct port (usually /dev/ttyACM0) ### OpenOCD Connection Fails - Ensure ST-LINK drivers are installed - Try `sudo` if permission denied - Check that no other debugger is connected +- Use `openocd --version` to verify installation + +### Features Not Initializing + +If you don't see "Initializing TLS/HTTPS/SSH/MQTT" messages in UART output: + +**Problem:** Built without proper `ENABLE_*` flags + +**Solution:** Rebuild with required flags: +```bash +CC=arm-none-eabi-gcc OBJCOPY=arm-none-eabi-objcopy \ +make clean && make ENABLE_TLS=1 ENABLE_HTTPS=1 ENABLE_SSH=1 ENABLE_MQTT=1 +``` + +**Verify build:** Check that strings exist in binary: +```bash +strings app.bin | grep "Initializing TLS" +strings app.bin | grep "Initializing HTTPS" +strings app.bin | grep "Initializing SSH" +strings app.bin | grep "Initializing MQTT" +``` ### Ethernet Not Responding - Verify physical Ethernet connection -- Check that host PC is on same subnet (192.168.12.x) -- Confirm PHY link is up (check serial output for "link" status) +- Check that host PC is on same subnet +- Confirm PHY link is up (check serial output for "PHY link: UP") +- If using DHCP, ensure a DHCP server is available on the network +- Try pinging the device: `ping ` + +### DHCP Netmask Display Issue + +**Known Issue:** The DHCP netmask may display incorrectly in UART output (showing gateway IP instead). + +**Workaround:** The device still functions correctly - the netmask is stored properly internally. This is a display-only issue in the UART output. You can verify correct operation by testing network connectivity. + +**Example UART output:** +``` +DHCP configuration received: + IP: 10.0.4.117 + Mask: 10.0.4.1 <- Shows gateway instead of 255.255.255.0 + GW: 10.0.4.1 +``` + +### HTTPS Content-Length Error + +If `curl` shows "Invalid Content-Length" error: +- This is a known issue with the httpd implementation +- Use `-k` flag: `curl -k https:///` +- Or access via web browser and accept the self-signed certificate + +### Build Fails with "arpa/inet.h not found" + +**Problem:** wolfSSL trying to include system headers + +**Solution:** Ensure `user_settings.h` contains: +```c +#define WOLFSSL_NO_SOCK +#define NO_WOLFSSL_DIR +``` +These are already in the default `user_settings.h`. ### TrustZone Errors @@ -224,6 +856,17 @@ If you see `stm32h5x.cpu in Secure state` but built with TZEN=0: - The board has TrustZone enabled - Either rebuild with `make TZEN=1` or disable TrustZone via option bytes +### Compiler Not Found + +If make fails with "command not found": +```bash +# Install ARM toolchain +sudo apt install gcc-arm-none-eabi + +# Or specify full path +make CC=/usr/bin/arm-none-eabi-gcc OBJCOPY=/usr/bin/arm-none-eabi-objcopy +``` + ## License This code is part of wolfIP and is licensed under GPLv3. See the LICENSE file in the repository root for details. diff --git a/src/port/stm32h563/certs.h b/src/port/stm32h563/certs.h new file mode 100644 index 0000000..9346a28 --- /dev/null +++ b/src/port/stm32h563/certs.h @@ -0,0 +1,53 @@ +/* certs.h + * + * Embedded TLS certificates for STM32H563 wolfIP example + * + * Copyright (C) 2024 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * WARNING: These are TEST certificates only. Generate your own for production. + * + * To generate new certificates: + * openssl ecparam -genkey -name prime256v1 -out server_key.pem + * openssl req -new -x509 -key server_key.pem -out server_cert.pem -days 3650 \ + * -subj "/CN=your-device/O=your-org/C=US" + * + * Then convert to C array: + * xxd -i server_cert.pem > certs.h + * xxd -i server_key.pem >> certs.h + * + * Or use the PEM strings directly as shown below. + */ + +#ifndef CERTS_H +#define CERTS_H + +/* ECC P-256 Server Certificate (PEM format) */ +static const char server_cert_pem[] = +"-----BEGIN CERTIFICATE-----\n" +"MIIByTCCAW+gAwIBAgIUW3k96+M3BtW7CJRDEO/u5BaaGjgwCgYIKoZIzj0EAwIw\n" +"OjEZMBcGA1UEAwwQd29sZklQLVNUTTMySDU2MzEQMA4GA1UECgwHd29sZlNTTDEL\n" +"MAkGA1UEBhMCVVMwHhcNMjYwMTIwMTgwMjU0WhcNMzYwMTE4MTgwMjU0WjA6MRkw\n" +"FwYDVQQDDBB3b2xmSVAtU1RNMzJINTYzMRAwDgYDVQQKDAd3b2xmU1NMMQswCQYD\n" +"VQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIIoRSUxD9kkXV67s06t\n" +"7yjcC7TZMIvoCwg8AJLFn/lcy9QklySeAkgWWXJrUHTM0XPYhqX9BRjF9aT4AdJ7\n" +"RTyjUzBRMB0GA1UdDgQWBBRxfBfKe/Ew5d8SArakH1z9DjxK9jAfBgNVHSMEGDAW\n" +"gBRxfBfKe/Ew5d8SArakH1z9DjxK9jAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49\n" +"BAMCA0gAMEUCIEUB8ArsbYI58PGtcy9KIdR6A3z5KCQblTXZWnIE7EDUAiEA8Oyi\n" +"LwVAHQ4M2+TcVwe4LQ+xG9F6uSmu4t/psG0IT+s=\n" +"-----END CERTIFICATE-----\n"; + +static const int server_cert_pem_len = sizeof(server_cert_pem); + +/* ECC P-256 Server Private Key (PEM format) */ +static const char server_key_pem[] = +"-----BEGIN EC PRIVATE KEY-----\n" +"MHcCAQEEIJH0YKpGLqYi2CESEXZu1gS75F7XQ+rEAHPjj0u3WGmGoAoGCCqGSM49\n" +"AwEHoUQDQgAEgihFJTEP2SRdXruzTq3vKNwLtNkwi+gLCDwAksWf+VzL1CSXJJ4C\n" +"SBZZcmtQdMzRc9iGpf0FGMX1pPgB0ntFPA==\n" +"-----END EC PRIVATE KEY-----\n"; + +static const int server_key_pem_len = sizeof(server_key_pem); + +#endif /* CERTS_H */ diff --git a/src/port/stm32h563/config.h b/src/port/stm32h563/config.h index aeb1277..b3f5f79 100644 --- a/src/port/stm32h563/config.h +++ b/src/port/stm32h563/config.h @@ -28,11 +28,11 @@ #define ETHERNET #define LINK_MTU 1536 -#define MAX_TCPSOCKETS 4 +#define MAX_TCPSOCKETS 12 /* Need enough for listen + accepted sockets */ #define MAX_UDPSOCKETS 2 -#define MAX_ICMPSOCKETS 2 -#define RXBUF_SIZE (LINK_MTU * 16) -#define TXBUF_SIZE (LINK_MTU * 16) +#define MAX_ICMPSOCKETS 1 /* Reduced from 2 */ +#define RXBUF_SIZE (LINK_MTU * 8) /* Reduced from 16 */ +#define TXBUF_SIZE (LINK_MTU * 8) /* Reduced from 16 */ #define MAX_NEIGHBORS 16 diff --git a/src/port/stm32h563/main.c b/src/port/stm32h563/main.c index 67980bd..e9fe260 100644 --- a/src/port/stm32h563/main.c +++ b/src/port/stm32h563/main.c @@ -24,6 +24,104 @@ #include "wolfip.h" #include "stm32h5_eth.h" +#ifdef ENABLE_TLS +#include "tls_server.h" +#include "tls_client.h" +#define TLS_PORT 8443 +#endif + +#ifdef ENABLE_HTTPS +#include "http/httpd.h" +#include "certs.h" +#define HTTPS_WEB_PORT 443 +#endif + +#ifdef ENABLE_SSH +#include "ssh_server.h" +#define SSH_PORT 22 +#endif + +#ifdef ENABLE_MQTT +#include "mqtt_client.h" +#endif + +#ifdef ENABLE_TLS + +/* Google IP for TLS client test (run: dig +short google.com) */ +#define GOOGLE_IP "142.250.189.174" +#define GOOGLE_HTTPS_PORT 443 + +/* TLS client test state */ +static int tls_client_test_started = 0; +static int tls_client_test_done = 0; +#endif + +#ifdef ENABLE_HTTPS +/* HTTPS server using wolfIP httpd */ +static struct httpd https_server; +static WOLFSSL_CTX *https_ssl_ctx; +static uint32_t https_uptime_sec; +static ip4 https_device_ip; + +/* Status page handler */ +static int https_status_handler(struct httpd *httpd, struct http_client *hc, + struct http_request *req) +{ + char response[512]; + char ip_str[16]; + char uptime_str[12]; + int len; + + (void)httpd; + (void)req; + + /* Format IP address (stored in network byte order) */ + { + uint8_t *b = (uint8_t *)&https_device_ip; + char *p = ip_str; + for (int i = 3; i >= 0; i--) { /* Reverse order for network byte order */ + int val = b[i]; + if (val >= 100) { *p++ = '0' + val / 100; val %= 100; } + if (val >= 10 || b[i] >= 100) { *p++ = '0' + val / 10; val %= 10; } + *p++ = '0' + val; + if (i > 0) *p++ = '.'; + } + *p = '\0'; + } + + /* Format uptime */ + { + uint32_t val = https_uptime_sec; + char tmp[12]; + int i = 0, j = 0; + if (val == 0) { uptime_str[0] = '0'; uptime_str[1] = '\0'; } + else { + while (val > 0) { tmp[i++] = '0' + (val % 10); val /= 10; } + while (i > 0) { uptime_str[j++] = tmp[--i]; } + uptime_str[j] = '\0'; + } + } + + /* Build HTML response */ + len = snprintf(response, sizeof(response), + "wolfIP STM32H563" + "" + "

wolfIP Status

" + "" + "" + "" + "" + "
DeviceSTM32H563
IP Address%s
Uptime%s sec
TLSTLS 1.3
", + ip_str, uptime_str); + + http_send_response_headers(hc, HTTP_STATUS_OK, "OK", "text/html", len); + http_send_response_body(hc, response, len); + return 0; +} +#endif + #define ECHO_PORT 7 #define RX_BUF_SIZE 1024 @@ -334,6 +432,26 @@ static void eth_gpio_init(void) gpio_eth_pin(GPIOG_BASE, 13); /* TXD0 */ } +#ifdef ENABLE_TLS +/* Callback for TLS client responses */ +static void tls_response_cb(const char *data, int len, void *ctx) +{ + (void)ctx; + uart_puts("TLS Client received "); + uart_putdec((uint32_t)len); + uart_puts(" bytes:\n"); + /* Print first 200 chars of response */ + for (int 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 + static void echo_cb(int fd, uint16_t event, void *arg) { struct wolfIP *s = (struct wolfIP *)arg; @@ -511,11 +629,166 @@ int main(void) (void)wolfIP_sock_bind(IPStack, listen_fd, (struct wolfIP_sockaddr *)&addr, sizeof(addr)); (void)wolfIP_sock_listen(IPStack, listen_fd, 1); +#ifdef ENABLE_TLS + uart_puts("Initializing TLS server on port 8443...\n"); + if (tls_server_init(IPStack, TLS_PORT, uart_puts) < 0) { + uart_puts("ERROR: TLS server init failed\n"); + } + + uart_puts("Initializing TLS client...\n"); + if (tls_client_init(IPStack, uart_puts) < 0) { + uart_puts("ERROR: TLS client init failed\n"); + } +#endif + +#ifdef ENABLE_HTTPS + uart_puts("Initializing HTTPS server on port 443...\n"); + + /* Create SSL context for HTTPS */ + https_ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); + if (https_ssl_ctx) { + wolfSSL_CTX_use_certificate_buffer(https_ssl_ctx, + (const unsigned char *)server_cert_pem, strlen(server_cert_pem), + SSL_FILETYPE_PEM); + wolfSSL_CTX_use_PrivateKey_buffer(https_ssl_ctx, + (const unsigned char *)server_key_pem, strlen(server_key_pem), + SSL_FILETYPE_PEM); + + if (httpd_init(&https_server, IPStack, HTTPS_WEB_PORT, https_ssl_ctx) == 0) { + httpd_register_handler(&https_server, "/", https_status_handler); + uart_puts("HTTPS: Server ready on port 443\n"); + } else { + uart_puts("ERROR: HTTPS server init failed\n"); + } + } else { + uart_puts("ERROR: HTTPS SSL context failed\n"); + } +#endif + +#ifdef ENABLE_SSH + uart_puts("Initializing SSH server on port 22...\n"); + if (ssh_server_init(IPStack, SSH_PORT, uart_puts) < 0) { + uart_puts("ERROR: SSH server init failed\n"); + } +#endif + +#ifdef ENABLE_MQTT + uart_puts("Initializing MQTT client...\n"); + { + mqtt_client_config_t mqtt_config = { + .broker_ip = "54.36.178.49", /* test.mosquitto.org IP (updated) */ + .broker_port = 8883, /* TLS port */ + .client_id = "wolfip-stm32h563", + .publish_topic = "wolfip/status", + .keep_alive_sec = 60 + }; + if (mqtt_client_init(IPStack, &mqtt_config, uart_puts) < 0) { + uart_puts("ERROR: MQTT client init failed\n"); + } + } +#endif + uart_puts("Entering main loop. Ready for connections!\n"); uart_puts("Loop starting...\n"); for (;;) { (void)wolfIP_poll(IPStack, tick++); + +#ifdef ENABLE_HTTPS + /* Update HTTPS server status info for handler */ + wolfIP_ipconfig_get(IPStack, &https_device_ip, NULL, NULL); + https_uptime_sec = (uint32_t)(tick / 1000); +#endif + +#ifdef ENABLE_SSH + /* Poll SSH server */ + ssh_server_poll(); +#endif + +#ifdef ENABLE_MQTT + /* Poll MQTT client */ + mqtt_client_poll(); + + /* Publish status periodically (every ~60 seconds) */ + { + static uint64_t last_publish_tick = 0; + if (mqtt_client_is_connected() && + (tick - last_publish_tick) > 60000) { + char status_msg[64]; + ip4 ip = 0; + wolfIP_ipconfig_get(IPStack, &ip, NULL, NULL); + + /* Format: "STM32H563 online, IP: x.x.x.x, uptime: XXXXX" */ + strcpy(status_msg, "STM32H563 online, uptime: "); + { + char num_buf[12]; + uint32_t uptime = (uint32_t)(tick / 1000); + int i = 0, j; + char tmp[12]; + + if (uptime == 0) { + num_buf[0] = '0'; + num_buf[1] = '\0'; + } else { + while (uptime > 0 && i < 11) { + tmp[i++] = '0' + (uptime % 10); + uptime /= 10; + } + j = 0; + while (i > 0) { + num_buf[j++] = tmp[--i]; + } + num_buf[j] = '\0'; + } + strcat(status_msg, num_buf); + } + strcat(status_msg, "s"); + + mqtt_client_publish(status_msg); + last_publish_tick = tick; + } + } +#endif + +#ifdef ENABLE_TLS + /* TLS client test: connect to Google after network settles */ + if (!tls_client_test_started && tick > 5000) { + uart_puts("\n--- TLS Client Test: Connecting to Google ---\n"); + uart_puts("Target: "); + uart_puts(GOOGLE_IP); + uart_puts(":"); + uart_putdec(GOOGLE_HTTPS_PORT); + uart_puts("\n"); + + if (tls_client_connect(GOOGLE_IP, GOOGLE_HTTPS_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; + } + + /* Poll TLS client state machine */ + tls_client_poll(); + + /* Send HTTP request once TLS handshake completes */ + if (tls_client_is_connected() && !tls_client_test_done) { + static int request_sent = 0; + if (!request_sent) { + const char *http_req = "GET / HTTP/1.1\r\n" + "Host: google.com\r\n" + "Connection: close\r\n\r\n"; + uart_puts("TLS Client: Sending HTTP GET request...\n"); + if (tls_client_send(http_req, (int)strlen(http_req)) > 0) { + uart_puts("TLS Client: Request sent\n"); + } else { + uart_puts("TLS Client: Send failed\n"); + } + request_sent = 1; + } + } +#endif + /* Toggle LED every ~256K iterations as heartbeat */ if ((tick & 0x3FFFF) == 0) { led_toggle(); diff --git a/src/port/stm32h563/mqtt_client.c b/src/port/stm32h563/mqtt_client.c new file mode 100644 index 0000000..f39eff5 --- /dev/null +++ b/src/port/stm32h563/mqtt_client.c @@ -0,0 +1,585 @@ +/* mqtt_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 "mqtt_client.h" +#include +#include +#include +#include + +/* Configuration defaults */ +#define DEFAULT_BROKER_IP "54.36.178.49" /* test.mosquitto.org */ +#define DEFAULT_BROKER_PORT 8883 /* TLS port */ +#define DEFAULT_CLIENT_ID "wolfip-stm32h563" +#define DEFAULT_PUBLISH_TOPIC "wolfip/status" +#define DEFAULT_KEEP_ALIVE 60 /* seconds */ + +/* Buffer sizes */ +#define MQTT_TX_BUF_SIZE 512 +#define MQTT_RX_BUF_SIZE 512 +#define MQTT_CMD_TIMEOUT_MS 30000 + +/* MQTT client state machine */ +typedef enum { + MQTT_STATE_IDLE, + MQTT_STATE_NET_CONNECT, + MQTT_STATE_TLS_CONNECT, + MQTT_STATE_MQTT_CONNECT, + MQTT_STATE_CONNECTED, + MQTT_STATE_PUBLISHING, + MQTT_STATE_DISCONNECTING, + MQTT_STATE_ERROR +} mqtt_state_t; + +/* Client context */ +static struct { + struct wolfIP *stack; + MqttClient client; + MqttNet net; + MqttConnect connect; + MqttPublish publish; + WOLFSSL_CTX *ssl_ctx; + WOLFSSL *ssl; + void *io_ctx; + mqtt_state_t state; + mqtt_debug_cb debug_cb; + uint8_t tx_buf[MQTT_TX_BUF_SIZE]; + uint8_t rx_buf[MQTT_RX_BUF_SIZE]; + char broker_ip[32]; + char client_id[32]; + char publish_topic[64]; + uint16_t broker_port; + uint16_t keep_alive; + int socket_fd; + int initialized; + int publish_pending; + char publish_msg[128]; +} ctx; + +/* External functions from wolfmqtt_io.c */ +extern void *wolfMQTT_Init_wolfIP(MqttNet *net, struct wolfIP *stack); +extern void wolfMQTT_Cleanup_wolfIP(void *context); +extern int wolfMQTT_GetFd_wolfIP(void *context); +extern void wolfMQTT_SetFd_wolfIP(void *context, int fd); +extern int wolfMQTT_IsConnected_wolfIP(void *context); + +/* 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 (ctx.debug_cb) { + ctx.debug_cb(msg); + } +} + +/* Format number to string */ +static void uint_to_str(uint32_t val, char *buf) +{ + char tmp[12]; + int i = 0; + int j = 0; + + if (val == 0) { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + while (val > 0) { + tmp[i++] = '0' + (val % 10); + val /= 10; + } + + while (i > 0) { + buf[j++] = tmp[--i]; + } + buf[j] = '\0'; +} + +/* TLS receive callback for wolfMQTT */ +static int mqtt_tls_read(void *context, byte *buf, int buf_len, int timeout_ms) +{ + int ret; + (void)timeout_ms; + (void)context; + + if (!ctx.ssl || !buf) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + ret = wolfSSL_read(ctx.ssl, buf, buf_len); + if (ret < 0) { + int err = wolfSSL_get_error(ctx.ssl, ret); + if (err == WOLFSSL_ERROR_WANT_READ || err == WOLFSSL_ERROR_WANT_WRITE) { + return MQTT_CODE_CONTINUE; + } + return MQTT_CODE_ERROR_NETWORK; + } + if (ret == 0) { + return MQTT_CODE_ERROR_NETWORK; + } + return ret; +} + +/* TLS send callback for wolfMQTT */ +static int mqtt_tls_write(void *context, const byte *buf, int buf_len, + int timeout_ms) +{ + int ret; + (void)timeout_ms; + (void)context; + + if (!ctx.ssl || !buf) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + ret = wolfSSL_write(ctx.ssl, buf, buf_len); + if (ret < 0) { + int err = wolfSSL_get_error(ctx.ssl, ret); + if (err == WOLFSSL_ERROR_WANT_READ || err == WOLFSSL_ERROR_WANT_WRITE) { + return MQTT_CODE_CONTINUE; + } + return MQTT_CODE_ERROR_NETWORK; + } + return ret; +} + +int mqtt_client_init(struct wolfIP *stack, const mqtt_client_config_t *config, + mqtt_debug_cb debug) +{ + int ret; + + memset(&ctx, 0, sizeof(ctx)); + ctx.stack = stack; + ctx.debug_cb = debug; + ctx.state = MQTT_STATE_IDLE; + ctx.socket_fd = -1; + + /* Apply configuration */ + if (config) { + if (config->broker_ip) { + strncpy(ctx.broker_ip, config->broker_ip, sizeof(ctx.broker_ip) - 1); + } + if (config->broker_port > 0) { + ctx.broker_port = config->broker_port; + } + if (config->client_id) { + strncpy(ctx.client_id, config->client_id, sizeof(ctx.client_id) - 1); + } + if (config->publish_topic) { + strncpy(ctx.publish_topic, config->publish_topic, + sizeof(ctx.publish_topic) - 1); + } + if (config->keep_alive_sec > 0) { + ctx.keep_alive = config->keep_alive_sec; + } + } + + /* Apply defaults for unset values */ + if (ctx.broker_ip[0] == '\0') { + strncpy(ctx.broker_ip, DEFAULT_BROKER_IP, sizeof(ctx.broker_ip) - 1); + } + if (ctx.broker_port == 0) { + ctx.broker_port = DEFAULT_BROKER_PORT; + } + if (ctx.client_id[0] == '\0') { + strncpy(ctx.client_id, DEFAULT_CLIENT_ID, sizeof(ctx.client_id) - 1); + } + if (ctx.publish_topic[0] == '\0') { + strncpy(ctx.publish_topic, DEFAULT_PUBLISH_TOPIC, + sizeof(ctx.publish_topic) - 1); + } + if (ctx.keep_alive == 0) { + ctx.keep_alive = DEFAULT_KEEP_ALIVE; + } + + debug_print("MQTT: Initializing client\n"); + + /* Initialize wolfMQTT I/O callbacks for wolfIP */ + ctx.io_ctx = wolfMQTT_Init_wolfIP(&ctx.net, stack); + if (!ctx.io_ctx) { + debug_print("MQTT: Failed to init I/O\n"); + return -1; + } + + /* Initialize MQTT client */ + ret = MqttClient_Init(&ctx.client, &ctx.net, NULL, ctx.tx_buf, + MQTT_TX_BUF_SIZE, ctx.rx_buf, MQTT_RX_BUF_SIZE, MQTT_CMD_TIMEOUT_MS); + if (ret != MQTT_CODE_SUCCESS) { + debug_print("MQTT: Client init failed\n"); + wolfMQTT_Cleanup_wolfIP(ctx.io_ctx); + return -1; + } + + /* Initialize TLS context */ + ctx.ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + if (!ctx.ssl_ctx) { + debug_print("MQTT: TLS context failed\n"); + MqttClient_DeInit(&ctx.client); + wolfMQTT_Cleanup_wolfIP(ctx.io_ctx); + return -1; + } + + /* Disable certificate verification for test broker + * (production should use proper CA certificates) */ + wolfSSL_CTX_set_verify(ctx.ssl_ctx, SSL_VERIFY_NONE, NULL); + + /* Register wolfIP I/O callbacks on the context */ + wolfSSL_SetIO_wolfIP_CTX(ctx.ssl_ctx, stack); + + ctx.initialized = 1; + ctx.state = MQTT_STATE_NET_CONNECT; + + debug_print("MQTT: Initialized, connecting to "); + debug_print(ctx.broker_ip); + debug_print(":"); + { + char port_str[8]; + uint_to_str(ctx.broker_port, port_str); + debug_print(port_str); + } + debug_print("\n"); + + return 0; +} + +/* Handle network connection state */ +static int handle_net_connect(void) +{ + int ret; + static uint32_t connect_polls = 0; + struct wolfIP_sockaddr_in addr; + + if (ctx.socket_fd < 0) { + ctx.socket_fd = wolfIP_sock_socket(ctx.stack, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (ctx.socket_fd < 0) { + debug_print("MQTT: Socket create failed\n"); + ctx.state = MQTT_STATE_ERROR; + return -1; + } + debug_print("MQTT: Connecting...\n"); + connect_polls = 0; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(ctx.broker_port); + addr.sin_addr.s_addr = ee32(atoip4(ctx.broker_ip)); + + ret = wolfIP_sock_connect(ctx.stack, ctx.socket_fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + + if (ret == 0) { + debug_print("MQTT: TCP connected\n"); + wolfMQTT_SetFd_wolfIP(ctx.io_ctx, ctx.socket_fd); + ctx.state = MQTT_STATE_TLS_CONNECT; + connect_polls = 0; + return 0; + } + + if (ret == -WOLFIP_EAGAIN || ret == -11) { + connect_polls++; + return 0; /* Still connecting (EAGAIN/EINPROGRESS) */ + } + + debug_print("MQTT: TCP connect failed\n"); + ctx.state = MQTT_STATE_ERROR; + return -1; +} + +/* Handle TLS handshake state */ +static int handle_tls_connect(void) +{ + int ret; + + if (!ctx.ssl) { + ctx.ssl = wolfSSL_new(ctx.ssl_ctx); + if (!ctx.ssl) { + debug_print("MQTT: TLS session create failed\n"); + ctx.state = MQTT_STATE_ERROR; + return -1; + } + + /* Set SNI for hostname-based virtual hosting */ + wolfSSL_UseSNI(ctx.ssl, WOLFSSL_SNI_HOST_NAME, ctx.broker_ip, + (word16)strlen(ctx.broker_ip)); + + /* Set wolfIP socket I/O callbacks */ + wolfSSL_SetIO_wolfIP(ctx.ssl, ctx.socket_fd); + + debug_print("MQTT: TLS handshake...\n"); + } + + ret = wolfSSL_connect(ctx.ssl); + if (ret == WOLFSSL_SUCCESS) { + debug_print("MQTT: TLS connected\n"); + + /* Replace net callbacks with TLS versions */ + ctx.net.read = mqtt_tls_read; + ctx.net.write = mqtt_tls_write; + + ctx.state = MQTT_STATE_MQTT_CONNECT; + return 0; + } + + int err = wolfSSL_get_error(ctx.ssl, ret); + if (err == WOLFSSL_ERROR_WANT_READ || err == WOLFSSL_ERROR_WANT_WRITE) { + return 0; /* Still handshaking */ + } + + debug_print("MQTT: TLS handshake failed\n"); + ctx.state = MQTT_STATE_ERROR; + return -1; +} + +/* Handle MQTT CONNECT state */ +static int handle_mqtt_connect(void) +{ + int ret; + static int connect_sent = 0; + + /* Set up MQTT connect parameters (only once) */ + if (!connect_sent) { + memset(&ctx.connect, 0, sizeof(ctx.connect)); + ctx.connect.keep_alive_sec = ctx.keep_alive; + ctx.connect.clean_session = 1; + ctx.connect.client_id = ctx.client_id; + connect_sent = 1; + debug_print("MQTT: Sending CONNECT...\n"); + } + + ret = MqttClient_Connect(&ctx.client, &ctx.connect); + + if (ret == MQTT_CODE_SUCCESS) { + debug_print("MQTT: Connected to broker\n"); + ctx.state = MQTT_STATE_CONNECTED; + connect_sent = 0; + return 0; + } + + if (ret == MQTT_CODE_CONTINUE) { + return 0; /* Still connecting */ + } + + debug_print("MQTT: CONNECT failed\n"); + ctx.state = MQTT_STATE_ERROR; + connect_sent = 0; + return -1; +} + +/* Handle connected state - process pending publishes */ +static int handle_connected(void) +{ + int ret; + + /* Check for pending publish */ + if (ctx.publish_pending) { + memset(&ctx.publish, 0, sizeof(ctx.publish)); + ctx.publish.topic_name = ctx.publish_topic; + ctx.publish.buffer = (byte *)ctx.publish_msg; + ctx.publish.total_len = (word16)strlen(ctx.publish_msg); + ctx.publish.qos = MQTT_QOS_0; + + ctx.state = MQTT_STATE_PUBLISHING; + } + + /* Ping to keep connection alive handled by wolfMQTT internally */ + ret = MqttClient_WaitMessage(&ctx.client, 0); + if (ret == MQTT_CODE_ERROR_TIMEOUT || ret == MQTT_CODE_CONTINUE) { + return 0; + } + if (ret < 0 && ret != MQTT_CODE_ERROR_TIMEOUT) { + debug_print("MQTT: Connection error\n"); + ctx.state = MQTT_STATE_ERROR; + return -1; + } + + return 0; +} + +/* Handle publishing state */ +static int handle_publishing(void) +{ + int ret; + + ret = MqttClient_Publish(&ctx.client, &ctx.publish); + if (ret == MQTT_CODE_SUCCESS) { + ctx.publish_pending = 0; + ctx.state = MQTT_STATE_CONNECTED; + return 0; + } + + if (ret == MQTT_CODE_CONTINUE) { + return 0; /* Still publishing */ + } + + debug_print("MQTT: Publish failed\n"); + ctx.publish_pending = 0; + ctx.state = MQTT_STATE_ERROR; + return -1; +} + +/* Handle disconnecting state */ +static int handle_disconnecting(void) +{ + int ret; + + ret = MqttClient_Disconnect(&ctx.client); + if (ret == MQTT_CODE_SUCCESS || ret != MQTT_CODE_CONTINUE) { + debug_print("MQTT: Disconnected\n"); + + /* Clean up TLS */ + if (ctx.ssl) { + wolfSSL_shutdown(ctx.ssl); + wolfSSL_free(ctx.ssl); + ctx.ssl = NULL; + } + + /* Clean up socket */ + if (ctx.socket_fd >= 0) { + wolfIP_sock_close(ctx.stack, ctx.socket_fd); + ctx.socket_fd = -1; + } + + ctx.state = MQTT_STATE_IDLE; + } + + return 0; +} + +int mqtt_client_poll(void) +{ + if (!ctx.initialized) { + return -1; + } + + switch (ctx.state) { + case MQTT_STATE_IDLE: + /* Nothing to do */ + break; + + case MQTT_STATE_NET_CONNECT: + handle_net_connect(); + break; + + case MQTT_STATE_TLS_CONNECT: + handle_tls_connect(); + break; + + case MQTT_STATE_MQTT_CONNECT: + handle_mqtt_connect(); + break; + + case MQTT_STATE_CONNECTED: + handle_connected(); + break; + + case MQTT_STATE_PUBLISHING: + handle_publishing(); + break; + + case MQTT_STATE_DISCONNECTING: + handle_disconnecting(); + break; + + case MQTT_STATE_ERROR: + /* Clean up and return to idle */ + if (ctx.ssl) { + wolfSSL_free(ctx.ssl); + ctx.ssl = NULL; + } + if (ctx.socket_fd >= 0) { + wolfIP_sock_close(ctx.stack, ctx.socket_fd); + ctx.socket_fd = -1; + } + ctx.publish_pending = 0; + ctx.state = MQTT_STATE_IDLE; + break; + + default: + break; + } + + return 0; +} + +int mqtt_client_publish(const char *message) +{ + if (!ctx.initialized) { + return -1; + } + + if (ctx.state != MQTT_STATE_CONNECTED) { + return 1; /* Not connected, busy */ + } + + if (ctx.publish_pending) { + return 1; /* Already publishing */ + } + + if (!message || strlen(message) >= sizeof(ctx.publish_msg)) { + return -1; + } + + strncpy(ctx.publish_msg, message, sizeof(ctx.publish_msg) - 1); + ctx.publish_msg[sizeof(ctx.publish_msg) - 1] = '\0'; + ctx.publish_pending = 1; + + return 0; +} + +int mqtt_client_is_connected(void) +{ + return (ctx.state == MQTT_STATE_CONNECTED || + ctx.state == MQTT_STATE_PUBLISHING); +} + +int mqtt_client_disconnect(void) +{ + if (!ctx.initialized) { + return -1; + } + + if (ctx.state == MQTT_STATE_CONNECTED || + ctx.state == MQTT_STATE_PUBLISHING) { + ctx.state = MQTT_STATE_DISCONNECTING; + } + + return 0; +} + +const char *mqtt_client_get_state_str(void) +{ + switch (ctx.state) { + case MQTT_STATE_IDLE: return "IDLE"; + case MQTT_STATE_NET_CONNECT: return "NET_CONNECT"; + case MQTT_STATE_TLS_CONNECT: return "TLS_CONNECT"; + case MQTT_STATE_MQTT_CONNECT: return "MQTT_CONNECT"; + case MQTT_STATE_CONNECTED: return "CONNECTED"; + case MQTT_STATE_PUBLISHING: return "PUBLISHING"; + case MQTT_STATE_DISCONNECTING: return "DISCONNECTING"; + case MQTT_STATE_ERROR: return "ERROR"; + default: return "UNKNOWN"; + } +} diff --git a/src/port/stm32h563/mqtt_client.h b/src/port/stm32h563/mqtt_client.h new file mode 100644 index 0000000..c33692d --- /dev/null +++ b/src/port/stm32h563/mqtt_client.h @@ -0,0 +1,68 @@ +/* mqtt_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 MQTT_CLIENT_H +#define MQTT_CLIENT_H + +#include "wolfip.h" +#include + +/* Debug callback type */ +typedef void (*mqtt_debug_cb)(const char *msg); + +/* MQTT client configuration */ +typedef struct { + const char *broker_ip; /* Broker IP address (e.g., "192.168.0.1") */ + uint16_t broker_port; /* Broker port (default: 8883 for TLS) */ + const char *client_id; /* Client ID */ + const char *publish_topic; /* Topic to publish to */ + uint16_t keep_alive_sec; /* Keep-alive interval in seconds */ +} mqtt_client_config_t; + +/* Initialize MQTT client + * stack: wolfIP stack instance + * config: MQTT client configuration (NULL for defaults) + * debug: Debug callback for status messages + * Returns 0 on success, -1 on failure */ +int mqtt_client_init(struct wolfIP *stack, const mqtt_client_config_t *config, + mqtt_debug_cb debug); + +/* Poll MQTT client - call from main loop + * Returns 0 on success */ +int mqtt_client_poll(void); + +/* Publish message to configured topic + * message: Message payload (null-terminated string) + * Returns 0 on success, -1 on failure, 1 if busy */ +int mqtt_client_publish(const char *message); + +/* Check if MQTT client is connected + * Returns 1 if connected, 0 otherwise */ +int mqtt_client_is_connected(void); + +/* Disconnect MQTT client cleanly + * Returns 0 on success */ +int mqtt_client_disconnect(void); + +/* Get MQTT client state as string (for debugging) */ +const char *mqtt_client_get_state_str(void); + +#endif /* MQTT_CLIENT_H */ diff --git a/src/port/stm32h563/ssh_keys.h b/src/port/stm32h563/ssh_keys.h new file mode 100644 index 0000000..9c89118 --- /dev/null +++ b/src/port/stm32h563/ssh_keys.h @@ -0,0 +1,51 @@ +/* ssh_keys.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 + * + * WARNING: This is a TEST host key only. Generate your own for production. + * + * To generate a new host key: + * openssl ecparam -genkey -name prime256v1 -out ssh_host_key.pem + * openssl ec -in ssh_host_key.pem -outform DER -out ssh_host_key.der + * xxd -i ssh_host_key.der > ssh_keys.h + * + * Then update the array name and length in this file. + */ + +#ifndef SSH_KEYS_H +#define SSH_KEYS_H + +/* ECC P-256 SSH Host Key (DER format) */ +static const unsigned char ssh_host_key_der[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x15, 0x96, 0x12, 0x34, 0x00, + 0xfd, 0x51, 0x45, 0x4a, 0xc9, 0x38, 0x66, 0x22, 0xa0, 0xd2, 0xbf, 0x39, + 0xc7, 0xa6, 0xb4, 0xc9, 0x84, 0xe6, 0xb9, 0xa0, 0x96, 0x87, 0xfb, 0x5c, + 0xea, 0xb3, 0xc4, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xbc, 0x28, 0x4a, + 0x71, 0x8f, 0x45, 0xf0, 0x39, 0x71, 0x88, 0x78, 0x71, 0xfc, 0x52, 0xe9, + 0x54, 0x88, 0x8a, 0x0e, 0x9f, 0x38, 0xc6, 0xd2, 0x8d, 0xba, 0xb5, 0x88, + 0x92, 0x0e, 0xaf, 0xe1, 0xfb, 0x00, 0xba, 0x91, 0x4b, 0x77, 0x4b, 0xd6, + 0xd0, 0x3f, 0x0c, 0x52, 0xdb, 0x4c, 0x7e, 0x2a, 0x5d, 0xbf, 0x36, 0x6d, + 0xd2, 0x4b, 0xde, 0x65, 0x00, 0xb3, 0xb2, 0xb8, 0x2a, 0x8e, 0x02, 0xfa, + 0x83 +}; + +static const unsigned int ssh_host_key_der_len = 121; + +#endif /* SSH_KEYS_H */ diff --git a/src/port/stm32h563/ssh_server.c b/src/port/stm32h563/ssh_server.c new file mode 100644 index 0000000..cf21852 --- /dev/null +++ b/src/port/stm32h563/ssh_server.c @@ -0,0 +1,442 @@ +/* ssh_server.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 "ssh_server.h" +#include "ssh_keys.h" + +#include +#include +#include + +/* Configuration */ +#define SSH_RX_BUF_SIZE 512 +#define SSH_TX_BUF_SIZE 512 + +/* Default credentials (change for production!) */ +#define SSH_USERNAME "admin" +#define SSH_PASSWORD "wolfip" + +/* SSH server state */ +typedef enum { + SSH_STATE_LISTENING, + SSH_STATE_ACCEPTING, + SSH_STATE_KEY_EXCHANGE, + SSH_STATE_AUTH, + SSH_STATE_CHANNEL_OPEN, + SSH_STATE_CONNECTED, + SSH_STATE_CLOSING +} ssh_state_t; + +/* Server context */ +static struct { + struct wolfIP *stack; + WOLFSSH_CTX *ctx; + WOLFSSH *ssh; + int listen_fd; + int client_fd; + ssh_state_t state; + ssh_debug_cb debug_cb; + uint8_t rx_buf[SSH_RX_BUF_SIZE]; + uint8_t tx_buf[SSH_TX_BUF_SIZE]; + int rx_len; + uint32_t start_tick; + int channel_open; +} server; + +/* External functions from wolfssh_io.c */ +extern void wolfSSH_CTX_SetIO_wolfIP(WOLFSSH_CTX *ctx); +extern int wolfSSH_SetIO_wolfIP(WOLFSSH *ssh, struct wolfIP *stack, int fd); + +#ifdef DEBUG_WOLFSSH +/* wolfSSH logging callback */ +static void ssh_log_cb(enum wolfSSH_LogLevel level, const char *msg) +{ + (void)level; + if (server.debug_cb && msg) { + server.debug_cb(msg); + server.debug_cb("\n"); + } +} +#endif + +/* Debug output helper */ +static void debug_print(const char *msg) +{ + if (server.debug_cb) { + server.debug_cb(msg); + } +} + +/* Password authentication callback */ +static int ssh_userauth_cb(byte authType, WS_UserAuthData *authData, void *ctx) +{ + (void)ctx; + + if (authType != WOLFSSH_USERAUTH_PASSWORD) { + return WOLFSSH_USERAUTH_INVALID_AUTHTYPE; + } + + /* Check username */ + if (authData->usernameSz != strlen(SSH_USERNAME) || + memcmp(authData->username, SSH_USERNAME, authData->usernameSz) != 0) { + debug_print("SSH: Invalid username\n"); + return WOLFSSH_USERAUTH_INVALID_USER; + } + + /* Check password */ + if (authData->sf.password.passwordSz != strlen(SSH_PASSWORD) || + memcmp(authData->sf.password.password, SSH_PASSWORD, + authData->sf.password.passwordSz) != 0) { + debug_print("SSH: Invalid password\n"); + return WOLFSSH_USERAUTH_INVALID_PASSWORD; + } + + debug_print("SSH: Authentication successful\n"); + return WOLFSSH_USERAUTH_SUCCESS; +} + +/* Format number to string */ +static void uint_to_str(uint32_t val, char *buf) +{ + char tmp[12]; + int i = 0; + + if (val == 0) { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + while (val > 0) { + tmp[i++] = '0' + (val % 10); + val /= 10; + } + + int j = 0; + while (i > 0) { + buf[j++] = tmp[--i]; + } + buf[j] = '\0'; +} + +/* Shell command handler */ +static int handle_command(const char *cmd, char *response, int max_len) +{ + char num_buf[12]; + int len = 0; + + /* Skip leading whitespace */ + while (*cmd == ' ' || *cmd == '\t') cmd++; + + if (strncmp(cmd, "help", 4) == 0) { + const char *help = + "\r\nAvailable commands:\r\n" + " help - Show this help\r\n" + " info - Show device information\r\n" + " uptime - Show uptime in seconds\r\n" + " exit - Close SSH session\r\n\r\n"; + len = strlen(help); + if (len < max_len) { + memcpy(response, help, len); + } + } + else if (strncmp(cmd, "info", 4) == 0) { + const char *info = + "\r\nDevice: STM32H563\r\n" + "Stack: wolfIP\r\n" + "SSH: wolfSSH\r\n" + "TLS: wolfSSL TLS 1.3\r\n\r\n"; + len = strlen(info); + if (len < max_len) { + memcpy(response, info, len); + } + } + else if (strncmp(cmd, "uptime", 6) == 0) { + uint32_t uptime = ssh_server_get_uptime(); + uint_to_str(uptime, num_buf); + const char *prefix = "\r\nUptime: "; + const char *suffix = " seconds\r\n\r\n"; + len = strlen(prefix) + strlen(num_buf) + strlen(suffix); + if (len < max_len) { + strcpy(response, prefix); + strcat(response, num_buf); + strcat(response, suffix); + } + } + else if (strncmp(cmd, "exit", 4) == 0 || strncmp(cmd, "quit", 4) == 0) { + const char *bye = "\r\nGoodbye!\r\n"; + len = strlen(bye); + if (len < max_len) { + memcpy(response, bye, len); + } + return -1; /* Signal to close connection */ + } + else if (cmd[0] != '\0' && cmd[0] != '\r' && cmd[0] != '\n') { + const char *unknown = "\r\nUnknown command. Type 'help' for available commands.\r\n\r\n"; + len = strlen(unknown); + if (len < max_len) { + memcpy(response, unknown, len); + } + } + else { + /* Empty command - just return prompt */ + len = 0; + } + + return len; +} + +int ssh_server_init(struct wolfIP *stack, uint16_t port, ssh_debug_cb debug) +{ + struct wolfIP_sockaddr_in addr; + int ret; + + memset(&server, 0, sizeof(server)); + server.stack = stack; + server.debug_cb = debug; + server.listen_fd = -1; + server.client_fd = -1; + server.state = SSH_STATE_LISTENING; + + debug_print("SSH: Initializing wolfSSH\n"); + + /* Initialize wolfSSH library */ + ret = wolfSSH_Init(); + if (ret != WS_SUCCESS) { + debug_print("SSH: wolfSSH_Init failed\n"); + return -1; + } + +#ifdef DEBUG_WOLFSSH + /* Enable wolfSSH debug logging */ + wolfSSH_Debugging_ON(); + wolfSSH_SetLoggingCb(ssh_log_cb); +#endif + + /* Create SSH server context */ + server.ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL); + if (server.ctx == NULL) { + debug_print("SSH: CTX_new failed\n"); + return -1; + } + + /* Set I/O callbacks on context */ + wolfSSH_CTX_SetIO_wolfIP(server.ctx); + + /* Set user authentication callback */ + wolfSSH_SetUserAuth(server.ctx, ssh_userauth_cb); + + /* Load host key (ECC P-256) */ + debug_print("SSH: Loading host key\n"); + ret = wolfSSH_CTX_UsePrivateKey_buffer(server.ctx, + ssh_host_key_der, ssh_host_key_der_len, + WOLFSSH_FORMAT_ASN1); + if (ret != WS_SUCCESS) { + debug_print("SSH: Failed to load host key\n"); + wolfSSH_CTX_free(server.ctx); + server.ctx = NULL; + return -1; + } + + /* Create listen socket */ + server.listen_fd = wolfIP_sock_socket(stack, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (server.listen_fd < 0) { + debug_print("SSH: socket() failed\n"); + wolfSSH_CTX_free(server.ctx); + server.ctx = NULL; + return -1; + } + + /* Bind to port */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(port); + addr.sin_addr.s_addr = 0; + + ret = wolfIP_sock_bind(stack, server.listen_fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + debug_print("SSH: bind() failed\n"); + wolfIP_sock_close(stack, server.listen_fd); + wolfSSH_CTX_free(server.ctx); + server.ctx = NULL; + return -1; + } + + /* Listen */ + ret = wolfIP_sock_listen(stack, server.listen_fd, 1); + if (ret < 0) { + debug_print("SSH: listen() failed\n"); + wolfIP_sock_close(stack, server.listen_fd); + wolfSSH_CTX_free(server.ctx); + server.ctx = NULL; + return -1; + } + + debug_print("SSH: Server ready on port 22\n"); + return 0; +} + +int ssh_server_poll(void) +{ + int ret; + struct wolfIP_sockaddr_in client_addr; + socklen_t addr_len = sizeof(client_addr); + + switch (server.state) { + case SSH_STATE_LISTENING: + /* Try to accept a connection */ + ret = wolfIP_sock_accept(server.stack, server.listen_fd, + (struct wolfIP_sockaddr *)&client_addr, &addr_len); + if (ret >= 0) { + server.client_fd = ret; + debug_print("SSH: Client connected\n"); + server.state = SSH_STATE_ACCEPTING; + } + break; + + case SSH_STATE_ACCEPTING: + /* Create SSH session */ + server.ssh = wolfSSH_new(server.ctx); + if (server.ssh == NULL) { + debug_print("SSH: wolfSSH_new failed\n"); + server.state = SSH_STATE_CLOSING; + break; + } + + /* Set I/O callbacks for wolfIP */ + ret = wolfSSH_SetIO_wolfIP(server.ssh, server.stack, server.client_fd); + if (ret != WS_SUCCESS) { + debug_print("SSH: SetIO failed\n"); + server.state = SSH_STATE_CLOSING; + break; + } + + server.channel_open = 0; + server.state = SSH_STATE_KEY_EXCHANGE; + break; + + case SSH_STATE_KEY_EXCHANGE: + /* Perform SSH handshake (key exchange + auth) */ + ret = wolfSSH_accept(server.ssh); + if (ret == WS_SUCCESS) { + debug_print("SSH: Handshake complete\n"); + server.state = SSH_STATE_CONNECTED; + server.rx_len = 0; + + /* Send welcome banner and prompt */ + const char *welcome = + "\r\n=== wolfIP SSH Shell ===\r\n" + "Type 'help' for available commands.\r\n\r\n" + "wolfip> "; + wolfSSH_stream_send(server.ssh, (byte *)welcome, strlen(welcome)); + } else { + int err = wolfSSH_get_error(server.ssh); + if (err != WS_WANT_READ && err != WS_WANT_WRITE) { + debug_print("SSH: Handshake failed\n"); + server.state = SSH_STATE_CLOSING; + } + } + break; + + case SSH_STATE_CONNECTED: + /* Read data from SSH channel */ + ret = wolfSSH_stream_read(server.ssh, + server.rx_buf + server.rx_len, + SSH_RX_BUF_SIZE - server.rx_len - 1); + if (ret > 0) { + server.rx_len += ret; + server.rx_buf[server.rx_len] = '\0'; + + /* Echo input character by character */ + wolfSSH_stream_send(server.ssh, server.rx_buf + server.rx_len - ret, ret); + + /* Check for complete command (newline) */ + char *newline = strchr((char *)server.rx_buf, '\r'); + if (newline == NULL) { + newline = strchr((char *)server.rx_buf, '\n'); + } + + if (newline != NULL) { + *newline = '\0'; + + /* Process command */ + char response[256]; + int resp_len = handle_command((char *)server.rx_buf, + response, sizeof(response)); + + if (resp_len < 0) { + /* Exit requested */ + wolfSSH_stream_send(server.ssh, (byte *)response, strlen(response)); + server.state = SSH_STATE_CLOSING; + break; + } + + /* Send response */ + if (resp_len > 0) { + wolfSSH_stream_send(server.ssh, (byte *)response, resp_len); + } + + /* Send prompt */ + const char *prompt = "wolfip> "; + wolfSSH_stream_send(server.ssh, (byte *)prompt, strlen(prompt)); + + /* Reset buffer */ + server.rx_len = 0; + } + } else if (ret < 0) { + int err = wolfSSH_get_error(server.ssh); + if (err != WS_WANT_READ && err != WS_WANT_WRITE) { + debug_print("SSH: Connection closed\n"); + server.state = SSH_STATE_CLOSING; + } + } + break; + + case SSH_STATE_CLOSING: + if (server.ssh) { + wolfSSH_shutdown(server.ssh); + wolfSSH_free(server.ssh); + server.ssh = NULL; + } + if (server.client_fd >= 0) { + wolfIP_sock_close(server.stack, server.client_fd); + server.client_fd = -1; + } + server.rx_len = 0; + server.channel_open = 0; + server.state = SSH_STATE_LISTENING; + debug_print("SSH: Ready for new connection\n"); + break; + + default: + break; + } + + return 0; +} + +uint32_t ssh_server_get_uptime(void) +{ + /* This would need integration with the main tick counter */ + /* For now, return a placeholder */ + return 0; +} diff --git a/src/port/stm32h563/ssh_server.h b/src/port/stm32h563/ssh_server.h new file mode 100644 index 0000000..1a77085 --- /dev/null +++ b/src/port/stm32h563/ssh_server.h @@ -0,0 +1,41 @@ +/* ssh_server.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 SSH_SERVER_H +#define SSH_SERVER_H + +#include "wolfip.h" + +/* Debug callback type */ +typedef void (*ssh_debug_cb)(const char *msg); + +/* Initialize SSH server on specified port + * Returns 0 on success, -1 on failure */ +int ssh_server_init(struct wolfIP *stack, uint16_t port, ssh_debug_cb debug); + +/* Poll SSH server - call from main loop + * Returns 0 on success */ +int ssh_server_poll(void); + +/* Get SSH server uptime in seconds (for status display) */ +uint32_t ssh_server_get_uptime(void); + +#endif /* SSH_SERVER_H */ diff --git a/src/port/stm32h563/tls_client.c b/src/port/stm32h563/tls_client.c new file mode 100644 index 0000000..8fe2576 --- /dev/null +++ b/src/port/stm32h563/tls_client.c @@ -0,0 +1,321 @@ +/* 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 new file mode 100644 index 0000000..cf7862a --- /dev/null +++ b/src/port/stm32h563/tls_client.h @@ -0,0 +1,101 @@ +/* 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/stm32h563/tls_server.c b/src/port/stm32h563/tls_server.c new file mode 100644 index 0000000..ee00904 --- /dev/null +++ b/src/port/stm32h563/tls_server.c @@ -0,0 +1,385 @@ +/* tls_server.c + * + * TLS Echo Server for STM32H563 using wolfSSL and wolfIP + * + * Copyright (C) 2024 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_server.h" +#include "certs.h" +#include "wolfip.h" + +#include +#include +#include + +/* Configuration */ +#ifndef TLS_SERVER_PORT +#define TLS_SERVER_PORT 8443 +#endif + +#ifndef TLS_RX_BUF_SIZE +#define TLS_RX_BUF_SIZE 1024 +#endif + +#ifndef TLS_MAX_CLIENTS +#define TLS_MAX_CLIENTS 2 +#endif + +/* Client connection state */ +typedef enum { + TLS_CLIENT_STATE_FREE = 0, + TLS_CLIENT_STATE_ACCEPTING, + TLS_CLIENT_STATE_HANDSHAKE, + TLS_CLIENT_STATE_CONNECTED, + TLS_CLIENT_STATE_CLOSING +} tls_client_state_t; + +/* Client context */ +typedef struct { + tls_client_state_t state; + int fd; + WOLFSSL *ssl; +} tls_client_t; + +/* Server context */ +static struct { + struct wolfIP *stack; + WOLFSSL_CTX *ctx; + int listen_fd; + tls_client_t clients[TLS_MAX_CLIENTS]; + uint8_t rx_buf[TLS_RX_BUF_SIZE]; + tls_server_debug_cb debug_cb; +} server; + +/* Forward declarations */ +static void tls_listen_cb(int fd, uint16_t event, void *arg); +static void tls_client_cb(int fd, uint16_t event, void *arg); +static tls_client_t *tls_client_alloc(void); +static void tls_client_free(tls_client_t *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 (server.debug_cb) { + server.debug_cb(msg); + } +} + +/* Custom random block generator for wolfSSL RNG + * Note: For production, use a hardware RNG like STM32 RNG peripheral. + * This uses wolfIP's LFSR PRNG which is NOT cryptographically secure. + */ +int custom_rand_gen_block(unsigned char *output, unsigned int sz) +{ + unsigned int i; + for (i = 0; i < sz; i++) { + output[i] = (unsigned char)(wolfIP_getrandom() & 0xFF); + } + return 0; +} + +int tls_server_init(struct wolfIP *stack, uint16_t port, + tls_server_debug_cb debug) +{ + struct wolfIP_sockaddr_in addr; + int ret; + int i; + + /* Store references */ + server.stack = stack; + server.debug_cb = debug; + server.listen_fd = -1; + server.ctx = NULL; + + /* Initialize client slots */ + for (i = 0; i < TLS_MAX_CLIENTS; i++) { + server.clients[i].state = TLS_CLIENT_STATE_FREE; + server.clients[i].fd = -1; + server.clients[i].ssl = NULL; + } + + debug_print("TLS: Initializing wolfSSL\n"); + + /* Initialize wolfSSL library */ + ret = wolfSSL_Init(); + if (ret != WOLFSSL_SUCCESS) { + debug_print("TLS: wolfSSL_Init failed\n"); + return -1; + } + + /* Create TLS 1.3 server context */ + server.ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); + if (server.ctx == NULL) { + debug_print("TLS: CTX_new failed\n"); + return -1; + } + + /* Load certificate */ + debug_print("TLS: Loading certificate\n"); + ret = wolfSSL_CTX_use_certificate_buffer(server.ctx, + (const unsigned char *)server_cert_pem, + server_cert_pem_len - 1, /* exclude null terminator */ + WOLFSSL_FILETYPE_PEM); + if (ret != WOLFSSL_SUCCESS) { + debug_print("TLS: Failed to load certificate\n"); + wolfSSL_CTX_free(server.ctx); + server.ctx = NULL; + return -1; + } + + /* Load private key */ + debug_print("TLS: Loading private key\n"); + ret = wolfSSL_CTX_use_PrivateKey_buffer(server.ctx, + (const unsigned char *)server_key_pem, + server_key_pem_len - 1, /* exclude null terminator */ + WOLFSSL_FILETYPE_PEM); + if (ret != WOLFSSL_SUCCESS) { + debug_print("TLS: Failed to load private key\n"); + wolfSSL_CTX_free(server.ctx); + server.ctx = NULL; + return -1; + } + + /* Register wolfIP I/O callbacks */ + wolfSSL_SetIO_wolfIP_CTX(server.ctx, stack); + + /* Create listening socket */ + debug_print("TLS: Creating listen socket\n"); + server.listen_fd = wolfIP_sock_socket(stack, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (server.listen_fd < 0) { + debug_print("TLS: socket() failed\n"); + wolfSSL_CTX_free(server.ctx); + server.ctx = NULL; + return -1; + } + + /* Bind to port */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(port); + addr.sin_addr.s_addr = 0; /* INADDR_ANY */ + + ret = wolfIP_sock_bind(stack, server.listen_fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + debug_print("TLS: bind() failed\n"); + wolfIP_sock_close(stack, server.listen_fd); + wolfSSL_CTX_free(server.ctx); + server.ctx = NULL; + return -1; + } + + /* Start listening */ + ret = wolfIP_sock_listen(stack, server.listen_fd, TLS_MAX_CLIENTS); + if (ret < 0) { + debug_print("TLS: listen() failed\n"); + wolfIP_sock_close(stack, server.listen_fd); + wolfSSL_CTX_free(server.ctx); + server.ctx = NULL; + return -1; + } + + /* Register callback for incoming connections */ + wolfIP_register_callback(stack, server.listen_fd, tls_listen_cb, NULL); + + debug_print("TLS: Server ready on port 8443\n"); + return 0; +} + +void tls_server_cleanup(void) +{ + int i; + + /* Close all client connections */ + for (i = 0; i < TLS_MAX_CLIENTS; i++) { + if (server.clients[i].state != TLS_CLIENT_STATE_FREE) { + tls_client_free(&server.clients[i]); + } + } + + /* Close listen socket */ + if (server.listen_fd >= 0 && server.stack) { + wolfIP_sock_close(server.stack, server.listen_fd); + server.listen_fd = -1; + } + + /* Free SSL context */ + if (server.ctx) { + wolfSSL_CTX_free(server.ctx); + server.ctx = NULL; + } + + /* Cleanup wolfSSL */ + wolfSSL_Cleanup(); +} + +static tls_client_t *tls_client_alloc(void) +{ + int i; + for (i = 0; i < TLS_MAX_CLIENTS; i++) { + if (server.clients[i].state == TLS_CLIENT_STATE_FREE) { + return &server.clients[i]; + } + } + return NULL; +} + +static void tls_client_free(tls_client_t *client) +{ + if (client->ssl) { + wolfSSL_shutdown(client->ssl); + wolfSSL_free(client->ssl); + client->ssl = NULL; + } + if (client->fd >= 0 && server.stack) { + wolfIP_sock_close(server.stack, client->fd); + client->fd = -1; + } + client->state = TLS_CLIENT_STATE_FREE; +} + +static void tls_listen_cb(int fd, uint16_t event, void *arg) +{ + tls_client_t *client; + int client_fd; + (void)arg; + + if (fd != server.listen_fd) { + return; + } + + if (!(event & CB_EVENT_READABLE)) { + return; + } + + /* Accept new connection */ + client_fd = wolfIP_sock_accept(server.stack, server.listen_fd, NULL, NULL); + if (client_fd < 0) { + return; + } + + /* Allocate client slot */ + client = tls_client_alloc(); + if (client == NULL) { + debug_print("TLS: No free client slots\n"); + wolfIP_sock_close(server.stack, client_fd); + return; + } + + debug_print("TLS: Client connected, starting handshake\n"); + + /* Create SSL object */ + client->ssl = wolfSSL_new(server.ctx); + if (client->ssl == NULL) { + debug_print("TLS: wolfSSL_new failed\n"); + wolfIP_sock_close(server.stack, client_fd); + client->state = TLS_CLIENT_STATE_FREE; + return; + } + + /* Associate SSL with socket */ + wolfSSL_SetIO_wolfIP(client->ssl, client_fd); + + client->fd = client_fd; + client->state = TLS_CLIENT_STATE_HANDSHAKE; + + /* Register callback for this client */ + wolfIP_register_callback(server.stack, client_fd, tls_client_cb, client); +} + +static void tls_client_cb(int fd, uint16_t event, void *arg) +{ + tls_client_t *client = (tls_client_t *)arg; + int ret; + int err; + + if (client == NULL || client->fd != fd) { + return; + } + + /* Handle connection closed */ + if (event & CB_EVENT_CLOSED) { + debug_print("TLS: Client disconnected\n"); + tls_client_free(client); + return; + } + + /* Handle based on state */ + switch (client->state) { + case TLS_CLIENT_STATE_HANDSHAKE: + if (!(event & (CB_EVENT_READABLE | CB_EVENT_WRITABLE))) { + break; + } + + /* Continue TLS handshake */ + ret = wolfSSL_accept(client->ssl); + if (ret == WOLFSSL_SUCCESS) { + debug_print("TLS: Handshake complete\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) { + debug_print("TLS: Handshake failed\n"); + tls_client_free(client); + } + /* WANT_READ/WANT_WRITE: handshake continues next callback */ + } + break; + + case TLS_CLIENT_STATE_CONNECTED: + if (!(event & CB_EVENT_READABLE)) { + break; + } + + /* Read encrypted data */ + ret = wolfSSL_read(client->ssl, server.rx_buf, + sizeof(server.rx_buf) - 1); + if (ret > 0) { + /* Echo data back */ + ret = wolfSSL_write(client->ssl, server.rx_buf, ret); + if (ret <= 0) { + err = wolfSSL_get_error(client->ssl, ret); + if (err != WOLFSSL_ERROR_WANT_WRITE) { + debug_print("TLS: Write error\n"); + tls_client_free(client); + } + } + } else { + err = wolfSSL_get_error(client->ssl, ret); + if (err == WOLFSSL_ERROR_ZERO_RETURN) { + /* Clean shutdown */ + debug_print("TLS: Client closed connection\n"); + tls_client_free(client); + } else if (err != WOLFSSL_ERROR_WANT_READ) { + debug_print("TLS: Read error\n"); + tls_client_free(client); + } + } + break; + + default: + break; + } +} diff --git a/src/port/stm32h563/tls_server.h b/src/port/stm32h563/tls_server.h new file mode 100644 index 0000000..5cea3d9 --- /dev/null +++ b/src/port/stm32h563/tls_server.h @@ -0,0 +1,63 @@ +/* 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_SERVER_H +#define TLS_SERVER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration */ +struct wolfIP; + +/* Debug callback type - receives status messages */ +typedef void (*tls_server_debug_cb)(const char *msg); + +/** + * Initialize the TLS echo server + * + * @param stack wolfIP stack instance + * @param port TCP port to listen on (default: 8443) + * @param debug Optional debug callback for status messages (can be NULL) + * + * @return 0 on success, negative on error + * + * Example: + * tls_server_init(stack, 8443, uart_puts); + */ +int tls_server_init(struct wolfIP *stack, uint16_t port, + tls_server_debug_cb debug); + +/** + * Cleanup TLS server resources + * + * Call this to shutdown the server and free all resources. + */ +void tls_server_cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TLS_SERVER_H */ diff --git a/src/port/stm32h563/user_settings.h b/src/port/stm32h563/user_settings.h new file mode 100644 index 0000000..7bb553b --- /dev/null +++ b/src/port/stm32h563/user_settings.h @@ -0,0 +1,227 @@ +/* user_settings.h + * + * wolfSSL/wolfSSH/wolfMQTT configuration for STM32H563 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 + +/* ------------------------------------------------------------------------- */ +/* Platform / OS */ +/* ------------------------------------------------------------------------- */ +#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 + +/* ------------------------------------------------------------------------- */ +/* Math - Portable C implementation */ +/* ------------------------------------------------------------------------- */ +#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 */ + +/* Use portable C code (no platform-specific assembly) */ +#define TFM_NO_ASM +#define WOLFSSL_NO_ASM + +/* ------------------------------------------------------------------------- */ +/* TLS Configuration */ +/* ------------------------------------------------------------------------- */ +#define WOLFSSL_TLS13 +#define HAVE_TLS_EXTENSIONS +#define HAVE_SUPPORTED_CURVES +#define HAVE_ENCRYPT_THEN_MAC +#define HAVE_SNI /* Server Name Indication - required by most servers */ + +/* Session */ +#define NO_SESSION_CACHE /* Save RAM - no session resumption */ +#define SMALL_SESSION_CACHE + +/* ------------------------------------------------------------------------- */ +/* Cipher Suites */ +/* ------------------------------------------------------------------------- */ + +/* AES-GCM (primary) */ +#define HAVE_AESGCM +#define GCM_SMALL /* Smaller GCM tables */ +#define WOLFSSL_AES_SMALL_TABLES +#define WOLFSSL_AES_DIRECT + +/* ChaCha20-Poly1305 (alternative, good for no-AES-HW platforms) */ +#define HAVE_CHACHA +#define HAVE_POLY1305 + +/* SHA-2 family */ +#define WOLFSSL_SHA384 +#define WOLFSSL_SHA512 + +/* HKDF for TLS 1.3 */ +#define HAVE_HKDF + +/* ------------------------------------------------------------------------- */ +/* Key Exchange / Certificates */ +/* ------------------------------------------------------------------------- */ + +/* ECC */ +#define HAVE_ECC +#define ECC_USER_CURVES /* Only enable curves we specify */ +#define HAVE_ECC256 /* P-256 (secp256r1) */ +#define ECC_SHAMIR +#define ECC_TIMING_RESISTANT + +/* Hardening options */ +#define TFM_TIMING_RESISTANT +#define WC_RSA_BLINDING + +/* RSA - needed for certificate verification (most servers use RSA certs) */ +#define WC_RSA_PSS /* Required for TLS 1.3 with RSA */ + +/* X.509 certificates */ +#define WOLFSSL_ASN_TEMPLATE /* Smaller ASN.1 code */ +#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 /* MD5 deprecated, not needed */ +#define NO_DES3 +#define NO_RABBIT +#define NO_HC128 +#define NO_PSK +#define NO_PWDBASED +#define NO_OLD_TLS /* Disable TLS 1.0/1.1 */ +#define NO_CHECK_PRIVATE_KEY /* Save code - we trust our own keys */ + +/* DH not needed if using ECC */ +#define NO_DH + +/* ------------------------------------------------------------------------- */ +/* Memory */ +/* ------------------------------------------------------------------------- */ + +/* Use wolfSSL static memory if desired (optional) + * For now, rely on newlib malloc from syscalls.c + * #define WOLFSSL_STATIC_MEMORY + * #define WOLFSSL_NO_MALLOC + */ + +/* Reduce memory usage */ +#define ALT_ECC_SIZE /* Smaller ECC structs */ +#define WOLFSSL_SMALL_CERT_VERIFY + +/* ------------------------------------------------------------------------- */ +/* RNG */ +/* ------------------------------------------------------------------------- */ +/* wc_GenerateSeed is implemented in tls_server.c + * (wolfSSL will call it for entropy) */ +#define CUSTOM_RAND_GENERATE_BLOCK custom_rand_gen_block +int custom_rand_gen_block(unsigned char* output, unsigned int sz); + +/* ------------------------------------------------------------------------- */ +/* Debug (comment out for production) */ +/* ------------------------------------------------------------------------- */ +/* #define DEBUG_WOLFSSL */ +/* #define WOLFSSL_DEBUG_TLS */ +/* #define DEBUG_WOLFSSH */ /* Enable wolfSSH debug output */ + +/* ------------------------------------------------------------------------- */ +/* wolfSSH Settings (when ENABLE_SSH=1) */ +/* ------------------------------------------------------------------------- */ +#ifdef ENABLE_SSH +/* Enable wolfSSL features needed for wolfSSH */ +#define WOLFSSL_WOLFSSH /* Enable wc_SSH_KDF function */ +#define WOLFSSL_KEY_GEN /* Key generation for wolfSSH keygen */ + +/* Disable features not needed for basic shell */ +#define WOLFSSH_NO_TIMESTAMP +#define WOLFSSH_NO_AGENT +#define WOLFSSH_NO_SFTP +#define WOLFSSH_NO_SCP + +/* Bare-metal: no termios/pty support */ +#define WOLFSSH_NO_TERM + +/* Memory optimization - reduced for embedded */ +#define WOLFSSH_SMALL_STACK +#define DEFAULT_WINDOW_SZ (4 * 1024) /* Reduced from 16KB to 4KB */ +#define DEFAULT_HIGHWATER_MARK ((DEFAULT_WINDOW_SZ * 3) / 4) +#define MAX_PACKET_SZ (DEFAULT_WINDOW_SZ + 256) + +/* Custom I/O - we use wolfIP sockets */ +#define WOLFSSH_USER_IO + +/* No certificate-based auth files */ +#define NO_WOLFSSH_CERTS_FROM_FILE + +/* ECC key support (matches our host key) */ +#define WOLFSSH_KEYGEN +#endif + +/* ------------------------------------------------------------------------- */ +/* wolfMQTT Settings (when ENABLE_MQTT=1) */ +/* ------------------------------------------------------------------------- */ +#ifdef ENABLE_MQTT +/* Non-blocking mode for integration with wolfIP event loop */ +#define WOLFMQTT_NONBLOCK + +/* No standard I/O available on bare-metal */ +#define WOLFMQTT_NO_STDIO + +/* Custom I/O callbacks - we use wolfIP sockets via wolfmqtt_io.c */ +#define WOLFMQTT_USER_IO + +/* Disable error strings to save space */ +#define WOLFMQTT_NO_ERROR_STRINGS + +/* Use TLS for secure MQTT connections */ +#define ENABLE_MQTT_TLS + +/* Define POSIX error codes for bare-metal */ +#ifndef EWOULDBLOCK +#define EWOULDBLOCK 11 +#endif +#ifndef EAGAIN +#define EAGAIN 11 +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* USER_SETTINGS_H */ diff --git a/src/port/wolfmqtt_io.c b/src/port/wolfmqtt_io.c new file mode 100644 index 0000000..22689a5 --- /dev/null +++ b/src/port/wolfmqtt_io.c @@ -0,0 +1,246 @@ +/* wolfmqtt_io.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 + * + * wolfIP <-> wolfMQTT glue for custom IO callbacks. + */ +#include "wolfip.h" +#include +#include + +#ifndef MAX_WOLFMQTT_CTX + #define MAX_WOLFMQTT_CTX 2 +#endif + +/* I/O descriptor for wolfMQTT clients */ +struct wolfmqtt_io_desc { + struct wolfIP *stack; + int fd; + int in_use; + int connected; +}; + +static struct wolfmqtt_io_desc io_descs[MAX_WOLFMQTT_CTX]; + +/* Find or allocate an I/O descriptor */ +static struct wolfmqtt_io_desc *io_desc_alloc(void) +{ + for (int i = 0; i < MAX_WOLFMQTT_CTX; i++) { + if (!io_descs[i].in_use) { + io_descs[i].in_use = 1; + io_descs[i].connected = 0; + return &io_descs[i]; + } + } + return NULL; +} + +/* Free an I/O descriptor */ +static void io_desc_free(struct wolfmqtt_io_desc *desc) +{ + if (desc) { + desc->stack = NULL; + desc->fd = -1; + desc->in_use = 0; + desc->connected = 0; + } +} + +/* wolfMQTT network connect callback */ +static int wolfmqtt_net_connect(void *context, const char *host, word16 port, + int timeout_ms) +{ + struct wolfmqtt_io_desc *desc = (struct wolfmqtt_io_desc *)context; + struct wolfIP_sockaddr_in addr; + int ret; + (void)timeout_ms; + + if (!desc || !desc->stack || !host) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* Create TCP socket */ + desc->fd = wolfIP_sock_socket(desc->stack, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (desc->fd < 0) { + return MQTT_CODE_ERROR_NETWORK; + } + + /* Set up address */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(port); + addr.sin_addr.s_addr = atoip4(host); + + /* Initiate non-blocking connect */ + ret = wolfIP_sock_connect(desc->stack, desc->fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + + if (ret == 0) { + desc->connected = 1; + return MQTT_CODE_SUCCESS; + } + + if (ret == -WOLFIP_EAGAIN || ret == -11) { + return MQTT_CODE_CONTINUE; /* EAGAIN/EINPROGRESS */ + } + + /* Connection failed */ + wolfIP_sock_close(desc->stack, desc->fd); + desc->fd = -1; + return MQTT_CODE_ERROR_NETWORK; +} + +/* wolfMQTT network read callback */ +static int wolfmqtt_net_read(void *context, byte *buf, int buf_len, + int timeout_ms) +{ + struct wolfmqtt_io_desc *desc = (struct wolfmqtt_io_desc *)context; + int ret; + (void)timeout_ms; + + if (!desc || !desc->stack || desc->fd < 0 || !buf) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + ret = wolfIP_sock_recv(desc->stack, desc->fd, buf, buf_len, 0); + if (ret == -WOLFIP_EAGAIN || ret == -1) { + return MQTT_CODE_CONTINUE; + } + if (ret == 0) { + return MQTT_CODE_ERROR_NETWORK; + } + if (ret < 0) { + return MQTT_CODE_ERROR_NETWORK; + } + return ret; +} + +/* wolfMQTT network write callback */ +static int wolfmqtt_net_write(void *context, const byte *buf, int buf_len, + int timeout_ms) +{ + struct wolfmqtt_io_desc *desc = (struct wolfmqtt_io_desc *)context; + int ret; + (void)timeout_ms; + + if (!desc || !desc->stack || desc->fd < 0 || !buf) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + ret = wolfIP_sock_send(desc->stack, desc->fd, buf, buf_len, 0); + if (ret == -WOLFIP_EAGAIN || ret == -1) { + return MQTT_CODE_CONTINUE; + } + if (ret == 0) { + return MQTT_CODE_ERROR_NETWORK; + } + if (ret < 0) { + return MQTT_CODE_ERROR_NETWORK; + } + return ret; +} + +/* wolfMQTT network disconnect callback */ +static int wolfmqtt_net_disconnect(void *context) +{ + struct wolfmqtt_io_desc *desc = (struct wolfmqtt_io_desc *)context; + + if (!desc) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (desc->fd >= 0 && desc->stack) { + wolfIP_sock_close(desc->stack, desc->fd); + desc->fd = -1; + } + desc->connected = 0; + + return MQTT_CODE_SUCCESS; +} + +/* Set up wolfMQTT network callbacks for wolfIP (call once during init) + * Returns the I/O context that should be set via MqttClient_SetContext() */ +void *wolfMQTT_Init_wolfIP(MqttNet *net, struct wolfIP *stack) +{ + struct wolfmqtt_io_desc *desc; + + if (!net || !stack) { + return NULL; + } + + desc = io_desc_alloc(); + if (!desc) { + return NULL; + } + + desc->stack = stack; + desc->fd = -1; + + /* Set callbacks */ + net->connect = wolfmqtt_net_connect; + net->read = wolfmqtt_net_read; + net->write = wolfmqtt_net_write; + net->disconnect = wolfmqtt_net_disconnect; + net->context = desc; + + return desc; +} + +/* Clean up wolfMQTT I/O context */ +void wolfMQTT_Cleanup_wolfIP(void *context) +{ + struct wolfmqtt_io_desc *desc = (struct wolfmqtt_io_desc *)context; + + if (desc) { + if (desc->fd >= 0 && desc->stack) { + wolfIP_sock_close(desc->stack, desc->fd); + } + io_desc_free(desc); + } +} + +/* Get the socket file descriptor (useful for TLS setup) */ +int wolfMQTT_GetFd_wolfIP(void *context) +{ + struct wolfmqtt_io_desc *desc = (struct wolfmqtt_io_desc *)context; + if (desc) { + return desc->fd; + } + return -1; +} + +/* Check if socket is connected */ +int wolfMQTT_IsConnected_wolfIP(void *context) +{ + struct wolfmqtt_io_desc *desc = (struct wolfmqtt_io_desc *)context; + if (desc) { + return desc->connected; + } + return 0; +} + +/* Set socket file descriptor (useful when socket created externally for TLS) */ +void wolfMQTT_SetFd_wolfIP(void *context, int fd) +{ + struct wolfmqtt_io_desc *desc = (struct wolfmqtt_io_desc *)context; + if (desc) { + desc->fd = fd; + desc->connected = (fd >= 0) ? 1 : 0; + } +} diff --git a/src/port/wolfssh_io.c b/src/port/wolfssh_io.c new file mode 100644 index 0000000..38a9db7 --- /dev/null +++ b/src/port/wolfssh_io.c @@ -0,0 +1,154 @@ +/* wolfssh_io.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 + * + * wolfIP <-> wolfSSH glue for custom IO callbacks. + */ +#include "wolfip.h" +#include + +#ifndef MAX_WOLFSSH_CTX + #define MAX_WOLFSSH_CTX 4 +#endif + +/* I/O descriptor for wolfSSH sessions */ +struct wolfssh_io_desc { + struct wolfIP *stack; + int fd; + int in_use; +}; + +static struct wolfssh_io_desc io_descs[MAX_WOLFSSH_CTX]; + +/* Find or allocate an I/O descriptor */ +static struct wolfssh_io_desc *io_desc_alloc(void) +{ + for (int i = 0; i < MAX_WOLFSSH_CTX; i++) { + if (!io_descs[i].in_use) { + io_descs[i].in_use = 1; + return &io_descs[i]; + } + } + return NULL; +} + +/* Free an I/O descriptor */ +static void io_desc_free(struct wolfssh_io_desc *desc) +{ + if (desc) { + desc->stack = NULL; + desc->fd = -1; + desc->in_use = 0; + } +} + +/* wolfSSH receive callback */ +static int wolfssh_io_recv(WOLFSSH *ssh, void *buf, word32 sz, void *ctx) +{ + struct wolfssh_io_desc *desc = (struct wolfssh_io_desc *)ctx; + int ret; + (void)ssh; + + if (!desc || !desc->stack || desc->fd < 0) { + return WS_CBIO_ERR_GENERAL; + } + + ret = wolfIP_sock_recv(desc->stack, desc->fd, buf, (int)sz, 0); + if (ret == -WOLFIP_EAGAIN || ret == -1) { + return WS_CBIO_ERR_WANT_READ; + } + if (ret == 0) { + return WS_CBIO_ERR_CONN_CLOSE; + } + if (ret < 0) { + return WS_CBIO_ERR_GENERAL; + } + return ret; +} + +/* wolfSSH send callback */ +static int wolfssh_io_send(WOLFSSH *ssh, void *buf, word32 sz, void *ctx) +{ + struct wolfssh_io_desc *desc = (struct wolfssh_io_desc *)ctx; + int ret; + (void)ssh; + + if (!desc || !desc->stack || desc->fd < 0) { + return WS_CBIO_ERR_GENERAL; + } + + ret = wolfIP_sock_send(desc->stack, desc->fd, buf, (int)sz, 0); + if (ret == -WOLFIP_EAGAIN || ret == -1) { + return WS_CBIO_ERR_WANT_WRITE; + } + if (ret == 0) { + return WS_CBIO_ERR_CONN_CLOSE; + } + if (ret < 0) { + return WS_CBIO_ERR_GENERAL; + } + return ret; +} + +/* Set up wolfSSH I/O callbacks on the context (call once during init) */ +void wolfSSH_CTX_SetIO_wolfIP(WOLFSSH_CTX *ctx) +{ + if (ctx) { + wolfSSH_SetIORecv(ctx, wolfssh_io_recv); + wolfSSH_SetIOSend(ctx, wolfssh_io_send); + } +} + +/* Set up wolfSSH I/O context for a wolfIP socket (call per-session) */ +int wolfSSH_SetIO_wolfIP(WOLFSSH *ssh, struct wolfIP *stack, int fd) +{ + struct wolfssh_io_desc *desc; + + if (!ssh || !stack || fd < 0) { + return WS_BAD_ARGUMENT; + } + + desc = io_desc_alloc(); + if (!desc) { + return WS_MEMORY_E; + } + + desc->stack = stack; + desc->fd = fd; + + wolfSSH_SetIOReadCtx(ssh, desc); + wolfSSH_SetIOWriteCtx(ssh, desc); + + return WS_SUCCESS; +} + +/* Clean up I/O descriptor when SSH session is done */ +void wolfSSH_CleanupIO_wolfIP(WOLFSSH *ssh) +{ + void *ctx; + + if (!ssh) { + return; + } + + ctx = wolfSSH_GetIOReadCtx(ssh); + if (ctx) { + io_desc_free((struct wolfssh_io_desc *)ctx); + } +} diff --git a/src/port/wolfssl_io.c b/src/port/wolfssl_io.c index dbd1f4b..f055fd0 100644 --- a/src/port/wolfssl_io.c +++ b/src/port/wolfssl_io.c @@ -1,4 +1,5 @@ /* wolfssl_io.c + * * Copyright (C) 2025 wolfSSL Inc. * * This file is part of wolfIP TCP/IP stack.