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)
This commit is contained in:
sftcd 2024-10-10 17:46:11 +01:00 committed by Matt Caswell
parent 20644180d1
commit 8f8d218e3f
15 changed files with 1521 additions and 575 deletions

View File

@ -80,12 +80,10 @@ 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;
int select = OSSL_ECHSTORE_ALL, numinfiles = 0;
char *outfile = NULL, *infile = NULL;
char *infiles[OSSL_ECH_MAXINFILES] = { NULL };
int numinfiles = 0;
char *public_name = NULL;
char *suitestr = 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;
@ -154,17 +152,13 @@ int ech_main(int argc, char **argv)
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
*/
/* Check ECH-specific inputs */
switch (ech_version) {
case OSSL_ECH_RFCXXXX_VERSION: /* fall through */
case 13:
@ -174,7 +168,6 @@ int ech_main(int argc, char **argv)
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,
@ -182,7 +175,6 @@ int ech_main(int argc, char **argv)
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);
@ -190,7 +182,6 @@ int ech_main(int argc, char **argv)
goto end;
}
}
/* Set default if needed */
if (outfile == NULL)
outfile = "echconfig.pem";
@ -211,7 +202,6 @@ int ech_main(int argc, char **argv)
BIO_printf(bio_err, "OSSL_ECHSTORE_new_config success\n");
rv = 0;
}
if (mode == OSSL_ECH_SELPRINT_MODE) {
if (numinfiles == 0)
goto opthelp;
@ -245,28 +235,35 @@ int ech_main(int argc, char **argv)
}
rv = 0;
}
if (text) {
OSSL_ECH_INFO *oi = NULL;
int oi_ind, oi_cnt = 0;
if (OSSL_ECHSTORE_get1_info(es, &oi, &oi_cnt) != 1)
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++) {
if (OSSL_ECH_INFO_print(bio_out, oi, oi_ind) != 1) {
BIO_printf(bio_err, "OSSL_ECH_INFO_print error entry (%d)\n",
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);
}
OSSL_ECH_INFO_free(oi, oi_cnt);
if (verbose)
BIO_printf(bio_err, "Success printing %d ECHConfigList\n", oi_cnt);
rv = 0;
}
end:
OSSL_ECHSTORE_free(es);
BIO_free_all(ecf);

View File

@ -100,7 +100,7 @@ static int configure_ech(SSL_CTX *ctx, int server,
OSSL_ECHSTORE *es = NULL;
BIO *es_in = BIO_new_mem_buf(buf, len);
if (es_in == NULL || (es = OSSL_ECHSTORE_init(NULL, NULL)) == NULL)
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;

View File

@ -4,9 +4,10 @@ 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 is based on another [prototype
branch](https://github.com/sftcd/openssl/tree/ECHStore-1) that is new.
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
@ -217,13 +218,15 @@ 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, OSSL_ECH_INFO **info,
int *count);
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);
```
@ -245,15 +248,8 @@ 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.
Generally, clients will deal with "singleton" ECHConfigList values, but it is
also possible (in multi-CDN or multi-algorithm cases), that a client may need
more fine-grained control of which ECHConfig from a set to use for a particular
TLS connection. Clients that only support a subset of algorithms can
automatically make such decisions, however, a client faced with a set of HTTPS
RR values might (in theory) need to match (in particular) the server IP address
for the connection to the ECHConfig value via the `public_name` field within
the ECHConfig value. To enable this selection, the `OSSL_ECHSTORE_get1_info()`
API presents the client with the information enabling such selection, and the
`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).
@ -264,13 +260,14 @@ 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_keys()` allows a server to see how many usable ECH 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.
`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:
@ -284,6 +281,15 @@ 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:
@ -295,44 +301,6 @@ 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.
Finer-grained client control
----------------------------
TODO(ECH): revisit this later, when we hopefully have some more information
about ECH deployments.
Applications that need fine control over which ECHConfigList (from those
available) will be used, can query an `OSSL_ECHSTORE`, retrieving information
about the set of "singleton" ECHConfigList values available, and then, if
desired, down-select to one of those, e.g., based on the `public_name` that
will be used. This would enable a client that selects the server address to use
based on IP address hints that can also be present in an HTTPS/SCVB resource
record to ensure that the correct matching ECH public value is used. The
information is presented to the caller using the `OSSL_ECH_INFO` type, which
provides a simplified view of ECH data, but where each element of an array
corresponds to exactly one ECH public value and set of names.
```c
/*
* Application-visible form of ECH information from the DNS, from config
* files, or from earlier API calls. APIs produce/process an array of these.
*/
typedef struct ossl_ech_info_st {
int index; /* externally re-usable reference to this value */
char *public_name; /* public_name from API or ECHConfig */
char *inner_name; /* server-name (for inner CH if doing ECH) */
unsigned char *outer_alpns; /* outer ALPN string */
size_t outer_alpns_len;
unsigned char *inner_alpns; /* inner ALPN string */
size_t inner_alpns_len;
char *echconfig; /* a JSON-like version of the associated ECHConfig */
int has_private_key; /* 0 if we don't have a related private key */
} OSSL_ECH_INFO;
void OSSL_ECH_INFO_free(OSSL_ECH_INFO *info, int count);
int OSSL_ECH_INFO_print(BIO *out, OSSL_ECH_INFO *info, int count);
```
ECH Store Internals
-------------------
@ -357,21 +325,20 @@ typedef struct ossl_echstore_entry_st {
uint8_t max_name_length;
uint8_t config_id;
STACK_OF(OSSL_ECHEXT) *exts;
char *pemfname; /* name of PEM file from which this was loaded */
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;
} OSSL_ECHSTORE_ENTRY;
DEFINE_STACK_OF(OSSL_ECHSTORE_entry)
DEFINE_STACK_OF(OSSL_ECHSTORE_ENTRY)
typedef struct ossl_echstore_st {
STACK_OF(OSSL_ECHSTORE_entry) *entries;
struct ossl_echstore_st {
STACK_OF(OSSL_ECHSTORE_ENTRY) *entries;
OSSL_LIB_CTX *libctx;
const char *propq;
} OSSL_ECHSTORE;
};
```
Some notes on the above ECHConfig fields:
@ -471,17 +438,7 @@ 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, ascii hex or binary) each of which may suit different applications.
ECHConfigList values may also be provided embedded in the DNS wire encoding of
HTTPS or SVCB resource records or in the equivalent zone file presentation
format.
`OSSL_ECHSTORE_find_echconfigs()` attempts to find and return the (possibly empty)
set of ECHConfigList values as an `OSSL_ECHSTORE` from the input `BIO`.
```c
OSSL_ECHSTORE *OSSL_ECHSTORE_find_echconfigs(BIO *in);
```
(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
@ -500,7 +457,7 @@ 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);
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);
@ -599,28 +556,6 @@ The following options are defined for ECH and may be set via
#define SSL_OP_ECH_GREASE_RETRY_CONFIG SSL_OP_BIT(39)
```
A Note on `_get_`,`_get0_`,`_get1_`,`_set_`,`_set0_`,`_set1_`
-------------------------------------------------------------
TODO(ECH): This text will likely disappear as things settle.
The abstraction behind the `_get_`,`_get0_`,`_get1_`,`_set_`,`_set0_`,`_set1_`
convention used in OpenSSL APIs is somewhat non-obvious, (but is what it is),
so some words of explanation of the function names above may be useful, partly
as a check that those usages are consistent with other APIs:
- `_set_` is appropriate where the input/output type(s) are basic and involve
no type-specific memory management (e.g. `SSL_set_enable_ech_grease`)
- there are no uses of `_get_` or `_get0_` above
- `_get1_` is appropriate when a pointer to a complex type is being returned
that may be modified and must be free'd by the application, e.g.
`OSSL_ECHSTORE_get1_info`.
- `_set0_` is also unused above, because...
- the `_set1_` variant seems easier to handle for the application ("with ECH
stuff, if you make it then give it to the library, you still need to free
it") and for consistency amongst these APIs, so that is often used, e.g.
`OSSL_ECHSTORE_set1_key_and_read_pem`.
Build Options
-------------
@ -650,16 +585,6 @@ 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.
### Setting an ECHConfigList
```c
OPENSSL_EXPORT int SSL_set1_ech_config_list(SSL *ssl,
const uint8_t *ech_config_list,
size_t ech_config_list_len);
```
This provides a subset of the equivalent client capabilities from our fork.
### Verifying the outer CH rather than inner
BoringSSL seems to use this API to change the DNS name being verified in order
@ -696,7 +621,6 @@ OPENSSL_EXPORT int SSL_ECH_KEYS_add(SSL_ECH_KEYS *keys, int is_retry_config,
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()`.
@ -710,8 +634,6 @@ ECH keys using:
OPENSSL_EXPORT int SSL_CTX_set1_ech_keys(SSL_CTX *ctx, SSL_ECH_KEYS *keys);
```
This is similar to the `SSL_CTX_ech_server_enable_*()` APIs.
### Getting status
BoringSSL has:

View File

@ -7,14 +7,15 @@ 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_keys, OSSL_ECHSTORE_flush_keys,
OSSL_ECH_INFO_free, OSSL_ECH_INFO_print, SSL_CTX_set1_echstore,
SSL_CTX_get1_echstore, SSL_get1_echstore, SSL_ech_set_server_names,
SSL_ech_set_outer_server_name, SSL_ech_set_outer_alpn_protos,
SSL_ech_get1_status, SSL_ech_set_grease_suite, SSL_ech_set_grease_type,
SSL_ech_set_callback, SSL_ech_get_retry_config,
SSL_CTX_ech_set_outer_alpn_protos, SSL_CTX_ech_raw_decrypt,
SSL_CTX_ech_set_callback
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
@ -28,32 +29,30 @@ SSL_CTX_ech_set_callback
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, OSSL_ECH_INFO **info,
int *count);
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);
void OSSL_ECH_INFO_free(OSSL_ECH_INFO *info, int count);
int OSSL_ECH_INFO_print(BIO *out, OSSL_ECH_INFO *info, int count);
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_set_server_names(SSL *s, const char *inner_name,
const char *outer_name, int no_outer);
int SSL_ech_set_outer_server_name(SSL *s, const char *outer_name, int no_outer);
int SSL_ech_set_outer_alpn_protos(SSL *s, const unsigned char *protos,
const size_t protos_len);
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_set_grease_suite(SSL *s, const char *suite);
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_get_retry_config(SSL *s, unsigned char **ec, size_t *eclen);
int SSL_CTX_ech_set_outer_alpn_protos(SSL_CTX *s, const unsigned char *protos,
const size_t protos_len);
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,
@ -61,10 +60,18 @@ SSL_CTX_ech_set_callback
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): Text is TBD, this is just enough for the build.
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
@ -77,23 +84,23 @@ 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 OSSL_ECH_INFO_free() is a thing
Mention OSSL_ECH_INFO_print() 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_set_server_names() is a thing
Mention SSL_ech_set_outer_server_name() is a thing
Mention SSL_ech_set_outer_alpn_protos() 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_set_grease_suite() 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_get_retry_config() is a thing
Mention SSL_CTX_ech_set_outer_alpn_protos() 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
@ -137,7 +144,10 @@ An example string I<str> as seen on a client might be:
=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
@ -148,9 +158,8 @@ 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
OSSL_ECH_INFO_free() returns zero on error
OSSL_ECH_INFO_print() 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
@ -162,10 +171,15 @@ 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_set_outer_alpn_protos() 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/>
@ -173,7 +187,7 @@ TODO(ECH) update link to RFC.
=head1 HISTORY
This functionality described here was added in OpenSSL 3.4.
The functionality described here was added in OpenSSL 3.5.
=head1 COPYRIGHT

