diff --git a/src/internal.c b/src/internal.c index aa9387267..d6c47eb6f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7298,7 +7298,7 @@ int InitHandshakeHashesAndCopy(WOLFSSL* ssl, HS_Hashes* source, /* save the original so we can put it back afterward */ tmpHashes = ssl->hsHashes; - ssl->hsHashes = NULL; + ssl->hsHashes = *destination; ret = InitHandshakeHashes(ssl); if (ret != 0) { @@ -8435,6 +8435,13 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) } FreeSuites(ssl); FreeHandshakeHashes(ssl); +#ifdef HAVE_ECH + /* try to free the ech hashes in case we errored out */ + ssl->hsHashes = ssl->hsHashesEch; + FreeHandshakeHashes(ssl); + ssl->hsHashes = ssl->hsHashesEchInner; + FreeHandshakeHashes(ssl); +#endif XFREE(ssl->buffers.domainName.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN); /* clear keys struct after session */ @@ -8448,9 +8455,6 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) if (ssl->options.useEch == 1) { FreeEchConfigs(ssl->echConfigs, ssl->heap); ssl->echConfigs = NULL; - /* free the ech specific hashes */ - ssl->hsHashes = ssl->hsHashesEch; - FreeHandshakeHashes(ssl); ssl->options.useEch = 0; } #endif /* HAVE_ECH */ diff --git a/src/ssl.c b/src/ssl.c index 16ab2998c..ca2a52c1b 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -556,7 +556,7 @@ int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl, char* echConfigs64, /* set the ech config from a raw buffer, this is the format ech configs are * sent using retry_configs from the ech server */ int wolfSSL_SetEchConfigs(WOLFSSL* ssl, const byte* echConfigs, - word32 echConfigsLen) + word32 echConfigsLen) { int ret = 0; int i; @@ -683,16 +683,17 @@ int wolfSSL_SetEchConfigs(WOLFSSL* ssl, const byte* echConfigs, &workingConfig->cipherSuites[j].aeadId); } echConfig += cipherSuitesLen; + /* ignore the maximum name length */ + echConfig++; /* publicNameLen */ - ato16(echConfig, &publicNameLen); + publicNameLen = *(echConfig); workingConfig->publicName = (char*)XMALLOC(publicNameLen + 1, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); if (workingConfig->publicName == NULL) { ret = MEMORY_E; break; } - - echConfig += 2; + echConfig++; /* publicName */ XMEMCPY(workingConfig->publicName, echConfig, publicNameLen); /* null terminated */ @@ -869,9 +870,13 @@ int GetEchConfig(WOLFSSL_EchConfig* config, byte* output, word32* outputLen) output += 2; } + /* set maximum name length to 0 */ + *output = 0; + output++; + /* publicName len */ - c16toa(XSTRLEN(config->publicName), output); - output += 2; + *output = XSTRLEN(config->publicName); + output++; /* publicName */ XMEMCPY(output, config->publicName, diff --git a/src/tls.c b/src/tls.c index 1dd6436d4..edf67d453 100644 --- a/src/tls.c +++ b/src/tls.c @@ -12881,41 +12881,35 @@ static int TLSX_ECH_Use(WOLFSSL_EchConfig* echConfig, TLSX** extensions, { int ret = 0; int suiteIndex; + TLSX* echX; WOLFSSL_ECH* ech; - if (extensions == NULL) return BAD_FUNC_ARG; - + /* skip if we already have an ech extension, we will for hrr */ + echX = TLSX_Find(*extensions, TLSX_ECH); + if (echX != NULL) + return 0; /* find a supported cipher suite */ suiteIndex = EchConfigGetSupportedCipherSuite(echConfig); - if (suiteIndex < 0) return suiteIndex; - ech = (WOLFSSL_ECH*)XMALLOC(sizeof(WOLFSSL_ECH), heap, DYNAMIC_TYPE_TMP_BUFFER); - if (ech == NULL) return MEMORY_E; - ForceZero(ech, sizeof(WOLFSSL_ECH)); - ech->state = ECH_WRITE_REAL; - ech->echConfig = echConfig; - /* 0 for outer */ ech->type = ECH_TYPE_OUTER; /* kemId */ ech->kemId = echConfig->kemId; - /* cipherSuite kdf */ ech->cipherSuite.kdfId = echConfig->cipherSuites[suiteIndex].kdfId; /* cipherSuite aead */ ech->cipherSuite.aeadId = echConfig->cipherSuites[suiteIndex].aeadId; /* configId */ ech->configId = echConfig->configId; - /* encLen */ switch (echConfig->kemId) { @@ -12935,30 +12929,23 @@ static int TLSX_ECH_Use(WOLFSSL_EchConfig* echConfig, TLSX** extensions, ech->encLen = DHKEM_X448_ENC_LEN; break; } - /* setup hpke */ ech->hpke = (Hpke*)XMALLOC(sizeof(Hpke), heap, DYNAMIC_TYPE_TMP_BUFFER); - if (ech->hpke == NULL) { XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } - ret = wc_HpkeInit(ech->hpke, ech->kemId, ech->cipherSuite.kdfId, ech->cipherSuite.aeadId, heap); - /* setup the ephemeralKey */ if (ret == 0) ret = wc_HpkeGenerateKeyPair(ech->hpke, &ech->ephemeralKey, rng); - if (ret == 0) ret = TLSX_Push(extensions, TLSX_ECH, ech, heap); - if (ret != 0) { XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER); } - return ret; } @@ -12969,41 +12956,31 @@ static int TLSX_ServerECH_Use(TLSX** extensions, void* heap, int ret; WOLFSSL_ECH* ech; TLSX* echX; - if (extensions == NULL) return BAD_FUNC_ARG; - /* if we already have ech don't override it */ echX = TLSX_Find(*extensions, TLSX_ECH); if (echX != NULL) return 0; - ech = (WOLFSSL_ECH*)XMALLOC(sizeof(WOLFSSL_ECH), heap, DYNAMIC_TYPE_TMP_BUFFER); - if (ech == NULL) return MEMORY_E; - ForceZero(ech, sizeof(WOLFSSL_ECH)); - ech->state = ECH_WRITE_NONE; - /* 0 for outer */ ech->type = ECH_TYPE_OUTER; - ech->echConfig = configs; - /* setup the rest of the settings when we receive ech from the client */ ret = TLSX_Push(extensions, TLSX_ECH, ech, heap); - if (ret != 0) XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER); - 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; @@ -13017,84 +12994,75 @@ static int TLSX_ECH_Write(WOLFSSL_ECH* ech, byte* writeBuf, word16* offset) Hpke hpke[1]; 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) { /* get size then write */ ret = GetEchConfigsEx(ech->echConfig, NULL, &configsLen); - if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) return ret; - ret = GetEchConfigsEx(ech->echConfig, writeBuf, &configsLen); - if (ret != WOLFSSL_SUCCESS) return ret; - *offset += configsLen; - return 0; } - -#ifdef WOLFSSL_SMALL_STACK - hpke = (Hpke*)XMALLOC(sizeof(Hpke), NULL, DYNAMIC_TYPE_TMP_BUFFER); - - if (hpke == NULL) - return MEMORY_E; - - rng = (WC_RNG*)XMALLOC(sizeof(WC_RNG), NULL, DYNAMIC_TYPE_RNG); - - if (rng == NULL) { - XFREE(hpke, NULL, DYNAMIC_TYPE_RNG); - return MEMORY_E; - } -#endif - /* type */ *writeBuf_p = ech->type; writeBuf_p += sizeof(ech->type); - /* outer has body, inner does not */ if (ech->type == ECH_TYPE_OUTER) { /* kdfId */ c16toa(ech->cipherSuite.kdfId, writeBuf_p); writeBuf_p += sizeof(ech->cipherSuite.kdfId); - /* aeadId */ c16toa(ech->cipherSuite.aeadId, writeBuf_p); writeBuf_p += sizeof(ech->cipherSuite.aeadId); - /* configId */ *writeBuf_p = ech->configId; writeBuf_p += sizeof(ech->configId); - /* encLen */ - c16toa(ech->encLen, writeBuf_p); + if (ech->hpkeContext == NULL) { + c16toa(ech->encLen, writeBuf_p); + } + else { + /* set to 0 if this is clientInner 2 */ + c16toa(0, writeBuf_p); + } writeBuf_p += 2; - if (ech->state == ECH_WRITE_GREASE) { +#ifdef WOLFSSL_SMALL_STACK + hpke = (Hpke*)XMALLOC(sizeof(Hpke), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (hpke == NULL) + return MEMORY_E; + rng = (WC_RNG*)XMALLOC(sizeof(WC_RNG), NULL, DYNAMIC_TYPE_RNG); + if (rng == NULL) { + XFREE(hpke, NULL, DYNAMIC_TYPE_RNG); + return MEMORY_E; + } +#endif /* hpke init */ ret = wc_HpkeInit(hpke, ech->kemId, ech->cipherSuite.kdfId, ech->cipherSuite.aeadId, NULL); - if (ret == 0) rngRet = ret = wc_InitRng(rng); - /* create the ephemeralKey */ if (ret == 0) ret = wc_HpkeGenerateKeyPair(hpke, &ephemeralKey, rng); - /* enc */ if (ret == 0) { ret = wc_HpkeSerializePublicKey(hpke, ephemeralKey, writeBuf_p, &ech->encLen); writeBuf_p += ech->encLen; } - if (ret == 0) { /* innerClientHelloLen */ c16toa(GREASE_ECH_SIZE + ((writeBuf_p + 2 - writeBuf) % 32), @@ -13106,45 +13074,40 @@ static int TLSX_ECH_Write(WOLFSSL_ECH* ech, byte* writeBuf, word16* offset) ((writeBuf_p - writeBuf) % 32)); writeBuf_p += GREASE_ECH_SIZE + ((writeBuf_p - writeBuf) % 32); } - if (rngRet == 0) wc_FreeRng(rng); - if (ephemeralKey != NULL) wc_HpkeFreeKey(hpke, hpke->kem, ephemeralKey, hpke->heap); +#ifdef WOLFSSL_SMALL_STACK + XFREE(hpke, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(rng, NULL, DYNAMIC_TYPE_RNG); +#endif } else { - /* write enc to writeBuf_p */ - ret = wc_HpkeSerializePublicKey(ech->hpke, ech->ephemeralKey, - writeBuf_p, &ech->encLen); - writeBuf_p += ech->encLen; - + /* only write enc if this is our first ech, no hpke context */ + if (ech->hpkeContext == NULL) { + /* 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); writeBuf_p += 2; - /* set payload offset for when we finalize */ ech->outerClientPayload = writeBuf_p; - /* write zeros for payload */ XMEMSET(writeBuf_p, 0, ech->innerClientHelloLen); writeBuf_p += ech->innerClientHelloLen; } } - -#ifdef WOLFSSL_SMALL_STACK - XFREE(hpke, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(rng, NULL, DYNAMIC_TYPE_RNG); -#endif - if (ret == 0) *offset += (writeBuf_p - writeBuf); - return ret; } /* return the size needed for the ech extension */ -static int TLSX_ECH_GetSize(WOLFSSL_ECH* ech) +static int TLSX_ECH_GetSize(WOLFSSL_ECH* ech, byte msgType) { int ret; word32 size; @@ -13156,6 +13119,9 @@ static int TLSX_ECH_GetSize(WOLFSSL_ECH* ech) size += GREASE_ECH_SIZE + (size % 32); } + else if (msgType == hello_retry_request) { + size = ECH_ACCEPT_CONFIRMATION_SZ; + } else if (ech->state == ECH_WRITE_NONE || ech->state == ECH_PARSED_INTERNAL) { size = 0; @@ -13174,8 +13140,11 @@ static int TLSX_ECH_GetSize(WOLFSSL_ECH* ech) else { size = sizeof(ech->type) + sizeof(ech->cipherSuite) + - sizeof(ech->configId) + sizeof(word16) + ech->encLen + - sizeof(word16) + ech->innerClientHelloLen; + sizeof(ech->configId) + sizeof(word16) + sizeof(word16) + + ech->innerClientHelloLen; + /* only set encLen if this is inner hello 1 */ + if (ech->hpkeContext == NULL) + size += ech->encLen; } return (int)size; @@ -13193,10 +13162,8 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, word32 rawConfigLen = 0; byte* info = NULL; word32 infoLen = 0; - if (ech == NULL || echConfig == NULL || aad == NULL) return BAD_FUNC_ARG; - /* verify the kem and key len */ switch (echConfig->kemId) { @@ -13219,10 +13186,8 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, expectedEncLen = 0; break; } - if (expectedEncLen != ech->encLen) return BAD_FUNC_ARG; - /* verify the cipher suite */ for (i = 0; i < echConfig->numCipherSuites; i++) { if (echConfig->cipherSuites[i].kdfId == ech->cipherSuite.kdfId && @@ -13230,54 +13195,69 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, break; } } - if (i >= echConfig->numCipherSuites) { return BAD_FUNC_ARG; } - - ech->hpke = (Hpke*)XMALLOC(sizeof(Hpke), heap, DYNAMIC_TYPE_TMP_BUFFER); - - if (ech->hpke == NULL) - return MEMORY_E; - - ret = wc_HpkeInit(ech->hpke, echConfig->kemId, ech->cipherSuite.kdfId, - ech->cipherSuite.aeadId, heap); - - /* get the rawConfigLen */ - if (ret == 0) - ret = GetEchConfig(echConfig, NULL, &rawConfigLen); - - if (ret == WC_NO_ERR_TRACE(LENGTH_ONLY_E)) - ret = 0; - - /* create info */ - if (ret == 0) { - infoLen = TLS_INFO_CONST_STRING_SZ + 1 + rawConfigLen; - info = (byte*)XMALLOC(infoLen, heap, DYNAMIC_TYPE_TMP_BUFFER); - - if (info == NULL) + /* check if hpke already exists, may if HelloRetryRequest */ + if (ech->hpke == NULL) { + ech->hpke = (Hpke*)XMALLOC(sizeof(Hpke), heap, DYNAMIC_TYPE_TMP_BUFFER); + if (ech->hpke == NULL) ret = MEMORY_E; - else { - XMEMCPY(info, (byte*)TLS_INFO_CONST_STRING, - TLS_INFO_CONST_STRING_SZ + 1); - ret = GetEchConfig(echConfig, info + - TLS_INFO_CONST_STRING_SZ + 1, &rawConfigLen); + /* init the hpke struct */ + if (ret == 0) { + ret = wc_HpkeInit(ech->hpke, echConfig->kemId, + ech->cipherSuite.kdfId, ech->cipherSuite.aeadId, heap); + } + if (ret == 0) { + /* allocate hpkeContext */ + ech->hpkeContext = + (HpkeBaseContext*)XMALLOC(sizeof(HpkeBaseContext), + ech->hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (ech->hpkeContext == NULL) + ret = MEMORY_E; + } + /* get the rawConfigLen */ + if (ret == 0) + ret = GetEchConfig(echConfig, NULL, &rawConfigLen); + if (ret == WC_NO_ERR_TRACE(LENGTH_ONLY_E)) + ret = 0; + /* create info */ + if (ret == 0) { + infoLen = TLS_INFO_CONST_STRING_SZ + 1 + rawConfigLen; + info = (byte*)XMALLOC(infoLen, heap, DYNAMIC_TYPE_TMP_BUFFER); + + if (info == NULL) + ret = MEMORY_E; + else { + XMEMCPY(info, (byte*)TLS_INFO_CONST_STRING, + TLS_INFO_CONST_STRING_SZ + 1); + ret = GetEchConfig(echConfig, info + + TLS_INFO_CONST_STRING_SZ + 1, &rawConfigLen); + } + } + /* init the context for opening */ + if (ret == 0) { + ret = wc_HpkeInitOpenContext(ech->hpke, ech->hpkeContext, + echConfig->receiverPrivkey, ech->enc, ech->encLen, info, + infoLen); } } - /* decrypt the ech payload */ - if (ret == 0) - ret = wc_HpkeOpenBase(ech->hpke, echConfig->receiverPrivkey, ech->enc, - ech->encLen, info, infoLen, aad, aadLen, ech->outerClientPayload, - ech->innerClientHelloLen, + if (ret == 0) { + ret = wc_HpkeContextOpenBase(ech->hpke, ech->hpkeContext, aad, aadLen, + ech->outerClientPayload, ech->innerClientHelloLen, ech->innerClientHello + HANDSHAKE_HEADER_SZ); - + } + /* free the hpke and context on failure */ if (ret != 0) { XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER); ech->hpke = NULL; + XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER); + ech->hpkeContext = NULL; } - XFREE(info, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (info != NULL) + XFREE(info, heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; } @@ -13294,94 +13274,98 @@ 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; - if (ssl->options.disableECH) { WOLFSSL_MSG("TLSX_ECH_Parse: ECH disabled. Ignoring."); return 0; } - + /* 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++; - - ato16(readBuf_p, &ech->encLen); - readBuf_p += 2; - - if (ech->encLen > HPKE_Npk_MAX) - return BAD_FUNC_ARG; - - XMEMCPY(ech->enc, readBuf_p, ech->encLen); - readBuf_p += ech->encLen; - + /* only get enc if we don't already have the hpke context */ + if (ech->hpkeContext == NULL) { + /* 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; + } + else { + readBuf_p += 2; + } + /* read hello inner len */ ato16(readBuf_p, &ech->innerClientHelloLen); ech->innerClientHelloLen -= WC_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 + WC_AES_BLOCK_SIZE); - + /* free the old ech in case this is our second client hello */ + if (ech->innerClientHello != NULL) + XFREE(ech->innerClientHello, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); /* 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) { @@ -13389,26 +13373,20 @@ 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 */ + /* 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; @@ -13416,19 +13394,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; } @@ -13442,7 +13416,10 @@ static void TLSX_ECH_Free(WOLFSSL_ECH* ech, void* heap) if (ech->ephemeralKey != NULL) wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, ech->ephemeralKey, ech->hpke->heap); - XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER); + 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; @@ -13452,58 +13429,65 @@ static void TLSX_ECH_Free(WOLFSSL_ECH* ech, void* heap) * status */ int TLSX_FinalizeEch(WOLFSSL_ECH* ech, byte* aad, word32 aadLen) { - int ret; + int ret = 0; void* receiverPubkey = NULL; - byte* info; - int infoLen; - byte* aadCopy; - - /* import the server public key */ - ret = wc_HpkeDeserializePublicKey(ech->hpke, &receiverPubkey, - ech->echConfig->receiverPubkey, ech->encLen); - - if (ret == 0) { - /* create info */ - infoLen = TLS_INFO_CONST_STRING_SZ + 1 + ech->echConfig->rawLen; - info = (byte*)XMALLOC(infoLen, ech->hpke->heap, - DYNAMIC_TYPE_TMP_BUFFER); - if (info == NULL) - ret = MEMORY_E; - + byte* info = NULL; + int infoLen = 0; + byte* aadCopy = NULL; + /* setup hpke context to seal, should be done at most once per connection */ + if (ech->hpkeContext == NULL) { + /* import the server public key */ + ret = wc_HpkeDeserializePublicKey(ech->hpke, &receiverPubkey, + ech->echConfig->receiverPubkey, ech->encLen); + if (ret == 0) { + /* allocate hpke context */ + ech->hpkeContext = + (HpkeBaseContext*)XMALLOC(sizeof(HpkeBaseContext), + ech->hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (ech->hpkeContext == NULL) + ret = MEMORY_E; + } + if (ret == 0) { + /* create info */ + infoLen = TLS_INFO_CONST_STRING_SZ + 1 + ech->echConfig->rawLen; + info = (byte*)XMALLOC(infoLen, ech->hpke->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (info == NULL) + ret = MEMORY_E; + } if (ret == 0) { /* puts the null byte in for me */ - XMEMCPY(info, (byte*)TLS_INFO_CONST_STRING, TLS_INFO_CONST_STRING_SZ - + 1); - XMEMCPY(info + TLS_INFO_CONST_STRING_SZ + 1, ech->echConfig->raw, - ech->echConfig->rawLen); - - /* make a copy of the aad since we overwrite it */ - aadCopy = (byte*)XMALLOC(aadLen, ech->hpke->heap, - DYNAMIC_TYPE_TMP_BUFFER); - if (aadCopy == NULL) { - XFREE(info, ech->hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); - ret = MEMORY_E; - } - } - - if (ret == 0) { - XMEMCPY(aadCopy, aad, aadLen); - - /* seal the payload */ - ret = wc_HpkeSealBase(ech->hpke, ech->ephemeralKey, receiverPubkey, - info, (word32)infoLen, aadCopy, aadLen, ech->innerClientHello, - ech->innerClientHelloLen - ech->hpke->Nt, - ech->outerClientPayload); - - XFREE(info, ech->hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(aadCopy, ech->hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); + XMEMCPY(info, (byte*)TLS_INFO_CONST_STRING, + TLS_INFO_CONST_STRING_SZ + 1); + XMEMCPY(info + TLS_INFO_CONST_STRING_SZ + 1, + ech->echConfig->raw, ech->echConfig->rawLen); + /* init the context for seal with info and keys */ + ret = wc_HpkeInitSealContext(ech->hpke, ech->hpkeContext, + ech->ephemeralKey, receiverPubkey, info, infoLen); } } - + if (ret == 0) { + /* make a copy of the aad since we overwrite it */ + aadCopy = (byte*)XMALLOC(aadLen, ech->hpke->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (aadCopy == NULL) { + ret = MEMORY_E; + } + } + if (ret == 0) { + XMEMCPY(aadCopy, aad, aadLen); + /* seal the payload with context */ + ret = wc_HpkeContextSealBase(ech->hpke, ech->hpkeContext, aadCopy, + aadLen, ech->innerClientHello, + ech->innerClientHelloLen - ech->hpke->Nt, ech->outerClientPayload); + } + if (info != NULL) + XFREE(info, ech->hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (aadCopy != NULL) + XFREE(aadCopy, ech->hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); if (receiverPubkey != NULL) wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, receiverPubkey, ech->hpke->heap); - return ret; } @@ -13891,7 +13875,7 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, #endif /* WOLFSSL_DTLS_CID */ #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) case TLSX_ECH: - length += ECH_GET_SIZE((WOLFSSL_ECH*)extension->data); + length += ECH_GET_SIZE((WOLFSSL_ECH*)extension->data, msgType); break; #endif default: @@ -14141,7 +14125,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 @@ -14999,7 +14983,9 @@ static int TLSX_GetSizeWithEch(WOLFSSL* ssl, byte* semaphore, byte msgType, echX = TLSX_Find(ssl->ctx->extensions, TLSX_ECH); /* if type is outer change sni to public name */ - if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->type == ECH_TYPE_OUTER) { + if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->type == ECH_TYPE_OUTER && + (ssl->options.echAccepted || + ((WOLFSSL_ECH*)echX->data)->innerCount == 0)) { if (ssl->extensions) { serverNameX = TLSX_Find(ssl->extensions, TLSX_SERVER_NAME); @@ -15206,7 +15192,9 @@ static int TLSX_WriteWithEch(WOLFSSL* ssl, byte* output, byte* semaphore, } /* if type is outer change sni to public name */ - if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->type == ECH_TYPE_OUTER) { + if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->type == ECH_TYPE_OUTER && + (ssl->options.echAccepted || + ((WOLFSSL_ECH*)echX->data)->innerCount == 0)) { if (ssl->extensions) { serverNameX = TLSX_Find(ssl->extensions, TLSX_SERVER_NAME); @@ -15266,31 +15254,36 @@ static int TLSX_WriteWithEch(WOLFSSL* ssl, byte* output, byte* semaphore, msgType, pOffset); } - if (echX != NULL) { - /* turn off and write it last */ - TURN_OFF(semaphore, TLSX_ToSemaphore(echX->type)); - } + /* only write if have a shot at acceptance */ + if (echX != NULL && + (ssl->options.echAccepted || + ((WOLFSSL_ECH*)echX->data)->innerCount == 0)) { + if (echX != NULL) { + /* turn off and write it last */ + TURN_OFF(semaphore, TLSX_ToSemaphore(echX->type)); + } - if (ret == 0 && ssl->extensions) { - ret = TLSX_Write(ssl->extensions, output + *pOffset, semaphore, - msgType, pOffset); - } + if (ret == 0 && ssl->extensions) { + ret = TLSX_Write(ssl->extensions, output + *pOffset, semaphore, + msgType, pOffset); + } - if (ret == 0 && ssl->ctx && ssl->ctx->extensions) { - ret = TLSX_Write(ssl->ctx->extensions, output + *pOffset, semaphore, - msgType, pOffset); - } + if (ret == 0 && ssl->ctx && ssl->ctx->extensions) { + ret = TLSX_Write(ssl->ctx->extensions, output + *pOffset, semaphore, + msgType, pOffset); + } - if (serverNameX != NULL) { - /* remove the public name SNI */ - TLSX_Remove(extensions, TLSX_SERVER_NAME, ssl->heap); + if (serverNameX != NULL) { + /* remove the public name SNI */ + TLSX_Remove(extensions, TLSX_SERVER_NAME, ssl->heap); - ret = TLSX_UseSNI(extensions, WOLFSSL_SNI_HOST_NAME, tmpServerName, - XSTRLEN(tmpServerName), ssl->heap); + ret = TLSX_UseSNI(extensions, WOLFSSL_SNI_HOST_NAME, tmpServerName, + XSTRLEN(tmpServerName), ssl->heap); - /* restore the inner server name */ - if (ret == WOLFSSL_SUCCESS) - ret = 0; + /* restore the inner server name */ + if (ret == WOLFSSL_SUCCESS) + ret = 0; + } } #ifdef WOLFSSL_SMALL_STACK @@ -15508,6 +15501,10 @@ int TLSX_GetResponseSize(WOLFSSL* ssl, byte msgType, word16* pLength) #ifdef WOLFSSL_SEND_HRR_COOKIE TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_COOKIE)); #endif +#ifdef HAVE_ECH + /* send the special confirmation */ + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_ECH)); +#endif break; #endif @@ -15651,6 +15648,10 @@ int TLSX_WriteResponse(WOLFSSL *ssl, byte* output, byte msgType, word16* pOffset TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_KEY_SHARE)); } #endif +#ifdef HAVE_ECH + /* send the special confirmation */ + TURN_OFF(semaphore, TLSX_ToSemaphore(TLSX_ECH)); +#endif /* Cookie is written below as last extension. */ break; #endif diff --git a/src/tls13.c b/src/tls13.c index 61d97e1e8..d0e3b4584 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -180,11 +180,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 @@ -4166,7 +4169,8 @@ int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config) /* returns status after we hash the ech inner */ static int EchHashHelloInner(WOLFSSL* ssl, WOLFSSL_ECH* ech) { - int ret; + int ret = 0; + word32 realSz; HS_Hashes* tmpHashes; #ifdef WOLFSSL_DTLS13 byte falseHeader[DTLS13_HANDSHAKE_HEADER_SZ]; @@ -4176,29 +4180,46 @@ static int EchHashHelloInner(WOLFSSL* ssl, WOLFSSL_ECH* ech) if (ssl == NULL || ech == NULL) return BAD_FUNC_ARG; - - /* switch hsHashes to the ech version */ - InitHandshakeHashesAndCopy(ssl, ssl->hsHashes, &ssl->hsHashesEch); - - /* swap hsHashes so the regular hash functions work */ + realSz = ech->innerClientHelloLen - ech->paddingLen - ech->hpke->Nt; tmpHashes = ssl->hsHashes; - ssl->hsHashes = ssl->hsHashesEch; - - /* do the handshake header then the body */ - AddTls13HandShakeHeader(falseHeader, - ech->innerClientHelloLen - ech->paddingLen - ech->hpke->Nt, 0, 0, - client_hello, ssl); - ret = HashRaw(ssl, falseHeader, HANDSHAKE_HEADER_SZ); - - /* hash the body */ + ssl->hsHashes = NULL; + /* init the ech hashes */ + InitHandshakeHashes(ssl); + ssl->hsHashesEch = ssl->hsHashes; if (ret == 0) { - ret = HashRaw(ssl, ech->innerClientHello, - (int)(ech->innerClientHelloLen - ech->paddingLen - ech->hpke->Nt)); + /* do the handshake header then the body */ + AddTls13HandShakeHeader(falseHeader, realSz, 0, 0, client_hello, ssl); + ret = HashRaw(ssl, falseHeader, HANDSHAKE_HEADER_SZ); + /* hash with inner */ + if (ret == 0) { + /* init hsHashesEchInner */ + if (ech->innerCount == 0) { + ssl->hsHashes = ssl->hsHashesEchInner; + InitHandshakeHashes(ssl); + ssl->hsHashesEchInner = ssl->hsHashes; + ech->innerCount = 1; + } + else { + /* switch back to hsHashes so we have hrr -> echInner2 */ + ssl->hsHashes = tmpHashes; + InitHandshakeHashesAndCopy(ssl, ssl->hsHashes, + &ssl->hsHashesEchInner); + } + ssl->hsHashes = ssl->hsHashesEchInner; + ret = HashRaw(ssl, falseHeader, HANDSHAKE_HEADER_SZ); + ssl->hsHashes = ssl->hsHashesEch; + } + } + /* hash the body */ + if (ret == 0) + ret = HashRaw(ssl, ech->innerClientHello, realSz); + /* hash with inner */ + if (ret == 0) { + ssl->hsHashes = ssl->hsHashesEchInner; + ret = HashRaw(ssl, ech->innerClientHello, realSz); } - /* swap hsHashes back */ ssl->hsHashes = tmpHashes; - return ret; } #endif @@ -4443,23 +4464,26 @@ int SendTls13ClientHello(WOLFSSL* ssl) if (args->ech == NULL) return WOLFSSL_FATAL_ERROR; - /* set the type to inner */ - args->ech->type = ECH_TYPE_INNER; - args->preXLength = (int)args->length; + /* only prepare if we have a chance at acceptance */ + if (ssl->options.echAccepted || args->ech->innerCount == 0) { + /* set the type to inner */ + args->ech->type = ECH_TYPE_INNER; + args->preXLength = (int)args->length; - /* get size for inner */ - ret = TLSX_GetRequestSize(ssl, client_hello, &args->length); - if (ret != 0) - return ret; + /* get size for inner */ + ret = TLSX_GetRequestSize(ssl, client_hello, &args->length); + if (ret != 0) + return ret; - /* set the type to outer */ - args->ech->type = 0; - /* set innerClientHelloLen to ClientHelloInner + padding + tag */ - args->ech->paddingLen = 31 - ((args->length - 1) % 32); - args->ech->innerClientHelloLen = (word16)(args->length + - args->ech->paddingLen + args->ech->hpke->Nt); - /* set the length back to before we computed ClientHelloInner size */ - args->length = (word32)args->preXLength; + /* set the type to outer */ + args->ech->type = 0; + /* set innerClientHelloLen to ClientHelloInner + padding + tag */ + args->ech->paddingLen = 31 - ((args->length - 1) % 32); + args->ech->innerClientHelloLen = (word16)(args->length + + args->ech->paddingLen + args->ech->hpke->Nt); + /* set the length back to before we computed ClientHelloInner size */ + args->length = (word32)args->preXLength; + } } #endif @@ -4585,42 +4609,41 @@ int SendTls13ClientHello(WOLFSSL* ssl) #if defined(HAVE_ECH) /* write inner then outer */ - if (ssl->options.useEch == 1 && !ssl->options.disableECH) { + if (ssl->options.useEch == 1 && !ssl->options.disableECH && + (ssl->options.echAccepted || args->ech->innerCount == 0)) { /* set the type to inner */ args->ech->type = ECH_TYPE_INNER; - + /* innerClientHello may already exist from hrr, free if it does */ + if (args->ech->innerClientHello != NULL) { + XFREE(args->ech->innerClientHello, ssl->heap, + DYNAMIC_TYPE_TMP_BUFFER); + } /* allocate the inner */ args->ech->innerClientHello = (byte*)XMALLOC(args->ech->innerClientHelloLen - args->ech->hpke->Nt, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); if (args->ech->innerClientHello == NULL) return MEMORY_E; - /* set the padding bytes to 0 */ XMEMSET(args->ech->innerClientHello + args->ech->innerClientHelloLen - args->ech->hpke->Nt - args->ech->paddingLen, 0, args->ech->paddingLen); - /* copy the client hello to the ech innerClientHello, exclude record */ /* and handshake headers */ XMEMCPY(args->ech->innerClientHello, args->output + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ, args->idx - (RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ)); - /* copy the client random to inner */ XMEMCPY(ssl->arrays->clientRandomInner, ssl->arrays->clientRandom, RAN_LEN); - /* change the outer client random */ ret = wc_RNG_GenerateBlock(ssl->rng, args->output + args->clientRandomOffset, RAN_LEN); if (ret != 0) return ret; - /* copy the new client random */ XMEMCPY(ssl->arrays->clientRandom, args->output + args->clientRandomOffset, RAN_LEN); - /* write the extensions for inner */ args->length = 0; ret = TLSX_WriteRequest(ssl, args->ech->innerClientHello + args->idx - @@ -4628,7 +4651,6 @@ int SendTls13ClientHello(WOLFSSL* ssl) &args->length); if (ret != 0) return ret; - /* set the type to outer */ args->ech->type = 0; } @@ -4645,7 +4667,8 @@ int SendTls13ClientHello(WOLFSSL* ssl) #if defined(HAVE_ECH) /* encrypt and pack the ech innerClientHello */ - if (ssl->options.useEch == 1 && !ssl->options.disableECH) { + if (ssl->options.useEch == 1 && !ssl->options.disableECH && + (ssl->options.echAccepted || args->ech->innerCount == 0)) { ret = TLSX_FinalizeEch(args->ech, args->output + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ, (word32)(args->sendSz - (RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ))); @@ -4675,7 +4698,8 @@ int SendTls13ClientHello(WOLFSSL* ssl) { #if defined(HAVE_ECH) /* compute the inner hash */ - if (ssl->options.useEch == 1 && !ssl->options.disableECH) + if (ssl->options.useEch == 1 && !ssl->options.disableECH && + (ssl->options.echAccepted || args->ech->innerCount == 0)) ret = EchHashHelloInner(ssl, args->ech); #endif /* compute the outer hash */ @@ -4768,15 +4792,15 @@ static int Dtls13ClientDoDowngrade(WOLFSSL* ssl) #endif /* WOLFSSL_DTLS13 && !WOLFSSL_NO_CLIENT*/ #if defined(HAVE_ECH) -/* check if the server accepted ech or not */ -static int EchCheckAcceptance(WOLFSSL* ssl, const byte* input, - int serverRandomOffset, int helloSz) +/* check if the server accepted ech or not, must be run after an hsHashes + * restart */ +static int EchCheckAcceptance(WOLFSSL* ssl, byte* label, word16 labelSz, + const byte* input, int acceptOffset, int helloSz) { int ret = 0; int digestType = 0; int digestSize = 0; HS_Hashes* tmpHashes; - HS_Hashes* acceptHashes; byte zeros[WC_MAX_DIGEST_SIZE]; byte transcriptEchConf[WC_MAX_DIGEST_SIZE]; byte expandLabelPrk[WC_MAX_DIGEST_SIZE]; @@ -4785,22 +4809,20 @@ static int EchCheckAcceptance(WOLFSSL* ssl, const byte* input, XMEMSET(transcriptEchConf, 0, sizeof(transcriptEchConf)); XMEMSET(expandLabelPrk, 0, sizeof(expandLabelPrk)); XMEMSET(acceptConfirmation, 0, sizeof(acceptConfirmation)); - /* copy ech hashes to accept */ - ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashesEch, &acceptHashes); - /* swap hsHashes to acceptHashes */ + /* store so we can restore regardless of the outcome */ tmpHashes = ssl->hsHashes; - ssl->hsHashes = acceptHashes; + /* swap hsHashes to hsHashesEch */ + ssl->hsHashes = ssl->hsHashesEch; /* 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) @@ -4856,97 +4878,83 @@ static int EchCheckAcceptance(WOLFSSL* ssl, const byte* input, /* tls expand with the confirmation label */ if (ret == 0) { PRIVATE_KEY_UNLOCK(); - ret = Tls13HKDFExpandKeyLabel(ssl, - acceptConfirmation, ECH_ACCEPT_CONFIRMATION_SZ, - expandLabelPrk, (word32)digestSize, - tls13ProtocolLabel, TLS13_PROTOCOL_LABEL_SZ, - echAcceptConfirmationLabel, ECH_ACCEPT_CONFIRMATION_LABEL_SZ, - transcriptEchConf, (word32)digestSize, digestType, WOLFSSL_SERVER_END); + ret = Tls13HKDFExpandKeyLabel(ssl, acceptConfirmation, + 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 (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 */ + /* set echAccepted to 1 */ + ssl->options.echAccepted = 1; + /* free hsHashes and go with inner */ ssl->hsHashes = tmpHashes; - /* set the final hsHashes to the ech hashes */ - tmpHashes = ssl->hsHashesEch; + FreeHandshakeHashes(ssl); + ssl->hsHashes = ssl->hsHashesEch; + tmpHashes = ssl->hsHashesEchInner; + ssl->hsHashesEchInner = NULL; } /* ech rejected */ else { - /* switch to hsHashesEch to free */ - ssl->hsHashes = ssl->hsHashesEch; + /* set echAccepted to 0, needed in case HRR */ + ssl->options.echAccepted = 0; + /* free inner since we're continuing with outer */ + ssl->hsHashes = ssl->hsHashesEchInner; + FreeHandshakeHashes(ssl); + ssl->hsHashesEchInner = NULL; } - /* free hsHashes */ - FreeHandshakeHashes(ssl); - /* set hsHashesEch to NULL to avoid double free */ - ssl->hsHashesEch = NULL; /* continue with outer if we failed to verify ech was accepted */ ret = 0; } - /* switch to acceptHashes */ - ssl->hsHashes = acceptHashes; - /* free acceptHashes */ FreeHandshakeHashes(ssl); - /* swap to tmp, will ech if accepted, hsHashes if rejected */ + /* set hsHashesEch to NULL to avoid double free */ + ssl->hsHashesEch = NULL; + /* swap to tmp, will be inner if accepted, hsHashes if rejected */ ssl->hsHashes = tmpHashes; 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, byte msgType) { int ret = 0; int digestType = 0; int digestSize = 0; HS_Hashes* tmpHashes = NULL; - HS_Hashes* acceptHashes = 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)); - - /* copy ech hashes to accept */ - ret = InitHandshakeHashesAndCopy(ssl, ssl->hsHashes, &acceptHashes); - - /* swap hsHashes to acceptHashes */ + /* store so we can restore regardless of the outcome */ 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); - + 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); - + 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: @@ -4976,7 +4984,7 @@ static int EchWriteAcceptance(WOLFSSL* ssl, byte* output, ret = WOLFSSL_FATAL_ERROR; break; } - + } /* extract clientRandom with a key of all zeros */ if (ret == 0) { PRIVATE_KEY_UNLOCK(); @@ -4991,29 +4999,23 @@ static int EchWriteAcceptance(WOLFSSL* ssl, byte* output, #endif PRIVATE_KEY_LOCK(); } - /* tls expand with the confirmation label */ if (ret == 0) { PRIVATE_KEY_UNLOCK(); - ret = Tls13HKDFExpandKeyLabel(ssl, - output + serverRandomOffset + RAN_LEN - ECH_ACCEPT_CONFIRMATION_SZ, - ECH_ACCEPT_CONFIRMATION_SZ, - expandLabelPrk, (word32)digestSize, - tls13ProtocolLabel, TLS13_PROTOCOL_LABEL_SZ, - echAcceptConfirmationLabel, ECH_ACCEPT_CONFIRMATION_LABEL_SZ, - transcriptEchConf, (word32)digestSize, digestType, WOLFSSL_SERVER_END); + 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(); } - - if (ret == 0) - XMEMCPY(ssl->arrays->serverRandom, output + serverRandomOffset, - RAN_LEN); - - /* free acceptHashes */ + /* 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; - return ret; } #endif @@ -5039,7 +5041,10 @@ typedef struct Dsh13Args { byte sessIdSz; byte extMsgType; #if defined(HAVE_ECH) - int serverRandomOffset; + TLSX* echX; + byte* acceptLabel; + word32 acceptOffset; + word16 acceptLabelSz; #endif } Dsh13Args; @@ -5196,7 +5201,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 = (int)args->idx; + /* last 8 bytes of server random */ + args->acceptOffset = args->idx + RAN_LEN - ECH_ACCEPT_CONFIRMATION_SZ; #endif args->idx += RAN_LEN; @@ -5492,15 +5498,6 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, if (ret != 0) return ret; -#if defined(HAVE_ECH) - /* check for acceptConfirmation and HashInput with 8 0 bytes */ - if (ssl->options.useEch == 1 && !ssl->options.disableECH) { - ret = EchCheckAcceptance(ssl, input, args->serverRandomOffset, (int)helloSz); - if (ret != 0) - return ret; - } -#endif - #ifdef HAVE_NULL_CIPHER if (ssl->options.cipherSuite0 == ECC_BYTE && (ssl->options.cipherSuite == TLS_SHA256_SHA256 || @@ -5538,6 +5535,36 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, return MATCH_SUITE_ERROR; } +#if defined(HAVE_ECH) + /* check for acceptConfirmation, must be done after hashes restart */ + if (ssl->options.useEch == 1) { + args->echX = TLSX_Find(ssl->extensions, TLSX_ECH); + /* account for hrr extension instead of server random */ + if (args->extMsgType == hello_retry_request) { + args->acceptOffset = + (word32)(((WOLFSSL_ECH*)args->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 */ + if (ret == 0) { + 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 /* HAVE_ECH */ + if (*extMsgType == server_hello) { #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) PreSharedKey* psk = NULL; @@ -6706,6 +6733,7 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #endif #if defined(HAVE_ECH) TLSX* echX = NULL; + HS_Hashes* tmpHashes; #endif WOLFSSL_START(WC_FUNC_CLIENT_HELLO_DO); @@ -7029,6 +7057,22 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } #endif +#if defined(HAVE_ECH) + /* hash clientHelloInner to hsHashesEch independently since it can't include + * the HRR */ + if (ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { + tmpHashes = ssl->hsHashes; + ssl->hsHashes = NULL; + ret = InitHandshakeHashes(ssl); + if (ret != 0) + goto exit_dch; + if ((ret = HashInput(ssl, input + args->begin, (int)helloSz)) != 0) + goto exit_dch; + ssl->hsHashesEch = ssl->hsHashes; + ssl->hsHashes = tmpHashes; + } +#endif + #if (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) && \ defined(HAVE_TLS_EXTENSIONS) ret = CheckPreSharedKeys(ssl, input + args->begin, helloSz, ssl->clSuites, @@ -7309,7 +7353,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); @@ -7368,7 +7414,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. */ @@ -7432,18 +7479,37 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType) #if defined(HAVE_ECH) if (ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { echX = TLSX_Find(ssl->extensions, TLSX_ECH); - if (echX == NULL) return WOLFSSL_FATAL_ERROR; - + /* use hrr offset */ + if (extMsgType == hello_retry_request) { + acceptOffset = + (word32)(((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, - (int)serverRandomOffset - 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 (ret == 0) { + ret = EchWriteAcceptance(ssl, acceptLabel, + acceptLabelSz, output + RECORD_HEADER_SZ, + acceptOffset - RECORD_HEADER_SZ, + sendSz - RECORD_HEADER_SZ, extMsgType); + } + if (extMsgType == hello_retry_request) { + /* reset the ech state for round 2 */ + ((WOLFSSL_ECH*)echX->data)->state = ECH_WRITE_NONE; + } + else { + 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 @@ -12684,16 +12750,15 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE) { - /* reset the inOutIdx to the outer start */ *inOutIdx = echInOutIdx; - /* call again with the inner hello */ - ret = DoTls13ClientHello(ssl, - ((WOLFSSL_ECH*)echX->data)->innerClientHello, - &echInOutIdx, - ((WOLFSSL_ECH*)echX->data)->innerClientHelloLen); - + if (ret == 0) { + ret = DoTls13ClientHello(ssl, + ((WOLFSSL_ECH*)echX->data)->innerClientHello, + &echInOutIdx, + ((WOLFSSL_ECH*)echX->data)->innerClientHelloLen); + } /* if the inner ech parsed successfully we have successfully * handled the hello and can skip the whole message */ if (ret == 0) diff --git a/tests/api.c b/tests/api.c index 7e4cec010..1383537be 100644 --- a/tests/api.c +++ b/tests/api.c @@ -50140,7 +50140,7 @@ static int test_wolfSSL_Tls13_ECH_params(void) return EXPECT_RESULT(); } -static int test_wolfSSL_Tls13_ECH(void) +static int test_wolfSSL_Tls13_ECH_ex(int hrr) { EXPECT_DECLS; tcp_ready ready; @@ -50211,8 +50211,14 @@ static int test_wolfSSL_Tls13_ECH(void) ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, privateName, privateNameLen)); + /* force hello retry request */ + if (hrr) + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_NoKeyShares(ssl)); + + /* connect like normal */ ExpectIntEQ(wolfSSL_set_fd(ssl, sockfd), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_connect(ssl), WOLFSSL_SUCCESS); + ExpectIntEQ(ssl->options.echAccepted, 1); ExpectIntEQ(wolfSSL_write(ssl, privateName, privateNameLen), privateNameLen); ExpectIntGT((replyLen = wolfSSL_read(ssl, reply, sizeof(reply))), 0); @@ -50231,6 +50237,16 @@ static int test_wolfSSL_Tls13_ECH(void) return EXPECT_RESULT(); } + +static int test_wolfSSL_Tls13_ECH(void) +{ + return test_wolfSSL_Tls13_ECH_ex(0); +} + +static int test_wolfSSL_Tls13_ECH_HRR(void) +{ + return test_wolfSSL_Tls13_ECH_ex(1); +} #endif /* HAVE_ECH && WOLFSSL_TLS13 */ #if defined(HAVE_IO_TESTS_DEPENDENCIES) && \ @@ -90415,6 +90431,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_Tls13_ECH_params), /* Uses Assert in handshake callback. */ TEST_DECL(test_wolfSSL_Tls13_ECH), + TEST_DECL(test_wolfSSL_Tls13_ECH_HRR), #endif TEST_DECL(test_wolfSSL_X509_TLS_version_test_1), diff --git a/wolfcrypt/src/hpke.c b/wolfcrypt/src/hpke.c index f42e3ab21..cef2ab83d 100644 --- a/wolfcrypt/src/hpke.c +++ b/wolfcrypt/src/hpke.c @@ -875,49 +875,63 @@ static int wc_HpkeSetupBaseSender(Hpke* hpke, HpkeBaseContext* context, return ret; } +/* give SetupBaseSender a more intuitive and wolfCrypt friendly name */ +int wc_HpkeInitSealContext(Hpke* hpke, HpkeBaseContext* context, + void* ephemeralKey, void* receiverKey, byte* info, word32 infoSz) +{ + if (hpke == NULL || context == NULL || ephemeralKey == NULL || + receiverKey == NULL || (info == NULL && infoSz > 0)) { + return BAD_FUNC_ARG; + } + + /* zero out all fields */ + XMEMSET(context, 0, sizeof(HpkeBaseContext)); + + return wc_HpkeSetupBaseSender(hpke, context, ephemeralKey, receiverKey, + info, infoSz); +} + /* encrypt a message using an hpke base context, return 0 or error */ -static int wc_HpkeContextSealBase(Hpke* hpke, HpkeBaseContext* context, +int wc_HpkeContextSealBase(Hpke* hpke, HpkeBaseContext* context, byte* aad, word32 aadSz, byte* plaintext, word32 ptSz, byte* out) { int ret; byte nonce[HPKE_Nn_MAX]; #ifndef WOLFSSL_SMALL_STACK - Aes aes_key[1]; + Aes aes[1]; #else - Aes* aes_key; + Aes* aes; #endif - - if (hpke == NULL) { + if (hpke == NULL || context == NULL || (aad == NULL && aadSz > 0) || + plaintext == NULL || out == NULL) { return BAD_FUNC_ARG; } - #ifdef WOLFSSL_SMALL_STACK - aes_key = (Aes*)XMALLOC(sizeof(Aes), hpke->heap, DYNAMIC_TYPE_AES); - if (aes_key == NULL) { + aes = (Aes*)XMALLOC(sizeof(Aes), hpke->heap, DYNAMIC_TYPE_AES); + if (aes == NULL) { return MEMORY_E; } #endif - - ret = wc_AesInit(aes_key, hpke->heap, INVALID_DEVID); + ret = wc_AesInit(aes, hpke->heap, INVALID_DEVID); if (ret == 0) { + /* compute nonce */ ret = wc_HpkeContextComputeNonce(hpke, context, nonce); if (ret == 0) { - ret = wc_AesGcmSetKey(aes_key, context->key, hpke->Nk); + ret = wc_AesGcmSetKey(aes, context->key, hpke->Nk); } if (ret == 0) { - ret = wc_AesGcmEncrypt(aes_key, out, plaintext, ptSz, nonce, + ret = wc_AesGcmEncrypt(aes, out, plaintext, ptSz, nonce, hpke->Nn, out + ptSz, hpke->Nt, aad, aadSz); } + /* increment sequence for non one shot */ if (ret == 0) { context->seq++; } - wc_AesFree(aes_key); + wc_AesFree(aes); } - #ifdef WOLFSSL_SMALL_STACK - XFREE(aes_key, hpke->heap, DYNAMIC_TYPE_AES); + XFREE(aes, hpke->heap, DYNAMIC_TYPE_AES); #endif - return ret; } @@ -1113,49 +1127,60 @@ static int wc_HpkeSetupBaseReceiver(Hpke* hpke, HpkeBaseContext* context, return ret; } +/* give SetupBaseReceiver a more intuitive and wolfCrypt friendly name */ +int wc_HpkeInitOpenContext(Hpke* hpke, HpkeBaseContext* context, + void* receiverKey, const byte* pubKey, word16 pubKeySz, byte* info, + word32 infoSz) +{ + if (hpke == NULL || context == NULL || receiverKey == NULL || pubKey == NULL + || (info == NULL && infoSz > 0)) { + return BAD_FUNC_ARG; + } + + return wc_HpkeSetupBaseReceiver(hpke, context, receiverKey, pubKey, + pubKeySz, info, infoSz); +} + /* decrypt a message using a setup hpke context, return 0 or error */ -static int wc_HpkeContextOpenBase(Hpke* hpke, HpkeBaseContext* context, - byte* aad, word32 aadSz, byte* ciphertext, word32 ctSz, byte* out) +int wc_HpkeContextOpenBase(Hpke* hpke, HpkeBaseContext* context, byte* aad, + word32 aadSz, byte* ciphertext, word32 ctSz, byte* out) { int ret; byte nonce[HPKE_Nn_MAX]; #ifndef WOLFSSL_SMALL_STACK - Aes aes_key[1]; + Aes aes[1]; #else - Aes* aes_key; + Aes* aes; #endif - if (hpke == NULL) { return BAD_FUNC_ARG; } - XMEMSET(nonce, 0, sizeof(nonce)); #ifdef WOLFSSL_SMALL_STACK - aes_key = (Aes*)XMALLOC(sizeof(Aes), hpke->heap, DYNAMIC_TYPE_AES); - if (aes_key == NULL) { + aes = (Aes*)XMALLOC(sizeof(Aes), hpke->heap, DYNAMIC_TYPE_AES); + if (aes == NULL) { return MEMORY_E; } #endif - + /* compute nonce */ ret = wc_HpkeContextComputeNonce(hpke, context, nonce); if (ret == 0) - ret = wc_AesInit(aes_key, hpke->heap, INVALID_DEVID); + ret = wc_AesInit(aes, hpke->heap, INVALID_DEVID); if (ret == 0) { - ret = wc_AesGcmSetKey(aes_key, context->key, hpke->Nk); + ret = wc_AesGcmSetKey(aes, context->key, hpke->Nk); if (ret == 0) { - ret = wc_AesGcmDecrypt(aes_key, out, ciphertext, ctSz, nonce, + ret = wc_AesGcmDecrypt(aes, out, ciphertext, ctSz, nonce, hpke->Nn, ciphertext + ctSz, hpke->Nt, aad, aadSz); } + /* increment sequence for non one shot */ if (ret == 0) { context->seq++; } - wc_AesFree(aes_key); + wc_AesFree(aes); } - #ifdef WOLFSSL_SMALL_STACK - XFREE(aes_key, hpke->heap, DYNAMIC_TYPE_AES); + XFREE(aes, hpke->heap, DYNAMIC_TYPE_AES); #endif - return ret; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 778eeca27..ed52d11b8 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -28671,6 +28671,106 @@ static wc_test_ret_t hpke_test_single(Hpke* hpke) return ret; } +static wc_test_ret_t hpke_test_multi(Hpke* hpke) +{ + wc_test_ret_t ret = 0; + int rngRet = 0; + WC_RNG rng[1]; + const char* start_text = "this is a test"; + const char* info_text = "info"; + const char* aad_text = "aad"; + byte ciphertexts[2][MAX_HPKE_LABEL_SZ]; + byte plaintext[MAX_HPKE_LABEL_SZ]; + void* receiverKey = NULL; + void* ephemeralKey = NULL; +#ifdef WOLFSSL_SMALL_STACK + HpkeBaseContext* context = NULL; + byte *pubKey = NULL; /* public key */ + word16 pubKeySz = (word16)HPKE_Npk_MAX; +#else + HpkeBaseContext context[1]; + byte pubKey[HPKE_Npk_MAX]; /* public key */ + word16 pubKeySz = (word16)sizeof(pubKey); +#endif + rngRet = ret = wc_InitRng(rng); + if (ret != 0) + return ret; +#ifdef WOLFSSL_SMALL_STACK + pubKey = (byte *)XMALLOC(pubKeySz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (pubKey == NULL) + ret = MEMORY_E; + if (ret == 0) { + context = (HpkeBaseContext*)XMALLOC(sizeof(HpkeBaseContext), HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + } + if (context == NULL) + ret = MEMORY_E; +#endif + /* generate the keys */ + if (ret == 0) + ret = wc_HpkeGenerateKeyPair(hpke, &ephemeralKey, rng); + if (ret == 0) + ret = wc_HpkeGenerateKeyPair(hpke, &receiverKey, rng); + /* setup seal context */ + if (ret == 0) { + ret = wc_HpkeInitSealContext(hpke, context, ephemeralKey, receiverKey, + (byte*)info_text, (word32)XSTRLEN(info_text)); + } + /* seal message 0 */ + if (ret == 0) { + ret = wc_HpkeContextSealBase(hpke, context, + (byte*)aad_text, (word32)XSTRLEN(aad_text), + (byte*)start_text, (word32)XSTRLEN(start_text), + ciphertexts[context->seq]); + } + /* seal message 1 */ + if (ret == 0) { + ret = wc_HpkeContextSealBase(hpke, context, + (byte*)aad_text, (word32)XSTRLEN(aad_text), + (byte*)start_text, (word32)XSTRLEN(start_text), + ciphertexts[context->seq]); + } + /* export ephemeral key */ + if (ret == 0) + ret = wc_HpkeSerializePublicKey(hpke, ephemeralKey, pubKey, &pubKeySz); + /* setup open context */ + if (ret == 0) { + ret = wc_HpkeInitOpenContext(hpke, context, receiverKey, pubKey, + pubKeySz, (byte*)info_text, (word32)XSTRLEN(info_text)); + } + /* open message 0 */ + if (ret == 0) { + ret = wc_HpkeContextOpenBase(hpke, context, (byte*)aad_text, + (word32)XSTRLEN(aad_text), ciphertexts[context->seq], + (word32)XSTRLEN(start_text), plaintext); + } + /* check message 0 */ + if (ret == 0) + ret = XMEMCMP(plaintext, start_text, XSTRLEN(start_text)); + /* open message 1 */ + if (ret == 0) { + ret = wc_HpkeContextOpenBase(hpke, context, (byte*)aad_text, + (word32)XSTRLEN(aad_text), ciphertexts[context->seq], + (word32)XSTRLEN(start_text), plaintext); + } + /* check message 1 */ + if (ret == 0) + ret = XMEMCMP(plaintext, start_text, XSTRLEN(start_text)); + if (ephemeralKey != NULL) + wc_HpkeFreeKey(hpke, hpke->kem, ephemeralKey, hpke->heap); + if (receiverKey != NULL) + wc_HpkeFreeKey(hpke, hpke->kem, receiverKey, hpke->heap); +#ifdef WOLFSSL_SMALL_STACK + if (pubKey != NULL) + XFREE(pubKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (context != NULL) + XFREE(context, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); +#endif + if (rngRet == 0) + wc_FreeRng(rng); + return ret; +} + WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) { wc_test_ret_t ret = 0; @@ -28682,14 +28782,15 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) /* p256 */ ret = wc_HpkeInit(hpke, DHKEM_P256_HKDF_SHA256, HKDF_SHA256, HPKE_AES_128_GCM, NULL); - if (ret != 0) return WC_TEST_RET_ENC_EC(ret); - ret = hpke_test_single(hpke); - if (ret != 0) return ret; + ret = hpke_test_multi(hpke); + if (ret != 0) + return ret; + #endif #if defined(WOLFSSL_SHA384) && \ @@ -28697,12 +28798,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) /* p384 */ ret = wc_HpkeInit(hpke, DHKEM_P384_HKDF_SHA384, HKDF_SHA384, HPKE_AES_128_GCM, NULL); - if (ret != 0) return WC_TEST_RET_ENC_EC(ret); - ret = hpke_test_single(hpke); - + if (ret != 0) + return ret; + ret = hpke_test_multi(hpke); if (ret != 0) return ret; #endif @@ -28712,12 +28813,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) /* p521 */ ret = wc_HpkeInit(hpke, DHKEM_P521_HKDF_SHA512, HKDF_SHA512, HPKE_AES_128_GCM, NULL); - if (ret != 0) return WC_TEST_RET_ENC_EC(ret); - ret = hpke_test_single(hpke); - + if (ret != 0) + return ret; + ret = hpke_test_multi(hpke); if (ret != 0) return ret; #endif @@ -28726,13 +28827,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) #if defined(HAVE_CURVE25519) /* test with curve25519 and aes256 */ ret = wc_HpkeInit(hpke, DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, - HPKE_AES_256_GCM, NULL); - + HPKE_AES_256_GCM, NULL); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); - ret = hpke_test_single(hpke); - + if (ret != 0) + return ret; + ret = hpke_test_multi(hpke); if (ret != 0) return ret; #endif diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 09f218d33..e24077365 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3117,6 +3117,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, @@ -3151,11 +3152,13 @@ typedef struct WOLFSSL_EchConfig { typedef struct WOLFSSL_ECH { Hpke* hpke; + HpkeBaseContext* hpkeContext; const byte* aad; void* ephemeralKey; WOLFSSL_EchConfig* echConfig; byte* innerClientHello; byte* outerClientPayload; + byte* confBuf; EchCipherSuite cipherSuite; word16 aadLen; word16 paddingLen; @@ -3166,6 +3169,7 @@ typedef struct WOLFSSL_ECH { byte type; byte configId; byte enc[HPKE_Npk_MAX]; + byte innerCount; } WOLFSSL_ECH; WOLFSSL_LOCAL int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config); @@ -5092,7 +5096,8 @@ struct Options { word16 useDtlsCID:1; #endif /* WOLFSSL_DTLS_CID */ #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) - word16 useEch:1; /* Do we have a valid config */ + word16 useEch:1; + word16 echAccepted:1; byte disableECH:1; /* Did the user disable ech */ #endif #ifdef WOLFSSL_SEND_HRR_COOKIE @@ -5819,6 +5824,7 @@ struct WOLFSSL { HS_Hashes* hsHashes; #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) HS_Hashes* hsHashesEch; + HS_Hashes* hsHashesEchInner; #endif void* IOCB_ReadCtx; void* IOCB_WriteCtx; diff --git a/wolfssl/wolfcrypt/hpke.h b/wolfssl/wolfcrypt/hpke.h index c200f63cf..cacfca6ce 100644 --- a/wolfssl/wolfcrypt/hpke.h +++ b/wolfssl/wolfcrypt/hpke.h @@ -120,9 +120,18 @@ WOLFSSL_API int wc_HpkeDeserializePublicKey(Hpke* hpke, void** key, const byte* in, word16 inSz); WOLFSSL_API void wc_HpkeFreeKey(Hpke* hpke, word16 kem, void* keypair, void* heap); +WOLFSSL_API int wc_HpkeInitSealContext(Hpke* hpke, HpkeBaseContext* context, + void* ephemeralKey, void* receiverKey, byte* info, word32 infoSz); +WOLFSSL_API int wc_HpkeContextSealBase(Hpke* hpke, HpkeBaseContext* context, + byte* aad, word32 aadSz, byte* plaintext, word32 ptSz, byte* out); WOLFSSL_API int wc_HpkeSealBase(Hpke* hpke, void* ephemeralKey, void* receiverKey, byte* info, word32 infoSz, byte* aad, word32 aadSz, byte* plaintext, word32 ptSz, byte* ciphertext); +WOLFSSL_API int wc_HpkeInitOpenContext(Hpke* hpke, HpkeBaseContext* context, + void* receiverKey, const byte* pubKey, word16 pubKeySz, byte* info, + word32 infoSz); +WOLFSSL_API int wc_HpkeContextOpenBase(Hpke* hpke, HpkeBaseContext* context, + byte* aad, word32 aadSz, byte* ciphertext, word32 ctSz, byte* out); WOLFSSL_API int wc_HpkeOpenBase(Hpke* hpke, void* receiverKey, const byte* pubKey, word16 pubKeySz, byte* info, word32 infoSz, byte* aad, word32 aadSz, byte* ciphertext, word32 ctSz, byte* plaintext);