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,24 +291,17 @@ static int TlsxFindByType(WolfSSL_ConstVector* ret, word16 extType,
}
#endif
#if defined(WOLFSSL_DTLS13) || defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME)
#if defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME)
#ifdef HAVE_SESSION_TICKET
static int TlsTicketIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector exts,
PskInfo* pskInfo, byte isTls13)
int* resume)
{
WolfSSL_ConstVector tlsxSessionTicket;
byte tempTicket[SESSION_TICKET_LEN];
InternalTicket* it;
int ret;
InternalTicket* it = NULL;
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)
return ret;
if (tlsxSessionTicket.size == 0)
@ -316,26 +309,20 @@ static int TlsTicketIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector exts,
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;
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,
PskInfo* pskInfo)
int* resume)
{
WOLFSSL_SESSION* sess;
word32 sessRow;
@ -353,17 +340,19 @@ static int TlsSessionIdIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector sessionID
ssl->ctx->get_sess_cb((WOLFSSL*)ssl, sessionID.elements, ID_LEN,
&unused);
if (sess != NULL) {
/* Store info for later */
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
pskInfo->pv = sess->version;
#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))
wolfSSL_FreeSession(ssl->ctx, sess);
else
#endif
pskInfo->cipherSuite0 = sess->cipherSuite0;
pskInfo->cipherSuite = sess->cipherSuite;
pskInfo->namedGroup = sess->namedGroup;
pskInfo->isValid = 1;
wolfSSL_FreeSession(ssl->ctx, sess);
return 0;
{
*resume = 1;
wolfSSL_FreeSession(ssl->ctx, sess);
return 0;
}
}
}
if (ssl->ctx->internalCacheLookupOff)
@ -372,15 +361,15 @@ static int TlsSessionIdIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector sessionID
#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;
#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
pskInfo->cipherSuite0 = sess->cipherSuite0;
pskInfo->cipherSuite = sess->cipherSuite;
pskInfo->namedGroup = sess->namedGroup;
pskInfo->isValid = 1;
{
*resume = 1;
}
TlsSessionCacheUnlockRow(sessRow);
}
@ -388,19 +377,18 @@ static int TlsSessionIdIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector sessionID
}
static int TlsResumptionIsValid(const WOLFSSL* ssl, WolfSSL_CH* ch,
PskInfo* pskInfo, byte isTls13)
int* resume)
{
int ret;
(void)isTls13;
#ifdef HAVE_SESSION_TICKET
ret = TlsTicketIsValid(ssl, ch->extension, pskInfo, isTls13);
ret = TlsTicketIsValid(ssl, ch->extension, resume);
if (ret != 0)
return ret;
if (pskInfo->isValid)
if (*resume)
return 0;
#endif /* HAVE_SESSION_TICKET */
ret = TlsSessionIdIsValid(ssl, ch->sessionId, pskInfo);
ret = TlsSessionIdIsValid(ssl, ch->sessionId, resume);
return ret;
}
#endif /* WOLFSSL_DTLS13 || WOLFSSL_DTLS_NO_HVR_ON_RESUME */
@ -454,18 +442,16 @@ static int CopySupportedGroup(TLSX* src, TLSX** dst, void* heap)
}
#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 */
static void FindPskSuiteFromExt(const WOLFSSL* ssl, TLSX* extensions,
PskInfo* pskInfo, Suites* suites)
{
TLSX* pskExt = TLSX_Find(extensions, TLSX_PRE_SHARED_KEY);
int found = 0;
PreSharedKey* current;
byte psk_key[MAX_PSK_KEY_LEN];
word32 psk_keySz;
int i;
byte foundSuite[SUITE_LEN];
int ret;
if (pskExt == NULL)
return;
@ -473,17 +459,61 @@ static void FindPskSuiteFromExt(const WOLFSSL* ssl, TLSX* extensions,
for (i = 0; i < suites->suiteSz; i += 2) {
for (current = (PreSharedKey*)pskExt->data; current != NULL;
current = current->next) {
if (FindPskSuite(ssl, current, psk_key, &psk_keySz,
suites->suites + i, &found, foundSuite) == 0) {
if (found) {
#ifdef HAVE_SESSION_TICKET
{
/* 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->cipherSuite = foundSuite[1];
pskInfo->isValid = 1;
return;
goto cleanup;
}
}
#endif
}
}
cleanup:
#ifdef HAVE_SESSION_TICKET
CleanupClientTickets((PreSharedKey*)pskExt->data);
#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"
#endif
static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch,
PskInfo* pskInfo)
#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;
@ -511,8 +545,10 @@ static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch,
CipherSpecs specs;
byte cookieHash[WC_MAX_DIGEST_SIZE];
int cookieHashSz;
(void)pskInfo;
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
PskInfo pskInfo;
XMEMSET(&pskInfo, 0, sizeof(pskInfo));
#endif
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. */
byte modes;
#ifndef NO_PSK
/* When we didn't find a valid ticket ask the user for the
* ciphersuite matching this identity */
if (!pskInfo->isValid) {
if (TLSX_PreSharedKey_Parse_ClientHello(&parsedExts,
tlsx.elements, tlsx.size, ssl->heap) == 0)
FindPskSuiteFromExt(ssl, parsedExts, pskInfo, &suites);
/* Revert to full handshake if PSK parsing failed */
}
#endif
/* 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) {
if (pskInfo.isValid) {
ret = TlsxFindByType(&tlsx, TLSX_PSK_KEY_EXCHANGE_MODES,
ch->extension);
if (ret != 0)
@ -638,9 +669,9 @@ static int SendStatelessReplyDtls13(const WOLFSSL* ssl, WolfSSL_CH* ch,
#endif
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
if (usePSK && pskInfo->isValid) {
cs.cipherSuite0 = pskInfo->cipherSuite0;
cs.cipherSuite = pskInfo->cipherSuite;
if (usePSK && pskInfo.isValid) {
cs.cipherSuite0 = pskInfo.cipherSuite0;
cs.cipherSuite = pskInfo.cipherSuite;
if (haveSG && !haveKS) {
WOLFSSL_MSG("Client didn't send KeyShare or Supported Groups.");
@ -737,15 +768,13 @@ dtls13_cleanup:
}
#endif
static int SendStatelessReply(const WOLFSSL* ssl, WolfSSL_CH* ch, byte isTls13,
PskInfo* pskInfo)
static int SendStatelessReply(const WOLFSSL* ssl, WolfSSL_CH* ch, byte isTls13)
{
int ret;
(void)isTls13;
(void)pskInfo;
#ifdef WOLFSSL_DTLS13
if (isTls13) {
ret = SendStatelessReplyDtls13(ssl, ch, pskInfo);
ret = SendStatelessReplyDtls13(ssl, ch);
}
else
#endif
@ -789,9 +818,7 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input,
int ret;
WolfSSL_CH ch;
byte isTls13 = 0;
PskInfo pskInfo;
XMEMSET(&pskInfo, 0, sizeof(pskInfo));
XMEMSET(&ch, 0, sizeof(ch));
ssl->options.dtlsStateful = 0;
@ -816,33 +843,21 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input,
if (ret != 0)
return ret;
#if defined(WOLFSSL_DTLS13) || defined(WOLFSSL_DTLS_NO_HVR_ON_RESUME)
ret = TlsResumptionIsValid(ssl, &ch, &pskInfo, isTls13);
if (ret != 0)
return ret;
#endif
#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME
if (pskInfo.isValid) {
ssl->options.dtlsStateful = 1;
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;
if (!isTls13) {
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) {
ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13, &pskInfo);
ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13);
}
else {
byte cookieGood;
@ -857,7 +872,7 @@ int DoClientHelloStateless(WOLFSSL* ssl, const byte* input,
ret = INVALID_PARAMETER;
else
#endif
ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13, &pskInfo);
ret = SendStatelessReply((WOLFSSL*)ssl, &ch, isTls13);
}
else
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)
{
ExternalTicket* et;
@ -34589,7 +34589,9 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
ret = WOLFSSL_TICKET_RET_REJECT;
}
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, &outLen,
ssl->ctx->ticketEncCtx);
@ -34614,142 +34616,265 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
return ret;
}
/* Parse ticket sent by client, returns callback return value */
int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len)
static int DoClientTicketCheckVersion(const WOLFSSL* ssl,
InternalTicket* it)
{
InternalTicket* it;
int ret;
if (ssl->version.minor < it->pv.minor) {
WOLFSSL_MSG("Ticket has greater version");
return VERSION_ERROR;
}
else if (ssl->version.minor > it->pv.minor) {
if (IsAtLeastTLSv1_3(it->pv) != IsAtLeastTLSv1_3(ssl->version)) {
WOLFSSL_MSG("Tickets cannot be shared between "
"TLS 1.3 and TLS 1.2 and lower");
return VERSION_ERROR;
}
if (!ssl->options.downgrade) {
WOLFSSL_MSG("Ticket has lesser version");
return VERSION_ERROR;
}
WOLFSSL_MSG("Downgrading protocol due to ticket");
if (it->pv.minor < ssl->options.minDowngrade) {
WOLFSSL_MSG("Ticket has lesser version than allowed");
return VERSION_ERROR;
}
}
/* 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;
}
/* 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;
XMEMCPY(ssl->session->altSessionID, it->id, ID_LEN);
if (wolfSSL_GetSession(ssl, NULL, 1) != NULL) {
WOLFSSL_MSG("Found session matching the session id"
" found in the ticket");
}
else {
WOLFSSL_MSG("Can't find session matching the session id"
" found in the ticket");
}
#endif
if (!IsAtLeastTLSv1_3(ssl->version)) {
XMEMCPY(ssl->arrays->masterSecret, it->msecret, SECRET_LEN);
/* Copy the haveExtendedMasterSecret property from the ticket to
* the saved session, so the property may be checked later. */
ssl->session->haveEMS = it->haveEMS;
ato32((const byte*)&it->timestamp, &ssl->session->bornOn);
#ifndef NO_RESUME_SUITE_CHECK
ssl->session->cipherSuite0 = it->suite[0];
ssl->session->cipherSuite = it->suite[1];
#endif
}
else {
#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. */
#ifdef WOLFSSL_32BIT_MILLI_TIME
ato32(it->timestamp, &ssl->session->ticketSeen);
#else
word32 seenHi, seenLo;
ato32(it->timestamp , &seenHi);
ato32(it->timestamp + OPAQUE32_LEN, &seenLo);
ssl->session->ticketSeen = ((sword64)seenHi << 32) + seenLo;
#endif
ato32(it->ageAdd, &ssl->session->ticketAdd);
ssl->session->cipherSuite0 = it->suite[0];
ssl->session->cipherSuite = it->suite[1];
#ifdef WOLFSSL_EARLY_DATA
ato32(it->maxEarlyDataSz, &ssl->session->maxEarlyDataSz);
#endif
/* Resumption master secret. */
XMEMCPY(ssl->session->masterSecret, it->msecret, SECRET_LEN);
#if defined(WOLFSSL_TICKET_NONCE_MALLOC) && \
(!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
if (ssl->session->ticketNonce.data
!= ssl->session->ticketNonce.dataStatic) {
XFREE(ssl->session->ticketNonce.data, ssl->heap,
DYNAMIC_TYPE_SESSION_TICK);
ssl->session->ticketNonce.data =
ssl->session->ticketNonce.dataStatic;
}
#endif /* defined(WOLFSSL_TICKET_NONCE_MALLOC) && FIPS_VERSION_GE(5,3) */
XMEMCPY(ssl->session->ticketNonce.data, it->ticketNonce,
it->ticketNonceLen);
ssl->session->ticketNonce.len = it->ticketNonceLen;
ato16(it->namedGroup, &ssl->session->namedGroup);
#endif
}
ssl->version.minor = it->pv.minor;
}
/* Parse ticket sent by client, returns callback return value. Doesn't
* modify ssl and stores the InternalTicket inside psk */
int DoClientTicket_ex(const WOLFSSL* ssl, PreSharedKey* psk)
{
int decryptRet;
int ret;
WOLFSSL_START(WC_FUNC_TICKET_DO);
WOLFSSL_ENTER("DoClientTicket");
WOLFSSL_ENTER("DoClientTicket_ex");
ret = DoDecryptTicket(ssl, input, len, &it);
if (ret != WOLFSSL_TICKET_RET_OK && ret != WOLFSSL_TICKET_RET_CREATE) {
WOLFSSL_LEAVE("DoClientTicket", ret);
return ret;
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
/* get master secret */
if (ret == WOLFSSL_TICKET_RET_OK || ret == WOLFSSL_TICKET_RET_CREATE) {
if (ssl->version.minor < it->pv.minor) {
WOLFSSL_MSG("Ticket has greater version");
ret = VERSION_ERROR;
goto error;
}
else if (ssl->version.minor > it->pv.minor) {
if (IsAtLeastTLSv1_3(it->pv) != IsAtLeastTLSv1_3(ssl->version)) {
WOLFSSL_MSG("Tickets cannot be shared between "
"TLS 1.3 and TLS 1.2 and lower");
ret = VERSION_ERROR;
goto error;
}
ret = DoClientTicketCheckVersion(ssl, psk->it);
if (ret != 0) {
psk->decryptRet = PSK_DECRYPT_FAIL;
return ret;
}
return decryptRet;
}
if (!ssl->options.downgrade) {
WOLFSSL_MSG("Ticket has lesser version");
ret = VERSION_ERROR;
goto 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_MSG("Downgrading protocol due to ticket");
WOLFSSL_START(WC_FUNC_TICKET_DO);
WOLFSSL_ENTER("DoClientTicket");
if (it->pv.minor < ssl->options.minDowngrade) {
WOLFSSL_MSG("Ticket has lesser version than allowed");
ret = VERSION_ERROR;
goto error;
}
ssl->version.minor = it->pv.minor;
}
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
#ifdef WOLFSSL_TICKET_HAVE_ID
{
ssl->session->haveAltSessionID = 1;
XMEMCPY(ssl->session->altSessionID, it->id, ID_LEN);
if (wolfSSL_GetSession(ssl, NULL, 1) != NULL) {
WOLFSSL_MSG("Found session matching the session id"
" found in the ticket");
}
else {
WOLFSSL_MSG("Can't find session matching the session id"
" found in the ticket");
}
}
ret = DoClientTicketCheckVersion(ssl, it);
if (ret != 0) {
ForceZero(it, sizeof(*it));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(it, sizeof(InternalTicket));
#endif
return ret;
}
DoClientTicketFinalize(ssl, it);
ForceZero(it, sizeof(*it));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(it, sizeof(InternalTicket));
#endif
if (!IsAtLeastTLSv1_3(ssl->version)) {
XMEMCPY(ssl->arrays->masterSecret, it->msecret, SECRET_LEN);
/* Copy the haveExtendedMasterSecret property from the ticket to
* the saved session, so the property may be checked later. */
ssl->session->haveEMS = it->haveEMS;
ato32((const byte*)&it->timestamp, &ssl->session->bornOn);
#ifndef NO_RESUME_SUITE_CHECK
ssl->session->cipherSuite0 = it->suite[0];
ssl->session->cipherSuite = it->suite[1];
#endif
}
else {
#ifdef WOLFSSL_TLS13
/* Restore information to renegotiate. */
#ifdef WOLFSSL_32BIT_MILLI_TIME
ato32(it->timestamp, &ssl->session->ticketSeen);
#else
word32 seenHi, seenLo;
return decryptRet;
}
ato32(it->timestamp , &seenHi);
ato32(it->timestamp + OPAQUE32_LEN, &seenLo);
ssl->session->ticketSeen = ((sword64)seenHi << 32) + seenLo;
#endif
ato32(it->ageAdd, &ssl->session->ticketAdd);
ssl->session->cipherSuite0 = it->suite[0];
ssl->session->cipherSuite = it->suite[1];
#ifdef WOLFSSL_EARLY_DATA
ato32(it->maxEarlyDataSz, &ssl->session->maxEarlyDataSz);
#endif
/* Resumption master secret. */
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) && \
(!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
if (ssl->session->ticketNonce.data
!= ssl->session->ticketNonce.dataStatic) {
XFREE(ssl->session->ticketNonce.data, ssl->heap,
DYNAMIC_TYPE_SESSION_TICK);
ssl->session->ticketNonce.data =
ssl->session->ticketNonce.dataStatic;
}
#endif /* defined(WOLFSSL_TICKET_NONCE_MALLOC) && FIPS_VERSION_GE(5,3) */
XMEMCPY(ssl->session->ticketNonce.data, it->ticketNonce,
it->ticketNonceLen);
ssl->session->ticketNonce.len = it->ticketNonceLen;
ato16(it->namedGroup, &ssl->session->namedGroup);
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
}
}
ForceZero(it, sizeof(*it));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(it, sizeof(InternalTicket));
#endif
WOLFSSL_LEAVE("DoClientTicket", ret);
WOLFSSL_END(WC_FUNC_TICKET_DO);
return ret;
error:
ForceZero(it, sizeof(*it));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(it, sizeof(InternalTicket));
#endif
WOLFSSL_ERROR_VERBOSE(ret);
return WOLFSSL_TICKET_RET_REJECT;
}

View File

@ -5658,68 +5658,33 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz,
#ifdef HAVE_SESSION_TICKET
/* 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
if (ret == WC_PENDING_E)
return ret;
#endif
if (ret == WOLFSSL_TICKET_RET_OK) {
#ifdef WOLFSSL_32BIT_MILLI_TIME
word32 now;
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) {
if (DoClientTicketCheck(current, ssl->timeout, suite) != 0) {
current = current->next;
continue;
}
#else
sword64 diff;
diff = TimeNowInMilliseconds();
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
DoClientTicketFinalize(ssl, current->it);
/* SERVER: using secret in session ticket for peer auth. */
ssl->options.peerAuthGood = 1;
@ -5893,13 +5858,23 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
ret = DoPreSharedKeys(ssl, input, helloSz - bindersLen,
suites->suites + i, usingPSK, &first);
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);
return ret;
}
}
#ifdef HAVE_SESSION_TICKET
CleanupClientTickets((PreSharedKey*)ext->data);
#endif
#else
ret = DoPreSharedKeys(ssl, input, helloSz - bindersLen, suite, usingPSK,
&first);
CleanupClientTickets((PreSharedKey*)ext->data);
if (ret != 0) {
WOLFSSL_MSG_EX("DoPreSharedKeys: %d", ret);
return ret;

View File

@ -62844,6 +62844,129 @@ static int test_wolfSSL_CRL_CERT_REVOKED_alert(void)
}
#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
*----------------------------------------------------------------------------*/
@ -63844,6 +63967,7 @@ TEST_CASE testCases[] = {
* !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) */
TEST_DECL(test_wolfSSL_CTX_set_ciphersuites),
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_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)
enum PskDecryptReturn {
PSK_DECRYPT_NONE = 0,
PSK_DECRYPT_OK,
PSK_DECRYPT_CREATE,
PSK_DECRYPT_FAIL,
};
/* The PreSharedKey extension information - entry in a linked list. */
typedef struct PreSharedKey {
word16 identityLen; /* Length of identity */
@ -3107,8 +3114,12 @@ typedef struct PreSharedKey {
word32 binderLen; /* Length of HMAC */
byte binder[WC_MAX_DIGEST_SIZE]; /* HMAC of handshake */
byte hmac; /* HMAC algorithm */
#ifdef HAVE_SESSION_TICKET
InternalTicket* it; /* ptr to ticket */
#endif
byte resumption:1; /* Resumption PSK */
byte chosen:1; /* Server's choice */
byte decryptRet:3; /* Ticket decrypt return */
struct PreSharedKey* next; /* List pointer */
} PreSharedKey;
@ -3933,13 +3944,8 @@ typedef struct TicketNonce {
#ifdef WOLFSSL_DTLS
typedef struct PskInfo {
#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)
/* Macro guard matches one for session->version */
ProtocolVersion pv;
#endif
byte cipherSuite0;
byte cipherSuite;
word16 namedGroup;
byte isValid:1;
} PskInfo;
#endif
@ -5650,10 +5656,16 @@ extern const WOLF_EC_NIST_NAME kNistCurves[];
WOLFSSL_LOCAL int SendChangeCipher(WOLFSSL* ssl);
WOLFSSL_LOCAL int SendTicket(WOLFSSL* ssl);
#ifdef HAVE_SESSION_TICKET
WOLFSSL_LOCAL int DoDecryptTicket(WOLFSSL* ssl, const byte* input, word32 len,
InternalTicket **it);
#endif /* HAVE_SESSION_TICKET */
WOLFSSL_LOCAL int DoDecryptTicket(const WOLFSSL* ssl, const byte* input,
word32 len, InternalTicket **it);
/* 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_ex(const WOLFSSL* ssl, PreSharedKey* psk);
#endif /* HAVE_SESSION_TICKET */
WOLFSSL_LOCAL int SendData(WOLFSSL* ssl, const void* data, int sz);
#ifdef WOLFSSL_TLS13
WOLFSSL_LOCAL int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType);