testing + bug fixes for TLS ECH

This commit is contained in:
sebastian-carpenter
2026-02-12 10:50:45 -07:00
parent cb2d693550
commit c3a38dced7
10 changed files with 1233 additions and 119 deletions
+312
View File
@@ -0,0 +1,312 @@
name: OpenSSL ECH Interop Test
# START OF COMMON SECTION
on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# END OF COMMON SECTION
jobs:
build_wolfssl:
name: Build wolfSSL
if: github.repository_owner == 'wolfssl'
runs-on: ubuntu-24.04
timeout-minutes: 4
steps:
- name: Build wolfSSL
uses: wolfSSL/actions-build-autotools-project@v1
with:
path: wolfssl
configure: --enable-ech CFLAGS='-DUSE_FLAT_TEST_H'
install: true
- name: tar build-dir
run: |
# need server.h and client.h which are not installed normally
cp "$GITHUB_WORKSPACE/wolfssl/examples/server/server.h" \
build-dir/share/doc/wolfssl/example/server.h
cp "$GITHUB_WORKSPACE/wolfssl/examples/client/client.h" \
build-dir/share/doc/wolfssl/example/client.h
# need certs so 'wolfSSL error: wolf root not found' does not show up
cp -r "$GITHUB_WORKSPACE/wolfssl/certs" build-dir/certs
tar -zcf build-dir.tgz build-dir
- name: Upload built wolfSSL
uses: actions/upload-artifact@v4
with:
name: wolf-install-openssl-ech
path: build-dir.tgz
retention-days: 5
build_openssl_ech:
name: Build OpenSSL (feature/ech)
if: github.repository_owner == 'wolfssl'
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout OpenSSL feature/ech branch
uses: actions/checkout@v4
with:
repository: openssl/openssl
ref: feature/ech
path: openssl
- name: Build OpenSSL
working-directory: openssl
run: |
./Configure --prefix=$GITHUB_WORKSPACE/openssl-install \
--openssldir=$GITHUB_WORKSPACE/openssl-install/ssl \
enable-ech no-docs
make -j$(nproc)
make install_sw
- name: tar openssl-install
run: tar -zcf openssl-install.tgz openssl-install
- name: Upload built OpenSSL
uses: actions/upload-artifact@v4
with:
name: openssl-ech-install
path: openssl-install.tgz
retention-days: 5
ech_server_interop_test:
name: ECH Server Interop Test
if: github.repository_owner == 'wolfssl'
needs: [build_wolfssl, build_openssl_ech]
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Download wolfSSL build
uses: actions/download-artifact@v4
with:
name: wolf-install-openssl-ech
- name: Download OpenSSL build
uses: actions/download-artifact@v4
with:
name: openssl-ech-install
- name: Extract builds
run: |
tar -xzf build-dir.tgz
tar -xzf openssl-install.tgz
- name: Build wolfssl server example
run: |
export WOLFSSL_INSTALL_DIR="$GITHUB_WORKSPACE/build-dir"
export WOLFSSL_BIN_DIR="$WOLFSSL_INSTALL_DIR/bin"
export CFLAGS="-Wall -I$WOLFSSL_INSTALL_DIR/include"
export LIBS="-L$WOLFSSL_INSTALL_DIR/lib -lm -lwolfssl"
export LD_LIBRARY_PATH="$WOLFSSL_INSTALL_DIR/lib/:$LD_LIBRARY_PATH"
gcc -o "$WOLFSSL_BIN_DIR/server" \
"$WOLFSSL_INSTALL_DIR/share/doc/wolfssl/example/server.c" \
$CFLAGS $LIBS -I"$WOLFSSL_INSTALL_DIR/share/doc/wolfssl/example"
- name: ECH interop - wolfSSL server, OpenSSL client
run: |
set -e
export LD_LIBRARY_PATH="$GITHUB_WORKSPACE/openssl-install/lib64:$GITHUB_WORKSPACE/openssl-install/lib:$GITHUB_WORKSPACE/build-dir/lib:$LD_LIBRARY_PATH"
OPENSSL=$GITHUB_WORKSPACE/openssl-install/bin/openssl
WOLFSSL_SERVER=$GITHUB_WORKSPACE/build-dir/bin/server
CERT_DIR="$GITHUB_WORKSPACE/build-dir/certs"
READY_FILE="$GITHUB_WORKSPACE/wolfssl_tls13_ready$$"
LOG_FILE="$GITHUB_WORKSPACE/log_file.log"
PRIV_NAME="ech-private-name.com"
PUB_NAME="ech-public-name.com"
ECH_CONFIG=""
PORT=0
rm -f "$READY_FILE"
# need to cd into build-dir so the certs/ dir is available for server
cd build-dir
$OPENSSL version | tee "$LOG_FILE"
# start server with ephemeral port + ready file
# also set server to be line buffered so the log can be grepped
stdbuf -oL $WOLFSSL_SERVER \
-v 4 \
-R "$READY_FILE" \
-p "$PORT" \
-S "$PRIV_NAME" \
--ech "$PUB_NAME" \
&>> "$LOG_FILE" &
# wait for server to be ready, then get port
counter=0
while [ ! -s "$READY_FILE" ]; do
sleep 0.1
counter=$((counter + 1))
if [ "$counter" -gt 50 ]; then
echo "ERROR: no ready file" &>> "$LOG_FILE"
exit 1
fi
done
PORT="$(cat "$READY_FILE")"
echo "parsed port: $PORT" &>> "$LOG_FILE"
# get ECH config from server
counter=0
while [ -z "$ECH_CONFIG" ]; do
ECH_CONFIG=$(grep -m1 "ECH config (base64): " "$LOG_FILE" \
2>/dev/null | sed 's/ECH config (base64): //g')
sleep 0.1
counter=$((counter + 1))
if [ "$counter" -gt 50 ]; then
echo "ERROR: no ECH configs" &>> "$LOG_FILE"
exit 1
fi
done
echo "parsed ech config: $ECH_CONFIG" &>> "$LOG_FILE"
# Test with OpenSSL s_client using ECH
echo "wolfssl" | $OPENSSL s_client \
-tls1_3 \
-connect "localhost:$PORT" \
-cert "$CERT_DIR/client-cert.pem" \
-key "$CERT_DIR/client-key.pem" \
-CAfile "$CERT_DIR/ca-cert.pem" \
-servername "$PRIV_NAME" \
-ech_config_list "$ECH_CONFIG" \
&>> "$LOG_FILE"
grep "ECH: success: 1" "$LOG_FILE"
# cleanup
rm -f "$READY_FILE"
rm -f "$LOG_FILE"
- name: Print debug info on failure
if: ${{ failure() }}
run: |
if [ -s "$GITHUB_WORKSPACE/log_file.log" ]; then
cat "$GITHUB_WORKSPACE/log_file.log"
else
echo "No log file"
fi
ech_client_interop_test:
name: ECH Client Interop Test
if: github.repository_owner == 'wolfssl'
needs: [build_wolfssl, build_openssl_ech]
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Download wolfSSL build
uses: actions/download-artifact@v4
with:
name: wolf-install-openssl-ech
- name: Download OpenSSL build
uses: actions/download-artifact@v4
with:
name: openssl-ech-install
- name: Extract builds
run: |
tar -xzf build-dir.tgz
tar -xzf openssl-install.tgz
- name: Build wolfssl client example
run: |
export WOLFSSL_INSTALL_DIR="$GITHUB_WORKSPACE/build-dir"
export WOLFSSL_BIN_DIR="$WOLFSSL_INSTALL_DIR/bin"
export CFLAGS="-Wall -I$WOLFSSL_INSTALL_DIR/include"
export LIBS="-L$WOLFSSL_INSTALL_DIR/lib -lm -lwolfssl"
export LD_LIBRARY_PATH="$WOLFSSL_INSTALL_DIR/lib/:$LD_LIBRARY_PATH"
gcc -o "$WOLFSSL_BIN_DIR/client" \
"$WOLFSSL_INSTALL_DIR/share/doc/wolfssl/example/client.c" \
$CFLAGS $LIBS -I"$WOLFSSL_INSTALL_DIR/share/doc/wolfssl/example"
- name: ECH interop - wolfSSL client, OpenSSL server
run: |
set -e
export LD_LIBRARY_PATH="$GITHUB_WORKSPACE/openssl-install/lib64:$GITHUB_WORKSPACE/openssl-install/lib:$GITHUB_WORKSPACE/build-dir/lib:$LD_LIBRARY_PATH"
OPENSSL=$GITHUB_WORKSPACE/openssl-install/bin/openssl
WOLFSSL_CLIENT=$GITHUB_WORKSPACE/build-dir/bin/client
CERT_DIR="$GITHUB_WORKSPACE/build-dir/certs"
LOG_FILE="$GITHUB_WORKSPACE/log_file.log"
ECH_FILE="$GITHUB_WORKSPACE/ech_config.pem"
PRIV_NAME="ech-private-name.com"
PUB_NAME="ech-public-name.com"
PORT=""
ECH_CONFIG=""
rm -f "$ECH_FILE"
# need to cd into build-dir so the certs/ dir is available for client
cd build-dir
$OPENSSL version | tee "$LOG_FILE"
$OPENSSL ech -public_name "$PUB_NAME" -out "$ECH_FILE" &>> "$LOG_FILE"
# parse ECH config from file
ECH_CONFIG=$(sed -n '/BEGIN ECHCONFIG/,/END ECHCONFIG/{/BEGIN ECHCONFIG\|END ECHCONFIG/d;p}' "$ECH_FILE" | tr -d '\n')
echo "parsed ech config: $ECH_CONFIG" &>> "$LOG_FILE"
# start OpenSSL ECH server with ephemeral port and make sure it is
# line-buffered
stdbuf -oL $OPENSSL s_server \
-tls1_3 \
-cert "$CERT_DIR/server-cert.pem" \
-key "$CERT_DIR/server-key.pem" \
-cert2 "$CERT_DIR/server-cert.pem" \
-key2 "$CERT_DIR/server-key.pem" \
-ech_key "$ECH_FILE" \
-servername "$PRIV_NAME" \
-accept 0 \
-naccept 1 \
&>> "$LOG_FILE" <<< "wolfssl!" &
# wait for server port to be ready and capture it
counter=0
while [ -z "$PORT" ]; do
PORT=$(grep -m1 "ACCEPT" "$LOG_FILE" | sed 's/.*:\([0-9]*\)$/\1/')
sleep 0.1
counter=$((counter + 1))
if [ "$counter" -gt 50 ]; then
echo "ERROR: server port not found" &>> "$LOG_FILE"
exit 1
fi
done
echo "parsed port: $PORT" &>> "$LOG_FILE"
# test with wolfssl client
$WOLFSSL_CLIENT -v 4 \
-p "$PORT" \
-S "$PRIV_NAME" \
--ech "$ECH_CONFIG" \
&>> "$LOG_FILE"
grep "ech_success=1" "$LOG_FILE"
# cleanup
rm -f "$LOG_FILE"
rm -f "$ECH_FILE"
- name: Print debug info on failure
if: ${{ failure() }}
run: |
if [ -s "$GITHUB_WORKSPACE/log_file.log" ]; then
cat "$GITHUB_WORKSPACE/log_file.log"
else
echo "No log file"
fi
+37 -4
View File
@@ -52,10 +52,14 @@ static const char *wolfsentry_config_path = NULL;
#endif
#include <wolfssl/test.h>
#include <examples/client/client.h>
#include <wolfssl/error-ssl.h>
#ifdef USE_FLAT_TEST_H
#include "client.h"
#else
#include "examples/client/client.h"
#endif
#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)
@@ -1171,7 +1175,7 @@ static int ClientWriteRead(WOLFSSL* ssl, const char* msg, int msgSz,
/* 4. add the same message into Japanese section */
/* (will be translated later) */
/* 5. add printf() into suitable position of Usage() */
static const char* client_usage_msg[][79] = {
static const char* client_usage_msg[][80] = {
/* English */
{
" NOTE: All files relative to wolfSSL home dir\n", /* 0 */
@@ -1425,10 +1429,15 @@ static const char* client_usage_msg[][79] = {
#endif
#ifdef HAVE_ECC_BRAINPOOL
"--bpKs Use Brainpool ECC group for key share\n", /* 77 */
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
"--ech <base64> Use Encrypted Client Hello with base64 encoded "
"ECH configs\n",
/* 78 */
#endif
"\n"
"For simpler wolfSSL TLS client examples, visit\n"
"https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", /* 78 */
"https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", /* 79 */
NULL,
},
#ifndef NO_MULTIBYTE_PRINT
@@ -1931,6 +1940,9 @@ static void Usage(void)
#endif
#ifdef HAVE_ECC_BRAINPOOL
printf("%s", msg[++msgid]); /* --bpKs */
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
printf("%s", msg[++msgid]); /* --ech */
#endif
printf("%s", msg[++msgid]); /* --files-are-der */
printf("%s", msg[++msgid]); /* Documentation Hint */
@@ -2119,6 +2131,9 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
#endif /* WOLFSSL_SYS_CRYPTO_POLICY */
#ifdef HAVE_ECC_BRAINPOOL
{ "bpKs", 0, 270 },
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
{ "ech", 1, 271 },
#endif
{ 0, 0, 0 }
};
@@ -2187,6 +2202,9 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
#ifdef HAVE_SNI
char* sniHostName = NULL;
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
char* echConfigs64 = NULL;
#endif
#ifdef HAVE_TRUSTED_CA
int trustedCaKeyId = 0;
#endif
@@ -3013,6 +3031,11 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
#endif
break;
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
case 271:
echConfigs64 = myoptarg;
break;
#endif
default:
Usage();
@@ -3878,6 +3901,16 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
err_sys("unable to get SSL object");
}
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
if (echConfigs64 != NULL) {
if (wolfSSL_SetEchConfigsBase64(ssl, echConfigs64,
(word32)XSTRLEN(echConfigs64)) != WOLFSSL_SUCCESS) {
wolfSSL_CTX_free(ctx); ctx = NULL;
err_sys("SetEchConfigsBase64 failed");
}
}
#endif
#ifdef WOLFSSL_DUAL_ALG_CERTS
if (!wolfSSL_UseCKS(ssl, cks_order, sizeof(cks_order))) {
wolfSSL_CTX_free(ctx); ctx = NULL;
+56 -3
View File
@@ -48,6 +48,10 @@
#include <wolfssl/wolfcrypt/ecc.h> /* wc_ecc_fp_free */
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
#include <wolfssl/wolfcrypt/coding.h>
#endif
#ifdef WOLFSSL_WOLFSENTRY_HOOKS
#include <wolfsentry/wolfsentry.h>
#if !defined(NO_FILESYSTEM) && !defined(WOLFSENTRY_NO_JSON)
@@ -73,7 +77,11 @@ static const char *wolfsentry_config_path = NULL;
#include <wolfssl/test.h>
#include <wolfssl/error-ssl.h>
#include "examples/server/server.h"
#ifdef USE_FLAT_TEST_H
#include "server.h"
#else
#include "examples/server/server.h"
#endif
#if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS)
@@ -911,7 +919,7 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519,
/* 4. add the same message into Japanese section */
/* (will be translated later) */
/* 5. add printf() into suitable position of Usage() */
static const char* server_usage_msg[][66] = {
static const char* server_usage_msg[][69] = {
/* English */
{
" NOTE: All files relative to wolfSSL home dir\n", /* 0 */
@@ -1107,11 +1115,16 @@ static const char* server_usage_msg[][66] = {
#endif
#ifdef WOLFSSL_SYS_CRYPTO_POLICY
"--crypto-policy <path to crypto policy file>\n", /* 66 */
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
"--ech <name> Generate Encrypted Client Hello config with "
"public name <name>\n",
/* 67 */
#endif
"\n"
"For simpler wolfSSL TLS server examples, visit\n"
"https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n",
/* 67 */
/* 68 */
NULL,
},
#ifndef NO_MULTIBYTE_PRINT
@@ -1486,6 +1499,9 @@ static void Usage(void)
#endif
#ifdef WOLFSSL_DUAL_ALG_CERTS
printf("%s", msg[++msgId]); /* --altPrivKey */
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
printf("%s", msg[++msgId]); /* --ech */
#endif
printf("%s", msg[++msgId]); /* Examples repo link */
}
@@ -1609,6 +1625,9 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
#if defined(WOLFSSL_SYS_CRYPTO_POLICY)
{ "crypto-policy", 1, 268 },
#endif /* WOLFSSL_SYS_CRYPTO_POLICY */
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
{ "ech", 1, 269 },
#endif
{ 0, 0, 0 }
};
#endif
@@ -1685,6 +1704,9 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
#ifdef HAVE_SNI
char* sniHostName = NULL;
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
char* echPublicName = NULL;
#endif
#ifdef HAVE_TRUSTED_CA
int trustedCaKeyId = 0;
@@ -2513,6 +2535,11 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
policy = myoptarg;
#endif /* WOLFSSL_SYS_CRYPTO_POLICY */
break;
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
case 269:
echPublicName = myoptarg;
break;
#endif
case -1:
default:
@@ -3073,6 +3100,32 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
err_sys_ex(runWithErrors, "UseSNI failed");
#endif
#ifdef HAVE_ECH
if (echPublicName != NULL) {
byte echConfig[512];
word32 echConfigLen = sizeof(echConfig);
char echConfigBase64[512];
word32 echConfigBase64Len = sizeof(echConfigBase64);
if (wolfSSL_CTX_GenerateEchConfig(ctx, echPublicName, 0, 0, 0)
!= WOLFSSL_SUCCESS) {
err_sys_ex(runWithErrors, "GenerateEchConfig failed");
}
if (wolfSSL_CTX_GetEchConfigs(ctx, echConfig, &echConfigLen)
!= WOLFSSL_SUCCESS) {
err_sys_ex(runWithErrors, "GetEchConfigs failed");
}
if (Base64_Encode_NoNl(echConfig, echConfigLen, (byte*)echConfigBase64,
&echConfigBase64Len) != 0) {
err_sys_ex(runWithErrors, "Base64_Encode_NoNl failed");
}
else {
echConfigBase64[echConfigBase64Len] = '\0';
printf("ECH config (base64): %s\n", echConfigBase64);
}
}
#endif
#ifdef USE_WINDOWS_API
if (port == 0) {
/* Generate random port for testing */
+1 -2
View File
@@ -8634,10 +8634,9 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl)
ForceZero(&ssl->serverSecret, sizeof(ssl->serverSecret));
#if defined(HAVE_ECH)
if (ssl->options.useEch == 1) {
if (ssl->echConfigs != NULL) {
FreeEchConfigs(ssl->echConfigs, ssl->heap);
ssl->echConfigs = NULL;
ssl->options.useEch = 0;
}
#endif /* HAVE_ECH */
#endif /* WOLFSSL_TLS13 */
+13 -16
View File
@@ -36,7 +36,6 @@ int wolfSSL_CTX_GenerateEchConfig(WOLFSSL_CTX* ctx, const char* publicName,
int ret = 0;
word16 encLen = DHKEM_X25519_ENC_LEN;
WOLFSSL_EchConfig* newConfig;
WOLFSSL_EchConfig* parentConfig;
#ifdef WOLFSSL_SMALL_STACK
Hpke* hpke = NULL;
WC_RNG* rng;
@@ -67,7 +66,9 @@ int wolfSSL_CTX_GenerateEchConfig(WOLFSSL_CTX* ctx, const char* publicName,
else
XMEMSET(newConfig, 0, sizeof(WOLFSSL_EchConfig));
/* set random config id */
/* set random configId */
/* TODO: if an equal configId is found should the old config be removed from
* the LL? Prevents growth beyond 255+ items */
if (ret == 0)
ret = wc_RNG_GenerateByte(rng, &newConfig->configId);
@@ -147,17 +148,14 @@ int wolfSSL_CTX_GenerateEchConfig(WOLFSSL_CTX* ctx, const char* publicName,
}
}
else {
parentConfig = ctx->echConfigs;
if (parentConfig == NULL) {
/* 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 {
while (parentConfig->next != NULL) {
parentConfig = parentConfig->next;
}
parentConfig->next = newConfig;
newConfig->next = ctx->echConfigs;
ctx->echConfigs = newConfig;
}
}
@@ -250,7 +248,7 @@ void wolfSSL_CTX_SetEchEnable(WOLFSSL_CTX* ctx, byte enable)
/* 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, char* echConfigs64,
int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl, const char* echConfigs64,
word32 echConfigs64Len)
{
int ret = 0;
@@ -261,7 +259,7 @@ int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl, char* echConfigs64,
return BAD_FUNC_ARG;
/* already have ech configs */
if (ssl->options.useEch == 1) {
if (ssl->echConfigs != NULL) {
return WOLFSSL_FATAL_ERROR;
}
@@ -274,7 +272,7 @@ int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl, char* echConfigs64,
decodedConfigs[decodedLen - 1] = 0;
/* decode the echConfigs */
ret = Base64_Decode((byte*)echConfigs64, echConfigs64Len,
ret = Base64_Decode((const byte*)echConfigs64, echConfigs64Len,
decodedConfigs, &decodedLen);
if (ret != 0) {
@@ -300,7 +298,7 @@ int wolfSSL_SetEchConfigs(WOLFSSL* ssl, const byte* echConfigs,
return BAD_FUNC_ARG;
/* already have ech configs */
if (ssl->options.useEch == 1) {
if (ssl->echConfigs != NULL) {
return WOLFSSL_FATAL_ERROR;
}
@@ -309,7 +307,6 @@ int wolfSSL_SetEchConfigs(WOLFSSL* ssl, const byte* echConfigs,
/* if we found valid configs */
if (ret == 0) {
ssl->options.useEch = 1;
return WOLFSSL_SUCCESS;
}
@@ -472,7 +469,7 @@ int wolfSSL_GetEchConfigs(WOLFSSL* ssl, byte* output, word32* outputLen)
return BAD_FUNC_ARG;
/* if we don't have ech configs */
if (ssl->options.useEch != 1) {
if (ssl->echConfigs == NULL) {
return WOLFSSL_FATAL_ERROR;
}
+67 -18
View File
@@ -2257,9 +2257,10 @@ static int TLSX_SNI_Parse(WOLFSSL* ssl, const byte* input, word16 length,
byte type;
byte matched;
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
TLSX* echX = NULL;
WOLFSSL_ECH* ech = NULL;
WOLFSSL_EchConfig* workingConfig;
TLSX* echX;
word16 privateNameLen;
#endif
#endif /* !NO_WOLFSSL_SERVER */
TLSX *extension = TLSX_Find(ssl->extensions, TLSX_SERVER_NAME);
@@ -2291,7 +2292,22 @@ static int TLSX_SNI_Parse(WOLFSSL* ssl, const byte* input, word16 length,
}
#ifndef NO_WOLFSSL_SERVER
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
if (!ssl->options.disableECH) {
echX = TLSX_Find(ssl->extensions, TLSX_ECH);
if (echX != NULL) {
ech = (WOLFSSL_ECH*)(echX->data);
}
}
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
if ((!extension || !extension->data) ||
(ech != NULL && ech->sniState == ECH_INNER_SNI &&
ech->privateName == NULL)) {
#else
if (!extension || !extension->data) {
#endif
/* This will keep SNI even though TLSX_UseSNI has not been called.
* Enable it so that the received sni is available to functions
* that use a custom callback when SNI is received.
@@ -2339,24 +2355,52 @@ static int TLSX_SNI_Parse(WOLFSSL* ssl, const byte* input, word16 length,
if (!cacheOnly && !(sni = TLSX_SNI_Find((SNI*)extension->data, type)))
return 0; /* not using this type of SNI. */
#ifdef WOLFSSL_TLS13
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
if (ech != NULL && ech->sniState == ECH_INNER_SNI){
/* SNI status is carried over from processing the outer hello so it is
* necessary to clear it before processing the inner hello */
ech->sniState = ECH_INNER_SNI_ATTEMPT;
if (sni != NULL){
sni->status = WOLFSSL_SNI_NO_MATCH;
}
}
else if (ech != NULL && ech->sniState == ECH_OUTER_SNI &&
ech->privateName == NULL && sni != NULL){
/* save the private SNI before it is overwritten by the public SNI */
privateNameLen = (word16)XSTRLEN(sni->data.host_name) + 1;
ech->privateName = (char*)XMALLOC(privateNameLen, ssl->heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (ech->privateName == NULL)
return MEMORY_E;
XMEMCPY((char*)ech->privateName, sni->data.host_name,
privateNameLen);
}
#endif
#if defined(WOLFSSL_TLS13)
/* Don't process the second ClientHello SNI extension if there
* was problems with the first.
*/
if (!cacheOnly && sni->status != 0)
if (!cacheOnly && sni->status != WOLFSSL_SNI_NO_MATCH)
return 0;
#endif
matched = cacheOnly || (XSTRLEN(sni->data.host_name) == size &&
XSTRNCMP(sni->data.host_name, (const char*)input + offset, size) == 0);
#if defined(HAVE_ECH)
if (ech != NULL && ech->sniState == ECH_INNER_SNI_ATTEMPT) {
matched = cacheOnly || (XSTRLEN(ech->privateName) == size &&
XSTRNCMP(ech->privateName, (const char*)input + offset, size) == 0);
}
else
#endif
{
matched = cacheOnly || (XSTRLEN(sni->data.host_name) == size &&
XSTRNCMP(sni->data.host_name, (const char*)input + offset,
size) == 0);
}
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
echX = TLSX_Find(ssl->extensions, TLSX_ECH);
if (echX != NULL)
ech = (WOLFSSL_ECH*)(echX->data);
if (!matched && ech != NULL) {
if (!matched && ech != NULL && ech->sniState == ECH_OUTER_SNI) {
workingConfig = ech->echConfig;
while (workingConfig != NULL) {
matched = XSTRLEN(workingConfig->publicName) == size &&
XSTRNCMP(workingConfig->publicName,
@@ -2374,6 +2418,7 @@ static int TLSX_SNI_Parse(WOLFSSL* ssl, const byte* input, word16 length,
int matchStat;
int r = TLSX_UseSNI(&ssl->extensions, type, input + offset, size,
ssl->heap);
if (r != WOLFSSL_SUCCESS)
return r; /* throws error. */
@@ -13922,13 +13967,12 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
word16 len;
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;
}
if (size == 0)
return BAD_FUNC_ARG;
/* retry configs */
if (msgType == encrypted_extensions) {
ret = wolfSSL_SetEchConfigs(ssl, readBuf, size);
@@ -13937,7 +13981,8 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
ret = 0;
}
/* HRR with special confirmation */
else if (msgType == hello_retry_request && ssl->options.useEch) {
else if (msgType == hello_retry_request && ssl->echConfigs != NULL &&
!ssl->options.disableECH) {
/* length must be 8 */
if (size != ECH_ACCEPT_CONFIRMATION_SZ)
return BAD_FUNC_ARG;
@@ -14055,6 +14100,7 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
if (ret == 0) {
i = 0;
/* decrement until before the padding */
/* TODO: verify padding is 0, abort with illegal_parameter */
while (ech->innerClientHello[ech->innerClientHelloLen +
HANDSHAKE_HEADER_SZ - i - 1] != ECH_TYPE_INNER) {
i++;
@@ -14090,6 +14136,8 @@ static void TLSX_ECH_Free(WOLFSSL_ECH* ech, void* heap)
XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (ech->hpkeContext != NULL)
XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (ech->privateName != NULL)
XFREE((char*)ech->privateName, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER);
(void)heap;
@@ -15868,7 +15916,7 @@ int TLSX_GetRequestSize(WOLFSSL* ssl, byte msgType, word32* pLength)
}
#endif
#if defined(HAVE_ECH)
if (ssl->options.useEch == 1 && !ssl->options.disableECH
if (ssl->echConfigs != NULL && !ssl->options.disableECH
&& msgType == client_hello) {
ret = TLSX_GetSizeWithEch(ssl, semaphore, msgType, &length);
if (ret != 0)
@@ -16053,7 +16101,7 @@ int TLSX_WriteRequest(WOLFSSL* ssl, byte* output, byte msgType, word32* pOffset)
#endif
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
if (ssl->options.useEch == 1 && !ssl->options.disableECH
if (ssl->echConfigs != NULL && !ssl->options.disableECH
&& msgType == client_hello) {
ret = TLSX_WriteWithEch(ssl, output, semaphore,
msgType, &offset);
@@ -17332,7 +17380,8 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType,
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
/* If client used ECH, server HRR must include ECH confirmation */
if (ret == 0 && msgType == hello_retry_request && ssl->options.useEch == 1) {
if (ret == 0 && msgType == hello_retry_request && ssl->echConfigs != NULL &&
!ssl->options.disableECH) {
TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH);
if (echX == NULL || ((WOLFSSL_ECH*)echX->data)->confBuf == NULL) {
WOLFSSL_MSG("ECH used but HRR missing ECH confirmation");
+28 -11
View File
@@ -4720,7 +4720,7 @@ int SendTls13ClientHello(WOLFSSL* ssl)
/* find length of outer and inner */
#if defined(HAVE_ECH)
if (ssl->options.useEch == 1 && !ssl->options.disableECH) {
if (ssl->echConfigs != NULL && !ssl->options.disableECH) {
TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH);
if (echX == NULL)
return WOLFSSL_FATAL_ERROR;
@@ -4874,7 +4874,7 @@ int SendTls13ClientHello(WOLFSSL* ssl)
#if defined(HAVE_ECH)
/* write inner then outer */
if (ssl->options.useEch == 1 && !ssl->options.disableECH &&
if (ssl->echConfigs != NULL && !ssl->options.disableECH &&
(ssl->options.echAccepted || args->ech->innerCount == 0)) {
/* set the type to inner */
args->ech->type = ECH_TYPE_INNER;
@@ -4939,7 +4939,7 @@ int SendTls13ClientHello(WOLFSSL* ssl)
#if defined(HAVE_ECH)
/* encrypt and pack the ech innerClientHello */
if (ssl->options.useEch == 1 && !ssl->options.disableECH &&
if (ssl->echConfigs != NULL && !ssl->options.disableECH &&
(ssl->options.echAccepted || args->ech->innerCount == 0)) {
ret = TLSX_FinalizeEch(args->ech,
args->output + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ,
@@ -4970,7 +4970,7 @@ int SendTls13ClientHello(WOLFSSL* ssl)
{
#if defined(HAVE_ECH)
/* compute the inner hash */
if (ssl->options.useEch == 1 && !ssl->options.disableECH &&
if (ssl->echConfigs != NULL && !ssl->options.disableECH &&
(ssl->options.echAccepted || args->ech->innerCount == 0)) {
ret = EchHashHelloInner(ssl, args->ech);
}
@@ -5664,7 +5664,7 @@ int DoTls13ServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
#if defined(HAVE_ECH)
/* check for acceptConfirmation, must be done after hashes restart */
if (ssl->options.useEch == 1) {
if (ssl->echConfigs != NULL && !ssl->options.disableECH) {
args->echX = TLSX_Find(ssl->extensions, TLSX_ECH);
/* account for hrr extension instead of server random */
if (args->extMsgType == hello_retry_request) {
@@ -7165,9 +7165,19 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
}
#if defined(HAVE_ECH)
/* jump to the end to clean things up */
if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE)
goto exit_dch;
if (echX != NULL && ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE) {
if (((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) {
/* Client sent real ECH and inner hello was decrypted, jump to
* exit so the caller can re-invoke with the inner hello */
goto exit_dch;
}
else {
/* Server has ECH but client did not send ECH. Clear the
* response flag so the empty ECH extension is not written
* in EncryptedExtensions. */
echX->resp = 0;
}
}
#endif
#ifdef HAVE_SNI
@@ -7244,7 +7254,8 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
#if defined(HAVE_ECH)
/* hash clientHelloInner to hsHashesEch */
if (echX != NULL && ssl->ctx->echConfigs != NULL &&
!ssl->options.disableECH) {
!ssl->options.disableECH &&
((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) {
ret = EchHashHelloInner(ssl, (WOLFSSL_ECH*)echX->data);
if (ret != 0)
goto exit_dch;
@@ -7503,7 +7514,8 @@ exit_dch:
#if defined(HAVE_ECH)
if (ret == 0 && echX != NULL &&
((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE) {
((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE &&
((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) {
/* add the header to the inner hello */
AddTls13HandShakeHeader(((WOLFSSL_ECH*)echX->data)->innerClientHello,
@@ -13072,16 +13084,21 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
echX = TLSX_Find(ssl->extensions, TLSX_ECH);
if (echX != NULL &&
((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE) {
((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE &&
((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) {
byte copyRandom = ((WOLFSSL_ECH*)echX->data)->innerCount == 0;
/* reset the inOutIdx to the outer start */
*inOutIdx = echInOutIdx;
/* call again with the inner hello */
if (ret == 0) {
((WOLFSSL_ECH*)echX->data)->sniState = ECH_INNER_SNI;
ret = DoTls13ClientHello(ssl,
((WOLFSSL_ECH*)echX->data)->innerClientHello,
&echInOutIdx,
((WOLFSSL_ECH*)echX->data)->innerClientHelloLen);
((WOLFSSL_ECH*)echX->data)->sniState = ECH_SNI_DONE;
}
/* if the inner ech parsed successfully we have successfully
* handled the hello and can skip the whole message */
+708 -62
View File
@@ -13790,6 +13790,7 @@ static int test_wolfSSL_CTX_add_client_CA(void)
defined(HAVE_IO_TESTS_DEPENDENCIES)
static THREAD_RETURN WOLFSSL_THREAD server_task_ech(void* args)
{
EXPECT_DECLS;
callback_functions* callbacks = ((func_args*)args)->callbacks;
WOLFSSL_CTX* ctx = callbacks->ctx;
WOLFSSL* ssl = NULL;
@@ -13814,15 +13815,17 @@ static THREAD_RETURN WOLFSSL_THREAD server_task_ech(void* args)
AssertIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
WOLFSSL_FILETYPE_PEM));
WOLFSSL_FILETYPE_PEM));
if (callbacks->ctx_ready)
callbacks->ctx_ready(ctx);
ssl = wolfSSL_new(ctx);
ExpectNotNull(ssl = wolfSSL_new(ctx));
/* set the sni for the server */
wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, privateName, privateNameLen);
AssertIntEQ(WOLFSSL_SUCCESS,
wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, privateName,
privateNameLen));
tcp_accept(&sfd, &cfd, (func_args*)args, port, 0, 0, 0, 0, 1, NULL, NULL);
CloseSocket(sfd);
@@ -13841,12 +13844,13 @@ static THREAD_RETURN WOLFSSL_THREAD server_task_ech(void* args)
if (ret != WOLFSSL_SUCCESS) {
char buff[WOLFSSL_MAX_ERROR_SZ];
fprintf(stderr, "error = %d, %s\n", err, wolfSSL_ERR_error_string(err, buff));
fprintf(stderr, "error = %d, %s\n", err, wolfSSL_ERR_error_string(err,
buff));
}
else {
if (0 < (idx = wolfSSL_read(ssl, input, sizeof(input)-1))) {
input[idx] = 0;
fprintf(stderr, "Client message: %s\n", input);
fprintf(stderr, "Client message: %s\n", input);
}
AssertIntEQ(privateNameLen, wolfSSL_write(ssl, privateName,
@@ -14058,20 +14062,27 @@ static int test_wolfSSL_Tls13_Key_Logging_test(void)
#endif /* OPENSSL_EXTRA && HAVE_SECRET_CALLBACK && WOLFSSL_TLS13 */
return EXPECT_RESULT();
}
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \
defined(HAVE_IO_TESTS_DEPENDENCIES)
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
#if defined(HAVE_IO_TESTS_DEPENDENCIES)
static int test_wolfSSL_Tls13_ECH_params(void)
{
EXPECT_DECLS;
#if !defined(NO_WOLFSSL_CLIENT)
word32 outputLen = 0;
byte testBuf[72];
WOLFSSL_CTX *ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
WOLFSSL *ssl = wolfSSL_new(ctx);
byte testBuf[256];
/* base64 ech configs from cloudflare-ech.com */
const char* b64Configs =
"AEX+DQBBFAAgACBuAoQI8+liEVYQbXKBDeVgTmF2rfXuKO2knhwrN7jgTgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA=";
word32 outputLen = sizeof(testBuf);
word16 tmpLen;
WOLFSSL_CTX* ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
WOLFSSL* ssl = wolfSSL_new(ctx);
ExpectNotNull(ctx);
ExpectNotNull(ssl);
/* CTX NULL errors */
/* invalid ctx */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GenerateEchConfig(NULL,
"ech-public-name.com", 0, 0, 0));
@@ -14080,57 +14091,142 @@ static int test_wolfSSL_Tls13_ECH_params(void)
0, 0));
/* invalid algorithms */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GenerateEchConfig(ctx,
"ech-public-name.com", 1000, 1000, 1000));
"ech-public-name.com", 1000, 0, 0));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GenerateEchConfig(ctx,
"ech-public-name.com", 0, 1000, 0));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GenerateEchConfig(ctx,
"ech-public-name.com", 0, 0, 1000));
/* invalid ctx */
/* invalid base64 configs: NULL ctx, NULL configs, 0 length */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigsBase64(NULL,
(char*)testBuf, sizeof(testBuf)));
/* invalid base64 configs */
b64Configs, (word32)XSTRLEN(b64Configs)));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigsBase64(ctx,
NULL, sizeof(testBuf)));
/* invalid length */
NULL, (word32)XSTRLEN(b64Configs)));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigsBase64(ctx,
(char*)testBuf, 0));
b64Configs, 0));
/* invalid ctx */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(NULL,
testBuf, sizeof(testBuf)));
/* invalid configs */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(ctx,
NULL, sizeof(testBuf)));
/* invalid length */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(ctx,
testBuf, 0));
/* invalid ctx */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GetEchConfigs(NULL, NULL,
&outputLen));
/* invalid output len */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GetEchConfigs(ctx, NULL, NULL));
/* invalid ssl */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(NULL,
(char*)testBuf, sizeof(testBuf)));
/* invalid configs64 */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(ssl, NULL,
/* invalid configs: NULL ctx, NULL configs, 0 length */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(NULL, testBuf,
sizeof(testBuf)));
/* invalid size */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(ssl,
(char*)testBuf, 0));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(ctx, NULL,
sizeof(testBuf)));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(ctx, testBuf, 0));
/* invalid ssl */
/* SSL NULL errors */
/* invalid base64 configs: NULL ssl, NULL configs, 0 length */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(NULL, b64Configs,
(word32)XSTRLEN(b64Configs)));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(ssl, NULL,
(word32)XSTRLEN(b64Configs)));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(ssl, b64Configs,
0));
/* invalid configs: NULL ssl, NULL configs, 0 length */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigs(NULL, testBuf,
sizeof(testBuf)));
/* invalid configs */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigs(ssl, NULL,
sizeof(testBuf)));
/* invalid size */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigs(ssl, testBuf, 0));
/* invalid ssl */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_GetEchConfigs(NULL, NULL, &outputLen));
/* invalid size */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_GetEchConfigs(ssl, NULL, NULL));
/* stateful errors */
/* actually generate configs */
ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_GenerateEchConfig(ctx,
"ech-public-name.com", 0, 0, 0));
/* bad get: NULL ctx, NULL output len, short output len */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GetEchConfigs(NULL, testBuf,
&outputLen));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GetEchConfigs(ctx, testBuf, NULL));
outputLen = 5;
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GetEchConfigs(ctx, testBuf,
&outputLen));
/* should be able to retrieve length with NULL buffer... */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_GetEchConfigs(ctx, NULL,
&outputLen));
ExpectIntGE(sizeof(testBuf), outputLen);
/* and the get should work with this length */
ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_GetEchConfigs(ctx, testBuf,
&outputLen));
/* reject config with invalid total length */
if (EXPECT_SUCCESS()) {
ato16(testBuf, &tmpLen);
testBuf[0] = 0xFF;
testBuf[1] = 0xFF;
}
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(ctx, testBuf,
outputLen));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigs(ssl, testBuf,
outputLen));
if (EXPECT_SUCCESS()) {
c16toa(tmpLen, testBuf);
}
/* reject config with invalid version */
if (EXPECT_SUCCESS()) {
testBuf[2] ^= 0x01;
}
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(ctx, testBuf,
outputLen));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigs(ssl, testBuf,
outputLen));
if (EXPECT_SUCCESS()) {
testBuf[2] ^= 0x01;
}
/* reject config with bad length */
if (EXPECT_SUCCESS()) {
ato16(testBuf + 4, &tmpLen);
testBuf[4] = 0xFF;
testBuf[5] = 0xFF;
}
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(ctx, testBuf,
outputLen));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigs(ssl, testBuf,
outputLen));
if (EXPECT_SUCCESS()) {
c16toa(tmpLen, testBuf + 4);
}
/* set valid configs */
ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigs(ctx, testBuf,
outputLen));
ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigs(ssl, testBuf,
outputLen));
/* NULL ssl, NULL buffer, NULL output len */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_GetEchConfigs(NULL, testBuf,
&outputLen));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_GetEchConfigs(ssl, NULL, &outputLen));
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_GetEchConfigs(ssl, testBuf, NULL));
/* reject setting configs when ssl already has them */
ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigs(ssl, testBuf,
outputLen));
/* unable to get configs from ssl with no configs (because of disable) */
wolfSSL_SetEchEnable(ssl, 0);
outputLen = sizeof(testBuf);
ExpectIntNE(WOLFSSL_SUCCESS,
wolfSSL_GetEchConfigs(ssl, testBuf, &outputLen));
/* base64 tests */
/* set base64 configs */
ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigsBase64(ctx,
b64Configs, (word32)XSTRLEN(b64Configs)));
ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(ssl,
b64Configs, (word32)XSTRLEN(b64Configs)));
/* disable and check ctx has no configs as well */
wolfSSL_CTX_SetEchEnable(ctx, 0);
outputLen = sizeof(testBuf);
ExpectIntNE(WOLFSSL_SUCCESS,
wolfSSL_CTX_GetEchConfigs(ctx, testBuf, &outputLen));
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
@@ -14139,7 +14235,8 @@ static int test_wolfSSL_Tls13_ECH_params(void)
return EXPECT_RESULT();
}
static int test_wolfSSL_Tls13_ECH_ex(int hrr)
static int test_wolfSSL_ECH_conn_ex(method_provider serverMeth,
method_provider clientMeth, int hrr)
{
EXPECT_DECLS;
tcp_ready ready;
@@ -14166,11 +14263,10 @@ static int test_wolfSSL_Tls13_ECH_ex(int hrr)
XMEMSET(&server_args, 0, sizeof(func_args));
XMEMSET(&server_cbf, 0, sizeof(callback_functions));
XMEMSET(&client_cbf, 0, sizeof(callback_functions));
server_cbf.method = wolfTLSv1_3_server_method; /* TLS1.3 */
server_cbf.method = serverMeth;
/* create the server context here so we can get the ech config */
ExpectNotNull(server_cbf.ctx =
wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
ExpectNotNull(server_cbf.ctx = wolfSSL_CTX_new(serverMeth()));
/* generate ech config */
ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_GenerateEchConfig(server_cbf.ctx,
@@ -14188,10 +14284,10 @@ static int test_wolfSSL_Tls13_ECH_ex(int hrr)
start_thread(server_task_ech, &server_args, &serverThread);
wait_tcp_ready(&server_args);
/* run as a TLS1.3 client */
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
/* set the client TLS version and run */
ExpectNotNull(ctx = wolfSSL_CTX_new(clientMeth()));
ExpectIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_load_verify_locations(ctx, caCertFile, 0));
wolfSSL_CTX_load_verify_locations(ctx, caCertFile, 0));
ExpectIntEQ(WOLFSSL_SUCCESS,
wolfSSL_CTX_use_certificate_file(ctx, cliCertFile, SSL_FILETYPE_PEM));
ExpectIntEQ(WOLFSSL_SUCCESS,
@@ -14221,8 +14317,8 @@ static int test_wolfSSL_Tls13_ECH_ex(int hrr)
ExpectIntEQ(wolfSSL_write(ssl, privateName, privateNameLen),
privateNameLen);
ExpectIntGT((replyLen = wolfSSL_read(ssl, reply, sizeof(reply))), 0);
/* add th null terminator for string compare */
reply[replyLen] = 0;
/* add the null terminator for string compare */
reply[replyLen] = '\0';
/* check that the server replied with the private name */
ExpectStrEQ(privateName, reply);
wolfSSL_free(ssl);
@@ -14239,13 +14335,553 @@ static int test_wolfSSL_Tls13_ECH_ex(int hrr)
static int test_wolfSSL_Tls13_ECH(void)
{
return test_wolfSSL_Tls13_ECH_ex(0);
return test_wolfSSL_ECH_conn_ex(wolfTLSv1_3_server_method,
wolfTLSv1_3_client_method, 0);
}
static int test_wolfSSL_Tls13_ECH_HRR(void)
{
return test_wolfSSL_Tls13_ECH_ex(1);
return test_wolfSSL_ECH_conn_ex(wolfTLSv1_3_server_method,
wolfTLSv1_3_client_method, 1);
}
static int test_wolfSSL_SubTls13_ECH(void)
{
EXPECT_DECLS;
#ifndef WOLFSSL_NO_TLS12
ExpectIntNE(test_wolfSSL_ECH_conn_ex(wolfTLSv1_3_server_method,
wolfTLSv1_2_client_method, 0), WOLFSSL_SUCCESS);
ExpectIntNE(test_wolfSSL_ECH_conn_ex(wolfTLSv1_2_server_method,
wolfTLSv1_3_client_method, 0), WOLFSSL_SUCCESS);
ExpectIntNE(test_wolfSSL_ECH_conn_ex(wolfSSLv23_server_method,
wolfTLSv1_2_client_method, 0), WOLFSSL_SUCCESS);
#endif
return EXPECT_RESULT();
}
#endif /* HAVE_IO_TESTS_DEPENDENCIES */
#ifdef HAVE_SSL_MEMIO_TESTS_DEPENDENCIES
/* Static storage for passing ECH config between server and client callbacks */
static byte echCbTestConfigs[512];
static word32 echCbTestConfigsLen;
static const char* echCbTestPublicName = "ech-public-name.com";
static const char* echCbTestPrivateName = "ech-private-name.com";
/* the arg is whether the client has ech enabled or not */
static int test_ech_server_sni_callback(WOLFSSL* ssl, int* ad, void* arg)
{
const char* name;
if (!wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, (void**)&name)) {
*ad = WOLFSSL_AD_UNRECOGNIZED_NAME;
return fatal_return;
}
/* reached by *_disable_conn test: expect name to be the public SNI when
* client has ECH enabled, otherwise it should be the private SNI */
if (arg != NULL && *(int*)arg == 1 &&
XSTRCMP(name, echCbTestPublicName) == 0) {
return 0;
}
else if (XSTRCMP(name, echCbTestPrivateName) == 0) {
return 0;
}
else {
*ad = WOLFSSL_AD_UNRECOGNIZED_NAME;
return fatal_return;
}
}
/* Server ctx_ready callback: generate ECH config */
static int test_ech_server_ctx_ready(WOLFSSL_CTX* ctx)
{
int ret;
ret = wolfSSL_CTX_GenerateEchConfig(ctx, echCbTestPublicName, 0, 0, 0);
if (ret != WOLFSSL_SUCCESS)
return TEST_FAIL;
echCbTestConfigsLen = sizeof(echCbTestConfigs);
ret = wolfSSL_CTX_GetEchConfigs(ctx, echCbTestConfigs,
&echCbTestConfigsLen);
if (ret != WOLFSSL_SUCCESS)
return TEST_FAIL;
return TEST_SUCCESS;
}
/* Server ssl_ready callback: set SNI */
static int test_ech_server_ssl_ready(WOLFSSL* ssl)
{
int ret;
ret = wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, echCbTestPrivateName,
(word16)XSTRLEN(echCbTestPrivateName));
if (ret != WOLFSSL_SUCCESS)
return TEST_FAIL;
return TEST_SUCCESS;
}
/* Client ssl_ready callback: set ECH configs and SNI */
static int test_ech_client_ssl_ready(WOLFSSL* ssl)
{
int ret;
ret = wolfSSL_SetEchConfigs(ssl, echCbTestConfigs, echCbTestConfigsLen);
if (ret != WOLFSSL_SUCCESS)
return TEST_FAIL;
ret = wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, echCbTestPrivateName,
(word16)XSTRLEN(echCbTestPrivateName));
if (ret != WOLFSSL_SUCCESS)
return TEST_FAIL;
return TEST_SUCCESS;
}
/* Test ECH when no private SNI is set */
static int test_wolfSSL_Tls13_ECH_no_private_name(void)
{
EXPECT_DECLS;
struct test_ssl_memio_ctx test_ctx;
/* client sends private SNI, server does not have one set */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready;
test_ctx.c_cb.ssl_ready = test_ech_client_ssl_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 1);
test_ssl_memio_cleanup(&test_ctx);
/* client does not send private SNI, server has one set */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready;
test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echCbTestConfigs,
echCbTestConfigsLen), WOLFSSL_SUCCESS);
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
test_ssl_memio_cleanup(&test_ctx);
/* client does not send private SNI, server does not have one set */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echCbTestConfigs,
echCbTestConfigsLen), WOLFSSL_SUCCESS);
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
test_ssl_memio_cleanup(&test_ctx);
return EXPECT_RESULT();
}
/* Test ECH rejection when configs don't match */
static int test_wolfSSL_Tls13_ECH_bad_configs_ex(int hrr, int sniCb)
{
EXPECT_DECLS;
struct test_ssl_memio_ctx test_ctx;
WOLFSSL_CTX* tempCtx = NULL;
const char* badPrivateName = "ech-bad-private-name.com";
byte badPublicConfig[128];
word32 badPublicConfigLen = sizeof(badPublicConfig);
/* verify with bad public SNI / config */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
/* server generates its own ECH config */
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready;
test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
/* generate throwaway ECH config for client to use */
ExpectNotNull(tempCtx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(tempCtx, echCbTestPublicName,
0, 0, 0), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(tempCtx, badPublicConfig,
&badPublicConfigLen), WOLFSSL_SUCCESS);
wolfSSL_CTX_free(tempCtx);
tempCtx = NULL;
/* set bad public config on client */
ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, badPublicConfig,
badPublicConfigLen), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME,
echCbTestPrivateName, (word16)XSTRLEN(echCbTestPrivateName)),
WOLFSSL_SUCCESS);
if (hrr) {
ExpectIntEQ(wolfSSL_NoKeyShares(test_ctx.c_ssl), WOLFSSL_SUCCESS);
}
if (sniCb) {
wolfSSL_CTX_set_servername_callback(test_ctx.s_ctx,
test_ech_server_sni_callback);
}
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
test_ssl_memio_cleanup(&test_ctx);
/* verify with bad private SNI */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready;
test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
/* set bad private SNI on client */
ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echCbTestConfigs,
echCbTestConfigsLen), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME,
badPrivateName, (word16)XSTRLEN(badPrivateName)), WOLFSSL_SUCCESS);
if (hrr) {
ExpectIntEQ(wolfSSL_NoKeyShares(test_ctx.c_ssl), WOLFSSL_SUCCESS);
}
if (sniCb) {
wolfSSL_CTX_set_servername_callback(test_ctx.s_ctx,
test_ech_server_sni_callback);
}
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
test_ssl_memio_cleanup(&test_ctx);
return EXPECT_RESULT();
}
static int test_wolfSSL_Tls13_ECH_bad_configs(void)
{
EXPECT_DECLS;
ExpectIntEQ(test_wolfSSL_Tls13_ECH_bad_configs_ex(0, 0), WOLFSSL_SUCCESS);
ExpectIntEQ(test_wolfSSL_Tls13_ECH_bad_configs_ex(0, 1), WOLFSSL_SUCCESS);
ExpectIntEQ(test_wolfSSL_Tls13_ECH_bad_configs_ex(1, 0), WOLFSSL_SUCCESS);
ExpectIntEQ(test_wolfSSL_Tls13_ECH_bad_configs_ex(1, 1), WOLFSSL_SUCCESS);
return EXPECT_RESULT();
}
/* Test that client info can be successfully decoded from one of multiple server
* ECH configs
* In this case the server is expected to try it's first config, fail, then try
* its second config and succeed */
static int test_wolfSSL_Tls13_ECH_new_config(void)
{
EXPECT_DECLS;
test_ssl_memio_ctx test_ctx;
byte altConfig[512];
word32 altConfigLen = sizeof(altConfig);
byte combinedConfigs[512];
word32 combinedConfigsLen = sizeof(combinedConfigs);
word16 firstConfigLen;
word16 secondConfigOffset;
word16 secondConfigLen;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
/* server generates its own ECH config */
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready;
test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
/* generate a second ECH config for the server */
ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(test_ctx.s_ctx,
echCbTestPrivateName, 0, 0, 0), WOLFSSL_SUCCESS);
ExpectNotNull(test_ctx.s_ctx->echConfigs->next);
/* capture the second ECH config in the list for the client to use */
ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(test_ctx.s_ctx, combinedConfigs,
&combinedConfigsLen), WOLFSSL_SUCCESS);
/* ECHConfigList: [2 byte list len] [ECHConfig]...
* ECHConfig: [2 byte version] [2 byte config len] [config data] */
ExpectIntGE(combinedConfigsLen, OPAQUE16_LEN * 3);
if (EXPECT_SUCCESS()) {
ato16(combinedConfigs + OPAQUE16_LEN + OPAQUE16_LEN, &firstConfigLen);
secondConfigOffset = OPAQUE16_LEN + OPAQUE16_LEN + OPAQUE16_LEN +
firstConfigLen;
ExpectIntGE(combinedConfigsLen,
secondConfigOffset + OPAQUE16_LEN + OPAQUE16_LEN);
}
if (EXPECT_SUCCESS()) {
ato16(combinedConfigs + secondConfigOffset + OPAQUE16_LEN,
&secondConfigLen);
secondConfigLen += OPAQUE16_LEN + OPAQUE16_LEN;
ExpectIntGE(combinedConfigsLen, secondConfigOffset + secondConfigLen);
}
if (EXPECT_SUCCESS()) {
/* build the ECHConfigList */
c16toa(secondConfigLen, altConfig);
ExpectIntLE(OPAQUE16_LEN + secondConfigLen, (word16)sizeof(altConfig));
if (EXPECT_SUCCESS()) {
XMEMCPY(altConfig + OPAQUE16_LEN,
combinedConfigs + secondConfigOffset, secondConfigLen);
altConfigLen = OPAQUE16_LEN + secondConfigLen;
}
}
/* Set client configs - server should try both and succeed with second
* Or seek the correct one immediately through the configId */
ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, altConfig, altConfigLen),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME,
echCbTestPrivateName, (word16)XSTRLEN(echCbTestPrivateName)),
WOLFSSL_SUCCESS);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 1);
test_ssl_memio_cleanup(&test_ctx);
return EXPECT_RESULT();
}
/* Test GREASE ECH:
* 1. client sends GREASE ECH extension but server has no ECH configs so it
* ignores it, handshake succeeds normally, no ECH configs received
* 2. client sends GREASE ECH extensions and server has ECH configs, handshake
* succeeds and client receives ECH configs */
static int test_wolfSSL_Tls13_ECH_GREASE(void)
{
EXPECT_DECLS;
test_ssl_memio_ctx test_ctx;
byte greaseConfigs[512];
word32 greaseConfigsLen = sizeof(greaseConfigs);
/* GREASE when server has no ECH configs */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME,
echCbTestPrivateName, (word16)XSTRLEN(echCbTestPrivateName)),
WOLFSSL_SUCCESS);
/* verify ECH is enabled on the client and server */
ExpectIntEQ(test_ctx.s_ssl->options.disableECH, 0);
ExpectIntEQ(test_ctx.c_ssl->options.disableECH, 0);
/* verify no ECH configs are set */
ExpectNull(test_ctx.s_ctx->echConfigs);
ExpectNull(test_ctx.c_ctx->echConfigs);
/* handshake should succeed - server ignores the GREASE ECH extension */
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
/* ECH should NOT be accepted since this was GREASE */
ExpectIntEQ(test_ctx.s_ssl->options.echAccepted, 0);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntNE(wolfSSL_GetEchConfigs(test_ctx.c_ssl, greaseConfigs,
&greaseConfigsLen), WOLFSSL_SUCCESS);
test_ssl_memio_cleanup(&test_ctx);
/* GREASE when server has ECH configs */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
/* generate ECH configs */
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready;
test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME,
echCbTestPrivateName, (word16)XSTRLEN(echCbTestPrivateName)),
WOLFSSL_SUCCESS);
/* verify ECH is enabled on the client and server */
ExpectIntEQ(test_ctx.s_ssl->options.disableECH, 0);
ExpectIntEQ(test_ctx.c_ssl->options.disableECH, 0);
/* verify ECH configs are set on server */
ExpectNotNull(test_ctx.s_ctx->echConfigs);
ExpectNull(test_ctx.c_ctx->echConfigs);
/* handshake should succeed - server responds to the GREASE ECH extension */
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
/* ECH should NOT be accepted since this was GREASE
* However, configs will be present this time */
ExpectIntEQ(test_ctx.s_ssl->options.echAccepted, 0);
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
ExpectIntEQ(wolfSSL_GetEchConfigs(test_ctx.c_ssl, greaseConfigs,
&greaseConfigsLen), WOLFSSL_SUCCESS);
test_ssl_memio_cleanup(&test_ctx);
return EXPECT_RESULT();
}
static int test_wolfSSL_Tls13_ECH_disable_conn_ex(int enableServer,
int enableClient)
{
EXPECT_DECLS;
test_ssl_memio_ctx test_ctx;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.s_cb.method = wolfTLSv1_3_server_method;
test_ctx.c_cb.method = wolfTLSv1_3_client_method;
/* both server and client will be setup to use ECH */
test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready;
test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready;
test_ctx.c_cb.ssl_ready = test_ech_client_ssl_ready;
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
/* this callback will ensure that the correct SNI is being held */
wolfSSL_CTX_set_servername_callback(test_ctx.s_ctx,
test_ech_server_sni_callback);
ExpectIntEQ(wolfSSL_CTX_set_servername_arg(test_ctx.s_ctx, &enableClient),
WOLFSSL_SUCCESS);
/* disable ECH on the appropriate side(s) */
wolfSSL_SetEchEnable(test_ctx.s_ssl, enableServer);
wolfSSL_SetEchEnable(test_ctx.c_ssl, enableClient);
if (!enableClient) {
/* client ECH disabled: no ECH extension sent, handshake succeeds
* normally but ECH is not accepted */
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_SUCCESS);
}
else if (!enableServer) {
/* client sends ECH but server can't process it: server has no ECH
* keys so it processes the outer ClientHello, client detects ECH
* rejection and aborts the handshake */
ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_SUCCESS);
}
ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 0);
test_ssl_memio_cleanup(&test_ctx);
return EXPECT_RESULT();
}
/* setup a server and client with ECH then disable on one, the other, or both.
* Verifies that disabling ECH prevents ECH from being used and that the
* public/private SNI's are verified correctly */
static int test_wolfSSL_Tls13_ECH_disable_conn(void)
{
EXPECT_DECLS;
ExpectIntEQ(test_wolfSSL_Tls13_ECH_disable_conn_ex(0, 1), TEST_SUCCESS);
ExpectIntEQ(test_wolfSSL_Tls13_ECH_disable_conn_ex(1, 0), TEST_SUCCESS);
ExpectIntEQ(test_wolfSSL_Tls13_ECH_disable_conn_ex(0, 0), TEST_SUCCESS);
return EXPECT_RESULT();
}
#endif /* HAVE_SSL_MEMIO_TESTS_DEPENDENCIES */
/* verify that ECH can be enabled/disabled without issue */
static int test_wolfSSL_Tls13_ECH_enable_disable(void)
{
EXPECT_DECLS;
#if !defined(NO_WOLFSSL_CLIENT)
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
byte echConfigs[128];
word32 echConfigsLen = sizeof(echConfigs);
/* NULL ctx, NULL ssl should not crash */
wolfSSL_SetEchEnable(ssl, 0);
wolfSSL_CTX_SetEchEnable(ctx, 0);
/* test CTX level enable/disable */
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(ctx, "public.com", 0, 0, 0),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(ctx, echConfigs, &echConfigsLen),
WOLFSSL_SUCCESS);
/* disable ECH at CTX level */
wolfSSL_CTX_SetEchEnable(ctx, 0);
ExpectIntEQ(ctx->disableECH, 1);
ExpectNull(ctx->echConfigs);
wolfSSL_CTX_SetEchEnable(ctx, 1);
ExpectIntEQ(ctx->disableECH, 0);
/* test SSL level enable/disable */
ExpectNotNull(ssl = wolfSSL_new(ctx));
ExpectIntEQ(wolfSSL_SetEchConfigs(ssl, echConfigs, echConfigsLen),
WOLFSSL_SUCCESS);
/* disable ECH at SSL level */
wolfSSL_SetEchEnable(ssl, 0);
ExpectIntEQ(ssl->options.disableECH, 1);
ExpectNull(ssl->echConfigs);
wolfSSL_SetEchEnable(ssl, 1);
ExpectIntEQ(ssl->options.disableECH, 0);
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
#endif /* !NO_WOLFSSL_CLIENT */
return EXPECT_RESULT();
}
#endif /* HAVE_ECH && WOLFSSL_TLS13 */
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && \
@@ -33498,13 +34134,23 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wolfSSL_SCR_check_enabled),
TEST_DECL(test_tls_ext_duplicate),
TEST_DECL(test_tls_bad_legacy_version),
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \
defined(HAVE_IO_TESTS_DEPENDENCIES)
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
#if defined(HAVE_IO_TESTS_DEPENDENCIES)
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),
TEST_DECL(test_wolfSSL_SubTls13_ECH),
#endif
#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES)
TEST_DECL(test_wolfSSL_Tls13_ECH_no_private_name),
TEST_DECL(test_wolfSSL_Tls13_ECH_bad_configs),
TEST_DECL(test_wolfSSL_Tls13_ECH_new_config),
TEST_DECL(test_wolfSSL_Tls13_ECH_GREASE),
TEST_DECL(test_wolfSSL_Tls13_ECH_disable_conn),
#endif
TEST_DECL(test_wolfSSL_Tls13_ECH_enable_disable),
#endif /* WOLFSSL_TLS13 && HAVE_ECH */
TEST_DECL(test_wolfSSL_X509_TLS_version_test_1),
TEST_DECL(test_wolfSSL_X509_TLS_version_test_2),
+9 -1
View File
@@ -3100,6 +3100,13 @@ typedef enum {
ECH_PARSED_INTERNAL,
} EchState;
typedef enum {
ECH_OUTER_SNI,
ECH_INNER_SNI,
ECH_INNER_SNI_ATTEMPT,
ECH_SNI_DONE,
} EchStateSNI;
typedef struct EchCipherSuite {
word16 kdfId;
word16 aeadId;
@@ -3122,6 +3129,7 @@ typedef struct WOLFSSL_ECH {
Hpke* hpke;
HpkeBaseContext* hpkeContext;
const byte* aad;
const char* privateName;
void* ephemeralKey;
WOLFSSL_EchConfig* echConfig;
byte* innerClientHello;
@@ -3134,6 +3142,7 @@ typedef struct WOLFSSL_ECH {
word16 kemId;
word16 encLen;
EchState state;
EchStateSNI sniState;
byte type;
byte configId;
byte enc[HPKE_Npk_MAX];
@@ -5156,7 +5165,6 @@ struct Options {
word16 useDtlsCID:1;
#endif /* WOLFSSL_DTLS_CID */
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
word16 useEch:1;
word16 echAccepted:1;
byte disableECH:1; /* Did the user disable ech */
#endif
+2 -2
View File
@@ -1230,8 +1230,8 @@ WOLFSSL_API int wolfSSL_CTX_GetEchConfigs(WOLFSSL_CTX* ctx, byte* output,
WOLFSSL_API void wolfSSL_CTX_SetEchEnable(WOLFSSL_CTX* ctx, byte enable);
WOLFSSL_API int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl, char* echConfigs64,
word32 echConfigs64Len);
WOLFSSL_API int wolfSSL_SetEchConfigsBase64(WOLFSSL* ssl,
const char* echConfigs64, word32 echConfigs64Len);
WOLFSSL_API int wolfSSL_SetEchConfigs(WOLFSSL* ssl, const byte* echConfigs,
word32 echConfigsLen);