Skip to content

Commit

Permalink
Add ECH server (draft-ietf-tls-esni-09).
Browse files Browse the repository at this point in the history
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 <[email protected]>
Commit-Queue: David Benjamin <[email protected]>
  • Loading branch information
dmcardle authored and CQ bot account: [email protected] committed Apr 8, 2021
1 parent 61d5aab commit 00e434d
Show file tree
Hide file tree
Showing 27 changed files with 3,165 additions and 744 deletions.
5 changes: 5 additions & 0 deletions crypto/err/ssl.errordata
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ SSL,264,DUPLICATE_KEY_SHARE
SSL,296,DUPLICATE_SIGNATURE_ALGORITHM
SSL,283,EARLY_DATA_NOT_IN_USE
SSL,144,ECC_CERT_NOT_FOR_SIGNING
SSL,310,ECH_SERVER_CONFIG_AND_PRIVATE_KEY_MISMATCH
SSL,311,ECH_SERVER_CONFIG_UNSUPPORTED_EXTENSION
SSL,313,ECH_SERVER_WOULD_HAVE_NO_RETRY_CONFIGS
SSL,282,EMPTY_HELLO_RETRY_REQUEST
SSL,145,EMS_STATE_INCONSISTENT
SSL,146,ENCRYPTED_LENGTH_TOO_LONG
Expand All @@ -76,6 +79,7 @@ SSL,156,HTTP_REQUEST
SSL,157,INAPPROPRIATE_FALLBACK
SSL,303,INCONSISTENT_CLIENT_HELLO
SSL,259,INVALID_ALPN_PROTOCOL
SSL,314,INVALID_CLIENT_HELLO_INNER
SSL,158,INVALID_COMMAND
SSL,256,INVALID_COMPRESSION_LIST
SSL,301,INVALID_DELEGATED_CREDENTIAL
Expand Down Expand Up @@ -226,6 +230,7 @@ SSL,235,UNKNOWN_STATE
SSL,236,UNSAFE_LEGACY_RENEGOTIATION_DISABLED
SSL,237,UNSUPPORTED_CIPHER
SSL,238,UNSUPPORTED_COMPRESSION_ALGORITHM
SSL,312,UNSUPPORTED_ECH_SERVER_CONFIG
SSL,239,UNSUPPORTED_ELLIPTIC_CURVE
SSL,240,UNSUPPORTED_PROTOCOL
SSL,252,UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY
Expand Down
17 changes: 11 additions & 6 deletions crypto/hpke/hpke.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@

#define KEM_CONTEXT_LEN (2 * X25519_PUBLIC_VALUE_LEN)

// HPKE KEM scheme IDs.
#define HPKE_DHKEM_X25519_HKDF_SHA256 0x0020

// This is strlen("HPKE") + 3 * sizeof(uint16_t).
#define HPKE_SUITE_ID_LEN 10

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

