Compare commits

...
Sign in to create a new pull request.

6 commits

Author SHA1 Message Date
sftcd
000b8958c9 ECH external APIs
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25663)
2025-01-09 16:53:04 +01:00
sftcd
9e8986d01d ECH CLI implementation
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25420)
2025-01-09 16:53:04 +01:00
Tomas Mraz
4cdbe04d98 Fixup conflicting SSL_R_ECH_REQUIRED
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tim Hudson <tjh@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25435)
2025-01-09 16:53:04 +01:00
Stephen Farrell
4666604387 ECH build artefacts and a bit of code
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25193)
2025-01-09 16:53:00 +01:00
Stephen Farrell
02e3203e40 Documents initial agreed APIs for Encrypted Client Hello (ECH)
and includes a minimal demo for some of those APIs.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/24738)
2025-01-09 16:51:39 +01:00
Stephen Farrell
cb616bf86c add ech-api.md
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/24738)
2025-01-09 16:51:39 +01:00
47 changed files with 5935 additions and 252 deletions

View file

@ -135,7 +135,7 @@ GENERATED_PODS={- # common0.tmpl provides @generated
fill_lines(" ", $COLUMNS - 15,
map { my $x = $_;
(
grep {
grep {
$unified_info{attributes}->{depends}
->{$x}->{$_}->{pod} // 0
}
@ -1328,7 +1328,8 @@ errors:
include/openssl/dtls1.h
include/openssl/srtp.h
include/openssl/quic.h
include/openssl/sslerr_legacy.h );
include/openssl/sslerr_legacy.h
include/openssl/ech.h);
my @cryptoheaders_tmpl =
qw( include/internal/dso.h
include/internal/o_dir.h

View file

@ -464,6 +464,7 @@ my @disablables = (
"ecdh",
"ecdsa",
"ecx",
"ech",
"egd",
"engine",
"err",
@ -619,7 +620,7 @@ my @disable_cascades = (
"blake2", "bf", "camellia", "cast", "chacha",
"cmac", "cms", "cmp", "comp", "ct",
"des", "dgram", "dh", "dsa",
"ec", "engine",
"ec", "ech", "engine",
"filenames",
"idea", "ktls",
"md4", "multiblock", "nextprotoneg",

View file

@ -781,6 +781,11 @@ Don't build support for Elliptic Curves.
Don't build support for binary Elliptic Curves
### no-ech
Don't build support for Encrypted Client Hello (ECH) extension (draft-ietf-tls-esni)
TODO(ECH) update link to RFC.
### enable-ec_nistp_64_gcc_128
Enable support for optimised implementations of some commonly used NIST

View file

@ -18,6 +18,7 @@ $OPENSSLSRC=\
pkcs8.c pkey.c pkeyparam.c pkeyutl.c prime.c rand.c req.c \
s_client.c s_server.c s_time.c sess_id.c smime.c speed.c \
spkac.c verify.c version.c x509.c rehash.c storeutl.c \
ech.c \
list.c info.c fipsinstall.c pkcs12.c
IF[{- !$disabled{'ec'} -}]
$OPENSSLSRC=$OPENSSLSRC ec.c ecparam.c

277
apps/ech.c Normal file
View file

@ -0,0 +1,277 @@
/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "apps.h"
#include "progs.h"
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/hpke.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#ifndef OPENSSL_NO_ECH
# define OSSL_ECH_KEYGEN_MODE 0 /* default: generate a key pair/ECHConfig */
# define OSSL_ECH_SELPRINT_MODE 1 /* we can print/down-select ECHConfigList */
# define OSSL_ECH_MAXINFILES 5 /* we'll only take this many inputs */
typedef enum OPTION_choice {
/* standard openssl options */
OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_VERBOSE, OPT_TEXT,
OPT_OUT, OPT_IN,
/* ECHConfig specifics */
OPT_PUBLICNAME, OPT_ECHVERSION,
OPT_MAXNAMELENGTH, OPT_HPKESUITE,
OPT_SELECT
} OPTION_CHOICE;
const OPTIONS ech_options[] = {
OPT_SECTION("General options"),
{"help", OPT_HELP, '-', "Display this summary"},
{"verbose", OPT_VERBOSE, '-', "Provide additional output"},
{"text", OPT_TEXT, '-', "Provide human-readable output"},
OPT_SECTION("Key generation"),
{"out", OPT_OUT, '>',
"Private key and/or ECHConfig [default: echconfig.pem]"},
{"public_name", OPT_PUBLICNAME, 's', "public_name value"},
{"max_name_len", OPT_MAXNAMELENGTH, 'n',
"Maximum host name length value [default: 0]"},
{"suite", OPT_HPKESUITE, 's', "HPKE ciphersuite: e.g. \"0x20,1,3\""},
{"ech_version", OPT_ECHVERSION, 'n',
"ECHConfig version [default: 0xff0d (13)]"},
OPT_SECTION("ECH PEM file downselect/display"),
{"in", OPT_IN, '<', "An ECH PEM file"},
{"select", OPT_SELECT, 'n', "Downselect to the numbered ECH config"},
{NULL}
};
/**
* @brief map version string like 0xff01 or 65291 to uint16_t
* @param arg is the version string, from command line
* @return is the uint16_t value (with zero for error cases)
*/
static uint16_t verstr2us(char *arg)
{
long lv = strtol(arg, NULL, 0);
uint16_t rv = 0;
if (lv < 0xffff && lv > 0)
rv = (uint16_t)lv;
return rv;
}
int ech_main(int argc, char **argv)
{
char *prog = NULL;
OPTION_CHOICE o;
int i, rv = 1, verbose = 0, text = 0, outsupp = 0;
int select = OSSL_ECHSTORE_ALL, numinfiles = 0;
char *outfile = NULL, *infile = NULL;
char *infiles[OSSL_ECH_MAXINFILES] = { NULL };
char *public_name = NULL, *suitestr = NULL;
uint16_t ech_version = OSSL_ECH_CURRENT_VERSION;
uint8_t max_name_length = 0;
OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
int mode = OSSL_ECH_KEYGEN_MODE; /* key generation */
OSSL_ECHSTORE *es = NULL;
BIO *ecf = NULL;
prog = opt_init(argc, argv, ech_options);
while ((o = opt_next()) != OPT_EOF) {
switch (o) {
case OPT_EOF:
case OPT_ERR:
BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
goto end;
case OPT_HELP:
opt_help(ech_options);
rv = 0;
goto end;
case OPT_VERBOSE:
verbose = 1;
break;
case OPT_TEXT:
text = 1;
break;
case OPT_SELECT:
mode = OSSL_ECH_SELPRINT_MODE;
select = strtol(opt_arg(), NULL, 10);
break;
case OPT_OUT:
outfile = opt_arg();
outsupp = 1;
break;
case OPT_IN:
mode = OSSL_ECH_SELPRINT_MODE;
infile = opt_arg();
if (numinfiles >= OSSL_ECH_MAXINFILES) {
BIO_printf(bio_err, "too many input files, only %d allowed\n",
OSSL_ECH_MAXINFILES);
goto opthelp;
}
infiles[numinfiles] = infile;
numinfiles++;
break;
case OPT_PUBLICNAME:
public_name = opt_arg();
break;
case OPT_ECHVERSION:
ech_version = verstr2us(opt_arg());
break;
case OPT_MAXNAMELENGTH:
{
long tmp = strtol(opt_arg(), NULL, 10);
if (tmp < 0 || tmp > OSSL_ECH_MAX_MAXNAMELEN) {
BIO_printf(bio_err,
"max name length out of range [0,%d] (%ld)\n",
OSSL_ECH_MAX_MAXNAMELEN, tmp);
goto opthelp;
} else {
max_name_length = (uint8_t)tmp;
}
}
break;
case OPT_HPKESUITE:
suitestr = opt_arg();
break;
}
}
argc = opt_num_rest();
argv = opt_rest();
if (argc != 0) {
BIO_printf(bio_err, "%s: Unknown parameter %s\n", prog, argv[0]);
goto opthelp;
}
/* Check ECH-specific inputs */
switch (ech_version) {
case OSSL_ECH_RFCXXXX_VERSION: /* fall through */
case 13:
ech_version = OSSL_ECH_RFCXXXX_VERSION;
break;
default:
BIO_printf(bio_err, "Un-supported version (0x%04x)\n", ech_version);
goto end;
}
if (max_name_length > OSSL_ECH_MAX_MAXNAMELEN) {
BIO_printf(bio_err, "Weird max name length (0x%04x) - biggest is "
"(0x%04x) - exiting\n", max_name_length,
OSSL_ECH_MAX_MAXNAMELEN);
ERR_print_errors(bio_err);
goto end;
}
if (suitestr != NULL) {
if (OSSL_HPKE_str2suite(suitestr, &hpke_suite) != 1) {
BIO_printf(bio_err, "Bad OSSL_HPKE_SUITE (%s)\n", suitestr);
ERR_print_errors(bio_err);
goto end;
}
}
/* Set default if needed */
if (outfile == NULL)
outfile = "echconfig.pem";
es = OSSL_ECHSTORE_new(NULL, NULL);
if (es == NULL)
goto end;
if (mode == OSSL_ECH_KEYGEN_MODE) {
if (verbose)
BIO_printf(bio_err, "Calling OSSL_ECHSTORE_new_config\n");
if ((ecf = BIO_new_file(outfile, "w")) == NULL
|| OSSL_ECHSTORE_new_config(es, ech_version, max_name_length,
public_name, hpke_suite) != 1
|| OSSL_ECHSTORE_write_pem(es, 0, ecf) != 1) {
BIO_printf(bio_err, "OSSL_ECHSTORE_new_config error\n");
goto end;
}
if (verbose)
BIO_printf(bio_err, "OSSL_ECHSTORE_new_config success\n");
rv = 0;
}
if (mode == OSSL_ECH_SELPRINT_MODE) {
if (numinfiles == 0)
goto opthelp;
for (i = 0; i != numinfiles; i++) {
if ((ecf = BIO_new_file(infiles[i], "r")) == NULL
|| OSSL_ECHSTORE_read_pem(es, ecf, OSSL_ECH_FOR_RETRY) != 1) {
if (verbose)
BIO_printf(bio_err, "OSSL_ECHSTORE_read_pem error for %s\n",
infiles[i]);
/* try read it as an ECHConfigList */
goto end;
}
BIO_free(ecf);
ecf = NULL;
}
if (verbose)
BIO_printf(bio_err, "Success reading %d files\n", numinfiles);
if (outsupp == 1) {
/* write result to that, with downselection if required */
if (verbose)
BIO_printf(bio_err, "Will write to %s\n", outfile);
if (verbose && select != OSSL_ECHSTORE_ALL)
BIO_printf(bio_err, "Selected entry: %d\n", select);
if ((ecf = BIO_new_file(outfile, "w")) == NULL
|| OSSL_ECHSTORE_write_pem(es, select, ecf) != 1) {
BIO_printf(bio_err, "OSSL_ECHSTORE_write_pem error\n");
goto end;
}
if (verbose)
BIO_printf(bio_err, "Success writing to %s\n", outfile);
}
rv = 0;
}
if (text) {
int oi_ind, oi_cnt = 0;
if (OSSL_ECHSTORE_num_entries(es, &oi_cnt) != 1)
goto end;
if (verbose)
BIO_printf(bio_err, "Printing %d ECHConfigList\n", oi_cnt);
for (oi_ind = 0; oi_ind != oi_cnt; oi_ind++) {
time_t secs = 0;
char *pn = NULL, *ec = NULL;
int has_priv, for_retry;
if (OSSL_ECHSTORE_get1_info(es, oi_ind, &secs, &pn, &ec,
&has_priv, &for_retry) != 1) {
OPENSSL_free(pn); /* just in case */
OPENSSL_free(ec);
goto end;
}
BIO_printf(bio_err, "ECH entry: %d public_name: %s age: %lld%s\n",
oi_ind, pn, (long long)secs,
has_priv ? " (has private key)" : "");
BIO_printf(bio_err, "\t%s\n", ec);
OPENSSL_free(pn);
OPENSSL_free(ec);
}
if (verbose)
BIO_printf(bio_err, "Success printing %d ECHConfigList\n", oi_cnt);
rv = 0;
}
end:
OSSL_ECHSTORE_free(es);
BIO_free_all(ecf);
return rv;
opthelp:
BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
BIO_printf(bio_err, "\tup to %d -in instances allowed\n", OSSL_ECH_MAXINFILES);
return rv;
}
#endif

View file

@ -772,6 +772,10 @@ static const STRINT_PAIR tlsext_types[] = {
{"certificate authorities", TLSEXT_TYPE_certificate_authorities},
{"post handshake auth", TLSEXT_TYPE_post_handshake_auth},
{"early_data", TLSEXT_TYPE_early_data},
#ifndef OPENSSL_NO_ECH
{"encrypted ClientHello (draft-13)", TLSEXT_TYPE_ech},
{"outer exts", TLSEXT_TYPE_outer_extensions},
#endif
{NULL}
};

View file

@ -1498,6 +1498,9 @@ static void list_disabled(void)
#ifdef OPENSSL_NO_ZSTD
BIO_puts(bio_out, "ZSTD\n");
#endif
#ifdef OPENSSL_NO_ECH
BIO_puts(bio_out, "ECH\n");
#endif
}
/* Unified enum for help and list commands. */

View file

@ -1340,6 +1340,7 @@ SSL_R_BAD_DH_VALUE:102:bad dh value
SSL_R_BAD_DIGEST_LENGTH:111:bad digest length
SSL_R_BAD_EARLY_DATA:233:bad early data
SSL_R_BAD_ECC_CERT:304:bad ecc cert
SSL_R_BAD_ECHCONFIG_EXTENSION:422:bad echconfig extension
SSL_R_BAD_ECPOINT:306:bad ecpoint
SSL_R_BAD_EXTENSION:110:bad extension
SSL_R_BAD_HANDSHAKE_LENGTH:332:bad handshake length
@ -1419,6 +1420,8 @@ SSL_R_DTLS_MESSAGE_TOO_BIG:334:dtls message too big
SSL_R_DUPLICATE_COMPRESSION_ID:309:duplicate compression id
SSL_R_ECC_CERT_NOT_FOR_SIGNING:318:ecc cert not for signing
SSL_R_ECDH_REQUIRED_FOR_SUITEB_MODE:374:ecdh required for suiteb mode
SSL_R_ECH_DECODE_ERROR:423:ech decode error
SSL_R_ECH_REQUIRED:421:ech required
SSL_R_EE_KEY_TOO_SMALL:399:ee key too small
SSL_R_EMPTY_RAW_PUBLIC_KEY:349:empty raw public key
SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST:354:empty srtp protection profile list

View file

@ -4,7 +4,7 @@
#
# LD_LIBRARY_PATH=../.. ./sslecho
TESTS = sslecho
TESTS = sslecho echecho
CFLAGS = -I../../include -g -Wall
LDFLAGS = -L../..
@ -14,6 +14,8 @@ all: $(TESTS)
sslecho: main.o
echecho: echecho.o
$(TESTS):
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS)

View file

@ -24,3 +24,48 @@ The cert.pem and key.pem files included are self signed certificates with the
"Common Name" of 'localhost'.
Best to create the 'pem' files using an actual hostname.
Encrypted Client Hello (ECH) Variant
====================================
``echecho.c`` implements the same functionality but demonstrates minimal code
changes needed to use ECH. The ``echecho`` binary has the same user interface
discussed above but enables ECH for the connection, based on hard-coded ECH
configuration data. A real server would load file(s), and a real client would
acquire an ECHConfigList from the DNS.
All that's required to use ECH is to load ECH data via `OSSL_ECHSTORE_read_*`
APIs and then enable ECH via ``SSL_CTX_set1_echstore()``. Both client and
server check and print out the status of ECH using ``SSL_ech_get1_status()``,
but that's optional.
To run the server:
$ LD_LIBRARY_PATH=../.. ./echecho s
To run the client:
$ LD_LIBRARY_PATH=../.. ./echecho c localhost
All going well both server and client will print the ECH status at the
start of each connection. That looks like:
ECH worked (status: 1, inner: localhost, outer: example.com)
If the non-ECH demo client (``sslecho``) is used instead the server will
output:
ECH failed/not-tried (status: -101, inner: (null), outer: (null))
If the non-ECH demo server (i.e., ``sslecho``) is used, the client will exit
with an error as ECH was attempted and failed. In a debug build, that looks
like:
80EBEE54227F0000:error:0A000163:SSL routines:tls_process_initial_server_flight:ech required:ssl/statem/statem_clnt.c:3274:
A real client would likely fall back to not using ECH, but the above
is ok for a demo.
In that case, the server will also exit based on the ECH alert from the client:
403787A8307F0000:error:0A000461:SSL routines:ssl3_read_bytes:reason(1121):../ssl/record/rec_layer_s3.c:1588:SSL alert number 121

405
demos/sslecho/echecho.c Normal file
View file

@ -0,0 +1,405 @@
/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
static const int server_port = 4433;
static const char echconfig[] = "AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEAAQALZXhhbXBsZS5jb20AAA==";
static const char echprivbuf[] =
"-----BEGIN PRIVATE KEY-----\n"
"MC4CAQAwBQYDK2VuBCIEICjd4yGRdsoP9gU7YT7My8DHx1Tjme8GYDXrOMCi8v1V\n"
"-----END PRIVATE KEY-----\n"
"-----BEGIN ECHCONFIG-----\n"
"AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEAAQALZXhhbXBsZS5jb20AAA==\n"
"-----END ECHCONFIG-----\n";
typedef unsigned char bool;
#define true 1
#define false 0
/*
* This flag won't be useful until both accept/read (TCP & SSL) methods
* can be called with a timeout. TBD.
*/
static volatile bool server_running = true;
int create_socket(bool isServer)
{
int s;
int optval = 1;
struct sockaddr_in addr = { 0 };
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
perror("Unable to create socket");
exit(EXIT_FAILURE);
}
if (isServer) {
addr.sin_family = AF_INET;
addr.sin_port = htons(server_port);
addr.sin_addr.s_addr = INADDR_ANY;
/* Reuse the address; good for quick restarts */
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))
< 0) {
perror("setsockopt(SO_REUSEADDR) failed");
exit(EXIT_FAILURE);
}
if (bind(s, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
perror("Unable to bind");
exit(EXIT_FAILURE);
}
if (listen(s, 1) < 0) {
perror("Unable to listen");
exit(EXIT_FAILURE);
}
}
return s;
}
SSL_CTX* create_context(bool isServer)
{
const SSL_METHOD *method;
SSL_CTX *ctx;
if (isServer)
method = TLS_server_method();
else
method = TLS_client_method();
ctx = SSL_CTX_new(method);
if (ctx == NULL) {
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}
static int configure_ech(SSL_CTX *ctx, int server,
unsigned char *buf, size_t len)
{
OSSL_ECHSTORE *es = NULL;
BIO *es_in = BIO_new_mem_buf(buf, len);
if (es_in == NULL || (es = OSSL_ECHSTORE_new(NULL, NULL)) == NULL)
goto err;
if (server && OSSL_ECHSTORE_read_pem(es, es_in, 1) != 1)
goto err;
if (!server && OSSL_ECHSTORE_read_echconfiglist(es, es_in) != 1)
goto err;
if (SSL_CTX_set1_echstore(ctx, es) != 1)
goto err;
BIO_free_all(es_in);
return 1;
err:
OSSL_ECHSTORE_free(es);
BIO_free_all(es_in);
return 0;
}
void configure_server_context(SSL_CTX *ctx)
{
/* Set the key and cert */
if (SSL_CTX_use_certificate_chain_file(ctx, "cert.pem") <= 0) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (configure_ech(ctx, 1, (unsigned char*)echprivbuf,
sizeof(echprivbuf) - 1) != 1) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
}
void configure_client_context(SSL_CTX *ctx)
{
/*
* Configure the client to abort the handshake if certificate verification
* fails
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
/*
* In a real application you would probably just use the default system certificate trust store and call:
* SSL_CTX_set_default_verify_paths(ctx);
* In this demo though we are using a self-signed certificate, so the client must trust it directly.
*/
if (!SSL_CTX_load_verify_locations(ctx, "cert.pem", NULL)) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (configure_ech(ctx, 0, (unsigned char*)echconfig,
sizeof(echconfig) - 1) != 1) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
}
void usage()
{
printf("Usage: echecho s\n");
printf(" --or--\n");
printf(" echecho c ip\n");
printf(" c=client, s=server, ip=dotted ip of server\n");
exit(1);
}
int main(int argc, char **argv)
{
bool isServer;
int result;
SSL_CTX *ssl_ctx = NULL;
SSL *ssl = NULL;
int server_skt = -1;
int client_skt = -1;
/* used by getline relying on realloc, can't be statically allocated */
char *txbuf = NULL;
size_t txcap = 0;
int txlen;
char rxbuf[128];
size_t rxcap = sizeof(rxbuf);
int rxlen;
char *rem_server_ip = NULL;
struct sockaddr_in addr = { 0 };
unsigned int addr_len = sizeof(addr);
char *outer_sni = NULL, *inner_sni = NULL;
int ech_status;
/* Splash */
printf("\nechecho : Simple Echo Client/Server: %s : %s\n\n", __DATE__,
__TIME__);
/* Need to know if client or server */
if (argc < 2) {
usage();
/* NOTREACHED */
}
isServer = (argv[1][0] == 's') ? true : false;
/* If client get remote server address (could be 127.0.0.1) */
if (!isServer) {
if (argc != 3) {
usage();
/* NOTREACHED */
}
rem_server_ip = argv[2];
}
/* Create context used by both client and server */
ssl_ctx = create_context(isServer);
/* If server */
if (isServer) {
printf("We are the server on port: %d\n\n", server_port);
/* Configure server context with appropriate key files */
configure_server_context(ssl_ctx);
/* Create server socket; will bind with server port and listen */
server_skt = create_socket(true);
/*
* Loop to accept clients.
* Need to implement timeouts on TCP & SSL connect/read functions
* before we can catch a CTRL-C and kill the server.
*/
while (server_running) {
/* Wait for TCP connection from client */
client_skt = accept(server_skt, (struct sockaddr*) &addr,
&addr_len);
if (client_skt < 0) {
perror("Unable to accept");
exit(EXIT_FAILURE);
}
printf("Client TCP connection accepted\n");
/* Create server SSL structure using newly accepted client socket */
ssl = SSL_new(ssl_ctx);
SSL_set_fd(ssl, client_skt);
/* Wait for SSL connection from the client */
if (SSL_accept(ssl) <= 0) {
ERR_print_errors_fp(stderr);
server_running = false;
} else {
printf("Client SSL connection accepted\n\n");
ech_status = SSL_ech_get1_status(ssl, &inner_sni, &outer_sni);
printf("ECH %s (status: %d, inner: %s, outer: %s)\n",
(ech_status == 1 ? "worked" : "failed/not-tried"),
ech_status, inner_sni, outer_sni);
OPENSSL_free(inner_sni);
OPENSSL_free(outer_sni);
inner_sni = outer_sni = NULL;
/* Echo loop */
while (true) {
/* Get message from client; will fail if client closes connection */
if ((rxlen = SSL_read(ssl, rxbuf, rxcap)) <= 0) {
if (rxlen == 0) {
printf("Client closed connection\n");
}
ERR_print_errors_fp(stderr);
break;
}
/* Insure null terminated input */
rxbuf[rxlen] = 0;
/* Look for kill switch */
if (strcmp(rxbuf, "kill\n") == 0) {
/* Terminate...with extreme prejudice */
printf("Server received 'kill' command\n");
server_running = false;
break;
}
/* Show received message */
printf("Received: %s", rxbuf);
/* Echo it back */
if (SSL_write(ssl, rxbuf, rxlen) <= 0) {
ERR_print_errors_fp(stderr);
}
}
}
if (server_running) {
/* Cleanup for next client */
SSL_shutdown(ssl);
SSL_free(ssl);
close(client_skt);
}
}
printf("Server exiting...\n");
}
/* Else client */
else {
printf("We are the client\n\n");
/* Configure client context so we verify the server correctly */
configure_client_context(ssl_ctx);
/* Create "bare" socket */
client_skt = create_socket(false);
/* Set up connect address */
addr.sin_family = AF_INET;
inet_pton(AF_INET, rem_server_ip, &addr.sin_addr.s_addr);
addr.sin_port = htons(server_port);
/* Do TCP connect with server */
if (connect(client_skt, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
perror("Unable to TCP connect to server");
goto exit;
} else {
printf("TCP connection to server successful\n");
}
/* Create client SSL structure using dedicated client socket */
ssl = SSL_new(ssl_ctx);
SSL_set_fd(ssl, client_skt);
/* Set hostname for SNI */
SSL_set_tlsext_host_name(ssl, rem_server_ip);
/* Configure server hostname check */
SSL_set1_host(ssl, rem_server_ip);
/* Now do SSL connect with server */
if (SSL_connect(ssl) == 1) {
printf("SSL connection to server successful\n\n");
ech_status = SSL_ech_get1_status(ssl, &inner_sni, &outer_sni);
printf("ECH %s (status: %d, inner: %s, outer: %s)\n",
(ech_status == 1 ? "worked" : "failed/not-tried"),
ech_status, inner_sni, outer_sni);
OPENSSL_free(inner_sni);
OPENSSL_free(outer_sni);
inner_sni = outer_sni = NULL;
/* Loop to send input from keyboard */
while (true) {
/* Get a line of input */
txlen = getline(&txbuf, &txcap, stdin);
/* Exit loop on error */
if (txlen < 0 || txbuf == NULL) {
break;
}
/* Exit loop if just a carriage return */
if (txbuf[0] == '\n') {
break;
}
/* Send it to the server */
if ((result = SSL_write(ssl, txbuf, txlen)) <= 0) {
printf("Server closed connection\n");
ERR_print_errors_fp(stderr);
break;
}
/* Wait for the echo */
rxlen = SSL_read(ssl, rxbuf, rxcap);
if (rxlen <= 0) {
printf("Server closed connection\n");
ERR_print_errors_fp(stderr);
break;
} else {
/* Show it */
rxbuf[rxlen] = 0;
printf("Received: %s", rxbuf);
}
}
printf("Client exiting...\n");
} else {
printf("SSL connection to server failed\n\n");
ERR_print_errors_fp(stderr);
}
}
exit:
/* Close up */
if (ssl != NULL) {
SSL_shutdown(ssl);
SSL_free(ssl);
}
SSL_CTX_free(ssl_ctx);
if (client_skt != -1)
close(client_skt);
if (server_skt != -1)
close(server_skt);
if (txbuf != NULL && txcap > 0)
free(txbuf);
printf("echecho exiting\n");
return 0;
}

View file

@ -82,6 +82,12 @@ DEPEND[man/man1/openssl-ec.1]=man1/openssl-ec.pod
GENERATE[man/man1/openssl-ec.1]=man1/openssl-ec.pod
DEPEND[man1/openssl-ec.pod]{pod}=man1/openssl-ec.pod.in
GENERATE[man1/openssl-ec.pod]=man1/openssl-ec.pod.in
DEPEND[html/man1/openssl-ech.html]=man1/openssl-ech.pod
GENERATE[html/man1/openssl-ech.html]=man1/openssl-ech.pod
DEPEND[man/man1/openssl-ech.1]=man1/openssl-ech.pod
GENERATE[man/man1/openssl-ech.1]=man1/openssl-ech.pod
DEPEND[man1/openssl-ech.pod]{pod}=man1/openssl-ech.pod.in
GENERATE[man1/openssl-ech.pod]=man1/openssl-ech.pod.in
DEPEND[html/man1/openssl-ecparam.html]=man1/openssl-ecparam.pod
GENERATE[html/man1/openssl-ecparam.html]=man1/openssl-ecparam.pod
DEPEND[man/man1/openssl-ecparam.1]=man1/openssl-ecparam.pod
@ -361,6 +367,7 @@ html/man1/openssl-dhparam.html \
html/man1/openssl-dsa.html \
html/man1/openssl-dsaparam.html \
html/man1/openssl-ec.html \
html/man1/openssl-ech.html \
html/man1/openssl-ecparam.html \
html/man1/openssl-enc.html \
html/man1/openssl-engine.html \
@ -421,6 +428,7 @@ man/man1/openssl-dhparam.1 \
man/man1/openssl-dsa.1 \
man/man1/openssl-dsaparam.1 \
man/man1/openssl-ec.1 \
man/man1/openssl-ech.1 \
man/man1/openssl-ecparam.1 \
man/man1/openssl-enc.1 \
man/man1/openssl-engine.1 \
@ -2739,6 +2747,10 @@ DEPEND[html/man3/SSL_session_reused.html]=man3/SSL_session_reused.pod
GENERATE[html/man3/SSL_session_reused.html]=man3/SSL_session_reused.pod
DEPEND[man/man3/SSL_session_reused.3]=man3/SSL_session_reused.pod
GENERATE[man/man3/SSL_session_reused.3]=man3/SSL_session_reused.pod
DEPEND[html/man3/SSL_set1_echstore.html]=man3/SSL_set1_echstore.pod
GENERATE[html/man3/SSL_set1_echstore.html]=man3/SSL_set1_echstore.pod
DEPEND[man/man3/SSL_set1_echstore.3]=man3/SSL_set1_echstore.pod
GENERATE[man/man3/SSL_set1_echstore.3]=man3/SSL_set1_echstore.pod
DEPEND[html/man3/SSL_set1_host.html]=man3/SSL_set1_host.pod
GENERATE[html/man3/SSL_set1_host.html]=man3/SSL_set1_host.pod
DEPEND[man/man3/SSL_set1_host.3]=man3/SSL_set1_host.pod
@ -3692,6 +3704,7 @@ html/man3/SSL_read.html \
html/man3/SSL_read_early_data.html \
html/man3/SSL_rstate_string.html \
html/man3/SSL_session_reused.html \
html/man3/SSL_set1_echstore.html \
html/man3/SSL_set1_host.html \
html/man3/SSL_set1_initial_peer_addr.html \
html/man3/SSL_set1_server_cert_type.html \
@ -4356,6 +4369,7 @@ man/man3/SSL_read.3 \
man/man3/SSL_read_early_data.3 \
man/man3/SSL_rstate_string.3 \
man/man3/SSL_session_reused.3 \
man/man3/SSL_set1_echstore.3 \
man/man3/SSL_set1_host.3 \
man/man3/SSL_set1_initial_peer_addr.3 \
man/man3/SSL_set1_server_cert_type.3 \

645
doc/designs/ech-api.md Normal file
View file

@ -0,0 +1,645 @@
Encrypted ClientHello (ECH) APIs
================================
TODO(ECH): replace references/links to the [sftcd
ECH-draft-13c](https://github.com/sftcd/openssl/tree/ECH-draft-13c) (the branch
that has good integration and interop) with relative links as files are
migrated into (PRs for) the feature branch.
The `OSSL_ECHSTORE` related text here matches the ECH
[feature branch](https://github.com/openssl/openssl/tree/feature/ech).
There is an [OpenSSL fork](https://github.com/sftcd/openssl/tree/ECH-draft-13c)
that has an implementation of Encrypted Client Hello (ECH) and these are design
notes taking the APIs implemented there as a starting point.
The ECH Protocol
----------------
ECH involves creating an "inner" ClientHello (CH) that contains the potentially
sensitive content of a CH, primarily the SNI and perhaps the ALPN values. That
inner CH is then encrypted and embedded (as a CH extension) in an outer CH that
contains presumably less sensitive values. The spec includes a "compression"
scheme that allows the inner CH to refer to extensions from the outer CH where
the same value would otherwise be present in both.
ECH makes use of [HPKE](https://datatracker.ietf.org/doc/rfc9180/) for the
encryption of the inner CH. HPKE code was merged to the master branch in
November 2022.
The ECH APIs are also documented
[here](https://github.com/sftcd/openssl/blob/ECH-draft-13c/doc/man3/SSL_ech_set1_echconfig.pod).
The descriptions here are less formal and provide some justification for the
API design.
Unless otherwise stated all APIs return 1 in the case of success and 0 for
error. All APIs call `SSLfatal` or `ERR_raise` macros as appropriate before
returning an error.
Prototypes are mostly in
[`include/openssl/ech.h`](https://github.com/sftcd/openssl/blob/ECH-draft-13c/include/openssl/ech.h)
for now.
General Approach
----------------
This ECH implementation has been prototyped via integrations with curl, apache2,
lighttpd, nginx and haproxy. The implementation interoperates with all other
known ECH implementations, including browsers, the libraries they use
(NSS/BoringSSL), a closed-source server implementation (Cloudflare's test
server) and with wolfssl and (reportedly) a rusttls client.
To date, the approach taken has been to minimise the application layer code
changes required to ECH-enable those applications. There is of course a tension
between that minimisation goal and providing generic and future-proof
interfaces.
In terms of implementation, it is expected (and welcome) that many details of
the current ECH implementation will change during review.
Specification
-------------
ECH is an IETF TLS WG specification. It has been stable since
[draft-13](https://datatracker.ietf.org/doc/draft-ietf-tls-esni/13/), published
in August 2021. The latest draft can be found
[here](https://datatracker.ietf.org/doc/draft-ietf-tls-esni/).
Once browsers and others have done sufficient testing the plan is to
proceed to publishing ECH as an RFC.
The only current ECHConfig version supported is 0xfe0d which will be the
value to be used in the eventual RFC when that issues. (We'll replace the
XXXX with the relevant RFC number once that's known.)
```c
/* version from RFC XXXX */
# define OSSL_ECH_RFCXXXX_VERSION 0xfe0d
/* latest version from an RFC */
# define OSSL_ECH_CURRENT_VERSION OSSL_ECH_RFCXXXX_VERSION
```
Note that 0xfe0d is also the value of the ECH extension codepoint:
```c
# define TLSEXT_TYPE_ech 0xfe0d
```
The uses of those should be correctly differentiated in the implementation, to
more easily avoid problems if/when new versions are defined.
Minimal Sample Code
-------------------
TODO(ECH): This sample code has only been compiled. The `OSSL_ECHSTORE` stuff
doesn't work yet.
OpenSSL includes code for an [`sslecho`](../../demos/sslecho) demo. We've
added a minimal [`echecho`](../../demos/sslecho/echecho.c) that shows how to
ECH-enable this demo.
Handling Custom Extensions
--------------------------
OpenSSL supports custom extensions (via `SSL_CTX_add_custom_ext()`) so that
extension values are supplied and parsed by client and server applications via
a callback. The ECH specification of course doesn't deal with such
implementation matters, but comprehensive ECH support for such custom
extensions could quickly become complex. At present, in the absence of evidence
of sensitive custom extension values, we handle all such extensions by using
the ECH compression mechanism. That means we require no API changes, only make
one call to the application callbacks and get interoperability, but that such
extension values remain visible to network observers. That could change if some
custom value turns out to be sensitive such that we'd prefer to not include it
in the outer CH.
Padding
-------
The privacy protection provided by ECH benefits from an observer not being able
to differentiate access to different web origins based on TLS handshake
packets. Some TLS handshake messages can however reduce the size of the
anonymity-set due to message-sizes. In particular the Certificate message size
will depend on the name of the SNI from the inner ClientHello. TLS however does
allow for record layer padding which can reduce the impact of underlying
message sizes on the size of the anonymity set. The recently added
`SSL_CTX_record_padding_ex()` and `SSL_record_padding_ex()` APIs allow for
setting separate padding sizes for the handshake messages, (that most affect
ECH), and application data messages (where padding may affect efficiency more).
ECHConfig Extensions
--------------------
The ECH protocol supports extensibility [within the ECHConfig
structure](https://www.ietf.org/archive/id/draft-ietf-tls-esni-18.html#section-4.2)
via a typical TLS type, length, value scheme. However, to date, there are no
extensions defined, nor do other implementations provide APIs for adding or
manipulating ECHConfig extensions. We therefore take the same approach here.
When running the ECH protocol, implementations are required to skip over
unknown ECHConfig extensions, or to fail for so-called "mandatory" unsupported
ECHConfig extensions. Our library code is compliant in that respect - it will
skip over extensions that are not "mandatory" (extension type high bit clear)
and fail if any "mandatory" ECHConfig extension (extension type high bit set)
is seen.
For testing purposes, ECHConfigList values that contain ECHConfig extensions
can be produced using external scripts, and used with the library, but there is
no API support for generating such, and the library has no support for any
specific ECHConfig extension type. (Other than skipping over or failing as
described above.)
In general, the ECHConfig extensibility mechanism seems to have no proven
utility. (If new fields for an ECHConfig are required, a new ECHConfig version
with the proposed changes can just as easily be developed/deployed.)
The theory for ECHConfig extensions is that such values might be used to
control the outer ClientHello - controls to affect the inner ClientHello, when
ECH is used, are envisaged to be published as SvcParamKey values in SVCB/HTTP
resource records in the DNS.
To repeat though: after a number of years of the development of ECH, no such
ECHConfig extensions have been proposed.
Should some useful ECHConfig extensions be defined in future, then the
`OSSL_ECHSTORE` APIs could be extended to enable management of such, or, new
opaque types could be developed enabling further manipulation of ECHConfig and
ECHConfigList values.
ECH keys versus TLS server keys
-------------------------------
ECH private keys are similar to, but different from, TLS server private keys
used to authenticate servers. Notably:
- ECH private keys are expected to be rotated roughly hourly, rather than every
month or two for TLS server private keys. Hourly ECH key rotation is an
attempt to provide better forward secrecy, given ECH implements an
ephemeral-static ECDH scheme.
- ECH private keys stand alone - there are no hierarchies and there is no
chaining, and no certificates and no defined relationships between current
and older ECH private keys. The expectation is that a "current" ECH public key
will be published in the DNS and that plus approx. 2 "older" ECH private keys
will remain usable for decryption at any given time. This is a way to balance
DNS TTLs versus forward secrecy and robustness.
- In particular, the above means that we do not see any need to repeatedly
parse or process related ECHConfigList structures - each can be processed
independently for all practical purposes.
- There are all the usual algorithm variations, and those will likely result in
the same x25519 versus p256 combinatorics. How that plays out has yet to be
seen as FIPS compliance for ECH is not (yet) a thing. For OpenSSL, it seems
wise to be agnostic and support all relevant combinations. (And doing so is not
that hard.)
ECH Store APIs
--------------
We introduce an externally opaque type `OSSL_ECHSTORE` to allow applications
to create and manage ECHConfigList values and associated meta-data. The
external APIs using `OSSL_ECHSTORE` are:
```c
typedef struct ossl_echstore_st OSSL_ECHSTORE;
/* if a caller wants to index the last entry in the store */
# define OSSL_ECHSTORE_LAST -1
/* if a caller wants all entries in the store, e.g. to print public values */
# define OSSL_ECHSTORE_ALL -2
OSSL_ECHSTORE *OSSL_ECHSTORE_new(OSSL_LIB_CTX *libctx, const char *propq);
void OSSL_ECHSTORE_free(OSSL_ECHSTORE *es);
int OSSL_ECHSTORE_new_config(OSSL_ECHSTORE *es,
uint16_t echversion, uint8_t max_name_length,
const char *public_name, OSSL_HPKE_SUITE suite);
int OSSL_ECHSTORE_write_pem(OSSL_ECHSTORE *es, int index, BIO *out);
int OSSL_ECHSTORE_read_echconfiglist(OSSL_ECHSTORE *es, BIO *in);
int OSSL_ECHSTORE_get1_info(OSSL_ECHSTORE *es, int index, time_t *loaded_secs,
char **public_name, char **echconfig,
int *has_private, int *for_retry);
int OSSL_ECHSTORE_downselect(OSSL_ECHSTORE *es, int index);
int OSSL_ECHSTORE_set1_key_and_read_pem(OSSL_ECHSTORE *es, EVP_PKEY *priv,
BIO *in, int for_retry);
int OSSL_ECHSTORE_read_pem(OSSL_ECHSTORE *es, BIO *in, int for_retry);
int OSSL_ECHSTORE_num_entries(OSSL_ECHSTORE *es, int *numentries);
int OSSL_ECHSTORE_num_keys(OSSL_ECHSTORE *es, int *numkeys);
int OSSL_ECHSTORE_flush_keys(OSSL_ECHSTORE *es, time_t age);
```
`OSSL_ECHSTORE_new()` and `OSSL_ECHSTORE_free()` are relatively obvious.
`OSSL_ECHSTORE_new_config()` allows the caller to create a new private key
value and the related "singleton" ECHConfigList structure.
`OSSL_ECHSTORE_write_pem()` allows the caller to produce a "PEM" data
structure (conforming to the [PEMECH
specification](https://datatracker.ietf.org/doc/draft-farrell-tls-pemesni/))
from the `OSSL_ECHSTORE` entry identified by the `index`. (An `index` of
`OSSL_ECHSTORE_LAST` will select the last entry. An `index` of
`OSSL_ECHSTORE_ALL` will output all public values, and no private values.)
These two APIs will typically be used via the `openssl ech` command line tool.
`OSSL_ECHSTORE_read_echconfiglist()` will typically be used by a client to
ingest the "ech=" SvcParamKey value found in an SVCB or HTTPS RR retrieved from
the DNS. The resulting set of ECHConfig values can then be associated with an
`SSL_CTX` or `SSL` structure for TLS connections.
`OSSL_ECHSTORE_get1_info()` presents the caller with information about the
content of the store for logging or for display, e.g. in a command line tool.
`OSSL_ECHSTORE_downselect()` API gives the client a way to select one
particular ECHConfig value from the set stored (discarding the rest).
`OSSL_ECHSTORE_set1_key_and_read_pem()` and `OSSL_ECHSTORE_read_pem()` can be
used to load a private key value and associated "singleton" ECHConfigList.
Those can be used (by servers) to enable ECH for an `SSL_CTX` or `SSL`
connection. In addition to loading those values, the application can also
indicate via `for_retry` which ECHConfig value(s) are to be included in the
`retry_configs` fallback scheme defined by the ECH protocol.
`OSSL_ECHSTORE_num_entries()` and `OSSL_ECHSTORE_num_keys()` allow an
application to see how many usable ECH configs and private keys are currently
in the store, and `OSSL_ECHSTORE_flush_keys()` allows a server to flush keys
that are older than `age` seconds. The general model is that a server can
maintain an `OSSL_ECHSTORE` into which it periodically loads the "latest" set
of keys, e.g. hourly, and also discards the keys that are too old, e.g. more
than 3 hours old. This allows for more robust private key management even if
public key distribution suffers temporary failures.
The APIs the clients and servers can use to associate an `OSSL_ECHSTORE`
with an `SSL_CTX` or `SSL` structure:
```c
int SSL_CTX_set1_echstore(SSL_CTX *ctx, OSSL_ECHSTORE *es);
int SSL_set1_echstore(SSL *s, OSSL_ECHSTORE *es);
```
ECH will be enabled for the relevant `SSL_CTX` or `SSL` connection
when these functions succeed. Any previously associated `OSSL_ECHSTORE`
will be `OSSL_ECHSTORE_free()`ed.
There is also an API that allows setting an ECHConfigList for an SSL
connection, that is compatible with BoringSSL. Note that the input
`ecl` here can be either base64 or binary encoded, but for
BoringSSL it must be binary encoded.
```c
int SSL_set1_ech_config_list(SSL *ssl, const uint8_t *ecl, size_t ecl_len);
```
To access the `OSSL_ECHSTORE` associated with an `SSL_CTX` or
`SSL` connection:
```c
OSSL_ECHSTORE *SSL_CTX_get1_echstore(const SSL_CTX *ctx);
OSSL_ECHSTORE *SSL_get1_echstore(const SSL *s);
```
The resulting `OSSL_ECHSTORE` can be modified and then re-associated
with an `SSL_CTX` or `SSL` connection.
ECH Store Internals
-------------------
The internal structure of an ECH Store is as described below:
```c
typedef struct ossl_echext_st {
uint16_t type;
uint16_t len;
unsigned char *val;
} OSSL_ECHEXT;
DEFINE_STACK_OF(OSSL_ECHEXT)
typedef struct ossl_echstore_entry_st {
uint16_t version; /* 0xff0d for draft-13 */
char *public_name;
size_t pub_len;
unsigned char *pub;
unsigned int nsuites;
OSSL_HPKE_SUITE *suites;
uint8_t max_name_length;
uint8_t config_id;
STACK_OF(OSSL_ECHEXT) *exts;
time_t loadtime; /* time public and private key were loaded from file */
EVP_PKEY *keyshare; /* long(ish) term ECH private keyshare on a server */
int for_retry; /* whether to use this ECHConfigList in a retry */
size_t encoded_len; /* length of overall encoded content */
unsigned char *encoded; /* overall encoded content */
} OSSL_ECHSTORE_ENTRY;
DEFINE_STACK_OF(OSSL_ECHSTORE_ENTRY)
struct ossl_echstore_st {
STACK_OF(OSSL_ECHSTORE_ENTRY) *entries;
OSSL_LIB_CTX *libctx;
const char *propq;
};
```
Some notes on the above ECHConfig fields:
- `version` should be `OSSL_ECH_CURRENT_VERSION` for the current version.
- `public_name` field is the name used in the SNI of the outer ClientHello, and
that a server ought be able to authenticate if using the `retry_configs`
fallback mechanism.
- `config_id` is a one-octet value used by servers to select which private
value to use to attempt ECH decryption. Servers can also do trial decryption
if desired, as clients might use a random value for the `confid_id` as an
anti-fingerprinting mechanism. (The use of one octet for this value was the
result of an extended debate about efficiency versus fingerprinting.)
- The `max_name_length` is an element of the ECHConfigList that is used by
clients as part of a padding algorithm. (That design is part of the spec, but
isn't necessarily great - the idea is to include the longest value that might
be the length of a DNS name included as an inner CH SNI.) A value of 0 is
perhaps most likely to be used, indicating that the maximum isn't known.
Essentially, an ECH store is a set of ECHConfig values, plus optionally
(for servers), relevant private key value information.
When a non-singleton ECHConfigList is ingested, that is expanded into
a store that is the same as if a set of singleton ECHConfigList values
had been ingested sequentially.
In addition to the obvious fields from each ECHConfig, we also store:
- The `encoded` value (and length) of the ECHConfig, as that is used
as an input for the HPKE encapsulation of the inner ClientHello. (Used
by both clients and servers.)
- The `EVP_PKEY` pointer to the private key value associated with the
relevant ECHConfig, for use by servers.
- The PEM filename and file modification time from which a private key value
and ECHConfigList were loaded. If those values are loaded from memory,
the filename value is the SHA-256 hash of the encoded ECHConfigList and
the load time is the time of loading. These values assist when servers
periodically re-load sets of files or PEM structures from memory.
Split-mode handling
-------------------
TODO(ECH): This ECH split-mode API should be considered tentative. It's design
will be revisited as we get to considering the internals.
ECH split-mode involves a front-end server that only does ECH decryption and
then passes on the decrypted inner CH to a back-end TLS server that negotiates
the actual TLS session with the client, based on the inner CH content. The
function to support this simply takes the outer CH, indicates whether
decryption has succeeded or not, and if it has, returns the inner CH and SNI
values (allowing routing to the correct back-end). Both the supplied (outer)
CH and returned (inner) CH here include the record layer header.
```c
int SSL_CTX_ech_raw_decrypt(SSL_CTX *ctx,
int *decrypted_ok,
char **inner_sni, char **outer_sni,
unsigned char *outer_ch, size_t outer_len,
unsigned char *inner_ch, size_t *inner_len,
unsigned char **hrrtok, size_t *toklen);
```
The caller allocates the `inner_ch` buffer, on input `inner_len` should
contain the size of the `inner_ch` buffer, on output the size of the actual
inner CH. Note that, when ECH decryption succeeds, the inner CH will always be
smaller than the outer CH.
If there is no ECH present in the outer CH then this will return 1 (i.e., the
call will succeed) but `decrypted_ok` will be zero. The same will result if a
GREASEd ECH is present or decryption fails for some other (indistinguishable)
reason.
If the caller wishes to support HelloRetryRequest (HRR), then it must supply
the same `hrrtok` and `toklen` pointers to both calls to
`SSL_CTX_ech_raw_decrypt()` (for the initial and second ClientHello
messages). When done, the caller must free the `hrrtok` using
`OPENSSL_free()`. If the caller doesn't need to support HRR, then it can
supply NULL values for these parameters. The value of the token is the client's
ephemeral public value, which is not sensitive having being sent in clear in
the first ClientHello. This value is missing from the second ClientHello but
is needed for ECH decryption.
Note that `SSL_CTX_ech_raw_decrypt()` only takes a ClientHello as input. If
the flight containing the ClientHello contains other messages (e.g. a
ChangeCipherSuite or Early data), then the caller is responsible for
disentangling those, and for assembling a new flight containing the inner
ClientHello.
Different encodings
-------------------
ECHConfigList values may be provided via a command line argument to the calling
application or (more likely) have been retrieved from DNS resource records by
the application. ECHConfigList values may be provided in various encodings
(base64 or binary) each of which may suit different applications.
If the input contains more than one (syntactically correct) ECHConfigList, then only
those that contain locally supported options (e.g. AEAD ciphers) will be
returned. If no ECHConfigList found has supported options then none will be
returned and the function will return NULL.
Additional Client Controls
--------------------------
Clients can additionally more directly control the values to be used for inner
and outer SNI and ALPN values via specific APIs. This allows a client to
override the `public_name` present in an ECHConfigList that will otherwise
be used for the outer SNI. The `no_outer` input allows a client to emit an
outer CH with no SNI at all. Providing a `NULL` for the `outer_name` means
to send the `public_name` provided from the ECHConfigList.
```c
int SSL_ech_set1_server_names(SSL *s, const char *inner_name,
const char *outer_name, int no_outer);
int SSL_ech_set1_outer_server_name(SSL *s, const char *outer_name, int no_outer);
int SSL_ech_set1_outer_alpn_protos(SSL *s, const unsigned char *protos,
size_t protos_len);
int SSL_CTX_ech_set1_outer_alpn_protos(SSL_CTX *s, const unsigned char *protos,
size_t protos_len);
```
If a client attempts ECH but that fails, or sends an ECH-GREASEd CH, to
an ECH-supporting server, then that server may return an ECH "retry-config"
value that the client could choose to use in a subsequent connection. The
client can detect this situation via the `SSL_ech_get1_status()` API and
can access the retry config value via:
```c
OSSL_ECHSTORE *SSL_ech_get1_retry_config(SSL *s);
```
GREASEing
---------
"GREASEing" is defined in
[RFC8701](https://datatracker.ietf.org/doc/html/rfc8701) and is a mechanism
intended to discourage protocol ossification that can be used for ECH. GREASEd
ECH may turn out to be important as an initial step towards widespread
deployment of ECH.
If a client wishes to GREASE ECH using a specific HPKE suite or ECH version
(represented by the TLS extension type code-point) then it can set those values
via:
```c
int SSL_ech_set1_grease_suite(SSL *s, const char *suite);
int SSL_ech_set_grease_type(SSL *s, uint16_t type);
```
ECH Status API
--------------
Clients and servers can check the status of ECH processing
on an SSL connection using this API:
```c
int SSL_ech_get1_status(SSL *s, char **inner_sni, char **outer_sni);
/* Return codes from SSL_ech_get1_status */
# define SSL_ECH_STATUS_BACKEND 4 /* ECH back-end: saw an ech_is_inner */
# define SSL_ECH_STATUS_GREASE_ECH 3 /* GREASEd and got an ECH in return */
# define SSL_ECH_STATUS_GREASE 2 /* ECH GREASE happened */
# define SSL_ECH_STATUS_SUCCESS 1 /* Success */
# define SSL_ECH_STATUS_FAILED 0 /* Some internal or protocol error */
# define SSL_ECH_STATUS_BAD_CALL -100 /* Some in/out arguments were NULL */
# define SSL_ECH_STATUS_NOT_TRIED -101 /* ECH wasn't attempted */
# define SSL_ECH_STATUS_BAD_NAME -102 /* ECH ok but server cert bad */
# define SSL_ECH_STATUS_NOT_CONFIGURED -103 /* ECH wasn't configured */
# define SSL_ECH_STATUS_FAILED_ECH -105 /* We tried, failed and got an ECH, from a good name */
# define SSL_ECH_STATUS_FAILED_ECH_BAD_NAME -106 /* We tried, failed and got an ECH, from a bad name */
```
The `inner_sni` and `outer_sni` values should be freed by callers
via `OPENSSL_free()`.
The function returns one of the status values above.
Call-backs and options
----------------------
Clients and servers can set a callback that will be triggered when ECH is
attempted and the result of ECH processing is known. The callback function can
access a string (`str`) that can be used for logging (but not for branching).
Callback functions might typically call `SSL_ech_get1_status()` if branching
is required.
```c
typedef unsigned int (*SSL_ech_cb_func)(SSL *s, const char *str);
void SSL_ech_set_callback(SSL *s, SSL_ech_cb_func f);
void SSL_CTX_ech_set_callback(SSL_CTX *ctx, SSL_ech_cb_func f);
```
The following options are defined for ECH and may be set via
`SSL_set_options()`:
```c
/* set this to tell client to emit greased ECH values when not doing
* "real" ECH */
#define SSL_OP_ECH_GREASE SSL_OP_BIT(36)
/* If this is set then the server side will attempt trial decryption */
/* of ECHs even if there is no matching record_digest. That's a bit */
/* inefficient, but more privacy friendly */
#define SSL_OP_ECH_TRIALDECRYPT SSL_OP_BIT(37)
/* If set, clients will ignore the supplied ECH config_id and replace
* that with a random value */
#define SSL_OP_ECH_IGNORE_CID SSL_OP_BIT(38)
/* If set, servers will add GREASEy ECHConfig values to those sent
* in retry_configs */
#define SSL_OP_ECH_GREASE_RETRY_CONFIG SSL_OP_BIT(39)
```
Build Options
-------------
All ECH code is protected via `#ifndef OPENSSL_NO_ECH` and there is
a `no-ech` option to build without this code.
BoringSSL APIs
--------------
Brief descriptions of BoringSSL APIs are below together with initial comments
comparing those to the above. (It may be useful to consider the extent to
which it is useful to make OpenSSL and BoringSSL APIs resemble one another.)
Just as our implementation is under development, BoringSSL's
`include/openssl/ssl.h` says: "ECH support in BoringSSL is still experimental
and under development."
### GREASE
BoringSSL uses an API to enable GREASEing rather than an option.
```c
OPENSSL_EXPORT void SSL_set_enable_ech_grease(SSL *ssl, int enable);
```
This could work as well for our implementation, or BoringSSL could probably
change to use an option, unless there's some reason to prefer not adding new
options.
### Verifying the outer CH rather than inner
BoringSSL seems to use this API to change the DNS name being verified in order
to validate a `retry_config`.
```c
OPENSSL_EXPORT void SSL_get0_ech_name_override(const SSL *ssl,
const char **out_name,
size_t *out_name_len);
```
I'm not sure how this compares. Need to investigate.
### Create an ECHConfigList
The first function below outputs an ECHConfig, the second adds one of those to
an `SSL_ECH_KEYS` structure, the last emits an ECHConfigList from that
structure. There are other APIs for managing memory for `SSL_ECH_KEYS`
These APIs also expose HPKE to the application via `EVP_HPKE_KEY` which is
defined in `include/openssl/hpke.h`. HPKE handling differs quite a bit from
the HPKE APIs merged to OpenSSL.
```c
OPENSSL_EXPORT int SSL_marshal_ech_config(uint8_t **out, size_t *out_len,
uint8_t config_id,
const EVP_HPKE_KEY *key,
const char *public_name,
size_t max_name_len);
OPENSSL_EXPORT int SSL_ECH_KEYS_add(SSL_ECH_KEYS *keys, int is_retry_config,
const uint8_t *ech_config,
size_t ech_config_len,
const EVP_HPKE_KEY *key);
OPENSSL_EXPORT int SSL_ECH_KEYS_marshal_retry_configs(const SSL_ECH_KEYS *keys,
uint8_t **out,
size_t *out_len);
```
Collectively these are similar to `OSSL_ECH_make_echconfig()`.
### Setting ECH keys on a server
Again using the `SSL_ECH_KEYS` type and APIs, servers can build up a set of
ECH keys using:
```c
OPENSSL_EXPORT int SSL_CTX_set1_ech_keys(SSL_CTX *ctx, SSL_ECH_KEYS *keys);
```
### Getting status
BoringSSL has:
```c
OPENSSL_EXPORT int SSL_ech_accepted(const SSL *ssl);
```
That seems to be a subset of `SSL_ech_get1_status()`.

View file

@ -16,6 +16,7 @@ DEPEND[openssl-dsaparam.pod]=../perlvars.pm
DEPEND[openssl-dsa.pod]=../perlvars.pm
DEPEND[openssl-ecparam.pod]=../perlvars.pm
DEPEND[openssl-ec.pod]=../perlvars.pm
DEPEND[openssl-ech.pod]=../perlvars.pm
DEPEND[openssl-enc.pod]=../perlvars.pm
DEPEND[openssl-engine.pod]=../perlvars.pm
DEPEND[openssl-errstr.pod]=../perlvars.pm

117
doc/man1/openssl-ech.pod.in Normal file
View file

@ -0,0 +1,117 @@
=pod
{- OpenSSL::safe::output_do_not_edit_headers(); -}
=head1 NAME
openssl-ech - ECH key generation
=head1 SYNOPSIS
B<openssl> B<ech>
[B<-help>]
[B<-verbose>]
[B<-in> I<files>]
[B<-out> I<file>]
[B<-public_name> I<name>]
[B<-max_name_len> I<len>]
[B<-suite> I<suite_str>]
[B<-ech_version> I<version>]
[B<-select> I<number>]
[B<-text>]
=head1 DESCRIPTION
The L<openssl-ech(1)> command generates Encrypted Client Hello (ECH) key pairs
in the ECHConfig PEM file format as specified in
L<https://datatracker.ietf.org/doc/draft-farrell-tls-pemesni/>.
TODO(ECH): update I-D reference to RFC when possible.
That format consists of an optional private key in PKCS#8 format and a base64
encoded ECHConfigList containing an entry with a matching public value (and
possibly other entries as well).
=head1 OPTIONS
The following options are supported:
=over 4
=item B<-help>
Print out a usage message.
=item B<-verbose>
Print more verbosely.
=item B<-in>
Provide an input ECH PEM file for printing or merging. Up to five
input files can be provided via use of multiple B<in> arguments.
=item B<-out> I<file>
Name of output ECHConfig PEM file. If a new key pair was generated the output
file will contain the private key and encoded ECHConfigList. If one or more
input files was provided the output file will contain a set of ECHConfigList
values with public keys from the inputs, and no private key(s).
=item B<-text>
Provide human-readable text ouput.
=item B<-public_name> I<name>
The DNS name to use in the "public_name" field of the ECHConfig.
=item B<-max_name_len> I<num>
Maximum name length field value to use in the ECHConfig.
=item B<-suite> I<str>
HPKE suite to use in the ECHConfig.
=item B<-ech_version> I<version>
The ECH version to use in the ECHConfig. Only 0xfe0d is supported in this version.
=item B<-select> I<number>
Select the N-th ECHConfig/public key from the set of input ECH PEM files and output
that.
=back
=head1 NOTES
Ciphersuites are specified using a comma-separated list of IANA-registered
codes/numbers e.g. "-c 0x20,1,3" or a comma-separated list of strings from:
- KEMs: p256, p384, p521, x25519, x448
- KDFs: hkdf-sha256, hkdf-sha384, hkdf-sha512
- AEADs: aes128gcm, aes256gcm, chachapoly1305
For example the default is: x25519, hkdf-sha256, aes128gcm
See L<OSSL_HPKE_CTX_new(3)> for details.
=head1 SEE ALSO
L<openssl(1)>,
L<openssl-s_client(1)>,
L<openssl-s_server(1)>,
L<SSL_set1_echstore(3)>
=head1 HISTORY
This functionality described here was added in OpenSSL 3.5.
=head1 COPYRIGHT
Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
in the file LICENSE in the source distribution or at
L<https://www.openssl.org/source/license.html>.
=cut

View file

@ -121,6 +121,10 @@ L<openssl-genpkey(1)> and L<openssl-pkeyparam(1)>.
EC (Elliptic curve) key processing.
=item B<ech>
Encrypted Client Hello (ECH) admin. See L<openssl-ech(1)>.
=item B<ecparam>
EC parameter manipulation and generation.

View file

@ -362,6 +362,30 @@ only understands up to SSLv3. In this case the client must still use the
same SSLv3.1=TLSv1 announcement. Some clients step down to SSLv3 with respect
to the server's answer and violate the version rollback protection.)
=item SSL_OP_ECH_GREASE
If set, TLS ClientHello messages emitted by the client will include GREASE
Encrypted ClientHello (ECH) extension values, if ECH is not really being
attempted.
=item SSL_OP_ECH_TRIALDECRYPT
If set, servers will attempt to decrypt ECH extensions using all loaded
ECH key pairs. By default, servers will only attempt decryption using
an ECH key pair that matches the config_id in the ECH extension value
received.
=item SSL_OP_ECH_GREASE_RETRY_CONFIG
If set, servers will add GREASEy ECHConfig values to those sent to the
client after the client GREASEd or the client tried and failed to use
ECH.
=item SSL_OP_ECH_IGNORED_CID
If set, TLS ClientHello messages emitted by the client will ignore the
ECHConfig config_id chosen by the server and use a random octet.
=back
The following options no longer have any effect but their identifiers are

View file

@ -0,0 +1,201 @@
=pod
=head1 NAME
SSL_set1_echstore,
OSSL_ECHSTORE_new, OSSL_ECHSTORE_free,
OSSL_ECHSTORE_new_config, OSSL_ECHSTORE_write_pem,
OSSL_ECHSTORE_read_echconfiglist, OSSL_ECHSTORE_get1_info,
OSSL_ECHSTORE_downselect, OSSL_ECHSTORE_set1_key_and_read_pem,
OSSL_ECHSTORE_read_pem, OSSL_ECHSTORE_num_entries,
OSSL_ECHSTORE_num_keys, OSSL_ECHSTORE_flush_keys,
SSL_CTX_set1_echstore,
SSL_CTX_get1_echstore, SSL_get1_echstore, SSL_ech_set1_server_names,
SSL_ech_set1_outer_server_name, SSL_ech_set1_outer_alpn_protos,
SSL_ech_get1_status, SSL_ech_set1_grease_suite, SSL_ech_set_grease_type,
SSL_ech_set_callback, SSL_ech_get1_retry_config,
SSL_CTX_ech_set1_outer_alpn_protos, SSL_CTX_ech_raw_decrypt,
SSL_CTX_ech_set_callback,SSL_set1_ech_config_list
- Encrypted Client Hello (ECH) functions
=head1 SYNOPSIS
#include <openssl/ech.h>
OSSL_ECHSTORE *OSSL_ECHSTORE_new(OSSL_LIB_CTX *libctx, const char *propq);
void OSSL_ECHSTORE_free(OSSL_ECHSTORE *es);
int OSSL_ECHSTORE_new_config(OSSL_ECHSTORE *es,
uint16_t echversion, uint16_t max_name_length,
const char *public_name, OSSL_HPKE_SUITE suite);
int OSSL_ECHSTORE_write_pem(OSSL_ECHSTORE *es, int index, BIO *out);
int OSSL_ECHSTORE_read_echconfiglist(OSSL_ECHSTORE *es, BIO *in);
int OSSL_ECHSTORE_get1_info(OSSL_ECHSTORE *es, int index, time_t *loaded_secs,
char **public_name, char **echconfig,
int *has_private, int *for_retry);
int OSSL_ECHSTORE_downselect(OSSL_ECHSTORE *es, int index);
int OSSL_ECHSTORE_set1_key_and_read_pem(OSSL_ECHSTORE *es, EVP_PKEY *priv,
BIO *in, int for_retry);
int OSSL_ECHSTORE_read_pem(OSSL_ECHSTORE *es, BIO *in, int for_retry);
int OSSL_ECHSTORE_num_entries(OSSL_ECHSTORE *es, int *numentries);
int OSSL_ECHSTORE_num_keys(OSSL_ECHSTORE *es, int *numkeys);
int OSSL_ECHSTORE_flush_keys(OSSL_ECHSTORE *es, time_t age);
int SSL_CTX_set1_echstore(SSL_CTX *ctx, OSSL_ECHSTORE *es);
int SSL_set1_echstore(SSL *s, OSSL_ECHSTORE *es);
OSSL_ECHSTORE *SSL_CTX_get1_echstore(const SSL_CTX *ctx);
OSSL_ECHSTORE *SSL_get1_echstore(const SSL *s);
int SSL_ech_set1_server_names(SSL *s, const char *inner_name,
const char *outer_name, int no_outer);
int SSL_ech_set1_outer_server_name(SSL *s, const char *outer_name, int no_outer);
int SSL_ech_set1_outer_alpn_protos(SSL *s, const unsigned char *protos,
const size_t protos_len);
int SSL_ech_get1_status(SSL *s, char **inner_sni, char **outer_sni);
int SSL_ech_set1_grease_suite(SSL *s, const char *suite);
int SSL_ech_set_grease_type(SSL *s, uint16_t type);
void SSL_ech_set_callback(SSL *s, SSL_ech_cb_func f);
int SSL_ech_get1_retry_config(SSL *s, unsigned char **ec, size_t *eclen);
int SSL_CTX_ech_raw_decrypt(SSL_CTX *ctx,
int *decrypted_ok,
char **inner_sni, char **outer_sni,
unsigned char *outer_ch, size_t outer_len,
unsigned char *inner_ch, size_t *inner_len,
unsigned char **hrrtok, size_t *toklen);
void SSL_CTX_ech_set_callback(SSL_CTX *ctx, SSL_ech_cb_func f);
int SSL_CTX_ech_set1_outer_alpn_protos(SSL_CTX *ctx,
const unsigned char *protos,
const size_t protos_len);
int SSL_set1_ech_config_list(SSL *ssl, const uint8_t *ecl, size_t ecl_len);
=head1 DESCRIPTION
TODO(ECH): Complete this text...
The Encrypted Client Hello (ECH) APIs described here are built around
the concept of an `OSSL_ECHSTORE` which contains ECH configuration
information relevant for the current 'SSL_CTX' or 'SSL' connection.
Mention SSL_set1_echstore() is a thing
Mention OSSL_ECHSTORE_new() is a thing
Mention OSSL_ECHSTORE_free() is a thing
Mention OSSL_ECHSTORE_new_config() is a thing
Mention OSSL_ECHSTORE_write_pem() is a thing
Mention OSSL_ECHSTORE_read_echconfiglist() is a thing
Mention OSSL_ECHSTORE_get1_info() is a thing
Mention OSSL_ECHSTORE_downselect() is a thing
Mention OSSL_ECHSTORE_set1_key_and_read_pem() is a thing
Mention OSSL_ECHSTORE_read_pem() is a thing
Mention OSSL_ECHSTORE_num_keys() is a thing
Mention OSSL_ECHSTORE_num_entries() is a thing
Mention OSSL_ECHSTORE_flush_keys() is a thing
Mention SSL_CTX_set1_echstore() is a thing
Mention SSL_CTX_get1_echstore() is a thing
Mention SSL_get1_echstore() is a thing
Mention SSL_ech_set1_server_names() is a thing
Mention SSL_ech_set1_outer_server_name() is a thing
Mention SSL_ech_set1_outer_alpn_protos() is a thing
Mention SSL_ech_get1_status() is a thing
Mention SSL_ech_set1_grease_suite() is a thing
Mention SSL_ech_set_grease_type() is a thing
Mention SSL_ech_set_callback() is a thing
Mention SSL_ech_get1_retry_config() is a thing
Mention SSL_CTX_ech1_set_outer_alpn_protos() is a thing
Mention SSL_CTX_ech_raw_decrypt() is a thing
Mention SSL_CTX_ech_set_callback() is a thing
Mention SSL_set1_ech_config_list() is a thing
=head2 Callback Function
Applications can set a callback function that will be called when the
outcome from an attempt at ECH has been determined. On the server,
that happens early, as part of construction of the ServerHello message.
On the client, the callback will happen after the SeverHello has
been processed. In the event of HelloRetryRequest, the callback will
only be triggered when processing the second ServerHello. The callback
function will be triggered even if the client is only GREASEing.
The callback function prototype is:
typedef unsigned int (*SSL_ech_cb_func)(SSL *s, const char *str);
To set a callback function use SSL_ech_set_callback() or
SSL_CTX_ech_set_callback() - the I<f> input should match the
above prototype.
When the callback function is called, the I<str> will point at a string
intended for logging describing the state of ECH processing.
Applications should not attempt to parse that string as the value depends
on compile time settings, local configuration and the specific processing
that happened prior to the callback. Applications that need to branch based
on the outcome of ECH processing should instead make a call to
SSL_ech_get1_status() from within their callback function.
An example string I<str> as seen on a client might be:
ech_attempted=1
ech_attempted_type=0xfe0d
ech_atttempted_cid=0x5d
ech_done=1
ech_grease=0
ech_returned_len=0
ech_backend=0
ech_success=1
2 ECHConfig values loaded
cfg(0): [fe0d,5d,cover.defo.ie,0020,[0001,0001],190984309c1a24cb944c005eb79d9c72ca9a4a979194b553dfd0bffc6b5c152d,00,00]
cfg(1): [fe0d,fd,cover.defo.ie,0020,[0001,0001],46dd4e2c81bb15ef9d194c99b86983844e2a1387e4fb7e7d3b8d368c8e1b4d2a,00,00]
=head1 RETURN VALUES
All functions named here return one on success and zero on error.
SSL_set1_echstore() returns zero on error
SSL_set1_ech_config_list() returns zero on error
OSSL_ECHSTORE_new() returns zero on error
OSSL_ECHSTORE_free() returns zero on error
OSSL_ECHSTORE_new_config() returns zero on error
OSSL_ECHSTORE_write_pem() returns zero on error
OSSL_ECHSTORE_read_echconfiglist() returns zero on error
OSSL_ECHSTORE_get1_info() returns zero on error
OSSL_ECHSTORE_downselect() returns zero on error
OSSL_ECHSTORE_set1_key_and_read_pem() returns zero on error
OSSL_ECHSTORE_read_pem() returns zero on error
OSSL_ECHSTORE_num_keys() returns zero on error
OSSL_ECHSTORE_num_entries() returns zero on error
OSSL_ECHSTORE_flush_keys() returns zero on error
SSL_CTX_set1_echstore() returns zero on error
SSL_CTX_get1_echstore() returns zero on error
SSL_get1_echstore() returns zero on error
SSL_ech_set_server_names() returns zero on error
SSL_ech_set_outer_server_name() returns zero on error
SSL_ech_set_outer_alpn_protos() returns zero on error
SSL_ech_get1_status() returns zero on error
SSL_ech_set_grease_suite() returns zero on error
SSL_ech_set_grease_type() returns zero on error
SSL_ech_set_callback() returns zero on error
SSL_ech_get_retry_config() returns zero on error
SSL_CTX_ech_set1_outer_alpn_protos() returns zero on error
SSL_CTX_ech_raw_decrypt() returns zero on error
SSL_CTX_ech_set_callback() returns zero on error
Note that SSL_CTX_ech_set1_outer_alpn_protos() and
SSL_ech_set1_outer_alpn_protos() return zero on error and 1 on success.
This is in contrast to SSL_CTX_set1_alpn_protos() and SSL_set1_alpn_protos()
which (unusually for OpenSSL) return 0 on success and 1 on error.
=head1 SEE ALSO
The Encrypted ClientHello specification: L<https://datatracker.ietf.org/doc/draft-ietf-tls-esni/>
TODO(ECH) update link to RFC.
=head1 HISTORY
The functionality described here was added in OpenSSL 3.5.
=head1 COPYRIGHT
Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
in the file LICENSE in the source distribution or at
L<https://www.openssl.org/source/license.html>.
=cut

133
include/openssl/ech.h Normal file
View file

@ -0,0 +1,133 @@
/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* Externally-visible data structures and prototypes for handling
* Encrypted ClientHello (ECH).
*/
#ifndef OPENSSL_ECH_H
# define OPENSSL_ECH_H
# pragma once
# include <openssl/ssl.h>
# include <openssl/hpke.h>
# ifndef OPENSSL_NO_ECH
/*
* Some externally visible limits - most used for sanity checks that could be
* bigger if needed, but that work for now
*/
# define OSSL_ECH_MAX_PAYLOAD_LEN 1500 /* max ECH ciphertext to en/decode */
# define OSSL_ECH_MIN_ECHCONFIG_LEN 32 /* min for all encodings */
# define OSSL_ECH_MAX_ECHCONFIG_LEN 1500 /* max for all encodings */
# define OSSL_ECH_MAX_ECHCONFIGEXT_LEN 512 /* ECHConfig extension max */
# define OSSL_ECH_MAX_MAXNAMELEN 255 /* ECHConfig max for max name length */
# define OSSL_ECH_MAX_PUBLICNAME 255 /* max ECHConfig public name length */
# define OSSL_ECH_MAX_ALPNLEN 255 /* max alpn length */
# define OSSL_ECH_OUTERS_MAX 20 /* max extensions we compress via outer-exts */
# define OSSL_ECH_ALLEXTS_MAX 32 /* max total number of extension we allow */
/*
* ECH version. We only support RFC XXXX as of now. As/if new ECHConfig
* versions are added, those will be noted here.
* TODO(ECH): Replace XXXX with the actual RFC number once known.
*/
# define OSSL_ECH_RFCXXXX_VERSION 0xfe0d /* official ECHConfig version */
/* latest version from an RFC */
# define OSSL_ECH_CURRENT_VERSION OSSL_ECH_RFCXXXX_VERSION
/* Return codes from SSL_ech_get1_status */
# define SSL_ECH_STATUS_BACKEND 4 /* ECH backend: saw an ech_is_inner */
# define SSL_ECH_STATUS_GREASE_ECH 3 /* GREASEd and got an ECH in return */
# define SSL_ECH_STATUS_GREASE 2 /* ECH GREASE happened */
# define SSL_ECH_STATUS_SUCCESS 1 /* Success */
# define SSL_ECH_STATUS_FAILED 0 /* Some internal or protocol error */
# define SSL_ECH_STATUS_BAD_CALL -100 /* Some in/out arguments were NULL */
# define SSL_ECH_STATUS_NOT_TRIED -101 /* ECH wasn't attempted */
# define SSL_ECH_STATUS_BAD_NAME -102 /* ECH ok but server cert bad */
# define SSL_ECH_STATUS_NOT_CONFIGURED -103 /* ECH wasn't configured */
# define SSL_ECH_STATUS_FAILED_ECH -105 /* Tried, failed, got an ECH, from a good name */
# define SSL_ECH_STATUS_FAILED_ECH_BAD_NAME -106 /* Tried, failed, got an ECH, from a bad name */
/* if a caller wants to index the last entry in the store */
# define OSSL_ECHSTORE_LAST -1
/* if a caller wants all entries in the store, e.g. to print public values */
# define OSSL_ECHSTORE_ALL -2
/* Values for the for_retry inputs */
# define OSSL_ECH_FOR_RETRY 1
# define OSSL_ECH_NO_RETRY 0
/*
* API calls built around OSSL_ECHSTORE
*/
OSSL_ECHSTORE *OSSL_ECHSTORE_new(OSSL_LIB_CTX *libctx, const char *propq);
void OSSL_ECHSTORE_free(OSSL_ECHSTORE *es);
int OSSL_ECHSTORE_new_config(OSSL_ECHSTORE *es,
uint16_t echversion, uint8_t max_name_length,
const char *public_name, OSSL_HPKE_SUITE suite);
int OSSL_ECHSTORE_write_pem(OSSL_ECHSTORE *es, int index, BIO *out);
int OSSL_ECHSTORE_read_echconfiglist(OSSL_ECHSTORE *es, BIO *in);
int OSSL_ECHSTORE_get1_info(OSSL_ECHSTORE *es, int index, time_t *loaded_secs,
char **public_name, char **echconfig,
int *has_private, int *for_retry);
int OSSL_ECHSTORE_downselect(OSSL_ECHSTORE *es, int index);
int OSSL_ECHSTORE_set1_key_and_read_pem(OSSL_ECHSTORE *es, EVP_PKEY *priv,
BIO *in, int for_retry);
int OSSL_ECHSTORE_read_pem(OSSL_ECHSTORE *es, BIO *in, int for_retry);
int OSSL_ECHSTORE_num_entries(const OSSL_ECHSTORE *es, int *numentries);
int OSSL_ECHSTORE_num_keys(OSSL_ECHSTORE *es, int *numkeys);
int OSSL_ECHSTORE_flush_keys(OSSL_ECHSTORE *es, time_t age);
/*
* APIs relating OSSL_ECHSTORE to SSL/SSL_CTX
*/
int SSL_CTX_set1_echstore(SSL_CTX *ctx, OSSL_ECHSTORE *es);
int SSL_set1_echstore(SSL *s, OSSL_ECHSTORE *es);
OSSL_ECHSTORE *SSL_CTX_get1_echstore(const SSL_CTX *ctx);
OSSL_ECHSTORE *SSL_get1_echstore(const SSL *s);
int SSL_ech_set1_server_names(SSL *s, const char *inner_name,
const char *outer_name, int no_outer);
int SSL_ech_set1_outer_server_name(SSL *s, const char *outer_name, int no_outer);
/*
* Note that this function returns 1 for success and 0 for error. This
* contrasts with SSL_set1_alpn_protos() which (unusually for OpenSSL)
* returns 0 for success and 1 on error.
*/
int SSL_ech_set1_outer_alpn_protos(SSL *s, const unsigned char *protos,
const size_t protos_len);
int SSL_ech_get1_status(SSL *s, char **inner_sni, char **outer_sni);
int SSL_ech_set1_grease_suite(SSL *s, const char *suite);
int SSL_ech_set_grease_type(SSL *s, uint16_t type);
typedef unsigned int (*SSL_ech_cb_func)(SSL *s, const char *str);
void SSL_ech_set_callback(SSL *s, SSL_ech_cb_func f);
int SSL_ech_get1_retry_config(SSL *s, unsigned char **ec, size_t *eclen);
/*
* Note that this function returns 1 for success and 0 for error. This
* contrasts with SSL_set1_alpn_protos() which (unusually for OpenSSL)
* returns 0 for success and 1 on error.
*/
int SSL_CTX_ech_set1_outer_alpn_protos(SSL_CTX *s, const unsigned char *protos,
const size_t protos_len);
int SSL_CTX_ech_raw_decrypt(SSL_CTX *ctx,
int *decrypted_ok,
char **inner_sni, char **outer_sni,
unsigned char *outer_ch, size_t outer_len,
unsigned char *inner_ch, size_t *inner_len,
unsigned char **hrrtok, size_t *toklen);
void SSL_CTX_ech_set_callback(SSL_CTX *ctx, SSL_ech_cb_func f);
int SSL_set1_ech_config_list(SSL *ssl, const uint8_t *ecl, size_t ecl_len);
# endif
#endif

View file

@ -59,6 +59,7 @@ extern "C" {
# define PEM_STRING_CMS "CMS"
# define PEM_STRING_SM2PARAMETERS "SM2 PARAMETERS"
# define PEM_STRING_ACERT "ATTRIBUTE CERTIFICATE"
# define PEM_STRING_ECHCONFIG "ECHCONFIG"
# define PEM_TYPE_ENCRYPTED 10
# define PEM_TYPE_MIC_ONLY 20

View file

@ -44,6 +44,9 @@ use OpenSSL::stackhash qw(generate_stack_macros generate_const_stack_macros);
# include <openssl/ct.h>
# include <openssl/sslerr.h>
# include <openssl/prov_ssl.h>
# ifndef OPENSSL_NO_ECH
# include <openssl/ech.h>
# endif
# ifndef OPENSSL_NO_STDIO
# include <stdio.h>
# endif
@ -426,6 +429,28 @@ typedef int (*SSL_async_callback_fn)(SSL *s, void *arg);
#define SSL_OP_PREFER_NO_DHE_KEX SSL_OP_BIT(35)
#ifndef OPENSSL_NO_ECH
/* Set this to tell client to emit greased ECH values */
# define SSL_OP_ECH_GREASE SSL_OP_BIT(36)
/*
* If this is set then the server side will attempt trial decryption
* of ECHs even if there is no matching ECH config_id. That's a bit
* inefficient, but more privacy friendly.
*/
# define SSL_OP_ECH_TRIALDECRYPT SSL_OP_BIT(37)
/*
* If set, clients will ignore the supplied ECH config_id and replace
* that with a random value.
*/
# define SSL_OP_ECH_IGNORE_CID SSL_OP_BIT(38)
/*
* If set, servers will add GREASEy ECHConfig values to those sent
* in retry_configs.
*/
# define SSL_OP_ECH_GREASE_RETRY_CONFIG SSL_OP_BIT(39)
#endif
/*
* Option "collections."
*/
@ -1191,6 +1216,9 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
# define SSL_AD_NO_RENEGOTIATION TLS1_AD_NO_RENEGOTIATION
# define SSL_AD_MISSING_EXTENSION TLS13_AD_MISSING_EXTENSION
# define SSL_AD_CERTIFICATE_REQUIRED TLS13_AD_CERTIFICATE_REQUIRED
# ifndef OPENSSL_NO_ECH
# define SSL_AD_ECH_REQUIRED TLS1_AD_ECH_REQUIRED
# endif
# define SSL_AD_UNSUPPORTED_EXTENSION TLS1_AD_UNSUPPORTED_EXTENSION
# define SSL_AD_CERTIFICATE_UNOBTAINABLE TLS1_AD_CERTIFICATE_UNOBTAINABLE
# define SSL_AD_UNRECOGNIZED_NAME TLS1_AD_UNRECOGNIZED_NAME

View file

@ -36,6 +36,7 @@
# define SSL_R_BAD_DIGEST_LENGTH 111
# define SSL_R_BAD_EARLY_DATA 233
# define SSL_R_BAD_ECC_CERT 304
# define SSL_R_BAD_ECHCONFIG_EXTENSION 422
# define SSL_R_BAD_ECPOINT 306
# define SSL_R_BAD_EXTENSION 110
# define SSL_R_BAD_HANDSHAKE_LENGTH 332
@ -112,6 +113,8 @@
# define SSL_R_DUPLICATE_COMPRESSION_ID 309
# define SSL_R_ECC_CERT_NOT_FOR_SIGNING 318
# define SSL_R_ECDH_REQUIRED_FOR_SUITEB_MODE 374
# define SSL_R_ECH_DECODE_ERROR 423
# define SSL_R_ECH_REQUIRED 421
# define SSL_R_EE_KEY_TOO_SMALL 399
# define SSL_R_EMPTY_RAW_PUBLIC_KEY 349
# define SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST 354

View file

@ -78,6 +78,9 @@ extern "C" {
# define TLS1_AD_BAD_CERTIFICATE_HASH_VALUE 114
# define TLS1_AD_UNKNOWN_PSK_IDENTITY 115/* fatal */
# define TLS1_AD_NO_APPLICATION_PROTOCOL 120 /* fatal */
# ifndef OPENSSL_NO_ECH
# define TLS1_AD_ECH_REQUIRED 121 /* fatal */
# endif
/* ExtensionType values from RFC3546 / RFC4366 / RFC6066 */
# define TLSEXT_TYPE_server_name 0
@ -168,6 +171,11 @@ extern "C" {
# define TLSEXT_TYPE_next_proto_neg 13172
# endif
# ifndef OPENSSL_NO_ECH
# define TLSEXT_TYPE_ech 0xfe0d
# define TLSEXT_TYPE_outer_extensions 0xfd00
# endif
/* NameType value from RFC3546 */
# define TLSEXT_NAMETYPE_host_name 0
/* status request value from RFC3546 */

View file

@ -238,6 +238,12 @@ typedef struct ossl_decoder_ctx_st OSSL_DECODER_CTX;
typedef struct ossl_self_test_st OSSL_SELF_TEST;
#ifndef OPENSSL_NO_ECH
/* opaque type for ECH related information */
typedef struct ossl_echstore_st OSSL_ECHSTORE;
#endif
#ifdef __cplusplus
}
#endif

View file

@ -6,6 +6,10 @@ IF[{- !$disabled{quic} -}]
SUBDIRS=quic
ENDIF
IF[{- !$disabled{ech} -}]
SUBDIRS=ech
ENDIF
SOURCE[../libssl]=\
pqueue.c \
statem/statem_srvr.c statem/statem_clnt.c s3_lib.c s3_enc.c \

3
ssl/ech/build.info Normal file
View file

@ -0,0 +1,3 @@
$LIBSSL=../../libssl
SOURCE[$LIBSSL]=ech_ssl_apis.c ech_store.c ech_internal.c ech_helper.c

15
ssl/ech/ech_helper.c Normal file
View file

@ -0,0 +1,15 @@
/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/ssl.h>
#include <openssl/ech.h>
#include "../ssl_local.h"
#include "ech_local.h"
/* TODO(ECH): move code that's used by internals and test here */

163
ssl/ech/ech_internal.c Normal file
View file

@ -0,0 +1,163 @@
/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/ssl.h>
#include <openssl/ech.h>
#include "../ssl_local.h"
#include "ech_local.h"
#ifndef OPENSSL_NO_ECH
/* ECH internal API functions */
static OSSL_ECHSTORE_ENTRY *ossl_echstore_entry_dup(const OSSL_ECHSTORE_ENTRY *orig)
{
OSSL_ECHSTORE_ENTRY *ret = NULL;
if (orig == NULL)
return NULL;
ret = OPENSSL_zalloc(sizeof(*ret));
if (ret == NULL)
return NULL;
ret->version = orig->version;
if (orig->public_name != NULL) {
ret->public_name = OPENSSL_strdup(orig->public_name);
if (ret->public_name == NULL)
goto err;
}
ret->pub_len = orig->pub_len;
if (orig->pub != NULL) {
ret->pub = OPENSSL_memdup(orig->pub, orig->pub_len);
if (ret->pub == NULL)
goto err;
}
ret->nsuites = orig->nsuites;
ret->suites = OPENSSL_memdup(orig->suites, sizeof(OSSL_HPKE_SUITE) * ret->nsuites);
if (ret->suites == NULL)
goto err;
ret->max_name_length = orig->max_name_length;
ret->config_id = orig->config_id;
if (orig->exts != NULL) {
ret->exts = sk_OSSL_ECHEXT_deep_copy(orig->exts, ossl_echext_dup,
ossl_echext_free);
if (ret->exts == NULL)
goto err;
}
ret->loadtime = orig->loadtime;
if (orig->keyshare != NULL) {
if (!EVP_PKEY_up_ref(orig->keyshare))
goto err;
ret->keyshare = orig->keyshare;
}
ret->for_retry = orig->for_retry;
if (orig->encoded != NULL) {
ret->encoded_len = orig->encoded_len;
ret->encoded = OPENSSL_memdup(orig->encoded, ret->encoded_len);
if (ret->encoded == NULL)
goto err;
}
return ret;
err:
ossl_echstore_entry_free(ret);
return NULL;
}
/* duplicate an OSSL_ECHSTORE as needed */
OSSL_ECHSTORE *ossl_echstore_dup(const OSSL_ECHSTORE *old)
{
OSSL_ECHSTORE *cp = NULL;
if (old == NULL)
return NULL;
cp = OPENSSL_zalloc(sizeof(*cp));
if (cp == NULL)
return NULL;
cp->libctx = old->libctx;
if (old->propq != NULL) {
cp->propq = OPENSSL_strdup(old->propq);
if (cp->propq == NULL)
goto err;
}
if (old->entries != NULL) {
cp->entries = sk_OSSL_ECHSTORE_ENTRY_deep_copy(old->entries,
ossl_echstore_entry_dup,
ossl_echstore_entry_free);
if (cp->entries == NULL)
goto err;
}
return cp;
err:
OSSL_ECHSTORE_free(cp);
return NULL;
}
void ossl_ech_ctx_clear(OSSL_ECH_CTX *ce)
{
if (ce == NULL)
return;
OSSL_ECHSTORE_free(ce->es);
OPENSSL_free(ce->alpn_outer);
return;
}
void ossl_ech_conn_clear(OSSL_ECH_CONN *ec)
{
if (ec == NULL)
return;
OSSL_ECHSTORE_free(ec->es);
OPENSSL_free(ec->outer_hostname);
OPENSSL_free(ec->alpn_outer);
OPENSSL_free(ec->former_inner);
OPENSSL_free(ec->innerch);
OPENSSL_free(ec->encoded_innerch);
OPENSSL_free(ec->innerch1);
OPENSSL_free(ec->kepthrr);
OPENSSL_free(ec->grease_suite);
OPENSSL_free(ec->sent);
OPENSSL_free(ec->returned);
OPENSSL_free(ec->pub);
OSSL_HPKE_CTX_free(ec->hpke_ctx);
EVP_PKEY_free(ec->tmp_pkey);
return;
}
/* called from ssl/ssl_lib.c: ossl_ssl_connection_new_int */
int ossl_ech_conn_init(SSL_CONNECTION *s, SSL_CTX *ctx,
const SSL_METHOD *method)
{
memset(&s->ext.ech, 0, sizeof(s->ext.ech));
if (ctx->ext.ech.es != NULL
&& (s->ext.ech.es = ossl_echstore_dup(ctx->ext.ech.es)) == NULL)
goto err;
s->ext.ech.cb = ctx->ext.ech.cb;
if (ctx->ext.ech.alpn_outer != NULL) {
s->ext.ech.alpn_outer = OPENSSL_memdup(ctx->ext.ech.alpn_outer,
ctx->ext.ech.alpn_outer_len);
if (s->ext.ech.alpn_outer == NULL)
goto err;
s->ext.ech.alpn_outer_len = ctx->ext.ech.alpn_outer_len;
}
/* initialise type/cid to unknown */
s->ext.ech.attempted_type = OSSL_ECH_type_unknown;
s->ext.ech.attempted_cid = OSSL_ECH_config_id_unset;
if (s->ext.ech.es != NULL)
s->ext.ech.attempted = 1;
if (ctx->options & SSL_OP_ECH_GREASE)
s->options |= SSL_OP_ECH_GREASE;
return 1;
err:
OSSL_ECHSTORE_free(s->ext.ech.es);
s->ext.ech.es = NULL;
OPENSSL_free(s->ext.ech.alpn_outer);
s->ext.ech.alpn_outer = NULL;
s->ext.ech.alpn_outer_len = 0;
return 0;
}
#endif

222
ssl/ech/ech_local.h Normal file
View file

@ -0,0 +1,222 @@
/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* Internal data structures and prototypes for handling
* Encrypted ClientHello (ECH)
*/
#ifndef OPENSSL_NO_ECH
# ifndef HEADER_ECH_LOCAL_H
# define HEADER_ECH_LOCAL_H
# include <openssl/ssl.h>
# include <openssl/ech.h>
# include <openssl/hpke.h>
/*
* Define this to get loads more lines of tracing which is
* very useful for interop.
* This needs tracing enabled at build time, e.g.:
* $ ./config enable-ssl-trace enable-trace
* This added tracing will finally (mostly) disappear once the ECH RFC
* has issued, but is very useful for interop testing so some of it might
* be retained.
*/
# define OSSL_ECH_SUPERVERBOSE
/* values for s->ext.ech.grease */
# define OSSL_ECH_GREASE_UNKNOWN -1 /* when we're not yet sure */
# define OSSL_ECH_NOT_GREASE 0 /* when decryption worked */
# define OSSL_ECH_IS_GREASE 1 /* when decryption failed or GREASE wanted */
/* value for uninitialised ECH version */
# define OSSL_ECH_type_unknown 0xffff
/* value for not yet set ECH config_id */
# define OSSL_ECH_config_id_unset -1
# define OSSL_ECH_CIPHER_LEN 4 /* ECHCipher length (2 for kdf, 2 for aead) */
/*
* Reminder of what goes in DNS for ECH RFC XXXX
*
* opaque HpkePublicKey<1..2^16-1>;
* uint16 HpkeKemId; // Defined in I-D.irtf-cfrg-hpke
* uint16 HpkeKdfId; // Defined in I-D.irtf-cfrg-hpke
* uint16 HpkeAeadId; // Defined in I-D.irtf-cfrg-hpke
* struct {
* HpkeKdfId kdf_id;
* HpkeAeadId aead_id;
* } HpkeSymmetricCipherSuite;
* struct {
* uint8 config_id;
* HpkeKemId kem_id;
* HpkePublicKey public_key;
* HpkeSymmetricCipherSuite cipher_suites<4..2^16-4>;
* } HpkeKeyConfig;
* struct {
* HpkeKeyConfig key_config;
* uint8 maximum_name_length;
* opaque public_name<1..255>;
* Extension extensions<0..2^16-1>;
* } ECHConfigContents;
* struct {
* uint16 version;
* uint16 length;
* select (ECHConfig.version) {
* case 0xfe0d: ECHConfigContents contents;
* }
* } ECHConfig;
* ECHConfig ECHConfigList<1..2^16-1>;
*/
typedef struct ossl_echext_st {
uint16_t type;
uint16_t len;
unsigned char *val;
} OSSL_ECHEXT;
DEFINE_STACK_OF(OSSL_ECHEXT)
typedef struct ossl_echstore_entry_st {
uint16_t version; /* 0xff0d for draft-13 */
char *public_name;
size_t pub_len;
unsigned char *pub;
unsigned int nsuites;
OSSL_HPKE_SUITE *suites;
uint8_t max_name_length;
uint8_t config_id;
STACK_OF(OSSL_ECHEXT) *exts;
time_t loadtime; /* time public and private key were loaded from file */
EVP_PKEY *keyshare; /* long(ish) term ECH private keyshare on a server */
int for_retry; /* whether to use this ECHConfigList in a retry */
size_t encoded_len; /* length of overall encoded content */
unsigned char *encoded; /* overall encoded content */
} OSSL_ECHSTORE_ENTRY;
DEFINE_STACK_OF(OSSL_ECHSTORE_ENTRY)
struct ossl_echstore_st {
STACK_OF(OSSL_ECHSTORE_ENTRY) *entries;
OSSL_LIB_CTX *libctx;
char *propq;
};
/* ECH details associated with an SSL_CTX */
typedef struct ossl_ech_ctx_st {
/* TODO(ECH): consider making es ref-counted */
OSSL_ECHSTORE *es;
unsigned char *alpn_outer;
size_t alpn_outer_len;
SSL_ech_cb_func cb; /* callback function for when ECH "done" */
} OSSL_ECH_CTX;
/* ECH details associated with an SSL_CONNECTION */
typedef struct ossl_ech_conn_st {
/* TODO(ECH): consider making es ref-counted */
OSSL_ECHSTORE *es; /* ECHConfigList details */
int no_outer; /* set to 1 if we should send no outer SNI at all */
char *outer_hostname;
unsigned char *alpn_outer;
size_t alpn_outer_len;
SSL_ech_cb_func cb; /* callback function for when ECH "done" */
/*
* If ECH fails, then we switch to verifying the cert for the
* outer_hostname, meanwhile we still want to be able to trace
* the value we tried as the inner SNI for debug purposes
*/
char *former_inner;
/*
* TODO(ECH): The next 4 buffers (and lengths) may change later
* if a better way to handle the mutiple transcripts needed is
* suggested/invented. I'd suggest we review these when that code
* is part of a PR (which won't be for a few PR's yet.)
*/
/*
* encoded inner ClientHello before/after ECH compression, which`
* is nitty/complex (to avoid repeating the same extenstion value
* in outer and inner, thus saving bandwidth) but (re-)calculating
* the compression is a pain, so we'll store those as we make them
*/
unsigned char *innerch; /* before compression */
size_t innerch_len;
unsigned char *encoded_innerch; /* after compression */
size_t encoded_innerch_len;
/*
* in case of HRR, we need to record the 1st inner client hello, and
* the first server hello (aka the HRR) so we can independently
* generate the transcript and accept confirmation when making the
* 2nd server hello
*/
unsigned char *innerch1;
size_t innerch1_len;
unsigned char *kepthrr;
size_t kepthrr_len;
/*
* Extensions are "outer-only" if the value is only sent in the
* outer CH and only the type is sent in the inner CH.
* We use this array to keep track of the extension types that
* have values only in the outer CH
* Currently, this is basically controlled at compile time, but
* in a way that could be varied, or, in future, put under
* run-time control, so having this isn't so much an overhead.
*/
uint16_t outer_only[OSSL_ECH_OUTERS_MAX];
size_t n_outer_only; /* the number of outer_only extensions so far */
/*
* Index of the current extension's entry in ext_defs - this is
* to avoid the need to change a couple of extension APIs.
* TODO(ECH): check if there's another way to get that value
*/
size_t ext_ind;
/* ECH status vars */
int ch_depth; /* set during CH creation, 0: doing outer, 1: doing inner */
int attempted; /* 1 if ECH was or is being attempted, 0 otherwise */
int done; /* 1 if we've finished ECH calculations, 0 otherwise */
uint16_t attempted_type; /* ECH version used */
int attempted_cid; /* ECH config id sent/rx'd */
int backend; /* 1 if we're a server backend in split-mode, 0 otherwise */
/*
* success is 1 if ECH succeeded, 0 otherwise, on the server this
* is known early, on the client we need to wait for the ECH confirm
* calculation based on the SH (or 2nd SH in case of HRR)
*/
int success;
int grease; /* 1 if we're GREASEing, 0 otherwise */
char *grease_suite; /* HPKE suite string for GREASEing */
unsigned char *sent; /* GREASEy ECH value sent, in case needed for re-tx */
size_t sent_len;
unsigned char *returned; /* binary ECHConfigList retry-configs value */
size_t returned_len;
unsigned char *pub; /* client ephemeral public kept by server in case HRR */
size_t pub_len;
OSSL_HPKE_CTX *hpke_ctx; /* HPKE context, needed for HRR */
/*
* Fields that differ on client between inner and outer that we need to
* keep and swap over IFF ECH has succeeded. Same names chosen as are
* used in SSL_CONNECTION
*/
EVP_PKEY *tmp_pkey; /* client's key share for inner */
int group_id; /* key share group */
unsigned char client_random[SSL3_RANDOM_SIZE]; /* CH random */
} OSSL_ECH_CONN;
/* Internal ECH APIs */
OSSL_ECHSTORE *ossl_echstore_dup(const OSSL_ECHSTORE *old);
void ossl_echstore_entry_free(OSSL_ECHSTORE_ENTRY *ee);
void ossl_ech_ctx_clear(OSSL_ECH_CTX *ce);
int ossl_ech_conn_init(SSL_CONNECTION *s, SSL_CTX *ctx,
const SSL_METHOD *method);
void ossl_ech_conn_clear(OSSL_ECH_CONN *ec);
void ossl_echext_free(OSSL_ECHEXT *e);
OSSL_ECHEXT *ossl_echext_dup(const OSSL_ECHEXT *src);
# endif
#endif

426
ssl/ech/ech_ssl_apis.c Normal file
View file

@ -0,0 +1,426 @@
/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <openssl/ssl.h>
#include <openssl/ech.h>
#include "../ssl_local.h"
int SSL_CTX_set1_echstore(SSL_CTX *ctx, OSSL_ECHSTORE *es)
{
if (ctx == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
OSSL_ECHSTORE_free(ctx->ext.ech.es);
ctx->ext.ech.es = NULL;
if (es == NULL)
return 1;
if ((ctx->ext.ech.es = ossl_echstore_dup(es)) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
return 1;
}
int SSL_set1_echstore(SSL *ssl, OSSL_ECHSTORE *es)
{
SSL_CONNECTION *s;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL)
return 0;
OSSL_ECHSTORE_free(s->ext.ech.es);
s->ext.ech.es = NULL;
if (es == NULL)
return 1;
if ((s->ext.ech.es = ossl_echstore_dup(es)) == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
/*
* Here, and below, if the application calls an API that implies it
* wants to try ECH, then we set attempted to 1
*/
s->ext.ech.attempted = 1;
return 1;
}
OSSL_ECHSTORE *SSL_CTX_get1_echstore(const SSL_CTX *ctx)
{
OSSL_ECHSTORE *dup = NULL;
if (ctx == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
if (ctx->ext.ech.es == NULL)
return NULL;
if ((dup = ossl_echstore_dup(ctx->ext.ech.es)) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return NULL;
}
return dup;
}
OSSL_ECHSTORE *SSL_get1_echstore(const SSL *ssl)
{
SSL_CONNECTION *s;
OSSL_ECHSTORE *dup = NULL;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
if (s->ext.ech.es == NULL)
return NULL;
if ((dup = ossl_echstore_dup(s->ext.ech.es)) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return NULL;
}
return dup;
}
int SSL_ech_set1_server_names(SSL *ssl, const char *inner_name,
const char *outer_name, int no_outer)
{
SSL_CONNECTION *s;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL)
return 0;
OPENSSL_free(s->ext.hostname);
s->ext.hostname = NULL;
if (inner_name != NULL) {
s->ext.hostname = OPENSSL_strdup(inner_name);
if (s->ext.hostname == NULL)
return 0;
}
OPENSSL_free(s->ext.ech.outer_hostname);
s->ext.ech.outer_hostname = NULL;
if (no_outer == 0 && outer_name != NULL && strlen(outer_name) > 0) {
s->ext.ech.outer_hostname = OPENSSL_strdup(outer_name);
if (s->ext.ech.outer_hostname == NULL)
return 0;
}
s->ext.ech.no_outer = no_outer;
s->ext.ech.attempted = 1;
return 1;
}
int SSL_ech_set1_outer_server_name(SSL *ssl, const char *outer_name,
int no_outer)
{
SSL_CONNECTION *s;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL)
return 0;
OPENSSL_free(s->ext.ech.outer_hostname);
s->ext.ech.outer_hostname = NULL;
if (no_outer == 0 && outer_name != NULL && strlen(outer_name) > 0) {
s->ext.ech.outer_hostname = OPENSSL_strdup(outer_name);
if (s->ext.ech.outer_hostname == NULL)
return 0;
}
s->ext.ech.no_outer = no_outer;
s->ext.ech.attempted = 1;
return 1;
}
/*
* Note that this function returns 1 for success and 0 for error. This
* contrasts with SSL_set1_alpn_protos() which (unusually for OpenSSL)
* returns 0 for success and 1 on error.
*/
int SSL_ech_set1_outer_alpn_protos(SSL *ssl, const unsigned char *protos,
const size_t protos_len)
{
SSL_CONNECTION *s;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL)
return 0;
OPENSSL_free(s->ext.ech.alpn_outer);
s->ext.ech.alpn_outer = NULL;
if (protos == NULL)
return 1;
if (protos_len == 0) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
s->ext.ech.alpn_outer = OPENSSL_memdup(protos, protos_len);
if (s->ext.ech.alpn_outer == NULL)
return 0;
s->ext.ech.alpn_outer_len = protos_len;
s->ext.ech.attempted = 1;
return 1;
}
int SSL_ech_get1_status(SSL *ssl, char **inner_sni, char **outer_sni)
{
char *sinner = NULL;
char *souter = NULL;
SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return SSL_ECH_STATUS_FAILED;
}
if (outer_sni == NULL || inner_sni == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return SSL_ECH_STATUS_FAILED;
}
*outer_sni = NULL;
*inner_sni = NULL;
if (s->ext.ech.grease == OSSL_ECH_IS_GREASE) {
if (s->ext.ech.returned != NULL)
return SSL_ECH_STATUS_GREASE_ECH;
return SSL_ECH_STATUS_GREASE;
}
if (s->options & SSL_OP_ECH_GREASE)
return SSL_ECH_STATUS_GREASE;
if (s->ext.ech.backend == 1) {
if (s->ext.hostname != NULL
&& (*inner_sni = OPENSSL_strdup(s->ext.hostname)) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return SSL_ECH_STATUS_FAILED;
}
return SSL_ECH_STATUS_BACKEND;
}
if (s->ext.ech.es == NULL)
return SSL_ECH_STATUS_NOT_CONFIGURED;
/* Set output vars - note we may be pointing to NULL which is fine */
if (s->server == 0) {
sinner = s->ext.hostname;
if (s->ext.ech.attempted == 1 && s->ext.ech.success == 0)
sinner = s->ext.ech.former_inner;
if (s->ext.ech.no_outer == 0)
souter = s->ext.ech.outer_hostname;
else
souter = NULL;
} else {
if (s->ext.ech.es != NULL && s->ext.ech.success == 1) {
sinner = s->ext.hostname;
souter = s->ext.ech.outer_hostname;
}
}
if (s->ext.ech.es != NULL && s->ext.ech.attempted == 1
&& s->ext.ech.attempted_type == TLSEXT_TYPE_ech
&& s->ext.ech.grease != OSSL_ECH_IS_GREASE) {
long vr = X509_V_OK;
vr = SSL_get_verify_result(ssl);
if (sinner != NULL
&& (*inner_sni = OPENSSL_strdup(sinner)) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return SSL_ECH_STATUS_FAILED;
}
if (souter != NULL
&& (*outer_sni = OPENSSL_strdup(souter)) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return SSL_ECH_STATUS_FAILED;
}
if (s->ext.ech.success == 1) {
if (vr == X509_V_OK)
return SSL_ECH_STATUS_SUCCESS;
else
return SSL_ECH_STATUS_BAD_NAME;
} else {
if (vr == X509_V_OK && s->ext.ech.returned != NULL)
return SSL_ECH_STATUS_FAILED_ECH;
else if (vr != X509_V_OK && s->ext.ech.returned != NULL)
return SSL_ECH_STATUS_FAILED_ECH_BAD_NAME;
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return SSL_ECH_STATUS_FAILED;
}
}
return SSL_ECH_STATUS_NOT_TRIED;
}
int SSL_ech_set1_grease_suite(SSL *ssl, const char *suite)
{
SSL_CONNECTION *s;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL)
return 0;
OPENSSL_free(s->ext.ech.grease_suite);
s->ext.ech.grease_suite = NULL;
if (suite == NULL)
return 1;
s->ext.ech.grease_suite = OPENSSL_strdup(suite);
if (s->ext.ech.grease_suite == NULL)
return 0;
s->ext.ech.attempted = 1;
return 1;
}
int SSL_ech_set_grease_type(SSL *ssl, uint16_t type)
{
SSL_CONNECTION *s;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL)
return 0;
s->ext.ech.attempted_type = type;
s->ext.ech.attempted = 1;
return 1;
}
void SSL_ech_set_callback(SSL *ssl, SSL_ech_cb_func f)
{
SSL_CONNECTION *s;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL)
return;
s->ext.ech.cb = f;
return;
}
int SSL_ech_get1_retry_config(SSL *ssl, unsigned char **ec, size_t *eclen)
{
SSL_CONNECTION *s;
OSSL_ECHSTORE *ve = NULL;
BIO *in = NULL;
int rv = 0;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL || ec == NULL || eclen == NULL)
goto err;
if (s->ext.ech.returned == NULL) {
*ec = NULL;
*eclen = 0;
return 1;
}
/*
* To not hand rubbish to application, we'll decode the value we have
* so only syntactically good things are passed up. We won't insist
* though that every entry in the retry_config list seems good - it
* could be that e.g. one is a newer version than we support now,
* and letting the application see that might cause someone to do an
* upgrade.
*/
if ((in = BIO_new(BIO_s_mem())) == NULL
|| BIO_write(in, s->ext.ech.returned, s->ext.ech.returned_len) <= 0
|| (ve = OSSL_ECHSTORE_new(s->ext.ech.es->libctx,
s->ext.ech.es->propq)) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
if (OSSL_ECHSTORE_read_echconfiglist(ve, in) != 1) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
/* all good, copy and return */
*ec = OPENSSL_memdup(s->ext.ech.returned, s->ext.ech.returned_len);
if (*ec == NULL)
goto err;
*eclen = s->ext.ech.returned_len;
rv = 1;
err:
OSSL_ECHSTORE_free(ve);
BIO_free_all(in);
return rv;
}
/*
* Note that this function returns 1 for success and 0 for error. This
* contrasts with SSL_CTX_set1_alpn_protos() which (unusually for OpenSSL)
* returns 0 for success and 1 on error.
*/
int SSL_CTX_ech_set1_outer_alpn_protos(SSL_CTX *ctx,
const unsigned char *protos,
const size_t protos_len)
{
if (ctx == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
OPENSSL_free(ctx->ext.ech.alpn_outer);
ctx->ext.ech.alpn_outer = NULL;
if (protos == NULL)
return 1;
if (protos_len == 0) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
ctx->ext.ech.alpn_outer = OPENSSL_memdup(protos, protos_len);
if (ctx->ext.ech.alpn_outer == NULL)
return 0;
ctx->ext.ech.alpn_outer_len = protos_len;
return 1;
}
int SSL_CTX_ech_raw_decrypt(SSL_CTX *ctx,
int *decrypted_ok,
char **inner_sni, char **outer_sni,
unsigned char *outer_ch, size_t outer_len,
unsigned char *inner_ch, size_t *inner_len,
unsigned char **hrrtok, size_t *toklen)
{
if (ctx == NULL) {
/*
* TODO(ECH): this is a bit of a bogus error, just so as
* to get the `make update` command to add the required
* error number. We don't need it yet, but it's involved
* in some of the build artefacts, so may as well jump
* the gun a bit on it.
*/
ERR_raise(ERR_LIB_SSL, SSL_R_ECH_REQUIRED);
return 0;
}
return 0;
}
void SSL_CTX_ech_set_callback(SSL_CTX *ctx, SSL_ech_cb_func f)
{
if (ctx == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return;
}
ctx->ext.ech.cb = f;
return;
}
int SSL_set1_ech_config_list(SSL *ssl, const uint8_t *ecl, size_t ecl_len)
{
int rv = 0;
SSL_CONNECTION *s;
OSSL_ECHSTORE *es = NULL;
BIO *es_in = NULL;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
goto err;
}
if (ecl == NULL) {
OSSL_ECHSTORE_free(s->ext.ech.es);
s->ext.ech.es = NULL;
return 1;
}
if (ecl_len == 0) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
if ((es_in = BIO_new_mem_buf(ecl, ecl_len)) == NULL
|| (es = OSSL_ECHSTORE_new(NULL, NULL)) == NULL
|| OSSL_ECHSTORE_read_echconfiglist(es, es_in) != 1
|| SSL_set1_echstore(ssl, es) != 1) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
rv = 1;
err:
OSSL_ECHSTORE_free(es);
BIO_free_all(es_in);
return rv;
}

1133
ssl/ech/ech_store.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -16,33 +16,35 @@
static const ERR_STRING_DATA SSL_str_reasons[] = {
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY),
"application data after close notify"},
"application data after close notify"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_APP_DATA_IN_HANDSHAKE),
"app data in handshake"},
"app data in handshake"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT),
"attempt to reuse session in different context"},
"attempt to reuse session in different context"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE),
"at least (D)TLS 1.2 needed in Suite B mode"},
"at least (D)TLS 1.2 needed in Suite B mode"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CERTIFICATE), "bad certificate"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CHANGE_CIPHER_SPEC),
"bad change cipher spec"},
"bad change cipher spec"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CIPHER), "bad cipher"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_COMPRESSION_ALGORITHM),
"bad compression algorithm"},
"bad compression algorithm"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA), "bad data"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK),
"bad data returned by callback"},
"bad data returned by callback"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DECOMPRESSION), "bad decompression"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DH_VALUE), "bad dh value"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DIGEST_LENGTH), "bad digest length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_EARLY_DATA), "bad early data"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ECC_CERT), "bad ecc cert"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ECHCONFIG_EXTENSION),
"bad echconfig extension"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ECPOINT), "bad ecpoint"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_EXTENSION), "bad extension"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HANDSHAKE_LENGTH),
"bad handshake length"},
"bad handshake length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HANDSHAKE_STATE),
"bad handshake state"},
"bad handshake state"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HELLO_REQUEST), "bad hello request"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HRR_VERSION), "bad hrr version"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_KEY_SHARE), "bad key share"},
@ -52,7 +54,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PACKET), "bad packet"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PACKET_LENGTH), "bad packet length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PROTOCOL_VERSION_NUMBER),
"bad protocol version number"},
"bad protocol version number"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PSK), "bad psk"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PSK_IDENTITY), "bad psk identity"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RECORD_TYPE), "bad record type"},
@ -62,560 +64,562 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_PARAMETERS), "bad srp parameters"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRTP_MKI_VALUE), "bad srtp mki value"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST),
"bad srtp protection profile list"},
"bad srtp protection profile list"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SSL_FILETYPE), "bad ssl filetype"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_VALUE), "bad value"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_WRITE_RETRY), "bad write retry"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BINDER_DOES_NOT_VERIFY),
"binder does not verify"},
"binder does not verify"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BIO_NOT_SET), "bio not set"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BLOCK_CIPHER_PAD_IS_WRONG),
"block cipher pad is wrong"},
"block cipher pad is wrong"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BN_LIB), "bn lib"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CALLBACK_FAILED), "callback failed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CANNOT_CHANGE_CIPHER),
"cannot change cipher"},
"cannot change cipher"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CANNOT_GET_GROUP_NAME),
"cannot get group name"},
"cannot get group name"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CA_DN_LENGTH_MISMATCH),
"ca dn length mismatch"},
"ca dn length mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CA_KEY_TOO_SMALL), "ca key too small"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CA_MD_TOO_WEAK), "ca md too weak"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CCS_RECEIVED_EARLY), "ccs received early"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED),
"certificate verify failed"},
"certificate verify failed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERT_CB_ERROR), "cert cb error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERT_LENGTH_MISMATCH),
"cert length mismatch"},
"cert length mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHERSUITE_DIGEST_HAS_CHANGED),
"ciphersuite digest has changed"},
"ciphersuite digest has changed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHER_CODE_WRONG_LENGTH),
"cipher code wrong length"},
"cipher code wrong length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CLIENTHELLO_TLSEXT), "clienthello tlsext"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSED_LENGTH_TOO_LONG),
"compressed length too long"},
"compressed length too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_DISABLED),
"compression disabled"},
"compression disabled"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_FAILURE),
"compression failure"},
"compression failure"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE),
"compression id not within private range"},
"compression id not within private range"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_LIBRARY_ERROR),
"compression library error"},
"compression library error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CONNECTION_TYPE_NOT_SET),
"connection type not set"},
"connection type not set"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CONN_USE_ONLY), "conn use only"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CONTEXT_NOT_DANE_ENABLED),
"context not dane enabled"},
"context not dane enabled"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COOKIE_GEN_CALLBACK_FAILURE),
"cookie gen callback failure"},
"cookie gen callback failure"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COOKIE_MISMATCH), "cookie mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COPY_PARAMETERS_FAILED),
"copy parameters failed"},
"copy parameters failed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CUSTOM_EXT_HANDLER_ALREADY_INSTALLED),
"custom ext handler already installed"},
"custom ext handler already installed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_ALREADY_ENABLED),
"dane already enabled"},
"dane already enabled"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_CANNOT_OVERRIDE_MTYPE_FULL),
"dane cannot override mtype full"},
"dane cannot override mtype full"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_NOT_ENABLED), "dane not enabled"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_TLSA_BAD_CERTIFICATE),
"dane tlsa bad certificate"},
"dane tlsa bad certificate"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_TLSA_BAD_CERTIFICATE_USAGE),
"dane tlsa bad certificate usage"},
"dane tlsa bad certificate usage"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_TLSA_BAD_DATA_LENGTH),
"dane tlsa bad data length"},
"dane tlsa bad data length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_TLSA_BAD_DIGEST_LENGTH),
"dane tlsa bad digest length"},
"dane tlsa bad digest length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_TLSA_BAD_MATCHING_TYPE),
"dane tlsa bad matching type"},
"dane tlsa bad matching type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_TLSA_BAD_PUBLIC_KEY),
"dane tlsa bad public key"},
"dane tlsa bad public key"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_TLSA_BAD_SELECTOR),
"dane tlsa bad selector"},
"dane tlsa bad selector"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DANE_TLSA_NULL_DATA),
"dane tlsa null data"},
"dane tlsa null data"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED),
"data between ccs and finished"},
"data between ccs and finished"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DATA_LENGTH_TOO_LONG),
"data length too long"},
"data length too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DECRYPTION_FAILED), "decryption failed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC),
"decryption failed or bad record mac"},
"decryption failed or bad record mac"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DH_KEY_TOO_SMALL), "dh key too small"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG),
"dh public value length is wrong"},
"dh public value length is wrong"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DIGEST_CHECK_FAILED),
"digest check failed"},
"digest check failed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DTLS_MESSAGE_TOO_BIG),
"dtls message too big"},
"dtls message too big"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DUPLICATE_COMPRESSION_ID),
"duplicate compression id"},
"duplicate compression id"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECC_CERT_NOT_FOR_SIGNING),
"ecc cert not for signing"},
"ecc cert not for signing"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECDH_REQUIRED_FOR_SUITEB_MODE),
"ecdh required for suiteb mode"},
"ecdh required for suiteb mode"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECH_DECODE_ERROR), "ech decode error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECH_REQUIRED), "ech required"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EE_KEY_TOO_SMALL), "ee key too small"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EMPTY_RAW_PUBLIC_KEY),
"empty raw public key"},
"empty raw public key"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST),
"empty srtp protection profile list"},
"empty srtp protection profile list"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ENCRYPTED_LENGTH_TOO_LONG),
"encrypted length too long"},
"encrypted length too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST),
"error in received cipher list"},
"error in received cipher list"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ERROR_IN_SYSTEM_DEFAULT_CONFIG),
"error in system default config"},
"error in system default config"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ERROR_SETTING_TLSA_BASE_DOMAIN),
"error setting tlsa base domain"},
"error setting tlsa base domain"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE),
"exceeds max fragment size"},
"exceeds max fragment size"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXCESSIVE_MESSAGE_SIZE),
"excessive message size"},
"excessive message size"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXTENSION_NOT_RECEIVED),
"extension not received"},
"extension not received"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXTRA_DATA_IN_MESSAGE),
"extra data in message"},
"extra data in message"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXT_LENGTH_MISMATCH),
"ext length mismatch"},
"ext length mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_FAILED_TO_GET_PARAMETER),
"failed to get parameter"},
"failed to get parameter"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_FAILED_TO_INIT_ASYNC),
"failed to init async"},
"failed to init async"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_FEATURE_NEGOTIATION_NOT_COMPLETE),
"feature negotiation not complete"},
"feature negotiation not complete"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_FEATURE_NOT_RENEGOTIABLE),
"feature not renegotiable"},
"feature not renegotiable"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_FRAGMENTED_CLIENT_HELLO),
"fragmented client hello"},
"fragmented client hello"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_A_FIN_BEFORE_A_CCS),
"got a fin before a ccs"},
"got a fin before a ccs"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HTTPS_PROXY_REQUEST),
"https proxy request"},
"https proxy request"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HTTP_REQUEST), "http request"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ILLEGAL_POINT_COMPRESSION),
"illegal point compression"},
"illegal point compression"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ILLEGAL_SUITEB_DIGEST),
"illegal Suite B digest"},
"illegal Suite B digest"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INAPPROPRIATE_FALLBACK),
"inappropriate fallback"},
"inappropriate fallback"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_COMPRESSION),
"inconsistent compression"},
"inconsistent compression"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_EARLY_DATA_ALPN),
"inconsistent early data alpn"},
"inconsistent early data alpn"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_EARLY_DATA_SNI),
"inconsistent early data sni"},
"inconsistent early data sni"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_EXTMS), "inconsistent extms"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INSUFFICIENT_SECURITY),
"insufficient security"},
"insufficient security"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_ALERT), "invalid alert"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CCS_MESSAGE),
"invalid ccs message"},
"invalid ccs message"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CERTIFICATE_OR_ALG),
"invalid certificate or alg"},
"invalid certificate or alg"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMMAND), "invalid command"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMPRESSION_ALGORITHM),
"invalid compression algorithm"},
"invalid compression algorithm"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CONFIG), "invalid config"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CONFIGURATION_NAME),
"invalid configuration name"},
"invalid configuration name"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CONTEXT), "invalid context"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CT_VALIDATION_TYPE),
"invalid ct validation type"},
"invalid ct validation type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_KEY_UPDATE_TYPE),
"invalid key update type"},
"invalid key update type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_MAX_EARLY_DATA),
"invalid max early data"},
"invalid max early data"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_NULL_CMD_NAME),
"invalid null cmd name"},
"invalid null cmd name"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_RAW_PUBLIC_KEY),
"invalid raw public key"},
"invalid raw public key"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_RECORD), "invalid record"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SEQUENCE_NUMBER),
"invalid sequence number"},
"invalid sequence number"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SERVERINFO_DATA),
"invalid serverinfo data"},
"invalid serverinfo data"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SESSION_ID), "invalid session id"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SRP_USERNAME),
"invalid srp username"},
"invalid srp username"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_STATUS_RESPONSE),
"invalid status response"},
"invalid status response"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_TICKET_KEYS_LENGTH),
"invalid ticket keys length"},
"invalid ticket keys length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED),
"legacy sigalg disallowed or unsupported"},
"legacy sigalg disallowed or unsupported"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LENGTH_MISMATCH), "length mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LENGTH_TOO_LONG), "length too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LENGTH_TOO_SHORT), "length too short"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LIBRARY_BUG), "library bug"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LIBRARY_HAS_NO_CIPHERS),
"library has no ciphers"},
"library has no ciphers"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MAXIMUM_ENCRYPTED_PKTS_REACHED),
"maximum encrypted pkts reached"},
"maximum encrypted pkts reached"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_DSA_SIGNING_CERT),
"missing dsa signing cert"},
"missing dsa signing cert"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_ECDSA_SIGNING_CERT),
"missing ecdsa signing cert"},
"missing ecdsa signing cert"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_FATAL), "missing fatal"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_PARAMETERS), "missing parameters"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_PSK_KEX_MODES_EXTENSION),
"missing psk kex modes extension"},
"missing psk kex modes extension"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_CERTIFICATE),
"missing rsa certificate"},
"missing rsa certificate"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_ENCRYPTING_CERT),
"missing rsa encrypting cert"},
"missing rsa encrypting cert"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_SIGNING_CERT),
"missing rsa signing cert"},
"missing rsa signing cert"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_SIGALGS_EXTENSION),
"missing sigalgs extension"},
"missing sigalgs extension"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_SIGNING_CERT),
"missing signing cert"},
"missing signing cert"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_SRP_PARAM),
"can't find SRP server param"},
"can't find SRP server param"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION),
"missing supported groups extension"},
"missing supported groups extension"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_SUPPORTED_VERSIONS_EXTENSION),
"missing supported versions extension"},
"missing supported versions extension"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_DH_KEY), "missing tmp dh key"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_ECDH_KEY),
"missing tmp ecdh key"},
"missing tmp ecdh key"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA),
"mixed handshake and non handshake data"},
"mixed handshake and non handshake data"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_ON_RECORD_BOUNDARY),
"not on record boundary"},
"not on record boundary"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_REPLACING_CERTIFICATE),
"not replacing certificate"},
"not replacing certificate"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_SERVER), "not server"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_APPLICATION_PROTOCOL),
"no application protocol"},
"no application protocol"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATES_RETURNED),
"no certificates returned"},
"no certificates returned"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_ASSIGNED),
"no certificate assigned"},
"no certificate assigned"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_SET), "no certificate set"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CHANGE_FOLLOWING_HRR),
"no change following hrr"},
"no change following hrr"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHERS_AVAILABLE),
"no ciphers available"},
"no ciphers available"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHERS_SPECIFIED),
"no ciphers specified"},
"no ciphers specified"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHER_MATCH), "no cipher match"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CLIENT_CERT_METHOD),
"no client cert method"},
"no client cert method"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_COMPRESSION_SPECIFIED),
"no compression specified"},
"no compression specified"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_COOKIE_CALLBACK_SET),
"no cookie callback set"},
"no cookie callback set"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER),
"Peer haven't sent GOST certificate, required for selected ciphersuite"},
"Peer haven't sent GOST certificate, required for selected ciphersuite"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_METHOD_SPECIFIED),
"no method specified"},
"no method specified"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PEM_EXTENSIONS), "no pem extensions"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PRIVATE_KEY_ASSIGNED),
"no private key assigned"},
"no private key assigned"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PROTOCOLS_AVAILABLE),
"no protocols available"},
"no protocols available"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_RENEGOTIATION), "no renegotiation"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_REQUIRED_DIGEST), "no required digest"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SHARED_CIPHER), "no shared cipher"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SHARED_GROUPS), "no shared groups"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS),
"no shared signature algorithms"},
"no shared signature algorithms"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SRTP_PROFILES), "no srtp profiles"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_STREAM), "no stream"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SUITABLE_DIGEST_ALGORITHM),
"no suitable digest algorithm"},
"no suitable digest algorithm"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SUITABLE_GROUPS), "no suitable groups"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SUITABLE_KEY_SHARE),
"no suitable key share"},
"no suitable key share"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SUITABLE_RECORD_LAYER),
"no suitable record layer"},
"no suitable record layer"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM),
"no suitable signature algorithm"},
"no suitable signature algorithm"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_VALID_SCTS), "no valid scts"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_VERIFY_COOKIE_CALLBACK),
"no verify cookie callback"},
"no verify cookie callback"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NULL_SSL_CTX), "null ssl ctx"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NULL_SSL_METHOD_PASSED),
"null ssl method passed"},
"null ssl method passed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OCSP_CALLBACK_FAILURE),
"ocsp callback failure"},
"ocsp callback failure"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED),
"old session cipher not returned"},
"old session cipher not returned"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED),
"old session compression algorithm not returned"},
"old session compression algorithm not returned"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OVERFLOW_ERROR), "overflow error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PACKET_LENGTH_TOO_LONG),
"packet length too long"},
"packet length too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PARSE_TLSEXT), "parse tlsext"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PATH_TOO_LONG), "path too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE),
"peer did not return a certificate"},
"peer did not return a certificate"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEM_NAME_BAD_PREFIX),
"pem name bad prefix"},
"pem name bad prefix"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEM_NAME_TOO_SHORT), "pem name too short"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PIPELINE_FAILURE), "pipeline failure"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_POLL_REQUEST_NOT_SUPPORTED),
"poll request not supported"},
"poll request not supported"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR),
"post handshake auth encoding err"},
"post handshake auth encoding err"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PRIVATE_KEY_MISMATCH),
"private key mismatch"},
"private key mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PROTOCOL_IS_SHUTDOWN),
"protocol is shutdown"},
"protocol is shutdown"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_IDENTITY_NOT_FOUND),
"psk identity not found"},
"psk identity not found"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_NO_CLIENT_CB), "psk no client cb"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_NO_SERVER_CB), "psk no server cb"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_QUIC_HANDSHAKE_LAYER_ERROR),
"quic handshake layer error"},
"quic handshake layer error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_QUIC_NETWORK_ERROR), "quic network error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_QUIC_PROTOCOL_ERROR),
"quic protocol error"},
"quic protocol error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_READ_BIO_NOT_SET), "read bio not set"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_READ_TIMEOUT_EXPIRED),
"read timeout expired"},
"read timeout expired"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORDS_NOT_RELEASED),
"records not released"},
"records not released"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORD_LAYER_FAILURE),
"record layer failure"},
"record layer failure"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORD_LENGTH_MISMATCH),
"record length mismatch"},
"record length mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORD_TOO_SMALL), "record too small"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REMOTE_PEER_ADDRESS_NOT_SET),
"remote peer address not set"},
"remote peer address not set"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATE_EXT_TOO_LONG),
"renegotiate ext too long"},
"renegotiate ext too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATION_ENCODING_ERR),
"renegotiation encoding err"},
"renegotiation encoding err"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATION_MISMATCH),
"renegotiation mismatch"},
"renegotiation mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUEST_PENDING), "request pending"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUEST_SENT), "request sent"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_CIPHER_MISSING),
"required cipher missing"},
"required cipher missing"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING),
"required compression algorithm missing"},
"required compression algorithm missing"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING),
"scsv received when renegotiating"},
"scsv received when renegotiating"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SCT_VERIFICATION_FAILED),
"sct verification failed"},
"sct verification failed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SEQUENCE_CTR_WRAPPED),
"sequence ctr wrapped"},
"sequence ctr wrapped"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SERVERHELLO_TLSEXT), "serverhello tlsext"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED),
"session id context uninitialized"},
"session id context uninitialized"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHUTDOWN_WHILE_IN_INIT),
"shutdown while in init"},
"shutdown while in init"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SIGNATURE_ALGORITHMS_ERROR),
"signature algorithms error"},
"signature algorithms error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE),
"signature for non signing certificate"},
"signature for non signing certificate"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRP_A_CALC), "error with the srp params"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES),
"srtp could not allocate profiles"},
"srtp could not allocate profiles"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG),
"srtp protection profile list too long"},
"srtp protection profile list too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE),
"srtp unknown protection profile"},
"srtp unknown protection profile"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH),
"ssl3 ext invalid max fragment length"},
"ssl3 ext invalid max fragment length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_SERVERNAME),
"ssl3 ext invalid servername"},
"ssl3 ext invalid servername"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE),
"ssl3 ext invalid servername type"},
"ssl3 ext invalid servername type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_SESSION_ID_TOO_LONG),
"ssl3 session id too long"},
"ssl3 session id too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE),
"ssl/tls alert bad certificate"},
"ssl/tls alert bad certificate"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_BAD_RECORD_MAC),
"ssl/tls alert bad record mac"},
"ssl/tls alert bad record mac"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED),
"ssl/tls alert certificate expired"},
"ssl/tls alert certificate expired"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED),
"ssl/tls alert certificate revoked"},
"ssl/tls alert certificate revoked"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN),
"ssl/tls alert certificate unknown"},
"ssl/tls alert certificate unknown"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE),
"ssl/tls alert decompression failure"},
"ssl/tls alert decompression failure"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE),
"ssl/tls alert handshake failure"},
"ssl/tls alert handshake failure"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER),
"ssl/tls alert illegal parameter"},
"ssl/tls alert illegal parameter"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_NO_CERTIFICATE),
"ssl/tls alert no certificate"},
"ssl/tls alert no certificate"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE),
"ssl/tls alert unexpected message"},
"ssl/tls alert unexpected message"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE),
"ssl/tls alert unsupported certificate"},
"ssl/tls alert unsupported certificate"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_COMMAND_SECTION_EMPTY),
"ssl command section empty"},
"ssl command section empty"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_COMMAND_SECTION_NOT_FOUND),
"ssl command section not found"},
"ssl command section not found"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION),
"ssl ctx has no default ssl version"},
"ssl ctx has no default ssl version"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_HANDSHAKE_FAILURE),
"ssl handshake failure"},
"ssl handshake failure"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS),
"ssl library has no ciphers"},
"ssl library has no ciphers"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_NEGATIVE_LENGTH),
"ssl negative length"},
"ssl negative length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SECTION_EMPTY), "ssl section empty"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SECTION_NOT_FOUND),
"ssl section not found"},
"ssl section not found"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED),
"ssl session id callback failed"},
"ssl session id callback failed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_CONFLICT),
"ssl session id conflict"},
"ssl session id conflict"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG),
"ssl session id context too long"},
"ssl session id context too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH),
"ssl session id has bad length"},
"ssl session id has bad length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_TOO_LONG),
"ssl session id too long"},
"ssl session id too long"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_VERSION_MISMATCH),
"ssl session version mismatch"},
"ssl session version mismatch"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STILL_IN_INIT), "still in init"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STREAM_COUNT_LIMITED),
"stream count limited"},
"stream count limited"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STREAM_FINISHED), "stream finished"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STREAM_RECV_ONLY), "stream recv only"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STREAM_RESET), "stream reset"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_STREAM_SEND_ONLY), "stream send only"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED),
"tlsv13 alert certificate required"},
"tlsv13 alert certificate required"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV13_ALERT_MISSING_EXTENSION),
"tlsv13 alert missing extension"},
"tlsv13 alert missing extension"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_ACCESS_DENIED),
"tlsv1 alert access denied"},
"tlsv1 alert access denied"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_DECODE_ERROR),
"tlsv1 alert decode error"},
"tlsv1 alert decode error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED),
"tlsv1 alert decryption failed"},
"tlsv1 alert decryption failed"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_DECRYPT_ERROR),
"tlsv1 alert decrypt error"},
"tlsv1 alert decrypt error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION),
"tlsv1 alert export restriction"},
"tlsv1 alert export restriction"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK),
"tlsv1 alert inappropriate fallback"},
"tlsv1 alert inappropriate fallback"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY),
"tlsv1 alert insufficient security"},
"tlsv1 alert insufficient security"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_INTERNAL_ERROR),
"tlsv1 alert internal error"},
"tlsv1 alert internal error"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL),
"tlsv1 alert no application protocol"},
"tlsv1 alert no application protocol"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION),
"tlsv1 alert no renegotiation"},
"tlsv1 alert no renegotiation"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION),
"tlsv1 alert protocol version"},
"tlsv1 alert protocol version"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW),
"tlsv1 alert record overflow"},
"tlsv1 alert record overflow"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_UNKNOWN_CA),
"tlsv1 alert unknown ca"},
"tlsv1 alert unknown ca"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY),
"tlsv1 alert unknown psk identity"},
"tlsv1 alert unknown psk identity"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_USER_CANCELLED),
"tlsv1 alert user cancelled"},
"tlsv1 alert user cancelled"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE),
"tlsv1 bad certificate hash value"},
"tlsv1 bad certificate hash value"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE),
"tlsv1 bad certificate status response"},
"tlsv1 bad certificate status response"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE),
"tlsv1 certificate unobtainable"},
"tlsv1 certificate unobtainable"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_UNRECOGNIZED_NAME),
"tlsv1 unrecognized name"},
"tlsv1 unrecognized name"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_UNSUPPORTED_EXTENSION),
"tlsv1 unsupported extension"},
"tlsv1 unsupported extension"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL),
"tls illegal exporter label"},
"tls illegal exporter label"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST),
"tls invalid ecpointformat list"},
"tls invalid ecpointformat list"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TOO_MANY_KEY_UPDATES),
"too many key updates"},
"too many key updates"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TOO_MANY_WARN_ALERTS),
"too many warn alerts"},
"too many warn alerts"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TOO_MUCH_EARLY_DATA),
"too much early data"},
"too much early data"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS),
"unable to find ecdh parameters"},
"unable to find ecdh parameters"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS),
"unable to find public key parameters"},
"unable to find public key parameters"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES),
"unable to load ssl3 md5 routines"},
"unable to load ssl3 md5 routines"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES),
"unable to load ssl3 sha1 routines"},
"unable to load ssl3 sha1 routines"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_CCS_MESSAGE),
"unexpected ccs message"},
"unexpected ccs message"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_END_OF_EARLY_DATA),
"unexpected end of early data"},
"unexpected end of early data"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_EOF_WHILE_READING),
"unexpected eof while reading"},
"unexpected eof while reading"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_MESSAGE), "unexpected message"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_RECORD), "unexpected record"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNINITIALIZED), "uninitialized"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_ALERT_TYPE), "unknown alert type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CERTIFICATE_TYPE),
"unknown certificate type"},
"unknown certificate type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CIPHER_RETURNED),
"unknown cipher returned"},
"unknown cipher returned"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CIPHER_TYPE),
"unknown cipher type"},
"unknown cipher type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CMD_NAME), "unknown cmd name"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_COMMAND), "unknown command"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_DIGEST), "unknown digest"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE),
"unknown key exchange type"},
"unknown key exchange type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_MANDATORY_PARAMETER),
"unknown mandatory parameter"},
"unknown mandatory parameter"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_PKEY_TYPE), "unknown pkey type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_PROTOCOL), "unknown protocol"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_SSL_VERSION),
"unknown ssl version"},
"unknown ssl version"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_STATE), "unknown state"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED),
"unsafe legacy renegotiation disabled"},
"unsafe legacy renegotiation disabled"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSOLICITED_EXTENSION),
"unsolicited extension"},
"unsolicited extension"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM),
"unsupported compression algorithm"},
"unsupported compression algorithm"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_CONFIG_VALUE),
"unsupported config value"},
"unsupported config value"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS),
"unsupported config value class"},
"unsupported config value class"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_CONFIG_VALUE_OP),
"unsupported config value op"},
"unsupported config value op"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE),
"unsupported elliptic curve"},
"unsupported elliptic curve"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_PROTOCOL),
"unsupported protocol"},
"unsupported protocol"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_SSL_VERSION),
"unsupported ssl version"},
"unsupported ssl version"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_STATUS_TYPE),
"unsupported status type"},
"unsupported status type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_WRITE_FLAG),
"unsupported write flag"},
"unsupported write flag"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_USE_SRTP_NOT_NEGOTIATED),
"use srtp not negotiated"},
"use srtp not negotiated"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_VERSION_TOO_HIGH), "version too high"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_VERSION_TOO_LOW), "version too low"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CERTIFICATE_TYPE),
"wrong certificate type"},
"wrong certificate type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CIPHER_RETURNED),
"wrong cipher returned"},
"wrong cipher returned"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CURVE), "wrong curve"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_RPK_TYPE), "wrong rpk type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_LENGTH),
"wrong signature length"},
"wrong signature length"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_SIZE),
"wrong signature size"},
"wrong signature size"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_TYPE),
"wrong signature type"},
"wrong signature type"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SSL_VERSION), "wrong ssl version"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_VERSION_NUMBER),
"wrong version number"},
"wrong version number"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_X509_LIB), "x509 lib"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS),
"x509 verification setup problems"},
"x509 verification setup problems"},
{0, NULL}
};

