Skip to content

Commit 00e434d

Browse files
dmcardleCQ bot account: commit-bot@chromium.org
authored and
CQ bot account: commit-bot@chromium.org
committed
Add ECH server (draft-ietf-tls-esni-09).
This CL adds an initial implementation of the ECH server, with pieces of the client in BoGo as necessary for testing. In particular, the server supports ClientHelloInner compression with ech_outer_extensions. When ECH decryption fails, it can send retry_configs back to the client. This server passes the "ech-accept" and "ech-reject" test cases in tls-interop-runner[0] when tested against both the cloudflare-go and nss clients. For reproducibility, I started with the main branch at commit 707604c262d8bcf3e944ed1d5a675077304732ce and updated the endpoint's script to pass the server's ECHConfig and private key to the boringssl tool. Follow-up CLs will update HPKE to the latest draft and catch us up to draft-10. [0]: https://github.com/xvzcf/tls-interop-runner Bug: 275 Change-Id: I49be35af46d1fd5dd9c62252f07d0bae179381ab Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/45285 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com>
1 parent 61d5aab commit 00e434d

27 files changed

+3165
-744
lines changed

crypto/err/ssl.errordata

+5
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ SSL,264,DUPLICATE_KEY_SHARE
5858
SSL,296,DUPLICATE_SIGNATURE_ALGORITHM
5959
SSL,283,EARLY_DATA_NOT_IN_USE
6060
SSL,144,ECC_CERT_NOT_FOR_SIGNING
61+
SSL,310,ECH_SERVER_CONFIG_AND_PRIVATE_KEY_MISMATCH
62+
SSL,311,ECH_SERVER_CONFIG_UNSUPPORTED_EXTENSION
63+
SSL,313,ECH_SERVER_WOULD_HAVE_NO_RETRY_CONFIGS
6164
SSL,282,EMPTY_HELLO_RETRY_REQUEST
6265
SSL,145,EMS_STATE_INCONSISTENT
6366
SSL,146,ENCRYPTED_LENGTH_TOO_LONG
@@ -76,6 +79,7 @@ SSL,156,HTTP_REQUEST
7679
SSL,157,INAPPROPRIATE_FALLBACK
7780
SSL,303,INCONSISTENT_CLIENT_HELLO
7881
SSL,259,INVALID_ALPN_PROTOCOL
82+
SSL,314,INVALID_CLIENT_HELLO_INNER
7983
SSL,158,INVALID_COMMAND
8084
SSL,256,INVALID_COMPRESSION_LIST
8185
SSL,301,INVALID_DELEGATED_CREDENTIAL
@@ -226,6 +230,7 @@ SSL,235,UNKNOWN_STATE
226230
SSL,236,UNSAFE_LEGACY_RENEGOTIATION_DISABLED
227231
SSL,237,UNSUPPORTED_CIPHER
228232
SSL,238,UNSUPPORTED_COMPRESSION_ALGORITHM
233+
SSL,312,UNSUPPORTED_ECH_SERVER_CONFIG
229234
SSL,239,UNSUPPORTED_ELLIPTIC_CURVE
230235
SSL,240,UNSUPPORTED_PROTOCOL
231236
SSL,252,UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY

crypto/hpke/hpke.c

+11-6
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@
3232

3333
#define KEM_CONTEXT_LEN (2 * X25519_PUBLIC_VALUE_LEN)
3434

35-
// HPKE KEM scheme IDs.
36-
#define HPKE_DHKEM_X25519_HKDF_SHA256 0x0020
37-
3835
// This is strlen("HPKE") + 3 * sizeof(uint16_t).
3936
#define HPKE_SUITE_ID_LEN 10
4037

@@ -51,8 +48,8 @@ static int add_label_string(CBB *cbb, const char *label) {
5148
// that the suite_id used outside of the KEM also includes the kdf_id and
5249
// aead_id.
5350
static const uint8_t kX25519SuiteID[] = {
54-
'K', 'E', 'M', HPKE_DHKEM_X25519_HKDF_SHA256 >> 8,
55-
HPKE_DHKEM_X25519_HKDF_SHA256 & 0x00ff};
51+
'K', 'E', 'M', EVP_HPKE_DHKEM_X25519_HKDF_SHA256 >> 8,
52+
EVP_HPKE_DHKEM_X25519_HKDF_SHA256 & 0x00ff};
5653

