mirror of https://github.com/wolfSSL/wolfssl.git
Send server ChangeCipherSpec when client offers a session id
RFC 8446 Appendix D.4 describes a TLS 1.3 server answering a ClientHello that carries a non-empty legacy_session_id with a ChangeCipherSpec. Both server send paths sat behind WOLFSSL_TLS13_MIDDLEBOX_COMPAT, which only --enable-tls13-middlebox-compat and --enable-jni define and which neither enables by default, so a default-built server never sent one. Peers that require the record, such as Erlang's ssl, abort the handshake with an unexpected_message alert. Compile the two server accept-path sends unconditionally and drive them from the runtime tls13MiddleBoxCompat flag, which the server already clears when the client's session id is empty. This needs the sentChangeCipher option bit compiled in for every TLS 1.3 build, and SendChangeCipher itself, whose definition was guarded on TLS 1.2 or old TLS or middlebox compat and so went missing from a TLS 1.3-only build such as examples/configs/user_settings_tls13.h. That definition is now compiled for a TLS 1.3 server, or a TLS 1.3 client in a middlebox compat build, so a client-only build that can never call it does not carry it. DTLS stays excluded by the existing guard. QUIC clears the flag on its own rather than trusting the peer to honour the empty session id RFC 9001 Section 8.4 calls for, since a ChangeCipherSpec record has no way to travel a QUIC CRYPTO stream. With ECH the outer ClientHello decides on its own, since TLSX_ECH_Parse rebuilds the inner hello carrying the outer session id, so no carve-out is needed for the inner re-parse. wolfSSL_clear() resets the flag and sentChangeCipher, or a reused server object would answer only its first peer. Under TLS 1.3 the record is a dummy and must not move handshake state, so the OPENSSL_EXTRA block no longer assigns serverState for it. It ran before any send could fail, so a retryable send left serverState reading as ChangeCipherSpec complete and the server stopped waiting for the second ClientHello. The info callback still fires on both sides, and the client's clientState is untouched, so nothing an application can observe changes. Both server sends go through one static helper, SendTls13ServerChangeCipher, so the guard, the mark and the flush are stated once, with the flush an argument because only the HelloRetryRequest needs it. sentChangeCipher is marked on a WANT_WRITE as well as on success, since SendChangeCipher appends the record to the output buffer before the flush that stops short, and the mark is all that stops a second one being built. That matters on the HelloRetryRequest path, where the resumed accept does not come back to the same case: the prologue drains the buffer and advances past TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE, so the flight falls through into TLS13_SERVER_HELLO_SENT, which asks for a ChangeCipherSpec of its own. SendTls13ServerHello flushes a HelloRetryRequest as soon as it is built, so the ChangeCipherSpec queued behind it needs a flush of its own. Without one, a server grouping its messages, which every OPENSSL_COMPATIBLE_DEFAULTS build does, leaves the record in the output buffer and the peer sees a bare HelloRetryRequest. The three client send sites in wolfSSL_connect_TLSv13 mark the same way. The one answering a HelloRetryRequest also keeps buildingMsg set when the send stops short, since the second ClientHello still has to follow and the connect state machine must stay on that case while the record drains; without it a refused write stranded the handshake. What a client puts on the wire is unchanged. Without WOLFSSL_TLS13_MIDDLEBOX_COMPAT it still sends an empty session id and no ChangeCipherSpec of its own. The test helper counts ChangeCipherSpec records rather than answering whether there is one, so that a second record going out reads differently from the one record that is owed. test_tls13_middlebox_compat_hrr_ccs covers the HelloRetryRequest path with messages grouped and ungrouped, and checks the flight answering the second ClientHello carries no further record. test_tls13_middlebox_compat_server_ccs_retry refuses the write carrying the record and resumes the handshake, so a send that stops part way still leaves exactly one on the wire, and test_tls13_middlebox_compat_hrr_ccs_retry does the same on the retry path, where the flight answering the second ClientHello must carry none. test_tls13_middlebox_compat_client_ccs_retry refuses the client's own record on both of its handshake flights, and test_tls13_middlebox_compat_client_ccs_retry_early_data on the third client send site, the early data one, where the record goes out ahead of the 0-RTT records rather than as part of a handshake flight and a resumed wolfSSL_write_early_data() would otherwise repeat it. Those client-side tests need WOLFSSL_TLS13_MIDDLEBOX_COMPAT, which no os-check-linux entry defined: only --enable-tls13-middlebox-compat and --enable-jni set it, and configure.ac turns it back off when ECH is on, as it is under --enable-all. The all-tls13-middlebox-compat entry is --enable-all with ECH off and the option on, so they run somewhere. Also correct the macro's doc comment, which claimed the feature defaults to on, reword the --enable-tls13-middlebox-compat help text and the matching CMake option description, which described the whole feature rather than the client half they now control, and drop a duplicated dtls test in the same condition.pull/11306/head
parent
659b71ab87
commit
34b515d138
|
|
@ -16,6 +16,9 @@
|
|||
"configure": ["--enable-all", "--enable-haproxy", "--enable-quic"]},
|
||||
{"name": "all-asn-template-old-oid-sum", "minutes": 8.5,
|
||||
"configure": ["--enable-all", "--enable-asn=template", "CPPFLAGS=-DWOLFSSL_OLD_OID_SUM"]},
|
||||
{"name": "all-tls13-middlebox-compat", "minutes": 8.5,
|
||||
"comment": "Only --enable-tls13-middlebox-compat and --enable-jni define WOLFSSL_TLS13_MIDDLEBOX_COMPAT, and configure.ac turns it back off when ECH is on, which --enable-all enables. So no other entry in this matrix compiles the client-side ChangeCipherSpec sends in wolfSSL_connect_TLSv13 or the tests/api/test_tls13.c middlebox tests guarded on that macro; without --disable-ech here they would all be skipped.",
|
||||
"configure": ["--enable-all", "--disable-ech", "--enable-tls13-middlebox-compat"]},
|
||||
{"name": "all-asn-original-old-oid-sum", "minutes": 8.4,
|
||||
"configure": ["--enable-all", "--enable-asn=original", "CPPFLAGS=-DWOLFSSL_OLD_OID_SUM"]},
|
||||
{"name": "all-asn-original", "minutes": 8.3,
|
||||
|
|
|
|||
|
|
@ -727,7 +727,7 @@ endif()
|
|||
|
||||
# TLS v1.3 middlebox compatibility (RFC 8446 Appendix D.4)
|
||||
add_option("WOLFSSL_TLS13_MIDDLEBOX_COMPAT"
|
||||
"Enable TLS v1.3 middlebox compatibility mode (default: disabled)"
|
||||
"Enable client-side TLS v1.3 middlebox compatibility: send a fake session id and a ChangeCipherSpec. Server-side is always on (default: disabled)"
|
||||
"no" "yes;no")
|
||||
|
||||
if(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
||||
|
|
@ -3139,7 +3139,7 @@ if(WOLFSSL_ECH)
|
|||
list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_ECH")
|
||||
if(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
||||
message(WARNING
|
||||
"ECH is incompatible with middlebox compatibility - disabling middlebox compatibility")
|
||||
"ECH is incompatible with client-side middlebox compatibility - disabling middlebox compatibility")
|
||||
override_cache(WOLFSSL_TLS13_MIDDLEBOX_COMPAT "no")
|
||||
list(REMOVE_ITEM WOLFSSL_DEFINITIONS
|
||||
"-DWOLFSSL_TLS13_MIDDLEBOX_COMPAT")
|
||||
|
|
|
|||
13
ChangeLog.md
13
ChangeLog.md
|
|
@ -228,6 +228,19 @@
|
|||
key of a reloaded key must keep the one exported at generation time, or
|
||||
load it into a separate key with `ImportPubRaw`.
|
||||
|
||||
* **Behavioral change (TLS 1.3 server ChangeCipherSpec)**: a TLS 1.3 server
|
||||
now answers a ClientHello carrying a non-empty `legacy_session_id` with a
|
||||
ChangeCipherSpec record, as RFC 8446 Appendix D.4 describes for middlebox
|
||||
compatibility mode. It only did so before in builds defining
|
||||
`WOLFSSL_TLS13_MIDDLEBOX_COMPAT`, which only `--enable-tls13-middlebox-compat`
|
||||
and `--enable-jni` set and neither is on by default, so a default-built
|
||||
server stayed silent and peers that expect the record - Erlang's `ssl`
|
||||
among them - aborted the handshake with an unexpected_message alert. A
|
||||
client sending an empty session id still sees none, and DTLS and QUIC are
|
||||
unaffected. One visible difference: `wolfSSL_get_state()` no longer passes
|
||||
through `WOLFSSL_SS_SERVER_CHANGECIPHERSPEC` or
|
||||
`WOLFSSL_SS_CLIENT_CHANGECIPHERSPEC` on a TLS 1.3 connection.
|
||||
|
||||
## New Features
|
||||
|
||||
* Added `WC_ALGO_TYPE_KEYSTORE`, a crypto callback algorithm type for lifetime operations on keys held in a hardware key store, with the public API in `wolfssl/wolfcrypt/wc_keystore.h` behind `--enable-cryptocbutils=keystore`. Seven operations - plaintext and wrapped import/export, derive, delete and get-info - address keys by an opaque device-defined reference that wolfCrypt copies through and never interprets, the same way it treats a key object's `id[]` blob. This lets a device create, wrap, derive and destroy keys that never appear in memory, which `WOLF_CRYPTO_CB_SETKEY` and `WOLF_CRYPTO_CB_EXPORT_KEY` cannot express because both are bound to a wolfCrypt key object holding material for its own use.
|
||||
|
|
|
|||
12
configure.ac
12
configure.ac
|
|
@ -1429,7 +1429,8 @@ then
|
|||
|
||||
if test "$KERNEL_MODE_DEFAULTS" != "yes"
|
||||
then
|
||||
# Disable QUIC with JNI since incompatible with WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
||||
# Disable QUIC with JNI since incompatible with client-side
|
||||
# WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
||||
test "$enable_quic" = "" && test "$enable_cryptonly" != "yes" && test "$enable_jni" != "yes" && enable_quic=yes
|
||||
AM_CFLAGS="$AM_CFLAGS -DHAVE_CRL_IO -DHAVE_IO_TIMEOUT"
|
||||
fi
|
||||
|
|
@ -1447,7 +1448,8 @@ then
|
|||
|
||||
if test "$ENABLED_FIPS" = "no"
|
||||
then
|
||||
# Disable ECH with JNI since incompatible with WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
||||
# Disable ECH with JNI since incompatible with client-side
|
||||
# WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
||||
test "$enable_ech" = "" && test "$enable_jni" != "yes" && enable_ech=yes
|
||||
test "$enable_scep" = "" && enable_scep=yes
|
||||
test "$enable_mcast" = "" && enable_mcast=yes
|
||||
|
|
@ -2744,7 +2746,7 @@ fi
|
|||
|
||||
# TLS v1.3 middlebox compatibility (RFC 8446 Appendix D.4)
|
||||
AC_ARG_ENABLE([tls13-middlebox-compat],
|
||||
[AS_HELP_STRING([--enable-tls13-middlebox-compat],[Enable TLS v1.3 middlebox compatibility mode (default: disabled)])],
|
||||
[AS_HELP_STRING([--enable-tls13-middlebox-compat],[Enable client-side TLS v1.3 middlebox compatibility: send a fake session id and a ChangeCipherSpec. Server-side is always on (default: disabled)])],
|
||||
[ ENABLED_TLS13_MIDDLEBOX_COMPAT=$enableval ],
|
||||
[ ENABLED_TLS13_MIDDLEBOX_COMPAT=no ]
|
||||
)
|
||||
|
|
@ -2756,7 +2758,7 @@ then
|
|||
ENABLED_TLS13_MIDDLEBOX_COMPAT="no"
|
||||
elif test "x$ENABLED_ECH" = "xyes"
|
||||
then
|
||||
AC_MSG_NOTICE([ECH is incompatible with middlebox compatibility - disabling middlebox compatibility])
|
||||
AC_MSG_NOTICE([ECH is incompatible with client-side middlebox compatibility - disabling middlebox compatibility])
|
||||
ENABLED_TLS13_MIDDLEBOX_COMPAT="no"
|
||||
else
|
||||
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_TLS13_MIDDLEBOX_COMPAT"
|
||||
|
|
@ -9775,7 +9777,7 @@ then
|
|||
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ALWAYS_KEEP_SNI"
|
||||
if test "x$ENABLED_ECH" = "xyes"
|
||||
then
|
||||
AC_MSG_NOTICE([ECH is incompatible with middlebox compatibility - not enabling middlebox compatibility for JNI])
|
||||
AC_MSG_NOTICE([ECH is incompatible with client-side middlebox compatibility - not enabling middlebox compatibility for JNI])
|
||||
else
|
||||
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_TLS13_MIDDLEBOX_COMPAT"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -8493,9 +8493,8 @@ static void InitSSL_Tls13Options(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
|
|||
ssl->numGroups = ctx->numGroups;
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
||||
ssl->options.tls13MiddleBoxCompat = 1;
|
||||
#endif
|
||||
/* Server clears this when the ClientHello legacy_session_id is empty. */
|
||||
ssl->options.tls13MiddleBoxCompat = 1;
|
||||
}
|
||||
#endif /* WOLFSSL_TLS13 */
|
||||
|
||||
|
|
@ -26325,8 +26324,12 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* The TLS 1.3 server accept path calls this for RFC 8446 Appendix D.4, so a
|
||||
* server needs it whenever TLS 1.3 is built, not only with middlebox compat.
|
||||
* A TLS 1.3 client only sends one in a middlebox compat build. */
|
||||
#if !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS) || \
|
||||
(defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT))
|
||||
(defined(WOLFSSL_TLS13) && (!defined(NO_WOLFSSL_SERVER) || \
|
||||
defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)))
|
||||
int SendChangeCipher(WOLFSSL* ssl)
|
||||
{
|
||||
byte *output;
|
||||
|
|
@ -26337,13 +26340,17 @@ int SendChangeCipher(WOLFSSL* ssl)
|
|||
#ifdef OPENSSL_EXTRA
|
||||
ssl->cbmode = WOLFSSL_CB_MODE_WRITE;
|
||||
if (ssl->options.side == WOLFSSL_SERVER_END){
|
||||
ssl->options.serverState = SERVER_CHANGECIPHERSPEC_COMPLETE;
|
||||
/* A TLS 1.3 record here is a dummy and moves no state. */
|
||||
if (!IsAtLeastTLSv1_3(ssl->version))
|
||||
ssl->options.serverState = SERVER_CHANGECIPHERSPEC_COMPLETE;
|
||||
if (ssl->CBIS != NULL)
|
||||
ssl->CBIS(ssl, WOLFSSL_CB_ACCEPT_LOOP, WOLFSSL_SUCCESS);
|
||||
}
|
||||
else {
|
||||
ssl->options.clientState =
|
||||
CLIENT_CHANGECIPHERSPEC_COMPLETE;
|
||||
/* As above; nothing on the TLS 1.3 connect path reads clientState. */
|
||||
if (!IsAtLeastTLSv1_3(ssl->version)) {
|
||||
ssl->options.clientState = CLIENT_CHANGECIPHERSPEC_COMPLETE;
|
||||
}
|
||||
if (ssl->CBIS != NULL)
|
||||
ssl->CBIS(ssl, WOLFSSL_CB_CONNECT_LOOP, WOLFSSL_SUCCESS);
|
||||
}
|
||||
|
|
@ -26443,7 +26450,7 @@ int SendChangeCipher(WOLFSSL* ssl)
|
|||
return SendBuffered(ssl);
|
||||
}
|
||||
#endif /* !WOLFSSL_NO_TLS12 || !NO_OLD_TLS ||
|
||||
* (WOLFSSL_TLS13 && WOLFSSL_TLS13_MIDDLEBOX_COMPAT) */
|
||||
* (WOLFSSL_TLS13 && (!NO_WOLFSSL_SERVER || WOLFSSL_TLS13_MIDDLEBOX_COMPAT)) */
|
||||
|
||||
|
||||
#if !defined(NO_OLD_TLS) && !defined(WOLFSSL_AEAD_ONLY)
|
||||
|
|
|
|||
|
|
@ -5701,6 +5701,10 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out,
|
|||
ssl->options.hrrSentCookie = 0;
|
||||
#endif
|
||||
ssl->options.hrrSentKeyShare = 0;
|
||||
ssl->options.sentChangeCipher = 0;
|
||||
/* Matches InitSSL_Tls13Options(); the server clears it again when the
|
||||
* next ClientHello carries an empty session id. */
|
||||
ssl->options.tls13MiddleBoxCompat = 1;
|
||||
#endif
|
||||
#ifdef WOLFSSL_DTLS
|
||||
ssl->options.dtlsStateful = 0;
|
||||
|
|
|
|||
109
src/tls13.c
109
src/tls13.c
|
|
@ -33,8 +33,13 @@
|
|||
* WOLFSSL_DTLS_CH_FRAG: Enable DTLS 1.3 ClientHello frag default: off
|
||||
*
|
||||
* Handshake:
|
||||
* WOLFSSL_TLS13_MIDDLEBOX_COMPAT: Enable middlebox compatibility default: on
|
||||
* Sends ChangeCipherSpec and includes session id
|
||||
* WOLFSSL_TLS13_MIDDLEBOX_COMPAT: Client-side middlebox compatibility
|
||||
* default: off
|
||||
* Makes the client send a fake session id and its
|
||||
* own ChangeCipherSpec. The server always answers
|
||||
* a non-empty client session id with a
|
||||
* ChangeCipherSpec, as RFC 8446 Appendix D.4
|
||||
* requires, whether or not this is defined.
|
||||
* WOLFSSL_SEND_HRR_COOKIE: Send cookie in HelloRetryRequest default: off
|
||||
* for stateless ClientHello tracking. A client
|
||||
* always echoes back a cookie it is sent.
|
||||
|
|
@ -8071,12 +8076,17 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
|
|||
}
|
||||
args->idx += sessIdSz;
|
||||
|
||||
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
||||
/* RFC 8446 Appendix D.4: server MUST only send CCS if the client's
|
||||
* ClientHello contains a non-empty legacy_session_id. */
|
||||
* ClientHello contains a non-empty legacy_session_id. An ECH inner hello
|
||||
* is rebuilt with the outer session id, so it decides either way. */
|
||||
if (sessIdSz == 0) {
|
||||
ssl->options.tls13MiddleBoxCompat = 0;
|
||||
}
|
||||
#ifdef WOLFSSL_QUIC
|
||||
/* RFC 9001 Section 8.4: QUIC has no compatibility mode to be had. */
|
||||
if (WOLFSSL_IS_QUIC(ssl)) {
|
||||
ssl->options.tls13MiddleBoxCompat = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSL_DTLS13
|
||||
|
|
@ -16409,13 +16419,20 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
|
|||
if (ssl->earlyData != no_early_data &&
|
||||
ssl->options.handShakeState != CLIENT_HELLO_COMPLETE) {
|
||||
#if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
||||
if (!ssl->options.dtls &&
|
||||
ssl->options.tls13MiddleBoxCompat) {
|
||||
if ((ssl->error = SendChangeCipher(ssl)) != 0) {
|
||||
if (!ssl->options.dtls && !ssl->options.sentChangeCipher
|
||||
&& ssl->options.tls13MiddleBoxCompat) {
|
||||
ssl->error = SendChangeCipher(ssl);
|
||||
/* A short send leaves the record queued in the output
|
||||
* buffer, so a resumed connect must not build a second
|
||||
* one. Same on every other site. */
|
||||
if (ssl->error == 0 ||
|
||||
ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) {
|
||||
ssl->options.sentChangeCipher = 1;
|
||||
}
|
||||
if (ssl->error != 0) {
|
||||
WOLFSSL_ERROR(ssl->error);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
ssl->options.sentChangeCipher = 1;
|
||||
}
|
||||
#endif
|
||||
ssl->options.handShakeState = CLIENT_HELLO_COMPLETE;
|
||||
|
|
@ -16461,11 +16478,19 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
|
|||
#if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
||||
if (!ssl->options.dtls && !ssl->options.sentChangeCipher
|
||||
&& ssl->options.tls13MiddleBoxCompat) {
|
||||
if ((ssl->error = SendChangeCipher(ssl)) != 0) {
|
||||
ssl->error = SendChangeCipher(ssl);
|
||||
if (ssl->error == 0 ||
|
||||
ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) {
|
||||
ssl->options.sentChangeCipher = 1;
|
||||
}
|
||||
if (ssl->error != 0) {
|
||||
/* The second ClientHello still has to follow, so hold
|
||||
* the state machine on this case while the record
|
||||
* drains. */
|
||||
ssl->options.buildingMsg = 1;
|
||||
WOLFSSL_ERROR(ssl->error);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
ssl->options.sentChangeCipher = 1;
|
||||
}
|
||||
#endif
|
||||
/* Try again with different security parameters. */
|
||||
|
|
@ -16542,11 +16567,15 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
|
|||
#if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
||||
if (!ssl->options.sentChangeCipher && !ssl->options.dtls
|
||||
&& ssl->options.tls13MiddleBoxCompat) {
|
||||
if ((ssl->error = SendChangeCipher(ssl)) != 0) {
|
||||
ssl->error = SendChangeCipher(ssl);
|
||||
if (ssl->error == 0 ||
|
||||
ssl->error == WC_NO_ERR_TRACE(WANT_WRITE)) {
|
||||
ssl->options.sentChangeCipher = 1;
|
||||
}
|
||||
if (ssl->error != 0) {
|
||||
WOLFSSL_ERROR(ssl->error);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
ssl->options.sentChangeCipher = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -17551,6 +17580,39 @@ const char* wolfSSL_get_cipher_name_by_hash(WOLFSSL* ssl, const char* hash)
|
|||
|
||||
#ifndef NO_WOLFSSL_SERVER
|
||||
|
||||
/* Send the RFC 8446 Appendix D.4 ChangeCipherSpec a server owes a client that
|
||||
* offered a non-empty legacy_session_id.
|
||||
*
|
||||
* ssl The SSL/TLS object.
|
||||
* flush Force the record out on its own. A HelloRetryRequest needs this
|
||||
* because SendTls13ServerHello has already sent it, unlike a
|
||||
* ServerHello, which waits for the rest of its flight.
|
||||
* returns 0 on success.
|
||||
*/
|
||||
static int SendTls13ServerChangeCipher(WOLFSSL* ssl, int flush)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* DoTls13ClientHello clears tls13MiddleBoxCompat for a QUIC peer, so the
|
||||
* check here is a local backstop, 0 when QUIC is not built. */
|
||||
if (ssl->options.dtls || WOLFSSL_IS_QUIC(ssl)
|
||||
|| !ssl->options.tls13MiddleBoxCompat
|
||||
|| ssl->options.sentChangeCipher) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = SendChangeCipher(ssl);
|
||||
/* A short send leaves the record queued in the output buffer. Mark it sent
|
||||
* anyway, or the resumed accept, which comes back in at the ServerHello
|
||||
* case, puts a second record on the wire. */
|
||||
if (ret == 0 || ret == WC_NO_ERR_TRACE(WANT_WRITE))
|
||||
ssl->options.sentChangeCipher = 1;
|
||||
if (ret == 0 && flush && ssl->options.groupMessages)
|
||||
ret = SendBuffered(ssl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The server accepting a connection from a client.
|
||||
* The protocol version is expecting to be TLS v1.3.
|
||||
* If the client downgrades, and older versions of the protocol are compiled
|
||||
|
|
@ -17792,18 +17854,14 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
|
|||
FALL_THROUGH;
|
||||
|
||||
case TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE :
|
||||
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
|
||||
if (!ssl->options.dtls && ssl->options.tls13MiddleBoxCompat
|
||||
&& ssl->options.serverState ==
|
||||
if (ssl->options.serverState ==
|
||||
SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
|
||||
if ((ssl->error = SendChangeCipher(ssl)) != 0) {
|
||||
ssl->error = SendTls13ServerChangeCipher(ssl, 1);
|
||||
if (ssl->error != 0) {
|
||||
WOLFSSL_ERROR(ssl->error);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
ssl->options.sentChangeCipher = 1;
|
||||
ssl->options.serverState = SERVER_HELLO_RETRY_REQUEST_COMPLETE;
|
||||
}
|
||||
#endif
|
||||
ssl->options.acceptState = TLS13_ACCEPT_FIRST_REPLY_DONE;
|
||||
WOLFSSL_MSG("accept state ACCEPT_FIRST_REPLY_DONE");
|
||||
FALL_THROUGH;
|
||||
|
|
@ -17851,16 +17909,11 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
|
|||
FALL_THROUGH;
|
||||
|
||||
case TLS13_SERVER_HELLO_SENT :
|
||||
#if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
||||
if (!ssl->options.dtls && ssl->options.tls13MiddleBoxCompat
|
||||
&& !ssl->options.sentChangeCipher && !ssl->options.dtls) {
|
||||
if ((ssl->error = SendChangeCipher(ssl)) != 0) {
|
||||
WOLFSSL_ERROR(ssl->error);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
ssl->options.sentChangeCipher = 1;
|
||||
ssl->error = SendTls13ServerChangeCipher(ssl, 0);
|
||||
if (ssl->error != 0) {
|
||||
WOLFSSL_ERROR(ssl->error);
|
||||
return WOLFSSL_FATAL_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
ssl->options.acceptState = TLS13_ACCEPT_THIRD_REPLY_DONE;
|
||||
WOLFSSL_MSG("accept state ACCEPT_THIRD_REPLY_DONE");
|
||||
|
|
|
|||
|
|
@ -7054,20 +7054,41 @@ static int Tls13PTARecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(WOLFSSL_TLS13) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
/* How many ChangeCipherSpec records does this buffer of TLS records carry?
|
||||
* A count rather than a yes or no, so that a second record going out, which
|
||||
* is what marking the record sent before the send guards against, does not
|
||||
* read the same as the one record that is owed. */
|
||||
static int test_tls13_count_ccs(const byte* buff, int len)
|
||||
{
|
||||
int i = 0;
|
||||
int count = 0;
|
||||
|
||||
while (i + RECORD_HEADER_SZ <= len) {
|
||||
if (buff[i] == change_cipher_spec)
|
||||
count++;
|
||||
i += RECORD_HEADER_SZ + ((buff[i + 3] << 8) | buff[i + 4]);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Test that when a TLS 1.3 client sends a ClientHello with an empty
|
||||
* legacy_session_id (indicating no middlebox compatibility), the server
|
||||
* should NOT send a ChangeCipherSpec message. Per RFC 8446 Appendix D.4,
|
||||
* the server only sends CCS if the client's ClientHello contains a
|
||||
* non-empty session_id.
|
||||
*
|
||||
* This test reproduces the bug reported in GitHub issue #9156 where
|
||||
* wolfSSL server always sends CCS when compiled with
|
||||
* WOLFSSL_TLS13_MIDDLEBOX_COMPAT, regardless of the client's session_id.
|
||||
* This test reproduces the bug reported in GitHub issue #9156, where the
|
||||
* server sent a ChangeCipherSpec whatever the client's session_id was.
|
||||
*/
|
||||
int test_tls13_middlebox_compat_empty_session_id(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) && \
|
||||
#if defined(WOLFSSL_TLS13) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
|
|
@ -7075,16 +7096,15 @@ int test_tls13_middlebox_compat_empty_session_id(void)
|
|||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
int i;
|
||||
int found_ccs = 0;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
|
||||
/* Disable middlebox compatibility on the client so it sends an empty
|
||||
* legacy_session_id in ClientHello. The server should respect this and
|
||||
* NOT send a ChangeCipherSpec. */
|
||||
/* Without WOLFSSL_TLS13_MIDDLEBOX_COMPAT the client already sends an
|
||||
* empty legacy_session_id; clearing the flag covers the build where it
|
||||
* would otherwise send a fake one. Either way the server must respect
|
||||
* the empty id and NOT send a ChangeCipherSpec. */
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ssl_c->options.tls13MiddleBoxCompat = 0;
|
||||
}
|
||||
|
|
@ -7101,30 +7121,10 @@ int test_tls13_middlebox_compat_empty_session_id(void)
|
|||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* Now examine the server's output (stored in c_buff, since the server
|
||||
* writes to the client's read buffer). Scan through TLS records looking
|
||||
* for a ChangeCipherSpec record (content type 0x14 = 20). */
|
||||
if (EXPECT_SUCCESS()) {
|
||||
i = 0;
|
||||
while (i + 5 <= test_ctx.c_len) {
|
||||
byte content_type = test_ctx.c_buff[i];
|
||||
int record_len = (test_ctx.c_buff[i + 3] << 8) |
|
||||
test_ctx.c_buff[i + 4];
|
||||
|
||||
if (content_type == 20) { /* change_cipher_spec */
|
||||
found_ccs = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Move to next TLS record: 5 byte header + payload */
|
||||
i += 5 + record_len;
|
||||
}
|
||||
}
|
||||
|
||||
/* The server should NOT have sent CCS since the client's ClientHello
|
||||
* had an empty legacy_session_id. If found_ccs is 1, this demonstrates
|
||||
/* The server writes into c_buff. It must NOT have sent a CCS, since the
|
||||
* client's ClientHello had an empty legacy_session_id. Sending one is
|
||||
* the bug from issue #9156. */
|
||||
ExpectIntEQ(found_ccs, 0);
|
||||
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 0);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
|
|
@ -7177,6 +7177,557 @@ int test_tls13_middlebox_compat_session_id(void)
|
|||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_tls13_middlebox_compat_server_ccs(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TLS13) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
|
||||
/* Give the client a non-empty legacy_session_id. GetTls13SessionId puts
|
||||
* it on the wire whatever the build options are, which is what drives
|
||||
* the server behaviour RFC 8446 Appendix D.4 requires. */
|
||||
if (EXPECT_SUCCESS()) {
|
||||
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
||||
ssl_c->session->sessionIDSz = ID_LEN;
|
||||
}
|
||||
|
||||
/* Client sends ClientHello with a non-empty session id. */
|
||||
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* Server processes it and writes its first flight. */
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* The client offered a non-empty session id, so the server owes it a
|
||||
* ChangeCipherSpec. Peers such as Erlang's ssl abort the handshake with
|
||||
* an unexpected_message alert when it is missing. */
|
||||
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* wolfSSL_clear() must not leave the previous handshake's ChangeCipherSpec
|
||||
* state behind, or a reused server object stops answering a non-empty
|
||||
* legacy_session_id after the first handshake. */
|
||||
int test_tls13_middlebox_compat_server_reuse(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TLS13) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_c2 = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
||||
ssl_c->session->sessionIDSz = ID_LEN;
|
||||
}
|
||||
|
||||
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
||||
|
||||
/* Reuse the server object for a second handshake with a fresh client. */
|
||||
ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS);
|
||||
test_memio_clear_buffer(&test_ctx, 0);
|
||||
test_memio_clear_buffer(&test_ctx, 1);
|
||||
|
||||
ExpectNotNull(ssl_c2 = wolfSSL_new(ctx_c));
|
||||
if (EXPECT_SUCCESS()) {
|
||||
wolfSSL_SetIOWriteCtx(ssl_c2, &test_ctx);
|
||||
wolfSSL_SetIOReadCtx(ssl_c2, &test_ctx);
|
||||
XMEMSET(ssl_c2->session->sessionID, 0x5A, ID_LEN);
|
||||
ssl_c2->session->sessionIDSz = ID_LEN;
|
||||
}
|
||||
|
||||
ExpectIntNE(wolfSSL_connect(ssl_c2), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c2,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* The second peer also offered a session id, so it is owed a CCS too. */
|
||||
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_c2);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(WOLFSSL_TLS13) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
/* One HelloRetryRequest handshake, with the server's messages grouped or
|
||||
* not. Its own verdict, so a failure in one setting still leaves the other
|
||||
* exercised. */
|
||||
static int test_tls13_hrr_ccs_grouped(int grouped)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
||||
ssl_c->session->sessionIDSz = ID_LEN;
|
||||
ssl_s->options.groupMessages = (byte)grouped;
|
||||
}
|
||||
|
||||
/* No key share entries, so the server has to ask for one. */
|
||||
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
||||
|
||||
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* The HelloRetryRequest flight carries exactly one, however the
|
||||
* server groups its messages. */
|
||||
ExpectIntEQ(ssl_s->options.serverState,
|
||||
SERVER_HELLO_RETRY_REQUEST_COMPLETE);
|
||||
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
||||
|
||||
/* Step to the second ClientHello and the flight answering it. The
|
||||
* client drains c_buff as it reads, so what is left is that flight
|
||||
* alone: the server owes no second record and must not send one. */
|
||||
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 0);
|
||||
|
||||
/* And the handshake the retry started still completes. */
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* A HelloRetryRequest is flushed as soon as it is built, so the
|
||||
* ChangeCipherSpec that follows it has to be flushed too. With grouped
|
||||
* messages, which every OPENSSL_COMPATIBLE_DEFAULTS build turns on, it would
|
||||
* otherwise sit in the output buffer and reach the peer a flight late. */
|
||||
int test_tls13_middlebox_compat_hrr_ccs(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TLS13) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
int ungrouped_res;
|
||||
int grouped_res;
|
||||
|
||||
/* Both run whatever the other does; both owe the same record. */
|
||||
ungrouped_res = test_tls13_hrr_ccs_grouped(0);
|
||||
grouped_res = test_tls13_hrr_ccs_grouped(1);
|
||||
ExpectIntEQ(ungrouped_res, TEST_SUCCESS);
|
||||
ExpectIntEQ(grouped_res, TEST_SUCCESS);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(WOLFSSL_TLS13) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
/* Refuses one ChangeCipherSpec write, then counts the ones that land. */
|
||||
struct test_tls13_ccs_ctx {
|
||||
struct test_memio_ctx* memio;
|
||||
int refuse;
|
||||
int sent;
|
||||
};
|
||||
|
||||
/* Refuse the write carrying the ChangeCipherSpec, once, and let every other
|
||||
* write through. Refusing by record type rather than by write count keeps
|
||||
* the test tied to the record it is about. */
|
||||
static int test_tls13_ccs_write_cb(WOLFSSL* ssl, char* data, int sz, void* ctx)
|
||||
{
|
||||
struct test_tls13_ccs_ctx* cctx = (struct test_tls13_ccs_ctx*)ctx;
|
||||
int ret;
|
||||
|
||||
if (sz > 0 && (byte)data[0] == change_cipher_spec && cctx->refuse) {
|
||||
cctx->refuse = 0;
|
||||
return WOLFSSL_CBIO_ERR_WANT_WRITE;
|
||||
}
|
||||
|
||||
ret = test_memio_write_cb(ssl, data, sz, cctx->memio);
|
||||
if (ret > 0 && sz > 0 && (byte)data[0] == change_cipher_spec)
|
||||
cctx->sent++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* A server send that stops short must not put a second ChangeCipherSpec on
|
||||
* the wire when it is resumed. Drive that path with a transport that refuses
|
||||
* that one write. The HelloRetryRequest flavour below is the one that pins
|
||||
* where sentChangeCipher is set; this one covers the straight-through
|
||||
* flight, which the accept-state prologue recovers on its own. */
|
||||
int test_tls13_middlebox_compat_server_ccs_retry(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TLS13) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
struct test_tls13_ccs_ctx ccs_ctx;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
XMEMSET(&ccs_ctx, 0, sizeof(ccs_ctx));
|
||||
ccs_ctx.memio = &test_ctx;
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
||||
ssl_c->session->sessionIDSz = ID_LEN;
|
||||
/* Ungrouped, so the record is flushed on its own and the refusal
|
||||
* below lands on it rather than on a flight carrying it along, and
|
||||
* without the automatic retry an OPENSSL_COMPATIBLE_DEFAULTS build
|
||||
* turns on, which would resume inside the send instead of through
|
||||
* the accept state machine this test is about. */
|
||||
ssl_s->options.groupMessages = 0;
|
||||
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
|
||||
defined(HAVE_MEMCACHED)
|
||||
/* Only a build with the mode API can have turned auto retry on, and
|
||||
* an OPENSSL_COMPATIBLE_DEFAULTS one has. Resuming the send has to
|
||||
* come back through the accept state machine, not inside it. */
|
||||
wolfSSL_CTX_clear_mode(ctx_s, WOLFSSL_MODE_AUTO_RETRY);
|
||||
#endif
|
||||
wolfSSL_SSLSetIOSend(ssl_s, test_tls13_ccs_write_cb);
|
||||
wolfSSL_SetIOWriteCtx(ssl_s, &ccs_ctx);
|
||||
ccs_ctx.refuse = 1;
|
||||
}
|
||||
|
||||
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* The ChangeCipherSpec write is refused, so the flight stops on it. */
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_WRITE);
|
||||
ExpectIntEQ(ccs_ctx.refuse, 0);
|
||||
|
||||
/* Resume the same handshake, now that the write goes through. */
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* One record, not two: the resumed accept must not repeat the send. */
|
||||
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
||||
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* The HelloRetryRequest path marks the record sent before sending it,
|
||||
* because a resumed accept falls through into TLS13_SERVER_HELLO_SENT and
|
||||
* only that flag stops it sending a second one. Refuse the write, resume,
|
||||
* and check the flight answering the second ClientHello stays empty. */
|
||||
int test_tls13_middlebox_compat_hrr_ccs_retry(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TLS13) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
struct test_tls13_ccs_ctx ccs_ctx;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
XMEMSET(&ccs_ctx, 0, sizeof(ccs_ctx));
|
||||
ccs_ctx.memio = &test_ctx;
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
XMEMSET(ssl_c->session->sessionID, 0xA5, ID_LEN);
|
||||
ssl_c->session->sessionIDSz = ID_LEN;
|
||||
ssl_s->options.groupMessages = 0;
|
||||
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
|
||||
defined(HAVE_MEMCACHED)
|
||||
/* Only a build with the mode API can have turned auto retry on, and
|
||||
* an OPENSSL_COMPATIBLE_DEFAULTS one has. Resuming the send has to
|
||||
* come back through the accept state machine, not inside it. */
|
||||
wolfSSL_CTX_clear_mode(ctx_s, WOLFSSL_MODE_AUTO_RETRY);
|
||||
#endif
|
||||
wolfSSL_SSLSetIOSend(ssl_s, test_tls13_ccs_write_cb);
|
||||
wolfSSL_SetIOWriteCtx(ssl_s, &ccs_ctx);
|
||||
ccs_ctx.refuse = 1;
|
||||
}
|
||||
|
||||
/* No key share entries, so the server has to ask for one. */
|
||||
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
||||
|
||||
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
|
||||
/* The record behind the HelloRetryRequest is refused. */
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_WRITE);
|
||||
ExpectIntEQ(ccs_ctx.refuse, 0);
|
||||
|
||||
/* Resume: the retry owes exactly the one record it stopped on. */
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 1);
|
||||
|
||||
/* The flight answering the second ClientHello owes none. A resumed
|
||||
* accept falls into TLS13_SERVER_HELLO_SENT, so this is what catches
|
||||
* the record being marked sent after the send rather than before. */
|
||||
ExpectIntNE(wolfSSL_connect(ssl_c), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectIntNE(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectIntEQ(test_tls13_count_ccs(test_ctx.c_buff, test_ctx.c_len), 0);
|
||||
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
/* One client handshake with its ChangeCipherSpec write refused once, either
|
||||
* straight through or after a HelloRetryRequest. */
|
||||
static int test_tls13_client_ccs_retry_once(int noKeyShares)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
struct test_tls13_ccs_ctx ccs_ctx;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
XMEMSET(&ccs_ctx, 0, sizeof(ccs_ctx));
|
||||
ccs_ctx.memio = &test_ctx;
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
wolfSSL_SSLSetIOSend(ssl_c, test_tls13_ccs_write_cb);
|
||||
wolfSSL_SetIOWriteCtx(ssl_c, &ccs_ctx);
|
||||
/* Refuse the client's first ChangeCipherSpec write, wherever in the
|
||||
* flight it falls. */
|
||||
ccs_ctx.refuse = 1;
|
||||
}
|
||||
if (noKeyShares) {
|
||||
ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS);
|
||||
}
|
||||
|
||||
/* A refused record answering a HelloRetryRequest must not advance
|
||||
* connectState past the second ClientHello. */
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
ExpectIntEQ(ccs_ctx.sent, 1);
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The client marks its own middlebox ChangeCipherSpec sent before sending it,
|
||||
* for the same reason the server does. Both flights get their own verdict. */
|
||||
int test_tls13_middlebox_compat_client_ccs_retry(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
int hrr_res;
|
||||
int direct_res;
|
||||
|
||||
hrr_res = test_tls13_client_ccs_retry_once(1);
|
||||
direct_res = test_tls13_client_ccs_retry_once(0);
|
||||
ExpectIntEQ(hrr_res, TEST_SUCCESS);
|
||||
ExpectIntEQ(direct_res, TEST_SUCCESS);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* The third client send site is the early data one, which puts the
|
||||
* ChangeCipherSpec out before the 0-RTT records rather than as part of a
|
||||
* handshake flight. Refuse that one write and resume: the record is owed
|
||||
* once, and a resumed wolfSSL_write_early_data() must not repeat it. */
|
||||
int test_tls13_middlebox_compat_client_ccs_retry_early_data(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) && \
|
||||
defined(WOLFSSL_EARLY_DATA) && defined(HAVE_SESSION_TICKET) && \
|
||||
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
||||
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
||||
WOLFSSL_CTX *ctx_c = NULL;
|
||||
WOLFSSL_CTX *ctx_s = NULL;
|
||||
WOLFSSL *ssl_c = NULL;
|
||||
WOLFSSL *ssl_s = NULL;
|
||||
WOLFSSL_SESSION *sess = NULL;
|
||||
struct test_memio_ctx test_ctx;
|
||||
struct test_tls13_ccs_ctx ccs_ctx;
|
||||
const char earlyMsg[] = "This is early data";
|
||||
char buf[64];
|
||||
int written = 0;
|
||||
int earlyRead = 0;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
XMEMSET(&ccs_ctx, 0, sizeof(ccs_ctx));
|
||||
ccs_ctx.memio = &test_ctx;
|
||||
|
||||
/* A full handshake first, only to get a ticket to resume with; early
|
||||
* data has nowhere else to come from. */
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
|
||||
/* Opt the server into 0-RTT (off by default per RFC 8446 E.5). */
|
||||
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
|
||||
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
||||
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
/* Read so that the NewSessionTicket is taken in. */
|
||||
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
||||
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
||||
|
||||
wolfSSL_free(ssl_c);
|
||||
ssl_c = NULL;
|
||||
wolfSSL_free(ssl_s);
|
||||
ssl_s = NULL;
|
||||
|
||||
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
||||
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
||||
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
||||
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
|
||||
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
/* Ungrouped, so the record is flushed on its own and the refusal
|
||||
* below lands on it rather than on a flight carrying it along. */
|
||||
ssl_c->options.groupMessages = 0;
|
||||
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
|
||||
defined(HAVE_MEMCACHED)
|
||||
/* Only a build with the mode API can have turned auto retry on, and
|
||||
* an OPENSSL_COMPATIBLE_DEFAULTS one has. Resuming the send has to
|
||||
* come back through the connect state machine, not inside it. */
|
||||
wolfSSL_CTX_clear_mode(ctx_c, WOLFSSL_MODE_AUTO_RETRY);
|
||||
#endif
|
||||
wolfSSL_SSLSetIOSend(ssl_c, test_tls13_ccs_write_cb);
|
||||
wolfSSL_SetIOWriteCtx(ssl_c, &ccs_ctx);
|
||||
ccs_ctx.refuse = 1;
|
||||
}
|
||||
|
||||
/* The ChangeCipherSpec ahead of the early data is refused, so the
|
||||
* client stops on it with nothing written. */
|
||||
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, earlyMsg, (int)sizeof(earlyMsg),
|
||||
&written), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WOLFSSL_ERROR_WANT_WRITE);
|
||||
ExpectIntEQ(ccs_ctx.refuse, 0);
|
||||
ExpectIntEQ(written, 0);
|
||||
|
||||
/* Resume: the record it stopped on goes out, and only that one. */
|
||||
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, earlyMsg, (int)sizeof(earlyMsg),
|
||||
&written), (int)sizeof(earlyMsg));
|
||||
ExpectIntEQ(written, (int)sizeof(earlyMsg));
|
||||
ExpectIntEQ(ccs_ctx.sent, 1);
|
||||
|
||||
/* The 0-RTT data the retry carried still arrives, and the handshake it
|
||||
* started still completes. */
|
||||
XMEMSET(buf, 0, sizeof(buf));
|
||||
(void)test_tls13_early_data_read_until_write_ok(ssl_s, buf, sizeof(buf),
|
||||
&earlyRead);
|
||||
ExpectIntEQ(earlyRead, (int)sizeof(earlyMsg));
|
||||
ExpectStrEQ(earlyMsg, buf);
|
||||
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
||||
ExpectTrue(wolfSSL_session_reused(ssl_c));
|
||||
|
||||
/* Still the one record, over the whole handshake. */
|
||||
ExpectIntEQ(ccs_ctx.sent, 1);
|
||||
|
||||
wolfSSL_SESSION_free(sess);
|
||||
wolfSSL_free(ssl_c);
|
||||
wolfSSL_free(ssl_s);
|
||||
wolfSSL_CTX_free(ctx_c);
|
||||
wolfSSL_CTX_free(ctx_s);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_tls13_plaintext_alert(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,13 @@ int test_tls13_duplicate_ech_extension(void);
|
|||
int test_key_share_mismatch(void);
|
||||
int test_tls13_middlebox_compat_empty_session_id(void);
|
||||
int test_tls13_middlebox_compat_session_id(void);
|
||||
int test_tls13_middlebox_compat_server_ccs(void);
|
||||
int test_tls13_middlebox_compat_server_reuse(void);
|
||||
int test_tls13_middlebox_compat_hrr_ccs(void);
|
||||
int test_tls13_middlebox_compat_server_ccs_retry(void);
|
||||
int test_tls13_middlebox_compat_hrr_ccs_retry(void);
|
||||
int test_tls13_middlebox_compat_client_ccs_retry(void);
|
||||
int test_tls13_middlebox_compat_client_ccs_retry_early_data(void);
|
||||
int test_tls13_plaintext_alert(void);
|
||||
int test_tls13_warning_alert_is_fatal(void);
|
||||
int test_tls13_unknown_ext_rejected(void);
|
||||
|
|
@ -170,6 +177,14 @@ int test_tls13_cryptocb_async(void);
|
|||
TEST_DECL_GROUP("tls13", test_key_share_mismatch), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_empty_session_id), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_session_id), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_server_ccs), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_server_reuse), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_hrr_ccs), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_server_ccs_retry), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_hrr_ccs_retry), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_client_ccs_retry), \
|
||||
TEST_DECL_GROUP("tls13", \
|
||||
test_tls13_middlebox_compat_client_ccs_retry_early_data), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_plaintext_alert), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_warning_alert_is_fatal), \
|
||||
TEST_DECL_GROUP("tls13", test_tls13_cert_req_sigalgs), \
|
||||
|
|
|
|||
|
|
@ -5576,7 +5576,7 @@ struct Options {
|
|||
#ifdef WOLFSSL_ALT_CERT_CHAINS
|
||||
word16 usingAltCertChain:1;/* Alternate cert chain was used */
|
||||
#endif
|
||||
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
|
||||
#ifdef WOLFSSL_TLS13
|
||||
word16 sentChangeCipher:1; /* Change Cipher Spec sent */
|
||||
#endif
|
||||
#if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
|
||||
|
|
|
|||
Loading…
Reference in New Issue