View file

@ -919,6 +919,11 @@ SSL *ossl_ssl_connection_new_int(SSL_CTX *ctx, SSL *user_ssl,
goto sslerr;
#endif
#ifndef OPENSSL_NO_ECH
if (!ossl_ech_conn_init(s, ctx, method))
goto sslerr;
#endif
s->ssl_pkey_num = SSL_PKEY_NUM + ctx->sigalg_list_len;
return ssl;
cerr:
@ -1495,6 +1500,9 @@ void ossl_ssl_connection_free(SSL *ssl)
BIO_free_all(s->rbio);
s->rbio = NULL;
OPENSSL_free(s->s3.tmp.valid_flags);
#ifndef OPENSSL_NO_ECH
ossl_ech_conn_clear(&s->ext.ech);
#endif
}
void SSL_set0_rbio(SSL *s, BIO *rbio)
@ -4375,6 +4383,10 @@ void SSL_CTX_free(SSL_CTX *a)
OPENSSL_free(a->qlog_title);
#endif
#ifndef OPENSSL_NO_ECH
ossl_ech_ctx_clear(&a->ext.ech);
#endif
OPENSSL_free(a);
}

View file

@ -38,6 +38,9 @@
# include "internal/ssl.h"
# include "internal/cryptlib.h"
# include "record/record.h"
# ifndef OPENSSL_NO_ECH
# include "ech/ech_local.h"
# endif
# ifdef OPENSSL_BUILD_SHLIBSSL
# undef OPENSSL_EXTERN
@ -1065,6 +1068,9 @@ struct ssl_ctx_st {
# endif
unsigned char cookie_hmac_key[SHA256_DIGEST_LENGTH];
# ifndef OPENSSL_NO_ECH
OSSL_ECH_CTX ech;
# endif
} ext;
# ifndef OPENSSL_NO_PSK
@ -1680,6 +1686,10 @@ struct ssl_connection_st {
uint8_t client_cert_type_ctos;
uint8_t server_cert_type;
uint8_t server_cert_type_ctos;
# ifndef OPENSSL_NO_ECH
OSSL_ECH_CONN ech;
# endif
} ext;
/*

View file

@ -64,7 +64,8 @@ IF[{- !$disabled{tests} -}]
ca_internals_test bio_tfo_test membio_test bio_dgram_test list_test \
fips_version_test x509_test hpke_test pairwise_fail_test \
nodefltctxtest evp_xof_test x509_load_cert_file_test bio_meth_test \
x509_acert_test x509_req_test strtoultest bio_pw_callback_test
x509_acert_test x509_req_test strtoultest bio_pw_callback_test \
ech_test
IF[{- !$disabled{'rpk'} -}]
PROGRAMS{noinst}=rpktest
@ -209,6 +210,10 @@ IF[{- !$disabled{tests} -}]
INCLUDE[hpke_test]=../include ../apps/include
DEPEND[hpke_test]=../libcrypto.a libtestutil.a
SOURCE[ech_test]=ech_test.c helpers/ssltestlib.c filterprov.c tls-provider.c
INCLUDE[ech_test]=../include ../apps/include
DEPEND[ech_test]=../libssl.a ../libcrypto.a libtestutil.a
SOURCE[evp_extra_test2]=evp_extra_test2.c $INITSRC tls-provider.c
INCLUDE[evp_extra_test2]=../include ../apps/include
DEPEND[evp_extra_test2]=../libcrypto libtestutil.a

25
test/certs/ech-big.pem Normal file
View file

@ -0,0 +1,25 @@
-----BEGIN ECHCONFIG-----
BNj+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBs
ZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtl
eGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwABAAB
AAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AW
PAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/Q
LGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUu
WkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc
8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERv
EyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF
/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBi
x2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7
ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA
/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUu
Y29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhh
bXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQAB
AAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwA
BAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxp
c2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpC
lg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd
8VUuWkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMs
pDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4R
CERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA
-----END ECHCONFIG-----

7
test/certs/ech-eg.pem Normal file
View file

@ -0,0 +1,7 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VuBCIEIKBC3rocwIF5tGY+/TaYQrCxY+ULsch94ja9DojkcvlT
-----END PRIVATE KEY-----
-----BEGIN ECHCONFIG-----
ADn+DQA1agAgACBtuySC1pphjFlGYKTaSm2KWNg7GQVRS8uAYvLTm5QlGwAEAAEA
AQAGZWcuY29tAAA=
-----END ECHCONFIG-----

37
test/certs/ech-giant.pem Normal file
View file

@ -0,0 +1,37 @@
-----BEGIN ECHCONFIG-----
B8D+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBs
ZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtl
eGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwABAAB
AAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AW
PAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/Q
LGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUu
WkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc
8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERv
EyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF
/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBi
x2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7
ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA
/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUu
Y29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhh
bXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQAB
AAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwA
BAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxp
c2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpC
lg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd
8VUuWkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMs
pDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4R
CERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdg
e/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAg
ACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4N
ADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNv
bQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwABAABAAEAC2V4YW1w
bGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQAL
ZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQA
AQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNg
FjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP
0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFV
LlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQz
nPGd8VUuWkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhE
bxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvy
xf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA
-----END ECHCONFIG-----`

11
test/certs/ech-mid.pem Normal file
View file

@ -0,0 +1,11 @@
-----BEGIN ECHCONFIG-----
AfD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBs
ZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtl
eGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUuWkKWD9AsaXNgFjwABAAB
AAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AW
PAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERvEyykM5zxnfFVLlpClg/Q
LGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA/g0AOrsAIAAgYsdge/LF/hEIRG8TLKQznPGd8VUu
WkKWD9AsaXNgFjwABAABAAEAC2V4YW1wbGUuY29tAAD+DQA6uwAgACBix2B78sX+EQhEbxMspDOc
8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAP4NADq7ACAAIGLHYHvyxf4RCERv
EyykM5zxnfFVLlpClg/QLGlzYBY8AAQAAQABAAtleGFtcGxlLmNvbQAA
-----END ECHCONFIG-----

14
test/certs/ech-rsa.pem Normal file
View file

@ -0,0 +1,14 @@
-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEApeb9fP5SDxyOQZQT
qGg2QeE0ypxY6Th33aDkRCRVB69rDMSA1Thfeyk65IfaPaA3bC4hsqAIBgslcFfk
1/i8KQIDAQABAkAsH3EPizwb1MZo3o8T3ROBFfpKYKas8F3Azgenr9oFfs5kPgya
VDdtZu+UweG5nTo+fZG5ZFmcwWXJTLtiUfABAiEAz2gvTuc0lPTQi3t6RFB5nGCt
h75Ofx/ceusHa2a36QECIQDMxXJQnuWY+bH/wSfPY/ySltQ6U2cy0LHQ37FIfSFr
KQIgUo++hUI0BDeP7HYyrY77WeyCJ07yIFimg6ebRH2XKAECIQCSavhTd1q6qIhD
VMzveRInixvTXMGkzx7mOJzeNUMJCQIhAJjjVdRjUpWPMquRDCddmwegh88ptsFX
T/Ygm1OubAyM
-----END PRIVATE KEY-----
-----BEGIN ECHCONFIG-----
AD7+DQA6bAAgACCY7B0f/3KvHIFdoqFaObdU8YYU+MdBf4vzbLhAAL2QCwAEAAEA
AQALZXhhbXBsZS5jb20AAA==
-----END ECHCONFIG-----

28
test/certs/echserver.key Normal file
View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCJHNs4e27KjdYU
8IgiT539WbEl16Eve6tu1UFpGdkqsHH8+yceoFkMWSdpr+Sh3PYDRk/Ek0qB33uK
y3FKlIejtolxVhBybtja5zYMmVXnRHsB/qe7FgyM/lv0xtO1nfSGFkVZVz1+xWPr
aslJN3U4HPaaL4SGghw5KIRD8FPx174v8FDOBeVhn6pzTK/xpTeqXLvAAxgPhF+Q
HOQ0pTXrOHbaiK4l+8JfVm+0fCJjMnT20mmGuTNjvdWZ4XIgPPYkEQrf1CpdONcU
kiiFcpcYtbVS0YyC91qqJLKFv51eki9STwUQISn5jLWIRQlXkBXhK6aGlkLWnov/
kqqUWEQTAgMBAAECggEAAah0LDAt7EwfyRwJgWS2E+C4SC1d2R2lOo9gnZ0+54m/
rx/4XqHwwbn4RIpoeN6bqPl6MHXZgk2KCGkiYxT9uOiVq+WvCDs36xm9qRRXmhbV
Z/ZE3/nJyBCxWvnmiH0y/kYZq5Vm/Hf1l9ywN27wv292OfIWJ6w+HCDVzJ6E3VlK
fuzBFhZmnBjul6Nlo76blNXwn5loWYomkg6nVWzrTjYWosGd0aKpZJR948nWpuEj
fkevLqMMfSuB+cXQ+zB4lttqB5dphFxbNv5gHOd1rllzHFdhK+/7e7ktGYmpQtuH
WRKPD1y173ek5FtvTxtcrL2rST+hoSDWcCQCws/70QKBgQC5QGlldraTs0JXhoH9
6X+V1mvsAWCItq7JhUvFHFtAxHuYacrlnsJRxv8aRS8AhuNYTtThJQcWzFL2UU7W
CdiB0VZr7phPNOVYsa95V8a8A1CHllfdTzxw1TyiOJ0sdeU7irWo3vnTDxftfySP
lkdPNbItO1RXqeIR6mJf+rVGowKBgQC9egaDDdM2YsMtl61fIRoPMPdJB3fGcL0A
FwUAtGQ1twETykzcUCeAqx0yx7zCAyPeA9WmpHzuz/LR8uA3TP/nDcLlCaQowfeR
VPdS3Q1iAnSyaCsF1THQPvhFsYMXbIn/svSSpddLOrP6ltSr+PORLOXXUmQJnffk
hxKaxK6T0QKBgFzyVm9UGtMMk/K6SCqPpzYUuV1Wa4rsrdHqkVO6oIZkjuav3d9L
wo+pWoFhyO1owFSkaOb13xKvPcjcjsOReRHZaJUKx1ymW5Qewr4NLmdS+mqtIjSl
9tteAegao7GVDYjMVcz+4zXkUssUidGJQwoZFObg57Z8RDNc+DLT5XQlAoGAYaO8
L1S0ftYuFhSPdvIr56AoDi4W/t+hxaYXIeHTsgp4N6aMLQvxD1EeXsim8KOFnCcF
tjYVW0s1qhMqj9TSGlLxF+379jTeSroqKT1YZCU31afwY7UVUmbgsalkEHISOv4R
InDrnQzHKl8HgQdtHGayml8OxhXtZIpmf/LSs8ECgYBrFbKl8ylhlzw5rC8DuP8n
hzKLOKzipKmHLn4eDBEFyLTyoyYrqx/nxLi3kSIyNP4fJ9vHOXgdjdrp9xRMcFEx
IA2sdywI5VuymxktP8OlORa0NK4eFZXkDNsQlkathYiKqCwGjUWdGk5+Ry5qO/UC
9ua9adjNa108aBzWLYZFCw==
-----END PRIVATE KEY-----

80
test/certs/echserver.pem Normal file
View file

@ -0,0 +1,80 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
18:45:8f:30:1d:fe:dc:22:9d:95:40:8c:e5:36:f9:38:0d:d5:58:a0
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=Root CA
Validity
Not Before: Oct 6 18:36:12 2023 GMT
Not After : Sep 12 18:36:12 2123 GMT
Subject: CN=server.example
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:89:1c:db:38:7b:6e:ca:8d:d6:14:f0:88:22:4f:
9d:fd:59:b1:25:d7:a1:2f:7b:ab:6e:d5:41:69:19:
d9:2a:b0:71:fc:fb:27:1e:a0:59:0c:59:27:69:af:
e4:a1:dc:f6:03:46:4f:c4:93:4a:81:df:7b:8a:cb:
71:4a:94:87:a3:b6:89:71:56:10:72:6e:d8:da:e7:
36:0c:99:55:e7:44:7b:01:fe:a7:bb:16:0c:8c:fe:
5b:f4:c6:d3:b5:9d:f4:86:16:45:59:57:3d:7e:c5:
63:eb:6a:c9:49:37:75:38:1c:f6:9a:2f:84:86:82:
1c:39:28:84:43:f0:53:f1:d7:be:2f:f0:50:ce:05:
e5:61:9f:aa:73:4c:af:f1:a5:37:aa:5c:bb:c0:03:
18:0f:84:5f:90:1c:e4:34:a5:35:eb:38:76:da:88:
ae:25:fb:c2:5f:56:6f:b4:7c:22:63:32:74:f6:d2:
69:86:b9:33:63:bd:d5:99:e1:72:20:3c:f6:24:11:
0a:df:d4:2a:5d:38:d7:14:92:28:85:72:97:18:b5:
b5:52:d1:8c:82:f7:5a:aa:24:b2:85:bf:9d:5e:92:
2f:52:4f:05:10:21:29:f9:8c:b5:88:45:09:57:90:
15:e1:2b:a6:86:96:42:d6:9e:8b:ff:92:aa:94:58:
44:13
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
8C:E0:38:04:70:7E:B4:CB:1F:BF:AA:E6:67:42:74:63:46:88:58:74
X509v3 Authority Key Identifier:
70:7F:2E:AE:83:68:59:98:04:23:2A:CD:EB:3E:17:CD:24:DD:01:49
X509v3 Subject Alternative Name:
DNS:*.server.example, DNS:server.example
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
9b:fe:bc:b1:40:d4:08:91:f6:1f:b4:0f:8c:50:ac:49:36:6f:
27:93:e8:94:13:bc:fe:1a:2a:cf:93:98:13:b3:b4:85:a5:62:
4d:58:8f:da:cd:f7:1b:c3:1f:42:ba:2a:89:45:11:33:49:86:
2c:3a:0a:99:17:4f:0c:f1:1e:35:31:2c:69:f9:15:d5:37:54:
cc:9e:e3:67:9f:d5:6e:ad:b1:26:60:df:aa:84:63:da:a7:31:
c9:69:a0:d8:c2:96:d3:82:b4:99:70:8c:3c:92:a4:c0:f0:7c:
3f:04:d3:29:4f:6c:c5:fd:39:12:95:65:7f:37:fb:52:5b:12:
99:d6:d7:b5:ba:44:6e:36:ec:5d:f2:5d:d4:aa:2d:8a:46:ce:
29:66:c1:ed:36:13:f2:f3:ae:92:4a:97:db:99:ed:8f:4e:4e:
ed:73:1b:fa:3e:64:63:40:5c:c2:03:76:2c:dc:58:01:3f:17:
d0:ae:a6:b2:64:85:47:ba:7d:5a:36:53:e4:90:00:8e:f5:17:
a5:ff:a3:81:ee:ed:25:ca:10:76:75:2d:65:ff:f8:b1:8c:3c:
a3:ff:81:12:72:c7:bc:b5:17:06:d8:c6:13:97:cb:8e:58:51:
2a:a4:be:91:59:40:4b:07:8d:69:2f:92:ee:ea:9c:bf:eb:42:
b7:62:b8:e3
-----BEGIN CERTIFICATE-----
MIIDNTCCAh2gAwIBAgIUGEWPMB3+3CKdlUCM5Tb5OA3VWKAwDQYJKoZIhvcNAQEL
BQAwEjEQMA4GA1UEAwwHUm9vdCBDQTAgFw0yMzEwMDYxODM2MTJaGA8yMTIzMDkx
MjE4MzYxMlowGTEXMBUGA1UEAwwOc2VydmVyLmV4YW1wbGUwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCJHNs4e27KjdYU8IgiT539WbEl16Eve6tu1UFp
GdkqsHH8+yceoFkMWSdpr+Sh3PYDRk/Ek0qB33uKy3FKlIejtolxVhBybtja5zYM
mVXnRHsB/qe7FgyM/lv0xtO1nfSGFkVZVz1+xWPraslJN3U4HPaaL4SGghw5KIRD
8FPx174v8FDOBeVhn6pzTK/xpTeqXLvAAxgPhF+QHOQ0pTXrOHbaiK4l+8JfVm+0
fCJjMnT20mmGuTNjvdWZ4XIgPPYkEQrf1CpdONcUkiiFcpcYtbVS0YyC91qqJLKF
v51eki9STwUQISn5jLWIRQlXkBXhK6aGlkLWnov/kqqUWEQTAgMBAAGjejB4MAkG
A1UdEwQCMAAwHQYDVR0OBBYEFIzgOARwfrTLH7+q5mdCdGNGiFh0MB8GA1UdIwQY
MBaAFHB/Lq6DaFmYBCMqzes+F80k3QFJMCsGA1UdEQQkMCKCECouc2VydmVyLmV4
YW1wbGWCDnNlcnZlci5leGFtcGxlMA0GCSqGSIb3DQEBCwUAA4IBAQCb/ryxQNQI
kfYftA+MUKxJNm8nk+iUE7z+GirPk5gTs7SFpWJNWI/azfcbwx9CuiqJRREzSYYs
OgqZF08M8R41MSxp+RXVN1TMnuNnn9VurbEmYN+qhGPapzHJaaDYwpbTgrSZcIw8
kqTA8Hw/BNMpT2zF/TkSlWV/N/tSWxKZ1te1ukRuNuxd8l3Uqi2KRs4pZsHtNhPy
866SSpfbme2PTk7tcxv6PmRjQFzCA3Ys3FgBPxfQrqayZIVHun1aNlPkkACO9Rel
/6OB7u0lyhB2dS1l//ixjDyj/4EScse8tRcG2MYTl8uOWFEqpL6RWUBLB41pL5Lu
6py/60K3Yrjj
-----END CERTIFICATE-----

1408
test/ech_test.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,93 @@
#! /usr/bin/env perl
# Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
#
use strict;
use warnings;
use OpenSSL::Test::Utils;
use OpenSSL::Test qw/:DEFAULT srctop_file srctop_dir bldtop_dir bldtop_file with/;
setup("test_app_ech");
plan skip_all => "ECH tests not supported in this build"
if disabled("ech") || disabled("tls1_3")
|| disabled("ec") || disabled("ecx");
plan tests => 13;
ok(run(app(["openssl", "ech", "-help"])),
"Run openssl ech with help");
ok(run(app(["openssl", "ech",
"-ech_version", "13",
"-public_name", "example.com",
"-out", "eg1.pem",
"-verbose",
"-text"])),
"Generate an ECH key pair for example.com");
ok(run(app(["openssl", "ech",
"-suite", "0x10,2,2",
"-public_name", "example.com",
"-out", "eg2.pem",
"-text"])),
"Generate an ECDSA ECH key pair for example.com");
ok(run(app(["openssl", "ech",
"-max_name_len", "13",
"-public_name", "example.com",
"-out", "eg2.pem",
"-text"])),
"Generate an ECH key pair for example.com with max name len 13");
ok(run(app(["openssl", "ech",
"-in", "eg1.pem",
"-in", "eg2.pem",
"-out", "eg3.pem",
"-verbose"])),
"Catenate the ECH for example.com twice");
ok(run(app(["openssl", "ech",
"-in", "eg3.pem",
"-select", "1",
"-verbose",
"-out", "eg4.pem"])),
"Select one ECH Config");
with({ exit_checker => sub { return shift == 1; } },
sub {
ok(run(app(["openssl", "ech" ])),
"Run openssl ech with no arg");
ok(run(app(["openssl", "ech", "-nohelpatall"])),
"Run openssl ech with unknown arg");
ok(run(app(["openssl", "ech", "nohelpatall"])),
"Run openssl ech with unknown non arg");
ok(run(app(["openssl", "ech",
"-ech_version", "0xfe09",
"-public_name", "example.com",
"-out", "eg1.pem",
"-text"])),
"Fail to generate an ECH key pair for old draft version");
ok(run(app(["openssl", "ech",
"-suite", "not,a,good,one",
"-public_name", "example.com",
"-out", "eg2.pem",
"-text"])),
"Fail to generate an ECH key pair with bad suite");
ok(run(app(["openssl", "ech",
"-max_name_len", "1300",
"-public_name", "example.com",
"-text"])),
"(Fail to) Generate an ECH key pair for example.com with max name len 1300");
ok(run(app(["openssl", "ech",
"-in", "eg1.pem",
"-in", "eg2.pem",
"-in", "eg3.pem",
"-in", "eg4.pem",
"-in", "eg1.pem",
"-in", "eg2.pem",
"-in", "eg3.pem",
"-in", "eg4.pem"])),
"Too many input files");
});

View file

@ -0,0 +1,21 @@
#! /usr/bin/env perl
# Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
use strict;
use OpenSSL::Test::Utils;
use OpenSSL::Test qw/:DEFAULT srctop_file srctop_dir bldtop_dir bldtop_file/;
setup("test_ech");
plan skip_all => "ECH tests not supported in this build"
if disabled("ech") || disabled("tls1_3") || disabled("ec") || disabled("ecx");
plan tests => 1;
ok(run(test(["ech_test", srctop_dir("test", "certs")])))

View file

@ -587,3 +587,31 @@ SSL_CTX_flush_sessions_ex 587 3_4_0 EXIST::FUNCTION:
SSL_CTX_set_block_padding_ex 588 3_4_0 EXIST::FUNCTION:
SSL_set_block_padding_ex 589 3_4_0 EXIST::FUNCTION:
SSL_get1_builtin_sigalgs 590 3_4_0 EXIST::FUNCTION:
OSSL_ECHSTORE_free ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_new_config ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_write_pem ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_read_echconfiglist ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_get1_info ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_downselect ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_set1_key_and_read_pem ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_read_pem ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_num_keys ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_flush_keys ? 3_5_0 EXIST::FUNCTION:ECH
SSL_CTX_set1_echstore ? 3_5_0 EXIST::FUNCTION:ECH
SSL_set1_echstore ? 3_5_0 EXIST::FUNCTION:ECH
SSL_CTX_get1_echstore ? 3_5_0 EXIST::FUNCTION:ECH
SSL_get1_echstore ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_get1_status ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_set_grease_type ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_set_callback ? 3_5_0 EXIST::FUNCTION:ECH
SSL_CTX_ech_raw_decrypt ? 3_5_0 EXIST::FUNCTION:ECH
SSL_CTX_ech_set_callback ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_new ? 3_5_0 EXIST::FUNCTION:ECH
SSL_set1_ech_config_list ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_set1_server_names ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_set1_outer_server_name ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_set1_outer_alpn_protos ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_set1_grease_suite ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_get1_retry_config ? 3_5_0 EXIST::FUNCTION:ECH
SSL_CTX_ech_set1_outer_alpn_protos ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECHSTORE_num_entries ? 3_5_0 EXIST::FUNCTION:ECH

View file

@ -99,6 +99,8 @@ use constant {
EXT_RENEGOTIATE => 65281,
EXT_NPN => 13172,
EXT_CRYPTOPRO_BUG_EXTENSION => 0xfde8,
EXT_ECH => 0xfe0d,
EXT_ECH_OUTER => 0xfd00,
EXT_UNKNOWN => 0xfffe,
#Unknown extension that should appear last
EXT_FORCE_LAST => 0xffff