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)); | ||||||
|  |  | ||||||
|  | @ -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,6 +13,11 @@ | ||||||
| #include "internal/cryptlib.h" | #include "internal/cryptlib.h" | ||||||
| #include "internal/ssl_unwrap.h" | #include "internal/ssl_unwrap.h" | ||||||
| 
 | 
 | ||||||
|  | #ifndef OPENSSL_NO_ECH | ||||||
|  | # include <openssl/rand.h> | ||||||
|  | # include <openssl/trace.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #define COOKIE_STATE_FORMAT_VERSION 1 | #define COOKIE_STATE_FORMAT_VERSION 1 | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -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 |     /*
 | ||||||
|  |      * Unless ECH has worked or not been configured we won't call | ||||||
|  |      * the session_secret_cb now because we'll need to calculate the | ||||||
|  |      * server random later to include the ECH accept value. | ||||||
|  |      * 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 |         && s->version >= TLS1_VERSION | ||||||
|         && !SSL_CONNECTION_IS_TLS13(s) |         && !SSL_CONNECTION_IS_TLS13(s) | ||||||
|         && !SSL_CONNECTION_IS_DTLS(s) |         && !SSL_CONNECTION_IS_DTLS(s) | ||||||
|         && s->ext.session_secret_cb != NULL) { |         && 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,6 +2253,7 @@ 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); | ||||||
|  |     if (clienthello != NULL) | ||||||
|         OPENSSL_free(clienthello->pre_proc_exts); |         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 */ |                 /* SSLfatal() already called */ | ||||||
|                 return CON_FUNC_ERROR; |                 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 */ | ||||||
|  |             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,9 +1203,8 @@ 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; | ||||||
|     } |     } | ||||||
|  | @ -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