5754
// The suite_id for non-KEM pieces of HPKE is defined as concat("HPKE",
5855
// I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)).
@@ -61,7 +58,7 @@ static int hpke_build_suite_id(uint8_t out[HPKE_SUITE_ID_LEN], uint16_t kdf_id,
6158
CBB cbb;
6259
int ret = CBB_init_fixed(&cbb, out, HPKE_SUITE_ID_LEN) &&
6360
add_label_string(&cbb, "HPKE") &&
64-
CBB_add_u16(&cbb, HPKE_DHKEM_X25519_HKDF_SHA256) &&
61+
CBB_add_u16(&cbb, EVP_HPKE_DHKEM_X25519_HKDF_SHA256) &&
6562
CBB_add_u16(&cbb, kdf_id) &&
6663
CBB_add_u16(&cbb, aead_id);
6764
CBB_cleanup(&cbb);
@@ -126,6 +123,14 @@ static int hpke_extract_and_expand(const EVP_MD *hkdf_md, uint8_t *out_key,
126123
return 1;
127124
}
128125

126+
uint16_t EVP_HPKE_CTX_get_aead_id(const EVP_HPKE_CTX *hpke) {
127+
return hpke->aead_id;
128+
}
129+
130+
uint16_t EVP_HPKE_CTX_get_kdf_id(const EVP_HPKE_CTX *hpke) {
131+
return hpke->kdf_id;
132+
}
133+
129134
const EVP_AEAD *EVP_HPKE_get_aead(uint16_t aead_id) {
130135
switch (aead_id) {
131136
case EVP_HPKE_AEAD_AES_128_GCM:

crypto/hpke/internal.h

+13
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ extern "C" {
3333
//
3434
// See https://tools.ietf.org/html/draft-irtf-cfrg-hpke-07.
3535

36+
// EVP_HPKE_DHKEM_* are KEM identifiers.
37+
#define EVP_HPKE_DHKEM_X25519_HKDF_SHA256 0x0020
38+
3639
// EVP_HPKE_AEAD_* are AEAD identifiers.
3740
#define EVP_HPKE_AEAD_AES_128_GCM 0x0001
3841
#define EVP_HPKE_AEAD_AES_256_GCM 0x0002
@@ -224,6 +227,16 @@ OPENSSL_EXPORT int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *hpke, uint8_t *out,
224227
// set up as a sender.
225228
OPENSSL_EXPORT size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *hpke);
226229

230+
// EVP_HPKE_CTX_get_aead_id returns |hpke|'s configured AEAD. The returned value
231+
// is one of the |EVP_HPKE_AEAD_*| constants, or zero if the context has not
232+
// been set up.
233+
OPENSSL_EXPORT uint16_t EVP_HPKE_CTX_get_aead_id(const EVP_HPKE_CTX *hpke);
234+
235+
// EVP_HPKE_CTX_get_aead_id returns |hpke|'s configured KDF. The returned value
236+
// is one of the |EVP_HPKE_HKDF_*| constants, or zero if the context has not
237+
// been set up.
238+
OPENSSL_EXPORT uint16_t EVP_HPKE_CTX_get_kdf_id(const EVP_HPKE_CTX *hpke);
239+
227240
// EVP_HPKE_get_aead returns the AEAD corresponding to |aead_id|, or NULL if
228241
// |aead_id| is not a known AEAD identifier.
229242
OPENSSL_EXPORT const EVP_AEAD *EVP_HPKE_get_aead(uint16_t aead_id);

fuzz/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ fuzzer(dtls_server ssl)
2929
fuzzer(dtls_client ssl)
3030
fuzzer(ssl_ctx_api ssl)
3131
fuzzer(session ssl)
32+
fuzzer(decode_client_hello_inner ssl)

fuzz/decode_client_hello_inner.cc

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* Copyright (c) 2021, Google Inc.
2+
*
3+
* Permission to use, copy, modify, and/or distribute this software for any
4+
* purpose with or without fee is hereby granted, provided that the above
5+
* copyright notice and this permission notice appear in all copies.
6+
*
7+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10+
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12+
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13+
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14+
15+
#include <openssl/bytestring.h>
16+
#include <openssl/ssl.h>
17+
#include <openssl/span.h>
18+
19+
#include "../ssl/internal.h"
20+
21+
22+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) {
23+
static bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
24+
static bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
25+
26+
CBS reader(bssl::MakeConstSpan(buf, len));
27+
CBS encoded_client_hello_inner_cbs;
28+
29+
if (!CBS_get_u24_length_prefixed(&reader, &encoded_client_hello_inner_cbs)) {
30+
return 0;
31+
}
32+
33+
bssl::Array<uint8_t> encoded_client_hello_inner;
34+
if (!encoded_client_hello_inner.CopyFrom(encoded_client_hello_inner_cbs)) {
35+
return 0;
36+
}
37+
38+
// Use the remaining bytes in |reader| as the ClientHelloOuter.
39+
SSL_CLIENT_HELLO client_hello_outer;
40+
if (!bssl::ssl_client_hello_init(ssl.get(), &client_hello_outer, reader)) {
41+
return 0;
42+
}
43+
44+
// Recover the ClientHelloInner from the EncodedClientHelloInner and
45+
// ClientHelloOuter.
46+
uint8_t alert_unused;
47+
bssl::Array<uint8_t> client_hello_inner;
48+
bssl::ssl_decode_client_hello_inner(
49+
ssl.get(), &alert_unused, &client_hello_inner, encoded_client_hello_inner,
50+
&client_hello_outer);
51+
return 0;
52+
}

include/openssl/base.h

+1
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ typedef struct spake2_ctx_st SPAKE2_CTX;
431431
typedef struct srtp_protection_profile_st SRTP_PROTECTION_PROFILE;
432432
typedef struct ssl_cipher_st SSL_CIPHER;
433433
typedef struct ssl_ctx_st SSL_CTX;
434+
typedef struct ssl_ech_server_config_list_st SSL_ECH_SERVER_CONFIG_LIST;
434435
typedef struct ssl_method_st SSL_METHOD;
435436
typedef struct ssl_private_key_method_st SSL_PRIVATE_KEY_METHOD;
436437
typedef struct ssl_quic_method_st SSL_QUIC_METHOD;

include/openssl/ssl.h

+76-1
Original file line numberDiff line numberDiff line change
@@ -3575,7 +3575,7 @@ OPENSSL_EXPORT const char *SSL_early_data_reason_string(
35753575
enum ssl_early_data_reason_t reason);
35763576

35773577

3578-
// Encrypted Client Hello.
3578+
// Encrypted ClientHello.
35793579
//
35803580
// ECH is a mechanism for encrypting the entire ClientHello message in TLS 1.3.
35813581
// This can prevent observers from seeing cleartext information about the
@@ -3589,6 +3589,72 @@ OPENSSL_EXPORT const char *SSL_early_data_reason_string(
35893589
// as part of this connection.
35903590
OPENSSL_EXPORT void SSL_set_enable_ech_grease(SSL *ssl, int enable);
35913591

3592+
// SSL_ECH_SERVER_CONFIG_LIST_new returns a newly-allocated
3593+
// |SSL_ECH_SERVER_CONFIG_LIST| or NULL on error.
3594+
OPENSSL_EXPORT SSL_ECH_SERVER_CONFIG_LIST *SSL_ECH_SERVER_CONFIG_LIST_new(void);
3595+
3596+
// SSL_ECH_SERVER_CONFIG_LIST_up_ref increments the reference count of |list|.
3597+
OPENSSL_EXPORT void SSL_ECH_SERVER_CONFIG_LIST_up_ref(
3598+
SSL_ECH_SERVER_CONFIG_LIST *list);
3599+
3600+
// SSL_ECH_SERVER_CONFIG_LIST_free releases memory associated with |list|.
3601+
OPENSSL_EXPORT void SSL_ECH_SERVER_CONFIG_LIST_free(
3602+
SSL_ECH_SERVER_CONFIG_LIST *list);
3603+
3604+
// SSL_ECH_SERVER_CONFIG_LIST_add appends an ECHConfig in |ech_config| and its
3605+
// corresponding private key in |private_key| to |list|. When |is_retry_config|
3606+
// is non-zero, this config will be returned to the client on configuration
3607+
// mismatch. It returns one on success and zero on error. See also
3608+
// |SSL_CTX_set1_ech_server_config_list|.
3609+
//
3610+
// This function should be called successively to register each ECHConfig in
3611+
// decreasing order of preference. This configuration must be completed before
3612+
// setting |list| on an |SSL_CTX| with |SSL_CTX_set1_ech_server_config_list|.
3613+
// After that point, |list| is immutable; no more ECHConfig values may be added.
3614+
OPENSSL_EXPORT int SSL_ECH_SERVER_CONFIG_LIST_add(
3615+
SSL_ECH_SERVER_CONFIG_LIST *list, int is_retry_config,
3616+
const uint8_t *ech_config, size_t ech_config_len,
3617+
const uint8_t *private_key, size_t private_key_len);
3618+
3619+
// SSL_CTX_set1_ech_server_config_list atomically sets the refcounted |list|
3620+
// onto |ctx|, releasing the old list. |SSL| objects associated with |ctx|, as
3621+
// servers, will use |list| to decrypt incoming encrypted ClientHello messages.
3622+
// It returns one on success, and zero on failure.
3623+
//
3624+
// If |list| does not contain any retry configs, this function will fail. Retry
3625+
// configs are marked as such when they are added to |list| with
3626+
// |SSL_ECH_SERVER_CONFIG_LIST_add|.
3627+
//
3628+
// Once |list| has been passed to this function, it is immutable. Unlike most
3629+
// |SSL_CTX| configuration functions, this function may be called even if |ctx|
3630+
// already has associated connections on multiple threads. This may be used to
3631+
// rotate keys in a long-lived server process.
3632+
//
3633+
// The configured ECHConfig values should also be advertised out-of-band via DNS
3634+
// (see draft-ietf-dnsop-svcb-https). Before advertising an ECHConfig in DNS,
3635+
// deployments should ensure all instances of the service are configured with
3636+
// the ECHConfig and corresponding private key.
3637+
//
3638+
// Only the most recent fully-deployed ECHConfigs should be advertised in DNS.
3639+
// |list| may contain a newer set if those ECHConfigs are mid-deployment. It
3640+
// should also contain older sets, until the DNS change has rolled out and the
3641+
// old records have expired from caches.
3642+
//
3643+
// If there is a mismatch, |SSL| objects associated with |ctx| will complete the
3644+
// handshake using the cleartext ClientHello and send updated ECHConfig values
3645+
// to the client. The client will then retry to recover, but with a latency
3646+
// penalty. This recovery flow depends on the public name in the ECHConfig.
3647+
// Before advertising an ECHConfig in DNS, deployments must ensure all instances
3648+
// of the service can present a valid certificate for the public name.
3649+
//
3650+
// BoringSSL negotiates ECH before certificate selection callbacks are called,
3651+
// including |SSL_CTX_set_select_certificate_cb|. If ECH is negotiated, the
3652+
// reported |SSL_CLIENT_HELLO| structure and |SSL_get_servername| function will
3653+
// transparently reflect the inner ClientHello. Callers should select parameters
3654+
// based on these values to correctly handle ECH as well as the recovery flow.
3655+
OPENSSL_EXPORT int SSL_CTX_set1_ech_server_config_list(
3656+
SSL_CTX *ctx, SSL_ECH_SERVER_CONFIG_LIST *list);
3657+
35923658

35933659
// Alerts.
35943660
//
@@ -4960,6 +5026,10 @@ BSSL_NAMESPACE_BEGIN
49605026
BORINGSSL_MAKE_DELETER(SSL, SSL_free)
49615027
BORINGSSL_MAKE_DELETER(SSL_CTX, SSL_CTX_free)
49625028
BORINGSSL_MAKE_UP_REF(SSL_CTX, SSL_CTX_up_ref)
5029+
BORINGSSL_MAKE_DELETER(SSL_ECH_SERVER_CONFIG_LIST,
5030+
SSL_ECH_SERVER_CONFIG_LIST_free)
5031+
BORINGSSL_MAKE_UP_REF(SSL_ECH_SERVER_CONFIG_LIST,
5032+
SSL_ECH_SERVER_CONFIG_LIST_up_ref)
49635033
BORINGSSL_MAKE_DELETER(SSL_SESSION, SSL_SESSION_free)
49645034
BORINGSSL_MAKE_UP_REF(SSL_SESSION, SSL_SESSION_up_ref)
49655035

@@ -5293,6 +5363,11 @@ BSSL_NAMESPACE_END
52935363
#define SSL_R_NO_APPLICATION_PROTOCOL 307
52945364
#define SSL_R_NEGOTIATED_ALPS_WITHOUT_ALPN 308
52955365
#define SSL_R_ALPS_MISMATCH_ON_EARLY_DATA 309
5366+
#define SSL_R_ECH_SERVER_CONFIG_AND_PRIVATE_KEY_MISMATCH 310
5367+
#define SSL_R_ECH_SERVER_CONFIG_UNSUPPORTED_EXTENSION 311
5368+
#define SSL_R_UNSUPPORTED_ECH_SERVER_CONFIG 312
5369+
#define SSL_R_ECH_SERVER_WOULD_HAVE_NO_RETRY_CONFIGS 313
5370+
#define SSL_R_INVALID_CLIENT_HELLO_INNER 314
52965371
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
52975372
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
52985373
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020

include/openssl/tls1.h

+1
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ extern "C" {
257257
// extension number.
258258
#define TLSEXT_TYPE_encrypted_client_hello 0xfe09
259259
#define TLSEXT_TYPE_ech_is_inner 0xda09
260+
#define TLSEXT_TYPE_ech_outer_extensions 0xfd00
260261

261262
// ExtensionType value from RFC6962
262263
#define TLSEXT_TYPE_certificate_timestamp 18

ssl/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_library(
1010
d1_srtp.cc
1111
dtls_method.cc
1212
dtls_record.cc
13+
encrypted_client_hello.cc
1314
handoff.cc
1415
handshake.cc
1516
handshake_client.cc

0 commit comments

Comments
 (0)