From e85913ee99a0ffaa350b6e46103be741f1651019 Mon Sep 17 00:00:00 2001 From: Masaki I <32640424+tamasan238@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:32:02 +0900 Subject: [PATCH] add session-tickets and resumption --- tls-options/Makefile | 43 +++++ tls-options/README.md | 71 ++++++++ tls-options/client-tls-resume.c | 280 +++++++++++++++++++++++++++++++ tls-options/client-tls-session.c | 257 ++++++++++++++++++++++++++++ 4 files changed, 651 insertions(+) create mode 100644 tls-options/Makefile create mode 100644 tls-options/README.md create mode 100644 tls-options/client-tls-resume.c create mode 100644 tls-options/client-tls-session.c diff --git a/tls-options/Makefile b/tls-options/Makefile new file mode 100644 index 00000000..82801ffb --- /dev/null +++ b/tls-options/Makefile @@ -0,0 +1,43 @@ +# TLS Examples Makefile +CC = gcc +LIB_PATH = /usr/local +CFLAGS = -Wall -I$(LIB_PATH)/include +LIBS = -L$(LIB_PATH)/lib -lm + +# option variables +DYN_LIB = -lwolfssl +STATIC_LIB = $(LIB_PATH)/lib/libwolfssl.a +DEBUG_FLAGS = -g -DDEBUG +DEBUG_INC_PATHS = -MD +OPTIMIZE = -Os + +# Options +#CFLAGS+=$(DEBUG_FLAGS) +CFLAGS+=$(OPTIMIZE) +#LIBS+=$(STATIC_LIB) +LIBS+=$(DYN_LIB) + +# build targets +SRC=$(wildcard *.c) +TARGETS=$(patsubst %.c, %, $(SRC)) + +all: $(TARGETS) + +debug: CFLAGS+=$(DEBUG_FLAGS) +debug: all + +# add the -pthread flag to any threaded examples +%-threaded: CFLAGS+=-pthread +%-writedup: CFLAGS+=-pthread +memory-tls: CFLAGS+=-pthread + +# compile tcp examples without the LIBS variable +%-tcp: LIBS= + +# build template +%: %.c + $(CC) -o $@ $< $(CFLAGS) $(LIBS) + +clean: + rm -f $(TARGETS) + rm -f session.bin diff --git a/tls-options/README.md b/tls-options/README.md new file mode 100644 index 00000000..f811c7b8 --- /dev/null +++ b/tls-options/README.md @@ -0,0 +1,71 @@ +# wolfSSL TLS with Some Options Example + +This example implements a simple echo client and server that uses TLS with some options. + +## Building + +You need to have wolfSSL installed on your computer prior to building. + +To compile these programs use `make` in this directory. + +## Running + +### Session Tickets and Resumption + +Here is an example of suspending a session once started and resuming the session later. + +1. Establish the first session with `client-tls-session`. +This program outputs a session ticket as a binary file. +2. Resume a previously interrupted session with `client-tls-resume`. + +#### Make session ticket + +On one console run the server, this should be executed first or the handshake will fail. + +You can use them as a server: +- TLS 1.2: ../tls/server-tls +- TLS 1.3: ../tls/server-tls13 + +These are in different directories, so you need to use `make` again. + +```sh +cd ../tls && make +./server-tls13 +``` + +Then in another terminal run the client: + +```sh +./client-tls-session +``` + +You will be able to send a message from client to server. + +Sending "break" as a message to the server will generate "session.bin" + + +#### Resume + +You can resume earlier session by doing: + +```sh +./client-tls-resume +``` + +"session.bin" is referenced as a session ticket. +If it doesn't exist or is invalid, this program will start a new session. + +You will be able to send a message from client to server. + +Sending "break" as a message to the server will break the session. +If you use TLS 1.3 server, You can resume many times. + + +## Cleaning Up + +You can remove executable files by doing: + +```sh +make clean +``` + diff --git a/tls-options/client-tls-resume.c b/tls-options/client-tls-resume.c new file mode 100644 index 00000000..6b7d0110 --- /dev/null +++ b/tls-options/client-tls-resume.c @@ -0,0 +1,280 @@ +/* client-tls-resume.c + * + * Copyright (C) 2006-2023 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 + */ + +/* the usual suspects */ +#include +#include +#include + +/* socket includes */ +#include +#include +#include +#include + +/* wolfSSL */ +#include +#include + +#define CERT_FILE "../certs/client-cert.pem" +#define KEY_FILE "../certs/client-key.pem" +#define CA_CERT_FILE "../certs/ca-cert.pem" +#define LOCALHOST "127.0.0.1" +#define DEFAULT_PORT 11111 +#define SAVED_SESS "session.bin" + +#define MSG_SIZE 256 + +/* Print SSL error message */ +static void print_SSL_error(const char* msg, SSL* ssl) +{ + int err; + + if (ssl != NULL) { + err = wolfSSL_get_error(ssl, 0); + fprintf(stderr, "ERROR: %s (err %d, %s)\n", msg, err, + ERR_error_string(err, NULL)); + } + else { + fprintf(stderr, "ERROR: %s \n", msg); + } +} + +/* read a session from the file if exists */ +static int read_SESS(const char* file, SSL* ssl) +{ + FILE* fp = NULL; + unsigned char* buff = NULL; + const unsigned char* p = NULL; + size_t sz; + WOLFSSL_SESSION* sess = NULL; + int ret = WOLFSSL_FAILURE; + + if (((fp = fopen(file, "rb")) == NULL) || + (fseek(fp, 0, SEEK_END) != 0) || + ((sz = ftell(fp)) == -1)) { + fprintf(stderr, "ERROR : failed file %s operation \n", file); + goto cleanup; + } + + rewind(fp); + if ((buff = (unsigned char*)malloc(sz)) == NULL || + (fread(buff, 1, sz, fp) != sz)) { + fprintf(stderr, "ERROR : failed reading file\n"); + goto cleanup; + } + + printf("%s size = %ld\n", SAVED_SESS, sz); + + p = buff; + if((sess = wolfSSL_d2i_SSL_SESSION(NULL, (const unsigned char**)&p, sz)) == NULL) { + print_SSL_error("wolfSSL_d2i_SSL_SESSION", NULL); + } + + if(sess != NULL && (ret = wolfSSL_set_session(ssl, sess) != WOLFSSL_SUCCESS)) { + print_SSL_error("failed SSL session", ssl); + } else { + printf("Resuming session\n"); + ret = WOLFSSL_SUCCESS; + } + +cleanup: + if (sess) + wolfSSL_SESSION_free(sess); + if (fp) + fclose(fp); + if (buff) + free(buff); + return ret; +} + +int main(int argc, char **argv) +{ + struct sockaddr_in servAddr; + int sockfd = -1; + char *ipadd = LOCALHOST; + char *ca_cert = CA_CERT_FILE; + int port = DEFAULT_PORT; + static const char kHttpGetMsg[] = "GET /index.html HTTP/1.0\r\n\r\n"; + struct hostent *host; + + char msg[MSG_SIZE]; + int ret = WOLFSSL_FAILURE; + + (void)ipadd; + + /* SSL objects */ + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + memset(&servAddr, 0, sizeof(servAddr)); + + /* Check for proper calling convention */ + if (argc == 1) + fprintf(stderr, "Send to localhost(%s)\n", LOCALHOST); + if (argc >=2) { + host = gethostbyname(argv[1]); + memcpy(&servAddr.sin_addr, host->h_addr_list[0], host->h_length); + } + if (argc >= 3) + ca_cert = argv[2]; + if (argc == 4) + port = atoi(argv[3]); + if (argc >= 5) { + fprintf(stderr, "ERROR: Too many arguments.\n"); + goto cleanup; + } + + /* Initialize library */ + if (wolfSSL_library_init() != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to initialize the library\n"); + goto cleanup; + } + + /* Create and initialize an SSL context object*/ + if ((ctx = wolfSSL_CTX_new(SSLv23_client_method())) == NULL) { + fprintf(stderr, "ERROR: failed to create an SSL context object\n"); + goto cleanup; + } + + /* Load client certificate into WOLFwolfSSL_CTX */ + if ((ret = wolfSSL_CTX_use_certificate_file(ctx, CERT_FILE, + WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", + CERT_FILE); + goto cleanup; + } + + /* Load client key into WOLFwolfSSL_CTX */ + if ((ret = wolfSSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, + WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", + KEY_FILE); + goto cleanup; + } + + /* Load CA certificate to the context */ + if ((ret = wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL)) + != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s \n", ca_cert); + goto cleanup; + } + + /* + * Set up a TCP Socket and connect to the server + */ + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + fprintf(stderr, "ERROR: failed to create a socket. errno %d\n", errno); + goto cleanup; + } + + servAddr.sin_family = AF_INET; /* using IPv4 */ + servAddr.sin_port = htons(port); /* on DEFAULT_PORT */ + + if ((ret = connect(sockfd, (struct sockaddr *)&servAddr, sizeof(servAddr))) + == -1) { + fprintf(stderr, "ERROR: failed to connect. errno %d\n", errno); + goto cleanup; + } + + /* Create an SSL object */ + if ((ssl = wolfSSL_new(ctx)) == NULL) { + fprintf(stderr, "ERROR: failed to create an SSL object\n"); + goto cleanup; + } + + /* read a seesion from file */ + if ((ret = read_SESS(SAVED_SESS, ssl)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to read session information\n"); + goto cleanup; + } + + /* Attach the socket to the SSL */ + if ((ret = wolfSSL_set_fd(ssl, sockfd)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: Failed to set the file descriptor\n"); + goto cleanup; + } + /* SSL connect to the server */ + if ((ret = wolfSSL_connect(ssl)) != WOLFSSL_SUCCESS) { + print_SSL_error("failed SSL connect", ssl); + goto cleanup; + } + + /* check if session is resued */ + if (wolfSSL_session_reused(ssl) == 1) { + printf("Session is reused\n"); + } + else { + printf("Session is not reused. New session was negotiated.\n"); + } + + /* + * Application messaging + */ + while (1) { + printf("Message to send: "); + if(fgets(msg, sizeof(msg), stdin) <= 0) + break; + if (strcmp(msg, "\n") == 0){ /* if empty send HTTP request */ + strncpy(msg, kHttpGetMsg, sizeof(msg)); + } else + msg[strnlen(msg, sizeof(msg))-1] = '\0'; + /* send a message to the server */ + if ((ret = wolfSSL_write(ssl, msg, strnlen(msg, sizeof(msg)))) < 0) { + print_SSL_error("failed SSL write", ssl); + break; + } + + /* + * closing the session, and write session information into a file + * before writing session information, the file is removed if exists + */ + if (strcmp(msg, "break") == 0) { + printf("Sending break command\n"); + ret = WOLFSSL_SUCCESS; + break; + } + + /* receive a message from the server */ + if ((ret = wolfSSL_read(ssl, msg, sizeof(msg) - 1)) < 0) { + print_SSL_error("failed SSL read", ssl); + break; + } + msg[ret] = '\0'; + printf("Received: %s\n", msg); + } + +/* Cleanup and return */ +cleanup: + if (ssl != NULL) { + wolfSSL_shutdown(ssl); + wolfSSL_free(ssl); + } + if (sockfd != -1) + close(sockfd); + if (ctx != NULL) + wolfSSL_CTX_free(ctx); + if (ret != WOLFSSL_SUCCESS) + ret = WOLFSSL_FAILURE; + printf("End of TLS Client\n"); + return ret; +} + diff --git a/tls-options/client-tls-session.c b/tls-options/client-tls-session.c new file mode 100644 index 00000000..8096b87a --- /dev/null +++ b/tls-options/client-tls-session.c @@ -0,0 +1,257 @@ +/* client-tls-session.c + * + * Copyright (C) 2006-2023 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 + */ + +/* the usual suspects */ +#include +#include +#include + +/* socket includes */ +#include +#include +#include +#include + +/* wolfSSL */ +#include +#include + +#define CERT_FILE "../certs/client-cert.pem" +#define KEY_FILE "../certs/client-key.pem" +#define CA_CERT_FILE "../certs/ca-cert.pem" +#define LOCALHOST "127.0.0.1" +#define DEFAULT_PORT 11111 +#define SAVED_SESS "session.bin" + +#define MSG_SIZE 256 + +/* Print SSL error message */ +static void print_SSL_error(const char* msg, SSL* ssl) +{ + int err; + + if (ssl != NULL) { + err = wolfSSL_get_error(ssl, 0); + fprintf(stderr, "ERROR: %s (err %d, %s)\n", msg, err, + ERR_error_string(err, NULL)); + } + else { + fprintf(stderr, "ERROR: %s \n", msg); + } +} + +/* write a session to the file */ +static int write_SESS(WOLFSSL_SESSION* sess, const char* file) +{ + FILE* fp = NULL; + unsigned char* buff = NULL; + size_t sz; + int ret = WOLFSSL_FAILURE; + + if ((fp = fopen(file, "wb")) == NULL) { + fprintf(stderr, "ERROR : file %s does't exists\n", file); + goto cleanup; + } + + if ((sz = wolfSSL_i2d_SSL_SESSION(sess, &buff)) <= 0){ + print_SSL_error("wolfSSL_i2d_SSL_SESSION", NULL); + goto cleanup; + } + + if ((fwrite(buff, 1, sz, fp)) != sz) { + fprintf(stderr, "ERROR : failed fwrite\n"); + goto cleanup; + } + +cleanup: + if (fp) + fclose(fp); + if (buff) + free(buff); + + return ret; +} + +int main(int argc, char **argv) +{ + struct sockaddr_in servAddr; + int sockfd = -1; + char *ipadd = LOCALHOST; + char *ca_cert = CA_CERT_FILE; + int port = DEFAULT_PORT; + static const char kHttpGetMsg[] = "GET /index.html HTTP/1.0\r\n\r\n"; + struct hostent *host; + + char msg[MSG_SIZE]; + int ret = WOLFSSL_FAILURE; + + (void)ipadd; + + /* SSL objects */ + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + memset(&servAddr, 0, sizeof(servAddr)); + + /* SSL SESSION object */ + WOLFSSL_SESSION* session= NULL; + + /* Check for proper calling convention */ + if (argc == 1) + fprintf(stderr, "Send to localhost(%s)\n", LOCALHOST); + if (argc >=2) { + host = gethostbyname(argv[1]); + memcpy(&servAddr.sin_addr, host->h_addr_list[0], host->h_length); + } + if (argc >= 3) + ca_cert = argv[2]; + if (argc == 4) + port = atoi(argv[3]); + if (argc >= 5) { + fprintf(stderr, "ERROR: Too many arguments.\n"); + goto cleanup; + } + + /* Initialize library */ + if (wolfSSL_library_init() != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to initialize the library\n"); + goto cleanup; + } + + /* Create and initialize an SSL context object*/ + if ((ctx = wolfSSL_CTX_new(SSLv23_client_method())) == NULL) { + fprintf(stderr, "ERROR: failed to create an SSL context object\n"); + goto cleanup; + } + + /* Load client certificate into WOLFSSL_CTX */ + if ((ret = wolfSSL_CTX_use_certificate_file(ctx, CERT_FILE, + WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", + CERT_FILE); + goto cleanup; + } + + /* Load client key into WOLFSSL_CTX */ + if ((ret = wolfSSL_CTX_use_PrivateKey_file(ctx, KEY_FILE, + WOLFSSL_FILETYPE_PEM)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s, please check the file.\n", + KEY_FILE); + goto cleanup; + } + + /* Load CA certificate to the context */ + if ((ret = wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL)) + != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: failed to load %s \n", ca_cert); + goto cleanup; + } + + /* + * Set up a TCP Socket and connect to the server + */ + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + fprintf(stderr, "ERROR: failed to create a socket. errno %d\n", errno); + goto cleanup; + } + + servAddr.sin_family = AF_INET; /* using IPv4 */ + servAddr.sin_port = htons(port); /* on DEFAULT_PORT */ + + if ((ret = connect(sockfd, (struct sockaddr *)&servAddr, sizeof(servAddr))) + == -1) { + fprintf(stderr, "ERROR: failed to connect. errno %d\n", errno); + goto cleanup; + } + + /* Create an SSL object */ + if ((ssl = wolfSSL_new(ctx)) == NULL) { + fprintf(stderr, "ERROR: failed to create an SSL object\n"); + goto cleanup; + } + + /* Attach the socket to the SSL */ + if ((ret = wolfSSL_set_fd(ssl, sockfd)) != WOLFSSL_SUCCESS) { + fprintf(stderr, "ERROR: Failed to set the file descriptor\n"); + goto cleanup; + } + /* SSL connect to the server */ + if ((ret = wolfSSL_connect(ssl)) != WOLFSSL_SUCCESS) { + print_SSL_error("failed SSL connect", ssl); + goto cleanup; + } + + /* + * Application messaging + */ + while (1) { + printf("Message to send: "); + if(fgets(msg, sizeof(msg), stdin) <= 0) + break; + if (strcmp(msg, "\n") == 0){ /* if empty send HTTP request */ + strncpy(msg, kHttpGetMsg, sizeof(msg)); + } else + msg[strnlen(msg, sizeof(msg)) - 1] = '\0'; + /* send a message to the server */ + if ((ret = wolfSSL_write(ssl, msg, strnlen(msg, sizeof(msg)))) < 0) + { + print_SSL_error("failed SSL write", ssl); + break; + } + + /* + * closing the session, and write session information into a file + * before writing session information + */ + if (strcmp(msg, "break") == 0) { + session = wolfSSL_get_session(ssl); + ret = write_SESS(session, SAVED_SESS); + break; + } + + /* receive a message from the server */ + if ((ret = wolfSSL_read(ssl, msg, sizeof(msg) - 1)) < 0) { + print_SSL_error("failed SSL read", ssl); + break; + } + msg[ret] = '\0'; + printf("Received: %s\n", msg); + } + +/* Cleanup and return */ +cleanup: + if (session != NULL) { + wolfSSL_SESSION_free(session); + } + if (ssl != NULL) { + wolfSSL_shutdown(ssl); + wolfSSL_free(ssl); + } + if (sockfd != -1) + close(sockfd); + if (ctx != NULL) + wolfSSL_CTX_free(ctx); + if (ret != WOLFSSL_SUCCESS) + ret = WOLFSSL_FAILURE; + printf("End of TLS Client\n"); + return ret; +} +