wolfssl/tests/api/test_tls13_bounds.c

930 lines
35 KiB
C

/* test_tls13_bounds.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
*/
/* MC/DC vectors for src/tls13.c's protocol-version and length/boundary
* decisions (ISO 26262 Part 7, Track B).
*
* Every test here drives a real handshake through the tests/utils.c memio
* transport and mutates one field of one flight, so no WOLFSSL_LOCAL symbol is
* referenced and the file links in a shared build. Each vector is written to
* complete an independence PAIR with the ordinary handshakes the tls13 group
* already runs: for a decision "A && B" the ordinary handshake supplies the
* (A=1, B=0, D=0) row, so the vector here has to supply either (A=1, B=1, D=1)
* or (A=0, D=0) -- a rejection on its own proves nothing.
*/
#include <tests/unit.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
#include <wolfssl/ssl.h>
#include <wolfssl/internal.h>
#include <tests/api/api.h>
#include <tests/utils.h>
#include <tests/api/test_tls13_bounds.h>
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12)
#define TLS13B_CH_BUF_SZ 4096
#define TLS13B_REC_HDR_SZ 5
#define TLS13B_HS_HDR_SZ 4
/* record header (5) + handshake header (4) = offset of legacy_version */
#define TLS13B_LEGACY_OFF (TLS13B_REC_HDR_SZ + TLS13B_HS_HDR_SZ)
/* Copy the first complete TLS record out of a memio direction buffer.
*
* dir 0 is the server's inbox (client -> server), dir 1 the client's inbox.
* A single memio "message" can hold several concatenated records (the server
* writes ServerHello + CCS + the first encrypted flight in one go), so the
* record is sliced out using its own 2-byte length field rather than the
* message size.
*
* Returns 0 on success. */
static int test_tls13b_take_record(struct test_memio_ctx* ctx, int dir,
byte* out, int out_cap, int* out_sz)
{
const char* msg = NULL;
int msg_sz = 0;
int rec_sz;
if (test_memio_get_message(ctx, dir, &msg, &msg_sz, 0) != 0)
return -1;
if (msg_sz < TLS13B_REC_HDR_SZ)
return -1;
rec_sz = TLS13B_REC_HDR_SZ +
(((int)(byte)msg[3] << 8) | (int)(byte)msg[4]);
if (rec_sz > msg_sz || rec_sz > out_cap)
return -1;
XMEMCPY(out, msg, (size_t)rec_sz);
*out_sz = rec_sz;
return 0;
}
/* Overwrite the legacy_version of the handshake message carried by a
* plaintext handshake record. hs_type guards against patching the wrong
* message. Returns 0 on success. */
static int test_tls13b_set_legacy_version(byte* rec, int rec_sz, byte hs_type,
byte major, byte minor)
{
if (rec_sz < TLS13B_LEGACY_OFF + 2)
return -1;
if (rec[0] != handshake)
return -1;
if (rec[TLS13B_REC_HDR_SZ] != hs_type)
return -1;
rec[TLS13B_LEGACY_OFF] = major;
rec[TLS13B_LEGACY_OFF + 1] = minor;
return 0;
}
/* Drive a TLS 1.3 client far enough to emit its ClientHello, rewrite the
* ClientHello's legacy_version to major.minor, replay it to a TLS 1.3-only
* server and require the given error.
*
* All three callers land in DoTls13ClientHello()'s legacy-version block
* (tls13.c ~7620-7665). */
static int test_tls13b_ch_legacy_version(byte major, byte minor, int expErr)
{
EXPECT_DECLS;
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
byte ch[TLS13B_CH_BUF_SZ];
int ch_sz = 0;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
/* Client emits the ClientHello and then blocks on the reply. */
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
WOLFSSL_ERROR_WANT_READ);
ExpectIntEQ(test_tls13b_take_record(&test_ctx, 0, ch, (int)sizeof(ch),
&ch_sz), 0);
ExpectIntEQ(test_tls13b_set_legacy_version(ch, ch_sz, client_hello,
major, minor), 0);
test_memio_clear_buffer(&test_ctx, 0);
if (EXPECT_SUCCESS()) {
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)ch,
ch_sz), 0);
}
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
expErr);
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_s);
return EXPECT_RESULT();
}
/* Run a TLS 1.3 handshake up to the server's first flight, rewrite the
* ServerHello's legacy_version to major.minor, feed the client that single
* record and require the given error.
*
* Lands in DoTls13ServerHello()'s downgrade block (tls13.c ~5432-5470). */
static int test_tls13b_sh_legacy_version(byte major, byte minor, int expErr)
{
EXPECT_DECLS;
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
byte sh[TLS13B_CH_BUF_SZ];
int sh_sz = 0;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
WOLFSSL_ERROR_WANT_READ);
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
WOLFSSL_ERROR_WANT_READ);
ExpectIntEQ(test_tls13b_take_record(&test_ctx, 1, sh, (int)sizeof(sh),
&sh_sz), 0);
ExpectIntEQ(test_tls13b_set_legacy_version(sh, sh_sz, server_hello,
major, minor), 0);
/* Drop the rest of the server flight: only the tampered ServerHello is
* replayed, so the client cannot get past the version check. */
test_memio_clear_buffer(&test_ctx, 1);
if (EXPECT_SUCCESS()) {
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)sh,
sh_sz), 0);
}
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
expErr);
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_s);
return EXPECT_RESULT();
}
/* --- WANT_WRITE resumption harness -----------------------------------------
*
* SendTls13Certificate() only fragments (and only takes its ssl->fragOffset
* resume path) when a send is interrupted part way through a multi-record
* Certificate. test_memio's own simulate_want_write is all-or-nothing from the
* start of the flight, so instead a counting send callback is layered over
* test_memio_write_cb: write number tls13b_ww_at fails with WANT_WRITE once,
* every other write goes through. Sweeping tls13b_ww_at across the whole
* flight interrupts each record in turn, and the handshake is still required
* to complete, so the resume paths are exercised without a rejection vector.
*/
static int tls13b_ww_at = -1;
static int tls13b_ww_n = 0;
static int test_tls13b_send_cb(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
if (tls13b_ww_n++ == tls13b_ww_at)
return WOLFSSL_CBIO_ERR_WANT_WRITE;
return test_memio_write_cb(ssl, buf, sz, ctx);
}
/* Build a memio pair whose server presents a certificate CHAIN (the default
* test_memio_setup loads the leaf only, which leaves certChainSz == 0 and the
* whole chain-walk block in SendTls13Certificate unreachable).
*
* The pre-made ctx_s makes test_memio_setup_ex skip its own certificate load
* AND its IO callback installation, so both are done here. */
static int test_tls13b_setup_chain(struct test_memio_ctx* tc,
WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c, WOLFSSL** ssl_s,
int wantWriteSide)
{
*ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
if (*ctx_s == NULL)
return -1;
if (wolfSSL_CTX_use_PrivateKey_file(*ctx_s, svrKeyFile, CERT_FILETYPE)
!= WOLFSSL_SUCCESS)
return -1;
if (wolfSSL_CTX_use_certificate_chain_file(*ctx_s, svrCertFile)
!= WOLFSSL_SUCCESS)
return -1;
if (wolfSSL_CTX_load_verify_locations(*ctx_s, caCertFile, 0)
!= WOLFSSL_SUCCESS)
return -1;
wolfSSL_SetIORecv(*ctx_s, test_memio_read_cb);
wolfSSL_SetIOSend(*ctx_s, wantWriteSide == 0 ? test_tls13b_send_cb
: test_memio_write_cb);
if (test_memio_setup_ex(tc, ctx_c, ctx_s, ssl_c, ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
NULL, 0, NULL, 0, NULL, 0) != 0)
return -1;
/* Per-SSL, not per-CTX: wolfSSL_new() has already copied ctx_c's
* callbacks into ssl_c by this point, so setting it on the CTX would be
* a no-op for this connection. */
if (wantWriteSide == 1)
wolfSSL_SSLSetIOSend(*ssl_c, test_tls13b_send_cb);
return 0;
}
/* One handshake with the server's write number 'at' interrupted by
* WANT_WRITE. Small max_fragment_length forces the Certificate across several
* records so at least one 'at' lands inside it. */
static int test_tls13b_server_frag_round(int at)
{
EXPECT_DECLS;
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
tls13b_ww_at = at;
tls13b_ww_n = 0;
ExpectIntEQ(test_tls13b_setup_chain(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
&ssl_s, 0), 0);
#ifdef HAVE_MAX_FRAGMENT
ExpectIntEQ(wolfSSL_UseMaxFragment(ssl_c, WOLFSSL_MFL_2_9),
WOLFSSL_SUCCESS);
#endif
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 64, NULL), 0);
tls13b_ww_at = -1;
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_s);
return EXPECT_RESULT();
}
/* Same, with the client holding a certificate chain and the interrupt on the
* client's writes, so the client-side resume path in wolfSSL_connect_TLSv13()
* runs too. */
static int test_tls13b_client_frag_round(int at)
{
EXPECT_DECLS;
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
tls13b_ww_at = at;
tls13b_ww_n = 0;
ExpectIntEQ(test_tls13b_setup_chain(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
&ssl_s, 1), 0);
/* Ask for the client's certificate so the client also fragments one.
* client-cert.pem is its own issuer, so it has to be added to the
* server's store for the verify to succeed. */
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, 0),
WOLFSSL_SUCCESS);
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_c, cliCertFile),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, CERT_FILETYPE),
WOLFSSL_SUCCESS);
#ifdef HAVE_MAX_FRAGMENT
ExpectIntEQ(wolfSSL_UseMaxFragment(ssl_c, WOLFSSL_MFL_2_9),
WOLFSSL_SUCCESS);
#endif
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 64, NULL), 0);
tls13b_ww_at = -1;
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_s);
return EXPECT_RESULT();
}
/* Rewrite a ServerHello record so it carries a present-but-EMPTY extensions
* block. That is the only shape that reaches DoTls13ServerHello()'s
* post-negotiation code with args->totalExtSz == 0: a ServerHello with no
* extensions FIELD at all returns earlier, at the truncated-length branch.
* Returns the new record length. */
static int test_tls13b_sh_empty_exts(byte* rec, int rec_sz)
{
int off, sessIdSz, bodySz;
if (rec_sz < TLS13B_LEGACY_OFF + 2 + RAN_LEN + 1)
return -1;
if (rec[0] != handshake || rec[TLS13B_REC_HDR_SZ] != server_hello)
return -1;
off = TLS13B_LEGACY_OFF + OPAQUE16_LEN + RAN_LEN;
sessIdSz = rec[off];
off += 1 + sessIdSz;
off += OPAQUE16_LEN + OPAQUE8_LEN; /* cipher suite + compression */
if (off + OPAQUE16_LEN > rec_sz)
return -1;
rec[off] = 0;
rec[off + 1] = 0;
off += OPAQUE16_LEN;
bodySz = off - (TLS13B_REC_HDR_SZ + TLS13B_HS_HDR_SZ);
rec[6] = (byte)(bodySz >> 16);
rec[7] = (byte)(bodySz >> 8);
rec[8] = (byte)bodySz;
rec[3] = (byte)((off - TLS13B_REC_HDR_SZ) >> 8);
rec[4] = (byte)(off - TLS13B_REC_HDR_SZ);
return off;
}
/* Find the first extension of the given type in a ClientHello record and
* return the offset of its body, or -1. */
static int test_tls13b_ch_find_ext(const byte* rec, int rec_sz, word16 type,
int* bodySz)
{
int off, sessIdSz, suiteSz, compSz, extEnd;
int extTotal;
if (rec_sz < TLS13B_LEGACY_OFF + 2 + RAN_LEN + 1)
return -1;
if (rec[0] != handshake || rec[TLS13B_REC_HDR_SZ] != client_hello)
return -1;
off = TLS13B_LEGACY_OFF + OPAQUE16_LEN + RAN_LEN;
sessIdSz = rec[off];
off += 1 + sessIdSz;
if (off + OPAQUE16_LEN > rec_sz)
return -1;
suiteSz = ((int)rec[off] << 8) | rec[off + 1];
off += OPAQUE16_LEN + suiteSz;
if (off + 1 > rec_sz)
return -1;
compSz = rec[off];
off += 1 + compSz;
if (off + OPAQUE16_LEN > rec_sz)
return -1;
extTotal = ((int)rec[off] << 8) | rec[off + 1];
off += OPAQUE16_LEN;
extEnd = off + extTotal;
if (extEnd > rec_sz)
return -1;
while (off + 4 <= extEnd) {
word16 t = (word16)(((word16)rec[off] << 8) | rec[off + 1]);
int l = ((int)rec[off + 2] << 8) | rec[off + 3];
if (off + 4 + l > extEnd)
return -1;
if (t == type) {
*bodySz = l;
return off + 4;
}
off += 4 + l;
}
return -1;
}
#endif /* guards */
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
(!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \
defined(HAVE_ED448))
/* A mutually authenticated TLS 1.3 handshake with a chosen key type on both
* ends. DoTls13CertificateVerify()'s peer-key / peerSigAlgo dispatch has one
* arm per algorithm and the group only ever ran the RSA one, so each arm's
* operands sat on a single row. */
/* The ECC, Ed25519 and Ed448 client certificates in certs/ are self-signed,
* so cliCa is the client certificate itself rather than a separate CA. */
static int test_tls13b_mutual_auth_round(const char* srvCa,
const char* srvCert, const char* srvKey,
const char* cliCa, const char* cliCert, const char* cliKey)
{
EXPECT_DECLS;
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, srvCa, 0),
WOLFSSL_SUCCESS);
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_s, srvCert),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, srvKey, CERT_FILETYPE),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCa, 0),
WOLFSSL_SUCCESS);
if (EXPECT_SUCCESS()) {
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_PEER |
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
/* Pre-made CTXs make test_memio_setup_ex skip its own IO callback
* install as well as its cert load. */
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
}
ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
NULL, 0, NULL, 0, NULL, 0), 0);
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_c, cliCert),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKey, CERT_FILETYPE),
WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 16, NULL), 0);
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_s);
return EXPECT_RESULT();
}
#endif /* guards */
/* tls13.c:7645 - "args->pv.major == SSLv3_MAJOR && args->pv.minor >=
* TLSv1_3_MINOR". A legacy_version of 0x0304 makes BOTH operands true, which
* is the decision-true row RFC 8446 4.2.1 lets a server abort on. It is the
* accepting partner for cond 0 (paired with the 0x0403 vector below) and for
* cond 1 (paired with every ordinary ClientHello, whose 0x0303 makes cond 1
* false). */
int test_tls13_ch_legacy_version_is_tls13(void)
{
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12) && \
!defined(WOLFSSL_ALLOW_BAD_TLS_LEGACY_VERSION)
return test_tls13b_ch_legacy_version(SSLv3_MAJOR, TLSv1_3_MINOR,
WC_NO_ERR_TRACE(VERSION_ERROR));
#else
return TEST_SKIPPED;
#endif
}
/* tls13.c:7645 cond 0 false row, and tls13.c:7652 cond 0 true row.
* A legacy_version major of 4 is > SSLv3_MAJOR, so :7645 short-circuits false
* on its first operand and :7652's first operand is true on its own. The
* server has downgrade disabled, so the forced downgrade is refused with
* VERSION_ERROR. */
int test_tls13_ch_legacy_version_major_above_ssl3(void)
{
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12)
return test_tls13b_ch_legacy_version(SSLv3_MAJOR + 1, TLSv1_2_MINOR,
WC_NO_ERR_TRACE(VERSION_ERROR));
#else
return TEST_SKIPPED;
#endif
}
/* tls13.c:7660 cond 1 true row - "args->pv.minor < TLSv1_2_MINOR" in the
* else-if that catches a pre-TLS1.2 legacy_version. 0x0301 (TLS 1.0) falls
* through :7645 and :7652 and makes this operand true; the ordinary
* ClientHello's 0x0303 is the false row. */
int test_tls13_ch_legacy_version_below_tls12(void)
{
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12)
return test_tls13b_ch_legacy_version(SSLv3_MAJOR, TLSv1_MINOR,
WC_NO_ERR_TRACE(VERSION_ERROR));
#else
return TEST_SKIPPED;
#endif
}
/* tls13.c:5436 - "args->pv.major == ssl->version.major && args->pv.minor <
* TLSv1_2_MINOR" in DoTls13ServerHello(). A ServerHello legacy_version of
* 0x0302 makes both operands true (the decision-true row that cond 0 and
* cond 1 both need); the client has downgrade disabled so it then fails
* :5466 cond 1 ("args->pv.minor != tls12minor"), whose false row every
* ordinary handshake supplies. */
int test_tls13_sh_legacy_version_below_tls12(void)
{
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12)
return test_tls13b_sh_legacy_version(SSLv3_MAJOR, TLSv1_1_MINOR,
WC_NO_ERR_TRACE(VERSION_ERROR));
#else
return TEST_SKIPPED;
#endif
}
/* tls13.c:5436 cond 0 false row and :5466 cond 0 true row. A ServerHello
* legacy_version major of 4 differs from the client's, so :5436
* short-circuits false without evaluating its second operand and :5466's
* first operand is true on its own. */
int test_tls13_sh_legacy_version_major_mismatch(void)
{
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12)
return test_tls13b_sh_legacy_version(SSLv3_MAJOR + 1, TLSv1_2_MINOR,
WC_NO_ERR_TRACE(VERSION_ERROR));
#else
return TEST_SKIPPED;
#endif
}
/* Interrupt every write of the server's flight in turn with WANT_WRITE while
* it sends a fragmented Certificate built from a real chain.
*
* Drives SendTls13Certificate()'s resume block: :10019 (certChainSz > 0 &&
* fragOffset >= certSz + extSz[0]), :10061 cond 1 (the fragment loop leaving
* with length still > 0 because SendBuffered() answered WANT_WRITE, which is
* the row an uninterrupted send can never produce), :10143, :10156, :10187 and
* :9655, plus wolfSSL_accept_TLSv13()'s :16693 buffered-fragment resume.
* Every round must still complete the handshake, so each vector has its
* accepting partner in the same run. */
int test_tls13_server_cert_fragment_want_write(void)
{
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12)
EXPECT_DECLS;
int at;
for (at = 0; at < 16 && EXPECT_SUCCESS(); at++)
ExpectIntEQ(test_tls13b_server_frag_round(at), TEST_SUCCESS);
return EXPECT_RESULT();
#else
return TEST_SKIPPED;
#endif
}
/* Client-side counterpart: the client sends its own fragmented Certificate
* and its writes are interrupted in turn, which drives
* wolfSSL_connect_TLSv13()'s :15347 buffered-fragment resume. */
int test_tls13_client_cert_fragment_want_write(void)
{
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12)
EXPECT_DECLS;
int at;
for (at = 0; at < 12 && EXPECT_SUCCESS(); at++)
ExpectIntEQ(test_tls13b_client_frag_round(at), TEST_SUCCESS);
return EXPECT_RESULT();
#else
return TEST_SKIPPED;
#endif
}
/* tls13.c:5719 cond 0 - "args->totalExtSz > 0" in DoTls13ServerHello().
* Every ordinary ServerHello supplies the (both operands true, decision true)
* row; this vector supplies the missing (cond 0 false) row by handing a
* downgrade-capable client a ServerHello whose extensions block is present but
* empty. The client then takes the !foundVersion downgrade path and arrives at
* :5719 with totalExtSz == 0. The handshake cannot complete (no key_share), so
* only the failure is asserted. */
int test_tls13_sh_empty_extensions_block(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12)
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
byte sh[TLS13B_CH_BUF_SZ];
int sh_sz = 0;
int new_sz = -1;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
/* SSLv23 client: downgrade enabled, which the !foundVersion branch needs. */
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfSSLv23_client_method, wolfTLSv1_3_server_method), 0);
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
ExpectIntEQ(test_tls13b_take_record(&test_ctx, 1, sh, (int)sizeof(sh),
&sh_sz), 0);
if (EXPECT_SUCCESS())
new_sz = test_tls13b_sh_empty_exts(sh, sh_sz);
ExpectIntGT(new_sz, 0);
test_memio_clear_buffer(&test_ctx, 1);
if (EXPECT_SUCCESS()) {
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)sh,
new_sz), 0);
}
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
/* tls13.c:7425 cond 1 - "!IsAtLeastTLSv1_3(ssl->version)" in
* DoTls13SupportedVersions(). Ordinary handshakes give the (foundVersion set,
* version still 1.3, decision false) row. This vector rewrites the single
* version in the ClientHello's supported_versions extension from 0x0304 to
* 0x0303 - same length, so no other field moves - and hands it to a
* downgrade-capable server, which negotiates TLS 1.2 and makes the operand
* true with foundVersion still set. */
int test_tls13_ch_supported_versions_tls12_only(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
!defined(WOLFSSL_NO_TLS12)
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
byte ch[TLS13B_CH_BUF_SZ];
int ch_sz = 0;
int svOff = -1, svSz = 0, i;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfSSLv23_server_method), 0);
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
ExpectIntEQ(test_tls13b_take_record(&test_ctx, 0, ch, (int)sizeof(ch),
&ch_sz), 0);
if (EXPECT_SUCCESS())
svOff = test_tls13b_ch_find_ext(ch, ch_sz, TLSX_SUPPORTED_VERSIONS,
&svSz);
ExpectIntGT(svOff, 0);
/* body is: 1-byte list length, then <major,minor> pairs */
ExpectIntGE(svSz, 3);
if (EXPECT_SUCCESS()) {
for (i = svOff + 1; i + 1 < svOff + svSz; i += 2) {
if (ch[i] == SSLv3_MAJOR && ch[i + 1] == TLSv1_3_MINOR)
ch[i + 1] = TLSv1_2_MINOR;
}
}
test_memio_clear_buffer(&test_ctx, 0);
if (EXPECT_SUCCESS()) {
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)ch,
ch_sz), 0);
}
/* The server downgrades; the TLS 1.3-only client cannot follow, so the
* handshake must not complete. */
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
/* A complete Encrypted ClientHello handshake in the tls13 group. The group's
* existing tests never build an ECHConfig, so every ssl->ctx->echConfigs and
* echX operand in DoTls13ClientHello(), DoTls13HandShakeMsgType() and
* SendTls13ClientHello() sits on its "no ECH" row only. This supplies the
* accepting row for all of them; the ordinary handshakes supply the other. */
int test_tls13_ech_accepted_handshake(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(HAVE_SNI) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
static const char pubName[] = "ech-public-name.com";
static const char privName[] = "ech-private-name.com";
byte configs[512];
word32 configsLen = (word32)sizeof(configs);
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
/* The handshake reads ssl->ctx->echConfigs, so generating on the CTX
* after wolfSSL_new() is still in time for this connection. */
ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(ctx_s, pubName, 0, 0, 0),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(ctx_s, configs, &configsLen),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_SetEchConfigs(ssl_c, configs, configsLen),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, privName,
(word16)XSTRLEN(privName)), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSNI(ssl_s, WOLFSSL_SNI_HOST_NAME, privName,
(word16)XSTRLEN(privName)), WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 16, NULL), 0);
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
/* Same handshake with the ECHConfig's public key corrupted, so the server
* cannot open the outer ClientHello and answers with retry_configs. That is
* the ECH-rejected arm of the same decisions - notably EchCheckAcceptance()
* and the acceptance-confirmation compare in DoTls13HandShakeMsgType(). */
int test_tls13_ech_rejected_handshake(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(HAVE_SNI) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
static const char pubName[] = "ech-public-name.com";
static const char privName[] = "ech-private-name.com";
byte configs[512];
word32 configsLen = (word32)sizeof(configs);
word16 idx;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(ctx_s, pubName, 0, 0, 0),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(ctx_s, configs, &configsLen),
WOLFSSL_SUCCESS);
if (EXPECT_SUCCESS()) {
/* skip list length, version, length, config id, kem id and public
* key length to land on the first byte of the public key */
idx = OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE8_LEN +
OPAQUE16_LEN + OPAQUE16_LEN;
ExpectIntLT((word32)idx, configsLen);
if (EXPECT_SUCCESS())
configs[idx] ^= 0xFF;
}
ExpectIntEQ(wolfSSL_SetEchConfigs(ssl_c, configs, configsLen),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, privName,
(word16)XSTRLEN(privName)), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSNI(ssl_s, WOLFSSL_SNI_HOST_NAME, pubName,
(word16)XSTRLEN(pubName)), WOLFSSL_SUCCESS);
/* The server cannot open the outer ClientHello, so it completes the
* handshake against the public name and returns retry_configs; RFC 9849
* 6.1.7 then makes the client abort with ECH_REQUIRED_E. Both sides run
* their full ECH code paths first, which is the point of the vector. */
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
WC_NO_ERR_TRACE(ECH_REQUIRED_E));
wolfSSL_free(ssl_c);
wolfSSL_CTX_free(ctx_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
/* ECDSA on both ends: drives DoTls13CertificateVerify()'s
* peerEccDsaKey / ecc_dsa_sa_algo arms. */
int test_tls13_mutual_auth_ecdsa(void)
{
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECC) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
return test_tls13b_mutual_auth_round(caEccCertFile, eccCertFile,
eccKeyFile, cliEccCertFile, cliEccCertFile, cliEccKeyFile);
#else
return TEST_SKIPPED;
#endif
}
/* Ed25519 on both ends: drives the peerEd25519Key / ed25519_sa_algo arms. */
int test_tls13_mutual_auth_ed25519(void)
{
#if defined(WOLFSSL_TLS13) && defined(HAVE_ED25519) && \
defined(HAVE_ED25519_SIGN) && defined(HAVE_ED25519_VERIFY) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_ED25519_CLIENT_AUTH) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
return test_tls13b_mutual_auth_round(caEdCertFile, edCertFile,
edKeyFile, cliEdCertFile, cliEdCertFile, cliEdKeyFile);
#else
return TEST_SKIPPED;
#endif
}
/* Ed448 on both ends: drives the peerEd448Key / ed448_sa_algo arms. */
int test_tls13_mutual_auth_ed448(void)
{
#if defined(WOLFSSL_TLS13) && defined(HAVE_ED448) && \
defined(HAVE_ED448_SIGN) && defined(HAVE_ED448_VERIFY) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_ED448_CLIENT_AUTH) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
return test_tls13b_mutual_auth_round(caEd448CertFile, ed448CertFile,
ed448KeyFile, cliEd448CertFile, cliEd448CertFile, cliEd448KeyFile);
#else
return TEST_SKIPPED;
#endif
}
/* RSA on both ends, mutually authenticated. The group already runs one-sided
* RSA handshakes; this adds the client-authenticating rows for the RSA arm of
* the same dispatch. */
int test_tls13_mutual_auth_rsa(void)
{
#if defined(WOLFSSL_TLS13) && !defined(NO_RSA) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
return test_tls13b_mutual_auth_round(caCertFile, svrCertFile,
svrKeyFile, cliCertFile, cliCertFile, cliKeyFile);
#else
return TEST_SKIPPED;
#endif
}