mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 14:30:49 +02:00
258 lines
8.5 KiB
C
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;
|
|
}
|