Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/scripts/openssl-ech.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ cleanup() {
trap cleanup EXIT

usage() {
echo "Usage: $0 <client|server> [--suite <KEM,KDF,AEAD>] [--workspace <path>]"
echo "Usage: $0 <client|server> [--suite <KEM,KDF,AEAD>] [--pqc <group>] [--workspace <path>]"
exit 1
}

MODE=""
SUITE=""
PQC=""

WORKSPACE=${GITHUB_WORKSPACE:-"."}

Expand All @@ -40,6 +41,11 @@ while [ $# -gt 0 ]; do
echo "Using suite: $SUITE"
echo ""
;;
--pqc)
[ -z "$2" ] && { echo "ERROR: --pqc requires a value"; exit 1; }
PQC="$2"
shift 2
;;
--workspace)
[ -z "$2" ] && { echo "ERROR: --workspace requires a value"; exit 1; }
WORKSPACE="$2"
Expand Down Expand Up @@ -104,6 +110,7 @@ openssl_server(){
-p "$port" \
-S "$PRIV_NAME" \
--ech "$ech_config" \
$PQC \
&>> "$TMP_LOG"

rm -f "$ech_file"
Expand Down Expand Up @@ -178,6 +185,9 @@ case "$MODE" in
if [ -n "$SUITE" ]; then
SUITE="-suite $SUITE"
fi
if [ -n "$PQC" ]; then
PQC="--pqc $PQC"
fi
openssl_server
;;
client)
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/openssl-ech.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ jobs:
with:
path: wolfssl
configure: >-
--enable-ech --enable-sha512 --enable-aes CFLAGS='-DUSE_FLAT_TEST_H'
--enable-ech --enable-sha512 --enable-aes --enable-mlkem
CFLAGS='-DUSE_FLAT_TEST_H'
install: true

- name: tar build-dir
Expand Down Expand Up @@ -140,14 +141,14 @@ jobs:

# default suite (DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, HPKE_AES_128_GCM)
echo -e "\nTesting default suite with OpenSSL server and wolfSSL client\n" &>> "$LOG_FILE"
bash ./openssl-ech.sh server &>> "$LOG_FILE"
bash ./openssl-ech.sh server --pqc SecP384r1MLKEM1024 &>> "$LOG_FILE"

echo -e "\nTesting default suite with OpenSSL client and wolfSSL server\n" &>> "$LOG_FILE"
bash ./openssl-ech.sh client &>> "$LOG_FILE"

# weird suite (DHKEM_P521_HKDF_SHA512, HKDF_SHA256, HPKE_AES_256_GCM)
echo -e "\nTesting weird suite with OpenSSL server and wolfSSL client\n" &>> "$LOG_FILE"
bash ./openssl-ech.sh server --suite "18,1,2" &>> "$LOG_FILE"
bash ./openssl-ech.sh server --suite "18,1,2" --pqc SecP384r1MLKEM1024 &>> "$LOG_FILE"

echo -e "\nTesting weird suite with OpenSSL client and wolfSSL server\n" &>> "$LOG_FILE"
bash ./openssl-ech.sh client --suite "18,1,2" &>> "$LOG_FILE"
Expand Down
1 change: 1 addition & 0 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -8020,6 +8020,7 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
ssl->options.disableECH = ctx->disableECH;
ssl->options.disableEchEncodeOE = ctx->disableEchEncodeOE;
#endif

/* default alert state (none) */
Expand Down
16 changes: 16 additions & 0 deletions src/ssl_ech.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ void wolfSSL_CTX_SetEchEnable(WOLFSSL_CTX* ctx, byte enable)
}
}

/* set/unset OuterExtensions usage on the client-side
* OuterExtensions can dramatically decrease the size of the inner hello */
void wolfSSL_CTX_SetEchEncodeOE(WOLFSSL_CTX* ctx, byte enable)
{
if (ctx != NULL)
ctx->disableEchEncodeOE = !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, const char* echConfigs64,
Expand Down Expand Up @@ -446,6 +454,14 @@ void wolfSSL_SetEchEnable(WOLFSSL* ssl, byte enable)
}
}

/* set/unset OuterExtensions usage on the client-side
* OuterExtensions can dramatically decrease the size of the inner hello */
void wolfSSL_SetEchEncodeOE(WOLFSSL* ssl, byte enable)
{
if (ssl != NULL)
ssl->options.disableEchEncodeOE = !enable;
}

