wolfssl-examples/embedded/efr32xg25/tls13_test.c

306 lines
10 KiB
C

/* tls13_test.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
*/
/* TLS 1.3 handshake and record layer over an in-memory transport, run on the
* device with every crypto operation routed to the Secure Element.
*
* The wolfCrypt test proves the callbacks against known answer vectors one call
* at a time; this drives them the way a connection does - ECDHE key share,
* ECDSA certificate and CertificateVerify, the HKDF key schedule and a running
* transcript hash, then application data through the record layer.
*
* That catches what the vector tests cannot. Handshake and record buffers are
* cursors into a larger buffer, so they land on arbitrary offsets rather than
* aligned stack arrays - if an engine needs alignment or a DMA-reachable
* buffer, it shows here. One cipher object is also reused across thousands of
* chained records, so state the port fails to carry between calls fails rather
* than passing by luck on a single shot.
*
* Client and server both run in one thread, wired through two byte queues.
* Modeled on examples/tls13/tls13_memio.c, with the file-based certificates
* replaced by the compiled-in test buffers and the SE devId on both ends.
*/
#include <wolfssl/wolfcrypt/settings.h>
#ifdef WOLFSSL_XG25_TLS13
#include <stdio.h>
#include <wolfssl/ssl.h>
#include <wolfssl/certs_test.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#include <wolfssl/wolfcrypt/port/silabs/silabs_settings.h>
/* Holds one handshake flight. The server's is the larger: certificate,
* CertificateVerify and Finished together stay under 2 KB with a P-256
* chain. */
#define XG25_TLS_BUF_SZ 4096
/* Handshake needs a few round trips; the cap only stops a stalled loop. */
#define XG25_TLS_MAX_ITERS 32
typedef struct membuf {
unsigned char data[XG25_TLS_BUF_SZ];
int len;
} membuf;
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;
}
static int mem_send(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
membuf* mb = (membuf*)ctx;
(void)ssl;
/* A write larger than the queue itself never fits, however much the peer
* drains, so fail it outright. Retrying to the iteration cap would report
* it as a stalled handshake instead. */
if (sz < 0 || sz > XG25_TLS_BUF_SZ) {
return WOLFSSL_CBIO_ERR_GENERAL;
}
if (mb->len > XG25_TLS_BUF_SZ - sz) {
return WOLFSSL_CBIO_ERR_WANT_WRITE;
}
XMEMCPY(mb->data + mb->len, buf, (size_t)sz);
mb->len += sz;
return sz;
}
/* Application data round trip at deliberately awkward lengths and offsets.
* Every record is sealed and opened by the SE and read back byte for byte, so
* a record layer that corrupts data fails here even after a good handshake. */
static int xg25_tls13_echo(WOLFSSL* c, WOLFSSL* s, int payloadSz, int offset)
{
static unsigned char out[512];
static unsigned char in[512];
int i;
int ret;
if (offset + payloadSz > (int)sizeof(out)) {
return BAD_FUNC_ARG;
}
for (i = 0; i < payloadSz; i++) {
out[offset + i] = (unsigned char)(i * 7 + payloadSz);
}
ret = wolfSSL_write(c, out + offset, payloadSz);
if (ret != payloadSz) {
printf(" write failed: %d (err %d)\n", ret, wolfSSL_get_error(c, ret));
return -1;
}
XMEMSET(in, 0, sizeof(in));
ret = wolfSSL_read(s, in + offset, payloadSz);
if (ret != payloadSz) {
printf(" read failed: %d (err %d)\n", ret, wolfSSL_get_error(s, ret));
return -1;
}
if (XMEMCMP(out + offset, in + offset, (size_t)payloadSz) != 0) {
printf(" payload mismatch at %d bytes, offset %d\n",
payloadSz, offset);
return -1;
}
return 0;
}
static int xg25_tls13_one(const char* suite)
{
/* Static rather than automatic: two 4 KB queues plus the peer objects
* would otherwise sit on a small-stack build's stack. */
static membuf c2s; /* client writes, server reads */
static membuf s2c; /* server writes, client reads */
/* Odd lengths and offsets on purpose: unaligned starts and partial final
* blocks are what an engine with an alignment or block assumption trips
* over. */
static const int sizes[] = { 1, 15, 16, 17, 31, 63, 127, 128, 255 };
static const int offsets[] = { 0, 1, 2, 3, 1, 2, 3, 1, 2 };
WOLFSSL_CTX* cctx = NULL;
WOLFSSL_CTX* sctx = NULL;
WOLFSSL* c = NULL;
WOLFSSL* s = NULL;
int cdone = 0, sdone = 0;
int cret = WOLFSSL_FATAL_ERROR;
int sret = WOLFSSL_FATAL_ERROR;
int ret = -1;
int i;
XMEMSET(&c2s, 0, sizeof(c2s));
XMEMSET(&s2c, 0, sizeof(s2c));
cctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
sctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
if (cctx == NULL || sctx == NULL) {
printf(" CTX_new failed\n");
goto done;
}
/* A single-suite list means a completed handshake proves that suite was
* the one used. */
if (wolfSSL_CTX_set_cipher_list(cctx, suite) != WOLFSSL_SUCCESS ||
wolfSSL_CTX_set_cipher_list(sctx, suite) != WOLFSSL_SUCCESS) {
printf(" %s: not built in, skipped\n", suite);
ret = 0;
goto done;
}
/* The whole point: every private key operation, key schedule step,
* transcript hash and record seal below goes to the Secure Element. */
if (wolfSSL_CTX_SetDevId(cctx, WOLFSSL_SILABS_DEVID) != WOLFSSL_SUCCESS ||
wolfSSL_CTX_SetDevId(sctx, WOLFSSL_SILABS_DEVID) != WOLFSSL_SUCCESS) {
printf(" CTX_SetDevId failed\n");
goto done;
}
/* P-256 server certificate and its issuing CA, from the compiled-in test
* buffers. Test credentials only - never ship these. */
if (wolfSSL_CTX_use_certificate_buffer(sctx, serv_ecc_der_256,
(long)sizeof_serv_ecc_der_256, WOLFSSL_FILETYPE_ASN1)
!= WOLFSSL_SUCCESS) {
printf(" server cert load failed\n");
goto done;
}
if (wolfSSL_CTX_use_PrivateKey_buffer(sctx, ecc_key_der_256,
(long)sizeof_ecc_key_der_256, WOLFSSL_FILETYPE_ASN1)
!= WOLFSSL_SUCCESS) {
printf(" server key load failed\n");
goto done;
}
ret = wolfSSL_CTX_load_verify_buffer(cctx, ca_ecc_cert_der_256,
(long)sizeof_ca_ecc_cert_der_256, WOLFSSL_FILETYPE_ASN1);
if (ret != WOLFSSL_SUCCESS) {
printf(" CA load failed: %d\n", ret);
ret = -1;
goto done;
}
ret = -1;
/* Peer verification stays on: the client must actually validate the
* server's chain, which drives ECDSA verify through the SE. */
wolfSSL_CTX_set_verify(cctx, WOLFSSL_VERIFY_PEER, NULL);
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(" SSL_new failed\n");
goto done;
}
wolfSSL_SetIOReadCtx(c, &s2c);
wolfSSL_SetIOWriteCtx(c, &c2s);
wolfSSL_SetIOReadCtx(s, &c2s);
wolfSSL_SetIOWriteCtx(s, &s2c);
for (i = 0; i < XG25_TLS_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) {
printf(" handshake FAILED (client err %d, server err %d)\n",
wolfSSL_get_error(c, cret), wolfSSL_get_error(s, sret));
goto done;
}
if (XSTRCMP(wolfSSL_get_version(c), "TLSv1.3") != 0) {
printf(" negotiated %s, expected TLSv1.3\n", wolfSSL_get_version(c));
goto done;
}
printf(" handshake OK: %s %s\n",
wolfSSL_get_version(c), wolfSSL_get_cipher(c));
for (i = 0; i < (int)(sizeof(sizes) / sizeof(sizes[0])); i++) {
if (xg25_tls13_echo(c, s, sizes[i], offsets[i]) != 0) {
goto done;
}
}
printf(" record layer OK: %d payloads, 1 to %d bytes\n",
(int)(sizeof(sizes) / sizeof(sizes[0])),
sizes[(sizeof(sizes) / sizeof(sizes[0])) - 1]);
ret = 0;
done:
wolfSSL_free(c);
wolfSSL_free(s);
wolfSSL_CTX_free(cctx);
wolfSSL_CTX_free(sctx);
return ret;
}
int xg25_tls13_test(void)
{
/* Three suites, exercising different engines. SHA-256 drives the key
* schedule and transcript hash through the SE, SHA-384 runs them in
* software, and the ChaCha suite covers a record cipher the AES suites
* never reach. */
static const char* suites[] = {
"TLS13-AES128-GCM-SHA256",
"TLS13-AES256-GCM-SHA384",
"TLS13-CHACHA20-POLY1305-SHA256"
};
int i;
int ret;
for (i = 0; i < (int)(sizeof(suites) / sizeof(suites[0])); i++) {
printf(" [%s]\n", suites[i]);
ret = xg25_tls13_one(suites[i]);
if (ret != 0) {
return ret;
}
}
return 0;
}
#endif /* WOLFSSL_XG25_TLS13 */