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 | ||||
| within a ClientHello callback. | ||||
| 
 | ||||
| TODO(ECH): How ECH is handled here needs to be documented. | ||||
| 
 | ||||
| =head1 RETURN VALUES | ||||
| 
 | ||||
| The application's supplied ClientHello callback returns | ||||
|  |  | |||
|  | @ -17,9 +17,42 @@ | |||
| 
 | ||||
| # 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, | ||||
|                            size_t encoding_length, | ||||
|                            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 | ||||
|  |  | |||
|  | @ -13,8 +13,6 @@ | |||
| #include "ech_local.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) */ | ||||
| /* "tls ech" */ | ||||
| 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); | ||||
|     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 */ | ||||
| } 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) | ||||
| 
 | ||||
| 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 */ | ||||
|     size_t pub_len; | ||||
|     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. | ||||
|      * 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; | ||||
|     } | ||||
|     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; | ||||
|     if (s->ext.ech.backend == 1) { | ||||
|         if (s->ext.hostname != NULL | ||||
|  |  | |||
|  | @ -498,13 +498,8 @@ static const EXTENSION_DEFINITION ext_defs[] = { | |||
|         SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST, | ||||
|         OSSL_ECH_HANDLING_CALL_BOTH, | ||||
|         init_ech, | ||||
|         /*
 | ||||
|          * TODO(ECH): add server calls as per below in a bit | ||||
|          * tls_parse_ctos_ech, tls_parse_stoc_ech, | ||||
|          * tls_construct_stoc_ech, tls_construct_ctos_ech, | ||||
|          */ | ||||
|         NULL, tls_parse_stoc_ech, | ||||
|         NULL, tls_construct_ctos_ech, | ||||
|         tls_parse_ctos_ech, tls_parse_stoc_ech, | ||||
|         tls_construct_stoc_ech, tls_construct_ctos_ech, | ||||
|         NULL | ||||
|     }, | ||||
|     { | ||||
|  |  | |||
|  | @ -14,12 +14,7 @@ | |||
| #include "statem_local.h" | ||||
| #ifndef OPENSSL_NO_ECH | ||||
| # include <openssl/rand.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) | ||||
| # include "internal/ech_helpers.h" | ||||
| #endif | ||||
| 
 | ||||
| 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; | ||||
|     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; | ||||
| 
 | ||||
|     /* 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 | ||||
|                           || ((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) { | ||||
|                     OSSL_TRACE_BEGIN(TLS) { | ||||
|                         BIO_printf(trc_out, | ||||
|                                 "Too many outers to compress (max=%d)\n", | ||||
|                                 OSSL_ECH_OUTERS_MAX); | ||||
|                                    "Too many outers to compress (max=%d)\n", | ||||
|                                    OSSL_ECH_OUTERS_MAX); | ||||
|                     } OSSL_TRACE_END(TLS); | ||||
|                     SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION); | ||||
|                     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++; | ||||
|                 OSSL_TRACE_BEGIN(TLS) { | ||||
|                     BIO_printf(trc_out, "ECH compressing type " | ||||
|                             "0x%04x (tot: %d)\n", | ||||
|                             (int) meth->ext_type, | ||||
|                             (int) s->ext.ech.n_outer_only); | ||||
|                                "0x%04x (tot: %d)\n", | ||||
|                                (int) meth->ext_type, | ||||
|                                (int) s->ext.ech.n_outer_only); | ||||
|                 } OSSL_TRACE_END(TLS); | ||||
|             } | ||||
|             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); | ||||
|                     return 0; | ||||
|                 } | ||||
|                 if (ossl_ech_copy_inner2outer(s, meth->ext_type, tind, pkt) | ||||
|                         != OSSL_ECH_SAME_EXT_DONE) { | ||||
|                 if (ossl_ech_copy_inner2outer(s, meth->ext_type, tind, | ||||
|                                               pkt) != OSSL_ECH_SAME_EXT_DONE) { | ||||
|                     /* for custom exts, we really should have found it */ | ||||
|                     SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_EXTENSION); | ||||
|                     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. | ||||
|      */ | ||||
|     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) | ||||
| #else | ||||
|             && ext_type != TLSEXT_TYPE_signed_certificate_timestamp) | ||||
| #endif | ||||
|         return 0; | ||||
| 
 | ||||