View File

@ -53,31 +53,14 @@
# 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 */
# 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
/*
* Application-visible form of ECH information from the DNS, from config
* files, or from earlier API calls. APIs produce/process an array of these.
*/
typedef struct ossl_ech_info_st {
int index; /* externally re-usable reference to this value */
time_t seconds_in_memory; /* number of seconds since this was loaded */
char *public_name; /* public_name from API or ECHConfig */
char *inner_name; /* server-name (for inner CH if doing ECH) */
unsigned char *outer_alpns; /* outer ALPN string */
size_t outer_alpns_len;
unsigned char *inner_alpns; /* inner ALPN string */
size_t inner_alpns_len;
char *echconfig; /* a JSON-like version of the associated ECHConfig */
int has_private_key; /* 0 if we don't have a related private key */
} OSSL_ECH_INFO;
/* Values for the for_retry inputs */
# define OSSL_ECH_FOR_RETRY 1
# define OSSL_ECH_NO_RETRY 0
@ -92,18 +75,17 @@ int OSSL_ECHSTORE_new_config(OSSL_ECHSTORE *es,
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, OSSL_ECH_INFO **info,
int *count);
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);
void OSSL_ECH_INFO_free(OSSL_ECH_INFO *info, int count);
int OSSL_ECH_INFO_print(BIO *out, OSSL_ECH_INFO *info, int count);
/*
* APIs relating OSSL_ECHSTORE to SSL/SSL_CTX
*/
@ -113,21 +95,31 @@ 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_set_server_names(SSL *s, const char *inner_name,
const char *outer_name, int no_outer);
int SSL_ech_set_outer_server_name(SSL *s, const char *outer_name, int no_outer);
int SSL_ech_set_outer_alpn_protos(SSL *s, const unsigned char *protos,
const size_t protos_len);
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_set_grease_suite(SSL *s, const char *suite);
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_get_retry_config(SSL *s, unsigned char **ec, size_t *eclen);
int SSL_ech_get1_retry_config(SSL *s, unsigned char **ec, size_t *eclen);
int SSL_CTX_ech_set_outer_alpn_protos(SSL_CTX *s, const unsigned char *protos,
const size_t protos_len);
/*
* 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,
@ -135,6 +127,7 @@ int SSL_CTX_ech_raw_decrypt(SSL_CTX *ctx,
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

@ -12,4 +12,152 @@
#include "../ssl_local.h"
#include "ech_local.h"
/* TODO(ECH): move ECH internal code here when we get to it */
#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

View File

