Update to draft-ietf-tls-esni-13. Later CLs will clean up the ClientHello construction a bit (draft-12 avoids computing ClientHelloOuter twice). I suspect the transcript handling on the client can also be simpler, but I'll see what's convenient after I've changed how ClientHelloOuter is constructed. Changes of note between draft-10 and draft-13: - There is now an ECH confirmation signal in both HRR and SH. We don't actually make much use of this in our client right now, but it resolves a bunch of weird issues around HRR, including edge cases if HRR applies to one ClientHello but not the other. - The confirmation signal no longer depends on key_share and PSK, so we don't have to work around a weird ordering issue. - ech_is_inner is now folded into the main encrypted_client_hello code point. This works better with some stuff around HRR. - Padding is moved from the padding extension, computed with ClientHelloInner, to something we fill in afterwards. This makes it easier to pad up the whole thing to a multiple of 32. I've accordingly updated to the latest recommended padding construction, and updated the GREASE logic to match. - ech_outer_extensions is much easier to process because the order is required to be consistent. We were doing that anyway, and now a simple linear scan works. - ClientHelloOuterAAD now uses an all zero placeholder payload of the same length. This lets us simplify the server code, but, for now, I've kept the client code the same. I'll follow this up with a CL to avoid computing ClientHelloOuter twice. - ClientHelloOuterAAD is allowed to contain a placeholder PSK. I haven't filled that in and will do it in a follow-up CL. Bug: 275 Change-Id: I7464345125c53968b2fe692f9268e392120fc2eb Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48912 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/internal.h b/ssl/internal.h index 6b7528c..ab23d29 100644 --- a/ssl/internal.h +++ b/ssl/internal.h
@@ -1451,7 +1451,7 @@ Span<const uint8_t> public_name; Span<const uint8_t> cipher_suites; uint16_t kem_id = 0; - uint16_t maximum_name_length = 0; + uint8_t maximum_name_length = 0; uint8_t config_id = 0; }; @@ -1488,6 +1488,10 @@ ssl_client_hello_outer, }; +// ECH_CLIENT_* are types for the ClientHello encrypted_client_hello extension. +#define ECH_CLIENT_OUTER 0 +#define ECH_CLIENT_INNER 1 + // ssl_decode_client_hello_inner recovers the full ClientHelloInner from the // EncodedClientHelloInner |encoded_client_hello_inner| by replacing its // outer_extensions extension with the referenced extensions from the @@ -1499,18 +1503,13 @@ Span<const uint8_t> encoded_client_hello_inner, const SSL_CLIENT_HELLO *client_hello_outer); -// ssl_client_hello_decrypt attempts to decrypt the given |payload| into -// |out_encoded_client_hello_inner|. The decrypted value should be an -// EncodedClientHelloInner. It returns false if any fatal errors occur and true -// otherwise, regardless of whether the decrypt was successful. It sets -// |out_encoded_client_hello_inner| to true if the decryption fails, and false -// otherwise. -bool ssl_client_hello_decrypt(EVP_HPKE_CTX *hpke_ctx, - Array<uint8_t> *out_encoded_client_hello_inner, +// ssl_client_hello_decrypt attempts to decrypt the |payload| and writes the +// result to |*out|. |payload| must point into |client_hello_outer|. It returns +// true on success and false on error. On error, it sets |*out_is_decrypt_error| +// to whether the failure was due to a bad ciphertext. +bool ssl_client_hello_decrypt(EVP_HPKE_CTX *hpke_ctx, Array<uint8_t> *out, bool *out_is_decrypt_error, const SSL_CLIENT_HELLO *client_hello_outer, - uint16_t kdf_id, uint16_t aead_id, - uint8_t config_id, Span<const uint8_t> enc, Span<const uint8_t> payload); #define ECH_CONFIRMATION_SIGNAL_LEN 8 @@ -1520,13 +1519,14 @@ size_t ssl_ech_confirmation_signal_hello_offset(const SSL *ssl); // ssl_ech_accept_confirmation computes the server's ECH acceptance signal, -// writing it to |out|. The signal is computed by concatenating |transcript| -// with |server_hello|. This function handles the fact that eight bytes of -// |server_hello| need to be replaced with zeros before hashing. It returns true -// on success, and false on failure. +// writing it to |out|. The transcript portion is the concatenation of +// |transcript| with |msg|. The |ECH_CONFIRMATION_SIGNAL_LEN| bytes from +// |offset| in |msg| are replaced with zeros before hashing. This function +// returns true on success, and false on failure. bool ssl_ech_accept_confirmation(const SSL_HANDSHAKE *hs, Span<uint8_t> out, - const SSLTranscript &transcript, - Span<const uint8_t> server_hello); + Span<const uint8_t> client_random, + const SSLTranscript &transcript, bool is_hrr, + Span<const uint8_t> msg, size_t offset); // ssl_is_valid_ech_public_name returns true if |public_name| is a valid ECH // public name and false otherwise. It is exported for testing. @@ -1832,8 +1832,9 @@ // cookie is the value of the cookie received from the server, if any. Array<uint8_t> cookie; - // ech_client_bytes contains the ECH extension to send in the ClientHello. - Array<uint8_t> ech_client_bytes; + // ech_client_outer contains the outer ECH extension to send in the + // ClientHello, excluding the header and type byte. + Array<uint8_t> ech_client_outer; // ech_retry_configs, on the client, contains the retry configs from the // server as a serialized ECHConfigList. @@ -1941,13 +1942,9 @@ // influence the handshake on match. UniquePtr<SSL_HANDSHAKE_HINTS> hints; - // ech_present, on the server, indicates whether the ClientHello contained an - // encrypted_client_hello extension. - bool ech_present : 1; - - // ech_is_inner_present, on the server, indicates whether the ClientHello - // contained an ech_is_inner extension. - bool ech_is_inner_present : 1; + // ech_is_inner, on the server, indicates whether the ClientHello contained an + // inner ECH extension. + bool ech_is_inner : 1; // ech_authenticated_reject, on the client, indicates whether an ECH rejection // handshake has been authenticated. @@ -2166,6 +2163,7 @@ bool ssl_add_client_hello(SSL_HANDSHAKE *hs); struct ParsedServerHello { + CBS raw; uint16_t legacy_version = 0; CBS random; CBS session_id; @@ -2278,6 +2276,9 @@ OPENSSL_EXPORT bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out, Span<const uint8_t> body); +bool ssl_parse_client_hello_with_trailing_data(const SSL *ssl, CBS *cbs, + SSL_CLIENT_HELLO *out); + bool ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello, CBS *out, uint16_t extension_type); @@ -3315,14 +3316,10 @@ // ClientHello extension was the pre_shared_key extension and needs a PSK binder // filled in. The caller should then update |out| and, if applicable, // |out_encoded| with the binder after completing the whole message. -// -// If |omit_ech_len| is non-zero, the ECH extension is omitted, but padding is -// computed as if there were an extension of length |omit_ech_len|. This is used -// to compute ClientHelloOuterAAD. bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded, bool *out_needs_psk_binder, - ssl_client_hello_type_t type, size_t header_len, - size_t omit_ech_len); + ssl_client_hello_type_t type, + size_t header_len); bool ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out); bool ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,