From fef27cf910dde4df4b8ac2fc9bb6b8d4867a2eb1 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 29 Oct 2025 17:49:00 +0100 Subject: [PATCH 1/4] Add TLS 1.3 early data examples Introduces client and server examples that demonstrate the use of TLS 1.3 early data (0-RTT) with session resumption. The client example performs an initial handshake to obtain a session ticket, then reconnects and sends early data. The server example receives early data and sends back a reply. --- .gitignore | 2 + tls/client-tls13-earlydata.c | 204 +++++++++++++++++++++++++++++++++++ tls/server-tls13-earlydata.c | 200 ++++++++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 tls/client-tls13-earlydata.c create mode 100644 tls/server-tls13-earlydata.c diff --git a/.gitignore b/.gitignore index c74d2f9c..04d5d6a3 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,8 @@ android/wolfssljni-ndk-sample/proguard-project.txt /tls/client-tls /tls/client-tls13 /tls/client-tls13-resume +/tls/client-tls13-earlydata +/tls/server-tls13-earlydata /tls/client-tls-bio /tls/client-tls-cacb /tls/client-tls-callback diff --git a/tls/client-tls13-earlydata.c b/tls/client-tls13-earlydata.c new file mode 100644 index 00000000..43d00917 --- /dev/null +++ b/tls/client-tls13-earlydata.c @@ -0,0 +1,204 @@ +/* client-tls13-earlydata.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL 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 2 of the License, or + * (at your option) any later version. + * + * wolfSSL 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-1301, USA + */ + +/* Example TLS 1.3 client using wolfSSL early data (0-RTT) with session resumption. + * Performs an initial handshake to obtain a session ticket, then reconnects and + * sends early data using wolfSSL_write_early_data(). + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEFAULT_PORT 11111 +#define CERT_FILE "../certs/client-cert.pem" +#define KEY_FILE "../certs/client-key.pem" +#define CA_FILE "../certs/ca-cert.pem" + +#define EARLY_DATA_MSG "Early data hello from early data client!" +#define EARLY_DATA_MSG_LEN (sizeof(EARLY_DATA_MSG)) + +static int tcp_connect(const char* ip, int port) { + int sockfd; + struct sockaddr_in servAddr; + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket()"); + return -1; + } + + memset(&servAddr, 0, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_port = htons(port); + + if (inet_pton(AF_INET, ip, &servAddr.sin_addr) != 1) { + perror("inet_pton()"); + close(sockfd); + return -1; + } + + if (connect(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0) { + perror("connect()"); + close(sockfd); + return -1; + } + + return sockfd; +} + +int main(int argc, char** argv) +{ + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + const char* server_ip = argv[1]; + int ret = 1; + int sockfd = -1; + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_SESSION* session = NULL; + char recvBuf[256]; + int len; + int earlyDataSent = 0; + + /* Initialize wolfSSL */ + if (wolfSSL_Init() != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_Init failed\n"); + goto cleanup; + } + + /* Create and configure context */ + ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + if (!ctx) { + fprintf(stderr, "wolfSSL_CTX_new failed\n"); + goto cleanup; + } + + if (wolfSSL_CTX_use_certificate_file(ctx, CERT_FILE, WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS || + wolfSSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS || + wolfSSL_CTX_load_verify_locations(ctx, CA_FILE, NULL) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Failed to load cert/key/CA\n"); + goto cleanup; + } + + /* === 1st connection: perform handshake and get session ticket === */ + sockfd = tcp_connect(server_ip, DEFAULT_PORT); + if (sockfd < 0) goto cleanup; + + ssl = wolfSSL_new(ctx); + if (!ssl) { + fprintf(stderr, "wolfSSL_new failed\n"); + goto cleanup; + } + if (wolfSSL_set_fd(ssl, sockfd) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_set_fd failed\n"); + goto cleanup; + } + + if (wolfSSL_connect(ssl) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_connect failed\n"); + goto cleanup; + } + + /* Check if ticket was received */ + if (!wolfSSL_SessionIsSetup(wolfSSL_SSL_get0_session(ssl))) { + /* Attempt to read a session ticket from server */ + (void)wolfSSL_read(ssl, recvBuf, sizeof(recvBuf)-1); + if (!wolfSSL_SessionIsSetup(wolfSSL_SSL_get0_session(ssl))) { + fprintf(stderr, "Session ticket not received from server\n"); + goto cleanup; + } + } + + /* Save session for resumption */ + session = wolfSSL_get1_session(ssl); + if (!session) { + fprintf(stderr, "wolfSSL_get1_session failed\n"); + goto cleanup; + } + + printf("Initial handshake complete, session ticket obtained.\n"); + + /* Clean up first connection */ + wolfSSL_shutdown(ssl); + wolfSSL_free(ssl); + ssl = NULL; + close(sockfd); + sockfd = -1; + + /* === 2nd connection: resume session and send early data === */ + sockfd = tcp_connect(server_ip, DEFAULT_PORT); + if (sockfd < 0) goto cleanup; + + ssl = wolfSSL_new(ctx); + if (!ssl) { + fprintf(stderr, "wolfSSL_new (2nd) failed\n"); + goto cleanup; + } + if (wolfSSL_set_fd(ssl, sockfd) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_set_fd (2nd) failed\n"); + goto cleanup; + } + + if (wolfSSL_set_session(ssl, session) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_set_session failed\n"); + goto cleanup; + } + + ret = wolfSSL_write_early_data(ssl, EARLY_DATA_MSG, EARLY_DATA_MSG_LEN, &earlyDataSent); + if (ret == EARLY_DATA_MSG_LEN && earlyDataSent == EARLY_DATA_MSG_LEN) { + printf("Sent early data: \"%s\"\n", EARLY_DATA_MSG); + } else { + fprintf(stderr, "wolfSSL_write_early_data failed: ret=%d sent=%d\n", ret, earlyDataSent); + goto cleanup; + } + + /* Complete handshake */ + if (wolfSSL_connect(ssl) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_connect (2nd) failed\n"); + goto cleanup; + } + printf("Handshake complete after early data.\n"); + + /* Read server response */ + memset(recvBuf, 0, sizeof(recvBuf)); + while ((len = wolfSSL_read(ssl, recvBuf, sizeof(recvBuf) - 1)) > 0) + printf("Server replied: %s\n", recvBuf); + + ret = 0; /* Success */ + +cleanup: + if (ssl) wolfSSL_free(ssl); + if (session) wolfSSL_SESSION_free(session); + if (ctx) wolfSSL_CTX_free(ctx); + if (sockfd >= 0) close(sockfd); + wolfSSL_Cleanup(); + return ret; +} diff --git a/tls/server-tls13-earlydata.c b/tls/server-tls13-earlydata.c new file mode 100644 index 00000000..072c34bf --- /dev/null +++ b/tls/server-tls13-earlydata.c @@ -0,0 +1,200 @@ +/* server-tls13-earlydata.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL 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 2 of the License, or + * (at your option) any later version. + * + * wolfSSL 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-1301, USA + */ + +/* Example TLS 1.3 server using wolfSSL early data (0-RTT). + * Receives early data from a client using wolfSSL_read_early_data(). + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEFAULT_PORT 11111 +#define CERT_FILE "../certs/server-cert.pem" +#define KEY_FILE "../certs/server-key.pem" +#define CA_FILE "../certs/client-cert.pem" + +#define EARLY_DATA_BUF_SZ 256 + +#define EARLY_DATA_QUICK_REPLY "0.5-RTT data hello from early data server!" +#define EARLY_DATA_REPLY "Normal data hello from early data server!" +#define EARLY_DATA_QUICK_REPLY_LEN (sizeof(EARLY_DATA_QUICK_REPLY) - 1) +#define EARLY_DATA_REPLY_LEN (sizeof(EARLY_DATA_REPLY) - 1) + +static int tcp_listen(int port) { + int sockfd; + struct sockaddr_in servAddr; + int on = 1; + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket()"); + return -1; + } + + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); +#ifdef SO_REUSEPORT + setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on)); +#endif + + memset(&servAddr, 0, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_port = htons(port); + servAddr.sin_addr.s_addr = INADDR_ANY; + + if (bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0) { + perror("bind()"); + close(sockfd); + return -1; + } + + if (listen(sockfd, 5) < 0) { + perror("listen()"); + close(sockfd); + return -1; + } + + return sockfd; +} + +int main(int argc, char** argv) +{ + int ret = 1; + int listenfd = -1, connd = -1; + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + /* Initialize wolfSSL */ + if (wolfSSL_Init() != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_Init failed\n"); + goto cleanup; + } + + /* Create and configure context */ + ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); + if (!ctx) { + fprintf(stderr, "wolfSSL_CTX_new failed\n"); + goto cleanup; + } + + if (wolfSSL_CTX_use_certificate_file(ctx, CERT_FILE, WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS || + wolfSSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS || + wolfSSL_CTX_load_verify_locations(ctx, CA_FILE, NULL) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Failed to load cert/key/CA\n"); + goto cleanup; + } + + if (wolfSSL_CTX_set_max_early_data(ctx, 4096) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Failed to set max early data\n"); + goto cleanup; + } + + /* Listen for connections */ + listenfd = tcp_listen(DEFAULT_PORT); + if (listenfd < 0) goto cleanup; + + printf("Listening on port %d...\n", DEFAULT_PORT); + + while (1) { + struct sockaddr_in clientAddr; + socklen_t size = sizeof(clientAddr); + char earlyDataBuf[EARLY_DATA_BUF_SZ]; + int earlyDataLen = 0; + int n; + + connd = accept(listenfd, (struct sockaddr*)&clientAddr, &size); + if (connd < 0) { + perror("accept()"); + continue; + } + + ssl = wolfSSL_new(ctx); + if (!ssl) { + fprintf(stderr, "wolfSSL_new failed\n"); + close(connd); + continue; + } + if (wolfSSL_set_fd(ssl, connd) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_set_fd failed\n"); + wolfSSL_free(ssl); + close(connd); + continue; + } + + /* wolfSSL_read_early_data reads early data and advances the handshake */ + while ((n = wolfSSL_read_early_data(ssl, earlyDataBuf, + sizeof(earlyDataBuf)-1, &earlyDataLen)) > 0) { + if (earlyDataLen > 0) { + earlyDataBuf[earlyDataLen] = '\0'; + printf("Received early data: \"%s\"\n", earlyDataBuf); + /* Send 0.5-RTT data */ + if (wolfSSL_write(ssl, EARLY_DATA_QUICK_REPLY, EARLY_DATA_QUICK_REPLY_LEN) != EARLY_DATA_QUICK_REPLY_LEN) { + fprintf(stderr, "wolfSSL_write failed\n"); + } else { + printf("Sent reply to client.\n"); + } + } else { + printf("No early data received (n=%d, len=%d)\n", n, earlyDataLen); + } + } + + /* wolfSSL_read_early_data might have completed the handshake */ + if (!wolfSSL_is_init_finished(ssl)) { + /* Complete handshake */ + if (wolfSSL_accept(ssl) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_accept failed\n"); + wolfSSL_free(ssl); + close(connd); + continue; + } + } + printf("Handshake complete.\n"); + + /* Send a reply */ + if (wolfSSL_write(ssl, EARLY_DATA_REPLY, EARLY_DATA_REPLY_LEN) != EARLY_DATA_REPLY_LEN) { + fprintf(stderr, "wolfSSL_write failed\n"); + } else { + printf("Sent reply to client.\n"); + } + + wolfSSL_shutdown(ssl); + wolfSSL_free(ssl); + ssl = NULL; + close(connd); + connd = -1; + } + + ret = 0; + +cleanup: + if (ssl) wolfSSL_free(ssl); + if (connd >= 0) close(connd); + if (ctx) wolfSSL_CTX_free(ctx); + if (listenfd >= 0) close(listenfd); + wolfSSL_Cleanup(); + return ret; +} From c967668ebf31af099c9894c4c4bf985413bdffcd Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 3 Nov 2025 14:59:11 +0100 Subject: [PATCH 2/4] Add DTLS 1.3 early data examples --- .gitignore | 2 + dtls/client-dtls13-earlydata.c | 211 +++++++++++++++++++++++++++++++++ dtls/server-dtls13-earlydata.c | 203 +++++++++++++++++++++++++++++++ tls/client-tls13-earlydata.c | 2 +- 4 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 dtls/client-dtls13-earlydata.c create mode 100644 dtls/server-dtls13-earlydata.c diff --git a/.gitignore b/.gitignore index 04d5d6a3..961fca2c 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ android/wolfssljni-ndk-sample/proguard-project.txt /dtls/client-dtls-shared /dtls/client-dtls /dtls/client-dtls13 +/dtls/client-dtls13-earlydata /dtls/client-udp /dtls/memory-bio-dtls /dtls/server-dtls-callback @@ -64,6 +65,7 @@ android/wolfssljni-ndk-sample/proguard-project.txt /dtls/server-dtls-threaded /dtls/server-dtls /dtls/server-dtls13 +/dtls/server-dtls13-earlydata /dtls/server-dtls13-event /dtls/server-udp diff --git a/dtls/client-dtls13-earlydata.c b/dtls/client-dtls13-earlydata.c new file mode 100644 index 00000000..004b2835 --- /dev/null +++ b/dtls/client-dtls13-earlydata.c @@ -0,0 +1,211 @@ +/* client-dtls13-earlydata.c + * + * Copyright (C) 2006-2020 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL 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 2 of the License, or + * (at your option) any later version. + * + * wolfSSL 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-1301, USA + */ + +/* Example DTLS 1.3 client using wolfSSL early data (0-RTT) with session resumption. + * Performs an initial handshake to obtain a session ticket, then reconnects and + * sends early data using wolfSSL_write_early_data(). + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEFAULT_PORT 11111 +#define CERT_FILE "../certs/client-cert.pem" +#define KEY_FILE "../certs/client-key.pem" +#define CA_FILE "../certs/ca-cert.pem" + +#define EARLY_DATA_MSG "Early data hello from early data DTLS client!" +#define EARLY_DATA_MSG_LEN (sizeof(EARLY_DATA_MSG)) +#define DATA_MSG "Normal data hello from early data DTLS client!" +#define DATA_MSG_LEN (sizeof(DATA_MSG)) + +static int udp_connect(const char* ip, int port, struct sockaddr_in* servAddr) { + int sockfd; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return -1; + } + + memset(servAddr, 0, sizeof(*servAddr)); + servAddr->sin_family = AF_INET; + servAddr->sin_port = htons(port); + + if (inet_pton(AF_INET, ip, &servAddr->sin_addr) != 1) { + perror("inet_pton()"); + close(sockfd); + return -1; + } + + return sockfd; +} + +int main(int argc, char** argv) +{ + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + const char* server_ip = argv[1]; + int ret = 1; + int sockfd = -1; + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_SESSION* session = NULL; + char recvBuf[256]; + int len; + int earlyDataSent = 0; + struct sockaddr_in servAddr; + + /* Initialize wolfSSL */ + if (wolfSSL_Init() != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_Init failed\n"); + goto cleanup; + } + + /* Create and configure context */ + ctx = wolfSSL_CTX_new(wolfDTLSv1_3_client_method()); + if (!ctx) { + fprintf(stderr, "wolfSSL_CTX_new failed\n"); + goto cleanup; + } + + if (wolfSSL_CTX_use_certificate_file(ctx, CERT_FILE, WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS || + wolfSSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS || + wolfSSL_CTX_load_verify_locations(ctx, CA_FILE, NULL) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Failed to load cert/key/CA\n"); + goto cleanup; + } + + /* === 1st connection: perform handshake and get session ticket === */ + sockfd = udp_connect(server_ip, DEFAULT_PORT, &servAddr); + if (sockfd < 0) goto cleanup; + + ssl = wolfSSL_new(ctx); + if (!ssl) { + fprintf(stderr, "wolfSSL_new failed\n"); + goto cleanup; + } + if (wolfSSL_set_fd(ssl, sockfd) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_set_fd failed\n"); + goto cleanup; + } + wolfSSL_dtls_set_peer(ssl, (struct sockaddr*)&servAddr, sizeof(servAddr)); + + if (wolfSSL_connect(ssl) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_connect failed\n"); + goto cleanup; + } + + /* Check if ticket was received */ + if (!wolfSSL_SessionIsSetup(wolfSSL_SSL_get0_session(ssl))) { + (void)wolfSSL_peek(ssl, recvBuf, 0); + if (!wolfSSL_SessionIsSetup(wolfSSL_SSL_get0_session(ssl))) { + fprintf(stderr, "Session ticket not received from server\n"); + goto cleanup; + } + } + + /* Save session for resumption */ + session = wolfSSL_get1_session(ssl); + if (!session) { + fprintf(stderr, "wolfSSL_get1_session failed\n"); + goto cleanup; + } + + printf("Initial handshake complete, session ticket obtained.\n"); + + /* Clean up first connection with full shutdown */ + wolfSSL_shutdown(ssl); + wolfSSL_free(ssl); + ssl = NULL; + close(sockfd); + sockfd = -1; + + /* === 2nd connection: resume session and send early data === */ + sockfd = udp_connect(server_ip, DEFAULT_PORT, &servAddr); + if (sockfd < 0) goto cleanup; + + ssl = wolfSSL_new(ctx); + if (!ssl) { + fprintf(stderr, "wolfSSL_new (2nd) failed\n"); + goto cleanup; + } + if (wolfSSL_set_fd(ssl, sockfd) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_set_fd (2nd) failed\n"); + goto cleanup; + } + wolfSSL_dtls_set_peer(ssl, (struct sockaddr*)&servAddr, sizeof(servAddr)); + + if (wolfSSL_set_session(ssl, session) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_set_session failed\n"); + goto cleanup; + } + + ret = wolfSSL_write_early_data(ssl, EARLY_DATA_MSG, EARLY_DATA_MSG_LEN, &earlyDataSent); + if (ret == EARLY_DATA_MSG_LEN && earlyDataSent == EARLY_DATA_MSG_LEN) { + printf("Sent early data: \"%s\"\n", EARLY_DATA_MSG); + } else { + fprintf(stderr, "wolfSSL_write_early_data failed: ret=%d sent=%d\n", ret, earlyDataSent); + goto cleanup; + } + + /* Complete handshake */ + if (wolfSSL_connect(ssl) != WOLFSSL_SUCCESS) { + if (wolfSSL_get_error(ssl, -1) != APP_DATA_READY) { + fprintf(stderr, "wolfSSL_connect (2nd) failed\n"); + goto cleanup; + } + } + printf("Handshake complete after early data.\n"); + + if (wolfSSL_write(ssl, DATA_MSG, DATA_MSG_LEN) != DATA_MSG_LEN) { + fprintf(stderr, "wolfSSL_write (normal data) failed\n"); + } + else { + printf("Sent normal data: \"%s\"\n", DATA_MSG); + } + + /* Read server response */ + memset(recvBuf, 0, sizeof(recvBuf)); + while ((len = wolfSSL_read(ssl, recvBuf, sizeof(recvBuf) - 1)) > 0) + printf("Server replied: %s\n", recvBuf); + + ret = 0; /* Success */ + +cleanup: + wolfSSL_shutdown(ssl); + if (ssl) wolfSSL_free(ssl); + if (session) wolfSSL_SESSION_free(session); + if (ctx) wolfSSL_CTX_free(ctx); + if (sockfd >= 0) close(sockfd); + wolfSSL_Cleanup(); + return ret; +} diff --git a/dtls/server-dtls13-earlydata.c b/dtls/server-dtls13-earlydata.c new file mode 100644 index 00000000..88733a71 --- /dev/null +++ b/dtls/server-dtls13-earlydata.c @@ -0,0 +1,203 @@ +/* server-dtls13-earlydata.c + * + * Copyright (C) 2006-2020 wolfSSL Inc. + * + * This file is part of wolfSSL. (formerly known as CyaSSL) + * + * wolfSSL 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 2 of the License, or + * (at your option) any later version. + * + * wolfSSL 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-1301, USA + */ + +/* Example DTLS 1.3 server using wolfSSL early data (0-RTT). + * Receives early data from a client using wolfSSL_read_early_data(). + * It is recommended to build wolfSSL with WOLFSSL_DTLS13_NO_HRR_ON_RESUME + * so that the server doesn't send a HelloRetryRequest when resuming sessions. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEFAULT_PORT 11111 +#define CERT_FILE "../certs/server-cert.pem" +#define KEY_FILE "../certs/server-key.pem" +#define CA_FILE "../certs/client-cert.pem" + +#define EARLY_DATA_BUF_SZ 256 + +#define EARLY_DATA_QUICK_REPLY "0.5-RTT data hello from early data DTLS server!" +#define EARLY_DATA_REPLY "Normal data hello from early data DTLS server!" +#define EARLY_DATA_QUICK_REPLY_LEN (sizeof(EARLY_DATA_QUICK_REPLY) - 1) +#define EARLY_DATA_REPLY_LEN (sizeof(EARLY_DATA_REPLY) - 1) + +static int udp_listen(int port) { + int sockfd; + struct sockaddr_in servAddr; + int on = 1; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket()"); + return -1; + } + + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); +#ifdef SO_REUSEPORT + setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on)); +#endif + + memset(&servAddr, 0, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_port = htons(port); + servAddr.sin_addr.s_addr = INADDR_ANY; + + if (bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0) { + perror("bind()"); + close(sockfd); + return -1; + } + + return sockfd; +} + +int main(int argc, char** argv) +{ + int ret = 1; + int listenfd = -1; + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + /* Initialize wolfSSL */ + if (wolfSSL_Init() != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_Init failed\n"); + goto cleanup; + } + + /* Create and configure context */ + ctx = wolfSSL_CTX_new(wolfDTLSv1_3_server_method()); + if (!ctx) { + fprintf(stderr, "wolfSSL_CTX_new failed\n"); + goto cleanup; + } + + if (wolfSSL_CTX_use_certificate_file(ctx, CERT_FILE, WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS || + wolfSSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS || + wolfSSL_CTX_load_verify_locations(ctx, CA_FILE, NULL) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Failed to load cert/key/CA\n"); + goto cleanup; + } + + if (wolfSSL_CTX_set_max_early_data(ctx, 4096) != WOLFSSL_SUCCESS) { + fprintf(stderr, "Failed to set max early data\n"); + goto cleanup; + } + + /* Listen for connections */ + listenfd = udp_listen(DEFAULT_PORT); + if (listenfd < 0) goto cleanup; + + printf("Listening on UDP port %d...\n", DEFAULT_PORT); + + while (1) { + char earlyDataBuf[EARLY_DATA_BUF_SZ]; + int earlyDataLen = 0; + int n; + + ssl = wolfSSL_new(ctx); + if (!ssl) { + fprintf(stderr, "wolfSSL_new failed\n"); + continue; + } + if (wolfSSL_set_fd(ssl, listenfd) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_set_fd failed\n"); + wolfSSL_free(ssl); + continue; + } + if (wolfSSL_dtls13_no_hrr_on_resume(ssl, 1) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_dtls13_no_hrr_on_resume failed\n"); + wolfSSL_free(ssl); + continue; + } + + do { + ret = wolfDTLS_accept_stateless(ssl); + if (ret == WOLFSSL_FATAL_ERROR) { + fprintf(stderr, "wolfDTLS_accept_stateless failed\n"); + goto cleanup; + } + } while (ret != WOLFSSL_SUCCESS); + + printf("Client connected, processing...\n"); + + /* wolfSSL_read_early_data reads early data and advances the handshake */ + while ((n = wolfSSL_read_early_data(ssl, earlyDataBuf, + sizeof(earlyDataBuf)-1, &earlyDataLen)) > 0) { + if (earlyDataLen > 0) { + earlyDataBuf[earlyDataLen] = '\0'; + printf("Received early data: \"%s\"\n", earlyDataBuf); + /* Send 0.5-RTT data */ + if (wolfSSL_write(ssl, EARLY_DATA_QUICK_REPLY, EARLY_DATA_QUICK_REPLY_LEN) != EARLY_DATA_QUICK_REPLY_LEN) { + fprintf(stderr, "wolfSSL_write failed\n"); + } else { + printf("Sent 0.5 data reply to client.\n"); + } + } else { + printf("No early data received (n=%d, len=%d)\n", n, earlyDataLen); + } + } + + /* wolfSSL_read_early_data might have completed the handshake */ + if (!wolfSSL_is_init_finished(ssl)) { + /* Complete handshake */ + if (wolfSSL_accept(ssl) != WOLFSSL_SUCCESS) { + fprintf(stderr, "wolfSSL_accept failed\n"); + wolfSSL_free(ssl); + continue; + } + } + printf("Handshake complete.\n"); + + if ((earlyDataLen = wolfSSL_read(ssl, earlyDataBuf, sizeof(earlyDataBuf)-1)) > 0) { + earlyDataBuf[earlyDataLen] = '\0'; + printf("Received post-handshake data: \"%s\"\n", earlyDataBuf); + } + + /* Send a reply */ + if (wolfSSL_write(ssl, EARLY_DATA_REPLY, EARLY_DATA_REPLY_LEN) != EARLY_DATA_REPLY_LEN) { + fprintf(stderr, "wolfSSL_write failed\n"); + } else { + printf("Sent reply to client.\n"); + } + + /* Attempt a full shutdown */ + wolfSSL_shutdown(ssl); + wolfSSL_free(ssl); + ssl = NULL; + } + + ret = 0; + +cleanup: + if (ssl) wolfSSL_free(ssl); + if (ctx) wolfSSL_CTX_free(ctx); + if (listenfd >= 0) close(listenfd); + wolfSSL_Cleanup(); + return ret; +} diff --git a/tls/client-tls13-earlydata.c b/tls/client-tls13-earlydata.c index 43d00917..f4f63715 100644 --- a/tls/client-tls13-earlydata.c +++ b/tls/client-tls13-earlydata.c @@ -130,7 +130,7 @@ int main(int argc, char** argv) /* Check if ticket was received */ if (!wolfSSL_SessionIsSetup(wolfSSL_SSL_get0_session(ssl))) { /* Attempt to read a session ticket from server */ - (void)wolfSSL_read(ssl, recvBuf, sizeof(recvBuf)-1); + (void)wolfSSL_peek(ssl, recvBuf, 0); if (!wolfSSL_SessionIsSetup(wolfSSL_SSL_get0_session(ssl))) { fprintf(stderr, "Session ticket not received from server\n"); goto cleanup; From 734f9e2ff9ea9cfc98ebe193d877da46b687153c Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 6 Nov 2025 17:58:56 +0100 Subject: [PATCH 3/4] remove extra wolfDTLS_accept_stateless step --- dtls/server-dtls13-earlydata.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dtls/server-dtls13-earlydata.c b/dtls/server-dtls13-earlydata.c index 88733a71..e199cc30 100644 --- a/dtls/server-dtls13-earlydata.c +++ b/dtls/server-dtls13-earlydata.c @@ -136,14 +136,6 @@ int main(int argc, char** argv) continue; } - do { - ret = wolfDTLS_accept_stateless(ssl); - if (ret == WOLFSSL_FATAL_ERROR) { - fprintf(stderr, "wolfDTLS_accept_stateless failed\n"); - goto cleanup; - } - } while (ret != WOLFSSL_SUCCESS); - printf("Client connected, processing...\n"); /* wolfSSL_read_early_data reads early data and advances the handshake */ From dd8f1716097716cc13fc2ff66cc03745af27ead9 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 7 Nov 2025 13:57:51 +0100 Subject: [PATCH 4/4] tls early client: read first 1-RTT data from server --- tls/client-tls13-earlydata.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tls/client-tls13-earlydata.c b/tls/client-tls13-earlydata.c index f4f63715..117e8be8 100644 --- a/tls/client-tls13-earlydata.c +++ b/tls/client-tls13-earlydata.c @@ -143,6 +143,12 @@ int main(int argc, char** argv) fprintf(stderr, "wolfSSL_get1_session failed\n"); goto cleanup; } + + len = wolfSSL_read(ssl, recvBuf, sizeof(recvBuf) - 1); + if (len > 0) { + recvBuf[len] = '\0'; + printf("Server sent: %s\n", recvBuf); + } printf("Initial handshake complete, session ticket obtained.\n");