diff --git a/examples/client/client.c b/examples/client/client.c index 51298e37e..3c010a869 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -1027,6 +1027,10 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) break; case 'v' : + if (myoptarg[0] == 'd') { + version = CLIENT_DOWNGRADE_VERSION; + break; + } version = atoi(myoptarg); if (version < 0 || version > 4) { Usage(); @@ -1414,6 +1418,10 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) method = wolfTLSv1_3_client_method_ex; break; #endif + + case CLIENT_DOWNGRADE_VERSION: + method = wolfSSLv23_client_method_ex; + break; #endif /* NO_TLS */ #ifdef WOLFSSL_DTLS diff --git a/examples/server/server.c b/examples/server/server.c index 058e4dc32..8f20ab8cb 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -643,6 +643,10 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) break; case 'v' : + if (myoptarg[0] == 'd') { + version = SERVER_DOWNGRADE_VERSION; + break; + } version = atoi(myoptarg); if (version < 0 || version > 4) { Usage(); @@ -893,6 +897,10 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) method = wolfTLSv1_3_server_method_ex; break; #endif + + case SERVER_DOWNGRADE_VERSION: + method = wolfSSLv23_server_method_ex; + break; #endif /* NO_TLS */ #ifdef CYASSL_DTLS diff --git a/scripts/tls13.test b/scripts/tls13.test index a04f29835..719515ac0 100755 --- a/scripts/tls13.test +++ b/scripts/tls13.test @@ -373,6 +373,22 @@ if [ $RESULT -eq 0 ]; then fi echo "" +# TLS Downgrade server / TLS 1.2 client. +echo -e "\n\nTLS server downgrading to TLS v1.2" +port=0 +./examples/server/server -v d -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -v 3 -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -ne 0 ]; then + echo -e "\n\nIssue with TLS server downgrading to TLS v1.2" + do_cleanup + exit 1 +fi +echo "" + # TLS 1.2 server / TLS 1.3 client. echo -e "\n\nTLS v1.3 client upgrading server to TLS v1.3" port=0 @@ -389,6 +405,22 @@ if [ $RESULT -eq 0 ]; then fi echo "" +# TLS 1.2 server / TLS downgrade client. +echo -e "\n\nTLS client downgrading to TLS v1.2" +port=0 +./examples/server/server -v 3 -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -v d -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -ne 0 ]; then + echo -e "\n\nIssue with TLS client downgrading to TLS v1.2" + do_cleanup + exit 1 +fi +echo "" + # TLS 1.3 server / TLS 1.3 client send KeyUpdate before sending app data. echo -e "\n\nTLS v1.3 KeyUpdate" port=0 diff --git a/src/internal.c b/src/internal.c index 783541795..c365168dd 100644 --- a/src/internal.c +++ b/src/internal.c @@ -17431,6 +17431,13 @@ void PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, } #endif /* HAVE_SECRET_CALLBACK */ + return CompleteServerHello(ssl); + } + + int CompleteServerHello(WOLFSSL* ssl) + { + int ret; + if (ssl->options.resuming) { if (DSH_CheckSessionId(ssl)) { if (SetCipherSpecs(ssl) == 0) { diff --git a/src/tls.c b/src/tls.c index 814099f9e..c6bf6cdc2 100644 --- a/src/tls.c +++ b/src/tls.c @@ -4679,6 +4679,13 @@ static int TLSX_SupportedVersions_Parse(WOLFSSL *ssl, byte* input, if (major != pv.major) 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 && + minor >= TLSv1_3_MINOR) { + /* Set minor version back to TLS v1.3+ */ + ssl->version.minor = ssl->ctx->method->version.minor; + } + /* No upgrade allowed. */ if (ssl->version.minor < minor) return VERSION_ERROR; @@ -8062,7 +8069,7 @@ int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isServer) } #if defined(HAVE_SESSION_TICKET) - if (ssl->options.resuming) { + if (ssl->options.resuming && ssl->session.ticketLen > 0) { WOLFSSL_SESSION* sess = &ssl->session; word32 milli; diff --git a/src/tls13.c b/src/tls13.c index d0856efb0..0b80cd5f9 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -2605,6 +2605,7 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, int ret; #ifndef WOLFSSL_TLS13_DRAFT_18 byte sessIdSz; + const byte* sessId; byte b; #endif word16 totalExtSz; @@ -2668,35 +2669,18 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, sessIdSz = input[i++]; if ((i - begin) + sessIdSz > helloSz) return BUFFER_ERROR; - #ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT - if (sessIdSz == 0) - return INVALID_PARAMETER; - if (ssl->session.sessionIDSz != 0) { - if (ssl->session.sessionIDSz != sessIdSz || - XMEMCMP(ssl->session.sessionID, input + i, sessIdSz) != 0) { - return INVALID_PARAMETER; - } - } - else if (XMEMCMP(ssl->arrays->clientRandom, input + i, sessIdSz) != 0) - return INVALID_PARAMETER; - #else - if (sessIdSz != ssl->session.sessionIDSz || (sessIdSz > 0 && - XMEMCMP(ssl->session.sessionID, input + i, sessIdSz) != 0)) { - WOLFSSL_MSG("Server sent different session id"); - return INVALID_PARAMETER; - } - #endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */ + sessId = input + i; i += sessIdSz; - ssl->options.haveSessionId = 1; #endif /* WOLFSSL_TLS13_DRAFT_18 */ + ssl->options.haveSessionId = 1; #ifdef WOLFSSL_TLS13_DRAFT_18 - /* Ciphersuite and extensions length check */ + /* Ciphersuite check */ if ((i - begin) + OPAQUE16_LEN + OPAQUE16_LEN > helloSz) return BUFFER_ERROR; #else - /* Ciphersuite, compression and extensions length check */ - if ((i - begin) + OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE16_LEN > helloSz) + /* Ciphersuite and compression check */ + if ((i - begin) + OPAQUE16_LEN + OPAQUE8_LEN > helloSz) return BUFFER_ERROR; #endif @@ -2713,18 +2697,33 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } #endif - /* Get extension length and length check. */ - ato16(&input[i], &totalExtSz); - i += OPAQUE16_LEN; - if ((i - begin) + totalExtSz > helloSz) - return BUFFER_ERROR; +#ifndef WOLFSSL_TLS13_DRAFT_18 + if ((i - begin) + OPAQUE16_LEN > helloSz) { + if (!ssl->options.downgrade) + return BUFFER_ERROR; + ssl->version.minor = TLSv1_2_MINOR; + ssl->options.haveEMS = 0; + } + if ((i - begin) < helloSz) +#endif + { + /* Get extension length and length check. */ + ato16(&input[i], &totalExtSz); + i += OPAQUE16_LEN; + if ((i - begin) + totalExtSz > helloSz) + return BUFFER_ERROR; - /* Parse and handle extensions. */ - ret = TLSX_Parse(ssl, (byte *) input + i, totalExtSz, extMsgType, NULL); - if (ret != 0) - return ret; +#ifndef WOLFSSL_TLS13_DRAFT_18 + if (ssl->options.downgrade) + ssl->version.minor = TLSv1_2_MINOR; +#endif + /* Parse and handle extensions. */ + ret = TLSX_Parse(ssl, (byte *) input + i, totalExtSz, extMsgType, NULL); + if (ret != 0) + return ret; - i += totalExtSz; + i += totalExtSz; + } *inOutIdx = i; ssl->options.serverState = SERVER_HELLO_COMPLETE; @@ -2739,6 +2738,51 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } #endif /* HAVE_SECRET_CALLBACK */ +#ifndef WOLFSSL_TLS13_DRAFT_18 + /* Version only negotiated in extensions for TLS v1.3. + * Only now do we know how to deal with session id. + */ + if (!IsAtLeastTLSv1_3(ssl->version)) { + ssl->arrays->sessionIDSz = sessIdSz; + + if (ssl->arrays->sessionIDSz > ID_LEN) { + WOLFSSL_MSG("Invalid session ID size"); + ssl->arrays->sessionIDSz = 0; + return BUFFER_ERROR; + } + else if (ssl->arrays->sessionIDSz) { + XMEMCPY(ssl->arrays->sessionID, sessId, ssl->arrays->sessionIDSz); + ssl->options.haveSessionId = 1; + } + + /* Complete TLS v1.2 processing of ServerHello. */ + ret = CompleteServerHello(ssl); + + WOLFSSL_LEAVE("DoTls13ServerHello", ret); + + return ret; + } + + #ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT + if (sessIdSz == 0) + return INVALID_PARAMETER; + if (ssl->session.sessionIDSz != 0) { + if (ssl->session.sessionIDSz != sessIdSz || + XMEMCMP(ssl->session.sessionID, sessId, sessIdSz) != 0) { + return INVALID_PARAMETER; + } + } + else if (XMEMCMP(ssl->arrays->clientRandom, sessId, sessIdSz) != 0) + return INVALID_PARAMETER; + #else + if (sessIdSz != ssl->session.sessionIDSz || (sessIdSz > 0 && + XMEMCMP(ssl->session.sessionID, sessId, sessIdSz) != 0)) { + WOLFSSL_MSG("Server sent different session id"); + return INVALID_PARAMETER; + } + #endif /* WOLFSSL_TLS13_MIDDLEBOX_COMPAT */ +#endif + ret = SetCipherSpecs(ssl); if (ret != 0) return ret; @@ -3173,6 +3217,9 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz, break; } + if (current == NULL) + return 0; + /* Hash the rest of the ClientHello. */ ret = HashInputRaw(ssl, input + helloSz - bindersLen, bindersLen); if (ret != 0) @@ -3567,10 +3614,11 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, return ret; } -#ifdef HAVE_STUNNEL +#if defined(HAVE_STUNNEL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) if ((ret = SNI_Callback(ssl)) != 0) return ret; -#endif /*HAVE_STUNNEL*/ + ssl->options.side = WOLFSSL_SERVER_END; +#endif /* HAVE_STUNNELi || WOLFSSL_NGINX || WOLFSSL_HAPROXY */ if (TLSX_Find(ssl->extensions, TLSX_SUPPORTED_VERSIONS) == NULL) { if (!ssl->options.downgrade) { diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 753330925..c057557ba 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1481,6 +1481,7 @@ WOLFSSL_LOCAL int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, #endif WOLFSSL_LOCAL int DoServerHello(WOLFSSL* ssl, const byte* input, word32*, word32); +WOLFSSL_LOCAL int CompleteServerHello(WOLFSSL *ssl); WOLFSSL_LOCAL int CheckVersion(WOLFSSL *ssl, ProtocolVersion pv); WOLFSSL_LOCAL void PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz); diff --git a/wolfssl/test.h b/wolfssl/test.h index fff6c1b7b..b4f193cb7 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -234,9 +234,11 @@ #define SERVER_DEFAULT_VERSION 3 #define SERVER_DTLS_DEFAULT_VERSION (-2) #define SERVER_INVALID_VERSION (-99) +#define SERVER_DOWNGRADE_VERSION (-98) #define CLIENT_DEFAULT_VERSION 3 #define CLIENT_DTLS_DEFAULT_VERSION (-2) #define CLIENT_INVALID_VERSION (-99) +#define CLIENT_DOWNGRADE_VERSION (-98) #if !defined(NO_FILESYSTEM) && defined(WOLFSSL_MAX_STRENGTH) #define DEFAULT_MIN_DHKEY_BITS 2048 #else