Implement GREASE for ECH (draft-ietf-tls-esni-08).

Bug: 275
Change-Id: I4927c0886e3acf5b39104e3d89ed51d67520a343
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/40204
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
chromium-5359
Dan McArdle 5 years ago committed by CQ bot account: commit-bot@chromium.org
parent f0400014b3
commit 1920c6f2ca
  1. 14
      crypto/hpke/hpke.c
  2. 13
      crypto/hpke/internal.h
  3. 15
      include/openssl/ssl.h
  4. 4
      include/openssl/tls1.h
  5. 8
      ssl/internal.h
  6. 8
      ssl/ssl_lib.cc
  7. 166
      ssl/t1_lib.cc
  8. 9
      ssl/test/runner/common.go
  9. 97
      ssl/test/runner/handshake_messages.go
  10. 8
      ssl/test/runner/handshake_server.go
  11. 110
      ssl/test/runner/runner.go
  12. 4
      ssl/test/test_config.cc
  13. 1
      ssl/test/test_config.h

@ -125,7 +125,7 @@ static int hpke_extract_and_expand(const EVP_MD *hkdf_md, uint8_t *out_key,
return 1;
}
static const EVP_AEAD *hpke_get_aead(uint16_t aead_id) {
const EVP_AEAD *EVP_HPKE_get_aead(uint16_t aead_id) {
switch (aead_id) {
case EVP_HPKE_AEAD_AES_GCM_128:
return EVP_aead_aes_128_gcm();
@ -138,7 +138,7 @@ static const EVP_AEAD *hpke_get_aead(uint16_t aead_id) {
return NULL;
}
static const EVP_MD *hpke_get_kdf(uint16_t kdf_id) {
const EVP_MD *EVP_HPKE_get_hkdf_md(uint16_t kdf_id) {
switch (kdf_id) {
case EVP_HPKE_HKDF_SHA256:
return EVP_sha256();
@ -174,7 +174,7 @@ static int hpke_key_schedule(EVP_HPKE_CTX *hpke, uint8_t mode,
}
// Attempt to get an EVP_AEAD*.
const EVP_AEAD *aead = hpke_get_aead(hpke->aead_id);
const EVP_AEAD *aead = EVP_HPKE_get_aead(hpke->aead_id);
if (aead == NULL) {
return 0;
}
@ -351,7 +351,7 @@ int EVP_HPKE_CTX_setup_base_s_x25519_for_test(
hpke->is_sender = 1;
hpke->kdf_id = kdf_id;
hpke->aead_id = aead_id;
hpke->hkdf_md = hpke_get_kdf(kdf_id);
hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id);
if (hpke->hkdf_md == NULL) {
return 0;
}
@ -375,7 +375,7 @@ int EVP_HPKE_CTX_setup_base_r_x25519(
hpke->is_sender = 0;
hpke->kdf_id = kdf_id;
hpke->aead_id = aead_id;
hpke->hkdf_md = hpke_get_kdf(kdf_id);
hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id);
if (hpke->hkdf_md == NULL) {
return 0;
}
@ -415,7 +415,7 @@ int EVP_HPKE_CTX_setup_psk_s_x25519_for_test(
hpke->is_sender = 1;
hpke->kdf_id = kdf_id;
hpke->aead_id = aead_id;
hpke->hkdf_md = hpke_get_kdf(kdf_id);
hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id);
if (hpke->hkdf_md == NULL) {
return 0;
}
@ -440,7 +440,7 @@ int EVP_HPKE_CTX_setup_psk_r_x25519(
hpke->is_sender = 0;
hpke->kdf_id = kdf_id;
hpke->aead_id = aead_id;
hpke->hkdf_md = hpke_get_kdf(kdf_id);
hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id);
if (hpke->hkdf_md == NULL) {
return 0;
}

@ -18,6 +18,7 @@
#include <openssl/aead.h>
#include <openssl/base.h>
#include <openssl/curve25519.h>
#include <openssl/digest.h>
#if defined(__cplusplus)
extern "C" {
@ -77,8 +78,8 @@ OPENSSL_EXPORT void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx);
// In each of the following functions, |hpke| must have been initialized with
// |EVP_HPKE_CTX_init|. |kdf_id| selects the KDF for non-KEM HPKE operations and
// must be one of the |EVP_HPKE_HKDF_*| constants. |aead_id| selects the AEAD
// for the "open" and "seal" operations and must be one of the |EVP_HPKE_AEAD_*"
// constants."
// for the "open" and "seal" operations and must be one of the |EVP_HPKE_AEAD_*|
// constants.
// EVP_HPKE_CTX_setup_base_s_x25519 sets up |hpke| as a sender context that can
// encrypt for the private key corresponding to |peer_public_value| (the
@ -215,6 +216,14 @@ 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_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);
// EVP_HPKE_get_hkdf_md returns the hash function associated with |kdf_id|, or
// NULL if |kdf_id| is not a known KDF identifier that uses HKDF.
OPENSSL_EXPORT const EVP_MD *EVP_HPKE_get_hkdf_md(uint16_t kdf_id);
#if defined(__cplusplus)
} // extern C

@ -3553,6 +3553,21 @@ OPENSSL_EXPORT const char *SSL_early_data_reason_string(
enum ssl_early_data_reason_t reason);
// Encrypted Client Hello.
//
// ECH is a mechanism for encrypting the entire ClientHello message in TLS 1.3.
// This can prevent observers from seeing cleartext information about the
// connection, such as the server_name extension.
//
// ECH support in BoringSSL is still experimental and under development.
//
// See https://tools.ietf.org/html/draft-ietf-tls-esni-08.
// SSL_set_enable_ech_grease configures whether the client may send ECH GREASE
// as part of this connection.
OPENSSL_EXPORT void SSL_set_enable_ech_grease(SSL *ssl, int enable);
// Alerts.
//
// TLS uses alerts to signal error conditions. Alerts have a type (warning or

@ -238,6 +238,10 @@ extern "C" {
// extension number.
#define TLSEXT_TYPE_application_settings 17513
// ExtensionType value from draft-ietf-tls-esni-08. This is not an IANA defined
// extension number.
#define TLSEXT_TYPE_encrypted_client_hello 0xfe08
// ExtensionType value from RFC6962
#define TLSEXT_TYPE_certificate_timestamp 18

@ -1638,6 +1638,10 @@ struct SSL_HANDSHAKE {
// cookie is the value of the cookie received from the server, if any.
Array<uint8_t> cookie;
// ech_grease contains the bytes of the GREASE ECH extension that was sent in
// the first ClientHello.
Array<uint8_t> ech_grease;
// key_share_bytes is the value of the previously sent KeyShare extension by
// the client in TLS 1.3.
Array<uint8_t> key_share_bytes;
@ -2729,6 +2733,10 @@ struct SSL_CONFIG {
// verify_mode is a bitmask of |SSL_VERIFY_*| values.
uint8_t verify_mode = SSL_VERIFY_NONE;
// ech_grease_enabled controls whether ECH GREASE may be sent in the
// ClientHello.
bool ech_grease_enabled : 1;
// Enable signed certificate time stamps. Currently client only.
bool signed_cert_timestamps_enabled : 1;

@ -722,6 +722,7 @@ SSL *SSL_new(SSL_CTX *ctx) {
SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg)
: ssl(ssl_arg),
ech_grease_enabled(false),
signed_cert_timestamps_enabled(false),
ocsp_stapling_enabled(false),
channel_id_enabled(false),
@ -1466,6 +1467,13 @@ const char *SSL_error_description(int err) {
}
}
void SSL_set_enable_ech_grease(SSL *ssl, int enable) {
if (!ssl->config) {
return;
}
ssl->config->ech_grease_enabled = !!enable;
}
uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) {
ctx->options |= options;
return ctx->options;

@ -113,10 +113,13 @@
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <openssl/aead.h>
#include <openssl/bytestring.h>
#include <openssl/chacha.h>
#include <openssl/curve25519.h>
#include <openssl/digest.h>
#include <openssl/err.h>
#include <openssl/evp.h>
@ -125,6 +128,7 @@
#include <openssl/nid.h>
#include <openssl/rand.h>
#include "../crypto/hpke/internal.h"
#include "../crypto/internal.h"
#include "internal.h"
@ -587,6 +591,160 @@ static bool ext_sni_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
}
// Encrypted Client Hello (ECH)
//
// https://tools.ietf.org/html/draft-ietf-tls-esni-08
// random_size returns a random value between |min| and |max|, inclusive.
static size_t random_size(size_t min, size_t max) {
assert(min < max);
size_t value;
RAND_bytes(reinterpret_cast<uint8_t *>(&value), sizeof(value));
return value % (max - min + 1) + min;
}
static bool ext_ech_add_clienthello_grease(SSL_HANDSHAKE *hs, CBB *out) {
// If we are responding to the server's HelloRetryRequest, we repeat the bytes
// of the first ECH GREASE extension.
if (hs->ssl->s3->used_hello_retry_request) {
CBB ech_body;
if (!CBB_add_u16(out, TLSEXT_TYPE_encrypted_client_hello) ||
!CBB_add_u16_length_prefixed(out, &ech_body) ||
!CBB_add_bytes(&ech_body, hs->ech_grease.data(),
hs->ech_grease.size()) ||
!CBB_flush(out)) {
return false;
}
return true;
}
constexpr uint16_t kdf_id = EVP_HPKE_HKDF_SHA256;
const EVP_MD *kdf = EVP_HPKE_get_hkdf_md(kdf_id);
assert(kdf != nullptr);
const uint16_t aead_id = EVP_has_aes_hardware()
? EVP_HPKE_AEAD_AES_GCM_128
: EVP_HPKE_AEAD_CHACHA20POLY1305;
const EVP_AEAD *aead = EVP_HPKE_get_aead(aead_id);
assert(aead != nullptr);
uint8_t ech_config_id_buf[EVP_MAX_MD_SIZE];
Span<uint8_t> ech_config_id(ech_config_id_buf, EVP_MD_size(kdf));
RAND_bytes(ech_config_id.data(), ech_config_id.size());
uint8_t ech_enc[X25519_PUBLIC_VALUE_LEN];
uint8_t private_key_unused[X25519_PRIVATE_KEY_LEN];
X25519_keypair(ech_enc, private_key_unused);
// To determine a plausible length for the payload, we first estimate the size
// of a typical EncodedClientHelloInner, with an expected use of
// outer_extensions. To limit the size, we only consider initial ClientHellos
// that do not offer resumption.
//
// Field/Extension Size
// ---------------------------------------------------------------------
// version 2
// random 32
// legacy_session_id 1
// - Has a U8 length prefix, but body is
// always empty string in inner CH.
// cipher_suites 2 (length prefix)
// - Only includes TLS 1.3 ciphers (3). 6
// - Maybe also include a GREASE suite. 2
// legacy_compression_methods 2 (length prefix)
// - Always has "null" compression method. 1
// extensions: 2 (length prefix)
// - encrypted_client_hello (empty). 4 (id + length prefix)
// - supported_versions. 4 (id + length prefix)
// - U8 length prefix 1
// - U16 protocol version (TLS 1.3) 2
// - outer_extensions. 4 (id + length prefix)
// - U8 length prefix 1
// - N extension IDs (2 bytes each):
// - key_share 2
// - sigalgs 2
// - sct 2
// - alpn 2
// - supported_groups. 2
// - status_request. 2
// - psk_key_exchange_modes. 2
// - compress_certificate. 2
//
// The server_name extension has an overhead of 9 bytes, plus up to an
// estimated 100 bytes of hostname. Rounding up to a multiple of 32 yields a
// range of 96 to 192. Note that this estimate does not fully capture
// optional extensions like GREASE, but the rounding gives some leeway.
uint8_t payload[EVP_AEAD_MAX_OVERHEAD + 192];
const size_t payload_len =
EVP_AEAD_max_overhead(aead) + 32 * random_size(96 / 32, 192 / 32);
assert(payload_len <= sizeof(payload));
RAND_bytes(payload, payload_len);
// Inside the TLS extension contents, write a serialized ClientEncryptedCH.
CBB ech_body, config_id_cbb, enc_cbb, payload_cbb;
if (!CBB_add_u16(out, TLSEXT_TYPE_encrypted_client_hello) ||
!CBB_add_u16_length_prefixed(out, &ech_body) ||
!CBB_add_u16(&ech_body, kdf_id) || //
!CBB_add_u16(&ech_body, aead_id) ||
!CBB_add_u8_length_prefixed(&ech_body, &config_id_cbb) ||
!CBB_add_bytes(&config_id_cbb, ech_config_id.data(),
ech_config_id.size()) ||
!CBB_add_u16_length_prefixed(&ech_body, &enc_cbb) ||
!CBB_add_bytes(&enc_cbb, ech_enc, OPENSSL_ARRAY_SIZE(ech_enc)) ||
!CBB_add_u16_length_prefixed(&ech_body, &payload_cbb) ||
!CBB_add_bytes(&payload_cbb, payload, payload_len) || //
!CBB_flush(&ech_body)) {
return false;
}
// Save the bytes of the newly-generated extension in case the server sends
// a HelloRetryRequest.
if (!hs->ech_grease.CopyFrom(
MakeConstSpan(CBB_data(&ech_body), CBB_len(&ech_body)))) {
return false;
}
return CBB_flush(out);
}
static bool ext_ech_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
if (hs->max_version < TLS1_3_VERSION) {
return true;
}
if (hs->config->ech_grease_enabled) {
return ext_ech_add_clienthello_grease(hs, out);
}
// Nothing to do, since we don't yet implement the non-GREASE parts of ECH.
return true;
}
static bool ext_ech_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
CBS *contents) {
if (contents == NULL) {
return true;
}
// If the client only sent GREASE, we must check the extension syntactically.
CBS ech_configs;
if (!CBS_get_u16_length_prefixed(contents, &ech_configs) ||
CBS_len(&ech_configs) == 0 || //
CBS_len(contents) > 0) {
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}
while (CBS_len(&ech_configs) > 0) {
// Do a top-level parse of the ECHConfig, stopping before ECHConfigContents.
uint16_t version;
CBS ech_config_contents;
if (!CBS_get_u16(&ech_configs, &version) ||
!CBS_get_u16_length_prefixed(&ech_configs, &ech_config_contents)) {
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}
}
return true;
}
// Renegotiation indication.
//
// https://tools.ietf.org/html/rfc5746
@ -2970,6 +3128,14 @@ static const struct tls_extension kExtensions[] = {
ext_sni_parse_clienthello,
ext_sni_add_serverhello,
},
{
TLSEXT_TYPE_encrypted_client_hello,
NULL,
ext_ech_add_clienthello,
ext_ech_parse_serverhello,
ignore_parse_clienthello,
dont_add_serverhello,
},
{
TLSEXT_TYPE_extended_master_secret,
NULL,

@ -127,6 +127,7 @@ const (
extensionChannelID uint16 = 30032 // not IANA assigned
extensionDelegatedCredentials uint16 = 0x22 // draft-ietf-tls-subcerts-06
extensionDuplicate uint16 = 0xffff // not IANA assigned
extensionEncryptedClientHello uint16 = 0xfe08 // not IANA assigned
)
// TLS signaling cipher suite values
@ -794,6 +795,14 @@ type ProtocolBugs struct {
// must specify in the server_name extension.
ExpectServerName string
// ExpectClientECH causes the server to expect the peer to send an
// encrypted_client_hello extension containing a ClientECH structure.
ExpectClientECH bool
// SendECHRetryConfigs, if not empty, contains the ECH server's serialized
// retry configs.
SendECHRetryConfigs []byte
// SwapNPNAndALPN switches the relative order between NPN and ALPN in
// both ClientHello and ServerHello.
SwapNPNAndALPN bool

@ -249,6 +249,48 @@ type pskIdentity struct {
obfuscatedTicketAge uint32
}
type HPKECipherSuite struct {
KDF uint16
AEAD uint16
}
type ECHConfig struct {
PublicName string
PublicKey []byte
KEM uint16
CipherSuites []HPKECipherSuite
MaxNameLen uint16
}
func MarshalECHConfig(e *ECHConfig) []byte {
bb := newByteBuilder()
// ECHConfig's wire format reuses the encrypted_client_hello extension
// codepoint as a version identifier.
bb.addU16(extensionEncryptedClientHello)
contents := bb.addU16LengthPrefixed()
contents.addU16LengthPrefixed().addBytes([]byte(e.PublicName))
contents.addU16LengthPrefixed().addBytes(e.PublicKey)
contents.addU16(e.KEM)
cipherSuites := contents.addU16LengthPrefixed()
for _, suite := range e.CipherSuites {
cipherSuites.addU16(suite.KDF)
cipherSuites.addU16(suite.AEAD)
}
contents.addU16(e.MaxNameLen)
contents.addU16(0) // Empty extensions field
return bb.finish()
}
// The contents of a CH "encrypted_client_hello" extension.
// https://tools.ietf.org/html/draft-ietf-tls-esni-08
type clientECH struct {
hpkeKDF uint16
hpkeAEAD uint16
configID []byte
enc []byte
payload []byte
}
type clientHelloMsg struct {
raw []byte
isDTLS bool
@ -260,6 +302,7 @@ type clientHelloMsg struct {
compressionMethods []uint8
nextProtoNeg bool
serverName string
clientECH *clientECH
ocspStapling bool
supportedCurves []CurveID
supportedPoints []uint8
@ -378,6 +421,20 @@ func (m *clientHelloMsg) marshal() []byte {
body: serverNameList.finish(),
})
}
if m.clientECH != nil {
// https://tools.ietf.org/html/draft-ietf-tls-esni-08
body := newByteBuilder()
body.addU16(m.clientECH.hpkeKDF)
body.addU16(m.clientECH.hpkeAEAD)
body.addU8LengthPrefixed().addBytes(m.clientECH.configID)
body.addU16LengthPrefixed().addBytes(m.clientECH.enc)
body.addU16LengthPrefixed().addBytes(m.clientECH.payload)
extensions = append(extensions, extension{
id: extensionEncryptedClientHello,
body: body.finish(),
})
}
if m.ocspStapling {
certificateStatusRequest := newByteBuilder()
// RFC 4366, section 3.6
@ -778,6 +835,19 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
m.serverName = string(name)
}
}
case extensionEncryptedClientHello:
var ech clientECH
if !body.readU16(&ech.hpkeKDF) ||
!body.readU16(&ech.hpkeAEAD) ||
!body.readU8LengthPrefixedBytes(&ech.configID) ||
!body.readU16LengthPrefixedBytes(&ech.enc) ||
len(ech.enc) == 0 ||
!body.readU16LengthPrefixedBytes(&ech.payload) ||
len(ech.payload) == 0 ||
len(body) > 0 {
return false
}
m.clientECH = &ech
case extensionNextProtoNeg:
if len(body) != 0 {
return false
@ -1260,6 +1330,7 @@ type serverExtensions struct {
serverNameAck bool
applicationSettings []byte
hasApplicationSettings bool
echRetryConfigs []byte
}
func (m *serverExtensions) marshal(extensions *byteBuilder) {
@ -1398,6 +1469,12 @@ func (m *serverExtensions) marshal(extensions *byteBuilder) {
extensions.addU16(extensionApplicationSettings)
extensions.addU16LengthPrefixed().addBytes(m.applicationSettings)
}
if len(m.echRetryConfigs) > 0 {
extensions.addU16(extensionEncryptedClientHello)
body := extensions.addU16LengthPrefixed()
echConfigs := body.addU16LengthPrefixed()
echConfigs.addBytes(m.echRetryConfigs)
}
}
func (m *serverExtensions) unmarshal(data byteReader, version uint16) bool {
@ -1509,6 +1586,26 @@ func (m *serverExtensions) unmarshal(data byteReader, version uint16) bool {
case extensionApplicationSettings:
m.hasApplicationSettings = true
m.applicationSettings = body
case extensionEncryptedClientHello:
var echConfigs byteReader
if !body.readU16LengthPrefixed(&echConfigs) {
return false
}
for len(echConfigs) > 0 {
// Validate the ECHConfig with a top-level parse.
echConfigReader := echConfigs
var version uint16
var contents byteReader
if !echConfigReader.readU16(&version) ||
!echConfigReader.readU16LengthPrefixed(&contents) {
return false
}
m.echRetryConfigs = contents
}
if len(body) > 0 {
return false
}
default:
// Unknown extensions are illegal from the server.
return false

@ -403,6 +403,14 @@ func (hs *serverHandshakeState) doTLS13Handshake() error {
return err
}
if config.Bugs.ExpectClientECH && hs.clientHello.clientECH == nil {
return errors.New("tls: expected client to send ClientECH")
}
if hs.clientHello.clientECH != nil && len(config.Bugs.SendECHRetryConfigs) > 0 {
encryptedExtensions.extensions.echRetryConfigs = config.Bugs.SendECHRetryConfigs
}
// Select the cipher suite.
var preferenceList, supportedList []uint16
if config.PreferServerCipherSuites {

@ -46,6 +46,7 @@ import (
"syscall"
"time"
"boringssl.googlesource.com/boringssl/ssl/test/runner/hpke"
"boringssl.googlesource.com/boringssl/util/testresult"
)
@ -16139,6 +16140,114 @@ func addDelegatedCredentialTests() {
})
}
func addEncryptedClientHelloTests() {
// Test ECH GREASE.
// Test the client's behavior when the server ignores ECH GREASE.
testCases = append(testCases, testCase{
testType: clientTest,
name: "ECH-GREASE-Client-TLS13",
config: Config{
MinVersion: VersionTLS13,
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
ExpectClientECH: true,
},
},
flags: []string{"-enable-ech-grease"},
})
// Test the client's ECH GREASE behavior when responding to server's
// HelloRetryRequest. This test implicitly checks that the first and second
// ClientHello messages have identical ECH extensions.
testCases = append(testCases, testCase{
testType: clientTest,
name: "ECH-GREASE-Client-TLS13-HelloRetryRequest",
config: Config{
MaxVersion: VersionTLS13,
MinVersion: VersionTLS13,
// P-384 requires a HelloRetryRequest against BoringSSL's default
// configuration. Assert this with ExpectMissingKeyShare.
CurvePreferences: []CurveID{CurveP384},
Bugs: ProtocolBugs{
ExpectMissingKeyShare: true,
ExpectClientECH: true,
},
},
flags: []string{"-enable-ech-grease", "-expect-hrr"},
})
retryConfigValid := ECHConfig{
PublicName: "example.com",
// A real X25519 public key obtained from hpke.GenerateKeyPair().
PublicKey: []byte{
0x23, 0x1a, 0x96, 0x53, 0x52, 0x81, 0x1d, 0x7a,
0x36, 0x76, 0xaa, 0x5e, 0xad, 0xdb, 0x66, 0x1c,
0x92, 0x45, 0x8a, 0x60, 0xc7, 0x81, 0x93, 0xb0,
0x47, 0x7b, 0x54, 0x18, 0x6b, 0x9a, 0x1d, 0x6d},
KEM: hpke.X25519WithHKDFSHA256,
CipherSuites: []HPKECipherSuite{
{
KDF: hpke.HKDFSHA256,
AEAD: hpke.AES256GCM,
},
},
MaxNameLen: 42,
}
retryConfigUnsupportedVersion := []byte{
// version
0xba, 0xdd,
// length
0x00, 0x05,
// contents
0x05, 0x04, 0x03, 0x02, 0x01,
}
var validAndInvalidConfigs []byte
validAndInvalidConfigs = append(validAndInvalidConfigs, MarshalECHConfig(&retryConfigValid)...)
validAndInvalidConfigs = append(validAndInvalidConfigs, retryConfigUnsupportedVersion...)
// Test that the client accepts a well-formed encrypted_client_hello
// extension in response to ECH GREASE. The response includes one ECHConfig
// with a supported version and one with an unsupported version.
testCases = append(testCases, testCase{
testType: clientTest,
name: "ECH-GREASE-Client-TLS13-Retry-Configs",
config: Config{
MinVersion: VersionTLS13,
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
ExpectClientECH: true,
// Include an additional well-formed ECHConfig with an invalid
// version. This ensures the client can iterate over the retry
// configs.
SendECHRetryConfigs: validAndInvalidConfigs,
},
},
flags: []string{"-enable-ech-grease"},
})
// Test that the client aborts with a decode_error alert when it receives a
// syntactically-invalid encrypted_client_hello extension from the server.
testCases = append(testCases, testCase{
testType: clientTest,
name: "ECH-GREASE-Client-TLS13-Invalid-Retry-Configs",
config: Config{
MinVersion: VersionTLS13,
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
ExpectClientECH: true,
SendECHRetryConfigs: []byte{0xba, 0xdd, 0xec, 0xcc},
},
},
flags: []string{"-enable-ech-grease"},
shouldFail: true,
expectedLocalError: "remote error: error decoding message",
expectedError: ":ERROR_PARSING_EXTENSION:",
})
}
func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
defer wg.Done()
@ -16316,6 +16425,7 @@ func main() {
addCertCompressionTests()
addJDK11WorkaroundTests()
addDelegatedCredentialTests()
addEncryptedClientHelloTests()
testCases = append(testCases, convertToSplitHandshakeTests(testCases)...)

@ -55,6 +55,7 @@ const Flag<bool> kBoolFlags[] = {
{"-dtls", &TestConfig::is_dtls},
{"-quic", &TestConfig::is_quic},
{"-fallback-scsv", &TestConfig::fallback_scsv},
{"-enable-ech-grease", &TestConfig::enable_ech_grease},
{"-require-any-client-certificate",
&TestConfig::require_any_client_certificate},
{"-false-start", &TestConfig::false_start},
@ -1576,6 +1577,9 @@ bssl::UniquePtr<SSL> TestConfig::NewSSL(
if (!expect_channel_id.empty() || enable_channel_id) {
SSL_set_tls_channel_id_enabled(ssl.get(), 1);
}
if (enable_ech_grease) {
SSL_set_enable_ech_grease(ssl.get(), 1);
}
if (!send_channel_id.empty()) {
SSL_set_tls_channel_id_enabled(ssl.get(), 1);
if (!async) {

@ -39,6 +39,7 @@ struct TestConfig {
std::string key_file;
std::string cert_file;
std::string expect_server_name;
bool enable_ech_grease = false;
std::string expect_certificate_types;
bool require_any_client_certificate = false;
std::string advertise_npn;

Loading…
Cancel
Save