refactor ECH code to handle hrr with special confirmation

This commit is contained in:
John Bland
2023-09-25 01:48:58 -04:00
parent 83d7225236
commit 037c44609d
3 changed files with 123 additions and 98 deletions

View File

@@ -11451,8 +11451,9 @@ static int TLSX_ServerECH_Use(TLSX** extensions, void* heap,
return ret;
}
/* return length after writing the ech */
static int TLSX_ECH_Write(WOLFSSL_ECH* ech, byte* writeBuf, word16* offset)
/* return status after writing the ech and updating offset */
static int TLSX_ECH_Write(WOLFSSL_ECH* ech, byte msgType, byte* writeBuf,
word16* offset)
{
int ret = 0;
int rngRet = -1;
@@ -11467,6 +11468,13 @@ static int TLSX_ECH_Write(WOLFSSL_ECH* ech, byte* writeBuf, word16* offset)
WC_RNG rng[1];
#endif
WOLFSSL_MSG("TLSX_ECH_Write");
if (msgType == hello_retry_request) {
/* reserve space to write the confirmation to */
*offset += ECH_ACCEPT_CONFIRMATION_SZ;
/* set confBuf */
ech->confBuf = writeBuf;
return 0;
}
if (ech->state == ECH_WRITE_NONE || ech->state == ECH_PARSED_INTERNAL)
return 0;
if (ech->state == ECH_WRITE_RETRY_CONFIGS) {
@@ -11543,9 +11551,15 @@ static int TLSX_ECH_Write(WOLFSSL_ECH* ech, byte* writeBuf, word16* offset)
#endif
}
else {
/* write enc to writeBuf_p */
ret = wc_HpkeSerializePublicKey(ech->hpke, ech->ephemeralKey,
writeBuf_p, &ech->encLen);
/* set to emptry string on hrr */
if (msgType == hello_retry_request) {
XMEMSET(writeBuf_p, 0, ech->encLen);
}
else {
/* write enc to writeBuf_p */
ret = wc_HpkeSerializePublicKey(ech->hpke, ech->ephemeralKey,
writeBuf_p, &ech->encLen);
}
writeBuf_p += ech->encLen;
/* innerClientHelloLen */
c16toa(ech->innerClientHelloLen, writeBuf_p);
@@ -11722,89 +11736,85 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
WOLFSSL_EchConfig* echConfig;
byte* aadCopy;
byte* readBuf_p = (byte*)readBuf;
WOLFSSL_MSG("TLSX_ECH_Parse");
if (size == 0)
return BAD_FUNC_ARG;
/* retry configs */
if (msgType == encrypted_extensions) {
ret = wolfSSL_SetEchConfigs(ssl, readBuf, size);
if (ret == WOLFSSL_SUCCESS)
ret = 0;
}
else if (msgType == client_hello && ssl->ctx->echConfigs != NULL) {
/* HRR with special confirmation */
else if (msgType == hello_retry_request && ssl->options.useEch) {
/* length must be 8 */
if (size != ECH_ACCEPT_CONFIRMATION_SZ)
return BAD_FUNC_ARG;
/* get extension */
echX = TLSX_Find(ssl->extensions, TLSX_ECH);
if (echX == NULL)
return BAD_FUNC_ARG;
ech = (WOLFSSL_ECH*)echX->data;
ech->confBuf = (byte*)readBuf;
}
else if (msgType == client_hello && ssl->ctx->echConfigs != NULL) {
/* get extension */
echX = TLSX_Find(ssl->extensions, TLSX_ECH);
if (echX == NULL)
return BAD_FUNC_ARG;
ech = (WOLFSSL_ECH*)echX->data;
/* read the ech parameters before the payload */
ech->type = *readBuf_p;
readBuf_p++;
if (ech->type == ECH_TYPE_INNER) {
ech->state = ECH_PARSED_INTERNAL;
return 0;
}
/* technically the payload would only be 1 byte at this length */
if (size < 11 + ech->encLen)
return BAD_FUNC_ARG;
/* read kdfId */
ato16(readBuf_p, &ech->cipherSuite.kdfId);
readBuf_p += 2;
/* read aeadId */
ato16(readBuf_p, &ech->cipherSuite.aeadId);
readBuf_p += 2;
/* read configId */
ech->configId = *readBuf_p;
readBuf_p++;
/* read encLen */
ato16(readBuf_p, &ech->encLen);
readBuf_p += 2;
if (ech->encLen > HPKE_Npk_MAX)
return BAD_FUNC_ARG;
/* read enc */
XMEMCPY(ech->enc, readBuf_p, ech->encLen);
readBuf_p += ech->encLen;
/* read hello inner len */
ato16(readBuf_p, &ech->innerClientHelloLen);
ech->innerClientHelloLen -= AES_BLOCK_SIZE;
readBuf_p += 2;
ech->outerClientPayload = readBuf_p;
/* make a copy of the aad */
aadCopy = (byte*)XMALLOC(ech->aadLen, ssl->heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (aadCopy == NULL)
return MEMORY_E;
XMEMCPY(aadCopy, ech->aad, ech->aadLen);
/* set the ech payload of the copy to zeros */
XMEMSET(aadCopy + (readBuf_p - ech->aad), 0,
ech->innerClientHelloLen + AES_BLOCK_SIZE);
/* allocate the inner payload buffer */
ech->innerClientHello =
(byte*)XMALLOC(ech->innerClientHelloLen + HANDSHAKE_HEADER_SZ,
ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
if (ech->innerClientHello == NULL) {
XFREE(aadCopy, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return MEMORY_E;
}
/* first check if the config id matches */
echConfig = ssl->ctx->echConfigs;
while (echConfig != NULL) {
/* decrypt with this config */
if (echConfig->configId == ech->configId) {
@@ -11812,25 +11822,19 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
ssl->heap);
break;
}
echConfig = echConfig->next;
}
/* try to decrypt with all configs */
if (echConfig == NULL || ret != 0) {
echConfig = ssl->ctx->echConfigs;
while (echConfig != NULL) {
ret = TLSX_ExtractEch(ech, echConfig, aadCopy, ech->aadLen,
ssl->heap);
if (ret== 0)
break;
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);
@@ -11839,19 +11843,15 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
}
else {
i = 0;
/* decrement until before the padding */
while (ech->innerClientHello[ech->innerClientHelloLen +
HANDSHAKE_HEADER_SZ - i - 1] != ECH_TYPE_INNER) {
i++;
}
/* subtract the length of the padding from the length */
ech->innerClientHelloLen -= i;
}
XFREE(aadCopy, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return 0;
}
@@ -11868,6 +11868,8 @@ static void TLSX_ECH_Free(WOLFSSL_ECH* ech, void* heap)
ech->hpke->heap);
if (ech->hpke != NULL)
XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (ech->hpkeContext != NULL)
XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER);
(void)heap;
@@ -12536,7 +12538,7 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
case TLSX_ECH:
WOLFSSL_MSG("ECH extension to write");
ret = ECH_WRITE((WOLFSSL_ECH*)extension->data,
ret = ECH_WRITE((WOLFSSL_ECH*)extension->data, msgType,
output + offset, &offset);
break;
#endif

View File

@@ -170,11 +170,14 @@ static const byte dtls13ProtocolLabel[DTLS13_PROTOCOL_LABEL_SZ + 1] = "dtls13";
#endif /* WOLFSSL_DTLS13 */
#if defined(HAVE_ECH)
#define ECH_ACCEPT_CONFIRMATION_SZ 8
#define ECH_ACCEPT_CONFIRMATION_LABEL_SZ 23
#define ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ 27
static const byte
echAcceptConfirmationLabel[ECH_ACCEPT_CONFIRMATION_LABEL_SZ + 1] =
"ech accept confirmation";
static const byte
echHrrAcceptConfirmationLabel[ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ + 1] =
"hrr ech accept confirmation";
#endif
#ifndef NO_CERTS
@@ -4719,8 +4722,8 @@ static int Dtls13DoDowngrade(WOLFSSL* ssl)
#if defined(HAVE_ECH)
/* check if the server accepted ech or not */
static int EchCheckAcceptance(WOLFSSL* ssl, const byte* input,
int serverRandomOffset, int helloSz)
static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz,
const byte* input, int acceptOffset, int helloSz)
{
int ret = 0;
int digestType;
@@ -4742,15 +4745,15 @@ static int EchCheckAcceptance(WOLFSSL* ssl, const byte* input,
ssl->hsHashes = acceptHashes;
/* hash up to the last 8 bytes */
if (ret == 0)
ret = HashRaw(ssl, input, serverRandomOffset + RAN_LEN -
ECH_ACCEPT_CONFIRMATION_SZ);
ret = HashRaw(ssl, input, 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, input + serverRandomOffset + RAN_LEN,
helloSz + HANDSHAKE_HEADER_SZ - (serverRandomOffset + RAN_LEN));
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)
@@ -4816,14 +4819,10 @@ static int EchCheckAcceptance(WOLFSSL* ssl, const byte* input,
}
if (ret == 0) {
/* last 8 bytes should match our expand output */
ret = XMEMCMP(acceptConfirmation,
ssl->arrays->serverRandom + RAN_LEN - ECH_ACCEPT_CONFIRMATION_SZ,
ret = XMEMCMP(acceptConfirmation, input + acceptOffset,
ECH_ACCEPT_CONFIRMATION_SZ);
/* ech accepted */
if (ret == 0) {
/* use the inner random for client random */
XMEMCPY(ssl->arrays->clientRandom, ssl->arrays->clientRandomInner,
RAN_LEN);
/* switch back to original hsHashes to free */
ssl->hsHashes = tmpHashes;
/* set the final hsHashes to the ech hashes */
@@ -4850,10 +4849,10 @@ static int EchCheckAcceptance(WOLFSSL* ssl, const byte* input,
return ret;
}
/* replace the last 8 bytes of the server random with the ech acceptance
* parameter, return status */
static int EchWriteAcceptance(WOLFSSL* ssl, byte* output,
int serverRandomOffset, int helloSz)
/* 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)
{
int ret = 0;
int digestType;
@@ -4866,37 +4865,30 @@ static int EchWriteAcceptance(WOLFSSL* ssl, byte* output,
XMEMSET(zeros, 0, sizeof(zeros));
XMEMSET(transcriptEchConf, 0, sizeof(transcriptEchConf));
XMEMSET(expandLabelPrk, 0, sizeof(expandLabelPrk));
/* copy ech hashes to accept */
ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashes, &acceptHashes);
/* swap hsHashes to acceptHashes */
tmpHashes = ssl->hsHashes;
ssl->hsHashes = acceptHashes;
/* hash up to the last 8 bytes */
if (ret == 0)
ret = HashRaw(ssl, output, serverRandomOffset + RAN_LEN -
ECH_ACCEPT_CONFIRMATION_SZ);
if (ret == 0) {
/* swap hsHashes to acceptHashes */
tmpHashes = ssl->hsHashes;
ssl->hsHashes = acceptHashes;
/* hash up to the acceptOffset */
ret = HashRaw(ssl, output, acceptOffset);
}
/* hash 8 zeros */
if (ret == 0)
ret = HashRaw(ssl, zeros, ECH_ACCEPT_CONFIRMATION_SZ);
ret = HashRaw(ssl, zeros, ECH_ACCEPT_CONFIRMATION_SZ);
/* hash the rest of the hello */
if (ret == 0)
ret = HashRaw(ssl, output + serverRandomOffset + RAN_LEN,
helloSz - (serverRandomOffset + RAN_LEN));
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)
if (ret == 0) {
switch (ssl->specs.mac_algorithm) {
#ifndef NO_SHA256
case sha256_mac:
@@ -4926,7 +4918,7 @@ static int EchWriteAcceptance(WOLFSSL* ssl, byte* output,
ret = -1;
break;
}
}
/* extract clientRandom with a key of all zeros */
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
@@ -4941,7 +4933,6 @@ static int EchWriteAcceptance(WOLFSSL* ssl, byte* output,
#endif
PRIVATE_KEY_LOCK();
}
/* tls expand with the confirmation label */
if (ret == 0) {
PRIVATE_KEY_UNLOCK();
@@ -4954,16 +4945,9 @@ static int EchWriteAcceptance(WOLFSSL* ssl, byte* output,
transcriptEchConf, digestSize, digestType, WOLFSSL_SERVER_END);
PRIVATE_KEY_LOCK();
}
if (ret == 0)
XMEMCPY(ssl->arrays->serverRandom, output + serverRandomOffset,
RAN_LEN);
/* free acceptHashes */
FreeHandshakeHashes(ssl);
ssl->hsHashes = tmpHashes;
return ret;
}
#endif
@@ -4989,7 +4973,9 @@ typedef struct Dsh13Args {
byte sessIdSz;
byte extMsgType;
#if defined(HAVE_ECH)
int serverRandomOffset;
byte* acceptLabel;
word32 acceptOffset;
word16 acceptLabelSz;
#endif
} Dsh13Args;
@@ -5146,7 +5132,8 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
/* Server random - keep for debugging. */
XMEMCPY(ssl->arrays->serverRandom, input + args->idx, RAN_LEN);
#if defined(HAVE_ECH)
args->serverRandomOffset = args->idx;
/* last 8 bytes of server random */
args->acceptOffset = args->idx + RAN_LEN - ECH_ACCEPT_CONFIRMATION_SZ;
#endif
args->idx += RAN_LEN;
@@ -5432,11 +5419,30 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
return ret;
#if defined(HAVE_ECH)
TLSX* echX = NULL;
/* check for acceptConfirmation and HashInput with 8 0 bytes */
if (ssl->options.useEch == 1) {
ret = EchCheckAcceptance(ssl, input, args->serverRandomOffset, helloSz);
echX = TLSX_Find(ssl->extensions, TLSX_ECH);
/* account for hrr */
if (args->extMsgType == hello_retry_request) {
args->acceptOffset = ((WOLFSSL_ECH*)echX->data)->confBuf - input;
args->acceptLabel = (byte*)echHrrAcceptConfirmationLabel;
args->acceptLabelSz = ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ;
}
else {
args->acceptLabel = (byte*)echAcceptConfirmationLabel;
args->acceptLabelSz = ECH_ACCEPT_CONFIRMATION_LABEL_SZ;
}
/* check acceptance */
ret = EchCheckAcceptance(ssl, args->acceptLabel, args->acceptLabelSz,
input, args->acceptOffset, helloSz);
if (ret != 0)
return ret;
/* use the inner random for client random */
if (args->extMsgType != hello_retry_request) {
XMEMCPY(ssl->arrays->clientRandom, ssl->arrays->clientRandomInner,
RAN_LEN);
}
}
#endif
@@ -7236,7 +7242,9 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
int sendSz;
#if defined(HAVE_ECH)
TLSX* echX = NULL;
word32 serverRandomOffset;
byte* acceptLabel = (byte*)echAcceptConfirmationLabel;
word32 acceptOffset;
word16 acceptLabelSz = ECH_ACCEPT_CONFIRMATION_LABEL_SZ;
#endif
WOLFSSL_START(WC_FUNC_SERVER_HELLO_SEND);
@@ -7295,7 +7303,8 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
}
#if defined(HAVE_ECH)
serverRandomOffset = idx;
/* last 8 bytes of server random */
acceptOffset = idx + RAN_LEN - ECH_ACCEPT_CONFIRMATION_SZ;
#endif
/* Store in SSL for debugging. */
@@ -7359,18 +7368,30 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType)
#if defined(HAVE_ECH)
if (ssl->ctx->echConfigs != NULL) {
echX = TLSX_Find(ssl->extensions, TLSX_ECH);
if (echX == NULL)
return -1;
/* use normal offset instead of hrr offset */
if (extMsgType == hello_retry_request) {
acceptOffset = ((WOLFSSL_ECH*)echX->data)->confBuf - output;
acceptLabel = (byte*)echHrrAcceptConfirmationLabel;
acceptLabelSz = ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ;
}
/* replace the last 8 bytes of server random with the accept */
if (((WOLFSSL_ECH*)echX->data)->state == ECH_PARSED_INTERNAL) {
ret = EchWriteAcceptance(ssl, output + RECORD_HEADER_SZ,
serverRandomOffset - RECORD_HEADER_SZ,
ret = EchWriteAcceptance(ssl, acceptLabel, acceptLabelSz,
output + RECORD_HEADER_SZ,
acceptOffset - RECORD_HEADER_SZ,
sendSz - RECORD_HEADER_SZ);
/* remove ech so we don't keep sending it in write */
TLSX_Remove(&ssl->extensions, TLSX_ECH, ssl->heap);
if (extMsgType != hello_retry_request) {
if (ret == 0) {
/* update serverRandom on success */
XMEMCPY(ssl->arrays->serverRandom,
output + acceptOffset -
(RAN_LEN -ECH_ACCEPT_CONFIRMATION_SZ), RAN_LEN);
}
/* remove ech so we don't keep sending it in write */
TLSX_Remove(&ssl->extensions, TLSX_ECH, ssl->heap);
}
}
}
#endif

View File

@@ -2886,6 +2886,7 @@ typedef struct RpkState {
#endif /* HAVE_RPK */
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
#define ECH_ACCEPT_CONFIRMATION_SZ 8
typedef enum {
ECH_TYPE_OUTER = 0,
@@ -2926,6 +2927,7 @@ typedef struct WOLFSSL_ECH {
WOLFSSL_EchConfig* echConfig;
byte* innerClientHello;
byte* outerClientPayload;
byte* confBuf;
EchCipherSuite cipherSuite;
word16 aadLen;
word16 paddingLen;