// The suite_id for non-KEM pieces of HPKE is defined as concat("HPKE",
// I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)).
Expand All @@ -61,7 +58,7 @@ static int hpke_build_suite_id(uint8_t out[HPKE_SUITE_ID_LEN], uint16_t kdf_id,
CBB cbb;
int ret = CBB_init_fixed(&cbb, out, HPKE_SUITE_ID_LEN) &&
add_label_string(&cbb, "HPKE") &&
CBB_add_u16(&cbb, HPKE_DHKEM_X25519_HKDF_SHA256) &&
CBB_add_u16(&cbb, EVP_HPKE_DHKEM_X25519_HKDF_SHA256) &&
CBB_add_u16(&cbb, kdf_id) &&
CBB_add_u16(&cbb, aead_id);
CBB_cleanup(&cbb);
Expand Down Expand Up @@ -126,6 +123,14 @@ static int hpke_extract_and_expand(const EVP_MD *hkdf_md, uint8_t *out_key,
return 1;
}

uint16_t EVP_HPKE_CTX_get_aead_id(const EVP_HPKE_CTX *hpke) {
return hpke->aead_id;
}

uint16_t EVP_HPKE_CTX_get_kdf_id(const EVP_HPKE_CTX *hpke) {
return hpke->kdf_id;
}

const EVP_AEAD *EVP_HPKE_get_aead(uint16_t aead_id) {
switch (aead_id) {
case EVP_HPKE_AEAD_AES_128_GCM:
Expand Down
13 changes: 13 additions & 0 deletions crypto/hpke/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ extern "C" {
//
// See https://tools.ietf.org/html/draft-irtf-cfrg-hpke-07.

// EVP_HPKE_DHKEM_* are KEM identifiers.
#define EVP_HPKE_DHKEM_X25519_HKDF_SHA256 0x0020

// EVP_HPKE_AEAD_* are AEAD identifiers.
#define EVP_HPKE_AEAD_AES_128_GCM 0x0001
#define EVP_HPKE_AEAD_AES_256_GCM 0x0002
Expand Down Expand Up @@ -224,6 +227,16 @@ OPENSSL_EXPORT int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *hpke, uint8_t *out,
// set up as a sender.
OPENSSL_EXPORT size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *hpke);

// EVP_HPKE_CTX_get_aead_id returns |hpke|'s configured AEAD. The returned value
// is one of the |EVP_HPKE_AEAD_*| constants, or zero if the context has not
// been set up.
OPENSSL_EXPORT uint16_t EVP_HPKE_CTX_get_aead_id(const EVP_HPKE_CTX *hpke);

// EVP_HPKE_CTX_get_aead_id returns |hpke|'s configured KDF. The returned value
// is one of the |EVP_HPKE_HKDF_*| constants, or zero if the context has not
// been set up.
OPENSSL_EXPORT uint16_t EVP_HPKE_CTX_get_kdf_id(const EVP_HPKE_CTX *hpke);

// EVP_HPKE_get_aead returns the AEAD corresponding to |aead_id|, or NULL if
// |aead_id| is not a known AEAD identifier.
OPENSSL_EXPORT const EVP_AEAD *EVP_HPKE_get_aead(uint16_t aead_id);
Expand Down
1 change: 1 addition & 0 deletions fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ fuzzer(dtls_server ssl)
fuzzer(dtls_client ssl)
fuzzer(ssl_ctx_api ssl)
fuzzer(session ssl)
fuzzer(decode_client_hello_inner ssl)
52 changes: 52 additions & 0 deletions fuzz/decode_client_hello_inner.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* Copyright (c) 2021, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */

#include <openssl/bytestring.h>
#include <openssl/ssl.h>
#include <openssl/span.h>

#include "../ssl/internal.h"


extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) {
static bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
static bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));

CBS reader(bssl::MakeConstSpan(buf, len));
CBS encoded_client_hello_inner_cbs;

if (!CBS_get_u24_length_prefixed(&reader, &encoded_client_hello_inner_cbs)) {
return 0;
}

bssl::Array<uint8_t> encoded_client_hello_inner;
if (!encoded_client_hello_inner.CopyFrom(encoded_client_hello_inner_cbs)) {
return 0;
}

// Use the remaining bytes in |reader| as the ClientHelloOuter.
SSL_CLIENT_HELLO client_hello_outer;
if (!bssl::ssl_client_hello_init(ssl.get(), &client_hello_outer, reader)) {
return 0;
}

// Recover the ClientHelloInner from the EncodedClientHelloInner and
// ClientHelloOuter.
uint8_t alert_unused;
bssl::Array<uint8_t> client_hello_inner;
bssl::ssl_decode_client_hello_inner(
ssl.get(), &alert_unused, &client_hello_inner, encoded_client_hello_inner,
&client_hello_outer);
return 0;
}
1 change: 1 addition & 0 deletions include/openssl/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ typedef struct spake2_ctx_st SPAKE2_CTX;
typedef struct srtp_protection_profile_st SRTP_PROTECTION_PROFILE;
typedef struct ssl_cipher_st SSL_CIPHER;
typedef struct ssl_ctx_st SSL_CTX;
typedef struct ssl_ech_server_config_list_st SSL_ECH_SERVER_CONFIG_LIST;
typedef struct ssl_method_st SSL_METHOD;
typedef struct ssl_private_key_method_st SSL_PRIVATE_KEY_METHOD;
typedef struct ssl_quic_method_st SSL_QUIC_METHOD;
Expand Down
77 changes: 76 additions & 1 deletion include/openssl/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3575,7 +3575,7 @@ OPENSSL_EXPORT const char *SSL_early_data_reason_string(
enum ssl_early_data_reason_t reason);


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

// SSL_ECH_SERVER_CONFIG_LIST_new returns a newly-allocated
// |SSL_ECH_SERVER_CONFIG_LIST| or NULL on error.
OPENSSL_EXPORT SSL_ECH_SERVER_CONFIG_LIST *SSL_ECH_SERVER_CONFIG_LIST_new(void);

// SSL_ECH_SERVER_CONFIG_LIST_up_ref increments the reference count of |list|.
OPENSSL_EXPORT void SSL_ECH_SERVER_CONFIG_LIST_up_ref(
SSL_ECH_SERVER_CONFIG_LIST *list);

// SSL_ECH_SERVER_CONFIG_LIST_free releases memory associated with |list|.
OPENSSL_EXPORT void SSL_ECH_SERVER_CONFIG_LIST_free(
SSL_ECH_SERVER_CONFIG_LIST *list);

// SSL_ECH_SERVER_CONFIG_LIST_add appends an ECHConfig in |ech_config| and its
// corresponding private key in |private_key| to |list|. When |is_retry_config|
// is non-zero, this config will be returned to the client on configuration
// mismatch. It returns one on success and zero on error. See also
// |SSL_CTX_set1_ech_server_config_list|.
//
// This function should be called successively to register each ECHConfig in
// decreasing order of preference. This configuration must be completed before
// setting |list| on an |SSL_CTX| with |SSL_CTX_set1_ech_server_config_list|.
// After that point, |list| is immutable; no more ECHConfig values may be added.
OPENSSL_EXPORT int SSL_ECH_SERVER_CONFIG_LIST_add(
SSL_ECH_SERVER_CONFIG_LIST *list, int is_retry_config,
const uint8_t *ech_config, size_t ech_config_len,
const uint8_t *private_key, size_t private_key_len);

