From ffc3c6e4ba7d4cae36c3e17995345181769097de Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Tue, 27 Jan 2026 15:30:24 +0100 Subject: [PATCH] examples: add wolfHSM DTLS client --- .../workflows/build-and-test-dtls-client.yml | 105 ++++ .../user_settings.h.70BDD9097831BA2D.idx | Bin 0 -> 690 bytes ...h_posix_dtls_client.c.E5862DCB5B11F175.idx | Bin 0 -> 5442 bytes .../index/wolfhsm_cfg.h.BE11D43E6AF362BB.idx | Bin 0 -> 300 bytes .../posix/tls/wh_posix_dtls_client/Makefile | 184 +++++++ .../tls/wh_posix_dtls_client/user_settings.h | 52 ++ .../wh_posix_dtls_client.c | 453 ++++++++++++++++++ .../tls/wh_posix_dtls_client/wolfhsm_cfg.h | 37 ++ 8 files changed, 831 insertions(+) create mode 100644 .github/workflows/build-and-test-dtls-client.yml create mode 100644 examples/posix/tls/wh_posix_dtls_client/.cache/clangd/index/user_settings.h.70BDD9097831BA2D.idx create mode 100644 examples/posix/tls/wh_posix_dtls_client/.cache/clangd/index/wh_posix_dtls_client.c.E5862DCB5B11F175.idx create mode 100644 examples/posix/tls/wh_posix_dtls_client/.cache/clangd/index/wolfhsm_cfg.h.BE11D43E6AF362BB.idx create mode 100644 examples/posix/tls/wh_posix_dtls_client/Makefile create mode 100644 examples/posix/tls/wh_posix_dtls_client/user_settings.h create mode 100644 examples/posix/tls/wh_posix_dtls_client/wh_posix_dtls_client.c create mode 100644 examples/posix/tls/wh_posix_dtls_client/wolfhsm_cfg.h diff --git a/.github/workflows/build-and-test-dtls-client.yml b/.github/workflows/build-and-test-dtls-client.yml new file mode 100644 index 000000000..bb78cb8ac --- /dev/null +++ b/.github/workflows/build-and-test-dtls-client.yml @@ -0,0 +1,105 @@ +name: Build and Test DTLS Client Example + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +jobs: + build-and-test-dtls: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + # Checkout repositories + - name: Checkout wolfHSM + uses: actions/checkout@v4 + + - name: Checkout wolfSSL + uses: actions/checkout@v4 + with: + repository: wolfssl/wolfssl + path: wolfssl + + # Build wolfSSL with DTLS support + - name: Build wolfSSL with DTLS + run: | + cd wolfssl + ./autogen.sh + ./configure --enable-dtls --enable-dtls13 --enable-ecc + make -j + + # Build wolfHSM server + - name: Build wolfHSM POSIX server + run: | + cd examples/posix/wh_posix_server + make clean + make -j WOLFSSL_DIR=../../../wolfssl + + # Build DTLS client + - name: Build DTLS client + run: | + cd examples/posix/tls/wh_posix_dtls_client + make clean + make + + # Start wolfHSM server in background + - name: Start wolfHSM server + run: | + cd examples/posix/wh_posix_server + ./Build/wh_posix_server.elf --type tcp \ + --key ../../../wolfssl/certs/ecc-client-key.der \ + --id 1 \ + --client 12 & + SERVER_PID=$! + echo "WOLFHSM_SERVER_PID=$SERVER_PID" >> $GITHUB_ENV + echo "Started wolfHSM server with PID $SERVER_PID" + sleep 2 + + # Start wolfSSL DTLS server in background + - name: Start wolfSSL DTLS server + run: | + cd wolfssl + ./examples/server/server -u -v 4 \ + -c ./certs/server-ecc.pem \ + -k ./certs/ecc-key.pem \ + -A ./certs/client-ecc-cert.pem \ + -p 11111 \ + -i & + DTLS_SERVER_PID=$! + echo "DTLS_SERVER_PID=$DTLS_SERVER_PID" >> $GITHUB_ENV + echo "Started DTLS server with PID $DTLS_SERVER_PID" + sleep 2 + + # Run DTLS client test + - name: Run DTLS client test + run: | + cd examples/posix/tls/wh_posix_dtls_client + + # Send test message with 5 second timeout + echo "Hello from CI test" | timeout 5 ./Build/wh_posix_dtls_client.elf 127.0.0.1 > client_output.txt 2>&1 || CLIENT_EXIT=$? + + # Display output for debugging + cat client_output.txt + + # Check for successful handshake (exit code 0 = clean exit, 124 = timeout) + if grep -q "DTLS handshake successful!" client_output.txt; then + echo "✓ DTLS client test completed successfully" + exit 0 + else + echo "✗ DTLS handshake did not complete" + exit 1 + fi + + # Cleanup servers (always run) + - name: Cleanup servers + if: always() + run: | + echo "Cleaning up server processes..." + kill ${{ env.WOLFHSM_SERVER_PID }} 2>/dev/null || true + kill ${{ env.DTLS_SERVER_PID }} 2>/dev/null || true + sleep 1 + kill -9 ${{ env.WOLFHSM_SERVER_PID }} 2>/dev/null || true + kill -9 ${{ env.DTLS_SERVER_PID }} 2>/dev/null || true + echo "Cleanup complete" diff --git a/examples/posix/tls/wh_posix_dtls_client/.cache/clangd/index/user_settings.h.70BDD9097831BA2D.idx b/examples/posix/tls/wh_posix_dtls_client/.cache/clangd/index/user_settings.h.70BDD9097831BA2D.idx new file mode 100644 index 0000000000000000000000000000000000000000..505b1c57cc20e8f644af955327474415a0a4c2a0 GIT binary patch literal 690 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 literal 0 HcmV?d00001 diff --git a/examples/posix/tls/wh_posix_dtls_client/.cache/clangd/index/wolfhsm_cfg.h.BE11D43E6AF362BB.idx b/examples/posix/tls/wh_posix_dtls_client/.cache/clangd/index/wolfhsm_cfg.h.BE11D43E6AF362BB.idx new file mode 100644 index 0000000000000000000000000000000000000000..f470b0b2b439ff284d872936ef6d414f98b1a558 GIT binary patch literal 300 zcmWIYbaPW-WMFVk@vO*AElFfyU|Oj`RDE{1areyU8l2}C4yY<0xlpF~;zGAT_CF!_6QKbM#eA8qey{#<)#a}e zr+M}Ex`h@Se?Px=>5XCjg`i`fYZ-R0v)Ef)nVST(f}toit+*IS>%N|}G+_M!W=1YX zSq@nRkQ4|^HQQ7-W2XrVP*{;eQ5qu1z~H-FCaW~uMi?k8$ss8Q6P{nQ^XB=b*$F^l zB@QJ;m~h_uWRb%u4gZ0{3LFY>VW8bPi6Aw_MajkDAO;hNfB;7S4U8QLOFbnSnHT^n Cs8VqN literal 0 HcmV?d00001 diff --git a/examples/posix/tls/wh_posix_dtls_client/Makefile b/examples/posix/tls/wh_posix_dtls_client/Makefile new file mode 100644 index 000000000..c81ab71cf --- /dev/null +++ b/examples/posix/tls/wh_posix_dtls_client/Makefile @@ -0,0 +1,184 @@ +## Makefile for wolfHSM DTLS Client example using POSIX port + +## Project name +# Sets output filenames +BIN = wh_posix_dtls_client + +## Important directories +# Base directory for additional project files +PROJECT_DIR ?= . +CONFIG_DIR ?= $(PROJECT_DIR) +SHARED_CONFIG_DIR ?= $(PROJECT_DIR)/../../ +# wolfSSL and wolfHSM directories +WOLFSSL_DIR ?= ../../../../wolfssl +WOLFHSM_DIR ?= ../../../../ +WOLFHSM_PORT_DIR ?= $(WOLFHSM_DIR)/port/posix + +# Output directory for build files +BUILD_DIR ?= $(PROJECT_DIR)/Build + +# Includes +INC = -I$(PROJECT_DIR) \ + -I$(CONFIG_DIR) \ + -I$(SHARED_CONFIG_DIR) \ + -I$(WOLFSSL_DIR) \ + -I$(WOLFHSM_DIR) \ + -I$(WOLFHSM_PORT_DIR) + +# POSIX requires C source be defined before any header +DEF += -D_POSIX_C_SOURCE=200809L + +# Library configuration defines for user-supplied settings +DEF += -DWOLFSSL_USER_SETTINGS -DWOLFHSM_CFG + +# Architecture flags for assembler, C compiler and linker +ARCHFLAGS ?= + +# Enable extra C compiler warnings +CFLAGS_EXTRA = -Werror -Wall -Wextra +# Place functions / data into separate sections to allow unused code removal +CFLAGS_EXTRA += -ffunction-sections -fdata-sections + +# C standard to use +CSTD ?= -std=c99 + +ASFLAGS ?= $(ARCHFLAGS) +CFLAGS ?= $(ARCHFLAGS) $(CSTD) $(CFLAGS_EXTRA) +LDFLAGS ?= $(ARCHFLAGS) + +# Enable garbage collection. Inexact handling of dead_strip +OS_NAME := $(shell uname -s | tr A-Z a-z) +ifeq ($(OS_NAME),darwin) + LDFLAGS += -Wl,-dead_strip +else + LDFLAGS += -Wl,--gc-sections +endif + +# Libc for printf, libm for math (used with DH) +LIBS = -lc -lm + +## Makefile options + +# Set to @ if you want to suppress command echo +CMD_ECHO ?= + +# Check if DEBUG is set to 1 and append debug flags +ifeq ($(DEBUG),1) + DBGFLAGS = -ggdb -g3 + CFLAGS += $(DBGFLAGS) + LDFLAGS += $(DBGFLAGS) + DEF += -DWOLFHSM_CFG_DEBUG +endif + +# Check if DEBUG_VERBOSE is set to 1 and enable verbose WOLFHSM debug output +# Note: DEBUG_VERBOSE implies DEBUG +ifeq ($(DEBUG_VERBOSE),1) + DBGFLAGS = -ggdb -g3 + CFLAGS += $(DBGFLAGS) + LDFLAGS += $(DBGFLAGS) + DEF += -DWOLFHSM_CFG_DEBUG -DWOLFHSM_CFG_DEBUG_VERBOSE +endif + +# Add address sanitizer option +ifeq ($(ASAN),1) + CFLAGS += -fsanitize=address + LDFLAGS += -fsanitize=address +endif + +## Source files +# Assembly source files +SRC_ASM += + +# wolfCrypt source files +WOLFCRYPT_SRC := $(wildcard $(WOLFSSL_DIR)/wolfcrypt/src/*.c) +SRC_C += $(filter-out %/evp.c %/misc.c,$(WOLFCRYPT_SRC)) + +# wolfSSL source files (needed for DTLS) +WOLFSSL_SRC := $(wildcard $(WOLFSSL_DIR)/src/*.c) +SRC_C += $(filter-out %/bio.c %/conf.c %/pk.c %/ssl_asn1.c %/ssl_bn.c %/ssl_certman.c %/ssl_crypto.c %/ssl_load.c %/ssl_misc.c %/ssl_p7p12.c %/ssl_sess.c %/ssl_sk.c %/x509.c %/x509_str.c,$(WOLFSSL_SRC)) + +# wolfHSM source files +SRC_C += $(wildcard $(WOLFHSM_DIR)/src/*.c) + +# wolfHSM port/HAL code +SRC_C += $(wildcard $(WOLFHSM_PORT_DIR)/*.c) + +# Project source files +SRC_C += $(wildcard $(PROJECT_DIR)/*.c) + +# Set the default device ID for wolfCrypt operations +DEF += -DWC_USE_DEVID=0x5748534D + +ifeq ($(SCAN),1) +SCAN_LOG = scan_posix_dtls_client.log +# Default target +.DEFAULT_GOAL := scan +endif + +## Automated processing below + +FILENAMES_C = $(notdir $(SRC_C)) +OBJS_C = $(addprefix $(BUILD_DIR)/, $(FILENAMES_C:.c=.o)) +vpath %.c $(dir $(SRC_C)) + +OBJS_ASM = $(addprefix $(BUILD_DIR)/, $(notdir $(SRC_ASM:.s=.o))) +vpath %.s $(dir $(SRC_ASM)) + + +## Makefile Targets + +.PHONY: build_app build_hex build_static clean run + +build_app: $(BUILD_DIR) $(BUILD_DIR)/$(BIN).elf + @echo Build complete. + +build_hex: $(BUILD_DIR) $(BUILD_DIR)/$(BIN).hex + @echo "" + $(CMD_ECHO) $(SIZE) $(BUILD_DIR)/$(BIN).elf + +build_static: $(BUILD_DIR) $(BUILD_DIR)/$(BIN).a + @echo "" + $(CMD_ECHO) $(SIZE) $(BUILD_DIR)/$(BIN).a + +$(BUILD_DIR): + $(CMD_ECHO) mkdir -p $(BUILD_DIR) + +$(BUILD_DIR)/$(BIN).hex: $(BUILD_DIR)/$(BIN).elf + @echo "Generating HEX binary: $(notdir $@)" + $(CMD_ECHO) $(OBJCOPY) -O ihex $< $@ + +$(BUILD_DIR)/%.o: %.s + @echo "Compiling ASM file: $(notdir $<)" + $(CMD_ECHO) $(AS) $(ASFLAGS) $(DEF) $(INC) -c -o $@ $< + +$(BUILD_DIR)/%.o: %.c + @echo "Compiling C file: $(notdir $<)" + $(CMD_ECHO) $(CC) $(CFLAGS) $(DEF) $(INC) -c -o $@ $< + +$(BUILD_DIR)/$(BIN).elf: $(OBJS_ASM) $(OBJS_C) + @echo "Linking ELF binary: $(notdir $@)" + $(CMD_ECHO) $(CC) $(LDFLAGS) $(SRC_LD) -o $@ $^ $(LIBS) + +$(BUILD_DIR)/$(BIN).a: $(OBJS_ASM) $(OBJS_C) + @echo "Building static library: $(notdir $@)" + $(CMD_ECHO) $(AR) -r $@ $^ + +analyze: $(OBJS_ASM) $(OBJS_C) + +scan:$(BUILD_DIR) + @echo "Running scan-build static analysis" + @mkdir -p $(WOLFHSM_DIR)/scan_out/ + @scan-build --exclude $(WOLFSSL_DIR)/wolfcrypt \ + --exclude $(WOLFSSL_DIR)/src \ + --status-bugs $(MAKE) analyze 2> $(WOLFHSM_DIR)/scan_out/$(SCAN_LOG) + +clean: + @echo "Cleaning build files" + @rm -f \ + $(BUILD_DIR)/*.elf \ + $(BUILD_DIR)/*.hex \ + $(BUILD_DIR)/*.map \ + $(BUILD_DIR)/*.o \ + $(BUILD_DIR)/*.a \ + $(BUILD_DIR)/*.sym \ + $(BUILD_DIR)/*.disasm diff --git a/examples/posix/tls/wh_posix_dtls_client/user_settings.h b/examples/posix/tls/wh_posix_dtls_client/user_settings.h new file mode 100644 index 000000000..8750aac9e --- /dev/null +++ b/examples/posix/tls/wh_posix_dtls_client/user_settings.h @@ -0,0 +1,52 @@ +#ifndef USER_SETTINGS_H_ +#define USER_SETTINGS_H_ + +/** wolfHSM Client required settings */ + +/* POSIX system headers */ +#define HAVE_SYS_TIME_H + +/* CryptoCB support - required for offloading crypto to HSM */ +#define WOLF_CRYPTO_CB +#define HAVE_ANONYMOUS_INLINE_AGGREGATES 1 + +/* PK callbacks - required for TLS-level HSM key operations */ +#define HAVE_PK_CALLBACKS + +/* Enable DTLS support */ +#define WOLFSSL_DTLS +#define WOLFSSL_DTLS13 +#define WOLFSSL_TLS13 +#define HAVE_TLS_EXTENSIONS +#define WOLFSSL_SEND_HRR_COOKIE + +/* Remove old TLS versions */ +#define NO_OLD_TLS + +/** Crypto Algorithm Options */ + +/* ECC for ECDHE key exchange and ECDSA authentication */ +#define HAVE_ECC +#define HAVE_SUPPORTED_CURVES + +/* AES-GCM for symmetric encryption */ +#define HAVE_AESGCM + +/* HKDF for key derivation */ +#define HAVE_HKDF + +/* Timing resistance / side-channel attack protection */ +#define TFM_TIMING_RESISTANT +#define ECC_TIMING_RESISTANT +#define WC_RSA_BLINDING + +/* Use wolfSSL's internal string comparison instead of system strcasecmp */ +#define USE_WOLF_STRCASECMP + +/* Remove unneeded features */ +#define NO_MAIN_DRIVER +#define NO_DO178 +#define NO_RSA +#define NO_DH + +#endif /* USER_SETTINGS_H_ */ diff --git a/examples/posix/tls/wh_posix_dtls_client/wh_posix_dtls_client.c b/examples/posix/tls/wh_posix_dtls_client/wh_posix_dtls_client.c new file mode 100644 index 000000000..e03e3475a --- /dev/null +++ b/examples/posix/tls/wh_posix_dtls_client/wh_posix_dtls_client.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2024 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM 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. + * + * wolfHSM 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 wolfHSM. If not, see . + */ +/* + * wh_posix_dtls_client.c + * + * wolfHSM DTLS Client Example + * + * This example demonstrates a DTLS client that uses wolfHSM as a cryptographic + * backend. All private key operations (ECDSA signing) and ephemeral key + * generation (ECDHE) are performed on the HSM - private keys never leave the + * secure environment. + * + * The client makes two connections: + * 1. TCP to wolfHSM server - for all cryptographic operations + * 2. UDP to external DTLS server - for secure application communication + * + * Usage: + * 1. Start wolfHSM server with client private key: + * ./wh_posix_server.elf --type tcp \ + * --key --id 1 --client 12 + * + * 2. Start external DTLS server (from wolfssl directory): + * ./examples/server/server -u -v 3 \ + * -c ./certs/server-ecc-cert.pem -k ./certs/ecc-key.pem \ + * -A ./certs/client-ecc-cert.pem -V + * + * 3. Run this DTLS client: + * ./wh_posix_dtls_client.elf 127.0.0.1 + * + * Note: Client certificates are built-in from certs_test.h, eliminating + * the need for a fixed wolfssl directory location. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* wolfSSL headers */ +#include "wolfssl/wolfcrypt/settings.h" +#include "wolfssl/ssl.h" +#include "wolfssl/wolfcrypt/ecc.h" +#include "wolfssl/wolfcrypt/cryptocb.h" + +/* Enable 256-bit ECC certificate buffers */ +#define USE_CERT_BUFFERS_256 +#include "wolfssl/certs_test.h" + +/* wolfHSM headers */ +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_client_crypto.h" +#include "wolfhsm/wh_client_cryptocb.h" + +/* posix headers */ +#include "port/posix/posix_transport_tcp.h" +#include "../../wh_posix_cfg.h" + +/* DTLS server connection parameters */ +#define DTLS_SERVER_PORT 11111 +#define MAX_LINE 4096 + +/* Default HSM key ID for client private key (must match server --id parameter) */ +#define HSM_KEY_ID 1 + +/* Context passed to crypto callback containing wolfHSM client info */ +typedef struct { + whClientContext* whClient; /* wolfHSM client context */ + whKeyId clientKeyId; /* HSM key ID for client private key */ +} DtlsHsmCtx; + +static DtlsHsmCtx hsmCtx; + +static posixTransportTcpClientContext tccTcp; +static posixTransportTcpConfig tcpConfig; +static whCommClientConfig commConfig; +static whTransportClientCb tcpCb = PTT_CLIENT_CB; + +/* wolfHSM client configuration setup for TCP transport */ +static int InitWolfHSMPosixTcpConfig(whClientConfig* conf) +{ + + memset(&tccTcp, 0, sizeof(posixTransportTcpClientContext)); + memset(&commConfig, 0, sizeof(whCommClientConfig)); + + tcpConfig.server_ip_string = WH_POSIX_SERVER_TCP_IPSTRING; + tcpConfig.server_port = WH_POSIX_SERVER_TCP_PORT; + + commConfig.transport_cb = &tcpCb; + commConfig.transport_context = (void*)&tccTcp; + commConfig.transport_config = (void*)&tcpConfig; + commConfig.client_id = WH_POSIX_CLIENT_ID; + conf->comm = &commConfig; + + return WH_ERROR_OK; +} +/** + * ECC Sign callback - called during TLS CertificateVerify + * + * This callback intercepts ECC signing operations during TLS handshake. + * Instead of using a local private key, it delegates signing to the HSM + * where the private key is stored. + * + * @param ssl wolfSSL session pointer (unused) + * @param in Hash to sign + * @param inSz Size of hash + * @param out Output buffer for signature + * @param outSz Input/output signature size + * @param keyDer DER-encoded key (unused - we use HSM key) + * @param keySz Size of DER key (unused) + * @param ctx User context containing HSM client and key ID + * @return 0 on success, negative on error + */ +static int myEccSignCb(WOLFSSL* ssl, const unsigned char* in, unsigned int inSz, + unsigned char* out, word32* outSz, + const unsigned char* keyDer, unsigned int keySz, + void* ctx) +{ + DtlsHsmCtx* hsmCtx = (DtlsHsmCtx*)ctx; + ecc_key eccKey[1]; + int ret; + uint16_t sigLen; + + (void)ssl; + (void)keyDer; + (void)keySz; /* unused - we use HSM key */ + + /* Initialize ECC key structure with HSM device ID */ + ret = wc_ecc_init_ex(eccKey, NULL, WH_DEV_ID); + if (ret != 0) { + printf(" EccSignCb: wc_ecc_init_ex failed: %d\n", ret); + return ret; + } + + /* Set curve parameters (P-256) */ + ret = wc_ecc_set_curve(eccKey, 32, ECC_SECP256R1); + if (ret != 0) { + printf(" EccSignCb: wc_ecc_set_curve failed: %d\n", ret); + wc_ecc_free(eccKey); + return ret; + } + + /* Associate the HSM key ID with this ecc_key structure */ + ret = wh_Client_EccSetKeyId(eccKey, hsmCtx->clientKeyId); + if (ret != 0) { + printf(" EccSignCb: wh_Client_EccSetKeyId failed: %d\n", ret); + wc_ecc_free(eccKey); + return ret; + } + + /* Perform signing on HSM - private key never leaves the HSM */ + sigLen = (uint16_t)*outSz; + ret = wh_Client_EccSign(hsmCtx->whClient, eccKey, in, (uint16_t)inSz, out, + &sigLen); + if (ret == 0) { + *outSz = sigLen; + } + else { + printf(" EccSignCb: wh_Client_EccSign failed: %d\n", ret); + } + + wc_ecc_free(eccKey); + return ret; +} + +static void Usage(const char* progName) +{ + printf("Usage: %s \n", progName); + printf(" DTLS server IP: IP address of the external DTLS server\n"); + printf("\nExample: %s 127.0.0.1\n", progName); +} + +int main(int argc, char** argv) +{ + int ret; + int sockfd = -1; + const char* dtlsServerIp; + whKeyId hsmKeyId = HSM_KEY_ID; + struct sockaddr_in servAddr; + char sendLine[MAX_LINE]; + char recvLine[MAX_LINE]; + int n; + + /* wolfHSM client */ + whClientContext whClient[1]; + whClientConfig whConfig[1]; + + /* wolfSSL/DTLS */ + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + int err; + + /* Parse command line arguments */ + if (argc != 2) { + Usage(argv[0]); + return 1; + } + dtlsServerIp = argv[1]; + + printf("wolfHSM DTLS Client Example\n"); + printf(" HSM Key ID: %u\n", hsmKeyId); + printf(" DTLS Server: %s:%d\n", dtlsServerIp, DTLS_SERVER_PORT); + + /* + * Initialize wolfHSM client (TCP connection to HSM server) + */ + printf("\n[1] Connecting to wolfHSM server...\n"); + memset(whConfig, 0, sizeof(whClientConfig)); + ret = InitWolfHSMPosixTcpConfig(whConfig); + if (ret != 0) { + printf("ERROR: wh_PosixDtlsClient_TcpConfig failed with %d\n", ret); + return 1; + } + + ret = wh_Client_Init(whClient, whConfig); + if (ret != 0) { + printf("ERROR: wh_Client_Init failed with %d\n", ret); + return 1; + } + + ret = wh_Client_CommInit(whClient, NULL, NULL); + if (ret != 0) { + printf("ERROR: wh_Client_CommInit failed with %d\n", ret); + goto cleanup_whclient; + } + printf(" Connected to wolfHSM server\n"); + + /* + * Set up a global context to be used in the callbacks + */ + hsmCtx.whClient = whClient; + hsmCtx.clientKeyId = hsmKeyId; + + /* + * Initialize wolfSSL library + */ + ret = wolfSSL_Init(); + if (ret != WOLFSSL_SUCCESS) { + printf("ERROR: wolfSSL_Init failed\n"); + goto cleanup; + } + + /* + * Register wolfHSM crypto callback This callback will intercepts wolfCrypt + * cryptographic operations and offloads them to HSM. + */ + ret = wc_CryptoCb_RegisterDevice(WH_DEV_ID, wh_Client_CryptoCb, whClient); + if (ret != 0) { + printf("ERROR: wc_CryptoCb_RegisterDevice failed with %d\n", ret); + goto cleanup; + } + printf(" Registered wolfHSM crypto callback (devId=0x%08X)\n", WH_DEV_ID); + + /* + * Create DTLS client context + */ + printf("\n[2] Creating DTLS context...\n"); + ctx = wolfSSL_CTX_new(wolfDTLS_client_method()); + if (ctx == NULL) { + printf("ERROR: wolfSSL_CTX_new failed\n"); + goto cleanup; + } + + /* + * Configure crypto callback device ID + * This tells wolfSSL to use our HSM for crypto operations + */ + ret = wolfSSL_CTX_SetDevId(ctx, WH_DEV_ID); + if (ret != WOLFSSL_SUCCESS) { + printf("ERROR: wolfSSL_CTX_SetDevId failed\n"); + goto cleanup; + } + printf(" Configured HSM device ID for crypto operations\n"); + + /* + * Register PK callbacks for HSM operations + * These callbacks intercept (D)TLS-level ECC operations (signing during + * CertificateVerify + Shared secret generation) and delegate them to the + * HSM. It allows to use the id of a private key stored on the HSM without + * ever exposing the private key itself. + */ + wolfSSL_CTX_SetEccSignCb(ctx, myEccSignCb); + wolfSSL_CTX_SetEccSignCtx(ctx, &hsmCtx); + printf(" Registered ECC PK callbacks for HSM operations\n"); + + /* + * Load CA certificate for verifying DTLS server + */ + ret = wolfSSL_CTX_load_verify_buffer(ctx, ca_ecc_cert_der_256, + sizeof_ca_ecc_cert_der_256, + WOLFSSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS) { + printf("ERROR: Failed to load CA certificate buffer\n"); + goto cleanup; + } + printf(" Loaded CA certificate from buffer\n"); + + /* + * Load client certificate (public part only) + * The private key is NOT loaded here - it stays on the HSM + */ + ret = wolfSSL_CTX_use_certificate_buffer(ctx, cliecc_cert_der_256, + sizeof_cliecc_cert_der_256, + WOLFSSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS) { + printf("ERROR: Failed to load client certificate buffer\n"); + goto cleanup; + } + printf(" Loaded client certificate from buffer\n"); + + /* + * Create UDP socket for DTLS + */ + printf("\n[3] Creating UDP socket...\n"); + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + printf("ERROR: Cannot create socket\n"); + goto cleanup; + } + + /* Set up DTLS server address */ + memset(&servAddr, 0, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_port = htons(DTLS_SERVER_PORT); + if (inet_pton(AF_INET, dtlsServerIp, &servAddr.sin_addr) != 1) { + printf("ERROR: Invalid IP address: %s\n", dtlsServerIp); + goto cleanup; + } + printf(" Created UDP socket\n"); + + /* + * Create DTLS session + */ + printf("\n[4] Creating DTLS session...\n"); + ssl = wolfSSL_new(ctx); + if (ssl == NULL) { + printf("ERROR: wolfSSL_new failed\n"); + goto cleanup; + } + + /* Configure DTLS peer and socket */ + ret = wolfSSL_dtls_set_peer(ssl, &servAddr, sizeof(servAddr)); + if (ret != WOLFSSL_SUCCESS) { + printf("ERROR: wolfSSL_dtls_set_peer failed\n"); + goto cleanup; + } + + ret = wolfSSL_set_fd(ssl, sockfd); + if (ret != WOLFSSL_SUCCESS) { + printf("ERROR: wolfSSL_set_fd failed\n"); + goto cleanup; + } + printf(" DTLS session configured\n"); + + /* + * Step 12: Perform DTLS handshake + */ + printf("\n[5] Performing DTLS handshake...\n"); + ret = wolfSSL_connect(ssl); + if (ret != WOLFSSL_SUCCESS) { + err = wolfSSL_get_error(ssl, ret); + printf("ERROR: wolfSSL_connect failed: %d (%s)\n", + err, wolfSSL_ERR_reason_error_string(err)); + goto cleanup; + } + printf(" DTLS handshake successful!\n"); + printf(" Cipher: %s\n", wolfSSL_get_cipher(ssl)); + + /* + * Application data exchange + */ + printf("\n[6] Ready for application data\n"); + printf("Enter message to send (or empty line to quit): "); + fflush(stdout); + + if (fgets(sendLine, sizeof(sendLine), stdin) != NULL) { + size_t len = strlen(sendLine); + + /* Remove trailing newline if present */ + if (len > 0 && sendLine[len - 1] == '\n') { + sendLine[len - 1] = '\0'; + len--; + } + + if (len > 0) { + /* Send message to server */ + ret = wolfSSL_write(ssl, sendLine, (int)len); + if (ret != (int)len) { + printf("ERROR: wolfSSL_write failed\n"); + goto cleanup; + } + printf(" Sent: %s\n", sendLine); + + /* Receive response from server */ + memset(recvLine, 0, sizeof(recvLine)); + n = wolfSSL_read(ssl, recvLine, sizeof(recvLine) - 1); + if (n < 0) { + err = wolfSSL_get_error(ssl, n); + if (err != WOLFSSL_ERROR_WANT_READ) { + printf("ERROR: wolfSSL_read failed: %d\n", err); + } + } + else { + recvLine[n] = '\0'; + printf(" Received: %s\n", recvLine); + } + } + } + + /* + * Step 14: Clean shutdown + */ + printf("\n[7] Closing connection...\n"); + wolfSSL_shutdown(ssl); + printf(" DTLS connection closed\n"); + +cleanup: + wolfSSL_free(ssl); + if (sockfd >= 0) { + close(sockfd); + } + wolfSSL_CTX_free(ctx); + + wolfSSL_Cleanup(); + +cleanup_whclient: + wh_Client_CommClose(whClient); + wh_Client_Cleanup(whClient); + printf(" wolfHSM client disconnected\n"); + + printf("\nDone.\n"); + return (ret == WOLFSSL_SUCCESS) ? 0 : 1; +} diff --git a/examples/posix/tls/wh_posix_dtls_client/wolfhsm_cfg.h b/examples/posix/tls/wh_posix_dtls_client/wolfhsm_cfg.h new file mode 100644 index 000000000..b1a6fe92e --- /dev/null +++ b/examples/posix/tls/wh_posix_dtls_client/wolfhsm_cfg.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM 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. + * + * wolfHSM 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 wolfHSM. If not, see . + */ +/* + * wolfhsm_cfg.h + * + * wolfHSM compile-time options for DTLS client example + */ + +#ifndef WOLFHSM_CFG_H_ +#define WOLFHSM_CFG_H_ + +#include "port/posix/posix_time.h" + +#define WOLFHSM_CFG_PORT_GETTIME posixGetTime + +/** wolfHSM settings */ +#define WOLFHSM_CFG_ENABLE_CLIENT +#define WOLFHSM_CFG_HEXDUMP +#define WOLFHSM_CFG_COMM_DATA_LEN 5000 + +#endif /* WOLFHSM_CFG_H_ */