Show how to do DTLS import and export

pull/550/head
Anthony Hu 2025-12-22 16:26:27 -05:00
parent 1d0c480da4
commit 9187968347
6 changed files with 1288 additions and 1 deletions

View File

@ -1675,7 +1675,7 @@ if (cont == 1 || cleanup == 1) {
return 0;
```
The code above was taken directly from the DTLS server nonblocking file.
The code above was taken directly from the DTLS server nonblocking file.
Be sure to keep in mind that the `AwaitDatagram` code is essentially one large loop that will attempt to listen for a client (in a nonblocking fashion) at every iteration, and will close the loop upon a signal passed by the user.
@ -1688,3 +1688,76 @@ 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
- **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 Running the Examples
In a terminal run the server export application.
```bash
./server-dtls-export
```
In another terminal, run the client export application, enter a message and then press enter.
```bash
./client-dtls-export 127.0.0.1
```
Both application will then exit. You can find new .bin files that are generated.
In the first terminal, run the the server import application.
```bash
./server-dtls-export
```
It will load the server binary file. In the second terminal, run the client import application, type a message and press enter. The server will be able to get the message and decrypt it as if the two applications had never been interrupted.
.
### 6.5 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]
[16 bytes: random IV]
[N bytes: AES-CBC encrypted session]
```
**Warning**: The examples use a hard-coded AES key for demonstration purposes. In production:
- Derive keys from a secure source
- Consider using authenticated encryption (AES-GCM)
- Implement proper key rotation

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_CLIENT_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_CLIENT_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);
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,187 @@
/* 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_CLIENT_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 sendLine[MAXLINE];
char recvLine[MAXLINE - 1];
const char* sessionFile = DEFAULT_CLIENT_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;
}
/* 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);
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,303 @@
/* 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_CLIENT_SESSION_FILE "dtls_client_session.bin"
#define DEFAULT_SERVER_SESSION_FILE "dtls_server_session.bin"
/* AES-256-CBC key (32 bytes) - In production, use a securely generated key.*/
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
*/
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 session data (caller must free), or NULL on error
* sessionSz is set to the size of the returned data
*/
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,282 @@
/* 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
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);
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,231 @@
/* 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
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)
{
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;
}
/* 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;
}