From 7516b80b48fac1f0752c3ce7fd6a9250736b72d5 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 20 Jan 2026 11:59:45 -0800 Subject: [PATCH 1/6] Add TLS 1.3 client/server support for STM32H563 - Add wolfSSL TLS 1.3 integration with wolfIP TCP/IP stack - TLS server: echo server on port 8443 with ECC P-256 certificate - TLS client: connects to Google (HTTPS) to verify outbound TLS - Uses ECDHE-P256 key exchange, RSA cert verification, AES-GCM/ChaCha20 - Custom I/O callbacks in wolfssl_io.c bridge wolfSSL and wolfIP - Build with: make ENABLE_TLS=1 - ~200KB code size with full TLS support - Tested: TLS 1.3 handshake to google.com successful --- src/port/stm32h563/Makefile | 126 +++++++++- src/port/stm32h563/README.md | 181 ++++++++++++-- src/port/stm32h563/certs.h | 53 ++++ src/port/stm32h563/main.c | 86 +++++++ src/port/stm32h563/tls_client.c | 321 ++++++++++++++++++++++++ src/port/stm32h563/tls_client.h | 101 ++++++++ src/port/stm32h563/tls_server.c | 385 +++++++++++++++++++++++++++++ src/port/stm32h563/tls_server.h | 63 +++++ src/port/stm32h563/user_settings.h | 180 ++++++++++++++ src/port/wolfssl_io.c | 1 + 10 files changed, 1478 insertions(+), 19 deletions(-) create mode 100644 src/port/stm32h563/certs.h create mode 100644 src/port/stm32h563/tls_client.c create mode 100644 src/port/stm32h563/tls_client.h create mode 100644 src/port/stm32h563/tls_server.c create mode 100644 src/port/stm32h563/tls_server.h create mode 100644 src/port/stm32h563/user_settings.h diff --git a/src/port/stm32h563/Makefile b/src/port/stm32h563/Makefile index a16c4e0..cf7ebd3 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,23 @@ 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 + +# Library paths - default to sibling directories (clone alongside pattern) +WOLFSSL_ROOT ?= $(ROOT)/../wolfssl + +# 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 +35,77 @@ 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 + +# 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 + +SRCS += $(WOLFSSL_SRCS) + +endif # ENABLE_TLS + +# ----------------------------------------------------------------------------- +# 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)" +ifeq ($(ENABLE_TLS),1) + @echo " wolfSSL: $(WOLFSSL_ROOT)" +endif app.elf: $(OBJS) $(LDSCRIPT) $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -Wl,--start-group -lc -lm -lgcc -lnosys -Wl,--end-group -o $@ @@ -37,7 +116,46 @@ app.bin: app.elf %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ +# wolfSSL objects use relaxed warnings +$(WOLFSSL_ROOT)/%.o: $(WOLFSSL_ROOT)/%.c + $(CC) $(CFLAGS_WOLFSSL) -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 .PHONY: all clean + +# ----------------------------------------------------------------------------- +# 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 " help Show this help" + @echo "" + @echo "Options:" + @echo " TZEN=1 Enable TrustZone support" + @echo " ENABLE_TLS=1 Enable TLS server (requires wolfSSL)" + @echo " WOLFSSL_ROOT= Path to wolfSSL (default: ../wolfssl)" + @echo "" + @echo "Examples:" + @echo " make # Basic build" + @echo " make TZEN=1 # TrustZone enabled" + @echo " make ENABLE_TLS=1 # With TLS server" + @echo " make TZEN=1 ENABLE_TLS=1 # Both" + @echo "" + @echo "Testing TLS server:" + @echo " echo 'Hello' | openssl s_client -connect :8443 -quiet" + +.PHONY: help diff --git a/src/port/stm32h563/README.md b/src/port/stm32h563/README.md index 7add454..69043ba 100644 --- a/src/port/stm32h563/README.md +++ b/src/port/stm32h563/README.md @@ -32,13 +32,13 @@ make This produces `app.elf` and `app.bin` for use with TZEN=0 (TrustZone disabled). -### TrustZone Enabled Build (Experimental) +### 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 +53,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 @@ -170,6 +191,145 @@ echo "Hello wolfIP!" | nc 192.168.12.11 7 ping 192.168.12.11 ``` +## TLS Support (wolfSSL) + +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: + +```bash +make ENABLE_TLS=1 WOLFSSL_ROOT=/path/to/wolfssl +``` + +### 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: 192.168.0.1 + 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 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 +``` + +### 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 +``` + +Expected output: +``` +depth=0 CN = wolfIP-STM32H563, O = wolfSSL, C = US +verify error:num=18:self-signed certificate +Hello TLS! +``` + +### 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 +# 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" + +# Convert to C header (update certs.h) +# Copy PEM content into certs.h as string literals +``` + ## Files | File | Description | @@ -184,19 +344,10 @@ 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 configuration (TLS builds only) | +| `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) | ## Troubleshooting 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/main.c b/src/port/stm32h563/main.c index 67980bd..08d8c64 100644 --- a/src/port/stm32h563/main.c +++ b/src/port/stm32h563/main.c @@ -24,6 +24,20 @@ #include "wolfip.h" #include "stm32h5_eth.h" +#ifdef ENABLE_TLS +#include "tls_server.h" +#include "tls_client.h" +#define TLS_PORT 8443 + +/* Google IP for TLS client test (run: dig +short google.com) */ +#define GOOGLE_IP "142.250.189.174" +#define HTTPS_PORT 443 + +/* TLS client test state */ +static int tls_client_test_started = 0; +static int tls_client_test_done = 0; +#endif + #define ECHO_PORT 7 #define RX_BUF_SIZE 1024 @@ -334,6 +348,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 +545,63 @@ 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 + uart_puts("Entering main loop. Ready for connections!\n"); uart_puts("Loop starting...\n"); for (;;) { (void)wolfIP_poll(IPStack, tick++); + +#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(HTTPS_PORT); + uart_puts("\n"); + + if (tls_client_connect(GOOGLE_IP, 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/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..cb91487 --- /dev/null +++ b/src/port/stm32h563/user_settings.h @@ -0,0 +1,180 @@ +/* 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 + +/* ------------------------------------------------------------------------- */ +/* 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 */ + +/* ------------------------------------------------------------------------- */ +/* wolfSSH Settings (when ENABLE_SSH=1) */ +/* ------------------------------------------------------------------------- */ +#ifdef ENABLE_SSH +#define WOLFSSH_NO_TIMESTAMP +#define WOLFSSH_NO_AGENT +#define WOLFSSH_NO_SFTP +#define WOLFSSH_SMALL_STACK +#define WOLFSSH_TERM +#endif + +/* ------------------------------------------------------------------------- */ +/* wolfMQTT Settings (when ENABLE_MQTT=1) */ +/* ------------------------------------------------------------------------- */ +#ifdef ENABLE_MQTT +#define WOLFMQTT_NONBLOCK +#define WOLFMQTT_NO_STDIO +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* USER_SETTINGS_H */ 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. From 7f9e8c16a605d92d4b0fd8791aa7d604936c22a2 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 20 Jan 2026 15:55:05 -0800 Subject: [PATCH 2/6] Add HTTPS web server and SSH shell support for STM32H563 - Add HTTPS server serving status page on port 443 (ENABLE_HTTPS=1) - Add SSH server with interactive shell on port 22 (ENABLE_SSH=1) - Add wolfssh_io.c for wolfSSH-wolfIP integration - Increase MAX_TCPSOCKETS from 4 to 8 to support multiple servers - Fix IP address byte order display in HTTPS status page - Update Makefile with ENABLE_HTTPS and ENABLE_SSH build flags - Update README with build and testing documentation --- src/port/stm32h563/Makefile | 88 +++++- src/port/stm32h563/README.md | 280 ++++++++++++++++- src/port/stm32h563/config.h | 2 +- src/port/stm32h563/https_server.c | 481 +++++++++++++++++++++++++++++ src/port/stm32h563/https_server.h | 41 +++ src/port/stm32h563/main.c | 50 ++- src/port/stm32h563/ssh_keys.h | 51 +++ src/port/stm32h563/ssh_server.c | 421 +++++++++++++++++++++++++ src/port/stm32h563/ssh_server.h | 41 +++ src/port/stm32h563/user_settings.h | 17 + src/port/wolfssh_io.c | 147 +++++++++ 11 files changed, 1594 insertions(+), 25 deletions(-) create mode 100644 src/port/stm32h563/https_server.c create mode 100644 src/port/stm32h563/https_server.h create mode 100644 src/port/stm32h563/ssh_keys.h create mode 100644 src/port/stm32h563/ssh_server.c create mode 100644 src/port/stm32h563/ssh_server.h create mode 100644 src/port/wolfssh_io.c diff --git a/src/port/stm32h563/Makefile b/src/port/stm32h563/Makefile index cf7ebd3..2d79f5e 100644 --- a/src/port/stm32h563/Makefile +++ b/src/port/stm32h563/Makefile @@ -11,8 +11,15 @@ TZEN ?= 0 # 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 + # Library paths - default to sibling directories (clone alongside pattern) WOLFSSL_ROOT ?= $(ROOT)/../wolfssl +WOLFSSH_ROOT ?= $(ROOT)/../wolfssh # Base compiler flags CFLAGS := -mcpu=cortex-m33 -mthumb -mcmse -Os -ffreestanding -fdata-sections -ffunction-sections @@ -53,11 +60,17 @@ CFLAGS += -DWOLFSSL_USER_SETTINGS CFLAGS += -DWOLFSSL_WOLFIP CFLAGS += -I$(WOLFSSL_ROOT) -# TLS server, client, and wolfIP-wolfSSL glue +# 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) +ifeq ($(ENABLE_HTTPS),1) +CFLAGS += -DENABLE_HTTPS +SRCS += https_server.c +endif + # wolfSSL source files (minimal set for TLS 1.3 server with ECC) WOLFSSL_SRCS := \ $(WOLFSSL_ROOT)/wolfcrypt/src/aes.c \ @@ -96,16 +109,59 @@ 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 +$(WOLFSSH_ROOT)/%.o: $(WOLFSSH_ROOT)/%.c + $(CC) $(CFLAGS_WOLFSSL) -c $< -o $@ + +endif # ENABLE_SSH + # ----------------------------------------------------------------------------- # Build rules # ----------------------------------------------------------------------------- OBJS := $(patsubst %.c,%.o,$(SRCS)) all: app.bin - @echo "Built with TZEN=$(TZEN) ENABLE_TLS=$(ENABLE_TLS)" + @echo "Built with TZEN=$(TZEN) ENABLE_TLS=$(ENABLE_TLS) ENABLE_HTTPS=$(ENABLE_HTTPS) ENABLE_SSH=$(ENABLE_SSH)" ifeq ($(ENABLE_TLS),1) @echo " wolfSSL: $(WOLFSSL_ROOT)" endif +ifeq ($(ENABLE_SSH),1) + @echo " wolfSSH: $(WOLFSSH_ROOT)" +endif app.elf: $(OBJS) $(LDSCRIPT) $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -Wl,--start-group -lc -lm -lgcc -lnosys -Wl,--end-group -o $@ @@ -128,6 +184,9 @@ 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 .PHONY: all clean @@ -145,17 +204,24 @@ help: @echo " help Show this help" @echo "" @echo "Options:" - @echo " TZEN=1 Enable TrustZone support" - @echo " ENABLE_TLS=1 Enable TLS server (requires wolfSSL)" - @echo " WOLFSSL_ROOT= Path to wolfSSL (default: ../wolfssl)" + @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 " WOLFSSL_ROOT= Path to wolfSSL (default: ../wolfssl)" + @echo " WOLFSSH_ROOT= Path to wolfSSH (default: ../wolfssh)" @echo "" @echo "Examples:" - @echo " make # Basic build" - @echo " make TZEN=1 # TrustZone enabled" - @echo " make ENABLE_TLS=1 # With TLS server" - @echo " make TZEN=1 ENABLE_TLS=1 # Both" + @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_HTTPS=1 ENABLE_SSH=1 # Full featured" @echo "" - @echo "Testing TLS server:" - @echo " echo 'Hello' | openssl s_client -connect :8443 -quiet" + @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)" .PHONY: help diff --git a/src/port/stm32h563/README.md b/src/port/stm32h563/README.md index 69043ba..62d11d7 100644 --- a/src/port/stm32h563/README.md +++ b/src/port/stm32h563/README.md @@ -179,19 +179,52 @@ sudo ip link set up Replace `` with your Ethernet interface name (e.g., `eth0`, `enp5s0`). -## Testing +## Testing TCP Echo Server -Once running, the echo server listens on TCP port 7: +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 -# Test with netcat -echo "Hello wolfIP!" | nc 192.168.12.11 7 +# Get the device IP from serial output (DHCP) or use static IP + +# Test ICMP connectivity +ping -# Test with ping -ping 192.168.12.11 +# 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 Support (wolfSSL) +## TLS The port includes optional TLS 1.3 support using wolfSSL. This enables secure encrypted communication. @@ -217,6 +250,47 @@ Or specify a custom wolfSSL path: 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: @@ -227,7 +301,7 @@ Initializing wolfIP stack... ... DHCP configuration received: IP: 192.168.0.197 - Mask: 192.168.0.1 + Mask: 255.255.255.0 GW: 192.168.0.1 Creating TCP socket on port 7... Initializing TLS server on port 8443... @@ -235,6 +309,11 @@ 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 @@ -253,6 +332,12 @@ 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: @@ -263,15 +348,21 @@ The TLS echo server listens on port 8443. Test with OpenSSL: # View full handshake details openssl s_client -connect :8443 -tls1_3 + +# View TLS session details +openssl s_client -connect :8443 -tls1_3 -brief ``` -Expected output: +### 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. @@ -330,6 +421,172 @@ openssl req -new -x509 -key server_key.pem -out server_cert.pem \ # 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 Address192.168.0.197
Uptime1234 sec
TLSTLS 1.3
CipherECC P-256
+... +``` + +### 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) +- Cipher suite (ECC P-256) + +## 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 +``` + ## Files | File | Description | @@ -344,10 +601,13 @@ openssl req -new -x509 -key server_key.pem -out server_cert.pem \ | `target_tzen.ld` | Linker script for TZEN=1 | | `config.h` | Build configuration | | `Makefile` | Build system | -| `user_settings.h` | wolfSSL configuration (TLS builds only) | +| `user_settings.h` | wolfSSL/wolfSSH 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) | +| `https_server.c/h` | HTTPS web server (HTTPS builds only) | +| `ssh_server.c/h` | SSH shell server (SSH builds only) | +| `ssh_keys.h` | Embedded SSH host key (SSH builds only) | ## Troubleshooting diff --git a/src/port/stm32h563/config.h b/src/port/stm32h563/config.h index aeb1277..89ccf35 100644 --- a/src/port/stm32h563/config.h +++ b/src/port/stm32h563/config.h @@ -28,7 +28,7 @@ #define ETHERNET #define LINK_MTU 1536 -#define MAX_TCPSOCKETS 4 +#define MAX_TCPSOCKETS 8 #define MAX_UDPSOCKETS 2 #define MAX_ICMPSOCKETS 2 #define RXBUF_SIZE (LINK_MTU * 16) diff --git a/src/port/stm32h563/https_server.c b/src/port/stm32h563/https_server.c new file mode 100644 index 0000000..187f76f --- /dev/null +++ b/src/port/stm32h563/https_server.c @@ -0,0 +1,481 @@ +/* https_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 "https_server.h" +#include "certs.h" + +#include +#include +#include + +/* Configuration */ +#define HTTPS_RX_BUF_SIZE 1024 +#define HTTPS_TX_BUF_SIZE 2048 + +/* Server state */ +typedef enum { + HTTPS_STATE_LISTENING, + HTTPS_STATE_ACCEPTING, + HTTPS_STATE_HANDSHAKE, + HTTPS_STATE_READ_REQUEST, + HTTPS_STATE_SEND_RESPONSE, + HTTPS_STATE_CLOSING +} https_state_t; + +/* Server context */ +static struct { + struct wolfIP *stack; + WOLFSSL_CTX *ctx; + WOLFSSL *ssl; + int listen_fd; + int client_fd; + https_state_t state; + https_debug_cb debug_cb; + uint8_t rx_buf[HTTPS_RX_BUF_SIZE]; + uint8_t tx_buf[HTTPS_TX_BUF_SIZE]; + int rx_len; + int tx_len; + int tx_sent; + uint32_t device_ip; + uint32_t uptime_sec; +} server; + +/* 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); + +/* Forward declarations */ +static void https_listen_cb(int fd, uint16_t event, void *arg); +static void https_client_cb(int fd, uint16_t event, void *arg); + +/* Debug output helper */ +static void debug_print(const char *msg) +{ + if (server.debug_cb) { + server.debug_cb(msg); + } +} + +/* Format IP address to string (portable, works on any endianness) */ +static void ip_to_str(uint32_t ip, char *buf) +{ + int i = 0; + uint8_t octets[4]; + + /* Extract octets using shifts - portable across all architectures */ + octets[0] = (ip >> 24) & 0xFF; + octets[1] = (ip >> 16) & 0xFF; + octets[2] = (ip >> 8) & 0xFF; + octets[3] = ip & 0xFF; + + for (int octet = 0; octet < 4; octet++) { + uint8_t val = octets[octet]; + if (val >= 100) { + buf[i++] = '0' + (val / 100); + val %= 100; + buf[i++] = '0' + (val / 10); + buf[i++] = '0' + (val % 10); + } else if (val >= 10) { + buf[i++] = '0' + (val / 10); + buf[i++] = '0' + (val % 10); + } else { + buf[i++] = '0' + val; + } + if (octet < 3) buf[i++] = '.'; + } + buf[i] = '\0'; +} + +/* 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'; +} + +/* Check if request starts with a method */ +static int starts_with(const char *str, const char *prefix) +{ + while (*prefix) { + if (*str++ != *prefix++) return 0; + } + return 1; +} + +/* Build HTTP response for status page */ +static int build_status_response(void) +{ + char ip_str[16]; + char uptime_str[12]; + char *p = (char *)server.tx_buf; + + ip_to_str(server.device_ip, ip_str); + uint_to_str(server.uptime_sec, uptime_str); + + /* Build HTML page */ + const char *html_start = + "" + "wolfIP STM32H563" + "" + "
" + "

