Add DTLS session export/import examples

Add four new example applications demonstrating the wolfSSL DTLS
session export and import functionality:

- client-dtls-export: DTLS client that exports session after handshake
- client-dtls-import: DTLS client that imports a previously exported session
- server-dtls-export: DTLS server that exports session after handshake
- server-dtls-import: DTLS server that imports a previously exported session

The examples include AES-256-CBC encryption for the exported session
data to protect the sensitive cryptographic keys contained within.

Also adds documentation to README.md explaining the usage and workflow.

Co-Authored-By: Anthony H <anthony@wolfssl.com>
devin/1766079702-dtls-session-export
Devin AI 2025-12-18 17:47:18 +00:00
parent 1b5089b5cf
commit 9b026485d0
6 changed files with 1454 additions and 0 deletions

View File

@ -1688,3 +1688,203 @@ And that's it! The server has been made into a nonblocking server, and the clien
2. The Open Group, “setsockopt - set the socket options”, Copyright © 1997, The Single UNIX ® Specification, Version 2
3. https://en.wikipedia.org/wiki/POSIX_Threads
4. https://www.quora.com/What-exactly-does-it-mean-for-a-web-server-to-be-blocking-versus-non-blocking
## Chapter 6: DTLS Session Export and Import
### 6.1 Overview
The DTLS session export/import feature allows you to serialize a DTLS session's state (including keys, cipher specs, sequence numbers, and peer information) to a buffer, save it to persistent storage, and later restore it to continue communication without performing a new handshake.
This is useful for:
- **Session migration**: Moving a DTLS session between processes or machines
- **Load balancing**: Distributing sessions across multiple servers
- **Persistence**: Saving session state across application restarts
- **Failover**: Restoring sessions after a crash or restart
**Important Security Note**: The exported session data contains sensitive cryptographic keys. These examples encrypt the session data with AES-256-CBC before saving to disk. In production, use a secure key management system instead of hard-coded keys.
### 6.2 Building wolfSSL with Session Export Support
To use these examples, wolfSSL must be compiled with DTLS and session export support:
```bash
cd wolfssl
./autogen.sh
./configure --enable-dtls --enable-sessionexport
make
sudo make install
sudo ldconfig
```
### 6.3 Building the Examples
```bash
cd wolfssl-examples/dtls
make client-dtls-export client-dtls-import server-dtls-export server-dtls-import
```
### 6.4 Example Applications
#### 6.4.1 client-dtls-export
A DTLS client that performs a handshake with a server and exports its session state to an encrypted file.
**Usage:**
```bash
./client-dtls-export <server_ip> [session_file]
```
**Parameters:**
- `server_ip`: IP address of the DTLS server
- `session_file`: Optional filename to save the session (default: `dtls_session.bin`)
**Example:**
```bash
# Start the standard DTLS server first
./server-dtls
# In another terminal, run the export client
./client-dtls-export 127.0.0.1
```
#### 6.4.2 client-dtls-import
A DTLS client that imports a previously exported session and resumes communication without a new handshake.
**Usage:**
```bash
./client-dtls-import <server_ip> [session_file]
```
**Parameters:**
- `server_ip`: IP address of the DTLS server
- `session_file`: Optional filename to load the session from (default: `dtls_session.bin`)
**Example:**
```bash
# After running client-dtls-export, use the import client
./client-dtls-import 127.0.0.1
```
#### 6.4.3 server-dtls-export
A DTLS server that accepts a client connection, performs a handshake, and exports its session state to an encrypted file.
**Usage:**
```bash
./server-dtls-export [session_file]
```
**Parameters:**
- `session_file`: Optional filename to save the session (default: `dtls_server_session.bin`)
**Example:**
```bash
./server-dtls-export
# Then connect with a standard DTLS client
./client-dtls 127.0.0.1
```
#### 6.4.4 server-dtls-import
A DTLS server that imports a previously exported session and resumes communication without a new handshake.
**Usage:**
```bash
./server-dtls-import [session_file]
```
**Parameters:**
- `session_file`: Optional filename to load the session from (default: `dtls_server_session.bin`)
**Example:**
```bash
# After running server-dtls-export, use the import server
./server-dtls-import
```
### 6.5 Complete Workflow Example
Here's a complete example demonstrating session export and import:
**Terminal 1 - Start the export server:**
```bash
./server-dtls-export
# Wait for client connection...
```
**Terminal 2 - Connect with export client:**
```bash
./client-dtls-export 127.0.0.1
# Type a message and press Enter
Hello from client!
# Session is exported to dtls_session.bin
```
**Terminal 1 - Server exports session:**
```
# Server receives message and exports to dtls_server_session.bin
```
**Now both client and server have exported their sessions. To resume:**
**Terminal 1 - Start the import server:**
```bash
./server-dtls-import
# Session loaded, waiting for client...
```
**Terminal 2 - Connect with import client:**
```bash
./client-dtls-import 127.0.0.1
# Session loaded, no handshake needed!
# Type messages to communicate
```
### 6.6 Session Data Encryption
The examples use AES-256-CBC to encrypt the session data before saving to disk. The file format is:
```
[4 bytes: original data length (big-endian)]
[16 bytes: random IV]
[N bytes: AES-CBC encrypted session data with PKCS#7 padding]
```
**Warning**: The examples use a hard-coded AES key for demonstration purposes. In production:
- Use a secure key management system (KMS)
- Derive keys from a secure source
- Consider using authenticated encryption (AES-GCM)
- Implement proper key rotation
### 6.7 API Reference
The key wolfSSL functions used in these examples:
```c
/* Export a DTLS session to a buffer */
int wolfSSL_dtls_export(WOLFSSL* ssl, unsigned char* buf, unsigned int* sz);
/* Import a DTLS session from a buffer */
int wolfSSL_dtls_import(WOLFSSL* ssl, const unsigned char* buf, unsigned int sz);
/* Export only the session state (sequence numbers, epoch, etc.) */
int wolfSSL_dtls_export_state_only(WOLFSSL* ssl, unsigned char* buf, unsigned int* sz);
```
For `wolfSSL_dtls_export`:
- Call with `buf=NULL` to get the required buffer size in `*sz`
- Call again with an allocated buffer to perform the export
- Returns the number of bytes written on success, or negative on error
For `wolfSSL_dtls_import`:
- Pass the buffer containing the exported session data
- Returns the number of bytes read on success, or negative on error
### 6.8 Software Bill of Materials (SBOM)
| Component | Version | License | Description |
|-----------|---------|---------|-------------|
| wolfSSL | 5.x+ | GPLv2 or Commercial | TLS/DTLS library with session export support |
| wolfCrypt | 5.x+ | GPLv2 or Commercial | Cryptographic library (AES, RNG) |

