mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
The session cache is shared by every CTX in the process, and a connection between a client and a server in the same process stores two entries under the same session ID, one per side. Both landed in the same row, so each connection used two of the row's three slots in the default cache. When the next connection's ID hashed to the same row, its two inserts wrapped around and overwrote the oldest entry of the first connection. Since the client caches its session in DoFinished(), after the server verified the Finished, the oldest entry is the server's, so the server could no longer resume the first session. That made test_session and test_session_handling in the CPython CI fail intermittently with session_reused False. Hash client entries into the row after the one their ID maps to, so the two sides of a connection never compete for the same row. This changes where a persisted cache keeps its client entries, but WOLFSSL_CACHE_VERSION 4 has not been in a release yet, so it covers the new mapping and caches saved by released builds (version 3) are still rejected on restore. The new test only builds with three or fewer sessions per row, since two connections cannot evict anything from a larger row, and reports a skip when it finds no pair of connections sharing a row.
6335 lines
259 KiB
C
6335 lines
259 KiB
C
/* test_tls.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 <tests/utils.h>
|
|
#include <tests/api/test_tls.h>
|
|
#include <wolfssl/internal.h>
|
|
#include <wolfssl/ssl_sess.h>
|
|
#include <wolfssl/ssl.h>
|
|
|
|
|
|
int test_utils_memio_move_message(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLS_client_method, wolfTLS_server_method), 0);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectIntEQ(wolfSSL_clear_group_messages(ssl_s), 1);
|
|
/* start handshake, send first ClientHello */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* send server's flight */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* If the server responded with a HelloRetryRequest it is waiting on a new
|
|
* ClientHello, so the buffered flight is just the HRR rather than the real
|
|
* ServerHello flight. Drive another connect/accept round so the message
|
|
* moving below operates on the real flight. */
|
|
if (EXPECT_SUCCESS() && test_memio_msg_is_hello_retry_request(&test_ctx)) {
|
|
/* client processes HRR and sends second ClientHello */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* server processes second ClientHello and sends its flight */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
}
|
|
/* Move messages around but they should be the same at the end */
|
|
ExpectIntEQ(test_memio_move_message(&test_ctx, 1, 1, 2), 0);
|
|
ExpectIntEQ(test_memio_move_message(&test_ctx, 1, 2, 1), 0);
|
|
ExpectIntEQ(test_memio_move_message(&test_ctx, 1, 1, 3), 0);
|
|
ExpectIntEQ(test_memio_move_message(&test_ctx, 1, 3, 1), 0);
|
|
ExpectIntEQ(test_memio_move_message(&test_ctx, 1, 0, 2), 0);
|
|
ExpectIntEQ(test_memio_move_message(&test_ctx, 1, 2, 0), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_unexpected_ccs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12)
|
|
const byte ccs[] = {
|
|
0x14, /* ccs type */
|
|
0x03, 0x03, /* version */
|
|
0x00, 0x01, /* length */
|
|
0x01, /* ccs value */
|
|
};
|
|
const byte badccs[] = {
|
|
0x14, /* ccs type */
|
|
0x03, 0x03, /* version */
|
|
0x00, 0x01, /* length */
|
|
0x99, /* wrong ccs value */
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* ccs in the wrong place */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
/* inject SH */
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)ccs, sizeof(ccs)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
OUT_OF_ORDER_E);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
ssl_s = NULL;
|
|
|
|
/* malformed ccs */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)badccs, sizeof(badccs)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
LENGTH_ERROR);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A TLS 1.2 client must not accept the server's ChangeCipherSpec before it has
|
|
* sent ClientKeyExchange: no master secret exists yet, so the CCS would switch
|
|
* the read side to all-zero keys and the expected Finished would be computed
|
|
* from an all-zero master secret. Deliver the server's flight and a CCS in one
|
|
* buffer so the client sees the CCS right behind ServerHelloDone. */
|
|
int test_tls12_early_server_ccs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12)
|
|
const byte ccs[] = {
|
|
0x14, /* ccs type */
|
|
0x03, 0x03, /* version */
|
|
0x00, 0x01, /* length */
|
|
0x01, /* ccs value */
|
|
};
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
/* start handshake, send first ClientHello */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* send server's flight */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* hand the whole flight plus a CCS to the client in one go */
|
|
ExpectIntGT(test_ctx.c_len, 0);
|
|
ExpectIntEQ(wolfSSL_inject(ssl_c, test_ctx.c_buff, test_ctx.c_len),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_inject(ssl_c, ccs, sizeof(ccs)), WOLFSSL_SUCCESS);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), OUT_OF_ORDER_E);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_unexpected_ccs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13)
|
|
const byte ccs[] = {
|
|
0x14, /* ccs type */
|
|
0x03, 0x03, /* version */
|
|
0x00, 0x01, /* length */
|
|
0x01, /* ccs value */
|
|
};
|
|
const byte badccs[] = {
|
|
0x14, /* ccs type */
|
|
0x03, 0x03, /* version */
|
|
0x00, 0x01, /* length */
|
|
0x99, /* wrong ccs value */
|
|
};
|
|
const byte unexpectedAlert[] = {
|
|
0x15, /* alert type */
|
|
0x03, 0x03, /* version */
|
|
0x00, 0x02, /* length */
|
|
0x02, /* level: fatal */
|
|
0x0a /* protocol version */
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* ccs can't appear before a CH */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)ccs, sizeof(ccs)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
UNKNOWN_RECORD_TYPE);
|
|
ExpectIntEQ(test_ctx.c_len, sizeof(unexpectedAlert));
|
|
ExpectBufEQ(test_ctx.c_buff, unexpectedAlert, sizeof(unexpectedAlert));
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
ssl_s = NULL;
|
|
|
|
/* malformed ccs */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)badccs, sizeof(badccs)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
UNKNOWN_RECORD_TYPE);
|
|
ExpectIntEQ(test_ctx.c_len, sizeof(unexpectedAlert));
|
|
ExpectBufEQ(test_ctx.c_buff, unexpectedAlert, sizeof(unexpectedAlert));
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13)
|
|
/* Set up a client and server and run the first flight of each, leaving the
|
|
* server's reply unread in the client's input. */
|
|
static int test_record_version_first_flight(struct test_memio_ctx *test_ctx,
|
|
WOLFSSL_CTX **ctx_c, WOLFSSL_CTX **ctx_s, WOLFSSL **ssl_c, WOLFSSL **ssl_s,
|
|
method_provider method_c, method_provider method_s)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
XMEMSET(test_ctx, 0, sizeof(*test_ctx));
|
|
ExpectIntEQ(test_memio_setup(test_ctx, ctx_c, ctx_s, ssl_c, ssl_s,
|
|
method_c, method_s), 0);
|
|
ExpectIntEQ(wolfSSL_connect(*ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(*ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(*ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(*ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
/* The ServerHello record header leads the client's input. */
|
|
ExpectIntGT(test_ctx->c_len, RECORD_HEADER_SZ);
|
|
ExpectIntEQ(test_ctx->c_buff[0], handshake);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* RFC 8446 5.1: a TLS 1.3 peer must ignore the minor version in
|
|
* TLSPlaintext.legacy_record_version. Rewrite it on the ServerHello and on a
|
|
* change_cipher_spec record and check that the handshake still completes. */
|
|
int test_tls13_plaintext_record_version_ignored(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13)
|
|
const byte minors[] = { SSLv3_MINOR, TLSv1_MINOR, TLSv1_1_MINOR,
|
|
TLSv1_3_MINOR };
|
|
const byte ccs[] = {
|
|
change_cipher_spec, SSLv3_MAJOR, TLSv1_1_MINOR,
|
|
0x00, 0x01, /* length */
|
|
0x01 /* ccs value */
|
|
};
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
size_t i;
|
|
|
|
for (i = 0; i < sizeof(minors); i++) {
|
|
ExpectIntEQ(test_record_version_first_flight(&test_ctx, &ctx_c,
|
|
&ctx_s, &ssl_c, &ssl_s, wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method), TEST_SUCCESS);
|
|
test_ctx.c_buff[2] = minors[i];
|
|
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);
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
ctx_c = NULL;
|
|
ctx_s = NULL;
|
|
}
|
|
|
|
/* Client's change_cipher_spec: rewrite the middlebox compatibility one
|
|
* when the build sends it, otherwise inject one ahead of the Finished. */
|
|
ExpectIntEQ(test_record_version_first_flight(&test_ctx, &ctx_c, &ctx_s,
|
|
&ssl_c, &ssl_s, wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method), TEST_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntGT(test_ctx.s_len, RECORD_HEADER_SZ);
|
|
if (EXPECT_SUCCESS()) {
|
|
if (test_ctx.s_buff[0] == change_cipher_spec) {
|
|
test_ctx.s_buff[2] = TLSv1_1_MINOR;
|
|
}
|
|
else {
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)ccs, sizeof(ccs)), 0);
|
|
ExpectIntEQ(test_memio_move_message(&test_ctx, 0,
|
|
test_ctx.s_msg_count - 1, 0), 0);
|
|
}
|
|
}
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The record version check that stays in place alongside the TLS 1.3
|
|
* relaxation: once a downgrade-capable client has negotiated TLS 1.2, every
|
|
* record must match the negotiated version. */
|
|
int test_tls_record_version_still_checked(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12)
|
|
char buf[8];
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* The client starts at TLS 1.3 and drops to TLS 1.2, after which a
|
|
* non-application_data record must match the negotiated version. */
|
|
ExpectIntEQ(test_record_version_first_flight(&test_ctx, &ctx_c, &ctx_s,
|
|
&ssl_c, &ssl_s, wolfTLS_client_method, wolfTLSv1_2_server_method),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_version(ssl_c), TLS1_2_VERSION);
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
ExpectIntGT(test_ctx.c_len, RECORD_HEADER_SZ);
|
|
ExpectIntEQ(test_ctx.c_buff[0], alert);
|
|
test_ctx.c_buff[2] = TLSv1_1_MINOR;
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, (int)sizeof(buf)),
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A record whose length field exceeds the protocol limit must be answered
|
|
* with a record_overflow alert (RFC 8446 section 5.1, RFC 5246 section 6.2.1).
|
|
* Before the fix the alert was only sent when HAVE_MAX_FRAGMENT was defined,
|
|
* so a default build dropped the connection with no alert on the wire. */
|
|
int test_tls_record_overflow_alert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
|
|
/* Record header only: handshake type, legacy version, length 0xFFFF.
|
|
* 0xFFFF is past MAX_RECORD_SIZE + MAX_MSG_EXTRA, so GetRecordHeader()
|
|
* returns LENGTH_ERROR before any body bytes are needed. The handshake
|
|
* type keeps the server off the OLD_HELLO_ALLOWED SSLv2 detection path. */
|
|
const byte oversized[] = {
|
|
0x16, /* handshake type */
|
|
0x03, 0x03, /* legacy record version */
|
|
0xFF, 0xFF /* length 65535 */
|
|
};
|
|
/* Cleartext record_overflow alert the peer is expected to emit. */
|
|
const byte overflowAlert[] = {
|
|
0x15, /* alert record */
|
|
0x03, 0x03, /* version */
|
|
0x00, 0x02, /* length */
|
|
0x02, /* level: fatal */
|
|
0x16 /* description: record_overflow (22) */
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
#ifdef WOLFSSL_TLS13
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL_ALERT_HISTORY history;
|
|
byte readBuf[64];
|
|
#endif
|
|
|
|
/* Server side before negotiation: the alert is in the clear, so the exact
|
|
* record can be matched on the wire. */
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)oversized, sizeof(oversized)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR), LENGTH_ERROR);
|
|
ExpectIntEQ(test_ctx.c_len, sizeof(overflowAlert));
|
|
ExpectBufEQ(test_ctx.c_buff, overflowAlert, sizeof(overflowAlert));
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_s = NULL;
|
|
ctx_s = NULL;
|
|
#endif /* !WOLFSSL_NO_TLS12 */
|
|
|
|
#ifdef WOLFSSL_TLS13
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)oversized, sizeof(oversized)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR), LENGTH_ERROR);
|
|
ExpectIntEQ(test_ctx.c_len, sizeof(overflowAlert));
|
|
ExpectBufEQ(test_ctx.c_buff, overflowAlert, sizeof(overflowAlert));
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_s = NULL;
|
|
ctx_s = NULL;
|
|
|
|
/* Client side after a completed TLS 1.3 handshake: the reported scenario.
|
|
* The alert is encrypted now, so it is decoded by letting the server read
|
|
* it and inspecting its received-alert history. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Drop any queued post-handshake traffic (session tickets) so only the
|
|
* crafted record and the resulting alert remain in the buffers. */
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char*)oversized, sizeof(oversized)), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)),
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), LENGTH_ERROR);
|
|
|
|
/* Server consumes the encrypted alert and records it. */
|
|
ExpectIntLE(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 0);
|
|
XMEMSET(&history, 0, sizeof(history));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_s, &history), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(history.last_rx.code, record_overflow);
|
|
ExpectIntEQ(history.last_rx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif /* WOLFSSL_TLS13 */
|
|
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && (!NO_TLS12 || TLS13) */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Must match the caller's guard exactly, or these functions compile unused. */
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_SHA256)
|
|
|
|
static int test_peer_name_cb_err = 0;
|
|
static int test_peer_name_cb_preverify = -1;
|
|
|
|
static int test_peer_name_verify_cb(int preverify,
|
|
WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
/* Only record the first failure - later certificates in the chain would
|
|
* otherwise overwrite it. */
|
|
if (!preverify && (test_peer_name_cb_err == 0)) {
|
|
test_peer_name_cb_preverify = preverify;
|
|
test_peer_name_cb_err = wolfSSL_X509_STORE_CTX_get_error(store);
|
|
}
|
|
return preverify;
|
|
}
|
|
|
|
/* mode: 0 = check_ip_address(), 1 = set1_ip_asc(), 2 = set1_host() */
|
|
static int test_peer_name_mismatch(int mode, const char* name, int expectErr)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
test_peer_name_cb_err = 0;
|
|
test_peer_name_cb_preverify = -1;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfSSLv23_server_method), 0);
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, test_peer_name_verify_cb);
|
|
|
|
if (mode == 0) {
|
|
ExpectIntEQ(wolfSSL_check_ip_address(ssl_c, name), WOLFSSL_SUCCESS);
|
|
}
|
|
else if (mode == 1) {
|
|
ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_ip_asc(
|
|
wolfSSL_get0_param(ssl_c), name), WOLFSSL_SUCCESS);
|
|
}
|
|
else {
|
|
ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_host(
|
|
wolfSSL_get0_param(ssl_c), name, 0), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
if (expectErr == 0) {
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(test_peer_name_cb_err, 0);
|
|
}
|
|
else {
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* The mismatch has to reach the application's verify callback so it
|
|
* can apply its own policy, not just fail the handshake. */
|
|
ExpectIntEQ(test_peer_name_cb_preverify, 0);
|
|
ExpectIntEQ(test_peer_name_cb_err, expectErr);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int test_peer_name_override_cb(int preverify,
|
|
WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
(void)preverify;
|
|
(void)store;
|
|
/* The application accepts the certificate despite the mismatch. */
|
|
return 1;
|
|
}
|
|
|
|
/* mode: 1 = set1_ip_asc(), 2 = set1_host() */
|
|
static int test_peer_name_override(int mode, const char* name, int expectRet)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfSSLv23_server_method), 0);
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, test_peer_name_override_cb);
|
|
|
|
if (mode == 1) {
|
|
ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_ip_asc(
|
|
wolfSSL_get0_param(ssl_c), name), WOLFSSL_SUCCESS);
|
|
}
|
|
else {
|
|
ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set1_host(
|
|
wolfSSL_get0_param(ssl_c), name, 0), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* The callback overrides the mismatch, so the handshake completes... */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* ...but the result must still name it, or a certificate issued to
|
|
* another name reads back as fully verified. */
|
|
ExpectIntEQ((int)wolfSSL_get_verify_result(ssl_c), expectRet);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* An overridden X509_VERIFY_PARAM name mismatch must still be visible through
|
|
* SSL_get_verify_result(). */
|
|
int test_tls_peer_name_mismatch_verify_result(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_SHA256)
|
|
#ifdef WOLFSSL_IP_ALT_NAME
|
|
ExpectIntEQ(test_peer_name_override(1, "127.0.0.2",
|
|
WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH), TEST_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(test_peer_name_override(2, "wrong.example.com",
|
|
WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH), TEST_SUCCESS);
|
|
/* A matching name still verifies clean. */
|
|
ExpectIntEQ(test_peer_name_override(2, "example.com",
|
|
WOLFSSL_X509_V_OK), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* SetupStoreCtxCallback() runs the handshake error through GetX509Error()
|
|
* only under OPENSSL_COMPATIBLE_DEFAULTS. Everywhere else the callback sees
|
|
* the internal code. */
|
|
#ifdef OPENSSL_COMPATIBLE_DEFAULTS
|
|
#define TEST_PEER_NAME_IPADDR_ERR WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH
|
|
#define TEST_PEER_NAME_DOMAIN_ERR WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH
|
|
#else
|
|
#define TEST_PEER_NAME_IPADDR_ERR WC_NO_ERR_TRACE(IPADDR_MISMATCH)
|
|
#define TEST_PEER_NAME_DOMAIN_ERR WC_NO_ERR_TRACE(DOMAIN_NAME_MISMATCH)
|
|
#endif
|
|
|
|
/* A peer name mismatch must be reported through SSL_set_verify()'s callback,
|
|
* whichever API named the expected peer. */
|
|
int test_tls_peer_name_mismatch_verify_cb(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_SHA256)
|
|
#ifdef WOLFSSL_IP_ALT_NAME
|
|
/* certs/server-cert.pem carries IP:127.0.0.1 and DNS:example.com. */
|
|
ExpectIntEQ(test_peer_name_mismatch(0, "127.0.0.2",
|
|
TEST_PEER_NAME_IPADDR_ERR), TEST_SUCCESS);
|
|
ExpectIntEQ(test_peer_name_mismatch(1, "127.0.0.2",
|
|
TEST_PEER_NAME_IPADDR_ERR), TEST_SUCCESS);
|
|
ExpectIntEQ(test_peer_name_mismatch(0, "127.0.0.1", 0), TEST_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(test_peer_name_mismatch(2, "wrong.example.com",
|
|
TEST_PEER_NAME_DOMAIN_ERR), TEST_SUCCESS);
|
|
ExpectIntEQ(test_peer_name_mismatch(2, "example.com", 0), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
defined(HAVE_CRL) && !defined(NO_RSA) && !defined(NO_CERTS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_SHA256)
|
|
/* Handshake with verify param flags set on the client. No CRL is loaded, so
|
|
* the handshake fails when the flags turn on CRL checks. */
|
|
static int test_param_flags_crl(unsigned long flags, int expectCrl)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfSSLv23_server_method), 0);
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectIntEQ(wolfSSL_X509_VERIFY_PARAM_set_flags(wolfSSL_get0_param(ssl_c),
|
|
flags), WOLFSSL_SUCCESS);
|
|
|
|
if (expectCrl) {
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
}
|
|
else {
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
}
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(SSL_CM(ssl_c)->crlEnabled, expectCrl);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* X509_V_FLAG_USE_CHECK_TIME in a verify param must not turn on CRL checks.
|
|
* It used to have the value of X509_V_FLAG_CRL_CHECK. */
|
|
int test_tls_param_flags_crl_check(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
defined(HAVE_CRL) && !defined(NO_RSA) && !defined(NO_CERTS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_SHA256)
|
|
ExpectIntEQ(test_param_flags_crl(WOLFSSL_USE_CHECK_TIME, 0), TEST_SUCCESS);
|
|
ExpectIntEQ(test_param_flags_crl(WOLFSSL_X509_V_FLAG_CRL_CHECK, 1),
|
|
TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* One macro per group test_tls_get_peer_tmp_key() can exercise, so that the
|
|
* helper below is never compiled without a caller. */
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_SHA256)
|
|
#if defined(HAVE_ECC) && !defined(NO_ECC_SECP) && \
|
|
(!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
|
|
#define TEST_PEER_TMP_KEY_ECC
|
|
#endif
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_CURVE25519) && !defined(HAVE_FIPS)
|
|
#define TEST_PEER_TMP_KEY_X25519
|
|
#endif
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_CURVE448) && !defined(HAVE_FIPS)
|
|
#define TEST_PEER_TMP_KEY_X448
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(TEST_PEER_TMP_KEY_ECC) || defined(TEST_PEER_TMP_KEY_X25519) || \
|
|
defined(TEST_PEER_TMP_KEY_X448)
|
|
static int test_peer_tmp_key_group(int group, int tls13, int expectedType)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_EVP_PKEY* pkey = NULL;
|
|
int groups[1];
|
|
method_provider method_c = NULL, method_s = NULL;
|
|
|
|
groups[0] = group;
|
|
(void)tls13;
|
|
|
|
/* Only one of the two versions may be compiled in, so the methods cannot
|
|
* be picked with a plain conditional expression. */
|
|
#ifdef WOLFSSL_TLS13
|
|
if (tls13) {
|
|
method_c = wolfTLSv1_3_client_method;
|
|
method_s = wolfTLSv1_3_server_method;
|
|
}
|
|
#endif
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
if (!tls13) {
|
|
method_c = wolfTLSv1_2_client_method;
|
|
method_s = wolfTLSv1_2_server_method;
|
|
}
|
|
#endif
|
|
if ((method_c == NULL) || (method_s == NULL))
|
|
return TEST_SKIPPED;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
method_c, method_s), 0);
|
|
ExpectIntEQ(wolfSSL_KeepHandshakeResources(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_get_peer_tmp_key(ssl_c, &pkey), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(pkey);
|
|
/* The key must be of the negotiated group's type, not just any key.
|
|
* EVP_PKEY_id() reports the stored type; EVP_PKEY_base_id() maps only the
|
|
* classic types and returns NID_undef for X25519/X448. */
|
|
ExpectIntEQ(wolfSSL_EVP_PKEY_id(pkey), expectedType);
|
|
wolfSSL_EVP_PKEY_free(pkey);
|
|
pkey = NULL;
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* The helper handshakes over TLS 1.3, so ECC alone needs it built. X25519 and
|
|
* X448 already imply it. */
|
|
#if defined(TEST_PEER_TMP_KEY_X25519) || \
|
|
(defined(TEST_PEER_TMP_KEY_ECC) && defined(WOLFSSL_TLS13))
|
|
/* Kept peer keys outlive the connection they came from. A reused object has to
|
|
* report the key of the group it just negotiated, not the one before it. */
|
|
static int test_peer_tmp_key_reuse(int group1, int expected1, int group2,
|
|
int expected2)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_EVP_PKEY* pkey = NULL;
|
|
int groups[1];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
groups[0] = group1;
|
|
ExpectIntEQ(wolfSSL_KeepHandshakeResources(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_peer_tmp_key(ssl_c, &pkey), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_EVP_PKEY_id(pkey), expected1);
|
|
wolfSSL_EVP_PKEY_free(pkey);
|
|
pkey = NULL;
|
|
|
|
ExpectIntEQ(wolfSSL_clear(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS);
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
groups[0] = group2;
|
|
ExpectIntEQ(wolfSSL_KeepHandshakeResources(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_peer_tmp_key(ssl_c, &pkey), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_EVP_PKEY_id(pkey), expected2);
|
|
wolfSSL_EVP_PKEY_free(pkey);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
#if defined(TEST_PEER_TMP_KEY_X25519) || defined(TEST_PEER_TMP_KEY_X448) || \
|
|
(defined(TEST_PEER_TMP_KEY_ECC) && defined(WOLFSSL_TLS13))
|
|
/* wolfSSL_clear() has to drop the kept peer key. Left in place, the next
|
|
* handshake on the object - one that does no (EC)DH, TLS 1.3 psk_ke or a
|
|
* PSK/RSA key exchange - would report the previous connection's key. */
|
|
static int test_peer_tmp_key_cleared(int group)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_EVP_PKEY* pkey = NULL;
|
|
int groups[1];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
groups[0] = group;
|
|
ExpectIntEQ(wolfSSL_KeepHandshakeResources(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_peer_tmp_key(ssl_c, &pkey), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(pkey);
|
|
wolfSSL_EVP_PKEY_free(pkey);
|
|
pkey = NULL;
|
|
|
|
ExpectIntEQ(wolfSSL_clear(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_peer_tmp_key(ssl_c, &pkey), WOLFSSL_FAILURE);
|
|
ExpectNull(pkey);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* SSL_get_peer_tmp_key() has to return the peer's ephemeral key for every key
|
|
* exchange group, not just TLS 1.2 ECDHE. */
|
|
int test_tls_get_peer_tmp_key(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_PEER_TMP_KEY_ECC
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(test_peer_tmp_key_group(WOLFSSL_ECC_SECP256R1, 0,
|
|
WC_EVP_PKEY_EC), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef WOLFSSL_TLS13
|
|
ExpectIntEQ(test_peer_tmp_key_group(WOLFSSL_ECC_SECP256R1, 1,
|
|
WC_EVP_PKEY_EC), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
#ifdef TEST_PEER_TMP_KEY_X25519
|
|
ExpectIntEQ(test_peer_tmp_key_group(WOLFSSL_ECC_X25519, 1,
|
|
WC_EVP_PKEY_X25519), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef TEST_PEER_TMP_KEY_X448
|
|
ExpectIntEQ(test_peer_tmp_key_group(WOLFSSL_ECC_X448, 1,
|
|
WC_EVP_PKEY_X448), TEST_SUCCESS);
|
|
#endif
|
|
#if defined(TEST_PEER_TMP_KEY_X25519) && defined(TEST_PEER_TMP_KEY_X448)
|
|
ExpectIntEQ(test_peer_tmp_key_reuse(WOLFSSL_ECC_X25519, WC_EVP_PKEY_X25519,
|
|
WOLFSSL_ECC_X448, WC_EVP_PKEY_X448), TEST_SUCCESS);
|
|
#endif
|
|
#if defined(TEST_PEER_TMP_KEY_X25519) && defined(TEST_PEER_TMP_KEY_ECC)
|
|
ExpectIntEQ(test_peer_tmp_key_reuse(WOLFSSL_ECC_SECP256R1, WC_EVP_PKEY_EC,
|
|
WOLFSSL_ECC_X25519, WC_EVP_PKEY_X25519), TEST_SUCCESS);
|
|
#endif
|
|
/* Same group twice. wolfSSL_clear() clears the present flag but keeps the
|
|
* key object, so the second handshake imports over the first one's key
|
|
* instead of taking the ReuseKey() path. */
|
|
#if defined(TEST_PEER_TMP_KEY_ECC) && defined(WOLFSSL_TLS13)
|
|
ExpectIntEQ(test_peer_tmp_key_reuse(WOLFSSL_ECC_SECP256R1, WC_EVP_PKEY_EC,
|
|
WOLFSSL_ECC_SECP256R1, WC_EVP_PKEY_EC), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef TEST_PEER_TMP_KEY_X25519
|
|
ExpectIntEQ(test_peer_tmp_key_reuse(WOLFSSL_ECC_X25519, WC_EVP_PKEY_X25519,
|
|
WOLFSSL_ECC_X25519, WC_EVP_PKEY_X25519), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef TEST_PEER_TMP_KEY_X25519
|
|
ExpectIntEQ(test_peer_tmp_key_cleared(WOLFSSL_ECC_X25519), TEST_SUCCESS);
|
|
#elif defined(TEST_PEER_TMP_KEY_X448)
|
|
ExpectIntEQ(test_peer_tmp_key_cleared(WOLFSSL_ECC_X448), TEST_SUCCESS);
|
|
#elif defined(TEST_PEER_TMP_KEY_ECC) && defined(WOLFSSL_TLS13)
|
|
ExpectIntEQ(test_peer_tmp_key_cleared(WOLFSSL_ECC_SECP256R1),
|
|
TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The group APIs are not EC-only, so each part of the test below carries just
|
|
* the configuration it needs. The second condition is the one the APIs
|
|
* themselves are built under - without an EC group or DH they do not exist. */
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
defined(WOLFSSL_TLS13) && (defined(HAVE_ECC) || \
|
|
defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) || !defined(NO_DH))
|
|
#define TEST_NEGOTIATED_GROUP
|
|
#if defined(HAVE_ECC) && !defined(NO_ECC_SECP) && \
|
|
(!defined(NO_ECC256) || defined(HAVE_ALL_CURVES))
|
|
#define TEST_NEGOTIATED_GROUP_P256
|
|
#endif
|
|
#if !defined(NO_DH) && defined(HAVE_FFDHE_2048)
|
|
#define TEST_NEGOTIATED_GROUP_FFDHE
|
|
#endif
|
|
#ifndef NO_PSK
|
|
#define TEST_NEGOTIATED_GROUP_PSK
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef TEST_NEGOTIATED_GROUP_PSK
|
|
static const byte test_tls_group_psk[] = { 0x1a, 0x2b, 0x3c, 0x4d };
|
|
|
|
static unsigned int test_tls_group_psk_client_cb(WOLFSSL* ssl,
|
|
const char* hint, char* identity, unsigned int id_max_len,
|
|
unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
(void)hint;
|
|
if ((id_max_len == 0) || (key_max_len < sizeof(test_tls_group_psk)))
|
|
return 0;
|
|
XSTRNCPY(identity, "group_client", id_max_len);
|
|
XMEMCPY(key, test_tls_group_psk, sizeof(test_tls_group_psk));
|
|
return (unsigned int)sizeof(test_tls_group_psk);
|
|
}
|
|
|
|
static unsigned int test_tls_group_psk_server_cb(WOLFSSL* ssl, const char* id,
|
|
unsigned char* key, unsigned int key_max_len)
|
|
{
|
|
(void)ssl;
|
|
if ((id == NULL) || (key_max_len < sizeof(test_tls_group_psk)))
|
|
return 0;
|
|
if (XSTRCMP(id, "group_client") != 0)
|
|
return 0;
|
|
XMEMCPY(key, test_tls_group_psk, sizeof(test_tls_group_psk));
|
|
return (unsigned int)sizeof(test_tls_group_psk);
|
|
}
|
|
#endif
|
|
|
|
/* SSL_get_negotiated_group() and SSL_group_to_name() report the group that was
|
|
* negotiated, using the names OpenSSL gives the TLS supported groups. */
|
|
int test_tls_get_negotiated_group(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_NEGOTIATED_GROUP
|
|
|
|
ExpectIntEQ(wolfSSL_get_negotiated_group(NULL), 0);
|
|
|
|
#ifdef TEST_NEGOTIATED_GROUP_P256
|
|
{
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int group = 0;
|
|
int p256[1];
|
|
|
|
p256[0] = WOLFSSL_ECC_SECP256R1;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, p256, 1), WOLFSSL_SUCCESS);
|
|
|
|
/* Nothing negotiated yet. */
|
|
ExpectIntEQ(wolfSSL_get_negotiated_group(ssl_c), 0);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntNE((group = wolfSSL_get_negotiated_group(ssl_c)), 0);
|
|
ExpectIntEQ(group, wolfSSL_get_negotiated_group(ssl_s));
|
|
/* OpenSSL names this group secp256r1, not by its NIST name P-256. */
|
|
ExpectStrEQ(wolfSSL_group_to_name(ssl_c, group), "secp256r1");
|
|
|
|
/* A cleared object has negotiated nothing again. */
|
|
ExpectIntEQ(wolfSSL_clear(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_negotiated_group(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_get_negotiated_group(ssl_s), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
|
|
#ifdef TEST_NEGOTIATED_GROUP_PSK
|
|
/* A psk_ke handshake exchanges no key share, so there is no group. */
|
|
{
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_tls_group_psk_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_tls_group_psk_server_cb);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_no_dhe_psk(ssl_s), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
|
|
ExpectIntEQ(ssl_c->options.pskNegotiated, 1);
|
|
ExpectIntEQ(wolfSSL_get_negotiated_group(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_get_negotiated_group(ssl_s), 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
|
|
#ifdef TEST_NEGOTIATED_GROUP_FFDHE
|
|
/* A finite field group is negotiated and named the same way. This block
|
|
* is the only caller in an OPENSSL_EXTRA build with no EC group at all. */
|
|
{
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int group = 0;
|
|
int ffdhe[1];
|
|
|
|
ffdhe[0] = WOLFSSL_FFDHE_2048;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
|
|
0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, ffdhe, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ((group = wolfSSL_get_negotiated_group(ssl_c)),
|
|
WC_NID_ffdhe2048);
|
|
ExpectIntEQ(group, wolfSSL_get_negotiated_group(ssl_s));
|
|
ExpectStrEQ(wolfSSL_group_to_name(ssl_c, group), "ffdhe2048");
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
#endif
|
|
|
|
/* Groups can also be named directly by their IANA code point. */
|
|
#ifdef HAVE_ECC
|
|
ExpectStrEQ(wolfSSL_group_to_name(NULL, WOLFSSL_ECC_SECP256R1),
|
|
"secp256r1");
|
|
#endif
|
|
#ifdef HAVE_CURVE25519
|
|
ExpectStrEQ(wolfSSL_group_to_name(NULL, WOLFSSL_ECC_X25519), "x25519");
|
|
#endif
|
|
#if !defined(NO_DH) && defined(HAVE_FFDHE_2048)
|
|
ExpectStrEQ(wolfSSL_group_to_name(NULL, WOLFSSL_FFDHE_2048), "ffdhe2048");
|
|
/* ...or by their NID, which RFC 7919 groups have just like curves do. */
|
|
ExpectStrEQ(wolfSSL_group_to_name(NULL, WC_NID_ffdhe2048), "ffdhe2048");
|
|
#ifdef HAVE_ECC
|
|
/* But they are not EC curves, so the EC lookups must not resolve them. */
|
|
ExpectNull(wolfSSL_EC_curve_nid2nist(WC_NID_ffdhe2048));
|
|
ExpectIntEQ(wolfSSL_EC_curve_nist2nid("ffdhe2048"), 0);
|
|
#endif
|
|
#endif
|
|
#ifdef HAVE_ECC
|
|
/* A non-curve NID must not name a curve. WOLFSSL_FFDHE_3072 (257) is
|
|
* WC_NID_md4, which is what made this table's NID column ambiguous. */
|
|
ExpectNull(wolfSSL_EC_curve_nid2nist(WC_NID_md4));
|
|
#endif
|
|
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_NO_ML_KEM) && \
|
|
!defined(WOLFSSL_NO_ML_KEM_768)
|
|
/* OpenSSL spells the standalone ML-KEM groups without underscores. */
|
|
ExpectStrEQ(wolfSSL_group_to_name(NULL, WOLFSSL_ML_KEM_768), "MLKEM768");
|
|
#endif
|
|
ExpectNull(wolfSSL_group_to_name(NULL, 9999));
|
|
|
|
/* wolfSSLv23_client_method() is built under !NO_TLS && !NO_WOLFSSL_CLIENT. */
|
|
#if defined(TEST_NEGOTIATED_GROUP_P256) && !defined(NO_TLS) && \
|
|
!defined(NO_WOLFSSL_CLIENT)
|
|
/* SSL_CTX_set_tmp_ecdh() seeds ctx->ecdhCurveOID, which InitSSL() copies
|
|
* into the SSL. That is a configured preference, not a negotiated group,
|
|
* so it must not be reported before a handshake has run. */
|
|
{
|
|
WOLFSSL_CTX* ctx_e = NULL;
|
|
WOLFSSL* ssl_e = NULL;
|
|
WOLFSSL_EC_KEY* ecKey = NULL;
|
|
|
|
ExpectNotNull(ctx_e = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ecKey = wolfSSL_EC_KEY_new_by_curve_name(
|
|
WC_NID_X9_62_prime256v1));
|
|
ExpectIntEQ(wolfSSL_SSL_CTX_set_tmp_ecdh(ctx_e, ecKey),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl_e = wolfSSL_new(ctx_e));
|
|
ExpectIntEQ(wolfSSL_get_negotiated_group(ssl_e), 0);
|
|
/* SSL_clear() keeps the configured preference. */
|
|
ExpectIntEQ(wolfSSL_clear(ssl_e), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl_e->ecdhCurveOID, ctx_e->ecdhCurveOID);
|
|
ExpectIntEQ(wolfSSL_get_negotiated_group(ssl_e), 0);
|
|
|
|
wolfSSL_EC_KEY_free(ecKey);
|
|
wolfSSL_free(ssl_e);
|
|
wolfSSL_CTX_free(ctx_e);
|
|
}
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_SHA256)
|
|
static int test_alert_cb_where[2];
|
|
static int test_alert_cb_ret[2];
|
|
|
|
static void test_alert_info_cb(const WOLFSSL* ssl, int where, int ret)
|
|
{
|
|
int idx = (wolfSSL_is_server((WOLFSSL*)ssl) != 0);
|
|
|
|
if (where & WOLFSSL_CB_ALERT) {
|
|
test_alert_cb_where[idx] = where;
|
|
test_alert_cb_ret[idx] = ret;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int test_tls_alert_info_cb(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_SHA256)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char buf[16];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(test_alert_cb_where, 0, sizeof(test_alert_cb_where));
|
|
XMEMSET(test_alert_cb_ret, 0, sizeof(test_alert_cb_ret));
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfSSLv23_server_method), 0);
|
|
wolfSSL_set_info_callback(ssl_c, test_alert_info_cb);
|
|
wolfSSL_set_info_callback(ssl_s, test_alert_info_cb);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Client sends close_notify, server reads it. */
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
ExpectIntLE(wolfSSL_read(ssl_s, buf, sizeof(buf)), 0);
|
|
|
|
/* Both directions report, and the alert is packed the way OpenSSL packs
|
|
* it: (level << 8) | description. */
|
|
ExpectIntEQ(test_alert_cb_where[0], WOLFSSL_CB_WRITE_ALERT);
|
|
ExpectIntEQ(test_alert_cb_ret[0], (alert_warning << 8) | close_notify);
|
|
ExpectIntEQ(test_alert_cb_where[1], WOLFSSL_CB_READ_ALERT);
|
|
ExpectIntEQ(test_alert_cb_ret[1], (alert_warning << 8) | close_notify);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Shutting down a handshake that never completed has to fail and send nothing,
|
|
* the way OpenSSL fails with SSL_R_SHUTDOWN_WHILE_IN_INIT. */
|
|
int test_tls_shutdown_in_init(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_RSA) && !defined(NO_CERTS) && !defined(NO_SHA256)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int len = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfSSLv23_server_method), 0);
|
|
|
|
/* Handshake not started. */
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(test_ctx.s_len, 0);
|
|
|
|
/* Handshake in progress. */
|
|
ExpectIntEQ(wolfSSL_negotiate(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntGT((len = test_ctx.s_len), 0);
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
/* No close_notify appended to the ClientHello. */
|
|
ExpectIntEQ(test_ctx.s_len, len);
|
|
/* The refused shutdown must not have failed the handshake either. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Refusing must not disable the guard: ssl->error is left alone, so a
|
|
* second call still refuses rather than taking the failed-handshake
|
|
* branch and sending the alert this exists to suppress. */
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(test_ctx.s_len, len);
|
|
|
|
#ifdef WOLFSSL_NONBLOCK_OCSP
|
|
/* A pending non-blocking OCSP lookup is an in-flight handshake too.
|
|
* ProcessPeerCerts() resumes off this error, so sending the alert - which
|
|
* overwrites ssl->error - would strand the handshake. */
|
|
if (ssl_c != NULL)
|
|
ssl_c->error = WC_NO_ERR_TRACE(OCSP_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(test_ctx.s_len, len);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->error, WC_NO_ERR_TRACE(OCSP_WANT_READ));
|
|
ssl_c->error = WC_NO_ERR_TRACE(WANT_READ);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_CERT_SETUP_CB
|
|
/* A certificate setup callback that asked to be called again leaves the
|
|
* handshake resumable as well. */
|
|
if (ssl_c != NULL)
|
|
ssl_c->error = WOLFSSL_ERROR_WANT_X509_LOOKUP;
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(test_ctx.s_len, len);
|
|
if (ssl_c != NULL) {
|
|
ExpectIntEQ(ssl_c->error, WOLFSSL_ERROR_WANT_X509_LOOKUP);
|
|
ssl_c->error = WC_NO_ERR_TRACE(WANT_READ);
|
|
}
|
|
#endif
|
|
|
|
/* Established connection still shuts down normally. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* With OLD_HELLO_ALLOWED a server awaiting its first ClientHello reads a record
|
|
* whose first byte is neither handshake nor change_cipher_spec as an SSLv2
|
|
* ClientHello, so these records never reach the ContentType check. */
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(OLD_HELLO_ALLOWED) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
|
|
/* Require a server built from `method` to reject `rec` with
|
|
* UNKNOWN_RECORD_TYPE only after putting a fatal unexpected_message alert on
|
|
* the wire. */
|
|
static int test_tls_unknown_record_type_case(method_provider method,
|
|
const byte* rec, int recSz)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* Cleartext alert the server is expected to emit. */
|
|
const byte unexpectedAlert[] = {
|
|
0x15, 0x03, 0x03, 0x00, 0x02,
|
|
0x02, /* level: fatal */
|
|
0x0a /* description: unexpected_message (10) */
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)rec,
|
|
recSz), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, method), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(UNKNOWN_RECORD_TYPE));
|
|
ExpectIntEQ(test_ctx.c_len, sizeof(unexpectedAlert));
|
|
ExpectBufEQ(test_ctx.c_buff, unexpectedAlert, sizeof(unexpectedAlert));
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && !OLD_HELLO_ALLOWED &&
|
|
* (!NO_TLS12 || TLS13) */
|
|
|
|
/* An undefined outer record ContentType must be answered with a fatal
|
|
* unexpected_message alert before the connection is torn down (RFC 8446/9846
|
|
* section 5, RFC 5246 section 6). Covers both rejection sites: the ContentType
|
|
* whitelist in GetRecordHeader(), and the dispatch switch in DoProcessReplyEx()
|
|
* for types the whitelist admits but no TLS connection handles. The dispatch
|
|
* switch is also reached from removeMsgInnerPadding(), which is not covered
|
|
* here. */
|
|
int test_tls_unknown_record_type_alert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(OLD_HELLO_ALLOWED) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
|
|
/* Undefined ContentType 32. The length must be non-zero, or the zero
|
|
* length check in GetRecordHeader() returns LENGTH_ERROR first. */
|
|
const byte unknownType[] = {
|
|
0x20, 0x03, 0x03, 0x00, 0x01, 0x00
|
|
};
|
|
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
|
|
/* dtls12_cid (25) passes the ContentType whitelist whenever the CID
|
|
* feature is built, but has no handler on a TLS connection. */
|
|
const byte cidType[] = {
|
|
0x19, 0x03, 0x03, 0x00, 0x01, 0x00
|
|
};
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS13
|
|
/* ack (26) likewise passes the whitelist but is only handled for
|
|
* DTLS 1.3. */
|
|
const byte ackType[] = {
|
|
0x1a, 0x03, 0x03, 0x00, 0x01, 0x00
|
|
};
|
|
#endif
|
|
#ifdef WOLFSSL_TLS13
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY history;
|
|
byte readBuf[64];
|
|
#endif
|
|
|
|
/* Both sites are version independent, so each record runs against every
|
|
* version the build has. This also keeps the DTLS-only types used wherever
|
|
* they are declared. */
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(test_tls_unknown_record_type_case(wolfTLSv1_2_server_method,
|
|
unknownType, (int)sizeof(unknownType)), TEST_SUCCESS);
|
|
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
|
|
ExpectIntEQ(test_tls_unknown_record_type_case(wolfTLSv1_2_server_method,
|
|
cidType, (int)sizeof(cidType)), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS13
|
|
ExpectIntEQ(test_tls_unknown_record_type_case(wolfTLSv1_2_server_method,
|
|
ackType, (int)sizeof(ackType)), TEST_SUCCESS);
|
|
#endif
|
|
#endif /* !WOLFSSL_NO_TLS12 */
|
|
#ifdef WOLFSSL_TLS13
|
|
ExpectIntEQ(test_tls_unknown_record_type_case(wolfTLSv1_3_server_method,
|
|
unknownType, (int)sizeof(unknownType)), TEST_SUCCESS);
|
|
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
|
|
ExpectIntEQ(test_tls_unknown_record_type_case(wolfTLSv1_3_server_method,
|
|
cidType, (int)sizeof(cidType)), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS13
|
|
ExpectIntEQ(test_tls_unknown_record_type_case(wolfTLSv1_3_server_method,
|
|
ackType, (int)sizeof(ackType)), TEST_SUCCESS);
|
|
#endif
|
|
|
|
/* After a completed TLS 1.3 handshake the alert is encrypted, so it is
|
|
* decoded by letting the peer read it and inspecting its received alert
|
|
* history. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Drop any queued post-handshake traffic (session tickets) so only the
|
|
* crafted record and the resulting alert remain in the buffers. */
|
|
test_memio_clear_buffer(&test_ctx, 1);
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char*)unknownType, (int)sizeof(unknownType)), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)),
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(UNKNOWN_RECORD_TYPE));
|
|
|
|
/* Server consumes the encrypted alert and records it. */
|
|
ExpectIntLE(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), 0);
|
|
XMEMSET(&history, 0, sizeof(history));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_s, &history), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(history.last_rx.code, unexpected_message);
|
|
ExpectIntEQ(history.last_rx.level, alert_fatal);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif /* WOLFSSL_TLS13 */
|
|
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && !OLD_HELLO_ALLOWED &&
|
|
* (!NO_TLS12 || TLS13) */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_curve_intersection(void) {
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_ECC) && \
|
|
defined(HAVE_CURVE25519)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int ret;
|
|
const char* curve_name;
|
|
int test1[] = {WOLFSSL_ECC_SECP256R1};
|
|
int test2[] = {WOLFSSL_ECC_SECP384R1};
|
|
int test3[] = {WOLFSSL_ECC_SECP256R1, WOLFSSL_ECC_SECP384R1};
|
|
int test4[] = {WOLFSSL_ECC_SECP384R1, WOLFSSL_ECC_SECP256R1};
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c,
|
|
test1, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Fix: Get curve name and compare with string comparison or use curve
|
|
* ID function */
|
|
curve_name = wolfSSL_get_curve_name(ssl_s);
|
|
/* or use appropriate string comparison */
|
|
ExpectStrEQ(curve_name, "SECP256R1");
|
|
curve_name = wolfSSL_get_curve_name(ssl_c);
|
|
ExpectStrEQ(curve_name, "SECP256R1");
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
ctx_c = NULL;
|
|
ctx_s = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c,
|
|
test2, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s,
|
|
test1, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ret = wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR);
|
|
|
|
/* Fix: Use proper constant or define HANDSHAKE_FAILURE */
|
|
ExpectTrue(ret == WC_NO_ERR_TRACE(ECC_CURVE_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
ctx_c = NULL;
|
|
ctx_s = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c,
|
|
test3, 2),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_s,
|
|
test4, 2),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
curve_name = wolfSSL_get_curve_name(ssl_s);
|
|
ExpectStrEQ(curve_name, "SECP256R1");
|
|
curve_name = wolfSSL_get_curve_name(ssl_c);
|
|
ExpectStrEQ(curve_name, "SECP256R1");
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_dhe_rsa_pss_sigalg(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_DH) && !defined(NO_RSA) && \
|
|
defined(WC_RSA_PSS) && !defined(NO_SHA256) && defined(HAVE_AESGCM) && \
|
|
!defined(WOLFSSL_HARDEN_TLS) && defined(OPENSSL_EXTRA)
|
|
/* Regression test for S1: SendServerKeyExchange had an inverted guard
|
|
* (#ifndef WC_RSA_PSS) that compiled out the rsa_pss_sa_algo case in the
|
|
* server-side signature self-check for the DHE key exchange path. This
|
|
* test drives a DHE-RSA handshake restricted to RSA-PSS+SHA256 so the
|
|
* server exercises that code path. The bug did not cause the handshake
|
|
* to fail, so we verify by asserting the negotiated sig algorithm. */
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "RSA-PSS+SHA256"), 1);
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "RSA-PSS+SHA256"), 1);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(ssl_s->options.sigAlgo, rsa_pss_sa_algo);
|
|
ExpectIntEQ(ssl_c->options.peerSigAlgo, rsa_pss_sa_algo);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_dhe_renegotiate_larger_prime(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_DH) && !defined(NO_RSA) && !defined(NO_SHA256) && \
|
|
defined(HAVE_AESGCM) && defined(HAVE_ECC) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_FFDHE_2048) && \
|
|
!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) && \
|
|
!defined(WOLFSSL_HARDEN_TLS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte readBuf[16];
|
|
byte p[256];
|
|
byte g[1];
|
|
word32 pSz = (word32)sizeof(p);
|
|
word32 gSz = (word32)sizeof(g);
|
|
int groups[1] = { WOLFSSL_ECC_SECP256R1 };
|
|
|
|
ExpectIntEQ(wc_DhCopyNamedKey(WOLFSSL_FFDHE_2048, p, &pSz, g, &gSz, NULL,
|
|
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_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
/* No FFDHE offer, so the server sends its own 1024-bit parameters. */
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_GetDhKey_Sz(ssl_c), 1024);
|
|
|
|
ExpectIntEQ(wolfSSL_SetTmpDH(ssl_s, p, (int)pSz, g, (int)gSz),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_Rehandshake(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_GetDhKey_Sz(ssl_c), 2048);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_DH) && !defined(NO_RSA) && \
|
|
!defined(NO_SHA256) && defined(HAVE_AESGCM) && defined(HAVE_ECC) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_FFDHE_2048) && \
|
|
!defined(WOLFSSL_REQUIRE_FFDHE) && !defined(WOLFSSL_OLD_PRIME_CHECK) && \
|
|
!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) && \
|
|
!defined(WOLFSSL_HARDEN_TLS)
|
|
#define TEST_TLS12_DHE_REUSE_PRIME_CHECK
|
|
/* A reused client has to test the prime of every server it talks to, no matter
|
|
* how the first handshake's parameters were checked. */
|
|
static int test_tls12_dhe_reuse_prime(int firstFfdhe)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte p[256];
|
|
byte g[1];
|
|
word32 pSz = (word32)sizeof(p);
|
|
word32 gSz = (word32)sizeof(g);
|
|
int groups[1] = { WOLFSSL_ECC_SECP256R1 };
|
|
|
|
ExpectIntEQ(wc_DhCopyNamedKey(WOLFSSL_FFDHE_2048, p, &pSz, g, &gSz, NULL,
|
|
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_set_cipher_list(ssl_c, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
/* No FFDHE offer, so the server sends its own parameters. */
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
|
|
if (firstFfdhe) {
|
|
ExpectIntEQ(wolfSSL_SetTmpDH(ssl_s, p, (int)pSz, g, (int)gSz),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_clear(ssl_c), WOLFSSL_SUCCESS);
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_2_server_method), 0);
|
|
|
|
/* ffdhe2048 p is 2 mod 3, so p - 2 is an odd composite. */
|
|
p[pSz - 1] -= 2;
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetTmpDH(ssl_s, p, (int)pSz, g, (int)gSz),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetEnableDhKeyTest(ssl_s, 0), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(DH_CHECK_PUB_E));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
int test_tls12_dhe_reuse_prime_check(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS12_DHE_REUSE_PRIME_CHECK
|
|
ExpectIntEQ(test_tls12_dhe_reuse_prime(0), TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls12_dhe_reuse_prime(1), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_ske_sig_param_binding(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_DH) && !defined(NO_RSA) && \
|
|
!defined(NO_SHA256) && defined(HAVE_AESGCM) && \
|
|
!defined(WOLFSSL_HARDEN_TLS) && defined(OPENSSL_EXTRA)
|
|
/* Negative test for the client-side ServerKeyExchange signature-content
|
|
* check in DoServerKeyExchange (classic rsa_sa_algo / PKCS#1 v1.5 path). */
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* msg = NULL;
|
|
int msgSz = 0;
|
|
int i;
|
|
int skeIdx = -1;
|
|
int off = 0;
|
|
int len = 0;
|
|
byte* b = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* Force the classic RSA PKCS#1 v1.5 signature path (not RSA-PSS). */
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "RSA+SHA256"), 1);
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "RSA+SHA256"), 1);
|
|
/* Send each handshake message in its own record so the ServerKeyExchange
|
|
* can be located and tampered with individually. */
|
|
ExpectIntEQ(wolfSSL_clear_group_messages(ssl_s), 1);
|
|
|
|
/* Client sends ClientHello. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* Server sends ServerHello..ServerKeyExchange..ServerHelloDone. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Locate the ServerKeyExchange record in the server->client queue. */
|
|
for (i = 0; test_memio_get_message(&test_ctx, 1, &msg, &msgSz, i) == 0;
|
|
i++) {
|
|
if (msgSz > 10 && (byte)msg[0] == handshake &&
|
|
(byte)msg[5] == server_key_exchange) {
|
|
skeIdx = i;
|
|
break;
|
|
}
|
|
}
|
|
ExpectIntGE(skeIdx, 0);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
/* Layout: record hdr[5] | hs hdr[4] | ServerDHParams. ServerDHParams
|
|
* is dh_p, dh_g, dh_Ys, each opaque<1..2^16-1> (a 2-byte length
|
|
* followed by that many bytes), then the signature. Walk to dh_Ys
|
|
* and corrupt its last byte. */
|
|
b = (byte*)msg;
|
|
/* start of dh_p */
|
|
off = 9;
|
|
len = (b[off] << 8) | b[off + 1]; off += 2 + len;
|
|
/* skip dh_p */
|
|
ExpectIntLT(off + 2, msgSz);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
/* skip dh_g */
|
|
len = (b[off] << 8) | b[off + 1]; off += 2 + len;
|
|
ExpectIntLT(off + 2, msgSz);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
/* dh_Ys length */
|
|
len = (b[off] << 8) | b[off + 1]; off += 2;
|
|
ExpectIntGE(len, 1);
|
|
ExpectIntLE(off + len, msgSz);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
/* corrupt server DH pub value */
|
|
b[off + len - 1] ^= 0x01;
|
|
}
|
|
|
|
/* Client consumes the tampered flight. It must notice that
|
|
* the signature no longer matches the parameters and fail. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
/* Confirm the classic PKCS#1 v1.5 path was exercised... */
|
|
ExpectIntEQ(ssl_c->options.peerSigAlgo, rsa_sa_algo);
|
|
/* ...and that the parameter/signature mismatch
|
|
* was the reason for failure. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1),
|
|
WC_NO_ERR_TRACE(VERIFY_SIGN_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_bad_cv_sig_content(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_DH) && !defined(NO_RSA) && \
|
|
!defined(NO_SHA256) && defined(HAVE_AESGCM) && \
|
|
!defined(WOLFSSL_HARDEN_TLS) && defined(OPENSSL_EXTRA) && \
|
|
!defined(WOLFSSL_NO_CLIENT_AUTH)
|
|
/* Negative test for the server-side CertificateVerify signature-content
|
|
* check in DoCertificateVerify (classic rsa_sa_algo / PKCS#1 v1.5 path).
|
|
* That XMEMCMP is the only proof the client holds the private key for the
|
|
* certificate it presented; peerAuthGood is set from havePeerCert whenever
|
|
* ret == 0, so it cannot catch a deleted compare. */
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* msg = NULL;
|
|
int msgSz = 0;
|
|
int i;
|
|
int ckeIdx = -1;
|
|
int off = 0;
|
|
int len = 0;
|
|
byte* b = 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);
|
|
|
|
/* Pin classic safe-prime DH params (no q) so
|
|
* a one-byte change to the client's DH public value still passes
|
|
* the server's range-only public-key check and
|
|
* reaches the CertificateVerify content comparison. */
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_SetTmpDH_file(ssl_s, dhParamFile, WOLFSSL_FILETYPE_PEM),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* Client presents an RSA certificate so its CertificateVerify is signed on
|
|
* the classic rsa_sa_algo path. */
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "DHE-RSA-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* Force the classic RSA PKCS#1 v1.5 path for the CertificateVerify. */
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "RSA+SHA256"), 1);
|
|
#ifdef HAVE_ECC
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "RSA+SHA256:ECDSA+SHA256"), 1);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "RSA+SHA256"), 1);
|
|
#endif
|
|
/* Send each client handshake message in its own record so the
|
|
* ClientKeyExchange can be located and tampered with individually. */
|
|
ExpectIntEQ(wolfSSL_clear_group_messages(ssl_c), 1);
|
|
|
|
/* Client sends ClientHello. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* Server sends ServerHello..CertificateRequest..ServerHelloDone. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* Client sends Certificate, ClientKeyExchange, CertificateVerify, ... */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Locate the ClientKeyExchange record in the client->server queue. */
|
|
for (i = 0; test_memio_get_message(&test_ctx, 0, &msg, &msgSz, i) == 0;
|
|
i++) {
|
|
if (msgSz > 11 && (byte)msg[0] == handshake &&
|
|
(byte)msg[5] == client_key_exchange) {
|
|
ckeIdx = i;
|
|
break;
|
|
}
|
|
}
|
|
ExpectIntGE(ckeIdx, 0);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
/* Layout: record hdr[5] | hs hdr[4] | dh_Yc (opaque<1..2^16-1>: a
|
|
* 2-byte length followed by that many bytes). Corrupt the last byte of
|
|
* the client's DH public value so the server hashes a transcript that
|
|
* differs from the one the client signed in its CertificateVerify. */
|
|
b = (byte*)msg;
|
|
off = 9;
|
|
len = (b[off] << 8) | b[off + 1]; off += 2;
|
|
ExpectIntGE(len, 1);
|
|
ExpectIntLE(off + len, msgSz);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
/* corrupt client DH pub value */
|
|
b[off + len - 1] ^= 0x01;
|
|
}
|
|
|
|
/* Server consumes the client's flight, the CertificateVerify signature no
|
|
* longer matches the transcript and must be rejected. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
/* Confirm the classic PKCS#1 v1.5 path was exercised... */
|
|
ExpectIntEQ(ssl_s->options.peerSigAlgo, rsa_sa_algo);
|
|
/* ...that the client was not accepted as authenticated... */
|
|
ExpectIntEQ(ssl_s->options.peerAuthGood, 0);
|
|
/* ...and that the content mismatch was the reason for failure. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1),
|
|
WC_NO_ERR_TRACE(VERIFY_CERT_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_curve_intersection(void) {
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_ECC) && defined(HAVE_CURVE25519)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* curve_name;
|
|
int test1[] ={WOLFSSL_ECC_SECP256R1};
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c,
|
|
test1, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
curve_name = wolfSSL_get_curve_name(ssl_s);
|
|
ExpectStrEQ(curve_name, "SECP256R1");
|
|
curve_name = wolfSSL_get_curve_name(ssl_c);
|
|
ExpectStrEQ(curve_name, "SECP256R1");
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
|
|
int test_tls_certreq_order(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_AESGCM) && \
|
|
defined(WOLFSSL_AES_256) && defined(WOLFSSL_SHA384) && !defined(NO_RSA) && \
|
|
defined(HAVE_ECC)
|
|
/* This test checks that a certificate request message
|
|
* received before server certificate message is properly detected.
|
|
*/
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int i = 0;
|
|
const char* msg = NULL;
|
|
int msgSz = 0;
|
|
int certIdx = 0;
|
|
int certReqIdx = 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);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectIntEQ(wolfSSL_clear_group_messages(ssl_s), 1);
|
|
|
|
/* start handshake, send first ClientHello */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* send server's flight */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
for (i = 0; test_memio_get_message(&test_ctx, 1, &msg, &msgSz, i) == 0; i++) {
|
|
if (msg[5] == 11) /* cert */
|
|
certIdx = i;
|
|
if (msg[5] == 13) /* certreq */
|
|
certReqIdx = i;
|
|
}
|
|
ExpectIntNE(certIdx, 0);
|
|
ExpectIntNE(certReqIdx, 0);
|
|
ExpectIntEQ(test_memio_move_message(&test_ctx, 1, certReqIdx, certIdx), 0);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), OUT_OF_ORDER_E);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A TLS 1.2 CertificateRequest carrying a supported_signature_algorithms
|
|
* vector whose length is not a multiple of the 2-byte element size must be
|
|
* rejected. We run a real handshake, locate the server's CertificateRequest
|
|
* in the memio queue and make the sig-algs length odd before the client parses
|
|
* it. The vector is shrunk by one byte and the
|
|
* record, handshake and sig-algs length fields are all decremented so the
|
|
* message stays self-consistent (only the sig-algs length parity is wrong).
|
|
* Without the fix the client would silently ignore the odd trailing byte and
|
|
* accept the message; with the fix it is rejected with BUFFER_ERROR. */
|
|
int test_tls12_certreq_odd_sigalgs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* msg = NULL;
|
|
int msgSz = 0;
|
|
int i = 0;
|
|
int certReqIdx = -1;
|
|
int certTypesCnt = 0;
|
|
int sigAlgsLenOff = 0;
|
|
int sigAlgsLen = 0;
|
|
int recAbs = 0;
|
|
int removeAbs = 0;
|
|
word32 val = 0;
|
|
byte* b = 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);
|
|
/* Make the server send a CertificateRequest. */
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
/* Send each handshake message in its own record so the CertificateRequest
|
|
* can be located and tampered with individually. */
|
|
ExpectIntEQ(wolfSSL_clear_group_messages(ssl_s), 1);
|
|
|
|
/* Client sends ClientHello. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
/* Server sends ServerHello..CertificateRequest..ServerHelloDone. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Locate the CertificateRequest record in the server->client queue. */
|
|
for (i = 0; test_memio_get_message(&test_ctx, 1, &msg, &msgSz, i) == 0;
|
|
i++) {
|
|
if (msgSz > 12 && (byte)msg[5] == certificate_request) {
|
|
certReqIdx = i;
|
|
break;
|
|
}
|
|
}
|
|
ExpectIntGE(certReqIdx, 0);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
/* Layout: record hdr[5] | hs hdr[4] | certTypesCount[1] | certTypes |
|
|
* sigAlgsLen[2] | certTypes... The sig-algs length is even; shrink the
|
|
* vector by one byte to make it odd while keeping all length fields
|
|
* consistent. */
|
|
certTypesCnt = (byte)msg[9];
|
|
sigAlgsLenOff = 10 + certTypesCnt;
|
|
ExpectIntLT(sigAlgsLenOff + 2, msgSz);
|
|
if (EXPECT_SUCCESS()) {
|
|
sigAlgsLen = ((byte)msg[sigAlgsLenOff] << 8) |
|
|
(byte)msg[sigAlgsLenOff + 1];
|
|
/* Need at least two pairs so a valid pair remains after shrinking. */
|
|
ExpectIntGE(sigAlgsLen, 2 * HELLO_EXT_SIGALGO_SZ);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
b = (byte*)msg;
|
|
/* Decrement record length (bytes 3..4). */
|
|
val = ((word32)b[3] << 8) | b[4];
|
|
val--;
|
|
b[3] = (byte)(val >> 8); b[4] = (byte)val;
|
|
/* Decrement handshake length (bytes 6..8). */
|
|
val = ((word32)b[6] << 16) | ((word32)b[7] << 8) | b[8];
|
|
val--;
|
|
b[6] = (byte)(val >> 16); b[7] = (byte)(val >> 8); b[8] = (byte)val;
|
|
/* Decrement sig-algs length, making it odd. */
|
|
val = (word32)sigAlgsLen - 1;
|
|
b[sigAlgsLenOff] = (byte)(val >> 8);
|
|
b[sigAlgsLenOff + 1] = (byte)val;
|
|
/* Drop the last byte of the sig-algs vector from the buffer. */
|
|
recAbs = (int)((const byte*)msg - test_ctx.c_buff);
|
|
removeAbs = recAbs + 12 + certTypesCnt + sigAlgsLen - 1;
|
|
ExpectIntEQ(test_memio_remove_from_buffer(&test_ctx, 1, removeAbs,
|
|
1), 0);
|
|
}
|
|
}
|
|
|
|
/* Client must reject the malformed CertificateRequest. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WC_NO_ERR_TRACE(BUFFER_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_CLIENT_AUTH) && \
|
|
!defined(NO_FILESYSTEM)
|
|
/* Called when writing. */
|
|
static int CsSend(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
(void)ssl;
|
|
(void)buf;
|
|
(void)ctx;
|
|
|
|
return sz;
|
|
}
|
|
/* Called when reading. */
|
|
static int CsRecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|
{
|
|
WOLFSSL_BUFFER_INFO* msg = (WOLFSSL_BUFFER_INFO*)ctx;
|
|
int len = (int)msg->length;
|
|
|
|
(void)ssl;
|
|
(void)sz;
|
|
|
|
/* Pass back as much of message as will fit in buffer. */
|
|
if (len > sz)
|
|
len = sz;
|
|
XMEMCPY(buf, msg->buffer, len);
|
|
/* Move over returned data. */
|
|
msg->buffer += len;
|
|
msg->length -= len;
|
|
|
|
/* Amount actually copied. */
|
|
return len;
|
|
}
|
|
#endif
|
|
|
|
int test_tls12_bad_cv_sig_alg(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_CLIENT_AUTH) && \
|
|
!defined(NO_FILESYSTEM)
|
|
byte clientMsgs[] = {
|
|
/* Client Hello */
|
|
0x16, 0x03, 0x03, 0x00, 0xe7,
|
|
0x01, 0x00, 0x00, 0xe3, 0x03, 0x03, 0x65, 0x27,
|
|
0x41, 0xdf, 0xd9, 0x17, 0xdb, 0x02, 0x5c, 0x2e,
|
|
0xf8, 0x4b, 0x77, 0x86, 0x5a, 0x20, 0x57, 0x7f,
|
|
0xc0, 0xe7, 0xef, 0x8f, 0x56, 0xef, 0xfa, 0x71,
|
|
0x36, 0xec, 0x55, 0x1d, 0x4e, 0xa2, 0x00, 0x00,
|
|
0x64, 0xc0, 0x2c, 0xc0, 0x2b, 0xc0, 0x30, 0xc0,
|
|
0x2f, 0x00, 0x9f, 0x00, 0x9e, 0x00, 0xab, 0x00,
|
|
0x34, 0x00, 0xa7, 0x00, 0xaa, 0xcc, 0xa9, 0xcc,
|
|
0xa8, 0xcc, 0xaa, 0xc0, 0x27, 0xc0, 0x23, 0xc0,
|
|
0x28, 0xc0, 0x24, 0xc0, 0x0a, 0xc0, 0x09, 0xc0,
|
|
0x07, 0xc0, 0x14, 0xc0, 0x13, 0xc0, 0x11, 0xc0,
|
|
0xac, 0xc0, 0xae, 0xc0, 0xaf, 0x00, 0x6b, 0x00,
|
|
0x67, 0x00, 0x39, 0x00, 0x33, 0xcc, 0x14, 0xcc,
|
|
0x13, 0xcc, 0x15, 0xc0, 0x06, 0x00, 0xb3, 0x00,
|
|
0xb2, 0xc0, 0xa6, 0xc0, 0xa7, 0xcc, 0xab, 0xcc,
|
|
0xac, 0xcc, 0xad, 0xc0, 0x37, 0xd0, 0x01, 0x00,
|
|
0xb5, 0xc0, 0x3a, 0x00, 0xb4, 0x00, 0x45, 0x00,
|
|
0x88, 0x00, 0xbe, 0x00, 0xc4, 0x01, 0x00, 0x00,
|
|
0x56, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x1e, 0x06,
|
|
0x03, 0x05, 0x03, 0x04, 0x03, 0x08, 0x07, 0x08,
|
|
0x08, 0x08, 0x06, 0x08, 0x0b, 0x08, 0x05, 0x08,
|
|
0x0a, 0x08, 0x04, 0x08, 0x09, 0x06, 0x01, 0x05,
|
|
0x01, 0x04, 0x01, 0x03, 0x01, 0x00, 0x0b, 0x00,
|
|
0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x1c, 0x00,
|
|
0x1a, 0x00, 0x19, 0x00, 0x1c, 0x00, 0x18, 0x00,
|
|
0x1b, 0x00, 0x1e, 0x00, 0x17, 0x00, 0x16, 0x00,
|
|
0x1a, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x14, 0x01,
|
|
0x01, 0x01, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
|
0x23, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00,
|
|
/* Certificate */
|
|
0x16, 0x03, 0x03, 0x05, 0x2b,
|
|
0x0b, 0x00, 0x05, 0x27, 0x00, 0x05, 0x24, 0x00,
|
|
0x05, 0x21, 0x30, 0x82, 0x05, 0x1d, 0x30, 0x82,
|
|
0x04, 0x05, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
|
|
0x14, 0x4f, 0x0d, 0x8c, 0xc5, 0xfa, 0xee, 0xa2,
|
|
0x9b, 0xb7, 0x35, 0x9e, 0xe9, 0x4a, 0x17, 0x99,
|
|
0xf0, 0xcc, 0x23, 0xf2, 0xec, 0x30, 0x0d, 0x06,
|
|
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
|
|
0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x9e, 0x31,
|
|
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
|
|
0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e,
|
|
0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x07, 0x4d,
|
|
0x6f, 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x31, 0x10,
|
|
0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c,
|
|
0x07, 0x42, 0x6f, 0x7a, 0x65, 0x6d, 0x61, 0x6e,
|
|
0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04,
|
|
0x0a, 0x0c, 0x0c, 0x77, 0x6f, 0x6c, 0x66, 0x53,
|
|
0x53, 0x4c, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x31,
|
|
0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b,
|
|
0x0c, 0x10, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61,
|
|
0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x2d, 0x32, 0x30,
|
|
0x34, 0x38, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03,
|
|
0x55, 0x04, 0x03, 0x0c, 0x0f, 0x77, 0x77, 0x77,
|
|
0x2e, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73, 0x6c,
|
|
0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x1f, 0x30, 0x1d,
|
|
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
|
|
0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6e, 0x66,
|
|
0x6f, 0x40, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73,
|
|
0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17,
|
|
0x0d, 0x32, 0x34, 0x31, 0x32, 0x31, 0x38, 0x32,
|
|
0x31, 0x32, 0x35, 0x32, 0x39, 0x5a, 0x17, 0x0d,
|
|
0x32, 0x37, 0x30, 0x39, 0x31, 0x34, 0x32, 0x31,
|
|
0x32, 0x35, 0x32, 0x39, 0x5a, 0x30, 0x81, 0x9e,
|
|
0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
|
|
0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30,
|
|
0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x07,
|
|
0x4d, 0x6f, 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x31,
|
|
0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07,
|
|
0x0c, 0x07, 0x42, 0x6f, 0x7a, 0x65, 0x6d, 0x61,
|
|
0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55,
|
|
0x04, 0x0a, 0x0c, 0x0c, 0x77, 0x6f, 0x6c, 0x66,
|
|
0x53, 0x53, 0x4c, 0x5f, 0x32, 0x30, 0x34, 0x38,
|
|
0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04,
|
|
0x0b, 0x0c, 0x10, 0x50, 0x72, 0x6f, 0x67, 0x72,
|
|
0x61, 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x2d, 0x32,
|
|
0x30, 0x34, 0x38, 0x31, 0x18, 0x30, 0x16, 0x06,
|
|
0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x77, 0x77,
|
|
0x77, 0x2e, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73,
|
|
0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x1f, 0x30,
|
|
0x1d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
|
0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6e,
|
|
0x66, 0x6f, 0x40, 0x77, 0x6f, 0x6c, 0x66, 0x73,
|
|
0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82,
|
|
0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
|
|
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
|
|
0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
|
|
0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc3,
|
|
0x03, 0xd1, 0x2b, 0xfe, 0x39, 0xa4, 0x32, 0x45,
|
|
0x3b, 0x53, 0xc8, 0x84, 0x2b, 0x2a, 0x7c, 0x74,
|
|
0x9a, 0xbd, 0xaa, 0x2a, 0x52, 0x07, 0x47, 0xd6,
|
|
0xa6, 0x36, 0xb2, 0x07, 0x32, 0x8e, 0xd0, 0xba,
|
|
0x69, 0x7b, 0xc6, 0xc3, 0x44, 0x9e, 0xd4, 0x81,
|
|
0x48, 0xfd, 0x2d, 0x68, 0xa2, 0x8b, 0x67, 0xbb,
|
|
0xa1, 0x75, 0xc8, 0x36, 0x2c, 0x4a, 0xd2, 0x1b,
|
|
0xf7, 0x8b, 0xba, 0xcf, 0x0d, 0xf9, 0xef, 0xec,
|
|
0xf1, 0x81, 0x1e, 0x7b, 0x9b, 0x03, 0x47, 0x9a,
|
|
0xbf, 0x65, 0xcc, 0x7f, 0x65, 0x24, 0x69, 0xa6,
|
|
0xe8, 0x14, 0x89, 0x5b, 0xe4, 0x34, 0xf7, 0xc5,
|
|
0xb0, 0x14, 0x93, 0xf5, 0x67, 0x7b, 0x3a, 0x7a,
|
|
0x78, 0xe1, 0x01, 0x56, 0x56, 0x91, 0xa6, 0x13,
|
|
0x42, 0x8d, 0xd2, 0x3c, 0x40, 0x9c, 0x4c, 0xef,
|
|
0xd1, 0x86, 0xdf, 0x37, 0x51, 0x1b, 0x0c, 0xa1,
|
|
0x3b, 0xf5, 0xf1, 0xa3, 0x4a, 0x35, 0xe4, 0xe1,
|
|
0xce, 0x96, 0xdf, 0x1b, 0x7e, 0xbf, 0x4e, 0x97,
|
|
0xd0, 0x10, 0xe8, 0xa8, 0x08, 0x30, 0x81, 0xaf,
|
|
0x20, 0x0b, 0x43, 0x14, 0xc5, 0x74, 0x67, 0xb4,
|
|
0x32, 0x82, 0x6f, 0x8d, 0x86, 0xc2, 0x88, 0x40,
|
|
0x99, 0x36, 0x83, 0xba, 0x1e, 0x40, 0x72, 0x22,
|
|
0x17, 0xd7, 0x52, 0x65, 0x24, 0x73, 0xb0, 0xce,
|
|
0xef, 0x19, 0xcd, 0xae, 0xff, 0x78, 0x6c, 0x7b,
|
|
0xc0, 0x12, 0x03, 0xd4, 0x4e, 0x72, 0x0d, 0x50,
|
|
0x6d, 0x3b, 0xa3, 0x3b, 0xa3, 0x99, 0x5e, 0x9d,
|
|
0xc8, 0xd9, 0x0c, 0x85, 0xb3, 0xd9, 0x8a, 0xd9,
|
|
0x54, 0x26, 0xdb, 0x6d, 0xfa, 0xac, 0xbb, 0xff,
|
|
0x25, 0x4c, 0xc4, 0xd1, 0x79, 0xf4, 0x71, 0xd3,
|
|
0x86, 0x40, 0x18, 0x13, 0xb0, 0x63, 0xb5, 0x72,
|
|
0x4e, 0x30, 0xc4, 0x97, 0x84, 0x86, 0x2d, 0x56,
|
|
0x2f, 0xd7, 0x15, 0xf7, 0x7f, 0xc0, 0xae, 0xf5,
|
|
0xfc, 0x5b, 0xe5, 0xfb, 0xa1, 0xba, 0xd3, 0x02,
|
|
0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x4f,
|
|
0x30, 0x82, 0x01, 0x4b, 0x30, 0x1d, 0x06, 0x03,
|
|
0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x33,
|
|
0xd8, 0x45, 0x66, 0xd7, 0x68, 0x87, 0x18, 0x7e,
|
|
0x54, 0x0d, 0x70, 0x27, 0x91, 0xc7, 0x26, 0xd7,
|
|
0x85, 0x65, 0xc0, 0x30, 0x81, 0xde, 0x06, 0x03,
|
|
0x55, 0x1d, 0x23, 0x04, 0x81, 0xd6, 0x30, 0x81,
|
|
0xd3, 0x80, 0x14, 0x33, 0xd8, 0x45, 0x66, 0xd7,
|
|
0x68, 0x87, 0x18, 0x7e, 0x54, 0x0d, 0x70, 0x27,
|
|
0x91, 0xc7, 0x26, 0xd7, 0x85, 0x65, 0xc0, 0xa1,
|
|
0x81, 0xa4, 0xa4, 0x81, 0xa1, 0x30, 0x81, 0x9e,
|
|
0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
|
|
0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30,
|
|
0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x07,
|
|
0x4d, 0x6f, 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x31,
|
|
0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07,
|
|
0x0c, 0x07, 0x42, 0x6f, 0x7a, 0x65, 0x6d, 0x61,
|
|
0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55,
|
|
0x04, 0x0a, 0x0c, 0x0c, 0x77, 0x6f, 0x6c, 0x66,
|
|
0x53, 0x53, 0x4c, 0x5f, 0x32, 0x30, 0x34, 0x38,
|
|
0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04,
|
|
0x0b, 0x0c, 0x10, 0x50, 0x72, 0x6f, 0x67, 0x72,
|
|
0x61, 0x6d, 0x6d, 0x69, 0x6e, 0x67, 0x2d, 0x32,
|
|
0x30, 0x34, 0x38, 0x31, 0x18, 0x30, 0x16, 0x06,
|
|
0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x77, 0x77,
|
|
0x77, 0x2e, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73,
|
|
0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x1f, 0x30,
|
|
0x1d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
|
0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6e,
|
|
0x66, 0x6f, 0x40, 0x77, 0x6f, 0x6c, 0x66, 0x73,
|
|
0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x82, 0x14,
|
|
0x4f, 0x0d, 0x8c, 0xc5, 0xfa, 0xee, 0xa2, 0x9b,
|
|
0xb7, 0x35, 0x9e, 0xe9, 0x4a, 0x17, 0x99, 0xf0,
|
|
0xcc, 0x23, 0xf2, 0xec, 0x30, 0x0c, 0x06, 0x03,
|
|
0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01,
|
|
0x01, 0xff, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x1d,
|
|
0x11, 0x04, 0x15, 0x30, 0x13, 0x82, 0x0b, 0x65,
|
|
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63,
|
|
0x6f, 0x6d, 0x87, 0x04, 0x7f, 0x00, 0x00, 0x01,
|
|
0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
|
|
0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01,
|
|
0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b,
|
|
0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
|
|
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
|
0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82,
|
|
0x01, 0x01, 0x00, 0x46, 0xab, 0xe4, 0x6d, 0xae,
|
|
0x49, 0x5b, 0x6a, 0x0b, 0xa9, 0x87, 0xe1, 0x95,
|
|
0x32, 0xa6, 0xd7, 0xae, 0xde, 0x28, 0xdc, 0xc7,
|
|
0x99, 0x68, 0xe2, 0x5f, 0xc9, 0x5a, 0x4c, 0x64,
|
|
0xb8, 0xf5, 0x28, 0x42, 0x5a, 0xe8, 0x5c, 0x59,
|
|
0x32, 0xfe, 0xd0, 0x1f, 0x0b, 0x55, 0x89, 0xdb,
|
|
0x67, 0xe7, 0x78, 0xf3, 0x70, 0xcf, 0x18, 0x51,
|
|
0x57, 0x8b, 0xf3, 0x2b, 0xa4, 0x66, 0x0b, 0xf6,
|
|
0x03, 0x6e, 0x11, 0xac, 0x83, 0x52, 0x16, 0x7e,
|
|
0xa2, 0x7c, 0x36, 0x77, 0xf6, 0xbb, 0x13, 0x19,
|
|
0x40, 0x2c, 0xb8, 0x8c, 0xca, 0xd6, 0x7e, 0x79,
|
|
0x7d, 0xf4, 0x14, 0x8d, 0xb5, 0xa4, 0x09, 0xf6,
|
|
0x2d, 0x4c, 0xe7, 0xf9, 0xb8, 0x25, 0x41, 0x15,
|
|
0x78, 0xf4, 0xca, 0x80, 0x41, 0xea, 0x3a, 0x05,
|
|
0x08, 0xf6, 0xb5, 0x5b, 0xa1, 0x3b, 0x5b, 0x48,
|
|
0xa8, 0x4b, 0x8c, 0x19, 0x8d, 0x6c, 0x87, 0x31,
|
|
0x76, 0x74, 0x02, 0x16, 0x8b, 0xdd, 0x7f, 0xd1,
|
|
0x11, 0x62, 0x27, 0x42, 0x39, 0xe0, 0x9a, 0x63,
|
|
0x26, 0x31, 0x19, 0xce, 0x3d, 0x41, 0xd5, 0x24,
|
|
0x47, 0x32, 0x0f, 0x76, 0xd6, 0x41, 0x37, 0x44,
|
|
0xad, 0x73, 0xf1, 0xb8, 0xec, 0x2b, 0x6e, 0x9c,
|
|
0x4f, 0x84, 0xc4, 0x4e, 0xd7, 0x92, 0x10, 0x7e,
|
|
0x23, 0x32, 0xa0, 0x75, 0x6a, 0xe7, 0xfe, 0x55,
|
|
0x95, 0x9f, 0x0a, 0xad, 0xdf, 0xf9, 0x2a, 0xa2,
|
|
0x1a, 0x59, 0xd5, 0x82, 0x63, 0xd6, 0x5d, 0x7d,
|
|
0x79, 0xf4, 0xa7, 0x2d, 0xdc, 0x8c, 0x04, 0xcd,
|
|
0x98, 0xb0, 0x42, 0x0e, 0x84, 0xfa, 0x86, 0x50,
|
|
0x10, 0x61, 0xac, 0x73, 0xcd, 0x79, 0x45, 0x30,
|
|
0xe8, 0x42, 0xa1, 0x6a, 0xf6, 0x77, 0x55, 0xec,
|
|
0x07, 0xdb, 0x52, 0x29, 0xca, 0x7a, 0xc8, 0xa2,
|
|
0xda, 0xe9, 0xf5, 0x98, 0x33, 0x6a, 0xe8, 0xbc,
|
|
0x89, 0xed, 0x01, 0xe2, 0xfe, 0x44, 0x86, 0x86,
|
|
0x80, 0x39, 0xec,
|
|
/* ClientKeyExchange */
|
|
0x16, 0x03, 0x03, 0x00, 0x46,
|
|
0x10, 0x00, 0x00, 0x42, 0x41, 0x04, 0xc5, 0xb9,
|
|
0x0f, 0xbc, 0x84, 0xe6, 0x0c, 0x02, 0xa6, 0x8d,
|
|
0x34, 0xa6, 0x3e, 0x1e, 0xb7, 0x88, 0xb8, 0x68,
|
|
0x29, 0x2b, 0x85, 0x67, 0xe2, 0x62, 0x4d, 0xd9,
|
|
0xa4, 0x38, 0xb3, 0xec, 0x33, 0xa1, 0xe5, 0xe1,
|
|
0xae, 0xe9, 0x07, 0xd1, 0xea, 0x1b, 0xec, 0xa6,
|
|
0xaf, 0x1f, 0x80, 0x87, 0x7c, 0x53, 0x80, 0x04,
|
|
0xee, 0x20, 0xeb, 0x64, 0x0d, 0xa0, 0xf7, 0x62,
|
|
0xb1, 0xcc, 0x73, 0x97, 0xf5, 0x80,
|
|
/* CertificateVerify */
|
|
0x16, 0x03, 0x03, 0x01, 0x08,
|
|
/* 0x04 - sha256, changed to 0x02 - sha1 */
|
|
0x0f, 0x00, 0x01, 0x04, 0x08, 0x02, 0x01, 0x00,
|
|
0x8b, 0x09, 0xa4, 0x58, 0x8d, 0x68, 0xd9, 0xc9,
|
|
0xef, 0xe9, 0xa5, 0x98, 0x7f, 0xa3, 0xa9, 0x7b,
|
|
0x56, 0xf7, 0xaa, 0x5f, 0x8f, 0x47, 0x7f, 0xd0,
|
|
0x7b, 0xcf, 0x4f, 0x84, 0xe1, 0xa9, 0x0e, 0xa8,
|
|
0x83, 0x19, 0xd8, 0xb3, 0x97, 0x23, 0x98, 0xc5,
|
|
0x2b, 0x56, 0x82, 0x66, 0x94, 0xcc, 0xd7, 0x23,
|
|
0xe6, 0x6e, 0x60, 0x83, 0x78, 0xfb, 0xaf, 0x8e,
|
|
0x8b, 0xae, 0x1f, 0x3c, 0x34, 0x96, 0x3b, 0xd5,
|
|
0x8d, 0x1e, 0xaf, 0x98, 0x1d, 0x27, 0x86, 0x97,
|
|
0x42, 0xd4, 0xfc, 0x62, 0xbc, 0x43, 0x94, 0x98,
|
|
0x19, 0x26, 0x87, 0xb0, 0x8c, 0xb5, 0x22, 0xa7,
|
|
0x6a, 0x5e, 0x56, 0x73, 0x0a, 0x75, 0xc9, 0xb9,
|
|
0x0e, 0xf7, 0x49, 0x4f, 0xa2, 0x0f, 0xfb, 0xdf,
|
|
0x3e, 0xe4, 0xc8, 0x31, 0x26, 0xc5, 0x5c, 0x83,
|
|
0x9f, 0x13, 0xcb, 0x4c, 0xdc, 0x21, 0xe6, 0x24,
|
|
0x2d, 0xd3, 0xe8, 0x18, 0x04, 0xaf, 0x5c, 0x42,
|
|
0x03, 0xa3, 0x0a, 0xb5, 0xfc, 0xb9, 0xbc, 0x8e,
|
|
0xd3, 0xe0, 0x78, 0xdc, 0xef, 0xb9, 0x91, 0x9f,
|
|
0x5b, 0xdc, 0xe3, 0x84, 0xd2, 0xca, 0x32, 0x33,
|
|
0x00, 0x7c, 0x13, 0xd3, 0x2d, 0x85, 0x65, 0x00,
|
|
0xc0, 0xb0, 0xde, 0x85, 0x37, 0x38, 0x18, 0xd2,
|
|
0x81, 0xd4, 0x35, 0xeb, 0xf1, 0xfb, 0x9f, 0x6c,
|
|
0x96, 0x95, 0xf5, 0xaa, 0xfd, 0x22, 0xca, 0x20,
|
|
0xfd, 0x3b, 0xa9, 0xa7, 0xb6, 0x5a, 0x26, 0x02,
|
|
0xb6, 0x0e, 0xdd, 0xaa, 0x0f, 0xa8, 0x96, 0x18,
|
|
0xaa, 0xb1, 0x79, 0x9c, 0x17, 0xb0, 0x7e, 0xa7,
|
|
0x4f, 0xc0, 0x98, 0x27, 0xbe, 0xac, 0x00, 0xda,
|
|
0x3b, 0x2e, 0xd4, 0x11, 0x41, 0x54, 0x34, 0x53,
|
|
0x5f, 0xc5, 0xcd, 0x72, 0xd7, 0x36, 0x04, 0xe1,
|
|
0x7f, 0xcf, 0x1e, 0x01, 0x97, 0xec, 0xeb, 0xad,
|
|
0x1c, 0xc6, 0x7f, 0x2d, 0x8c, 0x68, 0x29, 0xd1,
|
|
0x93, 0x47, 0x59, 0xc0, 0xe2, 0x4a, 0x36, 0x6c
|
|
};
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_BUFFER_INFO msg;
|
|
|
|
/* Set up wolfSSL context. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
CERT_FILETYPE));
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL);
|
|
}
|
|
/* Read from 'msg'. */
|
|
wolfSSL_SetIORecv(ctx, CsRecv);
|
|
/* No where to send to - dummy sender. */
|
|
wolfSSL_SetIOSend(ctx, CsSend);
|
|
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
msg.buffer = clientMsgs;
|
|
msg.length = (unsigned int)sizeof(clientMsgs);
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SetIOReadCtx(ssl, &msg);
|
|
}
|
|
/* Read all message include CertificateVerify with invalid signature
|
|
* algorithm. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
/* Expect an invalid parameter error. */
|
|
ExpectIntEQ(wolfSSL_get_error(ssl, WOLFSSL_FATAL_ERROR), -425);
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_no_null_compression(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12)
|
|
/* ClientHello with compression list missing the required null method (RFC
|
|
* 5246 7.4.1.2: the list MUST include the null compression method). */
|
|
const byte badClientHello[] = {
|
|
/* record header */
|
|
0x16, 0x03, 0x03, 0x00, 0x2d,
|
|
/* handshake header: ClientHello, length 41 */
|
|
0x01, 0x00, 0x00, 0x29,
|
|
/* client version: TLS 1.2 */
|
|
0x03, 0x03,
|
|
/* random: 32 bytes */
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
/* session id length: 0 */
|
|
0x00,
|
|
/* cipher suites length: 2, TLS_RSA_WITH_AES_128_CBC_SHA */
|
|
0x00, 0x02, 0x00, 0x2f,
|
|
/* compression methods: 1 entry, ZLIB only (null is absent) */
|
|
0x01, 0xdd,
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)badClientHello, sizeof(badClientHello)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(COMPRESSION_ERROR));
|
|
#ifdef WOLFSSL_EXTRA_ALERTS
|
|
{
|
|
const byte illegalParamAlert[] = {
|
|
0x15, /* alert content type */
|
|
0x03, 0x03, /* version: TLS 1.2 */
|
|
0x00, 0x02, /* length: 2 */
|
|
0x02, /* level: fatal */
|
|
0x2f, /* description: illegal_parameter (47) */
|
|
};
|
|
ExpectIntEQ(test_ctx.c_len, (int)sizeof(illegalParamAlert));
|
|
ExpectBufEQ(test_ctx.c_buff, illegalParamAlert,
|
|
sizeof(illegalParamAlert));
|
|
}
|
|
#endif
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8422 Section 5.1.2: a client that sends an ec_point_formats extension
|
|
* omitting the uncompressed (0) format while negotiating an ECC suite must be
|
|
* rejected by the server with a fatal illegal_parameter alert. This drives a
|
|
* real handshake all the way through DoClientHello so the abort path (not just
|
|
* the parse-time detection) is exercised.
|
|
*
|
|
* Rather than hand-craft a ClientHello (which would pin the cipher suite, named
|
|
* group and exact byte offsets, making the test fragile as extension handling
|
|
* evolves), the client builds its own ClientHello and we only suppress the
|
|
* uncompressed point format: TLSX_PopulateExtensions() adds the default
|
|
* uncompressed format only when no ec_point_formats extension already exists,
|
|
* so pre-seeding the client with a compressed-only list makes it advertise
|
|
* exactly that. The curve is negotiated normally, so the test is independent of
|
|
* which named groups are enabled. */
|
|
int test_tls12_ec_point_formats_no_uncompressed(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12) \
|
|
&& defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES) \
|
|
&& defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
|
|
/* Pin an ECDHE (ECC) suite so the server negotiates an ECC key exchange;
|
|
* gating on the BUILD_ macro skips the test in builds where the suite is
|
|
* unavailable (e.g. --disable-aescbc) instead of failing with
|
|
* MATCH_SUITE_ERROR. */
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "ECDHE-RSA-AES128-SHA"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "ECDHE-RSA-AES128-SHA"),
|
|
WOLFSSL_SUCCESS);
|
|
/* Make the client advertise only the compressed point format (1 ==
|
|
* ansiX962_compressed_prime), i.e. omit the uncompressed (0) format. */
|
|
ExpectIntEQ(TLSX_UsePointFormat(&ssl_c->extensions, 1, ssl_c->heap),
|
|
WOLFSSL_SUCCESS);
|
|
/* The server must reject the handshake with a fatal illegal_parameter
|
|
* alert (surfaced as INVALID_PARAMETER), not complete it. */
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(INVALID_PARAMETER));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 7507: a server that supports a version higher than the one offered by the
|
|
* client MUST abort with a fatal inappropriate_fallback alert when the client
|
|
* includes TLS_FALLBACK_SCSV (0x5600) in its cipher list. This enforcement is
|
|
* always compiled in, so it must hold in the default build.
|
|
*
|
|
* The ClientHello below offers TLS 1.2 with TLS_FALLBACK_SCSV present, against a
|
|
* downgrade-capable server whose maximum is TLS 1.3 - i.e. a genuine downgrade.
|
|
* The server must reject it with VERSION_ERROR and send inappropriate_fallback.
|
|
*/
|
|
int test_tls_fallback_scsv(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_TLS13)
|
|
const byte clientHello[] = {
|
|
/* record header: handshake, TLS 1.2, length 47 */
|
|
0x16, 0x03, 0x03, 0x00, 0x2f,
|
|
/* handshake header: ClientHello, length 43 */
|
|
0x01, 0x00, 0x00, 0x2b,
|
|
/* client version: TLS 1.2 (lower than the server's TLS 1.3 max) */
|
|
0x03, 0x03,
|
|
/* random: 32 bytes */
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
/* session id length: 0 */
|
|
0x00,
|
|
/* cipher suites length: 4 */
|
|
0x00, 0x04,
|
|
/* TLS_FALLBACK_SCSV, TLS_RSA_WITH_AES_128_CBC_SHA */
|
|
0x56, 0x00, 0x00, 0x2f,
|
|
/* compression methods: 1 entry, null */
|
|
0x01, 0x00,
|
|
};
|
|
/* Expected fatal inappropriate_fallback (86) alert on the wire. */
|
|
const byte fallbackAlert[] = {
|
|
0x15, /* alert content type */
|
|
0x03, 0x03, /* version: TLS 1.2 */
|
|
0x00, 0x02, /* length: 2 */
|
|
0x02, /* level: fatal */
|
|
0x56, /* description: inappropriate_fallback (86) */
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)clientHello, sizeof(clientHello)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfSSLv23_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
/* The first record on the wire must be the inappropriate_fallback alert.
|
|
* The accept state machine then also queues a generic protocol_version
|
|
* alert for the VERSION_ERROR, which RFC 7507 permits. */
|
|
ExpectIntGE(test_ctx.c_len, (int)sizeof(fallbackAlert));
|
|
ExpectBufEQ(test_ctx.c_buff, fallbackAlert, sizeof(fallbackAlert));
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 8422 Section 5.1.2 ties the missing-uncompressed-format abort to the
|
|
* server actually negotiating an ECC cipher suite. A client that omits the
|
|
* uncompressed point format but negotiates a NON-ECC suite (here DHE_RSA) must
|
|
* NOT be rejected - the handshake completes. This is the complement of
|
|
* test_tls12_ec_point_formats_no_uncompressed and guards against regressing
|
|
* back to an advertised-groups (parse-time) abort.
|
|
*
|
|
* As in that test the client builds a real ClientHello and we only suppress the
|
|
* uncompressed point format (see the comment there); the suite is pinned to a
|
|
* DHE (non-ECC) suite. */
|
|
int test_tls12_ec_point_formats_no_uncompressed_non_ecc(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12) \
|
|
&& defined(HAVE_SUPPORTED_CURVES) && !defined(NO_DH) && defined(HAVE_FFDHE) \
|
|
&& !defined(NO_RSA) && defined(BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA)
|
|
/* The negotiated suite must be non-ECC for the missing format to be
|
|
* irrelevant. RFC 9325 / WOLFSSL_HARDEN_TLS disables all TLS_DHE_* suites
|
|
* (NO_TLS_DH); gating on the BUILD_ macro skips the test there rather than
|
|
* failing with MATCH_SUITE_ERROR. */
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "DHE-RSA-AES128-SHA"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "DHE-RSA-AES128-SHA"),
|
|
WOLFSSL_SUCCESS);
|
|
/* Make the client advertise only the compressed point format (1 ==
|
|
* ansiX962_compressed_prime), i.e. omit the uncompressed (0) format. */
|
|
ExpectIntEQ(TLSX_UsePointFormat(&ssl_c->extensions, 1, ssl_c->heap),
|
|
WOLFSSL_SUCCESS);
|
|
/* The handshake must complete: the missing uncompressed format is
|
|
* irrelevant for a non-ECC (DHE) suite. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Sanity: the server really did observe a point-format list without the
|
|
* uncompressed format, yet proceeded. */
|
|
ExpectIntEQ(ssl_s->options.peerNoUncompPF, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* DTLS counterpart of test_tls_fallback_scsv, exercising the DTLS branch of the
|
|
* fallback-SCSV check. DTLS version minors decrease as the version increases
|
|
* (DTLS 1.0=0xff, 1.2=0xfd, 1.3=0xfc), so the downgrade comparison is inverted.
|
|
*
|
|
* The ClientHello offers DTLS 1.2 with TLS_FALLBACK_SCSV present, against a
|
|
* downgrade-capable server whose maximum is DTLS 1.3 - i.e. a genuine
|
|
* downgrade. dtlsStateful is forced on so DoClientHello skips the stateless
|
|
* cookie exchange (HelloVerifyRequest) and reaches the cipher-suite parsing.
|
|
*/
|
|
int test_dtls_fallback_scsv(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* The fallback is to DTLS 1.2, so the server needs that version to downgrade
|
|
* to for the alert to be the one under test. */
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS13) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
const byte clientHello[] = {
|
|
/* DTLS record header: handshake, DTLS 1.2, epoch 0, seq 0, length 56 */
|
|
0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x38,
|
|
/* DTLS handshake header: ClientHello, length 44, msg_seq 0,
|
|
* fragment_offset 0, fragment_length 44 */
|
|
0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x2c,
|
|
/* client version: DTLS 1.2 (lower than the server's DTLS 1.3 max) */
|
|
0xfe, 0xfd,
|
|
/* random: 32 bytes */
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
/* session id length: 0 */
|
|
0x00,
|
|
/* cookie length: 0 */
|
|
0x00,
|
|
/* cipher suites length: 4 */
|
|
0x00, 0x04,
|
|
/* TLS_FALLBACK_SCSV, TLS_RSA_WITH_AES_128_CBC_SHA */
|
|
0x56, 0x00, 0x00, 0x2f,
|
|
/* compression methods: 1 entry, null */
|
|
0x01, 0x00,
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)clientHello, sizeof(clientHello)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfDTLS_server_method), 0);
|
|
/* Skip the stateless cookie exchange so the suites are parsed directly. */
|
|
if (ssl_s != NULL)
|
|
ssl_s->options.dtlsStateful = 1;
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
/* The first record on the wire must be a fatal inappropriate_fallback alert.
|
|
* Check by offset to stay robust against the DTLS epoch/sequence bytes. */
|
|
ExpectIntGE(test_ctx.c_len, 15);
|
|
ExpectIntEQ((byte)test_ctx.c_buff[0], 0x15); /* alert content type */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[1], 0xfe); /* DTLS 1.2 */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[2], 0xfd);
|
|
ExpectIntEQ((byte)test_ctx.c_buff[11], 0x00); /* record length: 2 */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[12], 0x02);
|
|
ExpectIntEQ((byte)test_ctx.c_buff[13], 0x02); /* level: fatal */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[14], 0x56); /* inappropriate_fallback */
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* DTLS negative control mirroring test_tls_fallback_scsv_no_downgrade, guarding
|
|
* the inverted DTLS comparison (maxMinor < pv.minor). A DTLS 1.2 ClientHello
|
|
* carrying TLS_FALLBACK_SCSV against a DTLS 1.2-max server offers the server's
|
|
* highest supported version - not a downgrade - so RFC 7507 requires the
|
|
* handshake to proceed (no inappropriate_fallback). An inverted-operator or
|
|
* off-by-one regression on the DTLS branch would wrongly reject this.
|
|
*/
|
|
int test_dtls_fallback_scsv_no_downgrade(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
/* DTLS 1.2 ClientHello with TLS_FALLBACK_SCSV against a DTLS 1.2 server,
|
|
* i.e. the client offers the server's maximum version - not a downgrade. */
|
|
const byte clientHello[] = {
|
|
/* DTLS record header: handshake, DTLS 1.2, epoch 0, seq 0, length 56 */
|
|
0x16, 0xfe, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x38,
|
|
/* DTLS handshake header: ClientHello, length 44, msg_seq 0,
|
|
* fragment_offset 0, fragment_length 44 */
|
|
0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x2c,
|
|
/* client version: DTLS 1.2 (== server max) */
|
|
0xfe, 0xfd,
|
|
/* random: 32 bytes */
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
/* session id length: 0 */
|
|
0x00,
|
|
/* cookie length: 0 */
|
|
0x00,
|
|
/* cipher suites length: 4 */
|
|
0x00, 0x04,
|
|
/* TLS_FALLBACK_SCSV, TLS_RSA_WITH_AES_128_CBC_SHA */
|
|
0x56, 0x00, 0x00, 0x2f,
|
|
/* compression methods: 1 entry, null */
|
|
0x01, 0x00,
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)clientHello, sizeof(clientHello)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfDTLSv1_2_server_method), 0);
|
|
/* Skip the stateless cookie exchange so the suites are parsed directly. */
|
|
if (ssl_s != NULL)
|
|
ssl_s->options.dtlsStateful = 1;
|
|
/* The handshake must not be rejected for the fallback reason: no
|
|
* VERSION_ERROR and no inappropriate_fallback alert on the wire. Whatever
|
|
* happens afterwards (suite match or not) is not under test here. */
|
|
(void)wolfSSL_accept(ssl_s);
|
|
ExpectIntNE(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
ExpectFalse(test_ctx.c_len >= 15 && (byte)test_ctx.c_buff[0] == 0x15 &&
|
|
(byte)test_ctx.c_buff[14] == 0x56 /* inappropriate_fallback */);
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Negative control for the fallback-SCSV check: a ClientHello that carries
|
|
* TLS_FALLBACK_SCSV but offers the server's highest supported version is NOT a
|
|
* downgrade, so RFC 7507 requires the handshake to proceed (no
|
|
* inappropriate_fallback). Guards against over-aggressive enforcement now that
|
|
* the check is always compiled in.
|
|
*/
|
|
int test_tls_fallback_scsv_no_downgrade(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12)
|
|
/* TLS 1.2 ClientHello with TLS_FALLBACK_SCSV against a TLS 1.2 server, i.e.
|
|
* the client offers the server's maximum version - not a downgrade. */
|
|
const byte clientHello[] = {
|
|
/* record header: handshake, TLS 1.2, length 47 */
|
|
0x16, 0x03, 0x03, 0x00, 0x2f,
|
|
/* handshake header: ClientHello, length 43 */
|
|
0x01, 0x00, 0x00, 0x2b,
|
|
/* client version: TLS 1.2 (== server max) */
|
|
0x03, 0x03,
|
|
/* random: 32 bytes */
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
/* session id length: 0 */
|
|
0x00,
|
|
/* cipher suites length: 4 */
|
|
0x00, 0x04,
|
|
/* TLS_FALLBACK_SCSV, TLS_RSA_WITH_AES_128_CBC_SHA */
|
|
0x56, 0x00, 0x00, 0x2f,
|
|
/* compression methods: 1 entry, null */
|
|
0x01, 0x00,
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)clientHello, sizeof(clientHello)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfTLSv1_2_server_method), 0);
|
|
/* The handshake must not be rejected for the fallback reason: no
|
|
* VERSION_ERROR and no inappropriate_fallback alert on the wire. Whatever
|
|
* happens afterwards (suite match or not) is not under test here. */
|
|
(void)wolfSSL_accept(ssl_s);
|
|
ExpectIntNE(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
ExpectFalse(test_ctx.c_len >= 7 && (byte)test_ctx.c_buff[0] == 0x15 &&
|
|
(byte)test_ctx.c_buff[6] == 0x56 /* inappropriate_fallback */);
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Negative control for runtime max version handling: the fallback-SCSV ceiling
|
|
* must honor a runtime-disabled version, not the compiled method version. A
|
|
* TLS 1.3 capable method (wolfSSLv23_server_method) has SSL_OP_NO_TLSv1_3
|
|
* applied at runtime, so its effective maximum is TLS 1.2. A client that
|
|
* legitimately offers TLS 1.2 with TLS_FALLBACK_SCSV is therefore NOT
|
|
* downgrading and must not be rejected with inappropriate_fallback (RFC 7507).
|
|
* Before the fix the check compared against ssl->ctx->method->version.minor
|
|
* (still TLS 1.3), wrongly aborting.
|
|
*/
|
|
int test_tls_fallback_scsv_no_downgrade_runtime_max(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
/* TLS 1.2 ClientHello with TLS_FALLBACK_SCSV. The server's compiled max is
|
|
* TLS 1.3 but TLS 1.3 is disabled at runtime, so TLS 1.2 == effective max -
|
|
* not a downgrade. */
|
|
const byte clientHello[] = {
|
|
/* record header: handshake, TLS 1.2, length 47 */
|
|
0x16, 0x03, 0x03, 0x00, 0x2f,
|
|
/* handshake header: ClientHello, length 43 */
|
|
0x01, 0x00, 0x00, 0x2b,
|
|
/* client version: TLS 1.2 (== server effective max) */
|
|
0x03, 0x03,
|
|
/* random: 32 bytes */
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
/* session id length: 0 */
|
|
0x00,
|
|
/* cipher suites length: 4 */
|
|
0x00, 0x04,
|
|
/* TLS_FALLBACK_SCSV, TLS_RSA_WITH_AES_128_CBC_SHA */
|
|
0x56, 0x00, 0x00, 0x2f,
|
|
/* compression methods: 1 entry, null */
|
|
0x01, 0x00,
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)clientHello, sizeof(clientHello)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfSSLv23_server_method), 0);
|
|
/* Lower the effective max to TLS 1.2 at runtime; this updates
|
|
* ssl->version.minor but leaves ssl->ctx->method->version.minor at TLS 1.3,
|
|
* which is exactly the condition that triggered the false positive.
|
|
* Confirm the NO_TLSv1_3 bit actually took effect rather than just that the
|
|
* returned mask is nonzero. */
|
|
if (ssl_s != NULL) {
|
|
wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_TLSv1_3);
|
|
ExpectIntNE(wolfSSL_get_options(ssl_s) & WOLFSSL_OP_NO_TLSv1_3, 0);
|
|
}
|
|
/* The handshake must not be rejected for the fallback reason: no
|
|
* VERSION_ERROR and no inappropriate_fallback alert on the wire. Whatever
|
|
* happens afterwards (suite match or not) is not under test here. */
|
|
(void)wolfSSL_accept(ssl_s);
|
|
ExpectIntNE(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
ExpectFalse(test_ctx.c_len >= 7 && (byte)test_ctx.c_buff[0] == 0x15 &&
|
|
(byte)test_ctx.c_buff[6] == 0x56 /* inappropriate_fallback */);
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 5246 Section 7.2.2 and RFC 8446 Section 4.1.3: a server that cannot
|
|
* negotiate any version at or below ClientHello.client_version MUST abort with
|
|
* a fatal protocol_version alert.
|
|
*
|
|
* The dead end comes from runtime restrictions: a downgrade-capable method
|
|
* with a minimum of TLS 1.2 but both TLS 1.3 and TLS 1.2 masked off. Masking
|
|
* TLS 1.3 also keeps the ClientHello on the TLS 1.2 message path, since the
|
|
* TLS 1.3 handler alerts on its own and would hide the defect.
|
|
*
|
|
* Built only without WOLFSSL_EXTRA_ALERTS; with it the SendFatalAlertOnly()
|
|
* fallback emits the same alert and the test would pass either way. See
|
|
* test_tls_version_error_alert_mapping() for that build.
|
|
*/
|
|
int test_tls_no_acceptable_version_alert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_EXTRA_ALERTS)
|
|
const byte clientHello[] = {
|
|
/* record header: handshake, TLS 1.2, length 45 */
|
|
0x16, 0x03, 0x03, 0x00, 0x2d,
|
|
/* handshake header: ClientHello, length 41 */
|
|
0x01, 0x00, 0x00, 0x29,
|
|
/* client version: TLS 1.2 */
|
|
0x03, 0x03,
|
|
/* random: 32 bytes */
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
/* session id length: 0 */
|
|
0x00,
|
|
/* cipher suites length: 2, TLS_RSA_WITH_AES_128_CBC_SHA */
|
|
0x00, 0x02, 0x00, 0x2f,
|
|
/* compression methods: 1 entry, null */
|
|
0x01, 0x00,
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)clientHello, sizeof(clientHello)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfSSLv23_server_method), 0);
|
|
/* Minimum TLS 1.2, but both TLS 1.3 and TLS 1.2 are disabled, so the
|
|
* server has no version left at or below the offered TLS 1.2. */
|
|
ExpectIntEQ(wolfSSL_SetMinVersion(ssl_s, WOLFSSL_TLSV1_2), WOLFSSL_SUCCESS);
|
|
if (ssl_s != NULL) {
|
|
wolfSSL_set_options(ssl_s,
|
|
WOLFSSL_OP_NO_TLSv1_3 | WOLFSSL_OP_NO_TLSv1_2);
|
|
ExpectIntNE(wolfSSL_get_options(ssl_s) & WOLFSSL_OP_NO_TLSv1_2, 0);
|
|
ExpectIntNE(wolfSSL_get_options(ssl_s) & WOLFSSL_OP_NO_TLSv1_3, 0);
|
|
}
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
/* A fatal protocol_version (70) alert must be on the wire. Check by
|
|
* offset rather than comparing the whole record: the version in the alert
|
|
* record header follows whatever the downgrade logic last settled on. */
|
|
ExpectIntGE(test_ctx.c_len, 7);
|
|
ExpectIntEQ((byte)test_ctx.c_buff[0], 0x15); /* alert content type */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[5], 0x02); /* level: fatal */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[6], 0x46); /* protocol_version (70) */
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_EXTRA_ALERTS)
|
|
/* Drive a raw ClientHello offering client_version TLS 1.<clientMinor> at a
|
|
* server built from `method`, with `minVersion` as its minimum version (0 for
|
|
* the method default), and require a fatal protocol_version alert back.
|
|
*
|
|
* `noTls13` masks TLS 1.3 off. A TLS 1.3 capable server routes the
|
|
* ClientHello to the TLS 1.3 handler, which refuses it there and never gets to
|
|
* the version checks in DoClientHello; masking TLS 1.3 keeps the message on
|
|
* the TLS 1.2 path, like test_tls_no_acceptable_version_alert does. */
|
|
static int test_tls_lesser_version_alert_case(byte clientMinor,
|
|
method_provider method, int minVersion, int noTls13)
|
|
{
|
|
EXPECT_DECLS;
|
|
byte clientHello[] = {
|
|
/* record header: handshake, TLS 1.2, length 45. The record version
|
|
* is not what is under test; only client_version below is. */
|
|
0x16, 0x03, 0x03, 0x00, 0x2d,
|
|
/* handshake header: ClientHello, length 41 */
|
|
0x01, 0x00, 0x00, 0x29,
|
|
/* client version: minor patched below */
|
|
0x03, 0x03,
|
|
/* random: 32 bytes */
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
/* session id length: 0 */
|
|
0x00,
|
|
/* cipher suites length: 2, TLS_RSA_WITH_AES_128_CBC_SHA */
|
|
0x00, 0x02, 0x00, 0x2f,
|
|
/* compression methods: 1 entry, null */
|
|
0x01, 0x00,
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
clientHello[10] = clientMinor;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)clientHello, sizeof(clientHello)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, method), 0);
|
|
if (minVersion != 0) {
|
|
ExpectIntEQ(wolfSSL_SetMinVersion(ssl_s, minVersion),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
if (noTls13 && ssl_s != NULL) {
|
|
wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_TLSv1_3);
|
|
ExpectIntNE(wolfSSL_get_options(ssl_s) & WOLFSSL_OP_NO_TLSv1_3, 0);
|
|
}
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
/* A fatal protocol_version (70) alert must be on the wire. Check by
|
|
* offset rather than comparing the whole record: the version in the alert
|
|
* record header follows whatever the server settled on. */
|
|
ExpectIntGE(test_ctx.c_len, 7);
|
|
ExpectIntEQ((byte)test_ctx.c_buff[0], 0x15); /* alert content type */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[5], 0x02); /* level: fatal */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[6], 0x46); /* protocol_version (70) */
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* DoClientHello refuses a client_version it cannot come down to at four
|
|
* points; each must alert with protocol_version (RFC 5246 7.2.2). This covers
|
|
* the two that reject the offered version outright - the server does not
|
|
* downgrade at all, and the offer is below its minimum - while
|
|
* test_tls_no_acceptable_version_alert covers the one where the version mask
|
|
* lowers the server below its own minimum.
|
|
*
|
|
* The remaining one, behind WOLFSSL_OP_NO_SSLv3, is left out on purpose: it is
|
|
* only reached once the downgrade logic has settled on SSLv3, which no build
|
|
* of the test suite negotiates.
|
|
*
|
|
* Built only without WOLFSSL_EXTRA_ALERTS, for the reason given on
|
|
* test_tls_no_acceptable_version_alert.
|
|
*/
|
|
int test_tls_lesser_version_alerts(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_EXTRA_ALERTS)
|
|
/* A method fixed to TLS 1.2 does not downgrade, so a client offering
|
|
* TLS 1.1 has to be refused outright. */
|
|
ExpectIntEQ(test_tls_lesser_version_alert_case(TLSv1_1_MINOR,
|
|
wolfTLSv1_2_server_method, 0, 0), TEST_SUCCESS);
|
|
/* A downgrade-capable server can come down, but not below its configured
|
|
* minimum of TLS 1.2. TLS 1.3 is masked off only where it exists, to
|
|
* keep the ClientHello on the TLS 1.2 message path. */
|
|
#ifdef WOLFSSL_TLS13
|
|
ExpectIntEQ(test_tls_lesser_version_alert_case(TLSv1_1_MINOR,
|
|
wolfSSLv23_server_method, WOLFSSL_TLSV1_2, 1), TEST_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(test_tls_lesser_version_alert_case(TLSv1_1_MINOR,
|
|
wolfSSLv23_server_method, WOLFSSL_TLSV1_2, 0), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Two properties of the alert DoClientHello sends when the version mask walks
|
|
* the server below its own minimum, both invisible to the tests above:
|
|
*
|
|
* - The record carries the TLS 1.2 the client offered, not the TLS 1.1 the
|
|
* mask walk stepped down to before giving up. A peer is entitled to
|
|
* discard a record announcing a version it never proposed, which would
|
|
* leave it unable to tell a version mismatch from a dropped connection -
|
|
* the whole point of alerting here.
|
|
*
|
|
* - Exactly one fatal alert goes out. DoClientHello alerts itself, so the
|
|
* generic handler it returns through must not append a second one.
|
|
*
|
|
* The server is TLS 1.3 capable and only TLS 1.2 is masked off, so the TLS 1.3
|
|
* handler hands the TLS 1.2 ClientHello down to DoClientHello - which is also
|
|
* what puts a second alert within reach. Masking TLS 1.3 as well, the way the
|
|
* tests above do, would keep the message off that path and cover neither.
|
|
*/
|
|
int test_tls_version_mask_alert_record(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
const byte clientHello[] = {
|
|
/* record header: handshake, TLS 1.2, length 45 */
|
|
0x16, 0x03, 0x03, 0x00, 0x2d,
|
|
/* handshake header: ClientHello, length 41 */
|
|
0x01, 0x00, 0x00, 0x29,
|
|
/* client version: TLS 1.2 */
|
|
0x03, 0x03,
|
|
/* random: 32 bytes */
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
/* session id length: 0 */
|
|
0x00,
|
|
/* cipher suites length: 2, TLS_RSA_WITH_AES_128_CBC_SHA */
|
|
0x00, 0x02, 0x00, 0x2f,
|
|
/* compression methods: 1 entry, null */
|
|
0x01, 0x00,
|
|
};
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
|
(const char*)clientHello, sizeof(clientHello)), 0);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, NULL, &ctx_s, NULL, &ssl_s,
|
|
NULL, wolfSSLv23_server_method), 0);
|
|
/* Only TLS 1.2 is masked off. The server's version is still TLS 1.3 when
|
|
* the mask is applied, so nothing is lowered until DoClientHello has
|
|
* downgraded to the offered TLS 1.2 - which is what makes the mask walk
|
|
* run there and step down to TLS 1.1, below the TLS 1.2 minimum.
|
|
*
|
|
* The minimum is pinned rather than left at the build default: without
|
|
* NO_OLD_TLS that default is TLS 1.0, so the walk would settle on TLS 1.1
|
|
* and negotiate instead of failing. */
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(wolfSSL_SetMinVersion(ssl_s, WOLFSSL_TLSV1_2),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_TLSv1_2);
|
|
ExpectIntNE(wolfSSL_get_options(ssl_s) & WOLFSSL_OP_NO_TLSv1_2, 0);
|
|
}
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
/* One fatal alert record and nothing else: 5 byte header plus level and
|
|
* description. Nothing is encrypted this early, so a second alert would
|
|
* show up as another 7 bytes. */
|
|
ExpectIntEQ(test_ctx.c_len, 7);
|
|
ExpectIntEQ((byte)test_ctx.c_buff[0], 0x15); /* alert content type */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[1], 0x03); /* record version: TLS 1.2, */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[2], 0x03); /* not the TLS 1.1 walked to */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[5], 0x02); /* level: fatal */
|
|
ExpectIntEQ((byte)test_ctx.c_buff[6], 0x46); /* protocol_version (70) */
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Companion to test_tls_no_acceptable_version_alert covering the
|
|
* SendFatalAlertOnly() fallback (WOLFSSL_EXTRA_ALERTS builds only): a
|
|
* VERSION_ERROR must map to protocol_version, not handshake_failure.
|
|
*
|
|
* Reaching the mapping takes a rejection that does not alert on its own, and
|
|
* DTLS is where one is left. The TLS version checks all alert first, and
|
|
* SendFatalAlertOnly() bails out once a fatal alert has been sent.
|
|
* DoHelloVerifyRequest() does not alert: a HelloVerifyRequest carrying a
|
|
* non-DTLS version returns VERSION_ERROR straight up to ProcessReply.
|
|
*/
|
|
int test_tls_version_error_alert_mapping(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_EXTRA_ALERTS) && defined(WOLFSSL_DTLS) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
const byte helloVerifyRequest[] = {
|
|
/* record header: handshake, DTLS 1.0, epoch 0, sequence 0, length 15.
|
|
* A handshake record's version is not checked by GetRecordHeader(). */
|
|
0x16, 0xfe, 0xff,
|
|
0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x0f,
|
|
/* handshake header: HelloVerifyRequest, length 3, message_seq 0,
|
|
* fragment offset 0, fragment length 3 */
|
|
0x03, 0x00, 0x00, 0x03,
|
|
0x00, 0x00,
|
|
0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x03,
|
|
/* server version: TLS 1.2, i.e. not a DTLS version at all */
|
|
0x03, 0x03,
|
|
/* cookie length: 0 */
|
|
0x00,
|
|
};
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, NULL, &ssl_c, NULL,
|
|
wolfDTLSv1_2_client_method, NULL), 0);
|
|
|
|
/* First call sends the ClientHello and blocks with nothing to read. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 1,
|
|
(const char*)helloVerifyRequest, sizeof(helloVerifyRequest)), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERSION_ERROR));
|
|
|
|
/* The mapping is what picks the description here. */
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
ExpectIntEQ(h.last_tx.code, 70); /* protocol_version */
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that set_curves_list correctly resolves ECC curve names that fall
|
|
* through the kNistCurves table and reach the wc_ecc_get_curve_idx_from_name
|
|
* fallback path. The kNistCurves lookup uses a case-sensitive XSTRNCMP, so
|
|
* uppercase names like "SECP384R1" do not match the lowercase "secp384r1"
|
|
* entry; they fall through to the wolfCrypt ECC look-up which uses
|
|
* XSTRCASECMP. */
|
|
/* Regression test for the encrypt-then-MAC silent-disable bug.
|
|
*
|
|
* Before the fix, when a client sent a 32-byte session ID in its ClientHello
|
|
* (so the server set ssl->options.resuming = 1) but the server's session
|
|
* cache did not contain that session, DoClientHello would run an
|
|
* encrypt_then_mac decision *before* MatchSuite/SetCipherSpecs had populated
|
|
* ssl->specs.cipher_type. Because cipher_type was zero-initialized
|
|
* (== stream, not block), the ETM block cleared encThenMac to 0, and the
|
|
* post-MatchSuite block could not re-enable it. The connection then
|
|
* silently negotiated MAC-then-encrypt instead of encrypt-then-MAC.
|
|
*
|
|
* This test forces a stale-resumption ClientHello against a server with an
|
|
* empty session cache, using a CBC-mode cipher suite, and asserts that the
|
|
* server still negotiates encrypt-then-MAC. */
|
|
int test_tls12_etm_failed_resumption(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_ENCRYPT_THEN_MAC) && \
|
|
!defined(WOLFSSL_AEAD_ONLY) && !defined(NO_RSA) && !defined(NO_AES) && \
|
|
defined(HAVE_AES_CBC) && !defined(NO_SHA256) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(HAVE_ECC)
|
|
/* TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - a CBC suite, where ETM applies. */
|
|
const char* cbcSuite = "ECDHE-RSA-AES128-SHA256";
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
/* First handshake: establish a session-ID-based session on the client.
|
|
* Disable TLS 1.2 session tickets on both sides so resumption uses the
|
|
* session ID path (not tickets), which is the path the bug lives on. */
|
|
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_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, cbcSuite), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, cbcSuite), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Sanity: the first handshake itself must use ETM. */
|
|
ExpectIntEQ(ssl_s->options.encThenMac, 1);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
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;
|
|
|
|
/* Second handshake against a *fresh* server context (empty cache). The
|
|
* client offers the saved session, so the server's ClientHello parser
|
|
* sets options.resuming = 1, but HandleTlsResumption then fails to find
|
|
* the session and clears resuming. Pre-fix, ETM was silently dropped
|
|
* here. */
|
|
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);
|
|
/* The internal session cache is process-global, so the saved session is
|
|
* still findable via the cache. Disable lookups on this server SSL
|
|
* directly so that HandleTlsResumption hits its "session lookup failed"
|
|
* path - exactly the scenario the bug fix targets. */
|
|
if (ssl_s != NULL)
|
|
ssl_s->options.sessionCacheOff = 1;
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, cbcSuite), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, cbcSuite), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
if (ssl_s != NULL) {
|
|
/* The server should NOT have actually resumed (fresh ctx, empty
|
|
* cache). */
|
|
ExpectIntEQ(ssl_s->options.resuming, 0);
|
|
/* And - the regression check - encrypt-then-MAC must still be
|
|
* active. */
|
|
ExpectIntEQ(ssl_s->options.encThenMac, 1);
|
|
}
|
|
if (ssl_c != NULL)
|
|
ExpectIntEQ(ssl_c->options.encThenMac, 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* RFC 5246 7.4.1.3: a server resuming a TLS 1.2 session ticket MUST reuse the
|
|
* session's cipher suite. The ticket is opaque to the client, so the client
|
|
* cannot rely on the suite being bound inside it and must compare the
|
|
* ServerHello suite against the suite retained in the cached session (F-5811
|
|
* does this for session-ID resumption; it must hold for tickets too). This
|
|
* test establishes a ticket-based session, rewrites the cached session's suite
|
|
* to emulate a server that resumes the ticket under a different suite, and
|
|
* asserts the client aborts the resumption with MATCH_SUITE_ERROR. The same
|
|
* server CTX is reused for the second handshake so its ticket key persists. */
|
|
int test_tls12_resume_ticket_wrong_suite(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_RESUME_SUITE_CHECK) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
!defined(NO_AES) && defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
|
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
|
const char* suite = "ECDHE-RSA-AES128-GCM-SHA256";
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL;
|
|
WOLFSSL *ssl_c3 = NULL, *ssl_s3 = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
struct test_memio_ctx test_ctx2;
|
|
struct test_memio_ctx test_ctx3;
|
|
int ret;
|
|
|
|
/* First handshake: establish a ticket-based TLS 1.2 session. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, suite), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, suite), WOLFSSL_SUCCESS);
|
|
/* Opt the client into TLS 1.2 session tickets so the server issues one. */
|
|
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
/* Must be a ticket session to exercise the ticket path. */
|
|
ExpectIntGT(sess->ticketLen, 0);
|
|
|
|
/* Case 1 - downgrading server: change the cached suite so it no longer
|
|
* matches the suite the server reuses from the ticket, but keep it
|
|
* non-zero so it still counts as a retained suite. The value only feeds
|
|
* the comparison (the real keys come from the ServerHello suite), so
|
|
* flipping it is sufficient and safe. The client must reject the
|
|
* resumption against the same server CTX (ticket key persists). */
|
|
if (sess != NULL)
|
|
sess->cipherSuite = (byte)(sess->cipherSuite ^ 0xFF);
|
|
|
|
XMEMSET(&test_ctx2, 0, sizeof(test_ctx2));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx2, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c2, suite), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s2, suite), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS);
|
|
ret = test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL);
|
|
ExpectIntNE(ret, 0);
|
|
ExpectIntEQ(ssl_c2->error, WC_NO_ERR_TRACE(MATCH_SUITE_ERROR));
|
|
|
|
/* Case 2 - session that retained no suite (cipherSuite0/cipherSuite both
|
|
* zero), as for an EAP-FAST PAC whose keys come from the session-secret
|
|
* callback. There is nothing to compare against, so the check must be
|
|
* skipped and the resumption must still succeed. */
|
|
if (sess != NULL) {
|
|
sess->cipherSuite0 = 0;
|
|
sess->cipherSuite = 0;
|
|
}
|
|
|
|
XMEMSET(&test_ctx3, 0, sizeof(test_ctx3));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx3, &ctx_c, &ctx_s, &ssl_c3, &ssl_s3,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c3, suite), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s3, suite), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c3), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c3, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c3, ssl_s3, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c3), 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c2);
|
|
wolfSSL_free(ssl_s2);
|
|
wolfSSL_free(ssl_c3);
|
|
wolfSSL_free(ssl_s3);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A ticket the server can't honor must fall back to a full handshake (RFC 5077
|
|
* 3.4), even under a different suite than the cached ticket session - the
|
|
* F-5811 suite check must not abort it. The second handshake uses a fresh
|
|
* server CTX (new ticket key -> decline) offering only suite B while the client
|
|
* offers B and the session's suite A. */
|
|
int test_tls12_resume_ticket_decline_fallback(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_SESSION_CACHE) && \
|
|
!defined(NO_RESUME_SUITE_CHECK) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
!defined(NO_AES) && defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
|
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \
|
|
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
|
|
const char* suiteA = "ECDHE-RSA-AES128-GCM-SHA256";
|
|
const char* suiteB = "ECDHE-RSA-AES256-GCM-SHA384";
|
|
const char* suiteBA =
|
|
"ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256";
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL, *ctx_s2 = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
struct test_memio_ctx test_ctx2;
|
|
|
|
/* First handshake: establish a ticket-based TLS 1.2 session on suite A. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, suiteA), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, suiteA), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntGT(sess->ticketLen, 0);
|
|
|
|
/* Second handshake: fresh server CTX (NULL ctx_s2 -> new ticket key) so the
|
|
* ticket is declined and the server does a full handshake on suite B. */
|
|
XMEMSET(&test_ctx2, 0, sizeof(test_ctx2));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx2, &ctx_c, &ctx_s2, &ssl_c2, &ssl_s2,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c2, suiteBA), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s2, suiteB), WOLFSSL_SUCCESS);
|
|
/* Session cache off so the declining server emits an empty session ID and
|
|
* the client takes the graceful full-handshake fallback (set on the SSL as
|
|
* the flag is copied from the CTX at wolfSSL_new() time). */
|
|
if (ssl_s2 != NULL)
|
|
ssl_s2->options.sessionCacheOff = 1;
|
|
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS);
|
|
/* Fallback must succeed (no MATCH_SUITE_ERROR), not resume, and use B. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c2), 0);
|
|
ExpectStrEQ(wolfSSL_get_cipher_name(ssl_c2), suiteB);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c2);
|
|
wolfSSL_free(ssl_s2);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_s2);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A TLS 1.2 client that rejects the server Finished keeps no ticket, and
|
|
* leaves none in the cache for a later connection. */
|
|
int test_tls12_ticket_dropped_on_bad_finished(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_SESSION_CACHE) && \
|
|
!defined(NO_CLIENT_CACHE) && !defined(NO_SESSION_CACHE_REF) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER)
|
|
const byte serverID[] = "tls12-ticket-cache-test";
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL, *ssl_c2 = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* msg = NULL;
|
|
int msgSz = 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_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
|
/* Register the session under a server ID so a later connection can look it
|
|
* up in the client cache. */
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c, serverID, (int)sizeof(serverID), 1),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* ClientHello */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
/* ServerHello .. ServerHelloDone */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
/* ClientKeyExchange, ChangeCipherSpec, Finished */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
/* NewSessionTicket, ChangeCipherSpec, Finished */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
|
|
/* Flip the last byte of the plaintext NewSessionTicket record. */
|
|
ExpectIntEQ(test_memio_get_message(&test_ctx, 1, &msg, &msgSz, 0), 0);
|
|
ExpectIntGT(msgSz, RECORD_HEADER_SZ);
|
|
if (EXPECT_SUCCESS()) {
|
|
int off = (int)(msg - (const char*)test_ctx.c_buff);
|
|
word16 recSz = 0;
|
|
|
|
ExpectIntEQ((byte)msg[0], handshake);
|
|
ExpectIntEQ((byte)msg[RECORD_HEADER_SZ], session_ticket);
|
|
ato16((const byte*)msg + 3, &recSz);
|
|
ExpectIntGT(recSz, 0);
|
|
ExpectIntGE(msgSz, RECORD_HEADER_SZ + (int)recSz);
|
|
if (EXPECT_SUCCESS())
|
|
test_ctx.c_buff[off + RECORD_HEADER_SZ + recSz - 1] ^= 0xFF;
|
|
}
|
|
|
|
/* Client takes the ticket, then rejects the Finished and sends a fatal
|
|
* alert. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERIFY_FINISHED_ERROR));
|
|
/* Not a vacuous pass: the ticket really was processed. */
|
|
ExpectIntEQ(ssl_c->msgsReceived.got_session_ticket, 1);
|
|
/* The unverified ticket is dropped from the session too. */
|
|
ExpectIntEQ(ssl_c->session->ticketLen, 0);
|
|
|
|
/* The failed handshake must leave no ticket for the next connection. */
|
|
ExpectNotNull(ssl_c2 = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c2, serverID, (int)sizeof(serverID), 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl_c2->session);
|
|
ExpectIntEQ(ssl_c2->session->ticketLen, 0);
|
|
|
|
wolfSSL_free(ssl_c2);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A TLS 1.2 client that completes the handshake leaves its ticket session in
|
|
* the cache for a later connection. */
|
|
int test_tls12_ticket_cached_after_finished(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_SESSION_CACHE) && \
|
|
!defined(NO_CLIENT_CACHE) && !defined(NO_SESSION_CACHE_REF) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER)
|
|
const byte serverID[] = "tls12-ticket-cache-ok";
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL, *ssl_c2 = 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_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c, serverID, (int)sizeof(serverID), 1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntGT(ssl_c->session->ticketLen, 0);
|
|
/* Cached under a generated ID, not under the ticket bytes. */
|
|
ExpectIntEQ(ssl_c->session->haveAltSessionID, 1);
|
|
|
|
ExpectNotNull(ssl_c2 = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c2, serverID, (int)sizeof(serverID), 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl_c2->session);
|
|
ExpectIntGT(ssl_c2->session->ticketLen, 0);
|
|
|
|
wolfSSL_free(ssl_c2);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A resumed TLS 1.2 handshake in which the server sends an empty
|
|
* NewSessionTicket leaves the client's cached ticket in place. */
|
|
int test_tls12_empty_ticket_keeps_cached(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_SESSION_CACHE) && \
|
|
!defined(NO_CLIENT_CACHE) && !defined(NO_SESSION_CACHE_REF) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_TICKET_DECRYPT_NO_CREATE)
|
|
const byte serverID[] = "tls12-empty-ticket-test";
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL, *ssl_c3 = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
struct test_memio_ctx test_ctx2;
|
|
|
|
/* Full handshake, so the client caches a session holding a ticket. */
|
|
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_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c, serverID, (int)sizeof(serverID), 1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntGT(ssl_c->session->ticketLen, 0);
|
|
|
|
/* A hint of half the ticket key lifetime or more makes the server send an
|
|
* empty ticket instead of renewing. */
|
|
ExpectIntEQ(wolfSSL_CTX_set_TicketHint(ctx_s, WOLFSSL_TICKET_KEY_LIFETIME),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* Resume against the same server CTX, so the ticket is accepted. */
|
|
XMEMSET(&test_ctx2, 0, sizeof(test_ctx2));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx2, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c2, serverID, (int)sizeof(serverID), 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c2), 1);
|
|
/* The empty ticket cleared this connection's copy. */
|
|
ExpectIntEQ(ssl_c2->msgsReceived.got_session_ticket, 1);
|
|
ExpectIntEQ(ssl_c2->session->ticketLen, 0);
|
|
|
|
/* The cached ticket must not have been overwritten. */
|
|
ExpectNotNull(ssl_c3 = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c3, serverID, (int)sizeof(serverID), 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl_c3->session);
|
|
ExpectIntGT(ssl_c3->session->ticketLen, 0);
|
|
|
|
wolfSSL_free(ssl_c3);
|
|
wolfSSL_free(ssl_c2);
|
|
wolfSSL_free(ssl_s2);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_SESSION_CACHE) && \
|
|
!defined(MICRO_SESSION_CACHE) && defined(SESSIONS_PER_ROW) && \
|
|
SESSIONS_PER_ROW <= 3 && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER)
|
|
/* Full TLS 1.2 handshake on session IDs; row is where the ID hashes. */
|
|
static int test_tls12_cache_row_conn(WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s,
|
|
WOLFSSL_SESSION** sess, word32* row)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int error = 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);
|
|
#ifdef HAVE_SESSION_TICKET
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
*row = HashObject(ssl_s->session->sessionID, ID_LEN, &error) %
|
|
SESSION_ROWS;
|
|
ExpectIntEQ(error, 0);
|
|
}
|
|
if (sess != NULL)
|
|
ExpectNotNull(*sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* With client and server in one process, a later connection whose session ID
|
|
* hashes to the same cache row must not evict the server's entry. */
|
|
int test_tls12_session_cache_shared_row(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_SESSION_CACHE) && \
|
|
!defined(MICRO_SESSION_CACHE) && defined(SESSIONS_PER_ROW) && \
|
|
SESSIONS_PER_ROW <= 3 && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
word32 row1 = 0;
|
|
word32 row2 = 1;
|
|
int skip = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 64 && row1 != row2 && EXPECT_SUCCESS(); i++) {
|
|
wolfSSL_SESSION_free(sess);
|
|
sess = NULL;
|
|
ExpectIntEQ(test_tls12_cache_row_conn(&ctx_c, &ctx_s, &sess, &row1),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_tls12_cache_row_conn(&ctx_c, &ctx_s, NULL, &row2),
|
|
TEST_SUCCESS);
|
|
}
|
|
|
|
if (EXPECT_SUCCESS() && row1 != row2)
|
|
skip = 1;
|
|
|
|
if (!skip) {
|
|
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);
|
|
#ifdef HAVE_SESSION_TICKET
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1);
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
if (skip)
|
|
return TEST_SKIPPED;
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A TLS 1.2 ServerHello must not be able to select a TLS 1.3-only suite. The
|
|
* suite was offered, so DoServerHello() accepted it, and the TLS 1.2 key block
|
|
* of TLS_SHA384_SHA384 (2 * (48 + 48 + 48)) overran key_dig[MAX_PRF_DIG] in
|
|
* DeriveTlsKeys(). Resume a TLS 1.2 session, rewrite the suite in the server's
|
|
* resuming ServerHello and expect UNSUPPORTED_SUITE before any key derivation. */
|
|
int test_tls12_server_hello_tls13_suite(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_TLS13) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
!defined(NO_AES) && defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
|
!defined(WOLFSSL_NO_STRICT_CIPHER_SUITE) && \
|
|
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256)
|
|
const char* suite12 = "ECDHE-RSA-AES128-GCM-SHA256";
|
|
const char* suites_c =
|
|
#if defined(HAVE_NULL_CIPHER) && defined(BUILD_TLS_SHA384_SHA384)
|
|
"TLS13-SHA384-SHA384:"
|
|
#endif
|
|
"TLS13-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256";
|
|
const byte tls13Suites[][SUITE_LEN] = {
|
|
#if defined(HAVE_NULL_CIPHER) && defined(BUILD_TLS_SHA384_SHA384)
|
|
{ ECC_BYTE, TLS_SHA384_SHA384 },
|
|
#endif
|
|
{ TLS13_BYTE, TLS_AES_128_GCM_SHA256 },
|
|
};
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
word32 i;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, suites_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, suite12), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
|
|
for (i = 0; i < sizeof(tls13Suites) / sizeof(tls13Suites[0]); i++) {
|
|
int sidOff = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + VERSION_SZ +
|
|
RAN_LEN;
|
|
int suiteOff = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
|
|
&ssl_s, wolfSSLv23_client_method,
|
|
wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, suites_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, suite12), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
ExpectIntGT(test_ctx.c_len, sidOff + ENUM_LEN);
|
|
ExpectIntEQ(test_ctx.c_buff[0], handshake);
|
|
ExpectIntEQ(test_ctx.c_buff[RECORD_HEADER_SZ], server_hello);
|
|
if (EXPECT_SUCCESS()) {
|
|
suiteOff = sidOff + ENUM_LEN + test_ctx.c_buff[sidOff];
|
|
ExpectIntGT(test_ctx.c_len, suiteOff + SUITE_LEN);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(test_ctx.c_buff[suiteOff], ECC_BYTE);
|
|
ExpectIntEQ(test_ctx.c_buff[suiteOff + 1],
|
|
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256);
|
|
test_ctx.c_buff[suiteOff] = tls13Suites[i][0];
|
|
test_ctx.c_buff[suiteOff + 1] = tls13Suites[i][1];
|
|
}
|
|
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(UNSUPPORTED_SUITE));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
}
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_TLS13) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
!defined(NO_AES) && defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
|
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256)
|
|
/* Count occurrences of suite in the cipher_suites list of the ClientHello in
|
|
* buf, or -1 if the message is not a parseable ClientHello. */
|
|
static int test_client_hello_count_suite(const byte* buf, int len,
|
|
const byte* suite)
|
|
{
|
|
int idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + VERSION_SZ + RAN_LEN;
|
|
int suiteSz;
|
|
int found = 0;
|
|
|
|
if (len < idx + ENUM_LEN || buf[0] != handshake ||
|
|
buf[RECORD_HEADER_SZ] != client_hello) {
|
|
return -1;
|
|
}
|
|
idx += ENUM_LEN + buf[idx];
|
|
if (len < idx + OPAQUE16_LEN)
|
|
return -1;
|
|
suiteSz = (buf[idx] << 8) | buf[idx + 1];
|
|
idx += OPAQUE16_LEN;
|
|
if (suiteSz <= 0 || len < idx + suiteSz)
|
|
return -1;
|
|
|
|
for (; suiteSz >= SUITE_LEN; suiteSz -= SUITE_LEN, idx += SUITE_LEN) {
|
|
if (buf[idx] == suite[0] && buf[idx + 1] == suite[1])
|
|
found++;
|
|
}
|
|
return found;
|
|
}
|
|
#endif
|
|
|
|
/* A ClientHello pinned below TLS 1.3 by wolfSSL_set_session() must not offer
|
|
* TLS 1.3-only suites, which the server can only answer with a suite the
|
|
* negotiated version cannot use. */
|
|
int test_tls12_client_hello_no_tls13_suites(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_TLS13) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(NO_RSA) && defined(HAVE_ECC) && \
|
|
!defined(NO_AES) && defined(HAVE_AESGCM) && !defined(NO_SHA256) && \
|
|
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \
|
|
defined(BUILD_TLS_AES_128_GCM_SHA256)
|
|
const char* suite12 = "ECDHE-RSA-AES128-GCM-SHA256";
|
|
const char* suites_c =
|
|
#if defined(HAVE_NULL_CIPHER) && defined(BUILD_TLS_SHA384_SHA384)
|
|
"TLS13-SHA384-SHA384:"
|
|
#endif
|
|
"TLS13-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256";
|
|
const byte suite13[SUITE_LEN] = { TLS13_BYTE, TLS_AES_128_GCM_SHA256 };
|
|
#if defined(HAVE_NULL_CIPHER) && defined(BUILD_TLS_SHA384_SHA384)
|
|
const byte suiteNull[SUITE_LEN] = { ECC_BYTE, TLS_SHA384_SHA384 };
|
|
#endif
|
|
const byte suiteEcc[SUITE_LEN] =
|
|
{ ECC_BYTE, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 };
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, suites_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, suite12), WOLFSSL_SUCCESS);
|
|
/* Not pinned: the TLS 1.3 suites are offered. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_client_hello_count_suite(test_ctx.s_buff, test_ctx.s_len,
|
|
suite13), 1);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, suites_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, suite12), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_client_hello_count_suite(test_ctx.s_buff, test_ctx.s_len,
|
|
suite13), 0);
|
|
#if defined(HAVE_NULL_CIPHER) && defined(BUILD_TLS_SHA384_SHA384)
|
|
ExpectIntEQ(test_client_hello_count_suite(test_ctx.s_buff, test_ctx.s_len,
|
|
suiteNull), 0);
|
|
#endif
|
|
ExpectIntEQ(test_client_hello_count_suite(test_ctx.s_buff, test_ctx.s_len,
|
|
suiteEcc), 1);
|
|
/* The resumption itself must still work. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* wolfSSL_set_session() must reject a TLS 1.2 session when minDowngrade is
|
|
* set to TLS 1.3. */
|
|
int test_tls_set_session_min_downgrade(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_SESSION_TICKET)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLS_client_method, wolfTLS_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_SetMinVersion(ssl_c, WOLFSSL_TLSV1_3),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_FAILURE);
|
|
if (ssl_c != NULL)
|
|
ExpectIntEQ(ssl_c->options.resuming, 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13)) && \
|
|
defined(HAVE_SNI) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(NO_SESSION_CACHE)
|
|
/* Accept-all SNI callback. */
|
|
static int accept_any_sni_cb(WOLFSSL* ssl, int* ret, void* arg)
|
|
{
|
|
(void)ssl; (void)ret; (void)arg;
|
|
return 0; /* accept */
|
|
}
|
|
#endif
|
|
|
|
/* TLS resumption must proceed with full handshake to establish new session if
|
|
* SNI/ALPN does not match previously established session. */
|
|
int test_tls12_session_id_resumption_sni_mismatch(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SNI) && \
|
|
defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* sniA = "public.example";
|
|
const char* sniB = "admin.example";
|
|
|
|
/* Step 1: full TLS 1.2 handshake under SNI=public.example, with the
|
|
* session ticket path disabled so resumption can only happen via the
|
|
* server's session-ID cache. The server-side SNI callback ensures
|
|
* ssl->extensions retains the client's SNI in builds that don't
|
|
* compile in WOLFSSL_ALWAYS_KEEP_SNI. */
|
|
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);
|
|
wolfSSL_CTX_set_servername_callback(ctx_s, accept_any_sni_cb);
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME,
|
|
sniA, (word16)XSTRLEN(sniA)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Sanity: the first handshake was not a resumption. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Step 2: new SSL objects on the SAME WOLFSSL_CTX (so the server's
|
|
* session cache still holds the entry from step 1). The client offers
|
|
* the saved session but advertises a *different* SNI. The server's
|
|
* cache lookup will match by session ID, but per RFC 6066 Section 3 the
|
|
* server MUST NOT resume because the SNI differs from the original. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME,
|
|
sniB, (word16)XSTRLEN(sniB)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Post-fix expected behavior: server falls back to a full handshake
|
|
* because the SNI in the ClientHello does not match the SNI bound to
|
|
* the cached session. Pre-fix, the server silently resumes - which is
|
|
* the bug. Both sides should report no resumption. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* TLS 1.3 PSK resumption must fall back to a full handshake if the SNI in
|
|
* the resumed ClientHello does not match the SNI bound to the original
|
|
* session (RFC 6066 Section 3 / RFC 8446 Section 4.6.1). */
|
|
int test_tls13_session_resumption_sni_mismatch(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_SNI) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(NO_SESSION_CACHE)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* sniA = "public.example";
|
|
const char* sniB = "admin.example";
|
|
byte readBuf[16];
|
|
|
|
/* Step 1: full TLS 1.3 handshake under SNI=public.example to obtain a
|
|
* session ticket. The server-side SNI callback ensures ssl->extensions
|
|
* retains the client's SNI in builds that don't compile in
|
|
* WOLFSSL_ALWAYS_KEEP_SNI. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
wolfSSL_CTX_set_servername_callback(ctx_s, accept_any_sni_cb);
|
|
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME,
|
|
sniA, (word16)XSTRLEN(sniA)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Sanity: the first handshake was not a resumption. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
/* Drive the post-handshake NewSessionTicket through to the client so
|
|
* the saved session is a real resumption ticket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Step 2: new SSL objects on the SAME WOLFSSL_CTX (so the server's
|
|
* ticket key still matches). The client offers the saved session but
|
|
* advertises a *different* SNI. The server MUST NOT resume because the
|
|
* SNI differs from the original. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME,
|
|
sniB, (word16)XSTRLEN(sniB)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Desired behavior: server falls back to a full handshake because the
|
|
* SNI in the ClientHello does not match the SNI bound to the cached
|
|
* ticket. Both sides should report no resumption. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Regression test for the post-ALPN_Select PSK-head check.
|
|
* When ALPN_Select runs before CheckPreSharedKeys (so the per-PSK
|
|
* binding check has the negotiated ALPN available), TLSX_SetALPN
|
|
* prepends a new ALPN entry to ssl->extensions, displacing the PSK
|
|
* extension from the head of the list. The "PSK was last in
|
|
* ClientHello" check therefore must run right after TLSX_Parse,
|
|
* not inside CheckPreSharedKeys. This test exercises that path
|
|
* (TLS 1.3 PSK resumption with ALPN, no SNI callback -- the grpc
|
|
* server scenario). */
|
|
int test_tls13_resumption_with_alpn(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_SNI) && defined(HAVE_ALPN) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(NO_SESSION_CACHE)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char* sni = "foo.test.google.fr";
|
|
const char alpn[] = "h2";
|
|
byte readBuf[16];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME,
|
|
sni, (word16)XSTRLEN(sni)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpn, (word32)XSTRLEN(alpn),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpn, (word32)XSTRLEN(alpn),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME,
|
|
sni, (word16)XSTRLEN(sni)), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpn, (word32)XSTRLEN(alpn),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpn, (word32)XSTRLEN(alpn),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* TLS 1.2 stateful (session-ID) resumption must fall back to a full
|
|
* handshake if the ALPN protocol negotiated for the resumed connection
|
|
* does not match the ALPN bound to the original session. Mirrors
|
|
* test_tls12_session_id_resumption_sni_mismatch but varies ALPN instead
|
|
* of SNI. */
|
|
int test_tls12_session_id_resumption_alpn_mismatch(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_ALPN) && \
|
|
defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char alpnA[] = "h2";
|
|
const char alpnB[] = "http/1.1";
|
|
|
|
/* Step 1: full TLS 1.2 handshake negotiating ALPN=h2, with the
|
|
* session ticket path disabled so resumption can only happen via the
|
|
* server's session-ID cache. The negotiated ALPN is retained on
|
|
* ssl->extensions by ALPN_Select, so SetupSession binds its hash to
|
|
* the cached session. */
|
|
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_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnA, (word32)XSTRLEN(alpnA),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnA, (word32)XSTRLEN(alpnA),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Sanity: the first handshake was not a resumption. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Step 2: new SSL objects on the SAME WOLFSSL_CTX (so the server's
|
|
* session cache still holds the entry from step 1). The client offers
|
|
* the saved session but both sides now advertise a *different* ALPN
|
|
* (http/1.1), so the handshake negotiates http/1.1. The server's cache
|
|
* lookup matches by session ID, but the server MUST NOT resume because
|
|
* the negotiated ALPN differs from the one bound to the original
|
|
* session. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnB, (word32)XSTRLEN(alpnB),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnB, (word32)XSTRLEN(alpnB),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Expected behavior: server falls back to a full handshake because the
|
|
* negotiated ALPN does not match the ALPN bound to the cached session.
|
|
* Both sides should report no resumption. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* TLS 1.3 PSK resumption must fall back to a full handshake if the ALPN
|
|
* protocol negotiated for the resumed connection does not match the ALPN
|
|
* bound to the original session. Mirrors
|
|
* test_tls13_session_resumption_sni_mismatch but varies ALPN instead of
|
|
* SNI. */
|
|
int test_tls13_session_resumption_alpn_mismatch(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(HAVE_ALPN) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(NO_SESSION_CACHE)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
WOLFSSL_SESSION *sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
const char alpnA[] = "h2";
|
|
const char alpnB[] = "http/1.1";
|
|
byte readBuf[16];
|
|
|
|
/* Step 1: full TLS 1.3 handshake negotiating ALPN=h2 to obtain a
|
|
* session ticket. The negotiated ALPN is retained on ssl->extensions
|
|
* by ALPN_Select and bound to the ticket. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnA, (word32)XSTRLEN(alpnA),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnA, (word32)XSTRLEN(alpnA),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Sanity: the first handshake was not a resumption. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
/* Drive the post-handshake NewSessionTicket through to the client so
|
|
* the saved session is a real resumption ticket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Step 2: new SSL objects on the SAME WOLFSSL_CTX (so the server's
|
|
* ticket key still matches). The client offers the saved session but
|
|
* both sides now advertise a *different* ALPN (http/1.1). The server
|
|
* MUST NOT resume because the negotiated ALPN differs from the one
|
|
* bound to the original ticket. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
|
|
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
|
|
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
|
|
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
|
|
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnB, (word32)XSTRLEN(alpnB),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnB, (word32)XSTRLEN(alpnB),
|
|
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Expected behavior: server falls back to a full handshake because the
|
|
* negotiated ALPN does not match the ALPN bound to the cached ticket.
|
|
* Both sides should report no resumption. */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls_set_curves_list_ecc_fallback(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECC) && \
|
|
(defined(OPENSSL_EXTRA) || defined(HAVE_CURL)) && \
|
|
!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) && \
|
|
(defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \
|
|
ECC_MIN_KEY_SZ <= 384
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
/* "SECP384R1" (uppercase) is NOT in kNistCurves (case-sensitive table),
|
|
* so set_curves_list must use the wc_ecc_get_curve_idx_from_name fallback.
|
|
*/
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
|
|
/* CTX-level: set single curve via its wolfCrypt name (uppercase) */
|
|
ExpectIntEQ(wolfSSL_CTX_set1_curves_list(ctx, "SECP384R1"),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* Verify the correct curve was stored, not ecc_sets[0] */
|
|
ExpectIntEQ(ctx->numGroups, 1);
|
|
ExpectIntEQ(ctx->group[0], WOLFSSL_ECC_SECP384R1);
|
|
|
|
/* SSL-level: same check via wolfSSL_set1_curves_list */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_set1_curves_list(ssl, "SECP384R1"), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->numGroups, 1);
|
|
ExpectIntEQ(ssl->group[0], WOLFSSL_ECC_SECP384R1);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif /* NO_WOLFSSL_CLIENT */
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
static int test_tls12_find_client_finished(const struct test_memio_ctx* test_ctx,
|
|
int* finishedMsgPos, int* finishedOffInMsg, int* finishedLen)
|
|
{
|
|
int i;
|
|
const char* msg = NULL;
|
|
int msgSz = 0;
|
|
int ccsPos = -1;
|
|
|
|
*finishedMsgPos = -1;
|
|
*finishedOffInMsg = -1;
|
|
*finishedLen = 0;
|
|
|
|
for (i = 0; i < test_ctx->s_msg_count; i++) {
|
|
if (test_memio_get_message(test_ctx, 0, &msg, &msgSz, i) != 0 ||
|
|
msgSz < RECORD_HEADER_SZ) {
|
|
return -1;
|
|
}
|
|
|
|
if ((byte)msg[0] == change_cipher_spec) {
|
|
ccsPos = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ccsPos >= 0 &&
|
|
test_memio_get_message(test_ctx, 0, &msg, &msgSz, ccsPos + 1) == 0 &&
|
|
msgSz >= RECORD_HEADER_SZ && (byte)msg[0] == handshake) {
|
|
*finishedMsgPos = ccsPos + 1;
|
|
*finishedOffInMsg = 0;
|
|
*finishedLen = msgSz;
|
|
return 0;
|
|
}
|
|
|
|
if (test_ctx->s_msg_count == 1) {
|
|
int off = 0;
|
|
|
|
while (off + RECORD_HEADER_SZ <= test_ctx->s_len) {
|
|
word16 recLen;
|
|
int totalLen;
|
|
|
|
ato16(test_ctx->s_buff + off + 3, &recLen);
|
|
totalLen = RECORD_HEADER_SZ + recLen;
|
|
if (off + totalLen > test_ctx->s_len) {
|
|
return -1;
|
|
}
|
|
|
|
if (test_ctx->s_buff[off] == change_cipher_spec) {
|
|
int nextOff = off + totalLen;
|
|
|
|
if (nextOff + RECORD_HEADER_SZ > test_ctx->s_len ||
|
|
test_ctx->s_buff[nextOff] != handshake) {
|
|
return -1;
|
|
}
|
|
|
|
ato16(test_ctx->s_buff + nextOff + 3, &recLen);
|
|
totalLen = RECORD_HEADER_SZ + recLen;
|
|
if (nextOff + totalLen > test_ctx->s_len) {
|
|
return -1;
|
|
}
|
|
|
|
*finishedMsgPos = 0;
|
|
*finishedOffInMsg = nextOff;
|
|
*finishedLen = totalLen;
|
|
return 0;
|
|
}
|
|
|
|
off += totalLen;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/* Test that a corrupted TLS 1.2 Finished verify_data is properly rejected
|
|
* with VERIFY_FINISHED_ERROR. We let the client queue its second flight,
|
|
* remove the Finished record from the memio queue, allow the server to
|
|
* process up through CCS, then inject a corrupted Finished record. */
|
|
int test_tls12_corrupted_finished(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char finishedMsg[1024];
|
|
int finishedSz = (int)sizeof(finishedMsg);
|
|
int finishedMsgPos = -1;
|
|
int finishedOffInMsg = -1;
|
|
int finishedLen = 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);
|
|
|
|
/* Step 1: Client sends ClientHello */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 2: Server sends ServerHello..ServerHelloDone */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Step 3: Client processes server flight and queues its second flight. */
|
|
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(test_tls12_find_client_finished(&test_ctx, &finishedMsgPos,
|
|
&finishedOffInMsg, &finishedLen), 0);
|
|
ExpectIntGT(finishedLen, 0);
|
|
|
|
if (finishedOffInMsg == 0) {
|
|
ExpectIntEQ(test_memio_copy_message(&test_ctx, 0, finishedMsg,
|
|
&finishedSz, finishedMsgPos), 0);
|
|
ExpectIntEQ(test_memio_drop_message(&test_ctx, 0, finishedMsgPos), 0);
|
|
}
|
|
else {
|
|
ExpectIntGE(finishedSz, finishedLen);
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(finishedMsg, test_ctx.s_buff + finishedOffInMsg, finishedLen);
|
|
}
|
|
finishedSz = finishedLen;
|
|
ExpectIntEQ(test_memio_modify_message_len(&test_ctx, 0,
|
|
finishedMsgPos, finishedOffInMsg), 0);
|
|
}
|
|
}
|
|
|
|
/* Step 4: Server processes up through CCS but blocks waiting for Finished. */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(ssl_s->msgsReceived.got_change_cipher, 1);
|
|
ExpectNotNull(ssl_s->hsHashes);
|
|
XMEMSET(&ssl_s->hsHashes->verifyHashes, 0xA5,
|
|
sizeof(ssl_s->hsHashes->verifyHashes));
|
|
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, finishedMsg,
|
|
finishedSz), 0);
|
|
}
|
|
|
|
/* Step 5: Server processes corrupted Finished and must reject it. */
|
|
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(VERIFY_FINISHED_ERROR));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_wolfSSL_alert_type_string(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_TLS) && defined(OPENSSL_EXTRA)
|
|
ExpectStrEQ(wolfSSL_alert_type_string(alert_warning), "W");
|
|
ExpectStrEQ(wolfSSL_alert_type_string(alert_fatal), "F");
|
|
ExpectStrEQ(wolfSSL_alert_type_string(0), "U");
|
|
ExpectStrEQ(wolfSSL_alert_type_string(-1), "U");
|
|
ExpectStrEQ(wolfSSL_alert_type_string(99), "U");
|
|
/* packed info-callback value: (level << 8) | code */
|
|
ExpectStrEQ(wolfSSL_alert_type_string((alert_warning << 8) | close_notify), "W");
|
|
ExpectStrEQ(wolfSSL_alert_type_string((alert_fatal << 8) | handshake_failure), "F");
|
|
ExpectStrEQ(wolfSSL_alert_type_string_long((alert_fatal << 8) | handshake_failure), "handshake_failure");
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_wolfSSL_get_shared_ciphers(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
char buf[32];
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
ExpectNull(wolfSSL_get_shared_ciphers(NULL, buf, sizeof(buf)));
|
|
ExpectNull(wolfSSL_get_shared_ciphers(ssl, NULL, sizeof(buf)));
|
|
ExpectNull(wolfSSL_get_shared_ciphers(ssl, buf, 0));
|
|
#ifndef NO_ERROR_STRINGS
|
|
ExpectPtrEq(wolfSSL_get_shared_ciphers(ssl, buf, sizeof(buf)), buf);
|
|
#else
|
|
ExpectNull(wolfSSL_get_shared_ciphers(ssl, buf, sizeof(buf)));
|
|
#endif
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test the TLS 1.2 peerAuthGood fail-safe checks directly on both sides.
|
|
* The client branch sets NO_PEER_VERIFY; the server branch returns a generic
|
|
* fatal error from TICKET_SENT before sending its Finished. */
|
|
int test_tls12_peerauth_failsafe(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX *ctx_c = NULL;
|
|
WOLFSSL_CTX *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL;
|
|
WOLFSSL *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int ret;
|
|
|
|
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);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->options.connectState = FIRST_REPLY_SECOND;
|
|
ssl_c->options.peerAuthGood = 0;
|
|
ssl_c->options.sendVerify = 0;
|
|
ret = wolfSSL_connect(ssl_c);
|
|
ExpectIntEQ(ret, WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, ret),
|
|
WC_NO_ERR_TRACE(NO_PEER_VERIFY));
|
|
ExpectIntEQ(ssl_c->options.connectState, FIRST_REPLY_SECOND);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ctx_c = NULL;
|
|
ctx_s = NULL;
|
|
ssl_c = NULL;
|
|
ssl_s = NULL;
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_s->options.acceptState = TICKET_SENT;
|
|
ssl_s->options.peerAuthGood = 0;
|
|
ret = wolfSSL_accept(ssl_s);
|
|
ExpectIntEQ(ret, WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(ssl_s->options.acceptState, TICKET_SENT);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* TLS 1.2 mutual auth: an ECDHE-ECDSA server (ECDSA certificate) accepting an
|
|
* RSA client certificate. */
|
|
int test_tls12_ecdhe_ecdsa_rsa_client_cert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12) \
|
|
&& defined(HAVE_ECC) && !defined(NO_RSA) && !defined(NO_SHA256) \
|
|
&& defined(HAVE_AESGCM) && defined(KEEP_PEER_CERT) \
|
|
&& !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \
|
|
&& !defined(WOLFSSL_NO_CLIENT_AUTH) \
|
|
&& !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_X509* peer = NULL;
|
|
const char* cipher = "ECDHE-ECDSA-AES128-GCM-SHA256";
|
|
|
|
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);
|
|
|
|
/* Server: ECDSA certificate (=> ECDHE-ECDSA suite), require client
|
|
* authentication, and trust the (self-signed) RSA client certificate. */
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, cipher), WOLFSSL_SUCCESS);
|
|
|
|
/* Client: RSA certificate/key, and trust the ECC CA that signed the
|
|
* server's ECDSA certificate. */
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caEccCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, cipher), WOLFSSL_SUCCESS);
|
|
|
|
/* Mutual authentication completes and the server obtains the client's
|
|
* RSA certificate even though the negotiated suite is ECDHE-ECDSA. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectStrEQ(wolfSSL_get_cipher_name(ssl_c), cipher);
|
|
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s));
|
|
wolfSSL_X509_free(peer);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* TLS 1.2 mutual auth: an ECDHE-RSA server (RSA certificate) accepting an
|
|
* ECDSA client certificate. */
|
|
int test_tls12_ecdhe_rsa_ecdsa_client_cert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12) \
|
|
&& defined(HAVE_ECC) && !defined(NO_RSA) && !defined(NO_SHA256) \
|
|
&& defined(HAVE_AESGCM) && defined(KEEP_PEER_CERT) \
|
|
&& !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \
|
|
&& !defined(WOLFSSL_NO_CLIENT_AUTH) \
|
|
&& !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_X509* peer = NULL;
|
|
const char* cipher = "ECDHE-RSA-AES128-GCM-SHA256";
|
|
|
|
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);
|
|
|
|
/* Server: default RSA certificate (=> ECDHE-RSA), require client
|
|
* authentication, and trust the (self-signed) ECDSA client certificate. */
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliEccCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, cipher), WOLFSSL_SUCCESS);
|
|
|
|
/* Client: ECDSA certificate/key. The default client CTX already trusts
|
|
* the RSA CA that signed the server's certificate. */
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliEccCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliEccKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, cipher), WOLFSSL_SUCCESS);
|
|
|
|
/* Mutual authentication completes and the server obtains the client's
|
|
* ECDSA certificate even though the negotiated suite is ECDHE-RSA. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectStrEQ(wolfSSL_get_cipher_name(ssl_c), cipher);
|
|
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s));
|
|
wolfSSL_X509_free(peer);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_wolfSSL_alert_desc_string(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_TLS) && defined(OPENSSL_EXTRA)
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(close_notify), "CN");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(unexpected_message), "UM");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(bad_record_mac), "BM");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(record_overflow), "RO");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(decompression_failure), "DF");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(handshake_failure), "HF");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(no_certificate), "NC");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(bad_certificate), "BC");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(unsupported_certificate), "UC");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(certificate_revoked), "CR");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(certificate_expired), "CE");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(certificate_unknown), "CU");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(illegal_parameter), "IP");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(unknown_ca), "CA");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(access_denied), "AD");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(decode_error), "DE");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(decrypt_error), "DC");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(wolfssl_alert_protocol_version), "PV");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(insufficient_security), "IS");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(internal_error), "IE");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(inappropriate_fallback), "IF");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(user_canceled), "US");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(no_renegotiation), "NR");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(missing_extension), "ME");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(unsupported_extension), "UE");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(unrecognized_name), "UN");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(bad_certificate_status_response), "BR");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(unknown_psk_identity), "UP");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(certificate_required), "CQ");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(no_application_protocol), "AP");
|
|
/* Unknown alert description returns "UK" */
|
|
ExpectStrEQ(wolfSSL_alert_desc_string(255), "UK");
|
|
/* packed info-callback value: (level << 8) | code */
|
|
ExpectStrEQ(wolfSSL_alert_desc_string((alert_fatal << 8) | close_notify), "CN");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string((alert_warning << 8) | handshake_failure), "HF");
|
|
ExpectStrEQ(wolfSSL_alert_desc_string_long((alert_fatal << 8) | close_notify), "close_notify");
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
|
/* Cipher-name substrings that need extra setup (PSK callback, ECDSA cert,
|
|
* SRP, etc.) which the default test_memio_setup() doesn't provide. */
|
|
static int record_size_skip_cipher(const char *name)
|
|
{
|
|
/* "ECDH-" matches static-ECDH ciphers ("ECDH-RSA-*", "ECDH-ECDSA-*")
|
|
* and not ECDHE-* because of the trailing '-'. RENEGOTIATION-INFO is the
|
|
* TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling value, not a real cipher. */
|
|
static const char* const deny[] = {
|
|
"PSK", "SRP", "ANON", "NULL", "ECDSA", "ECDH-", "SM",
|
|
"RENEGOTIATION-INFO"
|
|
};
|
|
size_t i;
|
|
for (i = 0; i < XELEM_CNT(deny); i++) {
|
|
if (XSTRSTR(name, deny[i]) != NULL)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Cross-check wolfssl_local_GetRecordSize() against BuildMessage(sizeOnly=1)
|
|
* with the cache cold, then call it a second time and assert both calls
|
|
* return the same size - that exercises the cached path for AEAD ciphers
|
|
* without duplicating the BuildMessage arithmetic. */
|
|
static int record_size_check_ssl(WOLFSSL *ssl)
|
|
{
|
|
EXPECT_DECLS;
|
|
static const int payloads[] = { 1, 16, 256, 1300, 4096 };
|
|
size_t k;
|
|
|
|
for (k = 0; k < XELEM_CNT(payloads); k++) {
|
|
int payloadSz = payloads[k];
|
|
int expectedSz = BuildMessage(ssl, NULL, 0, NULL, payloadSz,
|
|
application_data, 0, 1, 0, CUR_ORDER);
|
|
int firstSz, secondSz;
|
|
|
|
ssl->recordSzOverhead = 0;
|
|
firstSz = wolfssl_local_GetRecordSize(ssl, payloadSz, 1);
|
|
secondSz = wolfssl_local_GetRecordSize(ssl, payloadSz, 1);
|
|
ExpectIntEQ(firstSz, expectedSz);
|
|
ExpectIntEQ(secondSz, expectedSz);
|
|
}
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Returns 1 if `suite` is selectable for the given client/server method
|
|
* pair, 0 otherwise. wolfSSL rejects some ciphers for DTLS at
|
|
* set_cipher_list time (e.g. RFC 7465 forbids RC4 in DTLS); skip those
|
|
* silently rather than failing the cross-check. */
|
|
static int record_size_cipher_selectable(method_provider client_method,
|
|
method_provider server_method, const char *suite)
|
|
{
|
|
WOLFSSL_CTX *ctx_c = wolfSSL_CTX_new(client_method());
|
|
WOLFSSL_CTX *ctx_s = wolfSSL_CTX_new(server_method());
|
|
int ok = (ctx_c != NULL && ctx_s != NULL &&
|
|
wolfSSL_CTX_set_cipher_list(ctx_c, suite) == WOLFSSL_SUCCESS &&
|
|
wolfSSL_CTX_set_cipher_list(ctx_s, suite) == WOLFSSL_SUCCESS);
|
|
if (ctx_c) wolfSSL_CTX_free(ctx_c);
|
|
if (ctx_s) wolfSSL_CTX_free(ctx_s);
|
|
return ok;
|
|
}
|
|
|
|
/* Run the cross-check on a memio pair using the given (de)multiplexing
|
|
* methods and cipher suite. Optionally enable DTLS-CID with peer CIDs of
|
|
* different sizes so the test covers CID-extended record framing. */
|
|
static int record_size_run_pair(method_provider client_method,
|
|
method_provider server_method, const char *suite, int useCid)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
(void)useCid;
|
|
if (!record_size_cipher_selectable(client_method, server_method, suite))
|
|
return TEST_SUCCESS; /* not valid for this protocol -- skip */
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
test_ctx.c_ciphers = test_ctx.s_ciphers = suite;
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
client_method, server_method), 0);
|
|
#ifdef WOLFSSL_DTLS_CID
|
|
if (useCid) {
|
|
/* Different sizes on each side to exercise asymmetric framing. */
|
|
static unsigned char client_cid[] = { 1, 2, 3, 4, 5, 6 };
|
|
static unsigned char server_cid[] = { 7, 8, 9 };
|
|
ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_c), 1);
|
|
ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_c, server_cid,
|
|
sizeof(server_cid)), 1);
|
|
ExpectIntEQ(wolfSSL_dtls_cid_use(ssl_s), 1);
|
|
ExpectIntEQ(wolfSSL_dtls_cid_set(ssl_s, client_cid,
|
|
sizeof(client_cid)), 1);
|
|
}
|
|
#endif
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 30, NULL), 0);
|
|
ExpectIntEQ(record_size_check_ssl(ssl_c), TEST_SUCCESS);
|
|
ExpectIntEQ(record_size_check_ssl(ssl_s), TEST_SUCCESS);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES */
|
|
|
|
int test_record_size_matches_build_message(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
|
|
const CipherSuiteInfo *suites = GetCipherNames();
|
|
int n = GetCipherNamesSize();
|
|
int i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
const char *name = suites[i].name;
|
|
/* Names prefixed "TLS13-" are TLS 1.3 suites regardless of
|
|
* cipherSuite0, which may be either TLS13_BYTE or ECC_BYTE (for
|
|
* the integrity-only TLS_SHA*_SHA* suites). */
|
|
int isTls13 = (XSTRNCMP(name, "TLS13-", 6) == 0);
|
|
if (record_size_skip_cipher(name))
|
|
continue;
|
|
|
|
if (isTls13) {
|
|
#ifdef WOLFSSL_TLS13
|
|
ExpectIntEQ(record_size_run_pair(wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method, name, 0), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS13
|
|
ExpectIntEQ(record_size_run_pair(wolfDTLSv1_3_client_method,
|
|
wolfDTLSv1_3_server_method, name, 0), TEST_SUCCESS);
|
|
#if defined(WOLFSSL_DTLS_CID)
|
|
ExpectIntEQ(record_size_run_pair(wolfDTLSv1_3_client_method,
|
|
wolfDTLSv1_3_server_method, name, 1), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
}
|
|
else {
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(record_size_run_pair(wolfTLSv1_2_client_method,
|
|
wolfTLSv1_2_server_method, name, 0), TEST_SUCCESS);
|
|
#endif
|
|
#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12)
|
|
ExpectIntEQ(record_size_run_pair(wolfDTLSv1_2_client_method,
|
|
wolfDTLSv1_2_server_method, name, 0), TEST_SUCCESS);
|
|
#if defined(WOLFSSL_DTLS_CID)
|
|
ExpectIntEQ(record_size_run_pair(wolfDTLSv1_2_client_method,
|
|
wolfDTLSv1_2_server_method, name, 1), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_ASYNC_CRYPT)
|
|
/* SendData() sizes the output buffer on every retry, including while an
|
|
* asynchronous BuildMessage is suspended part way through a record. Sizing
|
|
* runs the same build state machine, so the probe must not re-enter it: it
|
|
* would rewind buildMsgState, clear buildArgsSet, and the resumed record would
|
|
* be sized a second time. Check that a suspended build survives a probe, and
|
|
* that a probe from a clean state still returns the exact record size. */
|
|
static int record_size_state_check(method_provider client_method,
|
|
method_provider server_method)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int expectedSz = 0, cleanSz = 0, busySz = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
client_method, server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
if (ssl_c != NULL) {
|
|
expectedSz = BuildMessage(ssl_c, NULL, 0, NULL, 256,
|
|
application_data, 0, 1, 0, CUR_ORDER);
|
|
ssl_c->options.buildMsgState = BUILD_MSG_BEGIN;
|
|
ssl_c->options.buildArgsSet = 0;
|
|
ExpectIntGT(expectedSz, 256);
|
|
|
|
/* Clearing the cache is what forces the BuildMessage path; an AEAD
|
|
* suite would otherwise answer from ssl->recordSzOverhead and the
|
|
* assertions below would hold no matter what the probe did. */
|
|
ssl_c->recordSzOverhead = 0;
|
|
cleanSz = wolfssl_local_GetRecordSize(ssl_c, 256, 1);
|
|
ExpectIntEQ(cleanSz, expectedSz);
|
|
|
|
/* Same probe with a build suspended mid-record. */
|
|
ssl_c->recordSzOverhead = 0;
|
|
ssl_c->options.buildMsgState = BUILD_MSG_ENCRYPT;
|
|
ssl_c->options.buildArgsSet = 1;
|
|
|
|
busySz = wolfssl_local_GetRecordSize(ssl_c, 256, 1);
|
|
|
|
ExpectIntEQ(ssl_c->options.buildMsgState, BUILD_MSG_ENCRYPT);
|
|
ExpectIntEQ(ssl_c->options.buildArgsSet, 1);
|
|
/* Still exact: wolfssl_local_GetMaxPlaintextSize() sizes DTLS
|
|
* fragments from this, so it may not degrade to an upper bound. */
|
|
ExpectIntEQ(busySz, expectedSz);
|
|
|
|
ssl_c->options.buildMsgState = BUILD_MSG_BEGIN;
|
|
ssl_c->options.buildArgsSet = 0;
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && WOLFSSL_ASYNC_CRYPT */
|
|
|
|
int test_record_size_preserves_build_msg_state(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(WOLFSSL_ASYNC_CRYPT)
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(record_size_state_check(wolfTLSv1_2_client_method,
|
|
wolfTLSv1_2_server_method), TEST_SUCCESS);
|
|
#endif
|
|
#ifdef WOLFSSL_TLS13
|
|
/* BuildTls13Message() clobbers buildMsgState by a different route: its
|
|
* sizeOnly return bypasses exit_buildmsg entirely. */
|
|
ExpectIntEQ(record_size_state_check(wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_record_size_cache_invalidated_on_renegotiation(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
byte readBuf[16];
|
|
int sz;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
sz = wolfssl_local_GetRecordSize(ssl_c, 256, 1);
|
|
ExpectIntEQ(sz, BuildMessage(ssl_c, NULL, 0, NULL, 256,
|
|
application_data, 0, 1, 0, CUR_ORDER));
|
|
ExpectIntNE(ssl_c->recordSzOverhead, 0);
|
|
|
|
ExpectIntEQ(wolfSSL_Rehandshake(ssl_c), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* SetKeysSide() during renegotiation must have cleared the cache. */
|
|
sz = wolfssl_local_GetRecordSize(ssl_c, 256, 1);
|
|
ExpectIntEQ(sz, BuildMessage(ssl_c, NULL, 0, NULL, 256,
|
|
application_data, 0, 1, 0, CUR_ORDER));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Number of application records written while checking nonce uniqueness. */
|
|
#define TEST_TLS12_NONCE_RECS 4
|
|
/* Payload size - above WC_ASYNC_THRESH_AES_GCM for every async backend so an
|
|
* async build actually offloads the record encryption. */
|
|
#define TEST_TLS12_NONCE_PAYLOAD 256
|
|
/* Drive the record cipher through the software async simulator where the build
|
|
* has one. Non-blocking math livelocks the async re-drive - see
|
|
* test_tls13_pqc_hybrid_async_server() - so those builds run the test on the
|
|
* synchronous path, where it pins the same invariant. */
|
|
#if defined(WOLFSSL_ASYNC_CRYPT) && !defined(WC_ECC_NONBLOCK) && \
|
|
!defined(WOLFSSL_SP_NONBLOCK)
|
|
#define TEST_TLS12_NONCE_ASYNC
|
|
#endif
|
|
|
|
/*
|
|
* A TLS 1.2 AES-GCM sender must never reuse a nonce under one traffic key
|
|
* (RFC 5288 section 3, NIST SP 800-38D section 8.3). The 8-byte explicit part
|
|
* of the nonce is carried in the clear at the front of each record, so a
|
|
* repeat is directly observable on the wire - and a repeat hands an on-path
|
|
* attacker the GHASH subkey H and with it the ability to forge records.
|
|
*
|
|
* The explicit nonce comes from ssl->encrypt.nonce, which wc_AesGcmEncrypt_ex()
|
|
* fills from its internal counter. That counter used to advance only when the
|
|
* cipher returned a literal 0, so an asynchronous backend - which reports a
|
|
* successful submission with WC_PENDING_E - left it parked and every record
|
|
* went out under the same nonce.
|
|
*
|
|
* Writes several records, then reads the explicit nonces straight out of the
|
|
* memio wire buffer and requires them to be pairwise distinct. Runs in
|
|
* synchronous builds too, where it pins the same invariant end to end.
|
|
*/
|
|
int test_tls12_aesgcm_record_nonce_unique(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && defined(BUILD_AESGCM) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(HAVE_ECC) && !defined(NO_RSA) && defined(WOLFSSL_AES_128) && \
|
|
!defined(NO_PUBLIC_GCM_SET_IV) && \
|
|
((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) || \
|
|
(defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)))
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
byte payload[TEST_TLS12_NONCE_PAYLOAD];
|
|
byte readBuf[TEST_TLS12_NONCE_PAYLOAD];
|
|
byte nonces[TEST_TLS12_NONCE_RECS][AESGCM_EXP_IV_SZ];
|
|
int devId = INVALID_DEVID;
|
|
int off = 0;
|
|
int recLen = 0;
|
|
int ret = 0;
|
|
int err = 0;
|
|
int i;
|
|
int j;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
XMEMSET(nonces, 0, sizeof(nonces));
|
|
XMEMSET(payload, 0x5a, sizeof(payload));
|
|
test_ctx.c_ciphers = test_ctx.s_ciphers = "ECDHE-RSA-AES128-GCM-SHA256";
|
|
|
|
#ifdef TEST_TLS12_NONCE_ASYNC
|
|
/* Offload the record cipher so the WC_PENDING_E path is taken. A hardware
|
|
* device returns its devId, so any non-negative value is a good open. */
|
|
ExpectIntGE(wolfAsync_DevOpen(&devId), 0);
|
|
#endif
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
#ifdef TEST_TLS12_NONCE_ASYNC
|
|
ExpectIntEQ(wolfSSL_SetDevId(ssl_c, devId), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetDevId(ssl_s, devId), WOLFSSL_SUCCESS);
|
|
#endif
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectStrEQ(wolfSSL_get_cipher_name(ssl_c), "ECDHE-RSA-AES128-GCM-SHA256");
|
|
|
|
/* Drop the handshake traffic so the buffer holds only application data.
|
|
* The client writes into s_buff - the buffer the server reads from. */
|
|
if (EXPECT_SUCCESS()) {
|
|
test_memio_clear_buffer(&test_ctx, 0);
|
|
}
|
|
|
|
for (i = 0; i < TEST_TLS12_NONCE_RECS && EXPECT_SUCCESS(); i++) {
|
|
do {
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
if (err == WC_NO_ERR_TRACE(WC_PENDING_E)) {
|
|
ret = wolfSSL_AsyncPoll(ssl_c, WOLF_POLL_FLAG_CHECK_HW);
|
|
if (ret < 0)
|
|
break;
|
|
}
|
|
#endif
|
|
ret = wolfSSL_write(ssl_c, payload, (int)sizeof(payload));
|
|
err = (ret > 0) ? 0 : wolfSSL_get_error(ssl_c, ret);
|
|
} while (err == WC_NO_ERR_TRACE(WC_PENDING_E));
|
|
ExpectIntEQ(ret, (int)sizeof(payload));
|
|
}
|
|
|
|
/* Walk the wire buffer record by record and lift the explicit nonce out
|
|
* of each one: [type][version][length][8-byte explicit nonce][...]. */
|
|
for (i = 0; i < TEST_TLS12_NONCE_RECS && EXPECT_SUCCESS(); i++) {
|
|
ExpectIntGE(test_ctx.s_len - off,
|
|
RECORD_HEADER_SZ + AESGCM_EXP_IV_SZ);
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectIntEQ(test_ctx.s_buff[off], application_data);
|
|
recLen = (test_ctx.s_buff[off + 3] << 8) |
|
|
test_ctx.s_buff[off + 4];
|
|
ExpectIntGE(recLen, AESGCM_EXP_IV_SZ);
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(nonces[i], test_ctx.s_buff + off + RECORD_HEADER_SZ,
|
|
AESGCM_EXP_IV_SZ);
|
|
off += RECORD_HEADER_SZ + recLen;
|
|
}
|
|
}
|
|
ExpectIntEQ(off, test_ctx.s_len);
|
|
|
|
/* No explicit nonce may repeat under this traffic key. */
|
|
for (i = 1; i < TEST_TLS12_NONCE_RECS; i++) {
|
|
for (j = 0; j < i; j++) {
|
|
ExpectIntNE(XMEMCMP(nonces[i], nonces[j], AESGCM_EXP_IV_SZ), 0);
|
|
}
|
|
}
|
|
|
|
/* The records are still valid: the peer authenticates and decrypts each. */
|
|
for (i = 0; i < TEST_TLS12_NONCE_RECS && EXPECT_SUCCESS(); i++) {
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
/* Clear any pending state left by the previous record so the first
|
|
* pass reads rather than polls. */
|
|
err = 0;
|
|
#endif
|
|
do {
|
|
#ifdef WOLFSSL_ASYNC_CRYPT
|
|
if (err == WC_NO_ERR_TRACE(WC_PENDING_E)) {
|
|
ret = wolfSSL_AsyncPoll(ssl_s, WOLF_POLL_FLAG_CHECK_HW);
|
|
if (ret < 0)
|
|
break;
|
|
}
|
|
#endif
|
|
XMEMSET(readBuf, 0, sizeof(readBuf));
|
|
ret = wolfSSL_read(ssl_s, readBuf, (int)sizeof(readBuf));
|
|
err = (ret > 0) ? 0 : wolfSSL_get_error(ssl_s, ret);
|
|
} while (err == WC_NO_ERR_TRACE(WC_PENDING_E));
|
|
ExpectIntEQ(ret, (int)sizeof(payload));
|
|
ExpectIntEQ(XMEMCMP(readBuf, payload, sizeof(payload)), 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#ifdef TEST_TLS12_NONCE_ASYNC
|
|
wolfAsync_DevClose(&devId);
|
|
#endif
|
|
(void)devId;
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_PSK) && \
|
|
!defined(NO_DH) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(BUILD_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_ECC) && \
|
|
!defined(NO_ECC_SECP) && (MAX_DHKEY_SZ >= 256) && \
|
|
(ECC_MIN_KEY_SZ <= 256) && \
|
|
(!defined(NO_ECC256) || defined(HAVE_ALL_CURVES))
|
|
/* The overflow is about the largest prime the build accepts, so use the
|
|
* parameters that match MAX_DHKEY_SZ rather than pinning 4096 bits. */
|
|
#if MAX_DHKEY_SZ >= 512
|
|
#define TEST_DHE_PSK_MAX_DH_FILE "certs/dh4096.pem"
|
|
#define TEST_DHE_PSK_MAX_DH_BITS 4096
|
|
#elif MAX_DHKEY_SZ >= 384
|
|
#define TEST_DHE_PSK_MAX_DH_FILE "certs/dh3072.pem"
|
|
#define TEST_DHE_PSK_MAX_DH_BITS 3072
|
|
#else
|
|
#define TEST_DHE_PSK_MAX_DH_FILE "certs/dh2048.pem"
|
|
#define TEST_DHE_PSK_MAX_DH_BITS 2048
|
|
#endif
|
|
|
|
static unsigned int test_dhe_psk_max_client_cb(WOLFSSL* ssl, const char* hint,
|
|
char* identity, unsigned int id_max, unsigned char* key,
|
|
unsigned int key_max)
|
|
{
|
|
(void)ssl;
|
|
(void)hint;
|
|
|
|
if ((id_max < sizeof("Client_identity")) || (key_max < MAX_PSK_KEY_LEN))
|
|
return 0;
|
|
|
|
XSTRLCPY(identity, "Client_identity", id_max);
|
|
XMEMSET(key, 0xA5, MAX_PSK_KEY_LEN);
|
|
|
|
return MAX_PSK_KEY_LEN;
|
|
}
|
|
|
|
static unsigned int test_dhe_psk_max_server_cb(WOLFSSL* ssl,
|
|
const char* identity, unsigned char* key, unsigned int key_max)
|
|
{
|
|
(void)ssl;
|
|
(void)identity;
|
|
|
|
if (key_max < MAX_PSK_KEY_LEN)
|
|
return 0;
|
|
|
|
XMEMSET(key, 0xA5, MAX_PSK_KEY_LEN);
|
|
|
|
return MAX_PSK_KEY_LEN;
|
|
}
|
|
#endif
|
|
|
|
/* A DHE-PSK exchange fills the pre-master secret buffer with a two byte
|
|
* length, the DH shared secret, another two byte length and the PSK. With the
|
|
* largest DH parameters the build accepts and a maximum length PSK that is
|
|
* more than ENCRYPT_LEN, which the buffer used to be sized with, so the last
|
|
* two bytes of the key landed outside it.
|
|
*
|
|
* The client asks for a named group that is not FFDHE so that the server keeps
|
|
* the 4096 bit parameters it was given rather than negotiating a smaller
|
|
* group.
|
|
*/
|
|
int test_dhe_psk_max_premaster(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_PSK) && \
|
|
!defined(NO_DH) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(BUILD_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256) && \
|
|
defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_ECC) && \
|
|
!defined(NO_ECC_SECP) && (MAX_DHKEY_SZ >= 256) && \
|
|
(ECC_MIN_KEY_SZ <= 256) && \
|
|
(!defined(NO_ECC256) || defined(HAVE_ALL_CURVES))
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
int groups[1] = { WOLFSSL_ECC_SECP256R1 };
|
|
|
|
/* Both lengths, the largest secret the peer can pick and the longest key
|
|
* have to fit. */
|
|
ExpectTrue(MAX_PREMASTER_SZ >=
|
|
OPAQUE16_LEN + MAX_DHKEY_SZ + OPAQUE16_LEN + MAX_PSK_KEY_LEN);
|
|
/* The exchange this test drives has to fit as well. */
|
|
ExpectTrue(MAX_PREMASTER_SZ >= OPAQUE16_LEN +
|
|
(TEST_DHE_PSK_MAX_DH_BITS / 8) + OPAQUE16_LEN + MAX_PSK_KEY_LEN);
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "DHE-PSK-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, "DHE-PSK-AES128-GCM-SHA256"),
|
|
WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_set_psk_client_callback(ssl_c, test_dhe_psk_max_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, test_dhe_psk_max_server_cb);
|
|
}
|
|
ExpectIntEQ(wolfSSL_SetTmpDH_file(ssl_s, TEST_DHE_PSK_MAX_DH_FILE,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_groups(ssl_c, groups, 1), WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* The peer picked the parameters that make the secret its largest. */
|
|
ExpectIntEQ(wolfSSL_GetDhKey_Sz(ssl_c), TEST_DHE_PSK_MAX_DH_BITS);
|
|
|
|
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_NO_TLS12) && defined(HAVE_ECC) && \
|
|
defined(WOLF_PRIVATE_KEY_ID) && defined(WOLF_CRYPTO_CB) && \
|
|
!defined(NO_CHECK_PRIVATE_KEY) && !defined(WOLFSSL_BLIND_PRIVATE_KEY) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
|
|
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_SIGN) && \
|
|
defined(HAVE_ED25519_VERIFY) && defined(HAVE_ED25519_KEY_IMPORT) && \
|
|
defined(ED25519_MAX_ID_LEN)
|
|
#define TEST_TLS12_ED25519_DEV_KEY
|
|
#endif
|
|
#if defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) && \
|
|
defined(HAVE_ED448_VERIFY) && defined(HAVE_ED448_KEY_IMPORT) && \
|
|
defined(ED448_MAX_ID_LEN)
|
|
#define TEST_TLS12_ED448_DEV_KEY
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(TEST_TLS12_ED25519_DEV_KEY) || defined(TEST_TLS12_ED448_DEV_KEY)
|
|
#define TEST_TLS12_ED_DEV_DEVID 0x45444b59
|
|
|
|
static const char testTls12EdDevKeyLabel[] = "ed-device-key";
|
|
|
|
typedef struct TestTls12EdDevCtx {
|
|
#ifdef TEST_TLS12_ED25519_DEV_KEY
|
|
ed25519_key ed25519;
|
|
#endif
|
|
#ifdef TEST_TLS12_ED448_DEV_KEY
|
|
ed448_key ed448;
|
|
#endif
|
|
int signs;
|
|
int checks;
|
|
} TestTls12EdDevCtx;
|
|
|
|
/* Device holding the server private key: signs with it and checks it against
|
|
* the public key it is handed. */
|
|
static int test_tls12_ed_dev_cb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
|
{
|
|
TestTls12EdDevCtx* dev = (TestTls12EdDevCtx*)ctx;
|
|
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
|
|
(void)devIdArg;
|
|
|
|
if (info->algo_type != WC_ALGO_TYPE_PK) {
|
|
return ret;
|
|
}
|
|
#ifdef TEST_TLS12_ED25519_DEV_KEY
|
|
if (info->pk.type == WC_PK_TYPE_ED25519_SIGN) {
|
|
ret = wc_ed25519_sign_msg_ex(info->pk.ed25519sign.in,
|
|
info->pk.ed25519sign.inLen, info->pk.ed25519sign.out,
|
|
info->pk.ed25519sign.outLen, &dev->ed25519,
|
|
info->pk.ed25519sign.type, info->pk.ed25519sign.context,
|
|
info->pk.ed25519sign.contextLen);
|
|
dev->signs++;
|
|
}
|
|
else if (info->pk.type == WC_PK_TYPE_ED25519_CHECK_KEY) {
|
|
ret = 0;
|
|
if ((info->pk.ed25519checkkey.pubKeySz != ED25519_PUB_KEY_SIZE) ||
|
|
(XMEMCMP(info->pk.ed25519checkkey.pubKey, dev->ed25519.p,
|
|
ED25519_PUB_KEY_SIZE) != 0)) {
|
|
ret = PUBLIC_KEY_E;
|
|
}
|
|
dev->checks++;
|
|
}
|
|
#endif
|
|
#ifdef TEST_TLS12_ED448_DEV_KEY
|
|
if (info->pk.type == WC_PK_TYPE_ED448) {
|
|
ret = wc_ed448_sign_msg_ex(info->pk.ed448sign.in,
|
|
info->pk.ed448sign.inLen, info->pk.ed448sign.out,
|
|
info->pk.ed448sign.outLen, &dev->ed448, info->pk.ed448sign.type,
|
|
info->pk.ed448sign.context, info->pk.ed448sign.contextLen);
|
|
dev->signs++;
|
|
}
|
|
else if (info->pk.type == WC_PK_TYPE_ED448_CHECK_KEY) {
|
|
ret = 0;
|
|
if ((info->pk.ed448checkkey.pubKeySz != ED448_PUB_KEY_SIZE) ||
|
|
(XMEMCMP(info->pk.ed448checkkey.pubKey, dev->ed448.p,
|
|
ED448_PUB_KEY_SIZE) != 0)) {
|
|
ret = PUBLIC_KEY_E;
|
|
}
|
|
dev->checks++;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* TLS 1.2 server key exchange signed by a device key named by its label. */
|
|
static int test_tls12_ed_dev_key_round(TestTls12EdDevCtx* dev,
|
|
const char* caFile, const char* certFile)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_TLS12_ED_DEV_DEVID,
|
|
test_tls12_ed_dev_cb, dev), 0);
|
|
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_s, certFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_Label(ctx_s, testTls12EdDevKeyLabel,
|
|
TEST_TLS12_ED_DEV_DEVID), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_check_private_key(ctx_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(dev->checks, 1);
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
}
|
|
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
NULL, NULL), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_version(ssl_c), TLS1_2_VERSION);
|
|
ExpectIntGE(dev->signs, 1);
|
|
/* The handshake does not check the pair again. */
|
|
ExpectIntEQ(dev->checks, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wc_CryptoCb_UnRegisterDevice(TEST_TLS12_ED_DEV_DEVID);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* TEST_TLS12_ED25519_DEV_KEY || TEST_TLS12_ED448_DEV_KEY */
|
|
|
|
int test_tls12_ed25519_dev_private_key(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS12_ED25519_DEV_KEY
|
|
TestTls12EdDevCtx dev;
|
|
byte* der = NULL;
|
|
size_t derSz = 0;
|
|
word32 idx = 0;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
ExpectIntEQ(wc_ed25519_init(&dev.ed25519), 0);
|
|
ExpectIntEQ(load_file("./certs/ed25519/server-ed25519-priv.der", &der,
|
|
&derSz), 0);
|
|
ExpectIntEQ(wc_Ed25519PrivateKeyDecode(der, &idx, &dev.ed25519,
|
|
(word32)derSz), 0);
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
der = NULL;
|
|
idx = 0;
|
|
ExpectIntEQ(load_file("./certs/ed25519/server-ed25519-key.der", &der,
|
|
&derSz), 0);
|
|
ExpectIntEQ(wc_Ed25519PublicKeyDecode(der, &idx, &dev.ed25519,
|
|
(word32)derSz), 0);
|
|
|
|
ExpectIntEQ(test_tls12_ed_dev_key_round(&dev, caEdCertFile, edCertFile),
|
|
TEST_SUCCESS);
|
|
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
wc_ed25519_free(&dev.ed25519);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_ed448_dev_private_key(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_TLS12_ED448_DEV_KEY
|
|
TestTls12EdDevCtx dev;
|
|
byte* der = NULL;
|
|
size_t derSz = 0;
|
|
word32 idx = 0;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
ExpectIntEQ(wc_ed448_init(&dev.ed448), 0);
|
|
ExpectIntEQ(load_file("./certs/ed448/server-ed448-priv.der", &der,
|
|
&derSz), 0);
|
|
ExpectIntEQ(wc_Ed448PrivateKeyDecode(der, &idx, &dev.ed448,
|
|
(word32)derSz), 0);
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
der = NULL;
|
|
idx = 0;
|
|
ExpectIntEQ(load_file("./certs/ed448/server-ed448-key.der", &der,
|
|
&derSz), 0);
|
|
ExpectIntEQ(wc_Ed448PublicKeyDecode(der, &idx, &dev.ed448,
|
|
(word32)derSz), 0);
|
|
|
|
ExpectIntEQ(test_tls12_ed_dev_key_round(&dev, caEd448CertFile,
|
|
ed448CertFile), TEST_SUCCESS);
|
|
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
wc_ed448_free(&dev.ed448);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_CHAIN_VERIFY_CB) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
#define HAVE_CHAIN_VERIFY_CB_TESTS
|
|
#endif
|
|
|
|
#ifdef HAVE_CHAIN_VERIFY_CB_TESTS
|
|
|
|
/* State shared with the chain verify callbacks below. */
|
|
typedef struct test_chain_verify_cb_ctx {
|
|
int calls; /* how many times the callback ran */
|
|
int deferrals; /* how many times to answer CHAIN_VERIFY_WANT_E first */
|
|
int reject; /* non-zero to reject the chain */
|
|
int rejectRet; /* value to reject with, 0 for the default -1 */
|
|
int certsSeen; /* certsSz of the last call */
|
|
int leafMatched; /* certs[0] matched the expected leaf DER */
|
|
/* A copy of the expected leaf: the peer may unload its own certificate
|
|
* buffer once its side of the handshake completes, which in TLS 1.3 is
|
|
* before the server has processed it. */
|
|
unsigned char leafDer[2048];
|
|
unsigned int leafDerSz;
|
|
} test_chain_verify_cb_ctx;
|
|
|
|
/* Remember the certificate the peer will present as certs[0]. */
|
|
static int test_chain_verify_cb_set_leaf(test_chain_verify_cb_ctx* cbCtx,
|
|
const DerBuffer* cert)
|
|
{
|
|
if ((cert == NULL) || (cert->length > sizeof(cbCtx->leafDer)))
|
|
return -1;
|
|
XMEMCPY(cbCtx->leafDer, cert->buffer, cert->length);
|
|
cbCtx->leafDerSz = cert->length;
|
|
return 0;
|
|
}
|
|
|
|
static int test_chain_verify_cb(WOLFSSL* ssl, const WOLFSSL_BUFFER_INFO* certs,
|
|
int certsSz, void* ctx)
|
|
{
|
|
test_chain_verify_cb_ctx* cbCtx = (test_chain_verify_cb_ctx*)ctx;
|
|
|
|
(void)ssl;
|
|
|
|
cbCtx->calls++;
|
|
cbCtx->certsSeen = certsSz;
|
|
|
|
if ((certsSz > 0) && (cbCtx->leafDerSz > 0) &&
|
|
(certs[0].length == cbCtx->leafDerSz) &&
|
|
(XMEMCMP(certs[0].buffer, cbCtx->leafDer, cbCtx->leafDerSz) == 0)) {
|
|
cbCtx->leafMatched = 1;
|
|
}
|
|
|
|
if (cbCtx->calls <= cbCtx->deferrals)
|
|
return WC_NO_ERR_TRACE(CHAIN_VERIFY_WANT_E);
|
|
if (cbCtx->reject)
|
|
return (cbCtx->rejectRet != 0) ? cbCtx->rejectRet : -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* F5: the ordinary verify callback must not run while the chain verify
|
|
* callback owns the verdict. This one rejects, so being called at all fails
|
|
* the handshake as well as showing up in the count. */
|
|
static int test_chain_verify_cb_verify_calls;
|
|
|
|
static int test_chain_verify_cb_verify(int preverify,
|
|
WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
(void)preverify;
|
|
(void)store;
|
|
test_chain_verify_cb_verify_calls++;
|
|
return 0;
|
|
}
|
|
|
|
/* Which end of the connection verifies with the callback. */
|
|
#define TEST_CVC_CLIENT 0
|
|
#define TEST_CVC_SERVER 1
|
|
|
|
/* Set up a handshake that the verifying end cannot complete on its own.
|
|
* Client side: the client's CA store is emptied, so verifying the server's
|
|
* certificate fails with ASN_NO_SIGNER_E. Server side: the client presents a
|
|
* certificate the server has no CA for, and the server insists on one.
|
|
* Installing the chain verify callback on that end is then the only thing
|
|
* that can make the handshake succeed. Leaves the objects allocated so the
|
|
* caller can inspect them. Pass cbCtx as NULL to install no callback
|
|
* (negative control). */
|
|
static int test_chain_verify_cb_run(test_chain_verify_cb_ctx* cbCtx, int side,
|
|
method_provider client_method, method_provider server_method,
|
|
WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c, WOLFSSL** ssl_s,
|
|
struct test_memio_ctx* test_ctx)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL* verifier = NULL;
|
|
WOLFSSL* peer = NULL;
|
|
/* Only when the chain verify callback is installed: the negative control
|
|
* relies on wolfSSL's own verification failing. */
|
|
VerifyCallback verifyCb = (cbCtx != NULL) ? test_chain_verify_cb_verify
|
|
: NULL;
|
|
|
|
test_chain_verify_cb_verify_calls = 0;
|
|
XMEMSET(test_ctx, 0, sizeof(*test_ctx));
|
|
ExpectIntEQ(test_memio_setup(test_ctx, ctx_c, ctx_s, ssl_c, ssl_s,
|
|
client_method, server_method), 0);
|
|
|
|
if (side == TEST_CVC_CLIENT) {
|
|
ExpectIntEQ(wolfSSL_CTX_UnloadCAs(*ctx_c), WOLFSSL_SUCCESS);
|
|
/* explicit, since OPENSSL_COMPATIBLE_DEFAULTS turns verification off
|
|
* on clients by default */
|
|
wolfSSL_set_verify(*ssl_c, WOLFSSL_VERIFY_PEER, verifyCb);
|
|
verifier = *ssl_c;
|
|
peer = *ssl_s;
|
|
}
|
|
else {
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(*ssl_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(*ssl_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(*ssl_s,
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
|
verifyCb);
|
|
verifier = *ssl_s;
|
|
peer = *ssl_c;
|
|
}
|
|
|
|
if (EXPECT_SUCCESS() && (cbCtx != NULL)) {
|
|
if (side == TEST_CVC_CLIENT) {
|
|
/* context-level callback, object-level user context */
|
|
ExpectIntEQ(wolfSSL_CTX_SetChainVerifyCb(*ctx_c,
|
|
test_chain_verify_cb), WOLFSSL_SUCCESS);
|
|
wolfSSL_SetChainVerifyCtx(verifier, cbCtx);
|
|
}
|
|
else {
|
|
/* object-level callback, context-level user context */
|
|
ExpectIntEQ(wolfSSL_SetChainVerifyCb(verifier,
|
|
test_chain_verify_cb), WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_SetChainVerifyCtx(*ctx_s, cbCtx);
|
|
}
|
|
ExpectPtrEq(wolfSSL_GetChainVerifyCtx(verifier), cbCtx);
|
|
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
/* Seed a stale result, so the callback's verdict has to replace it
|
|
* rather than being reported as this earlier failure. */
|
|
wolfSSL_set_verify_result(verifier,
|
|
WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED);
|
|
#endif
|
|
|
|
/* certs[0] must be the peer's own certificate. */
|
|
ExpectIntEQ(test_chain_verify_cb_set_leaf(cbCtx,
|
|
peer->buffers.certificate), 0);
|
|
}
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static void test_chain_verify_cb_free(WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s,
|
|
WOLFSSL** ssl_c, WOLFSSL** ssl_s)
|
|
{
|
|
wolfSSL_free(*ssl_c);
|
|
wolfSSL_free(*ssl_s);
|
|
wolfSSL_CTX_free(*ctx_c);
|
|
wolfSSL_CTX_free(*ctx_s);
|
|
*ssl_c = NULL;
|
|
*ssl_s = NULL;
|
|
*ctx_c = NULL;
|
|
*ctx_s = NULL;
|
|
}
|
|
|
|
/* Negative control first, then the callback accepting after the given number
|
|
* of deferrals. */
|
|
static int test_chain_verify_cb_accept(int side, int deferrals,
|
|
method_provider client_method, method_provider server_method)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
test_chain_verify_cb_ctx cbCtx;
|
|
int err;
|
|
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
cbCtx.deferrals = deferrals;
|
|
|
|
/* Negative control: without the callback this same setup must fail, so the
|
|
* success below can only come from the callback replacing verification. */
|
|
ExpectIntEQ(test_chain_verify_cb_run(NULL, side, client_method,
|
|
server_method, &ctx_c, &ctx_s, &ssl_c, &ssl_s, &test_ctx),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), -1);
|
|
/* The client certificate is self-signed, which some builds report as
|
|
* such rather than as having no signer. */
|
|
err = wolfSSL_get_error((side == TEST_CVC_CLIENT) ? ssl_c : ssl_s,
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectTrue((err == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)) ||
|
|
(err == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E)));
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
|
|
ExpectIntEQ(test_chain_verify_cb_run(&cbCtx, side, client_method,
|
|
server_method, &ctx_c, &ctx_s, &ssl_c, &ssl_s, &test_ctx),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Deferred the requested number of times, then accepted, every call
|
|
* seeing the same chain. */
|
|
ExpectIntEQ(cbCtx.calls, deferrals + 1);
|
|
ExpectIntGT(cbCtx.certsSeen, 0);
|
|
ExpectIntEQ(cbCtx.leafMatched, 1);
|
|
|
|
/* The callback replaces wolfSSL's decision entirely, so the verify
|
|
* callback set with wolfSSL_set_verify() is never consulted. */
|
|
ExpectIntEQ(test_chain_verify_cb_verify_calls, 0);
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
/* Accepting reports success even though wolfSSL verified nothing: this
|
|
* records the callback's verdict, not a check wolfSSL made. */
|
|
ExpectIntEQ(wolfSSL_get_verify_result(
|
|
(side == TEST_CVC_CLIENT) ? ssl_c : ssl_s), WOLFSSL_X509_V_OK);
|
|
#endif
|
|
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if !defined(WOLFSSL_NO_TLS12) || defined(HAVE_CERTIFICATE_STATUS_REQUEST)
|
|
/* The verifying end fails before the callback is consulted, with the given
|
|
* error, and the callback is never called. */
|
|
static int test_chain_verify_cb_expect_fail(test_chain_verify_cb_ctx* cbCtx,
|
|
WOLFSSL* verifier, WOLFSSL* ssl_c, WOLFSSL* ssl_s, int err)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(verifier, WOLFSSL_FATAL_ERROR), err);
|
|
ExpectIntEQ(cbCtx->calls, 0);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
#endif /* HAVE_CHAIN_VERIFY_CB_TESTS */
|
|
|
|
int test_tls12_chain_verify_cb(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && !defined(WOLFSSL_NO_TLS12)
|
|
ExpectIntEQ(test_chain_verify_cb_accept(TEST_CVC_CLIENT, 0,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_chain_verify_cb_server(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(WOLFSSL_NO_CLIENT_AUTH)
|
|
/* Client authentication: the server verifies, deferring twice. */
|
|
ExpectIntEQ(test_chain_verify_cb_accept(TEST_CVC_SERVER, 2,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_chain_verify_cb_defer(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && defined(WOLFSSL_TLS13)
|
|
ExpectIntEQ(test_chain_verify_cb_accept(TEST_CVC_CLIENT, 3,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_chain_verify_cb_server(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && defined(WOLFSSL_TLS13)
|
|
ExpectIntEQ(test_chain_verify_cb_accept(TEST_CVC_SERVER, 2,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), TEST_SUCCESS);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_chain_verify_cb_reject(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && defined(WOLFSSL_TLS13)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
test_chain_verify_cb_ctx cbCtx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
#ifndef WOLFSSL_ASYNC_CRYPT
|
|
int sLen = 0;
|
|
#endif
|
|
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
cbCtx.deferrals = 1;
|
|
cbCtx.reject = 1;
|
|
|
|
ExpectIntEQ(test_chain_verify_cb_run(&cbCtx, TEST_CVC_CLIENT,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
&ctx_c, &ctx_s, &ssl_c, &ssl_s, &test_ctx), TEST_SUCCESS);
|
|
|
|
#ifndef WOLFSSL_ASYNC_CRYPT
|
|
/* Shutting down while the verdict is deferred leaves the handshake in
|
|
* flight: nothing is sent and the suspend error is kept. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_WANT_E));
|
|
ExpectIntEQ(cbCtx.calls, 1);
|
|
sLen = test_ctx.s_len;
|
|
ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(test_ctx.s_len, sLen);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_WANT_E));
|
|
#endif
|
|
|
|
/* Handshake must fail, and only after the deferral was honoured. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), -1);
|
|
ExpectIntEQ(cbCtx.calls, 2);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_CB_E));
|
|
|
|
/* A single generic certificate alert is sent - the callback's reason is
|
|
* never leaked to the peer. */
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
ExpectIntEQ(h.last_tx.code, bad_certificate);
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl_c),
|
|
WOLFSSL_X509_V_ERR_CERT_REJECTED);
|
|
#endif
|
|
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_chain_verify_cb_success_rejects(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && defined(WOLFSSL_TLS13)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
test_chain_verify_cb_ctx cbCtx;
|
|
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
cbCtx.reject = 1;
|
|
/* Every other wolfSSL verify callback accepts with WOLFSSL_SUCCESS. This
|
|
* one accepts on 0 only, so 1 has to reject. */
|
|
cbCtx.rejectRet = WOLFSSL_SUCCESS;
|
|
|
|
ExpectIntEQ(test_chain_verify_cb_run(&cbCtx, TEST_CVC_CLIENT,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
&ctx_c, &ctx_s, &ssl_c, &ssl_s, &test_ctx), TEST_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), -1);
|
|
ExpectIntEQ(cbCtx.calls, 1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_CB_E));
|
|
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_chain_verify_cb_bad_der(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && !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;
|
|
test_chain_verify_cb_ctx cbCtx;
|
|
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
|
|
ExpectIntEQ(test_chain_verify_cb_run(&cbCtx, TEST_CVC_CLIENT,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method,
|
|
&ctx_c, &ctx_s, &ssl_c, &ssl_s, &test_ctx), TEST_SUCCESS);
|
|
|
|
/* Break the length of the leaf's outer SEQUENCE. The Certificate message
|
|
* still frames correctly, but the certificate no longer decodes. */
|
|
ExpectNotNull(ssl_s->buffers.certificate);
|
|
ExpectIntGT(ssl_s->buffers.certificate->length, 4);
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_s->buffers.certificate->buffer[2] = 0xFF;
|
|
ssl_s->buffers.certificate->buffer[3] = 0xFF;
|
|
}
|
|
|
|
/* wolfSSL rejects it on its own - the callback is never consulted. */
|
|
ExpectIntEQ(test_chain_verify_cb_expect_fail(&cbCtx, ssl_c, ssl_c, ssl_s,
|
|
WC_NO_ERR_TRACE(ASN_PARSE_E)), TEST_SUCCESS);
|
|
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls12_chain_verify_cb_bad_chain(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && !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;
|
|
test_chain_verify_cb_ctx cbCtx;
|
|
|
|
/* First with the chain intact: the callback sees both the server's
|
|
* certificate and the issuer it sends along. */
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
ExpectIntEQ(test_chain_verify_cb_run(&cbCtx, TEST_CVC_CLIENT,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method,
|
|
&ctx_c, &ctx_s, &ssl_c, &ssl_s, &test_ctx), TEST_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_s, svrCertFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_chain_verify_cb_set_leaf(&cbCtx,
|
|
ssl_s->buffers.certificate), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(cbCtx.calls, 1);
|
|
ExpectIntEQ(cbCtx.certsSeen, 2);
|
|
ExpectIntEQ(cbCtx.leafMatched, 1);
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
|
|
/* Now break the issuer's outer SEQUENCE length. A chain entry is a 3-byte
|
|
* length followed by the DER, so the message still frames. */
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
ExpectIntEQ(test_chain_verify_cb_run(&cbCtx, TEST_CVC_CLIENT,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method,
|
|
&ctx_c, &ctx_s, &ssl_c, &ssl_s, &test_ctx), TEST_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_file(ssl_s, svrCertFile),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl_s->buffers.certChain);
|
|
ExpectIntGT(ssl_s->buffers.certChain->length, OPAQUE24_LEN + 4);
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_s->buffers.certChain->buffer[OPAQUE24_LEN + 2] = 0xFF;
|
|
ssl_s->buffers.certChain->buffer[OPAQUE24_LEN + 3] = 0xFF;
|
|
}
|
|
|
|
ExpectIntEQ(test_chain_verify_cb_expect_fail(&cbCtx, ssl_c, ssl_c, ssl_s,
|
|
WC_NO_ERR_TRACE(ASN_PARSE_E)), TEST_SUCCESS);
|
|
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_tls13_chain_verify_cb_postauth(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
test_chain_verify_cb_ctx cbCtx;
|
|
char buf[8];
|
|
#ifdef HAVE_WRITE_DUP
|
|
WOLFSSL* ssl_w = NULL;
|
|
#endif
|
|
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
cbCtx.deferrals = 2;
|
|
|
|
/* Client: has a certificate and allows post-handshake authentication.
|
|
* The certificate is loaded on the context: without OPENSSL_EXTRA a
|
|
* certificate loaded on the SSL object is unloaded when the handshake
|
|
* completes, and the post-handshake Certificate would be empty. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0);
|
|
|
|
/* Server: no CA for the client, the callback verifies instead, and only
|
|
* after the handshake. Message grouping (on by default with
|
|
* OPENSSL_COMPATIBLE_DEFAULTS) would hold the CertificateRequest back in
|
|
* the output buffer. */
|
|
wolfSSL_set_verify(ssl_s,
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_POST_HANDSHAKE, NULL);
|
|
ExpectIntEQ(wolfSSL_clear_group_messages(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetChainVerifyCb(ssl_s, test_chain_verify_cb),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_SetChainVerifyCtx(ssl_s, &cbCtx);
|
|
ExpectIntEQ(test_chain_verify_cb_set_leaf(&cbCtx,
|
|
ssl_c->buffers.certificate), 0);
|
|
|
|
/* No certificate during the handshake, so no callback yet. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(cbCtx.calls, 0);
|
|
|
|
/* The server asks; the client answers with its certificate from inside
|
|
* wolfSSL_read(). */
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* The certificate arrives inside wolfSSL_read(); the callback defers. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_WANT_E));
|
|
ExpectIntEQ(cbCtx.calls, 1);
|
|
|
|
/* Only wolfSSL_read() resumes it: a write is refused, and the callback
|
|
* is not asked. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_s, "hi", 2), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_WANT_E));
|
|
ExpectIntEQ(cbCtx.calls, 1);
|
|
|
|
/* wolfSSL_accept() meanwhile must leave the suspended message alone. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
|
|
|
#ifdef HAVE_WRITE_DUP
|
|
ExpectNotNull(ssl_w = wolfSSL_write_dup(ssl_s));
|
|
#endif
|
|
|
|
/* Reading again asks the callback again: still deferred. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_WANT_E));
|
|
ExpectIntEQ(cbCtx.calls, 2);
|
|
|
|
#ifdef HAVE_WRITE_DUP
|
|
/* The deferral is not a read error handed to the write duplicate. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_w, "hi", 2), 2);
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), 2);
|
|
#endif
|
|
|
|
/* Accepted; CertificateVerify and Finished follow, then nothing to read. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(cbCtx.calls, 3);
|
|
ExpectIntEQ(cbCtx.certsSeen, 1);
|
|
ExpectIntEQ(cbCtx.leafMatched, 1);
|
|
|
|
/* The connection is intact: application data flows. */
|
|
ExpectIntEQ(wolfSSL_write(ssl_c, "hi", 2), 2);
|
|
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), 2);
|
|
|
|
#ifdef HAVE_WRITE_DUP
|
|
wolfSSL_free(ssl_w);
|
|
#endif
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_chain_verify_cb_min_key(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && !defined(NO_RSA) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
test_chain_verify_cb_ctx cbCtx;
|
|
WOLFSSL_ALERT_HISTORY h;
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
method_provider client_method = wolfTLSv1_2_client_method;
|
|
method_provider server_method = wolfTLSv1_2_server_method;
|
|
#else
|
|
method_provider client_method = wolfTLSv1_3_client_method;
|
|
method_provider server_method = wolfTLSv1_3_server_method;
|
|
#endif
|
|
|
|
XMEMSET(&h, 0, sizeof(h));
|
|
|
|
/* The callback accepts, but the handshake uses the server's key directly,
|
|
* so the minimum key size still applies - even with verification turned
|
|
* off, which is not the callback's concern. */
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
ExpectIntEQ(test_chain_verify_cb_run(&cbCtx, TEST_CVC_CLIENT,
|
|
client_method, server_method, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
&test_ctx), TEST_SUCCESS);
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_SetMinRsaKey_Sz(ssl_c, 4096), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(RSA_KEY_SIZE_E));
|
|
ExpectIntEQ(cbCtx.calls, 1);
|
|
/* The peer is told why the connection ends. */
|
|
ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(h.last_tx.level, alert_fatal);
|
|
ExpectIntEQ(h.last_tx.code, bad_certificate);
|
|
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_chain_verify_cb_dtls(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && defined(WOLFSSL_DTLS) && \
|
|
!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;
|
|
test_chain_verify_cb_ctx cbCtx;
|
|
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_UnloadCAs(ctx_c), WOLFSSL_SUCCESS);
|
|
|
|
/* DTLS is not supported with the callback: the setters refuse it. */
|
|
ExpectIntEQ(wolfSSL_CTX_SetChainVerifyCb(ctx_c, test_chain_verify_cb),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E));
|
|
ExpectIntEQ(wolfSSL_SetChainVerifyCb(ssl_c, test_chain_verify_cb),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E));
|
|
ExpectNull(wolfSSL_GetChainVerifyCtx(ssl_c));
|
|
|
|
/* A callback that got in regardless fails the handshake before it is
|
|
* consulted. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ssl_c->chainVerifyCb = test_chain_verify_cb;
|
|
ssl_c->chainVerifyCtx = &cbCtx;
|
|
}
|
|
ExpectIntEQ(test_chain_verify_cb_expect_fail(&cbCtx, ssl_c, ssl_c, ssl_s,
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E)), TEST_SUCCESS);
|
|
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_chain_verify_cb_stapling(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHAIN_VERIFY_CB_TESTS) && \
|
|
defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
test_chain_verify_cb_ctx cbCtx;
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
method_provider client_method = wolfTLSv1_2_client_method;
|
|
method_provider server_method = wolfTLSv1_2_server_method;
|
|
#else
|
|
method_provider client_method = wolfTLSv1_3_client_method;
|
|
method_provider server_method = wolfTLSv1_3_server_method;
|
|
#endif
|
|
|
|
/* Stapling requested before the callback: the setters refuse it, on the
|
|
* object and on the context. The server side is not affected, since a
|
|
* server's stapling is about its own certificate. */
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
ExpectIntEQ(test_chain_verify_cb_run(NULL, TEST_CVC_CLIENT,
|
|
client_method, server_method, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
&test_ctx), TEST_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseOCSPStapling(ssl_c, WOLFSSL_CSR_OCSP, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SetChainVerifyCb(ssl_c, test_chain_verify_cb),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E));
|
|
ExpectIntEQ(wolfSSL_CTX_UseOCSPStapling(ctx_c, WOLFSSL_CSR_OCSP, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_SetChainVerifyCb(ctx_c, test_chain_verify_cb),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E));
|
|
ExpectIntEQ(wolfSSL_CTX_SetChainVerifyCb(ctx_s, test_chain_verify_cb),
|
|
WOLFSSL_SUCCESS);
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
|
|
/* Stapling requested after the callback: the handshake fails before the
|
|
* callback is consulted, whether or not the server staples. */
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
ExpectIntEQ(test_chain_verify_cb_run(&cbCtx, TEST_CVC_CLIENT,
|
|
client_method, server_method, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
&test_ctx), TEST_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseOCSPStapling(ssl_c, WOLFSSL_CSR_OCSP, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_chain_verify_cb_expect_fail(&cbCtx, ssl_c, ssl_c, ssl_s,
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E)), TEST_SUCCESS);
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
|
|
#ifdef HAVE_OCSP
|
|
/* So is must-staple on its own, both ways round. */
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
ExpectIntEQ(test_chain_verify_cb_run(NULL, TEST_CVC_CLIENT,
|
|
client_method, server_method, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
&test_ctx), TEST_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(ctx_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_SetChainVerifyCb(ctx_c, test_chain_verify_cb),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E));
|
|
ExpectIntEQ(wolfSSL_SetChainVerifyCb(ssl_c, test_chain_verify_cb),
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E));
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
ExpectIntEQ(test_chain_verify_cb_run(&cbCtx, TEST_CVC_CLIENT,
|
|
client_method, server_method, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
&test_ctx), TEST_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(ctx_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_chain_verify_cb_expect_fail(&cbCtx, ssl_c, ssl_c, ssl_s,
|
|
WC_NO_ERR_TRACE(CHAIN_VERIFY_UNSUPPORTED_E)), TEST_SUCCESS);
|
|
test_chain_verify_cb_free(&ctx_c, &ctx_s, &ssl_c, &ssl_s);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|