diff --git a/dtls/README.md b/dtls/README.md index 9fa58825..c2f7bebf 100644 --- a/dtls/README.md +++ b/dtls/README.md @@ -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,86 @@ 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-import +``` + +It will import the server binary file. + +In the second terminal, run the client import application. + +```bash +./client-dtls-import +``` + +It will import the client binary file. + +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 diff --git a/dtls/client-dtls-export.c b/dtls/client-dtls-export.c new file mode 100644 index 00000000..2cdc4b5e --- /dev/null +++ b/dtls/client-dtls-export.c @@ -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 +#include +#include +#include +#include +#include + +#define MAXLINE 4096 +#define SERV_PORT 11111 + +static void Usage(const char* progName) +{ + printf("Usage: %s [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; +} diff --git a/dtls/client-dtls-import.c b/dtls/client-dtls-import.c new file mode 100644 index 00000000..d6ff0269 --- /dev/null +++ b/dtls/client-dtls-import.c @@ -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 +#include +#include +#include +#include +#include + +#define MAXLINE 4096 +#define SERV_PORT 11111 + +static void Usage(const char* progName) +{ + printf("Usage: %s [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; +} diff --git a/dtls/dtls-export-common.h b/dtls/dtls-export-common.h new file mode 100644 index 00000000..70f0a184 --- /dev/null +++ b/dtls/dtls-export-common.h @@ -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 +#include +#include +#include +#include +#include +#include + +/* 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 */ diff --git a/dtls/server-dtls-export.c b/dtls/server-dtls-export.c new file mode 100644 index 00000000..857e00d9 --- /dev/null +++ b/dtls/server-dtls-export.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/dtls/server-dtls-import.c b/dtls/server-dtls-import.c new file mode 100644 index 00000000..f1a83b9c --- /dev/null +++ b/dtls/server-dtls-import.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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; +}