View File

@ -0,0 +1,211 @@
/* client-dtls-export.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
*
*=============================================================================
*
* DTLS client that exports its session after handshake.
* The exported session is encrypted with AES and saved to a file.
* This session can later be imported by client-dtls-import to resume
* communication without a new handshake.
*
* Requires wolfSSL compiled with:
* ./configure --enable-dtls --enable-sessionexport
*/
#include "dtls-export-common.h"
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define MAXLINE 4096
#define SERV_PORT 11111
static void Usage(const char* progName)
{
printf("Usage: %s <server IP> [session_file]\n", progName);
printf(" server IP - IP address of the DTLS server\n");
printf(" session_file - Optional: file to save session (default: %s)\n",
DEFAULT_SESSION_FILE);
}
int main(int argc, char** argv)
{
int sockfd = 0;
int ret;
struct sockaddr_in servAddr;
WOLFSSL* ssl = NULL;
WOLFSSL_CTX* ctx = NULL;
char certs[] = "../certs/ca-cert.pem";
char sendLine[MAXLINE];
char recvLine[MAXLINE - 1];
const char* sessionFile = DEFAULT_SESSION_FILE;
unsigned char* sessionBuf = NULL;
unsigned int sessionSz = 0;
int n;
/* Program argument checking */
if (argc < 2 || argc > 3) {
Usage(argv[0]);
return 1;
}
if (argc == 3) {
sessionFile = argv[2];
}
/* Initialize wolfSSL */
wolfSSL_Init();
/* Create DTLS 1.2 context */
ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method());
if (ctx == NULL) {
fprintf(stderr, "Error: wolfSSL_CTX_new failed\n");
ret = 1;
goto cleanup;
}
/* Load CA certificates */
ret = wolfSSL_CTX_load_verify_locations(ctx, certs, NULL);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error: Failed to load CA cert %s\n", certs);
ret = 1;
goto cleanup;
}
/* Create SSL object */
ssl = wolfSSL_new(ctx);
if (ssl == NULL) {
fprintf(stderr, "Error: wolfSSL_new failed\n");
ret = 1;
goto cleanup;
}
/* Setup server address */
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servAddr.sin_addr) != 1) {
fprintf(stderr, "Error: Invalid IP address: %s\n", argv[1]);
ret = 1;
goto cleanup;
}
/* Set DTLS peer */
wolfSSL_dtls_set_peer(ssl, &servAddr, sizeof(servAddr));
/* Create UDP socket */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
fprintf(stderr, "Error: Cannot create socket\n");
ret = 1;
goto cleanup;
}
/* Set the socket file descriptor */
wolfSSL_set_fd(ssl, sockfd);
/* Perform DTLS handshake */
printf("Connecting to server %s:%d...\n", argv[1], SERV_PORT);
ret = wolfSSL_connect(ssl);
if (ret != SSL_SUCCESS) {
int err = wolfSSL_get_error(ssl, ret);
fprintf(stderr, "Error: wolfSSL_connect failed: %d (%s)\n",
err, wolfSSL_ERR_reason_error_string(err));
ret = 1;
goto cleanup;
}
printf("DTLS handshake successful!\n");
/* Export the session */
printf("Exporting DTLS session...\n");
/* First call to get required buffer size */
ret = wolfSSL_dtls_export(ssl, NULL, &sessionSz);
if (ret != 0 && sessionSz == 0) {
fprintf(stderr, "Error: wolfSSL_dtls_export (get size) failed: %d\n", ret);
fprintf(stderr, "Make sure wolfSSL was compiled with --enable-sessionexport\n");
ret = 1;
goto cleanup;
}
/* Allocate buffer for session data */
sessionBuf = (unsigned char*)malloc(sessionSz);
if (sessionBuf == NULL) {
fprintf(stderr, "Error: Memory allocation failed\n");
ret = 1;
goto cleanup;
}
/* Export the session */
ret = wolfSSL_dtls_export(ssl, sessionBuf, &sessionSz);
if (ret <= 0) {
fprintf(stderr, "Error: wolfSSL_dtls_export failed: %d\n", ret);
ret = 1;
goto cleanup;
}
printf("Session exported: %d bytes\n", ret);
sessionSz = ret;
/* Save encrypted session to file */
ret = SaveEncryptedSession(sessionFile, sessionBuf, sessionSz);
if (ret != 0) {
fprintf(stderr, "Error: Failed to save encrypted session\n");
ret = 1;
goto cleanup;
}
/* Send a test message */
printf("\nEnter message to send (or press Enter to skip): ");
if (fgets(sendLine, MAXLINE, stdin) != NULL && sendLine[0] != '\n') {
ret = wolfSSL_write(ssl, sendLine, (int)strlen(sendLine));
if (ret != (int)strlen(sendLine)) {
fprintf(stderr, "Error: wolfSSL_write failed\n");
}
else {
printf("Sent: %s", sendLine);
/* Read response */
n = wolfSSL_read(ssl, recvLine, sizeof(recvLine) - 1);
if (n > 0) {
recvLine[n] = '\0';
printf("Received: %s", recvLine);
}
}
}
printf("\nSession exported and saved to: %s\n", sessionFile);
printf("You can now use client-dtls-import to resume this session.\n");
ret = 0;
cleanup:
if (sessionBuf != NULL) free(sessionBuf);
if (ssl != NULL) {
wolfSSL_shutdown(ssl);
wolfSSL_free(ssl);
}
if (sockfd > 0) close(sockfd);
if (ctx != NULL) wolfSSL_CTX_free(ctx);
wolfSSL_Cleanup();
return ret;
}

