wolfssl-w32/src/dtls.c

1373 lines
40 KiB
C

/* dtls.c
*
* Copyright (C) 2006-2023 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
*/
/*
* WOLFSSL_DTLS_NO_HVR_ON_RESUME
* WOLFSSL_DTLS13_NO_HRR_ON_RESUME
* If defined, a DTLS server will not do a cookie exchange on successful
* client resumption: the resumption will be faster (one RTT less) and
* will consume less bandwidth (one ClientHello and one
* HelloVerifyRequest/HelloRetryRequest less). On the other hand, if a valid
* SessionID/ticket/psk is collected, forged clientHello messages will
* consume resources on the server. For DTLS 1.3, using this option also
* allows for the server to process Early Data/0-RTT Data. Without this, the
* Early Data would be dropped since the server doesn't enter stateful
* processing until receiving a verified ClientHello with the cookie.
*
* To allow DTLS 1.3 resumption without the cookie exchange:
* - Compile wolfSSL with WOLFSSL_DTLS13_NO_HRR_ON_RESUME defined
* - Call wolfSSL_dtls13_no_hrr_on_resume(ssl, 1) on the WOLFSSL object to
* disable the cookie exchange on resumption
* - Continue like with a normal connection
* WOLFSSL_DTLS_CH_FRAG
* Allow a server to process a fragmented second/verified (one containing a
* valid cookie response) ClientHello message. The first/unverified (one
* without a cookie extension) ClientHello MUST be unfragmented so that the
* DTLS server can process it statelessly. This is only implemented for
* DTLS 1.3. The user MUST call wolfSSL_dtls13_allow_ch_frag() on the server
* to explicitly enable this during runtime.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#ifndef WOLFCRYPT_ONLY
#include <wolfssl/error-ssl.h>
#include <wolfssl/internal.h>
#include <wolfssl/ssl.h>
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
#define ERROR_OUT(err, eLabel) { ret = (err); goto eLabel; }
#ifdef WOLFSSL_DTLS
void DtlsResetState(WOLFSSL* ssl)
{
/* Reset the state so that we can statelessly await the
* ClientHello that contains the cookie. Don't gate on IsAtLeastTLSv1_3
* to handle the edge case when the peer wants a lower version. */
/* Reset DTLS window */
#ifdef WOLFSSL_DTLS13
w64Zero(&ssl->dtls13Epochs[0].nextSeqNumber);
w64Zero(&ssl->dtls13Epochs[0].nextPeerSeqNumber);
XMEMSET(ssl->dtls13Epochs[0].window, 0,
sizeof(ssl->dtls13Epochs[0].window));
Dtls13FreeFsmResources(ssl);
#endif
ssl->keys.dtls_expected_peer_handshake_number = 0;
ssl->keys.dtls_handshake_number = 0;
ssl->keys.dtls_sequence_number_hi = 0;
ssl->keys.dtls_sequence_number_lo = 0;
/* Reset states */
ssl->options.serverState = NULL_STATE;
ssl->options.clientState = NULL_STATE;
ssl->options.connectState = CONNECT_BEGIN;
ssl->options.acceptState = ACCEPT_BEGIN;
ssl->options.handShakeState = NULL_STATE;
ssl->options.seenUnifiedHdr = 0;
ssl->msgsReceived.got_client_hello = 0;
ssl->keys.dtls_handshake_number = 0;
ssl->keys.dtls_expected_peer_handshake_number = 0;
XMEMSET(ssl->keys.peerSeq, 0, sizeof(ssl->keys.peerSeq));
ssl->options.tls = 0;
ssl->options.tls1_1 = 0;
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;
}
}
void DtlsSetSeqNumForReply(WOLFSSL* ssl)
{
/* We cover both DTLS 1.2 and 1.3 cases because we may be negotiating
* protocols. */
/* 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;
#ifdef WOLFSSL_DTLS13
if (ssl->dtls13EncryptEpoch != NULL) {
ssl->dtls13EncryptEpoch->nextSeqNumber =
w64From32(ssl->keys.curSeq_hi, ssl->keys.curSeq_lo);
}
#endif
/* We should continue with the same handshake number as the
* Client Hello. */
ssl->keys.dtls_handshake_number =
ssl->keys.dtls_peer_handshake_number;
}
#if !defined(NO_WOLFSSL_SERVER)
#if defined(NO_SHA) && defined(NO_SHA256)
#error "DTLS needs either SHA or SHA-256"
#endif /* NO_SHA && NO_SHA256 */
#if !defined(NO_SHA) && defined(NO_SHA256)
#define DTLS_COOKIE_TYPE WC_SHA
#define DTLS_COOKIE_SZ WC_SHA_DIGEST_SIZE
#endif /* !NO_SHA && NO_SHA256 */
#ifndef NO_SHA256
#define DTLS_COOKIE_TYPE WC_SHA256
#define DTLS_COOKIE_SZ WC_SHA256_DIGEST_SIZE
#endif /* !NO_SHA256 */
#if defined(WOLFSSL_DTLS13) && (defined(HAVE_SESSION_TICKET) || \
!defined(NO_PSK))
typedef struct PskInfo {
byte cipherSuite0;
byte cipherSuite;
byte isValid:1;
} PskInfo;
#endif
typedef struct WolfSSL_ConstVector {
word32 size;
const byte* elements;
} WolfSSL_ConstVector;
typedef struct WolfSSL_CH {
ProtocolVersion* pv;
const byte* random;
WolfSSL_ConstVector sessionId;
WolfSSL_ConstVector cookie;
WolfSSL_ConstVector cipherSuite;
WolfSSL_ConstVector compression;
WolfSSL_ConstVector extension;
WolfSSL_ConstVector cookieExt;
const byte* raw;
word32 length;
/* Store the DTLS 1.2 cookie since we can just compute it once in dtls.c */
byte dtls12cookie[DTLS_COOKIE_SZ];
byte dtls12cookieSet:1;
} WolfSSL_CH;
static int ReadVector8(const byte* input, WolfSSL_ConstVector* v)
{
v->size = *input;
v->elements = input + OPAQUE8_LEN;
return v->size + OPAQUE8_LEN;
}
static int ReadVector16(const byte* input, WolfSSL_ConstVector* v)
{
word16 size16;
ato16(input, &size16);
v->size = (word32)size16;
v->elements = input + OPAQUE16_LEN;
return v->size + OPAQUE16_LEN;
}
static int CreateDtls12Cookie(const WOLFSSL* ssl, const WolfSSL_CH* ch,
byte* cookie)
{
int ret;
Hmac cookieHmac;
ret = wc_HmacInit(&cookieHmac, ssl->heap, ssl->devId);
if (ret == 0) {
ret = wc_HmacSetKey(&cookieHmac, DTLS_COOKIE_TYPE,
ssl->buffers.dtlsCookieSecret.buffer,
ssl->buffers.dtlsCookieSecret.length);
if (ret == 0) {
ret = wc_HmacUpdate(&cookieHmac,
(const byte*)ssl->buffers.dtlsCtx.peer.sa,
ssl->buffers.dtlsCtx.peer.sz);
}
if (ret == 0)
ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->pv, OPAQUE16_LEN);
if (ret == 0)
ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->random, RAN_LEN);
if (ret == 0) {
ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->sessionId.elements,
ch->sessionId.size);
}
if (ret == 0) {
ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->cipherSuite.elements,
ch->cipherSuite.size);
}
if (ret == 0) {
ret = wc_HmacUpdate(&cookieHmac, (byte*)ch->compression.elements,
ch->compression.size);
}
if (ret == 0)
ret = wc_HmacFinal(&cookieHmac, cookie);
wc_HmacFree(&cookieHmac);
}
return ret;
}
static int CheckDtlsCookie(const WOLFSSL* ssl, WolfSSL_CH* ch,
byte isTls13, byte* cookieGood)
{
int ret = 0;
(void)isTls13;
*cookieGood = 0;
#ifdef WOLFSSL_DTLS13
if (isTls13) {
word16 len;
if (ch->cookieExt.size < OPAQUE16_LEN + 1)
return BUFFER_E;
ato16(ch->cookieExt.elements, &len);
if (ch->cookieExt.size - OPAQUE16_LEN != len)
return BUFFER_E;
ret = TlsCheckCookie(ssl, ch->cookieExt.elements + OPAQUE16_LEN,
(word16)(ch->cookieExt.size - OPAQUE16_LEN));
if (ret < 0 && ret != HRR_COOKIE_ERROR)
return ret;
*cookieGood = ret > 0;
ret = 0;
}
else
#endif
{
if (ch->cookie.size != DTLS_COOKIE_SZ)
return 0;
if (!ch->dtls12cookieSet) {
ret = CreateDtls12Cookie(ssl, ch, ch->dtls12cookie);
if (ret != 0)
return ret;
ch->dtls12cookieSet = 1;
}
*cookieGood = ConstantCompare(ch->cookie.elements, ch->dtls12cookie,
DTLS_COOKIE_SZ) == 0;
}
return ret;
}
static int ParseClientHello(const byte* input, word32 helloSz, WolfSSL_CH* ch,
byte isFirstCHFrag)
{
word32 idx = 0;
(void)isFirstCHFrag;
/* protocol version, random and session id length check */
if (OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN > helloSz)
return BUFFER_ERROR;
ch->raw = input;
ch->pv = (ProtocolVersion*)(input + idx);
idx += OPAQUE16_LEN;
ch->random = (byte*)(input + idx);
idx += RAN_LEN;
idx += ReadVector8(input + idx, &ch->sessionId);
if (idx > helloSz - OPAQUE8_LEN)
return BUFFER_ERROR;
idx += ReadVector8(input + idx, &ch->cookie);
if (idx > helloSz - OPAQUE16_LEN)
return BUFFER_ERROR;
idx += ReadVector16(input + idx, &ch->cipherSuite);
if (idx > helloSz - OPAQUE8_LEN)
return BUFFER_ERROR;
idx += ReadVector8(input + idx, &ch->compression);
if (idx < helloSz - OPAQUE16_LEN) {
/* Extensions are optional */
#ifdef WOLFSSL_DTLS_CH_FRAG
word32 extStart = idx + OPAQUE16_LEN;
#endif
idx += ReadVector16(input + idx, &ch->extension);
if (idx > helloSz) {
#ifdef WOLFSSL_DTLS_CH_FRAG
idx = helloSz;
/* Allow incomplete extensions if we are parsing a fragment */
if (isFirstCHFrag && extStart < helloSz)
ch->extension.size = helloSz - extStart;
else
#endif
return BUFFER_ERROR;
}
}
if (idx != helloSz)
return BUFFER_ERROR;
ch->length = idx;
return 0;
}
#if (defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME) && defined(HAVE_SESSION_TICKET)) \
|| defined(WOLFSSL_DTLS13)
static int FindExtByType(WolfSSL_ConstVector* ret, word16 extType,
WolfSSL_ConstVector exts, int* tlsxFound)
{
word32 len, idx = 0;
word16 type;
WolfSSL_ConstVector ext;
XMEMSET(ret, 0, sizeof(*ret));
len = exts.size;
*tlsxFound = FALSE;
/* type + len */
while (len >= OPAQUE16_LEN + OPAQUE16_LEN) {
ato16(exts.elements + idx, &type);
idx += OPAQUE16_LEN;
idx += ReadVector16(exts.elements + idx, &ext);
if (idx > exts.size)
return BUFFER_ERROR;
if (type == extType) {
XMEMCPY(ret, &ext, sizeof(ext));
*tlsxFound = TRUE;
return 0;
}
len = exts.size - idx;
}
return 0;
}
#endif
#if defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME)
#ifdef HAVE_SESSION_TICKET
static int TlsTicketIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector exts,
int* resume)
{
WolfSSL_ConstVector tlsxSessionTicket;
byte tempTicket[SESSION_TICKET_LEN];
InternalTicket* it = NULL;
int ret = 0;
int tlsxFound;
*resume = FALSE;
ret = FindExtByType(&tlsxSessionTicket, TLSX_SESSION_TICKET, exts,
&tlsxFound);
if (ret != 0)
return ret;
if (tlsxSessionTicket.size == 0)
return 0;
if (tlsxSessionTicket.size > SESSION_TICKET_LEN)
return 0;
XMEMCPY(tempTicket, tlsxSessionTicket.elements, tlsxSessionTicket.size);
ret = DoDecryptTicket(ssl, tempTicket, (word32)tlsxSessionTicket.size, &it);
if (ret == WOLFSSL_TICKET_RET_OK || ret == WOLFSSL_TICKET_RET_CREATE) {
/* This logic is only for TLS <= 1.2 tickets. Don't accept TLS 1.3. */
if (!IsAtLeastTLSv1_3(it->pv))
*resume = TRUE;
}
if (it != NULL)
ForceZero(it, sizeof(InternalTicket));
return 0;
}
#endif /* HAVE_SESSION_TICKET */
static int TlsSessionIdIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector sessionID,
int* resume)
{
const WOLFSSL_SESSION* sess;
word32 sessRow;
int ret;
#ifdef HAVE_EXT_CACHE
int copy;
#endif
*resume = FALSE;
if (ssl->options.sessionCacheOff)
return 0;
if (sessionID.size != ID_LEN)
return 0;
#ifdef HAVE_EXT_CACHE
if (ssl->ctx->get_sess_cb != NULL) {
WOLFSSL_SESSION* extSess =
ssl->ctx->get_sess_cb((WOLFSSL*)ssl, sessionID.elements, ID_LEN,
&copy);
if (extSess != NULL) {
#if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \
defined(HAVE_SESSION_TICKET))
/* This logic is only for TLS <= 1.2 tickets. Don't accept
* TLS 1.3. */
if (!IsAtLeastTLSv1_3(extSess->version))
#endif
*resume = TRUE;
if (!copy)
wolfSSL_FreeSession(ssl->ctx, extSess);
if (*resume)
return 0;
}
}
if (ssl->ctx->internalCacheLookupOff)
return 0;
#endif
ret = TlsSessionCacheGetAndRdLock(sessionID.elements, &sess, &sessRow,
ssl->options.side);
if (ret == 0 && sess != NULL) {
#if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \
defined(HAVE_SESSION_TICKET))
/* This logic is only for TLS <= 1.2 tickets. Don't accept
* TLS 1.3. */
if (!IsAtLeastTLSv1_3(sess->version))
#endif
*resume = TRUE;
TlsSessionCacheUnlockRow(sessRow);
}
return 0;
}
static int TlsResumptionIsValid(const WOLFSSL* ssl, WolfSSL_CH* ch,
int* resume)
{
int ret;
#ifdef HAVE_SESSION_TICKET
ret = TlsTicketIsValid(ssl, ch->extension, resume);
if (ret != 0)
return ret;
if (*resume)
return 0;
#endif /* HAVE_SESSION_TICKET */
ret = TlsSessionIdIsValid(ssl, ch->sessionId, resume);
return ret;
}
#endif /* WOLFSSL_DTLS13 || WOLFSSL_DTLS_NO_HVR_ON_RESUME */
#ifdef WOLFSSL_DTLS13
static int TlsCheckSupportedVersion(const WOLFSSL* ssl,
WolfSSL_CH* ch, byte *isTls13)
{
WolfSSL_ConstVector tlsxSupportedVersions;
int ret;
ProtocolVersion pv = ssl->version;
int tlsxFound;
ret = FindExtByType(&tlsxSupportedVersions, TLSX_SUPPORTED_VERSIONS,
ch->extension, &tlsxFound);
if (ret != 0)
return ret;
if (!tlsxFound) {
*isTls13 = 0;
return 0;
}
ret = TLSX_SupportedVersions_Parse(ssl, tlsxSupportedVersions.elements,
(word16)tlsxSupportedVersions.size, client_hello, &pv, NULL, NULL);
if (ret != 0)
return ret;
if (IsAtLeastTLSv1_3(pv))
*isTls13 = 1;
else
*isTls13 = 0;
return 0;
}
#endif
#if defined(WOLFSSL_DTLS13) && \
(!defined(NO_PSK) || defined(HAVE_SESSION_TICKET))
/* Very simplified version of CheckPreSharedKeys to find the current suite */
static void FindPskSuiteFromExt(const WOLFSSL* ssl, TLSX* extensions,
PskInfo* pskInfo, Suites* suites)
{
TLSX* pskExt = TLSX_Find(extensions, TLSX_PRE_SHARED_KEY);
PreSharedKey* current;
int i;
int ret;
if (pskExt == NULL)
return;
for (i = 0; i < suites->suiteSz; i += 2) {
for (current = (PreSharedKey*)pskExt->data; current != NULL;
current = current->next) {
#ifdef HAVE_SESSION_TICKET
{
/* Decode the identity. */
switch (current->decryptRet) {
case PSK_DECRYPT_NONE:
ret = DoClientTicket_ex(ssl, current, 0);
break;
case PSK_DECRYPT_OK:
ret = WOLFSSL_TICKET_RET_OK;
break;
case PSK_DECRYPT_CREATE:
ret = WOLFSSL_TICKET_RET_CREATE;
break;
case PSK_DECRYPT_FAIL:
default:
ret = WOLFSSL_TICKET_RET_REJECT;
break;
}
if (ret == WOLFSSL_TICKET_RET_OK) {
if (DoClientTicketCheck(ssl, current, ssl->timeout,
suites->suites + i) != 0) {
continue;
}
pskInfo->cipherSuite0 = current->it->suite[0];
pskInfo->cipherSuite = current->it->suite[1];
pskInfo->isValid = 1;
goto cleanup;
}
}
#endif
#ifndef NO_PSK
{
int found = 0;
byte psk_key[MAX_PSK_KEY_LEN];
word32 psk_keySz;
byte foundSuite[SUITE_LEN];
ret = FindPskSuite(ssl, current, psk_key, &psk_keySz,
suites->suites + i, &found, foundSuite);
/* Clear the key just in case */
ForceZero(psk_key, sizeof(psk_key));
if (ret == 0 && found) {
pskInfo->cipherSuite0 = foundSuite[0];
pskInfo->cipherSuite = foundSuite[1];
pskInfo->isValid = 1;
goto cleanup;
}
}
#endif
}
}
/* Empty return necessary so we can have both the label and macro guard */
cleanup:
#ifdef HAVE_SESSION_TICKET
CleanupClientTickets((PreSharedKey*)pskExt->data);
#endif
return;
}
#endif
#ifdef WOLFSSL_DTLS13
#ifndef WOLFSSL_SEND_HRR_COOKIE
#error "WOLFSSL_SEND_HRR_COOKIE has to be defined to use DTLS 1.3 server"
#endif
#ifdef WOLFSSL_PSK_ONE_ID
#error WOLFSSL_PSK_ONE_ID is not compatible with stateless DTLS 1.3 server. \
wolfSSL needs to be able to make multiple calls for the same PSK.
#endif
static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch)
{
int ret = -1;
TLSX* parsedExts = NULL;
WolfSSL_ConstVector tlsx;
int tlsxFound;
Suites suites;
byte haveSA = 0;
byte haveKS = 0;
byte haveSG = 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;
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
PskInfo pskInfo;
XMEMSET(&pskInfo, 0, sizeof(pskInfo));
#endif
#ifndef HAVE_SUPPORTED_CURVES
(void)doKE;
#endif /* !HAVE_SUPPORTED_CURVES */
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);
}
/* 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 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;
#if defined(HAVE_SUPPORTED_CURVES)
ret = TLSX_SupportedCurve_Copy(ssl->extensions, &parsedExts, ssl->heap);
if (ret != 0)
goto dtls13_cleanup;
#endif
#if !defined(NO_CERTS)
/* Signature algs */
ret = FindExtByType(&tlsx, TLSX_SIGNATURE_ALGORITHMS,
ch->extension, &tlsxFound);
if (ret != 0)
goto dtls13_cleanup;
if (tlsxFound) {
WolfSSL_ConstVector sigAlgs;
if (tlsx.size < OPAQUE16_LEN)
ERROR_OUT(BUFFER_ERROR, dtls13_cleanup);
ReadVector16(tlsx.elements, &sigAlgs);
if (sigAlgs.size != tlsx.size - OPAQUE16_LEN)
ERROR_OUT(BUFFER_ERROR, dtls13_cleanup);
if ((sigAlgs.size % 2) != 0)
ERROR_OUT(BUFFER_ERROR, dtls13_cleanup);
suites.hashSigAlgoSz = (word16)sigAlgs.size;
XMEMCPY(suites.hashSigAlgo, sigAlgs.elements, sigAlgs.size);
haveSA = 1;
}
#endif /* !defined(NO_CERTS) */
#ifdef HAVE_SUPPORTED_CURVES
/* Supported groups */
ret = FindExtByType(&tlsx, TLSX_SUPPORTED_GROUPS,
ch->extension, &tlsxFound);
if (ret != 0)
goto dtls13_cleanup;
if (tlsxFound) {
ret = TLSX_SupportedCurve_Parse(ssl, tlsx.elements,
(word16)tlsx.size, 1, &parsedExts);
if (ret != 0)
goto dtls13_cleanup;
haveSG = 1;
}
/* Key share */
ret = FindExtByType(&tlsx, TLSX_KEY_SHARE,
ch->extension, &tlsxFound);
if (ret != 0)
goto dtls13_cleanup;
if (tlsxFound) {
ret = TLSX_KeyShare_Parse_ClientHello(ssl, tlsx.elements,
(word16)tlsx.size, &parsedExts);
if (ret != 0)
goto dtls13_cleanup;
haveKS = 1;
}
#endif /* HAVE_SUPPORTED_CURVES */
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
/* Pre-shared key */
ret = FindExtByType(&tlsx, TLSX_PRE_SHARED_KEY, ch->extension, &tlsxFound);
if (ret != 0)
goto dtls13_cleanup;
if (tlsxFound) {
/* 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;
/* Ask the user for the ciphersuite matching this identity */
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 */
if (pskInfo.isValid) {
ret = FindExtByType(&tlsx, TLSX_PSK_KEY_EXCHANGE_MODES,
ch->extension, &tlsxFound);
if (ret != 0)
goto dtls13_cleanup;
if (!tlsxFound)
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;
/* https://datatracker.ietf.org/doc/html/rfc8446#section-9.2 */
if (haveSG ^ haveKS) {
WOLFSSL_MSG("Client needs to send both or none of KeyShare and "
"SupportedGroups");
ERROR_OUT(INCOMPLETE_DATA, dtls13_cleanup);
}
#ifdef HAVE_SUPPORTED_CURVES
if (doKE) {
byte searched = 0;
ret = TLSX_KeyShare_Choose(ssl, parsedExts, cs.cipherSuite0,
cs.cipherSuite, &cs.clientKSE, &searched);
if (ret != 0)
goto dtls13_cleanup;
if (cs.clientKSE == NULL && searched)
cs.doHelloRetry = 1;
}
#endif /* HAVE_SUPPORTED_CURVES */
}
else
#endif /* defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) */
{
/* https://datatracker.ietf.org/doc/html/rfc8446#section-9.2 */
if (!haveKS || !haveSA || !haveSG) {
WOLFSSL_MSG("Client didn't send KeyShare or SigAlgs or "
"SupportedGroups.");
ERROR_OUT(INCOMPLETE_DATA, dtls13_cleanup);
}
/* TLSX_KeyShare_Choose is done deep inside MatchSuite_ex */
ret = MatchSuite_ex(ssl, &suites, &cs, parsedExts);
if (ret < 0) {
WOLFSSL_MSG("Unsupported cipher suite, ClientHello DTLS 1.3");
ERROR_OUT(INCOMPLETE_DATA, dtls13_cleanup);
}
}
#ifdef WOLFSSL_DTLS13_NO_HRR_ON_RESUME
if (ssl->options.dtls13NoHrrOnResume && usePSK && pskInfo.isValid &&
!cs.doHelloRetry) {
/* Skip HRR on resumption */
((WOLFSSL*)ssl)->options.dtlsStateful = 1;
goto dtls13_cleanup;
}
#endif
#ifdef HAVE_SUPPORTED_CURVES
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);
}
#endif /* HAVE_SUPPORTED_CURVES */
/* This is required to correctly generate the hash */
ret = GetCipherSpec(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
static int SendStatelessReply(const WOLFSSL* ssl, WolfSSL_CH* ch, byte isTls13)
{
int ret;
(void)isTls13;
#ifdef WOLFSSL_DTLS13
if (isTls13) {
ret = SendStatelessReplyDtls13(ssl, ch);
}
else
#endif
{
#if !defined(WOLFSSL_NO_TLS12)
if (!ch->dtls12cookieSet) {
ret = CreateDtls12Cookie(ssl, ch, ch->dtls12cookie);
if (ret != 0)
return ret;
ch->dtls12cookieSet = 1;
}
ret = SendHelloVerifyRequest((WOLFSSL*)ssl, ch->dtls12cookie,
DTLS_COOKIE_SZ);
#else
WOLFSSL_MSG("DTLS1.2 disabled with WOLFSSL_NO_TLS12");
WOLFSSL_ERROR_VERBOSE(NOT_COMPILED_IN);
ret = NOT_COMPILED_IN;
#endif
}
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 helloSz,
byte isFirstCHFrag, byte* tls13)
{
int ret;
WolfSSL_CH ch;
byte isTls13 = 0;
WOLFSSL_ENTER("DoClientHelloStateless");
if (isFirstCHFrag) {
#ifdef WOLFSSL_DTLS_CH_FRAG
WOLFSSL_MSG("\tProcessing fragmented ClientHello");
#else
WOLFSSL_MSG("\tProcessing fragmented ClientHello but "
"WOLFSSL_DTLS_CH_FRAG is not defined. This should not happen.");
return BAD_STATE_E;
#endif
}
if (tls13 != NULL)
*tls13 = 0;
XMEMSET(&ch, 0, sizeof(ch));
ssl->options.dtlsStateful = 0;
ret = ParseClientHello(input, helloSz, &ch, isFirstCHFrag);
if (ret != 0)
return ret;
#ifdef WOLFSSL_DTLS13
if (IsAtLeastTLSv1_3(ssl->version)) {
ret = TlsCheckSupportedVersion(ssl, &ch, &isTls13);
if (ret != 0)
return ret;
if (tls13 != NULL)
*tls13 = isTls13;
if (isTls13) {
int tlsxFound;
ret = FindExtByType(&ch.cookieExt, TLSX_COOKIE, ch.extension,
&tlsxFound);
if (ret != 0)
return ret;
}
}
#endif
ret = ClientHelloSanityCheck(&ch, isTls13);
if (ret != 0)
return ret;
#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME
if (!isTls13 && !isFirstCHFrag) {
int resume = FALSE;
ret = TlsResumptionIsValid(ssl, &ch, &resume);
if (ret != 0)
return ret;
if (resume) {
ssl->options.dtlsStateful = 1;
return 0;
}
}
#endif
if (ch.cookie.size == 0 && ch.cookieExt.size == 0) {
#ifdef WOLFSSL_DTLS_CH_FRAG
/* Don't send anything here when processing fragment */
if (isFirstCHFrag)
ret = COOKIE_ERROR;
else
#endif
ret = SendStatelessReply(ssl, &ch, isTls13);
}
else {
byte cookieGood;
ret = CheckDtlsCookie(ssl, &ch, isTls13, &cookieGood);
if (ret != 0)
return ret;
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
#ifdef WOLFSSL_DTLS_CH_FRAG
/* Don't send anything here when processing fragment */
if (isFirstCHFrag)
ret = COOKIE_ERROR;
else
#endif
ret = SendStatelessReply(ssl, &ch, isTls13);
}
else {
ssl->options.dtlsStateful = 1;
/* Update the window now that we enter the stateful parsing */
#ifdef WOLFSSL_DTLS13
if (isTls13)
ret = Dtls13UpdateWindowRecordRecvd(ssl);
else
#endif
DtlsUpdateWindow(ssl);
}
}
return ret;
}
#endif /* !defined(NO_WOLFSSL_SERVER) */
#if defined(WOLFSSL_DTLS_CID)
typedef struct ConnectionID {
byte length;
/* Ignore "nonstandard extension used : zero-sized array in struct/union"
* MSVC warning */
#ifdef _MSC_VER
#pragma warning(disable: 4200)
#endif
byte id[];
} ConnectionID;
typedef struct CIDInfo {
ConnectionID* tx;
ConnectionID* rx;
byte negotiated : 1;
} CIDInfo;
static ConnectionID* DtlsCidNew(const byte* cid, byte size, void* heap)
{
ConnectionID* ret;
ret = (ConnectionID*)XMALLOC(sizeof(ConnectionID) + size, heap,
DYNAMIC_TYPE_TLSX);
if (ret == NULL)
return NULL;
ret->length = size;
XMEMCPY(ret->id, cid, size);
return ret;
}
static WC_INLINE CIDInfo* DtlsCidGetInfo(WOLFSSL* ssl)
{
return ssl->dtlsCidInfo;
}
static int DtlsCidGetSize(WOLFSSL* ssl, unsigned int* size, int rx)
{
ConnectionID* id;
CIDInfo* info;
if (ssl == NULL || size == NULL)
return BAD_FUNC_ARG;
info = DtlsCidGetInfo(ssl);
if (info == NULL)
return WOLFSSL_FAILURE;
id = rx ? info->rx : info->tx;
if (id == NULL) {
*size = 0;
return WOLFSSL_SUCCESS;
}
*size = id->length;
return WOLFSSL_SUCCESS;
}
static int DtlsCidGet(WOLFSSL* ssl, unsigned char* buf, int bufferSz, int rx)
{
ConnectionID* id;
CIDInfo* info;
if (ssl == NULL || buf == NULL)
return BAD_FUNC_ARG;
info = DtlsCidGetInfo(ssl);
if (info == NULL)
return WOLFSSL_FAILURE;
id = rx ? info->rx : info->tx;
if (id == NULL || id->length == 0)
return WOLFSSL_SUCCESS;
if (id->length > bufferSz)
return LENGTH_ERROR;
XMEMCPY(buf, id->id, id->length);
return WOLFSSL_SUCCESS;
}
static CIDInfo* DtlsCidGetInfoFromExt(byte* ext)
{
WOLFSSL** sslPtr;
WOLFSSL* ssl;
if (ext == NULL)
return NULL;
sslPtr = (WOLFSSL**)ext;
ssl = *sslPtr;
if (ssl == NULL)
return NULL;
return ssl->dtlsCidInfo;
}
static void DtlsCidUnsetInfoFromExt(byte* ext)
{
WOLFSSL** sslPtr;
WOLFSSL* ssl;
if (ext == NULL)
return;
sslPtr = (WOLFSSL**)ext;
ssl = *sslPtr;
if (ssl == NULL)
return;
ssl->dtlsCidInfo = NULL;
}
void TLSX_ConnectionID_Free(byte* ext, void* heap)
{
CIDInfo* info;
(void)heap;
info = DtlsCidGetInfoFromExt(ext);
if (info == NULL)
return;
if (info->rx != NULL)
XFREE(info->rx, heap, DYNAMIC_TYPE_TLSX);
if (info->tx != NULL)
XFREE(info->tx, heap, DYNAMIC_TYPE_TLSX);
XFREE(info, heap, DYNAMIC_TYPE_TLSX);
DtlsCidUnsetInfoFromExt(ext);
XFREE(ext, heap, DYNAMIC_TYPE_TLSX);
}
word16 TLSX_ConnectionID_Write(byte* ext, byte* output)
{
CIDInfo* info;
info = DtlsCidGetInfoFromExt(ext);
if (info == NULL)
return 0;
/* empty CID */
if (info->rx == NULL) {
*output = 0;
return OPAQUE8_LEN;
}
*output = info->rx->length;
XMEMCPY(output + OPAQUE8_LEN, info->rx->id, info->rx->length);
return OPAQUE8_LEN + info->rx->length;
}
word16 TLSX_ConnectionID_GetSize(byte* ext)
{
CIDInfo* info = DtlsCidGetInfoFromExt(ext);
if (info == NULL)
return 0;
return info->rx == NULL ? OPAQUE8_LEN : OPAQUE8_LEN + info->rx->length;
}
int TLSX_ConnectionID_Use(WOLFSSL* ssl)
{
CIDInfo* info;
WOLFSSL** ext;
int ret;
ext = (WOLFSSL**)TLSX_Find(ssl->extensions, TLSX_CONNECTION_ID);
if (ext != NULL)
return 0;
info = (CIDInfo*)XMALLOC(sizeof(CIDInfo), ssl->heap, DYNAMIC_TYPE_TLSX);
if (info == NULL)
return MEMORY_ERROR;
ext = (WOLFSSL**)XMALLOC(sizeof(WOLFSSL**), ssl->heap, DYNAMIC_TYPE_TLSX);
if (ext == NULL) {
XFREE(info, ssl->heap, DYNAMIC_TYPE_TLSX);
return MEMORY_ERROR;
}
XMEMSET(info, 0, sizeof(CIDInfo));
/* CIDInfo needs to be accessed every time we send or receive a record. To
* avoid the cost of the extension lookup save a pointer to the structure
* inside the SSL object itself, and save a pointer to the SSL object in the
* extension. The extension freeing routine uses the pointer to the SSL
* object to find the structure and to set ssl->dtlsCidInfo pointer to NULL
* after freeing the structure. */
ssl->dtlsCidInfo = info;
*ext = ssl;
ret =
TLSX_Push(&ssl->extensions, TLSX_CONNECTION_ID, (void*)ext, ssl->heap);
if (ret != 0) {
XFREE(info, ssl->heap, DYNAMIC_TYPE_TLSX);
XFREE(ext, ssl->heap, DYNAMIC_TYPE_TLSX);
ssl->dtlsCidInfo = NULL;
return ret;
}
return 0;
}
int TLSX_ConnectionID_Parse(WOLFSSL* ssl, const byte* input, word16 length,
byte isRequest)
{
ConnectionID* id;
CIDInfo* info;
byte cidSize;
TLSX* ext;
ext = TLSX_Find(ssl->extensions, TLSX_CONNECTION_ID);
if (ext == NULL) {
/* CID not enabled */
if (isRequest) {
WOLFSSL_MSG("Received CID ext but it's not enabled, ignoring");
return 0;
}
else {
WOLFSSL_MSG("CID ext not requested by the Client, aborting");
return UNSUPPORTED_EXTENSION;
}
}
info = DtlsCidGetInfo(ssl);
if (info == NULL)
return BAD_STATE_E;
/* it may happen if we process two ClientHello because the server sent an
* HRR request */
if (info->tx != NULL) {
if (ssl->options.side != WOLFSSL_SERVER_END &&
ssl->options.serverState != SERVER_HELLO_RETRY_REQUEST_COMPLETE)
return BAD_STATE_E;
XFREE(info->tx, ssl->heap, DYNAMIC_TYPE_TLSX);
info->tx = NULL;
}
if (length < OPAQUE8_LEN)
return BUFFER_ERROR;
cidSize = *input;
if (cidSize + OPAQUE8_LEN > length)
return BUFFER_ERROR;
if (cidSize > 0) {
id = (ConnectionID*)XMALLOC(sizeof(*id) + cidSize, ssl->heap,
DYNAMIC_TYPE_TLSX);
if (id == NULL)
return MEMORY_ERROR;
XMEMCPY(id->id, input + OPAQUE8_LEN, cidSize);
id->length = cidSize;
info->tx = id;
}
info->negotiated = 1;
if (isRequest)
ext->resp = 1;
return 0;
}
void DtlsCIDOnExtensionsParsed(WOLFSSL* ssl)
{
CIDInfo* info;
info = DtlsCidGetInfo(ssl);
if (info == NULL)
return;
if (!info->negotiated) {
TLSX_Remove(&ssl->extensions, TLSX_CONNECTION_ID, ssl->heap);
return;
}
}
byte DtlsCIDCheck(WOLFSSL* ssl, const byte* input, word16 inputSize)
{
CIDInfo* info;
info = DtlsCidGetInfo(ssl);
if (info == NULL || info->rx == NULL || info->rx->length == 0)
return 0;
if (inputSize < info->rx->length)
return 0;
return XMEMCMP(input, info->rx->id, info->rx->length) == 0;
}
int wolfSSL_dtls_cid_use(WOLFSSL* ssl)
{
int ret;
/* CID is supported on DTLSv1.3 only */
if (!IsAtLeastTLSv1_3(ssl->version))
return WOLFSSL_FAILURE;
ssl->options.useDtlsCID = 1;
ret = TLSX_ConnectionID_Use(ssl);
if (ret != 0)
return ret;
return WOLFSSL_SUCCESS;
}
int wolfSSL_dtls_cid_is_enabled(WOLFSSL* ssl)
{
return DtlsCidGetInfo(ssl) != NULL;
}
int wolfSSL_dtls_cid_set(WOLFSSL* ssl, unsigned char* cid, unsigned int size)
{
ConnectionID* newCid;
CIDInfo* cidInfo;
if (!ssl->options.useDtlsCID)
return WOLFSSL_FAILURE;
cidInfo = DtlsCidGetInfo(ssl);
if (cidInfo == NULL)
return WOLFSSL_FAILURE;
if (cidInfo->rx != NULL) {
XFREE(cidInfo->rx, ssl->heap, DYNAMIC_TYPE_TLSX);
cidInfo->rx = NULL;
}
/* empty CID */
if (size == 0)
return WOLFSSL_SUCCESS;
if (size > DTLS_CID_MAX_SIZE)
return LENGTH_ERROR;
newCid = DtlsCidNew(cid, (byte)size, ssl->heap);
if (newCid == NULL)
return MEMORY_ERROR;
cidInfo->rx = newCid;
return WOLFSSL_SUCCESS;
}
int wolfSSL_dtls_cid_get_rx_size(WOLFSSL* ssl, unsigned int* size)
{
return DtlsCidGetSize(ssl, size, 1);
}
int wolfSSL_dtls_cid_get_rx(WOLFSSL* ssl, unsigned char* buf,
unsigned int bufferSz)
{
return DtlsCidGet(ssl, buf, bufferSz, 1);
}
int wolfSSL_dtls_cid_get_tx_size(WOLFSSL* ssl, unsigned int* size)
{
return DtlsCidGetSize(ssl, size, 0);
}
int wolfSSL_dtls_cid_get_tx(WOLFSSL* ssl, unsigned char* buf,
unsigned int bufferSz)
{
return DtlsCidGet(ssl, buf, bufferSz, 0);
}
#endif /* WOLFSSL_DTLS_CID */
#endif /* WOLFSSL_DTLS */
#endif /* WOLFCRYPT_ONLY */