@@ -180,9 +180,16 @@ struct st_decoded_ech_config_t {
180180 * Properties for ECH. Iff ECH is used and not rejected, `aead` is non-NULL.
181181 */
182182struct st_ptls_ech_t {
183- uint8_t offered : 1 ;
184- uint8_t offered_grease : 1 ;
185- uint8_t accepted : 1 ;
183+ /**
184+ * ECH state for this connection. `OFFERED` and `ACCEPTED` are used on both client and server; `GREASE` is client-only (server
185+ * cannot distinguish GREASE from a config mismatch, both are simply ECH that fails to decrypt).
186+ */
187+ enum en_ptls_ech_state_t {
188+ PTLS_ECH_STATE_NONE = 0 ,
189+ PTLS_ECH_STATE_OFFERED ,
190+ PTLS_ECH_STATE_ACCEPTED ,
191+ PTLS_ECH_STATE_GREASE
192+ } state ;
186193 uint8_t config_id ;
187194 ptls_hpke_kem_t * kem ;
188195 ptls_hpke_cipher_suite_t * cipher ;
@@ -2305,7 +2312,7 @@ static int encode_client_hello(ptls_context_t *ctx, ptls_buffer_t *sendbuf, enum
23052312 buffer_push_extension (sendbuf , PTLS_EXTENSION_TYPE_PRE_SHARED_KEY , {
23062313 ptls_buffer_push_block (sendbuf , 2 , {
23072314 ptls_buffer_push_block (sendbuf , 2 , {
2308- if (mode == ENCODE_CH_MODE_OUTER && ! ech -> offered_grease ) {
2315+ if (mode == ENCODE_CH_MODE_OUTER && ech -> state != PTLS_ECH_STATE_GREASE ) {
23092316 if ((ret = ptls_buffer_reserve (sendbuf , psk_identity .len )) != 0 )
23102317 goto Exit ;
23112318 ctx -> random_bytes (sendbuf -> base + sendbuf -> off , psk_identity .len );
@@ -2315,7 +2322,7 @@ static int encode_client_hello(ptls_context_t *ctx, ptls_buffer_t *sendbuf, enum
23152322 }
23162323 });
23172324 uint32_t age ;
2318- if (mode == ENCODE_CH_MODE_OUTER && ! ech -> offered_grease ) {
2325+ if (mode == ENCODE_CH_MODE_OUTER && ech -> state != PTLS_ECH_STATE_GREASE ) {
23192326 ctx -> random_bytes (& age , sizeof (age ));
23202327 } else {
23212328 age = obfuscated_ticket_age ;
@@ -2397,7 +2404,7 @@ static int send_client_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_
23972404 /* zero-length config indicates ECH greasing */
23982405 client_setup_ech_grease (& tls -> ech , tls -> ctx -> random_bytes , tls -> ctx -> ech .client .kems , tls -> ctx -> ech .client .ciphers ,
23992406 sni_name );
2400- tls -> ech .offered_grease = 1 ;
2407+ tls -> ech .state = PTLS_ECH_STATE_GREASE ;
24012408 }
24022409 }
24032410 }
@@ -2561,10 +2568,10 @@ static int send_client_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_
25612568 memcpy (tls -> ech .client .first_ech .base ,
25622569 emitter -> buf -> base + ech_size_offset - outer_ech_header_size (tls -> ech .client .enc .len ), len );
25632570 tls -> ech .client .first_ech .len = len ;
2564- if (! tls -> ech .offered_grease )
2565- tls -> ech .offered = 1 ;
2571+ if (tls -> ech .state != PTLS_ECH_STATE_GREASE )
2572+ tls -> ech .state = PTLS_ECH_STATE_OFFERED ;
25662573 }
2567- if (tls -> ech .offered_grease ) {
2574+ if (tls -> ech .state == PTLS_ECH_STATE_GREASE ) {
25682575 /* For grease ECH, the server sees the outer CH. Discard the inner state and adopt the outer, then compute the PSK
25692576 * binder over the outer CH using the standard flow. */
25702577 for (size_t i = 0 ; i < tls -> key_schedule -> num_hashes ; ++ i ) {
@@ -2723,7 +2730,7 @@ static int decode_server_hello(ptls_t *tls, struct st_ptls_server_hello_t *sh, c
27232730 break ;
27242731 case PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO :
27252732 assert (sh -> is_retry_request );
2726- if (!( tls -> ech .offered || tls -> ech . offered_grease ) ) {
2733+ if (tls -> ech .state == PTLS_ECH_STATE_NONE ) {
27272734 ret = PTLS_ALERT_UNSUPPORTED_EXTENSION ;
27282735 goto Exit ;
27292736 }
@@ -2824,9 +2831,11 @@ static int client_ech_select_hello(ptls_t *tls, ptls_iovec_t message, size_t con
28242831 if ((ret = ech_calc_confirmation (tls -> key_schedule , confirm_hash_expected , tls -> ech .inner_client_random , label , message )) !=
28252832 0 )
28262833 goto Exit ;
2827- tls -> ech .accepted = ptls_mem_equal (confirm_hash_delivered , confirm_hash_expected , sizeof (confirm_hash_delivered ));
2834+ tls -> ech .state = ptls_mem_equal (confirm_hash_delivered , confirm_hash_expected , sizeof (confirm_hash_delivered ))
2835+ ? PTLS_ECH_STATE_ACCEPTED
2836+ : PTLS_ECH_STATE_OFFERED ;
28282837 memcpy (message .base + confirm_hash_off , confirm_hash_delivered , sizeof (confirm_hash_delivered ));
2829- if (tls -> ech .accepted )
2838+ if (tls -> ech .state == PTLS_ECH_STATE_ACCEPTED )
28302839 goto Exit ;
28312840 }
28322841
@@ -2836,8 +2845,8 @@ static int client_ech_select_hello(ptls_t *tls, ptls_iovec_t message, size_t con
28362845 key_schedule_select_outer (tls -> key_schedule );
28372846
28382847Exit :
2839- PTLS_PROBE (ECH_SELECTION , tls , !! tls -> ech .accepted );
2840- PTLS_LOG_CONN (ech_selection , tls , { PTLS_LOG_ELEMENT_BOOL (is_ech , tls -> ech .accepted ); });
2848+ PTLS_PROBE (ECH_SELECTION , tls , tls -> ech .state == PTLS_ECH_STATE_ACCEPTED );
2849+ PTLS_LOG_CONN (ech_selection , tls , { PTLS_LOG_ELEMENT_BOOL (is_ech , tls -> ech .state == PTLS_ECH_STATE_ACCEPTED ); });
28412850 ptls_clear_memory (confirm_hash_expected , sizeof (confirm_hash_expected ));
28422851 return ret ;
28432852}
@@ -2863,11 +2872,9 @@ static int client_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl
28632872 key_schedule_transform_post_ch1hash (tls -> key_schedule );
28642873 if (tls -> ech .aead != NULL ) {
28652874 size_t confirm_hash_off = 0 ;
2866- if (tls -> ech .offered ) {
2875+ if (tls -> ech .state != PTLS_ECH_STATE_GREASE ) {
28672876 if (sh .retry_request .ech != NULL )
28682877 confirm_hash_off = sh .retry_request .ech - message .base ;
2869- } else {
2870- assert (tls -> ech .offered_grease );
28712878 }
28722879 if ((ret = client_ech_select_hello (tls , message , confirm_hash_off , ECH_CONFIRMATION_HRR )) != 0 )
28732880 goto Exit ;
@@ -2883,11 +2890,9 @@ static int client_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl
28832890 /* check if ECH is accepted */
28842891 if (tls -> ech .aead != NULL ) {
28852892 size_t confirm_hash_off = 0 ;
2886- if (tls -> ech .offered ) {
2893+ if (tls -> ech .state != PTLS_ECH_STATE_GREASE ) {
28872894 confirm_hash_off =
28882895 PTLS_HANDSHAKE_HEADER_SIZE + 2 /* legacy_version */ + PTLS_HELLO_RANDOM_SIZE - PTLS_ECH_CONFIRM_LENGTH ;
2889- } else {
2890- assert (tls -> ech .offered_grease );
28912896 }
28922897 if ((ret = client_ech_select_hello (tls , message , confirm_hash_off , ECH_CONFIRMATION_SERVER_HELLO )) != 0 )
28932898 goto Exit ;
@@ -3036,15 +3041,15 @@ static int client_handle_encrypted_extensions(ptls_t *tls, ptls_iovec_t message,
30363041 break ;
30373042 case PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO : {
30383043 /* accept retry_configs only if we offered ECH (or grease) but rejected */
3039- if (!(( tls -> ech .offered || tls -> ech .offered_grease ) && ! ptls_is_ech_handshake ( tls , NULL , NULL , NULL ) )) {
3044+ if (!(tls -> ech .state == PTLS_ECH_STATE_OFFERED || tls -> ech .state == PTLS_ECH_STATE_GREASE )) {
30403045 ret = PTLS_ALERT_UNSUPPORTED_EXTENSION ;
30413046 goto Exit ;
30423047 }
30433048 /* parse retry_config, and if it is applicable, provide that to the application (grease clients just verify syntax) */
30443049 struct st_decoded_ech_config_t decoded ;
30453050 if ((ret = client_decode_ech_config_list (tls -> ctx , & decoded , ptls_iovec_init (src , end - src ))) != 0 )
30463051 goto Exit ;
3047- if (tls -> ech .offered_grease ) {
3052+ if (tls -> ech .state == PTLS_ECH_STATE_GREASE ) {
30483053 /* GREASE clients ignore retry_configs after verifying the syntax */
30493054 } else if (decoded .kem != NULL && decoded .cipher != NULL && properties != NULL &&
30503055 properties -> client .ech .retry_configs != NULL ) {
@@ -3315,7 +3320,7 @@ static int handle_certificate(ptls_t *tls, const uint8_t *src, const uint8_t *en
33153320 if (tls -> ctx -> verify_certificate != NULL ) {
33163321 const char * server_name = NULL ;
33173322 if (!ptls_is_server (tls )) {
3318- if (tls -> ech .offered && ! ptls_is_ech_handshake ( tls , NULL , NULL , NULL ) ) {
3323+ if (tls -> ech .state == PTLS_ECH_STATE_OFFERED ) {
33193324 server_name = tls -> ech .client .public_name ;
33203325 } else {
33213326 server_name = tls -> server_name ;
@@ -3486,7 +3491,7 @@ static int server_handle_certificate_verify(ptls_t *tls, ptls_iovec_t message)
34863491static int client_handle_finished (ptls_t * tls , ptls_message_emitter_t * emitter , ptls_iovec_t message )
34873492{
34883493 uint8_t send_secret [PTLS_MAX_DIGEST_SIZE ];
3489- int alert_ech_required = tls -> ech .offered && ! ptls_is_ech_handshake ( tls , NULL , NULL , NULL ) , ret ;
3494+ int alert_ech_required = tls -> ech .state == PTLS_ECH_STATE_OFFERED , ret ;
34903495
34913496 if ((ret = verify_finished (tls , message )) != 0 )
34923497 goto Exit ;
@@ -4431,7 +4436,7 @@ static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl
44314436 goto Exit ;
44324437 }
44334438 if (!is_second_flight )
4434- tls -> ech .offered = 1 ;
4439+ tls -> ech .state = PTLS_ECH_STATE_OFFERED ;
44354440 /* obtain AEAD context for opening inner CH */
44364441 if (!is_second_flight && ch -> ech .payload .base != NULL && tls -> ctx -> ech .server .create_opener != NULL ) {
44374442 if ((tls -> ech .aead = tls -> ctx -> ech .server .create_opener -> cb (
@@ -4454,7 +4459,7 @@ static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl
44544459 memset (ech .ch_outer_aad + (ch -> ech .payload .base - (message .base + PTLS_HANDSHAKE_HEADER_SIZE )), 0 , ch -> ech .payload .len );
44554460 if (ptls_aead_decrypt (tls -> ech .aead , ech .encoded_ch_inner , ch -> ech .payload .base , ch -> ech .payload .len , is_second_flight ,
44564461 ech .ch_outer_aad , message .len - PTLS_HANDSHAKE_HEADER_SIZE ) != SIZE_MAX ) {
4457- tls -> ech .accepted = 1 ;
4462+ tls -> ech .state = PTLS_ECH_STATE_ACCEPTED ;
44584463 /* successfully decrypted EncodedCHInner, build CHInner */
44594464 if ((ret = rebuild_ch_inner (& ech .ch_inner , ech .encoded_ch_inner ,
44604465 ech .encoded_ch_inner + ch -> ech .payload .len - tls -> ech .aead -> algo -> tag_size , ch ,
@@ -4482,7 +4487,7 @@ static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl
44824487 tls -> ech .aead = NULL ;
44834488 }
44844489 }
4485- } else if (tls -> ech .offered ) {
4490+ } else if (tls -> ech .state != PTLS_ECH_STATE_NONE ) {
44864491 assert (is_second_flight );
44874492 ret = PTLS_ALERT_ILLEGAL_PARAMETER ;
44884493 goto Exit ;
@@ -4856,7 +4861,7 @@ static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptl
48564861 if (tls -> pending_handshake_secret != NULL )
48574862 buffer_push_extension (sendbuf , PTLS_EXTENSION_TYPE_EARLY_DATA , {});
48584863 /* send ECH retry_configs, if ECH was offered by rejected, even though we (the server) could have accepted ECH */
4859- if (tls -> ech .offered && ! ptls_is_ech_handshake ( tls , NULL , NULL , NULL ) && tls -> ctx -> ech .server .create_opener != NULL &&
4864+ if (tls -> ech .state == PTLS_ECH_STATE_OFFERED && tls -> ctx -> ech .server .create_opener != NULL &&
48604865 tls -> ctx -> ech .server .retry_configs .len != 0 )
48614866 buffer_push_extension (sendbuf , PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO , {
48624867 ptls_buffer_pushv (sendbuf , tls -> ctx -> ech .server .retry_configs .base , tls -> ctx -> ech .server .retry_configs .len );
@@ -5622,7 +5627,7 @@ int ptls_is_psk_handshake(ptls_t *tls)
56225627
56235628int ptls_is_ech_handshake (ptls_t * tls , uint8_t * config_id , ptls_hpke_kem_t * * kem , ptls_hpke_cipher_suite_t * * cipher )
56245629{
5625- if (tls -> ech .accepted ) {
5630+ if (tls -> ech .state == PTLS_ECH_STATE_ACCEPTED ) {
56265631 if (config_id != NULL )
56275632 * config_id = tls -> ech .config_id ;
56285633 if (kem != NULL )
0 commit comments