diff --git a/CMakeLists.txt b/CMakeLists.txt
index aac2062d2..28312f8cb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -266,6 +266,26 @@ if("${FIPS_VERSION}" STREQUAL "v1")
override_cache(WOLFSSL_TLS13 "no")
endif()
+# DTLS v1.3
+add_option("WOLFSSL_DTLS13"
+ "Enable wolfSSL DTLS v1.3 (default: disabled)"
+ "no" "yes;no")
+
+if(WOLFSSL_DTLS13)
+ if (NOT WOLFSSL_DTLS)
+ message(FATAL_ERROR "DTLS13 requires DTLS")
+ endif()
+ if (NOT WOLFSSL_TLS13)
+ message(FATAL_ERROR "DTLS13 requires TLS13")
+ endif()
+ list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_DTLS13")
+ list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_W64_WRAPPER")
+
+ if (WOLFSSL_AES)
+ list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_AES_DIRECT")
+ endif()
+endif()
+
# Post-handshake authentication
add_option("WOLFSSL_POSTAUTH"
"Enable wolfSSL Post-handshake Authentication (default: disabled)"
@@ -1870,6 +1890,7 @@ if(WOLFSSL_EXAMPLES)
tests/hash.c
tests/srp.c
tests/suites.c
+ tests/w64wrapper.c
tests/unit.c
examples/server/server.c
examples/client/client.c)
diff --git a/IDE/WIN/wolfssl-fips.vcxproj b/IDE/WIN/wolfssl-fips.vcxproj
index f7a23dd18..1de003294 100644
--- a/IDE/WIN/wolfssl-fips.vcxproj
+++ b/IDE/WIN/wolfssl-fips.vcxproj
@@ -307,6 +307,8 @@
+
+
diff --git a/IDE/WIN10/wolfssl-fips.vcxproj b/IDE/WIN10/wolfssl-fips.vcxproj
index c88fea693..7f7f2adaa 100644
--- a/IDE/WIN10/wolfssl-fips.vcxproj
+++ b/IDE/WIN10/wolfssl-fips.vcxproj
@@ -278,6 +278,7 @@
+
diff --git a/cmake/functions.cmake b/cmake/functions.cmake
index 3e63810a1..3c839ce37 100644
--- a/cmake/functions.cmake
+++ b/cmake/functions.cmake
@@ -41,6 +41,9 @@ function(generate_build_flags)
if(WOLFSSL_TLS13 OR WOLFSSL_USER_SETTINGS)
set(BUILD_TLS13 "yes" PARENT_SCOPE)
endif()
+ if(WOLFSSL_DTLS13 OR WOLFSSL_USER_SETTINGS)
+ set(BUILD_DTLS13 "yes" PARENT_SCOPE)
+ endif()
if(WOLFSSL_RNG OR WOLFSSL_USER_SETTINGS)
set(BUILD_RNG "yes" PARENT_SCOPE)
endif()
@@ -812,6 +815,10 @@ function(generate_lib_src_list LIB_SOURCES)
list(APPEND LIB_SOURCES src/tls13.c)
endif()
+ if(BUILD_DTLS13)
+ list(APPEND LIB_SOURCES src/dtls13.c)
+ endif()
+
if(BUILD_OCSP)
list(APPEND LIB_SOURCES src/ocsp.c)
endif()
diff --git a/configure.ac b/configure.ac
index 8a6657e86..5b9645b26 100644
--- a/configure.ac
+++ b/configure.ac
@@ -876,7 +876,6 @@ then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DTLS_MTU"
fi
-
# TLS v1.3 Draft 18 (Note: only final TLS v1.3 supported, here for backwards build compatibility)
AC_ARG_ENABLE([tls13-draft18],
[AS_HELP_STRING([--enable-tls13-draft18],[Enable wolfSSL TLS v1.3 Draft 18 (default: disabled)])],
@@ -3513,6 +3512,24 @@ else
fi
fi
+# DTLSv1.3
+AC_ARG_ENABLE([dtls13],
+ [AS_HELP_STRING([--enable-dtls13],[Enable wolfSSL DTLS v1.3 (default: disabled)])],
+ [ ENABLED_DTLS13=$enableval ],
+ [ ENABLED_DTLS13=no ]
+ )
+if test "x$ENABLED_DTLS13" = "xyes"
+then
+ if test "x$ENABLED_DTLS" != "xyes" || test "x$ENABLED_TLS13" != "xyes"
+ then
+ AC_MSG_ERROR([You need to enable both DTLS and TLSv1.3 to use DTLSv1.3])
+ fi
+ AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DTLS13 -DWOLFSSL_W64_WRAPPER"
+ if test "x$ENABLED_AES" = "xyes"
+ then
+ AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_AES_DIRECT"
+ fi
+fi
# CODING
AC_ARG_ENABLE([coding],
@@ -7850,6 +7867,7 @@ AM_CONDITIONAL([BUILD_HMAC],[test "x$ENABLED_HMAC" = "xyes"])
AM_CONDITIONAL([BUILD_ERROR_STRINGS],[test "x$ENABLED_ERROR_STRINGS" = "xyes"])
AM_CONDITIONAL([BUILD_DO178],[test "x$ENABLED_DO178" = "xyes"])
AM_CONDITIONAL([BUILD_PSA],[test "x$ENABLED_PSA" = "xyes"])
+AM_CONDITIONAL([BUILD_DTLS13],[test "x$ENABLED_DTLS13" = "xyes"])
if test "$ENABLED_REPRODUCIBLE_BUILD" != "yes" &&
(test "$ax_enable_debug" = "yes" ||
@@ -8169,6 +8187,7 @@ echo " * chrony: $ENABLED_CHRONY"
echo " * strongSwan: $ENABLED_STRONGSWAN"
echo " * ERROR_STRINGS: $ENABLED_ERROR_STRINGS"
echo " * DTLS: $ENABLED_DTLS"
+echo " * DTLS v1.3: $ENABLED_DTLS13"
echo " * SCTP: $ENABLED_SCTP"
echo " * SRTP: $ENABLED_SRTP"
echo " * Indefinite Length: $ENABLED_BER_INDEF"
diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h
index 681f2cee0..203fd15c2 100644
--- a/doc/dox_comments/header_files/ssl.h
+++ b/doc/dox_comments/header_files/ssl.h
@@ -3326,6 +3326,47 @@ int wolfSSL_dtls_get_using_nonblock(WOLFSSL*);
\sa wolfSSL_dtls_set_peer
*/
int wolfSSL_dtls_get_current_timeout(WOLFSSL* ssl);
+/*!
+ \brief This function returns true if the application should setup a quicker
+ timeout. When using non-blocking sockets, something in the user code needs
+ to decide when to check for available data and how long it needs to wait. If
+ this function returns true, it means that the library already detected some
+ disruption in the communication, but it wants to wait for a little longer in
+ case some messages from the other peers are still in flight. Is up to the
+ application to fine tune the value of this timer, a good one may be
+ dtls_get_current_timeout() / 4.
+
+ \return true if the application code should setup a quicker timeout
+
+ \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new().
+
+ \sa wolfSSL_dtls
+ \sa wolfSSL_dtls_get_peer
+ \sa wolfSSL_dtls_got_timeout
+ \sa wolfSSL_dtls_set_peer
+ \sa wolfSSL_dtls13_set_send_more_acks
+*/
+int wolfSSL_dtls13_use_quick_timeout(WOLFSSL *ssl);
+/*!
+ \ingroup Setup
+
+ \brief This function sets whether the library should send ACKs to the other
+ peer immediately when detecting disruption or not. Sending ACKs immediately
+ assures minimum latency but it may consume more bandwidth than necessary. If
+ the application manages the timer by itself and this option is set to 0 then
+ application code can use wolfSSL_dtls13_use_quick_timeout() to determine if
+ it should setup a quicker timeout to send those delayed ACKs.
+
+ \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new().
+ \param value 1 to set the option, 0 to disable the option
+
+ \sa wolfSSL_dtls
+ \sa wolfSSL_dtls_get_peer
+ \sa wolfSSL_dtls_got_timeout
+ \sa wolfSSL_dtls_set_peer
+ \sa wolfSSL_dtls13_use_quick_timeout
+*/
+void wolfSSL_dtls13_set_send_more_acks(WOLFSSL *ssl, int value);
/*!
\ingroup Setup
@@ -14044,3 +14085,12 @@ int wolfSSL_RSA_sign_generic_padding(int type, const unsigned char* m,
unsigned int mLen, unsigned char* sigRet,
unsigned int* sigLen, WOLFSSL_RSA* rsa,
int flag, int padding);
+/*!
+
+\brief checks if DTLSv1.3 stack has some messages sent but not yet acknowledged
+ by the other peer
+
+ \return 1 if there are pending messages, 0 otherwise
+ \param ssl A WOLFSSL object pointer
+*/
+int wolfSSL_dtls13_has_pending_msg(WOLFSSL *ssl);
diff --git a/examples/benchmark/tls_bench.c b/examples/benchmark/tls_bench.c
index ba5ca2fb2..b9b7d8265 100644
--- a/examples/benchmark/tls_bench.c
+++ b/examples/benchmark/tls_bench.c
@@ -905,9 +905,15 @@ static int bench_tls_client(info_t* info)
#ifdef WOLFSSL_DTLS
if (info->doDTLS) {
if (tls13) {
- return WOLFSSL_SUCCESS;
+ #ifdef WOLFSSL_DTLS13
+ cli_ctx = wolfSSL_CTX_new(wolfDTLSv1_3_client_method());
+ #endif
+ }
+ else {
+ #ifndef WOLFSSL_NO_TLS12
+ cli_ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method());
+ #endif
}
- cli_ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method());
}
else
#endif
@@ -1348,9 +1354,15 @@ static int bench_tls_server(info_t* info)
#ifdef WOLFSSL_DTLS
if (info->doDTLS) {
if (tls13) {
- return WOLFSSL_SUCCESS;
+ #ifdef WOLFSSL_DTLS13
+ srv_ctx = wolfSSL_CTX_new(wolfDTLSv1_3_server_method());
+ #endif
+ }
+ else {
+ #ifndef WOLFSSL_NO_TLS12
+ srv_ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method());
+ #endif
}
- srv_ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method());
}
else
#endif
diff --git a/examples/client/client.c b/examples/client/client.c
index bfd152c1c..faa97955f 100644
--- a/examples/client/client.c
+++ b/examples/client/client.c
@@ -142,6 +142,10 @@ static int lng_index = 0;
#endif
#ifdef HAVE_SESSION_TICKET
+
+#ifndef SESSION_TICKET_LEN
+#define SESSION_TICKET_LEN 256
+#endif
static int sessionTicketCB(WOLFSSL* ssl,
const unsigned char* ticket, int ticketSz,
void* ctx)
@@ -803,7 +807,8 @@ static int ClientBenchmarkThroughput(WOLFSSL_CTX* ctx, char* host, word16 port,
}
else
#endif
- if (err != WOLFSSL_ERROR_WANT_READ) {
+ if (err != WOLFSSL_ERROR_WANT_READ &&
+ err != WOLFSSL_ERROR_WANT_WRITE) {
fprintf(stderr, "SSL_read bench error %d\n", err);
err_sys("SSL_read failed");
}
@@ -1068,7 +1073,9 @@ static int ClientRead(WOLFSSL* ssl, char* reply, int replyLen, int mustRead,
}
}
- if (mustRead && err == WOLFSSL_ERROR_WANT_READ) {
+ if (mustRead &&
+ (err == WOLFSSL_ERROR_WANT_READ
+ || err == WOLFSSL_ERROR_WANT_WRITE)) {
elapsed = current_time(0) - start;
if (elapsed > MAX_NON_BLOCK_SEC) {
fprintf(stderr, "Nonblocking read timeout\n");
@@ -1868,6 +1875,12 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
#ifdef WOLFSSL_SRTP
{ "srtp", 2, 260 }, /* optional argument */
#endif
+#ifdef WOLFSSL_DTLS13
+ /* allow waitTicket option even when HAVE_SESSION_TICKET is 0. Otherwise
+ * tests that use this option will ignore the options following
+ * --waitTicket in the command line and fail */
+ {"waitTicket", 0, 261},
+#endif /* WOLFSSL_DTLS13 */
{ 0, 0, 0 }
};
#endif
@@ -1994,6 +2007,10 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
const char* dtlsSrtpProfiles = NULL;
#endif
+#ifdef HAVE_SESSION_TICKET
+ int waitTicket = 0;
+#endif /* HAVE_SESSION_TICKET */
+
char buffer[WOLFSSL_MAX_ERROR_SZ];
int argc = ((func_args*)args)->argc;
@@ -2141,6 +2158,14 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
break;
#endif
+#ifdef WOLFSSL_DTLS13
+ case 261:
+#ifdef HAVE_SESSION_TICKET
+ waitTicket = 1;
+#endif /* HAVE_SESSION_TICKET */
+ break;
+#endif /* WOLFSSL_DTLS13 */
+
case 'G' :
#ifdef WOLFSSL_SCTP
doDTLS = 1;
@@ -2749,12 +2774,21 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
}
else {
if (doDTLS) {
- if (version == 3)
+ if (version == 3) {
version = -2;
+ }
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE)
- else if (version == EITHER_DOWNGRADE_VERSION)
+ else if (version == EITHER_DOWNGRADE_VERSION) {
version = -3;
+ }
#endif
+ else if (version == 4) {
+#ifdef WOLFSSL_DTLS13
+ version = -4;
+#else
+ err_sys("Bad DTLS version");
+#endif /* WOLFSSL_DTLS13 */
+ }
else
version = -1;
}
@@ -2833,6 +2867,11 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
method = wolfDTLSv1_2_client_method_ex;
break;
#endif
+#ifdef WOLFSSL_DTLS13
+ case -4:
+ method = wolfDTLSv1_3_client_method_ex;
+ break;
+#endif /* WOLFSSL_DTLS13 */
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE)
case -3:
method = wolfDTLSv1_2_method_ex;
@@ -3450,7 +3489,7 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES)
- if (!helloRetry && version >= 4) {
+ if (!helloRetry && (version >= 4 || version <= -4)) {
SetKeyShare(ssl, onlyKeyShare, useX25519, useX448, usePqc,
pqcAlg, 0);
}
@@ -4029,6 +4068,29 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
(void)ClientWrite(ssl, msg, msgSz, "", 0);
#endif
+#if defined(HAVE_SESSION_TICKET)
+ while (waitTicket == 1) {
+ unsigned char ticketBuf[SESSION_TICKET_LEN];
+ int zeroReturn = 0;
+ word32 size;
+
+ (void)zeroReturn;
+ size = sizeof(ticketBuf);
+ err = wolfSSL_get_SessionTicket(ssl, ticketBuf, &size);
+ if (err < 0)
+ err_sys("wolfSSL_get_SessionTicket failed");
+
+ if (size == 0) {
+ err = process_handshake_messages(ssl, !nonBlocking, &zeroReturn);
+ if (err < 0)
+ err_sys("error waiting for session ticket ");
+ }
+ else {
+ waitTicket = 0;
+ }
+ }
+#endif
+
#ifndef NO_SESSION_CACHE
if (resumeSession) {
session = wolfSSL_get1_session(ssl);
diff --git a/examples/echoclient/echoclient.c b/examples/echoclient/echoclient.c
index 08fcaa459..48b11ed91 100644
--- a/examples/echoclient/echoclient.c
+++ b/examples/echoclient/echoclient.c
@@ -138,7 +138,11 @@ void echoclient_test(void* args)
#endif
#if defined(CYASSL_DTLS)
+ #ifdef WOLFSSL_DTLS13
+ method = wolfDTLSv1_3_client_method();
+ #elif !defined(WOLFSSL_NO_TLS12)
method = DTLSv1_2_client_method();
+ #endif
#elif !defined(NO_TLS)
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_SNIFFER)
method = CyaTLSv1_2_client_method();
diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c
index 9f9f104b1..2df45d994 100644
--- a/examples/echoserver/echoserver.c
+++ b/examples/echoserver/echoserver.c
@@ -145,7 +145,11 @@ THREAD_RETURN CYASSL_THREAD echoserver_test(void* args)
tcp_listen(&sockfd, &port, useAnyAddr, doDTLS, 0);
#if defined(CYASSL_DTLS)
+ #ifdef WOLFSSL_DTLS13
+ method = wolfDTLSv1_3_server_method();
+ #elif !defined(WOLFSSL_NO_TLS12)
method = CyaDTLSv1_2_server_method();
+ #endif
#elif !defined(NO_TLS)
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_SNIFFER)
method = CyaTLSv1_2_server_method();
diff --git a/examples/server/server.c b/examples/server/server.c
index 3e92e9d9d..de6204c9a 100644
--- a/examples/server/server.c
+++ b/examples/server/server.c
@@ -416,6 +416,7 @@ int ServerEchoData(SSL* ssl, int clientfd, int echoData, int block,
else
#endif
if (err != WOLFSSL_ERROR_WANT_READ &&
+ err != WOLFSSL_ERROR_WANT_WRITE &&
err != WOLFSSL_ERROR_ZERO_RETURN &&
err != APP_DATA_READY) {
fprintf(stderr, "SSL_read echo error %d\n", err);
@@ -546,9 +547,16 @@ static void ServerRead(WOLFSSL* ssl, char* input, int inputLen)
}
else if (SSL_get_error(ssl, 0) == 0 &&
tcp_select(SSL_get_fd(ssl), 0) == TEST_RECV_READY) {
+ err = wolfSSL_peek(ssl, buffer, 0);
+ if(err < 0) {
+ err_sys_ex(runWithErrors, "wolfSSL_peek failed");
+ }
+ if (wolfSSL_pending(ssl))
err = WOLFSSL_ERROR_WANT_READ;
}
- } while (err == WC_PENDING_E || err == WOLFSSL_ERROR_WANT_READ);
+ } while (err == WC_PENDING_E
+ || err == WOLFSSL_ERROR_WANT_READ
+ || err == WOLFSSL_ERROR_WANT_WRITE);
if (ret > 0) {
/* null terminate message */
input[ret] = '\0';
@@ -2160,13 +2168,22 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
}
else {
if (doDTLS) {
- if (version == 3)
+ if (version == 3) {
version = -2;
+ }
+ else if (version == 4) {
+#ifdef WOLFSSL_DTLS13
+ version = -4;
+#else
+ err_sys_ex(runWithErrors, "Bad DTLS version");
+#endif /* WOLFSSL_DTLS13 */
+ }
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE)
- else if (version == EITHER_DOWNGRADE_VERSION)
+ else if (version == EITHER_DOWNGRADE_VERSION) {
version = -3;
+ }
#endif
- else
+ else if (version == 2)
version = -1;
}
}
@@ -2225,7 +2242,16 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
#endif
case SERVER_DOWNGRADE_VERSION:
- method = wolfSSLv23_server_method_ex;
+ if (!doDTLS) {
+ method = wolfSSLv23_server_method_ex;
+ }
+ else {
+#ifdef WOLFSSL_DTLS13
+ method = wolfDTLS_server_method_ex;
+#else
+ err_sys_ex(runWithErrors, "version not supported");
+#endif /* WOLFSSL_DTLS13 */
+ }
break;
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE)
case EITHER_DOWNGRADE_VERSION:
@@ -2246,6 +2272,11 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
method = wolfDTLSv1_2_server_method_ex;
break;
#endif
+#ifdef WOLFSSL_DTLS13
+ case -4:
+ method = wolfDTLSv1_3_server_method_ex;
+ break;
+#endif
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE)
case -3:
method = wolfDTLSv1_2_method_ex;
@@ -2291,6 +2322,19 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
err_sys_ex(catastrophic, "unable to get ctx");
if (minVersion != SERVER_INVALID_VERSION) {
+#ifdef WOLFSSL_DTLS13
+ switch (minVersion) {
+ case 4:
+ minVersion = WOLFSSL_DTLSV1_3;
+ break;
+ case 3:
+ minVersion = WOLFSSL_DTLSV1_2;
+ break;
+ case 2:
+ minVersion = WOLFSSL_DTLSV1;
+ break;
+ }
+#endif /* WOLFSSL_DTLS13 */
wolfSSL_CTX_SetMinVersion(ctx, minVersion);
}
@@ -2789,10 +2833,6 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
if (postHandAuth) {
unsigned int verify_flags = 0;
- SSL_set_verify(ssl, WOLFSSL_VERIFY_PEER |
- ((usePskPlus) ? WOLFSSL_VERIFY_FAIL_EXCEPT_PSK :
- WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT), 0);
-
#ifdef TEST_BEFORE_DATE
verify_flags |= WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY;
#endif
@@ -3337,9 +3377,20 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
if (updateKeysIVs)
wolfSSL_update_keys(ssl);
#endif
-#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
- if (postHandAuth)
+
+#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
+ #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
+ if (postHandAuth) {
+
+ SSL_set_verify(ssl, WOLFSSL_VERIFY_PEER |
+ ((usePskPlus) ? WOLFSSL_VERIFY_FAIL_EXCEPT_PSK :
+ WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT), 0);
+
wolfSSL_request_certificate(ssl);
+
+ }
+
+ #endif
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
if (sendTicket) {
@@ -3389,6 +3440,23 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
Task_yield();
#endif
+#if defined(WOLFSSL_DTLS13)
+ if (wolfSSL_dtls(ssl) && version == -4) {
+ int zero_return = 0;
+ while (wolfSSL_dtls13_has_pending_msg(ssl)) {
+ err =
+ process_handshake_messages(ssl, !nonBlocking, &zero_return);
+ if (err < 0) {
+ /* other peer closes the connection, non fatal */
+ if (zero_return)
+ break;
+
+ err_sys("Error while processing pending DTLSv1.3 messages");
+ }
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
+
ret = SSL_shutdown(ssl);
if (wc_shutdown && ret == WOLFSSL_SHUTDOWN_NOT_DONE) {
while (tcp_select(wolfSSL_get_fd(ssl), DEFAULT_TIMEOUT_SEC) ==
diff --git a/scripts/dtls13.test b/scripts/dtls13.test
new file mode 100755
index 000000000..732eafddc
--- /dev/null
+++ b/scripts/dtls13.test
@@ -0,0 +1,168 @@
+#!/bin/bash
+
+set -e
+
+cleanup () {
+ echo
+ echo "Cleaning up..."
+ if [ ! -z "$UDP_PROXY_PID" ];then
+ echo "Killing udp_proxy $UDP_PROXY_PID"
+ kill $UDP_PROXY_PID
+ fi
+ if [ ! -z "$SERVER_PID" ];then
+ echo "Killing server $SERVER_PID"
+ kill $SERVER_PID
+ fi
+}
+
+trap cleanup err exit
+
+WOLFSSL_ROOT=$(pwd)
+UDP_PROXY_PATH=$WOLFSSL_ROOT/../udp-proxy/udp_proxy
+PROXY_PORT=12345
+SERVER_PORT=11111
+NEW_SESSION_TICKET_SIZE=200
+KEY_UPDATE_SIZE=35
+
+(killall udp_proxy || true)
+(killall lt-server || true)
+(killall lt-client || true)
+
+# $WOLFSSL_ROOT/tests/unit.test tests/test-dtls13.conf
+
+test_dropping_packets () {
+ for i in $(seq 0 11);do
+ echo -e "\ndropping packet $i\n" | tee -a /tmp/serr | tee -a /tmp/cerr | tee -a /tmp/udp
+ $UDP_PROXY_PATH -p $PROXY_PORT -s 127.0.0.1:$SERVER_PORT -f $i -u >>/tmp/udp &
+ UDP_PROXY_PID=$!
+ $WOLFSSL_ROOT/examples/server/server -v4 -u -Ta 2>>/tmp/serr &
+ SERVER_PID=$!
+ sleep 0.2
+ now=$(date +%s.%N)
+ $WOLFSSL_ROOT/examples/client/client -v4 -u -p$PROXY_PORT 2>>/tmp/cerr
+ elapsed=$(echo $(date +%s.%N) - $now | bc)
+ echo "it took ${elapsed} sec(s)" >> /tmp/udp
+ wait $SERVER_PID
+ SERVER_PID=
+ kill $UDP_PROXY_PID
+ UDP_PROXY_PID=
+ done
+
+
+ echo -e "\nTesting WANT_WRITE\n" | tee -a /tmp/serr | tee -a /tmp/cerr | tee -a /tmp/udp
+
+ # dropping last ack would be client error as wolfssl_read doesn't support WANT_WRITE as returned error
+ for i in $(seq 0 10);do
+ echo -e "\ndropping packet $i\n" | tee -a /tmp/serr | tee -a /tmp/cerr | tee -a /tmp/udp
+ $UDP_PROXY_PATH -p $PROXY_PORT -s 127.0.0.1:$SERVER_PORT -f $i -u >>/tmp/udp &
+ UDP_PROXY_PID=$!
+ $WOLFSSL_ROOT/examples/server/server -v4 -u -Ta -6 2>>/tmp/serr &
+ SERVER_PID=$!
+ sleep 0.2
+ now=$(date +%s.%N)
+ $WOLFSSL_ROOT/examples/client/client -v4 -u -p$PROXY_PORT -6 2>>/tmp/cerr
+ elapsed=$(echo $(date +%s.%N) - $now | bc)
+ echo "it took ${elapsed} sec(s)" >> /tmp/udp
+ wait $SERVER_PID
+ SERVER_PID=
+ kill $UDP_PROXY_PID
+ UDP_PROXY_PID=
+ done
+}
+
+# this test is based on detecting newSessionTicket message by its size. This is rather fragile.
+test_dropping_new_session_ticket() {
+ echo -e "\ndropping new session ticket packet of size $NEW_SESSION_TICKET_SIZE\n" | tee -a /tmp/serr | tee -a /tmp/cerr | tee -a /tmp/udp
+ $UDP_PROXY_PATH -p $PROXY_PORT -s 127.0.0.1:$SERVER_PORT -F $NEW_SESSION_TICKET_SIZE -u >>/tmp/udp &
+ UDP_PROXY_PID=$!
+ $WOLFSSL_ROOT/examples/server/server -v4 -w -u 2>>/tmp/serr &
+ SERVER_PID=$!
+ sleep 0.2
+ now=$(date +%s.%N)
+ $WOLFSSL_ROOT/examples/client/client -v4 -u -p$PROXY_PORT -w --waitTicket 2>>/tmp/cerr
+ elapsed=$(echo $(date +%s.%N) - $now | bc)
+ echo "it took ${elapsed} sec(s)" >> /tmp/udp
+ wait $SERVER_PID
+ SERVER_PID=
+ kill $UDP_PROXY_PID
+ UDP_PROXY_PID=
+}
+
+test_permutations () {
+ SIDE=$1
+ PERMUTATIONS=$(python3 << EOF
+import itertools
+for p in itertools.permutations("$2"):
+ print(''.join(p))
+EOF
+ )
+ echo "Testing $SIDE msg permutations"
+ for i in $PERMUTATIONS;do
+ echo -n "Testing $SIDE order $i"
+ UDP_LOGFILE=/tmp/udp-$SIDE-$i
+ $UDP_PROXY_PATH -p $PROXY_PORT -s 127.0.0.1:$SERVER_PORT -u -r $i -l $UDP_LOGFILE -S $SIDE &
+ UDP_PROXY_PID=$!
+ $WOLFSSL_ROOT/examples/server/server -v4 -u -Ta -w &> /tmp/serr &
+ SERVER_PID=$!
+ sleep 0.2
+ now=$(date +%s.%N)
+ $WOLFSSL_ROOT/examples/client/client -v4 -u -p$PROXY_PORT -w &> /tmp/cerr
+ elapsed=$(echo $(date +%s.%N) - $now | bc)
+ udp_lines=$(grep -P 'client:|server:' $UDP_LOGFILE | wc -l)
+ echo " took ${elapsed} sec(s) and produced ${udp_lines} messages"
+ wait $SERVER_PID
+ SERVER_PID=
+ kill $UDP_PROXY_PID
+ UDP_PROXY_PID=
+ rm $UDP_LOGFILE
+ done
+ echo "All $SIDE msg permutations succeeded"
+}
+
+
+test_time_delays () {
+ DELAYS=$(python3 << EOF
+import itertools
+t = [0.1, 0.5, 1.1]
+tt = []
+for i in itertools.product(t, t, t):
+ tt.append(i * 15)
+for i in tt:
+ print(','.join(map(lambda x: str(x) , i)))
+EOF
+ )
+ for DELAY in $DELAYS;do
+ echo -n "Testing delay $DELAY"
+ UDP_LOGFILE=/tmp/udp-delay-$DELAY
+ $UDP_PROXY_PATH -p $PROXY_PORT -s 127.0.0.1:$SERVER_PORT -u -l "$UDP_LOGFILE" -t $DELAY &
+ UDP_PROXY_PID=$!
+ $WOLFSSL_ROOT/examples/server/server -v4 -u -Ta -w &> /tmp/serr &
+ SERVER_PID=$!
+ sleep 0.2
+ now=$(date +%s.%N)
+ $WOLFSSL_ROOT/examples/client/client -v4 -u -p$PROXY_PORT -w &> /tmp/cerr
+ elapsed=$(echo $(date +%s.%N) - $now | bc)
+ udp_lines=$(grep -P 'client:|server:' "$UDP_LOGFILE" | wc -l)
+ echo " took ${elapsed} sec(s) and produced ${udp_lines} messages"
+ wait $SERVER_PID
+ SERVER_PID=
+ kill $UDP_PROXY_PID
+ UDP_PROXY_PID=
+ rm "$UDP_LOGFILE"
+ done
+}
+
+test_dropping_packets
+test_permutations client 012
+test_dropping_new_session_ticket
+
+if [ ! -z $DTLS13_DO_SERVER_PERMUTATION_TEST ];then
+ test_permutations server 0123456
+fi
+
+# TODO: fix udp_proxy to not re-order close alert before app data
+if [ ! -z $DTLS13_DO_DELAY_TEST ];then
+ test_time_delays
+fi
+
+
diff --git a/scripts/include.am b/scripts/include.am
index 683e71cf7..9bebd3a67 100644
--- a/scripts/include.am
+++ b/scripts/include.am
@@ -112,3 +112,7 @@ EXTRA_DIST += scripts/dertoc.pl
EXTRA_DIST += scripts/stm32l4-v4_0_1_build.sh
EXTRA_DIST += scripts/cleanup_testfiles.sh
+
+if BUILD_DTLS13
+EXTRA_DIST += scripts/dtls13.test
+endif
diff --git a/src/dtls13.c b/src/dtls13.c
new file mode 100644
index 000000000..2f30c98db
--- /dev/null
+++ b/src/dtls13.c
@@ -0,0 +1,2431 @@
+/* dtls13.c
+ *
+ * Copyright (C) 2006-2022 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 2 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include
+#endif
+
+#include
+
+#ifdef WOLFSSL_DTLS13
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef NO_INLINE
+#include
+#else
+#define WOLFSSL_MISC_INCLUDED
+#include
+#endif
+
+/**
+ * enum rnDirection - distinguish between RecordNumber Enc/Dec
+ * PROTECT: encrypt the Record Number
+ * DEPROTECT: decrypt the Record Number
+ */
+enum rnDirection {
+ PROTECT = 0,
+ DEPROTECT,
+};
+
+/**
+ * struct Dtls13HandshakeHeader: represent DTLS Handshake header
+ * @msg_type: type of message (client_hello,server_hello,etc)
+ * @length: length of the message
+ * @messageSeq: message sequence number (used for reordering and retransmission)
+ * @fragmentOffset: this is the offset of the data in the complete message. For
+ * an unfragmented message this is always zero
+ * @fragmentLength: length of this fragment (if not fragmented @fragmentLength
+ * is always equal to @length)
+ */
+typedef struct Dtls13HandshakeHeader {
+ byte msg_type;
+ byte length[3];
+ byte messageSeq[2];
+ byte fragmentOffset[3];
+ byte fragmentLength[3];
+} Dtls13HandshakeHeader;
+
+/**
+ * struct Dtls13Recordplaintextheader: represent header of unprotected DTLSv1.3
+ * record
+ * @contentType: content type of the record (handshake, applicationData, etc)
+ * @legacyversionrecord: legacy version field
+ * @epoch: epoch number (lower 16 bits)
+ * @sequenceNumber: sequence number (lower 16 bits)
+ * @length: length of the record
+ */
+typedef struct Dtls13RecordPlaintextHeader {
+ byte contentType;
+ ProtocolVersion legacyVersionRecord;
+ byte epoch[2];
+ byte sequenceNumber[6];
+ byte length[2];
+} Dtls13RecordPlaintextHeader;
+
+/**
+ * struct Dtls13RecordCiphertextHeader: represent the header of protected
+ * DTLSv1.3 used in wolfSSL
+ * @unifiedHdrflags: the first three bits are always 001, then CID bit: if
+ * setted the header contains a CID, S bit: if setted the header contains the 16
+ * LSBs of sequence number length, otherwise only the 8 LSBs are present, then
+ * the L bit: if present the length of the record is present, lastly EE: the
+ * last two bits of the epoch
+ * @sequenceNumber: 16 LSBs of the sequence number
+ * @length: length of the record
+ *
+ * DTLSv1.3 uses a variable length header, this struct represent only the
+ * representation used by wolfSSL, where CID is never present (not supported),
+ * the 16 LSBs of the sequence number and the length are always present.
+ */
+typedef struct Dtls13RecordCiphertextHeader {
+ /* 0 0 1 C S L E E */
+ byte unifiedHdrFlags;
+ byte sequenceNumber[2];
+ byte length[2];
+} Dtls13RecordCiphertextHeader;
+
+/* size of the len field in the unified header */
+#define DTLS13_LEN_SIZE 2
+/* size of the mask used to encrypt/decrypt Record Number */
+#define DTLS13_RN_MASK_SIZE 16
+/* size of the flags in the unified header */
+#define DTLS13_HDR_FLAGS_SIZE 1
+/* size of the sequence number wher SEQ_LEN_BIT is present */
+#define DTLS13_SEQ_16_LEN 2
+/* size of the sequence number wher SEQ_LEN_BIT is not present */
+#define DTLS13_SEQ_8_LEN 1
+
+/* fixed bits mask to detect unified header */
+#define DTLS13_FIXED_BITS_MASK (0x111 << 5)
+/* fixed bits value to detect unified header */
+#define DTLS13_FIXED_BITS (0x1 << 5)
+/* ConnectionID present bit in the unified header flags */
+#define DTLS13_CID_BIT (0x1 << 4)
+/* Sequence number is 16 bits if this bit is into unified header flags */
+#define DTLS13_SEQ_LEN_BIT (0x1 << 3)
+/* Length field is present if this bit is into unified header flags */
+#define DTLS13_LEN_BIT (0x1 << 2)
+
+/* For now, the size of the outgoing DTLSv1.3 record header is fixed to 5 bytes
+ (8 bit header flags + 16bit record number + 16 bit length). In the future, we
+ can dynamically choose to remove the length from the header to save
+ space. Also it will need to account for client connection ID when
+ supported. */
+#define DTLS13_UNIFIED_HEADER_SIZE 5
+#define DTLS13_MIN_CIPHERTEXT 16
+#define DTLS13_MIN_RTX_INTERVAL 1
+
+WOLFSSL_METHOD* wolfDTLSv1_3_client_method_ex(void* heap)
+{
+ WOLFSSL_METHOD* method;
+
+ WOLFSSL_ENTER("DTLSv1_3_client_method_ex");
+
+ method = (WOLFSSL_METHOD*)XMALLOC(sizeof(WOLFSSL_METHOD), heap,
+ DYNAMIC_TYPE_METHOD);
+ if (method)
+ InitSSL_Method(method, MakeDTLSv1_3());
+
+ return method;
+}
+
+WOLFSSL_METHOD* wolfDTLSv1_3_server_method_ex(void* heap)
+{
+ WOLFSSL_METHOD* method;
+
+ WOLFSSL_ENTER("DTLSv1_3_server_method_ex");
+
+ method = (WOLFSSL_METHOD*)XMALLOC(sizeof(WOLFSSL_METHOD), heap,
+ DYNAMIC_TYPE_METHOD);
+ if (method) {
+ InitSSL_Method(method, MakeDTLSv1_3());
+ method->side = WOLFSSL_SERVER_END;
+ }
+
+ return method;
+}
+
+WOLFSSL_METHOD* wolfDTLSv1_3_client_method(void)
+{
+ return wolfDTLSv1_3_client_method_ex(NULL);
+}
+
+WOLFSSL_METHOD* wolfDTLSv1_3_server_method(void)
+{
+ return wolfDTLSv1_3_server_method_ex(NULL);
+}
+
+int Dtls13RlAddPlaintextHeader(WOLFSSL* ssl, byte* out,
+ enum ContentType content_type, word16 length)
+{
+ Dtls13RecordPlaintextHeader* hdr;
+ word32 seq[2];
+ int ret;
+
+ hdr = (Dtls13RecordPlaintextHeader*)out;
+ hdr->contentType = content_type;
+ hdr->legacyVersionRecord.major = DTLS_MAJOR;
+ hdr->legacyVersionRecord.minor = DTLSv1_2_MINOR;
+
+ ret = Dtls13GetSeq(ssl, CUR_ORDER, seq, 1);
+ if (ret != 0)
+ return ret;
+
+ /* seq[0] combines the epoch and 16 MSB of sequence number. We write on the
+ epoch field and will overflow to the first two bytes of the sequence
+ number */
+ c32toa(seq[0], hdr->epoch);
+ c32toa(seq[1], &hdr->sequenceNumber[2]);
+
+ c16toa(length, hdr->length);
+
+ return 0;
+}
+
+static int Dtls13HandshakeAddHeaderFrag(WOLFSSL* ssl, byte* output,
+ enum HandShakeType msg_type, word32 frag_offset, word32 frag_length,
+ word32 msg_length)
+{
+ Dtls13HandshakeHeader* hdr;
+
+ hdr = (Dtls13HandshakeHeader*)output;
+
+ hdr->msg_type = msg_type;
+ c32to24((word32)msg_length, hdr->length);
+ c16toa(ssl->keys.dtls_handshake_number, hdr->messageSeq);
+
+ c32to24(frag_offset, hdr->fragmentOffset);
+ c32to24(frag_length, hdr->fragmentLength);
+
+ return 0;
+}
+
+static byte Dtls13TypeIsEncrypted(enum HandShakeType hs_type)
+{
+ int ret = 0;
+
+ switch (hs_type) {
+ case hello_request:
+ case hello_verify_request:
+ case client_hello:
+ case server_hello:
+ break;
+ case encrypted_extensions:
+ case session_ticket:
+ case end_of_early_data:
+ case hello_retry_request:
+ case certificate:
+ case server_key_exchange:
+ case certificate_request:
+ case server_hello_done:
+ case certificate_verify:
+ case client_key_exchange:
+ case finished:
+ case certificate_status:
+ case key_update:
+ case change_cipher_hs:
+ case message_hash:
+ case no_shake:
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int Dtls13GetRnMask(WOLFSSL* ssl, const byte* ciphertext, byte* mask,
+ enum rnDirection dir)
+{
+ RecordNumberCiphers* c;
+ int ret;
+
+ if (dir == PROTECT)
+ c = &ssl->dtlsRecordNumberEncrypt;
+ else
+ c = &ssl->dtlsRecordNumberDecrypt;
+
+#ifdef HAVE_AESGCM
+ if (ssl->specs.bulk_cipher_algorithm == wolfssl_aes_gcm) {
+
+ if (c->aes == NULL)
+ return BAD_STATE_E;
+ return wc_AesEncryptDirect(c->aes, mask, ciphertext);
+ }
+#endif /* HAVE_AESGCM */
+
+#ifdef HAVE_CHACHA
+ if (ssl->specs.bulk_cipher_algorithm == wolfssl_chacha) {
+ word32 counter;
+
+ if (c->chacha == NULL)
+ return BAD_STATE_E;
+
+ /* assuming CIPHER[0..3] should be interpreted as little endian 32-bits
+ integer. The draft rfc isn't really clear on that. See sec 4.2.3 of
+ the draft. See also Section 2.3 of the Chacha RFC. */
+ XMEMCPY(&counter, ciphertext, sizeof(counter));
+#ifdef BIG_ENDIAN
+ counter = ByteReverseWord32(counter);
+#endif /* BIG_ENDIAN */
+
+ ret = wc_Chacha_SetIV(c->chacha, &ciphertext[4], counter);
+ if (ret != 0)
+ return ret;
+
+ XMEMSET(mask, 0, DTLS13_RN_MASK_SIZE);
+
+ return wc_Chacha_Process(c->chacha, mask, mask, DTLS13_RN_MASK_SIZE);
+ }
+#endif /* HAVE_CHACHA */
+
+ return NOT_COMPILED_IN;
+}
+
+static int Dtls13EncryptDecryptRecordNumber(WOLFSSL* ssl, byte* seq,
+ int SeqLength, const byte* ciphertext, enum rnDirection dir)
+{
+ byte mask[DTLS13_RN_MASK_SIZE];
+ int ret;
+
+ ret = Dtls13GetRnMask(ssl, ciphertext, mask, dir);
+ if (ret != 0)
+ return ret;
+
+ xorbuf(seq, mask, SeqLength);
+
+ return 0;
+}
+
+static byte Dtls13RtxMsgNeedsAck(WOLFSSL* ssl, enum HandShakeType hs)
+{
+
+#ifndef NO_WOLFSSL_SERVER
+ /* we send an ACK when processing the finished message. In this case either
+ we already sent an ACK for client's Certificate/CertificateVerify or they
+ are in our list of seen records and will be included in the ACK
+ message */
+ if (ssl->options.side == WOLFSSL_SERVER_END && (hs == finished))
+ return 1;
+#endif /* NO_WOLFSSL_SERVER */
+
+ if (hs == session_ticket || hs == key_update)
+ return 1;
+
+ return 0;
+}
+
+static void Dtls13MsgWasProcessed(WOLFSSL* ssl, enum HandShakeType hs)
+{
+ ssl->keys.dtls_expected_peer_handshake_number++;
+
+ /* we need to send ACKs on the last message of a flight that needs explicit
+ acknowledgment */
+ ssl->dtls13Rtx.sendAcks = Dtls13RtxMsgNeedsAck(ssl, hs);
+}
+
+static int Dtls13ProcessBufferedMessages(WOLFSSL* ssl)
+{
+ DtlsMsg* msg = ssl->dtls_rx_msg_list;
+ word32 idx = 0;
+ int ret = 0;
+
+ WOLFSSL_ENTER("Dtls13ProcessBufferedMessages()");
+
+ while (msg != NULL) {
+ idx = 0;
+
+ /* message not in order */
+ if (ssl->keys.dtls_expected_peer_handshake_number != msg->seq)
+ break;
+
+ /* message not complete */
+ if (msg->fragSz != msg->sz)
+ break;
+
+ ret = DoTls13HandShakeMsgType(ssl, msg->msg, &idx, msg->type, msg->sz,
+ msg->sz);
+ if (ret != 0)
+ break;
+
+ Dtls13MsgWasProcessed(ssl, (enum HandShakeType)msg->type);
+
+ ssl->dtls_rx_msg_list = msg->next;
+ DtlsMsgDelete(msg, ssl->heap);
+ msg = ssl->dtls_rx_msg_list;
+ ssl->dtls_rx_msg_list_sz--;
+ }
+
+ WOLFSSL_LEAVE("dtls13_process_buffered_messages()", ret);
+
+ return ret;
+}
+
+static int Dtls13NextMessageComplete(WOLFSSL* ssl)
+{
+ return ssl->dtls_rx_msg_list != NULL &&
+ ssl->dtls_rx_msg_list->fragSz == ssl->dtls_rx_msg_list->sz &&
+ ssl->dtls_rx_msg_list->seq ==
+ ssl->keys.dtls_expected_peer_handshake_number;
+}
+
+static WC_INLINE int FragIsInOutputBuffer(WOLFSSL* ssl, const byte* frag)
+{
+ const byte* OutputBuffer = ssl->buffers.outputBuffer.buffer;
+ word32 OutputBufferSize = ssl->buffers.outputBuffer.bufferSize;
+
+ return frag >= OutputBuffer && frag < OutputBuffer + OutputBufferSize;
+}
+
+static int Dtls13SendFragFromBuffer(WOLFSSL* ssl, byte* output, word16 length)
+{
+ byte* buf;
+ int ret;
+
+ if (FragIsInOutputBuffer(ssl, output))
+ return BAD_FUNC_ARG;
+
+ ret = CheckAvailableSize(ssl, length);
+ if (ret != 0)
+ return ret;
+
+ buf = ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.length;
+
+ XMEMCPY(buf, output, length);
+
+ ssl->buffers.outputBuffer.length += length;
+
+ return SendBuffered(ssl);
+}
+
+static int Dtls13SendNow(WOLFSSL* ssl, enum HandShakeType handshakeType)
+{
+ if (!ssl->options.groupMessages)
+ return 1;
+
+ if (handshakeType == client_hello || handshakeType == hello_retry_request ||
+ handshakeType == finished || handshakeType == session_ticket ||
+ handshakeType == session_ticket || handshakeType == key_update)
+ return 1;
+
+ return 0;
+}
+
+/* Handshake header DTLS only fields are not inlcuded in the transcript hash */
+int Dtls13HashHandshake(WOLFSSL* ssl, const byte* output, word16 length)
+{
+ int ret;
+
+ if (length < DTLS_HANDSHAKE_HEADER_SZ)
+ return BAD_FUNC_ARG;
+
+ /* msg_type(1) + length (3) */
+ ret = HashRaw(ssl, output, OPAQUE32_LEN);
+ if (ret != 0)
+ return ret;
+
+ output += OPAQUE32_LEN;
+ length -= OPAQUE32_LEN;
+
+ /* message_seq(2) + fragment_offset(3) + fragment_length(3) */
+ output += OPAQUE64_LEN;
+ length -= OPAQUE64_LEN;
+
+ return HashRaw(ssl, output, length);
+}
+
+static int Dtls13SendFragment(WOLFSSL* ssl, byte* output, word16 output_size,
+ word16 length, enum HandShakeType handshakeType, int hashOutput,
+ int sendImmediately)
+{
+ word16 recordHeaderLength;
+ word16 recordLength;
+ byte isProtected;
+ int sendLength;
+ byte* msg;
+ int ret;
+
+ if (output_size < length)
+ return BUFFER_ERROR;
+
+ isProtected = Dtls13TypeIsEncrypted(handshakeType);
+ recordHeaderLength = Dtls13GetRlHeaderLength(isProtected);
+
+ if (length <= recordHeaderLength)
+ return BUFFER_ERROR;
+
+ recordLength = length - recordHeaderLength;
+
+ if (!isProtected) {
+ ret = Dtls13RlAddPlaintextHeader(ssl, output, handshake, recordLength);
+ if (ret != 0)
+ return ret;
+ }
+ else {
+ msg = output + recordHeaderLength;
+
+ if (length <= recordHeaderLength)
+ return BUFFER_ERROR;
+
+ if (hashOutput) {
+ ret = Dtls13HashHandshake(ssl, msg, recordLength);
+ if (ret != 0)
+ return ret;
+ }
+
+ sendLength = BuildTls13Message(ssl, output, output_size, msg,
+ recordLength, handshake, 0, 0, 0);
+ if (sendLength < 0)
+ return sendLength;
+
+ length = (word16)sendLength;
+ }
+
+ if (!FragIsInOutputBuffer(ssl, output))
+ return Dtls13SendFragFromBuffer(ssl, output, length);
+
+ ssl->buffers.outputBuffer.length += length;
+
+ ret = 0;
+ if (sendImmediately)
+ ret = SendBuffered(ssl);
+
+ return ret;
+}
+
+static void Dtls13FreeFragmentsBuffer(WOLFSSL* ssl)
+{
+ XFREE(ssl->dtls13FragmentsBuffer.buffer, ssl->heap,
+ DYNAMIC_TYPE_TEMP_BUFFER);
+ ssl->dtls13FragmentsBuffer.buffer = NULL;
+ ssl->dtls13SendingFragments = 0;
+ ssl->dtls13MessageLength = ssl->dtls13FragOffset = 0;
+}
+
+static WC_INLINE void Dtls13FreeRtxBufferRecord(WOLFSSL* ssl,
+ Dtls13RtxRecord* r)
+{
+ (void)ssl;
+
+ XFREE(r->data, ssl->heap, DYNAMIC_TYPE_DTLS_MSG);
+ XFREE(r, ssl->heap, DYNAMIC_TYPE_DTLS_MSG);
+}
+
+static Dtls13RtxRecord* Dtls13RtxNewRecord(WOLFSSL* ssl, byte* data,
+ word16 length, enum HandShakeType handshakeType, w64wrapper seq)
+{
+ w64wrapper epochNumber;
+ Dtls13RtxRecord* r;
+
+ WOLFSSL_ENTER("Dtls13RtxNewRecord");
+
+ if (ssl->dtls13EncryptEpoch == NULL)
+ return NULL;
+
+ epochNumber = ssl->dtls13EncryptEpoch->epochNumber;
+
+ r = (Dtls13RtxRecord*)XMALLOC(sizeof(*r), ssl->heap, DYNAMIC_TYPE_DTLS_MSG);
+ if (r == NULL)
+ return NULL;
+
+ r->data = (byte*)XMALLOC(length, ssl->heap, DYNAMIC_TYPE_DTLS_MSG);
+ if (r->data == NULL) {
+ XFREE(r, ssl->heap, DYNAMIC_TYPE_DTLS_MSG);
+ return NULL;
+ }
+
+ XMEMCPY(r->data, data, length);
+ r->epoch = epochNumber;
+ r->length = length;
+ r->next = NULL;
+ r->handshakeType = handshakeType;
+ r->seq[0] = seq;
+ r->rnIdx = 1;
+
+ return r;
+}
+
+static void Dtls13RtxAddRecord(Dtls13Rtx* fsm, Dtls13RtxRecord* r)
+{
+ WOLFSSL_ENTER("Dtls13RtxAddRecord");
+
+ *fsm->rtxRecordTailPtr = r;
+ fsm->rtxRecordTailPtr = &r->next;
+ r->next = NULL;
+}
+
+static void Dtls13RtxRecordUnlink(WOLFSSL* ssl, Dtls13RtxRecord** prevNext,
+ Dtls13RtxRecord* r)
+{
+ /* if r was at the tail of the list, update the tail pointer */
+ if (r->next == NULL)
+ ssl->dtls13Rtx.rtxRecordTailPtr = prevNext;
+
+ /* unlink */
+ *prevNext = r->next;
+}
+
+static void Dtls13RtxFlushBuffered(WOLFSSL* ssl, byte keepNewSessionTicket)
+{
+ Dtls13RtxRecord *r, **prevNext;
+
+ WOLFSSL_ENTER("Dtls13RtxFlushBuffered");
+
+ prevNext = &ssl->dtls13Rtx.rtxRecords;
+ r = ssl->dtls13Rtx.rtxRecords;
+
+ /* we process the head at the end */
+ while (r != NULL) {
+
+ if (keepNewSessionTicket && r->handshakeType == session_ticket) {
+ prevNext = &r->next;
+ r = r->next;
+ continue;
+ }
+
+ *prevNext = r->next;
+ Dtls13FreeRtxBufferRecord(ssl, r);
+ r = *prevNext;
+ }
+
+ ssl->dtls13Rtx.rtxRecordTailPtr = prevNext;
+}
+
+static Dtls13RecordNumber* Dtls13NewRecordNumber(WOLFSSL* ssl, w64wrapper epoch,
+ w64wrapper seq)
+{
+ Dtls13RecordNumber* rn;
+
+ rn = (Dtls13RecordNumber*)XMALLOC(sizeof(*rn), ssl->heap,
+ DYNAMIC_TYPE_DTLS_MSG);
+ if (rn == NULL)
+ return NULL;
+
+ rn->next = NULL;
+ rn->epoch = epoch;
+ rn->seq = seq;
+
+ return rn;
+}
+
+static int Dtls13RtxAddAck(WOLFSSL* ssl, w64wrapper epoch, w64wrapper seq)
+{
+ Dtls13RecordNumber* rn;
+
+ WOLFSSL_ENTER("Dtls13RtxAddAck");
+
+ rn = Dtls13NewRecordNumber(ssl, epoch, seq);
+ if (rn == NULL)
+ return MEMORY_E;
+
+ rn->next = ssl->dtls13Rtx.seenRecords;
+ ssl->dtls13Rtx.seenRecords = rn;
+
+ return 0;
+}
+
+static void Dtls13RtxFlushAcks(WOLFSSL* ssl)
+{
+ Dtls13RecordNumber *list, *rn;
+
+ (void)ssl;
+
+ WOLFSSL_ENTER("Dtls13RtxFlushAcks");
+
+ list = ssl->dtls13Rtx.seenRecords;
+
+ while (list != NULL) {
+ rn = list;
+ list = rn->next;
+ XFREE(rn, ssl->heap, DYNAMIC_TYEP_DTLS_MSG);
+ }
+
+ ssl->dtls13Rtx.seenRecords = NULL;
+}
+
+static int Dtls13DetectDisruption(WOLFSSL* ssl, word32 fragOffset)
+{
+ /* retransmission. The other peer may have lost our flight or our ACKs. We
+ don't account this as a disruption */
+ if (ssl->keys.dtls_peer_handshake_number <
+ ssl->keys.dtls_expected_peer_handshake_number)
+ return 0;
+
+ /* out of order message */
+ if (ssl->keys.dtls_peer_handshake_number >
+ ssl->keys.dtls_expected_peer_handshake_number) {
+ return 1;
+ }
+
+ /* first fragment of in-order message */
+ if (fragOffset == 0)
+ return 0;
+
+ /* is not the next fragment in the message (the check is not 100% perfect,
+ in the worst case, we don't detect the disruption and wait for the other
+ peer retransmission) */
+ if (ssl->dtls_rx_msg_list == NULL ||
+ ssl->dtls_rx_msg_list->fragSz != fragOffset) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void Dtls13RtxRemoveCurAck(WOLFSSL* ssl)
+{
+ Dtls13RecordNumber *rn, **prevNext;
+
+ prevNext = &ssl->dtls13Rtx.seenRecords;
+ rn = ssl->dtls13Rtx.seenRecords;
+
+ while (rn != NULL) {
+ if (w64Equal(rn->epoch, ssl->keys.curEpoch64) &&
+ w64Equal(rn->seq, ssl->keys.curSeq)) {
+ *prevNext = rn->next;
+ XFREE(rn, ssl->heap, DYNAMIC_TYEP_DTLS_MSG);
+ return;
+ }
+
+ prevNext = &rn->next;
+ rn = rn->next;
+ }
+}
+
+static int Dtls13RtxMsgRecvd(WOLFSSL* ssl, enum HandShakeType hs,
+ word32 fragOffset)
+{
+ WOLFSSL_ENTER("Dtls13RtxMsgRecvd");
+
+ if (!ssl->options.handShakeDone &&
+ ssl->keys.dtls_peer_handshake_number >=
+ ssl->keys.dtls_expected_peer_handshake_number) {
+
+ /* In the handshake, receiving part of the next flight, acknowledge the
+ sent flight. The only exception is, on the server side, receiving the
+ last client flight does not ACK any sent new_session_ticket
+ messages. */
+ Dtls13RtxFlushBuffered(ssl, 1);
+ }
+
+ if (ssl->keys.dtls_peer_handshake_number <
+ ssl->keys.dtls_expected_peer_handshake_number) {
+
+ /* retransmission detected. */
+ ssl->dtls13Rtx.retransmit = 1;
+
+ /* the other peer may have retransmitted because an ACK for a flight
+ that needs explicit ACK was lost.*/
+ if (ssl->dtls13Rtx.seenRecords != NULL)
+ ssl->dtls13Rtx.sendAcks = (byte)ssl->options.dtls13SendMoreAcks;
+ }
+
+ if (ssl->keys.dtls_peer_handshake_number ==
+ ssl->keys.dtls_expected_peer_handshake_number &&
+ ssl->options.handShakeDone && hs == certificate_request) {
+
+ /* the current record, containing a post-handshake certificate request,
+ is implicitly acknowledged by the
+ certificate/certificate_verify/finished flight we are about to
+ send. Please note that if the certificate request came out-of-order
+ and we didn't send an ACK (sendMoreAcks == 0 and the missing
+ packet(s) arrive before that fast timeout expired), then we will send
+ both the ACK and the flight. While unnecessary this it's harmless, it
+ should be rare and simplifies the code. Otherwise, it would be
+ necessary to track which record number contained a CertificateRequest
+ with a particular context id */
+ Dtls13RtxRemoveCurAck(ssl);
+ }
+
+ if (ssl->options.dtls13SendMoreAcks && Dtls13DetectDisruption(ssl, fragOffset)) {
+ WOLFSSL_MSG("Disruption detected");
+ ssl->dtls13Rtx.sendAcks = 1;
+ }
+
+ return 0;
+}
+
+void Dtls13FreeFsmResources(WOLFSSL* ssl)
+{
+ Dtls13RtxFlushAcks(ssl);
+ Dtls13RtxFlushBuffered(ssl, 0);
+}
+
+static int Dtls13SendOneFragmentRtx(WOLFSSL* ssl,
+ enum HandShakeType handshakeType, word16 outputSize, byte* message,
+ word32 length, int hashOutput)
+{
+ Dtls13RtxRecord* rtxRecord;
+ word16 recordHeaderLength;
+ byte isProtected;
+ int ret;
+
+ isProtected = Dtls13TypeIsEncrypted(handshakeType);
+ recordHeaderLength = Dtls13GetRlHeaderLength(isProtected);
+
+ rtxRecord = Dtls13RtxNewRecord(ssl, message + recordHeaderLength,
+ (word16)(length - recordHeaderLength), handshakeType,
+ ssl->dtls13EncryptEpoch->nextSeqNumber);
+
+ if (rtxRecord == NULL)
+ return MEMORY_E;
+
+ ret = Dtls13SendFragment(ssl, message, outputSize, (word16)length,
+ handshakeType, hashOutput, Dtls13SendNow(ssl, handshakeType));
+
+ if (ret == 0 || ret == WANT_WRITE)
+ Dtls13RtxAddRecord(&ssl->dtls13Rtx, rtxRecord);
+ else
+ Dtls13FreeRtxBufferRecord(ssl, rtxRecord);
+
+ return ret;
+}
+
+static int Dtls13SendFragmentedInternal(WOLFSSL* ssl)
+{
+ int fragLength, rlHeaderLength;
+ int remainingSize, maxFragment;
+ int recordLength;
+ byte isEncrypted;
+ byte* output;
+ int ret;
+
+ isEncrypted = Dtls13TypeIsEncrypted(
+ (enum HandShakeType)ssl->dtls13FragHandshakeType);
+ rlHeaderLength = Dtls13GetRlHeaderLength(isEncrypted);
+ maxFragment = wolfSSL_GetMaxFragSize(ssl, MAX_RECORD_SIZE);
+
+ remainingSize = ssl->dtls13MessageLength - ssl->dtls13FragOffset;
+
+ while (remainingSize > 0) {
+
+ fragLength = maxFragment - rlHeaderLength - DTLS_HANDSHAKE_HEADER_SZ;
+
+ recordLength = maxFragment;
+
+ if (fragLength > remainingSize) {
+ fragLength = remainingSize;
+ recordLength =
+ fragLength + rlHeaderLength + DTLS_HANDSHAKE_HEADER_SZ;
+ }
+
+ ret = CheckAvailableSize(ssl, recordLength + MAX_MSG_EXTRA);
+ if (ret != 0)
+ return ret;
+
+ output =
+ ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.length;
+
+ ret = Dtls13HandshakeAddHeaderFrag(ssl, output + rlHeaderLength,
+ (enum HandShakeType)ssl->dtls13FragHandshakeType,
+ ssl->dtls13FragOffset, fragLength, ssl->dtls13MessageLength);
+ if (ret != 0) {
+ Dtls13FreeFragmentsBuffer(ssl);
+ return ret;
+ }
+
+ XMEMCPY(output + rlHeaderLength + DTLS_HANDSHAKE_HEADER_SZ,
+ ssl->dtls13FragmentsBuffer.buffer + ssl->dtls13FragOffset,
+ fragLength);
+
+ ret = Dtls13SendOneFragmentRtx(ssl,
+ (enum HandShakeType)ssl->dtls13FragHandshakeType,
+ recordLength + MAX_MSG_EXTRA, output, recordLength, 0);
+ if (ret == WANT_WRITE) {
+ ssl->dtls13FragOffset += fragLength;
+ return ret;
+ }
+
+ if (ret != 0) {
+ Dtls13FreeFragmentsBuffer(ssl);
+ return ret;
+ }
+
+ ssl->dtls13FragOffset += fragLength;
+ remainingSize -= fragLength;
+ }
+
+ /* we sent all fragments */
+ Dtls13FreeFragmentsBuffer(ssl);
+ return 0;
+}
+
+static int Dtls13SendFragmented(WOLFSSL* ssl, byte* message, word16 length,
+ enum HandShakeType handshake_type, int hash_output)
+{
+ int rlHeaderLength;
+ byte isEncrypted;
+ int messageSize;
+ int ret;
+
+ if (ssl->dtls13SendingFragments != 0) {
+ WOLFSSL_MSG(
+ "dtls13_send_fragmented() invoked while already sending fragments");
+ return BAD_STATE_E;
+ }
+
+ isEncrypted = Dtls13TypeIsEncrypted(handshake_type);
+ rlHeaderLength = Dtls13GetRlHeaderLength(isEncrypted);
+
+ if (length < rlHeaderLength)
+ return INCOMPLETE_DATA;
+
+ /* DTLSv1.3 do not consider fragmentation for hash transcript. Build the
+ hash now pretending fragmentation will not happen */
+ if (hash_output) {
+ ret = Dtls13HashHandshake(ssl, message + rlHeaderLength,
+ length - rlHeaderLength);
+ if (ret != 0)
+ return ret;
+ }
+
+ messageSize = length - rlHeaderLength - DTLS_HANDSHAKE_HEADER_SZ;
+
+ ssl->dtls13FragmentsBuffer.buffer =
+ (byte*)XMALLOC(messageSize, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
+
+ if (ssl->dtls13FragmentsBuffer.buffer == NULL)
+ return MEMORY_E;
+
+ XMEMCPY(ssl->dtls13FragmentsBuffer.buffer,
+ message + rlHeaderLength + DTLS_HANDSHAKE_HEADER_SZ, messageSize);
+
+ ssl->dtls13MessageLength = messageSize;
+ ssl->dtls13FragHandshakeType = handshake_type;
+ ssl->dtls13SendingFragments = 1;
+
+ return Dtls13SendFragmentedInternal(ssl);
+}
+
+static WC_INLINE word8 Dtls13GetEpochBits(w64wrapper epoch)
+{
+ return w64GetLow32(epoch) & EE_MASK;
+}
+
+/**
+ * dtls13RlAddCiphertextHeader() - add record layer header in the buffer
+ * @ssl: ssl object
+ * @out: output buffer where to put the header
+ * @length: length of the record
+ */
+int Dtls13RlAddCiphertextHeader(WOLFSSL* ssl, byte* out, word16 length)
+{
+ Dtls13RecordCiphertextHeader* hdr;
+ word16 seqNumber;
+
+ if (out == NULL)
+ return BAD_FUNC_ARG;
+
+ if (ssl->dtls13EncryptEpoch == NULL)
+ return BAD_STATE_E;
+
+ hdr = (Dtls13RecordCiphertextHeader*)out;
+
+ hdr->unifiedHdrFlags = DTLS13_FIXED_BITS;
+ hdr->unifiedHdrFlags |=
+ Dtls13GetEpochBits(ssl->dtls13EncryptEpoch->epochNumber);
+
+ /* include 16-bit seq */
+ hdr->unifiedHdrFlags |= DTLS13_SEQ_LEN_BIT;
+ /* include 16-bit length */
+ hdr->unifiedHdrFlags |= DTLS13_LEN_BIT;
+
+ seqNumber = (word16)w64GetLow32(ssl->dtls13EncryptEpoch->nextSeqNumber);
+ c16toa(seqNumber, hdr->sequenceNumber);
+ c16toa(length, hdr->length);
+
+ return 0;
+}
+
+/**
+ * Dtls13HandshakeAddHeader() - add handshake layer header
+ * @ssl: ssl object
+ * @output: output buffer
+ * @msg_type: handshake type
+ * @length: length of the message
+ */
+int Dtls13HandshakeAddHeader(WOLFSSL* ssl, byte* output,
+ enum HandShakeType msg_type, word32 length)
+{
+ Dtls13HandshakeHeader* hdr;
+
+ hdr = (Dtls13HandshakeHeader*)output;
+
+ hdr->msg_type = msg_type;
+ c32to24((word32)length, hdr->length);
+ c16toa(ssl->keys.dtls_handshake_number, hdr->messageSeq);
+
+ /* send unfragmented first */
+ c32to24(0, hdr->fragmentOffset);
+ c32to24((word32)length, hdr->fragmentLength);
+
+ return 0;
+}
+
+/**
+ * Dtls13EncryptRecordNumber() - encrypt record number in the header
+ * @ssl: ssl object
+ * @hdr: header
+ *
+ * Further info rfc draft 43 sec 4.2.3
+ */
+int Dtls13EncryptRecordNumber(WOLFSSL* ssl, byte* hdr, word16 recordLength)
+{
+ int seqLength;
+ int hdrLength;
+
+ if (ssl == NULL || hdr == NULL)
+ return BAD_FUNC_ARG;
+
+ /* we need at least a 16 bytes of ciphertext to encrypt record number see
+ 4.2.3*/
+ if (recordLength < Dtls13GetRlHeaderLength(1) + DTLS13_MIN_CIPHERTEXT)
+ return BUFFER_ERROR;
+
+ seqLength = (*hdr & DTLS13_LEN_BIT) ? DTLS13_SEQ_16_LEN : DTLS13_SEQ_8_LEN;
+
+ /* header flags + seq number */
+ hdrLength = 1 + seqLength;
+
+ /* length present */
+ if (*hdr & DTLS13_LEN_BIT)
+ hdrLength += DTLS13_LEN_SIZE;
+
+ return Dtls13EncryptDecryptRecordNumber(ssl,
+ /* seq number offset */
+ hdr + 1,
+ /* seq size */
+ seqLength,
+ /* cipher text */
+ hdr + hdrLength, PROTECT);
+}
+
+/**
+ * Dtls13GetRlHeaderLength() - get record layer header length
+ * @ssl: ssl object
+ * @isEncrypted: whether the record will be protected or not
+ *
+ * returns the length of the record layer header in bytes.
+ */
+word16 Dtls13GetRlHeaderLength(byte isEncrypted)
+{
+ /* the function looks useless but allow to support variable length unified
+ header in the future */
+ if (!isEncrypted)
+ return DTLS_RECORD_HEADER_SZ;
+
+ return DTLS13_UNIFIED_HEADER_SIZE;
+}
+
+/**
+ * Dtls13GetHeadersLength() - return length of record + handshake header
+ * @type: type of handshake in the message
+ */
+word16 Dtls13GetHeadersLength(enum HandShakeType type)
+{
+ byte isEncrypted;
+
+ isEncrypted = Dtls13TypeIsEncrypted(type);
+
+ return Dtls13GetRlHeaderLength(isEncrypted) + DTLS_HANDSHAKE_HEADER_SZ;
+}
+
+/**
+ * Dtls13IsUnifiedHeader() - check if header is a DTLS unified header
+ * @header_flags: first byte of the header
+ *
+ * Further info: dtls v1.3 draft43 section 4
+ */
+int Dtls13IsUnifiedHeader(byte hdrFirstByte)
+{
+ if (hdrFirstByte == alert || hdrFirstByte == handshake ||
+ hdrFirstByte == ack)
+ return 0;
+
+ return ((hdrFirstByte & DTLS13_FIXED_BITS_MASK) == DTLS13_FIXED_BITS);
+}
+
+int Dtls13ReconstructSeqNumber(WOLFSSL* ssl, Dtls13UnifiedHdrInfo* hdrInfo,
+ w64wrapper* out)
+{
+ word16 expectedLowBits;
+ word16 seqLowBits;
+ w64wrapper temp;
+ word32 out32;
+ word32 shift;
+ word16 mask;
+ byte wrap = 0;
+
+ if (hdrInfo->seqHiPresent) {
+ seqLowBits = (hdrInfo->seqHi << 8) | hdrInfo->seqLo;
+ mask = 0xffff;
+ shift = (1 << 16);
+ }
+ else {
+ seqLowBits = hdrInfo->seqLo;
+ mask = 0xff;
+ shift = (1 << 8);
+ }
+
+ /* *out = (nextPeerSeqNumber & ~mask) | seqLowbits */
+ out32 = w64GetLow32(ssl->dtls13DecryptEpoch->nextPeerSeqNumber);
+ expectedLowBits = out32 & mask;
+ out32 = (out32 & ~mask) | seqLowBits;
+ *out = ssl->dtls13DecryptEpoch->nextPeerSeqNumber;
+ w64SetLow32(out, out32);
+ if (seqLowBits >= expectedLowBits) {
+ if ((word32)(seqLowBits - expectedLowBits) > shift / 2) {
+ temp = w64Sub32(*out, shift, &wrap);
+ if (!wrap)
+ *out = temp;
+ return 0;
+ }
+ }
+ else {
+ /* seqLowbits < expectedLowBits */
+ if ((word32)(expectedLowBits - seqLowBits) > shift / 2) {
+ temp = w64Add32(*out, shift, &wrap);
+ if (!wrap)
+ *out = temp;
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+int Dtls13ReconstructEpochNumber(WOLFSSL* ssl, byte epochBits,
+ w64wrapper* epoch)
+{
+ w64wrapper _epoch;
+ Dtls13Epoch* e;
+ byte found;
+ int i;
+
+ if (Dtls13GetEpochBits(ssl->dtls13PeerEpoch) == epochBits) {
+ *epoch = ssl->dtls13PeerEpoch;
+ return 0;
+ }
+
+ w64Zero(&_epoch);
+
+ for (i = 0; i < DTLS13_EPOCH_SIZE; ++i) {
+ e = &ssl->dtls13Epochs[i];
+
+ if (!e->isValid)
+ continue;
+
+ if (Dtls13GetEpochBits(e->epochNumber) != epochBits)
+ continue;
+
+ if (w64GT(e->epochNumber, _epoch)) {
+ found = 1;
+ _epoch = e->epochNumber;
+ }
+ }
+
+ if (found) {
+ *epoch = _epoch;
+ return 0;
+ }
+
+ return SEQUENCE_ERROR;
+}
+
+/**
+ * Dtls13ParseUnifiedRecordLayer() - parse DTLS unified header
+ * @ssl: [in] ssl object
+ * @input: [in] buffer where the header is
+ * @inputSize: [in] size of the input buffer
+ * @hdrInfo: [out] header info struct
+ *
+ * It parse the header and put the relevant information inside @hdrInfo. Further
+ * info: draft43 section 4
+ *
+ * return 0 on success
+ */
+int Dtls13ParseUnifiedRecordLayer(WOLFSSL* ssl, const byte* input,
+ word16 inputSize, Dtls13UnifiedHdrInfo* hdrInfo)
+{
+ byte seqLen, hasLength;
+ byte* seqNum;
+ word16 idx;
+ int ret;
+
+ if (input == NULL || inputSize == 0)
+ return BAD_FUNC_ARG;
+
+ if (*input & DTLS13_CID_BIT) {
+ WOLFSSL_MSG("DTLS1.3 header with connection ID. Not supported");
+ return WOLFSSL_NOT_IMPLEMENTED;
+ }
+
+ idx = DTLS13_HDR_FLAGS_SIZE;
+
+ seqLen = (*input & DTLS13_SEQ_LEN_BIT) != 0 ? DTLS13_SEQ_16_LEN
+ : DTLS13_SEQ_8_LEN;
+ hasLength = *input & DTLS13_LEN_BIT;
+ hdrInfo->epochBits = *input & EE_MASK;
+
+ idx += seqLen;
+
+ if (inputSize < idx)
+ return BUFFER_ERROR;
+
+ if (hasLength) {
+ if (inputSize < idx + DTLS13_LEN_SIZE)
+ return BUFFER_ERROR;
+
+ ato16(input + idx, &hdrInfo->recordLength);
+ idx += DTLS13_LEN_SIZE;
+
+ /* DTLS message must fit inside a datagram */
+ if (inputSize < idx + hdrInfo->recordLength)
+ return LENGTH_ERROR;
+ }
+ else {
+ /* length not present. The size of the record is the all the remaining
+ data received with this datagram */
+ hdrInfo->recordLength = inputSize - idx;
+ }
+
+ /* minimum size for a dtls1.3 packet is 16 bytes (to have enough ciphertext
+ to create record number xor mask). (draft 43 - Sec 4.2.3) */
+ if (hdrInfo->recordLength < DTLS13_RN_MASK_SIZE)
+ return LENGTH_ERROR;
+
+ seqNum = (byte*)(input + DTLS13_HDR_FLAGS_SIZE);
+
+ ret = Dtls13EncryptDecryptRecordNumber(ssl, seqNum, seqLen, input + idx,
+ DEPROTECT);
+ if (ret != 0)
+ return ret;
+
+ hdrInfo->headerLength = idx;
+
+ if (seqLen == DTLS13_SEQ_16_LEN) {
+ hdrInfo->seqHiPresent = 1;
+ hdrInfo->seqHi = seqNum[0];
+ hdrInfo->seqLo = seqNum[1];
+ }
+ else {
+ hdrInfo->seqHiPresent = 0;
+ hdrInfo->seqLo = seqNum[0];
+ }
+
+ return 0;
+}
+
+int Dtls13RecordRecvd(WOLFSSL* ssl)
+{
+ int ret;
+
+ if (ssl->curRL.type != handshake)
+ return 0;
+
+ if (!ssl->options.dtls13SendMoreAcks)
+ ssl->dtls13FastTimeout = 1;
+
+ ret = Dtls13RtxAddAck(ssl, ssl->keys.curEpoch64, ssl->keys.curSeq);
+ if (ret != 0)
+ WOLFSSL_MSG("can't save ack fragment");
+
+ return ret;
+}
+
+static void Dtls13RtxMoveToEndOfList(WOLFSSL* ssl, Dtls13RtxRecord** prevNext,
+ Dtls13RtxRecord* r)
+{
+ /* already at the end */
+ if (r->next == NULL)
+ return;
+
+ Dtls13RtxRecordUnlink(ssl, prevNext, r);
+ /* add to the end */
+ Dtls13RtxAddRecord(&ssl->dtls13Rtx, r);
+}
+
+static int Dtls13RtxSendBuffered(WOLFSSL* ssl)
+{
+ word16 headerLength;
+ Dtls13RtxRecord *r, **prevNext;
+ w64wrapper seq;
+ byte* output;
+ int isLast;
+ int sendSz;
+ word32 now;
+ int ret;
+
+ WOLFSSL_ENTER("Dtls13RtxSendBuffered");
+
+ now = LowResTimer();
+ if (now - ssl->dtls13Rtx.lastRtx < DTLS13_MIN_RTX_INTERVAL) {
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG("Avoid too fast retransmission");
+#endif /* WOLFSSL_DEBUG_TLS */
+ return 0;
+ }
+
+ ssl->dtls13Rtx.lastRtx = now;
+
+ r = ssl->dtls13Rtx.rtxRecords;
+ prevNext = &ssl->dtls13Rtx.rtxRecords;
+ while (r != NULL) {
+ isLast = r->next == NULL;
+ WOLFSSL_MSG("Dtls13Rtx One Record");
+
+ headerLength = Dtls13GetRlHeaderLength(!w64IsZero(r->epoch));
+
+ sendSz = r->length + headerLength;
+
+ if (!w64IsZero(r->epoch))
+ sendSz += MAX_MSG_EXTRA;
+
+ ret = CheckAvailableSize(ssl, sendSz);
+ if (ret != 0)
+ return ret;
+
+ output =
+ ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.length;
+
+ XMEMCPY(output + headerLength, r->data, r->length);
+
+ if (!w64Equal(ssl->dtls13EncryptEpoch->epochNumber, r->epoch)) {
+ ret = Dtls13SetEpochKeys(ssl, r->epoch, ENCRYPT_SIDE_ONLY);
+ if (ret != 0)
+ return ret;
+ }
+
+ seq = ssl->dtls13EncryptEpoch->nextSeqNumber;
+
+ ret = Dtls13SendFragment(ssl, output, sendSz, r->length + headerLength,
+ (enum HandShakeType)r->handshakeType, 0,
+ isLast || !ssl->options.groupMessages);
+ if (ret != 0 && ret != WANT_WRITE)
+ return ret;
+
+ if (r->rnIdx >= DTLS13_RETRANS_RN_SIZE)
+ r->rnIdx = 0;
+
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG_EX("tracking r hs: %d with seq: %ld", r->handshakeType,
+ seq);
+#endif /* WOLFSSL_DEBUG_TLS */
+
+ r->seq[r->rnIdx] = seq;
+ r->rnIdx++;
+
+ if (ret == WANT_WRITE) {
+ /* this fragment will be sent eventually. Move it to the end of the
+ list so next time we start with a new one. */
+ Dtls13RtxMoveToEndOfList(ssl, prevNext, r);
+ return ret;
+ }
+
+ prevNext = &r->next;
+ r = r->next;
+ }
+
+ return 0;
+}
+
+/**
+ * Dtls13HandshakeRecv() - process an handshake message. Deal with
+ fragmentation if needed
+ * @ssl: [in] ssl object
+ * @input: [in] input buffer
+ * @size: [in] input buffer size
+ * @type: [out] content type
+ * @processedSize: [out] amount of byte processed
+ *
+ * returns 0 on success
+ */
+static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size,
+ word32* processedSize)
+{
+ word32 frag_off, frag_length;
+ byte isComplete, isFirst;
+ word32 message_length;
+ byte handshake_type;
+ word32 idx;
+ int ret;
+
+ idx = 0;
+ ret = GetDtlsHandShakeHeader(ssl, input, &idx, &handshake_type,
+ &message_length, &frag_off, &frag_length, size);
+ if (ret != 0)
+ return PARSE_ERROR;
+
+ if (idx + frag_length > size) {
+ WOLFSSL_ERROR(INCOMPLETE_DATA);
+ return INCOMPLETE_DATA;
+ }
+
+ if (frag_off + frag_length > message_length)
+ return BUFFER_ERROR;
+
+ ret = Dtls13RtxMsgRecvd(ssl, (enum HandShakeType)handshake_type, frag_off);
+ if (ret != 0)
+ return ret;
+
+ if (ssl->keys.dtls_peer_handshake_number <
+ ssl->keys.dtls_expected_peer_handshake_number) {
+
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG(
+ "DTLS1.3 retransmission detected - discard and schedule a rtx");
+#endif /* WOLFSSL_DEBUG_TLS */
+
+ /* ignore the message */
+ *processedSize = idx + frag_length;
+
+ *processedSize += ssl->keys.padSz;
+
+ return 0;
+ }
+
+ isFirst = frag_off == 0;
+ isComplete = isFirst && frag_length == message_length;
+
+ if (!isComplete || ssl->keys.dtls_peer_handshake_number >
+ ssl->keys.dtls_expected_peer_handshake_number) {
+ DtlsMsgStore(ssl, w64GetLow32(ssl->keys.curEpoch64),
+ ssl->keys.dtls_peer_handshake_number,
+ input + DTLS_HANDSHAKE_HEADER_SZ, message_length, handshake_type,
+ frag_off, frag_length, ssl->heap);
+
+ *processedSize = idx + frag_length;
+
+ *processedSize += ssl->keys.padSz;
+
+ if (Dtls13NextMessageComplete(ssl))
+ return Dtls13ProcessBufferedMessages(ssl);
+
+ return 0;
+ }
+
+ ret = DoTls13HandShakeMsgType(ssl, input, &idx, handshake_type,
+ message_length, size);
+ if (ret != 0)
+ return ret;
+
+ Dtls13MsgWasProcessed(ssl, (enum HandShakeType)handshake_type);
+
+ *processedSize = idx;
+
+ /* check if we have buffered some message */
+ if (Dtls13NextMessageComplete(ssl))
+ return Dtls13ProcessBufferedMessages(ssl);
+
+ return 0;
+}
+
+int Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32* inOutIdx,
+ word32 totalSz)
+{
+ word32 maxSize, processedSize = 0;
+ byte* message;
+ int ret;
+
+ message = input + *inOutIdx;
+ maxSize = totalSz - *inOutIdx;
+
+ ret = _Dtls13HandshakeRecv(ssl, message, maxSize, &processedSize);
+
+ *inOutIdx += processedSize;
+
+ return ret;
+}
+
+/**
+ * Dtls13FragmentsContinue() - keep sending pending fragments
+ * @ssl: ssl object
+ */
+int Dtls13FragmentsContinue(WOLFSSL* ssl)
+{
+ int ret;
+
+ ret = Dtls13SendFragmentedInternal(ssl);
+ if (ret == 0)
+ ssl->keys.dtls_handshake_number++;
+
+ return ret;
+}
+
+/**
+ * Dtls13AddHeaders() - setup handshake header
+ * @output: output buffer at the start of the record
+ * @length: length of the full message, included headers
+ * @hsType: handshake type
+ * @ssl: ssl object
+ *
+ * This function add the handshake headers and leaves space for the record
+ * layer. The real record layer will be added in dtls_send() for unprotected
+ * messages and in BuildTls13message() for protected messages.
+ *
+ * returns 0 on success, -1 otherwise
+ */
+int Dtls13AddHeaders(byte* output, word32 length, enum HandShakeType hsType,
+ WOLFSSL* ssl)
+{
+ word16 handshakeOffset;
+ int isEncrypted;
+
+ isEncrypted = Dtls13TypeIsEncrypted(hsType);
+ handshakeOffset = Dtls13GetRlHeaderLength(isEncrypted);
+
+ /* The record header is placed by either Dtls13HandshakeSend() or
+ BuildTls13Message() */
+
+ return Dtls13HandshakeAddHeader(ssl, output + handshakeOffset, hsType,
+ length);
+}
+
+/**
+ * Dtls13HandshakeSend() - send an handshake message. Fragment if necessary.
+ *
+ * @ssl: ssl object
+ * @message: message where the buffer is in. Handshake header already in place.
+ * @output_size: size of the @message buffer
+ * @length: length of the message including headers
+ * @handshakeType: handshake type of the message
+ * @hashOutput: if true add the message to the transcript hash
+ *
+ */
+int Dtls13HandshakeSend(WOLFSSL* ssl, byte* message, word16 outputSize,
+ word16 length, enum HandShakeType handshakeType, int hashOutput)
+{
+ int maxFrag;
+ int maxLen;
+ int ret;
+
+ if (ssl->dtls13EncryptEpoch == NULL)
+ return BAD_STATE_E;
+
+ /* if we are here, the message is built */
+ ssl->options.buildingMsg = 0;
+
+ if (!ssl->options.handShakeDone) {
+
+ /* during the handshake, if we are sending a new flight, we can flush
+ our ACK list. When sending client
+ [certificate/certificate_verify]/finished flight, we may flush an ACK
+ for a newSessionticket message, sent by the server just after sending
+ its finished message. This should not be a problem. That message
+ arrived out-of-order (before the server finished) so likely an ACK
+ was already sent. In the worst case we will ACK the server
+ retranmission*/
+ if (handshakeType == certificate || handshakeType == finished ||
+ handshakeType == server_hello || handshakeType == client_hello)
+ Dtls13RtxFlushAcks(ssl);
+ }
+
+ /* we want to send always with the highest epoch */
+ if (!w64Equal(ssl->dtls13EncryptEpoch->epochNumber, ssl->dtls13Epoch)) {
+ ret = Dtls13SetEpochKeys(ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY);
+ if (ret != 0)
+ return ret;
+ }
+
+ maxFrag = wolfSSL_GetMaxFragSize(ssl, MAX_RECORD_SIZE);
+ maxLen = length;
+
+ if (handshakeType == key_update)
+ ssl->dtls13WaitKeyUpdateAck = 1;
+
+ if (maxLen < maxFrag) {
+ ret = Dtls13SendOneFragmentRtx(ssl, handshakeType, outputSize, message,
+ length, hashOutput);
+ if (ret == 0 || ret == WANT_WRITE)
+ ssl->keys.dtls_handshake_number++;
+ }
+ else {
+ ret = Dtls13SendFragmented(ssl, message, length, handshakeType,
+ hashOutput);
+ if (ret == 0)
+ ssl->keys.dtls_handshake_number++;
+ }
+
+ return ret;
+}
+
+#define SN_LABEL_SZ 2
+static const byte snLabel[SN_LABEL_SZ + 1] = "sn";
+
+/**
+ * Dtls13DeriveSnKeys() - derive the key used to encrypt the record number
+ * @ssl: ssl object
+ * @provision: which side (CLIENT or SERVER) to provision
+ */
+int Dtls13DeriveSnKeys(WOLFSSL* ssl, int provision)
+{
+ byte key_dig[MAX_PRF_DIG];
+ int ret = 0;
+
+ if (provision & PROVISION_CLIENT) {
+ WOLFSSL_MSG("Derive SN Client key");
+ ret = Tls13DeriveKey(ssl, key_dig, ssl->specs.key_size,
+ ssl->clientSecret, snLabel, SN_LABEL_SZ, ssl->specs.mac_algorithm,
+ 0);
+ if (ret != 0)
+ goto end;
+
+ XMEMCPY(ssl->keys.client_sn_key, key_dig, ssl->specs.key_size);
+ }
+
+ if (provision & PROVISION_SERVER) {
+ WOLFSSL_MSG("Derive SN Server key");
+ ret = Tls13DeriveKey(ssl, key_dig, ssl->specs.key_size,
+ ssl->serverSecret, snLabel, SN_LABEL_SZ, ssl->specs.mac_algorithm,
+ 0);
+ if (ret != 0)
+ goto end;
+
+ XMEMCPY(ssl->keys.server_sn_key, key_dig, ssl->specs.key_size);
+ }
+
+end:
+ ForceZero(key_dig, MAX_PRF_DIG);
+ return ret;
+}
+
+static int Dtls13InitAesCipher(WOLFSSL* ssl, RecordNumberCiphers* cipher,
+ const byte* key, word16 keySize)
+{
+ int ret;
+ if (cipher->aes == NULL) {
+ cipher->aes =
+ (Aes*)XMALLOC(sizeof(Aes), ssl->heap, DYNAMIC_TYPE_CIPHER);
+ if (cipher->aes == NULL)
+ return MEMORY_E;
+ }
+ else {
+ wc_AesFree(cipher->aes);
+ }
+
+ XMEMSET(cipher->aes, 0, sizeof(*cipher->aes));
+
+ ret = wc_AesInit(cipher->aes, ssl->heap, INVALID_DEVID);
+ if (ret != 0)
+ return ret;
+
+ return wc_AesSetKey(cipher->aes, key, keySize, NULL, AES_ENCRYPTION);
+}
+
+#ifdef HAVE_CHACHA
+static int Dtls13InitChaChaCipher(RecordNumberCiphers* c, byte* key,
+ word16 keySize, void* heap)
+{
+ (void)heap;
+
+ if (c->chacha == NULL) {
+ c->chacha = (ChaCha*)XMALLOC(sizeof(ChaCha), heap, DYNAMIC_TYPE_CIPHER);
+
+ if (c->chacha == NULL)
+ return MEMORY_E;
+ }
+
+ return wc_Chacha_SetKey(c->chacha, key, keySize);
+}
+#endif /* HAVE_CHACHA */
+
+struct Dtls13Epoch* Dtls13GetEpoch(WOLFSSL* ssl, w64wrapper epochNumber)
+{
+ Dtls13Epoch* e;
+ int i;
+
+ for (i = 0; i < DTLS13_EPOCH_SIZE; ++i) {
+ e = &ssl->dtls13Epochs[i];
+ if (w64Equal(e->epochNumber, epochNumber) && e->isValid)
+ return e;
+ }
+
+ return NULL;
+}
+
+static void Dtls13EpochCopyKeys(WOLFSSL* ssl, Dtls13Epoch* e, Keys* k, int side)
+{
+ byte clientWrite, serverWrite;
+ byte enc, dec;
+
+ WOLFSSL_ENTER("Dtls13SetEpochKeys");
+
+ clientWrite = serverWrite = 0;
+ enc = dec = 0;
+ switch (side) {
+
+ case ENCRYPT_SIDE_ONLY:
+ if (ssl->options.side == WOLFSSL_CLIENT_END)
+ clientWrite = 1;
+ if (ssl->options.side == WOLFSSL_SERVER_END)
+ serverWrite = 1;
+ enc = 1;
+ break;
+
+ case DECRYPT_SIDE_ONLY:
+ if (ssl->options.side == WOLFSSL_CLIENT_END)
+ serverWrite = 1;
+ if (ssl->options.side == WOLFSSL_SERVER_END)
+ clientWrite = 1;
+ dec = 1;
+ break;
+
+ case ENCRYPT_AND_DECRYPT_SIDE:
+ clientWrite = serverWrite = 1;
+ enc = dec = 1;
+ break;
+ }
+
+ if (clientWrite) {
+ XMEMCPY(e->client_write_key, k->client_write_key,
+ sizeof(e->client_write_key));
+
+ XMEMCPY(e->client_write_IV, k->client_write_IV,
+ sizeof(e->client_write_IV));
+
+ XMEMCPY(e->client_sn_key, k->client_sn_key, sizeof(e->client_sn_key));
+ }
+
+ if (serverWrite) {
+ XMEMCPY(e->server_write_key, k->server_write_key,
+ sizeof(e->server_write_key));
+ XMEMCPY(e->server_write_IV, k->server_write_IV,
+ sizeof(e->server_write_IV));
+ XMEMCPY(e->server_sn_key, k->server_sn_key, sizeof(e->server_sn_key));
+ }
+
+ if (enc)
+ XMEMCPY(e->aead_enc_imp_IV, k->aead_enc_imp_IV,
+ sizeof(e->aead_enc_imp_IV));
+
+ if (dec)
+ XMEMCPY(e->aead_dec_imp_IV, k->aead_dec_imp_IV,
+ sizeof(e->aead_dec_imp_IV));
+}
+
+/* For storing the sequence number we use a word32[2] array here, instead of
+ word64. This is to reuse existing code */
+int Dtls13GetSeq(WOLFSSL* ssl, int order, word32* seq, byte increment)
+{
+ w64wrapper* nativeSeq;
+
+ if (order == PEER_ORDER) {
+ nativeSeq = &ssl->keys.curSeq;
+ /* never increment seq number for curent record. In DTLS seq number are
+ explicit */
+ increment = 0;
+ }
+ else if (order == CUR_ORDER) {
+
+ if (ssl->dtls13EncryptEpoch == NULL) {
+ return BAD_STATE_E;
+ }
+
+ nativeSeq = &ssl->dtls13EncryptEpoch->nextSeqNumber;
+ }
+ else {
+ return BAD_FUNC_ARG;
+ }
+
+ seq[0] = w64GetHigh32(*nativeSeq);
+ seq[1] = w64GetLow32(*nativeSeq);
+
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG_EX("Dtls13GetSeq(): using seq: %ld", *nativeSeq);
+#endif /* WOLFSSL_DEBUG_TLS */
+
+ if (increment) {
+ w64Increment(nativeSeq);
+
+ /* seq number wrapped up */
+ if (w64IsZero(*nativeSeq))
+ return BAD_STATE_E;
+ }
+
+ return 0;
+}
+
+static Dtls13Epoch* Dtls13NewEpochSlot(WOLFSSL* ssl)
+{
+ Dtls13Epoch *e, *oldest = NULL;
+ w64wrapper oldestNumber;
+ int i;
+
+ /* FIXME: add max function */
+ oldestNumber = w64From32((word32)-1, (word32)-1);
+ oldest = NULL;
+
+ for (i = 0; i < DTLS13_EPOCH_SIZE; ++i) {
+ e = &ssl->dtls13Epochs[i];
+ if (!e->isValid)
+ return e;
+
+ if (!w64Equal(e->epochNumber, ssl->dtls13Epoch) &&
+ !w64Equal(e->epochNumber, ssl->dtls13PeerEpoch) &&
+ w64LT(e->epochNumber, oldestNumber))
+ oldest = e;
+ }
+
+ if (oldest == NULL)
+ return NULL;
+
+ e = oldest;
+
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG_EX("Delete epoch: %d", e->epochNumber);
+#endif /* WOLFSSL_DEBUG_TLS */
+
+ XMEMSET(e, 0, sizeof(*e));
+
+ return e;
+}
+
+int Dtls13NewEpoch(WOLFSSL* ssl, w64wrapper epochNumber, int side)
+{
+ Dtls13Epoch* e;
+
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG_EX("New epoch: %d", w64GetLow32(epochNumber));
+#endif /* WOLFSSL_DEBUG_TLS */
+
+ e = Dtls13GetEpoch(ssl, epochNumber);
+ if (e == NULL) {
+ e = Dtls13NewEpochSlot(ssl);
+ if (e == NULL)
+ return BAD_STATE_E;
+ }
+
+ Dtls13EpochCopyKeys(ssl, e, &ssl->keys, side);
+
+ if (!e->isValid) {
+ /* fresh epoch, initialize fields */
+ e->epochNumber = epochNumber;
+ e->isValid = 1;
+ e->side = side;
+ }
+ else if (e->side != side) {
+ /* epoch used for the other side already. update side */
+ e->side = ENCRYPT_AND_DECRYPT_SIDE;
+ }
+
+ return 0;
+}
+
+int Dtls13SetEpochKeys(WOLFSSL* ssl, w64wrapper epochNumber,
+ enum encrypt_side side)
+{
+ byte clientWrite, serverWrite;
+ Dtls13Epoch* e;
+ byte enc, dec;
+
+ WOLFSSL_ENTER("Dtls13SetEpochKeys");
+
+ clientWrite = serverWrite = 0;
+ enc = dec = 0;
+ switch (side) {
+
+ case ENCRYPT_SIDE_ONLY:
+ if (ssl->options.side == WOLFSSL_CLIENT_END)
+ clientWrite = 1;
+ if (ssl->options.side == WOLFSSL_SERVER_END)
+ serverWrite = 1;
+ enc = 1;
+ break;
+
+ case DECRYPT_SIDE_ONLY:
+ if (ssl->options.side == WOLFSSL_CLIENT_END)
+ serverWrite = 1;
+ if (ssl->options.side == WOLFSSL_SERVER_END)
+ clientWrite = 1;
+ dec = 1;
+ break;
+
+ case ENCRYPT_AND_DECRYPT_SIDE:
+ clientWrite = serverWrite = 1;
+ enc = dec = 1;
+ break;
+ }
+
+ e = Dtls13GetEpoch(ssl, epochNumber);
+ /* we don't have the requested key */
+ if (e == NULL)
+ return BAD_STATE_E;
+
+ if (e->side != ENCRYPT_AND_DECRYPT_SIDE && e->side != side)
+ return BAD_STATE_E;
+
+ if (enc)
+ ssl->dtls13EncryptEpoch = e;
+ if (dec)
+ ssl->dtls13DecryptEpoch = e;
+
+ /* epoch 0 has no key to copy */
+ if (w64IsZero(epochNumber))
+ return 0;
+
+ if (clientWrite) {
+ XMEMCPY(ssl->keys.client_write_key, e->client_write_key,
+ sizeof(ssl->keys.client_write_key));
+
+ XMEMCPY(ssl->keys.client_write_IV, e->client_write_IV,
+ sizeof(ssl->keys.client_write_IV));
+
+ XMEMCPY(ssl->keys.client_sn_key, e->client_sn_key,
+ sizeof(ssl->keys.client_sn_key));
+ }
+
+ if (serverWrite) {
+ XMEMCPY(ssl->keys.server_write_key, e->server_write_key,
+ sizeof(ssl->keys.server_write_key));
+
+ XMEMCPY(ssl->keys.server_write_IV, e->server_write_IV,
+ sizeof(ssl->keys.server_write_IV));
+
+ XMEMCPY(ssl->keys.server_sn_key, e->server_sn_key,
+ sizeof(ssl->keys.server_sn_key));
+ }
+
+ if (enc)
+ XMEMCPY(ssl->keys.aead_enc_imp_IV, e->aead_enc_imp_IV,
+ sizeof(ssl->keys.aead_enc_imp_IV));
+ if (dec)
+ XMEMCPY(ssl->keys.aead_dec_imp_IV, e->aead_dec_imp_IV,
+ sizeof(ssl->keys.aead_dec_imp_IV));
+
+ return SetKeysSide(ssl, side);
+}
+
+int Dtls13SetRecordNumberKeys(WOLFSSL* ssl, enum encrypt_side side)
+{
+ RecordNumberCiphers* enc = NULL;
+ RecordNumberCiphers* dec = NULL;
+ byte *encKey, *decKey;
+ int ret;
+
+ if (ssl == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ switch (side) {
+ case ENCRYPT_SIDE_ONLY:
+ enc = &ssl->dtlsRecordNumberEncrypt;
+ break;
+ case DECRYPT_SIDE_ONLY:
+ dec = &ssl->dtlsRecordNumberDecrypt;
+ break;
+ case ENCRYPT_AND_DECRYPT_SIDE:
+ enc = &ssl->dtlsRecordNumberEncrypt;
+ dec = &ssl->dtlsRecordNumberDecrypt;
+ break;
+ }
+
+ if (enc) {
+ if (ssl->options.side == WOLFSSL_CLIENT_END)
+ encKey = ssl->keys.client_sn_key;
+ else
+ encKey = ssl->keys.server_sn_key;
+ }
+
+ if (dec) {
+ if (ssl->options.side == WOLFSSL_CLIENT_END)
+ decKey = ssl->keys.server_sn_key;
+ else
+ decKey = ssl->keys.client_sn_key;
+ }
+
+ /* DTLSv1.3 supports only AEAD algorithm. */
+#if defined(BUILD_AESGCM) || defined(HAVE_AESCCM)
+ if (ssl->specs.bulk_cipher_algorithm == wolfssl_aes_gcm ||
+ ssl->specs.bulk_cipher_algorithm == wolfssl_aes_ccm) {
+
+ if (enc) {
+ ret = Dtls13InitAesCipher(ssl, enc, encKey, ssl->specs.key_size);
+ if (ret != 0)
+ return ret;
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG("Provisioning AES Record Number enc key:");
+ WOLFSSL_BUFFER(encKey, ssl->specs.key_size);
+#endif /* WOLFSSL_DEBUG_TLS */
+ }
+
+ if (dec) {
+ ret = Dtls13InitAesCipher(ssl, dec, decKey, ssl->specs.key_size);
+ if (ret != 0)
+ return ret;
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG("Provisioning AES Record Number dec key:");
+ WOLFSSL_BUFFER(decKey, ssl->specs.key_size);
+#endif /* WOLFSSL_DEBUG_TLS */
+ }
+
+ return 0;
+ }
+#endif /* BUILD_AESGCM || HAVE_AESCCM */
+
+#ifdef HAVE_CHACHA
+ if (ssl->specs.bulk_cipher_algorithm == wolfssl_chacha) {
+ if (enc) {
+ ret = Dtls13InitChaChaCipher(enc, encKey, ssl->specs.key_size,
+ ssl->heap);
+ if (ret != 0)
+ return ret;
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG("Provisioning CHACHA Record Number enc key:");
+ WOLFSSL_BUFFER(encKey, ssl->specs.key_size);
+#endif /* WOLFSSL_DEBUG_TLS */
+ }
+
+ if (dec) {
+ ret = Dtls13InitChaChaCipher(dec, decKey, ssl->specs.key_size,
+ ssl->heap);
+ if (ret != 0)
+ return ret;
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG("Provisioning CHACHA Record Number dec key:");
+ WOLFSSL_BUFFER(decKey, ssl->specs.key_size);
+#endif /* WOLFSSL_DEBUG_TLS */
+ }
+
+ return 0;
+ }
+#endif /* HAVE_CHACHA */
+
+ return NOT_COMPILED_IN;
+}
+
+/* 64 bits epoch + 64 bits sequence */
+#define DTLS13_RN_SIZE 16
+
+static int Dtls13GetAckListLength(Dtls13RecordNumber* list, word16* length)
+{
+ int numberElements;
+
+ numberElements = 0;
+
+ /* TODO: check that we don't exceed the maximum length */
+
+ while (list != NULL) {
+ list = list->next;
+ numberElements++;
+ }
+
+ *length = DTLS13_RN_SIZE * numberElements;
+ return 0;
+}
+
+static int Dtls13WriteAckMessage(WOLFSSL* ssl,
+ Dtls13RecordNumber* recordNumberList, word32* length)
+{
+ word16 msgSz, headerLength;
+ byte *output, *ackMessage;
+ word32 sendSz;
+ int ret;
+
+ sendSz = 0;
+
+ if (ssl->dtls13EncryptEpoch == NULL)
+ return BAD_STATE_E;
+
+ if (w64IsZero(ssl->dtls13EncryptEpoch->epochNumber)) {
+ /* unprotected ACK */
+ headerLength = DTLS_RECORD_HEADER_SZ;
+ ;
+ }
+ else {
+ headerLength = Dtls13GetRlHeaderLength(1);
+ sendSz += MAX_MSG_EXTRA;
+ }
+
+ ret = Dtls13GetAckListLength(recordNumberList, &msgSz);
+ if (ret != 0)
+ return ret;
+
+ sendSz += headerLength;
+
+ /* ACK list 2 bytes length field */
+ sendSz += OPAQUE16_LEN;
+
+ /* ACK list */
+ sendSz += msgSz;
+
+ ret = CheckAvailableSize(ssl, sendSz);
+ if (ret != 0)
+ return ret;
+
+ output =
+ ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.length;
+
+ ackMessage = output + headerLength;
+
+ c16toa(msgSz, ackMessage);
+ ackMessage += OPAQUE16_LEN;
+
+ while (recordNumberList != NULL) {
+ c64toa(&recordNumberList->epoch, ackMessage);
+ ackMessage += OPAQUE64_LEN;
+ c64toa(&recordNumberList->seq, ackMessage);
+ ackMessage += OPAQUE64_LEN;
+ recordNumberList = recordNumberList->next;
+ }
+
+ *length = msgSz + OPAQUE16_LEN;
+
+ return 0;
+}
+
+static int Dtls13RtxIsTrackedByRn(const Dtls13RtxRecord* r, w64wrapper epoch,
+ w64wrapper seq)
+{
+ int i;
+ if (!w64Equal(r->epoch, epoch))
+ return 0;
+
+ for (i = 0; i < r->rnIdx; ++i) {
+ if (w64Equal(r->seq[i], seq))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int Dtls13KeyUpdateAckReceived(WOLFSSL* ssl)
+{
+ int ret;
+ w64Increment(&ssl->dtls13Epoch);
+
+ /* Epoch wrapped up */
+ if (w64IsZero(ssl->dtls13Epoch))
+ return BAD_STATE_E;
+
+ ret = DeriveTls13Keys(ssl, update_traffic_key, ENCRYPT_SIDE_ONLY, 1);
+ if (ret != 0)
+ return ret;
+
+ ret = Dtls13NewEpoch(ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY);
+ if (ret != 0)
+ return ret;
+
+ return Dtls13SetEpochKeys(ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY);
+}
+
+#ifdef WOLFSSL_DEBUG_TLS
+static void Dtls13PrintRtxRecord(Dtls13RtxRecord* r)
+{
+ int i;
+
+ WOLFSSL_MSG_EX("r: hs: %d epoch: %ld", r->handshakeType, r->epoch);
+ for (i = 0; i < r->rnIdx; i++)
+ WOLFSSL_MSG_EX("seq: %ld", r->seq[i]);
+}
+#endif /* WOLFSSL_DEBUG_TLS */
+
+static void Dtls13RtxRemoveRecord(WOLFSSL* ssl, w64wrapper epoch,
+ w64wrapper seq)
+{
+ Dtls13RtxRecord *r, **prevNext;
+
+ prevNext = &ssl->dtls13Rtx.rtxRecords;
+ r = ssl->dtls13Rtx.rtxRecords;
+
+ while (r != NULL) {
+#ifdef WOLFSSL_DEBUG_TLS
+ Dtls13PrintRtxRecord(r);
+#endif /* WOLFSSL_DEBUG_TLS */
+
+ if (Dtls13RtxIsTrackedByRn(r, epoch, seq)) {
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG("removing record");
+#endif /* WOLFSSL_DEBUG_TLS */
+ Dtls13RtxRecordUnlink(ssl, prevNext, r);
+ Dtls13FreeRtxBufferRecord(ssl, r);
+ return;
+ }
+ prevNext = &r->next;
+ r = r->next;
+ }
+
+ return;
+}
+
+int Dtls13DoScheduledWork(WOLFSSL* ssl)
+{
+ int ret;
+
+ WOLFSSL_ENTER("Dtls13DoScheduledWork");
+
+ ssl->dtls13SendingAckOrRtx = 1;
+
+ if (ssl->dtls13Rtx.sendAcks) {
+ ssl->dtls13Rtx.sendAcks = 0;
+ ret = SendDtls13Ack(ssl);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (ssl->dtls13Rtx.retransmit) {
+ ssl->dtls13Rtx.retransmit = 0;
+ ret = Dtls13RtxSendBuffered(ssl);
+ if (ret != 0)
+ return ret;
+ }
+
+ ssl->dtls13SendingAckOrRtx = 0;
+
+ return 0;
+}
+
+/* Send ACKs when available after a timeout but only retransmit the last
+ * flight after a long timeout */
+int Dtls13RtxTimeout(WOLFSSL* ssl)
+{
+ int ret = 0;
+
+ if (ssl->dtls13Rtx.seenRecords != NULL) {
+ ssl->dtls13Rtx.sendAcks = 0;
+ /* reset fast timeout as we are sending ACKs */
+ ssl->dtls13FastTimeout = 0;
+ ret = SendDtls13Ack(ssl);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* we have two timeouts, a shorter (dtls13FastTimeout = 1) and a longer
+ one. When the shorter expires we only send ACKs, as it normally means
+ that some messages we are waiting for dont't arrive yet. But we
+ retransmit our buffered messages only if the longer timeout
+ expires. fastTimeout is 1/4 of the longer timeout */
+ if (ssl->dtls13FastTimeout) {
+ ssl->dtls13FastTimeout = 0;
+ return 0;
+ }
+
+ return Dtls13RtxSendBuffered(ssl);
+}
+
+static int Dtls13RtxHasKeyUpdateBuffered(WOLFSSL* ssl)
+{
+ Dtls13RtxRecord* r = ssl->dtls13Rtx.rtxRecords;
+
+ while (r != NULL) {
+ if (r->handshakeType == key_update)
+ return 1;
+
+ r = r->next;
+ }
+
+ return 0;
+}
+
+int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize,
+ word32* processedSize)
+{
+ const byte* ackMessage;
+ w64wrapper epoch, seq;
+ word16 length;
+ int ret;
+ int i;
+
+ if (inputSize < OPAQUE16_LEN)
+ return BUFFER_ERROR;
+
+ ato16(input, &length);
+
+ if (inputSize < (word32)(OPAQUE16_LEN + length))
+ return BUFFER_ERROR;
+
+ if (length % (DTLS13_RN_SIZE) != 0)
+ return PARSE_ERROR;
+
+ ackMessage = input + OPAQUE16_LEN;
+ for (i = 0; i < length; i += DTLS13_RN_SIZE) {
+ ato64(ackMessage + i, &epoch);
+ ato64(ackMessage + i + OPAQUE64_LEN, &seq);
+ Dtls13RtxRemoveRecord(ssl, epoch, seq);
+ }
+
+ /* last client flight was completely acknowledged by the server. Handshake
+ is complete. */
+ if (ssl->options.side == WOLFSSL_CLIENT_END &&
+ ssl->options.connectState == WAIT_FINISHED_ACK &&
+ ssl->dtls13Rtx.rtxRecords == NULL) {
+ ssl->options.serverState = SERVER_FINISHED_ACKED;
+ }
+
+ if (ssl->dtls13WaitKeyUpdateAck) {
+ if (!Dtls13RtxHasKeyUpdateBuffered(ssl)) {
+ /* we removed the KeyUpdate message because it was ACKed */
+ ssl->dtls13WaitKeyUpdateAck = 0;
+ ret = Dtls13KeyUpdateAckReceived(ssl);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ *processedSize = length + OPAQUE16_LEN;
+
+ /* After the handshake, not retransmitting here may incur in some extra time
+ in case a post-handshake authentication message is lost, because the ACK
+ mechanism does not shortcut the retransmission timer. If, on the other
+ hand, we retransmit we may do extra retransmissions of unrelated messages
+ in the queue. ex: we send KeyUpdate, CertificateRequest that are
+ unrelated between each other, receiving the ACK for the KeyUpdate will
+ trigger re-sending the CertificateRequest before the timeout.*/
+ /* TODO: be more smart about when doing retransmission looking in the
+ retransmission queue or based on the type of message removed from the
+ seen record list */
+ if (ssl->dtls13Rtx.rtxRecords != NULL)
+ ssl->dtls13Rtx.retransmit = 1;
+
+ return 0;
+}
+
+int SendDtls13Ack(WOLFSSL* ssl)
+{
+ word32 outputSize;
+ int headerSize;
+ word32 length;
+ byte* output;
+ int ret;
+
+ if (ssl->dtls13EncryptEpoch == NULL)
+ return BAD_STATE_E;
+
+ WOLFSSL_ENTER("SendDtls13Ack");
+
+ ret = 0;
+
+ /* The handshake is not complete and the client didn't setup the TRAFFIC0
+ epoch yet */
+ if (ssl->options.side == WOLFSSL_SERVER_END &&
+ !ssl->options.handShakeDone &&
+ w64GTE(ssl->dtls13Epoch, w64From32(0, DTLS13_EPOCH_TRAFFIC0))) {
+ ret = Dtls13SetEpochKeys(ssl, w64From32(0, DTLS13_EPOCH_HANDSHAKE),
+ ENCRYPT_SIDE_ONLY);
+ }
+ else if (!w64Equal(ssl->dtls13Epoch,
+ ssl->dtls13EncryptEpoch->epochNumber)) {
+ ret = Dtls13SetEpochKeys(ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY);
+ }
+
+ if (ret != 0)
+ return ret;
+
+ if (w64IsZero(ssl->dtls13EncryptEpoch->epochNumber)) {
+
+ ret = Dtls13WriteAckMessage(ssl, ssl->dtls13Rtx.seenRecords, &length);
+ if (ret != 0)
+ return ret;
+
+ output =
+ ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.length;
+
+ ret = Dtls13RlAddPlaintextHeader(ssl, output, ack, length);
+ if (ret != 0)
+ return ret;
+
+ ssl->buffers.outputBuffer.length += length + DTLS_RECORD_HEADER_SZ;
+ }
+ else {
+
+ ret = Dtls13WriteAckMessage(ssl, ssl->dtls13Rtx.seenRecords, &length);
+ if (ret != 0)
+ return ret;
+
+ output =
+ ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.length;
+
+ outputSize = ssl->buffers.outputBuffer.bufferSize -
+ ssl->buffers.outputBuffer.length;
+
+ headerSize = Dtls13GetRlHeaderLength(1);
+
+ ret = BuildTls13Message(ssl, output, outputSize, output + headerSize,
+ length, ack, 0, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ ssl->buffers.outputBuffer.length += ret;
+ }
+
+ Dtls13RtxFlushAcks(ssl);
+
+ return SendBuffered(ssl);
+}
+
+static int Dtls13RtxRecordMatchesReqCtx(Dtls13RtxRecord* r, byte* ctx,
+ byte ctxLen)
+{
+ if (r->handshakeType != certificate_request)
+ return 0;
+ if (r->length <= ctxLen + 1)
+ return 0;
+ return XMEMCMP(ctx, r->data + 1, ctxLen) == 0;
+}
+
+int Dtls13RtxProcessingCertificate(WOLFSSL* ssl, byte* input, word32 inputSize)
+{
+ Dtls13RtxRecord* rtxRecord = ssl->dtls13Rtx.rtxRecords;
+ Dtls13RtxRecord** prevNext = &ssl->dtls13Rtx.rtxRecords;
+ byte ctxLength;
+
+ WOLFSSL_ENTER("Dtls13RtxProcessingCertificate");
+
+ if (inputSize <= 1) {
+ WOLFSSL_MSG("Malformed Certificate");
+ return BAD_FUNC_ARG;
+ }
+
+ ctxLength = *input;
+
+ if (inputSize < (word32)ctxLength + OPAQUE8_LEN) {
+ WOLFSSL_MSG("Malformed Certificate");
+ return BAD_FUNC_ARG;
+ }
+
+ while (rtxRecord != NULL) {
+ if (Dtls13RtxRecordMatchesReqCtx(rtxRecord, input + 1, ctxLength)) {
+ Dtls13RtxRecordUnlink(ssl, prevNext, rtxRecord);
+ Dtls13FreeRtxBufferRecord(ssl, rtxRecord);
+ return 0;
+ }
+ prevNext = &rtxRecord->next;
+ rtxRecord = rtxRecord->next;
+ }
+
+ /* This isn't an error since we just can't find a Dtls13RtxRecord that
+ * matches the Request Context. Request Context validity is checked
+ * later. */
+ WOLFSSL_MSG("Can't find any previous Certificate Request");
+ return 0;
+}
+
+int wolfSSL_dtls13_has_pending_msg(WOLFSSL* ssl)
+{
+ return ssl->dtls13Rtx.rtxRecords != NULL;
+}
+
+#endif /* WOLFSSL_DTLS13 */
diff --git a/src/include.am b/src/include.am
index c30935e49..7e3e466aa 100644
--- a/src/include.am
+++ b/src/include.am
@@ -693,6 +693,10 @@ if BUILD_SNIFFER
src_libwolfssl_la_SOURCES += src/sniffer.c
endif
+if BUILD_DTLS13
+src_libwolfssl_la_SOURCES += src/dtls13.c
+endif
+
endif !BUILD_CRYPTONLY
diff --git a/src/internal.c b/src/internal.c
index c1d27b1dc..98fc5cc36 100644
--- a/src/internal.c
+++ b/src/internal.c
@@ -189,13 +189,17 @@ WOLFSSL_CALLBACKS needs LARGE_STATIC_BUFFERS, please add LARGE_STATIC_BUFFERS
#endif
#endif
- static int cipherExtraData(WOLFSSL* ssl);
#ifdef WOLFSSL_DTLS
static WC_INLINE int DtlsCheckWindow(WOLFSSL* ssl);
static WC_INLINE int DtlsUpdateWindow(WOLFSSL* ssl);
#endif
+#ifdef WOLFSSL_DTLS13
+#ifndef WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT
+#define WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT 0
+#endif
+#endif /* WOLFSSL_DTLS13 */
enum processReply {
doProcessInit = 0,
@@ -207,6 +211,7 @@ enum processReply {
verifyEncryptedMessage,
decryptMessage,
verifyMessage,
+ runProcessingOneRecord,
runProcessingOneMessage
};
@@ -495,15 +500,31 @@ int IsAtLeastTLSv1_2(const WOLFSSL* ssl)
int IsAtLeastTLSv1_3(const ProtocolVersion pv)
{
- return (pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_3_MINOR);
+ int ret;
+ ret = (pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_3_MINOR);
+
+#ifdef WOLFSSL_DTLS13
+ if (ret == 0 && pv.major == DTLS_MAJOR && pv.minor <= DTLSv1_3_MINOR)
+ return 1;
+#endif
+
+ return ret;
}
static WC_INLINE int IsEncryptionOn(WOLFSSL* ssl, int isSend)
{
#ifdef WOLFSSL_DTLS
/* For DTLS, epoch 0 is always not encrypted. */
- if (ssl->options.dtls && !isSend && ssl->keys.curEpoch == 0)
- return 0;
+ if (ssl->options.dtls && !isSend) {
+ if (!IsAtLeastTLSv1_3(ssl->version) && ssl->keys.curEpoch == 0)
+ return 0;
+#ifdef WOLFSSL_DTLS13
+ else if (IsAtLeastTLSv1_3(ssl->version)
+ && w64IsZero(ssl->keys.curEpoch64))
+ return 0;
+#endif /* WOLFSSL_DTLS13 */
+
+ }
#endif /* WOLFSSL_DTLS */
return ssl->keys.encryptionOn &&
@@ -524,6 +545,7 @@ int IsDtlsNotSctpMode(WOLFSSL* ssl)
#endif
}
+#ifndef WOLFSSL_NO_TLS12
/* Secure Real-time Transport Protocol */
/* If SRTP is not enabled returns the state of the dtls option.
* If SRTP is enabled returns dtls && !dtlsSrtpProfiles. */
@@ -535,6 +557,7 @@ static WC_INLINE int IsDtlsNotSrtpMode(WOLFSSL* ssl)
return ssl->options.dtls;
#endif
}
+#endif /* !WOLFSSL_NO_TLS12 */
#endif /* WOLFSSL_DTLS */
@@ -2104,7 +2127,17 @@ int InitSSL_Ctx(WOLFSSL_CTX* ctx, WOLFSSL_METHOD* method, void* heap)
ctx->refCount = 1; /* so either CTX_free or SSL_free can release */
ctx->heap = ctx; /* defaults to self */
ctx->timeout = WOLFSSL_SESSION_TIMEOUT;
- ctx->minDowngrade = WOLFSSL_MIN_DOWNGRADE; /* current default: TLSv1_MINOR */
+
+#ifdef WOLFSSL_DTLS
+ if (method->version.major == DTLS_MAJOR) {
+ ctx->minDowngrade = WOLFSSL_MIN_DTLS_DOWNGRADE;
+ }
+ else
+#endif /* WOLFSSL_DTLS */
+ {
+ /* current default: TLSv1_MINOR */
+ ctx->minDowngrade = WOLFSSL_MIN_DOWNGRADE;
+ }
if (wc_InitMutex(&ctx->countMutex) < 0) {
WOLFSSL_MSG("Mutex error on CTX init");
@@ -2579,6 +2612,14 @@ void InitCiphers(WOLFSSL* ssl)
#ifdef HAVE_ONE_TIME_AUTH
ssl->auth.setup = 0;
#endif
+
+#ifdef WOLFSSL_DTLS13
+ XMEMSET(&ssl->dtlsRecordNumberEncrypt, 0,
+ sizeof(ssl->dtlsRecordNumberEncrypt));
+ XMEMSET(&ssl->dtlsRecordNumberDecrypt, 0,
+ sizeof(ssl->dtlsRecordNumberEncrypt));
+#endif /* WOLFSSL_DTLS13 */
+
}
@@ -2638,6 +2679,29 @@ void FreeCiphers(WOLFSSL* ssl)
XFREE(ssl->encrypt.hmac, ssl->heap, DYNAMIC_TYPE_CIPHER);
XFREE(ssl->decrypt.hmac, ssl->heap, DYNAMIC_TYPE_CIPHER);
#endif
+
+#ifdef WOLFSSL_DTLS13
+#ifdef BUILD_AES
+ if (ssl->dtlsRecordNumberEncrypt.aes != NULL) {
+ wc_AesFree(ssl->dtlsRecordNumberEncrypt.aes);
+ XFREE(ssl->dtlsRecordNumberEncrypt.aes, ssl->heap, DYNAMIC_TYPE_CIPHER);
+ ssl->dtlsRecordNumberEncrypt.aes = NULL;
+ }
+ if (ssl->dtlsRecordNumberDecrypt.aes != NULL) {
+ wc_AesFree(ssl->dtlsRecordNumberDecrypt.aes);
+ XFREE(ssl->dtlsRecordNumberDecrypt.aes, ssl->heap, DYNAMIC_TYPE_CIPHER);
+ ssl->dtlsRecordNumberDecrypt.aes = NULL;
+ }
+#endif /* BUILD_AES */
+#ifdef HAVE_CHACHA
+ XFREE(ssl->dtlsRecordNumberEncrypt.chacha,
+ ssl->heap, DYNAMIC_TYPE_CIPHER);
+ XFREE(ssl->dtlsRecordNumberDecrypt.chacha,
+ ssl->heap, DYNAMIC_TYPE_CIPHER);
+ ssl->dtlsRecordNumberEncrypt.chacha = NULL;
+ ssl->dtlsRecordNumberDecrypt.chacha = NULL;
+#endif /* HAVE_CHACHA */
+#endif /* WOLFSSL_DTLS13 */
}
@@ -2858,6 +2922,13 @@ void InitSuites(Suites* suites, ProtocolVersion pv, int keySz, word16 haveRSA,
int dtls = 0;
int haveRSAsig = 1;
+#ifdef WOLFSSL_DTLS
+ /* If DTLS v1.2 or later than set tls1_2 flag */
+ if (pv.major == DTLS_MAJOR && pv.minor <= DTLSv1_2_MINOR) {
+ tls1_2 = 1;
+ }
+#endif
+
(void)tls; /* shut up compiler */
(void)tls1_2;
(void)dtls;
@@ -6728,6 +6799,17 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
}
#endif /* HAVE_SECURE_RENEGOTIATION */
+
+#ifdef WOLFSSL_DTLS13
+ /* setup 0 (un-protected) epoch */
+ ssl->dtls13Epochs[0].isValid = 1;
+ ssl->dtls13Epochs[0].side = ENCRYPT_AND_DECRYPT_SIDE;
+ ssl->dtls13EncryptEpoch = &ssl->dtls13Epochs[0];
+ ssl->dtls13DecryptEpoch = &ssl->dtls13Epochs[0];
+ ssl->options.dtls13SendMoreAcks = WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT;
+ ssl->dtls13Rtx.rtxRecordTailPtr = &ssl->dtls13Rtx.rtxRecords;
+#endif /* WOLFSSL_DTLS13 */
+
return 0;
}
@@ -7381,6 +7463,9 @@ void SSL_ResourceFree(WOLFSSL* ssl)
wolfSSL_sk_X509_NAME_pop_free(ssl->ca_names, NULL);
ssl->ca_names = NULL;
#endif
+#ifdef WOLFSSL_DTLS13
+ Dtls13FreeFsmResources(ssl);
+#endif /* WOLFSSL_DTLS13 */
}
/* Free any handshake resources no longer needed */
@@ -7389,8 +7474,8 @@ void FreeHandshakeResources(WOLFSSL* ssl)
WOLFSSL_ENTER("FreeHandshakeResources");
#ifdef WOLFSSL_DTLS
- /* DTLS_POOL */
- if (ssl->options.dtls) {
+ /* DTLS_POOL (DTLSv1.3 flushes the queue autonomously) */
+ if (ssl->options.dtls && !IsAtLeastTLSv1_3(ssl->version)) {
DtlsMsgPoolReset(ssl);
DtlsMsgListDelete(ssl->dtls_rx_msg_list, ssl->heap);
ssl->dtls_rx_msg_list = NULL;
@@ -8373,6 +8458,18 @@ ProtocolVersion MakeDTLSv1_2(void)
#endif /* !WOLFSSL_NO_TLS12 */
+#ifdef WOLFSSL_DTLS13
+
+ProtocolVersion MakeDTLSv1_3(void)
+{
+ ProtocolVersion pv;
+ pv.major = DTLS_MAJOR;
+ pv.minor = DTLSv1_3_MINOR;
+
+ return pv;
+}
+
+#endif /* WOLFSSL_DTLS13 */
#endif /* WOLFSSL_DTLS */
@@ -8715,8 +8812,19 @@ int HashOutput(WOLFSSL* ssl, const byte* output, int sz, int ivSz)
#endif
#ifdef WOLFSSL_DTLS
if (ssl->options.dtls) {
- adj += DTLS_RECORD_EXTRA;
- sz -= DTLS_RECORD_EXTRA;
+ if (IsAtLeastTLSv1_3(ssl->version)) {
+#ifdef WOLFSSL_DTLS13
+ word16 dtls_record_extra;
+ dtls_record_extra = Dtls13GetRlHeaderLength(IsEncryptionOn(ssl, 1));
+ dtls_record_extra -= RECORD_HEADER_SZ;
+
+ adj += dtls_record_extra;
+ sz -= dtls_record_extra;
+#endif /* WOLFSSL_DTLS13 */
+ } else {
+ adj += DTLS_RECORD_EXTRA;
+ sz -= DTLS_RECORD_EXTRA;
+ }
}
#endif
@@ -8740,6 +8848,12 @@ int HashInput(WOLFSSL* ssl, const byte* input, int sz)
if (ssl->options.dtls) {
adj -= DTLS_HANDSHAKE_EXTRA;
sz += DTLS_HANDSHAKE_EXTRA;
+
+#ifdef WOLFSSL_DTLS13
+ if (IsAtLeastTLSv1_3(ssl->version))
+ return Dtls13HashHandshake(ssl, adj, sz);
+#endif /* WOLFSSL_DTLS13 */
+
}
#endif
@@ -9106,6 +9220,18 @@ retry:
case WOLFSSL_CBIO_ERR_TIMEOUT:
#ifdef WOLFSSL_DTLS
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
+ /* TODO: support WANT_WRITE here */
+ if (Dtls13RtxTimeout(ssl) < 0) {
+ WOLFSSL_MSG(
+ "Error trying to retransmit DTLS buffered message");
+ return -1;
+ }
+ goto retry;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
if (IsDtlsNotSctpMode(ssl) &&
ssl->options.handShakeState != HANDSHAKE_DONE &&
DtlsMsgPoolTimeout(ssl) == 0 &&
@@ -9445,7 +9571,12 @@ int CheckAvailableSize(WOLFSSL *ssl, int size)
#else
ssl->dtls_expected_rx
#endif
- ) {
+#ifdef WOLFSSL_DTLS13
+ /* DTLS1.3 uses the output buffer to store the full message and deal
+ with fragmentation later in dtls13HandshakeSend() */
+ && !IsAtLeastTLSv1_3(ssl->version)
+#endif /* WOLFSSL_DTLS13 */
+ ) {
WOLFSSL_MSG("CheckAvailableSize() called with size greater than MTU.");
return DTLS_SIZE_ERROR;
}
@@ -9461,14 +9592,186 @@ int CheckAvailableSize(WOLFSSL *ssl, int size)
return 0;
}
+#ifdef WOLFSSL_DTLS13
+static int GetDtls13RecordHeader(WOLFSSL* ssl, const byte* input,
+ word32* inOutIdx, RecordLayerHeader* rh, word16* size)
+{
+
+ Dtls13UnifiedHdrInfo hdrInfo;
+ w64wrapper epochNumber;
+ byte epochBits;
+ int readSize;
+ int ret;
+
+ readSize = ssl->buffers.inputBuffer.length - *inOutIdx;
+
+ epochBits = *input & EE_MASK;
+ ret = Dtls13ReconstructEpochNumber(ssl, epochBits, &epochNumber);
+ if (ret != 0)
+ return ret;
+
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG_EX("reconstructed epoch number: %ld",
+ epochNumber);
+#endif /* WOLFSSL_DEBUG_TLS */
+
+ /* protected records always use unified_headers in DTLSv1.3 */
+ if (w64IsZero(epochNumber))
+ return SEQUENCE_ERROR;
+
+ if (ssl->dtls13DecryptEpoch == NULL)
+ return BAD_STATE_E;
+
+#ifdef WOLFSSL_EARLY_DATA
+ if (w64Equal(epochNumber, w64From32(0x0, DTLS13_EPOCH_EARLYDATA)) &&
+ ssl->options.handShakeDone) {
+ WOLFSSL_MSG("discarding early data after handshake");
+ return SEQUENCE_ERROR;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
+ if (!w64Equal(ssl->dtls13DecryptEpoch->epochNumber, epochNumber)) {
+ ret = Dtls13SetEpochKeys(ssl, epochNumber, DECRYPT_SIDE_ONLY);
+ if (ret != 0)
+ return SEQUENCE_ERROR;
+ }
+
+ ret = Dtls13ParseUnifiedRecordLayer(ssl, input + *inOutIdx, readSize,
+ &hdrInfo);
+
+ if (ret != 0)
+ return ret;
+
+ *size = hdrInfo.recordLength;
+ c16toa(*size, rh->length);
+
+ /* type is implicit */
+ rh->type = application_data;
+
+ /* version is implicit */
+ rh->pvMajor = ssl->version.major;
+ rh->pvMinor = DTLSv1_2_MINOR;
+
+ ssl->keys.curEpoch64 = epochNumber;
+
+ ret = Dtls13ReconstructSeqNumber(ssl, &hdrInfo, &ssl->keys.curSeq);
+ if (ret != 0)
+ return ret;
+
+#ifdef WOLFSSL_DEBUG_TLS
+ WOLFSSL_MSG_EX("reconstructed seq number: %ld",
+ ssl->keys.curSeq);
+#endif /* WOLFSSL_DEBUG_TLS */
+
+ *inOutIdx += hdrInfo.headerLength;
+ ssl->dtls13CurRlLength = hdrInfo.headerLength;
+
+ return 0;
+}
+
+#endif /* WOLFSSL_DTLS13 */
+
+#ifdef WOLFSSL_DTLS
+static int GetDtlsRecordHeader(WOLFSSL* ssl, const byte* input,
+ word32* inOutIdx, RecordLayerHeader* rh, word16* size)
+{
+
+#ifdef HAVE_FUZZER
+ if (ssl->fuzzerCb)
+ ssl->fuzzerCb(ssl, input + *inOutIdx, DTLS_RECORD_HEADER_SZ,
+ FUZZ_HEAD, ssl->fuzzerCtx);
+#endif
+
+#ifdef WOLFSSL_DTLS13
+ word32 read_size;
+ int ret;
+
+ read_size = ssl->buffers.inputBuffer.length - *inOutIdx;
+
+ if (Dtls13IsUnifiedHeader(*(input + *inOutIdx))) {
+
+ /* version 1.3 already negotiated */
+ if (ssl->options.tls1_3) {
+ ret = GetDtls13RecordHeader(ssl, input, inOutIdx, rh, size);
+ if (ret == 0 || ret != SEQUENCE_ERROR)
+ return ret;
+ }
+
+#ifndef NO_WOLFSSL_CLIENT
+ if (ssl->options.side == WOLFSSL_CLIENT_END
+ && ssl->options.serverState < SERVER_HELLO_COMPLETE
+ && IsAtLeastTLSv1_3(ssl->version)
+ && !ssl->options.handShakeDone) {
+ /* we may have lost ServerHello. Try to send a empty ACK to shortcut
+ Server retransmission timer */
+ ssl->dtls13Rtx.sendAcks = 1;
+ }
+#endif
+ return SEQUENCE_ERROR;
+ }
+
+ /* not a unified header, check that we have at least
+ DTLS_RECORD_HEADER_SZ */
+ if (read_size < DTLS_RECORD_HEADER_SZ)
+ return LENGTH_ERROR;
+
+#endif /* WOLFSSL_DTLS13 */
+
+ /* type and version in same spot */
+ XMEMCPY(rh, input + *inOutIdx, ENUM_LEN + VERSION_SZ);
+ *inOutIdx += ENUM_LEN + VERSION_SZ;
+ ato16(input + *inOutIdx, &ssl->keys.curEpoch);
+#ifdef WOLFSSL_DTLS13
+ /* only non protected message can use the DTLSPlaintext record header */
+ if (ssl->options.tls1_3 && ssl->keys.curEpoch != 0)
+ return SEQUENCE_ERROR;
+
+ w64Zero(&ssl->keys.curEpoch64);
+ if (!w64IsZero(ssl->dtls13DecryptEpoch->epochNumber))
+ Dtls13SetEpochKeys(ssl, ssl->keys.curEpoch64, DECRYPT_SIDE_ONLY);
+
+#endif /* WOLFSSL_DTLS13 */
+ *inOutIdx += OPAQUE16_LEN;
+ if (ssl->options.haveMcast) {
+ #ifdef WOLFSSL_MULTICAST
+ ssl->keys.curPeerId = input[*inOutIdx];
+ ssl->keys.curSeq_hi = input[*inOutIdx+1];
+ #endif
+ }
+ else
+ ato16(input + *inOutIdx, &ssl->keys.curSeq_hi);
+ *inOutIdx += OPAQUE16_LEN;
+ ato32(input + *inOutIdx, &ssl->keys.curSeq_lo);
+ *inOutIdx += OPAQUE32_LEN; /* advance past rest of seq */
+
+#ifdef WOLFSSL_DTLS13
+ /* DTLSv1.3 PlainText records use DTLSv1.2 sequence number encoding. Update
+ the DTLv1.3 word64 version as well */
+ ssl->keys.curSeq = w64From32(ssl->keys.curSeq_hi, ssl->keys.curSeq_lo);
+#endif /* WOLFSSL_DTLS13 */
+
+ ato16(input + *inOutIdx, size);
+ *inOutIdx += LENGTH_SZ;
+
+ return 0;
+}
+#endif /* WOLFSSL_DTLS */
/* do all verify and sanity checks on record header */
static int GetRecordHeader(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
RecordLayerHeader* rh, word16 *size)
{
+ byte tls12minor;
+#ifdef WOLFSSL_DTLS
+ int ret;
+#endif /* WOLFSSL_DTLS */
+
#ifdef OPENSSL_ALL
word32 start = *inOutIdx;
#endif
+
+ (void)tls12minor;
+
if (!ssl->options.dtls) {
#ifdef HAVE_FUZZER
if (ssl->fuzzerCb)
@@ -9481,34 +9784,16 @@ static int GetRecordHeader(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
else {
#ifdef WOLFSSL_DTLS
-#ifdef HAVE_FUZZER
- if (ssl->fuzzerCb)
- ssl->fuzzerCb(ssl, input + *inOutIdx, DTLS_RECORD_HEADER_SZ,
- FUZZ_HEAD, ssl->fuzzerCtx);
-#endif
- /* type and version in same sport */
- XMEMCPY(rh, input + *inOutIdx, ENUM_LEN + VERSION_SZ);
- *inOutIdx += ENUM_LEN + VERSION_SZ;
- ato16(input + *inOutIdx, &ssl->keys.curEpoch);
- *inOutIdx += OPAQUE16_LEN;
- if (ssl->options.haveMcast) {
- #ifdef WOLFSSL_MULTICAST
- ssl->keys.curPeerId = input[*inOutIdx];
- ssl->keys.curSeq_hi = input[*inOutIdx+1];
- #endif
- }
- else
- ato16(input + *inOutIdx, &ssl->keys.curSeq_hi);
- *inOutIdx += OPAQUE16_LEN;
- ato32(input + *inOutIdx, &ssl->keys.curSeq_lo);
- *inOutIdx += OPAQUE32_LEN; /* advance past rest of seq */
- ato16(input + *inOutIdx, size);
- *inOutIdx += LENGTH_SZ;
+ ret = GetDtlsRecordHeader(ssl, input, inOutIdx, rh, size);
+ if (ret != 0)
+ return ret;
#endif
}
#ifdef WOLFSSL_DTLS
- if (IsDtlsNotSctpMode(ssl)) {
+ /* DTLSv1.3 MUST check window after deprotecting to avoid timing channel
+ (RFC9147 Section 4.5.1) */
+ if (IsDtlsNotSctpMode(ssl) && !IsAtLeastTLSv1_3(ssl->version)) {
if (!DtlsCheckWindow(ssl) ||
(rh->type == application_data && ssl->keys.curEpoch == 0) ||
(rh->type == alert && ssl->options.handShakeDone &&
@@ -9519,13 +9804,18 @@ static int GetRecordHeader(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
#endif
+ tls12minor = TLSv1_2_MINOR;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ tls12minor = DTLSv1_2_MINOR;
+#endif /* WOLFSSL_DTLS13 */
/* catch version mismatch */
#ifndef WOLFSSL_TLS13
if (rh->pvMajor != ssl->version.major || rh->pvMinor != ssl->version.minor)
#else
if (rh->pvMajor != ssl->version.major ||
(rh->pvMinor != ssl->version.minor &&
- (!IsAtLeastTLSv1_3(ssl->version) || rh->pvMinor != TLSv1_2_MINOR)
+ (!IsAtLeastTLSv1_3(ssl->version) || rh->pvMinor != tls12minor)
))
#endif
{
@@ -9540,6 +9830,15 @@ static int GetRecordHeader(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
else if (ssl->options.dtls && rh->type == handshake)
/* Check the DTLS handshake message RH version later. */
WOLFSSL_MSG("DTLS handshake, skip RH version number check");
+#ifdef WOLFSSL_DTLS13
+ else if (ssl->options.dtls && !ssl->options.handShakeDone) {
+ /* we may have lost the ServerHello and this is a unified record
+ before version been negotiated */
+ if (Dtls13IsUnifiedHeader(*input)) {
+ return SEQUENCE_ERROR;
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
else {
WOLFSSL_MSG("SSL version error");
/* send alert per RFC5246 Appendix E. Backward Compatibility */
@@ -9576,6 +9875,9 @@ static int GetRecordHeader(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
case change_cipher_spec:
case application_data:
case alert:
+#ifdef WOLFSSL_DTLS13
+ case ack:
+#endif /* WOLFSSL_DTLS13 */
break;
case no_type:
default:
@@ -9624,10 +9926,10 @@ static int GetHandShakeHeader(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
#endif
#ifdef WOLFSSL_DTLS
-static int GetDtlsHandShakeHeader(WOLFSSL* ssl, const byte* input,
- word32* inOutIdx, byte *type, word32 *size,
- word32 *fragOffset, word32 *fragSz,
- word32 totalSz)
+int GetDtlsHandShakeHeader(WOLFSSL* ssl, const byte* input,
+ word32* inOutIdx, byte *type, word32 *size,
+ word32 *fragOffset, word32 *fragSz,
+ word32 totalSz)
{
word32 idx = *inOutIdx;
@@ -9648,9 +9950,10 @@ static int GetDtlsHandShakeHeader(WOLFSSL* ssl, const byte* input,
idx += DTLS_HANDSHAKE_FRAG_SZ;
c24to32(input + idx, fragSz);
- if (ssl->curRL.pvMajor != ssl->version.major ||
- ssl->curRL.pvMinor != ssl->version.minor) {
-
+ if ((ssl->curRL.pvMajor != ssl->version.major) ||
+ (!IsAtLeastTLSv1_3(ssl->version) && ssl->curRL.pvMinor != ssl->version.minor) ||
+ (IsAtLeastTLSv1_3(ssl->version) && ssl->curRL.pvMinor != DTLSv1_2_MINOR)
+ ) {
if (*type != client_hello && *type != hello_verify_request) {
WOLFSSL_ERROR(VERSION_ERROR);
return VERSION_ERROR;
@@ -14878,6 +15181,51 @@ static WC_INLINE int DtlsCheckWindow(WOLFSSL* ssl)
return 1;
}
+#ifdef WOLFSSL_DTLS13
+static WC_INLINE int Dtls13CheckWindow(WOLFSSL* ssl)
+{
+ w64wrapper nextSeq, seq;
+ w64wrapper diff64;
+ word32 *window;
+ int wordOffset;
+ int wordIndex;
+ word32 diff;
+
+ if (ssl->dtls13DecryptEpoch == NULL) {
+ WOLFSSL_MSG("Can't find decrypting epoch");
+ return 0;
+ }
+
+ nextSeq = ssl->dtls13DecryptEpoch->nextPeerSeqNumber;
+ window = ssl->dtls13DecryptEpoch->window;
+ seq = ssl->keys.curSeq;
+
+ if (w64GTE(seq, nextSeq))
+ return 1;
+
+ /* seq < nextSeq, nextSeq - seq */
+ diff64 = w64Sub(nextSeq, seq);
+
+ /* diff >= DTLS_SEQ_BITS, outside of the window */
+ if (w64GT(diff64, w64From32(0, DTLS_SEQ_BITS)))
+ return 0;
+
+ /* we are assuming DTLS_SEQ_BITS <= 2**32 */
+ diff = w64GetLow32(diff64);
+
+ /* zero based index */
+ diff--;
+
+ wordIndex = ((int)diff) / DTLS_WORD_BITS;
+ wordOffset = ((int)diff) % DTLS_WORD_BITS;
+
+ if (window[wordIndex] & (1 << wordOffset))
+ return 0;
+
+ return 1;
+}
+
+#endif /* WOLFSSL_DTLS13 */
#ifdef WOLFSSL_MULTICAST
static WC_INLINE word32 UpdateHighwaterMark(word32 cur, word32 first,
@@ -14896,21 +15244,80 @@ static WC_INLINE word32 UpdateHighwaterMark(word32 cur, word32 first,
}
#endif /* WOLFSSL_MULTICAST */
-
-static WC_INLINE int DtlsUpdateWindow(WOLFSSL* ssl)
+/* diff must be already incremented by one */
+static void DtlsUpdateWindowGTSeq(word32 diff, word32* window)
+{
+ word32 idx, newDiff, temp, i;
+ word32 oldWindow[WOLFSSL_DTLS_WINDOW_WORDS];
+
+ if (diff >= DTLS_SEQ_BITS)
+ XMEMSET(window, 0, DTLS_SEQ_SZ);
+ else {
+ temp = 0;
+ idx = diff / DTLS_WORD_BITS;
+ newDiff = diff % DTLS_WORD_BITS;
+
+ XMEMCPY(oldWindow, window, sizeof(oldWindow));
+
+ for (i = 0; i < WOLFSSL_DTLS_WINDOW_WORDS; i++) {
+ if (i < idx)
+ window[i] = 0;
+ else {
+ temp |= (oldWindow[i-idx] << newDiff);
+ window[i] = temp;
+ temp = oldWindow[i-idx] >> (DTLS_WORD_BITS - newDiff - 1);
+ }
+ }
+ }
+ window[0] |= 1;
+}
+
+static WC_INLINE int _DtlsUpdateWindow(WOLFSSL* ssl, word16* next_hi,
+ word32* next_lo, word32 *window)
{
- word32* window;
- word32* next_lo;
- word16* next_hi;
- int curLT;
word32 cur_lo, diff;
+ int curLT;
word16 cur_hi;
- WOLFSSL_DTLS_PEERSEQ* peerSeq = ssl->keys.peerSeq;
cur_hi = ssl->keys.curSeq_hi;
cur_lo = ssl->keys.curSeq_lo;
+ if (cur_hi == *next_hi) {
+ curLT = cur_lo < *next_lo;
+ diff = curLT ? *next_lo - cur_lo - 1 : cur_lo - *next_lo + 1;
+ }
+ else {
+ curLT = cur_hi < *next_hi;
+ diff = curLT ? cur_lo - *next_lo - 1 : *next_lo - cur_lo + 1;
+ }
+
+ if (curLT) {
+ word32 idx = diff / DTLS_WORD_BITS;
+ word32 newDiff = diff % DTLS_WORD_BITS;
+
+ if (idx < WOLFSSL_DTLS_WINDOW_WORDS)
+ window[idx] |= (1 << newDiff);
+ }
+ else {
+ DtlsUpdateWindowGTSeq(diff, window);
+ *next_lo = cur_lo + 1;
+ if (*next_lo < cur_lo)
+ (*next_hi)++;
+ }
+
+ return 1;
+}
+
+static WC_INLINE int DtlsUpdateWindow(WOLFSSL* ssl)
+{
+ WOLFSSL_DTLS_PEERSEQ* peerSeq = ssl->keys.peerSeq;
+ word16 *next_hi;
+ word32 *next_lo;
+ word32* window;
+
#ifdef WOLFSSL_MULTICAST
+ word32 cur_lo = ssl->keys.curSeq_lo;
+
if (ssl->options.haveMcast) {
WOLFSSL_DTLS_PEERSEQ* p;
int i;
@@ -14962,56 +15369,65 @@ static WC_INLINE int DtlsUpdateWindow(WOLFSSL* ssl)
window = peerSeq->prevWindow;
}
- if (cur_hi == *next_hi) {
- curLT = cur_lo < *next_lo;
- diff = curLT ? *next_lo - cur_lo - 1 : cur_lo - *next_lo + 1;
- }
- else {
- curLT = cur_hi < *next_hi;
- diff = curLT ? cur_lo - *next_lo - 1 : *next_lo - cur_lo + 1;
+ return _DtlsUpdateWindow(ssl, next_hi, next_lo, window);
+}
+
+#ifdef WOLFSSL_DTLS13
+static WC_INLINE int Dtls13UpdateWindow(WOLFSSL* ssl)
+{
+ w64wrapper nextSeq, seq;
+ w64wrapper diff64;
+ word32 *window;
+ int wordOffset;
+ int wordIndex;
+ word32 diff;
+
+ if (ssl->dtls13DecryptEpoch == NULL) {
+ WOLFSSL_MSG("Can't find decrypting Epoch");
+ return BAD_STATE_E;
}
- if (curLT) {
- word32 idx = diff / DTLS_WORD_BITS;
- word32 newDiff = diff % DTLS_WORD_BITS;
+ nextSeq = ssl->dtls13DecryptEpoch->nextPeerSeqNumber;
+ window = ssl->dtls13DecryptEpoch->window;
+ seq = ssl->keys.curSeq;
- if (idx < WOLFSSL_DTLS_WINDOW_WORDS)
- window[idx] |= (1 << newDiff);
- }
- else {
- if (diff >= DTLS_SEQ_BITS)
- XMEMSET(window, 0, DTLS_SEQ_SZ);
- else {
- word32 idx, newDiff, temp, i;
- word32 oldWindow[WOLFSSL_DTLS_WINDOW_WORDS];
+ /* seq < nextSeq */
+ if (w64LT(seq, nextSeq)) {
+ diff64 = w64Sub(nextSeq, seq);
- temp = 0;
- idx = diff / DTLS_WORD_BITS;
- newDiff = diff % DTLS_WORD_BITS;
+ /* zero based index */
+ w64Decrement(&diff64);
- XMEMCPY(oldWindow, window, sizeof(oldWindow));
+ /* FIXME: check that diff64 < DTLS_WORDS_BITS */
+ diff = w64GetLow32(diff64);
+ wordIndex = ((int)diff) / DTLS_WORD_BITS;
+ wordOffset = ((int)diff) % DTLS_WORD_BITS;
- for (i = 0; i < WOLFSSL_DTLS_WINDOW_WORDS; i++) {
- if (i < idx)
- window[i] = 0;
- else {
- temp |= (oldWindow[i-idx] << newDiff);
- window[i] = temp;
- temp = oldWindow[i-idx] >> (DTLS_WORD_BITS - newDiff - 1);
- }
- }
+ if (wordIndex >= WOLFSSL_DTLS_WINDOW_WORDS) {
+ WOLFSSL_MSG("Invalid sequence number to Dtls13UpdateWindow");
+ return BAD_STATE_E;
}
- window[0] |= 1;
- *next_lo = cur_lo + 1;
- if (*next_lo < cur_lo)
- (*next_hi)++;
+
+ window[wordIndex] |= (1 << wordOffset);
+ return 1;
}
+ /* seq >= nextSeq, seq - nextSeq */
+ diff64 = w64Sub(seq, nextSeq);
+
+ /* as we are considering nextSeq inside the window, we should add + 1 */
+ w64Increment(&diff64);
+ DtlsUpdateWindowGTSeq(w64GetLow32(diff64), window);
+
+ w64Increment(&seq);
+ ssl->dtls13DecryptEpoch->nextPeerSeqNumber = seq;
+
return 1;
}
+#endif /* WOLFSSL_DTLS13 */
-static int DtlsMsgDrain(WOLFSSL* ssl)
+int DtlsMsgDrain(WOLFSSL* ssl)
{
DtlsMsg* item = ssl->dtls_rx_msg_list;
int ret = 0;
@@ -15026,8 +15442,15 @@ static int DtlsMsgDrain(WOLFSSL* ssl)
item->fragSz == item->sz &&
ret == 0) {
word32 idx = 0;
- if ((ret = DoHandShakeMsgType(ssl, item->msg, &idx, item->type,
- item->sz, item->sz)) == 0) {
+
+ #ifdef WOLFSSL_NO_TLS12
+ ret = DoTls13HandShakeMsgType(ssl, item->msg, &idx, item->type,
+ item->sz, item->sz);
+ #else
+ ret = DoHandShakeMsgType(ssl, item->msg, &idx, item->type,
+ item->sz, item->sz);
+ #endif
+ if (ret == 0) {
DtlsTxMsgListClean(ssl);
}
#ifdef WOLFSSL_ASYNC_CRYPT
@@ -15164,8 +15587,13 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
*inOutIdx = totalSz;
}
else {
+ #ifdef WOLFSSL_NO_TLS12
+ ret = DoTls13HandShakeMsgType(ssl, input, inOutIdx, type, size,
+ totalSz);
+ #else
ret = DoHandShakeMsgType(ssl, input, inOutIdx, type, size,
totalSz);
+ #endif
}
}
}
@@ -15267,7 +15695,12 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
#endif
ret = DtlsMsgDrain(ssl);
#else
+ #ifdef WOLFSSL_NO_TLS12
+ ret = DoTls13HandShakeMsgType(ssl, input, inOutIdx, type, size,
+ totalSz);
+ #else
ret = DoHandShakeMsgType(ssl, input, inOutIdx, type, size, totalSz);
+ #endif
if (ret == 0) {
DtlsTxMsgListClean(ssl);
if (ssl->dtls_rx_msg_list != NULL) {
@@ -15280,7 +15713,7 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
WOLFSSL_LEAVE("DoDtlsHandShakeMsg()", ret);
return ret;
}
-#endif
+#endif /* WOLFSSL_DTLS13 */
#ifndef WOLFSSL_NO_TLS12
@@ -17430,8 +17863,15 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
readSz = RECORD_HEADER_SZ;
#ifdef WOLFSSL_DTLS
- if (ssl->options.dtls)
+ if (ssl->options.dtls) {
readSz = DTLS_RECORD_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.tls1_3) {
+ /* dtls1.3 unified header can be as little as 2 bytes */
+ readSz = DTLS_UNIFIED_HEADER_MIN_SZ;
+ }
+#endif /* WOLFSSL_DTLS13 */
+ }
#endif
/* get header or return error */
@@ -17527,6 +17967,11 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
/* get the record layer header */
case getRecordLayerHeader:
+ /* DTLSv1.3 record numbers in the header are encrypted, and AAD
+ * uses the unecrypted form. Because of this we need to modify the
+ * header, decrypting the numbers inside
+ * DtlsParseUnifiedRecordLayer(). This violates the const attribute
+ * of the buffer parameter of GetRecordHeader() used here. */
ret = GetRecordHeader(ssl, ssl->buffers.inputBuffer.buffer,
&ssl->buffers.inputBuffer.idx,
&ssl->curRL, &ssl->curSize);
@@ -17541,6 +17986,13 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
ssl->replayDropCount++;
#endif /* WOLFSSL_DTLS_DROP_STATS */
+#ifdef WOLFSSL_DTLS13
+ /* return to send ACKS and shortcut rtx timer */
+ if (IsAtLeastTLSv1_3(ssl->version)
+ && ssl->dtls13Rtx.sendAcks)
+ return 0;
+#endif /* WOLFSSL_DTLS13 */
+
continue;
}
#endif
@@ -17747,11 +18199,22 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
else
{
#ifdef WOLFSSL_TLS13
+ byte *aad = (byte*)&ssl->curRL;
+ word16 aad_size = RECORD_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ /* aad now points to the record header */
+ aad = in->buffer +
+ in->idx - ssl->dtls13CurRlLength;
+ aad_size = ssl->dtls13CurRlLength;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
ret = DecryptTls13(ssl,
in->buffer + in->idx,
in->buffer + in->idx,
ssl->curSize,
- (byte*)&ssl->curRL, RECORD_HEADER_SZ, 1);
+ aad, aad_size, 1);
#else
ret = DECRYPT_ERROR;
#endif /* WOLFSSL_TLS13 */
@@ -17778,6 +18241,15 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
else {
WOLFSSL_MSG("Decrypt failed");
WOLFSSL_ERROR(ret);
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.tls1_3 && ssl->options.dtls) {
+ WOLFSSL_MSG("DTLS: Ignoring decrypted failed record");
+ ssl->options.processReply = doProcessInit;
+ ssl->buffers.inputBuffer.idx =
+ ssl->buffers.inputBuffer.length;
+ return 0;
+ }
+#endif /* WOLFSSL_DTLS13 */
#ifdef WOLFSSL_EARLY_DATA
if (ssl->options.tls1_3) {
if (ssl->options.side == WOLFSSL_SERVER_END &&
@@ -17893,10 +18365,43 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
#endif
}
- ssl->options.processReply = runProcessingOneMessage;
+ ssl->options.processReply = runProcessingOneRecord;
FALL_THROUGH;
/* the record layer is here */
+ case runProcessingOneRecord:
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
+
+ if(!Dtls13CheckWindow(ssl)) {
+ /* drop packet */
+ WOLFSSL_MSG(
+ "Dropping DTLS record outside receiving window");
+ ssl->options.processReply = doProcessInit;
+ ssl->buffers.inputBuffer.idx += ssl->curSize;
+ if (ssl->buffers.inputBuffer.idx >
+ ssl->buffers.inputBuffer.length)
+ return BUFFER_E;
+
+ continue;
+ }
+
+ ret = Dtls13UpdateWindow(ssl);
+ if (ret != 1) {
+ WOLFSSL_ERROR(ret);
+ return ret;
+ }
+
+ ret = Dtls13RecordRecvd(ssl);
+ if (ret != 0) {
+ WOLFSSL_ERROR(ret);
+ return ret;
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
+ ssl->options.processReply = runProcessingOneMessage;
+ FALL_THROUGH;
+
case runProcessingOneMessage:
/* can't process a message if we have no data. */
if (ssl->buffers.inputBuffer.idx
@@ -17944,7 +18449,7 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
}
#ifdef WOLFSSL_DTLS
- if (IsDtlsNotSctpMode(ssl)) {
+ if (IsDtlsNotSctpMode(ssl) && !IsAtLeastTLSv1_3(ssl->version)) {
DtlsUpdateWindow(ssl);
}
#endif /* WOLFSSL_DTLS */
@@ -17957,11 +18462,36 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
/* debugging in DoHandShakeMsg */
if (ssl->options.dtls) {
#ifdef WOLFSSL_DTLS
- ret = DoDtlsHandShakeMsg(ssl,
- ssl->buffers.inputBuffer.buffer,
- &ssl->buffers.inputBuffer.idx,
- ssl->buffers.inputBuffer.length);
+ if (!IsAtLeastTLSv1_3(ssl->version)) {
+ ret = DoDtlsHandShakeMsg(ssl,
+ ssl->buffers.inputBuffer.buffer,
+ &ssl->buffers.inputBuffer.idx,
+ ssl->buffers.inputBuffer.length);
+ }
#endif
+#ifdef WOLFSSL_DTLS13
+ if (IsAtLeastTLSv1_3(ssl->version)) {
+ ret = Dtls13HandshakeRecv(ssl,
+ ssl->buffers.inputBuffer.buffer,
+ &ssl->buffers.inputBuffer.idx,
+ ssl->buffers.inputBuffer.length);
+#ifdef WOLFSSL_EARLY_DATA
+ if (ret == 0 &&
+ ssl->options.side == WOLFSSL_SERVER_END &&
+ ssl->earlyData > early_data_ext &&
+ ssl->options.handShakeState == HANDSHAKE_DONE) {
+
+ /* return so wolfSSL_read_early_data can return
+ exit */
+ ssl->earlyData = no_early_data;
+ ssl->options.processReply = doProcessInit;
+
+ return ZERO_RETURN;
+ }
+#endif /* WOLFSSL_EARLY_DATA */
+
+ }
+#endif /* WOLFSSL_DTLS13 */
}
else if (!IsAtLeastTLSv1_3(ssl->version)
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12)
@@ -18235,6 +18765,22 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
ret = 0;
break;
+#ifdef WOLFSSL_DTLS13
+ case ack:
+ WOLFSSL_MSG("got ACK");
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
+ word32 processedSize = 0;
+ ret = DoDtls13Ack(ssl, ssl->buffers.inputBuffer.buffer +
+ ssl->buffers.inputBuffer.idx,
+ ssl->buffers.inputBuffer.length -
+ ssl->buffers.inputBuffer.idx -
+ ssl->keys.padSz, &processedSize);
+ ssl->buffers.inputBuffer.idx += processedSize;
+ ssl->buffers.inputBuffer.idx += ssl->keys.padSz;
+ break;
+ }
+ FALL_THROUGH;
+#endif /* WOLFSSL_DTLS13 */
default:
WOLFSSL_ERROR(UNKNOWN_RECORD_TYPE);
return UNKNOWN_RECORD_TYPE;
@@ -19466,7 +20012,7 @@ int CreateOcspResponse(WOLFSSL* ssl, OcspRequest** ocspRequest,
#endif
#endif /* !NO_WOLFSSL_SERVER */
-static int cipherExtraData(WOLFSSL* ssl)
+int cipherExtraData(WOLFSSL* ssl)
{
int cipherExtra;
/* Cipher data that may be added by BuildMessage */
@@ -20455,6 +21001,42 @@ int SendData(WOLFSSL* ssl, const void* data, int sz)
byte comp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
#endif
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && ssl->options.tls1_3) {
+ byte isEarlyData = 0;
+
+ if (ssl->dtls13EncryptEpoch == NULL)
+ return ssl->error = BAD_STATE_E;
+
+#ifdef WOLFSSL_EARLY_DATA
+ isEarlyData = ssl->earlyData != no_early_data;
+#endif
+
+ if (isEarlyData) {
+#ifdef WOLFSSL_EARLY_DATA
+ ret = Dtls13SetEpochKeys(ssl,
+ w64From32(0x0, DTLS13_EPOCH_EARLYDATA), ENCRYPT_SIDE_ONLY);
+ if (ret != 0) {
+ WOLFSSL_MSG(
+ "trying to send early data without epoch 1");
+ ssl->error = BUILD_MSG_ERROR;
+ return WOLFSSL_FATAL_ERROR;
+ }
+#endif /* WOLFSSL_EARLY_DATA */
+ }
+ else if (!w64Equal(
+ ssl->dtls13EncryptEpoch->epochNumber,
+ ssl->dtls13Epoch)) {
+ ret = Dtls13SetEpochKeys(
+ ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY);
+ if (ret != 0) {
+ ssl->error = BUILD_MSG_ERROR;
+ return WOLFSSL_FATAL_ERROR;
+ }
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
+
#ifdef WOLFSSL_DTLS
if (ssl->options.dtls) {
buffSz = wolfSSL_GetMaxFragSize(ssl, sz - sent);
@@ -20657,6 +21239,16 @@ startScr:
}
return ssl->error;
}
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ /* Dtls13DoScheduledWork(ssl) may return WANT_WRITE */
+ if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return ssl->error;
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
#ifdef HAVE_SECURE_RENEGOTIATION
if (ssl->secure_renegotiation &&
ssl->secure_renegotiation->startScr) {
@@ -20680,6 +21272,17 @@ startScr:
}
}
#endif
+
+#ifdef WOLFSSL_DTLS13
+ /* if wolfSSL_Peek() is invoked with sz == 0 it will not block (but
+ * it processes pending non-application records) */
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version) && peek &&
+ sz == 0 && ssl->buffers.inputBuffer.idx
+ - ssl->buffers.inputBuffer.length == 0) {
+ return 0;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
#ifndef WOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE
#ifdef WOLFSSL_TLS13
if (IsAtLeastTLSv1_3(ssl->version) && ssl->options.handShakeDone &&
@@ -20798,12 +21401,33 @@ static int SendAlert_ex(WOLFSSL* ssl, int severity, int type)
* TLS 1.3 encrypts handshake packets after the ServerHello
*/
if (IsEncryptionOn(ssl, 1)) {
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls
+ && IsAtLeastTLSv1_3(ssl->version)
+ && !w64Equal(ssl->dtls13EncryptEpoch->epochNumber, ssl->dtls13Epoch)) {
+ ret = Dtls13SetEpochKeys(ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY);
+ if (ret != 0)
+ return ret;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
sendSz = BuildMessage(ssl, output, outputSz, input, ALERT_SIZE, alert,
0, 0, 0, CUR_ORDER);
}
else {
- AddRecordHeader(output, ALERT_SIZE, alert, ssl, CUR_ORDER);
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
+ ret = Dtls13RlAddPlaintextHeader(ssl, output, alert, ALERT_SIZE);
+ if (ret != 0)
+ return ret;
+ }
+ else
+#endif /* WOLFSSL_DTLS13 */
+ {
+ AddRecordHeader(output, ALERT_SIZE, alert, ssl, CUR_ORDER);
+ }
+
output += RECORD_HEADER_SZ;
#ifdef WOLFSSL_DTLS
if (ssl->options.dtls)
@@ -30227,6 +30851,8 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
word32 i = *inOutIdx;
word32 begin = i;
int ret = 0;
+ byte lesserVersion;
+
#ifdef WOLFSSL_DTLS
Hmac cookieHmac;
byte newCookie[MAX_COOKIE_LEN];
@@ -30284,11 +30910,11 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
if (pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_3_MINOR)
pv.minor = TLSv1_2_MINOR;
- if ((!ssl->options.dtls && ssl->version.minor > pv.minor) ||
- (ssl->options.dtls && ssl->version.minor != DTLS_MINOR
- && ssl->version.minor != DTLSv1_2_MINOR && pv.minor != DTLS_MINOR
- && pv.minor != DTLSv1_2_MINOR)) {
+ lesserVersion = !ssl->options.dtls && ssl->version.minor > pv.minor;
+ lesserVersion |= ssl->options.dtls && ssl->version.minor < pv.minor;
+ if (lesserVersion) {
+ byte belowMinDowngrade;
word16 haveRSA = 0;
word16 havePSK = 0;
int keySz = 0;
@@ -30301,7 +30927,15 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
ret = VERSION_ERROR;
goto out;
}
- if (pv.minor < ssl->options.minDowngrade) {
+
+ belowMinDowngrade = pv.minor < ssl->options.minDowngrade;
+
+ /* DTLS versions increase backwards (-1,-2,-3) ecc */
+ if (ssl->options.dtls)
+ belowMinDowngrade = ssl->options.dtls
+ && pv.minor > ssl->options.minDowngrade;
+
+ if (belowMinDowngrade) {
WOLFSSL_MSG("\tversion below minimum allowed, fatal error");
#if defined(WOLFSSL_EXTRA_ALERTS) || defined(OPENSSL_EXTRA)
SendAlert(ssl, alert_fatal, handshake_failure);
@@ -30310,26 +30944,46 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
goto out;
}
- if (pv.minor == SSLv3_MINOR) {
- /* turn off tls */
- WOLFSSL_MSG("\tdowngrading to SSLv3");
- ssl->options.tls = 0;
- ssl->options.tls1_1 = 0;
- ssl->version.minor = SSLv3_MINOR;
+ if (!ssl->options.dtls) {
+ if (pv.minor == SSLv3_MINOR) {
+ /* turn off tls */
+ WOLFSSL_MSG("\tdowngrading to SSLv3");
+ ssl->options.tls = 0;
+ ssl->options.tls1_1 = 0;
+ ssl->version.minor = SSLv3_MINOR;
+ }
+ else if (pv.minor == TLSv1_MINOR) {
+ /* turn off tls 1.1+ */
+ WOLFSSL_MSG("\tdowngrading to TLSv1");
+ ssl->options.tls1_1 = 0;
+ ssl->version.minor = TLSv1_MINOR;
+ }
+ else if (pv.minor == TLSv1_1_MINOR) {
+ WOLFSSL_MSG("\tdowngrading to TLSv1.1");
+ ssl->version.minor = TLSv1_1_MINOR;
+ }
+ else if (pv.minor == TLSv1_2_MINOR) {
+ WOLFSSL_MSG(" downgrading to TLSv1.2");
+ ssl->version.minor = TLSv1_2_MINOR;
+ }
}
- else if (pv.minor == TLSv1_MINOR) {
- /* turn off tls 1.1+ */
- WOLFSSL_MSG("\tdowngrading to TLSv1");
- ssl->options.tls1_1 = 0;
- ssl->version.minor = TLSv1_MINOR;
- }
- else if (pv.minor == TLSv1_1_MINOR) {
- WOLFSSL_MSG("\tdowngrading to TLSv1.1");
- ssl->version.minor = TLSv1_1_MINOR;
- }
- else if (pv.minor == TLSv1_2_MINOR) {
- WOLFSSL_MSG(" downgrading to TLSv1.2");
- ssl->version.minor = TLSv1_2_MINOR;
+ else {
+ if (pv.minor == DTLSv1_2_MINOR) {
+ WOLFSSL_MSG("\tDowngrading to DTLSv1.2");
+ ssl->options.tls1_3 = 0;
+ ssl->version.minor = DTLSv1_2_MINOR;
+
+ /* reset hashes, DTLSv1.2 will take care of the hashing
+ later */
+ ret = InitHandshakeHashes(ssl);
+ if (ret != 0)
+ return ret;
+ }
+ else if (pv.minor == DTLS_MINOR) {
+ WOLFSSL_MSG("\tDowngrading to DTLSv1.2");
+ ssl->options.tls1_3 = 0;
+ ssl->version.minor = DTLS_MINOR;
+ }
}
#ifndef NO_RSA
haveRSA = 1;
diff --git a/src/keys.c b/src/keys.c
index b467275d9..672f074ed 100644
--- a/src/keys.c
+++ b/src/keys.c
@@ -2081,6 +2081,15 @@ int SetCipherSpecs(WOLFSSL* ssl)
#endif
}
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls &&
+ ssl->version.major == DTLS_MAJOR &&
+ ssl->version.minor <= DTLSv1_3_MINOR) {
+ ssl->options.tls = 1;
+ ssl->options.tls1_3 = 1;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
if (IsAtLeastTLSv1_3(ssl->version) || ssl->specs.cipher_type != block)
ssl->options.encThenMac = 0;
@@ -2959,9 +2968,14 @@ int SetKeysSide(WOLFSSL* ssl, enum encrypt_side side)
ssl->heap, ssl->devId, ssl->rng, ssl->options.tls1_3);
}
+#ifdef WOLFSSL_DTLS13
+ if (ret == 0 && ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version))
+ ret = Dtls13SetRecordNumberKeys(ssl, side);
+#endif /* WOLFSSL_DTLS13 */
+
#ifdef HAVE_SECURE_RENEGOTIATION
#ifdef WOLFSSL_DTLS
- if (ret == 0 && ssl->options.dtls) {
+ if (ret == 0 && ssl->options.dtls && !ssl->options.tls1_3) {
if (wc_encrypt)
wc_encrypt->src = keys == &ssl->keys ? KEYS : SCR;
if (wc_decrypt)
diff --git a/src/ssl.c b/src/ssl.c
index ef782458e..40412c777 100644
--- a/src/ssl.c
+++ b/src/ssl.c
@@ -4516,6 +4516,18 @@ static int SetMinVersionHelper(byte* minVersion, int version)
break;
#endif
+#ifdef WOLFSSL_DTLS13
+ case WOLFSSL_DTLSV1:
+ *minVersion = DTLS_MINOR;
+ break;
+ case WOLFSSL_DTLSV1_2:
+ *minVersion = DTLSv1_2_MINOR;
+ break;
+ case WOLFSSL_DTLSV1_3:
+ *minVersion = DTLSv1_3_MINOR;
+ break;
+#endif /* WOLFSSL_DTLS13 */
+
default:
WOLFSSL_MSG("Bad function argument");
return BAD_FUNC_ARG;
@@ -11490,6 +11502,30 @@ int wolfSSL_dtls_get_current_timeout(WOLFSSL* ssl)
return timeout;
}
+#ifdef WOLFSSL_DTLS13
+
+/*
+ * This API returns 1 when the user should set a short timeout for receiving
+ * data. It is recommended that it is at most 1/4 the value returned by
+ * wolfSSL_dtls_get_current_timeout().
+ */
+int wolfSSL_dtls13_use_quick_timeout(WOLFSSL* ssl)
+{
+ return ssl->dtls13FastTimeout;
+}
+
+/*
+ * When this is set, a DTLS 1.3 connection will send acks immediately when a
+ * disruption is detected to shortcut timeouts. This results in potentially
+ * more traffic but may make the handshake quicker.
+ */
+void wolfSSL_dtls13_set_send_more_acks(WOLFSSL* ssl, int value)
+{
+ if (ssl != NULL)
+ ssl->options.dtls13SendMoreAcks = !!value;
+}
+#endif /* WOLFSSL_DTLS13 */
+
int wolfSSL_DTLSv1_get_timeout(WOLFSSL* ssl, WOLFSSL_TIMEVAL* timeleft)
{
if (ssl && timeleft) {
@@ -11560,6 +11596,21 @@ int wolfSSL_dtls_got_timeout(WOLFSSL* ssl)
if (ssl == NULL)
return WOLFSSL_FATAL_ERROR;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
+ result = Dtls13RtxTimeout(ssl);
+ if (result < 0) {
+ if (result == WANT_WRITE)
+ ssl->dtls13SendingAckOrRtx = 1;
+ ssl->error = result;
+ WOLFSSL_ERROR(result);
+ return WOLFSSL_FATAL_ERROR;
+ }
+
+ return WOLFSSL_SUCCESS;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
if ((IsSCR(ssl) || !ssl->options.handShakeDone)) {
if (DtlsMsgPoolTimeout(ssl) < 0){
ssl->error = SOCKET_ERROR_E;
@@ -11782,6 +11833,7 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
{
#if !(defined(WOLFSSL_NO_TLS12) && defined(NO_OLD_TLS) && defined(WOLFSSL_TLS13))
int neededState;
+ byte advanceState;
#endif
int ret = 0;
@@ -11849,6 +11901,21 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
}
#endif
+ /* fragOffset is non-zero when sending fragments. On the last
+ * fragment, fragOffset is zero again, and the state can be
+ * advanced. */
+ advanceState = ssl->fragOffset == 0 &&
+ (ssl->options.connectState == CONNECT_BEGIN ||
+ ssl->options.connectState == HELLO_AGAIN ||
+ (ssl->options.connectState >= FIRST_REPLY_DONE &&
+ ssl->options.connectState <= FIRST_REPLY_FOURTH));
+;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version))
+ advanceState = advanceState && !ssl->dtls13SendingAckOrRtx;
+#endif /* WOLFSSL_DTLS13 */
+
if (ssl->buffers.outputBuffer.length > 0
#ifdef WOLFSSL_ASYNC_CRYPT
/* do not send buffered or advance state if last error was an
@@ -11856,15 +11923,9 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
&& ssl->error != WC_PENDING_E
#endif
) {
- if ( (ret = SendBuffered(ssl)) == 0) {
- /* fragOffset is non-zero when sending fragments. On the last
- * fragment, fragOffset is zero again, and the state can be
- * advanced. */
+ if ( (ssl->error = SendBuffered(ssl)) == 0) {
if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) {
- if (ssl->options.connectState == CONNECT_BEGIN ||
- ssl->options.connectState == HELLO_AGAIN ||
- (ssl->options.connectState >= FIRST_REPLY_DONE &&
- ssl->options.connectState <= FIRST_REPLY_FOURTH)) {
+ if (advanceState) {
ssl->options.connectState++;
WOLFSSL_MSG("connect state: "
"Advanced from last buffered fragment send");
@@ -11935,6 +11996,27 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
#endif
neededState = SERVER_HELLODONE_COMPLETE;
}
+#ifdef WOLFSSL_DTLS13
+
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)
+ && ssl->dtls13Rtx.sendAcks == 1) {
+ ssl->dtls13Rtx.sendAcks = 0;
+ /* we aren't negotiated the version yet, so we aren't sure
+ * the other end can speak v1.3. On the other side we have
+ * received a unified records, assuming that the
+ * ServerHello got lost, we will send an empty ACK. In case
+ * the server is a DTLS with version less than 1.3, it
+ * should just ignore the message */
+ if ((ssl->error = SendDtls13Ack(ssl)) < 0) {
+ if (ssl->error == WANT_WRITE)
+ ssl->dtls13SendingAckOrRtx = 1;
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+ }
+
+
+#endif /* WOLFSSL_DTLS13 */
}
ssl->options.connectState = HELLO_AGAIN;
@@ -12088,6 +12170,12 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl,
WOLFSSL_MSG("connect state: FINISHED_DONE");
FALL_THROUGH;
+#ifdef WOLFSSL_DTLS13
+ case WAIT_FINISHED_ACK:
+ ssl->options.connectState = FINISHED_DONE;
+ FALL_THROUGH;
+#endif /* WOLFSSL_DTLS13 */
+
case FINISHED_DONE :
/* get response */
while (ssl->options.serverState < SERVER_FINISHED_COMPLETE)
@@ -19806,6 +19894,8 @@ static const char* wolfSSL_internal_get_version(const ProtocolVersion* version)
return "DTLS";
case DTLSv1_2_MINOR :
return "DTLSv1.2";
+ case DTLSv1_3_MINOR :
+ return "DTLSv1.3";
default:
return "unknown";
}
diff --git a/src/tls.c b/src/tls.c
index 2c09b8ba7..8f9baf753 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -5633,6 +5633,58 @@ static int TLSX_UseSRTP(TLSX** extensions, word16 profiles, void* heap)
/******************************************************************************/
#ifdef WOLFSSL_TLS13
+static WC_INLINE int versionIsGreater(byte isDtls, byte a, byte b)
+{
+ (void)isDtls;
+
+#ifdef WOLFSSL_DTLS
+ /* DTLS version increases backwards (-1,-2,-3,etc) */
+ if (isDtls)
+ return a < b;
+#endif /* WOLFSSL_DTLS */
+
+ return a > b;
+}
+
+static WC_INLINE int versionIsLesser(byte isDtls, byte a, byte b)
+{
+ (void)isDtls;
+
+#ifdef WOLFSSL_DTLS
+ /* DTLS version increases backwards (-1,-2,-3,etc) */
+ if (isDtls)
+ return a > b;
+#endif /* WOLFSSL_DTLS */
+
+ return a < b;
+}
+
+static WC_INLINE int versionIsAtLeast(byte isDtls, byte a, byte b)
+{
+ (void)isDtls;
+
+#ifdef WOLFSSL_DTLS
+ /* DTLS version increases backwards (-1,-2,-3,etc) */
+ if (isDtls)
+ return a <= b;
+#endif /* WOLFSSL_DTLS */
+
+ return a >= b;
+}
+
+static WC_INLINE int versionIsLessEqual(byte isDtls, byte a, byte b)
+{
+ (void)isDtls;
+
+#ifdef WOLFSSL_DTLS
+ /* DTLS version increases backwards (-1,-2,-3,etc) */
+ if (isDtls)
+ return a >= b;
+#endif /* WOLFSSL_DTLS */
+
+ return a <= b;
+}
+
/* Return the size of the SupportedVersions extension's data.
*
* data The SSL/TLS object.
@@ -5642,12 +5694,23 @@ static int TLSX_UseSRTP(TLSX** extensions, word16 profiles, void* heap)
static int TLSX_SupportedVersions_GetSize(void* data, byte msgType, word16* pSz)
{
WOLFSSL* ssl = (WOLFSSL*)data;
+ byte tls13Minor, tls12Minor, tls11Minor, isDtls;
+
+ /* unused on some configuration */
+ (void)tls12Minor;
+ (void)tls13Minor;
+ (void)tls11Minor;
+
+ isDtls = !!ssl->options.dtls;
+ tls13Minor = (byte)(isDtls ? DTLSv1_3_MINOR : TLSv1_3_MINOR);
+ tls12Minor = (byte)(isDtls ? DTLSv1_2_MINOR : TLSv1_2_MINOR);
+ tls11Minor = (byte)(isDtls ? DTLS_MINOR : TLSv1_1_MINOR);
if (msgType == client_hello) {
/* TLS v1.2 and TLS v1.3 */
int cnt = 0;
- if ((ssl->options.minDowngrade <= TLSv1_3_MINOR)
+ if (versionIsLessEqual(isDtls, ssl->options.minDowngrade, tls13Minor)
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
defined(WOLFSSL_WPAS_SMALL)
&& (ssl->options.mask & SSL_OP_NO_TLSv1_3) == 0
@@ -5658,17 +5721,19 @@ static int TLSX_SupportedVersions_GetSize(void* data, byte msgType, word16* pSz)
if (ssl->options.downgrade) {
#ifndef WOLFSSL_NO_TLS12
- if ((ssl->options.minDowngrade <= TLSv1_2_MINOR)
- #if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
- defined(WOLFSSL_WPAS_SMALL)
+ if (versionIsLessEqual(
+ isDtls, ssl->options.minDowngrade, tls12Minor)
+#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
+ defined(WOLFSSL_WPAS_SMALL)
&& (ssl->options.mask & SSL_OP_NO_TLSv1_2) == 0
- #endif
+#endif
) {
cnt++;
}
- #endif
+#endif
#ifndef NO_OLD_TLS
- if ((ssl->options.minDowngrade <= TLSv1_1_MINOR)
+ if (versionIsLessEqual(
+ isDtls, ssl->options.minDowngrade, tls11Minor)
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
defined(WOLFSSL_WPAS_SMALL)
&& (ssl->options.mask & SSL_OP_NO_TLSv1_1) == 0
@@ -5677,7 +5742,7 @@ static int TLSX_SupportedVersions_GetSize(void* data, byte msgType, word16* pSz)
cnt++;
}
#ifdef WOLFSSL_ALLOW_TLSV10
- if ((ssl->options.minDowngrade <= TLSv1_MINOR)
+ if (!ssl->options.dtls && (ssl->options.minDowngrade <= TLSv1_MINOR)
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
defined(WOLFSSL_WPAS_SMALL)
&& (ssl->options.mask & SSL_OP_NO_TLSv1) == 0
@@ -5714,6 +5779,24 @@ static int TLSX_SupportedVersions_Write(void* data, byte* output,
WOLFSSL* ssl = (WOLFSSL*)data;
byte major;
byte* cnt;
+ byte tls13minor, tls12minor, tls11minor, isDtls = 0;
+
+ tls13minor = (byte)TLSv1_3_MINOR;
+ tls12minor = (byte)TLSv1_2_MINOR;
+ tls11minor = (byte)TLSv1_1_MINOR;
+
+ /* unused in some configuration */
+ (void)tls11minor;
+ (void)tls12minor;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ tls13minor = (byte)DTLSv1_3_MINOR;
+ tls12minor = (byte)DTLSv1_2_MINOR;
+ tls11minor = (byte)DTLS_MINOR;
+ isDtls = 1;
+ }
+#endif /* WOLFSSL_DTLS13 */
if (msgType == client_hello) {
major = ssl->ctx->method->version.major;
@@ -5721,11 +5804,11 @@ static int TLSX_SupportedVersions_Write(void* data, byte* output,
cnt = output++;
*cnt = 0;
- if ((ssl->options.minDowngrade <= TLSv1_3_MINOR)
- #if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
- defined(WOLFSSL_WPAS_SMALL)
+ if (versionIsLessEqual(isDtls, ssl->options.minDowngrade, tls13minor)
+#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
+ defined(WOLFSSL_WPAS_SMALL)
&& (ssl->options.mask & SSL_OP_NO_TLSv1_3) == 0
- #endif
+#endif
) {
*cnt += OPAQUE16_LEN;
#ifdef WOLFSSL_TLS13_DRAFT
@@ -5735,26 +5818,26 @@ static int TLSX_SupportedVersions_Write(void* data, byte* output,
*(output++) = TLS_DRAFT_MINOR;
#else
*(output++) = major;
- *(output++) = (byte)TLSv1_3_MINOR;
+ *(output++) = tls13minor;
#endif
}
if (ssl->options.downgrade) {
#ifndef WOLFSSL_NO_TLS12
- if ((ssl->options.minDowngrade <= TLSv1_2_MINOR)
- #if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
+ if (versionIsLessEqual(isDtls, ssl->options.minDowngrade, tls12minor)
+#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
defined(WOLFSSL_WPAS_SMALL)
&& (ssl->options.mask & SSL_OP_NO_TLSv1_2) == 0
#endif
) {
*cnt += OPAQUE16_LEN;
*(output++) = major;
- *(output++) = (byte)TLSv1_2_MINOR;
+ *(output++) = tls12minor;
}
#endif
#ifndef NO_OLD_TLS
- if ((ssl->options.minDowngrade <= TLSv1_1_MINOR)
+ if (versionIsLessEqual(isDtls, ssl->options.minDowngrade, tls11minor)
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
defined(WOLFSSL_WPAS_SMALL)
&& (ssl->options.mask & SSL_OP_NO_TLSv1_1) == 0
@@ -5762,10 +5845,10 @@ static int TLSX_SupportedVersions_Write(void* data, byte* output,
) {
*cnt += OPAQUE16_LEN;
*(output++) = major;
- *(output++) = (byte)TLSv1_1_MINOR;
+ *(output++) = tls11minor;
}
#ifdef WOLFSSL_ALLOW_TLSV10
- if ((ssl->options.minDowngrade <= TLSv1_MINOR)
+ if (!ssl->options.dtls && (ssl->options.minDowngrade <= TLSv1_MINOR)
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
defined(WOLFSSL_WPAS_SMALL)
&& (ssl->options.mask & SSL_OP_NO_TLSv1) == 0
@@ -5811,6 +5894,20 @@ static int TLSX_SupportedVersions_Parse(WOLFSSL* ssl, const byte* input,
int newMinor = 0;
int set = 0;
int ret;
+ int tls13minor;
+ int tls12minor;
+ byte isDtls;
+
+ tls13minor = TLSv1_3_MINOR;
+ tls12minor = TLSv1_2_MINOR;
+ isDtls = ssl->options.dtls == 1;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ tls13minor = DTLSv1_3_MINOR;
+ tls12minor = DTLSv1_2_MINOR;
+ }
+#endif /* WOLFSSL_DTLS13 */
if (msgType == client_hello) {
/* Must contain a length and at least one version. */
@@ -5844,23 +5941,27 @@ static int TLSX_SupportedVersions_Parse(WOLFSSL* ssl, const byte* input,
continue;
/* No upgrade allowed. */
- if (minor > ssl->version.minor)
+ if (versionIsGreater(isDtls, minor, ssl->version.minor))
continue;
+
/* Check downgrade. */
- if (minor < ssl->version.minor) {
+ if (versionIsLesser(isDtls, minor, ssl->version.minor)) {
if (!ssl->options.downgrade)
continue;
- if (minor < ssl->options.minDowngrade)
+ if (versionIsLesser(
+ isDtls, minor, ssl->options.minDowngrade))
continue;
- if (newMinor == 0 && minor > ssl->options.oldMinor) {
+ if (newMinor == 0 &&
+ versionIsGreater(
+ isDtls, minor, ssl->options.oldMinor)) {
/* Downgrade the version. */
ssl->version.minor = minor;
}
}
- if (minor >= TLSv1_3_MINOR) {
+ if (versionIsAtLeast(isDtls, minor, tls13minor)) {
if (!ssl->options.tls1_3) {
ssl->options.tls1_3 = 1;
ret = TLSX_Prepend(&ssl->extensions,
@@ -5870,12 +5971,13 @@ static int TLSX_SupportedVersions_Parse(WOLFSSL* ssl, const byte* input,
}
TLSX_SetResponse(ssl, TLSX_SUPPORTED_VERSIONS);
}
- if (minor > newMinor) {
+ if (versionIsGreater(isDtls, minor, newMinor)) {
ssl->version.minor = minor;
newMinor = minor;
}
}
- else if (minor > ssl->options.oldMinor)
+ else if (versionIsGreater(
+ isDtls, minor, ssl->options.oldMinor))
ssl->options.oldMinor = minor;
set = 1;
@@ -5901,25 +6003,26 @@ static int TLSX_SupportedVersions_Parse(WOLFSSL* ssl, const byte* input,
return VERSION_ERROR;
/* Can't downgrade with this extension below TLS v1.3. */
- if (minor < TLSv1_3_MINOR)
+ if (versionIsLesser(isDtls, minor, tls13minor))
return VERSION_ERROR;
/* Version is TLS v1.2 to handle downgrading from TLS v1.3+. */
- if (ssl->options.downgrade && ssl->version.minor == TLSv1_2_MINOR) {
+ if (ssl->options.downgrade && ssl->version.minor == tls12minor) {
/* Set minor version back to TLS v1.3+ */
ssl->version.minor = ssl->ctx->method->version.minor;
}
/* No upgrade allowed. */
- if (ssl->version.minor < minor)
+ if (versionIsLesser(isDtls, ssl->version.minor, minor))
return VERSION_ERROR;
/* Check downgrade. */
- if (ssl->version.minor > minor) {
+ if (versionIsGreater(isDtls, ssl->version.minor, minor)) {
if (!ssl->options.downgrade)
return VERSION_ERROR;
- if (minor < ssl->options.minDowngrade)
+ if (versionIsLesser(
+ isDtls, minor, ssl->options.minDowngrade))
return VERSION_ERROR;
/* Downgrade the version. */
@@ -12412,7 +12515,9 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType,
(void)heap;
WOLFSSL_ENTER("DTLS_client_method_ex");
if (method) {
- #if !defined(WOLFSSL_NO_TLS12)
+ #if defined(WOLFSSL_DTLS13)
+ InitSSL_Method(method, MakeDTLSv1_3());
+ #elif !defined(WOLFSSL_NO_TLS12)
InitSSL_Method(method, MakeDTLSv1_2());
#elif !defined(NO_OLD_TLS)
InitSSL_Method(method, MakeDTLSv1());
@@ -12773,7 +12878,9 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType,
(void)heap;
WOLFSSL_ENTER("DTLS_server_method_ex");
if (method) {
- #if !defined(WOLFSSL_NO_TLS12)
+ #if defined(WOLFSSL_DTLS13)
+ InitSSL_Method(method, MakeDTLSv1_3());
+ #elif !defined(WOLFSSL_NO_TLS12)
InitSSL_Method(method, MakeDTLSv1_2());
#elif !defined(NO_OLD_TLS)
InitSSL_Method(method, MakeDTLSv1());
diff --git a/src/tls13.c b/src/tls13.c
index dc9eb8b36..1ea3d0794 100644
--- a/src/tls13.c
+++ b/src/tls13.c
@@ -153,6 +153,11 @@
/* The protocol label for TLS v1.3. */
static const byte tls13ProtocolLabel[TLS13_PROTOCOL_LABEL_SZ + 1] = "tls13 ";
+#ifdef WOLFSSL_DTLS13
+#define DTLS13_PROTOCOL_LABEL_SZ 6
+static const byte dtls13ProtocolLabel[DTLS13_PROTOCOL_LABEL_SZ + 1] = "dtls13";
+#endif /* WOLFSSL_DTLS13 */
+
/* Derive a key from a message.
*
* ssl The SSL/TLS object.
@@ -234,7 +239,15 @@ static int DeriveKeyMsg(WOLFSSL* ssl, byte* output, int outputLen,
protocol = tls13ProtocolLabel;
protocolLen = TLS13_PROTOCOL_LABEL_SZ;
break;
+#ifdef WOLFSSL_DTLS13
+ case DTLSv1_3_MINOR:
+ if (!ssl->options.dtls)
+ return VERSION_ERROR;
+ protocol = dtls13ProtocolLabel;
+ protocolLen = DTLS13_PROTOCOL_LABEL_SZ;
+ break;
+#endif /* WOLFSSL_DTLS13 */
default:
return VERSION_ERROR;
}
@@ -261,9 +274,9 @@ static int DeriveKeyMsg(WOLFSSL* ssl, byte* output, int outputLen,
* includeMsgs Whether to include a hash of the handshake messages so far.
* returns 0 on success, otherwise failure.
*/
-static int DeriveKey(WOLFSSL* ssl, byte* output, int outputLen,
- const byte* secret, const byte* label, word32 labelLen,
- int hashAlgo, int includeMsgs)
+int Tls13DeriveKey(WOLFSSL* ssl, byte* output, int outputLen,
+ const byte* secret, const byte* label, word32 labelLen,
+ int hashAlgo, int includeMsgs)
{
int ret = 0;
byte hash[WC_MAX_DIGEST_SIZE];
@@ -308,10 +321,16 @@ static int DeriveKey(WOLFSSL* ssl, byte* output, int outputLen,
if (ret != 0)
return ret;
- /* Only one protocol version defined at this time. */
protocol = tls13ProtocolLabel;
protocolLen = TLS13_PROTOCOL_LABEL_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ protocol = dtls13ProtocolLabel;
+ protocolLen = DTLS13_PROTOCOL_LABEL_SZ;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
if (outputLen == -1)
outputLen = hashSz;
if (includeMsgs)
@@ -441,7 +460,7 @@ static int DeriveEarlyTrafficSecret(WOLFSSL* ssl, byte* key)
if (ssl == NULL || ssl->arrays == NULL) {
return BAD_FUNC_ARG;
}
- ret = DeriveKey(ssl, key, -1, ssl->arrays->secret,
+ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->secret,
earlyTrafficLabel, EARLY_TRAFFIC_LABEL_SZ,
ssl->specs.mac_algorithm, 1);
#ifdef HAVE_SECRET_CALLBACK
@@ -486,7 +505,7 @@ static int DeriveClientHandshakeSecret(WOLFSSL* ssl, byte* key)
if (ssl == NULL || ssl->arrays == NULL) {
return BAD_FUNC_ARG;
}
- ret = DeriveKey(ssl, key, -1, ssl->arrays->preMasterSecret,
+ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->preMasterSecret,
clientHandshakeLabel, CLIENT_HANDSHAKE_LABEL_SZ,
ssl->specs.mac_algorithm, 1);
#ifdef HAVE_SECRET_CALLBACK
@@ -529,7 +548,7 @@ static int DeriveServerHandshakeSecret(WOLFSSL* ssl, byte* key)
if (ssl == NULL || ssl->arrays == NULL) {
return BAD_FUNC_ARG;
}
- ret = DeriveKey(ssl, key, -1, ssl->arrays->preMasterSecret,
+ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->preMasterSecret,
serverHandshakeLabel, SERVER_HANDSHAKE_LABEL_SZ,
ssl->specs.mac_algorithm, 1);
#ifdef HAVE_SECRET_CALLBACK
@@ -572,7 +591,7 @@ static int DeriveClientTrafficSecret(WOLFSSL* ssl, byte* key)
if (ssl == NULL || ssl->arrays == NULL) {
return BAD_FUNC_ARG;
}
- ret = DeriveKey(ssl, key, -1, ssl->arrays->masterSecret,
+ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->masterSecret,
clientAppLabel, CLIENT_APP_LABEL_SZ,
ssl->specs.mac_algorithm, 1);
#ifdef HAVE_SECRET_CALLBACK
@@ -615,7 +634,7 @@ static int DeriveServerTrafficSecret(WOLFSSL* ssl, byte* key)
if (ssl == NULL || ssl->arrays == NULL) {
return BAD_FUNC_ARG;
}
- ret = DeriveKey(ssl, key, -1, ssl->arrays->masterSecret,
+ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->masterSecret,
serverAppLabel, SERVER_APP_LABEL_SZ,
ssl->specs.mac_algorithm, 1);
#ifdef HAVE_SECRET_CALLBACK
@@ -659,9 +678,9 @@ static int DeriveExporterSecret(WOLFSSL* ssl, byte* key)
if (ssl == NULL || ssl->arrays == NULL) {
return BAD_FUNC_ARG;
}
- ret = DeriveKey(ssl, key, -1, ssl->arrays->masterSecret,
- exporterMasterLabel, EXPORTER_MASTER_LABEL_SZ,
- ssl->specs.mac_algorithm, 1);
+ ret = Tls13DeriveKey(ssl, key, -1, ssl->arrays->masterSecret,
+ exporterMasterLabel, EXPORTER_MASTER_LABEL_SZ,
+ ssl->specs.mac_algorithm, 1);
#ifdef HAVE_SECRET_CALLBACK
if (ret == 0 && ssl->tls13SecretCb != NULL) {
ret = ssl->tls13SecretCb(ssl, EXPORTER_SECRET, key,
@@ -813,7 +832,7 @@ int DeriveResumptionSecret(WOLFSSL* ssl, byte* key)
else {
masterSecret = ssl->session->masterSecret;
}
- return DeriveKey(ssl, key, -1, masterSecret, resumeMasterLabel,
+ return Tls13DeriveKey(ssl, key, -1, masterSecret, resumeMasterLabel,
RESUME_MASTER_LABEL_SZ, ssl->specs.mac_algorithm, 1);
}
#endif
@@ -832,7 +851,7 @@ static const byte finishedLabel[FINISHED_LABEL_SZ+1] = "finished";
static int DeriveFinishedSecret(WOLFSSL* ssl, byte* key, byte* secret)
{
WOLFSSL_MSG("Derive Finished Secret");
- return DeriveKey(ssl, secret, -1, key, finishedLabel, FINISHED_LABEL_SZ,
+ return Tls13DeriveKey(ssl, secret, -1, key, finishedLabel, FINISHED_LABEL_SZ,
ssl->specs.mac_algorithm, 0);
}
@@ -851,7 +870,7 @@ static const byte appTrafficLabel[APP_TRAFFIC_LABEL_SZ + 1] =
static int DeriveTrafficSecret(WOLFSSL* ssl, byte* secret)
{
WOLFSSL_MSG("Derive New Application Traffic Secret");
- return DeriveKey(ssl, secret, -1, secret,
+ return Tls13DeriveKey(ssl, secret, -1, secret,
appTrafficLabel, APP_TRAFFIC_LABEL_SZ,
ssl->specs.mac_algorithm, 0);
}
@@ -1220,7 +1239,7 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store)
if (provision & PROVISION_CLIENT) {
/* Derive the client key. */
WOLFSSL_MSG("Derive Client Key");
- ret = DeriveKey(ssl, &key_dig[i], ssl->specs.key_size,
+ ret = Tls13DeriveKey(ssl, &key_dig[i], ssl->specs.key_size,
ssl->clientSecret, writeKeyLabel,
WRITE_KEY_LABEL_SZ, ssl->specs.mac_algorithm, 0);
if (ret != 0)
@@ -1231,7 +1250,7 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store)
if (provision & PROVISION_SERVER) {
/* Derive the server key. */
WOLFSSL_MSG("Derive Server Key");
- ret = DeriveKey(ssl, &key_dig[i], ssl->specs.key_size,
+ ret = Tls13DeriveKey(ssl, &key_dig[i], ssl->specs.key_size,
ssl->serverSecret, writeKeyLabel,
WRITE_KEY_LABEL_SZ, ssl->specs.mac_algorithm, 0);
if (ret != 0)
@@ -1242,7 +1261,7 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store)
if (provision & PROVISION_CLIENT) {
/* Derive the client IV. */
WOLFSSL_MSG("Derive Client IV");
- ret = DeriveKey(ssl, &key_dig[i], ssl->specs.iv_size,
+ ret = Tls13DeriveKey(ssl, &key_dig[i], ssl->specs.iv_size,
ssl->clientSecret, writeIVLabel,
WRITE_IV_LABEL_SZ, ssl->specs.mac_algorithm, 0);
if (ret != 0)
@@ -1253,7 +1272,7 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store)
if (provision & PROVISION_SERVER) {
/* Derive the server IV. */
WOLFSSL_MSG("Derive Server IV");
- ret = DeriveKey(ssl, &key_dig[i], ssl->specs.iv_size,
+ ret = Tls13DeriveKey(ssl, &key_dig[i], ssl->specs.iv_size,
ssl->serverSecret, writeIVLabel,
WRITE_IV_LABEL_SZ, ssl->specs.mac_algorithm, 0);
if (ret != 0)
@@ -1264,6 +1283,18 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store)
/* Store keys and IVs but don't activate them. */
ret = StoreKeys(ssl, key_dig, provision);
+#ifdef WOLFSSL_DTLS13
+ if (ret != 0)
+ goto end;
+
+ if (ssl->options.dtls) {
+ ret = Dtls13DeriveSnKeys(ssl, provision);
+ if (ret != 0)
+ return ret;
+ }
+
+#endif /* WOLFSSL_DTLS13 */
+
end:
ForceZero(key_dig, i);
#ifdef WOLFSSL_SMALL_STACK
@@ -1617,6 +1648,18 @@ static void AddTls13HandShakeHeader(byte* output, word32 length,
(void)fragLength;
(void)ssl;
+#ifdef WOLFSSL_DTLS13
+ /* message_hash type is used for a syntetic message that replaces the first
+ ClientHello in the hash transcript when using HelloRetryRequest. It will
+ never be transmitted and, as the DTLS-only fields must not be considered
+ when computing the hash transcript, we can avoid to use the DTLS
+ handshake header. */
+ if (ssl->options.dtls && type != message_hash) {
+ Dtls13HandshakeAddHeader(ssl, output, (enum HandShakeType)type, length);
+ return;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
/* handshake header */
hs = (HandShakeHeader*)output;
hs->type = type;
@@ -1637,6 +1680,13 @@ static void AddTls13Headers(byte* output, word32 length, byte type,
word32 lengthAdj = HANDSHAKE_HEADER_SZ;
word32 outputAdj = RECORD_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ Dtls13AddHeaders(output, length, (enum HandShakeType)type, ssl);
+ return;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
AddTls13RecordHeader(output, length + lengthAdj, handshake, ssl);
AddTls13HandShakeHeader(output + outputAdj, length, 0, length, type, ssl);
}
@@ -1659,6 +1709,15 @@ static void AddTls13FragHeaders(byte* output, word32 fragSz, word32 fragOffset,
word32 outputAdj = RECORD_HEADER_SZ;
(void)fragSz;
+#ifdef WOLFSSL_DTLS13
+ /* we ignore fragmentation fields here because fragmentation logic for
+ DTLS1.3 is inside dtls13_handshake_send(). */
+ if (ssl->options.dtls) {
+ Dtls13AddHeaders(output, length, (enum HandShakeType)type, ssl);
+ return;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
AddTls13RecordHeader(output, fragSz + lengthAdj, handshake, ssl);
AddTls13HandShakeHeader(output + outputAdj, length, fragOffset, fragSz,
type, ssl);
@@ -1676,7 +1735,12 @@ static WC_INLINE void WriteSEQTls13(WOLFSSL* ssl, int verifyOrder, byte* out)
{
word32 seq[2] = {0, 0};
- if (verifyOrder) {
+ if (ssl->options.dtls) {
+#ifdef WOLFSSL_DTLS13
+ Dtls13GetSeq(ssl, verifyOrder, seq, 1);
+#endif /* WOLFSSL_DTLS13 */
+ }
+ else if (verifyOrder) {
seq[0] = ssl->keys.peer_sequence_number_hi;
seq[1] = ssl->keys.peer_sequence_number_lo++;
/* handle rollover */
@@ -2392,9 +2456,15 @@ int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
ssl->options.buildMsgState = BUILD_MSG_BEGIN;
XMEMSET(args, 0, sizeof(BuildMsg13Args));
- args->sz = RECORD_HEADER_SZ + inSz;
- args->idx = RECORD_HEADER_SZ;
args->headerSz = RECORD_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ args->headerSz = Dtls13GetRlHeaderLength(1);
+#endif /* WOLFSSL_DTLS13 */
+
+ args->sz = args->headerSz + inSz;
+ args->idx = args->headerSz;
+
#ifdef WOLFSSL_ASYNC_CRYPT
if (asyncOkay)
ssl->async->freeArgs = FreeBuildMsg13Args;
@@ -2435,7 +2505,15 @@ int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
* Always have the content type as application data for encrypted
* messages in TLS v1.3.
*/
- AddTls13RecordHeader(output, args->size, application_data, ssl);
+
+ if (ssl->options.dtls) {
+#ifdef WOLFSSL_DTLS13
+ Dtls13RlAddCiphertextHeader(ssl, output, args->size);
+#endif /* WOLFSSL_DTLS13 */
+ }
+ else {
+ AddTls13RecordHeader(output, args->size, application_data, ssl);
+ }
/* TLS v1.3 can do in place encryption. */
if (input != output + args->idx)
@@ -2478,7 +2556,13 @@ int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
const byte* aad = output;
output += args->headerSz;
ret = EncryptTls13(ssl, output, output, args->size, aad,
- RECORD_HEADER_SZ, asyncOkay);
+ args->headerSz, asyncOkay);
+#ifdef WOLFSSL_DTLS13
+ if (ret == 0 && ssl->options.dtls) {
+ /* AAD points to the header. Reuse the variable */
+ ret = Dtls13EncryptRecordNumber(ssl, (byte*)aad, args->sz);
+ }
+#endif /* WOLFSSL_DTLS13 */
}
break;
}
@@ -2644,6 +2728,8 @@ static int CreateCookie(WOLFSSL* ssl, byte* hash, byte hashSz)
}
#endif
+#define HRR_MAX_HS_HEADER_SZ HANDSHAKE_HEADER_SZ
+
/* Restart the handshake hash with a hash of the previous messages.
*
* ssl The SSL/TLS object.
@@ -2653,7 +2739,7 @@ int RestartHandshakeHash(WOLFSSL* ssl)
{
int ret;
Hashes hashes;
- byte header[HANDSHAKE_HEADER_SZ] = {0};
+ byte header[HRR_MAX_HS_HEADER_SZ] = {0};
byte* hash = NULL;
byte hashSz = 0;
@@ -2998,7 +3084,14 @@ static int WritePSKBinders(WOLFSSL* ssl, byte* output, word32 idx)
idx -= len;
/* Hash truncated ClientHello - up to binders. */
- ret = HashOutput(ssl, output, idx, 0);
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ ret = Dtls13HashHandshake(ssl, output + Dtls13GetRlHeaderLength(0),
+ idx - Dtls13GetRlHeaderLength(0));
+ else
+#endif /* WOLFSSL_DTLS13 */
+ ret = HashOutput(ssl, output, idx, 0);
+
if (ret != 0)
return ret;
@@ -3061,6 +3154,16 @@ static int WritePSKBinders(WOLFSSL* ssl, byte* output, word32 idx)
return ret;
if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0)
return ret;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13NewEpoch(
+ ssl, w64From32(0x0, DTLS13_EPOCH_EARLYDATA), ENCRYPT_SIDE_ONLY);
+ if (ret != 0)
+ return ret;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
}
#endif
@@ -3096,10 +3199,22 @@ int SendTls13ClientHello(WOLFSSL* ssl)
#else
Sch13Args args[1];
#endif
+ byte major, tls12minor;
+
WOLFSSL_START(WC_FUNC_CLIENT_HELLO_SEND);
WOLFSSL_ENTER("SendTls13ClientHello");
+ major = SSLv3_MAJOR;
+ tls12minor = TLSv1_2_MINOR;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ major = DTLS_MAJOR;
+ tls12minor = DTLSv1_2_MINOR;
+ }
+#endif /* WOLFSSL_DTLS */
+
if (ssl == NULL) {
return BAD_FUNC_ARG;
}
@@ -3156,8 +3271,14 @@ int SendTls13ClientHello(WOLFSSL* ssl)
switch (ssl->options.asyncState) {
case TLS_ASYNC_BEGIN:
{
+
args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ args->idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+#endif /* WOLFSSL_DTLS13 */
+
/* Version | Random | Session Id | Cipher Suites | Compression */
args->length = VERSION_SZ + RAN_LEN + ENUM_LEN + ssl->suites->suiteSz +
SUITE_LEN + COMP_LEN + ENUM_LEN;
@@ -3168,6 +3289,12 @@ int SendTls13ClientHello(WOLFSSL* ssl)
args->length += ssl->session->sessionIDSz;
#endif
+#ifdef WOLFSSL_DTLS13
+ /* legacy_cookie_id (always 0 length) */
+ if (ssl->options.dtls)
+ args->length += OPAQUE8_LEN;
+#endif /* WOLFSSL_DTLS13 */
+
/* Advance state and proceed */
ssl->options.asyncState = TLS_ASYNC_BUILD;
} /* case TLS_ASYNC_BEGIN */
@@ -3213,6 +3340,11 @@ int SendTls13ClientHello(WOLFSSL* ssl)
/* Total message size. */
args->sendSz = args->length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ args->sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+#endif /* WOLFSSL_DTLS13 */
+
/* Check buffers are big enough and grow if needed. */
if ((ret = CheckAvailableSize(ssl, args->sendSz)) != 0)
return ret;
@@ -3225,8 +3357,9 @@ int SendTls13ClientHello(WOLFSSL* ssl)
AddTls13Headers(args->output, args->length, client_hello, ssl);
/* Protocol version - negotiation now in extension: supported_versions. */
- args->output[args->idx++] = SSLv3_MAJOR;
- args->output[args->idx++] = TLSv1_2_MINOR;
+ args->output[args->idx++] = major;
+ args->output[args->idx++] = tls12minor;
+
/* Keep for downgrade. */
ssl->chVersion = ssl->version;
@@ -3264,6 +3397,12 @@ int SendTls13ClientHello(WOLFSSL* ssl)
#endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */
}
+#ifdef WOLFSSL_DTLS13
+ /* legacy_cookie_id. always 0 length vector */
+ if (ssl->options.dtls)
+ args->output[args->idx++] = 0;
+#endif /* WOLFSSL_DTLS13 */
+
/* Cipher suites */
c16toa(ssl->suites->suiteSz, args->output + args->idx);
args->idx += OPAQUE16_LEN;
@@ -3297,11 +3436,21 @@ int SendTls13ClientHello(WOLFSSL* ssl)
/* Resumption has a specific set of extensions and binder is calculated
* for each identity.
*/
- if (TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY))
+ if (TLSX_Find(ssl->extensions, TLSX_PRE_SHARED_KEY)) {
ret = WritePSKBinders(ssl, args->output, args->idx);
+ }
else
#endif
- ret = HashOutput(ssl, args->output, args->idx, 0);
+ {
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ ret = Dtls13HashHandshake(ssl,
+ args->output + Dtls13GetRlHeaderLength(0),
+ args->idx - Dtls13GetRlHeaderLength(0));
+ else
+#endif /* WOLFSSL_DTLS13 */
+ ret = HashOutput(ssl, args->output, args->idx, 0);
+ }
if (ret != 0)
return ret;
@@ -3315,6 +3464,17 @@ int SendTls13ClientHello(WOLFSSL* ssl)
}
#endif
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13HandshakeSend(ssl, args->output, args->sendSz,
+ args->idx, client_hello, 0);
+
+ WOLFSSL_LEAVE("SendTls13ClientHello", ret);
+ WOLFSSL_END(WC_FUNC_CLIENT_HELLO_SEND);
+ return ret;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
ssl->buffers.outputBuffer.length += args->sendSz;
/* Advance state and proceed */
@@ -3372,6 +3532,7 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
{
int ret;
byte suite[2];
+ byte tls12minor;
#ifdef WOLFSSL_ASYNC_CRYPT
Dsh13Args* args = NULL;
WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args);
@@ -3382,6 +3543,14 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
WOLFSSL_START(WC_FUNC_SERVER_HELLO_DO);
WOLFSSL_ENTER("DoTls13ServerHello");
+ tls12minor = TLSv1_2_MINOR;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ tls12minor = DTLSv1_2_MINOR;
+#endif /* WOLFSSL_DTLS13 */
+
+
if (ssl == NULL || ssl->arrays == NULL)
return BAD_FUNC_ARG;
@@ -3436,18 +3605,37 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
args->idx += OPAQUE16_LEN;
#ifndef WOLFSSL_NO_TLS12
- if (args->pv.major == ssl->version.major &&
- args->pv.minor < TLSv1_2_MINOR &&
- ssl->options.downgrade)
{
- /* Force client hello version 1.2 to work for static RSA. */
- ssl->chVersion.minor = TLSv1_2_MINOR;
- ssl->version.minor = TLSv1_2_MINOR;
- return DoServerHello(ssl, input, inOutIdx, helloSz);
+ byte wantDowngrade;
+
+ wantDowngrade = args->pv.major == ssl->version.major &&
+ args->pv.minor < TLSv1_2_MINOR;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ wantDowngrade = args->pv.major == ssl->version.major &&
+ args->pv.minor > DTLSv1_2_MINOR;
+#endif /* WOLFSSL_DTLS13 */
+
+ if (wantDowngrade && ssl->options.downgrade) {
+ /* Force client hello version 1.2 to work for static RSA. */
+ ssl->chVersion.minor = TLSv1_2_MINOR;
+ ssl->version.minor = TLSv1_2_MINOR;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ssl->chVersion.minor = DTLSv1_2_MINOR;
+ ssl->version.minor = DTLSv1_2_MINOR;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
+ return DoServerHello(ssl, input, inOutIdx, helloSz);
+ }
}
#endif
+
if (args->pv.major != ssl->version.major ||
- args->pv.minor != TLSv1_2_MINOR) {
+ args->pv.minor != tls12minor) {
return VERSION_ERROR;
}
@@ -3510,6 +3698,14 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
/* Force client hello version 1.2 to work for static RSA. */
ssl->chVersion.minor = TLSv1_2_MINOR;
ssl->version.minor = TLSv1_2_MINOR;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ssl->chVersion.minor = DTLSv1_2_MINOR;
+ ssl->version.minor = DTLSv1_2_MINOR;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
#endif
ssl->options.haveEMS = 0;
if (args->pv.minor < ssl->options.minDowngrade)
@@ -3553,8 +3749,13 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
#endif
- if (args->pv.minor < ssl->options.minDowngrade)
+ if (!ssl->options.dtls &&
+ args->pv.minor < ssl->options.minDowngrade)
return VERSION_ERROR;
+
+ if (ssl->options.dtls && args->pv.minor > ssl->options.minDowngrade)
+ return VERSION_ERROR;
+
ssl->version.minor = args->pv.minor;
}
}
@@ -4354,6 +4555,16 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0)
return ret;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13NewEpoch(ssl,
+ w64From32(0x0, DTLS13_EPOCH_EARLYDATA),
+ DECRYPT_SIDE_ONLY);
+ if (ret != 0)
+ return ret;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
ssl->earlyData = process_early_data;
}
else
@@ -4461,7 +4672,7 @@ static int CheckCookie(WOLFSSL* ssl, byte* cookie, byte cookieSz)
#define HRR_BODY_SZ (VERSION_SZ + RAN_LEN + ENUM_LEN + ID_LEN + \
SUITE_LEN + COMP_LEN + OPAQUE16_LEN)
/* HH | PV | CipherSuite | Ext Len | Key Share | Supported Version | Cookie */
-#define MAX_HRR_SZ (HANDSHAKE_HEADER_SZ + \
+#define MAX_HRR_SZ (HRR_MAX_HS_HEADER_SZ + \
HRR_BODY_SZ + \
HRR_KEY_SHARE_SZ + \
HRR_VERSIONS_SZ + \
@@ -4476,7 +4687,7 @@ static int CheckCookie(WOLFSSL* ssl, byte* cookie, byte cookieSz)
*/
static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
{
- byte header[HANDSHAKE_HEADER_SZ] = {0};
+ byte header[HRR_MAX_HS_HEADER_SZ] = {0};
byte hrr[MAX_HRR_SZ] = {0};
int hrrIdx;
word32 idx;
@@ -4496,6 +4707,7 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
/* Restart handshake hash with synthetic message hash. */
AddTls13HandShakeHeader(header, hashSz, 0, 0, message_hash, ssl);
+
if ((ret = InitHandshakeHashes(ssl)) != 0)
return ret;
if ((ret = HashRaw(ssl, header, sizeof(header))) != 0)
@@ -4518,9 +4730,14 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
idx += hashSz;
hrrIdx = HANDSHAKE_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ hrrIdx += DTLS_HANDSHAKE_EXTRA;
+#endif /* WOLFSSL_DTLS13 */
+
/* The negotiated protocol version. */
hrr[hrrIdx++] = ssl->version.major;
- hrr[hrrIdx++] = TLSv1_2_MINOR;
+ hrr[hrrIdx++] = ssl->options.dtls ? DTLSv1_2_MINOR : TLSv1_2_MINOR;
/* HelloRetryRequest message has fixed value for random. */
XMEMCPY(hrr + hrrIdx, helloRetryRequestRandom, RAN_LEN);
@@ -4580,8 +4797,19 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
WOLFSSL_BUFFER(cookieData, cookie->len);
#endif
- if ((ret = HashRaw(ssl, hrr, hrrIdx)) != 0)
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13HashHandshake(ssl, hrr, hrrIdx);
+ }
+ else
+#endif /* WOLFSSL_DTLS13 */
+ {
+ ret = HashRaw(ssl, hrr, hrrIdx);
+ }
+
+ if (ret != 0)
return ret;
+
return HashRaw(ssl, cookieData, cookie->len);
}
#endif
@@ -4611,6 +4839,16 @@ static int DoTls13SupportedVersions(WOLFSSL* ssl, const byte* input, word32 i,
return BUFFER_ERROR;
}
i += b;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ /* legacy_cookie - not used in DTLS v1.3 */
+ b = input[i++];
+ if (i + b > helloSz) {
+ return BUFFER_ERROR;
+ }
+ i += b;
+ }
+#endif /* WOLFSSL_DTLS13 */
/* Cipher suites */
if (i + OPAQUE16_LEN > helloSz)
return BUFFER_ERROR;
@@ -4747,6 +4985,9 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
XMEMCPY(&args->pv, input + args->idx, OPAQUE16_LEN);
ssl->chVersion = args->pv; /* store */
args->idx += OPAQUE16_LEN;
+
+
+ /* this check pass for DTLS Major (0xff) */
if (args->pv.major < SSLv3_MAJOR) {
WOLFSSL_MSG("Legacy version field contains unsupported value");
#ifdef WOLFSSL_MYSQL_COMPATIBLE
@@ -4757,25 +4998,38 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
ERROR_OUT(INVALID_PARAMETER, exit_dch);
}
- /* Legacy protocol version cannot negotiate TLS 1.3 or higher. */
- if (args->pv.major > SSLv3_MAJOR || (args->pv.major == SSLv3_MAJOR &&
- args->pv.minor >= TLSv1_3_MINOR)) {
- args->pv.major = SSLv3_MAJOR;
- args->pv.minor = TLSv1_2_MINOR;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls &&
+ args->pv.major == DTLS_MAJOR && args->pv.minor > DTLSv1_2_MINOR) {
wantDowngrade = 1;
ssl->version.minor = args->pv.minor;
}
- /* Legacy version must be [ SSLv3_MAJOR, TLSv1_2_MINOR ] for TLS v1.3 */
- else if (args->pv.major == SSLv3_MAJOR && args->pv.minor < TLSv1_2_MINOR) {
- wantDowngrade = 1;
- ssl->version.minor = args->pv.minor;
+#endif /* WOLFSSL_DTLS13 */
+
+ if (!ssl->options.dtls) {
+ /* Legacy protocol version cannot negotiate TLS 1.3 or higher. */
+ if (args->pv.major > SSLv3_MAJOR || (args->pv.major == SSLv3_MAJOR &&
+ args->pv.minor >= TLSv1_3_MINOR)) {
+ args->pv.major = SSLv3_MAJOR;
+ args->pv.minor = TLSv1_2_MINOR;
+ wantDowngrade = 1;
+ ssl->version.minor = args->pv.minor;
+ }
+ /* Legacy version must be [ SSLv3_MAJOR, TLSv1_2_MINOR ] for TLS v1.3 */
+ else if (args->pv.major == SSLv3_MAJOR &&
+ args->pv.minor < TLSv1_2_MINOR) {
+ wantDowngrade = 1;
+ ssl->version.minor = args->pv.minor;
+ }
}
- else {
+
+ if (!wantDowngrade) {
ret = DoTls13SupportedVersions(ssl, input + args->begin,
args->idx - args->begin, helloSz, &wantDowngrade);
if (ret < 0)
goto exit_dch;
}
+
if (wantDowngrade) {
#ifndef WOLFSSL_NO_TLS12
if (!ssl->options.downgrade) {
@@ -4787,7 +5041,9 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
ERROR_OUT(VERSION_ERROR, exit_dch);
}
- if (args->pv.minor < ssl->options.minDowngrade) {
+ if ((!ssl->options.dtls
+ && args->pv.minor < ssl->options.minDowngrade) ||
+ (ssl->options.dtls && args->pv.minor > ssl->options.minDowngrade)) {
WOLFSSL_MSG("\tversion below minimum allowed, fatal error");
#if defined(WOLFSSL_EXTRA_ALERTS) || defined(OPENSSL_EXTRA)
SendAlert(ssl, alert_fatal, handshake_failure);
@@ -4832,6 +5088,12 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
args->idx += ID_LEN;
}
+#ifdef WOLFSSL_DTLS13
+ /* legacy_cookie */
+ if (ssl->options.dtls)
+ args->idx += OPAQUE8_LEN;
+#endif /* WOLFSSL_DTLS13 */
+
args->clSuites = (Suites*)XMALLOC(sizeof(Suites), ssl->heap,
DYNAMIC_TYPE_SUITES);
if (args->clSuites == NULL) {
@@ -5037,6 +5299,26 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
ret = INPUT_CASE_ERROR;
} /* switch (ssl->options.asyncState) */
+#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_SEND_HRR_COOKIE)
+ /* We are using DTLSv13 and set the HRR cookie secret, use the cookie to
+ perform a return-routability check. */
+ if (ret == 0 && ssl->options.dtls && ssl->options.sendCookie &&
+ ssl->options.serverState != SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
+
+ /* ssl->options.serverState != SERVER_HELLO_RETRY_REQUEST_COMPLETE
+ so the client already provided a good KeyShareEntry. In this case
+ we don't add the KEY_SHARE extension to the HelloRetryRequest or
+ in the Cookie. The RFC8446 forbids to select a supported group
+ with KeyShare extension in HelloRetryRequest if the client
+ already provided a KeyShareEntry for that group. See rfc8446
+ section 4.1.4 */
+ TLSX_Remove(&ssl->extensions, TLSX_KEY_SHARE, ssl->heap);
+
+ /* send an HRR (see wolfSSL_Accept_TLSv13()) */
+ ssl->options.serverState = SERVER_HELLO_RETRY_REQUEST_COMPLETE;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
exit_dch:
WOLFSSL_LEAVE("DoTls13ClientHello", ret);
@@ -5081,6 +5363,11 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
return ret;
}
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ idx = DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ;
+#endif /* WOLFSSL_DTLS13 */
+
/* Protocol version, server random, session id, cipher suite, compression
* and extensions.
*/
@@ -5104,7 +5391,7 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
/* The protocol version must be TLS v1.2 for middleboxes. */
output[idx++] = ssl->version.major;
- output[idx++] = TLSv1_2_MINOR;
+ output[idx++] = ssl->options.dtls ? DTLSv1_2_MINOR : TLSv1_2_MINOR;
if (extMsgType == server_hello) {
/* Generate server random. */
@@ -5148,9 +5435,20 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
if (ret != 0)
return ret;
- ssl->buffers.outputBuffer.length += sendSz;
- if ((ret = HashOutput(ssl, output, sendSz, 0)) != 0)
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13HashHandshake(ssl,
+ output + Dtls13GetRlHeaderLength(0) ,
+ sendSz - Dtls13GetRlHeaderLength(0));
+ }
+ else
+#endif /* WOLFSSL_DTLS13 */
+ {
+ ret = HashOutput(ssl, output, sendSz, 0);
+ }
+
+ if (ret != 0)
return ret;
#ifdef WOLFSSL_CALLBACKS
@@ -5165,6 +5463,19 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
if (extMsgType == server_hello)
ssl->options.serverState = SERVER_HELLO_COMPLETE;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13HandshakeSend(ssl, output, sendSz, sendSz,
+ server_hello, 0);
+
+ WOLFSSL_LEAVE("SendTls13ServerHello", ret);
+ WOLFSSL_END(WC_FUNC_SERVER_HELLO_SEND);
+ return ret;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
+ ssl->buffers.outputBuffer.length += sendSz;
+
if (!ssl->options.groupMessages || extMsgType != server_hello)
ret = SendBuffered(ssl);
@@ -5188,7 +5499,7 @@ static int SendTls13EncryptedExtensions(WOLFSSL* ssl)
int ret;
byte* output;
word16 length = 0;
- word32 idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+ word32 idx;
int sendSz;
WOLFSSL_START(WC_FUNC_ENCRYPTED_EXTENSIONS_SEND);
@@ -5196,6 +5507,16 @@ static int SendTls13EncryptedExtensions(WOLFSSL* ssl)
ssl->keys.encryptionOn = 1;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ idx = Dtls13GetHeadersLength(encrypted_extensions);
+ }
+ else
+#endif /* WOLFSSL_DTLS13 */
+ {
+ idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+ }
+
#if defined(HAVE_SUPPORTED_CURVES) && !defined(WOLFSSL_NO_SERVER_GROUPS_EXT)
if ((ret = TLSX_SupportedCurve_CheckPriority(ssl)) != 0)
return ret;
@@ -5223,6 +5544,24 @@ static int SendTls13EncryptedExtensions(WOLFSSL* ssl)
return ret;
#endif
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ w64wrapper epochHandshake = w64From32(0, DTLS13_EPOCH_HANDSHAKE);
+ ssl->dtls13Epoch = epochHandshake;
+
+ ret = Dtls13NewEpoch(
+ ssl, epochHandshake, ENCRYPT_AND_DECRYPT_SIDE);
+ if (ret != 0)
+ return ret;
+
+ ret = Dtls13SetEpochKeys(
+ ssl, epochHandshake, ENCRYPT_AND_DECRYPT_SIDE);
+ if (ret != 0)
+ return ret;
+
+ }
+#endif /* WOLFSSL_DTLS13 */
+
ret = TLSX_GetResponseSize(ssl, encrypted_extensions, &length);
if (ret != 0)
return ret;
@@ -5257,6 +5596,21 @@ static int SendTls13EncryptedExtensions(WOLFSSL* ssl)
}
#endif
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13HandshakeSend(ssl, output, sendSz, idx,
+ encrypted_extensions, 1);
+
+ if (ret == 0)
+ ssl->options.serverState = SERVER_ENCRYPTED_EXTENSIONS_COMPLETE;
+
+ WOLFSSL_LEAVE("SendTls13EncryptedExtensions", ret);
+ WOLFSSL_END(WC_FUNC_ENCRYPTED_EXTENSIONS_SEND);
+
+ return ret;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
/* This handshake message is always encrypted. */
sendSz = BuildTls13Message(ssl, output, sendSz, output + RECORD_HEADER_SZ,
idx - RECORD_HEADER_SZ, handshake, 1, 0, 0);
@@ -5270,6 +5624,7 @@ static int SendTls13EncryptedExtensions(WOLFSSL* ssl)
if (!ssl->options.groupMessages)
ret = SendBuffered(ssl);
+
WOLFSSL_LEAVE("SendTls13EncryptedExtensions", ret);
WOLFSSL_END(WC_FUNC_ENCRYPTED_EXTENSIONS_SEND);
@@ -5310,6 +5665,11 @@ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx,
ext->resp = 0;
i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ i = Dtls13GetRlHeaderLength(1) + DTLS_HANDSHAKE_HEADER_SZ;
+#endif /* WOLFSSL_DTLS13 */
+
reqSz = (word16)(OPAQUE8_LEN + reqCtxLen);
ret = TLSX_GetRequestSize(ssl, certificate_request, &reqSz);
if (ret != 0)
@@ -5344,6 +5704,19 @@ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx,
return ret;
i += reqSz;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret =
+ Dtls13HandshakeSend(ssl, output, sendSz, i, certificate_request, 1);
+
+ WOLFSSL_LEAVE("SendTls13CertificateRequest", ret);
+ WOLFSSL_END(WC_FUNC_CERTIFICATE_REQUEST_SEND);
+
+ return ret;
+
+ }
+#endif /* WOLFSSL_DTLS13 */
+
/* Always encrypted. */
sendSz = BuildTls13Message(ssl, output, sendSz, output + RECORD_HEADER_SZ,
i - RECORD_HEADER_SZ, handshake, 1, 0, 0);
@@ -5959,6 +6332,13 @@ static int SendTls13Certificate(WOLFSSL* ssl)
word32 i = RECORD_HEADER_SZ;
int sendSz = RECORD_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ i = Dtls13GetRlHeaderLength(1);
+ sendSz = (int)i;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
ssl->options.buildingMsg = 1;
if (ssl->fragOffset == 0) {
@@ -5966,11 +6346,25 @@ static int SendTls13Certificate(WOLFSSL* ssl)
maxFragment - HANDSHAKE_HEADER_SZ) {
fragSz = headerSz + certSz + extSz + certChainSz;
}
- else
+#ifdef WOLFSSL_DTLS13
+ else if (ssl->options.dtls){
+ /* short-circuit the fragmentation logic here. DTLS
+ fragmentation will be done in dtls13HandshakeSend() */
+ fragSz = headerSz + certSz + extSz + certChainSz;
+ }
+#endif /* WOLFSSL_DTLS13 */
+ else {
fragSz = maxFragment - HANDSHAKE_HEADER_SZ;
+ }
sendSz += fragSz + HANDSHAKE_HEADER_SZ;
i += HANDSHAKE_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ sendSz += DTLS_HANDSHAKE_EXTRA;
+ i += DTLS_HANDSHAKE_EXTRA;
+ }
+#endif /* WOLFSSL_DTLS13 */
}
else {
fragSz = min(length, maxFragment);
@@ -6059,25 +6453,35 @@ static int SendTls13Certificate(WOLFSSL* ssl)
return BUFFER_E;
}
- /* This message is always encrypted. */
- sendSz = BuildTls13Message(ssl, output, sendSz,
- output + RECORD_HEADER_SZ,
- i - RECORD_HEADER_SZ, handshake, 1, 0, 0);
- if (sendSz < 0)
- return sendSz;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ /* DTLS1.3 uses a separate variable and logic for fragments */
+ ssl->fragOffset = 0;
+ ret = Dtls13HandshakeSend(ssl, output, sendSz, i, certificate, 1);
+ }
+ else
+#endif /* WOLFSSL_DTLS13 */
+ {
+ /* This message is always encrypted. */
+ sendSz = BuildTls13Message(ssl, output, sendSz,
+ output + RECORD_HEADER_SZ, i - RECORD_HEADER_SZ, handshake, 1,
+ 0, 0);
+ if (sendSz < 0)
+ return sendSz;
- #ifdef WOLFSSL_CALLBACKS
+#ifdef WOLFSSL_CALLBACKS
if (ssl->hsInfoOn)
AddPacketName(ssl, "Certificate");
if (ssl->toInfoOn) {
AddPacketInfo(ssl, "Certificate", handshake, output,
- sendSz, WRITE_PROTO, ssl->heap);
+ sendSz, WRITE_PROTO, ssl->heap);
}
- #endif
+#endif
- ssl->buffers.outputBuffer.length += sendSz;
- if (!ssl->options.groupMessages)
- ret = SendBuffered(ssl);
+ ssl->buffers.outputBuffer.length += sendSz;
+ if (!ssl->options.groupMessages)
+ ret = SendBuffered(ssl);
+ }
}
if (ret != WANT_WRITE) {
@@ -6151,9 +6555,22 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
Scv13Args args[1];
#endif
+#ifdef WOLFSSL_DTLS13
+ int recordLayerHdrExtra;
+#endif /* WOLFSSL_DTLS13 */
+
WOLFSSL_START(WC_FUNC_CERTIFICATE_VERIFY_SEND);
WOLFSSL_ENTER("SendTls13CertificateVerify");
+#ifdef WOLFSSL_DTLS13
+ /* can be negative */
+ if (ssl->options.dtls)
+ recordLayerHdrExtra = Dtls13GetRlHeaderLength(1) - RECORD_HEADER_SZ;
+ else
+ recordLayerHdrExtra = 0;
+
+#endif /* WOLFSSL_DTLS13 */
+
#ifdef WOLFSSL_ASYNC_CRYPT
if (ssl->async == NULL) {
ssl->async = (struct WOLFSSL_ASYNC*)
@@ -6219,6 +6636,14 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
args->verify =
&args->output[RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ];
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ rem -= recordLayerHdrExtra + DTLS_HANDSHAKE_EXTRA;
+ args->idx += recordLayerHdrExtra + DTLS_HANDSHAKE_EXTRA;
+ args->verify += recordLayerHdrExtra + DTLS_HANDSHAKE_EXTRA;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
if (ssl->buffers.key == NULL) {
#ifdef HAVE_PK_CALLBACKS
if (wolfSSL_CTX_IsPrivatePkSet(ssl->ctx))
@@ -6477,7 +6902,11 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
args->sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ +
args->length + HASH_SIG_SIZE + VERIFY_HEADER;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ args->sendSz += recordLayerHdrExtra + DTLS_HANDSHAKE_EXTRA;
+#endif /* WOLFSSL_DTLS13 */
/* Advance state and proceed */
ssl->options.asyncState = TLS_ASYNC_END;
} /* case TLS_ASYNC_FINALIZE */
@@ -6485,6 +6914,18 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
case TLS_ASYNC_END:
{
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13HandshakeSend(ssl, args->output,
+ MAX_CERT_VERIFY_SZ + MAX_MSG_EXTRA + MAX_MSG_EXTRA,
+ args->sendSz, certificate_verify, 1);
+ if (ret != 0)
+ goto exit_scv;
+
+ break;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
/* This message is always encrypted. */
ret = BuildTls13Message(ssl, args->output,
MAX_CERT_VERIFY_SZ + MAX_MSG_EXTRA,
@@ -6558,12 +6999,21 @@ exit_scv:
static int DoTls13Certificate(WOLFSSL* ssl, byte* input, word32* inOutIdx,
word32 totalSz)
{
- int ret;
+ int ret = 0;
WOLFSSL_START(WC_FUNC_CERTIFICATE_DO);
WOLFSSL_ENTER("DoTls13Certificate");
- ret = ProcessPeerCerts(ssl, input, inOutIdx, totalSz);
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && ssl->options.handShakeDone) {
+ /* certificate needs some special care after the handshake */
+ ret = Dtls13RtxProcessingCertificate(
+ ssl, input + *inOutIdx, totalSz);
+ }
+#endif /* WOLFSSL_DTLS13 */
+
+ if (ret == 0)
+ ret = ProcessPeerCerts(ssl, input, inOutIdx, totalSz);
if (ret == 0) {
#if !defined(NO_WOLFSSL_CLIENT)
if (ssl->options.side == WOLFSSL_CLIENT_END)
@@ -7083,7 +7533,7 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
#endif
/* check against totalSz */
- if (*inOutIdx + size + ssl->keys.padSz > totalSz)
+ if (*inOutIdx + size > totalSz)
return BUFFER_E;
if (ssl->options.handShakeDone) {
@@ -7174,6 +7624,14 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
#endif
+#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_EARLY_DATA)
+ if (ssl->options.dtls && ssl->earlyData > early_data_ext) {
+ /* DTLSv1.3 has no EndOfearlydata messages. We stop processing EarlyData
+ as soon we receive the client's finished message */
+ ssl->earlyData = done_early_data;
+ }
+#endif /* WOLFSSL_DTLS13 && WOLFSSL_EARLY_DATA */
+
WOLFSSL_LEAVE("DoTls13Finished", 0);
WOLFSSL_END(WC_FUNC_FINISHED_DO);
@@ -7197,9 +7655,22 @@ static int SendTls13Finished(WOLFSSL* ssl)
int outputSz;
byte* secret;
+#ifdef WOLFSSL_DTLS13
+ int dtlsRet = 0, isDtls = 0;
+#endif /* WOLFSSL_DTLS13 */
+
WOLFSSL_START(WC_FUNC_FINISHED_SEND);
WOLFSSL_ENTER("SendTls13Finished");
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ headerSz = DTLS_HANDSHAKE_HEADER_SZ;
+ /* using isDtls instead of ssl->options.dtls will abide clang static
+ analyzer on unsing an uninitialized value */
+ isDtls = 1;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
outputSz = WC_MAX_DIGEST_SIZE + DTLS_HANDSHAKE_HEADER_SZ + MAX_MSG_EXTRA;
/* Check buffers are big enough and grow if needed. */
if ((ret = CheckAvailableSize(ssl, outputSz)) != 0)
@@ -7210,6 +7681,11 @@ static int SendTls13Finished(WOLFSSL* ssl)
ssl->buffers.outputBuffer.length;
input = output + RECORD_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (isDtls)
+ input = output + Dtls13GetRlHeaderLength(1);
+#endif /* WOLFSSL_DTLS13 */
+
AddTls13HandShakeHeader(input, finishedSz, 0, finishedSz, finished, ssl);
/* make finished hashes */
@@ -7252,6 +7728,17 @@ static int SendTls13Finished(WOLFSSL* ssl)
ssl->serverFinished_len = finishedSz;
}
#endif /* WOLFSSL_HAVE_TLS_UNIQUE */
+
+#ifdef WOLFSSL_DTLS13
+ if (isDtls) {
+ dtlsRet = Dtls13HandshakeSend(ssl, output, outputSz,
+ Dtls13GetRlHeaderLength(1) + headerSz + finishedSz, finished, 1);
+ if (dtlsRet != 0 && dtlsRet != WANT_WRITE)
+ return ret;
+
+ } else
+#endif /* WOLFSSL_DTLS13 */
+ {
/* This message is always encrypted. */
sendSz = BuildTls13Message(ssl, output, outputSz, input,
headerSz + finishedSz, handshake, 1, 0, 0);
@@ -7267,20 +7754,32 @@ static int SendTls13Finished(WOLFSSL* ssl)
#endif
ssl->buffers.outputBuffer.length += sendSz;
+ }
if (ssl->options.side == WOLFSSL_SERVER_END) {
+#ifdef WOLFSSL_EARLY_DATA
+ byte storeTrafficDecKeys = ssl->earlyData == no_early_data;
+#endif
/* Can send application data now. */
if ((ret = DeriveMasterSecret(ssl)) != 0)
return ret;
/* Last use of preMasterSecret - zeroize as soon as possible. */
ForceZero(ssl->arrays->preMasterSecret, ssl->arrays->preMasterSz);
#ifdef WOLFSSL_EARLY_DATA
+
+#ifdef WOLFSSL_DTLS13
+ /* DTLS13 dynamically change keys and it needs all
+ the keys in ssl->keys to save the keying material */
+ if (isDtls)
+ storeTrafficDecKeys = 1;
+#endif /* WOLFSSL_DTLS13 */
+
if ((ret = DeriveTls13Keys(ssl, traffic_key, ENCRYPT_SIDE_ONLY, 1))
!= 0) {
return ret;
}
if ((ret = DeriveTls13Keys(ssl, traffic_key, DECRYPT_SIDE_ONLY,
- ssl->earlyData == no_early_data)) != 0) {
+ storeTrafficDecKeys)) != 0) {
return ret;
}
#else
@@ -7291,6 +7790,26 @@ static int SendTls13Finished(WOLFSSL* ssl)
#endif
if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0)
return ret;
+
+#ifdef WOLFSSL_DTLS13
+ if (isDtls) {
+ w64wrapper epochTraffic0;
+ epochTraffic0 = w64From32(0, DTLS13_EPOCH_TRAFFIC0);
+ ssl->dtls13Epoch = epochTraffic0;
+ ssl->dtls13PeerEpoch = epochTraffic0;
+
+ ret = Dtls13NewEpoch(
+ ssl, epochTraffic0, ENCRYPT_AND_DECRYPT_SIDE);
+ if (ret != 0)
+ return ret;
+
+ ret = Dtls13SetEpochKeys(
+ ssl, epochTraffic0, ENCRYPT_AND_DECRYPT_SIDE);
+ if (ret != 0)
+ return ret;
+
+ }
+#endif /* WOLFSSL_DTLS13 */
}
if (ssl->options.side == WOLFSSL_CLIENT_END &&
@@ -7312,6 +7831,26 @@ static int SendTls13Finished(WOLFSSL* ssl)
if (ret != 0)
return ret;
#endif
+
+#ifdef WOLFSSL_DTLS13
+ if (isDtls) {
+ w64wrapper epochTraffic0;
+ epochTraffic0 = w64From32(0, DTLS13_EPOCH_TRAFFIC0);
+ ssl->dtls13Epoch = epochTraffic0;
+ ssl->dtls13PeerEpoch = epochTraffic0;
+
+ ret = Dtls13NewEpoch(
+ ssl, epochTraffic0, ENCRYPT_AND_DECRYPT_SIDE);
+ if (ret != 0)
+ return ret;
+
+ ret = Dtls13SetEpochKeys(
+ ssl, epochTraffic0, ENCRYPT_AND_DECRYPT_SIDE);
+ if (ret != 0)
+ return ret;
+
+ }
+#endif /* WOLFSSL_DTLS13 */
}
#ifndef NO_WOLFSSL_CLIENT
@@ -7327,6 +7866,15 @@ static int SendTls13Finished(WOLFSSL* ssl)
}
#endif
+#ifdef WOLFSSL_DTLS13
+ if (isDtls) {
+ WOLFSSL_LEAVE("SendTls13Finished", ret);
+ WOLFSSL_END(WC_FUNC_FINISHED_SEND);
+
+ return dtlsRet;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
if ((ret = SendBuffered(ssl)) != 0)
return ret;
@@ -7356,6 +7904,11 @@ static int SendTls13KeyUpdate(WOLFSSL* ssl)
WOLFSSL_START(WC_FUNC_KEY_UPDATE_SEND);
WOLFSSL_ENTER("SendTls13KeyUpdate");
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ i = Dtls13GetRlHeaderLength(1) + DTLS_HANDSHAKE_HEADER_SZ;
+#endif /* WOLFSSL_DTLS13 */
+
outputSz = OPAQUE8_LEN + MAX_MSG_EXTRA;
/* Check buffers are big enough and grow if needed. */
if ((ret = CheckAvailableSize(ssl, outputSz)) != 0)
@@ -7366,6 +7919,11 @@ static int SendTls13KeyUpdate(WOLFSSL* ssl)
ssl->buffers.outputBuffer.length;
input = output + RECORD_HEADER_SZ;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ input = output + Dtls13GetRlHeaderLength(1);
+#endif /* WOLFSSL_DTLS13 */
+
AddTls13Headers(output, OPAQUE8_LEN, key_update, ssl);
/* If:
@@ -7378,6 +7936,15 @@ static int SendTls13KeyUpdate(WOLFSSL* ssl)
/* Sent response, no longer need to respond. */
ssl->keys.keyUpdateRespond = 0;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13HandshakeSend(ssl, output, outputSz,
+ OPAQUE8_LEN + Dtls13GetRlHeaderLength(1) + DTLS_HANDSHAKE_HEADER_SZ,
+ key_update, 0);
+ }
+ else {
+#endif /* WOLFSSL_DTLS13 */
+
/* This message is always encrypted. */
sendSz = BuildTls13Message(ssl, output, outputSz, input,
headerSz + OPAQUE8_LEN, handshake, 0, 0, 0);
@@ -7395,15 +7962,26 @@ static int SendTls13KeyUpdate(WOLFSSL* ssl)
ssl->buffers.outputBuffer.length += sendSz;
ret = SendBuffered(ssl);
+
+
if (ret != 0 && ret != WANT_WRITE)
return ret;
+#ifdef WOLFSSL_DTLS13
+ }
+#endif /* WOLFSSL_DTLS13 */
+
+ /* In DTLS we must wait for the ack before setting up the new keys */
+ if (!ssl->options.dtls) {
+
+ /* Future traffic uses new encryption keys. */
+ if ((ret = DeriveTls13Keys(
+ ssl, update_traffic_key, ENCRYPT_SIDE_ONLY, 1))
+ != 0)
+ return ret;
+ if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0)
+ return ret;
+ }
- /* Future traffic uses new encryption keys. */
- if ((ret = DeriveTls13Keys(ssl, update_traffic_key, ENCRYPT_SIDE_ONLY, 1))
- != 0)
- return ret;
- if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0)
- return ret;
WOLFSSL_LEAVE("SendTls13KeyUpdate", ret);
WOLFSSL_END(WC_FUNC_KEY_UPDATE_SEND);
@@ -7461,8 +8039,37 @@ static int DoTls13KeyUpdate(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0)
return ret;
- if (ssl->keys.keyUpdateRespond)
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ w64Increment(&ssl->dtls13PeerEpoch);
+
+ ret = Dtls13NewEpoch(ssl, ssl->dtls13PeerEpoch, DECRYPT_SIDE_ONLY);
+ if (ret != 0)
+ return ret;
+
+ ret = Dtls13SetEpochKeys(ssl, ssl->dtls13PeerEpoch, DECRYPT_SIDE_ONLY);
+ if (ret != 0)
+ return ret;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
+ if (ssl->keys.keyUpdateRespond) {
+
+#ifdef WOLFSSL_DTLS13
+ /* we already sent a keyUpdate (either in response to a previous
+ KeyUpdate or initiated by the application) and we are waiting for the
+ ack. We can't send a new KeyUpdate right away but to honor the RFC we
+ should send another KeyUpdate after the one in-flight is acked. We
+ don't do that as it looks redundant, it will make the code more
+ complex and I don't see a good use case for that. */
+ if (ssl->options.dtls && ssl->dtls13WaitKeyUpdateAck) {
+ ssl->keys.keyUpdateRespond = 0;
+ return 0;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
return SendTls13KeyUpdate(ssl);
+ }
WOLFSSL_LEAVE("DoTls13KeyUpdate", ret);
WOLFSSL_END(WC_FUNC_KEY_UPDATE_DO);
@@ -7813,6 +8420,11 @@ static int SendTls13NewSessionTicket(WOLFSSL* ssl)
WOLFSSL_START(WC_FUNC_NEW_SESSION_TICKET_SEND);
WOLFSSL_ENTER("SendTls13NewSessionTicket");
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ idx = Dtls13GetRlHeaderLength(1) + DTLS_HANDSHAKE_HEADER_SZ;
+#endif /* WOLFSSL_DTLS13 */
+
#ifdef WOLFSSL_TLS13_TICKET_BEFORE_FINISHED
if (!ssl->msgsReceived.got_finished) {
if ((ret = ExpectedResumptionSecret(ssl)) != 0)
@@ -7898,6 +8510,11 @@ static int SendTls13NewSessionTicket(WOLFSSL* ssl)
AddSession(ssl);
#endif
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ return Dtls13HandshakeSend(ssl, output, sendSz, idx, session_ticket, 0);
+#endif /* WOLFSSL_DTLS13 */
+
/* This message is always encrypted. */
sendSz = BuildTls13Message(ssl, output, sendSz, output + RECORD_HEADER_SZ,
idx - RECORD_HEADER_SZ, handshake, 0, 0, 0);
@@ -8146,7 +8763,7 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type)
* Handshake Authentication). */
if (ssl->options.serverState !=
SERVER_ENCRYPTED_EXTENSIONS_COMPLETE &&
- (ssl->options.serverState != SERVER_FINISHED_COMPLETE ||
+ (ssl->options.serverState < SERVER_FINISHED_COMPLETE ||
ssl->options.clientState != CLIENT_FINISHED_COMPLETE)) {
WOLFSSL_MSG("CertificateRequest received out of order");
return OUT_OF_ORDER_E;
@@ -8257,7 +8874,7 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type)
#ifndef NO_WOLFSSL_SERVER
/* Check state on server. */
if (ssl->options.side == WOLFSSL_SERVER_END) {
- if (ssl->options.serverState != SERVER_FINISHED_COMPLETE) {
+ if (ssl->options.serverState < SERVER_FINISHED_COMPLETE) {
WOLFSSL_MSG("Finished received out of order - serverState");
return OUT_OF_ORDER_E;
}
@@ -8266,7 +8883,9 @@ static int SanityCheckTls13MsgReceived(WOLFSSL* ssl, byte type)
return OUT_OF_ORDER_E;
}
#ifdef WOLFSSL_EARLY_DATA
- if (ssl->earlyData == process_early_data) {
+ if (ssl->earlyData == process_early_data &&
+ /* early data may be lost when using DTLS */
+ !ssl->options.dtls) {
return OUT_OF_ORDER_E;
}
#endif
@@ -8478,7 +9097,7 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
break;
case key_update:
- WOLFSSL_MSG("processing finished");
+ WOLFSSL_MSG("processing key update");
ret = DoTls13KeyUpdate(ssl, input, inOutIdx, size);
break;
@@ -8531,6 +9150,26 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
#endif
if ((ret = SetKeysSide(ssl, ENCRYPT_AND_DECRYPT_SIDE)) != 0)
return ret;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ w64wrapper epochHandshake;
+ epochHandshake = w64From32(0, DTLS13_EPOCH_HANDSHAKE);
+ ssl->dtls13Epoch = epochHandshake;
+ ssl->dtls13PeerEpoch = epochHandshake;
+
+ ret = Dtls13NewEpoch(
+ ssl, epochHandshake, ENCRYPT_AND_DECRYPT_SIDE);
+ if (ret != 0)
+ return ret;
+
+ ret = Dtls13SetEpochKeys(
+ ssl, epochHandshake, ENCRYPT_AND_DECRYPT_SIDE);
+ if (ret != 0)
+ return ret;
+
+ }
+#endif /* WOLFSSL_DTLS13 */
}
if (type == finished) {
@@ -8561,6 +9200,15 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
ssl->options.handShakeState = CLIENT_HELLO_COMPLETE;
ssl->options.processReply = 0; /* doProcessInit */
+ /*
+ DTLSv1.3 note: We can't reset serverState to
+ SERVER_FINISHED_COMPLETE with the goal that this connect
+ blocks until the cert/cert_verify/finished flight gets ACKed
+ by the server. The problem is that we will invoke
+ ProcessReplyEx() in that case, but we came here from
+ ProcessReplyEx() and it is not re-entrant safe (the input
+ buffer would still have the certificate_request message). */
+
if (wolfSSL_connect_TLSv13(ssl) != WOLFSSL_SUCCESS) {
ret = ssl->error;
if (ret != WC_PENDING_E)
@@ -8717,6 +9365,7 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
*/
int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
{
+ int advanceState;
int ret = 0;
WOLFSSL_ENTER("wolfSSL_connect_TLSv13()");
@@ -8744,6 +9393,20 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
}
#endif /* WOLFSSL_WOLFSENTRY_HOOKS */
+ /* fragOffset is non-zero when sending fragments. On the last
+ * fragment, fragOffset is zero again, and the state can be
+ * advanced. Also, only advance from states in which we send data */
+ advanceState = (ssl->options.connectState == CONNECT_BEGIN ||
+ ssl->options.connectState == HELLO_AGAIN ||
+ (ssl->options.connectState >= FIRST_REPLY_DONE &&
+ ssl->options.connectState <= FIRST_REPLY_FOURTH));
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ advanceState = advanceState && !ssl->dtls13SendingFragments
+ && !ssl->dtls13SendingAckOrRtx;
+#endif /* WOLFSSL_DTLS13 */
+
if (ssl->buffers.outputBuffer.length > 0
#ifdef WOLFSSL_ASYNC_CRYPT
/* do not send buffered or advance state if last error was an
@@ -8751,17 +9414,24 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
&& ssl->error != WC_PENDING_E
#endif
) {
- if ((ret = SendBuffered(ssl)) == 0) {
- /* fragOffset is non-zero when sending fragments. On the last
- * fragment, fragOffset is zero again, and the state can be
- * advanced. */
+ if ((ssl->error = SendBuffered(ssl)) == 0) {
if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) {
- /* Only increment from states in which we send data */
- if (ssl->options.connectState == CONNECT_BEGIN ||
- ssl->options.connectState == HELLO_AGAIN ||
- (ssl->options.connectState >= FIRST_REPLY_DONE &&
- ssl->options.connectState <= FIRST_REPLY_FOURTH)) {
- ssl->options.connectState++;
+ if (advanceState) {
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version) &&
+ ssl->options.connectState == FIRST_REPLY_FOURTH) {
+ /* WAIT_FINISHED_ACK is a state added afterwards, but it
+ can't follow FIRST_REPLY_FOURTH in the enum order. Indeed
+ the value of the enum ConnectState is stored in
+ serialized session. This would make importing serialized
+ session from other wolfSSL version incompatible */
+ ssl->options.connectState = WAIT_FINISHED_ACK;
+ }
+ else
+#endif /* WOLFSSL_DTLS13 */
+ {
+ ssl->options.connectState++;
+ }
WOLFSSL_MSG("connect state: "
"Advanced from last buffered fragment send");
}
@@ -8773,6 +9443,12 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
#ifdef WOLFSSL_ASYNC_IO
FreeAsyncCtx(ssl, 0);
#endif
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ ssl->dtls13SendingAckOrRtx =0;
+#endif /* WOLFSSL_DTLS13 */
+
}
else {
ssl->error = ret;
@@ -8788,6 +9464,18 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
return WOLFSSL_FATAL_ERROR;
}
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && ssl->dtls13SendingFragments) {
+ if ((ssl->error = Dtls13FragmentsContinue(ssl)) != 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+
+ /* we sent all the fragments. Advance state. */
+ ssl->options.connectState++;
+ }
+#endif /* WOLFSSL_DTLS13 */
+
switch (ssl->options.connectState) {
case CONNECT_BEGIN:
@@ -8802,14 +9490,16 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
#ifdef WOLFSSL_EARLY_DATA
if (ssl->earlyData != no_early_data) {
#if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
- if ((ssl->error = SendChangeCipher(ssl)) != 0) {
- WOLFSSL_ERROR(ssl->error);
- return WOLFSSL_FATAL_ERROR;
+ if (!ssl->options.dtls) {
+ if ((ssl->error = SendChangeCipher(ssl)) != 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+ ssl->options.sentChangeCipher = 1;
}
- ssl->options.sentChangeCipher = 1;
#endif
- ssl->options.handShakeState = CLIENT_HELLO_COMPLETE;
- return WOLFSSL_SUCCESS;
+ ssl->options.handShakeState = CLIENT_HELLO_COMPLETE;
+ return WOLFSSL_SUCCESS;
}
#endif
FALL_THROUGH;
@@ -8819,9 +9509,18 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
while (ssl->options.serverState <
SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
if ((ssl->error = ProcessReply(ssl)) < 0) {
- WOLFSSL_ERROR(ssl->error);
- return WOLFSSL_FATAL_ERROR;
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
}
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
}
if (!ssl->options.tls1_3) {
@@ -8844,7 +9543,7 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
if (ssl->options.serverState ==
SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
#if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
- if (!ssl->options.sentChangeCipher) {
+ if (!ssl->options.dtls && !ssl->options.sentChangeCipher) {
if ((ssl->error = SendChangeCipher(ssl)) != 0) {
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
@@ -8867,9 +9566,18 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
/* Get the response/s from the server. */
while (ssl->options.serverState < SERVER_FINISHED_COMPLETE) {
if ((ssl->error = ProcessReply(ssl)) < 0) {
- WOLFSSL_ERROR(ssl->error);
- return WOLFSSL_FATAL_ERROR;
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
}
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
}
ssl->options.connectState = FIRST_REPLY_DONE;
@@ -8878,7 +9586,7 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
case FIRST_REPLY_DONE:
#ifdef WOLFSSL_EARLY_DATA
- if (ssl->earlyData != no_early_data) {
+ if (!ssl->options.dtls && ssl->earlyData != no_early_data) {
if ((ssl->error = SendTls13EndOfEarlyData(ssl)) != 0) {
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
@@ -8893,7 +9601,7 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
case FIRST_REPLY_FIRST:
#if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
- if (!ssl->options.sentChangeCipher) {
+ if (!ssl->options.sentChangeCipher && !ssl->options.dtls) {
if ((ssl->error = SendChangeCipher(ssl)) != 0) {
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
@@ -8962,6 +9670,26 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl)
}
WOLFSSL_MSG("sent: finished");
+#ifdef WOLFSSL_DTLS13
+ ssl->options.connectState = WAIT_FINISHED_ACK;
+ WOLFSSL_MSG("connect state: WAIT_FINISHED_ACK");
+ FALL_THROUGH;
+
+ case WAIT_FINISHED_ACK:
+ if (ssl->options.dtls) {
+ while (ssl->options.serverState != SERVER_FINISHED_ACKED) {
+ if ((ssl->error = ProcessReply(ssl)) < 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+
+ if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
ssl->options.connectState = FINISHED_DONE;
WOLFSSL_MSG("connect state: FINISHED_DONE");
FALL_THROUGH;
@@ -9234,6 +9962,17 @@ int wolfSSL_update_keys(WOLFSSL* ssl)
if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version))
return BAD_FUNC_ARG;
+#ifdef WOLFSSL_DTLS13
+ /* we are already waiting for the ack of a sent key update message. We can't
+ send another one before receiving its ack. Either wolfSSL_update_keys()
+ was invoked multiple times over a short period of time or we replied to a
+ KeyUpdate with update request. We'll just ignore sending this
+ KeyUpdate. */
+ /* TODO: add WOLFSSL_ERROR_ALREADY_IN_PROGRESS type of error here */
+ if (ssl->options.dtls && ssl->dtls13WaitKeyUpdateAck)
+ return WOLFSSL_SUCCESS;
+#endif /* WOLFSSL_DTLS13 */
+
ret = SendTls13KeyUpdate(ssl);
if (ret == WANT_WRITE)
ret = WOLFSSL_ERROR_WANT_WRITE;
@@ -9651,9 +10390,12 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
#if !defined(NO_CERTS) && (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK))
word16 havePSK = 0;
#endif
+ int advanceState;
int ret = 0;
+
WOLFSSL_ENTER("SSL_accept_TLSv13()");
+
#ifdef HAVE_ERRNO_H
errno = 0;
#endif
@@ -9734,23 +10476,33 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
&& ssl->error != WC_PENDING_E
#endif
) {
- if ((ret = SendBuffered(ssl)) == 0) {
- /* fragOffset is non-zero when sending fragments. On the last
- * fragment, fragOffset is zero again, and the state can be
- * advanced. */
+
+ /* fragOffset is non-zero when sending fragments. On the last
+ * fragment, fragOffset is zero again, and the state can be
+ * advanced. */
+ advanceState =
+ (ssl->options.acceptState == TLS13_ACCEPT_CLIENT_HELLO_DONE ||
+ ssl->options.acceptState ==
+ TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE ||
+ ssl->options.acceptState == TLS13_ACCEPT_SECOND_REPLY_DONE ||
+ ssl->options.acceptState == TLS13_SERVER_HELLO_SENT ||
+ ssl->options.acceptState == TLS13_ACCEPT_THIRD_REPLY_DONE ||
+ ssl->options.acceptState == TLS13_SERVER_EXTENSIONS_SENT ||
+ ssl->options.acceptState == TLS13_CERT_REQ_SENT ||
+ ssl->options.acceptState == TLS13_CERT_SENT ||
+ ssl->options.acceptState == TLS13_CERT_VERIFY_SENT ||
+ ssl->options.acceptState == TLS13_ACCEPT_FINISHED_SENT ||
+ ssl->options.acceptState == TLS13_ACCEPT_FINISHED_DONE);
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ advanceState = advanceState && !ssl->dtls13SendingFragments
+ && !ssl->dtls13SendingAckOrRtx;
+#endif /* WOLFSSL_DTLS13 */
+
+ if ((ssl->error = SendBuffered(ssl)) == 0) {
if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) {
- /* Only increment from states in which we send data */
- if (ssl->options.acceptState == TLS13_ACCEPT_CLIENT_HELLO_DONE ||
- ssl->options.acceptState == TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE ||
- ssl->options.acceptState == TLS13_ACCEPT_SECOND_REPLY_DONE ||
- ssl->options.acceptState == TLS13_SERVER_HELLO_SENT ||
- ssl->options.acceptState == TLS13_ACCEPT_THIRD_REPLY_DONE ||
- ssl->options.acceptState == TLS13_SERVER_EXTENSIONS_SENT ||
- ssl->options.acceptState == TLS13_CERT_REQ_SENT ||
- ssl->options.acceptState == TLS13_CERT_SENT ||
- ssl->options.acceptState == TLS13_CERT_VERIFY_SENT ||
- ssl->options.acceptState == TLS13_ACCEPT_FINISHED_SENT ||
- ssl->options.acceptState == TLS13_ACCEPT_FINISHED_DONE) {
+ if (advanceState) {
ssl->options.acceptState++;
WOLFSSL_MSG("accept state: "
"Advanced from last buffered fragment send");
@@ -9763,6 +10515,12 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
WOLFSSL_MSG("accept state: "
"Not advanced, more fragments to send");
}
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls)
+ ssl->dtls13SendingAckOrRtx = 0;
+#endif /* WOLFSSL_DTLS13 */
+
}
else {
ssl->error = ret;
@@ -9777,6 +10535,17 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
}
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && ssl->dtls13SendingFragments) {
+ if ((ssl->error = Dtls13FragmentsContinue(ssl)) != 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+
+ /* we sent all the fragments. Advance state. */
+ ssl->options.acceptState++;
+ }
+#endif /* WOLFSSL_DTLS13 */
switch (ssl->options.acceptState) {
@@ -9790,6 +10559,16 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
}
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
+
}
ssl->options.acceptState = TLS13_ACCEPT_CLIENT_HELLO_DONE;
@@ -9814,7 +10593,7 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
case TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE :
#ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
- if (ssl->options.serverState ==
+ if (!ssl->options.dtls && ssl->options.serverState ==
SERVER_HELLO_RETRY_REQUEST_COMPLETE) {
if ((ssl->error = SendChangeCipher(ssl)) != 0) {
WOLFSSL_ERROR(ssl->error);
@@ -9837,6 +10616,16 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
}
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
+
}
}
@@ -9855,7 +10644,8 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
case TLS13_SERVER_HELLO_SENT :
#if defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT)
- if (!ssl->options.sentChangeCipher) {
+ if (!ssl->options.dtls
+ && !ssl->options.sentChangeCipher && !ssl->options.dtls) {
if ((ssl->error = SendChangeCipher(ssl)) != 0) {
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
@@ -9970,11 +10760,21 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
FALL_THROUGH;
case TLS13_PRE_TICKET_SENT :
- while (ssl->options.clientState < CLIENT_FINISHED_COMPLETE)
+ while (ssl->options.clientState < CLIENT_FINISHED_COMPLETE) {
if ( (ssl->error = ProcessReply(ssl)) < 0) {
- WOLFSSL_ERROR(ssl->error);
- return WOLFSSL_FATAL_ERROR;
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ if ((ssl->error = Dtls13DoScheduledWork(ssl)) < 0) {
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
}
+#endif /* WOLFSSL_DTLS13 */
+ }
ssl->options.acceptState = TLS13_ACCEPT_FINISHED_DONE;
WOLFSSL_MSG("accept state ACCEPT_FINISHED_DONE");
@@ -10273,8 +11073,19 @@ int wolfSSL_read_early_data(WOLFSSL* ssl, void* data, int sz, int* outSz)
ret = ReceiveData(ssl, (byte*)data, sz, FALSE);
if (ret > 0)
*outSz = ret;
- if (ssl->error == ZERO_RETURN)
+ if (ssl->error == ZERO_RETURN) {
ssl->error = WOLFSSL_ERROR_NONE;
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls) {
+ ret = Dtls13DoScheduledWork(ssl);
+ if (ret < 0) {
+ ssl->error = ret;
+ WOLFSSL_ERROR(ssl->error);
+ return WOLFSSL_FATAL_ERROR;
+ }
+ }
+#endif /* WOLFSSL_DTLS13 */
+ }
}
else
ret = 0;
diff --git a/src/wolfio.c b/src/wolfio.c
index 6685cb8e6..34a0742fc 100644
--- a/src/wolfio.c
+++ b/src/wolfio.c
@@ -381,6 +381,7 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx)
int recvd;
int sd = dtlsCtx->rfd;
int dtls_timeout = wolfSSL_dtls_get_current_timeout(ssl);
+ byte doDtlsTimeout;
SOCKADDR_S peer;
XSOCKLENT peerSz = sizeof(peer);
@@ -388,16 +389,41 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx)
/* Don't use ssl->options.handShakeDone since it is true even if
* we are in the process of renegotiation */
- if (ssl->options.handShakeState == HANDSHAKE_DONE)
+ doDtlsTimeout = ssl->options.handShakeState != HANDSHAKE_DONE;
+
+#ifdef WOLFSSL_DTLS13
+ if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
+ doDtlsTimeout =
+ doDtlsTimeout || ssl->dtls13Rtx.rtxRecords != NULL ||
+ (ssl->dtls13FastTimeout && ssl->dtls13Rtx.seenRecords != NULL);
+ }
+#endif /* WOLFSSL_DTLS13 */
+
+ if (!doDtlsTimeout)
dtls_timeout = 0;
if (!wolfSSL_get_using_nonblock(ssl)) {
#ifdef USE_WINDOWS_API
DWORD timeout = dtls_timeout * 1000;
+ #ifdef WOLFSSL_DTLS13
+ if (wolfSSL_dtls13_use_quick_timeout(ssl) &&
+ IsAtLeastTLSv1_3(ssl->version))
+ timeout /= 4;
+ #endif /* WOLFSSL_DTLS13 */
#else
struct timeval timeout;
XMEMSET(&timeout, 0, sizeof(timeout));
- timeout.tv_sec = dtls_timeout;
+ #ifdef WOLFSSL_DTLS13
+ if (wolfSSL_dtls13_use_quick_timeout(ssl) &&
+ IsAtLeastTLSv1_3(ssl->version)) {
+ if (dtls_timeout >= 4)
+ timeout.tv_sec = dtls_timeout / 4;
+ else
+ timeout.tv_usec = dtls_timeout * 1000000 / 4;
+ }
+ else
+ #endif /* WOLFSSL_DTLS13 */
+ timeout.tv_sec = dtls_timeout;
#endif
if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
sizeof(timeout)) != 0) {
diff --git a/tests/include.am b/tests/include.am
index daf96c814..c4b6b7af4 100644
--- a/tests/include.am
+++ b/tests/include.am
@@ -10,6 +10,7 @@ tests_unit_test_SOURCES = \
tests/api.c \
tests/suites.c \
tests/hash.c \
+ tests/w64wrapper.c \
tests/srp.c \
examples/client/client.c \
examples/server/server.c
@@ -39,6 +40,9 @@ EXTRA_DIST += tests/unit.h \
tests/test-dtls-sha2.conf \
tests/test-dtls-srtp.conf \
tests/test-dtls-srtp-fails.conf \
+ tests/test-dtls13.conf \
+ tests/test-dtls13-downgrade.conf \
+ tests/test-dtls13-psk.conf \
tests/test-sctp.conf \
tests/test-sctp-sha2.conf \
tests/test-sig.conf \
diff --git a/tests/suites.c b/tests/suites.c
index 86bdcf20c..5f1a1fb62 100644
--- a/tests/suites.c
+++ b/tests/suites.c
@@ -1088,6 +1088,42 @@ int SuiteTest(int argc, char** argv)
strcpy(argv0[2], "");
#endif
+#ifdef WOLFSSL_DTLS13
+ args.argc = 2;
+ strcpy(argv0[1], "tests/test-dtls13.conf");
+ printf("starting DTLSv1.3 suite\n");
+ test_harness(&args);
+ if (args.return_code != 0) {
+ printf("error from script %d\n", args.return_code);
+ args.return_code = EXIT_FAILURE;
+ goto exit;
+ }
+
+#ifndef WOLFSSL_NO_TLS12
+ args.argc = 2;
+ strcpy(argv0[1], "tests/test-dtls13-downgrade.conf");
+ printf("starting DTLSv1.3 suite - downgrade\n");
+ test_harness(&args);
+ if (args.return_code != 0) {
+ printf("error from script %d\n", args.return_code);
+ args.return_code = EXIT_FAILURE;
+ goto exit;
+ }
+#endif /* WOLFSSL_NO_TLS12 */
+
+#ifndef NO_PSK
+ XSTRLCPY(argv0[1], "tests/test-dtls13-psk.conf", sizeof(argv0[1]));
+ printf("starting DTLS 1.3 psk suite tests\n");
+ test_harness(&args);
+ if (args.return_code != 0) {
+ printf("error from script %d\n", args.return_code);
+ args.return_code = EXIT_FAILURE;
+ goto exit;
+ }
+#endif /* NO_PSK */
+
+#endif /* WOLFSSL_DTLS13 */
+
#endif
#ifdef WOLFSSL_SCTP
/* add dtls-sctp extra suites */
diff --git a/tests/test-dtls13-downgrade.conf b/tests/test-dtls13-downgrade.conf
new file mode 100644
index 000000000..4bde3259f
--- /dev/null
+++ b/tests/test-dtls13-downgrade.conf
@@ -0,0 +1,11 @@
+# server DTLSv1.3 allow downgrading
+-vd
+-7 2
+-u
+-l TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+
+# client TLSv1.2 group message
+-v 3
+-u
+-l TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+-f
diff --git a/tests/test-dtls13-psk.conf b/tests/test-dtls13-psk.conf
new file mode 100644
index 000000000..bb4093f29
--- /dev/null
+++ b/tests/test-dtls13-psk.conf
@@ -0,0 +1,54 @@
+# server TLSv1.3 PSK
+# Use AES128-GCM and SHA256
+-v 4
+-u
+-s
+-l TLS13-AES128-GCM-SHA256
+-d
+
+# client TLSv1.3 PSK
+# Use AES128-GCM and SHA256
+-v 4
+-u
+-s
+-l TLS13-AES128-GCM-SHA256
+
+# server TLSv1.3 PSK plus
+-v 4
+-u
+-j
+-l TLS13-AES128-GCM-SHA256
+-d
+
+# client TLSv1.3 PSK
+-v 4
+-u
+-s
+-l TLS13-AES128-GCM-SHA256
+
+# server TLSv1.3 PSK
+-v 4
+-u
+-j
+-l TLS13-AES128-GCM-SHA256
+-d
+
+# client TLSv1.3 not-PSK
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+
+# server TLSv1.3 PSK
+# AES256-GCM and SHA384
+-v 4
+-u
+-s
+-l TLS13-AES256-GCM-SHA384
+-d
+
+# client TLSv1.3 PSK
+# AES256-GCM and SHA384
+-v 4
+-u
+-s
+-l TLS13-AES256-GCM-SHA384
diff --git a/tests/test-dtls13.conf b/tests/test-dtls13.conf
new file mode 100644
index 000000000..516a61796
--- /dev/null
+++ b/tests/test-dtls13.conf
@@ -0,0 +1,262 @@
+# server DTLSv1.3 defaults
+-u
+-v 4
+-l TLS_AES_128_GCM_SHA256
+
+# client DTLSv1.3 defaults
+-u
+-v 4
+-l TLS_AES_128_GCM_SHA256
+
+# server DTLSv1.3 defaults async I/O
+-u
+-v 4
+-l TLS_AES_128_GCM_SHA256
+-6
+
+# client DTLSv1.3 defaults async I/O
+-u
+-v 4
+-l TLS_AES_128_GCM_SHA256
+-6
+
+# server DTLSv1.3 TLS13-CHACHA20-POLY1305-SHA256
+-u
+-v 4
+-l TLS13-CHACHA20-POLY1305-SHA256
+
+# client DTLSv1.3 TLS13-CHACHA20-POLY1305-SHA256
+-u
+-v 4
+-l TLS13-CHACHA20-POLY1305-SHA256
+
+# server DTLSv1.3 TLS13-AES128-CCM-SHA256
+-v 4
+-u
+-l TLS13-AES128-CCM-SHA256
+
+# client DTLSv1.3 TLS13-AES128-CCM-SHA256
+-u
+-v 4
+-u
+-l TLS13-AES128-CCM-SHA256
+
+# server DTLSv1.3 resumption
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-r
+
+# client DTLSv1.3 resumption
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-r
+
+# server DTLSv1.3 resumption - SHA384
+-v 4
+-u
+-l TLS13-AES256-GCM-SHA384
+-r
+
+# client DTLSv1.3 resumption - SHA384
+-v 4
+-u
+-l TLS13-AES256-GCM-SHA384
+-r
+
+# server DTLSv1.3 PSK without (EC)DHE
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-r
+
+# client DTLSv1.3 PSK without (EC)DHE
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-r
+-K
+
+# server DTLSv1.3 accepting EarlyData
+-u
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-r
+-0
+
+# client DTLSv1.3 sending EarlyData
+-u
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-r
+-0
+
+# client DTLSv1.3 sending EarlyData
+-u
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-r
+-0
+
+# server DTLSv1.3 not accepting EarlyData
+-u
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-r
+
+# server DTLSv1.3 accepting EarlyData
+-u
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-r
+-0
+
+# client DTLSv1.3 not sending EarlyData
+-u
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-r
+
+# server DTLSv1.3
+-u
+-v 4
+-l TLS13-AES128-GCM-SHA256
+
+# client DTLSv1.3 HelloRetryRequest to negotiate Key Exchange algorithm
+-u
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-J
+
+# server DTLSv1.3
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-J
+
+# client DTLSv1.3 HelloRetryRequest with cookie
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-J
+
+# server DTLSv1.3
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+
+# client DTLSv1.3 DH key exchange
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-y
+
+# server DTLSv1.3
+-v 4
+-l TLS13-AES128-GCM-SHA256
+
+# client DTLSv1.3 ECC key exchange
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-Y
+
+# server DTLSv1.3 multiple cipher suites
+-v 4
+-l TLS13-AES128-GCM-SHA256:TLS13-AES256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES128-CCM-SHA256:TLS13-AES128-CCM-8-SHA256
+
+# client DTLSv1.3
+-v 4
+-l TLS13-AES128-GCM-SHA256:TLS13-AES256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES128-CCM-SHA256:TLS13-AES128-CCM-8-SHA256
+
+# server DTLSv1.3 KeyUpdate
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-U
+
+# client DTLSv1.3 KeyUpdate
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-I
+
+# server DTLSv1.3 KeyUpdate
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-U
+
+# client DTLSv1.3 KeyUpdate
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-I
+
+# server DTLSv1.3 No session ticket
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-u
+-T
+
+# client DTLSv1.3 No session ticket
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+
+# server DTLSv1.3 No session ticket
+-v 4
+-l TLS13-AES128-GCM-SHA256
+-u
+
+# client DTLSv1.3 wait ticket
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+--waitTicket
+
+# server DTLSv1.3 Post-Handshake Authentication
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-Q
+
+# client DTLSv1.3 Post-Handshake Authentication
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-Q
+
+# server DTLSv1.3 group messages
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-f
+
+# client DTLSv1.3 group message
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-f
+
+# server DTLSv1.3 group messages
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-f
+
+# client DTLSv1.3
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+
+# server DTLSv1.3
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+
+# client DTLSv1.3 group message
+-v 4
+-u
+-l TLS13-AES128-GCM-SHA256
+-f
diff --git a/tests/unit.c b/tests/unit.c
index d1a502b06..59bcd8b1e 100644
--- a/tests/unit.c
+++ b/tests/unit.c
@@ -163,6 +163,14 @@ int unit_test(int argc, char** argv)
printf("hash test failed with %d\n", ret);
goto exit;
}
+
+#ifdef WOLFSSL_W64_WRAPPER
+ if ((ret = w64wrapper_test()) != 0) {
+ printf("w64wrapper test failed with %d\n", ret);
+ goto exit;
+ }
+#endif /* WOLFSSL_W64_WRAPPER */
+
}
#ifndef NO_WOLFSSL_CIPHER_SUITE_TEST
diff --git a/tests/unit.h b/tests/unit.h
index 8e0e59554..afb3225ae 100644
--- a/tests/unit.h
+++ b/tests/unit.h
@@ -107,6 +107,7 @@ void ApiTest(void);
int SuiteTest(int argc, char** argv);
int HashTest(void);
void SrpTest(void);
+int w64wrapper_test(void);
#endif /* CyaSSL_UNIT_H */
diff --git a/tests/w64wrapper.c b/tests/w64wrapper.c
new file mode 100644
index 000000000..6a06a4446
--- /dev/null
+++ b/tests/w64wrapper.c
@@ -0,0 +1,218 @@
+/* w64wrapper.c w64wrapper unit tests
+ *
+ * Copyright (C) 2006-2021 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 2 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
+ */
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#include
+#include
+
+#ifdef WOLFSSL_W64_WRAPPER
+
+#ifndef NO_INLINE
+#define WOLFSSL_MISC_INCLUDED
+#include
+#else
+#include
+#endif
+
+int w64wrapper_test(void)
+{
+ w64wrapper a, b, c;
+ byte wrap, raw[8];
+
+ a = w64From32(0x01020304, 0x05060708);
+#if defined(WORD64_AVAILABLE) && !defined(WOLFSSL_W64_WRAPPER_TEST)
+ if (a.n != 0x0102030405060708)
+ return -1;
+#else
+ if (a.n[0] != 0x01020304 || a.n[1] != 0x05060708)
+ return -1;
+#endif /* WORD64_AVAILABLE && WOLFSSL_W64_WRAPPER_TEST */
+
+ if (w64GetLow32(a) != 0x05060708)
+ return -2;
+ if (w64GetHigh32(a) != 0x01020304)
+ return -3;
+ w64SetLow32(&a, 0xabcdefff);
+ if (w64GetLow32(a) != 0xabcdefff || w64GetHigh32(a) != 0x01020304)
+ return -4;
+
+ a = w64From32(0,0);
+ w64Increment(&a);
+ if (w64GetLow32(a) != 1 || w64GetHigh32(a) != 0)
+ return -5;
+
+ a = w64From32(0, 0xffffffff);
+ w64Increment(&a);
+ if (w64GetLow32(a) != 0 || w64GetHigh32(a) != 1)
+ return -6;
+
+ a = w64From32(0,1);
+ w64Decrement(&a);
+ if (w64GetLow32(a) != 0 || w64GetHigh32(a) != 0)
+ return -7;
+
+ a = w64From32(1,0);
+ w64Decrement(&a);
+ if (w64GetLow32(a) != 0xffffffff || w64GetHigh32(a) != 0)
+ return -8;
+
+ a = w64From32(0xabcdef, 0xdeed);
+ b = w64From32(0xabcdef, 0xdeed);
+ if (!w64Equal(a, b))
+ return -9;
+
+ a = w64From32(1, 1);
+ b = w64From32(0, 1);
+ if (w64Equal(a, b))
+ return -10;
+
+ wrap = 0;
+ a = w64From32(0x0, 0x1);
+ b = w64Add32(a, 0x1, &wrap);
+ if (w64GetLow32(b) != 0x2 || w64GetHigh32(b) != 0x0 || wrap)
+ return -11;
+
+ wrap = 0;
+ a = w64From32(0x0, 0xffffffff);
+ b = w64Add32(a, 0x1, &wrap);
+ if (w64GetLow32(b) != 0x0 || w64GetHigh32(b) != 0x01 || wrap)
+ return -12;
+
+ wrap = 0;
+ a = w64From32(0xffffffff, 0xffffffff);
+ b = w64Add32(a, 0x1, &wrap);
+ if (w64GetLow32(b) != 0x0 || w64GetHigh32(b) != 0x00 || !wrap)
+ return -13;
+
+ wrap = 0;
+ a = w64From32(0x0, 0x1);
+ b = w64Sub32(a, 0x1, &wrap);
+ if (w64GetLow32(b) != 0x0 || w64GetHigh32(b) != 0x00 || wrap)
+ return -14;
+
+ wrap = 0;
+ a = w64From32(0xffffffff, 0x0);
+ b = w64Sub32(a, 0x1, &wrap);
+ if (w64GetLow32(b) != 0xffffffff ||
+ w64GetHigh32(b) != 0xfffffffe || wrap)
+ return -15;
+
+ wrap = 0;
+ a = w64From32(0x0, 0x0);
+ b = w64Sub32(a, 0x1, &wrap);
+ if (w64GetLow32(b) != 0xffffffff ||
+ w64GetHigh32(b) != 0xffffffff || !wrap)
+ return -16;
+
+ a = w64From32(0x0, 0x0);
+ b = w64From32(0x0, 0x0);
+ if (w64GT(a,b) || w64GT(b,a) || !w64GTE(a,b) || w64LT(a,b) || w64LT(b,a))
+ return -17;
+
+ a = w64From32(0x0, 0x1);
+ b = w64From32(0x0, 0x0);
+ if (!w64GT(a, b) || w64GT(b, a) || !w64GTE(a, b) || w64GTE(b, a) ||
+ w64LT(a, b) || !w64LT(b, a))
+ return -18;
+
+ a = w64From32(0x1, 0x0);
+ b = w64From32(0x0, 0x0);
+ if (!w64GT(a, b) || w64GT(b, a) || !w64GTE(a, b) || w64GTE(b, a) ||
+ !w64LT(b, a) || w64LT(a, b))
+ return -19;
+
+ a = w64From32(0x1, 0x0);
+ b = w64From32(0x1, 0x0);
+ if (w64GT(a,b) || w64GT(b,a) || !w64GTE(a,b) || w64LT(a,b))
+ return -20;
+
+ a = w64From32(0x1, 0x1);
+ b = w64From32(0x1, 0x0);
+ if (!w64GT(a, b) || w64GT(b, a) || !w64GTE(a, b) || w64GTE(b, a) ||
+ w64LT(a, b) || !w64LT(b, a))
+ return -21;
+
+ a = w64From32(0x2, 0x1);
+ b = w64From32(0x1, 0x3);
+ if (!w64GT(a, b) || w64GT(b, a) || !w64GTE(a, b) || w64GTE(b, a) ||
+ w64LT(a, b) || !w64LT(b, a))
+ return -22;
+
+ a = w64From32(0x0, 0x0);
+ if (!w64IsZero(a))
+ return -23;
+
+ a = w64From32(0x01020304, 0x05060708);
+ c64toa(&a, raw);
+ if (raw[0] != 0x01
+ ||raw[1] != 0x02
+ ||raw[2] != 0x03
+ ||raw[3] != 0x04
+ ||raw[4] != 0x05
+ ||raw[5] != 0x06
+ ||raw[6] != 0x07
+ ||raw[7] != 0x08) {
+ return -24;
+ }
+
+ b = w64From32(0x0,0x0);
+ ato64(raw, &b);
+ if (w64GetHigh32(b) != 0x01020304 || w64GetLow32(b) != 0x05060708)
+ return -25;
+
+ w64Zero(&b);
+ if (w64GetHigh32(b) != 0x0 || w64GetLow32(b) != 0x0)
+ return -26;
+
+ b = w64From32(0x0, 0xffffffff);
+ w64Increment(&b);
+ if (w64GetHigh32(b) != 0x1 || w64GetLow32(b) != 0x0)
+ return -27;
+ b = w64From32(0xffffffff, 0xffffffff);
+ w64Increment(&b);
+ if (w64GetHigh32(b) != 0x0 || w64GetLow32(b) != 0x0)
+ return -28;
+ b = w64From32(0xffffffff, 0x0);
+ w64Decrement(&b);
+ if (w64GetHigh32(b) != 0xfffffffe || w64GetLow32(b) != 0xffffffff)
+ return -29;
+
+ a = w64From32(0x01, 0x20);
+ b = w64From32(0x01, 0x10);
+ c = w64Sub(a,b);
+ if (w64GetHigh32(c) != 0x0 || w64GetLow32(c) != 0x10)
+ return -30;
+ c = w64Sub(b,a);
+ if (w64GetHigh32(c) != 0xffffffff || w64GetLow32(c) != 0xfffffff0)
+ return -31;
+
+ a = w64From32(0x01, 0x10);
+ b = w64From32(0x00, 0x20);
+ c = w64Sub(a,b);
+ if (w64GetHigh32(c) != 0x00000000 || w64GetLow32(c) != 0xfffffff0)
+ return -32;
+
+ return 0;
+}
+
+#endif /* WOLFSSL_W64_WRAPPER */
diff --git a/wolfcrypt/src/misc.c b/wolfcrypt/src/misc.c
index 898f997c2..ac9620d21 100644
--- a/wolfcrypt/src/misc.c
+++ b/wolfcrypt/src/misc.c
@@ -546,6 +546,250 @@ WC_STATIC WC_INLINE byte ctSetLTE(int a, int b)
}
#endif
+#if defined(WOLFSSL_W64_WRAPPER)
+#if defined(WORD64_AVAILABLE) && !defined(WOLFSSL_W64_WRAPPER_TEST)
+WC_STATIC WC_INLINE void w64Increment(w64wrapper *n) {
+ n->n++;
+}
+
+WC_STATIC WC_INLINE void w64Decrement(w64wrapper *n) {
+ n->n--;
+}
+
+WC_STATIC WC_INLINE byte w64Equal(w64wrapper a, w64wrapper b) {
+ return (a.n == b.n);
+}
+
+WC_STATIC WC_INLINE word32 w64GetLow32(w64wrapper n) {
+ return (word32)n.n;
+}
+
+WC_STATIC WC_INLINE word32 w64GetHigh32(w64wrapper n) {
+ return (word32)(n.n >> 32);
+}
+
+WC_STATIC WC_INLINE void w64SetLow32(w64wrapper *n, word32 low) {
+ n->n = (n->n & (~(word64)(0xffffffff))) | low;
+}
+
+WC_STATIC WC_INLINE w64wrapper w64Add32(w64wrapper a, word32 b, byte *wrap) {
+ a.n = a.n + b;
+ if (a.n < b && wrap != NULL)
+ *wrap = 1;
+
+ return a;
+}
+
+WC_STATIC WC_INLINE w64wrapper w64Sub32(w64wrapper a, word32 b, byte *wrap)
+{
+ if (a.n < b && wrap != NULL)
+ *wrap = 1;
+ a.n = a.n - b;
+ return a;
+}
+
+WC_STATIC WC_INLINE byte w64GT(w64wrapper a, w64wrapper b)
+{
+ return a.n > b.n;
+}
+
+WC_STATIC WC_INLINE byte w64IsZero(w64wrapper a)
+{
+ return a.n == 0;
+}
+
+WC_STATIC WC_INLINE void c64toa(const w64wrapper *a, byte *out)
+{
+#ifdef BIG_ENDIAN_ORDER
+ XMEMCPY(out, &a->n, sizeof(a->n));
+#else
+ word64 _out;
+ _out = ByteReverseWord64(a->n);
+ XMEMCPY(out, &_out, sizeof(_out));
+#endif /* BIG_ENDIAN_ORDER */
+}
+
+WC_STATIC WC_INLINE void ato64(const byte *in, w64wrapper *w64)
+{
+#ifdef BIG_ENDIAN_ORDER
+ XMEMCPY(&w64->n, in, sizeof(w64->n));
+#else
+ word64 _in;
+ XMEMCPY(&_in, in, sizeof(_in));
+ w64->n = ByteReverseWord64(_in);
+#endif /* BIG_ENDIAN_ORDER */
+}
+
+WC_STATIC WC_INLINE w64wrapper w64From32(word32 hi, word32 lo)
+{
+ w64wrapper ret;
+ ret.n = ((word64)hi << 32) | lo;
+ return ret;
+}
+
+WC_STATIC WC_INLINE byte w64GTE(w64wrapper a, w64wrapper b)
+{
+ return a.n >= b.n;
+}
+
+WC_STATIC WC_INLINE byte w64LT(w64wrapper a, w64wrapper b)
+{
+ return a.n < b.n;
+}
+
+WC_STATIC WC_INLINE w64wrapper w64Sub(w64wrapper a, w64wrapper b)
+{
+ a.n -= b.n;
+ return a;
+}
+
+WC_STATIC WC_INLINE void w64Zero(w64wrapper *a)
+{
+ a->n = 0;
+}
+
+#else
+WC_STATIC WC_INLINE void w64Increment(w64wrapper *n)
+{
+ n->n[1]++;
+ if (n->n[1] == 0)
+ n->n[0]++;
+}
+
+WC_STATIC WC_INLINE void w64Decrement(w64wrapper *n) {
+ if (n->n[1] == 0)
+ n->n[0]--;
+ n->n[1]--;
+}
+
+WC_STATIC WC_INLINE byte w64Equal(w64wrapper a, w64wrapper b)
+{
+ return (a.n[0] == b.n[0] && a.n[1] == b.n[1]);
+}
+
+WC_STATIC WC_INLINE word32 w64GetLow32(w64wrapper n) {
+ return n.n[1];
+}
+
+WC_STATIC WC_INLINE word32 w64GetHigh32(w64wrapper n) {
+ return n.n[0];
+}
+
+WC_STATIC WC_INLINE void w64SetLow32(w64wrapper *n, word32 low)
+{
+ n->n[1] = low;
+}
+
+WC_STATIC WC_INLINE w64wrapper w64Add32(w64wrapper a, word32 b, byte *wrap)
+{
+ a.n[1] = a.n[1] + b;
+ if (a.n[1] < b) {
+ a.n[0]++;
+ if (wrap != NULL && a.n[0] == 0)
+ *wrap = 1;
+ }
+
+ return a;
+}
+
+WC_STATIC WC_INLINE w64wrapper w64Sub32(w64wrapper a, word32 b, byte *wrap)
+{
+ byte _underflow = 0;
+ if (a.n[1] < b)
+ _underflow = 1;
+
+ a.n[1] -= b;
+ if (_underflow) {
+ if (a.n[0] == 0 && wrap != NULL)
+ *wrap = 1;
+ a.n[0]--;
+ }
+
+ return a;
+}
+
+WC_STATIC WC_INLINE w64wrapper w64Sub(w64wrapper a, w64wrapper b)
+{
+ if (a.n[1] < b.n[1])
+ a.n[0]--;
+ a.n[1] -= b.n[1];
+ a.n[0] -= b.n[0];
+ return a;
+}
+
+WC_STATIC WC_INLINE void w64Zero(w64wrapper *a)
+{
+ a->n[0] = a->n[1] = 0;
+}
+
+WC_STATIC WC_INLINE byte w64GT(w64wrapper a, w64wrapper b)
+{
+ if (a.n[0] > b.n[0])
+ return 1;
+ if (a.n[0] == b.n[0])
+ return a.n[1] > b.n[1];
+ return 0;
+}
+
+WC_STATIC WC_INLINE byte w64GTE(w64wrapper a, w64wrapper b)
+{
+ if (a.n[0] > b.n[0])
+ return 1;
+ if (a.n[0] == b.n[0])
+ return a.n[1] >= b.n[1];
+ return 0;
+}
+
+WC_STATIC WC_INLINE byte w64IsZero(w64wrapper a)
+{
+ return a.n[0] == 0 && a.n[1] == 0;
+}
+
+WC_STATIC WC_INLINE void c64toa(w64wrapper *a, byte *out)
+{
+#ifdef BIG_ENDIAN_ORDER
+ word32 *_out = (word32*)(out);
+ _out[0] = a->n[0];
+ _out[1] = a->n[1];
+#else
+ c32toa(a->n[0], out);
+ c32toa(a->n[1], out + 4);
+#endif /* BIG_ENDIAN_ORDER */
+}
+
+WC_STATIC WC_INLINE void ato64(const byte *in, w64wrapper *w64)
+{
+#ifdef BIG_ENDIAN_ORDER
+ const word32 *_in = (const word32*)(in);
+ w64->n[0] = *_in;
+ w64->n[1] = *(_in + 1);
+#else
+ ato32(in, &w64->n[0]);
+ ato32(in + 4, &w64->n[1]);
+#endif /* BIG_ENDIAN_ORDER */
+}
+
+WC_STATIC WC_INLINE w64wrapper w64From32(word32 hi, word32 lo)
+{
+ w64wrapper w64;
+ w64.n[0] = hi;
+ w64.n[1] = lo;
+ return w64;
+}
+
+WC_STATIC WC_INLINE byte w64LT(w64wrapper a, w64wrapper b)
+{
+ if (a.n[0] < b.n[0])
+ return 1;
+ if (a.n[0] == b.n[0])
+ return a.n[1] < b.n[1];
+
+ return 0;
+}
+
+#endif /* WORD64_AVAILABLE && !WOLFSSL_W64_WRAPPER_TEST */
+#endif /* WOLFSSL_W64_WRAPPER */
+
#undef WC_STATIC
#endif /* !WOLFSSL_MISC_INCLUDED && !NO_INLINE */
diff --git a/wolfssl/internal.h b/wolfssl/internal.h
index 7cc28a0d4..8103dbc04 100644
--- a/wolfssl/internal.h
+++ b/wolfssl/internal.h
@@ -1179,6 +1179,7 @@ enum Misc {
DTLS_MAJOR = 0xfe, /* DTLS major version number */
DTLS_MINOR = 0xff, /* DTLS minor version number */
DTLSv1_2_MINOR = 0xfd, /* DTLS minor version number */
+ DTLSv1_3_MINOR = 0xfc, /* DTLS minor version number */
SSLv3_MAJOR = 3, /* SSLv3 and TLSv1+ major version number */
SSLv3_MINOR = 0, /* TLSv1 minor version number */
TLSv1_MINOR = 1, /* TLSv1 minor version number */
@@ -1293,6 +1294,8 @@ enum Misc {
DTLS_HANDSHAKE_HEADER_SZ = 12, /* normal + seq(2) + offset(3) + length(3) */
DTLS_RECORD_HEADER_SZ = 13, /* normal + epoch(2) + seq_num(6) */
+ DTLS_UNIFIED_HEADER_MIN_SZ = 2,
+ DTLS_RECORD_HEADER_MAX_SZ = 13,
DTLS_HANDSHAKE_EXTRA = 8, /* diff from normal */
DTLS_RECORD_EXTRA = 8, /* diff from normal */
DTLS_HANDSHAKE_SEQ_SZ = 2, /* handshake header sequence number */
@@ -1497,6 +1500,11 @@ enum Misc {
#endif
#endif
+/* minimum DTLS Downgrade Minor version */
+#ifndef WOLFSSL_MIN_DTLS_DOWNGRADE
+#define WOLFSSL_MIN_DTLS_DOWNGRADE DTLS_MINOR;
+#endif
+
/* Set max implicit IV size for AEAD cipher suites */
#define AEAD_MAX_IMP_SZ 12
@@ -1625,7 +1633,7 @@ enum Misc {
typedef char _args_test[sizeof((x)) >= sizeof((y)) ? 1 : -1]; \
(void)sizeof(_args_test)
-/* states */
+/* states. Adding state before HANDSHAKE_DONE will break session importing */
enum states {
NULL_STATE = 0,
@@ -1646,7 +1654,12 @@ enum states {
CLIENT_CHANGECIPHERSPEC_COMPLETE,
CLIENT_FINISHED_COMPLETE,
- HANDSHAKE_DONE
+ HANDSHAKE_DONE,
+
+#ifdef WOLFSSL_DTLS13
+ SERVER_FINISHED_ACKED,
+#endif /* WOLFSSL_DTLS13 */
+
};
/* SSL Version */
@@ -1666,6 +1679,10 @@ WOLFSSL_LOCAL ProtocolVersion MakeTLSv1_3(void);
WOLFSSL_LOCAL ProtocolVersion MakeDTLSv1(void);
WOLFSSL_LOCAL ProtocolVersion MakeDTLSv1_2(void);
+#ifdef WOLFSSL_DTLS13
+ WOLFSSL_LOCAL ProtocolVersion MakeDTLSv1_3(void);
+#endif /* WOLFSSL_DTLS13 */
+
#endif
#ifdef WOLFSSL_SESSION_EXPORT
WOLFSSL_LOCAL int wolfSSL_session_export_internal(WOLFSSL* ssl, byte* buf,
@@ -1799,6 +1816,10 @@ WOLFSSL_LOCAL int DoTls13ServerHello(WOLFSSL* ssl, const byte* input,
word32* inOutIdx, word32 helloSz,
byte* extMsgType);
WOLFSSL_LOCAL int RestartHandshakeHash(WOLFSSL* ssl);
+
+WOLFSSL_LOCAL int Tls13DeriveKey(WOLFSSL *ssl, byte *output, int outputLen,
+ const byte *secret, const byte *label, word32 labelLen, int hashAlgo,
+ int includeMsgs);
#endif
int TimingPadVerify(WOLFSSL* ssl, const byte* input, int padLen, int macSz,
int pLen, int content);
@@ -2228,6 +2249,11 @@ typedef struct Keys {
byte aead_dec_imp_IV[AEAD_MAX_IMP_SZ];
#endif
+#ifdef WOLFSSL_DTLS13
+ byte client_sn_key[MAX_SYM_KEY_SIZE];
+ byte server_sn_key[MAX_SYM_KEY_SIZE];
+#endif /* WOLFSSL_DTLS13 */
+
word32 peer_sequence_number_hi;
word32 peer_sequence_number_lo;
word32 sequence_number_hi;
@@ -2237,6 +2263,12 @@ typedef struct Keys {
word16 curEpoch; /* Received epoch in current record */
word16 curSeq_hi; /* Received sequence in current record */
word32 curSeq_lo;
+
+#ifdef WOLFSSL_DTLS13
+ w64wrapper curEpoch64; /* Received epoch in current record */
+ w64wrapper curSeq;
+#endif /* WOLFSSL_DTLS13 */
+
#ifdef WOLFSSL_MULTICAST
byte curPeerId; /* Received peer group ID in current record */
#endif
@@ -3300,6 +3332,16 @@ typedef struct Ciphers {
#endif
} Ciphers;
+#ifdef WOLFSSL_DTLS13
+typedef struct RecordNumberCiphers {
+#if defined(BUILD_AES) || defined(BUILD_AESGCM)
+ Aes *aes;
+#endif /* BUILD_AES || BUILD_AESGCM */
+#ifdef HAVE_CHACHA
+ ChaCha *chacha;
+#endif
+} RecordNumberCiphers;
+#endif /* WOLFSSL_DTLS13 */
#ifdef HAVE_ONE_TIME_AUTH
/* Ciphers for one time authentication such as poly1305 */
@@ -3519,7 +3561,12 @@ enum ConnectState {
FIRST_REPLY_THIRD,
FIRST_REPLY_FOURTH,
FINISHED_DONE,
- SECOND_REPLY_DONE
+ SECOND_REPLY_DONE,
+
+#ifdef WOLFSSL_DTLS13
+ WAIT_FINISHED_ACK
+#endif /* WOLFSSL_DTLS13 */
+
};
@@ -3799,6 +3846,10 @@ typedef struct Options {
#endif
word16 buildingMsg:1; /* If set then we need to re-enter the
* handshake logic. */
+#ifdef WOLFSSL_DTLS13
+ word16 dtls13SendMoreAcks:1; /* Send more acks during the
+ * handshake process */
+#endif
/* need full byte values for this section */
byte processReply; /* nonblocking resume */
@@ -4288,6 +4339,92 @@ typedef enum EarlyDataState {
} EarlyDataState;
#endif
+#ifdef WOLFSSL_DTLS13
+
+typedef struct Dtls13UnifiedHdrInfo {
+ word16 recordLength;
+ word16 headerLength;
+ byte seqLo;
+ byte seqHi;
+ byte seqHiPresent:1;
+ byte epochBits;
+} Dtls13UnifiedHdrInfo;
+
+enum {
+ DTLS13_EPOCH_EARLYDATA = 1,
+ DTLS13_EPOCH_HANDSHAKE = 2,
+ DTLS13_EPOCH_TRAFFIC0 = 3
+};
+
+typedef struct Dtls13Epoch {
+ w64wrapper epochNumber;
+
+ w64wrapper nextSeqNumber;
+ w64wrapper nextPeerSeqNumber;
+
+ word32 window[WOLFSSL_DTLS_WINDOW_WORDS];
+
+ /* key material for the epoch */
+ byte client_write_key[MAX_SYM_KEY_SIZE];
+ byte server_write_key[MAX_SYM_KEY_SIZE];
+ byte client_write_IV[MAX_WRITE_IV_SZ];
+ byte server_write_IV[MAX_WRITE_IV_SZ];
+
+ byte aead_exp_IV[AEAD_MAX_EXP_SZ];
+ byte aead_enc_imp_IV[AEAD_MAX_IMP_SZ];
+ byte aead_dec_imp_IV[AEAD_MAX_IMP_SZ];
+
+ byte client_sn_key[MAX_SYM_KEY_SIZE];
+ byte server_sn_key[MAX_SYM_KEY_SIZE];
+
+ byte isValid;
+ byte side;
+} Dtls13Epoch;
+
+#ifndef DTLS13_EPOCH_SIZE
+#define DTLS13_EPOCH_SIZE 4
+#endif
+
+#ifndef DTLS13_RETRANS_RN_SIZE
+#define DTLS13_RETRANS_RN_SIZE 3
+#endif
+
+enum Dtls13RtxFsmState {
+ DTLS13_RTX_FSM_PREPARING = 0,
+ DTLS13_RTX_FSM_SENDING,
+ DTLS13_RTX_FSM_WAITING,
+ DTLS13_RTX_FSM_FINISHED
+};
+
+typedef struct Dtls13RtxRecord {
+ struct Dtls13RtxRecord *next;
+ word16 length;
+ byte *data;
+ w64wrapper epoch;
+ w64wrapper seq[DTLS13_RETRANS_RN_SIZE];
+ byte rnIdx;
+ byte handshakeType;
+} Dtls13RtxRecord;
+
+typedef struct Dtls13RecordNumber {
+ struct Dtls13RecordNumber *next;
+ w64wrapper epoch;
+ w64wrapper seq;
+} Dtls13RecordNumber;
+
+typedef struct Dtls13Rtx {
+ enum Dtls13RtxFsmState state;
+ Dtls13RtxRecord *rtxRecords;
+ Dtls13RtxRecord **rtxRecordTailPtr;
+ Dtls13RecordNumber *seenRecords;
+ byte triggeredRtxs;
+ byte sendAcks:1;
+ byte retransmit:1;
+ word32 lastRtx;
+} Dtls13Rtx;
+
+#endif /* WOLFSSL_DTLS13 */
+
/* wolfSSL ssl type */
struct WOLFSSL {
WOLFSSL_CTX* ctx;
@@ -4482,6 +4619,29 @@ struct WOLFSSL {
* (selected profiles - up to 16) */
word16 dtlsSrtpId; /* DTLS-with-SRTP profile ID selected */
#endif
+#ifdef WOLFSSL_DTLS13
+ RecordNumberCiphers dtlsRecordNumberEncrypt;
+ RecordNumberCiphers dtlsRecordNumberDecrypt;
+ Dtls13Epoch dtls13Epochs[DTLS13_EPOCH_SIZE];
+ Dtls13Epoch *dtls13EncryptEpoch;
+ Dtls13Epoch *dtls13DecryptEpoch;
+ w64wrapper dtls13Epoch;
+ w64wrapper dtls13PeerEpoch;
+
+ word16 dtls13CurRlLength;
+
+ /* used to store the message if it needs to be fragmented */
+ buffer dtls13FragmentsBuffer;
+ byte dtls13SendingFragments:1;
+ byte dtls13SendingAckOrRtx:1;
+ byte dtls13FastTimeout:1;
+ byte dtls13WaitKeyUpdateAck:1;
+ word32 dtls13MessageLength;
+ word32 dtls13FragOffset;
+ byte dtls13FragHandshakeType;
+ Dtls13Rtx dtls13Rtx;
+
+#endif /* WOLFSSL_DTLS13 */
#endif /* WOLFSSL_DTLS */
#ifdef WOLFSSL_CALLBACKS
TimeoutInfo timeoutInfo; /* info saved during handshake */
@@ -4765,7 +4925,10 @@ enum ContentType {
change_cipher_spec = 20,
alert = 21,
handshake = 22,
- application_data = 23
+ application_data = 23,
+#ifdef WOLFSSL_DTLS13
+ ack = 26,
+#endif /* WOLFSSL_DTLS13 */
};
@@ -4989,6 +5152,8 @@ WOLFSSL_LOCAL void DoCertFatalAlert(WOLFSSL* ssl, int ret);
#endif
#endif
+WOLFSSL_LOCAL int cipherExtraData(WOLFSSL* ssl);
+
#ifndef NO_WOLFSSL_CLIENT
WOLFSSL_LOCAL int SendClientHello(WOLFSSL* ssl);
#ifdef WOLFSSL_TLS13
@@ -5027,6 +5192,10 @@ WOLFSSL_LOCAL void DoCertFatalAlert(WOLFSSL* ssl, int ret);
WOLFSSL_LOCAL int VerifyForTxDtlsMsgDelete(WOLFSSL* ssl, DtlsMsg* item);
WOLFSSL_LOCAL void DtlsMsgPoolReset(WOLFSSL* ssl);
WOLFSSL_LOCAL int DtlsMsgPoolSend(WOLFSSL* ssl, int sendOnlyFirstPacket);
+ WOLFSSL_LOCAL int GetDtlsHandShakeHeader(WOLFSSL *ssl, const byte *input,
+ word32 *inOutIdx, byte *type, word32 *size, word32 *fragOffset,
+ word32 *fragSz, word32 totalSz);
+ WOLFSSL_LOCAL int DtlsMsgDrain(WOLFSSL *ssl);
#endif /* WOLFSSL_DTLS */
#if defined(HAVE_SECURE_RENEGOTIATION) && defined(WOLFSSL_DTLS)
@@ -5218,6 +5387,59 @@ WOLFSSL_LOCAL int oid2nid(word32 oid, int grp);
WOLFSSL_LOCAL word32 nid2oid(int nid, int grp);
#endif
+#ifdef WOLFSSL_DTLS13
+
+WOLFSSL_LOCAL struct Dtls13Epoch* Dtls13GetEpoch(WOLFSSL* ssl,
+ w64wrapper epochNumber);
+WOLFSSL_LOCAL int Dtls13NewEpoch(WOLFSSL* ssl, w64wrapper epochNumber,
+ int side);
+WOLFSSL_LOCAL int Dtls13SetEpochKeys(WOLFSSL* ssl, w64wrapper epochNumber,
+ enum encrypt_side side);
+WOLFSSL_LOCAL int Dtls13GetSeq(WOLFSSL* ssl, int order, word32* seq,
+ byte increment);
+WOLFSSL_LOCAL int Dtls13DoScheduledWork(WOLFSSL* ssl);
+WOLFSSL_LOCAL int Dtls13DeriveSnKeys(WOLFSSL* ssl, int provision);
+WOLFSSL_LOCAL int Dtls13SetRecordNumberKeys(WOLFSSL* ssl,
+ enum encrypt_side side);
+
+WOLFSSL_LOCAL int Dtls13AddHeaders(byte* output, word32 length,
+ enum HandShakeType hs_type, WOLFSSL* ssl);
+WOLFSSL_LOCAL word16 Dtls13GetHeadersLength(enum HandShakeType type);
+WOLFSSL_LOCAL word16 Dtls13GetRlHeaderLength(byte is_encrypted);
+WOLFSSL_LOCAL int Dtls13RlAddCiphertextHeader(WOLFSSL* ssl, byte* out,
+ word16 length);
+WOLFSSL_LOCAL int Dtls13RlAddPlaintextHeader(WOLFSSL* ssl, byte* out,
+ enum ContentType content_type, word16 length);
+WOLFSSL_LOCAL int Dtls13EncryptRecordNumber(WOLFSSL* ssl, byte* hdr,
+ word16 recordLength);
+WOLFSSL_LOCAL int Dtls13IsUnifiedHeader(byte header_flags);
+WOLFSSL_LOCAL int Dtls13ParseUnifiedRecordLayer(WOLFSSL* ssl, const byte* input,
+ word16 input_size, Dtls13UnifiedHdrInfo* hdrInfo);
+WOLFSSL_LOCAL int Dtls13HandshakeSend(WOLFSSL* ssl, byte* output,
+ word16 output_size, word16 length, enum HandShakeType handshake_type,
+ int hash_output);
+WOLFSSL_LOCAL int Dtls13RecordRecvd(WOLFSSL* ssl);
+WOLFSSL_LOCAL int Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input,
+ word32* inOutIdx, word32 totalSz);
+WOLFSSL_LOCAL int Dtls13HandshakeAddHeader(WOLFSSL* ssl, byte* output,
+ enum HandShakeType msg_type, word32 length);
+#define EE_MASK (0x3)
+WOLFSSL_LOCAL int Dtls13FragmentsContinue(WOLFSSL* ssl);
+WOLFSSL_LOCAL int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize,
+ word32* processedSize);
+WOLFSSL_LOCAL int Dtls13ReconstructEpochNumber(WOLFSSL* ssl, byte epochBits,
+ w64wrapper* epoch);
+WOLFSSL_LOCAL int Dtls13ReconstructSeqNumber(WOLFSSL* ssl,
+ Dtls13UnifiedHdrInfo* hdrInfo, w64wrapper* out);
+WOLFSSL_LOCAL int SendDtls13Ack(WOLFSSL* ssl);
+WOLFSSL_LOCAL int Dtls13RtxProcessingCertificate(WOLFSSL* ssl, byte* input,
+ word32 inputSize);
+WOLFSSL_LOCAL int Dtls13HashHandshake(WOLFSSL* ssl, const byte* output,
+ word16 length);
+WOLFSSL_LOCAL void Dtls13FreeFsmResources(WOLFSSL* ssl);
+WOLFSSL_LOCAL int Dtls13RtxTimeout(WOLFSSL* ssl);
+#endif /* WOLFSSL_DTLS13 */
+
#ifdef WOLFSSL_STATIC_EPHEMERAL
WOLFSSL_LOCAL int wolfSSL_StaticEphemeralKeyLoad(WOLFSSL* ssl, int keyAlgo, void* keyPtr);
#endif
diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h
index 8b6382806..829fc4745 100644
--- a/wolfssl/ssl.h
+++ b/wolfssl/ssl.h
@@ -865,6 +865,13 @@ WOLFSSL_API WOLFSSL_METHOD *wolfSSLv23_client_method_ex(void* heap);
WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_2_method_ex(void* heap);
WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_2_client_method_ex(void* heap);
WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_2_server_method_ex(void* heap);
+
+#ifdef WOLFSSL_DTLS13
+ WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_3_client_method_ex(void* heap);
+ WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_3_server_method_ex(void* heap);
+ WOLFSSL_API int wolfSSL_dtls13_has_pending_msg(WOLFSSL *ssl);
+#endif
+
#endif
/* CTX Method Constructor Functions */
@@ -899,6 +906,12 @@ WOLFSSL_ABI WOLFSSL_API WOLFSSL_METHOD *wolfTLSv1_2_client_method(void);
WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_2_method(void);
WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_2_client_method(void);
WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_2_server_method(void);
+
+#ifdef WOLFSSL_DTLS13
+ WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_3_client_method(void);
+ WOLFSSL_API WOLFSSL_METHOD *wolfDTLSv1_3_server_method(void);
+#endif
+
#endif
#ifdef HAVE_POLY1305
@@ -1298,6 +1311,8 @@ WOLFSSL_API int wolfSSL_dtls_get_using_nonblock(WOLFSSL* ssl);
#define wolfSSL_get_using_nonblock wolfSSL_dtls_get_using_nonblock
/* The old names are deprecated. */
WOLFSSL_API int wolfSSL_dtls_get_current_timeout(WOLFSSL* ssl);
+WOLFSSL_API int wolfSSL_dtls13_use_quick_timeout(WOLFSSL* ssl);
+WOLFSSL_API void wolfSSL_dtls13_set_send_more_acks(WOLFSSL* ssl, int value);
WOLFSSL_API int wolfSSL_DTLSv1_get_timeout(WOLFSSL* ssl,
WOLFSSL_TIMEVAL* timeleft);
WOLFSSL_API void wolfSSL_DTLSv1_set_initial_timeout_duration(WOLFSSL* ssl,
@@ -2995,6 +3010,7 @@ enum {
WOLFSSL_TLSV1_3 = 4,
WOLFSSL_DTLSV1 = 5,
WOLFSSL_DTLSV1_2 = 6,
+ WOLFSSL_DTLSV1_3 = 7,
WOLFSSL_USER_CA = 1, /* user added as trusted */
WOLFSSL_CHAIN_CA = 2 /* added to cache from trusted chain */
diff --git a/wolfssl/test.h b/wolfssl/test.h
index f5db32209..7ce3bb8cc 100644
--- a/wolfssl/test.h
+++ b/wolfssl/test.h
@@ -350,6 +350,10 @@
#endif
#endif
+#ifndef DEFAULT_TIMEOUT_SEC
+#define DEFAULT_TIMEOUT_SEC 2
+#endif
+
/* all certs relative to wolfSSL home directory now */
#if defined(WOLFSSL_NO_CURRDIR) || defined(WOLFSSL_MDK_SHELL)
#define caCertFile "certs/ca-cert.pem"
@@ -5299,6 +5303,67 @@ static WC_INLINE void EarlyDataStatus(WOLFSSL* ssl)
}
#endif /* WOLFSSL_EARLY_DATA */
+#if defined(HAVE_SESSION_TICKET) || defined (WOLFSSL_DTLS13)
+static WC_INLINE int process_handshake_messages(WOLFSSL* ssl, int blocking,
+ int* zero_return)
+{
+ int timeout = DEFAULT_TIMEOUT_SEC;
+ char foo[1];
+ int ret = 0;
+ int dtls;
+
+ (void)dtls;
+
+ if (zero_return == NULL || ssl == NULL)
+ return -1;
+
+ dtls = wolfSSL_dtls(ssl);
+ *zero_return = 0;
+
+ if (!blocking) {
+#ifdef WOLFSSL_DTLS
+ if (dtls) {
+ timeout = wolfSSL_dtls_get_current_timeout(ssl);
+
+#ifdef WOLFSSL_DTLS13
+ if (timeout > 4 && wolfSSL_dtls13_use_quick_timeout(ssl))
+ timeout /= 4;
+#endif /* WOLFSSL_DTLS13 */
+ }
+#endif /* WOLFSSL_DTLS */
+
+ ret = tcp_select(wolfSSL_get_fd(ssl), timeout);
+ if (ret == TEST_ERROR_READY) {
+ err_sys("tcp_select error");
+ return -1;
+ }
+
+ if (ret == TEST_TIMEOUT) {
+#ifdef WOLFSSL_DTLS
+ if (dtls) {
+ ret = wolfSSL_dtls_got_timeout(ssl);
+ if (ret != WOLFSSL_SUCCESS && !wolfSSL_want_write(ssl) &&
+ !wolfSSL_want_read(ssl)) {
+ err_sys("got timeout error");
+ return -1;
+ }
+ }
+#endif /* WOLFSSL_DTLS */
+ /* do the peek to detect if the peer closed the connection*/
+ }
+ }
+
+ ret = wolfSSL_peek(ssl, foo, 0);
+ if (ret < 0 && !wolfSSL_want_read(ssl) && !wolfSSL_want_write(ssl)) {
+ ret = wolfSSL_get_error(ssl, ret);
+ if (ret == WOLFSSL_ERROR_ZERO_RETURN)
+ *zero_return = 1;
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* HAVE_SESSION_TICKET || WOLFSSL_DTLS13 */
#if !defined(NO_FILESYSTEM) && defined(OPENSSL_EXTRA) && \
defined(DEBUG_UNIT_TEST_CERTS)
diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h
index cc3e1b204..cb70ad9e8 100644
--- a/wolfssl/wolfcrypt/settings.h
+++ b/wolfssl/wolfcrypt/settings.h
@@ -2700,6 +2700,22 @@ extern void uITRON4_free(void *p) ;
#define NO_SESSION_CACHE_REF
#endif
+/* DTLS v1.3 requires 64-bit number wrappers */
+#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_W64_WRAPPER)
+ #define WOLFSSL_W64_WRAPPER
+#endif
+
+/* DTLS v1.3 requires AES ECB if using AES */
+#if defined(WOLFSSL_DTLS13) && !defined(NO_AES) && \
+ !defined(WOLFSSL_AES_DIRECT)
+#define WOLFSSL_AES_DIRECT
+#endif
+
+#if defined(WOLFSSL_DTLS13) && (!defined(WOLFSSL_DTLS) || \
+ !defined(WOLFSSL_TLS13))
+#error "DTLS v1.3 requires both WOLFSSL_TLS13 and WOLFSSL_DTLS"
+#endif
+
/* ---------------------------------------------------------------------------
* Depricated Algorithm Handling
diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h
index d7c4a4dfd..fc9391af8 100644
--- a/wolfssl/wolfcrypt/types.h
+++ b/wolfssl/wolfcrypt/types.h
@@ -218,6 +218,14 @@ decouple library dependencies with standard string, memory and so on.
mp_digit, no 64 bit type so make mp_digit 16 bit */
#endif
+typedef struct w64wrapper {
+#if defined(WORD64_AVAILABLE) && !defined(WOLFSSL_W64_WRAPPER_TEST)
+ word64 n;
+#else
+ word32 n[2];
+#endif /* WORD64_AVAILABLE && WOLFSSL_W64_WRAPPER_TEST */
+} w64wrapper;
+
#ifdef WC_PTR_TYPE /* Allow user suppied type */
typedef WC_PTR_TYPE wc_ptr_t;
#elif defined(HAVE_UINTPTR_T)