@ -31,6 +31,16 @@
*/
# 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
@ -83,7 +93,6 @@ typedef struct ossl_echstore_entry_st {
uint8_t max_name_length;
uint8_t config_id;
STACK_OF(OSSL_ECHEXT) *exts;
char *pemfname; /* name of PEM file from which this was loaded */
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 */
@ -96,8 +105,118 @@ DEFINE_STACK_OF(OSSL_ECHSTORE_ENTRY)
struct ossl_echstore_st {
STACK_OF(OSSL_ECHSTORE_ENTRY) *entries;
OSSL_LIB_CTX *libctx;
const char *propq;
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

View File

@ -13,70 +13,350 @@
int SSL_CTX_set1_echstore(SSL_CTX *ctx, OSSL_ECHSTORE *es)
{
return 0;
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 *s, OSSL_ECHSTORE *es)
int SSL_set1_echstore(SSL *ssl, OSSL_ECHSTORE *es)
{
return 0;
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)
{
return NULL;
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 *s)
OSSL_ECHSTORE *SSL_get1_echstore(const SSL *ssl)
{
return NULL;
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_set_server_names(SSL *s, const char *inner_name,
const char *outer_name, int no_outer)
int SSL_ech_set1_server_names(SSL *ssl, const char *inner_name,
const char *outer_name, int no_outer)
{
return 0;
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_set_outer_server_name(SSL *s, const char *outer_name, int no_outer)
int SSL_ech_set1_outer_server_name(SSL *ssl, const char *outer_name,
int no_outer)
{
return 0;
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;
}
int SSL_ech_set_outer_alpn_protos(SSL *s, const unsigned char *protos,
const size_t protos_len)
/*
* 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)
{
return 0;
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 *s, char **inner_sni, char **outer_sni)
int SSL_ech_get1_status(SSL *ssl, char **inner_sni, char **outer_sni)
{
return 0;
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_set_grease_suite(SSL *s, const char *suite)
int SSL_ech_set1_grease_suite(SSL *ssl, const char *suite)
{
return 0;
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 *s, uint16_t type)
int SSL_ech_set_grease_type(SSL *ssl, uint16_t type)
{
return 0;
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 *s, SSL_ech_cb_func f)
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_get_retry_config(SSL *s, unsigned char **ec, size_t *eclen)
int SSL_ech_get1_retry_config(SSL *ssl, unsigned char **ec, size_t *eclen)
{
return 0;
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;
}
int SSL_CTX_ech_set_outer_alpn_protos(SSL_CTX *s, const unsigned char *protos,
const size_t protos_len)
/*
* 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)
{
return 0;
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,
@ -102,5 +382,45 @@ int SSL_CTX_ech_raw_decrypt(SSL_CTX *ctx,
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;
}

View File

@ -58,7 +58,7 @@ static const char B64_alphabet[] =
* local functions - public APIs are at the end
*/
static void ossl_echext_free(OSSL_ECHEXT *e)
void ossl_echext_free(OSSL_ECHEXT *e)
{
if (e == NULL)
return;
@ -67,13 +67,30 @@ static void ossl_echext_free(OSSL_ECHEXT *e)
return;
}
static void ossl_echstore_entry_free(OSSL_ECHSTORE_ENTRY *ee)
OSSL_ECHEXT *ossl_echext_dup(const OSSL_ECHEXT *src)
{
OSSL_ECHEXT *ext = OPENSSL_zalloc(sizeof(*src));
if (ext == NULL)
return NULL;
*ext = *src;
ext->val = NULL;
if (ext->len != 0) {
ext->val = OPENSSL_memdup(src->val, src->len);
if (ext->val == NULL) {
ossl_echext_free(ext);
return NULL;
}
}
return ext;
}
void ossl_echstore_entry_free(OSSL_ECHSTORE_ENTRY *ee)
{
if (ee == NULL)
return;
OPENSSL_free(ee->public_name);
OPENSSL_free(ee->pub);
OPENSSL_free(ee->pemfname);
EVP_PKEY_free(ee->keyshare);
OPENSSL_free(ee->encoded);
OPENSSL_free(ee->suites);
@ -82,32 +99,6 @@ static void ossl_echstore_entry_free(OSSL_ECHSTORE_ENTRY *ee)
return;
}
/*
* @brief hash a buffer as a pretend file name being ascii-hex of hashed buffer
* @param es is the OSSL_ECHSTORE we're dealing with
* @param buf is the input buffer
* @param blen is the length of buf
* @param ah_hash is a pointer to where to put the result
* @param ah_len is the length of ah_hash
*/
static int ech_hash_pub_as_fname(OSSL_ECHSTORE *es,
const unsigned char *buf, size_t blen,
char *ah_hash, size_t ah_len)
{
unsigned char hashval[EVP_MAX_MD_SIZE];
size_t hashlen, actual_ah_len;
if (es == NULL
|| EVP_Q_digest(es->libctx, "SHA2-256", es->propq,
buf, blen, hashval, &hashlen) != 1
|| OPENSSL_buf2hexstr_ex(ah_hash, ah_len, &actual_ah_len,
hashval, hashlen, '\0') != 1) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
return 1;
}
/*
* @brief Read a buffer from an input 'till eof
* @param in is the BIO input
@ -618,7 +609,14 @@ OSSL_ECHSTORE *OSSL_ECHSTORE_new(OSSL_LIB_CTX *libctx, const char *propq)
return 0;
}
es->libctx = libctx;
es->propq = propq;
if (propq != NULL) {
es->propq = OPENSSL_strdup(propq);
if (es->propq == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
}
return es;
}
@ -627,6 +625,7 @@ void OSSL_ECHSTORE_free(OSSL_ECHSTORE *es)
if (es == NULL)
return;
sk_OSSL_ECHSTORE_ENTRY_pop_free(es->entries, ossl_echstore_entry_free);
OPENSSL_free(es->propq);
OPENSSL_free(es);
return;
}
@ -645,8 +644,6 @@ int OSSL_ECHSTORE_new_config(OSSL_ECHSTORE *es,
WPACKET epkt;
BUF_MEM *epkt_mem = NULL;
OSSL_ECHSTORE_ENTRY *ee = NULL;
char pembuf[2 * EVP_MAX_MD_SIZE + 1];
size_t pembuflen = 2 * EVP_MAX_MD_SIZE + 1;
/* basic checks */
if (es == NULL) {
@ -754,10 +751,6 @@ int OSSL_ECHSTORE_new_config(OSSL_ECHSTORE *es,
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
if (ech_hash_pub_as_fname(es, pub, publen, pembuf, pembuflen) != 1) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
ee->version = echversion;
ee->pub_len = publen;
ee->pub = OPENSSL_memdup(pub, publen);
@ -780,11 +773,6 @@ int OSSL_ECHSTORE_new_config(OSSL_ECHSTORE *es,
ee->encoded_len = bblen;
epkt_mem->data = NULL;
epkt_mem->length = 0;
ee->pemfname = OPENSSL_strdup(pembuf);
if (ee->pemfname == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
ee->loadtime = time(0);
/* push entry into store */
if (es->entries == NULL)
@ -899,52 +887,54 @@ int OSSL_ECHSTORE_read_echconfiglist(OSSL_ECHSTORE *es, BIO *in)
return ech_read_priv_echconfiglist(es, in, NULL, 0);
}
int OSSL_ECHSTORE_get1_info(OSSL_ECHSTORE *es, OSSL_ECH_INFO **info,
int *count)
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)
{
OSSL_ECH_INFO *linfo = NULL, *inst = NULL;
OSSL_ECHSTORE_ENTRY *ee = NULL;
unsigned int i = 0, j = 0, num = 0;
unsigned int j = 0;
int num = 0;
BIO *out = NULL;
time_t now = time(0);
size_t ehlen;
unsigned char *ignore = NULL;
if (es == NULL || info == NULL || count == NULL) {
if (es == NULL || loaded_secs == NULL || public_name == NULL
|| echconfig == NULL || has_private == NULL || for_retry == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
num = (es->entries == NULL ? 0 : sk_OSSL_ECHSTORE_ENTRY_num(es->entries));
if (num == 0) {
*info = NULL;
*count = 0;
return 1;
if (num == 0 || index < 0 || index >= num) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
linfo = OPENSSL_zalloc(num * sizeof(*linfo));
if (linfo == NULL)
goto err;
for (i = 0; i != num; i++) {
inst = &linfo[i];
ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, i);
inst->index = i;
inst->seconds_in_memory = now - ee->loadtime;
inst->public_name = OPENSSL_strdup(ee->public_name);
inst->has_private_key = (ee->keyshare == NULL ? 0 : 1);
/* Now "print" the ECHConfigList */
out = BIO_new(BIO_s_mem());
if (out == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
ee = sk_OSSL_ECHSTORE_ENTRY_value(es->entries, index);
if (ee == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
*loaded_secs = now - ee->loadtime;
if (ee->public_name != NULL) {
*public_name = OPENSSL_strdup(ee->public_name);
if (*public_name == NULL)
goto err;
}
if (ee->version != OSSL_ECH_RFCXXXX_VERSION) {
/* just note we don't support that one today */
BIO_printf(out, "[Unsupported version (%04x)]", ee->version);
continue;
}
} else {
*public_name = NULL;
}
*has_private = (ee->keyshare == NULL ? 0 : 1);
/* Now "print" the ECHConfigList */
out = BIO_new(BIO_s_mem());
if (out == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
if (ee->version != OSSL_ECH_RFCXXXX_VERSION) {
/* just note we don't support that one today */
BIO_printf(out, "[Unsupported version (%04x)]", ee->version);
} else {
/* version, config_id, public_name, and kem */
BIO_printf(out, "[%04x,%02x,%s,[", ee->version,
ee->config_id,
BIO_printf(out, "[%04x,%02x,%s,[", ee->version, ee->config_id,
ee->public_name != NULL ? (char *)ee->public_name : "NULL");
/* ciphersuites */
for (j = 0; j != ee->nsuites; j++) {
@ -960,24 +950,20 @@ int OSSL_ECHSTORE_get1_info(OSSL_ECHSTORE *es, OSSL_ECH_INFO **info,
/* max name length and (only) number of extensions */
BIO_printf(out, ",%02x,%02x]", ee->max_name_length,
ee->exts == NULL ? 0 : sk_OSSL_ECHEXT_num(ee->exts));
ehlen = BIO_get_mem_data(out, &ignore);
inst->echconfig = OPENSSL_malloc(ehlen + 1);
if (inst->echconfig == NULL)
goto err;
if (BIO_read(out, inst->echconfig, ehlen) <= 0) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
goto err;
}
inst->echconfig[ehlen] = '\0';
BIO_free(out);
out = NULL;
}
*count = num;
*info = linfo;
ehlen = BIO_get_mem_data(out, &ignore);
*echconfig = OPENSSL_malloc(ehlen + 1);
if (*echconfig == NULL)
goto err;
if (BIO_read(out, *echconfig, ehlen) <= 0) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
(*echconfig)[ehlen] = '\0';
BIO_free(out);
return 1;
err:
BIO_free(out);
OSSL_ECH_INFO_free(linfo, num);
return 0;
}
@ -1085,6 +1071,16 @@ err:
return rv;
}
int OSSL_ECHSTORE_num_entries(const OSSL_ECHSTORE *es, int *numentries)
{
if (es == NULL || numentries == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
*numentries = (es->entries == NULL ? 0 : sk_OSSL_ECHSTORE_ENTRY_num(es->entries));
return 1;
}
int OSSL_ECHSTORE_num_keys(OSSL_ECHSTORE *es, int *numkeys)
{
int i, num = 0, count = 0;
@ -1135,34 +1131,3 @@ int OSSL_ECHSTORE_flush_keys(OSSL_ECHSTORE *es, time_t age)
}
return 1;
}
void OSSL_ECH_INFO_free(OSSL_ECH_INFO *info, int count)
{
int i;
if (info == NULL)
return;
for (i = 0; i != count; i++) {
OPENSSL_free(info[i].public_name);
OPENSSL_free(info[i].inner_name);
OPENSSL_free(info[i].outer_alpns);
OPENSSL_free(info[i].inner_alpns);
OPENSSL_free(info[i].echconfig);
}
OPENSSL_free(info);
return;
}
int OSSL_ECH_INFO_print(BIO *out, OSSL_ECH_INFO *info, int index)
{
if (out == NULL || info == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
BIO_printf(out, "ECH entry: %d public_name: %s age: %d%s\n",
index, info[index].public_name,
(int)info[index].seconds_in_memory,
info[index].has_private_key ? " (has private key)" : "");
BIO_printf(out, "\t%s\n", info[index].echconfig);
return 1;
}

View File

@ -915,6 +915,11 @@ SSL *ossl_ssl_connection_new_int(SSL_CTX *ctx, const SSL_METHOD *method)
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:
@ -1491,6 +1496,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)
@ -4246,6 +4254,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
@ -1666,6 +1672,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;
/*

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

View File

@ -16,8 +16,102 @@
# define DEF_CERTS_DIR "test/certs"
static OSSL_LIB_CTX *libctx = NULL;
static char *propq = NULL;
static int verbose = 0;
static char *certsdir = NULL;
static char *cert = NULL;
static char *privkey = NULL;
static char *rootcert = NULL;
/* callback */
static unsigned int test_cb(SSL *s, const char *str)
{
return 1;
}
/*
* The define/vars below and the 3 callback functions are modified
* from test/sslapitest.c
*/
# define TEST_EXT_TYPE1 0xffab /* custom ext type 1: has 1 octet payload */
# define TEST_EXT_TYPE2 0xffcd /* custom ext type 2: no payload */
/* A well-encoded ECH extension value */
static const unsigned char encoded_ech_val[] = {
0x00, 0x00, 0x01, 0x00, 0x01, 0xf7, 0x00, 0x20,
0xc9, 0x2c, 0x12, 0xc9, 0xc0, 0x4d, 0x11, 0x5d,
0x09, 0xe1, 0xeb, 0x7a, 0x18, 0xb2, 0x83, 0x28,
0x35, 0x00, 0x3c, 0x8d, 0x78, 0x09, 0xfd, 0x09,
0x84, 0xca, 0x94, 0x77, 0xcf, 0x78, 0xd0, 0x04,
0x00, 0x90, 0x5e, 0xc7, 0xc0, 0x62, 0x84, 0x8d,
0x4b, 0x85, 0xd5, 0x6a, 0x9a, 0xc1, 0xc6, 0xc2,
0x28, 0xac, 0x87, 0xb9, 0x2f, 0x36, 0xa0, 0xf7,
0x5f, 0xd0, 0x23, 0x7b, 0xf4, 0xc1, 0x62, 0x1c,
0xf1, 0x91, 0xfd, 0x46, 0x35, 0x41, 0xc9, 0x06,
0xd3, 0x19, 0xd6, 0x34, 0x01, 0xc3, 0xb3, 0x66,
0x4e, 0x7a, 0x28, 0xac, 0xd4, 0xd2, 0x35, 0x2b,
0xd0, 0xc6, 0x94, 0x34, 0xc1, 0x94, 0x62, 0x77,
0x1b, 0x5a, 0x02, 0x3c, 0xdd, 0xa2, 0x4d, 0x33,
0xa5, 0xd0, 0x59, 0x12, 0xf5, 0x17, 0x03, 0xe5,
0xab, 0xbd, 0x83, 0x52, 0x40, 0x6c, 0x99, 0xac,
0x25, 0x07, 0x63, 0x8c, 0x16, 0x5d, 0x93, 0x34,
0x56, 0x34, 0x60, 0x86, 0x25, 0xa7, 0x0d, 0xac,
0xb8, 0x5e, 0x87, 0xc6, 0xf7, 0x23, 0xaf, 0xf8,
0x3e, 0x2a, 0x46, 0x75, 0xa9, 0x5f, 0xaf, 0xd2,
0x91, 0xe6, 0x44, 0xcb, 0xe7, 0xe0, 0x85, 0x36,
0x9d, 0xd2, 0xaf, 0xae, 0xb3, 0x0f, 0x70, 0x6a,
0xaf, 0x42, 0xc0, 0xb3, 0xe4, 0x65, 0x53, 0x01,
0x75, 0xbf
};
static int new_add_cb(SSL *s, unsigned int ext_type, unsigned int context,
const unsigned char **out, size_t *outlen, X509 *x,
size_t chainidx, int *al, void *add_arg)
{
int *server = (int *)add_arg;
unsigned char *data;
if (*server != SSL_is_server(s))
return -1;
if (ext_type == TEST_EXT_TYPE1) {
if ((data = OPENSSL_malloc(sizeof(*data))) == NULL)
return -1;
*data = 1;
*out = data;
*outlen = sizeof(*data);
} else if (ext_type == OSSL_ECH_CURRENT_VERSION) {
/* inject a sample ECH extension value into the CH */
if ((data = OPENSSL_memdup(encoded_ech_val,
sizeof(encoded_ech_val))) == NULL)
return -1;
*out = data;
*outlen = sizeof(encoded_ech_val);
} else {
/* inject a TEST_EXT_TYPE2, with a zero-length payload */
*out = NULL;
*outlen = 0;
}
return 1;
}
static void new_free_cb(SSL *s, unsigned int ext_type, unsigned int context,
const unsigned char *out, void *add_arg)
{
OPENSSL_free((unsigned char *)out);
}
static int new_parse_cb(SSL *s, unsigned int ext_type, unsigned int context,
const unsigned char *in, size_t inlen, X509 *x,
size_t chainidx, int *al, void *parse_arg)
{
int *server = (int *)parse_arg;
if (*server != SSL_is_server(s)
|| inlen != sizeof(char) || *in != 1)
return -1;
return 1;
}
/* general test vector values */
@ -688,6 +782,17 @@ static fnt_t fnames[] = {
{ "ech-rsa.pem", 0 },
};
/* string from which we construct varieties of HPKE suite */
static const char *kem_str_list[] = {
"P-256", "P-384", "P-521", "x25519", "x448",
};
static const char *kdf_str_list[] = {
"hkdf-sha256", "hkdf-sha384", "hkdf-sha512",
};
static const char *aead_str_list[] = {
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305",
};
typedef enum OPTION_choice {
OPT_ERR = -1,
OPT_EOF = 0,
@ -719,11 +824,11 @@ const OPTIONS *test_get_options(void)
static int ech_ingest_test(int run)
{
OSSL_ECHSTORE *es = NULL;
OSSL_ECH_INFO *ei = NULL;
BIO *in = NULL, *out = NULL;
int i, rv = 0, keysb4, keysaftr, actual_ents = 0;
int i, rv = 0, keysb4, keysaftr, actual_ents = 0, has_priv, for_retry;
ingest_tv_t *tv = &ingest_tvs[run];
time_t now = 0;
time_t now = 0, secs = 0;
char *pn = NULL, *ec = NULL;
if ((in = BIO_new(BIO_s_mem())) == NULL
|| BIO_write(in, tv->tv, tv->len) <= 0
@ -739,97 +844,56 @@ static int ech_ingest_test(int run)
}
if (tv->pemenc == 1
&& !TEST_int_eq(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_NO_RETRY),
tv->read)) {
TEST_info("OSSL_ECHSTORE_read_pem unexpected result");
tv->read))
goto end;
}
if (tv->pemenc != 1
&& !TEST_int_eq(OSSL_ECHSTORE_read_echconfiglist(es, in),
tv->read)) {
TEST_info("OSSL_ECHSTORE_read_echconfiglist unexpected result");
&& !TEST_int_eq(OSSL_ECHSTORE_read_echconfiglist(es, in), tv->read))
goto end;
}
/* if we provided a deliberately bad tv then we're done */
if (tv->read != 1) {
rv = 1;
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_num_keys(es, &keysb4), 1)) {
TEST_info("OSSL_ECHSTORE_num_keys unexpected fail");
if (!TEST_true(OSSL_ECHSTORE_num_keys(es, &keysb4))
|| !TEST_true(OSSL_ECHSTORE_num_entries(es, &actual_ents))
|| !TEST_int_eq(keysb4, tv->keysb4)
|| !TEST_int_eq(actual_ents, tv->entsb4)
|| !TEST_int_eq(OSSL_ECHSTORE_get1_info(es, -1, &secs, &pn, &ec,
&has_priv, &for_retry), 0))
goto end;
}
if (!TEST_int_eq(keysb4, tv->keysb4)) {
TEST_info("OSSL_ECHSTORE_num_keys unexpected number of keys (b4)");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_get1_info(es, &ei, &actual_ents), 1)) {
TEST_info("OSSL_ECHSTORE_get1_info unexpected fail");
goto end;
}
OPENSSL_free(pn);
pn = NULL;
OPENSSL_free(ec);
ec = NULL;
for (i = 0; i != actual_ents; i++) {
if (!TEST_int_eq(OSSL_ECH_INFO_print(bio_err, ei, i), 1)) {
TEST_info("OSSL_ECH_INFO_print unexpected fail");
OSSL_ECH_INFO_free(ei, actual_ents);
if (!TEST_true(OSSL_ECHSTORE_get1_info(es, i, &secs, &pn, &ec,
&has_priv, &for_retry)))
goto end;
}
OPENSSL_free(pn);
pn = NULL;
OPENSSL_free(ec);
ec = NULL;
}
if (!TEST_int_eq(actual_ents, tv->entsb4)) {
TEST_info("OSSL_ECHSTORE_get1_info unexpected number of entries (b4)");
goto end;
}
OSSL_ECH_INFO_free(ei, actual_ents);
ei = NULL;
/* ensure silly index fails ok */
if (!TEST_int_eq(OSSL_ECHSTORE_downselect(es, -20), 0)) {
TEST_info("OSSL_ECHSTORE_downselect unexpected non-zero");
if (!TEST_false(OSSL_ECHSTORE_downselect(es, -20))
|| !TEST_int_eq(OSSL_ECHSTORE_downselect(es, tv->index), tv->expected)
|| !TEST_true(OSSL_ECHSTORE_num_keys(es, &keysaftr))
|| !TEST_int_eq(keysaftr, tv->keysaftr)
|| !TEST_true(OSSL_ECHSTORE_num_entries(es, &actual_ents))
|| !TEST_int_eq(actual_ents, tv->entsaftr)
|| !TEST_true(OSSL_ECHSTORE_write_pem(es, OSSL_ECHSTORE_LAST, out))
|| !TEST_true(OSSL_ECHSTORE_write_pem(es, OSSL_ECHSTORE_ALL, out))
|| !TEST_false(OSSL_ECHSTORE_write_pem(es, 100, out)))
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_downselect(es, tv->index), tv->expected)) {
TEST_info("OSSL_ECHSTORE_downselect unexpected result");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_num_keys(es, &keysaftr), 1)) {
TEST_info("OSSL_ECHSTORE_num_keys unexpected fail");
goto end;
}
if (!TEST_int_eq(keysaftr, tv->keysaftr)) {
TEST_info("OSSL_ECHSTORE_num_keys unexpected number of keys (aftr)");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_get1_info(es, &ei, &actual_ents), 1)) {
TEST_info("OSSL_ECHSTORE_get1_info unexpected fail");
goto end;
}
OSSL_ECH_INFO_free(ei, actual_ents);
ei = NULL;
if (!TEST_int_eq(actual_ents, tv->entsaftr)) {
TEST_info("OSSL_ECHSTORE_get1_info unexpected number of entries (aftr)");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_write_pem(es, OSSL_ECHSTORE_ALL, out), 1)) {
TEST_info("OSSL_ECHSTORE_write_pem unexpected fail");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_write_pem(es, 100, out), 0)) {
TEST_info("OSSL_ECHSTORE_write_pem unexpected result");
goto end;
}
now = time(0);
if (!TEST_int_eq(OSSL_ECHSTORE_flush_keys(es, now), 1)) {
TEST_info("OSSL_ECHSTORE_flush_keys unexpected fail");
if (!TEST_true(OSSL_ECHSTORE_flush_keys(es, now))
|| !TEST_true(OSSL_ECHSTORE_num_keys(es, &keysaftr))
|| !TEST_false(keysaftr))
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_num_keys(es, &keysaftr), 1)) {
TEST_info("OSSL_ECHSTORE_num_keys unexpected fail");
goto end;
}
if (!TEST_int_eq(keysaftr, 0)) {
TEST_info("OSSL_ECHSTORE_flush_keys unexpected non-zero");
goto end;
}
rv = 1;
end:
OSSL_ECH_INFO_free(ei, actual_ents);
OPENSSL_free(pn);
OPENSSL_free(ec);
OSSL_ECHSTORE_free(es);
BIO_free_all(in);
BIO_free_all(out);
@ -839,155 +903,54 @@ end:
/* make a bunch of calls with bad, mostly NULL, arguments */
static int ech_store_null_calls(void)
{
int rv = 0, count = 0;
int rv = 0, count = 0, has_priv, for_retry;
OSSL_ECHSTORE *es = OSSL_ECHSTORE_new(NULL, NULL);
OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
BIO *inout = BIO_new(BIO_s_mem());
OSSL_ECH_INFO *info = NULL;
EVP_PKEY *priv = EVP_PKEY_new();
time_t secs;
char *pn = NULL, *ec = NULL;
OSSL_ECHSTORE_free(NULL);
if (!TEST_int_eq(OSSL_ECHSTORE_new_config(NULL, OSSL_ECH_CURRENT_VERSION,
0, "example.com", hpke_suite),
0)) {
TEST_info("OSSL_ECHSTORE_new_config unexpected non-zero");
if (!TEST_false(OSSL_ECHSTORE_new_config(NULL, OSSL_ECH_CURRENT_VERSION,
0, "example.com", hpke_suite))
|| !TEST_false(OSSL_ECHSTORE_new_config(es, OSSL_ECH_CURRENT_VERSION,
0, NULL, hpke_suite))
|| !TEST_false(OSSL_ECHSTORE_new_config(es, 0xffff, 0,
"example.com", hpke_suite)))
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_new_config(es, OSSL_ECH_CURRENT_VERSION,
0, NULL, hpke_suite),
0)) {
TEST_info("OSSL_ECHSTORE_new_config unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_new_config(es, 0xffff,
0, "example.com", hpke_suite),
0)) {
TEST_info("OSSL_ECHSTORE_new_config unexpected non-zero");
goto end;
}
hpke_suite.kdf_id = 0xAAAA; /* a bad value */
if (!TEST_int_eq(OSSL_ECHSTORE_new_config(es, OSSL_ECH_CURRENT_VERSION,
0, "example.com", hpke_suite),
0)) {
TEST_info("OSSL_ECHSTORE_new_config unexpected non-zero");
if (!TEST_false(OSSL_ECHSTORE_new_config(es, OSSL_ECH_CURRENT_VERSION,
0, "example.com", hpke_suite))
|| !TEST_false(OSSL_ECHSTORE_write_pem(NULL, 0, inout))
|| !TEST_false(OSSL_ECHSTORE_write_pem(es, 0, NULL))
|| !TEST_false(OSSL_ECHSTORE_write_pem(es, 100, inout))
|| !TEST_false(OSSL_ECHSTORE_read_echconfiglist(NULL, inout))
|| !TEST_false(OSSL_ECHSTORE_read_echconfiglist(es, NULL))
|| !TEST_false(OSSL_ECHSTORE_get1_info(NULL, 0, &secs, &pn, &ec,
&has_priv, &for_retry))
|| !TEST_false(OSSL_ECHSTORE_downselect(NULL, 0))
|| !TEST_false(OSSL_ECHSTORE_downselect(es, 100))
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(NULL, priv,
inout, 0))
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(es, NULL, inout, 0))
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(es, priv, NULL, 0))
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(es, priv,
inout, 100))
/* this one fails 'cause priv has no real value, even if non NULL */
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(es, priv, inout,
OSSL_ECH_NO_RETRY))
|| !TEST_false(OSSL_ECHSTORE_read_pem(NULL, inout, OSSL_ECH_NO_RETRY))
|| !TEST_false(OSSL_ECHSTORE_read_pem(es, NULL, OSSL_ECH_NO_RETRY))
|| !TEST_false(OSSL_ECHSTORE_read_pem(es, inout, 100))
|| !TEST_false(OSSL_ECHSTORE_num_keys(NULL, &count))
|| !TEST_false(OSSL_ECHSTORE_num_keys(es, NULL))
|| !TEST_false(OSSL_ECHSTORE_flush_keys(NULL, 0))
|| !TEST_false(OSSL_ECHSTORE_flush_keys(es, -1))
|| !TEST_false(OSSL_ECHSTORE_num_entries(es, NULL)))
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_write_pem(NULL, 0, inout), 0)) {
TEST_info("OSSL_ECHSTORE_write_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_write_pem(es, 0, NULL), 0)) {
TEST_info("OSSL_ECHSTORE_write_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_write_pem(es, 100, inout), 0)) {
TEST_info("OSSL_ECHSTORE_write_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_read_echconfiglist(NULL, inout), 0)) {
TEST_info("OSSL_ECHSTORE_read_echconfiglist unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_read_echconfiglist(es, NULL), 0)) {
TEST_info("OSSL_ECHSTORE_read_echconfiglist unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_get1_info(NULL, &info, &count), 0)) {
TEST_info("OSSL_ECHSTORE_get1_info unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_get1_info(es, NULL, &count), 0)) {
TEST_info("OSSL_ECHSTORE_get1_info unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_get1_info(es, &info, NULL), 0)) {
TEST_info("OSSL_ECHSTORE_get1_info unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_get1_info(es, &info, &count), 1)) {
TEST_info("OSSL_ECHSTORE_get1_info unexpected zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_downselect(NULL, 0), 0)) {
TEST_info("OSSL_ECHSTORE_downselect unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_downselect(es, 100), 0)) {
TEST_info("OSSL_ECHSTORE_downselect unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_set1_key_and_read_pem(NULL, priv,
inout, 0), 0)) {
TEST_info("OSSL_ECHSTORE_set1_key_and_readp_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_set1_key_and_read_pem(es, NULL,
inout, 0), 0)) {
TEST_info("OSSL_ECHSTORE_set1_key_and_readp_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_set1_key_and_read_pem(es, priv,
NULL, 0), 0)) {
TEST_info("OSSL_ECHSTORE_set1_key_and_readp_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_set1_key_and_read_pem(es, priv,
inout, 100), 0)) {
TEST_info("OSSL_ECHSTORE_set1_key_and_readp_pem unexpected non-zero");
goto end;
}
/* this one fails 'cause priv has no real value, even if non NULL */
if (!TEST_int_eq(OSSL_ECHSTORE_set1_key_and_read_pem(es, priv, inout,
OSSL_ECH_NO_RETRY),
0)) {
TEST_info("OSSL_ECHSTORE_set1_key_and_readp_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_read_pem(NULL, inout, OSSL_ECH_NO_RETRY), 0)) {
TEST_info("OSSL_ECHSTORE_read_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_read_pem(es, NULL, OSSL_ECH_NO_RETRY), 0)) {
TEST_info("OSSL_ECHSTORE_read_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_read_pem(es, inout, 100), 0)) {
TEST_info("OSSL_ECHSTORE_read_pem unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_num_keys(NULL, &count), 0)) {
TEST_info("OSSL_ECHSTORE_num_keys unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_num_keys(es, NULL), 0)) {
TEST_info("OSSL_ECHSTORE_num_keys unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_flush_keys(NULL, 0), 0)) {
TEST_info("OSSL_ECHSTORE_flush_keys unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_flush_keys(es, -1), 0)) {
TEST_info("OSSL_ECHSTORE_flush_keys unexpected non-zero");
goto end;
}
/* check free NULL is ok */
OSSL_ECH_INFO_free(NULL, 100);
if (!TEST_int_eq(OSSL_ECH_INFO_print(inout, NULL, -1), 0)) {
TEST_info("OSSL_ECHSTORE_flush_keys unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECH_INFO_print(NULL, info, -1), 0)) {
TEST_info("OSSL_ECHSTORE_flush_keys unexpected non-zero");
goto end;
}
if (!TEST_int_eq(OSSL_ECH_INFO_print(inout, info, 0), 0)) {
TEST_info("OSSL_ECHSTORE_flush_keys unexpected non-zero");
goto end;
}
rv = 1;
end:
OSSL_ECH_INFO_free(info, count);
OSSL_ECHSTORE_free(es);
BIO_free_all(inout);
EVP_PKEY_free(priv);
@ -1020,10 +983,8 @@ static int ech_test_file_read(int run)
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_NO_RETRY),
ft->read)) {
TEST_info("OSSL_ECHSTORE_read_pem unexpected fail");
ft->read))
goto end;
}
rv = 1;
end:
OPENSSL_free(fullname);
@ -1032,12 +993,368 @@ end:
return rv;
}
/* calls with bad, NULL, and simple, arguments, for generic code coverage */
static int ech_api_basic_calls(void)
{
int rv = 0;
SSL_CTX *ctx = NULL;
SSL *s = NULL;
OSSL_ECHSTORE *es = NULL, *es1 = NULL;
char *rinner, *inner = "inner.example.com";
char *router, *outer = "example.com";
unsigned char alpns[] = { 'h', '2' };
size_t alpns_len = sizeof(alpns);
char *gsuite = "X25519,hkdf-sha256,aes-256-gcm";
uint16_t gtype = 0xfe09;
unsigned char *rc = NULL;
size_t rclen = 0;
BIO *in = NULL;
/* NULL args */
if (!TEST_false(SSL_CTX_set1_echstore(NULL, NULL))
|| !TEST_false(SSL_set1_echstore(NULL, NULL))
|| !TEST_ptr_eq(SSL_CTX_get1_echstore(NULL), NULL)
|| !TEST_ptr_eq(SSL_get1_echstore(NULL), NULL)
|| !TEST_false(SSL_ech_set1_server_names(NULL, NULL, NULL, -1))
|| !TEST_false(SSL_ech_set1_outer_server_name(NULL, NULL, -1))
|| !TEST_false(SSL_CTX_ech_set1_outer_alpn_protos(NULL, NULL, -1))
|| !TEST_false(SSL_ech_set1_outer_alpn_protos(NULL, NULL, -1))
|| !TEST_false(SSL_ech_set1_grease_suite(NULL, NULL))
|| !TEST_false(SSL_ech_set_grease_type(NULL, 0)))
goto end;
SSL_CTX_ech_set_callback(NULL, NULL);
SSL_ech_set_callback(NULL, NULL);
if (!TEST_false(SSL_ech_get1_retry_config(NULL, NULL, NULL))
|| !TEST_false(SSL_CTX_ech_raw_decrypt(NULL, NULL, NULL, NULL,
NULL, 0, NULL, NULL,
NULL, NULL))
|| !TEST_int_eq(SSL_ech_get1_status(NULL, &rinner, &router),
SSL_ECH_STATUS_FAILED))
goto end;
/* add an ECHConfigList with extensions to exercise init code */
if (!TEST_ptr(es = OSSL_ECHSTORE_new(NULL, NULL))
|| !TEST_ptr(in = BIO_new(BIO_s_mem()))
|| !TEST_int_gt(BIO_write(in, bin_ok_exts, sizeof(bin_ok_exts)), 0)
|| !TEST_true(OSSL_ECHSTORE_read_echconfiglist(es, in))
|| !TEST_ptr(ctx = SSL_CTX_new_ex(NULL, NULL, TLS_server_method())))
goto end;
/* check status of SSL connection before OSSL_ECHSTORE set */
if (!TEST_ptr(s = SSL_new(ctx))
|| !TEST_int_eq(SSL_ech_get1_status(s, NULL, NULL),
SSL_ECH_STATUS_FAILED)
|| !TEST_int_eq(SSL_ech_get1_status(s, &rinner, &router),
SSL_ECH_STATUS_NOT_CONFIGURED))
goto end;
SSL_set_options(s, SSL_OP_ECH_GREASE);
if (!TEST_int_eq(SSL_ech_get1_status(s, &rinner, &router),
SSL_ECH_STATUS_GREASE))
goto end;
SSL_free(s);
s = NULL; /* for some other tests */
if (!TEST_true(SSL_CTX_set1_echstore(ctx, es)))
goto end;
if (!TEST_ptr((es1 = SSL_CTX_get1_echstore(ctx))))
goto end;
OSSL_ECHSTORE_free(es1);
es1 = NULL;
if (!TEST_false(SSL_set1_echstore(s, es)))
goto end;
/* do this one before SSL_new to exercise a bit of init code */
if (!TEST_true(SSL_CTX_ech_set1_outer_alpn_protos(ctx, alpns, alpns_len)))
goto end;
s = SSL_new(ctx);
if (!TEST_true(SSL_set1_echstore(s, es)))
goto end;
if (!TEST_ptr(es1 = SSL_get1_echstore(s)))
goto end;
OSSL_ECHSTORE_free(es1);
es1 = NULL;
if (!TEST_true(SSL_ech_set1_server_names(s, inner, outer, 0))
|| !TEST_true(SSL_ech_set1_outer_server_name(s, outer, 0))
|| !TEST_true(SSL_ech_set1_outer_alpn_protos(s, alpns, alpns_len))
|| !TEST_true(SSL_ech_set1_grease_suite(s, gsuite))
|| !TEST_true(SSL_ech_set_grease_type(s, gtype))
|| !TEST_true(SSL_ech_get1_retry_config(s, &rc, &rclen))
|| !TEST_false(rclen)
|| !TEST_ptr_eq(rc, NULL))
goto end;
SSL_CTX_ech_set_callback(ctx, test_cb);
SSL_ech_set_callback(s, test_cb);
/* all good */
rv = 1;
end:
BIO_free_all(in);
OSSL_ECHSTORE_free(es1);
OSSL_ECHSTORE_free(es);
SSL_CTX_free(ctx);
SSL_free(s);
return rv;
}
/*
* Test boringssl compatibility API. We don't need exhaustive
* tests here as this is a simple enough wrapper on things
* tested elsewhere.
*/
static int ech_boring_compat(void)
{
int rv = 0;
SSL_CTX *ctx = NULL;
SSL *s = NULL;
if (!TEST_false(SSL_set1_ech_config_list(NULL, NULL, 0))
|| !TEST_ptr(ctx = SSL_CTX_new_ex(NULL, NULL, TLS_server_method()))
|| !TEST_ptr(s = SSL_new(ctx))
|| !TEST_true(SSL_set1_ech_config_list(s, NULL, 0))
|| !TEST_true(SSL_set1_ech_config_list(s, (uint8_t *)b64_pk1,
sizeof(b64_pk1) - 1))
|| !TEST_true(SSL_set1_ech_config_list(s, (uint8_t *)bin_6_to_3,
sizeof(bin_6_to_3)))
/* test a fail */
|| !TEST_false(SSL_set1_ech_config_list(s, (uint8_t *)b64_pk1,
sizeof(b64_pk1) - 2)))
goto end;
rv = 1;
end:
SSL_CTX_free(ctx);
SSL_free(s);
return rv;
}
/* values that can be used in helper below */
# define OSSL_ECH_TEST_BASIC 0
# define OSSL_ECH_TEST_HRR 1
# define OSSL_ECH_TEST_EARLY 2
# define OSSL_ECH_TEST_CUSTOM 3
/*
* @brief ECH roundtrip test helper
* @param idx specifies which ciphersuite
* @araam combo specifies which particular test we want to roundtrip
* @return 1 for good, 0 for bad
*
* The idx input here is from 0..44 and is broken down into a
* kem, kdf and aead. If you run in verbose more ("-v") then
* there'll be a "Doing: ..." trace line that says which suite
* is being tested in string form.
*
* The combo input is one of the #define'd OSSL_ECH_TEST_*
* values above.
*
* TODO(ECH): we're not yet really attempting ECH, but we currently
* set the inputs as if we were doing ECH, yet don't expect to see
* real ECH status outcomes, so while we do make calls to get that
* status outcome, we don't compare vs. real expected results.
* That's done via the "if (0 &&" clauses below which will be
* removed once ECH is really being attempted.
*/
static int test_ech_roundtrip_helper(int idx, int combo)
{
int res = 0, kemind, kdfind, aeadind, kemsz, kdfsz, aeadsz;
char suitestr[100];
OSSL_ECHSTORE *es = NULL;
OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
uint16_t ech_version = OSSL_ECH_CURRENT_VERSION;
uint8_t max_name_length = 0;
char *public_name = "example.com";
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL;
int clientstatus, serverstatus;
char *cinner = NULL, *couter = NULL, *sinner = NULL, *souter = NULL;
SSL_SESSION *sess = NULL;
unsigned char ed[21];
size_t written = 0, readbytes = 0;
unsigned char buf[1024];
unsigned int context;
int server = 1, client = 0;
/* split idx into kemind, kdfind, aeadind */
kemsz = OSSL_NELEM(kem_str_list);
kdfsz = OSSL_NELEM(kdf_str_list);
aeadsz = OSSL_NELEM(aead_str_list);
kemind = (idx / (kdfsz * aeadsz)) % kemsz;
kdfind = (idx / aeadsz) % kdfsz;
aeadind = idx % aeadsz;
/* initialise early data stuff, just in case */
memset(ed, 'A', sizeof(ed));
snprintf(suitestr, 100, "%s,%s,%s", kem_str_list[kemind],
kdf_str_list[kdfind], aead_str_list[aeadind]);
if (verbose)
TEST_info("Doing: iter: %d, suite: %s", idx, suitestr);
if (!TEST_true(OSSL_HPKE_str2suite(suitestr, &hpke_suite))
|| !TEST_ptr(es = OSSL_ECHSTORE_new(libctx, propq))
|| !TEST_true(OSSL_ECHSTORE_new_config(es, ech_version, max_name_length,
public_name, hpke_suite))
|| !TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
TLS_client_method(),
TLS1_3_VERSION, TLS1_3_VERSION,
&sctx, &cctx, cert, privkey)))
goto end;
if (combo == OSSL_ECH_TEST_EARLY) {
/* just to keep the format checker happy :-) */
int lrv = 0;
if (!TEST_true(SSL_CTX_set_options(sctx, SSL_OP_NO_ANTI_REPLAY))
|| !TEST_true(SSL_CTX_set_max_early_data(sctx,
SSL3_RT_MAX_PLAIN_LENGTH)))
goto end;
lrv = SSL_CTX_set_recv_max_early_data(sctx, SSL3_RT_MAX_PLAIN_LENGTH);
if (!TEST_true(lrv))
goto end;
}
if (combo == OSSL_ECH_TEST_CUSTOM) {
/* add custom CH ext to client and server */
context = SSL_EXT_CLIENT_HELLO;
if (!TEST_true(SSL_CTX_add_custom_ext(cctx, TEST_EXT_TYPE1, context,
new_add_cb, new_free_cb,
&client, new_parse_cb, &client))
|| !TEST_true(SSL_CTX_add_custom_ext(sctx, TEST_EXT_TYPE1, context,
new_add_cb, new_free_cb,
&server, new_parse_cb, &server))
|| !TEST_true(SSL_CTX_add_custom_ext(cctx, TEST_EXT_TYPE2, context,
new_add_cb, NULL,
&client, NULL, &client))
|| !TEST_true(SSL_CTX_add_custom_ext(sctx, TEST_EXT_TYPE2, context,
new_add_cb, NULL,
&server, NULL, &server)))
goto end;
}
if (!TEST_true(SSL_CTX_set1_echstore(cctx, es))
|| !TEST_true(SSL_CTX_set1_echstore(sctx, es))
|| !TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
&clientssl, NULL, NULL)))
goto end;
if (combo == OSSL_ECH_TEST_HRR
&& !TEST_true(SSL_set1_groups_list(serverssl, "P-384")))
goto end;
if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example"))
|| !TEST_true(create_ssl_connection(serverssl, clientssl,
SSL_ERROR_NONE)))
goto end;
serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter);
if (verbose)
TEST_info("server status %d, %s, %s", serverstatus, sinner, souter);
if (0 && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS))
goto end;
/* override cert verification */
SSL_set_verify_result(clientssl, X509_V_OK);
clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter);
if (verbose)
TEST_info("client status %d, %s, %s", clientstatus, cinner, couter);
if (0 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS))
goto end;
/* all good */
if (combo == OSSL_ECH_TEST_BASIC
|| combo == OSSL_ECH_TEST_HRR
|| combo == OSSL_ECH_TEST_CUSTOM) {
res = 1;
goto end;
}
/* continue for EARLY test */
if (combo != OSSL_ECH_TEST_EARLY)
goto end;
/* shutdown for start over */
sess = SSL_get1_session(clientssl);
OPENSSL_free(sinner);
OPENSSL_free(souter);
OPENSSL_free(cinner);
OPENSSL_free(couter);
sinner = souter = cinner = couter = NULL;
SSL_shutdown(clientssl);
SSL_shutdown(serverssl);
SSL_free(serverssl);
SSL_free(clientssl);
serverssl = clientssl = NULL;
/* second connection */
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
&clientssl, NULL, NULL))
|| !TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example"))
|| !TEST_true(SSL_set_session(clientssl, sess))
|| !TEST_true(SSL_write_early_data(clientssl, ed, sizeof(ed), &written))
|| !TEST_size_t_eq(written, sizeof(ed))
|| !TEST_int_eq(SSL_read_early_data(serverssl, buf,
sizeof(buf), &readbytes),
SSL_READ_EARLY_DATA_SUCCESS)
|| !TEST_size_t_eq(written, readbytes))
goto end;
/*
* Server should be able to write data, and client should be able to
* read it.
*/
if (!TEST_true(SSL_write_early_data(serverssl, ed, sizeof(ed), &written))
|| !TEST_size_t_eq(written, sizeof(ed))
|| !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &readbytes))
|| !TEST_mem_eq(buf, readbytes, ed, sizeof(ed)))
goto end;
serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter);
if (verbose)
TEST_info("server status %d, %s, %s", serverstatus, sinner, souter);
if (0 && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS))
goto end;
/* override cert verification */
SSL_set_verify_result(clientssl, X509_V_OK);
clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter);
if (verbose)
TEST_info("client status %d, %s, %s", clientstatus, cinner, couter);
if (0 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS))
goto end;
/* all good */
res = 1;
end:
OSSL_ECHSTORE_free(es);
OPENSSL_free(sinner);
OPENSSL_free(souter);
OPENSSL_free(cinner);
OPENSSL_free(couter);
SSL_SESSION_free(sess);
SSL_free(clientssl);
SSL_free(serverssl);
SSL_CTX_free(cctx);
SSL_CTX_free(sctx);
return res;
}
/* Test roundtrip with ECH for any suite */
static int test_ech_suites(int idx)
{
if (verbose)
TEST_info("Doing: test_ech_suites");
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_BASIC);
}
/* ECH with HRR for the given suite */
static int test_ech_hrr(int idx)
{
if (verbose)
TEST_info("Doing: test_ech_hrr");
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_HRR);
}
/* ECH with early data for the given suite */
static int test_ech_early(int idx)
{
if (verbose)
TEST_info("Doing: test_ech_early");
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_EARLY);
}
/* Test a roundtrip with ECH, and a custom CH extension */
static int ech_custom_test(int idx)
{
if (verbose)
TEST_info("Doing: ech_custom_test");
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_CUSTOM);
}
#endif
int setup_tests(void)
{
#ifndef OPENSSL_NO_ECH
OPTION_CHOICE o;
int suite_combos;
while ((o = opt_next()) != OPT_EOF) {
switch (o) {
@ -1053,11 +1370,30 @@ int setup_tests(void)
certsdir = test_get_argument(0);
if (certsdir == NULL)
certsdir = DEF_CERTS_DIR;
cert = test_mk_file_path(certsdir, "echserver.pem");
if (cert == NULL)
goto err;
privkey = test_mk_file_path(certsdir, "echserver.key");
if (privkey == NULL)
goto err;
rootcert = test_mk_file_path(certsdir, "rootcert.pem");
if (rootcert == NULL)
goto err;
ADD_ALL_TESTS(ech_ingest_test, OSSL_NELEM(ingest_tvs));
ADD_TEST(ech_store_null_calls);
ADD_ALL_TESTS(ech_test_file_read, OSSL_NELEM(fnames));
/* TODO(ECH): we'll add more test code once other TODO's settle */
ADD_TEST(ech_api_basic_calls);
ADD_TEST(ech_boring_compat);
suite_combos = OSSL_NELEM(kem_str_list) * OSSL_NELEM(kdf_str_list)
* OSSL_NELEM(aead_str_list);
ADD_ALL_TESTS(test_ech_suites, suite_combos);
ADD_ALL_TESTS(test_ech_hrr, suite_combos);
ADD_ALL_TESTS(test_ech_early, suite_combos);
ADD_ALL_TESTS(ech_custom_test, suite_combos);
/* TODO(ECH): add more test code as other PRs done */
return 1;
err:
return 0;
#endif
return 1;
}
@ -1065,6 +1401,8 @@ int setup_tests(void)
void cleanup_tests(void)
{
#ifndef OPENSSL_NO_ECH
;
OPENSSL_free(cert);
OPENSSL_free(privkey);
OPENSSL_free(rootcert);
#endif
}

View File

@ -597,21 +597,21 @@ 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
OSSL_ECH_INFO_free ? 3_5_0 EXIST::FUNCTION:ECH
OSSL_ECH_INFO_print ? 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_set_server_names ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_set_outer_server_name ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_set_outer_alpn_protos ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_get1_status ? 3_5_0 EXIST::FUNCTION:ECH
SSL_ech_set_grease_suite ? 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_ech_get_retry_config ? 3_5_0 EXIST::FUNCTION:ECH
SSL_CTX_ech_set_outer_alpn_protos ? 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