|     /* Extension type must fit in 16 bits */ | ||||
|  |  | |||
|  | @ -13,7 +13,12 @@ | |||
| #include "internal/cryptlib.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 | ||||
|  | @ -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); | ||||
|     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; | ||||
|     } | ||||
|     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; | ||||
|     s->ext.ech.innerch_len = innerlen; | ||||
|     /* 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, | ||||
|                                   unsigned int context, X509 *x, | ||||
|                                   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, | ||||
|                                   size_t chainidx); | ||||
| int tls_parse_stoc_ech(SSL_CONNECTION *s, PACKET *pkt, unsigned int context, | ||||
|  |  | |||
|  | @ -35,6 +35,10 @@ | |||
| 
 | ||||
| #define TICKET_NONCE_SIZE       8 | ||||
| 
 | ||||
| #ifndef OPENSSL_NO_ECH | ||||
| # include "../ech/ech_local.h" | ||||
| #endif | ||||
| 
 | ||||
| typedef struct { | ||||
|   ASN1_TYPE *kxBlob; | ||||
|   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; | ||||
|     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 */ | ||||
|     if (s->renegotiate == 0 && !SSL_IS_FIRST_HANDSHAKE(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) | ||||
|         OPENSSL_free(clienthello->pre_proc_exts); | ||||
|     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; | ||||
| } | ||||
|  | @ -1989,12 +2079,24 @@ static int tls_early_post_process_client_hello(SSL_CONNECTION *s) | |||
|         goto err; | ||||
|     } | ||||
| 
 | ||||
