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; }