diff --git a/certs/ech-client-cert.pem b/certs/ech-client-cert.pem
index 3059a518..66d80864 100644
--- a/certs/ech-client-cert.pem
+++ b/certs/ech-client-cert.pem
@@ -1,75 +1,59 @@
------BEGIN CERTIFICATE-----
-MIIFTDCCBPKgAwIBAgIQA5JQzuqRJy3ljWStInKQTjAKBggqhkjOPQQDAjBKMQsw
-CQYDVQQGEwJVUzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEgMB4GA1UEAxMX
-Q2xvdWRmbGFyZSBJbmMgRUNDIENBLTMwHhcNMjMwMzA0MDAwMDAwWhcNMjQwMzAz
-MjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG
-A1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEe
-MBwGA1UEAxMVc25pLmNsb3VkZmxhcmVzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZI
-zj0DAQcDQgAE1RXa9mRvotUaWVPtrpuTGJGAawyYYNRRkK2czd3xEadvstkDYygE
-vE+xcpZFPPZQDkBlAAfvv8j2PNJ6f1nRN6OCA40wggOJMB8GA1UdIwQYMBaAFKXO
-N+rrsHUOlGeItEX62SQQh5YfMB0GA1UdDgQWBBRekDZj95YFBGStBKQhAaqPvUhb
-AzBQBgNVHREESTBHghVzbmkuY2xvdWRmbGFyZXNzbC5jb22CFWNyeXB0by5jbG91
-ZGZsYXJlLmNvbYIXKi5jcnlwdG8uY2xvdWRmbGFyZS5jb20wDgYDVR0PAQH/BAQD
-AgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB7BgNVHR8EdDByMDeg
-NaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vQ2xvdWRmbGFyZUluY0VDQ0NB
-LTMuY3JsMDegNaAzhjFodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vQ2xvdWRmbGFy
-ZUluY0VDQ0NBLTMuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQICMCkwJwYIKwYBBQUH
-AgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzB2BggrBgEFBQcBAQRqMGgw
-JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcw
-AoY0aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Nsb3VkZmxhcmVJbmNFQ0ND
-QS0zLmNydDAMBgNVHRMBAf8EAjAAMIIBgQYKKwYBBAHWeQIEAgSCAXEEggFtAWsA
-dwDuzdBk1dsazsVct520zROiModGfLzs3sNRSFlGcR+1mwAAAYaqF9guAAAEAwBI
-MEYCIQCiv+PHjCl3yCBIN1geIV6nM8JVsdMDz+bi3fC0c5iSAAIhAN1bPyq66wKn
-kXI9P85jRI++sCieQ8zS4KBN/yL19DsfAHcAc9meiRtMlnigIH1HneayxhzQUV5x
-GSqMa4AQesF3crUAAAGGqhfYmwAABAMASDBGAiEA/rhcMmQfwxP2VpbyYpFPu5Sx
-n/0Jc+/PMDSRqpst6QYCIQCuaV7aGhmR/PbE0SyQ5Y81IUPew23t5cWgQZIDLddU
-GgB3AEiw42vapkc0D+VqAvqdMOscUgHLVt0sgdm7v6s52IRzAAABhqoX2GoAAAQD
-AEgwRgIhAI58QAtsPkKun97n+4/gpHNqUQrC9GIyxzTTeu1quBvSAiEA1t0uZKdn
-6KO27mCPHjtR8DUkhE27U2vhUICyuJGgVokwCgYIKoZIzj0EAwIDSAAwRQIhAPdp
-FGP8NBnFfOe0w0vRmNwRxujz2eXnMk2LrPKqUavGAiANK5eY+3XClhMvDTTJkhzh
-PEAwQeEKtlCRDESaSxItJw==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIDzTCCArWgAwIBAgIQCjeHZF5ftIwiTv0b7RQMPDANBgkqhkiG9w0BAQsFADBa
-MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl
-clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTIw
-MDEyNzEyNDgwOFoXDTI0MTIzMTIzNTk1OVowSjELMAkGA1UEBhMCVVMxGTAXBgNV
-BAoTEENsb3VkZmxhcmUsIEluYy4xIDAeBgNVBAMTF0Nsb3VkZmxhcmUgSW5jIEVD
-QyBDQS0zMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEua1NZpkUC0bsH4HRKlAe
-nQMVLzQSfS2WuIg4m4Vfj7+7Te9hRsTJc9QkT+DuHM5ss1FxL2ruTAUJd9NyYqSb
-16OCAWgwggFkMB0GA1UdDgQWBBSlzjfq67B1DpRniLRF+tkkEIeWHzAfBgNVHSME
-GDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0l
-BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYI
-KwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j
-b20wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL09t
-bmlyb290MjAyNS5jcmwwbQYDVR0gBGYwZDA3BglghkgBhv1sAQEwKjAoBggrBgEF
-BQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1sAQIw
-CAYGZ4EMAQIBMAgGBmeBDAECAjAIBgZngQwBAgMwDQYJKoZIhvcNAQELBQADggEB
-AAUkHd0bsCrrmNaF4zlNXmtXnYJX/OvoMaJXkGUFvhZEOFp3ArnPEELG4ZKk40Un
-+ABHLGioVplTVI+tnkDB0A+21w0LOEhsUCxJkAZbZB2LzEgwLt4I4ptJIsCSDBFe
-lpKU1fwg3FZs5ZKTv3ocwDfjhUkV+ivhdDkYD7fa86JXWGBPzI6UAPxGezQxPk1H
-goE6y/SJXQ7vTQ1unBuCJN0yJV0ReFEQPaA1IwQvZW+cwdFD19Ae8zFnWSfda9J1
-CZMRJCQUzym+5iPDuI9yP+kHyCREU3qzuWFloUwOxkgAyXVjBYdwRVKD05WdRerw
-6DEdfgkfCv4+3ao8XnTSrLE=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
-RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
-VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
-DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
-ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
-VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
-mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
-IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
-mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
-XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
-dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
-jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
-BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
-DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
-9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
-jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
-Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
-ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
-R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
------END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICnzCCAiWgAwIBAgIQf/MZd5csIkp2FV0TttaF4zAKBggqhkjOPQQDAzBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMjMxMjEzMDkwMDAwWhcNMjkwMjIwMTQw
+MDAwWjA7MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzMQwwCgYDVQQDEwNXRTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARvzTr+
+Z1dHTCEDhUDCR127WEcPQMFcF4XGGTfn1XzthkubgdnXGhOlCgP4mMTG6J7/EFmP
+LCaY9eYmJbsPAvpWo4H+MIH7MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggr
+BgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU
+kHeSNWfE/6jMqeZ72YB5e8yT+TgwHwYDVR0jBBgwFoAUgEzW63T/STaj1dj8tT7F
+avCUHYwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzAChhhodHRwOi8vaS5wa2ku
+Z29vZy9yNC5jcnQwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2MucGtpLmdvb2cv
+ci9yNC5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgEwCgYIKoZIzj0EAwMDaAAwZQIx
+AOcCq1HW90OVznX+0RGU1cxAQXomvtgM8zItPZCuFQ8jSBJSjz5keROv9aYsAm5V
+sQIwJonMaAFi54mrfhfoFNZEfuNMSQ6/bIBiNLiyoX46FohQvKeIoJ99cx7sUkFN
+7uJW
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDejCCAmKgAwIBAgIQf+UwvzMTQ77dghYQST2KGzANBgkqhkiG9w0BAQsFADBX
+MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE
+CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIzMTEx
+NTAzNDMyMVoXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT
+GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFI0
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE83Rzp2iLYK5DuDXFgTB7S0md+8Fhzube
+Rr1r1WEYNa5A3XP3iZEwWus87oV8okB2O6nGuEfYKueSkWpz6bFyOZ8pn6KY019e
+WIZlD6GEZQbR3IvJx3PIjGov5cSr0R2Ko4H/MIH8MA4GA1UdDwEB/wQEAwIBhjAd
+BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd
+BgNVHQ4EFgQUgEzW63T/STaj1dj8tT7FavCUHYwwHwYDVR0jBBgwFoAUYHtmGkUN
+l8qJUC99BM00qP/8/UswNgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAChhpodHRw
+Oi8vaS5wa2kuZ29vZy9nc3IxLmNydDAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8v
+Yy5wa2kuZ29vZy9yL2dzcjEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMA0GCSqG
+SIb3DQEBCwUAA4IBAQAYQrsPBtYDh5bjP2OBDwmkoWhIDDkic574y04tfzHpn+cJ
+odI2D4SseesQ6bDrarZ7C30ddLibZatoKiws3UL9xnELz4ct92vID24FfVbiI1hY
++SW6FoVHkNeWIP0GCbaM4C6uVdF5dTUsMVs/ZbzNnIdCp5Gxmx5ejvEau8otR/Cs
+kGN+hr/W5GvT1tMBjgWKZ1i4//emhA1JG1BbPzoLJQvyEotc03lXjTaCzv8mEbep
+8RqZ7a2CPsgRbuvTPBwcOMBBmuFeU88+FSBX6+7iP0il8b4Z0QFqIwwMHfs/L6K1
+vepuoxtGzi4CZ68zJpiq1UvSqTbFJjtbD4seiMHl
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
+MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
+aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
+jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
+xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
+1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
+snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
+U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
+9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
+BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
+AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
+yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
+38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
+AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
+DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
+HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
+-----END CERTIFICATE-----
diff --git a/tls/README.md b/tls/README.md
index 20778e28..5b0513d6 100644
--- a/tls/README.md
+++ b/tls/README.md
@@ -1339,33 +1339,66 @@ To generate your own cert text, see the [DER to C script](https://github.com/wol
## Encrypted Client Hello
-Encrypted Client Hello (ECH) encrypts sensitive fields in the client hello step of the TLS handshake. The client-ech example connects to a cloudflare server that is setup to test different TLS options including ECH. To build wolfSSL for this ech example run `./configure --enable-ech && make && sudo make install`.
+Encrypted Client Hello (ECH) encrypts sensitive fields in the TLS ClientHello
+message. Doing so provides a means for hiding the Server Name Indication (SNI),
+among other things, from passive observers.
-This test is successful if the cloudflare http response shows that `sni=encrypted`.
+To run these examples build wolfSSL with ECH support:
```sh
-make
-./client-ech
+./configure --enable-ech && make && sudo make install
+```
+
+There are four ECH example programs in this directory:
+
+| Program | Description |
+|---|---|
+| `client-ech` | Connects to Cloudflare to demonstrate real-world ECH |
+| `server-ech-local` | Local ECH server; generates its own ECH config at startup |
+| `client-ech-local` | Local ECH client; accepts a base64 ECH config as an argument |
+| `client-ech-grease` | GREASE ECH probe; retrieves retry configs from a local server |
+
+### client-ech — Real-World ECH with Cloudflare
+
+`client-ech` demonstrates ECH against `crypto.cloudflare.com` in two phases:
+
+1. **GREASE phase**: Connects to Cloudflare's ECH endpoint
+ (`cloudflare-ech.com`) without ECH configs set, which causes the library to
+ send GREASE ECH. The server responds with its actual ECH configs as retry
+ configs, which are collected via `wolfSSL_GetEchConfigs()`.
+2. **ECH phase**: Reconnects using the retrieved configs. The retrieved configs
+ are set with `wolfSSL_SetEchConfigs()` which will set the public SNI to
+ (`cloudflare-ech.com`) in addition to enabling encryption of the client
+ hello. The private SNI is set via `wolfSSL_UseSNI()` to
+ (`crypto.cloudflare.com`).
+
+The test succeeds when Cloudflare's `/cdn-cgi/trace` response shows
+`sni=encrypted`.
+
+```sh
+make client-ech
+./client-ech
HTTP/1.1 200 OK
-Access-Control-Allow-Origin: *
-Cache-Control: no-cache
-Cf-Ray: 77c3e3e937c6b08e-ATL
+Date: Tue, 24 Feb 2026 17:42:20 GMT
Content-Type: text/plain
-Expires: Thu, 01 Jan 1970 00:00:01 GMT
+Transfer-Encoding: chunked
+Connection: keep-alive
+Access-Control-Allow-Origin: *
Server: cloudflare
-X-Content-Type-Options: nosniff
+CF-RAY: 9d30c24adad5e17a-SEA
X-Frame-Options: DENY
-Date: Mon, 19 Dec 2022 23:24:11 GMT
-Transfer-Encoding: chunked
+X-Content-Type-Options: nosniff
+Expires: Thu, 01 Jan 1970 00:00:01 GMT
+Cache-Control: no-cache
-106
-fl=507f46
+10f
+fl=542f337
h=crypto.cloudflare.com
ip=173.93.184.37
-ts=1671492251.082
+ts=1771954940.618
visit_scheme=https
uag=Mozilla/5.0 (X11; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0
-colo=ATL
+colo=SEA
sliver=none
http=http/1.1
loc=US
@@ -1373,11 +1406,88 @@ tls=TLSv1.3
sni=encrypted
warp=off
gateway=off
-kex=P-256
+rbi=off
+kex=X25519
0
```
+### Local ECH pair — server-ech-local and client-ech-local
+
+`server-ech-local` and `client-ech-local` demonstrate ECH between two local
+processes without requiring internet access or DNS.
+
+`server-ech-local` generates its own ECH config at startup using
+`wolfSSL_CTX_GenerateEchConfig()`, then encodes and prints it in base64. It
+listens on port 11111 and sets its private SNI to `ech-private-name.com`. The
+server loops accepting clients until it receives the message `shutdown`.
+
+`client-ech-local` takes the server's base64 ECH config as a command-line
+argument, loads it with `wolfSSL_SetEchConfigsBase64()`, and sets its private
+SNI to `ech-private-name.com` before connecting on port 11111. It then reads a
+message from stdin and sends it to the server.
+
+Build:
+
+```sh
+make server-ech-local client-ech-local
+```
+
+Run the server in one terminal; it will print its ECH config:
+
+```sh
+./server-ech-local
+ECH config:
+Waiting for a connection...
+```
+
+Run the client in a second terminal, passing the base64 config the server printed:
+
+```sh
+./client-ech-local
+Message for server: hello
+Server: I hear ya fa shizzle!
+Shutdown complete
+```
+
+Send `shutdown` as the message to stop the server.
+
+### client-ech-grease — GREASE Probe to Retrieve Server ECH Configs
+
+GREASE (Generate Random Extensions And Sustain Extensibility) provides several
+benefits to a user:
+1. Determines if a server supports ECH based on the response it gives.
+2. Retrieves ECH configs from the server if the client does not know any. It is
+ an alternative to fetching them from DNS HTTPS records.
+3. Reduces the extent to which GREASE vs ECH connections stick out.
+
+`client-ech-grease` connects to a local server (such as `server-ech-local`) on
+port 11111 without valid ECH configs, which causes the library to send GREASE
+ECH. The server responds with its actual ECH configs as retry configs.
+`client-ech-grease` retrieves these via `wolfSSL_GetEchConfigs()` and prints
+them in base64. It takes the public SNI as a command-line argument.
+
+Build:
+
+```sh
+make client-ech-grease
+```
+
+With `server-ech-local` already running in another terminal, probe for configs:
+
+```sh
+./client-ech-grease ech-public-name.com
+ECH config:
+
+Shutdown complete
+```
+
+The printed base64 config can then be passed directly to `client-ech-local`:
+
+```sh
+./client-ech-local
+```
+
## TLS Example with Post-Handshake Authentication
See `client-tls-posthsauth.c` and `server-tls-posthsauth.c`. These server and client applications show how to do a handshake without the server authenticating the client. Then after the handshake is complete, the server requests authentication and the client authenticates itself to the server. This is mutual authentication with a faster handshake because the client authentication is done later. This can lead to a better user experience if there are conditions where the client need not be authenticated.
diff --git a/tls/client-ech-grease.c b/tls/client-ech-grease.c
new file mode 100644
index 00000000..bd79dae7
--- /dev/null
+++ b/tls/client-ech-grease.c
@@ -0,0 +1,185 @@
+/* client-ech-grease.c
+ *
+ * Copyright (C) 2023 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL. (formerly known as CyaSSL)
+ *
+ * wolfSSL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/* the usual suspects */
+#include
+#include
+#include
+
+/* socket includes */
+#include
+#include
+#include
+#include
+
+/* wolfSSL */
+#include
+#include
+#include
+
+#define DEFAULT_PORT 11111
+
+#define CERT_FILE "../certs/ca-cert.pem"
+
+#ifdef HAVE_ECH
+int main(int argc, char **argv)
+{
+ int sockfd;
+ struct sockaddr_in servAddr;
+ byte echConfig[512];
+ word32 echConfigLen = 512;
+ char echConfigBase64[512];
+ word32 echConfigBase64Len = 512;
+ int ret;
+
+ /* declare wolfSSL objects */
+ WOLFSSL_CTX* ctx;
+ WOLFSSL* ssl;
+
+ /* Check for proper calling convention */
+ if (argc != 2) {
+ printf("usage: %s \n", argv[0]);
+ return 0;
+ }
+
+ /* Create a socket that uses an internet IPv4 address,
+ * Sets the socket to be stream based (TCP),
+ * 0 means choose the default protocol. */
+ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ fprintf(stderr, "ERROR: failed to create the socket\n");
+ ret = -1;
+ goto end;
+ }
+
+ /* Initialize the server address struct with zeros */
+ memset(&servAddr, 0, sizeof(servAddr));
+
+ /* Fill in the server address */
+ servAddr.sin_family = AF_INET; /* using IPv4 */
+ servAddr.sin_port = htons(DEFAULT_PORT); /* on DEFAULT_PORT */
+
+ /* set the ip to localhost */
+ if (inet_pton(AF_INET, "127.0.0.1", &servAddr.sin_addr) != 1) {
+ fprintf(stderr, "ERROR: invalid address\n");
+ ret = -1;
+ goto end;
+ }
+
+ /* Connect to the server */
+ if ((ret = connect(sockfd, (struct sockaddr*) &servAddr, sizeof(servAddr)))
+ == -1) {
+ fprintf(stderr, "ERROR: failed to connect\n");
+ goto end;
+ }
+
+ /* Initialize wolfSSL */
+ if ((ret = wolfSSL_Init()) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: Failed to initialize the library\n");
+ goto socket_cleanup;
+ }
+
+ /* Create and initialize WOLFSSL_CTX */
+ if ((ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())) == NULL) {
+ fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n");
+ ret = -1;
+ goto socket_cleanup;
+ }
+
+ /* Load client certificates into WOLFSSL_CTX */
+ if ((ret = wolfSSL_CTX_load_verify_locations(ctx, CERT_FILE, NULL))
+ != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: failed to load %s, please check the file.\n",
+ CERT_FILE);
+ goto ctx_cleanup;
+ }
+
+ /* Create a WOLFSSL object */
+ if ((ssl = wolfSSL_new(ctx)) == NULL) {
+ fprintf(stderr, "ERROR: failed to create WOLFSSL object\n");
+ ret = -1;
+ goto ctx_cleanup;
+ }
+
+ /* Set SNI to probe against */
+ if (wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, argv[1], strlen(argv[1])) !=
+ WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: Failed to set public SNI\n");
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* Attach wolfSSL to the socket */
+ if ((ret = wolfSSL_set_fd(ssl, sockfd)) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: Failed to set the file descriptor\n");
+ goto cleanup;
+ }
+
+ /* Connect to server */
+ if ((ret = wolfSSL_connect(ssl)) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: failed to connect to server\n");
+ goto cleanup;
+ }
+
+ /* If the GREASE was successful then retry configs sent by the server
+ * should be available */
+ if(wolfSSL_GetEchConfigs(ssl, echConfig, &echConfigLen) !=
+ WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: failed to get GREASE configs\n");
+ ret = -1;
+ goto cleanup;
+ }
+
+ /* Print the retrieved configs in Base64 */
+ if (Base64_Encode_NoNl(echConfig, echConfigLen, (byte*)echConfigBase64,
+ &echConfigBase64Len) != 0) {
+ fprintf(stderr, "ERROR: failed to encode ECH configs in Base64\n");
+ ret = -1;
+ goto cleanup;
+ }
+
+ printf("ECH config: %s\n\n", echConfigBase64);
+
+ /* Bidirectional shutdown */
+ while (wolfSSL_shutdown(ssl) == SSL_SHUTDOWN_NOT_DONE) {
+ fprintf(stderr, "Shutdown not complete\n");
+ }
+ fprintf(stderr, "Shutdown complete\n");
+
+ ret = 0;
+
+ /* Cleanup and return */
+cleanup:
+ wolfSSL_free(ssl); /* Free the wolfSSL object */
+ctx_cleanup:
+ wolfSSL_CTX_free(ctx); /* Free the wolfSSL context object */
+ wolfSSL_Cleanup(); /* Cleanup the wolfSSL environment */
+socket_cleanup:
+ close(sockfd); /* Close the connection to the server */
+end:
+ return ret; /* Return reporting a success */
+}
+#else
+int main(void)
+{
+ printf("Please build wolfssl with ./configure --enable-ech\n");
+ return 1;
+}
+#endif
diff --git a/tls/client-ech-local.c b/tls/client-ech-local.c
index b7018dcb..82611bdc 100644
--- a/tls/client-ech-local.c
+++ b/tls/client-ech-local.c
@@ -75,7 +75,7 @@ int main(int argc, char** argv)
servAddr.sin_family = AF_INET; /* using IPv4 */
servAddr.sin_port = htons(DEFAULT_PORT); /* on DEFAULT_PORT */
- /* Get the server IPv4 address from the command line call */
+ /* Connect over localhost */
if (inet_pton(AF_INET, "127.0.0.1", &servAddr.sin_addr) != 1) {
fprintf(stderr, "ERROR: invalid address\n");
ret = -1;
@@ -89,9 +89,10 @@ int main(int argc, char** argv)
goto end;
}
- /*---------------------------------*/
+ /*---------------------------------------------------*/
/* Start of wolfSSL initialization and configuration */
- /*---------------------------------*/
+ /*---------------------------------------------------*/
+
/* Initialize wolfSSL */
if ((ret = wolfSSL_Init()) != WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: Failed to initialize the library\n");
@@ -107,7 +108,7 @@ int main(int argc, char** argv)
/* Load client certificates into WOLFSSL_CTX */
if ((ret = wolfSSL_CTX_load_verify_locations(ctx, CERT_FILE, NULL))
- != SSL_SUCCESS) {
+ != WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: failed to load %s, please check the file.\n",
CERT_FILE);
goto ctx_cleanup;
@@ -120,16 +121,18 @@ int main(int argc, char** argv)
goto ctx_cleanup;
}
+ /* Set ECH configs to those provided on the command line */
if (wolfSSL_SetEchConfigsBase64(ssl, argv[1], strlen(argv[1])) !=
WOLFSSL_SUCCESS) {
- fprintf(stderr, "ERROR: Failed to wolfSSL_SetEchConfigsBase64\n");
+ fprintf(stderr, "ERROR: Failed to set ECH configs\n");
ret = -1;
goto cleanup;
}
+ /* Use privateName for private SNI */
if (wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, privateName,
privateNameLen) != WOLFSSL_SUCCESS) {
- fprintf(stderr, "ERROR: Failed to wolfSSL_UseSNI\n");
+ fprintf(stderr, "ERROR: Failed to set private SNI\n");
ret = -1;
goto cleanup;
}
@@ -141,7 +144,7 @@ int main(int argc, char** argv)
}
/* Connect to wolfSSL on the server side */
- if ((ret = wolfSSL_connect(ssl)) != SSL_SUCCESS) {
+ if ((ret = wolfSSL_connect(ssl)) != WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: failed to connect to wolfSSL\n");
goto cleanup;
}
diff --git a/tls/client-ech.c b/tls/client-ech.c
index 045bac4e..22e7c5bc 100644
--- a/tls/client-ech.c
+++ b/tls/client-ech.c
@@ -18,11 +18,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
+/* the usual suspects */
+#include
+#include
+#include
+#include
+
+/* socket includes */
+#include
+#include
+#include
+#include
+
#include
#include
#include
#include
-#include
#define SERV_PORT 443
#define CERT_FILE "../certs/ech-client-cert.pem"
@@ -37,7 +49,6 @@ int main(void)
int sockfd = -1;
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
- WOLFSSL_METHOD* method;
struct sockaddr_in servAddr;
const char message[] =
"GET /cdn-cgi/trace/ HTTP/1.1\r\n"
@@ -55,75 +66,110 @@ int main(void)
"Pragma: no-cache\r\n"
"Cache-Control: no-cache\r\n"
"\r\n";
- const char ip_string[] = "162.159.137.85";
- const char SNI[] = "crypto.cloudflare.com";
- uint8_t ech_configs[ECHBUFF_LEN];
- uint32_t ech_configs_len = ECHBUFF_LEN;
+ const char publicIP[] = "104.18.11.118";
+ const char publicSNI[] = "cloudflare-ech.com";
+ const char privateSNI[] = "crypto.cloudflare.com";
+ byte echConfigs[ECHBUFF_LEN];
+ word32 echConfigsLen = ECHBUFF_LEN;
+
+ /* Initialize wolfSSL */
+ if ((ret = wolfSSL_Init()) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: Failed to initialize the library\n");
+ goto cleanup;
+ }
- /* this first tls connection is only used to get the retry configs */
- /* these configs can also be retrieved from DNS */
+ /* Create and initialize WOLFSSL_CTX */
+ if ((ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())) == NULL) {
+ fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n");
+ ret = -1;
+ goto cleanup;
+ }
- /* create and set up socket */
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- memset(&servAddr, 0, sizeof(servAddr));
- servAddr.sin_family = AF_INET;
- servAddr.sin_port = htons(SERV_PORT);
- /* set the ip string to the cloudflare server */
- servAddr.sin_addr.s_addr = inet_addr( ip_string );
+ /*---------------------------------------------------*/
+ /* Start of wolfSSL GREASE connection */
+ /*---------------------------------------------------*/
- /* connect to socket */
- connect(sockfd, (struct sockaddr *) &servAddr, sizeof(servAddr));
+ /* this first tls connection is only used to get the retry configs - these
+ * configs can also be retrieved from DNS
+ * i.e. `$ dig cloudflare-ech.com HTTPS` */
- /* initialize wolfssl library */
- wolfSSL_Init();
- method = wolfTLSv1_3_client_method(); /* use TLS v1.3 */
- /* make new ssl context */
- if ((ctx = wolfSSL_CTX_new(method)) == NULL) {
- ret = 1;
+ /* Create a socket that uses an internet IPv4 address,
+ * Sets the socket to be stream based (TCP),
+ * 0 means choose the default protocol. */
+ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ fprintf(stderr, "ERROR: failed to create the socket\n");
+ ret = -1;
goto cleanup;
}
- /* set the server name we want to connect to */
- ret = wolfSSL_CTX_UseSNI( ctx, WOLFSSL_SNI_HOST_NAME,
- SNI, strlen(SNI) );
+ /* Initialize the server address struct with zeros */
+ memset(&servAddr, 0, sizeof(servAddr));
+
+ /* Fill in the server address */
+ servAddr.sin_family = AF_INET; /* using IPv4 */
+ servAddr.sin_port = htons(SERV_PORT); /* on DEFAULT_PORT */
+
+ /* set the ip string to the cloudflare ECH server */
+ servAddr.sin_addr.s_addr = inet_addr( publicIP );
- if (ret != WOLFSSL_SUCCESS) {
- printf("wolfSSL_CTX_UseSNI error %d", ret);
- goto ctx_clean;
+ /* Connect to the server */
+ if ((ret = connect(sockfd, (struct sockaddr*) &servAddr, sizeof(servAddr)))
+ == -1) {
+ fprintf(stderr, "ERROR: failed to connect\n");
+ goto cleanup;
}
- /* make new wolfSSL struct */
- if ((ssl = wolfSSL_new(ctx)) == NULL) {
- printf("wolfSSL_new error");
- ret = 1;
- goto ctx_clean;
+ /* Load client certificates into WOLFSSL_CTX */
+ if ((ret = wolfSSL_CTX_load_verify_locations(ctx, CERT_FILE, NULL))
+ != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: failed to load %s, please check the file.\n",
+ CERT_FILE);
+ goto ctx_cleanup;
}
- /* Add cert to ctx */
- if ((ret = wolfSSL_CTX_load_verify_locations(ctx, CERT_FILE, 0)) !=
- WOLFSSL_SUCCESS) {
- printf("wolfSSL_CTX_load_verify_locations error %d", ret);
- goto ssl_clean;
+ /* Create a WOLFSSL object */
+ if ((ssl = wolfSSL_new(ctx)) == NULL) {
+ fprintf(stderr, "ERROR: failed to create WOLFSSL object\n");
+ ret = -1;
+ goto ctx_cleanup;
}
- /* Connect wolfssl to the socket, server, then send message */
- wolfSSL_set_fd(ssl, sockfd);
+ /* Set SNI to the ECH server
+ * Take care not to set this to the private SNI because that would leak
+ * information about it. The private SNI will only be encrypted once the ECH
+ * configs are set. */
+ if ((ret = wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, publicSNI,
+ strlen(publicSNI))) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: Failed to set public SNI %d\n", ret);
+ ret = -1;
+ goto ssl_cleanup;
+ }
- /* this connect will send a grease ech and get the retry configs back */
- ret = wolfSSL_connect(ssl);
+ /* Attach wolfSSL to the socket */
+ if ((ret = wolfSSL_set_fd(ssl, sockfd)) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: Failed to set the file descriptor %d\n", ret);
+ goto ssl_cleanup;
+ }
- if (ret != WOLFSSL_SUCCESS) {
- printf("%d %d\n", ret, wolfSSL_get_error(ssl, ret));
- goto ssl_clean;
+ /* Connect to Cloudflare ECH server */
+ if ((ret = wolfSSL_connect(ssl)) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: failed to connect to Cloudflare ECH server\n");
+ fprintf(stderr, "%d %d\n", ret, wolfSSL_get_error(ssl, ret));
+ goto ssl_cleanup;
}
- /* retrieve the retry configs sent by the server */
- ret = wolfSSL_GetEchConfigs(ssl, ech_configs, &ech_configs_len);
+ /* If the GREASE was successful then retry configs sent by the server
+ * should be available. Store these in echConfigs for encrypting the
+ * upcoming ECH connection. */
+ if ((ret = wolfSSL_GetEchConfigs(ssl, echConfigs, &echConfigsLen)) !=
+ WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: unable to get GREASE configs %d\n", ret);
+ goto ssl_cleanup;
+ }
- if (ret != WOLFSSL_SUCCESS) {
- printf("wolfSSL_GetEchConfigs error %d\n", ret);
- goto ssl_clean;
+ while (wolfSSL_shutdown(ssl) == WOLFSSL_SHUTDOWN_NOT_DONE) {
+ ; /* do nothing */
}
/* frees all data before client termination */
@@ -133,47 +179,75 @@ int main(void)
ssl = NULL;
sockfd = -1;
- /* now we create a new connection that will send the real ech */
+ /*---------------------------------------------------*/
+ /* Start of wolfSSL ECH connection */
+ /*---------------------------------------------------*/
- /* create and set up socket */
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ fprintf(stderr, "ERROR: failed to create the socket\n");
+ ret = -1;
+ goto ctx_cleanup;
+ }
memset(&servAddr, 0, sizeof(servAddr));
- servAddr.sin_family = AF_INET;
- servAddr.sin_port = htons(SERV_PORT);
- /* set the ip string to the cloudflare server */
- servAddr.sin_addr.s_addr = inet_addr( ip_string );
-
- /* connect to socket */
- connect(sockfd, (struct sockaddr *) &servAddr, sizeof(servAddr));
-
- /* make new wolfSSL struct */
- if ( (ssl = wolfSSL_new(ctx)) == NULL) {
- printf("wolfSSL_new error");
- ret = 1;
- goto ctx_clean;
+ servAddr.sin_family = AF_INET; /* using IPv4 */
+ servAddr.sin_port = htons(SERV_PORT); /* on DEFAULT_PORT */
+ servAddr.sin_addr.s_addr = inet_addr( publicIP );
+
+ if ((ret = connect(sockfd, (struct sockaddr*) &servAddr, sizeof(servAddr)))
+ == -1) {
+ fprintf(stderr, "ERROR: failed to connect\n");
+ goto ctx_cleanup;
}
- /* set the ech configs taken from dns */
- ret = wolfSSL_SetEchConfigs(ssl, ech_configs, ech_configs_len);
+ /* Create a WOLFSSL object */
+ if ((ssl = wolfSSL_new(ctx)) == NULL) {
+ fprintf(stderr, "ERROR: failed to create WOLFSSL object\n");
+ ret = -1;
+ goto ctx_cleanup;
+ }
- if ( ret != WOLFSSL_SUCCESS ) {
- printf("wolfSSL_SetEchConfigs error %d", ret);
- goto ssl_clean;
+ /* set the ECH configs with configs taken from the GREASE connection */
+ if ((ret = wolfSSL_SetEchConfigs(ssl, echConfigs, echConfigsLen)) !=
+ WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: failed to set GREASE configs %d\n", ret);
+ goto ssl_cleanup;
}
- /* Connect wolfssl to the socket, server, then send message */
- wolfSSL_set_fd(ssl, sockfd);
+ /* Now that ECH configs are set the private SNI will be encrypted, therefore
+ * it is now fine (and correct) to set the private SNI here */
+ if ((ret = wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, privateSNI,
+ strlen(privateSNI))) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: Failed to set private SNI %d\n", ret);
+ ret = -1;
+ goto ssl_cleanup;
+ }
- /* this connect will send the real ech */
- ret = wolfSSL_connect(ssl);
+ /* Attach wolfSSL to the socket */
+ if ((ret = wolfSSL_set_fd(ssl, sockfd)) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: Failed to set the file descriptor %d\n", ret);
+ goto ssl_cleanup;
+ }
- if (ret != WOLFSSL_SUCCESS) {
- printf( "%d %d\n", ret, wolfSSL_get_error( ssl, ret ) );
- goto ssl_clean;
+ /* Connect to Cloudflare */
+ if ((ret = wolfSSL_connect(ssl)) != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "ERROR: failed to connect to Cloudflare\n");
+ fprintf(stderr, "%d %d\n", ret, wolfSSL_get_error(ssl, ret));
+ goto ssl_cleanup;
}
+ /* Write the message that will ask the server for information on the
+ * connection */
wolfSSL_write(ssl, message, strlen(message));
+ if ((ret = wolfSSL_write(ssl, message, strlen(message))) !=
+ strlen(message)) {
+ fprintf(stderr, "ERROR: failed to write entire message\n");
+ fprintf(stderr, "%d bytes of %d bytes were sent",
+ ret, (int)strlen(message));
+ goto ssl_cleanup;
+ }
+ /* Retrieve the server's response:
+ * If ECH is being correctly used then 'sni=encrypted' should show up */
do
{
ret = wolfSSL_read(ssl, rd_buf, RDBUFF_LEN);
@@ -181,28 +255,32 @@ int main(void)
if (ret <= 0)
break;
- printf("%.*s", ret, rd_buf);
+ fprintf(stdout, "%.*s", ret, rd_buf);
/* read until the chunk size is 0 */
} while (rd_buf[0] != '0');
+ while (wolfSSL_shutdown(ssl) == WOLFSSL_SHUTDOWN_NOT_DONE) {
+ ; /* do nothing */
+ }
+
ret = 0;
-ssl_clean:
+ssl_cleanup:
wolfSSL_free(ssl);
ssl = NULL;
- close(sockfd);
- sockfd = -1;
-ctx_clean:
+ctx_cleanup:
wolfSSL_CTX_free(ctx);
cleanup:
wolfSSL_Cleanup();
+ close(sockfd);
+ sockfd = -1;
return ret;
}
#else
int main(void)
{
- printf("Please build wolfssl with ./configure --enable-ech\n");
+ fprintf(stderr, "Please build wolfssl with ./configure --enable-ech\n");
return 1;
}
#endif
diff --git a/tls/server-ech-local.c b/tls/server-ech-local.c
index 2a03be72..bc2b4e74 100644
--- a/tls/server-ech-local.c
+++ b/tls/server-ech-local.c
@@ -87,21 +87,21 @@ int main()
/* Generate ech config */
if (wolfSSL_CTX_GenerateEchConfig(ctx, publicName, 0, 0, 0)
!= WOLFSSL_SUCCESS) {
- fprintf(stderr, "ERROR: failed to wolfSSL_CTX_GenerateEchConfig\n");
+ fprintf(stderr, "ERROR: failed to generate ECH config\n");
ret = -1;
goto exit;
}
if(wolfSSL_CTX_GetEchConfigs(ctx, echConfig, &echConfigLen) !=
WOLFSSL_SUCCESS) {
- fprintf(stderr, "ERROR: failed to wolfSSL_CTX_GetEchConfigs\n");
+ fprintf(stderr, "ERROR: failed to get ECH configs\n");
ret = -1;
goto exit;
}
if (Base64_Encode_NoNl(echConfig, echConfigLen, (byte*)echConfigBase64,
&echConfigBase64Len) != 0) {
- fprintf(stderr, "ERROR: failed to Base64_Encode_NoNl\n");
+ fprintf(stderr, "ERROR: failed to encode ECH configs in Base64\n");
ret = -1;
goto exit;
}
@@ -111,7 +111,7 @@ int main()
if(wolfSSL_CTX_UseSNI(ctx, WOLFSSL_SNI_HOST_NAME, privateName,
privateNameLen) != WOLFSSL_SUCCESS) {
fprintf(stderr,
- "ERROR: failed to wolfSSL_CTX_UseSNIwolfSSL_CTX_UseSNI\n");
+ "ERROR: failed to set private SNI\n");
ret = -1;
goto exit;
}
@@ -143,7 +143,7 @@ int main()
/* Bind the server socket to our port */
if (bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
fprintf(stderr, "ERROR: failed to bind\n");
- ret = -1;
+ ret = -1;
goto exit;
}