TLS 1.3: hold decrypted ticket to check which ciphersuite matches

DTLS 1.3: Move stateless ticket decoding to FindPskSuiteFromExt
This commit is contained in:
Juliusz Sosinowicz
2023-02-10 16:31:36 +01:00
parent 8c08dbb6ce
commit 5f39c594aa
5 changed files with 535 additions and 284 deletions

View File

@@ -291,23 +291,16 @@ static int TlsxFindByType(WolfSSL_ConstVector* ret, word16 extType,
} }
#endif #endif
#if defined(WOLFSSL_DTLS13) || defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME) #if defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME)
#ifdef HAVE_SESSION_TICKET #ifdef HAVE_SESSION_TICKET
static int TlsTicketIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector exts, static int TlsTicketIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector exts,
PskInfo* pskInfo, byte isTls13) int* resume)
{ {
WolfSSL_ConstVector tlsxSessionTicket; WolfSSL_ConstVector tlsxSessionTicket;
byte tempTicket[SESSION_TICKET_LEN]; byte tempTicket[SESSION_TICKET_LEN];
InternalTicket* it; InternalTicket* it = NULL;
int ret; int ret = 0;
(void)isTls13;
#if defined(WOLFSSL_TLS13) && !defined(NO_PSK)
if (isTls13)
ret = TlsxFindByType(&tlsxSessionTicket, TLSX_PRE_SHARED_KEY, exts);
else
#endif
ret = TlsxFindByType(&tlsxSessionTicket, TLSX_SESSION_TICKET, exts); ret = TlsxFindByType(&tlsxSessionTicket, TLSX_SESSION_TICKET, exts);
if (ret != 0) if (ret != 0)
return ret; return ret;
@@ -316,26 +309,20 @@ static int TlsTicketIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector exts,
if (tlsxSessionTicket.size > SESSION_TICKET_LEN) if (tlsxSessionTicket.size > SESSION_TICKET_LEN)
return 0; return 0;
XMEMCPY(tempTicket, tlsxSessionTicket.elements, tlsxSessionTicket.size); XMEMCPY(tempTicket, tlsxSessionTicket.elements, tlsxSessionTicket.size);
ret = DoDecryptTicket((WOLFSSL*)ssl, tempTicket, ret = DoDecryptTicket(ssl, tempTicket, (word32)tlsxSessionTicket.size, &it);
(word32)tlsxSessionTicket.size, &it); if (ret == WOLFSSL_TICKET_RET_OK || ret == WOLFSSL_TICKET_RET_CREATE) {
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. */
return 0; if (!IsAtLeastTLSv1_3(it->pv))
/* Store info for later */ *resume = TRUE;
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) }
pskInfo->pv = it->pv; if (it != NULL)
#endif
pskInfo->cipherSuite0 = it->suite[0];
pskInfo->cipherSuite = it->suite[1];
ato16(it->namedGroup, &pskInfo->namedGroup);
ForceZero(it, sizeof(InternalTicket)); ForceZero(it, sizeof(InternalTicket));
pskInfo->isValid = 1;
return 0; return 0;
} }
#endif /* HAVE_SESSION_TICKET */ #endif /* HAVE_SESSION_TICKET */
static int TlsSessionIdIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector sessionID, static int TlsSessionIdIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector sessionID,
PskInfo* pskInfo) int* resume)
{ {
WOLFSSL_SESSION* sess; WOLFSSL_SESSION* sess;
word32 sessRow; word32 sessRow;
@@ -353,34 +340,36 @@ static int TlsSessionIdIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector sessionID
ssl->ctx->get_sess_cb((WOLFSSL*)ssl, sessionID.elements, ID_LEN, ssl->ctx->get_sess_cb((WOLFSSL*)ssl, sessionID.elements, ID_LEN,
&unused); &unused);
if (sess != NULL) { if (sess != NULL) {
/* Store info for later */ #if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) defined(HAVE_SESSION_TICKET))
pskInfo->pv = sess->version; /* This logic is only for TLS <= 1.2 tickets. Don't accept
* TLS 1.3. */
if (IsAtLeastTLSv1_3(sess->version))
wolfSSL_FreeSession(ssl->ctx, sess);
else
#endif #endif
pskInfo->cipherSuite0 = sess->cipherSuite0; {
pskInfo->cipherSuite = sess->cipherSuite; *resume = 1;
pskInfo->namedGroup = sess->namedGroup;
pskInfo->isValid = 1;
wolfSSL_FreeSession(ssl->ctx, sess); wolfSSL_FreeSession(ssl->ctx, sess);
return 0; return 0;
} }
} }
}
if (ssl->ctx->internalCacheLookupOff) if (ssl->ctx->internalCacheLookupOff)
return 0; return 0;
} }
#endif #endif
ret = TlsSessionCacheGetAndLock(sessionID.elements, &sess, &sessRow, 1); ret = TlsSessionCacheGetAndLock(sessionID.elements, &sess, &sessRow, 1);
if (ret == 0 && sess != NULL) { if (ret == 0 && sess != NULL) {
/* Store info for later */ #if defined(SESSION_CERTS) || (defined(WOLFSSL_TLS13) && \
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) defined(HAVE_SESSION_TICKET))
pskInfo->pv = sess->version; /* This logic is only for TLS <= 1.2 tickets. Don't accept
* TLS 1.3. */
if (!IsAtLeastTLSv1_3(sess->version))
#endif #endif
pskInfo->cipherSuite0 = sess->cipherSuite0; {
pskInfo->cipherSuite = sess->cipherSuite; *resume = 1;
pskInfo->namedGroup = sess->namedGroup; }
pskInfo->isValid = 1;
TlsSessionCacheUnlockRow(sessRow); TlsSessionCacheUnlockRow(sessRow);
} }
@@ -388,19 +377,18 @@ static int TlsSessionIdIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector sessionID
} }
static int TlsResumptionIsValid(const WOLFSSL* ssl, WolfSSL_CH* ch, static int TlsResumptionIsValid(const WOLFSSL* ssl, WolfSSL_CH* ch,
PskInfo* pskInfo, byte isTls13) int* resume)
{ {
int ret; int ret;
(void)isTls13;
#ifdef HAVE_SESSION_TICKET #ifdef HAVE_SESSION_TICKET
ret = TlsTicketIsValid(ssl, ch->extension, pskInfo, isTls13); ret = TlsTicketIsValid(ssl, ch->extension, resume);
if (ret != 0) if (ret != 0)
return ret; return ret;
if (pskInfo->isValid) if (*resume)
return 0; return 0;
#endif /* HAVE_SESSION_TICKET */ #endif /* HAVE_SESSION_TICKET */
ret = TlsSessionIdIsValid(ssl, ch->sessionId, pskInfo); ret = TlsSessionIdIsValid(ssl, ch->sessionId, resume);
return ret; return ret;
} }
#endif /* WOLFSSL_DTLS13 || WOLFSSL_DTLS_NO_HVR_ON_RESUME */ #endif /* WOLFSSL_DTLS13 || WOLFSSL_DTLS_NO_HVR_ON_RESUME */
@@ -454,18 +442,16 @@ static int CopySupportedGroup(TLSX* src, TLSX** dst, void* heap)
} }
#endif #endif
#if defined(WOLFSSL_DTLS13) && !defined(NO_PSK) #if defined(WOLFSSL_DTLS13) && \
(!defined(NO_PSK) || defined(HAVE_SESSION_TICKET))
/* Very simplified version of CheckPreSharedKeys to find the current suite */ /* Very simplified version of CheckPreSharedKeys to find the current suite */
static void FindPskSuiteFromExt(const WOLFSSL* ssl, TLSX* extensions, static void FindPskSuiteFromExt(const WOLFSSL* ssl, TLSX* extensions,
PskInfo* pskInfo, Suites* suites) PskInfo* pskInfo, Suites* suites)
{ {
TLSX* pskExt = TLSX_Find(extensions, TLSX_PRE_SHARED_KEY); TLSX* pskExt = TLSX_Find(extensions, TLSX_PRE_SHARED_KEY);
int found = 0;
PreSharedKey* current; PreSharedKey* current;
byte psk_key[MAX_PSK_KEY_LEN];
word32 psk_keySz;
int i; int i;
byte foundSuite[SUITE_LEN]; int ret;
if (pskExt == NULL) if (pskExt == NULL)
return; return;
@@ -473,17 +459,61 @@ static void FindPskSuiteFromExt(const WOLFSSL* ssl, TLSX* extensions,
for (i = 0; i < suites->suiteSz; i += 2) { for (i = 0; i < suites->suiteSz; i += 2) {
for (current = (PreSharedKey*)pskExt->data; current != NULL; for (current = (PreSharedKey*)pskExt->data; current != NULL;
current = current->next) { current = current->next) {
if (FindPskSuite(ssl, current, psk_key, &psk_keySz, #ifdef HAVE_SESSION_TICKET
suites->suites + i, &found, foundSuite) == 0) { {
if (found) { /* Decode the identity. */
switch (current->decryptRet) {
case PSK_DECRYPT_NONE:
ret = DoClientTicket_ex(ssl, current);
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:
ret = WOLFSSL_TICKET_RET_REJECT;
break;
}
if (ret == WOLFSSL_TICKET_RET_OK) {
if (DoClientTicketCheck(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->cipherSuite0 = foundSuite[0];
pskInfo->cipherSuite = foundSuite[1]; pskInfo->cipherSuite = foundSuite[1];
pskInfo->isValid = 1; pskInfo->isValid = 1;
return; goto cleanup;
} }
} }
#endif
} }
} }
cleanup:
#ifdef HAVE_SESSION_TICKET
CleanupClientTickets((PreSharedKey*)pskExt->data);
#endif
} }
#endif #endif
@@ -493,8 +523,12 @@ static void FindPskSuiteFromExt(const WOLFSSL* ssl, TLSX* extensions,
#error "WOLFSSL_SEND_HRR_COOKIE has to be defined to use DTLS 1.3 server" #error "WOLFSSL_SEND_HRR_COOKIE has to be defined to use DTLS 1.3 server"
#endif #endif
static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch, #ifdef WOLFSSL_PSK_ONE_ID
PskInfo* pskInfo) #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; int ret = -1;
TLSX* parsedExts = NULL; TLSX* parsedExts = NULL;
@@ -511,8 +545,10 @@ static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch,
CipherSpecs specs; CipherSpecs specs;
byte cookieHash[WC_MAX_DIGEST_SIZE]; byte cookieHash[WC_MAX_DIGEST_SIZE];
int cookieHashSz; int cookieHashSz;
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
(void)pskInfo; PskInfo pskInfo;
XMEMSET(&pskInfo, 0, sizeof(pskInfo));
#endif
XMEMSET(&cs, 0, sizeof(cs)); XMEMSET(&cs, 0, sizeof(cs));
@@ -601,18 +637,13 @@ static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch,
* and if they don't match we will error out there anyway. */ * and if they don't match we will error out there anyway. */
byte modes; byte modes;
#ifndef NO_PSK /* Ask the user for the ciphersuite matching this identity */
/* 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, if (TLSX_PreSharedKey_Parse_ClientHello(&parsedExts,
tlsx.elements, tlsx.size, ssl->heap) == 0) tlsx.elements, tlsx.size, ssl->heap) == 0)
FindPskSuiteFromExt(ssl, parsedExts, pskInfo, &suites); FindPskSuiteFromExt(ssl, parsedExts, &pskInfo, &suites);
/* Revert to full handshake if PSK parsing failed */ /* Revert to full handshake if PSK parsing failed */
}
#endif
if (pskInfo->isValid) { if (pskInfo.isValid) {
ret = TlsxFindByType(&tlsx, TLSX_PSK_KEY_EXCHANGE_MODES, ret = TlsxFindByType(&tlsx, TLSX_PSK_KEY_EXCHANGE_MODES,
ch->extension); ch->extension);
if (ret != 0) if (ret != 0)
@@ -638,9 +669,9 @@ static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch,
#endif #endif
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
if (usePSK && pskInfo->isValid) { if (usePSK && pskInfo.isValid) {
cs.cipherSuite0 = pskInfo->cipherSuite0; cs.cipherSuite0 = pskInfo.cipherSuite0;
cs.cipherSuite = pskInfo->cipherSuite; cs.cipherSuite = pskInfo.cipherSuite;
if (haveSG && !haveKS) { if (haveSG && !haveKS) {
WOLFSSL_MSG("Client didn't send KeyShare or Supported Groups."); WOLFSSL_MSG("Client didn't send KeyShare or Supported Groups.");
@@ -737,15 +768,13 @@ dtls13_cleanup:
} }
#endif #endif
static int SendStatelessReply(const WOLFSSL* ssl, WolfSSL_CH* ch, byte isTls13, static int SendStatelessReply(const WOLFSSL* ssl, WolfSSL_CH* ch, byte isTls13)
PskInfo* pskInfo)
{ {
int ret; int ret;
(void)isTls13; (void)isTls13;
(void)pskInfo;
#ifdef WOLFSSL_DTLS13 #ifdef WOLFSSL_DTLS13
if (isTls13) { if (isTls13) {
ret = SendStatelessReplyDtls13(ssl, ch, pskInfo); ret = SendStatelessReplyDtls13(ssl, ch);
} }
else else
#endif #endif
@@ -789,9 +818,7 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input,
int ret; int ret;
WolfSSL_CH ch; WolfSSL_CH ch;
byte isTls13 = 0; byte isTls13 = 0;
PskInfo pskInfo;
XMEMSET(&pskInfo, 0, sizeof(pskInfo));
XMEMSET(&ch, 0, sizeof(ch)); XMEMSET(&ch, 0, sizeof(ch));
ssl->options.dtlsStateful = 0; ssl->options.dtlsStateful = 0;
@@ -816,33 +843,21 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input,
if (ret != 0) if (ret != 0)
return ret; return ret;
#if defined(WOLFSSL_DTLS13) || defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME) #ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME
ret = TlsResumptionIsValid(ssl, &ch, &pskInfo, isTls13); if (!isTls13) {
int resume = FALSE;
ret = TlsResumptionIsValid(ssl, &ch, &resume);
if (ret != 0) if (ret != 0)
return ret; return ret;
#endif if (resume) {
#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME
if (pskInfo.isValid) {
ssl->options.dtlsStateful = 1; ssl->options.dtlsStateful = 1;
return 0; return 0;
} }
#endif
#if defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET)
if (pskInfo.isValid) {
if (IsAtLeastTLSv1_3(pskInfo.pv)) {
if (!isTls13)
return VERSION_ERROR;
}
else {
if (isTls13)
return VERSION_ERROR;
}
} }
#endif #endif
if (ch.cookie.size == 0 && ch.cookieExt.size == 0) { if (ch.cookie.size == 0 && ch.cookieExt.size == 0) {
ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13, &pskInfo); ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13);
} }
else { else {
byte cookieGood; byte cookieGood;
@@ -857,7 +872,7 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input,
ret = INVALID_PARAMETER; ret = INVALID_PARAMETER;
else else
#endif #endif
ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13, &pskInfo); ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13);
} }
else else
ssl->options.dtlsStateful = 1; ssl->options.dtlsStateful = 1;

View File

@@ -34547,7 +34547,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
} }
int DoDecryptTicket(WOLFSSL* ssl, const byte* input, word32 len, int DoDecryptTicket(const WOLFSSL* ssl, const byte* input, word32 len,
InternalTicket **it) InternalTicket **it)
{ {
ExternalTicket* et; ExternalTicket* et;
@@ -34589,7 +34589,9 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
ret = WOLFSSL_TICKET_RET_REJECT; ret = WOLFSSL_TICKET_RET_REJECT;
} }
else { else {
ret = ssl->ctx->ticketEncCb(ssl, et->key_name, et->iv, /* Callback uses ssl without const but for DTLS, it really shouldn't
* modify its state. */
ret = ssl->ctx->ticketEncCb((WOLFSSL*)ssl, et->key_name, et->iv,
et->enc_ticket + inLen, 0, et->enc_ticket + inLen, 0,
et->enc_ticket, inLen, &outLen, et->enc_ticket, inLen, &outLen,
ssl->ctx->ticketEncCtx); ssl->ctx->ticketEncCtx);
@@ -34614,58 +34616,108 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
return ret; return ret;
} }
/* Parse ticket sent by client, returns callback return value */ static int DoClientTicketCheckVersion(const WOLFSSL* ssl,
int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len) InternalTicket* it)
{ {
InternalTicket* it;
int ret;
WOLFSSL_START(WC_FUNC_TICKET_DO);
WOLFSSL_ENTER("DoClientTicket");
ret = DoDecryptTicket(ssl, input, len, &it);
if (ret != WOLFSSL_TICKET_RET_OK && ret != WOLFSSL_TICKET_RET_CREATE) {
WOLFSSL_LEAVE("DoClientTicket", ret);
return ret;
}
#ifdef WOLFSSL_CHECK_MEM_ZERO
/* Internal ticket successfully decrypted. */
wc_MemZero_Add("Do Client Ticket internal", it, sizeof(InternalTicket));
#endif
/* get master secret */
if (ret == WOLFSSL_TICKET_RET_OK || ret == WOLFSSL_TICKET_RET_CREATE) {
if (ssl->version.minor < it->pv.minor) { if (ssl->version.minor < it->pv.minor) {
WOLFSSL_MSG("Ticket has greater version"); WOLFSSL_MSG("Ticket has greater version");
ret = VERSION_ERROR; return VERSION_ERROR;
goto error;
} }
else if (ssl->version.minor > it->pv.minor) { else if (ssl->version.minor > it->pv.minor) {
if (IsAtLeastTLSv1_3(it->pv) != IsAtLeastTLSv1_3(ssl->version)) { if (IsAtLeastTLSv1_3(it->pv) != IsAtLeastTLSv1_3(ssl->version)) {
WOLFSSL_MSG("Tickets cannot be shared between " WOLFSSL_MSG("Tickets cannot be shared between "
"TLS 1.3 and TLS 1.2 and lower"); "TLS 1.3 and TLS 1.2 and lower");
ret = VERSION_ERROR; return VERSION_ERROR;
goto error;
} }
if (!ssl->options.downgrade) { if (!ssl->options.downgrade) {
WOLFSSL_MSG("Ticket has lesser version"); WOLFSSL_MSG("Ticket has lesser version");
ret = VERSION_ERROR; return VERSION_ERROR;
goto error;
} }
WOLFSSL_MSG("Downgrading protocol due to ticket"); WOLFSSL_MSG("Downgrading protocol due to ticket");
if (it->pv.minor < ssl->options.minDowngrade) { if (it->pv.minor < ssl->options.minDowngrade) {
WOLFSSL_MSG("Ticket has lesser version than allowed"); WOLFSSL_MSG("Ticket has lesser version than allowed");
ret = VERSION_ERROR; return VERSION_ERROR;
goto error;
} }
ssl->version.minor = it->pv.minor; }
/* Check resumption master secret. */
if (IsAtLeastTLSv1_3(it->pv) &&
it->ticketNonceLen > MAX_TICKET_NONCE_STATIC_SZ) {
WOLFSSL_MSG("Unsupported ticketNonce len in ticket");
return BAD_TICKET_ENCRYPT;
}
return 0;
} }
#ifdef WOLFSSL_TICKET_HAVE_ID /* Return 0 when check successful. <0 on failure. */
int DoClientTicketCheck(const PreSharedKey* psk, sword64 timeout,
const byte* suite)
{ {
word32 ticketAdd;
#ifdef WOLFSSL_32BIT_MILLI_TIME
word32 now;
sword64 diff;
word32 ticketSeen; /* Time ticket seen (ms) */
ato32(psk->it->timestamp, &ticketSeen);
now = TimeNowInMilliseconds();
if (now == 0)
return GETTIME_ERROR;
/* Difference between now and time ticket constructed
* (from decrypted ticket). */
diff = now;
diff -= ticketSeen;
if (diff > timeout * 1000 ||
diff > (sword64)TLS13_MAX_TICKET_AGE * 1000)
return -1;
#else
sword64 diff;
sword64 ticketSeen; /* Time ticket seen (ms) */
word32 seenHi, seenLo;
ato32(psk->it->timestamp , &seenHi);
ato32(psk->it->timestamp + OPAQUE32_LEN, &seenLo);
ticketSeen = ((sword64)seenHi << 32) + seenLo;
diff = TimeNowInMilliseconds();
if (diff == 0)
return GETTIME_ERROR;
/* Difference between now and time ticket constructed
* (from decrypted ticket). */
diff -= ticketSeen;
if (diff > timeout * 1000 ||
diff > (sword64)TLS13_MAX_TICKET_AGE * 1000)
return -1;
#endif
ato32(psk->it->ageAdd, &ticketAdd);
/* Subtract client's ticket age and unobfuscate. */
diff -= psk->ticketAge;
diff += ticketAdd;
/* Check session and ticket age timeout.
* Allow +/- 1000 milliseconds on ticket age.
*/
if (diff < -1000 || diff - MAX_TICKET_AGE_DIFF * 1000 > 1000)
return -1;
#ifndef WOLFSSL_PSK_ONE_ID
/* Check whether resumption is possible based on suites in SSL and
* ciphersuite in ticket.
*/
if (XMEMCMP(suite, psk->it->suite, SUITE_LEN) != 0)
return -1;
#else
if (!FindSuiteSSL(ssl, psk->it->suite))
return -1;
#endif
return 0;
}
void DoClientTicketFinalize(WOLFSSL* ssl, InternalTicket* it)
{
#ifdef WOLFSSL_TICKET_HAVE_ID
ssl->session->haveAltSessionID = 1; ssl->session->haveAltSessionID = 1;
XMEMCPY(ssl->session->altSessionID, it->id, ID_LEN); XMEMCPY(ssl->session->altSessionID, it->id, ID_LEN);
if (wolfSSL_GetSession(ssl, NULL, 1) != NULL) { if (wolfSSL_GetSession(ssl, NULL, 1) != NULL) {
@@ -34676,7 +34728,6 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
WOLFSSL_MSG("Can't find session matching the session id" WOLFSSL_MSG("Can't find session matching the session id"
" found in the ticket"); " found in the ticket");
} }
}
#endif #endif
if (!IsAtLeastTLSv1_3(ssl->version)) { if (!IsAtLeastTLSv1_3(ssl->version)) {
@@ -34685,36 +34736,37 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
* the saved session, so the property may be checked later. */ * the saved session, so the property may be checked later. */
ssl->session->haveEMS = it->haveEMS; ssl->session->haveEMS = it->haveEMS;
ato32((const byte*)&it->timestamp, &ssl->session->bornOn); ato32((const byte*)&it->timestamp, &ssl->session->bornOn);
#ifndef NO_RESUME_SUITE_CHECK #ifndef NO_RESUME_SUITE_CHECK
ssl->session->cipherSuite0 = it->suite[0]; ssl->session->cipherSuite0 = it->suite[0];
ssl->session->cipherSuite = it->suite[1]; ssl->session->cipherSuite = it->suite[1];
#endif #endif
} }
else { else {
#ifdef WOLFSSL_TLS13 #ifdef WOLFSSL_TLS13
/* This should have been already checked in
* DoClientTicketCheckVersion */
if (it->ticketNonceLen > MAX_TICKET_NONCE_STATIC_SZ) {
WOLFSSL_MSG("Unsupported ticketNonce len in ticket");
return;
}
/* Restore information to renegotiate. */ /* Restore information to renegotiate. */
#ifdef WOLFSSL_32BIT_MILLI_TIME #ifdef WOLFSSL_32BIT_MILLI_TIME
ato32(it->timestamp, &ssl->session->ticketSeen); ato32(it->timestamp, &ssl->session->ticketSeen);
#else #else
word32 seenHi, seenLo; word32 seenHi, seenLo;
ato32(it->timestamp , &seenHi); ato32(it->timestamp , &seenHi);
ato32(it->timestamp + OPAQUE32_LEN, &seenLo); ato32(it->timestamp + OPAQUE32_LEN, &seenLo);
ssl->session->ticketSeen = ((sword64)seenHi << 32) + seenLo; ssl->session->ticketSeen = ((sword64)seenHi << 32) + seenLo;
#endif #endif
ato32(it->ageAdd, &ssl->session->ticketAdd); ato32(it->ageAdd, &ssl->session->ticketAdd);
ssl->session->cipherSuite0 = it->suite[0]; ssl->session->cipherSuite0 = it->suite[0];
ssl->session->cipherSuite = it->suite[1]; ssl->session->cipherSuite = it->suite[1];
#ifdef WOLFSSL_EARLY_DATA #ifdef WOLFSSL_EARLY_DATA
ato32(it->maxEarlyDataSz, &ssl->session->maxEarlyDataSz); ato32(it->maxEarlyDataSz, &ssl->session->maxEarlyDataSz);
#endif #endif
/* Resumption master secret. */ /* Resumption master secret. */
XMEMCPY(ssl->session->masterSecret, it->msecret, SECRET_LEN); XMEMCPY(ssl->session->masterSecret, it->msecret, SECRET_LEN);
if (it->ticketNonceLen > MAX_TICKET_NONCE_STATIC_SZ) {
WOLFSSL_MSG("Unsupported ticketNonce len in ticket");
WOLFSSL_LEAVE("DoClientTicket", BAD_TICKET_ENCRYPT);
return BAD_TICKET_ENCRYPT;
}
#if defined(WOLFSSL_TICKET_NONCE_MALLOC) && \ #if defined(WOLFSSL_TICKET_NONCE_MALLOC) && \
(!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
if (ssl->session->ticketNonce.data if (ssl->session->ticketNonce.data
@@ -34731,25 +34783,98 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
ato16(it->namedGroup, &ssl->session->namedGroup); ato16(it->namedGroup, &ssl->session->namedGroup);
#endif #endif
} }
ssl->version.minor = it->pv.minor;
} }
ForceZero(it, sizeof(*it)); /* Parse ticket sent by client, returns callback return value. Doesn't
#ifdef WOLFSSL_CHECK_MEM_ZERO * modify ssl and stores the InternalTicket inside psk */
wc_MemZero_Check(it, sizeof(InternalTicket)); int DoClientTicket_ex(const WOLFSSL* ssl, PreSharedKey* psk)
#endif {
int decryptRet;
int ret;
WOLFSSL_LEAVE("DoClientTicket", ret); WOLFSSL_START(WC_FUNC_TICKET_DO);
WOLFSSL_END(WC_FUNC_TICKET_DO); WOLFSSL_ENTER("DoClientTicket_ex");
decryptRet = DoDecryptTicket(ssl, psk->identity, psk->identityLen,
&psk->it);
switch (decryptRet) {
case WOLFSSL_TICKET_RET_OK:
psk->decryptRet = PSK_DECRYPT_OK;
break;
case WOLFSSL_TICKET_RET_CREATE:
psk->decryptRet = PSK_DECRYPT_CREATE;
break;
default:
psk->decryptRet = PSK_DECRYPT_FAIL;
return decryptRet;
}
#ifdef WOLFSSL_CHECK_MEM_ZERO
/* Internal ticket successfully decrypted. */
wc_MemZero_Add("Do Client Ticket internal", it, sizeof(InternalTicket));
#endif
ret = DoClientTicketCheckVersion(ssl, psk->it);
if (ret != 0) {
psk->decryptRet = PSK_DECRYPT_FAIL;
return ret; return ret;
}
return decryptRet;
}
error: /* Parse ticket sent by client, returns callback return value */
int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len)
{
int decryptRet;
int ret;
InternalTicket* it
WOLFSSL_START(WC_FUNC_TICKET_DO);
WOLFSSL_ENTER("DoClientTicket");
decryptRet = DoDecryptTicket(ssl, input, len, &it);
if (decryptRet != WOLFSSL_TICKET_RET_OK &&
decryptRet != WOLFSSL_TICKET_RET_CREATE)
return decryptRet;
#ifdef WOLFSSL_CHECK_MEM_ZERO
/* Internal ticket successfully decrypted. */
wc_MemZero_Add("Do Client Ticket internal", it, sizeof(InternalTicket));
#endif
ret = DoClientTicketCheckVersion(ssl, it);
if (ret != 0) {
ForceZero(it, sizeof(*it)); ForceZero(it, sizeof(*it));
#ifdef WOLFSSL_CHECK_MEM_ZERO #ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(it, sizeof(InternalTicket)); wc_MemZero_Check(it, sizeof(InternalTicket));
#endif #endif
WOLFSSL_ERROR_VERBOSE(ret); return ret;
return WOLFSSL_TICKET_RET_REJECT; }
DoClientTicketFinalize(ssl, it);
ForceZero(it, sizeof(*it));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(it, sizeof(InternalTicket));
#endif
return decryptRet;
}
void CleanupClientTickets(PreSharedKey* psk)
{
for (; psk != NULL; psk = psk->next) {
if (psk->decryptRet == PSK_DECRYPT_OK ||
psk->decryptRet == PSK_DECRYPT_CREATE) {
psk->decryptRet = PSK_DECRYPT_NONE;
ForceZero(psk->identity, psk->identityLen);
#ifdef WOLFSSL_CHECK_MEM_ZERO
/* We want to check the InternalTicket area since that is what
* we registered in DoClientTicket_ex */
wc_MemZero_Check((((ExternalTicket*)psk->identity)->enc_ticket),
sizeof(InternalTicket));
#endif
}
}
} }

View File

@@ -5658,68 +5658,33 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz,
#ifdef HAVE_SESSION_TICKET #ifdef HAVE_SESSION_TICKET
/* Decode the identity. */ /* Decode the identity. */
ret = DoClientTicket(ssl, current->identity, current->identityLen); switch (current->decryptRet) {
case PSK_DECRYPT_NONE:
ret = DoClientTicket_ex(ssl, current);
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:
ret = WOLFSSL_TICKET_RET_REJECT;
break;
}
#ifdef WOLFSSL_ASYNC_CRYPT #ifdef WOLFSSL_ASYNC_CRYPT
if (ret == WC_PENDING_E) if (ret == WC_PENDING_E)
return ret; return ret;
#endif #endif
if (ret == WOLFSSL_TICKET_RET_OK) { if (ret == WOLFSSL_TICKET_RET_OK) {
#ifdef WOLFSSL_32BIT_MILLI_TIME if (DoClientTicketCheck(current, ssl->timeout, suite) != 0) {
word32 now; current = current->next;
sword64 diff;
now = TimeNowInMilliseconds();
if (now == 0)
return GETTIME_ERROR;
/* Difference between now and time ticket constructed
* (from decrypted ticket). */
diff = now;
diff -= ssl->session->ticketSeen;
if (diff > (sword64)ssl->timeout * 1000 ||
diff > (sword64)TLS13_MAX_TICKET_AGE * 1000) {
continue; continue;
} }
#else
sword64 diff;
diff = TimeNowInMilliseconds(); DoClientTicketFinalize(ssl, current->it);
if (diff == 0)
return GETTIME_ERROR;
/* Difference between now and time ticket constructed
* (from decrypted ticket). */
diff -= ssl->session->ticketSeen;
if (diff > (sword64)ssl->timeout * 1000 ||
diff > (sword64)TLS13_MAX_TICKET_AGE * 1000) {
continue;
}
#endif
/* Subtract client's ticket age and unobfuscate. */
diff -= current->ticketAge;
diff += ssl->session->ticketAdd;
/* Check session and ticket age timeout.
* Allow +/- 1000 milliseconds on ticket age.
*/
if (diff < -1000 || diff - MAX_TICKET_AGE_DIFF * 1000 > 1000)
continue;
#if !defined(WOLFSSL_PSK_ONE_ID) && !defined(WOLFSSL_PRIORITIZE_PSK)
/* Check whether resumption is possible based on suites in SSL and
* ciphersuite in ticket.
*/
if ((suite[0] != ssl->session->cipherSuite0) ||
(suite[1] != ssl->session->cipherSuite))
continue;
#else
{
byte s[2] = {
ssl->session->cipherSuite0,
ssl->session->cipherSuite,
};
if (!FindSuiteSSL(ssl, s))
continue;
}
#endif
/* SERVER: using secret in session ticket for peer auth. */ /* SERVER: using secret in session ticket for peer auth. */
ssl->options.peerAuthGood = 1; ssl->options.peerAuthGood = 1;
@@ -5893,13 +5858,23 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
ret = DoPreSharedKeys(ssl, input, helloSz - bindersLen, ret = DoPreSharedKeys(ssl, input, helloSz - bindersLen,
suites->suites + i, usingPSK, &first); suites->suites + i, usingPSK, &first);
if (ret != 0) { if (ret != 0) {
#ifdef HAVE_SESSION_TICKET
#ifdef WOLFSSL_ASYNC_CRYPT
if (ret != WC_PENDING_E)
#endif
CleanupClientTickets((PreSharedKey*)ext->data);
#endif
WOLFSSL_MSG_EX("DoPreSharedKeys: %d", ret); WOLFSSL_MSG_EX("DoPreSharedKeys: %d", ret);
return ret; return ret;
} }
} }
#ifdef HAVE_SESSION_TICKET
CleanupClientTickets((PreSharedKey*)ext->data);
#endif
#else #else
ret = DoPreSharedKeys(ssl, input, helloSz - bindersLen, suite, usingPSK, ret = DoPreSharedKeys(ssl, input, helloSz - bindersLen, suite, usingPSK,
&first); &first);
CleanupClientTickets((PreSharedKey*)ext->data);
if (ret != 0) { if (ret != 0) {
WOLFSSL_MSG_EX("DoPreSharedKeys: %d", ret); WOLFSSL_MSG_EX("DoPreSharedKeys: %d", ret);
return ret; return ret;

View File

@@ -62844,6 +62844,129 @@ static int test_wolfSSL_CRL_CERT_REVOKED_alert(void)
} }
#endif #endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) \
&& defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_AESGCM) && \
!defined(NO_SHA256) && defined(WOLFSSL_AES_128) && \
defined(WOLFSSL_SHA384) && defined(WOLFSSL_AES_256)
static WOLFSSL_CTX* test_TLS_13_ticket_different_ciphers_ctx = NULL;
static WOLFSSL_SESSION* test_TLS_13_ticket_different_ciphers_session = NULL;
static int test_TLS_13_ticket_different_ciphers_run = 0;
static void test_TLS_13_ticket_different_ciphers_ssl_ready(WOLFSSL* ssl)
{
switch (test_TLS_13_ticket_different_ciphers_run) {
case 0:
/* First run */
AssertIntEQ(wolfSSL_set_cipher_list(ssl, "TLS13-AES128-GCM-SHA256"),
WOLFSSL_SUCCESS);
if (wolfSSL_is_server(ssl)) {
AssertNotNull(test_TLS_13_ticket_different_ciphers_ctx =
wolfSSL_get_SSL_CTX(ssl));
AssertIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_up_ref(test_TLS_13_ticket_different_ciphers_ctx));
}
break;
case 1:
/* Second run */
AssertIntEQ(wolfSSL_set_cipher_list(ssl, "TLS13-AES256-GCM-SHA384:"
"TLS13-AES128-GCM-SHA256"),
WOLFSSL_SUCCESS);
if (!wolfSSL_is_server(ssl)) {
AssertIntEQ(wolfSSL_set_session(ssl,
test_TLS_13_ticket_different_ciphers_session),
WOLFSSL_SUCCESS);
}
break;
default:
/* Bad state? */
Fail(("Should not enter here"), ("Should not enter here"));
}
}
static void test_TLS_13_ticket_different_ciphers_on_result(WOLFSSL* ssl)
{
switch (test_TLS_13_ticket_different_ciphers_run) {
case 0:
/* First run */
AssertNotNull(test_TLS_13_ticket_different_ciphers_session =
wolfSSL_get1_session(ssl));
break;
case 1:
/* Second run */
AssertTrue(wolfSSL_session_reused(ssl));
break;
default:
/* Bad state? */
Fail(("Should not enter here"), ("Should not enter here"));
}
}
static int test_TLS_13_ticket_different_ciphers(void)
{
/* Check that we handle the connection when the ticket doesn't match
* the first ciphersuite. */
callback_functions client_cbs, server_cbs;
struct test_params {
method_provider client_meth;
method_provider server_meth;
int doUdp;
} params[] = {
#ifdef WOLFSSL_DTLS13
/* Test that the stateless code handles sessions correctly */
{wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, 1},
#endif
{wolfTLSv1_3_client_method, wolfTLSv1_3_server_method, 0},
};
size_t i;
for (i = 0; i < sizeof(params)/sizeof(*params); i++) {
XMEMSET(&client_cbs, 0, sizeof(client_cbs));
XMEMSET(&server_cbs, 0, sizeof(server_cbs));
test_TLS_13_ticket_different_ciphers_run = 0;
client_cbs.doUdp = server_cbs.doUdp = params[i].doUdp;
client_cbs.method = params[i].client_meth;
server_cbs.method = params[i].server_meth;
client_cbs.ssl_ready = test_TLS_13_ticket_different_ciphers_ssl_ready;
server_cbs.ssl_ready = test_TLS_13_ticket_different_ciphers_ssl_ready;
client_cbs.on_result = test_TLS_13_ticket_different_ciphers_on_result;
server_cbs.ticNoInit = 1;
test_wolfSSL_client_server_nofail(&client_cbs, &server_cbs);
AssertTrue(client_cbs.return_code);
AssertTrue(server_cbs.return_code);
test_TLS_13_ticket_different_ciphers_run++;
server_cbs.ctx = test_TLS_13_ticket_different_ciphers_ctx;
test_wolfSSL_client_server_nofail(&client_cbs, &server_cbs);
AssertTrue(client_cbs.return_code);
AssertTrue(server_cbs.return_code);
wolfSSL_SESSION_free(test_TLS_13_ticket_different_ciphers_session);
test_TLS_13_ticket_different_ciphers_session = NULL;
wolfSSL_CTX_free(test_TLS_13_ticket_different_ciphers_ctx);
test_TLS_13_ticket_different_ciphers_ctx = NULL;
}
return TEST_RES_CHECK(1);
}
#else
static int test_TLS_13_ticket_different_ciphers(void)
{
return TEST_SKIPPED;
}
#endif
/*----------------------------------------------------------------------------* /*----------------------------------------------------------------------------*
| Main | Main
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
@@ -63844,6 +63967,7 @@ TEST_CASE testCases[] = {
* !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) */ * !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) */
TEST_DECL(test_wolfSSL_CTX_set_ciphersuites), TEST_DECL(test_wolfSSL_CTX_set_ciphersuites),
TEST_DECL(test_wolfSSL_CRL_CERT_REVOKED_alert), TEST_DECL(test_wolfSSL_CRL_CERT_REVOKED_alert),
TEST_DECL(test_TLS_13_ticket_different_ciphers),
TEST_DECL(test_WOLFSSL_dtls_version_alert), TEST_DECL(test_WOLFSSL_dtls_version_alert),
TEST_DECL(test_ForceZero), TEST_DECL(test_ForceZero),

