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)