View File

@ -0,0 +1,197 @@
/* client-dtls-import.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
*
*=============================================================================
*
* DTLS client that imports a previously exported session.
* The session is loaded from an encrypted file and decrypted with AES.
* This allows resuming communication without a new handshake.
*
* Requires wolfSSL compiled with:
* ./configure --enable-dtls --enable-sessionexport
*/
#include "dtls-export-common.h"
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define MAXLINE 4096
#define SERV_PORT 11111
static void Usage(const char* progName)
{
printf("Usage: %s <server IP> [session_file]\n", progName);
printf(" server IP - IP address of the DTLS server\n");
printf(" session_file - Optional: file to load session from (default: %s)\n",
DEFAULT_SESSION_FILE);
}
int main(int argc, char** argv)
{
int sockfd = 0;
int ret;
struct sockaddr_in servAddr;
WOLFSSL* ssl = NULL;
WOLFSSL_CTX* ctx = NULL;
char certs[] = "../certs/ca-cert.pem";
char sendLine[MAXLINE];
char recvLine[MAXLINE - 1];
const char* sessionFile = DEFAULT_SESSION_FILE;
unsigned char* sessionBuf = NULL;
unsigned int sessionSz = 0;
int n;
/* Program argument checking */
if (argc < 2 || argc > 3) {
Usage(argv[0]);
return 1;
}
if (argc == 3) {
sessionFile = argv[2];
}
/* Initialize wolfSSL */
wolfSSL_Init();
/* Create DTLS 1.2 context */
ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method());
if (ctx == NULL) {
fprintf(stderr, "Error: wolfSSL_CTX_new failed\n");
ret = 1;
goto cleanup;
}
/* Load CA certificates */
ret = wolfSSL_CTX_load_verify_locations(ctx, certs, NULL);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error: Failed to load CA cert %s\n", certs);
ret = 1;
goto cleanup;
}
/* Create SSL object */
ssl = wolfSSL_new(ctx);
if (ssl == NULL) {
fprintf(stderr, "Error: wolfSSL_new failed\n");
ret = 1;
goto cleanup;
}
/* Load encrypted session from file */
printf("Loading session from %s...\n", sessionFile);
sessionBuf = LoadEncryptedSession(sessionFile, &sessionSz);
if (sessionBuf == NULL) {
fprintf(stderr, "Error: Failed to load session from file\n");
ret = 1;
goto cleanup;
}
/* Import the session */
printf("Importing DTLS session (%u bytes)...\n", sessionSz);
ret = wolfSSL_dtls_import(ssl, sessionBuf, sessionSz);
if (ret < 0) {
fprintf(stderr, "Error: wolfSSL_dtls_import failed: %d\n", ret);
fprintf(stderr, "Make sure wolfSSL was compiled with --enable-sessionexport\n");
ret = 1;
goto cleanup;
}
printf("Session imported successfully!\n");
/* Setup server address */
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(SERV_PORT);
if (inet_pton(AF_INET, argv[1], &servAddr.sin_addr) != 1) {
fprintf(stderr, "Error: Invalid IP address: %s\n", argv[1]);
ret = 1;
goto cleanup;
}
/* Set DTLS peer */
wolfSSL_dtls_set_peer(ssl, &servAddr, sizeof(servAddr));
/* Create UDP socket */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
fprintf(stderr, "Error: Cannot create socket\n");
ret = 1;
goto cleanup;
}
/* Set the socket file descriptor */
wolfSSL_set_fd(ssl, sockfd);
printf("Session restored - no handshake needed!\n");
printf("Ready to communicate with server %s:%d\n\n", argv[1], SERV_PORT);
/* Send messages using the imported session */
printf("Enter message to send (or 'quit' to exit):\n");
while (fgets(sendLine, MAXLINE, stdin) != NULL) {
if (strncmp(sendLine, "quit", 4) == 0) {
break;
}
/* Send message */
ret = wolfSSL_write(ssl, sendLine, (int)strlen(sendLine));
if (ret != (int)strlen(sendLine)) {
int err = wolfSSL_get_error(ssl, ret);
fprintf(stderr, "Error: wolfSSL_write failed: %d (%s)\n",
err, wolfSSL_ERR_reason_error_string(err));
break;
}
printf("Sent: %s", sendLine);
/* Read response */
n = wolfSSL_read(ssl, recvLine, sizeof(recvLine) - 1);
if (n > 0) {
recvLine[n] = '\0';
printf("Received: %s", recvLine);
}
else {
int err = wolfSSL_get_error(ssl, n);
if (err != SSL_ERROR_WANT_READ) {
fprintf(stderr, "Error: wolfSSL_read failed: %d (%s)\n",
err, wolfSSL_ERR_reason_error_string(err));
break;
}
}
printf("\nEnter message (or 'quit' to exit):\n");
}
ret = 0;
cleanup:
if (sessionBuf != NULL) free(sessionBuf);
if (ssl != NULL) {
wolfSSL_shutdown(ssl);
wolfSSL_free(ssl);
}
if (sockfd > 0) close(sockfd);
if (ctx != NULL) wolfSSL_CTX_free(ctx);
wolfSSL_Cleanup();
return ret;
}

