From 19dcdb30a01aca7745e7f5747fcf43b4c1a22dc5 Mon Sep 17 00:00:00 2001 From: sebastian-carpenter Date: Mon, 8 Jun 2026 10:42:47 -0600 Subject: [PATCH] ECH multi-sni and simplifications --- .github/scripts/openssl-ech.sh | 89 +++- certs/ech-public-cert.der | Bin 0 -> 1233 bytes certs/ech-public-cert.pem | 91 ++++ certs/ech-public-key.der | Bin 0 -> 1216 bytes certs/ech-public-key.pem | 28 ++ certs/include.am | 8 + certs/renewcerts.sh | 75 +++ certs/renewcerts/wolfssl.cnf | 23 + certs/tenant-a-cert.der | Bin 0 -> 1250 bytes certs/tenant-a-cert.pem | 92 ++++ certs/tenant-b-cert.der | Bin 0 -> 1250 bytes certs/tenant-b-cert.pem | 92 ++++ examples/client/client.c | 5 +- examples/server/server.c | 174 ++++++- gencertbuf.pl | 4 +- src/internal.c | 1 + src/ssl.c | 7 + src/ssl_ech.c | 107 +++++ src/tls.c | 46 +- src/tls13.c | 77 ++- tests/api.c | 842 +++++++++++++++++++++++---------- wolfssl/certs_test.h | 258 ++++++++++ wolfssl/internal.h | 6 +- wolfssl/test.h | 43 ++ 24 files changed, 1722 insertions(+), 346 deletions(-) create mode 100644 certs/ech-public-cert.der create mode 100644 certs/ech-public-cert.pem create mode 100644 certs/ech-public-key.der create mode 100644 certs/ech-public-key.pem create mode 100644 certs/tenant-a-cert.der create mode 100644 certs/tenant-a-cert.pem create mode 100644 certs/tenant-b-cert.der create mode 100644 certs/tenant-b-cert.pem diff --git a/.github/scripts/openssl-ech.sh b/.github/scripts/openssl-ech.sh index 555cfbbb0c2..1803ff1b090 100644 --- a/.github/scripts/openssl-ech.sh +++ b/.github/scripts/openssl-ech.sh @@ -70,6 +70,13 @@ if [ "$FORCE_HRR" -ne 0 ] && [ -n "$PQC" ]; then exit 1 fi +# corrupt_ech_config() flips the first public-key byte, this is valid only for +# the default X25519 KEM so consider --suite and --reject mutually exclusive +if [ "$REJECT" -ne 0 ] && [ -n "$SUITE" ]; then + echo "ERROR: --reject only supports the default X25519 suite" + exit 1 +fi + # Pick exactly one test variant. The variant decides which -groups go to # each side and any extra flags needed to drive the desired handshake. # default - both sides use secp256r1 (no HRR) @@ -89,18 +96,35 @@ WOLFSSL_CLIENT=${WOLFSSL_CLIENT:-"$WORKSPACE/examples/client/client"} WOLFSSL_SERVER=${WOLFSSL_SERVER:-"$WORKSPACE/examples/server/server"} CERT_DIR=${CERT_DIR:-"$WORKSPACE/certs"} -# correct ECH config, but it's old, ECH will be rejected -REJECT_ECH_CONFIG="AD7+DQA6rAAgACCATZdDlHed6GlDeiYsu3r7sdWUkLVHZuTa3lbOf+hIbAAEAAEAAQALZXhhbXBsZS5jb20AAA==" - TMP_LOG="$WORKSPACE/tmp_file.log" -# Will need to look into validating the name against the cert for the OSSL cli. -# This is fine, but should be upgraded to use a second cert in the future. -PRIV_NAME="ech-private-name.com" -# example.com is taken from the server certificate, -# echConfigs needs to authenticate against the cert with this name to succeed -PUB_NAME="example.com" +# private inner name; matches the private cert swapped in once ECH is accepted +PRIV_NAME="example.com" +# ECH public name; matches certs/ech-public-cert.pem, the public-facing cert the +# outer (or a rejected) handshake authenticates against +PUB_NAME="public.com" MAX_WAIT=50 +# -------------------------------------------------------------------------- +# Flip a bit in the HPKE public key of the config the server published. The +# client will offer ECH, but the server can't decrypt so ECH is rejected. +# -------------------------------------------------------------------------- +corrupt_ech_config() { + local config="$1" + local bytes=() + local b + + mapfile -t bytes < <(printf '%s' "$config" | base64 -d | od -An -tx1 -v \ + | tr -s ' ' '\n' | grep -v '^$') + + # list len (2) + version (2) + config len (2) + config_id (1) + + # kem_id (2) + public key len (2), so byte 11 is the first key byte + bytes[11]=$(printf '%02x' $(( 0x${bytes[11]} ^ 0x01 ))) + + for b in "${bytes[@]}"; do + printf "\\x$b" + done | base64 -w 0 +} + # -------------------------------------------------------------------------- # server mode -- OpenSSL is the server, wolfSSL is the client # -------------------------------------------------------------------------- @@ -140,16 +164,22 @@ openssl_server(){ # parse ECH config from file ech_config=$(sed -n '/BEGIN ECHCONFIG/,/END ECHCONFIG/{/BEGIN ECHCONFIG\|END ECHCONFIG/d;p}' "$ech_file" | tr -d '\n') - # reject overrides the config the client connects with - [ "$REJECT" -ne 0 ] && ech_config="$REJECT_ECH_CONFIG" echo "parsed ech config: $ech_config" &>> "$TMP_LOG" + # reject: corrupt the config so the server can't decrypt the client's ECH + if [ "$REJECT" -ne 0 ]; then + ech_config=$(corrupt_ech_config "$ech_config") + echo "bad ech config : $ech_config" &>> "$TMP_LOG" + fi + # start OpenSSL ECH server with ephemeral port; line-buffer so the # log can be grepped - stdbuf -oL $OPENSSL s_server \ + # -cert/-key is the public-name cert served on the outer/rejected handshake; + # -cert2/-key2 is the private inner cert switched to on ECH acceptance. + timeout 30 stdbuf -oL $OPENSSL s_server \ -tls1_3 \ - -cert "$CERT_DIR/server-cert.pem" \ - -key "$CERT_DIR/server-key.pem" \ + -cert "$CERT_DIR/ech-public-cert.pem" \ + -key "$CERT_DIR/ech-public-key.pem" \ -cert2 "$CERT_DIR/server-cert.pem" \ -key2 "$CERT_DIR/server-key.pem" \ -ech_key "$ech_file" \ @@ -184,11 +214,16 @@ openssl_server(){ $wolfssl_extra \ &>> "$TMP_LOG" || [ "$REJECT" -ne 0 ] + # let s_server finish writing its ech_success= line before grepping; + # on reject it sees a fatal alert, so tolerate a nonzero exit + wait || [ "$REJECT" -ne 0 ] + if [ "$REJECT" -ne 0 ]; then - grep -q "ECH offered but rejected by server" "$TMP_LOG" && \ - grep -q "ech_success=0" "$TMP_LOG" + grep -q "ech_success=0" "$TMP_LOG" && \ + grep -q "ECH status: rejected" "$TMP_LOG" else - grep -q "ech_success=1" "$TMP_LOG" + grep -q "ech_success=1" "$TMP_LOG" && \ + grep -q "ECH status: accepted" "$TMP_LOG" fi } @@ -231,7 +266,7 @@ openssl_client(){ # start server with ephemeral port + ready file; line-buffer so the # log can be grepped - stdbuf -oL $WOLFSSL_SERVER \ + timeout 30 stdbuf -oL $WOLFSSL_SERVER \ -v 4 \ -R "$ready_file" \ -p "$port" \ @@ -267,10 +302,14 @@ openssl_client(){ exit 1 fi done - # reject overrides the config the client connects with - [ "$REJECT" -ne 0 ] && ech_config="$REJECT_ECH_CONFIG" echo "parsed ech config: $ech_config" &>> "$TMP_LOG" + # reject: corrupt the config so the server can't decrypt the client's ECH + if [ "$REJECT" -ne 0 ]; then + ech_config=$(corrupt_ech_config "$ech_config") + echo "bad ech config : $ech_config" &>> "$TMP_LOG" + fi + # test with OpenSSL s_client using ECH # in reject mode the s_client is expected to error out, so tolerate a # nonzero exit @@ -285,10 +324,16 @@ openssl_client(){ $openssl_groups \ &>> "$TMP_LOG" || [ "$REJECT" -ne 0 ] + # let the wolfSSL server finish writing its ECH status line before + # grepping; on reject it errors out, so tolerate a nonzero exit + wait || [ "$REJECT" -ne 0 ] + if [ "$REJECT" -ne 0 ]; then - grep -q "ECH: Got 1 retry-configs" "$TMP_LOG" + grep -q "ECH: Got 1 retry-configs" "$TMP_LOG" && \ + grep -q "ECH status: rejected" "$TMP_LOG" else - grep -q "ECH: success: 1" "$TMP_LOG" + grep -q "ECH: success: 1" "$TMP_LOG" && \ + grep -q "ECH status: accepted" "$TMP_LOG" fi } diff --git a/certs/ech-public-cert.der b/certs/ech-public-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..9d360437d92a6b3fd72df26e9b2f588ecdfb8957 GIT binary patch literal 1233 zcmXqLVmWKj#JqI@GZP~d6Qi;LFB_*;n@8JsUPeZ4RtAH{sfOGJoNUaYENsF|p}~d% z27Dk62M@b%eqKppULs6{orm2izbZ91G0#xYfFGoWi-#jPvAiTdza+y@*gy~@#LdIy zoS#=*np2XQmu@IwAO;d-=HV|dFV`#2&q*sT&e2QG&oxvqkb^selTl1CEit*I*a1m} zft)z6k(q(Hp@ETssfnRk6p(9aU}9hjk`xx>Va6z2S?CR_R z^8_;wS3zk~PG&OLsqzN0$W9f=%uCBhacL8y5^|t2vNA9?G4eAQG%<29H8Ca8?OJO9aFGjG8p z0oE*Yok}KVMh3>kO^ims5YYn$CUdAPABz}^NcIhZ)RR*(l(pp2)6Y$io76iYNYkM4 zDo9?LrSYsmVCBwg~-iu%X8 zze=n8pKoHix%NnT+z)ULlNDxRGGH(e1^G~rh0lPe2`QZ!2!n)GSwJb0Lz|6}m6e^D z5jkA}Gb=D%F)}1~ecZh0$uo_t0?&osDPY)AUTz9X#qf_8= zb~D%8b(6SxSu3?P7F@Nm`TpqqJst@Mlhm`5 z-ESN8XNx&m*1d7xJIhXzb6Qr%&Y6>9@2&s(J9CHg2X(!xQ8!N-9QonQ_{x#x_1}0M Zz8VcdN57Mo(``IDnY4YLg=}Zw0RZ7qvq%5{ literal 0 HcmV?d00001 diff --git a/certs/ech-public-cert.pem b/certs/ech-public-cert.pem new file mode 100644 index 00000000000..0821c35b583 --- /dev/null +++ b/certs/ech-public-cert.pem @@ -0,0 +1,91 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 35 (0x23) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com, emailAddress=facts@wolfssl.com + Validity + Not Before: Jul 10 20:54:16 2026 GMT + Not After : Apr 5 20:54:16 2029 GMT + Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=ECH, CN=public.com, emailAddress=info@wolfssl.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d1:42:85:b2:a7:81:6f:e7:1b:26:c6:f2:53:d1: + 42:ff:18:f1:0b:3f:75:a0:01:fa:5c:8f:b2:c7:b4: + 06:f5:a5:6e:e2:ae:cc:a5:5d:35:b4:56:28:21:d3: + 59:b1:6e:81:3f:69:7d:85:e9:33:0a:cd:7a:32:8a: + fc:41:5a:52:1e:23:9e:2a:22:d4:c9:20:7e:46:db: + 6c:4d:56:e8:18:aa:79:b7:e7:20:b9:f3:97:28:1d: + 63:44:03:b4:05:7b:03:93:4c:3f:83:8e:21:c4:4e: + f2:ee:28:8c:01:39:de:aa:64:57:34:9a:58:35:89: + c1:eb:4e:fc:8e:23:fd:10:ad:1e:ca:97:4e:a9:8f: + 5d:d7:f9:52:fd:3a:45:90:cd:21:97:bc:b4:e2:8e: + c2:60:16:43:51:b3:71:e6:54:ec:60:00:76:b7:f6: + 06:b1:0c:c9:93:d7:6c:c6:9e:ab:0d:4d:58:f9:d5: + c7:fc:d1:d9:53:66:b7:47:4b:4a:12:6b:37:23:b4: + df:f6:21:d4:23:6b:92:2d:f7:ca:e5:90:53:c7:84: + 29:c0:a0:ac:d5:31:73:72:05:27:55:f6:4d:1a:c9: + e5:da:d9:8e:54:37:77:9c:3b:7f:2a:b9:72:6f:ed: + 2e:a9:36:66:cf:f2:4f:29:6e:a0:92:10:05:6a:37: + 2c:79 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 6B:D8:10:65:C9:94:68:23:2A:1E:67:67:CE:90:1E:92:8D:90:52:29 + X509v3 Authority Key Identifier: + keyid:27:8E:67:11:74:C3:26:1D:3F:ED:33:63:B3:A4:D8:1D:30:E5:E8:D5 + DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=facts@wolfssl.com + serial:67:19:D2:A8:7F:E3:2D:FA:75:7A:4F:E7:B2:02:D9:AD:C4:77:5E:F8 + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Alternative Name: + DNS:public.com + X509v3 Extended Key Usage: + TLS Web Server Authentication + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 63:8a:f1:b3:a2:e4:e6:28:6a:63:c5:39:56:71:65:95:3d:08: + 86:f6:ee:aa:a2:61:9d:f3:77:31:f2:e1:61:a8:d7:47:7e:dc: + 2c:70:44:cd:b3:0a:ed:ae:5b:5b:bf:d6:4c:70:47:98:66:40: + 8f:f4:0e:4b:f4:7e:fa:45:87:9e:fb:70:fc:e4:7a:3b:ff:da: + 5f:57:8b:32:75:4f:ec:6b:ad:95:58:44:18:31:b7:45:77:76: + d4:e6:96:66:b1:63:42:df:86:c1:f4:0b:53:f8:51:8a:71:b0: + 99:ea:7a:16:27:0e:22:5a:ac:e9:c5:46:0b:06:16:e9:ba:85: + ae:2f:ae:56:f9:cc:de:69:8c:e1:0c:e6:38:93:f2:21:88:a9: + 0d:25:9b:5a:cc:45:f6:6a:e8:d5:d8:f6:69:2e:fa:b0:cc:09: + 76:3b:9a:fc:ab:4e:9a:a9:2a:28:a0:d5:3a:3c:f7:e2:cf:de: + 0c:18:40:34:65:cd:93:47:db:30:8f:6b:16:42:39:7e:ec:47: + bd:9a:3e:19:09:96:6a:88:b9:99:92:5d:de:af:f5:fb:69:b8: + 43:f0:27:2e:d5:5a:d9:c9:30:c4:f8:43:01:ea:41:04:eb:fd: + 5f:2c:0e:7c:28:11:41:4e:c9:d3:67:3c:48:89:02:2b:4c:e6: + 54:b7:00:0c +-----BEGIN CERTIFICATE----- +MIIEzTCCA7WgAwIBAgIBIzANBgkqhkiG9w0BAQsFADCBlTELMAkGA1UEBhMCVVMx +EDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNh +d3Rvb3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNz +bC5jb20xIDAeBgkqhkiG9w0BCQEWEWZhY3RzQHdvbGZzc2wuY29tMB4XDTI2MDcx +MDIwNTQxNloXDTI5MDQwNTIwNTQxNlowgYcxCzAJBgNVBAYTAlVTMRAwDgYDVQQI +DAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMQww +CgYDVQQLDANFQ0gxEzARBgNVBAMMCnB1YmxpYy5jb20xHzAdBgkqhkiG9w0BCQEW +EGluZm9Ad29sZnNzbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDRQoWyp4Fv5xsmxvJT0UL/GPELP3WgAfpcj7LHtAb1pW7irsylXTW0Vigh01mx +boE/aX2F6TMKzXoyivxBWlIeI54qItTJIH5G22xNVugYqnm35yC585coHWNEA7QF +ewOTTD+DjiHETvLuKIwBOd6qZFc0mlg1icHrTvyOI/0QrR7Kl06pj13X+VL9OkWQ +zSGXvLTijsJgFkNRs3HmVOxgAHa39gaxDMmT12zGnqsNTVj51cf80dlTZrdHS0oS +azcjtN/2IdQja5It98rlkFPHhCnAoKzVMXNyBSdV9k0ayeXa2Y5UN3ecO38quXJv +7S6pNmbP8k8pbqCSEAVqNyx5AgMBAAGjggEyMIIBLjAdBgNVHQ4EFgQUa9gQZcmU +aCMqHmdnzpAeko2QUikwgdUGA1UdIwSBzTCByoAUJ45nEXTDJh0/7TNjs6TYHTDl +6NWhgZukgZgwgZUxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYD +VQQHDAdCb3plbWFuMREwDwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3Vs +dGluZzEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMSAwHgYJKoZIhvcNAQkBFhFm +YWN0c0B3b2xmc3NsLmNvbYIUZxnSqH/jLfp1ek/nsgLZrcR3XvgwCQYDVR0TBAIw +ADAVBgNVHREEDjAMggpwdWJsaWMuY29tMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0G +CSqGSIb3DQEBCwUAA4IBAQBjivGzouTmKGpjxTlWcWWVPQiG9u6qomGd83cx8uFh +qNdHftwscETNswrtrltbv9ZMcEeYZkCP9A5L9H76RYee+3D85Ho7/9pfV4sydU/s +a62VWEQYMbdFd3bU5pZmsWNC34bB9AtT+FGKcbCZ6noWJw4iWqzpxUYLBhbpuoWu +L65W+czeaYzhDOY4k/IhiKkNJZtazEX2aujV2PZpLvqwzAl2O5r8q06aqSoooNU6 +PPfiz94MGEA0Zc2TR9swj2sWQjl+7Ee9mj4ZCZZqiLmZkl3er/X7abhD8Ccu1VrZ +yTDE+EMB6kEE6/1fLA58KBFBTsnTZzxIiQIrTOZUtwAM +-----END CERTIFICATE----- diff --git a/certs/ech-public-key.der b/certs/ech-public-key.der new file mode 100644 index 0000000000000000000000000000000000000000..c1aedd2f05f3cb7fd001c62beebf14110b9da607 GIT binary patch literal 1216 zcmV;x1V8&Qf&{z*0RS)!1_>&LNQUrrZ9p8q5=T`0)hbn0MSB)vZsM> z=Nl%*@>9`5{}}NLKXsr1`dp8)$Fv6ZrEcP`%%xp5v{on~(^;`@fj?<|h3PX2&3ZD5 z{6Sh$9wVMABGkzsen#7DO;+d_s(H8PAi49GC>>)&1GEKu1CvZYgN`A@PV(+3i~%{` zs$^F*npic7!Rt=^jwAgLtscskPN|Pw*ZETYIz^DpA(y#7DG|9apqL)U;uWv z_6D&G$&=S?#-6JUO<4KW$NbURQ)ahEOG*-JHzTy)_94_GYmzPZ%H@z#$Al@spsdv~ zb8-bIRrXC9$>rMFj#M{yoI8IixpHsqE~z$V&+<)6g009Dm0RUy7 z6jczDL|Ck^cGJzzx*)N7xz!~n z9Y*Z?fj=ki&;6Se85~2NKKc65R!ozHpBb0F3{xXQSzD)1(>UIaNo}_pq_?2NZ)91- z0);`@)P7$%;GWazd`|30mPCE5>D))S zn#mDu#wp-&!8AlY)E+DA?GT+#ynyRKiSQ^Dklxhy9dL8Bi6LP(Ivr1zPc3|pr`Mne z*h6-$9fdJJ^Fq<#tg!G zS{lT#3TH0KuWTUC;R4_OJooBY{&SN#`~Gg(v-DrGv}=t9BoE2CdWv0)(4F@rtR-hC z7aWv>kDJ5_84$}ptCcSvzU9#9E?IS@4SbYYRg?g0i^6$|HYk2Ol>)aoD@81KJ$nLy zfMLDO!OkI#oq`W3iyzKxQf(h8d!EzaA(Q_T*w@&2iy{)3Q|b9zN>q$IU3R549mtO# zpd+PQPrl)*$|qO->AG1NkoaWdsGy&~%BMp?t6knMxEaT01e$XDA1ilLy^eUFt-~21 zC(u_w-LR2WKETduXXW%+`I0rLQQqO8XYpfa3iSlo@5tj2%K z0)c>1>=@ty{<0~&(zB85=(1ciJcjeKyQvPdv+h*%N2)PxJqRL z(sd>sye7GvJ2g+7`htfdFi!~sM0NW|te#Wdp};_7?`s${x2H^LE_6%iu&~;duUcP? eNSP<(A_vM|s!`P5NlI%2V0SA?Wi4##Id%Th=u;#B literal 0 HcmV?d00001 diff --git a/certs/ech-public-key.pem b/certs/ech-public-key.pem new file mode 100644 index 00000000000..26c797915f7 --- /dev/null +++ b/certs/ech-public-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDRQoWyp4Fv5xsm +xvJT0UL/GPELP3WgAfpcj7LHtAb1pW7irsylXTW0Vigh01mxboE/aX2F6TMKzXoy +ivxBWlIeI54qItTJIH5G22xNVugYqnm35yC585coHWNEA7QFewOTTD+DjiHETvLu +KIwBOd6qZFc0mlg1icHrTvyOI/0QrR7Kl06pj13X+VL9OkWQzSGXvLTijsJgFkNR +s3HmVOxgAHa39gaxDMmT12zGnqsNTVj51cf80dlTZrdHS0oSazcjtN/2IdQja5It +98rlkFPHhCnAoKzVMXNyBSdV9k0ayeXa2Y5UN3ecO38quXJv7S6pNmbP8k8pbqCS +EAVqNyx5AgMBAAECggEAZaAUVRCTRFisr3bTzc/lZQTkXy2I/tWnFFe3H9Q2wwp+ +IPl6Kl7ri3KCF/dP6mL7wuOE0clQgBENJMmpu0VVdwyeLeFvjGPK37eFT8QCgKQd +66mEE7qQcKtg/3F69mRo9pqDh+y5SmB7Cx1G7PuBPyfuz/2bFBkcQ54++frRVkyT +hZ8Zl74MUyNCWVunT9M43o5JbbcapLegxG9kWcQChUHVU5U7ZjiSG2eKOWbY7qY0 +8/lwNnr6X1x/SViS07p0tNy02cXBsVWItZb50LtpSdtMSfJkE8Ed6Z5wQWggDle9 +ll6dOUSUvK0zrzq1nk7X/iJ71837/A9ZTFzpEKOyHwKBgQD7J2QjP+u44v/Gn4PK +5f6p3WMvIfqs2dzuXwLR1eq+uUs01H5fOeCe0+l8TuxIlkR9rOncR7iayRFtxing +ccE0RD3UHivr7RCdTryA60CJ8CgVkN7U9x1wc7SJIWE3Oh1Plk8tfI+n16AI2ErF +lVzGb9jEOzkvSOZWLAx7UDDpmwKBgQDVTDCbJfL/5c3pxtPR6egJNLdQ8pVwxgzC +eloaxLAKZy7Jr2wgz+EC3/489+pY/nOTOfv+btmz9F+ytGuNBiQPybl6il2M0J33 +JKwlZygXHJSDj5vEChkQyz6rlS8evuXQ6C5ZdaUNfJRZVZQAa4vCeYo2KH47lQK3 +OCtFLHc9ewKBgGG9zcHOIY2dgg8pix/ObFJtHyl7ntPgIZP/E9jX2HiLIhKYU+n5 +W0pUjDxddqU1HciPH6AjpVtPvuGqyidX/em6WRmQ+GTjqKCfwMqnQ0GrXd4uuBnH +ZgSacvsfK3dTvY54n63DGSEn0FdA3bCRVT7Azmpn5fRZ+ZI1qFHhPnfbAoGAJOXG +LsCU1bGiOkOb1t84tYb6AzXDpjuMb4QM3D6UGWiaDmebM93iFcY7y74zOuvhgGFy +dyQj4t5uQ5K0XDPovxZtUIZpAngAK4WbhejfZYgbJNsN3g7FIUOXdsUa3p21Ubso +cW9JexjG7OFB9gSkq6KsxwugMpxnWNyNl6zGf8sCgYBS7BjgAf6yKbzSs6eZaD3N +dq9Y5GLB/bg6EktrXYmdGZJAb44we3Yf3ssVh1u4SmUC0nUmHbwmuZw7NU+c+oKH +IjBPCQNEdftHrJ5T3aHAQGTvaxgzt6dMaS50S+iwsNqVr1pfjUiZJ+QiB8peqlHU +3klKawNgdytJZS1s6jl1/g== +-----END PRIVATE KEY----- diff --git a/certs/include.am b/certs/include.am index 97d8bfe75a7..c6276dc7759 100644 --- a/certs/include.am +++ b/certs/include.am @@ -45,6 +45,10 @@ EXTRA_DIST += \ certs/tsa-ecc-cert.pem \ certs/tsa-ecc-key.pem \ certs/tsa-key.pem \ + certs/ech-public-cert.pem \ + certs/ech-public-key.pem \ + certs/tenant-a-cert.pem \ + certs/tenant-b-cert.pem \ certs/server-ecc.pem \ certs/server-ecc-self.pem \ certs/server-ecc-comp.pem \ @@ -135,6 +139,10 @@ EXTRA_DIST += \ certs/tsa-extra-eku-cert.der \ certs/tsa-chain-cert.der \ certs/tsa-chain-key.der \ + certs/ech-public-cert.der \ + certs/ech-public-key.der \ + certs/tenant-a-cert.der \ + certs/tenant-b-cert.der \ certs/server-ecc-comp.der \ certs/server-ecc.der \ certs/server-ecc-self.der \ diff --git a/certs/renewcerts.sh b/certs/renewcerts.sh index 1cdff5feb41..5b185714d76 100755 --- a/certs/renewcerts.sh +++ b/certs/renewcerts.sh @@ -50,6 +50,12 @@ # tsa-chain-cert.pem # tsa-chain-cert.der # intermediate/ca-int-cert.der +# ech-public-cert.pem +# ech-public-cert.der +# tenant-a-cert.pem +# tenant-a-cert.der +# tenant-b-cert.pem +# tenant-b-cert.der # updates the following crls: # crl/cliCrl.pem # crl/crl.pem @@ -1184,6 +1190,75 @@ EOF echo "End of section" echo "---------------------------------------------------------------------" + ############################################################ + ########## update and sign ech-public-cert.pem ############ + ############################################################ + echo "Updating ech-public-cert.pem" + echo "" + openssl genrsa -out ech-public-key.pem 2048 + check_result $? "Step 1" + + echo -e "US\\nMontana\\nBozeman\\nwolfSSL\\nECH\\npublic.com\\ninfo@wolfssl.com\\n.\\n.\\n" | openssl req -new -key ech-public-key.pem -config ./wolfssl.cnf -nodes > ech-public-req.pem + check_result $? "Step 2" + + openssl x509 -req -in ech-public-req.pem -extfile wolfssl.cnf -extensions ech_public_opts -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 0x23 > ech-public-cert.pem + check_result $? "Step 3" + + rm ech-public-req.pem + + openssl x509 -in ech-public-cert.pem -text > tmp.pem + check_result $? "Step 4" + mv tmp.pem ech-public-cert.pem + + openssl x509 -inform PEM -in ech-public-cert.pem -outform DER -out ech-public-cert.der + check_result $? "Step 5" + openssl rsa -inform PEM -in ech-public-key.pem -outform DER -out ech-public-key.der + check_result $? "Step 6" + echo "End of section" + echo "---------------------------------------------------------------------" + + ############################################################ + ########## update and sign tenant-a-cert.pem ############## + ############################################################ + echo "Updating tenant-a-cert.pem" + echo "" + echo -e "US\\nMontana\\nBozeman\\nwolfSSL\\nECH demo\\ntenant-a.example\\ninfo@wolfssl.com\\n.\\n.\\n" | openssl req -new -key server-key.pem -config ./wolfssl.cnf -nodes > tenant-a-req.pem + check_result $? "Step 1" + + openssl x509 -req -in tenant-a-req.pem -extfile wolfssl.cnf -extensions ech_tenant_a_opts -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 0x21 > tenant-a-cert.pem + check_result $? "Step 2" + rm tenant-a-req.pem + + openssl x509 -in tenant-a-cert.pem -text > tmp.pem + check_result $? "Step 3" + mv tmp.pem tenant-a-cert.pem + + openssl x509 -inform PEM -in tenant-a-cert.pem -outform DER -out tenant-a-cert.der + check_result $? "Step 4" + echo "End of section" + echo "---------------------------------------------------------------------" + + ############################################################ + ########## update and sign tenant-b-cert.pem ############## + ############################################################ + echo "Updating tenant-b-cert.pem" + echo "" + echo -e "US\\nMontana\\nBozeman\\nwolfSSL\\nECH demo\\ntenant-b.example\\ninfo@wolfssl.com\\n.\\n.\\n" | openssl req -new -key server-key.pem -config ./wolfssl.cnf -nodes > tenant-b-req.pem + check_result $? "Step 1" + + openssl x509 -req -in tenant-b-req.pem -extfile wolfssl.cnf -extensions ech_tenant_b_opts -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 0x22 > tenant-b-cert.pem + check_result $? "Step 2" + rm tenant-b-req.pem + + openssl x509 -in tenant-b-cert.pem -text > tmp.pem + check_result $? "Step 3" + mv tmp.pem tenant-b-cert.pem + + openssl x509 -inform PEM -in tenant-b-cert.pem -outform DER -out tenant-b-cert.der + check_result $? "Step 4" + echo "End of section" + echo "---------------------------------------------------------------------" + ############################################################ #### ML-DSA (FIPS 204) self-signed certificates ### ############################################################ diff --git a/certs/renewcerts/wolfssl.cnf b/certs/renewcerts/wolfssl.cnf index 8ba00360716..1d43bfc7b95 100644 --- a/certs/renewcerts/wolfssl.cnf +++ b/certs/renewcerts/wolfssl.cnf @@ -129,6 +129,29 @@ basicConstraints=CA:true subjectAltName=DNS:example.com, IP:127.0.0.1 extendedKeyUsage=serverAuth, clientAuth +# ECH public name cert, presented when ECH is rejected +[ech_public_opts] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints=CA:false +subjectAltName=DNS:public.com +extendedKeyUsage=serverAuth + +# ECH multi-SNI demo tenant extensions +[ech_tenant_a_opts] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints=CA:false +subjectAltName=DNS:tenant-a.example +extendedKeyUsage=serverAuth + +[ech_tenant_b_opts] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints=CA:false +subjectAltName=DNS:tenant-b.example +extendedKeyUsage=serverAuth + #wolfssl extensions for intermediate CAs [wolfssl_opts_ICA] subjectKeyIdentifier=hash diff --git a/certs/tenant-a-cert.der b/certs/tenant-a-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..1c72249c9aec978ac3e0fc1f12a504d3654df013 GIT binary patch literal 1250 zcmXqLV!3C~#C&W4GZP~d6QiO5FB_*;n@8JsUPeZ4RtAH{sfOGJoNUaYENsF|p}~d% z27Dk62M@b%eqKppULs6{orm2izbZ91G0#xYfFGoWi-#jPvAiTdza+y@*gy~@#LdIy zoS#=*np2XQmu@IwAO;d-=HV|dFV`#2&q*sT&e2QG&oxvqkb^selTl1CEit*I*a1m} zft)z6k(q(Hp@ETssfnRk6p(9aU}9hj8_JR3?n}@^I*+U^E zH8L68n41{+ z84Q{jxtN+585s^t<#-tG_^B{=`z2RLb;i_VuIoPSQ8Cyt^_;%nwQn*VdurzYUXfn^ zFJ?|)>G!Tv?9Wa0`ycMDKA>N-I%S3UpHs2byB;V$Ez--^Yr8SE_|_Wf;GOaI*ClyX z{U0VL2X=F(*spcou~a6;Wb-t}WhEt*^NCZ(1PF-jK}?bK_<9C>LMkWbPEXB(O8(u-1iz)4cXP zydo8Se8GW($rb!A<_1R78jO^uE-c?Ad}(^|qgd6GS-giW6P9e*km=;a`SJ$G9%F^g z5lYKfy*~2rSz`0iOVjS(yx_m~IukP^1LNW*Mhjqwm;eKlIaHR9MT|vcv!Kz*Ni$j= zo&0gZI*-}ulssRbjX~p8ki0TW<5`2oQw<{Oed&TFht*{5-x?=xUUEa$;OUF23ma!I zX`C@w({PhWy5yx5^^bLbl~(yb-^6rt?UC}hAK=U;E6l=Vz+fN^@}VG$h=EWOMrtz< z21%;2@ECBhacHwKva+%>Ga{!hVD1H`E=Gp_T?<6hXYg`##43D#*{E|jXlb%&z5NsE z_^gxeR}&Wh=K958_v)R4-K_k!cfad&Kjoi(;S|5k_2<4#H|?Ts-g$p*t?8C!KbsO~ zTL{-?GV;Xic(`Y)rb6(M8GM`1+NEsvd!lnB|69Sf^Wp{X<6L?h9^Q>uC)cV}b~L}e zJSD|7qV!M6u4$`%UK%@;PuE_2eA3mpNw?+6Zv)1=jlSmrw8_JR3?n}@^I*+U^E zH86X5CdOp*I)(+wN59o`M`lzMFYA$5$&|bPuq3124uQwV&NytzTg@x0+_B^~I z6@7fcfrH5v{4VAOM$;OMl&3B%-z9u$dhw%J)stDghb%2FD&_h0PI4 z%U8WV^6*(=^U+Jw?%%xNzxFy4Gb01z;wDB5V2GFi1Cu#amXAe@MP##}(aA|OS{|MJ zaltx|+3A!#U!RRZ<5iHnGE3uGgT_-0BInJ8XD?}- zF<8@ZlSsPcr4{v$b$^vs`9I&pbaU;I^0*)1%qA<$!eqc;APw@NAd85BP!mRKGY|$z zs5eH+bLUVc|L*~)M!Hn;bd zpv+RHYsMEWm-gIv-ni)WH?zE3e}x((TT?&9pH HPKE suite to use for ech config.\n" " Supplied as 3 integers (ex: 32,1,3)\n", /* 69 */ + "--ech-cert Cert for the ECH public name\n", /* 70 */ + "--ech-key Key for the ECH public name\n", /* 71 */ #endif "\n" "For simpler wolfSSL TLS server examples, visit\n" "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", - /* 70 */ + /* 72 */ NULL, }, #ifndef NO_MULTIBYTE_PRINT @@ -1371,12 +1373,14 @@ static const char* server_usage_msg[][71] = { "--ech-suite HPKE suite to use for ech config.\n" " Supplied as 3 integers (ex: 32,1,3)\n", /* 69 */ + "--ech-cert Cert for the ECH public name\n", /* 70 */ + "--ech-key Key for the ECH public name\n", /* 71 */ #endif "\n" "より簡単なwolfSSL TSL クライアントの例については" "下記にアクセスしてください\n" "https://github.com/wolfSSL/wolfssl-examples/tree/master/tls\n", - /* 70 */ + /* 72 */ NULL, }, #endif @@ -1543,6 +1547,8 @@ static void Usage(void) #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) printf("%s", msg[++msgId]); /* --ech */ printf("%s", msg[++msgId]); /* --ech-suite */ + printf("%s", msg[++msgId]); /* --ech-cert */ + printf("%s", msg[++msgId]); /* --ech-key */ #endif printf("%s", msg[++msgId]); /* Examples repo link */ } @@ -1610,6 +1616,87 @@ static int server_srtp_test(WOLFSSL *ssl, func_args *args) } #endif +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) +/* The cert/key the SNI callback should install */ +typedef struct EchPrivateCert { +#ifdef NO_FILESYSTEM + const unsigned char* cert; + long certSz; + const unsigned char* key; + long keySz; +#else + const char* certFile; + const char* keyFile; +#endif + const char* name; + const char* publicName; +} EchPrivateCert; + +/* SNI callback: swap the private cert/key if ECH is accepted and the name + * is matched */ +static int EchSwapToPrivateCert(WOLFSSL* ssl, int* ad, void* arg) +{ + const EchPrivateCert* priv = (const EchPrivateCert*)arg; + char* name = NULL; + word16 nameLen; + + /* keep the public cert if ECH is not accepted */ + if (wolfSSL_GetEchStatus(ssl) != WOLFSSL_ECH_STATUS_ACCEPTED) + return 0; + +#ifdef NO_FILESYSTEM + if (priv == NULL || priv->cert == NULL || priv->key == NULL) { +#else + if (priv == NULL || priv->certFile == NULL || priv->keyFile == NULL) { +#endif + *ad = internal_error; + return fatal_return; + } + + nameLen = wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, (void**)&name); + if (nameLen == 0) + name = NULL; + + /* the public cert is already in place, keep it for the public name */ + if (priv->publicName != NULL && name != NULL && + nameLen == (word16)XSTRLEN(priv->publicName) && + XSTRCMP(name, priv->publicName) == 0) { + return 0; + } + + if (priv->name != NULL && + (name == NULL || nameLen != (word16)XSTRLEN(priv->name) || + XSTRCMP(name, priv->name) != 0)) { + *ad = unrecognized_name; + return fatal_return; + } + + printf("ECH accepted, serving private cert\n"); + +#if defined(NO_FILESYSTEM) + if (wolfSSL_use_certificate_chain_buffer_format(ssl, priv->cert, + priv->certSz, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS || + wolfSSL_use_PrivateKey_buffer(ssl, priv->key, priv->keySz, + WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) { +#elif defined(WOLFSSL_PEM_TO_DER) + if (wolfSSL_use_certificate_chain_file(ssl, priv->certFile) + != WOLFSSL_SUCCESS || + wolfSSL_use_PrivateKey_file(ssl, priv->keyFile, + WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS) { +#else + if (wolfSSL_use_certificate_chain_file_format(ssl, priv->certFile, + WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS || + wolfSSL_use_PrivateKey_file(ssl, priv->keyFile, + WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) { +#endif + *ad = internal_error; + return fatal_return; + } + + return 0; +} +#endif /* WOLFSSL_TLS13 && HAVE_ECH */ + THREAD_RETURN WOLFSSL_THREAD server_test(void* args) { @@ -1669,6 +1756,8 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) { "ech", 1, 269 }, { "ech-suite", 1, 270 }, + { "ech-cert", 1, 272 }, + { "ech-key", 1, 273 }, #endif #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ !defined(NO_PSK) @@ -1735,6 +1824,12 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) const char* verifyCert; const char* ourCert; const char* ourKey; +#ifdef NO_FILESYSTEM + const unsigned char* ourCertBuf = server_cert_der_2048; + long ourCertBufSz = (long)sizeof_server_cert_der_2048; + const unsigned char* ourKeyBuf = server_key_der_2048; + long ourKeyBufSz = (long)sizeof_server_key_der_2048; +#endif const char* ourDhParam = dhParamFile; tcp_ready* readySignal = NULL; int argc = ((func_args*)args)->argc; @@ -1754,6 +1849,9 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) char* echPublicName = NULL; char* echSuite = NULL; + const char* echPublicCert = echPublicCertFile; + const char* echPublicKey = echPublicKeyFile; + EchPrivateCert echPrivateCert = { 0 }; word16 kemId = 0; word16 kdfId = 0; word16 aeadId = 0; @@ -1928,6 +2026,11 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) (void)usePqc; (void)altPrivKey; (void)usePskWithCerts; +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && defined(NO_FILESYSTEM) + /* the ECH public cert/key files are unused when loading from buffers */ + (void)echPublicCert; + (void)echPublicKey; +#endif #ifdef WOLFSSL_TIRTOS fdOpenSession(Task_self()); @@ -2616,6 +2719,12 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) aeadId = (word16)atoi(echSuite); } + break; + case 272: + echPublicCert = myoptarg; + break; + case 273: + echPublicKey = myoptarg; break; #endif #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && \ @@ -2652,6 +2761,12 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) err_sys_ex(runWithErrors, "Cannot use DTLS with both UDP and SCTP."); } +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && defined(HAVE_PK_CALLBACKS) + if (echPublicName != NULL && pkCallbacks) { + err_sys_ex(runWithErrors, "PK callbacks not tested with ECH."); + } +#endif + /* sort out DTLS versus TLS versions */ if (version == CLIENT_INVALID_VERSION) { if (doDTLS) @@ -2972,12 +3087,36 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) wolfSSL_CTX_set_default_passwd_cb(ctx, PasswordCallBack); #endif +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) + /* with ECH active use the 'public' cert/key, the servername callback swaps + * in the private cert/key once ECH is accepted */ + if (echPublicName != NULL) { + #ifdef NO_FILESYSTEM + echPrivateCert.cert = ourCertBuf; + echPrivateCert.certSz = ourCertBufSz; + echPrivateCert.key = ourKeyBuf; + echPrivateCert.keySz = ourKeyBufSz; + ourCertBuf = ech_public_cert_der_2048; + ourCertBufSz = (long)sizeof_ech_public_cert_der_2048; + ourKeyBuf = ech_public_key_der_2048; + ourKeyBufSz = (long)sizeof_ech_public_key_der_2048; + #else + echPrivateCert.certFile = ourCert; + echPrivateCert.keyFile = ourKey; + ourCert = echPublicCert; + ourKey = echPublicKey; + #endif + echPrivateCert.name = sniHostName; + echPrivateCert.publicName = echPublicName; + } +#endif + #if !defined(NO_CERTS) if ((!usePsk || usePskPlus || usePskWithCerts) && !useAnon && !(loadCertKeyIntoSSLObj == 1)) { #if defined(NO_FILESYSTEM) && defined(USE_CERT_BUFFERS_2048) if (wolfSSL_CTX_use_certificate_chain_buffer_format(ctx, - server_cert_der_2048, sizeof_server_cert_der_2048, + ourCertBuf, ourCertBufSz, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) err_sys_ex(catastrophic, "can't load server cert buffer"); #elif !defined(TEST_LOAD_BUFFER) @@ -3025,8 +3164,8 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #endif /* HAVE_PK_CALLBACKS && TEST_PK_PRIVKEY */ ) { #ifdef NO_FILESYSTEM - if (wolfSSL_CTX_use_PrivateKey_buffer(ctx, server_key_der_2048, - sizeof_server_key_der_2048, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) + if (wolfSSL_CTX_use_PrivateKey_buffer(ctx, ourKeyBuf, + ourKeyBufSz, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) err_sys_ex(catastrophic, "can't load server private key buffer"); #elif !defined(TEST_LOAD_BUFFER) #if defined(WOLFSSL_PEM_TO_DER) @@ -3203,7 +3342,12 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #endif #ifdef HAVE_SNI - if (sniHostName) + /* with ECH rely on the SNI callback to match against the private name */ + if (sniHostName + #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) + && echPublicName == NULL + #endif + ) if (wolfSSL_CTX_UseSNI(ctx, WOLFSSL_SNI_HOST_NAME, sniHostName, (word16) XSTRLEN(sniHostName)) != WOLFSSL_SUCCESS) err_sys_ex(runWithErrors, "UseSNI failed"); @@ -3217,6 +3361,12 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) char* echConfigBase64Ptr; word32 echConfigBase64Len = sizeof(echConfigBase64); + wolfSSL_CTX_set_servername_callback(ctx, EchSwapToPrivateCert); + if (wolfSSL_CTX_set_servername_arg(ctx, &echPrivateCert) + != WOLFSSL_SUCCESS) { + err_sys_ex(runWithErrors, "set_servername_arg failed"); + } + if (wolfSSL_CTX_GenerateEchConfig(ctx, echPublicName, kemId, kdfId, aeadId) != WOLFSSL_SUCCESS) { err_sys_ex(runWithErrors, "GenerateEchConfig failed"); @@ -3353,7 +3503,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) loadCertKeyIntoSSLObj) { #if defined(NO_FILESYSTEM) && defined(USE_CERT_BUFFERS_2048) if (wolfSSL_use_certificate_chain_buffer_format(ssl, - server_cert_der_2048, sizeof_server_cert_der_2048, + ourCertBuf, ourCertBufSz, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) err_sys_ex(catastrophic, "can't load server cert buffer"); #elif !defined(TEST_LOAD_BUFFER) @@ -3374,8 +3524,8 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #endif /* HAVE_PK_CALLBACKS && TEST_PK_PRIVKEY */ ) { #if defined(NO_FILESYSTEM) - if (wolfSSL_use_PrivateKey_buffer(ssl, server_key_der_2048, - sizeof_server_key_der_2048, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) + if (wolfSSL_use_PrivateKey_buffer(ssl, ourKeyBuf, + ourKeyBufSz, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) err_sys_ex(catastrophic, "can't load server private key buffer"); #elif !defined(TEST_LOAD_BUFFER) if (wolfSSL_use_PrivateKey_file(ssl, ourKey, WOLFSSL_FILETYPE_PEM) @@ -3777,6 +3927,10 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) #endif #ifdef WOLFSSL_EARLY_DATA EarlyDataStatus(ssl); +#endif +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) + /* print before ret is checked: ECH status is always significant */ + PrintEchStatus(ssl); #endif if (ret != WOLFSSL_SUCCESS) { err = wolfSSL_get_error(ssl, ret); diff --git a/gencertbuf.pl b/gencertbuf.pl index 500e6ac74eb..8e70a046430 100755 --- a/gencertbuf.pl +++ b/gencertbuf.pl @@ -96,7 +96,9 @@ [ "./certs/tsa-extra-eku-cert.der", "tsa_extra_eku_cert_der_2048" ], [ "./certs/intermediate/ca-int-cert.der", "ca_int_cert_der_2048" ], [ "./certs/tsa-chain-key.der", "tsa_chain_key_der_2048" ], - [ "./certs/tsa-chain-cert.der", "tsa_chain_cert_der_2048" ] + [ "./certs/tsa-chain-cert.der", "tsa_chain_cert_der_2048" ], + [ "./certs/ech-public-key.der", "ech_public_key_der_2048" ], + [ "./certs/ech-public-cert.der", "ech_public_cert_der_2048" ] ); # 3072-bit certs/keys to be converted diff --git a/src/internal.c b/src/internal.c index 8aa3fe065ee..8522f5bef51 100644 --- a/src/internal.c +++ b/src/internal.c @@ -43720,6 +43720,7 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], WOLFSSL_MSG("Server quietly not acking servername."); break; default: + TLSX_SetResponse(ssl, TLSX_SERVER_NAME); break; } } diff --git a/src/ssl.c b/src/ssl.c index 52abaa02c95..9ac7520b134 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -13151,6 +13151,13 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) InitSSL_CTX_Suites(ctx); } +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) + /* hpke should already be setup for ECH, the public material is all that + * is needed */ + if (CopyEchConfigsPub(ssl) != 0) + return NULL; +#endif + wolfSSL_RefWithMutexInc(&ctx->ref, &ret); #ifdef WOLFSSL_REFCNT_ERROR_RETURN if (ret != 0) { diff --git a/src/ssl_ech.c b/src/ssl_ech.c index ebc407c2af9..918b7869731 100644 --- a/src/ssl_ech.c +++ b/src/ssl_ech.c @@ -906,6 +906,113 @@ int GetEchConfigsEx(WOLFSSL_EchConfig* configs, byte* output, word32* outputLen) return WOLFSSL_SUCCESS; } +/* allocate *out and copy the public fields from src into it + * returns 0 on success, error otherwise */ +static int EchConfigCopyPub(WOLFSSL_EchConfig** out, + const WOLFSSL_EchConfig* src, void* heap) +{ + WOLFSSL_EchConfig* copy; + word32 suitesSz; + word32 nameLen; + + if (out == NULL || src == NULL || src->numCipherSuites == 0) + return BAD_FUNC_ARG; + + copy = (WOLFSSL_EchConfig*)XMALLOC(sizeof(WOLFSSL_EchConfig), heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (copy == NULL) + return MEMORY_E; + XMEMSET(copy, 0, sizeof(WOLFSSL_EchConfig)); + + suitesSz = (word32)src->numCipherSuites * (word32)sizeof(EchCipherSuite); + copy->cipherSuites = (EchCipherSuite*)XMALLOC(suitesSz, heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (copy->cipherSuites == NULL) { + XFREE(copy, heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } + + XMEMCPY(copy->cipherSuites, src->cipherSuites, suitesSz); + copy->configId = src->configId; + copy->kemId = src->kemId; + copy->numCipherSuites = src->numCipherSuites; + copy->maxNameLen = src->maxNameLen; + XMEMCPY(copy->receiverPubkey, src->receiverPubkey, HPKE_Npk_MAX); + + /* publicName shouldn't be NULL here, but make sure since the TLS code + * relies on it existing */ + if (src->publicName == NULL) { + XFREE(copy->cipherSuites, heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(copy, heap, DYNAMIC_TYPE_TMP_BUFFER); + return BAD_FUNC_ARG; + } + + nameLen = (word32)XSTRLEN(src->publicName) + 1; + copy->publicName = (char*)XMALLOC(nameLen, heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (copy->publicName == NULL) { + XFREE(copy->cipherSuites, heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(copy, heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } + XMEMCPY(copy->publicName, src->publicName, nameLen); + + /* don't copy raw and rawLen, these are derived later */ + + *out = copy; + return 0; +} + +/* copy the public parts of the ech configs into ssl owned memory + * returns 0 on success, error otherwise */ +int CopyEchConfigsPub(WOLFSSL* ssl) +{ + int ret = 0; + TLSX* echX; + WOLFSSL_ECH* ech; + WOLFSSL_EchConfig* workingConfig; + WOLFSSL_EchConfig* copy = NULL; + WOLFSSL_EchConfig* configList = NULL; + WOLFSSL_EchConfig* lastConfig = NULL; + + if (ssl == NULL) + return BAD_FUNC_ARG; + + echX = TLSX_Find(ssl->extensions, TLSX_ECH); + if (echX == NULL || echX->data == NULL) + return 0; + + ech = (WOLFSSL_ECH*)echX->data; + + /* the configs will be copied onto the ssl, and only copied once */ + if (ssl->options.side != WOLFSSL_SERVER_END || ech->copiedConfig) + return 0; + + for (workingConfig = ech->echConfig; workingConfig != NULL; + workingConfig = workingConfig->next) { + ret = EchConfigCopyPub(©, workingConfig, ssl->heap); + if (ret != 0) + break; + + if (configList == NULL) + configList = copy; + else + lastConfig->next = copy; + + lastConfig = copy; + } + + if (ret != 0) { + FreeEchConfigs(configList, ssl->heap); + return ret; + } + + ech->echConfig = configList; + ech->copiedConfig = 1; + + return 0; +} + #endif /* WOLFSSL_TLS13 && HAVE_ECH */ #endif /* !WOLFSSL_SSL_ECH_INCLUDED */ diff --git a/src/tls.c b/src/tls.c index 69c2e0a710b..ad8e613922f 100644 --- a/src/tls.c +++ b/src/tls.c @@ -1749,7 +1749,6 @@ static void TLSX_SetResponseInList(TLSX* list, TLSX_Type type) extension->resp = 1; } -void TLSX_SetResponse(WOLFSSL* ssl, TLSX_Type type); /** Mark an extension to be sent back to the client. */ void TLSX_SetResponse(WOLFSSL* ssl, TLSX_Type type) { @@ -2702,8 +2701,8 @@ int TLSX_UseSNI(TLSX** extensions, byte type, const void* data, word16 size, /* client-side needs this function when ECH is enabled */ #if !defined(NO_WOLFSSL_SERVER) || defined(HAVE_ECH) /** Tells the SNI requested by the client. */ -word16 TLSX_SNI_GetRequest(TLSX* extensions, byte type, void** data, - byte ignoreStatus) +WOLFSSL_TEST_VIS word16 TLSX_SNI_GetRequest(TLSX* extensions, byte type, + void** data, byte ignoreStatus) { TLSX* extension = TLSX_Find(extensions, TLSX_SERVER_NAME); SNI* sni = TLSX_SNI_Find(extension ? (SNI*)extension->data : NULL, type); @@ -14782,12 +14781,12 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, ech->confBuf = (byte*)readBuf; } - else if (msgType == client_hello && ssl->ctx->echConfigs != NULL) { - /* get extension */ - echX = TLSX_Find(ssl->extensions, TLSX_ECH); - if (echX == NULL) - return BAD_FUNC_ARG; + else if (msgType == client_hello && + (echX = TLSX_Find(ssl->extensions, TLSX_ECH)) != NULL && + echX->data != NULL && + ((WOLFSSL_ECH*)echX->data)->echConfig != NULL) { ech = (WOLFSSL_ECH*)echX->data; + TLSX_SetResponse(ssl, TLSX_ECH); /* if the first ECH was rejected or CH1 did not have ECH then there is * no need to decrypt this one */ @@ -14812,6 +14811,13 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, /* MUST process INNER in inner hello and OUTER in outer hello */ return INVALID_PARAMETER; } + + /* expect hpke and its context to be available on ClientHello2 */ + if (ssl->options.serverState >= SERVER_HELLO_RETRY_REQUEST_COMPLETE && + (ech->hpke == NULL || ech->hpkeContext == NULL)) { + return BAD_FUNC_ARG; + } + /* Must have kdfId, aeadId, configId, enc len and payload len. */ if (size < offset + 2 + 2 + 1 + 2 + 2) { return BUFFER_ERROR; @@ -14913,7 +14919,7 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, return MEMORY_E; } /* try to decrypt with matching configId */ - echConfig = ssl->ctx->echConfigs; + echConfig = ech->echConfig; while (echConfig != NULL) { if (echConfig->configId == ech->configId) { ret = TLSX_ExtractEch(ssl, ech, echConfig, aadCopy, @@ -14925,7 +14931,7 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, } /* otherwise, try to decrypt with all configs (trial decryption) */ if (echConfig == NULL && ssl->options.enableEchTrialDecrypt) { - echConfig = ssl->ctx->echConfigs; + echConfig = ech->echConfig; while (echConfig != NULL) { if (echConfig->configId != ech->configId) { ret = TLSX_ExtractEch(ssl, ech, echConfig, aadCopy, @@ -14965,7 +14971,7 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, ret = TLSX_ECH_CheckInnerPadding(ssl, ech); if (ret == 0) { /* expand EchOuterExtensions if present. - * Also, if it exists, copy sessionID from outer hello */ + * Also, if it exists, copy sessionID from outer hello */ ret = TLSX_ECH_ExpandOuterExtensions(ssl, ech, ssl->heap); } } @@ -14996,6 +15002,9 @@ static void TLSX_ECH_Free(WOLFSSL_ECH* ech, void* heap) ForceZero(ech->hpkeContext, sizeof(HpkeBaseContext)); XFREE(ech->hpkeContext, heap, DYNAMIC_TYPE_TMP_BUFFER); } + if (ech->copiedConfig && ech->echConfig != NULL) { + FreeEchConfigs(ech->echConfig, heap); + } XFREE(ech, heap, DYNAMIC_TYPE_TMP_BUFFER); (void)heap; @@ -16735,14 +16744,10 @@ int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isServer) #endif } #if defined(HAVE_ECH) - else if (IsAtLeastTLSv1_3(ssl->version)) { - if (ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { - ret = SERVER_ECH_USE(&(ssl->extensions), ssl->heap, - ssl->ctx->echConfigs); - - if (ret == 0) - TLSX_SetResponse(ssl, TLSX_ECH); - } + else if (IsAtLeastTLSv1_3(ssl->version) && !ssl->options.disableECH && + ssl->ctx->echConfigs != NULL) { + ret = SERVER_ECH_USE(&(ssl->extensions), ssl->heap, + ssl->ctx->echConfigs); } #endif @@ -18823,8 +18828,7 @@ WOLFSSL_TEST_VIS int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, /* Reconcile ECH inner/outer extensions before verifying SNI so the verify * pass sees the authoritative list */ if (ret == 0 && msgType == client_hello && isRequest && - !ssl->options.echProcessingInner && - ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { + !ssl->options.disableECH && !ssl->options.echProcessingInner) { TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH); WOLFSSL_ECH* ech = NULL; if (echX != NULL) diff --git a/src/tls13.c b/src/tls13.c index 55fcc3e7db7..0229f67a19b 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -7430,6 +7430,7 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #endif #if defined(HAVE_ECH) TLSX* echX = NULL; + WOLFSSL_ECH* ech = NULL; #endif WOLFSSL_START(WC_FUNC_CLIENT_HELLO_DO); @@ -7723,15 +7724,15 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, goto exit_dch; #if defined(HAVE_ECH) - if (ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { + if (!ssl->options.disableECH) { /* save the start of the buffer so we can use it when parsing ech */ echX = TLSX_Find(ssl->extensions, TLSX_ECH); - if (echX == NULL) - ERROR_OUT(WOLFSSL_FATAL_ERROR, exit_dch); - - ((WOLFSSL_ECH*)echX->data)->aad = input + HANDSHAKE_HEADER_SZ; - ((WOLFSSL_ECH*)echX->data)->aadLen = helloSz; + if (echX != NULL && echX->data != NULL) { + ech = (WOLFSSL_ECH*)echX->data; + ech->aad = input + HANDSHAKE_HEADER_SZ; + ech->aadLen = helloSz; + } } #endif @@ -7764,9 +7765,11 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, /* ECH accept/reject reconciliation is done at the end of TLSX_Parse. On * acceptance the inner hello was decrypted, so jump to exit and let the * caller re-invoke with the inner hello. */ - if (!ssl->options.echProcessingInner && echX != NULL && - ((WOLFSSL_ECH*)echX->data)->state == ECH_WRITE_NONE && - ((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) { + if (!ssl->options.echProcessingInner && ech != NULL && + ech->state == ECH_WRITE_NONE && ech->innerClientHello != NULL) { + /* need to add handshake header to the inner hello ourselves */ + AddTls13HandShakeHeader(ech->innerClientHello, ech->innerClientHelloLen, + 0, 0, client_hello, ssl); goto exit_dch; } #endif @@ -7844,13 +7847,11 @@ 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 && - ((WOLFSSL_ECH*)echX->data)->innerClientHello != NULL) { - ret = EchHashHelloInner(ssl, (WOLFSSL_ECH*)echX->data); + if (ech != NULL && ech->innerClientHello != NULL) { + ret = EchHashHelloInner(ssl, ech); if (ret != 0) goto exit_dch; - ((WOLFSSL_ECH*)echX->data)->innerCount = 1; + ech->innerCount = 1; } #endif @@ -8135,18 +8136,6 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, WOLFSSL_ERROR_VERBOSE(ret); } -#if defined(HAVE_ECH) - if (ret == 0 && echX != NULL && - ((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, - ((WOLFSSL_ECH*)echX->data)->innerClientHelloLen, 0, 0, - client_hello, ssl); - } -#endif - return ret; } @@ -8166,6 +8155,7 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType) int sendSz; #if defined(HAVE_ECH) TLSX* echX = NULL; + WOLFSSL_ECH* ech = NULL; byte* acceptLabel = (byte*)echAcceptConfirmationLabel; word32 acceptOffset; word16 acceptLabelSz = ECH_ACCEPT_CONFIRMATION_LABEL_SZ; @@ -8326,19 +8316,22 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType) #endif /* WOLFSSL_DTLS13 */ { #if defined(HAVE_ECH) - if (ssl->ctx->echConfigs != NULL && !ssl->options.disableECH) { - echX = TLSX_Find(ssl->extensions, TLSX_ECH); - if (echX == NULL) - return WOLFSSL_FATAL_ERROR; - /* use hrr offset */ - if (extMsgType == hello_retry_request) { - acceptOffset = - (word32)(((WOLFSSL_ECH*)echX->data)->confBuf - output); - acceptLabel = (byte*)echHrrAcceptConfirmationLabel; - acceptLabelSz = ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ; - } + if (!ssl->options.disableECH && + (echX = TLSX_Find(ssl->extensions, TLSX_ECH)) != NULL && + echX->data != NULL) { + ech = (WOLFSSL_ECH*)echX->data; + /* replace the last 8 bytes of server random with the accept */ - if (((WOLFSSL_ECH*)echX->data)->state == ECH_PARSED_INTERNAL) { + if (ech->state == ECH_PARSED_INTERNAL) { + /* use hrr offset */ + if (extMsgType == hello_retry_request && + ech->confBuf != NULL) { + acceptOffset = + (word32)(ech->confBuf - output); + acceptLabel = (byte*)echHrrAcceptConfirmationLabel; + acceptLabelSz = ECH_HRR_ACCEPT_CONFIRMATION_LABEL_SZ; + } + if (ret == 0) { ret = EchWriteAcceptance(ssl, acceptLabel, acceptLabelSz, output + RECORD_HEADER_SZ, @@ -8347,11 +8340,11 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType) } if (extMsgType == hello_retry_request) { /* reset the ech state for round 2 */ - ((WOLFSSL_ECH*)echX->data)->state = ECH_WRITE_NONE; + ech->state = ECH_WRITE_NONE; /* inner hello no longer needed, free it */ - XFREE(((WOLFSSL_ECH*)echX->data)->innerClientHello, - ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); - ((WOLFSSL_ECH*)echX->data)->innerClientHello = NULL; + XFREE(ech->innerClientHello, ssl->heap, + DYNAMIC_TYPE_TMP_BUFFER); + ech->innerClientHello = NULL; } else { if (ret == 0) { diff --git a/tests/api.c b/tests/api.c index 79748e936ca..4205f54d387 100644 --- a/tests/api.c +++ b/tests/api.c @@ -344,12 +344,22 @@ int testDevId = INVALID_DEVID; #endif #ifdef HAVE_ECH - #define echPublicName "example.com" - #define echPrivateName "ech-private-name.com" - #define echOtherName "mismatch.io" + #define echPublicName "public.com" + #define echPrivateName "example.com" + #define echOtherName "clash.info" + #define tenantAName "tenant-a.example" + #define tenantBName "tenant-b.example" #define ECH_CONFIG_LEN 256 + + #define test_ech_public_cert_setup(test_ctx) \ + do { \ + (test_ctx).s_cb.certPemFile = echPublicCertFile; \ + (test_ctx).s_cb.keyPemFile = echPublicKeyFile; \ + } while (0) + #endif + /*----------------------------------------------------------------------------* | BIO with fixed read/write size *----------------------------------------------------------------------------*/ @@ -13526,6 +13536,65 @@ static int test_wolfSSL_CTX_add_client_CA(void) #endif /* OPENSSL_EXTRA && !NO_RSA && !NO_CERTS && !NO_WOLFSSL_CLIENT */ return EXPECT_RESULT(); } + +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \ + (defined(HAVE_IO_TESTS_DEPENDENCIES) || \ + defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES)) +/* Multi-SNI (Multi-tenant) ECH: one server CTX fronts multiple SNI's + * -> Each tenant has its own cert/key */ +typedef struct { + const char* name; + const char* certFile; + const char* keyFile; + WOLFSSL_CTX* ctx; +} test_ech_tenant_cfg; + +static int test_ech_multi_sni_callback(WOLFSSL* ssl, int* ad, void* arg) +{ + const test_ech_tenant_cfg* tenants = (const test_ech_tenant_cfg*)arg; + const char* name = NULL; + if (tenants == NULL) { + *ad = internal_error; + return fatal_return; + } + /* only switch to a private tenant cert once ECH has been accepted + * until then the public, outer SNI is in play and the default cert stands */ + if (wolfSSL_GetEchStatus(ssl) != WOLFSSL_ECH_STATUS_ACCEPTED) + return 0; + + if (!wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, (void**)&name)) { + *ad = WOLFSSL_AD_UNRECOGNIZED_NAME; + return fatal_return; + } + for (; tenants->name != NULL; tenants++) { + if (XSTRCMP(name, tenants->name) == 0) { + /* swap to the tenant's CTX if one was provided, otherwise replace + * the cert/key on the connection directly */ +#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) + if (tenants->ctx != NULL) { + if (wolfSSL_set_SSL_CTX(ssl, tenants->ctx) == NULL) { + *ad = internal_error; + return fatal_return; + } + } + else +#endif + if (wolfSSL_use_certificate_file(ssl, tenants->certFile, + CERT_FILETYPE) != WOLFSSL_SUCCESS || + wolfSSL_use_PrivateKey_file(ssl, tenants->keyFile, + CERT_FILETYPE) != WOLFSSL_SUCCESS) { + *ad = internal_error; + return fatal_return; + } + return 0; + } + } + *ad = WOLFSSL_AD_UNRECOGNIZED_NAME; + return fatal_return; +} +#endif /* HAVE_ECH && (HAVE_IO_TESTS_DEPENDENCIES || + * HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) */ + #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \ defined(HAVE_IO_TESTS_DEPENDENCIES) static THREAD_RETURN WOLFSSL_THREAD server_task_ech(void* args) @@ -13539,8 +13608,11 @@ static THREAD_RETURN WOLFSSL_THREAD server_task_ech(void* args) char input[1024]; int idx; int ret, err = 0; - const char* privateName = "ech-private-name.com"; - int privateNameLen = (int)XSTRLEN(privateName); + int echPrivateNameLen = (int)XSTRLEN(echPrivateName); + static const test_ech_tenant_cfg echServerTenants[] = { + { echPrivateName, svrCertFile, svrKeyFile, NULL }, + { NULL, NULL, NULL, NULL } + }; ((func_args*)args)->return_code = TEST_FAIL; port = ((func_args*)args)->signal->port; @@ -13548,25 +13620,26 @@ static THREAD_RETURN WOLFSSL_THREAD server_task_ech(void* args) AssertIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_load_verify_locations(ctx, cliCertFile, 0)); + /* Front with the ECH public cert; the SNI callback swaps to the cert for + * the private inner name once ECH is accepted */ AssertIntEQ(WOLFSSL_SUCCESS, - wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + wolfSSL_CTX_use_certificate_file(ctx, echPublicCertFile, WOLFSSL_FILETYPE_PEM)); AssertIntEQ(WOLFSSL_SUCCESS, - wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + wolfSSL_CTX_use_PrivateKey_file(ctx, echPublicKeyFile, WOLFSSL_FILETYPE_PEM)); + wolfSSL_CTX_set_servername_callback(ctx, test_ech_multi_sni_callback); + AssertIntEQ(WOLFSSL_SUCCESS, + wolfSSL_CTX_set_servername_arg(ctx, (void*)echServerTenants)); + if (callbacks->ctx_ready) callbacks->ctx_ready(ctx); ssl = wolfSSL_new(ctx); AssertNotNull(ssl); - /* set the sni for the server */ - 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); AssertIntEQ(WOLFSSL_SUCCESS, wolfSSL_set_fd(ssl, cfd)); @@ -13587,14 +13660,17 @@ static THREAD_RETURN WOLFSSL_THREAD server_task_ech(void* args) fprintf(stderr, "error = %d, %s\n", err, wolfSSL_ERR_error_string(err, buff)); } + else if (wolfSSL_GetEchStatus(ssl) != WOLFSSL_ECH_STATUS_ACCEPTED) { + fprintf(stderr, "error = Client could not connect with ECH\n"); + } else { if (0 < (idx = wolfSSL_read(ssl, input, sizeof(input)-1))) { input[idx] = 0; fprintf(stderr, "Client message: %s\n", input); } - AssertIntEQ(privateNameLen, wolfSSL_write(ssl, privateName, - privateNameLen)); + AssertIntEQ(echPrivateNameLen, wolfSSL_write(ssl, echPrivateName, + echPrivateNameLen)); ((func_args*)args)->return_code = TEST_SUCCESS; } @@ -13950,6 +14026,7 @@ static int test_wolfSSL_Tls13_Key_Logging_ech_rejected(void) 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_ech_public_cert_setup(test_ctx); /* server generates its real ECH config and sets the keylog callback */ test_ctx.s_cb.ctx_ready = test_wolfSSL_Tls13_Key_Logging_server_ctx_ready; /* client sets the keylog callback */ @@ -13962,9 +14039,8 @@ static int test_wolfSSL_Tls13_Key_Logging_ech_rejected(void) TEST_SUCCESS); /* set inner SNI */ ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - "ech-private-name.com", (word16)XSTRLEN("ech-private-name.com")), - WOLFSSL_SUCCESS); - /* client sends empty cert on rejection, server should not ask for one */ + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); + /* client sends no cert on rejection */ wolfSSL_set_verify(test_ctx.s_ssl, WOLFSSL_VERIFY_NONE, NULL); /* clean up keylog file */ @@ -14483,7 +14559,7 @@ static int test_wolfSSL_Tls13_ECH_params_b64(void) ExpectIntNE(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(ssl, b64Mandatory, (word32)XSTRLEN(b64Mandatory))); - /* unrecognized mandatory extension */ + /* unrecognized mandatory extension first, should only have config 2 set */ ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_SetEchConfigsBase64(ctx, b64MandatoryFirst, (word32)XSTRLEN(b64MandatoryFirst))); ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_SetEchConfigsBase64(ssl, @@ -14559,10 +14635,8 @@ static int test_wolfSSL_ECH_conn_ex(method_provider serverMeth, SOCKET_T sockfd = 0; WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; - const char* publicName = "ech-public-name.com"; - const char* privateName = "ech-private-name.com"; - int privateNameLen = 20; char reply[1024]; + int echPrivateNameLen = (int)XSTRLEN(echPrivateName); int replyLen = 0; byte rawEchConfig[ECH_CONFIG_LEN]; word32 rawEchConfigLen = sizeof(rawEchConfig); @@ -14581,7 +14655,7 @@ static int test_wolfSSL_ECH_conn_ex(method_provider serverMeth, /* generate ech config */ ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_GenerateEchConfig(server_cbf.ctx, - publicName, 0, 0, 0)); + echPublicName, 0, 0, 0)); /* get the config for the client to use */ ExpectIntEQ(WOLFSSL_SUCCESS, @@ -14615,7 +14689,7 @@ static int test_wolfSSL_ECH_conn_ex(method_provider serverMeth, /* set the sni for the client */ ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, - privateName, privateNameLen)); + echPrivateName, echPrivateNameLen)); /* force hello retry request */ if (hrr) @@ -14625,13 +14699,13 @@ static int test_wolfSSL_ECH_conn_ex(method_provider serverMeth, ExpectIntEQ(wolfSSL_set_fd(ssl, sockfd), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_connect(ssl), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_GetEchStatus(ssl), WOLFSSL_ECH_STATUS_ACCEPTED); - ExpectIntEQ(wolfSSL_write(ssl, privateName, privateNameLen), - privateNameLen); + ExpectIntEQ(wolfSSL_write(ssl, echPrivateName, echPrivateNameLen), + echPrivateNameLen); ExpectIntGT((replyLen = wolfSSL_read(ssl, reply, sizeof(reply))), 0); /* add the null terminator for string compare */ reply[replyLen] = '\0'; /* check that the server replied with the private name */ - ExpectStrEQ(privateName, reply); + ExpectStrEQ(echPrivateName, reply); wolfSSL_free(ssl); wolfSSL_CTX_free(ctx); @@ -14646,14 +14720,14 @@ static int test_wolfSSL_ECH_conn_ex(method_provider serverMeth, static int test_wolfSSL_Tls13_ECH(void) { - return test_wolfSSL_ECH_conn_ex(wolfTLSv1_3_server_method, - wolfTLSv1_3_client_method, 0); -} + EXPECT_DECLS; -static int test_wolfSSL_Tls13_ECH_HRR(void) -{ - return test_wolfSSL_ECH_conn_ex(wolfTLSv1_3_server_method, - wolfTLSv1_3_client_method, 1); + ExpectIntEQ(test_wolfSSL_ECH_conn_ex(wolfTLSv1_3_server_method, + wolfTLSv1_3_client_method, 0), TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_ECH_conn_ex(wolfTLSv1_3_server_method, + wolfTLSv1_3_client_method, 1), TEST_SUCCESS); + + return EXPECT_RESULT(); } static int test_wolfSSL_SubTls13_ECH(void) @@ -14755,6 +14829,87 @@ static int ech_find_extension(byte* buf, word16* idx_p, word16 extType) return BAD_FUNC_ARG; } +/* swap two extensions in a CH record so they appear in the opposite order + * returns 0 on success, error otherwise. */ +static int ech_swap_extensions(byte* buf, word16 typeFirst, word16 typeSecond) +{ + word16 idxA = 0; + word16 idxB = 0; + word16 lenA; + word16 lenB; + word16 midLen; + word16 middle; + word16 swap; + byte* tmp; + int ret; + + if (typeFirst == typeSecond) + return BAD_FUNC_ARG; + + ret = ech_find_extension(buf, &idxA, typeFirst); + if (ret != 0) + return ret; + ret = ech_find_extension(buf, &idxB, typeSecond); + if (ret != 0) + return ret; + + /* idxA must be the earlier extension on the wire */ + if (idxA > idxB) { + swap = idxA; + idxA = idxB; + idxB = swap; + } + + /* a block is type + length + body */ + ato16(buf + idxA + OPAQUE16_LEN, &lenA); + lenA += 2 * OPAQUE16_LEN; + ato16(buf + idxB + OPAQUE16_LEN, &lenB); + lenB += 2 * OPAQUE16_LEN; + midLen = idxB - (idxA + lenA); + middle = lenA + midLen; + + tmp = (byte*)XMALLOC(middle, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (tmp == NULL) + return MEMORY_E; + + /* stash [A][middle], slide [B] to the beginning, join [middle] then [A] */ + XMEMCPY(tmp, buf + idxA, middle); + XMEMMOVE(buf + idxA, buf + idxB, lenB); + XMEMCPY(buf + idxA + lenB, tmp + lenA, midLen); + XMEMCPY(buf + idxA + lenB + midLen, tmp, lenA); + + XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return 0; +} + +/* the buffer must carry exactly one SNI */ +static int test_ech_assert_wire_sni(byte* buff, const char* publicName) +{ + EXPECT_DECLS; + word16 idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; + word16 extLen; + word16 listLen; + word16 nameLen; + word16 publicLen = (word16)XSTRLEN(publicName); + + ExpectIntEQ(ech_find_extension(buff, &idx, TLSXT_SERVER_NAME), 0); + + ato16(buff + idx + 2, &extLen); + ato16(buff + idx + 4, &listLen); + ExpectIntEQ(buff[idx + 6], WOLFSSL_SNI_HOST_NAME); + ato16(buff + idx + 7, &nameLen); + + /* the single entry is the public name */ + ExpectIntEQ(nameLen, publicLen); + ExpectIntEQ(XMEMCMP(buff + idx + 9, publicName, publicLen), 0); + /* and it is the only entry: the list and extension hold nothing else */ + ExpectIntEQ(listLen, OPAQUE8_LEN + OPAQUE16_LEN + nameLen); + ExpectIntEQ(extLen, OPAQUE16_LEN + listLen); + + return EXPECT_RESULT(); +} + /* the arg is whether the client has ech enabled or not */ static int test_ech_server_sni_callback(WOLFSSL* ssl, int* ad, void* arg) { @@ -14843,6 +14998,9 @@ static int test_ech_client_ssl_ready(WOLFSSL* ssl) if (ret != WOLFSSL_SUCCESS) return TEST_FAIL; + /* force verify to be on, regardless of the defaults */ + wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER, NULL); + return TEST_SUCCESS; } @@ -14855,6 +15013,7 @@ static int test_wolfSSL_Tls13_ECH_all_algos_ex(void) test_ctx.s_cb.method = wolfTLSv1_3_server_method; test_ctx.c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(test_ctx); test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready; @@ -14970,6 +15129,7 @@ static int test_wolfSSL_Tls13_ECH_no_private_name(void) test_ctx.s_cb.method = wolfTLSv1_3_server_method; test_ctx.c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(test_ctx); test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; @@ -15003,6 +15163,7 @@ static int test_wolfSSL_Tls13_ECH_retry_configs_ex(int hrr) 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_ech_public_cert_setup(test_ctx); test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready; @@ -15012,8 +15173,7 @@ static int test_wolfSSL_Tls13_ECH_retry_configs_ex(int hrr) ExpectIntEQ(test_ech_set_bad_echconfigs(test_ctx.s_ctx, test_ctx.c_ssl), TEST_SUCCESS); ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - echPrivateName, (word16)XSTRLEN(echPrivateName)), - WOLFSSL_SUCCESS); + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); if (hrr) ExpectIntEQ(wolfSSL_NoKeyShares(test_ctx.c_ssl), WOLFSSL_SUCCESS); @@ -15053,8 +15213,7 @@ static int test_wolfSSL_Tls13_ECH_retry_configs_ex(int hrr) ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, retryConfigs, retryConfigsLen), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - echPrivateName, (word16)XSTRLEN(echPrivateName)), - WOLFSSL_SUCCESS); + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); if (hrr) ExpectIntEQ(wolfSSL_NoKeyShares(test_ctx.c_ssl), WOLFSSL_SUCCESS); @@ -15086,41 +15245,45 @@ static int test_wolfSSL_Tls13_ECH_retry_configs(void) } /* Test retry configs are cleared when authentication fails */ -static int test_wolfSSL_Tls13_ECH_retry_configs_auth_fail_ex(int hrr) +static int test_wolfSSL_Tls13_ECH_retry_configs_auth_fail(void) { EXPECT_DECLS; test_ssl_memio_ctx test_ctx; - WOLFSSL_CTX* tempCtx = NULL; - byte badConfigs[ECH_CONFIG_LEN]; - word32 badConfigsLen = sizeof(badConfigs); - word32 retryConfigsLen = sizeof(badConfigs); + byte retryConfigs[ECH_CONFIG_LEN]; + word32 retryConfigsLen = sizeof(retryConfigs); const char* badPublicName = "ech-public-name.com"; 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_ech_public_cert_setup(test_ctx); 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 mismatched ECH configs so retry_configs are sent - * and use a bad public name so auth fails in outer hello */ - ExpectNotNull(tempCtx = wolfSSL_CTX_new(wolfTLSv1_3_server_method())); - ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(tempCtx, badPublicName, - 0, 0, 0), WOLFSSL_SUCCESS); - ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(tempCtx, badConfigs, &badConfigsLen), - WOLFSSL_SUCCESS); - wolfSSL_CTX_free(tempCtx); - tempCtx = NULL; - - ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, badConfigs, - badConfigsLen), WOLFSSL_SUCCESS); - ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - echPrivateName, (word16)XSTRLEN(echPrivateName)), - WOLFSSL_SUCCESS); + { + WOLFSSL_CTX* tempCtx = NULL; + byte badConfigs[ECH_CONFIG_LEN]; + word32 badConfigsLen = sizeof(badConfigs); + + /* generate mismatched ECH configs so retry_configs are sent + * and use a bad public name so auth fails in outer hello */ + ExpectNotNull(tempCtx = wolfSSL_CTX_new(wolfTLSv1_3_server_method())); + ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(tempCtx, badPublicName, + 0, 0, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(tempCtx, badConfigs, + &badConfigsLen), WOLFSSL_SUCCESS); + wolfSSL_CTX_free(tempCtx); + tempCtx = NULL; + + ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, badConfigs, + badConfigsLen), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); + } - /* Do not require client cert on server so it does not send - * CertificateRequest */ + /* client sends no cert on rejection */ wolfSSL_set_verify(test_ctx.s_ssl, WOLFSSL_VERIFY_NONE, NULL); wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_PEER, NULL); @@ -15129,16 +15292,13 @@ static int test_wolfSSL_Tls13_ECH_retry_configs_auth_fail_ex(int hrr) badPublicName, (word16)XSTRLEN(badPublicName)), WOLFSSL_SUCCESS); - if (hrr) - ExpectIntEQ(wolfSSL_NoKeyShares(test_ctx.c_ssl), WOLFSSL_SUCCESS); - /* auth failure in outer handshake, not ech_required */ ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0), WC_NO_ERR_TRACE(DOMAIN_NAME_MISMATCH)); /* retry configs must not be accessible */ - ExpectIntNE(wolfSSL_GetEchRetryConfigs(test_ctx.c_ssl, NULL, + ExpectIntNE(wolfSSL_GetEchRetryConfigs(test_ctx.c_ssl, retryConfigs, &retryConfigsLen), WOLFSSL_SUCCESS); test_ssl_memio_cleanup(&test_ctx); @@ -15146,29 +15306,19 @@ static int test_wolfSSL_Tls13_ECH_retry_configs_auth_fail_ex(int hrr) return EXPECT_RESULT(); } -static int test_wolfSSL_Tls13_ECH_retry_configs_auth_fail(void) -{ - EXPECT_DECLS; - - ExpectIntEQ(test_wolfSSL_Tls13_ECH_retry_configs_auth_fail_ex(0), - TEST_SUCCESS); - ExpectIntEQ(test_wolfSSL_Tls13_ECH_retry_configs_auth_fail_ex(1), - TEST_SUCCESS); - - return EXPECT_RESULT(); -} - /* Test that bad retry configs (unsupported cipher suite) from the server are * ignored rather than propagating an error */ static int test_wolfSSL_Tls13_ECH_retry_configs_bad(void) { EXPECT_DECLS; test_ssl_memio_ctx test_ctx; - word32 retryConfigsLen = sizeof(echCbTestConfigs); + byte retryConfigs[ECH_CONFIG_LEN]; + word32 retryConfigsLen = sizeof(retryConfigs); 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_ech_public_cert_setup(test_ctx); 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; @@ -15194,7 +15344,7 @@ static int test_wolfSSL_Tls13_ECH_retry_configs_bad(void) WC_NO_ERR_TRACE(ECH_REQUIRED_E)); /* no retry configs should be stored since they were all unsupported */ - ExpectIntNE(wolfSSL_GetEchRetryConfigs(test_ctx.c_ssl, NULL, + ExpectIntNE(wolfSSL_GetEchRetryConfigs(test_ctx.c_ssl, retryConfigs, &retryConfigsLen), WOLFSSL_SUCCESS); test_ssl_memio_cleanup(&test_ctx); @@ -15222,6 +15372,7 @@ static int test_wolfSSL_Tls13_ECH_new_config(void) test_ctx.s_cb.method = wolfTLSv1_3_server_method; test_ctx.c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(test_ctx); /* server generates its own ECH config */ test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; @@ -15270,8 +15421,7 @@ static int test_wolfSSL_Tls13_ECH_new_config(void) ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, altConfig, altConfigLen), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - echPrivateName, (word16)XSTRLEN(echPrivateName)), - WOLFSSL_SUCCESS); + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), @@ -15297,6 +15447,7 @@ static int test_wolfSSL_Tls13_ECH_trial_decrypt(void) test_ctx.s_cb.method = wolfTLSv1_3_server_method; test_ctx.c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(test_ctx); /* *_trial_decrypt sets enableEchTrialDecrypt to 1 - overriding the default * value of 0 */ @@ -15338,6 +15489,7 @@ static int test_wolfSSL_Tls13_ECH_trial_decrypt(void) test_ctx.s_cb.method = wolfTLSv1_3_server_method; test_ctx.c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(test_ctx); test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready; @@ -15382,7 +15534,8 @@ static int test_wolfSSL_Tls13_ECH_GREASE(void) { EXPECT_DECLS; test_ssl_memio_ctx test_ctx; - word32 retryConfigsLen = sizeof(test_ctx); + byte retryConfigs[ECH_CONFIG_LEN]; + word32 retryConfigsLen = sizeof(retryConfigs); /* GREASE when server has no ECH configs */ @@ -15394,8 +15547,7 @@ static int test_wolfSSL_Tls13_ECH_GREASE(void) ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - echPrivateName, (word16)XSTRLEN(echPrivateName)), - WOLFSSL_SUCCESS); + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); /* verify ECH is enabled on the client and server */ ExpectIntEQ(test_ctx.s_ssl->options.disableECH, 0); @@ -15415,7 +15567,7 @@ static int test_wolfSSL_Tls13_ECH_GREASE(void) /* verify no ECH configs are received */ ExpectNull(test_ctx.c_ssl->echConfigs); /* retry configs must not be saved */ - ExpectIntNE(wolfSSL_GetEchRetryConfigs(test_ctx.c_ssl, NULL, + ExpectIntNE(wolfSSL_GetEchRetryConfigs(test_ctx.c_ssl, retryConfigs, &retryConfigsLen), WOLFSSL_SUCCESS); test_ssl_memio_cleanup(&test_ctx); @@ -15423,9 +15575,11 @@ static int test_wolfSSL_Tls13_ECH_GREASE(void) /* GREASE when server has ECH configs */ XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + retryConfigsLen = sizeof(retryConfigs); test_ctx.s_cb.method = wolfTLSv1_3_server_method; test_ctx.c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(test_ctx); /* generate ECH configs */ test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; @@ -15434,8 +15588,7 @@ static int test_wolfSSL_Tls13_ECH_GREASE(void) ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - echPrivateName, (word16)XSTRLEN(echPrivateName)), - WOLFSSL_SUCCESS); + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); /* verify ECH is enabled on the client and server */ ExpectIntEQ(test_ctx.s_ssl->options.disableECH, 0); @@ -15456,7 +15609,7 @@ static int test_wolfSSL_Tls13_ECH_GREASE(void) /* verify no ECH configs are received */ ExpectNull(test_ctx.c_ssl->echConfigs); /* retry configs must not be saved */ - ExpectIntNE(wolfSSL_GetEchRetryConfigs(test_ctx.c_ssl, NULL, + ExpectIntNE(wolfSSL_GetEchRetryConfigs(test_ctx.c_ssl, retryConfigs, &retryConfigsLen), WOLFSSL_SUCCESS); test_ssl_memio_cleanup(&test_ctx); @@ -15464,55 +15617,27 @@ static int test_wolfSSL_Tls13_ECH_GREASE(void) return EXPECT_RESULT(); } -/* the outer ClientHello in buff must carry exactly one SNI: the public name */ -static int test_ech_assert_wire_sni(byte* buff, const char* publicName) -{ - EXPECT_DECLS; - word16 idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; - word16 extLen; - word16 listLen; - word16 nameLen; - word16 publicLen = (word16)XSTRLEN(publicName); - - ExpectIntEQ(ech_find_extension(buff, &idx, TLSXT_SERVER_NAME), 0); - - ato16(buff + idx + 2, &extLen); - ato16(buff + idx + 4, &listLen); - ExpectIntEQ(buff[idx + 6], WOLFSSL_SNI_HOST_NAME); - ato16(buff + idx + 7, &nameLen); - - /* the single entry is the public name */ - ExpectIntEQ(nameLen, publicLen); - ExpectIntEQ(XMEMCMP(buff + idx + 9, publicName, publicLen), 0); - /* and it is the only entry: the list and extension hold nothing else */ - ExpectIntEQ(listLen, OPAQUE8_LEN + OPAQUE16_LEN + nameLen); - ExpectIntEQ(extLen, OPAQUE16_LEN + listLen); - - return EXPECT_RESULT(); -} - -/* The public name must be visible in plaintext - * useCtx installs the inner SNI on the client ctx */ -static int test_wolfSSL_Tls13_ECH_wire_sni_ex(int accept, int useCtx) +/* Test the client properly inherits an SNI from the ctx, + * the public name must be visible in plaintext */ +static int test_wolfSSL_Tls13_ECH_ctx_sni_ex(int reject) { EXPECT_DECLS; test_ssl_memio_ctx test_ctx; - const char* expectedSni = - accept ? echPrivateName : echPublicName; + const char* expectedSni = reject ? echPublicName : echPrivateName; void* sniName = NULL; 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_ech_public_cert_setup(test_ctx); 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); /* Reject path installs bad configs (with the correct public name) */ - if (!accept) { + if (reject) { /* derive a bad config from the server's real one to reject ECH */ ExpectIntEQ(test_ech_set_bad_echconfigs(test_ctx.s_ctx, test_ctx.c_ssl), TEST_SUCCESS); @@ -15526,25 +15651,21 @@ static int test_wolfSSL_Tls13_ECH_wire_sni_ex(int accept, int useCtx) WOLFSSL_SNI_ABORT_ON_ABSENCE); } - /* install the private (inner) SNI on the ctx or the per-connection ssl */ - if (useCtx) { - ExpectIntEQ(wolfSSL_CTX_UseSNI(test_ctx.c_ctx, WOLFSSL_SNI_HOST_NAME, - echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); - } - else { - ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); - } + /* install the private (inner) SNI on the ctx */ + ExpectIntEQ(wolfSSL_CTX_UseSNI(test_ctx.c_ctx, WOLFSSL_SNI_HOST_NAME, + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); /* force HelloRetryRequest */ ExpectIntEQ(wolfSSL_NoKeyShares(test_ctx.c_ssl), WOLFSSL_SUCCESS); - /* On reject, client aborts with ech_required and won't send a cert. */ - if (!accept) { + /* client sends no cert on rejection */ + if (reject) { wolfSSL_set_verify(test_ctx.s_ssl, WOLFSSL_VERIFY_NONE, NULL); - wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_PEER, NULL); } + /* verify mode may be VERIFY_NONE so force it on */ + wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_PEER, NULL); + /* client writes CH1 into s_buff */ ExpectIntEQ(wolfSSL_connect(test_ctx.c_ssl), WOLFSSL_FATAL_ERROR); ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, WOLFSSL_FATAL_ERROR), @@ -15571,29 +15692,29 @@ static int test_wolfSSL_Tls13_ECH_wire_sni_ex(int accept, int useCtx) TEST_SUCCESS); /* sanity check: finish the handshake and verify ECH acceptance */ - if (accept) { - ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), + if (reject) { + ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), - WOLFSSL_ECH_STATUS_ACCEPTED); + WOLFSSL_ECH_STATUS_REJECTED); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), - WOLFSSL_ECH_STATUS_ACCEPTED); + WOLFSSL_ECH_STATUS_REJECTED); + ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0), + WC_NO_ERR_TRACE(ECH_REQUIRED_E)); } else { - ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), - WOLFSSL_ECH_STATUS_REJECTED); + WOLFSSL_ECH_STATUS_ACCEPTED); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), - WOLFSSL_ECH_STATUS_REJECTED); + WOLFSSL_ECH_STATUS_ACCEPTED); } - /* verify the correct SNI is authoritative */ - ExpectIntEQ(test_ctx.c_ssl->options.echAccepted, accept); wolfSSL_SNI_GetRequest(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); /* an inner SNI installed on the ctx is never copied onto the ssl, so on * accept it does not become the authoritative request on the client ssl */ - if (accept && useCtx) + if (!reject) ExpectNull(sniName); else ExpectStrEQ((const char*)sniName, expectedSni); @@ -15601,25 +15722,21 @@ static int test_wolfSSL_Tls13_ECH_wire_sni_ex(int accept, int useCtx) wolfSSL_SNI_GetRequest(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); ExpectStrEQ((const char*)sniName, expectedSni); /* verify the ctx always has the private SNI */ - if (useCtx) { - sniName = NULL; - TLSX_SNI_GetRequest(test_ctx.c_ctx->extensions, WOLFSSL_SNI_HOST_NAME, - &sniName, 1); - ExpectStrEQ((const char*)sniName, echPrivateName); - } + sniName = NULL; + TLSX_SNI_GetRequest(test_ctx.c_ctx->extensions, WOLFSSL_SNI_HOST_NAME, + &sniName, 1); + ExpectStrEQ((const char*)sniName, echPrivateName); test_ssl_memio_cleanup(&test_ctx); return EXPECT_RESULT(); } -static int test_wolfSSL_Tls13_ECH_wire_sni(void) +static int test_wolfSSL_Tls13_ECH_ctx_sni(void) { EXPECT_DECLS; - ExpectIntEQ(test_wolfSSL_Tls13_ECH_wire_sni_ex(0, 0), TEST_SUCCESS); - ExpectIntEQ(test_wolfSSL_Tls13_ECH_wire_sni_ex(1, 0), TEST_SUCCESS); - ExpectIntEQ(test_wolfSSL_Tls13_ECH_wire_sni_ex(0, 1), TEST_SUCCESS); - ExpectIntEQ(test_wolfSSL_Tls13_ECH_wire_sni_ex(1, 1), TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_ctx_sni_ex(0), TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_ctx_sni_ex(1), TEST_SUCCESS); return EXPECT_RESULT(); } @@ -15633,15 +15750,15 @@ static int test_wolfSSL_Tls13_ECH_disable_conn_ex(int enableServer, test_ctx.s_cb.method = wolfTLSv1_3_server_method; test_ctx.c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(test_ctx); /* 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 */ + /* this callback will verify that the correct SNI is being processed */ 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), @@ -15667,6 +15784,8 @@ static int test_wolfSSL_Tls13_ECH_disable_conn_ex(int enableServer, TEST_SUCCESS); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), WOLFSSL_ECH_STATUS_REJECTED); + ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0), + WC_NO_ERR_TRACE(ECH_REQUIRED_E)); } ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), WOLFSSL_ECH_STATUS_NOT_OFFERED); @@ -15690,6 +15809,183 @@ static int test_wolfSSL_Tls13_ECH_disable_conn(void) return EXPECT_RESULT(); } +static int test_wolfSSL_Tls13_ECH_multi_sni_ex( + const test_ech_tenant_cfg* tenants, const test_ech_tenant_cfg* tenant, + int hrr, int reject) +{ + EXPECT_DECLS; + test_ssl_memio_ctx test_ctx; + void* sniName = NULL; + + 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_ech_public_cert_setup(test_ctx); + test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; + + ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); + + if (reject) { + /* bad config derived from the server's real one to force + * ECH rejection */ + ExpectIntEQ(test_ech_set_bad_echconfigs(test_ctx.s_ctx, test_ctx.c_ssl), + TEST_SUCCESS); + } + else { + ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echCbTestConfigs, + echCbTestConfigsLen), WOLFSSL_SUCCESS); + } + + /* choose tenant to connect to */ + ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, + tenant->name, (word16)XSTRLEN(tenant->name)), WOLFSSL_SUCCESS); + + /* multi-tenant dispatch on the server */ + wolfSSL_CTX_set_servername_callback(test_ctx.s_ctx, + test_ech_multi_sni_callback); + wolfSSL_CTX_set_servername_arg(test_ctx.s_ctx, (void*)tenants); + + /* client sends no cert on rejection */ + if (reject) { + wolfSSL_set_verify(test_ctx.s_ssl, WOLFSSL_VERIFY_NONE, NULL); + } + + /* verify mode may be VERIFY_NONE so force it on */ + wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_PEER, NULL); + + /* check the returned cert matches expected tenant name */ + ExpectIntEQ(wolfSSL_check_domain_name(test_ctx.c_ssl, tenant->name), + WOLFSSL_SUCCESS); + + /* force a HelloRetryRequest */ + if (hrr) + ExpectIntEQ(wolfSSL_NoKeyShares(test_ctx.c_ssl), WOLFSSL_SUCCESS); + + /* client writes CH1; the outer SNI on the wire must be the public name */ + ExpectIntEQ(wolfSSL_connect(test_ctx.c_ssl), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ech_assert_wire_sni(test_ctx.s_buff, echPublicName), + TEST_SUCCESS); + + if (hrr) { + /* server consumes CH1 and writes HRR */ + ExpectIntEQ(wolfSSL_accept(test_ctx.s_ssl), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(test_ctx.s_ssl, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.s_ssl->options.serverState, + SERVER_HELLO_RETRY_REQUEST_COMPLETE); + /* client reads HRR and writes CH2; the public name must still be the + * only name on the wire */ + ExpectIntEQ(wolfSSL_connect(test_ctx.c_ssl), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ech_assert_wire_sni(test_ctx.s_buff, echPublicName), + TEST_SUCCESS); + } + + if (reject) { + ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), + TEST_SUCCESS); + ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), + WOLFSSL_ECH_STATUS_REJECTED); + ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), + WOLFSSL_ECH_STATUS_REJECTED); + ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0), + WC_NO_ERR_TRACE(ECH_REQUIRED_E)); + } + else { + ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), + TEST_SUCCESS); + ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), + WOLFSSL_ECH_STATUS_ACCEPTED); + ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), + WOLFSSL_ECH_STATUS_ACCEPTED); + } + + /* Correct authoritative SNI on both sides */ + if (reject) { + wolfSSL_SNI_GetRequest(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); + ExpectStrEQ((const char*)sniName, echPublicName); + sniName = NULL; + wolfSSL_SNI_GetRequest(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); + ExpectStrEQ((const char*)sniName, echPublicName); + } + else { + wolfSSL_SNI_GetRequest(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); + ExpectStrEQ((const char*)sniName, tenant->name); + sniName = NULL; + wolfSSL_SNI_GetRequest(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); + ExpectStrEQ((const char*)sniName, tenant->name); + } + + test_ssl_memio_cleanup(&test_ctx); + + return EXPECT_RESULT(); +} + +/* a test for the wolfSSL_CTX_set_servername_callback with ECH + * tests manual swapping of certificate and key */ +static int test_wolfSSL_Tls13_ECH_multi_sni(void) +{ + EXPECT_DECLS; + const test_ech_tenant_cfg tenants[] = { + { tenantAName, tenantACertFile, svrKeyFile, NULL }, + { tenantBName, tenantBCertFile, svrKeyFile, NULL }, + { NULL, NULL, NULL, NULL } + }; + + ExpectIntEQ(test_wolfSSL_Tls13_ECH_multi_sni_ex(tenants, &tenants[0], 0, 0), + TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_multi_sni_ex(tenants, &tenants[1], 1, 0), + TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_multi_sni_ex(tenants, &tenants[0], 0, 1), + TEST_SUCCESS); + + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) +/* a test for the wolfSSL_CTX_set_servername_callback with ECH + * tests ctx swapping with wolfSSL_set_SSL_CTX + * - This is more tricky because echConfig inheritance must be considered */ +static int test_wolfSSL_Tls13_ECH_multi_ctx(void) +{ + EXPECT_DECLS; + int i; + test_ech_tenant_cfg tenants[] = { + { tenantAName, tenantACertFile, svrKeyFile, NULL }, + { tenantBName, tenantBCertFile, svrKeyFile, NULL }, + { NULL, NULL, NULL, NULL } + }; + + /* build a per-tenant CTX the callback can swap to with wolfSSL_set_SSL_CTX. + * Note that no ECH config is set/generated. */ + for (i = 0; tenants[i].name != NULL; i++) { + ExpectNotNull(tenants[i].ctx = + wolfSSL_CTX_new(wolfTLSv1_3_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(tenants[i].ctx, + tenants[i].certFile, CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(tenants[i].ctx, + tenants[i].keyFile, CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(tenants[i].ctx, + cliCertFile, 0), WOLFSSL_SUCCESS); + } + + ExpectIntEQ(test_wolfSSL_Tls13_ECH_multi_sni_ex(tenants, &tenants[0], 0, 0), + TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_multi_sni_ex(tenants, &tenants[1], 1, 0), + TEST_SUCCESS); + ExpectIntEQ(test_wolfSSL_Tls13_ECH_multi_sni_ex(tenants, &tenants[0], 0, 1), + TEST_SUCCESS); + + for (i = 0; tenants[i].name != NULL; i++) + wolfSSL_CTX_free(tenants[i].ctx); + + return EXPECT_RESULT(); +} +#endif + /* Test the HRR ECH rejection fallback path: * client offers ECH, HRR is triggered, server sends HRR without ECH extension, * client falls back to the outer transcript, then aborts with ech_required. @@ -15699,28 +15995,29 @@ static int test_wolfSSL_Tls13_ECH_HRR_rejection(void) EXPECT_DECLS; test_ssl_memio_ctx test_ctx; + /* this test is somewhat adversarial, a server hosting the public name + * implies an ECH server but this one does not support ECH */ 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_ech_public_cert_setup(test_ctx); /* Server generates ECH config with good public name */ test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; - /* Client sets the correct ECH config and private SNI */ + /* Client sets the correct ECH config and a private SNI */ test_ctx.c_cb.ssl_ready = test_ech_client_ssl_ready; ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); - /* Server must not require a client certificate */ + /* client sends no cert on rejection */ wolfSSL_set_verify(test_ctx.s_ssl, WOLFSSL_VERIFY_NONE, NULL); - wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_PEER, NULL); /* Disable ECH on the server SSL object: the server ignores ECH in CH1 and * sends HRR without an ECH extension (confBuf stays NULL on the client) */ wolfSSL_SetEchEnable(test_ctx.s_ssl, 0); ExpectIntEQ(wolfSSL_UseSNI(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, - echPublicName, (word16)XSTRLEN(echPublicName)), - WOLFSSL_SUCCESS); + echPublicName, (word16)XSTRLEN(echPublicName)), WOLFSSL_SUCCESS); /* Force HRR so client receives HRR with no ECH extension, * detects confBuf == NULL and frees hsHashesEch, falling back to the @@ -15772,6 +16069,7 @@ static int test_wolfSSL_Tls13_ECH_ch2_no_ech(void) test_ctx.s_cb.method = wolfTLSv1_3_server_method; test_ctx.c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(test_ctx); test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready; @@ -15819,6 +16117,7 @@ static int test_wolfSSL_Tls13_ECH_ch2_decrypt_error(void) test_ctx.s_cb.method = wolfTLSv1_3_server_method; test_ctx.c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(test_ctx); test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready; @@ -15875,52 +16174,43 @@ static int test_wolfSSL_Tls13_ECH_rejected_cert_valid_ex(const char* publicName, { EXPECT_DECLS; test_ssl_memio_ctx test_ctx; - byte echConfigs[ECH_CONFIG_LEN]; - word32 echConfigsLen = sizeof(echConfigs); 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; + /* cert presented when ECH is rejected; valid for the public name */ + test_ech_public_cert_setup(test_ctx); + /* server hosts the private name */ + test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready; ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS); - /* Generate ECH config with given public_name */ + /* Server has its own ECH config with the given public_name */ ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(test_ctx.s_ctx, publicName, 0, 0, 0), WOLFSSL_SUCCESS); - ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(test_ctx.s_ctx, echConfigs, - &echConfigsLen), WOLFSSL_SUCCESS); - /* Client loads ECH configs and sets a private SNI */ - ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echConfigs, - echConfigsLen), WOLFSSL_SUCCESS); + /* bad config derived from the server's real one to force ECH rejection */ + ExpectIntEQ(test_ech_set_bad_echconfigs(test_ctx.s_ctx, test_ctx.c_ssl), + TEST_SUCCESS); ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - "ech-private.com", (word16)XSTRLEN("ech-private.com")), - WOLFSSL_SUCCESS); + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); - /* Do not require client cert on server so it does not send - * CertificateRequest */ + /* client sends no cert on rejection */ wolfSSL_set_verify(test_ctx.s_ssl, WOLFSSL_VERIFY_NONE, NULL); wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_PEER, NULL); - /* Disable ECH on the server side so ECH is rejected */ - wolfSSL_SetEchEnable(test_ctx.s_ssl, 0); - - /* Match the server SNI to the ECH public_name */ - ExpectIntEQ(wolfSSL_UseSNI(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, - publicName, (word16)XSTRLEN(publicName)), WOLFSSL_SUCCESS); - - /* client sends ECH but server can't process it, however it is possible to - * fall back to the outer handshake */ + /* client sends ECH but server can't decrypt it, falls back to the outer + * handshake */ ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), WOLFSSL_ECH_STATUS_REJECTED); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), - WOLFSSL_ECH_STATUS_NOT_OFFERED); + WOLFSSL_ECH_STATUS_REJECTED); if (validName) { /* the server should see the handshake as successful - * the client should abort because the server did not use ECH */ + * the client should abort because the server rejected ECH */ ExpectIntEQ(wolfSSL_get_error(test_ctx.s_ssl, 0), WC_NO_ERR_TRACE(WOLFSSL_ERROR_NONE)); ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, 0), @@ -15943,8 +16233,7 @@ static int test_wolfSSL_Tls13_ECH_rejected_cert_valid(void) { EXPECT_DECLS; - /* "example.com" appears in the SAN of certs/server-cert.pem */ - ExpectIntEQ(test_wolfSSL_Tls13_ECH_rejected_cert_valid_ex("example.com", 1), + ExpectIntEQ(test_wolfSSL_Tls13_ECH_rejected_cert_valid_ex(echPublicName, 1), TEST_SUCCESS); ExpectIntEQ(test_wolfSSL_Tls13_ECH_rejected_cert_valid_ex("badname.com", 0), TEST_SUCCESS); @@ -15958,46 +16247,33 @@ static int test_wolfSSL_Tls13_ECH_rejected_empty_client_cert(void) { EXPECT_DECLS; test_ssl_memio_ctx test_ctx; - byte echConfigs[ECH_CONFIG_LEN]; - word32 echConfigsLen = sizeof(echConfigs); - const char* publicName = "example.com"; 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_ech_public_cert_setup(test_ctx); + 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 ECH config with public_name matching the server cert SAN */ - ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(test_ctx.s_ctx, publicName, - 0, 0, 0), WOLFSSL_SUCCESS); - ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(test_ctx.s_ctx, echConfigs, - &echConfigsLen), WOLFSSL_SUCCESS); - - /* Client loads ECH configs and sets a private SNI */ - ExpectIntEQ(wolfSSL_SetEchConfigs(test_ctx.c_ssl, echConfigs, - echConfigsLen), WOLFSSL_SUCCESS); + /* bad config derived from the server's real one to force ECH rejection */ + ExpectIntEQ(test_ech_set_bad_echconfigs(test_ctx.s_ctx, test_ctx.c_ssl), + TEST_SUCCESS); ExpectIntEQ(wolfSSL_UseSNI(test_ctx.c_ssl, WOLFSSL_SNI_HOST_NAME, - "ech-private.com", (word16)XSTRLEN("ech-private.com")), - WOLFSSL_SUCCESS); + echPrivateName, (word16)XSTRLEN(echPrivateName)), WOLFSSL_SUCCESS); + /* Server requests client cert; on reject the client returns an empty one */ wolfSSL_set_verify(test_ctx.s_ssl, WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_PEER, NULL); - /* Disable ECH on the server so ECH is rejected */ - wolfSSL_SetEchEnable(test_ctx.s_ssl, 0); - - /* Match the Server SNI to the ECH public_name */ - ExpectIntEQ(wolfSSL_UseSNI(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, - publicName, (word16)XSTRLEN(publicName)), WOLFSSL_SUCCESS); - ExpectIntNE(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.c_ssl), WOLFSSL_ECH_STATUS_REJECTED); ExpectIntEQ(wolfSSL_GetEchStatus(test_ctx.s_ssl), - WOLFSSL_ECH_STATUS_NOT_OFFERED); + WOLFSSL_ECH_STATUS_REJECTED); /* Server cert is valid for public_name, cert check passes, ech_required * is sent on the client side. */ @@ -16014,9 +16290,25 @@ static int test_wolfSSL_Tls13_ECH_rejected_empty_client_cert(void) return EXPECT_RESULT(); } +/* test vector for TLSX_Parse */ +typedef struct echSniVec { + const char* desc; /* human-readable label */ + const char* sSNI; /* server-side SNI */ + const char* innerName; /* client private SNI */ + const char* outerName; /* client public SNI */ + const char* authoritative; /* value expected from wolfSSL_SNI_GetRequest */ + int outerRet; /* expected return from parsing the outer CH */ + int innerRet; /* expected return from parsing the inner CH */ + int reject; /* setup client's ECH to be rejected */ + byte sniOpt; /* options for the server SNI */ + byte reorder; /* swap SNI/ECH so ECH parses first */ + byte reparseSni; /* re-parse outer SNI after accept (post-HRR) */ +} echSniVec; + /* Install ECH on the server and initialize the ECH AAD, then run TLSX_Parse. * ECH acceptance will be determined during TLSX_Parse */ -static int ech_server_parse_outer_ch(WOLFSSL* ssl, byte* chRecord) +static int ech_server_parse_outer_ch(WOLFSSL* ssl, byte* chRecord, + const echSniVec* v) { byte* chBody = chRecord + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; TLSX* echX; @@ -16025,6 +16317,13 @@ static int ech_server_parse_outer_ch(WOLFSSL* ssl, byte* chRecord) word16 extOff; int ret; + /* reorder now only if rejection is desired */ + if (v->reorder && v->reject) { + ret = ech_swap_extensions(chBody, TLSXT_SERVER_NAME, TLSXT_ECH); + if (ret != 0) + return ret; + } + ret = TLSX_ServerECH_Use(&ssl->extensions, ssl->heap, ssl->ctx->echConfigs); if (ret != 0) return ret; @@ -16047,7 +16346,7 @@ static int ech_server_parse_outer_ch(WOLFSSL* ssl, byte* chRecord) } /* If ECH is accepted, run the decrypted inner extensions through TLSX_Parse */ -static int ech_server_parse_inner_ch(WOLFSSL* ssl) +static int ech_server_parse_inner_ch(WOLFSSL* ssl, const echSniVec* v) { TLSX* echX = TLSX_Find(ssl->extensions, TLSX_ECH); WOLFSSL_ECH* ech; @@ -16063,6 +16362,12 @@ static int ech_server_parse_inner_ch(WOLFSSL* ssl) return WOLFSSL_FATAL_ERROR; innerBody = ech->innerClientHello + HANDSHAKE_HEADER_SZ; + if (v->reorder) { + ret = ech_swap_extensions(innerBody, TLSXT_SERVER_NAME, TLSXT_ECH); + if (ret != 0) + return ret; + } + ret = ech_seek_extensions(innerBody, &extLen); if (ret < 0) return ret; @@ -16076,18 +16381,33 @@ static int ech_server_parse_inner_ch(WOLFSSL* ssl) return ret; } -/* test vector for TLSX_Parse */ -typedef struct echSniVec { - const char* desc; /* human-readable label */ - const char* sSNI; /* server-side SNI */ - const char* innerName; /* client private SNI */ - const char* outerName; /* client public SNI */ - const char* authoritative; /* value expected from wolfSSL_SNI_GetRequest */ - int outerRet; /* expected return from parsing the outer CH */ - int innerRet; /* expected return from parsing the inner CH */ - int reject; /* setup client's ECH to be rejected */ - byte sniOpt; /* options for the server SNI */ -} echSniVec; +/* Re-parse the outer server_name through TLSX_Parse */ +static int ech_server_reparse_outer_sni(WOLFSSL* ssl, byte* chRecord, + const echSniVec* v) +{ + word16 idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; + word16 sniLen; + int ret; + + /* extensions must be swapped, otherwise ECH would cause an error */ + if (!v->reorder) { + ret = ech_swap_extensions(chRecord + idx, TLSXT_SERVER_NAME, TLSXT_ECH); + if (ret != 0) + return ret; + } + + ret = ech_find_extension(chRecord, &idx, TLSXT_SERVER_NAME); + if (ret != 0) + return ret; + + /* idx is at the extension header: type(2) + length(2) + body */ + ato16(chRecord + idx + OPAQUE16_LEN, &sniLen); + + ret = TLSX_Parse(ssl, chRecord + idx, + (word16)(sniLen + OPAQUE16_LEN + OPAQUE16_LEN), client_hello, + (Suites*)WOLFSSL_SUITES(ssl)); + return ret; +} #ifdef WOLFSSL_ALWAYS_KEEP_SNI #define ECH_KEPT(name) (name) @@ -16165,7 +16485,7 @@ static int test_ech_sni_parse_vec(WOLFSSL_CTX* serverCtx, const echSniVec* v) ExpectIntEQ(wolfSSL_get_error(test_ctx.c_ssl, WOLFSSL_FATAL_ERROR), WOLFSSL_ERROR_WANT_READ); - ExpectIntEQ(ech_server_parse_outer_ch(test_ctx.s_ssl, test_ctx.s_buff), + ExpectIntEQ(ech_server_parse_outer_ch(test_ctx.s_ssl, test_ctx.s_buff, v), v->outerRet); /* ECH acceptance must line up */ @@ -16174,17 +16494,21 @@ static int test_ech_sni_parse_vec(WOLFSSL_CTX* serverCtx, const echSniVec* v) /* on accept the inner hello must be parsed to learn the private name */ doInner = (!v->reject && v->outerRet == 0); if (doInner) - ExpectIntEQ(ech_server_parse_inner_ch(test_ctx.s_ssl), v->innerRet); + ExpectIntEQ(ech_server_parse_inner_ch(test_ctx.s_ssl, v), v->innerRet); /* verify the authoritative SNI has the correct name and state */ - if (v->outerRet == 0 && (!doInner || v->innerRet == 0)) { - wolfSSL_SNI_GetRequest(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); - if (v->authoritative != NULL) - ExpectStrEQ((const char*)sniName, v->authoritative); - else - ExpectNull(sniName); - ExpectIntEQ(wolfSSL_SNI_Status(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME), - ech_sni_expected_status(v)); + wolfSSL_SNI_GetRequest(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME, &sniName); + if (v->authoritative != NULL) + ExpectStrEQ((const char*)sniName, v->authoritative); + else + ExpectNull(sniName); + ExpectIntEQ(wolfSSL_SNI_Status(test_ctx.s_ssl, WOLFSSL_SNI_HOST_NAME), + ech_sni_expected_status(v)); + + /* re-parse the outer SNI as a second (post-HRR) ClientHello would */ + if (v->reparseSni && v->outerRet == 0) { + ExpectIntEQ(ech_server_reparse_outer_sni(test_ctx.s_ssl, + test_ctx.s_buff, v), 0); } test_ssl_memio_cleanup(&test_ctx); @@ -16203,55 +16527,72 @@ static int test_wolfSSL_Tls13_ECH_sni_parse(void) /* --- ECH rejected: outer (public) name governs --- */ { "reject: no sSNI, outer=public", NULL, echPrivateName, echPublicName, - echPublicName, 0, 0, 1, 0 }, + echPublicName, 0, 0, 1, 0, 0, 0 }, /* "reject: sSNI=private, outer=public" is the generic ECH-reject * scenario (server keeps its private SNI, client falls back to the * public name); it is exercised by most ECH handshake tests. */ { "reject: sSNI=public, outer=public", echPublicName, echPrivateName, echPublicName, - echPublicName, 0, 0, 1, 0 }, + echPublicName, 0, 0, 1, 0, 0, 0 }, { "reject: no sSNI, outer=other", NULL, echPrivateName, echOtherName, - ECH_KEPT(echOtherName), 0, 0, 1, 0 }, + ECH_KEPT(echOtherName), 0, 0, 1, 0, 0, 0 }, { "reject: sSNI=private, outer=other", echPrivateName, echPrivateName, echOtherName, - NULL, WC_NO_ERR_TRACE(UNKNOWN_SNI_HOST_NAME_E), 0, 1, 0 }, + NULL, WC_NO_ERR_TRACE(UNKNOWN_SNI_HOST_NAME_E), 0, 1, 0, 0, 0 }, { "reject: sSNI=private+continue, outer=other", echPrivateName, echPrivateName, echOtherName, - NULL, 0, 0, 1, WOLFSSL_SNI_CONTINUE_ON_MISMATCH }, + NULL, 0, 0, 1, WOLFSSL_SNI_CONTINUE_ON_MISMATCH, 0, 0 }, { "reject: sSNI=private+answer, outer=other", echPrivateName, echPrivateName, echOtherName, - echOtherName, 0, 0, 1, WOLFSSL_SNI_ANSWER_ON_MISMATCH }, + echOtherName, 0, 0, 1, WOLFSSL_SNI_ANSWER_ON_MISMATCH, 0, 0 }, /* --- ECH accepted: inner (private) name governs --- */ { "accept: no sSNI, no inner", NULL, NULL, echPublicName, - NULL, 0, 0, 0, 0 }, + NULL, 0, 0, 0, 0, 0, 0 }, { "accept: no sSNI, inner=private", NULL, echPrivateName, echPublicName, - ECH_KEPT(echPrivateName), 0, 0, 0, 0 }, + ECH_KEPT(echPrivateName), 0, 0, 0, 0, 0, 0 }, /* "accept: sSNI=private, inner=private" is the generic ECH-accept * scenario (private inner name matches the server SNI); it is * exercised by most ECH handshake tests. */ + { "accept: sSNI=private, no inner", + echPrivateName, NULL, echPublicName, + NULL, 0, 0, 0, 0, 0, 0 }, { "accept: sSNI=private+abort, no inner", echPrivateName, NULL, echPublicName, NULL, 0, WC_NO_ERR_TRACE(SNI_ABSENT_ERROR), 0, - WOLFSSL_SNI_ABORT_ON_ABSENCE }, + WOLFSSL_SNI_ABORT_ON_ABSENCE, 0, 0 }, { "accept: sSNI=private, inner=other", echPrivateName, echOtherName, echPublicName, - NULL, 0, WC_NO_ERR_TRACE(UNKNOWN_SNI_HOST_NAME_E), 0, 0 }, + NULL, 0, WC_NO_ERR_TRACE(UNKNOWN_SNI_HOST_NAME_E), 0, 0, 0, 0 }, { "accept: sSNI=private+continue, inner=other", echPrivateName, echOtherName, echPublicName, - NULL, 0, 0, 0, WOLFSSL_SNI_CONTINUE_ON_MISMATCH }, + NULL, 0, 0, 0, WOLFSSL_SNI_CONTINUE_ON_MISMATCH, 0, 0 }, { "accept: sSNI=private+answer, inner=other", echPrivateName, echOtherName, echPublicName, - echOtherName, 0, 0, 0, WOLFSSL_SNI_ANSWER_ON_MISMATCH }, + echOtherName, 0, 0, 0, WOLFSSL_SNI_ANSWER_ON_MISMATCH, 0, 0 }, { "accept: sSNI=public, inner=public", echPublicName, echPublicName, echPublicName, - echPublicName, 0, 0, 0, 0 }, + echPublicName, 0, 0, 0, 0, 0, 0 }, { "accept: sSNI=public, inner=private", echPublicName, echPrivateName, echPublicName, - NULL, 0, WC_NO_ERR_TRACE(UNKNOWN_SNI_HOST_NAME_E), 0, 0 }, + NULL, 0, WC_NO_ERR_TRACE(UNKNOWN_SNI_HOST_NAME_E), 0, 0, 0, 0 }, + + /* --- ECH accepted / rejected, reorder and reparse --- */ + { "reorder, reject: ECH before SNI", + echPrivateName, echPrivateName, echPublicName, + echPublicName, 0, 0, 1, 0, 1, 0 }, + { "reorder, accept: ECH before SNI", + echPrivateName, echPrivateName, echPublicName, + echPrivateName, 0, 0, 0, 0, 1, 0 }, + { "reparse, reject: outer SNI re-parsed, no sSNI", + NULL, echPrivateName, echPublicName, + echPublicName, 0, 0, 1, 0, 0, 1 }, + { "reparse, accept: outer SNI re-parsed, no sSNI", + NULL, echPrivateName, echPublicName, + ECH_KEPT(echPrivateName), 0, 0, 0, 0, 0, 1 }, }; /* One server CTX, reused by every vector. test_ssl_memio_setup treats a @@ -16294,7 +16635,7 @@ static int test_wolfSSL_Tls13_ECH_enable_disable(void) /* test CTX level enable/disable */ ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())); - ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(ctx, "public.com", 0, 0, 0), + ExpectIntEQ(wolfSSL_CTX_GenerateEchConfig(ctx, echPublicName, 0, 0, 0), WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_CTX_GetEchConfigs(ctx, echConfigs, &echConfigsLen), WOLFSSL_SUCCESS); @@ -16456,6 +16797,7 @@ static int test_wolfSSL_Tls13_ECH_tamper_ex(struct test_ssl_memio_ctx* test_ctx) test_ctx->s_cb.method = wolfTLSv1_3_server_method; test_ctx->c_cb.method = wolfTLSv1_3_client_method; + test_ech_public_cert_setup(*test_ctx); test_ctx->s_cb.ctx_ready = test_ech_server_ctx_ready; test_ctx->s_cb.ssl_ready = test_ech_server_ssl_ready; @@ -16477,6 +16819,7 @@ static int test_wolfSSL_Tls13_ECH_tamper_client(void) /* try to downgrade to TLS 1.2 in the inner hello */ test_ctx.s_cb.method = wolfSSLv23_server_method; test_ctx.c_cb.method = wolfSSLv23_client_method; + test_ech_public_cert_setup(test_ctx); test_ctx.s_cb.ctx_ready = test_ech_server_ctx_ready; test_ctx.s_cb.ssl_ready = test_ech_server_ssl_ready; @@ -36904,7 +37247,6 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_Tls13_ECH_params_b64), /* 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) @@ -36916,8 +37258,12 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_Tls13_ECH_new_config), TEST_DECL(test_wolfSSL_Tls13_ECH_trial_decrypt), TEST_DECL(test_wolfSSL_Tls13_ECH_GREASE), - TEST_DECL(test_wolfSSL_Tls13_ECH_wire_sni), + TEST_DECL(test_wolfSSL_Tls13_ECH_ctx_sni), TEST_DECL(test_wolfSSL_Tls13_ECH_disable_conn), + TEST_DECL(test_wolfSSL_Tls13_ECH_multi_sni), +#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) + TEST_DECL(test_wolfSSL_Tls13_ECH_multi_ctx), +#endif TEST_DECL(test_wolfSSL_Tls13_ECH_HRR_rejection), TEST_DECL(test_wolfSSL_Tls13_ECH_ch2_no_ech), TEST_DECL(test_wolfSSL_Tls13_ECH_ch2_decrypt_error), diff --git a/wolfssl/certs_test.h b/wolfssl/certs_test.h index 64b8c204dd7..b1dd34ddf64 100644 --- a/wolfssl/certs_test.h +++ b/wolfssl/certs_test.h @@ -3039,6 +3039,264 @@ static const unsigned char tsa_chain_cert_der_2048[] = }; #define sizeof_tsa_chain_cert_der_2048 (sizeof(tsa_chain_cert_der_2048)) +/* ./certs/ech-public-key.der, 2048-bit */ +static const unsigned char ech_public_key_der_2048[] = +{ + 0x30, 0x82, 0x04, 0xBC, 0x02, 0x01, 0x00, 0x30, 0x0D, 0x06, + 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x04, 0x82, 0x04, 0xA6, 0x30, 0x82, 0x04, 0xA2, + 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xD1, 0x42, + 0x85, 0xB2, 0xA7, 0x81, 0x6F, 0xE7, 0x1B, 0x26, 0xC6, 0xF2, + 0x53, 0xD1, 0x42, 0xFF, 0x18, 0xF1, 0x0B, 0x3F, 0x75, 0xA0, + 0x01, 0xFA, 0x5C, 0x8F, 0xB2, 0xC7, 0xB4, 0x06, 0xF5, 0xA5, + 0x6E, 0xE2, 0xAE, 0xCC, 0xA5, 0x5D, 0x35, 0xB4, 0x56, 0x28, + 0x21, 0xD3, 0x59, 0xB1, 0x6E, 0x81, 0x3F, 0x69, 0x7D, 0x85, + 0xE9, 0x33, 0x0A, 0xCD, 0x7A, 0x32, 0x8A, 0xFC, 0x41, 0x5A, + 0x52, 0x1E, 0x23, 0x9E, 0x2A, 0x22, 0xD4, 0xC9, 0x20, 0x7E, + 0x46, 0xDB, 0x6C, 0x4D, 0x56, 0xE8, 0x18, 0xAA, 0x79, 0xB7, + 0xE7, 0x20, 0xB9, 0xF3, 0x97, 0x28, 0x1D, 0x63, 0x44, 0x03, + 0xB4, 0x05, 0x7B, 0x03, 0x93, 0x4C, 0x3F, 0x83, 0x8E, 0x21, + 0xC4, 0x4E, 0xF2, 0xEE, 0x28, 0x8C, 0x01, 0x39, 0xDE, 0xAA, + 0x64, 0x57, 0x34, 0x9A, 0x58, 0x35, 0x89, 0xC1, 0xEB, 0x4E, + 0xFC, 0x8E, 0x23, 0xFD, 0x10, 0xAD, 0x1E, 0xCA, 0x97, 0x4E, + 0xA9, 0x8F, 0x5D, 0xD7, 0xF9, 0x52, 0xFD, 0x3A, 0x45, 0x90, + 0xCD, 0x21, 0x97, 0xBC, 0xB4, 0xE2, 0x8E, 0xC2, 0x60, 0x16, + 0x43, 0x51, 0xB3, 0x71, 0xE6, 0x54, 0xEC, 0x60, 0x00, 0x76, + 0xB7, 0xF6, 0x06, 0xB1, 0x0C, 0xC9, 0x93, 0xD7, 0x6C, 0xC6, + 0x9E, 0xAB, 0x0D, 0x4D, 0x58, 0xF9, 0xD5, 0xC7, 0xFC, 0xD1, + 0xD9, 0x53, 0x66, 0xB7, 0x47, 0x4B, 0x4A, 0x12, 0x6B, 0x37, + 0x23, 0xB4, 0xDF, 0xF6, 0x21, 0xD4, 0x23, 0x6B, 0x92, 0x2D, + 0xF7, 0xCA, 0xE5, 0x90, 0x53, 0xC7, 0x84, 0x29, 0xC0, 0xA0, + 0xAC, 0xD5, 0x31, 0x73, 0x72, 0x05, 0x27, 0x55, 0xF6, 0x4D, + 0x1A, 0xC9, 0xE5, 0xDA, 0xD9, 0x8E, 0x54, 0x37, 0x77, 0x9C, + 0x3B, 0x7F, 0x2A, 0xB9, 0x72, 0x6F, 0xED, 0x2E, 0xA9, 0x36, + 0x66, 0xCF, 0xF2, 0x4F, 0x29, 0x6E, 0xA0, 0x92, 0x10, 0x05, + 0x6A, 0x37, 0x2C, 0x79, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x65, 0xA0, 0x14, 0x55, 0x10, 0x93, 0x44, + 0x58, 0xAC, 0xAF, 0x76, 0xD3, 0xCD, 0xCF, 0xE5, 0x65, 0x04, + 0xE4, 0x5F, 0x2D, 0x88, 0xFE, 0xD5, 0xA7, 0x14, 0x57, 0xB7, + 0x1F, 0xD4, 0x36, 0xC3, 0x0A, 0x7E, 0x20, 0xF9, 0x7A, 0x2A, + 0x5E, 0xEB, 0x8B, 0x72, 0x82, 0x17, 0xF7, 0x4F, 0xEA, 0x62, + 0xFB, 0xC2, 0xE3, 0x84, 0xD1, 0xC9, 0x50, 0x80, 0x11, 0x0D, + 0x24, 0xC9, 0xA9, 0xBB, 0x45, 0x55, 0x77, 0x0C, 0x9E, 0x2D, + 0xE1, 0x6F, 0x8C, 0x63, 0xCA, 0xDF, 0xB7, 0x85, 0x4F, 0xC4, + 0x02, 0x80, 0xA4, 0x1D, 0xEB, 0xA9, 0x84, 0x13, 0xBA, 0x90, + 0x70, 0xAB, 0x60, 0xFF, 0x71, 0x7A, 0xF6, 0x64, 0x68, 0xF6, + 0x9A, 0x83, 0x87, 0xEC, 0xB9, 0x4A, 0x60, 0x7B, 0x0B, 0x1D, + 0x46, 0xEC, 0xFB, 0x81, 0x3F, 0x27, 0xEE, 0xCF, 0xFD, 0x9B, + 0x14, 0x19, 0x1C, 0x43, 0x9E, 0x3E, 0xF9, 0xFA, 0xD1, 0x56, + 0x4C, 0x93, 0x85, 0x9F, 0x19, 0x97, 0xBE, 0x0C, 0x53, 0x23, + 0x42, 0x59, 0x5B, 0xA7, 0x4F, 0xD3, 0x38, 0xDE, 0x8E, 0x49, + 0x6D, 0xB7, 0x1A, 0xA4, 0xB7, 0xA0, 0xC4, 0x6F, 0x64, 0x59, + 0xC4, 0x02, 0x85, 0x41, 0xD5, 0x53, 0x95, 0x3B, 0x66, 0x38, + 0x92, 0x1B, 0x67, 0x8A, 0x39, 0x66, 0xD8, 0xEE, 0xA6, 0x34, + 0xF3, 0xF9, 0x70, 0x36, 0x7A, 0xFA, 0x5F, 0x5C, 0x7F, 0x49, + 0x58, 0x92, 0xD3, 0xBA, 0x74, 0xB4, 0xDC, 0xB4, 0xD9, 0xC5, + 0xC1, 0xB1, 0x55, 0x88, 0xB5, 0x96, 0xF9, 0xD0, 0xBB, 0x69, + 0x49, 0xDB, 0x4C, 0x49, 0xF2, 0x64, 0x13, 0xC1, 0x1D, 0xE9, + 0x9E, 0x70, 0x41, 0x68, 0x20, 0x0E, 0x57, 0xBD, 0x96, 0x5E, + 0x9D, 0x39, 0x44, 0x94, 0xBC, 0xAD, 0x33, 0xAF, 0x3A, 0xB5, + 0x9E, 0x4E, 0xD7, 0xFE, 0x22, 0x7B, 0xD7, 0xCD, 0xFB, 0xFC, + 0x0F, 0x59, 0x4C, 0x5C, 0xE9, 0x10, 0xA3, 0xB2, 0x1F, 0x02, + 0x81, 0x81, 0x00, 0xFB, 0x27, 0x64, 0x23, 0x3F, 0xEB, 0xB8, + 0xE2, 0xFF, 0xC6, 0x9F, 0x83, 0xCA, 0xE5, 0xFE, 0xA9, 0xDD, + 0x63, 0x2F, 0x21, 0xFA, 0xAC, 0xD9, 0xDC, 0xEE, 0x5F, 0x02, + 0xD1, 0xD5, 0xEA, 0xBE, 0xB9, 0x4B, 0x34, 0xD4, 0x7E, 0x5F, + 0x39, 0xE0, 0x9E, 0xD3, 0xE9, 0x7C, 0x4E, 0xEC, 0x48, 0x96, + 0x44, 0x7D, 0xAC, 0xE9, 0xDC, 0x47, 0xB8, 0x9A, 0xC9, 0x11, + 0x6D, 0xC6, 0x29, 0xE0, 0x71, 0xC1, 0x34, 0x44, 0x3D, 0xD4, + 0x1E, 0x2B, 0xEB, 0xED, 0x10, 0x9D, 0x4E, 0xBC, 0x80, 0xEB, + 0x40, 0x89, 0xF0, 0x28, 0x15, 0x90, 0xDE, 0xD4, 0xF7, 0x1D, + 0x70, 0x73, 0xB4, 0x89, 0x21, 0x61, 0x37, 0x3A, 0x1D, 0x4F, + 0x96, 0x4F, 0x2D, 0x7C, 0x8F, 0xA7, 0xD7, 0xA0, 0x08, 0xD8, + 0x4A, 0xC5, 0x95, 0x5C, 0xC6, 0x6F, 0xD8, 0xC4, 0x3B, 0x39, + 0x2F, 0x48, 0xE6, 0x56, 0x2C, 0x0C, 0x7B, 0x50, 0x30, 0xE9, + 0x9B, 0x02, 0x81, 0x81, 0x00, 0xD5, 0x4C, 0x30, 0x9B, 0x25, + 0xF2, 0xFF, 0xE5, 0xCD, 0xE9, 0xC6, 0xD3, 0xD1, 0xE9, 0xE8, + 0x09, 0x34, 0xB7, 0x50, 0xF2, 0x95, 0x70, 0xC6, 0x0C, 0xC2, + 0x7A, 0x5A, 0x1A, 0xC4, 0xB0, 0x0A, 0x67, 0x2E, 0xC9, 0xAF, + 0x6C, 0x20, 0xCF, 0xE1, 0x02, 0xDF, 0xFE, 0x3C, 0xF7, 0xEA, + 0x58, 0xFE, 0x73, 0x93, 0x39, 0xFB, 0xFE, 0x6E, 0xD9, 0xB3, + 0xF4, 0x5F, 0xB2, 0xB4, 0x6B, 0x8D, 0x06, 0x24, 0x0F, 0xC9, + 0xB9, 0x7A, 0x8A, 0x5D, 0x8C, 0xD0, 0x9D, 0xF7, 0x24, 0xAC, + 0x25, 0x67, 0x28, 0x17, 0x1C, 0x94, 0x83, 0x8F, 0x9B, 0xC4, + 0x0A, 0x19, 0x10, 0xCB, 0x3E, 0xAB, 0x95, 0x2F, 0x1E, 0xBE, + 0xE5, 0xD0, 0xE8, 0x2E, 0x59, 0x75, 0xA5, 0x0D, 0x7C, 0x94, + 0x59, 0x55, 0x94, 0x00, 0x6B, 0x8B, 0xC2, 0x79, 0x8A, 0x36, + 0x28, 0x7E, 0x3B, 0x95, 0x02, 0xB7, 0x38, 0x2B, 0x45, 0x2C, + 0x77, 0x3D, 0x7B, 0x02, 0x81, 0x80, 0x61, 0xBD, 0xCD, 0xC1, + 0xCE, 0x21, 0x8D, 0x9D, 0x82, 0x0F, 0x29, 0x8B, 0x1F, 0xCE, + 0x6C, 0x52, 0x6D, 0x1F, 0x29, 0x7B, 0x9E, 0xD3, 0xE0, 0x21, + 0x93, 0xFF, 0x13, 0xD8, 0xD7, 0xD8, 0x78, 0x8B, 0x22, 0x12, + 0x98, 0x53, 0xE9, 0xF9, 0x5B, 0x4A, 0x54, 0x8C, 0x3C, 0x5D, + 0x76, 0xA5, 0x35, 0x1D, 0xC8, 0x8F, 0x1F, 0xA0, 0x23, 0xA5, + 0x5B, 0x4F, 0xBE, 0xE1, 0xAA, 0xCA, 0x27, 0x57, 0xFD, 0xE9, + 0xBA, 0x59, 0x19, 0x90, 0xF8, 0x64, 0xE3, 0xA8, 0xA0, 0x9F, + 0xC0, 0xCA, 0xA7, 0x43, 0x41, 0xAB, 0x5D, 0xDE, 0x2E, 0xB8, + 0x19, 0xC7, 0x66, 0x04, 0x9A, 0x72, 0xFB, 0x1F, 0x2B, 0x77, + 0x53, 0xBD, 0x8E, 0x78, 0x9F, 0xAD, 0xC3, 0x19, 0x21, 0x27, + 0xD0, 0x57, 0x40, 0xDD, 0xB0, 0x91, 0x55, 0x3E, 0xC0, 0xCE, + 0x6A, 0x67, 0xE5, 0xF4, 0x59, 0xF9, 0x92, 0x35, 0xA8, 0x51, + 0xE1, 0x3E, 0x77, 0xDB, 0x02, 0x81, 0x80, 0x24, 0xE5, 0xC6, + 0x2E, 0xC0, 0x94, 0xD5, 0xB1, 0xA2, 0x3A, 0x43, 0x9B, 0xD6, + 0xDF, 0x38, 0xB5, 0x86, 0xFA, 0x03, 0x35, 0xC3, 0xA6, 0x3B, + 0x8C, 0x6F, 0x84, 0x0C, 0xDC, 0x3E, 0x94, 0x19, 0x68, 0x9A, + 0x0E, 0x67, 0x9B, 0x33, 0xDD, 0xE2, 0x15, 0xC6, 0x3B, 0xCB, + 0xBE, 0x33, 0x3A, 0xEB, 0xE1, 0x80, 0x61, 0x72, 0x77, 0x24, + 0x23, 0xE2, 0xDE, 0x6E, 0x43, 0x92, 0xB4, 0x5C, 0x33, 0xE8, + 0xBF, 0x16, 0x6D, 0x50, 0x86, 0x69, 0x02, 0x78, 0x00, 0x2B, + 0x85, 0x9B, 0x85, 0xE8, 0xDF, 0x65, 0x88, 0x1B, 0x24, 0xDB, + 0x0D, 0xDE, 0x0E, 0xC5, 0x21, 0x43, 0x97, 0x76, 0xC5, 0x1A, + 0xDE, 0x9D, 0xB5, 0x51, 0xBB, 0x28, 0x71, 0x6F, 0x49, 0x7B, + 0x18, 0xC6, 0xEC, 0xE1, 0x41, 0xF6, 0x04, 0xA4, 0xAB, 0xA2, + 0xAC, 0xC7, 0x0B, 0xA0, 0x32, 0x9C, 0x67, 0x58, 0xDC, 0x8D, + 0x97, 0xAC, 0xC6, 0x7F, 0xCB, 0x02, 0x81, 0x80, 0x52, 0xEC, + 0x18, 0xE0, 0x01, 0xFE, 0xB2, 0x29, 0xBC, 0xD2, 0xB3, 0xA7, + 0x99, 0x68, 0x3D, 0xCD, 0x76, 0xAF, 0x58, 0xE4, 0x62, 0xC1, + 0xFD, 0xB8, 0x3A, 0x12, 0x4B, 0x6B, 0x5D, 0x89, 0x9D, 0x19, + 0x92, 0x40, 0x6F, 0x8E, 0x30, 0x7B, 0x76, 0x1F, 0xDE, 0xCB, + 0x15, 0x87, 0x5B, 0xB8, 0x4A, 0x65, 0x02, 0xD2, 0x75, 0x26, + 0x1D, 0xBC, 0x26, 0xB9, 0x9C, 0x3B, 0x35, 0x4F, 0x9C, 0xFA, + 0x82, 0x87, 0x22, 0x30, 0x4F, 0x09, 0x03, 0x44, 0x75, 0xFB, + 0x47, 0xAC, 0x9E, 0x53, 0xDD, 0xA1, 0xC0, 0x40, 0x64, 0xEF, + 0x6B, 0x18, 0x33, 0xB7, 0xA7, 0x4C, 0x69, 0x2E, 0x74, 0x4B, + 0xE8, 0xB0, 0xB0, 0xDA, 0x95, 0xAF, 0x5A, 0x5F, 0x8D, 0x48, + 0x99, 0x27, 0xE4, 0x22, 0x07, 0xCA, 0x5E, 0xAA, 0x51, 0xD4, + 0xDE, 0x49, 0x4A, 0x6B, 0x03, 0x60, 0x77, 0x2B, 0x49, 0x65, + 0x2D, 0x6C, 0xEA, 0x39, 0x75, 0xFE +}; +#define sizeof_ech_public_key_der_2048 (sizeof(ech_public_key_der_2048)) + +/* ./certs/ech-public-cert.der, 2048-bit */ +static const unsigned char ech_public_cert_der_2048[] = +{ + 0x30, 0x82, 0x04, 0xCD, 0x30, 0x82, 0x03, 0xB5, 0xA0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x01, 0x23, 0x30, 0x0D, 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, + 0x00, 0x30, 0x81, 0x95, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, + 0x0E, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, + 0x6E, 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, + 0x6D, 0x61, 0x6E, 0x31, 0x11, 0x30, 0x0F, 0x06, 0x03, 0x55, + 0x04, 0x0A, 0x0C, 0x08, 0x53, 0x61, 0x77, 0x74, 0x6F, 0x6F, + 0x74, 0x68, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x0B, 0x0C, 0x0A, 0x43, 0x6F, 0x6E, 0x73, 0x75, 0x6C, 0x74, + 0x69, 0x6E, 0x67, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, + 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, + 0x20, 0x30, 0x1E, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x09, 0x01, 0x16, 0x11, 0x66, 0x61, 0x63, 0x74, + 0x73, 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, + 0x63, 0x6F, 0x6D, 0x30, 0x1E, 0x17, 0x0D, 0x32, 0x36, 0x30, + 0x37, 0x31, 0x30, 0x32, 0x30, 0x35, 0x34, 0x31, 0x36, 0x5A, + 0x17, 0x0D, 0x32, 0x39, 0x30, 0x34, 0x30, 0x35, 0x32, 0x30, + 0x35, 0x34, 0x31, 0x36, 0x5A, 0x30, 0x81, 0x87, 0x31, 0x0B, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x0C, 0x07, 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E, 0x61, 0x31, + 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07, + 0x42, 0x6F, 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31, 0x10, 0x30, + 0x0E, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07, 0x77, 0x6F, + 0x6C, 0x66, 0x53, 0x53, 0x4C, 0x31, 0x0C, 0x30, 0x0A, 0x06, + 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x03, 0x45, 0x43, 0x48, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0A, + 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x2E, 0x63, 0x6F, 0x6D, + 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E, 0x66, + 0x6F, 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, + 0x63, 0x6F, 0x6D, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, + 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, + 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xD1, 0x42, 0x85, 0xB2, + 0xA7, 0x81, 0x6F, 0xE7, 0x1B, 0x26, 0xC6, 0xF2, 0x53, 0xD1, + 0x42, 0xFF, 0x18, 0xF1, 0x0B, 0x3F, 0x75, 0xA0, 0x01, 0xFA, + 0x5C, 0x8F, 0xB2, 0xC7, 0xB4, 0x06, 0xF5, 0xA5, 0x6E, 0xE2, + 0xAE, 0xCC, 0xA5, 0x5D, 0x35, 0xB4, 0x56, 0x28, 0x21, 0xD3, + 0x59, 0xB1, 0x6E, 0x81, 0x3F, 0x69, 0x7D, 0x85, 0xE9, 0x33, + 0x0A, 0xCD, 0x7A, 0x32, 0x8A, 0xFC, 0x41, 0x5A, 0x52, 0x1E, + 0x23, 0x9E, 0x2A, 0x22, 0xD4, 0xC9, 0x20, 0x7E, 0x46, 0xDB, + 0x6C, 0x4D, 0x56, 0xE8, 0x18, 0xAA, 0x79, 0xB7, 0xE7, 0x20, + 0xB9, 0xF3, 0x97, 0x28, 0x1D, 0x63, 0x44, 0x03, 0xB4, 0x05, + 0x7B, 0x03, 0x93, 0x4C, 0x3F, 0x83, 0x8E, 0x21, 0xC4, 0x4E, + 0xF2, 0xEE, 0x28, 0x8C, 0x01, 0x39, 0xDE, 0xAA, 0x64, 0x57, + 0x34, 0x9A, 0x58, 0x35, 0x89, 0xC1, 0xEB, 0x4E, 0xFC, 0x8E, + 0x23, 0xFD, 0x10, 0xAD, 0x1E, 0xCA, 0x97, 0x4E, 0xA9, 0x8F, + 0x5D, 0xD7, 0xF9, 0x52, 0xFD, 0x3A, 0x45, 0x90, 0xCD, 0x21, + 0x97, 0xBC, 0xB4, 0xE2, 0x8E, 0xC2, 0x60, 0x16, 0x43, 0x51, + 0xB3, 0x71, 0xE6, 0x54, 0xEC, 0x60, 0x00, 0x76, 0xB7, 0xF6, + 0x06, 0xB1, 0x0C, 0xC9, 0x93, 0xD7, 0x6C, 0xC6, 0x9E, 0xAB, + 0x0D, 0x4D, 0x58, 0xF9, 0xD5, 0xC7, 0xFC, 0xD1, 0xD9, 0x53, + 0x66, 0xB7, 0x47, 0x4B, 0x4A, 0x12, 0x6B, 0x37, 0x23, 0xB4, + 0xDF, 0xF6, 0x21, 0xD4, 0x23, 0x6B, 0x92, 0x2D, 0xF7, 0xCA, + 0xE5, 0x90, 0x53, 0xC7, 0x84, 0x29, 0xC0, 0xA0, 0xAC, 0xD5, + 0x31, 0x73, 0x72, 0x05, 0x27, 0x55, 0xF6, 0x4D, 0x1A, 0xC9, + 0xE5, 0xDA, 0xD9, 0x8E, 0x54, 0x37, 0x77, 0x9C, 0x3B, 0x7F, + 0x2A, 0xB9, 0x72, 0x6F, 0xED, 0x2E, 0xA9, 0x36, 0x66, 0xCF, + 0xF2, 0x4F, 0x29, 0x6E, 0xA0, 0x92, 0x10, 0x05, 0x6A, 0x37, + 0x2C, 0x79, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x82, 0x01, + 0x32, 0x30, 0x82, 0x01, 0x2E, 0x30, 0x1D, 0x06, 0x03, 0x55, + 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0x6B, 0xD8, 0x10, 0x65, + 0xC9, 0x94, 0x68, 0x23, 0x2A, 0x1E, 0x67, 0x67, 0xCE, 0x90, + 0x1E, 0x92, 0x8D, 0x90, 0x52, 0x29, 0x30, 0x81, 0xD5, 0x06, + 0x03, 0x55, 0x1D, 0x23, 0x04, 0x81, 0xCD, 0x30, 0x81, 0xCA, + 0x80, 0x14, 0x27, 0x8E, 0x67, 0x11, 0x74, 0xC3, 0x26, 0x1D, + 0x3F, 0xED, 0x33, 0x63, 0xB3, 0xA4, 0xD8, 0x1D, 0x30, 0xE5, + 0xE8, 0xD5, 0xA1, 0x81, 0x9B, 0xA4, 0x81, 0x98, 0x30, 0x81, + 0x95, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E, 0x74, 0x61, + 0x6E, 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D, 0x61, 0x6E, + 0x31, 0x11, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, + 0x08, 0x53, 0x61, 0x77, 0x74, 0x6F, 0x6F, 0x74, 0x68, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x0A, + 0x43, 0x6F, 0x6E, 0x73, 0x75, 0x6C, 0x74, 0x69, 0x6E, 0x67, + 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, + 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, 0x6C, 0x66, 0x73, + 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x20, 0x30, 0x1E, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, + 0x01, 0x16, 0x11, 0x66, 0x61, 0x63, 0x74, 0x73, 0x40, 0x77, + 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, + 0x82, 0x14, 0x67, 0x19, 0xD2, 0xA8, 0x7F, 0xE3, 0x2D, 0xFA, + 0x75, 0x7A, 0x4F, 0xE7, 0xB2, 0x02, 0xD9, 0xAD, 0xC4, 0x77, + 0x5E, 0xF8, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x04, + 0x02, 0x30, 0x00, 0x30, 0x15, 0x06, 0x03, 0x55, 0x1D, 0x11, + 0x04, 0x0E, 0x30, 0x0C, 0x82, 0x0A, 0x70, 0x75, 0x62, 0x6C, + 0x69, 0x63, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x1D, 0x25, 0x04, 0x0C, 0x30, 0x0A, 0x06, 0x08, 0x2B, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x0D, 0x06, + 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x63, 0x8A, 0xF1, + 0xB3, 0xA2, 0xE4, 0xE6, 0x28, 0x6A, 0x63, 0xC5, 0x39, 0x56, + 0x71, 0x65, 0x95, 0x3D, 0x08, 0x86, 0xF6, 0xEE, 0xAA, 0xA2, + 0x61, 0x9D, 0xF3, 0x77, 0x31, 0xF2, 0xE1, 0x61, 0xA8, 0xD7, + 0x47, 0x7E, 0xDC, 0x2C, 0x70, 0x44, 0xCD, 0xB3, 0x0A, 0xED, + 0xAE, 0x5B, 0x5B, 0xBF, 0xD6, 0x4C, 0x70, 0x47, 0x98, 0x66, + 0x40, 0x8F, 0xF4, 0x0E, 0x4B, 0xF4, 0x7E, 0xFA, 0x45, 0x87, + 0x9E, 0xFB, 0x70, 0xFC, 0xE4, 0x7A, 0x3B, 0xFF, 0xDA, 0x5F, + 0x57, 0x8B, 0x32, 0x75, 0x4F, 0xEC, 0x6B, 0xAD, 0x95, 0x58, + 0x44, 0x18, 0x31, 0xB7, 0x45, 0x77, 0x76, 0xD4, 0xE6, 0x96, + 0x66, 0xB1, 0x63, 0x42, 0xDF, 0x86, 0xC1, 0xF4, 0x0B, 0x53, + 0xF8, 0x51, 0x8A, 0x71, 0xB0, 0x99, 0xEA, 0x7A, 0x16, 0x27, + 0x0E, 0x22, 0x5A, 0xAC, 0xE9, 0xC5, 0x46, 0x0B, 0x06, 0x16, + 0xE9, 0xBA, 0x85, 0xAE, 0x2F, 0xAE, 0x56, 0xF9, 0xCC, 0xDE, + 0x69, 0x8C, 0xE1, 0x0C, 0xE6, 0x38, 0x93, 0xF2, 0x21, 0x88, + 0xA9, 0x0D, 0x25, 0x9B, 0x5A, 0xCC, 0x45, 0xF6, 0x6A, 0xE8, + 0xD5, 0xD8, 0xF6, 0x69, 0x2E, 0xFA, 0xB0, 0xCC, 0x09, 0x76, + 0x3B, 0x9A, 0xFC, 0xAB, 0x4E, 0x9A, 0xA9, 0x2A, 0x28, 0xA0, + 0xD5, 0x3A, 0x3C, 0xF7, 0xE2, 0xCF, 0xDE, 0x0C, 0x18, 0x40, + 0x34, 0x65, 0xCD, 0x93, 0x47, 0xDB, 0x30, 0x8F, 0x6B, 0x16, + 0x42, 0x39, 0x7E, 0xEC, 0x47, 0xBD, 0x9A, 0x3E, 0x19, 0x09, + 0x96, 0x6A, 0x88, 0xB9, 0x99, 0x92, 0x5D, 0xDE, 0xAF, 0xF5, + 0xFB, 0x69, 0xB8, 0x43, 0xF0, 0x27, 0x2E, 0xD5, 0x5A, 0xD9, + 0xC9, 0x30, 0xC4, 0xF8, 0x43, 0x01, 0xEA, 0x41, 0x04, 0xEB, + 0xFD, 0x5F, 0x2C, 0x0E, 0x7C, 0x28, 0x11, 0x41, 0x4E, 0xC9, + 0xD3, 0x67, 0x3C, 0x48, 0x89, 0x02, 0x2B, 0x4C, 0xE6, 0x54, + 0xB7, 0x00, 0x0C +}; +#define sizeof_ech_public_cert_der_2048 (sizeof(ech_public_cert_der_2048)) + #endif /* USE_CERT_BUFFERS_2048 */ #ifdef USE_CERT_BUFFERS_3072 diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 895ac670983..257aba726cf 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3180,7 +3180,8 @@ typedef struct WOLFSSL_ECH { byte configId; byte enc[HPKE_Npk_MAX]; byte innerCount; - byte writeEncoded; + WC_BITFIELD writeEncoded:1; + WC_BITFIELD copiedConfig:1; } WOLFSSL_ECH; WOLFSSL_LOCAL int EchConfigGetSupportedCipherSuite(WOLFSSL_EchConfig* config); @@ -3213,6 +3214,8 @@ WOLFSSL_LOCAL int GetEchConfigsEx(WOLFSSL_EchConfig* configs, WOLFSSL_LOCAL void FreeEchConfigs(WOLFSSL_EchConfig* configs, void* heap); +WOLFSSL_LOCAL int CopyEchConfigsPub(WOLFSSL* ssl); + WOLFSSL_LOCAL int SetRetryConfigs(WOLFSSL* ssl, const byte* echConfigs, word32 echConfigsLen); #endif @@ -3267,6 +3270,7 @@ WOLFSSL_LOCAL int TLSX_Push(TLSX** list, TLSX_Type type, const void* data, void* heap); WOLFSSL_LOCAL int TLSX_Append(TLSX** list, TLSX_Type type, const void* data, void* heap); +WOLFSSL_LOCAL void TLSX_SetResponse(WOLFSSL* ssl, TLSX_Type type); #elif defined(HAVE_SNI) \ || defined(HAVE_MAX_FRAGMENT) \ diff --git a/wolfssl/test.h b/wolfssl/test.h index 97454e1702b..32842256e68 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -537,6 +537,10 @@ err_sys_with_errno(const char* msg) #define cliEd448KeyFile "certs/ed448/client-ed448-priv.pem" #define caEd448CertFile "certs/ed448/ca-ed448.pem" #define noIssuerCertFile "certs/empty-issuer-cert.pem" +#define echPublicCertFile "certs/ech-public-cert.pem" +#define echPublicKeyFile "certs/ech-public-key.pem" +#define tenantACertFile "certs/tenant-a-cert.pem" +#define tenantBCertFile "certs/tenant-b-cert.pem" #else #define caCertFile "certs/ca-cert.der" #define eccCertFile "certs/server-ecc.der" @@ -571,6 +575,10 @@ err_sys_with_errno(const char* msg) #define cliEd448KeyFile "certs/ed448/client-ed448-priv.der" #define caEd448CertFile "certs/ed448/ca-ed448.der" #define noIssuerCertFile "certs/empty-issuer-cert.der" +#define echPublicCertFile "certs/ech-public-cert.der" +#define echPublicKeyFile "certs/ech-public-key.der" +#define tenantACertFile "certs/tenant-a-cert.der" +#define tenantBCertFile "certs/tenant-b-cert.der" #endif #define caCertFolder "certs/" #ifdef HAVE_WNR @@ -637,6 +645,10 @@ err_sys_with_errno(const char* msg) #define cliEd448KeyFile "./certs/ed448/client-ed448-priv.pem" #define caEd448CertFile "./certs/ed448/ca-ed448.pem" #define noIssuerCertFile "./certs/empty-issuer-cert.pem" +#define echPublicCertFile "./certs/ech-public-cert.pem" +#define echPublicKeyFile "./certs/ech-public-key.pem" +#define tenantACertFile "./certs/tenant-a-cert.pem" +#define tenantBCertFile "./certs/tenant-b-cert.pem" #else #define caCertFile "./certs/ca-cert.der" #define eccCertFile "./certs/server-ecc.der" @@ -671,6 +683,10 @@ err_sys_with_errno(const char* msg) #define cliEd448KeyFile "./certs/ed448/client-ed448-priv.der" #define caEd448CertFile "./certs/ed448/ca-ed448.der" #define noIssuerCertFile "./certs/empty-issuer-cert.der" +#define echPublicCertFile "./certs/ech-public-cert.der" +#define echPublicKeyFile "./certs/ech-public-key.der" +#define tenantACertFile "./certs/tenant-a-cert.der" +#define tenantBCertFile "./certs/tenant-b-cert.der" #endif #define caCertFolder "./certs/" #ifdef HAVE_WNR @@ -4910,6 +4926,33 @@ static WC_INLINE void EarlyDataStatus(WOLFSSL* ssl) } #endif /* WOLFSSL_EARLY_DATA */ +#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) +static WC_INLINE void PrintEchStatus(WOLFSSL* ssl) +{ + const char* status; + + switch (wolfSSL_GetEchStatus(ssl)) { + case WOLFSSL_ECH_STATUS_NOT_OFFERED: + status = "not offered"; + break; + case WOLFSSL_ECH_STATUS_GREASE: + status = "GREASE"; + break; + case WOLFSSL_ECH_STATUS_REJECTED: + status = "rejected"; + break; + case WOLFSSL_ECH_STATUS_ACCEPTED: + status = "accepted"; + break; + default: + status = "unknown"; + break; + } + + printf("ECH status: %s\n", status); +} +#endif /* WOLFSSL_TLS13 && HAVE_ECH */ + #if defined(HAVE_SESSION_TICKET) || defined (WOLFSSL_DTLS13) static WC_INLINE int process_handshake_messages(WOLFSSL* ssl, int blocking, int* zero_return)