// SSL_CTX_set1_ech_server_config_list atomically sets the refcounted |list|
// onto |ctx|, releasing the old list. |SSL| objects associated with |ctx|, as
// servers, will use |list| to decrypt incoming encrypted ClientHello messages.
// It returns one on success, and zero on failure.
//
// If |list| does not contain any retry configs, this function will fail. Retry
// configs are marked as such when they are added to |list| with
// |SSL_ECH_SERVER_CONFIG_LIST_add|.
//
// Once |list| has been passed to this function, it is immutable. Unlike most
// |SSL_CTX| configuration functions, this function may be called even if |ctx|
// already has associated connections on multiple threads. This may be used to
// rotate keys in a long-lived server process.
//
// The configured ECHConfig values should also be advertised out-of-band via DNS
// (see draft-ietf-dnsop-svcb-https). Before advertising an ECHConfig in DNS,
// deployments should ensure all instances of the service are configured with
// the ECHConfig and corresponding private key.
//
// Only the most recent fully-deployed ECHConfigs should be advertised in DNS.
// |list| may contain a newer set if those ECHConfigs are mid-deployment. It
// should also contain older sets, until the DNS change has rolled out and the
// old records have expired from caches.
//
// If there is a mismatch, |SSL| objects associated with |ctx| will complete the
// handshake using the cleartext ClientHello and send updated ECHConfig values
// to the client. The client will then retry to recover, but with a latency
// penalty. This recovery flow depends on the public name in the ECHConfig.
// Before advertising an ECHConfig in DNS, deployments must ensure all instances
// of the service can present a valid certificate for the public name.
//
// BoringSSL negotiates ECH before certificate selection callbacks are called,
// including |SSL_CTX_set_select_certificate_cb|. If ECH is negotiated, the
// reported |SSL_CLIENT_HELLO| structure and |SSL_get_servername| function will
// transparently reflect the inner ClientHello. Callers should select parameters
// based on these values to correctly handle ECH as well as the recovery flow.
OPENSSL_EXPORT int SSL_CTX_set1_ech_server_config_list(
SSL_CTX *ctx, SSL_ECH_SERVER_CONFIG_LIST *list);


// Alerts.
//
Expand Down Expand Up @@ -4960,6 +5026,10 @@ BSSL_NAMESPACE_BEGIN
BORINGSSL_MAKE_DELETER(SSL, SSL_free)
BORINGSSL_MAKE_DELETER(SSL_CTX, SSL_CTX_free)
BORINGSSL_MAKE_UP_REF(SSL_CTX, SSL_CTX_up_ref)
BORINGSSL_MAKE_DELETER(SSL_ECH_SERVER_CONFIG_LIST,
SSL_ECH_SERVER_CONFIG_LIST_free)
BORINGSSL_MAKE_UP_REF(SSL_ECH_SERVER_CONFIG_LIST,
SSL_ECH_SERVER_CONFIG_LIST_up_ref)
BORINGSSL_MAKE_DELETER(SSL_SESSION, SSL_SESSION_free)
BORINGSSL_MAKE_UP_REF(SSL_SESSION, SSL_SESSION_up_ref)

Expand Down Expand Up @@ -5293,6 +5363,11 @@ BSSL_NAMESPACE_END
#define SSL_R_NO_APPLICATION_PROTOCOL 307
#define SSL_R_NEGOTIATED_ALPS_WITHOUT_ALPN 308
#define SSL_R_ALPS_MISMATCH_ON_EARLY_DATA 309
#define SSL_R_ECH_SERVER_CONFIG_AND_PRIVATE_KEY_MISMATCH 310
#define SSL_R_ECH_SERVER_CONFIG_UNSUPPORTED_EXTENSION 311
#define SSL_R_UNSUPPORTED_ECH_SERVER_CONFIG 312
#define SSL_R_ECH_SERVER_WOULD_HAVE_NO_RETRY_CONFIGS 313
#define SSL_R_INVALID_CLIENT_HELLO_INNER 314
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
Expand Down
1 change: 1 addition & 0 deletions include/openssl/tls1.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ extern "C" {
// extension number.
#define TLSEXT_TYPE_encrypted_client_hello 0xfe09
#define TLSEXT_TYPE_ech_is_inner 0xda09
#define TLSEXT_TYPE_ech_outer_extensions 0xfd00

// ExtensionType value from RFC6962
#define TLSEXT_TYPE_certificate_timestamp 18
Expand Down
1 change: 1 addition & 0 deletions ssl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_library(
d1_srtp.cc
dtls_method.cc
dtls_record.cc
encrypted_client_hello.cc
handoff.cc
handshake.cc
handshake_client.cc
Expand Down
Loading

0 comments on commit 00e434d

Please sign in to comment.