wolfIP Status

" + "" + "" + "" + "" + "" + "" + "
DeviceSTM32H563
IP Address"; + + const char *html_mid1 = "
Uptime"; + + const char *html_mid2 = " sec
TLSTLS 1.3
CipherECC P-256
" + "

Powered by wolfSSL + wolfIP

" + ""; + + /* Calculate content length */ + int content_len = strlen(html_start) + strlen(ip_str) + strlen(html_mid1) + + strlen(uptime_str) + strlen(html_mid2); + + /* HTTP header */ + strcpy(p, "HTTP/1.1 200 OK\r\n"); + p += strlen(p); + strcpy(p, "Content-Type: text/html\r\n"); + p += strlen(p); + strcpy(p, "Connection: close\r\n"); + p += strlen(p); + strcpy(p, "Content-Length: "); + p += strlen(p); + + char len_str[12]; + uint_to_str(content_len, len_str); + strcpy(p, len_str); + p += strlen(p); + strcpy(p, "\r\n\r\n"); + p += strlen(p); + + /* HTML body */ + strcpy(p, html_start); + p += strlen(p); + strcpy(p, ip_str); + p += strlen(p); + strcpy(p, html_mid1); + p += strlen(p); + strcpy(p, uptime_str); + p += strlen(p); + strcpy(p, html_mid2); + p += strlen(p); + + return (int)(p - (char *)server.tx_buf); +} + +/* Build 404 response */ +static int build_404_response(void) +{ + const char *response = + "HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/html\r\n" + "Connection: close\r\n" + "Content-Length: 44\r\n\r\n" + "

