Files

258 lines
8.5 KiB
C

/* tls13_memio.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* 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 3 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-1335, USA
*/
/* Self-contained TLS 1.3 handshake over an in-memory transport.
*
* Single process, no sockets, no threads (SINGLE_THREADED safe): the client
* and server WOLFSSL objects are wired together through two in-memory byte
* queues, and the handshake is driven to completion in one loop. It exercises
* the real TLS 1.3 handshake state machine for builds where the example/unit
* test harness is not available, e.g. a minimal --disable-examples build.
*
* By default it runs the minimal PSK + ECDHE handshake (no X.509). On a cert
* build (WOLFSSL_TINY_TLS13_CERT) it runs a certificate handshake instead: the
* server presents an ECDSA P-256 certificate and the client validates it,
* driving the Certificate / CertificateVerify path. Cert files default to
* ../certs (the layout used by parallel-make-check.py builds); override the
* CERT_DIR macro at build time to point elsewhere.
*
* Build against a static build and run:
* cc -I<build> -I<src> tls13_memio.c <build>/src/.libs/libwolfssl.a -lm \
* -o tls13_memio && ./tls13_memio
*/
#ifndef WOLFSSL_USER_SETTINGS
#include <wolfssl/options.h>
#endif
#include <wolfssl/ssl.h>
#include <string.h>
#include <stdio.h>
/* In-memory transport buffer; must hold one handshake flight. Build-time override. */
#ifndef MEM_BUF_SZ
#define MEM_BUF_SZ 32768
#endif
/* Max connect/accept round trips to drive the handshake. Build-time override. */
#ifndef HS_MAX_ITERS
#define HS_MAX_ITERS 50
#endif
#ifndef WOLFSSL_TINY_TLS13_CERT
/* Example-only fixed test PSK and identity shared by both endpoints; not a
* real secret. Provision your own for anything beyond this smoke test. */
static const unsigned char psk_key[16] = {
0x1a, 0x2b, 0x3c, 0x4d, 0x5e, 0x6f, 0x70, 0x81,
0x92, 0xa3, 0xb4, 0xc5, 0xd6, 0xe7, 0xf8, 0x09
};
static const char psk_identity[] = "tinytls13-client";
#endif /* !WOLFSSL_TINY_TLS13_CERT */
typedef struct membuf {
unsigned char data[MEM_BUF_SZ];
int len;
} membuf;
/* recv: drain from the queue this endpoint reads from */
static int mem_recv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
membuf* mb = (membuf*)ctx;
int n;
(void)ssl;
if (mb->len == 0)
return WOLFSSL_CBIO_ERR_WANT_READ;
n = (sz < mb->len) ? sz : mb->len;
XMEMCPY(buf, mb->data, (size_t)n);
XMEMMOVE(mb->data, mb->data + n, (size_t)(mb->len - n));
mb->len -= n;
return n;
}
/* send: append to the queue the peer reads from */
static int mem_send(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
membuf* mb = (membuf*)ctx;
(void)ssl;
if (sz < 0 || mb->len > MEM_BUF_SZ - sz)
return WOLFSSL_CBIO_ERR_WANT_WRITE;
XMEMCPY(mb->data + mb->len, buf, (size_t)sz);
mb->len += sz;
return sz;
}
#ifndef WOLFSSL_TINY_TLS13_CERT
static unsigned int psk_client_cb(WOLFSSL* ssl, const char* hint,
char* identity, unsigned int id_max, unsigned char* key,
unsigned int key_max)
{
(void)ssl;
(void)hint;
if (id_max < sizeof(psk_identity) || key_max < sizeof(psk_key))
return 0;
XMEMCPY(identity, psk_identity, sizeof(psk_identity));
XMEMCPY(key, psk_key, sizeof(psk_key));
return (unsigned int)sizeof(psk_key);
}
static unsigned int psk_server_cb(WOLFSSL* ssl, const char* identity,
unsigned char* key, unsigned int key_max)
{
(void)ssl;
(void)identity;
if (key_max < sizeof(psk_key))
return 0;
XMEMCPY(key, psk_key, sizeof(psk_key));
return (unsigned int)sizeof(psk_key);
}
#endif /* !WOLFSSL_TINY_TLS13_CERT */
#ifdef WOLFSSL_TINY_TLS13_CERT
/* Build-time override to point at a different certs tree. */
#ifndef CERT_DIR
#define CERT_DIR "../certs"
#endif
#if defined(WOLFSSL_HAVE_MLDSA)
#define SERVER_CERT_FILE CERT_DIR "/mldsa/ecc-leaf-mldsa44.pem"
#define CLIENT_CA_FILE CERT_DIR "/mldsa/mldsa44-cert.pem"
#elif defined(WOLFSSL_TINY_TLS13_RSA_VERIFY)
#define SERVER_CERT_FILE CERT_DIR "/rsapss/ecc-leaf-rsapss.pem"
#define CLIENT_CA_FILE CERT_DIR "/rsapss/ca-rsapss.pem"
#else
#define SERVER_CERT_FILE CERT_DIR "/server-ecc.pem"
#define CLIENT_CA_FILE CERT_DIR "/ca-ecc-cert.pem"
#endif
#define SERVER_KEY_FILE CERT_DIR "/ecc-key.pem"
#endif /* WOLFSSL_TINY_TLS13_CERT */
int main(int argc, char** argv)
{
WOLFSSL_CTX* cctx = NULL;
WOLFSSL_CTX* sctx = NULL;
WOLFSSL* c = NULL;
WOLFSSL* s = NULL;
membuf c2s; /* client writes, server reads */
membuf s2c; /* server writes, client reads */
int i, cdone = 0, sdone = 0, ret = 1;
int cret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR);
int sret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR);
const char* cipher = (argc > 1) ? argv[1] : "-";
const char* group = (argc > 2) ? argv[2] : "-";
int mlkemGroup[1];
XMEMSET(&c2s, 0, sizeof(c2s));
XMEMSET(&s2c, 0, sizeof(s2c));
wolfSSL_Init();
cctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
sctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
if (cctx == NULL || sctx == NULL) {
printf("smoke: CTX_new failed\n");
goto done;
}
/* Force a specific suite when asked, so an adder config proves its cipher
* negotiates: a single-suite list means a completed handshake used it. */
if (cipher[0] != '\0' && cipher[0] != '-') {
wolfSSL_CTX_set_cipher_list(cctx, cipher);
wolfSSL_CTX_set_cipher_list(sctx, cipher);
}
#ifdef WOLFSSL_TINY_TLS13_CERT
/* Server presents an ECDSA leaf signed by the CA whose algorithm this
* profile verifies (ECDSA, ML-DSA-44, or RSA-PSS); a completed handshake
* drives that verify path. */
if (wolfSSL_CTX_use_certificate_file(sctx, SERVER_CERT_FILE,
WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS ||
wolfSSL_CTX_use_PrivateKey_file(sctx, SERVER_KEY_FILE,
WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS ||
wolfSSL_CTX_load_verify_locations(cctx, CLIENT_CA_FILE, NULL)
!= WOLFSSL_SUCCESS) {
printf("smoke: cert load failed (dir=%s)\n", CERT_DIR);
goto done;
}
#else
wolfSSL_CTX_set_psk_client_callback(cctx, psk_client_cb);
wolfSSL_CTX_set_psk_server_callback(sctx, psk_server_cb);
#endif
wolfSSL_CTX_SetIORecv(cctx, mem_recv);
wolfSSL_CTX_SetIOSend(cctx, mem_send);
wolfSSL_CTX_SetIORecv(sctx, mem_recv);
wolfSSL_CTX_SetIOSend(sctx, mem_send);
c = wolfSSL_new(cctx);
s = wolfSSL_new(sctx);
if (c == NULL || s == NULL) {
printf("smoke: SSL_new failed\n");
goto done;
}
/* Restrict to the ML-KEM hybrid key share when asked, so a completed
* handshake proves the hybrid KEX was negotiated. */
if (XSTRCMP(group, "mlkem") == 0) {
mlkemGroup[0] = WOLFSSL_X25519MLKEM768;
wolfSSL_set_groups(c, mlkemGroup, 1);
wolfSSL_set_groups(s, mlkemGroup, 1);
}
/* client reads s2c, writes c2s; server reads c2s, writes s2c */
wolfSSL_SetIOReadCtx(c, &s2c);
wolfSSL_SetIOWriteCtx(c, &c2s);
wolfSSL_SetIOReadCtx(s, &c2s);
wolfSSL_SetIOWriteCtx(s, &s2c);
for (i = 0; i < HS_MAX_ITERS && !(cdone && sdone); i++) {
if (!cdone) {
cret = wolfSSL_connect(c);
if (cret == WOLFSSL_SUCCESS)
cdone = 1;
}
if (!sdone) {
sret = wolfSSL_accept(s);
if (sret == WOLFSSL_SUCCESS)
sdone = 1;
}
}
if (cdone && sdone &&
XSTRCMP(wolfSSL_get_version(c), "TLSv1.3") == 0) {
printf("tls13_memio handshake OK: %s %s\n",
wolfSSL_get_version(c), wolfSSL_get_cipher(c));
ret = 0;
}
else {
printf("tls13_memio handshake FAILED (client err %d, server err %d)\n",
wolfSSL_get_error(c, cret), wolfSSL_get_error(s, sret));
}
done:
wolfSSL_free(c);
wolfSSL_free(s);
wolfSSL_CTX_free(cctx);
wolfSSL_CTX_free(sctx);
wolfSSL_Cleanup();
return ret;
}