mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
17320 lines
676 KiB
C
17320 lines
676 KiB
C
/* test_tls13.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
|
|
*/
|
|
|
|
#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.h>
|
|
|
|
#if defined(WOLFSSL_SEND_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER)
|
|
#ifdef WC_SHA384_DIGEST_SIZE
|
|
WC_MAYBE_UNUSED static byte fixedKey[WC_SHA384_DIGEST_SIZE] = { 0, };
|
|
#else
|
|
WC_MAYBE_UNUSED static byte fixedKey[WC_SHA256_DIGEST_SIZE] = { 0, };
|
|
#endif
|
|
#endif
|
|
#ifdef WOLFSSL_EARLY_DATA
|
|
static const char earlyData[] = "Early Data";
|
|
static char earlyDataBuffer[1];
|
|
#endif
|
|
|
|
int test_tls13_apis(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef WOLFSSL_TLS13
|
|
#if defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_ECC) && \
|
|
(!defined(NO_WOLFSSL_SERVER) || !defined(NO_WOLFSSL_CLIENT))
|
|
int ret;
|
|
#endif
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
WOLFSSL_CTX* clientTls12Ctx = NULL;
|
|
WOLFSSL* clientTls12Ssl = NULL;
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
WOLFSSL_CTX* serverTls12Ctx = NULL;
|
|
WOLFSSL* serverTls12Ssl = NULL;
|
|
#endif
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
WOLFSSL_CTX* clientCtx = NULL;
|
|
WOLFSSL* clientSsl = NULL;
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
WOLFSSL_CTX* serverCtx = NULL;
|
|
WOLFSSL* serverSsl = NULL;
|
|
#if !defined(NO_CERTS) && !defined(NO_FILESYSTEM)
|
|
#ifndef NO_RSA
|
|
const char* ourCert = svrCertFile;
|
|
const char* ourKey = svrKeyFile;
|
|
#elif defined(HAVE_ECC)
|
|
const char* ourCert = eccCertFile;
|
|
const char* ourKey = eccKeyFile;
|
|
#elif defined(HAVE_ED25519)
|
|
const char* ourCert = edCertFile;
|
|
const char* ourKey = edKeyFile;
|
|
#elif defined(HAVE_ED448)
|
|
const char* ourCert = ed448CertFile;
|
|
const char* ourKey = ed448KeyFile;
|
|
/* The CERT_FILES gates, not the signing ones: only a certificate on the context
|
|
* is needed here, so the key load below is allowed to fail. */
|
|
#elif defined(TEST_HAVE_MLDSA_CERT_FILES)
|
|
const char* ourCert = mldsaCertFile;
|
|
const char* ourKey = mldsaKeyFile;
|
|
#elif defined(TEST_HAVE_SLHDSA_CERT_FILES)
|
|
const char* ourCert = slhdsaCertFile;
|
|
const char* ourKey = slhdsaKeyFile;
|
|
#else
|
|
/* No certificate to load. The loads below ignore the return value and a
|
|
* NULL filename fails cleanly rather than being dereferenced. */
|
|
const char* ourCert = NULL;
|
|
const char* ourKey = NULL;
|
|
#endif
|
|
#endif
|
|
#endif
|
|
int required;
|
|
#ifdef WOLFSSL_EARLY_DATA
|
|
int outSz;
|
|
#endif
|
|
#if defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES)
|
|
int groups[2] = { WOLFSSL_ECC_SECP256R1,
|
|
#if defined(WOLFSSL_HAVE_MLKEM) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
|
|
#ifdef WOLFSSL_MLKEM_KYBER
|
|
#ifndef WOLFSSL_NO_KYBER512
|
|
WOLFSSL_KYBER_LEVEL1
|
|
#elif !defined(WOLFSSL_NO_KYBER768)
|
|
WOLFSSL_KYBER_LEVEL3
|
|
#else
|
|
WOLFSSL_KYBER_LEVEL5
|
|
#endif
|
|
#elif !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
|
|
#ifndef WOLFSSL_NO_ML_KEM_512
|
|
WOLFSSL_ML_KEM_512
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_768)
|
|
WOLFSSL_ML_KEM_768
|
|
#else
|
|
WOLFSSL_ML_KEM_1024
|
|
#endif
|
|
#else
|
|
#ifndef WOLFSSL_NO_ML_KEM_768
|
|
WOLFSSL_SECP256R1MLKEM768
|
|
#else
|
|
WOLFSSL_ECC_SECP256R1
|
|
#endif
|
|
#endif
|
|
#else
|
|
WOLFSSL_ECC_SECP256R1
|
|
#endif
|
|
};
|
|
#if !defined(NO_WOLFSSL_SERVER) || !defined(NO_WOLFSSL_CLIENT)
|
|
int bad_groups[2] = { 0xDEAD, 0xBEEF };
|
|
#endif /* !NO_WOLFSSL_SERVER || !NO_WOLFSSL_CLIENT */
|
|
int numGroups = 2;
|
|
#if defined(OPENSSL_EXTRA) && !defined(NO_WOLFSSL_CLIENT)
|
|
int too_many_groups[WOLFSSL_MAX_GROUP_COUNT + 1];
|
|
#endif
|
|
#endif
|
|
#if defined(OPENSSL_EXTRA) && defined(HAVE_ECC)
|
|
char groupList[] =
|
|
#ifdef HAVE_CURVE25519
|
|
"X25519:"
|
|
#endif
|
|
#ifdef HAVE_CURVE448
|
|
"X448:"
|
|
#endif
|
|
#ifndef NO_ECC_SECP
|
|
#if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 521
|
|
"P-521:secp521r1:"
|
|
#endif
|
|
#if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384
|
|
"P-384:secp384r1:"
|
|
#endif
|
|
#if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256
|
|
"P-256:secp256r1"
|
|
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_MALLOC) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
|
|
#ifdef WOLFSSL_MLKEM_KYBER
|
|
#ifndef WOLFSSL_NO_KYBER512
|
|
":P256_KYBER_LEVEL1"
|
|
#elif !defined(WOLFSSL_NO_KYBER768)
|
|
":P256_KYBER_LEVEL3"
|
|
#elif !defined(WOLFSSL_NO_KYBER1024)
|
|
":P256_KYBER_LEVEL5"
|
|
#endif
|
|
#else
|
|
#if !defined(WOLFSSL_NO_ML_KEM_512) && defined(WOLFSSL_EXTRA_PQC_HYBRIDS)
|
|
":SecP256r1MLKEM512"
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_768) && defined(WOLFSSL_PQC_HYBRIDS)
|
|
":SecP256r1MLKEM768"
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_1024) && defined(WOLFSSL_PQC_HYBRIDS)
|
|
":SecP384r1MLKEM1024"
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_1024) && \
|
|
!defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
|
|
":ML_KEM_1024"
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_768) && \
|
|
!defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
|
|
":ML_KEM_768"
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif /* !defined(NO_ECC_SECP) */
|
|
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_MALLOC) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
|
|
#ifdef WOLFSSL_MLKEM_KYBER
|
|
#ifndef WOLFSSL_NO_KYBER512
|
|
":KYBER_LEVEL1"
|
|
#elif !defined(WOLFSSL_NO_KYBER768)
|
|
":KYBER_LEVEL3"
|
|
#elif !defined(WOLFSSL_NO_KYBER1024)
|
|
":KYBER_LEVEL5"
|
|
#endif
|
|
#elif !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
|
|
#if !defined(WOLFSSL_NO_ML_KEM_512)
|
|
":ML_KEM_512"
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_768)
|
|
":ML_KEM_768"
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_1024)
|
|
":ML_KEM_1024"
|
|
#endif
|
|
#endif
|
|
#endif
|
|
"";
|
|
#endif /* defined(OPENSSL_EXTRA) && defined(HAVE_ECC) */
|
|
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_MALLOC) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
(!defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) || \
|
|
(defined(HAVE_CURVE25519) && !defined(WOLFSSL_NO_ML_KEM_768)) || \
|
|
(defined(HAVE_ECC) && !defined(WOLFSSL_NO_ML_KEM_768)))
|
|
int mlkemLevel;
|
|
#endif
|
|
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
clientTls12Ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method());
|
|
clientTls12Ssl = wolfSSL_new(clientTls12Ctx);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
serverTls12Ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method());
|
|
#if !defined(NO_CERTS)
|
|
#if !defined(NO_FILESYSTEM)
|
|
wolfSSL_CTX_use_certificate_chain_file(serverTls12Ctx, ourCert);
|
|
wolfSSL_CTX_use_PrivateKey_file(serverTls12Ctx, ourKey,
|
|
CERT_FILETYPE);
|
|
#elif defined(USE_CERT_BUFFERS_2048)
|
|
wolfSSL_CTX_use_certificate_chain_buffer_format(serverTls12Ctx,
|
|
server_cert_der_2048, sizeof_server_cert_der_2048,
|
|
WOLFSSL_FILETYPE_ASN1);
|
|
wolfSSL_CTX_use_PrivateKey_buffer(serverTls12Ctx, server_key_der_2048,
|
|
sizeof_server_key_der_2048, WOLFSSL_FILETYPE_ASN1);
|
|
#elif defined(USE_CERT_BUFFERS_256)
|
|
wolfSSL_CTX_use_certificate_chain_buffer_format(serverTls12Ctx,
|
|
serv_ecc_der_256, sizeof_serv_ecc_der_256, WOLFSSL_FILETYPE_ASN1);
|
|
wolfSSL_CTX_use_PrivateKey_buffer(serverTls12Ctx, ecc_key_der_256,
|
|
sizeof_ecc_key_der_256, WOLFSSL_FILETYPE_ASN1);
|
|
#endif
|
|
#endif
|
|
serverTls12Ssl = wolfSSL_new(serverTls12Ctx);
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
clientCtx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
|
|
clientSsl = wolfSSL_new(clientCtx);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
serverCtx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
|
|
#if !defined(NO_CERTS)
|
|
/* ignore load failures, since we just need the server to have a cert set */
|
|
#if !defined(NO_FILESYSTEM)
|
|
wolfSSL_CTX_use_certificate_chain_file(serverCtx, ourCert);
|
|
wolfSSL_CTX_use_PrivateKey_file(serverCtx, ourKey, CERT_FILETYPE);
|
|
#elif defined(USE_CERT_BUFFERS_2048)
|
|
wolfSSL_CTX_use_certificate_chain_buffer_format(serverCtx,
|
|
server_cert_der_2048, sizeof_server_cert_der_2048,
|
|
WOLFSSL_FILETYPE_ASN1);
|
|
wolfSSL_CTX_use_PrivateKey_buffer(serverCtx, server_key_der_2048,
|
|
sizeof_server_key_der_2048, WOLFSSL_FILETYPE_ASN1);
|
|
#elif defined(USE_CERT_BUFFERS_256)
|
|
wolfSSL_CTX_use_certificate_chain_buffer_format(serverCtx, serv_ecc_der_256,
|
|
sizeof_serv_ecc_der_256, WOLFSSL_FILETYPE_ASN1);
|
|
wolfSSL_CTX_use_PrivateKey_buffer(serverCtx, ecc_key_der_256,
|
|
sizeof_ecc_key_der_256, WOLFSSL_FILETYPE_ASN1);
|
|
#endif
|
|
#endif
|
|
serverSsl = wolfSSL_new(serverCtx);
|
|
ExpectNotNull(serverSsl);
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_SEND_HRR_COOKIE
|
|
ExpectIntEQ(wolfSSL_send_hrr_cookie(NULL, NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_send_hrr_cookie(clientSsl, NULL, 0),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_enable_cookie(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_disable_cookie(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_enable_cookie(clientSsl), WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
ExpectIntEQ(wolfSSL_disable_cookie(clientSsl), WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_send_hrr_cookie(serverTls12Ssl, NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_enable_cookie(serverTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_disable_cookie(serverTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_enable_cookie(serverSsl), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(serverSsl->options.sendCookie, 1);
|
|
ExpectNotNull(serverSsl->buffers.tls13CookieSecret.buffer);
|
|
ExpectIntEQ(wolfSSL_send_hrr_cookie(serverSsl, NULL, 0), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_send_hrr_cookie(serverSsl, fixedKey, sizeof(fixedKey)),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_enable_cookie(serverSsl), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(serverSsl->buffers.tls13CookieSecret.length, sizeof(fixedKey));
|
|
ExpectBufEQ(serverSsl->buffers.tls13CookieSecret.buffer, fixedKey,
|
|
sizeof(fixedKey));
|
|
ExpectIntEQ(wolfSSL_disable_cookie(serverSsl), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(serverSsl->options.sendCookie, 0);
|
|
ExpectNull(serverSsl->buffers.tls13CookieSecret.buffer);
|
|
ExpectIntEQ(serverSsl->buffers.tls13CookieSecret.length, 0);
|
|
ExpectIntEQ(wolfSSL_disable_cookie(serverSsl), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_enable_cookie(serverSsl), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(serverSsl->options.sendCookie, 1);
|
|
ExpectNotNull(serverSsl->buffers.tls13CookieSecret.buffer);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef HAVE_SUPPORTED_CURVES
|
|
#ifdef HAVE_ECC
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(NULL, WOLFSSL_ECC_SECP256R1),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
do {
|
|
ret = wolfSSL_UseKeyShare(serverSsl, WOLFSSL_ECC_SECP256R1);
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
|
|
wolfSSL_AsyncPoll(serverSsl, WOLF_POLL_FLAG_CHECK_HW);
|
|
#endif
|
|
}
|
|
while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
|
|
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
do {
|
|
ret = wolfSSL_UseKeyShare(clientTls12Ssl, WOLFSSL_ECC_SECP256R1);
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
|
|
wolfSSL_AsyncPoll(clientTls12Ssl, WOLF_POLL_FLAG_CHECK_HW);
|
|
#endif
|
|
}
|
|
while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
|
|
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
|
|
#endif
|
|
do {
|
|
ret = wolfSSL_UseKeyShare(clientSsl, WOLFSSL_ECC_SECP256R1);
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
|
|
wolfSSL_AsyncPoll(clientSsl, WOLF_POLL_FLAG_CHECK_HW);
|
|
#endif
|
|
}
|
|
while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
|
|
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
|
|
#endif
|
|
#elif defined(HAVE_CURVE25519)
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(NULL, WOLFSSL_ECC_X25519),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(serverSsl, WOLFSSL_ECC_X25519),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(clientTls12Ssl, WOLFSSL_ECC_X25519),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(clientSsl, WOLFSSL_ECC_X25519),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
#elif defined(HAVE_CURVE448)
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(NULL, WOLFSSL_ECC_X448),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(serverSsl, WOLFSSL_ECC_X448),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(clientTls12Ssl, WOLFSSL_ECC_X448),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(clientSsl, WOLFSSL_ECC_X448),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
#else
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(NULL, WOLFSSL_ECC_SECP256R1),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(clientTls12Ssl, WOLFSSL_ECC_SECP256R1),
|
|
WC_NO_ERR_TRACE(NOT_COMPILED_IN));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(clientSsl, WOLFSSL_ECC_SECP256R1),
|
|
WC_NO_ERR_TRACE(NOT_COMPILED_IN));
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_MALLOC) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
(!defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) || \
|
|
(defined(HAVE_CURVE25519) && !defined(WOLFSSL_NO_ML_KEM_768)) || \
|
|
(defined(HAVE_ECC) && !defined(WOLFSSL_NO_ML_KEM_768)))
|
|
#ifndef WOLFSSL_NO_ML_KEM
|
|
#ifndef WOLFSSL_TLS_NO_MLKEM_STANDALONE
|
|
#ifndef WOLFSSL_NO_ML_KEM_768
|
|
mlkemLevel = WOLFSSL_ML_KEM_768;
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_1024)
|
|
mlkemLevel = WOLFSSL_ML_KEM_1024;
|
|
#else
|
|
mlkemLevel = WOLFSSL_ML_KEM_512;
|
|
#endif
|
|
#else
|
|
#if defined(HAVE_CURVE25519) && !defined(WOLFSSL_NO_ML_KEM_768)
|
|
mlkemLevel = WOLFSSL_X25519MLKEM768;
|
|
#elif defined(HAVE_ECC) && !defined(WOLFSSL_NO_ML_KEM_768)
|
|
mlkemLevel = WOLFSSL_SECP256R1MLKEM768;
|
|
#endif
|
|
#endif
|
|
#else
|
|
#ifndef WOLFSSL_NO_KYBER768
|
|
mlkemLevel = WOLFSSL_KYBER_LEVEL3;
|
|
#elif !defined(WOLFSSL_NO_KYBER1024)
|
|
mlkemLevel = WOLFSSL_KYBER_LEVEL5;
|
|
#else
|
|
mlkemLevel = WOLFSSL_KYBER_LEVEL1;
|
|
#endif
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(NULL, mlkemLevel),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(serverSsl, mlkemLevel),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(clientTls12Ssl, mlkemLevel),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(clientSsl, mlkemLevel),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(serverSsl), WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(clientTls12Ssl), WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(clientSsl), WOLFSSL_SUCCESS);
|
|
#endif
|
|
#endif /* HAVE_SUPPORTED_CURVES */
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_no_ticket_TLSv13(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_CTX_no_ticket_TLSv13(clientCtx),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_CTX_no_ticket_TLSv13(serverTls12Ctx),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_no_ticket_TLSv13(serverCtx), 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_no_ticket_TLSv13(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_no_ticket_TLSv13(clientSsl),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_no_ticket_TLSv13(serverTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_no_ticket_TLSv13(serverSsl), 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_no_dhe_psk(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_CTX_no_dhe_psk(clientTls12Ctx),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_no_dhe_psk(clientCtx), 0);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_CTX_no_dhe_psk(serverCtx), 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(clientTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(clientSsl), 0);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(serverSsl), 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_update_keys(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_update_keys(clientTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_update_keys(clientSsl),
|
|
WC_NO_ERR_TRACE(BUILD_MSG_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_update_keys(serverSsl),
|
|
WC_NO_ERR_TRACE(BUILD_MSG_ERROR));
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_key_update_response(NULL, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_key_update_response(NULL, &required),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_key_update_response(clientTls12Ssl, &required),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_key_update_response(clientSsl, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_key_update_response(serverSsl, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
|
|
/* The accepting vector for the whole guard:
|
|
* required == NULL || ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)
|
|
* Every case above leaves at least one operand true, so none of the three
|
|
* operands had a partner vector with the decision false. A live TLS 1.3
|
|
* object with a non-NULL out-parameter supplies it. */
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_key_update_response(clientSsl, &required), 0);
|
|
#endif
|
|
|
|
/* The remaining TLS 1.3-only entry points guarded by
|
|
* ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)
|
|
* (or the CTX equivalent). Each needs all three vectors -- NULL, a
|
|
* TLS 1.2 object, and a TLS 1.3 object -- for both operands to have an
|
|
* independence partner. Return values other than BAD_FUNC_ARG are
|
|
* intentional: these calls are made on objects that never handshook, so a
|
|
* side/state error is the correct success indication that the argument
|
|
* guard was passed. */
|
|
#ifdef WOLFSSL_SEND_HRR_COOKIE
|
|
ExpectIntEQ(wolfSSL_disable_hrr_cookie(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_disable_hrr_cookie(clientTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_disable_hrr_cookie(clientSsl),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_disable_hrr_cookie(serverSsl), WOLFSSL_SUCCESS);
|
|
#endif
|
|
#endif /* WOLFSSL_SEND_HRR_COOKIE */
|
|
|
|
#ifdef HAVE_SUPPORTED_CURVES
|
|
ExpectIntEQ(wolfSSL_CTX_only_dhe_psk(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_CTX_only_dhe_psk(clientTls12Ctx),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_only_dhe_psk(clientCtx), 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_only_dhe_psk(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_only_dhe_psk(clientTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_only_dhe_psk(clientSsl), 0);
|
|
#endif
|
|
#endif /* HAVE_SUPPORTED_CURVES */
|
|
|
|
/* wolfSSL_require_psk() already has its NULL and TLS 1.3 vectors in
|
|
* test_tls13_require_psk_apis(); only the TLS 1.2 middle vector -- the one
|
|
* that makes the version operand true on its own -- was missing. */
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_require_psk(clientTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_require_psk(clientSsl), 0);
|
|
#endif
|
|
|
|
#if !defined(NO_WOLFSSL_SERVER) && defined(HAVE_SESSION_TICKET)
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(serverTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
/* Never handshook, so the argument guard is passed and the handshake-state
|
|
* check rejects it. */
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(serverSsl),
|
|
WC_NO_ERR_TRACE(NOT_READY_ERROR));
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_EARLY_DATA
|
|
ExpectIntEQ(wolfSSL_get_early_data_status(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_get_early_data_status(clientTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_get_early_data_status(clientSsl),
|
|
WOLFSSL_EARLY_DATA_NOT_SENT);
|
|
#endif
|
|
#endif /* WOLFSSL_EARLY_DATA */
|
|
|
|
/* wolfSSL_get_cipher_name_by_hash():
|
|
* hash == NULL || ssl == NULL || (ssl->suites == NULL && ssl->ctx == NULL)
|
|
* Only the first two operands are reachable: ssl->ctx is invariant non-NULL
|
|
* for a live WOLFSSL, so the parenthesised sub-expression is dead and both
|
|
* of its operands are recorded in the exclusion record. */
|
|
#ifndef NO_PSK
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectNull(wolfSSL_get_cipher_name_by_hash(clientSsl, NULL));
|
|
ExpectNull(wolfSSL_get_cipher_name_by_hash(NULL, "SHA256"));
|
|
#if !defined(NO_SHA256) && (defined(HAVE_AESGCM) || defined(HAVE_CHACHA))
|
|
ExpectNotNull(wolfSSL_get_cipher_name_by_hash(clientSsl, "SHA256"));
|
|
#endif
|
|
#endif
|
|
#endif /* !NO_PSK */
|
|
|
|
#if !defined(NO_CERTS) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(serverCtx),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(clientTls12Ctx),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(clientCtx), 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(serverSsl),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(clientTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(clientSsl), 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_request_certificate(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_request_certificate(clientSsl),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_request_certificate(serverTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_request_certificate(serverSsl),
|
|
WC_NO_ERR_TRACE(NOT_READY_ERROR));
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef HAVE_ECC
|
|
#ifndef WOLFSSL_NO_SERVER_GROUPS_EXT
|
|
ExpectIntEQ(wolfSSL_preferred_group(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_preferred_group(serverSsl),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_preferred_group(clientTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_preferred_group(clientSsl),
|
|
WC_NO_ERR_TRACE(NOT_READY_ERROR));
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef HAVE_SUPPORTED_CURVES
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(NULL, NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(clientCtx, NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(NULL, groups, numGroups),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(clientTls12Ctx, groups, numGroups),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(clientCtx, groups,
|
|
WOLFSSL_MAX_GROUP_COUNT + 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(clientCtx, groups, numGroups),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(clientCtx, groups, -1),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(clientCtx->numGroups, numGroups);
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(clientCtx, bad_groups, numGroups),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(serverCtx, groups, numGroups),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_set_groups(serverCtx, bad_groups, numGroups),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_set_groups(NULL, NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_set_groups(clientSsl, NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_set_groups(NULL, groups, numGroups),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_set_groups(clientTls12Ssl, groups, numGroups),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_set_groups(clientSsl, groups,
|
|
WOLFSSL_MAX_GROUP_COUNT + 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_set_groups(clientSsl, groups, numGroups),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(clientSsl, groups, -1),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(clientSsl->numGroups, numGroups);
|
|
ExpectIntEQ(wolfSSL_set_groups(clientSsl, bad_groups, numGroups),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_set_groups(serverSsl, groups, numGroups),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(serverSsl, bad_groups, numGroups),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups_list(NULL, NULL),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups_list(clientCtx, NULL),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups_list(NULL, groupList),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
#if defined(OPENSSL_EXTRA) && !defined(NO_WOLFSSL_CLIENT)
|
|
{
|
|
int idx;
|
|
for (idx = 0; idx < WOLFSSL_MAX_GROUP_COUNT + 1; idx++)
|
|
too_many_groups[idx] = WOLFSSL_ECC_SECP256R1;
|
|
}
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups(clientCtx, too_many_groups,
|
|
WOLFSSL_MAX_GROUP_COUNT + 1), WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_set1_groups(clientSsl, too_many_groups,
|
|
WOLFSSL_MAX_GROUP_COUNT + 1), WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups(clientCtx, NULL, 1),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_set1_groups(clientSsl, NULL, 1),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups(clientCtx, groups, numGroups),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups(clientCtx, groups, -1),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(clientCtx->numGroups, numGroups);
|
|
ExpectIntEQ(wolfSSL_set1_groups(clientSsl, groups, numGroups),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set1_groups(clientSsl, groups, -1),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(clientSsl->numGroups, numGroups);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups_list(clientTls12Ctx, groupList),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups_list(clientCtx, groupList),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_CTX_set1_groups_list(serverCtx, groupList),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_set1_groups_list(NULL, NULL),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_set1_groups_list(clientSsl, NULL),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_set1_groups_list(NULL, groupList),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_set1_groups_list(clientTls12Ssl, groupList),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_set1_groups_list(clientSsl, groupList),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_set1_groups_list(serverSsl, groupList),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
#endif /* OPENSSL_EXTRA */
|
|
#endif /* HAVE_SUPPORTED_CURVES */
|
|
#endif /* HAVE_ECC */
|
|
|
|
#ifdef WOLFSSL_EARLY_DATA
|
|
#ifndef OPENSSL_EXTRA
|
|
ExpectIntEQ(wolfSSL_CTX_set_max_early_data(NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_CTX_get_max_early_data(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#else
|
|
ExpectIntEQ(SSL_CTX_set_max_early_data(NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(SSL_CTX_get_max_early_data(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef OPENSSL_EXTRA
|
|
ExpectIntEQ(wolfSSL_CTX_set_max_early_data(clientCtx, 0),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
ExpectIntEQ(wolfSSL_CTX_get_max_early_data(clientCtx),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#else
|
|
ExpectIntEQ(SSL_CTX_set_max_early_data(clientCtx, 0),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
ExpectIntEQ(SSL_CTX_get_max_early_data(clientCtx),
|
|
WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
#ifndef OPENSSL_EXTRA
|
|
ExpectIntEQ(wolfSSL_CTX_set_max_early_data(serverTls12Ctx, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_CTX_get_max_early_data(serverTls12Ctx),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#else
|
|
ExpectIntEQ(SSL_CTX_set_max_early_data(serverTls12Ctx, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(SSL_CTX_get_max_early_data(serverTls12Ctx),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#endif
|
|
#ifndef OPENSSL_EXTRA
|
|
#ifdef WOLFSSL_ERROR_CODE_OPENSSL
|
|
ExpectIntEQ(wolfSSL_CTX_set_max_early_data(serverCtx, 32),
|
|
WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_CTX_set_max_early_data(serverCtx, 32), 0);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_get_max_early_data(serverCtx), 32);
|
|
#else
|
|
ExpectIntEQ(SSL_CTX_set_max_early_data(serverCtx, 32), 1);
|
|
ExpectIntEQ(SSL_CTX_get_max_early_data(serverCtx), 32);
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef OPENSSL_EXTRA
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_get_max_early_data(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#else
|
|
ExpectIntEQ(SSL_set_max_early_data(NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(SSL_get_max_early_data(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef OPENSSL_EXTRA
|
|
#ifdef WOLFSSL_ERROR_CODE_OPENSSL
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(clientSsl, 17), WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(clientSsl, 17), 0);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_get_max_early_data(clientSsl), 17);
|
|
#else
|
|
ExpectIntEQ(SSL_set_max_early_data(clientSsl, 17), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(SSL_get_max_early_data(clientSsl), 17);
|
|
#endif
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
#ifndef OPENSSL_EXTRA
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(serverTls12Ssl, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_get_max_early_data(serverTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#else
|
|
ExpectIntEQ(SSL_set_max_early_data(serverTls12Ssl, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(SSL_get_max_early_data(serverTls12Ssl),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#endif
|
|
#ifndef OPENSSL_EXTRA
|
|
#ifdef WOLFSSL_ERROR_CODE_OPENSSL
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(serverSsl, 16), WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(serverSsl, 16), 0);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_get_max_early_data(serverSsl), 16);
|
|
#else
|
|
ExpectIntEQ(SSL_set_max_early_data(serverSsl, 16), 1);
|
|
ExpectIntEQ(SSL_get_max_early_data(serverSsl), 16);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
ExpectIntEQ(wolfSSL_write_early_data(NULL, earlyData, sizeof(earlyData),
|
|
&outSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_write_early_data(clientSsl, NULL, sizeof(earlyData),
|
|
&outSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_write_early_data(clientSsl, earlyData, -1, &outSz),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_write_early_data(clientSsl, earlyData,
|
|
sizeof(earlyData), NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_write_early_data(serverSsl, earlyData,
|
|
sizeof(earlyData), &outSz), WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_write_early_data(clientTls12Ssl, earlyData,
|
|
sizeof(earlyData), &outSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
/* invoking without session or psk cbs */
|
|
ExpectIntEQ(wolfSSL_write_early_data(clientSsl, earlyData,
|
|
sizeof(earlyData), &outSz), WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
/* verify *outSz is initialized to 0 even on non-success paths */
|
|
outSz = 42;
|
|
ExpectIntEQ(wolfSSL_write_early_data(clientSsl, earlyData,
|
|
sizeof(earlyData), &outSz), WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
ExpectIntEQ(outSz, 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(wolfSSL_read_early_data(NULL, earlyDataBuffer,
|
|
sizeof(earlyDataBuffer), &outSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectIntEQ(wolfSSL_read_early_data(serverSsl, NULL,
|
|
sizeof(earlyDataBuffer), &outSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_read_early_data(serverSsl, earlyDataBuffer, -1,
|
|
&outSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_read_early_data(serverSsl, earlyDataBuffer,
|
|
sizeof(earlyDataBuffer), NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectIntEQ(wolfSSL_read_early_data(clientSsl, earlyDataBuffer,
|
|
sizeof(earlyDataBuffer), &outSz), WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
#endif
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(wolfSSL_read_early_data(serverTls12Ssl, earlyDataBuffer,
|
|
sizeof(earlyDataBuffer), &outSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_read_early_data(serverSsl, earlyDataBuffer,
|
|
sizeof(earlyDataBuffer), &outSz), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_EARLY_DATA)
|
|
ExpectIntLT(SSL_get_early_data_status(NULL), 0);
|
|
#endif
|
|
|
|
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
wolfSSL_free(serverSsl);
|
|
wolfSSL_CTX_free(serverCtx);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
wolfSSL_free(clientSsl);
|
|
wolfSSL_CTX_free(clientCtx);
|
|
#endif
|
|
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
wolfSSL_free(serverTls12Ssl);
|
|
wolfSSL_CTX_free(serverTls12Ctx);
|
|
#endif
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
wolfSSL_free(clientTls12Ssl);
|
|
wolfSSL_CTX_free(clientTls12Ctx);
|
|
#endif
|
|
#endif
|
|
#endif /* WOLFSSL_TLS13 */
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \
|
|
!defined(NO_PSK)
|
|
int test_tls13_cert_with_extern_psk_apis(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_set_cert_with_extern_psk(NULL, 0), WOLFSSL_FAILURE);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(NULL, 0), WOLFSSL_FAILURE);
|
|
|
|
ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
|
|
ExpectNotNull(ctx);
|
|
ssl = wolfSSL_new(ctx);
|
|
ExpectNotNull(ssl);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
/* Any non-zero value enables cert_with_extern_psk. */
|
|
ExpectIntEQ(wolfSSL_CTX_set_cert_with_extern_psk(ctx, -1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_set_cert_with_extern_psk(ctx, 2),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl, -1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl, 2), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_set_cert_with_extern_psk(ctx, 1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl, 0), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl, 1), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#else
|
|
int test_tls13_cert_with_extern_psk_apis(void)
|
|
{
|
|
return TEST_SKIPPED;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \
|
|
!defined(NO_PSK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* 32-byte external PSK (SHA-256 digest size) used by cwep test callbacks. */
|
|
static const unsigned char test_tls13_cwep_psk[32] = {
|
|
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
|
|
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
|
|
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
|
|
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A
|
|
};
|
|
|
|
static unsigned int test_tls13_cwep_client_cb(WOLFSSL* ssl, const char* hint,
|
|
char* identity, unsigned int id_max_len, unsigned char* key,
|
|
unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)hint;
|
|
if (id_max_len == 0 || key_max_len < sizeof(test_tls13_cwep_psk))
|
|
return 0;
|
|
XSTRNCPY(identity, "cwep_client", id_max_len);
|
|
XMEMCPY(key, test_tls13_cwep_psk, sizeof(test_tls13_cwep_psk));
|
|
return (unsigned int)sizeof(test_tls13_cwep_psk);
|
|
}
|
|
|
|
static unsigned int test_tls13_cwep_server_cb(WOLFSSL* ssl, const char* id,
|
|
unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
if (key_max_len < sizeof(test_tls13_cwep_psk) || id == NULL)
|
|
return 0;
|
|
if (XSTRCMP(id, "cwep_client") != 0)
|
|
return 0;
|
|
XMEMCPY(key, test_tls13_cwep_psk, sizeof(test_tls13_cwep_psk));
|
|
return (unsigned int)sizeof(test_tls13_cwep_psk);
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_cert_with_extern_psk_handshake(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \
|
|
!defined(NO_PSK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char appMsg[] = "cert_with_extern_psk test";
|
|
char readBuf[sizeof(appMsg)];
|
|
int readSz;
|
|
|
|
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_PEER, NULL);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
#if !defined(NO_CERTS) && !defined(NO_FILESYSTEM)
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_CTX_load_verify_locations(ctx_c, caEccCertFile,
|
|
NULL) == WOLFSSL_SUCCESS);
|
|
#elif !defined(NO_RSA)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_CTX_load_verify_locations(ctx_c, caCertFile,
|
|
NULL) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
#endif
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_cwep_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_cwep_server_cb);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_c, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_s, 1), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(ssl_c->options.pskNegotiated, 1);
|
|
ExpectIntEQ(ssl_s->options.pskNegotiated, 1);
|
|
ExpectIntEQ(ssl_c->options.certWithExternPsk, 1);
|
|
ExpectIntEQ(ssl_s->options.certWithExternPsk, 1);
|
|
ExpectIntEQ(ssl_c->msgsReceived.got_certificate, 1);
|
|
ExpectIntEQ(ssl_c->msgsReceived.got_certificate_verify, 1);
|
|
|
|
/* Verify application data exchange works with the derived keys. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, appMsg, (int)XSTRLEN(appMsg)),
|
|
(int)XSTRLEN(appMsg));
|
|
readSz = wolfSSL_read(ssl_s, readBuf, sizeof(readBuf));
|
|
ExpectIntEQ(readSz, (int)XSTRLEN(appMsg));
|
|
ExpectIntEQ(XMEMCMP(readBuf, appMsg, (size_t)readSz), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_cert_with_extern_psk_client_requires_cert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \
|
|
!defined(NO_PSK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && !defined(NO_CERTS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* The client requests cert_with_extern_psk and mandates a peer certificate
|
|
* (WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT). The server negotiates the external
|
|
* PSK but does NOT enable cert_with_extern_psk, so it completes a PSK-only
|
|
* handshake without sending a Certificate/CertificateVerify. The client
|
|
* must therefore reject in DoTls13Finished with NO_PEER_CERT instead of
|
|
* accepting a PSK-only handshake. */
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_cwep_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_cwep_server_cb);
|
|
/* Only the client requests cert_with_extern_psk. */
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_c, 1), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(NO_PEER_CERT));
|
|
ExpectIntEQ(ssl_c->options.pskNegotiated, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_cert_with_extern_psk_requires_key_share(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \
|
|
!defined(NO_PSK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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));
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
#if !defined(NO_CERTS) && !defined(NO_FILESYSTEM)
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#elif !defined(NO_RSA)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
#endif
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_cwep_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_cwep_server_cb);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_c, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_s, 1), WOLFSSL_SUCCESS);
|
|
/* Omit key_share in CH1 to force the server to send an HRR. */
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
/* CH1: client -> server (no key_share). */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* HRR: server reads CH1, sends HRR requesting a key_share group. */
|
|
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(ssl_s->options.serverState,
|
|
SERVER_HELLO_RETRY_REQUEST_COMPLETE);
|
|
|
|
/* Complete the handshake: client sends CH2 (with key_share), server
|
|
* responds with SH + cert + cert-verify + Finished, client finishes. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
|
|
/* Verify that cert_with_extern_psk was negotiated end-to-end. */
|
|
ExpectIntEQ(ssl_c->options.pskNegotiated, 1);
|
|
ExpectIntEQ(ssl_s->options.pskNegotiated, 1);
|
|
ExpectIntEQ(ssl_c->options.certWithExternPsk, 1);
|
|
ExpectIntEQ(ssl_s->options.certWithExternPsk, 1);
|
|
ExpectIntEQ(ssl_c->msgsReceived.got_certificate, 1);
|
|
ExpectIntEQ(ssl_c->msgsReceived.got_certificate_verify, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_cert_with_extern_psk_rejects_resumption(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \
|
|
!defined(NO_PSK) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(HAVE_ECC) || !defined(NO_RSA))
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte readBuf[16];
|
|
|
|
/* Step 1: plain TLS 1.3 handshake to obtain a session ticket. The same
|
|
* server CTX is reused below so the ticket encryption key matches. */
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
/* Load on ssl_s, not ctx_s: ssl_s already exists (test_memio_setup created
|
|
* it) and shares the CTX key buffers. Reloading on ctx_s would free those
|
|
* buffers, leaving ssl_s with a dangling key that crashes when the server
|
|
* decodes it for CertificateVerify. */
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Drain the NewSessionTicket post-handshake message. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
|
|
/* Step 2: attempt to resume while also offering cert_with_extern_psk.
|
|
* RFC 9973 Sect. 5.1 requires all PSKs offered alongside
|
|
* cert_with_extern_psk to be external PSKs. The client MUST therefore
|
|
* suppress the resumption ticket identity from the pre_shared_key
|
|
* extension. The handshake succeeds as a cert_with_extern_psk handshake
|
|
* using only the external PSK. */
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_cwep_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_cwep_server_cb);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_c, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_s, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
/* Handshake succeeds; the client correctly omits the resumption ticket. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
/* Verify we got a cert_with_extern_psk handshake, not a resumption. */
|
|
ExpectIntEQ(ssl_c->options.certWithExternPsk, 1);
|
|
ExpectIntEQ(ssl_s->options.certWithExternPsk, 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \
|
|
!defined(NO_PSK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* Locate the extensions block of a TLS 1.3 ServerHello record. On success,
|
|
* writes the offset of the 2-byte extensions_length field into *ext_len_off
|
|
* and returns 0. Returns -1 on malformed input. Only the plaintext SH
|
|
* record (type 0x16, handshake subtype 0x02) is supported. */
|
|
static int test_cwep_sh_find_ext_block(const byte* sh, int sh_len,
|
|
int* ext_len_off)
|
|
{
|
|
int idx;
|
|
int sid_len;
|
|
|
|
/* 5 byte record hdr + 4 byte handshake hdr + 2 byte legacy_version
|
|
* + 32 byte random + 1 byte legacy_session_id length. */
|
|
if (sh_len < 5 + 4 + 2 + 32 + 1)
|
|
return -1;
|
|
if (sh[0] != 0x16 || sh[5] != 0x02)
|
|
return -1;
|
|
idx = 5 + 4 + 2 + 32;
|
|
sid_len = sh[idx];
|
|
idx += 1 + sid_len + 2 + 1; /* skip sid + cipher_suite + compression */
|
|
if (idx + 2 > sh_len)
|
|
return -1;
|
|
*ext_len_off = idx;
|
|
return 0;
|
|
}
|
|
|
|
/* Apply a delta to the record, handshake and extensions length fields of a
|
|
* TLS 1.3 SH record. Negative values shrink the message. */
|
|
static void test_cwep_sh_adjust_lengths(byte* sh, int ext_len_off, int delta)
|
|
{
|
|
int v;
|
|
|
|
v = (int)(((word32)sh[3] << 8) | sh[4]) + delta;
|
|
sh[3] = (byte)(v >> 8);
|
|
sh[4] = (byte)v;
|
|
v = (int)(((word32)sh[6] << 16) | ((word32)sh[7] << 8) | sh[8]) + delta;
|
|
sh[6] = (byte)(v >> 16);
|
|
sh[7] = (byte)(v >> 8);
|
|
sh[8] = (byte)v;
|
|
v = (int)(((word32)sh[ext_len_off] << 8) | sh[ext_len_off + 1]) + delta;
|
|
sh[ext_len_off] = (byte)(v >> 8);
|
|
sh[ext_len_off + 1] = (byte)v;
|
|
}
|
|
|
|
/* Remove the first extension of the given type from a TLS 1.3 SH record.
|
|
* Returns the new record length, or -1 if the extension was not present. */
|
|
static int test_cwep_sh_strip_extension(byte* sh, int sh_len, word16 ext_type)
|
|
{
|
|
int ext_len_off;
|
|
int ext_base, ext_end;
|
|
int p;
|
|
word16 ext_total;
|
|
|
|
if (test_cwep_sh_find_ext_block(sh, sh_len, &ext_len_off) != 0)
|
|
return -1;
|
|
ext_total = (word16)(((word16)sh[ext_len_off] << 8) | sh[ext_len_off + 1]);
|
|
ext_base = ext_len_off + 2;
|
|
ext_end = ext_base + ext_total;
|
|
if (ext_end > sh_len)
|
|
return -1;
|
|
|
|
p = ext_base;
|
|
while (p + 4 <= ext_end) {
|
|
word16 t = (word16)(((word16)sh[p] << 8) | sh[p + 1]);
|
|
word16 l = (word16)(((word16)sh[p + 2] << 8) | sh[p + 3]);
|
|
int entry = 4 + (int)l;
|
|
if (p + entry > ext_end)
|
|
return -1;
|
|
if (t == ext_type) {
|
|
XMEMMOVE(sh + p, sh + p + entry,
|
|
(size_t)(sh_len - p - entry));
|
|
test_cwep_sh_adjust_lengths(sh, ext_len_off, -entry);
|
|
return sh_len - entry;
|
|
}
|
|
p += entry;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#if defined(HAVE_SESSION_TICKET)
|
|
/* Append a zero-length extension of the given type to a TLS 1.3 SH record.
|
|
* The SH body must be the tail of the record, which is the normal case. */
|
|
static int test_cwep_sh_append_empty_extension(byte* sh, int sh_len,
|
|
int sh_cap, word16 ext_type)
|
|
{
|
|
int ext_len_off;
|
|
int ext_base, ext_end;
|
|
word16 ext_total;
|
|
|
|
if (test_cwep_sh_find_ext_block(sh, sh_len, &ext_len_off) != 0)
|
|
return -1;
|
|
ext_total = (word16)(((word16)sh[ext_len_off] << 8) | sh[ext_len_off + 1]);
|
|
ext_base = ext_len_off + 2;
|
|
ext_end = ext_base + ext_total;
|
|
if (ext_end != sh_len)
|
|
return -1;
|
|
if (sh_len + 4 > sh_cap)
|
|
return -1;
|
|
|
|
sh[sh_len + 0] = (byte)(ext_type >> 8);
|
|
sh[sh_len + 1] = (byte)ext_type;
|
|
sh[sh_len + 2] = 0;
|
|
sh[sh_len + 3] = 0;
|
|
test_cwep_sh_adjust_lengths(sh, ext_len_off, 4);
|
|
return sh_len + 4;
|
|
}
|
|
#endif /* HAVE_SESSION_TICKET */
|
|
#endif
|
|
|
|
int test_tls13_cert_with_extern_psk_sh_missing_key_share(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \
|
|
!defined(NO_PSK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(HAVE_ECC) || !defined(NO_RSA))
|
|
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_buf[4096];
|
|
const char* sh_bytes = NULL;
|
|
int sh_sz = 0;
|
|
int new_sz;
|
|
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
/* Load on ssl_s, not ctx_s: ssl_s already exists (test_memio_setup created
|
|
* it) and shares the CTX key buffers. Reloading on ctx_s would free those
|
|
* buffers, leaving ssl_s with a dangling key that crashes when the server
|
|
* decodes it for CertificateVerify. */
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_cwep_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_cwep_server_cb);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_c, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_s, 1), WOLFSSL_SUCCESS);
|
|
|
|
/* Drive the client to emit the ClientHello, then let the server produce
|
|
* its flight. */
|
|
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);
|
|
|
|
/* The first "message" recorded by memio may contain several concatenated
|
|
* records (SH + CCS + first encrypted handshake record). Slice the
|
|
* plaintext SH record out using its own length field. */
|
|
ExpectIntEQ(test_memio_get_message(&test_ctx, 1, &sh_bytes, &sh_sz, 0), 0);
|
|
if (sh_sz >= 5 && (byte)sh_bytes[0] == 0x16) {
|
|
int rec_body = ((int)(byte)sh_bytes[3] << 8) | (byte)sh_bytes[4];
|
|
sh_sz = 5 + rec_body;
|
|
}
|
|
ExpectTrue(sh_sz > 0 && sh_sz <= (int)sizeof(sh_buf));
|
|
if (sh_sz > 0 && sh_sz <= (int)sizeof(sh_buf)) {
|
|
XMEMCPY(sh_buf, sh_bytes, (size_t)sh_sz);
|
|
/* Strip the key_share extension from the SH so the resulting SH
|
|
* confirms cert_with_extern_psk without negotiating (EC)DHE. */
|
|
new_sz = test_cwep_sh_strip_extension(sh_buf, sh_sz, 0x0033);
|
|
ExpectIntGT(new_sz, 0);
|
|
}
|
|
else {
|
|
new_sz = -1;
|
|
}
|
|
|
|
/* Throw away the entire server flight and feed only the tampered SH. */
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
if (new_sz > 0) {
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char*)sh_buf, new_sz), 0);
|
|
}
|
|
|
|
/* Client must reject the SH with EXT_MISSING. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
EXT_MISSING);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_cert_with_extern_psk_sh_confirms_resumption(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \
|
|
!defined(NO_PSK) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(HAVE_ECC) || !defined(NO_RSA))
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte sh_buf[4096];
|
|
const char* sh_bytes = NULL;
|
|
byte drain[16];
|
|
int sh_sz = 0;
|
|
int new_sz;
|
|
|
|
/* Phase 1: plain handshake so the client gets a session ticket. The
|
|
* server CTX is reused below to keep the ticket encryption key. */
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
/* Load on ssl_s, not ctx_s: ssl_s already exists (test_memio_setup created
|
|
* it) and shares the CTX key buffers. Reloading on ctx_s would free those
|
|
* buffers, leaving ssl_s with a dangling key that crashes when the server
|
|
* decodes it for CertificateVerify. */
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Drain the NewSessionTicket post-handshake message. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, drain, sizeof(drain)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
|
|
/* Phase 2: client resumes WITHOUT cert_with_extern_psk. The server
|
|
* performs a normal resumption. We then tamper the SH to inject an
|
|
* unsolicited cert_with_extern_psk extension. The client must reject
|
|
* it because it never offered the extension. */
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
/* Run client CH then server flight. */
|
|
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_memio_get_message(&test_ctx, 1, &sh_bytes, &sh_sz, 0), 0);
|
|
if (sh_sz >= 5 && (byte)sh_bytes[0] == 0x16) {
|
|
int rec_body = ((int)(byte)sh_bytes[3] << 8) | (byte)sh_bytes[4];
|
|
sh_sz = 5 + rec_body;
|
|
}
|
|
ExpectTrue(sh_sz > 0 && sh_sz <= (int)sizeof(sh_buf));
|
|
if (sh_sz > 0 && sh_sz <= (int)sizeof(sh_buf)) {
|
|
XMEMCPY(sh_buf, sh_bytes, (size_t)sh_sz);
|
|
/* Append an unsolicited cert_with_extern_psk (0x0021) extension.
|
|
* The client never offered this extension, so it must be rejected. */
|
|
new_sz = test_cwep_sh_append_empty_extension(sh_buf, sh_sz,
|
|
(int)sizeof(sh_buf), 0x0021);
|
|
ExpectIntGT(new_sz, 0);
|
|
}
|
|
else {
|
|
new_sz = -1;
|
|
}
|
|
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
if (new_sz > 0) {
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char*)sh_buf, new_sz), 0);
|
|
}
|
|
|
|
/* Client must reject the unsolicited extension. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_api(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
(void)ctx;
|
|
(void)ssl;
|
|
|
|
/* NULL arguments are rejected. */
|
|
ExpectIntEQ(wolfSSL_CTX_require_psk(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_require_psk(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* The failNoPSK bit is only settable when external-PSK or session-ticket
|
|
* support is compiled in; mirror the guard used by the API itself. */
|
|
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
|
|
/* wolfSSL_CTX_require_psk() sets the failNoPSK bit on the CTX, and a new
|
|
* SSL object inherits it. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_require_psk(ctx), 0);
|
|
ExpectIntEQ(ctx->failNoPSK, 1);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(ssl->options.failNoPSK, 1);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* wolfSSL_require_psk() sets the option directly on the SSL object. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(ssl->options.failNoPSK, 0);
|
|
ExpectIntEQ(wolfSSL_require_psk(ssl), 0);
|
|
ExpectIntEQ(ssl->options.failNoPSK, 1);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* The requirement is fail-closed: on a downgrade-capable context (created
|
|
* from a v23 method) require_psk() disables version downgrade so the
|
|
* handshake cannot silently fall back to (D)TLS 1.2 without a PSK. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_require_psk(ctx), 0);
|
|
ExpectIntEQ(ctx->method->downgrade, 0);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(ssl->options.downgrade, 0);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
/* Setting a version range after require_psk() must not hand the downgrade
|
|
* back. The minimum is below TLS 1.3, where the requirement cannot be
|
|
* enforced, so the requirement wins and the single version stands. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_require_psk(ssl), 0);
|
|
ExpectIntEQ(wolfSSL_SetVersion(ssl, WOLFSSL_TLSV1_3), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetMinVersion(ssl, WOLFSSL_TLSV1_2), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->options.downgrade, 0);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
/* Same through the OpenSSL compatibility setter. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_require_psk(ssl), 0);
|
|
ExpectIntEQ(wolfSSL_SetVersion(ssl, WOLFSSL_TLSV1_3), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_min_proto_version(ssl, TLS1_2_VERSION),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->options.downgrade, 0);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
#endif
|
|
|
|
/* Without the requirement the same calls do make a range again. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_SetVersion(ssl, WOLFSSL_TLSV1_3), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetMinVersion(ssl, WOLFSSL_TLSV1_2), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->options.downgrade, 1);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
/* The API is TLS 1.3 only - a TLS 1.2 context is rejected so users are not
|
|
* misled into thinking the option applies to TLS 1.2. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_require_psk(ctx), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* 32-byte external PSK used by the fail-if-no-PSK test callbacks. */
|
|
static const unsigned char test_tls13_fnp_psk[32] = {
|
|
0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A,
|
|
0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A,
|
|
0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A,
|
|
0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A
|
|
};
|
|
|
|
static unsigned int test_tls13_fnp_client_cb(WOLFSSL* ssl, const char* hint,
|
|
char* identity, unsigned int id_max_len, unsigned char* key,
|
|
unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)hint;
|
|
if (id_max_len == 0 || key_max_len < sizeof(test_tls13_fnp_psk))
|
|
return 0;
|
|
XSTRNCPY(identity, "fnp_client", id_max_len);
|
|
XMEMCPY(key, test_tls13_fnp_psk, sizeof(test_tls13_fnp_psk));
|
|
return (unsigned int)sizeof(test_tls13_fnp_psk);
|
|
}
|
|
|
|
static unsigned int test_tls13_fnp_server_cb(WOLFSSL* ssl, const char* id,
|
|
unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
if (key_max_len < sizeof(test_tls13_fnp_psk) || id == NULL)
|
|
return 0;
|
|
if (XSTRCMP(id, "fnp_client") != 0)
|
|
return 0;
|
|
XMEMCPY(key, test_tls13_fnp_psk, sizeof(test_tls13_fnp_psk));
|
|
return (unsigned int)sizeof(test_tls13_fnp_psk);
|
|
}
|
|
|
|
/* Server PSK callback that finds no PSK for any offered identity (returns 0),
|
|
* mirroring an application whose lookup misses. */
|
|
static unsigned int test_tls13_fnp_reject_server_cb(WOLFSSL* ssl,
|
|
const char* id, unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)id;
|
|
(void)key;
|
|
(void)key_max_len;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_fail_if_no_psk_handshake(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char appMsg[] = "fail_if_no_psk test";
|
|
char readBuf[sizeof(appMsg)];
|
|
int readSz;
|
|
|
|
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);
|
|
|
|
/* Both endpoints require a PSK to be negotiated. */
|
|
wolfSSL_require_psk(ssl_c);
|
|
wolfSSL_require_psk(ssl_s);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
|
|
/* A PSK is configured on both ends, so the handshake completes. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(ssl_c->options.failNoPSK, 1);
|
|
ExpectIntEQ(ssl_s->options.failNoPSK, 1);
|
|
ExpectIntEQ(ssl_c->options.pskNegotiated, 1);
|
|
ExpectIntEQ(ssl_s->options.pskNegotiated, 1);
|
|
|
|
/* Application data flows over the PSK-derived keys. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, appMsg, (int)XSTRLEN(appMsg)),
|
|
(int)XSTRLEN(appMsg));
|
|
readSz = wolfSSL_read(ssl_s, readBuf, sizeof(readBuf));
|
|
ExpectIntEQ(readSz, (int)XSTRLEN(appMsg));
|
|
ExpectIntEQ(XMEMCMP(readBuf, appMsg, (size_t)readSz), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_rejects_no_psk(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(HAVE_ECC) || !defined(NO_RSA))
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* The server requires a PSK and has one configured, but the client offers
|
|
* none. The server must abort the handshake with PSK_MISSING_ERROR rather
|
|
* than fall back to a certificate-only handshake. */
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_require_psk(ssl_s);
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
/* Only the server installs a PSK callback; the client sends no PSK. */
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(PSK_MISSING_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_client_no_psk_configured(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* WOLFSSL_QT force-enables havePSK on every CTX (see wolfSSL_CTX_new), so the
|
|
* "no PSK configured" precondition this test relies on cannot be set up there. */
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && !defined(WOLFSSL_QT) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(HAVE_ECC) || !defined(NO_RSA))
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* The client requires a PSK but configures none. Because failNoPSK is gated
|
|
* on havePSK, this must NOT hard-fail: a normal certificate handshake is
|
|
* expected to succeed instead of raising PSK_MISSING_ERROR. */
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, NULL);
|
|
wolfSSL_require_psk(ssl_c);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_CTX_load_verify_locations(ctx_c, caEccCertFile,
|
|
NULL) == WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_CTX_load_verify_locations(ctx_c, caCertFile,
|
|
NULL) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
|
|
ExpectIntEQ(ssl_c->options.failNoPSK, 1);
|
|
ExpectIntEQ(ssl_c->options.havePSK, 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(ssl_c->options.pskNegotiated, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_client_rejects(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(HAVE_ECC) || !defined(NO_RSA))
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* The client requires a PSK and has one configured, but the server has no
|
|
* PSK callback so it never selects one. The client must abort with
|
|
* PSK_MISSING_ERROR rather than fall back to a cert-only handshake. */
|
|
wolfSSL_require_psk(ssl_c);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
/* Only the client installs a PSK callback. */
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(PSK_MISSING_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_requires_dhe(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* Baseline: a pure psk_ke handshake (no DHE) succeeds when the PSK is
|
|
* optional, confirming the setup itself is valid. */
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_s), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(ssl_s->options.pskNegotiated, 1);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = NULL; ssl_s = NULL; ctx_c = NULL; ctx_s = NULL;
|
|
|
|
/* With failNoPSK on the server, the same pure psk_ke negotiation (no
|
|
* forward secrecy) must be rejected with PSK_KEY_ERROR. */
|
|
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);
|
|
wolfSSL_require_psk(ssl_s);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_s), 0);
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(PSK_KEY_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_client_requires_dhe(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* Only the client mandates the PSK (failNoPSK). The server does NOT set
|
|
* failNoPSK and is configured for pure psk_ke (wolfSSL_no_dhe_psk), so it
|
|
* accepts the PSK and replies with a ServerHello that omits a key_share.
|
|
* When the client processes that ServerHello in SetupPskKey, the mandatory
|
|
* external PSK has no (EC)DHE for forward secrecy, so the client must
|
|
* reject it with PSK_KEY_ERROR. This exercises the client-side branch that
|
|
* the server-only requires_dhe test cannot reach (there the server rejects
|
|
* the pure psk_ke negotiation first). */
|
|
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_require_psk(ssl_c);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
/* Force the server to negotiate a pure psk_ke (no (EC)DHE) exchange. */
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_s), 0);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(PSK_KEY_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_resumption_exempt_from_dhe(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(HAVE_ECC) || !defined(NO_RSA))
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte readBuf[16];
|
|
|
|
/* Step 1: full TLS 1.3 handshake to obtain a session ticket. The server
|
|
* cert/key are already loaded by test_memio_setup; the default ticket
|
|
* encryption callback lets a fresh server CTX below decrypt the ticket. */
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Drain the NewSessionTicket post-handshake message. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Step 2: resume with a pure psk_ke (no (EC)DHE) exchange while the server
|
|
* has the mandatory-PSK requirement set. A session-ticket resumption is
|
|
* exempt from the mandatory-(EC)DHE rule, so the handshake must succeed
|
|
* (an external PSK in the same situation would be rejected). */
|
|
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);
|
|
wolfSSL_require_psk(ssl_s);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(ssl_c->options.resuming, 1);
|
|
ExpectIntEQ(ssl_s->options.resuming, 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_server_rejects_offered_psk(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* Baseline: the client offers a PSK but the server's callback rejects it
|
|
* (returns 0). Without failNoPSK the server (which has a certificate from
|
|
* test_memio_setup) falls back to a normal certificate handshake, which
|
|
* succeeds and negotiates no PSK. */
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_reject_server_cb);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(ssl_s->options.pskNegotiated, 0);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
/* With failNoPSK the same rejected-PSK negotiation must abort with
|
|
* PSK_MISSING_ERROR instead of falling back to the certificate handshake. */
|
|
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);
|
|
wolfSSL_require_psk(ssl_s);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_reject_server_cb);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(PSK_MISSING_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_no_cert_server(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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));
|
|
|
|
/* Build the CTXs manually so the server has NO certificate configured,
|
|
* matching a PSK-only deployment. The client offers a PSK, the server's
|
|
* callback rejects it, and failNoPSK is set: the server must report
|
|
* PSK_MISSING_ERROR (not BAD_BINDER, and no certificate fallback). */
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
/* PSK callbacks are set on the CTX so PSK cipher suites are available when
|
|
* the (cert-less) SSL objects are created. */
|
|
wolfSSL_CTX_set_psk_client_callback(ctx_c, test_tls13_fnp_client_cb);
|
|
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
wolfSSL_CTX_set_psk_server_callback(ctx_s, test_tls13_fnp_reject_server_cb);
|
|
wolfSSL_CTX_require_psk(ctx_s);
|
|
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(PSK_MISSING_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_dtls13_handshake(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char appMsg[] = "fail_if_no_psk dtls13 test";
|
|
char readBuf[sizeof(appMsg)];
|
|
int readSz;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
|
|
|
|
/* Both endpoints require a PSK to be negotiated over DTLS 1.3, exercising
|
|
* the HRR/cookie flow through DoTls13ServerHello and CheckPreSharedKeys. */
|
|
wolfSSL_require_psk(ssl_c);
|
|
wolfSSL_require_psk(ssl_s);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
|
|
/* A PSK is configured on both ends, so the handshake completes. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(ssl_c->options.failNoPSK, 1);
|
|
ExpectIntEQ(ssl_s->options.failNoPSK, 1);
|
|
ExpectIntEQ(ssl_c->options.pskNegotiated, 1);
|
|
ExpectIntEQ(ssl_s->options.pskNegotiated, 1);
|
|
|
|
/* Application data flows over the PSK-derived keys. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, appMsg, (int)XSTRLEN(appMsg)),
|
|
(int)XSTRLEN(appMsg));
|
|
readSz = wolfSSL_read(ssl_s, readBuf, sizeof(readBuf));
|
|
ExpectIntEQ(readSz, (int)XSTRLEN(appMsg));
|
|
ExpectIntEQ(XMEMCMP(readBuf, appMsg, (size_t)readSz), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_fail_if_no_psk_dtls13_rejects_no_psk(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(HAVE_ECC) || !defined(NO_RSA))
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
|
|
|
|
/* The server requires a PSK and has one configured, but the client offers
|
|
* none. Over DTLS 1.3 the server must still abort with PSK_MISSING_ERROR
|
|
* rather than fall back to a certificate-only handshake. */
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_require_psk(ssl_s);
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
/* Only the server installs a PSK callback; the client sends no PSK. */
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(PSK_MISSING_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(NO_WOLFSSL_SERVER) && defined(HAVE_ECC) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256) && \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384)
|
|
/* Called when writing. */
|
|
static int CsSend(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)sz;
|
|
(void)ctx;
|
|
|
|
/* Force error return from wolfSSL_accept_TLSv13(). */
|
|
return WANT_WRITE;
|
|
}
|
|
/* Called when reading. */
|
|
static int CsRecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
WOLFSSL_BUFFER_INFO* msg = (WOLFSSL_BUFFER_INFO*)ctx;
|
|
int len = (int)msg->length;
|
|
|
|
(void)ssl;
|
|
(void)sz;
|
|
|
|
/* Pass back as much of message as will fit in buffer. */
|
|
if (len > sz)
|
|
len = sz;
|
|
XMEMCPY(buf, msg->buffer, len);
|
|
/* Move over returned data. */
|
|
msg->buffer += len;
|
|
msg->length -= len;
|
|
|
|
/* Amount actually copied. */
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_cipher_suites(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(NO_WOLFSSL_SERVER) && defined(HAVE_ECC) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256) && \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL *ssl = NULL;
|
|
int i;
|
|
byte clientHello[] = {
|
|
0x16, 0x03, 0x03, 0x01, 0x9b, 0x01, 0x00, 0x01,
|
|
0x97, 0x03, 0x03, 0xf4, 0x65, 0xbd, 0x22, 0xfe,
|
|
0x6e, 0xab, 0x66, 0xdd, 0xcf, 0xe9, 0x65, 0x55,
|
|
0xe8, 0xdf, 0xc3, 0x8e, 0x4b, 0x00, 0xbc, 0xf8,
|
|
0x23, 0x57, 0x1b, 0xa0, 0xc8, 0xa9, 0xe2, 0x8c,
|
|
0x91, 0x6e, 0xf9, 0x20, 0xf7, 0x5c, 0xc5, 0x5b,
|
|
0x75, 0x8c, 0x47, 0x0a, 0x0e, 0xc4, 0x1a, 0xda,
|
|
0xef, 0x75, 0xe5, 0x21, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
|
|
/* Cipher suites: 0x13, 0x01 = TLS13-AES128-GCM-SHA256, twice. */
|
|
0x13, 0x01,
|
|
0x13, 0x01, 0x01, 0x00, 0x01, 0x4a, 0x00, 0x2d,
|
|
0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x33, 0x00,
|
|
0x47, 0x00, 0x45, 0x00, 0x17, 0x00, 0x41, 0x04,
|
|
0x90, 0xfc, 0xe2, 0x97, 0x05, 0x7c, 0xb5, 0x23,
|
|
0x5d, 0x5f, 0x5b, 0xcd, 0x0c, 0x1e, 0xe0, 0xe9,
|
|
0xab, 0x38, 0x6b, 0x1e, 0x20, 0x5c, 0x1c, 0x90,
|
|
0x2a, 0x9e, 0x68, 0x8e, 0x70, 0x05, 0x10, 0xa8,
|
|
0x02, 0x1b, 0xf9, 0x5c, 0xef, 0xc9, 0xaf, 0xca,
|
|
0x1a, 0x3b, 0x16, 0x8b, 0xe4, 0x1b, 0x3c, 0x15,
|
|
0xb8, 0x0d, 0xbd, 0xaf, 0x62, 0x8d, 0xa7, 0x13,
|
|
0xa0, 0x7c, 0xe0, 0x59, 0x0c, 0x4f, 0x8a, 0x6d,
|
|
0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, 0x00,
|
|
0x0d, 0x00, 0x20, 0x00, 0x1e, 0x06, 0x03, 0x05,
|
|
0x03, 0x04, 0x03, 0x02, 0x03, 0x08, 0x06, 0x08,
|
|
0x0b, 0x08, 0x05, 0x08, 0x0a, 0x08, 0x04, 0x08,
|
|
0x09, 0x06, 0x01, 0x05, 0x01, 0x04, 0x01, 0x03,
|
|
0x01, 0x02, 0x01, 0x00, 0x0a, 0x00, 0x04, 0x00,
|
|
0x02, 0x00, 0x17, 0x00, 0x16, 0x00, 0x00, 0x00,
|
|
0x23, 0x00, 0x00, 0x00, 0x29, 0x00, 0xb9, 0x00,
|
|
0x94, 0x00, 0x8e, 0x0f, 0x12, 0xfa, 0x84, 0x1f,
|
|
0x76, 0x94, 0xd7, 0x09, 0x5e, 0xad, 0x08, 0x51,
|
|
0xb6, 0x80, 0x28, 0x31, 0x8b, 0xfd, 0xc6, 0xbd,
|
|
0x9e, 0xf5, 0x3b, 0x4d, 0x02, 0xbe, 0x1d, 0x73,
|
|
0xea, 0x13, 0x68, 0x00, 0x4c, 0xfd, 0x3d, 0x48,
|
|
0x51, 0xf9, 0x06, 0xbb, 0x92, 0xed, 0x42, 0x9f,
|
|
0x7f, 0x2c, 0x73, 0x9f, 0xd9, 0xb4, 0xef, 0x05,
|
|
0x26, 0x5b, 0x60, 0x5c, 0x0a, 0xfc, 0xa3, 0xbd,
|
|
0x2d, 0x2d, 0x8b, 0xf9, 0xaa, 0x5c, 0x96, 0x3a,
|
|
0xf2, 0xec, 0xfa, 0xe5, 0x57, 0x2e, 0x87, 0xbe,
|
|
0x27, 0xc5, 0x3d, 0x4f, 0x5d, 0xdd, 0xde, 0x1c,
|
|
0x1b, 0xb3, 0xcc, 0x27, 0x27, 0x57, 0x5a, 0xd9,
|
|
0xea, 0x99, 0x27, 0x23, 0xa6, 0x0e, 0xea, 0x9c,
|
|
0x0d, 0x85, 0xcb, 0x72, 0xeb, 0xd7, 0x93, 0xe3,
|
|
0xfe, 0xf7, 0x5c, 0xc5, 0x5b, 0x75, 0x8c, 0x47,
|
|
0x0a, 0x0e, 0xc4, 0x1a, 0xda, 0xef, 0x75, 0xe5,
|
|
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0xfb, 0x92, 0xce, 0xaa, 0x00, 0x21, 0x20,
|
|
0xcb, 0x73, 0x25, 0x80, 0x46, 0x78, 0x4f, 0xe5,
|
|
0x34, 0xf6, 0x91, 0x13, 0x7f, 0xc8, 0x8d, 0xdc,
|
|
0x81, 0x04, 0xb7, 0x0d, 0x49, 0x85, 0x2e, 0x12,
|
|
0x7a, 0x07, 0x23, 0xe9, 0x13, 0xa4, 0x6d, 0x8c
|
|
};
|
|
WOLFSSL_BUFFER_INFO msg;
|
|
/* Offset into ClientHello message data of first cipher suite. */
|
|
const int csOff = 78;
|
|
/* Server cipher list. */
|
|
const char* serverCs = "TLS13-AES256-GCM-SHA384:TLS13-AES128-GCM-SHA256";
|
|
/* Suite list with duplicates. */
|
|
const char* dupCs = "TLS13-AES128-GCM-SHA256:"
|
|
"TLS13-AES128-GCM-SHA256:"
|
|
"TLS13-AES256-GCM-SHA384:"
|
|
"TLS13-AES256-GCM-SHA384:"
|
|
"TLS13-AES128-GCM-SHA256";
|
|
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_SET_CIPHER_BYTES)
|
|
const byte dupCsBytes[] = { TLS13_BYTE, TLS_AES_256_GCM_SHA384,
|
|
TLS13_BYTE, TLS_AES_256_GCM_SHA384,
|
|
TLS13_BYTE, TLS_AES_128_GCM_SHA256,
|
|
TLS13_BYTE, TLS_AES_128_GCM_SHA256,
|
|
TLS13_BYTE, TLS_AES_256_GCM_SHA384 };
|
|
#endif
|
|
|
|
/* Set up wolfSSL context. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, eccCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, eccKeyFile,
|
|
CERT_FILETYPE));
|
|
/* Read from 'msg'. */
|
|
wolfSSL_SetIORecv(ctx, CsRecv);
|
|
/* No where to send to - dummy sender. */
|
|
wolfSSL_SetIOSend(ctx, CsSend);
|
|
|
|
/* Test cipher suite list with many copies of a cipher suite. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = clientHello;
|
|
msg.length = (unsigned int)sizeof(clientHello);
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
/* Force server to have as many occurrences of same cipher suite as
|
|
* possible. */
|
|
if (ssl != NULL) {
|
|
Suites* suites = (Suites*)WOLFSSL_SUITES(ssl);
|
|
suites->suiteSz = WOLFSSL_MAX_SUITE_SZ;
|
|
for (i = 0; i < suites->suiteSz; i += 2) {
|
|
suites->suites[i + 0] = TLS13_BYTE;
|
|
suites->suites[i + 1] = TLS_AES_128_GCM_SHA256;
|
|
}
|
|
}
|
|
/* Test multiple occurrences of same cipher suite. */
|
|
ExpectIntEQ(wolfSSL_accept_TLSv13(ssl),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* Set client order opposite to server order:
|
|
* TLS13-AES128-GCM-SHA256:TLS13-AES256-GCM-SHA384 */
|
|
clientHello[csOff + 0] = TLS13_BYTE;
|
|
clientHello[csOff + 1] = TLS_AES_128_GCM_SHA256;
|
|
clientHello[csOff + 2] = TLS13_BYTE;
|
|
clientHello[csOff + 3] = TLS_AES_256_GCM_SHA384;
|
|
|
|
/* Test server order negotiation. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = clientHello;
|
|
msg.length = (unsigned int)sizeof(clientHello);
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
/* Server order: TLS13-AES256-GCM-SHA384:TLS13-AES128-GCM-SHA256 */
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl, serverCs), WOLFSSL_SUCCESS);
|
|
/* Negotiate cipher suites in server order: TLS13-AES256-GCM-SHA384 */
|
|
ExpectIntEQ(wolfSSL_accept_TLSv13(ssl),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
/* Check refined order - server order. */
|
|
ExpectIntEQ(ssl->suites->suiteSz, 4);
|
|
ExpectIntEQ(ssl->suites->suites[0], TLS13_BYTE);
|
|
ExpectIntEQ(ssl->suites->suites[1], TLS_AES_256_GCM_SHA384);
|
|
ExpectIntEQ(ssl->suites->suites[2], TLS13_BYTE);
|
|
ExpectIntEQ(ssl->suites->suites[3], TLS_AES_128_GCM_SHA256);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* Test client order negotiation. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = clientHello;
|
|
msg.length = (unsigned int)sizeof(clientHello);
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
/* Server order: TLS13-AES256-GCM-SHA384:TLS13-AES128-GCM-SHA256 */
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl, serverCs), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseClientSuites(ssl), 0);
|
|
/* Negotiate cipher suites in client order: TLS13-AES128-GCM-SHA256 */
|
|
ExpectIntEQ(wolfSSL_accept_TLSv13(ssl),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
/* Check refined order - client order. */
|
|
ExpectIntEQ(ssl->suites->suiteSz, 4);
|
|
ExpectIntEQ(ssl->suites->suites[0], TLS13_BYTE);
|
|
ExpectIntEQ(ssl->suites->suites[1], TLS_AES_128_GCM_SHA256);
|
|
ExpectIntEQ(ssl->suites->suites[2], TLS13_BYTE);
|
|
ExpectIntEQ(ssl->suites->suites[3], TLS_AES_256_GCM_SHA384);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* Check duplicate detection is working. */
|
|
ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx, dupCs), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ctx->suites->suiteSz, 4);
|
|
ExpectIntEQ(ctx->suites->suites[0], TLS13_BYTE);
|
|
ExpectIntEQ(ctx->suites->suites[1], TLS_AES_128_GCM_SHA256);
|
|
ExpectIntEQ(ctx->suites->suites[2], TLS13_BYTE);
|
|
ExpectIntEQ(ctx->suites->suites[3], TLS_AES_256_GCM_SHA384);
|
|
|
|
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_SET_CIPHER_BYTES)
|
|
ExpectIntEQ(wolfSSL_CTX_set_cipher_list_bytes(ctx, dupCsBytes,
|
|
sizeof(dupCsBytes)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ctx->suites->suiteSz, 4);
|
|
ExpectIntEQ(ctx->suites->suites[0], TLS13_BYTE);
|
|
ExpectIntEQ(ctx->suites->suites[1], TLS_AES_256_GCM_SHA384);
|
|
ExpectIntEQ(ctx->suites->suites[2], TLS13_BYTE);
|
|
ExpectIntEQ(ctx->suites->suites[3], TLS_AES_128_GCM_SHA256);
|
|
#endif
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256) && defined(HAVE_ECC) && \
|
|
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
|
/* Suites has a bitfield member (setSuites:1) trailed by compiler padding
|
|
* whose value is indeterminate, so a whole-struct XMEMCMP is unreliable.
|
|
* Compare the meaningful members individually instead. */
|
|
static int test_tls13_suites_eq(const Suites* a, const Suites* b)
|
|
{
|
|
return a->suiteSz == b->suiteSz &&
|
|
a->hashSigAlgoSz == b->hashSigAlgoSz &&
|
|
a->setSuites == b->setSuites &&
|
|
XMEMCMP(a->suites, b->suites, sizeof(a->suites)) == 0 &&
|
|
XMEMCMP(a->hashSigAlgo, b->hashSigAlgo, sizeof(a->hashSigAlgo))
|
|
== 0;
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_cipher_list_no_tls13_ctx(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256) && defined(HAVE_ECC) && \
|
|
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
|
const char* tls12Suite = "ECDHE-RSA-AES128-GCM-SHA256";
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
Suites suitesBefore;
|
|
|
|
/* ctx->method caps the connection at TLS 1.2, so a cipher list that
|
|
* names only TLS 1.3 suites can never take effect on it. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
|
|
/* Configure a real <= TLS 1.2 suite first so the "unchanged" checks
|
|
* below compare against a known, non-empty suite list instead of
|
|
* two empty ones. */
|
|
ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx, tls12Suite),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull((ctx != NULL) ? ctx->suites : NULL);
|
|
if (ctx != NULL && ctx->suites != NULL) {
|
|
ExpectIntGT(ctx->suites->suiteSz, 0);
|
|
XMEMCPY(&suitesBefore, ctx->suites, sizeof(Suites));
|
|
}
|
|
|
|
/* A TLS 1.3-only list is unusable on this ctx: the call must fail and
|
|
* must not mutate the previously configured suites. */
|
|
ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx, "TLS13-AES128-GCM-SHA256"),
|
|
WOLFSSL_FAILURE);
|
|
if (ctx != NULL && ctx->suites != NULL) {
|
|
ExpectIntEQ(test_tls13_suites_eq(ctx->suites, &suitesBefore), 1);
|
|
}
|
|
|
|
/* ssl->version inherits the same TLS 1.2 cap from ctx->method, so
|
|
* wolfSSL_set_cipher_list() must behave the same as the ctx call
|
|
* above. This exercises the ssl->version branch of the condition,
|
|
* not just the ctx->method->version branch. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl, tls12Suite), WOLFSSL_SUCCESS);
|
|
ExpectNotNull((ssl != NULL) ? ssl->suites : NULL);
|
|
if (ssl != NULL && ssl->suites != NULL) {
|
|
ExpectIntGT(ssl->suites->suiteSz, 0);
|
|
XMEMCPY(&suitesBefore, ssl->suites, sizeof(Suites));
|
|
}
|
|
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl, "TLS13-AES128-GCM-SHA256"),
|
|
WOLFSSL_FAILURE);
|
|
if (ssl != NULL && ssl->suites != NULL) {
|
|
ExpectIntEQ(test_tls13_suites_eq(ssl->suites, &suitesBefore), 1);
|
|
}
|
|
wolfSSL_free(ssl);
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)\
|
|
&& !defined(NO_PSK)
|
|
static unsigned int test_tls13_bad_psk_binder_client_cb(WOLFSSL* ssl,
|
|
const char* hint, char* identity, unsigned int id_max_len,
|
|
unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)hint;
|
|
(void)key_max_len;
|
|
|
|
/* see internal.h MAX_PSK_ID_LEN for PSK identity limit */
|
|
XSTRNCPY(identity, "Client_identity", id_max_len);
|
|
|
|
key[0] = 0x20;
|
|
return 1;
|
|
}
|
|
|
|
static unsigned int test_tls13_bad_psk_binder_server_cb(WOLFSSL* ssl,
|
|
const char* id, unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)id;
|
|
(void)key_max_len;
|
|
/* zero means error */
|
|
key[0] = 0x10;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_bad_psk_binder(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)\
|
|
&& !defined(NO_PSK)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
|
|
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_psk_client_callback(ssl_c, test_tls13_bad_psk_binder_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_bad_psk_binder_server_cb);
|
|
|
|
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)),
|
|
WC_NO_ERR_TRACE(BAD_BINDER));
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_rx.code, decrypt_error);
|
|
ExpectIntEQ(h.last_rx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD) && \
|
|
!defined(NO_PSK)
|
|
static unsigned int test_tls13_psk_no_cert_client_cb(WOLFSSL* ssl,
|
|
const char* hint, char* identity, unsigned int id_max_len,
|
|
unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)hint;
|
|
(void)key_max_len;
|
|
|
|
/* Offer a PSK so the client sends a pre_shared_key extension. */
|
|
XSTRNCPY(identity, "Client_identity", id_max_len);
|
|
key[0] = 0x20;
|
|
return 1;
|
|
}
|
|
|
|
static unsigned int test_tls13_psk_no_cert_server_cb(WOLFSSL* ssl,
|
|
const char* id, unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)id;
|
|
(void)key;
|
|
(void)key_max_len;
|
|
|
|
/* Reject every identity so the server finds no matching PSK. */
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* When no offered PSK matches and the server has no certificate to fall back
|
|
* to, the server must abort the handshake with BAD_BINDER rather than silently
|
|
* continuing. This covers both configurations:
|
|
* - NO_CERTS defined: the certificate fall-back branch is compiled out.
|
|
* - certificates compiled in but none loaded: ssl->buffers.certificate is
|
|
* NULL, so the runtime check takes the same abort path.
|
|
* The contexts are built by hand (no certificate loaded) so the test exercises
|
|
* whichever branch the build provides.
|
|
* When certificates are compiled in, a second connection sets a certificate
|
|
* and key against the server context and verifies the opposite branch: the
|
|
* non-matching PSK is ignored and the handshake falls back to a full
|
|
* certificate handshake instead of aborting. */
|
|
int test_tls13_psk_no_cert_bad_binder(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD) && \
|
|
!defined(NO_PSK)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
/* Don't use test_memio_setup(): it loads a default server certificate,
|
|
* which would let the server fall back to a certificate handshake. Build
|
|
* the contexts by hand so the server has no certificate loaded. */
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
if (ctx_c != NULL) {
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
}
|
|
if (ctx_s != NULL) {
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
}
|
|
|
|
/* Set the PSK callbacks on the contexts, not the SSL objects: with
|
|
* certificates compiled in, creating a server-side SSL object without a
|
|
* certificate and key fails (NO_PRIVATE_KEY) unless ctx->havePSK is
|
|
* already set when wolfSSL_new() is called. */
|
|
wolfSSL_CTX_set_psk_client_callback(ctx_c,
|
|
test_tls13_psk_no_cert_client_cb);
|
|
wolfSSL_CTX_set_psk_server_callback(ctx_s,
|
|
test_tls13_psk_no_cert_server_cb);
|
|
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
if (ssl_c != NULL) {
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
}
|
|
if (ssl_s != NULL) {
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
}
|
|
|
|
/* Confirm the precondition: the server really has no certificate. */
|
|
#ifndef NO_CERTS
|
|
if (ssl_s != NULL) {
|
|
ExpectNull(ssl_s->buffers.certificate);
|
|
}
|
|
#endif
|
|
|
|
/* Client sends ClientHello (with PSK) and waits for the response. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Server processes ClientHello: no PSK matches and no certificate is
|
|
* available, so it must abort with BAD_BINDER. */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(BAD_BINDER));
|
|
|
|
/* Client reads the server's alert: BAD_BINDER maps to a fatal
|
|
* decrypt_error alert (see TranslateErrorToAlert). */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_rx.code, decrypt_error);
|
|
ExpectIntEQ(h.last_rx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
|
|
#ifndef NO_CERTS
|
|
/* Conversely, with a certificate and key set against the server context,
|
|
* a non-matching PSK must not leak the mismatch: the server ignores the
|
|
* PSK and falls back to a full certificate handshake. test_memio_setup()
|
|
* loads the default CA, server certificate and key. */
|
|
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_psk_client_callback(ssl_c, test_tls13_psk_no_cert_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_psk_no_cert_server_cb);
|
|
|
|
/* Confirm the precondition: the server has a certificate this time. */
|
|
if (ssl_s != NULL) {
|
|
ExpectNotNull(ssl_s->buffers.certificate);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif /* !NO_CERTS */
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD) && \
|
|
!defined(NO_PSK)
|
|
#define TEST_TLS13_PSK_AGE_KNOWN_ID "known_id"
|
|
#define TEST_TLS13_PSK_AGE_UNKNOWN_ID "unknown_id"
|
|
|
|
/* Identity the client offers; TLS 1.3 has no identity hint to carry it, so a
|
|
* file-scope value serves both the known and the unknown case. */
|
|
static const char* test_tls13_psk_age_identity = TEST_TLS13_PSK_AGE_KNOWN_ID;
|
|
|
|
static unsigned int test_tls13_psk_age_client_cb(WOLFSSL* ssl,
|
|
const char* hint, char* identity, unsigned int id_max_len,
|
|
unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)hint;
|
|
if (id_max_len <= XSTRLEN(test_tls13_psk_age_identity) || key_max_len < 32)
|
|
return 0;
|
|
XSTRNCPY(identity, test_tls13_psk_age_identity, id_max_len);
|
|
XMEMSET(key, 0x5A, 32);
|
|
return 32;
|
|
}
|
|
|
|
/* Set when the server callback recognises the identity, so the test can prove
|
|
* the known-identity run really exercised the matched path. */
|
|
static int test_tls13_psk_age_matched = 0;
|
|
|
|
static unsigned int test_tls13_psk_age_server_cb(WOLFSSL* ssl, const char* id,
|
|
unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
if (id == NULL || key_max_len < 32)
|
|
return 0;
|
|
if (XSTRCMP(id, TEST_TLS13_PSK_AGE_KNOWN_ID) != 0)
|
|
return 0;
|
|
test_tls13_psk_age_matched = 1;
|
|
XMEMSET(key, 0x5A, 32);
|
|
return 32;
|
|
}
|
|
|
|
/* Overwrite the 4-byte obfuscated_ticket_age that follows the identity in the
|
|
* ClientHello's pre_shared_key extension. wolfSSL's own client always sends
|
|
* zero there, so the field has to be rewritten on the wire to model a hostile
|
|
* peer. Returns 1 when the identity was found and patched. */
|
|
static int test_tls13_psk_age_patch(byte* buf, int len, const char* id)
|
|
{
|
|
int idLen = (int)XSTRLEN(id);
|
|
int i;
|
|
|
|
/* Anchor on the 2-byte identity length that precedes the identity in
|
|
* PskIdentity, so a stray copy of the string cannot be matched. */
|
|
for (i = 2; i + idLen + 4 <= len; i++) {
|
|
if (buf[i - 2] != ((idLen >> 8) & 0xff) || buf[i - 1] != (idLen & 0xff))
|
|
continue;
|
|
if (XMEMCMP(buf + i, id, (size_t)idLen) == 0) {
|
|
buf[i + idLen + 0] = 0x00;
|
|
buf[i + idLen + 1] = 0x00;
|
|
buf[i + idLen + 2] = 0x00;
|
|
buf[i + idLen + 3] = 0x01;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Drive one ClientHello carrying the given identity and a non-zero age, and
|
|
* report the alert the server sends back. */
|
|
static int test_tls13_psk_age_alert(const char* identity, int* alertOut,
|
|
int expectMatch, int patchAge)
|
|
{
|
|
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;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_tls13_psk_age_identity = identity;
|
|
test_tls13_psk_age_matched = 0;
|
|
|
|
/* No certificate on the server, so a non-matching PSK has nothing to fall
|
|
* back to and the handshake has to abort. */
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
if (ctx_c != NULL) {
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
}
|
|
if (ctx_s != NULL) {
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
}
|
|
wolfSSL_CTX_set_psk_client_callback(ctx_c, test_tls13_psk_age_client_cb);
|
|
wolfSSL_CTX_set_psk_server_callback(ctx_s, test_tls13_psk_age_server_cb);
|
|
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
if (ssl_c != NULL) {
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
}
|
|
if (ssl_s != NULL) {
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
}
|
|
|
|
if (!patchAge) {
|
|
/* Control: the same fixture, ClientHello untouched. It must complete,
|
|
* so a failure in the patched runs is attributable to the age rewrite
|
|
* and not to a fixture that never negotiated a PSK at all. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->options.isPSK, 1);
|
|
}
|
|
ExpectIntEQ(test_tls13_psk_age_matched, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Client emits the ClientHello into the server inbound buffer. */
|
|
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_tls13_psk_age_patch(test_ctx.s_buff, test_ctx.s_len,
|
|
identity), 1);
|
|
|
|
/* Server must reject, and must not reveal whether it knew the identity.
|
|
* The server-side error is asserted too, matching the convention of the
|
|
* neighbouring PSK tests, so an abort for an unrelated reason that also
|
|
* maps to decrypt_error cannot pass unnoticed. */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(BAD_BINDER));
|
|
|
|
/* isPSK is set only after FindPskSuite() reported a match and the early
|
|
* secret was derived, so it proves the known run really took the matched
|
|
* path. The callback flag alone would not: the callback runs before the
|
|
* ciphersuite match that decides whether the PSK is used at all. */
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->options.isPSK, expectMatch);
|
|
}
|
|
ExpectIntEQ(test_tls13_psk_age_matched, expectMatch);
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
if (alertOut != NULL)
|
|
*alertOut = h.last_rx.code;
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* A ClientHello that offers a known PSK identity with a non-zero
|
|
* obfuscated_ticket_age must fail exactly like one offering an unknown
|
|
* identity, or an unauthenticated peer could enumerate the server's PSK
|
|
* identities. The two runs reach that same alert by different routes: the
|
|
* known identity is accepted by FindPsk(), so the server derives the binder
|
|
* and rejects it at the comparison (the age rewrite invalidated the
|
|
* transcript), while the unknown identity never matches and aborts earlier.
|
|
* Before the fix the known run returned PSK_KEY_ERROR (illegal_parameter)
|
|
* and the unknown run BAD_BINDER (decrypt_error).
|
|
*
|
|
* A control run with an untouched ClientHello completes the handshake first,
|
|
* so the two rejecting runs cannot pass by way of a fixture that never
|
|
* negotiated a PSK.
|
|
*
|
|
* The accept side for a non-zero age, where a conformant client sends one and
|
|
* the handshake completes, is not covered: wolfSSL's client always sends zero,
|
|
* and rewriting the age on the wire necessarily invalidates the binder the
|
|
* client already computed. */
|
|
int test_tls13_psk_age_no_identity_oracle(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD) && \
|
|
!defined(NO_PSK)
|
|
int knownAlert = -1;
|
|
int unknownAlert = -1;
|
|
|
|
ExpectIntEQ(test_tls13_psk_age_alert(TEST_TLS13_PSK_AGE_KNOWN_ID,
|
|
NULL, 1, 0), TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_psk_age_alert(TEST_TLS13_PSK_AGE_KNOWN_ID,
|
|
&knownAlert, 1, 1), TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_psk_age_alert(TEST_TLS13_PSK_AGE_UNKNOWN_ID,
|
|
&unknownAlert, 0, 1), TEST_SUCCESS);
|
|
|
|
ExpectIntEQ(knownAlert, unknownAlert);
|
|
ExpectIntEQ(knownAlert, decrypt_error);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
#if defined(HAVE_RPK) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER)
|
|
|
|
#define svrRpkCertFile "./certs/rpk/server-cert-rpk.der"
|
|
#define clntRpkCertFile "./certs/rpk/client-cert-rpk.der"
|
|
|
|
#if defined(WOLFSSL_ALWAYS_VERIFY_CB) && defined(WOLFSSL_TLS13)
|
|
static int MyRpkVerifyCb(int mode, WOLFSSL_X509_STORE_CTX* strctx)
|
|
{
|
|
int ret = WOLFSSL_SUCCESS;
|
|
(void)mode;
|
|
(void)strctx;
|
|
WOLFSSL_ENTER("MyRpkVerifyCb");
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_ALWAYS_VERIFY_CB && WOLFSSL_TLS13 */
|
|
|
|
#if !defined(NO_SHA256)
|
|
/* Pin the peer's Raw Public Key so it is authenticated out of band (RFC 7250).
|
|
* In this test the RPK cert files are DER SubjectPublicKeyInfo (passed as
|
|
* WOLFSSL_FILETYPE_ASN1), which is exactly what is sent on the wire, so the raw
|
|
* file bytes are the pin. PEM files are X.509 and are left unpinned. */
|
|
static int test_rpk_pin_peer(WOLFSSL_CTX* ctx, const char* file, int fmt)
|
|
{
|
|
byte* buf = NULL;
|
|
size_t sz = 0;
|
|
int ret = 0;
|
|
|
|
if (fmt != WOLFSSL_FILETYPE_ASN1)
|
|
return 0; /* not an RPK file - nothing to pin */
|
|
if (load_file(file, &buf, &sz) != 0)
|
|
return -1;
|
|
if (wolfSSL_CTX_set_expected_rpk(ctx, buf, (unsigned int)sz)
|
|
!= WOLFSSL_SUCCESS) {
|
|
ret = -1;
|
|
}
|
|
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return ret;
|
|
}
|
|
#endif /* !NO_SHA256 */
|
|
|
|
static WC_INLINE int test_rpk_memio_setup(
|
|
struct test_memio_ctx *ctx,
|
|
WOLFSSL_CTX **ctx_c,
|
|
WOLFSSL_CTX **ctx_s,
|
|
WOLFSSL **ssl_c,
|
|
WOLFSSL **ssl_s,
|
|
method_provider method_c,
|
|
method_provider method_s,
|
|
const char* certfile_c, int fmt_cc, /* client cert file path and format */
|
|
const char* certfile_s, int fmt_cs, /* server cert file path and format */
|
|
const char* pkey_c, int fmt_kc, /* client private key and format */
|
|
const char* pkey_s, int fmt_ks /* server private key and format */
|
|
)
|
|
{
|
|
int ret;
|
|
if (ctx_c != NULL && *ctx_c == NULL) {
|
|
*ctx_c = wolfSSL_CTX_new(method_c());
|
|
if (*ctx_c == NULL) {
|
|
return -1;
|
|
}
|
|
wolfSSL_CTX_set_verify(*ctx_c, WOLFSSL_VERIFY_PEER, NULL);
|
|
|
|
ret = wolfSSL_CTX_load_verify_locations(*ctx_c, caCertFile, 0);
|
|
if (ret != WOLFSSL_SUCCESS) {
|
|
return -1;
|
|
}
|
|
wolfSSL_SetIORecv(*ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(*ctx_c, test_memio_write_cb);
|
|
|
|
ret = wolfSSL_CTX_use_certificate_file(*ctx_c, certfile_c, fmt_cc);
|
|
if (ret != WOLFSSL_SUCCESS) {
|
|
return -1;
|
|
}
|
|
ret = wolfSSL_CTX_use_PrivateKey_file(*ctx_c, pkey_c, fmt_kc);
|
|
if (ret != WOLFSSL_SUCCESS) {
|
|
return -1;
|
|
}
|
|
/* This helper sets WOLFSSL_VERIFY_PEER on both ends, so the RPK
|
|
* handshake cases below rely on the peer key being pinned here to
|
|
* complete (otherwise they fail closed). Pinning needs SHA-256; the
|
|
* RPK + NO_SHA256 combination is not exercised because the unit test
|
|
* suite does not build under NO_SHA256 (e.g. test_random.c). */
|
|
#if !defined(NO_SHA256)
|
|
/* client must trust the server's RPK (if the server presents one) */
|
|
if (test_rpk_pin_peer(*ctx_c, certfile_s, fmt_cs) != 0) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (ctx_s != NULL && *ctx_s == NULL) {
|
|
*ctx_s = wolfSSL_CTX_new(method_s());
|
|
if (*ctx_s == NULL) {
|
|
return -1;
|
|
}
|
|
wolfSSL_CTX_set_verify(*ctx_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
|
|
ret = wolfSSL_CTX_load_verify_locations(*ctx_s, cliCertFile, 0);
|
|
if (ret != WOLFSSL_SUCCESS) {
|
|
return -1;
|
|
}
|
|
|
|
ret = wolfSSL_CTX_use_PrivateKey_file(*ctx_s, pkey_s, fmt_ks);
|
|
if (ret != WOLFSSL_SUCCESS) {
|
|
return -1;
|
|
}
|
|
ret = wolfSSL_CTX_use_certificate_file(*ctx_s, certfile_s, fmt_cs);
|
|
if (ret != WOLFSSL_SUCCESS) {
|
|
return -1;
|
|
}
|
|
#if !defined(NO_SHA256)
|
|
/* server must trust the client's RPK (if the client presents one) */
|
|
if (test_rpk_pin_peer(*ctx_s, certfile_c, fmt_cc) != 0) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
wolfSSL_SetIORecv(*ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(*ctx_s, test_memio_write_cb);
|
|
if (ctx->s_ciphers != NULL) {
|
|
ret = wolfSSL_CTX_set_cipher_list(*ctx_s, ctx->s_ciphers);
|
|
if (ret != WOLFSSL_SUCCESS) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ctx_c != NULL && ssl_c != NULL) {
|
|
*ssl_c = wolfSSL_new(*ctx_c);
|
|
if (*ssl_c == NULL) {
|
|
return -1;
|
|
}
|
|
wolfSSL_SetIOWriteCtx(*ssl_c, ctx);
|
|
wolfSSL_SetIOReadCtx(*ssl_c, ctx);
|
|
}
|
|
if (ctx_s != NULL && ssl_s != NULL) {
|
|
*ssl_s = wolfSSL_new(*ctx_s);
|
|
if (*ssl_s == NULL) {
|
|
return -1;
|
|
}
|
|
wolfSSL_SetIOWriteCtx(*ssl_s, ctx);
|
|
wolfSSL_SetIOReadCtx(*ssl_s, ctx);
|
|
#if !defined(NO_DH)
|
|
SetDH(*ssl_s);
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* HAVE_RPK && !NO_TLS && !NO_WOLFSSL_CLIENT && !NO_WOLFSSL_SERVER */
|
|
|
|
|
|
#if defined(HAVE_RPK) && defined(WOLFSSL_CHAIN_VERIFY_CB) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_TLS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
static int test_chain_verify_cb_rpk_cb(WOLFSSL* ssl,
|
|
const WOLFSSL_BUFFER_INFO* certs, int certsSz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)certs;
|
|
(void)certsSz;
|
|
(*(int*)ctx)++;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* A raw public key is not supported with the chain verify callback. */
|
|
int test_tls13_chain_verify_cb_rpk(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_RPK) && defined(WOLFSSL_CHAIN_VERIFY_CB) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_TLS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char certType[MAX_CLIENT_CERT_TYPE_CNT];
|
|
int calls = 0;
|
|
int i;
|
|
|
|
certType[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
|
|
/* First with raw public keys configured before the callback: the setters
|
|
* refuse it. Then with the callback set first: the handshake fails before
|
|
* the callback is consulted. */
|
|
for (i = 0; i < 2; i++) {
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
clntRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
if (i == 1) {
|
|
ExpectIntEQ(wolfSSL_SetChainVerifyCb(ssl_c,
|
|
test_chain_verify_cb_rpk_cb), WOLFSSL_SUCCESS);
|
|
wolfSSL_SetChainVerifyCtx(ssl_c, &calls);
|
|
}
|
|
|
|
/* Both ends negotiate raw public keys only. */
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_c, certType, 1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType, 1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_s, certType, 1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType, 1),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
if (i == 0) {
|
|
ExpectIntEQ(wolfSSL_SetChainVerifyCb(ssl_c,
|
|
test_chain_verify_cb_rpk_cb),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E));
|
|
ExpectIntEQ(wolfSSL_SetChainVerifyCb(ssl_s,
|
|
test_chain_verify_cb_rpk_cb),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E));
|
|
}
|
|
else {
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E));
|
|
ExpectIntEQ(calls, 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_rpk_handshake(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_RPK) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
#ifdef WOLFSSL_TLS13
|
|
int ret = 0;
|
|
#endif
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int err;
|
|
char certType_c[MAX_CLIENT_CERT_TYPE_CNT];
|
|
char certType_s[MAX_CLIENT_CERT_TYPE_CNT];
|
|
int typeCnt_c;
|
|
int typeCnt_s;
|
|
int tp = 0;
|
|
#if defined(WOLFSSL_ALWAYS_VERIFY_CB) && defined(WOLFSSL_TLS13)
|
|
int isServer;
|
|
#endif
|
|
|
|
(void)err;
|
|
(void)typeCnt_c;
|
|
(void)typeCnt_s;
|
|
(void)certType_c;
|
|
(void)certType_s;
|
|
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
/* TLS1.2
|
|
* Both client and server load x509 cert and start handshaking.
|
|
* Check no negotiation occurred.
|
|
*/
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method,
|
|
cliCertFile, CERT_FILETYPE,
|
|
svrCertFile, CERT_FILETYPE,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE)
|
|
, 0);
|
|
|
|
|
|
/* set client certificate type in client end */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_c = 2;
|
|
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_s[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_s = 2;
|
|
|
|
/* both client and server do not call client/server_cert_type APIs,
|
|
* expecting default settings works and no negotiation performed.
|
|
*/
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* confirm no negotiation occurred */
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ((int)tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
(void)typeCnt_c;
|
|
(void)typeCnt_s;
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_TLS13
|
|
/* Both client and server load x509 cert and start handshaking.
|
|
* Check no negotiation occurred.
|
|
*/
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
cliCertFile, CERT_FILETYPE,
|
|
svrCertFile, CERT_FILETYPE,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
/* set client certificate type in client end */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_c = 2;
|
|
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_s[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_s = 2;
|
|
|
|
/* both client and server do not call client/server_cert_type APIs,
|
|
* expecting default settings works and no negotiation performed.
|
|
*/
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* confirm no negotiation occurred */
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ((int)tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
/* x509 handshake: peer cert was verified against a CA, so the verify
|
|
* result stays WOLFSSL_X509_V_OK. This is the contrast case for the
|
|
* RPK checks below. */
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_c), WOLFSSL_X509_V_OK);
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_s), WOLFSSL_X509_V_OK);
|
|
#endif
|
|
|
|
(void)typeCnt_c;
|
|
(void)typeCnt_s;
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
|
|
|
|
/* Both client and server load RPK cert and start handshaking.
|
|
* Confirm negotiated cert types match as expected.
|
|
*/
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
clntRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
/* set client certificate type in client end */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_c = 2;
|
|
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_s[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_s = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_c, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set server certificate type in client end */
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set client certificate type in server end */
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_s, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set server certificate type in server end */
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
/* TLS1.3 RPK: an RPK cert (RFC 7250) has no issuer and no signature chain,
|
|
* so it is only authenticated by the out-of-band pin established in
|
|
* test_rpk_memio_setup(). Because the peer's key was pinned, the handshake
|
|
* completes and wolfSSL_get_verify_result() reports WOLFSSL_X509_V_OK. */
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_c), WOLFSSL_X509_V_OK);
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_s), WOLFSSL_X509_V_OK);
|
|
#endif
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
#endif
|
|
|
|
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
/* TLS1.2
|
|
* Both client and server load RPK cert and start handshaking.
|
|
* Confirm negotiated cert types match as expected.
|
|
*/
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method,
|
|
clntRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
/* set client certificate type in client end */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_c = 2;
|
|
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_s[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_s = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_c, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set server certificate type in client end */
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set client certificate type in server end */
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_s, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set server certificate type in server end */
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
if (test_memio_do_handshake(ssl_c, ssl_s, 10, NULL) != 0)
|
|
return TEST_FAIL;
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
/* TLS1.2 RPK: same as the TLS1.3 case above - the peer's key was pinned
|
|
* out of band, so the verify result is WOLFSSL_X509_V_OK. */
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_c), WOLFSSL_X509_V_OK);
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_s), WOLFSSL_X509_V_OK);
|
|
#endif
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
#endif
|
|
|
|
|
|
#ifdef WOLFSSL_TLS13
|
|
/* Both client and server load x509 cert.
|
|
* Have client call set_client_cert_type with both RPK and x509.
|
|
* This doesn't makes client add client cert type extension to ClientHello,
|
|
* since it does not load RPK cert actually.
|
|
*/
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
cliCertFile, CERT_FILETYPE,
|
|
svrCertFile, CERT_FILETYPE,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
/* set client certificate type in client end
|
|
*
|
|
* client indicates both RPK and x509 certs are available but loaded RPK
|
|
* cert only. It does not have client add client-cert-type extension in CH.
|
|
*/
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_c = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_c, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* client indicates both RPK and x509 certs are acceptable */
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_s[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_s = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* server indicates both RPK and x509 certs are acceptable */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_c = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_s, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* server should indicate only RPK cert is available */
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_s[1] = -1;
|
|
typeCnt_s = 1;
|
|
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
if (test_memio_do_handshake(ssl_c, ssl_s, 10, NULL) != 0)
|
|
return TEST_FAIL;
|
|
|
|
/* Negotiation for client-cert-type should NOT happen. Therefore -1 should
|
|
* be returned as cert type.
|
|
*/
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_X509);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_X509);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
|
|
|
|
/* Have client load RPK cert and have server load x509 cert.
|
|
* Check the negotiation result from both ends.
|
|
*/
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
clntRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
svrCertFile, CERT_FILETYPE,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
/* have client tell to use RPK cert */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = -1;
|
|
typeCnt_c = 1;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_c, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* have client tell to accept both RPK and x509 cert */
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_s[1] = WOLFSSL_CERT_TYPE_RPK;
|
|
typeCnt_s = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* have server accept to both RPK and x509 cert */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_RPK;
|
|
typeCnt_c = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_s, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* does not call wolfSSL_set_server_cert_type intentionally in sesrver
|
|
* end, expecting the default setting works.
|
|
*/
|
|
|
|
|
|
if (test_memio_do_handshake(ssl_c, ssl_s, 10, NULL) != 0)
|
|
return TEST_FAIL;
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_X509);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_X509);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
|
|
|
|
/* Have both client and server load RPK cert, however, have server
|
|
* indicate its cert type x509.
|
|
* Client is expected to detect the cert type mismatch then to send alert
|
|
* with "unsupported_certificate".
|
|
*/
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
clntRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1, /* server sends RPK cert */
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
/* have client tell to use RPK cert */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = -1;
|
|
typeCnt_c = 1;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_c, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* have client tell to accept both RPK and x509 cert */
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_s[1] = WOLFSSL_CERT_TYPE_RPK;
|
|
typeCnt_s = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* have server accept to both RPK and x509 cert */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_RPK;
|
|
typeCnt_c = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_s, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* have server tell to use x509 cert intentionally. This will bring
|
|
* certificate type mismatch in client side.
|
|
*/
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_s[1] = -1;
|
|
typeCnt_s = 1;
|
|
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* expect client detect cert type mismatch then send Alert */
|
|
ret = test_memio_do_handshake(ssl_c, ssl_s, 10, NULL);
|
|
if (ret != -1)
|
|
return TEST_FAIL;
|
|
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, ret),
|
|
WC_NO_ERR_TRACE(UNSUPPORTED_CERTIFICATE));
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_X509);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_X509);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
|
|
|
|
/* Have client load x509 cert and server load RPK cert,
|
|
* however, have client indicate its cert type RPK.
|
|
* Server is expected to detect the cert type mismatch then to send alert
|
|
* with "unsupported_certificate".
|
|
*/
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
cliCertFile, CERT_FILETYPE,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
/* have client tell to use RPK cert intentionally */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = -1;
|
|
typeCnt_c = 1;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_c, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* have client tell to accept both RPK and x509 cert */
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_s[1] = WOLFSSL_CERT_TYPE_RPK;
|
|
typeCnt_s = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* have server accept to both RPK and x509 cert */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_RPK;
|
|
typeCnt_c = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_s, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* have server tell to use x509 cert intentionally. This will bring
|
|
* certificate type mismatch in client side.
|
|
*/
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_s[1] = -1;
|
|
typeCnt_s = 1;
|
|
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ret = test_memio_do_handshake(ssl_c, ssl_s, 10, NULL);
|
|
|
|
/* expect server detect cert type mismatch then send Alert */
|
|
ExpectIntNE(ret, 0);
|
|
err = wolfSSL_get_error(ssl_c, ret);
|
|
ExpectIntEQ(err, WC_NO_ERR_TRACE(UNSUPPORTED_CERTIFICATE));
|
|
|
|
/* client did not load RPK cert actually, so negotiation did not happen */
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_X509);
|
|
|
|
/* client did not load RPK cert actually, so negotiation did not happen */
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_UNKNOWN);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_X509);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
|
|
|
|
#if defined(WOLFSSL_ALWAYS_VERIFY_CB)
|
|
/* Both client and server load RPK cert and set certificate verify
|
|
* callbacks then start handshaking.
|
|
* Confirm both side can refer the peer's cert.
|
|
*/
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
clntRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
/* set client certificate type in client end */
|
|
certType_c[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_c[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_c = 2;
|
|
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType_s[1] = WOLFSSL_CERT_TYPE_X509;
|
|
typeCnt_s = 2;
|
|
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_c, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set server certificate type in client end */
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set client certificate type in server end */
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_s, certType_c, typeCnt_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set server certificate type in server end */
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* set certificate verify callback to both client and server */
|
|
isServer = 0;
|
|
wolfSSL_SetCertCbCtx(ssl_c, &isServer);
|
|
wolfSSL_set_verify(ssl_c, SSL_VERIFY_PEER, MyRpkVerifyCb);
|
|
|
|
isServer = 1;
|
|
wolfSSL_SetCertCbCtx(ssl_c, &isServer);
|
|
wolfSSL_set_verify(ssl_s, SSL_VERIFY_PEER, MyRpkVerifyCb);
|
|
|
|
ret = test_memio_do_handshake(ssl_c, ssl_s, 10, NULL);
|
|
if (ret != 0)
|
|
return TEST_FAIL;
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
#endif /* WOLFSSL_ALWAYS_VERIFY_CB */
|
|
#endif /* WOLFSSL_TLS13 */
|
|
|
|
#endif /* HAVE_RPK && (!WOLFSSL_NO_TLS12 || WOLFSSL_TLS13) */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Regression: a peer must not present a raw public key (RPK) that was never
|
|
* negotiated. Neither side calls set_client/server_cert_type, so RPK is not
|
|
* negotiated and the default type is X.509; a received bare key must be
|
|
* rejected instead of accepted without any chain verification (auth bypass).
|
|
* Covers both directions: server presenting an RPK to the client, and client
|
|
* presenting an RPK to the server. */
|
|
#if defined(HAVE_RPK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_RSA) && !defined(NO_FILESYSTEM)
|
|
static int rpk_waive_issuer_cb(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
(void)preverify;
|
|
/* the common "accept any issuer" pattern: waive issuer-lookup errors */
|
|
if (store->error == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_rpk_unnegotiated_not_overridable(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_RPK) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_RSA) && !defined(NO_FILESYSTEM)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char certType_s[MAX_CLIENT_CERT_TYPE_CNT];
|
|
int typeCnt_s;
|
|
int ret;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
cliCertFile, CERT_FILETYPE,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE )
|
|
, 0);
|
|
|
|
certType_s[0] = WOLFSSL_CERT_TYPE_X509;
|
|
certType_s[1] = -1;
|
|
typeCnt_s = 1;
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType_s, typeCnt_s),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, rpk_waive_issuer_cb);
|
|
|
|
ret = test_memio_do_handshake(ssl_c, ssl_s, 10, NULL);
|
|
ExpectIntNE(ret, 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, ret),
|
|
WC_NO_ERR_TRACE(UNSUPPORTED_CERTIFICATE));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_rpk_handshake_no_negotiation(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_RPK) && defined(WOLFSSL_TLS13) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int tp = 0;
|
|
|
|
/* Direction 1: server loads an RPK cert, client is a plain X.509 client. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
cliCertFile, CERT_FILETYPE,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE)
|
|
, 0);
|
|
|
|
/* Handshake must fail and the client must not record RPK as negotiated. */
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_negotiated_server_cert_type(ssl_c, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntNE(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = ssl_s = NULL;
|
|
ctx_c = ctx_s = NULL;
|
|
|
|
/* Direction 2: client loads an RPK cert and the server requests client
|
|
* auth (VERIFY_PEER, set in the helper). The server expects X.509 and must
|
|
* reject the client's un-negotiated bare key. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
clntRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
svrCertFile, CERT_FILETYPE,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE)
|
|
, 0);
|
|
|
|
/* Handshake must fail and the server must not record RPK as negotiated. */
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_negotiated_client_cert_type(ssl_s, &tp),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntNE(tp, WOLFSSL_CERT_TYPE_RPK);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Post-handshake auth with the client identity loaded on the SSL, after an
|
|
* initial handshake with and without client auth: every round must get it. */
|
|
int test_tls13_pha(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(SESSION_CERTS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_X509_CHAIN* chain = NULL;
|
|
char msg[] = "hello wolfssl!";
|
|
char buf[sizeof(msg)];
|
|
int mutual, round;
|
|
|
|
for (mutual = 0; mutual < 2 && EXPECT_SUCCESS(); mutual++) {
|
|
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);
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile,
|
|
NULL), WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | (mutual ?
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT :
|
|
WOLFSSL_VERIFY_POST_HANDSHAKE), NULL);
|
|
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_s));
|
|
ExpectIntEQ(wolfSSL_get_chain_count(chain), mutual);
|
|
|
|
for (round = 0; round < 2 && EXPECT_SUCCESS(); round++) {
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 1);
|
|
ExpectIntEQ(ssl_s->options.havePeerVerify, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
/* DH parameters are not reference counted, so each session takes its own copy
|
|
* of them and the context is free to replace its own at any time. */
|
|
int test_tls13_ctx_dh_rotation(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_DH) && !defined(NO_RSA) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
|
|
!defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
const byte* startP = NULL;
|
|
const byte* startG = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx, dhParamFile, CERT_FILETYPE),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
if ((ssl != NULL) && (ctx != NULL)) {
|
|
/* The session took its own copy of the parameters. */
|
|
ExpectPtrNE(ssl->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer);
|
|
ExpectPtrNE(ssl->buffers.serverDH_G.buffer, ctx->serverDH_G.buffer);
|
|
ExpectIntEQ(ssl->buffers.serverDH_P.length, ctx->serverDH_P.length);
|
|
ExpectIntEQ(ssl->buffers.serverDH_G.length, ctx->serverDH_G.length);
|
|
ExpectBufEQ(ssl->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer,
|
|
ctx->serverDH_P.length);
|
|
ExpectBufEQ(ssl->buffers.serverDH_G.buffer, ctx->serverDH_G.buffer,
|
|
ctx->serverDH_G.length);
|
|
startP = ssl->buffers.serverDH_P.buffer;
|
|
startG = ssl->buffers.serverDH_G.buffer;
|
|
}
|
|
|
|
/* Replacing them on the context while the session is alive is allowed and
|
|
* leaves the session's copy where it was. */
|
|
ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx, dhParamFile, CERT_FILETYPE),
|
|
WOLFSSL_SUCCESS);
|
|
if (ssl != NULL) {
|
|
ExpectPtrEq(ssl->buffers.serverDH_P.buffer, startP);
|
|
ExpectPtrEq(ssl->buffers.serverDH_G.buffer, startG);
|
|
ExpectNotNull(ssl->buffers.serverDH_P.buffer);
|
|
ExpectNotNull(ssl->buffers.serverDH_G.buffer);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Post-handshake auth over a resumed (ticket-PSK) connection. */
|
|
int test_tls13_pha_resumption(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "hello wolfssl!";
|
|
char buf[sizeof(msg)];
|
|
int i;
|
|
|
|
/* Setup only creates a CTX when the pointer is NULL, so the call in the
|
|
* loop reuses these and creates just the SSL objects. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
/* PHA has to be offered in the ClientHello. */
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(ctx_c), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
/* No client certificate wanted for the handshake itself. */
|
|
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
for (i = 0; i < 2 && EXPECT_SUCCESS(); i++) {
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
if (i == 1)
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), i);
|
|
|
|
if (i == 0) {
|
|
/* Drain the NewSessionTicket so the ticket gets cached. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
}
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* The server's write carries the CertificateRequest. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 1);
|
|
ExpectIntEQ(ssl_s->options.havePeerVerify, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
/* Post-handshake auth on a connection whose 0-RTT data the server accepted.
|
|
* The client's record sequence number must keep counting: restarting it at 0
|
|
* would repeat AEAD nonces already spent under the application traffic key. */
|
|
int test_tls13_pha_after_early_data(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "hello wolfssl!";
|
|
char buf[sizeof(msg)];
|
|
word32 seq = 0;
|
|
int written = 0;
|
|
int read = 0;
|
|
int 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, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
/* PHA has to be offered in the ClientHello. */
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(ctx_c), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
/* The ticket only advertises 0-RTT when the server is opted in already. */
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
|
|
/* First connection is only there to hand out a ticket. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
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);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
|
|
/* Resume with 0-RTT data that the server accepts. */
|
|
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, msg, (int)sizeof(msg) - 1,
|
|
&written), (int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read_early_data(ssl_s, buf, sizeof(buf) - 1, &read),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_read_early_data(ssl_s, buf, sizeof(buf) - 1, &read), 0);
|
|
ExpectTrue(wolfSSL_is_init_finished(ssl_s));
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1);
|
|
|
|
/* Enough application records that a restart at 0 cannot be mistaken for
|
|
* normal progress. */
|
|
for (i = 0; i < 6 && EXPECT_SUCCESS(); i++) {
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
}
|
|
if (ssl_c != NULL)
|
|
seq = ssl_c->keys.sequence_number_lo;
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
}
|
|
/* Driven unchecked: the point of the test is the state left behind, and
|
|
* the unfixed client makes the server reject the flight. */
|
|
if (EXPECT_SUCCESS()) {
|
|
(void)wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1);
|
|
(void)wolfSSL_read(ssl_c, buf, sizeof(buf) - 1);
|
|
(void)wolfSSL_read(ssl_s, buf, sizeof(buf) - 1);
|
|
}
|
|
|
|
if (ssl_c != NULL)
|
|
ExpectIntGT(ssl_c->keys.sequence_number_lo, seq);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 1);
|
|
ExpectIntEQ(ssl_s->options.havePeerVerify, 1);
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A context that gains DH parameters after a session already exists hands the
|
|
* session its own copy when the session is turned into a server. */
|
|
int test_tls13_accept_state_dh_copy(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_EXTRA) || \
|
|
defined(WOLFSSL_WPAS_SMALL)) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_DH) && !defined(NO_RSA) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
|
|
!defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
const byte* startP = NULL;
|
|
|
|
/* Starts as a client, so that set_accept_state has a side to switch. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
|
|
/* The session is made before the context has any parameters. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
if (ssl != NULL) {
|
|
ExpectNull(ssl->buffers.serverDH_P.buffer);
|
|
}
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx, dhParamFile, CERT_FILETYPE),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_set_accept_state(ssl);
|
|
|
|
/* It picked them up, as its own copy rather than the context's. */
|
|
if ((ssl != NULL) && (ctx != NULL)) {
|
|
ExpectNotNull(ssl->buffers.serverDH_P.buffer);
|
|
ExpectPtrNE(ssl->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer);
|
|
ExpectPtrNE(ssl->buffers.serverDH_G.buffer, ctx->serverDH_G.buffer);
|
|
ExpectIntEQ(ssl->buffers.serverDH_P.length, ctx->serverDH_P.length);
|
|
ExpectIntEQ(ssl->buffers.serverDH_G.length, ctx->serverDH_G.length);
|
|
ExpectBufEQ(ssl->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer,
|
|
ctx->serverDH_P.length);
|
|
ExpectBufEQ(ssl->buffers.serverDH_G.buffer, ctx->serverDH_G.buffer,
|
|
ctx->serverDH_G.length);
|
|
startP = ssl->buffers.serverDH_P.buffer;
|
|
}
|
|
|
|
/* So the context may still replace its own. */
|
|
ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx, dhParamFile, CERT_FILETYPE),
|
|
WOLFSSL_SUCCESS);
|
|
if (ssl != NULL) {
|
|
ExpectPtrEq(ssl->buffers.serverDH_P.buffer, startP);
|
|
ExpectNotNull(ssl->buffers.serverDH_P.buffer);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Server must reject a post-handshake Finished that omits the Certificate
|
|
* flight on a resumed connection. A client answers the request from inside
|
|
* wolfSSL_read(), so handShakeState is parked for that read and the response is
|
|
* driven by hand with sendVerify cleared. handShakeDone stays set, so
|
|
* SendTls13Finished() still produces a valid MAC. */
|
|
int test_tls13_pha_resumption_bare_finished(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "hello wolfssl!";
|
|
char buf[sizeof(msg)];
|
|
int i;
|
|
|
|
/* Setup only creates a CTX when the pointer is NULL, so the call in the
|
|
* loop reuses these and creates just the SSL objects. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(ctx_c), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
/* First connection only exists to obtain a ticket. */
|
|
for (i = 0; i < 2 && EXPECT_SUCCESS(); i++) {
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
if (i == 1)
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), i);
|
|
|
|
if (i == 0) {
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
}
|
|
else {
|
|
if (EXPECT_SUCCESS()) {
|
|
/* POST_HANDSHAKE so the deferred-verification exemption is
|
|
* evaluated rather than short-circuited. */
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT |
|
|
WOLFSSL_VERIFY_POST_HANDSHAKE, NULL);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
|
|
/* Consume the CertificateRequest without answering it. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->options.handShakeState = CLIENT_FINISHED_COMPLETE;
|
|
}
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(ssl_c->options.sendVerify, SEND_CERT);
|
|
|
|
/* Answer with a bare Finished over that same transcript. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->options.sendVerify = 0;
|
|
ssl_c->options.clientState = CLIENT_HELLO_COMPLETE;
|
|
ssl_c->options.connectState = FIRST_REPLY_DONE;
|
|
ssl_c->options.handShakeState = CLIENT_HELLO_COMPLETE;
|
|
ssl_c->options.processReply = 0;
|
|
ExpectIntEQ(wolfSSL_connect_TLSv13(ssl_c), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Data behind the Finished: a server that wrongly accepted it
|
|
* returns this instead of an error. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf) - 1), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), OUT_OF_ORDER_E);
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 0);
|
|
ExpectIntEQ(ssl_s->options.havePeerVerify, 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A second post-handshake round must prove possession again: peer-auth state
|
|
* from the previous round is cleared when the next request goes out, so the
|
|
* message-order checks cannot be satisfied by it. */
|
|
int test_tls13_pha_resumption_second_round(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "hello wolfssl!";
|
|
char buf[sizeof(msg)];
|
|
int i, round;
|
|
|
|
/* Setup only creates a CTX when the pointer is NULL, so the call in the
|
|
* loop reuses these and creates just the SSL objects. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(ctx_c), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
for (i = 0; i < 2 && EXPECT_SUCCESS(); i++) {
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
if (i == 1)
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), i);
|
|
|
|
if (i == 0) {
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
}
|
|
|
|
for (round = 0; i == 1 && round < 2 && EXPECT_SUCCESS(); round++) {
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
/* Whatever the previous round established is not carried over. */
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 0);
|
|
ExpectIntEQ(ssl_s->options.havePeerVerify, 0);
|
|
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
|
|
/* And the round that just ran established them again. */
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 1);
|
|
ExpectIntEQ(ssl_s->options.havePeerVerify, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && !defined(NO_PSK) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
static const char test_tls13_pha_psk_id[] = "pha_psk";
|
|
static const byte test_tls13_pha_psk_key[] = {
|
|
0x35, 0x1F, 0x2A, 0x0C, 0x7D, 0x64, 0x4B, 0x18,
|
|
0x9E, 0xC2, 0x50, 0x77, 0x36, 0xA1, 0xE9, 0x83
|
|
};
|
|
|
|
static unsigned int test_tls13_pha_psk_client_cb(WOLFSSL* ssl, const char* hint,
|
|
char* identity, unsigned int id_max_len, unsigned char* key,
|
|
unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)hint;
|
|
if (id_max_len < sizeof(test_tls13_pha_psk_id) ||
|
|
key_max_len < sizeof(test_tls13_pha_psk_key))
|
|
return 0;
|
|
XSTRNCPY(identity, test_tls13_pha_psk_id, id_max_len);
|
|
XMEMCPY(key, test_tls13_pha_psk_key, sizeof(test_tls13_pha_psk_key));
|
|
return (unsigned int)sizeof(test_tls13_pha_psk_key);
|
|
}
|
|
|
|
static unsigned int test_tls13_pha_psk_server_cb(WOLFSSL* ssl, const char* id,
|
|
unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
if (id == NULL || key_max_len < sizeof(test_tls13_pha_psk_key) ||
|
|
XSTRCMP(id, test_tls13_pha_psk_id) != 0)
|
|
return 0;
|
|
XMEMCPY(key, test_tls13_pha_psk_key, sizeof(test_tls13_pha_psk_key));
|
|
return (unsigned int)sizeof(test_tls13_pha_psk_key);
|
|
}
|
|
#endif
|
|
|
|
/* Bounds the other side of the RFC 8446 4.3.2 relaxation: a CertificateRequest
|
|
* during the main handshake under a PSK must still be rejected. The client is
|
|
* untouched here - it has not sent its Finished, so handShakeDone is 0 on its
|
|
* own. A wolfSSL server normally clears verifyPeer once a PSK is chosen and so
|
|
* never sends one; cert_with_extern_psk on the server alone suppresses that
|
|
* clearing, which makes it emit the request the client must refuse. */
|
|
int test_tls13_psk_cert_request_in_handshake(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && !defined(NO_PSK) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
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_psk_client_callback(ssl_c, test_tls13_pha_psk_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_pha_psk_server_cb);
|
|
/* Server side only: the client neither offers the extension nor takes the
|
|
* matching exemption, so it must reject on message order alone. */
|
|
ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_s, 1), WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), SANITY_MSG_E);
|
|
ExpectIntEQ(ssl_c->options.pskNegotiated, 1);
|
|
ExpectIntEQ(ssl_c->options.certWithExternPsk, 0);
|
|
ExpectIntEQ(ssl_c->options.handShakeDone, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A client with no certificate answers the post-handshake request with an empty
|
|
* Certificate; a server requiring one must reject it. Pins behaviour on the
|
|
* resumed path that nothing else covers - the rejection is in ProcessPeerCerts()
|
|
* and does not depend on the resumption state. */
|
|
int test_tls13_pha_resumption_blank_cert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(WOLFSSL_NO_CLIENT_CERT_ERROR) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "hello wolfssl!";
|
|
char buf[sizeof(msg)];
|
|
int i;
|
|
|
|
/* Setup only creates a CTX when the pointer is NULL, so the call in the
|
|
* loop reuses these and creates just the SSL objects. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
/* No client cert/key: the client can only answer with a blank Certificate. */
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(ctx_c), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
for (i = 0; i < 2 && EXPECT_SUCCESS(); i++) {
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
if (i == 1)
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), i);
|
|
|
|
if (i == 0) {
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
}
|
|
else {
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(ssl_c->options.sendVerify, SEND_BLANK_CERT);
|
|
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf) - 1), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), NO_PEER_CERT);
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_RPK) && defined(WOLFSSL_TLS13) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_SHA256)
|
|
/* Verify callback that unconditionally accepts the peer (returns "ok"),
|
|
* overriding any error - used to exercise the documented RPK override path.
|
|
* Only used by test_tls13_rpk_trust(), which requires SHA-256. */
|
|
static int test_rpk_accept_cb(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
(void)preverify;
|
|
(void)store;
|
|
return 1;
|
|
}
|
|
|
|
/* Mimics a real-world X.509 verify callback that accepts ONLY the "issuer not
|
|
* found locally" errors (the pattern used elsewhere to allow an unchained or
|
|
* self-signed leaf). It must NOT rescue an untrusted RPK: because the RPK
|
|
* failure now reports WOLFSSL_X509_V_ERR_RPK_UNTRUSTED (not the X.509
|
|
* issuer-lookup codes), these checks no longer match and the RPK stays rejected.
|
|
* Returns 1 (accept) only for the X.509 issuer codes, otherwise rejects. */
|
|
static int test_rpk_x509_issuer_accept_cb(int preverify,
|
|
WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
(void)preverify;
|
|
if (store->error == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
|| store->error == WOLFSSL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
|
|
#endif
|
|
) {
|
|
return 1; /* would wrongly accept an RPK that reused these codes */
|
|
}
|
|
return 0; /* reject everything else, including WOLFSSL_X509_V_ERR_RPK_* */
|
|
}
|
|
#endif /* HAVE_RPK && WOLFSSL_TLS13 && client && server && !NO_SHA256 */
|
|
|
|
#if defined(HAVE_RPK) && \
|
|
(defined(WOLFSSL_TLS13) || !defined(WOLFSSL_NO_TLS12)) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* Build a client/server pair (protocol chosen by the caller) that both present
|
|
* a Raw Public Key but pin NOTHING (unlike test_rpk_memio_setup, which pins the
|
|
* peer key). Verify mode is left at the CTX default; the caller sets it on the
|
|
* SSL objects before the handshake. Returns 0 on success. */
|
|
static int test_rpk_nopin_setup(struct test_memio_ctx* tctx,
|
|
WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s,
|
|
WOLFSSL** ssl_c, WOLFSSL** ssl_s,
|
|
method_provider method_c, method_provider method_s)
|
|
{
|
|
char certType[MAX_CLIENT_CERT_TYPE_CNT];
|
|
|
|
*ctx_c = wolfSSL_CTX_new(method_c());
|
|
*ctx_s = wolfSSL_CTX_new(method_s());
|
|
if ((*ctx_c == NULL) || (*ctx_s == NULL))
|
|
return -1;
|
|
|
|
if (wolfSSL_CTX_load_verify_locations(*ctx_c, caCertFile, 0)
|
|
!= WOLFSSL_SUCCESS)
|
|
return -1;
|
|
if (wolfSSL_CTX_use_certificate_file(*ctx_c, clntRpkCertFile,
|
|
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS)
|
|
return -1;
|
|
if (wolfSSL_CTX_use_PrivateKey_file(*ctx_c, cliKeyFile, CERT_FILETYPE)
|
|
!= WOLFSSL_SUCCESS)
|
|
return -1;
|
|
wolfSSL_SetIORecv(*ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(*ctx_c, test_memio_write_cb);
|
|
|
|
if (wolfSSL_CTX_load_verify_locations(*ctx_s, cliCertFile, 0)
|
|
!= WOLFSSL_SUCCESS)
|
|
return -1;
|
|
if (wolfSSL_CTX_use_PrivateKey_file(*ctx_s, svrKeyFile, CERT_FILETYPE)
|
|
!= WOLFSSL_SUCCESS)
|
|
return -1;
|
|
if (wolfSSL_CTX_use_certificate_file(*ctx_s, svrRpkCertFile,
|
|
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS)
|
|
return -1;
|
|
wolfSSL_SetIORecv(*ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(*ctx_s, test_memio_write_cb);
|
|
|
|
*ssl_c = wolfSSL_new(*ctx_c);
|
|
*ssl_s = wolfSSL_new(*ctx_s);
|
|
if ((*ssl_c == NULL) || (*ssl_s == NULL))
|
|
return -1;
|
|
wolfSSL_SetIOWriteCtx(*ssl_c, tctx);
|
|
wolfSSL_SetIOReadCtx(*ssl_c, tctx);
|
|
wolfSSL_SetIOWriteCtx(*ssl_s, tctx);
|
|
wolfSSL_SetIOReadCtx(*ssl_s, tctx);
|
|
#if !defined(NO_DH)
|
|
SetDH(*ssl_s);
|
|
#endif
|
|
|
|
/* let both sides negotiate RPK in either direction */
|
|
certType[0] = WOLFSSL_CERT_TYPE_RPK;
|
|
certType[1] = WOLFSSL_CERT_TYPE_X509;
|
|
if ((wolfSSL_set_client_cert_type(*ssl_c, certType, 2) != WOLFSSL_SUCCESS) ||
|
|
(wolfSSL_set_server_cert_type(*ssl_c, certType, 2) != WOLFSSL_SUCCESS) ||
|
|
(wolfSSL_set_client_cert_type(*ssl_s, certType, 2) != WOLFSSL_SUCCESS) ||
|
|
(wolfSSL_set_server_cert_type(*ssl_s, certType, 2) != WOLFSSL_SUCCESS))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* HAVE_RPK && (WOLFSSL_TLS13 || !WOLFSSL_NO_TLS12) && client && server */
|
|
|
|
/* Regression test for the RPK fail-closed behaviour: a peer that presents a
|
|
* Raw Public Key (RFC 7250) without an out-of-band pin must NOT be silently
|
|
* accepted while it is being authenticated. Covers both an explicit
|
|
* WOLFSSL_VERIFY_PEER client and a default-verify client (verify mode never
|
|
* set), since RPK server
|
|
* authentication is gated on !verifyNone, matching the X.509 path. Runs for
|
|
* every available protocol version, since the fail-closed code in
|
|
* ProcessPeerCerts is shared between the TLS 1.2 and TLS 1.3 state machines. */
|
|
int test_tls13_rpk_untrusted(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_RPK) && \
|
|
(defined(WOLFSSL_TLS13) || !defined(WOLFSSL_NO_TLS12)) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int explicitVerifyPeer;
|
|
size_t v;
|
|
struct {
|
|
method_provider c;
|
|
method_provider s;
|
|
} methods[2];
|
|
size_t methodCnt = 0;
|
|
|
|
#ifdef WOLFSSL_TLS13
|
|
methods[methodCnt].c = wolfTLSv1_3_client_method;
|
|
methods[methodCnt].s = wolfTLSv1_3_server_method;
|
|
methodCnt++;
|
|
#endif
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
methods[methodCnt].c = wolfTLSv1_2_client_method;
|
|
methods[methodCnt].s = wolfTLSv1_2_server_method;
|
|
methodCnt++;
|
|
#endif
|
|
|
|
for (v = 0; v < methodCnt; v++) {
|
|
/* round 0: explicit WOLFSSL_VERIFY_PEER on both ends.
|
|
* round 1: default authenticating client - verifyNone=0 but verifyPeer
|
|
* never set. Both must fail closed against an unpinned RPK server. */
|
|
for (explicitVerifyPeer = 1; explicitVerifyPeer >= 0;
|
|
explicitVerifyPeer--) {
|
|
ctx_c = ctx_s = NULL;
|
|
ssl_c = ssl_s = NULL;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(test_rpk_nopin_setup(&test_ctx, &ctx_c, &ctx_s,
|
|
&ssl_c, &ssl_s, methods[v].c, methods[v].s), 0);
|
|
if (explicitVerifyPeer) {
|
|
/* explicit mutual WOLFSSL_VERIFY_PEER on both ends */
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, NULL);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
}
|
|
else {
|
|
/* Default authenticating client: verifyNone=0 with verifyPeer
|
|
* NOT set. WOLFSSL_VERIFY_DEFAULT sets exactly that state, and
|
|
* it is applied explicitly so the test is deterministic
|
|
* regardless of OPENSSL_COMPATIBLE_DEFAULTS (which would
|
|
* otherwise default the CTX to WOLFSSL_VERIFY_NONE and skip
|
|
* authentication). The server is left non-requesting
|
|
* (verifyPeer=0) so the only authentication is the client
|
|
* validating the server's RPK - isolating the !verifyNone gate:
|
|
* it must fail closed even though verifyPeer was never set. */
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_DEFAULT, NULL);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_DEFAULT, NULL);
|
|
}
|
|
|
|
/* With no pin and the peer being authenticated the handshake must
|
|
* NOT complete. */
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
/* and the failure is specifically the untrusted RPK */
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_c),
|
|
WOLFSSL_X509_V_ERR_RPK_UNTRUSTED);
|
|
#endif
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
}
|
|
#endif /* HAVE_RPK && (WOLFSSL_TLS13 || !WOLFSSL_NO_TLS12) && client&&server */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Exercises the trust-establishment paths around the RPK fail-closed change:
|
|
* - WOLFSSL_VERIFY_NONE: handshake completes, get_verify_result() still
|
|
* reports the key as untrusted.
|
|
* - verify callback override: an untrusted RPK is accepted because the
|
|
* callback returns ok.
|
|
* - SSL-level pin via wolfSSL_set_expected_rpk(): pinned key -> V_OK.
|
|
* - pinning API argument and capacity errors. */
|
|
int test_tls13_rpk_trust(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_RPK) && defined(WOLFSSL_TLS13) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_SHA256)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte* svrSpki = NULL;
|
|
byte* cliSpki = NULL;
|
|
size_t svrSpkiSz = 0;
|
|
size_t cliSpkiSz = 0;
|
|
int i;
|
|
|
|
ExpectIntEQ(load_file(svrRpkCertFile, &svrSpki, &svrSpkiSz), 0);
|
|
ExpectIntEQ(load_file(clntRpkCertFile, &cliSpki, &cliSpkiSz), 0);
|
|
/* If either pin failed to load, stop before feeding NULL/zero-length
|
|
* buffers into the pinning calls below (which would only produce
|
|
* misleading downstream failures). */
|
|
if (EXPECT_FAIL()) {
|
|
XFREE(svrSpki, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(cliSpki, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* --- WOLFSSL_VERIFY_NONE: completes, but reported as untrusted --- */
|
|
ctx_c = ctx_s = NULL;
|
|
ssl_c = ssl_s = NULL;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_rpk_nopin_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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_c),
|
|
WOLFSSL_X509_V_ERR_RPK_UNTRUSTED);
|
|
#endif
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
/* --- verify callback overrides the untrusted RPK -> completes --- */
|
|
ctx_c = ctx_s = NULL;
|
|
ssl_c = ssl_s = NULL;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_rpk_nopin_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_PEER, test_rpk_accept_cb);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, test_rpk_accept_cb);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
/* --- a callback that accepts only X.509 issuer-lookup errors must NOT
|
|
* rescue an untrusted RPK: the RPK failure reports its own error code, so
|
|
* the callback's X.509 checks do not match and the handshake fails closed.
|
|
* (Would complete if the RPK reused ASN_NO_SIGNER_E / the issuer-lookup
|
|
* verify-result code.) --- */
|
|
ctx_c = ctx_s = NULL;
|
|
ssl_c = ssl_s = NULL;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_rpk_nopin_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_PEER,
|
|
test_rpk_x509_issuer_accept_cb);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER,
|
|
test_rpk_x509_issuer_accept_cb);
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_c),
|
|
WOLFSSL_X509_V_ERR_RPK_UNTRUSTED);
|
|
#endif
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
/* --- SSL-level pin via wolfSSL_set_expected_rpk() -> V_OK --- */
|
|
ctx_c = ctx_s = NULL;
|
|
ssl_c = ssl_s = NULL;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_rpk_nopin_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_PEER, NULL);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
/* client trusts the server's key, server trusts the client's key */
|
|
ExpectIntEQ(wolfSSL_set_expected_rpk(ssl_c, svrSpki,
|
|
(unsigned int)svrSpkiSz), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_expected_rpk(ssl_s, cliSpki,
|
|
(unsigned int)cliSpkiSz), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_c), WOLFSSL_X509_V_OK);
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_s), WOLFSSL_X509_V_OK);
|
|
#endif
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
/* --- non-matching pin: table populated, but with the WRONG key ---
|
|
* The client pins the *client's* own key, which is not what the server
|
|
* presents (the server's key), so RpkIsTrusted's compare loop runs to
|
|
* completion and finds no match -> fail closed. Distinct from the no-pin
|
|
* case (empty table) above. */
|
|
ctx_c = ctx_s = NULL;
|
|
ssl_c = ssl_s = NULL;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_rpk_nopin_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_PEER, NULL);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
/* wrong key pinned on the client: it expects cliSpki but gets svrSpki */
|
|
ExpectIntEQ(wolfSSL_set_expected_rpk(ssl_c, cliSpki,
|
|
(unsigned int)cliSpkiSz), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_c),
|
|
WOLFSSL_X509_V_ERR_RPK_UNTRUSTED);
|
|
#endif
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
/* --- pinning API argument and capacity errors --- */
|
|
ctx_c = NULL;
|
|
ssl_c = NULL;
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
/* NULL handle / NULL spki / zero length all report BAD_FUNC_ARG */
|
|
ExpectIntEQ(wolfSSL_set_expected_rpk(NULL, svrSpki,
|
|
(unsigned int)svrSpkiSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_set_expected_rpk(ssl_c, NULL,
|
|
(unsigned int)svrSpkiSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_set_expected_rpk(ssl_c, svrSpki, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* fill the pin table, then the next add overflows with BUFFER_E */
|
|
for (i = 0; i < WOLFSSL_MAX_RPK_PINS; i++) {
|
|
ExpectIntEQ(wolfSSL_set_expected_rpk(ssl_c, svrSpki,
|
|
(unsigned int)svrSpkiSz), WOLFSSL_SUCCESS);
|
|
}
|
|
ExpectIntEQ(wolfSSL_set_expected_rpk(ssl_c, svrSpki,
|
|
(unsigned int)svrSpkiSz), WC_NO_ERR_TRACE(BUFFER_E));
|
|
/* clear empties the (full) table so an add succeeds again */
|
|
ExpectIntEQ(wolfSSL_clear_expected_rpk(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_clear_expected_rpk(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_expected_rpk(ssl_c, svrSpki,
|
|
(unsigned int)svrSpkiSz), WOLFSSL_SUCCESS);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
|
|
/* same argument/capacity errors for the CTX-level wrapper */
|
|
ctx_c = NULL;
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_set_expected_rpk(NULL, svrSpki,
|
|
(unsigned int)svrSpkiSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_CTX_set_expected_rpk(ctx_c, NULL,
|
|
(unsigned int)svrSpkiSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_CTX_set_expected_rpk(ctx_c, svrSpki, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
for (i = 0; i < WOLFSSL_MAX_RPK_PINS; i++) {
|
|
ExpectIntEQ(wolfSSL_CTX_set_expected_rpk(ctx_c, svrSpki,
|
|
(unsigned int)svrSpkiSz), WOLFSSL_SUCCESS);
|
|
}
|
|
ExpectIntEQ(wolfSSL_CTX_set_expected_rpk(ctx_c, svrSpki,
|
|
(unsigned int)svrSpkiSz), WC_NO_ERR_TRACE(BUFFER_E));
|
|
/* clear empties the (full) table so an add succeeds again */
|
|
ExpectIntEQ(wolfSSL_CTX_clear_expected_rpk(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_CTX_clear_expected_rpk(ctx_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_set_expected_rpk(ctx_c, svrSpki,
|
|
(unsigned int)svrSpkiSz), WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
|
|
XFREE(svrSpki, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(cliSpki, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif /* HAVE_RPK && WOLFSSL_TLS13 && client && server && !NO_SHA256 */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_rpk_unoffered_cert_type(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_RPK) && defined(WOLFSSL_TLS13) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
int isServerType;
|
|
|
|
/* round 0: server_cert_type. round 1: the client_cert_type twin. */
|
|
for (isServerType = 1; isServerType >= 0 && EXPECT_SUCCESS();
|
|
isServerType--) {
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_rpk_nopin_setup(&test_ctx, &ctx_c, &ctx_s,
|
|
&ssl_c, &ssl_s, wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method), 0);
|
|
|
|
/* Both ends must authenticate the peer. Under
|
|
* OPENSSL_COMPATIBLE_DEFAULTS (--enable-all) the CTX defaults to
|
|
* WOLFSSL_VERIFY_NONE, and a non-verifying server omits the
|
|
* client_certificate_type response entirely, leaving nothing for the
|
|
* second round to reject. Set it explicitly so both rounds behave the
|
|
* same across build configs. */
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, NULL);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
|
|
/* ClientHello out, then the server's flight carrying the response. */
|
|
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);
|
|
|
|
/* Drop the offer, making the response unsolicited. */
|
|
if (ssl_c != NULL) {
|
|
if (isServerType)
|
|
ssl_c->options.rpkState.sending_ServerCertTypeCnt = 0;
|
|
else
|
|
ssl_c->options.rpkState.sending_ClientCertTypeCnt = 0;
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), UNSUPPORTED_EXTENSION);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif /* HAVE_RPK && WOLFSSL_TLS13 && client && server */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 Section 4.4.2: "If the RawPublicKey certificate type was negotiated,
|
|
* then the certificate_list MUST contain no more than one CertificateEntry,
|
|
* which contains an ASN1_subjectPublicKeyInfo value as defined in [RFC7250],
|
|
* Section 3." Make each side send a second raw public key and check that its
|
|
* peer rejects the Certificate message as illegal_parameter. */
|
|
int test_tls13_rpk_multiple_certs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_RPK) && defined(WOLFSSL_TLS13) && !defined(NO_TLS) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_SHA256) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
byte* spki = NULL;
|
|
size_t spkiSz = 0;
|
|
DerBuffer* chain = NULL;
|
|
char certType[] = { WOLFSSL_CERT_TYPE_RPK, WOLFSSL_CERT_TYPE_X509 };
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
clntRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE)
|
|
, 0);
|
|
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType,
|
|
(int)sizeof(certType)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType,
|
|
(int)sizeof(certType)), WOLFSSL_SUCCESS);
|
|
|
|
/* Append a second entry to the server's certificate_list: the same raw
|
|
* public key again, in the length-prefixed form the chain is sent in. */
|
|
ExpectIntEQ(load_file(svrRpkCertFile, &spki, &spkiSz), 0);
|
|
ExpectIntEQ(AllocDer(&chain, (word32)spkiSz + CERT_HEADER_SZ,
|
|
CERT_TYPE, NULL), 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
chain->buffer[0] = (byte)(spkiSz >> 16);
|
|
chain->buffer[1] = (byte)(spkiSz >> 8);
|
|
chain->buffer[2] = (byte)spkiSz;
|
|
XMEMCPY(chain->buffer + CERT_HEADER_SZ, spki, spkiSz);
|
|
ssl_s->buffers.certChain = chain;
|
|
ssl_s->buffers.certChainCnt = 1;
|
|
ssl_s->buffers.weOwnCertChain = 1;
|
|
chain = NULL; /* owned by ssl_s now */
|
|
}
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(INVALID_PARAMETER));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, illegal_parameter);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
FreeDer(&chain);
|
|
XFREE(spki, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
spki = NULL;
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_c = NULL;
|
|
ctx_s = NULL;
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
|
|
/* Client direction: with client RawPublicKey negotiated too, a second
|
|
* entry in the client's certificate_list must be rejected by the
|
|
* server the same way. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(
|
|
test_rpk_memio_setup(
|
|
&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
clntRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
svrRpkCertFile, WOLFSSL_FILETYPE_ASN1,
|
|
cliKeyFile, CERT_FILETYPE,
|
|
svrKeyFile, CERT_FILETYPE)
|
|
, 0);
|
|
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_c, certType,
|
|
(int)sizeof(certType)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_server_cert_type(ssl_s, certType,
|
|
(int)sizeof(certType)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_c, certType,
|
|
(int)sizeof(certType)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_client_cert_type(ssl_s, certType,
|
|
(int)sizeof(certType)), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(load_file(clntRpkCertFile, &spki, &spkiSz), 0);
|
|
ExpectIntEQ(AllocDer(&chain, (word32)spkiSz + CERT_HEADER_SZ,
|
|
CERT_TYPE, NULL), 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
chain->buffer[0] = (byte)(spkiSz >> 16);
|
|
chain->buffer[1] = (byte)(spkiSz >> 8);
|
|
chain->buffer[2] = (byte)spkiSz;
|
|
XMEMCPY(chain->buffer + CERT_HEADER_SZ, spki, spkiSz);
|
|
ssl_c->buffers.certChain = chain;
|
|
ssl_c->buffers.certChainCnt = 1;
|
|
ssl_c->buffers.weOwnCertChain = 1;
|
|
chain = NULL; /* owned by ssl_c now */
|
|
}
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(INVALID_PARAMETER));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_s, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, illegal_parameter);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
FreeDer(&chain);
|
|
XFREE(spki, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif /* HAVE_RPK && WOLFSSL_TLS13 && memio && !NO_SHA256 && client+server */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
(!defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) || \
|
|
(defined(HAVE_CURVE25519) && !defined(WOLFSSL_NO_ML_KEM_768)) || \
|
|
(defined(HAVE_ECC) && !defined(WOLFSSL_NO_ML_KEM_768)))
|
|
static void test_tls13_pq_groups_ctx_ready(WOLFSSL_CTX* ctx)
|
|
{
|
|
#ifdef WOLFSSL_MLKEM_KYBER
|
|
#if !defined(WOLFSSL_NO_KYBER1024)
|
|
int group = WOLFSSL_KYBER_LEVEL5;
|
|
#elif !defined(WOLFSSL_NO_KYBER768)
|
|
int group = WOLFSSL_KYBER_LEVEL3;
|
|
#else
|
|
int group = WOLFSSL_KYBER_LEVEL1;
|
|
#endif
|
|
#elif !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
|
|
#if !defined(WOLFSSL_NO_ML_KEM_1024)
|
|
int group = WOLFSSL_ML_KEM_1024;
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_768)
|
|
int group = WOLFSSL_ML_KEM_768;
|
|
#else
|
|
int group = WOLFSSL_ML_KEM_512;
|
|
#endif
|
|
#elif defined(HAVE_ECC) && !defined(WOLFSSL_NO_ML_KEM_768) && \
|
|
defined(WOLFSSL_PQC_HYBRIDS)
|
|
int group = WOLFSSL_SECP256R1MLKEM768;
|
|
#elif defined(HAVE_CURVE25519) && !defined(WOLFSSL_NO_ML_KEM_768) && \
|
|
defined(WOLFSSL_PQC_HYBRIDS)
|
|
int group = WOLFSSL_X25519MLKEM768;
|
|
#endif
|
|
|
|
AssertIntEQ(wolfSSL_CTX_set_groups(ctx, &group, 1), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
static void test_tls13_pq_groups_on_result(WOLFSSL* ssl)
|
|
{
|
|
#ifdef WOLFSSL_MLKEM_KYBER
|
|
#if !defined(WOLFSSL_NO_KYBER1024)
|
|
AssertStrEQ(wolfSSL_get_curve_name(ssl), "KYBER_LEVEL5");
|
|
#elif !defined(WOLFSSL_NO_KYBER768)
|
|
AssertStrEQ(wolfSSL_get_curve_name(ssl), "KYBER_LEVEL3");
|
|
#else
|
|
AssertStrEQ(wolfSSL_get_curve_name(ssl), "KYBER_LEVEL1");
|
|
#endif
|
|
#elif !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
|
|
#if !defined(WOLFSSL_NO_ML_KEM_1024)
|
|
AssertStrEQ(wolfSSL_get_curve_name(ssl), "ML_KEM_1024");
|
|
#elif !defined(WOLFSSL_NO_ML_KEM_768)
|
|
AssertStrEQ(wolfSSL_get_curve_name(ssl), "ML_KEM_768");
|
|
#else
|
|
AssertStrEQ(wolfSSL_get_curve_name(ssl), "ML_KEM_512");
|
|
#endif
|
|
#elif defined(HAVE_ECC) && !defined(WOLFSSL_NO_ML_KEM_768) && \
|
|
defined(WOLFSSL_PQC_HYBRIDS)
|
|
AssertStrEQ(wolfSSL_get_curve_name(ssl), "SecP256r1MLKEM768");
|
|
#elif defined(HAVE_CURVE25519) && !defined(WOLFSSL_NO_ML_KEM_768) && \
|
|
defined(WOLFSSL_PQC_HYBRIDS)
|
|
AssertStrEQ(wolfSSL_get_curve_name(ssl), "X25519MLKEM768");
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_pq_groups(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
(!defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) || \
|
|
(defined(HAVE_CURVE25519) && !defined(WOLFSSL_NO_ML_KEM_768)) || \
|
|
(defined(HAVE_ECC) && !defined(WOLFSSL_NO_ML_KEM_768)))
|
|
callback_functions func_cb_client;
|
|
callback_functions func_cb_server;
|
|
|
|
XMEMSET(&func_cb_client, 0, sizeof(callback_functions));
|
|
XMEMSET(&func_cb_server, 0, sizeof(callback_functions));
|
|
|
|
func_cb_client.method = wolfTLSv1_3_client_method;
|
|
func_cb_server.method = wolfTLSv1_3_server_method;
|
|
func_cb_client.ctx_ready = test_tls13_pq_groups_ctx_ready;
|
|
func_cb_client.on_result = test_tls13_pq_groups_on_result;
|
|
func_cb_server.on_result = test_tls13_pq_groups_on_result;
|
|
|
|
test_wolfSSL_client_server_nofail(&func_cb_client, &func_cb_server);
|
|
|
|
ExpectIntEQ(func_cb_client.return_code, TEST_SUCCESS);
|
|
ExpectIntEQ(func_cb_server.return_code, TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Regression test handling multiple PQC key shares in the ClientHello.
|
|
*
|
|
* Previously, the server eagerly ran KEM encapsulation on every PQC/hybrid
|
|
* key_share entry while parsing the ClientHello, clobbering
|
|
* ssl->arrays->preMasterSecret with whichever entry was parsed last.
|
|
* When the ClientHello offers both SecP384R1_MLKEM1024 and pure
|
|
* ML_KEM_1024, the resulting handshake either produces keys that the
|
|
* client cannot decrypt (hybrid chosen, pure-ML-KEM secret written) or
|
|
* trips a BUFFER_E inside the second encapsulation. Either ordering
|
|
* causes the handshake to fail.
|
|
*
|
|
* The test runs a memio TLS 1.3 handshake for both orderings and
|
|
* expects the handshake to complete successfully with the hybrid group
|
|
* selected (higher server preference rank). */
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_PQC_HYBRIDS) && \
|
|
!defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) && \
|
|
!defined(WOLFSSL_NO_ML_KEM_1024) && \
|
|
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
defined(HAVE_ECC) && (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \
|
|
ECC_MIN_KEY_SZ <= 384 && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
#define TEST_TLS13_MULTI_PQC_KEY_SHARE_ENABLED
|
|
|
|
/* Run one TLS 1.3 memio handshake where the client offers both
|
|
* WOLFSSL_SECP384R1MLKEM1024 and WOLFSSL_ML_KEM_1024 in the key_share
|
|
* extension, in the order dictated by `hybridFirst`. */
|
|
static int test_tls13_multi_pqc_key_share_once(int hybridFirst)
|
|
{
|
|
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));
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
/* Force the client to include both PQC key shares in the ClientHello
|
|
* by calling UseKeyShare twice. The order of the UseKeyShare calls
|
|
* determines the order of the entries in the key_share extension. */
|
|
if (hybridFirst) {
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_SECP384R1MLKEM1024),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_ML_KEM_1024),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
else {
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_ML_KEM_1024),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_SECP384R1MLKEM1024),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* The server ranks SecP384R1_MLKEM1024 higher than ML_KEM_1024, so
|
|
* the hybrid group must be selected regardless of client ordering. */
|
|
ExpectStrEQ(wolfSSL_get_curve_name(ssl_s), "SecP384r1MLKEM1024");
|
|
ExpectStrEQ(wolfSSL_get_curve_name(ssl_c), "SecP384r1MLKEM1024");
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* TEST_TLS13_MULTI_PQC_KEY_SHARE_ENABLED */
|
|
|
|
int test_tls13_multi_pqc_key_share(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS13_MULTI_PQC_KEY_SHARE_ENABLED
|
|
/* Hybrid first, then pure ML-KEM: pre-fix the server selected the
|
|
* hybrid but had overwritten preMasterSecret with the pure-KEM
|
|
* result, producing 32-byte KE Secret instead of 80 and causing the
|
|
* client to fail to decrypt the server's first encrypted record. */
|
|
ExpectIntEQ(test_tls13_multi_pqc_key_share_once(1), TEST_SUCCESS);
|
|
|
|
/* Pure ML-KEM first, then hybrid: pre-fix the server tripped
|
|
* BUFFER_E inside the second encapsulation because preMasterSz was
|
|
* left at 32 from the first call, and the hybrid handler then
|
|
* overflowed the preMasterSecret buffer. */
|
|
ExpectIntEQ(test_tls13_multi_pqc_key_share_once(0), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 4.2.8 and the ML-KEM TLS drafts require an illegal_parameter alert
|
|
* when an ML-KEM key share fails the FIPS 203 input checks. Tamper with the
|
|
* key_share of the ClientHello or ServerHello on the wire and check that the
|
|
* receiver sends the alert instead of closing silently. */
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) && \
|
|
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
#if !defined(WOLFSSL_NO_ML_KEM_768) && \
|
|
!defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE)
|
|
#define TEST_TLS13_BAD_KS_ML_KEM_768
|
|
#endif
|
|
#if defined(WOLFSSL_PQC_HYBRIDS) && !defined(WOLFSSL_NO_ML_KEM_768) && \
|
|
defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256
|
|
#define TEST_TLS13_BAD_KS_X25519MLKEM768
|
|
#endif
|
|
#if defined(WOLFSSL_PQC_HYBRIDS) && !defined(WOLFSSL_NO_ML_KEM_768) && \
|
|
defined(HAVE_ECC) && !defined(NO_ECC256) && ECC_MIN_KEY_SZ <= 256
|
|
#define TEST_TLS13_BAD_KS_SECP256R1MLKEM768
|
|
#endif
|
|
#if defined(WOLFSSL_PQC_HYBRIDS) && !defined(WOLFSSL_NO_ML_KEM_1024) && \
|
|
defined(HAVE_ECC) && \
|
|
(defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \
|
|
ECC_MIN_KEY_SZ <= 384
|
|
#define TEST_TLS13_BAD_KS_SECP384R1MLKEM1024
|
|
#endif
|
|
#if defined(WOLFSSL_EXTRA_PQC_HYBRIDS) && \
|
|
!defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE448) && \
|
|
ECC_MIN_KEY_SZ <= 448
|
|
#define TEST_TLS13_BAD_KS_X448MLKEM768
|
|
#endif
|
|
#if defined(TEST_TLS13_BAD_KS_ML_KEM_768) || \
|
|
defined(TEST_TLS13_BAD_KS_X25519MLKEM768) || \
|
|
defined(TEST_TLS13_BAD_KS_SECP256R1MLKEM768) || \
|
|
defined(TEST_TLS13_BAD_KS_SECP384R1MLKEM1024) || \
|
|
defined(TEST_TLS13_BAD_KS_X448MLKEM768)
|
|
#define TEST_TLS13_MLKEM_BAD_KEY_SHARE_ENABLED
|
|
#endif
|
|
/* Plain X448 uses the helpers below, which need one of the ML-KEM
|
|
* groups. */
|
|
#if defined(TEST_TLS13_MLKEM_BAD_KEY_SHARE_ENABLED) && \
|
|
defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448
|
|
#define TEST_TLS13_BAD_KS_X448
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef TEST_TLS13_MLKEM_BAD_KEY_SHARE_ENABLED
|
|
/* Add diff to the 16-bit big-endian length at p. */
|
|
static void test_tls13_adjust_len16(byte* p, int diff)
|
|
{
|
|
word16 v;
|
|
|
|
ato16(p, &v);
|
|
c16toa((word16)(v + diff), p);
|
|
}
|
|
|
|
/* Edit the first key_share entry of the hello record at the start of the
|
|
* memio buffer and fix every enclosing length. A negative diff removes bytes
|
|
* at key_exchange offset at, a positive diff inserts zero bytes there, and an
|
|
* at of -1 edits the end of key_exchange. When poke is non-negative the
|
|
* pokeSz bytes of pokeData are written at that key_exchange offset. A NULL
|
|
* pokeData writes two 0xff bytes, which makes an ML-KEM coefficient
|
|
* 4095 >= q. */
|
|
static int test_tls13_tamper_key_share(struct test_memio_ctx* ctx, int client,
|
|
int at, int diff, int poke, const byte* pokeData, int pokeSz)
|
|
{
|
|
static const byte badCoeff[2] = { 0xff, 0xff };
|
|
byte* buf = client ? ctx->c_buff : ctx->s_buff;
|
|
int* len = client ? &ctx->c_len : &ctx->s_len;
|
|
int* msgSz = client ? &ctx->c_msg_sizes[0] : &ctx->s_msg_sizes[0];
|
|
int isCh;
|
|
int idx;
|
|
int extTotIdx;
|
|
int extEnd;
|
|
int extLenIdx = 0;
|
|
int keIdx;
|
|
word16 v16;
|
|
word32 v24;
|
|
int keLen;
|
|
|
|
if (*len < RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + OPAQUE16_LEN +
|
|
RAN_LEN + OPAQUE8_LEN || buf[0] != handshake) {
|
|
return -1;
|
|
}
|
|
isCh = (buf[RECORD_HEADER_SZ] == client_hello);
|
|
idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + OPAQUE16_LEN + RAN_LEN;
|
|
idx += OPAQUE8_LEN + buf[idx];
|
|
if (isCh) {
|
|
ato16(buf + idx, &v16);
|
|
idx += OPAQUE16_LEN + v16;
|
|
idx += OPAQUE8_LEN + buf[idx];
|
|
}
|
|
else {
|
|
idx += OPAQUE16_LEN + OPAQUE8_LEN;
|
|
}
|
|
if (idx + OPAQUE16_LEN > *len)
|
|
return -1;
|
|
extTotIdx = idx;
|
|
ato16(buf + idx, &v16);
|
|
extEnd = idx + OPAQUE16_LEN + v16;
|
|
idx += OPAQUE16_LEN;
|
|
while (idx + 2 * OPAQUE16_LEN <= extEnd &&
|
|
idx + 2 * OPAQUE16_LEN <= *len) {
|
|
ato16(buf + idx, &v16);
|
|
if (v16 == TLSX_KEY_SHARE) {
|
|
extLenIdx = idx + OPAQUE16_LEN;
|
|
break;
|
|
}
|
|
ato16(buf + idx + OPAQUE16_LEN, &v16);
|
|
idx += 2 * OPAQUE16_LEN + v16;
|
|
}
|
|
if (extLenIdx == 0)
|
|
return -1;
|
|
/* Skip extension length, client_shares length (ClientHello only), group
|
|
* and key_exchange length. */
|
|
keIdx = extLenIdx + 3 * OPAQUE16_LEN;
|
|
if (isCh)
|
|
keIdx += OPAQUE16_LEN;
|
|
if (keIdx > *len)
|
|
return -1;
|
|
ato16(buf + keIdx - OPAQUE16_LEN, &v16);
|
|
keLen = v16;
|
|
if (at < 0)
|
|
at = (diff < 0) ? keLen + diff : keLen;
|
|
if (keIdx + keLen > *len || at < 0 || at - diff > keLen ||
|
|
*len + diff > TEST_MEMIO_BUF_SZ) {
|
|
return -1;
|
|
}
|
|
|
|
if (poke >= 0) {
|
|
if (pokeData == NULL) {
|
|
pokeData = badCoeff;
|
|
pokeSz = (int)sizeof(badCoeff);
|
|
}
|
|
if (poke + pokeSz > keLen)
|
|
return -1;
|
|
XMEMCPY(buf + keIdx + poke, pokeData, (size_t)pokeSz);
|
|
}
|
|
if (diff == 0)
|
|
return 0;
|
|
|
|
test_tls13_adjust_len16(buf + 3, diff);
|
|
ato24(buf + RECORD_HEADER_SZ + 1, &v24);
|
|
c32to24(v24 + (word32)diff, buf + RECORD_HEADER_SZ + 1);
|
|
test_tls13_adjust_len16(buf + extTotIdx, diff);
|
|
test_tls13_adjust_len16(buf + extLenIdx, diff);
|
|
if (isCh)
|
|
test_tls13_adjust_len16(buf + extLenIdx + OPAQUE16_LEN, diff);
|
|
test_tls13_adjust_len16(buf + keIdx - OPAQUE16_LEN, diff);
|
|
if (diff < 0)
|
|
return test_memio_remove_from_buffer(ctx, client, keIdx + at, -diff);
|
|
|
|
XMEMMOVE(buf + keIdx + at + diff, buf + keIdx + at,
|
|
(size_t)(*len - keIdx - at));
|
|
XMEMSET(buf + keIdx + at, 0, (size_t)diff);
|
|
*len += diff;
|
|
*msgSz += diff;
|
|
return 0;
|
|
}
|
|
|
|
/* Call wolfSSL_negotiate() again while it reports WC_PENDING_E, polling the
|
|
* async device in between. A non-blocking X25519 operation pends thousands of
|
|
* times. Returns 0 on success, else the last error. */
|
|
static int test_tls13_bad_ks_negotiate(WOLFSSL* ssl)
|
|
{
|
|
int err;
|
|
int rounds = 0;
|
|
|
|
do {
|
|
if (wolfSSL_negotiate(ssl) == WOLFSSL_SUCCESS)
|
|
return 0;
|
|
err = wolfSSL_get_error(ssl, WOLFSSL_FATAL_ERROR);
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
if ((err == WC_NO_ERR_TRACE(WC_PENDING_E)) &&
|
|
(wolfSSL_AsyncPoll(ssl, WOLF_POLL_FLAG_CHECK_HW) < 0)) {
|
|
break;
|
|
}
|
|
#endif
|
|
} while ((err == WC_NO_ERR_TRACE(WC_PENDING_E)) && (++rounds < 100000));
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Tamper with the ClientHello (serverSide) or ServerHello key share and
|
|
* check the receiver fails with expErr and a fatal illegal_parameter alert.
|
|
* Both ends use devId, so an async device makes the key share pend. */
|
|
static int test_tls13_mlkem_bad_key_share_once_ex(int group, int serverSide,
|
|
int at, int diff, int poke, const byte* pokeData, int pokeSz, int devId,
|
|
int expErr)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL *ssl_rx = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
int ret;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
if (devId != INVALID_DEVID) {
|
|
ExpectIntEQ(wolfSSL_SetDevId(ssl_c, devId), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetDevId(ssl_s, devId), WOLFSSL_SUCCESS);
|
|
}
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, &group, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s, &group, 1), WOLFSSL_SUCCESS);
|
|
do {
|
|
ret = wolfSSL_UseKeyShare(ssl_c, (word16)group);
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
|
|
wolfSSL_AsyncPoll(ssl_c, WOLF_POLL_FLAG_CHECK_HW);
|
|
#endif
|
|
} while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
|
|
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_tls13_bad_ks_negotiate(ssl_c), WOLFSSL_ERROR_WANT_READ);
|
|
if (serverSide) {
|
|
ssl_rx = ssl_s;
|
|
}
|
|
else {
|
|
ExpectIntEQ(test_tls13_bad_ks_negotiate(ssl_s),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ssl_rx = ssl_c;
|
|
}
|
|
|
|
ExpectIntEQ(test_tls13_tamper_key_share(&test_ctx, !serverSide, at, diff,
|
|
poke, pokeData, pokeSz), 0);
|
|
ExpectIntEQ(test_tls13_bad_ks_negotiate(ssl_rx), expErr);
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_rx, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, illegal_parameter);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int test_tls13_mlkem_bad_key_share_once(int group, int serverSide,
|
|
int at, int diff, int poke, int expErr)
|
|
{
|
|
return test_tls13_mlkem_bad_key_share_once_ex(group, serverSide, at, diff,
|
|
poke, NULL, 0, INVALID_DEVID, expErr);
|
|
}
|
|
|
|
#if defined(TEST_TLS13_BAD_KS_X25519MLKEM768) && \
|
|
!defined(WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK)
|
|
/* An X25519 point of order 8 gives an all-zero shared secret, which
|
|
* RFC 10024 Section 4.3 answers with illegal_parameter. */
|
|
static int test_tls13_x25519_low_order_key_share(int devId)
|
|
{
|
|
EXPECT_DECLS;
|
|
static const byte lowOrder[CURVE25519_KEYSIZE] = {
|
|
0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae,
|
|
0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a,
|
|
0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd,
|
|
0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00
|
|
};
|
|
int bad = WC_NO_ERR_TRACE(ECC_PEERKEY_ERROR);
|
|
|
|
/* The X25519 part follows the 1184 byte ML-KEM-768 encapsulation key in
|
|
* the ClientHello and the 1088 byte ciphertext in the ServerHello. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once_ex(WOLFSSL_X25519MLKEM768,
|
|
1, -1, 0, 1184, lowOrder, (int)sizeof(lowOrder), devId, bad),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once_ex(WOLFSSL_X25519MLKEM768,
|
|
0, -1, 0, 1088, lowOrder, (int)sizeof(lowOrder), devId, bad),
|
|
TEST_SUCCESS);
|
|
/* A plain X25519 ServerHello takes the same path. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once_ex(WOLFSSL_ECC_X25519,
|
|
0, -1, 0, 0, lowOrder, (int)sizeof(lowOrder), devId, bad),
|
|
TEST_SUCCESS);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
#ifdef TEST_TLS13_BAD_KS_X448
|
|
/* The X448 inputs that give an all-zero shared secret: u = 0, 1 and p-1, and
|
|
* the non-canonical encodings p and p+1 of 0 and 1 (p = 2^448 - 2^224 - 1).
|
|
* wc_curve448_check_public() rejects all of them, so they are reported as a
|
|
* bad peer key and answered with illegal_parameter. */
|
|
static int test_tls13_x448_low_order_key_share(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
byte lowOrder[5][CURVE448_PUB_KEY_SIZE];
|
|
int bad = WC_NO_ERR_TRACE(ECC_PEERKEY_ERROR);
|
|
int i;
|
|
|
|
/* Little-endian encodings; lowOrder[0] is 0. */
|
|
XMEMSET(lowOrder, 0, sizeof(lowOrder));
|
|
/* 1 */
|
|
lowOrder[1][0] = 0x01;
|
|
/* p-1 */
|
|
XMEMSET(lowOrder[2], 0xff, CURVE448_PUB_KEY_SIZE);
|
|
lowOrder[2][0] = 0xfe;
|
|
lowOrder[2][28] = 0xfe;
|
|
/* p */
|
|
XMEMSET(lowOrder[3], 0xff, CURVE448_PUB_KEY_SIZE);
|
|
lowOrder[3][28] = 0xfe;
|
|
/* p+1 */
|
|
XMEMSET(lowOrder[4] + 28, 0xff, CURVE448_PUB_KEY_SIZE - 28);
|
|
|
|
for (i = 0; i < (int)(sizeof(lowOrder) / sizeof(lowOrder[0])); i++) {
|
|
#ifdef TEST_TLS13_BAD_KS_X448MLKEM768
|
|
/* The X448 part follows the 1184 byte ML-KEM-768 encapsulation key
|
|
* in the ClientHello and the 1088 byte ciphertext in the
|
|
* ServerHello. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once_ex(
|
|
WOLFSSL_X448MLKEM768, 1, -1, 0, 1184, lowOrder[i],
|
|
CURVE448_PUB_KEY_SIZE, INVALID_DEVID, bad), TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once_ex(
|
|
WOLFSSL_X448MLKEM768, 0, -1, 0, 1088, lowOrder[i],
|
|
CURVE448_PUB_KEY_SIZE, INVALID_DEVID, bad), TEST_SUCCESS);
|
|
#endif
|
|
/* A plain X448 ServerHello takes the same path. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once_ex(WOLFSSL_ECC_X448,
|
|
0, -1, 0, 0, lowOrder[i], CURVE448_PUB_KEY_SIZE, INVALID_DEVID,
|
|
bad), TEST_SUCCESS);
|
|
}
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* kemOff: where the ML-KEM part starts in both key shares.
|
|
* eccLen: bytes of ECDH public key in each share, 0 for pure ML-KEM. */
|
|
static int test_tls13_mlkem_bad_key_share_group(int group, int kemOff,
|
|
int eccLen)
|
|
{
|
|
EXPECT_DECLS;
|
|
int bad = WC_NO_ERR_TRACE(BAD_KEY_SHARE_DATA);
|
|
/* A hybrid ServerHello with the right ciphertext size but the wrong total
|
|
* length leaves the ECDH part the wrong size, which ECDH rejects. */
|
|
int badSh = (eccLen > 0) ? WC_NO_ERR_TRACE(ECC_PEERKEY_ERROR) : bad;
|
|
|
|
/* ClientHello: total share 1 or 32 bytes short, cut from the end. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once(group, 1, -1, -1, -1,
|
|
bad), TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once(group, 1, -1, -32, -1,
|
|
bad), TEST_SUCCESS);
|
|
/* ClientHello: ML-KEM key 1 byte short, ECDH part intact. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once(group, 1, kemOff, -1, -1,
|
|
bad), TEST_SUCCESS);
|
|
/* ClientHello: total share 1 byte long. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once(group, 1, -1, 1, -1,
|
|
bad), TEST_SUCCESS);
|
|
/* ClientHello: ML-KEM key fails the modulus check. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once(group, 1, -1, 0, kemOff,
|
|
bad), TEST_SUCCESS);
|
|
|
|
/* ServerHello: ML-KEM ciphertext 1 byte short, ECDH part intact. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once(group, 0, kemOff, -1, -1,
|
|
badSh), TEST_SUCCESS);
|
|
/* ServerHello: total share 1 byte long. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once(group, 0, -1, 1, -1,
|
|
badSh), TEST_SUCCESS);
|
|
/* ServerHello: share 1 byte shorter than the ML-KEM ciphertext alone. */
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_once(group, 0, -1,
|
|
-(eccLen + 1), -1, bad), TEST_SUCCESS);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* TEST_TLS13_MLKEM_BAD_KEY_SHARE_ENABLED */
|
|
|
|
int test_tls13_mlkem_bad_key_share(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS13_BAD_KS_ML_KEM_768
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_group(WOLFSSL_ML_KEM_768, 0,
|
|
0), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef TEST_TLS13_BAD_KS_X25519MLKEM768
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_group(WOLFSSL_X25519MLKEM768, 0,
|
|
CURVE25519_KEYSIZE), TEST_SUCCESS);
|
|
#ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK
|
|
ExpectIntEQ(test_tls13_x25519_low_order_key_share(INVALID_DEVID),
|
|
TEST_SUCCESS);
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
{
|
|
/* Again with an async device. With the non-blocking X25519 code the
|
|
* shared secret pends, finishes in the poll, and its error comes back
|
|
* through wolfSSL_AsyncPop(). */
|
|
int devId = INVALID_DEVID;
|
|
|
|
ExpectIntGE(wolfAsync_DevOpen(&devId), 0);
|
|
ExpectIntEQ(test_tls13_x25519_low_order_key_share(devId),
|
|
TEST_SUCCESS);
|
|
wolfAsync_DevClose(&devId);
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#ifdef TEST_TLS13_BAD_KS_SECP256R1MLKEM768
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_group(WOLFSSL_SECP256R1MLKEM768,
|
|
65, 65), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef TEST_TLS13_BAD_KS_SECP384R1MLKEM1024
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_group(
|
|
WOLFSSL_SECP384R1MLKEM1024, 97, 97), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef TEST_TLS13_BAD_KS_X448MLKEM768
|
|
ExpectIntEQ(test_tls13_mlkem_bad_key_share_group(WOLFSSL_X448MLKEM768, 0,
|
|
CURVE448_PUB_KEY_SIZE), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef TEST_TLS13_BAD_KS_X448
|
|
ExpectIntEQ(test_tls13_x448_low_order_key_share(), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_EARLY_DATA) && defined(HAVE_SESSION_TICKET)
|
|
static int test_tls13_read_until_write_ok(WOLFSSL* ssl, void* buf, int bufLen)
|
|
{
|
|
int ret, err;
|
|
int tries = 5;
|
|
|
|
err = 0;
|
|
do {
|
|
ret = wolfSSL_read(ssl, buf, bufLen);
|
|
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)) {
|
|
err = wolfSSL_get_error(ssl, ret);
|
|
}
|
|
} while (tries-- && ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR) &&
|
|
err == WC_NO_ERR_TRACE(WOLFSSL_ERROR_WANT_WRITE));
|
|
return ret;
|
|
}
|
|
static int test_tls13_connect_until_write_ok(WOLFSSL* ssl)
|
|
{
|
|
int ret, err;
|
|
int tries = 5;
|
|
|
|
err = 0;
|
|
do {
|
|
ret = wolfSSL_connect(ssl);
|
|
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)) {
|
|
err = wolfSSL_get_error(ssl, ret);
|
|
}
|
|
} while (tries-- && ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR) &&
|
|
err == WC_NO_ERR_TRACE(WOLFSSL_ERROR_WANT_WRITE));
|
|
return ret;
|
|
}
|
|
static int test_tls13_write_until_write_ok(WOLFSSL* ssl, const void* msg,
|
|
int msgLen)
|
|
{
|
|
int ret, err;
|
|
int tries = 5;
|
|
|
|
err = 0;
|
|
do {
|
|
ret = wolfSSL_write(ssl, msg, msgLen);
|
|
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)) {
|
|
err = wolfSSL_get_error(ssl, ret);
|
|
}
|
|
} while (tries-- && ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR) &&
|
|
err == WC_NO_ERR_TRACE(WOLFSSL_ERROR_WANT_WRITE));
|
|
return ret;
|
|
}
|
|
static int test_tls13_early_data_read_until_write_ok(WOLFSSL* ssl, void* buf,
|
|
int bufLen, int* read)
|
|
{
|
|
int ret, err;
|
|
int tries = 5;
|
|
|
|
err = 0;
|
|
do {
|
|
ret = wolfSSL_read_early_data(ssl, buf, bufLen, read);
|
|
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)) {
|
|
err = wolfSSL_get_error(ssl, ret);
|
|
}
|
|
} while (tries-- && ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR) &&
|
|
err == WC_NO_ERR_TRACE(WOLFSSL_ERROR_WANT_WRITE));
|
|
return ret;
|
|
}
|
|
static int test_tls13_early_data_write_until_write_ok(WOLFSSL* ssl,
|
|
const void* msg, int msgLen, int* written)
|
|
{
|
|
int ret, err;
|
|
int tries = 5;
|
|
|
|
err = 0;
|
|
do {
|
|
ret = wolfSSL_write_early_data(ssl, msg, msgLen, written);
|
|
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)) {
|
|
err = wolfSSL_get_error(ssl, ret);
|
|
}
|
|
} while (tries-- && ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR) &&
|
|
err == WC_NO_ERR_TRACE(WOLFSSL_ERROR_WANT_WRITE));
|
|
return ret;
|
|
}
|
|
struct test_tls13_wwrite_ctx {
|
|
int want_write;
|
|
struct test_memio_ctx *test_ctx;
|
|
};
|
|
static int test_tls13_mock_wantwrite_cb(WOLFSSL* ssl, char* data, int sz,
|
|
void* ctx)
|
|
{
|
|
struct test_tls13_wwrite_ctx *wwctx = (struct test_tls13_wwrite_ctx *)ctx;
|
|
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
|
/* Write ChangeCipherSpec message. */
|
|
if (data[0] != 0x14)
|
|
#endif
|
|
{
|
|
wwctx->want_write = !wwctx->want_write;
|
|
if (wwctx->want_write) {
|
|
return WOLFSSL_CBIO_ERR_WANT_WRITE;
|
|
}
|
|
}
|
|
return test_memio_write_cb(ssl, data, sz, wwctx->test_ctx);
|
|
}
|
|
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && WOLFSSL_EARLY_DATA */
|
|
int test_tls13_early_data(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_EARLY_DATA) && defined(HAVE_SESSION_TICKET)
|
|
int written = 0;
|
|
int read = 0;
|
|
size_t i;
|
|
char msg[] = "This is early data";
|
|
char msg2[] = "This is client data";
|
|
char msg3[] = "This is server data";
|
|
char msg4[] = "This is server immediate data";
|
|
char msgBuf[50];
|
|
struct {
|
|
method_provider client_meth;
|
|
method_provider server_meth;
|
|
const char* tls_version;
|
|
int isUdp;
|
|
int splitEarlyData;
|
|
int everyWriteWantWrite;
|
|
} params[] = {
|
|
#ifdef WOLFSSL_TLS13
|
|
{ wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
"TLS 1.3", 0, 0, 0 },
|
|
{ wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
"TLS 1.3", 0, 1, 0 },
|
|
{ wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
"TLS 1.3", 0, 0, 1 },
|
|
{ wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
"TLS 1.3", 0, 1, 1 },
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS13
|
|
{ wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method,
|
|
"DTLS 1.3", 1, 0, 0 },
|
|
{ wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method,
|
|
"DTLS 1.3", 1, 1, 0 },
|
|
{ wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method,
|
|
"DTLS 1.3", 1, 0, 1 },
|
|
{ wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method,
|
|
"DTLS 1.3", 1, 1, 1 },
|
|
#endif
|
|
};
|
|
|
|
for (i = 0; i < sizeof(params)/sizeof(*params) && !EXPECT_FAIL(); i++) {
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
int splitEarlyData = params[i].splitEarlyData;
|
|
int everyWriteWantWrite = params[i].everyWriteWantWrite;
|
|
struct test_tls13_wwrite_ctx wwrite_ctx_s, wwrite_ctx_c;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&wwrite_ctx_c, 0, sizeof(wwrite_ctx_c));
|
|
XMEMSET(&wwrite_ctx_s, 0, sizeof(wwrite_ctx_s));
|
|
|
|
fprintf(stderr, "\tEarly data with %s%s%s\n", params[i].tls_version,
|
|
splitEarlyData ? " (split early data)" : "",
|
|
everyWriteWantWrite ? " (every write WANT_WRITE)" : "");
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s, params[i].client_meth, params[i].server_meth), 0);
|
|
|
|
/* Opt the server into 0-RTT (off by default per RFC 8446 E.5). */
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ),
|
|
0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
|
|
if (params[i].isUdp) {
|
|
/* Early data is incompatible with HRR usage. Hence, we have to make
|
|
* sure a group is negotiated that does not cause a fragemented CH.
|
|
*/
|
|
int group[1] = {
|
|
#ifdef HAVE_ECC
|
|
WOLFSSL_ECC_SECP256R1,
|
|
#elif defined(HAVE_CURVE25519)
|
|
WOLFSSL_ECC_X25519,
|
|
#elif defined(HAVE_CURVE448)
|
|
WOLFSSL_ECC_X448,
|
|
#elif defined(HAVE_FFDHE_2048)
|
|
WOLFSSL_FFDHE_2048,
|
|
#endif
|
|
};
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, group, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s, group, 1), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Get a ticket so that we can do 0-RTT on the next connection */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Make sure we read the ticket */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, msgBuf, sizeof(msgBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s, params[i].client_meth, params[i].server_meth), 0);
|
|
wolfSSL_SetLoggingPrefix("client");
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
#ifdef WOLFSSL_DTLS13
|
|
if (params[i].isUdp) {
|
|
wolfSSL_SetLoggingPrefix("server");
|
|
#ifdef WOLFSSL_DTLS13_NO_HRR_ON_RESUME
|
|
ExpectIntEQ(wolfSSL_dtls13_no_hrr_on_resume(ssl_s, 1),
|
|
WOLFSSL_SUCCESS);
|
|
#else
|
|
/* Let's test this but we generally don't recommend turning off
|
|
* the cookie exchange */
|
|
ExpectIntEQ(wolfSSL_disable_hrr_cookie(ssl_s), WOLFSSL_SUCCESS);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
if (everyWriteWantWrite) {
|
|
wwrite_ctx_c.test_ctx = &test_ctx;
|
|
wwrite_ctx_s.test_ctx = &test_ctx;
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &wwrite_ctx_c);
|
|
wolfSSL_SSLSetIOSend(ssl_c, test_tls13_mock_wantwrite_cb);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &wwrite_ctx_s);
|
|
wolfSSL_SSLSetIOSend(ssl_s, test_tls13_mock_wantwrite_cb);
|
|
}
|
|
/* Test 0-RTT data */
|
|
wolfSSL_SetLoggingPrefix("client");
|
|
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c, msg,
|
|
sizeof(msg), &written),
|
|
sizeof(msg));
|
|
ExpectIntEQ(written, sizeof(msg));
|
|
|
|
if (splitEarlyData) {
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c, msg,
|
|
sizeof(msg), &written),
|
|
sizeof(msg));
|
|
ExpectIntEQ(written, sizeof(msg));
|
|
}
|
|
|
|
/* Read first 0-RTT data (if split otherwise entire data) */
|
|
wolfSSL_SetLoggingPrefix("server");
|
|
ExpectIntEQ(test_tls13_early_data_read_until_write_ok(ssl_s, msgBuf,
|
|
sizeof(msgBuf), &read),
|
|
sizeof(msg));
|
|
ExpectIntEQ(read, sizeof(msg));
|
|
ExpectStrEQ(msg, msgBuf);
|
|
|
|
/* Test 0.5-RTT data */
|
|
ExpectIntEQ(test_tls13_write_until_write_ok(ssl_s, msg4, sizeof(msg4)),
|
|
sizeof(msg4));
|
|
|
|
if (splitEarlyData) {
|
|
/* Read second 0-RTT data */
|
|
ExpectIntEQ(test_tls13_early_data_read_until_write_ok(ssl_s, msgBuf,
|
|
sizeof(msgBuf), &read),
|
|
sizeof(msg));
|
|
ExpectIntEQ(read, sizeof(msg));
|
|
ExpectStrEQ(msg, msgBuf);
|
|
}
|
|
|
|
if (params[i].isUdp) {
|
|
wolfSSL_SetLoggingPrefix("client");
|
|
ExpectIntEQ(test_tls13_connect_until_write_ok(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WC_NO_ERR_TRACE(APP_DATA_READY));
|
|
|
|
/* Read server 0.5-RTT data */
|
|
ExpectIntEQ(
|
|
test_tls13_read_until_write_ok(ssl_c, msgBuf, sizeof(msgBuf)),
|
|
sizeof(msg4));
|
|
ExpectStrEQ(msg4, msgBuf);
|
|
|
|
/* Complete handshake */
|
|
ExpectIntEQ(test_tls13_connect_until_write_ok(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
/* Use wolfSSL_is_init_finished to check if handshake is
|
|
* complete. Normally a user would loop until it is true but
|
|
* here we control both sides so we just assert the expected
|
|
* value. wolfSSL_read_early_data does not provide handshake
|
|
* status to us with non-blocking IO and we can't use
|
|
* wolfSSL_accept as TLS layer may return ZERO_RETURN due to
|
|
* early data parsing logic. */
|
|
wolfSSL_SetLoggingPrefix("server");
|
|
ExpectFalse(wolfSSL_is_init_finished(ssl_s));
|
|
ExpectIntEQ(test_tls13_early_data_read_until_write_ok(ssl_s, msgBuf,
|
|
sizeof(msgBuf), &read),
|
|
0);
|
|
ExpectIntEQ(read, 0);
|
|
ExpectTrue(wolfSSL_is_init_finished(ssl_s));
|
|
|
|
wolfSSL_SetLoggingPrefix("client");
|
|
ExpectIntEQ(test_tls13_connect_until_write_ok(ssl_c),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
else {
|
|
wolfSSL_SetLoggingPrefix("client");
|
|
ExpectIntEQ(test_tls13_connect_until_write_ok(ssl_c),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
wolfSSL_SetLoggingPrefix("server");
|
|
ExpectFalse(wolfSSL_is_init_finished(ssl_s));
|
|
ExpectIntEQ(test_tls13_early_data_read_until_write_ok(ssl_s, msgBuf,
|
|
sizeof(msgBuf), &read),
|
|
0);
|
|
ExpectIntEQ(read, 0);
|
|
ExpectTrue(wolfSSL_is_init_finished(ssl_s));
|
|
|
|
/* Read server 0.5-RTT data */
|
|
wolfSSL_SetLoggingPrefix("client");
|
|
ExpectIntEQ(
|
|
test_tls13_read_until_write_ok(ssl_c, msgBuf, sizeof(msgBuf)),
|
|
sizeof(msg4));
|
|
ExpectStrEQ(msg4, msgBuf);
|
|
}
|
|
|
|
/* Test bi-directional write */
|
|
wolfSSL_SetLoggingPrefix("client");
|
|
ExpectIntEQ(test_tls13_write_until_write_ok(ssl_c, msg2, sizeof(msg2)),
|
|
sizeof(msg2));
|
|
wolfSSL_SetLoggingPrefix("server");
|
|
ExpectIntEQ(
|
|
test_tls13_read_until_write_ok(ssl_s, msgBuf, sizeof(msgBuf)),
|
|
sizeof(msg2));
|
|
ExpectStrEQ(msg2, msgBuf);
|
|
ExpectIntEQ(test_tls13_write_until_write_ok(ssl_s, msg3, sizeof(msg3)),
|
|
sizeof(msg3));
|
|
wolfSSL_SetLoggingPrefix("client");
|
|
ExpectIntEQ(
|
|
test_tls13_read_until_write_ok(ssl_c, msgBuf, sizeof(msgBuf)),
|
|
sizeof(msg3));
|
|
ExpectStrEQ(msg3, msgBuf);
|
|
|
|
wolfSSL_SetLoggingPrefix(NULL);
|
|
ExpectTrue(wolfSSL_session_reused(ssl_c));
|
|
ExpectTrue(wolfSSL_session_reused(ssl_s));
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_EXT_CACHE)
|
|
/* Single-slot external session cache keyed by altSessionID, used by
|
|
* test_tls13_early_data_0rtt_replay to assert the 0-RTT anti-replay
|
|
* fix clears both caches. */
|
|
static struct {
|
|
byte id[ID_LEN];
|
|
byte has_entry;
|
|
WOLFSSL_SESSION* sess;
|
|
int new_calls;
|
|
int get_calls;
|
|
int rem_calls;
|
|
} test_tls13_0rtt_replay_cache;
|
|
|
|
static void test_tls13_0rtt_replay_cache_reset(void)
|
|
{
|
|
/* wolfSSL_SESSION_free is NULL-safe, so unconditionally drop any
|
|
* stored session without touching has_entry first. */
|
|
wolfSSL_SESSION_free(test_tls13_0rtt_replay_cache.sess);
|
|
XMEMSET(&test_tls13_0rtt_replay_cache, 0,
|
|
sizeof(test_tls13_0rtt_replay_cache));
|
|
}
|
|
|
|
/* Stateful-ticket sessions always have haveAltSessionID set, so key the
|
|
* cache on altSessionID directly (wolfSSL_SESSION_get_id is only
|
|
* declared under the OpenSSL compatibility layer). */
|
|
static int test_tls13_0rtt_replay_new_cb(WOLFSSL* ssl, WOLFSSL_SESSION* s)
|
|
{
|
|
(void)ssl;
|
|
test_tls13_0rtt_replay_cache.new_calls++;
|
|
if (s == NULL || !s->haveAltSessionID)
|
|
return 0;
|
|
wolfSSL_SESSION_free(test_tls13_0rtt_replay_cache.sess);
|
|
XMEMCPY(test_tls13_0rtt_replay_cache.id, s->altSessionID, ID_LEN);
|
|
test_tls13_0rtt_replay_cache.sess = s;
|
|
test_tls13_0rtt_replay_cache.has_entry = 1;
|
|
return 1; /* retain the reference; freed in the rem callback */
|
|
}
|
|
|
|
static WOLFSSL_SESSION* test_tls13_0rtt_replay_get_cb(WOLFSSL* ssl,
|
|
const byte* id, int idLen, int* ref)
|
|
{
|
|
(void)ssl;
|
|
test_tls13_0rtt_replay_cache.get_calls++;
|
|
*ref = 1; /* keep ownership; wolfSSL duplicates from us */
|
|
if (!test_tls13_0rtt_replay_cache.has_entry || idLen != ID_LEN)
|
|
return NULL;
|
|
if (XMEMCMP(test_tls13_0rtt_replay_cache.id, id, ID_LEN) != 0)
|
|
return NULL;
|
|
return test_tls13_0rtt_replay_cache.sess;
|
|
}
|
|
|
|
static void test_tls13_0rtt_replay_rem_cb(WOLFSSL_CTX* ctx,
|
|
WOLFSSL_SESSION* s)
|
|
{
|
|
const byte* id;
|
|
(void)ctx;
|
|
if (!test_tls13_0rtt_replay_cache.has_entry || s == NULL)
|
|
return;
|
|
/* Internal-cache-evicted sessions have haveAltSessionID cleared
|
|
* (that field sits before the DupSession copy offset), so fall
|
|
* back to sessionID when altSessionID is not set. Both carry the
|
|
* ID_LEN lookup key. */
|
|
if (s->haveAltSessionID)
|
|
id = s->altSessionID;
|
|
else if (s->sessionIDSz == ID_LEN)
|
|
id = s->sessionID;
|
|
else
|
|
return;
|
|
if (XMEMCMP(test_tls13_0rtt_replay_cache.id, id, ID_LEN) != 0)
|
|
return;
|
|
wolfSSL_SESSION_free(test_tls13_0rtt_replay_cache.sess);
|
|
test_tls13_0rtt_replay_cache.sess = NULL;
|
|
test_tls13_0rtt_replay_cache.has_entry = 0;
|
|
test_tls13_0rtt_replay_cache.rem_calls++;
|
|
}
|
|
|
|
#ifndef WOLFSSL_NO_DEF_TICKET_ENC_CB
|
|
/* Resume sess offering early data. Returns the early-data bytes the server
|
|
* accepted, or -1 if the round did not complete. */
|
|
static int test_tls13_0rtt_replay_round(WOLFSSL_CTX* ctx_c, WOLFSSL_CTX* ctx_s,
|
|
WOLFSSL_SESSION* sess)
|
|
{
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
const char earlyMsg[] = "0rtt-replay";
|
|
char earlyBuf[sizeof(earlyMsg)];
|
|
int written = 0;
|
|
int earlyRead = 0;
|
|
int ret = -1;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(earlyBuf, 0, sizeof(earlyBuf));
|
|
|
|
if (test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method) != 0)
|
|
goto done;
|
|
if (wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ) < 0)
|
|
goto done;
|
|
if (wolfSSL_set_session(ssl_c, sess) != WOLFSSL_SUCCESS)
|
|
goto done;
|
|
if (test_tls13_early_data_write_until_write_ok(ssl_c, earlyMsg,
|
|
(int)sizeof(earlyMsg), &written) != (int)sizeof(earlyMsg))
|
|
goto done;
|
|
(void)test_tls13_early_data_read_until_write_ok(ssl_s, earlyBuf,
|
|
sizeof(earlyBuf), &earlyRead);
|
|
/* Refused early data still leaves a completed 1-RTT handshake. */
|
|
if (test_memio_do_handshake(ssl_c, ssl_s, 10, NULL) != 0)
|
|
goto done;
|
|
if (earlyRead != 0 && (earlyRead != (int)sizeof(earlyMsg) ||
|
|
XMEMCMP(earlyMsg, earlyBuf, sizeof(earlyMsg)) != 0))
|
|
goto done;
|
|
ret = earlyRead;
|
|
done:
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
return ret;
|
|
}
|
|
#endif /* !WOLFSSL_NO_DEF_TICKET_ENC_CB */
|
|
|
|
/* RFC 8446 section 8 anti-replay: a 0-RTT-eligible session must be
|
|
* evicted from both the internal and external caches on resumption so
|
|
* the same ClientHello cannot replay early data. */
|
|
int test_tls13_early_data_0rtt_replay(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
char buf[64];
|
|
int round;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_tls13_0rtt_replay_cache_reset();
|
|
|
|
/* Step 1: full handshake populates both caches. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
/* Stateful tickets + 0-RTT enabled. */
|
|
ExpectTrue(wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_TICKET) != 0);
|
|
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_ERROR_CODE_OPENSSL)
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(ssl_s, 128), WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(ssl_s, 128), 0);
|
|
#endif
|
|
wolfSSL_CTX_sess_set_new_cb(ctx_s, test_tls13_0rtt_replay_new_cb);
|
|
wolfSSL_CTX_sess_set_get_cb(ctx_s, test_tls13_0rtt_replay_get_cb);
|
|
wolfSSL_CTX_sess_set_remove_cb(ctx_s, test_tls13_0rtt_replay_rem_cb);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Let the client consume NewSessionTicket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntEQ(wolfSSL_SessionIsSetup(sess), 1);
|
|
/* Stateful (ID-only) ticket on the client side. */
|
|
ExpectIntEQ(sess->ticketLen, ID_LEN);
|
|
ExpectIntEQ((int)sess->maxEarlyDataSz, 128);
|
|
/* External cache saw the add. */
|
|
ExpectIntGT(test_tls13_0rtt_replay_cache.new_calls, 0);
|
|
ExpectIntEQ(test_tls13_0rtt_replay_cache.has_entry, 1);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Resume the same session twice, offering 0-RTT each time. */
|
|
for (round = 0; round < 2 && !EXPECT_FAIL(); round++) {
|
|
const char earlyMsg[] = "early-data-0rtt";
|
|
int written = 0;
|
|
int earlyRead = 0;
|
|
char earlyBuf[sizeof(earlyMsg)];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(earlyBuf, 0, sizeof(earlyBuf));
|
|
/* Reuse the CTXs so both caches survive (test_memio_setup
|
|
* leaves *ctx alone when non-NULL). */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s, wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method), 0);
|
|
ExpectTrue(wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_TICKET) != 0);
|
|
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_ERROR_CODE_OPENSSL)
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(ssl_s, 128),
|
|
WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_set_max_early_data(ssl_s, 128), 0);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_SessionIsSetup(sess), 1);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c,
|
|
earlyMsg, (int)sizeof(earlyMsg), &written),
|
|
sizeof(earlyMsg));
|
|
ExpectIntEQ(written, sizeof(earlyMsg));
|
|
|
|
(void)test_tls13_early_data_read_until_write_ok(ssl_s, earlyBuf,
|
|
sizeof(earlyBuf), &earlyRead);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (round == 0) {
|
|
ExpectTrue(wolfSSL_session_reused(ssl_s));
|
|
ExpectIntEQ(earlyRead, sizeof(earlyMsg));
|
|
ExpectStrEQ(earlyMsg, earlyBuf);
|
|
/* Fix fired exactly once to evict the cached entry. */
|
|
ExpectIntEQ(test_tls13_0rtt_replay_cache.rem_calls, 1);
|
|
}
|
|
else {
|
|
ExpectFalse(wolfSSL_session_reused(ssl_s));
|
|
ExpectIntEQ(earlyRead, 0);
|
|
/* No additional eviction in the replay round. */
|
|
ExpectIntEQ(test_tls13_0rtt_replay_cache.rem_calls, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
test_tls13_0rtt_replay_cache_reset();
|
|
return EXPECT_RESULT();
|
|
}
|
|
#else
|
|
int test_tls13_early_data_0rtt_replay(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* Verify that maxEarlyDataSz defaults to 0 (RFC 8446 E.5): a server that
|
|
* has not called wolfSSL_set_max_early_data must not advertise 0-RTT in its
|
|
* NewSessionTicket. Fails without the ctx->maxEarlyDataSz=0 default fix
|
|
* because the old default was MAX_EARLY_DATA_SZ (4096). */
|
|
int test_tls13_0rtt_default_off(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
char buf[64];
|
|
int written = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
/* Step 1: handshake WITHOUT opting into 0-RTT on the server. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
/* Deliberately do NOT call wolfSSL_set_max_early_data. */
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Consume NewSessionTicket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Step 2: resume - early data write must fail because the ticket
|
|
* was issued without max_early_data_size. Without the default-to-0
|
|
* fix the old default (4096) would let this succeed. */
|
|
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);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, "test", 4, &written),
|
|
WOLFSSL_FATAL_ERROR);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Verify that a stateless self-encrypted ticket can carry 0-RTT exactly
|
|
* once: the first resumption succeeds with early data, the second (replay)
|
|
* refuses it because wolfSSL_SSL_CTX_remove_session evicted the cache entry.
|
|
* Fails without the WOLFSSL_TICKET_HAVE_ID implication + the
|
|
* remove_session-based gate because the old code either never populated
|
|
* the cache for stateless tickets or never checked the return value. */
|
|
int test_tls13_0rtt_stateless_replay(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
char buf[64];
|
|
int round;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
/* Step 1: full handshake to get a stateless ticket with 0-RTT. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
/* Do NOT set WOLFSSL_OP_NO_TICKET - keep stateless tickets. */
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntEQ(wolfSSL_SessionIsSetup(sess), 1);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Step 2: resume twice, reissuance on. Each new ticket gets its own ID,
|
|
* so round 0's claimed entry is not resurrected. Round 1 = replay. */
|
|
for (round = 0; round < 2 && !EXPECT_FAIL(); round++) {
|
|
const char earlyMsg[] = "stateless-0rtt";
|
|
int written = 0;
|
|
int earlyRead = 0;
|
|
char earlyBuf[sizeof(earlyMsg)];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(earlyBuf, 0, sizeof(earlyBuf));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s, wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c,
|
|
earlyMsg, (int)sizeof(earlyMsg), &written),
|
|
sizeof(earlyMsg));
|
|
ExpectIntEQ(written, sizeof(earlyMsg));
|
|
|
|
(void)test_tls13_early_data_read_until_write_ok(ssl_s, earlyBuf,
|
|
sizeof(earlyBuf), &earlyRead);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (round == 0) {
|
|
/* First use: 0-RTT accepted. */
|
|
ExpectIntEQ(earlyRead, sizeof(earlyMsg));
|
|
ExpectStrEQ(earlyMsg, earlyBuf);
|
|
}
|
|
else {
|
|
/* Replay: 0-RTT refused, handshake still completes (1-RTT). */
|
|
ExpectIntEQ(earlyRead, 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Verify wolfSSL_SSL_CTX_remove_session returns OpenSSL-compatible values:
|
|
* 1 when the session was in the cache and removed, 0 otherwise.
|
|
* Fails without the return-value fix because the old code returned 0/
|
|
* BAD_FUNC_ARG. */
|
|
int test_tls13_remove_session_return(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(WOLFSSL_TICKET_HAVE_ID) && !defined(NO_SESSION_CACHE) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
char buf[64];
|
|
|
|
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);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Consume NewSessionTicket so the cache is populated (AddSession fires
|
|
* because WOLFSSL_TICKET_HAVE_ID is defined). */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_s));
|
|
/* Session is in the cache - first remove returns 1. */
|
|
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_s, sess), 1);
|
|
/* Already removed - second remove returns 0. */
|
|
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_s, sess), 0);
|
|
/* NULL args - returns 0 (not BAD_FUNC_ARG). */
|
|
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(NULL, sess), 0);
|
|
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_s, NULL), 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_EXT_CACHE)
|
|
/* Thin external cache: only tracks rem_calls to verify that
|
|
* wolfSSL_SSL_CTX_remove_session counts the callback as "found". */
|
|
static int test_0rtt_ext_only_rem_calls;
|
|
|
|
static int test_0rtt_ext_only_new_cb(WOLFSSL* ssl, WOLFSSL_SESSION* s)
|
|
{
|
|
(void)ssl; (void)s;
|
|
return 0; /* don't retain */
|
|
}
|
|
|
|
static WOLFSSL_SESSION* test_0rtt_ext_only_get_cb(WOLFSSL* ssl,
|
|
const byte* id, int idLen, int* ref)
|
|
{
|
|
(void)ssl; (void)id; (void)idLen;
|
|
*ref = 0;
|
|
return NULL;
|
|
}
|
|
|
|
static void test_0rtt_ext_only_rem_cb(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* s)
|
|
{
|
|
(void)ctx; (void)s;
|
|
test_0rtt_ext_only_rem_calls++;
|
|
}
|
|
#endif
|
|
|
|
/* Verify that when the internal cache is off but an external cache callback
|
|
* is registered, wolfSSL_SSL_CTX_remove_session returns 1 (the ext callback
|
|
* fired, so we assume the session was present). Fails without the fix
|
|
* because the old code only set found=1 on an internal-cache hit. */
|
|
int test_tls13_0rtt_ext_cache_eviction(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_EXT_CACHE) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
char buf[64];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_0rtt_ext_only_rem_calls = 0;
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
/* Turn off internal cache; rely on external callbacks only. */
|
|
ExpectIntEQ(wolfSSL_CTX_set_session_cache_mode(ctx_s,
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL), WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_sess_set_new_cb(ctx_s, test_0rtt_ext_only_new_cb);
|
|
wolfSSL_CTX_sess_set_get_cb(ctx_s, test_0rtt_ext_only_get_cb);
|
|
wolfSSL_CTX_sess_set_remove_cb(ctx_s, test_0rtt_ext_only_rem_cb);
|
|
|
|
ExpectTrue(wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_TICKET) != 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* remove_session on an ext-cache-only server: rem_cb should fire and
|
|
* the function should return 1 (assumes the ext cache had it). */
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_s));
|
|
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_s, sess), 1);
|
|
ExpectIntGT(test_0rtt_ext_only_rem_calls, 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Establish a stateless-ticket session against an already-configured server
|
|
* CTX and hand back the client session. */
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_EXT_CACHE) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
static int test_tls13_0rtt_make_session(WOLFSSL_CTX* ctx_c, WOLFSSL_CTX* ctx_s,
|
|
WOLFSSL_SESSION** sess)
|
|
{
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
char buf[64];
|
|
int ret = -1;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
*sess = NULL;
|
|
|
|
if (test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method) != 0)
|
|
goto done;
|
|
if (wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ) < 0)
|
|
goto done;
|
|
if (test_memio_do_handshake(ssl_c, ssl_s, 10, NULL) != 0)
|
|
goto done;
|
|
/* Let the client consume NewSessionTicket. */
|
|
if (wolfSSL_read(ssl_c, buf, sizeof(buf)) != -1 ||
|
|
wolfSSL_get_error(ssl_c, -1) != WOLFSSL_ERROR_WANT_READ)
|
|
goto done;
|
|
*sess = wolfSSL_get1_session(ssl_c);
|
|
if (*sess == NULL || !wolfSSL_SessionIsSetup(*sess))
|
|
goto done;
|
|
ret = 0;
|
|
done:
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/* An external-only cache reports presence through its get callback: 0-RTT is
|
|
* accepted once, the replay misses. Fails without the fix. */
|
|
int test_tls13_0rtt_ext_cache_replay(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_EXT_CACHE) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_tls13_0rtt_replay_cache_reset();
|
|
|
|
/* Build the CTXs, then drop the SSL objects: only the CTXs are reused. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_set_session_cache_mode(ctx_s,
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL), WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_sess_set_new_cb(ctx_s, test_tls13_0rtt_replay_new_cb);
|
|
wolfSSL_CTX_sess_set_get_cb(ctx_s, test_tls13_0rtt_replay_get_cb);
|
|
wolfSSL_CTX_sess_set_remove_cb(ctx_s, test_tls13_0rtt_replay_rem_cb);
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
|
|
|
ExpectIntEQ(test_tls13_0rtt_make_session(ctx_c, ctx_s, &sess), 0);
|
|
ExpectIntEQ(test_tls13_0rtt_replay_cache.has_entry, 1);
|
|
|
|
/* First use: cache hit, 0-RTT accepted. Replay: the entry is gone, so
|
|
* early data is refused and the handshake completes as 1-RTT. */
|
|
ExpectIntEQ(test_tls13_0rtt_replay_round(ctx_c, ctx_s, sess),
|
|
(int)sizeof("0rtt-replay"));
|
|
ExpectIntEQ(test_tls13_0rtt_replay_round(ctx_c, ctx_s, sess), 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
test_tls13_0rtt_replay_cache_reset();
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A registered remove callback must not weaken the internal cache's own
|
|
* anti-replay bound. Fails without the fix. */
|
|
int test_tls13_0rtt_remove_cb_replay(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_EXT_CACHE) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_tls13_0rtt_replay_cache_reset();
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Internal cache left on; only the remove callback is registered. */
|
|
wolfSSL_CTX_sess_set_remove_cb(ctx_s, test_tls13_0rtt_replay_rem_cb);
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
|
|
|
ExpectIntEQ(test_tls13_0rtt_make_session(ctx_c, ctx_s, &sess), 0);
|
|
|
|
ExpectIntEQ(test_tls13_0rtt_replay_round(ctx_c, ctx_s, sess),
|
|
(int)sizeof("0rtt-replay"));
|
|
ExpectIntEQ(test_tls13_0rtt_replay_round(ctx_c, ctx_s, sess), 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
test_tls13_0rtt_replay_cache_reset();
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && \
|
|
defined(HAVE_EX_DATA_CRYPTO) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
/* Remove the cache entry for id through wolfSSL_SSL_CTX_remove_session(). */
|
|
static int test_tls13_0rtt_remove_id(WOLFSSL_CTX* ctx, const byte* id)
|
|
{
|
|
WOLFSSL_SESSION* tmp = wolfSSL_SESSION_new();
|
|
int ret = -1;
|
|
|
|
if (tmp != NULL) {
|
|
XMEMCPY(tmp->sessionID, id, ID_LEN);
|
|
tmp->sessionIDSz = ID_LEN;
|
|
ret = wolfSSL_SSL_CTX_remove_session(ctx, tmp);
|
|
}
|
|
wolfSSL_SESSION_free(tmp);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_EX_DATA_CRYPTO) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
static int test_tls13_0rtt_ex_data_marker;
|
|
static int test_tls13_0rtt_ex_data_marker2;
|
|
static int test_tls13_0rtt_ex_data_idx = -1;
|
|
static int test_tls13_0rtt_ex_data_frees;
|
|
static int test_tls13_0rtt_ex_data_frees2;
|
|
|
|
static void test_tls13_0rtt_ex_data_free_cb(void* parent, void* ptr,
|
|
WOLFSSL_CRYPTO_EX_DATA* a, int idx, long argl, void* argp)
|
|
{
|
|
(void)parent;
|
|
(void)ptr;
|
|
(void)argl;
|
|
(void)argp;
|
|
if (idx == test_tls13_0rtt_ex_data_idx) {
|
|
void* data = wolfSSL_CRYPTO_get_ex_data(a, idx);
|
|
if (data == (void*)&test_tls13_0rtt_ex_data_marker)
|
|
test_tls13_0rtt_ex_data_frees++;
|
|
else if (data == (void*)&test_tls13_0rtt_ex_data_marker2)
|
|
test_tls13_0rtt_ex_data_frees2++;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Reissuing a ticket under a new ID must leave one cache entry owning the
|
|
* session ex_data, so it is freed exactly once. */
|
|
int test_tls13_0rtt_ticket_ex_data_owner(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_EX_DATA_CRYPTO) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte idA[ID_LEN];
|
|
byte idB[ID_LEN];
|
|
const byte* id = NULL;
|
|
unsigned int idLen = 0;
|
|
char buf[64];
|
|
|
|
XMEMSET(idA, 0, sizeof(idA));
|
|
XMEMSET(idB, 0, sizeof(idB));
|
|
test_tls13_0rtt_ex_data_frees = 0;
|
|
if (test_tls13_0rtt_ex_data_idx < 0) {
|
|
test_tls13_0rtt_ex_data_idx = wolfSSL_SESSION_get_ex_new_index(0,
|
|
NULL, NULL, NULL, test_tls13_0rtt_ex_data_free_cb);
|
|
}
|
|
ExpectIntGE(test_tls13_0rtt_ex_data_idx, 0);
|
|
|
|
/* Full handshake. The ticket is 0-RTT capable, so it gets its own ID. */
|
|
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);
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntEQ(wolfSSL_SESSION_set_ex_data(wolfSSL_get_session(ssl_s),
|
|
test_tls13_0rtt_ex_data_idx, &test_tls13_0rtt_ex_data_marker),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(id = wolfSSL_SESSION_get_id(wolfSSL_get_session(ssl_s),
|
|
&idLen));
|
|
ExpectIntEQ(idLen, ID_LEN);
|
|
if (id != NULL)
|
|
XMEMCPY(idA, id, ID_LEN);
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Resume without early data. The new ticket gets a new ID. */
|
|
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);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1);
|
|
ExpectNotNull(id = wolfSSL_SESSION_get_id(wolfSSL_get_session(ssl_s),
|
|
&idLen));
|
|
ExpectIntEQ(idLen, ID_LEN);
|
|
if (id != NULL)
|
|
XMEMCPY(idB, id, ID_LEN);
|
|
ExpectIntNE(XMEMCMP(idA, idB, ID_LEN), 0);
|
|
/* The live session still sees its ex_data. */
|
|
ExpectPtrEq(wolfSSL_SESSION_get_ex_data(wolfSSL_get_session(ssl_s),
|
|
test_tls13_0rtt_ex_data_idx), &test_tls13_0rtt_ex_data_marker);
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Drop both entries: the ex_data is freed once. */
|
|
(void)test_tls13_0rtt_remove_id(ctx_s, idA);
|
|
(void)test_tls13_0rtt_remove_id(ctx_s, idB);
|
|
ExpectIntEQ(test_tls13_0rtt_ex_data_frees, 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Two resumptions of one ticket overlap: both pass the ClientHello before
|
|
* either reissues its ticket. Only one new cache entry may own the session
|
|
* ex_data, so it is freed exactly once. */
|
|
int test_tls13_0rtt_ticket_ex_data_overlap(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_EX_DATA_CRYPTO) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
struct test_memio_ctx test_ctx2;
|
|
byte idA[ID_LEN];
|
|
byte idB[ID_LEN];
|
|
byte idC[ID_LEN];
|
|
const byte* id = NULL;
|
|
unsigned int idLen = 0;
|
|
char buf[64];
|
|
|
|
XMEMSET(idA, 0, sizeof(idA));
|
|
XMEMSET(idB, 0, sizeof(idB));
|
|
XMEMSET(idC, 0, sizeof(idC));
|
|
test_tls13_0rtt_ex_data_frees = 0;
|
|
test_tls13_0rtt_ex_data_frees2 = 0;
|
|
if (test_tls13_0rtt_ex_data_idx < 0) {
|
|
test_tls13_0rtt_ex_data_idx = wolfSSL_SESSION_get_ex_new_index(0,
|
|
NULL, NULL, NULL, test_tls13_0rtt_ex_data_free_cb);
|
|
}
|
|
ExpectIntGE(test_tls13_0rtt_ex_data_idx, 0);
|
|
|
|
/* Full handshake. The cache entry for the ticket owns the ex_data. */
|
|
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);
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntEQ(wolfSSL_SESSION_set_ex_data(wolfSSL_get_session(ssl_s),
|
|
test_tls13_0rtt_ex_data_idx, &test_tls13_0rtt_ex_data_marker),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(id = wolfSSL_SESSION_get_id(wolfSSL_get_session(ssl_s),
|
|
&idLen));
|
|
ExpectIntEQ(idLen, ID_LEN);
|
|
if (id != NULL)
|
|
XMEMCPY(idA, id, ID_LEN);
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Start two resumptions without early data. Each server waits for the
|
|
* client Finished, so neither has reissued its ticket yet. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&test_ctx2, 0, sizeof(test_ctx2));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx2, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s2, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c2), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c2, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s2), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s2, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Finish both. Each reissued ticket gets its own ID. The second sets
|
|
* its own ex_data after the first took the old entry's. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_SESSION_set_ex_data(wolfSSL_get_session(ssl_s2),
|
|
test_tls13_0rtt_ex_data_idx, &test_tls13_0rtt_ex_data_marker2),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s2), 1);
|
|
ExpectNotNull(id = wolfSSL_SESSION_get_id(wolfSSL_get_session(ssl_s),
|
|
&idLen));
|
|
ExpectIntEQ(idLen, ID_LEN);
|
|
if (id != NULL)
|
|
XMEMCPY(idB, id, ID_LEN);
|
|
ExpectNotNull(id = wolfSSL_SESSION_get_id(wolfSSL_get_session(ssl_s2),
|
|
&idLen));
|
|
ExpectIntEQ(idLen, ID_LEN);
|
|
if (id != NULL)
|
|
XMEMCPY(idC, id, ID_LEN);
|
|
ExpectIntNE(XMEMCMP(idA, idB, ID_LEN), 0);
|
|
ExpectIntNE(XMEMCMP(idA, idC, ID_LEN), 0);
|
|
ExpectIntNE(XMEMCMP(idB, idC, ID_LEN), 0);
|
|
/* The first reissue took the ex_data. */
|
|
ExpectPtrEq(wolfSSL_SESSION_get_ex_data(wolfSSL_get_session(ssl_s),
|
|
test_tls13_0rtt_ex_data_idx), &test_tls13_0rtt_ex_data_marker);
|
|
ExpectPtrEq(wolfSSL_SESSION_get_ex_data(wolfSSL_get_session(ssl_s2),
|
|
test_tls13_0rtt_ex_data_idx), &test_tls13_0rtt_ex_data_marker2);
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_free(ssl_c2); ssl_c2 = NULL;
|
|
wolfSSL_free(ssl_s2); ssl_s2 = NULL;
|
|
|
|
/* Drop all entries: the ex_data is freed once. */
|
|
(void)test_tls13_0rtt_remove_id(ctx_s, idA);
|
|
(void)test_tls13_0rtt_remove_id(ctx_s, idB);
|
|
(void)test_tls13_0rtt_remove_id(ctx_s, idC);
|
|
ExpectIntEQ(test_tls13_0rtt_ex_data_frees, 1);
|
|
ExpectIntEQ(test_tls13_0rtt_ex_data_frees2, 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 Section 4.2.10: after the server accepts early data, a 0-RTT
|
|
* record that fails to decrypt must result in a fatal bad_record_mac
|
|
* alert. Fails without the fix because the corrupted record was skipped
|
|
* as ignored early data. */
|
|
int test_tls13_early_data_bad_record_mac(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
const char earlyMsg[] = "early-data-bad-mac";
|
|
char earlyBuf[sizeof(earlyMsg)];
|
|
char buf[64];
|
|
int written = 0;
|
|
int earlyRead = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(earlyBuf, 0, sizeof(earlyBuf));
|
|
|
|
/* Mint a ticket that permits 0-RTT. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Resume with early data. */
|
|
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);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c, earlyMsg,
|
|
(int)sizeof(earlyMsg), &written), (int)sizeof(earlyMsg));
|
|
ExpectIntEQ(written, (int)sizeof(earlyMsg));
|
|
|
|
/* Corrupt the first encrypted application_data record from the client
|
|
* before the server decrypts it. */
|
|
if (EXPECT_SUCCESS()) {
|
|
int off = 0;
|
|
int corrupted = 0;
|
|
while (off + 5 <= test_ctx.s_len) {
|
|
int recLen = ((int)test_ctx.s_buff[off + 3] << 8) |
|
|
(int)test_ctx.s_buff[off + 4];
|
|
/* A record whose length runs past the buffer means the scan has
|
|
* lost sync; stop rather than index out of bounds. */
|
|
if (recLen <= 0 || off + 5 + recLen > test_ctx.s_len)
|
|
break;
|
|
if (test_ctx.s_buff[off] == 0x17) {
|
|
test_ctx.s_buff[off + 5] ^= 0x01;
|
|
corrupted = 1;
|
|
break;
|
|
}
|
|
off += 5 + recLen;
|
|
}
|
|
ExpectIntEQ(corrupted, 1);
|
|
}
|
|
|
|
/* Server must send a fatal bad_record_mac alert. */
|
|
ExpectIntEQ(test_tls13_early_data_read_until_write_ok(ssl_s, earlyBuf,
|
|
(int)sizeof(earlyBuf), &earlyRead), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_s, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, bad_record_mac);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 Section 8.2: a freshly started server should reject 0-RTT.
|
|
* Simulate a restart by minting a ticket, recreating the server ctx with
|
|
* the ticket encryption keys preserved and resuming with early data. The
|
|
* ticket predates the new ctx so early data must be rejected while the
|
|
* resumption handshake still completes. */
|
|
int test_tls13_0rtt_fresh_start(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
byte ticketKeys[WOLFSSL_TICKET_KEYS_SZ];
|
|
const char earlyMsg[] = "fresh-start-0rtt";
|
|
char earlyBuf[sizeof(earlyMsg)];
|
|
char buf[64];
|
|
int written = 0;
|
|
int earlyRead = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(earlyBuf, 0, sizeof(earlyBuf));
|
|
|
|
/* Mint a ticket that permits 0-RTT. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntEQ(wolfSSL_CTX_get_tlsext_ticket_keys(ctx_s, ticketKeys,
|
|
sizeof(ticketKeys)), WOLFSSL_SUCCESS);
|
|
|
|
/* "Restart" the server: new ctx with the ticket keys preserved. */
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
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);
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(ctx_s, ticketKeys,
|
|
sizeof(ticketKeys)), WOLFSSL_SUCCESS);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
/* Pretend the restart happened later so that the ticket clearly
|
|
* predates the new ctx. Avoids sub-second timing flakiness. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ctx_s->ticketStartTime += 5000;
|
|
}
|
|
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c, earlyMsg,
|
|
(int)sizeof(earlyMsg), &written), (int)sizeof(earlyMsg));
|
|
(void)test_tls13_early_data_read_until_write_ok(ssl_s, earlyBuf,
|
|
(int)sizeof(earlyBuf), &earlyRead);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* 0-RTT refused, resumption handshake still completes. */
|
|
ExpectIntEQ(earlyRead, 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Disabling the check admits early data from pre-ctx tickets. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(earlyBuf, 0, sizeof(earlyBuf));
|
|
written = 0;
|
|
earlyRead = 0;
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
ExpectIntEQ(wolfSSL_CTX_no_early_data_fresh_start_check(ctx_s), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c, earlyMsg,
|
|
(int)sizeof(earlyMsg), &written), (int)sizeof(earlyMsg));
|
|
(void)test_tls13_early_data_read_until_write_ok(ssl_s, earlyBuf,
|
|
(int)sizeof(earlyBuf), &earlyRead);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(earlyRead, (int)sizeof(earlyMsg));
|
|
ExpectStrEQ(earlyMsg, earlyBuf);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Error returns of wolfSSL_CTX_no_early_data_fresh_start_check(). */
|
|
int test_tls13_0rtt_fresh_start_check_args(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_no_early_data_fresh_start_check(NULL),
|
|
BAD_FUNC_ARG);
|
|
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_no_early_data_fresh_start_check(ctx),
|
|
BAD_FUNC_ARG);
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
#endif
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_no_early_data_fresh_start_check(ctx), SIDE_ERROR);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
/* Check that the client won't send the same CH after a HRR. An HRR without
|
|
* a KeyShare or a Cookie extension will trigger the error. */
|
|
int test_tls13_same_ch(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_AES_128) && \
|
|
defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
|
/* middlebox compat requires that the session ID is echoed */ \
|
|
!defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
/* Transport Layer Security
|
|
* TLSv1.3 Record Layer: Handshake Protocol: Hello Retry Request
|
|
* Content Type: Handshake (22)
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Length: 50
|
|
* Handshake Protocol: Hello Retry Request
|
|
* Handshake Type: Server Hello (2)
|
|
* Length: 46
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Random: cf21ad74e59a6111be1d8c021e65b891c2a211167abb8c5e079e09e2c8a8339c (HelloRetryRequest magic)
|
|
* Session ID Length: 0
|
|
* Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
|
|
* Compression Method: null (0)
|
|
* Extensions Length: 6
|
|
* Extension: supported_versions (len=2) TLS 1.3 */
|
|
unsigned char hrr[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x32, 0x02, 0x00, 0x00, 0x2e, 0x03, 0x03, 0xcf,
|
|
0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, 0x02, 0x1e,
|
|
0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e, 0x07,
|
|
0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c, 0x00, 0x13, 0x01, 0x00, 0x00,
|
|
0x06, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04
|
|
};
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
|
|
wolfTLSv1_3_client_method, NULL), 0);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (char*)hrr,
|
|
sizeof(hrr)), 0);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
/* issue 9653: use a more appropriate error than DUPLICATE_MSG_E.
|
|
* Since the cause of this is missing extension, return that. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), EXT_MISSING);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_hrr_different_cs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384) && \
|
|
defined(BUILD_TLS_CHACHA20_POLY1305_SHA256) && \
|
|
defined(HAVE_ECC) && defined(HAVE_ECC384) && \
|
|
!defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
|
/*
|
|
* TLSv1.3 Record Layer: Handshake Protocol: Hello Retry Request
|
|
* Content Type: Handshake (22)
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Length: 56
|
|
* Handshake Protocol: Hello Retry Request
|
|
* Handshake Type: Server Hello (2)
|
|
* Length: 52
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Random: cf21ad74e59a6111be1d8c021e65b891c2a211167abb8c5e079e09e2c8a8339c (HelloRetryRequest magic)
|
|
* Session ID Length: 0
|
|
* Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
|
|
* Compression Method: null (0)
|
|
* Extensions Length: 12
|
|
* Extension: supported_versions (len=2) TLS 1.3
|
|
* Extension: key_share (len=2) secp384r1
|
|
*
|
|
*/
|
|
unsigned char hrr[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x38, 0x02, 0x00, 0x00, 0x34, 0x03, 0x03, 0xcf,
|
|
0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, 0x02, 0x1e,
|
|
0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e, 0x07,
|
|
0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c, 0x00, 0x13, 0x02, 0x00, 0x00,
|
|
0x0c, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04, 0x00, 0x33, 0x00, 0x02, 0x00,
|
|
0x18
|
|
};
|
|
/*
|
|
* TLSv1.3 Record Layer: Handshake Protocol: Server Hello
|
|
* Content Type: Handshake (22)
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Length: 155
|
|
* Handshake Protocol: Server Hello
|
|
* Handshake Type: Server Hello (2)
|
|
* Length: 151
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Random: 0101010101010101010101010101010101010101010101010101010101010101
|
|
* Session ID Length: 0
|
|
* Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
|
|
* Compression Method: null (0)
|
|
* Extensions Length: 111
|
|
* Extension: key_share (len=101) secp384r1
|
|
* Extension: supported_versions (len=2) TLS 1.3
|
|
*
|
|
*/
|
|
unsigned char sh[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x9b, 0x02, 0x00, 0x00, 0x97, 0x03, 0x03, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x13, 0x03, 0x00, 0x00,
|
|
0x6f, 0x00, 0x33, 0x00, 0x65, 0x00, 0x18, 0x00, 0x61, 0x04, 0x53, 0x3e,
|
|
0xe5, 0xbf, 0x40, 0xec, 0x2d, 0x67, 0x98, 0x8b, 0x77, 0xf3, 0x17, 0x48,
|
|
0x9b, 0xb6, 0xdf, 0x95, 0x29, 0x25, 0xc7, 0x09, 0xfc, 0x03, 0x81, 0x11,
|
|
0x1a, 0x59, 0x56, 0xf2, 0xd7, 0x58, 0x11, 0x0e, 0x59, 0xd3, 0xd7, 0xc1,
|
|
0x72, 0x9e, 0x2c, 0x0d, 0x70, 0xea, 0xf7, 0x73, 0xe6, 0x12, 0x01, 0x16,
|
|
0x42, 0x6d, 0xe2, 0x43, 0x6a, 0x2f, 0x5f, 0xdd, 0x7f, 0xe5, 0x4f, 0xaf,
|
|
0x95, 0x2b, 0x04, 0xfd, 0x13, 0xf5, 0x16, 0xce, 0x62, 0x7f, 0x89, 0xd2,
|
|
0x01, 0x9d, 0x4c, 0x87, 0x96, 0x95, 0x9e, 0x43, 0x33, 0xc7, 0x06, 0x5b,
|
|
0x49, 0x6c, 0xa6, 0x34, 0xd5, 0xdc, 0x63, 0xbd, 0xe9, 0x1f, 0x00, 0x2b,
|
|
0x00, 0x02, 0x03, 0x04
|
|
};
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
|
|
wolfTLSv1_3_client_method, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (char*)hrr,
|
|
sizeof(hrr)), 0);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (char*)sh,
|
|
sizeof(sh)), 0);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), INVALID_PARAMETER);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Server-side complement to test_tls13_hrr_different_cs: the client sends a
|
|
* different cipher suite in CH2 than what the server selected in the HRR. */
|
|
int test_tls13_ch2_different_cs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256) && \
|
|
defined(HAVE_ECC) && defined(HAVE_ECC384)
|
|
/*
|
|
* First ClientHello: cipher suite TLS_AES_256_GCM_SHA384 (0x1302),
|
|
* empty key_share, secp384r1 in supported_groups. This triggers the
|
|
* server to send a HelloRetryRequest selecting TLS_AES_256_GCM_SHA384
|
|
* and requesting a secp384r1 key share.
|
|
*/
|
|
/*
|
|
* TLSv1.3 Record Layer: Handshake Protocol: Client Hello
|
|
* Content Type: Handshake (22)
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Length: 110
|
|
* Handshake Protocol: Client Hello
|
|
* Handshake Type: Client Hello (1)
|
|
* Length: 106
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Random: 0101010101010101010101010101010101010101010101010101010101010101
|
|
* Session ID Length: 32
|
|
* Session ID: 0303030303030303030303030303030303030303030303030303030303030303
|
|
* Cipher Suites Length: 2
|
|
* Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
|
|
* Compression Methods Length: 1
|
|
* Compression Method: null (0)
|
|
* Extensions Length: 31
|
|
* Extension: supported_groups (len=4) secp384r1 (0x0018)
|
|
* Extension: signature_algorithms (len=6) rsa_pkcs1_sha256 (0x0401),
|
|
* rsa_pss_rsae_sha256 (0x0804)
|
|
* Extension: key_share (len=2) client_shares length=0 (empty)
|
|
* Extension: supported_versions (len=3) TLS 1.3 (0x0304)
|
|
*/
|
|
unsigned char ch1[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x6e, 0x01, 0x00, 0x00, 0x6a, 0x03, 0x03, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x00, 0x02, 0x13, 0x02, 0x01, 0x00, 0x00, 0x1f,
|
|
0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x18, 0x00, 0x0d, 0x00, 0x06,
|
|
0x00, 0x04, 0x04, 0x01, 0x08, 0x04, 0x00, 0x33, 0x00, 0x02, 0x00, 0x00,
|
|
0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04
|
|
};
|
|
/*
|
|
* TLSv1.3 Record Layer: Handshake Protocol: Client Hello
|
|
* Content Type: Handshake (22)
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Length: 211
|
|
* Handshake Protocol: Client Hello
|
|
* Handshake Type: Client Hello (1)
|
|
* Length: 207
|
|
* Version: TLS 1.2 (0x0303)
|
|
* Random: 0101010101010101010101010101010101010101010101010101010101010101
|
|
* Session ID Length: 32
|
|
* Session ID: 0303030303030303030303030303030303030303030303030303030303030303
|
|
* Cipher Suites Length: 2
|
|
* Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
|
|
* Compression Methods Length: 1
|
|
* Compression Method: null (0)
|
|
* Extensions Length: 132
|
|
* Extension: supported_groups (len=4) secp384r1 (0x0018)
|
|
* Extension: signature_algorithms (len=6) rsa_pkcs1_sha256 (0x0401),
|
|
* rsa_pss_rsae_sha256 (0x0804)
|
|
* Extension: key_share (len=103)
|
|
* client_shares length: 101
|
|
* KeyShareEntry: group secp384r1 (0x0018), key_exchange length: 97
|
|
* key_exchange: 04 || X(48) || Y(48) (uncompressed P-384 point)
|
|
* Extension: supported_versions (len=3) TLS 1.3 (0x0304)
|
|
*/
|
|
unsigned char ch2[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0xd3, 0x01, 0x00, 0x00, 0xcf, 0x03, 0x03, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x00, 0x02, 0x13, 0x01, 0x01, 0x00, 0x00, 0x84,
|
|
0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x18, 0x00, 0x0d, 0x00, 0x06,
|
|
0x00, 0x04, 0x04, 0x01, 0x08, 0x04, 0x00, 0x33, 0x00, 0x67, 0x00, 0x65,
|
|
0x00, 0x18, 0x00, 0x61, 0x04, 0x53, 0x3e, 0xe5, 0xbf, 0x40, 0xec, 0x2d,
|
|
0x67, 0x98, 0x8b, 0x77, 0xf3, 0x17, 0x48, 0x9b, 0xb6, 0xdf, 0x95, 0x29,
|
|
0x25, 0xc7, 0x09, 0xfc, 0x03, 0x81, 0x11, 0x1a, 0x59, 0x56, 0xf2, 0xd7,
|
|
0x58, 0x11, 0x0e, 0x59, 0xd3, 0xd7, 0xc1, 0x72, 0x9e, 0x2c, 0x0d, 0x70,
|
|
0xea, 0xf7, 0x73, 0xe6, 0x12, 0x01, 0x16, 0x42, 0x6d, 0xe2, 0x43, 0x6a,
|
|
0x2f, 0x5f, 0xdd, 0x7f, 0xe5, 0x4f, 0xaf, 0x95, 0x2b, 0x04, 0xfd, 0x13,
|
|
0xf5, 0x16, 0xce, 0x62, 0x7f, 0x89, 0xd2, 0x01, 0x9d, 0x4c, 0x87, 0x96,
|
|
0x95, 0x9e, 0x43, 0x33, 0xc7, 0x06, 0x5b, 0x49, 0x6c, 0xa6, 0x34, 0xd5,
|
|
0xdc, 0x63, 0xbd, 0xe9, 0x1f, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* Server reads CH1, sends HRR, then waits for CH2 */
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (char*)ch1,
|
|
sizeof(ch1)), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Server must reject CH2 because the cipher suite changed from the HRR */
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (char*)ch2,
|
|
sizeof(ch2)), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), INVALID_PARAMETER);
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_ECC) && !defined(NO_FILESYSTEM)
|
|
/* Called when writing. */
|
|
static int MESend(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)sz;
|
|
(void)ctx;
|
|
|
|
/* Force error return from wolfSSL_accept_TLSv13(). */
|
|
return WANT_WRITE;
|
|
}
|
|
/* Called when reading. */
|
|
static int MERecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
WOLFSSL_BUFFER_INFO* msg = (WOLFSSL_BUFFER_INFO*)ctx;
|
|
int len = (int)msg->length;
|
|
|
|
(void)ssl;
|
|
|
|
/* Pass back as much of message as will fit in buffer. */
|
|
if (len > sz)
|
|
len = sz;
|
|
XMEMCPY(buf, msg->buffer, len);
|
|
/* Move over returned data. */
|
|
msg->buffer += len;
|
|
msg->length -= len;
|
|
|
|
/* Amount actually copied. */
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_sg_missing(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_ECC) && !defined(NO_FILESYSTEM)
|
|
WOLFSSL_CTX *ctx = NULL;
|
|
WOLFSSL *ssl = NULL;
|
|
byte clientHello[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0xcb, 0x01, 0x00, 0x00,
|
|
0xc7, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x20, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x00, 0x02, 0x13, 0x01,
|
|
0x01, 0x00, 0x00, 0x7c, 0x00, 0x0d, 0x00, 0x06,
|
|
0x00, 0x04, 0x04, 0x01, 0x08, 0x04,
|
|
/* KeyShare */
|
|
0x00, 0x33,
|
|
0x00, 0x67, 0x00, 0x65, 0x00, 0x18, 0x00, 0x61,
|
|
0x04, 0x53, 0x3e, 0xe5, 0xbf, 0x40, 0xec, 0x2d,
|
|
0x67, 0x98, 0x8b, 0x77, 0xf3, 0x17, 0x48, 0x9b,
|
|
0xb6, 0xdf, 0x95, 0x29, 0x25, 0xc7, 0x09, 0xfc,
|
|
0x03, 0x81, 0x11, 0x1a, 0x59, 0x56, 0xf2, 0xd7,
|
|
0x58, 0x11, 0x0e, 0x59, 0xd3, 0xd7, 0xc1, 0x72,
|
|
0x9e, 0x2c, 0x0d, 0x70, 0xea, 0xf7, 0x73, 0xe6,
|
|
0x12, 0x01, 0x16, 0x42, 0x6d, 0xe2, 0x43, 0x6a,
|
|
0x2f, 0x5f, 0xdd, 0x7f, 0xe5, 0x4f, 0xaf, 0x95,
|
|
0x2b, 0x04, 0xfd, 0x13, 0xf5, 0x16, 0xce, 0x62,
|
|
0x7f, 0x89, 0xd2, 0x01, 0x9d, 0x4c, 0x87, 0x96,
|
|
0x95, 0x9e, 0x43, 0x33, 0xc7, 0x06, 0x5b, 0x49,
|
|
0x6c, 0xa6, 0x34, 0xd5, 0xdc, 0x63, 0xbd, 0xe9,
|
|
0x1f,
|
|
/* SupportedVersions */
|
|
0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04
|
|
/* Missing SupportedGroups. */
|
|
};
|
|
WOLFSSL_BUFFER_INFO msg;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
|
|
/* Set up wolfSSL context. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, eccCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, eccKeyFile,
|
|
CERT_FILETYPE));
|
|
/* Read from 'msg'. */
|
|
wolfSSL_SetIORecv(ctx, MERecv);
|
|
/* No where to send to - dummy sender. */
|
|
wolfSSL_SetIOSend(ctx, MESend);
|
|
|
|
/* Test cipher suite list with many copies of a cipher suite. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = clientHello;
|
|
msg.length = (unsigned int)sizeof(clientHello);
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
|
|
ExpectIntEQ(wolfSSL_accept_TLSv13(ssl),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, missing_extension);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_ks_missing(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_ECC) && !defined(NO_FILESYSTEM)
|
|
WOLFSSL_CTX *ctx = NULL;
|
|
WOLFSSL *ssl = NULL;
|
|
byte clientHello[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x66, 0x01, 0x00, 0x00,
|
|
0x62, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x20, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x00, 0x02, 0x13, 0x01,
|
|
0x01, 0x00, 0x00, 0x17, 0x00, 0x0d, 0x00, 0x06,
|
|
0x00, 0x04, 0x04, 0x01, 0x08, 0x04,
|
|
/* SupportedGroups */
|
|
0x00, 0x0a,
|
|
0x00, 0x02, 0x00, 0x18,
|
|
/* SupportedVersions */
|
|
0x00, 0x2b, 0x00, 0x03,
|
|
0x02, 0x03, 0x04
|
|
/* Missing KeyShare. */
|
|
};
|
|
WOLFSSL_BUFFER_INFO msg;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
|
|
/* Set up wolfSSL context. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, eccCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, eccKeyFile,
|
|
CERT_FILETYPE));
|
|
/* Read from 'msg'. */
|
|
wolfSSL_SetIORecv(ctx, MERecv);
|
|
/* No where to send to - dummy sender. */
|
|
wolfSSL_SetIOSend(ctx, MESend);
|
|
|
|
/* Test cipher suite list with many copies of a cipher suite. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = clientHello;
|
|
msg.length = (unsigned int)sizeof(clientHello);
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
|
|
ExpectIntEQ(wolfSSL_accept_TLSv13(ssl),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, missing_extension);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
defined(HAVE_ECC)
|
|
/* Called when writing. */
|
|
static int DESend(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)sz;
|
|
(void)ctx;
|
|
|
|
return sz;
|
|
}
|
|
/* Called when reading. */
|
|
static int DERecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
WOLFSSL_BUFFER_INFO* msg = (WOLFSSL_BUFFER_INFO*)ctx;
|
|
int len = (int)msg->length;
|
|
|
|
(void)ssl;
|
|
(void)sz;
|
|
|
|
/* Pass back as much of message as will fit in buffer. */
|
|
if (len > sz)
|
|
len = sz;
|
|
XMEMCPY(buf, msg->buffer, len);
|
|
/* Move over returned data. */
|
|
msg->buffer += len;
|
|
msg->length -= len;
|
|
|
|
/* Amount actually copied. */
|
|
return len;
|
|
}
|
|
|
|
#endif
|
|
|
|
int test_tls13_duplicate_extension(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
defined(HAVE_ECC)
|
|
WOLFSSL_CTX *ctx = NULL;
|
|
WOLFSSL *ssl = NULL;
|
|
byte serverHello[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x81, 0x02, 0x00, 0x00,
|
|
0x7d, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x00, 0x13, 0x01, 0x00, 0x00,
|
|
0x55, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04, 0x00,
|
|
0x33, 0x00, 0x45, 0x00, 0x17, 0x00, 0x41, 0x04,
|
|
0x0c, 0x90, 0x1d, 0x42, 0x3c, 0x83, 0x1c, 0xa8,
|
|
0x5e, 0x27, 0xc7, 0x3c, 0x26, 0x3b, 0xa1, 0x32,
|
|
0x72, 0x1b, 0xb9, 0xd7, 0xa8, 0x4c, 0x4f, 0x03,
|
|
0x80, 0xb2, 0xa6, 0x75, 0x6f, 0xd6, 0x01, 0x33,
|
|
0x1c, 0x88, 0x70, 0x23, 0x4d, 0xec, 0x87, 0x85,
|
|
0x04, 0xc1, 0x74, 0x14, 0x4f, 0xa4, 0xb1, 0x4b,
|
|
0x66, 0xa6, 0x51, 0x69, 0x16, 0x06, 0xd8, 0x17,
|
|
0x3e, 0x55, 0xbd, 0x37, 0xe3, 0x81, 0x56, 0x9e,
|
|
0x00, 0x2b, 0x00, 0x02, 0x03, 0x04
|
|
};
|
|
WOLFSSL_BUFFER_INFO msg;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
|
|
/* Set up wolfSSL context. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
/* Read from 'msg'. */
|
|
wolfSSL_SetIORecv(ctx, DERecv);
|
|
/* No where to send to - dummy sender. */
|
|
wolfSSL_SetIOSend(ctx, DESend);
|
|
|
|
/* Test cipher suite list with many copies of a cipher suite. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = serverHello;
|
|
msg.length = (unsigned int)sizeof(serverHello);
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
|
|
ExpectIntEQ(wolfSSL_connect_TLSv13(ssl),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, illegal_parameter);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && \
|
|
(!defined(NO_RSA) || defined(HAVE_ECC))
|
|
static int DupEchSend(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)sz;
|
|
(void)ctx;
|
|
|
|
return sz;
|
|
}
|
|
static int DupEchRecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
WOLFSSL_BUFFER_INFO* msg = (WOLFSSL_BUFFER_INFO*)ctx;
|
|
int len = (int)msg->length;
|
|
|
|
(void)ssl;
|
|
(void)sz;
|
|
|
|
if (len > sz)
|
|
len = sz;
|
|
XMEMCPY(buf, msg->buffer, len);
|
|
msg->buffer += len;
|
|
msg->length -= len;
|
|
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
/* Test detection of duplicate ECH extension (type 0xfe0d) in ClientHello.
|
|
* ECH has a semaphore mapping in TLSX_ToSemaphore() and needs to be included
|
|
* in the duplicate-detection gate in TLSX_Parse(). RFC 8446 section 4.2
|
|
* requires rejecting messages with duplicate extensions.
|
|
*/
|
|
int test_tls13_duplicate_ech_extension(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && \
|
|
(!defined(NO_RSA) || defined(HAVE_ECC))
|
|
/* TLS 1.3 ClientHello with two ECH extensions (type 0xfe0d).
|
|
* Extensions block contains: supported_versions + ECH + ECH (dup). */
|
|
const unsigned char clientHelloDupEch[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x40, 0x01, 0x00, 0x00,
|
|
0x3c, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x13, 0x01,
|
|
0x01, 0x00, 0x00, 0x11, 0x00, 0x2b, 0x00, 0x03,
|
|
0x02, 0x03, 0x04, 0xfe, 0x0d, 0x00, 0x01, 0x00,
|
|
0xfe, 0x0d, 0x00, 0x01, 0x00
|
|
};
|
|
WOLFSSL_BUFFER_INFO msg;
|
|
const char* testCertFile;
|
|
const char* testKeyFile;
|
|
WOLFSSL_CTX *ctx = NULL;
|
|
WOLFSSL *ssl = NULL;
|
|
|
|
#ifndef NO_RSA
|
|
testCertFile = svrCertFile;
|
|
testKeyFile = svrKeyFile;
|
|
#elif defined(HAVE_ECC)
|
|
testCertFile = eccCertFile;
|
|
testKeyFile = eccKeyFile;
|
|
#endif
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, testCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, testKeyFile,
|
|
CERT_FILETYPE));
|
|
|
|
/* Read from 'msg'. */
|
|
wolfSSL_SetIORecv(ctx, DupEchRecv);
|
|
/* No where to send to - dummy sender. */
|
|
wolfSSL_SetIOSend(ctx, DupEchSend);
|
|
|
|
ssl = wolfSSL_new(ctx);
|
|
ExpectNotNull(ssl);
|
|
|
|
msg.buffer = (unsigned char*)clientHelloDupEch;
|
|
msg.length = (unsigned int)sizeof(clientHelloDupEch);
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
|
|
ExpectIntNE(wolfSSL_accept(ssl), WOLFSSL_SUCCESS);
|
|
/* Can return duplicate ext error or socket error if the peer closed
|
|
* down while sending alert. */
|
|
if (wolfSSL_get_error(ssl, 0) != WC_NO_ERR_TRACE(SOCKET_ERROR_E)) {
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, 0),
|
|
WC_NO_ERR_TRACE(DUPLICATE_TLS_EXT_E));
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && defined(HAVE_SNI) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA)
|
|
|
|
#define ECH_TEST_PUB_NAME "ech-public-name.com"
|
|
#define ECH_TEST_PRIV_NAME "ech-private-name.com"
|
|
|
|
/* Bring an ECH client and server up to the point where the server has written
|
|
* a genuine, ECH-accepting HelloRetryRequest into the client's memio buffer. */
|
|
static int EchHrrSetup(struct test_memio_ctx* test_ctx, WOLFSSL_CTX** ctx_c,
|
|
WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c, WOLFSSL** ssl_s)
|
|
{
|
|
EXPECT_DECLS;
|
|
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);
|
|
if (EXPECT_FAIL())
|
|
return EXPECT_RESULT();
|
|
|
|
wolfSSL_set_verify(*ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(*ctx_s, ECH_TEST_PUB_NAME, 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,
|
|
ECH_TEST_PRIV_NAME, (word16)XSTRLEN(ECH_TEST_PRIV_NAME)),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSNI(*ssl_s, WOLFSSL_SNI_HOST_NAME,
|
|
ECH_TEST_PRIV_NAME, (word16)XSTRLEN(ECH_TEST_PRIV_NAME)),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* No key share in ClientHello1, so the server answers with a
|
|
* HelloRetryRequest that carries a valid ECH confirmation. */
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(*ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(wolfSSL_connect(*ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(*ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(*ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(*ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_memio_msg_is_hello_retry_request(test_ctx), 1);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Build a ServerHello handshake message that echoes the session id and cipher
|
|
* suite of the HelloRetryRequest record in hrr, so the client's pre-ECH checks
|
|
* pass. Returns the message length, or -1. */
|
|
static int EchBuildServerHello(const byte* hrr, int hrrSz, byte* out,
|
|
int outSz)
|
|
{
|
|
int sessIdSz;
|
|
int csOff;
|
|
int bodySz;
|
|
int idx = 0;
|
|
|
|
/* record header (5) + handshake header (4) + legacy_version (2) +
|
|
* random (32) is where the HelloRetryRequest's session id length sits. */
|
|
if (hrrSz < 5 + 4 + 2 + RAN_LEN + 1)
|
|
return -1;
|
|
sessIdSz = hrr[5 + 4 + 2 + RAN_LEN];
|
|
csOff = 5 + 4 + 2 + RAN_LEN + 1 + sessIdSz;
|
|
if (hrrSz < csOff + 2)
|
|
return -1;
|
|
|
|
/* version + random + session id + cipher suite + compression +
|
|
* a 6-byte supported_versions extension block */
|
|
bodySz = 2 + RAN_LEN + 1 + sessIdSz + 2 + 1 + 2 + 6;
|
|
if (outSz < bodySz + 4)
|
|
return -1;
|
|
|
|
out[idx++] = server_hello;
|
|
out[idx++] = 0x00;
|
|
out[idx++] = (byte)(bodySz >> 8);
|
|
out[idx++] = (byte)bodySz;
|
|
out[idx++] = SSLv3_MAJOR;
|
|
out[idx++] = TLSv1_2_MINOR;
|
|
XMEMSET(out + idx, 0x41, RAN_LEN);
|
|
idx += RAN_LEN;
|
|
out[idx++] = (byte)sessIdSz;
|
|
XMEMCPY(out + idx, hrr + 5 + 4 + 2 + RAN_LEN + 1, (size_t)sessIdSz);
|
|
idx += sessIdSz;
|
|
out[idx++] = hrr[csOff];
|
|
out[idx++] = hrr[csOff + 1];
|
|
out[idx++] = 0x00;
|
|
out[idx++] = 0x00;
|
|
out[idx++] = 0x06;
|
|
out[idx++] = 0x00;
|
|
out[idx++] = TLSX_SUPPORTED_VERSIONS;
|
|
out[idx++] = 0x00;
|
|
out[idx++] = 0x02;
|
|
out[idx++] = SSLv3_MAJOR;
|
|
out[idx++] = TLSv1_3_MINOR;
|
|
|
|
return idx;
|
|
}
|
|
#endif
|
|
|
|
/* A ServerHello coalesced after the HelloRetryRequest in one record: the ECH
|
|
* acceptance check used to take its offsets from the record base, so the
|
|
* trailing hash length went negative and ran off the end of the record. */
|
|
int test_tls13_ech_hrr_coalesced_server_hello(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && defined(HAVE_SNI) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* hrr = NULL;
|
|
int hrrSz = 0;
|
|
byte sh[128];
|
|
int shSz = 0;
|
|
byte rec[512];
|
|
int recLen;
|
|
|
|
ExpectIntEQ(EchHrrSetup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_memio_get_message(&test_ctx, 1, &hrr, &hrrSz, 0), 0);
|
|
ExpectIntGT(shSz = EchBuildServerHello((const byte*)hrr, hrrSz, sh,
|
|
(int)sizeof(sh)), 0);
|
|
ExpectIntLE(hrrSz + shSz, (int)sizeof(rec));
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(rec, hrr, (size_t)hrrSz);
|
|
XMEMCPY(rec + hrrSz, sh, (size_t)shSz);
|
|
/* patch the record length to cover both handshake messages */
|
|
recLen = ((rec[3] << 8) | rec[4]) + shSz;
|
|
rec[3] = (byte)(recLen >> 8);
|
|
rec[4] = (byte)recLen;
|
|
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)rec,
|
|
hrrSz + shSz), 0);
|
|
}
|
|
|
|
/* RFC 9849 6.1.5: the confirmation fails on the appended ServerHello, so
|
|
* ECH is rejected after the HelloRetryRequest accepted it. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(INVALID_PARAMETER));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The same offsets against a ServerHello reassembled from two records, where
|
|
* input points at the message body: ECH acceptance used to read 4 bytes past
|
|
* ssl->pendingMsg and report a rejection the server never sent. */
|
|
int test_tls13_ech_fragmented_server_hello(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && defined(HAVE_SNI) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA)
|
|
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 configs[512];
|
|
word32 configsLen = (word32)sizeof(configs);
|
|
byte buf[TEST_MEMIO_BUF_SZ];
|
|
int len = 0;
|
|
int shLen = 0;
|
|
int frag1 = 16;
|
|
|
|
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, ECH_TEST_PUB_NAME, 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,
|
|
ECH_TEST_PRIV_NAME, (word16)XSTRLEN(ECH_TEST_PRIV_NAME)),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSNI(ssl_s, WOLFSSL_SNI_HOST_NAME,
|
|
ECH_TEST_PRIV_NAME, (word16)XSTRLEN(ECH_TEST_PRIV_NAME)),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Record 0 of the server's flight is the plaintext ServerHello; split it
|
|
* into two records so the client has to reassemble it. */
|
|
ExpectIntGT(test_ctx.c_len, 5);
|
|
if (EXPECT_SUCCESS()) {
|
|
shLen = (test_ctx.c_buff[3] << 8) | test_ctx.c_buff[4];
|
|
ExpectIntGT(shLen, frag1);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(buf, test_ctx.c_buff, 5);
|
|
buf[3] = (byte)(frag1 >> 8);
|
|
buf[4] = (byte)frag1;
|
|
len = 5;
|
|
XMEMCPY(buf + len, test_ctx.c_buff + 5, (size_t)frag1);
|
|
len += frag1;
|
|
XMEMCPY(buf + len, test_ctx.c_buff, 5);
|
|
buf[len + 3] = (byte)((shLen - frag1) >> 8);
|
|
buf[len + 4] = (byte)(shLen - frag1);
|
|
len += 5;
|
|
XMEMCPY(buf + len, test_ctx.c_buff + 5 + frag1,
|
|
(size_t)(shLen - frag1));
|
|
len += shLen - frag1;
|
|
XMEMCPY(buf + len, test_ctx.c_buff + 5 + shLen,
|
|
(size_t)(test_ctx.c_len - 5 - shLen));
|
|
len += test_ctx.c_len - 5 - shLen;
|
|
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)buf,
|
|
len), 0);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_GetEchStatus(ssl_c), WOLFSSL_ECH_STATUS_ACCEPTED);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
int test_key_share_mismatch(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_ECC) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256) && (!defined(WOLFSSL_SP_MATH) || \
|
|
(defined(WOLFSSL_SP_521) && !defined(WOLFSSL_SP_NO_256) && \
|
|
defined(WOLFSSL_SP_384)))
|
|
/* Taken from payload in https://github.com/wolfSSL/wolfssl/issues/9362 */
|
|
const byte ch1_bin[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x96, 0x01, 0x00, 0x00, 0x92, 0x03, 0x03, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x00, 0x02, 0x13, 0x01, 0x01, 0x00, 0x00, 0x47,
|
|
0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x18, 0x00, 0x17, 0x00, 0x1d,
|
|
0x00, 0x0d, 0x00, 0x06, 0x00, 0x04, 0x04, 0x01, 0x08, 0x04, 0x00, 0x33,
|
|
0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x07, 0xaa, 0xff, 0x3e,
|
|
0x9f, 0xc1, 0x67, 0x27, 0x55, 0x44, 0xf4, 0xc3, 0xa6, 0xa1, 0x7c, 0xd8,
|
|
0x37, 0xf2, 0xec, 0x6e, 0x78, 0xcd, 0x8a, 0x57, 0xb1, 0xe3, 0xdf, 0xb3,
|
|
0xcc, 0x03, 0x5a, 0x76, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04
|
|
};
|
|
const byte ch2_bin[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0xb7, 0x01, 0x00, 0x00, 0xb3, 0x03, 0x03, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x20, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
|
0x03, 0x03, 0x03, 0x03, 0x00, 0x02, 0x13, 0x01, 0x01, 0x00, 0x00, 0x68,
|
|
0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x18, 0x00, 0x17, 0x00, 0x1d,
|
|
0x00, 0x0d, 0x00, 0x06, 0x00, 0x04, 0x04, 0x01, 0x08, 0x04, 0x00, 0x33,
|
|
0x00, 0x47, 0x00, 0x45, 0x00, 0x17, 0x00, 0x41, 0x04, 0x0c, 0x90, 0x1d,
|
|
0x42, 0x3c, 0x83, 0x1c, 0xa8, 0x5e, 0x27, 0xc7, 0x3c, 0x26, 0x3b, 0xa1,
|
|
0x32, 0x72, 0x1b, 0xb9, 0xd7, 0xa8, 0x4c, 0x4f, 0x03, 0x80, 0xb2, 0xa6,
|
|
0x75, 0x6f, 0xd6, 0x01, 0x33, 0x1c, 0x88, 0x70, 0x23, 0x4d, 0xec, 0x87,
|
|
0x85, 0x04, 0xc1, 0x74, 0x14, 0x4f, 0xa4, 0xb1, 0x4b, 0x66, 0xa6, 0x51,
|
|
0x69, 0x16, 0x06, 0xd8, 0x17, 0x3e, 0x55, 0xbd, 0x37, 0xe3, 0x81, 0x56,
|
|
0x9e, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04
|
|
};
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
int client_group[] = {WOLFSSL_ECC_SECP521R1};
|
|
int server_group[] = {WOLFSSL_ECC_SECP384R1, WOLFSSL_ECC_SECP256R1};
|
|
int ret;
|
|
|
|
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);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c,
|
|
client_group, XELEM_CNT(client_group)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s,
|
|
server_group, XELEM_CNT(server_group)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), -1);
|
|
/* No mutual group: server sends a fatal handshake_failure alert instead
|
|
* of an HRR for a group the client never advertised (RFC 8446 4.2.1).
|
|
* The client reads the alert on the next connect. */
|
|
ret = wolfSSL_connect(ssl_c);
|
|
ExpectIntNE(ret, WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, ret), WC_NO_ERR_TRACE(FATAL_ERROR));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_rx.code, handshake_failure);
|
|
ExpectIntEQ(h.last_rx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s,
|
|
server_group, XELEM_CNT(server_group)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)ch1_bin,
|
|
sizeof(ch1_bin)), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)ch2_bin,
|
|
sizeof(ch2_bin)), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), BAD_KEY_SHARE_DATA);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* PSK-DHE variant of test_key_share_mismatch: the certificate handshake above
|
|
* fails in MatchSuite before reaching TLSX_KeyShare_Establish, which only the
|
|
* PSK-DHE path (usingPSK == 2) drives. */
|
|
int test_key_share_mismatch_psk_dhe(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_ECC) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
(!defined(WOLFSSL_SP_MATH) || (defined(WOLFSSL_SP_521) && \
|
|
!defined(WOLFSSL_SP_NO_256) && defined(WOLFSSL_SP_384)))
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
int client_group[] = {WOLFSSL_ECC_SECP521R1};
|
|
int server_group[] = {WOLFSSL_ECC_SECP384R1, WOLFSSL_ECC_SECP256R1};
|
|
int ret;
|
|
|
|
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_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c,
|
|
client_group, XELEM_CNT(client_group)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s,
|
|
server_group, XELEM_CNT(server_group)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), -1);
|
|
/* PSK-DHE needs a key share, and no group overlaps, so the server sends
|
|
* a fatal handshake_failure alert (RFC 8446 4.2.1). The client reads the
|
|
* alert on the next connect. */
|
|
ret = wolfSSL_connect(ssl_c);
|
|
ExpectIntNE(ret, WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, ret), WC_NO_ERR_TRACE(FATAL_ERROR));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_rx.code, handshake_failure);
|
|
ExpectIntEQ(h.last_rx.level, alert_fatal);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WC_NO_ERR_TRACE(KEY_SHARE_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
defined(HAVE_AESGCM) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_FILESYSTEM)
|
|
/* Called when writing. */
|
|
static int Tls13PTASend(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)ctx;
|
|
|
|
return sz;
|
|
}
|
|
static int Tls13PTARecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
WOLFSSL_BUFFER_INFO* msg = (WOLFSSL_BUFFER_INFO*)ctx;
|
|
int len;
|
|
|
|
(void)ssl;
|
|
|
|
if (msg->length == 0) {
|
|
/* Only do as many alerts as required to get to max alert count. */
|
|
msg->buffer[0]--;
|
|
if (msg->buffer[0] > 0) {
|
|
msg->buffer -= 7;
|
|
msg->length += 7;
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
len = (int)msg->length;
|
|
/* Pass back as much of message as will fit in buffer. */
|
|
if (len > sz)
|
|
len = sz;
|
|
XMEMCPY(buf, msg->buffer, len);
|
|
/* Move over returned data. */
|
|
msg->buffer += len;
|
|
msg->length -= len;
|
|
|
|
/* Amount actually copied. */
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* How many ChangeCipherSpec records does this buffer of TLS records carry?
|
|
* A count rather than a yes or no, so that a second record going out, which
|
|
* is what marking the record sent before the send guards against, does not
|
|
* read the same as the one record that is owed. */
|
|
static int test_tls13_count_ccs(const byte* buff, int len)
|
|
{
|
|
int i = 0;
|
|
int count = 0;
|
|
|
|
while (i + RECORD_HEADER_SZ <= len) {
|
|
if (buff[i] == change_cipher_spec)
|
|
count++;
|
|
i += RECORD_HEADER_SZ + ((buff[i + 3] << 8) | buff[i + 4]);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
#endif
|
|
|
|
/* Test that when a TLS 1.3 client sends a ClientHello with an empty
|
|
* legacy_session_id (indicating no middlebox compatibility), the server
|
|
* should NOT send a ChangeCipherSpec message. Per RFC 8446 Appendix D.4,
|
|
* the server only sends CCS if the client's ClientHello contains a
|
|
* non-empty session_id.
|
|
*
|
|
* This test reproduces the bug reported in GitHub issue #9156, where the
|
|
* server sent a ChangeCipherSpec whatever the client's session_id was.
|
|
*/
|
|
int test_tls13_middlebox_compat_empty_session_id(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* Without WOLFSSL_TLS13_MIDDLEBOX_COMPAT the client already sends an
|
|
* empty legacy_session_id; clearing the flag covers the build where it
|
|
* would otherwise send a fake one. Either way the server must respect
|
|
* the empty id and NOT send a ChangeCipherSpec. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->options.tls13MiddleBoxCompat = 0;
|
|
}
|
|
|
|
/* Client sends ClientHello with empty session ID */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Server processes ClientHello and sends its flight:
|
|
* ServerHello, EncryptedExtensions, Certificate, CertVerify, Finished
|
|
* (and potentially an unwanted CCS) */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* The server writes into c_buff. It must NOT have sent a CCS, since the
|
|
* client's ClientHello had an empty legacy_session_id. Sending one is
|
|
* the bug from issue #9156. */
|
|
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 Section 4.1.2: in middlebox compatibility mode a client not
|
|
* offering a pre-TLS 1.3 session must send a non-empty 32-byte
|
|
* legacy_session_id. */
|
|
int test_tls13_middlebox_compat_session_id(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* Client sends ClientHello. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* 5 byte record header + 4 byte handshake header + 2 byte
|
|
* legacy_version + 32 byte random = offset 43 holds the
|
|
* legacy_session_id length. */
|
|
ExpectIntGT(test_ctx.s_len, 43 + ID_LEN);
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(test_ctx.s_buff[0], 0x16); /* handshake */
|
|
ExpectIntEQ(test_ctx.s_buff[43], ID_LEN);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* wolfSSL_set_session() attaches an expired session but does not resume it,
|
|
* so no PSK is offered. The session's ID must not be sent either: the
|
|
* ClientHello carries an empty legacy_session_id, or the fake one of
|
|
* middlebox compatibility mode, and a full handshake follows. */
|
|
int test_tls13_expired_session_id(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_ASN_TIME) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte oldId[ID_LEN];
|
|
const byte* ch = NULL;
|
|
char buf[16];
|
|
int 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, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Take in the NewSessionTicket. Its tail becomes the session ID. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
/* Round 0: control, the session resumes and its ID is sent. Round 1: the
|
|
* same session made to look expired. */
|
|
for (i = 0; i < 2 && EXPECT_SUCCESS(); i++) {
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
ExpectIntEQ(sess->sessionIDSz, ID_LEN);
|
|
XMEMCPY(oldId, sess->sessionID, ID_LEN);
|
|
if (i == 0) {
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
}
|
|
else {
|
|
sess->bornOn -= sess->timeout + 1;
|
|
/* OpenSSL compatibility reports success for an expired session. */
|
|
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_ERROR_CODE_OPENSSL)
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_FAILURE);
|
|
#endif
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* 5 byte record header + 4 byte handshake header + 2 byte
|
|
* legacy_version + 32 byte random = offset 43 holds the
|
|
* legacy_session_id length. */
|
|
ch = test_ctx.s_buff;
|
|
ExpectIntGT(test_ctx.s_len, 43 + ID_LEN);
|
|
ExpectIntEQ(ch[0], handshake);
|
|
ExpectIntEQ(ch[5], client_hello);
|
|
if (i == 0) {
|
|
ExpectIntEQ(ch[43], ID_LEN);
|
|
ExpectBufEQ(ch + 44, oldId, ID_LEN);
|
|
}
|
|
else {
|
|
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
|
/* The fake session ID is the client random. */
|
|
ExpectIntEQ(ch[43], ID_LEN);
|
|
ExpectBufEQ(ch + 44, ch + 11, ID_LEN);
|
|
#else
|
|
ExpectIntEQ(ch[43], 0);
|
|
#endif
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), i == 0 ? 1 : 0);
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The same for an expired TLS 1.2 session on a client that can do TLS 1.3.
|
|
* The server still has the session cached, so had the ClientHello carried its
|
|
* ID the server would resume it, and the client, expecting a full handshake,
|
|
* would fail on the server's ChangeCipherSpec with OUT_OF_ORDER_E. */
|
|
int test_tls13_expired_tls12_session_id(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(NO_ASN_TIME) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte oldId[ID_LEN];
|
|
const byte* ch = NULL;
|
|
int i;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
/* Round 0: control, the session resumes. Round 1: the same session made
|
|
* to look expired to the client only. */
|
|
for (i = 0; i < 2 && EXPECT_SUCCESS(); i++) {
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
ExpectIntEQ(sess->sessionIDSz, ID_LEN);
|
|
XMEMCPY(oldId, sess->sessionID, ID_LEN);
|
|
if (i == 0) {
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
}
|
|
else {
|
|
sess->bornOn -= sess->timeout + 1;
|
|
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_ERROR_CODE_OPENSSL)
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_FAILURE);
|
|
#endif
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
ch = test_ctx.s_buff;
|
|
ExpectIntGT(test_ctx.s_len, 43 + ID_LEN);
|
|
ExpectIntEQ(ch[0], handshake);
|
|
ExpectIntEQ(ch[5], client_hello);
|
|
if (i == 0) {
|
|
ExpectIntEQ(ch[43], ID_LEN);
|
|
ExpectBufEQ(ch + 44, oldId, ID_LEN);
|
|
}
|
|
else {
|
|
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
|
ExpectIntEQ(ch[43], ID_LEN);
|
|
ExpectBufEQ(ch + 44, ch + 11, ID_LEN);
|
|
#else
|
|
ExpectIntEQ(ch[43], 0);
|
|
#endif
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), i == 0 ? 1 : 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), i == 0 ? 1 : 0);
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A resumed handshake leaves the object marked as resuming, and
|
|
* wolfSSL_clear() keeps that state for the kept session. A session that
|
|
* wolfSSL_set_session() then rejects must not inherit it: neither its ID nor
|
|
* its ticket may be offered. */
|
|
int test_tls13_clear_expired_session_id(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_ASN_TIME) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
(defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL))
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const byte* ch = NULL;
|
|
char buf[16];
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntEQ(sess->sessionIDSz, ID_LEN);
|
|
|
|
/* Resume the session. */
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1);
|
|
|
|
/* Reuse the client object with the session now expired. */
|
|
ExpectIntEQ(wolfSSL_clear(ssl_c), WOLFSSL_SUCCESS);
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_3_server_method), 0);
|
|
if (sess != NULL)
|
|
sess->bornOn -= sess->timeout + 1;
|
|
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_ERROR_CODE_OPENSSL)
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_FAILURE);
|
|
#endif
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
ch = test_ctx.s_buff;
|
|
ExpectIntGT(test_ctx.s_len, 43 + ID_LEN);
|
|
ExpectIntEQ(ch[0], handshake);
|
|
ExpectIntEQ(ch[5], client_hello);
|
|
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
|
ExpectIntEQ(ch[43], ID_LEN);
|
|
ExpectBufEQ(ch + 44, ch + 11, ID_LEN);
|
|
#else
|
|
ExpectIntEQ(ch[43], 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_middlebox_compat_server_ccs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* Give the client a non-empty legacy_session_id. GetTls13SessionId puts
|
|
* it on the wire whatever the build options are, which is what drives
|
|
* the server behaviour RFC 8446 Appendix D.4 requires. It is only sent
|
|
* when resuming; with no ticket to offer the handshake is still full. */
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
ssl_c->options.resuming = 1;
|
|
}
|
|
|
|
/* Client sends ClientHello with a non-empty session id. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Server processes it and writes its first flight. */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* The client offered a non-empty session id, so the server owes it a
|
|
* ChangeCipherSpec. Peers such as Erlang's ssl abort the handshake with
|
|
* an unexpected_message alert when it is missing. */
|
|
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* wolfSSL_clear() must not leave the previous handshake's ChangeCipherSpec
|
|
* state behind, or a reused server object stops answering a non-empty
|
|
* legacy_session_id after the first handshake. */
|
|
int test_tls13_middlebox_compat_server_reuse(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_c2 = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
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);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
ssl_c->options.resuming = 1;
|
|
}
|
|
|
|
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_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
|
|
|
/* Reuse the server object for a second handshake with a fresh client. */
|
|
ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS);
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
|
|
ExpectNotNull(ssl_c2 = wolfSSL_new(ctx_c));
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SetIOWriteCtx(ssl_c2, &test_ctx);
|
|
wolfSSL_SetIOReadCtx(ssl_c2, &test_ctx);
|
|
XMEMSET(ssl_c2->session->sessionID, 0x5A, ID_LEN);
|
|
ssl_c2->session->sessionIDSz = ID_LEN;
|
|
ssl_c2->options.resuming = 1;
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c2), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c2,
|
|
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);
|
|
|
|
/* The second peer also offered a session id, so it is owed a CCS too. */
|
|
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_c2);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* One HelloRetryRequest handshake, with the server's messages grouped or
|
|
* not. Its own verdict, so a failure in one setting still leaves the other
|
|
* exercised. */
|
|
static int test_tls13_hrr_ccs_grouped(int grouped)
|
|
{
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
ssl_c->options.resuming = 1;
|
|
ssl_s->options.groupMessages = (byte)grouped;
|
|
}
|
|
|
|
/* No key share entries, so the server has to ask for one. */
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
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);
|
|
|
|
/* The HelloRetryRequest flight carries exactly one, however the
|
|
* server groups its messages. */
|
|
ExpectIntEQ(ssl_s->options.serverState,
|
|
SERVER_HELLO_RETRY_REQUEST_COMPLETE);
|
|
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
|
|
|
/* Step to the second ClientHello and the flight answering it. The
|
|
* client drains c_buff as it reads, so what is left is that flight
|
|
* alone: the server owes no second record and must not send one. */
|
|
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_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 0);
|
|
|
|
/* And the handshake the retry started still completes. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* A HelloRetryRequest is flushed as soon as it is built, so the
|
|
* ChangeCipherSpec that follows it has to be flushed too. With grouped
|
|
* messages, which every OPENSSL_COMPATIBLE_DEFAULTS build turns on, it would
|
|
* otherwise sit in the output buffer and reach the peer a flight late. */
|
|
int test_tls13_middlebox_compat_hrr_ccs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
int ungrouped_res;
|
|
int grouped_res;
|
|
|
|
/* Both run whatever the other does; both owe the same record. */
|
|
ungrouped_res = test_tls13_hrr_ccs_grouped(0);
|
|
grouped_res = test_tls13_hrr_ccs_grouped(1);
|
|
ExpectIntEQ(ungrouped_res, TEST_SUCCESS);
|
|
ExpectIntEQ(grouped_res, TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* HelloRetryRequest handshake with the record layer version of the second
|
|
* ClientHello, and any ChangeCipherSpec before it, rewritten to
|
|
* pvMajor.pvMinor. Sets *serverErr to the server's error, or 0 when the
|
|
* handshake completes. Returns the test verdict. */
|
|
static int test_tls13_hrr_ch2_record_version_run(byte pvMajor, byte pvMinor,
|
|
int* serverErr)
|
|
{
|
|
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;
|
|
int idx = 0;
|
|
int patched = 0;
|
|
|
|
*serverErr = 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);
|
|
|
|
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
|
/* A session ID turns on the middlebox compatibility ChangeCipherSpec. */
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
}
|
|
#endif
|
|
/* No key share entries, so the server has to ask for one. */
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
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(ssl_s->options.serverState,
|
|
SERVER_HELLO_RETRY_REQUEST_COMPLETE);
|
|
|
|
/* Client reads the HelloRetryRequest and writes the second ClientHello,
|
|
* possibly behind a middlebox compatibility ChangeCipherSpec. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
|
/* The ChangeCipherSpec exception is only covered if one is present. */
|
|
ExpectIntEQ(test_tls13_count_ccs(test_ctx.s_buff, test_ctx.s_len), 1);
|
|
#endif
|
|
|
|
while (EXPECT_SUCCESS() && idx + RECORD_HEADER_SZ <= test_ctx.s_len) {
|
|
if (test_ctx.s_buff[idx] == handshake) {
|
|
patched = 1;
|
|
}
|
|
test_ctx.s_buff[idx + 1] = pvMajor;
|
|
test_ctx.s_buff[idx + 2] = pvMinor;
|
|
idx += RECORD_HEADER_SZ + ((test_ctx.s_buff[idx + 3] << 8) |
|
|
test_ctx.s_buff[idx + 4]);
|
|
}
|
|
ExpectIntEQ(patched, 1);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
if (wolfSSL_accept(ssl_s) != WOLFSSL_SUCCESS) {
|
|
*serverErr = wolfSSL_get_error(ssl_s,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
}
|
|
}
|
|
if (EXPECT_SUCCESS() && *serverErr == WOLFSSL_ERROR_WANT_READ) {
|
|
*serverErr = 0;
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* RFC 8446 Appendix D.2: a server MUST ignore the TLS 1.x record layer
|
|
* version, including on the second ClientHello after a HelloRetryRequest. */
|
|
int test_tls13_hrr_ch2_record_version(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
int serverErr = 0;
|
|
|
|
ExpectIntEQ(test_tls13_hrr_ch2_record_version_run(SSLv3_MAJOR,
|
|
TLSv1_2_MINOR, &serverErr), TEST_SUCCESS);
|
|
ExpectIntEQ(serverErr, 0);
|
|
ExpectIntEQ(test_tls13_hrr_ch2_record_version_run(SSLv3_MAJOR,
|
|
TLSv1_MINOR, &serverErr), TEST_SUCCESS);
|
|
ExpectIntEQ(serverErr, 0);
|
|
ExpectIntEQ(test_tls13_hrr_ch2_record_version_run(SSLv3_MAJOR,
|
|
TLSv1_1_MINOR, &serverErr), TEST_SUCCESS);
|
|
ExpectIntEQ(serverErr, 0);
|
|
/* tls 1.3 receiver must ignore the legacy record version, even when
|
|
* major byte is not SSLv3_MAJOR. */
|
|
ExpectIntEQ(test_tls13_hrr_ch2_record_version_run(SSLv3_MAJOR + 1,
|
|
TLSv1_2_MINOR, &serverErr), TEST_SUCCESS);
|
|
ExpectIntEQ(serverErr, 0);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* Refuses one ChangeCipherSpec write, then counts the ones that land. */
|
|
struct test_tls13_ccs_ctx {
|
|
struct test_memio_ctx* memio;
|
|
int refuse;
|
|
int sent;
|
|
};
|
|
|
|
/* Refuse the write carrying the ChangeCipherSpec, once, and let every other
|
|
* write through. Refusing by record type rather than by write count keeps
|
|
* the test tied to the record it is about. */
|
|
static int test_tls13_ccs_write_cb(WOLFSSL* ssl, char* data, int sz, void* ctx)
|
|
{
|
|
struct test_tls13_ccs_ctx* cctx = (struct test_tls13_ccs_ctx*)ctx;
|
|
int ret;
|
|
|
|
if (sz > 0 && (byte)data[0] == change_cipher_spec && cctx->refuse) {
|
|
cctx->refuse = 0;
|
|
return WOLFSSL_CBIO_ERR_WANT_WRITE;
|
|
}
|
|
|
|
ret = test_memio_write_cb(ssl, data, sz, cctx->memio);
|
|
if (ret > 0 && sz > 0 && (byte)data[0] == change_cipher_spec)
|
|
cctx->sent++;
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/* A server send that stops short must not put a second ChangeCipherSpec on
|
|
* the wire when it is resumed. Drive that path with a transport that refuses
|
|
* that one write. The HelloRetryRequest flavour below is the one that pins
|
|
* where sentChangeCipher is set; this one covers the straight-through
|
|
* flight, which the accept-state prologue recovers on its own. */
|
|
int test_tls13_middlebox_compat_server_ccs_retry(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
struct test_tls13_ccs_ctx ccs_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&ccs_ctx, 0, sizeof(ccs_ctx));
|
|
ccs_ctx.memio = &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);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
ssl_c->options.resuming = 1;
|
|
/* Ungrouped, so the record is flushed on its own and the refusal
|
|
* below lands on it rather than on a flight carrying it along, and
|
|
* without the automatic retry an OPENSSL_COMPATIBLE_DEFAULTS build
|
|
* turns on, which would resume inside the send instead of through
|
|
* the accept state machine this test is about. */
|
|
ssl_s->options.groupMessages = 0;
|
|
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
|
|
defined(HAVE_MEMCACHED)
|
|
/* Only a build with the mode API can have turned auto retry on, and
|
|
* an OPENSSL_COMPATIBLE_DEFAULTS one has. Resuming the send has to
|
|
* come back through the accept state machine, not inside it. */
|
|
wolfSSL_CTX_clear_mode(ctx_s, WOLFSSL_MODE_AUTO_RETRY);
|
|
#endif
|
|
wolfSSL_SSLSetIOSend(ssl_s, test_tls13_ccs_write_cb);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &ccs_ctx);
|
|
ccs_ctx.refuse = 1;
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* The ChangeCipherSpec write is refused, so the flight stops on it. */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_WRITE);
|
|
ExpectIntEQ(ccs_ctx.refuse, 0);
|
|
|
|
/* Resume the same handshake, now that the write goes through. */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* One record, not two: the resumed accept must not repeat the send. */
|
|
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The HelloRetryRequest path marks the record sent before sending it,
|
|
* because a resumed accept falls through into TLS13_SERVER_HELLO_SENT and
|
|
* only that flag stops it sending a second one. Refuse the write, resume,
|
|
* and check the flight answering the second ClientHello stays empty. */
|
|
int test_tls13_middlebox_compat_hrr_ccs_retry(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
struct test_tls13_ccs_ctx ccs_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&ccs_ctx, 0, sizeof(ccs_ctx));
|
|
ccs_ctx.memio = &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);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
ssl_c->options.resuming = 1;
|
|
ssl_s->options.groupMessages = 0;
|
|
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
|
|
defined(HAVE_MEMCACHED)
|
|
/* Only a build with the mode API can have turned auto retry on, and
|
|
* an OPENSSL_COMPATIBLE_DEFAULTS one has. Resuming the send has to
|
|
* come back through the accept state machine, not inside it. */
|
|
wolfSSL_CTX_clear_mode(ctx_s, WOLFSSL_MODE_AUTO_RETRY);
|
|
#endif
|
|
wolfSSL_SSLSetIOSend(ssl_s, test_tls13_ccs_write_cb);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &ccs_ctx);
|
|
ccs_ctx.refuse = 1;
|
|
}
|
|
|
|
/* No key share entries, so the server has to ask for one. */
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* The record behind the HelloRetryRequest is refused. */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_WRITE);
|
|
ExpectIntEQ(ccs_ctx.refuse, 0);
|
|
|
|
/* Resume: the retry owes exactly the one record it stopped on. */
|
|
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_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
|
|
|
/* The flight answering the second ClientHello owes none. A resumed
|
|
* accept falls into TLS13_SERVER_HELLO_SENT, so this is what catches
|
|
* the record being marked sent after the send rather than before. */
|
|
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_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 0);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* One client handshake with its ChangeCipherSpec write refused once, either
|
|
* straight through or after a HelloRetryRequest. */
|
|
static int test_tls13_client_ccs_retry_once(int noKeyShares)
|
|
{
|
|
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;
|
|
struct test_tls13_ccs_ctx ccs_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&ccs_ctx, 0, sizeof(ccs_ctx));
|
|
ccs_ctx.memio = &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);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SSLSetIOSend(ssl_c, test_tls13_ccs_write_cb);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &ccs_ctx);
|
|
/* Refuse the client's first ChangeCipherSpec write, wherever in the
|
|
* flight it falls. */
|
|
ccs_ctx.refuse = 1;
|
|
}
|
|
if (noKeyShares) {
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* A refused record answering a HelloRetryRequest must not advance
|
|
* connectState past the second ClientHello. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ccs_ctx.sent, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* The client marks its own middlebox ChangeCipherSpec sent before sending it,
|
|
* for the same reason the server does. Both flights get their own verdict. */
|
|
int test_tls13_middlebox_compat_client_ccs_retry(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
int hrr_res;
|
|
int direct_res;
|
|
|
|
hrr_res = test_tls13_client_ccs_retry_once(1);
|
|
direct_res = test_tls13_client_ccs_retry_once(0);
|
|
ExpectIntEQ(hrr_res, TEST_SUCCESS);
|
|
ExpectIntEQ(direct_res, TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The third client send site is the early data one, which puts the
|
|
* ChangeCipherSpec out before the 0-RTT records rather than as part of a
|
|
* handshake flight. Refuse that one write and resume: the record is owed
|
|
* once, and a resumed wolfSSL_write_early_data() must not repeat it. */
|
|
int test_tls13_middlebox_compat_client_ccs_retry_early_data(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) && \
|
|
defined(WOLFSSL_EARLY_DATA) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
struct test_tls13_ccs_ctx ccs_ctx;
|
|
const char earlyMsg[] = "This is early data";
|
|
char buf[64];
|
|
int written = 0;
|
|
int earlyRead = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&ccs_ctx, 0, sizeof(ccs_ctx));
|
|
ccs_ctx.memio = &test_ctx;
|
|
|
|
/* A full handshake first, only to get a ticket to resume with; early
|
|
* data has nowhere else to come from. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* Opt the server into 0-RTT (off by default per RFC 8446 E.5). */
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Read so that the NewSessionTicket is taken in. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
|
|
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);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
/* Ungrouped, so the record is flushed on its own and the refusal
|
|
* below lands on it rather than on a flight carrying it along. */
|
|
ssl_c->options.groupMessages = 0;
|
|
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
|
|
defined(HAVE_MEMCACHED)
|
|
/* Only a build with the mode API can have turned auto retry on, and
|
|
* an OPENSSL_COMPATIBLE_DEFAULTS one has. Resuming the send has to
|
|
* come back through the connect state machine, not inside it. */
|
|
wolfSSL_CTX_clear_mode(ctx_c, WOLFSSL_MODE_AUTO_RETRY);
|
|
#endif
|
|
wolfSSL_SSLSetIOSend(ssl_c, test_tls13_ccs_write_cb);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &ccs_ctx);
|
|
ccs_ctx.refuse = 1;
|
|
}
|
|
|
|
/* The ChangeCipherSpec ahead of the early data is refused, so the
|
|
* client stops on it with nothing written. */
|
|
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, earlyMsg, (int)sizeof(earlyMsg),
|
|
&written), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_WRITE);
|
|
ExpectIntEQ(ccs_ctx.refuse, 0);
|
|
ExpectIntEQ(written, 0);
|
|
|
|
/* Resume: the record it stopped on goes out, and only that one. */
|
|
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, earlyMsg, (int)sizeof(earlyMsg),
|
|
&written), (int)sizeof(earlyMsg));
|
|
ExpectIntEQ(written, (int)sizeof(earlyMsg));
|
|
ExpectIntEQ(ccs_ctx.sent, 1);
|
|
|
|
/* The 0-RTT data the retry carried still arrives, and the handshake it
|
|
* started still completes. */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
(void)test_tls13_early_data_read_until_write_ok(ssl_s, buf, sizeof(buf),
|
|
&earlyRead);
|
|
ExpectIntEQ(earlyRead, (int)sizeof(earlyMsg));
|
|
ExpectStrEQ(earlyMsg, buf);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectTrue(wolfSSL_session_reused(ssl_c));
|
|
|
|
/* Still the one record, over the whole handshake. */
|
|
ExpectIntEQ(ccs_ctx.sent, 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_plaintext_alert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
defined(HAVE_AESGCM) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_FILESYSTEM)
|
|
byte clientMsgs[] = {
|
|
/* Client Hello */
|
|
0x16, 0x03, 0x03, 0x01, 0x9b, 0x01, 0x00, 0x01,
|
|
0x97, 0x03, 0x03, 0xf4, 0x65, 0xbd, 0x22, 0xfe,
|
|
0x6e, 0xab, 0x66, 0xdd, 0xcf, 0xe9, 0x65, 0x55,
|
|
0xe8, 0xdf, 0xc3, 0x8e, 0x4b, 0x00, 0xbc, 0xf8,
|
|
0x23, 0x57, 0x1b, 0xa0, 0xc8, 0xa9, 0xe2, 0x8c,
|
|
0x91, 0x6e, 0xf9, 0x20, 0xf7, 0x5c, 0xc5, 0x5b,
|
|
0x75, 0x8c, 0x47, 0x0a, 0x0e, 0xc4, 0x1a, 0xda,
|
|
0xef, 0x75, 0xe5, 0x21, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x13, 0x01,
|
|
0x13, 0x02, 0x01, 0x00, 0x01, 0x4a, 0x00, 0x2d,
|
|
0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x33, 0x00,
|
|
0x47, 0x00, 0x45, 0x00, 0x17, 0x00, 0x41, 0x04,
|
|
0x90, 0xfc, 0xe2, 0x97, 0x05, 0x7c, 0xb5, 0x23,
|
|
0x5d, 0x5f, 0x5b, 0xcd, 0x0c, 0x1e, 0xe0, 0xe9,
|
|
0xab, 0x38, 0x6b, 0x1e, 0x20, 0x5c, 0x1c, 0x90,
|
|
0x2a, 0x9e, 0x68, 0x8e, 0x70, 0x05, 0x10, 0xa8,
|
|
0x02, 0x1b, 0xf9, 0x5c, 0xef, 0xc9, 0xaf, 0xca,
|
|
0x1a, 0x3b, 0x16, 0x8b, 0xe4, 0x1b, 0x3c, 0x15,
|
|
0xb8, 0x0d, 0xbd, 0xaf, 0x62, 0x8d, 0xa7, 0x13,
|
|
0xa0, 0x7c, 0xe0, 0x59, 0x0c, 0x4f, 0x8a, 0x6d,
|
|
0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, 0x00,
|
|
0x0d, 0x00, 0x20, 0x00, 0x1e, 0x06, 0x03, 0x05,
|
|
0x03, 0x04, 0x03, 0x02, 0x03, 0x08, 0x06, 0x08,
|
|
0x0b, 0x08, 0x05, 0x08, 0x0a, 0x08, 0x04, 0x08,
|
|
0x09, 0x06, 0x01, 0x05, 0x01, 0x04, 0x01, 0x03,
|
|
0x01, 0x02, 0x01, 0x00, 0x0a, 0x00, 0x04, 0x00,
|
|
0x02, 0x00, 0x17, 0x00, 0x16, 0x00, 0x00, 0x00,
|
|
0x23, 0x00, 0x00, 0x00, 0x29, 0x00, 0xb9, 0x00,
|
|
0x94, 0x00, 0x8e, 0x0f, 0x12, 0xfa, 0x84, 0x1f,
|
|
0x76, 0x94, 0xd7, 0x09, 0x5e, 0xad, 0x08, 0x51,
|
|
0xb6, 0x80, 0x28, 0x31, 0x8b, 0xfd, 0xc6, 0xbd,
|
|
0x9e, 0xf5, 0x3b, 0x4d, 0x02, 0xbe, 0x1d, 0x73,
|
|
0xea, 0x13, 0x68, 0x00, 0x4c, 0xfd, 0x3d, 0x48,
|
|
0x51, 0xf9, 0x06, 0xbb, 0x92, 0xed, 0x42, 0x9f,
|
|
0x7f, 0x2c, 0x73, 0x9f, 0xd9, 0xb4, 0xef, 0x05,
|
|
0x26, 0x5b, 0x60, 0x5c, 0x0a, 0xfc, 0xa3, 0xbd,
|
|
0x2d, 0x2d, 0x8b, 0xf9, 0xaa, 0x5c, 0x96, 0x3a,
|
|
0xf2, 0xec, 0xfa, 0xe5, 0x57, 0x2e, 0x87, 0xbe,
|
|
0x27, 0xc5, 0x3d, 0x4f, 0x5d, 0xdd, 0xde, 0x1c,
|
|
0x1b, 0xb3, 0xcc, 0x27, 0x27, 0x57, 0x5a, 0xd9,
|
|
0xea, 0x99, 0x27, 0x23, 0xa6, 0x0e, 0xea, 0x9c,
|
|
0x0d, 0x85, 0xcb, 0x72, 0xeb, 0xd7, 0x93, 0xe3,
|
|
0xfe, 0xf7, 0x5c, 0xc5, 0x5b, 0x75, 0x8c, 0x47,
|
|
0x0a, 0x0e, 0xc4, 0x1a, 0xda, 0xef, 0x75, 0xe5,
|
|
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0xfb, 0x92, 0xce, 0xaa, 0x00, 0x21, 0x20,
|
|
0xcb, 0x73, 0x25, 0x80, 0x46, 0x78, 0x4f, 0xe5,
|
|
0x34, 0xf6, 0x91, 0x13, 0x7f, 0xc8, 0x8d, 0xdc,
|
|
0x81, 0x04, 0xb7, 0x0d, 0x49, 0x85, 0x2e, 0x12,
|
|
0x7a, 0x07, 0x23, 0xe9, 0x13, 0xa4, 0x6d, 0x8c,
|
|
0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x00, 0x00
|
|
};
|
|
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_BUFFER_INFO msg;
|
|
|
|
#ifdef WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
|
|
/* We fail on WOLFSSL_ALERT_COUNT_MAX alerts. */
|
|
|
|
/* Set up wolfSSL context. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
CERT_FILETYPE));
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL);
|
|
}
|
|
/* Read from 'msg'. */
|
|
wolfSSL_SetIORecv(ctx, Tls13PTARecv);
|
|
/* No where to send to - dummy sender. */
|
|
wolfSSL_SetIOSend(ctx, Tls13PTASend);
|
|
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = clientMsgs;
|
|
msg.length = (unsigned int)sizeof(clientMsgs) - 1;
|
|
clientMsgs[sizeof(clientMsgs) - 1] = WOLFSSL_ALERT_COUNT_MAX;
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
}
|
|
/* Alert will be ignored until too many. */
|
|
/* Read all message include CertificateVerify with invalid signature
|
|
* algorithm. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
/* Expect an invalid parameter error. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(ALERT_COUNT_E));
|
|
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* Set up wolfSSL context. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
CERT_FILETYPE));
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL);
|
|
}
|
|
/* Read from 'msg'. */
|
|
wolfSSL_SetIORecv(ctx, Tls13PTARecv);
|
|
/* No where to send to - dummy sender. */
|
|
wolfSSL_SetIOSend(ctx, Tls13PTASend);
|
|
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = clientMsgs;
|
|
msg.length = (unsigned int)sizeof(clientMsgs) - 1;
|
|
clientMsgs[sizeof(clientMsgs) - 1] = WOLFSSL_ALERT_COUNT_MAX - 1;
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
}
|
|
/* Alert will be ignored until too many. */
|
|
/* Read all message include CertificateVerify with invalid signature
|
|
* algorithm. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
/* Expect an invalid parameter error. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(SOCKET_ERROR_E));
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
ssl = NULL;
|
|
ctx = NULL;
|
|
|
|
/* Negative test: a plaintext alert must NOT be ignored once the peer has
|
|
* responded with an encrypted handshake message. Complete a handshake so
|
|
* the peer is encrypting, then feed the client a plaintext alert. */
|
|
#if !defined(NO_WOLFSSL_CLIENT) && !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;
|
|
/* Plaintext alert record: fatal (2), handshake_failure (40). */
|
|
byte ptAlert[] = { 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x28 };
|
|
char data[16];
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Drop any post-handshake data (e.g. session tickets) queued for the
|
|
* client and feed it only the plaintext alert. */
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)ptAlert,
|
|
(int)sizeof(ptAlert)), 0);
|
|
|
|
/* Plaintext alert is rejected as the peer is encrypting. */
|
|
ExpectIntLT(wolfSSL_read(ssl_c, data, (int)sizeof(data)), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(PARSE_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
|
|
/* Negative test (server): a plaintext alert must NOT be ignored once the
|
|
* client has sent an encrypted handshake message, even before the
|
|
* handshake is complete. Use client authentication so that the client
|
|
* sends an encrypted Certificate message before Finished. */
|
|
{
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
/* Plaintext alert record: fatal (2), handshake_failure (40). */
|
|
byte ptAlert[] = { 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x28 };
|
|
int end = 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);
|
|
/* Server requires a client certificate. */
|
|
ExpectTrue(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile,
|
|
NULL) == WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
}
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
|
|
/* Client Hello. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* Server flight including CertificateRequest. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* Client flight: [CCS,] Certificate, CertificateVerify, Finished. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
/* Find the end of the first encrypted record (outer content type
|
|
* application_data) the client sent - the Certificate message. */
|
|
while (end + 5 <= test_ctx.s_len) {
|
|
byte recType = test_ctx.s_buff[end];
|
|
end += 5 + ((test_ctx.s_buff[end + 3] << 8) |
|
|
test_ctx.s_buff[end + 4]);
|
|
if (recType == 0x17)
|
|
break;
|
|
}
|
|
ExpectIntLE(end, test_ctx.s_len);
|
|
ExpectIntGT(end, 0);
|
|
/* Remove the records after it (CertificateVerify and Finished),
|
|
* working backwards a message at a time. */
|
|
while (EXPECT_SUCCESS() && test_ctx.s_len > end) {
|
|
int i;
|
|
int msgOff = 0;
|
|
|
|
for (i = 0; i < test_ctx.s_msg_count - 1; i++)
|
|
msgOff += test_ctx.s_msg_sizes[i];
|
|
if (msgOff >= end) {
|
|
/* Last message is wholly after the Certificate record. */
|
|
ExpectIntEQ(test_memio_drop_message(&test_ctx, 0,
|
|
test_ctx.s_msg_count - 1), 0);
|
|
}
|
|
else {
|
|
/* Last message also holds the records to keep. */
|
|
ExpectIntEQ(test_memio_remove_from_buffer(&test_ctx, 0, end,
|
|
test_ctx.s_len - end), 0);
|
|
}
|
|
}
|
|
/* Follow the encrypted Certificate message with a plaintext alert. */
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)ptAlert, (int)sizeof(ptAlert)), 0);
|
|
|
|
/* Plaintext alert is rejected as the client has sent an encrypted
|
|
* handshake message. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(PARSE_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
#else
|
|
/* Fail on plaintext alert when encryption keys on. */
|
|
|
|
/* Set up wolfSSL context. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
CERT_FILETYPE));
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL);
|
|
}
|
|
/* Read from 'msg'. */
|
|
wolfSSL_SetIORecv(ctx, Tls13PTARecv);
|
|
/* No where to send to - dummy sender. */
|
|
wolfSSL_SetIOSend(ctx, Tls13PTASend);
|
|
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = clientMsgs;
|
|
msg.length = (unsigned int)sizeof(clientMsgs) - 1;
|
|
clientMsgs[sizeof(clientMsgs) - 1] = 1;
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
}
|
|
/* Alert will be ignored until too many. */
|
|
/* Read all message include CertificateVerify with invalid signature
|
|
* algorithm. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
/* Expect an invalid parameter error. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(PARSE_ERROR));
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
#endif
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that TLS 1.3 warning-level alerts are treated as fatal (RFC 8446
|
|
* Section 6.2).
|
|
* A peer sending e.g. {alert_warning, handshake_failure} must still cause the
|
|
* connection to be terminated, not silently continued.
|
|
*/
|
|
int test_tls13_warning_alert_is_fatal(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
/* TLS record: content_type=alert(0x15), version=TLS1.2(0x0303), len=2,
|
|
* level=warning(0x01), code=handshake_failure(0x28=40) */
|
|
static const unsigned char warn_alert[] =
|
|
{ 0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x28 };
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
|
|
wolfTLSv1_3_client_method, NULL), 0);
|
|
|
|
/* Client sends ClientHello, then waits for the server response. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Inject a warning-level handshake_failure alert as if from the server.
|
|
* RFC 8446 Section 6.2: In TLS 1.3, all error alerts MUST be treated as
|
|
* fatalregardless of the AlertLevel byte. */
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char *)warn_alert, sizeof(warn_alert)), 0);
|
|
|
|
/* Expect the connection to be terminated, not silently continued. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WC_NO_ERR_TRACE(FATAL_ERROR));
|
|
|
|
/* The alert details should be recorded correctly. */
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_rx.code, handshake_failure);
|
|
ExpectIntEQ(h.last_rx.level, alert_warning);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that an unknown extension in a TLS 1.3 server-to-client message is
|
|
* rejected with unsupported_extension (RFC 8446 Sec. 4.2). The client MUST
|
|
* abort the handshake when it receives an extension it did not advertise.
|
|
*/
|
|
int test_tls13_unknown_ext_rejected(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && defined(WOLFSSL_AES_128) && \
|
|
defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
|
!defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
/* HelloRetryRequest carrying TLS_AES_128_GCM_SHA256, supported_versions
|
|
* (TLS 1.3), and an extra unknown extension type 0xFABC.
|
|
*
|
|
* The base HRR (from test_tls13_same_ch) extended with 4 bytes:
|
|
* extensions length: 6 -> 10 (0x00,0x0a)
|
|
* handshake body length: 46 -> 50 (0x00,0x00,0x32)
|
|
* record body length: 50 -> 54 (0x00,0x36)
|
|
* appended: 0xfa,0xbc,0x00,0x00 (unknown type, zero-length value)
|
|
*/
|
|
static const unsigned char hrr_unknown_ext[] = {
|
|
/* TLS record header: handshake, TLS 1.2 compat, len=54 */
|
|
0x16, 0x03, 0x03, 0x00, 0x36,
|
|
/* Handshake header: ServerHello, len=50 */
|
|
0x02, 0x00, 0x00, 0x32,
|
|
/* legacy_version: TLS 1.2 */
|
|
0x03, 0x03,
|
|
/* HelloRetryRequest magic random */
|
|
0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11,
|
|
0xbe, 0x1d, 0x8c, 0x02, 0x1e, 0x65, 0xb8, 0x91,
|
|
0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e,
|
|
0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c,
|
|
/* session ID length: 0 */
|
|
0x00,
|
|
/* cipher suite: TLS_AES_128_GCM_SHA256 */
|
|
0x13, 0x01,
|
|
/* compression: null */
|
|
0x00,
|
|
/* extensions length: 10 */
|
|
0x00, 0x0a,
|
|
/* supported_versions: TLS 1.3 (0x0304) */
|
|
0x00, 0x2b, 0x00, 0x02, 0x03, 0x04,
|
|
/* unknown extension type 0xFABC, zero-length value */
|
|
0xfa, 0xbc, 0x00, 0x00
|
|
};
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
|
|
wolfTLSv1_3_client_method, NULL), 0);
|
|
|
|
/* Inject the crafted HRR before the client starts the handshake.
|
|
* wolfSSL_connect will send the ClientHello and then read this message. */
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char *)hrr_unknown_ext, sizeof(hrr_unknown_ext)), 0);
|
|
|
|
/* RFC 8446 Sec. 4.2: the client MUST abort with unsupported_extension. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION));
|
|
|
|
/* The client MUST also transmit the fatal unsupported_extension alert
|
|
* on the wire, not merely surface a local error. The client's outgoing
|
|
* data lands in test_ctx.s_buff; at this point in the handshake no
|
|
* traffic keys are derived yet, so the alert record is plaintext.
|
|
* Expected record: type=alert(0x15), version=TLS1.2(0x0303), len=2,
|
|
* level=fatal(0x02), description=unsupported_extension(0x6e=110). */
|
|
{
|
|
static const unsigned char expected_alert[] =
|
|
{ 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x6e };
|
|
int found = 0;
|
|
int i;
|
|
for (i = 0;
|
|
i + (int)sizeof(expected_alert) <= test_ctx.s_len;
|
|
i++) {
|
|
if (XMEMCMP(test_ctx.s_buff + i, expected_alert,
|
|
sizeof(expected_alert)) == 0) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
ExpectIntEQ(found, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Regression test for a malformed HelloRetryRequest sent to a downgrade capable
|
|
* (TLS 1.2 and TLS 1.3) client. Both crafted messages bear the HelloRetryRequest
|
|
* sentinel Random but omit supported_versions, which an HRR MUST carry, so the
|
|
* client must reject them with missing_extension (RFC 8446 Sec. 4.1.4/4.2.1/9.2
|
|
* - a missing mandatory extension) rather than downgrading to TLS 1.2 and losing
|
|
* the HRR context:
|
|
* 1. has-extensions form: carries a server_name extension (recognized but not
|
|
* permitted in an HRR). Before the fix the downgrade reparsed it as a plain
|
|
* server_hello, where server_name is allowed, and the client wrongly sent
|
|
* unsupported_extension (when SNI is compiled in). The fix rejects it in
|
|
* the version-negotiation step, so the assertions hold regardless of SNI.
|
|
* 2. no-extensions form: no extensions field at all. Before the fix this
|
|
* downgraded silently, treating the sentinel Random as a normal
|
|
* ServerHello.Random.
|
|
*/
|
|
int test_tls13_hrr_recognized_ext_downgrade(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && \
|
|
defined(WOLFSSL_AES_128) && defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
|
!defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
/* HelloRetryRequest bearing the sentinel Random and TLS_AES_128_GCM_SHA256,
|
|
* with NO supported_versions extension and a zero-length server_name
|
|
* extension (type 0x0000).
|
|
* extensions length: 4 (0x00,0x04)
|
|
* handshake body length: 44 (0x00,0x00,0x2c)
|
|
* record body length: 48 (0x00,0x30)
|
|
*/
|
|
static const unsigned char hrr_sni_no_sv[] = {
|
|
/* TLS record header: handshake, TLS 1.2 compat, len=48 */
|
|
0x16, 0x03, 0x03, 0x00, 0x30,
|
|
/* Handshake header: ServerHello, len=44 */
|
|
0x02, 0x00, 0x00, 0x2c,
|
|
/* legacy_version: TLS 1.2 */
|
|
0x03, 0x03,
|
|
/* HelloRetryRequest magic random */
|
|
0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11,
|
|
0xbe, 0x1d, 0x8c, 0x02, 0x1e, 0x65, 0xb8, 0x91,
|
|
0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e,
|
|
0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c,
|
|
/* session ID length: 0 */
|
|
0x00,
|
|
/* cipher suite: TLS_AES_128_GCM_SHA256 */
|
|
0x13, 0x01,
|
|
/* compression: null */
|
|
0x00,
|
|
/* extensions length: 4 */
|
|
0x00, 0x04,
|
|
/* server_name extension type 0x0000, zero-length value */
|
|
0x00, 0x00, 0x00, 0x00
|
|
};
|
|
/* HelloRetryRequest bearing the sentinel Random with NO extensions field.
|
|
* handshake body length: 38 (0x00,0x00,0x26)
|
|
* record body length: 42 (0x00,0x2a)
|
|
*/
|
|
static const unsigned char hrr_no_ext[] = {
|
|
/* TLS record header: handshake, TLS 1.2 compat, len=42 */
|
|
0x16, 0x03, 0x03, 0x00, 0x2a,
|
|
/* Handshake header: ServerHello, len=38 */
|
|
0x02, 0x00, 0x00, 0x26,
|
|
/* legacy_version: TLS 1.2 */
|
|
0x03, 0x03,
|
|
/* HelloRetryRequest magic random */
|
|
0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11,
|
|
0xbe, 0x1d, 0x8c, 0x02, 0x1e, 0x65, 0xb8, 0x91,
|
|
0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e,
|
|
0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c,
|
|
/* session ID length: 0 */
|
|
0x00,
|
|
/* cipher suite: TLS_AES_128_GCM_SHA256 */
|
|
0x13, 0x01,
|
|
/* compression: null */
|
|
0x00
|
|
};
|
|
|
|
/* Scenario 1: HelloRetryRequest with a not-permitted server_name extension
|
|
* and no supported_versions. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
|
|
/* Client allows both TLS 1.2 and TLS 1.3 so the missing supported_versions
|
|
* would otherwise trigger a downgrade. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
|
|
wolfSSLv23_client_method, NULL), 0);
|
|
|
|
/* Inject the crafted HelloRetryRequest before the client starts the
|
|
* handshake. wolfSSL_connect sends the ClientHello then reads this. */
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char *)hrr_sni_no_sv, sizeof(hrr_sni_no_sv)), 0);
|
|
|
|
/* Handshake must fail because the mandatory supported_versions extension
|
|
* is absent from the HelloRetryRequest. The HRR is rejected before the
|
|
* downgrade reparses the remaining (server_name) extension, so the result
|
|
* is the missing mandatory extension (INCOMPLETE_DATA). */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WC_NO_ERR_TRACE(INCOMPLETE_DATA));
|
|
|
|
/* RFC 8446 Sec. 4.1.4/4.2: a mandatory-but-absent extension MUST abort with
|
|
* a missing_extension (109) alert. */
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, missing_extension);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
|
|
/* Scenario 2: HelloRetryRequest sentinel with no extensions field at all.
|
|
* Must also be rejected with missing_extension rather than downgraded. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
|
|
wolfSSLv23_client_method, NULL), 0);
|
|
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char *)hrr_no_ext, sizeof(hrr_no_ext)), 0);
|
|
|
|
/* An HRR with no extensions field at all is still missing the mandatory
|
|
* supported_versions extension, so - consistently with scenario 1 - the
|
|
* client aborts with missing_extension (RFC 8446 4.2.1/9.2). */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WC_NO_ERR_TRACE(INCOMPLETE_DATA));
|
|
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, missing_extension);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that wolfSSL_set1_sigalgs_list() is honored in TLS 1.3
|
|
*/
|
|
int test_tls13_cert_req_sigalgs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_CERTS) && !defined(NO_RSA) && defined(WC_RSA_PSS) && \
|
|
defined(HAVE_ECC) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_FILESYSTEM)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
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);
|
|
|
|
/* Server: require client cert and load ECC client cert for verification */
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_set_verify(ssl_s,
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s,
|
|
cliEccCertFile, 0), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Server: restrict CertificateRequest to RSA-PSS+SHA256 only */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "RSA-PSS+SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Client: load ECC cert/key */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliEccCertFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliEccKeyFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Handshake must fail: ECC client cannot match RSA-PSS+SHA256 */
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
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);
|
|
|
|
/* Server: require client cert and load RSA client cert for verification */
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_set_verify(ssl_s,
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s,
|
|
cliCertFile, 0), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Server: restrict CertificateRequest to RSA-PSS+SHA256 only */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "RSA-PSS+SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Client: load RSA cert/key */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Handshake must succeed: RSA client satisfies RSA-PSS+SHA256 */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
#endif
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 Section 4.4.2.2: the chain a server sends MUST NOT be SHA-1 signed
|
|
* unless the client's advertisement permits SHA-1. */
|
|
int test_tls13_sha1_cert_chain(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* The SHA-1 fixtures below are only shipped as PEM, so this needs a build that
|
|
* can convert PEM to DER. WOLFSSL_ALLOW_TLS_SHA1 puts SHA-1 back in the
|
|
* advertised signature algorithms, which is exactly what the rule under test
|
|
* defers to, so there is nothing left to check in that build. */
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_CERTS) && !defined(NO_RSA) && !defined(NO_SHA) && \
|
|
!defined(WOLFSSL_NO_SIGALG) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && \
|
|
defined(WOLFSSL_PEM_TO_DER) && !defined(WOLFSSL_ALLOW_TLS_SHA1)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* sha1CertFile = "./certs/server-cert-sha1.pem";
|
|
const char* sha1RootFile = "./certs/server-cert-sha1-root.pem";
|
|
const char* sha1CliFile = "./certs/client-cert-sha1.pem";
|
|
byte* leafBuf = NULL;
|
|
byte* sha1Buf = NULL;
|
|
byte* chainBuf = NULL;
|
|
size_t leafSz = 0;
|
|
size_t sha1Sz = 0;
|
|
#ifdef WC_RSA_PSS
|
|
const char* pssCaFile = "./certs/rsapss/ca-rsapss.pem";
|
|
byte* pssBuf = NULL;
|
|
size_t pssSz = 0;
|
|
#endif
|
|
|
|
/* A TLS 1.3 client does not offer SHA-1, so the SHA-1 signed leaf must
|
|
* not be sent. */
|
|
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);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_s, sha1CertFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Read the raw error; wolfSSL_get_error() remaps this one to
|
|
* WOLFSSL_ERROR_SYSCALL when OPENSSL_EXTRA is on. */
|
|
ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(MATCH_SUITE_ERROR));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(WC_RSA_PSS)
|
|
/* Same certificate, but this client advertises rsa_pkcs1_sha1, which the
|
|
* RFC allows the server to honor. Verification is off for the same reason
|
|
* as the signature_algorithms_cert case below. */
|
|
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);
|
|
if (EXPECT_SUCCESS())
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_s, sha1CertFile),
|
|
WOLFSSL_SUCCESS);
|
|
/* The signature algorithm list parser has no name for SHA-1 once old TLS
|
|
* versions are compiled out, so append the scheme directly. */
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "RSA-PSS+SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->suites->hashSigAlgo[ssl_c->suites->hashSigAlgoSz++] = sha_mac;
|
|
ssl_c->suites->hashSigAlgo[ssl_c->suites->hashSigAlgoSz++] =
|
|
rsa_sa_algo;
|
|
}
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
#endif /* OPENSSL_EXTRA && WC_RSA_PSS */
|
|
|
|
/* A SHA-256 signed chain is unaffected. */
|
|
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);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_s, svrCertFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
/* The self signed root may be dropped from the chain entirely, so its
|
|
* SHA-1 signature does not block the handshake. */
|
|
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);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_s, sha1RootFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
/* A SHA-1 signed certificate further up the chain is caught as well. The
|
|
* chain sent is a SHA-256 leaf followed by a CA issued SHA-1 certificate,
|
|
* which is not exempt the way a self signed trust anchor is. */
|
|
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);
|
|
ExpectIntEQ(load_file(svrCertFile, &leafBuf, &leafSz), 0);
|
|
ExpectIntEQ(load_file(sha1CliFile, &sha1Buf, &sha1Sz), 0);
|
|
ExpectNotNull(chainBuf = (byte*)XMALLOC(leafSz + sha1Sz, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(chainBuf, leafBuf, leafSz);
|
|
XMEMCPY(chainBuf + leafSz, sha1Buf, sha1Sz);
|
|
}
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_buffer_format(ssl_s, chainBuf,
|
|
(long)(leafSz + sha1Sz), WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(MATCH_SUITE_ERROR));
|
|
|
|
XFREE(chainBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); chainBuf = NULL;
|
|
XFREE(leafBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); leafBuf = NULL;
|
|
XFREE(sha1Buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); sha1Buf = NULL;
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
#ifdef WC_RSA_PSS
|
|
/* An RSASSA-PSS signed certificate carries one signature OID for every
|
|
* digest and names the digest in the algorithm parameters instead, so the
|
|
* chain check has to read the parameters to tell it apart from SHA-1.
|
|
* This CA is SHA-256 signed, so the chain may be sent. Verification is off
|
|
* because the chain is a leaf and an unrelated CA, which is all this case
|
|
* needs the server to send. */
|
|
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);
|
|
if (EXPECT_SUCCESS())
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(load_file(svrCertFile, &leafBuf, &leafSz), 0);
|
|
ExpectIntEQ(load_file(pssCaFile, &pssBuf, &pssSz), 0);
|
|
ExpectNotNull(chainBuf = (byte*)XMALLOC(leafSz + pssSz, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(chainBuf, leafBuf, leafSz);
|
|
XMEMCPY(chainBuf + leafSz, pssBuf, pssSz);
|
|
}
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_buffer_format(ssl_s, chainBuf,
|
|
(long)(leafSz + pssSz), WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
XFREE(chainBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); chainBuf = NULL;
|
|
XFREE(leafBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); leafBuf = NULL;
|
|
XFREE(pssBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); pssBuf = NULL;
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
#endif /* WC_RSA_PSS */
|
|
|
|
/* signature_algorithms_cert covers the chain on its own: this client
|
|
* offers no SHA-1 for handshake signatures but does allow a SHA-1 signed
|
|
* certificate, so the SHA-1 leaf may be sent. What is under test is the
|
|
* chain the server sends, so verification is off: a platform validator
|
|
* such as Apple's Security framework refuses a SHA-1 signature outright
|
|
* and would fail the handshake for an unrelated reason. */
|
|
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);
|
|
if (EXPECT_SUCCESS())
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_s, sha1CertFile),
|
|
WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->certHashSigAlgo = (byte*)XMALLOC(2, ssl_c->heap,
|
|
DYNAMIC_TYPE_TLSX);
|
|
}
|
|
ExpectNotNull(ssl_c->certHashSigAlgo);
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->certHashSigAlgo[0] = sha_mac;
|
|
ssl_c->certHashSigAlgo[1] = rsa_sa_algo;
|
|
ssl_c->certHashSigAlgoSz = 2;
|
|
}
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_s->options.peerSha1CertOk, 1);
|
|
ExpectNull(ssl_s->certHashSigAlgo);
|
|
ExpectIntEQ(ssl_s->certHashSigAlgoSz, 0);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(WC_RSA_PSS)
|
|
/* When both extensions are present the certificate list is the one that
|
|
* counts, so SHA-1 offered only in signature_algorithms does not let the
|
|
* SHA-1 leaf through. */
|
|
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);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_s, sha1CertFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "RSA-PSS+SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->suites->hashSigAlgo[ssl_c->suites->hashSigAlgoSz++] = sha_mac;
|
|
ssl_c->suites->hashSigAlgo[ssl_c->suites->hashSigAlgoSz++] =
|
|
rsa_sa_algo;
|
|
ssl_c->certHashSigAlgo = (byte*)XMALLOC(2, ssl_c->heap,
|
|
DYNAMIC_TYPE_TLSX);
|
|
}
|
|
ExpectNotNull(ssl_c->certHashSigAlgo);
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->certHashSigAlgo[0] = sha256_mac;
|
|
ssl_c->certHashSigAlgo[1] = rsa_sa_algo;
|
|
ssl_c->certHashSigAlgoSz = 2;
|
|
}
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(MATCH_SUITE_ERROR));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
#endif /* OPENSSL_EXTRA && WC_RSA_PSS */
|
|
|
|
/* A client whose own chain is SHA-1 signed sends an empty
|
|
* certificate_list instead of failing (RFC 8446 Section 4.4.2). The
|
|
* server asks for a certificate but does not require one. */
|
|
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);
|
|
if (EXPECT_SUCCESS())
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_c, sha1CliFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, CERT_FILETYPE),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_c->options.sendVerify, SEND_BLANK_CERT);
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 0);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(WC_RSA_PSS) && \
|
|
!defined(WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION)
|
|
/* Same client chain, but the CertificateRequest advertises
|
|
* rsa_pkcs1_sha1, so the client sends its real certificate. The server has
|
|
* to request and then accept that chain, so it cannot run where the
|
|
* platform validator rejects SHA-1 for wolfSSL. */
|
|
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);
|
|
if (EXPECT_SUCCESS())
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "RSA-PSS+SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_s->suites->hashSigAlgo[ssl_s->suites->hashSigAlgoSz++] = sha_mac;
|
|
ssl_s->suites->hashSigAlgo[ssl_s->suites->hashSigAlgoSz++] =
|
|
rsa_sa_algo;
|
|
}
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_c, sha1CliFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, CERT_FILETYPE),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_c->options.sendVerify, SEND_CERT);
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 1);
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
#endif /* OPENSSL_EXTRA && WC_RSA_PSS &&
|
|
* !WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */
|
|
#endif
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_derive_keys_no_key(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
|
|
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);
|
|
|
|
/* DeriveTls13Keys with no_key should succeed (skip secret derivation,
|
|
* only derive keys/IVs from existing secrets). This is used with early
|
|
* data to derive keys without re-deriving the secrets. */
|
|
ExpectIntEQ(DeriveTls13Keys(ssl_s, no_key, DECRYPT_SIDE_ONLY, 0), 0);
|
|
ExpectIntEQ(DeriveTls13Keys(ssl_s, no_key, ENCRYPT_SIDE_ONLY, 0), 0);
|
|
ExpectIntEQ(DeriveTls13Keys(ssl_c, no_key, ENCRYPT_AND_DECRYPT_SIDE, 0),
|
|
0);
|
|
|
|
/* Unknown secret type should return BAD_FUNC_ARG */
|
|
ExpectIntEQ(DeriveTls13Keys(ssl_c, -1, ENCRYPT_SIDE_ONLY, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a truncated PQC hybrid KeyShare in a ServerHello does not cause a
|
|
* heap use-after-free during cleanup. A malicious server sends
|
|
* SECP256R1MLKEM768 with only 10 bytes of key exchange data (expected: 1120+).
|
|
* This exercises the error path in TLSX_KeyShare_ProcessPqcHybridClient().
|
|
* Under ASAN the UAF manifests as ForceZero writing to freed MlKemKey memory
|
|
* during wolfSSL_free -> TLSX_FreeAll -> TLSX_KeyShare_FreeAll. */
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_PQC_HYBRIDS) && \
|
|
!defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY)
|
|
/* Called when writing - discard output. */
|
|
static int PqcHybridUafSend(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)ctx;
|
|
return sz;
|
|
}
|
|
/* Called when reading - feed from buffer. */
|
|
static int PqcHybridUafRecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
WOLFSSL_BUFFER_INFO* msg = (WOLFSSL_BUFFER_INFO*)ctx;
|
|
int len = (int)msg->length;
|
|
|
|
(void)ssl;
|
|
|
|
if (len > sz)
|
|
len = sz;
|
|
XMEMCPY(buf, msg->buffer, len);
|
|
msg->buffer += len;
|
|
msg->length -= len;
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_pqc_hybrid_truncated_keyshare(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_PQC_HYBRIDS) && \
|
|
!defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY)
|
|
WOLFSSL_CTX *ctx = NULL;
|
|
WOLFSSL *ssl = NULL;
|
|
/* Crafted TLS 1.3 ServerHello with SECP256R1MLKEM768 (0x11EB) key_share
|
|
* containing only 10 bytes of key exchange data instead of the expected
|
|
* ~1120 bytes. This triggers the error cleanup path. */
|
|
byte serverHello[] = {
|
|
/* TLS record: Handshake, TLS 1.2 compat, length 68 */
|
|
0x16, 0x03, 0x03, 0x00, 0x44,
|
|
/* Handshake: ServerHello (0x02), length 64 */
|
|
0x02, 0x00, 0x00, 0x40,
|
|
/* legacy_version */
|
|
0x03, 0x03,
|
|
/* random (32 bytes) */
|
|
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
|
|
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
|
|
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
|
|
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
|
|
/* legacy_session_id_echo length: 0 */
|
|
0x00,
|
|
/* cipher_suite: TLS_AES_128_GCM_SHA256 */
|
|
0x13, 0x01,
|
|
/* legacy_compression_method: null */
|
|
0x00,
|
|
/* extensions length: 24 */
|
|
0x00, 0x18,
|
|
/* extension: supported_versions -> TLS 1.3 */
|
|
0x00, 0x2b, 0x00, 0x02, 0x03, 0x04,
|
|
/* extension: key_share (truncated hybrid data) */
|
|
0x00, 0x33, /* type */
|
|
0x00, 0x0e, /* length: 14 */
|
|
0x11, 0xeb, /* named_group: SECP256R1MLKEM768 (4587) */
|
|
0x00, 0x0a, /* key_exchange length: 10 (truncated!) */
|
|
0x41, 0x41, 0x41, 0x41, 0x41, /* bogus key data */
|
|
0x41, 0x41, 0x41, 0x41, 0x41
|
|
};
|
|
WOLFSSL_BUFFER_INFO msg;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
wolfSSL_SetIORecv(ctx, PqcHybridUafRecv);
|
|
wolfSSL_SetIOSend(ctx, PqcHybridUafSend);
|
|
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* Generate the client-side PQC hybrid key share so the truncated
|
|
* ServerHello key_share will be processed (group must match). */
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(ssl, WOLFSSL_SECP256R1MLKEM768),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
msg.buffer = serverHello;
|
|
msg.length = (unsigned int)sizeof(serverHello);
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
|
|
/* Connect should fail gracefully on the truncated key share. */
|
|
ExpectIntEQ(wolfSSL_connect_TLSv13(ssl),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
|
|
/* The UAF, if present, triggers here: wolfSSL_free -> TLSX_FreeAll ->
|
|
* TLSX_KeyShare_FreeAll -> ForceZero on already-freed MlKemKey. */
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a malformed ECDH portion in a correctly-sized PQC hybrid
|
|
* KeyShare does not leave a dangling pointer in keyShareEntry->key.
|
|
*
|
|
* The earlier truncated-keyshare test is rejected by the keLen <= ctSz
|
|
* check before TLSX_KeyShare_ProcessPqcHybridClient sets up the
|
|
* ecc_kse->key = keyShareEntry->key alias, so it does not exercise the
|
|
* dangling-pointer path. This test sends a SECP256R1MLKEM768 key_share
|
|
* whose total length is correct (65-byte ECDH point + 1088-byte ML-KEM
|
|
* ciphertext = 1153 bytes) but whose ECDH leading byte (0x05) is not a
|
|
* valid X9.63 marker. ProcessEcc_ex then fails at wc_ecc_import_x963
|
|
* AFTER its unconditional cleanup at the end of the function frees the
|
|
* aliased key. Without the fix, the outer keyShareEntry->key still
|
|
* holds the freed pointer; wolfSSL_free -> TLSX_KeyShare_FreeAll calls
|
|
* wc_ecc_free + XFREE on it, producing a use-after-free and a double
|
|
* free that ASAN flags. */
|
|
int test_tls13_pqc_hybrid_malformed_ecdh(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_PQC_HYBRIDS) && \
|
|
!defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
(!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \
|
|
!defined(NO_ECC_SECP)
|
|
WOLFSSL_CTX *ctx = NULL;
|
|
WOLFSSL *ssl = NULL;
|
|
/* 5 (record) + 4 (HS) + 1207 (ServerHello body) = 1216 bytes. */
|
|
static byte serverHello[1216];
|
|
word32 i = 0;
|
|
WOLFSSL_BUFFER_INFO msg;
|
|
|
|
XMEMSET(serverHello, 0, sizeof(serverHello));
|
|
|
|
/* Record: handshake, TLS 1.2 compat, length 1211 (0x04bb). */
|
|
serverHello[i++] = 0x16; serverHello[i++] = 0x03; serverHello[i++] = 0x03;
|
|
serverHello[i++] = 0x04; serverHello[i++] = 0xbb;
|
|
/* Handshake: ServerHello (0x02), length 1207 (0x0004b7). */
|
|
serverHello[i++] = 0x02;
|
|
serverHello[i++] = 0x00; serverHello[i++] = 0x04; serverHello[i++] = 0xb7;
|
|
/* legacy_version */
|
|
serverHello[i++] = 0x03; serverHello[i++] = 0x03;
|
|
/* random (32 bytes) */
|
|
XMEMSET(&serverHello[i], 0x42, 32); i += 32;
|
|
/* legacy_session_id_echo length: 0 */
|
|
serverHello[i++] = 0x00;
|
|
/* cipher_suite: TLS_AES_128_GCM_SHA256 */
|
|
serverHello[i++] = 0x13; serverHello[i++] = 0x01;
|
|
/* legacy_compression_method: null */
|
|
serverHello[i++] = 0x00;
|
|
/* extensions length: 1167 (0x048f) */
|
|
serverHello[i++] = 0x04; serverHello[i++] = 0x8f;
|
|
/* extension: supported_versions -> TLS 1.3 */
|
|
serverHello[i++] = 0x00; serverHello[i++] = 0x2b;
|
|
serverHello[i++] = 0x00; serverHello[i++] = 0x02;
|
|
serverHello[i++] = 0x03; serverHello[i++] = 0x04;
|
|
/* extension: key_share, extension_data length 1157 (0x0485) */
|
|
serverHello[i++] = 0x00; serverHello[i++] = 0x33;
|
|
serverHello[i++] = 0x04; serverHello[i++] = 0x85;
|
|
/* server_share.group: SECP256R1MLKEM768 (0x11eb) */
|
|
serverHello[i++] = 0x11; serverHello[i++] = 0xeb;
|
|
/* key_exchange length: 1153 (0x0481) */
|
|
serverHello[i++] = 0x04; serverHello[i++] = 0x81;
|
|
/* ECDH portion (65 bytes): leading 0x05 is not a valid X9.63 marker
|
|
* (valid markers: 0x04, 0x06, 0x07). The remaining 64 bytes stay zero
|
|
* from the initial XMEMSET. */
|
|
serverHello[i++] = 0x05;
|
|
i += 64;
|
|
/* PQC portion (1088 bytes): all zero from the initial XMEMSET. */
|
|
i += 1088;
|
|
AssertIntEQ((int)i, (int)sizeof(serverHello));
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
wolfSSL_SetIORecv(ctx, PqcHybridUafRecv);
|
|
wolfSSL_SetIOSend(ctx, PqcHybridUafSend);
|
|
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* Match the server's offered group so this key_share is processed. */
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(ssl, WOLFSSL_SECP256R1MLKEM768),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
msg.buffer = serverHello;
|
|
msg.length = (unsigned int)sizeof(serverHello);
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
|
|
/* Connect should fail gracefully on the malformed ECDH point. */
|
|
ExpectIntEQ(wolfSSL_connect_TLSv13(ssl),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
|
|
/* Without the fix, this triggers UAF + double-free in
|
|
* TLSX_KeyShare_FreeAll. */
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Regression test for the async hybrid PQC server key share. Drives a full
|
|
* TLS 1.3 P-256 + ML-KEM-768 handshake through the software async simulator
|
|
* while forcing the server's ECDH keygen to complete synchronously so that
|
|
* only the ECDH shared-secret derivation suspends. This "B-first" ordering is
|
|
* what Intel QAT exhibits and previously dropped the server's KEM ciphertext,
|
|
* failing the handshake with SSL_connect -173. */
|
|
int test_tls13_pqc_hybrid_async_server(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_ASYNC_CRYPT) && \
|
|
defined(WOLFSSL_ASYNC_CRYPT_SW) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_PQC_HYBRIDS) && \
|
|
!defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) && \
|
|
!defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
|
|
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
|
|
!defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
(!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && !defined(NO_ECC_SECP) && \
|
|
!defined(WC_ECC_NONBLOCK) && !defined(WOLFSSL_SP_NONBLOCK)
|
|
/* Non-blocking math livelocks the hybrid async re-drive; not applicable. */
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
int group = WOLFSSL_SECP256R1MLKEM768;
|
|
int devId = INVALID_DEVID;
|
|
|
|
/* Open the software async device so the handshake runs the async path. */
|
|
ExpectIntEQ(wolfAsync_DevOpen(&devId), 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);
|
|
|
|
ExpectIntEQ(wolfSSL_SetDevId(ssl_c, devId), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetDevId(ssl_s, devId), WOLFSSL_SUCCESS);
|
|
|
|
/* Negotiate the P-256 + ML-KEM-768 hybrid group on both ends. */
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, &group, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s, &group, 1), WOLFSSL_SUCCESS);
|
|
|
|
/* Force the server's ECDH keygen to run synchronously so that only the
|
|
* ECDH shared-secret derivation suspends (the QAT "B-first" ordering). */
|
|
wolfAsync_SwForceSyncType(ASYNC_SW_ECC_MAKE);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
|
|
/* Server selected and completed the hybrid group end-to-end. */
|
|
ExpectIntEQ(ssl_s->namedGroup, WOLFSSL_SECP256R1MLKEM768);
|
|
|
|
/* Restore default simulator ordering for subsequent tests. */
|
|
wolfAsync_SwForceSyncType(ASYNC_SW_NONE);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfAsync_DevClose(&devId);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a TLS 1.3 NewSessionTicket with a ticket shorter than ID_LEN
|
|
* (32 bytes) does not cause an unsigned integer underflow / OOB read in
|
|
* SetTicket. Uses a full memio handshake, then injects a crafted
|
|
* NewSessionTicket with a 5-byte ticket into the client's read path. */
|
|
int test_tls13_empty_record_limit(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
int recSz = 0;
|
|
/* Send exactly WOLFSSL_MAX_EMPTY_RECORDS to pin the boundary check.
|
|
* The Nth record increments the counter to N, and `N >= N` triggers
|
|
* the error. Sending one more would let a `>=` -> `>` mutation survive
|
|
* (the extra record would still trip the mutated check). */
|
|
int numRecs = WOLFSSL_MAX_EMPTY_RECORDS;
|
|
byte rec[128]; /* buffer for one encrypted record */
|
|
byte *allRecs = NULL;
|
|
int i;
|
|
char buf[64];
|
|
|
|
/* Test 1: Exceeding the empty record limit returns an error. */
|
|
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);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
/* Consume any post-handshake messages (e.g. NewSessionTicket). */
|
|
wolfSSL_read(ssl_c, buf, sizeof(buf));
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
|
|
/* Get the size of an encrypted zero-length app data record. */
|
|
recSz = BuildTls13Message(ssl_c, NULL, 0, NULL, 0,
|
|
application_data, 0, 1, 0);
|
|
ExpectIntGT(recSz, 0);
|
|
ExpectIntLE(recSz, (int)sizeof(rec));
|
|
}
|
|
|
|
/* Build all empty records into one contiguous buffer. */
|
|
if (EXPECT_SUCCESS()) {
|
|
allRecs = (byte*)XMALLOC((size_t)(recSz * numRecs), NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
ExpectNotNull(allRecs);
|
|
}
|
|
|
|
for (i = 0; i < numRecs && EXPECT_SUCCESS(); i++) {
|
|
XMEMSET(rec, 0, sizeof(rec));
|
|
ExpectIntEQ(BuildTls13Message(ssl_c, rec, (int)sizeof(rec), rec +
|
|
RECORD_HEADER_SZ, 0, application_data, 0, 0, 0),
|
|
recSz);
|
|
XMEMCPY(allRecs + i * recSz, rec, (size_t)recSz);
|
|
}
|
|
|
|
/* Inject all records as a single message. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)allRecs, recSz * numRecs), 0);
|
|
}
|
|
|
|
/* The server's wolfSSL_read should fail with EMPTY_RECORD_LIMIT_E. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(EMPTY_RECORD_LIMIT_E));
|
|
}
|
|
|
|
XFREE(allRecs, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
allRecs = NULL;
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
|
|
/* Test 2: Counter resets on non-empty record.
|
|
* Send (limit - 1) empty records, then 1 non-empty, then (limit - 1)
|
|
* more empty records. Should succeed without hitting the limit. */
|
|
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);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_read(ssl_c, buf, sizeof(buf));
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
|
|
recSz = BuildTls13Message(ssl_c, NULL, 0, NULL, 0,
|
|
application_data, 0, 1, 0);
|
|
ExpectIntGT(recSz, 0);
|
|
}
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
int emptyBefore = WOLFSSL_MAX_EMPTY_RECORDS - 1;
|
|
int emptyAfter = WOLFSSL_MAX_EMPTY_RECORDS - 1;
|
|
int dataRecSz = 0;
|
|
byte dataRec[128];
|
|
byte payload[1] = { 'a' };
|
|
int totalSz = 0;
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
dataRecSz = BuildTls13Message(ssl_c, NULL, 0, NULL, 1,
|
|
application_data, 0, 1, 0);
|
|
ExpectIntGT(dataRecSz, 0);
|
|
}
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
totalSz = recSz * (emptyBefore + emptyAfter) + dataRecSz;
|
|
allRecs = (byte*)XMALLOC((size_t)totalSz, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
ExpectNotNull(allRecs);
|
|
}
|
|
|
|
/* Build (limit - 1) empty records */
|
|
for (i = 0; i < emptyBefore && EXPECT_SUCCESS(); i++) {
|
|
XMEMSET(rec, 0, sizeof(rec));
|
|
ExpectIntEQ(BuildTls13Message(ssl_c, rec, (int)sizeof(rec),
|
|
rec + RECORD_HEADER_SZ, 0, application_data,
|
|
0, 0, 0), recSz);
|
|
XMEMCPY(allRecs + i * recSz, rec, (size_t)recSz);
|
|
}
|
|
|
|
/* Build 1 non-empty record */
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(dataRec, 0, sizeof(dataRec));
|
|
XMEMCPY(dataRec + RECORD_HEADER_SZ, payload, sizeof(payload));
|
|
ExpectIntEQ(BuildTls13Message(ssl_c, dataRec, (int)sizeof(dataRec),
|
|
dataRec + RECORD_HEADER_SZ, 1, application_data,
|
|
0, 0, 0), dataRecSz);
|
|
XMEMCPY(allRecs + emptyBefore * recSz, dataRec,
|
|
(size_t)dataRecSz);
|
|
}
|
|
|
|
/* Build (limit - 1) more empty records */
|
|
for (i = 0; i < emptyAfter && EXPECT_SUCCESS(); i++) {
|
|
XMEMSET(rec, 0, sizeof(rec));
|
|
ExpectIntEQ(BuildTls13Message(ssl_c, rec, (int)sizeof(rec),
|
|
rec + RECORD_HEADER_SZ, 0, application_data,
|
|
0, 0, 0), recSz);
|
|
XMEMCPY(allRecs + emptyBefore * recSz + dataRecSz + i * recSz,
|
|
rec, (size_t)recSz);
|
|
}
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)allRecs, totalSz), 0);
|
|
}
|
|
}
|
|
|
|
/* wolfSSL_read should return the 1-byte payload. The counter resets
|
|
* on the non-empty record so neither batch of (limit - 1) empties
|
|
* triggers the error. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), 1);
|
|
ExpectIntEQ(buf[0], 'a');
|
|
}
|
|
|
|
XFREE(allRecs, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a TLS 1.3 NewSessionTicket with a ticket shorter than ID_LEN
|
|
* (32 bytes) does not cause an unsigned integer underflow / OOB read in
|
|
* SetTicket. Uses a full memio handshake, then injects a crafted
|
|
* NewSessionTicket with a 5-byte ticket into the client's read path. */
|
|
int test_tls13_short_session_ticket(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
char buf[64];
|
|
|
|
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);
|
|
|
|
/* Complete a TLS 1.3 handshake. The server will send a
|
|
* NewSessionTicket as part of post-handshake messages. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Read on client to consume the server's NewSessionTicket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Now directly test SetTicket with a short ticket by poking the
|
|
* session. The session object is accessible; replicate the exact
|
|
* vulnerable arithmetic: ticket + length - ID_LEN with length=5.
|
|
* With the fix, sessIdLen is capped to length so no underflow. */
|
|
if (EXPECT_SUCCESS()) {
|
|
byte shortTicket[5] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
|
|
word32 length = sizeof(shortTicket);
|
|
word32 sessIdLen = ID_LEN;
|
|
|
|
if (length < ID_LEN)
|
|
sessIdLen = length;
|
|
|
|
XMEMCPY(ssl_c->session->staticTicket, shortTicket, length);
|
|
ssl_c->session->ticketLen = (word16)length;
|
|
ssl_c->session->ticket = ssl_c->session->staticTicket;
|
|
|
|
/* This is the exact code from SetTicket. Before the fix,
|
|
* sessIdLen would be ID_LEN (32), causing: ticket + 5 - 32
|
|
* to underflow and read OOB. */
|
|
XMEMSET(ssl_c->session->sessionID, 0, ID_LEN);
|
|
XMEMCPY(ssl_c->session->sessionID,
|
|
ssl_c->session->ticket + length - sessIdLen,
|
|
sessIdLen);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
|
|
/* Verify: sessionID should contain only the 5 ticket bytes,
|
|
* zero-padded, not garbage from an OOB read. */
|
|
ExpectBufEQ(ssl_c->session->sessionID, shortTicket, 5);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test valid looking session ticket, but with zero length ticket.
|
|
* */
|
|
int test_tls13_zero_length_session_ticket(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
char buf[64];
|
|
byte rec[256];
|
|
byte ticketMsg[18];
|
|
int recSz = 0;
|
|
int i = 0;
|
|
int idx = 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);
|
|
|
|
/* Complete a TLS 1.3 handshake. The server will send a
|
|
* NewSessionTicket as part of post-handshake messages. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Read on client to consume the server's NewSessionTicket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Encode a valid looking session ticket but with zero length. */
|
|
if (EXPECT_SUCCESS()) {
|
|
idx = 0;
|
|
ticketMsg[idx++] = session_ticket; /* handshake msg type */
|
|
ticketMsg[idx++] = 0x00; /* 24-bit body length */
|
|
ticketMsg[idx++] = 0x00;
|
|
ticketMsg[idx++] = 0x0E; /* = 14 */
|
|
ticketMsg[idx++] = 0x00; ticketMsg[idx++] = 0x00; /* lifetime = 3600 */
|
|
ticketMsg[idx++] = 0x0E; ticketMsg[idx++] = 0x10;
|
|
ticketMsg[idx++] = 0x01; ticketMsg[idx++] = 0x02; /* ticket_age_add */
|
|
ticketMsg[idx++] = 0x03; ticketMsg[idx++] = 0x04;
|
|
ticketMsg[idx++] = 0x01; /* nonce length */
|
|
for (i = 0; i < 1; i++)
|
|
ticketMsg[idx++] = (byte)(0xA0 + i); /* nonce */
|
|
ticketMsg[idx++] = 0x00; ticketMsg[idx++] = 0x00; /* ticket length=0 */
|
|
/* zero ticket data to add */
|
|
ticketMsg[idx++] = 0x00; ticketMsg[idx++] = 0x00; /* extensions: none */
|
|
ExpectIntEQ(idx, (int)sizeof(ticketMsg));
|
|
}
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
recSz = BuildTls13Message(ssl_s, rec, (int)sizeof(rec), ticketMsg,
|
|
idx, handshake, 0, 0, 0);
|
|
ExpectIntGT(recSz, 0);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)rec,
|
|
recSz), 0);
|
|
}
|
|
|
|
/* Read on client to consume the server's NewSessionTicket.
|
|
* The zero length ticket should return BUFFER_ERROR. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), BUFFER_ERROR);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
|
|
/* Custom ticket encryption callback with no key-lifetime limit, so the server
|
|
* issues a ticket for any hint (the default callback refuses a hint of at least
|
|
* half the key lifetime). */
|
|
static int test_tls13_max_lifetime_ticketEncCb(WOLFSSL* ssl,
|
|
byte key_name[WOLFSSL_TICKET_NAME_SZ], byte iv[WOLFSSL_TICKET_IV_SZ],
|
|
byte mac[WOLFSSL_TICKET_MAC_SZ], int enc, byte* ticket, int inLen,
|
|
int* outLen, void* userCtx)
|
|
{
|
|
int i;
|
|
(void)ssl;
|
|
(void)userCtx;
|
|
if (enc) {
|
|
XMEMSET(key_name, 0x2A, WOLFSSL_TICKET_NAME_SZ);
|
|
XMEMSET(iv, 0x2A, WOLFSSL_TICKET_IV_SZ);
|
|
XMEMSET(mac, 0x2A, WOLFSSL_TICKET_MAC_SZ);
|
|
}
|
|
for (i = 0; i < inLen; i++)
|
|
ticket[i] = (byte)(ticket[i] ^ 0xA5);
|
|
*outLen = inLen;
|
|
return WOLFSSL_TICKET_RET_OK;
|
|
}
|
|
#endif
|
|
|
|
/* RFC 8446 Section 4.6.1: a NewSessionTicket lifetime greater than
|
|
* MAX_LIFETIME (604800 seconds, 7 days) must be rejected. The public
|
|
* wolfSSL_CTX_set_TicketHint setter clamps the value, so write the
|
|
* out-of-range hint directly into the server CTX. A custom encryption callback
|
|
* is installed so the default callback's key-lifetime limit does not block
|
|
* issuance, forcing the server to encode the over-limit lifetime onto the wire
|
|
* and confirming the client's DoTls13NewSessionTicket bound check fires. */
|
|
int test_tls13_new_session_ticket_max_lifetime(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
char buf[64];
|
|
|
|
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_CTX_set_TicketEncCb(ctx_s, test_tls13_max_lifetime_ticketEncCb);
|
|
|
|
/* Bypass the public-API clamp at 604800. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ctx_s->ticketHint = MAX_LIFETIME + 1;
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Reading the post-handshake NewSessionTicket should surface the
|
|
* over-limit lifetime as SERVER_HINT_ERROR. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(SERVER_HINT_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
/* A TLS 1.3 client that does not retain its handshake arrays after the
|
|
* handshake (e.g. built without HAVE_SESSION_TICKET, as on a small embedded
|
|
* target) must still be able to receive a post-handshake NewSessionTicket that
|
|
* the negotiated max_fragment_length has split across multiple records.
|
|
*
|
|
* Regression test: the handshake-message defragmentation buffer used to live
|
|
* inside ssl->arrays, which FreeHandshakeResources() releases once the
|
|
* handshake completes. A fragmented NewSessionTicket arriving afterwards could
|
|
* therefore not be reassembled and the connection was torn down with
|
|
* INCOMPLETE_DATA (-310). Fragmentation *during* the handshake was unaffected
|
|
* because the arrays still existed then.
|
|
*
|
|
* The test completes a normal handshake, forces the post-handshake
|
|
* "arrays released" state with FreeArrays(), then injects a NewSessionTicket
|
|
* fragmented across two records and confirms the client reassembles and
|
|
* consumes it (WANT_READ, no application data) instead of failing with
|
|
* INCOMPLETE_DATA. */
|
|
int test_tls13_fragmented_session_ticket(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
char buf[64];
|
|
/* NewSessionTicket: lifetime(4) + age_add(4) + nonce(1+8) + ticket(2+32) +
|
|
* extensions(2) = 53 byte body, 4 + 53 = 57 byte handshake message. */
|
|
byte ticketMsg[57];
|
|
byte rec[256];
|
|
int recSz = 0;
|
|
int split = 20; /* fragment boundary inside the message */
|
|
int readRet, errRet;
|
|
int i, 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);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Drain any genuine NewSessionTicket the server already sent so the
|
|
* application-data record sequence numbers of both peers stay in sync with
|
|
* the records injected below. */
|
|
if (EXPECT_SUCCESS()) {
|
|
readRet = wolfSSL_read(ssl_c, buf, sizeof(buf));
|
|
errRet = wolfSSL_get_error(ssl_c, readRet);
|
|
ExpectIntEQ(readRet, -1);
|
|
ExpectIntEQ(errRet, WOLFSSL_ERROR_WANT_READ);
|
|
}
|
|
|
|
/* Reproduce the embedded/no-session-ticket configuration in which the
|
|
* client's handshake arrays are released once the handshake completes
|
|
* (FreeHandshakeResources() -> FreeArrays()). It is done here inline using
|
|
* the library's own allocation types rather than calling FreeArrays(),
|
|
* which is an internal-only symbol hidden in shared-library builds. In
|
|
* configurations that already release the arrays (e.g. no HAVE_SESSION_TICKET)
|
|
* they are NULL at this point and the free is skipped. */
|
|
if (EXPECT_SUCCESS() && ssl_c->arrays != NULL) {
|
|
/* The pre-master secret is carried at the end of the same allocation,
|
|
* so it goes with it. Zero before freeing so WOLFSSL_CHECK_MEM_ZERO
|
|
* builds don't abort. */
|
|
ForceZero(ssl_c->arrays, sizeof(Arrays));
|
|
XFREE(ssl_c->arrays, ssl_c->heap, DYNAMIC_TYPE_ARRAYS);
|
|
ssl_c->arrays = NULL;
|
|
}
|
|
/* The post-handshake NewSessionTicket must now be processed with
|
|
* ssl->arrays == NULL, which is what historically broke reassembly. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectNull(ssl_c->arrays);
|
|
}
|
|
|
|
/* Encode a syntactically valid NewSessionTicket so that a client built with
|
|
* HAVE_SESSION_TICKET parses it cleanly; a client built without it simply
|
|
* skips the body. Either way reassembly must succeed first. */
|
|
if (EXPECT_SUCCESS()) {
|
|
idx = 0;
|
|
ticketMsg[idx++] = session_ticket; /* handshake msg type */
|
|
ticketMsg[idx++] = 0x00; /* 24-bit body length */
|
|
ticketMsg[idx++] = 0x00;
|
|
ticketMsg[idx++] = 0x35; /* = 53 */
|
|
ticketMsg[idx++] = 0x00; ticketMsg[idx++] = 0x00; /* lifetime = 3600 */
|
|
ticketMsg[idx++] = 0x0E; ticketMsg[idx++] = 0x10;
|
|
ticketMsg[idx++] = 0x01; ticketMsg[idx++] = 0x02; /* ticket_age_add */
|
|
ticketMsg[idx++] = 0x03; ticketMsg[idx++] = 0x04;
|
|
ticketMsg[idx++] = 0x08; /* nonce length */
|
|
for (i = 0; i < 8; i++)
|
|
ticketMsg[idx++] = (byte)(0xA0 + i); /* nonce */
|
|
ticketMsg[idx++] = 0x00; ticketMsg[idx++] = 0x20; /* ticket length=32 */
|
|
for (i = 0; i < 32; i++)
|
|
ticketMsg[idx++] = (byte)(0x40 + i); /* ticket */
|
|
ticketMsg[idx++] = 0x00; ticketMsg[idx++] = 0x00; /* extensions: none */
|
|
ExpectIntEQ(idx, (int)sizeof(ticketMsg));
|
|
}
|
|
|
|
/* Fragment 1: bytes [0, split) of the handshake message, encrypted with the
|
|
* server's keys, injected into the client's read path. */
|
|
if (EXPECT_SUCCESS()) {
|
|
recSz = BuildTls13Message(ssl_s, rec, (int)sizeof(rec), ticketMsg,
|
|
split, handshake, 0, 0, 0);
|
|
ExpectIntGT(recSz, 0);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)rec,
|
|
recSz), 0);
|
|
}
|
|
/* Fragment 2: the remaining bytes of the handshake message. */
|
|
if (EXPECT_SUCCESS()) {
|
|
recSz = BuildTls13Message(ssl_s, rec, (int)sizeof(rec),
|
|
ticketMsg + split,
|
|
(int)sizeof(ticketMsg) - split, handshake,
|
|
0, 0, 0);
|
|
ExpectIntGT(recSz, 0);
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, (const char*)rec,
|
|
recSz), 0);
|
|
}
|
|
|
|
/* Before the fix the first fragment reaches DoTls13HandShakeMsg while
|
|
* ssl->arrays is NULL, cannot be buffered, and the read fails with
|
|
* INCOMPLETE_DATA. With the fix the two records reassemble into the
|
|
* NewSessionTicket, which is consumed, leaving no application data. */
|
|
if (EXPECT_SUCCESS()) {
|
|
readRet = wolfSSL_read(ssl_c, buf, sizeof(buf));
|
|
errRet = wolfSSL_get_error(ssl_c, readRet);
|
|
ExpectIntEQ(readRet, -1);
|
|
ExpectIntNE(errRet, WC_NO_ERR_TRACE(INCOMPLETE_DATA));
|
|
ExpectIntEQ(errRet, WOLFSSL_ERROR_WANT_READ);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
/* Test that a corrupted TLS 1.3 Finished verify_data is properly rejected
|
|
* with VERIFY_FINISHED_ERROR. We run the handshake step-by-step and corrupt
|
|
* the server's client_write_MAC_secret before it processes the client's
|
|
* Finished, causing the HMAC comparison to fail.
|
|
*/
|
|
int test_tls13_corrupted_finished(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int ret;
|
|
|
|
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);
|
|
|
|
/* Step 1: Client sends ClientHello */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 2: Server processes CH, sends SH + EE + Cert + CV + Finished */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 3: Client processes server flight, verifies server Finished,
|
|
* sends client Finished */
|
|
ret = wolfSSL_connect(ssl_c);
|
|
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)) {
|
|
/* Actually: Server sent HelloRetryRequest */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 1: Client sends ClientHello */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 2: Server processes CH, sends SH + EE + Cert + CV + Finished */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 3: Client processes server flight, verifies server Finished,
|
|
* sends client Finished */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
}
|
|
else {
|
|
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Corrupt the server's client_write_MAC_secret so that when it computes
|
|
* the expected Finished HMAC, the result won't match the client's actual
|
|
* Finished message. */
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(ssl_s->keys.client_write_MAC_secret, 0xFF,
|
|
sizeof(ssl_s->keys.client_write_MAC_secret));
|
|
}
|
|
|
|
/* Step 4: Server processes client Finished - should fail */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERIFY_FINISHED_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
/* Test that DoTls13CertificateVerify rejects a CertificateVerify whose
|
|
* signature algorithm is not one the receiver advertised.
|
|
*
|
|
* This drives the validSigAlgo == 0 path in src/tls13.c: the loop that scans
|
|
* the receiver's hashSigAlgo list for the algorithm carried in the
|
|
* CertificateVerify message. When no entry matches, validSigAlgo stays 0 and
|
|
* the message is rejected with INVALID_PARAMETER.
|
|
*
|
|
* The client sends its ClientHello (advertising its real signature
|
|
* algorithms), the server picks one of them to sign its CertificateVerify, and
|
|
* then - before the client processes the server flight - the client's accepted
|
|
* signature algorithm list is overwritten with a bogus entry. The server's
|
|
* (legitimately chosen) algorithm is therefore absent from the client's list
|
|
* at verification time, so the lookup fails.
|
|
*/
|
|
int test_tls13_certificate_verify_bad_sigalgo(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int group = WOLFSSL_ECC_SECP256R1;
|
|
Suites* suites = NULL;
|
|
|
|
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);
|
|
|
|
/* Constrain both peers to a single, mutually-supported key share group so
|
|
* the server never sends a HelloRetryRequest. That keeps the message
|
|
* stepping below deterministic: one ClientHello, then the full server
|
|
* flight (which contains the CertificateVerify we want to corrupt). */
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, &group, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, group), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s, &group, 1), WOLFSSL_SUCCESS);
|
|
|
|
/* Step 1: Client sends ClientHello, advertising its real signature
|
|
* algorithms. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 2: Server processes the ClientHello and emits SH + EE + Cert +
|
|
* CertVerify + Finished, signing the CertVerify with an algorithm taken
|
|
* from the list the client just advertised. */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* The ClientHello has now been sent, so changing the client's accepted
|
|
* signature algorithms no longer affects what it advertised. Replace the
|
|
* list with a single bogus pair (not a valid SignatureScheme) that the
|
|
* server cannot have used. When the client verifies the server's
|
|
* CertificateVerify, the lookup loop finds no match -> validSigAlgo == 0. */
|
|
if (EXPECT_SUCCESS()) {
|
|
suites = (Suites*)WOLFSSL_SUITES(ssl_c);
|
|
ExpectNotNull(suites);
|
|
}
|
|
if (suites != NULL) {
|
|
suites->hashSigAlgo[0] = 0xFE;
|
|
suites->hashSigAlgo[1] = 0xFE;
|
|
suites->hashSigAlgoSz = 2;
|
|
}
|
|
|
|
/* Step 3: Client processes the server flight. DoTls13CertificateVerify
|
|
* must reject the CertificateVerify with INVALID_PARAMETER. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(INVALID_PARAMETER));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
/* Test the TLS 1.3 peerAuthGood fail-safe checks on both sides.
|
|
* The client branch queues a real server flight before forcing
|
|
* FIRST_REPLY_SECOND on a live handshake object, and the server branch clears
|
|
* peerAuthGood just before processing the client's Finished.
|
|
*/
|
|
int test_tls13_peerauth_failsafe(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int ret;
|
|
|
|
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);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
/* Queue ClientHello and server flight. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
ssl_c->options.peerAuthGood = 0;
|
|
ssl_c->options.sendVerify = 0;
|
|
ssl_c->options.connectState = FIRST_REPLY_SECOND;
|
|
ret = wolfSSL_connect(ssl_c);
|
|
ExpectIntEQ(ret, WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(ssl_c->options.connectState, FIRST_REPLY_SECOND);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ctx_c = NULL;
|
|
ctx_s = NULL;
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ret = wolfSSL_connect(ssl_c);
|
|
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)) {
|
|
/* HelloRetryRequest sent by server. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
}
|
|
else {
|
|
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
ssl_s->options.peerAuthGood = 0;
|
|
ret = wolfSSL_accept(ssl_s);
|
|
ExpectIntEQ(ret, WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(ssl_s->options.peerAuthGood, 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
/* Test that a corrupted HRR cookie HMAC is rejected with HRR_COOKIE_ERROR. */
|
|
int test_tls13_hrr_bad_cookie(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_SEND_HRR_COOKIE) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_send_hrr_cookie(ssl_s, NULL, 0), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
/* Step 1: Client sends CH1 (no key shares) */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 2: Server sends HRR with cookie */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 3: Client processes HRR, sends CH2 with cookie */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Corrupt the server-side cookie secret after HRR so CH2's cookie no longer
|
|
* verifies in TlsCheckCookie(). */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectNotNull(ssl_s->buffers.tls13CookieSecret.buffer);
|
|
ExpectIntGT(ssl_s->buffers.tls13CookieSecret.length, 0);
|
|
ssl_s->buffers.tls13CookieSecret.buffer[
|
|
ssl_s->buffers.tls13CookieSecret.length - 1] ^= 0xFF;
|
|
}
|
|
|
|
/* Step 4: Server processes corrupted CH2 - should fail */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(HRR_COOKIE_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Run a handshake where the server sends a cookie in its HelloRetryRequest:
|
|
* the client must echo it back for the server to accept the second
|
|
* ClientHello. With noKeyShares the retry also asks for a different group,
|
|
* without it the cookie is the only thing the retry changes. */
|
|
static int test_tls13_hrr_cookie_handshake_once(int noKeyShares)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_SEND_HRR_COOKIE) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_send_hrr_cookie(ssl_s, NULL, 0), WOLFSSL_SUCCESS);
|
|
if (noKeyShares) {
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Client saw a cookie and the server accepted the one echoed back. */
|
|
ExpectIntEQ(ssl_c->options.hrrSentCookie, 1);
|
|
ExpectIntEQ(ssl_s->options.cookieGood, 1);
|
|
ExpectIntEQ(ssl_c->options.hrrSentKeyShare, noKeyShares ? 1 : 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#else
|
|
(void)noKeyShares;
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* HelloRetryRequest carrying both a key share and a cookie. */
|
|
int test_tls13_hrr_cookie_handshake(void)
|
|
{
|
|
return test_tls13_hrr_cookie_handshake_once(1);
|
|
}
|
|
|
|
/* HelloRetryRequest whose only change is the cookie. This is what a server
|
|
* sends when it wants the client's address confirmed and the offered key
|
|
* share was already usable. */
|
|
int test_tls13_hrr_cookie_only_handshake(void)
|
|
{
|
|
return test_tls13_hrr_cookie_handshake_once(0);
|
|
}
|
|
|
|
/* Test that a client stores a cookie received in a HelloRetryRequest and
|
|
* writes it back out in the second ClientHello. Required of every TLS 1.3
|
|
* client by RFC 8446 4.2.2, so it must hold without WOLFSSL_SEND_HRR_COOKIE,
|
|
* which only controls a server creating cookies. */
|
|
int test_tls13_client_cookie_echo(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
TLSX* ext = NULL;
|
|
Cookie* cookie = NULL;
|
|
word32 length = 0;
|
|
word32 offset = 0;
|
|
byte out[128];
|
|
/* Cookie extension of a HelloRetryRequest: type, extension length,
|
|
* cookie length, cookie. */
|
|
static const byte hrrExt[] = {
|
|
0x00, 0x2c, 0x00, 0x08, 0x00, 0x06, 'w', 'o', 'l', 'f', 'S', 'L'
|
|
};
|
|
|
|
XMEMSET(out, 0, sizeof(out));
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
ExpectIntEQ(TLSX_Parse(ssl, hrrExt, (word16)sizeof(hrrExt),
|
|
hello_retry_request, NULL), 0);
|
|
|
|
ExpectNotNull(ext = TLSX_Find(ssl->extensions, TLSX_COOKIE));
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectNotNull(cookie = (Cookie*)ext->data);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(cookie->len, sizeof(hrrExt) - 6);
|
|
ExpectBufEQ(cookie->data, hrrExt + 6, sizeof(hrrExt) - 6);
|
|
}
|
|
|
|
ExpectIntEQ(TLSX_GetRequestSize(ssl, client_hello, &length), 0);
|
|
ExpectIntGT(length, 0);
|
|
ExpectIntLE(length, (word32)sizeof(out));
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(TLSX_WriteRequest(ssl, out, client_hello, &offset), 0);
|
|
ExpectIntEQ(offset, length);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
int found = 0;
|
|
word32 i;
|
|
|
|
for (i = 0; i + sizeof(hrrExt) <= offset; i++) {
|
|
if (XMEMCMP(out + i, hrrExt, sizeof(hrrExt)) == 0) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
ExpectIntEQ(found, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a client rejects a HelloRetryRequest cookie larger than it is
|
|
* willing to store and echo back. RFC 8446 4.2.2 allows up to 2^16-1 bytes;
|
|
* WOLFSSL_MAX_TLS13_COOKIE_SZ is the smaller local cap tested here.
|
|
* One byte over the cap plus the six bytes of extension headers must still
|
|
* fit the word16 length TLSX_Parse() takes: 65535 - 1 - 6 = 65528. */
|
|
int test_tls13_client_cookie_too_big(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
WOLFSSL_MAX_TLS13_COOKIE_SZ <= 65528
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
byte* hrrExt = NULL;
|
|
const word16 cookieLen = WOLFSSL_MAX_TLS13_COOKIE_SZ + 1;
|
|
const word16 extLen = cookieLen + OPAQUE16_LEN;
|
|
const word32 totalLen = (word32)extLen + OPAQUE16_LEN * 2;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
ExpectNotNull(hrrExt = (byte*)XMALLOC(totalLen, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(hrrExt, 0, totalLen);
|
|
c16toa(TLSX_COOKIE, hrrExt);
|
|
c16toa(extLen, hrrExt + OPAQUE16_LEN);
|
|
c16toa(cookieLen, hrrExt + OPAQUE16_LEN * 2);
|
|
}
|
|
|
|
ExpectIntEQ(TLSX_Parse(ssl, hrrExt, (word16)totalLen, hello_retry_request,
|
|
NULL), WC_NO_ERR_TRACE(HRR_COOKIE_ERROR));
|
|
ExpectNull(TLSX_Find(ssl->extensions, TLSX_COOKIE));
|
|
|
|
XFREE(hrrExt, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test the cookie extension of a ClientHello on the server. How much of it a
|
|
* server checks depends on the build:
|
|
* - without WOLFSSL_TLS13_COOKIE there is no cookie code, so the extension
|
|
* is skipped without being looked at;
|
|
* - with WOLFSSL_TLS13_COOKIE, which any build holding a client has, the
|
|
* extension is parsed and so its encoding is checked;
|
|
* - with WOLFSSL_SEND_HRR_COOKIE the server also kept the cookie it sent,
|
|
* and the echoed value has to match it. */
|
|
int test_tls13_server_cookie_parse(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_ECC) && !defined(NO_FILESYSTEM)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
int expectBad = 0;
|
|
int expectUnsent = 0;
|
|
/* Cookie extension: type, extension length, cookie length, cookie. */
|
|
static const byte cookieExt[] = {
|
|
0x00, 0x2c, 0x00, 0x08, 0x00, 0x06, 'w', 'o', 'l', 'f', 'S', 'L'
|
|
};
|
|
/* Same, with a cookie length that disagrees with the extension length. */
|
|
static const byte badExt[] = {
|
|
0x00, 0x2c, 0x00, 0x08, 0x00, 0x07, 'w', 'o', 'l', 'f', 'S', 'L'
|
|
};
|
|
/* Well formed, same length as the cookie the server sent, but not it. */
|
|
static const byte otherExt[] = {
|
|
0x00, 0x2c, 0x00, 0x08, 0x00, 0x06, 'w', 'o', 'l', 'f', 'S', 'S'
|
|
};
|
|
/* Well formed, and not even the length of the cookie sent. */
|
|
static const byte longerExt[] = {
|
|
0x00, 0x2c, 0x00, 0x09, 0x00, 0x07, 'w', 'o', 'l', 'f', 'S', 'S', 'L'
|
|
};
|
|
|
|
#ifdef WOLFSSL_TLS13_COOKIE
|
|
expectBad = WC_NO_ERR_TRACE(BUFFER_E);
|
|
#ifdef WOLFSSL_SEND_HRR_COOKIE
|
|
expectUnsent = WC_NO_ERR_TRACE(HRR_COOKIE_ERROR);
|
|
#endif
|
|
#endif
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, eccCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, eccKeyFile,
|
|
CERT_FILETYPE));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
ExpectIntEQ(TLSX_Parse(ssl, badExt, (word16)sizeof(badExt), client_hello,
|
|
(Suites*)WOLFSSL_SUITES(ssl)), expectBad);
|
|
|
|
/* Nothing has been sent yet, so there is no cookie to echo. */
|
|
ExpectIntEQ(TLSX_Parse(ssl, cookieExt, (word16)sizeof(cookieExt),
|
|
client_hello, (Suites*)WOLFSSL_SUITES(ssl)), expectUnsent);
|
|
|
|
#ifdef WOLFSSL_SEND_HRR_COOKIE
|
|
/* Give the server the cookie it sends in a HelloRetryRequest. Parsing the
|
|
* extension as one leaves it stored the way the server stores it. */
|
|
ExpectIntEQ(TLSX_Parse(ssl, cookieExt, (word16)sizeof(cookieExt),
|
|
hello_retry_request, NULL), 0);
|
|
#endif
|
|
|
|
/* The cookie the server sent, echoed back unchanged. */
|
|
ExpectIntEQ(TLSX_Parse(ssl, cookieExt, (word16)sizeof(cookieExt),
|
|
client_hello, (Suites*)WOLFSSL_SUITES(ssl)), 0);
|
|
|
|
#ifdef WOLFSSL_SEND_HRR_COOKIE
|
|
ExpectIntEQ(TLSX_Parse(ssl, otherExt, (word16)sizeof(otherExt),
|
|
client_hello, (Suites*)WOLFSSL_SUITES(ssl)),
|
|
WC_NO_ERR_TRACE(HRR_COOKIE_ERROR));
|
|
ExpectIntEQ(TLSX_Parse(ssl, longerExt, (word16)sizeof(longerExt),
|
|
client_hello, (Suites*)WOLFSSL_SUITES(ssl)),
|
|
WC_NO_ERR_TRACE(HRR_COOKIE_ERROR));
|
|
#else
|
|
(void)otherExt;
|
|
(void)longerExt;
|
|
#endif
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a TLS 1.3 encrypted record whose inner content type resolves to
|
|
* zero is rejected in removeMsgInnerPadding() with PARSE_ERROR and an
|
|
* unexpected_message alert. */
|
|
int test_tls13_zero_inner_content_type(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
byte record[64];
|
|
byte dummy = 0;
|
|
char readBuf[8];
|
|
int recordSz;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_no_ticket_TLSv13(ssl_s), 0);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(test_ctx.c_len, 0);
|
|
ExpectIntEQ(test_ctx.s_len, 0);
|
|
|
|
recordSz = BuildTls13Message(ssl_c, record, (int)sizeof(record), &dummy,
|
|
0, no_type, 0, 0, 0);
|
|
ExpectIntGT(recordSz, 0);
|
|
ExpectIntEQ(wolfSSL_inject(ssl_s, record, recordSz), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, (int)sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WC_NO_ERR_TRACE(PARSE_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_s, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, unexpected_message);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 Section 4.6.2: A client that receives a post-handshake
|
|
* CertificateRequest message without having sent the "post_handshake_auth"
|
|
* extension MUST send an "unexpected_message" fatal alert.
|
|
*
|
|
* This test completes a TLS 1.3 handshake in which the client never enabled
|
|
* post-handshake auth (so no extension was sent in the ClientHello), then
|
|
* forces the server to transmit a post-handshake CertificateRequest anyway.
|
|
* The client must reject the message with an unexpected_message fatal
|
|
* alert. */
|
|
int test_tls13_post_handshake_auth_no_ext(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
char readBuf[8];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
/* Keep the post-handshake byte stream simple by suppressing the server's
|
|
* NewSessionTicket so the only post-handshake record from the server is
|
|
* the CertificateRequest under test. */
|
|
ExpectIntEQ(wolfSSL_no_ticket_TLSv13(ssl_s), 0);
|
|
|
|
/* Intentionally do NOT call wolfSSL_allow_post_handshake_auth() on the
|
|
* client so the post_handshake_auth extension is omitted from the
|
|
* ClientHello. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* The server's wolfSSL_request_certificate() refuses to send a
|
|
* post-handshake CertificateRequest unless its postHandshakeAuth flag is
|
|
* set (normally set when parsing the client's extension). To exercise
|
|
* the receive-side check we are testing, simulate a server that sends
|
|
* one anyway by toggling the flag directly. */
|
|
if (ssl_s != NULL)
|
|
ssl_s->options.postHandshakeAuth = 1;
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntGT(test_ctx.c_len, 0);
|
|
|
|
/* The client must reject the unsolicited CertificateRequest. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(OUT_OF_ORDER_E));
|
|
|
|
/* And the client must transmit a fatal unexpected_message alert. */
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, unexpected_message);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* wolfSSL_allow_post_handshake_auth() must be called before the handshake
|
|
* starts. A late call must fail with BAD_STATE_E and must not enable
|
|
* post-handshake authentication for the connection. */
|
|
int test_tls13_post_handshake_auth_late_allow(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
char readBuf[8];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_no_ticket_TLSv13(ssl_s), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Too late: handshake is complete. */
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
|
|
if (ssl_s != NULL)
|
|
ssl_s->options.postHandshakeAuth = 1;
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntGT(test_ctx.c_len, 0);
|
|
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(OUT_OF_ORDER_E));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, unexpected_message);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A post-handshake CertificateRequest must reach the wire from
|
|
* wolfSSL_request_certificate() even when the server groups messages. */
|
|
int test_tls13_pha_group_messages(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char readBuf[8];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
NULL, NULL), 0);
|
|
wolfSSL_set_verify(ssl_s,
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_POST_HANDSHAKE, NULL);
|
|
ExpectIntEQ(wolfSSL_no_ticket_TLSv13(ssl_s), 0);
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_set_group_messages(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_ctx.c_len, 0);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntGT(test_ctx.c_len, 0);
|
|
|
|
/* The client answers with Certificate, CertificateVerify and Finished
|
|
* without the server writing any application data first. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntGT(test_ctx.s_len, 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, (int)sizeof(readBuf)),
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_ctx.s_len, 0);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->options.havePeerVerify, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a TLS 1.3-capable client rejects downgrade sentinels in a
|
|
* downgraded ServerHello random for both TLS 1.2 and TLS 1.1-or-lower. */
|
|
int test_tls13_downgrade_sentinel(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* The sentinel marks a downgrade to TLS 1.2 or below, so the test needs a
|
|
* server of that version to produce one. */
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
int randomOff = 11 + 24;
|
|
static const byte downgradeTls12[8] = {
|
|
0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01
|
|
};
|
|
#ifndef NO_OLD_TLS
|
|
static const byte downgradeTls11[8] = {
|
|
0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00
|
|
};
|
|
#endif
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLS_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntGT(test_ctx.c_len, randomOff + (int)sizeof(downgradeTls12));
|
|
XMEMCPY(test_ctx.c_buff + randomOff, downgradeTls12,
|
|
sizeof(downgradeTls12));
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectTrue(h.last_tx.code == illegal_parameter ||
|
|
h.last_tx.code == wolfssl_alert_protocol_version);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
#ifndef NO_OLD_TLS
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ctx_c = NULL;
|
|
ctx_s = NULL;
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLS_client_method, wolfTLSv1_1_server_method), 0);
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntGT(test_ctx.c_len, randomOff + (int)sizeof(downgradeTls11));
|
|
XMEMCPY(test_ctx.c_buff + randomOff, downgradeTls11,
|
|
sizeof(downgradeTls11));
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectTrue(h.last_tx.code == illegal_parameter ||
|
|
h.last_tx.code == wolfssl_alert_protocol_version);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a client does not treat a ServerHello carrying supported_versions
|
|
* as an older-version ServerHello because of its legacy_version. RFC 8446
|
|
* Section 4.2.1: "A server which negotiates a version of TLS prior to TLS 1.3
|
|
* MUST set ServerHello.version and MUST NOT send the "supported_versions"
|
|
* extension", and a client "MUST ignore the ServerHello.legacy_version value
|
|
* and MUST use only the "supported_versions" extension to determine the
|
|
* selected version". A server selecting TLS 1.3 must set legacy_version to
|
|
* 0x0303 (Section 4.1.3). */
|
|
int test_tls13_serverhello_legacy_version(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
/* legacy_version follows the record (5) and handshake (4) headers. */
|
|
int verOff = 9;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
/* Client allows downgrading, so the legacy dispatch is reachable. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLS_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Claim TLS 1.0 in legacy_version. supported_versions still selects
|
|
* TLS 1.3. Rejected by the version floor when old TLS is compiled out,
|
|
* and by the supported_versions check when it is not; either way the
|
|
* result must be a fatal protocol_version alert. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntGT(test_ctx.c_len, verOff + 1);
|
|
test_ctx.c_buff[verOff + 0] = SSLv3_MAJOR;
|
|
test_ctx.c_buff[verOff + 1] = TLSv1_MINOR;
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, wolfssl_alert_protocol_version);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
/* Expect skips the next wolfSSL_new when this block's allocation failed,
|
|
* so the pointers still name the objects just freed. */
|
|
ssl_c = NULL;
|
|
ctx_c = NULL;
|
|
ssl_s = NULL;
|
|
ctx_s = NULL;
|
|
|
|
/* supported_versions on a ServerHello negotiating below TLS 1.3 is
|
|
* refused whatever version it names (RFC 8446 Section 4.2.1). Hand the
|
|
* extension straight to the parser as a TLS 1.2 client: no version floor
|
|
* filters this route, so the check runs in every build. */
|
|
{
|
|
/* supported_versions (43) of length 2 naming TLS 1.3 */
|
|
static const byte svExt[6] = { 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04 };
|
|
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(TLSX_Parse(ssl_c, svExt, (word16)sizeof(svExt),
|
|
server_hello, NULL), WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ssl_c = NULL;
|
|
ctx_c = NULL;
|
|
}
|
|
|
|
/* A ServerHello that really is older still downgrades: no
|
|
* supported_versions extension to contradict its legacy_version. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ctx_c = NULL;
|
|
ctx_s = NULL;
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLS_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_version(ssl_c), TLS1_2_VERSION);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a TLS 1.3 client rejects ServerHello cipher suites that are not
|
|
* TLS 1.3 suites or were not offered by the client. */
|
|
int test_tls13_serverhello_bad_cipher_suites(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(WOLFSSL_NO_TLS12) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256) && \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx12_c = NULL;
|
|
WOLFSSL_CTX *ctx12_s = NULL;
|
|
WOLFSSL *ssl12_c = NULL;
|
|
WOLFSSL *ssl12_s = NULL;
|
|
struct test_memio_ctx test_ctx12;
|
|
int suiteOff;
|
|
byte tls12Suite0 = 0;
|
|
byte tls12Suite = 0;
|
|
|
|
XMEMSET(&test_ctx12, 0, sizeof(test_ctx12));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx12, &ctx12_c, &ctx12_s, &ssl12_c,
|
|
&ssl12_s, wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl12_c, ssl12_s, 10, NULL), 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
tls12Suite0 = ssl12_c->options.cipherSuite0;
|
|
tls12Suite = ssl12_c->options.cipherSuite;
|
|
ExpectIntNE(tls12Suite0, TLS13_BYTE);
|
|
}
|
|
wolfSSL_free(ssl12_c);
|
|
wolfSSL_CTX_free(ctx12_c);
|
|
wolfSSL_free(ssl12_s);
|
|
wolfSSL_CTX_free(ctx12_s);
|
|
|
|
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);
|
|
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "TLS13-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "TLS13-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
suiteOff = 44 + (byte)test_ctx.c_buff[43];
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntGT(test_ctx.c_len, suiteOff + 1);
|
|
ExpectNotNull(ssl_c->suites);
|
|
ssl_c->suites->suiteSz = 2;
|
|
ssl_c->suites->suites[0] = tls12Suite0;
|
|
ssl_c->suites->suites[1] = tls12Suite;
|
|
test_ctx.c_buff[suiteOff + 0] = tls12Suite0;
|
|
test_ctx.c_buff[suiteOff + 1] = tls12Suite;
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(INVALID_PARAMETER));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ctx_c = NULL;
|
|
ctx_s = NULL;
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "TLS13-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "TLS13-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
suiteOff = 44 + (byte)test_ctx.c_buff[43];
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntGT(test_ctx.c_len, suiteOff + 1);
|
|
test_ctx.c_buff[suiteOff + 0] = TLS13_BYTE;
|
|
test_ctx.c_buff[suiteOff + 1] = TLS_AES_256_GCM_SHA384;
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(INVALID_PARAMETER));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Verify that a peer certificate restored from a session ticket is re-verified
|
|
* against the current trust store. After CA removal, the cert must not be
|
|
* installed into ssl->peerCert even though the ticket itself decrypts fine. */
|
|
int test_tls13_ticket_peer_cert_reverify(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
|
|
!defined(NO_CERT_IN_TICKET) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_RSA) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
WOLFSSL_X509 *peer = NULL;
|
|
char readBuf[64];
|
|
|
|
/* --- Step 1: mTLS handshake, obtain a session ticket --- */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
/* Set up CTXs manually so we can configure mTLS before SSL creation */
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
/* Server trusts both its own CA and the client CA for mTLS */
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s,
|
|
"certs/client-ca.pem", 0), WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
|
|
/* Create SSL objects from fully-configured CTXs */
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Drain post-handshake NewSessionTicket */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Peer cert should be available after initial handshake */
|
|
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s));
|
|
wolfSSL_X509_free(peer);
|
|
peer = NULL;
|
|
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
|
|
/* --- Step 2: remove the client CA from the server trust store --- */
|
|
ExpectIntEQ(wolfSSL_CTX_UnloadCAs(ctx_s), WOLFSSL_SUCCESS);
|
|
/* Re-load only the server's own CA so TLS works, but not the client CA */
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* --- Step 3: resume with the old ticket --- */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
/* Resumption handshake succeeds (the ticket master secret is fine) */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* The session should have been resumed via PSK. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1);
|
|
/* But the peer cert must NOT be restored because the issuing CA is
|
|
* no longer in the trust store. Check the peerCert directly rather
|
|
* than wolfSSL_get_peer_certificate which has a session-chain
|
|
* fallback that may see stale cache state. */
|
|
ExpectIntEQ(ssl_s->peerCert.issuer.sz, 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Verify that a TLS 1.3 server never stores a session in its cache under the
|
|
* client-chosen legacy_session_id. A second client that replays another
|
|
* client's legacy_session_id in a full handshake but sends no certificate must
|
|
* not inherit that client's peer certificate chain from the cache entry. */
|
|
int test_tls13_legacy_session_id_peer_chain(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(WOLFSSL_TICKET_HAVE_ID) && !defined(NO_SESSION_CACHE) && \
|
|
defined(SESSION_CERTS) && defined(KEEP_PEER_CERT) && \
|
|
!defined(NO_RSA) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_X509 *peer = NULL;
|
|
byte legacyId[ID_LEN];
|
|
int i;
|
|
|
|
/* legacy_session_id that both clients put in their ClientHello */
|
|
for (i = 0; i < ID_LEN; i++)
|
|
legacyId[i] = (byte)(0xA0 + i);
|
|
|
|
/* Server with optional client authentication */
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
|
|
/* --- Step 1: client that authenticates with a certificate --- */
|
|
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, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
|
|
if (ssl_c != NULL) {
|
|
XMEMCPY(ssl_c->session->sessionID, legacyId, ID_LEN);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
/* Only a resuming client sends its session ID. */
|
|
ssl_c->options.resuming = 1;
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s));
|
|
wolfSSL_X509_free(peer);
|
|
peer = NULL;
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
|
|
/* --- Step 2: client without a certificate replaying the same ID --- */
|
|
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, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
|
|
if (ssl_c != NULL) {
|
|
XMEMCPY(ssl_c->session->sessionID, legacyId, ID_LEN);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
/* Only a resuming client sends its session ID. */
|
|
ssl_c->options.resuming = 1;
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
/* No certificate was received on this connection so none may be
|
|
* reported, in particular not the one from step 1. */
|
|
ExpectNull(peer = wolfSSL_get_peer_certificate(ssl_s));
|
|
ExpectIntEQ(wolfSSL_get_chain_count(wolfSSL_get_peer_chain(ssl_s)), 0);
|
|
|
|
wolfSSL_X509_free(peer);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_clear_preserves_psk_dhe(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES) && \
|
|
(defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) && \
|
|
!defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_no_dhe_psk(ctx), 0);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(ssl->options.noPskDheKe, 1);
|
|
ExpectIntEQ(ssl->options.noPskDheKePolicy, 1);
|
|
|
|
/* SSL reuse must preserve the CTX-level noPskDheKe; resetting to 0
|
|
* would silently re-enable psk_dhe_ke for the next handshake. */
|
|
ExpectIntEQ(wolfSSL_clear(ssl), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->options.noPskDheKe, 1);
|
|
ExpectIntEQ(ssl->options.noPskDheKePolicy, 1);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
(defined(BUILD_TLS_AES_128_GCM_SHA256) || \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384) || \
|
|
defined(BUILD_TLS_CHACHA20_POLY1305_SHA256) || \
|
|
defined(BUILD_TLS_AES_128_CCM_SHA256) || \
|
|
defined(BUILD_TLS_AES_128_CCM_8_SHA256))
|
|
/* One iteration of the AEAD fuzz test: run a fresh handshake
|
|
* up to the point where the first AEAD-protected record from the side under
|
|
* test sits in the receiver's input buffer, flip one random byte of the
|
|
* encrypted payload to a random non-zero value, and confirm the receiver
|
|
* fails with VERIFY_MAC_ERROR. side==0 fuzzes the server's first encrypted
|
|
* record (EncryptedExtensions, read by the client). side==1 fuzzes the
|
|
* client's first encrypted record (Finished, read by the server). */
|
|
static int test_tls13_cipher_fuzz_once(WC_RNG* rng,
|
|
const char* cipher, int side)
|
|
{
|
|
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 *buf = NULL;
|
|
int buf_len = 0;
|
|
int rec_off = 0;
|
|
int rec_len = 0;
|
|
int fuzz_off;
|
|
byte fuzz_xor;
|
|
word32 rand32;
|
|
int ret;
|
|
int err;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_ctx.c_ciphers = cipher;
|
|
test_ctx.s_ciphers = cipher;
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* Drive the handshake forward until the side being fuzzed has written
|
|
* its first AEAD-encrypted record into the peer's read buffer. The
|
|
* server's first encrypted record is queued after its first
|
|
* wolfSSL_accept() (EncryptedExtensions, immediately following
|
|
* ServerHello). The client's first encrypted record is queued once
|
|
* wolfSSL_connect() returns success and the client has sent its
|
|
* Finished. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* The default groups can lead the server to respond with a
|
|
* HelloRetryRequest, in which case it is waiting on a new ClientHello and
|
|
* has not yet sent any encrypted record. Drive another connect/accept round
|
|
* so the buffers hold the real flight before fuzzing. */
|
|
if (EXPECT_SUCCESS() && test_memio_msg_is_hello_retry_request(&test_ctx)) {
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
}
|
|
if (side == 1) {
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
buf = test_ctx.s_buff;
|
|
buf_len = test_ctx.s_len;
|
|
}
|
|
else {
|
|
buf = test_ctx.c_buff;
|
|
buf_len = test_ctx.c_len;
|
|
}
|
|
|
|
/* Walk the TLS records in the target buffer and locate the first
|
|
* application_data record (content type 0x17), which holds the first
|
|
* encrypted handshake message. Plaintext records (ServerHello,
|
|
* ChangeCipherSpec for middlebox compatibility) precede it and must be
|
|
* skipped over. */
|
|
if (EXPECT_SUCCESS()) {
|
|
int off = 0;
|
|
while (off + 5 <= buf_len) {
|
|
int this_len = ((int)buf[off + 3] << 8) | (int)buf[off + 4];
|
|
if (buf[off] == 0x17) {
|
|
rec_off = off;
|
|
rec_len = this_len;
|
|
break;
|
|
}
|
|
off += 5 + this_len;
|
|
}
|
|
}
|
|
ExpectIntGT(rec_len, 0);
|
|
ExpectIntLE(rec_off + 5 + rec_len, buf_len);
|
|
|
|
/* Pick a random offset within the encrypted payload (skipping the
|
|
* 5-byte record header) and XOR it with a non-zero value so the byte
|
|
* is guaranteed to change. */
|
|
if (EXPECT_SUCCESS()) {
|
|
rand32 = 0;
|
|
fuzz_off = 0;
|
|
ExpectIntEQ(wc_RNG_GenerateBlock(rng, (byte*)&rand32,
|
|
sizeof(rand32)), 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
fuzz_off = rec_off + 5 + (int)(rand32 % (word32)rec_len);
|
|
}
|
|
do {
|
|
ExpectIntEQ(wc_RNG_GenerateByte(rng, &fuzz_xor), 0);
|
|
} while (EXPECT_SUCCESS() && fuzz_xor == 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
buf[fuzz_off] ^= fuzz_xor;
|
|
}
|
|
}
|
|
|
|
/* Drive the receiving side. It must report VERIFY_MAC_ERROR - the
|
|
* corrupted cipher text or tag must surface as a hard error. */
|
|
if (EXPECT_SUCCESS()) {
|
|
if (side == 1) {
|
|
ret = wolfSSL_accept(ssl_s);
|
|
err = wolfSSL_get_error(ssl_s, ret);
|
|
}
|
|
else {
|
|
ret = wolfSSL_connect(ssl_c);
|
|
err = wolfSSL_get_error(ssl_c, ret);
|
|
}
|
|
ExpectIntEQ(ret, WOLFSSL_FATAL_ERROR);
|
|
ExpectTrue((err == WC_NO_ERR_TRACE(VERIFY_MAC_ERROR)) ||
|
|
(err == WC_NO_ERR_TRACE(AES_GCM_AUTH_E)) ||
|
|
(err == WC_NO_ERR_TRACE(AES_CCM_AUTH_E)));
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Run 5 fuzz iterations per side for a single cipher suite. */
|
|
static int test_tls13_cipher_fuzz_cs(WC_RNG* rng, const char* cipher)
|
|
{
|
|
EXPECT_DECLS;
|
|
int side;
|
|
int iter;
|
|
|
|
for (side = 0; side < 2 && EXPECT_SUCCESS(); side++) {
|
|
for (iter = 0; iter < 5 && EXPECT_SUCCESS(); iter++) {
|
|
int _r = test_tls13_cipher_fuzz_once(rng, cipher, side);
|
|
if (_r != TEST_SUCCESS) {
|
|
fprintf(stderr, "FAIL cipher=%s side=%d iter=%d\n",
|
|
cipher, side, iter);
|
|
}
|
|
ExpectIntEQ(_r, TEST_SUCCESS);
|
|
}
|
|
}
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* Each per-cipher-suite test below runs the fuzz body (test_tls13_cipher_fuzz_cs)
|
|
* against a single AEAD cipher: it flips a random byte of the first encrypted
|
|
* record on each side of a TLS 1.3 handshake and expects the receiver to fail
|
|
* authentication. AEAD authentication makes it cryptographically infeasible
|
|
* for any single-byte change in the ciphertext or tag to leave authentication
|
|
* intact, so the receiver must report a hard auth error. */
|
|
|
|
int test_tls13_cipher_fuzz_aes128_gcm_sha256(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256)
|
|
WC_RNG rng;
|
|
int rngInit = 0;
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
if (EXPECT_SUCCESS())
|
|
rngInit = 1;
|
|
|
|
ExpectIntEQ(test_tls13_cipher_fuzz_cs(&rng, "TLS13-AES128-GCM-SHA256"),
|
|
TEST_SUCCESS);
|
|
|
|
if (rngInit)
|
|
wc_FreeRng(&rng);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_cipher_fuzz_aes256_gcm_sha384(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384)
|
|
WC_RNG rng;
|
|
int rngInit = 0;
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
if (EXPECT_SUCCESS())
|
|
rngInit = 1;
|
|
|
|
ExpectIntEQ(test_tls13_cipher_fuzz_cs(&rng, "TLS13-AES256-GCM-SHA384"),
|
|
TEST_SUCCESS);
|
|
|
|
if (rngInit)
|
|
wc_FreeRng(&rng);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_cipher_fuzz_chacha20_poly1305_sha256(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(BUILD_TLS_CHACHA20_POLY1305_SHA256)
|
|
WC_RNG rng;
|
|
int rngInit = 0;
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
if (EXPECT_SUCCESS())
|
|
rngInit = 1;
|
|
|
|
ExpectIntEQ(test_tls13_cipher_fuzz_cs(&rng,
|
|
"TLS13-CHACHA20-POLY1305-SHA256"), TEST_SUCCESS);
|
|
|
|
if (rngInit)
|
|
wc_FreeRng(&rng);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_cipher_fuzz_aes128_ccm_sha256(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(BUILD_TLS_AES_128_CCM_SHA256)
|
|
WC_RNG rng;
|
|
int rngInit = 0;
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
if (EXPECT_SUCCESS())
|
|
rngInit = 1;
|
|
|
|
ExpectIntEQ(test_tls13_cipher_fuzz_cs(&rng, "TLS13-AES128-CCM-SHA256"),
|
|
TEST_SUCCESS);
|
|
|
|
if (rngInit)
|
|
wc_FreeRng(&rng);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_cipher_fuzz_aes128_ccm_8_sha256(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(BUILD_TLS_AES_128_CCM_8_SHA256)
|
|
WC_RNG rng;
|
|
int rngInit = 0;
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
if (EXPECT_SUCCESS())
|
|
rngInit = 1;
|
|
|
|
ExpectIntEQ(test_tls13_cipher_fuzz_cs(&rng, "TLS13-AES128-CCM-8-SHA256"),
|
|
TEST_SUCCESS);
|
|
|
|
if (rngInit)
|
|
wc_FreeRng(&rng);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Regression test for the AEAD record-protection limit constants in
|
|
* internal.h. The macros expand to w64From32(hi, lo). A prior version split
|
|
* the intended 32-bit constants into 16-bit halves and passed each half as
|
|
* a separate 32-bit argument, producing a 64-bit value many orders of
|
|
* magnitude larger than RFC 8446 / RFC 9147 require. That made
|
|
* CheckTLS13AEADSendLimit's key-update trigger effectively unreachable.
|
|
* Compare against the hard-coded spec values so a recurrence is caught even
|
|
* if the macro is reused on both sides of the comparison. */
|
|
int test_tls13_AEAD_limit_macros(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)
|
|
w64wrapper limit;
|
|
|
|
/* RFC 8446 5.5: 2^24.5 ~= 23726566 (0x016A09E6). */
|
|
limit = AEAD_AES_LIMIT;
|
|
ExpectIntEQ(w64GetHigh32(limit), 0);
|
|
ExpectIntEQ(w64GetLow32(limit), 0x016A09E6);
|
|
|
|
#ifdef WOLFSSL_DTLS13
|
|
/* RFC 9147 (AES-CCM integrity): 2^23.5 ~= 11863283 (0x00B504F3). */
|
|
limit = DTLS_AEAD_AES_CCM_FAIL_LIMIT;
|
|
ExpectIntEQ(w64GetHigh32(limit), 0);
|
|
ExpectIntEQ(w64GetLow32(limit), 0x00B504F3);
|
|
|
|
/* Key-update threshold is half the fail limit: 5931641 (0x005A8279). */
|
|
limit = DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT;
|
|
ExpectIntEQ(w64GetHigh32(limit), 0);
|
|
ExpectIntEQ(w64GetLow32(limit), 0x005A8279);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
(defined(BUILD_TLS_AES_128_GCM_SHA256) || \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384) || \
|
|
defined(BUILD_TLS_AES_128_CCM_SHA256) || \
|
|
defined(BUILD_TLS_AES_128_CCM_8_SHA256))
|
|
/* Drive the client's encrypt sequence number towards the spec limit for
|
|
* `suite` and verify CheckTLS13AEADSendLimit's KeyUpdate trigger fires at
|
|
* exactly the right boundary.
|
|
*
|
|
* Two writes are exercised:
|
|
* 1. Counter set to limit - 2. After the write the counter must read
|
|
* limit - 1 (record incremented it by 1) and no KeyUpdate must have
|
|
* been emitted. CheckTLS13AEADSendLimit uses `seq >= limit`, so neither
|
|
* the pre-send check nor the trailing loop check (which runs once more
|
|
* after the last record before wolfSSL_write exits) is allowed to fire.
|
|
* 2. A second write follows with the counter already sitting at limit - 1
|
|
* from the previous record. The user record goes out at seq = limit-1,
|
|
* which bumps the counter to limit; the trailing limit check then
|
|
* fires SendTls13KeyUpdate. SetKeysSide zeroes the encrypt counter, so
|
|
* the post-write counter is 0.
|
|
*
|
|
* With the previous broken AEAD-limit macros the limit was unreachable, no
|
|
* KeyUpdate would ever fire, and the counter would simply advance to
|
|
* limit_lo + 1 in the second case instead of being reset.
|
|
*
|
|
* The AEAD nonce mixes in the record sequence number on both sides, so the
|
|
* server's decrypt counter has to be advanced in lockstep with the client's
|
|
* encrypt counter or the record fails the integrity check. */
|
|
static int test_tls13_AEAD_limit_triggers_KeyUpdate_cs(const char* suite,
|
|
word32 limit_hi, word32 limit_lo, int expected_bulk_cipher)
|
|
{
|
|
EXPECT_DECLS;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
const char msg[] = "post-limit-record";
|
|
char buf[sizeof(msg)];
|
|
int written;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_ctx.c_ciphers = suite;
|
|
test_ctx.s_ciphers = suite;
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL && ssl_s != NULL) {
|
|
/* Sanity check: the negotiated bulk cipher matches what the caller
|
|
* intends to exercise. If a build flag combination falls through to
|
|
* a different suite, the limit constant would be wrong. */
|
|
ExpectIntEQ(ssl_c->specs.bulk_cipher_algorithm, expected_bulk_cipher);
|
|
|
|
/* Stage the counters two below the limit so the first write stays
|
|
* comfortably below the trigger threshold. */
|
|
ssl_c->keys.sequence_number_hi = limit_hi;
|
|
ssl_c->keys.sequence_number_lo = limit_lo - 2;
|
|
ssl_s->keys.peer_sequence_number_hi = limit_hi;
|
|
ssl_s->keys.peer_sequence_number_lo = limit_lo - 2;
|
|
}
|
|
|
|
/* First write: below the limit, no KeyUpdate expected. */
|
|
written = wolfSSL_write(ssl_c, msg, (int)sizeof(msg));
|
|
ExpectIntEQ(written, (int)sizeof(msg));
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* The record bumped the counter from limit-2 to limit-1. A
|
|
* KeyUpdate would have zeroed it via SetKeysSide and bumped to 1. */
|
|
ExpectIntEQ((int)ssl_c->keys.sequence_number_hi, (int)limit_hi);
|
|
ExpectIntEQ(ssl_c->keys.sequence_number_lo, limit_lo - 1);
|
|
}
|
|
|
|
/* Server consumes the below-limit record with its existing keys. */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, (int)sizeof(buf)), (int)sizeof(msg));
|
|
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
|
|
|
|
/* Second write: the client's counter is now at limit-1. Sending this
|
|
* record will push it to limit, at which point the trailing check
|
|
* inside SendData's loop fires SendTls13KeyUpdate. No manual counter
|
|
* adjustment is needed -- the counter is allowed to "naturally" reach
|
|
* the limit through the previous send. */
|
|
written = wolfSSL_write(ssl_c, msg, (int)sizeof(msg));
|
|
ExpectIntEQ(written, (int)sizeof(msg));
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* SendTls13KeyUpdate -> DeriveTls13Keys -> SetKeysSide zeroes the
|
|
* encrypt sequence number. The user record went out before the
|
|
* trigger fired, so no record was sent on the new keys. */
|
|
ExpectIntEQ((int)ssl_c->keys.sequence_number_hi, 0);
|
|
ExpectIntEQ((int)ssl_c->keys.sequence_number_lo, 0);
|
|
}
|
|
|
|
/* The server reads the user record (sent under the pre-update keys at
|
|
* seq = limit - 1) before it sees the KeyUpdate record. The KeyUpdate
|
|
* is consumed transparently on a subsequent read; for the test we just
|
|
* need to confirm the user data round-trips. */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
{
|
|
int r = -1, attempts;
|
|
for (attempts = 0; attempts < 5; attempts++) {
|
|
r = wolfSSL_read(ssl_s, buf, (int)sizeof(buf));
|
|
if (r > 0)
|
|
break;
|
|
if (wolfSSL_get_error(ssl_s, r) != WOLFSSL_ERROR_WANT_READ)
|
|
break;
|
|
}
|
|
ExpectIntEQ(r, (int)sizeof(msg));
|
|
}
|
|
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
int test_tls13_AEAD_limit_KU_aes128_gcm_sha256(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256)
|
|
ExpectIntEQ(test_tls13_AEAD_limit_triggers_KeyUpdate_cs(
|
|
"TLS13-AES128-GCM-SHA256", 0, 0x016A09E6, wolfssl_aes_gcm),
|
|
TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_AEAD_limit_KU_aes256_gcm_sha384(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384)
|
|
ExpectIntEQ(test_tls13_AEAD_limit_triggers_KeyUpdate_cs(
|
|
"TLS13-AES256-GCM-SHA384", 0, 0x016A09E6, wolfssl_aes_gcm),
|
|
TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_AEAD_limit_KU_aes128_ccm_sha256(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(BUILD_TLS_AES_128_CCM_SHA256)
|
|
ExpectIntEQ(test_tls13_AEAD_limit_triggers_KeyUpdate_cs(
|
|
"TLS13-AES128-CCM-SHA256", 0, 0x016A09E6, wolfssl_aes_ccm),
|
|
TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_AEAD_limit_KU_aes128_ccm_8_sha256(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(BUILD_TLS_AES_128_CCM_8_SHA256)
|
|
ExpectIntEQ(test_tls13_AEAD_limit_triggers_KeyUpdate_cs(
|
|
"TLS13-AES128-CCM-8-SHA256", 0, 0x016A09E6, wolfssl_aes_ccm),
|
|
TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 9846 Section 4.7.3: a sending implementation MUST NOT allow its number of
|
|
* key updates to exceed 2^48-1. Establish a TLS 1.3 connection and exercise both
|
|
* sides of the boundary: one update seeded just below the ceiling must succeed
|
|
* and advance the sender count to exactly 2^48-1, and a further update once the
|
|
* count has reached the ceiling must be refused with BAD_STATE_E without
|
|
* advancing the count. The count is inspected directly because
|
|
* SendTls13KeyUpdate enforces the limit before touching the transport. */
|
|
int test_tls13_KeyUpdate_sender_limit(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
w64wrapper ceiling;
|
|
|
|
ceiling = w64From32(TLS13_KEY_UPDATE_MAX_HI32, TLS13_KEY_UPDATE_MAX_LO32);
|
|
|
|
/* One below the ceiling: the update is allowed and must advance the
|
|
* sender count by one, reaching exactly 2^48-1. */
|
|
ssl_c->keys.keyUpdateCount = w64From32(TLS13_KEY_UPDATE_MAX_HI32,
|
|
TLS13_KEY_UPDATE_MAX_LO32 - 1);
|
|
ExpectIntEQ(wolfSSL_update_keys(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectTrue(w64Equal(ssl_c->keys.keyUpdateCount, ceiling));
|
|
|
|
/* At the ceiling: a further update must be refused rather than
|
|
* advancing the count past 2^48-1. */
|
|
ExpectIntEQ(wolfSSL_update_keys(ssl_c), WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
ExpectTrue(w64Equal(ssl_c->keys.keyUpdateCount, ceiling));
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The other user_canceled tests inject a plaintext record while the client has
|
|
* only sent its ClientHello, so DoProcessAlertRecord() never sees the alert
|
|
* arrive over the encrypted channel with keys.decryptedCur set. That is the
|
|
* case RFC 9846 Section 6.1 is actually about: a peer cancels after the
|
|
* handshake, and the receiver "SHOULD continue to read data from the peer
|
|
* until a 'close_notify' is received".
|
|
*
|
|
* Drive it end to end: complete the handshake, have the server send
|
|
* user_canceled over the established connection, and confirm the client reads
|
|
* past it and the connection still carries data in both directions.
|
|
*
|
|
* wolfSSL_SendUserCanceled() emits the alert at warning level, which is what
|
|
* a conforming peer sends; no public API produces a fatal-level
|
|
* user_canceled, so the level-independence of the exemption is covered by
|
|
* test_tls13_user_canceled_fatal_level instead. */
|
|
int test_tls13_user_canceled_encrypted(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
const char msg[] = "survives-user-canceled";
|
|
char buf[sizeof(msg)];
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Application data first, then the cancel. On the wire the client now has
|
|
* three encrypted records queued: data, user_canceled, close_notify. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
/* Return value is deliberately not asserted: under
|
|
* WOLFSSL_ERROR_CODE_OPENSSL a shutdown still awaiting the peer's
|
|
* close_notify reports 0, which is also WOLFSSL_FAILURE. The reads
|
|
* below are the real proof that both alerts went out. */
|
|
(void)wolfSSL_SendUserCanceled(ssl_s);
|
|
|
|
/* The data behind the alert is delivered normally. */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, (int)sizeof(buf)), (int)sizeof(msg));
|
|
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* Records are being decrypted, so the alert really did arrive over
|
|
* the encrypted channel rather than as a plaintext record. */
|
|
ExpectIntEQ(ssl_c->keys.decryptedCur, 1);
|
|
ExpectIntEQ(ssl_c->options.isClosed, 0);
|
|
}
|
|
|
|
/* The next read walks past user_canceled and stops at close_notify - the
|
|
* "keep reading until close_notify" behaviour, rather than failing on the
|
|
* cancel itself. */
|
|
ExpectIntLE(wolfSSL_read(ssl_c, buf, (int)sizeof(buf)), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_ZERO_RETURN);
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* Proof it got past the cancel: the last alert seen is the one
|
|
* behind it. */
|
|
ExpectIntEQ(ssl_c->alert_history.last_rx.code, close_notify);
|
|
ExpectIntEQ(ssl_c->options.isClosed, 0);
|
|
}
|
|
|
|
/* The session is not invalidated either: the TLS 1.3 exemption covers
|
|
* eviction as well as teardown, and this connection has genuinely
|
|
* negotiated TLS 1.3 so it is the case RFC 9846 6.1 describes. Removing it
|
|
* now must find it still cached. */
|
|
#if !defined(NO_SESSION_CACHE) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_c, sess), 1);
|
|
wolfSSL_SESSION_free(sess);
|
|
#endif
|
|
(void)sess;
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
/* The DoAlert() rework split isClosed and session invalidation into explicit
|
|
* TLS 1.3 and pre-1.3 branches. The other new tests only drive the TLS 1.3
|
|
* side, so a regression that stopped a TLS 1.2 fatal alert from closing the
|
|
* connection or evicting the session would go unnoticed.
|
|
*
|
|
* Over TLS 1.2 the AlertLevel byte does carry meaning, so the outcome is the
|
|
* opposite of the TLS 1.3 case: the connection closes and the session is
|
|
* evicted.
|
|
*
|
|
* wolfSSL_SSL_CTX_remove_session() returns 0 both for "the alert evicted it"
|
|
* and for "it was never cached", so asserting 0 on its own would pass
|
|
* vacuously. The flow therefore runs twice on independent contexts: pass 0
|
|
* injects nothing and must find the session still cached (1), pass 1 injects
|
|
* the alert and must find it gone (0). The control pass is what makes the 0
|
|
* meaningful. */
|
|
int test_tls12_fatal_alert_closes_and_evicts(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_SESSION_CACHE)
|
|
/* Same alert as the TLS 1.3 test: fatal level, user_canceled(90). */
|
|
static const unsigned char fatalUserCanceled[] =
|
|
{ 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x5a };
|
|
int pass;
|
|
|
|
for (pass = 0; pass < 2 && !EXPECT_FAIL(); pass++) {
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Resume, so options.resuming is set: InvalidateSessionOnFatalAlert()
|
|
* returns early on a connection that is neither done nor resuming. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->options.resuming, 1);
|
|
}
|
|
|
|
if (pass == 1) {
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char *)fatalUserCanceled,
|
|
sizeof(fatalUserCanceled)), 0);
|
|
/* In TLS 1.2 the fatal level is authoritative: connection dies. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntNE(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->options.isClosed, 1);
|
|
ExpectIntEQ(ssl_c->options.fatalAlert, 1);
|
|
}
|
|
}
|
|
|
|
/* Pass 0 proves the session is cached at this point in the flow;
|
|
* pass 1 proves the alert removed it. */
|
|
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_c, sess),
|
|
(pass == 0) ? 1 : 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 Section 6.2: "Whenever an implementation encounters a fatal
|
|
* error condition, it SHOULD send an appropriate fatal alert and MUST
|
|
* close the connection without sending or receiving any additional data."
|
|
*
|
|
* This drives the client into sending a fatal unexpected_message alert the
|
|
* same way test_tls13_post_handshake_auth_no_ext() does, then checks that a
|
|
* subsequent wolfSSL_write() is refused rather than queuing a new
|
|
* application data record on the connection it just tore down. */
|
|
int test_tls13_no_app_data_after_fatal_alert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
char readBuf[8];
|
|
char msg[] = "should never be sent";
|
|
int c_len_after_alert;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_no_ticket_TLSv13(ssl_s), 0);
|
|
|
|
/* Intentionally do NOT call wolfSSL_allow_post_handshake_auth() on the
|
|
* client so the post_handshake_auth extension is omitted from the
|
|
* ClientHello. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Force the server to send an unsolicited post-handshake
|
|
* CertificateRequest, as in test_tls13_post_handshake_auth_no_ext(). */
|
|
if (ssl_s != NULL)
|
|
ssl_s->options.postHandshakeAuth = 1;
|
|
ExpectIntEQ(wolfSSL_clear_group_messages(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntGT(test_ctx.c_len, 0);
|
|
|
|
/* The client rejects it and transmits a fatal unexpected_message
|
|
* alert - the connection is now closed. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(OUT_OF_ORDER_E));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, unexpected_message);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
if (ssl_c != NULL)
|
|
ExpectIntEQ(ssl_c->options.isClosed, 1);
|
|
|
|
c_len_after_alert = test_ctx.c_len;
|
|
|
|
/* Clear the error the failed read left behind so the write's own
|
|
* SOCKET_PEER_CLOSED_E can be told apart from it. */
|
|
if (ssl_c != NULL)
|
|
ssl_c->error = WOLFSSL_ERROR_NONE;
|
|
|
|
/* A write attempted after the fatal alert must be refused instead of
|
|
* building and queuing a new application data record. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
if (ssl_c != NULL)
|
|
ExpectIntEQ(ssl_c->error, WC_NO_ERR_TRACE(SOCKET_PEER_CLOSED_E));
|
|
ExpectIntNE(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), 0);
|
|
/* No new record was appended to what the alert itself already sent. */
|
|
ExpectIntEQ(test_ctx.c_len, c_len_after_alert);
|
|
|
|
/* The server receiving that alert must stop writing too. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, (int)sizeof(readBuf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->options.fatalAlert, 1);
|
|
ssl_s->error = WOLFSSL_ERROR_NONE;
|
|
}
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
if (ssl_s != NULL)
|
|
ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(SOCKET_PEER_CLOSED_E));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 Section 6.2: every TLS 1.3 error alert is fatal, whatever its
|
|
* level byte says. */
|
|
int test_tls13_warning_error_alert_is_fatal(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* level=warning(0x01), code=handshake_failure(0x28) */
|
|
static const unsigned char warnHandshakeFailure[] =
|
|
{ 0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x28 };
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
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);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char *)warnHandshakeFailure, sizeof(warnHandshakeFailure)), 0);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntNE(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->options.isClosed, 1);
|
|
ExpectIntEQ(ssl_c->options.fatalAlert, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A failed peer certificate check is a fatal error, so it blocks writes. */
|
|
int test_tls13_cert_failure_sets_fatal_alert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
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_PEER, NULL);
|
|
ExpectIntEQ(wolfSSL_check_domain_name(ssl_c, "bad.example.com"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->options.isClosed, 1);
|
|
ExpectIntEQ(ssl_c->options.fatalAlert, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
static int ConnCloseRecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)sz;
|
|
(void)ctx;
|
|
return WOLFSSL_CBIO_ERR_CONN_CLOSE;
|
|
}
|
|
#endif
|
|
|
|
/* A transport EOF without a fatal alert must not block writes; nginx answers
|
|
* a request whose client already closed its socket. */
|
|
int test_tls13_write_after_transport_close(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char readBuf[32];
|
|
char msg[] = "response";
|
|
int c_len_before;
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (ssl_s != NULL)
|
|
wolfSSL_SSLSetIORecv(ssl_s, ConnCloseRecv);
|
|
ExpectIntLE(wolfSSL_read(ssl_s, readBuf, (int)sizeof(readBuf)), 0);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->options.isClosed, 1);
|
|
ExpectIntEQ(ssl_s->options.fatalAlert, 0);
|
|
}
|
|
|
|
c_len_before = test_ctx.c_len;
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)), (int)sizeof(msg));
|
|
ExpectIntGT(test_ctx.c_len, c_len_before);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
|
|
(int)sizeof(msg));
|
|
ExpectStrEQ(readBuf, msg);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* After our close_notify writes are refused but the peer's data still reads */
|
|
int test_tls13_no_write_after_close_notify(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char readBuf[32];
|
|
char msg[] = "response";
|
|
char req[] = "request";
|
|
int c_len_before;
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
c_len_before = test_ctx.c_len;
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(SSL_SHUTDOWN_ALREADY_DONE_E));
|
|
ExpectIntEQ(test_ctx.c_len, c_len_before);
|
|
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, req, (int)sizeof(req)), (int)sizeof(req));
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, (int)sizeof(readBuf)),
|
|
(int)sizeof(req));
|
|
ExpectStrEQ(readBuf, req);
|
|
|
|
if (ssl_s != NULL)
|
|
wolfSSL_SSLSetIORecv(ssl_s, ConnCloseRecv);
|
|
ExpectIntLE(wolfSSL_read(ssl_s, readBuf, (int)sizeof(readBuf)), 0);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->options.isClosed, 1);
|
|
ExpectIntEQ(ssl_s->options.fatalAlert, 0);
|
|
}
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(test_ctx.c_len, c_len_before);
|
|
|
|
ExpectIntLE(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)), 0);
|
|
if (ssl_c != NULL)
|
|
ExpectIntEQ(ssl_c->options.closeNotify, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A write refused after our close_notify must not fail the shutdown. */
|
|
int test_tls13_shutdown_after_refused_write(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "response";
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
ExpectIntEQ(wolfSSL_shutdown(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();
|
|
}
|
|
|
|
int test_tls13_no_handshake_after_user_canceled(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "request";
|
|
int s_len_before;
|
|
|
|
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);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
(void)wolfSSL_SendUserCanceled(ssl_c);
|
|
if (ssl_c != NULL)
|
|
ExpectIntEQ(ssl_c->options.sentNotify, 1);
|
|
s_len_before = test_ctx.s_len;
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(SSL_SHUTDOWN_ALREADY_DONE_E));
|
|
ExpectIntEQ(test_ctx.s_len, s_len_before);
|
|
if (ssl_c != NULL)
|
|
ExpectIntEQ(ssl_c->options.handShakeDone, 0);
|
|
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_ctx.s_len, s_len_before);
|
|
if (ssl_c != NULL)
|
|
ExpectIntEQ(ssl_c->options.handShakeDone, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_no_key_update_after_close_notify(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_RW_THREADED) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char readBuf[32];
|
|
char msg[] = "response";
|
|
int c_len_before;
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_update_keys(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, (int)sizeof(readBuf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
if (ssl_s != NULL)
|
|
ExpectIntEQ(ssl_s->options.sendKeyUpdate, 1);
|
|
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
c_len_before = test_ctx.c_len;
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(SSL_SHUTDOWN_ALREADY_DONE_E));
|
|
ExpectIntEQ(test_ctx.c_len, c_len_before);
|
|
if (ssl_s != NULL)
|
|
ExpectIntEQ(ssl_s->options.sendKeyUpdate, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_no_key_update_response_after_close_notify(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char readBuf[32];
|
|
char msg[] = "after update";
|
|
int c_len_before;
|
|
int useShutdown;
|
|
|
|
for (useShutdown = 0; useShutdown < 2 && EXPECT_SUCCESS(); useShutdown++) {
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
ExpectIntEQ(wolfSSL_update_keys(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
c_len_before = test_ctx.c_len;
|
|
if (useShutdown) {
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
}
|
|
else {
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, (int)sizeof(readBuf)),
|
|
(int)sizeof(msg));
|
|
ExpectBufEQ(readBuf, msg, sizeof(msg));
|
|
}
|
|
ExpectIntEQ(test_ctx.c_len, c_len_before);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_no_pha_response_after_close_notify(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char readBuf[32];
|
|
char msg[] = "after request";
|
|
int s_len_before;
|
|
|
|
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);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)), (int)sizeof(msg));
|
|
s_len_before = test_ctx.s_len;
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
|
|
(int)sizeof(msg));
|
|
ExpectBufEQ(readBuf, msg, sizeof(msg));
|
|
ExpectIntEQ(test_ctx.s_len, s_len_before);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->options.handShakeState, HANDSHAKE_DONE);
|
|
ExpectNull(ssl_c->certReqCtx);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_write_dup_no_response_after_close_notify(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_WRITE_DUP) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL *ssl_w = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char readBuf[32];
|
|
char msg[] = "dup message";
|
|
int s_len_before;
|
|
|
|
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);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNotNull(ssl_w = wolfSSL_write_dup(ssl_c));
|
|
|
|
/* Drain the session tickets so the write side's shutdown reads nothing */
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)), (int)sizeof(msg));
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_w), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
|
|
ExpectIntEQ(wolfSSL_update_keys(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)), (int)sizeof(msg));
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
|
|
(int)sizeof(msg));
|
|
ExpectBufEQ(readBuf, msg, sizeof(msg));
|
|
|
|
s_len_before = test_ctx.s_len;
|
|
ExpectIntEQ(wolfSSL_write(ssl_w, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_w, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(SSL_SHUTDOWN_ALREADY_DONE_E));
|
|
ExpectIntEQ(test_ctx.s_len, s_len_before);
|
|
|
|
wolfSSL_free(ssl_w);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_no_renegotiation_after_close_notify(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
WOLFSSL *closer;
|
|
WOLFSSL *peer;
|
|
struct test_memio_ctx test_ctx;
|
|
char readBuf[32];
|
|
int *closerOutLen;
|
|
int lenBefore;
|
|
int serverCloses;
|
|
int mode;
|
|
|
|
/* 0: client closes, 1: server closes, 2: WOLFSSL_OP_NO_RENEGOTIATION too */
|
|
for (mode = 0; mode < 3 && EXPECT_SUCCESS(); mode++) {
|
|
serverCloses = (mode > 0);
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s, wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS);
|
|
if (mode == 2)
|
|
wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_RENEGOTIATION);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
closer = serverCloses ? ssl_s : ssl_c;
|
|
peer = serverCloses ? ssl_c : ssl_s;
|
|
closerOutLen = serverCloses ? &test_ctx.c_len : &test_ctx.s_len;
|
|
|
|
/* HelloRequest to a closed client, or ClientHello to a closed server */
|
|
ExpectIntEQ(wolfSSL_shutdown(closer), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
ExpectIntEQ(wolfSSL_Rehandshake(peer),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
lenBefore = *closerOutLen;
|
|
ExpectIntEQ(wolfSSL_read(closer, readBuf, (int)sizeof(readBuf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(closer,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(*closerOutLen, lenBefore);
|
|
if (closer != NULL)
|
|
ExpectIntEQ(closer->options.handShakeState, HANDSHAKE_DONE);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_refused_write_after_peek(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char buf[32];
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS);
|
|
/* The ticket leaves the positive WOLFSSL_ERROR_WANT_READ in ssl->error */
|
|
ExpectIntEQ(wolfSSL_peek(ssl_c, buf, (int)sizeof(buf)), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, buf, (int)sizeof(buf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(SSL_SHUTDOWN_ALREADY_DONE_E));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_SNI) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* arg is { callback return, alert to set or -1 to leave ad unset } */
|
|
static int test_sni_alert_cb(WOLFSSL* ssl, int* ad, void* arg)
|
|
{
|
|
const int* cfg = (const int*)arg;
|
|
|
|
(void)ssl;
|
|
if (cfg[1] >= 0)
|
|
*ad = cfg[1];
|
|
return cfg[0];
|
|
}
|
|
#endif
|
|
|
|
int test_tls_sni_callback_alerts(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_SNI) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "hello";
|
|
char readBuf[16];
|
|
int cfg[2];
|
|
int i;
|
|
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_EXTRA_ALERTS)
|
|
const int defaultAlert = unrecognized_name;
|
|
#else
|
|
const int defaultAlert = handshake_failure;
|
|
#endif
|
|
/* tls13, callback return, ad (-1 unset), handshake succeeds */
|
|
static const int cases[][4] = {
|
|
{ 1, warning_return, unrecognized_name, 1 },
|
|
{ 0, warning_return, -1, 1 },
|
|
{ 1, fatal_return, -1, 0 },
|
|
{ 0, fatal_return, -1, 0 },
|
|
};
|
|
|
|
for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])) && EXPECT_SUCCESS();
|
|
i++) {
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s, cases[i][0] ? wolfTLSv1_3_client_method :
|
|
wolfTLSv1_2_client_method, cases[i][0] ?
|
|
wolfTLSv1_3_server_method : wolfTLSv1_2_server_method), 0);
|
|
cfg[0] = cases[i][1];
|
|
cfg[1] = cases[i][2];
|
|
wolfSSL_CTX_set_servername_callback(ctx_s, test_sni_alert_cb);
|
|
ExpectIntEQ(wolfSSL_CTX_set_servername_arg(ctx_s, cfg),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME,
|
|
"example.com", (word16)XSTRLEN("example.com")), WOLFSSL_SUCCESS);
|
|
|
|
if (cases[i][3]) {
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
|
|
(int)sizeof(msg));
|
|
}
|
|
else {
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->alert_history.last_rx.level, alert_fatal);
|
|
ExpectIntEQ(ssl_c->alert_history.last_rx.code, defaultAlert);
|
|
}
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 9846 Section 4.7.3, write-duplicate path. The read side of a write dup
|
|
* cannot send, so a peer KeyUpdate(update_requested) is delegated to the write
|
|
* side via dupWrite->keyUpdateRespond and sent from
|
|
* wolfssl_write_dup_do_tls13_work(). That route bypassed the cap check in
|
|
* DoTls13KeyUpdate, so at the ceiling Tls13UpdateKeys() still returned
|
|
* BAD_STATE_E and the next wolfSSL_write() failed the connection - the exact
|
|
* outcome the "ignore the update_requested flag" rule forbids.
|
|
*
|
|
* The cap is checked on the write side because the two sides are separate
|
|
* WOLFSSL objects with separate keys; only the write side sends KeyUpdates, so
|
|
* only its counter is meaningful. */
|
|
int test_tls13_KeyUpdate_limit_writedup(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_WRITE_DUP) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL, *ssl_w = NULL;
|
|
const char msg[] = "after-ignored-delegated-request";
|
|
w64wrapper ceiling;
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* ssl_c becomes read-only; ssl_w is the write side. */
|
|
ExpectNotNull(ssl_w = wolfSSL_write_dup(ssl_c));
|
|
|
|
ceiling = w64From32(TLS13_KEY_UPDATE_MAX_HI32, TLS13_KEY_UPDATE_MAX_LO32);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_w != NULL) {
|
|
/* Write side has exhausted its key update budget, and the read side
|
|
* has delegated a response to it. */
|
|
ssl_w->keys.keyUpdateCount = ceiling;
|
|
ssl_w->dupWrite->keyUpdateRespond = 1;
|
|
}
|
|
|
|
/* The write settles the delegated work first. It must drop the response
|
|
* rather than fail. Before the fix this returned WOLFSSL_FATAL_ERROR and
|
|
* left BAD_STATE_E on the write-side object. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_w, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
|
|
if (EXPECT_SUCCESS() && ssl_w != NULL) {
|
|
ExpectTrue(w64Equal(ssl_w->keys.keyUpdateCount, ceiling));
|
|
ExpectIntEQ(ssl_w->keys.keyUpdateRespond, 0);
|
|
ExpectIntEQ(ssl_w->dupWrite->keyUpdateRespond, 0);
|
|
}
|
|
|
|
/* The application data still arrives, so the connection survived. */
|
|
if (EXPECT_SUCCESS()) {
|
|
char buf[sizeof(msg)];
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, (int)sizeof(buf)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_w);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The AEAD limit check runs at the top of every SendData() record loop
|
|
* iteration, including the one after the last record. A write whose final
|
|
* record takes the last available slot therefore meets the check on the way
|
|
* out, with sent == sz - a complete write. It must not leave
|
|
* TOO_MUCH_EARLY_DATA behind: wolfSSL_get_error() would call a fully
|
|
* successful write a failure, and ReceiveData() refuses to read on an object
|
|
* with a stored error.
|
|
*
|
|
* Leave room for exactly one application-data record and write a single
|
|
* record's worth, so the write completes and takes the last slot the
|
|
* early-data budget has to give - the one after it stays reserved for
|
|
* EndOfEarlyData. */
|
|
int test_tls13_early_data_AEAD_limit_exact(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
const char earlyMsg[] = "exactly-at-the-limit";
|
|
char buf[64];
|
|
int written = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_ctx.c_ciphers = test_ctx.s_ciphers = "TLS13-AES128-GCM-SHA256";
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_ctx.c_ciphers = test_ctx.s_ciphers = "TLS13-AES128-GCM-SHA256";
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
/* Drive the ClientHello out and leave early data in flight. */
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c, "x", 1,
|
|
&written), 1);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* Two below the AES-GCM limit of 2^24.5. The record below is the last
|
|
* application-data record allowed: the final slot stays reserved for
|
|
* the EndOfEarlyData that an accepted 0-RTT exchange still owes. */
|
|
ssl_c->keys.sequence_number_hi = 0;
|
|
ssl_c->keys.sequence_number_lo = 0x016A09E6 - 2;
|
|
}
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_HAVE_ERROR_QUEUE)
|
|
/* Start from a clean queue so the check below is about this write. */
|
|
wolfSSL_ERR_clear_error();
|
|
#endif
|
|
|
|
/* Complete write: full count, and no error left on the object. */
|
|
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, earlyMsg, (int)sizeof(earlyMsg),
|
|
&written), (int)sizeof(earlyMsg));
|
|
ExpectIntEQ(written, (int)sizeof(earlyMsg));
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->error, 0);
|
|
}
|
|
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_HAVE_ERROR_QUEUE)
|
|
/* Nothing queued either: the limit check runs on the way out of a
|
|
* completed write, and an entry left there would surface from
|
|
* ERR_get_error() as a failure the caller never had. */
|
|
ExpectIntEQ(wolfSSL_ERR_peek_error(), 0);
|
|
#endif
|
|
|
|
/* The budget really is exhausted, so the next attempt fails cleanly. */
|
|
written = 0;
|
|
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, earlyMsg, (int)sizeof(earlyMsg),
|
|
&written), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(TOO_MUCH_EARLY_DATA));
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Companion to test_tls13_early_data_AEAD_limit: a write that would cross the
|
|
* limit part way through must fail outright rather than report a short count.
|
|
*
|
|
* SendData()'s non-partialWrite contract is all-or-error, and
|
|
* wolfSSL_get_error(ssl, ret) reports nothing for a positive ret, so a short
|
|
* count would be indistinguishable from a complete write to a caller following
|
|
* the documented contract - it would silently drop the tail.
|
|
*
|
|
* Leave room for exactly one application-data record - the slot after it is
|
|
* reserved for EndOfEarlyData - then write more than one record's worth. The
|
|
* first record goes out and the second trips the check, which is the partial
|
|
* case: the call must still be refused outright, with nothing reported as
|
|
* sent. */
|
|
int test_tls13_early_data_AEAD_limit_partial(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
/* Comfortably more than one record, so the write must split. */
|
|
static byte earlyMsg[20000];
|
|
char buf[64];
|
|
int written = 0;
|
|
int first = 0;
|
|
|
|
XMEMSET(earlyMsg, 'E', sizeof(earlyMsg));
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_ctx.c_ciphers = test_ctx.s_ciphers = "TLS13-AES128-GCM-SHA256";
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, sizeof(earlyMsg) * 2), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_ctx.c_ciphers = test_ctx.s_ciphers = "TLS13-AES128-GCM-SHA256";
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, sizeof(earlyMsg) * 2), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
/* Drive the ClientHello out and leave early data in flight. */
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c, "x", 1,
|
|
&written), 1);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* Two below the AES-GCM limit of 2^24.5. One record still fits - the
|
|
* slot after it is reserved for EndOfEarlyData - so the first record
|
|
* of this multi-record write goes out and the second trips the
|
|
* check, which is the partial case under test. */
|
|
ssl_c->keys.sequence_number_hi = 0;
|
|
ssl_c->keys.sequence_number_lo = 0x016A09E6 - 2;
|
|
}
|
|
|
|
/* Refused outright, reported through the conventional idiom, and nothing
|
|
* claimed as written. */
|
|
first = wolfSSL_write_early_data(ssl_c, earlyMsg, (int)sizeof(earlyMsg),
|
|
&written);
|
|
ExpectIntEQ(first, WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, first),
|
|
WC_NO_ERR_TRACE(TOO_MUCH_EARLY_DATA));
|
|
ExpectIntEQ(written, 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 9846 Section 4.3, text with no counterpart in RFC 8446: "Unless
|
|
* otherwise specified, trailing data is forbidden. ... When processing an
|
|
* extension, receivers MUST abort the handshake with a 'decode_error' alert if
|
|
* there is data left over after parsing the structure."
|
|
*
|
|
* The extension parsers do detect malformed structures, but a third of them
|
|
* report it as the wolfCrypt BUFFER_E rather than BUFFER_ERROR, and only
|
|
* BUFFER_ERROR was mapped to an alert. TranslateErrorToAlert returned
|
|
* invalid_alert for BUFFER_E, and every caller skips SendAlert when the
|
|
* translation is invalid_alert - so the handshake aborted with nothing on the
|
|
* wire and the peer saw only a dropped connection.
|
|
*
|
|
* Inject a ServerHello whose pre_shared_key body is 3 bytes rather than the
|
|
* required 2. TLSX_PreSharedKey_Parse rejects that with BUFFER_E before it
|
|
* looks at whether the client offered the extension, so this reaches the
|
|
* mapping. The client must send decode_error. */
|
|
int test_tls13_extension_trailing_data_alert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && \
|
|
(defined(HAVE_SESSION_TICKET) || !defined(NO_PSK))
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
/* Handshake record holding a ServerHello with supported_versions (TLS 1.3)
|
|
* and a pre_shared_key whose body is one byte too long. */
|
|
static const unsigned char badPskSh[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x39, /* record: handshake, len 57 */
|
|
0x02, 0x00, 0x00, 0x35, /* server_hello, len 53 */
|
|
0x03, 0x03, /* legacy_version */
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* random[32] */
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x00, /* legacy_session_id, empty */
|
|
0x13, 0x01, /* TLS_AES_128_GCM_SHA256 */
|
|
0x00, /* legacy_compression_method */
|
|
0x00, 0x0d, /* extensions, 13 bytes */
|
|
0x00, 0x2b, 0x00, 0x02, 0x03, 0x04, /* supported_versions = TLS 1.3 */
|
|
0x00, 0x29, 0x00, 0x03, /* pre_shared_key, 3-byte body */
|
|
0xaa, 0xbb, 0xcc /* ...must be exactly 2 */
|
|
};
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
|
|
wolfTLSv1_3_client_method, NULL), 0);
|
|
|
|
/* Client sends ClientHello, then waits for the server response. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char *)badPskSh, sizeof(badPskSh)), 0);
|
|
|
|
/* The handshake must fail... */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntNE(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* ...and it must say so on the wire, with decode_error. Before the fix the
|
|
* BUFFER_E from the parser mapped to invalid_alert and nothing was sent. */
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, decode_error);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 9846 Section 4.7.3: "If a sending implementation receives a KeyUpdate
|
|
* with request_update set to 'update_requested', it MUST NOT send its own
|
|
* KeyUpdate if that would cause it to exceed these limits and SHOULD instead
|
|
* ignore the 'update_requested' flag."
|
|
*
|
|
* Park the client's key update count on the 2^48-1 ceiling, have the server
|
|
* request an update, and confirm the client ignores the request and keeps the
|
|
* connection usable rather than failing. Before the fix DoTls13KeyUpdate
|
|
* called SendTls13KeyUpdate unconditionally, which refuses at the ceiling with
|
|
* BAD_STATE_E and tore the connection down.
|
|
*
|
|
* An application-initiated wolfSSL_update_keys() at the ceiling must still
|
|
* report BAD_STATE_E - the "ignore" rule is specific to responding to a peer's
|
|
* update_requested. That case is covered by
|
|
* test_tls13_KeyUpdate_sender_limit above. */
|
|
int test_tls13_KeyUpdate_limit_ignores_update_requested(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
const char msg[] = "after-ignored-request";
|
|
char buf[sizeof(msg)];
|
|
w64wrapper ceiling;
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ceiling = w64From32(TLS13_KEY_UPDATE_MAX_HI32, TLS13_KEY_UPDATE_MAX_LO32);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* Client has exhausted its key update budget. */
|
|
ssl_c->keys.keyUpdateCount = ceiling;
|
|
}
|
|
|
|
/* Server asks the client to update. A fresh KeyUpdate from the server has
|
|
* request_update set, since it has neither an outstanding request of its
|
|
* own nor a pending response to make. */
|
|
ExpectIntEQ(wolfSSL_update_keys(ssl_s), WOLFSSL_SUCCESS);
|
|
|
|
/* Client processes the KeyUpdate. It must not error out. */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
ExpectIntLT(wolfSSL_read(ssl_c, buf, (int)sizeof(buf)), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* The request was ignored, not answered: no KeyUpdate was sent, so the
|
|
* count is untouched and no response is left pending. */
|
|
ExpectTrue(w64Equal(ssl_c->keys.keyUpdateCount, ceiling));
|
|
ExpectIntEQ(ssl_c->keys.keyUpdateRespond, 0);
|
|
ExpectIntEQ(ssl_c->options.isClosed, 0);
|
|
}
|
|
|
|
/* The connection still works in both directions. The server reads with the
|
|
* keys it already had, since the client never rekeyed its send side. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)), (int)sizeof(msg));
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, (int)sizeof(buf)), (int)sizeof(msg));
|
|
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)), (int)sizeof(msg));
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, (int)sizeof(buf)), (int)sizeof(msg));
|
|
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 9846 Section 5.5: "it is not possible to perform a KeyUpdate for early
|
|
* data; therefore, implementations MUST NOT exceed the limits when sending
|
|
* early data."
|
|
*
|
|
* Resume with 0-RTT, park the client's encrypt sequence number on the AES-GCM
|
|
* limit while early data is still in flight, and write again. The write must
|
|
* fail. Before the fix CheckTLS13AEADSendLimit called Tls13UpdateKeys here
|
|
* regardless, emitting a KeyUpdate in the CLIENT_HELLO_COMPLETE state - before
|
|
* the handshake had finished, where the peer must reject it. */
|
|
int test_tls13_early_data_AEAD_limit(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
|
|
defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
const char earlyMsg[] = "early-data-at-limit";
|
|
char buf[64];
|
|
int written = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_ctx.c_ciphers = test_ctx.s_ciphers = "TLS13-AES128-GCM-SHA256";
|
|
|
|
/* Step 1: full handshake to obtain a ticket that permits early data. */
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Pump the NewSessionTicket through to the client. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Step 2: resume with early data. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_ctx.c_ciphers = test_ctx.s_ciphers = "TLS13-AES128-GCM-SHA256";
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
/* The first early-data write also drives the ClientHello out, leaving the
|
|
* client in CLIENT_HELLO_COMPLETE with early data still in flight. */
|
|
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c, earlyMsg,
|
|
(int)sizeof(earlyMsg), &written), (int)sizeof(earlyMsg));
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* Still mid-early-data, so no KeyUpdate is possible from here. */
|
|
ExpectIntNE(ssl_c->earlyData, no_early_data);
|
|
ExpectIntNE(ssl_c->earlyData, done_early_data);
|
|
ExpectIntEQ(ssl_c->specs.bulk_cipher_algorithm, wolfssl_aes_gcm);
|
|
|
|
/* Park the encrypt counter on the AES-GCM limit of 2^24.5. */
|
|
ssl_c->keys.sequence_number_hi = 0;
|
|
ssl_c->keys.sequence_number_lo = 0x016A09E6;
|
|
}
|
|
|
|
/* Limit reached with no way to rekey: the write must fail rather than
|
|
* send a KeyUpdate. */
|
|
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, earlyMsg,
|
|
(int)sizeof(earlyMsg), &written), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(TOO_MUCH_EARLY_DATA));
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 9846 Section 6.1 exempts "user_canceled" from tearing a TLS 1.3
|
|
* connection down whatever AlertLevel was used. That only applies once the
|
|
* connection really is running TLS 1.3.
|
|
*
|
|
* Neither obvious flag proves that before the ServerHello arrives:
|
|
* ssl->version holds the highest version offered, and options.tls1_3 is set
|
|
* straight from the session by wolfSSL_set_session(). A client resuming a TLS
|
|
* 1.3 session therefore has both set while the server may still choose TLS
|
|
* 1.2, so exempting on either would swallow that peer's fatal alert and leave
|
|
* the caller waiting for a handshake that never comes.
|
|
*
|
|
* Both shapes are driven here - a plain TLS 1.3 client, and one that has
|
|
* loaded a TLS 1.3 session so options.tls1_3 is already set - and a fatal
|
|
* user_canceled arriving before negotiation must terminate either way. The
|
|
* exemption itself is covered on a completed connection by
|
|
* test_tls13_user_canceled_encrypted. */
|
|
int test_tls13_user_canceled_fatal_level(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_SESSION_CACHE) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
|
|
/* TLS record: content_type=alert(0x15), version=TLS1.2(0x0303), len=2,
|
|
* level=fatal(0x02), code=user_canceled(0x5a=90) */
|
|
static const unsigned char fatalUserCanceled[] =
|
|
{ 0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x5a };
|
|
int resuming;
|
|
|
|
for (resuming = 0; resuming < 2 && !EXPECT_FAIL(); resuming++) {
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
char buf[64];
|
|
|
|
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);
|
|
|
|
if (resuming) {
|
|
/* A completed handshake first, so there is a TLS 1.3 session to
|
|
* load on the connection under test. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
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);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* Client sends ClientHello, then waits for the server response. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
/* Loading the session already claims TLS 1.3 even though nothing
|
|
* has been negotiated - the state this test exists to guard. */
|
|
ExpectIntEQ(ssl_c->options.tls1_3, resuming ? 1 : 0);
|
|
ExpectIntEQ(ssl_c->keys.decryptedCur, 0);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char *)fatalUserCanceled, sizeof(fatalUserCanceled)), 0);
|
|
|
|
/* The version is not settled, so the alert is acted on. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntNE(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->options.isClosed, 1);
|
|
}
|
|
|
|
/* Recorded either way. RFC 9846 6.1 permits a receiver to "log or
|
|
* otherwise record" the alert. */
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_rx.code, user_canceled);
|
|
ExpectIntEQ(h.last_rx.level, alert_fatal);
|
|
|
|
if (resuming) {
|
|
/* Treated as a pre-1.3 fatal alert, so the session goes too. */
|
|
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_c, sess), 0);
|
|
wolfSSL_SESSION_free(sess);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
|
|
defined(HAVE_CERTIFICATE_STATUS_REQUEST) && defined(HAVE_OCSP) && \
|
|
defined(KEEP_PEER_CERT) && \
|
|
!defined(NO_CERTS) && !defined(NO_RSA) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* Mock OCSP I/O callback that yields no response. It lets stapling be enabled
|
|
* without contacting a real responder, but may not even be invoked (the test
|
|
* certs need not advertise an OCSP responder URL). Either way no staple is
|
|
* produced, which is the "no staple available" path this test exercises. */
|
|
static int test_pha_ocsp_io_cb(void* ioCtx, const char* url, int urlSz,
|
|
unsigned char* req, int reqSz, unsigned char** resp)
|
|
{
|
|
(void)ioCtx; (void)url; (void)urlSz; (void)req; (void)reqSz;
|
|
*resp = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static void test_pha_ocsp_resp_free_cb(void* ioCtx, unsigned char* resp)
|
|
{
|
|
(void)ioCtx; (void)resp;
|
|
}
|
|
#endif
|
|
|
|
/* Post-Handshake Authentication combined with OCSP stapling
|
|
* (status_request) on the TLS 1.3 CertificateRequest message.
|
|
*
|
|
* Regression for two related issues:
|
|
* 1. The server's PHA CertificateRequest must include the
|
|
* status_request extension (with an empty body) when the client
|
|
* offered status_request in its ClientHello (RFC 8446 4.2 / 4.3.2).
|
|
* Without the fix the extension was suppressed and the size and
|
|
* write paths disagreed by 4 bytes, causing the client to send a
|
|
* decode_error alert (BUFFER_ERROR / -328).
|
|
* 2. The server-side OCSP-status check in ProcessPeerCerts must run
|
|
* for the PHA-received client Certificate. The check must also
|
|
* tolerate a missing/empty stapled response unless the verifier
|
|
* enforces must-staple, per RFC 8446 4.4.2.1 (client OCSP staple
|
|
* is MAY) and RFC 7633 (must-staple). Without the fix the server
|
|
* either skipped the check entirely or returned
|
|
* BAD_CERTIFICATE_STATUS_ERROR (-406) for the no-staple case.
|
|
*
|
|
* The test exercises a single TLS 1.3 connection: an initial handshake
|
|
* without client authentication, followed by a server-initiated PHA
|
|
* exchange. The server expects to receive (and verify) the client
|
|
* certificate even though no OCSP staple is supplied.
|
|
*
|
|
* KEEP_PEER_CERT is part of the guard because the check for the received
|
|
* client certificate uses wolfSSL_get_peer_certificate(), which is only
|
|
* built when that macro is defined.
|
|
*/
|
|
int test_tls13_pha_status_request(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
|
|
defined(HAVE_CERTIFICATE_STATUS_REQUEST) && defined(HAVE_OCSP) && \
|
|
defined(KEEP_PEER_CERT) && \
|
|
!defined(NO_CERTS) && !defined(NO_RSA) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_X509 *peer = NULL;
|
|
const char msg[] = "ping";
|
|
char buf[8];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
/* --- Client CTX ------------------------------------------------ */
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
/* Must opt in to PHA before wolfSSL_connect to add the
|
|
* post_handshake_auth extension to the ClientHello. */
|
|
ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(ctx_c), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(ctx_c), WOLFSSL_SUCCESS);
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
|
|
/* --- Server CTX ------------------------------------------------ */
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
/* Trust the client cert issuer as well, otherwise the PHA
|
|
* Certificate verification would fail with ASN_SELF_SIGNED_E. */
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s,
|
|
"./certs/client-ca.pem", 0), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(ctx_s), WOLFSSL_SUCCESS);
|
|
/* Mock callback: stapling negotiates but the response is empty. */
|
|
ExpectIntEQ(wolfSSL_CTX_SetOCSP_Cb(ctx_s, test_pha_ocsp_io_cb,
|
|
test_pha_ocsp_resp_free_cb, NULL), WOLFSSL_SUCCESS);
|
|
/* Initial handshake: do not request the client certificate yet -
|
|
* the server promotes verification only when triggering PHA. */
|
|
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
|
|
/* --- SSL objects ----------------------------------------------- */
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
/* Causes status_request in the ClientHello so that the server's
|
|
* PHA CertificateRequest re-emits the same extension. */
|
|
ExpectIntEQ(wolfSSL_UseOCSPStapling(ssl_c, WOLFSSL_CSR_OCSP, 0),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
|
|
/* --- Initial handshake (no client cert requested) -------------- */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNull(wolfSSL_get_peer_certificate(ssl_s));
|
|
|
|
/* --- Trigger PHA: server now requires the client certificate -- */
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_set_verify(ssl_s,
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
|
NULL);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* The server's wolfSSL_write below carries the PHA
|
|
* CertificateRequest record. The client's wolfSSL_read consumes
|
|
* the request, transmits Certificate/CertificateVerify/Finished
|
|
* and surfaces the application data to us. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
|
|
/* The client's reply lets the server's wolfSSL_read drain the
|
|
* incoming PHA Certificate flight before the application data. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf) - 1),
|
|
(int)sizeof(msg) - 1);
|
|
|
|
/* PHA succeeded: the server now holds the client certificate.
|
|
* Reaching this point also implies the server tolerated the
|
|
* empty OCSP staple instead of failing with -406. */
|
|
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s));
|
|
wolfSSL_X509_free(peer);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 7748 Section 5: a receiver of a 32-byte X25519 u-coordinate MUST mask
|
|
* (clear) the reserved high bit of the final byte rather than reject it.
|
|
* Craft a ClientHello whose key_share entry has that bit set and confirm
|
|
* the server masks it and proceeds, instead of aborting the handshake with
|
|
* ECC_PEERKEY_ERROR the way wc_curve25519_check_public() alone would.
|
|
*/
|
|
int test_tls13_x25519_keyshare_masks_reserved_bit(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_CURVE25519) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(HAVE_ECC) || !defined(NO_RSA))
|
|
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_buf[4096];
|
|
const char* ch_bytes = NULL;
|
|
int ch_sz = 0;
|
|
int i;
|
|
int keShareOff = -1;
|
|
int ret;
|
|
|
|
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);
|
|
|
|
#if defined(HAVE_ECC)
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#else
|
|
ExpectTrue(wolfSSL_use_certificate_file(ssl_s, svrCertFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
ExpectTrue(wolfSSL_use_PrivateKey_file(ssl_s, svrKeyFile,
|
|
CERT_FILETYPE) == WOLFSSL_SUCCESS);
|
|
#endif
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
/* Force an X25519 key share so the ClientHello carries a key_share
|
|
* entry we can find by its fixed group id (0x001D) and length
|
|
* (0x0020) markers, without parsing the full extension list. */
|
|
do {
|
|
ret = wolfSSL_UseKeyShare(ssl_c, WOLFSSL_ECC_X25519);
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
|
|
wolfSSL_AsyncPoll(ssl_c, WOLF_POLL_FLAG_CHECK_HW);
|
|
#endif
|
|
} while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
|
|
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Pull the ClientHello out of the buffer the server will read from
|
|
* (client=0), tamper with the X25519 public value in place, then feed
|
|
* the modified message back in. */
|
|
ExpectIntEQ(test_memio_get_message(&test_ctx, 0, &ch_bytes, &ch_sz, 0),
|
|
0);
|
|
ExpectTrue(ch_sz > 0 && ch_sz <= (int)sizeof(ch_buf));
|
|
if (ch_sz > 0 && ch_sz <= (int)sizeof(ch_buf)) {
|
|
XMEMCPY(ch_buf, ch_bytes, (size_t)ch_sz);
|
|
|
|
for (i = 0; i + 4 + CURVE25519_KEYSIZE <= ch_sz; i++) {
|
|
if (ch_buf[i] == 0x00 && ch_buf[i + 1] == 0x1D &&
|
|
ch_buf[i + 2] == 0x00 &&
|
|
ch_buf[i + 3] == CURVE25519_KEYSIZE) {
|
|
keShareOff = i;
|
|
break;
|
|
}
|
|
}
|
|
ExpectIntGE(keShareOff, 0);
|
|
if (keShareOff >= 0) {
|
|
/* Set the reserved high bit of the final wire byte of the
|
|
* public value -- the bit RFC 7748 requires masking. */
|
|
ch_buf[keShareOff + 4 + CURVE25519_KEYSIZE - 1] |= 0x80;
|
|
}
|
|
}
|
|
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
if (keShareOff >= 0) {
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)ch_buf, ch_sz), 0);
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
#ifdef WOLFSSL_X25519_NO_MASK_PEER
|
|
/* Masking is compiled out: wc_curve25519_check_public() still sees
|
|
* the reserved bit and rejects the key, so the server must abort the
|
|
* handshake with ECC_PEERKEY_ERROR instead of masking it. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(ECC_PEERKEY_ERROR));
|
|
#else
|
|
/* The bit is masked before the check, so the server accepts the
|
|
* share and moves on to building its response flight. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
#endif
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* A send callback that refuses only the client's Finished flush. */
|
|
static int test_tls13_block_finished_send_cb(WOLFSSL* ssl, char* data, int sz,
|
|
void* ctx)
|
|
{
|
|
if (ssl->options.connectState == FIRST_REPLY_FOURTH)
|
|
return WOLFSSL_CBIO_ERR_WANT_WRITE;
|
|
return test_memio_write_cb(ssl, data, sz, ctx);
|
|
}
|
|
#endif
|
|
|
|
/* Test that the handshake is not reported complete while the client's
|
|
* Finished is still unsent.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_tls13_is_init_finished_want_write(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int sMsgCount = 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);
|
|
|
|
if (ssl_c != NULL) {
|
|
wolfSSL_SSLSetIOSend(ssl_c, test_tls13_block_finished_send_cb);
|
|
}
|
|
|
|
/* The ClientHello goes out and the server replies with its flight. The
|
|
* client has sent nothing of its own final flight yet. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_is_init_finished(ssl_c), 0);
|
|
|
|
/* The client reads the server flight and builds its Finished, but the
|
|
* transport refuses the flush. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_WRITE);
|
|
if (ssl_c != NULL) {
|
|
/* The Finished is still ours, it sits in the output buffer and the
|
|
* connect state is still the one before the send. */
|
|
ExpectIntGT(ssl_c->buffers.outputBuffer.length, 0);
|
|
ExpectIntEQ(ssl_c->options.connectState, FIRST_REPLY_FOURTH);
|
|
}
|
|
/* The server has not received it either, so it is still waiting to read.
|
|
* Neither side of the transport has the client's Finished. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WOLFSSL_ERROR_WANT_READ);
|
|
/* The handshake is therefore not complete and must not be reported as
|
|
* complete. */
|
|
ExpectIntEQ(wolfSSL_is_init_finished(ssl_c), 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
sMsgCount = test_ctx.s_msg_count;
|
|
}
|
|
|
|
/* With the transport working again the flush gets the Finished out and
|
|
* the handshake really does complete. */
|
|
if (ssl_c != NULL) {
|
|
wolfSSL_SSLSetIOSend(ssl_c, test_memio_write_cb);
|
|
}
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_is_init_finished(ssl_c), 1);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->buffers.outputBuffer.length, 0);
|
|
ExpectIntEQ(ssl_c->options.connectState, FINISHED_DONE);
|
|
}
|
|
/* Exactly one more record reached the server, which is
|
|
* the Finished that had been buffered, not a rebuilt duplicate of it. */
|
|
ExpectIntEQ(test_ctx.s_msg_count, sMsgCount + 1);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLFSSL_ASYNC_REINVOKE) && \
|
|
defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
|
|
#define TEST_TLS13_CB_PEND_DEVID_C 1
|
|
#define TEST_TLS13_CB_PEND_DEVID_S 2
|
|
#define TEST_TLS13_CB_PEND_JOBS 64
|
|
|
|
/* Which class of operation the callback pends. */
|
|
enum TestTls13PendTarget {
|
|
TEST_TLS13_PEND_AESGCM,
|
|
TEST_TLS13_PEND_ECC_KEYGEN,
|
|
TEST_TLS13_PEND_ECDSA_SIGN,
|
|
TEST_TLS13_PEND_ECDSA_VERIFY,
|
|
TEST_TLS13_PEND_KDF,
|
|
TEST_TLS13_PEND_HMAC
|
|
};
|
|
|
|
typedef struct TestTls13PendCtx {
|
|
/* Job table keyed by request: first sight queues and pends, the
|
|
* identical re-invocation completes (falls through to software). */
|
|
unsigned long jobs[TEST_TLS13_CB_PEND_JOBS];
|
|
int jobCount;
|
|
int target; /* enum TestTls13PendTarget */
|
|
int pended; /* WC_PENDING_E results issued; asserted non-zero */
|
|
int seen; /* matching requests observed; asserted non-zero */
|
|
} TestTls13PendCtx;
|
|
|
|
static int TestTls13PendMatches(int target, wc_CryptoInfo* info)
|
|
{
|
|
int match = 0;
|
|
|
|
switch (target) {
|
|
#ifndef WOLF_CRYPTO_CB_ASYNC_POLL
|
|
/* With WOLF_CRYPTO_CB_ASYNC_POLL the record ciphers follow the
|
|
* poll-completion contract instead of re-invocation; that model is
|
|
* covered by tests/api/test_async.c. */
|
|
case TEST_TLS13_PEND_AESGCM:
|
|
match = (info->algo_type == WC_ALGO_TYPE_CIPHER) &&
|
|
(info->cipher.type == WC_CIPHER_AES_GCM);
|
|
break;
|
|
#endif
|
|
case TEST_TLS13_PEND_ECC_KEYGEN:
|
|
match = (info->algo_type == WC_ALGO_TYPE_PK) &&
|
|
(info->pk.type == WC_PK_TYPE_EC_KEYGEN);
|
|
break;
|
|
case TEST_TLS13_PEND_ECDSA_SIGN:
|
|
match = (info->algo_type == WC_ALGO_TYPE_PK) &&
|
|
(info->pk.type == WC_PK_TYPE_ECDSA_SIGN);
|
|
break;
|
|
case TEST_TLS13_PEND_ECDSA_VERIFY:
|
|
match = (info->algo_type == WC_ALGO_TYPE_PK) &&
|
|
(info->pk.type == WC_PK_TYPE_ECDSA_VERIFY);
|
|
break;
|
|
case TEST_TLS13_PEND_KDF:
|
|
match = (info->algo_type == WC_ALGO_TYPE_KDF);
|
|
break;
|
|
case TEST_TLS13_PEND_HMAC:
|
|
match = (info->algo_type == WC_ALGO_TYPE_HMAC);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
/* Request fingerprint over the whole info struct: op class, sizes, pointers
|
|
* and inline content distinguish interleaved requests. */
|
|
static unsigned long TestTls13PendHash(wc_CryptoInfo* info)
|
|
{
|
|
unsigned long h = 5381;
|
|
const unsigned char* b = (const unsigned char*)info;
|
|
size_t i;
|
|
|
|
for (i = 0; i < sizeof(*info); i++)
|
|
h = h * 33 + b[i];
|
|
return h;
|
|
}
|
|
|
|
static int TestTls13PendCb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
|
{
|
|
TestTls13PendCtx* c = (TestTls13PendCtx*)ctx;
|
|
unsigned long h;
|
|
int i;
|
|
|
|
(void)devIdArg;
|
|
|
|
if (info == NULL || c == NULL)
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
#if defined(HAVE_HKDF) && !defined(NO_HMAC) && !defined(HAVE_SELFTEST) && \
|
|
(!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0))
|
|
if (c->target == TEST_TLS13_PEND_HMAC &&
|
|
info->algo_type == WC_ALGO_TYPE_KDF) {
|
|
/* Serve the key schedule synchronously in software; falling back
|
|
* with the devId set would route its internal HMACs back here and
|
|
* those cannot resume (see wolfcrypt/src/hmac.c). Only the
|
|
* TLS-layer transcript HMACs are left to pend. */
|
|
if (info->kdf.type == WC_KDF_TYPE_HKDF_EXTRACT) {
|
|
return wc_HKDF_Extract_ex(info->kdf.hkdf_extract.hashType,
|
|
info->kdf.hkdf_extract.salt, info->kdf.hkdf_extract.saltSz,
|
|
info->kdf.hkdf_extract.inKey, info->kdf.hkdf_extract.inKeySz,
|
|
info->kdf.hkdf_extract.out, NULL, INVALID_DEVID);
|
|
}
|
|
if (info->kdf.type == WC_KDF_TYPE_HKDF_EXPAND) {
|
|
return wc_HKDF_Expand_ex(info->kdf.hkdf_expand.hashType,
|
|
info->kdf.hkdf_expand.inKey, info->kdf.hkdf_expand.inKeySz,
|
|
info->kdf.hkdf_expand.info, info->kdf.hkdf_expand.infoSz,
|
|
info->kdf.hkdf_expand.out, info->kdf.hkdf_expand.outSz,
|
|
NULL, INVALID_DEVID);
|
|
}
|
|
if (info->kdf.type == WC_KDF_TYPE_HKDF) {
|
|
return wc_HKDF_ex(info->kdf.hkdf.hashType,
|
|
info->kdf.hkdf.inKey, info->kdf.hkdf.inKeySz,
|
|
info->kdf.hkdf.salt, info->kdf.hkdf.saltSz,
|
|
info->kdf.hkdf.info, info->kdf.hkdf.infoSz,
|
|
info->kdf.hkdf.out, info->kdf.hkdf.outSz,
|
|
NULL, INVALID_DEVID);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!TestTls13PendMatches(c->target, info))
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
|
|
c->seen++;
|
|
h = TestTls13PendHash(info);
|
|
for (i = 0; i < c->jobCount; i++) {
|
|
if (c->jobs[i] == h) {
|
|
/* Re-invocation of a pended request: complete it. */
|
|
c->jobs[i] = c->jobs[--c->jobCount];
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
}
|
|
if (c->jobCount < TEST_TLS13_CB_PEND_JOBS) {
|
|
c->jobs[c->jobCount++] = h;
|
|
c->pended++;
|
|
return WC_PENDING_E;
|
|
}
|
|
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
|
|
/* wolfSSL_write()/wolfSSL_read() can return WC_PENDING_E just as the
|
|
* handshake does; drive them with the poll-and-retry loop the application is
|
|
* expected to use. Returns the byte count, or the error. */
|
|
static int TestTls13PendWrite(WOLFSSL* ssl, const char* buf, int sz)
|
|
{
|
|
int ret;
|
|
int err;
|
|
int rounds = 0;
|
|
|
|
do {
|
|
ret = wolfSSL_write(ssl, buf, sz);
|
|
if (ret > 0)
|
|
break;
|
|
err = wolfSSL_get_error(ssl, ret);
|
|
if (err == WC_NO_ERR_TRACE(WC_PENDING_E)) {
|
|
if (wolfSSL_AsyncPoll(ssl, WOLF_POLL_FLAG_CHECK_HW) < 0)
|
|
return -1;
|
|
}
|
|
else if (err != WOLFSSL_ERROR_WANT_READ &&
|
|
err != WOLFSSL_ERROR_WANT_WRITE) {
|
|
return ret;
|
|
}
|
|
} while (++rounds < 100);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int TestTls13PendRead(WOLFSSL* ssl, char* buf, int sz)
|
|
{
|
|
int ret;
|
|
int err;
|
|
int rounds = 0;
|
|
|
|
do {
|
|
ret = wolfSSL_read(ssl, buf, sz);
|
|
if (ret > 0)
|
|
break;
|
|
err = wolfSSL_get_error(ssl, ret);
|
|
if (err == WC_NO_ERR_TRACE(WC_PENDING_E)) {
|
|
if (wolfSSL_AsyncPoll(ssl, WOLF_POLL_FLAG_CHECK_HW) < 0)
|
|
return -1;
|
|
}
|
|
else if (err != WOLFSSL_ERROR_WANT_READ &&
|
|
err != WOLFSSL_ERROR_WANT_WRITE) {
|
|
return ret;
|
|
}
|
|
} while (++rounds < 100);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* One TLS 1.3 handshake with the given operation class pending on both sides,
|
|
* then application data both ways so records queued after the handshake are
|
|
* parsed by the peer as well. */
|
|
static int test_tls13_cryptocb_pend_one(int target, int mutual)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
TestTls13PendCtx cliCtx;
|
|
TestTls13PendCtx srvCtx;
|
|
struct test_memio_ctx memio;
|
|
const char msg[] = "hello over TLS 1.3";
|
|
char buf[64];
|
|
|
|
XMEMSET(&cliCtx, 0, sizeof(cliCtx));
|
|
XMEMSET(&srvCtx, 0, sizeof(srvCtx));
|
|
XMEMSET(&memio, 0, sizeof(memio));
|
|
cliCtx.target = target;
|
|
srvCtx.target = target;
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_TLS13_CB_PEND_DEVID_C,
|
|
TestTls13PendCb, &cliCtx), 0);
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_TLS13_CB_PEND_DEVID_S,
|
|
TestTls13PendCb, &srvCtx), 0);
|
|
|
|
/* devId set on the CTX before credentials and SSL objects so all of
|
|
* it inherits the devId. ECC credentials: an RSA key under a devId
|
|
* would be treated as device-held. */
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_SetDevId(ctx_c, TEST_TLS13_CB_PEND_DEVID_C),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_SetDevId(ctx_s, TEST_TLS13_CB_PEND_DEVID_S),
|
|
WOLFSSL_SUCCESS);
|
|
#ifdef HAVE_AESGCM
|
|
if (target == TEST_TLS13_PEND_AESGCM) {
|
|
/* Pin the AEAD so the pend assertion cannot depend on suite
|
|
* preference ordering. */
|
|
ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx_c,
|
|
"TLS13-AES128-GCM-SHA256"), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx_s,
|
|
"TLS13-AES128-GCM-SHA256"), WOLFSSL_SUCCESS);
|
|
}
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c,
|
|
"./certs/ca-ecc-cert.pem", 0), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_s,
|
|
"./certs/server-ecc.pem"), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, "./certs/ecc-key.pem",
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
if (mutual) {
|
|
/* Mutual auth: pending verifies of the client's chain and CV
|
|
* cover the received-marker restores. */
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_c,
|
|
"./certs/client-ecc-cert.pem"), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c,
|
|
"./certs/ecc-client-key.pem", WOLFSSL_FILETYPE_PEM),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s,
|
|
"./certs/client-ecc-cert.pem", 0), WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
}
|
|
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
|
|
/* Pin the group so every run negotiates the same way; the default offer
|
|
* can trigger HelloRetryRequest or a PQC key share, which are separate
|
|
* scenarios from the pend-resume paths this test covers. */
|
|
if (EXPECT_SUCCESS()) {
|
|
int groups[1];
|
|
groups[0] = WOLFSSL_ECC_SECP256R1;
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s, groups, 1), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* The shared memio transport; its TLS path serves a byte stream, so
|
|
* the re-read patterns of pending crypto operations cannot desync it. */
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SSLSetIORecv(ssl_c, test_memio_read_cb);
|
|
wolfSSL_SSLSetIOSend(ssl_c, test_memio_write_cb);
|
|
wolfSSL_SSLSetIORecv(ssl_s, test_memio_read_cb);
|
|
wolfSSL_SSLSetIOSend(ssl_s, test_memio_write_cb);
|
|
wolfSSL_SetIOReadCtx(ssl_c, &memio);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &memio);
|
|
wolfSSL_SetIOReadCtx(ssl_s, &memio);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &memio);
|
|
}
|
|
|
|
/* Generous rounds: pending every KDF request costs one poll-and-retry
|
|
* round trip per operation, and a TLS 1.3 handshake with session tickets
|
|
* runs a couple of hundred of them. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 600, NULL), 0);
|
|
|
|
/* Assert per-side pends so the test cannot pass without exercising a
|
|
* resume path; the client signs nothing unless mutual. */
|
|
ExpectIntGT(srvCtx.seen, 0);
|
|
ExpectIntGT(srvCtx.pended, 0);
|
|
if (mutual || target != TEST_TLS13_PEND_ECDSA_SIGN) {
|
|
ExpectIntGT(cliCtx.seen, 0);
|
|
ExpectIntGT(cliCtx.pended, 0);
|
|
}
|
|
|
|
ExpectIntEQ(TestTls13PendWrite(ssl_c, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
ExpectIntEQ(TestTls13PendRead(ssl_s, buf, (int)sizeof(buf)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
|
|
|
|
ExpectIntEQ(TestTls13PendWrite(ssl_s, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
ExpectIntEQ(TestTls13PendRead(ssl_c, buf, (int)sizeof(buf)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(XMEMCMP(buf, msg, sizeof(msg)), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wc_CryptoCb_UnRegisterDevice(TEST_TLS13_CB_PEND_DEVID_C);
|
|
wc_CryptoCb_UnRegisterDevice(TEST_TLS13_CB_PEND_DEVID_S);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#endif /* guards */
|
|
|
|
/* Drive TLS 1.3 handshakes and application data with the poll-and-retry
|
|
* loop while a crypto callback pends each matching request. */
|
|
int test_tls13_cryptocb_async(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLFSSL_ASYNC_REINVOKE) && \
|
|
defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
#if defined(HAVE_AESGCM) && !defined(WOLF_CRYPTO_CB_ASYNC_POLL)
|
|
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_AESGCM, 0),
|
|
TEST_SUCCESS);
|
|
#endif
|
|
#ifdef HAVE_ECC
|
|
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_ECC_KEYGEN, 0),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_ECDSA_SIGN, 0),
|
|
TEST_SUCCESS);
|
|
/* Mutual auth with pending verifies: regression for the msgsReceived
|
|
* marker restores (a pended Certificate/CertificateVerify replay used
|
|
* to leave its marker clear and fail Finished with OUT_OF_ORDER_E). */
|
|
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_ECDSA_VERIFY, 1),
|
|
TEST_SUCCESS);
|
|
#endif
|
|
#if defined(HAVE_HKDF) && !defined(NO_HMAC)
|
|
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_KDF, 0),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_KDF, 1),
|
|
TEST_SUCCESS);
|
|
#if !defined(HAVE_SELFTEST) && \
|
|
(!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0))
|
|
/* Transcript HMACs (Finished verify_data) pending on both sides. */
|
|
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_HMAC, 0),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_cryptocb_pend_one(TEST_TLS13_PEND_HMAC, 1),
|
|
TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
defined(WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* Drive a TLS 1.3 handshake up to, but not including, the server's final
|
|
* wolfSSL_accept() - the call that runs the NewSessionTicket loop. */
|
|
static int test_tls13_handshake_to_ticket(WOLFSSL* ssl_c, WOLFSSL* ssl_s)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
/* ClientHello. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
/* Server flight. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
/* Client Finished. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* RFC 9846 Section 4.3.9 and Section 4.7.1: a NewSessionTicket creates a
|
|
* resumption PSK, so the server may only send one when the ClientHello
|
|
* advertised a psk_key_exchange_modes mode it can be used with. */
|
|
int test_tls13_ticket_psk_modes(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
defined(WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* The wolfSSL client always advertises psk_key_exchange_modes, so drive
|
|
* the no-extension case by clearing the recorded flag after the
|
|
* handshake: a ClientHello without the extension gets no ticket. */
|
|
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);
|
|
ExpectIntEQ(test_tls13_handshake_to_ticket(ssl_c, ssl_s), TEST_SUCCESS);
|
|
ExpectIntEQ(ssl_s->options.pskKeModesRecvd, 1);
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_s->options.pskKeModesRecvd = 0;
|
|
}
|
|
ExpectIntEQ(test_ctx.c_len, 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_ctx.c_len, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
|
|
/* Control: the wolfSSL client advertises the modes, so a ticket is sent. */
|
|
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);
|
|
ExpectIntEQ(test_tls13_handshake_to_ticket(ssl_c, ssl_s), TEST_SUCCESS);
|
|
ExpectIntEQ(test_ctx.c_len, 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntGT(test_ctx.c_len, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* wolfSSL_send_SessionTicket() applies the same RFC 9846 precondition as the
|
|
* automatic ticket path, and reports why it will not send. */
|
|
int test_tls13_send_session_ticket_psk_modes(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
defined(WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
/* No psk_key_exchange_modes extension in the ClientHello. */
|
|
ssl_s->options.pskKeModesRecvd = 0;
|
|
}
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s),
|
|
WC_NO_ERR_TRACE(MISSING_HANDSHAKE_DATA));
|
|
if (EXPECT_SUCCESS()) {
|
|
/* Extension present but carrying only unrecognized modes. */
|
|
ssl_s->options.pskKeModesRecvd = 1;
|
|
ssl_s->options.pskKeModes = 0;
|
|
}
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s),
|
|
WC_NO_ERR_TRACE(PSK_KEY_ERROR));
|
|
if (EXPECT_SUCCESS()) {
|
|
/* psk_dhe_ke only, but the server refuses (EC)DHE with PSK. Set
|
|
* through the public API - the handshake clears noPskDheKe, so the
|
|
* decision has to read the configured policy. */
|
|
ssl_s->options.pskKeModes = 1 << PSK_DHE_KE;
|
|
}
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_s), 0);
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s),
|
|
WC_NO_ERR_TRACE(PSK_KEY_ERROR));
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_s->options.noPskDheKePolicy = 0;
|
|
}
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS);
|
|
#ifdef HAVE_SUPPORTED_CURVES
|
|
if (EXPECT_SUCCESS()) {
|
|
/* psk_ke only, but the server requires (EC)DHE with PSK. */
|
|
ssl_s->options.pskKeModes = 1 << PSK_KE;
|
|
}
|
|
ExpectIntEQ(wolfSSL_only_dhe_psk(ssl_s), 0);
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s),
|
|
WC_NO_ERR_TRACE(PSK_KEY_ERROR));
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_s->options.onlyPskDheKe = 0;
|
|
}
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS);
|
|
#endif
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A psk_ke handshake, where preMasterSz is 0, must leave no handshake secret
|
|
* behind in preMasterSecret. */
|
|
int test_tls13_hs_secret_zeroized_psk_ke(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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 zeros[MAX_PREMASTER_SZ];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(zeros, 0, sizeof(zeros));
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
|
|
/* No key_share, so preMasterSz is zero when DeriveHandshakeSecret runs. */
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_s), 0);
|
|
|
|
wolfSSL_KeepArrays(ssl_c);
|
|
wolfSSL_KeepArrays(ssl_s);
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(ssl_c->arrays->preMasterSecret, 0xAA, MAX_PREMASTER_SZ);
|
|
XMEMSET(ssl_s->arrays->preMasterSecret, 0xAA, MAX_PREMASTER_SZ);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_c->options.noPskDheKe, 1);
|
|
ExpectIntEQ(ssl_c->arrays->preMasterSz, 0);
|
|
|
|
ExpectIntEQ(XMEMCMP(ssl_c->arrays->preMasterSecret, zeros,
|
|
MAX_PREMASTER_SZ), 0);
|
|
ExpectIntEQ(XMEMCMP(ssl_s->arrays->preMasterSecret, zeros,
|
|
MAX_PREMASTER_SZ), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* Build a NewSessionTicket handshake message carrying the given extensions
|
|
* block verbatim. Returns the message length, including the handshake
|
|
* header, or -1 when it does not fit. */
|
|
static int test_tls13_make_nst(byte* out, int outSz, const byte* exts,
|
|
int extsSz)
|
|
{
|
|
static const byte body[] = {
|
|
0x00, 0x00, 0x0e, 0x10, /* ticket_lifetime: 3600 */
|
|
0x01, 0x02, 0x03, 0x04, /* ticket_age_add */
|
|
0x01, 0x00, /* ticket_nonce<1> */
|
|
0x00, 0x04, 0xde, 0xad, 0xbe, 0xef /* ticket<4> */
|
|
};
|
|
int len = (int)sizeof(body) + OPAQUE16_LEN + extsSz;
|
|
|
|
if (outSz < HANDSHAKE_HEADER_SZ + len)
|
|
return -1;
|
|
|
|
out[0] = session_ticket;
|
|
out[1] = (byte)(len >> 16);
|
|
out[2] = (byte)(len >> 8);
|
|
out[3] = (byte)len;
|
|
XMEMCPY(out + HANDSHAKE_HEADER_SZ, body, sizeof(body));
|
|
c16toa((word16)extsSz, out + HANDSHAKE_HEADER_SZ + sizeof(body));
|
|
if (extsSz > 0) {
|
|
XMEMCPY(out + HANDSHAKE_HEADER_SZ + sizeof(body) + OPAQUE16_LEN, exts,
|
|
(size_t)extsSz);
|
|
}
|
|
|
|
return HANDSHAKE_HEADER_SZ + len;
|
|
}
|
|
|
|
/* Encrypt a post-handshake message with the server's keys and hand it to the
|
|
* client. Returns 0 on success. */
|
|
static int test_tls13_send_post_hs(struct test_memio_ctx* test_ctx,
|
|
WOLFSSL* ssl_s, const byte* msg, int msgSz)
|
|
{
|
|
EXPECT_DECLS;
|
|
byte rec[256];
|
|
int recSz;
|
|
|
|
recSz = BuildTls13Message(ssl_s, rec, (int)sizeof(rec), msg, msgSz,
|
|
handshake, 0, 0, 0);
|
|
ExpectIntGT(recSz, 0);
|
|
ExpectIntLE(recSz, (int)sizeof(rec));
|
|
ExpectIntEQ(test_memio_inject_message(test_ctx, 1, (const char*)rec, recSz),
|
|
0);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* RFC 9846 Section 4.7.1 defines NewSessionTicket.extensions as a list of
|
|
* Section 4.3 Extension TLVs, and Section 6 requires a decode_error alert for
|
|
* a message that cannot be parsed. The framing has to be checked whether or
|
|
* not early data - the only extension wolfSSL acts on there - is compiled in.
|
|
*/
|
|
int test_tls13_new_session_ticket_ext_framing(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* Well formed extension of an unknown type - must be ignored. */
|
|
static const byte extOk[] = { 0x12, 0x34, 0x00, 0x02, 0xaa, 0xbb };
|
|
/* Two unknown types, the second with empty extension_data. */
|
|
static const byte extTwo[] = { 0x12, 0x34, 0x00, 0x02, 0xaa, 0xbb,
|
|
0x56, 0x78, 0x00, 0x00 };
|
|
/* Vector too short to hold an Extension header. */
|
|
static const byte extShort[] = { 0x00, 0x2a, 0x00 };
|
|
/* extension_data length runs past the end of the vector. */
|
|
static const byte extTrunc[] = { 0x12, 0x34, 0x00, 0x04, 0xaa, 0xbb };
|
|
static const char appData[] = "still talking";
|
|
struct {
|
|
const byte* exts;
|
|
int extsSz;
|
|
int expectErr;
|
|
} cases[] = {
|
|
{ NULL, 0, 0 },
|
|
{ extOk, (int)sizeof(extOk), 0 },
|
|
{ extTwo, (int)sizeof(extTwo), 0 },
|
|
{ extShort, (int)sizeof(extShort), BUFFER_ERROR },
|
|
{ extTrunc, (int)sizeof(extTrunc), BUFFER_ERROR },
|
|
};
|
|
size_t i;
|
|
char buf[64];
|
|
|
|
for (i = 0; i < XELEM_CNT(cases) && EXPECT_SUCCESS(); i++) {
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
byte msg[64];
|
|
int msgSz;
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Consume the server's own NewSessionTicket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
msgSz = -1;
|
|
if (EXPECT_SUCCESS()) {
|
|
msgSz = test_tls13_make_nst(msg, (int)sizeof(msg), cases[i].exts,
|
|
cases[i].extsSz);
|
|
}
|
|
ExpectIntGT(msgSz, 0);
|
|
ExpectIntEQ(test_tls13_send_post_hs(&test_ctx, ssl_s, msg, msgSz),
|
|
TEST_SUCCESS);
|
|
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
if (cases[i].expectErr == 0) {
|
|
/* Accepted: no application data follows the ticket, and the
|
|
* client raised no alert. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, -1);
|
|
ExpectIntEQ(h.last_tx.level, -1);
|
|
/* The connection carries on: data still flows both ways. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, appData, (int)sizeof(appData) - 1),
|
|
(int)sizeof(appData) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)),
|
|
(int)sizeof(appData) - 1);
|
|
ExpectIntEQ(XMEMCMP(buf, appData, sizeof(appData) - 1), 0);
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, appData, (int)sizeof(appData) - 1),
|
|
(int)sizeof(appData) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)),
|
|
(int)sizeof(appData) - 1);
|
|
ExpectIntEQ(XMEMCMP(buf, appData, sizeof(appData) - 1), 0);
|
|
}
|
|
else {
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
cases[i].expectErr);
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, decode_error);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Parsing a NewSessionTicket must not disturb the extended_master_secret
|
|
* state. TLSX_Parse() clears haveEMS for any non-request message that does
|
|
* not carry the extension, and wolfSSL_clear() never puts it back, so a
|
|
* cleared flag would leave a reused object negotiating TLS 1.2 without EMS. */
|
|
int test_tls13_new_session_ticket_keeps_ems(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_EXTENDED_MASTER) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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 msg[64];
|
|
char buf[64];
|
|
int msgSz;
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Consume the server's own NewSessionTicket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL)
|
|
ssl_c->options.haveEMS = 1;
|
|
|
|
/* A ticket with an empty extensions vector. */
|
|
msgSz = -1;
|
|
if (EXPECT_SUCCESS())
|
|
msgSz = test_tls13_make_nst(msg, (int)sizeof(msg), NULL, 0);
|
|
ExpectIntGT(msgSz, 0);
|
|
ExpectIntEQ(test_tls13_send_post_hs(&test_ctx, ssl_s, msg, msgSz),
|
|
TEST_SUCCESS);
|
|
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
if (EXPECT_SUCCESS() && ssl_c != NULL)
|
|
ExpectIntEQ(ssl_c->options.haveEMS, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
/* One connection, resumed from *sess when set. The server issues one ticket,
|
|
* or two with sendTicket. The newest ticket, with nonce expected, replaces
|
|
* *sess. */
|
|
static int test_tls13_ticket_nonce_round(WOLFSSL_CTX* ctx_c,
|
|
WOLFSSL_CTX* ctx_s, struct test_memio_ctx* test_ctx,
|
|
WOLFSSL_SESSION** sess, int sendTicket, byte expected)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
char buf[64];
|
|
|
|
test_memio_clear_buffer(test_ctx, 0);
|
|
test_memio_clear_buffer(test_ctx, 1);
|
|
ExpectIntEQ(test_memio_setup(test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
if (*sess != NULL)
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, *sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), *sess != NULL);
|
|
if (sendTicket)
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS);
|
|
|
|
/* Consume the NewSessionTicket messages. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Both ends hold the newest ticket. */
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->session->ticketNonce.len, DEF_TICKET_NONCE_SZ);
|
|
ExpectIntEQ(ssl_c->session->ticketNonce.data[0], expected);
|
|
}
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->session->ticketNonce.len, DEF_TICKET_NONCE_SZ);
|
|
ExpectIntEQ(ssl_s->session->ticketNonce.data[0], expected);
|
|
}
|
|
|
|
wolfSSL_SESSION_free(*sess);
|
|
*sess = NULL;
|
|
ExpectNotNull(*sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* The nonce only has to be unique among the tickets of one connection
|
|
* (RFC 8446 Section 4.6.1), so each connection numbers its tickets from 0.
|
|
* The nonce restored from a resumed ticket derives the PSK and is no starting
|
|
* point for the count: a chain of resumptions, each from the newest ticket,
|
|
* must not run the one byte nonce into SESSION_TICKET_NONCE_OVERFLOW. */
|
|
int test_tls13_ticket_nonce_per_connection(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int i;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* A full handshake, then more resumptions than a one byte nonce can
|
|
* count, each from the ticket of the connection before. */
|
|
for (i = 0; i <= 256 && EXPECT_SUCCESS(); i++) {
|
|
ExpectIntEQ(test_tls13_ticket_nonce_round(ctx_c, ctx_s, &test_ctx,
|
|
&sess, 0, 0), TEST_SUCCESS);
|
|
}
|
|
/* A second ticket on a resumed connection is numbered after the first
|
|
* one, and the connection resumed from it counts from 0 again. */
|
|
ExpectIntEQ(test_tls13_ticket_nonce_round(ctx_c, ctx_s, &test_ctx, &sess,
|
|
1, 1), TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_ticket_nonce_round(ctx_c, ctx_s, &test_ctx, &sess,
|
|
0, 0), TEST_SUCCESS);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A server object reused with wolfSSL_clear() keeps its session and with it
|
|
* the nonce of the last ticket. The connection after it numbers from 0. */
|
|
int test_tls13_ticket_nonce_server_reuse(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char buf[64];
|
|
int 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, wolfTLSv1_3_server_method), 0);
|
|
|
|
for (i = 0; i < 3 && EXPECT_SUCCESS(); i++) {
|
|
if (i > 0) {
|
|
/* The same server object, a fresh client. */
|
|
ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS);
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
NULL, wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
}
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Consume the NewSessionTicket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->session->ticketNonce.len, DEF_TICKET_NONCE_SZ);
|
|
ExpectIntEQ(ssl_c->session->ticketNonce.data[0], 0);
|
|
}
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* An X25519 handshake under TLS_AES_256_GCM_SHA384, where preMasterSz is 32
|
|
* and the handshake secret 48 bytes, must leave none of it in preMasterSecret.
|
|
*/
|
|
int test_tls13_hs_secret_zeroized_sha384(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_CURVE25519) && \
|
|
defined(BUILD_TLS_AES_256_GCM_SHA384) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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 zeros[MAX_PREMASTER_SZ];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(zeros, 0, sizeof(zeros));
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "TLS13-AES256-GCM-SHA384"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "TLS13-AES256-GCM-SHA384"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_ECC_X25519),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
wolfSSL_KeepArrays(ssl_c);
|
|
wolfSSL_KeepArrays(ssl_s);
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(ssl_c->arrays->preMasterSecret, 0xAA, MAX_PREMASTER_SZ);
|
|
XMEMSET(ssl_s->arrays->preMasterSecret, 0xAA, MAX_PREMASTER_SZ);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_c->specs.hash_size, WC_SHA384_DIGEST_SIZE);
|
|
|
|
ExpectIntEQ(XMEMCMP(ssl_c->arrays->preMasterSecret, zeros,
|
|
MAX_PREMASTER_SZ), 0);
|
|
ExpectIntEQ(XMEMCMP(ssl_s->arrays->preMasterSecret, zeros,
|
|
MAX_PREMASTER_SZ), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A TLS 1.3 handshake must leave no early secret behind in arrays->secret. */
|
|
int test_tls13_early_secret_zeroized(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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 zeros[SECRET_LEN];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(zeros, 0, sizeof(zeros));
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
wolfSSL_KeepArrays(ssl_c);
|
|
wolfSSL_KeepArrays(ssl_s);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(XMEMCMP(ssl_c->arrays->secret, zeros, SECRET_LEN), 0);
|
|
ExpectIntEQ(XMEMCMP(ssl_s->arrays->secret, zeros, SECRET_LEN), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 9846 Section 4.3.11: when the modes the client advertised leave no PSK
|
|
* the server may use, the PSK is ignored and a certificate handshake runs.
|
|
* Aborting instead would kill a connection that can still be completed. */
|
|
int test_tls13_psk_mode_mismatch_falls_back(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
|
|
int round;
|
|
|
|
for (round = 0; round < 2 && EXPECT_SUCCESS(); round++) {
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
byte readBuf[16];
|
|
char buf[32];
|
|
static const char appData[] = "still talking";
|
|
|
|
/* Full handshake with no mode policy, to obtain a ticket. */
|
|
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(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Drain the NewSessionTicket so the session carries a ticket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
|
|
/* Resume into a mode mismatch. */
|
|
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);
|
|
if (round == 0) {
|
|
/* Client offers psk_ke only, server requires psk_dhe_ke. */
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_only_dhe_psk(ssl_s), 0);
|
|
}
|
|
else {
|
|
/* Client offers psk_dhe_ke only, server refuses (EC)DHE. */
|
|
ExpectIntEQ(wolfSSL_only_dhe_psk(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_s), 0);
|
|
}
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
/* The PSK was declined, not used, and no alert was raised. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
ExpectIntEQ(ssl_s->options.resuming, 0);
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_s, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.code, -1);
|
|
ExpectIntEQ(h.last_tx.level, -1);
|
|
if (round == 1) {
|
|
/* The certificate handshake clears noPskDheKe, so the configured
|
|
* policy has to be kept separately. */
|
|
ExpectIntEQ(ssl_s->options.noPskDheKePolicy, 1);
|
|
}
|
|
/* The connection carries on: data still flows both ways. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, appData, (int)sizeof(appData) - 1),
|
|
(int)sizeof(appData) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)),
|
|
(int)sizeof(appData) - 1);
|
|
ExpectIntEQ(XMEMCMP(buf, appData, sizeof(appData) - 1), 0);
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, appData, (int)sizeof(appData) - 1),
|
|
(int)sizeof(appData) - 1);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)),
|
|
(int)sizeof(appData) - 1);
|
|
ExpectIntEQ(XMEMCMP(buf, appData, sizeof(appData) - 1), 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The ticket decision must use the configured (EC)DHE policy. A certificate
|
|
* handshake clears ssl->options.noPskDheKe before the ticket is sent, so
|
|
* reading that field would make the psk_dhe_ke case always look compatible. */
|
|
int test_tls13_ticket_psk_modes_uses_policy(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
defined(WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* Client advertises psk_dhe_ke only, server is configured to refuse
|
|
* (EC)DHE with PSK, so no ticket it could issue is usable. */
|
|
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);
|
|
ExpectIntEQ(wolfSSL_only_dhe_psk(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_s), 0);
|
|
ExpectIntEQ(test_tls13_handshake_to_ticket(ssl_c, ssl_s), TEST_SUCCESS);
|
|
/* The handshake used a certificate, which clears the negotiated flag. */
|
|
ExpectIntEQ(ssl_s->options.noPskDheKe, 0);
|
|
ExpectIntEQ(ssl_s->options.noPskDheKePolicy, 1);
|
|
ExpectIntEQ(test_ctx.c_len, 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_ctx.c_len, 0);
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s),
|
|
WC_NO_ERR_TRACE(PSK_KEY_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
|
|
/* Control: same client, server with no policy, so a ticket is sent. */
|
|
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);
|
|
ExpectIntEQ(wolfSSL_only_dhe_psk(ssl_c), 0);
|
|
ExpectIntEQ(test_tls13_handshake_to_ticket(ssl_c, ssl_s), TEST_SUCCESS);
|
|
ExpectIntEQ(test_ctx.c_len, 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntGT(test_ctx.c_len, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8446 Section 5.1: a TLS 1.3 receiver MUST ignore legacy_record_version
|
|
* "for all purposes". Garble those two bytes in the server's first flight
|
|
* (the unencrypted ServerHello record, which is not covered by the transcript
|
|
* hash) and the handshake must still complete. For TLS 1.2 the record layer
|
|
* version is still meaningful, so the same tampering must be rejected. */
|
|
int test_tls13_ignore_legacy_record_version(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
|
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));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
/* Run the ClientHello, then the server's first flight. */
|
|
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);
|
|
|
|
/* Bytes 1 and 2 of the record header are legacy_record_version. */
|
|
ExpectIntGT(test_ctx.c_len, RECORD_HEADER_SZ);
|
|
if (EXPECT_SUCCESS()) {
|
|
test_ctx.c_buff[1] = 0xEE;
|
|
test_ctx.c_buff[2] = 0xEE;
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
/* Same tampering under TLS 1.2 must still be caught. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
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);
|
|
|
|
/* Bytes 1 and 2 of the record header are legacy_record_version. */
|
|
ExpectIntGT(test_ctx.c_len, RECORD_HEADER_SZ);
|
|
if (EXPECT_SUCCESS()) {
|
|
test_ctx.c_buff[1] = 0xEE;
|
|
test_ctx.c_buff[2] = 0xEE;
|
|
}
|
|
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
#endif /* !WOLFSSL_NO_TLS12 */
|
|
#endif /* WOLFSSL_TLS13 && HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* An external PSK handshake must leave no PSK behind in arrays->psk_key. */
|
|
int test_tls13_psk_key_zeroized(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
/* Covers both psk_key and secret, as MAX_PSK_KEY_LEN is user-defined. */
|
|
byte zeros[MAX_PSK_KEY_LEN > SECRET_LEN ? MAX_PSK_KEY_LEN : SECRET_LEN];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(zeros, 0, sizeof(zeros));
|
|
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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls13_fnp_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls13_fnp_server_cb);
|
|
|
|
wolfSSL_KeepArrays(ssl_c);
|
|
wolfSSL_KeepArrays(ssl_s);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_s->options.isPSK, 1);
|
|
|
|
ExpectIntEQ(XMEMCMP(ssl_c->arrays->psk_key, zeros, MAX_PSK_KEY_LEN), 0);
|
|
ExpectIntEQ(XMEMCMP(ssl_s->arrays->psk_key, zeros, MAX_PSK_KEY_LEN), 0);
|
|
ExpectIntEQ(XMEMCMP(ssl_c->arrays->secret, zeros, SECRET_LEN), 0);
|
|
ExpectIntEQ(XMEMCMP(ssl_s->arrays->secret, zeros, SECRET_LEN), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*
|
|
| Session export and import
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_SESSION_EXPORT)
|
|
#define TEST_TLS13_EXPORT
|
|
#endif
|
|
|
|
#ifdef TEST_TLS13_EXPORT
|
|
/* process buffered post-handshake messages, success being WANT_READ */
|
|
static int test_tls13_export_pump(WOLFSSL* ssl)
|
|
{
|
|
unsigned char buf[8];
|
|
int ret;
|
|
|
|
ret = wolfSSL_read(ssl, buf, (int)sizeof(buf));
|
|
if (ret > 0)
|
|
return -1;
|
|
if (wolfSSL_get_error(ssl, ret) != WOLFSSL_ERROR_WANT_READ)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int test_tls13_export_connect(struct test_memio_ctx* test_ctx,
|
|
WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c,
|
|
WOLFSSL** ssl_s)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
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);
|
|
ExpectIntEQ(test_memio_do_handshake(*ssl_c, *ssl_s, 10, NULL), 0);
|
|
/* the tickets sent with the server's Finished */
|
|
ExpectIntEQ(test_tls13_export_pump(*ssl_c), 0);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int test_tls13_export_alloc(WOLFSSL* ssl, unsigned char** out,
|
|
unsigned int* outSz)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
*outSz = 0;
|
|
ExpectIntEQ(wolfSSL_tls_export(ssl, NULL, outSz),
|
|
WC_NO_ERR_TRACE(LENGTH_ONLY_E));
|
|
ExpectNotNull(*out = (unsigned char*)XMALLOC(*outSz, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectIntGT(wolfSSL_tls_export(ssl, *out, outSz), 0);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* import 'session' into a fresh WOLFSSL wired to the same memio buffers */
|
|
static int test_tls13_export_import(WOLFSSL_CTX* ctx,
|
|
struct test_memio_ctx* test_ctx, WOLFSSL** ssl,
|
|
const unsigned char* session, unsigned int sessionSz)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
ExpectNotNull(*ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_tls_import(*ssl, session, sessionSz), (int)sessionSz);
|
|
if (*ssl != NULL) {
|
|
wolfSSL_SetIOWriteCtx(*ssl, test_ctx);
|
|
wolfSSL_SetIOReadCtx(*ssl, test_ctx);
|
|
}
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* replace *ssl by an import of its own export */
|
|
static int test_tls13_export_reload(WOLFSSL_CTX* ctx,
|
|
struct test_memio_ctx* test_ctx, WOLFSSL** ssl)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL* imp = NULL;
|
|
unsigned char* session = NULL;
|
|
unsigned int sessionSz = 0;
|
|
|
|
ExpectIntEQ(test_tls13_export_alloc(*ssl, &session, &sessionSz),
|
|
TEST_SUCCESS);
|
|
wolfSSL_free(*ssl);
|
|
*ssl = NULL;
|
|
ExpectIntEQ(test_tls13_export_import(ctx, test_ctx, &imp, session,
|
|
sessionSz), TEST_SUCCESS);
|
|
*ssl = imp;
|
|
XFREE(session, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int test_tls13_export_xfer(WOLFSSL* w, WOLFSSL* r, const char* msg)
|
|
{
|
|
EXPECT_DECLS;
|
|
unsigned char buf[64];
|
|
int msgSz = (int)XSTRLEN(msg);
|
|
|
|
ExpectIntEQ(wolfSSL_write(w, msg, msgSz), msgSz);
|
|
ExpectIntEQ(wolfSSL_read(r, buf, (int)sizeof(buf)), msgSz);
|
|
ExpectBufEQ(buf, msg, msgSz);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static void test_tls13_export_free(WOLFSSL_CTX* ctx_c, WOLFSSL_CTX* ctx_s,
|
|
WOLFSSL* ssl_c, WOLFSSL* ssl_s)
|
|
{
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
|
|
#ifdef HAVE_SESSION_TICKET
|
|
/* A ticket issued after the import is numbered after the ones issued before
|
|
* it and derives from the resumption secret of the connection, so that it
|
|
* resumes, whichever side was imported. */
|
|
static int test_tls13_export_ticket_after_import(int exportClient)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
ExpectIntEQ(test_tls13_export_connect(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s), TEST_SUCCESS);
|
|
/* the ticket sent with the server's Finished, the first of the
|
|
* connection */
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->session->ticketNonce.len, DEF_TICKET_NONCE_SZ);
|
|
ExpectIntEQ(ssl_c->session->ticketNonce.data[0], 0);
|
|
}
|
|
|
|
if (exportClient) {
|
|
ExpectIntEQ(test_tls13_export_reload(ctx_c, &test_ctx, &ssl_c),
|
|
TEST_SUCCESS);
|
|
}
|
|
else {
|
|
ExpectIntEQ(test_tls13_export_reload(ctx_s, &test_ctx, &ssl_s),
|
|
TEST_SUCCESS);
|
|
}
|
|
|
|
/* the second ticket of the connection, issued after the import */
|
|
ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_tls13_export_pump(ssl_c), 0);
|
|
/* numbered after the first one, not like it (RFC 8446 Section 4.6.1) */
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->session->ticketNonce.len, DEF_TICKET_NONCE_SZ);
|
|
ExpectIntEQ(ssl_c->session->ticketNonce.data[0], 1);
|
|
}
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
/* and it resumes */
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
test_tls13_export_free(ctx_c, ctx_s, ssl_c, ssl_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* HAVE_SESSION_TICKET */
|
|
|
|
/* A KeyUpdate after the import, from either side, derives the next keys from
|
|
* the traffic secrets of the connection. */
|
|
static int test_tls13_export_key_update(int clientInitiates)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL *initiator = NULL, *peer = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
ExpectIntEQ(test_tls13_export_connect(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s), TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_export_reload(ctx_s, &test_ctx, &ssl_s),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_export_xfer(ssl_s, ssl_c, "hello client"),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_export_xfer(ssl_c, ssl_s, "hello server"),
|
|
TEST_SUCCESS);
|
|
|
|
initiator = clientInitiates ? ssl_c : ssl_s;
|
|
peer = clientInitiates ? ssl_s : ssl_c;
|
|
ExpectIntEQ(wolfSSL_update_keys(initiator), WOLFSSL_SUCCESS);
|
|
/* the peer takes the KeyUpdate in ahead of the record that follows it and
|
|
* answers with its own ahead of its reply */
|
|
ExpectIntEQ(test_tls13_export_xfer(initiator, peer, "after the update"),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls13_export_xfer(peer, initiator, "and back"),
|
|
TEST_SUCCESS);
|
|
|
|
test_tls13_export_free(ctx_c, ctx_s, ssl_c, ssl_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* TEST_TLS13_EXPORT */
|
|
|
|
int test_tls13_export_server_ticket_after_import(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(TEST_TLS13_EXPORT) && defined(HAVE_SESSION_TICKET)
|
|
ExpectIntEQ(test_tls13_export_ticket_after_import(0), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_export_client_ticket_after_import(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(TEST_TLS13_EXPORT) && defined(HAVE_SESSION_TICKET)
|
|
ExpectIntEQ(test_tls13_export_ticket_after_import(1), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_export_server_key_update(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS13_EXPORT
|
|
ExpectIntEQ(test_tls13_export_key_update(0), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_export_client_key_update(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS13_EXPORT
|
|
ExpectIntEQ(test_tls13_export_key_update(1), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* An imported connection that had seen a fatal alert still refuses writes. */
|
|
int test_tls13_export_keeps_fatal_alert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS13_EXPORT
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "should never be sent";
|
|
|
|
ExpectIntEQ(test_tls13_export_connect(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s), TEST_SUCCESS);
|
|
if (ssl_c != NULL) {
|
|
ssl_c->options.isClosed = 1;
|
|
ssl_c->options.fatalAlert = 1;
|
|
}
|
|
ExpectIntEQ(test_tls13_export_reload(ctx_c, &test_ctx, &ssl_c),
|
|
TEST_SUCCESS);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->options.isClosed, 1);
|
|
ExpectIntEQ(ssl_c->options.fatalAlert, 1);
|
|
}
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
|
|
test_tls13_export_free(ctx_c, ctx_s, ssl_c, ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* An imported connection closed only by a transport EOF can still write. */
|
|
int test_tls13_export_keeps_transport_close(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS13_EXPORT
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
ExpectIntEQ(test_tls13_export_connect(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s), TEST_SUCCESS);
|
|
if (ssl_c != NULL)
|
|
ssl_c->options.isClosed = 1;
|
|
ExpectIntEQ(test_tls13_export_reload(ctx_c, &test_ctx, &ssl_c),
|
|
TEST_SUCCESS);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->options.isClosed, 1);
|
|
ExpectIntEQ(ssl_c->options.fatalAlert, 0);
|
|
}
|
|
ExpectIntEQ(test_tls13_export_xfer(ssl_c, ssl_s, "after the close"),
|
|
TEST_SUCCESS);
|
|
|
|
test_tls13_export_free(ctx_c, ctx_s, ssl_c, ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A version 6 export only has isClosed, which then also stands for a fatal
|
|
* alert, so the imported connection still refuses writes. */
|
|
int test_tls12_export_v6_closed_refuses_write(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(TEST_TLS13_EXPORT) && !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL, *imp = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
unsigned char* session = NULL;
|
|
unsigned int sessionSz = 0;
|
|
char msg[] = "should never be sent";
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
if (ssl_c != NULL)
|
|
ssl_c->options.isClosed = 1;
|
|
ExpectIntEQ(test_tls13_export_alloc(ssl_c, &session, &sessionSz),
|
|
TEST_SUCCESS);
|
|
if (EXPECT_SUCCESS())
|
|
test_export_options_v6(session, &sessionSz);
|
|
|
|
ExpectIntEQ(test_tls13_export_import(ctx_c, &test_ctx, &imp, session,
|
|
sessionSz), TEST_SUCCESS);
|
|
if (imp != NULL) {
|
|
ExpectIntEQ(imp->options.isClosed, 1);
|
|
ExpectIntEQ(imp->options.fatalAlert, 1);
|
|
}
|
|
ExpectIntEQ(wolfSSL_write(imp, msg, (int)sizeof(msg)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
|
|
XFREE(session, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
wolfSSL_free(imp);
|
|
test_tls13_export_free(ctx_c, ctx_s, ssl_c, ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLF_PRIVATE_KEY_ID) && \
|
|
defined(WOLF_CRYPTO_CB) && !defined(NO_CHECK_PRIVATE_KEY) && \
|
|
!defined(WOLFSSL_BLIND_PRIVATE_KEY) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
|
|
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN) && \
|
|
defined(HAVE_ED25519_VERIFY) && defined(HAVE_ED25519_KEY_IMPORT) && \
|
|
defined(ED25519_MAX_ID_LEN)
|
|
#define TEST_TLS13_ED25519_DEV_KEY
|
|
#endif
|
|
#if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) && \
|
|
defined(HAVE_ED448_VERIFY) && defined(HAVE_ED448_KEY_IMPORT) && \
|
|
defined(ED448_MAX_ID_LEN)
|
|
#define TEST_TLS13_ED448_DEV_KEY
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(TEST_TLS13_ED25519_DEV_KEY) || defined(TEST_TLS13_ED448_DEV_KEY)
|
|
#define TEST_TLS13_ED_DEV_DEVID 0x45444b59
|
|
|
|
static const byte testEdDevKeyId[] = { 0x0e, 0xd0, 0x00, 0x01 };
|
|
static const char testEdDevKeyLabel[] = "ed-device-key";
|
|
|
|
typedef struct TestEdDevCtx {
|
|
#ifdef TEST_TLS13_ED25519_DEV_KEY
|
|
ed25519_key ed25519;
|
|
#endif
|
|
#ifdef TEST_TLS13_ED448_DEV_KEY
|
|
ed448_key ed448;
|
|
#endif
|
|
int signs;
|
|
int checks;
|
|
int declineCheck;
|
|
} TestEdDevCtx;
|
|
|
|
/* Returns 1 when the key names the device key by its id or label. */
|
|
static int test_tls13_ed_dev_ref(const byte* id, int idLen, const char* label,
|
|
int labelLen)
|
|
{
|
|
if ((idLen == (int)sizeof(testEdDevKeyId)) &&
|
|
(XMEMCMP(id, testEdDevKeyId, sizeof(testEdDevKeyId)) == 0)) {
|
|
return 1;
|
|
}
|
|
if ((labelLen == (int)XSTRLEN(testEdDevKeyLabel)) &&
|
|
(XMEMCMP(label, testEdDevKeyLabel, (size_t)labelLen) == 0)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Device holding the server private key: signs with it and checks it against
|
|
* the public key it is handed. */
|
|
static int test_tls13_ed_dev_cb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
|
{
|
|
TestEdDevCtx* dev = (TestEdDevCtx*)ctx;
|
|
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
|
|
(void)devIdArg;
|
|
|
|
if (info->algo_type != WC_ALGO_TYPE_PK) {
|
|
return ret;
|
|
}
|
|
#ifdef TEST_TLS13_ED25519_DEV_KEY
|
|
if (info->pk.type == WC_PK_TYPE_ED25519_SIGN) {
|
|
ed25519_key* key = info->pk.ed25519sign.key;
|
|
|
|
if (!test_tls13_ed_dev_ref(key->id, key->idLen, key->label,
|
|
key->labelLen)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
ret = wc_ed25519_sign_msg_ex(info->pk.ed25519sign.in,
|
|
info->pk.ed25519sign.inLen, info->pk.ed25519sign.out,
|
|
info->pk.ed25519sign.outLen, &dev->ed25519,
|
|
info->pk.ed25519sign.type, info->pk.ed25519sign.context,
|
|
info->pk.ed25519sign.contextLen);
|
|
dev->signs++;
|
|
}
|
|
else if (info->pk.type == WC_PK_TYPE_ED25519_CHECK_KEY) {
|
|
ed25519_key* key = info->pk.ed25519checkkey.key;
|
|
|
|
if (dev->declineCheck) {
|
|
ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
else if (!info->pk.ed25519checkkey.checkPriv ||
|
|
(info->pk.ed25519checkkey.pubKeySz != ED25519_PUB_KEY_SIZE) ||
|
|
!test_tls13_ed_dev_ref(key->id, key->idLen, key->label,
|
|
key->labelLen)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
else if (XMEMCMP(info->pk.ed25519checkkey.pubKey, dev->ed25519.p,
|
|
ED25519_PUB_KEY_SIZE) != 0) {
|
|
ret = PUBLIC_KEY_E;
|
|
}
|
|
else {
|
|
ret = 0;
|
|
}
|
|
dev->checks++;
|
|
}
|
|
#endif
|
|
#ifdef TEST_TLS13_ED448_DEV_KEY
|
|
if (info->pk.type == WC_PK_TYPE_ED448) {
|
|
ed448_key* key = info->pk.ed448sign.key;
|
|
|
|
if (!test_tls13_ed_dev_ref(key->id, key->idLen, key->label,
|
|
key->labelLen)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
ret = wc_ed448_sign_msg_ex(info->pk.ed448sign.in,
|
|
info->pk.ed448sign.inLen, info->pk.ed448sign.out,
|
|
info->pk.ed448sign.outLen, &dev->ed448, info->pk.ed448sign.type,
|
|
info->pk.ed448sign.context, info->pk.ed448sign.contextLen);
|
|
dev->signs++;
|
|
}
|
|
else if (info->pk.type == WC_PK_TYPE_ED448_CHECK_KEY) {
|
|
ed448_key* key = info->pk.ed448checkkey.key;
|
|
|
|
if (dev->declineCheck) {
|
|
ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
else if (!info->pk.ed448checkkey.checkPriv ||
|
|
(info->pk.ed448checkkey.pubKeySz != ED448_PUB_KEY_SIZE) ||
|
|
!test_tls13_ed_dev_ref(key->id, key->idLen, key->label,
|
|
key->labelLen)) {
|
|
ret = BAD_FUNC_ARG;
|
|
}
|
|
else if (XMEMCMP(info->pk.ed448checkkey.pubKey, dev->ed448.p,
|
|
ED448_PUB_KEY_SIZE) != 0) {
|
|
ret = PUBLIC_KEY_E;
|
|
}
|
|
else {
|
|
ret = 0;
|
|
}
|
|
dev->checks++;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Server private key referenced by id or label in a device: the pair check
|
|
* must reach the device and the handshake must be signed by it. */
|
|
static int test_tls13_ed_dev_key_round(TestEdDevCtx* dev, const char* caFile,
|
|
const char* certFile, const char* otherCertFile)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL_CTX* ctx_o = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte longId[64];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(longId, 0x0e, sizeof(longId));
|
|
dev->signs = 0;
|
|
dev->checks = 0;
|
|
dev->declineCheck = 0;
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_TLS13_ED_DEV_DEVID,
|
|
test_tls13_ed_dev_cb, dev), 0);
|
|
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_s, certFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_Label(ctx_s, testEdDevKeyLabel,
|
|
TEST_TLS13_ED_DEV_DEVID), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_check_private_key(ctx_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(dev->checks, 1);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_Id(ctx_s, testEdDevKeyId,
|
|
(long)sizeof(testEdDevKeyId), TEST_TLS13_ED_DEV_DEVID),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_check_private_key(ctx_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(dev->checks, 2);
|
|
|
|
/* An id longer than the key can hold fails before reaching the device. */
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_Id(ctx_s, longId,
|
|
(long)sizeof(longId), TEST_TLS13_ED_DEV_DEVID), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_check_private_key(ctx_s), WOLFSSL_FAILURE);
|
|
ExpectIntEQ(dev->checks, 2);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_Id(ctx_s, testEdDevKeyId,
|
|
(long)sizeof(testEdDevKeyId), TEST_TLS13_ED_DEV_DEVID),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* Software cannot see the device key, so a declined check must fail. */
|
|
dev->declineCheck = 1;
|
|
ExpectIntEQ(wolfSSL_CTX_check_private_key(ctx_s), WOLFSSL_FAILURE);
|
|
ExpectIntEQ(dev->checks, 3);
|
|
dev->declineCheck = 0;
|
|
|
|
/* The device key does not belong to this certificate. */
|
|
ExpectNotNull(ctx_o = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_o, otherCertFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_Id(ctx_o, testEdDevKeyId,
|
|
(long)sizeof(testEdDevKeyId), TEST_TLS13_ED_DEV_DEVID),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_check_private_key(ctx_o), WOLFSSL_FAILURE);
|
|
ExpectIntEQ(dev->checks, 4);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
}
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
}
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
NULL, NULL), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntGE(dev->signs, 1);
|
|
/* The handshake does not check the pair again. */
|
|
ExpectIntEQ(dev->checks, 4);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_o);
|
|
wc_CryptoCb_UnRegisterDevice(TEST_TLS13_ED_DEV_DEVID);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* TEST_TLS13_ED25519_DEV_KEY || TEST_TLS13_ED448_DEV_KEY */
|
|
|
|
int test_tls13_ed25519_dev_private_key(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS13_ED25519_DEV_KEY
|
|
TestEdDevCtx dev;
|
|
byte* der = NULL;
|
|
size_t derSz = 0;
|
|
word32 idx = 0;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
ExpectIntEQ(wc_ed25519_init(&dev.ed25519), 0);
|
|
ExpectIntEQ(load_file("./certs/ed25519/server-ed25519-priv.der", &der,
|
|
&derSz), 0);
|
|
ExpectIntEQ(wc_Ed25519PrivateKeyDecode(der, &idx, &dev.ed25519,
|
|
(word32)derSz), 0);
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
der = NULL;
|
|
idx = 0;
|
|
ExpectIntEQ(load_file("./certs/ed25519/server-ed25519-key.der", &der, &derSz), 0);
|
|
ExpectIntEQ(wc_Ed25519PublicKeyDecode(der, &idx, &dev.ed25519, (word32)derSz),
|
|
0);
|
|
|
|
ExpectIntEQ(test_tls13_ed_dev_key_round(&dev, caEdCertFile, edCertFile,
|
|
cliEdCertFile), TEST_SUCCESS);
|
|
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
wc_ed25519_free(&dev.ed25519);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_ed448_dev_private_key(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS13_ED448_DEV_KEY
|
|
TestEdDevCtx dev;
|
|
byte* der = NULL;
|
|
size_t derSz = 0;
|
|
word32 idx = 0;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
ExpectIntEQ(wc_ed448_init(&dev.ed448), 0);
|
|
ExpectIntEQ(load_file("./certs/ed448/server-ed448-priv.der", &der,
|
|
&derSz), 0);
|
|
ExpectIntEQ(wc_Ed448PrivateKeyDecode(der, &idx, &dev.ed448,
|
|
(word32)derSz), 0);
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
der = NULL;
|
|
idx = 0;
|
|
ExpectIntEQ(load_file("./certs/ed448/server-ed448-key.der", &der, &derSz), 0);
|
|
ExpectIntEQ(wc_Ed448PublicKeyDecode(der, &idx, &dev.ed448, (word32)derSz),
|
|
0);
|
|
|
|
ExpectIntEQ(test_tls13_ed_dev_key_round(&dev, caEd448CertFile,
|
|
ed448CertFile, cliEd448CertFile), TEST_SUCCESS);
|
|
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
wc_ed448_free(&dev.ed448);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DUAL_ALG_CERTS) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_ECC) && \
|
|
defined(USE_CERT_BUFFERS_256) && defined(USE_WOLFSSL_MEMORY) && \
|
|
!defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY) && \
|
|
!defined(WOLFSSL_DEBUG_MEMORY)
|
|
#define TEST_TLS13_CKS_REPARSE
|
|
|
|
static wolfSSL_Free_cb test_tls13_cks_prev_free;
|
|
static void* test_tls13_cks_watch;
|
|
static byte test_tls13_cks_freed;
|
|
|
|
static void test_tls13_cks_free_cb(void* ptr)
|
|
{
|
|
if (ptr != NULL && ptr == test_tls13_cks_watch) {
|
|
/* Held until the test ends so the address is not handed out again. */
|
|
test_tls13_cks_freed++;
|
|
return;
|
|
}
|
|
if (test_tls13_cks_prev_free != NULL)
|
|
test_tls13_cks_prev_free(ptr);
|
|
else
|
|
free(ptr);
|
|
}
|
|
#endif
|
|
|
|
/* The server copies the peer's CKS list from each ClientHello. After a
|
|
* HelloRetryRequest the copy taken from the first ClientHello has to be
|
|
* released when the second one replaces it. */
|
|
int test_tls13_cks_hrr_reparse(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(TEST_TLS13_CKS_REPARSE) && defined(USE_WOLFSSL_MEMORY)
|
|
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 cks[1] = { WOLFSSL_CKS_SIGSPEC_NATIVE };
|
|
wolfSSL_Malloc_cb prev_mc = NULL;
|
|
wolfSSL_Realloc_cb prev_rc = NULL;
|
|
byte hooked = 0;
|
|
|
|
test_tls13_cks_watch = NULL;
|
|
test_tls13_cks_freed = 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);
|
|
ExpectIntEQ(wolfSSL_use_AltPrivateKey_buffer(ssl_s, ecc_key_der_256,
|
|
sizeof_ecc_key_der_256, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseCKS(ssl_c, cks, (word16)sizeof(cks)),
|
|
WOLFSSL_SUCCESS);
|
|
/* No key share entries, so the server has to ask for one. */
|
|
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
|
|
|
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(ssl_s->options.serverState,
|
|
SERVER_HELLO_RETRY_REQUEST_COMPLETE);
|
|
ExpectNotNull(test_tls13_cks_watch = ssl_s->peerSigSpec);
|
|
|
|
ExpectIntEQ(wolfSSL_GetAllocators(&prev_mc, &test_tls13_cks_prev_free,
|
|
&prev_rc), 0);
|
|
ExpectIntEQ(wolfSSL_SetAllocators(prev_mc, test_tls13_cks_free_cb,
|
|
prev_rc), 0);
|
|
if (EXPECT_SUCCESS())
|
|
hooked = 1;
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (hooked)
|
|
(void)wolfSSL_SetAllocators(prev_mc, test_tls13_cks_prev_free,
|
|
prev_rc);
|
|
|
|
ExpectNotNull(ssl_c->peerSigSpec);
|
|
ExpectIntEQ(test_tls13_cks_freed, 1);
|
|
ExpectNotNull(ssl_s->peerSigSpec);
|
|
ExpectPtrNE(ssl_s->peerSigSpec, test_tls13_cks_watch);
|
|
ExpectIntEQ(ssl_s->peerSigSpecSz, sizeof(cks));
|
|
/* With no preference of its own the server follows the second list. */
|
|
ExpectPtrEq(ssl_s->sigSpec, ssl_s->peerSigSpec);
|
|
|
|
if (test_tls13_cks_freed > 0 && ssl_s != NULL &&
|
|
ssl_s->peerSigSpec != test_tls13_cks_watch)
|
|
XFREE(test_tls13_cks_watch, NULL, DYNAMIC_TYPE_TLSX);
|
|
test_tls13_cks_watch = NULL;
|
|
test_tls13_cks_prev_free = NULL;
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|