404 Not Found

"; + + strcpy((char *)server.tx_buf, response); + return strlen(response); +} + +/* Parse HTTP request and build response */ +static void process_request(void) +{ + server.rx_buf[server.rx_len] = '\0'; + + if (starts_with((char *)server.rx_buf, "GET / ") || + starts_with((char *)server.rx_buf, "GET /index")) { + server.tx_len = build_status_response(); + } else { + server.tx_len = build_404_response(); + } + server.tx_sent = 0; +} + +int https_server_init(struct wolfIP *stack, uint16_t port, https_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 = HTTPS_STATE_LISTENING; + + debug_print("HTTPS: Initializing wolfSSL\n"); + + /* Initialize wolfSSL (may already be done) */ + wolfSSL_Init(); + + /* Create TLS 1.3 server context */ + server.ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); + if (server.ctx == NULL) { + debug_print("HTTPS: CTX_new failed\n"); + return -1; + } + + /* Load certificate */ + debug_print("HTTPS: Loading certificate\n"); + ret = wolfSSL_CTX_use_certificate_buffer(server.ctx, + (const unsigned char *)server_cert_pem, server_cert_pem_len, + WOLFSSL_FILETYPE_PEM); + if (ret != WOLFSSL_SUCCESS) { + debug_print("HTTPS: Certificate load failed\n"); + return -1; + } + + /* Load private key */ + debug_print("HTTPS: Loading private key\n"); + ret = wolfSSL_CTX_use_PrivateKey_buffer(server.ctx, + (const unsigned char *)server_key_pem, server_key_pem_len, + WOLFSSL_FILETYPE_PEM); + if (ret != WOLFSSL_SUCCESS) { + debug_print("HTTPS: Private key load failed\n"); + return -1; + } + + /* Register wolfIP I/O callbacks */ + wolfSSL_SetIO_wolfIP_CTX(server.ctx, stack); + + /* Create listen socket */ + debug_print("HTTPS: Creating listen socket\n"); + server.listen_fd = wolfIP_sock_socket(stack, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (server.listen_fd < 0) { + debug_print("HTTPS: socket() failed\n"); + 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("HTTPS: bind() failed\n"); + return -1; + } + + /* Listen */ + ret = wolfIP_sock_listen(stack, server.listen_fd, 1); + if (ret < 0) { + debug_print("HTTPS: listen() failed\n"); + return -1; + } + + /* Register callback for incoming connections */ + wolfIP_register_callback(stack, server.listen_fd, https_listen_cb, NULL); + + debug_print("HTTPS: Server ready\n"); + return 0; +} + +/* Callback for listen socket - handles incoming connections */ +static void https_listen_cb(int fd, uint16_t event, void *arg) +{ + struct wolfIP_sockaddr_in client_addr; + socklen_t addr_len = sizeof(client_addr); + int client_fd; + (void)arg; + + if (fd != server.listen_fd) { + return; + } + + if (!(event & CB_EVENT_READABLE)) { + return; + } + + /* Only accept if we're in listening state */ + if (server.state != HTTPS_STATE_LISTENING) { + return; + } + + /* Accept new connection */ + client_fd = wolfIP_sock_accept(server.stack, server.listen_fd, + (struct wolfIP_sockaddr *)&client_addr, &addr_len); + if (client_fd < 0) { + return; + } + + debug_print("HTTPS: Client connected!\n"); + server.client_fd = client_fd; + server.state = HTTPS_STATE_ACCEPTING; + + /* Register callback for client socket */ + wolfIP_register_callback(server.stack, client_fd, https_client_cb, NULL); +} + +/* Callback for client socket - handles data events */ +static void https_client_cb(int fd, uint16_t event, void *arg) +{ + (void)arg; + + if (fd != server.client_fd) { + return; + } + + /* Handle connection closed */ + if (event & CB_EVENT_CLOSED) { + debug_print("HTTPS: Client disconnected\n"); + server.state = HTTPS_STATE_CLOSING; + } +} + +int https_server_poll(void) +{ + int ret; + int err; + + switch (server.state) { + case HTTPS_STATE_LISTENING: + /* Accept is handled by https_listen_cb callback */ + break; + + case HTTPS_STATE_ACCEPTING: + /* Create SSL object */ + server.ssl = wolfSSL_new(server.ctx); + if (server.ssl == NULL) { + debug_print("HTTPS: wolfSSL_new failed\n"); + server.state = HTTPS_STATE_CLOSING; + break; + } + + ret = wolfSSL_SetIO_wolfIP(server.ssl, server.client_fd); + if (ret != 0) { + debug_print("HTTPS: SetIO failed\n"); + server.state = HTTPS_STATE_CLOSING; + break; + } + + server.state = HTTPS_STATE_HANDSHAKE; + break; + + case HTTPS_STATE_HANDSHAKE: + ret = wolfSSL_accept(server.ssl); + if (ret == WOLFSSL_SUCCESS) { + debug_print("HTTPS: TLS handshake complete\n"); + server.rx_len = 0; + server.state = HTTPS_STATE_READ_REQUEST; + } else { + err = wolfSSL_get_error(server.ssl, ret); + if (err != WOLFSSL_ERROR_WANT_READ && + err != WOLFSSL_ERROR_WANT_WRITE) { + debug_print("HTTPS: Handshake failed\n"); + server.state = HTTPS_STATE_CLOSING; + } + } + break; + + case HTTPS_STATE_READ_REQUEST: + ret = wolfSSL_read(server.ssl, + server.rx_buf + server.rx_len, + HTTPS_RX_BUF_SIZE - server.rx_len - 1); + if (ret > 0) { + server.rx_len += ret; + /* Check for end of HTTP request */ + server.rx_buf[server.rx_len] = '\0'; + if (strstr((char *)server.rx_buf, "\r\n\r\n") != NULL) { + process_request(); + server.state = HTTPS_STATE_SEND_RESPONSE; + } + } else { + err = wolfSSL_get_error(server.ssl, ret); + if (err != WOLFSSL_ERROR_WANT_READ) { + server.state = HTTPS_STATE_CLOSING; + } + } + break; + + case HTTPS_STATE_SEND_RESPONSE: + ret = wolfSSL_write(server.ssl, + server.tx_buf + server.tx_sent, + server.tx_len - server.tx_sent); + if (ret > 0) { + server.tx_sent += ret; + if (server.tx_sent >= server.tx_len) { + debug_print("HTTPS: Response sent\n"); + server.state = HTTPS_STATE_CLOSING; + } + } else { + err = wolfSSL_get_error(server.ssl, ret); + if (err != WOLFSSL_ERROR_WANT_WRITE) { + server.state = HTTPS_STATE_CLOSING; + } + } + break; + + case HTTPS_STATE_CLOSING: + if (server.ssl) { + wolfSSL_shutdown(server.ssl); + wolfSSL_free(server.ssl); + server.ssl = NULL; + } + if (server.client_fd >= 0) { + wolfIP_sock_close(server.stack, server.client_fd); + server.client_fd = -1; + } + server.rx_len = 0; + server.tx_len = 0; + server.tx_sent = 0; + server.state = HTTPS_STATE_LISTENING; + break; + } + + return 0; +} + +void https_server_set_info(uint32_t ip_addr, uint32_t uptime_sec) +{ + server.device_ip = ip_addr; + server.uptime_sec = uptime_sec; +} diff --git a/src/port/stm32h563/https_server.h b/src/port/stm32h563/https_server.h new file mode 100644 index 0000000..a7ccda7 --- /dev/null +++ b/src/port/stm32h563/https_server.h @@ -0,0 +1,41 @@ +/* https_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 HTTPS_SERVER_H +#define HTTPS_SERVER_H + +#include "wolfip.h" + +/* Debug callback type */ +typedef void (*https_debug_cb)(const char *msg); + +/* Initialize HTTPS server on specified port + * Returns 0 on success, -1 on failure */ +int https_server_init(struct wolfIP *stack, uint16_t port, https_debug_cb debug); + +/* Poll HTTPS server - call from main loop + * Returns 0 on success */ +int https_server_poll(void); + +/* Set device info for status page */ +void https_server_set_info(uint32_t ip_addr, uint32_t uptime_sec); + +#endif /* HTTPS_SERVER_H */ diff --git a/src/port/stm32h563/main.c b/src/port/stm32h563/main.c index 08d8c64..3ec7c5c 100644 --- a/src/port/stm32h563/main.c +++ b/src/port/stm32h563/main.c @@ -28,10 +28,23 @@ #include "tls_server.h" #include "tls_client.h" #define TLS_PORT 8443 +#endif + +#ifdef ENABLE_HTTPS +#include "https_server.h" +#define HTTPS_WEB_PORT 443 +#endif + +#ifdef ENABLE_SSH +#include "ssh_server.h" +#define SSH_PORT 22 +#endif + +#ifdef ENABLE_TLS /* Google IP for TLS client test (run: dig +short google.com) */ #define GOOGLE_IP "142.250.189.174" -#define HTTPS_PORT 443 +#define GOOGLE_HTTPS_PORT 443 /* TLS client test state */ static int tls_client_test_started = 0; @@ -557,12 +570,43 @@ int main(void) } #endif +#ifdef ENABLE_HTTPS + uart_puts("Initializing HTTPS server on port 443...\n"); + if (https_server_init(IPStack, HTTPS_WEB_PORT, uart_puts) < 0) { + uart_puts("ERROR: HTTPS server init 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 + 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 */ + { + ip4 ip = 0, nm = 0, gw = 0; + wolfIP_ipconfig_get(IPStack, &ip, &nm, &gw); + https_server_set_info(ip, (uint32_t)(tick / 1000)); + } + + /* Poll HTTPS server */ + https_server_poll(); +#endif + +#ifdef ENABLE_SSH + /* Poll SSH server */ + ssh_server_poll(); +#endif + #ifdef ENABLE_TLS /* TLS client test: connect to Google after network settles */ if (!tls_client_test_started && tick > 5000) { @@ -570,10 +614,10 @@ int main(void) uart_puts("Target: "); uart_puts(GOOGLE_IP); uart_puts(":"); - uart_putdec(HTTPS_PORT); + uart_putdec(GOOGLE_HTTPS_PORT); uart_puts("\n"); - if (tls_client_connect(GOOGLE_IP, HTTPS_PORT, tls_response_cb, NULL) == 0) { + 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"); 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..fe45d39 --- /dev/null +++ b/src/port/stm32h563/ssh_server.c @@ -0,0 +1,421 @@ +/* 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 int wolfSSH_SetIO_wolfIP(WOLFSSH *ssh, struct wolfIP *stack, int fd); + +/* 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->usernameLen != strlen(SSH_USERNAME) || + memcmp(authData->username, SSH_USERNAME, authData->usernameLen) != 0) { + debug_print("SSH: Invalid username\n"); + return WOLFSSH_USERAUTH_INVALID_USER; + } + + /* Check password */ + if (authData->sf.password.passwordLen != strlen(SSH_PASSWORD) || + memcmp(authData->sf.password.password, SSH_PASSWORD, + authData->sf.password.passwordLen) != 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; + } + + /* 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 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 */ + debug_print("SSH: Creating listen socket\n"); + 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/user_settings.h b/src/port/stm32h563/user_settings.h index cb91487..2663127 100644 --- a/src/port/stm32h563/user_settings.h +++ b/src/port/stm32h563/user_settings.h @@ -158,11 +158,28 @@ int custom_rand_gen_block(unsigned char* output, unsigned int sz); /* wolfSSH Settings (when ENABLE_SSH=1) */ /* ------------------------------------------------------------------------- */ #ifdef ENABLE_SSH +/* Disable features not needed for basic shell */ #define WOLFSSH_NO_TIMESTAMP #define WOLFSSH_NO_AGENT #define WOLFSSH_NO_SFTP +#define WOLFSSH_NO_SCP + +/* Memory optimization */ #define WOLFSSH_SMALL_STACK +#define DEFAULT_WINDOW_SZ (16 * 1024) +#define DEFAULT_HIGHWATER_MARK ((DEFAULT_WINDOW_SZ * 3) / 4) + +/* Terminal support for shell */ #define WOLFSSH_TERM + +/* 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 /* ------------------------------------------------------------------------- */ diff --git a/src/port/wolfssh_io.c b/src/port/wolfssh_io.c new file mode 100644 index 0000000..439ff32 --- /dev/null +++ b/src/port/wolfssh_io.c @@ -0,0 +1,147 @@ +/* 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 for a wolfIP socket */ +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_SetIORecv(ssh, wolfssh_io_recv); + wolfSSH_SetIOSend(ssh, wolfssh_io_send); + 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); + } +} From ba91cc98838e7950ac88350b8d8e005e47c6af19 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Wed, 21 Jan 2026 12:04:21 -0800 Subject: [PATCH 3/6] Fix SSH server for STM32H563 bare-metal operation Commit Body: Fix wolfSSH integration and optimize memory for embedded deployment. wolfSSH API updates: - Add wolfSSH_CTX_SetIO_wolfIP() for context-level I/O callbacks - Update field names: usernameLen->usernameSz, passwordLen->passwordSz - Wrap debug logging callback in #ifdef DEBUG_WOLFSSH Memory optimization for STM32H563 (640KB SRAM): - Reduce wolfSSH window size from 16KB to 4KB - Reduce wolfIP RX/TX buffers from 16x to 8x MTU - Reduce MAX_TCPSOCKETS to 6, MAX_ICMPSOCKETS to 1 wolfSSL/wolfSSH configuration: - Add WOLFSSL_WOLFSSH for wc_SSH_KDF support - Add WOLFSSL_KEY_GEN for host key generation - Add WOLFSSH_NO_TERM for bare-metal operation Build system: - Add signature.c for SSH signature verification - Fix pattern rules for wolfSSH compilation Tested: SSH login (admin/wolfip), shell commands working --- src/port/stm32h563/Makefile | 14 ++++++++++---- src/port/stm32h563/config.h | 8 ++++---- src/port/stm32h563/ssh_server.c | 31 +++++++++++++++++++++++++----- src/port/stm32h563/user_settings.h | 16 ++++++++++----- src/port/wolfssh_io.c | 13 ++++++++++--- 5 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/port/stm32h563/Makefile b/src/port/stm32h563/Makefile index 2d79f5e..2f9e4c7 100644 --- a/src/port/stm32h563/Makefile +++ b/src/port/stm32h563/Makefile @@ -105,6 +105,12 @@ WOLFSSL_SRCS += \ 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 @@ -143,9 +149,9 @@ WOLFSSH_SRCS := \ SRCS += $(WOLFSSH_SRCS) -# wolfSSH objects use relaxed warnings +# wolfSSH objects use relaxed warnings + SSH/SSL include paths + user_settings.h $(WOLFSSH_ROOT)/%.o: $(WOLFSSH_ROOT)/%.c - $(CC) $(CFLAGS_WOLFSSL) -c $< -o $@ + $(CC) $(CFLAGS_WOLFSSL) -DENABLE_SSH -DWOLFSSL_USER_SETTINGS -DWOLFSSH_USER_SETTINGS -I$(WOLFSSH_ROOT) -I$(WOLFSSL_ROOT) -c $< -o $@ endif # ENABLE_SSH @@ -172,9 +178,9 @@ app.bin: app.elf %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ -# wolfSSL objects use relaxed warnings +# wolfSSL objects use relaxed warnings + user_settings.h + include paths $(WOLFSSL_ROOT)/%.o: $(WOLFSSL_ROOT)/%.c - $(CC) $(CFLAGS_WOLFSSL) -c $< -o $@ + $(CC) $(CFLAGS_WOLFSSL) -DWOLFSSL_USER_SETTINGS $(if $(filter 1,$(ENABLE_SSH)),-DENABLE_SSH) -I$(WOLFSSL_ROOT) -c $< -o $@ clean: rm -f *.o app.elf app.bin diff --git a/src/port/stm32h563/config.h b/src/port/stm32h563/config.h index 89ccf35..d75554a 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 8 +#define MAX_TCPSOCKETS 6 /* 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/ssh_server.c b/src/port/stm32h563/ssh_server.c index fe45d39..cf21852 100644 --- a/src/port/stm32h563/ssh_server.c +++ b/src/port/stm32h563/ssh_server.c @@ -62,8 +62,21 @@ static struct { } 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) { @@ -82,16 +95,16 @@ static int ssh_userauth_cb(byte authType, WS_UserAuthData *authData, void *ctx) } /* Check username */ - if (authData->usernameLen != strlen(SSH_USERNAME) || - memcmp(authData->username, SSH_USERNAME, authData->usernameLen) != 0) { + 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.passwordLen != strlen(SSH_PASSWORD) || + if (authData->sf.password.passwordSz != strlen(SSH_PASSWORD) || memcmp(authData->sf.password.password, SSH_PASSWORD, - authData->sf.password.passwordLen) != 0) { + authData->sf.password.passwordSz) != 0) { debug_print("SSH: Invalid password\n"); return WOLFSSH_USERAUTH_INVALID_PASSWORD; } @@ -212,6 +225,12 @@ int ssh_server_init(struct wolfIP *stack, uint16_t port, ssh_debug_cb debug) 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) { @@ -219,6 +238,9 @@ int ssh_server_init(struct wolfIP *stack, uint16_t port, ssh_debug_cb debug) 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); @@ -235,7 +257,6 @@ int ssh_server_init(struct wolfIP *stack, uint16_t port, ssh_debug_cb debug) } /* Create listen socket */ - debug_print("SSH: Creating listen socket\n"); server.listen_fd = wolfIP_sock_socket(stack, AF_INET, IPSTACK_SOCK_STREAM, 0); if (server.listen_fd < 0) { debug_print("SSH: socket() failed\n"); diff --git a/src/port/stm32h563/user_settings.h b/src/port/stm32h563/user_settings.h index 2663127..b107b2f 100644 --- a/src/port/stm32h563/user_settings.h +++ b/src/port/stm32h563/user_settings.h @@ -153,24 +153,30 @@ int custom_rand_gen_block(unsigned char* output, unsigned int sz); /* ------------------------------------------------------------------------- */ /* #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 -/* Memory optimization */ +/* Bare-metal: no termios/pty support */ +#define WOLFSSH_NO_TERM + +/* Memory optimization - reduced for embedded */ #define WOLFSSH_SMALL_STACK -#define DEFAULT_WINDOW_SZ (16 * 1024) +#define DEFAULT_WINDOW_SZ (4 * 1024) /* Reduced from 16KB to 4KB */ #define DEFAULT_HIGHWATER_MARK ((DEFAULT_WINDOW_SZ * 3) / 4) - -/* Terminal support for shell */ -#define WOLFSSH_TERM +#define MAX_PACKET_SZ (DEFAULT_WINDOW_SZ + 256) /* Custom I/O - we use wolfIP sockets */ #define WOLFSSH_USER_IO diff --git a/src/port/wolfssh_io.c b/src/port/wolfssh_io.c index 439ff32..38a9db7 100644 --- a/src/port/wolfssh_io.c +++ b/src/port/wolfssh_io.c @@ -106,7 +106,16 @@ static int wolfssh_io_send(WOLFSSH *ssh, void *buf, word32 sz, void *ctx) return ret; } -/* Set up wolfSSH I/O callbacks for a wolfIP socket */ +/* 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; @@ -123,8 +132,6 @@ int wolfSSH_SetIO_wolfIP(WOLFSSH *ssh, struct wolfIP *stack, int fd) desc->stack = stack; desc->fd = fd; - wolfSSH_SetIORecv(ssh, wolfssh_io_recv); - wolfSSH_SetIOSend(ssh, wolfssh_io_send); wolfSSH_SetIOReadCtx(ssh, desc); wolfSSH_SetIOWriteCtx(ssh, desc); From 31c0cd14bfb6d121639366fc09bc3f1d6b6d881c Mon Sep 17 00:00:00 2001 From: aidan garske Date: Wed, 21 Jan 2026 14:46:18 -0800 Subject: [PATCH 4/6] Add MQTT client support for STM32H563 bare-metal operation - Add mqtt_client.c/h: Non-blocking MQTT client with state machine (IDLE -> CONNECTING -> TLS -> MQTT_CONNECT -> CONNECTED) - Add wolfmqtt_io.c: I/O glue layer for wolfIP sockets - Update Makefile: Add ENABLE_MQTT build option and wolfMQTT sources - Update user_settings.h: Add wolfMQTT configuration (WOLFMQTT_NONBLOCK, WOLFMQTT_USER_IO, WOLFMQTT_NO_STDIO, EWOULDBLOCK/EAGAIN defines) - Update main.c: Integrate MQTT client into main loop with periodic status publishing - Update README.md: Add MQTT documentation section Features: - TLS 1.3 encrypted connection to MQTT broker (port 8883) - Connects to test.mosquitto.org by default - Publishes status messages to wolfip/status topic - QoS 0 (fire and forget) for minimal overhead - 60 second keep-alive Build: make ENABLE_TLS=1 ENABLE_MQTT=1 --- src/port/stm32h563/Makefile | 63 ++- src/port/stm32h563/README.md | 96 +++++ src/port/stm32h563/main.c | 65 ++++ src/port/stm32h563/mqtt_client.c | 589 +++++++++++++++++++++++++++++ src/port/stm32h563/mqtt_client.h | 68 ++++ src/port/stm32h563/user_settings.h | 20 + src/port/wolfmqtt_io.c | 246 ++++++++++++ 7 files changed, 1141 insertions(+), 6 deletions(-) create mode 100644 src/port/stm32h563/mqtt_client.c create mode 100644 src/port/stm32h563/mqtt_client.h create mode 100644 src/port/wolfmqtt_io.c diff --git a/src/port/stm32h563/Makefile b/src/port/stm32h563/Makefile index 2f9e4c7..907d1d4 100644 --- a/src/port/stm32h563/Makefile +++ b/src/port/stm32h563/Makefile @@ -17,9 +17,13 @@ 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 @@ -155,19 +159,59 @@ $(WOLFSSH_ROOT)/%.o: $(WOLFSSH_ROOT)/%.c 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) ENABLE_TLS=$(ENABLE_TLS) ENABLE_HTTPS=$(ENABLE_HTTPS) ENABLE_SSH=$(ENABLE_SSH)" + @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 $@ @@ -193,6 +237,9 @@ endif ifeq ($(ENABLE_SSH),1) rm -f $(WOLFSSH_ROOT)/src/*.o endif +ifeq ($(ENABLE_MQTT),1) + rm -f $(WOLFMQTT_ROOT)/src/*.o +endif .PHONY: all clean @@ -214,20 +261,24 @@ help: @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 "" @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_HTTPS=1 ENABLE_SSH=1 # Full featured" + @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 "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: help diff --git a/src/port/stm32h563/README.md b/src/port/stm32h563/README.md index 62d11d7..c1e43f1 100644 --- a/src/port/stm32h563/README.md +++ b/src/port/stm32h563/README.md @@ -587,6 +587,100 @@ 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 | @@ -608,6 +702,8 @@ xxd -i ssh_host_key.der > ssh_keys.h | `https_server.c/h` | HTTPS web server (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) | +| `../wolfmqtt_io.c` | wolfMQTT I/O callbacks for wolfIP (MQTT builds only) | ## Troubleshooting diff --git a/src/port/stm32h563/main.c b/src/port/stm32h563/main.c index 3ec7c5c..8f59519 100644 --- a/src/port/stm32h563/main.c +++ b/src/port/stm32h563/main.c @@ -40,6 +40,10 @@ #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) */ @@ -584,6 +588,22 @@ int main(void) } #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"); @@ -607,6 +627,51 @@ int main(void) 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) { diff --git a/src/port/stm32h563/mqtt_client.c b/src/port/stm32h563/mqtt_client.c new file mode 100644 index 0000000..adc8179 --- /dev/null +++ b/src/port/stm32h563/mqtt_client.c @@ -0,0 +1,589 @@ +/* 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++; + /* Print status every ~50000 polls */ + if ((connect_polls % 50000) == 0) { + debug_print("MQTT: Waiting for TCP...\n"); + } + 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/user_settings.h b/src/port/stm32h563/user_settings.h index b107b2f..da82855 100644 --- a/src/port/stm32h563/user_settings.h +++ b/src/port/stm32h563/user_settings.h @@ -192,8 +192,28 @@ int custom_rand_gen_block(unsigned char* output, unsigned int sz); /* 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 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; + } +} From efbe4d6ae8411656773bd95025773f89e83540c3 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Wed, 21 Jan 2026 16:23:50 -0800 Subject: [PATCH 5/6] Use existing wolfIP httpd for HTTPS server --- src/http/httpd.c | 4 +- src/port/stm32h563/Makefile | 4 +- src/port/stm32h563/config.h | 2 +- src/port/stm32h563/https_server.c | 481 ------------------------------ src/port/stm32h563/https_server.h | 41 --- src/port/stm32h563/main.c | 102 ++++++- src/port/stm32h563/mqtt_client.c | 4 - 7 files changed, 95 insertions(+), 543 deletions(-) delete mode 100644 src/port/stm32h563/https_server.c delete mode 100644 src/port/stm32h563/https_server.h 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 907d1d4..b7f0247 100644 --- a/src/port/stm32h563/Makefile +++ b/src/port/stm32h563/Makefile @@ -69,10 +69,10 @@ SRCS += tls_server.c SRCS += tls_client.c SRCS += $(ROOT)/src/port/wolfssl_io.c -# HTTPS web server (requires TLS) +# HTTPS web server (requires TLS) - uses existing wolfIP httpd ifeq ($(ENABLE_HTTPS),1) CFLAGS += -DENABLE_HTTPS -SRCS += https_server.c +SRCS += $(ROOT)/src/http/httpd.c endif # wolfSSL source files (minimal set for TLS 1.3 server with ECC) diff --git a/src/port/stm32h563/config.h b/src/port/stm32h563/config.h index d75554a..b3f5f79 100644 --- a/src/port/stm32h563/config.h +++ b/src/port/stm32h563/config.h @@ -28,7 +28,7 @@ #define ETHERNET #define LINK_MTU 1536 -#define MAX_TCPSOCKETS 6 /* Need enough for listen + accepted sockets */ +#define MAX_TCPSOCKETS 12 /* Need enough for listen + accepted sockets */ #define MAX_UDPSOCKETS 2 #define MAX_ICMPSOCKETS 1 /* Reduced from 2 */ #define RXBUF_SIZE (LINK_MTU * 8) /* Reduced from 16 */ diff --git a/src/port/stm32h563/https_server.c b/src/port/stm32h563/https_server.c deleted file mode 100644 index 187f76f..0000000 --- a/src/port/stm32h563/https_server.c +++ /dev/null @@ -1,481 +0,0 @@ -/* https_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 "https_server.h" -#include "certs.h" - -#include -#include -#include - -/* Configuration */ -#define HTTPS_RX_BUF_SIZE 1024 -#define HTTPS_TX_BUF_SIZE 2048 - -/* Server state */ -typedef enum { - HTTPS_STATE_LISTENING, - HTTPS_STATE_ACCEPTING, - HTTPS_STATE_HANDSHAKE, - HTTPS_STATE_READ_REQUEST, - HTTPS_STATE_SEND_RESPONSE, - HTTPS_STATE_CLOSING -} https_state_t; - -/* Server context */ -static struct { - struct wolfIP *stack; - WOLFSSL_CTX *ctx; - WOLFSSL *ssl; - int listen_fd; - int client_fd; - https_state_t state; - https_debug_cb debug_cb; - uint8_t rx_buf[HTTPS_RX_BUF_SIZE]; - uint8_t tx_buf[HTTPS_TX_BUF_SIZE]; - int rx_len; - int tx_len; - int tx_sent; - uint32_t device_ip; - uint32_t uptime_sec; -} server; - -/* 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); - -/* Forward declarations */ -static void https_listen_cb(int fd, uint16_t event, void *arg); -static void https_client_cb(int fd, uint16_t event, void *arg); - -/* Debug output helper */ -static void debug_print(const char *msg) -{ - if (server.debug_cb) { - server.debug_cb(msg); - } -} - -/* Format IP address to string (portable, works on any endianness) */ -static void ip_to_str(uint32_t ip, char *buf) -{ - int i = 0; - uint8_t octets[4]; - - /* Extract octets using shifts - portable across all architectures */ - octets[0] = (ip >> 24) & 0xFF; - octets[1] = (ip >> 16) & 0xFF; - octets[2] = (ip >> 8) & 0xFF; - octets[3] = ip & 0xFF; - - for (int octet = 0; octet < 4; octet++) { - uint8_t val = octets[octet]; - if (val >= 100) { - buf[i++] = '0' + (val / 100); - val %= 100; - buf[i++] = '0' + (val / 10); - buf[i++] = '0' + (val % 10); - } else if (val >= 10) { - buf[i++] = '0' + (val / 10); - buf[i++] = '0' + (val % 10); - } else { - buf[i++] = '0' + val; - } - if (octet < 3) buf[i++] = '.'; - } - buf[i] = '\0'; -} - -/* 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'; -} - -/* Check if request starts with a method */ -static int starts_with(const char *str, const char *prefix) -{ - while (*prefix) { - if (*str++ != *prefix++) return 0; - } - return 1; -} - -/* Build HTTP response for status page */ -static int build_status_response(void) -{ - char ip_str[16]; - char uptime_str[12]; - char *p = (char *)server.tx_buf; - - ip_to_str(server.device_ip, ip_str); - uint_to_str(server.uptime_sec, uptime_str); - - /* Build HTML page */ - const char *html_start = - "" - "wolfIP STM32H563" - "" - "
" - "