View File

@ -0,0 +1,302 @@
/* dtls-export-common.h
*
* 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
*
*=============================================================================
*
* Common header for DTLS session export/import examples.
* Provides AES encryption/decryption for session data protection.
*/
#ifndef DTLS_EXPORT_COMMON_H
#define DTLS_EXPORT_COMMON_H
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include <wolfssl/wolfcrypt/aes.h>
#include <wolfssl/wolfcrypt/random.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Default file for session export/import */
#define DEFAULT_SESSION_FILE "dtls_session.bin"
/* AES-256-CBC key (32 bytes) - In production, use a secure key management system */
static const unsigned char AES_KEY[32] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20
};
/* AES block size */
#define AES_BLOCK_SZ 16
/* File format:
* [4 bytes: original data length]
* [16 bytes: IV]
* [N bytes: encrypted data (padded to AES_BLOCK_SZ)]
*/
/*
* Encrypt session data and save to file
* Returns 0 on success, negative on error
*/
static int SaveEncryptedSession(const char* filename,
const unsigned char* sessionData,
unsigned int sessionSz)
{
FILE* fp = NULL;
Aes aes;
WC_RNG rng;
unsigned char iv[AES_BLOCK_SZ];
unsigned char* paddedData = NULL;
unsigned char* encryptedData = NULL;
unsigned int paddedSz;
unsigned int padding;
int ret = 0;
int aesInit = 0;
int rngInit = 0;
/* Calculate padded size (must be multiple of AES_BLOCK_SZ) */
padding = AES_BLOCK_SZ - (sessionSz % AES_BLOCK_SZ);
paddedSz = sessionSz + padding;
/* Initialize RNG for IV generation */
ret = wc_InitRng(&rng);
if (ret != 0) {
printf("Error: wc_InitRng failed: %d\n", ret);
return -1;
}
rngInit = 1;
/* Generate random IV */
ret = wc_RNG_GenerateBlock(&rng, iv, AES_BLOCK_SZ);
if (ret != 0) {
printf("Error: Failed to generate IV: %d\n", ret);
goto cleanup;
}
/* Allocate buffers */
paddedData = (unsigned char*)malloc(paddedSz);
encryptedData = (unsigned char*)malloc(paddedSz);
if (paddedData == NULL || encryptedData == NULL) {
printf("Error: Memory allocation failed\n");
ret = -1;
goto cleanup;
}
/* Copy data and add PKCS#7 padding */
memcpy(paddedData, sessionData, sessionSz);
memset(paddedData + sessionSz, (int)padding, padding);
/* Initialize AES */
ret = wc_AesInit(&aes, NULL, INVALID_DEVID);
if (ret != 0) {
printf("Error: wc_AesInit failed: %d\n", ret);
goto cleanup;
}
aesInit = 1;
/* Set AES key for encryption */
ret = wc_AesSetKey(&aes, AES_KEY, sizeof(AES_KEY), iv, AES_ENCRYPTION);
if (ret != 0) {
printf("Error: wc_AesSetKey failed: %d\n", ret);
goto cleanup;
}
/* Encrypt the data */
ret = wc_AesCbcEncrypt(&aes, encryptedData, paddedData, paddedSz);
if (ret != 0) {
printf("Error: wc_AesCbcEncrypt failed: %d\n", ret);
goto cleanup;
}
/* Write to file */
fp = fopen(filename, "wb");
if (fp == NULL) {
printf("Error: Cannot open file %s for writing\n", filename);
ret = -1;
goto cleanup;
}
/* Write original size (4 bytes, big-endian) */
{
unsigned char sizeBuf[4];
sizeBuf[0] = (sessionSz >> 24) & 0xFF;
sizeBuf[1] = (sessionSz >> 16) & 0xFF;
sizeBuf[2] = (sessionSz >> 8) & 0xFF;
sizeBuf[3] = sessionSz & 0xFF;
if (fwrite(sizeBuf, 1, 4, fp) != 4) {
printf("Error: Failed to write size to file\n");
ret = -1;
goto cleanup;
}
}
/* Write IV */
if (fwrite(iv, 1, AES_BLOCK_SZ, fp) != AES_BLOCK_SZ) {
printf("Error: Failed to write IV to file\n");
ret = -1;
goto cleanup;
}
/* Write encrypted data */
if (fwrite(encryptedData, 1, paddedSz, fp) != paddedSz) {
printf("Error: Failed to write encrypted data to file\n");
ret = -1;
goto cleanup;
}
printf("Session saved to %s (%u bytes encrypted)\n", filename, paddedSz);
ret = 0;
cleanup:
if (fp != NULL) fclose(fp);
if (paddedData != NULL) free(paddedData);
if (encryptedData != NULL) free(encryptedData);
if (aesInit) wc_AesFree(&aes);
if (rngInit) wc_FreeRng(&rng);
return ret;
}
/*
* Load and decrypt session data from file
* Returns allocated buffer with session data (caller must free), or NULL on error
* sessionSz is set to the size of the returned data
*/
static unsigned char* LoadEncryptedSession(const char* filename,
unsigned int* sessionSz)
{
FILE* fp = NULL;
Aes aes;
unsigned char iv[AES_BLOCK_SZ];
unsigned char sizeBuf[4];
unsigned char* encryptedData = NULL;
unsigned char* decryptedData = NULL;
unsigned int originalSz;
unsigned int encryptedSz;
long fileSize;
int ret = 0;
int aesInit = 0;
/* Open file */
fp = fopen(filename, "rb");
if (fp == NULL) {
printf("Error: Cannot open file %s for reading\n", filename);
return NULL;
}
/* Get file size */
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (fileSize < (4 + AES_BLOCK_SZ + AES_BLOCK_SZ)) {
printf("Error: File too small to contain valid session data\n");
fclose(fp);
return NULL;
}
/* Read original size */
if (fread(sizeBuf, 1, 4, fp) != 4) {
printf("Error: Failed to read size from file\n");
fclose(fp);
return NULL;
}
originalSz = ((unsigned int)sizeBuf[0] << 24) |
((unsigned int)sizeBuf[1] << 16) |
((unsigned int)sizeBuf[2] << 8) |
(unsigned int)sizeBuf[3];
/* Read IV */
if (fread(iv, 1, AES_BLOCK_SZ, fp) != AES_BLOCK_SZ) {
printf("Error: Failed to read IV from file\n");
fclose(fp);
return NULL;
}
/* Calculate encrypted data size */
encryptedSz = (unsigned int)(fileSize - 4 - AES_BLOCK_SZ);
/* Allocate buffers */
encryptedData = (unsigned char*)malloc(encryptedSz);
decryptedData = (unsigned char*)malloc(encryptedSz);
if (encryptedData == NULL || decryptedData == NULL) {
printf("Error: Memory allocation failed\n");
goto cleanup;
}
/* Read encrypted data */
if (fread(encryptedData, 1, encryptedSz, fp) != encryptedSz) {
printf("Error: Failed to read encrypted data from file\n");
goto cleanup;
}
fclose(fp);
fp = NULL;
/* Initialize AES */
ret = wc_AesInit(&aes, NULL, INVALID_DEVID);
if (ret != 0) {
printf("Error: wc_AesInit failed: %d\n", ret);
goto cleanup;
}
aesInit = 1;
/* Set AES key for decryption */
ret = wc_AesSetKey(&aes, AES_KEY, sizeof(AES_KEY), iv, AES_DECRYPTION);
if (ret != 0) {
printf("Error: wc_AesSetKey failed: %d\n", ret);
goto cleanup;
}
/* Decrypt the data */
ret = wc_AesCbcDecrypt(&aes, decryptedData, encryptedData, encryptedSz);
if (ret != 0) {
printf("Error: wc_AesCbcDecrypt failed: %d\n", ret);
goto cleanup;
}
/* Verify padding and original size */
if (originalSz > encryptedSz) {
printf("Error: Invalid original size in file\n");
goto cleanup;
}
*sessionSz = originalSz;
printf("Session loaded from %s (%u bytes)\n", filename, originalSz);
/* Clean up and return decrypted data */
if (encryptedData != NULL) free(encryptedData);
if (aesInit) wc_AesFree(&aes);
return decryptedData;
cleanup:
if (fp != NULL) fclose(fp);
if (encryptedData != NULL) free(encryptedData);
if (decryptedData != NULL) free(decryptedData);
if (aesInit) wc_AesFree(&aes);
return NULL;
}
#endif /* DTLS_EXPORT_COMMON_H */

