bad rebase + fixing dtls13 support for ECH

This commit is contained in:
sebastian-carpenter
2026-02-03 16:49:43 -07:00
parent a2fe12a38a
commit cb2d693550
2 changed files with 390 additions and 428 deletions
+73 -47
View File
@@ -13440,10 +13440,20 @@ static int TLSX_ECH_GetSize(WOLFSSL_ECH* ech, byte msgType)
return (int)size;
}
/* locate the given extension type, use the extOffset to start off after where a
* previous call to this function ended */
static const byte* EchFindOuterExtension(const byte* outerCh, word32 chLen,
word16 extType, word16* extLen, word16* extOffset,
/* Locate the given extension type, use the extOffset to start off after where a
* previous call to this function ended
*
* outerCh The outer ClientHello buffer.
* chLen Outer ClientHello length.
* extType Extension type to look for.
* extLen Out parameter, length of found extension.
* extOffset Offset into outer ClientHello to look for extension from.
* extensionsStart Start of outer ClientHello extensions.
* extensionsLen Length of outer ClientHello extensions.
* returns 0 on success and otherwise failure.
*/
static const byte* TLSX_ECH_FindOuterExtension(const byte* outerCh,
word32 chLen, word16 extType, word16* extLen, word16* extOffset,
word16* extensionsStart, word16* extensionsLen)
{
word32 idx = *extOffset;
@@ -13505,10 +13515,19 @@ static const byte* EchFindOuterExtension(const byte* outerCh, word32 chLen,
return NULL;
}
/* if newInnerCh is NULL, validate ordering and existence of references
* - updates newInnerChLen with total length of selected extensions
* if not NULL, copy extensions into the provided buffer */
static int EchCopyOuterExtensions(const byte* outerCh, word32 outerChLen,
/* If newinnerCh is NULL, validate ordering and existence of references
* - updates newInnerChLen with total length of selected extensions
* If newinnerCh in not NULL, copy extensions into newInnerCh
*
* outerCh The outer ClientHello buffer.
* outerChLen Outer ClientHello length.
* newInnerCh The inner ClientHello buffer.
* newInnerChLen Inner ClientHello length.
* numOuterRefs Number of references described by OuterExtensions extension.
* numOuterTypes References described by OuterExtensions extension.
* returns 0 on success and otherwise failure.
*/
static int TLSX_ECH_CopyOuterExtensions(const byte* outerCh, word32 outerChLen,
byte** newInnerCh, word32* newInnerChLen,
word16 numOuterRefs, const byte* outerRefTypes)
{
@@ -13532,9 +13551,9 @@ static int EchCopyOuterExtensions(const byte* outerCh, word32 outerChLen,
break;
}
outerExtData = EchFindOuterExtension(outerCh, outerChLen,
refType, &outerExtLen, &outerExtOffset,
&extsStart, &extsLen);
outerExtData = TLSX_ECH_FindOuterExtension(outerCh, outerChLen,
refType, &outerExtLen, &outerExtOffset,
&extsStart, &extsLen);
if (outerExtData == NULL) {
WOLFSSL_MSG("ECH: referenced extension not in outer CH");
@@ -13551,9 +13570,9 @@ static int EchCopyOuterExtensions(const byte* outerCh, word32 outerChLen,
while (numOuterRefs-- > 0) {
ato16(outerRefTypes, &refType);
outerExtData = EchFindOuterExtension(outerCh, outerChLen,
refType, &outerExtLen, &outerExtOffset,
&extsStart, &extsLen);
outerExtData = TLSX_ECH_FindOuterExtension(outerCh, outerChLen,
refType, &outerExtLen, &outerExtOffset,
&extsStart, &extsLen);
if (outerExtData == NULL) {
ret = INVALID_PARAMETER;
@@ -13570,13 +13589,21 @@ static int EchCopyOuterExtensions(const byte* outerCh, word32 outerChLen,
return ret;
}
/* expand ech_outer_extensions in the inner ClientHello by copying referenced
* extensions from the outer ClientHello
/* Expand ech_outer_extensions in the inner ClientHello by copying referenced
* extensions from the outer ClientHello.
* If the sessionID exists in the outer ClientHello then also copy that into the
* expanded inner ClientHello.
*
* ssl SSL/TLS object.
* ech ECH object.
* heap Heap hint.
* returns 0 on success and otherwise failure.
*/
static int TLSX_ExpandEchOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
static int TLSX_ECH_ExpandOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
void* heap)
{
int ret = 0;
int headerSz;
const byte* innerCh;
word32 innerChLen;
const byte* outerCh;
@@ -13604,7 +13631,14 @@ static int TLSX_ExpandEchOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
if (ech == NULL || ech->innerClientHello == NULL || ech->aad == NULL)
return BAD_FUNC_ARG;
innerCh = ech->innerClientHello + HANDSHAKE_HEADER_SZ;
#ifdef WOLFSSL_DTLS13
headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ :
HANDSHAKE_HEADER_SZ;
#else
headerSz = HANDSHAKE_HEADER_SZ;
#endif
innerCh = ech->innerClientHello + headerSz;
innerChLen = ech->innerClientHelloLen;
outerCh = ech->aad;
outerChLen = ech->aadLen;
@@ -13675,8 +13709,8 @@ static int TLSX_ExpandEchOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
outerRefTypes = innerCh + idx + 1;
numOuterRefs = outerExtListLen / OPAQUE16_LEN;
ret = EchCopyOuterExtensions(outerCh, outerChLen, NULL, &extraSize,
numOuterRefs, outerRefTypes);
ret = TLSX_ECH_CopyOuterExtensions(outerCh, outerChLen, NULL,
&extraSize, numOuterRefs, outerRefTypes);
if (ret != 0)
return ret;
}
@@ -13685,7 +13719,7 @@ static int TLSX_ExpandEchOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
}
newInnerChLen = innerChLen - echOuterExtLen + extraSize - sessionIdLen +
ssl->session->sessionIDSz;
ssl->session->sessionIDSz;
if (!foundEchOuter && sessionIdLen == ssl->session->sessionIDSz) {
/* no extensions + no sessionID to copy */
@@ -13693,8 +13727,8 @@ static int TLSX_ExpandEchOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
return ret;
}
else {
newInnerCh = (byte*)XMALLOC(newInnerChLen + HANDSHAKE_HEADER_SZ, heap,
DYNAMIC_TYPE_TMP_BUFFER);
newInnerCh = (byte*)XMALLOC(newInnerChLen + headerSz, heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (newInnerCh == NULL)
return MEMORY_E;
}
@@ -13704,7 +13738,7 @@ static int TLSX_ExpandEchOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
* AddTls13HandShakeHeader() in DoTls13ClientHello(). */
/* copy everything up to EchOuterExtensions */
newInnerChRef = newInnerCh + HANDSHAKE_HEADER_SZ;
newInnerChRef = newInnerCh + headerSz;
copyLen = OPAQUE16_LEN + RAN_LEN;
XMEMCPY(newInnerChRef, innerCh, copyLen);
newInnerChRef += copyLen;
@@ -13726,24 +13760,22 @@ static int TLSX_ExpandEchOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
}
else {
copyLen = echOuterExtIdx - OPAQUE16_LEN - RAN_LEN - OPAQUE8_LEN -
sessionIdLen;
sessionIdLen;
XMEMCPY(newInnerChRef, innerCh + OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN +
sessionIdLen, copyLen);
sessionIdLen, copyLen);
newInnerChRef += copyLen;
/* update extensions length in the new ClientHello */
innerExtIdx = innerExtIdx - sessionIdLen + ssl->session->sessionIDSz;
c16toa(innerExtLen - echOuterExtLen + (word16)extraSize,
newInnerChRef - OPAQUE16_LEN);
newInnerChRef - OPAQUE16_LEN);
/* insert expanded extensions from outer ClientHello */
ret = EchCopyOuterExtensions(outerCh, outerChLen, &newInnerChRef,
ret = TLSX_ECH_CopyOuterExtensions(outerCh, outerChLen, &newInnerChRef,
&newInnerChLen, numOuterRefs, outerRefTypes);
if (ret == 0) {
/* copy remaining extensions after ech_outer_extensions */
copyLen = innerChLen - (echOuterExtIdx + echOuterExtLen);
XMEMCPY(newInnerChRef, innerCh + echOuterExtIdx + echOuterExtLen,
copyLen);
copyLen);
WOLFSSL_MSG("ECH: expanded ech_outer_extensions successfully");
}
@@ -14020,13 +14052,7 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
echConfig = echConfig->next;
}
}
/* if we failed to extract, set state to retry configs */
if (ret != 0) {
XFREE(ech->innerClientHello, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
ech->innerClientHello = NULL;
ech->state = ECH_WRITE_RETRY_CONFIGS;
}
else {
if (ret == 0) {
i = 0;
/* decrement until before the padding */
while (ech->innerClientHello[ech->innerClientHelloLen +
@@ -14036,15 +14062,15 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
/* subtract the length of the padding from the length */
ech->innerClientHelloLen -= i;
/* expand EchOuterExtensions if present */
ret = TLSX_ExpandEchOuterExtensions(ssl, ech, ssl->heap);
if (ret != 0) {
WOLFSSL_MSG_EX("ECH: failed to expand EchOuterExtensions");
XFREE(ech->innerClientHello, ssl->heap,
DYNAMIC_TYPE_TMP_BUFFER);
ech->innerClientHello = NULL;
ech->state = ECH_WRITE_RETRY_CONFIGS;
}
/* expand EchOuterExtensions if present
* and, if it exists, copy sessionID from outer hello */
ret = TLSX_ECH_ExpandOuterExtensions(ssl, ech, ssl->heap);
}
/* if we failed to extract/expand, set state to retry configs */
if (ret != 0) {
XFREE(ech->innerClientHello, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
ech->innerClientHello = NULL;
ech->state = ECH_WRITE_RETRY_CONFIGS;
}
XFREE(aadCopy, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return 0;
+317 -381
View File
@@ -3806,6 +3806,255 @@ int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config)
return WOLFSSL_FATAL_ERROR;
}
/* Hash the inner client hello, initializing the hsHashesEch field if needed.
*
* ssl SSL/TLS object.
* ech ECH object.
* returns 0 on success and otherwise failure.
*/
static int EchHashHelloInner(WOLFSSL* ssl, WOLFSSL_ECH* ech)
{
int ret = 0;
int headerSz;
word32 realSz;
HS_Hashes* tmpHashes;
#ifndef NO_WOLFSSL_CLIENT
byte falseHeader[HRR_MAX_HS_HEADER_SZ];
#endif
if (ssl == NULL || ech == NULL) {
return BAD_FUNC_ARG;
}
#ifdef WOLFSSL_DTLS13
headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ :
HANDSHAKE_HEADER_SZ;
#else
headerSz = HANDSHAKE_HEADER_SZ;
#endif
realSz = ech->innerClientHelloLen;
#ifndef NO_WOLFSSL_CLIENT
if (ssl->options.side == WOLFSSL_CLIENT_END) {
realSz -= ech->paddingLen + ech->hpke->Nt;
}
#endif
tmpHashes = ssl->hsHashes;
ssl->hsHashes = ssl->hsHashesEch;
if (ssl->options.echAccepted == 0 && ssl->hsHashes == NULL) {
ret = InitHandshakeHashes(ssl);
if (ret == 0) {
ssl->hsHashesEch = ssl->hsHashes;
ech->innerCount = 1;
}
}
if (ret == 0) {
#ifndef NO_WOLFSSL_CLIENT
if (ssl->options.side == WOLFSSL_CLIENT_END) {
/* client-side: innerClientHello contains body only */
AddTls13HandShakeHeader(falseHeader, realSz, 0, 0, client_hello,
ssl);
ret = HashRaw(ssl, falseHeader, headerSz);
if (ret == 0) {
ret = HashRaw(ssl, ech->innerClientHello, realSz);
}
}
#endif
#ifndef NO_WOLFSSL_SERVER
if (ssl->options.side == WOLFSSL_SERVER_END) {
/* server-side: innerClientHello contains header + body */
ret = HashRaw(ssl, ech->innerClientHello, headerSz + realSz);
}
#endif
}
ssl->hsHashes = tmpHashes;
return ret;
}
/* Calculate the 8 ECH confirmation bytes.
*
* ssl SSL/TLS object.
* label Ascii string describing ECH acceptance or rejection.
* labelSz Length of label excluding NULL character.
* input The buffer to calculate confirmation off of.
* acceptOffset Where the 8 ECH confirmation bytes start.
* helloSz Size of hello message.
* isHrr Whether message is a HelloRetryRequest or not.
* acceptExpanded An 8 byte array to store calculated confirmation to.
* returns 0 on success and otherwise failure.
*/
static int EchCalcAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
const byte* input, int acceptOffset, int helloSz, byte isHrr,
byte* acceptExpanded)
{
int ret = 0;
int digestType = 0;
int digestSize = 0;
int hashSz = 0;
int headerSz;
HS_Hashes* tmpHashes;
HS_Hashes* acceptHash = NULL;
byte zeros[WC_MAX_DIGEST_SIZE];
byte transcriptEchConf[WC_MAX_DIGEST_SIZE];
byte clientHelloInnerHash[WC_MAX_DIGEST_SIZE];
byte expandLabelPrk[WC_MAX_DIGEST_SIZE];
byte messageHashHeader[HRR_MAX_HS_HEADER_SZ];
XMEMSET(zeros, 0, sizeof(zeros));
XMEMSET(transcriptEchConf, 0, sizeof(transcriptEchConf));
XMEMSET(clientHelloInnerHash, 0, sizeof(clientHelloInnerHash));
XMEMSET(expandLabelPrk, 0, sizeof(expandLabelPrk));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Add("ECH PRK", expandLabelPrk, sizeof(expandLabelPrk));
#endif
tmpHashes = ssl->hsHashes;
ssl->hsHashes = ssl->hsHashesEch;
#ifdef WOLFSSL_DTLS13
headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ :
HANDSHAKE_HEADER_SZ;
#else
headerSz = HANDSHAKE_HEADER_SZ;
#endif
if (isHrr) {
/* the transcript hash of ClientHelloInner1 */
hashSz = GetMsgHash(ssl, clientHelloInnerHash);
if (hashSz > 0) {
ret = 0;
}
/* restart ECH transcript hash, similar to RestartHandshakeHash but
* don't add a cookie */
if (ret == 0) {
ret = InitHandshakeHashes(ssl);
}
if (ret == 0) {
ssl->hsHashesEch = ssl->hsHashes;
AddTls13HandShakeHeader(messageHashHeader, (word32)hashSz, 0, 0,
message_hash, ssl);
ret = HashRaw(ssl, messageHashHeader, headerSz);
}
if (ret == 0) {
ret = HashRaw(ssl, clientHelloInnerHash, (word32)hashSz);
}
}
/* hash with zeros for confirmation computation */
if (ret == 0) {
ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashesEch, &acceptHash);
}
if (ret == 0) {
ssl->hsHashes = acceptHash;
ret = HashRaw(ssl, input, acceptOffset);
}
if (ret == 0) {
ret = HashRaw(ssl, zeros, ECH_ACCEPT_CONFIRMATION_SZ);
}
if (ret == 0) {
ret = HashRaw(ssl, input + acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ,
helloSz + headerSz - (acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ));
}
/* get the modified transcript hash */
if (ret == 0) {
ret = GetMsgHash(ssl, transcriptEchConf);
if (ret > 0) {
ret = 0;
}
}
/* pick the right type and size based on mac_algorithm */
if (ret == 0) {
switch (ssl->specs.mac_algorithm) {
#ifndef NO_SHA256
case sha256_mac:
digestType = WC_SHA256;
digestSize = WC_SHA256_DIGEST_SIZE;
break;
#endif /* !NO_SHA256 */
#ifdef WOLFSSL_SHA384
case sha384_mac:
digestType = WC_SHA384;
digestSize = WC_SHA384_DIGEST_SIZE;
break;
#endif /* WOLFSSL_SHA384 */
#ifdef WOLFSSL_TLS13_SHA512
case sha512_mac:
digestType = WC_SHA512;
digestSize = WC_SHA512_DIGEST_SIZE;
break;
#endif /* WOLFSSL_TLS13_SHA512 */
#ifdef WOLFSSL_SM3
case sm3_mac:
digestType = WC_SM3;
digestSize = WC_SM3_DIGEST_SIZE;
break;
#endif /* WOLFSSL_SM3 */
default:
ret = WOLFSSL_FATAL_ERROR;
break;
}
}
/* extract clientRandomInner with a key of all zeros */
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
#if !defined(HAVE_FIPS) || \
(defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0))
ret = wc_HKDF_Extract_ex(digestType, zeros, (word32)digestSize,
ssl->arrays->clientRandomInner, RAN_LEN, expandLabelPrk,
ssl->heap, ssl->devId);
#else
ret = wc_HKDF_Extract(digestType, zeros, digestSize,
ssl->arrays->clientRandomInner, RAN_LEN, expandLabelPrk);
#endif
PRIVATE_KEY_LOCK();
}
/* tls expand with the confirmation label */
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
#ifdef WOLFSSL_DTLS13
if (ssl->options.dtls) {
ret = Tls13HKDFExpandKeyLabel(ssl, acceptExpanded,
ECH_ACCEPT_CONFIRMATION_SZ, expandLabelPrk, (word32)digestSize,
dtls13ProtocolLabel, DTLS13_PROTOCOL_LABEL_SZ, label, labelSz,
transcriptEchConf, (word32)digestSize, digestType,
WOLFSSL_SERVER_END);
}
else
#endif
{
ret = Tls13HKDFExpandKeyLabel(ssl, acceptExpanded,
ECH_ACCEPT_CONFIRMATION_SZ, expandLabelPrk, (word32)digestSize,
tls13ProtocolLabel, TLS13_PROTOCOL_LABEL_SZ, label, labelSz,
transcriptEchConf, (word32)digestSize, digestType,
WOLFSSL_SERVER_END);
}
PRIVATE_KEY_LOCK();
}
if (acceptHash != NULL) {
ssl->hsHashes = acceptHash;
FreeHandshakeHashes(ssl);
}
ssl->hsHashes = tmpHashes;
ForceZero(expandLabelPrk, sizeof(expandLabelPrk));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(expandLabelPrk, sizeof(expandLabelPrk));
#endif
return ret;
}
#endif
#ifndef NO_WOLFSSL_CLIENT
@@ -4226,90 +4475,6 @@ static int WritePSKBinders(WOLFSSL* ssl, byte* output, word32 idx)
}
#endif
#if defined(HAVE_ECH)
/* returns the index of the first supported cipher suite, -1 if none */
int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config)
{
int i, j, supported = 0;
for (i = 0; i < config->numCipherSuites; i++) {
supported = 0;
for (j = 0; j < HPKE_SUPPORTED_KDF_LEN; j++) {
if (config->cipherSuites[i].kdfId == hpkeSupportedKdf[j])
break;
}
if (j < HPKE_SUPPORTED_KDF_LEN)
for (j = 0; j < HPKE_SUPPORTED_AEAD_LEN; j++) {
if (config->cipherSuites[i].aeadId == hpkeSupportedAead[j]) {
supported = 1;
break;
}
}
if (supported)
return i;
}
return WOLFSSL_FATAL_ERROR;
}
/* returns status after we hash the inner client hello */
static int EchHashHelloInner(WOLFSSL* ssl, WOLFSSL_ECH* ech)
{
int ret = 0;
word32 realSz;
HS_Hashes* tmpHashes;
#ifdef WOLFSSL_DTLS13
byte falseHeader[DTLS13_HANDSHAKE_HEADER_SZ];
#else
byte falseHeader[HANDSHAKE_HEADER_SZ];
#endif
if (ssl == NULL || ech == NULL) {
return BAD_FUNC_ARG;
}
if (ssl->options.side == WOLFSSL_CLIENT_END) {
realSz = ech->innerClientHelloLen - ech->paddingLen - ech->hpke->Nt;
}
else {
realSz = ech->innerClientHelloLen;
}
tmpHashes = ssl->hsHashes;
ssl->hsHashes = ssl->hsHashesEch;
if (ssl->options.echAccepted == 0 && ssl->hsHashes == NULL) {
ret = InitHandshakeHashes(ssl);
if (ret == 0) {
ssl->hsHashesEch = ssl->hsHashes;
ech->innerCount = 1;
}
}
if (ret == 0) {
if (ssl->options.side == WOLFSSL_CLIENT_END) {
/* client-side: innerClientHello contains body only */
AddTls13HandShakeHeader(falseHeader, realSz, 0, 0, client_hello, ssl);
ret = HashRaw(ssl, falseHeader, sizeof(falseHeader));
if (ret == 0) {
ret = HashRaw(ssl, ech->innerClientHello, realSz);
}
}
else {
/* server-side: innerClientHello contains header + body */
ret = HashRaw(ssl, ech->innerClientHello,
sizeof(falseHeader) + realSz);
}
}
ssl->hsHashes = tmpHashes;
return ret;
}
#endif
static void GetTls13SessionId(WOLFSSL* ssl, byte* output, word32* idx)
{
if (ssl->session->sessionIDSz > 0) {
@@ -4900,162 +5065,24 @@ static int Dtls13ClientDoDowngrade(WOLFSSL* ssl)
#endif /* WOLFSSL_DTLS13 && !WOLFSSL_NO_CLIENT*/
#if defined(HAVE_ECH)
static int EchCalcAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
const byte* input, int acceptOffset, int helloSz, byte isHrr,
byte* acceptExpanded)
{
int ret = 0;
int digestType = 0;
int digestSize = 0;
int hashSz = 0;
HS_Hashes* tmpHashes;
HS_Hashes* acceptHash = NULL;
byte zeros[WC_MAX_DIGEST_SIZE];
byte transcriptEchConf[WC_MAX_DIGEST_SIZE];
byte clientHelloInnerHash[WC_MAX_DIGEST_SIZE];
byte expandLabelPrk[WC_MAX_DIGEST_SIZE];
byte messageHashHeader[HANDSHAKE_HEADER_SZ];
XMEMSET(zeros, 0, sizeof(zeros));
XMEMSET(transcriptEchConf, 0, sizeof(transcriptEchConf));
XMEMSET(clientHelloInnerHash, 0, sizeof(clientHelloInnerHash));
XMEMSET(expandLabelPrk, 0, sizeof(expandLabelPrk));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Add("ECH PRK", expandLabelPrk, sizeof(expandLabelPrk));
#endif
tmpHashes = ssl->hsHashes;
if (isHrr) {
/* the transcript hash of ClientHelloInner1 */
hashSz = GetMsgHash(ssl, clientHelloInnerHash);
if (hashSz <= 0) {
ret = hashSz;
}
/* restart ECH transcript hash, similar to RestartHandshakeHash but
* don't add a cookie */
if (ret == 0) {
ret = InitHandshakeHashes(ssl);
}
if (ret == 0) {
ssl->hsHashesEch = ssl->hsHashes;
AddTls13HandShakeHeader(messageHashHeader, (word32)hashSz, 0, 0,
message_hash, ssl);
ret = HashRaw(ssl, messageHashHeader, sizeof(messageHashHeader));
}
if (ret == 0) {
ret = HashRaw(ssl, clientHelloInnerHash, (word32)hashSz);
}
}
/* hash with zeros for confirmation computation */
if (ret == 0) {
ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashesEch, &acceptHash);
}
if (ret == 0) {
ssl->hsHashes = acceptHash;
ret = HashRaw(ssl, input, acceptOffset);
}
if (ret == 0) {
ret = HashRaw(ssl, zeros, ECH_ACCEPT_CONFIRMATION_SZ);
}
if (ret == 0) {
ret = HashRaw(ssl, input + acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ,
helloSz + HANDSHAKE_HEADER_SZ -
(acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ));
}
/* get the modified transcript hash */
if (ret == 0) {
ret = GetMsgHash(ssl, transcriptEchConf);
if (ret > 0) {
ret = 0;
}
}
/* pick the right type and size based on mac_algorithm */
if (ret == 0) {
switch (ssl->specs.mac_algorithm) {
#ifndef NO_SHA256
case sha256_mac:
digestType = WC_SHA256;
digestSize = WC_SHA256_DIGEST_SIZE;
break;
#endif /* !NO_SHA256 */
#ifdef WOLFSSL_SHA384
case sha384_mac:
digestType = WC_SHA384;
digestSize = WC_SHA384_DIGEST_SIZE;
break;
#endif /* WOLFSSL_SHA384 */
#ifdef WOLFSSL_TLS13_SHA512
case sha512_mac:
digestType = WC_SHA512;
digestSize = WC_SHA512_DIGEST_SIZE;
break;
#endif /* WOLFSSL_TLS13_SHA512 */
#ifdef WOLFSSL_SM3
case sm3_mac:
digestType = WC_SM3;
digestSize = WC_SM3_DIGEST_SIZE;
break;
#endif /* WOLFSSL_SM3 */
default:
ret = WOLFSSL_FATAL_ERROR;
break;
}
}
/* extract clientRandomInner with a key of all zeros */
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
#if !defined(HAVE_FIPS) || \
(defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0))
ret = wc_HKDF_Extract_ex(digestType, zeros, (word32)digestSize,
ssl->arrays->clientRandomInner, RAN_LEN, expandLabelPrk,
ssl->heap, ssl->devId);
#else
ret = wc_HKDF_Extract(digestType, zeros, digestSize,
ssl->arrays->clientRandomInner, RAN_LEN, expandLabelPrk);
#endif
PRIVATE_KEY_LOCK();
}
/* tls expand with the confirmation label */
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
ret = Tls13HKDFExpandKeyLabel(ssl, acceptExpanded,
ECH_ACCEPT_CONFIRMATION_SZ, expandLabelPrk, (word32)digestSize,
tls13ProtocolLabel, TLS13_PROTOCOL_LABEL_SZ, label, labelSz,
transcriptEchConf, (word32)digestSize, digestType,
WOLFSSL_SERVER_END);
PRIVATE_KEY_LOCK();
}
if (acceptHash != NULL) {
ssl->hsHashes = acceptHash;
FreeHandshakeHashes(ssl);
}
ssl->hsHashes = tmpHashes;
return ret;
}
/* check if the server accepted ech or not, return status */
static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
const byte* input, int acceptOffset, int helloSz)
{
int ret = 0;
int isHrr = 0;
int headerSz;
HS_Hashes* tmpHashes;
byte acceptConfirmation[ECH_ACCEPT_CONFIRMATION_SZ];
XMEMSET(acceptConfirmation, 0, sizeof(acceptConfirmation));
tmpHashes = ssl->hsHashes;
ssl->hsHashes = ssl->hsHashesEch;
#ifdef WOLFSSL_DTLS13
headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ :
HANDSHAKE_HEADER_SZ;
#else
headerSz = HANDSHAKE_HEADER_SZ;
#endif
if (labelSz == ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ &&
XMEMCMP(label, echHrrAcceptConfirmationLabel, labelSz) == 0) {
@@ -5065,6 +5092,9 @@ static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
ret = EchCalcAcceptance(ssl, label, labelSz, input, acceptOffset, helloSz,
isHrr, acceptConfirmation);
tmpHashes = ssl->hsHashes;
ssl->hsHashes = ssl->hsHashesEch;
if (ret == 0) {
/* last 8 bytes must match the expand output */
ret = ConstantCompare(acceptConfirmation, input + acceptOffset,
@@ -5076,8 +5106,7 @@ static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
/* after HRR, hsHashesEch must contain:
* message_hash(ClientHelloInner1) || HRR (actual, not zeros) */
if (isHrr) {
ssl->hsHashes = ssl->hsHashesEch;
ret = HashRaw(ssl, input, helloSz + HANDSHAKE_HEADER_SZ);
ret = HashRaw(ssl, input, helloSz + headerSz);
}
/* normal TLS code will calculate transcript of ServerHello */
else {
@@ -5100,46 +5129,7 @@ static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
ssl->hsHashes = tmpHashes;
return ret;
}
/* replace the last acceptance field for either ServerHello or HRR with the ECH
* acceptance parameter, return status */
static int EchWriteAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
byte* output, int acceptOffset, int helloSz, byte msgType)
{
int ret = 0;
HS_Hashes* tmpHashes;
tmpHashes = ssl->hsHashes;
ssl->hsHashes = ssl->hsHashesEch;
ret = EchCalcAcceptance(ssl, label, labelSz, output, acceptOffset,
helloSz - HANDSHAKE_HEADER_SZ, msgType == hello_retry_request,
output + acceptOffset);
/* after HRR, hsHashesEch must contain:
* message_hash(ClientHelloInner1) || HRR (actual, not zeros) */
if (ret == 0 && msgType == hello_retry_request) {
ssl->hsHashes = ssl->hsHashesEch;
ret = HashRaw(ssl, output, helloSz);
}
/* normal TLS code will calculate transcript of ServerHello */
else if (ret == 0) {
ssl->options.echAccepted = 1;
ssl->hsHashes = tmpHashes;
FreeHandshakeHashes(ssl);
tmpHashes = ssl->hsHashesEch;
ssl->hsHashesEch = NULL;
}
ssl->hsHashes = tmpHashes;
ForceZero(expandLabelPrk, sizeof(expandLabelPrk));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(expandLabelPrk, sizeof(expandLabelPrk));
#endif
return ret;
}
#endif
#endif /* HAVE_ECH */
/* handle processing of TLS 1.3 server_hello (2) and hello_retry_request (6) */
/* Handle the ServerHello message from the server.
@@ -6782,6 +6772,60 @@ static int DoTls13SupportedVersions(WOLFSSL* ssl, const byte* input, word32 i,
return 0;
}
#ifdef HAVE_ECH
/* Calculate and write the 8 ECH confirmation bytes.
* Output into confirmation field on HRR and into ServerRandom on ServerHello.
*
* ssl SSL/TLS object.
* label Ascii string describing ECH acceptance or rejection.
* labelSz Length of label excluding NULL character.
* output The buffer to calculate/write confirmation from/to.
* acceptOffset Where the 8 ECH confirmation bytes should be placed.
* helloSz Size of hello message.
* msgType Type of message being written.
* returns 0 on success and otherwise failure.
*/
static int EchWriteAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
byte* output, int acceptOffset, int helloSz, byte msgType)
{
int ret = 0;
int headerSz;
HS_Hashes* tmpHashes;
#ifdef WOLFSSL_DTLS13
headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ :
HANDSHAKE_HEADER_SZ;
#else
headerSz = HANDSHAKE_HEADER_SZ;
#endif
ret = EchCalcAcceptance(ssl, label, labelSz, output, acceptOffset,
helloSz - headerSz, msgType == hello_retry_request,
output + acceptOffset);
tmpHashes = ssl->hsHashes;
ssl->hsHashes = ssl->hsHashesEch;
/* after HRR, hsHashesEch must contain:
* message_hash(ClientHelloInner1) || HRR (actual, not zeros) */
if (ret == 0 && msgType == hello_retry_request) {
ret = HashRaw(ssl, output, helloSz);
}
/* normal TLS code will calculate transcript of ServerHello */
else if (ret == 0) {
ssl->options.echAccepted = 1;
ssl->hsHashes = tmpHashes;
FreeHandshakeHashes(ssl);
tmpHashes = ssl->hsHashesEch;
ssl->hsHashesEch = NULL;
}
ssl->hsHashes = tmpHashes;
return ret;
}
#endif
/* Handle a ClientHello handshake message.
* If the protocol version in the message is not TLS v1.3 or higher, use
* DoClientHello()
@@ -7199,7 +7243,8 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
#if defined(HAVE_ECH)
/* hash clientHelloInner to hsHashesEch */
if (ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) {
if (echX != NULL && ssl->ctx->echConfigs != NULL &&
!ssl->options.disableECH) {
ret = EchHashHelloInner(ssl, (WOLFSSL_ECH*)echX->data);
if (ret != 0)
goto exit_dch;
@@ -7470,115 +7515,6 @@ exit_dch:
return ret;
}
#ifdef HAVE_ECH
/* replace the last acceptance field for either sever hello or hrr with the ech
* acceptance parameter, return status */
static int EchWriteAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
byte* output, int acceptOffset, int helloSz, byte msgType)
{
int ret = 0;
int digestType = 0;
int digestSize = 0;
HS_Hashes* tmpHashes = NULL;
byte zeros[WC_MAX_DIGEST_SIZE];
byte transcriptEchConf[WC_MAX_DIGEST_SIZE];
byte expandLabelPrk[WC_MAX_DIGEST_SIZE];
XMEMSET(zeros, 0, sizeof(zeros));
XMEMSET(transcriptEchConf, 0, sizeof(transcriptEchConf));
XMEMSET(expandLabelPrk, 0, sizeof(expandLabelPrk));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Add("ECH PRK", expandLabelPrk,
sizeof(expandLabelPrk));
#endif
/* store so we can restore regardless of the outcome */
tmpHashes = ssl->hsHashes;
ssl->hsHashes = ssl->hsHashesEch;
/* hash up to the acceptOffset */
ret = HashRaw(ssl, output, acceptOffset);
/* hash 8 zeros */
if (ret == 0)
ret = HashRaw(ssl, zeros, ECH_ACCEPT_CONFIRMATION_SZ);
/* hash the rest of the hello */
if (ret == 0) {
ret = HashRaw(ssl, output + acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ,
helloSz - (acceptOffset + ECH_ACCEPT_CONFIRMATION_SZ));
}
/* get the modified transcript hash */
if (ret == 0)
ret = GetMsgHash(ssl, transcriptEchConf);
if (ret > 0)
ret = 0;
/* pick the right type and size based on mac_algorithm */
if (ret == 0) {
switch (ssl->specs.mac_algorithm) {
#ifndef NO_SHA256
case sha256_mac:
digestType = WC_SHA256;
digestSize = WC_SHA256_DIGEST_SIZE;
break;
#endif /* !NO_SHA256 */
#ifdef WOLFSSL_SHA384
case sha384_mac:
digestType = WC_SHA384;
digestSize = WC_SHA384_DIGEST_SIZE;
break;
#endif /* WOLFSSL_SHA384 */
#ifdef WOLFSSL_TLS13_SHA512
case sha512_mac:
digestType = WC_SHA512;
digestSize = WC_SHA512_DIGEST_SIZE;
break;
#endif /* WOLFSSL_TLS13_SHA512 */
#ifdef WOLFSSL_SM3
case sm3_mac:
digestType = WC_SM3;
digestSize = WC_SM3_DIGEST_SIZE;
break;
#endif /* WOLFSSL_SM3 */
default:
ret = WOLFSSL_FATAL_ERROR;
break;
}
}
/* extract clientRandom with a key of all zeros */
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
#if !defined(HAVE_FIPS) || \
(defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(6,0))
ret = wc_HKDF_Extract_ex(digestType, zeros, (word32)digestSize,
ssl->arrays->clientRandom, RAN_LEN, expandLabelPrk,
ssl->heap, ssl->devId);
#else
ret = wc_HKDF_Extract(digestType, zeros, digestSize,
ssl->arrays->clientRandom, RAN_LEN, expandLabelPrk);
#endif
PRIVATE_KEY_LOCK();
}
/* tls expand with the confirmation label */
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
ret = Tls13HKDFExpandKeyLabel(ssl, output + acceptOffset,
ECH_ACCEPT_CONFIRMATION_SZ, expandLabelPrk, (word32)digestSize,
tls13ProtocolLabel, TLS13_PROTOCOL_LABEL_SZ, label, labelSz,
transcriptEchConf, (word32)digestSize, digestType,
WOLFSSL_SERVER_END);
PRIVATE_KEY_LOCK();
}
/* mark that ech was accepted */
if (ret == 0 && msgType != hello_retry_request)
ssl->options.echAccepted = 1;
/* free hsHashesEch, if this is an HRR we will start at client hello 2*/
FreeHandshakeHashes(ssl);
ssl->hsHashesEch = NULL;
ssl->hsHashes = tmpHashes;
ForceZero(expandLabelPrk, sizeof(expandLabelPrk));
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(expandLabelPrk, sizeof(expandLabelPrk));
#endif
return ret;
}
#endif
/* Send TLS v1.3 ServerHello message to client.
* Only a server will send this message.
*