wolfIP Status

" - "" - "" - "" - "" - "" - "" - "
DeviceSTM32H563
IP Address"; - - const char *html_mid1 = "
Uptime"; - - const char *html_mid2 = " sec
TLSTLS 1.3
CipherECC P-256
" - "

Powered by wolfSSL + wolfIP

" - ""; - - /* Calculate content length */ - int content_len = strlen(html_start) + strlen(ip_str) + strlen(html_mid1) + - strlen(uptime_str) + strlen(html_mid2); - - /* HTTP header */ - strcpy(p, "HTTP/1.1 200 OK\r\n"); - p += strlen(p); - strcpy(p, "Content-Type: text/html\r\n"); - p += strlen(p); - strcpy(p, "Connection: close\r\n"); - p += strlen(p); - strcpy(p, "Content-Length: "); - p += strlen(p); - - char len_str[12]; - uint_to_str(content_len, len_str); - strcpy(p, len_str); - p += strlen(p); - strcpy(p, "\r\n\r\n"); - p += strlen(p); - - /* HTML body */ - strcpy(p, html_start); - p += strlen(p); - strcpy(p, ip_str); - p += strlen(p); - strcpy(p, html_mid1); - p += strlen(p); - strcpy(p, uptime_str); - p += strlen(p); - strcpy(p, html_mid2); - p += strlen(p); - - return (int)(p - (char *)server.tx_buf); -} - -/* Build 404 response */ -static int build_404_response(void) -{ - const char *response = - "HTTP/1.1 404 Not Found\r\n" - "Content-Type: text/html\r\n" - "Connection: close\r\n" - "Content-Length: 44\r\n\r\n" - "

