diff --git a/.github/scripts/openssl-ech.sh b/.github/scripts/openssl-ech.sh new file mode 100644 index 00000000000..d3dde32b6ef --- /dev/null +++ b/.github/scripts/openssl-ech.sh @@ -0,0 +1,192 @@ +#!/bin/bash + +set -e + +cleanup() { + if [ -f "$TMP_LOG" ]; then + cat "$TMP_LOG" + rm -f "$TMP_LOG" + fi +} +trap cleanup EXIT + +usage() { + echo "Usage: $0 [--suite ] [--workspace ]" + exit 1 +} + +MODE="" +SUITE="" + +WORKSPACE=${GITHUB_WORKSPACE:-"."} + +if [ $# -lt 1 ]; then + usage +fi + +case "$1" in + client|server) MODE="$1" ;; + *) usage ;; +esac +shift + +while [ $# -gt 0 ]; do + case "$1" in + --suite) + [ -z "$2" ] && { echo "ERROR: --suite requires a value"; exit 1; } + SUITE="$2" + shift 2 + echo "" + echo "Using suite: $SUITE" + echo "" + ;; + --workspace) + [ -z "$2" ] && { echo "ERROR: --workspace requires a value"; exit 1; } + WORKSPACE="$2" + shift 2 + ;; + *) echo "Unknown argument: $1"; usage ;; + esac +done + +OPENSSL=${OPENSSL:-"openssl"} +WOLFSSL_CLIENT=${WOLFSSL_CLIENT:-"$WORKSPACE/examples/client/client"} +WOLFSSL_SERVER=${WOLFSSL_SERVER:-"$WORKSPACE/examples/server/server"} +CERT_DIR=${CERT_DIR:-"$WORKSPACE/certs"} + +TMP_LOG="$WORKSPACE/tmp_file.log" +PRIV_NAME="ech-private-name.com" +PUB_NAME="ech-public-name.com" +MAX_WAIT=50 + +openssl_server(){ + local ech_file="$WORKSPACE/ech_config.pem" + local ech_config="" + local port="" + + rm -f "$ech_file" + + $OPENSSL ech -public_name "$PUB_NAME" -out "$ech_file" $SUITE &>> "$TMP_LOG" + + # 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" &>> "$TMP_LOG" + + # 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 \ + &>> "$TMP_LOG" <<< "wolfssl!" & + + # wait for server port to be ready and capture it + counter=0 + while [ -z "$port" ]; do + port=$(grep -m1 "ACCEPT" "$TMP_LOG" | sed 's/.*:\([0-9]*\)$/\1/') + sleep 0.1 + counter=$((counter + 1)) + if [ "$counter" -gt "$MAX_WAIT" ]; then + echo "ERROR: server port not found" &>> "$TMP_LOG" + exit 1 + fi + done + echo "parsed port: $port" &>> "$TMP_LOG" + + # test with wolfssl client + $WOLFSSL_CLIENT -v 4 \ + -p "$port" \ + -S "$PRIV_NAME" \ + --ech "$ech_config" \ + &>> "$TMP_LOG" + + rm -f "$ech_file" + + grep -q "ech_success=1" "$TMP_LOG" +} + +openssl_client(){ + local ready_file="$WORKSPACE/wolfssl_tls13_ready$$" + local ech_config="" + local port=0 + + rm -f "$ready_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" \ + $SUITE \ + &>> "$TMP_LOG" & + + # 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 "$MAX_WAIT" ]; then + echo "ERROR: no ready file" &>> "$TMP_LOG" + exit 1 + fi + done + port="$(cat "$ready_file")" + rm -f "$ready_file" + echo "parsed port: $port" &>> "$TMP_LOG" + + # get ECH config from server + counter=0 + while [ -z "$ech_config" ]; do + ech_config=$(grep -m1 "ECH config (base64): " "$TMP_LOG" \ + 2>/dev/null | sed 's/ECH config (base64): //g') + sleep 0.1 + counter=$((counter + 1)) + if [ "$counter" -gt "$MAX_WAIT" ]; then + echo "ERROR: no ECH configs" &>> "$TMP_LOG" + exit 1 + fi + done + echo "parsed ech config: $ech_config" &>> "$TMP_LOG" + + # 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" \ + &>> "$TMP_LOG" + + grep -q "ECH: success: 1" "$TMP_LOG" +} + +rm -f "$TMP_LOG" + +case "$MODE" in + server) + if [ -n "$SUITE" ]; then + SUITE="-suite $SUITE" + fi + openssl_server + ;; + client) + if [ -n "$SUITE" ]; then + SUITE="--ech-suite $SUITE" + fi + openssl_client + ;; + *) + exit 1 + ;; +esac diff --git a/.github/workflows/openssl-ech.yml b/.github/workflows/openssl-ech.yml index 83a73ee55db..e295062c716 100644 --- a/.github/workflows/openssl-ech.yml +++ b/.github/workflows/openssl-ech.yml @@ -23,7 +23,8 @@ jobs: uses: wolfSSL/actions-build-autotools-project@v1 with: path: wolfssl - configure: --enable-ech CFLAGS='-DUSE_FLAT_TEST_H' + configure: >- + --enable-ech --enable-sha512 --enable-aes CFLAGS='-DUSE_FLAT_TEST_H' install: true - name: tar build-dir @@ -36,6 +37,11 @@ jobs: # need certs so 'wolfSSL error: wolf root not found' does not show up cp -r "$GITHUB_WORKSPACE/wolfssl/certs" build-dir/certs + + # need the ech script to run tests + cp "$GITHUB_WORKSPACE/wolfssl/.github/scripts/openssl-ech.sh" \ + build-dir/openssl-ech.sh + tar -zcf build-dir.tgz build-dir - name: Upload built wolfSSL @@ -77,8 +83,8 @@ jobs: path: openssl-install.tgz retention-days: 5 - ech_server_interop_test: - name: ECH Server Interop Test + ech_interop_test: + name: ECH Interop Test if: github.repository_owner == 'wolfssl' needs: [build_wolfssl, build_openssl_ech] runs-on: ubuntu-24.04 @@ -99,7 +105,7 @@ jobs: tar -xzf build-dir.tgz tar -xzf openssl-install.tgz - - name: Build wolfssl server example + - name: Build wolfssl client and server examples run: | export WOLFSSL_INSTALL_DIR="$GITHUB_WORKSPACE/build-dir" export WOLFSSL_BIN_DIR="$WOLFSSL_INSTALL_DIR/bin" @@ -107,200 +113,47 @@ jobs: 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" + 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 + - name: Interop test 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$$" + export OPENSSL="$GITHUB_WORKSPACE/openssl-install/bin/openssl" + export WOLFSSL_CLIENT="$GITHUB_WORKSPACE/build-dir/bin/client" + export WOLFSSL_SERVER="$GITHUB_WORKSPACE/build-dir/bin/server" + export CERT_DIR="$GITHUB_WORKSPACE/build-dir/certs" 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!" & + # 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" - # 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" + echo -e "\nTesting default suite with OpenSSL client and wolfSSL server\n" &>> "$LOG_FILE" + bash ./openssl-ech.sh client &>> "$LOG_FILE" - # test with wolfssl client - $WOLFSSL_CLIENT -v 4 \ - -p "$PORT" \ - -S "$PRIV_NAME" \ - --ech "$ECH_CONFIG" \ - &>> "$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" - grep "ech_success=1" "$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" # cleanup rm -f "$LOG_FILE" - rm -f "$ECH_FILE" - name: Print debug info on failure if: ${{ failure() }} diff --git a/examples/server/server.c b/examples/server/server.c index 07e480eb558..166939e7834 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -919,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[][69] = { +static const char* server_usage_msg[][70] = { /* English */ { " NOTE: All files relative to wolfSSL home dir\n", /* 0 */ @@ -1120,11 +1120,14 @@ static const char* server_usage_msg[][69] = { "--ech Generate Encrypted Client Hello config with " "public name \n", /* 67 */ + "--ech-suite HPKE suite to use for ech config.\n" + " Supplied as 3 integers (ex: 32,1,3)\n", + /* 68 */ #endif "\n" "For simpler wolfSSL TLS server examples, visit\n" "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", - /* 68 */ + /* 69 */ NULL, }, #ifndef NO_MULTIBYTE_PRINT @@ -1335,12 +1338,20 @@ static const char* server_usage_msg[][69] = { #endif #ifdef WOLFSSL_SYS_CRYPTO_POLICY "--crypto-policy \n", /* 66 */ +#endif +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) + "--ech Generate Encrypted Client Hello config with " + "public name \n", + /* 67 */ + "--ech-suite HPKE suite to use for ech config.\n" + " Supplied as 3 integers (ex: 32,1,3)\n", + /* 68 */ #endif "\n" "より簡単なwolfSSL TSL クライアントの例については" "下記にアクセスしてください\n" "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", - /* 67 */ + /* 69 */ NULL, }, #endif @@ -1502,6 +1513,7 @@ static void Usage(void) #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) printf("%s", msg[++msgId]); /* --ech */ + printf("%s", msg[++msgId]); /* --ech-suite */ #endif printf("%s", msg[++msgId]); /* Examples repo link */ } @@ -1627,6 +1639,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #endif /* WOLFSSL_SYS_CRYPTO_POLICY */ #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) { "ech", 1, 269 }, + { "ech-suite", 1, 270 }, #endif { 0, 0, 0 } }; @@ -1706,6 +1719,10 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #endif #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) char* echPublicName = NULL; + char* echSuite = NULL; + word16 kemId = 0; + word16 kdfId = 0; + word16 aeadId = 0; #endif #ifdef HAVE_TRUSTED_CA @@ -2538,6 +2555,32 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) case 269: echPublicName = myoptarg; + break; + case 270: + echSuite = myoptarg; + + /* parse alg id's + * commas can be entered with no number to accept the default */ + if (echSuite != NULL) { + kemId = (word16)atoi(echSuite); + for (; *echSuite != '\0' && *echSuite != ','; echSuite++); + if (*echSuite != ',') { + LOG_ERROR("Expected two commas in '%s'\n", myoptarg); + XEXIT_T(EXIT_FAILURE); + } + echSuite++; + + kdfId = (word16)atoi(echSuite); + for (; *echSuite != '\0' && *echSuite != ','; echSuite++); + if (*echSuite != ',') { + LOG_ERROR("Expected two commas in '%s'\n", myoptarg); + XEXIT_T(EXIT_FAILURE); + } + echSuite++; + + aeadId = (word16)atoi(echSuite); + } + break; #endif @@ -3108,8 +3151,8 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) char* echConfigBase64Ptr; word32 echConfigBase64Len = sizeof(echConfigBase64); - if (wolfSSL_CTX_GenerateEchConfig(ctx, echPublicName, 0, 0, 0) - != WOLFSSL_SUCCESS) { + if (wolfSSL_CTX_GenerateEchConfig(ctx, echPublicName, kemId, kdfId, + aeadId) != WOLFSSL_SUCCESS) { err_sys_ex(runWithErrors, "GenerateEchConfig failed"); } if (wolfSSL_CTX_GetEchConfigs(ctx, echConfig, &echConfigLen) diff --git a/src/ssl_ech.c b/src/ssl_ech.c index 6eccc3f8718..86f6fe40ee0 100644 --- a/src/ssl_ech.c +++ b/src/ssl_ech.c @@ -34,8 +34,8 @@ int wolfSSL_CTX_GenerateEchConfig(WOLFSSL_CTX* ctx, const char* publicName, word16 kemId, word16 kdfId, word16 aeadId) { int ret = 0; - word16 encLen = DHKEM_X25519_ENC_LEN; WOLFSSL_EchConfig* newConfig; + word16 encLen = HPKE_Npk_MAX; #ifdef WOLFSSL_SMALL_STACK Hpke* hpke = NULL; WC_RNG* rng; @@ -338,23 +338,7 @@ int GetEchConfig(WOLFSSL_EchConfig* config, byte* output, word32* outputLen) totalLen += 2; /* hpke_pub_key */ - switch (config->kemId) { - case DHKEM_P256_HKDF_SHA256: - totalLen += DHKEM_P256_ENC_LEN; - break; - case DHKEM_P384_HKDF_SHA384: - totalLen += DHKEM_P384_ENC_LEN; - break; - case DHKEM_P521_HKDF_SHA512: - totalLen += DHKEM_P521_ENC_LEN; - break; - case DHKEM_X25519_HKDF_SHA256: - totalLen += DHKEM_X25519_ENC_LEN; - break; - case DHKEM_X448_HKDF_SHA512: - totalLen += DHKEM_X448_ENC_LEN; - break; - } + totalLen += wc_HpkeKemGetEncLen(config->kemId); /* cipherSuitesLen */ totalLen += 2; @@ -693,16 +677,9 @@ int SetEchConfigsEx(WOLFSSL_EchConfig** outputConfigs, void* heap, break; } - /* check that we support this config */ - for (j = 0; j < HPKE_SUPPORTED_KEM_LEN; j++) { - if (hpkeSupportedKem[j] == workingConfig->kemId) - break; - } - /* KEM or ciphersuite not supported, free this config and then try to * parse another */ - if (j >= HPKE_SUPPORTED_KEM_LEN || - EchConfigGetSupportedCipherSuite(workingConfig) < 0) { + if (EchConfigGetSupportedCipherSuite(workingConfig) < 0) { XFREE(workingConfig->cipherSuites, heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(workingConfig->publicName, heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(workingConfig->raw, heap, DYNAMIC_TYPE_TMP_BUFFER); diff --git a/src/tls.c b/src/tls.c index ec949a752fc..1ad10c6ab7f 100644 --- a/src/tls.c +++ b/src/tls.c @@ -13259,23 +13259,10 @@ static int TLSX_ECH_Use(WOLFSSL_EchConfig* echConfig, TLSX** extensions, /* configId */ ech->configId = echConfig->configId; /* encLen */ - switch (echConfig->kemId) - { - case DHKEM_P256_HKDF_SHA256: - ech->encLen = DHKEM_P256_ENC_LEN; - break; - case DHKEM_P384_HKDF_SHA384: - ech->encLen = DHKEM_P384_ENC_LEN; - break; - case DHKEM_P521_HKDF_SHA512: - ech->encLen = DHKEM_P521_ENC_LEN; - break; - case DHKEM_X25519_HKDF_SHA256: - ech->encLen = DHKEM_X25519_ENC_LEN; - break; - case DHKEM_X448_HKDF_SHA512: - ech->encLen = DHKEM_X448_ENC_LEN; - break; + ech->encLen = wc_HpkeKemGetEncLen(echConfig->kemId); + if (ech->encLen == 0) { + XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER); + return BAD_FUNC_ARG; } /* setup hpke */ ech->hpke = (Hpke*)XMALLOC(sizeof(Hpke), heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -13288,8 +13275,13 @@ static int TLSX_ECH_Use(WOLFSSL_EchConfig* echConfig, TLSX** extensions, /* setup the ephemeralKey */ if (ret == 0) ret = wc_HpkeGenerateKeyPair(ech->hpke, &ech->ephemeralKey, rng); - if (ret == 0) + if (ret == 0) { ret = TLSX_Push(extensions, TLSX_ECH, ech, heap); + if (ret != 0) { + wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, ech->ephemeralKey, + ech->hpke->heap); + } + } if (ret != 0) { XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER); XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -13916,7 +13908,6 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, byte* aad, word32 aadLen, void* heap) { int ret = 0; - int expectedEncLen; int i; word32 rawConfigLen = 0; byte* info = NULL; @@ -13924,28 +13915,7 @@ static int TLSX_ExtractEch(WOLFSSL_ECH* ech, WOLFSSL_EchConfig* echConfig, if (ech == NULL || echConfig == NULL || aad == NULL) return BAD_FUNC_ARG; /* verify the kem and key len */ - switch (echConfig->kemId) - { - case DHKEM_P256_HKDF_SHA256: - expectedEncLen = DHKEM_P256_ENC_LEN; - break; - case DHKEM_P384_HKDF_SHA384: - expectedEncLen = DHKEM_P384_ENC_LEN; - break; - case DHKEM_P521_HKDF_SHA512: - expectedEncLen = DHKEM_P521_ENC_LEN; - break; - case DHKEM_X25519_HKDF_SHA256: - expectedEncLen = DHKEM_X25519_ENC_LEN; - break; - case DHKEM_X448_HKDF_SHA512: - expectedEncLen = DHKEM_X448_ENC_LEN; - break; - default: - expectedEncLen = 0; - break; - } - if (expectedEncLen != ech->encLen) + if (wc_HpkeKemGetEncLen(echConfig->kemId) != ech->encLen) return BAD_FUNC_ARG; /* verify the cipher suite */ for (i = 0; i < echConfig->numCipherSuites; i++) { @@ -14229,11 +14199,12 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, static void TLSX_ECH_Free(WOLFSSL_ECH* ech, void* heap) { XFREE(ech->innerClientHello, heap, DYNAMIC_TYPE_TMP_BUFFER); - if (ech->ephemeralKey != NULL) - wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, ech->ephemeralKey, - ech->hpke->heap); - if (ech->hpke != NULL) + if (ech->hpke != NULL) { + if (ech->ephemeralKey != NULL) + wc_HpkeFreeKey(ech->hpke, ech->hpke->kem, ech->ephemeralKey, + ech->hpke->heap); XFREE(ech->hpke, heap, DYNAMIC_TYPE_TMP_BUFFER); + } if (ech->hpkeContext != NULL) XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER); if (ech->privateName != NULL) diff --git a/src/tls13.c b/src/tls13.c index 0401b83e036..749e4137052 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -3782,26 +3782,17 @@ static byte helloRetryRequestRandom[] = { /* returns the index of the first supported cipher suite, -1 if none */ int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config) { - int i, j, supported = 0; + int i = 0; - for (i = 0; i < config->numCipherSuites; i++) { - supported = 0; - - for (j = 0; j < HPKE_SUPPORTED_KDF_LEN; j++) { - if (config->cipherSuites[i].kdfId == hpkeSupportedKdf[j]) - break; - } - - if (j < HPKE_SUPPORTED_KDF_LEN) - for (j = 0; j < HPKE_SUPPORTED_AEAD_LEN; j++) { - if (config->cipherSuites[i].aeadId == hpkeSupportedAead[j]) { - supported = 1; - break; - } - } + if (!wc_HpkeKemIsSupported(config->kemId)) { + return WOLFSSL_FATAL_ERROR; + } - if (supported) + for (i = 0; i < config->numCipherSuites; i++) { + if (wc_HpkeKdfIsSupported(config->cipherSuites[i].kdfId) && + wc_HpkeAeadIsSupported(config->cipherSuites[i].aeadId)) { return i; + } } return WOLFSSL_FATAL_ERROR; diff --git a/tests/api.c b/tests/api.c index 9e9689b9e8c..59a04dacf90 100644 --- a/tests/api.c +++ b/tests/api.c @@ -14438,6 +14438,9 @@ static byte echCbTestConfigs[512]; static word32 echCbTestConfigsLen; static const char* echCbTestPublicName = "ech-public-name.com"; static const char* echCbTestPrivateName = "ech-private-name.com"; +static word16 echCbTestKemID = 0; +static word16 echCbTestKdfID = 0; +static word16 echCbTestAeadID = 0; /* the arg is whether the client has ech enabled or not */ static int test_ech_server_sni_callback(WOLFSSL* ssl, int* ad, void* arg) @@ -14469,7 +14472,8 @@ static int test_ech_server_ctx_ready(WOLFSSL_CTX* ctx) { int ret; - ret = wolfSSL_CTX_GenerateEchConfig(ctx, echCbTestPublicName, 0, 0, 0); + ret = wolfSSL_CTX_GenerateEchConfig(ctx, echCbTestPublicName, + echCbTestKemID, echCbTestKdfID, echCbTestAeadID); if (ret != WOLFSSL_SUCCESS) return TEST_FAIL; @@ -14512,6 +14516,115 @@ static int test_ech_client_ssl_ready(WOLFSSL* ssl) return TEST_SUCCESS; } +static int test_wolfSSL_Tls13_ECH_all_algos_ex(void) +{ + EXPECT_DECLS; + struct 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; + + 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); + + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); + ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, 1); + + if (echCbTestKemID != 0 && echCbTestKdfID != 0 && echCbTestAeadID != 0) { + TLSX* echX = TLSX_Find(test_ctx.c_ssl->extensions, TLSX_ECH); + ExpectNotNull(echX); + if (echX != NULL) { + WOLFSSL_ECH* ech = (WOLFSSL_ECH*)echX->data; + ExpectNotNull(ech); + if (ech != NULL) { + /* verify that the ech extension has the correct algos */ + ExpectIntEQ(ech->kemId, echCbTestKemID); + ExpectIntEQ(ech->cipherSuite.kdfId, echCbTestKdfID); + ExpectIntEQ(ech->cipherSuite.aeadId, echCbTestAeadID); + if (ech->hpke != NULL) { + /* and that hpke was initialized with these algos */ + ExpectIntEQ(ech->hpke->kem, echCbTestKemID); + ExpectIntEQ(ech->hpke->kdf, echCbTestKdfID); + ExpectIntEQ(ech->hpke->aead, echCbTestAeadID); + } + if (ech->echConfig != NULL) { + ExpectIntEQ(ech->echConfig->kemId, echCbTestKemID); + } + } + } + } + + test_ssl_memio_cleanup(&test_ctx); + + return EXPECT_RESULT(); +} + +static int test_wolfSSL_Tls13_ECH_all_algos(void) +{ + EXPECT_DECLS; + int i; + int j; + int k; + static const word16 kems[] = { +#if defined(HAVE_ECC) +#if (defined(WOLFSSL_SHA224) || !defined(NO_SHA256)) + DHKEM_P256_HKDF_SHA256, +#endif +#if defined(WOLFSSL_SHA384) + DHKEM_P384_HKDF_SHA384, +#endif +#if (defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512)) + DHKEM_P521_HKDF_SHA512, +#endif +#endif /* HAVE_ECC */ +#if defined(HAVE_CURVE25519) && (defined(WOLFSSL_SHA224) || !defined(NO_SHA256)) + DHKEM_X25519_HKDF_SHA256, +#endif + }; + static const word16 kdfs[] = { +#if defined(WOLFSSL_SHA224) || !defined(NO_SHA256) + HKDF_SHA256, +#endif +#ifdef WOLFSSL_SHA384 + HKDF_SHA384, +#endif +#ifdef WOLFSSL_SHA512 + HKDF_SHA512, +#endif + }; + static const word16 aeads[] = { +#ifdef WOLFSSL_AES_128 + HPKE_AES_128_GCM, +#endif +#ifdef WOLFSSL_AES_256 + HPKE_AES_256_GCM, +#endif + }; + + for (i = 0; i < (int)(sizeof(kems) / sizeof(*kems)); i++) { + echCbTestKemID = kems[i]; + for (j = 0; j < (int)(sizeof(kdfs) / sizeof(*kdfs)); j++) { + echCbTestKdfID = kdfs[j]; + for (k = 0; k < (int)(sizeof(aeads) / sizeof(*aeads)); k++) { + echCbTestAeadID = aeads[k]; + ExpectIntEQ(test_wolfSSL_Tls13_ECH_all_algos_ex(), + TEST_SUCCESS); + } + } + } + + echCbTestKemID = 0; + echCbTestKdfID = 0; + echCbTestAeadID = 0; + + return EXPECT_RESULT(); +} + /* Test ECH when no private SNI is set */ static int test_wolfSSL_Tls13_ECH_no_private_name(void) { @@ -34426,6 +34539,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_SubTls13_ECH), #endif #if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) + TEST_DECL(test_wolfSSL_Tls13_ECH_all_algos), 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), diff --git a/wolfcrypt/src/hpke.c b/wolfcrypt/src/hpke.c index 86b740c9d02..fd5f76a2663 100644 --- a/wolfcrypt/src/hpke.c +++ b/wolfcrypt/src/hpke.c @@ -45,24 +45,6 @@ #include #endif -const int hpkeSupportedKem[HPKE_SUPPORTED_KEM_LEN] = { - DHKEM_P256_HKDF_SHA256, - DHKEM_P384_HKDF_SHA384, - DHKEM_P521_HKDF_SHA512, - DHKEM_X25519_HKDF_SHA256, -}; - -const int hpkeSupportedKdf[HPKE_SUPPORTED_KDF_LEN] = { - HKDF_SHA256, - HKDF_SHA384, - HKDF_SHA512, -}; - -const int hpkeSupportedAead[HPKE_SUPPORTED_AEAD_LEN] = { - HPKE_AES_128_GCM, - HPKE_AES_256_GCM, -}; - static const char* KEM_STR = "KEM"; static const int KEM_STR_LEN = 3; @@ -132,9 +114,9 @@ int wc_HpkeInit(Hpke* hpke, int kem, int kdf, int aead, void* heap) } XMEMSET(hpke, 0, sizeof(*hpke)); - hpke->kem = (word32)kem; - hpke->kdf = (word32)kdf; - hpke->aead = (word32)aead; + hpke->kem = (word16)kem; + hpke->kdf = (word16)kdf; + hpke->aead = (word16)aead; hpke->heap = heap; /* set kem_suite_id */ @@ -166,52 +148,52 @@ int wc_HpkeInit(Hpke* hpke, int kem, int kdf, int aead, void* heap) if (ret == 0) { switch (kem) { #if defined(HAVE_ECC) -#if defined(WOLFSSL_SHA224) || !defined(NO_SHA256) +#if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && !defined(NO_SHA256) case DHKEM_P256_HKDF_SHA256: - hpke->curve_id = ECC_SECP256R1; + hpke->curveId = ECC_SECP256R1; hpke->Nsecret = WC_SHA256_DIGEST_SIZE; - hpke->Nh = WC_SHA256_DIGEST_SIZE; - hpke->Ndh = (word32)wc_ecc_get_curve_size_from_id(hpke->curve_id); + hpke->kemDigest = WC_SHA256; + hpke->Ndh = (word32)wc_ecc_get_curve_size_from_id(hpke->curveId); hpke->Npk = 1 + hpke->Ndh * 2; break; #endif -#ifdef WOLFSSL_SHA384 +#if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA384) case DHKEM_P384_HKDF_SHA384: - hpke->curve_id = ECC_SECP384R1; + hpke->curveId = ECC_SECP384R1; hpke->Nsecret = WC_SHA384_DIGEST_SIZE; - hpke->Nh = WC_SHA384_DIGEST_SIZE; - hpke->Ndh = (word32)wc_ecc_get_curve_size_from_id(hpke->curve_id); + hpke->kemDigest = WC_SHA384; + hpke->Ndh = (word32)wc_ecc_get_curve_size_from_id(hpke->curveId); hpke->Npk = 1 + hpke->Ndh * 2; break; #endif -#if defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512) +#if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA512) case DHKEM_P521_HKDF_SHA512: - hpke->curve_id = ECC_SECP521R1; + hpke->curveId = ECC_SECP521R1; hpke->Nsecret = WC_SHA512_DIGEST_SIZE; - hpke->Nh = WC_SHA512_DIGEST_SIZE; - hpke->Ndh = (word32)wc_ecc_get_curve_size_from_id(hpke->curve_id); + hpke->kemDigest = WC_SHA512; + hpke->Ndh = (word32)wc_ecc_get_curve_size_from_id(hpke->curveId); hpke->Npk = 1 + hpke->Ndh * 2; break; #endif -#endif +#endif /* HAVE_ECC */ -#if defined(HAVE_CURVE25519) &&\ - (defined(WOLFSSL_SHA224) || !defined(NO_SHA256)) +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) case DHKEM_X25519_HKDF_SHA256: hpke->Nsecret = WC_SHA256_DIGEST_SIZE; - hpke->Nh = WC_SHA256_DIGEST_SIZE; + hpke->kemDigest = WC_SHA256; hpke->Ndh = CURVE25519_KEYSIZE; hpke->Npk = CURVE25519_PUB_KEY_SIZE; break; #endif -#if defined(HAVE_CURVE448) &&\ - (defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512)) +#if defined(HAVE_CURVE448) && defined(WOLFSSL_SHA512) case DHKEM_X448_HKDF_SHA512: hpke->Nsecret = WC_SHA512_DIGEST_SIZE; - hpke->Nh = WC_SHA512_DIGEST_SIZE; + hpke->kemDigest = WC_SHA512; /* size of x448 shared secret */ hpke->Ndh = 64; hpke->Npk = CURVE448_PUB_KEY_SIZE; @@ -227,17 +209,26 @@ int wc_HpkeInit(Hpke* hpke, int kem, int kdf, int aead, void* heap) if (ret == 0) { switch (kdf) { +#if !defined(NO_SHA256) case HKDF_SHA256: - hpke->kdf_digest = WC_SHA256; + hpke->Nh = WC_SHA256_DIGEST_SIZE; + hpke->kdfDigest = WC_SHA256; break; +#endif +#ifdef WOLFSSL_SHA384 case HKDF_SHA384: - hpke->kdf_digest = WC_SHA384; + hpke->Nh = WC_SHA384_DIGEST_SIZE; + hpke->kdfDigest = WC_SHA384; break; +#endif +#ifdef WOLFSSL_SHA512 case HKDF_SHA512: - hpke->kdf_digest = WC_SHA512; + hpke->Nh = WC_SHA512_DIGEST_SIZE; + hpke->kdfDigest = WC_SHA512; break; +#endif default: ret = BAD_FUNC_ARG; @@ -247,17 +238,21 @@ int wc_HpkeInit(Hpke* hpke, int kem, int kdf, int aead, void* heap) if (ret == 0) { switch (aead) { +#ifdef WOLFSSL_AES_128 case HPKE_AES_128_GCM: hpke->Nk = AES_128_KEY_SIZE; hpke->Nn = GCM_NONCE_MID_SZ; hpke->Nt = WC_AES_BLOCK_SIZE; break; +#endif +#ifdef WOLFSSL_AES_256 case HPKE_AES_256_GCM: hpke->Nk = AES_256_KEY_SIZE; hpke->Nn = GCM_NONCE_MID_SZ; hpke->Nt = WC_AES_BLOCK_SIZE; break; +#endif default: ret = BAD_FUNC_ARG; @@ -283,26 +278,34 @@ int wc_HpkeGenerateKeyPair(Hpke* hpke, void** keypair, WC_RNG* rng) switch (hpke->kem) { #if defined(HAVE_ECC) + #if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && !defined(NO_SHA256) case DHKEM_P256_HKDF_SHA256: *keypair = wc_ecc_key_new(hpke->heap); if (*keypair != NULL) ret = wc_ecc_make_key_ex(rng, 32, (ecc_key*)*keypair, ECC_SECP256R1); break; +#endif + #if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA384) case DHKEM_P384_HKDF_SHA384: *keypair = wc_ecc_key_new(hpke->heap); if (*keypair != NULL) ret = wc_ecc_make_key_ex(rng, 48, (ecc_key*)*keypair, ECC_SECP384R1); break; + #endif + #if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA512) case DHKEM_P521_HKDF_SHA512: *keypair = wc_ecc_key_new(hpke->heap); if (*keypair != NULL) ret = wc_ecc_make_key_ex(rng, 66, (ecc_key*)*keypair, ECC_SECP521R1); break; + #endif #endif -#if defined(HAVE_CURVE25519) +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) case DHKEM_X25519_HKDF_SHA256: *keypair = XMALLOC(sizeof(curve25519_key), hpke->heap, DYNAMIC_TYPE_CURVE25519); @@ -315,8 +318,10 @@ int wc_HpkeGenerateKeyPair(Hpke* hpke, void** keypair, WC_RNG* rng) } break; #endif +#if defined(HAVE_CURVE448) && defined(WOLFSSL_SHA512) case DHKEM_X448_HKDF_SHA512: /* TODO: Add X448 */ +#endif default: ret = BAD_FUNC_ARG; break; @@ -326,7 +331,7 @@ int wc_HpkeGenerateKeyPair(Hpke* hpke, void** keypair, WC_RNG* rng) ret = MEMORY_E; if (ret != 0 && *keypair != NULL) { - wc_HpkeFreeKey(hpke, (word16)hpke->kem, *keypair, hpke->heap); + wc_HpkeFreeKey(hpke, hpke->kem, *keypair, hpke->heap); *keypair = NULL; } @@ -355,13 +360,16 @@ int wc_HpkeSerializePublicKey(Hpke* hpke, void* key, byte* out, word16* outSz) ret = wc_ecc_export_x963_ex((ecc_key*)key, out, &tmpOutSz, 0); break; #endif -#if defined(HAVE_CURVE25519) +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) case DHKEM_X25519_HKDF_SHA256: ret = wc_curve25519_export_public_ex((curve25519_key*)key, out, &tmpOutSz, EC25519_LITTLE_ENDIAN); break; #endif +#if defined(HAVE_CURVE448) && defined(WOLFSSL_SHA512) case DHKEM_X448_HKDF_SHA512: + /* TODO: Add X448 */ +#endif default: ret = -1; break; @@ -397,11 +405,11 @@ int wc_HpkeDeserializePublicKey(Hpke* hpke, void** key, const byte* in, if (*key != NULL) { /* import the x963 key */ ret = wc_ecc_import_x963_ex(in, inSz, (ecc_key*)*key, - hpke->curve_id); + hpke->curveId); } break; #endif -#if defined(HAVE_CURVE25519) +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) case DHKEM_X25519_HKDF_SHA256: *key = XMALLOC(sizeof(curve25519_key), hpke->heap, DYNAMIC_TYPE_CURVE25519); @@ -414,7 +422,10 @@ int wc_HpkeDeserializePublicKey(Hpke* hpke, void** key, const byte* in, } break; #endif +#if defined(HAVE_CURVE448) && defined(WOLFSSL_SHA512) case DHKEM_X448_HKDF_SHA512: + /* TODO: Add X448 */ +#endif default: ret = -1; break; @@ -424,7 +435,7 @@ int wc_HpkeDeserializePublicKey(Hpke* hpke, void** key, const byte* in, ret = MEMORY_E; if (ret != 0 && *key != NULL) { - wc_HpkeFreeKey(hpke, (word16)hpke->kem, *key, hpke->heap); + wc_HpkeFreeKey(hpke, hpke->kem, *key, hpke->heap); *key = NULL; } @@ -443,14 +454,16 @@ void wc_HpkeFreeKey(Hpke* hpke, word16 kem, void* keypair, void* heap) wc_ecc_key_free((ecc_key*)keypair); break; #endif -#if defined(HAVE_CURVE25519) +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) case DHKEM_X25519_HKDF_SHA256: wc_curve25519_free((curve25519_key*)keypair); XFREE(keypair, heap, DYNAMIC_TYPE_CURVE25519); break; #endif +#if defined(HAVE_CURVE448) && defined(WOLFSSL_SHA512) case DHKEM_X448_HKDF_SHA512: /* TODO: Add X448 */ +#endif default: break; } @@ -459,7 +472,7 @@ void wc_HpkeFreeKey(Hpke* hpke, word16 kem, void* keypair, void* heap) } static int wc_HpkeLabeledExtract(Hpke* hpke, byte* suite_id, - word32 suite_id_len, byte* salt, word32 salt_len, byte* label, + word32 suite_id_len, int digest, byte* salt, word32 salt_len, byte* label, word32 label_len, byte* ikm, word32 ikm_len, byte* out) { int ret; @@ -516,7 +529,7 @@ static int wc_HpkeLabeledExtract(Hpke* hpke, byte* suite_id, /* call extract */ PRIVATE_KEY_UNLOCK(); - ret = wc_HKDF_Extract(hpke->kdf_digest, salt, salt_len, labeled_ikm, + ret = wc_HKDF_Extract(digest, salt, salt_len, labeled_ikm, (word32)(size_t)(labeled_ikm_p - labeled_ikm), out); PRIVATE_KEY_LOCK(); @@ -528,8 +541,8 @@ static int wc_HpkeLabeledExtract(Hpke* hpke, byte* suite_id, /* do hkdf expand with the format specified in the hpke rfc, return 0 or * error */ static int wc_HpkeLabeledExpand(Hpke* hpke, byte* suite_id, word32 suite_id_len, - byte* prk, word32 prk_len, byte* label, word32 label_len, byte* info, - word32 infoSz, word32 L, byte* out) + int digest, byte* prk, word32 prk_len, byte* label, word32 label_len, + byte* info, word32 infoSz, word32 L, byte* out) { int ret; byte* labeled_info_p; @@ -592,10 +605,8 @@ static int wc_HpkeLabeledExpand(Hpke* hpke, byte* suite_id, word32 suite_id_len, /* call expand */ PRIVATE_KEY_UNLOCK(); - ret = wc_HKDF_Expand(hpke->kdf_digest, - prk, prk_len, - labeled_info, (word32)(size_t)(labeled_info_p - labeled_info), - out, L); + ret = wc_HKDF_Expand(digest, prk, prk_len, labeled_info, + (word32)(size_t)(labeled_info_p - labeled_info), out, L); PRIVATE_KEY_LOCK(); } @@ -643,15 +654,16 @@ static int wc_HpkeExtractAndExpand( Hpke* hpke, byte* dh, word32 dh_len, /* extract */ ret = wc_HpkeLabeledExtract(hpke, hpke->kem_suite_id, - sizeof( hpke->kem_suite_id ), NULL, 0, (byte*)EAE_PRK_LABEL_STR, - EAE_PRK_LABEL_STR_LEN, dh, dh_len, eae_prk); + sizeof( hpke->kem_suite_id ), hpke->kemDigest, NULL, 0, + (byte*)EAE_PRK_LABEL_STR, EAE_PRK_LABEL_STR_LEN, dh, dh_len, eae_prk); /* expand */ if ( ret == 0 ) { ret = wc_HpkeLabeledExpand(hpke, hpke->kem_suite_id, - sizeof( hpke->kem_suite_id ), eae_prk, hpke->Nh, - (byte*)SHARED_SECRET_LABEL_STR, SHARED_SECRET_LABEL_STR_LEN, - kemContext, kem_context_length, hpke->Nsecret, sharedSecret); + sizeof( hpke->kem_suite_id ), hpke->kemDigest, eae_prk, + hpke->Nsecret, (byte*)SHARED_SECRET_LABEL_STR, + SHARED_SECRET_LABEL_STR_LEN, kemContext, kem_context_length, + hpke->Nsecret, sharedSecret); } ForceZero(eae_prk, WC_MAX_DIGEST_SIZE); @@ -701,35 +713,37 @@ static int wc_HpkeKeyScheduleBase(Hpke* hpke, HpkeBaseContext* context, /* extract psk_id, which for base is null */ ret = wc_HpkeLabeledExtract(hpke, hpke->hpke_suite_id, - sizeof( hpke->hpke_suite_id ), NULL, 0, (byte*)PSK_ID_HASH_LABEL_STR, - PSK_ID_HASH_LABEL_STR_LEN, NULL, 0, key_schedule_context + 1); + sizeof( hpke->hpke_suite_id ), hpke->kdfDigest, NULL, 0, + (byte*)PSK_ID_HASH_LABEL_STR, PSK_ID_HASH_LABEL_STR_LEN, NULL, 0, + key_schedule_context + 1); /* extract info */ if (ret == 0) { ret = wc_HpkeLabeledExtract(hpke, hpke->hpke_suite_id, - sizeof( hpke->hpke_suite_id ), NULL, 0, (byte*)INFO_HASH_LABEL_STR, - INFO_HASH_LABEL_STR_LEN, info, infoSz, + sizeof( hpke->hpke_suite_id ), hpke->kdfDigest, NULL, 0, + (byte*)INFO_HASH_LABEL_STR, INFO_HASH_LABEL_STR_LEN, info, infoSz, key_schedule_context + 1 + hpke->Nh); } /* extract secret */ if (ret == 0) { ret = wc_HpkeLabeledExtract(hpke, hpke->hpke_suite_id, - sizeof( hpke->hpke_suite_id ), sharedSecret, hpke->Nsecret, - (byte*)SECRET_LABEL_STR, SECRET_LABEL_STR_LEN, NULL, 0, secret); + sizeof( hpke->hpke_suite_id ), hpke->kdfDigest, sharedSecret, + hpke->Nsecret, (byte*)SECRET_LABEL_STR, SECRET_LABEL_STR_LEN, + NULL, 0, secret); } /* expand key */ if (ret == 0) ret = wc_HpkeLabeledExpand(hpke, hpke->hpke_suite_id, - sizeof( hpke->hpke_suite_id ), secret, hpke->Nh, + sizeof( hpke->hpke_suite_id ), hpke->kdfDigest, secret, hpke->Nh, (byte*)KEY_LABEL_STR, KEY_LABEL_STR_LEN, key_schedule_context, 1 + 2 * hpke->Nh, hpke->Nk, context->key); /* expand nonce */ if (ret == 0) { ret = wc_HpkeLabeledExpand(hpke, hpke->hpke_suite_id, - sizeof( hpke->hpke_suite_id ), secret, hpke->Nh, + sizeof( hpke->hpke_suite_id ), hpke->kdfDigest, secret, hpke->Nh, (byte*)BASE_NONCE_LABEL_STR, BASE_NONCE_LABEL_STR_LEN, key_schedule_context, 1 + 2 * hpke->Nh, hpke->Nn, context->base_nonce); @@ -738,7 +752,7 @@ static int wc_HpkeKeyScheduleBase(Hpke* hpke, HpkeBaseContext* context, /* expand exporter_secret */ if (ret == 0) { ret = wc_HpkeLabeledExpand(hpke, hpke->hpke_suite_id, - sizeof( hpke->hpke_suite_id ), secret, hpke->Nh, + sizeof( hpke->hpke_suite_id ), hpke->kdfDigest, secret, hpke->Nh, (byte*)EXP_LABEL_STR, EXP_LABEL_STR_LEN, key_schedule_context, 1 + 2 * hpke->Nh, hpke->Nh, context->exporter_secret); } @@ -757,7 +771,7 @@ static int wc_HpkeEncap(Hpke* hpke, void* ephemeralKey, void* receiverKey, byte* sharedSecret) { int ret; -#ifdef ECC_TIMING_RESISTANT +#if defined(ECC_TIMING_RESISTANT) && defined(HAVE_ECC) WC_RNG* rng; #endif word32 dh_len; @@ -818,15 +832,17 @@ static int wc_HpkeEncap(Hpke* hpke, void* ephemeralKey, void* receiverKey, #endif break; #endif -#if defined(HAVE_CURVE25519) +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) case DHKEM_X25519_HKDF_SHA256: ret = wc_curve25519_shared_secret_ex((curve25519_key*)ephemeralKey, (curve25519_key*)receiverKey, dh, &dh_len, EC25519_LITTLE_ENDIAN); break; #endif +#if defined(HAVE_CURVE448) && defined(WOLFSSL_SHA512) case DHKEM_X448_HKDF_SHA512: /* TODO: Add X448 */ +#endif default: ret = -1; break; @@ -1051,7 +1067,7 @@ static int wc_HpkeDecap(Hpke* hpke, void* receiverKey, const byte* pubKey, #endif break; #endif -#if defined(HAVE_CURVE25519) +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) case DHKEM_X25519_HKDF_SHA256: #ifdef WOLFSSL_CURVE25519_BLINDING rng = wc_rng_new(NULL, 0, hpke->heap); @@ -1071,15 +1087,17 @@ static int wc_HpkeDecap(Hpke* hpke, void* receiverKey, const byte* pubKey, #endif break; #endif +#if defined(HAVE_CURVE448) && defined(WOLFSSL_SHA512) case DHKEM_X448_HKDF_SHA512: /* TODO: Add X448 */ +#endif default: ret = -1; break; } if (ephemeralKey != NULL) - wc_HpkeFreeKey(hpke, (word16)hpke->kem, ephemeralKey, hpke->heap); + wc_HpkeFreeKey(hpke, hpke->kem, ephemeralKey, hpke->heap); if (ret == 0) { /* copy pubKey into kemContext */ @@ -1222,4 +1240,102 @@ int wc_HpkeOpenBase(Hpke* hpke, void* receiverKey, const byte* pubKey, return ret; } +/* return the encrypted length of the KEM + * return 0 otherwise */ +WOLFSSL_LOCAL word16 wc_HpkeKemGetEncLen(word16 kemId) +{ + switch (kemId) + { +#if defined(HAVE_ECC) +#if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && !defined(NO_SHA256) + case DHKEM_P256_HKDF_SHA256: + return DHKEM_P256_ENC_LEN; +#endif +#if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA384) + case DHKEM_P384_HKDF_SHA384: + return DHKEM_P384_ENC_LEN; +#endif +#if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA512) + case DHKEM_P521_HKDF_SHA512: + return DHKEM_P521_ENC_LEN; +#endif +#endif /* HAVE_ECC */ +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) + case DHKEM_X25519_HKDF_SHA256: + return DHKEM_X25519_ENC_LEN; +#endif + default: + return 0; + } +} + +/* return true if hpke is compiled with support for the given KEM + * return false otherwise */ +WOLFSSL_LOCAL int wc_HpkeKemIsSupported(word16 kemId) +{ + switch (kemId) { +#if defined(HAVE_ECC) +#if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && !defined(NO_SHA256) + case DHKEM_P256_HKDF_SHA256: +#endif +#if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA384) + case DHKEM_P384_HKDF_SHA384: +#endif +#if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA512) + case DHKEM_P521_HKDF_SHA512: +#endif +#endif /* HAVE_ECC */ +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) + case DHKEM_X25519_HKDF_SHA256: +#endif + return 1; + + default: + return 0; + } +} + +/* return true if hpke is compiled with support for the given KDF + * return false otherwise */ +WOLFSSL_LOCAL int wc_HpkeKdfIsSupported(word16 kdfId) +{ + switch (kdfId) { +#if !defined(NO_SHA256) + case HKDF_SHA256: +#endif +#ifdef WOLFSSL_SHA384 + case HKDF_SHA384: +#endif +#ifdef WOLFSSL_SHA512 + case HKDF_SHA512: +#endif + return 1; + + default: + return 0; + } +} + +/* return true if hpke is compiled with support for the given AEAD + * return false otherwise */ +WOLFSSL_LOCAL int wc_HpkeAeadIsSupported(word16 aeadId) +{ + switch (aeadId) { +#ifdef WOLFSSL_AES_128 + case HPKE_AES_128_GCM: +#endif +#ifdef WOLFSSL_AES_256 + case HPKE_AES_256_GCM: +#endif + return 1; + + default: + return 0; + } +} + #endif /* HAVE_HPKE && (HAVE_ECC || HAVE_CURVE25519) && HAVE_AESGCM */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index cad604a6bed..7687c584f72 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -639,8 +639,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t sshkdf_test(void); WOLFSSL_TEST_SUBROUTINE wc_test_ret_t tls13_kdf_test(void); #endif WOLFSSL_TEST_SUBROUTINE wc_test_ret_t x963kdf_test(void); -#if defined(HAVE_HPKE) && defined(HAVE_ECC) && defined(HAVE_AESGCM) && \ - defined(WOLFSSL_AES_256) +#if defined(HAVE_HPKE) && \ + (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \ + defined(HAVE_CURVE448)) && \ + defined(HAVE_AESGCM) WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void); #endif #ifdef WC_SRTP_KDF @@ -2360,8 +2362,10 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("X963-KDF test passed!\n"); #endif -#if defined(HAVE_HPKE) && defined(HAVE_ECC) && defined(HAVE_AESGCM) && \ - defined(WOLFSSL_AES_256) +#if defined(HAVE_HPKE) && \ + (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \ + defined(HAVE_CURVE448)) && \ + defined(HAVE_AESGCM) PRIVATE_KEY_UNLOCK(); if ( (ret = hpke_test()) != 0) TEST_FAIL("HPKE test failed!\n", ret); @@ -31859,8 +31863,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t x963kdf_test(void) #endif /* HAVE_X963_KDF */ #if defined(HAVE_HPKE) && \ - (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \ - defined(HAVE_AESGCM) && defined(WOLFSSL_AES_256) + (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \ + defined(HAVE_CURVE448)) && \ + defined(HAVE_AESGCM) static wc_test_ret_t hpke_test_single(Hpke* hpke) { @@ -32089,8 +32094,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) Hpke hpke[1]; WOLFSSL_ENTER("hpke_test"); -#if defined(HAVE_ECC) - #if defined(WOLFSSL_SHA224) || !defined(NO_SHA256) +#if defined(HAVE_ECC) && defined(WOLFSSL_AES_128) + #if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && !defined(NO_SHA256) /* p256 */ ret = wc_HpkeInit(hpke, DHKEM_P256_HKDF_SHA256, HKDF_SHA256, HPKE_AES_128_GCM, NULL); @@ -32102,11 +32107,26 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) ret = hpke_test_multi(hpke); if (ret != 0) return ret; + #endif + #if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ + !defined(NO_SHA256) && defined(WOLFSSL_SHA512) + /* p256 with sha512 kdf */ + ret = wc_HpkeInit(hpke, DHKEM_P256_HKDF_SHA256, HKDF_SHA512, + HPKE_AES_128_GCM, NULL); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + ret = hpke_test_single(hpke); + if (ret != 0) + return ret; + ret = hpke_test_multi(hpke); + if (ret != 0) + return ret; #endif - #if defined(WOLFSSL_SHA384) && \ - (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) + + #if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA384) /* p384 */ ret = wc_HpkeInit(hpke, DHKEM_P384_HKDF_SHA384, HKDF_SHA384, HPKE_AES_128_GCM, NULL); @@ -32120,8 +32140,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) return ret; #endif - #if (defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512)) && \ - (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) + #if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA512) /* p521 */ ret = wc_HpkeInit(hpke, DHKEM_P521_HKDF_SHA512, HKDF_SHA512, HPKE_AES_128_GCM, NULL); @@ -32134,9 +32154,24 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) if (ret != 0) return ret; #endif + + #if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ + defined(WOLFSSL_SHA384) && defined(WOLFSSL_SHA512) + /* p521 with sha384 kdf */ + ret = wc_HpkeInit(hpke, DHKEM_P521_HKDF_SHA512, HKDF_SHA384, + HPKE_AES_128_GCM, NULL); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + ret = hpke_test_single(hpke); + if (ret != 0) + return ret; + ret = hpke_test_multi(hpke); + if (ret != 0) + return ret; + #endif #endif -#if defined(HAVE_CURVE25519) +#if defined(HAVE_CURVE25519) && !defined(NO_SHA256) && defined(WOLFSSL_AES_256) /* test with curve25519 and aes256 */ ret = wc_HpkeInit(hpke, DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, HPKE_AES_256_GCM, NULL); @@ -32151,8 +32186,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) #endif -#if defined(HAVE_CURVE448) && \ - (defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512)) +#if defined(HAVE_CURVE448) && defined(WOLFSSL_SHA512) && \ + defined(WOLFSSL_AES_256) /* test with curve448 and aes256 */ ret = wc_HpkeInit(hpke, DHKEM_X448_HKDF_SHA512, HKDF_SHA512, HPKE_AES_256_GCM, NULL); @@ -32173,7 +32208,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void) return ret; } -#endif /* HAVE_HPKE && HAVE_ECC && HAVE_AESGCM && WOLFSSL_AES_256 */ +#endif /* HAVE_HPKE && (HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448) && + HAVE_AESGCM */ #if defined(WC_SRTP_KDF) typedef struct Srtp_Kdf_Tv { diff --git a/wolfcrypt/test/test.h b/wolfcrypt/test/test.h index b1b1b5feb9c..ddaea0f310f 100644 --- a/wolfcrypt/test/test.h +++ b/wolfcrypt/test/test.h @@ -163,7 +163,10 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t sshkdf_test(void); extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t tls13_kdf_test(void); #endif extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t x963kdf_test(void); -#if defined(HAVE_HPKE) && defined(HAVE_ECC) && defined(HAVE_AESGCM) +#if defined(HAVE_HPKE) && \ + (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \ + defined(HAVE_CURVE448)) && \ + defined(HAVE_AESGCM) extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hpke_test(void); #endif #ifdef WC_SRTP_KDF diff --git a/wolfssl/wolfcrypt/hpke.h b/wolfssl/wolfcrypt/hpke.h index 307f46b6ea9..c71619ccf79 100644 --- a/wolfssl/wolfcrypt/hpke.h +++ b/wolfssl/wolfcrypt/hpke.h @@ -31,7 +31,8 @@ extern "C" { #endif -#if defined(HAVE_HPKE) && defined(HAVE_ECC) +#if defined(HAVE_HPKE) && (defined(HAVE_ECC) || defined(HAVE_CURVE25519)) && \ + defined(HAVE_AESGCM) #ifndef WOLFCRYPT_HPKE #define WOLFCRYPT_HPKE @@ -64,14 +65,6 @@ enum { HPKE_AES_256_GCM = 0x0002 }; -/* TODO better way of doing this */ -#define HPKE_SUPPORTED_KEM_LEN 4 -#define HPKE_SUPPORTED_KDF_LEN 3 -#define HPKE_SUPPORTED_AEAD_LEN 2 -extern const int hpkeSupportedKem[HPKE_SUPPORTED_KEM_LEN]; -extern const int hpkeSupportedKdf[HPKE_SUPPORTED_KDF_LEN]; -extern const int hpkeSupportedAead[HPKE_SUPPORTED_AEAD_LEN]; - #define HPKE_Nh_MAX 64 #define HPKE_Nk_MAX 32 #define HPKE_Nn_MAX 12 @@ -88,9 +81,6 @@ extern const int hpkeSupportedAead[HPKE_SUPPORTED_AEAD_LEN]; typedef struct { void* heap; - word32 kem; - word32 kdf; - word32 aead; word32 Nh; word32 Nk; word32 Nn; @@ -98,8 +88,12 @@ typedef struct { word32 Ndh; word32 Npk; word32 Nsecret; - int kdf_digest; - int curve_id; + int kdfDigest; + int kemDigest; + int curveId; + word16 kem; + word16 kdf; + word16 aead; byte kem_suite_id[KEM_SUITE_ID_LEN]; byte hpke_suite_id[HPKE_SUITE_ID_LEN]; } Hpke; @@ -136,9 +130,14 @@ WOLFSSL_API int wc_HpkeOpenBase(Hpke* hpke, void* receiverKey, const byte* pubKey, word16 pubKeySz, byte* info, word32 infoSz, byte* aad, word32 aadSz, byte* ciphertext, word32 ctSz, byte* plaintext); +WOLFSSL_LOCAL word16 wc_HpkeKemGetEncLen(word16 kemId); +WOLFSSL_LOCAL int wc_HpkeKemIsSupported(word16 kemId); +WOLFSSL_LOCAL int wc_HpkeKdfIsSupported(word16 kdfId); +WOLFSSL_LOCAL int wc_HpkeAeadIsSupported(word16 aeadId); + #endif -#endif /* HAVE_HPKE && HAVE_ECC */ +#endif /* HAVE_HPKE && (HAVE_ECC || HAVE_CURVE25519) && HAVE_AESGCM */ #ifdef __cplusplus } /* extern "C" */