View File

@ -0,0 +1,284 @@
/* server-dtls-export.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
*
*=============================================================================
*
* DTLS server that exports its session after handshake.
* The exported session is encrypted with AES and saved to a file.
* This session can later be imported by server-dtls-import to resume
* communication without a new handshake.
*
* Requires wolfSSL compiled with:
* ./configure --enable-dtls --enable-sessionexport
*/
#include "dtls-export-common.h"
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#define SERV_PORT 11111
#define MSGLEN 4096
#define DEFAULT_SERVER_SESSION_FILE "dtls_server_session.bin"
static int cleanup = 0;
static void sig_handler(int sig)
{
(void)sig;
cleanup = 1;
}
static void Usage(const char* progName)
{
printf("Usage: %s [session_file]\n", progName);
printf(" session_file - Optional: file to save session (default: %s)\n",
DEFAULT_SERVER_SESSION_FILE);
}
int main(int argc, char** argv)
{
char caCertLoc[] = "../certs/ca-cert.pem";
char servCertLoc[] = "../certs/server-cert.pem";
char servKeyLoc[] = "../certs/server-key.pem";
int ret = 0;
int on = 1;
int listenfd = 0;
int recvLen;
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
struct sockaddr_in servAddr;
struct sockaddr_in cliAddr;
socklen_t cliLen;
socklen_t len = sizeof(int);
unsigned char buf[MSGLEN];
char ack[] = "Message received!\n";
const char* sessionFile = DEFAULT_SERVER_SESSION_FILE;
unsigned char* sessionBuf = NULL;
unsigned int sessionSz = 0;
/* Program argument checking */
if (argc > 2) {
Usage(argv[0]);
return 1;
}
if (argc == 2) {
sessionFile = argv[1];
}
/* Set up signal handler for clean shutdown */
signal(SIGINT, sig_handler);
/* Initialize wolfSSL */
wolfSSL_Init();
/* Create DTLS 1.2 context */
ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method());
if (ctx == NULL) {
fprintf(stderr, "Error: wolfSSL_CTX_new failed\n");
ret = 1;
goto cleanup;
}
/* Load CA certificates */
ret = wolfSSL_CTX_load_verify_locations(ctx, caCertLoc, NULL);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error: Failed to load CA cert %s\n", caCertLoc);
ret = 1;
goto cleanup;
}
/* Load server certificate */
ret = wolfSSL_CTX_use_certificate_file(ctx, servCertLoc, SSL_FILETYPE_PEM);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error: Failed to load server cert %s\n", servCertLoc);
ret = 1;
goto cleanup;
}
/* Load server private key */
ret = wolfSSL_CTX_use_PrivateKey_file(ctx, servKeyLoc, SSL_FILETYPE_PEM);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error: Failed to load server key %s\n", servKeyLoc);
ret = 1;
goto cleanup;
}
/* Create UDP socket */
listenfd = socket(AF_INET, SOCK_DGRAM, 0);
if (listenfd < 0) {
fprintf(stderr, "Error: Cannot create socket\n");
ret = 1;
goto cleanup;
}
/* Set socket options */
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, len);
if (ret < 0) {
fprintf(stderr, "Error: setsockopt SO_REUSEADDR failed\n");
ret = 1;
goto cleanup;
}
/* Setup server address */
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(SERV_PORT);
/* Bind socket */
ret = bind(listenfd, (struct sockaddr*)&servAddr, sizeof(servAddr));
if (ret < 0) {
fprintf(stderr, "Error: bind failed\n");
ret = 1;
goto cleanup;
}
printf("DTLS server listening on port %d\n", SERV_PORT);
printf("Waiting for client connection...\n");
/* Wait for client connection */
cliLen = sizeof(cliAddr);
recvLen = (int)recvfrom(listenfd, buf, sizeof(buf), MSG_PEEK,
(struct sockaddr*)&cliAddr, &cliLen);
if (recvLen < 0) {
fprintf(stderr, "Error: recvfrom failed\n");
ret = 1;
goto cleanup;
}
/* Connect UDP socket to client */
ret = connect(listenfd, (struct sockaddr*)&cliAddr, cliLen);
if (ret != 0) {
fprintf(stderr, "Error: UDP connect failed\n");
ret = 1;
goto cleanup;
}
printf("Client connected from %s:%d\n",
inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));
/* Create SSL object */
ssl = wolfSSL_new(ctx);
if (ssl == NULL) {
fprintf(stderr, "Error: wolfSSL_new failed\n");
ret = 1;
goto cleanup;
}
/* Set DTLS peer */
wolfSSL_dtls_set_peer(ssl, &cliAddr, cliLen);
/* Set socket file descriptor */
wolfSSL_set_fd(ssl, listenfd);
/* Perform DTLS handshake */
printf("Performing DTLS handshake...\n");
ret = wolfSSL_accept(ssl);
if (ret != SSL_SUCCESS) {
int err = wolfSSL_get_error(ssl, ret);
fprintf(stderr, "Error: wolfSSL_accept failed: %d (%s)\n",
err, wolfSSL_ERR_reason_error_string(err));
ret = 1;
goto cleanup;
}
printf("DTLS handshake successful!\n");
/* Export the session */
printf("Exporting DTLS session...\n");
/* First call to get required buffer size */
ret = wolfSSL_dtls_export(ssl, NULL, &sessionSz);
if (ret != 0 && sessionSz == 0) {
fprintf(stderr, "Error: wolfSSL_dtls_export (get size) failed: %d\n", ret);
fprintf(stderr, "Make sure wolfSSL was compiled with --enable-sessionexport\n");
ret = 1;
goto cleanup;
}
/* Allocate buffer for session data */
sessionBuf = (unsigned char*)malloc(sessionSz);
if (sessionBuf == NULL) {
fprintf(stderr, "Error: Memory allocation failed\n");
ret = 1;
goto cleanup;
}
/* Export the session */
ret = wolfSSL_dtls_export(ssl, sessionBuf, &sessionSz);
if (ret <= 0) {
fprintf(stderr, "Error: wolfSSL_dtls_export failed: %d\n", ret);
ret = 1;
goto cleanup;
}
printf("Session exported: %d bytes\n", ret);
sessionSz = ret;
/* Save encrypted session to file */
ret = SaveEncryptedSession(sessionFile, sessionBuf, sessionSz);
if (ret != 0) {
fprintf(stderr, "Error: Failed to save encrypted session\n");
ret = 1;
goto cleanup;
}
/* Handle one message exchange */
printf("\nWaiting for message from client...\n");
recvLen = wolfSSL_read(ssl, buf, sizeof(buf) - 1);
if (recvLen > 0) {
buf[recvLen] = '\0';
printf("Received: %s", (char*)buf);
/* Send acknowledgment */
ret = wolfSSL_write(ssl, ack, (int)strlen(ack));
if (ret > 0) {
printf("Sent: %s", ack);
}
}
else {
int err = wolfSSL_get_error(ssl, recvLen);
if (err != SSL_ERROR_WANT_READ) {
fprintf(stderr, "Warning: wolfSSL_read returned: %d (%s)\n",
err, wolfSSL_ERR_reason_error_string(err));
}
}
printf("\nSession exported and saved to: %s\n", sessionFile);
printf("You can now use server-dtls-import to resume this session.\n");
ret = 0;
cleanup:
if (sessionBuf != NULL) free(sessionBuf);
if (ssl != NULL) {
wolfSSL_shutdown(ssl);
wolfSSL_free(ssl);
}
if (listenfd > 0) close(listenfd);
if (ctx != NULL) wolfSSL_CTX_free(ctx);
wolfSSL_Cleanup();
return ret;
}

