ECH client side

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26011)
This commit is contained in:
sftcd 2024-11-20 14:10:30 +00:00 committed by Matt Caswell
parent 7debe0ddef
commit ade10c5dbf
21 changed files with 3036 additions and 199 deletions

View file

@ -131,6 +131,7 @@ ignore-words-list =
Gost,
GOST,
Hart,
hashin,
hasTable,
hel,
hist,

View file

@ -107,6 +107,9 @@ static int keymatexportlen = 20;
static BIO *bio_c_out = NULL;
static int c_quiet = 0;
static char *sess_out = NULL;
#ifndef OPENSSL_NO_ECH
static char *ech_config_list = NULL;
#endif
static SSL_SESSION *psksess = NULL;
static void print_stuff(BIO *berr, SSL *con, int full);
@ -608,6 +611,9 @@ typedef enum OPTION_choice {
OPT_ENABLE_CLIENT_RPK,
OPT_SCTP_LABEL_BUG,
OPT_KTLS,
#ifndef OPENSSL_NO_ECH
OPT_ECHCONFIGLIST,
#endif
OPT_R_ENUM,
OPT_PROV_ENUM
} OPTION_CHOICE;
@ -801,6 +807,10 @@ const OPTIONS s_client_options[] = {
{ "enable_pha", OPT_ENABLE_PHA, '-', "Enable post-handshake-authentication" },
{ "enable_server_rpk", OPT_ENABLE_SERVER_RPK, '-', "Enable raw public keys (RFC7250) from the server" },
{ "enable_client_rpk", OPT_ENABLE_CLIENT_RPK, '-', "Enable raw public keys (RFC7250) from the client" },
#ifndef OPENSSL_NO_ECH
{ "ech_config_list", OPT_ECHCONFIGLIST, 's',
"Set ECHConfigList, value is base 64 encoded ECHConfigList" },
#endif
#ifndef OPENSSL_NO_SRTP
{ "use_srtp", OPT_USE_SRTP, 's',
"Offer SRTP key management with a colon-separated profile list" },
@ -1582,6 +1592,11 @@ int s_client_main(int argc, char **argv)
case OPT_SERVERNAME:
servername = opt_arg();
break;
#ifndef OPENSSL_NO_ECH
case OPT_ECHCONFIGLIST:
ech_config_list = opt_arg();
break;
#endif
case OPT_NOSERVERNAME:
noservername = 1;
break;
@ -2171,6 +2186,14 @@ int s_client_main(int argc, char **argv)
}
}
#ifndef OPENSSL_NO_ECH
if (ech_config_list != NULL
&& SSL_set1_ech_config_list(con, (unsigned char *)ech_config_list,
strlen(ech_config_list))
!= 1)
goto end;
#endif
if (dane_tlsa_domain != NULL) {
if (SSL_dane_enable(con, dane_tlsa_domain) <= 0) {
BIO_printf(bio_err, "%s: Error enabling DANE TLSA "
@ -3409,6 +3432,102 @@ static void print_cert_key_info(BIO *bio, X509 *cert)
OPENSSL_free(curve);
}
#ifndef OPENSSL_NO_ECH
static void print_ech_retry_configs(BIO *bio, SSL *s)
{
int ind, cnt = 0, has_priv, for_retry;
OSSL_ECHSTORE *es = NULL;
time_t secs = 0;
char *pn = NULL, *ec = NULL;
size_t rtlen = 0;
unsigned char *rtval = NULL;
BIO *biom = NULL;
if (SSL_ech_get1_retry_config(s, &rtval, &rtlen) != 1) {
BIO_printf(bio, "ECH: Error getting retry-configs\n");
return;
}
/*
* print nicely, note that any non-supported versions
* sent by server will have been filtered out by now
*/
if ((biom = BIO_new(BIO_s_mem())) == NULL
|| BIO_write(biom, rtval, rtlen) <= 0
|| (es = OSSL_ECHSTORE_new(NULL, NULL)) == NULL
|| OSSL_ECHSTORE_read_echconfiglist(es, biom) != 1) {
BIO_printf(bio, "ECH: Error loading retry-configs\n");
goto end;
}
if (OSSL_ECHSTORE_num_entries(es, &cnt) != 1)
goto end;
BIO_printf(bio, "ECH: Got %d retry-configs\n", cnt);
for (ind = 0; ind != cnt; ind++) {
if (OSSL_ECHSTORE_get1_info(es, ind, &secs, &pn, &ec,
&has_priv, &for_retry)
!= 1) {
BIO_printf(bio, "ECH: Error getting retry-config %d\n", ind);
goto end;
}
BIO_printf(bio, "ECH: entry: %d public_name: %s age: %d%s\n",
ind, pn, (int)secs, has_priv ? " (has private key)" : "");
BIO_printf(bio, "ECH: \t%s\n", ec);
OPENSSL_free(pn);
pn = NULL;
OPENSSL_free(ec);
ec = NULL;
}
end:
BIO_free_all(biom);
OPENSSL_free(rtval);
OPENSSL_free(pn);
OPENSSL_free(ec);
OSSL_ECHSTORE_free(es);
return;
}
static void print_ech_status(BIO *bio, SSL *s, int estat)
{
switch (estat) {
case SSL_ECH_STATUS_NOT_TRIED:
BIO_printf(bio, "ECH: not tried: %d\n", estat);
break;
case SSL_ECH_STATUS_FAILED:
BIO_printf(bio, "ECH: tried but failed: %d\n", estat);
break;
case SSL_ECH_STATUS_FAILED_ECH:
BIO_printf(bio, "ECH: failed+retry-configs: %d\n", estat);
break;
case SSL_ECH_STATUS_SUCCESS:
BIO_printf(bio, "ECH: success: %d\n", estat);
break;
case SSL_ECH_STATUS_GREASE_ECH:
BIO_printf(bio, "ECH: GREASE+retry-configs%d\n", estat);
break;
case SSL_ECH_STATUS_BACKEND:
BIO_printf(bio, "ECH: BACKEND: %d\n", estat);
break;
case SSL_ECH_STATUS_GREASE:
BIO_printf(bio, "ECH: GREASE: %d\n", estat);
break;
case SSL_ECH_STATUS_BAD_CALL:
BIO_printf(bio, "ECH: BAD CALL: %d\n", estat);
break;
case SSL_ECH_STATUS_BAD_NAME:
BIO_printf(bio, "ECH: BAD NAME: %d\n", estat);
break;
case SSL_ECH_STATUS_NOT_CONFIGURED:
BIO_printf(bio, "ECH: NOT CONFIGURED: %d\n", estat);
break;
case SSL_ECH_STATUS_FAILED_ECH_BAD_NAME:
BIO_printf(bio, "ECH: failed+retry-configs: %d\n", estat);
break;
default:
BIO_printf(bio, "ECH: unexpected status: %d\n", estat);
}
return;
}
#endif
static void print_stuff(BIO *bio, SSL *s, int full)
{
X509 *peer = NULL;
@ -3652,6 +3771,26 @@ static void print_stuff(BIO *bio, SSL *s, int full)
OPENSSL_free(exportedkeymat);
}
BIO_puts(bio, "---\n");
#ifndef OPENSSL_NO_ECH
{
char *inner = NULL, *outer = NULL;
int estat = 0;
estat = SSL_ech_get1_status(s, &inner, &outer);
print_ech_status(bio, s, estat);
if (estat == SSL_ECH_STATUS_SUCCESS) {
BIO_printf(bio, "ECH: inner: %s\n", inner);
BIO_printf(bio, "ECH: outer: %s\n", outer);
}
if (estat == SSL_ECH_STATUS_FAILED_ECH
|| estat == SSL_ECH_STATUS_FAILED_ECH_BAD_NAME)
print_ech_retry_configs(bio, s);
OPENSSL_free(inner);
OPENSSL_free(outer);
}
BIO_puts(bio, "---\n");
#endif
/* flush, or debugging output gets mixed with http response */
(void)BIO_flush(bio);
}

View file

@ -123,6 +123,7 @@ B<openssl> B<s_client>
[B<-enable_server_rpk>]
[B<-enable_client_rpk>]
[I<host>:I<port>]
[B<-ech_config_list>]
=head1 DESCRIPTION
@ -177,6 +178,15 @@ specified with this flag and issues an HTTP CONNECT command to connect
to the desired server.
If the host string is an IPv6 address, it must be enclosed in C<[> and C<]>.
=item B<-ech_config_list> I<value>
Specifies the ECHConfigList value to use for Encrypted Client Hello (ECH) for
the TLS session. The value must be a base64 encoded ECHConfigList.
The ECHConfigList structure is defined in RFC XXXX. (That's currently in
L<https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni/>)
=for comment TODO(ECH): replace XXXX when RFC published.
=item B<-proxy_user> I<userid>
When used with the B<-proxy> flag, the program will attempt to authenticate

View file

@ -0,0 +1,27 @@
/*
* 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
*/
/*
* These functions are ECH helpers that are used within the library but
* also by ECH test code.
*/
#ifndef OPENSSL_ECH_HELPERS_H
#define OPENSSL_ECH_HELPERS_H
#pragma once
#ifndef OPENSSL_NO_ECH
int ossl_ech_get_sh_offsets(const unsigned char *sh, size_t sh_len,
size_t *exts, size_t *echoffset,
uint16_t *echtype);
int ossl_ech_make_enc_info(unsigned char *encoding, size_t encoding_length,
unsigned char *info, size_t *info_len);
#endif
#endif

View file

@ -11,5 +11,143 @@
#include <openssl/ech.h>
#include "../ssl_local.h"
#include "ech_local.h"
#include "internal/ech_helpers.h"
/* TODO(ECH): move code that's used by internals and test here */
/* TODO(ECH): move more code that's used by internals and test here */
/* used in ECH crypto derivations (odd format for EBCDIC goodness) */
/* "tls ech" */
static const char OSSL_ECH_CONTEXT_STRING[] = "\x74\x6c\x73\x20\x65\x63\x68";
/*
* Given a SH (or HRR) find the offsets of the ECH (if any)
* sh is the SH buffer
* sh_len is the length of the SH
* exts points to offset of extensions
* echoffset points to offset of ECH
* echtype points to the ext type of the ECH
* return 1 for success, zero otherwise
*
* Offsets are returned to the type or length field in question.
* Offsets are set to zero if relevant thing not found.
*
* Note: input here is untrusted!
*/
int ossl_ech_get_sh_offsets(const unsigned char *sh, size_t sh_len,
size_t *exts, size_t *echoffset,
uint16_t *echtype)
{
unsigned int etype = 0, pi_tmp = 0;
const unsigned char *pp_tmp = NULL, *shstart = NULL;
PACKET pkt, session_id, extpkt, oneext;
size_t extlens = 0;
int done = 0;
#ifdef OSSL_ECH_SUPERVERBOSE
size_t echlen = 0; /* length of ECH, including type & ECH-internal length */
size_t sessid_offset = 0, sessid_len = 0;
#endif
if (sh == NULL || sh_len == 0 || exts == NULL || echoffset == NULL
|| echtype == NULL)
return 0;
*exts = *echoffset = *echtype = 0;
if (!PACKET_buf_init(&pkt, sh, sh_len))
return 0;
shstart = PACKET_data(&pkt);
if (!PACKET_get_net_2(&pkt, &pi_tmp))
return 0;
/*
* TODO(ECH): we've had a TLSv1.2 test in the past where we add an
* ECH to a TLSv1.2 CH to ensure server code ignores that properly.
* We might or might not keep that, if we don't then the test below
* should allow TLSv1.3 only.
*/
/* if we're not TLSv1.2+ then we can bail, but it's not an error */
if (pi_tmp != TLS1_2_VERSION && pi_tmp != TLS1_3_VERSION)
return 1;
if (!PACKET_get_bytes(&pkt, &pp_tmp, SSL3_RANDOM_SIZE)
#ifdef OSSL_ECH_SUPERVERBOSE
|| (sessid_offset = PACKET_data(&pkt) - shstart) == 0
#endif
|| !PACKET_get_length_prefixed_1(&pkt, &session_id)
#ifdef OSSL_ECH_SUPERVERBOSE
|| (sessid_len = PACKET_remaining(&session_id)) == 0
#endif
|| !PACKET_get_net_2(&pkt, &pi_tmp) /* ciphersuite */
|| !PACKET_get_1(&pkt, &pi_tmp) /* compression */
|| (*exts = PACKET_data(&pkt) - shstart) == 0
|| !PACKET_as_length_prefixed_2(&pkt, &extpkt)
|| PACKET_remaining(&pkt) != 0)
return 0;
extlens = PACKET_remaining(&extpkt);
if (extlens == 0) /* not an error, in theory */
return 1;
while (PACKET_remaining(&extpkt) > 0 && done < 1) {
if (!PACKET_get_net_2(&extpkt, &etype)
|| !PACKET_get_length_prefixed_2(&extpkt, &oneext))
return 0;
if (etype == TLSEXT_TYPE_ech) {
if (PACKET_remaining(&oneext) != 8)
return 0;
*echoffset = PACKET_data(&oneext) - shstart - 4;
*echtype = etype;
#ifdef OSSL_ECH_SUPERVERBOSE
echlen = PACKET_remaining(&oneext) + 4; /* type/length included */
#endif
done++;
}
}
#ifdef OSSL_ECH_SUPERVERBOSE
OSSL_TRACE_BEGIN(TLS)
{
BIO_printf(trc_out, "orig SH/ECH type: %4x\n", *echtype);
}
OSSL_TRACE_END(TLS);
ossl_ech_pbuf("orig SH", (unsigned char *)sh, sh_len);
ossl_ech_pbuf("orig SH session_id", (unsigned char *)sh + sessid_offset,
sessid_len);
ossl_ech_pbuf("orig SH exts", (unsigned char *)sh + *exts, extlens);
ossl_ech_pbuf("orig SH/ECH ", (unsigned char *)sh + *echoffset, echlen);
#endif
return 1;
}
/*
* make up HPKE "info" input as per spec
* encoding is the ECHconfig being used
* encodinglen is the length of ECHconfig being used
* info is a caller-allocated buffer for results
* info_len is the buffer size on input, used-length on output
* return 1 for success, zero otherwise
*/
int ossl_ech_make_enc_info(unsigned char *encoding, size_t encoding_length,
unsigned char *info, size_t *info_len)
{
WPACKET ipkt = { 0 };
BUF_MEM *ipkt_mem = NULL;
if (encoding == NULL || info == NULL || info_len == NULL)
return 0;
if (*info_len < (sizeof(OSSL_ECH_CONTEXT_STRING) + encoding_length))
return 0;
if ((ipkt_mem = BUF_MEM_new()) == NULL
|| !WPACKET_init(&ipkt, ipkt_mem)
|| !WPACKET_memcpy(&ipkt, OSSL_ECH_CONTEXT_STRING,
sizeof(OSSL_ECH_CONTEXT_STRING) - 1)
/*
* the zero valued octet is required by the spec, section 7.1 so
* a tiny bit better to add it explicitly rather than depend on
* the context string being NUL terminated
*/
|| !WPACKET_put_bytes_u8(&ipkt, 0)
|| !WPACKET_memcpy(&ipkt, encoding, encoding_length)) {
WPACKET_cleanup(&ipkt);
BUF_MEM_free(ipkt_mem);
return 0;
}
*info_len = sizeof(OSSL_ECH_CONTEXT_STRING) + encoding_length;
memcpy(info, WPACKET_get_curr(&ipkt) - *info_len, *info_len);
WPACKET_cleanup(&ipkt);
BUF_MEM_free(ipkt_mem);
return 1;
}

File diff suppressed because it is too large Load diff

View file

@ -41,7 +41,22 @@
/* value for not yet set ECH config_id */
#define OSSL_ECH_config_id_unset -1
#define OSSL_ECH_OUTER_CH_TYPE 0 /* outer ECHClientHello enum */
#define OSSL_ECH_INNER_CH_TYPE 1 /* inner ECHClientHello enum */
#define OSSL_ECH_CIPHER_LEN 4 /* ECHCipher length (2 for kdf, 2 for aead) */
#define OSSL_ECH_SIGNAL_LEN 8 /* length of ECH acceptance signal */
#ifndef CLIENT_VERSION_LEN
/*
* This is the legacy version length, i.e. len(0x0303). The same
* label is used in e.g. test/sslapitest.c and elsewhere but not
* defined in a header file I could find.
*/
#define CLIENT_VERSION_LEN 2
#endif
/*
* Reminder of what goes in DNS for ECH RFC XXXX
*
@ -84,7 +99,7 @@ typedef struct ossl_echext_st {
DEFINE_STACK_OF(OSSL_ECHEXT)
typedef struct ossl_echstore_entry_st {
uint16_t version; /* 0xff0d for draft-13 */
uint16_t version; /* 0xfe0d for RFC XXXX */
char *public_name;
size_t pub_len;
unsigned char *pub;
@ -133,10 +148,15 @@ typedef struct ossl_ech_conn_st {
*/
char *former_inner;
/*
* TODO(ECH): The next 4 buffers (and lengths) may change later
* if a better way to handle the multiple 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.)
* TODO(ECH): The next 4 buffers (and lengths) may change if a
* better way to handle the multiple transcripts needed is
* suggested/invented. I suggest re-factoring transcript handling
* (which is probably needed) after/with the PR that includes the
* server-side ECH code. That should be much easier as at that point
* the full set of tests can be run, whereas for now, we're limited
* to testing the client side really works via bodged s_client
* scripts, so there'd be a bigger risk of breaking something
* subtly if we try re-factor now.
*/
/*
* encoded inner ClientHello before/after ECH compression, which`
@ -174,7 +194,7 @@ typedef struct ossl_ech_conn_st {
* 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;
int 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 */
@ -207,6 +227,39 @@ typedef struct ossl_ech_conn_st {
unsigned char client_random[SSL3_RANDOM_SIZE]; /* CH random */
} OSSL_ECH_CONN;
/* Return values from ossl_ech_same_ext */
#define OSSL_ECH_SAME_EXT_ERR 0 /* bummer something wrong */
#define OSSL_ECH_SAME_EXT_DONE 1 /* proceed with same value in inner/outer */
#define OSSL_ECH_SAME_EXT_CONTINUE 2 /* generate a new value for outer CH */
/*
* During extension construction (in extensions_clnt.c and surprisingly also in
* extensions.c), we need to handle inner/outer CH cloning - ossl_ech_same_ext
* will (depending on compile time handling options) copy the value from
* CH.inner to CH.outer or else processing will continue, for a 2nd call,
* likely generating a fresh value for the outer CH. The fresh value could well
* be the same as in the inner.
*
* This macro should be called in each _ctos_ function that doesn't explicitly
* have special ECH handling.
*
* Note that the placement of this macro needs a bit of thought - it has to go
* after declarations (to keep the ansi-c compile happy) and also after any
* checks that result in the extension not being sent but before any relevant
* state changes that would affect a possible 2nd call to the constructor.
* Luckily, that's usually not too hard, but it's not mechanical.
*/
#define ECH_SAME_EXT(s, pkt) \
if (s->ext.ech.es != NULL && s->ext.ech.grease == 0) { \
int ech_iosame_rv = ossl_ech_same_ext(s, pkt); \
\
if (ech_iosame_rv == OSSL_ECH_SAME_EXT_ERR) \
return EXT_RETURN_FAIL; \
if (ech_iosame_rv == OSSL_ECH_SAME_EXT_DONE) \
return EXT_RETURN_SENT; \
/* otherwise continue as normal */ \
}
/* Internal ECH APIs */
OSSL_ECHSTORE *ossl_echstore_dup(const OSSL_ECHSTORE *old);
@ -217,6 +270,36 @@ int ossl_ech_conn_init(SSL_CONNECTION *s, SSL_CTX *ctx,
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);
#ifdef OSSL_ECH_SUPERVERBOSE
void ossl_ech_pbuf(const char *msg,
const unsigned char *buf, const size_t blen);
void ossl_ech_ptranscript(SSL_CONNECTION *s, const char *msg);
#endif
int ossl_ech_get_retry_configs(SSL_CONNECTION *s, unsigned char **rcfgs,
size_t *rcfgslen);
int ossl_ech_send_grease(SSL_CONNECTION *s, WPACKET *pkt);
int ossl_ech_pick_matching_cfg(SSL_CONNECTION *s, OSSL_ECHSTORE_ENTRY **ee,
OSSL_HPKE_SUITE *suite);
int ossl_ech_encode_inner(SSL_CONNECTION *s);
int ossl_ech_find_confirm(SSL_CONNECTION *s, int hrr,
unsigned char acbuf[OSSL_ECH_SIGNAL_LEN],
const unsigned char *shbuf, const size_t shlen);
int ossl_ech_make_transcript_buffer(SSL_CONNECTION *s, int for_hrr,
const unsigned char *shbuf, size_t shlen,
unsigned char **tbuf, size_t *tlen,
size_t *chend, size_t *fixedshbuf_len);
int ossl_ech_reset_hs_buffer(SSL_CONNECTION *s, const unsigned char *buf,
size_t blen);
int ossl_ech_aad_and_encrypt(SSL_CONNECTION *s, WPACKET *pkt);
int ossl_ech_swaperoo(SSL_CONNECTION *s);
int ossl_ech_calc_confirm(SSL_CONNECTION *s, int for_hrr,
unsigned char acbuf[OSSL_ECH_SIGNAL_LEN],
const unsigned char *shbuf, const size_t shlen);
/* these are internal but located in ssl/statem/extensions.c */
int ossl_ech_same_ext(SSL_CONNECTION *s, WPACKET *pkt);
int ossl_ech_same_key_share(void);
int ossl_ech_2bcompressed(int ind);
#endif
#endif

View file

@ -1126,7 +1126,7 @@ int OSSL_ECHSTORE_flush_keys(OSSL_ECHSTORE *es, time_t age)
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
return 0;
}
if (ee->keyshare != NULL && ((ee->loadtime + age) > now)) {
if (ee->keyshare != NULL && ee->loadtime + age >= now) {
ossl_echstore_entry_free(ee);
sk_OSSL_ECHSTORE_ENTRY_delete(es->entries, i);
}

View file

@ -2212,3 +2212,106 @@ const char *OSSL_default_ciphersuites(void)
"TLS_CHACHA20_POLY1305_SHA256:"
"TLS_AES_128_GCM_SHA256";
}
int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk,
WPACKET *pkt)
{
int i;
size_t totlen = 0, len, maxlen, maxverok = 0;
int empty_reneg_info_scsv = !s->renegotiate
&& !SSL_CONNECTION_IS_DTLS(s)
&& ssl_security(s, SSL_SECOP_VERSION, 0, TLS1_VERSION, NULL)
&& s->min_proto_version <= TLS1_VERSION;
SSL *ssl = SSL_CONNECTION_GET_SSL(s);
/* Set disabled masks for this session */
if (!ssl_set_client_disabled(s)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_PROTOCOLS_AVAILABLE);
return 0;
}
if (sk == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
#ifdef OPENSSL_MAX_TLS1_2_CIPHER_LENGTH
#if OPENSSL_MAX_TLS1_2_CIPHER_LENGTH < 6
#error Max cipher length too short
#endif
/*
* Some servers hang if client hello > 256 bytes as hack workaround
* chop number of supported ciphers to keep it well below this if we
* use TLS v1.2
*/
if (TLS1_get_version(ssl) >= TLS1_2_VERSION)
maxlen = OPENSSL_MAX_TLS1_2_CIPHER_LENGTH & ~1;
else
#endif
/* Maximum length that can be stored in 2 bytes. Length must be even */
maxlen = 0xfffe;
if (empty_reneg_info_scsv)
maxlen -= 2;
if (s->mode & SSL_MODE_SEND_FALLBACK_SCSV)
maxlen -= 2;
for (i = 0; i < sk_SSL_CIPHER_num(sk) && totlen < maxlen; i++) {
const SSL_CIPHER *c;
c = sk_SSL_CIPHER_value(sk, i);
/* Skip disabled ciphers */
if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_SUPPORTED, 0))
continue;
if (!ssl->method->put_cipher_by_char(c, pkt, &len)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
/* Sanity check that the maximum version we offer has ciphers enabled */
if (!maxverok) {
int minproto = SSL_CONNECTION_IS_DTLS(s) ? c->min_dtls : c->min_tls;
int maxproto = SSL_CONNECTION_IS_DTLS(s) ? c->max_dtls : c->max_tls;
if (ssl_version_cmp(s, maxproto, s->s3.tmp.max_ver) >= 0
&& ssl_version_cmp(s, minproto, s->s3.tmp.max_ver) <= 0)
maxverok = 1;
}
totlen += len;
}
if (totlen == 0 || !maxverok) {
const char *maxvertext = !maxverok
? "No ciphers enabled for max supported SSL/TLS version"
: NULL;
SSLfatal_data(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_CIPHERS_AVAILABLE,
maxvertext);
return 0;
}
if (totlen != 0) {
if (empty_reneg_info_scsv) {
static const SSL_CIPHER scsv = {
0, NULL, NULL, SSL3_CK_SCSV, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
if (!ssl->method->put_cipher_by_char(&scsv, pkt, &len)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
if (s->mode & SSL_MODE_SEND_FALLBACK_SCSV) {
static const SSL_CIPHER scsv = {
0, NULL, NULL, SSL3_CK_FALLBACK_SCSV, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
if (!ssl->method->put_cipher_by_char(&scsv, pkt, &len)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
}
return 1;
}

View file

@ -694,6 +694,8 @@ typedef enum tlsext_index_en {
TLSEXT_IDX_compress_certificate,
TLSEXT_IDX_early_data,
TLSEXT_IDX_certificate_authorities,
TLSEXT_IDX_ech,
TLSEXT_IDX_outer_extensions,
TLSEXT_IDX_padding,
TLSEXT_IDX_psk,
/* Dummy index - must always be the last entry */
@ -2583,6 +2585,8 @@ __owur STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL_CONNECTION *sc);
__owur int ssl_x509err2alert(int type);
void ssl_sort_cipher_list(void);
int ssl_load_ciphers(SSL_CTX *ctx);
int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk,
WPACKET *pkt);
__owur int ssl_setup_sigalgs(SSL_CTX *ctx);
int ssl_load_groups(SSL_CTX *ctx);
int ssl_load_sigalgs(SSL_CTX *ctx);

View file

@ -333,6 +333,10 @@ const char *SSL_alert_desc_string(int value)
return "BH";
case TLS1_AD_UNKNOWN_PSK_IDENTITY:
return "UP";
#ifndef OPENSSL_NO_ECH
case TLS1_AD_ECH_REQUIRED:
return "RR";
#endif
default:
return "UK";
}
@ -403,6 +407,10 @@ const char *SSL_alert_desc_string_long(int value)
return "unknown PSK identity";
case TLS1_AD_NO_APPLICATION_PROTOCOL:
return "no application protocol";
#ifndef OPENSSL_NO_ECH
case TLS1_AD_ECH_REQUIRED:
return "ECH required";
#endif
default:
return "unknown";
}

View file

@ -21,6 +21,40 @@
#include <openssl/ocsp.h>
#include <openssl/core_names.h>
/*
* values for ext_defs ech_handling field
* exceptionally, we don't conditionally compile that field to avoid a pile of
* fndefs all over the ext_defs values
*/
#define OSSL_ECH_HANDLING_CALL_BOTH 1 /* call constructor both times */
#define OSSL_ECH_HANDLING_COMPRESS 2 /* compress outer value into inner */
#define OSSL_ECH_HANDLING_DUPLICATE 3 /* same value in inner and outer */
/*
* DUPLICATE isn't really useful other than to show we can,
* and for debugging/tests/coverage so may disappear. Changes mostly
* won't affect the outer CH size, due to padding, but might for some
* larger extensions.
*
* Note there is a co-dependency with test/recipes/75-test_quicapi.t:
* If you change an |ech_handling| value, that may well affect the order
* of extensions in a ClientHello, which is reflected in the test data
* in test/recipes/75-test_quicapi_data/\*.txt files. To fix, you need
* to look in test-runs/test_quicapi for the "new" files and then edit
* (replacing actual octets with "?" in relevant places), and copy the
* result back over to test/recipes/75-test_quicapi_data/. The reason
* this happens is the ECH COMPRESS'd extensions need to be contiguous
* in the ClientHello, so changes to/from COMPRESS affect extension
* order, in inner and outer CH. There doesn't seem to be an easy,
* generic, way to reconcile these compile-time changes with having
* fixed value test files. Likely the best option is to decide on the
* disposition of ECH COMPRESS or not and consider that an at least
* medium-term thing. (But still allow other builds to vary at
* compile time if they need something different.)
*/
#ifndef OPENSSL_NO_ECH
static int init_ech(SSL_CONNECTION *s, unsigned int context);
#endif /* OPENSSL_NO_ECH */
static int final_renegotiate(SSL_CONNECTION *s, unsigned int context, int sent);
static int init_server_name(SSL_CONNECTION *s, unsigned int context);
static int final_server_name(SSL_CONNECTION *s, unsigned int context, int sent);
@ -86,6 +120,11 @@ typedef struct extensions_definition_st {
* protocol versions
*/
unsigned int context;
/*
* exceptionally, we don't conditionally compile this field to avoid a pile of
* fndefs all over the ext_defs values
*/
int ech_handling; /* how to handle ECH for this extension type */
/*
* Initialise extension before parsing. Always called for relevant contexts
* even if extension not present
@ -140,17 +179,20 @@ typedef struct extensions_definition_st {
* NOTE: WebSphere Application Server 7+ cannot handle empty extensions at
* the end, keep these extensions before signature_algorithm.
*/
#define INVALID_EXTENSION { TLSEXT_TYPE_invalid, 0, NULL, NULL, NULL, NULL, NULL, NULL }
#define INVALID_EXTENSION { TLSEXT_TYPE_invalid, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_renegotiate,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_2_AND_BELOW_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
NULL, tls_parse_ctos_renegotiate, tls_parse_stoc_renegotiate,
tls_construct_stoc_renegotiate, tls_construct_ctos_renegotiate,
final_renegotiate },
{ TLSEXT_TYPE_server_name,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
OSSL_ECH_HANDLING_CALL_BOTH,
init_server_name,
tls_parse_ctos_server_name, tls_parse_stoc_server_name,
tls_construct_stoc_server_name, tls_construct_ctos_server_name,
@ -158,12 +200,14 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_max_fragment_length,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
OSSL_ECH_HANDLING_COMPRESS,
NULL, tls_parse_ctos_maxfragmentlen, tls_parse_stoc_maxfragmentlen,
tls_construct_stoc_maxfragmentlen, tls_construct_ctos_maxfragmentlen,
final_maxfragmentlen },
#ifndef OPENSSL_NO_SRP
{ TLSEXT_TYPE_srp,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
init_srp, tls_parse_ctos_srp, NULL, NULL, tls_construct_ctos_srp, NULL },
#else
INVALID_EXTENSION,
@ -171,6 +215,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_ec_point_formats,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_2_AND_BELOW_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
init_ec_point_formats, tls_parse_ctos_ec_pt_formats, tls_parse_stoc_ec_pt_formats,
tls_construct_stoc_ec_pt_formats, tls_construct_ctos_ec_pt_formats,
final_ec_pt_formats },
@ -202,12 +247,14 @@ static const EXTENSION_DEFINITION ext_defs[] = {
TLSEXT_TYPE_supported_groups,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| SSL_EXT_TLS1_2_SERVER_HELLO,
OSSL_ECH_HANDLING_COMPRESS,
NULL, tls_parse_ctos_supported_groups, NULL,
tls_construct_stoc_supported_groups,
tls_construct_ctos_supported_groups, NULL },
{ TLSEXT_TYPE_session_ticket,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_2_AND_BELOW_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
init_session_ticket, tls_parse_ctos_session_ticket,
tls_parse_stoc_session_ticket, tls_construct_stoc_session_ticket,
tls_construct_ctos_session_ticket, NULL },
@ -215,6 +262,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_status_request,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_3_CERTIFICATE | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
OSSL_ECH_HANDLING_COMPRESS,
init_status_request, tls_parse_ctos_status_request,
tls_parse_stoc_status_request, tls_construct_stoc_status_request,
tls_construct_ctos_status_request, NULL },
@ -225,6 +273,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_next_proto_neg,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_2_AND_BELOW_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
init_npn, tls_parse_ctos_npn, tls_parse_stoc_npn,
tls_construct_stoc_next_proto_neg, tls_construct_ctos_npn, NULL },
#else
@ -237,12 +286,14 @@ static const EXTENSION_DEFINITION ext_defs[] = {
TLSEXT_TYPE_application_layer_protocol_negotiation,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
OSSL_ECH_HANDLING_CALL_BOTH,
init_alpn, tls_parse_ctos_alpn, tls_parse_stoc_alpn,
tls_construct_stoc_alpn, tls_construct_ctos_alpn, final_alpn },
#ifndef OPENSSL_NO_SRTP
{ TLSEXT_TYPE_use_srtp,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS | SSL_EXT_DTLS_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
init_srtp, tls_parse_ctos_use_srtp, tls_parse_stoc_use_srtp,
tls_construct_stoc_use_srtp, tls_construct_ctos_use_srtp, NULL },
#else
@ -251,12 +302,22 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_encrypt_then_mac,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_2_AND_BELOW_ONLY,
/*
* If you want to demonstrate/exercise duplicate, then
* this does that and has no effect on sizes, but it
* will break the quicapi test (see above). Probably
* best done in local tests and not committed to any
* upstream.
* OSSL_ECH_HANDLING_DUPLICATE,
*/
OSSL_ECH_HANDLING_COMPRESS,
init_etm, tls_parse_ctos_etm, tls_parse_stoc_etm,
tls_construct_stoc_etm, tls_construct_ctos_etm, NULL },
#ifndef OPENSSL_NO_CT
{ TLSEXT_TYPE_signed_certificate_timestamp,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_3_CERTIFICATE | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
OSSL_ECH_HANDLING_COMPRESS,
NULL,
/*
* No server side support for this, but can be provided by a custom
@ -270,10 +331,12 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_extended_master_secret,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_2_AND_BELOW_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
init_ems, tls_parse_ctos_ems, tls_parse_stoc_ems,
tls_construct_stoc_ems, tls_construct_ctos_ems, final_ems },
{ TLSEXT_TYPE_signature_algorithms_cert,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
OSSL_ECH_HANDLING_COMPRESS,
init_sig_algs_cert, tls_parse_ctos_sig_algs_cert,
tls_parse_ctos_sig_algs_cert,
/* We do not generate signature_algorithms_cert at present. */
@ -281,6 +344,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{
TLSEXT_TYPE_post_handshake_auth,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
init_post_handshake_auth,
tls_parse_ctos_post_handshake_auth,
NULL,
@ -291,6 +355,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_client_cert_type,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| SSL_EXT_TLS1_2_SERVER_HELLO,
OSSL_ECH_HANDLING_CALL_BOTH,
init_client_cert_type,
tls_parse_ctos_client_cert_type, tls_parse_stoc_client_cert_type,
tls_construct_stoc_client_cert_type, tls_construct_ctos_client_cert_type,
@ -298,18 +363,21 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_server_cert_type,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| SSL_EXT_TLS1_2_SERVER_HELLO,
OSSL_ECH_HANDLING_CALL_BOTH,
init_server_cert_type,
tls_parse_ctos_server_cert_type, tls_parse_stoc_server_cert_type,
tls_construct_stoc_server_cert_type, tls_construct_ctos_server_cert_type,
NULL },
{ TLSEXT_TYPE_signature_algorithms,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
OSSL_ECH_HANDLING_COMPRESS,
init_sig_algs, tls_parse_ctos_sig_algs,
tls_parse_ctos_sig_algs, tls_construct_ctos_sig_algs,
tls_construct_ctos_sig_algs, final_sig_algs },
{ TLSEXT_TYPE_supported_versions,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_SERVER_HELLO
| SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST | SSL_EXT_TLS_IMPLEMENTATION_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
NULL,
/* Processed inline as part of version selection */
NULL, tls_parse_stoc_supported_versions,
@ -318,6 +386,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_psk_kex_modes,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS_IMPLEMENTATION_ONLY
| SSL_EXT_TLS1_3_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
init_psk_kex_modes, tls_parse_ctos_psk_kex_modes, NULL, NULL,
tls_construct_ctos_psk_kex_modes, NULL },
{ /*
@ -328,6 +397,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_SERVER_HELLO
| SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST | SSL_EXT_TLS_IMPLEMENTATION_ONLY
| SSL_EXT_TLS1_3_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
NULL, tls_parse_ctos_key_share, tls_parse_stoc_key_share,
tls_construct_stoc_key_share, tls_construct_ctos_key_share,
final_key_share },
@ -335,6 +405,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
TLSEXT_TYPE_cookie,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST
| SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
NULL, tls_parse_ctos_cookie, tls_parse_stoc_cookie,
tls_construct_stoc_cookie, tls_construct_ctos_cookie, NULL },
{ /*
@ -345,10 +416,12 @@ static const EXTENSION_DEFINITION ext_defs[] = {
TLSEXT_TYPE_cryptopro_bug,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
| SSL_EXT_TLS1_2_AND_BELOW_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
NULL, NULL, NULL, tls_construct_stoc_cryptopro_bug, NULL, NULL },
{ TLSEXT_TYPE_compress_certificate,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST
| SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
tls_init_compress_certificate,
tls_parse_compress_certificate, tls_parse_compress_certificate,
tls_construct_compress_certificate, tls_construct_compress_certificate,
@ -356,6 +429,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
{ TLSEXT_TYPE_early_data,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| SSL_EXT_TLS1_3_NEW_SESSION_TICKET | SSL_EXT_TLS1_3_ONLY,
OSSL_ECH_HANDLING_CALL_BOTH,
NULL, tls_parse_ctos_early_data, tls_parse_stoc_early_data,
tls_construct_stoc_early_data, tls_construct_ctos_early_data,
final_early_data },
@ -363,6 +437,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
TLSEXT_TYPE_certificate_authorities,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST
| SSL_EXT_TLS1_3_ONLY,
OSSL_ECH_HANDLING_COMPRESS,
init_certificate_authorities,
tls_parse_certificate_authorities,
tls_parse_certificate_authorities,
@ -370,9 +445,34 @@ static const EXTENSION_DEFINITION ext_defs[] = {
tls_construct_certificate_authorities,
NULL,
},
#ifndef OPENSSL_NO_ECH
{ TLSEXT_TYPE_ech,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ONLY | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS | SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
OSSL_ECH_HANDLING_CALL_BOTH,
init_ech,
/*
* TODO(ECH): add server calls as per below in a bit
* tls_parse_ctos_ech, tls_parse_stoc_ech,
* tls_construct_stoc_ech, tls_construct_ctos_ech,
*/
NULL, tls_parse_stoc_ech,
NULL, tls_construct_ctos_ech,
NULL },
{ TLSEXT_TYPE_outer_extensions,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ONLY,
OSSL_ECH_HANDLING_CALL_BOTH,
NULL,
NULL, NULL,
NULL, NULL,
NULL },
#else /* OPENSSL_NO_ECH */
INVALID_EXTENSION,
INVALID_EXTENSION,
#endif /* END_OPENSSL_NO_ECH */
{ /* Must be immediately before pre_shared_key */
TLSEXT_TYPE_padding,
SSL_EXT_CLIENT_HELLO,
OSSL_ECH_HANDLING_CALL_BOTH,
NULL,
/* We send this, but don't read it */
NULL, NULL, NULL, tls_construct_ctos_padding, NULL },
@ -380,10 +480,140 @@ static const EXTENSION_DEFINITION ext_defs[] = {
TLSEXT_TYPE_psk,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_SERVER_HELLO
| SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
OSSL_ECH_HANDLING_CALL_BOTH,
NULL, tls_parse_ctos_psk, tls_parse_stoc_psk, tls_construct_stoc_psk,
tls_construct_ctos_psk, final_psk }
};
#ifndef OPENSSL_NO_ECH
/*
* Copy an inner extension value to outer.
* inner CH must have been pre-decoded into s->clienthello->pre_proc_exts
* already.
*/
static int ech_copy_inner2outer(SSL_CONNECTION *s, uint16_t ext_type,
int ind, WPACKET *pkt)
{
RAW_EXTENSION *myext = NULL, *raws = NULL;
if (s == NULL || s->clienthello == NULL)
return OSSL_ECH_SAME_EXT_ERR;
raws = s->clienthello->pre_proc_exts;
if (raws == NULL)
return OSSL_ECH_SAME_EXT_ERR;
myext = &raws[ind];
OSSL_TRACE_BEGIN(TLS)
{
BIO_printf(trc_out, "inner2outer: Copying ext type %d to outer\n",
ext_type);
}
OSSL_TRACE_END(TLS);
/*
* This one wasn't in inner, so re-do processing. We don't
* actually do this currently, but could.
*/
if (myext == NULL)
return OSSL_ECH_SAME_EXT_CONTINUE;
/* copy inner value to outer */
if (PACKET_data(&myext->data) != NULL
&& PACKET_remaining(&myext->data) > 0) {
if (!WPACKET_put_bytes_u16(pkt, ext_type)
|| !WPACKET_sub_memcpy_u16(pkt, PACKET_data(&myext->data),
PACKET_remaining(&myext->data)))
return OSSL_ECH_SAME_EXT_ERR;
} else {
/* empty extension */
if (!WPACKET_put_bytes_u16(pkt, ext_type)
|| !WPACKET_put_bytes_u16(pkt, 0))
return OSSL_ECH_SAME_EXT_ERR;
}
return 1;
}
/*
* DUPEMALL is useful for testing - this turns off compression and
* causes two calls to each extension constructor, which'd be the same
* as making all entries in ext_tab use the CALL_BOTH value
*/
#undef DUPEMALL
/*
* Check if we're using the same/different key shares
* return 1 if same key share in inner and outer, 0 otherwise
*/
int ossl_ech_same_key_share(void)
{
#ifdef DUPEMALL
return 0;
#endif
return ext_defs[TLSEXT_IDX_key_share].ech_handling
!= OSSL_ECH_HANDLING_CALL_BOTH;
}
/*
* say if extension at index |ind| in ext_defs is to be ECH compressed
* return 1 if this one is to be compressed, 0 if not, -1 for error
*/
int ossl_ech_2bcompressed(int ind)
{
const int nexts = OSSL_NELEM(ext_defs);
#ifdef DUPEMALL
return 0;
#endif
if (ind < 0 || ind >= nexts)
return -1;
return ext_defs[ind].ech_handling == OSSL_ECH_HANDLING_COMPRESS;
}
/* as needed, repeat extension from inner in outer handling compression */
int ossl_ech_same_ext(SSL_CONNECTION *s, WPACKET *pkt)
{
unsigned int type = 0;
int tind = 0, nexts = OSSL_NELEM(ext_defs);
#ifdef DUPEMALL
return OSSL_ECH_SAME_EXT_CONTINUE;
#endif
if (s == NULL || s->ext.ech.es == NULL)
return OSSL_ECH_SAME_EXT_CONTINUE; /* nothing to do */
tind = s->ext.ech.ext_ind;
/* If this index'd extension won't be compressed, we're done */
if (tind < 0 || tind >= nexts)
return OSSL_ECH_SAME_EXT_ERR;
type = ext_defs[tind].type;
if (s->ext.ech.ch_depth == 1) {
/* inner CH - just note compression as configured */
if (ext_defs[tind].ech_handling != OSSL_ECH_HANDLING_COMPRESS)
return OSSL_ECH_SAME_EXT_CONTINUE;
/* mark this one to be "compressed" */
if (s->ext.ech.n_outer_only >= OSSL_ECH_OUTERS_MAX)
return OSSL_ECH_SAME_EXT_ERR;
s->ext.ech.outer_only[s->ext.ech.n_outer_only] = type;
s->ext.ech.n_outer_only++;
OSSL_TRACE_BEGIN(TLS)
{
BIO_printf(trc_out, "ech_same_ext: Marking (type %u, ind %d "
"tot-comp %d) for compression\n",
type, tind,
(int)s->ext.ech.n_outer_only);
}
OSSL_TRACE_END(TLS);
return OSSL_ECH_SAME_EXT_CONTINUE;
} else {
/* Copy value from inner to outer, or indicate a new value needed */
if (s->clienthello == NULL || pkt == NULL)
return OSSL_ECH_SAME_EXT_ERR;
if (ext_defs[tind].ech_handling == OSSL_ECH_HANDLING_CALL_BOTH)
return OSSL_ECH_SAME_EXT_CONTINUE;
else
return ech_copy_inner2outer(s, type, tind, pkt);
}
/* just in case - shouldn't happen */
return OSSL_ECH_SAME_EXT_ERR;
}
#endif
/* Returns a TLSEXT_TYPE for the given index */
unsigned int ossl_get_extension_type(size_t idx)
{
@ -804,6 +1034,9 @@ int tls_construct_extensions(SSL_CONNECTION *s, WPACKET *pkt,
int min_version, max_version = 0, reason;
const EXTENSION_DEFINITION *thisexd;
int for_comp = (context & SSL_EXT_TLS1_3_CERTIFICATE_COMPRESSION) != 0;
#ifndef OPENSSL_NO_ECH
int pass;
#endif
if (!WPACKET_start_sub_packet_u16(pkt)
/*
@ -838,38 +1071,75 @@ int tls_construct_extensions(SSL_CONNECTION *s, WPACKET *pkt,
return 0;
}
for (i = 0, thisexd = ext_defs; i < OSSL_NELEM(ext_defs); i++, thisexd++) {
EXT_RETURN (*construct)(SSL_CONNECTION *s, WPACKET *pkt,
unsigned int context,
X509 *x, size_t chainidx);
EXT_RETURN ret;
#ifndef OPENSSL_NO_ECH
/*
* Two passes - we first construct the to-be-ECH-compressed
* extensions, and then go around again constructing those that
* aren't to be ECH-compressed. We need to ensure this ordering
* so that all the ECH-compressed extensions are contiguous
* in the encoding. The actual compression happens later in
* ech_encode_inner().
*/
for (pass = 0; pass <= 1; pass++)
#endif
/* Skip if not relevant for our context */
if (!should_add_extension(s, thisexd->context, context, max_version))
continue;
for (i = 0, thisexd = ext_defs; i < OSSL_NELEM(ext_defs);
i++, thisexd++) {
EXT_RETURN (*construct)(SSL_CONNECTION *s, WPACKET *pkt,
unsigned int context,
X509 *x, size_t chainidx);
EXT_RETURN ret;
construct = s->server ? thisexd->construct_stoc
: thisexd->construct_ctos;
#ifndef OPENSSL_NO_ECH
/* do compressed in pass 0, non-compressed in pass 1 */
if (ossl_ech_2bcompressed(i) == pass)
continue;
/* stash index - needed for COMPRESS ECH handling */
s->ext.ech.ext_ind = i;
#endif
/* Skip if not relevant for our context */
if (!should_add_extension(s, thisexd->context, context, max_version))
continue;
if (construct == NULL)
continue;
construct = s->server ? thisexd->construct_stoc
: thisexd->construct_ctos;
ret = construct(s, pkt, context, x, chainidx);
if (ret == EXT_RETURN_FAIL) {
/* SSLfatal() already called */
if (construct == NULL)
continue;
ret = construct(s, pkt, context, x, chainidx);
if (ret == EXT_RETURN_FAIL) {
/* SSLfatal() already called */
return 0;
}
if (ret == EXT_RETURN_SENT
&& (context & (SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST | SSL_EXT_TLS1_3_NEW_SESSION_TICKET)) != 0)
s->ext.extflags[i] |= SSL_EXT_FLAG_SENT;
}
#ifndef OPENSSL_NO_ECH
/*
* don't close yet if client in the middle of doing ECH, we'll
* eventually close this in ech_aad_and_encrypt() after we add
* the real ECH extension value
*/
if (s->server
|| s->ext.ech.attempted == 0
|| s->ext.ech.ch_depth == 1
|| s->ext.ech.grease == OSSL_ECH_IS_GREASE) {
if (!WPACKET_close(pkt)) {
if (!for_comp)
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
if (ret == EXT_RETURN_SENT
&& (context & (SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST | SSL_EXT_TLS1_3_NEW_SESSION_TICKET)) != 0)
s->ext.extflags[i] |= SSL_EXT_FLAG_SENT;
}
#else
if (!WPACKET_close(pkt)) {
if (!for_comp)
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
#endif
return 1;
}
@ -931,6 +1201,26 @@ static int init_server_name(SSL_CONNECTION *s, unsigned int context)
return 1;
}
#ifndef OPENSSL_NO_ECH
/*
* Just note that ech is not yet done
* return 1 for good, 0 otherwise
*/
static int init_ech(SSL_CONNECTION *s, unsigned int context)
{
const int nexts = OSSL_NELEM(ext_defs);
/* we don't need this assert everywhere - anywhere is fine */
if (!ossl_assert(TLSEXT_IDX_num_builtins == nexts)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
if ((context & SSL_EXT_CLIENT_HELLO) != 0)
s->ext.ech.done = 0;
return 1;
}
#endif /* OPENSSL_NO_ECH */
static int final_server_name(SSL_CONNECTION *s, unsigned int context, int sent)
{
int ret = SSL_TLSEXT_ERR_NOACK;
@ -1478,6 +1768,13 @@ int tls_psk_do_binder(SSL_CONNECTION *s, const EVP_MD *md,
int usepskfored = 0;
SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
#ifndef OPENSSL_NO_ECH
unsigned char hashval[EVP_MAX_MD_SIZE];
unsigned int hashlen = 0;
EVP_MD_CTX *ctx = NULL;
WPACKET tpkt;
BUF_MEM *tpkt_mem = NULL;
#endif
/* Ensure cast to size_t is safe */
if (!ossl_assert(hashsizei > 0)) {
@ -1559,11 +1856,47 @@ int tls_psk_do_binder(SSL_CONNECTION *s, const EVP_MD *md,
long hdatalen_l;
void *hdata;
hdatalen = hdatalen_l = BIO_get_mem_data(s->s3.handshake_buffer, &hdata);
if (hdatalen_l <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_HANDSHAKE_LENGTH);
goto err;
#ifndef OPENSSL_NO_ECH
/* handle the hashing as per ECH needs (on client) */
if (s->ext.ech.attempted == 1 && s->ext.ech.ch_depth == 1) {
if ((tpkt_mem = BUF_MEM_new()) == NULL
|| !BUF_MEM_grow(tpkt_mem, SSL3_RT_MAX_PLAIN_LENGTH)
|| !WPACKET_init(&tpkt, tpkt_mem)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
hashlen = EVP_MD_size(md);
if ((ctx = EVP_MD_CTX_new()) == NULL
|| EVP_DigestInit_ex(ctx, md, NULL) <= 0
|| EVP_DigestUpdate(ctx, s->ext.ech.innerch1,
s->ext.ech.innerch1_len)
<= 0
|| EVP_DigestFinal_ex(ctx, hashval, &hashlen) <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
EVP_MD_CTX_free(ctx);
ctx = NULL;
if (!WPACKET_put_bytes_u8(&tpkt, SSL3_MT_MESSAGE_HASH)
|| !WPACKET_put_bytes_u24(&tpkt, hashlen)
|| !WPACKET_memcpy(&tpkt, hashval, hashlen)
|| !WPACKET_memcpy(&tpkt, s->ext.ech.kepthrr,
s->ext.ech.kepthrr_len)
|| !WPACKET_get_length(&tpkt, &hdatalen)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
hdata = WPACKET_get_curr(&tpkt) - hdatalen;
} else {
#endif
hdatalen = hdatalen_l = BIO_get_mem_data(s->s3.handshake_buffer, &hdata);
if (hdatalen_l <= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_HANDSHAKE_LENGTH);
goto err;
}
#ifndef OPENSSL_NO_ECH
}
#endif
/*
* For servers the handshake buffer data will include the second
@ -1635,6 +1968,13 @@ err:
OPENSSL_cleanse(finishedkey, sizeof(finishedkey));
EVP_PKEY_free(mackey);
EVP_MD_CTX_free(mctx);
#ifndef OPENSSL_NO_ECH
EVP_MD_CTX_free(ctx);
if (tpkt_mem != NULL) {
WPACKET_cleanup(&tpkt);
BUF_MEM_free(tpkt_mem);
}
#endif
return ret;
}
@ -1762,6 +2102,9 @@ static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET
if (sc->cert_comp_prefs[0] == TLSEXT_comp_cert_none)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(sc, pkt);
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_compress_certificate)
|| !WPACKET_start_sub_packet_u16(pkt)

View file

@ -13,6 +13,9 @@
#include "internal/ssl_unwrap.h"
#include "internal/tlsgroups.h"
#include "statem_local.h"
#ifndef OPENSSL_NO_ECH
#include <openssl/rand.h>
#endif
/* Used in the negotiate_dhe function */
typedef enum {
@ -43,6 +46,10 @@ EXT_RETURN tls_construct_ctos_renegotiate(SSL_CONNECTION *s, WPACKET *pkt,
return EXT_RETURN_NOT_SENT;
}
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_renegotiate)
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_put_bytes_u8(pkt, 0)
@ -54,6 +61,10 @@ EXT_RETURN tls_construct_ctos_renegotiate(SSL_CONNECTION *s, WPACKET *pkt,
return EXT_RETURN_SENT;
}
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
/* Add a complete RI extension if renegotiating */
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_renegotiate)
|| !WPACKET_start_sub_packet_u16(pkt)
@ -71,6 +82,43 @@ EXT_RETURN tls_construct_ctos_server_name(SSL_CONNECTION *s, WPACKET *pkt,
unsigned int context, X509 *x,
size_t chainidx)
{
#ifndef OPENSSL_NO_ECH
char *chosen = s->ext.hostname;
OSSL_HPKE_SUITE suite;
OSSL_ECHSTORE_ENTRY *ee = NULL;
if (s->ext.ech.es != NULL) {
if (ossl_ech_pick_matching_cfg(s, &ee, &suite) != 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_NOT_SENT;
}
/* Don't send outer SNI if external API says so */
if (s->ext.ech.ch_depth == 0 && s->ext.ech.no_outer == 1)
return EXT_RETURN_NOT_SENT;
if (s->ext.ech.ch_depth == 1) /* inner */
chosen = s->ext.hostname;
if (s->ext.ech.ch_depth == 0) { /* outer */
if (s->ext.ech.outer_hostname != NULL) /* prefer API */
chosen = s->ext.ech.outer_hostname;
else /* use name from ECHConfig */
chosen = ee->public_name;
}
}
if (chosen == NULL)
return EXT_RETURN_NOT_SENT;
/* Add TLS extension servername to the Client Hello message */
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_server_name)
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_put_bytes_u8(pkt, TLSEXT_NAMETYPE_host_name)
|| !WPACKET_sub_memcpy_u16(pkt, chosen, strlen(chosen))
|| !WPACKET_close(pkt)
|| !WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
return EXT_RETURN_SENT;
#else
if (s->ext.hostname == NULL)
return EXT_RETURN_NOT_SENT;
@ -90,6 +138,7 @@ EXT_RETURN tls_construct_ctos_server_name(SSL_CONNECTION *s, WPACKET *pkt,
}
return EXT_RETURN_SENT;
#endif
}
/* Push a Max Fragment Len extension into ClientHello */
@ -99,6 +148,9 @@ EXT_RETURN tls_construct_ctos_maxfragmentlen(SSL_CONNECTION *s, WPACKET *pkt,
{
if (s->ext.max_fragment_len_mode == TLSEXT_max_fragment_length_DISABLED)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
/* Add Max Fragment Length extension if client enabled it. */
/*-
@ -125,6 +177,9 @@ EXT_RETURN tls_construct_ctos_srp(SSL_CONNECTION *s, WPACKET *pkt,
/* Add SRP username if there is one */
if (s->srp_ctx.login == NULL)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_srp)
/* Sub-packet for SRP extension */
@ -238,6 +293,9 @@ EXT_RETURN tls_construct_ctos_ec_pt_formats(SSL_CONNECTION *s, WPACKET *pkt,
tls1_get_formatlist(s, &pformats, &num_formats);
if (num_formats == 0)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
/* Add TLS extension ECPointFormats to the ClientHello message */
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ec_point_formats)
@ -277,6 +335,9 @@ EXT_RETURN tls_construct_ctos_supported_groups(SSL_CONNECTION *s, WPACKET *pkt,
&& (dtls ? DTLS_VERSION_LE(max_version, DTLS1_2_VERSION)
: (max_version <= TLS1_2_VERSION)))
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
/*
* Add TLS extension supported_groups to the ClientHello message
@ -339,6 +400,9 @@ EXT_RETURN tls_construct_ctos_session_ticket(SSL_CONNECTION *s, WPACKET *pkt,
if (!tls_use_ticket(s))
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!s->new_session && s->session != NULL
&& s->session->ext.tick != NULL
@ -395,6 +459,10 @@ EXT_RETURN tls_construct_ctos_sig_algs(SSL_CONNECTION *s, WPACKET *pkt,
return EXT_RETURN_NOT_SENT;
}
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
salglen = tls12_get_psigalgs(s, 1, &salg);
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_signature_algorithms)
/* Sub-packet for sig-algs extension */
@ -424,6 +492,9 @@ EXT_RETURN tls_construct_ctos_status_request(SSL_CONNECTION *s, WPACKET *pkt,
if (s->ext.status_type != TLSEXT_STATUSTYPE_ocsp)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_status_request)
/* Sub-packet for status request extension */
@ -484,6 +555,9 @@ EXT_RETURN tls_construct_ctos_npn(SSL_CONNECTION *s, WPACKET *pkt,
if (SSL_CONNECTION_GET_CTX(s)->ext.npn_select_cb == NULL
|| !SSL_IS_FIRST_HANDSHAKE(s))
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
/*
* The client advertises an empty extension to indicate its support
@ -503,8 +577,46 @@ EXT_RETURN tls_construct_ctos_alpn(SSL_CONNECTION *s, WPACKET *pkt,
unsigned int context,
X509 *x, size_t chainidx)
{
s->s3.alpn_sent = 0;
#ifndef OPENSSL_NO_ECH
unsigned char *aval = NULL;
size_t alen = 0;
#endif
s->s3.alpn_sent = 0;
#ifndef OPENSSL_NO_ECH
/*
* If we have different alpn and alpn_outer values, then we set
* the appropriate one for inner and outer.
* If no alpn is set (for inner or outer), we don't send any.
* If only an inner is set then we send the same in both.
* Logic above is on the basis that alpn's aren't that sensitive,
* usually, so special action is needed to do better.
* We also don't support a way to send alpn only in the inner.
* If you don't want the inner value in the outer, you have to
* pick what to send in the outer and send that.
*/
if (!SSL_IS_FIRST_HANDSHAKE(s))
return EXT_RETURN_NOT_SENT;
aval = s->ext.alpn;
alen = s->ext.alpn_len;
if (s->ext.ech.ch_depth == 1 && s->ext.alpn == NULL) /* inner */
return EXT_RETURN_NOT_SENT;
if (s->ext.ech.ch_depth == 0 && s->ext.alpn == NULL
&& s->ext.ech.alpn_outer == NULL) /* outer */
return EXT_RETURN_NOT_SENT;
if (s->ext.ech.ch_depth == 0 && s->ext.ech.alpn_outer != NULL) {
aval = s->ext.ech.alpn_outer;
alen = s->ext.ech.alpn_outer_len;
}
if (!WPACKET_put_bytes_u16(pkt,
TLSEXT_TYPE_application_layer_protocol_negotiation)
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_sub_memcpy_u16(pkt, aval, alen)
|| !WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
#else
if (s->ext.alpn == NULL || !SSL_IS_FIRST_HANDSHAKE(s))
return EXT_RETURN_NOT_SENT;
@ -517,6 +629,7 @@ EXT_RETURN tls_construct_ctos_alpn(SSL_CONNECTION *s, WPACKET *pkt,
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
#endif
s->s3.alpn_sent = 1;
return EXT_RETURN_SENT;
@ -533,6 +646,9 @@ EXT_RETURN tls_construct_ctos_use_srtp(SSL_CONNECTION *s, WPACKET *pkt,
if (clnt == NULL)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_use_srtp)
/* Sub-packet for SRTP extension */
@ -570,6 +686,9 @@ EXT_RETURN tls_construct_ctos_etm(SSL_CONNECTION *s, WPACKET *pkt,
{
if (s->options & SSL_OP_NO_ENCRYPT_THEN_MAC)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_encrypt_then_mac)
|| !WPACKET_put_bytes_u16(pkt, 0)) {
@ -591,6 +710,9 @@ EXT_RETURN tls_construct_ctos_sct(SSL_CONNECTION *s, WPACKET *pkt,
/* Not defined for client Certificates */
if (x != NULL)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_signed_certificate_timestamp)
|| !WPACKET_put_bytes_u16(pkt, 0)) {
@ -608,6 +730,9 @@ EXT_RETURN tls_construct_ctos_ems(SSL_CONNECTION *s, WPACKET *pkt,
{
if (s->options & SSL_OP_NO_EXTENDED_MASTER_SECRET)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_extended_master_secret)
|| !WPACKET_put_bytes_u16(pkt, 0)) {
@ -636,6 +761,9 @@ EXT_RETURN tls_construct_ctos_supported_versions(SSL_CONNECTION *s, WPACKET *pkt
*/
if (max_version < TLS1_3_VERSION)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_supported_versions)
|| !WPACKET_start_sub_packet_u16(pkt)
@ -668,6 +796,10 @@ EXT_RETURN tls_construct_ctos_psk_kex_modes(SSL_CONNECTION *s, WPACKET *pkt,
#ifndef OPENSSL_NO_TLS1_3
int nodhe = s->options & SSL_OP_ALLOW_NO_DHE_KEX;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_psk_kex_modes)
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_start_sub_packet_u8(pkt)
@ -727,6 +859,15 @@ static int add_key_share(SSL_CONNECTION *s, WPACKET *pkt, unsigned int group_id,
goto err;
}
#ifndef OPENSSL_NO_ECH
if (s->ext.ech.ch_depth == 1) { /* stash inner */
EVP_PKEY_up_ref(key_share_key);
EVP_PKEY_free(s->ext.ech.tmp_pkey);
s->ext.ech.tmp_pkey = key_share_key;
s->ext.ech.group_id = group_id;
}
#endif
/* For backward compatibility, we use the first valid group to add a key share */
if (loop_num == 0) {
s->s3.tmp.pkey = key_share_key;
@ -760,6 +901,10 @@ EXT_RETURN tls_construct_ctos_key_share(SSL_CONNECTION *s, WPACKET *pkt,
int add_only_one = 0;
size_t valid_keyshare = 0;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
/* key_share extension */
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_key_share)
/* Extension data sub-packet */
@ -845,6 +990,9 @@ EXT_RETURN tls_construct_ctos_cookie(SSL_CONNECTION *s, WPACKET *pkt,
/* Should only be set if we've had an HRR */
if (s->ext.tls13_cookie_len == 0)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_cookie)
/* Extension data sub-packet */
@ -879,6 +1027,33 @@ EXT_RETURN tls_construct_ctos_early_data(SSL_CONNECTION *s, WPACKET *pkt,
const EVP_MD *handmd = NULL;
SSL *ussl = SSL_CONNECTION_GET_USER_SSL(s);
#ifndef OPENSSL_NO_ECH
/*
* If we're attempting ECH and processing the outer CH
* then we only need to check if the extension is to be
* sent or not - any other processing (with side effects)
* happened already for the inner CH.
*/
if (s->ext.ech.es != NULL && s->ext.ech.ch_depth == 0) {
/*
* if we called this for inner and did send then
* the following two things should be set, if so,
* then send again in the outer CH.
*/
if (s->ext.early_data == SSL_EARLY_DATA_REJECTED
&& s->ext.early_data_ok == 1) {
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data)
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
return EXT_RETURN_SENT;
} else {
return EXT_RETURN_NOT_SENT;
}
}
#endif
if (s->hello_retry_request == SSL_HRR_PENDING)
handmd = ssl_handshake_md(s);
@ -1259,6 +1434,90 @@ dopsksess:
return EXT_RETURN_FAIL;
}
#ifndef OPENSSL_NO_ECH
/*
* For ECH if we're processing the outer CH and the inner CH
* has a PSK, then we want to send a GREASE PSK in the outer.
* We'll do that by just replacing the ticket value itself
* with random values of the same length.
*/
if (s->ext.ech.es != NULL && s->ext.ech.ch_depth == 0) {
unsigned char *rndbuf = NULL;
size_t totalrndsize = 0;
if (s->session == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
totalrndsize = s->session->ext.ticklen
+ 4 /* agems */
+ s->psksession_id_len
+ reshashsize
+ pskhashsize;
rndbuf = OPENSSL_malloc(totalrndsize);
if (rndbuf == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
/* outer CH allocate a similar sized random value */
if (RAND_bytes_ex(s->ssl.ctx->libctx, rndbuf, totalrndsize,
RAND_DRBG_STRENGTH)
<= 0) {
OPENSSL_free(rndbuf);
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
/* set agems from random buffer */
agems = *((uint32_t *)(rndbuf + s->session->ext.ticklen));
if (dores != 0) {
if (!WPACKET_sub_memcpy_u16(pkt, rndbuf,
s->session->ext.ticklen)
|| !WPACKET_put_bytes_u32(pkt, agems)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
OPENSSL_free(rndbuf);
return EXT_RETURN_FAIL;
}
}
if (s->psksession != NULL) {
if (!WPACKET_sub_memcpy_u16(pkt,
rndbuf + s->session->ext.ticklen + 4,
s->psksession_id_len)
|| !WPACKET_put_bytes_u32(pkt, 0)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
OPENSSL_free(rndbuf);
return EXT_RETURN_FAIL;
}
}
if (!WPACKET_close(pkt)
|| !WPACKET_get_total_written(pkt, &binderoffset)
|| !WPACKET_start_sub_packet_u16(pkt)
|| (dores == 1
&& !WPACKET_sub_memcpy_u8(pkt,
rndbuf + s->session->ext.ticklen
+ 4 + s->psksession_id_len,
reshashsize))
|| (s->psksession != NULL
&& !WPACKET_sub_memcpy_u8(pkt,
rndbuf + s->session->ext.ticklen
+ 4 + s->psksession_id_len
+ reshashsize,
pskhashsize))
|| !WPACKET_close(pkt)
|| !WPACKET_close(pkt)
|| !WPACKET_get_total_written(pkt, &msglen)
/*
* We need to fill in all the sub-packet lengths now so we can
* calculate the HMAC of the message up to the binders
*/
|| !WPACKET_fill_lengths(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
OPENSSL_free(rndbuf);
return EXT_RETURN_FAIL;
}
OPENSSL_free(rndbuf);
return EXT_RETURN_SENT;
}
#endif /* OPENSSL_NO_ECH */
if (dores) {
if (!WPACKET_sub_memcpy_u16(pkt, s->session->ext.tick,
s->session->ext.ticklen)
@ -1329,6 +1588,9 @@ EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL_CONNECTION *s, WPACKET *pk
#ifndef OPENSSL_NO_TLS1_3
if (!s->pha_enabled)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(s, pkt)
#endif
/* construct extension - 0 length, no contents */
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_post_handshake_auth)
@ -1449,6 +1711,32 @@ int tls_parse_stoc_server_name(SSL_CONNECTION *s, PACKET *pkt,
unsigned int context,
X509 *x, size_t chainidx)
{
#ifndef OPENSSL_NO_ECH
char *eff_sni = s->ext.hostname;
/* if we tried ECH and failed, the outer is what's expected */
if (s->ext.ech.es != NULL && s->ext.ech.success == 0)
eff_sni = s->ext.ech.outer_hostname;
if (eff_sni == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
if (PACKET_remaining(pkt) > 0) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
return 0;
}
if (!s->hit) {
if (s->session->ext.hostname != NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
s->session->ext.hostname = OPENSSL_strdup(eff_sni);
if (s->session->ext.hostname == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
#else
if (s->ext.hostname == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
@ -1470,7 +1758,7 @@ int tls_parse_stoc_server_name(SSL_CONNECTION *s, PACKET *pkt,
return 0;
}
}
#endif
return 1;
}
@ -2219,6 +2507,9 @@ EXT_RETURN tls_construct_ctos_client_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
if (sc->client_cert_type == NULL)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(sc, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_client_cert_type)
|| !WPACKET_start_sub_packet_u16(pkt)
@ -2271,6 +2562,9 @@ EXT_RETURN tls_construct_ctos_server_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
sc->ext.server_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
if (sc->server_cert_type == NULL)
return EXT_RETURN_NOT_SENT;
#ifndef OPENSSL_NO_ECH
ECH_SAME_EXT(sc, pkt)
#endif
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_server_cert_type)
|| !WPACKET_start_sub_packet_u16(pkt)
@ -2315,3 +2609,97 @@ int tls_parse_stoc_server_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
sc->ext.server_cert_type = type;
return 1;
}
#ifndef OPENSSL_NO_ECH
EXT_RETURN tls_construct_ctos_ech(SSL_CONNECTION *s, WPACKET *pkt,
unsigned int context, X509 *x,
size_t chainidx)
{
if (s->ext.ech.attempted_type != TLSEXT_TYPE_ech
&& s->ext.ech.grease != OSSL_ECH_IS_GREASE
&& !(s->options & SSL_OP_ECH_GREASE))
return EXT_RETURN_NOT_SENT;
/* send grease if not really attempting ECH */
if (s->ext.ech.attempted == 0
&& (s->ext.ech.grease == OSSL_ECH_IS_GREASE
|| (s->options & SSL_OP_ECH_GREASE))) {
if (s->hello_retry_request == SSL_HRR_PENDING
&& s->ext.ech.sent != NULL) {
/* re-tx already sent GREASEy ECH */
if (WPACKET_memcpy(pkt, s->ext.ech.sent,
s->ext.ech.sent_len)
!= 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
return EXT_RETURN_SENT;
}
/* if nobody set a type, use the default */
if (s->ext.ech.attempted_type == OSSL_ECH_type_unknown)
s->ext.ech.attempted_type = TLSEXT_TYPE_ech;
if (ossl_ech_send_grease(s, pkt) != 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_NOT_SENT;
}
return EXT_RETURN_SENT;
}
/*
* If not GREASEing we fake sending the outer value - after the
* entire thing has been constructed we only then finally encode
* and encrypt - need to do it that way as we need the rest of
* the outer CH as AAD input to the encryption.
*/
if (s->ext.ech.ch_depth == 0)
return EXT_RETURN_NOT_SENT;
/* For the inner CH - we simply include one of these saying "inner" */
if (s->ext.ech.ch_depth == 1) {
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ech)
|| !WPACKET_start_sub_packet_u16(pkt)
|| !WPACKET_put_bytes_u8(pkt, OSSL_ECH_INNER_CH_TYPE)
|| !WPACKET_close(pkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
return EXT_RETURN_SENT;
}
return EXT_RETURN_FAIL;
}
/* if the server thinks we GREASE'd then we may get an ECHConfigList */
int tls_parse_stoc_ech(SSL_CONNECTION *s, PACKET *pkt, unsigned int context,
X509 *x, size_t chainidx)
{
unsigned int rlen = 0;
const unsigned char *rval = NULL;
unsigned char *srval = NULL;
/*
* An HRR will have an ECH extension with the
* 8-octet confirmation value, already handled
*/
if (context == SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST)
return 1;
/* otherwise we expect retry-configs */
if (!PACKET_get_net_2(pkt, &rlen)) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
return 0;
}
if (!PACKET_get_bytes(pkt, &rval, rlen)) {
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_LENGTH_MISMATCH);
return 0;
}
OPENSSL_free(s->ext.ech.returned);
s->ext.ech.returned = NULL;
srval = OPENSSL_malloc(rlen + 2);
if (srval == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
srval[0] = (rlen >> 8) & 0xff;
srval[1] = rlen & 0xff;
memcpy(srval + 2, rval, rlen);
s->ext.ech.returned = srval;
s->ext.ech.returned_len = rlen + 2;
return 1;
}
#endif /* END_OPENSSL_NO_ECH */

View file

@ -37,8 +37,6 @@ static MSG_PROCESS_RETURN tls_process_encrypted_extensions(SSL_CONNECTION *s,
static ossl_inline int cert_req_allowed(SSL_CONNECTION *s);
static int key_exchange_expected(SSL_CONNECTION *s);
static int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk,
WPACKET *pkt);
static ossl_inline int received_server_cert(SSL_CONNECTION *sc)
{
@ -1171,7 +1169,253 @@ WORK_STATE ossl_statem_client_post_process_message(SSL_CONNECTION *s,
}
}
CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s, WPACKET *pkt)
#ifndef OPENSSL_NO_ECH
/*
* Wrap the existing ClientHello construction with ECH code.
*
* As needed, we'll call the existing CH constructor twice,
* first for inner, and then for outer.
*
* So the old tls_construct_client_hello is renamed to the _aux
* variant, and the new tls_construct_client_hello just calls
* that if there's no ECH involved, but otherwise does ECH
* things around calls to the _aux variant.
*
* Our basic model is that, when really attempting ECH we
* indicate via the ch_depth field whether we're dealing
* with inner or outer CH (1 for inner, 0 for outer).
*
* After creating the fields for the inner CH, we encode
* those (so we can re-use existing code) then decode again
* (using the existing tls_process_client_hello previously
* only used on servers), again to maximise code re-use.
*
* We next re-encode inner but this time including the
* optimisations for inner CH "compression" (outer exts etc.)
* to produce our plaintext for encrypting.
*
* We then process the outer CH in more or less the
* usual manner.
*
* We lastly form up the AAD etc and encrypt to give us
* the ciphertext for inclusion in the value of the outer
* CH ECH extension.
*
* It may seem odd to form up the outer CH before
* encrypting, but we need to do it that way so we get
* the octets for the AAD used in encryption.
*
* Phew!
*/
static int tls_construct_client_hello_aux(SSL_CONNECTION *s, WPACKET *pkt);
__owur CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s,
WPACKET *pkt)
{
unsigned char *innerch_full = NULL, *innerch_end = NULL;
WPACKET inner; /* "fake" pkt for inner */
BUF_MEM *inner_mem = NULL;
PACKET rpkt; /* we'll decode back the inner ch to help make the outer */
SSL_SESSION *sess = NULL;
size_t sess_id_len = 0, innerlen = 0;
int mt = SSL3_MT_CLIENT_HELLO, rv = 0;
OSSL_HPKE_SUITE suite;
OSSL_ECHSTORE_ENTRY *ee = NULL;
/* Work out what SSL/TLS/DTLS version to use */
int protverr = ssl_set_client_hello_version(s);
if (protverr != 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, protverr);
return 0;
}
/* If we're not really attempting ECH, just call existing code. */
if (s->ext.ech.es == NULL)
return tls_construct_client_hello_aux(s, pkt);
/* note version we're attempting and that an attempt is being made */
if (s->ext.ech.es->entries != NULL) {
if (ossl_ech_pick_matching_cfg(s, &ee, &suite) != 1 || ee == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, protverr);
return 0;
}
if (ee->version != OSSL_ECH_RFCXXXX_VERSION) {
/* we only support that version for now */
SSLfatal(s, SSL_AD_INTERNAL_ERROR, protverr);
return 0;
}
s->ext.ech.attempted_type = TLSEXT_TYPE_ech;
s->ext.ech.attempted_cid = ee->config_id;
s->ext.ech.attempted = 1;
if (s->ext.ech.outer_hostname == NULL && ee->public_name != NULL) {
s->ext.ech.outer_hostname = OPENSSL_strdup((char *)ee->public_name);
if (s->ext.ech.outer_hostname == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
}
/* If doing real ECH and application requested GREASE too, over-ride that */
if (s->ext.ech.grease == OSSL_ECH_IS_GREASE && s->ext.ech.attempted == 1)
s->ext.ech.grease = OSSL_ECH_NOT_GREASE;
/*
* Session ID is handled "oddly" by not being encoded into inner CH (an
* optimisation) so is the same for both inner and outer.
*/
sess = s->session;
if (sess == NULL
|| !ssl_version_supported(s, sess->ssl_version, NULL)
|| !SSL_SESSION_is_resumable(sess)) {
if (s->hello_retry_request == SSL_HRR_NONE
&& !ssl_get_new_session(s, 0))
return 0; /* SSLfatal() already called */
}
if (s->new_session || s->session->ssl_version == TLS1_3_VERSION) {
if (s->version == TLS1_3_VERSION
&& (s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0) {
sess_id_len = sizeof(s->tmp_session_id);
s->tmp_session_id_len = sess_id_len;
if (s->hello_retry_request == SSL_HRR_NONE
&& RAND_bytes_ex(s->ssl.ctx->libctx, s->tmp_session_id,
sess_id_len, RAND_DRBG_STRENGTH)
<= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
memcpy(s->session->session_id, s->tmp_session_id, sess_id_len);
s->session->session_id_length = sess_id_len;
} else {
sess_id_len = 0;
}
} else {
assert(s->session->session_id_length <= sizeof(s->session->session_id));
sess_id_len = s->session->session_id_length;
if (s->version == TLS1_3_VERSION) {
s->tmp_session_id_len = sess_id_len;
memcpy(s->tmp_session_id, s->session->session_id, sess_id_len);
}
}
if (s->hello_retry_request != SSL_HRR_NONE) {
s->ext.ech.n_outer_only = 0; /* reset count of "compressed" exts */
OPENSSL_free(s->ext.ech.encoded_innerch);
s->ext.ech.encoded_innerch = NULL;
s->ext.ech.encoded_innerch_len = 0;
if (s->ext.ech.innerch != NULL) {
OPENSSL_free(s->ext.ech.innerch1);
s->ext.ech.innerch1 = s->ext.ech.innerch;
s->ext.ech.innerch1_len = s->ext.ech.innerch_len;
s->ext.ech.innerch_len = 0;
s->ext.ech.innerch = NULL;
}
}
/*
* Set CH depth flag so that other code (e.g. extension handlers)
* know where we're at: 1 is "inner CH", 0 is "outer CH"
*/
s->ext.ech.ch_depth = 1;
if ((inner_mem = BUF_MEM_new()) == NULL
|| !WPACKET_init(&inner, inner_mem)
|| !ssl_set_handshake_header(s, &inner, mt)
|| tls_construct_client_hello_aux(s, &inner) != 1
|| !WPACKET_close(&inner)
|| !WPACKET_get_length(&inner, &innerlen)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, protverr);
goto err;
}
innerch_full = OPENSSL_malloc(innerlen);
if (innerch_full == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
innerch_end = WPACKET_get_curr(&inner);
memcpy(innerch_full, innerch_end - innerlen, innerlen);
OPENSSL_free(s->ext.ech.innerch);
s->ext.ech.innerch = innerch_full;
s->ext.ech.innerch_len = innerlen;
WPACKET_cleanup(&inner);
BUF_MEM_free(inner_mem);
inner_mem = NULL;
#ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_pbuf("inner CH", s->ext.ech.innerch, s->ext.ech.innerch_len);
ossl_ech_pbuf("inner, client_random", s->ext.ech.client_random,
SSL3_RANDOM_SIZE);
ossl_ech_pbuf("inner, session_id", s->session->session_id,
s->session->session_id_length);
#endif
/* Decode inner so that we can make up encoded inner */
if (!PACKET_buf_init(&rpkt, (unsigned char *)s->ext.ech.innerch + 4,
s->ext.ech.innerch_len - 4)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
/*
* Parse the full inner CH (usually done on server). This gets us
* individually encoded extensions so we can choose to compress
* and/or to re-use the same value in outer.
*/
if (!tls_process_client_hello(s, &rpkt)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
/* Make ClientHelloInner and EncodedClientHelloInner as per spec. */
if (ossl_ech_encode_inner(s) != 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
#ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_pbuf("encoded inner CH", s->ext.ech.encoded_innerch,
s->ext.ech.encoded_innerch_len);
#endif
s->ext.ech.ch_depth = 0; /* set depth for outer CH */
/*
* If we want different key shares for inner and outer, then
* zap the one for the inner. The inner key_share is stashed
* in s.ext.ech.tmp_pkey already.
*/
if (ossl_ech_same_key_share() == 0) {
EVP_PKEY_free(s->s3.tmp.pkey);
s->s3.tmp.pkey = NULL;
}
/* Make second call into CH construction for outer CH. */
rv = tls_construct_client_hello_aux(s, pkt);
if (rv != 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, protverr);
goto err;
}
#ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_pbuf("outer, client_random", s->s3.client_random,
SSL3_RANDOM_SIZE);
ossl_ech_pbuf("outer, session_id", s->session->session_id,
s->session->session_id_length);
#endif
/* Finally, calculate AAD and encrypt using HPKE */
if (ossl_ech_aad_and_encrypt(s, pkt) != 1) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
/* Free up raw exts as needed (happens like this on real server) */
if (s->clienthello != NULL
&& s->clienthello->pre_proc_exts != NULL) {
OPENSSL_free(s->clienthello->pre_proc_exts);
OPENSSL_free(s->clienthello);
s->clienthello = NULL;
}
return 1;
err:
if (inner_mem != NULL) {
WPACKET_cleanup(&inner);
BUF_MEM_free(inner_mem);
}
if (s->clienthello != NULL) {
OPENSSL_free(s->clienthello->pre_proc_exts);
OPENSSL_free(s->clienthello);
s->clienthello = NULL;
}
return 0;
}
static int tls_construct_client_hello_aux(SSL_CONNECTION *s, WPACKET *pkt)
#else
__owur CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s, WPACKET *pkt)
#endif
{
unsigned char *p;
size_t sess_id_len;
@ -1190,18 +1434,30 @@ CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s, WPACKET *pkt)
return CON_FUNC_ERROR;
}
if (sess == NULL
|| !ssl_version_supported(s, sess->ssl_version, NULL)
|| !SSL_SESSION_is_resumable(sess)) {
if (s->hello_retry_request == SSL_HRR_NONE
&& !ssl_get_new_session(s, 0)) {
/* SSLfatal() already called */
return CON_FUNC_ERROR;
#ifndef OPENSSL_NO_ECH
/* if we're doing ECH, re-use session ID setup earlier */
if (s->ext.ech.es == NULL)
#endif
if (sess == NULL
|| !ssl_version_supported(s, sess->ssl_version, NULL)
|| !SSL_SESSION_is_resumable(sess)) {
if (s->hello_retry_request == SSL_HRR_NONE
&& !ssl_get_new_session(s, 0)) {
/* SSLfatal() already called */
return CON_FUNC_ERROR;
}
}
}
/* else use the pre-loaded session */
#ifndef OPENSSL_NO_ECH
/* use different client_random fields for inner and outer */
if (s->ext.ech.es != NULL && s->ext.ech.ch_depth == 1)
p = s->ext.ech.client_random;
else
p = s->s3.client_random;
#else
p = s->s3.client_random;
#endif
/*
* for DTLS if client_random is initialized, reuse it, we are
@ -1258,38 +1514,56 @@ CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s, WPACKET *pkt)
* For TLS 1.3 we always set the ClientHello version to 1.2 and rely on the
* supported_versions extension for the real supported versions.
*/
#ifndef OPENSSL_NO_ECH
if (!WPACKET_put_bytes_u16(pkt, s->client_version)
|| !WPACKET_memcpy(pkt, p, SSL3_RANDOM_SIZE)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return CON_FUNC_ERROR;
}
#else
if (!WPACKET_put_bytes_u16(pkt, s->client_version)
|| !WPACKET_memcpy(pkt, s->s3.client_random, SSL3_RANDOM_SIZE)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return CON_FUNC_ERROR;
}
#endif
/* Session ID */
session_id = s->session->session_id;
if (s->new_session || s->session->ssl_version == TLS1_3_VERSION) {
if (s->version == TLS1_3_VERSION
&& (s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0) {
sess_id_len = sizeof(s->tmp_session_id);
s->tmp_session_id_len = sess_id_len;
session_id = s->tmp_session_id;
if (s->hello_retry_request == SSL_HRR_NONE
&& RAND_bytes_ex(sctx->libctx, s->tmp_session_id,
sess_id_len, 0)
<= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return CON_FUNC_ERROR;
#ifndef OPENSSL_NO_ECH
/* same session ID is used for inner/outer when doing ECH */
if (s->ext.ech.es != NULL) {
sess_id_len = sizeof(s->tmp_session_id);
} else {
#endif
if (s->new_session || s->session->ssl_version == TLS1_3_VERSION) {
if (s->version == TLS1_3_VERSION
&& (s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0) {
sess_id_len = sizeof(s->tmp_session_id);
s->tmp_session_id_len = sess_id_len;
session_id = s->tmp_session_id;
if (s->hello_retry_request == SSL_HRR_NONE
&& RAND_bytes_ex(sctx->libctx, s->tmp_session_id,
sess_id_len, 0)
<= 0) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return CON_FUNC_ERROR;
}
} else {
sess_id_len = 0;
}
} else {
sess_id_len = 0;
}
} else {
assert(s->session->session_id_length <= sizeof(s->session->session_id));
sess_id_len = s->session->session_id_length;
if (s->version == TLS1_3_VERSION) {
s->tmp_session_id_len = sess_id_len;
memcpy(s->tmp_session_id, s->session->session_id, sess_id_len);
assert(s->session->session_id_length <= sizeof(s->session->session_id));
sess_id_len = s->session->session_id_length;
if (s->version == TLS1_3_VERSION) {
s->tmp_session_id_len = sess_id_len;
memcpy(s->tmp_session_id, s->session->session_id, sess_id_len);
}
}
#ifndef OPENSSL_NO_ECH
}
#endif
if (!WPACKET_start_sub_packet_u8(pkt)
|| (sess_id_len != 0 && !WPACKET_memcpy(pkt, session_id, sess_id_len))
|| !WPACKET_close(pkt)) {
@ -1477,6 +1751,24 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL_CONNECTION *s, PACKET *pkt)
#ifndef OPENSSL_NO_COMP
SSL_COMP *comp;
#endif
#ifndef OPENSSL_NO_ECH
const unsigned char *shbuf = NULL;
size_t shlen, chend, fixedshbuf_len, alen;
/*
* client and server accept signal buffers, initialise in case of
* e.g. memory fail when calculating, only really applies when
* SUPERVERBOSE is defined and we trace these.
*/
unsigned char c_signal[OSSL_ECH_SIGNAL_LEN] = { 0 };
unsigned char s_signal[OSSL_ECH_SIGNAL_LEN] = { 0xff };
unsigned char *abuf = NULL;
shlen = PACKET_remaining(pkt);
if (PACKET_peek_bytes(pkt, &shbuf, shlen) != 1) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
goto err;
}
#endif
if (!PACKET_get_net_2(pkt, &sversion)) {
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
@ -1532,6 +1824,74 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL_CONNECTION *s, PACKET *pkt)
goto err;
}
#ifndef OPENSSL_NO_ECH
/*
* If we sent an ECH then check if that worked based on the
* ServerHello.random confirmation trick. If that is good
* then we'll swap over the inner and outer contexts and
* proceed with inner. There are some HRR wrinkles too
* though.
*/
if (s->ext.ech.es != NULL
&& s->ext.ech.done != 1 && s->ext.ech.ch_depth == 0
&& s->ext.ech.grease == OSSL_ECH_NOT_GREASE
&& s->ext.ech.attempted_type == TLSEXT_TYPE_ech) {
/* try set this earlier see what happens */
if (!set_client_ciphersuite(s, cipherchars)) {
/* SSLfatal() already called */
goto err;
}
/* check the ECH accept signal */
if (ossl_ech_calc_confirm(s, hrr, c_signal, shbuf, shlen) != 1
|| ossl_ech_find_confirm(s, hrr, s_signal, shbuf, shlen) != 1
|| memcmp(s_signal, c_signal, sizeof(c_signal)) != 0) {
OSSL_TRACE(TLS, "ECH accept check failed\n");
#ifdef OSSL_ECH_SUPERVERBOSE
ossl_ech_pbuf("ECH client accept val:", c_signal, sizeof(c_signal));
ossl_ech_pbuf("ECH server accept val:", s_signal, sizeof(s_signal));
#endif
s->ext.ech.success = 0;
} else { /* match, ECH worked */
OSSL_TRACE_BEGIN(TLS)
{
BIO_printf(trc_out, "ECH accept check ok\n");
BIO_printf(trc_out, "ECH set session hostname to %s\n",
s->ext.hostname ? s->ext.hostname : "NULL");
}
OSSL_TRACE_END(TLS);
s->ext.ech.success = 1;
}
if (!hrr && s->ext.ech.success == 1) {
if (ossl_ech_swaperoo(s) != 1
|| ossl_ech_make_transcript_buffer(s, hrr, shbuf, shlen,
&abuf, &alen,
&chend, &fixedshbuf_len)
!= 1
|| ossl_ech_reset_hs_buffer(s, abuf, alen) != 1) {
OPENSSL_free(abuf);
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
OPENSSL_free(abuf);
} else if (!hrr) {
/*
* If we got retry_configs then we should be validating
* the outer CH, so we better set the hostname for the
* connection accordingly.
*/
s->ext.ech.former_inner = s->ext.hostname;
s->ext.hostname = NULL;
if (s->ext.ech.outer_hostname != NULL) {
s->ext.hostname = OPENSSL_strdup(s->ext.ech.outer_hostname);
if (s->ext.hostname == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
}
}
}
#endif
/* TLS extensions */
if (PACKET_remaining(pkt) == 0 && !hrr) {
PACKET_null_init(&extpkt);
@ -3033,6 +3393,18 @@ int tls_process_initial_server_flight(SSL_CONNECTION *s)
}
#endif
#ifndef OPENSSL_NO_ECH
/* check result of ech and return error if needed */
if (!s->server
&& s->ext.ech.es != NULL
&& s->ext.ech.attempted == 1
&& s->ext.ech.success != 1
&& s->ext.ech.grease != OSSL_ECH_IS_GREASE) {
SSLfatal(s, SSL_AD_ECH_REQUIRED, SSL_R_ECH_REQUIRED);
return 0;
}
#endif /* OPENSSL_NO_ECH */
return 1;
}
@ -4130,109 +4502,6 @@ int ssl_do_client_cert_cb(SSL_CONNECTION *s, X509 **px509, EVP_PKEY **ppkey)
return i;
}
int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk,
WPACKET *pkt)
{
int i;
size_t totlen = 0, len, maxlen, maxverok = 0;
int empty_reneg_info_scsv = !s->renegotiate
&& !SSL_CONNECTION_IS_DTLS(s)
&& ssl_security(s, SSL_SECOP_VERSION, 0, TLS1_VERSION, NULL)
&& s->min_proto_version <= TLS1_VERSION;
SSL *ssl = SSL_CONNECTION_GET_SSL(s);
/* Set disabled masks for this session */
if (!ssl_set_client_disabled(s)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_PROTOCOLS_AVAILABLE);
return 0;
}
if (sk == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
#ifdef OPENSSL_MAX_TLS1_2_CIPHER_LENGTH
#if OPENSSL_MAX_TLS1_2_CIPHER_LENGTH < 6
#error Max cipher length too short
#endif
/*
* Some servers hang if client hello > 256 bytes as hack workaround
* chop number of supported ciphers to keep it well below this if we
* use TLS v1.2
*/
if (TLS1_get_version(ssl) >= TLS1_2_VERSION)
maxlen = OPENSSL_MAX_TLS1_2_CIPHER_LENGTH & ~1;
else
#endif
/* Maximum length that can be stored in 2 bytes. Length must be even */
maxlen = 0xfffe;
if (empty_reneg_info_scsv)
maxlen -= 2;
if (s->mode & SSL_MODE_SEND_FALLBACK_SCSV)
maxlen -= 2;
for (i = 0; i < sk_SSL_CIPHER_num(sk) && totlen < maxlen; i++) {
const SSL_CIPHER *c;
c = sk_SSL_CIPHER_value(sk, i);
/* Skip disabled ciphers */
if (ssl_cipher_disabled(s, c, SSL_SECOP_CIPHER_SUPPORTED, 0))
continue;
if (!ssl->method->put_cipher_by_char(c, pkt, &len)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
/* Sanity check that the maximum version we offer has ciphers enabled */
if (!maxverok) {
int minproto = SSL_CONNECTION_IS_DTLS(s) ? c->min_dtls : c->min_tls;
int maxproto = SSL_CONNECTION_IS_DTLS(s) ? c->max_dtls : c->max_tls;
if (ssl_version_cmp(s, maxproto, s->s3.tmp.max_ver) >= 0
&& ssl_version_cmp(s, minproto, s->s3.tmp.max_ver) <= 0)
maxverok = 1;
}
totlen += len;
}
if (totlen == 0 || !maxverok) {
const char *maxvertext = !maxverok
? "No ciphers enabled for max supported SSL/TLS version"
: NULL;
SSLfatal_data(s, SSL_AD_INTERNAL_ERROR, SSL_R_NO_CIPHERS_AVAILABLE,
maxvertext);
return 0;
}
if (totlen != 0) {
if (empty_reneg_info_scsv) {
static const SSL_CIPHER scsv = {
0, NULL, NULL, SSL3_CK_SCSV, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
if (!ssl->method->put_cipher_by_char(&scsv, pkt, &len)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
if (s->mode & SSL_MODE_SEND_FALLBACK_SCSV) {
static const SSL_CIPHER scsv = {
0, NULL, NULL, SSL3_CK_FALLBACK_SCSV, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
if (!ssl->method->put_cipher_by_char(&scsv, pkt, &len)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
}
}
return 1;
}
CON_FUNC_RETURN tls_construct_end_of_early_data(SSL_CONNECTION *s, WPACKET *pkt)
{
if (s->early_data_state != SSL_EARLY_DATA_WRITE_RETRY

View file

@ -570,3 +570,13 @@ int tls_parse_ctos_server_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
int tls_parse_stoc_server_cert_type(SSL_CONNECTION *s, PACKET *pkt,
unsigned int context,
X509 *x, size_t chainidx);
#ifndef OPENSSL_NO_ECH
EXT_RETURN tls_construct_ctos_ech(SSL_CONNECTION *s, WPACKET *pkt,
unsigned int context, X509 *x,
size_t chainidx);
EXT_RETURN tls_construct_ctos_ech(SSL_CONNECTION *s, WPACKET *pkt,
unsigned int context, X509 *x,
size_t chainidx);
int tls_parse_stoc_ech(SSL_CONNECTION *s, PACKET *pkt, unsigned int context,
X509 *x, size_t chainidx);
#endif

View file

@ -600,6 +600,10 @@ int tls1_alert_code(int code)
return SSL_AD_HANDSHAKE_FAILURE;
case TLS13_AD_MISSING_EXTENSION:
return SSL_AD_HANDSHAKE_FAILURE;
#ifndef OPENSSL_NO_ECH
case SSL_AD_ECH_REQUIRED:
return TLS1_AD_ECH_REQUIRED;
#endif
default:
return -1;
}

View file

@ -501,6 +501,10 @@ static const ssl_trace_tbl ssl_exts_tbl[] = {
#ifndef OPENSSL_NO_NEXTPROTONEG
{ TLSEXT_TYPE_next_proto_neg, "next_proto_neg" },
#endif
#ifndef OPENSSL_NO_ECH
{ TLSEXT_TYPE_ech, "encrypted_client_hello" },
{ TLSEXT_TYPE_outer_extensions, "outer_extension" },
#endif
};
static const ssl_trace_tbl ssl_groups_tbl[] = {

View file

@ -828,7 +828,7 @@ static int ech_ingest_test(int run)
BIO *in = NULL, *out = NULL;
int i, rv = 0, keysb4, keysaftr, actual_ents = 0, has_priv, for_retry;
ingest_tv_t *tv = &ingest_tvs[run];
time_t now = 0, secs = 0;
time_t secs = 0, add_time = 0, flush_time = 0;
char *pn = NULL, *ec = NULL;
if ((in = BIO_new(BIO_s_mem())) == NULL
@ -843,6 +843,7 @@ static int ech_ingest_test(int run)
TEST_info("Bad test vector entry");
goto end;
}
add_time = time(0);
if (tv->pemenc == 1
&& !TEST_int_eq(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_NO_RETRY),
tv->read))
@ -887,10 +888,10 @@ static int ech_ingest_test(int run)
|| !TEST_true(OSSL_ECHSTORE_write_pem(es, OSSL_ECHSTORE_ALL, out))
|| !TEST_false(OSSL_ECHSTORE_write_pem(es, 100, out)))
goto end;
now = time(0);
if (!TEST_true(OSSL_ECHSTORE_flush_keys(es, now))
|| !TEST_true(OSSL_ECHSTORE_num_keys(es, &keysaftr))
|| !TEST_false(keysaftr))
flush_time = time(0);
if (!TEST_true(OSSL_ECHSTORE_flush_keys(es, flush_time - add_time))
|| !TEST_int_eq(OSSL_ECHSTORE_num_keys(es, &keysaftr), 1)
|| !TEST_int_eq(keysaftr, 0))
goto end;
rv = 1;
end:
@ -1130,6 +1131,7 @@ end:
#define OSSL_ECH_TEST_HRR 1
#define OSSL_ECH_TEST_EARLY 2
#define OSSL_ECH_TEST_CUSTOM 3
#define OSSL_ECH_TEST_ENOE 4 /* early + no-ech */
/*
* @brief ECH roundtrip test helper
@ -1155,22 +1157,19 @@ end:
static int test_ech_roundtrip_helper(int idx, int combo)
{
int res = 0, kemind, kdfind, aeadind, kemsz, kdfsz, aeadsz;
char suitestr[100];
int clientstatus, serverstatus, server = 1, client = 0;
unsigned int context;
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";
char *public_name = "example.com", suitestr[100];
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;
unsigned char ed[21], buf[1024];
/* split idx into kemind, kdfind, aeadind */
kemsz = OSSL_NELEM(kem_str_list);
@ -1194,21 +1193,17 @@ static int test_ech_roundtrip_helper(int idx, int combo)
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 (combo == OSSL_ECH_TEST_EARLY || combo == OSSL_ECH_TEST_ENOE) {
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))
if (!TEST_true(SSL_CTX_set_recv_max_early_data(sctx,
SSL3_RT_MAX_PLAIN_LENGTH)))
goto end;
}
if (combo == OSSL_ECH_TEST_CUSTOM) {
/* add custom CH ext to client and server */
context = SSL_EXT_CLIENT_HELLO;
context = SSL_EXT_CLIENT_HELLO; /* add custom CH ext to client/server */
if (!TEST_true(SSL_CTX_add_custom_ext(cctx, TEST_EXT_TYPE1, context,
new_add_cb, new_free_cb,
&client, new_parse_cb, &client))
@ -1223,18 +1218,40 @@ static int test_ech_roundtrip_helper(int idx, int combo)
&server, NULL, &server)))
goto end;
}
if (!TEST_true(SSL_CTX_set1_echstore(cctx, es))
|| !TEST_true(SSL_CTX_set1_echstore(sctx, es))
if (combo != OSSL_ECH_TEST_ENOE
&& !TEST_true(SSL_CTX_set1_echstore(cctx, es)))
goto end;
if (!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,
if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example")))
goto end;
#undef DROPFORNOW
#ifdef DROPFORNOW
/* TODO(ECH): we'll re-instate this once server-side ECH code is in */
if (!TEST_true(create_ssl_connection(serverssl, clientssl,
SSL_ERROR_NONE)))
goto end;
#else
/*
* For this PR, check connections fail when client does ECH
* and server doesn't, but work if client doesn't do ECH.
* Added in early data for the no-ECH case because an
* intermediate state of the code had an issue.
*/
if (combo != OSSL_ECH_TEST_ENOE
&& !TEST_false(create_ssl_connection(serverssl, clientssl,
SSL_ERROR_NONE)))
goto end;
if (combo == OSSL_ECH_TEST_ENOE
&& !TEST_true(create_ssl_connection(serverssl, clientssl,
SSL_ERROR_NONE)))
goto end;
#endif
serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter);
if (verbose)
TEST_info("server status %d, %s, %s", serverstatus, sinner, souter);
@ -1255,8 +1272,16 @@ static int test_ech_roundtrip_helper(int idx, int combo)
goto end;
}
/* continue for EARLY test */
if (combo != OSSL_ECH_TEST_EARLY)
#ifdef DROPFORNOW
/* TODO(ECH): turn back on later */
if (combo != OSSL_ECH_TEST_EARLY && combo != OSSL_ECH_TEST_ENOE)
goto end;
#else
if (combo != OSSL_ECH_TEST_ENOE) {
res = 1;
goto end;
}
#endif
/* shutdown for start over */
sess = SSL_get1_session(clientssl);
OPENSSL_free(sinner);
@ -1350,6 +1375,14 @@ static int ech_custom_test(int idx)
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_CUSTOM);
}
/* Test a roundtrip with No ECH, and early data */
static int ech_enoe_test(int idx)
{
if (verbose)
TEST_info("Doing: ech_no ech + early test ");
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_ENOE);
}
#endif
int setup_tests(void)
@ -1392,6 +1425,7 @@ int setup_tests(void)
ADD_ALL_TESTS(test_ech_hrr, suite_combos);
ADD_ALL_TESTS(test_ech_early, suite_combos);
ADD_ALL_TESTS(ech_custom_test, suite_combos);
ADD_ALL_TESTS(ech_enoe_test, suite_combos);
/* TODO(ECH): add more test code as other PRs done */
return 1;
err:

View file

@ -72,6 +72,13 @@ static EXT_LIST ext_list[] = {
EXT_ENTRY(compress_certificate),
EXT_ENTRY(early_data),
EXT_ENTRY(certificate_authorities),
#ifndef OPENSSL_NO_ECH
EXT_ENTRY(ech),
EXT_ENTRY(outer_extensions),
#else
EXT_EXCEPTION(ech),
EXT_EXCEPTION(outer_extensions),
#endif
EXT_ENTRY(padding),
EXT_ENTRY(psk),
EXT_END(num_builtins)

View file

@ -29,8 +29,6 @@ Header:
ffdhe2048 (256)
ffdhe3072 (257)
extension_type=session_ticket(35), length=0
extension_type=application_layer_protocol_negotiation(16), length=11
ossltest
extension_type=encrypt_then_mac(22), length=0
extension_type=extended_master_secret(23), length=0
extension_type=signature_algorithms(13), length=?
@ -65,6 +63,8 @@ Header:
key_exchange: (len=32): ?
extension_type=compress_certificate(27), length=3
zlib (1)
extension_type=application_layer_protocol_negotiation(16), length=11
ossltest
Sent Frame: Crypto
Offset: 0

View file

@ -29,8 +29,6 @@ Header:
ffdhe2048 (256)
ffdhe3072 (257)
extension_type=session_ticket(35), length=0
extension_type=application_layer_protocol_negotiation(16), length=11
ossltest
extension_type=encrypt_then_mac(22), length=0
extension_type=extended_master_secret(23), length=0
extension_type=signature_algorithms(13), length=?
@ -63,6 +61,8 @@ Header:
key_exchange: (len=1216): ?
NamedGroup: ecdh_x25519 (29)
key_exchange: (len=32): ?
extension_type=application_layer_protocol_negotiation(16), length=11
ossltest
Sent Frame: Crypto
Offset: 0