From d8ac35579c5a270e43c6ac8b1cd106665cf2e2c2 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 09:59:23 +0200 Subject: [PATCH 01/19] dtls13: add autotools, cmake build options and vstudio paths --- CMakeLists.txt | 20 ++++++++++++++++++++ IDE/WIN/wolfssl-fips.vcxproj | 2 ++ IDE/WIN10/wolfssl-fips.vcxproj | 1 + cmake/functions.cmake | 7 +++++++ configure.ac | 20 +++++++++++++++++++- src/dtls13.c | 30 ++++++++++++++++++++++++++++++ src/include.am | 4 ++++ wolfssl/wolfcrypt/settings.h | 11 +++++++++++ 8 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/dtls13.c diff --git a/CMakeLists.txt b/CMakeLists.txt index aac2062d2..5de61e5a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -266,6 +266,25 @@ 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") + + 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 +1889,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..8deaaa4c2 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,23 @@ 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 + if test "x$ENABLED_AES" = "xyes" + then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_AES_DIRECT" + fi +fi # CODING AC_ARG_ENABLE([coding], @@ -7850,6 +7866,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 +8186,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/src/dtls13.c b/src/dtls13.c new file mode 100644 index 000000000..209ce30c8 --- /dev/null +++ b/src/dtls13.c @@ -0,0 +1,30 @@ +/* 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 + +#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/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index cc3e1b204..fc71c3480 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -2700,6 +2700,17 @@ extern void uITRON4_free(void *p) ; #define NO_SESSION_CACHE_REF #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 From 758685173409be4d9f1512c6a9c6eb67a5a3b93d Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 09:59:26 +0200 Subject: [PATCH 02/19] dtls13: export functions They will be used by DTLSv1.3 code --- src/internal.c | 13 ++++++------- src/tls13.c | 36 ++++++++++++++++++------------------ wolfssl/internal.h | 10 ++++++++++ 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/internal.c b/src/internal.c index 19100f04e..1b2c4ee56 100644 --- a/src/internal.c +++ b/src/internal.c @@ -189,7 +189,6 @@ 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); @@ -9575,10 +9574,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; @@ -14960,7 +14959,7 @@ static WC_INLINE int DtlsUpdateWindow(WOLFSSL* ssl) } -static int DtlsMsgDrain(WOLFSSL* ssl) +int DtlsMsgDrain(WOLFSSL* ssl) { DtlsMsg* item = ssl->dtls_rx_msg_list; int ret = 0; @@ -19413,7 +19412,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 */ diff --git a/src/tls13.c b/src/tls13.c index 7e6b15729..e0d9a97a9 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -261,9 +261,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]; @@ -441,7 +441,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 +486,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 +529,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 +572,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 +615,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 +659,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 +813,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 +832,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 +851,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 +1220,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 +1231,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 +1242,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 +1253,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) diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 7cc28a0d4..ccf27b81f 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1799,6 +1799,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); @@ -4989,6 +4993,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 +5033,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) From 9d22e11776dedb558a46d2888ab2d7ca2039635e Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Mon, 30 May 2022 11:17:03 +0200 Subject: [PATCH 03/19] misc.c: introduce w64wrapper to handle 64bit numbers as word64 is not always available, introduce an abstract type and companion operations. They use a word64 if available and fallback on word32[2] otherwise. --- CMakeLists.txt | 1 + configure.ac | 1 + tests/include.am | 1 + tests/unit.c | 8 ++ tests/unit.h | 1 + tests/w64wrapper.c | 218 +++++++++++++++++++++++++++++++ wolfcrypt/src/misc.c | 244 +++++++++++++++++++++++++++++++++++ wolfssl/wolfcrypt/settings.h | 5 + wolfssl/wolfcrypt/types.h | 8 ++ 9 files changed, 487 insertions(+) create mode 100644 tests/w64wrapper.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5de61e5a7..28312f8cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -279,6 +279,7 @@ if(WOLFSSL_DTLS13) 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") diff --git a/configure.ac b/configure.ac index 8deaaa4c2..5b9645b26 100644 --- a/configure.ac +++ b/configure.ac @@ -3524,6 +3524,7 @@ then 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" diff --git a/tests/include.am b/tests/include.am index daf96c814..555ca90f6 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 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/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index fc71c3480..cb70ad9e8 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -2700,6 +2700,11 @@ 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) 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) From 60834ba51699d09cfe0ce386817c134df94fae25 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 09:59:24 +0200 Subject: [PATCH 04/19] dtls13: new methods and version negotiation --- src/dtls13.c | 43 ++++++++++++ src/internal.c | 127 ++++++++++++++++++++++++++-------- src/ssl.c | 14 ++++ src/tls.c | 169 ++++++++++++++++++++++++++++++++++++--------- src/tls13.c | 118 +++++++++++++++++++++++++------ wolfssl/internal.h | 13 ++++ wolfssl/ssl.h | 13 ++++ 7 files changed, 412 insertions(+), 85 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index 209ce30c8..3f860723c 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -27,4 +27,47 @@ #ifdef WOLFSSL_DTLS13 +#include +#include + +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); +} + #endif /* WOLFSSL_DTLS13 */ diff --git a/src/internal.c b/src/internal.c index 1b2c4ee56..4ecd716b4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -490,7 +490,15 @@ 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) @@ -2099,7 +2107,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"); @@ -8350,6 +8368,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 */ @@ -9416,6 +9446,7 @@ int CheckAvailableSize(WOLFSSL *ssl, int size) static int GetRecordHeader(WOLFSSL* ssl, const byte* input, word32* inOutIdx, RecordLayerHeader* rh, word16 *size) { + byte tls12minor; #ifdef OPENSSL_ALL word32 start = *inOutIdx; #endif @@ -9469,13 +9500,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 { @@ -9598,9 +9634,10 @@ 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; @@ -30173,6 +30210,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]; @@ -30230,11 +30269,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; @@ -30247,7 +30286,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); @@ -30256,26 +30303,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/ssl.c b/src/ssl.c index 7a0e15d32..25f27d237 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; @@ -19795,6 +19807,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 749a6e7bd..346d731b9 100644 --- a/src/tls.c +++ b/src/tls.c @@ -5628,6 +5628,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. @@ -5637,12 +5689,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 @@ -5653,17 +5716,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 @@ -5672,7 +5737,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 @@ -5709,6 +5774,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; @@ -5716,11 +5799,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 @@ -5730,26 +5813,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 @@ -5757,10 +5840,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 @@ -5806,6 +5889,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. */ @@ -5839,23 +5936,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, @@ -5865,12 +5966,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; @@ -5896,25 +5998,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. */ @@ -12768,7 +12871,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 e0d9a97a9..b929e01ab 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -3088,10 +3088,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; } @@ -3217,8 +3229,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; @@ -3364,6 +3377,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); @@ -3374,6 +3388,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; @@ -3428,18 +3450,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; } @@ -3502,6 +3543,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) @@ -3545,8 +3594,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; } } @@ -4739,6 +4793,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 @@ -4749,25 +4806,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) { @@ -4779,7 +4849,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); @@ -5096,7 +5168,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. */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index ccf27b81f..7603ab2a3 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 */ @@ -1497,6 +1498,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 @@ -1666,6 +1672,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, @@ -5228,6 +5238,9 @@ WOLFSSL_LOCAL int oid2nid(word32 oid, int grp); WOLFSSL_LOCAL word32 nid2oid(int nid, int grp); #endif +#ifdef WOLFSSL_DTLS13 + +#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..1d02e0f87 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -865,6 +865,12 @@ 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); +#endif + #endif /* CTX Method Constructor Functions */ @@ -899,6 +905,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 @@ -2995,6 +3007,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 */ From de0497305130f3c89920df205744d26c59067912 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 09:59:29 +0200 Subject: [PATCH 05/19] dtls13: record number encryption and decryption --- src/dtls13.c | 189 ++++++++++++++++++++++++++++++++++++++++++++- src/internal.c | 31 ++++++++ src/keys.c | 5 ++ src/tls13.c | 33 +++++++- wolfssl/internal.h | 24 ++++++ 5 files changed, 280 insertions(+), 2 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index 3f860723c..66ef38eb2 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -27,8 +27,17 @@ #ifdef WOLFSSL_DTLS13 -#include +#include #include +#include +#include + +#ifdef NO_INLINE +#include +#else +#define WOLFSSL_MISC_INCLUDED +#include +#endif WOLFSSL_METHOD* wolfDTLSv1_3_client_method_ex(void* heap) { @@ -70,4 +79,182 @@ WOLFSSL_METHOD* wolfDTLSv1_3_server_method(void) return wolfDTLSv1_3_server_method_ex(NULL); } +#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 */ + +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; +} + #endif /* WOLFSSL_DTLS13 */ diff --git a/src/internal.c b/src/internal.c index 4ecd716b4..ad3c099b5 100644 --- a/src/internal.c +++ b/src/internal.c @@ -2589,6 +2589,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 */ + } @@ -2642,6 +2650,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 */ } diff --git a/src/keys.c b/src/keys.c index b467275d9..e07300628 100644 --- a/src/keys.c +++ b/src/keys.c @@ -2959,6 +2959,11 @@ 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) { diff --git a/src/tls13.c b/src/tls13.c index b929e01ab..b1200b88b 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; } @@ -308,10 +321,16 @@ int Tls13DeriveKey(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) @@ -1263,6 +1282,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: #ifdef WOLFSSL_SMALL_STACK XFREE(key_dig, ssl->heap, DYNAMIC_TYPE_DIGEST); diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 7603ab2a3..c192ae257 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2242,6 +2242,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; @@ -3314,6 +3319,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 */ @@ -4496,6 +4511,11 @@ struct WOLFSSL { * (selected profiles - up to 16) */ word16 dtlsSrtpId; /* DTLS-with-SRTP profile ID selected */ #endif +#ifdef WOLFSSL_DTLS13 + RecordNumberCiphers dtlsRecordNumberEncrypt; + RecordNumberCiphers dtlsRecordNumberDecrypt; +#endif /* WOLFSSL_DTLS13 */ + #endif /* WOLFSSL_DTLS */ #ifdef WOLFSSL_CALLBACKS TimeoutInfo timeoutInfo; /* info saved during handshake */ @@ -5240,6 +5260,10 @@ WOLFSSL_LOCAL word32 nid2oid(int nid, int grp); #ifdef WOLFSSL_DTLS13 +WOLFSSL_LOCAL int Dtls13DeriveSnKeys(WOLFSSL* ssl, int provision); +WOLFSSL_LOCAL int Dtls13SetRecordNumberKeys(WOLFSSL* ssl, + enum encrypt_side side); + #endif /* WOLFSSL_DTLS13 */ #ifdef WOLFSSL_STATIC_EPHEMERAL WOLFSSL_LOCAL int wolfSSL_StaticEphemeralKeyLoad(WOLFSSL* ssl, int keyAlgo, void* keyPtr); From 2696c3cdd3fd07234ffc7e56681804c3095464ef Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 09:59:33 +0200 Subject: [PATCH 06/19] dtls13: change encryption keys dynamically based on the epoch In DTLSv1.3, because of retransmission and reordering, we may need to encrypt or decrypt records with older keys. As an example, if the server finished message is lost, the server will need to retransmit that message using handshake traffic keys, even if he already used the traffic0 ones (as, for example, to send NewSessionTicket just after the finished message). This commit implements a way to save the key bound to a DTLS epoch and setting the right key/epoch when needed. --- src/dtls13.c | 224 +++++++++++++++++++++++++++++++++++++++++++++ src/internal.c | 9 ++ wolfssl/internal.h | 49 ++++++++++ 3 files changed, 282 insertions(+) diff --git a/src/dtls13.c b/src/dtls13.c index 66ef38eb2..c0b825b19 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -159,6 +159,230 @@ static int Dtls13InitChaChaCipher(RecordNumberCiphers* c, byte* key, } #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)); +} + +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; diff --git a/src/internal.c b/src/internal.c index ad3c099b5..692307a93 100644 --- a/src/internal.c +++ b/src/internal.c @@ -6762,6 +6762,15 @@ 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]; +#endif /* WOLFSSL_DTLS13 */ + return 0; } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index c192ae257..9b238babc 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4317,6 +4317,43 @@ typedef enum EarlyDataState { } EarlyDataState; #endif +#ifdef WOLFSSL_DTLS13 + +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; + +#define DTLS13_EPOCH_SIZE 3 + +#endif /* WOLFSSL_DTLS13 */ + /* wolfSSL ssl type */ struct WOLFSSL { WOLFSSL_CTX* ctx; @@ -4514,6 +4551,12 @@ struct WOLFSSL { #ifdef WOLFSSL_DTLS13 RecordNumberCiphers dtlsRecordNumberEncrypt; RecordNumberCiphers dtlsRecordNumberDecrypt; + Dtls13Epoch dtls13Epochs[DTLS13_EPOCH_SIZE]; + Dtls13Epoch *dtls13EncryptEpoch; + Dtls13Epoch *dtls13DecryptEpoch; + w64wrapper dtls13Epoch; + w64wrapper dtls13PeerEpoch; + #endif /* WOLFSSL_DTLS13 */ #endif /* WOLFSSL_DTLS */ @@ -5260,6 +5303,12 @@ WOLFSSL_LOCAL word32 nid2oid(int nid, int grp); #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 Dtls13DeriveSnKeys(WOLFSSL* ssl, int provision); WOLFSSL_LOCAL int Dtls13SetRecordNumberKeys(WOLFSSL* ssl, enum encrypt_side side); From 30fb664163f5e728704b27b3c1c81fcb49a9987d Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 09:59:36 +0200 Subject: [PATCH 07/19] internal.c: add runProcessingOneRecord section DTLSv1.3 needs to do some operation per-record, this commit adds an appropriate section to ProcessReplyEx. --- src/internal.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/internal.c b/src/internal.c index 692307a93..874d94a12 100644 --- a/src/internal.c +++ b/src/internal.c @@ -206,6 +206,7 @@ enum processReply { verifyEncryptedMessage, decryptMessage, verifyMessage, + runProcessingOneRecord, runProcessingOneMessage }; @@ -17916,10 +17917,14 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) #endif } - ssl->options.processReply = runProcessingOneMessage; + ssl->options.processReply = runProcessingOneRecord; FALL_THROUGH; /* the record layer is here */ + case runProcessingOneRecord: + ssl->options.processReply = runProcessingOneMessage; + FALL_THROUGH; + case runProcessingOneMessage: /* can't process a message if we have no data. */ if (ssl->buffers.inputBuffer.idx From 173077b14287534f97e7ef15563a8cf0756623ed Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 09:59:50 +0200 Subject: [PATCH 08/19] dtls: refactor DtlsUpdateWindow() window split the DtlsUpdateWindow() function, so part of the code can be reused by DTLSv1.3 code. --- src/internal.c | 120 +++++++++++++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 53 deletions(-) diff --git a/src/internal.c b/src/internal.c index 874d94a12..70229f91d 100644 --- a/src/internal.c +++ b/src/internal.c @@ -14922,21 +14922,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; @@ -14988,52 +15047,7 @@ 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; - } - - 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 { - if (diff >= DTLS_SEQ_BITS) - XMEMSET(window, 0, DTLS_SEQ_SZ); - else { - word32 idx, newDiff, temp, i; - word32 oldWindow[WOLFSSL_DTLS_WINDOW_WORDS]; - - 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; - *next_lo = cur_lo + 1; - if (*next_lo < cur_lo) - (*next_hi)++; - } - - return 1; + return _DtlsUpdateWindow(ssl, next_hi, next_lo, window); } From d0796627651674c0d1c24e6f60f1fb8fb5db6665 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 09:59:34 +0200 Subject: [PATCH 09/19] dtls13: support fragmentation, sending and receiving This commit implements the core of the header parsing, building, and the sending and receiving routines that handle fragmentation and defragmentation. * In DTLSv1.3 the header used for protected messages is a variable-length header, and it is described RFC9147 Section 4. * Fragmentation happens after building the full message, if necessary. If the underlying I/O can't send a fragment because of a WANT_WRITE error, the sending of fragments will continue in the next invocation of wolfSSL_connect/wolfSSL_accept/wolfSSL_write. In this case the message is saved in a buffer inside the WolfSSL object. * Defragmentation works like DTLSv1.2 defragmentation, and re-use most of the same code. * The Dtls13AddHeaders() function does not add the record layer header, but it lefts space for it. It is eventually placed by BuildTls13Message() to allow easier management of sequence numbers. --- src/dtls13.c | 1107 ++++++++++++++++++++++++++++++++++++++++++++ src/internal.c | 488 +++++++++++++++++-- src/keys.c | 11 +- src/tls13.c | 654 +++++++++++++++++++++++--- wolfssl/internal.h | 64 ++- 5 files changed, 2212 insertions(+), 112 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index c0b825b19..acc1a8686 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -31,6 +31,10 @@ #include #include #include +#include +#include +#include +#include #ifdef NO_INLINE #include @@ -39,6 +43,103 @@ #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 + WOLFSSL_METHOD* wolfDTLSv1_3_client_method_ex(void* heap) { WOLFSSL_METHOD* method; @@ -79,6 +180,970 @@ 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 void Dtls13MsgWasProcessed(WOLFSSL* ssl, enum HandShakeType hs) +{ + (void)hs; + + ssl->keys.dtls_expected_peer_handshake_number++; +} + +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, 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 int Dtls13SendFragmentedInternal(WOLFSSL* ssl) +{ + int fragLength, rlHeaderLength; + int remainingSize, maxFragment; + int recordLength; + byte isEncrypted; + byte* output; + int ret; + + isEncrypted = Dtls13TypeIsEncrypted(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, + 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 = Dtls13SendFragment(ssl, output, maxFragment, recordLength, + ssl->dtls13FragHandshakeType, 0, 1); + 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) +{ + (void)ssl; + + 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; + + 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, 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; + + /* 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 (maxLen < maxFrag) { + ret = Dtls13SendFragment(ssl, message, outputSize, length, + handshakeType, hashOutput, Dtls13SendNow(ssl, handshakeType)); + + 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"; @@ -233,6 +1298,48 @@ static void Dtls13EpochCopyKeys(WOLFSSL* ssl, Dtls13Epoch* e, Keys* k, int side) 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; diff --git a/src/internal.c b/src/internal.c index 70229f91d..3486d0be2 100644 --- a/src/internal.c +++ b/src/internal.c @@ -506,8 +506,16 @@ 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 && @@ -7425,8 +7433,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; @@ -8759,8 +8767,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 @@ -8784,6 +8803,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 @@ -9466,7 +9491,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; } @@ -9482,15 +9512,171 @@ 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; + + read_size = ssl->buffers.inputBuffer.length - *inOutIdx; + + if (Dtls13IsUnifiedHeader(*(input + *inOutIdx))) { + + /* version 1.3 already negotiated */ + if (ssl->options.tls1_3) + return GetDtls13RecordHeader(ssl, input, inOutIdx, rh, size); + + } + + /* 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) @@ -9503,34 +9689,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 && @@ -14904,6 +15072,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, @@ -15050,6 +15263,60 @@ static WC_INLINE int DtlsUpdateWindow(WOLFSSL* ssl) 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; + } + + nextSeq = ssl->dtls13DecryptEpoch->nextPeerSeqNumber; + window = ssl->dtls13DecryptEpoch->window; + seq = ssl->keys.curSeq; + + /* seq < nextSeq */ + if (w64LT(seq, nextSeq)) { + diff64 = w64Sub(nextSeq, seq); + + /* zero based index */ + w64Decrement(&diff64); + + /* FIXME: check that diff64 < DTLS_WORDS_BITS */ + diff = w64GetLow32(diff64); + wordIndex = ((int)diff) / DTLS_WORD_BITS; + wordOffset = ((int)diff) % DTLS_WORD_BITS; + + if (wordIndex >= WOLFSSL_DTLS_WINDOW_WORDS) { + WOLFSSL_MSG("Invalid sequence number to Dtls13UpdateWindow"); + return BAD_STATE_E; + } + + 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 */ + int DtlsMsgDrain(WOLFSSL* ssl) { @@ -17468,8 +17735,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 */ @@ -17565,6 +17839,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); @@ -17785,11 +18064,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 */ @@ -17816,6 +18106,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 && @@ -17936,6 +18235,35 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) /* 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; @@ -17986,7 +18314,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 */ @@ -17999,11 +18327,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) @@ -20497,6 +20850,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); @@ -20840,12 +21229,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) diff --git a/src/keys.c b/src/keys.c index e07300628..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; @@ -2966,7 +2975,7 @@ int SetKeysSide(WOLFSSL* ssl, enum encrypt_side side) #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/tls13.c b/src/tls13.c index b1200b88b..e1522f19c 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -1646,6 +1646,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, type, length); + return; + } +#endif /* WOLFSSL_DTLS13 */ + /* handshake header */ hs = (HandShakeHeader*)output; hs->type = type; @@ -1666,6 +1678,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, type, ssl); + return; + } +#endif /* WOLFSSL_DTLS13 */ + AddTls13RecordHeader(output, length + lengthAdj, handshake, ssl); AddTls13HandShakeHeader(output + outputAdj, length, 0, length, type, ssl); } @@ -1688,6 +1707,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, type, ssl); + return; + } +#endif /* WOLFSSL_DTLS13 */ + AddTls13RecordHeader(output, fragSz + lengthAdj, handshake, ssl); AddTls13HandShakeHeader(output + outputAdj, length, fragOffset, fragSz, type, ssl); @@ -1705,7 +1733,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 */ @@ -2419,9 +2452,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; @@ -2462,7 +2501,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) @@ -2505,7 +2552,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; } @@ -2671,6 +2724,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. @@ -2680,7 +2735,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; @@ -3025,7 +3080,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; @@ -3084,6 +3146,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 @@ -3191,8 +3263,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; @@ -3203,6 +3281,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 */ @@ -3248,6 +3332,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; @@ -3300,6 +3389,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; @@ -3333,11 +3428,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; @@ -3351,6 +3456,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 */ @@ -4431,6 +4547,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 @@ -4538,7 +4664,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 + \ @@ -4553,7 +4679,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; @@ -4573,6 +4699,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) @@ -4595,9 +4722,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); @@ -4657,8 +4789,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 @@ -4688,6 +4831,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; @@ -4927,6 +5080,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) { @@ -5132,6 +5291,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); @@ -5176,6 +5355,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. */ @@ -5243,9 +5427,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 @@ -5260,6 +5455,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); @@ -5283,7 +5491,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); @@ -5291,6 +5499,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; @@ -5318,6 +5536,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; @@ -5352,6 +5588,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); @@ -5365,6 +5616,7 @@ static int SendTls13EncryptedExtensions(WOLFSSL* ssl) if (!ssl->options.groupMessages) ret = SendBuffered(ssl); + WOLFSSL_LEAVE("SendTls13EncryptedExtensions", ret); WOLFSSL_END(WC_FUNC_ENCRYPTED_EXTENSIONS_SEND); @@ -5405,6 +5657,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) @@ -5439,6 +5696,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); @@ -6054,6 +6324,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) { @@ -6061,11 +6338,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); @@ -6154,25 +6445,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) { @@ -6246,9 +6547,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*) @@ -6314,6 +6628,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)) @@ -6572,7 +6894,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 */ @@ -6580,6 +6906,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, @@ -7178,7 +7516,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) { @@ -7269,6 +7607,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); @@ -7292,9 +7638,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) @@ -7305,6 +7664,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 */ @@ -7347,6 +7711,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); @@ -7362,20 +7737,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 @@ -7386,6 +7773,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 && @@ -7407,6 +7814,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 @@ -7422,6 +7849,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; @@ -7908,6 +8344,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) @@ -7993,6 +8434,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); @@ -8241,7 +8687,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; @@ -8352,7 +8798,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; } @@ -8361,7 +8807,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 @@ -8625,6 +9073,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) { @@ -8811,6 +9279,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()"); @@ -8838,6 +9307,19 @@ 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; +#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 @@ -8845,16 +9327,9 @@ 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)) { + if (advanceState) { ssl->options.connectState++; WOLFSSL_MSG("connect state: " "Advanced from last buffered fragment send"); @@ -8882,6 +9357,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: @@ -8896,14 +9383,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; @@ -8938,7 +9427,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; @@ -8972,7 +9461,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; @@ -8987,7 +9476,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; @@ -9745,9 +10234,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 @@ -9828,23 +10320,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; +#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"); @@ -9871,6 +10373,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) { @@ -9908,7 +10421,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); @@ -9949,7 +10462,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; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 9b238babc..1da42bae0 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1294,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 */ @@ -2256,6 +2258,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 @@ -4319,6 +4327,15 @@ typedef enum EarlyDataState { #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, @@ -4557,8 +4574,15 @@ struct WOLFSSL { w64wrapper dtls13Epoch; w64wrapper dtls13PeerEpoch; -#endif /* WOLFSSL_DTLS13 */ + word16 dtls13CurRlLength; + /* used to store the message if it needs to be fragmented */ + buffer dtls13FragmentsBuffer; + byte dtls13SendingFragments:1; + word32 dtls13MessageLength; + word32 dtls13FragOffset; + byte dtls13FragHandshakeType; +#endif /* WOLFSSL_DTLS13 */ #endif /* WOLFSSL_DTLS */ #ifdef WOLFSSL_CALLBACKS TimeoutInfo timeoutInfo; /* info saved during handshake */ @@ -4842,7 +4866,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 */ }; @@ -5309,11 +5336,44 @@ 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 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 Dtls13ReconstructEpochNumber(WOLFSSL* ssl, byte epochBits, + w64wrapper* epoch); +WOLFSSL_LOCAL int Dtls13ReconstructSeqNumber(WOLFSSL* ssl, + Dtls13UnifiedHdrInfo* hdrInfo, w64wrapper* out); +WOLFSSL_LOCAL int Dtls13HashHandshake(WOLFSSL* ssl, const byte* output, + word16 length); #endif /* WOLFSSL_DTLS13 */ + #ifdef WOLFSSL_STATIC_EPHEMERAL WOLFSSL_LOCAL int wolfSSL_StaticEphemeralKeyLoad(WOLFSSL* ssl, int keyAlgo, void* keyPtr); #endif From d1924928c0d300072d9c19761a4c48b2eff8a20c Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 09:59:43 +0200 Subject: [PATCH 10/19] dtls13: support retransmission Introduce ACK and retransmission logic, encapsulated in a Dtls13RtxFsm object. The retransmission or the sending of an ACK is scheduled by setting the appropriate flag inside the Dtls13RtxFSM object but the actual writing on the socket is deferred and done in wolfSSL_Accept/Connect. * Retransmission Each sent message is encapsulated in a Dtl13RtxRecord and saved on a list. If we receive an ACK for at record, we remove it from the list so it will be not retransmitted further, then we will retransmit the remaining ones. Retransmission is throttled: beside link congestion, this also avoid too many sequence numbers bounded with a record. * ACK For each received record we save the record sequence number, so we can send an ACK if needed. We send an ACK either if explicitly needed by the flight or if we detect a disruption. Co-authored-by: Juliusz Sosinowicz --- doc/dox_comments/header_files/ssl.h | 41 ++ src/dtls13.c | 804 +++++++++++++++++++++++++++- src/internal.c | 86 ++- src/ssl.c | 92 +++- src/tls13.c | 152 +++++- src/wolfio.c | 30 +- wolfssl/internal.h | 75 ++- wolfssl/ssl.h | 2 + 8 files changed, 1241 insertions(+), 41 deletions(-) diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 681f2cee0..a1f498117 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 diff --git a/src/dtls13.c b/src/dtls13.c index acc1a8686..abfe8aa9e 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -139,6 +139,7 @@ typedef struct Dtls13RecordCiphertextHeader { 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) { @@ -320,11 +321,31 @@ static int Dtls13EncryptDecryptRecordNumber(WOLFSSL* ssl, byte* seq, 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) { - (void)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) @@ -505,6 +526,285 @@ static void Dtls13FreeFragmentsBuffer(WOLFSSL* ssl) 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; @@ -551,8 +851,8 @@ static int Dtls13SendFragmentedInternal(WOLFSSL* ssl) ssl->dtls13FragmentsBuffer.buffer + ssl->dtls13FragOffset, fragLength); - ret = Dtls13SendFragment(ssl, output, maxFragment, recordLength, - ssl->dtls13FragHandshakeType, 0, 1); + ret = Dtls13SendOneFragmentRtx(ssl, ssl->dtls13FragHandshakeType, + recordLength + MAX_MSG_EXTRA, output, recordLength, 0); if (ret == WANT_WRITE) { ssl->dtls13FragOffset += fragLength; return ret; @@ -940,7 +1240,112 @@ int Dtls13ParseUnifiedRecordLayer(WOLFSSL* ssl, const byte* input, int Dtls13RecordRecvd(WOLFSSL* ssl) { - (void)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, + 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; } @@ -956,8 +1361,8 @@ int Dtls13RecordRecvd(WOLFSSL* ssl) * * returns 0 on success */ -static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte *input, word32 size, - word32 *processedSize) +static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, + word32* processedSize) { word32 frag_off, frag_length; byte isComplete, isFirst; @@ -980,6 +1385,10 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte *input, word32 size, if (frag_off + frag_length > message_length) return BUFFER_ERROR; + ret = Dtls13RtxMsgRecvd(ssl, handshake_type, frag_off); + if (ret != 0) + return ret; + if (ssl->keys.dtls_peer_handshake_number < ssl->keys.dtls_expected_peer_handshake_number) { @@ -1117,6 +1526,21 @@ int Dtls13HandshakeSend(WOLFSSL* ssl, byte* message, word16 outputSize, /* 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); @@ -1128,9 +1552,8 @@ int Dtls13HandshakeSend(WOLFSSL* ssl, byte* message, word16 outputSize, maxLen = length; if (maxLen < maxFrag) { - ret = Dtls13SendFragment(ssl, message, outputSize, length, - handshakeType, hashOutput, Dtls13SendNow(ssl, handshakeType)); - + ret = Dtls13SendOneFragmentRtx(ssl, handshakeType, outputSize, message, + length, hashOutput); if (ret == 0 || ret == WANT_WRITE) ssl->keys.dtls_handshake_number++; } @@ -1588,4 +2011,365 @@ int Dtls13SetRecordNumberKeys(WOLFSSL* ssl, enum encrypt_side side) 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; +} + +#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); +} + +int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, + word32* processedSize) +{ + const byte* ackMessage; + w64wrapper epoch, seq; + word16 length; + 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; + } + + *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; +} + #endif /* WOLFSSL_DTLS13 */ diff --git a/src/internal.c b/src/internal.c index 3486d0be2..ea5e21ac3 100644 --- a/src/internal.c +++ b/src/internal.c @@ -195,6 +195,11 @@ WOLFSSL_CALLBACKS needs LARGE_STATIC_BUFFERS, please add LARGE_STATIC_BUFFERS 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, @@ -6778,6 +6783,8 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) 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; @@ -7425,6 +7432,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 */ @@ -9175,6 +9185,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 && @@ -9604,15 +9626,30 @@ static int GetDtlsRecordHeader(WOLFSSL* ssl, const byte* input, #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) - return GetDtls13RecordHeader(ssl, input, inOutIdx, rh, size); + 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 @@ -9735,6 +9772,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 */ @@ -9771,6 +9817,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: @@ -17858,6 +17907,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 @@ -18630,6 +18686,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; @@ -21088,6 +21160,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) { diff --git a/src/ssl.c b/src/ssl.c index 25f27d237..ea30642ff 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -11497,6 +11497,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) { @@ -11567,6 +11591,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; @@ -11789,6 +11828,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; @@ -11856,6 +11896,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 @@ -11863,15 +11918,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"); @@ -11942,6 +11991,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; @@ -12095,6 +12165,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) diff --git a/src/tls13.c b/src/tls13.c index e1522f19c..d44b25887 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -6991,12 +6991,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) @@ -9123,6 +9132,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) @@ -9317,7 +9335,8 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl) #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) - advanceState = advanceState && !ssl->dtls13SendingFragments; + advanceState = advanceState && !ssl->dtls13SendingFragments + && !ssl->dtls13SendingAckOrRtx; #endif /* WOLFSSL_DTLS13 */ if (ssl->buffers.outputBuffer.length > 0 @@ -9330,7 +9349,21 @@ int wolfSSL_connect_TLSv13(WOLFSSL* ssl) if ((ssl->error = SendBuffered(ssl)) == 0) { if (ssl->fragOffset == 0 && !ssl->options.buildingMsg) { if (advanceState) { - ssl->options.connectState++; +#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"); } @@ -9342,6 +9375,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; @@ -9402,9 +9441,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) { @@ -9450,9 +9498,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; @@ -9545,6 +9602,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; @@ -10340,8 +10417,8 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl) #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) - advanceState = advanceState && - !ssl->dtls13SendingFragments; + advanceState = advanceState && !ssl->dtls13SendingFragments + && !ssl->dtls13SendingAckOrRtx; #endif /* WOLFSSL_DTLS13 */ if ((ssl->error = SendBuffered(ssl)) == 0) { @@ -10359,6 +10436,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; @@ -10397,6 +10480,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; @@ -10444,6 +10537,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 */ + } } @@ -10578,11 +10681,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"); @@ -10881,8 +10994,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/wolfssl/internal.h b/wolfssl/internal.h index 1da42bae0..d40e5f120 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1633,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, @@ -1654,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 */ @@ -3556,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 */ + }; @@ -3836,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 */ @@ -4367,7 +4381,47 @@ typedef struct Dtls13Epoch { byte side; } Dtls13Epoch; -#define DTLS13_EPOCH_SIZE 3 +#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 */ @@ -4579,9 +4633,13 @@ struct WOLFSSL { /* used to store the message if it needs to be fragmented */ buffer dtls13FragmentsBuffer; byte dtls13SendingFragments:1; + byte dtls13SendingAckOrRtx:1; + byte dtls13FastTimeout:1; word32 dtls13MessageLength; word32 dtls13FragOffset; byte dtls13FragHandshakeType; + Dtls13Rtx dtls13Rtx; + #endif /* WOLFSSL_DTLS13 */ #endif /* WOLFSSL_DTLS */ #ifdef WOLFSSL_CALLBACKS @@ -5338,6 +5396,7 @@ 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); @@ -5365,13 +5424,19 @@ 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 diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 1d02e0f87..d419339fa 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1310,6 +1310,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, From dfc9873c0fe2088631571778a5218793b3fce2a4 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 10:00:06 +0200 Subject: [PATCH 11/19] dtls13: support KeyUpdate messages --- src/dtls13.c | 48 ++++++++++++++++++++++++++ src/tls13.c | 86 +++++++++++++++++++++++++++++++++++++++++----- wolfssl/internal.h | 1 + 3 files changed, 127 insertions(+), 8 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index abfe8aa9e..2e7947481 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -1551,6 +1551,9 @@ int Dtls13HandshakeSend(WOLFSSL* ssl, byte* message, word16 outputSize, 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); @@ -2106,6 +2109,26 @@ static int Dtls13RtxIsTrackedByRn(const Dtls13RtxRecord* r, w64wrapper epoch, 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) { @@ -2200,12 +2223,27 @@ int Dtls13RtxTimeout(WOLFSSL* ssl) 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) @@ -2234,6 +2272,16 @@ int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, 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 diff --git a/src/tls13.c b/src/tls13.c index d44b25887..e2732cd5e 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -7896,6 +7896,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) @@ -7906,6 +7911,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: @@ -7918,6 +7928,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); @@ -7935,15 +7954,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); @@ -8001,8 +8031,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); @@ -9029,7 +9088,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; @@ -9894,6 +9953,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; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index d40e5f120..8103dbc04 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4635,6 +4635,7 @@ struct WOLFSSL { byte dtls13SendingFragments:1; byte dtls13SendingAckOrRtx:1; byte dtls13FastTimeout:1; + byte dtls13WaitKeyUpdateAck:1; word32 dtls13MessageLength; word32 dtls13FragOffset; byte dtls13FragHandshakeType; From e2abdf23a75873eab3d9c37dfb85d26759ef89e2 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 10:00:18 +0200 Subject: [PATCH 12/19] internal: return from wolfSSL_Peek() with sz 0 if we don't have data This way we can use wolfSSL_Peek() invoked with sz == 0 to process pending records and, if none of this records is an application data record, we will not block. --- src/internal.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/internal.c b/src/internal.c index ea5e21ac3..cc0451c04 100644 --- a/src/internal.c +++ b/src/internal.c @@ -21193,6 +21193,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 && From ca05ad2dc033b83098776c2c29b3a16d85a5a01f Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 10:00:23 +0200 Subject: [PATCH 13/19] dtls13: introduce wolfSSL_dtls_13_has_pending_msg() API --- doc/dox_comments/header_files/ssl.h | 9 +++++++++ src/dtls13.c | 5 +++++ wolfssl/ssl.h | 1 + 3 files changed, 15 insertions(+) diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index a1f498117..203fd15c2 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -14085,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/src/dtls13.c b/src/dtls13.c index 2e7947481..a08812826 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -2420,4 +2420,9 @@ int Dtls13RtxProcessingCertificate(WOLFSSL* ssl, byte* input, word32 inputSize) return 0; } +int wolfSSL_dtls13_has_pending_msg(WOLFSSL* ssl) +{ + return ssl->dtls13Rtx.rtxRecords != NULL; +} + #endif /* WOLFSSL_DTLS13 */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index d419339fa..829fc4745 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -869,6 +869,7 @@ WOLFSSL_API WOLFSSL_METHOD *wolfSSLv23_client_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 From c1dc90d9b0096ced4b1af07b81d1ad62e6a822b2 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 10:00:28 +0200 Subject: [PATCH 14/19] server: request cert only once if doing post-handshake auth --- examples/server/server.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/server/server.c b/examples/server/server.c index 3e92e9d9d..9c7bcd155 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -2789,10 +2789,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 +3333,19 @@ 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) { From 4e112419f533889d6cc9720695381f6a6121cc74 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 10:00:24 +0200 Subject: [PATCH 15/19] examples: client/server: support DTLSv1.3 (-u -v4) This commits add some new options to examples/[server,client] to support testing of DTLS v1.3. client: add waitTicket option If this option is used, the client will wait until it receives a sessionTicket from the server. This is useful when testing DTLS retransmission. client: add waitKeyUpdate option When this option is set, the client waits until the UpdateKey message is acknowledged by the server. This is useful to test DTLS retransmission logic --- examples/client/client.c | 65 ++++++++++++++++++++++++++++++++++++-- examples/server/server.c | 67 +++++++++++++++++++++++++++++++++++++--- wolfssl/test.h | 65 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+), 7 deletions(-) diff --git a/examples/client/client.c b/examples/client/client.c index bfd152c1c..7dd11403a 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) @@ -1868,6 +1872,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 +2004,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 +2155,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 +2771,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 +2864,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 +3486,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 +4065,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/server/server.c b/examples/server/server.c index 9c7bcd155..23973ee38 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -546,6 +546,11 @@ 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); @@ -2160,13 +2165,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 +2239,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 +2269,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 +2319,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); } @@ -3343,6 +3384,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT), 0); wolfSSL_request_certificate(ssl); + } #endif @@ -3395,6 +3437,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/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) From 12a3efeca807795ba54151b6513e3faf768005f1 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 10:00:37 +0200 Subject: [PATCH 16/19] client/server: tolerate WANT_WRITE errors --- examples/client/client.c | 7 +++++-- examples/server/server.c | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/client/client.c b/examples/client/client.c index 7dd11403a..faa97955f 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -807,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"); } @@ -1072,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"); diff --git a/examples/server/server.c b/examples/server/server.c index 23973ee38..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); @@ -553,7 +554,9 @@ static void ServerRead(WOLFSSL* ssl, char* input, int inputLen) 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'; From 25cf98a4171c220bf149e413c2aec52164648326 Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 10:00:40 +0200 Subject: [PATCH 17/19] test: add DTLSv1.3 test suites --- tests/include.am | 3 + tests/suites.c | 36 +++++ tests/test-dtls13-downgrade.conf | 11 ++ tests/test-dtls13-psk.conf | 54 +++++++ tests/test-dtls13.conf | 262 +++++++++++++++++++++++++++++++ 5 files changed, 366 insertions(+) create mode 100644 tests/test-dtls13-downgrade.conf create mode 100644 tests/test-dtls13-psk.conf create mode 100644 tests/test-dtls13.conf diff --git a/tests/include.am b/tests/include.am index 555ca90f6..c4b6b7af4 100644 --- a/tests/include.am +++ b/tests/include.am @@ -40,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 From 3a9176bcc975e7d9e8871621d8ff60389d184b7b Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Fri, 20 May 2022 10:00:42 +0200 Subject: [PATCH 18/19] scripts: test for dtls13 retransmission Co-authored-by: Juliusz Sosinowicz --- scripts/dtls13.test | 168 ++++++++++++++++++++++++++++++++++++++++++++ scripts/include.am | 4 ++ 2 files changed, 172 insertions(+) create mode 100755 scripts/dtls13.test 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 From aa8df1af789280caa32ab4076f9a77834649ea78 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 15 Jun 2022 10:12:51 -0700 Subject: [PATCH 19/19] Fixes for building without DTLS v1.2 and TLS v1.2. Fixes for explicit cast warnings. --- examples/benchmark/tls_bench.c | 20 ++++++++++++++++---- examples/echoclient/echoclient.c | 4 ++++ examples/echoserver/echoserver.c | 4 ++++ src/dtls13.c | 19 +++++++++++-------- src/internal.c | 32 +++++++++++++++++++++++++++++--- src/tls.c | 4 +++- src/tls13.c | 6 +++--- 7 files changed, 70 insertions(+), 19 deletions(-) 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/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/src/dtls13.c b/src/dtls13.c index a08812826..2f30c98db 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -372,7 +372,7 @@ static int Dtls13ProcessBufferedMessages(WOLFSSL* ssl) if (ret != 0) break; - Dtls13MsgWasProcessed(ssl, msg->type); + Dtls13MsgWasProcessed(ssl, (enum HandShakeType)msg->type); ssl->dtls_rx_msg_list = msg->next; DtlsMsgDelete(msg, ssl->heap); @@ -814,7 +814,8 @@ static int Dtls13SendFragmentedInternal(WOLFSSL* ssl) byte* output; int ret; - isEncrypted = Dtls13TypeIsEncrypted(ssl->dtls13FragHandshakeType); + isEncrypted = Dtls13TypeIsEncrypted( + (enum HandShakeType)ssl->dtls13FragHandshakeType); rlHeaderLength = Dtls13GetRlHeaderLength(isEncrypted); maxFragment = wolfSSL_GetMaxFragSize(ssl, MAX_RECORD_SIZE); @@ -840,8 +841,8 @@ static int Dtls13SendFragmentedInternal(WOLFSSL* ssl) ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.length; ret = Dtls13HandshakeAddHeaderFrag(ssl, output + rlHeaderLength, - ssl->dtls13FragHandshakeType, ssl->dtls13FragOffset, fragLength, - ssl->dtls13MessageLength); + (enum HandShakeType)ssl->dtls13FragHandshakeType, + ssl->dtls13FragOffset, fragLength, ssl->dtls13MessageLength); if (ret != 0) { Dtls13FreeFragmentsBuffer(ssl); return ret; @@ -851,7 +852,8 @@ static int Dtls13SendFragmentedInternal(WOLFSSL* ssl) ssl->dtls13FragmentsBuffer.buffer + ssl->dtls13FragOffset, fragLength); - ret = Dtls13SendOneFragmentRtx(ssl, ssl->dtls13FragHandshakeType, + ret = Dtls13SendOneFragmentRtx(ssl, + (enum HandShakeType)ssl->dtls13FragHandshakeType, recordLength + MAX_MSG_EXTRA, output, recordLength, 0); if (ret == WANT_WRITE) { ssl->dtls13FragOffset += fragLength; @@ -1321,7 +1323,8 @@ static int Dtls13RtxSendBuffered(WOLFSSL* ssl) seq = ssl->dtls13EncryptEpoch->nextSeqNumber; ret = Dtls13SendFragment(ssl, output, sendSz, r->length + headerLength, - r->handshakeType, 0, isLast || !ssl->options.groupMessages); + (enum HandShakeType)r->handshakeType, 0, + isLast || !ssl->options.groupMessages); if (ret != 0 && ret != WANT_WRITE) return ret; @@ -1385,7 +1388,7 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, if (frag_off + frag_length > message_length) return BUFFER_ERROR; - ret = Dtls13RtxMsgRecvd(ssl, handshake_type, frag_off); + ret = Dtls13RtxMsgRecvd(ssl, (enum HandShakeType)handshake_type, frag_off); if (ret != 0) return ret; @@ -1430,7 +1433,7 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, if (ret != 0) return ret; - Dtls13MsgWasProcessed(ssl, handshake_type); + Dtls13MsgWasProcessed(ssl, (enum HandShakeType)handshake_type); *processedSize = idx; diff --git a/src/internal.c b/src/internal.c index cc0451c04..17aa0a964 100644 --- a/src/internal.c +++ b/src/internal.c @@ -541,6 +541,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. */ @@ -552,6 +553,7 @@ static WC_INLINE int IsDtlsNotSrtpMode(WOLFSSL* ssl) return ssl->options.dtls; #endif } +#endif /* !WOLFSSL_NO_TLS12 */ #endif /* WOLFSSL_DTLS */ @@ -2907,6 +2909,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; @@ -15382,8 +15391,15 @@ 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 @@ -15520,8 +15536,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 } } } @@ -15623,7 +15644,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) { @@ -15636,7 +15662,7 @@ static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx, WOLFSSL_LEAVE("DoDtlsHandShakeMsg()", ret); return ret; } -#endif +#endif /* WOLFSSL_DTLS13 */ #ifndef WOLFSSL_NO_TLS12 diff --git a/src/tls.c b/src/tls.c index 346d731b9..3f3ead852 100644 --- a/src/tls.c +++ b/src/tls.c @@ -12510,7 +12510,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()); diff --git a/src/tls13.c b/src/tls13.c index e2732cd5e..1f095c400 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -1653,7 +1653,7 @@ static void AddTls13HandShakeHeader(byte* output, word32 length, when computing the hash transcript, we can avoid to use the DTLS handshake header. */ if (ssl->options.dtls && type != message_hash) { - Dtls13HandshakeAddHeader(ssl, output, type, length); + Dtls13HandshakeAddHeader(ssl, output, (enum HandShakeType)type, length); return; } #endif /* WOLFSSL_DTLS13 */ @@ -1680,7 +1680,7 @@ static void AddTls13Headers(byte* output, word32 length, byte type, #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { - Dtls13AddHeaders(output, length, type, ssl); + Dtls13AddHeaders(output, length, (enum HandShakeType)type, ssl); return; } #endif /* WOLFSSL_DTLS13 */ @@ -1711,7 +1711,7 @@ static void AddTls13FragHeaders(byte* output, word32 fragSz, word32 fragOffset, /* we ignore fragmentation fields here because fragmentation logic for DTLS1.3 is inside dtls13_handshake_send(). */ if (ssl->options.dtls) { - Dtls13AddHeaders(output, length, type, ssl); + Dtls13AddHeaders(output, length, (enum HandShakeType)type, ssl); return; } #endif /* WOLFSSL_DTLS13 */