int SetEchConfigsEx(WOLFSSL_EchConfig** outputConfigs, void* heap,
const byte* echConfigs, word32 echConfigsLen)
{
Expand Down
154 changes: 150 additions & 4 deletions src/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -14237,6 +14237,9 @@ static int TLSX_ECH_ExpandOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
sessionIdLen, copyLen);
}
else {
innerExtIdx = headerSz + innerExtIdx - OPAQUE16_LEN -
sessionIdLen + ssl->session->sessionIDSz;

copyLen = echOuterExtIdx - OPAQUE16_LEN - RAN_LEN - OPAQUE8_LEN -
sessionIdLen;
XMEMCPY(newInnerChRef, innerCh + OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN +
Expand All @@ -14245,7 +14248,7 @@ static int TLSX_ECH_ExpandOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,

/* update extensions length in the new ClientHello */
c16toa(innerExtLen - echOuterExtLen + (word16)extraSize,
newInnerChRef - OPAQUE16_LEN);
newInnerCh + innerExtIdx);

ret = TLSX_ECH_CopyOuterExtensions(outerCh, outerChLen, &newInnerChRef,
&newInnerChLen, numOuterRefs, outerRefTypes);
Expand Down Expand Up @@ -16287,6 +16290,100 @@ static int TLSX_EchRestoreSNI(WOLFSSL* ssl, char* serverName,
return ret;
}

/* Returns 1 if the extension may be encoded into ech_outer_extensions,
* 0 otherwise */
static int TLSX_ECH_IsEncodable(word16 type)
{
switch (type) {
case TLSX_SERVER_NAME:
case TLSX_ECH:
case TLSX_APPLICATION_LAYER_PROTOCOL:
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
case TLSX_PRE_SHARED_KEY:
#endif
#ifdef WOLFSSL_EARLY_DATA
case TLSX_EARLY_DATA:
#endif
return 0;
default:
return 1;
}
}

/* find extensions that can be encoded into ech_outer_extensions.
* If output is non-NULL, then write the encoded form.
*
* Layout of OuterExtensions (RFC 9849, S5.1):
Comment thread
sebastian-carpenter marked this conversation as resolved.
* 2-byte extension_type + 2-byte extension_data length +
* 1-byte list length + 2*count bytes of extension types
*/
static void TLSX_ECH_BuildOuterExtensions(WOLFSSL* ssl, const byte* semaphore,
byte msgType, byte* output, word16* pOffset, word16* outCount,
byte* encodeMask)
{
TLSX* list;
TLSX* extension;
byte* typesStart = NULL;
int listIdx;
word16 count = 0;
byte isRequest = (msgType == client_hello ||
msgType == certificate_request);
byte seen[SEMAPHORE_SIZE];

/* backup semaphore so it can be aliased by encodeMask */
XMEMCPY(seen, semaphore, SEMAPHORE_SIZE);

if (output != NULL && pOffset != NULL) {
typesStart = output + *pOffset
+ HELLO_EXT_TYPE_SZ + OPAQUE16_LEN + OPAQUE8_LEN;
}

for (listIdx = 0; listIdx < 2; listIdx++) {
list = (listIdx == 0) ? ssl->extensions :
(ssl->ctx != NULL ? ssl->ctx->extensions : NULL);
for (extension = list; extension != NULL; extension = extension->next) {
word16 type = (word16)extension->type;
word16 semIdx = TLSX_ToSemaphore(type);

/* OuterExtensions is <2..254>, so reference at most 127 types */
if (count >= 127) {
WOLFSSL_MSG("ECH: cannot encode more than 127 extensions");
break;
}

if (!isRequest && !extension->resp)
continue;
if (!IS_OFF(seen, semIdx))
continue;
TURN_ON(seen, semIdx);
if (type == TLSX_ECH || !TLSX_ECH_IsEncodable(type))
continue;

if (typesStart != NULL)
c16toa(type, typesStart + count * OPAQUE16_LEN);
count++;
TURN_ON(encodeMask, semIdx);
}
}

if (count > 0 && pOffset != NULL) {
word16 listLen = (word16)(OPAQUE16_LEN * count);
word16 blockSz = (word16)(HELLO_EXT_TYPE_SZ + OPAQUE16_LEN
+ OPAQUE8_LEN + listLen);
if (output != NULL) {
byte* hdr = output + *pOffset;
c16toa(TLSXT_ECH_OUTER_EXTENSIONS, hdr);
c16toa((word16)(OPAQUE8_LEN + listLen), hdr + OPAQUE16_LEN);
hdr[OPAQUE16_LEN + OPAQUE16_LEN] = (byte)listLen;
}

/* accumulate offset even if nothing is written */
*pOffset += blockSz;
}

*outCount = count;
}

/* because the size of ech depends on the size of other extensions we need to
* get the size with ech special and process ech last, return status */
static int TLSX_GetSizeWithEch(WOLFSSL* ssl, byte* semaphore, byte msgType,
Expand All @@ -16296,11 +16393,25 @@ static int TLSX_GetSizeWithEch(WOLFSSL* ssl, byte* semaphore, byte msgType,
TLSX* echX = NULL;
TLSX* serverNameX = NULL;
TLSX** extensions = NULL;
WOLFSSL_ECH* ech = NULL;
word16 count = 0;
WC_DECLARE_VAR(serverName, char, WOLFSSL_HOST_NAME_MAX, 0);

WC_ALLOC_VAR_EX(serverName, char, WOLFSSL_HOST_NAME_MAX, NULL,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);

r = TLSX_EchChangeSNI(ssl, &echX, serverName, &serverNameX, &extensions);

if (echX != NULL)
ech = (WOLFSSL_ECH*)echX->data;

/* if encoding, then count encoded form of inner ClientHello.
* `semaphore` is in/out so encodable extensions will later be ignored */
if (r == 0 && ech != NULL && ech->type == ECH_TYPE_INNER &&
ech->writeEncoded) {
TLSX_ECH_BuildOuterExtensions(ssl, semaphore, msgType,
NULL, pLength, &count, semaphore);
}
if (r == 0 && ssl->extensions)
ret = TLSX_GetSize(ssl->extensions, semaphore, msgType, pLength);
if (r == 0 && ret == 0 && ssl->ctx && ssl->ctx->extensions)
Expand Down Expand Up @@ -16438,27 +16549,62 @@ static int TLSX_WriteWithEch(WOLFSSL* ssl, byte* output, byte* semaphore,
TLSX* echX = NULL;
TLSX* serverNameX = NULL;
TLSX** extensions = NULL;
WOLFSSL_ECH* ech = NULL;
WC_DECLARE_VAR(serverName, char, WOLFSSL_HOST_NAME_MAX, 0);

WC_ALLOC_VAR_EX(serverName, char, WOLFSSL_HOST_NAME_MAX, NULL,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
r = TLSX_EchChangeSNI(ssl, &echX, serverName, &serverNameX, &extensions);
ret = r;
if (ret == 0 && echX != NULL)
if (ret == 0 && echX != NULL) {
ech = (WOLFSSL_ECH*)echX->data;
/* turn ech on so it doesn't write, then write it last */
TURN_ON(semaphore, TLSX_ToSemaphore(echX->type));
}

/* for ECH inner, print the encodable block first, then the non-encodables.
* This allows the same transcript to be produced on either side
* (the transcript is over the expanded form). */
if (ret == 0 && ech != NULL && ech->type == ECH_TYPE_INNER) {
byte encodeMask[SEMAPHORE_SIZE];
byte* mask = ech->writeEncoded ? semaphore : encodeMask;
word16 count = 0;
int i;

XMEMSET(encodeMask, 0, SEMAPHORE_SIZE);

TLSX_ECH_BuildOuterExtensions(ssl, semaphore, msgType,
ech->writeEncoded ? output : NULL,
ech->writeEncoded ? pOffset : NULL,
&count, mask);
if (count >= 1 && !ech->writeEncoded) {
/* expanded: print encodable block normally */
for (i = 0; i < SEMAPHORE_SIZE; i++) {
semaphore[i] |= encodeMask[i];
encodeMask[i] = (byte)~encodeMask[i];
}
if (ssl->extensions) {
ret = TLSX_Write(ssl->extensions, output + *pOffset,
encodeMask, msgType, pOffset);
}
if (ret == 0 && ssl->ctx && ssl->ctx->extensions) {
ret = TLSX_Write(ssl->ctx->extensions, output + *pOffset,
encodeMask, msgType, pOffset);
}
}
}

/* print non-encodable block */
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);
}

/* only write if have a shot at acceptance */
/* only write ECH if have a shot at acceptance */
if (ret == 0 && echX != NULL &&
(ssl->options.echAccepted ||
((WOLFSSL_ECH*)echX->data)->innerCount == 0)) {
Expand Down
Loading
Loading