404 Not Found

"; - - strcpy((char *)server.tx_buf, response); - return strlen(response); -} - -/* Parse HTTP request and build response */ -static void process_request(void) -{ - server.rx_buf[server.rx_len] = '\0'; - - if (starts_with((char *)server.rx_buf, "GET / ") || - starts_with((char *)server.rx_buf, "GET /index")) { - server.tx_len = build_status_response(); - } else { - server.tx_len = build_404_response(); - } - server.tx_sent = 0; -} - -int https_server_init(struct wolfIP *stack, uint16_t port, https_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 = HTTPS_STATE_LISTENING; - - debug_print("HTTPS: Initializing wolfSSL\n"); - - /* Initialize wolfSSL (may already be done) */ - wolfSSL_Init(); - - /* Create TLS 1.3 server context */ - server.ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); - if (server.ctx == NULL) { - debug_print("HTTPS: CTX_new failed\n"); - return -1; - } - - /* Load certificate */ - debug_print("HTTPS: Loading certificate\n"); - ret = wolfSSL_CTX_use_certificate_buffer(server.ctx, - (const unsigned char *)server_cert_pem, server_cert_pem_len, - WOLFSSL_FILETYPE_PEM); - if (ret != WOLFSSL_SUCCESS) { - debug_print("HTTPS: Certificate load failed\n"); - return -1; - } - - /* Load private key */ - debug_print("HTTPS: Loading private key\n"); - ret = wolfSSL_CTX_use_PrivateKey_buffer(server.ctx, - (const unsigned char *)server_key_pem, server_key_pem_len, - WOLFSSL_FILETYPE_PEM); - if (ret != WOLFSSL_SUCCESS) { - debug_print("HTTPS: Private key load failed\n"); - return -1; - } - - /* Register wolfIP I/O callbacks */ - wolfSSL_SetIO_wolfIP_CTX(server.ctx, stack); - - /* Create listen socket */ - debug_print("HTTPS: Creating listen socket\n"); - server.listen_fd = wolfIP_sock_socket(stack, AF_INET, IPSTACK_SOCK_STREAM, 0); - if (server.listen_fd < 0) { - debug_print("HTTPS: socket() failed\n"); - 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("HTTPS: bind() failed\n"); - return -1; - } - - /* Listen */ - ret = wolfIP_sock_listen(stack, server.listen_fd, 1); - if (ret < 0) { - debug_print("HTTPS: listen() failed\n"); - return -1; - } - - /* Register callback for incoming connections */ - wolfIP_register_callback(stack, server.listen_fd, https_listen_cb, NULL); - - debug_print("HTTPS: Server ready\n"); - return 0; -} - -/* Callback for listen socket - handles incoming connections */ -static void https_listen_cb(int fd, uint16_t event, void *arg) -{ - struct wolfIP_sockaddr_in client_addr; - socklen_t addr_len = sizeof(client_addr); - int client_fd; - (void)arg; - - if (fd != server.listen_fd) { - return; - } - - if (!(event & CB_EVENT_READABLE)) { - return; - } - - /* Only accept if we're in listening state */ - if (server.state != HTTPS_STATE_LISTENING) { - return; - } - - /* Accept new connection */ - client_fd = wolfIP_sock_accept(server.stack, server.listen_fd, - (struct wolfIP_sockaddr *)&client_addr, &addr_len); - if (client_fd < 0) { - return; - } - - debug_print("HTTPS: Client connected!\n"); - server.client_fd = client_fd; - server.state = HTTPS_STATE_ACCEPTING; - - /* Register callback for client socket */ - wolfIP_register_callback(server.stack, client_fd, https_client_cb, NULL); -} - -/* Callback for client socket - handles data events */ -static void https_client_cb(int fd, uint16_t event, void *arg) -{ - (void)arg; - - if (fd != server.client_fd) { - return; - } - - /* Handle connection closed */ - if (event & CB_EVENT_CLOSED) { - debug_print("HTTPS: Client disconnected\n"); - server.state = HTTPS_STATE_CLOSING; - } -} - -int https_server_poll(void) -{ - int ret; - int err; - - switch (server.state) { - case HTTPS_STATE_LISTENING: - /* Accept is handled by https_listen_cb callback */ - break; - - case HTTPS_STATE_ACCEPTING: - /* Create SSL object */ - server.ssl = wolfSSL_new(server.ctx); - if (server.ssl == NULL) { - debug_print("HTTPS: wolfSSL_new failed\n"); - server.state = HTTPS_STATE_CLOSING; - break; - } - - ret = wolfSSL_SetIO_wolfIP(server.ssl, server.client_fd); - if (ret != 0) { - debug_print("HTTPS: SetIO failed\n"); - server.state = HTTPS_STATE_CLOSING; - break; - } - - server.state = HTTPS_STATE_HANDSHAKE; - break; - - case HTTPS_STATE_HANDSHAKE: - ret = wolfSSL_accept(server.ssl); - if (ret == WOLFSSL_SUCCESS) { - debug_print("HTTPS: TLS handshake complete\n"); - server.rx_len = 0; - server.state = HTTPS_STATE_READ_REQUEST; - } else { - err = wolfSSL_get_error(server.ssl, ret); - if (err != WOLFSSL_ERROR_WANT_READ && - err != WOLFSSL_ERROR_WANT_WRITE) { - debug_print("HTTPS: Handshake failed\n"); - server.state = HTTPS_STATE_CLOSING; - } - } - break; - - case HTTPS_STATE_READ_REQUEST: - ret = wolfSSL_read(server.ssl, - server.rx_buf + server.rx_len, - HTTPS_RX_BUF_SIZE - server.rx_len - 1); - if (ret > 0) { - server.rx_len += ret; - /* Check for end of HTTP request */ - server.rx_buf[server.rx_len] = '\0'; - if (strstr((char *)server.rx_buf, "\r\n\r\n") != NULL) { - process_request(); - server.state = HTTPS_STATE_SEND_RESPONSE; - } - } else { - err = wolfSSL_get_error(server.ssl, ret); - if (err != WOLFSSL_ERROR_WANT_READ) { - server.state = HTTPS_STATE_CLOSING; - } - } - break; - - case HTTPS_STATE_SEND_RESPONSE: - ret = wolfSSL_write(server.ssl, - server.tx_buf + server.tx_sent, - server.tx_len - server.tx_sent); - if (ret > 0) { - server.tx_sent += ret; - if (server.tx_sent >= server.tx_len) { - debug_print("HTTPS: Response sent\n"); - server.state = HTTPS_STATE_CLOSING; - } - } else { - err = wolfSSL_get_error(server.ssl, ret); - if (err != WOLFSSL_ERROR_WANT_WRITE) { - server.state = HTTPS_STATE_CLOSING; - } - } - break; - - case HTTPS_STATE_CLOSING: - if (server.ssl) { - wolfSSL_shutdown(server.ssl); - wolfSSL_free(server.ssl); - server.ssl = NULL; - } - if (server.client_fd >= 0) { - wolfIP_sock_close(server.stack, server.client_fd); - server.client_fd = -1; - } - server.rx_len = 0; - server.tx_len = 0; - server.tx_sent = 0; - server.state = HTTPS_STATE_LISTENING; - break; - } - - return 0; -} - -void https_server_set_info(uint32_t ip_addr, uint32_t uptime_sec) -{ - server.device_ip = ip_addr; - server.uptime_sec = uptime_sec; -} diff --git a/src/port/stm32h563/https_server.h b/src/port/stm32h563/https_server.h deleted file mode 100644 index a7ccda7..0000000 --- a/src/port/stm32h563/https_server.h +++ /dev/null @@ -1,41 +0,0 @@ -/* https_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 HTTPS_SERVER_H -#define HTTPS_SERVER_H - -#include "wolfip.h" - -/* Debug callback type */ -typedef void (*https_debug_cb)(const char *msg); - -/* Initialize HTTPS server on specified port - * Returns 0 on success, -1 on failure */ -int https_server_init(struct wolfIP *stack, uint16_t port, https_debug_cb debug); - -/* Poll HTTPS server - call from main loop - * Returns 0 on success */ -int https_server_poll(void); - -/* Set device info for status page */ -void https_server_set_info(uint32_t ip_addr, uint32_t uptime_sec); - -#endif /* HTTPS_SERVER_H */ diff --git a/src/port/stm32h563/main.c b/src/port/stm32h563/main.c index 8f59519..e9fe260 100644 --- a/src/port/stm32h563/main.c +++ b/src/port/stm32h563/main.c @@ -31,7 +31,8 @@ #endif #ifdef ENABLE_HTTPS -#include "https_server.h" +#include "http/httpd.h" +#include "certs.h" #define HTTPS_WEB_PORT 443 #endif @@ -55,6 +56,72 @@ 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 @@ -576,8 +643,25 @@ int main(void) #ifdef ENABLE_HTTPS uart_puts("Initializing HTTPS server on port 443...\n"); - if (https_server_init(IPStack, HTTPS_WEB_PORT, uart_puts) < 0) { - uart_puts("ERROR: HTTPS server init failed\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 @@ -611,15 +695,9 @@ int main(void) (void)wolfIP_poll(IPStack, tick++); #ifdef ENABLE_HTTPS - /* Update HTTPS server status info */ - { - ip4 ip = 0, nm = 0, gw = 0; - wolfIP_ipconfig_get(IPStack, &ip, &nm, &gw); - https_server_set_info(ip, (uint32_t)(tick / 1000)); - } - - /* Poll HTTPS server */ - https_server_poll(); + /* 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 diff --git a/src/port/stm32h563/mqtt_client.c b/src/port/stm32h563/mqtt_client.c index adc8179..f39eff5 100644 --- a/src/port/stm32h563/mqtt_client.c +++ b/src/port/stm32h563/mqtt_client.c @@ -300,10 +300,6 @@ static int handle_net_connect(void) if (ret == -WOLFIP_EAGAIN || ret == -11) { connect_polls++; - /* Print status every ~50000 polls */ - if ((connect_polls % 50000) == 0) { - debug_print("MQTT: Waiting for TCP...\n"); - } return 0; /* Still connecting (EAGAIN/EINPROGRESS) */ } From bb5463e8a2f29537582b6a92776e9bd9bd314ad4 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 22 Jan 2026 15:04:58 -0800 Subject: [PATCH 6/6] Minor cleanups --- src/port/stm32h563/Makefile | 36 ++++++- src/port/stm32h563/README.md | 168 ++++++++++++++++++++++++++--- src/port/stm32h563/user_settings.h | 4 + 3 files changed, 191 insertions(+), 17 deletions(-) diff --git a/src/port/stm32h563/Makefile b/src/port/stm32h563/Makefile index b7f0247..0ab8105 100644 --- a/src/port/stm32h563/Makefile +++ b/src/port/stm32h563/Makefile @@ -71,6 +71,12 @@ 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 @@ -241,7 +247,27 @@ ifeq ($(ENABLE_MQTT),1) rm -f $(WOLFMQTT_ROOT)/src/*.o endif -.PHONY: all clean +# 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 @@ -254,6 +280,8 @@ help: @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:" @@ -265,6 +293,8 @@ help: @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)" @@ -274,6 +304,10 @@ help: @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" diff --git a/src/port/stm32h563/README.md b/src/port/stm32h563/README.md index c1e43f1..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,11 +60,47 @@ 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). +### 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 @@ -468,16 +537,15 @@ openssl s_client -connect :443 -tls1_3 ```html wolfIP STM32H563 -... -

