mirror of https://github.com/openssl/openssl.git
Add server-side handling of Encrypted Client Hello
Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/27561)
This commit is contained in:
parent
ecd40163f2
commit
497daff2b9
|
|
@ -104,6 +104,8 @@ resumption and the historical servername callback.
|
||||||
The SSL_client_hello_* family of functions may only be called from code executing
|
The SSL_client_hello_* family of functions may only be called from code executing
|
||||||
within a ClientHello callback.
|
within a ClientHello callback.
|
||||||
|
|
||||||
|
TODO(ECH): How ECH is handled here needs to be documented.
|
||||||
|
|
||||||
=head1 RETURN VALUES
|
=head1 RETURN VALUES
|
||||||
|
|
||||||
The application's supplied ClientHello callback returns
|
The application's supplied ClientHello callback returns
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,42 @@
|
||||||
|
|
||||||
# ifndef OPENSSL_NO_ECH
|
# ifndef OPENSSL_NO_ECH
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the max HPKE 'info' we'll process is the max ECHConfig size
|
||||||
|
* (OSSL_ECH_MAX_ECHCONFIG_LEN) plus OSSL_ECH_CONTEXT_STRING(len=7) + 1
|
||||||
|
*/
|
||||||
|
# define OSSL_ECH_MAX_INFO_LEN (OSSL_ECH_MAX_ECHCONFIG_LEN + 8)
|
||||||
|
|
||||||
int ossl_ech_make_enc_info(const unsigned char *encoding,
|
int ossl_ech_make_enc_info(const unsigned char *encoding,
|
||||||
size_t encoding_length,
|
size_t encoding_length,
|
||||||
unsigned char *info, size_t *info_len);
|
unsigned char *info, size_t *info_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a CH find the offsets of the session id, extensions and ECH
|
||||||
|
* ch is the encoded client hello
|
||||||
|
* ch_len is the length of ch
|
||||||
|
* sessid_off returns offset of session_id length
|
||||||
|
* exts_off points to offset of extensions
|
||||||
|
* exts_len returns length of extensions
|
||||||
|
* ech_off returns offset of ECH
|
||||||
|
* echtype returns the ext type of the ECH
|
||||||
|
* ech_len returns the length of the ECH
|
||||||
|
* sni_off returns offset of (outer) SNI
|
||||||
|
* sni_len returns the length of the SNI
|
||||||
|
* inner 1 if the ECH is marked as an inner, 0 for outer
|
||||||
|
* return 1 for success, other otherwise
|
||||||
|
*
|
||||||
|
* Offsets are set to zero if relevant thing not found.
|
||||||
|
* Offsets are returned to the type or length field in question.
|
||||||
|
*
|
||||||
|
* Note: input here is untrusted!
|
||||||
|
*/
|
||||||
|
int ossl_ech_helper_get_ch_offsets(const unsigned char *ch, size_t ch_len,
|
||||||
|
size_t *sessid_off, size_t *exts_off,
|
||||||
|
size_t *exts_len,
|
||||||
|
size_t *ech_off, uint16_t *echtype,
|
||||||
|
size_t *ech_len, size_t *sni_off,
|
||||||
|
size_t *sni_len, int *inner);
|
||||||
|
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@
|
||||||
#include "ech_local.h"
|
#include "ech_local.h"
|
||||||
#include "internal/ech_helpers.h"
|
#include "internal/ech_helpers.h"
|
||||||
|
|
||||||
/* TODO(ECH): move more code that's used by internals and test here */
|
|
||||||
|
|
||||||
/* used in ECH crypto derivations (odd format for EBCDIC goodness) */
|
/* used in ECH crypto derivations (odd format for EBCDIC goodness) */
|
||||||
/* "tls ech" */
|
/* "tls ech" */
|
||||||
static const char OSSL_ECH_CONTEXT_STRING[] = "\x74\x6c\x73\x20\x65\x63\x68";
|
static const char OSSL_ECH_CONTEXT_STRING[] = "\x74\x6c\x73\x20\x65\x63\x68";
|
||||||
|
|
@ -52,3 +50,101 @@ int ossl_ech_make_enc_info(const unsigned char *encoding,
|
||||||
WPACKET_cleanup(&ipkt);
|
WPACKET_cleanup(&ipkt);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a CH find the offsets of the session id, extensions and ECH
|
||||||
|
* ch is the encoded client hello
|
||||||
|
* ch_len is the length of ch
|
||||||
|
* sessid_off returns offset of session_id length
|
||||||
|
* exts_off points to offset of extensions
|
||||||
|
* exts_len returns length of extensions
|
||||||
|
* ech_off returns offset of ECH
|
||||||
|
* echtype returns the ext type of the ECH
|
||||||
|
* ech_len returns the length of the ECH
|
||||||
|
* sni_off returns offset of (outer) SNI
|
||||||
|
* sni_len returns the length of the SNI
|
||||||
|
* inner 1 if the ECH is marked as an inner, 0 for outer
|
||||||
|
* return 1 for success, other otherwise
|
||||||
|
*
|
||||||
|
* Offsets are set to zero if relevant thing not found.
|
||||||
|
* Offsets are returned to the type or length field in question.
|
||||||
|
*
|
||||||
|
* Note: input here is untrusted!
|
||||||
|
*/
|
||||||
|
int ossl_ech_helper_get_ch_offsets(const unsigned char *ch, size_t ch_len,
|
||||||
|
size_t *sessid_off, size_t *exts_off,
|
||||||
|
size_t *exts_len,
|
||||||
|
size_t *ech_off, uint16_t *echtype,
|
||||||
|
size_t *ech_len, size_t *sni_off,
|
||||||
|
size_t *sni_len, int *inner)
|
||||||
|
{
|
||||||
|
unsigned int elen = 0, etype = 0, pi_tmp = 0;
|
||||||
|
const unsigned char *pp_tmp = NULL, *chstart = NULL, *estart = NULL;
|
||||||
|
PACKET pkt;
|
||||||
|
int done = 0;
|
||||||
|
|
||||||
|
if (ch == NULL || ch_len == 0 || sessid_off == NULL || exts_off == NULL
|
||||||
|
|| ech_off == NULL || echtype == NULL || ech_len == NULL
|
||||||
|
|| sni_off == NULL || inner == NULL)
|
||||||
|
return 0;
|
||||||
|
*sessid_off = *exts_off = *ech_off = *sni_off = *sni_len = *ech_len = 0;
|
||||||
|
*echtype = 0xffff;
|
||||||
|
if (!PACKET_buf_init(&pkt, ch, ch_len))
|
||||||
|
return 0;
|
||||||
|
chstart = PACKET_data(&pkt);
|
||||||
|
if (!PACKET_get_net_2(&pkt, &pi_tmp))
|
||||||
|
return 0;
|
||||||
|
/* 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;
|
||||||
|
/* chew up the packet to extensions */
|
||||||
|
if (!PACKET_get_bytes(&pkt, &pp_tmp, SSL3_RANDOM_SIZE)
|
||||||
|
|| (*sessid_off = PACKET_data(&pkt) - chstart) == 0
|
||||||
|
|| !PACKET_get_1(&pkt, &pi_tmp) /* sessid len */
|
||||||
|
|| !PACKET_get_bytes(&pkt, &pp_tmp, pi_tmp) /* sessid */
|
||||||
|
|| !PACKET_get_net_2(&pkt, &pi_tmp) /* ciphersuite len */
|
||||||
|
|| !PACKET_get_bytes(&pkt, &pp_tmp, pi_tmp) /* suites */
|
||||||
|
|| !PACKET_get_1(&pkt, &pi_tmp) /* compression meths */
|
||||||
|
|| !PACKET_get_bytes(&pkt, &pp_tmp, pi_tmp) /* comp meths */
|
||||||
|
|| (*exts_off = PACKET_data(&pkt) - chstart) == 0
|
||||||
|
|| !PACKET_get_net_2(&pkt, &pi_tmp) /* len(extensions) */
|
||||||
|
|| (*exts_len = (size_t) pi_tmp) == 0)
|
||||||
|
/*
|
||||||
|
* unexpectedly, we return 1 here, as doing otherwise will
|
||||||
|
* break some non-ECH test code that truncates CH messages
|
||||||
|
* The same is true below when looking through extensions.
|
||||||
|
* That's ok though, we'll only set those offsets we've
|
||||||
|
* found.
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
|
/* no extensions is theoretically ok, if uninteresting */
|
||||||
|
if (*exts_len == 0)
|
||||||
|
return 1;
|
||||||
|
/* find what we want from extensions */
|
||||||
|
estart = PACKET_data(&pkt);
|
||||||
|
while (PACKET_remaining(&pkt) > 0
|
||||||
|
&& (size_t)(PACKET_data(&pkt) - estart) < *exts_len
|
||||||
|
&& done < 2) {
|
||||||
|
if (!PACKET_get_net_2(&pkt, &etype)
|
||||||
|
|| !PACKET_get_net_2(&pkt, &elen))
|
||||||
|
return 1; /* see note above */
|
||||||
|
if (etype == TLSEXT_TYPE_ech) {
|
||||||
|
if (elen == 0)
|
||||||
|
return 0;
|
||||||
|
*ech_off = PACKET_data(&pkt) - chstart - 4;
|
||||||
|
*echtype = etype;
|
||||||
|
*ech_len = elen;
|
||||||
|
done++;
|
||||||
|
}
|
||||||
|
if (etype == TLSEXT_TYPE_server_name) {
|
||||||
|
*sni_off = PACKET_data(&pkt) - chstart - 4;
|
||||||
|
*sni_len = elen;
|
||||||
|
done++;
|
||||||
|
}
|
||||||
|
if (!PACKET_get_bytes(&pkt, &pp_tmp, elen))
|
||||||
|
return 1; /* see note above */
|
||||||
|
if (etype == TLSEXT_TYPE_ech)
|
||||||
|
*inner = pp_tmp[0];
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -118,6 +118,33 @@ typedef struct ossl_echstore_entry_st {
|
||||||
unsigned char *encoded; /* overall encoded content */
|
unsigned char *encoded; /* overall encoded content */
|
||||||
} OSSL_ECHSTORE_ENTRY;
|
} OSSL_ECHSTORE_ENTRY;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* What we send in the ech CH extension:
|
||||||
|
* enum { outer(0), inner(1) } ECHClientHelloType;
|
||||||
|
* struct {
|
||||||
|
* ECHClientHelloType type;
|
||||||
|
* select (ECHClientHello.type) {
|
||||||
|
* case outer:
|
||||||
|
* HpkeSymmetricCipherSuite cipher_suite;
|
||||||
|
* uint8 config_id;
|
||||||
|
* opaque enc<0..2^16-1>;
|
||||||
|
* opaque payload<1..2^16-1>;
|
||||||
|
* case inner:
|
||||||
|
* Empty;
|
||||||
|
* };
|
||||||
|
* } ECHClientHello;
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct ech_encch_st {
|
||||||
|
uint16_t kdf_id; /* ciphersuite */
|
||||||
|
uint16_t aead_id; /* ciphersuite */
|
||||||
|
uint8_t config_id; /* (maybe) identifies DNS RR value used */
|
||||||
|
size_t enc_len; /* public share */
|
||||||
|
unsigned char *enc; /* public share for sender */
|
||||||
|
size_t payload_len; /* ciphertext */
|
||||||
|
unsigned char *payload; /* ciphertext */
|
||||||
|
} OSSL_ECH_ENCCH;
|
||||||
|
|
||||||
DEFINE_STACK_OF(OSSL_ECHSTORE_ENTRY)
|
DEFINE_STACK_OF(OSSL_ECHSTORE_ENTRY)
|
||||||
|
|
||||||
struct ossl_echstore_st {
|
struct ossl_echstore_st {
|
||||||
|
|
@ -205,6 +232,22 @@ typedef struct ossl_ech_conn_st {
|
||||||
unsigned char *pub; /* client ephemeral public kept by server in case HRR */
|
unsigned char *pub; /* client ephemeral public kept by server in case HRR */
|
||||||
size_t pub_len;
|
size_t pub_len;
|
||||||
OSSL_HPKE_CTX *hpke_ctx; /* HPKE context, needed for HRR */
|
OSSL_HPKE_CTX *hpke_ctx; /* HPKE context, needed for HRR */
|
||||||
|
/*
|
||||||
|
* Offsets of various things we need to know about in an inbound
|
||||||
|
* ClientHello (CH) plus the type of ECH and whether that CH is an inner or
|
||||||
|
* outer CH. We find these once for the outer CH, by roughly parsing the CH
|
||||||
|
* so store them for later re-use. We need to re-do this parsing when we
|
||||||
|
* get the 2nd CH in the case of HRR, and when we move to processing the
|
||||||
|
* inner CH after successful ECH decyption, so we have a flag to say if
|
||||||
|
* we've done the work or not.
|
||||||
|
*/
|
||||||
|
int ch_offsets_done;
|
||||||
|
size_t sessid_off; /* offset of session_id length */
|
||||||
|
size_t exts_off; /* to offset of extensions */
|
||||||
|
size_t ech_off; /* offset of ECH */
|
||||||
|
size_t sni_off; /* offset of (outer) SNI */
|
||||||
|
int echtype; /* ext type of the ECH */
|
||||||
|
int inner; /* 1 if the ECH is marked as an inner, 0 for outer */
|
||||||
/*
|
/*
|
||||||
* A pointer to, and copy of, the hrrsignal from an HRR message.
|
* A pointer to, and copy of, the hrrsignal from an HRR message.
|
||||||
* We need both, as we zero-out the octets when re-calculating and
|
* We need both, as we zero-out the octets when re-calculating and
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ int SSL_ech_get1_status(SSL *ssl, char **inner_sni, char **outer_sni)
|
||||||
return SSL_ECH_STATUS_GREASE_ECH;
|
return SSL_ECH_STATUS_GREASE_ECH;
|
||||||
return SSL_ECH_STATUS_GREASE;
|
return SSL_ECH_STATUS_GREASE;
|
||||||
}
|
}
|
||||||
if ((s->options & SSL_OP_ECH_GREASE) !=0 && s->ext.ech.attempted != 1)
|
if ((s->options & SSL_OP_ECH_GREASE) != 0 && s->ext.ech.attempted != 1)
|
||||||
return SSL_ECH_STATUS_GREASE;
|
return SSL_ECH_STATUS_GREASE;
|
||||||
if (s->ext.ech.backend == 1) {
|
if (s->ext.ech.backend == 1) {
|
||||||
if (s->ext.hostname != NULL
|
if (s->ext.hostname != NULL
|
||||||
|
|
|
||||||
|
|
@ -498,13 +498,8 @@ static const EXTENSION_DEFINITION ext_defs[] = {
|
||||||
SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
|
SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
|
||||||
OSSL_ECH_HANDLING_CALL_BOTH,
|
OSSL_ECH_HANDLING_CALL_BOTH,
|
||||||
init_ech,
|
init_ech,
|
||||||
/*
|
tls_parse_ctos_ech, tls_parse_stoc_ech,
|
||||||
* TODO(ECH): add server calls as per below in a bit
|
tls_construct_stoc_ech, tls_construct_ctos_ech,
|
||||||
* 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
|
NULL
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,7 @@
|
||||||
#include "statem_local.h"
|
#include "statem_local.h"
|
||||||
#ifndef OPENSSL_NO_ECH
|
#ifndef OPENSSL_NO_ECH
|
||||||
# include <openssl/rand.h>
|
# include <openssl/rand.h>
|
||||||
#include "internal/ech_helpers.h"
|
# include "internal/ech_helpers.h"
|
||||||
/*
|
|
||||||
* the max HPKE 'info' we'll process is the max ECHConfig size
|
|
||||||
* (OSSL_ECH_MAX_ECHCONFIG_LEN) plus OSSL_ECH_CONTEXT_STRING(len=7) + 1
|
|
||||||
*/
|
|
||||||
#define OSSL_ECH_MAX_INFO_LEN (OSSL_ECH_MAX_ECHCONFIG_LEN + 8)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
EXT_RETURN tls_construct_ctos_renegotiate(SSL_CONNECTION *s, WPACKET *pkt,
|
EXT_RETURN tls_construct_ctos_renegotiate(SSL_CONNECTION *s, WPACKET *pkt,
|
||||||
|
|
@ -2524,7 +2519,6 @@ EXT_RETURN tls_construct_ctos_ech(SSL_CONNECTION *s, WPACKET *pkt,
|
||||||
unsigned char *encoded = NULL, *mypub = NULL;
|
unsigned char *encoded = NULL, *mypub = NULL;
|
||||||
size_t cipherlen = 0, aad_len = 0, lenclen = 0, mypub_len = 0;
|
size_t cipherlen = 0, aad_len = 0, lenclen = 0, mypub_len = 0;
|
||||||
size_t info_len = OSSL_ECH_MAX_INFO_LEN, clear_len = 0, encoded_len = 0;
|
size_t info_len = OSSL_ECH_MAX_INFO_LEN, clear_len = 0, encoded_len = 0;
|
||||||
|
|
||||||
/* whether or not we've been asked to GREASE, one way or another */
|
/* whether or not we've been asked to GREASE, one way or another */
|
||||||
int grease_opt_set = (s->ext.ech.grease == OSSL_ECH_IS_GREASE
|
int grease_opt_set = (s->ext.ech.grease == OSSL_ECH_IS_GREASE
|
||||||
|| ((s->options & SSL_OP_ECH_GREASE) != 0));
|
|| ((s->options & SSL_OP_ECH_GREASE) != 0));
|
||||||
|
|
|
||||||
|
|
@ -209,8 +209,8 @@ int custom_ext_add(SSL_CONNECTION *s, int context, WPACKET *pkt, X509 *x,
|
||||||
if (s->ext.ech.n_outer_only >= OSSL_ECH_OUTERS_MAX) {
|
if (s->ext.ech.n_outer_only >= OSSL_ECH_OUTERS_MAX) {
|
||||||
OSSL_TRACE_BEGIN(TLS) {
|
OSSL_TRACE_BEGIN(TLS) {
|
||||||
BIO_printf(trc_out,
|
BIO_printf(trc_out,
|
||||||
"Too many outers to compress (max=%d)\n",
|
"Too many outers to compress (max=%d)\n",
|
||||||
OSSL_ECH_OUTERS_MAX);
|
OSSL_ECH_OUTERS_MAX);
|
||||||
} OSSL_TRACE_END(TLS);
|
} OSSL_TRACE_END(TLS);
|
||||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -219,9 +219,9 @@ int custom_ext_add(SSL_CONNECTION *s, int context, WPACKET *pkt, X509 *x,
|
||||||
s->ext.ech.n_outer_only++;
|
s->ext.ech.n_outer_only++;
|
||||||
OSSL_TRACE_BEGIN(TLS) {
|
OSSL_TRACE_BEGIN(TLS) {
|
||||||
BIO_printf(trc_out, "ECH compressing type "
|
BIO_printf(trc_out, "ECH compressing type "
|
||||||
"0x%04x (tot: %d)\n",
|
"0x%04x (tot: %d)\n",
|
||||||
(int) meth->ext_type,
|
(int) meth->ext_type,
|
||||||
(int) s->ext.ech.n_outer_only);
|
(int) s->ext.ech.n_outer_only);
|
||||||
} OSSL_TRACE_END(TLS);
|
} OSSL_TRACE_END(TLS);
|
||||||
}
|
}
|
||||||
if (s->ext.ech.ch_depth == 0) {
|
if (s->ext.ech.ch_depth == 0) {
|
||||||
|
|
@ -247,8 +247,8 @@ int custom_ext_add(SSL_CONNECTION *s, int context, WPACKET *pkt, X509 *x,
|
||||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (ossl_ech_copy_inner2outer(s, meth->ext_type, tind, pkt)
|
if (ossl_ech_copy_inner2outer(s, meth->ext_type, tind,
|
||||||
!= OSSL_ECH_SAME_EXT_DONE) {
|
pkt) != OSSL_ECH_SAME_EXT_DONE) {
|
||||||
/* for custom exts, we really should have found it */
|
/* for custom exts, we really should have found it */
|
||||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -505,19 +505,7 @@ int ossl_tls_add_custom_ext_intern(SSL_CTX *ctx, custom_ext_methods *exts,
|
||||||
* for extension types that previously were not supported, but now are.
|
* for extension types that previously were not supported, but now are.
|
||||||
*/
|
*/
|
||||||
if (SSL_extension_supported(ext_type)
|
if (SSL_extension_supported(ext_type)
|
||||||
#if !defined(OPENSSL_NO_ECH) && defined(OPENSSL_ECH_ALLOW_CUST_INJECT)
|
|
||||||
/*
|
|
||||||
* Do this conditionally so we can test an ECH in TLSv1.2
|
|
||||||
* via the custom extensions API.
|
|
||||||
* OPENSSL_ECH_ALLOW_CUST_INJECT is defined (or not) in
|
|
||||||
* include/openssl/ech.h and if defined enables a test in
|
|
||||||
* test/ech_test.c
|
|
||||||
*/
|
|
||||||
&& ext_type != TLSEXT_TYPE_ech
|
|
||||||
&& ext_type != TLSEXT_TYPE_signed_certificate_timestamp)
|
&& ext_type != TLSEXT_TYPE_signed_certificate_timestamp)
|
||||||
#else
|
|
||||||
&& ext_type != TLSEXT_TYPE_signed_certificate_timestamp)
|
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Extension type must fit in 16 bits */
|
/* Extension type must fit in 16 bits */
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,12 @@
|
||||||
#include "internal/cryptlib.h"
|
#include "internal/cryptlib.h"
|
||||||
#include "internal/ssl_unwrap.h"
|
#include "internal/ssl_unwrap.h"
|
||||||
|
|
||||||
#define COOKIE_STATE_FORMAT_VERSION 1
|
#ifndef OPENSSL_NO_ECH
|
||||||
|
# include <openssl/rand.h>
|
||||||
|
# include <openssl/trace.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define COOKIE_STATE_FORMAT_VERSION 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 2 bytes for packet length, 2 bytes for format version, 2 bytes for
|
* 2 bytes for packet length, 2 bytes for format version, 2 bytes for
|
||||||
|
|
@ -2420,3 +2425,152 @@ int tls_parse_ctos_server_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
|
||||||
SSLfatal(sc, SSL_AD_UNSUPPORTED_CERTIFICATE, SSL_R_BAD_EXTENSION);
|
SSLfatal(sc, SSL_AD_UNSUPPORTED_CERTIFICATE, SSL_R_BAD_EXTENSION);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_ECH
|
||||||
|
/*
|
||||||
|
* ECH handling for edge cases (GREASE/inner) and errors.
|
||||||
|
* return 1 for good, 0 otherwise
|
||||||
|
*
|
||||||
|
* Real ECH handling (i.e. decryption) happens before, via
|
||||||
|
* ech_early_decrypt(), but if that failed (e.g. decryption
|
||||||
|
* failed, which may be down to GREASE) then we end up here,
|
||||||
|
* processing the ECH from the outer CH.
|
||||||
|
* Otherwise, we only expect to see an inner ECH with a fixed
|
||||||
|
* value here.
|
||||||
|
*/
|
||||||
|
int tls_parse_ctos_ech(SSL_CONNECTION *s, PACKET *pkt, unsigned int context,
|
||||||
|
X509 *x, size_t chainidx)
|
||||||
|
{
|
||||||
|
unsigned int echtype = 0;
|
||||||
|
|
||||||
|
if (s->ext.ech.grease == OSSL_ECH_IS_GREASE) {
|
||||||
|
/* GREASE is fine */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (s->ext.ech.es == NULL) {
|
||||||
|
/* If not configured for ECH then we ignore it */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (s->ext.ech.attempted_type != TLSEXT_TYPE_ech) {
|
||||||
|
/* if/when new versions of ECH are added we'll update here */
|
||||||
|
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* we only allow "inner" which is one octet, valued 0x01
|
||||||
|
* and only if we decrypted ok or are a backend
|
||||||
|
*/
|
||||||
|
if (PACKET_get_1(pkt, &echtype) != 1
|
||||||
|
|| echtype != OSSL_ECH_INNER_CH_TYPE
|
||||||
|
|| PACKET_remaining(pkt) != 0) {
|
||||||
|
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (s->ext.ech.success != 1 && s->ext.ech.backend != 1) {
|
||||||
|
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* yay - we're ok with this */
|
||||||
|
OSSL_TRACE_BEGIN(TLS) {
|
||||||
|
BIO_printf(trc_out, "ECH seen in inner as exptected.\n");
|
||||||
|
} OSSL_TRACE_END(TLS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Answer an ECH, as needed
|
||||||
|
* return 1 for good, 0 otherwise
|
||||||
|
*
|
||||||
|
* Return most-recent ECH config for retry, as needed.
|
||||||
|
* If doing HRR we include the confirmation value, but
|
||||||
|
* for now, we'll just add the zeros - the real octets
|
||||||
|
* will be added later via ech_calc_ech_confirm() which
|
||||||
|
* is called when constructing the server hello.
|
||||||
|
*/
|
||||||
|
EXT_RETURN tls_construct_stoc_ech(SSL_CONNECTION *s, WPACKET *pkt,
|
||||||
|
unsigned int context, X509 *x,
|
||||||
|
size_t chainidx)
|
||||||
|
{
|
||||||
|
unsigned char *rcfgs = NULL;
|
||||||
|
size_t rcfgslen = 0;
|
||||||
|
|
||||||
|
if (context == SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST
|
||||||
|
&& (s->ext.ech.success == 1 || s->ext.ech.backend == 1)
|
||||||
|
&& s->ext.ech.attempted_type == TLSEXT_TYPE_ech) {
|
||||||
|
unsigned char eightzeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
if (!WPACKET_put_bytes_u16(pkt, s->ext.ech.attempted_type)
|
||||||
|
|| !WPACKET_sub_memcpy_u16(pkt, eightzeros, 8)) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
OSSL_TRACE_BEGIN(TLS) {
|
||||||
|
BIO_printf(trc_out, "set 8 zeros for ECH accept confirm in HRR\n");
|
||||||
|
} OSSL_TRACE_END(TLS);
|
||||||
|
return EXT_RETURN_SENT;
|
||||||
|
}
|
||||||
|
/* GREASE or error => random confirmation in HRR case */
|
||||||
|
if (context == SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST
|
||||||
|
&& s->ext.ech.attempted_type == TLSEXT_TYPE_ech
|
||||||
|
&& s->ext.ech.attempted == 1) {
|
||||||
|
unsigned char randomconf[8];
|
||||||
|
|
||||||
|
if (RAND_bytes_ex(s->ssl.ctx->libctx, randomconf, 8,
|
||||||
|
RAND_DRBG_STRENGTH) <= 0) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!WPACKET_put_bytes_u16(pkt, s->ext.ech.attempted_type)
|
||||||
|
|| !WPACKET_sub_memcpy_u16(pkt, randomconf, 8)) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
OSSL_TRACE_BEGIN(TLS) {
|
||||||
|
BIO_printf(trc_out, "set random for ECH acccpt confirm in HRR\n");
|
||||||
|
} OSSL_TRACE_END(TLS);
|
||||||
|
return EXT_RETURN_SENT;
|
||||||
|
}
|
||||||
|
/* in other HRR circumstances: don't set */
|
||||||
|
if (context == SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST)
|
||||||
|
return EXT_RETURN_NOT_SENT;
|
||||||
|
/* If in some weird state we ignore and send nothing */
|
||||||
|
if (s->ext.ech.grease != OSSL_ECH_IS_GREASE
|
||||||
|
|| s->ext.ech.attempted_type != TLSEXT_TYPE_ech)
|
||||||
|
return EXT_RETURN_NOT_SENT;
|
||||||
|
/*
|
||||||
|
* If the client GREASEd, or we think it did, return the
|
||||||
|
* most-recently loaded ECHConfigList, as the value of the
|
||||||
|
* extension. Most-recently loaded can be anywhere in the
|
||||||
|
* list, depending on changing or non-changing file names.
|
||||||
|
*/
|
||||||
|
if (s->ext.ech.es == NULL) {
|
||||||
|
OSSL_TRACE_BEGIN(TLS) {
|
||||||
|
BIO_printf(trc_out, "ECH - not sending ECHConfigList to client "
|
||||||
|
"even though they GREASE'd as I've no loaded configs\n");
|
||||||
|
} OSSL_TRACE_END(TLS);
|
||||||
|
return EXT_RETURN_NOT_SENT;
|
||||||
|
}
|
||||||
|
if (ossl_ech_get_retry_configs(s, &rcfgs, &rcfgslen) != 1) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (rcfgslen == 0) {
|
||||||
|
OSSL_TRACE_BEGIN(TLS) {
|
||||||
|
BIO_printf(trc_out, "ECH - not sending ECHConfigList to client "
|
||||||
|
"even though they GREASE'd and I have configs but "
|
||||||
|
"I've no configs set to be returned\n");
|
||||||
|
} OSSL_TRACE_END(TLS);
|
||||||
|
return EXT_RETURN_NOT_SENT;
|
||||||
|
}
|
||||||
|
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_ech)
|
||||||
|
|| !WPACKET_start_sub_packet_u16(pkt)
|
||||||
|
|| !WPACKET_sub_memcpy_u16(pkt, rcfgs, rcfgslen)
|
||||||
|
|| !WPACKET_close(pkt)) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
OPENSSL_free(rcfgs);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
OPENSSL_free(rcfgs);
|
||||||
|
return EXT_RETURN_SENT;
|
||||||
|
}
|
||||||
|
#endif /* END OPENSSL_NO_ECH */
|
||||||
|
|
|
||||||
|
|
@ -1312,7 +1312,7 @@ __owur CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
OPENSSL_free(s->ext.ech.innerch);
|
OPENSSL_free(s->ext.ech.innerch);
|
||||||
s->ext.ech.innerch = (unsigned char*)inner_mem->data;
|
s->ext.ech.innerch = (unsigned char *)inner_mem->data;
|
||||||
inner_mem->data = NULL;
|
inner_mem->data = NULL;
|
||||||
s->ext.ech.innerch_len = innerlen;
|
s->ext.ech.innerch_len = innerlen;
|
||||||
/* add inner to transcript */
|
/* add inner to transcript */
|
||||||
|
|
|
||||||
|
|
@ -574,7 +574,9 @@ int tls_parse_stoc_server_cert_type(SSL_CONNECTION *s, PACKET *pkt,
|
||||||
EXT_RETURN tls_construct_ctos_ech(SSL_CONNECTION *s, WPACKET *pkt,
|
EXT_RETURN tls_construct_ctos_ech(SSL_CONNECTION *s, WPACKET *pkt,
|
||||||
unsigned int context, X509 *x,
|
unsigned int context, X509 *x,
|
||||||
size_t chainidx);
|
size_t chainidx);
|
||||||
EXT_RETURN tls_construct_ctos_ech(SSL_CONNECTION *s, WPACKET *pkt,
|
int tls_parse_ctos_ech(SSL_CONNECTION *s, PACKET *pkt, unsigned int context,
|
||||||
|
X509 *x, size_t chainidx);
|
||||||
|
EXT_RETURN tls_construct_stoc_ech(SSL_CONNECTION *s, WPACKET *pkt,
|
||||||
unsigned int context, X509 *x,
|
unsigned int context, X509 *x,
|
||||||
size_t chainidx);
|
size_t chainidx);
|
||||||
int tls_parse_stoc_ech(SSL_CONNECTION *s, PACKET *pkt, unsigned int context,
|
int tls_parse_stoc_ech(SSL_CONNECTION *s, PACKET *pkt, unsigned int context,
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,10 @@
|
||||||
|
|
||||||
#define TICKET_NONCE_SIZE 8
|
#define TICKET_NONCE_SIZE 8
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_ECH
|
||||||
|
# include "../ech/ech_local.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ASN1_TYPE *kxBlob;
|
ASN1_TYPE *kxBlob;
|
||||||
ASN1_TYPE *opaqueBlob;
|
ASN1_TYPE *opaqueBlob;
|
||||||
|
|
@ -1495,6 +1499,86 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL_CONNECTION *s, PACKET *pkt)
|
||||||
static const unsigned char null_compression = 0;
|
static const unsigned char null_compression = 0;
|
||||||
CLIENTHELLO_MSG *clienthello = NULL;
|
CLIENTHELLO_MSG *clienthello = NULL;
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_ECH
|
||||||
|
/*
|
||||||
|
* For a split-mode backend we want to have a way to point at the CH octets
|
||||||
|
* for the accept-confirmation calculation. The split-mode backend does not
|
||||||
|
* need any ECH secrets, but it does need to see the inner CH and be the TLS
|
||||||
|
* endpoint with which the ECH encrypting client sets up the TLS session.
|
||||||
|
* The split-mode backend however does need to do an ECH confirm calculation
|
||||||
|
* so we need to tee that up. The result of that calculation will be put in
|
||||||
|
* the ServerHello.random (or ECH extension if HRR) to signal to the client
|
||||||
|
* that ECH "worked."
|
||||||
|
*/
|
||||||
|
if (s->server && PACKET_remaining(pkt) != 0) {
|
||||||
|
int rv = 0, innerflag = -1;
|
||||||
|
size_t startofsessid = 0, startofexts = 0, echoffset = 0;
|
||||||
|
size_t outersnioffset = 0; /* offset to SNI in outer */
|
||||||
|
uint16_t echtype = OSSL_ECH_type_unknown; /* type of ECH seen */
|
||||||
|
const unsigned char *pbuf = NULL;
|
||||||
|
|
||||||
|
/* reset needed in case of HRR */
|
||||||
|
s->ext.ech.ch_offsets_done = 0;
|
||||||
|
rv = ossl_ech_get_ch_offsets(s, pkt, &startofsessid, &startofexts,
|
||||||
|
&echoffset, &echtype, &innerflag,
|
||||||
|
&outersnioffset);
|
||||||
|
if (rv != 1) {
|
||||||
|
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (innerflag == OSSL_ECH_INNER_CH_TYPE) {
|
||||||
|
WPACKET inner;
|
||||||
|
|
||||||
|
OSSL_TRACE(TLS, "Got inner ECH so setting backend\n");
|
||||||
|
/* For backend, include msg type & 3 octet length */
|
||||||
|
s->ext.ech.backend = 1;
|
||||||
|
s->ext.ech.attempted_type = TLSEXT_TYPE_ech;
|
||||||
|
OPENSSL_free(s->ext.ech.innerch);
|
||||||
|
s->ext.ech.innerch_len = PACKET_remaining(pkt);
|
||||||
|
if (PACKET_peek_bytes(pkt, &pbuf, s->ext.ech.innerch_len) != 1) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
s->ext.ech.innerch_len += SSL3_HM_HEADER_LENGTH; /* 4 */
|
||||||
|
s->ext.ech.innerch = OPENSSL_malloc(s->ext.ech.innerch_len);
|
||||||
|
if (s->ext.ech.innerch == NULL) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (!WPACKET_init_static_len(&inner, s->ext.ech.innerch,
|
||||||
|
s->ext.ech.innerch_len, 0)
|
||||||
|
|| !WPACKET_put_bytes_u8(&inner, SSL3_MT_CLIENT_HELLO)
|
||||||
|
|| !WPACKET_put_bytes_u24(&inner, s->ext.ech.innerch_len
|
||||||
|
- SSL3_HM_HEADER_LENGTH)
|
||||||
|
|| !WPACKET_memcpy(&inner, pbuf, s->ext.ech.innerch_len
|
||||||
|
- SSL3_HM_HEADER_LENGTH)
|
||||||
|
|| !WPACKET_finish(&inner)) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else if (s->ext.ech.es != NULL) {
|
||||||
|
PACKET newpkt;
|
||||||
|
|
||||||
|
if (ossl_ech_early_decrypt(s, pkt, &newpkt) != 1) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (s->ext.ech.success == 1) {
|
||||||
|
/*
|
||||||
|
* Replace the outer CH with the inner, as long as there's
|
||||||
|
* space, which there better be! (a bug triggered a bigger
|
||||||
|
* inner CH once;-)
|
||||||
|
*/
|
||||||
|
if (PACKET_remaining(&newpkt) > PACKET_remaining(pkt)) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
*pkt = newpkt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Check if this is actually an unexpected renegotiation ClientHello */
|
/* Check if this is actually an unexpected renegotiation ClientHello */
|
||||||
if (s->renegotiate == 0 && !SSL_IS_FIRST_HANDSHAKE(s)) {
|
if (s->renegotiate == 0 && !SSL_IS_FIRST_HANDSHAKE(s)) {
|
||||||
if (!ossl_assert(!SSL_CONNECTION_IS_TLS13(s))) {
|
if (!ossl_assert(!SSL_CONNECTION_IS_TLS13(s))) {
|
||||||
|
|
@ -1696,6 +1780,12 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL_CONNECTION *s, PACKET *pkt)
|
||||||
if (clienthello != NULL)
|
if (clienthello != NULL)
|
||||||
OPENSSL_free(clienthello->pre_proc_exts);
|
OPENSSL_free(clienthello->pre_proc_exts);
|
||||||
OPENSSL_free(clienthello);
|
OPENSSL_free(clienthello);
|
||||||
|
#ifndef OPENSSL_NO_ECH
|
||||||
|
s->clienthello = NULL;
|
||||||
|
OPENSSL_free(s->ext.ech.innerch);
|
||||||
|
s->ext.ech.innerch = NULL;
|
||||||
|
s->ext.ech.innerch_len = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
return MSG_PROCESS_ERROR;
|
return MSG_PROCESS_ERROR;
|
||||||
}
|
}
|
||||||
|
|
@ -1989,12 +2079,24 @@ static int tls_early_post_process_client_hello(SSL_CONNECTION *s)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s->hit
|
/*
|
||||||
&& s->version >= TLS1_VERSION
|
* Unless ECH has worked or not been configured we won't call
|
||||||
&& !SSL_CONNECTION_IS_TLS13(s)
|
* the session_secret_cb now because we'll need to calculate the
|
||||||
&& !SSL_CONNECTION_IS_DTLS(s)
|
* server random later to include the ECH accept value.
|
||||||
&& s->ext.session_secret_cb != NULL) {
|
* We can't do it now as we don't yet have the SH encoding.
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
#ifndef OPENSSL_NO_ECH
|
||||||
|
((s->ext.ech.es != NULL && s->ext.ech.success == 1)
|
||||||
|
|| s->ext.ech.es == NULL) &&
|
||||||
|
#endif
|
||||||
|
!s->hit
|
||||||
|
&& s->version >= TLS1_VERSION
|
||||||
|
&& !SSL_CONNECTION_IS_TLS13(s)
|
||||||
|
&& !SSL_CONNECTION_IS_DTLS(s)
|
||||||
|
&& s->ext.session_secret_cb != NULL) {
|
||||||
const SSL_CIPHER *pref_cipher = NULL;
|
const SSL_CIPHER *pref_cipher = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* s->session->master_key_length is a size_t, but this is an int for
|
* s->session->master_key_length is a size_t, but this is an int for
|
||||||
* backwards compat reasons
|
* backwards compat reasons
|
||||||
|
|
@ -2151,7 +2253,8 @@ static int tls_early_post_process_client_hello(SSL_CONNECTION *s)
|
||||||
err:
|
err:
|
||||||
sk_SSL_CIPHER_free(ciphers);
|
sk_SSL_CIPHER_free(ciphers);
|
||||||
sk_SSL_CIPHER_free(scsvs);
|
sk_SSL_CIPHER_free(scsvs);
|
||||||
OPENSSL_free(clienthello->pre_proc_exts);
|
if (clienthello != NULL)
|
||||||
|
OPENSSL_free(clienthello->pre_proc_exts);
|
||||||
OPENSSL_free(s->clienthello);
|
OPENSSL_free(s->clienthello);
|
||||||
s->clienthello = NULL;
|
s->clienthello = NULL;
|
||||||
|
|
||||||
|
|
@ -2513,16 +2616,147 @@ CON_FUNC_RETURN tls_construct_server_hello(SSL_CONNECTION *s, WPACKET *pkt)
|
||||||
* Re-initialise the Transcript Hash. We're going to prepopulate it with
|
* Re-initialise the Transcript Hash. We're going to prepopulate it with
|
||||||
* a synthetic message_hash in place of ClientHello1.
|
* a synthetic message_hash in place of ClientHello1.
|
||||||
*/
|
*/
|
||||||
if (!create_synthetic_message_hash(s, NULL, 0, NULL, 0)) {
|
#ifndef OPENSSL_NO_ECH
|
||||||
|
/*
|
||||||
|
* if we're sending 2nd SH after HRR and we did ECH
|
||||||
|
* then we want to inject the hash of the inner CH1
|
||||||
|
* and not the outer (which is the default)
|
||||||
|
*/
|
||||||
|
OSSL_TRACE_BEGIN(TLS) {
|
||||||
|
BIO_printf(trc_out, "Checking success (%d)/innerCH (%p)\n",
|
||||||
|
s->ext.ech.success, (void *)s->ext.ech.innerch);
|
||||||
|
} OSSL_TRACE_END(TLS);
|
||||||
|
if ((s->ext.ech.backend == 1 || s->ext.ech.success == 1)
|
||||||
|
&& s->ext.ech.innerch != NULL) {
|
||||||
|
/* do pre-existing HRR stuff */
|
||||||
|
unsigned char hashval[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int hashlen;
|
||||||
|
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||||
|
const EVP_MD *md = NULL;
|
||||||
|
|
||||||
|
OSSL_TRACE(TLS, "Adding in digest of ClientHello\n");
|
||||||
|
# ifdef OSSL_ECH_SUPERVERBOSE
|
||||||
|
ossl_ech_pbuf("innerch", s->ext.ech.innerch,
|
||||||
|
s->ext.ech.innerch_len);
|
||||||
|
# endif
|
||||||
|
if (ctx == NULL) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
md = ssl_handshake_md(s);
|
||||||
|
if (md == NULL) {
|
||||||
|
EVP_MD_CTX_free(ctx);
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
if (EVP_DigestInit_ex(ctx, md, NULL) <= 0
|
||||||
|
|| EVP_DigestUpdate(ctx, s->ext.ech.innerch,
|
||||||
|
s->ext.ech.innerch_len) <= 0
|
||||||
|
|| EVP_DigestFinal_ex(ctx, hashval, &hashlen) <= 0) {
|
||||||
|
EVP_MD_CTX_free(ctx);
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
# ifdef OSSL_ECH_SUPERVERBOSE
|
||||||
|
ossl_ech_pbuf("digested CH", hashval, hashlen);
|
||||||
|
# endif
|
||||||
|
EVP_MD_CTX_free(ctx);
|
||||||
|
if (ossl_ech_reset_hs_buffer(s, NULL, 0) != 1) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
if (!create_synthetic_message_hash(s, hashval, hashlen, NULL, 0)) {
|
||||||
|
/* SSLfatal() already called */
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!create_synthetic_message_hash(s, NULL, 0, NULL, 0))
|
||||||
|
return CON_FUNC_ERROR; /* SSLfatal() already called */
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!create_synthetic_message_hash(s, NULL, 0, NULL, 0))
|
||||||
/* SSLfatal() already called */
|
/* SSLfatal() already called */
|
||||||
return CON_FUNC_ERROR;
|
return CON_FUNC_ERROR;
|
||||||
}
|
#endif /* OPENSSL_NO_ECH */
|
||||||
} else if (!(s->verify_mode & SSL_VERIFY_PEER)
|
} else if (!(s->verify_mode & SSL_VERIFY_PEER)
|
||||||
&& !ssl3_digest_cached_records(s, 0)) {
|
&& !ssl3_digest_cached_records(s, 0)) {
|
||||||
/* SSLfatal() already called */;
|
/* SSLfatal() already called */;
|
||||||
return CON_FUNC_ERROR;
|
return CON_FUNC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_ECH
|
||||||
|
/*
|
||||||
|
* Calculate the ECH-accept server random to indicate that
|
||||||
|
* we're accepting ECH, if that's the case
|
||||||
|
*/
|
||||||
|
if (s->ext.ech.attempted_type == TLSEXT_TYPE_ech
|
||||||
|
&& (s->ext.ech.backend == 1
|
||||||
|
|| (s->ext.ech.es != NULL && s->ext.ech.success == 1))) {
|
||||||
|
unsigned char acbuf[8];
|
||||||
|
unsigned char *shbuf = NULL;
|
||||||
|
size_t shlen = 0;
|
||||||
|
size_t shoffset = 0;
|
||||||
|
int hrr = 0;
|
||||||
|
|
||||||
|
if (s->hello_retry_request == SSL_HRR_PENDING)
|
||||||
|
hrr = 1;
|
||||||
|
memset(acbuf, 0, 8);
|
||||||
|
if (WPACKET_get_total_written(pkt, &shlen) != 1) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
shbuf = WPACKET_get_curr(pkt) - shlen;
|
||||||
|
/* we need to fixup SH length here */
|
||||||
|
shbuf[1] = ((shlen - 4)) >> 16 & 0xff;
|
||||||
|
shbuf[2] = ((shlen - 4)) >> 8 & 0xff;
|
||||||
|
shbuf[3] = (shlen - 4) & 0xff;
|
||||||
|
if (ossl_ech_intbuf_add(s, shbuf, shlen, hrr) != 1) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
if (ossl_ech_calc_confirm(s, hrr, acbuf, shlen) != 1) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
memcpy(s->s3.server_random + SSL3_RANDOM_SIZE - 8, acbuf, 8);
|
||||||
|
if (hrr == 0) {
|
||||||
|
/* confirm value hacked into SH.random rightmost octets */
|
||||||
|
shoffset = SSL3_HM_HEADER_LENGTH /* 4 */
|
||||||
|
+ CLIENT_VERSION_LEN /* 2 */
|
||||||
|
+ SSL3_RANDOM_SIZE /* 32 */
|
||||||
|
- 8;
|
||||||
|
memcpy(shbuf + shoffset, acbuf, 8);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* confirm value is in extension in HRR case as the SH.random
|
||||||
|
* is already hacked to be a specific value in a HRR
|
||||||
|
*/
|
||||||
|
memcpy(WPACKET_get_curr(pkt) - 8, acbuf, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* call ECH callback, if appropriate */
|
||||||
|
if (s->ext.ech.attempted == 1 && s->ext.ech.cb != NULL
|
||||||
|
&& s->hello_retry_request != SSL_HRR_PENDING) {
|
||||||
|
char pstr[OSSL_ECH_PBUF_SIZE + 1];
|
||||||
|
BIO *biom = BIO_new(BIO_s_mem());
|
||||||
|
unsigned int cbrv = 0;
|
||||||
|
|
||||||
|
if (biom == NULL) {
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
memset(pstr, 0, OSSL_ECH_PBUF_SIZE + 1);
|
||||||
|
ossl_ech_status_print(biom, s, OSSL_ECHSTORE_ALL);
|
||||||
|
BIO_read(biom, pstr, OSSL_ECH_PBUF_SIZE);
|
||||||
|
cbrv = s->ext.ech.cb(&s->ssl, pstr);
|
||||||
|
BIO_free(biom);
|
||||||
|
if (cbrv != 1) {
|
||||||
|
OSSL_TRACE(TLS, "Error from tls_construct_server_hello/ech_cb\n");
|
||||||
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
||||||
|
return CON_FUNC_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* OPENSSL_NO_ECH */
|
||||||
return CON_FUNC_SUCCESS;
|
return CON_FUNC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -893,11 +893,15 @@ static int ech_ingest_test(int run)
|
||||||
* Occasionally, flush_time will be 1 more than add_time. We'll
|
* Occasionally, flush_time will be 1 more than add_time. We'll
|
||||||
* check for that as that should catch a few more code paths
|
* check for that as that should catch a few more code paths
|
||||||
* in the flush_keys API.
|
* in the flush_keys API.
|
||||||
|
* When flush_time is 1 more, we may or may not have flushed
|
||||||
|
* the one and only key (depending on which "side" of the second
|
||||||
|
* it was generated, so we may be left with 0 or 1 keys.
|
||||||
*/
|
*/
|
||||||
if (!TEST_true(OSSL_ECHSTORE_flush_keys(es, flush_time - add_time))
|
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(OSSL_ECHSTORE_num_keys(es, &keysaftr), 1)
|
||||||
|| ((flush_time <= add_time) && !TEST_int_eq(keysaftr, 0))
|
|| ((flush_time <= add_time) && !TEST_int_eq(keysaftr, 0))
|
||||||
|| ((flush_time > add_time) && !TEST_int_eq(keysaftr, 1))) {
|
|| ((flush_time > add_time) && !TEST_int_eq(keysaftr, 1)
|
||||||
|
&& !TEST_int_eq(keysaftr, 0))) {
|
||||||
TEST_info("Flush time: %lld, add_time: %lld", (long long)flush_time,
|
TEST_info("Flush time: %lld, add_time: %lld", (long long)flush_time,
|
||||||
(long long)add_time);
|
(long long)add_time);
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -1141,6 +1145,7 @@ end:
|
||||||
# define OSSL_ECH_TEST_EARLY 2
|
# define OSSL_ECH_TEST_EARLY 2
|
||||||
# define OSSL_ECH_TEST_CUSTOM 3
|
# define OSSL_ECH_TEST_CUSTOM 3
|
||||||
# define OSSL_ECH_TEST_ENOE 4 /* early + no-ech */
|
# define OSSL_ECH_TEST_ENOE 4 /* early + no-ech */
|
||||||
|
/* note: early-data is prohibited after HRR so no tests for that */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @brief ECH roundtrip test helper
|
* @brief ECH roundtrip test helper
|
||||||
|
|
@ -1155,13 +1160,6 @@ end:
|
||||||
*
|
*
|
||||||
* The combo input is one of the #define'd OSSL_ECH_TEST_*
|
* The combo input is one of the #define'd OSSL_ECH_TEST_*
|
||||||
* values above.
|
* values above.
|
||||||
*
|
|
||||||
* TODO(ECH): we're not yet really attempting ECH, but we currently
|
|
||||||
* set the inputs as if we were doing ECH, yet don't expect to see
|
|
||||||
* real ECH status outcomes, so while we do make calls to get that
|
|
||||||
* status outcome, we don't compare vs. real expected results.
|
|
||||||
* That's done via the "if (0 &&" clauses below which will be
|
|
||||||
* removed once ECH is really being attempted.
|
|
||||||
*/
|
*/
|
||||||
static int test_ech_roundtrip_helper(int idx, int combo)
|
static int test_ech_roundtrip_helper(int idx, int combo)
|
||||||
{
|
{
|
||||||
|
|
@ -1205,10 +1203,9 @@ static int test_ech_roundtrip_helper(int idx, int combo)
|
||||||
if (combo == OSSL_ECH_TEST_EARLY || combo == OSSL_ECH_TEST_ENOE) {
|
if (combo == OSSL_ECH_TEST_EARLY || combo == OSSL_ECH_TEST_ENOE) {
|
||||||
if (!TEST_true(SSL_CTX_set_options(sctx, SSL_OP_NO_ANTI_REPLAY))
|
if (!TEST_true(SSL_CTX_set_options(sctx, SSL_OP_NO_ANTI_REPLAY))
|
||||||
|| !TEST_true(SSL_CTX_set_max_early_data(sctx,
|
|| !TEST_true(SSL_CTX_set_max_early_data(sctx,
|
||||||
SSL3_RT_MAX_PLAIN_LENGTH)))
|
SSL3_RT_MAX_PLAIN_LENGTH))
|
||||||
goto end;
|
|| !TEST_true(SSL_CTX_set_recv_max_early_data(sctx,
|
||||||
if (!TEST_true(SSL_CTX_set_recv_max_early_data(sctx,
|
SSL3_RT_MAX_PLAIN_LENGTH)))
|
||||||
SSL3_RT_MAX_PLAIN_LENGTH)))
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
if (combo == OSSL_ECH_TEST_CUSTOM) {
|
if (combo == OSSL_ECH_TEST_CUSTOM) {
|
||||||
|
|
@ -1239,58 +1236,38 @@ static int test_ech_roundtrip_helper(int idx, int combo)
|
||||||
goto end;
|
goto end;
|
||||||
if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example")))
|
if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example")))
|
||||||
goto end;
|
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,
|
if (!TEST_true(create_ssl_connection(serverssl, clientssl,
|
||||||
SSL_ERROR_NONE)))
|
SSL_ERROR_NONE)))
|
||||||
goto end;
|
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);
|
|
||||||
if (0 && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS))
|
|
||||||
goto end;
|
|
||||||
/* override cert verification */
|
/* override cert verification */
|
||||||
SSL_set_verify_result(clientssl, X509_V_OK);
|
SSL_set_verify_result(clientssl, X509_V_OK);
|
||||||
clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter);
|
clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter);
|
||||||
if (verbose)
|
if (verbose)
|
||||||
TEST_info("client status %d, %s, %s", clientstatus, cinner, couter);
|
TEST_info("client status %d, %s, %s", clientstatus, cinner, couter);
|
||||||
if (0 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS))
|
serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter);
|
||||||
|
if (verbose)
|
||||||
|
TEST_info("server status %d, %s, %s", serverstatus, sinner, souter);
|
||||||
|
if (combo != OSSL_ECH_TEST_ENOE
|
||||||
|
&& !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS))
|
||||||
|
goto end;
|
||||||
|
if (combo == OSSL_ECH_TEST_ENOE
|
||||||
|
&& !TEST_int_eq(serverstatus, SSL_ECH_STATUS_NOT_TRIED))
|
||||||
|
goto end;
|
||||||
|
if (combo != OSSL_ECH_TEST_ENOE
|
||||||
|
&& !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS))
|
||||||
|
goto end;
|
||||||
|
if (combo == OSSL_ECH_TEST_ENOE
|
||||||
|
&& !TEST_int_eq(clientstatus, SSL_ECH_STATUS_NOT_CONFIGURED))
|
||||||
goto end;
|
goto end;
|
||||||
/* all good */
|
/* all good */
|
||||||
if (combo == OSSL_ECH_TEST_BASIC
|
if (combo == OSSL_ECH_TEST_BASIC || combo == OSSL_ECH_TEST_HRR
|
||||||
|| combo == OSSL_ECH_TEST_HRR
|
|
||||||
|| combo == OSSL_ECH_TEST_CUSTOM) {
|
|| combo == OSSL_ECH_TEST_CUSTOM) {
|
||||||
res = 1;
|
res = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
/* continue for EARLY test */
|
/* continue for EARLY test */
|
||||||
# ifdef DROPFORNOW
|
|
||||||
/* TODO(ECH): turn back on later */
|
|
||||||
if (combo != OSSL_ECH_TEST_EARLY && combo != OSSL_ECH_TEST_ENOE)
|
if (combo != OSSL_ECH_TEST_EARLY && combo != OSSL_ECH_TEST_ENOE)
|
||||||
goto end;
|
goto end;
|
||||||
# else
|
|
||||||
if (combo != OSSL_ECH_TEST_ENOE) {
|
|
||||||
res = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
/* shutdown for start over */
|
/* shutdown for start over */
|
||||||
sess = SSL_get1_session(clientssl);
|
sess = SSL_get1_session(clientssl);
|
||||||
OPENSSL_free(sinner);
|
OPENSSL_free(sinner);
|
||||||
|
|
@ -1310,8 +1287,8 @@ static int test_ech_roundtrip_helper(int idx, int combo)
|
||||||
|| !TEST_true(SSL_set_session(clientssl, sess))
|
|| !TEST_true(SSL_set_session(clientssl, sess))
|
||||||
|| !TEST_true(SSL_write_early_data(clientssl, ed, sizeof(ed), &written))
|
|| !TEST_true(SSL_write_early_data(clientssl, ed, sizeof(ed), &written))
|
||||||
|| !TEST_size_t_eq(written, sizeof(ed))
|
|| !TEST_size_t_eq(written, sizeof(ed))
|
||||||
|| !TEST_int_eq(SSL_read_early_data(serverssl, buf,
|
|| !TEST_int_eq(SSL_read_early_data(serverssl, buf, sizeof(buf),
|
||||||
sizeof(buf), &readbytes),
|
&readbytes),
|
||||||
SSL_READ_EARLY_DATA_SUCCESS)
|
SSL_READ_EARLY_DATA_SUCCESS)
|
||||||
|| !TEST_size_t_eq(written, readbytes))
|
|| !TEST_size_t_eq(written, readbytes))
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -1324,17 +1301,25 @@ static int test_ech_roundtrip_helper(int idx, int combo)
|
||||||
|| !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &readbytes))
|
|| !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &readbytes))
|
||||||
|| !TEST_mem_eq(buf, readbytes, ed, sizeof(ed)))
|
|| !TEST_mem_eq(buf, readbytes, ed, sizeof(ed)))
|
||||||
goto end;
|
goto end;
|
||||||
serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter);
|
|
||||||
if (verbose)
|
|
||||||
TEST_info("server status %d, %s, %s", serverstatus, sinner, souter);
|
|
||||||
if (0 && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS))
|
|
||||||
goto end;
|
|
||||||
/* override cert verification */
|
/* override cert verification */
|
||||||
SSL_set_verify_result(clientssl, X509_V_OK);
|
SSL_set_verify_result(clientssl, X509_V_OK);
|
||||||
clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter);
|
clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter);
|
||||||
if (verbose)
|
if (verbose)
|
||||||
TEST_info("client status %d, %s, %s", clientstatus, cinner, couter);
|
TEST_info("client status %d, %s, %s", clientstatus, cinner, couter);
|
||||||
if (0 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS))
|
serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter);
|
||||||
|
if (verbose)
|
||||||
|
TEST_info("server status %d, %s, %s", serverstatus, sinner, souter);
|
||||||
|
if (combo != OSSL_ECH_TEST_ENOE
|
||||||
|
&& !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS))
|
||||||
|
goto end;
|
||||||
|
if (combo == OSSL_ECH_TEST_ENOE
|
||||||
|
&& !TEST_int_eq(serverstatus, SSL_ECH_STATUS_NOT_TRIED))
|
||||||
|
goto end;
|
||||||
|
if (combo != OSSL_ECH_TEST_ENOE
|
||||||
|
&& !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS))
|
||||||
|
goto end;
|
||||||
|
if (combo == OSSL_ECH_TEST_ENOE
|
||||||
|
&& !TEST_int_eq(clientstatus, SSL_ECH_STATUS_NOT_CONFIGURED))
|
||||||
goto end;
|
goto end;
|
||||||
/* all good */
|
/* all good */
|
||||||
res = 1;
|
res = 1;
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ __current_exception_context
|
||||||
strlen
|
strlen
|
||||||
strstr
|
strstr
|
||||||
strchr
|
strchr
|
||||||
|
strlen
|
||||||
memmove
|
memmove
|
||||||
strrchr
|
strrchr
|
||||||
memcmp
|
memcmp
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue