proposed OpenSSL 4.0 API. Notes from PR #551:
This build only supports ECH "shared-mode" where mod_ssl does the ECH
decryption and also hosts both the ECH `public-name` and `backend` web
sites.
## Build
> [!NOTE]
> ECH is not yet a part of an OpenSSL release, our current goal is that ECH be
> part of an OpenSSL 4.0 release in spring 2026.
There is client and server ECH code in the OpenSSL ECH feature branch at
[https://github.com/openssl/openssl/tree/feature/ech](https://github.com/openssl/openssl/tree/feature/ech).
At present, ECH-enabling apache2 therefore requires building from source, using
the OpenSSL ECH feature branch.
## Code changes
- All code changes are within `modules/ssl` and are protected via `#ifdef
HAVE_OPENSSL_ECH`. That's defined in `ssl_private.h` if the included
`ssl.h` defines `SSL_OP_ECH_GREASE`.
- There're a bunch of changes to add the new `SSLECHKeyDir` directive that
are mosly obvious.
- We load the keys from `SSLECHKeyDir` using the `load_echkeys()` function in
`ssl_engine_init.c`. That also ECH-enables the `SSL_CTX` when keys are
loaded, which triggers ECH decryption as needed.
> [!NOTE]
> `load_echkeys()` will include the public component all loaded keys in the ECH
> `retry-configs` in the fallback scenario. If desired, we could add a naming
> convention or additional configuration setting to distinguish which to
> include in `retry-configs` or not. For now, we assume that'd better be done
> in a subsequent PR, if experience shows the feature is really useful/needed.
> (We can envisage some odd deployments where that might be the case, but not
> clear those'd really happen - it'd seem to need loads of key pairs or else
> some that are never published in the DNS that we don't want to expose to
> random clients - neither seems compelling.)
- We add a callback to `SSL_CTX_ech_set_callback` also in `ssl_engine_init.c`.
- We add calls to set the `SSL_ECH_STATUS` etc. variables to the environment
(for PHP etc) in `ssl_engine_kernel.c` and also do the logging of ECH outcomes
(to the error log).
Submitted by: sftcd <stephen.farrell cs.tcd.ie>, rpluem
Github: closes #551
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@
1928357 13f79535-47bb-0310-9956-
ffa450edef68
APU_CONFIG="--without-crypto"
pkgs: subversion
# -------------------------------------------------------------------------
+ - name: OpenSSL ECH branch
+ config: --enable-mods-shared=most --enable-maintainer-mode --disable-md --disable-http2 --disable-ldap --disable-crypto
+ notest-cflags: -Werror -O2
+ env: |
+ TEST_OPENSSL3=ech
+ TEST_OPENSSL3_BRANCH=feature/ech
+ OPENSSL_CONFIG=no-engine
+ APR_VERSION=1.7.6
+ APU_VERSION=1.6.3
+ APU_CONFIG="--without-crypto"
+ pkgs: subversion
+ # -------------------------------------------------------------------------
runs-on: ${{ matrix.os == '' && 'ubuntu-latest' || matrix.os }}
timeout-minutes: 30
env:
--- /dev/null
+ *) mod_ssl: Add support for Encrypted Client Hello (ECH)
+ Github #551. [Stephen Farrell <stephen.farrell cs.tcd.ie>]
<tr><td><code>SSL_CLIENTHELLO_SIG_ALGOS</code></td> <td>string</td> <td>Value of Signature Algorithms extension (13) from ClientHello as four hex encoded characters per item</td></tr>
<tr><td><code>SSL_CLIENTHELLO_ALPN</code></td> <td>string</td> <td>Value of ALPN extension (16) from ClientHello as hex encoded string including leading string lengths</td></tr>
<tr><td><code>SSL_CLIENTHELLO_VERSIONS</code></td> <td>string</td> <td>Value of Supported Versions extension (43) from ClientHello as four hex encoded characters per item</td></tr>
+<tr><td><code>SSL_ECH_STATUS</code></td> <td>string</td> <td><code>success</code> means that others also mean what they say</td></tr>
+<tr><td><code>SSL_ECH_INNER_SNI</code></td> <td>string</td> <td>SNI value that was encrypted in ECH (or `NONE`)</td></tr>
+<tr><td><code>SSL_ECH_OUTER_SNI</code></td> <td>string</td> <td>SNI value that was seen in plaintext SNI (or `NONE`)</td></tr>
</table>
<p><em>x509</em> specifies a component of an X.509 DN; one of
</usage>
</directivesynopsis>
+<directivesynopsis>
+<name>SSLECHKeyDir</name>
+<description>Load the set of Encrypted Client Hello (ECH) PEM files in the named directory</description>
+<syntax>SSLECHKeyDir <em>dirname</em></syntax>
+<contextlist><context>server config</context></contextlist>
+<compatibility>Available in Apache HTTP Server 2.5.1 and later</compatibility>
+
+<usage>
+
+<p>
+ECH is specified in
+ <a href="https://datatracker.ietf.org/doc/draft-ietf-tls-esni/">draft-ietf-tls-esni</a>
+httpd supports ECH "shared-mode" where the httpd instance does the
+ECH decryption and also hosts both the ECH `public-name` and `backend` web
+sites.
+</p>
+
+<p>
+The <code>SSLECHKeyDir</code> directive
+names the directory where ECH PEM files (named <code>*.ech</code>) are stored.
+Once an ECH PEM file is successfully loaded, httpd will perform ECH decryption
+and, if that succeeds, will process the relevant TLS session using the
+SNI from the inner ClientHello.
+</p>
+
+<example><title>Example ECH Config</title>
+<highlight language="config">
+...
+SSLEngine On
+SSLProtocol TLSv1.3
+SSLECHKeyDir /etc/apache2/echkeydir
+...
+# virtual hosts
+<VirtualHost *:443>
+ SSLEngine On
+ SSLProtocol TLSv1.3
+ ServerName example.com
+ DocumentRoot "/var/www/dir-example.com"
+</VirtualHost>
+<VirtualHost *:443>
+ SSLEngine On
+ SSLProtocol TLSv1.3
+ ServerName foo.example.com
+ DocumentRoot "/var/www/dir-foo.example.com"
+</VirtualHost>
+...
+</highlight>
+</example>
+
+<note><title>ECH Key Generation and Publication</title>
+<p>
+In the above, we describe a configuration that uses <code>example.com</code> as the
+ECH <code>public-name</code> and where <code>foo.example.com</code> is a web-site for which we want
+ECH to be used, with both hosted on the same httpd instance.
+</p>
+<p>
+Using ECH requries that httpd load an ECH key pair with a private value for ECH
+decryption. Browsers will require that the public component of that key pair be
+published in the DNS. With OpenSSL we generate and store that key pair in an ECH PEM
+formatted file as shown below.
+</p>
+<p>
+To generate ECH PEM files, use the ECH-enabled openssl command line
+to generate an ECH key pair and store the result in an ECH PEM file.
+You must also supply the <code>public-name</code> required by the ECH protocol.
+</p>
+<p>
+Key generation operations should be carried out under whatever local account is
+used for httpd configuration.
+</p>
+<example><title>Example: ECH Key Generation</title>
+<highlight language="config">
+~# OSSL=/home/user/code/openssl/apps/openssl
+~# mkdir -p /etc/apache2/echkeydir
+~# chmod 700 /etc/apache2/echkeydir
+~# cd /etc/apache2/echkeydir
+~# $OSSL ech -public-name example.com -o example.com.pem.ech
+~# cat example.com.pem.ech
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VuBCIEIJi22Im2rJ/lJqzNFZdGfsVfmknXAc8xz3fYPhD0Na5I
+-----END PRIVATE KEY-----
+-----BEGIN ECHCONFIG-----
+AD7+DQA6QwAgACA8mxkEsSTp2xXC/RUFCC6CZMMgdM4x1iTWKu3EONjbMAAEAAEA
+AQALZXhhbXBsZS5vcmcAAA==
+-----END ECHCONFIG-----
+</highlight>
+</example>
+<p>
+The ECHConfig value then needs to be published in an HTTPS resource record in
+the DNS, so as to be accessible as shown below:
+</p>
+<example><title>Accessing an ECH config from DNS</title>
+<highlight language="config">
+$ dig +short HTTPS foo.example.com
+1 . ech=AD7+DQA6QwAgACA8mxkEsSTp2xXC/RUFCC6CZMMgdM4x1iTWKu3EONjbMAAEAAEAAQALZXhhbXBsZS5vcmcAAA==
+</highlight>
+</example>
+<p>
+Various other fields may be included in an HTTPS resource record. For many
+httpd deployments, existing methods for publishing DNS records may be used to
+achieve the above. In some cases, one might use
+<a href="https://datatracker.ietf.org/doc/html/draft-ietf-tls-wkech">
+A well-known URI for publishing service parameters</a>
+designed to assist web servers in handling e.g. frequent ECH key rotation.
+</p>
+</note>
+
+<note><title>Reloading ECH Keys</title>
+
+<p>
+Giving httpd a command line argument of <code>-k graceful</code> causes a graceful reload
+of the configuration, without dropping existing connections.
+</p>
+
+</note>
+
+</usage>
+</directivesynopsis>
+
</modulesynopsis>
SSL_CMD_SRV(SessionTicketKeyFile, TAKE1,
"TLS session ticket encryption/decryption key file (RFC 5077) "
"('/path/to/file' - file with 48 bytes of random data)")
+#endif
+#ifdef HAVE_OPENSSL_ECH
+ SSL_CMD_SRV(ECHKeyDir, TAKE1,
+ "TLS ECH Key Directory"
+ "('/path/to/dir' - directory with ECH key pairs)")
#endif
SSL_CMD_ALL(CACertificatePath, TAKE1,
"SSL CA Certificate path "
#endif
sc->clienthello_vars = UNSET;
sc->session_tickets = UNSET;
+#ifdef HAVE_OPENSSL_ECH
+ sc->echkeydir = NULL;
+#endif
modssl_ctx_init_server(sc, p);
cfgMergeBool(compression);
#endif
cfgMergeBool(session_tickets);
+#ifdef HAVE_OPENSSL_ECH
+ cfgMergeString(echkeydir);
+#endif
modssl_ctx_cfg_merge_server(p, base->server, add->server, mrg->server);
return "Argument must be On or Off";
}
+#ifdef HAVE_OPENSSL_ECH
+const char *ssl_cmd_SSLECHKeyDir(cmd_parms *cmd, void *dcfg, const char *arg)
+{
+ SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
+
+ sc->echkeydir=arg;
+
+#if !defined(SSL_HAVE_PROTOCOL_TLSV1_3)
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10533)
+ "ECHKeyDir configured but TLSv1.3 not supported - exiting.");
+ return "ECHKeyDir configured but TLSv1.3 not supported";
+#endif
+ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, cmd->server,
+ "%s: ECHKeyDir set to %s",
+ cmd->cmd->name, sc->echkeydir);
+ return NULL;
+}
+#endif
+
const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag)
{
#ifdef HAVE_FIPS
DMP_LONG( "SSLSessionCacheTimeout", sc->session_cache_timeout);
DMP_ON_OFF("SSLStrictSNIVHostCheck", sc->strict_sni_vhost_check);
DMP_ON_OFF("SSLSessionTickets", sc->session_tickets);
+#ifdef HAVE_OPENSSL_ECH
+ DMP_STRING("SSLECHKeyDir", sc->echkeydir);
+#endif
}
static void ssl_policy_dump(SSLSrvConfigRec *policy, apr_pool_t *p,
modver, AP_SERVER_BASEVERSION, incver);
}
+#ifdef HAVE_OPENSSL_ECH
+/*
+ * Load any ECH PEM files we find in the ECHKeyDir directory
+ * Those are files matching "*.ech"
+ * The caller checks that echdir is non-NULL.
+ */
+static int load_echkeys(SSL_CTX *ctx, const char *echdir, server_rec *s,
+ apr_pool_t *ptemp)
+{
+ size_t elen = 0;
+ int keystried = 0, keysworked = 0, keysloaded=0;
+ OSSL_ECHSTORE *es = NULL;
+ apr_dir_t *dir = NULL;
+ apr_finfo_t direntry;
+ apr_int32_t finfo_flags = APR_FINFO_TYPE|APR_FINFO_NAME;
+ apr_status_t dorv;
+
+ elen = strlen(echdir);
+ if ((elen + 7) >= PATH_MAX) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10521)
+ "load_echkeys: directory name too long: %s - exiting", echdir);
+ return -1;
+ }
+ dorv = apr_dir_open(&dir, echdir, ptemp);
+ if (dorv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10522)
+ "load_echkeys: can't open directory %s - exiting (error %d)",
+ echdir, dorv);
+ return -1;
+ }
+ es = OSSL_ECHSTORE_new(NULL, NULL);
+ if (es == NULL) {
+ apr_dir_close(dir);
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10523)
+ "load_echkeys: can't alloc store");
+ return -1;
+ }
+
+ while ((apr_dir_read(&direntry, finfo_flags, dir)) == APR_SUCCESS) {
+ const char *fname;
+ size_t pnlen = 0;
+ apr_finfo_t theinfo;
+
+ if (direntry.filetype == APR_DIR) {
+ continue; /* don't try to load directories */
+ }
+ fname = apr_pstrcat(ptemp, echdir, "/", direntry.name, NULL);
+ if (!fname) {
+ continue;
+ }
+ pnlen = strlen(fname);
+ if (pnlen < 5 || pnlen > (PATH_MAX-1)) {
+ continue;
+ }
+ if (!(fname[pnlen - 4] == '.'
+ && fname[pnlen - 3] == 'e'
+ && fname[pnlen - 2] == 'c'
+ && fname[pnlen - 1] == 'h')) {
+ continue;
+ }
+ if (apr_stat(&theinfo, fname, APR_FINFO_MIN, ptemp) == APR_SUCCESS) {
+ BIO *in = BIO_new_file(fname, "r");
+ const int is_retry_config = OSSL_ECH_FOR_RETRY;
+
+ keystried++;
+ if (in && OSSL_ECHSTORE_read_pem(es, in, is_retry_config) == 1) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s,
+ "load_echkeys: worked for %s",fname);
+ keysworked++;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10525)
+ "load_echkeys: failed for %s (could be non-fatal)",
+ fname);
+ }
+ BIO_free_all(in);
+ }
+
+ }
+ apr_dir_close(dir);
+
+ if (!OSSL_ECHSTORE_num_keys(es, &keysloaded)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10526)
+ "OSSL_ECHSTORE_num_keys failed - exiting");
+ OSSL_ECHSTORE_free(es);
+ return -1;
+ }
+ if (SSL_CTX_set1_echstore(ctx, es) != 1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10527)
+ "load_echkeys: SSL_CTX_set1_echstore failed");
+ OSSL_ECHSTORE_free(es);
+ return -1;
+ }
+ OSSL_ECHSTORE_free(es);
+ if (keysworked == 0) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10528)
+ "load_echkeys: didn't load new keys (%d tried/failed) "
+ "but we have already some (%d) - continuing",
+ keystried, keysloaded);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(10529)
+ "ECH: %d keys loaded", keysloaded);
+ }
+ return 0;
+}
+#endif
+
/* _________________________________________________________________
**
** Let other answer special connection attempts.
modssl_ctx_t *mctx)
{
apr_status_t rv;
+#ifdef HAVE_OPENSSL_ECH
+ SSLSrvConfigRec *sc = mySrvConfig(s);
+#endif
/*
* Configure TLS extensions support
SSL_CTX_set_client_hello_cb(mctx->ssl_ctx, ssl_callback_ClientHello, NULL);
#endif
+#ifdef HAVE_OPENSSL_ECH
+ if (sc != NULL && sc->echkeydir != NULL) {
+ /* callback logs ECH outcome */
+ SSL_CTX_ech_set_callback(mctx->ssl_ctx, ssl_callback_ECH);
+ }
+#endif
+
#ifdef HAVE_OCSP_STAPLING
/*
* OCSP Stapling support, status_request extension
SSL_CTX_set_options(ctx, SSL_OP_IGNORE_UNEXPECTED_EOF);
}
#endif
-
+
+#ifdef HAVE_OPENSSL_ECH
+ /* ECH only really makes sense for TLSv1.3 */
+ prot = SSL_CTX_get_max_proto_version(ctx);
+ if (sc->echkeydir) {
+ if (prot == TLS1_3_VERSION) {
+ /* try load the keys */
+ if (load_echkeys(ctx, sc->echkeydir, s, ptemp) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10531)
+ "ECHKeyDir failed to load keys - exiting.");
+ SSL_CTX_free(ctx);
+ mctx->ssl_ctx = NULL;
+ return ssl_die(s);
+ }
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10532)
+ "ECHKeyDir configured but TLSv1.3 turned off - exiting.");
+ SSL_CTX_free(ctx);
+ mctx->ssl_ctx = NULL;
+ return ssl_die(s);
+ }
+ }
+#endif
+
return APR_SUCCESS;
}
#include "util_md5.h"
#include "scoreboard.h"
+#ifdef HAVE_OPENSSL_ECH
+#include <openssl/ech.h>
+#endif
+
static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn);
#ifdef HAVE_TLSEXT
static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s);
apr_table_set(env, "SSL_TLS_SNI", servername);
}
#endif
+#ifdef HAVE_OPENSSL_ECH
+ extract_to_env(r, env, "SSL_ECH_INNER_SNI");
+ extract_to_env(r, env, "SSL_ECH_OUTER_SNI");
+ extract_to_env(r, env, "SSL_ECH_STATUS");
+#endif
/* standard SSL environment variables */
if (dc->nOptions & SSL_OPT_STDENVVARS) {
return APR_NOTFOUND;
}
+#ifdef HAVE_OPENSSL_ECH
+unsigned int ssl_callback_ECH(SSL *ssl, const char *str)
+{
+ char *inner_sni = NULL, *outer_sni = NULL;
+ int echrv;
+ conn_rec *c = NULL;
+ const char *ech_servername;
+
+ c = (conn_rec *)SSL_get_app_data(ssl);
+ ech_servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (ech_servername == NULL) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+ echrv = SSL_ech_get1_status(ssl, &inner_sni, &outer_sni);
+ switch (echrv) {
+ case SSL_ECH_STATUS_NOT_TRIED:
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "ECH not attempted");
+ break;
+ case SSL_ECH_STATUS_FAILED:
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "ECH tried but failed");
+ break;
+ case SSL_ECH_STATUS_BAD_NAME:
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "ECH worked but bad name");
+ break;
+ case SSL_ECH_STATUS_SUCCESS:
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "ECH success outer_sni: %s inner_sni: %s",
+ (outer_sni ? outer_sni : "NONE"),
+ (inner_sni ? inner_sni : "NONE"));
+ break;
+ default:
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "Error getting ECH status");
+ }
+ OPENSSL_free(inner_sni);
+ OPENSSL_free(outer_sni);
+ return 1;
+}
+#endif
+
/*
* This callback function is executed when OpenSSL encounters an extended
* client hello with a server name indication extension ("SNI", cf. RFC 6066).
return result ? result : "";
}
+#ifdef HAVE_OPENSSL_ECH
+/* Extract ECH status variable from SSL object 'ssl' */
+static const char *ssl_var_lookup_ech_status(apr_pool_t *p, const char *var,
+ SSL *ssl)
+{
+ char *inner_sni = NULL, *outer_sni = NULL;
+ int echrv;
+ const char *result = NULL;
+
+ if (ssl == NULL)
+ return result;
+ echrv = SSL_ech_get1_status(ssl, &inner_sni, &outer_sni);
+ if (strcEQ(var, "STATUS")) {
+ switch (echrv) {
+ case SSL_ECH_STATUS_NOT_TRIED:
+ result = "not attempted";
+ break;
+ case SSL_ECH_STATUS_FAILED:
+ result = "tried but failed";
+ break;
+ case SSL_ECH_STATUS_BAD_NAME:
+ result = "ECH worked but bad name";
+ break;
+ case SSL_ECH_STATUS_SUCCESS:
+ result = "success";
+ break;
+ default:
+ result = "error getting ECH status";
+ }
+ }
+ else if (echrv == SSL_ECH_STATUS_SUCCESS) {
+ if (strcEQ(var, "INNER_SNI")) {
+ result = apr_pstrdup(p, inner_sni);
+ }
+ if (strcEQ(var, "OUTER_SNI")) {
+ result = apr_pstrdup(p, outer_sni);
+ }
+ }
+ OPENSSL_free(inner_sni);
+ OPENSSL_free(outer_sni);
+ return result;
+}
+#endif
+
static const char *ssl_var_lookup_ssl(apr_pool_t *p, const SSLConnRec *sslconn,
request_rec *r, const char *var)
{
}
}
#endif
+#ifdef HAVE_OPENSSL_ECH
+ else if (ssl != NULL && strcEQn(var, "ECH_", 4)) {
+ result = ssl_var_lookup_ech_status(p, var+4, ssl);
+ }
+#endif
return result;
}
#define MODSSL_HAVE_OPENSSL_STORE 0
#endif
+/*
+ * Check if we have an ECH-enabled OpenSSL. If we do then this symbol will
+ * be defined in ssl.h and we compile in ECH code.
+ */
+#if defined(SSL_OP_ECH_GREASE)
+#define HAVE_OPENSSL_ECH
+#else
+#undef HAVE_OPENSSL_ECH
+#endif
+
#if (OPENSSL_VERSION_NUMBER < 0x0090801f)
#error mod_ssl requires OpenSSL 0.9.8a or later
#endif
#endif
BOOL session_tickets;
BOOL clienthello_vars;
+#ifdef HAVE_OPENSSL_ECH
+ const char *echkeydir;
+#endif
};
/**
const char *ssl_cmd_SSLCryptoDevice(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLRandomSeed(cmd_parms *, void *, const char *, const char *, const char *);
const char *ssl_cmd_SSLEngine(cmd_parms *, void *, const char *);
+#ifdef HAVE_OPENSSL_ECH
+const char *ssl_cmd_SSLECHKeyDir(cmd_parms *cmd, void *dcfg, const char *arg);
+#endif
const char *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *, const char *);
const char *ssl_cmd_SSLCertificateFile(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, void *, const char *);
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
int ssl_callback_ClientHello(SSL *, int *, void *);
+#ifdef HAVE_OPENSSL_ECH
+unsigned int ssl_callback_ECH(SSL *, const char *);
+#endif
#endif
#ifdef HAVE_TLS_SESSION_TICKETS
int ssl_callback_SessionTicket(SSL *ssl,
mkdir -p build/openssl
pushd build/openssl
- curl -L "https://github.com/openssl/openssl/releases/download/openssl-${TEST_OPENSSL3}/openssl-${TEST_OPENSSL3}.tar.gz" |
- tar -xzf -
+ if test -v TEST_OPENSSL3_BRANCH; then
+ git clone -b $TEST_OPENSSL3_BRANCH -q https://github.com/openssl/openssl openssl-${TEST_OPENSSL3}
+ else
+ curl -L "https://github.com/openssl/openssl/releases/download/openssl-${TEST_OPENSSL3}/openssl-${TEST_OPENSSL3}.tar.gz" |
+ tar -xzf -
+ fi
cd openssl-${TEST_OPENSSL3}
# Build with RPATH so ./bin/openssl doesn't require $LD_LIBRARY_PATH
./Configure --prefix=$HOME/root/openssl3 \