diff --git a/src/dtls.c b/src/dtls.c index 1f184d0b2..986869749 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -82,6 +82,23 @@ void DtlsResetState(WOLFSSL* ssl) ssl->options.tls1_3 = 0; } +int DtlsIgnoreError(int err) +{ + /* Whitelist of errors not to ignore */ + switch (err) { + case MEMORY_E: + case MEMORY_ERROR: + case ASYNC_INIT_E: + case ASYNC_OP_E: + case SOCKET_ERROR_E: + case WANT_READ: + case WANT_WRITE: + return 0; + default: + return 1; + } +} + #if !defined(NO_WOLFSSL_SERVER) #if defined(NO_SHA) && defined(NO_SHA256) @@ -482,237 +499,235 @@ static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch, PskInfo* pskInfo) { int ret = -1; + TLSX* parsedExts = NULL; + WolfSSL_ConstVector tlsx; + Suites suites; + word16 len; + byte haveSA = 0; + byte haveKS = 0; +#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) + byte usePSK = 0; + byte doKE = 0; +#endif + CipherSuite cs; + CipherSpecs specs; + byte cookieHash[WC_MAX_DIGEST_SIZE]; + int cookieHashSz; (void)pskInfo; - if (ch->cookieExt.size == 0) { - TLSX* parsedExts = NULL; - WolfSSL_ConstVector tlsx; - Suites suites; - word16 len; -#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - byte haveKS = 0; - byte usePSK = 0; - byte doKE = 0; -#endif - CipherSuite cs; - CipherSpecs specs; - byte cookieHash[WC_MAX_DIGEST_SIZE]; - int cookieHashSz; + XMEMSET(&cs, 0, sizeof(cs)); - XMEMSET(&cs, 0, sizeof(cs)); + /* We need to echo the session ID sent by the client */ + if (ch->sessionId.size > ID_LEN) { + /* Too large. We can't echo this. */ + ERROR_OUT(INVALID_PARAMETER, dtls13_cleanup); + } - /* We need to echo the session ID sent by the client */ - if (ch->sessionId.size > ID_LEN) { - /* Too large. We can't echo this. */ - ERROR_OUT(INVALID_PARAMETER, dtls13_cleanup); - } + /* Populate the suites struct to find a common ciphersuite */ + XMEMSET(&suites, 0, sizeof(suites)); + suites.suiteSz = (word16)ch->cipherSuite.size; + if ((suites.suiteSz % 2) != 0) + ERROR_OUT(INVALID_PARAMETER, dtls13_cleanup); + if (suites.suiteSz > WOLFSSL_MAX_SUITE_SZ) + ERROR_OUT(BUFFER_ERROR, dtls13_cleanup); + XMEMCPY(suites.suites, ch->cipherSuite.elements, suites.suiteSz); - /* Populate the suites struct to find a common ciphersuite */ - XMEMSET(&suites, 0, sizeof(suites)); - suites.suiteSz = (word16)ch->cipherSuite.size; - if ((suites.suiteSz % 2) != 0) - ERROR_OUT(INVALID_PARAMETER, dtls13_cleanup); - if (suites.suiteSz > WOLFSSL_MAX_SUITE_SZ) + /* Populate extensions */ + + /* Supported versions always need to be present. Has to appear after + * key share as that is the order we reconstruct it in + * RestartHandshakeHashWithCookie. */ + ret = TLSX_Push(&parsedExts, + TLSX_SUPPORTED_VERSIONS, ssl, ssl->heap); + if (ret != 0) + goto dtls13_cleanup; + /* Set that this is a response extension */ + parsedExts->resp = 1; + + ret = CopyExtensions(ssl->extensions, &parsedExts, ssl->heap); + if (ret != 0) + goto dtls13_cleanup; + + /* Signature algs */ + ret = TlsxFindByType(&tlsx, TLSX_SIGNATURE_ALGORITHMS, + ch->extension); + if (ret != 0) + goto dtls13_cleanup; + if (tlsx.size > OPAQUE16_LEN) { + ato16(tlsx.elements, &len); + if (len != tlsx.size - OPAQUE16_LEN) ERROR_OUT(BUFFER_ERROR, dtls13_cleanup); - XMEMCPY(suites.suites, ch->cipherSuite.elements, suites.suiteSz); + if ((len % 2) != 0) + ERROR_OUT(BUFFER_ERROR, dtls13_cleanup); + suites.hashSigAlgoSz = len; + XMEMCPY(suites.hashSigAlgo, tlsx.elements + OPAQUE16_LEN, + len); + haveSA = 1; + } - /* Populate extensions */ - - /* Supported versions always need to be present. Has to appear after - * key share as that is the order we reconstruct it in - * RestartHandshakeHashWithCookie. */ - ret = TLSX_Push(&parsedExts, - TLSX_SUPPORTED_VERSIONS, ssl, ssl->heap); + /* Supported groups */ + ret = TlsxFindByType(&tlsx, TLSX_SUPPORTED_GROUPS, + ch->extension); + if (ret != 0) + goto dtls13_cleanup; + if (tlsx.size != 0) { + ret = TLSX_SupportedCurve_Parse(ssl, tlsx.elements, + (word16)tlsx.size, 1, &parsedExts); if (ret != 0) goto dtls13_cleanup; - /* Set that this is a response extension */ - parsedExts->resp = 1; + } - ret = CopyExtensions(ssl->extensions, &parsedExts, ssl->heap); + /* Key share */ + ret = TlsxFindByType(&tlsx, TLSX_KEY_SHARE, + ch->extension); + if (ret != 0) + goto dtls13_cleanup; + if (tlsx.size != 0) { + ret = TLSX_KeyShare_Parse_ClientHello(ssl, tlsx.elements, + (word16)tlsx.size, &parsedExts); if (ret != 0) goto dtls13_cleanup; - - /* Signature algs */ - ret = TlsxFindByType(&tlsx, TLSX_SIGNATURE_ALGORITHMS, - ch->extension); - if (ret != 0) - goto dtls13_cleanup; - if (tlsx.size > OPAQUE16_LEN) { - ato16(tlsx.elements, &len); - if (len != tlsx.size - OPAQUE16_LEN) - ERROR_OUT(BUFFER_ERROR, dtls13_cleanup); - if ((len % 2) != 0) - ERROR_OUT(BUFFER_ERROR, dtls13_cleanup); - suites.hashSigAlgoSz = len; - XMEMCPY(suites.hashSigAlgo, tlsx.elements + OPAQUE16_LEN, - len); - } - - /* Supported groups */ - ret = TlsxFindByType(&tlsx, TLSX_SUPPORTED_GROUPS, - ch->extension); - if (ret != 0) - goto dtls13_cleanup; - if (tlsx.size != 0) { - ret = TLSX_SupportedCurve_Parse(ssl, tlsx.elements, - (word16)tlsx.size, 1, &parsedExts); - if (ret != 0) - goto dtls13_cleanup; - } - - /* Key share */ - ret = TlsxFindByType(&tlsx, TLSX_KEY_SHARE, - ch->extension); - if (ret != 0) - goto dtls13_cleanup; - if (tlsx.size != 0) { - ret = TLSX_KeyShare_Parse_ClientHello(ssl, tlsx.elements, - (word16)tlsx.size, &parsedExts); - if (ret != 0) - goto dtls13_cleanup; -#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - haveKS = 1; -#endif - } + haveKS = 1; + } #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - /* Pre-shared key */ - ret = TlsxFindByType(&tlsx, TLSX_PRE_SHARED_KEY, ch->extension); - if (ret != 0) - goto dtls13_cleanup; - if (tlsx.size != 0) { - /* Let's just assume that the binders are correct here. We will - * actually verify this in the stateful part of the processing - * and if they don't match we will error out there anyway. */ - byte modes; + /* Pre-shared key */ + ret = TlsxFindByType(&tlsx, TLSX_PRE_SHARED_KEY, ch->extension); + if (ret != 0) + goto dtls13_cleanup; + if (tlsx.size != 0) { + /* Let's just assume that the binders are correct here. We will + * actually verify this in the stateful part of the processing + * and if they don't match we will error out there anyway. */ + byte modes; #ifndef NO_PSK - /* When we didn't find a valid ticket ask the user for the - * ciphersuite matching this identity */ - if (!pskInfo->isValid) { - if (TLSX_PreSharedKey_Parse_ClientHello(&parsedExts, - tlsx.elements, tlsx.size, ssl->heap) == 0) - FindPskSuiteFromExt(ssl, parsedExts, pskInfo, &suites); - /* Revert to full handshake if PSK parsing failed */ - } + /* When we didn't find a valid ticket ask the user for the + * ciphersuite matching this identity */ + if (!pskInfo->isValid) { + if (TLSX_PreSharedKey_Parse_ClientHello(&parsedExts, + tlsx.elements, tlsx.size, ssl->heap) == 0) + FindPskSuiteFromExt(ssl, parsedExts, pskInfo, &suites); + /* Revert to full handshake if PSK parsing failed */ + } #endif - if (pskInfo->isValid) { - ret = TlsxFindByType(&tlsx, TLSX_PSK_KEY_EXCHANGE_MODES, - ch->extension); - if (ret != 0) - goto dtls13_cleanup; - if (tlsx.size == 0) - ERROR_OUT(MISSING_HANDSHAKE_DATA, dtls13_cleanup); - ret = TLSX_PskKeyModes_Parse_Modes(tlsx.elements, tlsx.size, - client_hello, &modes); - if (ret != 0) - goto dtls13_cleanup; - if ((modes & (1 << PSK_DHE_KE)) && - !ssl->options.noPskDheKe) { - if (!haveKS) - ERROR_OUT(MISSING_HANDSHAKE_DATA, dtls13_cleanup); - doKE = 1; - } - else if ((modes & (1 << PSK_KE)) == 0) { - ERROR_OUT(PSK_KEY_ERROR, dtls13_cleanup); - } - usePSK = 1; + if (pskInfo->isValid) { + ret = TlsxFindByType(&tlsx, TLSX_PSK_KEY_EXCHANGE_MODES, + ch->extension); + if (ret != 0) + goto dtls13_cleanup; + if (tlsx.size == 0) + ERROR_OUT(PSK_KEY_ERROR, dtls13_cleanup); + ret = TLSX_PskKeyModes_Parse_Modes(tlsx.elements, tlsx.size, + client_hello, &modes); + if (ret != 0) + goto dtls13_cleanup; + if ((modes & (1 << PSK_DHE_KE)) && + !ssl->options.noPskDheKe) { + if (!haveKS) + ERROR_OUT(PSK_KEY_ERROR, dtls13_cleanup); + doKE = 1; } + else if ((modes & (1 << PSK_KE)) == 0) { + ERROR_OUT(PSK_KEY_ERROR, dtls13_cleanup); + } + usePSK = 1; } + } #endif #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - if (usePSK && pskInfo->isValid) { - cs.cipherSuite0 = pskInfo->cipherSuite0; - cs.cipherSuite = pskInfo->cipherSuite; + if (usePSK && pskInfo->isValid) { + cs.cipherSuite0 = pskInfo->cipherSuite0; + cs.cipherSuite = pskInfo->cipherSuite; - if (doKE) { - byte searched = 0; - ret = TLSX_KeyShare_Choose(ssl, parsedExts, &cs.clientKSE, - &searched); - if (ret != 0) - goto dtls13_cleanup; - if (cs.clientKSE == NULL && searched) - cs.doHelloRetry = 1; - } - } - else -#endif - { - ret = MatchSuite_ex(ssl, &suites, &cs, parsedExts); - if (ret < 0) { - WOLFSSL_MSG("Unsupported cipher suite, ClientHello"); - SendAlert((WOLFSSL*)ssl, alert_fatal, handshake_failure); - goto dtls13_cleanup; - } - } - if (cs.doHelloRetry) { - ret = TLSX_KeyShare_SetSupported(ssl, &parsedExts); + if (doKE) { + byte searched = 0; + ret = TLSX_KeyShare_Choose(ssl, parsedExts, &cs.clientKSE, + &searched); if (ret != 0) goto dtls13_cleanup; + if (cs.clientKSE == NULL && searched) + cs.doHelloRetry = 1; } - else { - /* Need to remove the keyshare ext if we found a common group - * and are not doing curve negotiation. */ - TLSX_Remove(&parsedExts, TLSX_KEY_SHARE, ssl->heap); - } - - /* This is required to correctly generate the hash */ - ret = SetCipherSpecs_ex(WOLFSSL_SERVER_END, cs.cipherSuite0, - cs.cipherSuite, &specs, NULL); - if (ret != 0) - goto dtls13_cleanup; - - /* Calculate the cookie hash */ - ret = Dtls13HashClientHello(ssl, cookieHash, &cookieHashSz, ch->raw, - ch->length, &specs); - if (ret != 0) - goto dtls13_cleanup; - - /* Push the cookie to extensions */ - ret = CreateCookieExt(ssl, cookieHash, (word16)cookieHashSz, - &parsedExts, cs.cipherSuite0, cs.cipherSuite); - if (ret != 0) - goto dtls13_cleanup; - - { - WOLFSSL* nonConstSSL = (WOLFSSL*)ssl; - TLSX* sslExts = nonConstSSL->extensions; - - if (ret != 0) - goto dtls13_cleanup; - nonConstSSL->options.tls = 1; - nonConstSSL->options.tls1_1 = 1; - nonConstSSL->options.tls1_3 = 1; - - XMEMCPY(nonConstSSL->session->sessionID, ch->sessionId.elements, - ch->sessionId.size); - nonConstSSL->session->sessionIDSz = (byte)ch->sessionId.size; - nonConstSSL->options.cipherSuite0 = cs.cipherSuite0; - nonConstSSL->options.cipherSuite = cs.cipherSuite; - nonConstSSL->extensions = parsedExts; - - ret = SendTls13ServerHello(nonConstSSL, hello_retry_request); - - /* Can be modified inside SendTls13ServerHello */ - parsedExts = nonConstSSL->extensions; - - nonConstSSL->session->sessionIDSz = 0; - nonConstSSL->options.cipherSuite0 = 0; - nonConstSSL->options.cipherSuite = 0; - nonConstSSL->extensions = sslExts; - - nonConstSSL->options.tls = 0; - nonConstSSL->options.tls1_1 = 0; - nonConstSSL->options.tls1_3 = 0; - } -dtls13_cleanup: - TLSX_FreeAll(parsedExts, ssl->heap); } else - ret = SendAlert((WOLFSSL*)ssl, alert_fatal, illegal_parameter); +#endif + { + if (!haveKS || !haveSA) { + WOLFSSL_MSG("Client didn't send KeyShare or SigAlgs"); + ERROR_OUT(INCOMPLETE_DATA, dtls13_cleanup); + } + ret = MatchSuite_ex(ssl, &suites, &cs, parsedExts); + if (ret < 0) { + WOLFSSL_MSG("Unsupported cipher suite, ClientHello"); + ERROR_OUT(INCOMPLETE_DATA, dtls13_cleanup); + } + } + if (cs.doHelloRetry) { + ret = TLSX_KeyShare_SetSupported(ssl, &parsedExts); + if (ret != 0) + goto dtls13_cleanup; + } + else { + /* Need to remove the keyshare ext if we found a common group + * and are not doing curve negotiation. */ + TLSX_Remove(&parsedExts, TLSX_KEY_SHARE, ssl->heap); + } + + /* This is required to correctly generate the hash */ + ret = SetCipherSpecs_ex(WOLFSSL_SERVER_END, cs.cipherSuite0, + cs.cipherSuite, &specs, NULL); + if (ret != 0) + goto dtls13_cleanup; + + /* Calculate the cookie hash */ + ret = Dtls13HashClientHello(ssl, cookieHash, &cookieHashSz, ch->raw, + ch->length, &specs); + if (ret != 0) + goto dtls13_cleanup; + + /* Push the cookie to extensions */ + ret = CreateCookieExt(ssl, cookieHash, (word16)cookieHashSz, + &parsedExts, cs.cipherSuite0, cs.cipherSuite); + if (ret != 0) + goto dtls13_cleanup; + + { + WOLFSSL* nonConstSSL = (WOLFSSL*)ssl; + TLSX* sslExts = nonConstSSL->extensions; + + if (ret != 0) + goto dtls13_cleanup; + nonConstSSL->options.tls = 1; + nonConstSSL->options.tls1_1 = 1; + nonConstSSL->options.tls1_3 = 1; + + XMEMCPY(nonConstSSL->session->sessionID, ch->sessionId.elements, + ch->sessionId.size); + nonConstSSL->session->sessionIDSz = (byte)ch->sessionId.size; + nonConstSSL->options.cipherSuite0 = cs.cipherSuite0; + nonConstSSL->options.cipherSuite = cs.cipherSuite; + nonConstSSL->extensions = parsedExts; + + ret = SendTls13ServerHello(nonConstSSL, hello_retry_request); + + /* Can be modified inside SendTls13ServerHello */ + parsedExts = nonConstSSL->extensions; + + nonConstSSL->session->sessionIDSz = 0; + nonConstSSL->options.cipherSuite0 = 0; + nonConstSSL->options.cipherSuite = 0; + nonConstSSL->extensions = sslExts; + + nonConstSSL->options.tls = 0; + nonConstSSL->options.tls1_1 = 0; + nonConstSSL->options.tls1_3 = 0; + } +dtls13_cleanup: + TLSX_FreeAll(parsedExts, ssl->heap); return ret; } #endif @@ -742,6 +757,27 @@ static int SendStatelessReply(const WOLFSSL* ssl, WolfSSL_CH* ch, byte isTls13, return ret; } +static int ClientHelloSanityCheck(WolfSSL_CH* ch, byte isTls13) +{ + /* Do basic checks on the basic fields */ + + /* Check the protocol version */ + if (ch->pv->major != DTLS_MAJOR) + return VERSION_ERROR; + if (ch->pv->minor != DTLSv1_2_MINOR && ch->pv->minor != DTLS_MINOR) + return VERSION_ERROR; + if (isTls13) { + if (ch->cookie.size != 0) + return INVALID_PARAMETER; + if (ch->compression.size != COMP_LEN) + return INVALID_PARAMETER; + if (ch->compression.elements[0] != NO_COMPRESSION) + return INVALID_PARAMETER; + } + + return 0; +} + int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 helloSz) { @@ -771,6 +807,10 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, } #endif + ret = ClientHelloSanityCheck(&ch, isTls13); + if (ret != 0) + return ret; + #if defined(WOLFSSL_DTLS13) || defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME) ret = TlsResumptionIsValid(ssl, &ch, &pskInfo, isTls13); if (ret != 0) @@ -783,7 +823,7 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, } #endif -#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) +#if defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET) if (pskInfo.isValid) { if (IsAtLeastTLSv1_3(pskInfo.pv)) { if (!isTls13) @@ -804,8 +844,16 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input, ret = CheckDtlsCookie(ssl, &ch, isTls13, &cookieGood); if (ret != 0) return ret; - if (!cookieGood) - ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13, &pskInfo); + if (!cookieGood) { +#ifdef WOLFSSL_DTLS13 + /* Invalid cookie for DTLS 1.3 results in an alert. Alert to be sent + * in DoTls13ClientHello. */ + if (isTls13) + ret = INVALID_PARAMETER; + else +#endif + ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13, &pskInfo); + } else ssl->options.dtlsStateful = 1; } diff --git a/src/dtls13.c b/src/dtls13.c index 4a1cced90..aa0da54b0 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -435,7 +435,7 @@ static int Dtls13SendNow(WOLFSSL* ssl, enum HandShakeType handshakeType) return 0; } -/* Handshake header DTLS only fields are not inlcuded in the transcript hash. +/* Handshake header DTLS only fields are not included in the transcript hash. * body points to the body of the DTLSHandshake message. */ int Dtls13HashClientHello(const WOLFSSL* ssl, byte* hash, int* hashSz, const byte* body, word32 length, CipherSpecs* specs) diff --git a/src/internal.c b/src/internal.c index bf91fd546..9d3d55fe0 100644 --- a/src/internal.c +++ b/src/internal.c @@ -30656,6 +30656,32 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } #endif /* HAVE_ECC */ + int TranslateErrorToAlert(int err) + { + switch (err) { + case BUFFER_ERROR: + return decode_error; + case EXT_NOT_ALLOWED: + case PEER_KEY_ERROR: + case ECC_PEERKEY_ERROR: + case BAD_KEY_SHARE_DATA: + case PSK_KEY_ERROR: + case INVALID_PARAMETER: + case HRR_COOKIE_ERROR: + return illegal_parameter; + break; + case INCOMPLETE_DATA: + return missing_extension; + case MATCH_SUITE_ERROR: + case MISSING_HANDSHAKE_DATA: + return handshake_failure; + case VERSION_ERROR: + return wolfssl_alert_protocol_version; + default: + return invalid_alert; + } + } + #ifndef NO_WOLFSSL_SERVER @@ -33158,22 +33184,23 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, /* Update the ssl->options.dtlsStateful setting `if` statement in * wolfSSL_accept when changing this one. */ if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && !IsSCR(ssl)) { - if (((ssl->keys.dtls_sequence_number_hi == ssl->keys.curSeq_hi && - ssl->keys.dtls_sequence_number_lo < ssl->keys.curSeq_lo) || - (ssl->keys.dtls_sequence_number_hi < ssl->keys.curSeq_hi))) { - /* We should continue with the same sequence number as the - * Client Hello if available. */ - ssl->keys.dtls_sequence_number_hi = ssl->keys.curSeq_hi; - ssl->keys.dtls_sequence_number_lo = ssl->keys.curSeq_lo; - } + /* We should continue with the same sequence number as the + * Client Hello. */ + ssl->keys.dtls_sequence_number_hi = ssl->keys.curSeq_hi; + ssl->keys.dtls_sequence_number_lo = ssl->keys.curSeq_lo; /* We should continue with the same handshake number as the * Client Hello. */ ssl->keys.dtls_handshake_number = ssl->keys.dtls_peer_handshake_number; ret = DoClientHelloStateless(ssl, input, inOutIdx, helloSz); if (ret != 0 || !ssl->options.dtlsStateful) { + int alertType = TranslateErrorToAlert(ret); + if (alertType != invalid_alert) + SendAlert(ssl, alert_fatal, alertType); *inOutIdx += helloSz; DtlsResetState(ssl); + if (DtlsIgnoreError(ret)) + ret = 0; return ret; } } @@ -35440,7 +35467,6 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], } ssl->buffers.outputBuffer.length += sendSz; - DtlsResetState(ssl); return SendBuffered(ssl); } diff --git a/src/tls13.c b/src/tls13.c index 96e6137b9..f5ecf1dd6 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -6398,7 +6398,6 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, ret = DoClientHelloStateless(ssl, input, inOutIdx, helloSz); if (ret != 0 || !ssl->options.dtlsStateful) { *inOutIdx += helloSz; - DtlsResetState(ssl); goto exit_dch; } } @@ -6422,8 +6421,7 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, /* this check pass for DTLS Major (0xff) */ if (args->pv.major < SSLv3_MAJOR) { WOLFSSL_MSG("Legacy version field contains unsupported value"); - SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version); - ERROR_OUT(INVALID_PARAMETER, exit_dch); + ERROR_OUT(VERSION_ERROR, exit_dch); } #ifdef WOLFSSL_DTLS13 @@ -6501,13 +6499,13 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #endif sessIdSz = input[args->idx++]; - if (sessIdSz != ID_LEN && sessIdSz != 0) + if (sessIdSz != ID_LEN && sessIdSz != 0) { ERROR_OUT(INVALID_PARAMETER, exit_dch); - - if (sessIdSz + args->idx > helloSz) { - ERROR_OUT(BUFFER_ERROR, exit_dch); } + if (sessIdSz + args->idx > helloSz) + ERROR_OUT(BUFFER_ERROR, exit_dch); + ssl->session->sessionIDSz = sessIdSz; if (sessIdSz == ID_LEN) { XMEMCPY(ssl->session->sessionID, input + args->idx, sessIdSz); @@ -6516,8 +6514,13 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #ifdef WOLFSSL_DTLS13 /* legacy_cookie */ - if (ssl->options.dtls) - args->idx += OPAQUE8_LEN; + if (ssl->options.dtls) { + /* https://www.rfc-editor.org/rfc/rfc9147.html#section-5.3 */ + byte cookieLen = input[args->idx++]; + if (cookieLen != 0) { + ERROR_OUT(INVALID_PARAMETER, exit_dch); + } + } #endif /* WOLFSSL_DTLS13 */ args->clSuites = (Suites*)XMALLOC(sizeof(Suites), ssl->heap, @@ -6624,12 +6627,6 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, */ if (ext->resp == 0) { ret = RestartHandshakeHashWithCookie(ssl, (Cookie*)ext->data); -#ifdef WOLFSSL_DTLS13 - /* Send a new cookie request */ - if (ret == HRR_COOKIE_ERROR && ssl->options.dtls) - ssl->options.serverState = NULL_STATE; - else -#endif if (ret != 0) goto exit_dch; ssl->options.serverState = SERVER_HELLO_COMPLETE; @@ -6675,12 +6672,10 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #ifndef NO_CERTS if (TLSX_Find(ssl->extensions, TLSX_KEY_SHARE) == NULL) { WOLFSSL_MSG("Client did not send a KeyShare extension"); - SendAlert(ssl, alert_fatal, missing_extension); ERROR_OUT(INCOMPLETE_DATA, exit_dch); } if (TLSX_Find(ssl->extensions, TLSX_SIGNATURE_ALGORITHMS) == NULL) { WOLFSSL_MSG("Client did not send a SignatureAlgorithms extension"); - SendAlert(ssl, alert_fatal, missing_extension); ERROR_OUT(INCOMPLETE_DATA, exit_dch); } #else @@ -6706,11 +6701,9 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (!args->usingPSK) { if ((ret = MatchSuite(ssl, args->clSuites)) < 0) { #ifdef WOLFSSL_ASYNC_CRYPT - if (ret == WC_PENDING_E) - goto exit_dch; + if (ret != WC_PENDING_E) #endif - WOLFSSL_MSG("Unsupported cipher suite, ClientHello"); - SendAlert(ssl, alert_fatal, handshake_failure); + WOLFSSL_MSG("Unsupported cipher suite, ClientHello"); goto exit_dch; } } @@ -6758,8 +6751,7 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (ssl->options.cipherSuite0 != TLS13_BYTE) { WOLFSSL_MSG("Negotiated ciphersuite from lesser version than " "TLS v1.3"); - SendAlert(ssl, alert_fatal, handshake_failure); - ERROR_OUT(VERSION_ERROR, exit_dch); + ERROR_OUT(MATCH_SUITE_ERROR, exit_dch); } #ifdef HAVE_SESSION_TICKET @@ -6781,22 +6773,12 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } /* 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; + ssl->options.serverState <= SERVER_HELLO_RETRY_REQUEST_COMPLETE) { + /* Cookie and key share negotiation should be handled in + * DoClientHelloStateless. If we enter here then something went wrong + * in our logic. */ + ERROR_OUT(BAD_HELLO, exit_dch); } #endif /* WOLFSSL_DTLS13 */ @@ -6820,22 +6802,6 @@ exit_dch: } #endif - if (ret == VERSION_ERROR) { -#ifdef WOLFSSL_DTLS - if (ssl->options.dtls) { - if (((ssl->keys.dtls_sequence_number_hi == ssl->keys.curSeq_hi && - ssl->keys.dtls_sequence_number_lo < ssl->keys.curSeq_lo) || - (ssl->keys.dtls_sequence_number_hi < ssl->keys.curSeq_hi))) { - /* We should continue with the same sequence number as the - * Client Hello if available. */ - ssl->keys.dtls_sequence_number_hi = ssl->keys.curSeq_hi; - ssl->keys.dtls_sequence_number_lo = ssl->keys.curSeq_lo; - } - } -#endif - SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version); - } - FreeDch13Args(ssl, args); #ifdef WOLFSSL_ASYNC_CRYPT FreeAsyncCtx(ssl, 0); @@ -10937,6 +10903,7 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, { int ret = 0; word32 inIdx = *inOutIdx; + int alertType = invalid_alert; #if defined(HAVE_ECH) TLSX* echX = NULL; word32 echInOutIdx; @@ -11143,12 +11110,18 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, ret = HashInput(ssl, input + inIdx, size); } - if (ret == BUFFER_ERROR || ret == MISSING_HANDSHAKE_DATA) - SendAlert(ssl, alert_fatal, decode_error); - else if (ret == EXT_NOT_ALLOWED || ret == PEER_KEY_ERROR || - ret == ECC_PEERKEY_ERROR || ret == BAD_KEY_SHARE_DATA || - ret == PSK_KEY_ERROR || ret == INVALID_PARAMETER) { - SendAlert(ssl, alert_fatal, illegal_parameter); + alertType = TranslateErrorToAlert(ret); + + if (alertType != invalid_alert) { +#ifdef WOLFSSL_DTLS13 + if (type == client_hello && ssl->options.dtls) { + /* We should continue with the same sequence number as the + * Client Hello. */ + ssl->dtls13EncryptEpoch->nextSeqNumber = + w64From32(ssl->keys.curSeq_hi, ssl->keys.curSeq_lo); + } +#endif + SendAlert(ssl, alert_fatal, alertType); } if (ret == 0 && ssl->options.tls1_3) { @@ -11263,7 +11236,15 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, #endif /* NO_WOLFSSL_SERVER */ } - WOLFSSL_LEAVE("DoTls13HandShakeMsgType", ret); +#ifdef WOLFSSL_DTLS13 + if (ssl->options.dtls && !ssl->options.dtlsStateful) { + DtlsResetState(ssl); + if (DtlsIgnoreError(ret)) + ret = 0; + } +#endif + + WOLFSSL_LEAVE("DoTls13HandShakeMsgType()", ret); return ret; } @@ -12524,47 +12505,6 @@ const char* wolfSSL_get_cipher_name_by_hash(WOLFSSL* ssl, const char* hash) #ifndef NO_WOLFSSL_SERVER -#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_SEND_HRR_COOKIE) -static int DtlsAcceptStateless(WOLFSSL *ssl) -{ - int ret; - - if (!ssl->options.dtls) - return 0; - - switch (ssl->options.acceptState) { - case TLS13_ACCEPT_BEGIN: - - while (ssl->options.clientState < CLIENT_HELLO_COMPLETE) { - ret = ProcessReply(ssl); - if (ret != 0) - return ret; - } - - if (!IsAtLeastTLSv1_3(ssl->version)) - return wolfSSL_accept(ssl); - - ssl->options.acceptState = TLS13_ACCEPT_CLIENT_HELLO_DONE; - WOLFSSL_MSG("accept state ACCEPT_CLIENT_HELLO_DONE"); - FALL_THROUGH; - - case TLS13_ACCEPT_CLIENT_HELLO_DONE: - if (ssl->options.serverState == - SERVER_HELLO_RETRY_REQUEST_COMPLETE) { - ret = SendTls13ServerHello(ssl, hello_retry_request); - DtlsResetState(ssl); - return ret; - } - - ssl->options.acceptState = TLS13_ACCEPT_HELLO_RETRY_REQUEST_DONE; - WOLFSSL_MSG("accept state ACCEPT_HELLO_RETRY_REQUEST_DONE"); - FALL_THROUGH; - - default: - return 0; - } -} -#endif /* WOLFSSL_DTLS13 */ /* The server accepting a connection from a client. * The protocol version is expecting to be TLS v1.3. @@ -12761,19 +12701,6 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl) } #endif /* WOLFSSL_DTLS13 */ -#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_SEND_HRR_COOKIE) - if (ssl->options.dtls && ssl->options.sendCookie) { - while (ssl->options.serverState < SERVER_HELLO_COMPLETE) { - ret = DtlsAcceptStateless(ssl); - if (ret != 0) { - ssl->error = ret; - WOLFSSL_ERROR(ssl->error); - return WOLFSSL_FATAL_ERROR; - } - } - } -#endif /* defined(WOLFSSL_DTLS13) && defined(WOLFSSL_SEND_HRR_COOKIE) */ - switch (ssl->options.acceptState) { #ifdef HAVE_SECURE_RENEGOTIATION diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 4a2108a51..1764640a2 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6071,6 +6071,7 @@ WOLFSSL_LOCAL word32 nid2oid(int nid, int grp); WOLFSSL_API int wolfSSL_DtlsUpdateWindow(word16 cur_hi, word32 cur_lo, word16* next_hi, word32* next_lo, word32 *window); WOLFSSL_LOCAL void DtlsResetState(WOLFSSL *ssl); +WOLFSSL_LOCAL int DtlsIgnoreError(int err); #endif #ifdef WOLFSSL_DTLS13 @@ -6226,6 +6227,8 @@ WOLFSSL_LOCAL int CreateCookieExt(const WOLFSSL* ssl, byte* hash, byte cipherSuite0, byte cipherSuite); #endif +WOLFSSL_LOCAL int TranslateErrorToAlert(int err); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 18c7c3a5f..4f74588ba 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -729,6 +729,7 @@ typedef struct WOLFSSL_RAND_METHOD { * Add alert string to the function wolfSSL_alert_type_string_long in src/ssl.c */ enum AlertDescription { + invalid_alert = -1, close_notify = 0, unexpected_message = 10, bad_record_mac = 20, diff --git a/wolfssl/wolfcrypt/logging.h b/wolfssl/wolfcrypt/logging.h index 5095a32a5..d47abec87 100644 --- a/wolfssl/wolfcrypt/logging.h +++ b/wolfssl/wolfcrypt/logging.h @@ -170,7 +170,7 @@ WOLFSSL_API void wolfSSL_Debugging_OFF(void); WOLFSSL_API void WOLFSSL_MSG_EX(const char* fmt, ...); #define HAVE_WOLFSSL_MSG_EX #else - #define WOLFSSL_MSG_EX(...) + #define WOLFSSL_MSG_EX(...) do{} while(0) #endif WOLFSSL_API void WOLFSSL_MSG(const char* msg); WOLFSSL_API void WOLFSSL_BUFFER(const byte* buffer, word32 length);