View File

@ -0,0 +1,260 @@
/* server-dtls-import.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
*
*=============================================================================
*
* DTLS server that imports a previously exported session.
* The session is loaded from an encrypted file and decrypted with AES.
* This allows resuming communication without a new handshake.
*
* Requires wolfSSL compiled with:
* ./configure --enable-dtls --enable-sessionexport
*/
#include "dtls-export-common.h"
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#define SERV_PORT 11111
#define MSGLEN 4096
#define DEFAULT_SERVER_SESSION_FILE "dtls_server_session.bin"
static int cleanup = 0;
static void sig_handler(int sig)
{
(void)sig;
cleanup = 1;
}
static void Usage(const char* progName)
{
printf("Usage: %s [session_file]\n", progName);
printf(" session_file - Optional: file to load session from (default: %s)\n",
DEFAULT_SERVER_SESSION_FILE);
}
int main(int argc, char** argv)
{
char caCertLoc[] = "../certs/ca-cert.pem";
char servCertLoc[] = "../certs/server-cert.pem";
char servKeyLoc[] = "../certs/server-key.pem";
int ret = 0;
int on = 1;
int listenfd = 0;
int recvLen;
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
struct sockaddr_in servAddr;
struct sockaddr_in cliAddr;
socklen_t cliLen;
socklen_t len = sizeof(int);
unsigned char buf[MSGLEN];
char ack[] = "Message received (resumed session)!\n";
const char* sessionFile = DEFAULT_SERVER_SESSION_FILE;
unsigned char* sessionBuf = NULL;
unsigned int sessionSz = 0;
/* Program argument checking */
if (argc > 2) {
Usage(argv[0]);
return 1;
}
if (argc == 2) {
sessionFile = argv[1];
}
/* Set up signal handler for clean shutdown */
signal(SIGINT, sig_handler);
/* Initialize wolfSSL */
wolfSSL_Init();
/* Create DTLS 1.2 context */
ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method());
if (ctx == NULL) {
fprintf(stderr, "Error: wolfSSL_CTX_new failed\n");
ret = 1;
goto cleanup;
}
/* Load CA certificates */
ret = wolfSSL_CTX_load_verify_locations(ctx, caCertLoc, NULL);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error: Failed to load CA cert %s\n", caCertLoc);
ret = 1;
goto cleanup;
}
/* Load server certificate */
ret = wolfSSL_CTX_use_certificate_file(ctx, servCertLoc, SSL_FILETYPE_PEM);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error: Failed to load server cert %s\n", servCertLoc);
ret = 1;
goto cleanup;
}
/* Load server private key */
ret = wolfSSL_CTX_use_PrivateKey_file(ctx, servKeyLoc, SSL_FILETYPE_PEM);
if (ret != SSL_SUCCESS) {
fprintf(stderr, "Error: Failed to load server key %s\n", servKeyLoc);
ret = 1;
goto cleanup;
}
/* Create SSL object */
ssl = wolfSSL_new(ctx);
if (ssl == NULL) {
fprintf(stderr, "Error: wolfSSL_new failed\n");
ret = 1;
goto cleanup;
}
/* Load encrypted session from file */
printf("Loading session from %s...\n", sessionFile);
sessionBuf = LoadEncryptedSession(sessionFile, &sessionSz);
if (sessionBuf == NULL) {
fprintf(stderr, "Error: Failed to load session from file\n");
ret = 1;
goto cleanup;
}
/* Import the session */
printf("Importing DTLS session (%u bytes)...\n", sessionSz);
ret = wolfSSL_dtls_import(ssl, sessionBuf, sessionSz);
if (ret < 0) {
fprintf(stderr, "Error: wolfSSL_dtls_import failed: %d\n", ret);
fprintf(stderr, "Make sure wolfSSL was compiled with --enable-sessionexport\n");
ret = 1;
goto cleanup;
}
printf("Session imported successfully!\n");
/* Create UDP socket */
listenfd = socket(AF_INET, SOCK_DGRAM, 0);
if (listenfd < 0) {
fprintf(stderr, "Error: Cannot create socket\n");
ret = 1;
goto cleanup;
}
/* Set socket options */
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, len);
if (ret < 0) {
fprintf(stderr, "Error: setsockopt SO_REUSEADDR failed\n");
ret = 1;
goto cleanup;
}
/* Setup server address */
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(SERV_PORT);
/* Bind socket */
ret = bind(listenfd, (struct sockaddr*)&servAddr, sizeof(servAddr));
if (ret < 0) {
fprintf(stderr, "Error: bind failed\n");
ret = 1;
goto cleanup;
}
printf("Session restored - no handshake needed!\n");
printf("DTLS server listening on port %d\n", SERV_PORT);
printf("Waiting for client messages...\n\n");
/* Wait for client connection */
cliLen = sizeof(cliAddr);
recvLen = (int)recvfrom(listenfd, buf, sizeof(buf), MSG_PEEK,
(struct sockaddr*)&cliAddr, &cliLen);
if (recvLen < 0) {
fprintf(stderr, "Error: recvfrom failed\n");
ret = 1;
goto cleanup;
}
/* Connect UDP socket to client */
ret = connect(listenfd, (struct sockaddr*)&cliAddr, cliLen);
if (ret != 0) {
fprintf(stderr, "Error: UDP connect failed\n");
ret = 1;
goto cleanup;
}
printf("Client connected from %s:%d\n",
inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));
/* Set DTLS peer */
wolfSSL_dtls_set_peer(ssl, &cliAddr, cliLen);
/* Set socket file descriptor */
wolfSSL_set_fd(ssl, listenfd);
/* Handle messages using the imported session */
while (!cleanup) {
recvLen = wolfSSL_read(ssl, buf, sizeof(buf) - 1);
if (recvLen > 0) {
buf[recvLen] = '\0';
printf("Received: %s", (char*)buf);
/* Send acknowledgment */
ret = wolfSSL_write(ssl, ack, (int)strlen(ack));
if (ret > 0) {
printf("Sent: %s", ack);
}
else {
int err = wolfSSL_get_error(ssl, ret);
fprintf(stderr, "Error: wolfSSL_write failed: %d (%s)\n",
err, wolfSSL_ERR_reason_error_string(err));
break;
}
}
else {
int err = wolfSSL_get_error(ssl, recvLen);
if (err != SSL_ERROR_WANT_READ) {
fprintf(stderr, "Error: wolfSSL_read failed: %d (%s)\n",
err, wolfSSL_ERR_reason_error_string(err));
break;
}
}
}
ret = 0;
cleanup:
if (sessionBuf != NULL) free(sessionBuf);
if (ssl != NULL) {
wolfSSL_shutdown(ssl);
wolfSSL_free(ssl);
}
if (listenfd > 0) close(listenfd);
if (ctx != NULL) wolfSSL_CTX_free(ctx);
wolfSSL_Cleanup();
return ret;
}