Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 98aaf4eda3 | |||
| b641b6aaab |
@@ -619,6 +619,8 @@ OPENSSL_EXPORT int DTLSv1_handle_timeout(SSL *ssl);
|
||||
#define DTLS1_VERSION 0xfeff
|
||||
#define DTLS1_2_VERSION 0xfefd
|
||||
|
||||
#define ESNI_VERSION 0xff03
|
||||
|
||||
// SSL_CTX_set_min_proto_version sets the minimum protocol version for |ctx| to
|
||||
// |version|. If |version| is zero, the default minimum version is used. It
|
||||
// returns one on success and zero if |version| is invalid.
|
||||
@@ -3906,6 +3908,29 @@ OPENSSL_EXPORT int SSL_is_tls13_downgrade(const SSL *ssl);
|
||||
OPENSSL_EXPORT void SSL_set_jdk11_workaround(SSL *ssl, int enable);
|
||||
|
||||
|
||||
// ESNI functions.
|
||||
|
||||
// SSL_set_enable_esni configures whether to use ESNI as part of this
|
||||
// connection.
|
||||
OPENSSL_EXPORT void SSL_set_enable_esni(SSL *ssl, int enable);
|
||||
|
||||
// SSL_set_esni_keys sets the ESNIKeys structure that should be used for
|
||||
// connections to the server.
|
||||
OPENSSL_EXPORT int SSL_set_esni_keys(SSL *ssl, const uint8_t *key_struct,
|
||||
size_t key_len);
|
||||
|
||||
// SSL_set_esni_private_key sets the keypair that the server should use to decrypt ESNI.
|
||||
//
|
||||
// TODO: Support multiple keypairs.
|
||||
OPENSSL_EXPORT int SSL_set_esni_private_key(SSL *ssl, const uint8_t *pub,
|
||||
size_t pub_len, const uint8_t *priv,
|
||||
size_t priv_len);
|
||||
|
||||
// SSL_get_retry_keys returns the ESNIKeys structure that was returned by the
|
||||
// server if ESNI was rejected or a retry was requested.
|
||||
OPENSSL_EXPORT void SSL_get_retry_keys(SSL *ssl);
|
||||
|
||||
|
||||
// Deprecated functions.
|
||||
|
||||
// SSL_library_init calls |CRYPTO_library_init| and returns one.
|
||||
|
||||
@@ -235,6 +235,9 @@ extern "C" {
|
||||
// extension number.
|
||||
#define TLSEXT_TYPE_delegated_credential 0xff02
|
||||
|
||||
// ExtensionType value from draft-ietf-tls-esni.
|
||||
#define TLSEXT_TYPE_encrypted_server_name 0xffce
|
||||
|
||||
// ExtensionType value from RFC6962
|
||||
#define TLSEXT_TYPE_certificate_timestamp 18
|
||||
|
||||
|
||||
+34
-9
@@ -526,6 +526,40 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) {
|
||||
return ssl_hs_handoff;
|
||||
}
|
||||
|
||||
hs->client_version = client_hello.version;
|
||||
if (client_hello.random_len != SSL3_RANDOM_SIZE) {
|
||||
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
||||
return ssl_hs_error;
|
||||
}
|
||||
OPENSSL_memcpy(ssl->s3->client_random, client_hello.random,
|
||||
client_hello.random_len);
|
||||
|
||||
CBS encrypted_sni;
|
||||
if (ssl_client_hello_get_extension(&client_hello, &encrypted_sni,
|
||||
TLSEXT_TYPE_encrypted_server_name)) {
|
||||
CBS key_share, kse_bytes;
|
||||
if (!ssl_client_hello_get_extension(&client_hello, &key_share,
|
||||
TLSEXT_TYPE_key_share)) {
|
||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
|
||||
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
|
||||
return ssl_hs_error;
|
||||
}
|
||||
|
||||
if (!CBS_get_u16_length_prefixed(&key_share, &kse_bytes) ||
|
||||
!hs->key_share_bytes.CopyFrom(
|
||||
MakeConstSpan(CBS_data(&kse_bytes), CBS_len(&kse_bytes)))) {
|
||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
|
||||
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
|
||||
return ssl_hs_error;
|
||||
}
|
||||
|
||||
uint8_t alert = SSL_AD_DECODE_ERROR;
|
||||
if (!ssl_ext_encrypted_server_name_parse_clienthello(hs, &alert, &encrypted_sni)) {
|
||||
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
|
||||
return ssl_hs_error;
|
||||
}
|
||||
}
|
||||
|
||||
// Run the early callback.
|
||||
if (ssl->ctx->select_certificate_cb != NULL) {
|
||||
switch (ssl->ctx->select_certificate_cb(&client_hello)) {
|
||||
@@ -559,14 +593,6 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) {
|
||||
return ssl_hs_error;
|
||||
}
|
||||
|
||||
hs->client_version = client_hello.version;
|
||||
if (client_hello.random_len != SSL3_RANDOM_SIZE) {
|
||||
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
||||
return ssl_hs_error;
|
||||
}
|
||||
OPENSSL_memcpy(ssl->s3->client_random, client_hello.random,
|
||||
client_hello.random_len);
|
||||
|
||||
// Only null compression is supported. TLS 1.3 further requires the peer
|
||||
// advertise no other compression.
|
||||
if (OPENSSL_memchr(client_hello.compression_methods, 0,
|
||||
@@ -577,7 +603,6 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) {
|
||||
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
|
||||
return ssl_hs_error;
|
||||
}
|
||||
|
||||
// TLS extensions.
|
||||
if (!ssl_parse_clienthello_tlsext(hs, &client_hello)) {
|
||||
OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
|
||||
|
||||
@@ -1315,6 +1315,22 @@ bool tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
|
||||
const SSLMessage &msg, CBS *binders);
|
||||
|
||||
|
||||
// ESNI
|
||||
|
||||
enum ssl_esni_state_t {
|
||||
ssl_esni_unknown = 0,
|
||||
ssl_esni_attempted = 1,
|
||||
ssl_esni_verified = 2,
|
||||
ssl_esni_retry_required = 3,
|
||||
};
|
||||
|
||||
bool tls13_derive_esni_secrets(SSL_HANDSHAKE *hs, Array<uint8_t> shared_secret);
|
||||
|
||||
bool ssl_ext_encrypted_server_name_parse_clienthello(SSL_HANDSHAKE *hs,
|
||||
uint8_t *out_alert,
|
||||
CBS *contents);
|
||||
|
||||
|
||||
// Handshake functions.
|
||||
|
||||
enum ssl_hs_wait_t {
|
||||
@@ -1562,6 +1578,18 @@ struct SSL_HANDSHAKE {
|
||||
// key_block is the record-layer key block for TLS 1.2 and earlier.
|
||||
Array<uint8_t> key_block;
|
||||
|
||||
// esni_state is the state of ESNI for this connection.
|
||||
enum ssl_esni_state_t esni_state;
|
||||
|
||||
// esni_nonce is the nonce used for ESNI in this connection.
|
||||
uint8_t esni_nonce[16];
|
||||
|
||||
uint8_t esni_iv[EVP_AEAD_MAX_NONCE_LENGTH];
|
||||
size_t esni_iv_len;
|
||||
|
||||
// esni_aead_ctx is the AEAD CTX to use with ESNI.
|
||||
ScopedEVP_AEAD_CTX esni_aead_ctx;
|
||||
|
||||
// scts_requested is true if the SCT extension is in the ClientHello.
|
||||
bool scts_requested : 1;
|
||||
|
||||
@@ -2549,6 +2577,14 @@ struct SSL_CONFIG {
|
||||
// verify_mode is a bitmask of |SSL_VERIFY_*| values.
|
||||
uint8_t verify_mode = SSL_VERIFY_NONE;
|
||||
|
||||
CBS esni_public_name;
|
||||
uint16_t esni_group = 0;
|
||||
const SSL_CIPHER *esni_cipher = nullptr;
|
||||
Array<uint8_t> esni_server_keyshare;
|
||||
Array<uint8_t> esni_record_digest;
|
||||
uint16_t esni_padded_length = 0;
|
||||
Array<uint8_t> esni_private;
|
||||
|
||||
// Enable signed certificate time stamps. Currently client only.
|
||||
bool signed_cert_timestamps_enabled : 1;
|
||||
|
||||
@@ -2588,6 +2624,9 @@ struct SSL_CONFIG {
|
||||
// jdk11_workaround is whether to disable TLS 1.3 for JDK 11 clients, as a
|
||||
// workaround for https://bugs.openjdk.java.net/browse/JDK-8211806.
|
||||
bool jdk11_workaround : 1;
|
||||
|
||||
// enable_esni is whether the connection should attempt to use ESNI.
|
||||
bool enable_esni : 1;
|
||||
};
|
||||
|
||||
// From RFC 8446, used in determining PSK modes.
|
||||
|
||||
+98
-1
@@ -735,7 +735,8 @@ SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg)
|
||||
handoff(false),
|
||||
shed_handshake_config(false),
|
||||
ignore_tls13_downgrade(false),
|
||||
jdk11_workaround(false) {
|
||||
jdk11_workaround(false),
|
||||
enable_esni(false) {
|
||||
assert(ssl);
|
||||
}
|
||||
|
||||
@@ -2849,6 +2850,102 @@ void SSL_set_jdk11_workaround(SSL *ssl, int enable) {
|
||||
ssl->config->jdk11_workaround = !!enable;
|
||||
}
|
||||
|
||||
void SSL_set_enable_esni(SSL *ssl, int enable) {
|
||||
if (!ssl->config) {
|
||||
return;
|
||||
}
|
||||
ssl->config->enable_esni = !!enable;
|
||||
}
|
||||
|
||||
int SSL_set_esni_keys(SSL *ssl, const uint8_t *key_struct, size_t key_len) {
|
||||
if (!ssl->config->enable_esni) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CBS esni_keys, public_name, keys, cipher_suites, extensions;
|
||||
uint16_t version;
|
||||
CBS_init(&esni_keys, key_struct, key_len);
|
||||
if (!CBS_get_u16(&esni_keys, &version) ||
|
||||
version != ESNI_VERSION ||
|
||||
!CBS_get_u16_length_prefixed(&esni_keys,
|
||||
&ssl->config->esni_public_name) ||
|
||||
!CBS_get_u16_length_prefixed(&esni_keys, &keys) ||
|
||||
!CBS_get_u16_length_prefixed(&esni_keys, &cipher_suites) ||
|
||||
!CBS_get_u16(&esni_keys, &ssl->config->esni_padded_length) ||
|
||||
!CBS_get_u16_length_prefixed(&esni_keys, &extensions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(svaldez): Handle extensions.
|
||||
|
||||
static const uint16_t kDefaultGroups[] = {
|
||||
SSL_CURVE_X25519,
|
||||
SSL_CURVE_SECP256R1,
|
||||
SSL_CURVE_SECP384R1,
|
||||
};
|
||||
Span<const uint16_t> supp = Span<const uint16_t>(kDefaultGroups);
|
||||
if (!ssl->config->supported_group_list.empty()) {
|
||||
supp = ssl->config->supported_group_list;
|
||||
}
|
||||
|
||||
CBS esni_keyshare_bytes;
|
||||
for (uint16_t supp_group : supp) {
|
||||
CBS copy = keys;
|
||||
uint16_t group;
|
||||
while (CBS_len(©) > 0) {
|
||||
if (!CBS_get_u16(©, &group) ||
|
||||
!CBS_get_u16_length_prefixed(©, &esni_keyshare_bytes)) {
|
||||
return false;
|
||||
}
|
||||
if (group == supp_group) {
|
||||
ssl->config->esni_group = supp_group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ssl->config->esni_group != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssl->config->esni_group == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ssl->config->esni_server_keyshare.CopyFrom(esni_keyshare_bytes);
|
||||
ssl->config->esni_cipher = ssl_choose_tls13_cipher(
|
||||
cipher_suites, TLS1_3_VERSION, ssl->config->esni_group);
|
||||
if (!ssl->config->esni_cipher) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const EVP_MD *digest =
|
||||
ssl_get_handshake_digest(TLS1_3_VERSION, ssl->config->esni_cipher);
|
||||
|
||||
uint8_t esni_digest[EVP_MAX_MD_SIZE];
|
||||
unsigned esni_digest_len;
|
||||
if (!EVP_Digest(key_struct, key_len, esni_digest, &esni_digest_len, digest,
|
||||
NULL) ||
|
||||
!ssl->config->esni_record_digest.CopyFrom(
|
||||
Span<uint8_t>(esni_digest, esni_digest_len))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(svaldez): Support multiple ESNIKeys structs on the server.
|
||||
int SSL_set_esni_private_key(SSL *ssl, const uint8_t *pub, size_t pub_len,
|
||||
const uint8_t *priv, size_t priv_len) {
|
||||
if (!ssl->config->enable_esni) {
|
||||
return false;
|
||||
}
|
||||
ssl->config->esni_server_keyshare.CopyFrom(Span<const uint8_t>(pub, pub_len));
|
||||
ssl->config->esni_private.CopyFrom(Span<const uint8_t>(priv, priv_len));
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Get Retry Keys.
|
||||
|
||||
int SSL_clear(SSL *ssl) {
|
||||
if (!ssl->config) {
|
||||
return 0; // SSL_clear may not be used after shedding config.
|
||||
|
||||
@@ -4080,6 +4080,75 @@ TEST(SSLTest, CertCompression) {
|
||||
EXPECT_TRUE(SSL_get_app_data(server.get()) == XORCompressFunc);
|
||||
}
|
||||
|
||||
TEST(SSLTest, ESNI) {
|
||||
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
|
||||
bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
|
||||
ASSERT_TRUE(client_ctx);
|
||||
ASSERT_TRUE(server_ctx);
|
||||
|
||||
bssl::UniquePtr<X509> cert = GetTestCertificate();
|
||||
bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
|
||||
ASSERT_TRUE(cert);
|
||||
ASSERT_TRUE(key);
|
||||
ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
|
||||
ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
|
||||
|
||||
ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION));
|
||||
ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION));
|
||||
|
||||
bssl::UniquePtr<SSL> client(SSL_new(client_ctx.get())),
|
||||
server(SSL_new(server_ctx.get()));
|
||||
ASSERT_TRUE(client);
|
||||
ASSERT_TRUE(server);
|
||||
SSL_set_connect_state(client.get());
|
||||
SSL_set_accept_state(server.get());
|
||||
|
||||
ASSERT_TRUE(SSL_set_tlsext_host_name(client.get(), "esni-test.com"));
|
||||
SSL_set_enable_esni(client.get(), true);
|
||||
SSL_set_enable_esni(server.get(), true);
|
||||
|
||||
UniquePtr<SSLKeyShare> keyshare = SSLKeyShare::Create(SSL_CURVE_X25519);
|
||||
|
||||
CBB key_struct, public_name, keys, kse_bytes, kse_bytes_c, cipher_suites;
|
||||
if (!CBB_init(&key_struct, 0) ||
|
||||
!CBB_add_u16(&key_struct, ESNI_VERSION) ||
|
||||
!CBB_add_u16_length_prefixed(&key_struct, &public_name) ||
|
||||
!CBB_add_bytes(&public_name, (const uint8_t *)"pn-test.com",
|
||||
strlen("pn-test.com")) ||
|
||||
!CBB_add_u16_length_prefixed(&key_struct, &keys) ||
|
||||
!CBB_add_u16(&keys, SSL_CURVE_X25519) ||
|
||||
!CBB_add_u16_length_prefixed(&keys, &kse_bytes)) {
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
|
||||
kse_bytes_c = kse_bytes;
|
||||
if (!keyshare->Offer(&kse_bytes) ||
|
||||
!CBB_add_u16_length_prefixed(&key_struct, &cipher_suites) ||
|
||||
!CBB_add_u16(&cipher_suites, 0x1303) ||
|
||||
!CBB_add_u16(&key_struct, 32) ||
|
||||
!CBB_add_u16(&key_struct, 0) ||
|
||||
!CBB_flush(&key_struct)) {
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
ASSERT_TRUE(SSL_set_esni_keys(client.get(), CBB_data(&key_struct),
|
||||
CBB_len(&key_struct)));
|
||||
CBB private_key;
|
||||
ASSERT_TRUE(CBB_init(&private_key, 0));
|
||||
ASSERT_TRUE(keyshare->Serialize(&private_key));
|
||||
ASSERT_TRUE(SSL_set_esni_private_key(
|
||||
server.get(), CBB_data(&kse_bytes_c), CBB_len(&kse_bytes_c) - 8,
|
||||
CBB_data(&private_key), CBB_len(&private_key)));
|
||||
|
||||
BIO *bio1, *bio2;
|
||||
ASSERT_TRUE(BIO_new_bio_pair(&bio1, 0, &bio2, 0));
|
||||
// SSL_set_bio takes ownership.
|
||||
SSL_set_bio(client.get(), bio1, bio1);
|
||||
SSL_set_bio(server.get(), bio2, bio2);
|
||||
|
||||
ASSERT_TRUE(CompleteHandshakes(client.get(), server.get()));
|
||||
printf("%s\n", SSL_get_servername(server.get(), TLSEXT_NAMETYPE_host_name));
|
||||
}
|
||||
|
||||
void MoveBIOs(SSL *dest, SSL *src) {
|
||||
BIO *rbio = SSL_get_rbio(src);
|
||||
BIO_up_ref(rbio);
|
||||
|
||||
+221
-5
@@ -614,14 +614,23 @@ static bool ext_sni_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
|
||||
!CBB_add_u16_length_prefixed(out, &contents) ||
|
||||
!CBB_add_u16_length_prefixed(&contents, &server_name_list) ||
|
||||
!CBB_add_u8(&server_name_list, TLSEXT_NAMETYPE_host_name) ||
|
||||
!CBB_add_u16_length_prefixed(&server_name_list, &name) ||
|
||||
!CBB_add_bytes(&name, (const uint8_t *)ssl->hostname.get(),
|
||||
strlen(ssl->hostname.get())) ||
|
||||
!CBB_flush(out)) {
|
||||
!CBB_add_u16_length_prefixed(&server_name_list, &name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (hs->config->enable_esni && hs->config->esni_group != 0) {
|
||||
if (!CBB_add_bytes(&name, CBS_data(&ssl->config->esni_public_name),
|
||||
CBS_len(&ssl->config->esni_public_name))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!CBB_add_bytes(&name, (const uint8_t *)ssl->hostname.get(),
|
||||
strlen(ssl->hostname.get()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return CBB_flush(out);
|
||||
}
|
||||
|
||||
static bool ext_sni_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
|
||||
@@ -2947,6 +2956,205 @@ static bool ext_pq_experiment_signal_add_serverhello(SSL_HANDSHAKE *hs,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Encrypted Server Name
|
||||
//
|
||||
// https://tools.ietf.org/html/draft-ietf-tls-esni
|
||||
// TODO: Move this out of the callbacks to do early.
|
||||
static bool ext_encrypted_server_name_add_clienthello(SSL_HANDSHAKE *hs,
|
||||
CBB *out) {
|
||||
SSL *const ssl = hs->ssl;
|
||||
if (!hs->config->enable_esni || SSL_is_dtls(ssl) ||
|
||||
hs->config->esni_group == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
hs->esni_state = ssl_esni_attempted;
|
||||
|
||||
CBB contents, kse_bytes, key_exchange, record_digest, esni;
|
||||
if (!CBB_add_u16(out, TLSEXT_TYPE_encrypted_server_name) ||
|
||||
!CBB_add_u16_length_prefixed(out, &contents) ||
|
||||
!CBB_add_u16(&contents, ssl_cipher_get_value(ssl->config->esni_cipher)) ||
|
||||
!CBB_add_u16_length_prefixed(&contents, &kse_bytes) ||
|
||||
!CBB_add_u16(&kse_bytes, ssl->config->esni_group) ||
|
||||
!CBB_add_u16_length_prefixed(&kse_bytes, &key_exchange)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UniquePtr<SSLKeyShare> esni_client_share =
|
||||
SSLKeyShare::Create(ssl->config->esni_group);
|
||||
Array<uint8_t> shared_secret;
|
||||
uint8_t alert;
|
||||
if (!esni_client_share ||
|
||||
!esni_client_share->Accept(&key_exchange, &shared_secret, &alert,
|
||||
ssl->config->esni_server_keyshare)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CBB_add_u16_length_prefixed(&contents, &record_digest) ||
|
||||
!CBB_add_bytes(&record_digest, ssl->config->esni_record_digest.data(),
|
||||
ssl->config->esni_record_digest.size()) ||
|
||||
!CBB_add_u16_length_prefixed(&contents, &esni)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RAND_bytes(hs->esni_nonce, sizeof(hs->esni_nonce))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tls13_derive_esni_secrets(hs, std::move(shared_secret))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t padding_len = ssl->config->esni_padded_length - strlen(ssl->hostname.get());
|
||||
|
||||
CBB client_esni, dns_name;
|
||||
uint8_t *padding;
|
||||
if (!CBB_init(&client_esni, 0) ||
|
||||
!CBB_add_bytes(&client_esni, hs->esni_nonce, sizeof(hs->esni_nonce)) ||
|
||||
!CBB_add_u16_length_prefixed(&client_esni, &dns_name) ||
|
||||
!CBB_add_bytes(&dns_name, (const uint8_t *)ssl->hostname.get(),
|
||||
strlen(ssl->hostname.get())) ||
|
||||
!CBB_add_space(&dns_name, &padding, padding_len) ||
|
||||
!CBB_flush(&client_esni)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OPENSSL_memset(padding, padding_len, 0);
|
||||
size_t esni_max_len =
|
||||
CBB_len(&client_esni) + EVP_AEAD_max_overhead(hs->esni_aead_ctx->aead);
|
||||
Array<uint8_t> inner_esni;
|
||||
size_t inner_esni_len = 0;
|
||||
if (!inner_esni.Init(esni_max_len) ||
|
||||
!EVP_AEAD_CTX_seal(hs->esni_aead_ctx.get(), inner_esni.data(),
|
||||
&inner_esni_len, esni_max_len, hs->esni_iv,
|
||||
hs->esni_iv_len, CBB_data(&client_esni),
|
||||
CBB_len(&client_esni), hs->key_share_bytes.data(),
|
||||
hs->key_share_bytes.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CBB_add_bytes(&esni, inner_esni.data(), inner_esni_len) ||
|
||||
!CBB_flush(&esni)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CBB_flush(out);
|
||||
}
|
||||
|
||||
static bool ext_encrypted_server_name_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
|
||||
CBS *contents) {
|
||||
if (contents == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(!SSL_is_dtls(hs->ssl));
|
||||
assert(hs->config->enable_esni);
|
||||
|
||||
uint8_t response_type;
|
||||
if (!CBS_get_u8(contents, &response_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response_type == 0) {
|
||||
if (CBS_len(contents) != 16 ||
|
||||
!CBS_mem_equal(contents, hs->esni_nonce, 16)) {
|
||||
*out_alert = SSL_AD_ILLEGAL_PARAMETER;
|
||||
return false;
|
||||
}
|
||||
hs->esni_state = ssl_esni_verified;
|
||||
} else if (response_type == 1) {
|
||||
// TODO(svaldez): Handle this.
|
||||
// esni_retry_request
|
||||
//ESNIKeys retry_keys<1..2^16-1>;
|
||||
hs->esni_state = ssl_esni_retry_required;
|
||||
return false;
|
||||
} else {
|
||||
*out_alert = SSL_AD_ILLEGAL_PARAMETER;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ssl_ext_encrypted_server_name_parse_clienthello(SSL_HANDSHAKE *hs,
|
||||
uint8_t *out_alert,
|
||||
CBS *contents) {
|
||||
SSL *const ssl = hs->ssl;
|
||||
|
||||
if (contents == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CBS kse_bytes, key_exchange, record_digest, esni;
|
||||
uint16_t cipher_id;
|
||||
if (!CBS_get_u16(contents, &cipher_id) ||
|
||||
!CBS_get_u16_length_prefixed(contents, &kse_bytes) ||
|
||||
!CBS_get_u16(&kse_bytes, &hs->config->esni_group) ||
|
||||
!CBS_get_u16_length_prefixed(&kse_bytes, &key_exchange) ||
|
||||
!CBS_get_u16_length_prefixed(contents, &record_digest) ||
|
||||
!CBS_get_u16_length_prefixed(contents, &esni) ||
|
||||
CBS_len(contents) != 0) {
|
||||
return false;
|
||||
}
|
||||
hs->config->esni_cipher = SSL_get_cipher_by_value(cipher_id);
|
||||
if (!hs->config->esni_record_digest.CopyFrom(
|
||||
MakeSpan(CBS_data(&record_digest), CBS_len(&record_digest)))) {
|
||||
return false;
|
||||
}
|
||||
CBS server_keyshare;
|
||||
CBS_init(&server_keyshare, ssl->config->esni_private.data(),
|
||||
ssl->config->esni_private.size());
|
||||
UniquePtr<SSLKeyShare> esni_server_share =
|
||||
SSLKeyShare::Create(&server_keyshare);
|
||||
|
||||
Array<uint8_t> shared_secret;
|
||||
uint8_t alert;
|
||||
if (!esni_server_share->Finish(&shared_secret, &alert, key_exchange)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tls13_derive_esni_secrets(hs, std::move(shared_secret))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Array<uint8_t> inner_esni;
|
||||
size_t inner_esni_len;
|
||||
size_t esni_max_len = CBS_len(&esni);
|
||||
if (!inner_esni.Init(esni_max_len) ||
|
||||
!EVP_AEAD_CTX_open(hs->esni_aead_ctx.get(), inner_esni.data(),
|
||||
&inner_esni_len, esni_max_len, hs->esni_iv,
|
||||
hs->esni_iv_len, CBS_data(&esni), CBS_len(&esni),
|
||||
hs->key_share_bytes.data(),
|
||||
hs->key_share_bytes.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inner_esni.Shrink(inner_esni_len);
|
||||
CBS real_esni, dns_name;
|
||||
CBS_init(&real_esni, inner_esni.data(), inner_esni.size());
|
||||
uint8_t nonce[16];
|
||||
if (!CBS_copy_bytes(&real_esni, nonce, sizeof(nonce)) ||
|
||||
!CBS_get_u16_length_prefixed(&real_esni, &dns_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the hostname as a string.
|
||||
char *raw = nullptr;
|
||||
if (!CBS_strdup(&dns_name, &raw)) {
|
||||
*out_alert = SSL_AD_INTERNAL_ERROR;
|
||||
return false;
|
||||
}
|
||||
ssl->s3->hostname.reset(raw);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ext_encrypted_server_name_add_serverhello(SSL_HANDSHAKE *hs,
|
||||
CBB *out) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// kExtensions contains all the supported extensions.
|
||||
static const struct tls_extension kExtensions[] = {
|
||||
{
|
||||
@@ -3143,6 +3351,14 @@ static const struct tls_extension kExtensions[] = {
|
||||
ext_pq_experiment_signal_parse_clienthello,
|
||||
ext_pq_experiment_signal_add_serverhello,
|
||||
},
|
||||
{
|
||||
TLSEXT_TYPE_encrypted_server_name,
|
||||
NULL,
|
||||
ext_encrypted_server_name_add_clienthello,
|
||||
ext_encrypted_server_name_parse_serverhello,
|
||||
ignore_parse_clienthello,
|
||||
ext_encrypted_server_name_add_serverhello,
|
||||
},
|
||||
};
|
||||
|
||||
#define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
|
||||
|
||||
@@ -565,4 +565,64 @@ bool tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
|
||||
return true;
|
||||
}
|
||||
|
||||
//TODO(svaldez): Support HRR case.
|
||||
bool tls13_derive_esni_secrets(SSL_HANDSHAKE *hs, Array<uint8_t> shared_secret) {
|
||||
SSL const *ssl = hs->ssl;
|
||||
|
||||
const EVP_MD *digest =
|
||||
ssl_get_handshake_digest(TLS1_3_VERSION, hs->config->esni_cipher);
|
||||
|
||||
uint8_t zeroes[EVP_MAX_MD_SIZE] = {0};
|
||||
uint8_t zx[EVP_MAX_MD_SIZE] = {0};
|
||||
size_t zx_len;
|
||||
if (!HKDF_extract(zx, &zx_len, digest, shared_secret.data(),
|
||||
shared_secret.size(), zeroes, EVP_MD_size(digest))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CBB esni_cbb, record_digest, keyshare;
|
||||
Array<uint8_t> esni_contents;
|
||||
if (!CBB_init(&esni_cbb, 0) ||
|
||||
!CBB_add_u16_length_prefixed(&esni_cbb, &record_digest) ||
|
||||
!CBB_add_bytes(&record_digest, ssl->config->esni_record_digest.data(),
|
||||
ssl->config->esni_record_digest.size()) ||
|
||||
!CBB_add_u16(&esni_cbb, ssl->config->esni_group) ||
|
||||
!CBB_add_u16_length_prefixed(&esni_cbb, &keyshare) ||
|
||||
!CBB_add_bytes(&keyshare, ssl->config->esni_server_keyshare.data(),
|
||||
ssl->config->esni_server_keyshare.size()) ||
|
||||
!CBB_add_bytes(&esni_cbb, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
|
||||
!CBBFinishArray(&esni_cbb, &esni_contents)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t esni_hash[EVP_MAX_MD_SIZE];
|
||||
unsigned esni_hash_len;
|
||||
if (!EVP_Digest(esni_contents.data(), esni_contents.size(), esni_hash,
|
||||
&esni_hash_len, digest, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const EVP_AEAD *aead;
|
||||
size_t discard;
|
||||
if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard, ssl->config->esni_cipher,
|
||||
TLS1_3_VERSION, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Derive the key and iv.
|
||||
size_t key_len = EVP_AEAD_key_length(aead);
|
||||
uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
|
||||
hs->esni_iv_len = EVP_AEAD_nonce_length(aead);
|
||||
|
||||
if (!hkdf_expand_label(key, digest, zx, zx_len, "esni key", 8, esni_hash,
|
||||
esni_hash_len, key_len) ||
|
||||
!hkdf_expand_label(hs->esni_iv, digest, zx, zx_len, "esni iv", 7,
|
||||
esni_hash, esni_hash_len, hs->esni_iv_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return EVP_AEAD_CTX_init(hs->esni_aead_ctx.get(), aead, key, key_len,
|
||||
hs->esni_iv_len, nullptr);
|
||||
}
|
||||
|
||||
BSSL_NAMESPACE_END
|
||||
|
||||
Reference in New Issue
Block a user