View File

@@ -3097,6 +3097,13 @@ WOLFSSL_LOCAL int TLSX_KeyShare_Parse_ClientHello(const WOLFSSL* ssl,
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
enum PskDecryptReturn {
PSK_DECRYPT_NONE = 0,
PSK_DECRYPT_OK,
PSK_DECRYPT_CREATE,
PSK_DECRYPT_FAIL,
};
/* The PreSharedKey extension information - entry in a linked list. */ /* The PreSharedKey extension information - entry in a linked list. */
typedef struct PreSharedKey { typedef struct PreSharedKey {
word16 identityLen; /* Length of identity */ word16 identityLen; /* Length of identity */
@@ -3107,8 +3114,12 @@ typedef struct PreSharedKey {
word32 binderLen; /* Length of HMAC */ word32 binderLen; /* Length of HMAC */
byte binder[WC_MAX_DIGEST_SIZE]; /* HMAC of handshake */ byte binder[WC_MAX_DIGEST_SIZE]; /* HMAC of handshake */
byte hmac; /* HMAC algorithm */ byte hmac; /* HMAC algorithm */
#ifdef HAVE_SESSION_TICKET
InternalTicket* it; /* ptr to ticket */
#endif
byte resumption:1; /* Resumption PSK */ byte resumption:1; /* Resumption PSK */
byte chosen:1; /* Server's choice */ byte chosen:1; /* Server's choice */
byte decryptRet:3; /* Ticket decrypt return */
struct PreSharedKey* next; /* List pointer */ struct PreSharedKey* next; /* List pointer */
} PreSharedKey; } PreSharedKey;
@@ -3933,13 +3944,8 @@ typedef struct TicketNonce {
#ifdef WOLFSSL_DTLS #ifdef WOLFSSL_DTLS
typedef struct PskInfo { typedef struct PskInfo {
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
/* Macro guard matches one for session->version */
ProtocolVersion pv;
#endif
byte cipherSuite0; byte cipherSuite0;
byte cipherSuite; byte cipherSuite;
word16 namedGroup;
byte isValid:1; byte isValid:1;
} PskInfo; } PskInfo;
#endif #endif
@@ -5650,10 +5656,16 @@ extern const WOLF_EC_NIST_NAME kNistCurves[];
WOLFSSL_LOCAL int SendChangeCipher(WOLFSSL* ssl); WOLFSSL_LOCAL int SendChangeCipher(WOLFSSL* ssl);
WOLFSSL_LOCAL int SendTicket(WOLFSSL* ssl); WOLFSSL_LOCAL int SendTicket(WOLFSSL* ssl);
#ifdef HAVE_SESSION_TICKET #ifdef HAVE_SESSION_TICKET
WOLFSSL_LOCAL int DoDecryptTicket(WOLFSSL* ssl, const byte* input, word32 len, WOLFSSL_LOCAL int DoDecryptTicket(const WOLFSSL* ssl, const byte* input,
InternalTicket **it); word32 len, InternalTicket **it);
#endif /* HAVE_SESSION_TICKET */ /* Return 0 when check successful. <0 on failure. */
WOLFSSL_LOCAL int DoClientTicketCheck(const PreSharedKey* psk, sword64 timeout,
const byte* suite);
WOLFSSL_LOCAL void DoClientTicketFinalize(WOLFSSL* ssl, InternalTicket* it);
WOLFSSL_LOCAL void CleanupClientTickets(PreSharedKey* psk);
WOLFSSL_LOCAL int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len); WOLFSSL_LOCAL int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len);
WOLFSSL_LOCAL int DoClientTicket_ex(const WOLFSSL* ssl, PreSharedKey* psk);
#endif /* HAVE_SESSION_TICKET */
WOLFSSL_LOCAL int SendData(WOLFSSL* ssl, const void* data, int sz); WOLFSSL_LOCAL int SendData(WOLFSSL* ssl, const void* data, int sz);
#ifdef WOLFSSL_TLS13 #ifdef WOLFSSL_TLS13
WOLFSSL_LOCAL int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType); WOLFSSL_LOCAL int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType);