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:
parent
7debe0ddef
commit
ade10c5dbf
21 changed files with 3036 additions and 199 deletions
|
|
@ -131,6 +131,7 @@ ignore-words-list =
|
|||
Gost,
|
||||
GOST,
|
||||
Hart,
|
||||
hashin,
|
||||
hasTable,
|
||||
hel,
|
||||
hist,
|
||||
|
|
|
|||
139
apps/s_client.c
139
apps/s_client.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
27
include/internal/ech_helpers.h
Normal file
27
include/internal/ech_helpers.h
Normal 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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
103
ssl/ssl_ciph.c
103
ssl/ssl_ciph.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue