Files
wolfssl/src/ssl_ech.c
T
Tobias Frauenschläger 428bfe9f58 Restore the caller's key RNG after an HPKE operation
The encap and decap paths create a temporary RNG, install it into a key that
belongs to the caller so the shared secret computation can blind, and then
free it without taking it back out. Both wc_ecc_set_rng and
wc_curve25519_set_rng only record the pointer, and nothing else ever writes
that field, so the caller's key was left pointing at freed memory. Encap does
this to the ephemeral key and decap to the receiver key, including the
curve25519 branch. Repeated HPKE calls hide it because each one installs a
fresh RNG first, but any other use of the key that consults it, such as an
ECDH or a signature under timing resistance, reads the freed object. ECH
holds exactly such a long lived key.

Save whatever RNG the key already had and put it back before freeing the
temporary one, so the key is handed back to the caller unchanged. Clearing
the field instead would silently drop an RNG the caller had installed for
blinding of their own.

Extend the HPKE round trip test to give both keys an RNG of its own and to
require them to still have it once the seal and open have finished.

Fixes F-7083.
2026-08-01 12:04:56 +02:00

922 lines
26 KiB
C

/* ssl_ech.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
#if !defined(WOLFSSL_SSL_ECH_INCLUDED)
#ifndef WOLFSSL_IGNORE_FILE_WARN
#warning ssl_ech.c does not need to be compiled separately from ssl.c
#endif
#else
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
/* create the hpke key and ech config to send to clients */
int wolfSSL_CTX_GenerateEchConfig(WOLFSSL_CTX* ctx, const char* publicName,
word16 kemId, word16 kdfId, word16 aeadId)
{
return wolfSSL_CTX_GenerateEchConfigEx(ctx, publicName, kemId, kdfId,
aeadId, 0);
}
/* create the hpke key and ech config to send to clients
* maximum_name_length may also be set for a more stable padding length */
int wolfSSL_CTX_GenerateEchConfigEx(WOLFSSL_CTX* ctx, const char* publicName,
word16 kemId, word16 kdfId, word16 aeadId, byte maxNameLen)
{
int ret = 0;
WOLFSSL_EchConfig* newConfig;
word16 encLen = HPKE_Npk_MAX;
#ifdef WOLFSSL_SMALL_STACK
Hpke* hpke = NULL;
WC_RNG* rng;
#else
Hpke hpke[1];
WC_RNG rng[1];
#endif
if (ctx == NULL || publicName == NULL)
return BAD_FUNC_ARG;
/* ECH spec limits public_name to 255 bytes (1-byte length prefix) */
if (XSTRLEN(publicName) > 255)
return BAD_FUNC_ARG;
WC_ALLOC_VAR_EX(rng, WC_RNG, 1, ctx->heap, DYNAMIC_TYPE_RNG,
return MEMORY_E);
ret = wc_InitRng(rng);
if (ret != 0) {
WC_FREE_VAR_EX(rng, ctx->heap, DYNAMIC_TYPE_RNG);
return ret;
}
newConfig = (WOLFSSL_EchConfig*)XMALLOC(sizeof(WOLFSSL_EchConfig),
ctx->heap, DYNAMIC_TYPE_TMP_BUFFER);
if (newConfig == NULL)
ret = MEMORY_E;
else
XMEMSET(newConfig, 0, sizeof(WOLFSSL_EchConfig));
/* set random configId */
if (ret == 0)
ret = wc_RNG_GenerateByte(rng, &newConfig->configId);
/* if 0 is selected for algorithms use default, may change with draft */
if (kemId == 0)
kemId = DHKEM_X25519_HKDF_SHA256;
if (kdfId == 0)
kdfId = HKDF_SHA256;
if (aeadId == 0)
aeadId = HPKE_AES_128_GCM;
if (ret == 0) {
/* set the kem id */
newConfig->kemId = kemId;
/* set the cipher suite, only 1 for now */
newConfig->numCipherSuites = 1;
newConfig->cipherSuites =
(EchCipherSuite*)XMALLOC(sizeof(EchCipherSuite), ctx->heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (newConfig->cipherSuites == NULL) {
ret = MEMORY_E;
}
else {
newConfig->cipherSuites[0].kdfId = kdfId;
newConfig->cipherSuites[0].aeadId = aeadId;
}
}
#ifdef WOLFSSL_SMALL_STACK
if (ret == 0) {
hpke = (Hpke*)XMALLOC(sizeof(Hpke), ctx->heap, DYNAMIC_TYPE_TMP_BUFFER);
if (hpke == NULL)
ret = MEMORY_E;
}
#endif
if (ret == 0)
ret = wc_HpkeInit(hpke, kemId, kdfId, aeadId, ctx->heap);
/* generate the receiver private key */
if (ret == 0)
ret = wc_HpkeGenerateKeyPair(hpke, &newConfig->receiverPrivkey, rng);
/* The key outlives this RNG, and key generation may have stored a
* pointer to it in the key, so take it back out before freeing it. */
#if defined(HAVE_CURVE25519) && defined(WOLFSSL_CURVE25519_BLINDING)
if ((ret == 0) && (kemId == DHKEM_X25519_HKDF_SHA256)) {
(void)wc_curve25519_set_rng(
(curve25519_key*)newConfig->receiverPrivkey, NULL);
}
#endif
/* done with RNG */
wc_FreeRng(rng);
/* serialize the receiver key */
if (ret == 0)
ret = wc_HpkeSerializePublicKey(hpke, newConfig->receiverPrivkey,
newConfig->receiverPubkey, &encLen);
if (ret == 0) {
newConfig->publicName = (char*)XMALLOC(XSTRLEN(publicName) + 1,
ctx->heap, DYNAMIC_TYPE_TMP_BUFFER);
if (newConfig->publicName == NULL) {
ret = MEMORY_E;
}
else {
XMEMCPY(newConfig->publicName, publicName, XSTRLEN(publicName) + 1);
newConfig->maxNameLen = maxNameLen;
}
}
if (ret != 0) {
if (newConfig) {
if (newConfig->receiverPrivkey != NULL) {
wc_HpkeFreeKey(hpke, newConfig->kemId,
newConfig->receiverPrivkey, ctx->heap);
}
XFREE(newConfig->cipherSuites, ctx->heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(newConfig->publicName, ctx->heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(newConfig, ctx->heap, DYNAMIC_TYPE_TMP_BUFFER);
}
}
else {
/* insert new configs at beginning of LL as preference should be given
* to the most recently generated configs */
if (ctx->echConfigs == NULL) {
ctx->echConfigs = newConfig;
}
else {
newConfig->next = ctx->echConfigs;
ctx->echConfigs = newConfig;
}
}
if (ret == 0)
ret = WOLFSSL_SUCCESS;
WC_FREE_VAR_EX(hpke, ctx->heap, DYNAMIC_TYPE_TMP_BUFFER);
WC_FREE_VAR_EX(rng, ctx->heap, DYNAMIC_TYPE_RNG);
return ret;
}
/* base64-decode echConfigs into a freshly allocated buffer */
static int DecodeEchConfigsBase64(void* heap, const char* echConfigs64,
word32 echConfigs64Len, byte** decodedConfigs, word32* decodedLen)
{
int ret = 0;
byte* buf;
word32 len = echConfigs64Len * 3 / 4 + 1;
if (echConfigs64 == NULL || echConfigs64Len == 0)
return BAD_FUNC_ARG;
buf = (byte*)XMALLOC(len, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (buf == NULL)
return MEMORY_E;
buf[len - 1] = 0;
/* decode the echConfigs */
ret = Base64_Decode((const byte*)echConfigs64, echConfigs64Len, buf, &len);
if (ret != 0) {
XFREE(buf, heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
*decodedConfigs = buf;
*decodedLen = len;
return 0;
}
int wolfSSL_CTX_SetEchConfigsBase64(WOLFSSL_CTX* ctx, const char* echConfigs64,
word32 echConfigs64Len)
{
int ret;
word32 decodedLen;
byte* decodedConfigs;
if (ctx == NULL)
return BAD_FUNC_ARG;
ret = DecodeEchConfigsBase64(ctx->heap, echConfigs64, echConfigs64Len,
&decodedConfigs, &decodedLen);
if (ret != 0)
return ret;
ret = wolfSSL_CTX_SetEchConfigs(ctx, decodedConfigs, decodedLen);
XFREE(decodedConfigs, ctx->heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
int wolfSSL_CTX_SetEchConfigs(WOLFSSL_CTX* ctx, const byte* echConfigs,
word32 echConfigsLen)
{
int ret;
if (ctx == NULL || echConfigs == NULL || echConfigsLen == 0)
return BAD_FUNC_ARG;
FreeEchConfigs(ctx->echConfigs, ctx->heap);
ctx->echConfigs = NULL;
ret = SetEchConfigsEx(&ctx->echConfigs, ctx->heap, echConfigs,
echConfigsLen);
if (ret == 0)
return WOLFSSL_SUCCESS;
return ret;
}
/* get the ech configs that the server context is using */
int wolfSSL_CTX_GetEchConfigs(WOLFSSL_CTX* ctx, byte* output,
word32* outputLen) {
if (ctx == NULL || outputLen == NULL)
return BAD_FUNC_ARG;
/* if we don't have ech configs */
if (ctx->echConfigs == NULL)
return WOLFSSL_FATAL_ERROR;
return GetEchConfigsEx(ctx->echConfigs, output, outputLen);
}
void wolfSSL_CTX_SetEchEnable(WOLFSSL_CTX* ctx, byte enable)
{
if (ctx != NULL) {
ctx->disableECH = !enable;
if (ctx->disableECH) {
TLSX_Remove(&ctx->extensions, TLSX_ECH, ctx->heap);
FreeEchConfigs(ctx->echConfigs, ctx->heap);
ctx->echConfigs = NULL;
}
}
}
/* disabled (default) -> only decrypt the ClientHello with configs that have a
* matching configId
* enabled -> try to decrypt the inner ClientHello with all configs */
void wolfSSL_CTX_SetEchEnableTrialDecrypt(WOLFSSL_CTX* ctx, byte enable)
{
if (ctx != NULL)
ctx->enableEchTrialDecrypt = (enable != 0);
}
/* set the ech config from base64 for our client ssl object, base64 is the
* format ech configs are sent using dns records */
int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl, const char* echConfigs64,
word32 echConfigs64Len)
{
int ret;
word32 decodedLen;
byte* decodedConfigs;
if (ssl == NULL)
return BAD_FUNC_ARG;
ret = DecodeEchConfigsBase64(ssl->heap, echConfigs64, echConfigs64Len,
&decodedConfigs, &decodedLen);
if (ret != 0)
return ret;
ret = wolfSSL_SetEchConfigs(ssl, decodedConfigs, decodedLen);
XFREE(decodedConfigs, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
/* 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)
{
int ret;
if (ssl == NULL || echConfigs == NULL || echConfigsLen == 0)
return BAD_FUNC_ARG;
/* already have ech configs */
if (ssl->echConfigs != NULL) {
return WOLFSSL_FATAL_ERROR;
}
ret = SetEchConfigsEx(&ssl->echConfigs, ssl->heap, echConfigs,
echConfigsLen);
/* if we found valid configs */
if (ret == 0) {
return WOLFSSL_SUCCESS;
}
return ret;
}
/* store retry configs received during ECH rejection
* returns 0 on success, error otherwise */
int SetRetryConfigs(WOLFSSL* ssl, const byte* echConfigs, word32 echConfigsLen)
{
int ret;
if (ssl == NULL || echConfigs == NULL || echConfigsLen == 0)
return BAD_FUNC_ARG;
if (ssl->echRetryConfigs != NULL) {
return WOLFSSL_FATAL_ERROR;
}
ret = SetEchConfigsEx(&ssl->echRetryConfigs, ssl->heap, echConfigs,
echConfigsLen);
return ret;
}
/* get the raw ech config from our struct */
int GetEchConfig(WOLFSSL_EchConfig* config, byte* output, word32* outputLen)
{
int i;
word16 totalLen = 0;
word16 kemEncLen;
word16 publicNameLen;
if (config == NULL || (output == NULL && outputLen == NULL))
return BAD_FUNC_ARG;
/* ECH spec limits public_name to 255 bytes (1-byte length prefix) */
if (config->publicName == NULL || XSTRLEN(config->publicName) > 255)
return BAD_FUNC_ARG;
publicNameLen = (word16)XSTRLEN(config->publicName);
/* 2 for version */
totalLen += 2;
/* 2 for length */
totalLen += 2;
/* 1 for configId */
totalLen += 1;
/* 2 for kemId */
totalLen += 2;
/* 2 for hpke_len */
totalLen += 2;
/* hpke_pub_key */
kemEncLen = wc_HpkeKemGetEncLen(config->kemId);
if (kemEncLen == 0)
return BAD_FUNC_ARG;
totalLen += kemEncLen;
/* cipherSuitesLen */
totalLen += 2;
/* cipherSuites */
totalLen += config->numCipherSuites * 4;
/* public name len */
totalLen += 2;
/* public name */
totalLen += publicNameLen;
/* trailing zeros */
totalLen += 2;
if (output == NULL) {
*outputLen = totalLen;
return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
}
if (totalLen > *outputLen) {
*outputLen = totalLen;
return INPUT_SIZE_E;
}
/* version */
c16toa(TLSX_ECH, output);
output += 2;
/* length - 4 for version and length itself */
c16toa(totalLen - 4, output);
output += 2;
/* configId */
*output = config->configId;
output++;
/* kemId */
c16toa(config->kemId, output);
output += 2;
/* length and key itself */
c16toa(kemEncLen, output);
output += 2;
XMEMCPY(output, config->receiverPubkey, kemEncLen);
output += kemEncLen;
/* cipherSuites len */
c16toa(config->numCipherSuites * 4, output);
output += 2;
/* cipherSuites */
for (i = 0; i < config->numCipherSuites; i++) {
c16toa(config->cipherSuites[i].kdfId, output);
output += 2;
c16toa(config->cipherSuites[i].aeadId, output);
output += 2;
}
/* maximum name len */
*output = config->maxNameLen;
output++;
/* publicName len */
*output = (byte)publicNameLen;
output++;
/* publicName */
XMEMCPY(output, config->publicName, publicNameLen);
output += publicNameLen;
/* no extensions, print zeros */
c16toa(0, output);
/* output += 2; */
*outputLen = totalLen;
return 0;
}
/* wrapper function to get ech configs from application code */
int wolfSSL_GetEchConfigs(WOLFSSL* ssl, byte* output, word32* outputLen)
{
if (ssl == NULL || outputLen == NULL)
return BAD_FUNC_ARG;
/* if we don't have ech configs */
if (ssl->echConfigs == NULL) {
return WOLFSSL_FATAL_ERROR;
}
return GetEchConfigsEx(ssl->echConfigs, output, outputLen);
}
/* wrapper function to get retry configs
* a client should only call this after the 'wolfSSL_connect()' call fails
* returns error if retry configs were not received or were malformed */
int wolfSSL_GetEchRetryConfigs(WOLFSSL* ssl, byte* output, word32* outputLen)
{
if (ssl == NULL || outputLen == NULL)
return BAD_FUNC_ARG;
if (ssl->echRetryConfigs == NULL || !ssl->options.echRetryConfigsAccepted) {
return WOLFSSL_FATAL_ERROR;
}
return GetEchConfigsEx(ssl->echRetryConfigs, output, outputLen);
}
void wolfSSL_SetEchEnable(WOLFSSL* ssl, byte enable)
{
if (ssl != NULL) {
ssl->options.disableECH = !enable;
if (ssl->options.disableECH) {
TLSX_Remove(&ssl->extensions, TLSX_ECH, ssl->heap);
FreeEchConfigs(ssl->echConfigs, ssl->heap);
ssl->echConfigs = NULL;
}
}
}
/* disabled (default) -> only decrypt the ClientHello with configs that have a
* matching configId
* enabled -> try to decrypt the inner ClientHello with all configs */
void wolfSSL_SetEchEnableTrialDecrypt(WOLFSSL* ssl, byte enable)
{
if (ssl != NULL)
ssl->options.enableEchTrialDecrypt = (enable != 0);
}
/* Return the status of the ECH connection. Possible return values:
* WOLFSSL_ECH_STATUS_NOT_OFFERED:
* server - client did not send ECH or it is not setup
* client - ECH is not setup
* pending - connection has not been initiated
*
* WOLFSSL_ECH_STATUS_GREASE:
* client - GREASE ECH extension sent
*
* WOLFSSL_ECH_STATUS_REJECTED:
* server - ECH was not accepted (decryption of inner ClientHello failed)
* client - ECH was offered but the server rejected it
*
* In both cases the connection fell back to the outer transcript.
*
* WOLFSSL_ECH_STATUS_ACCEPTED:
* Decryption of the inner ClientHello was successful and the inner
* transcript was used.
*
* Returns BAD_FUNC_ARG if ssl is NULL. */
int wolfSSL_GetEchStatus(const WOLFSSL* ssl)
{
if (ssl == NULL)
return BAD_FUNC_ARG;
if (ssl->options.disableECH)
return WOLFSSL_ECH_STATUS_NOT_OFFERED;
if (ssl->options.echAccepted)
return WOLFSSL_ECH_STATUS_ACCEPTED;
if (ssl->options.side == WOLFSSL_SERVER_END) {
TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH);
WOLFSSL_ECH* ech;
if (echX == NULL || echX->data == NULL)
return WOLFSSL_ECH_STATUS_NOT_OFFERED;
/* state stays at ECH_WRITE_NONE and innerClientHello stays NULL when
* the client did not send an ECH extension */
ech = (WOLFSSL_ECH*)echX->data;
if (ech->state == ECH_WRITE_NONE && ech->innerClientHello == NULL)
return WOLFSSL_ECH_STATUS_NOT_OFFERED;
return WOLFSSL_ECH_STATUS_REJECTED;
}
/* client */
if (ssl->options.connectState < CLIENT_HELLO_SENT)
return WOLFSSL_ECH_STATUS_NOT_OFFERED;
if (ssl->echConfigs == NULL)
return WOLFSSL_ECH_STATUS_GREASE;
return WOLFSSL_ECH_STATUS_REJECTED;
}
/* Walk the ECHConfigExtension list and check for mandatory extensions.
* Returns:
* 0 if all extensions are known/optional,
* error otherwise. */
static int EchConfigCheckExtensions(const byte* exts, word16 extsLen)
{
word16 bytesLeft = extsLen;
word16 extType;
word16 extDataLen;
while (bytesLeft >= 4) {
ato16(exts, &extType);
ato16(exts + 2, &extDataLen);
if (bytesLeft - 4 < extDataLen)
return BUFFER_E;
if (extType & 0x8000)
return UNSUPPORTED_EXTENSION;
exts += 4 + extDataLen;
bytesLeft -= 4 + extDataLen;
}
if (bytesLeft != 0)
return BUFFER_E;
return 0;
}
/* Parse the ECH configs and output to the corresponding outputConfigs
* return 0 on success, error otherwise */
int SetEchConfigsEx(WOLFSSL_EchConfig** outputConfigs, void* heap,
const byte* echConfigs, word32 echConfigsLen)
{
int ret = 0;
int unsupportedAlgos = 0;
word32 configIdx;
word32 idx;
int j;
word16 totalLength;
word16 version;
word16 length;
word16 hpkePubkeyLen;
word16 cipherSuitesLen;
word16 extensionsLen;
byte publicNameLen;
WOLFSSL_EchConfig* configList = NULL;
WOLFSSL_EchConfig* workingConfig = NULL;
WOLFSSL_EchConfig* lastConfig = NULL;
const byte* echConfig = NULL;
if (outputConfigs == NULL || echConfigs == NULL || echConfigsLen < 2)
return BAD_FUNC_ARG;
/* check that the total length is well formed */
ato16(echConfigs, &totalLength);
if (totalLength != echConfigsLen - 2)
return BUFFER_E;
configIdx = 2;
do {
/* version (2) + length (2) */
if (configIdx + 4 > echConfigsLen) {
ret = BUFFER_E;
break;
}
echConfig = echConfigs + configIdx;
ato16(echConfig, &version);
ato16(echConfig + 2, &length);
if (configIdx + 4 + length > echConfigsLen) {
ret = BUFFER_E;
break;
}
if (version != TLSX_ECH) {
configIdx += 4 + length;
continue;
}
if (workingConfig == NULL) {
workingConfig = (WOLFSSL_EchConfig*)XMALLOC(
sizeof(WOLFSSL_EchConfig), heap, DYNAMIC_TYPE_TMP_BUFFER);
configList = workingConfig;
}
else {
lastConfig = workingConfig;
workingConfig->next = (WOLFSSL_EchConfig*)XMALLOC(
sizeof(WOLFSSL_EchConfig), heap, DYNAMIC_TYPE_TMP_BUFFER);
workingConfig = workingConfig->next;
}
if (workingConfig == NULL) {
ret = MEMORY_E;
break;
}
XMEMSET(workingConfig, 0, sizeof(WOLFSSL_EchConfig));
workingConfig->rawLen = 4 + length;
workingConfig->raw = (byte*)XMALLOC(workingConfig->rawLen, heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (workingConfig->raw == NULL) {
ret = MEMORY_E;
break;
}
XMEMCPY(workingConfig->raw, echConfig, workingConfig->rawLen);
/* version and length already checked */
echConfig += 4;
idx = 0;
/* configId */
if (idx + 1 > length) {
ret = BUFFER_E;
break;
}
workingConfig->configId = echConfig[idx];
idx += 1;
/* kemId */
if (idx + 2 > length) {
ret = BUFFER_E;
break;
}
ato16(echConfig + idx, &workingConfig->kemId);
idx += 2;
/* hpke public_key */
if (idx + 2 > length) {
ret = BUFFER_E;
break;
}
ato16(echConfig + idx, &hpkePubkeyLen);
idx += 2;
if (idx + hpkePubkeyLen > length) {
ret = BUFFER_E;
break;
}
/* unsupported KEM: skip pubkey; end of loop will free this config */
if (wc_HpkeKemIsSupported(workingConfig->kemId)) {
if (hpkePubkeyLen != wc_HpkeKemGetEncLen(workingConfig->kemId)) {
ret = BUFFER_E;
break;
}
XMEMCPY(workingConfig->receiverPubkey, echConfig + idx, hpkePubkeyLen);
}
idx += hpkePubkeyLen;
/* cipher suites */
if (idx + 2 > length) {
ret = BUFFER_E;
break;
}
ato16(echConfig + idx, &cipherSuitesLen);
idx += 2;
if (cipherSuitesLen == 0 || cipherSuitesLen % 4 != 0 ||
cipherSuitesLen >= 1024) {
/* numCipherSuites is a byte so only 256 ciphersuites (each 4 bytes)
* can be accessed */
ret = BUFFER_E;
break;
}
if (idx + cipherSuitesLen > length) {
ret = BUFFER_E;
break;
}
workingConfig->cipherSuites = (EchCipherSuite*)XMALLOC(cipherSuitesLen,
heap, DYNAMIC_TYPE_TMP_BUFFER);
if (workingConfig->cipherSuites == NULL) {
ret = MEMORY_E;
break;
}
workingConfig->numCipherSuites = (byte)(cipherSuitesLen / 4);
for (j = 0; j < workingConfig->numCipherSuites; j++) {
ato16(echConfig + idx, &workingConfig->cipherSuites[j].kdfId);
ato16(echConfig + idx + 2, &workingConfig->cipherSuites[j].aeadId);
idx += 4;
}
/* maxNameLen */
if (idx + 1 > length) {
ret = BUFFER_E;
break;
}
workingConfig->maxNameLen = echConfig[idx];
idx += 1;
/* publicName */
if (idx + 1 > length) {
ret = BUFFER_E;
break;
}
publicNameLen = echConfig[idx];
idx += 1;
if (publicNameLen == 0) {
ret = BUFFER_E;
break;
}
if (idx + publicNameLen > length) {
ret = BUFFER_E;
break;
}
workingConfig->publicName = (char*)XMALLOC(publicNameLen + 1, heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (workingConfig->publicName == NULL) {
ret = MEMORY_E;
break;
}
XMEMCPY(workingConfig->publicName, echConfig + idx, publicNameLen);
workingConfig->publicName[publicNameLen] = '\0';
idx += publicNameLen;
/* extensions */
if (idx + 2 > length) {
ret = BUFFER_E;
break;
}
ato16(echConfig + idx, &extensionsLen);
idx += 2;
if (idx + extensionsLen != length) {
ret = BUFFER_E;
break;
}
ret = EchConfigCheckExtensions(echConfig + idx, extensionsLen);
if (ret < 0 && ret != WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION))
break;
/* KEM, ciphersuite, or mandatory extension not supported, free this
* config and then try to parse another */
if (ret == WC_NO_ERR_TRACE(UNSUPPORTED_EXTENSION) ||
EchConfigGetSupportedCipherSuite(workingConfig) < 0) {
ret = 0;
unsupportedAlgos = 1;
XFREE(workingConfig->cipherSuites, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(workingConfig->publicName, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(workingConfig->raw, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(workingConfig, heap, DYNAMIC_TYPE_TMP_BUFFER);
workingConfig = lastConfig;
if (workingConfig != NULL)
workingConfig->next = NULL;
else
configList = NULL;
}
configIdx += 4 + length;
} while (configIdx < echConfigsLen);
if (ret == 0 && configIdx != echConfigsLen)
ret = BUFFER_E;
/* if we found valid configs */
if (ret == 0 && configList != NULL) {
*outputConfigs = configList;
return ret;
}
workingConfig = configList;
while (workingConfig != NULL) {
lastConfig = workingConfig;
workingConfig = workingConfig->next;
XFREE(lastConfig->cipherSuites, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(lastConfig->publicName, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(lastConfig->raw, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(lastConfig, heap, DYNAMIC_TYPE_TMP_BUFFER);
}
/* syntactically correct but configs are not supported */
if (ret == 0 && unsupportedAlgos)
return UNSUPPORTED_SUITE;
if (ret == 0)
return UNSUPPORTED_PROTO_VERSION;
return ret;
}
/* get the raw ech configs from our linked list of ech config structs */
int GetEchConfigsEx(WOLFSSL_EchConfig* configs, byte* output, word32* outputLen)
{
int ret = 0;
WOLFSSL_EchConfig* workingConfig = NULL;
byte* outputStart = output;
word32 totalLen = 2;
word32 workingOutputLen = 0;
if (configs == NULL || outputLen == NULL ||
(output != NULL && *outputLen < totalLen)) {
return BAD_FUNC_ARG;
}
/* skip over total length which we fill in later */
if (output != NULL) {
workingOutputLen = *outputLen - totalLen;
output += 2;
}
else {
/* caller getting the size only, set current 2 byte length size */
*outputLen = totalLen;
}
workingConfig = configs;
while (workingConfig != NULL) {
/* get this config */
ret = GetEchConfig(workingConfig, output, &workingOutputLen);
if (output != NULL)
output += workingOutputLen;
/* add this config's length to the total length */
totalLen += workingOutputLen;
if (totalLen > *outputLen)
workingOutputLen = 0;
else
workingOutputLen = *outputLen - totalLen;
/* only error we break on, other 2 we need to keep finding length */
if (ret == WC_NO_ERR_TRACE(BAD_FUNC_ARG))
return BAD_FUNC_ARG;
workingConfig = workingConfig->next;
}
if (output == NULL) {
*outputLen = totalLen;
return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
}
if (totalLen > *outputLen) {
*outputLen = totalLen;
return INPUT_SIZE_E;
}
/* total size -2 for size itself */
c16toa(totalLen - 2, outputStart);
*outputLen = totalLen;
return WOLFSSL_SUCCESS;
}
#endif /* WOLFSSL_TLS13 && HAVE_ECH */
#endif /* !WOLFSSL_SSL_ECH_INCLUDED */