incoming TLS connections get routed to GetEchClientHelloInner

This commit is contained in:
Yves Rutschle committed 2026-08-20 07:56:34 +00:00
1 parent 34645a62b2
commit 17a0425f4f
10 files changed
+308 -52

No files matched your search

+37
View File
@@ -1,3 +1,40 @@
ech branch:
This is an experimental version and does not work.
Do not use it unless you are experimenting with ECH
and an experimental wolfSSL.
A lot of context is discussed here:
https://github.com/wolfSSL/wolfssl/issues/10485
What it does:
- It creates a public ECH configuration for the
first public name (configure in the `listen`
section of the configuration file). This gets
printed in a file configured in `ech_conf_sink`
(see ech_test.cfg example).
- Upon incoming connections, it will call an
imaginary wolfSSL_GetEchClientHelloInner().
The resulting packet is hexdumped.
- It will also use wolfSSL to decode the SNI and
print it (but does not yet use it to forward).
Most things happen in tls_ech.c, in particular there
is a stub of GetEchClientHelloInner() that needs to
be removed when the function becomes available in
wolfSSL.
TODO:
Change configure script to check for appropriate
wolfSSL version.
Create ECH configurations for each public name
Link decoded InnerHello to SNI extraction code
....
Error cases still to be worked out (see
conversations on github)
Profit!
v2.3.0: v2.3.0:
Added `max_connections` setting to `listen` and Added `max_connections` setting to `listen` and
`protocol` configuration; see the `protocol` configuration; see the
+3
View File
@@ -11,6 +11,9 @@ AC_CHECK_LIB([wrap], [hosts_ctl], [AC_DEFINE(HAVE_LIBWRAP) LIBS="$LIBS -lwrap" ]
AC_CHECK_LIB([cap], [cap_get_proc], [AC_DEFINE(HAVE_LIBCAP) LIBS="$LIBS -lcap" ], []) AC_CHECK_LIB([cap], [cap_get_proc], [AC_DEFINE(HAVE_LIBCAP) LIBS="$LIBS -lcap" ], [])
AC_CHECK_LIB([bsd], [setproctitle], [AC_DEFINE(HAVE_LIBBSD) LIBS="$LIBS -lbsd" ], []) AC_CHECK_LIB([bsd], [setproctitle], [AC_DEFINE(HAVE_LIBBSD) LIBS="$LIBS -lbsd" ], [])
dnl TODO: check that wolfssl contains wolfSSL_GetEchClientHelloInner instead of just existing
AC_CHECK_LIB([wolfssl], [wolfSSL_Init], [AC_DEFINE(HAVE_WOLFSSL) LIBS="$LIBS -lwolfssl" ], [])
AC_CHECK_HEADERS(linux/landlock.h, AC_DEFINE(HAVE_LANDLOCK), []) AC_CHECK_HEADERS(linux/landlock.h, AC_DEFINE(HAVE_LANDLOCK), [])
AC_CHECK_HEADERS(proxy_protocol.h, [AC_DEFINE(HAVE_PROXYPROTOCOL) LIBS="$LIBS -lproxyprotocol" ], []) AC_CHECK_HEADERS(proxy_protocol.h, [AC_DEFINE(HAVE_PROXYPROTOCOL) LIBS="$LIBS -lproxyprotocol" ], [])
+45
View File
@@ -0,0 +1,45 @@
# sslh configuration for saaz:
# ssh to localhost
# https to nextcloud
foreground: true;
inetd: false;
numeric: false;
transparent: false;
user: "yves";
pidfile: "/tmp/sslh.pid";
syslog_facility: "auth";
# Where to write the ECH configurations
ech_conf_sink: "/tmp/ech";
# Logging configuration
# Value: 1: stdout; 2: syslog; 3: stdout+syslog; 4: logfile; ...; 7: all
verbose: 1;
# Change hostname with your external address name.
listen:
(
{ host: "inet"; port: "4443"; },
{ host: "chaton"; port: "4445"; }
);
protocols:
(
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; fork: true; },
{ name: "tls"; sni_hostnames: [
"inet.rutschle.net", "lists.rutschle.net"
];
host: "localhost"; port: "4453"; },
{ name: "tls"; sni_hostnames: [
"chaton.rutschle.net"
];
host: "localhost"; port: "4455"; }
);
on-timeout: "ssh";
+61 -15
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Wed Sep 10 15:49:49 2025. * on Sat Jul 18 17:34:37 2026.
# conf2struct: generate libconf parsers that read to structs # conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle # Copyright (C) 2018-2024 Yves Rutschle
@@ -482,6 +482,8 @@ struct arg_file* sslhcfg_conffile;
struct arg_str* sslhcfg_chroot; struct arg_str* sslhcfg_chroot;
struct arg_str* sslhcfg_syslog_facility; struct arg_str* sslhcfg_syslog_facility;
struct arg_str* sslhcfg_logfile; struct arg_str* sslhcfg_logfile;
struct arg_str* sslhcfg_ech_conf_sink;
struct arg_str* sslhcfg_ech_public_name;
struct arg_str* sslhcfg_on_timeout; struct arg_str* sslhcfg_on_timeout;
struct arg_str* sslhcfg_prefix; struct arg_str* sslhcfg_prefix;
struct arg_str* sslhcfg_listen; struct arg_str* sslhcfg_listen;
@@ -1314,6 +1316,38 @@ static struct config_desc table_sslhcfg[] = {
/* default_val*/ .default_val.def_string = NULL /* default_val*/ .default_val.def_string = NULL
}, },
{
/* name */ "ech_conf_sink",
/* type */ CFG_STRING,
/* sub_group*/ NULL,
/* arg_cl */ & sslhcfg_ech_conf_sink,
/* base_addr */ NULL,
/* offset */ offsetof(struct sslhcfg_item, ech_conf_sink),
/* offset_len */ 0,
/* offset_present */ offsetof(struct sslhcfg_item, ech_conf_sink_is_present),
/* size */ sizeof(char*),
/* array_type */ -1,
/* mandatory */ 0,
/* optional */ 1,
/* default_val*/ .default_val.def_string = NULL
},
{
/* name */ "ech_public_name",
/* type */ CFG_STRING,
/* sub_group*/ NULL,
/* arg_cl */ & sslhcfg_ech_public_name,
/* base_addr */ NULL,
/* offset */ offsetof(struct sslhcfg_item, ech_public_name),
/* offset_len */ 0,
/* offset_present */ offsetof(struct sslhcfg_item, ech_public_name_is_present),
/* size */ sizeof(char*),
/* array_type */ -1,
/* mandatory */ 0,
/* optional */ 1,
/* default_val*/ .default_val.def_string = NULL
},
{ {
/* name */ "on_timeout", /* name */ "on_timeout",
/* type */ CFG_STRING, /* type */ CFG_STRING,
@@ -1500,7 +1534,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: listen */ { /* arg: listen */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_listen, .arg_cl = & sslhcfg_listen,
.base_entry = & table_sslhcfg [26], .base_entry = & table_sslhcfg [28],
.targets = sslhcfg_listen_targets, .targets = sslhcfg_listen_targets,
@@ -1512,7 +1546,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: ssh */ { /* arg: ssh */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_ssh, .arg_cl = & sslhcfg_ssh,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_ssh_targets, .targets = sslhcfg_ssh_targets,
@@ -1524,7 +1558,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: tls */ { /* arg: tls */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_tls, .arg_cl = & sslhcfg_tls,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_tls_targets, .targets = sslhcfg_tls_targets,
@@ -1536,7 +1570,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: ssl */ { /* arg: ssl */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_ssl, .arg_cl = & sslhcfg_ssl,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_ssl_targets, .targets = sslhcfg_ssl_targets,
@@ -1548,7 +1582,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: openvpn */ { /* arg: openvpn */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_openvpn, .arg_cl = & sslhcfg_openvpn,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_openvpn_targets, .targets = sslhcfg_openvpn_targets,
@@ -1560,7 +1594,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: tinc */ { /* arg: tinc */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_tinc, .arg_cl = & sslhcfg_tinc,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_tinc_targets, .targets = sslhcfg_tinc_targets,
@@ -1572,7 +1606,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: wireguard */ { /* arg: wireguard */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_wireguard, .arg_cl = & sslhcfg_wireguard,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_wireguard_targets, .targets = sslhcfg_wireguard_targets,
@@ -1584,7 +1618,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: xmpp */ { /* arg: xmpp */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_xmpp, .arg_cl = & sslhcfg_xmpp,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_xmpp_targets, .targets = sslhcfg_xmpp_targets,
@@ -1596,7 +1630,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: http */ { /* arg: http */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_http, .arg_cl = & sslhcfg_http,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_http_targets, .targets = sslhcfg_http_targets,
@@ -1608,7 +1642,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: adb */ { /* arg: adb */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_adb, .arg_cl = & sslhcfg_adb,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_adb_targets, .targets = sslhcfg_adb_targets,
@@ -1620,7 +1654,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: socks5 */ { /* arg: socks5 */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_socks5, .arg_cl = & sslhcfg_socks5,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_socks5_targets, .targets = sslhcfg_socks5_targets,
@@ -1632,7 +1666,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: syslog */ { /* arg: syslog */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_syslog, .arg_cl = & sslhcfg_syslog,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_syslog_targets, .targets = sslhcfg_syslog_targets,
@@ -1644,7 +1678,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: msrdp */ { /* arg: msrdp */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_msrdp, .arg_cl = & sslhcfg_msrdp,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_msrdp_targets, .targets = sslhcfg_msrdp_targets,
@@ -1656,7 +1690,7 @@ static struct compound_cl_arg compound_cl_args[] = {
{ /* arg: anyprot */ { /* arg: anyprot */
.regex = "(.+):(\\w+)", .regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_anyprot, .arg_cl = & sslhcfg_anyprot,
.base_entry = & table_sslhcfg [27], .base_entry = & table_sslhcfg [29],
.targets = sslhcfg_anyprot_targets, .targets = sslhcfg_anyprot_targets,
@@ -2345,6 +2379,8 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg)
sslhcfg_chroot = arg_strn("C", "chroot", "<path>", 0, 1, "Root to change to after set-up"), sslhcfg_chroot = arg_strn("C", "chroot", "<path>", 0, 1, "Root to change to after set-up"),
sslhcfg_syslog_facility = arg_strn(NULL, "syslog-facility", "<str>", 0, 1, "Facility to syslog to"), sslhcfg_syslog_facility = arg_strn(NULL, "syslog-facility", "<str>", 0, 1, "Facility to syslog to"),
sslhcfg_logfile = arg_strn(NULL, "logfile", "<str>", 0, 1, "Log messages to a file"), sslhcfg_logfile = arg_strn(NULL, "logfile", "<str>", 0, 1, "Log messages to a file"),
sslhcfg_ech_conf_sink = arg_strn(NULL, "ech-conf-sink", "<str>", 0, 1, "File where to write ECH configurations"),
sslhcfg_ech_public_name = arg_strn(NULL, "ech-public-name", "<str>", 0, 1, "Public name for ECH client-facing proxy (usually, sslh's address)"),
sslhcfg_on_timeout = arg_strn(NULL, "on-timeout", "<str>", 0, 1, "Target to connect to when timing out"), sslhcfg_on_timeout = arg_strn(NULL, "on-timeout", "<str>", 0, 1, "Target to connect to when timing out"),
sslhcfg_prefix = arg_strn(NULL, "prefix", "<str>", 0, 1, "Reserved for testing"), sslhcfg_prefix = arg_strn(NULL, "prefix", "<str>", 0, 1, "Reserved for testing"),
sslhcfg_listen = arg_strn("p", "listen", "<host:port>", 0, 10, "Listen on host:port"), sslhcfg_listen = arg_strn("p", "listen", "<host:port>", 0, 10, "Listen on host:port"),
@@ -2621,6 +2657,16 @@ void sslhcfg_fprint(
fprintf(out, " <unset>"); fprintf(out, " <unset>");
fprintf(out, "\n"); fprintf(out, "\n");
indent(out, depth); indent(out, depth);
fprintf(out, "ech_conf_sink: %s", sslhcfg->ech_conf_sink);
if (! sslhcfg->ech_conf_sink_is_present)
fprintf(out, " <unset>");
fprintf(out, "\n");
indent(out, depth);
fprintf(out, "ech_public_name: %s", sslhcfg->ech_public_name);
if (! sslhcfg->ech_public_name_is_present)
fprintf(out, " <unset>");
fprintf(out, "\n");
indent(out, depth);
fprintf(out, "on_timeout: %s", sslhcfg->on_timeout); fprintf(out, "on_timeout: %s", sslhcfg->on_timeout);
fprintf(out, "\n"); fprintf(out, "\n");
indent(out, depth); indent(out, depth);
+6 -1
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Wed Sep 10 15:49:49 2025. * on Sat Jul 18 17:34:37 2026.
# conf2struct: generate libconf parsers that read to structs # conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle # Copyright (C) 2018-2024 Yves Rutschle
@@ -38,6 +38,7 @@
#include "probe.h" #include "probe.h"
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <wolfssl/ssl.h>
#include <netdb.h> #include <netdb.h>
struct sslhcfg_listen_item { struct sslhcfg_listen_item {
@@ -114,6 +115,10 @@ struct sslhcfg_item {
char* syslog_facility; char* syslog_facility;
int logfile_is_present; int logfile_is_present;
char* logfile; char* logfile;
int ech_conf_sink_is_present;
char* ech_conf_sink;
int ech_public_name_is_present;
char* ech_public_name;
char* on_timeout; char* on_timeout;
char* prefix; char* prefix;
size_t listen_len; size_t listen_len;
+1 -1
View File
@@ -347,7 +347,7 @@ int main(int argc, char *argv[], char* envp[])
/* Open log file for writing */ /* Open log file for writing */
setup_logfile(); setup_logfile();
ech_init(); ech_init(listen_sockets, num_addr_listen);
if (cfg.user || cfg.chroot) if (cfg.user || cfg.chroot)
drop_privileges(cfg.user, cfg.chroot); drop_privileges(cfg.user, cfg.chroot);
+2 -4
View File
@@ -86,6 +86,8 @@ config: {
description: "Facility to syslog to"; }, description: "Facility to syslog to"; },
{ name: "logfile"; type: "string"; optional: true; { name: "logfile"; type: "string"; optional: true;
description: "Log messages to a file" }, description: "Log messages to a file" },
{ name: "ech_conf_sink"; type: "string"; optional: true;
description: "File where to write ECH configurations" },
{ name: "on-timeout"; type: "string"; default: "ssh"; { name: "on-timeout"; type: "string"; default: "ssh";
description: "Target to connect to when timing out"; }, description: "Target to connect to when timing out"; },
@@ -131,10 +133,6 @@ config: {
type: "array", type: "array",
element_type: "string" element_type: "string"
}, },
{ name: "wolfssl_ctx", # One context object per public hostname; they will have the same index as prot->sni_hostnames
type: "runtime",
c_type: "WOLFSSL_CTX**"
},
{ name: "alpn_protocols", { name: "alpn_protocols",
type: "array", type: "array",
element_type: "string" element_type: "string"
+6
View File
@@ -61,6 +61,7 @@ static int parse_server_name_extension(const struct TLSProtocol *, const char *,
static int parse_alpn_extension(const struct TLSProtocol *, const char *, size_t); static int parse_alpn_extension(const struct TLSProtocol *, const char *, size_t);
static int has_match(const char**, size_t, const char*, size_t); static int has_match(const char**, size_t, const char*, size_t);
/* Parse a TLS packet for the Server Name Indication and ALPN extension in the client /* Parse a TLS packet for the Server Name Indication and ALPN extension in the client
* hello handshake, returning a status code * hello handshake, returning a status code
* *
@@ -81,6 +82,11 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da
if (data_len < TLS_HEADER_LEN) if (data_len < TLS_HEADER_LEN)
return TLS_ELENGTH; return TLS_ELENGTH;
/* If if it is an ECH, decrypt and match */
ech_match_sni(data, data_len);
tls_content_type = data[0]; tls_content_type = data[0];
if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) {
print_message(msg_probe_info, "Request did not begin with TLS handshake.\n"); print_message(msg_probe_info, "Request did not begin with TLS handshake.\n");
+1
View File
@@ -44,5 +44,6 @@ struct TLSProtocol *tls_data_set_list(struct TLSProtocol *, int, const char**, s
#define TLS_ENOEXT -4 /* No ALPN or SNI extension found */ #define TLS_ENOEXT -4 /* No ALPN or SNI extension found */
#define TLS_EPROTOCOL -5 /* Protocol error */ #define TLS_EPROTOCOL -5 /* Protocol error */
void ech_match_sni(const char* buffer_in, size_t buffer_len);
#endif #endif
+146 -31
View File
@@ -33,34 +33,134 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include "config.h"
#include "log.h" #include "log.h"
#include "sslh-conf.h" #include "sslh-conf.h"
#include "tls.h"
WOLFSSL_CTX* wolfssl_ctx;
void ech_get_client_hello_inner(const char* data, size_t datalen) // Hard-coded server certificate (PEM format)
// from snakeoil -- these serves no purpose beyond initialising the context
static const char* SERVER_CERT_PEM =
"-----BEGIN CERTIFICATE-----\n"
"MIICyjCCAbKgAwIBAgIUZrCzD4tEIu1r9EhkOn96raiupe4wDQYJKoZIhvcNAQEL\n"
"BQAwDzENMAsGA1UEAwwEc2FhejAeFw0yMTA1MjgyMDM0MjRaFw0zMTA1MjYyMDM0\n"
"MjRaMA8xDTALBgNVBAMMBHNhYXowggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n"
"AoIBAQC5s1UhjcmsrRnSdJMH82NxRQ3hlwMxGZz5NnI0MukXN5kptAYSge4BQc6J\n"
"MAL1MbPtX0pfR+yBILWUdn6JCnDqhv9ipO8OpeVeh+RKa2kGiEdeoQhBbKNZdsoe\n"
"gD9FE3lE895GIhEv7Jd9bt/r8CcCpmOLuhJF6md0cLWGPrAp4l+iCV/ixyE0z0bK\n"
"fJzuOxi7oIeINLEiR0Z4hJsM2XAEXlBEsI4oh2KdDJiU6ow3w25IY7+/nYVDDLD2\n"
"QFMByfkEmAGp9Br+2tquRUmR8qmSvZPMtWpOzMv8+iKTrxUcAUCtCpDMjHqNR8fd\n"
"6G6TLenPbQPEaac5UgGZ7rJvL9idAgMBAAGjHjAcMAkGA1UdEwQCMAAwDwYDVR0R\n"
"BAgwBoIEc2FhejANBgkqhkiG9w0BAQsFAAOCAQEAZPasi1Hu7q4S9wJsbIzcGj7m\n"
"ZCAxNdm3AINeV3mxfhToeqtXSoa2sujCEgwCuiYi0OE8wmseLO8e1xIsRc/NEpOl\n"
"h9u5OupV2Xbf7nuKzPl31YQ7kv+90mnixizf6CFpHXAdU3WEZ33+fMtS+lV/ZBYo\n"
"Nh9GGNJ7Grv5KIKu/b2cR403pRF1hGrjWABrIDZO3ZCtNgRp/My1Qr+/04rTs8VF\n"
"WyDshOe0u6Xo//M3sCMbx5+62BopCMmAu2TxiPew0OTM8eDa3IBbc2kLGxp8c4J+\n"
"EYpAKTZbkhs/6SleNMC4BOEmZQ1tk6D0N0u4Y0b1IKEjgx7HqwZ6mIdmbU1lZQ==\n"
"-----END CERTIFICATE-----\n";
static const char* SERVER_KEY_PEM =
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5s1UhjcmsrRnS\n"
"dJMH82NxRQ3hlwMxGZz5NnI0MukXN5kptAYSge4BQc6JMAL1MbPtX0pfR+yBILWU\n"
"dn6JCnDqhv9ipO8OpeVeh+RKa2kGiEdeoQhBbKNZdsoegD9FE3lE895GIhEv7Jd9\n"
"bt/r8CcCpmOLuhJF6md0cLWGPrAp4l+iCV/ixyE0z0bKfJzuOxi7oIeINLEiR0Z4\n"
"hJsM2XAEXlBEsI4oh2KdDJiU6ow3w25IY7+/nYVDDLD2QFMByfkEmAGp9Br+2tqu\n"
"RUmR8qmSvZPMtWpOzMv8+iKTrxUcAUCtCpDMjHqNR8fd6G6TLenPbQPEaac5UgGZ\n"
"7rJvL9idAgMBAAECggEAbXNQctViD596/7rryhze+KztsI/UNsVU6uH3T+LN1XxU\n"
"jurnRVNFV4JU3DDrHV9tIDQw3pXCxJzlyRpKHDWGNgTZU2fI6sZGuX+4w1Apd8ss\n"
"3Y5mEkGYyaslF/MY13rR2B0wXf/IR4nYA/ssiaBjLcGqHHnQOVseHNgtdXFPXW9n\n"
"5IXJ2PSeaFW0Z13vbTQm9Cx2kCBKL4J3TymhkuSW3DUHxmlDfWUK14lkyXCZIZ3i\n"
"TbXQAXhwTrz3wqbG++iEP4MZM0Ka7/+WXqrsxA/yCirb+HnMewxwBKzGvkym2I2H\n"
"3BkjDn9exDgTePD7i0w22UYCF5uezDB9YOiPVZ634QKBgQDyKIgxD7XMMJezeg0Y\n"
"PaG+Eig/hE27mm2pyTQSd6MMZ1HpbDUpLQ/GO3Npy+bKS0/5eefNT4PWVcmpRL4W\n"
"blNIfixkcZq1a6wax9Nhk89jXZdhBinO8gfXFOiANFDS7B4fEXeTrfhmuryqUw9m\n"
"L6GJ0jk/H6fs6ads15y+O7A6tQKBgQDEUKlZg4qXxNMNKq7l+v7R6IH8VrNLt0I/\n"
"f4fhpW3IhmPJCRuOFOS0UpDPYjTeu14i3EFcpe0D+DVoQiggBTAh3I/qMHvRko5g\n"
"DT83YHp2/ec0AY0ocwnLwye224t6GF7S8R8vhG1FkIBk6DTJgWFZLDtY+dekZugz\n"
"IHvgtxSPSQKBgHWD7fPBJ/xbaIMUq94jqqZOsXGBhyePncBTgA2mOV3/leStOm8t\n"
"CwasOyoQZYOuYLU0z6T2/Ye6Qg7+6TCBgbEgafKknuuDwRWN+6rSzEXwWVIgZ5Gi\n"
"KuPZparxuHdjVorFMz5borxXys7tV//DBaWYe2eCuT9jdHiBefNni/IxAoGAWHTn\n"
"+deVqVEcsAAdkq8IBtk3SNZgL6vBhA8Y0QnTb9luOPWLnve3HbFeYrOjkwDmJ9sK\n"
"8I4rP/ClT/cPUW5FA9z4U9PI1uOsl4cghvlH9Tnu5bYVPranIVMsH+7I6Bj3ESFo\n"
"peaLvh6gW9dtgaZ0kNOnYi4hhoh/9Bmc9+JGt1ECgYAbGSQLa21L4Hx3NvMparVQ\n"
"bGjN26igvqKmhBaKrH8YolOlhEw/mCwTlg9zI7jrhdO0VClPrB8IVBoL+6HvrT21\n"
"LJM31J3ddqME9/5fQgKMQsD60kRDD6iV/iK2S6TOUjxJn1f6MuLmGSOHZYcbdBPZ\n"
"DyBKgbz/xGV6katjXvg74A==\n"
"-----END PRIVATE KEY-----\n";
#if 1
/* Will be provided by wolfSSL */
int wolfSSL_GetEchClientHelloInner(
WOLFSSL* ssl, /* Connected to an existing context with the ECH configs. Stores hpke state */
const byte* buffer_in, /* The full ClientHelloOuter */
word32 buffer_len, /* How many bytes in the buffer */
byte* buffer_out,
word32* buffer_out_len
)
{
char* inner = "some clienthelloinner";
*buffer_out_len = strlen(inner);
memcpy(buffer_out, inner, *buffer_out_len);
print_message(msg_fd, "GetEchClientHelloInner set\n");
return 0;
}
#endif
void ech_get_client_hello_inner(WOLFSSL_CTX* ctx, const byte* data, size_t datalen)
{ {
#if 0
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL; WOLFSSL* ssl = NULL;
printf("wolfSSL_new(%p)\n", ctx);
ssl = wolfSSL_new(ctx); ssl = wolfSSL_new(ctx);
if (ssl == NULL) { if (ssl == NULL) {
fprintf(stderr, "Failed to create WOLFSSL object\n"); fprintf(stderr, "Failed to create WOLFSSL object\n");
close(client_fd);
return; return;
} }
// Disable certificate verification on this connection // Disable certificate verification on this connection
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE, NULL); wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE, NULL);
byte client_hello_inner[1024];
word32 ch_len = sizeof(client_hello_inner);;
char* sni_data = NULL; int res = wolfSSL_GetEchClientHelloInner(
word16 sni_len = wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, (void**)&sni_data); ssl,
#endif data, /* The full ClientHelloOuter */
datalen, /* How many bytes in the buffer */
(byte*)&client_hello_inner,
&ch_len
);
client_hello_inner[ch_len] = 0;
if (!res) {
print_message(msg_fd, "GetEchClientHelloInner: Innerlen: %d\n", ch_len);
hexdump(msg_fd, (char*)client_hello_inner, ch_len);
print_message(msg_fd, "/GetEchClientHelloInner\n");
}
byte sni_data[1024];
unsigned int sni_len = sizeof(sni_data);
memset(sni_data, 'A', sni_len);
printf("SNI_GetFromBuffer\n");
wolfSSL_SNI_GetFromBuffer(data, datalen, 0, (byte*)&sni_data, &sni_len);
sni_data[sni_len] = 0;
printf("sni_len = %d\n", sni_len);
printf("sni: %s\n", sni_data);
} }
void ech_match_sni(void) /* See if the context matches */
void ech_match_sni(const char* buffer_in, size_t buffer_len)
{ {
ech_get_client_hello_inner(wolfssl_ctx, (byte*)buffer_in, buffer_len);
} }
@@ -90,8 +190,6 @@ static void reset_conf(void)
print_message(msg_config_error, "Unable to open %s:%d:%s\n", cfg.ech_conf_sink, errno, strerror(errno)); print_message(msg_config_error, "Unable to open %s:%d:%s\n", cfg.ech_conf_sink, errno, strerror(errno));
return; return;
} }
printf("erased and opend %s\n", cfg.ech_conf_sink); printf("erased and opend %s\n", cfg.ech_conf_sink);
} }
@@ -103,6 +201,8 @@ static void ech_genkey(WOLFSSL_CTX* ctx, const char* sni)
{ {
int ret; int ret;
printf("ech_genkey for ctx %p\n", ctx);
print_message(msg_config, "Generating ECH config for outer SNI: %s\n", sni); print_message(msg_config, "Generating ECH config for outer SNI: %s\n", sni);
ret = wolfSSL_CTX_GenerateEchConfig(ctx, sni, 0, 0, 0); ret = wolfSSL_CTX_GenerateEchConfig(ctx, sni, 0, 0, 0);
if (ret != WOLFSSL_SUCCESS) { if (ret != WOLFSSL_SUCCESS) {
@@ -119,7 +219,7 @@ static void ech_genkey(WOLFSSL_CTX* ctx, const char* sni)
} }
char out[512]; char out[512];
int outLen = sizeof(out); word32 outLen = sizeof(out);
if (Base64_Encode_NoNl(echConfig, echConfigLen, (byte*)out, &outLen) != 0) { if (Base64_Encode_NoNl(echConfig, echConfigLen, (byte*)out, &outLen) != 0) {
print_message(msg_system_error, "Failed to encode to base64: %d\n", ret); print_message(msg_system_error, "Failed to encode to base64: %d\n", ret);
exit(1); exit(1);
@@ -132,38 +232,53 @@ static void ech_genkey(WOLFSSL_CTX* ctx, const char* sni)
* (Later, this will load the configurations from some place else */ * (Later, this will load the configurations from some place else */
static void ech_create_configs(WOLFSSL_CTX** ctx) static void ech_create_configs(WOLFSSL_CTX** ctx)
{ {
int i, j; int ret;
reset_conf(); reset_conf();
for (i = 0; i < cfg.listen_len; i++) { *ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
struct sslhcfg_protocols_item* listen = &cfg.listen[i]; if (!*ctx) {
printf("1\n"); print_message(msg_system_error, "Failed to create WOLFSSL_CTX\n");
*ctx = malloc(cfg.listen_len * sizeof(*ctx)); exit(1);
/* TODO deal with malloc failure */
printf("2\n");
ctx[i] = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
if (!ctx[i]) {
print_message(msg_system_error, "Failed to create WOLFSSL_CTX\n");
exit(1);
}
printf("3\n");
ech_genkey(ctx[i], cfg.listen[i].host);
printf("4\n");
} }
// Load certificate from memory
ret = wolfSSL_CTX_use_certificate_buffer(*ctx,
(const unsigned char*)SERVER_CERT_PEM,
strlen(SERVER_CERT_PEM),
WOLFSSL_FILETYPE_PEM);
if (ret != WOLFSSL_SUCCESS) {
fprintf(stderr, "Failed to load certificate: %d\n", ret);
fprintf(stderr, "Please replace SERVER_CERT_PEM with a valid certificate\n");
wolfSSL_CTX_free(*ctx);
exit(1);
}
// Load private key from memory
ret = wolfSSL_CTX_use_PrivateKey_buffer(*ctx,
(const unsigned char*)SERVER_KEY_PEM,
strlen(SERVER_KEY_PEM),
WOLFSSL_FILETYPE_PEM);
if (ret != WOLFSSL_SUCCESS) {
fprintf(stderr, "Failed to load private key: %d\n", ret);
fprintf(stderr, "Please replace SERVER_KEY_PEM with a valid private key\n");
wolfSSL_CTX_free(*ctx);
exit(1);
}
printf("Certificate and key loaded successfully\n");
ech_genkey(*ctx, cfg.listen[0].host); /* For now this only works on the first host */
} }
/* Initialises the ECH subsystem: create wolfSSL context, create ECH configs, /* Initialises the ECH subsystem: create wolfSSL context, create ECH configs,
* etc */ * etc */
void ech_init() void ech_init()
{ {
WOLFSSL_CTX* ctx = NULL;
wolfSSL_Init(); wolfSSL_Init();
ech_create_configs(&ctx); ech_create_configs(&wolfssl_ctx);
} }