forked from wolfSSL/wolfssl
1097 lines
32 KiB
C
1097 lines
32 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
|
|
* 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
|
|
* less). On the other hand, if a valid SessionID is collected, forged
|
|
* clientHello messages will consume resources on the server.
|
|
*/
|
|
|
|
#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;
|
|
|
|
/* 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->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;
|
|
}
|
|
|
|
#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 */
|
|
|
|
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;
|
|
const byte* msg;
|
|
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->cookie.size < OPAQUE16_LEN + 1)
|
|
return BUFFER_E;
|
|
ato16(ch->cookie.elements, &len);
|
|
if (ch->cookie.size - OPAQUE16_LEN != len)
|
|
return BUFFER_E;
|
|
ret = TlsCheckCookie(ssl, ch->cookie.elements + OPAQUE16_LEN,
|
|
ch->cookie.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)
|
|
{
|
|
word32 idx = 0;
|
|
|
|
/* protocol version, random and session id length check */
|
|
if (OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN > helloSz)
|
|
return BUFFER_ERROR;
|
|
|
|
ch->msg = input - DTLS_HANDSHAKE_HEADER_SZ;
|
|
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)
|
|
return BUFFER_ERROR;
|
|
idx += ReadVector16(input + idx, &ch->extension);
|
|
if (idx > helloSz)
|
|
return BUFFER_ERROR;
|
|
ch->length = idx + DTLS_HANDSHAKE_HEADER_SZ;
|
|
return 0;
|
|
}
|
|
|
|
#if (defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME) && defined(HAVE_SESSION_TICKET)) \
|
|
|| defined(WOLFSSL_DTLS13)
|
|
static int TlsxFindByType(WolfSSL_ConstVector* ret, word16 extType,
|
|
WolfSSL_ConstVector exts)
|
|
{
|
|
word32 len, idx = 0;
|
|
word16 type;
|
|
WolfSSL_ConstVector ext;
|
|
|
|
XMEMSET(ret, 0, sizeof(*ret));
|
|
len = exts.size;
|
|
/* 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));
|
|
return 0;
|
|
}
|
|
len = exts.size - idx;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME
|
|
#ifdef HAVE_SESSION_TICKET
|
|
static int TlsTicketIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector exts,
|
|
PskInfo* pskInfo)
|
|
{
|
|
WolfSSL_ConstVector tlsxSessionTicket;
|
|
byte tempTicket[SESSION_TICKET_LEN];
|
|
InternalTicket* it;
|
|
int ret;
|
|
|
|
ret = TlsxFindByType(&tlsxSessionTicket, TLSX_SESSION_TICKET, exts);
|
|
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((WOLFSSL*)ssl, tempTicket,
|
|
(word32)tlsxSessionTicket.size, &it);
|
|
if (ret != WOLFSSL_TICKET_RET_OK && ret != WOLFSSL_TICKET_RET_CREATE)
|
|
return 0;
|
|
/* Store info for later */
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
|
|
pskInfo->pv = it->pv;
|
|
#endif
|
|
pskInfo->cipherSuite0 = it->suite[0];
|
|
pskInfo->cipherSuite = it->suite[1];
|
|
ato16(it->namedGroup, &pskInfo->namedGroup);
|
|
|
|
ForceZero(it, sizeof(InternalTicket));
|
|
pskInfo->isValid = 1;
|
|
return 0;
|
|
}
|
|
#endif /* HAVE_SESSION_TICKET */
|
|
|
|
static int TlsSessionIdIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector sessionID,
|
|
PskInfo* pskInfo)
|
|
{
|
|
WOLFSSL_SESSION* sess;
|
|
word32 sessRow;
|
|
int ret;
|
|
if (ssl->options.sessionCacheOff)
|
|
return 0;
|
|
if (sessionID.size != ID_LEN)
|
|
return 0;
|
|
#ifdef HAVE_EXT_CACHE
|
|
{
|
|
|
|
if (ssl->ctx->get_sess_cb != NULL) {
|
|
int unused;
|
|
sess =
|
|
ssl->ctx->get_sess_cb((WOLFSSL*)ssl, sessionID.elements, ID_LEN,
|
|
&unused);
|
|
if (sess != NULL) {
|
|
/* Store info for later */
|
|
pskInfo->pv = sess->version;
|
|
pskInfo->cipherSuite0 = sess->cipherSuite0;
|
|
pskInfo->cipherSuite = sess->cipherSuite;
|
|
pskInfo->namedGroup = sess->namedGroup;
|
|
|
|
pskInfo->isValid = 1;
|
|
wolfSSL_FreeSession(ssl->ctx, sess);
|
|
return 0;
|
|
}
|
|
}
|
|
if (ssl->ctx->internalCacheLookupOff)
|
|
return 0;
|
|
}
|
|
#endif
|
|
ret = TlsSessionCacheGetAndLock(sessionID.elements, &sess, &sessRow, 1);
|
|
if (ret == 0 && sess != NULL) {
|
|
/* Store info for later */
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
|
|
pskInfo->pv = sess->version;
|
|
#endif
|
|
pskInfo->cipherSuite0 = sess->cipherSuite0;
|
|
pskInfo->cipherSuite = sess->cipherSuite;
|
|
pskInfo->namedGroup = sess->namedGroup;
|
|
|
|
pskInfo->isValid = 1;
|
|
TlsSessionCacheUnlockRow(sessRow);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int TlsResumptionIsValid(const WOLFSSL* ssl, WolfSSL_CH* ch,
|
|
PskInfo* pskInfo)
|
|
{
|
|
int ret;
|
|
|
|
#ifdef HAVE_SESSION_TICKET
|
|
ret = TlsTicketIsValid(ssl, ch->extension, pskInfo);
|
|
if (ret != 0)
|
|
return ret;
|
|
if (pskInfo->isValid)
|
|
return 0;
|
|
#endif /* HAVE_SESSION_TICKET */
|
|
ret = TlsSessionIdIsValid(ssl, ch->sessionId, pskInfo);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_DTLS_NO_HVR_ON_RESUME */
|
|
|
|
#ifdef WOLFSSL_DTLS13
|
|
static int TlsCheckSupportedVersion(const WOLFSSL* ssl,
|
|
WolfSSL_CH* ch, byte *isTls13, PskInfo* pskInfo)
|
|
{
|
|
WolfSSL_ConstVector tlsxSupportedVersions;
|
|
int ret;
|
|
ProtocolVersion pv = ssl->version;
|
|
|
|
(void)pskInfo;
|
|
|
|
ret = TlsxFindByType(&tlsxSupportedVersions, TLSX_SUPPORTED_VERSIONS,
|
|
ch->extension);
|
|
if (ret != 0)
|
|
return ret;
|
|
if (tlsxSupportedVersions.size == 0) {
|
|
*isTls13 = 0;
|
|
return 0;
|
|
}
|
|
ret = TLSX_SupportedVersions_Parse(ssl, tlsxSupportedVersions.elements,
|
|
tlsxSupportedVersions.size, client_hello, &pv, NULL, NULL);
|
|
if (ret != 0)
|
|
return ret;
|
|
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
|
|
if (pskInfo->isValid && (pskInfo->pv.major != pv.major ||
|
|
pskInfo->pv.minor != pv.minor))
|
|
return VERSION_ERROR;
|
|
#endif
|
|
if (IsAtLeastTLSv1_3(pv))
|
|
*isTls13 = 1;
|
|
else
|
|
*isTls13 = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int CopyExtensions(TLSX* src, TLSX** dst, void* heap)
|
|
{
|
|
/* Copy the following extensions
|
|
* * SupportedCurves */
|
|
TLSX* extension;
|
|
int ret;
|
|
|
|
extension = TLSX_Find(src, TLSX_SUPPORTED_GROUPS);
|
|
if (extension != NULL) {
|
|
SupportedCurve* curve;
|
|
curve = (SupportedCurve*)extension->data;
|
|
for (curve = (SupportedCurve*)extension->data; curve != NULL;
|
|
curve = curve->next) {
|
|
ret = TLSX_UseSupportedCurve(dst, curve->name, heap);
|
|
if (ret != WOLFSSL_SUCCESS)
|
|
return MEMORY_E;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int SendStatelessReply(const WOLFSSL* ssl, WolfSSL_CH* ch, byte isTls13,
|
|
PskInfo* pskInfo)
|
|
{
|
|
int ret;
|
|
(void)isTls13;
|
|
(void)pskInfo;
|
|
#ifdef WOLFSSL_DTLS13
|
|
if (isTls13) {
|
|
#ifdef WOLFSSL_SEND_HRR_COOKIE
|
|
if (ch->cookie.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;
|
|
|
|
XMEMSET(&cs, 0, sizeof(cs));
|
|
XMEMSET(&specs, 0, sizeof(specs));
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* Hashes are reset in SendTls13ServerHello when sending a HRR */
|
|
ret = Dtls13HashHandshake((WOLFSSL*)ssl, ch->msg, ch->length);
|
|
if (ret != 0)
|
|
goto dtls13_cleanup;
|
|
|
|
/* Populate the suites struct to find a common ciphersuite */
|
|
XMEMSET(&suites, 0, sizeof(suites));
|
|
suites.suiteSz = 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;
|
|
|
|
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);
|
|
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,
|
|
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,
|
|
tlsx.size, &parsedExts);
|
|
if (ret != 0)
|
|
goto dtls13_cleanup;
|
|
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
|
|
haveKS = 1;
|
|
#endif
|
|
}
|
|
|
|
#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;
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
|
|
if (usePSK) {
|
|
if (pskInfo->isValid) {
|
|
cs.cipherSuite0 = pskInfo->cipherSuite0;
|
|
cs.cipherSuite = pskInfo->cipherSuite;
|
|
}
|
|
else {
|
|
/* Only support the default ciphersuite for PSK */
|
|
cs.cipherSuite0 = TLS13_BYTE;
|
|
cs.cipherSuite = WOLFSSL_DEF_PSK_CIPHER;
|
|
}
|
|
|
|
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 (ret != 0)
|
|
goto dtls13_cleanup;
|
|
}
|
|
|
|
/* Need to remove the keyshare ext if we are not doing PSK and we
|
|
* found a common group. */
|
|
if (cs.clientKSE != NULL
|
|
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
|
|
&& !usePSK
|
|
#endif
|
|
)
|
|
{
|
|
TLSX_Remove(&parsedExts, TLSX_KEY_SHARE, ssl->heap);
|
|
}
|
|
|
|
{
|
|
WOLFSSL* nonConstSSL = (WOLFSSL*)ssl;
|
|
TLSX* sslExts = nonConstSSL->extensions;
|
|
|
|
/* This is required to correctly generate the hash */
|
|
ret = SetCipherSpecs_ex(WOLFSSL_SERVER_END, cs.cipherSuite0,
|
|
cs.cipherSuite, &nonConstSSL->specs, NULL);
|
|
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 = 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;
|
|
|
|
InitCipherSpecs(&nonConstSSL->specs);
|
|
|
|
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);
|
|
#else
|
|
#error "WOLFSSL_SEND_HRR_COOKIE has to be defined to use DTLS 1.3 server"
|
|
#endif
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int DoClientHelloStateless(WOLFSSL* ssl, const byte* input,
|
|
word32* inOutIdx, word32 helloSz)
|
|
{
|
|
int ret;
|
|
WolfSSL_CH ch;
|
|
byte isTls13 = 0;
|
|
PskInfo pskInfo;
|
|
|
|
XMEMSET(&pskInfo, 0, sizeof(pskInfo));
|
|
XMEMSET(&ch, 0, sizeof(ch));
|
|
|
|
ssl->options.dtlsStateful = 0;
|
|
ret = ParseClientHello(input + *inOutIdx, helloSz, &ch);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME
|
|
ret = TlsResumptionIsValid(ssl, &ch, &pskInfo);
|
|
if (ret != 0)
|
|
return ret;
|
|
if (pskInfo.isValid) {
|
|
ssl->options.dtlsStateful = 1;
|
|
return 0;
|
|
}
|
|
#endif /* WOLFSSL_DTLS_NO_HVR_ON_RESUME */
|
|
|
|
#ifdef WOLFSSL_DTLS13
|
|
if (IsAtLeastTLSv1_3(ssl->version)) {
|
|
ret = TlsCheckSupportedVersion(ssl, &ch, &isTls13, &pskInfo);
|
|
if (ret != 0)
|
|
return ret;
|
|
if (isTls13) {
|
|
ret = TlsxFindByType(&ch.cookie, TLSX_COOKIE, ch.extension);
|
|
if (ret != 0)
|
|
return ret;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (ch.cookie.size == 0) {
|
|
ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13, &pskInfo);
|
|
}
|
|
else {
|
|
byte cookieGood;
|
|
ret = CheckDtlsCookie(ssl, &ch, isTls13, &cookieGood);
|
|
if (ret != 0)
|
|
return ret;
|
|
if (!cookieGood)
|
|
ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13, &pskInfo);
|
|
else
|
|
ssl->options.dtlsStateful = 1;
|
|
}
|
|
|
|
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 te 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 */
|