mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
* add NO_WOLFSSL_WIRE_MANGLE_TESTS macro gate to disable expensive stochastic tests (test_dtls13_wire_mangle, test_dtls13_wire_mangle, test_dtls12_packet_forgeries, test_dtls13_packet_forgeries, test_tls_wire_mangle, test_tls_wire_sequence). * fix C++ enum/int clash warnings in test_dtls_cid_wire().
2632 lines
96 KiB
C
2632 lines
96 KiB
C
/* test_ssl_hs.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 <wolfssl/wolfio.h>
|
|
|
|
#include <tests/utils.h>
|
|
#include <tests/api/test_ssl_hs.h>
|
|
|
|
/* Tests for the handshake APIs in src/ssl_api_hs.c (moved from ssl.c). These
|
|
* cover functions not already exercised elsewhere in api.c. */
|
|
|
|
/* Test wolfSSL_state_string_long() over a live handshake.
|
|
*
|
|
* Covers the NULL case, the unknown-protocol case and sampling the state
|
|
* before, during and after a handshake.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_state_string_long(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) \
|
|
&& !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* A NULL object has no state to report. */
|
|
ExpectNull(wolfSSL_state_string_long(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);
|
|
|
|
/* Before the handshake both sides report the initial state, named for the
|
|
* method's version rather than a negotiated one. */
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl_c), "TLSv1_2 Initialization");
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl_s), "TLSv1_2 Initialization");
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* After the handshake each side reports the last message it handled,
|
|
* prefixed with the negotiated protocol. Both have finished, so neither
|
|
* is still reporting the initial state. */
|
|
ExpectIntEQ(XSTRNCMP(wolfSSL_state_string_long(ssl_c), "TLSv1_2 ", 8), 0);
|
|
ExpectIntEQ(XSTRNCMP(wolfSSL_state_string_long(ssl_s), "TLSv1_2 ", 8), 0);
|
|
ExpectStrNE(wolfSSL_state_string_long(ssl_c), "TLSv1_2 Initialization");
|
|
ExpectStrNE(wolfSSL_state_string_long(ssl_s), "TLSv1_2 Initialization");
|
|
|
|
/* The completed state has one string for both directions, unlike the
|
|
* per-message states which name the direction. */
|
|
if (ssl_c != NULL) {
|
|
ssl_c->cbmode = WOLFSSL_CB_MODE_WRITE;
|
|
ssl_c->options.clientState = HANDSHAKE_DONE;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl_c), "TLSv1_2 Handshake Done");
|
|
ssl_c->cbmode = WOLFSSL_CB_MODE_READ;
|
|
ssl_c->cbtype = server_hello;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl_c),
|
|
"TLSv1_2 read Server Hello");
|
|
}
|
|
|
|
/* An unrecognized protocol version reports an empty string. */
|
|
if (ssl_c != NULL) {
|
|
ProtocolVersion saved = ssl_c->version;
|
|
|
|
ssl_c->version.major = 0x7f;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl_c), "");
|
|
ssl_c->version = saved;
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test wolfSSL_state_string_long() across the states it can report.
|
|
*
|
|
* The reported string is chosen from the callback mode, the handshake message
|
|
* type when reading, and the connection state when writing. Walk each of those
|
|
* so every arm of the translation is taken.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_state_string_long_states(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(OPENSSL_EXTRA) && !defined(NO_WOLFSSL_CLIENT) \
|
|
&& !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
/* Handshake message types reported while reading, with the string each
|
|
* maps to. Only one side sends these, so the object's own side does not
|
|
* come into it. */
|
|
static const struct {
|
|
int type;
|
|
const char* str;
|
|
} readStates[] = {
|
|
{ hello_request, "TLSv1_2 read Server Hello Request" },
|
|
{ hello_verify_request, "TLSv1_2 read Server Hello Verify Request" },
|
|
{ session_ticket, "TLSv1_2 read Server Session Ticket" },
|
|
{ end_of_early_data, "TLSv1_2 read Client End Of Early Data" },
|
|
{ hello_retry_request, "TLSv1_2 read Server Hello Retry Request" },
|
|
{ client_hello, "TLSv1_2 read Client Hello" },
|
|
{ server_hello, "TLSv1_2 read Server Hello" },
|
|
{ encrypted_extensions, "TLSv1_2 read Server Encrypted Extensions" },
|
|
{ server_key_exchange, "TLSv1_2 read Server Key Exchange" },
|
|
{ certificate_request, "TLSv1_2 read Server Certificate Request" },
|
|
{ server_hello_done, "TLSv1_2 read Server Hello Done" },
|
|
{ certificate_verify, "TLSv1_2 read Client Certificate Verify" },
|
|
{ client_key_exchange, "TLSv1_2 read Client Key Exchange" },
|
|
{ certificate_status, "TLSv1_2 read Server Certificate Status" },
|
|
/* An unrecognized type reports the null state. */
|
|
{ 0x7f, "TLSv1_2 Initialization" }
|
|
};
|
|
/* Message types both sides send. The string names the side that sent the
|
|
* message, which is the peer - the opposite of the object's own side. */
|
|
static const struct {
|
|
int type;
|
|
const char* asClient;
|
|
const char* asServer;
|
|
} sidedStates[] = {
|
|
{ certificate,
|
|
"TLSv1_2 read Server Cert",
|
|
"TLSv1_2 read Client Cert" },
|
|
{ finished,
|
|
"TLSv1_2 read Server Finished",
|
|
"TLSv1_2 read Client Finished" },
|
|
{ key_update,
|
|
"TLSv1_2 read server Key Update",
|
|
"TLSv1_2 read Client Key Update" },
|
|
{ change_cipher_hs,
|
|
"TLSv1_2 read Server Change CipherSpec",
|
|
"TLSv1_2 read Client Change CipherSpec" }
|
|
};
|
|
/* Connection states reported while writing. Every state from the first
|
|
* to HANDSHAKE_DONE is listed, so the walk below covers the range. */
|
|
static const struct {
|
|
int state;
|
|
const char* str;
|
|
} writeStates[] = {
|
|
{ NULL_STATE,
|
|
"TLSv1_2 Initialization" },
|
|
{ SERVER_HELLOVERIFYREQUEST_COMPLETE,
|
|
"TLSv1_2 write Server Hello Verify Request" },
|
|
{ SERVER_HELLO_RETRY_REQUEST_COMPLETE,
|
|
"TLSv1_2 write Server Hello Retry Request" },
|
|
{ SERVER_HELLO_COMPLETE,
|
|
"TLSv1_2 write Server Hello" },
|
|
{ SERVER_ENCRYPTED_EXTENSIONS_COMPLETE,
|
|
"TLSv1_2 write Server Encrypted Extensions" },
|
|
{ SERVER_CERT_COMPLETE,
|
|
"TLSv1_2 write Server Cert" },
|
|
/* Has no string of its own, so reports the null state. */
|
|
{ SERVER_CERT_VERIFY_COMPLETE,
|
|
"TLSv1_2 Initialization" },
|
|
{ SERVER_KEYEXCHANGE_COMPLETE,
|
|
"TLSv1_2 write Server Key Exchange" },
|
|
{ SERVER_HELLODONE_COMPLETE,
|
|
"TLSv1_2 write Server Hello Done" },
|
|
{ SERVER_CHANGECIPHERSPEC_COMPLETE,
|
|
"TLSv1_2 write Server Change CipherSpec" },
|
|
{ SERVER_FINISHED_COMPLETE,
|
|
"TLSv1_2 write Server Finished" },
|
|
{ CLIENT_HELLO_RETRY,
|
|
"TLSv1_2 write Client Hello" },
|
|
{ CLIENT_HELLO_COMPLETE,
|
|
"TLSv1_2 write Client Hello" },
|
|
{ CLIENT_KEYEXCHANGE_COMPLETE,
|
|
"TLSv1_2 write Client Key Exchange" },
|
|
{ CLIENT_CHANGECIPHERSPEC_COMPLETE,
|
|
"TLSv1_2 write Client Change CipherSpec" },
|
|
{ CLIENT_FINISHED_COMPLETE,
|
|
"TLSv1_2 write Client Finished" },
|
|
{ HANDSHAKE_DONE,
|
|
"TLSv1_2 Handshake Done" }
|
|
};
|
|
/* Each protocol version has its own set of strings. */
|
|
static const struct {
|
|
int major;
|
|
int minor;
|
|
const char* str;
|
|
} protocols[] = {
|
|
{ SSLv3_MAJOR, SSLv3_MINOR, "SSLv3 read Server Hello" },
|
|
{ SSLv3_MAJOR, TLSv1_MINOR, "TLSv1 read Server Hello" },
|
|
{ SSLv3_MAJOR, TLSv1_1_MINOR, "TLSv1_1 read Server Hello" },
|
|
{ SSLv3_MAJOR, TLSv1_2_MINOR, "TLSv1_2 read Server Hello" },
|
|
{ SSLv3_MAJOR, TLSv1_3_MINOR, "TLSv1_3 read Server Hello" },
|
|
{ DTLS_MAJOR, DTLS_MINOR, "DTLSv1 read Server Hello" },
|
|
{ DTLS_MAJOR, DTLSv1_2_MINOR, "DTLSv1_2 read Server Hello" },
|
|
{ DTLS_MAJOR, DTLSv1_3_MINOR, "DTLSv1_3 read Server Hello" },
|
|
/* An unrecognized minor version of either major reports no string. */
|
|
{ SSLv3_MAJOR, 0x7f, "" },
|
|
{ DTLS_MAJOR, 0x7f, "" }
|
|
};
|
|
int i;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
if (ssl != NULL) {
|
|
/* Reading: every message type maps to a state string. */
|
|
ssl->cbmode = WOLFSSL_CB_MODE_READ;
|
|
for (i = 0; i < (int)XELEM_CNT(readStates); i++) {
|
|
ssl->cbtype = readStates[i].type;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl), readStates[i].str);
|
|
}
|
|
|
|
/* The messages both sides send name their sender, so the string
|
|
* follows the side of the object that read them. */
|
|
for (i = 0; i < (int)XELEM_CNT(sidedStates); i++) {
|
|
ssl->cbtype = sidedStates[i].type;
|
|
ssl->options.side = WOLFSSL_CLIENT_END;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl),
|
|
sidedStates[i].asClient);
|
|
ssl->options.side = WOLFSSL_SERVER_END;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl),
|
|
sidedStates[i].asServer);
|
|
/* With no side established the sender cannot be named. */
|
|
ssl->options.side = WOLFSSL_NEITHER_END;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl),
|
|
"TLSv1_2 Initialization");
|
|
}
|
|
ssl->options.side = WOLFSSL_CLIENT_END;
|
|
|
|
/* Writing: the connection state is reported instead. */
|
|
ssl->cbmode = WOLFSSL_CB_MODE_WRITE;
|
|
for (i = 0; i < (int)XELEM_CNT(writeStates); i++) {
|
|
ssl->options.clientState = (byte)writeStates[i].state;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl), writeStates[i].str);
|
|
}
|
|
|
|
/* Which state is reported follows the side: a server reports its own
|
|
* rather than the client's. */
|
|
ssl->options.serverState = SERVER_HELLO_COMPLETE;
|
|
ssl->options.clientState = CLIENT_FINISHED_COMPLETE;
|
|
ssl->options.side = WOLFSSL_SERVER_END;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl),
|
|
"TLSv1_2 write Server Hello");
|
|
ssl->options.side = WOLFSSL_CLIENT_END;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl),
|
|
"TLSv1_2 write Client Finished");
|
|
|
|
/* Neither reading nor writing: the string carries no direction. */
|
|
ssl->cbmode = 0;
|
|
ssl->options.clientState = SERVER_HELLO_COMPLETE;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl), "TLSv1_2 Server Hello");
|
|
|
|
/* Each protocol version has its own set of strings, and one that is
|
|
* not recognized has none. */
|
|
ssl->cbmode = WOLFSSL_CB_MODE_READ;
|
|
ssl->cbtype = server_hello;
|
|
for (i = 0; i < (int)XELEM_CNT(protocols); i++) {
|
|
ssl->version.major = (byte)protocols[i].major;
|
|
ssl->version.minor = (byte)protocols[i].minor;
|
|
ExpectStrEQ(wolfSSL_state_string_long(ssl), protocols[i].str);
|
|
}
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test wolfSSL_set_connect_state() and wolfSSL_set_accept_state().
|
|
*
|
|
* Each switches the side the object will act as.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_set_connect_accept_state(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) \
|
|
&& !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \
|
|
&& !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
/* A NULL object is ignored by both. */
|
|
wolfSSL_set_connect_state(NULL);
|
|
wolfSSL_set_accept_state(NULL);
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* Created from a client method. */
|
|
ExpectIntEQ(wolfSSL_is_server(ssl), 0);
|
|
|
|
/* Switch to acting as a server, then back. */
|
|
wolfSSL_set_accept_state(ssl);
|
|
ExpectIntEQ(wolfSSL_is_server(ssl), 1);
|
|
wolfSSL_set_connect_state(ssl);
|
|
ExpectIntEQ(wolfSSL_is_server(ssl), 0);
|
|
|
|
/* Setting the same side again is harmless. */
|
|
wolfSSL_set_connect_state(ssl);
|
|
ExpectIntEQ(wolfSSL_is_server(ssl), 0);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test wolfSSL_SSL_do_handshake().
|
|
*
|
|
* Drives a handshake through the OpenSSL-compatibility entry point, which
|
|
* dispatches to connect or accept based on the side of the object.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_SSL_do_handshake(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) \
|
|
&& defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \
|
|
&& !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int i;
|
|
int cRet = WOLFSSL_FATAL_ERROR;
|
|
int sRet = WOLFSSL_FATAL_ERROR;
|
|
|
|
/* A NULL object is rejected. */
|
|
ExpectIntEQ(wolfSSL_SSL_do_handshake(NULL), WOLFSSL_FAILURE);
|
|
|
|
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);
|
|
|
|
/* Alternate the two sides until both report the handshake is complete. */
|
|
for (i = 0; i < 10; i++) {
|
|
if (cRet != WOLFSSL_SUCCESS) {
|
|
cRet = wolfSSL_SSL_do_handshake(ssl_c);
|
|
}
|
|
if (sRet != WOLFSSL_SUCCESS) {
|
|
sRet = wolfSSL_SSL_do_handshake(ssl_s);
|
|
}
|
|
if ((cRet == WOLFSSL_SUCCESS) && (sRet == WOLFSSL_SUCCESS)) {
|
|
break;
|
|
}
|
|
}
|
|
ExpectIntEQ(cRet, WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(sRet, WOLFSSL_SUCCESS);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test wolfSSL_SSL_in_init(), wolfSSL_SSL_in_before() and
|
|
* wolfSSL_SSL_in_connect_init().
|
|
*
|
|
* The three report where in the handshake the object is.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_SSL_in_init_hs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) \
|
|
&& defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \
|
|
&& !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* NULL objects report no state. */
|
|
ExpectIntEQ(wolfSSL_SSL_in_before(NULL), WOLFSSL_FAILURE);
|
|
ExpectIntEQ(wolfSSL_SSL_in_connect_init(NULL), WOLFSSL_FAILURE);
|
|
|
|
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);
|
|
|
|
/* Nothing has happened yet. */
|
|
ExpectIntEQ(wolfSSL_SSL_in_before(ssl_c), 1);
|
|
ExpectIntEQ(wolfSSL_SSL_in_init(ssl_c), 1);
|
|
ExpectIntEQ(wolfSSL_SSL_in_connect_init(ssl_c), 0);
|
|
|
|
/* A partial handshake leaves the client mid-connect. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SSL_in_connect_init(ssl_c), 1);
|
|
ExpectIntEQ(wolfSSL_SSL_in_before(ssl_c), 1);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Once complete, none of the in-progress states hold. */
|
|
ExpectIntEQ(wolfSSL_SSL_in_init(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_SSL_in_before(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_SSL_in_connect_init(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_SSL_in_init(ssl_s), 0);
|
|
ExpectIntEQ(wolfSSL_SSL_in_connect_init(ssl_s), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test wolfSSL_is_init_finished().
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_is_init_finished(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) \
|
|
&& !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* A NULL object has not finished. */
|
|
ExpectIntEQ(wolfSSL_is_init_finished(NULL), 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_is_init_finished(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_is_init_finished(ssl_s), 0);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_is_init_finished(ssl_c), 1);
|
|
ExpectIntEQ(wolfSSL_is_init_finished(ssl_s), 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(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_HANDSHAKE_DONE_CB) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
/* Count of handshake-done callback invocations. */
|
|
static int test_ssl_hs_done_calls = 0;
|
|
|
|
/* Handshake-done callback recording that it ran.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @param [in] user_ctx User context; expected to be &test_ssl_hs_done_calls.
|
|
* @return 0 to let the handshake complete.
|
|
*/
|
|
static int test_ssl_hs_done_cb(WOLFSSL* ssl, void* user_ctx)
|
|
{
|
|
(void)ssl;
|
|
|
|
if (user_ctx == &test_ssl_hs_done_calls) {
|
|
test_ssl_hs_done_calls++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Test wolfSSL_SetHsDoneCb().
|
|
*
|
|
* The registered callback must run when the handshake completes.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_SetHsDoneCb(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) \
|
|
&& !defined(NO_HANDSHAKE_DONE_CB) && !defined(NO_TLS) \
|
|
&& !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* A NULL object is rejected. */
|
|
ExpectIntEQ(wolfSSL_SetHsDoneCb(NULL, test_ssl_hs_done_cb, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
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);
|
|
|
|
test_ssl_hs_done_calls = 0;
|
|
ExpectIntEQ(wolfSSL_SetHsDoneCb(ssl_c, test_ssl_hs_done_cb,
|
|
&test_ssl_hs_done_calls), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* The client's callback ran; the server had none registered. */
|
|
ExpectIntEQ(test_ssl_hs_done_calls, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test the public-key callback context accessors.
|
|
*
|
|
* Each Set/Get pair stores and returns an opaque application pointer, and each
|
|
* accessor tolerates a NULL object.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_pk_callback_ctx(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_PK_CALLBACKS) && !defined(NO_CERTS) \
|
|
&& !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) \
|
|
&& !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
int marker = 0;
|
|
void* p = ▮
|
|
|
|
/* NULL objects are tolerated by every accessor. */
|
|
ExpectNull(wolfSSL_GetGenPreMasterCtx(NULL));
|
|
ExpectNull(wolfSSL_GetGenMasterSecretCtx(NULL));
|
|
ExpectNull(wolfSSL_GetGenSessionKeyCtx(NULL));
|
|
ExpectNull(wolfSSL_GetEncryptKeysCtx(NULL));
|
|
ExpectNull(wolfSSL_GetTlsFinishedCtx(NULL));
|
|
/* The VerifyMac accessors only exist when a MAC is used, so an AEAD-only
|
|
* build does not have them. */
|
|
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
|
|
ExpectNull(wolfSSL_GetVerifyMacCtx(NULL));
|
|
#endif
|
|
wolfSSL_SetGenPreMasterCtx(NULL, p);
|
|
wolfSSL_SetGenMasterSecretCtx(NULL, p);
|
|
wolfSSL_SetGenSessionKeyCtx(NULL, p);
|
|
wolfSSL_SetEncryptKeysCtx(NULL, p);
|
|
wolfSSL_SetTlsFinishedCtx(NULL, p);
|
|
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
|
|
wolfSSL_SetVerifyMacCtx(NULL, p);
|
|
#endif
|
|
wolfSSL_CTX_SetGenPreMasterCb(NULL, NULL);
|
|
wolfSSL_CTX_SetGenMasterSecretCb(NULL, NULL);
|
|
wolfSSL_CTX_SetGenSessionKeyCb(NULL, NULL);
|
|
wolfSSL_CTX_SetEncryptKeysCb(NULL, NULL);
|
|
wolfSSL_CTX_SetTlsFinishedCb(NULL, NULL);
|
|
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
|
|
wolfSSL_CTX_SetVerifyMacCb(NULL, NULL);
|
|
#endif
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* Nothing stored yet. */
|
|
ExpectNull(wolfSSL_GetGenPreMasterCtx(ssl));
|
|
ExpectNull(wolfSSL_GetGenMasterSecretCtx(ssl));
|
|
ExpectNull(wolfSSL_GetGenSessionKeyCtx(ssl));
|
|
ExpectNull(wolfSSL_GetEncryptKeysCtx(ssl));
|
|
ExpectNull(wolfSSL_GetTlsFinishedCtx(ssl));
|
|
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
|
|
ExpectNull(wolfSSL_GetVerifyMacCtx(ssl));
|
|
#endif
|
|
|
|
/* Each pair round-trips the pointer it was given. */
|
|
wolfSSL_SetGenPreMasterCtx(ssl, p);
|
|
ExpectPtrEq(wolfSSL_GetGenPreMasterCtx(ssl), p);
|
|
wolfSSL_SetGenMasterSecretCtx(ssl, p);
|
|
ExpectPtrEq(wolfSSL_GetGenMasterSecretCtx(ssl), p);
|
|
wolfSSL_SetGenSessionKeyCtx(ssl, p);
|
|
ExpectPtrEq(wolfSSL_GetGenSessionKeyCtx(ssl), p);
|
|
wolfSSL_SetEncryptKeysCtx(ssl, p);
|
|
ExpectPtrEq(wolfSSL_GetEncryptKeysCtx(ssl), p);
|
|
wolfSSL_SetTlsFinishedCtx(ssl, p);
|
|
ExpectPtrEq(wolfSSL_GetTlsFinishedCtx(ssl), p);
|
|
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
|
|
wolfSSL_SetVerifyMacCtx(ssl, p);
|
|
ExpectPtrEq(wolfSSL_GetVerifyMacCtx(ssl), p);
|
|
#endif
|
|
|
|
/* The context-level callback setters accept a NULL callback. */
|
|
wolfSSL_CTX_SetGenPreMasterCb(ctx, NULL);
|
|
wolfSSL_CTX_SetGenMasterSecretCb(ctx, NULL);
|
|
wolfSSL_CTX_SetGenSessionKeyCb(ctx, NULL);
|
|
wolfSSL_CTX_SetEncryptKeysCb(ctx, NULL);
|
|
wolfSSL_CTX_SetTlsFinishedCb(ctx, NULL);
|
|
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
|
|
wolfSSL_CTX_SetVerifyMacCb(ctx, NULL);
|
|
#endif
|
|
|
|
/* Not guarded on HAVE_EXTENDED_MASTER: these three are defined for the
|
|
* whole of HAVE_PK_CALLBACKS and back onto fields that are unconditional
|
|
* too, so they are reachable whether or not the extended master secret
|
|
* is built in. */
|
|
ExpectNull(wolfSSL_GetGenExtMasterSecretCtx(NULL));
|
|
wolfSSL_SetGenExtMasterSecretCtx(NULL, p);
|
|
wolfSSL_CTX_SetGenExtMasterSecretCb(NULL, NULL);
|
|
ExpectNull(wolfSSL_GetGenExtMasterSecretCtx(ssl));
|
|
wolfSSL_SetGenExtMasterSecretCtx(ssl, p);
|
|
ExpectPtrEq(wolfSSL_GetGenExtMasterSecretCtx(ssl), p);
|
|
wolfSSL_CTX_SetGenExtMasterSecretCb(ctx, NULL);
|
|
|
|
#ifdef WOLFSSL_TLS13
|
|
ExpectNull(wolfSSL_GetHKDFExtractCtx(NULL));
|
|
wolfSSL_SetHKDFExtractCtx(NULL, p);
|
|
wolfSSL_CTX_SetHKDFExtractCb(NULL, NULL);
|
|
ExpectNull(wolfSSL_GetHKDFExtractCtx(ssl));
|
|
wolfSSL_SetHKDFExtractCtx(ssl, p);
|
|
ExpectPtrEq(wolfSSL_GetHKDFExtractCtx(ssl), p);
|
|
wolfSSL_CTX_SetHKDFExtractCb(ctx, NULL);
|
|
wolfSSL_CTX_SetHKDFExpandLabelCb(NULL, NULL);
|
|
wolfSSL_CTX_SetHKDFExpandLabelCb(ctx, NULL);
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_PUBLIC_ASN
|
|
wolfSSL_CTX_SetProcessPeerCertCb(NULL, NULL);
|
|
wolfSSL_CTX_SetProcessPeerCertCb(ctx, NULL);
|
|
#endif
|
|
wolfSSL_CTX_SetProcessServerSigKexCb(NULL, NULL);
|
|
wolfSSL_CTX_SetProcessServerSigKexCb(ctx, NULL);
|
|
wolfSSL_CTX_SetPerformTlsRecordProcessingCb(NULL, NULL);
|
|
wolfSSL_CTX_SetPerformTlsRecordProcessingCb(ctx, NULL);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a static ECC key survives wolfSSL_set_accept_state().
|
|
*
|
|
* The check that the key is an EC key decodes it, so under
|
|
* WOLFSSL_BLIND_PRIVATE_KEY it has to be unmasked first. Decoding the masked
|
|
* bytes always fails, which silently withdrew the ECC capabilities for a key
|
|
* that was perfectly good.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_set_accept_state_static_ecc(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_EXTRA) || \
|
|
defined(WOLFSSL_WPAS_SMALL)) && \
|
|
defined(HAVE_ECC) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
/* The key is only re-checked when a client object is switched to being a
|
|
* server, so start from a client. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, eccCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, eccKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
if (ssl != NULL) {
|
|
ssl->options.haveStaticECC = 1;
|
|
ssl->options.haveECC = 1;
|
|
ssl->options.haveECDSAsig = 1;
|
|
|
|
wolfSSL_set_accept_state(ssl);
|
|
|
|
/* The key really is an EC key, so nothing may be withdrawn. Without
|
|
* unmasking, the decode fails and all three are cleared. */
|
|
ExpectIntEQ(ssl->options.haveStaticECC, 1);
|
|
ExpectIntEQ(ssl->options.haveECC, 1);
|
|
ExpectIntEQ(ssl->options.haveECDSAsig, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test the client-to-server switch in wolfSSL_set_accept_state().
|
|
*
|
|
* Switching a client object to act as a server re-checks a static ECC key and
|
|
* adopts any DH parameters the context has since acquired. A key that is not
|
|
* an EC key must clear the ECC capability flags rather than be trusted.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_set_accept_state_reinit(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) \
|
|
&& !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \
|
|
&& !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_TLS) \
|
|
&& !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
/* Declared only when at least one of the cases below is built. */
|
|
#if defined(HAVE_ECC) || (!defined(NO_DH) && !defined(NO_RSA))
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
#endif
|
|
|
|
#if defined(HAVE_ECC) && !defined(NO_RSA)
|
|
/* An RSA key cannot be decoded as an EC key, so the static ECC
|
|
* capability must be withdrawn. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
if (ssl != NULL) {
|
|
ExpectNotNull(ssl->buffers.key);
|
|
ssl->options.haveStaticECC = 1;
|
|
ssl->options.haveECC = 1;
|
|
ssl->options.haveECDSAsig = 1;
|
|
|
|
wolfSSL_set_accept_state(ssl);
|
|
|
|
ExpectIntEQ(ssl->options.haveStaticECC, 0);
|
|
ExpectIntEQ(ssl->options.haveECC, 0);
|
|
ExpectIntEQ(ssl->options.haveECDSAsig, 0);
|
|
ExpectIntEQ(wolfSSL_is_server(ssl), 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
#endif /* HAVE_ECC && !NO_RSA */
|
|
|
|
#ifdef HAVE_ECC
|
|
/* A real EC key decodes, so the flags are left alone. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, eccKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
if (ssl != NULL) {
|
|
ssl->options.haveStaticECC = 1;
|
|
ssl->options.haveECC = 1;
|
|
|
|
wolfSSL_set_accept_state(ssl);
|
|
|
|
ExpectIntEQ(ssl->options.haveStaticECC, 1);
|
|
ExpectIntEQ(ssl->options.haveECC, 1);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
#endif /* HAVE_ECC */
|
|
|
|
#if !defined(NO_DH) && (!defined(NO_RSA) || defined(HAVE_ECC))
|
|
/* DH parameters added to the context after the object was created are
|
|
* picked up when the object becomes a server. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* Not inherited at creation time - the context had none then. */
|
|
if (ssl != NULL) {
|
|
ExpectIntEQ(ssl->options.haveDH, 0);
|
|
}
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx, dhParamFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
|
|
wolfSSL_set_accept_state(ssl);
|
|
|
|
/* Picked up as the object's own copy, not the context's buffers. */
|
|
if ((ssl != NULL) && (ctx != NULL)) {
|
|
ExpectIntEQ(ssl->options.haveDH, 1);
|
|
ExpectNotNull(ssl->buffers.serverDH_P.buffer);
|
|
ExpectNotNull(ssl->buffers.serverDH_G.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);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
#endif /* !NO_DH */
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test the argument checks of wolfSSL_negotiate() and wolfSSL_connect_cert().
|
|
*
|
|
* Both reject a NULL object, each with its own failure code.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_negotiate_bad_args(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifndef NO_TLS
|
|
ExpectIntEQ(wolfSSL_negotiate(NULL), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
#if !defined(NO_WOLFSSL_CLIENT)
|
|
ExpectIntEQ(wolfSSL_connect_cert(NULL), WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_QUIC) && defined(WOLFSSL_TLS13) && \
|
|
(defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)
|
|
/* QUIC callbacks that do nothing. wolfSSL_SSL_do_handshake() only needs the
|
|
* method to be set so that the object counts as a QUIC one. */
|
|
|
|
/* QUIC callback that takes the traffic secrets and does nothing with them.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @param [in] level Encryption level. Unused.
|
|
* @param [in] rx Secret for reading. Unused.
|
|
* @param [in] tx Secret for writing. Unused.
|
|
* @param [in] len Length of each secret. Unused.
|
|
* @return 1 always.
|
|
*/
|
|
static int test_ssl_hs_quic_secrets(WOLFSSL* ssl,
|
|
WOLFSSL_ENCRYPTION_LEVEL level, const uint8_t* rx, const uint8_t* tx,
|
|
size_t len)
|
|
{
|
|
(void)ssl;
|
|
(void)level;
|
|
(void)rx;
|
|
(void)tx;
|
|
(void)len;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* QUIC callback that accepts handshake data and does nothing with it.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @param [in] level Encryption level. Unused.
|
|
* @param [in] data Handshake data. Unused.
|
|
* @param [in] len Length of the data. Unused.
|
|
* @return 1 always.
|
|
*/
|
|
static int test_ssl_hs_quic_add_data(WOLFSSL* ssl,
|
|
WOLFSSL_ENCRYPTION_LEVEL level, const uint8_t* data, size_t len)
|
|
{
|
|
(void)ssl;
|
|
(void)level;
|
|
(void)data;
|
|
(void)len;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* QUIC callback that reports the data as flushed without doing anything.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @return 1 always.
|
|
*/
|
|
static int test_ssl_hs_quic_flush(WOLFSSL* ssl)
|
|
{
|
|
(void)ssl;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* QUIC callback that accepts an alert and does nothing with it.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @param [in] level Encryption level. Unused.
|
|
* @param [in] err Alert to send. Unused.
|
|
* @return 1 always.
|
|
*/
|
|
static int test_ssl_hs_quic_alert(WOLFSSL* ssl,
|
|
WOLFSSL_ENCRYPTION_LEVEL level, uint8_t err)
|
|
{
|
|
(void)ssl;
|
|
(void)level;
|
|
(void)err;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static WOLFSSL_QUIC_METHOD test_ssl_hs_quic_method = {
|
|
test_ssl_hs_quic_secrets,
|
|
test_ssl_hs_quic_add_data,
|
|
test_ssl_hs_quic_flush,
|
|
test_ssl_hs_quic_alert
|
|
};
|
|
#endif
|
|
|
|
/* Test that wolfSSL_SSL_do_handshake() dispatches QUIC objects to the QUIC
|
|
* handshake rather than to connect or accept.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_SSL_do_handshake_quic(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_QUIC) && defined(WOLFSSL_TLS13) && \
|
|
(defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* Not a QUIC object yet. */
|
|
if (ssl != NULL) {
|
|
ExpectNull(ssl->quic.method);
|
|
}
|
|
|
|
ExpectIntEQ(wolfSSL_set_quic_method(ssl, &test_ssl_hs_quic_method),
|
|
WOLFSSL_SUCCESS);
|
|
if (ssl != NULL) {
|
|
ExpectNotNull(ssl->quic.method);
|
|
}
|
|
|
|
/* The QUIC handshake is attempted. With no transport parameters set and
|
|
* nothing to read it cannot complete, so only the dispatch is checked. */
|
|
ExpectIntNE(wolfSSL_SSL_do_handshake(ssl), WOLFSSL_SUCCESS);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that wolfSSL_set_connect_state() discards server DH parameters.
|
|
*
|
|
* A client generates its own DH parameters, so any server ones are dropped.
|
|
* The object holds its own copy either way, whether set on it directly or
|
|
* taken from the context, so the context is left untouched.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_set_connect_state_dh(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) \
|
|
&& !defined(NO_DH) && !defined(NO_WOLFSSL_SERVER) \
|
|
&& !defined(NO_WOLFSSL_CLIENT) && !defined(NO_FILESYSTEM) \
|
|
&& !defined(NO_CERTS) && !defined(NO_TLS) && !defined(NO_RSA) \
|
|
&& !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL* ssl2 = NULL;
|
|
|
|
/* Parameters owned by the object are freed. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
ExpectIntEQ(wolfSSL_SetTmpDH_file(ssl, dhParamFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
if (ssl != NULL) {
|
|
ExpectNotNull(ssl->buffers.serverDH_P.buffer);
|
|
ExpectNotNull(ssl->buffers.serverDH_G.buffer);
|
|
ExpectIntEQ(ssl->buffers.weOwnDH, 1);
|
|
}
|
|
|
|
wolfSSL_set_connect_state(ssl);
|
|
|
|
/* Freed and unlinked; the object is now a client. */
|
|
if (ssl != NULL) {
|
|
ExpectNull(ssl->buffers.serverDH_P.buffer);
|
|
ExpectNull(ssl->buffers.serverDH_G.buffer);
|
|
}
|
|
ExpectIntEQ(wolfSSL_is_server(ssl), 0);
|
|
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* Parameters taken from the context are a copy the object owns. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx, dhParamFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* Copied from the context, so owned here. */
|
|
if ((ssl != NULL) && (ctx != NULL)) {
|
|
ExpectNotNull(ssl->buffers.serverDH_P.buffer);
|
|
ExpectPtrNE(ssl->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer);
|
|
ExpectIntEQ(ssl->buffers.serverDH_P.length, ctx->serverDH_P.length);
|
|
ExpectBufEQ(ssl->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer,
|
|
ctx->serverDH_P.length);
|
|
ExpectIntEQ(ssl->buffers.weOwnDH, 1);
|
|
}
|
|
|
|
wolfSSL_set_connect_state(ssl);
|
|
|
|
if (ssl != NULL) {
|
|
ExpectNull(ssl->buffers.serverDH_P.buffer);
|
|
ExpectNull(ssl->buffers.serverDH_G.buffer);
|
|
}
|
|
/* The context kept its own copy, so it can still be used. */
|
|
if (ctx != NULL) {
|
|
ExpectNotNull(ctx->serverDH_P.buffer);
|
|
ExpectNotNull(ctx->serverDH_G.buffer);
|
|
}
|
|
ExpectNotNull(ssl2 = wolfSSL_new(ctx));
|
|
if ((ssl2 != NULL) && (ctx != NULL)) {
|
|
ExpectNotNull(ssl2->buffers.serverDH_P.buffer);
|
|
ExpectPtrNE(ssl2->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer);
|
|
ExpectIntEQ(ssl2->buffers.serverDH_P.length, ctx->serverDH_P.length);
|
|
ExpectBufEQ(ssl2->buffers.serverDH_P.buffer, ctx->serverDH_P.buffer,
|
|
ctx->serverDH_P.length);
|
|
}
|
|
|
|
wolfSSL_free(ssl2);
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* DH parameters taken from the context are the object's own copy, given back
|
|
* at the end of the handshake and taken again when next needed.
|
|
*
|
|
* The copy is what lets the context replace its parameters while sessions are
|
|
* running. Holding it only for the handshake keeps the cost off connections
|
|
* that are established and idle.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_dh_ctx_params_reuse(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* Handing the object a second connection needs its certificate and key to
|
|
* outlast the first handshake, which only these builds arrange. */
|
|
#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(BUILD_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_DH) && !defined(NO_RSA) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_WOLFSSL_CLIENT)
|
|
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));
|
|
test_ctx.c_ciphers = test_ctx.s_ciphers = "DHE-RSA-AES128-GCM-SHA256";
|
|
|
|
/* The parameters go on the context before any session takes them. A
|
|
* context made here is used as-is, so it gets the credentials and the
|
|
* memio wiring that test_memio_setup would otherwise have given it. */
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx_s, test_ctx.s_ciphers),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx_s, dhParamFile, CERT_FILETYPE),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
/* test_memio_setup puts its own parameters on the server session, which
|
|
* would stand in for the ones under test. Take a fresh session from the
|
|
* same context so the parameters really are the context's. */
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
|
|
/* Its own copy, matching the context's but not the same buffers. */
|
|
if ((ssl_s != NULL) && (ctx_s != NULL)) {
|
|
ExpectNotNull(ssl_s->buffers.serverDH_P.buffer);
|
|
ExpectNotNull(ssl_s->buffers.serverDH_G.buffer);
|
|
ExpectPtrNE(ssl_s->buffers.serverDH_P.buffer, ctx_s->serverDH_P.buffer);
|
|
ExpectPtrNE(ssl_s->buffers.serverDH_G.buffer, ctx_s->serverDH_G.buffer);
|
|
ExpectIntEQ(ssl_s->buffers.serverDH_P.length, ctx_s->serverDH_P.length);
|
|
ExpectIntEQ(ssl_s->buffers.serverDH_G.length, ctx_s->serverDH_G.length);
|
|
ExpectBufEQ(ssl_s->buffers.serverDH_P.buffer, ctx_s->serverDH_P.buffer,
|
|
ctx_s->serverDH_P.length);
|
|
ExpectBufEQ(ssl_s->buffers.serverDH_G.buffer, ctx_s->serverDH_G.buffer,
|
|
ctx_s->serverDH_G.length);
|
|
ExpectIntEQ(ssl_s->buffers.weOwnDH, 1);
|
|
}
|
|
|
|
/* Offering no FFDHE group keeps the server on the parameters it was
|
|
* given, rather than switching to a named group and dropping them. A build
|
|
* without supported curves sends no groups at all, so there is nothing to
|
|
* narrow down. */
|
|
#ifdef HAVE_SUPPORTED_CURVES
|
|
ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl_c, WOLFSSL_ECC_SECP256R1),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* The handshake ran on them and then gave them back. A build that may
|
|
* renegotiate holds on to every handshake resource, parameters included. */
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->specs.kea, diffie_hellman_kea);
|
|
#ifndef HAVE_SECURE_RENEGOTIATION
|
|
ExpectNull(ssl_s->buffers.serverDH_P.buffer);
|
|
ExpectNull(ssl_s->buffers.serverDH_G.buffer);
|
|
#endif
|
|
}
|
|
|
|
/* So the object can be handed a second connection. */
|
|
ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS);
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
#ifdef HAVE_SUPPORTED_CURVES
|
|
ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl_c, WOLFSSL_ECC_SECP256R1),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
|
|
/* The second handshake took a fresh copy from the context. Without it
|
|
* there would be no parameters left to send and it would not complete. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(ssl_s->specs.kea, diffie_hellman_kea);
|
|
#ifndef HAVE_SECURE_RENEGOTIATION
|
|
ExpectNull(ssl_s->buffers.serverDH_P.buffer);
|
|
ExpectNull(ssl_s->buffers.serverDH_G.buffer);
|
|
#endif
|
|
}
|
|
|
|
/* And the context still has its own to hand out. */
|
|
if (ctx_s != NULL) {
|
|
ExpectNotNull(ctx_s->serverDH_P.buffer);
|
|
ExpectNotNull(ctx_s->serverDH_G.buffer);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_EXTRA) || \
|
|
defined(WOLFSSL_WPAS_SMALL)) && \
|
|
!defined(NO_DH) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \
|
|
!defined(NO_CERTS) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && defined(USE_WOLFSSL_MEMORY) && \
|
|
!defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY)
|
|
#define TEST_DH_OOM
|
|
|
|
/* Makes every allocation fail, so the DH copy is certain to run out of
|
|
* memory whichever allocations a build makes. */
|
|
static int dhOomFailAll = 0;
|
|
static int dhOomFailed = 0;
|
|
|
|
#ifdef WOLFSSL_DEBUG_MEMORY
|
|
static void* dhOomMalloc(size_t size, const char* func, unsigned int line)
|
|
{
|
|
(void)func;
|
|
(void)line;
|
|
#else
|
|
static void* dhOomMalloc(size_t size)
|
|
{
|
|
#endif
|
|
if (dhOomFailAll) {
|
|
dhOomFailed = 1;
|
|
return NULL;
|
|
}
|
|
return malloc(size);
|
|
}
|
|
|
|
#ifdef WOLFSSL_DEBUG_MEMORY
|
|
static void dhOomFree(void* ptr, const char* func, unsigned int line)
|
|
{
|
|
(void)func;
|
|
(void)line;
|
|
#else
|
|
static void dhOomFree(void* ptr)
|
|
{
|
|
#endif
|
|
free(ptr);
|
|
}
|
|
|
|
#ifdef WOLFSSL_DEBUG_MEMORY
|
|
static void* dhOomRealloc(void* ptr, size_t size, const char* func,
|
|
unsigned int line)
|
|
{
|
|
(void)func;
|
|
(void)line;
|
|
#else
|
|
static void* dhOomRealloc(void* ptr, size_t size)
|
|
{
|
|
#endif
|
|
if (dhOomFailAll) {
|
|
dhOomFailed = 1;
|
|
return NULL;
|
|
}
|
|
return realloc(ptr, size);
|
|
}
|
|
#endif /* TEST_DH_OOM */
|
|
|
|
/* Running out of memory while copying the context's DH parameters.
|
|
*
|
|
* wolfSSL_set_accept_state has nothing to report a failure through, so it
|
|
* must leave the object without DH rather than claim parameters it does not
|
|
* hold. Every allocation fails here rather than a chosen one: the call
|
|
* reaches the copy whatever else runs out of memory, and only the copy
|
|
* failing can leave haveDH clear.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_dh_ctx_params_oom(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_DH_OOM
|
|
wolfSSL_Malloc_cb prevM = NULL;
|
|
wolfSSL_Free_cb prevF = NULL;
|
|
wolfSSL_Realloc_cb prevR = NULL;
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
/* The parameters reach the context after the object is made, so the
|
|
* object has none of its own to lose. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_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);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_CTX_SetTmpDH_file(ctx, dhParamFile, CERT_FILETYPE),
|
|
WOLFSSL_SUCCESS);
|
|
if (ssl != NULL) {
|
|
ExpectIntEQ(ssl->options.haveDH, 0);
|
|
ExpectNull(ssl->buffers.serverDH_P.buffer);
|
|
}
|
|
|
|
ExpectIntEQ(wolfSSL_GetAllocators(&prevM, &prevF, &prevR), 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(wolfSSL_SetAllocators(dhOomMalloc, dhOomFree,
|
|
dhOomRealloc), 0);
|
|
dhOomFailed = 0;
|
|
dhOomFailAll = 1;
|
|
wolfSSL_set_accept_state(ssl);
|
|
dhOomFailAll = 0;
|
|
(void)wolfSSL_SetAllocators(prevM, prevF, prevR);
|
|
}
|
|
|
|
ExpectIntEQ(dhOomFailed, 1);
|
|
if (ssl != NULL) {
|
|
ExpectIntEQ(ssl->options.haveDH, 0);
|
|
ExpectNull(ssl->buffers.serverDH_P.buffer);
|
|
ExpectNull(ssl->buffers.serverDH_G.buffer);
|
|
}
|
|
/* The context kept its own, so a later object still gets them. */
|
|
if (ctx != NULL) {
|
|
ExpectNotNull(ctx->serverDH_P.buffer);
|
|
ExpectNotNull(ctx->serverDH_G.buffer);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The helpers below only install I/O callbacks, so they need nothing beyond
|
|
* TLS 1.2 itself. The guard is nevertheless the union of their six callers'
|
|
* guards, so the block is neither compiled without a caller nor missing when
|
|
* one is present. The first term covers the five callers that drive a
|
|
* handshake with real credentials; the second covers the memio caller, which
|
|
* runs without RSA when raw public keys are enabled. */
|
|
#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
((!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \
|
|
defined(WOLFSSL_PEM_TO_DER)) || \
|
|
(defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)))
|
|
/* Transport send callback that always fails.
|
|
*
|
|
* Makes every handshake message send fail so that the error handling of each
|
|
* step of wolfSSL_connect()/wolfSSL_accept() can be reached.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @param [in] buf Data to send. Unused.
|
|
* @param [in] sz Length of data. Unused.
|
|
* @param [in] ctx I/O context. Unused.
|
|
* @return WOLFSSL_CBIO_ERR_GENERAL always.
|
|
*/
|
|
static int test_ssl_hs_send_fail(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)sz;
|
|
(void)ctx;
|
|
|
|
return WOLFSSL_CBIO_ERR_GENERAL;
|
|
}
|
|
|
|
#if !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
/* Fail every send, recording the content type of the first record offered.
|
|
*
|
|
* The first byte of a TLS record is its content type, so this reports what
|
|
* the object tried to send first: 'alert' when a pending alert was retried,
|
|
* 'handshake' when it went straight on with the handshake instead. Without
|
|
* this the two are indistinguishable - both fail the send and both record
|
|
* SOCKET_ERROR_E.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @param [in] buf Record being sent.
|
|
* @param [in] sz Length of buf in bytes.
|
|
* @param [in, out] ctx int holding the first content type seen. Must be set
|
|
* to a value no content type uses before the call.
|
|
* @return WOLFSSL_CBIO_ERR_GENERAL always.
|
|
*/
|
|
static int test_ssl_hs_send_fail_record(WOLFSSL* ssl, char* buf, int sz,
|
|
void* ctx)
|
|
{
|
|
int* first = (int*)ctx;
|
|
|
|
(void)ssl;
|
|
|
|
if ((first != NULL) && (*first < 0) && (sz > 0)) {
|
|
*first = (int)(unsigned char)buf[0];
|
|
}
|
|
|
|
return WOLFSSL_CBIO_ERR_GENERAL;
|
|
}
|
|
#endif /* !NO_CERTS && !NO_FILESYSTEM && !NO_RSA && WOLFSSL_PEM_TO_DER */
|
|
|
|
/* Transport receive callback that always fails.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @param [in] buf Buffer to fill. Unused.
|
|
* @param [in] sz Size of buffer. Unused.
|
|
* @param [in] ctx I/O context. Unused.
|
|
* @return WOLFSSL_CBIO_ERR_GENERAL always.
|
|
*/
|
|
static int test_ssl_hs_recv_fail(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)sz;
|
|
(void)ctx;
|
|
|
|
return WOLFSSL_CBIO_ERR_GENERAL;
|
|
}
|
|
|
|
/* Require server credentials so that the checks in wolfSSL_accept() are made.
|
|
*
|
|
* Anonymous cipher suites are available by default in some builds and they let
|
|
* a server run without a certificate, which skips the credential checks.
|
|
*
|
|
* Only used by the tests that need server credentials, so it is guarded to
|
|
* match them rather than the callbacks above.
|
|
*
|
|
* @param [in, out] ssl SSL/TLS object.
|
|
*/
|
|
#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(WOLFSSL_PEM_TO_DER) && !defined(NO_CERTS) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_RSA)
|
|
static void test_ssl_hs_need_creds(WOLFSSL* ssl)
|
|
{
|
|
if (ssl != NULL) {
|
|
#ifdef HAVE_ANON
|
|
ssl->options.useAnon = 0;
|
|
#endif
|
|
#ifndef NO_PSK
|
|
ssl->options.havePSK = 0;
|
|
#endif
|
|
#ifdef WOLFSSL_MULTICAST
|
|
ssl->options.haveMcast = 0;
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Replace the transport of an object with one that always fails.
|
|
*
|
|
* Lets a handshake step be driven to its error handling without a peer and
|
|
* without blocking on a socket.
|
|
*
|
|
* @param [in, out] ssl SSL/TLS object.
|
|
*/
|
|
static void test_ssl_hs_break_io(WOLFSSL* ssl)
|
|
{
|
|
if (ssl != NULL) {
|
|
wolfSSL_SSLSetIOSend(ssl, test_ssl_hs_send_fail);
|
|
wolfSSL_SSLSetIORecv(ssl, test_ssl_hs_recv_fail);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Test the argument and side checks of wolfSSL_connect().
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_connect_bad_args(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
/* A NULL object is rejected. */
|
|
ExpectIntEQ(wolfSSL_connect(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* Connecting with a server object is a side error. Credentials are loaded
|
|
* because a server object cannot be created without them when there are no
|
|
* anonymous cipher suites. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
ExpectIntEQ(wolfSSL_connect(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, 0), WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* An unrecognized connect state is rejected. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
if (ssl != NULL) {
|
|
ssl->options.connectState = 0x7f;
|
|
ExpectIntEQ(wolfSSL_connect(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test the argument, side and credential checks of wolfSSL_accept().
|
|
*
|
|
* A server needs both a certificate and a private key unless a certificate
|
|
* setup callback is installed to supply them later.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_accept_bad_args(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_CERTS) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_RSA) && !defined(WOLFSSL_NO_TLS12) \
|
|
&& defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
/* A NULL object is rejected. */
|
|
ExpectIntEQ(wolfSSL_accept(NULL), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
|
|
/* Accepting with a client object is a side error. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, 0), WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* No certificate. It is hidden from the SSL object rather than left
|
|
* unloaded, because a server object cannot be created without credentials
|
|
* when there are no anonymous cipher suites. The pointer is put back
|
|
* afterwards so that the object still disposes of it: depending on the
|
|
* build the object owns this buffer rather than the context. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
test_ssl_hs_need_creds(ssl);
|
|
if (ssl != NULL) {
|
|
DerBuffer* savedCert = ssl->buffers.certificate;
|
|
|
|
ssl->buffers.certificate = NULL;
|
|
ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, 0), WC_NO_ERR_TRACE(NO_PRIVATE_KEY));
|
|
ssl->buffers.certificate = savedCert;
|
|
}
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* Certificate present but no private key. The key is dropped from the SSL
|
|
* object rather than left unloaded so that the certificate check above is
|
|
* passed first; the context still owns the real key. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
test_ssl_hs_need_creds(ssl);
|
|
if (ssl != NULL) {
|
|
DerBuffer* savedKey = ssl->buffers.key;
|
|
int savedDevId = ssl->devId;
|
|
|
|
ExpectNotNull(ssl->buffers.certificate);
|
|
ssl->buffers.key = NULL;
|
|
ssl->devId = INVALID_DEVID;
|
|
ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, 0), WC_NO_ERR_TRACE(NO_PRIVATE_KEY));
|
|
/* Put both back so the object disposes of it as it would have. */
|
|
ssl->buffers.key = savedKey;
|
|
ssl->devId = savedDevId;
|
|
}
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* An unrecognized accept state is rejected. Credentials are loaded so the
|
|
* checks above are passed. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
if (ssl != NULL) {
|
|
ssl->options.acceptState = 0x7f;
|
|
ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that each step of wolfSSL_connect() copes with a failing transport.
|
|
*
|
|
* The object is placed in each connect state in turn with a transport that
|
|
* always fails, so that every step is entered. The call must reach a
|
|
* definite outcome rather than hang; which error arm ran is not
|
|
* observable from here. The object is
|
|
* discarded after each attempt because a failed handshake is not resumable.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_connect_step_failures(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_CERTS) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_RSA) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
/* Each connect state, paired with whether it still has something to send.
|
|
* One that does must fail on the broken transport; one that does not may
|
|
* report success. Pairing them here rather than splitting the list at an
|
|
* index means adding a state forces its expectation to be written down,
|
|
* instead of silently shifting the boundary for its neighbours. */
|
|
static const struct {
|
|
int state;
|
|
int sends;
|
|
} states[] = {
|
|
{ CONNECT_BEGIN, 1 },
|
|
{ CLIENT_HELLO_SENT, 1 },
|
|
{ HELLO_AGAIN, 1 },
|
|
{ HELLO_AGAIN_REPLY, 1 },
|
|
{ FIRST_REPLY_DONE, 1 },
|
|
{ FIRST_REPLY_FIRST, 1 },
|
|
{ FIRST_REPLY_SECOND, 1 },
|
|
{ FIRST_REPLY_THIRD, 1 },
|
|
{ FIRST_REPLY_FOURTH, 1 },
|
|
{ FINISHED_DONE, 1 },
|
|
{ SECOND_REPLY_DONE, 0 }
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i < (int)(sizeof(states) / sizeof(states[0])); i++) {
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
int ret = 0;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx, caCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
|
|
if (ssl != NULL) {
|
|
/* Pretend the handshake reached this step, and that the peer was
|
|
* authenticated so the fail-safe check is passed. A client
|
|
* certificate is requested so the certificate steps are taken. */
|
|
ssl->options.connectState = (byte)states[i].state;
|
|
ssl->options.peerAuthGood = 1;
|
|
ssl->options.sendVerify = SEND_CERT;
|
|
ssl->options.resuming = 0;
|
|
|
|
ret = wolfSSL_connect(ssl);
|
|
|
|
/* A step that sends must fail on the broken transport; only a
|
|
* state with nothing left to send may report success. Both arms
|
|
* are cast as they come from different enumerations, which a C++
|
|
* compiler will not mix in a conditional. */
|
|
ExpectIntEQ(ret, states[i].sends ?
|
|
(int)WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR) :
|
|
(int)WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that each step of wolfSSL_accept() copes with a failing transport.
|
|
*
|
|
* The object is placed in each accept state in turn with a transport that
|
|
* always fails, so that every step is entered. The call must reach a
|
|
* definite outcome rather than hang; which error arm ran is not
|
|
* observable from here.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_accept_step_failures(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_TLS) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_CERTS) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_RSA) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
/* As in test_wolfSSL_connect_step_failures(): each state paired with
|
|
* whether it still has something to send, so that adding one cannot
|
|
* silently change what is expected of another. Only states the TLS 1.2
|
|
* accept switch has a case for are listed - ACCEPT_HELLO_RETRY_REQUEST_DONE
|
|
* and CERT_VERIFY_SENT belong to the TLS 1.3 machine and would exercise
|
|
* the unrecognized-state path, which test_wolfSSL_accept_bad_args covers
|
|
* directly. */
|
|
static const struct {
|
|
int state;
|
|
int sends;
|
|
} states[] = {
|
|
{ ACCEPT_BEGIN, 1 },
|
|
/* These two have a case only when their feature is built in.
|
|
* Without it the state is unrecognized, which returns the same
|
|
* value for a different reason, so they are left out rather than
|
|
* appearing to cover a step that is not there. */
|
|
#ifdef HAVE_SECURE_RENEGOTIATION
|
|
{ ACCEPT_BEGIN_RENEG, 1 },
|
|
#endif
|
|
#ifdef WOLFSSL_TLS13
|
|
{ ACCEPT_CLIENT_HELLO_DONE, 1 },
|
|
#endif
|
|
{ ACCEPT_FIRST_REPLY_DONE, 1 },
|
|
{ SERVER_HELLO_SENT, 1 },
|
|
{ CERT_SENT, 1 },
|
|
{ CERT_STATUS_SENT, 1 },
|
|
{ KEY_EXCHANGE_SENT, 1 },
|
|
{ CERT_REQ_SENT, 1 },
|
|
{ SERVER_HELLO_DONE, 1 },
|
|
{ ACCEPT_SECOND_REPLY_DONE, 1 },
|
|
{ TICKET_SENT, 1 },
|
|
{ CHANGE_CIPHER_SENT, 1 },
|
|
{ ACCEPT_FINISHED_DONE, 0 },
|
|
{ ACCEPT_THIRD_REPLY_DONE, 0 }
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i < (int)(sizeof(states) / sizeof(states[0])); i++) {
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
int ret = 0;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
|
|
if (ssl != NULL) {
|
|
ssl->options.acceptState = (byte)states[i].state;
|
|
/* Ask for a client certificate so the certificate request and
|
|
* verify steps are taken. */
|
|
ssl->options.verifyPeer = 1;
|
|
ssl->options.sendVerify = SEND_CERT;
|
|
/* Claim the client was authenticated so the fail-safe checks are
|
|
* passed and the steps after them are reached. */
|
|
ssl->options.peerAuthGood = 1;
|
|
#ifdef HAVE_SESSION_TICKET
|
|
/* Ask for a session ticket so that step is taken too. */
|
|
ssl->options.createTicket = 1;
|
|
ssl->options.noTicketTls12 = 0;
|
|
#endif
|
|
|
|
ret = wolfSSL_accept(ssl);
|
|
|
|
/* A step that sends fails on the broken transport. The states at
|
|
* the end of the handshake have nothing left to send and so
|
|
* report success. Both arms are cast as they come from different
|
|
* enumerations, which a C++ compiler will not mix in a
|
|
* conditional. */
|
|
ExpectIntEQ(ret, states[i].sends ?
|
|
(int)WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR) :
|
|
(int)WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a failure to flush the output buffer is reported.
|
|
*
|
|
* A send that reports "want write" leaves the message in the output buffer.
|
|
* Failing the send on the next call makes the flush at the start of
|
|
* wolfSSL_connect() and wolfSSL_accept() fail.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_hs_send_buffered_fail(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) && \
|
|
!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;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* Client: the ClientHello is left unsent. */
|
|
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);
|
|
test_memio_simulate_want_write(&test_ctx, 1, 1);
|
|
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) {
|
|
ExpectIntGT(ssl_c->buffers.outputBuffer.length, 0);
|
|
}
|
|
/* The retry now fails outright. */
|
|
test_ssl_hs_break_io(ssl_c);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WC_NO_ERR_TRACE(SOCKET_ERROR_E));
|
|
|
|
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;
|
|
|
|
/* Server: the ClientHello is delivered so the server has a reply to send,
|
|
* and that reply is left unsent. */
|
|
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_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ);
|
|
test_memio_simulate_want_write(&test_ctx, 0, 1);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WOLFSSL_ERROR_WANT_WRITE);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntGT(ssl_s->buffers.outputBuffer.length, 0);
|
|
}
|
|
test_ssl_hs_break_io(ssl_s);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WC_NO_ERR_TRACE(SOCKET_ERROR_E));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that flushing a buffered message advances the handshake state.
|
|
*
|
|
* A send that reports "want write" leaves the message in the output buffer and
|
|
* the state where it was, before the send. The flush at the start of the next
|
|
* wolfSSL_connect() or wolfSSL_accept() gets the message out and steps the
|
|
* state past that send, so the message is not built and sent a second time.
|
|
* This is the success side of the failures covered by
|
|
* test_wolfSSL_hs_send_buffered_fail().
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_hs_send_buffered_advance(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_TLS) && \
|
|
!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;
|
|
struct test_memio_ctx test_ctx;
|
|
int acceptState = -1;
|
|
|
|
/* Client: the ClientHello is left unsent. */
|
|
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);
|
|
test_memio_simulate_want_write(&test_ctx, 1, 1);
|
|
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) {
|
|
ExpectIntGT(ssl_c->buffers.outputBuffer.length, 0);
|
|
/* The send did not complete, so the state is still before it. */
|
|
ExpectIntEQ(ssl_c->options.connectState, CONNECT_BEGIN);
|
|
}
|
|
/* Nothing reached the peer. */
|
|
ExpectIntEQ(test_ctx.s_msg_count, 0);
|
|
|
|
/* With the transport working again the flush sends what was buffered and
|
|
* steps the state past the send. */
|
|
test_memio_simulate_want_write(&test_ctx, 1, 0);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->options.connectState, CLIENT_HELLO_SENT);
|
|
ExpectIntEQ(ssl_c->buffers.outputBuffer.length, 0);
|
|
}
|
|
/* Exactly one ClientHello was written: the state moved past the send, so
|
|
* the message was not built again. Without the advance there would be
|
|
* two. */
|
|
ExpectIntEQ(test_ctx.s_msg_count, 1);
|
|
|
|
/* The handshake carries on from there. */
|
|
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;
|
|
|
|
/* Server: the ClientHello is delivered so the server has a reply to send,
|
|
* and that reply is left unsent. The accept side is the one where the
|
|
* state to advance is chosen before the flush rather than after it. */
|
|
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_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ);
|
|
test_memio_simulate_want_write(&test_ctx, 0, 1);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WOLFSSL_ERROR_WANT_WRITE);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntGT(ssl_s->buffers.outputBuffer.length, 0);
|
|
/* The send did not complete, so the state is still before it. How
|
|
* far into its flight the server got before reaching the transport
|
|
* depends on the build, so the state is recorded rather than
|
|
* named. */
|
|
acceptState = ssl_s->options.acceptState;
|
|
}
|
|
ExpectIntEQ(test_ctx.c_msg_count, 0);
|
|
|
|
/* The flush sends what was buffered and steps the state past that send.
|
|
* The rest of the flight follows, and the server then waits on the
|
|
* client. */
|
|
test_memio_simulate_want_write(&test_ctx, 0, 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WOLFSSL_ERROR_WANT_READ);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntGT(ssl_s->options.acceptState, acceptState);
|
|
}
|
|
|
|
/* Not advancing would rebuild and resend the message the flush had
|
|
* already sent, leaving the client with a duplicate - which is what
|
|
* completing the handshake here rules out. */
|
|
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_CALLBACKS) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
/* Timeout callback that does nothing.
|
|
*
|
|
* Supplying one is what makes wolfSSL_ex_wrapper() set up the timer.
|
|
*
|
|
* @param [in] info Timeout information. Unused.
|
|
* @return 0 always.
|
|
*/
|
|
static int test_ssl_hs_to_cb(TimeoutInfo* info)
|
|
{
|
|
(void)info;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Test that connecting with no side established is reported as a failure.
|
|
*
|
|
* wolfSSL_ex_wrapper() dispatches on the side and leaves its result alone
|
|
* when neither the client nor the server branch runs, so the failure it was
|
|
* seeded with has to survive the timer setup that precedes the dispatch.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_connect_ex_no_side(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_CALLBACKS) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_TIMEVAL timeout;
|
|
|
|
/* Long enough that it cannot fire while the test runs. */
|
|
timeout.tv_sec = 60;
|
|
timeout.tv_usec = 0;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
if (ssl != NULL) {
|
|
/* With a timeout callback the timer is set up first. Its success must
|
|
* not be mistaken for the handshake's. */
|
|
ssl->options.side = WOLFSSL_NEITHER_END;
|
|
ExpectIntEQ(wolfSSL_connect_ex(ssl, NULL, test_ssl_hs_to_cb, timeout),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
/* And without one, where no timer is set up at all. */
|
|
ssl->options.side = WOLFSSL_NEITHER_END;
|
|
ExpectIntEQ(wolfSSL_connect_ex(ssl, NULL, NULL, timeout),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a failure to send a pending alert is reported.
|
|
*
|
|
* An alert that could not be sent earlier is retried at the start of
|
|
* wolfSSL_connect() and wolfSSL_accept().
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_hs_retry_alert_fail(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_CERTS) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_RSA) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
int firstType;
|
|
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx, caCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
firstType = -1;
|
|
wolfSSL_SSLSetIOSend(ssl, test_ssl_hs_send_fail_record);
|
|
wolfSSL_SetIOWriteCtx(ssl, &firstType);
|
|
if (ssl != NULL) {
|
|
ssl->pendingAlert.code = unexpected_message;
|
|
ssl->pendingAlert.level = alert_fatal;
|
|
ExpectIntEQ(wolfSSL_connect(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, 0),
|
|
WC_NO_ERR_TRACE(SOCKET_ERROR_E));
|
|
/* The pending alert is what it tried to send, not a ClientHello.
|
|
* Both fail with SOCKET_ERROR_E, so only this separates them. */
|
|
ExpectIntEQ(firstType, alert);
|
|
}
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
#endif
|
|
|
|
#ifndef NO_WOLFSSL_SERVER
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
test_ssl_hs_break_io(ssl);
|
|
firstType = -1;
|
|
wolfSSL_SSLSetIOSend(ssl, test_ssl_hs_send_fail_record);
|
|
wolfSSL_SetIOWriteCtx(ssl, &firstType);
|
|
if (ssl != NULL) {
|
|
ssl->pendingAlert.code = unexpected_message;
|
|
ssl->pendingAlert.level = alert_fatal;
|
|
ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, 0),
|
|
WC_NO_ERR_TRACE(SOCKET_ERROR_E));
|
|
ExpectIntEQ(firstType, alert);
|
|
}
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_HANDSHAKE_DONE_CB) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
/* Handshake done callback that refuses to let the handshake complete.
|
|
*
|
|
* @param [in] ssl SSL object. Unused.
|
|
* @param [in] user_ctx User context. Unused.
|
|
* @return A negative value to stop the handshake.
|
|
*/
|
|
static int test_ssl_hs_done_cb_fail(WOLFSSL* ssl, void* user_ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)user_ctx;
|
|
return -4242;
|
|
}
|
|
#endif
|
|
|
|
/* Test that a handshake done callback reporting an error stops the handshake.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_hs_done_cb_error(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_HANDSHAKE_DONE_CB) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
int i;
|
|
|
|
/* Register the callback on the client and then on the server. */
|
|
for (i = 0; i < 2; 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;
|
|
|
|
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_SetHsDoneCb((i == 0) ? ssl_c : ssl_s,
|
|
test_ssl_hs_done_cb_fail, NULL), WOLFSSL_SUCCESS);
|
|
|
|
/* The handshake cannot complete because the callback fails. */
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error((i == 0) ? ssl_c : ssl_s, 0), -4242);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
static int test_ssl_hs_info_calls_c = 0;
|
|
static int test_ssl_hs_info_calls_s = 0;
|
|
|
|
/* Information callback for the client, recording that it was called.
|
|
*
|
|
* @param [in] ssl SSL object. Unused.
|
|
* @param [in] type Type of event. Unused.
|
|
* @param [in] val Value associated with event. Unused.
|
|
*/
|
|
static void test_ssl_hs_info_cb_c(const WOLFSSL* ssl, int type, int val)
|
|
{
|
|
(void)ssl;
|
|
(void)type;
|
|
(void)val;
|
|
test_ssl_hs_info_calls_c++;
|
|
}
|
|
|
|
/* Information callback for the server, recording that it was called.
|
|
*
|
|
* @param [in] ssl SSL object. Unused.
|
|
* @param [in] type Type of event. Unused.
|
|
* @param [in] val Value associated with event. Unused.
|
|
*/
|
|
static void test_ssl_hs_info_cb_s(const WOLFSSL* ssl, int type, int val)
|
|
{
|
|
(void)ssl;
|
|
(void)type;
|
|
(void)val;
|
|
test_ssl_hs_info_calls_s++;
|
|
}
|
|
#endif
|
|
|
|
/* Test that the information callback is called when a handshake starts.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_hs_info_cb(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(OPENSSL_EXTRA) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
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);
|
|
|
|
test_ssl_hs_info_calls_c = 0;
|
|
test_ssl_hs_info_calls_s = 0;
|
|
wolfSSL_set_info_callback(ssl_c, test_ssl_hs_info_cb_c);
|
|
wolfSSL_set_info_callback(ssl_s, test_ssl_hs_info_cb_s);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Each side reported against its own callback. */
|
|
ExpectIntGT(test_ssl_hs_info_calls_c, 0);
|
|
ExpectIntGT(test_ssl_hs_info_calls_s, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Handshakes corrupted in flight.
|
|
*
|
|
* A conforming pair of endpoints produces one handshake, and every error path
|
|
* in the receive code stays dark no matter how many times it is run. Those
|
|
* paths are most of what remains uncovered in src/internal.c, and they are
|
|
* reached only by a peer that sends something wrong.
|
|
*
|
|
* No transport is needed to be that peer. test_memio already runs both
|
|
* endpoints through a plain byte buffer with credentials from certs/, and both
|
|
* sides are ours, so the buffer between them can be edited between rounds --
|
|
* flip a bit in a length, in a type byte, in the middle of a certificate, in
|
|
* the key exchange -- and the handshake continues into whatever the receiver
|
|
* does about it. Same idea as sitting on the wire with a packet mangler, with
|
|
* neither a socket nor a second process.
|
|
*
|
|
* The assertion is deliberately weak: a corrupted handshake is *expected* to
|
|
* fail. What is being tested is that it fails rather than crashes, leaks or
|
|
* hangs, and the coverage comes from the paths it takes on the way out.
|
|
* ------------------------------------------------------------------------- */
|
|
/* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES, not the _BUILD variant: the _BUILD
|
|
* macro is certificate-agnostic and is also defined for NO_CERTS builds, where
|
|
* test_memio_setup() installs neither credentials nor a PSK callback and no
|
|
* handshake can complete. This sweep asserts that unmodified handshakes do
|
|
* complete, so it needs the credential-bearing guard. Keep it identical on the
|
|
* test body below, or the helper is compiled unused. */
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && \
|
|
!defined(NO_WOLFSSL_WIRE_MANGLE_TESTS)
|
|
|
|
/* One handshake, with one byte flipped at one point.
|
|
*
|
|
* `dir` selects whose bytes get hit. test_memio_write_cb stores by the
|
|
* WRITER's side, so c_buff holds what the server wrote and s_buff what the
|
|
* client wrote:
|
|
*
|
|
* dir 0 (server -> client): one round of test_memio_do_handshake() runs the
|
|
* client and then the server, so at the end of a round the client's bytes
|
|
* have already been consumed and only the server's are in flight. Mangle
|
|
* c_buff.
|
|
*
|
|
* dir 1 (client -> server): step the client on its own so its output is
|
|
* left pending, then mangle s_buff before the server reads it. This is the
|
|
* only way to reach the server's receive and error paths -- driving whole
|
|
* rounds and mangling s_buff corrupted nothing at all, because s_buff was
|
|
* empty every time.
|
|
*
|
|
* `round` is how many exchange rounds to let pass first, which is what selects
|
|
* WHICH handshake message gets hit -- the ClientHello, the certificate, the
|
|
* key exchange, the Finished.
|
|
*
|
|
* Returns 1 if a byte was actually corrupted, 0 otherwise, and reports through
|
|
* *hsDone whether the handshake nevertheless completed on both sides. Without
|
|
* the second output a broken fixture -- no handshake ever completing -- looks
|
|
* exactly like a healthy sweep. */
|
|
static int test_wire_mangle_one(method_provider mc, method_provider ms,
|
|
int dir, int round, int off, byte mask,
|
|
int* hsDone)
|
|
{
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
int i;
|
|
int ret = 0;
|
|
|
|
if (hsDone != NULL)
|
|
*hsDone = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
if (test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, mc, ms)
|
|
!= 0) {
|
|
/* This build has no usable credentials for these methods; that is a
|
|
* configuration fact, not a failure. */
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return 0; /* no usable credentials: no mutation either */
|
|
}
|
|
|
|
for (i = 0; i < 12; i++) {
|
|
int rounds = 0;
|
|
|
|
if (i == round && dir == 1) {
|
|
/* Client only, so what it writes stays queued in s_buff. */
|
|
(void)wolfSSL_connect(ssl_c);
|
|
}
|
|
else {
|
|
(void)test_memio_do_handshake(ssl_c, ssl_s, 1, &rounds);
|
|
}
|
|
|
|
if (i == round) {
|
|
byte* buf = (dir == 0) ? test_ctx.c_buff : test_ctx.s_buff;
|
|
int len = (dir == 0) ? test_ctx.c_len : test_ctx.s_len;
|
|
|
|
if (len > off) {
|
|
buf[off] ^= mask;
|
|
ret = 1; /* a byte was actually corrupted */
|
|
}
|
|
/* else: nothing in flight at this round/offset; ret stays 0 */
|
|
}
|
|
}
|
|
|
|
if (hsDone != NULL) {
|
|
*hsDone = (wolfSSL_is_init_finished(ssl_c) &&
|
|
wolfSSL_is_init_finished(ssl_s));
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
int test_tls_wire_mangle(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && \
|
|
!defined(NO_WOLFSSL_WIRE_MANGLE_TESTS)
|
|
/* Offsets chosen against the record and handshake framing rather than at
|
|
* random: 0 is the record type, 1-2 the record version, 3-4 the record
|
|
* length, 5 the handshake type, 6-8 the handshake length, and the rest
|
|
* land inside the body -- a session id, a certificate, a key share. */
|
|
static const int offsets[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13,
|
|
20, 45, 80, 120, 200, 400, 900 };
|
|
static const byte masks[] = { 0x01, 0x80, 0xff };
|
|
/* Per method and per direction, not just in total: one aggregate count is
|
|
* satisfied by a single mutation anywhere, which lets an entire protocol's
|
|
* sweep -- or one whole direction of it -- be a no-op unnoticed. A method
|
|
* that is not compiled stays zero and is excluded from the assertions by
|
|
* the same #ifdef that skips its calls. */
|
|
enum { WM_TLS12, WM_TLS13, WM_DTLS12, WM_DTLS13, WM_METHODS };
|
|
enum { WM_S2C, WM_C2S, WM_DIRS };
|
|
int round, o, m, d;
|
|
/* The client-to-server half is not run under WOLFSSL_ASYNC_CRYPT. A
|
|
* ClientHello with one byte flipped drives the TLS 1.3 server through
|
|
* SendTls13Certificate -> BuildTls13Message -> wolfAsync_EventInit, which
|
|
* takes SIGSEGV. Measured with --enable-asynccrypt --enable-all --enable-dtls13
|
|
* --disable-mlkem, at method TLS 1.3, round 0, offset 200, mask 0x01. It is not
|
|
* the harness leaving an async operation unserviced: the client-only step
|
|
* returns WANT_READ on every one of its 194 calls in that build and never
|
|
* WC_PENDING_E. Whether the library ought to survive a malformed ClientHello in
|
|
* that configuration is a library question, reported separately; this sweep has
|
|
* no business asserting it either way. The server-to-client half runs
|
|
* everywhere. */
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
#define WM_LAST_DIR WM_S2C
|
|
#else
|
|
#define WM_LAST_DIR WM_C2S
|
|
#endif
|
|
int applied[WM_METHODS][WM_DIRS];
|
|
int broke[WM_METHODS][WM_DIRS];
|
|
|
|
XMEMSET(applied, 0, sizeof(applied));
|
|
XMEMSET(broke, 0, sizeof(broke));
|
|
|
|
#define WM_RUN(idx, cm, sm) \
|
|
do { \
|
|
int hs = 0; \
|
|
int hit = test_wire_mangle_one((cm), (sm), d, round, offsets[o], \
|
|
masks[m], &hs); \
|
|
applied[idx][d] += hit; \
|
|
if (hit && !hs) \
|
|
broke[idx][d]++; \
|
|
} while (0)
|
|
|
|
for (d = 0; d <= WM_LAST_DIR; d++) {
|
|
for (round = 0; round < 7; round++) {
|
|
for (o = 0; o < (int)(sizeof(offsets) / sizeof(offsets[0])); o++) {
|
|
for (m = 0; m < (int)(sizeof(masks) / sizeof(masks[0])); m++) {
|
|
WM_RUN(WM_TLS12, wolfTLSv1_2_client_method,
|
|
wolfTLSv1_2_server_method);
|
|
#ifdef WOLFSSL_TLS13
|
|
WM_RUN(WM_TLS13, wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS
|
|
WM_RUN(WM_DTLS12, wolfDTLSv1_2_client_method,
|
|
wolfDTLSv1_2_server_method);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS13
|
|
WM_RUN(WM_DTLS13, wolfDTLSv1_3_client_method,
|
|
wolfDTLSv1_3_server_method);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef WM_RUN
|
|
|
|
/* A clean run of the same fixture, per method and per direction, before
|
|
* any of the counts above are believed. Without one for each method,
|
|
* broke[] cannot tell "the mutation broke this handshake" from "this
|
|
* method's fixture never completes a handshake at all" -- a pre-broken
|
|
* TLS 1.3 or DTLS setup that still puts bytes on the wire satisfies both
|
|
* applied[] and broke[] while proving nothing. It also gives every
|
|
* decision the corrupted runs took one way its partner in this same
|
|
* binary.
|
|
*
|
|
* Round 99 matches no iteration, so the first call flips nothing and runs
|
|
* whole rounds. The second uses an offset past the end of any buffer, so
|
|
* it takes the client-only step -- costing the round one server turn --
|
|
* and still flips nothing: that is what shows the stepping itself does
|
|
* not stop a handshake. */
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
/* No client-to-server sweep in this build, so no control for one. */
|
|
#define WM_CLEAN_C2S(cm, sm) do { } while (0)
|
|
#else
|
|
#define WM_CLEAN_C2S(cm, sm) \
|
|
do { \
|
|
int chs = 0; \
|
|
ExpectIntEQ(test_wire_mangle_one((cm), (sm), WM_C2S, 0, \
|
|
TEST_MEMIO_BUF_SZ, 0x00, &chs), 0); \
|
|
ExpectIntEQ(chs, 1); \
|
|
} while (0)
|
|
#endif
|
|
|
|
#define WM_CLEAN(cm, sm) \
|
|
do { \
|
|
int hs = 0; \
|
|
ExpectIntEQ(test_wire_mangle_one((cm), (sm), WM_S2C, 99, 0, 0x00, \
|
|
&hs), 0); \
|
|
ExpectIntEQ(hs, 1); \
|
|
WM_CLEAN_C2S((cm), (sm)); \
|
|
} while (0)
|
|
|
|
WM_CLEAN(wolfTLSv1_2_client_method, wolfTLSv1_2_server_method);
|
|
#ifdef WOLFSSL_TLS13
|
|
WM_CLEAN(wolfTLSv1_3_client_method, wolfTLSv1_3_server_method);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS
|
|
WM_CLEAN(wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS13
|
|
WM_CLEAN(wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method);
|
|
#endif
|
|
|
|
#undef WM_CLEAN
|
|
#undef WM_CLEAN_C2S
|
|
|
|
/* Every compiled method must have corrupted a byte in each direction; a
|
|
* zero means that whole protocol's sweep, or that whole direction of it,
|
|
* ran clean traffic. And a corrupted byte must have broken at least one
|
|
* handshake: if none of them ever failed, the mutations never reached a
|
|
* parser and the error arms this test claims are untouched. Not every
|
|
* applied vector may fail -- a flipped byte in a session id, a random or
|
|
* an ignored extension is carried through to a complete handshake -- so
|
|
* this is asserted over the sweep, not per vector. */
|
|
for (d = 0; d <= WM_LAST_DIR; d++) {
|
|
ExpectIntGT(applied[WM_TLS12][d], 0);
|
|
ExpectIntGT(broke[WM_TLS12][d], 0);
|
|
#ifdef WOLFSSL_TLS13
|
|
ExpectIntGT(applied[WM_TLS13][d], 0);
|
|
ExpectIntGT(broke[WM_TLS13][d], 0);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS
|
|
ExpectIntGT(applied[WM_DTLS12][d], 0);
|
|
ExpectIntGT(broke[WM_DTLS12][d], 0);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS13
|
|
ExpectIntGT(applied[WM_DTLS13][d], 0);
|
|
ExpectIntGT(broke[WM_DTLS13][d], 0);
|
|
#endif
|
|
}
|
|
|
|
#undef WM_LAST_DIR
|
|
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Handshakes with messages dropped, duplicated, reordered and truncated.
|
|
*
|
|
* Flipping a byte reaches the parsers' error arms. It does not reach the
|
|
* ordering and retransmit logic, because a flipped byte still arrives once, in
|
|
* sequence, at the right length. That logic -- duplicate detection, out-of-
|
|
* order rejection, the DTLS retransmit pool, the fragment reassembler -- is
|
|
* only entered when the SEQUENCE is wrong, and a conforming peer never gets it
|
|
* wrong.
|
|
*
|
|
* test_memio already knows how to do this to the buffer between the two
|
|
* endpoints: drop a message, move one ahead of another, copy one back in a
|
|
* second time, or rewrite its declared length. All four are on the same
|
|
* fixture as the byte mangler, and none of them needs a socket.
|
|
* ------------------------------------------------------------------------- */
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && \
|
|
!defined(NO_WOLFSSL_WIRE_MANGLE_TESTS)
|
|
|
|
enum wire_op { WIRE_DROP, WIRE_DUP, WIRE_MOVE, WIRE_TRUNC, WIRE_SHORTEN,
|
|
WIRE_NONE };
|
|
|
|
static int test_wire_seq_one(method_provider mc, method_provider ms,
|
|
int round, int op, int msgPos)
|
|
{
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
int i;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
if (test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, mc, ms)
|
|
!= 0) {
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < 12; i++) {
|
|
int rounds = 0;
|
|
|
|
(void)test_memio_do_handshake(ssl_c, ssl_s, 1, &rounds);
|
|
|
|
if (i != round)
|
|
continue;
|
|
|
|
switch (op) {
|
|
case WIRE_DROP:
|
|
/* the receiver never sees this message at all */
|
|
(void)test_memio_drop_message(&test_ctx, 1, msgPos);
|
|
break;
|
|
case WIRE_DUP: {
|
|
/* the same message twice: what duplicate detection is for */
|
|
char copy[2048];
|
|
int copySz = (int)sizeof(copy);
|
|
|
|
if (test_memio_copy_message(&test_ctx, 1, copy, ©Sz,
|
|
msgPos) == 0) {
|
|
(void)test_memio_inject_message(&test_ctx, 1, copy, copySz);
|
|
}
|
|
break;
|
|
}
|
|
case WIRE_MOVE:
|
|
/* arrives before the message it must follow */
|
|
(void)test_memio_move_message(&test_ctx, 1, msgPos,
|
|
msgPos + 1);
|
|
break;
|
|
case WIRE_TRUNC:
|
|
/* declares more than it carries */
|
|
(void)test_memio_modify_message_len(&test_ctx, 1, msgPos,
|
|
4096);
|
|
break;
|
|
case WIRE_SHORTEN:
|
|
/* declares less than it carries, and loses its tail */
|
|
(void)test_memio_modify_message_len(&test_ctx, 1, msgPos, 4);
|
|
(void)test_memio_remove_from_buffer(&test_ctx, 1, 5, 4);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
int test_tls_wire_sequence(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES_BUILD) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && \
|
|
!defined(NO_WOLFSSL_WIRE_MANGLE_TESTS)
|
|
int round, op, pos;
|
|
|
|
for (round = 0; round < 6; round++) {
|
|
for (op = 0; op < (int)WIRE_NONE; op++) {
|
|
for (pos = 0; pos < 3; pos++) {
|
|
(void)test_wire_seq_one(wolfTLSv1_2_client_method,
|
|
wolfTLSv1_2_server_method, round, op, pos);
|
|
#ifdef WOLFSSL_TLS13
|
|
(void)test_wire_seq_one(wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method, round, op, pos);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS
|
|
/* DTLS is where dropping and reordering are not merely
|
|
* hostile but expected, so the retransmit and reassembly
|
|
* paths are entered rather than just refused. */
|
|
(void)test_wire_seq_one(wolfDTLSv1_2_client_method,
|
|
wolfDTLSv1_2_server_method, round, op, pos);
|
|
#endif
|
|
#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_TLS13)
|
|
(void)test_wire_seq_one(wolfDTLSv1_3_client_method,
|
|
wolfDTLSv1_3_server_method, round, op, pos);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/* the untouched partner, in this same binary */
|
|
ExpectIntEQ(test_wire_seq_one(wolfTLSv1_2_client_method,
|
|
wolfTLSv1_2_server_method, 99, (int)WIRE_NONE, 0), 0);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|