wolfIP Status

- - - - - - -
DeviceSTM32H563
IP Address192.168.0.197
Uptime1234 sec
TLSTLS 1.3
CipherECC P-256
-... + +

wolfIP Status

+ + + + +
DeviceSTM32H563
IP Address10.0.4.117
Uptime1234 sec
TLSTLS 1.3
``` ### HTTPS Status Page @@ -487,7 +555,6 @@ The status page displays: - IP address (dynamically updated from DHCP or static config) - Uptime in seconds (continuously updated) - TLS version (TLS 1.3) -- Cipher suite (ECC P-256) ## SSH Server @@ -695,14 +762,16 @@ This provides: | `target_tzen.ld` | Linker script for TZEN=1 | | `config.h` | Build configuration | | `Makefile` | Build system | -| `user_settings.h` | wolfSSL/wolfSSH configuration | +| `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) | -| `https_server.c/h` | HTTPS web server (HTTPS 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 @@ -712,18 +781,74 @@ This provides: - 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 @@ -731,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/user_settings.h b/src/port/stm32h563/user_settings.h index da82855..7bb553b 100644 --- a/src/port/stm32h563/user_settings.h +++ b/src/port/stm32h563/user_settings.h @@ -39,6 +39,10 @@ extern "C" { #define NO_WRITEV #define NO_MAIN_DRIVER +/* Bare-metal: no system headers */ +#define WOLFSSL_NO_SOCK +#define NO_WOLFSSL_DIR + /* ------------------------------------------------------------------------- */ /* Math - Portable C implementation */ /* ------------------------------------------------------------------------- */