|     if (!s->hit | ||||
|             && s->version >= TLS1_VERSION | ||||
|             && !SSL_CONNECTION_IS_TLS13(s) | ||||
|             && !SSL_CONNECTION_IS_DTLS(s) | ||||
|             && s->ext.session_secret_cb != NULL) { | ||||
|     /*
 | ||||
|      * 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 | ||||
|         && !SSL_CONNECTION_IS_TLS13(s) | ||||
|         && !SSL_CONNECTION_IS_DTLS(s) | ||||
|         && s->ext.session_secret_cb != NULL) { | ||||
|         const SSL_CIPHER *pref_cipher = NULL; | ||||
| 
 | ||||
|         /*
 | ||||
|          * s->session->master_key_length is a size_t, but this is an int for | ||||
|          * backwards compat reasons | ||||
|  | @ -2151,7 +2253,8 @@ static int tls_early_post_process_client_hello(SSL_CONNECTION *s) | |||
|  err: | ||||
|     sk_SSL_CIPHER_free(ciphers); | ||||
|     sk_SSL_CIPHER_free(scsvs); | ||||
|     OPENSSL_free(clienthello->pre_proc_exts); | ||||
|     if (clienthello != NULL) | ||||
|         OPENSSL_free(clienthello->pre_proc_exts); | ||||
|     OPENSSL_free(s->clienthello); | ||||
|     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 | ||||
|          * 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 */ | ||||
|             return CON_FUNC_ERROR; | ||||
|         } | ||||
| #endif /* OPENSSL_NO_ECH */ | ||||
|     } else if (!(s->verify_mode & SSL_VERIFY_PEER) | ||||
|                 && !ssl3_digest_cached_records(s, 0)) { | ||||
|                && !ssl3_digest_cached_records(s, 0)) { | ||||
|         /* SSLfatal() already called */; | ||||
|         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; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -893,11 +893,15 @@ static int ech_ingest_test(int run) | |||
|      * Occasionally, flush_time will be 1 more than add_time. We'll | ||||
|      * check for that as that should catch a few more code paths | ||||
|      * 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)) | ||||
|         || !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, 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, | ||||
|                   (long long)add_time); | ||||
|         goto end; | ||||
|  | @ -1141,6 +1145,7 @@ end: | |||
| # define OSSL_ECH_TEST_EARLY    2 | ||||
| # define OSSL_ECH_TEST_CUSTOM   3 | ||||
| # 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 | ||||
|  | @ -1155,13 +1160,6 @@ end: | |||
|  * | ||||
|  * The combo input is one of the #define'd OSSL_ECH_TEST_* | ||||
|  * values above. | ||||
|  * | ||||
|  * TODO(ECH): we're not yet really attempting ECH, but we currently | ||||
|  * set the inputs as if we were doing ECH, yet don't expect to see | ||||
|  * real ECH status outcomes, so while we do make calls to get that | ||||
|  * status outcome, we don't compare vs. real expected results. | ||||
|  * That's done via the "if (0 &&" clauses below which will be | ||||
|  * removed once ECH is really being attempted. | ||||
|  */ | ||||
| static int test_ech_roundtrip_helper(int idx, int combo) | ||||
| { | ||||
|  | @ -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 (!TEST_true(SSL_CTX_set_options(sctx, SSL_OP_NO_ANTI_REPLAY)) | ||||
|             || !TEST_true(SSL_CTX_set_max_early_data(sctx, | ||||
|                                                      SSL3_RT_MAX_PLAIN_LENGTH))) | ||||
|             goto end; | ||||
|         if (!TEST_true(SSL_CTX_set_recv_max_early_data(sctx, | ||||
|                                                        SSL3_RT_MAX_PLAIN_LENGTH))) | ||||
|                                                      SSL3_RT_MAX_PLAIN_LENGTH)) | ||||
|             || !TEST_true(SSL_CTX_set_recv_max_early_data(sctx, | ||||
|                                                           SSL3_RT_MAX_PLAIN_LENGTH))) | ||||
|             goto end; | ||||
|     } | ||||
|     if (combo == OSSL_ECH_TEST_CUSTOM) { | ||||
|  | @ -1239,58 +1236,38 @@ static int test_ech_roundtrip_helper(int idx, int combo) | |||
|         goto end; | ||||
|     if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example"))) | ||||
|         goto end; | ||||
| # undef DROPFORNOW | ||||
| # ifdef DROPFORNOW | ||||
|     /* TODO(ECH): we'll re-instate this once server-side ECH code is in */ | ||||
|     if (!TEST_true(create_ssl_connection(serverssl, clientssl, | ||||
|                                          SSL_ERROR_NONE))) | ||||
|         goto end; | ||||
| # else | ||||
|     /*
 | ||||
|      * For this PR, check connections fail when client does ECH | ||||
|      * and server doesn't, but work if client doesn't do ECH. | ||||
|      * Added in early data for the no-ECH case because an | ||||
|      * intermediate state of the code had an issue. | ||||
|      */ | ||||
|     if (combo != OSSL_ECH_TEST_ENOE | ||||
|         && !TEST_false(create_ssl_connection(serverssl, clientssl, | ||||
|                                              SSL_ERROR_NONE))) | ||||
|         goto end; | ||||
|     if (combo == OSSL_ECH_TEST_ENOE | ||||
|         && !TEST_true(create_ssl_connection(serverssl, clientssl, | ||||
|                                             SSL_ERROR_NONE))) | ||||
|         goto end; | ||||
| # endif | ||||
|     serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter); | ||||
|     if (verbose) | ||||
|         TEST_info("server status %d, %s, %s", serverstatus, sinner, souter); | ||||
|     if (0 && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS)) | ||||
|         goto end; | ||||
|     /* override cert verification */ | ||||
|     SSL_set_verify_result(clientssl, X509_V_OK); | ||||
|     clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter); | ||||
|     if (verbose) | ||||
|         TEST_info("client status %d, %s, %s", clientstatus, cinner, couter); | ||||
|     if (0 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS)) | ||||
|     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; | ||||
|     /* all good */ | ||||
|     if (combo == OSSL_ECH_TEST_BASIC | ||||
|         || combo == OSSL_ECH_TEST_HRR | ||||
|     if (combo == OSSL_ECH_TEST_BASIC || combo == OSSL_ECH_TEST_HRR | ||||
|         || combo == OSSL_ECH_TEST_CUSTOM) { | ||||
|         res = 1; | ||||
|         goto end; | ||||
|     } | ||||
|     /* continue for EARLY test */ | ||||
| # ifdef DROPFORNOW | ||||
|     /* TODO(ECH): turn back on later */ | ||||
|     if (combo != OSSL_ECH_TEST_EARLY && combo != OSSL_ECH_TEST_ENOE) | ||||
|         goto end; | ||||
| # else | ||||
|     if (combo != OSSL_ECH_TEST_ENOE) { | ||||
|         res = 1; | ||||
|         goto end; | ||||
|     } | ||||
| # endif | ||||
|     /* shutdown for start over */ | ||||
|     sess = SSL_get1_session(clientssl); | ||||
|     OPENSSL_free(sinner); | ||||
|  | @ -1310,8 +1287,8 @@ static int test_ech_roundtrip_helper(int idx, int combo) | |||
|         || !TEST_true(SSL_set_session(clientssl, sess)) | ||||
|         || !TEST_true(SSL_write_early_data(clientssl, ed, sizeof(ed), &written)) | ||||
|         || !TEST_size_t_eq(written, sizeof(ed)) | ||||
|         || !TEST_int_eq(SSL_read_early_data(serverssl, buf, | ||||
|                                             sizeof(buf), &readbytes), | ||||
|         || !TEST_int_eq(SSL_read_early_data(serverssl, buf, sizeof(buf), | ||||
|                                             &readbytes), | ||||
|                         SSL_READ_EARLY_DATA_SUCCESS) | ||||
|         || !TEST_size_t_eq(written, readbytes)) | ||||
|         goto end; | ||||
|  | @ -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_mem_eq(buf, readbytes, ed, sizeof(ed))) | ||||
|         goto end; | ||||
|     serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter); | ||||
|     if (verbose) | ||||
|         TEST_info("server status %d, %s, %s", serverstatus, sinner, souter); | ||||
|     if (0 && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS)) | ||||
|         goto end; | ||||
|     /* override cert verification */ | ||||
|     SSL_set_verify_result(clientssl, X509_V_OK); | ||||
|     clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter); | ||||
|     if (verbose) | ||||
|         TEST_info("client status %d, %s, %s", clientstatus, cinner, couter); | ||||
|     if (0 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS)) | ||||
|     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; | ||||
|     /* all good */ | ||||
|     res = 1; | ||||
|  |  | |||
|  | @ -89,6 +89,7 @@ __current_exception_context | |||
| strlen | ||||
| strstr | ||||
| strchr | ||||
| strlen | ||||
| memmove | ||||
| strrchr | ||||
| memcmp | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue