external/boringssl: Sync to 8625ec4b436ccb4098ed4aac10891eff8372be41.
This includes the following changes:
https://boringssl.googlesource.com/boringssl/+log/c596415ec62b501523d80f9afa26b135406da6bf..8625ec4b436ccb4098ed4aac10891eff8372be41
Test: cts -m CtsLibcoreTestCases
Change-Id: I47a45e6b6f46b19fcbcb6c917895867d56dcd2ca
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index 3eab0b5..f2f60ca 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -4,7 +4,6 @@
ssl
bio_ssl.cc
- custom_extensions.cc
d1_both.cc
d1_lib.cc
d1_pkt.cc
diff --git a/src/ssl/custom_extensions.cc b/src/ssl/custom_extensions.cc
deleted file mode 100644
index 85b8a33..0000000
--- a/src/ssl/custom_extensions.cc
+++ /dev/null
@@ -1,265 +0,0 @@
-/* Copyright (c) 2014, 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/ssl.h>
-
-#include <assert.h>
-#include <string.h>
-
-#include <openssl/bytestring.h>
-#include <openssl/err.h>
-#include <openssl/mem.h>
-#include <openssl/stack.h>
-
-#include "internal.h"
-
-
-namespace bssl {
-
-void SSL_CUSTOM_EXTENSION_free(SSL_CUSTOM_EXTENSION *custom_extension) {
- OPENSSL_free(custom_extension);
-}
-
-static const SSL_CUSTOM_EXTENSION *custom_ext_find(
- STACK_OF(SSL_CUSTOM_EXTENSION) *stack,
- unsigned *out_index, uint16_t value) {
- for (size_t i = 0; i < sk_SSL_CUSTOM_EXTENSION_num(stack); i++) {
- const SSL_CUSTOM_EXTENSION *ext = sk_SSL_CUSTOM_EXTENSION_value(stack, i);
- if (ext->value == value) {
- if (out_index != NULL) {
- *out_index = i;
- }
- return ext;
- }
- }
-
- return NULL;
-}
-
-// default_add_callback is used as the |add_callback| when the user doesn't
-// provide one. For servers, it does nothing while, for clients, it causes an
-// empty extension to be included.
-static int default_add_callback(SSL *ssl, unsigned extension_value,
- const uint8_t **out, size_t *out_len,
- int *out_alert_value, void *add_arg) {
- if (ssl->server) {
- return 0;
- }
- *out_len = 0;
- return 1;
-}
-
-static int custom_ext_add_hello(SSL_HANDSHAKE *hs, CBB *extensions) {
- SSL *const ssl = hs->ssl;
- STACK_OF(SSL_CUSTOM_EXTENSION) *stack = ssl->ctx->client_custom_extensions;
- if (ssl->server) {
- stack = ssl->ctx->server_custom_extensions;
- }
-
- if (stack == NULL) {
- return 1;
- }
-
- for (size_t i = 0; i < sk_SSL_CUSTOM_EXTENSION_num(stack); i++) {
- const SSL_CUSTOM_EXTENSION *ext = sk_SSL_CUSTOM_EXTENSION_value(stack, i);
-
- if (ssl->server &&
- !(hs->custom_extensions.received & (1u << i))) {
- // Servers cannot echo extensions that the client didn't send.
- continue;
- }
-
- const uint8_t *contents;
- size_t contents_len;
- int alert = SSL_AD_DECODE_ERROR;
- CBB contents_cbb;
-
- switch (ext->add_callback(ssl, ext->value, &contents, &contents_len, &alert,
- ext->add_arg)) {
- case 1:
- if (!CBB_add_u16(extensions, ext->value) ||
- !CBB_add_u16_length_prefixed(extensions, &contents_cbb) ||
- !CBB_add_bytes(&contents_cbb, contents, contents_len) ||
- !CBB_flush(extensions)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- ERR_add_error_dataf("extension %u", (unsigned) ext->value);
- if (ext->free_callback && 0 < contents_len) {
- ext->free_callback(ssl, ext->value, contents, ext->add_arg);
- }
- return 0;
- }
-
- if (ext->free_callback && 0 < contents_len) {
- ext->free_callback(ssl, ext->value, contents, ext->add_arg);
- }
-
- if (!ssl->server) {
- assert((hs->custom_extensions.sent & (1u << i)) == 0);
- hs->custom_extensions.sent |= (1u << i);
- }
- break;
-
- case 0:
- break;
-
- default:
- ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
- OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
- ERR_add_error_dataf("extension %u", (unsigned) ext->value);
- return 0;
- }
- }
-
- return 1;
-}
-
-int custom_ext_add_clienthello(SSL_HANDSHAKE *hs, CBB *extensions) {
- return custom_ext_add_hello(hs, extensions);
-}
-
-int custom_ext_parse_serverhello(SSL_HANDSHAKE *hs, int *out_alert,
- uint16_t value, const CBS *extension) {
- SSL *const ssl = hs->ssl;
- unsigned index;
- const SSL_CUSTOM_EXTENSION *ext =
- custom_ext_find(ssl->ctx->client_custom_extensions, &index, value);
-
- if (// Unknown extensions are not allowed in a ServerHello.
- ext == NULL ||
- // Also, if we didn't send the extension, that's also unacceptable.
- !(hs->custom_extensions.sent & (1u << index))) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
- ERR_add_error_dataf("extension %u", (unsigned)value);
- *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
- return 0;
- }
-
- if (ext->parse_callback != NULL &&
- !ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension),
- out_alert, ext->parse_arg)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
- ERR_add_error_dataf("extension %u", (unsigned)ext->value);
- return 0;
- }
-
- return 1;
-}
-
-int custom_ext_parse_clienthello(SSL_HANDSHAKE *hs, int *out_alert,
- uint16_t value, const CBS *extension) {
- SSL *const ssl = hs->ssl;
- unsigned index;
- const SSL_CUSTOM_EXTENSION *ext =
- custom_ext_find(ssl->ctx->server_custom_extensions, &index, value);
-
- if (ext == NULL) {
- return 1;
- }
-
- assert((hs->custom_extensions.received & (1u << index)) == 0);
- hs->custom_extensions.received |= (1u << index);
-
- if (ext->parse_callback &&
- !ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension),
- out_alert, ext->parse_arg)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
- ERR_add_error_dataf("extension %u", (unsigned)ext->value);
- return 0;
- }
-
- return 1;
-}
-
-int custom_ext_add_serverhello(SSL_HANDSHAKE *hs, CBB *extensions) {
- return custom_ext_add_hello(hs, extensions);
-}
-
-// MAX_NUM_CUSTOM_EXTENSIONS is the maximum number of custom extensions that
-// can be set on an |SSL_CTX|. It's determined by the size of the bitset used
-// to track when an extension has been sent.
-#define MAX_NUM_CUSTOM_EXTENSIONS \
- (sizeof(((SSL_HANDSHAKE *)NULL)->custom_extensions.sent) * 8)
-
-static int custom_ext_append(STACK_OF(SSL_CUSTOM_EXTENSION) **stack,
- unsigned extension_value,
- SSL_custom_ext_add_cb add_cb,
- SSL_custom_ext_free_cb free_cb, void *add_arg,
- SSL_custom_ext_parse_cb parse_cb,
- void *parse_arg) {
- if (add_cb == NULL ||
- 0xffff < extension_value ||
- SSL_extension_supported(extension_value) ||
- // Specifying a free callback without an add callback is nonsensical
- // and an error.
- (*stack != NULL &&
- (MAX_NUM_CUSTOM_EXTENSIONS <= sk_SSL_CUSTOM_EXTENSION_num(*stack) ||
- custom_ext_find(*stack, NULL, extension_value) != NULL))) {
- return 0;
- }
-
- SSL_CUSTOM_EXTENSION *ext =
- (SSL_CUSTOM_EXTENSION *)OPENSSL_malloc(sizeof(SSL_CUSTOM_EXTENSION));
- if (ext == NULL) {
- return 0;
- }
- ext->add_callback = add_cb;
- ext->add_arg = add_arg;
- ext->free_callback = free_cb;
- ext->parse_callback = parse_cb;
- ext->parse_arg = parse_arg;
- ext->value = extension_value;
-
- if (*stack == NULL) {
- *stack = sk_SSL_CUSTOM_EXTENSION_new_null();
- if (*stack == NULL) {
- SSL_CUSTOM_EXTENSION_free(ext);
- return 0;
- }
- }
-
- if (!sk_SSL_CUSTOM_EXTENSION_push(*stack, ext)) {
- SSL_CUSTOM_EXTENSION_free(ext);
- if (sk_SSL_CUSTOM_EXTENSION_num(*stack) == 0) {
- sk_SSL_CUSTOM_EXTENSION_free(*stack);
- *stack = NULL;
- }
- return 0;
- }
-
- return 1;
-}
-
-} // namespace bssl
-
-using namespace bssl;
-
-int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned extension_value,
- SSL_custom_ext_add_cb add_cb,
- SSL_custom_ext_free_cb free_cb, void *add_arg,
- SSL_custom_ext_parse_cb parse_cb,
- void *parse_arg) {
- return custom_ext_append(&ctx->client_custom_extensions, extension_value,
- add_cb ? add_cb : default_add_callback, free_cb,
- add_arg, parse_cb, parse_arg);
-}
-
-int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned extension_value,
- SSL_custom_ext_add_cb add_cb,
- SSL_custom_ext_free_cb free_cb, void *add_arg,
- SSL_custom_ext_parse_cb parse_cb,
- void *parse_arg) {
- return custom_ext_append(&ctx->server_custom_extensions, extension_value,
- add_cb ? add_cb : default_add_callback, free_cb,
- add_arg, parse_cb, parse_arg);
-}
diff --git a/src/ssl/d1_both.cc b/src/ssl/d1_both.cc
index f561332..f22a498 100644
--- a/src/ssl/d1_both.cc
+++ b/src/ssl/d1_both.cc
@@ -536,6 +536,20 @@
return true;
}
+// ssl_size_t_greater_than_32_bits returns whether |v| exceeds the bounds of a
+// 32-bit value. The obvious thing doesn't work because, in some 32-bit build
+// configurations, the compiler warns that the test is always false and breaks
+// the build.
+static bool ssl_size_t_greater_than_32_bits(size_t v) {
+#if defined(OPENSSL_64_BIT)
+ return v > 0xffffffff;
+#elif defined(OPENSSL_32_BIT)
+ return false;
+#else
+#error "Building for neither 32- nor 64-bits."
+#endif
+}
+
// add_outgoing adds a new handshake message or ChangeCipherSpec to the current
// outgoing flight. It returns true on success and false on error.
static bool add_outgoing(SSL *ssl, bool is_ccs, Array<uint8_t> data) {
@@ -550,7 +564,7 @@
(1 << 8 * sizeof(ssl->d1->outgoing_messages_len)),
"outgoing_messages_len is too small");
if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT ||
- data.size() > 0xffffffff) {
+ ssl_size_t_greater_than_32_bits(data.size())) {
assert(false);
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return false;
@@ -604,12 +618,12 @@
// |SSL_set_mtu|. Does this need to be so complex?
if (ssl->d1->mtu < dtls1_min_mtu() &&
!(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
- long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
+ long mtu = BIO_ctrl(ssl->wbio.get(), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
ssl->d1->mtu = (unsigned)mtu;
} else {
ssl->d1->mtu = kDefaultMTU;
- BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
+ BIO_ctrl(ssl->wbio.get(), BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
}
}
@@ -789,7 +803,7 @@
goto err;
}
- int bio_ret = BIO_write(ssl->wbio, packet, packet_len);
+ int bio_ret = BIO_write(ssl->wbio.get(), packet, packet_len);
if (bio_ret <= 0) {
// Retry this packet the next time around.
ssl->d1->outgoing_written = old_written;
@@ -800,7 +814,7 @@
}
}
- if (BIO_flush(ssl->wbio) <= 0) {
+ if (BIO_flush(ssl->wbio.get()) <= 0) {
ssl->s3->rwstate = SSL_WRITING;
goto err;
}
diff --git a/src/ssl/d1_lib.cc b/src/ssl/d1_lib.cc
index eff06ee..d73e538 100644
--- a/src/ssl/d1_lib.cc
+++ b/src/ssl/d1_lib.cc
@@ -171,7 +171,8 @@
// Reduce MTU after 2 unsuccessful retransmissions
if (ssl->d1->num_timeouts > DTLS1_MTU_TIMEOUTS &&
!(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
- long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL);
+ long mtu =
+ BIO_ctrl(ssl->wbio.get(), BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, nullptr);
if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
ssl->d1->mtu = (unsigned)mtu;
}
diff --git a/src/ssl/d1_pkt.cc b/src/ssl/d1_pkt.cc
index d29a5c2..a694c5f 100644
--- a/src/ssl/d1_pkt.cc
+++ b/src/ssl/d1_pkt.cc
@@ -260,7 +260,7 @@
// If the alert is fatal, flush the BIO now.
if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
- BIO_flush(ssl->wbio);
+ BIO_flush(ssl->wbio.get());
}
ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, ssl->s3->send_alert);
diff --git a/src/ssl/d1_srtp.cc b/src/ssl/d1_srtp.cc
index f27c9ff..96d7d51 100644
--- a/src/ssl/d1_srtp.cc
+++ b/src/ssl/d1_srtp.cc
@@ -158,8 +158,9 @@
return 0;
}
-static int ssl_ctx_make_profiles(const char *profiles_string,
- STACK_OF(SRTP_PROTECTION_PROFILE) **out) {
+static int ssl_ctx_make_profiles(
+ const char *profiles_string,
+ UniquePtr<STACK_OF(SRTP_PROTECTION_PROFILE)> *out) {
UniquePtr<STACK_OF(SRTP_PROTECTION_PROFILE)> profiles(
sk_SRTP_PROTECTION_PROFILE_new_null());
if (profiles == nullptr) {
@@ -188,8 +189,7 @@
}
} while (col);
- sk_SRTP_PROTECTION_PROFILE_free(*out);
- *out = profiles.release();
+ *out = std::move(profiles);
return 1;
}
@@ -198,23 +198,23 @@
}
int SSL_set_srtp_profiles(SSL *ssl, const char *profiles) {
- return ssl_ctx_make_profiles(profiles, &ssl->srtp_profiles);
+ return ssl->config != nullptr &&
+ ssl_ctx_make_profiles(profiles, &ssl->config->srtp_profiles);
}
STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(SSL *ssl) {
- if (ssl == NULL) {
- return NULL;
+ if (ssl == nullptr) {
+ return nullptr;
}
- if (ssl->srtp_profiles != NULL) {
- return ssl->srtp_profiles;
+ if (ssl->config == nullptr) {
+ assert(0);
+ return nullptr;
}
- if (ssl->ctx->srtp_profiles != NULL) {
- return ssl->ctx->srtp_profiles;
- }
-
- return NULL;
+ return ssl->config->srtp_profiles != nullptr
+ ? ssl->config->srtp_profiles.get()
+ : ssl->ctx->srtp_profiles.get();
}
const SRTP_PROTECTION_PROFILE *SSL_get_selected_srtp_profile(SSL *ssl) {
diff --git a/src/ssl/handoff.cc b/src/ssl/handoff.cc
index 2cbbaeb..68cac5b 100644
--- a/src/ssl/handoff.cc
+++ b/src/ssl/handoff.cc
@@ -55,7 +55,7 @@
return false;
}
- ssl->handoff = false;
+ s3->hs->config->handoff = false;
return true;
}
@@ -94,18 +94,29 @@
s3->hs->transcript.Update(transcript);
s3->is_v2_hello = true;
}
- ssl->handback = true;
+ s3->hs->handback = true;
return true;
}
bool SSL_serialize_handback(const SSL *ssl, CBB *out) {
- if (!ssl->server ||
- (ssl->s3->hs->state != state12_finish_server_handshake &&
- ssl->s3->hs->state != state12_read_client_certificate) ||
- ssl->method->is_dtls || ssl->version < TLS1_VERSION) {
+ if (!ssl->server || ssl->method->is_dtls) {
return false;
}
+ handback_t type;
+ switch (ssl->s3->hs->state) {
+ case state12_read_change_cipher_spec:
+ type = handback_after_session_resumption;
+ break;
+ case state12_read_client_certificate:
+ type = handback_after_ecdhe;
+ break;
+ case state12_finish_server_handshake:
+ type = handback_after_handshake;
+ break;
+ default:
+ return false;
+ }
const SSL3_STATE *const s3 = ssl->s3;
size_t hostname_len = 0;
@@ -113,26 +124,36 @@
hostname_len = strlen(s3->hostname.get());
}
- size_t iv_len = 0;
- const uint8_t *read_iv = nullptr, *write_iv = nullptr;
Span<const uint8_t> transcript;
- if (ssl->s3->hs->state == state12_finish_server_handshake) {
- if (ssl->version == TLS1_VERSION &&
- SSL_CIPHER_is_block_cipher(s3->aead_read_ctx->cipher()) &&
- (!s3->aead_read_ctx->GetIV(&read_iv, &iv_len) ||
- !s3->aead_write_ctx->GetIV(&write_iv, &iv_len))) {
- return false;
- }
- } else {
+ if (type == handback_after_ecdhe ||
+ type == handback_after_session_resumption) {
transcript = s3->hs->transcript.buffer();
}
+ size_t write_iv_len = 0;
+ const uint8_t *write_iv = nullptr;
+ if ((type == handback_after_session_resumption ||
+ type == handback_after_handshake) &&
+ ssl->version == TLS1_VERSION &&
+ SSL_CIPHER_is_block_cipher(s3->aead_write_ctx->cipher()) &&
+ !s3->aead_write_ctx->GetIV(&write_iv, &write_iv_len)) {
+ return false;
+ }
+ size_t read_iv_len = 0;
+ const uint8_t *read_iv = nullptr;
+ if (type == handback_after_handshake &&
+ ssl->version == TLS1_VERSION &&
+ SSL_CIPHER_is_block_cipher(s3->aead_read_ctx->cipher()) &&
+ !s3->aead_read_ctx->GetIV(&read_iv, &read_iv_len)) {
+ return false;
+ }
// TODO(mab): make sure everything is serialized.
CBB seq, key_share;
- SSL_SESSION *session =
- s3->session_reused ? ssl->session : s3->hs->new_session.get();
+ const SSL_SESSION *session =
+ s3->session_reused ? ssl->session.get() : s3->hs->new_session.get();
if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1_uint64(&seq, kHandbackVersion) ||
+ !CBB_add_asn1_uint64(&seq, type) ||
!CBB_add_asn1_octet_string(&seq, s3->read_sequence,
sizeof(s3->read_sequence)) ||
!CBB_add_asn1_octet_string(&seq, s3->write_sequence,
@@ -141,10 +162,10 @@
sizeof(s3->server_random)) ||
!CBB_add_asn1_octet_string(&seq, s3->client_random,
sizeof(s3->client_random)) ||
- !CBB_add_asn1_octet_string(&seq, read_iv, iv_len) ||
- !CBB_add_asn1_octet_string(&seq, write_iv, iv_len) ||
+ !CBB_add_asn1_octet_string(&seq, read_iv, read_iv_len) ||
+ !CBB_add_asn1_octet_string(&seq, write_iv, write_iv_len) ||
!CBB_add_asn1_bool(&seq, s3->session_reused) ||
- !CBB_add_asn1_bool(&seq, s3->tlsext_channel_id_valid) ||
+ !CBB_add_asn1_bool(&seq, s3->channel_id_valid) ||
!ssl_session_serialize(session, &seq) ||
!CBB_add_asn1_octet_string(&seq, s3->next_proto_negotiated.data(),
s3->next_proto_negotiated.size()) ||
@@ -153,8 +174,8 @@
!CBB_add_asn1_octet_string(
&seq, reinterpret_cast<uint8_t *>(s3->hostname.get()),
hostname_len) ||
- !CBB_add_asn1_octet_string(&seq, s3->tlsext_channel_id,
- sizeof(s3->tlsext_channel_id)) ||
+ !CBB_add_asn1_octet_string(&seq, s3->channel_id,
+ sizeof(s3->channel_id)) ||
!CBB_add_asn1_bool(&seq, ssl->s3->token_binding_negotiated) ||
!CBB_add_asn1_uint64(&seq, ssl->s3->negotiated_token_binding_param) ||
!CBB_add_asn1_bool(&seq, s3->hs->next_proto_neg_seen) ||
@@ -166,7 +187,7 @@
!CBB_add_asn1(&seq, &key_share, CBS_ASN1_SEQUENCE)) {
return false;
}
- if (ssl->s3->hs->state == state12_read_client_certificate &&
+ if (type == handback_after_ecdhe &&
!s3->hs->key_share->Serialize(&key_share)) {
return false;
}
@@ -180,7 +201,7 @@
}
SSL3_STATE *const s3 = ssl->s3;
- uint64_t handback_version, negotiated_token_binding_param, cipher;
+ uint64_t handback_version, negotiated_token_binding_param, cipher, type;
CBS seq, read_seq, write_seq, server_rand, client_rand, read_iv, write_iv,
next_proto, alpn, hostname, channel_id, transcript, key_share;
@@ -191,7 +212,8 @@
CBS handback_cbs(handback);
if (!CBS_get_asn1(&handback_cbs, &seq, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_uint64(&seq, &handback_version) ||
- handback_version != kHandbackVersion) {
+ handback_version != kHandbackVersion ||
+ !CBS_get_asn1_uint64(&seq, &type)) {
return false;
}
@@ -217,9 +239,8 @@
s3->hs = ssl_handshake_new(ssl);
if (session_reused) {
ssl->session =
- SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool)
- .release();
- session = ssl->session;
+ SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool);
+ session = ssl->session.get();
} else {
s3->hs->new_session =
SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool);
@@ -230,9 +251,9 @@
!CBS_get_asn1(&seq, &alpn, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1(&seq, &hostname, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1(&seq, &channel_id, CBS_ASN1_OCTETSTRING) ||
- CBS_len(&channel_id) != sizeof(s3->tlsext_channel_id) ||
- !CBS_copy_bytes(&channel_id, s3->tlsext_channel_id,
- sizeof(s3->tlsext_channel_id)) ||
+ CBS_len(&channel_id) != sizeof(s3->channel_id) ||
+ !CBS_copy_bytes(&channel_id, s3->channel_id,
+ sizeof(s3->channel_id)) ||
!CBS_get_asn1_bool(&seq, &token_binding_negotiated) ||
!CBS_get_asn1_uint64(&seq, &negotiated_token_binding_param) ||
!CBS_get_asn1_bool(&seq, &next_proto_neg_seen) ||
@@ -252,14 +273,36 @@
}
ssl->version = session->ssl_version;
+ s3->have_version = true;
+ if (!ssl_method_supports_version(ssl->method, ssl->version) ||
+ session->cipher != s3->hs->new_cipher ||
+ ssl_protocol_version(ssl) < SSL_CIPHER_get_min_version(session->cipher) ||
+ SSL_CIPHER_get_max_version(session->cipher) < ssl_protocol_version(ssl)) {
+ return false;
+ }
ssl->do_handshake = ssl_server_handshake;
ssl->server = true;
-
- s3->have_version = true;
- s3->hs->state = CBS_len(&transcript) == 0 ? state12_finish_server_handshake
- : state12_read_client_certificate;
+ switch (type) {
+ case handback_after_session_resumption:
+ ssl->s3->hs->state = state12_read_change_cipher_spec;
+ if (!session_reused) {
+ return false;
+ }
+ break;
+ case handback_after_ecdhe:
+ ssl->s3->hs->state = state12_read_client_certificate;
+ if (session_reused) {
+ return false;
+ }
+ break;
+ case handback_after_handshake:
+ ssl->s3->hs->state = state12_finish_server_handshake;
+ break;
+ default:
+ return false;
+ }
s3->session_reused = session_reused;
- s3->tlsext_channel_id_valid = channel_id_valid;
+ s3->channel_id_valid = channel_id_valid;
s3->next_proto_negotiated.CopyFrom(next_proto);
s3->alpn_selected.CopyFrom(alpn);
@@ -284,31 +327,33 @@
s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
s3->hs->cert_request = cert_request;
- if (s3->hs->state == state12_finish_server_handshake) {
- Array<uint8_t> key_block;
- if (!tls1_configure_aead(ssl, evp_aead_open, &key_block, session->cipher,
- read_iv) ||
- !tls1_configure_aead(ssl, evp_aead_seal, &key_block, session->cipher,
- write_iv)) {
- return false;
- }
-
- if (!CBS_copy_bytes(&read_seq, s3->read_sequence,
- sizeof(s3->read_sequence)) ||
- !CBS_copy_bytes(&write_seq, s3->write_sequence,
- sizeof(s3->write_sequence))) {
- return false;
- }
- } else {
- if (!s3->hs->transcript.Init() ||
- !s3->hs->transcript.InitHash(ssl_protocol_version(ssl),
- s3->hs->new_cipher) ||
- !s3->hs->transcript.Update(transcript)) {
- return false;
- }
- if ((s3->hs->key_share = SSLKeyShare::Create(&key_share)) == nullptr) {
- return false;
- }
+ Array<uint8_t> key_block;
+ if ((type == handback_after_session_resumption ||
+ type == handback_after_handshake) &&
+ (!tls1_configure_aead(ssl, evp_aead_seal, &key_block, session->cipher,
+ write_iv) ||
+ !CBS_copy_bytes(&write_seq, s3->write_sequence,
+ sizeof(s3->write_sequence)))) {
+ return false;
+ }
+ if (type == handback_after_handshake &&
+ (!tls1_configure_aead(ssl, evp_aead_open, &key_block, session->cipher,
+ read_iv) ||
+ !CBS_copy_bytes(&read_seq, s3->read_sequence,
+ sizeof(s3->read_sequence)))) {
+ return false;
+ }
+ if ((type == handback_after_ecdhe ||
+ type == handback_after_session_resumption) &&
+ (!s3->hs->transcript.Init() ||
+ !s3->hs->transcript.InitHash(ssl_protocol_version(ssl),
+ s3->hs->new_cipher) ||
+ !s3->hs->transcript.Update(transcript))) {
+ return false;
+ }
+ if (type == handback_after_ecdhe &&
+ (s3->hs->key_share = SSLKeyShare::Create(&key_share)) == nullptr) {
+ return false;
}
return CBS_len(&seq) == 0;
diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc
index 00a2cc5..4683ac5 100644
--- a/src/ssl/handshake.cc
+++ b/src/ssl/handshake.cc
@@ -130,7 +130,6 @@
needs_psk_binder(false),
received_hello_retry_request(false),
sent_hello_retry_request(false),
- received_custom_extension(false),
handshake_finalized(false),
accept_psk_mode(false),
cert_request(false),
@@ -146,7 +145,10 @@
ticket_expected(false),
extended_master_secret(false),
pending_private_key_op(false),
- grease_seeded(false) {
+ grease_seeded(false),
+ handback(false),
+ cert_compression_negotiated(false) {
+ assert(ssl);
}
SSL_HANDSHAKE::~SSL_HANDSHAKE() {
@@ -159,6 +161,11 @@
!hs->transcript.Init()) {
return nullptr;
}
+ hs->config = ssl->config.get();
+ if (!hs->config) {
+ assert(hs->config);
+ return nullptr;
+ }
return hs;
}
@@ -189,7 +196,8 @@
static const size_t kMaxMessageLen = 16384;
if (SSL_in_init(ssl)) {
- if ((!ssl->server || (ssl->verify_mode & SSL_VERIFY_PEER)) &&
+ SSL_CONFIG *config = ssl->config.get(); // SSL_in_init() implies not NULL.
+ if ((!ssl->server || (config->verify_mode & SSL_VERIFY_PEER)) &&
kMaxMessageLen < ssl->max_cert_list) {
return ssl->max_cert_list;
}
@@ -272,16 +280,6 @@
return 1;
}
-static void set_crypto_buffer(CRYPTO_BUFFER **dest, CRYPTO_BUFFER *src) {
- // TODO(davidben): Remove this helper once |SSL_SESSION| can use |UniquePtr|
- // and |UniquePtr| has up_ref helpers.
- CRYPTO_BUFFER_free(*dest);
- *dest = src;
- if (src != nullptr) {
- CRYPTO_BUFFER_up_ref(src);
- }
-}
-
enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
const SSL_SESSION *prev_session = ssl->s3->established_session.get();
@@ -291,18 +289,19 @@
// so this check is sufficient to ensure the reported peer certificate never
// changes on renegotiation.
assert(!ssl->server);
- if (sk_CRYPTO_BUFFER_num(prev_session->certs) !=
- sk_CRYPTO_BUFFER_num(hs->new_session->certs)) {
+ if (sk_CRYPTO_BUFFER_num(prev_session->certs.get()) !=
+ sk_CRYPTO_BUFFER_num(hs->new_session->certs.get())) {
OPENSSL_PUT_ERROR(SSL, SSL_R_SERVER_CERT_CHANGED);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_verify_invalid;
}
- for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(hs->new_session->certs); i++) {
+ for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(hs->new_session->certs.get());
+ i++) {
const CRYPTO_BUFFER *old_cert =
- sk_CRYPTO_BUFFER_value(prev_session->certs, i);
+ sk_CRYPTO_BUFFER_value(prev_session->certs.get(), i);
const CRYPTO_BUFFER *new_cert =
- sk_CRYPTO_BUFFER_value(hs->new_session->certs, i);
+ sk_CRYPTO_BUFFER_value(hs->new_session->certs.get(), i);
if (CRYPTO_BUFFER_len(old_cert) != CRYPTO_BUFFER_len(new_cert) ||
OPENSSL_memcmp(CRYPTO_BUFFER_data(old_cert),
CRYPTO_BUFFER_data(new_cert),
@@ -317,25 +316,24 @@
// certificate. Since we only authenticated the previous one, copy other
// authentication from the established session and ignore what was newly
// received.
- set_crypto_buffer(&hs->new_session->ocsp_response,
- prev_session->ocsp_response);
- set_crypto_buffer(&hs->new_session->signed_cert_timestamp_list,
- prev_session->signed_cert_timestamp_list);
+ hs->new_session->ocsp_response = UpRef(prev_session->ocsp_response);
+ hs->new_session->signed_cert_timestamp_list =
+ UpRef(prev_session->signed_cert_timestamp_list);
hs->new_session->verify_result = prev_session->verify_result;
return ssl_verify_ok;
}
uint8_t alert = SSL_AD_CERTIFICATE_UNKNOWN;
enum ssl_verify_result_t ret;
- if (ssl->custom_verify_callback != nullptr) {
- ret = ssl->custom_verify_callback(ssl, &alert);
+ if (hs->config->custom_verify_callback != nullptr) {
+ ret = hs->config->custom_verify_callback(ssl, &alert);
switch (ret) {
case ssl_verify_ok:
hs->new_session->verify_result = X509_V_OK;
break;
case ssl_verify_invalid:
// If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result.
- if (ssl->verify_mode == SSL_VERIFY_NONE) {
+ if (hs->config->verify_mode == SSL_VERIFY_NONE) {
ERR_clear_error();
ret = ssl_verify_ok;
}
@@ -346,7 +344,7 @@
}
} else {
ret = ssl->ctx->x509_method->session_verify_cert_chain(
- hs->new_session.get(), ssl, &alert)
+ hs->new_session.get(), hs, &alert)
? ssl_verify_ok
: ssl_verify_invalid;
}
@@ -356,6 +354,22 @@
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
}
+ // Emulate OpenSSL's client OCSP callback. OpenSSL verifies certificates
+ // before it receives the OCSP, so it needs a second callback for OCSP.
+ if (ret == ssl_verify_ok && !ssl->server &&
+ hs->config->ocsp_stapling_enabled &&
+ ssl->ctx->legacy_ocsp_callback != nullptr) {
+ int cb_ret =
+ ssl->ctx->legacy_ocsp_callback(ssl, ssl->ctx->legacy_ocsp_callback_arg);
+ if (cb_ret <= 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_OCSP_CB_ERROR);
+ ssl_send_alert(ssl, SSL3_AL_FATAL,
+ cb_ret == 0 ? SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE
+ : SSL_AD_INTERNAL_ERROR);
+ ret = ssl_verify_invalid;
+ }
+ }
+
return ret;
}
@@ -408,20 +422,18 @@
}
// Copy the Finished so we can use it for renegotiation checks.
- if (ssl->version != SSL3_VERSION) {
- if (finished_len > sizeof(ssl->s3->previous_client_finished) ||
- finished_len > sizeof(ssl->s3->previous_server_finished)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return ssl_hs_error;
- }
+ if (finished_len > sizeof(ssl->s3->previous_client_finished) ||
+ finished_len > sizeof(ssl->s3->previous_server_finished)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
- if (ssl->server) {
- OPENSSL_memcpy(ssl->s3->previous_client_finished, finished, finished_len);
- ssl->s3->previous_client_finished_len = finished_len;
- } else {
- OPENSSL_memcpy(ssl->s3->previous_server_finished, finished, finished_len);
- ssl->s3->previous_server_finished_len = finished_len;
- }
+ if (ssl->server) {
+ OPENSSL_memcpy(ssl->s3->previous_client_finished, finished, finished_len);
+ ssl->s3->previous_client_finished_len = finished_len;
+ } else {
+ OPENSSL_memcpy(ssl->s3->previous_server_finished, finished, finished_len);
+ ssl->s3->previous_server_finished_len = finished_len;
}
ssl->method->next_message(ssl);
@@ -447,20 +459,18 @@
}
// Copy the Finished so we can use it for renegotiation checks.
- if (ssl->version != SSL3_VERSION) {
- if (finished_len > sizeof(ssl->s3->previous_client_finished) ||
- finished_len > sizeof(ssl->s3->previous_server_finished)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
+ if (finished_len > sizeof(ssl->s3->previous_client_finished) ||
+ finished_len > sizeof(ssl->s3->previous_server_finished)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
- if (ssl->server) {
- OPENSSL_memcpy(ssl->s3->previous_server_finished, finished, finished_len);
- ssl->s3->previous_server_finished_len = finished_len;
- } else {
- OPENSSL_memcpy(ssl->s3->previous_client_finished, finished, finished_len);
- ssl->s3->previous_client_finished_len = finished_len;
- }
+ if (ssl->server) {
+ OPENSSL_memcpy(ssl->s3->previous_server_finished, finished, finished_len);
+ ssl->s3->previous_server_finished_len = finished_len;
+ } else {
+ OPENSSL_memcpy(ssl->s3->previous_client_finished, finished, finished_len);
+ ssl->s3->previous_client_finished_len = finished_len;
}
ScopedCBB cbb;
@@ -475,12 +485,13 @@
return 1;
}
-bool ssl_output_cert_chain(SSL *ssl) {
+bool ssl_output_cert_chain(SSL_HANDSHAKE *hs) {
ScopedCBB cbb;
CBB body;
- if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CERTIFICATE) ||
- !ssl_add_cert_chain(ssl, &body) ||
- !ssl_add_message_cbb(ssl, cbb.get())) {
+ if (!hs->ssl->method->init_message(hs->ssl, cbb.get(), &body,
+ SSL3_MT_CERTIFICATE) ||
+ !ssl_add_cert_chain(hs, &body) ||
+ !ssl_add_message_cbb(hs->ssl, cbb.get())) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return false;
}
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index fbef2e1..de5d8e9 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -199,13 +199,13 @@
// ssl_get_client_disabled sets |*out_mask_a| and |*out_mask_k| to masks of
// disabled algorithms.
-static void ssl_get_client_disabled(SSL *ssl, uint32_t *out_mask_a,
+static void ssl_get_client_disabled(SSL_HANDSHAKE *hs, uint32_t *out_mask_a,
uint32_t *out_mask_k) {
*out_mask_a = 0;
*out_mask_k = 0;
// PSK requires a client callback.
- if (ssl->psk_client_callback == NULL) {
+ if (hs->config->psk_client_callback == NULL) {
*out_mask_a |= SSL_aPSK;
*out_mask_k |= SSL_kPSK;
}
@@ -214,7 +214,7 @@
static int ssl_write_client_cipher_list(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
uint32_t mask_a, mask_k;
- ssl_get_client_disabled(ssl, &mask_a, &mask_k);
+ ssl_get_client_disabled(hs, &mask_a, &mask_k);
CBB child;
if (!CBB_add_u16_length_prefixed(out, &child)) {
@@ -269,15 +269,6 @@
}
}
- // For SSLv3, the SCSV is added. Otherwise the renegotiation extension is
- // added.
- if (hs->max_version == SSL3_VERSION &&
- !ssl->s3->initial_handshake_complete) {
- if (!CBB_add_u16(&child, SSL3_CK_SCSV & 0xffff)) {
- return 0;
- }
- }
-
if (ssl->mode & SSL_MODE_SEND_FALLBACK_SCSV) {
if (!CBB_add_u16(&child, SSL3_CK_FALLBACK_SCSV & 0xffff)) {
return 0;
@@ -390,16 +381,10 @@
ssl->s3->session_reused = false;
// Freeze the version range.
- if (!ssl_get_version_range(ssl, &hs->min_version, &hs->max_version)) {
+ if (!ssl_get_version_range(hs, &hs->min_version, &hs->max_version)) {
return ssl_hs_error;
}
- // SSL 3.0 ClientHellos should use SSL 3.0 not TLS 1.0, for the record-layer
- // version.
- if (hs->max_version == SSL3_VERSION) {
- ssl->s3->aead_write_ctx->SetVersionIfNullCipher(SSL3_VERSION);
- }
-
// Always advertise the ClientHello version from the original maximum version,
// even on renegotiation. The static RSA key exchange uses this field, and
// some servers fail when it changes across handshakes.
@@ -417,9 +402,9 @@
if (ssl->session->is_server ||
!ssl_supports_version(hs, ssl->session->ssl_version) ||
(ssl->session->session_id_length == 0 &&
- ssl->session->tlsext_ticklen == 0) ||
+ ssl->session->ticket.empty()) ||
ssl->session->not_resumable ||
- !ssl_session_is_time_valid(ssl, ssl->session)) {
+ !ssl_session_is_time_valid(ssl, ssl->session.get())) {
ssl_set_session(ssl, NULL);
}
}
@@ -480,8 +465,7 @@
// Stash the early data session, so connection properties may be queried out
// of it.
hs->in_early_data = true;
- SSL_SESSION_up_ref(ssl->session);
- hs->early_session.reset(ssl->session);
+ hs->early_session = UpRef(ssl->session);
hs->can_early_write = true;
hs->state = state_read_server_hello;
@@ -659,7 +643,7 @@
// The cipher must be allowed in the selected version and enabled.
uint32_t mask_a, mask_k;
- ssl_get_client_disabled(ssl, &mask_a, &mask_k);
+ ssl_get_client_disabled(hs, &mask_a, &mask_k);
if ((cipher->algorithm_mkey & mask_k) || (cipher->algorithm_auth & mask_a) ||
SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl) ||
@@ -680,7 +664,7 @@
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
- if (!ssl_session_is_context_valid(ssl, ssl->session)) {
+ if (!ssl_session_is_context_valid(hs, ssl->session.get())) {
// This is actually a client application bug.
OPENSSL_PUT_ERROR(SSL,
SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
@@ -788,16 +772,13 @@
CBS body = msg.body;
uint8_t alert = SSL_AD_DECODE_ERROR;
- UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
- if (!ssl_parse_cert_chain(&alert, &chain, &hs->peer_pubkey, NULL, &body,
- ssl->ctx->pool)) {
+ if (!ssl_parse_cert_chain(&alert, &hs->new_session->certs, &hs->peer_pubkey,
+ NULL, &body, ssl->ctx->pool)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
- sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
- hs->new_session->certs = chain.release();
- if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0 ||
+ if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0 ||
CBS_len(&body) != 0 ||
!ssl->ctx->x509_method->session_cache_objects(hs->new_session.get())) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
@@ -807,7 +788,7 @@
if (!ssl_check_leaf_certificate(
hs, hs->peer_pubkey.get(),
- sk_CRYPTO_BUFFER_value(hs->new_session->certs, 0))) {
+ sk_CRYPTO_BUFFER_value(hs->new_session->certs.get(), 0))) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
@@ -854,9 +835,8 @@
return ssl_hs_error;
}
- CRYPTO_BUFFER_free(hs->new_session->ocsp_response);
- hs->new_session->ocsp_response =
- CRYPTO_BUFFER_new_from_CBS(&ocsp_response, ssl->ctx->pool);
+ hs->new_session->ocsp_response.reset(
+ CRYPTO_BUFFER_new_from_CBS(&ocsp_response, ssl->ctx->pool));
if (hs->new_session->ocsp_response == nullptr) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
@@ -970,7 +950,7 @@
hs->new_session->group_id = group_id;
// Ensure the group is consistent with preferences.
- if (!tls1_check_group_id(ssl, group_id)) {
+ if (!tls1_check_group_id(hs, group_id)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
@@ -1176,8 +1156,8 @@
}
// Call cert_cb to update the certificate.
- if (ssl->cert->cert_cb != NULL) {
- int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+ if (hs->config->cert->cert_cb != NULL) {
+ int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg);
if (rv == 0) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
@@ -1189,23 +1169,13 @@
}
}
- if (!ssl_has_certificate(ssl)) {
+ if (!ssl_has_certificate(hs->config)) {
// Without a client certificate, the handshake buffer may be released.
hs->transcript.FreeBuffer();
-
- // In SSL 3.0, the Certificate message is replaced with a warning alert.
- if (ssl->version == SSL3_VERSION) {
- if (!ssl->method->add_alert(ssl, SSL3_AL_WARNING,
- SSL_AD_NO_CERTIFICATE)) {
- return ssl_hs_error;
- }
- hs->state = state_send_client_key_exchange;
- return ssl_hs_ok;
- }
}
if (!ssl_on_certificate_selected(hs) ||
- !ssl_output_cert_chain(ssl)) {
+ !ssl_output_cert_chain(hs)) {
return ssl_hs_error;
}
@@ -1234,16 +1204,16 @@
unsigned psk_len = 0;
uint8_t psk[PSK_MAX_PSK_LEN];
if (alg_a & SSL_aPSK) {
- if (ssl->psk_client_callback == NULL) {
+ if (hs->config->psk_client_callback == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_CLIENT_CB);
return ssl_hs_error;
}
char identity[PSK_MAX_IDENTITY_LEN + 1];
OPENSSL_memset(identity, 0, sizeof(identity));
- psk_len =
- ssl->psk_client_callback(ssl, hs->peer_psk_identity_hint.get(),
- identity, sizeof(identity), psk, sizeof(psk));
+ psk_len = hs->config->psk_client_callback(
+ ssl, hs->peer_psk_identity_hint.get(), identity, sizeof(identity), psk,
+ sizeof(psk));
if (psk_len == 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
@@ -1251,9 +1221,8 @@
}
assert(psk_len <= PSK_MAX_PSK_LEN);
- OPENSSL_free(hs->new_session->psk_identity);
- hs->new_session->psk_identity = BUF_strdup(identity);
- if (hs->new_session->psk_identity == NULL) {
+ hs->new_session->psk_identity.reset(BUF_strdup(identity));
+ if (hs->new_session->psk_identity == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return ssl_hs_error;
}
@@ -1286,21 +1255,14 @@
return ssl_hs_error;
}
- CBB child, *enc_pms = &body;
- size_t enc_pms_len;
- // In TLS, there is a length prefix.
- if (ssl->version > SSL3_VERSION) {
- if (!CBB_add_u16_length_prefixed(&body, &child)) {
- return ssl_hs_error;
- }
- enc_pms = &child;
- }
-
+ CBB enc_pms;
uint8_t *ptr;
- if (!CBB_reserve(enc_pms, &ptr, RSA_size(rsa)) ||
+ size_t enc_pms_len;
+ if (!CBB_add_u16_length_prefixed(&body, &enc_pms) ||
+ !CBB_reserve(&enc_pms, &ptr, RSA_size(rsa)) ||
!RSA_encrypt(rsa, &enc_pms_len, ptr, RSA_size(rsa), pms.data(),
pms.size(), RSA_PKCS1_PADDING) ||
- !CBB_did_write(enc_pms, enc_pms_len) ||
+ !CBB_did_write(&enc_pms, enc_pms_len) ||
!CBB_flush(&body)) {
return ssl_hs_error;
}
@@ -1373,12 +1335,12 @@
static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- if (!hs->cert_request || !ssl_has_certificate(ssl)) {
+ if (!hs->cert_request || !ssl_has_certificate(hs->config)) {
hs->state = state_send_client_finished;
return ssl_hs_ok;
}
- assert(ssl_has_private_key(ssl));
+ assert(ssl_has_private_key(hs->config));
ScopedCBB cbb;
CBB body, child;
if (!ssl->method->init_message(ssl, cbb.get(), &body,
@@ -1388,6 +1350,7 @@
uint16_t signature_algorithm;
if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
return ssl_hs_error;
}
if (ssl_protocol_version(ssl) >= TLS1_2_VERSION) {
@@ -1407,40 +1370,16 @@
}
size_t sig_len = max_sig_len;
- // The SSL3 construction for CertificateVerify does not decompose into a
- // single final digest and signature, and must be special-cased.
- if (ssl_protocol_version(ssl) == SSL3_VERSION) {
- if (ssl->cert->key_method != NULL) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
+ switch (ssl_private_key_sign(hs, ptr, &sig_len, max_sig_len,
+ signature_algorithm,
+ hs->transcript.buffer())) {
+ case ssl_private_key_success:
+ break;
+ case ssl_private_key_failure:
return ssl_hs_error;
- }
-
- uint8_t digest[EVP_MAX_MD_SIZE];
- size_t digest_len;
- if (!hs->transcript.GetSSL3CertVerifyHash(
- digest, &digest_len, hs->new_session.get(), signature_algorithm)) {
- return ssl_hs_error;
- }
-
- UniquePtr<EVP_PKEY_CTX> pctx(
- EVP_PKEY_CTX_new(ssl->cert->privatekey.get(), nullptr));
- if (!pctx ||
- !EVP_PKEY_sign_init(pctx.get()) ||
- !EVP_PKEY_sign(pctx.get(), ptr, &sig_len, digest, digest_len)) {
- return ssl_hs_error;
- }
- } else {
- switch (ssl_private_key_sign(hs, ptr, &sig_len, max_sig_len,
- signature_algorithm,
- hs->transcript.buffer())) {
- case ssl_private_key_success:
- break;
- case ssl_private_key_failure:
- return ssl_hs_error;
- case ssl_private_key_retry:
- hs->state = state_send_client_certificate_verify;
- return ssl_hs_private_key_operation;
- }
+ case ssl_private_key_retry:
+ hs->state = state_send_client_certificate_verify;
+ return ssl_hs_private_key_operation;
}
if (!CBB_did_write(&child, sig_len) ||
@@ -1458,12 +1397,12 @@
static enum ssl_hs_wait_t do_send_client_finished(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
// Resolve Channel ID first, before any non-idempotent operations.
- if (ssl->s3->tlsext_channel_id_valid) {
- if (!ssl_do_channel_id_callback(ssl)) {
+ if (ssl->s3->channel_id_valid) {
+ if (!ssl_do_channel_id_callback(hs)) {
return ssl_hs_error;
}
- if (ssl->tlsext_channel_id_private == NULL) {
+ if (hs->config->channel_id_private == NULL) {
hs->state = state_send_client_finished;
return ssl_hs_channel_id_lookup;
}
@@ -1493,7 +1432,7 @@
}
}
- if (ssl->s3->tlsext_channel_id_valid) {
+ if (ssl->s3->channel_id_valid) {
ScopedCBB cbb;
CBB body;
if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CHANNEL_ID) ||
@@ -1583,8 +1522,8 @@
}
CBS new_session_ticket = msg.body, ticket;
- uint32_t tlsext_tick_lifetime_hint;
- if (!CBS_get_u32(&new_session_ticket, &tlsext_tick_lifetime_hint) ||
+ uint32_t ticket_lifetime_hint;
+ if (!CBS_get_u32(&new_session_ticket, &ticket_lifetime_hint) ||
!CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) ||
CBS_len(&new_session_ticket) != 0) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -1609,7 +1548,7 @@
// immutable once established, so duplicate all but the ticket of the
// existing session.
renewed_session =
- SSL_SESSION_dup(ssl->session, SSL_SESSION_INCLUDE_NONAUTH);
+ SSL_SESSION_dup(ssl->session.get(), SSL_SESSION_INCLUDE_NONAUTH);
if (!renewed_session) {
// This should never happen.
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -1618,14 +1557,13 @@
session = renewed_session.get();
}
- // |tlsext_tick_lifetime_hint| is measured from when the ticket was issued.
+ // |ticket_lifetime_hint| is measured from when the ticket was issued.
ssl_session_rebase_time(ssl, session);
- if (!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ if (!session->ticket.CopyFrom(ticket)) {
return ssl_hs_error;
}
- session->tlsext_tick_lifetime_hint = tlsext_tick_lifetime_hint;
+ session->ticket_lifetime_hint = ticket_lifetime_hint;
// Generate a session ID for this session based on the session ticket. We use
// the session ID mechanism for detecting ticket resumption. This also fits in
@@ -1637,9 +1575,8 @@
}
if (renewed_session) {
- session->not_resumable = 0;
- SSL_SESSION_free(ssl->session);
- ssl->session = renewed_session.release();
+ session->not_resumable = false;
+ ssl->session = std::move(renewed_session);
}
ssl->method->next_message(ssl);
@@ -1678,8 +1615,7 @@
ssl->method->on_handshake_complete(ssl);
if (ssl->session != NULL) {
- SSL_SESSION_up_ref(ssl->session);
- ssl->s3->established_session.reset(ssl->session);
+ ssl->s3->established_session = UpRef(ssl->session);
} else {
// We make a copy of the session in order to maintain the immutability
// of the new established_session due to False Start. The caller may
@@ -1691,7 +1627,7 @@
}
// Renegotiations do not participate in session resumption.
if (!ssl->s3->initial_handshake_complete) {
- ssl->s3->established_session->not_resumable = 0;
+ ssl->s3->established_session->not_resumable = false;
}
hs->new_session.reset();
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index 6404cc9..0159c9e 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -172,8 +172,8 @@
namespace bssl {
-int ssl_client_cipher_list_contains_cipher(const SSL_CLIENT_HELLO *client_hello,
- uint16_t id) {
+bool ssl_client_cipher_list_contains_cipher(
+ const SSL_CLIENT_HELLO *client_hello, uint16_t id) {
CBS cipher_suites;
CBS_init(&cipher_suites, client_hello->cipher_suites,
client_hello->cipher_suites_len);
@@ -181,19 +181,19 @@
while (CBS_len(&cipher_suites) > 0) {
uint16_t got_id;
if (!CBS_get_u16(&cipher_suites, &got_id)) {
- return 0;
+ return false;
}
if (got_id == id) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
-static int negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
- const SSL_CLIENT_HELLO *client_hello) {
+static bool negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+ const SSL_CLIENT_HELLO *client_hello) {
SSL *const ssl = hs->ssl;
assert(!ssl->s3->have_version);
CBS supported_versions, versions;
@@ -204,7 +204,7 @@
CBS_len(&versions) == 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
*out_alert = SSL_AD_DECODE_ERROR;
- return 0;
+ return false;
}
} else {
// Convert the ClientHello version to an equivalent supported_versions
@@ -213,7 +213,6 @@
0x03, 0x03, // TLS 1.2
0x03, 0x02, // TLS 1.1
0x03, 0x01, // TLS 1
- 0x03, 0x00, // SSL 3
};
static const uint8_t kDTLSVersions[] = {
@@ -232,12 +231,10 @@
versions_len);
} else {
if (client_hello->version >= TLS1_2_VERSION) {
- versions_len = 8;
- } else if (client_hello->version >= TLS1_1_VERSION) {
versions_len = 6;
- } else if (client_hello->version >= TLS1_VERSION) {
+ } else if (client_hello->version >= TLS1_1_VERSION) {
versions_len = 4;
- } else if (client_hello->version >= SSL3_VERSION) {
+ } else if (client_hello->version >= TLS1_VERSION) {
versions_len = 2;
}
CBS_init(&versions, kTLSVersions + sizeof(kTLSVersions) - versions_len,
@@ -246,7 +243,7 @@
}
if (!ssl_negotiate_version(hs, out_alert, &ssl->version, &versions)) {
- return 0;
+ return false;
}
// At this point, the connection's version is known and |ssl->version| is
@@ -260,10 +257,10 @@
ssl_protocol_version(ssl) < hs->max_version) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
*out_alert = SSL3_AD_INAPPROPRIATE_FALLBACK;
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static UniquePtr<STACK_OF(SSL_CIPHER)> ssl_parse_client_cipher_list(
@@ -303,11 +300,10 @@
static void ssl_get_compatible_server_ciphers(SSL_HANDSHAKE *hs,
uint32_t *out_mask_k,
uint32_t *out_mask_a) {
- SSL *const ssl = hs->ssl;
uint32_t mask_k = 0;
uint32_t mask_a = 0;
- if (ssl_has_certificate(ssl)) {
+ if (ssl_has_certificate(hs->config)) {
mask_a |= ssl_cipher_auth_mask_for_key(hs->local_pubkey.get());
if (EVP_PKEY_id(hs->local_pubkey.get()) == EVP_PKEY_RSA) {
mask_k |= SSL_kRSA;
@@ -321,7 +317,7 @@
}
// PSK requires a server callback.
- if (ssl->psk_server_callback != NULL) {
+ if (hs->config->psk_server_callback != NULL) {
mask_k |= SSL_kPSK;
mask_a |= SSL_aPSK;
}
@@ -417,7 +413,7 @@
return ssl_hs_error;
}
- if (ssl->handoff) {
+ if (hs->config->handoff) {
return ssl_hs_handoff;
}
@@ -446,7 +442,7 @@
}
// Freeze the version range after the early callback.
- if (!ssl_get_version_range(ssl, &hs->min_version, &hs->max_version)) {
+ if (!ssl_get_version_range(hs, &hs->min_version, &hs->max_version)) {
return ssl_hs_error;
}
@@ -494,8 +490,8 @@
}
// Call |cert_cb| to update server certificates if required.
- if (ssl->cert->cert_cb != NULL) {
- int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+ if (hs->config->cert->cert_cb != NULL) {
+ int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg);
if (rv == 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
@@ -510,6 +506,22 @@
return ssl_hs_error;
}
+ if (hs->ocsp_stapling_requested &&
+ ssl->ctx->legacy_ocsp_callback != nullptr) {
+ switch (ssl->ctx->legacy_ocsp_callback(
+ ssl, ssl->ctx->legacy_ocsp_callback_arg)) {
+ case SSL_TLSEXT_ERR_OK:
+ break;
+ case SSL_TLSEXT_ERR_NOACK:
+ hs->ocsp_stapling_requested = false;
+ break;
+ default:
+ OPENSSL_PUT_ERROR(SSL, SSL_R_OCSP_CB_ERROR);
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+ }
+
if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
// Jump to the TLS 1.3 state machine.
hs->state = state12_tls13;
@@ -523,8 +535,10 @@
// Negotiate the cipher suite. This must be done after |cert_cb| so the
// certificate is finalized.
- hs->new_cipher =
- ssl3_choose_cipher(hs, &client_hello, ssl_get_cipher_preferences(ssl));
+ SSLCipherPreferenceList *prefs = hs->config->cipher_list
+ ? hs->config->cipher_list.get()
+ : ssl->ctx->cipher_list.get();
+ hs->new_cipher = ssl3_choose_cipher(hs, &client_hello, prefs);
if (hs->new_cipher == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
@@ -562,7 +576,7 @@
UniquePtr<SSL_SESSION> session;
bool tickets_supported = false, renew_ticket = false;
enum ssl_hs_wait_t wait = ssl_get_prev_session(
- ssl, &session, &tickets_supported, &renew_ticket, &client_hello);
+ hs, &session, &tickets_supported, &renew_ticket, &client_hello);
if (wait != ssl_hs_ok) {
return wait;
}
@@ -587,7 +601,7 @@
if (session) {
// Use the old session.
hs->ticket_expected = renew_ticket;
- ssl->session = session.release();
+ ssl->session = std::move(session);
ssl->s3->session_reused = true;
} else {
hs->ticket_expected = tickets_supported;
@@ -614,10 +628,10 @@
hs->new_session->cipher = hs->new_cipher;
// Determine whether to request a client certificate.
- hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
+ hs->cert_request = !!(hs->config->verify_mode & SSL_VERIFY_PEER);
// Only request a certificate if Channel ID isn't negotiated.
- if ((ssl->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
- ssl->s3->tlsext_channel_id_valid) {
+ if ((hs->config->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
+ ssl->s3->channel_id_valid) {
hs->cert_request = false;
}
// CertificateRequest may only be sent in certificate-based ciphers.
@@ -650,7 +664,7 @@
// Handback includes the whole handshake transcript, so we cannot free the
// transcript buffer in the handback case.
- if (!hs->cert_request && !hs->ssl->handback) {
+ if (!hs->cert_request && !hs->handback) {
hs->transcript.FreeBuffer();
}
@@ -665,9 +679,9 @@
// We only accept ChannelIDs on connections with ECDHE in order to avoid a
// known attack while we fix ChannelID itself.
- if (ssl->s3->tlsext_channel_id_valid &&
+ if (ssl->s3->channel_id_valid &&
(hs->new_cipher->algorithm_mkey & SSL_kECDHE) == 0) {
- ssl->s3->tlsext_channel_id_valid = false;
+ ssl->s3->channel_id_valid = false;
}
// If this is a resumption and the original handshake didn't support
@@ -675,7 +689,7 @@
// session and so cannot resume with ChannelIDs.
if (ssl->session != NULL &&
ssl->session->original_handshake_hash_len == 0) {
- ssl->s3->tlsext_channel_id_valid = false;
+ ssl->s3->channel_id_valid = false;
}
struct OPENSSL_timeval now;
@@ -700,8 +714,8 @@
}
const SSL_SESSION *session = hs->new_session.get();
- if (ssl->session != NULL) {
- session = ssl->session;
+ if (ssl->session != nullptr) {
+ session = ssl->session.get();
}
ScopedCBB cbb;
@@ -733,12 +747,12 @@
ScopedCBB cbb;
if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
- if (!ssl_has_certificate(ssl)) {
+ if (!ssl_has_certificate(hs->config)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
return ssl_hs_error;
}
- if (!ssl_output_cert_chain(ssl)) {
+ if (!ssl_output_cert_chain(hs)) {
return ssl_hs_error;
}
@@ -748,9 +762,10 @@
SSL3_MT_CERTIFICATE_STATUS) ||
!CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) ||
!CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
- !CBB_add_bytes(&ocsp_response,
- CRYPTO_BUFFER_data(ssl->cert->ocsp_response.get()),
- CRYPTO_BUFFER_len(ssl->cert->ocsp_response.get())) ||
+ !CBB_add_bytes(
+ &ocsp_response,
+ CRYPTO_BUFFER_data(hs->config->cert->ocsp_response.get()),
+ CRYPTO_BUFFER_len(hs->config->cert->ocsp_response.get())) ||
!ssl_add_message_cbb(ssl, cbb.get())) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return ssl_hs_error;
@@ -762,8 +777,7 @@
uint32_t alg_k = hs->new_cipher->algorithm_mkey;
uint32_t alg_a = hs->new_cipher->algorithm_auth;
if (ssl_cipher_requires_server_key_exchange(hs->new_cipher) ||
- ((alg_a & SSL_aPSK) && ssl->psk_identity_hint)) {
-
+ ((alg_a & SSL_aPSK) && hs->config->psk_identity_hint)) {
// Pre-allocate enough room to comfortably fit an ECDHE public key. Prepend
// the client and server randoms for the signing transcript.
CBB child;
@@ -775,10 +789,12 @@
// PSK ciphers begin with an identity hint.
if (alg_a & SSL_aPSK) {
- size_t len =
- (ssl->psk_identity_hint == NULL) ? 0 : strlen(ssl->psk_identity_hint);
+ size_t len = hs->config->psk_identity_hint == nullptr
+ ? 0
+ : strlen(hs->config->psk_identity_hint.get());
if (!CBB_add_u16_length_prefixed(cbb.get(), &child) ||
- !CBB_add_bytes(&child, (const uint8_t *)ssl->psk_identity_hint,
+ !CBB_add_bytes(&child,
+ (const uint8_t *)hs->config->psk_identity_hint.get(),
len)) {
return ssl_hs_error;
}
@@ -837,7 +853,7 @@
// Add a signature.
if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
- if (!ssl_has_private_key(ssl)) {
+ if (!ssl_has_private_key(hs->config)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
@@ -845,6 +861,7 @@
// Determine the signature algorithm.
uint16_t signature_algorithm;
if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
return ssl_hs_error;
}
if (ssl_protocol_version(ssl) >= TLS1_2_VERSION) {
@@ -900,15 +917,14 @@
SSL3_MT_CERTIFICATE_REQUEST) ||
!CBB_add_u8_length_prefixed(&body, &cert_types) ||
!CBB_add_u8(&cert_types, SSL3_CT_RSA_SIGN) ||
- (ssl_protocol_version(ssl) >= TLS1_VERSION &&
- !CBB_add_u8(&cert_types, TLS_CT_ECDSA_SIGN)) ||
+ !CBB_add_u8(&cert_types, TLS_CT_ECDSA_SIGN) ||
// TLS 1.2 has no way to specify different signature algorithms for
// certificates and the online signature, so emit the more restrictive
// certificate list.
(ssl_protocol_version(ssl) >= TLS1_2_VERSION &&
(!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) ||
!tls12_add_verify_sigalgs(ssl, &sigalgs_cbb, true /* certs */))) ||
- !ssl_add_client_CA_list(ssl, &body) ||
+ !ssl_add_client_CA_list(hs, &body) ||
!ssl_add_message_cbb(ssl, cbb.get())) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return ssl_hs_error;
@@ -929,8 +945,8 @@
static enum ssl_hs_wait_t do_read_client_certificate(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- if (ssl->handback && hs->new_cipher->algorithm_mkey == SSL_kECDHE) {
- return ssl_hs_handback;
+ if (hs->handback && hs->new_cipher->algorithm_mkey == SSL_kECDHE) {
+ return ssl_hs_handback;
}
if (!hs->cert_request) {
hs->state = state12_verify_client_certificate;
@@ -942,26 +958,7 @@
return ssl_hs_read_message;
}
- if (msg.type != SSL3_MT_CERTIFICATE) {
- if (ssl->version == SSL3_VERSION &&
- msg.type == SSL3_MT_CLIENT_KEY_EXCHANGE) {
- // In SSL 3.0, the Certificate message is omitted to signal no
- // certificate.
- if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
- return ssl_hs_error;
- }
-
- // OpenSSL returns X509_V_OK when no certificates are received. This is
- // classed by them as a bug, but it's assumed by at least NGINX.
- hs->new_session->verify_result = X509_V_OK;
- hs->state = state12_verify_client_certificate;
- return ssl_hs_ok;
- }
-
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+ if (!ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE)) {
return ssl_hs_error;
}
@@ -971,17 +968,14 @@
CBS certificate_msg = msg.body;
uint8_t alert = SSL_AD_DECODE_ERROR;
- UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
- if (!ssl_parse_cert_chain(&alert, &chain, &hs->peer_pubkey,
- ssl->retain_only_sha256_of_client_certs
+ if (!ssl_parse_cert_chain(&alert, &hs->new_session->certs, &hs->peer_pubkey,
+ hs->config->retain_only_sha256_of_client_certs
? hs->new_session->peer_sha256
- : NULL,
+ : nullptr,
&certificate_msg, ssl->ctx->pool)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
- sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
- hs->new_session->certs = chain.release();
if (CBS_len(&certificate_msg) != 0 ||
!ssl->ctx->x509_method->session_cache_objects(hs->new_session.get())) {
@@ -990,19 +984,11 @@
return ssl_hs_error;
}
- if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0) {
+ if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0) {
// No client certificate so the handshake buffer may be discarded.
hs->transcript.FreeBuffer();
- // In SSL 3.0, sending no certificate is signaled by omitting the
- // Certificate message.
- if (ssl->version == SSL3_VERSION) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED);
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
- return ssl_hs_error;
- }
-
- if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+ if (hs->config->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
// Fail for TLS only if we required a certificate
OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
@@ -1012,7 +998,7 @@
// OpenSSL returns X509_V_OK when no certificates are received. This is
// classed by them as a bug, but it's assumed by at least NGINX.
hs->new_session->verify_result = X509_V_OK;
- } else if (ssl->retain_only_sha256_of_client_certs) {
+ } else if (hs->config->retain_only_sha256_of_client_certs) {
// The hash will have been filled in.
hs->new_session->peer_sha256_valid = 1;
}
@@ -1023,7 +1009,7 @@
}
static enum ssl_hs_wait_t do_verify_client_certificate(SSL_HANDSHAKE *hs) {
- if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) > 0) {
+ if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) > 0) {
switch (ssl_verify_peer_cert(hs)) {
case ssl_verify_ok:
break;
@@ -1072,28 +1058,25 @@
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
-
- if (!CBS_strdup(&psk_identity, &hs->new_session->psk_identity)) {
+ char *raw = nullptr;
+ if (!CBS_strdup(&psk_identity, &raw)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
+ hs->new_session->psk_identity.reset(raw);
}
// Depending on the key exchange method, compute |premaster_secret|.
Array<uint8_t> premaster_secret;
if (alg_k & SSL_kRSA) {
CBS encrypted_premaster_secret;
- if (ssl->version > SSL3_VERSION) {
- if (!CBS_get_u16_length_prefixed(&client_key_exchange,
- &encrypted_premaster_secret) ||
- CBS_len(&client_key_exchange) != 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- return ssl_hs_error;
- }
- } else {
- encrypted_premaster_secret = client_key_exchange;
+ if (!CBS_get_u16_length_prefixed(&client_key_exchange,
+ &encrypted_premaster_secret) ||
+ CBS_len(&client_key_exchange) != 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ return ssl_hs_error;
}
// Allocate a buffer large enough for an RSA decryption.
@@ -1187,7 +1170,7 @@
// For a PSK cipher suite, the actual pre-master secret is combined with the
// pre-shared key.
if (alg_a & SSL_aPSK) {
- if (ssl->psk_server_callback == NULL) {
+ if (hs->config->psk_server_callback == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
@@ -1195,8 +1178,8 @@
// Look up the key for the identity.
uint8_t psk[PSK_MAX_PSK_LEN];
- unsigned psk_len = ssl->psk_server_callback(
- ssl, hs->new_session->psk_identity, psk, sizeof(psk));
+ unsigned psk_len = hs->config->psk_server_callback(
+ ssl, hs->new_session->psk_identity.get(), psk, sizeof(psk));
if (psk_len > PSK_MAX_PSK_LEN) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
@@ -1300,29 +1283,9 @@
return ssl_hs_error;
}
- bool sig_ok;
- // The SSL3 construction for CertificateVerify does not decompose into a
- // single final digest and signature, and must be special-cased.
- if (ssl_protocol_version(ssl) == SSL3_VERSION) {
- uint8_t digest[EVP_MAX_MD_SIZE];
- size_t digest_len;
- if (!hs->transcript.GetSSL3CertVerifyHash(
- digest, &digest_len, hs->new_session.get(), signature_algorithm)) {
- return ssl_hs_error;
- }
-
- UniquePtr<EVP_PKEY_CTX> pctx(
- EVP_PKEY_CTX_new(hs->peer_pubkey.get(), nullptr));
- sig_ok = pctx &&
- EVP_PKEY_verify_init(pctx.get()) &&
- EVP_PKEY_verify(pctx.get(), CBS_data(&signature),
- CBS_len(&signature), digest, digest_len);
- } else {
- sig_ok =
- ssl_public_key_verify(ssl, signature, signature_algorithm,
- hs->peer_pubkey.get(), hs->transcript.buffer());
- }
-
+ bool sig_ok =
+ ssl_public_key_verify(ssl, signature, signature_algorithm,
+ hs->peer_pubkey.get(), hs->transcript.buffer());
#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
sig_ok = true;
ERR_clear_error();
@@ -1346,6 +1309,9 @@
}
static enum ssl_hs_wait_t do_read_change_cipher_spec(SSL_HANDSHAKE *hs) {
+ if (hs->handback && hs->ssl->session != NULL) {
+ return ssl_hs_handback;
+ }
hs->state = state12_process_change_cipher_spec;
return ssl_hs_read_change_cipher_spec;
}
@@ -1398,7 +1364,7 @@
static enum ssl_hs_wait_t do_read_channel_id(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- if (!ssl->s3->tlsext_channel_id_valid) {
+ if (!ssl->s3->channel_id_valid) {
hs->state = state12_read_client_finished;
return ssl_hs_ok;
}
@@ -1435,7 +1401,7 @@
// If this is a full handshake with ChannelID then record the handshake
// hashes in |hs->new_session| in case we need them to verify a
// ChannelID signature on a resumption of this session in the future.
- if (ssl->session == NULL && ssl->s3->tlsext_channel_id_valid &&
+ if (ssl->session == NULL && ssl->s3->channel_id_valid &&
!tls1_record_handshake_hashes_for_channel_id(hs)) {
return ssl_hs_error;
}
@@ -1456,7 +1422,8 @@
} else {
// We are renewing an existing session. Duplicate the session to adjust
// the timeout.
- session_copy = SSL_SESSION_dup(ssl->session, SSL_SESSION_INCLUDE_NONAUTH);
+ session_copy =
+ SSL_SESSION_dup(ssl->session.get(), SSL_SESSION_INCLUDE_NONAUTH);
if (!session_copy) {
return ssl_hs_error;
}
@@ -1471,7 +1438,7 @@
SSL3_MT_NEW_SESSION_TICKET) ||
!CBB_add_u32(&body, session->timeout) ||
!CBB_add_u16_length_prefixed(&body, &ticket) ||
- !ssl_encrypt_ticket(ssl, &ticket, session) ||
+ !ssl_encrypt_ticket(hs, &ticket, session) ||
!ssl_add_message_cbb(ssl, cbb.get())) {
return ssl_hs_error;
}
@@ -1494,25 +1461,24 @@
static enum ssl_hs_wait_t do_finish_server_handshake(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- if (ssl->handback) {
+ if (hs->handback) {
return ssl_hs_handback;
}
ssl->method->on_handshake_complete(ssl);
// If we aren't retaining peer certificates then we can discard it now.
- if (hs->new_session != NULL && ssl->retain_only_sha256_of_client_certs) {
- sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
- hs->new_session->certs = NULL;
+ if (hs->new_session != NULL &&
+ hs->config->retain_only_sha256_of_client_certs) {
+ hs->new_session->certs.reset();
ssl->ctx->x509_method->session_clear(hs->new_session.get());
}
if (ssl->session != NULL) {
- SSL_SESSION_up_ref(ssl->session);
- ssl->s3->established_session.reset(ssl->session);
+ ssl->s3->established_session = UpRef(ssl->session);
} else {
ssl->s3->established_session = std::move(hs->new_session);
- ssl->s3->established_session->not_resumable = 0;
+ ssl->s3->established_session->not_resumable = false;
}
hs->handshake_finalized = true;
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index b258589..cc3e075 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -175,8 +175,10 @@
namespace bssl {
+struct SSL_CONFIG;
struct SSL_HANDSHAKE;
struct SSL_PROTOCOL_METHOD;
+struct SSL_X509_METHOD;
// C++ utilities.
@@ -371,12 +373,16 @@
// ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
// minimum and maximum enabled protocol versions, respectively.
-bool ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+bool ssl_get_version_range(const SSL_HANDSHAKE *hs, uint16_t *out_min_version,
uint16_t *out_max_version);
// ssl_supports_version returns whether |hs| supports |version|.
bool ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version);
+// ssl_method_supports_version returns whether |method| supports |version|.
+bool ssl_method_supports_version(const SSL_PROTOCOL_METHOD *method,
+ uint16_t version);
+
// ssl_add_supported_versions writes the supported versions of |hs| to |cbb|, in
// decreasing preference order.
bool ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb);
@@ -447,10 +453,8 @@
// Bits for |algorithm_mac| (symmetric authentication).
#define SSL_SHA1 0x00000001u
-#define SSL_SHA256 0x00000002u
-#define SSL_SHA384 0x00000004u
// SSL_AEAD is set for all AEADs.
-#define SSL_AEAD 0x00000008u
+#define SSL_AEAD 0x00000002u
// Bits for |algorithm_prf| (handshake digest).
#define SSL_HANDSHAKE_MAC_DEFAULT 0x1
@@ -520,7 +524,7 @@
// true on success and false on failure. If |strict| is true, nonsense will be
// rejected. If false, nonsense will be silently ignored. An empty result is
// considered an error regardless of |strict|.
-bool ssl_create_cipher_list(SSLCipherPreferenceList **out_cipher_list,
+bool ssl_create_cipher_list(UniquePtr<SSLCipherPreferenceList> *out_cipher_list,
const char *rule_str, bool strict);
// ssl_cipher_get_value returns the cipher suite id of |cipher|.
@@ -602,14 +606,6 @@
// the number of bytes written. Otherwise, it returns false.
bool GetHash(uint8_t *out, size_t *out_len);
- // GetSSL3CertVerifyHash writes the SSL 3.0 CertificateVerify hash into the
- // bytes pointed to by |out| and writes the number of bytes to
- // |*out_len|. |out| must have room for |EVP_MAX_MD_SIZE| bytes. It returns
- // one on success and zero on failure.
- bool GetSSL3CertVerifyHash(uint8_t *out, size_t *out_len,
- const SSL_SESSION *session,
- uint16_t signature_algorithm);
-
// GetFinishedMAC computes the MAC for the Finished message into the bytes
// pointed by |out| and writes the number of bytes to |*out_len|. |out| must
// have room for |EVP_MAX_MD_SIZE| bytes. It returns true on success and false
@@ -620,12 +616,8 @@
private:
// buffer_, if non-null, contains the handshake transcript.
UniquePtr<BUF_MEM> buffer_;
- // hash, if initialized with an |EVP_MD|, maintains the handshake hash. For
- // TLS 1.1 and below, it is the SHA-1 half.
+ // hash, if initialized with an |EVP_MD|, maintains the handshake hash.
ScopedEVP_MD_CTX hash_;
- // md5, if initialized with an |EVP_MD|, maintains the MD5 half of the
- // handshake hash for TLS 1.1 and below.
- ScopedEVP_MD_CTX md5_;
};
// tls1_prf computes the PRF function for |ssl|. It fills |out|, using |secret|
@@ -771,9 +763,6 @@
// omit_length_in_ad_ is true if the length should be omitted in the
// AEAD's ad parameter.
bool omit_length_in_ad_ : 1;
- // omit_version_in_ad_ is true if the version should be omitted
- // in the AEAD's ad parameter.
- bool omit_version_in_ad_ : 1;
// omit_ad_ is true if the AEAD's ad parameter should be omitted.
bool omit_ad_ : 1;
// ad_is_header_ is true if the AEAD's ad parameter is the record header.
@@ -908,9 +897,9 @@
// Private key operations.
-// ssl_has_private_key returns one if |ssl| has a private key
-// configured and zero otherwise.
-int ssl_has_private_key(const SSL *ssl);
+// ssl_has_private_key returns one if |cfg| has a private key configured and
+// zero otherwise.
+int ssl_has_private_key(const SSL_CONFIG *cfg);
// ssl_private_key_* perform the corresponding operation on
// |SSL_PRIVATE_KEY_METHOD|. If there is a custom private key configured, they
@@ -940,36 +929,6 @@
Span<const uint8_t> in);
-// Custom extensions
-
-} // namespace bssl
-
-// |SSL_CUSTOM_EXTENSION| is a structure that contains information about
-// custom-extension callbacks. It is defined unnamespaced for compatibility with
-// |STACK_OF(SSL_CUSTOM_EXTENSION)|.
-typedef struct ssl_custom_extension {
- SSL_custom_ext_add_cb add_callback;
- void *add_arg;
- SSL_custom_ext_free_cb free_callback;
- SSL_custom_ext_parse_cb parse_callback;
- void *parse_arg;
- uint16_t value;
-} SSL_CUSTOM_EXTENSION;
-
-DEFINE_STACK_OF(SSL_CUSTOM_EXTENSION)
-
-namespace bssl {
-
-void SSL_CUSTOM_EXTENSION_free(SSL_CUSTOM_EXTENSION *custom_extension);
-
-int custom_ext_add_clienthello(SSL_HANDSHAKE *hs, CBB *extensions);
-int custom_ext_parse_serverhello(SSL_HANDSHAKE *hs, int *out_alert,
- uint16_t value, const CBS *extension);
-int custom_ext_parse_clienthello(SSL_HANDSHAKE *hs, int *out_alert,
- uint16_t value, const CBS *extension);
-int custom_ext_add_serverhello(SSL_HANDSHAKE *hs, CBB *extensions);
-
-
// Key shares.
// SSLKeyShare abstracts over Diffie-Hellman-like key exchanges.
@@ -1067,6 +1026,9 @@
// |tls_has_unprocessed_handshake_data| for DTLS.
bool dtls_has_unprocessed_handshake_data(const SSL *ssl);
+// tls_flush_pending_hs_data flushes any handshake plaintext data.
+bool tls_flush_pending_hs_data(SSL *ssl);
+
struct DTLS_OUTGOING_MESSAGE {
DTLS_OUTGOING_MESSAGE() {}
DTLS_OUTGOING_MESSAGE(const DTLS_OUTGOING_MESSAGE &) = delete;
@@ -1175,7 +1137,7 @@
// ssl_has_certificate returns one if a certificate and private key are
// configured and zero otherwise.
-int ssl_has_certificate(const SSL *ssl);
+int ssl_has_certificate(const SSL_CONFIG *cfg);
// ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
// by a TLS Certificate message. On success, it advances |cbs| and returns
@@ -1194,10 +1156,10 @@
uint8_t *out_leaf_sha256, CBS *cbs,
CRYPTO_BUFFER_POOL *pool);
-// ssl_add_cert_chain adds |ssl|'s certificate chain to |cbb| in the format used
-// by a TLS Certificate message. If there is no certificate chain, it emits an
-// empty certificate list. It returns one on success and zero on error.
-int ssl_add_cert_chain(SSL *ssl, CBB *cbb);
+// ssl_add_cert_chain adds |hs->ssl|'s certificate chain to |cbb| in the format
+// used by a TLS Certificate message. If there is no certificate chain, it emits
+// an empty certificate list. It returns one on success and zero on error.
+int ssl_add_cert_chain(SSL_HANDSHAKE *hs, CBB *cbb);
// ssl_cert_check_digital_signature_key_usage parses the DER-encoded, X.509
// certificate in |in| and returns one if doesn't specify a key usage or, if it
@@ -1219,12 +1181,12 @@
CBS *cbs);
// ssl_has_client_CAs returns there are configured CAs.
-bool ssl_has_client_CAs(SSL *ssl);
+bool ssl_has_client_CAs(const SSL_CONFIG *cfg);
// ssl_add_client_CA_list adds the configured CA list to |cbb| in the format
// used by a TLS CertificateRequest message. It returns one on success and zero
// on error.
-int ssl_add_client_CA_list(SSL *ssl, CBB *cbb);
+int ssl_add_client_CA_list(SSL_HANDSHAKE *hs, CBB *cbb);
// ssl_check_leaf_certificate returns one if |pkey| and |leaf| are suitable as
// a server's leaf certificate for |hs|. Otherwise, it returns zero and pushes
@@ -1371,6 +1333,14 @@
state12_done,
};
+// handback_t lists the points in the state machine where a handback can occur.
+// These are the different points at which key material is no longer needed.
+enum handback_t {
+ handback_after_session_resumption,
+ handback_after_ecdhe,
+ handback_after_handshake,
+};
+
struct SSL_HANDSHAKE {
explicit SSL_HANDSHAKE(SSL *ssl);
~SSL_HANDSHAKE();
@@ -1379,6 +1349,9 @@
// ssl is a non-owning pointer to the parent |SSL| object.
SSL *ssl;
+ // config is a non-owning pointer to the handshake configuration.
+ SSL_CONFIG *config;
+
// wait contains the operation the handshake is currently blocking on or
// |ssl_hs_ok| if none.
enum ssl_hs_wait_t wait = ssl_hs_ok;
@@ -1418,17 +1391,6 @@
uint32_t received;
} extensions;
- union {
- // sent is a bitset where the bits correspond to elements of
- // |client_custom_extensions| in the |SSL_CTX|. Each bit is set if that
- // extension was sent in a ClientHello. It's not used by servers.
- uint16_t sent = 0;
- // received is a bitset, like |sent|, but is used by servers to record
- // which custom extensions were received from a client. The bits here
- // correspond to |server_custom_extensions|.
- uint16_t received;
- } custom_extensions;
-
// retry_group is the group ID selected by the server in HelloRetryRequest in
// TLS 1.3.
uint16_t retry_group = 0;
@@ -1472,6 +1434,11 @@
// sent.
uint16_t negotiated_token_binding_version;
+ // cert_compression_alg_id, for a server, contains the negotiated certificate
+ // compression algorithm for this client. It is only valid if
+ // |cert_compression_negotiated| is true.
+ uint16_t cert_compression_alg_id;
+
// server_params, in a TLS 1.2 server, stores the ServerKeyExchange
// parameters. It has client and server randoms prepended for signing
// convenience.
@@ -1524,8 +1491,6 @@
bool received_hello_retry_request:1;
bool sent_hello_retry_request:1;
- bool received_custom_extension:1;
-
// handshake_finalized is true once the handshake has completed, at which
// point accessors should use the established state.
bool handshake_finalized:1;
@@ -1586,6 +1551,14 @@
// grease_seeded is true if |grease_seed| has been initialized.
bool grease_seeded:1;
+ // handback indicates that a server should pause the handshake after
+ // finishing operations that require private key material, in such a way that
+ // |SSL_get_error| returns |SSL_HANDBACK|. It is set by |SSL_apply_handoff|.
+ bool handback:1;
+
+ // cert_compression_negotiated is true iff |cert_compression_alg_id| is valid.
+ bool cert_compression_negotiated:1;
+
// client_version is the value sent or received in the ClientHello version.
uint16_t client_version = 0;
@@ -1678,8 +1651,8 @@
bool ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out);
// ssl_is_sct_list_valid does a shallow parse of the SCT list in |contents| and
-// returns one iff it's valid.
-int ssl_is_sct_list_valid(const CBS *contents);
+// returns whether it's valid.
+bool ssl_is_sct_list_valid(const CBS *contents);
int ssl_write_client_hello(SSL_HANDSHAKE *hs);
@@ -1698,8 +1671,9 @@
enum ssl_cert_verify_context_t cert_verify_context);
// ssl_is_alpn_protocol_allowed returns whether |protocol| is a valid server
-// selection for |ssl|'s client preferences.
-bool ssl_is_alpn_protocol_allowed(const SSL *ssl, Span<const uint8_t> protocol);
+// selection for |hs->ssl|'s client preferences.
+bool ssl_is_alpn_protocol_allowed(const SSL_HANDSHAKE *hs,
+ Span<const uint8_t> protocol);
// ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns
// true on successful negotiation or if nothing was negotiated. It returns false
@@ -1727,8 +1701,7 @@
enum ssl_hs_wait_t ssl_get_finished(SSL_HANDSHAKE *hs);
bool ssl_send_finished(SSL_HANDSHAKE *hs);
-bool ssl_output_cert_chain(SSL *ssl);
-
+bool ssl_output_cert_chain(SSL_HANDSHAKE *hs);
// SSLKEYLOGFILE functions.
@@ -1740,14 +1713,14 @@
// ClientHello functions.
-int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out,
- const SSLMessage &msg);
+bool ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out,
+ const SSLMessage &msg);
-int ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
- CBS *out, uint16_t extension_type);
+bool ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
+ CBS *out, uint16_t extension_type);
-int ssl_client_cipher_list_contains_cipher(const SSL_CLIENT_HELLO *client_hello,
- uint16_t id);
+bool ssl_client_cipher_list_contains_cipher(
+ const SSL_CLIENT_HELLO *client_hello, uint16_t id);
// GREASE.
@@ -1869,9 +1842,6 @@
// ticket key. Only sessions with a matching value will be accepted.
uint8_t sid_ctx_length = 0;
uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH] = {0};
-
- // If enable_early_data is true, early data can be sent and accepted.
- bool enable_early_data:1;
};
// |SSL_PROTOCOL_METHOD| abstracts between TLS and DTLS.
@@ -1951,6 +1921,62 @@
size_t *out_consumed, uint8_t *out_alert,
Span<uint8_t> in);
+struct SSL_X509_METHOD {
+ // check_client_CA_list returns one if |names| is a good list of X.509
+ // distinguished names and zero otherwise. This is used to ensure that we can
+ // reject unparsable values at handshake time when using crypto/x509.
+ int (*check_client_CA_list)(STACK_OF(CRYPTO_BUFFER) *names);
+
+ // cert_clear frees and NULLs all X509 certificate-related state.
+ void (*cert_clear)(CERT *cert);
+ // cert_free frees all X509-related state.
+ void (*cert_free)(CERT *cert);
+ // cert_flush_cached_chain drops any cached |X509|-based certificate chain
+ // from |cert|.
+ // cert_dup duplicates any needed fields from |cert| to |new_cert|.
+ void (*cert_dup)(CERT *new_cert, const CERT *cert);
+ void (*cert_flush_cached_chain)(CERT *cert);
+ // cert_flush_cached_chain drops any cached |X509|-based leaf certificate
+ // from |cert|.
+ void (*cert_flush_cached_leaf)(CERT *cert);
+
+ // session_cache_objects fills out |sess->x509_peer| and |sess->x509_chain|
+ // from |sess->certs| and erases |sess->x509_chain_without_leaf|. It returns
+ // one on success or zero on error.
+ int (*session_cache_objects)(SSL_SESSION *session);
+ // session_dup duplicates any needed fields from |session| to |new_session|.
+ // It returns one on success or zero on error.
+ int (*session_dup)(SSL_SESSION *new_session, const SSL_SESSION *session);
+ // session_clear frees any X509-related state from |session|.
+ void (*session_clear)(SSL_SESSION *session);
+ // session_verify_cert_chain verifies the certificate chain in |session|,
+ // sets |session->verify_result| and returns one on success or zero on
+ // error.
+ int (*session_verify_cert_chain)(SSL_SESSION *session, SSL_HANDSHAKE *ssl,
+ uint8_t *out_alert);
+
+ // hs_flush_cached_ca_names drops any cached |X509_NAME|s from |hs|.
+ void (*hs_flush_cached_ca_names)(SSL_HANDSHAKE *hs);
+ // ssl_new does any neccessary initialisation of |hs|. It returns one on
+ // success or zero on error.
+ int (*ssl_new)(SSL_HANDSHAKE *hs);
+ // ssl_free frees anything created by |ssl_new|.
+ void (*ssl_config_free)(SSL_CONFIG *cfg);
+ // ssl_flush_cached_client_CA drops any cached |X509_NAME|s from |ssl|.
+ void (*ssl_flush_cached_client_CA)(SSL_CONFIG *cfg);
+ // ssl_auto_chain_if_needed runs the deprecated auto-chaining logic if
+ // necessary. On success, it updates |ssl|'s certificate configuration as
+ // needed and returns one. Otherwise, it returns zero.
+ int (*ssl_auto_chain_if_needed)(SSL_HANDSHAKE *hs);
+ // ssl_ctx_new does any neccessary initialisation of |ctx|. It returns one on
+ // success or zero on error.
+ int (*ssl_ctx_new)(SSL_CTX *ctx);
+ // ssl_ctx_free frees anything created by |ssl_ctx_new|.
+ void (*ssl_ctx_free)(SSL_CTX *ctx);
+ // ssl_ctx_flush_cached_client_CA drops any cached |X509_NAME|s from |ctx|.
+ void (*ssl_ctx_flush_cached_client_CA)(SSL_CTX *ssl);
+};
+
// ssl_crypto_x509_method provides the |SSL_X509_METHOD| functions using
// crypto/x509.
extern const SSL_X509_METHOD ssl_crypto_x509_method;
@@ -1959,302 +1985,35 @@
// crypto/x509.
extern const SSL_X509_METHOD ssl_noop_x509_method;
-struct tlsext_ticket_key {
+struct TicketKey {
static constexpr bool kAllowUniquePtr = true;
- uint8_t name[SSL_TICKET_KEY_NAME_LEN];
- uint8_t hmac_key[16];
- uint8_t aes_key[16];
+ uint8_t name[SSL_TICKET_KEY_NAME_LEN] = {0};
+ uint8_t hmac_key[16] = {0};
+ uint8_t aes_key[16] = {0};
// next_rotation_tv_sec is the time (in seconds from the epoch) when the
// current key should be superseded by a new key, or the time when a previous
// key should be dropped. If zero, then the key should not be automatically
// rotated.
- uint64_t next_rotation_tv_sec;
+ uint64_t next_rotation_tv_sec = 0;
+};
+
+struct CertCompressionAlg {
+ static constexpr bool kAllowUniquePtr = true;
+
+ ssl_cert_compression_func_t compress = nullptr;
+ ssl_cert_decompression_func_t decompress = nullptr;
+ uint16_t alg_id = 0;
};
} // namespace bssl
DECLARE_LHASH_OF(SSL_SESSION)
+DEFINE_NAMED_STACK_OF(CertCompressionAlg, bssl::CertCompressionAlg);
+
namespace bssl {
-// SSLContext backs the public |SSL_CTX| type. Due to compatibility constraints,
-// it is a base class for |ssl_ctx_st|.
-struct SSLContext {
- const SSL_PROTOCOL_METHOD *method;
- const SSL_X509_METHOD *x509_method;
-
- // lock is used to protect various operations on this object.
- CRYPTO_MUTEX lock;
-
- // conf_max_version is the maximum acceptable protocol version configured by
- // |SSL_CTX_set_max_proto_version|. Note this version is normalized in DTLS
- // and is further constrainted by |SSL_OP_NO_*|.
- uint16_t conf_max_version;
-
- // conf_min_version is the minimum acceptable protocol version configured by
- // |SSL_CTX_set_min_proto_version|. Note this version is normalized in DTLS
- // and is further constrainted by |SSL_OP_NO_*|.
- uint16_t conf_min_version;
-
- // tls13_variant is the variant of TLS 1.3 we are using for this
- // configuration.
- enum tls13_variant_t tls13_variant;
-
- SSLCipherPreferenceList *cipher_list;
-
- X509_STORE *cert_store;
- LHASH_OF(SSL_SESSION) *sessions;
- // Most session-ids that will be cached, default is
- // SSL_SESSION_CACHE_MAX_SIZE_DEFAULT. 0 is unlimited.
- unsigned long session_cache_size;
- SSL_SESSION *session_cache_head;
- SSL_SESSION *session_cache_tail;
-
- // handshakes_since_cache_flush is the number of successful handshakes since
- // the last cache flush.
- int handshakes_since_cache_flush;
-
- // This can have one of 2 values, ored together,
- // SSL_SESS_CACHE_CLIENT,
- // SSL_SESS_CACHE_SERVER,
- // Default is SSL_SESSION_CACHE_SERVER, which means only
- // SSL_accept which cache SSL_SESSIONS.
- int session_cache_mode;
-
- // session_timeout is the default lifetime for new sessions in TLS 1.2 and
- // earlier, in seconds.
- uint32_t session_timeout;
-
- // session_psk_dhe_timeout is the default lifetime for new sessions in TLS
- // 1.3, in seconds.
- uint32_t session_psk_dhe_timeout;
-
- // If this callback is not null, it will be called each time a session id is
- // added to the cache. If this function returns 1, it means that the
- // callback will do a SSL_SESSION_free() when it has finished using it.
- // Otherwise, on 0, it means the callback has finished with it. If
- // remove_session_cb is not null, it will be called when a session-id is
- // removed from the cache. After the call, OpenSSL will SSL_SESSION_free()
- // it.
- int (*new_session_cb)(SSL *ssl, SSL_SESSION *sess);
- void (*remove_session_cb)(SSL_CTX *ctx, SSL_SESSION *sess);
- SSL_SESSION *(*get_session_cb)(SSL *ssl, const uint8_t *data, int len,
- int *copy);
-
- CRYPTO_refcount_t references;
-
- // if defined, these override the X509_verify_cert() calls
- int (*app_verify_callback)(X509_STORE_CTX *store_ctx, void *arg);
- void *app_verify_arg;
-
- enum ssl_verify_result_t (*custom_verify_callback)(SSL *ssl,
- uint8_t *out_alert);
-
- // Default password callback.
- pem_password_cb *default_passwd_callback;
-
- // Default password callback user data.
- void *default_passwd_callback_userdata;
-
- // get client cert callback
- int (*client_cert_cb)(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey);
-
- // get channel id callback
- void (*channel_id_cb)(SSL *ssl, EVP_PKEY **out_pkey);
-
- CRYPTO_EX_DATA ex_data;
-
- // custom_*_extensions stores any callback sets for custom extensions. Note
- // that these pointers will be NULL if the stack would otherwise be empty.
- STACK_OF(SSL_CUSTOM_EXTENSION) *client_custom_extensions;
- STACK_OF(SSL_CUSTOM_EXTENSION) *server_custom_extensions;
-
- // Default values used when no per-SSL value is defined follow
-
- void (*info_callback)(const SSL *ssl, int type, int value);
-
- // what we put in client cert requests
- STACK_OF(CRYPTO_BUFFER) *client_CA;
-
- // cached_x509_client_CA is a cache of parsed versions of the elements of
- // |client_CA|.
- STACK_OF(X509_NAME) *cached_x509_client_CA;
-
-
- // Default values to use in SSL structures follow (these are copied by
- // SSL_new)
-
- uint32_t options;
- uint32_t mode;
- uint32_t max_cert_list;
-
- CERT *cert;
-
- // callback that allows applications to peek at protocol messages
- void (*msg_callback)(int write_p, int version, int content_type,
- const void *buf, size_t len, SSL *ssl, void *arg);
- void *msg_callback_arg;
-
- int verify_mode;
- int (*default_verify_callback)(
- int ok, X509_STORE_CTX *ctx); // called 'verify_callback' in the SSL
-
- X509_VERIFY_PARAM *param;
-
- // select_certificate_cb is called before most ClientHello processing and
- // before the decision whether to resume a session is made. See
- // |ssl_select_cert_result_t| for details of the return values.
- enum ssl_select_cert_result_t (*select_certificate_cb)(
- const SSL_CLIENT_HELLO *);
-
- // dos_protection_cb is called once the resumption decision for a ClientHello
- // has been made. It returns one to continue the handshake or zero to
- // abort.
- int (*dos_protection_cb) (const SSL_CLIENT_HELLO *);
-
- // Maximum amount of data to send in one fragment. actual record size can be
- // more than this due to padding and MAC overheads.
- uint16_t max_send_fragment;
-
- // TLS extensions servername callback
- int (*tlsext_servername_callback)(SSL *, int *, void *);
- void *tlsext_servername_arg;
-
- // RFC 4507 session ticket keys. |tlsext_ticket_key_current| may be NULL
- // before the first handshake and |tlsext_ticket_key_prev| may be NULL at any
- // time. Automatically generated ticket keys are rotated as needed at
- // handshake time. Hence, all access must be synchronized through |lock|.
- struct tlsext_ticket_key *tlsext_ticket_key_current;
- struct tlsext_ticket_key *tlsext_ticket_key_prev;
-
- // Callback to support customisation of ticket key setting
- int (*tlsext_ticket_key_cb)(SSL *ssl, uint8_t *name, uint8_t *iv,
- EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc);
-
- // Server-only: psk_identity_hint is the default identity hint to send in
- // PSK-based key exchanges.
- char *psk_identity_hint;
-
- unsigned int (*psk_client_callback)(SSL *ssl, const char *hint,
- char *identity,
- unsigned int max_identity_len,
- uint8_t *psk, unsigned int max_psk_len);
- unsigned int (*psk_server_callback)(SSL *ssl, const char *identity,
- uint8_t *psk, unsigned int max_psk_len);
-
-
- // Next protocol negotiation information
- // (for experimental NPN extension).
-
- // For a server, this contains a callback function by which the set of
- // advertised protocols can be provided.
- int (*next_protos_advertised_cb)(SSL *ssl, const uint8_t **out,
- unsigned *out_len, void *arg);
- void *next_protos_advertised_cb_arg;
- // For a client, this contains a callback function that selects the
- // next protocol from the list provided by the server.
- int (*next_proto_select_cb)(SSL *ssl, uint8_t **out, uint8_t *out_len,
- const uint8_t *in, unsigned in_len, void *arg);
- void *next_proto_select_cb_arg;
-
- // ALPN information
- // (we are in the process of transitioning from NPN to ALPN.)
-
- // For a server, this contains a callback function that allows the
- // server to select the protocol for the connection.
- // out: on successful return, this must point to the raw protocol
- // name (without the length prefix).
- // outlen: on successful return, this contains the length of |*out|.
- // in: points to the client's list of supported protocols in
- // wire-format.
- // inlen: the length of |in|.
- int (*alpn_select_cb)(SSL *ssl, const uint8_t **out, uint8_t *out_len,
- const uint8_t *in, unsigned in_len, void *arg);
- void *alpn_select_cb_arg;
-
- // For a client, this contains the list of supported protocols in wire
- // format.
- uint8_t *alpn_client_proto_list;
- unsigned alpn_client_proto_list_len;
-
- // SRTP profiles we are willing to do from RFC 5764
- STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles;
-
- // Supported group values inherited by SSL structure
- size_t supported_group_list_len;
- uint16_t *supported_group_list;
-
- // The client's Channel ID private key.
- EVP_PKEY *tlsext_channel_id_private;
-
- // keylog_callback, if not NULL, is the key logging callback. See
- // |SSL_CTX_set_keylog_callback|.
- void (*keylog_callback)(const SSL *ssl, const char *line);
-
- // current_time_cb, if not NULL, is the function to use to get the current
- // time. It sets |*out_clock| to the current time. The |ssl| argument is
- // always NULL. See |SSL_CTX_set_current_time_cb|.
- void (*current_time_cb)(const SSL *ssl, struct timeval *out_clock);
-
- // pool is used for all |CRYPTO_BUFFER|s in case we wish to share certificate
- // memory.
- CRYPTO_BUFFER_POOL *pool;
-
- // ticket_aead_method contains function pointers for opening and sealing
- // session tickets.
- const SSL_TICKET_AEAD_METHOD *ticket_aead_method;
-
- // verify_sigalgs, if not empty, is the set of signature algorithms
- // accepted from the peer in decreasing order of preference.
- uint16_t *verify_sigalgs;
- size_t num_verify_sigalgs;
-
- // retain_only_sha256_of_client_certs is true if we should compute the SHA256
- // hash of the peer's certificate and then discard it to save memory and
- // session space. Only effective on the server side.
- bool retain_only_sha256_of_client_certs:1;
-
- // quiet_shutdown is true if the connection should not send a close_notify on
- // shutdown.
- bool quiet_shutdown:1;
-
- // ocsp_stapling_enabled is only used by client connections and indicates
- // whether OCSP stapling will be requested.
- bool ocsp_stapling_enabled:1;
-
- // If true, a client will request certificate timestamps.
- bool signed_cert_timestamps_enabled:1;
-
- // tlsext_channel_id_enabled is whether Channel ID is enabled. For a server,
- // means that we'll accept Channel IDs from clients. For a client, means that
- // we'll advertise support.
- bool tlsext_channel_id_enabled:1;
-
- // grease_enabled is whether draft-davidben-tls-grease-01 is enabled.
- bool grease_enabled:1;
-
- // allow_unknown_alpn_protos is whether the client allows unsolicited ALPN
- // protocols from the peer.
- bool allow_unknown_alpn_protos:1;
-
- // ed25519_enabled is whether Ed25519 is advertised in the handshake.
- bool ed25519_enabled:1;
-
- // rsa_pss_rsae_certs_enabled is whether rsa_pss_rsae_* are supported by the
- // certificate verifier.
- bool rsa_pss_rsae_certs_enabled:1;
-
- // false_start_allowed_without_alpn is whether False Start (if
- // |SSL_MODE_ENABLE_FALSE_START| is enabled) is allowed without ALPN.
- bool false_start_allowed_without_alpn:1;
-
- // handoff indicates that a server should stop after receiving the
- // ClientHello and pause the handshake in such a way that |SSL_get_error|
- // returns |SSL_HANDOFF|.
- bool handoff:1;
-};
-
// An ssl_shutdown_t describes the shutdown state of one end of the connection,
// whether it is alive or has been shutdown via close_notify or fatal alert.
enum ssl_shutdown_t {
@@ -2359,9 +2118,8 @@
// In a client, this means that the server supported Channel ID and that a
// Channel ID was sent. In a server it means that we echoed support for
- // Channel IDs and that tlsext_channel_id will be valid after the
- // handshake.
- bool tlsext_channel_id_valid:1;
+ // Channel IDs and that |channel_id| will be valid after the handshake.
+ bool channel_id_valid:1;
// key_update_pending is true if we have a KeyUpdate acknowledgment
// outstanding.
@@ -2383,6 +2141,11 @@
// hs_buf is the buffer of handshake data to process.
UniquePtr<BUF_MEM> hs_buf;
+ // pending_hs_data contains the pending handshake data that has not yet
+ // been encrypted to |pending_flight|. This allows packing the handshake into
+ // fewer records.
+ UniquePtr<BUF_MEM> pending_hs_data;
+
// pending_flight is the pending outgoing flight. This is used to flush each
// handshake flight in a single write. |write_buffer| must be written out
// before this data.
@@ -2449,10 +2212,10 @@
UniquePtr<char> hostname;
// For a server:
- // If |tlsext_channel_id_valid| is true, then this contains the
+ // If |channel_id_valid| is true, then this contains the
// verified Channel ID from the client: a P256 point, (x,y), where
// each are big-endian values.
- uint8_t tlsext_channel_id[64] = {0};
+ uint8_t channel_id[64] = {0};
// Contains the QUIC transport params received by the peer.
Array<uint8_t> peer_quic_transport_params;
@@ -2580,147 +2343,89 @@
unsigned timeout_duration_ms = 0;
};
-// SSLConnection backs the public |SSL| type. Due to compatibility constraints,
-// it is a base class for |ssl_st|.
-struct SSLConnection {
- // method is the method table corresponding to the current protocol (DTLS or
- // TLS).
- const SSL_PROTOCOL_METHOD *method;
+// SSL_CONFIG contains configuration bits that can be shed after the handshake
+// completes. Objects of this type are not shared; they are unique to a
+// particular |SSL|.
+//
+// See SSL_shed_handshake_config() for more about the conditions under which
+// configuration can be shed.
+struct SSL_CONFIG {
+ static constexpr bool kAllowUniquePtr = true;
- // version is the protocol version.
- uint16_t version;
+ explicit SSL_CONFIG(SSL *ssl_arg);
+ ~SSL_CONFIG();
+
+ // ssl is a non-owning pointer to the parent |SSL| object.
+ SSL *const ssl = nullptr;
// conf_max_version is the maximum acceptable protocol version configured by
// |SSL_set_max_proto_version|. Note this version is normalized in DTLS and is
// further constrainted by |SSL_OP_NO_*|.
- uint16_t conf_max_version;
+ uint16_t conf_max_version = 0;
// conf_min_version is the minimum acceptable protocol version configured by
// |SSL_set_min_proto_version|. Note this version is normalized in DTLS and is
// further constrainted by |SSL_OP_NO_*|.
- uint16_t conf_min_version;
+ uint16_t conf_min_version = 0;
- uint16_t max_send_fragment;
-
- // There are 2 BIO's even though they are normally both the same. This is so
- // data can be read and written to different handlers
-
- BIO *rbio; // used by SSL_read
- BIO *wbio; // used by SSL_write
-
- // do_handshake runs the handshake. On completion, it returns |ssl_hs_ok|.
- // Otherwise, it returns a value corresponding to what operation is needed to
- // progress.
- enum ssl_hs_wait_t (*do_handshake)(SSL_HANDSHAKE *hs);
-
- SSL3_STATE *s3; // SSLv3 variables
- DTLS1_STATE *d1; // DTLSv1 variables
-
- // callback that allows applications to peek at protocol messages
- void (*msg_callback)(int write_p, int version, int content_type,
- const void *buf, size_t len, SSL *ssl, void *arg);
- void *msg_callback_arg;
-
- X509_VERIFY_PARAM *param;
+ X509_VERIFY_PARAM *param = nullptr;
// crypto
- SSLCipherPreferenceList *cipher_list;
-
- // session info
+ UniquePtr<SSLCipherPreferenceList> cipher_list;
// This is used to hold the local certificate used (i.e. the server
// certificate for a server or the client certificate for a client).
- CERT *cert;
-
- // initial_timeout_duration_ms is the default DTLS timeout duration in
- // milliseconds. It's used to initialize the timer any time it's restarted.
- unsigned initial_timeout_duration_ms;
-
- // tls13_variant is the variant of TLS 1.3 we are using for this
- // configuration.
- enum tls13_variant_t tls13_variant;
-
- // session is the configured session to be offered by the client. This session
- // is immutable.
- SSL_SESSION *session;
+ UniquePtr<CERT> cert;
int (*verify_callback)(int ok,
- X509_STORE_CTX *ctx); // fail if callback returns 0
+ X509_STORE_CTX *ctx) =
+ nullptr; // fail if callback returns 0
- enum ssl_verify_result_t (*custom_verify_callback)(SSL *ssl,
- uint8_t *out_alert);
-
- void (*info_callback)(const SSL *ssl, int type, int value);
-
+ enum ssl_verify_result_t (*custom_verify_callback)(
+ SSL *ssl, uint8_t *out_alert) = nullptr;
// Server-only: psk_identity_hint is the identity hint to send in
// PSK-based key exchanges.
- char *psk_identity_hint;
+ UniquePtr<char> psk_identity_hint;
- unsigned int (*psk_client_callback)(SSL *ssl, const char *hint,
- char *identity,
- unsigned int max_identity_len,
- uint8_t *psk, unsigned int max_psk_len);
- unsigned int (*psk_server_callback)(SSL *ssl, const char *identity,
- uint8_t *psk, unsigned int max_psk_len);
-
- SSL_CTX *ctx;
-
- // extra application data
- CRYPTO_EX_DATA ex_data;
+ unsigned (*psk_client_callback)(SSL *ssl, const char *hint, char *identity,
+ unsigned max_identity_len, uint8_t *psk,
+ unsigned max_psk_len) = nullptr;
+ unsigned (*psk_server_callback)(SSL *ssl, const char *identity, uint8_t *psk,
+ unsigned max_psk_len) = nullptr;
// for server side, keep the list of CA_dn we can use
- STACK_OF(CRYPTO_BUFFER) *client_CA;
+ UniquePtr<STACK_OF(CRYPTO_BUFFER)> client_CA;
// cached_x509_client_CA is a cache of parsed versions of the elements of
// |client_CA|.
- STACK_OF(X509_NAME) *cached_x509_client_CA;
+ STACK_OF(X509_NAME) *cached_x509_client_CA = nullptr;
- uint32_t options; // protocol behaviour
- uint32_t mode; // API behaviour
- uint32_t max_cert_list;
- uint16_t dummy_pq_padding_len;
- char *tlsext_hostname;
- size_t supported_group_list_len;
- uint16_t *supported_group_list; // our list
-
- // session_ctx is the |SSL_CTX| used for the session cache and related
- // settings.
- SSL_CTX *session_ctx;
-
- // srtp_profiles is the list of configured SRTP protection profiles for
- // DTLS-SRTP.
- STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles;
+ uint16_t dummy_pq_padding_len = 0;
+ Array<uint16_t> supported_group_list; // our list
// The client's Channel ID private key.
- EVP_PKEY *tlsext_channel_id_private;
+ UniquePtr<EVP_PKEY> channel_id_private;
// For a client, this contains the list of supported protocols in wire
// format.
- uint8_t *alpn_client_proto_list;
- unsigned alpn_client_proto_list_len;
+ Array<uint8_t> alpn_client_proto_list;
// Contains a list of supported Token Binding key parameters.
- uint8_t *token_binding_params;
- size_t token_binding_params_len;
+ Array<uint8_t> token_binding_params;
// Contains the QUIC transport params that this endpoint will send.
- uint8_t *quic_transport_params;
- size_t quic_transport_params_len;
+ Array<uint8_t> quic_transport_params;
- // renegotiate_mode controls how peer renegotiation attempts are handled.
- enum ssl_renegotiate_mode_t renegotiate_mode;
+ // verify_sigalgs, if not empty, is the set of signature algorithms
+ // accepted from the peer in decreasing order of preference.
+ Array<uint16_t> verify_sigalgs;
+
+ // srtp_profiles is the list of configured SRTP protection profiles for
+ // DTLS-SRTP.
+ UniquePtr<STACK_OF(SRTP_PROTECTION_PROFILE)> srtp_profiles;
// verify_mode is a bitmask of |SSL_VERIFY_*| values.
- uint8_t verify_mode;
-
- // server is true iff the this SSL* is the server half. Note: before the SSL*
- // is initialized by either SSL_set_accept_state or SSL_set_connect_state,
- // the side is not determined. In this state, server is always false.
- bool server:1;
-
- // quiet_shutdown is true if the connection should not send a close_notify on
- // shutdown.
- bool quiet_shutdown:1;
+ uint8_t verify_mode = SSL_VERIFY_NONE;
// Enable signed certificate time stamps. Currently client only.
bool signed_cert_timestamps_enabled:1;
@@ -2729,10 +2434,10 @@
// whether OCSP stapling will be requested.
bool ocsp_stapling_enabled:1;
- // tlsext_channel_id_enabled is copied from the |SSL_CTX|. For a server,
- // means that we'll accept Channel IDs from clients. For a client, means that
- // we'll advertise support.
- bool tlsext_channel_id_enabled:1;
+ // channel_id_enabled is copied from the |SSL_CTX|. For a server, means that
+ // we'll accept Channel IDs from clients. For a client, means that we'll
+ // advertise support.
+ bool channel_id_enabled:1;
// retain_only_sha256_of_client_certs is true if we should compute the SHA256
// hash of the peer's certificate and then discard it to save memory and
@@ -2745,15 +2450,9 @@
// element of the same name and may be cleared if the handoff is declined.
bool handoff:1;
- // handback indicates that a server should pause the handshake after
- // finishing operations that require private key material, in such a way that
- // |SSL_get_error| returns |SSL_HANDBACK|. It is set by |SSL_apply_handoff|.
- bool handback : 1;
-
- // did_dummy_pq_padding is only valid for a client. In that context, it is
- // true iff the client observed the server echoing a dummy PQ padding
- // extension.
- bool did_dummy_pq_padding:1;
+ // shed_handshake_config indicates that the handshake config (this object!)
+ // should be freed after the handshake completes.
+ bool shed_handshake_config : 1;
};
// From draft-ietf-tls-tls13-18, used in determining PSK modes.
@@ -2780,27 +2479,31 @@
const EVP_PKEY *privkey);
int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey);
int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server);
-int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session);
+int ssl_encrypt_ticket(SSL_HANDSHAKE *hs, CBB *out, const SSL_SESSION *session);
int ssl_ctx_rotate_ticket_encryption_key(SSL_CTX *ctx);
// ssl_session_new returns a newly-allocated blank |SSL_SESSION| or nullptr on
// error.
UniquePtr<SSL_SESSION> ssl_session_new(const SSL_X509_METHOD *x509_method);
+// ssl_hash_session_id returns a hash of |session_id|, suitable for a hash table
+// keyed on session IDs.
+uint32_t ssl_hash_session_id(Span<const uint8_t> session_id);
+
// SSL_SESSION_parse parses an |SSL_SESSION| from |cbs| and advances |cbs| over
// the parsed data.
-UniquePtr<SSL_SESSION> SSL_SESSION_parse(CBS *cbs,
- const SSL_X509_METHOD *x509_method,
- CRYPTO_BUFFER_POOL *pool);
+OPENSSL_EXPORT UniquePtr<SSL_SESSION> SSL_SESSION_parse(
+ CBS *cbs, const SSL_X509_METHOD *x509_method, CRYPTO_BUFFER_POOL *pool);
// ssl_session_serialize writes |in| to |cbb| as if it were serialising a
// session for Session-ID resumption. It returns one on success and zero on
// error.
-int ssl_session_serialize(const SSL_SESSION *in, CBB *cbb);
+OPENSSL_EXPORT int ssl_session_serialize(const SSL_SESSION *in, CBB *cbb);
// ssl_session_is_context_valid returns one if |session|'s session ID context
-// matches the one set on |ssl| and zero otherwise.
-int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session);
+// matches the one set on |hs| and zero otherwise.
+int ssl_session_is_context_valid(const SSL_HANDSHAKE *hs,
+ const SSL_SESSION *session);
// ssl_session_is_time_valid returns one if |session| is still valid and zero if
// it has expired.
@@ -2827,7 +2530,7 @@
// |ssl_hs_pending_session| and should be called again. If a ticket could not be
// decrypted immediately it returns |ssl_hs_pending_ticket| and should also
// be called again. Otherwise, it returns |ssl_hs_error|.
-enum ssl_hs_wait_t ssl_get_prev_session(SSL *ssl,
+enum ssl_hs_wait_t ssl_get_prev_session(SSL_HANDSHAKE *hs,
UniquePtr<SSL_SESSION> *out_session,
bool *out_tickets_supported,
bool *out_renew_ticket,
@@ -2856,10 +2559,6 @@
void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session,
uint32_t timeout);
-// ssl_get_cipher_preferences returns the cipher preference list for TLS 1.2 and
-// below.
-const SSLCipherPreferenceList *ssl_get_cipher_preferences(const SSL *ssl);
-
void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
int ssl_send_alert(SSL *ssl, int level, int desc);
@@ -2952,41 +2651,38 @@
Span<const uint8_t> premaster);
// tls1_get_grouplist returns the locally-configured group preference list.
-Span<const uint16_t> tls1_get_grouplist(const SSL *ssl);
+Span<const uint16_t> tls1_get_grouplist(const SSL_HANDSHAKE *ssl);
-// tls1_check_group_id returns one if |group_id| is consistent with
-// locally-configured group preferences.
-int tls1_check_group_id(const SSL *ssl, uint16_t group_id);
+// tls1_check_group_id returns whether |group_id| is consistent with locally-
+// configured group preferences.
+bool tls1_check_group_id(const SSL_HANDSHAKE *ssl, uint16_t group_id);
// tls1_get_shared_group sets |*out_group_id| to the first preferred shared
-// group between client and server preferences and returns one. If none may be
-// found, it returns zero.
-int tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id);
+// group between client and server preferences and returns true. If none may be
+// found, it returns false.
+bool tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id);
-// tls1_set_curves converts the array of |ncurves| NIDs pointed to by |curves|
-// into a newly allocated array of TLS group IDs. On success, the function
-// returns one and writes the array to |*out_group_ids| and its size to
-// |*out_group_ids_len|. Otherwise, it returns zero.
-int tls1_set_curves(uint16_t **out_group_ids, size_t *out_group_ids_len,
- const int *curves, size_t ncurves);
+// tls1_set_curves converts the array of NIDs in |curves| into a newly allocated
+// array of TLS group IDs. On success, the function returns true and writes the
+// array to |*out_group_ids|. Otherwise, it returns false.
+bool tls1_set_curves(Array<uint16_t> *out_group_ids, Span<const int> curves);
// tls1_set_curves_list converts the string of curves pointed to by |curves|
// into a newly allocated array of TLS group IDs. On success, the function
-// returns one and writes the array to |*out_group_ids| and its size to
-// |*out_group_ids_len|. Otherwise, it returns zero.
-int tls1_set_curves_list(uint16_t **out_group_ids, size_t *out_group_ids_len,
- const char *curves);
+// returns true and writes the array to |*out_group_ids|. Otherwise, it returns
+// false.
+bool tls1_set_curves_list(Array<uint16_t> *out_group_ids, const char *curves);
-// ssl_add_clienthello_tlsext writes ClientHello extensions to |out|. It
-// returns one on success and zero on failure. The |header_len| argument is the
-// length of the ClientHello written so far and is used to compute the padding
-// length. (It does not include the record header.)
-int ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, size_t header_len);
+// ssl_add_clienthello_tlsext writes ClientHello extensions to |out|. It returns
+// true on success and false on failure. The |header_len| argument is the length
+// of the ClientHello written so far and is used to compute the padding length.
+// (It does not include the record header.)
+bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, size_t header_len);
-int ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out);
-int ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
- const SSL_CLIENT_HELLO *client_hello);
-int ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs);
+bool ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out);
+bool ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
+ const SSL_CLIENT_HELLO *client_hello);
+bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs);
#define tlsext_tick_md EVP_sha256
@@ -3000,38 +2696,41 @@
// Retry later.
// |ssl_ticket_aead_error|: an error occured that is fatal to the connection.
enum ssl_ticket_aead_result_t ssl_process_ticket(
- SSL *ssl, UniquePtr<SSL_SESSION> *out_session, bool *out_renew_ticket,
- const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id,
- size_t session_id_len);
+ SSL_HANDSHAKE *hs, UniquePtr<SSL_SESSION> *out_session,
+ bool *out_renew_ticket, const uint8_t *ticket, size_t ticket_len,
+ const uint8_t *session_id, size_t session_id_len);
// tls1_verify_channel_id processes |msg| as a Channel ID message, and verifies
-// the signature. If the key is valid, it saves the Channel ID and returns
-// one. Otherwise, it returns zero.
-int tls1_verify_channel_id(SSL_HANDSHAKE *hs, const SSLMessage &msg);
+// the signature. If the key is valid, it saves the Channel ID and returns true.
+// Otherwise, it returns false.
+bool tls1_verify_channel_id(SSL_HANDSHAKE *hs, const SSLMessage &msg);
// tls1_write_channel_id generates a Channel ID message and puts the output in
-// |cbb|. |ssl->tlsext_channel_id_private| must already be set before calling.
-// This function returns true on success and false on error.
+// |cbb|. |ssl->channel_id_private| must already be set before calling. This
+// function returns true on success and false on error.
bool tls1_write_channel_id(SSL_HANDSHAKE *hs, CBB *cbb);
// tls1_channel_id_hash computes the hash to be signed by Channel ID and writes
// it to |out|, which must contain at least |EVP_MAX_MD_SIZE| bytes. It returns
-// one on success and zero on failure.
-int tls1_channel_id_hash(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len);
+// true on success and false on failure.
+bool tls1_channel_id_hash(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len);
-int tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs);
+// tls1_record_handshake_hashes_for_channel_id records the current handshake
+// hashes in |hs->new_session| so that Channel ID resumptions can sign that
+// data.
+bool tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs);
-// ssl_do_channel_id_callback checks runs |ssl->ctx->channel_id_cb| if
-// necessary. It returns one on success and zero on fatal error. Note that, on
-// success, |ssl->tlsext_channel_id_private| may be unset, in which case the
+// ssl_do_channel_id_callback checks runs |hs->ssl->ctx->channel_id_cb| if
+// necessary. It returns true on success and false on fatal error. Note that, on
+// success, |hs->ssl->channel_id_private| may be unset, in which case the
// operation should be retried later.
-int ssl_do_channel_id_callback(SSL *ssl);
+bool ssl_do_channel_id_callback(SSL_HANDSHAKE *hs);
-// ssl_can_write returns one if |ssl| is allowed to write and zero otherwise.
-int ssl_can_write(const SSL *ssl);
+// ssl_can_write returns whether |ssl| is allowed to write.
+bool ssl_can_write(const SSL *ssl);
-// ssl_can_read returns one if |ssl| is allowed to read and zero otherwise.
-int ssl_can_read(const SSL *ssl);
+// ssl_can_read returns wheter |ssl| is allowed to read.
+bool ssl_can_read(const SSL *ssl);
void ssl_get_current_time(const SSL *ssl, struct OPENSSL_timeval *out_clock);
void ssl_ctx_get_current_time(const SSL_CTX *ctx,
@@ -3063,70 +2762,521 @@
const bssl::SSL_PROTOCOL_METHOD *method;
// x509_method contains pointers to functions that might deal with |X509|
// compatibility, or might be a no-op, depending on the application.
- const SSL_X509_METHOD *x509_method;
+ const bssl::SSL_X509_METHOD *x509_method;
};
-struct ssl_x509_method_st {
- // check_client_CA_list returns one if |names| is a good list of X.509
- // distinguished names and zero otherwise. This is used to ensure that we can
- // reject unparsable values at handshake time when using crypto/x509.
- int (*check_client_CA_list)(STACK_OF(CRYPTO_BUFFER) *names);
+struct ssl_ctx_st {
+ explicit ssl_ctx_st(const SSL_METHOD *ssl_method);
+ ssl_ctx_st(const ssl_ctx_st &) = delete;
+ ssl_ctx_st &operator=(const ssl_ctx_st &) = delete;
- // cert_clear frees and NULLs all X509 certificate-related state.
- void (*cert_clear)(bssl::CERT *cert);
- // cert_free frees all X509-related state.
- void (*cert_free)(bssl::CERT *cert);
- // cert_flush_cached_chain drops any cached |X509|-based certificate chain
- // from |cert|.
- // cert_dup duplicates any needed fields from |cert| to |new_cert|.
- void (*cert_dup)(bssl::CERT *new_cert, const bssl::CERT *cert);
- void (*cert_flush_cached_chain)(bssl::CERT *cert);
- // cert_flush_cached_chain drops any cached |X509|-based leaf certificate
- // from |cert|.
- void (*cert_flush_cached_leaf)(bssl::CERT *cert);
+ const bssl::SSL_PROTOCOL_METHOD *method = nullptr;
+ const bssl::SSL_X509_METHOD *x509_method = nullptr;
- // session_cache_objects fills out |sess->x509_peer| and |sess->x509_chain|
- // from |sess->certs| and erases |sess->x509_chain_without_leaf|. It returns
- // one on success or zero on error.
- int (*session_cache_objects)(SSL_SESSION *session);
- // session_dup duplicates any needed fields from |session| to |new_session|.
- // It returns one on success or zero on error.
- int (*session_dup)(SSL_SESSION *new_session, const SSL_SESSION *session);
- // session_clear frees any X509-related state from |session|.
- void (*session_clear)(SSL_SESSION *session);
- // session_verify_cert_chain verifies the certificate chain in |session|,
- // sets |session->verify_result| and returns one on success or zero on
- // error.
- int (*session_verify_cert_chain)(SSL_SESSION *session, SSL *ssl,
- uint8_t *out_alert);
+ // lock is used to protect various operations on this object.
+ CRYPTO_MUTEX lock;
- // hs_flush_cached_ca_names drops any cached |X509_NAME|s from |hs|.
- void (*hs_flush_cached_ca_names)(bssl::SSL_HANDSHAKE *hs);
- // ssl_new does any neccessary initialisation of |ssl|. It returns one on
- // success or zero on error.
- int (*ssl_new)(SSL *ssl);
- // ssl_free frees anything created by |ssl_new|.
- void (*ssl_free)(SSL *ssl);
- // ssl_flush_cached_client_CA drops any cached |X509_NAME|s from |ssl|.
- void (*ssl_flush_cached_client_CA)(SSL *ssl);
- // ssl_auto_chain_if_needed runs the deprecated auto-chaining logic if
- // necessary. On success, it updates |ssl|'s certificate configuration as
- // needed and returns one. Otherwise, it returns zero.
- int (*ssl_auto_chain_if_needed)(SSL *ssl);
- // ssl_ctx_new does any neccessary initialisation of |ctx|. It returns one on
- // success or zero on error.
- int (*ssl_ctx_new)(SSL_CTX *ctx);
- // ssl_ctx_free frees anything created by |ssl_ctx_new|.
- void (*ssl_ctx_free)(SSL_CTX *ctx);
- // ssl_ctx_flush_cached_client_CA drops any cached |X509_NAME|s from |ctx|.
- void (*ssl_ctx_flush_cached_client_CA)(SSL_CTX *ssl);
+ // conf_max_version is the maximum acceptable protocol version configured by
+ // |SSL_CTX_set_max_proto_version|. Note this version is normalized in DTLS
+ // and is further constrainted by |SSL_OP_NO_*|.
+ uint16_t conf_max_version = 0;
+
+ // conf_min_version is the minimum acceptable protocol version configured by
+ // |SSL_CTX_set_min_proto_version|. Note this version is normalized in DTLS
+ // and is further constrainted by |SSL_OP_NO_*|.
+ uint16_t conf_min_version = 0;
+
+ // tls13_variant is the variant of TLS 1.3 we are using for this
+ // configuration.
+ tls13_variant_t tls13_variant = tls13_default;
+
+ bssl::UniquePtr<bssl::SSLCipherPreferenceList> cipher_list;
+
+ X509_STORE *cert_store = nullptr;
+ LHASH_OF(SSL_SESSION) *sessions = nullptr;
+ // Most session-ids that will be cached, default is
+ // SSL_SESSION_CACHE_MAX_SIZE_DEFAULT. 0 is unlimited.
+ unsigned long session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;
+ SSL_SESSION *session_cache_head = nullptr;
+ SSL_SESSION *session_cache_tail = nullptr;
+
+ // handshakes_since_cache_flush is the number of successful handshakes since
+ // the last cache flush.
+ int handshakes_since_cache_flush = 0;
+
+ // This can have one of 2 values, ored together,
+ // SSL_SESS_CACHE_CLIENT,
+ // SSL_SESS_CACHE_SERVER,
+ // Default is SSL_SESSION_CACHE_SERVER, which means only
+ // SSL_accept which cache SSL_SESSIONS.
+ int session_cache_mode = SSL_SESS_CACHE_SERVER;
+
+ // session_timeout is the default lifetime for new sessions in TLS 1.2 and
+ // earlier, in seconds.
+ uint32_t session_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+
+ // session_psk_dhe_timeout is the default lifetime for new sessions in TLS
+ // 1.3, in seconds.
+ uint32_t session_psk_dhe_timeout = SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT;
+
+ // If this callback is not null, it will be called each time a session id is
+ // added to the cache. If this function returns 1, it means that the
+ // callback will do a SSL_SESSION_free() when it has finished using it.
+ // Otherwise, on 0, it means the callback has finished with it. If
+ // remove_session_cb is not null, it will be called when a session-id is
+ // removed from the cache. After the call, OpenSSL will SSL_SESSION_free()
+ // it.
+ int (*new_session_cb)(SSL *ssl, SSL_SESSION *sess) = nullptr;
+ void (*remove_session_cb)(SSL_CTX *ctx, SSL_SESSION *sess) = nullptr;
+ SSL_SESSION *(*get_session_cb)(SSL *ssl, const uint8_t *data, int len,
+ int *copy) = nullptr;
+
+ CRYPTO_refcount_t references = 1;
+
+ // if defined, these override the X509_verify_cert() calls
+ int (*app_verify_callback)(X509_STORE_CTX *store_ctx, void *arg) = nullptr;
+ void *app_verify_arg = nullptr;
+
+ ssl_verify_result_t (*custom_verify_callback)(SSL *ssl,
+ uint8_t *out_alert) = nullptr;
+
+ // Default password callback.
+ pem_password_cb *default_passwd_callback = nullptr;
+
+ // Default password callback user data.
+ void *default_passwd_callback_userdata = nullptr;
+
+ // get client cert callback
+ int (*client_cert_cb)(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) = nullptr;
+
+ // get channel id callback
+ void (*channel_id_cb)(SSL *ssl, EVP_PKEY **out_pkey) = nullptr;
+
+ CRYPTO_EX_DATA ex_data;
+
+ // Default values used when no per-SSL value is defined follow
+
+ void (*info_callback)(const SSL *ssl, int type, int value) = nullptr;
+
+ // what we put in client cert requests
+ bssl::UniquePtr<STACK_OF(CRYPTO_BUFFER)> client_CA;
+
+ // cached_x509_client_CA is a cache of parsed versions of the elements of
+ // |client_CA|.
+ STACK_OF(X509_NAME) *cached_x509_client_CA = nullptr;
+
+
+ // Default values to use in SSL structures follow (these are copied by
+ // SSL_new)
+
+ uint32_t options = 0;
+ // Disable the auto-chaining feature by default. wpa_supplicant relies on this
+ // feature, but require callers opt into it.
+ uint32_t mode = SSL_MODE_NO_AUTO_CHAIN;
+ uint32_t max_cert_list = SSL_MAX_CERT_LIST_DEFAULT;
+
+ bssl::UniquePtr<bssl::CERT> cert;
+
+ // callback that allows applications to peek at protocol messages
+ void (*msg_callback)(int write_p, int version, int content_type,
+ const void *buf, size_t len, SSL *ssl, void *arg) = nullptr;
+ void *msg_callback_arg = nullptr;
+
+ int verify_mode = SSL_VERIFY_NONE;
+ int (*default_verify_callback)(int ok, X509_STORE_CTX *ctx) =
+ nullptr; // called 'verify_callback' in the SSL
+
+ X509_VERIFY_PARAM *param = nullptr;
+
+ // select_certificate_cb is called before most ClientHello processing and
+ // before the decision whether to resume a session is made. See
+ // |ssl_select_cert_result_t| for details of the return values.
+ ssl_select_cert_result_t (*select_certificate_cb)(const SSL_CLIENT_HELLO *) =
+ nullptr;
+
+ // dos_protection_cb is called once the resumption decision for a ClientHello
+ // has been made. It returns one to continue the handshake or zero to
+ // abort.
+ int (*dos_protection_cb) (const SSL_CLIENT_HELLO *) = nullptr;
+
+ // Maximum amount of data to send in one fragment. actual record size can be
+ // more than this due to padding and MAC overheads.
+ uint16_t max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH;
+
+ // TLS extensions servername callback
+ int (*servername_callback)(SSL *, int *, void *) = nullptr;
+ void *servername_arg = nullptr;
+
+ // RFC 4507 session ticket keys. |ticket_key_current| may be NULL before the
+ // first handshake and |ticket_key_prev| may be NULL at any time.
+ // Automatically generated ticket keys are rotated as needed at handshake
+ // time. Hence, all access must be synchronized through |lock|.
+ bssl::UniquePtr<bssl::TicketKey> ticket_key_current;
+ bssl::UniquePtr<bssl::TicketKey> ticket_key_prev;
+
+ // Callback to support customisation of ticket key setting
+ int (*ticket_key_cb)(SSL *ssl, uint8_t *name, uint8_t *iv,
+ EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc) = nullptr;
+
+ // Server-only: psk_identity_hint is the default identity hint to send in
+ // PSK-based key exchanges.
+ bssl::UniquePtr<char> psk_identity_hint;
+
+ unsigned (*psk_client_callback)(SSL *ssl, const char *hint, char *identity,
+ unsigned max_identity_len, uint8_t *psk,
+ unsigned max_psk_len) = nullptr;
+ unsigned (*psk_server_callback)(SSL *ssl, const char *identity, uint8_t *psk,
+ unsigned max_psk_len) = nullptr;
+
+
+ // Next protocol negotiation information
+ // (for experimental NPN extension).
+
+ // For a server, this contains a callback function by which the set of
+ // advertised protocols can be provided.
+ int (*next_protos_advertised_cb)(SSL *ssl, const uint8_t **out,
+ unsigned *out_len, void *arg) = nullptr;
+ void *next_protos_advertised_cb_arg = nullptr;
+ // For a client, this contains a callback function that selects the
+ // next protocol from the list provided by the server.
+ int (*next_proto_select_cb)(SSL *ssl, uint8_t **out, uint8_t *out_len,
+ const uint8_t *in, unsigned in_len,
+ void *arg) = nullptr;
+ void *next_proto_select_cb_arg = nullptr;
+
+ // ALPN information
+ // (we are in the process of transitioning from NPN to ALPN.)
+
+ // For a server, this contains a callback function that allows the
+ // server to select the protocol for the connection.
+ // out: on successful return, this must point to the raw protocol
+ // name (without the length prefix).
+ // outlen: on successful return, this contains the length of |*out|.
+ // in: points to the client's list of supported protocols in
+ // wire-format.
+ // inlen: the length of |in|.
+ int (*alpn_select_cb)(SSL *ssl, const uint8_t **out, uint8_t *out_len,
+ const uint8_t *in, unsigned in_len,
+ void *arg) = nullptr;
+ void *alpn_select_cb_arg = nullptr;
+
+ // For a client, this contains the list of supported protocols in wire
+ // format.
+ bssl::Array<uint8_t> alpn_client_proto_list;
+
+ // SRTP profiles we are willing to do from RFC 5764
+ bssl::UniquePtr<STACK_OF(SRTP_PROTECTION_PROFILE)> srtp_profiles;
+
+ // Defined compression algorithms for certificates.
+ bssl::UniquePtr<STACK_OF(CertCompressionAlg)> cert_compression_algs;
+
+ // Supported group values inherited by SSL structure
+ bssl::Array<uint16_t> supported_group_list;
+
+ // The client's Channel ID private key.
+ bssl::UniquePtr<EVP_PKEY> channel_id_private;
+
+ // keylog_callback, if not NULL, is the key logging callback. See
+ // |SSL_CTX_set_keylog_callback|.
+ void (*keylog_callback)(const SSL *ssl, const char *line) = nullptr;
+
+ // current_time_cb, if not NULL, is the function to use to get the current
+ // time. It sets |*out_clock| to the current time. The |ssl| argument is
+ // always NULL. See |SSL_CTX_set_current_time_cb|.
+ void (*current_time_cb)(const SSL *ssl, struct timeval *out_clock) = nullptr;
+
+ // pool is used for all |CRYPTO_BUFFER|s in case we wish to share certificate
+ // memory.
+ CRYPTO_BUFFER_POOL *pool = nullptr;
+
+ // ticket_aead_method contains function pointers for opening and sealing
+ // session tickets.
+ const SSL_TICKET_AEAD_METHOD *ticket_aead_method = nullptr;
+
+ // legacy_ocsp_callback implements an OCSP-related callback for OpenSSL
+ // compatibility.
+ int (*legacy_ocsp_callback)(SSL *ssl, void *arg) = nullptr;
+ void *legacy_ocsp_callback_arg = nullptr;
+
+ // verify_sigalgs, if not empty, is the set of signature algorithms
+ // accepted from the peer in decreasing order of preference.
+ bssl::Array<uint16_t> verify_sigalgs;
+
+ // retain_only_sha256_of_client_certs is true if we should compute the SHA256
+ // hash of the peer's certificate and then discard it to save memory and
+ // session space. Only effective on the server side.
+ bool retain_only_sha256_of_client_certs:1;
+
+ // quiet_shutdown is true if the connection should not send a close_notify on
+ // shutdown.
+ bool quiet_shutdown:1;
+
+ // ocsp_stapling_enabled is only used by client connections and indicates
+ // whether OCSP stapling will be requested.
+ bool ocsp_stapling_enabled:1;
+
+ // If true, a client will request certificate timestamps.
+ bool signed_cert_timestamps_enabled:1;
+
+ // channel_id_enabled is whether Channel ID is enabled. For a server, means
+ // that we'll accept Channel IDs from clients. For a client, means that we'll
+ // advertise support.
+ bool channel_id_enabled:1;
+
+ // grease_enabled is whether draft-davidben-tls-grease-01 is enabled.
+ bool grease_enabled:1;
+
+ // allow_unknown_alpn_protos is whether the client allows unsolicited ALPN
+ // protocols from the peer.
+ bool allow_unknown_alpn_protos:1;
+
+ // ed25519_enabled is whether Ed25519 is advertised in the handshake.
+ bool ed25519_enabled:1;
+
+ // rsa_pss_rsae_certs_enabled is whether rsa_pss_rsae_* are supported by the
+ // certificate verifier.
+ bool rsa_pss_rsae_certs_enabled:1;
+
+ // false_start_allowed_without_alpn is whether False Start (if
+ // |SSL_MODE_ENABLE_FALSE_START| is enabled) is allowed without ALPN.
+ bool false_start_allowed_without_alpn:1;
+
+ // handoff indicates that a server should stop after receiving the
+ // ClientHello and pause the handshake in such a way that |SSL_get_error|
+ // returns |SSL_HANDOFF|.
+ bool handoff:1;
+
+ // If enable_early_data is true, early data can be sent and accepted.
+ bool enable_early_data : 1;
+
+ private:
+ ~ssl_ctx_st();
+ friend void SSL_CTX_free(SSL_CTX *);
};
-// The following types back public C-exposed types which must live in the global
-// namespace. We use subclassing so the implementations may be C++ types with
-// methods and destructor without polluting the global namespace.
-struct ssl_ctx_st : public bssl::SSLContext {};
-struct ssl_st : public bssl::SSLConnection {};
+struct ssl_st {
+ explicit ssl_st(SSL_CTX *ctx_arg);
+ ssl_st(const ssl_st &) = delete;
+ ssl_st &operator=(const ssl_st &) = delete;
+ ~ssl_st();
+
+ // method is the method table corresponding to the current protocol (DTLS or
+ // TLS).
+ const bssl::SSL_PROTOCOL_METHOD *method = nullptr;
+
+ // config is a container for handshake configuration. Accesses to this field
+ // should check for nullptr, since configuration may be shed after the
+ // handshake completes. (If you have the |SSL_HANDSHAKE| object at hand, use
+ // that instead, and skip the null check.)
+ bssl::UniquePtr<bssl::SSL_CONFIG> config;
+
+ // version is the protocol version.
+ uint16_t version = 0;
+
+ uint16_t max_send_fragment = 0;
+
+ // There are 2 BIO's even though they are normally both the same. This is so
+ // data can be read and written to different handlers
+
+ bssl::UniquePtr<BIO> rbio; // used by SSL_read
+ bssl::UniquePtr<BIO> wbio; // used by SSL_write
+
+ // do_handshake runs the handshake. On completion, it returns |ssl_hs_ok|.
+ // Otherwise, it returns a value corresponding to what operation is needed to
+ // progress.
+ bssl::ssl_hs_wait_t (*do_handshake)(bssl::SSL_HANDSHAKE *hs) = nullptr;
+
+ bssl::SSL3_STATE *s3 = nullptr; // TLS variables
+ bssl::DTLS1_STATE *d1 = nullptr; // DTLS variables
+
+ // callback that allows applications to peek at protocol messages
+ void (*msg_callback)(int write_p, int version, int content_type,
+ const void *buf, size_t len, SSL *ssl,
+ void *arg) = nullptr;
+ void *msg_callback_arg = nullptr;
+
+ // session info
+
+ // initial_timeout_duration_ms is the default DTLS timeout duration in
+ // milliseconds. It's used to initialize the timer any time it's restarted.
+ //
+ // RFC 6347 states that implementations SHOULD use an initial timer value of 1
+ // second.
+ unsigned initial_timeout_duration_ms = 1000;
+
+ // tls13_variant is the variant of TLS 1.3 we are using for this
+ // configuration.
+ tls13_variant_t tls13_variant = tls13_default;
+
+ // session is the configured session to be offered by the client. This session
+ // is immutable.
+ bssl::UniquePtr<SSL_SESSION> session;
+
+ void (*info_callback)(const SSL *ssl, int type, int value) = nullptr;
+
+ bssl::UniquePtr<SSL_CTX> ctx;
+
+ // session_ctx is the |SSL_CTX| used for the session cache and related
+ // settings.
+ bssl::UniquePtr<SSL_CTX> session_ctx;
+
+ // extra application data
+ CRYPTO_EX_DATA ex_data;
+
+ uint32_t options = 0; // protocol behaviour
+ uint32_t mode = 0; // API behaviour
+ uint32_t max_cert_list = 0;
+ bssl::UniquePtr<char> hostname;
+
+ // renegotiate_mode controls how peer renegotiation attempts are handled.
+ ssl_renegotiate_mode_t renegotiate_mode = ssl_renegotiate_never;
+
+ // server is true iff the this SSL* is the server half. Note: before the SSL*
+ // is initialized by either SSL_set_accept_state or SSL_set_connect_state,
+ // the side is not determined. In this state, server is always false.
+ bool server : 1;
+
+ // quiet_shutdown is true if the connection should not send a close_notify on
+ // shutdown.
+ bool quiet_shutdown : 1;
+
+ // did_dummy_pq_padding is only valid for a client. In that context, it is
+ // true iff the client observed the server echoing a dummy PQ padding
+ // extension.
+ bool did_dummy_pq_padding:1;
+
+ // If enable_early_data is true, early data can be sent and accepted.
+ bool enable_early_data : 1;
+};
+
+struct ssl_session_st {
+ explicit ssl_session_st(const bssl::SSL_X509_METHOD *method);
+ ssl_session_st(const ssl_session_st &) = delete;
+ ssl_session_st &operator=(const ssl_session_st &) = delete;
+
+ CRYPTO_refcount_t references = 1;
+ uint16_t ssl_version = 0; // what ssl version session info is being kept in here?
+
+ // group_id is the ID of the ECDH group used to establish this session or zero
+ // if not applicable or unknown.
+ uint16_t group_id = 0;
+
+ // peer_signature_algorithm is the signature algorithm used to authenticate
+ // the peer, or zero if not applicable or unknown.
+ uint16_t peer_signature_algorithm = 0;
+
+ // master_key, in TLS 1.2 and below, is the master secret associated with the
+ // session. In TLS 1.3 and up, it is the resumption secret.
+ int master_key_length = 0;
+ uint8_t master_key[SSL_MAX_MASTER_KEY_LENGTH] = {0};
+
+ // session_id - valid?
+ unsigned session_id_length = 0;
+ uint8_t session_id[SSL_MAX_SSL_SESSION_ID_LENGTH] = {0};
+ // this is used to determine whether the session is being reused in
+ // the appropriate context. It is up to the application to set this,
+ // via SSL_new
+ uint8_t sid_ctx_length = 0;
+ uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH] = {0};
+
+ bssl::UniquePtr<char> psk_identity;
+
+ // certs contains the certificate chain from the peer, starting with the leaf
+ // certificate.
+ bssl::UniquePtr<STACK_OF(CRYPTO_BUFFER)> certs;
+
+ const bssl::SSL_X509_METHOD *x509_method = nullptr;
+
+ // x509_peer is the peer's certificate.
+ X509 *x509_peer = nullptr;
+
+ // x509_chain is the certificate chain sent by the peer. NOTE: for historical
+ // reasons, when a client (so the peer is a server), the chain includes
+ // |peer|, but when a server it does not.
+ STACK_OF(X509) *x509_chain = nullptr;
+
+ // x509_chain_without_leaf is a lazily constructed copy of |x509_chain| that
+ // omits the leaf certificate. This exists because OpenSSL, historically,
+ // didn't include the leaf certificate in the chain for a server, but did for
+ // a client. The |x509_chain| always includes it and, if an API call requires
+ // a chain without, it is stored here.
+ STACK_OF(X509) *x509_chain_without_leaf = nullptr;
+
+ // verify_result is the result of certificate verification in the case of
+ // non-fatal certificate errors.
+ long verify_result = X509_V_ERR_INVALID_CALL;
+
+ // timeout is the lifetime of the session in seconds, measured from |time|.
+ // This is renewable up to |auth_timeout|.
+ uint32_t timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+
+ // auth_timeout is the non-renewable lifetime of the session in seconds,
+ // measured from |time|.
+ uint32_t auth_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+
+ // time is the time the session was issued, measured in seconds from the UNIX
+ // epoch.
+ uint64_t time = 0;
+
+ const SSL_CIPHER *cipher = nullptr;
+
+ CRYPTO_EX_DATA ex_data; // application specific data
+
+ // These are used to make removal of session-ids more efficient and to
+ // implement a maximum cache size.
+ SSL_SESSION *prev = nullptr, *next = nullptr;
+
+ bssl::Array<uint8_t> ticket;
+
+ bssl::UniquePtr<CRYPTO_BUFFER> signed_cert_timestamp_list;
+
+ // The OCSP response that came with the session.
+ bssl::UniquePtr<CRYPTO_BUFFER> ocsp_response;
+
+ // peer_sha256 contains the SHA-256 hash of the peer's certificate if
+ // |peer_sha256_valid| is true.
+ uint8_t peer_sha256[SHA256_DIGEST_LENGTH] = {0};
+
+ // original_handshake_hash contains the handshake hash (either SHA-1+MD5 or
+ // SHA-2, depending on TLS version) for the original, full handshake that
+ // created a session. This is used by Channel IDs during resumption.
+ uint8_t original_handshake_hash[EVP_MAX_MD_SIZE] = {0};
+ uint8_t original_handshake_hash_len = 0;
+
+ uint32_t ticket_lifetime_hint = 0; // Session lifetime hint in seconds
+
+ uint32_t ticket_age_add = 0;
+
+ // ticket_max_early_data is the maximum amount of data allowed to be sent as
+ // early data. If zero, 0-RTT is disallowed.
+ uint32_t ticket_max_early_data = 0;
+
+ // early_alpn is the ALPN protocol from the initial handshake. This is only
+ // stored for TLS 1.3 and above in order to enforce ALPN matching for 0-RTT
+ // resumptions.
+ bssl::Array<uint8_t> early_alpn;
+
+ // extended_master_secret is whether the master secret in this session was
+ // generated using EMS and thus isn't vulnerable to the Triple Handshake
+ // attack.
+ bool extended_master_secret:1;
+
+ // peer_sha256_valid is whether |peer_sha256| is valid.
+ bool peer_sha256_valid:1; // Non-zero if peer_sha256 is valid
+
+ // not_resumable is used to indicate that session resumption is disallowed.
+ bool not_resumable:1;
+
+ // ticket_age_add_valid is whether |ticket_age_add| is valid.
+ bool ticket_age_add_valid:1;
+
+ // is_server is whether this session was created by a server.
+ bool is_server:1;
+
+ private:
+ ~ssl_session_st();
+ friend void SSL_SESSION_free(SSL_SESSION *);
+};
#endif // OPENSSL_HEADER_SSL_INTERNAL_H
diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc
index ede4ba7..98896a3 100644
--- a/src/ssl/s3_both.cc
+++ b/src/ssl/s3_both.cc
@@ -134,6 +134,8 @@
static bool add_record_to_flight(SSL *ssl, uint8_t type,
Span<const uint8_t> in) {
+ // The caller should have flushed |pending_hs_data| first.
+ assert(!ssl->s3->pending_hs_data);
// We'll never add a flight while in the process of writing it out.
assert(ssl->s3->pending_flight_offset == 0);
@@ -182,17 +184,51 @@
}
bool ssl3_add_message(SSL *ssl, Array<uint8_t> msg) {
- // Add the message to the current flight, splitting into several records if
- // needed.
+ // Pack handshake data into the minimal number of records. This avoids
+ // unnecessary encryption overhead, notably in TLS 1.3 where we send several
+ // encrypted messages in a row. For now, we do not do this for the null
+ // cipher. The benefit is smaller and there is a risk of breaking buggy
+ // implementations. Additionally, we tie this to draft-28 as a sanity check,
+ // on the off chance middleboxes have fixated on sizes.
+ //
+ // TODO(davidben): See if we can do this uniformly.
Span<const uint8_t> rest = msg;
- do {
- Span<const uint8_t> chunk = rest.subspan(0, ssl->max_send_fragment);
- rest = rest.subspan(chunk.size());
+ if (ssl->s3->aead_write_ctx->is_null_cipher() ||
+ ssl->version == TLS1_3_DRAFT23_VERSION) {
+ while (!rest.empty()) {
+ Span<const uint8_t> chunk = rest.subspan(0, ssl->max_send_fragment);
+ rest = rest.subspan(chunk.size());
- if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, chunk)) {
- return false;
+ if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, chunk)) {
+ return false;
+ }
}
- } while (!rest.empty());
+ } else {
+ while (!rest.empty()) {
+ // Flush if |pending_hs_data| is full.
+ if (ssl->s3->pending_hs_data &&
+ ssl->s3->pending_hs_data->length >= ssl->max_send_fragment &&
+ !tls_flush_pending_hs_data(ssl)) {
+ return false;
+ }
+
+ size_t pending_len =
+ ssl->s3->pending_hs_data ? ssl->s3->pending_hs_data->length : 0;
+ Span<const uint8_t> chunk =
+ rest.subspan(0, ssl->max_send_fragment - pending_len);
+ assert(!chunk.empty());
+ rest = rest.subspan(chunk.size());
+
+ if (!ssl->s3->pending_hs_data) {
+ ssl->s3->pending_hs_data.reset(BUF_MEM_new());
+ }
+ if (!ssl->s3->pending_hs_data ||
+ !BUF_MEM_append(ssl->s3->pending_hs_data.get(), chunk.data(),
+ chunk.size())) {
+ return false;
+ }
+ }
+ }
ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, msg);
// TODO(svaldez): Move this up a layer to fix abstraction for SSLTranscript on
@@ -204,10 +240,23 @@
return true;
}
+bool tls_flush_pending_hs_data(SSL *ssl) {
+ if (!ssl->s3->pending_hs_data || ssl->s3->pending_hs_data->length == 0) {
+ return true;
+ }
+
+ UniquePtr<BUF_MEM> pending_hs_data = std::move(ssl->s3->pending_hs_data);
+ return add_record_to_flight(
+ ssl, SSL3_RT_HANDSHAKE,
+ MakeConstSpan(reinterpret_cast<const uint8_t *>(pending_hs_data->data),
+ pending_hs_data->length));
+}
+
bool ssl3_add_change_cipher_spec(SSL *ssl) {
static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
- if (!add_record_to_flight(ssl, SSL3_RT_CHANGE_CIPHER_SPEC,
+ if (!tls_flush_pending_hs_data(ssl) ||
+ !add_record_to_flight(ssl, SSL3_RT_CHANGE_CIPHER_SPEC,
kChangeCipherSpec)) {
return false;
}
@@ -219,7 +268,8 @@
bool ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc) {
uint8_t alert[2] = {level, desc};
- if (!add_record_to_flight(ssl, SSL3_RT_ALERT, alert)) {
+ if (!tls_flush_pending_hs_data(ssl) ||
+ !add_record_to_flight(ssl, SSL3_RT_ALERT, alert)) {
return false;
}
@@ -229,6 +279,10 @@
}
int ssl3_flush_flight(SSL *ssl) {
+ if (!tls_flush_pending_hs_data(ssl)) {
+ return -1;
+ }
+
if (ssl->s3->pending_flight == nullptr) {
return 1;
}
@@ -238,8 +292,8 @@
return -1;
}
- if (ssl->s3->pending_flight->length > 0xffffffff ||
- ssl->s3->pending_flight->length > INT_MAX) {
+ static_assert(INT_MAX <= 0xffffffff, "int is larger than 32 bits");
+ if (ssl->s3->pending_flight->length > INT_MAX) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return -1;
}
@@ -257,7 +311,7 @@
// Write the pending flight.
while (ssl->s3->pending_flight_offset < ssl->s3->pending_flight->length) {
int ret = BIO_write(
- ssl->wbio,
+ ssl->wbio.get(),
ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
ssl->s3->pending_flight->length - ssl->s3->pending_flight_offset);
if (ret <= 0) {
@@ -268,7 +322,7 @@
ssl->s3->pending_flight_offset += ret;
}
- if (BIO_flush(ssl->wbio) <= 0) {
+ if (BIO_flush(ssl->wbio.get()) <= 0) {
ssl->s3->rwstate = SSL_WRITING;
return -1;
}
@@ -343,7 +397,7 @@
OPENSSL_memcpy(random + (SSL3_RANDOM_SIZE - rand_len), CBS_data(&challenge),
rand_len);
- // Write out an equivalent SSLv3 ClientHello.
+ // Write out an equivalent TLS ClientHello.
size_t max_v3_client_hello = SSL3_HM_HEADER_LENGTH + 2 /* version */ +
SSL3_RANDOM_SIZE + 1 /* session ID length */ +
2 /* cipher list length */ +
diff --git a/src/ssl/s3_lib.cc b/src/ssl/s3_lib.cc
index b1fc5fb..9e4d7d8 100644
--- a/src/ssl/s3_lib.cc
+++ b/src/ssl/s3_lib.cc
@@ -173,7 +173,7 @@
initial_handshake_complete(false),
session_reused(false),
send_connection_binding(false),
- tlsext_channel_id_valid(false),
+ channel_id_valid(false),
key_update_pending(false),
wpend_pending(false),
early_data_accepted(false),
@@ -215,12 +215,4 @@
ssl->s3 = NULL;
}
-const SSLCipherPreferenceList *ssl_get_cipher_preferences(const SSL *ssl) {
- if (ssl->cipher_list != NULL) {
- return ssl->cipher_list;
- }
-
- return ssl->ctx->cipher_list;
-}
-
} // namespace bssl
diff --git a/src/ssl/s3_pkt.cc b/src/ssl/s3_pkt.cc
index 5eb68f6..50e709b 100644
--- a/src/ssl/s3_pkt.cc
+++ b/src/ssl/s3_pkt.cc
@@ -234,6 +234,9 @@
return 0;
}
+ if (!tls_flush_pending_hs_data(ssl)) {
+ return -1;
+ }
size_t flight_len = 0;
if (ssl->s3->pending_flight != nullptr) {
flight_len =
@@ -411,7 +414,7 @@
// If the alert is fatal, flush the BIO now.
if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
- BIO_flush(ssl->wbio);
+ BIO_flush(ssl->wbio.get());
}
ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, ssl->s3->send_alert);
diff --git a/src/ssl/ssl_aead_ctx.cc b/src/ssl/ssl_aead_ctx.cc
index 363c959..322b1b5 100644
--- a/src/ssl/ssl_aead_ctx.cc
+++ b/src/ssl/ssl_aead_ctx.cc
@@ -42,7 +42,6 @@
random_variable_nonce_(false),
xor_fixed_nonce_(false),
omit_length_in_ad_(false),
- omit_version_in_ad_(false),
omit_ad_(false),
ad_is_header_(false) {
OPENSSL_memset(fixed_nonce_, 0, sizeof(fixed_nonce_));
@@ -147,7 +146,6 @@
aead_ctx->variable_nonce_included_in_record_ = true;
aead_ctx->random_variable_nonce_ = true;
aead_ctx->omit_length_in_ad_ = true;
- aead_ctx->omit_version_in_ad_ = (protocol_version == SSL3_VERSION);
}
return aead_ctx;
@@ -235,10 +233,8 @@
OPENSSL_memcpy(storage, seqnum, 8);
size_t len = 8;
storage[len++] = type;
- if (!omit_version_in_ad_) {
- storage[len++] = static_cast<uint8_t>((record_version >> 8));
- storage[len++] = static_cast<uint8_t>(record_version);
- }
+ storage[len++] = static_cast<uint8_t>((record_version >> 8));
+ storage[len++] = static_cast<uint8_t>(record_version);
if (!omit_length_in_ad_) {
storage[len++] = static_cast<uint8_t>((plaintext_len >> 8));
storage[len++] = static_cast<uint8_t>(plaintext_len);
diff --git a/src/ssl/ssl_asn1.cc b/src/ssl/ssl_asn1.cc
index 078ad1f..5dfacb2 100644
--- a/src/ssl/ssl_asn1.cc
+++ b/src/ssl/ssl_asn1.cc
@@ -224,8 +224,8 @@
// The peer certificate is only serialized if the SHA-256 isn't
// serialized instead.
- if (sk_CRYPTO_BUFFER_num(in->certs) > 0 && !in->peer_sha256_valid) {
- const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(in->certs, 0);
+ if (sk_CRYPTO_BUFFER_num(in->certs.get()) > 0 && !in->peer_sha256_valid) {
+ const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(in->certs.get(), 0);
if (!CBB_add_asn1(&session, &child, kPeerTag) ||
!CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer),
CRYPTO_BUFFER_len(buffer))) {
@@ -252,25 +252,26 @@
if (in->psk_identity) {
if (!CBB_add_asn1(&session, &child, kPSKIdentityTag) ||
- !CBB_add_asn1_octet_string(&child, (const uint8_t *)in->psk_identity,
- strlen(in->psk_identity))) {
+ !CBB_add_asn1_octet_string(&child,
+ (const uint8_t *)in->psk_identity.get(),
+ strlen(in->psk_identity.get()))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
}
- if (in->tlsext_tick_lifetime_hint > 0) {
+ if (in->ticket_lifetime_hint > 0) {
if (!CBB_add_asn1(&session, &child, kTicketLifetimeHintTag) ||
- !CBB_add_asn1_uint64(&child, in->tlsext_tick_lifetime_hint)) {
+ !CBB_add_asn1_uint64(&child, in->ticket_lifetime_hint)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
}
- if (in->tlsext_tick && !for_ticket) {
+ if (!in->ticket.empty() && !for_ticket) {
if (!CBB_add_asn1(&session, &child, kTicketTag) ||
- !CBB_add_asn1_octet_string(&child, in->tlsext_tick,
- in->tlsext_ticklen)) {
+ !CBB_add_asn1_octet_string(&child, in->ticket.data(),
+ in->ticket.size())) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
@@ -297,8 +298,8 @@
if (in->signed_cert_timestamp_list != nullptr) {
if (!CBB_add_asn1(&session, &child, kSignedCertTimestampListTag) ||
!CBB_add_asn1_octet_string(
- &child, CRYPTO_BUFFER_data(in->signed_cert_timestamp_list),
- CRYPTO_BUFFER_len(in->signed_cert_timestamp_list))) {
+ &child, CRYPTO_BUFFER_data(in->signed_cert_timestamp_list.get()),
+ CRYPTO_BUFFER_len(in->signed_cert_timestamp_list.get()))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
@@ -306,9 +307,9 @@
if (in->ocsp_response != nullptr) {
if (!CBB_add_asn1(&session, &child, kOCSPResponseTag) ||
- !CBB_add_asn1_octet_string(&child,
- CRYPTO_BUFFER_data(in->ocsp_response),
- CRYPTO_BUFFER_len(in->ocsp_response))) {
+ !CBB_add_asn1_octet_string(
+ &child, CRYPTO_BUFFER_data(in->ocsp_response.get()),
+ CRYPTO_BUFFER_len(in->ocsp_response.get()))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
@@ -333,13 +334,13 @@
// serialized instead.
if (in->certs != NULL &&
!in->peer_sha256_valid &&
- sk_CRYPTO_BUFFER_num(in->certs) >= 2) {
+ sk_CRYPTO_BUFFER_num(in->certs.get()) >= 2) {
if (!CBB_add_asn1(&session, &child, kCertChainTag)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
- for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(in->certs); i++) {
- const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(in->certs, i);
+ for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(in->certs.get()); i++) {
+ const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(in->certs.get(), i);
if (!CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer),
CRYPTO_BUFFER_len(buffer))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
@@ -386,10 +387,10 @@
return 0;
}
- if (in->early_alpn) {
+ if (!in->early_alpn.empty()) {
if (!CBB_add_asn1(&session, &child, kEarlyALPNTag) ||
- !CBB_add_asn1_octet_string(&child, (const uint8_t *)in->early_alpn,
- in->early_alpn_len)) {
+ !CBB_add_asn1_octet_string(&child, in->early_alpn.data(),
+ in->early_alpn.size())) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
@@ -398,13 +399,11 @@
return CBB_flush(cbb);
}
-// SSL_SESSION_parse_string gets an optional ASN.1 OCTET STRING
-// explicitly tagged with |tag| from |cbs| and saves it in |*out|. On
-// entry, if |*out| is not NULL, it frees the existing contents. If
-// the element was not found, it sets |*out| to NULL. It returns one
-// on success, whether or not the element was found, and zero on
-// decode error.
-static int SSL_SESSION_parse_string(CBS *cbs, char **out, unsigned tag) {
+// SSL_SESSION_parse_string gets an optional ASN.1 OCTET STRING explicitly
+// tagged with |tag| from |cbs| and saves it in |*out|. If the element was not
+// found, it sets |*out| to NULL. It returns one on success, whether or not the
+// element was found, and zero on decode error.
+static int SSL_SESSION_parse_string(CBS *cbs, UniquePtr<char> *out, unsigned tag) {
CBS value;
int present;
if (!CBS_get_optional_asn1_octet_string(cbs, &value, &present, tag)) {
@@ -416,38 +415,33 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return 0;
}
- if (!CBS_strdup(&value, out)) {
+ char *raw = nullptr;
+ if (!CBS_strdup(&value, &raw)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
+ out->reset(raw);
} else {
- OPENSSL_free(*out);
- *out = NULL;
+ out->reset();
}
return 1;
}
-// SSL_SESSION_parse_string gets an optional ASN.1 OCTET STRING
-// explicitly tagged with |tag| from |cbs| and stows it in |*out_ptr|
-// and |*out_len|. If |*out_ptr| is not NULL, it frees the existing
-// contents. On entry, if the element was not found, it sets
-// |*out_ptr| to NULL. It returns one on success, whether or not the
-// element was found, and zero on decode error.
-static int SSL_SESSION_parse_octet_string(CBS *cbs, uint8_t **out_ptr,
- size_t *out_len, unsigned tag) {
+// SSL_SESSION_parse_octet_string gets an optional ASN.1 OCTET STRING explicitly
+// tagged with |tag| from |cbs| and stows it in |*out|. It returns one on
+// success, whether or not the element was found, and zero on decode error.
+static bool SSL_SESSION_parse_octet_string(CBS *cbs, Array<uint8_t> *out,
+ unsigned tag) {
CBS value;
if (!CBS_get_optional_asn1_octet_string(cbs, &value, NULL, tag)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
- return 0;
+ return false;
}
- if (!CBS_stow(&value, out_ptr, out_len)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
- }
- return 1;
+ return out->CopyFrom(value);
}
-static int SSL_SESSION_parse_crypto_buffer(CBS *cbs, CRYPTO_BUFFER **out,
+static int SSL_SESSION_parse_crypto_buffer(CBS *cbs,
+ UniquePtr<CRYPTO_BUFFER> *out,
unsigned tag,
CRYPTO_BUFFER_POOL *pool) {
if (!CBS_peek_asn1_tag(cbs, tag)) {
@@ -461,8 +455,7 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return 0;
}
- CRYPTO_BUFFER_free(*out);
- *out = CRYPTO_BUFFER_new_from_CBS(&value, pool);
+ out->reset(CRYPTO_BUFFER_new_from_CBS(&value, pool));
if (*out == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
@@ -617,10 +610,9 @@
if (!SSL_SESSION_parse_string(&session, &ret->psk_identity,
kPSKIdentityTag) ||
- !SSL_SESSION_parse_u32(&session, &ret->tlsext_tick_lifetime_hint,
+ !SSL_SESSION_parse_u32(&session, &ret->ticket_lifetime_hint,
kTicketLifetimeHintTag, 0) ||
- !SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick,
- &ret->tlsext_ticklen, kTicketTag)) {
+ !SSL_SESSION_parse_octet_string(&session, &ret->ticket, kTicketTag)) {
return nullptr;
}
@@ -680,8 +672,8 @@
return nullptr;
}
if (has_peer || has_cert_chain) {
- ret->certs = sk_CRYPTO_BUFFER_new_null();
- if (ret->certs == NULL) {
+ ret->certs.reset(sk_CRYPTO_BUFFER_new_null());
+ if (ret->certs == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return nullptr;
}
@@ -689,7 +681,7 @@
if (has_peer) {
UniquePtr<CRYPTO_BUFFER> buffer(CRYPTO_BUFFER_new_from_CBS(&peer, pool));
if (!buffer ||
- !PushToStack(ret->certs, std::move(buffer))) {
+ !PushToStack(ret->certs.get(), std::move(buffer))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return nullptr;
}
@@ -703,10 +695,9 @@
return nullptr;
}
- CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&cert, pool);
- if (buffer == NULL ||
- !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) {
- CRYPTO_BUFFER_free(buffer);
+ UniquePtr<CRYPTO_BUFFER> buffer(CRYPTO_BUFFER_new_from_CBS(&cert, pool));
+ if (buffer == nullptr ||
+ !PushToStack(ret->certs.get(), std::move(buffer))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return nullptr;
}
@@ -727,7 +718,7 @@
CBS_len(&age_add) != 0) {
return nullptr;
}
- ret->ticket_age_add_valid = age_add_present;
+ ret->ticket_age_add_valid = age_add_present != 0;
int is_server;
if (!CBS_get_optional_asn1_bool(&session, &is_server, kIsServerTag,
@@ -747,7 +738,7 @@
!SSL_SESSION_parse_u32(&session, &ret->auth_timeout, kAuthTimeoutTag,
ret->timeout) ||
!SSL_SESSION_parse_octet_string(&session, &ret->early_alpn,
- &ret->early_alpn_len, kEarlyALPNTag) ||
+ kEarlyALPNTag) ||
CBS_len(&session) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
return nullptr;
diff --git a/src/ssl/ssl_buffer.cc b/src/ssl/ssl_buffer.cc
index da1de93..72647a4 100644
--- a/src/ssl/ssl_buffer.cc
+++ b/src/ssl/ssl_buffer.cc
@@ -113,7 +113,8 @@
}
// Read a single packet from |ssl->rbio|. |buf->cap()| must fit in an int.
- int ret = BIO_read(ssl->rbio, buf->data(), static_cast<int>(buf->cap()));
+ int ret =
+ BIO_read(ssl->rbio.get(), buf->data(), static_cast<int>(buf->cap()));
if (ret <= 0) {
ssl->s3->rwstate = SSL_READING;
return ret;
@@ -134,7 +135,7 @@
while (buf->size() < len) {
// The amount of data to read is bounded by |buf->cap|, which must fit in an
// int.
- int ret = BIO_read(ssl->rbio, buf->data() + buf->size(),
+ int ret = BIO_read(ssl->rbio.get(), buf->data() + buf->size(),
static_cast<int>(len - buf->size()));
if (ret <= 0) {
ssl->s3->rwstate = SSL_READING;
@@ -163,7 +164,7 @@
return -1;
}
- if (ssl->rbio == NULL) {
+ if (ssl->rbio == nullptr) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET);
return -1;
}
@@ -240,7 +241,7 @@
SSLBuffer *buf = &ssl->s3->write_buffer;
while (!buf->empty()) {
- int ret = BIO_write(ssl->wbio, buf->data(), buf->size());
+ int ret = BIO_write(ssl->wbio.get(), buf->data(), buf->size());
if (ret <= 0) {
ssl->s3->rwstate = SSL_WRITING;
return ret;
@@ -257,7 +258,7 @@
return 1;
}
- int ret = BIO_write(ssl->wbio, buf->data(), buf->size());
+ int ret = BIO_write(ssl->wbio.get(), buf->data(), buf->size());
if (ret <= 0) {
ssl->s3->rwstate = SSL_WRITING;
// If the write failed, drop the write buffer anyway. Datagram transports
@@ -271,7 +272,7 @@
}
int ssl_write_buffer_flush(SSL *ssl) {
- if (ssl->wbio == NULL) {
+ if (ssl->wbio == nullptr) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET);
return -1;
}
diff --git a/src/ssl/ssl_cert.cc b/src/ssl/ssl_cert.cc
index 20b4514..4842974 100644
--- a/src/ssl/ssl_cert.cc
+++ b/src/ssl/ssl_cert.cc
@@ -136,7 +136,7 @@
namespace bssl {
CERT::CERT(const SSL_X509_METHOD *x509_method_arg)
- : x509_method(x509_method_arg), enable_early_data(false) {}
+ : x509_method(x509_method_arg) {}
CERT::~CERT() {
ssl_cert_clear_certs(this);
@@ -162,11 +162,7 @@
}
}
- if (cert->privatekey) {
- EVP_PKEY_up_ref(cert->privatekey.get());
- ret->privatekey.reset(cert->privatekey.get());
- }
-
+ ret->privatekey = UpRef(cert->privatekey);
ret->key_method = cert->key_method;
if (!ret->sigalgs.CopyFrom(cert->sigalgs)) {
@@ -178,22 +174,12 @@
ret->x509_method->cert_dup(ret.get(), cert);
- if (cert->signed_cert_timestamp_list) {
- CRYPTO_BUFFER_up_ref(cert->signed_cert_timestamp_list.get());
- ret->signed_cert_timestamp_list.reset(
- cert->signed_cert_timestamp_list.get());
- }
-
- if (cert->ocsp_response) {
- CRYPTO_BUFFER_up_ref(cert->ocsp_response.get());
- ret->ocsp_response.reset(cert->ocsp_response.get());
- }
+ ret->signed_cert_timestamp_list = UpRef(cert->signed_cert_timestamp_list);
+ ret->ocsp_response = UpRef(cert->ocsp_response);
ret->sid_ctx_length = cert->sid_ctx_length;
OPENSSL_memcpy(ret->sid_ctx, cert->sid_ctx, sizeof(ret->sid_ctx));
- ret->enable_early_data = cert->enable_early_data;
-
return ret;
}
@@ -291,16 +277,12 @@
}
for (size_t i = 0; i < num_certs; i++) {
- if (!sk_CRYPTO_BUFFER_push(certs_sk.get(), certs[i])) {
+ if (!PushToStack(certs_sk.get(), UpRef(certs[i]))) {
return 0;
}
- CRYPTO_BUFFER_up_ref(certs[i]);
}
- if (privkey != nullptr) {
- EVP_PKEY_up_ref(privkey);
- }
- cert->privatekey.reset(privkey);
+ cert->privatekey = UpRef(privkey);
cert->key_method = privkey_method;
cert->chain = std::move(certs_sk);
@@ -342,10 +324,10 @@
return 1;
}
-int ssl_has_certificate(const SSL *ssl) {
- return ssl->cert->chain != nullptr &&
- sk_CRYPTO_BUFFER_value(ssl->cert->chain.get(), 0) != nullptr &&
- ssl_has_private_key(ssl);
+int ssl_has_certificate(const SSL_CONFIG *cfg) {
+ return cfg->cert->chain != nullptr &&
+ sk_CRYPTO_BUFFER_value(cfg->cert->chain.get(), 0) != nullptr &&
+ ssl_has_private_key(cfg);
}
bool ssl_parse_cert_chain(uint8_t *out_alert,
@@ -412,8 +394,8 @@
return true;
}
-int ssl_add_cert_chain(SSL *ssl, CBB *cbb) {
- if (!ssl_has_certificate(ssl)) {
+int ssl_add_cert_chain(SSL_HANDSHAKE *hs, CBB *cbb) {
+ if (!ssl_has_certificate(hs->config)) {
return CBB_add_u24(cbb, 0);
}
@@ -423,7 +405,7 @@
return 0;
}
- STACK_OF(CRYPTO_BUFFER) *chain = ssl->cert->chain.get();
+ STACK_OF(CRYPTO_BUFFER) *chain = hs->config->cert->chain.get();
for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(chain); i++) {
CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(chain, i);
CBB child;
@@ -665,7 +647,7 @@
}
if (!ssl->ctx->x509_method->check_client_CA_list(ret.get())) {
- *out_alert = SSL_AD_INTERNAL_ERROR;
+ *out_alert = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
return nullptr;
}
@@ -673,26 +655,26 @@
return ret;
}
-bool ssl_has_client_CAs(SSL *ssl) {
- STACK_OF(CRYPTO_BUFFER) *names = ssl->client_CA;
- if (names == NULL) {
- names = ssl->ctx->client_CA;
+bool ssl_has_client_CAs(const SSL_CONFIG *cfg) {
+ const STACK_OF(CRYPTO_BUFFER) *names = cfg->client_CA.get();
+ if (names == nullptr) {
+ names = cfg->ssl->ctx->client_CA.get();
}
- if (names == NULL) {
+ if (names == nullptr) {
return false;
}
return sk_CRYPTO_BUFFER_num(names) > 0;
}
-int ssl_add_client_CA_list(SSL *ssl, CBB *cbb) {
+int ssl_add_client_CA_list(SSL_HANDSHAKE *hs, CBB *cbb) {
CBB child, name_cbb;
if (!CBB_add_u16_length_prefixed(cbb, &child)) {
return 0;
}
- STACK_OF(CRYPTO_BUFFER) *names = ssl->client_CA;
+ const STACK_OF(CRYPTO_BUFFER) *names = hs->config->client_CA.get();
if (names == NULL) {
- names = ssl->ctx->client_CA;
+ names = hs->ssl->ctx->client_CA.get();
}
if (names == NULL) {
return CBB_flush(cbb);
@@ -711,8 +693,7 @@
int ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
const CRYPTO_BUFFER *leaf) {
- SSL *const ssl = hs->ssl;
- assert(ssl_protocol_version(ssl) < TLS1_3_VERSION);
+ assert(ssl_protocol_version(hs->ssl) < TLS1_3_VERSION);
// Check the certificate's type matches the cipher.
if (!(hs->new_cipher->algorithm_auth & ssl_cipher_auth_mask_for_key(pkey))) {
@@ -740,7 +721,7 @@
uint16_t group_id;
if (!ssl_nid_to_group_id(
&group_id, EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key))) ||
- !tls1_check_group_id(ssl, group_id) ||
+ !tls1_check_group_id(hs, group_id) ||
EC_KEY_get_conv_form(ec_key) != POINT_CONVERSION_UNCOMPRESSED) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECC_CERT);
return 0;
@@ -752,18 +733,18 @@
int ssl_on_certificate_selected(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- if (!ssl_has_certificate(ssl)) {
+ if (!ssl_has_certificate(hs->config)) {
// Nothing to do.
return 1;
}
- if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(ssl)) {
+ if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(hs)) {
return 0;
}
CBS leaf;
- CRYPTO_BUFFER_init_CBS(sk_CRYPTO_BUFFER_value(ssl->cert->chain.get(), 0),
- &leaf);
+ CRYPTO_BUFFER_init_CBS(
+ sk_CRYPTO_BUFFER_value(hs->config->cert->chain.get(), 0), &leaf);
hs->local_pubkey = ssl_cert_parse_pubkey(&leaf);
return hs->local_pubkey != NULL;
@@ -776,14 +757,17 @@
int SSL_set_chain_and_key(SSL *ssl, CRYPTO_BUFFER *const *certs,
size_t num_certs, EVP_PKEY *privkey,
const SSL_PRIVATE_KEY_METHOD *privkey_method) {
- return cert_set_chain_and_key(ssl->cert, certs, num_certs, privkey,
- privkey_method);
+ if (!ssl->config) {
+ return 0;
+ }
+ return cert_set_chain_and_key(ssl->config->cert.get(), certs, num_certs,
+ privkey, privkey_method);
}
int SSL_CTX_set_chain_and_key(SSL_CTX *ctx, CRYPTO_BUFFER *const *certs,
size_t num_certs, EVP_PKEY *privkey,
const SSL_PRIVATE_KEY_METHOD *privkey_method) {
- return cert_set_chain_and_key(ctx->cert, certs, num_certs, privkey,
+ return cert_set_chain_and_key(ctx->cert.get(), certs, num_certs, privkey,
privkey_method);
}
@@ -794,37 +778,40 @@
return 0;
}
- return ssl_set_cert(ctx->cert, std::move(buffer));
+ return ssl_set_cert(ctx->cert.get(), std::move(buffer));
}
int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
UniquePtr<CRYPTO_BUFFER> buffer(CRYPTO_BUFFER_new(der, der_len, NULL));
- if (!buffer) {
+ if (!buffer || !ssl->config) {
return 0;
}
- return ssl_set_cert(ssl->cert, std::move(buffer));
+ return ssl_set_cert(ssl->config->cert.get(), std::move(buffer));
}
void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg),
void *arg) {
- ssl_cert_set_cert_cb(ctx->cert, cb, arg);
+ ssl_cert_set_cert_cb(ctx->cert.get(), cb, arg);
}
void SSL_set_cert_cb(SSL *ssl, int (*cb)(SSL *ssl, void *arg), void *arg) {
- ssl_cert_set_cert_cb(ssl->cert, cb, arg);
+ if (!ssl->config) {
+ return;
+ }
+ ssl_cert_set_cert_cb(ssl->config->cert.get(), cb, arg);
}
-STACK_OF(CRYPTO_BUFFER) *SSL_get0_peer_certificates(const SSL *ssl) {
+const STACK_OF(CRYPTO_BUFFER) *SSL_get0_peer_certificates(const SSL *ssl) {
SSL_SESSION *session = SSL_get_session(ssl);
if (session == NULL) {
return NULL;
}
- return session->certs;
+ return session->certs.get();
}
-STACK_OF(CRYPTO_BUFFER) *SSL_get0_server_requested_CAs(const SSL *ssl) {
+const STACK_OF(CRYPTO_BUFFER) *SSL_get0_server_requested_CAs(const SSL *ssl) {
if (ssl->s3->hs == NULL) {
return NULL;
}
@@ -847,12 +834,16 @@
int SSL_CTX_set_signed_cert_timestamp_list(SSL_CTX *ctx, const uint8_t *list,
size_t list_len) {
- return set_signed_cert_timestamp_list(ctx->cert, list, list_len);
+ return set_signed_cert_timestamp_list(ctx->cert.get(), list, list_len);
}
int SSL_set_signed_cert_timestamp_list(SSL *ssl, const uint8_t *list,
size_t list_len) {
- return set_signed_cert_timestamp_list(ssl->cert, list, list_len);
+ if (!ssl->config) {
+ return 0;
+ }
+ return set_signed_cert_timestamp_list(ssl->config->cert.get(), list,
+ list_len);
}
int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response,
@@ -864,19 +855,23 @@
int SSL_set_ocsp_response(SSL *ssl, const uint8_t *response,
size_t response_len) {
- ssl->cert->ocsp_response.reset(
+ if (!ssl->config) {
+ return 0;
+ }
+ ssl->config->cert->ocsp_response.reset(
CRYPTO_BUFFER_new(response, response_len, nullptr));
- return ssl->cert->ocsp_response != nullptr;
+ return ssl->config->cert->ocsp_response != nullptr;
}
void SSL_CTX_set0_client_CAs(SSL_CTX *ctx, STACK_OF(CRYPTO_BUFFER) *name_list) {
ctx->x509_method->ssl_ctx_flush_cached_client_CA(ctx);
- sk_CRYPTO_BUFFER_pop_free(ctx->client_CA, CRYPTO_BUFFER_free);
- ctx->client_CA = name_list;
+ ctx->client_CA.reset(name_list);
}
void SSL_set0_client_CAs(SSL *ssl, STACK_OF(CRYPTO_BUFFER) *name_list) {
- ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl);
- sk_CRYPTO_BUFFER_pop_free(ssl->client_CA, CRYPTO_BUFFER_free);
- ssl->client_CA = name_list;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl->config.get());
+ ssl->config->client_CA.reset(name_list);
}
diff --git a/src/ssl/ssl_cipher.cc b/src/ssl/ssl_cipher.cc
index f02fa8a..5899500 100644
--- a/src/ssl/ssl_cipher.cc
+++ b/src/ssl/ssl_cipher.cc
@@ -157,7 +157,7 @@
namespace bssl {
// kCiphers is an array of all supported ciphers, sorted by id.
-static const SSL_CIPHER kCiphers[] = {
+static constexpr SSL_CIPHER kCiphers[] = {
// The RSA ciphers
// Cipher 02
{
@@ -210,33 +210,6 @@
SSL_HANDSHAKE_MAC_DEFAULT,
},
-
- // TLS v1.2 ciphersuites
-
- // Cipher 3C
- {
- TLS1_TXT_RSA_WITH_AES_128_SHA256,
- "TLS_RSA_WITH_AES_128_CBC_SHA256",
- TLS1_CK_RSA_WITH_AES_128_SHA256,
- SSL_kRSA,
- SSL_aRSA,
- SSL_AES128,
- SSL_SHA256,
- SSL_HANDSHAKE_MAC_SHA256,
- },
-
- // Cipher 3D
- {
- TLS1_TXT_RSA_WITH_AES_256_SHA256,
- "TLS_RSA_WITH_AES_256_CBC_SHA256",
- TLS1_CK_RSA_WITH_AES_256_SHA256,
- SSL_kRSA,
- SSL_aRSA,
- SSL_AES256,
- SSL_SHA256,
- SSL_HANDSHAKE_MAC_SHA256,
- },
-
// PSK cipher suites.
// Cipher 8C
@@ -375,58 +348,6 @@
SSL_HANDSHAKE_MAC_DEFAULT,
},
-
- // HMAC based TLS v1.2 ciphersuites from RFC5289
-
- // Cipher C023
- {
- TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256,
- "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
- TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256,
- SSL_kECDHE,
- SSL_aECDSA,
- SSL_AES128,
- SSL_SHA256,
- SSL_HANDSHAKE_MAC_SHA256,
- },
-
- // Cipher C024
- {
- TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384,
- "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
- TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384,
- SSL_kECDHE,
- SSL_aECDSA,
- SSL_AES256,
- SSL_SHA384,
- SSL_HANDSHAKE_MAC_SHA384,
- },
-
- // Cipher C027
- {
- TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256,
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
- TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
- SSL_kECDHE,
- SSL_aRSA,
- SSL_AES128,
- SSL_SHA256,
- SSL_HANDSHAKE_MAC_SHA256,
- },
-
- // Cipher C028
- {
- TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384,
- "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
- TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
- SSL_kECDHE,
- SSL_aRSA,
- SSL_AES256,
- SSL_SHA384,
- SSL_HANDSHAKE_MAC_SHA384,
- },
-
-
// GCM based TLS v1.2 ciphersuites from RFC5289
// Cipher C02B
@@ -616,8 +537,6 @@
// MAC aliases
{"SHA1", ~0u, ~0u, ~0u, SSL_SHA1, 0},
{"SHA", ~0u, ~0u, ~0u, SSL_SHA1, 0},
- {"SHA256", ~0u, ~0u, ~0u, SSL_SHA256, 0},
- {"SHA384", ~0u, ~0u, ~0u, SSL_SHA384, 0},
// Legacy protocol minimum version aliases. "TLSv1" is intentionally the
// same as "SSLv3".
@@ -628,23 +547,15 @@
// Legacy strength classes.
{"HIGH", ~0u, ~0u, ~0u, ~0u, 0},
{"FIPS", ~0u, ~0u, ~0u, ~0u, 0},
+
+ // Temporary no-op aliases corresponding to removed SHA-2 legacy CBC
+ // ciphers. These should be removed after 2018-05-14.
+ {"SHA256", 0, 0, 0, 0, 0},
+ {"SHA384", 0, 0, 0, 0, 0},
};
static const size_t kCipherAliasesLen = OPENSSL_ARRAY_SIZE(kCipherAliases);
-static int ssl_cipher_id_cmp(const void *in_a, const void *in_b) {
- const SSL_CIPHER *a = reinterpret_cast<const SSL_CIPHER *>(in_a);
- const SSL_CIPHER *b = reinterpret_cast<const SSL_CIPHER *>(in_b);
-
- if (a->id > b->id) {
- return 1;
- } else if (a->id < b->id) {
- return -1;
- } else {
- return 0;
- }
-}
-
bool ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead,
size_t *out_mac_secret_len,
size_t *out_fixed_iv_len, const SSL_CIPHER *cipher,
@@ -654,15 +565,26 @@
*out_fixed_iv_len = 0;
const int is_tls12 = version == TLS1_2_VERSION && !is_dtls;
+ const int is_tls13 = version == TLS1_3_VERSION && !is_dtls;
if (cipher->algorithm_mac == SSL_AEAD) {
if (cipher->algorithm_enc == SSL_AES128GCM) {
- *out_aead =
- is_tls12 ? EVP_aead_aes_128_gcm_tls12() : EVP_aead_aes_128_gcm();
+ if (is_tls12) {
+ *out_aead = EVP_aead_aes_128_gcm_tls12();
+ } else if (is_tls13) {
+ *out_aead = EVP_aead_aes_128_gcm_tls13();
+ } else {
+ *out_aead = EVP_aead_aes_128_gcm();
+ }
*out_fixed_iv_len = 4;
} else if (cipher->algorithm_enc == SSL_AES256GCM) {
- *out_aead =
- is_tls12 ? EVP_aead_aes_256_gcm_tls12() : EVP_aead_aes_256_gcm();
+ if (is_tls12) {
+ *out_aead = EVP_aead_aes_256_gcm_tls12();
+ } else if (is_tls13) {
+ *out_aead = EVP_aead_aes_256_gcm_tls13();
+ } else {
+ *out_aead = EVP_aead_aes_256_gcm();
+ }
*out_fixed_iv_len = 4;
} else if (cipher->algorithm_enc == SSL_CHACHA20POLY1305) {
*out_aead = EVP_aead_chacha20_poly1305();
@@ -678,36 +600,23 @@
}
} else if (cipher->algorithm_mac == SSL_SHA1) {
if (cipher->algorithm_enc == SSL_eNULL) {
- if (version == SSL3_VERSION) {
- *out_aead = EVP_aead_null_sha1_ssl3();
- } else {
- *out_aead = EVP_aead_null_sha1_tls();
- }
+ *out_aead = EVP_aead_null_sha1_tls();
} else if (cipher->algorithm_enc == SSL_3DES) {
- if (version == SSL3_VERSION) {
- *out_aead = EVP_aead_des_ede3_cbc_sha1_ssl3();
- *out_fixed_iv_len = 8;
- } else if (version == TLS1_VERSION) {
+ if (version == TLS1_VERSION) {
*out_aead = EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv();
*out_fixed_iv_len = 8;
} else {
*out_aead = EVP_aead_des_ede3_cbc_sha1_tls();
}
} else if (cipher->algorithm_enc == SSL_AES128) {
- if (version == SSL3_VERSION) {
- *out_aead = EVP_aead_aes_128_cbc_sha1_ssl3();
- *out_fixed_iv_len = 16;
- } else if (version == TLS1_VERSION) {
+ if (version == TLS1_VERSION) {
*out_aead = EVP_aead_aes_128_cbc_sha1_tls_implicit_iv();
*out_fixed_iv_len = 16;
} else {
*out_aead = EVP_aead_aes_128_cbc_sha1_tls();
}
} else if (cipher->algorithm_enc == SSL_AES256) {
- if (version == SSL3_VERSION) {
- *out_aead = EVP_aead_aes_256_cbc_sha1_ssl3();
- *out_fixed_iv_len = 16;
- } else if (version == TLS1_VERSION) {
+ if (version == TLS1_VERSION) {
*out_aead = EVP_aead_aes_256_cbc_sha1_tls_implicit_iv();
*out_fixed_iv_len = 16;
} else {
@@ -718,23 +627,6 @@
}
*out_mac_secret_len = SHA_DIGEST_LENGTH;
- } else if (cipher->algorithm_mac == SSL_SHA256) {
- if (cipher->algorithm_enc == SSL_AES128) {
- *out_aead = EVP_aead_aes_128_cbc_sha256_tls();
- } else if (cipher->algorithm_enc == SSL_AES256) {
- *out_aead = EVP_aead_aes_256_cbc_sha256_tls();
- } else {
- return false;
- }
-
- *out_mac_secret_len = SHA256_DIGEST_LENGTH;
- } else if (cipher->algorithm_mac == SSL_SHA384) {
- if (cipher->algorithm_enc != SSL_AES256) {
- return false;
- }
-
- *out_aead = EVP_aead_aes_256_cbc_sha384_tls();
- *out_mac_secret_len = SHA384_DIGEST_LENGTH;
} else {
return false;
}
@@ -1232,7 +1124,7 @@
return true;
}
-bool ssl_create_cipher_list(SSLCipherPreferenceList **out_cipher_list,
+bool ssl_create_cipher_list(UniquePtr<SSLCipherPreferenceList> *out_cipher_list,
const char *rule_str, bool strict) {
// Return with error if nothing to do.
if (rule_str == NULL || out_cipher_list == NULL) {
@@ -1350,10 +1242,7 @@
return false;
}
- if (*out_cipher_list) {
- Delete(*out_cipher_list);
- }
- *out_cipher_list = pref_list.release();
+ *out_cipher_list = std::move(pref_list);
// Configuring an empty cipher list is an error but still updates the
// output.
@@ -1367,7 +1256,8 @@
uint16_t ssl_cipher_get_value(const SSL_CIPHER *cipher) {
uint32_t id = cipher->id;
- // All ciphers are SSLv3.
+ // All OpenSSL cipher IDs are prefaced with 0x03. Historically this referred
+ // to SSLv2 vs SSLv3.
assert((id & 0xff000000) == 0x03000000);
return id & 0xffff;
}
@@ -1420,6 +1310,36 @@
using namespace bssl;
+static constexpr int ssl_cipher_id_cmp_inner(const SSL_CIPHER *a,
+ const SSL_CIPHER *b) {
+ // C++11's constexpr functions must have a body consisting of just a
+ // return-statement.
+ return (a->id > b->id) ? 1 : ((a->id < b->id) ? -1 : 0);
+}
+
+static int ssl_cipher_id_cmp(const void *in_a, const void *in_b) {
+ return ssl_cipher_id_cmp_inner(reinterpret_cast<const SSL_CIPHER *>(in_a),
+ reinterpret_cast<const SSL_CIPHER *>(in_b));
+}
+
+template <typename T, size_t N>
+static constexpr size_t countof(T const (&)[N]) {
+ return N;
+}
+
+template <typename T, size_t I>
+static constexpr int check_order(const T (&arr)[I], size_t N) {
+ // C++11's constexpr functions must have a body consisting of just a
+ // return-statement.
+ return N > 1 ? ((ssl_cipher_id_cmp_inner(&arr[N - 2], &arr[N - 1]) < 0)
+ ? check_order(arr, N - 1)
+ : 0)
+ : 1;
+}
+
+static_assert(check_order(kCiphers, countof(kCiphers)) == 1,
+ "Ciphers are not sorted, bsearch won't work");
+
const SSL_CIPHER *SSL_get_cipher_by_value(uint16_t value) {
SSL_CIPHER c;
@@ -1461,10 +1381,6 @@
return NID_undef;
case SSL_SHA1:
return NID_sha1;
- case SSL_SHA256:
- return NID_sha256;
- case SSL_SHA384:
- return NID_sha384;
}
assert(0);
return NID_undef;
@@ -1731,14 +1647,6 @@
mac = "SHA1";
break;
- case SSL_SHA256:
- mac = "SHA256";
- break;
-
- case SSL_SHA384:
- mac = "SHA384";
- break;
-
case SSL_AEAD:
mac = "AEAD";
break;
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index 78a1860..36c26cd 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -219,11 +219,11 @@
return true;
}
-int ssl_can_write(const SSL *ssl) {
+bool ssl_can_write(const SSL *ssl) {
return !SSL_in_init(ssl) || ssl->s3->hs->can_early_write;
}
-int ssl_can_read(const SSL *ssl) {
+bool ssl_can_read(const SSL *ssl) {
return !SSL_in_init(ssl) || ssl->s3->hs->can_early_read;
}
@@ -274,7 +274,7 @@
void ssl_update_cache(SSL_HANDSHAKE *hs, int mode) {
SSL *const ssl = hs->ssl;
- SSL_CTX *ctx = ssl->session_ctx;
+ SSL_CTX *ctx = ssl->session_ctx.get();
// Never cache sessions with empty session IDs.
if (ssl->s3->established_session->session_id_length == 0 ||
ssl->s3->established_session->not_resumable ||
@@ -289,16 +289,16 @@
// A client may see new sessions on abbreviated handshakes if the server
// decides to renew the ticket. Once the handshake is completed, it should be
// inserted into the cache.
- if (ssl->s3->established_session.get() != ssl->session ||
+ if (ssl->s3->established_session.get() != ssl->session.get() ||
(!ssl->server && hs->ticket_expected)) {
if (use_internal_cache) {
SSL_CTX_add_session(ctx, ssl->s3->established_session.get());
}
if (ctx->new_session_cb != NULL) {
- SSL_SESSION_up_ref(ssl->s3->established_session.get());
- if (!ctx->new_session_cb(ssl, ssl->s3->established_session.get())) {
+ UniquePtr<SSL_SESSION> ref = UpRef(ssl->s3->established_session);
+ if (ctx->new_session_cb(ssl, ref.get())) {
// |new_session_cb|'s return value signals whether it took ownership.
- SSL_SESSION_free(ssl->s3->established_session.get());
+ ref.release();
}
}
}
@@ -406,7 +406,7 @@
void ssl_get_current_time(const SSL *ssl, struct OPENSSL_timeval *out_clock) {
// TODO(martinkr): Change callers to |ssl_ctx_get_current_time| and drop the
// |ssl| arg from |current_time_cb| if possible.
- ssl_ctx_get_current_time(ssl->ctx, out_clock);
+ ssl_ctx_get_current_time(ssl->ctx.get(), out_clock);
}
void ssl_ctx_get_current_time(const SSL_CTX *ctx,
@@ -459,6 +459,53 @@
ctx->handoff = on;
}
+static bool ssl_can_renegotiate(const SSL *ssl) {
+ if (ssl->server || SSL_is_dtls(ssl)) {
+ return false;
+ }
+
+ if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+ return false;
+ }
+
+ // The config has already been shed.
+ if (!ssl->config) {
+ return false;
+ }
+
+ switch (ssl->renegotiate_mode) {
+ case ssl_renegotiate_ignore:
+ case ssl_renegotiate_never:
+ return false;
+
+ case ssl_renegotiate_freely:
+ return true;
+ case ssl_renegotiate_once:
+ return ssl->s3->total_renegotiations == 0;
+ }
+
+ assert(0);
+ return false;
+}
+
+static void ssl_maybe_shed_handshake_config(SSL *ssl) {
+ if (ssl->s3->hs != nullptr ||
+ ssl->config == nullptr ||
+ !ssl->config->shed_handshake_config ||
+ ssl_can_renegotiate(ssl)) {
+ return;
+ }
+
+ ssl->config.reset();
+}
+
+void SSL_set_handoff_mode(SSL *ssl, bool on) {
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->handoff = on;
+}
+
} // namespace bssl
using namespace bssl;
@@ -474,34 +521,11 @@
}
static uint32_t ssl_session_hash(const SSL_SESSION *sess) {
- const uint8_t *session_id = sess->session_id;
-
- uint8_t tmp_storage[sizeof(uint32_t)];
- if (sess->session_id_length < sizeof(tmp_storage)) {
- OPENSSL_memset(tmp_storage, 0, sizeof(tmp_storage));
- OPENSSL_memcpy(tmp_storage, sess->session_id, sess->session_id_length);
- session_id = tmp_storage;
- }
-
- uint32_t hash =
- ((uint32_t)session_id[0]) |
- ((uint32_t)session_id[1] << 8) |
- ((uint32_t)session_id[2] << 16) |
- ((uint32_t)session_id[3] << 24);
-
- return hash;
+ return ssl_hash_session_id(
+ MakeConstSpan(sess->session_id, sess->session_id_length));
}
-// NB: If this function (or indeed the hash function which uses a sort of
-// coarser function than this one) is changed, ensure
-// SSL_CTX_has_matching_session_id() is checked accordingly. It relies on being
-// able to construct an SSL_SESSION that will collide with any existing session
-// with a matching session ID.
static int ssl_session_cmp(const SSL_SESSION *a, const SSL_SESSION *b) {
- if (a->ssl_version != b->ssl_version) {
- return 1;
- }
-
if (a->session_id_length != b->session_id_length) {
return 1;
}
@@ -509,84 +533,69 @@
return OPENSSL_memcmp(a->session_id, b->session_id, a->session_id_length);
}
-SSL_CTX *SSL_CTX_new(const SSL_METHOD *method) {
- SSL_CTX *ret = NULL;
+ssl_ctx_st::ssl_ctx_st(const SSL_METHOD *ssl_method)
+ : method(ssl_method->method),
+ x509_method(ssl_method->x509_method),
+ retain_only_sha256_of_client_certs(false),
+ quiet_shutdown(false),
+ ocsp_stapling_enabled(false),
+ signed_cert_timestamps_enabled(false),
+ channel_id_enabled(false),
+ grease_enabled(false),
+ allow_unknown_alpn_protos(false),
+ ed25519_enabled(false),
+ rsa_pss_rsae_certs_enabled(true),
+ false_start_allowed_without_alpn(false),
+ handoff(false),
+ enable_early_data(false) {
+ CRYPTO_MUTEX_init(&lock);
+ CRYPTO_new_ex_data(&ex_data);
+}
+ssl_ctx_st::~ssl_ctx_st() {
+ // Free the internal session cache. Note that this calls the caller-supplied
+ // remove callback, so we must do it before clearing ex_data. (See ticket
+ // [openssl.org #212].)
+ SSL_CTX_flush_sessions(this, 0);
+
+ CRYPTO_free_ex_data(&g_ex_data_class_ssl_ctx, this, &ex_data);
+
+ CRYPTO_MUTEX_cleanup(&lock);
+ lh_SSL_SESSION_free(sessions);
+ x509_method->ssl_ctx_free(this);
+}
+
+SSL_CTX *SSL_CTX_new(const SSL_METHOD *method) {
if (method == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NULL_SSL_METHOD_PASSED);
- return NULL;
+ return nullptr;
}
- ret = (SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX));
- if (ret == NULL) {
- goto err;
+ UniquePtr<SSL_CTX> ret = MakeUnique<SSL_CTX>(method);
+ if (!ret) {
+ return nullptr;
}
- OPENSSL_memset(ret, 0, sizeof(SSL_CTX));
-
- ret->method = method->method;
- ret->x509_method = method->x509_method;
-
- CRYPTO_MUTEX_init(&ret->lock);
-
- ret->session_cache_mode = SSL_SESS_CACHE_SERVER;
- ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;
-
- ret->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
- ret->session_psk_dhe_timeout = SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT;
-
- ret->references = 1;
-
- ret->max_cert_list = SSL_MAX_CERT_LIST_DEFAULT;
- ret->verify_mode = SSL_VERIFY_NONE;
- ret->cert = New<CERT>(method->x509_method);
- if (ret->cert == NULL) {
- goto err;
- }
-
+ ret->cert = MakeUnique<CERT>(method->x509_method);
ret->sessions = lh_SSL_SESSION_new(ssl_session_hash, ssl_session_cmp);
- if (ret->sessions == NULL) {
- goto err;
+ ret->client_CA.reset(sk_CRYPTO_BUFFER_new_null());
+ if (ret->cert == nullptr ||
+ ret->sessions == nullptr ||
+ ret->client_CA == nullptr ||
+ !ret->x509_method->ssl_ctx_new(ret.get())) {
+ return nullptr;
}
- if (!ret->x509_method->ssl_ctx_new(ret)) {
- goto err;
- }
-
- if (!SSL_CTX_set_strict_cipher_list(ret, SSL_DEFAULT_CIPHER_LIST)) {
- goto err2;
- }
-
- ret->client_CA = sk_CRYPTO_BUFFER_new_null();
- if (ret->client_CA == NULL) {
- goto err;
- }
-
- CRYPTO_new_ex_data(&ret->ex_data);
-
- ret->max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH;
-
- // Disable the auto-chaining feature by default. Once this has stuck without
- // problems, the feature will be removed entirely.
- ret->mode = SSL_MODE_NO_AUTO_CHAIN;
-
- ret->rsa_pss_rsae_certs_enabled = true;
-
- // Lock the SSL_CTX to the specified version, for compatibility with legacy
- // uses of SSL_METHOD.
- if (!SSL_CTX_set_max_proto_version(ret, method->version) ||
- !SSL_CTX_set_min_proto_version(ret, method->version)) {
+ if (!SSL_CTX_set_strict_cipher_list(ret.get(), SSL_DEFAULT_CIPHER_LIST) ||
+ // Lock the SSL_CTX to the specified version, for compatibility with
+ // legacy uses of SSL_METHOD.
+ !SSL_CTX_set_max_proto_version(ret.get(), method->version) ||
+ !SSL_CTX_set_min_proto_version(ret.get(), method->version)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- goto err2;
+ return nullptr;
}
- return ret;
-
-err:
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-err2:
- SSL_CTX_free(ret);
- return NULL;
+ return ret.release();
}
int SSL_CTX_up_ref(SSL_CTX *ctx) {
@@ -600,184 +609,118 @@
return;
}
- // Free internal session cache. However: the remove_cb() may reference the
- // ex_data of SSL_CTX, thus the ex_data store can only be removed after the
- // sessions were flushed. As the ex_data handling routines might also touch
- // the session cache, the most secure solution seems to be: empty (flush) the
- // cache, then free ex_data, then finally free the cache. (See ticket
- // [openssl.org #212].)
- SSL_CTX_flush_sessions(ctx, 0);
-
- CRYPTO_free_ex_data(&g_ex_data_class_ssl_ctx, ctx, &ctx->ex_data);
-
- CRYPTO_MUTEX_cleanup(&ctx->lock);
- lh_SSL_SESSION_free(ctx->sessions);
- Delete(ctx->cipher_list);
- Delete(ctx->cert);
- sk_SSL_CUSTOM_EXTENSION_pop_free(ctx->client_custom_extensions,
- SSL_CUSTOM_EXTENSION_free);
- sk_SSL_CUSTOM_EXTENSION_pop_free(ctx->server_custom_extensions,
- SSL_CUSTOM_EXTENSION_free);
- sk_CRYPTO_BUFFER_pop_free(ctx->client_CA, CRYPTO_BUFFER_free);
- ctx->x509_method->ssl_ctx_free(ctx);
- sk_SRTP_PROTECTION_PROFILE_free(ctx->srtp_profiles);
- OPENSSL_free(ctx->psk_identity_hint);
- OPENSSL_free(ctx->supported_group_list);
- OPENSSL_free(ctx->alpn_client_proto_list);
- EVP_PKEY_free(ctx->tlsext_channel_id_private);
- OPENSSL_free(ctx->verify_sigalgs);
- OPENSSL_free(ctx->tlsext_ticket_key_current);
- OPENSSL_free(ctx->tlsext_ticket_key_prev);
-
+ ctx->~ssl_ctx_st();
OPENSSL_free(ctx);
}
+ssl_st::ssl_st(SSL_CTX *ctx_arg)
+ : method(ctx_arg->method),
+ max_send_fragment(ctx_arg->max_send_fragment),
+ msg_callback(ctx_arg->msg_callback),
+ msg_callback_arg(ctx_arg->msg_callback_arg),
+ tls13_variant(ctx_arg->tls13_variant),
+ ctx(UpRef(ctx_arg)),
+ session_ctx(UpRef(ctx_arg)),
+ options(ctx->options),
+ mode(ctx->mode),
+ max_cert_list(ctx->max_cert_list),
+ server(false),
+ quiet_shutdown(ctx->quiet_shutdown),
+ did_dummy_pq_padding(false),
+ enable_early_data(ctx->enable_early_data) {
+ CRYPTO_new_ex_data(&ex_data);
+}
+
+ssl_st::~ssl_st() {
+ CRYPTO_free_ex_data(&g_ex_data_class_ssl, this, &ex_data);
+ // |config| refers to |this|, so we must release it earlier.
+ config.reset();
+ if (method != NULL) {
+ method->ssl_free(this);
+ }
+}
+
SSL *SSL_new(SSL_CTX *ctx) {
- if (ctx == NULL) {
+ if (ctx == nullptr) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NULL_SSL_CTX);
- return NULL;
- }
- if (ctx->method == NULL) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION);
- return NULL;
+ return nullptr;
}
- SSL *ssl = (SSL *)OPENSSL_malloc(sizeof(SSL));
- if (ssl == NULL) {
- goto err;
- }
- OPENSSL_memset(ssl, 0, sizeof(SSL));
-
- ssl->conf_min_version = ctx->conf_min_version;
- ssl->conf_max_version = ctx->conf_max_version;
- ssl->tls13_variant = ctx->tls13_variant;
-
- // RFC 6347 states that implementations SHOULD use an initial timer value of
- // 1 second.
- ssl->initial_timeout_duration_ms = 1000;
-
- ssl->options = ctx->options;
- ssl->mode = ctx->mode;
- ssl->max_cert_list = ctx->max_cert_list;
-
- ssl->cert = ssl_cert_dup(ctx->cert).release();
- if (ssl->cert == NULL) {
- goto err;
+ UniquePtr<SSL> ssl = MakeUnique<SSL>(ctx);
+ if (ssl == nullptr) {
+ return nullptr;
}
- ssl->msg_callback = ctx->msg_callback;
- ssl->msg_callback_arg = ctx->msg_callback_arg;
- ssl->verify_mode = ctx->verify_mode;
- ssl->verify_callback = ctx->default_verify_callback;
- ssl->custom_verify_callback = ctx->custom_verify_callback;
- ssl->retain_only_sha256_of_client_certs =
+ ssl->config = MakeUnique<SSL_CONFIG>(ssl.get());
+ if (ssl->config == nullptr) {
+ return nullptr;
+ }
+ ssl->config->conf_min_version = ctx->conf_min_version;
+ ssl->config->conf_max_version = ctx->conf_max_version;
+
+ ssl->config->cert = ssl_cert_dup(ctx->cert.get());
+ if (ssl->config->cert == nullptr) {
+ return nullptr;
+ }
+
+ ssl->config->verify_mode = ctx->verify_mode;
+ ssl->config->verify_callback = ctx->default_verify_callback;
+ ssl->config->custom_verify_callback = ctx->custom_verify_callback;
+ ssl->config->retain_only_sha256_of_client_certs =
ctx->retain_only_sha256_of_client_certs;
- ssl->quiet_shutdown = ctx->quiet_shutdown;
- ssl->max_send_fragment = ctx->max_send_fragment;
-
- SSL_CTX_up_ref(ctx);
- ssl->ctx = ctx;
- SSL_CTX_up_ref(ctx);
- ssl->session_ctx = ctx;
-
- if (!ssl->ctx->x509_method->ssl_new(ssl)) {
- goto err;
+ if (!ssl->config->supported_group_list.CopyFrom(ctx->supported_group_list) ||
+ !ssl->config->alpn_client_proto_list.CopyFrom(
+ ctx->alpn_client_proto_list) ||
+ !ssl->config->verify_sigalgs.CopyFrom(ctx->verify_sigalgs)) {
+ return nullptr;
}
- if (ctx->supported_group_list) {
- ssl->supported_group_list = (uint16_t *)BUF_memdup(
- ctx->supported_group_list, ctx->supported_group_list_len * 2);
- if (!ssl->supported_group_list) {
- goto err;
- }
- ssl->supported_group_list_len = ctx->supported_group_list_len;
- }
-
- if (ctx->alpn_client_proto_list) {
- ssl->alpn_client_proto_list = (uint8_t *)BUF_memdup(
- ctx->alpn_client_proto_list, ctx->alpn_client_proto_list_len);
- if (ssl->alpn_client_proto_list == NULL) {
- goto err;
- }
- ssl->alpn_client_proto_list_len = ctx->alpn_client_proto_list_len;
- }
-
- ssl->method = ctx->method;
-
- if (!ssl->method->ssl_new(ssl)) {
- goto err;
- }
-
- CRYPTO_new_ex_data(&ssl->ex_data);
-
- ssl->psk_identity_hint = NULL;
if (ctx->psk_identity_hint) {
- ssl->psk_identity_hint = BUF_strdup(ctx->psk_identity_hint);
- if (ssl->psk_identity_hint == NULL) {
- goto err;
+ ssl->config->psk_identity_hint.reset(
+ BUF_strdup(ctx->psk_identity_hint.get()));
+ if (ssl->config->psk_identity_hint == nullptr) {
+ return nullptr;
}
}
- ssl->psk_client_callback = ctx->psk_client_callback;
- ssl->psk_server_callback = ctx->psk_server_callback;
+ ssl->config->psk_client_callback = ctx->psk_client_callback;
+ ssl->config->psk_server_callback = ctx->psk_server_callback;
- ssl->tlsext_channel_id_enabled = ctx->tlsext_channel_id_enabled;
- if (ctx->tlsext_channel_id_private) {
- EVP_PKEY_up_ref(ctx->tlsext_channel_id_private);
- ssl->tlsext_channel_id_private = ctx->tlsext_channel_id_private;
+ ssl->config->channel_id_enabled = ctx->channel_id_enabled;
+ ssl->config->channel_id_private = UpRef(ctx->channel_id_private);
+
+ ssl->config->signed_cert_timestamps_enabled =
+ ctx->signed_cert_timestamps_enabled;
+ ssl->config->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
+ ssl->config->handoff = ctx->handoff;
+
+ if (!ssl->method->ssl_new(ssl.get()) ||
+ !ssl->ctx->x509_method->ssl_new(ssl->s3->hs.get())) {
+ return nullptr;
}
- ssl->signed_cert_timestamps_enabled = ctx->signed_cert_timestamps_enabled;
- ssl->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
- ssl->handoff = ctx->handoff;
+ return ssl.release();
+}
- return ssl;
+SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg)
+ : ssl(ssl_arg),
+ signed_cert_timestamps_enabled(false),
+ ocsp_stapling_enabled(false),
+ channel_id_enabled(false),
+ retain_only_sha256_of_client_certs(false),
+ handoff(false),
+ shed_handshake_config(false) {
+ assert(ssl);
+}
-err:
- SSL_free(ssl);
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-
- return NULL;
+SSL_CONFIG::~SSL_CONFIG() {
+ if (ssl->ctx != nullptr) {
+ ssl->ctx->x509_method->ssl_config_free(this);
+ }
}
void SSL_free(SSL *ssl) {
- if (ssl == NULL) {
- return;
- }
-
- if (ssl->ctx != NULL) {
- ssl->ctx->x509_method->ssl_free(ssl);
- }
-
- CRYPTO_free_ex_data(&g_ex_data_class_ssl, ssl, &ssl->ex_data);
-
- BIO_free_all(ssl->rbio);
- BIO_free_all(ssl->wbio);
-
- // add extra stuff
- Delete(ssl->cipher_list);
-
- SSL_SESSION_free(ssl->session);
-
- Delete(ssl->cert);
-
- OPENSSL_free(ssl->tlsext_hostname);
- SSL_CTX_free(ssl->session_ctx);
- OPENSSL_free(ssl->supported_group_list);
- OPENSSL_free(ssl->alpn_client_proto_list);
- OPENSSL_free(ssl->token_binding_params);
- OPENSSL_free(ssl->quic_transport_params);
- EVP_PKEY_free(ssl->tlsext_channel_id_private);
- OPENSSL_free(ssl->psk_identity_hint);
- sk_CRYPTO_BUFFER_pop_free(ssl->client_CA, CRYPTO_BUFFER_free);
- sk_SRTP_PROTECTION_PROFILE_free(ssl->srtp_profiles);
-
- if (ssl->method != NULL) {
- ssl->method->ssl_free(ssl);
- }
- SSL_CTX_free(ssl->ctx);
-
- OPENSSL_free(ssl);
+ Delete(ssl);
}
void SSL_set_connect_state(SSL *ssl) {
@@ -791,13 +734,11 @@
}
void SSL_set0_rbio(SSL *ssl, BIO *rbio) {
- BIO_free_all(ssl->rbio);
- ssl->rbio = rbio;
+ ssl->rbio.reset(rbio);
}
void SSL_set0_wbio(SSL *ssl, BIO *wbio) {
- BIO_free_all(ssl->wbio);
- ssl->wbio = wbio;
+ ssl->wbio.reset(wbio);
}
void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) {
@@ -834,9 +775,9 @@
SSL_set0_wbio(ssl, wbio);
}
-BIO *SSL_get_rbio(const SSL *ssl) { return ssl->rbio; }
+BIO *SSL_get_rbio(const SSL *ssl) { return ssl->rbio.get(); }
-BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio; }
+BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio.get(); }
int SSL_do_handshake(SSL *ssl) {
ssl_reset_error_state(ssl);
@@ -864,6 +805,7 @@
// Destroy the handshake object if the handshake has completely finished.
if (!early_return) {
ssl->s3->hs.reset();
+ ssl_maybe_shed_handshake_config(ssl);
}
return 1;
@@ -892,11 +834,12 @@
return tls13_post_handshake(ssl, msg);
}
- // We do not accept renegotiations as a server or SSL 3.0. SSL 3.0 will be
- // removed entirely in the future and requires retaining more data for
- // renegotiation_info.
- if (ssl->server || ssl->version == SSL3_VERSION) {
- goto no_renegotiation;
+ // Check for renegotiation on the server before parsing to use the correct
+ // error. Renegotiation is triggered by a different message for servers.
+ if (ssl->server) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
+ return 0;
}
if (msg.type != SSL3_MT_HELLO_REQUEST || CBS_len(&msg.body) != 0) {
@@ -905,31 +848,20 @@
return 0;
}
- switch (ssl->renegotiate_mode) {
- case ssl_renegotiate_ignore:
- // Ignore the HelloRequest.
- return 1;
-
- case ssl_renegotiate_once:
- if (ssl->s3->total_renegotiations != 0) {
- goto no_renegotiation;
- }
- break;
-
- case ssl_renegotiate_never:
- goto no_renegotiation;
-
- case ssl_renegotiate_freely:
- break;
+ if (ssl->renegotiate_mode == ssl_renegotiate_ignore) {
+ return 1; // Ignore the HelloRequest.
}
- // Renegotiation is only supported at quiescent points in the application
- // protocol, namely in HTTPS, just before reading the HTTP response. Require
- // the record-layer be idle and avoid complexities of sending a handshake
- // record while an application_data record is being written.
- if (!ssl->s3->write_buffer.empty() ||
+ if (!ssl_can_renegotiate(ssl) ||
+ // Renegotiation is only supported at quiescent points in the application
+ // protocol, namely in HTTPS, just before reading the HTTP response.
+ // Require the record-layer be idle and avoid complexities of sending a
+ // handshake record while an application_data record is being written.
+ !ssl->s3->write_buffer.empty() ||
ssl->s3->write_shutdown != ssl_shutdown_none) {
- goto no_renegotiation;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
+ return 0;
}
// Begin a new handshake.
@@ -944,11 +876,6 @@
ssl->s3->total_renegotiations++;
return 1;
-
-no_renegotiation:
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION);
- return 0;
}
static int ssl_read_impl(SSL *ssl) {
@@ -1127,13 +1054,11 @@
}
ssl->s3->read_shutdown = ssl_shutdown_close_notify;
} else {
- // Keep discarding data until we see a close_notify.
- for (;;) {
- ssl->s3->pending_app_data = Span<uint8_t>();
- int ret = ssl_read_impl(ssl);
- if (ret <= 0) {
- break;
- }
+ // Process records until an error, close_notify, or application data.
+ if (ssl_read_impl(ssl) > 0) {
+ // We received some unexpected application data.
+ OPENSSL_PUT_ERROR(SSL, SSL_R_APPLICATION_DATA_ON_SHUTDOWN);
+ return -1;
}
if (ssl->s3->read_shutdown != ssl_shutdown_close_notify) {
return -1;
@@ -1161,12 +1086,8 @@
int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
size_t params_len) {
- ssl->quic_transport_params = (uint8_t *)BUF_memdup(params, params_len);
- if (!ssl->quic_transport_params) {
- return 0;
- }
- ssl->quic_transport_params_len = params_len;
- return 1;
+ return ssl->config && ssl->config->quic_transport_params.CopyFrom(
+ MakeConstSpan(params, params_len));
}
void SSL_get_peer_quic_transport_params(const SSL *ssl,
@@ -1177,7 +1098,7 @@
}
void SSL_CTX_set_early_data_enabled(SSL_CTX *ctx, int enabled) {
- ctx->cert->enable_early_data = !!enabled;
+ ctx->enable_early_data = !!enabled;
}
void SSL_CTX_set_tls13_variant(SSL_CTX *ctx, enum tls13_variant_t variant) {
@@ -1189,7 +1110,7 @@
}
void SSL_set_early_data_enabled(SSL *ssl, int enabled) {
- ssl->cert->enable_early_data = !!enabled;
+ ssl->enable_early_data = !!enabled;
}
int SSL_in_early_data(const SSL *ssl) {
@@ -1386,9 +1307,8 @@
*out_len = 0;
OPENSSL_memset(out, 0, max_out);
- // tls-unique is not defined for SSL 3.0 or TLS 1.3.
+ // tls-unique is not defined for TLS 1.3.
if (!ssl->s3->initial_handshake_complete ||
- ssl_protocol_version(ssl) < TLS1_VERSION ||
ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
return 0;
}
@@ -1431,20 +1351,33 @@
int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const uint8_t *sid_ctx,
size_t sid_ctx_len) {
- return set_session_id_context(ctx->cert, sid_ctx, sid_ctx_len);
+ return set_session_id_context(ctx->cert.get(), sid_ctx, sid_ctx_len);
}
int SSL_set_session_id_context(SSL *ssl, const uint8_t *sid_ctx,
size_t sid_ctx_len) {
- return set_session_id_context(ssl->cert, sid_ctx, sid_ctx_len);
+ if (!ssl->config) {
+ return 0;
+ }
+ return set_session_id_context(ssl->config->cert.get(), sid_ctx, sid_ctx_len);
}
const uint8_t *SSL_get0_session_id_context(const SSL *ssl, size_t *out_len) {
- *out_len = ssl->cert->sid_ctx_length;
- return ssl->cert->sid_ctx;
+ if (!ssl->config) {
+ assert(ssl->config);
+ *out_len = 0;
+ return NULL;
+ }
+ *out_len = ssl->config->cert->sid_ctx_length;
+ return ssl->config->cert->sid_ctx;
}
-void SSL_certs_clear(SSL *ssl) { ssl_cert_clear_certs(ssl->cert); }
+void SSL_certs_clear(SSL *ssl) {
+ if (!ssl->config) {
+ return;
+ }
+ ssl_cert_clear_certs(ssl->config->cert.get());
+}
int SSL_get_fd(const SSL *ssl) { return SSL_get_rfd(ssl); }
@@ -1527,7 +1460,6 @@
size_t SSL_get_finished(const SSL *ssl, void *buf, size_t count) {
if (!ssl->s3->initial_handshake_complete ||
- ssl_protocol_version(ssl) < TLS1_VERSION ||
ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
return 0;
}
@@ -1543,7 +1475,6 @@
size_t SSL_get_peer_finished(const SSL *ssl, void *buf, size_t count) {
if (!ssl->s3->initial_handshake_complete ||
- ssl_protocol_version(ssl) < TLS1_VERSION ||
ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
return 0;
}
@@ -1557,7 +1488,13 @@
ssl->s3->previous_server_finished_len);
}
-int SSL_get_verify_mode(const SSL *ssl) { return ssl->verify_mode; }
+int SSL_get_verify_mode(const SSL *ssl) {
+ if (!ssl->config) {
+ assert(ssl->config);
+ return -1;
+ }
+ return ssl->config->verify_mode;
+}
int SSL_get_extms_support(const SSL *ssl) {
// TLS 1.3 does not require extended master secret and always reports as
@@ -1586,22 +1523,25 @@
int SSL_get_read_ahead(const SSL *ssl) { return 0; }
-void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { }
+int SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { return 1; }
-void SSL_set_read_ahead(SSL *ssl, int yes) { }
+int SSL_set_read_ahead(SSL *ssl, int yes) { return 1; }
int SSL_pending(const SSL *ssl) {
return static_cast<int>(ssl->s3->pending_app_data.size());
}
-// Fix this so it checks all the valid key/cert options
int SSL_CTX_check_private_key(const SSL_CTX *ctx) {
- return ssl_cert_check_private_key(ctx->cert, ctx->cert->privatekey.get());
+ return ssl_cert_check_private_key(ctx->cert.get(),
+ ctx->cert->privatekey.get());
}
-// Fix this function so that it takes an optional type parameter
int SSL_check_private_key(const SSL *ssl) {
- return ssl_cert_check_private_key(ssl->cert, ssl->cert->privatekey.get());
+ if (!ssl->config) {
+ return 0;
+ }
+ return ssl_cert_check_private_key(ssl->config->cert.get(),
+ ssl->config->cert->privatekey.get());
}
long SSL_get_default_timeout(const SSL *ssl) {
@@ -1727,9 +1667,9 @@
uint8_t *out_bytes = reinterpret_cast<uint8_t *>(out);
MutexReadLock lock(&ctx->lock);
- OPENSSL_memcpy(out_bytes, ctx->tlsext_ticket_key_current->name, 16);
- OPENSSL_memcpy(out_bytes + 16, ctx->tlsext_ticket_key_current->hmac_key, 16);
- OPENSSL_memcpy(out_bytes + 32, ctx->tlsext_ticket_key_current->aes_key, 16);
+ OPENSSL_memcpy(out_bytes, ctx->ticket_key_current->name, 16);
+ OPENSSL_memcpy(out_bytes + 16, ctx->ticket_key_current->hmac_key, 16);
+ OPENSSL_memcpy(out_bytes + 32, ctx->ticket_key_current->aes_key, 16);
return 1;
}
@@ -1741,22 +1681,19 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH);
return 0;
}
- if (!ctx->tlsext_ticket_key_current) {
- ctx->tlsext_ticket_key_current =
- (tlsext_ticket_key *)OPENSSL_malloc(sizeof(tlsext_ticket_key));
- if (!ctx->tlsext_ticket_key_current) {
- return 0;
- }
+ auto key = MakeUnique<TicketKey>();
+ if (!key) {
+ return 0;
}
- OPENSSL_memset(ctx->tlsext_ticket_key_current, 0, sizeof(tlsext_ticket_key));
const uint8_t *in_bytes = reinterpret_cast<const uint8_t *>(in);
- OPENSSL_memcpy(ctx->tlsext_ticket_key_current->name, in_bytes, 16);
- OPENSSL_memcpy(ctx->tlsext_ticket_key_current->hmac_key, in_bytes + 16, 16);
- OPENSSL_memcpy(ctx->tlsext_ticket_key_current->aes_key, in_bytes + 32, 16);
- OPENSSL_free(ctx->tlsext_ticket_key_prev);
- ctx->tlsext_ticket_key_prev = nullptr;
- // Disable automatic key rotation.
- ctx->tlsext_ticket_key_current->next_rotation_tv_sec = 0;
+ OPENSSL_memcpy(key->name, in_bytes, 16);
+ OPENSSL_memcpy(key->hmac_key, in_bytes + 16, 16);
+ OPENSSL_memcpy(key->aes_key, in_bytes + 32, 16);
+ // Disable automatic key rotation for manually-configured keys. This is now
+ // the caller's responsibility.
+ key->next_rotation_tv_sec = 0;
+ ctx->ticket_key_current = std::move(key);
+ ctx->ticket_key_prev.reset();
return 1;
}
@@ -1764,30 +1701,32 @@
SSL_CTX *ctx, int (*callback)(SSL *ssl, uint8_t *key_name, uint8_t *iv,
EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
int encrypt)) {
- ctx->tlsext_ticket_key_cb = callback;
+ ctx->ticket_key_cb = callback;
return 1;
}
int SSL_CTX_set1_curves(SSL_CTX *ctx, const int *curves, size_t curves_len) {
return tls1_set_curves(&ctx->supported_group_list,
- &ctx->supported_group_list_len, curves,
- curves_len);
+ MakeConstSpan(curves, curves_len));
}
int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) {
- return tls1_set_curves(&ssl->supported_group_list,
- &ssl->supported_group_list_len, curves,
- curves_len);
+ if (!ssl->config) {
+ return 0;
+ }
+ return tls1_set_curves(&ssl->config->supported_group_list,
+ MakeConstSpan(curves, curves_len));
}
int SSL_CTX_set1_curves_list(SSL_CTX *ctx, const char *curves) {
- return tls1_set_curves_list(&ctx->supported_group_list,
- &ctx->supported_group_list_len, curves);
+ return tls1_set_curves_list(&ctx->supported_group_list, curves);
}
int SSL_set1_curves_list(SSL *ssl, const char *curves) {
- return tls1_set_curves_list(&ssl->supported_group_list,
- &ssl->supported_group_list_len, curves);
+ if (!ssl->config) {
+ return 0;
+ }
+ return tls1_set_curves_list(&ssl->config->supported_group_list, curves);
}
uint16_t SSL_get_curve_id(const SSL *ssl) {
@@ -1824,9 +1763,13 @@
if (ssl == NULL) {
return NULL;
}
+ if (ssl->config == NULL) {
+ assert(ssl->config);
+ return NULL;
+ }
- const SSLCipherPreferenceList *prefs = ssl_get_cipher_preferences(ssl);
- return prefs == nullptr ? nullptr : prefs->ciphers.get();
+ return ssl->config->cipher_list ? ssl->config->cipher_list->ciphers.get()
+ : ssl->ctx->cipher_list->ciphers.get();
}
const char *SSL_get_cipher_list(const SSL *ssl, int n) {
@@ -1856,11 +1799,19 @@
}
int SSL_set_cipher_list(SSL *ssl, const char *str) {
- return ssl_create_cipher_list(&ssl->cipher_list, str, false /* not strict */);
+ if (!ssl->config) {
+ return 0;
+ }
+ return ssl_create_cipher_list(&ssl->config->cipher_list, str,
+ false /* not strict */);
}
int SSL_set_strict_cipher_list(SSL *ssl, const char *str) {
- return ssl_create_cipher_list(&ssl->cipher_list, str, true /* strict */);
+ if (!ssl->config) {
+ return 0;
+ }
+ return ssl_create_cipher_list(&ssl->config->cipher_list, str,
+ true /* strict */);
}
const char *SSL_get_servername(const SSL *ssl, const int type) {
@@ -1870,8 +1821,8 @@
// Historically, |SSL_get_servername| was also the configuration getter
// corresponding to |SSL_set_tlsext_host_name|.
- if (ssl->tlsext_hostname != NULL) {
- return ssl->tlsext_hostname;
+ if (ssl->hostname != nullptr) {
+ return ssl->hostname.get();
}
return ssl->s3->hostname.get();
@@ -1894,8 +1845,11 @@
void SSL_set_custom_verify(
SSL *ssl, int mode,
enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)) {
- ssl->verify_mode = mode;
- ssl->custom_verify_callback = callback;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->verify_mode = mode;
+ ssl->config->custom_verify_callback = callback;
}
void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) {
@@ -1903,7 +1857,10 @@
}
void SSL_enable_signed_cert_timestamps(SSL *ssl) {
- ssl->signed_cert_timestamps_enabled = true;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->signed_cert_timestamps_enabled = true;
}
void SSL_CTX_enable_ocsp_stapling(SSL_CTX *ctx) {
@@ -1911,7 +1868,10 @@
}
void SSL_enable_ocsp_stapling(SSL *ssl) {
- ssl->ocsp_stapling_enabled = true;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->ocsp_stapling_enabled = true;
}
void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, const uint8_t **out,
@@ -1923,8 +1883,8 @@
return;
}
- *out = CRYPTO_BUFFER_data(session->signed_cert_timestamp_list);
- *out_len = CRYPTO_BUFFER_len(session->signed_cert_timestamp_list);
+ *out = CRYPTO_BUFFER_data(session->signed_cert_timestamp_list.get());
+ *out_len = CRYPTO_BUFFER_len(session->signed_cert_timestamp_list.get());
}
void SSL_get0_ocsp_response(const SSL *ssl, const uint8_t **out,
@@ -1936,15 +1896,13 @@
return;
}
- *out = CRYPTO_BUFFER_data(session->ocsp_response);
- *out_len = CRYPTO_BUFFER_len(session->ocsp_response);
+ *out = CRYPTO_BUFFER_data(session->ocsp_response.get());
+ *out_len = CRYPTO_BUFFER_len(session->ocsp_response.get());
}
int SSL_set_tlsext_host_name(SSL *ssl, const char *name) {
- OPENSSL_free(ssl->tlsext_hostname);
- ssl->tlsext_hostname = NULL;
-
- if (name == NULL) {
+ ssl->hostname.reset();
+ if (name == nullptr) {
return 1;
}
@@ -1953,8 +1911,8 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME);
return 0;
}
- ssl->tlsext_hostname = BUF_strdup(name);
- if (ssl->tlsext_hostname == NULL) {
+ ssl->hostname.reset(BUF_strdup(name));
+ if (ssl->hostname == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
@@ -1963,12 +1921,12 @@
int SSL_CTX_set_tlsext_servername_callback(
SSL_CTX *ctx, int (*callback)(SSL *ssl, int *out_alert, void *arg)) {
- ctx->tlsext_servername_callback = callback;
+ ctx->servername_callback = callback;
return 1;
}
int SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg) {
- ctx->tlsext_servername_arg = arg;
+ ctx->servername_arg = arg;
return 1;
}
@@ -2029,25 +1987,21 @@
int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const uint8_t *protos,
unsigned protos_len) {
- OPENSSL_free(ctx->alpn_client_proto_list);
- ctx->alpn_client_proto_list = (uint8_t *)BUF_memdup(protos, protos_len);
- if (!ctx->alpn_client_proto_list) {
- return 1;
- }
- ctx->alpn_client_proto_list_len = protos_len;
-
- return 0;
+ // Note this function's calling convention is backwards.
+ return ctx->alpn_client_proto_list.CopyFrom(MakeConstSpan(protos, protos_len))
+ ? 0
+ : 1;
}
int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, unsigned protos_len) {
- OPENSSL_free(ssl->alpn_client_proto_list);
- ssl->alpn_client_proto_list = (uint8_t *)BUF_memdup(protos, protos_len);
- if (!ssl->alpn_client_proto_list) {
+ // Note this function's calling convention is backwards.
+ if (!ssl->config) {
return 1;
}
- ssl->alpn_client_proto_list_len = protos_len;
-
- return 0;
+ return ssl->config->alpn_client_proto_list.CopyFrom(
+ MakeConstSpan(protos, protos_len))
+ ? 0
+ : 1;
}
void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
@@ -2062,8 +2016,8 @@
void SSL_get0_alpn_selected(const SSL *ssl, const uint8_t **out_data,
unsigned *out_len) {
if (SSL_in_early_data(ssl) && !ssl->server) {
- *out_data = ssl->s3->hs->early_session->early_alpn;
- *out_len = ssl->s3->hs->early_session->early_alpn_len;
+ *out_data = ssl->s3->hs->early_session->early_alpn.data();
+ *out_len = ssl->s3->hs->early_session->early_alpn.size();
} else {
*out_data = ssl->s3->alpn_selected.data();
*out_len = ssl->s3->alpn_selected.size();
@@ -2074,8 +2028,45 @@
ctx->allow_unknown_alpn_protos = !!enabled;
}
+int SSL_CTX_add_cert_compression_alg(SSL_CTX *ctx, uint16_t alg_id,
+ ssl_cert_compression_func_t compress,
+ ssl_cert_decompression_func_t decompress) {
+ assert(compress != nullptr || decompress != nullptr);
+
+ for (const auto *alg : ctx->cert_compression_algs.get()) {
+ if (alg->alg_id == alg_id) {
+ return 0;
+ }
+ }
+
+ UniquePtr<CertCompressionAlg> alg = MakeUnique<CertCompressionAlg>();
+ if (alg == nullptr) {
+ return 0;
+ }
+
+ alg->alg_id = alg_id;
+ alg->compress = compress;
+ alg->decompress = decompress;
+
+ if (ctx->cert_compression_algs == nullptr) {
+ ctx->cert_compression_algs.reset(sk_CertCompressionAlg_new_null());
+ if (ctx->cert_compression_algs == nullptr) {
+ return 0;
+ }
+ }
+
+ if (!PushToStack(ctx->cert_compression_algs.get(), std::move(alg))) {
+ if (sk_CertCompressionAlg_num(ctx->cert_compression_algs.get()) == 0) {
+ ctx->cert_compression_algs.reset();
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
void SSL_CTX_set_tls_channel_id_enabled(SSL_CTX *ctx, int enabled) {
- ctx->tlsext_channel_id_enabled = !!enabled;
+ ctx->channel_id_enabled = !!enabled;
}
int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx) {
@@ -2084,7 +2075,10 @@
}
void SSL_set_tls_channel_id_enabled(SSL *ssl, int enabled) {
- ssl->tlsext_channel_id_enabled = !!enabled;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->channel_id_enabled = !!enabled;
}
int SSL_enable_tls_channel_id(SSL *ssl) {
@@ -2105,49 +2099,44 @@
return 0;
}
- EVP_PKEY_free(ctx->tlsext_channel_id_private);
- EVP_PKEY_up_ref(private_key);
- ctx->tlsext_channel_id_private = private_key;
- ctx->tlsext_channel_id_enabled = true;
+ ctx->channel_id_private = UpRef(private_key);
+ ctx->channel_id_enabled = true;
return 1;
}
int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) {
+ if (!ssl->config) {
+ return 0;
+ }
if (!is_p256_key(private_key)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256);
return 0;
}
- EVP_PKEY_free(ssl->tlsext_channel_id_private);
- EVP_PKEY_up_ref(private_key);
- ssl->tlsext_channel_id_private = private_key;
- ssl->tlsext_channel_id_enabled = true;
+ ssl->config->channel_id_private = UpRef(private_key);
+ ssl->config->channel_id_enabled = true;
return 1;
}
size_t SSL_get_tls_channel_id(SSL *ssl, uint8_t *out, size_t max_out) {
- if (!ssl->s3->tlsext_channel_id_valid) {
+ if (!ssl->s3->channel_id_valid) {
return 0;
}
- OPENSSL_memcpy(out, ssl->s3->tlsext_channel_id,
- (max_out < 64) ? max_out : 64);
+ OPENSSL_memcpy(out, ssl->s3->channel_id, (max_out < 64) ? max_out : 64);
return 64;
}
int SSL_set_token_binding_params(SSL *ssl, const uint8_t *params, size_t len) {
+ if (!ssl->config) {
+ return 0;
+ }
if (len > 256) {
OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
return 0;
}
- OPENSSL_free(ssl->token_binding_params);
- ssl->token_binding_params = (uint8_t *)BUF_memdup(params, len);
- if (!ssl->token_binding_params) {
- return 0;
- }
- ssl->token_binding_params_len = len;
- return 1;
+ return ssl->config->token_binding_params.CopyFrom(MakeConstSpan(params, len));
}
int SSL_is_token_binding_negotiated(const SSL *ssl) {
@@ -2158,18 +2147,32 @@
return ssl->s3->negotiated_token_binding_param;
}
-size_t SSL_get0_certificate_types(SSL *ssl, const uint8_t **out_types) {
- if (ssl->server || ssl->s3->hs == NULL) {
- *out_types = NULL;
- return 0;
+size_t SSL_get0_certificate_types(const SSL *ssl, const uint8_t **out_types) {
+ Span<const uint8_t> types;
+ if (!ssl->server && ssl->s3->hs != nullptr) {
+ types = ssl->s3->hs->certificate_types;
}
- *out_types = ssl->s3->hs->certificate_types.data();
- return ssl->s3->hs->certificate_types.size();
+ *out_types = types.data();
+ return types.size();
+}
+
+size_t SSL_get0_peer_verify_algorithms(const SSL *ssl,
+ const uint16_t **out_sigalgs) {
+ Span<const uint16_t> sigalgs;
+ if (ssl->s3->hs != nullptr) {
+ sigalgs = ssl->s3->hs->peer_sigalgs;
+ }
+ *out_sigalgs = sigalgs.data();
+ return sigalgs.size();
}
EVP_PKEY *SSL_get_privatekey(const SSL *ssl) {
- if (ssl->cert != NULL) {
- return ssl->cert->privatekey.get();
+ if (!ssl->config) {
+ assert(ssl->config);
+ return NULL;
+ }
+ if (ssl->config->cert != NULL) {
+ return ssl->config->cert->privatekey.get();
}
return NULL;
@@ -2195,7 +2198,7 @@
const COMP_METHOD *SSL_get_current_expansion(SSL *ssl) { return NULL; }
-int *SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; }
+int SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; }
void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode) {
ctx->quiet_shutdown = (mode != 0);
@@ -2241,11 +2244,14 @@
return ret;
}
-SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) { return ssl->ctx; }
+SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) { return ssl->ctx.get(); }
SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx) {
- if (ssl->ctx == ctx) {
- return ssl->ctx;
+ if (!ssl->config) {
+ return NULL;
+ }
+ if (ssl->ctx.get() == ctx) {
+ return ssl->ctx.get();
}
// One cannot change the X.509 callbacks during a connection.
@@ -2255,17 +2261,19 @@
}
if (ctx == NULL) {
- ctx = ssl->session_ctx;
+ ctx = ssl->session_ctx.get();
}
- Delete(ssl->cert);
- ssl->cert = ssl_cert_dup(ctx->cert).release();
+ UniquePtr<CERT> new_cert = ssl_cert_dup(ctx->cert.get());
+ if (!new_cert) {
+ return nullptr;
+ }
- SSL_CTX_up_ref(ctx);
- SSL_CTX_free(ssl->ctx);
- ssl->ctx = ctx;
+ ssl->config->cert = std::move(new_cert);
+ ssl->ctx = UpRef(ctx);
+ ssl->enable_early_data = ssl->ctx->enable_early_data;
- return ssl->ctx;
+ return ssl->ctx.get();
}
void SSL_set_info_callback(SSL *ssl,
@@ -2345,23 +2353,23 @@
void SSL_set_tmp_dh_callback(SSL *ssl, DH *(*cb)(SSL *ssl, int is_export,
int keylength)) {}
-static int use_psk_identity_hint(char **out, const char *identity_hint) {
+static int use_psk_identity_hint(UniquePtr<char> *out,
+ const char *identity_hint) {
if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
return 0;
}
// Clear currently configured hint, if any.
- OPENSSL_free(*out);
- *out = NULL;
+ out->reset();
// Treat the empty hint as not supplying one. Plain PSK makes it possible to
// send either no hint (omit ServerKeyExchange) or an empty hint, while
// ECDHE_PSK can only spell empty hint. Having different capabilities is odd,
// so we interpret empty and missing as identical.
if (identity_hint != NULL && identity_hint[0] != '\0') {
- *out = BUF_strdup(identity_hint);
- if (*out == NULL) {
+ out->reset(BUF_strdup(identity_hint));
+ if (*out == nullptr) {
return 0;
}
}
@@ -2374,14 +2382,21 @@
}
int SSL_use_psk_identity_hint(SSL *ssl, const char *identity_hint) {
- return use_psk_identity_hint(&ssl->psk_identity_hint, identity_hint);
+ if (!ssl->config) {
+ return 0;
+ }
+ return use_psk_identity_hint(&ssl->config->psk_identity_hint, identity_hint);
}
const char *SSL_get_psk_identity_hint(const SSL *ssl) {
if (ssl == NULL) {
return NULL;
}
- return ssl->psk_identity_hint;
+ if (ssl->config == NULL) {
+ assert(ssl->config);
+ return NULL;
+ }
+ return ssl->config->psk_identity_hint.get();
}
const char *SSL_get_psk_identity(const SSL *ssl) {
@@ -2392,14 +2407,17 @@
if (session == NULL) {
return NULL;
}
- return session->psk_identity;
+ return session->psk_identity.get();
}
void SSL_set_psk_client_callback(
SSL *ssl, unsigned (*cb)(SSL *ssl, const char *hint, char *identity,
unsigned max_identity_len, uint8_t *psk,
unsigned max_psk_len)) {
- ssl->psk_client_callback = cb;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->psk_client_callback = cb;
}
void SSL_CTX_set_psk_client_callback(
@@ -2412,7 +2430,10 @@
void SSL_set_psk_server_callback(
SSL *ssl, unsigned (*cb)(SSL *ssl, const char *identity, uint8_t *psk,
unsigned max_psk_len)) {
- ssl->psk_server_callback = cb;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->psk_server_callback = cb;
}
void SSL_CTX_set_psk_server_callback(
@@ -2422,11 +2443,14 @@
}
int SSL_set_dummy_pq_padding_size(SSL *ssl, size_t num_bytes) {
+ if (!ssl->config) {
+ return 0;
+ }
if (num_bytes > 0xffff) {
return 0;
}
- ssl->dummy_pq_padding_len = num_bytes;
+ ssl->config->dummy_pq_padding_len = num_bytes;
return 1;
}
@@ -2523,6 +2547,11 @@
void SSL_set_renegotiate_mode(SSL *ssl, enum ssl_renegotiate_mode_t mode) {
ssl->renegotiate_mode = mode;
+
+ // Check if |ssl_can_renegotiate| has changed and the configuration may now be
+ // shed. HTTP clients may initially allow renegotiation for HTTP/1.1, and then
+ // disable after the handshake once the ALPN protocol is known to be HTTP/2.
+ ssl_maybe_shed_handshake_config(ssl);
}
int SSL_get_ivs(const SSL *ssl, const uint8_t **out_read_iv,
@@ -2605,7 +2634,10 @@
}
void SSL_set_retain_only_sha256_of_client_certs(SSL *ssl, int enabled) {
- ssl->retain_only_sha256_of_client_certs = !!enabled;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->retain_only_sha256_of_client_certs = !!enabled;
}
void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx, int enabled) {
@@ -2626,14 +2658,24 @@
int SSL_is_draft_downgrade(const SSL *ssl) { return ssl->s3->draft_downgrade; }
+void SSL_set_shed_handshake_config(SSL *ssl, int enable) {
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->shed_handshake_config = !!enable;
+}
+
int SSL_clear(SSL *ssl) {
+ if (!ssl->config) {
+ return 0; // SSL_clear may not be used after shedding config.
+ }
+
// In OpenSSL, reusing a client |SSL| with |SSL_clear| causes the previously
// established session to be offered the next time around. wpa_supplicant
// depends on this behavior, so emulate it.
UniquePtr<SSL_SESSION> session;
if (!ssl->server && ssl->s3->established_session != NULL) {
- session.reset(ssl->s3->established_session.get());
- SSL_SESSION_up_ref(session.get());
+ session = UpRef(ssl->s3->established_session);
}
// The ssl->d1->mtu is simultaneously configuration (preserved across
@@ -2707,3 +2749,36 @@
const SSL_TICKET_AEAD_METHOD *aead_method) {
ctx->ticket_aead_method = aead_method;
}
+
+int SSL_set_tlsext_status_type(SSL *ssl, int type) {
+ if (!ssl->config) {
+ return 0;
+ }
+ ssl->config->ocsp_stapling_enabled = type == TLSEXT_STATUSTYPE_ocsp;
+ return 1;
+}
+
+int SSL_set_tlsext_status_ocsp_resp(SSL *ssl, uint8_t *resp, size_t resp_len) {
+ if (SSL_set_ocsp_response(ssl, resp, resp_len)) {
+ OPENSSL_free(resp);
+ return 1;
+ }
+ return 0;
+}
+
+size_t SSL_get_tlsext_status_ocsp_resp(const SSL *ssl, const uint8_t **out) {
+ size_t ret;
+ SSL_get0_ocsp_response(ssl, out, &ret);
+ return ret;
+}
+
+int SSL_CTX_set_tlsext_status_cb(SSL_CTX *ctx,
+ int (*callback)(SSL *ssl, void *arg)) {
+ ctx->legacy_ocsp_callback = callback;
+ return 1;
+}
+
+int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg) {
+ ctx->legacy_ocsp_callback_arg = arg;
+ return 1;
+}
diff --git a/src/ssl/ssl_privkey.cc b/src/ssl/ssl_privkey.cc
index bba03b7..fecac39 100644
--- a/src/ssl/ssl_privkey.cc
+++ b/src/ssl/ssl_privkey.cc
@@ -89,9 +89,7 @@
return 0;
}
- EVP_PKEY_up_ref(pkey);
- cert->privatekey.reset(pkey);
-
+ cert->privatekey = UpRef(pkey);
return 1;
}
@@ -134,8 +132,8 @@
return NULL;
}
-int ssl_has_private_key(const SSL *ssl) {
- return ssl->cert->privatekey != nullptr || ssl->cert->key_method != nullptr;
+int ssl_has_private_key(const SSL_CONFIG *cfg) {
+ return cfg->cert->privatekey != nullptr || cfg->cert->key_method != nullptr;
}
static int pkey_supports_algorithm(const SSL *ssl, EVP_PKEY *pkey,
@@ -196,13 +194,13 @@
SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
uint16_t sigalg, Span<const uint8_t> in) {
SSL *const ssl = hs->ssl;
- if (ssl->cert->key_method != NULL) {
+ if (hs->config->cert->key_method != NULL) {
enum ssl_private_key_result_t ret;
if (hs->pending_private_key_op) {
- ret = ssl->cert->key_method->complete(ssl, out, out_len, max_out);
+ ret = hs->config->cert->key_method->complete(ssl, out, out_len, max_out);
} else {
- ret = ssl->cert->key_method->sign(ssl, out, out_len, max_out, sigalg,
- in.data(), in.size());
+ ret = hs->config->cert->key_method->sign(ssl, out, out_len, max_out,
+ sigalg, in.data(), in.size());
}
if (ret == ssl_private_key_failure) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PRIVATE_KEY_OPERATION_FAILED);
@@ -213,7 +211,7 @@
*out_len = max_out;
ScopedEVP_MD_CTX ctx;
- if (!setup_ctx(ssl, ctx.get(), ssl->cert->privatekey.get(), sigalg,
+ if (!setup_ctx(ssl, ctx.get(), hs->config->cert->privatekey.get(), sigalg,
0 /* sign */) ||
!EVP_DigestSign(ctx.get(), out, out_len, in.data(), in.size())) {
return ssl_private_key_failure;
@@ -236,13 +234,13 @@
size_t max_out,
Span<const uint8_t> in) {
SSL *const ssl = hs->ssl;
- if (ssl->cert->key_method != NULL) {
+ if (hs->config->cert->key_method != NULL) {
enum ssl_private_key_result_t ret;
if (hs->pending_private_key_op) {
- ret = ssl->cert->key_method->complete(ssl, out, out_len, max_out);
+ ret = hs->config->cert->key_method->complete(ssl, out, out_len, max_out);
} else {
- ret = ssl->cert->key_method->decrypt(ssl, out, out_len, max_out,
- in.data(), in.size());
+ ret = hs->config->cert->key_method->decrypt(ssl, out, out_len, max_out,
+ in.data(), in.size());
}
if (ret == ssl_private_key_failure) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PRIVATE_KEY_OPERATION_FAILED);
@@ -251,7 +249,7 @@
return ret;
}
- RSA *rsa = EVP_PKEY_get0_RSA(ssl->cert->privatekey.get());
+ RSA *rsa = EVP_PKEY_get0_RSA(hs->config->cert->privatekey.get());
if (rsa == NULL) {
// Decrypt operations are only supported for RSA keys.
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -294,7 +292,7 @@
using namespace bssl;
int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa) {
- if (rsa == NULL) {
+ if (rsa == NULL || ssl->config == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
@@ -306,7 +304,7 @@
return 0;
}
- return ssl_set_pkey(ssl->cert, pkey.get());
+ return ssl_set_pkey(ssl->config->cert.get(), pkey.get());
}
int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
@@ -320,12 +318,12 @@
}
int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) {
- if (pkey == NULL) {
+ if (pkey == NULL || ssl->config == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
- return ssl_set_pkey(ssl->cert, pkey);
+ return ssl_set_pkey(ssl->config->cert.get(), pkey);
}
int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *der,
@@ -358,7 +356,7 @@
return 0;
}
- return ssl_set_pkey(ctx->cert, pkey.get());
+ return ssl_set_pkey(ctx->cert.get(), pkey.get());
}
int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const uint8_t *der,
@@ -378,7 +376,7 @@
return 0;
}
- return ssl_set_pkey(ctx->cert, pkey);
+ return ssl_set_pkey(ctx->cert.get(), pkey);
}
int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const uint8_t *der,
@@ -400,7 +398,10 @@
void SSL_set_private_key_method(SSL *ssl,
const SSL_PRIVATE_KEY_METHOD *key_method) {
- ssl->cert->key_method = key_method;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->cert->key_method = key_method;
}
void SSL_CTX_set_private_key_method(SSL_CTX *ctx,
@@ -408,38 +409,49 @@
ctx->cert->key_method = key_method;
}
+static constexpr size_t kMaxSignatureAlgorithmNameLen = 23;
+
+// This was "constexpr" rather than "const", but that triggered a bug in MSVC
+// where it didn't pad the strings to the correct length.
+static const struct {
+ uint16_t signature_algorithm;
+ const char name[kMaxSignatureAlgorithmNameLen];
+} kSignatureAlgorithmNames[] = {
+ {SSL_SIGN_RSA_PKCS1_MD5_SHA1, "rsa_pkcs1_md5_sha1"},
+ {SSL_SIGN_RSA_PKCS1_SHA1, "rsa_pkcs1_sha1"},
+ {SSL_SIGN_RSA_PKCS1_SHA256, "rsa_pkcs1_sha256"},
+ {SSL_SIGN_RSA_PKCS1_SHA384, "rsa_pkcs1_sha384"},
+ {SSL_SIGN_RSA_PKCS1_SHA512, "rsa_pkcs1_sha512"},
+ {SSL_SIGN_ECDSA_SHA1, "ecdsa_sha1"},
+ {SSL_SIGN_ECDSA_SECP256R1_SHA256, "ecdsa_secp256r1_sha256"},
+ {SSL_SIGN_ECDSA_SECP384R1_SHA384, "ecdsa_secp384r1_sha384"},
+ {SSL_SIGN_ECDSA_SECP521R1_SHA512, "ecdsa_secp521r1_sha512"},
+ {SSL_SIGN_RSA_PSS_RSAE_SHA256, "rsa_pss_rsae_sha256"},
+ {SSL_SIGN_RSA_PSS_RSAE_SHA384, "rsa_pss_rsae_sha384"},
+ {SSL_SIGN_RSA_PSS_RSAE_SHA512, "rsa_pss_rsae_sha512"},
+ {SSL_SIGN_ED25519, "ed25519"},
+};
+
const char *SSL_get_signature_algorithm_name(uint16_t sigalg,
int include_curve) {
- switch (sigalg) {
- case SSL_SIGN_RSA_PKCS1_MD5_SHA1:
- return "rsa_pkcs1_md5_sha1";
- case SSL_SIGN_RSA_PKCS1_SHA1:
- return "rsa_pkcs1_sha1";
- case SSL_SIGN_RSA_PKCS1_SHA256:
- return "rsa_pkcs1_sha256";
- case SSL_SIGN_RSA_PKCS1_SHA384:
- return "rsa_pkcs1_sha384";
- case SSL_SIGN_RSA_PKCS1_SHA512:
- return "rsa_pkcs1_sha512";
- case SSL_SIGN_ECDSA_SHA1:
- return "ecdsa_sha1";
- case SSL_SIGN_ECDSA_SECP256R1_SHA256:
- return include_curve ? "ecdsa_secp256r1_sha256" : "ecdsa_sha256";
- case SSL_SIGN_ECDSA_SECP384R1_SHA384:
- return include_curve ? "ecdsa_secp384r1_sha384" : "ecdsa_sha384";
- case SSL_SIGN_ECDSA_SECP521R1_SHA512:
- return include_curve ? "ecdsa_secp521r1_sha512" : "ecdsa_sha512";
- case SSL_SIGN_RSA_PSS_RSAE_SHA256:
- return "rsa_pss_rsae_sha256";
- case SSL_SIGN_RSA_PSS_RSAE_SHA384:
- return "rsa_pss_rsae_sha384";
- case SSL_SIGN_RSA_PSS_RSAE_SHA512:
- return "rsa_pss_rsae_sha512";
- case SSL_SIGN_ED25519:
- return "ed25519";
- default:
- return NULL;
+ if (!include_curve) {
+ switch (sigalg) {
+ case SSL_SIGN_ECDSA_SECP256R1_SHA256:
+ return "ecdsa_sha256";
+ case SSL_SIGN_ECDSA_SECP384R1_SHA384:
+ return "ecdsa_sha384";
+ case SSL_SIGN_ECDSA_SECP521R1_SHA512:
+ return "ecdsa_sha512";
+ }
}
+
+ for (const auto &candidate : kSignatureAlgorithmNames) {
+ if (candidate.signature_algorithm == sigalg) {
+ return candidate.name;
+ }
+ }
+
+ return NULL;
}
int SSL_get_signature_algorithm_key_type(uint16_t sigalg) {
@@ -460,21 +472,6 @@
return alg != nullptr && alg->is_rsa_pss;
}
-static int set_algorithm_prefs(uint16_t **out_prefs, size_t *out_num_prefs,
- const uint16_t *prefs, size_t num_prefs) {
- OPENSSL_free(*out_prefs);
-
- *out_num_prefs = 0;
- *out_prefs = (uint16_t *)BUF_memdup(prefs, num_prefs * sizeof(prefs[0]));
- if (*out_prefs == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
- }
- *out_num_prefs = num_prefs;
-
- return 1;
-}
-
int SSL_CTX_set_signing_algorithm_prefs(SSL_CTX *ctx, const uint16_t *prefs,
size_t num_prefs) {
return ctx->cert->sigalgs.CopyFrom(MakeConstSpan(prefs, num_prefs));
@@ -482,11 +479,322 @@
int SSL_set_signing_algorithm_prefs(SSL *ssl, const uint16_t *prefs,
size_t num_prefs) {
- return ssl->cert->sigalgs.CopyFrom(MakeConstSpan(prefs, num_prefs));
+ if (!ssl->config) {
+ return 0;
+ }
+ return ssl->config->cert->sigalgs.CopyFrom(MakeConstSpan(prefs, num_prefs));
+}
+
+static constexpr struct {
+ int pkey_type;
+ int hash_nid;
+ uint16_t signature_algorithm;
+} kSignatureAlgorithmsMapping[] = {
+ {EVP_PKEY_RSA, NID_sha1, SSL_SIGN_RSA_PKCS1_SHA1},
+ {EVP_PKEY_RSA, NID_sha256, SSL_SIGN_RSA_PKCS1_SHA256},
+ {EVP_PKEY_RSA, NID_sha384, SSL_SIGN_RSA_PKCS1_SHA384},
+ {EVP_PKEY_RSA, NID_sha512, SSL_SIGN_RSA_PKCS1_SHA512},
+ {EVP_PKEY_RSA_PSS, NID_sha256, SSL_SIGN_RSA_PSS_RSAE_SHA256},
+ {EVP_PKEY_RSA_PSS, NID_sha384, SSL_SIGN_RSA_PSS_RSAE_SHA384},
+ {EVP_PKEY_RSA_PSS, NID_sha512, SSL_SIGN_RSA_PSS_RSAE_SHA512},
+ {EVP_PKEY_EC, NID_sha1, SSL_SIGN_ECDSA_SHA1},
+ {EVP_PKEY_EC, NID_sha256, SSL_SIGN_ECDSA_SECP256R1_SHA256},
+ {EVP_PKEY_EC, NID_sha384, SSL_SIGN_ECDSA_SECP384R1_SHA384},
+ {EVP_PKEY_EC, NID_sha512, SSL_SIGN_ECDSA_SECP521R1_SHA512},
+ {EVP_PKEY_ED25519, NID_undef, SSL_SIGN_ED25519},
+};
+
+static bool parse_sigalg_pairs(Array<uint16_t> *out, const int *values,
+ size_t num_values) {
+ if ((num_values & 1) == 1) {
+ return false;
+ }
+
+ const size_t num_pairs = num_values / 2;
+ if (!out->Init(num_pairs)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < num_values; i += 2) {
+ const int hash_nid = values[i];
+ const int pkey_type = values[i+1];
+
+ bool found = false;
+ for (const auto &candidate : kSignatureAlgorithmsMapping) {
+ if (candidate.pkey_type == pkey_type && candidate.hash_nid == hash_nid) {
+ (*out)[i / 2] = candidate.signature_algorithm;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("unknown hash:%d pkey:%d", hash_nid, pkey_type);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int compare_uint16_t(const void *p1, const void *p2) {
+ uint16_t u1 = *((const uint16_t *)p1);
+ uint16_t u2 = *((const uint16_t *)p2);
+ if (u1 < u2) {
+ return -1;
+ } else if (u1 > u2) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static bool sigalgs_unique(Span<const uint16_t> in_sigalgs) {
+ Array<uint16_t> sigalgs;
+ if (!sigalgs.CopyFrom(in_sigalgs)) {
+ return false;
+ }
+
+ qsort(sigalgs.data(), sigalgs.size(), sizeof(uint16_t), compare_uint16_t);
+
+ for (size_t i = 1; i < sigalgs.size(); i++) {
+ if (sigalgs[i - 1] == sigalgs[i]) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_SIGNATURE_ALGORITHM);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int SSL_CTX_set1_sigalgs(SSL_CTX *ctx, const int *values, size_t num_values) {
+ Array<uint16_t> sigalgs;
+ if (!parse_sigalg_pairs(&sigalgs, values, num_values) ||
+ !sigalgs_unique(sigalgs)) {
+ return 0;
+ }
+
+ if (!SSL_CTX_set_signing_algorithm_prefs(ctx, sigalgs.data(),
+ sigalgs.size()) ||
+ !ctx->verify_sigalgs.CopyFrom(sigalgs)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int SSL_set1_sigalgs(SSL *ssl, const int *values, size_t num_values) {
+ if (!ssl->config) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ Array<uint16_t> sigalgs;
+ if (!parse_sigalg_pairs(&sigalgs, values, num_values) ||
+ !sigalgs_unique(sigalgs)) {
+ return 0;
+ }
+
+ if (!SSL_set_signing_algorithm_prefs(ssl, sigalgs.data(), sigalgs.size()) ||
+ !ssl->config->verify_sigalgs.CopyFrom(sigalgs)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static bool parse_sigalgs_list(Array<uint16_t> *out, const char *str) {
+ // str looks like "RSA+SHA1:ECDSA+SHA256:ecdsa_secp256r1_sha256".
+
+ // Count colons to give the number of output elements from any successful
+ // parse.
+ size_t num_elements = 1;
+ size_t len = 0;
+ for (const char *p = str; *p; p++) {
+ len++;
+ if (*p == ':') {
+ num_elements++;
+ }
+ }
+
+ if (!out->Init(num_elements)) {
+ return false;
+ }
+ size_t out_i = 0;
+
+ enum {
+ pkey_or_name,
+ hash_name,
+ } state = pkey_or_name;
+
+ char buf[kMaxSignatureAlgorithmNameLen];
+ // buf_used is always < sizeof(buf). I.e. it's always safe to write
+ // buf[buf_used] = 0.
+ size_t buf_used = 0;
+
+ int pkey_type = 0, hash_nid = 0;
+
+ // Note that the loop runs to len+1, i.e. it'll process the terminating NUL.
+ for (size_t offset = 0; offset < len+1; offset++) {
+ const char c = str[offset];
+
+ switch (c) {
+ case '+':
+ if (state == hash_name) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("+ found in hash name at offset %zu", offset);
+ return false;
+ }
+ if (buf_used == 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("empty public key type at offset %zu", offset);
+ return false;
+ }
+ buf[buf_used] = 0;
+
+ if (strcmp(buf, "RSA") == 0) {
+ pkey_type = EVP_PKEY_RSA;
+ } else if (strcmp(buf, "RSA-PSS") == 0 ||
+ strcmp(buf, "PSS") == 0) {
+ pkey_type = EVP_PKEY_RSA_PSS;
+ } else if (strcmp(buf, "ECDSA") == 0) {
+ pkey_type = EVP_PKEY_EC;
+ } else {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("unknown public key type '%s'", buf);
+ return false;
+ }
+
+ state = hash_name;
+ buf_used = 0;
+ break;
+
+ case ':':
+ OPENSSL_FALLTHROUGH;
+ case 0:
+ if (buf_used == 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("empty element at offset %zu", offset);
+ return false;
+ }
+
+ buf[buf_used] = 0;
+
+ if (state == pkey_or_name) {
+ // No '+' was seen thus this is a TLS 1.3-style name.
+ bool found = false;
+ for (const auto &candidate : kSignatureAlgorithmNames) {
+ if (strcmp(candidate.name, buf) == 0) {
+ assert(out_i < num_elements);
+ (*out)[out_i++] = candidate.signature_algorithm;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("unknown signature algorithm '%s'", buf);
+ return false;
+ }
+ } else {
+ if (strcmp(buf, "SHA1") == 0) {
+ hash_nid = NID_sha1;
+ } else if (strcmp(buf, "SHA256") == 0) {
+ hash_nid = NID_sha256;
+ } else if (strcmp(buf, "SHA384") == 0) {
+ hash_nid = NID_sha384;
+ } else if (strcmp(buf, "SHA512") == 0) {
+ hash_nid = NID_sha512;
+ } else {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("unknown hash function '%s'", buf);
+ return false;
+ }
+
+ bool found = false;
+ for (const auto &candidate : kSignatureAlgorithmsMapping) {
+ if (candidate.pkey_type == pkey_type &&
+ candidate.hash_nid == hash_nid) {
+ assert(out_i < num_elements);
+ (*out)[out_i++] = candidate.signature_algorithm;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("unknown pkey:%d hash:%s", pkey_type, buf);
+ return false;
+ }
+ }
+
+ state = pkey_or_name;
+ buf_used = 0;
+ break;
+
+ default:
+ if (buf_used == sizeof(buf) - 1) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("substring too long at offset %zu", offset);
+ return false;
+ }
+
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || c == '-' || c == '_') {
+ buf[buf_used++] = c;
+ } else {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
+ ERR_add_error_dataf("invalid character 0x%02x at offest %zu", c,
+ offset);
+ return false;
+ }
+ }
+ }
+
+ assert(out_i == out->size());
+ return true;
+}
+
+int SSL_CTX_set1_sigalgs_list(SSL_CTX *ctx, const char *str) {
+ Array<uint16_t> sigalgs;
+ if (!parse_sigalgs_list(&sigalgs, str) ||
+ !sigalgs_unique(sigalgs)) {
+ return 0;
+ }
+
+ if (!SSL_CTX_set_signing_algorithm_prefs(ctx, sigalgs.data(),
+ sigalgs.size()) ||
+ !ctx->verify_sigalgs.CopyFrom(sigalgs)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int SSL_set1_sigalgs_list(SSL *ssl, const char *str) {
+ if (!ssl->config) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ Array<uint16_t> sigalgs;
+ if (!parse_sigalgs_list(&sigalgs, str) ||
+ !sigalgs_unique(sigalgs)) {
+ return 0;
+ }
+
+ if (!SSL_set_signing_algorithm_prefs(ssl, sigalgs.data(), sigalgs.size()) ||
+ !ssl->config->verify_sigalgs.CopyFrom(sigalgs)) {
+ return 0;
+ }
+
+ return 1;
}
int SSL_CTX_set_verify_algorithm_prefs(SSL_CTX *ctx, const uint16_t *prefs,
size_t num_prefs) {
- return set_algorithm_prefs(&ctx->verify_sigalgs, &ctx->num_verify_sigalgs,
- prefs, num_prefs);
+ return ctx->verify_sigalgs.CopyFrom(MakeConstSpan(prefs, num_prefs));
}
diff --git a/src/ssl/ssl_session.cc b/src/ssl/ssl_session.cc
index 272fc55..70be17e 100644
--- a/src/ssl/ssl_session.cc
+++ b/src/ssl/ssl_session.cc
@@ -166,22 +166,27 @@
static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock);
UniquePtr<SSL_SESSION> ssl_session_new(const SSL_X509_METHOD *x509_method) {
- UniquePtr<SSL_SESSION> session(
- (SSL_SESSION *)OPENSSL_malloc(sizeof(SSL_SESSION)));
- if (!session) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
- }
- OPENSSL_memset(session.get(), 0, sizeof(SSL_SESSION));
+ return MakeUnique<SSL_SESSION>(x509_method);
+}
- session->x509_method = x509_method;
- session->verify_result = X509_V_ERR_INVALID_CALL;
- session->references = 1;
- session->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
- session->auth_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
- session->time = time(NULL);
- CRYPTO_new_ex_data(&session->ex_data);
- return session;
+uint32_t ssl_hash_session_id(Span<const uint8_t> session_id) {
+ // Take the first four bytes of |session_id|. Session IDs are generated by the
+ // server randomly, so we can assume even using the first four bytes results
+ // in a good distribution.
+ uint8_t tmp_storage[sizeof(uint32_t)];
+ if (session_id.size() < sizeof(tmp_storage)) {
+ OPENSSL_memset(tmp_storage, 0, sizeof(tmp_storage));
+ OPENSSL_memcpy(tmp_storage, session_id.data(), session_id.size());
+ session_id = tmp_storage;
+ }
+
+ uint32_t hash =
+ ((uint32_t)session_id[0]) |
+ ((uint32_t)session_id[1] << 8) |
+ ((uint32_t)session_id[2] << 16) |
+ ((uint32_t)session_id[3] << 24);
+
+ return hash;
}
UniquePtr<SSL_SESSION> SSL_SESSION_dup(SSL_SESSION *session, int dup_flags) {
@@ -202,24 +207,22 @@
new_session->cipher = session->cipher;
// Copy authentication state.
- if (session->psk_identity != NULL) {
- new_session->psk_identity = BUF_strdup(session->psk_identity);
- if (new_session->psk_identity == NULL) {
+ if (session->psk_identity != nullptr) {
+ new_session->psk_identity.reset(BUF_strdup(session->psk_identity.get()));
+ if (new_session->psk_identity == nullptr) {
return nullptr;
}
}
- if (session->certs != NULL) {
- new_session->certs = sk_CRYPTO_BUFFER_new_null();
- if (new_session->certs == NULL) {
+ if (session->certs != nullptr) {
+ auto buf_up_ref = [](CRYPTO_BUFFER *buf) {
+ CRYPTO_BUFFER_up_ref(buf);
+ return buf;
+ };
+ new_session->certs.reset(sk_CRYPTO_BUFFER_deep_copy(
+ session->certs.get(), buf_up_ref, CRYPTO_BUFFER_free));
+ if (new_session->certs == nullptr) {
return nullptr;
}
- for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(session->certs); i++) {
- CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(session->certs, i);
- if (!sk_CRYPTO_BUFFER_push(new_session->certs, buffer)) {
- return nullptr;
- }
- CRYPTO_BUFFER_up_ref(buffer);
- }
}
if (!session->x509_method->session_dup(new_session.get(), session)) {
@@ -228,16 +231,9 @@
new_session->verify_result = session->verify_result;
- if (session->ocsp_response != NULL) {
- new_session->ocsp_response = session->ocsp_response;
- CRYPTO_BUFFER_up_ref(new_session->ocsp_response);
- }
-
- if (session->signed_cert_timestamp_list != NULL) {
- new_session->signed_cert_timestamp_list =
- session->signed_cert_timestamp_list;
- CRYPTO_BUFFER_up_ref(new_session->signed_cert_timestamp_list);
- }
+ new_session->ocsp_response = UpRef(session->ocsp_response);
+ new_session->signed_cert_timestamp_list =
+ UpRef(session->signed_cert_timestamp_list);
OPENSSL_memcpy(new_session->peer_sha256, session->peer_sha256,
SHA256_DIGEST_LENGTH);
@@ -262,36 +258,25 @@
session->original_handshake_hash_len);
new_session->original_handshake_hash_len =
session->original_handshake_hash_len;
- new_session->tlsext_tick_lifetime_hint = session->tlsext_tick_lifetime_hint;
+ new_session->ticket_lifetime_hint = session->ticket_lifetime_hint;
new_session->ticket_age_add = session->ticket_age_add;
new_session->ticket_max_early_data = session->ticket_max_early_data;
new_session->extended_master_secret = session->extended_master_secret;
- if (session->early_alpn != NULL) {
- new_session->early_alpn =
- (uint8_t *)BUF_memdup(session->early_alpn, session->early_alpn_len);
- if (new_session->early_alpn == NULL) {
- return nullptr;
- }
+ if (!new_session->early_alpn.CopyFrom(session->early_alpn)) {
+ return nullptr;
}
- new_session->early_alpn_len = session->early_alpn_len;
}
// Copy the ticket.
- if (dup_flags & SSL_SESSION_INCLUDE_TICKET) {
- if (session->tlsext_tick != NULL) {
- new_session->tlsext_tick =
- (uint8_t *)BUF_memdup(session->tlsext_tick, session->tlsext_ticklen);
- if (new_session->tlsext_tick == NULL) {
- return nullptr;
- }
- }
- new_session->tlsext_ticklen = session->tlsext_ticklen;
+ if (dup_flags & SSL_SESSION_INCLUDE_TICKET &&
+ !new_session->ticket.CopyFrom(session->ticket)) {
+ return nullptr;
}
// The new_session does not get a copy of the ex_data.
- new_session->not_resumable = 1;
+ new_session->not_resumable = true;
return new_session;
}
@@ -405,16 +390,16 @@
session->session_id_length = 0;
}
- if (ssl->cert->sid_ctx_length > sizeof(session->sid_ctx)) {
+ if (hs->config->cert->sid_ctx_length > sizeof(session->sid_ctx)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
- OPENSSL_memcpy(session->sid_ctx, ssl->cert->sid_ctx,
- ssl->cert->sid_ctx_length);
- session->sid_ctx_length = ssl->cert->sid_ctx_length;
+ OPENSSL_memcpy(session->sid_ctx, hs->config->cert->sid_ctx,
+ hs->config->cert->sid_ctx_length);
+ session->sid_ctx_length = hs->config->cert->sid_ctx_length;
// The session is marked not resumable until it is completely filled in.
- session->not_resumable = 1;
+ session->not_resumable = true;
session->verify_result = X509_V_ERR_INVALID_CALL;
hs->new_session = std::move(session);
@@ -429,53 +414,50 @@
// Avoid acquiring a write lock in the common case (i.e. a non-default key
// is used or the default keys have not expired yet).
MutexReadLock lock(&ctx->lock);
- if (ctx->tlsext_ticket_key_current &&
- (ctx->tlsext_ticket_key_current->next_rotation_tv_sec == 0 ||
- ctx->tlsext_ticket_key_current->next_rotation_tv_sec > now.tv_sec) &&
- (!ctx->tlsext_ticket_key_prev ||
- ctx->tlsext_ticket_key_prev->next_rotation_tv_sec > now.tv_sec)) {
+ if (ctx->ticket_key_current &&
+ (ctx->ticket_key_current->next_rotation_tv_sec == 0 ||
+ ctx->ticket_key_current->next_rotation_tv_sec > now.tv_sec) &&
+ (!ctx->ticket_key_prev ||
+ ctx->ticket_key_prev->next_rotation_tv_sec > now.tv_sec)) {
return 1;
}
}
MutexWriteLock lock(&ctx->lock);
- if (!ctx->tlsext_ticket_key_current ||
- (ctx->tlsext_ticket_key_current->next_rotation_tv_sec != 0 &&
- ctx->tlsext_ticket_key_current->next_rotation_tv_sec <= now.tv_sec)) {
+ if (!ctx->ticket_key_current ||
+ (ctx->ticket_key_current->next_rotation_tv_sec != 0 &&
+ ctx->ticket_key_current->next_rotation_tv_sec <= now.tv_sec)) {
// The current key has not been initialized or it is expired.
- auto new_key = bssl::MakeUnique<struct tlsext_ticket_key>();
+ auto new_key = bssl::MakeUnique<TicketKey>();
if (!new_key) {
return 0;
}
- OPENSSL_memset(new_key.get(), 0, sizeof(struct tlsext_ticket_key));
- if (ctx->tlsext_ticket_key_current) {
+ RAND_bytes(new_key->name, 16);
+ RAND_bytes(new_key->hmac_key, 16);
+ RAND_bytes(new_key->aes_key, 16);
+ new_key->next_rotation_tv_sec =
+ now.tv_sec + SSL_DEFAULT_TICKET_KEY_ROTATION_INTERVAL;
+ if (ctx->ticket_key_current) {
// The current key expired. Rotate it to prev and bump up its rotation
// timestamp. Note that even with the new rotation time it may still be
- // expired and get droppped below.
- ctx->tlsext_ticket_key_current->next_rotation_tv_sec +=
+ // expired and get dropped below.
+ ctx->ticket_key_current->next_rotation_tv_sec +=
SSL_DEFAULT_TICKET_KEY_ROTATION_INTERVAL;
- OPENSSL_free(ctx->tlsext_ticket_key_prev);
- ctx->tlsext_ticket_key_prev = ctx->tlsext_ticket_key_current;
+ ctx->ticket_key_prev = std::move(ctx->ticket_key_current);
}
- ctx->tlsext_ticket_key_current = new_key.release();
- RAND_bytes(ctx->tlsext_ticket_key_current->name, 16);
- RAND_bytes(ctx->tlsext_ticket_key_current->hmac_key, 16);
- RAND_bytes(ctx->tlsext_ticket_key_current->aes_key, 16);
- ctx->tlsext_ticket_key_current->next_rotation_tv_sec =
- now.tv_sec + SSL_DEFAULT_TICKET_KEY_ROTATION_INTERVAL;
+ ctx->ticket_key_current = std::move(new_key);
}
// Drop an expired prev key.
- if (ctx->tlsext_ticket_key_prev &&
- ctx->tlsext_ticket_key_prev->next_rotation_tv_sec <= now.tv_sec) {
- OPENSSL_free(ctx->tlsext_ticket_key_prev);
- ctx->tlsext_ticket_key_prev = nullptr;
+ if (ctx->ticket_key_prev &&
+ ctx->ticket_key_prev->next_rotation_tv_sec <= now.tv_sec) {
+ ctx->ticket_key_prev.reset();
}
return 1;
}
-static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out,
+static int ssl_encrypt_ticket_with_cipher_ctx(SSL_HANDSHAKE *hs, CBB *out,
const uint8_t *session_buf,
size_t session_len) {
ScopedEVP_CIPHER_CTX ctx;
@@ -493,12 +475,12 @@
// Initialize HMAC and cipher contexts. If callback present it does all the
// work otherwise use generated values from parent ctx.
- SSL_CTX *tctx = ssl->session_ctx;
+ SSL_CTX *tctx = hs->ssl->session_ctx.get();
uint8_t iv[EVP_MAX_IV_LENGTH];
uint8_t key_name[16];
- if (tctx->tlsext_ticket_key_cb != NULL) {
- if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, ctx.get(), hctx.get(),
- 1 /* encrypt */) < 0) {
+ if (tctx->ticket_key_cb != NULL) {
+ if (tctx->ticket_key_cb(hs->ssl, key_name, iv, ctx.get(), hctx.get(),
+ 1 /* encrypt */) < 0) {
return 0;
}
} else {
@@ -509,12 +491,12 @@
MutexReadLock lock(&tctx->lock);
if (!RAND_bytes(iv, 16) ||
!EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_cbc(), NULL,
- tctx->tlsext_ticket_key_current->aes_key, iv) ||
- !HMAC_Init_ex(hctx.get(), tctx->tlsext_ticket_key_current->hmac_key, 16,
+ tctx->ticket_key_current->aes_key, iv) ||
+ !HMAC_Init_ex(hctx.get(), tctx->ticket_key_current->hmac_key, 16,
tlsext_tick_md(), NULL)) {
return 0;
}
- OPENSSL_memcpy(key_name, tctx->tlsext_ticket_key_current->name, 16);
+ OPENSSL_memcpy(key_name, tctx->ticket_key_current->name, 16);
}
uint8_t *ptr;
@@ -554,9 +536,10 @@
return 1;
}
-static int ssl_encrypt_ticket_with_method(SSL *ssl, CBB *out,
+static int ssl_encrypt_ticket_with_method(SSL_HANDSHAKE *hs, CBB *out,
const uint8_t *session_buf,
size_t session_len) {
+ SSL *const ssl = hs->ssl;
const SSL_TICKET_AEAD_METHOD *method = ssl->session_ctx->ticket_aead_method;
const size_t max_overhead = method->max_overhead(ssl);
const size_t max_out = session_len + max_overhead;
@@ -571,7 +554,8 @@
}
size_t out_len;
- if (!method->seal(ssl, ptr, &out_len, max_out, session_buf, session_len)) {
+ if (!method->seal(ssl, ptr, &out_len, max_out, session_buf,
+ session_len)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_TICKET_ENCRYPTION_FAILED);
return 0;
}
@@ -583,7 +567,8 @@
return 1;
}
-int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) {
+int ssl_encrypt_ticket(SSL_HANDSHAKE *hs, CBB *out,
+ const SSL_SESSION *session) {
// Serialize the SSL_SESSION to be encoded into the ticket.
uint8_t *session_buf = NULL;
size_t session_len;
@@ -592,25 +577,25 @@
}
int ret = 0;
- if (ssl->session_ctx->ticket_aead_method) {
- ret = ssl_encrypt_ticket_with_method(ssl, out, session_buf, session_len);
+ if (hs->ssl->session_ctx->ticket_aead_method) {
+ ret = ssl_encrypt_ticket_with_method(hs, out, session_buf, session_len);
} else {
- ret =
- ssl_encrypt_ticket_with_cipher_ctx(ssl, out, session_buf, session_len);
+ ret = ssl_encrypt_ticket_with_cipher_ctx(hs, out, session_buf, session_len);
}
OPENSSL_free(session_buf);
return ret;
}
-int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session) {
+int ssl_session_is_context_valid(const SSL_HANDSHAKE *hs,
+ const SSL_SESSION *session) {
if (session == NULL) {
return 0;
}
- return session->sid_ctx_length == ssl->cert->sid_ctx_length &&
- OPENSSL_memcmp(session->sid_ctx, ssl->cert->sid_ctx,
- ssl->cert->sid_ctx_length) == 0;
+ return session->sid_ctx_length == hs->config->cert->sid_ctx_length &&
+ OPENSSL_memcmp(session->sid_ctx, hs->config->cert->sid_ctx,
+ hs->config->cert->sid_ctx_length) == 0;
}
int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session) {
@@ -632,34 +617,35 @@
int ssl_session_is_resumable(const SSL_HANDSHAKE *hs,
const SSL_SESSION *session) {
const SSL *const ssl = hs->ssl;
- return ssl_session_is_context_valid(ssl, session) &&
+ return ssl_session_is_context_valid(hs, session) &&
// The session must have been created by the same type of end point as
// we're now using it with.
ssl->server == session->is_server &&
// The session must not be expired.
ssl_session_is_time_valid(ssl, session) &&
/* Only resume if the session's version matches the negotiated
- * version. */
+ * version. */
ssl->version == session->ssl_version &&
// Only resume if the session's cipher matches the negotiated one.
hs->new_cipher == session->cipher &&
// If the session contains a client certificate (either the full
// certificate or just the hash) then require that the form of the
// certificate matches the current configuration.
- ((sk_CRYPTO_BUFFER_num(session->certs) == 0 &&
+ ((sk_CRYPTO_BUFFER_num(session->certs.get()) == 0 &&
!session->peer_sha256_valid) ||
session->peer_sha256_valid ==
- ssl->retain_only_sha256_of_client_certs);
+ hs->config->retain_only_sha256_of_client_certs);
}
// ssl_lookup_session looks up |session_id| in the session cache and sets
// |*out_session| to an |SSL_SESSION| object if found.
static enum ssl_hs_wait_t ssl_lookup_session(
- SSL *ssl, UniquePtr<SSL_SESSION> *out_session, const uint8_t *session_id,
- size_t session_id_len) {
+ SSL_HANDSHAKE *hs, UniquePtr<SSL_SESSION> *out_session,
+ Span<const uint8_t> session_id) {
+ SSL *const ssl = hs->ssl;
out_session->reset();
- if (session_id_len == 0 || session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+ if (session_id.empty() || session_id.size() > SSL_MAX_SSL_SESSION_ID_LENGTH) {
return ssl_hs_ok;
}
@@ -667,25 +653,26 @@
// Try the internal cache, if it exists.
if (!(ssl->session_ctx->session_cache_mode &
SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) {
- SSL_SESSION data;
- data.ssl_version = ssl->version;
- data.session_id_length = session_id_len;
- OPENSSL_memcpy(data.session_id, session_id, session_id_len);
-
+ uint32_t hash = ssl_hash_session_id(session_id);
+ auto cmp = [](const void *key, const SSL_SESSION *sess) -> int {
+ Span<const uint8_t> key_id =
+ *reinterpret_cast<const Span<const uint8_t> *>(key);
+ Span<const uint8_t> sess_id =
+ MakeConstSpan(sess->session_id, sess->session_id_length);
+ return key_id == sess_id ? 0 : 1;
+ };
MutexReadLock lock(&ssl->session_ctx->lock);
- session.reset(lh_SSL_SESSION_retrieve(ssl->session_ctx->sessions, &data));
- if (session) {
- // |lh_SSL_SESSION_retrieve| returns a non-owning pointer.
- SSL_SESSION_up_ref(session.get());
- }
+ // |lh_SSL_SESSION_retrieve_key| returns a non-owning pointer.
+ session = UpRef(lh_SSL_SESSION_retrieve_key(ssl->session_ctx->sessions,
+ &session_id, hash, cmp));
// TODO(davidben): This should probably move it to the front of the list.
}
// Fall back to the external cache, if it exists.
if (!session && ssl->session_ctx->get_session_cb != nullptr) {
int copy = 1;
- session.reset(ssl->session_ctx->get_session_cb(ssl, session_id,
- session_id_len, ©));
+ session.reset(ssl->session_ctx->get_session_cb(ssl, session_id.data(),
+ session_id.size(), ©));
if (!session) {
return ssl_hs_ok;
}
@@ -706,13 +693,13 @@
// Add the externally cached session to the internal cache if necessary.
if (!(ssl->session_ctx->session_cache_mode &
SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
- SSL_CTX_add_session(ssl->session_ctx, session.get());
+ SSL_CTX_add_session(ssl->session_ctx.get(), session.get());
}
}
if (session && !ssl_session_is_time_valid(ssl, session.get())) {
// The session was from the cache, so remove it.
- SSL_CTX_remove_session(ssl->session_ctx, session.get());
+ SSL_CTX_remove_session(ssl->session_ctx.get(), session.get());
session.reset();
}
@@ -720,13 +707,13 @@
return ssl_hs_ok;
}
-enum ssl_hs_wait_t ssl_get_prev_session(SSL *ssl,
+enum ssl_hs_wait_t ssl_get_prev_session(SSL_HANDSHAKE *hs,
UniquePtr<SSL_SESSION> *out_session,
bool *out_tickets_supported,
bool *out_renew_ticket,
const SSL_CLIENT_HELLO *client_hello) {
// This is used only by servers.
- assert(ssl->server);
+ assert(hs->ssl->server);
UniquePtr<SSL_SESSION> session;
bool renew_ticket = false;
@@ -734,12 +721,11 @@
const uint8_t *ticket = NULL;
size_t ticket_len = 0;
const bool tickets_supported =
- !(SSL_get_options(ssl) & SSL_OP_NO_TICKET) &&
- ssl->version > SSL3_VERSION &&
+ !(SSL_get_options(hs->ssl) & SSL_OP_NO_TICKET) &&
SSL_early_callback_ctx_extension_get(
client_hello, TLSEXT_TYPE_session_ticket, &ticket, &ticket_len);
if (tickets_supported && ticket_len > 0) {
- switch (ssl_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len,
+ switch (ssl_process_ticket(hs, &session, &renew_ticket, ticket, ticket_len,
client_hello->session_id,
client_hello->session_id_len)) {
case ssl_ticket_aead_success:
@@ -755,7 +741,8 @@
} else {
// The client didn't send a ticket, so the session ID is a real ID.
enum ssl_hs_wait_t lookup_ret = ssl_lookup_session(
- ssl, &session, client_hello->session_id, client_hello->session_id_len);
+ hs, &session,
+ MakeConstSpan(client_hello->session_id, client_hello->session_id_len));
if (lookup_ret != ssl_hs_ok) {
return lookup_ret;
}
@@ -798,15 +785,11 @@
}
void ssl_set_session(SSL *ssl, SSL_SESSION *session) {
- if (ssl->session == session) {
+ if (ssl->session.get() == session) {
return;
}
- SSL_SESSION_free(ssl->session);
- ssl->session = session;
- if (session != NULL) {
- SSL_SESSION_up_ref(session);
- }
+ ssl->session = UpRef(session);
}
// locked by SSL_CTX in the calling function
@@ -860,6 +843,22 @@
using namespace bssl;
+ssl_session_st::ssl_session_st(const SSL_X509_METHOD *method)
+ : x509_method(method),
+ extended_master_secret(false),
+ peer_sha256_valid(false),
+ not_resumable(false),
+ ticket_age_add_valid(false),
+ is_server(false) {
+ CRYPTO_new_ex_data(&ex_data);
+ time = ::time(nullptr);
+}
+
+ssl_session_st::~ssl_session_st() {
+ CRYPTO_free_ex_data(&g_ex_data_class, this, &ex_data);
+ x509_method->session_clear(this);
+}
+
SSL_SESSION *SSL_SESSION_new(const SSL_CTX *ctx) {
return ssl_session_new(ctx->x509_method).release();
}
@@ -875,17 +874,7 @@
return;
}
- CRYPTO_free_ex_data(&g_ex_data_class, session, &session->ex_data);
-
- OPENSSL_cleanse(session->master_key, sizeof(session->master_key));
- OPENSSL_cleanse(session->session_id, sizeof(session->session_id));
- sk_CRYPTO_BUFFER_pop_free(session->certs, CRYPTO_BUFFER_free);
- session->x509_method->session_clear(session);
- OPENSSL_free(session->tlsext_tick);
- CRYPTO_BUFFER_free(session->signed_cert_timestamp_list);
- CRYPTO_BUFFER_free(session->ocsp_response);
- OPENSSL_free(session->psk_identity);
- OPENSSL_free(session->early_alpn);
+ session->~ssl_session_st();
OPENSSL_free(session);
}
@@ -897,6 +886,19 @@
return session->session_id;
}
+int SSL_SESSION_set1_id(SSL_SESSION *session, const uint8_t *sid,
+ size_t sid_len) {
+ if (sid_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_TOO_LONG);
+ return 0;
+ }
+
+ // Use memmove in case someone passes in the output of |SSL_SESSION_get_id|.
+ OPENSSL_memmove(session->session_id, sid, sid_len);
+ session->session_id_length = sid_len;
+ return 1;
+}
+
uint32_t SSL_SESSION_get_timeout(const SSL_SESSION *session) {
return session->timeout;
}
@@ -915,7 +917,30 @@
const STACK_OF(CRYPTO_BUFFER) *
SSL_SESSION_get0_peer_certificates(const SSL_SESSION *session) {
- return session->certs;
+ return session->certs.get();
+}
+
+void SSL_SESSION_get0_signed_cert_timestamp_list(const SSL_SESSION *session,
+ const uint8_t **out,
+ size_t *out_len) {
+ if (session->signed_cert_timestamp_list) {
+ *out = CRYPTO_BUFFER_data(session->signed_cert_timestamp_list.get());
+ *out_len = CRYPTO_BUFFER_len(session->signed_cert_timestamp_list.get());
+ } else {
+ *out = nullptr;
+ *out_len = 0;
+ }
+}
+
+void SSL_SESSION_get0_ocsp_response(const SSL_SESSION *session,
+ const uint8_t **out, size_t *out_len) {
+ if (session->ocsp_response) {
+ *out = CRYPTO_BUFFER_data(session->ocsp_response.get());
+ *out_len = CRYPTO_BUFFER_len(session->ocsp_response.get());
+ } else {
+ *out = nullptr;
+ *out_len = 0;
+ }
}
size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, uint8_t *out,
@@ -950,6 +975,14 @@
return 1;
}
+const uint8_t *SSL_SESSION_get0_id_context(const SSL_SESSION *session,
+ unsigned *out_len) {
+ if (out_len != NULL) {
+ *out_len = session->sid_ctx_length;
+ }
+ return session->sid_ctx;
+}
+
int SSL_SESSION_set1_id_context(SSL_SESSION *session, const uint8_t *sid_ctx,
size_t sid_ctx_len) {
if (sid_ctx_len > sizeof(session->sid_ctx)) {
@@ -973,25 +1006,45 @@
}
int SSL_SESSION_has_ticket(const SSL_SESSION *session) {
- return session->tlsext_ticklen > 0;
+ return !session->ticket.empty();
}
void SSL_SESSION_get0_ticket(const SSL_SESSION *session,
const uint8_t **out_ticket, size_t *out_len) {
if (out_ticket != nullptr) {
- *out_ticket = session->tlsext_tick;
+ *out_ticket = session->ticket.data();
}
- *out_len = session->tlsext_ticklen;
+ *out_len = session->ticket.size();
+}
+
+int SSL_SESSION_set_ticket(SSL_SESSION *session, const uint8_t *ticket,
+ size_t ticket_len) {
+ return session->ticket.CopyFrom(MakeConstSpan(ticket, ticket_len));
}
uint32_t SSL_SESSION_get_ticket_lifetime_hint(const SSL_SESSION *session) {
- return session->tlsext_tick_lifetime_hint;
+ return session->ticket_lifetime_hint;
}
const SSL_CIPHER *SSL_SESSION_get0_cipher(const SSL_SESSION *session) {
return session->cipher;
}
+int SSL_SESSION_has_peer_sha256(const SSL_SESSION *session) {
+ return session->peer_sha256_valid;
+}
+
+void SSL_SESSION_get0_peer_sha256(const SSL_SESSION *session,
+ const uint8_t **out_ptr, size_t *out_len) {
+ if (session->peer_sha256_valid) {
+ *out_ptr = session->peer_sha256;
+ *out_len = sizeof(session->peer_sha256);
+ } else {
+ *out_ptr = nullptr;
+ *out_len = 0;
+ }
+}
+
SSL_SESSION *SSL_magic_pending_session_ptr(void) {
return (SSL_SESSION *)&g_pending_session_magic;
}
@@ -1010,7 +1063,7 @@
if (hs->new_session) {
return hs->new_session.get();
}
- return ssl->session;
+ return ssl->session.get();
}
SSL_SESSION *SSL_get1_session(SSL *ssl) {
@@ -1044,8 +1097,7 @@
int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *session) {
// Although |session| is inserted into two structures (a doubly-linked list
// and the hash table), |ctx| only takes one reference.
- SSL_SESSION_up_ref(session);
- UniquePtr<SSL_SESSION> owned_session(session);
+ UniquePtr<SSL_SESSION> owned_session = UpRef(session);
SSL_SESSION *old_session;
MutexWriteLock lock(&ctx->lock);
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 5fb4fb4..74c4e9e 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -48,6 +48,10 @@
#include <sys/time.h>
#endif
+#if !defined(OPENSSL_NO_THREADS)
+#include <thread>
+#endif
+
namespace bssl {
@@ -71,7 +75,6 @@
static const size_t kTicketKeyLen = 48;
static const VersionParam kAllVersions[] = {
- {SSL3_VERSION, VersionParam::is_tls, "SSL3"},
{TLS1_VERSION, VersionParam::is_tls, "TLS1"},
{TLS1_1_VERSION, VersionParam::is_tls, "TLS1_1"},
{TLS1_2_VERSION, VersionParam::is_tls, "TLS1_2"},
@@ -227,7 +230,7 @@
{
// To simplify things, banish all but {ECDHE_RSA,RSA} x
// {CHACHA20,AES_256_CBC,AES_128_CBC} x SHA1.
- "!AESGCM:!3DES:!SHA256:!SHA384:"
+ "!AESGCM:!3DES:"
// Order some ciphers backwards by strength.
"ALL:-CHACHA20:-AES256:-AES128:-ALL:"
// Select ECDHE ones and sort them by strength. Ties should resolve
@@ -286,15 +289,15 @@
},
// SSLv3 matches everything that existed before TLS 1.2.
{
- "AES128-SHA:AES128-SHA256:!SSLv3",
+ "AES128-SHA:ECDHE-RSA-AES128-GCM-SHA256:!SSLv3",
{
- {TLS1_CK_RSA_WITH_AES_128_SHA256, 0},
+ {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
},
false,
},
// TLSv1.2 matches everything added in TLS 1.2.
{
- "AES128-SHA:AES128-SHA256:!TLSv1.2",
+ "AES128-SHA:ECDHE-RSA-AES128-GCM-SHA256:!TLSv1.2",
{
{TLS1_CK_RSA_WITH_AES_128_SHA, 0},
},
@@ -303,21 +306,21 @@
// The two directives have no intersection. But each component is valid, so
// even in strict mode it is accepted.
{
- "AES128-SHA:AES128-SHA256:!TLSv1.2+SSLv3",
+ "AES128-SHA:ECDHE-RSA-AES128-GCM-SHA256:!TLSv1.2+SSLv3",
{
{TLS1_CK_RSA_WITH_AES_128_SHA, 0},
- {TLS1_CK_RSA_WITH_AES_128_SHA256, 0},
+ {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
},
false,
},
// Spaces, semi-colons and commas are separators.
{
- "AES128-SHA: AES128-SHA256 AES256-SHA ,AES256-SHA256 ; AES128-GCM-SHA256",
+ "AES128-SHA: ECDHE-RSA-AES128-GCM-SHA256 AES256-SHA ,ECDHE-ECDSA-AES128-GCM-SHA256 ; AES128-GCM-SHA256",
{
{TLS1_CK_RSA_WITH_AES_128_SHA, 0},
- {TLS1_CK_RSA_WITH_AES_128_SHA256, 0},
+ {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
{TLS1_CK_RSA_WITH_AES_256_SHA, 0},
- {TLS1_CK_RSA_WITH_AES_256_SHA256, 0},
+ {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0},
{TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0},
},
// …but not in strict mode.
@@ -494,7 +497,7 @@
ASSERT_TRUE(ctx);
ASSERT_TRUE(SSL_CTX_set1_curves_list(ctx.get(), t.rule));
- ASSERT_EQ(t.expected.size(), ctx->supported_group_list_len);
+ ASSERT_EQ(t.expected.size(), ctx->supported_group_list.size());
for (size_t i = 0; i < t.expected.size(); i++) {
EXPECT_EQ(t.expected[i], ctx->supported_group_list[i]);
}
@@ -864,22 +867,22 @@
NID_md5_sha1,
},
{
- TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
- "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+ TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
NID_aes_128_cbc,
- NID_sha256,
+ NID_sha1,
NID_kx_ecdhe,
NID_auth_rsa,
- NID_sha256,
+ NID_md5_sha1,
},
{
- TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
- "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+ TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
NID_aes_256_cbc,
- NID_sha384,
+ NID_sha1,
NID_kx_ecdhe,
NID_auth_rsa,
- NID_sha384,
+ NID_md5_sha1,
},
{
TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
@@ -987,28 +990,20 @@
if (!ssl_ctx) {
return nullptr;
}
+ // Use a garbage ticket.
+ std::vector<uint8_t> ticket(ticket_len, 'a');
bssl::UniquePtr<SSL_SESSION> session(
SSL_SESSION_from_bytes(der.data(), der.size(), ssl_ctx.get()));
- if (!session) {
+ if (!session ||
+ !SSL_SESSION_set_protocol_version(session.get(), version) ||
+ !SSL_SESSION_set_ticket(session.get(), ticket.data(), ticket.size())) {
return nullptr;
}
-
- session->ssl_version = version;
-
- // Swap out the ticket for a garbage one.
- OPENSSL_free(session->tlsext_tick);
- session->tlsext_tick = reinterpret_cast<uint8_t*>(OPENSSL_malloc(ticket_len));
- if (session->tlsext_tick == nullptr) {
- return nullptr;
- }
- OPENSSL_memset(session->tlsext_tick, 'a', ticket_len);
- session->tlsext_ticklen = ticket_len;
-
// Fix up the timeout.
#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
- session->time = 1234;
+ SSL_SESSION_set_time(session.get(), 1234);
#else
- session->time = time(NULL);
+ SSL_SESSION_set_time(session.get(), time(nullptr));
#endif
return session;
}
@@ -1331,9 +1326,7 @@
bssl::UniquePtr<STACK_OF(X509_NAME)> stack(sk_X509_NAME_new_null());
ASSERT_TRUE(stack);
-
- ASSERT_TRUE(sk_X509_NAME_push(stack.get(), name_dup.get()));
- name_dup.release();
+ ASSERT_TRUE(PushToStack(stack.get(), std::move(name_dup)));
// |SSL_set_client_CA_list| takes ownership.
SSL_set_client_CA_list(ssl.get(), stack.release());
@@ -1424,9 +1417,11 @@
return nullptr;
}
- ret->session_id_length = SSL3_SSL_SESSION_ID_LENGTH;
- OPENSSL_memset(ret->session_id, 0, ret->session_id_length);
- OPENSSL_memcpy(ret->session_id, &number, sizeof(number));
+ uint8_t id[SSL3_SSL_SESSION_ID_LENGTH] = {0};
+ OPENSSL_memcpy(id, &number, sizeof(number));
+ if (!SSL_SESSION_set1_id(ret.get(), id, sizeof(id))) {
+ return nullptr;
+ }
return ret;
}
@@ -1538,7 +1533,8 @@
bssl::UniquePtr<SSL> *out_server,
SSL_CTX *client_ctx, SSL_CTX *server_ctx,
const ClientConfig &config = ClientConfig(),
- bool do_handshake = true) {
+ bool do_handshake = true,
+ bool shed_handshake_config = true) {
bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
if (!client || !server) {
return false;
@@ -1562,6 +1558,9 @@
SSL_set_bio(client.get(), bio1, bio1);
SSL_set_bio(server.get(), bio2, bio2);
+ SSL_set_shed_handshake_config(client.get(), shed_handshake_config);
+ SSL_set_shed_handshake_config(server.get(), shed_handshake_config);
+
if (do_handshake && !CompleteHandshakes(client.get(), server.get())) {
return false;
}
@@ -1608,7 +1607,8 @@
bool Connect(const ClientConfig &config = ClientConfig()) {
return ConnectClientAndServer(&client_, &server_, client_ctx_.get(),
- server_ctx_.get(), config);
+ server_ctx_.get(), config, true,
+ shed_handshake_config_);
}
uint16_t version() const { return GetParam().version; }
@@ -1617,6 +1617,7 @@
return GetParam().ssl_method == VersionParam::is_dtls;
}
+ bool shed_handshake_config_ = true;
bssl::UniquePtr<SSL> client_, server_;
bssl::UniquePtr<SSL_CTX> server_ctx_, client_ctx_;
bssl::UniquePtr<X509> cert_;
@@ -1719,7 +1720,7 @@
bssl::SSL_SESSION_dup(session0, SSL_SESSION_DUP_ALL);
ASSERT_TRUE(session1);
- session1->not_resumable = 0;
+ session1->not_resumable = false;
uint8_t *s0_bytes, *s1_bytes;
size_t s0_len, s1_len;
@@ -1943,9 +1944,12 @@
EXPECT_FALSE(peer);
SSL_SESSION *session = SSL_get_session(server_.get());
- EXPECT_TRUE(session->peer_sha256_valid);
+ EXPECT_TRUE(SSL_SESSION_has_peer_sha256(session));
- EXPECT_EQ(Bytes(cert_sha256), Bytes(session->peer_sha256));
+ const uint8_t *peer_sha256;
+ size_t peer_sha256_len;
+ SSL_SESSION_get0_peer_sha256(session, &peer_sha256, &peer_sha256_len);
+ EXPECT_EQ(Bytes(cert_sha256), Bytes(peer_sha256, peer_sha256_len));
}
// Tests that our ClientHellos do not change unexpectedly. These are purely
@@ -1956,13 +1960,6 @@
uint16_t max_version;
std::vector<uint8_t> expected;
} kTests[] = {
- {SSL3_VERSION,
- {0x16, 0x03, 0x00, 0x00, 0x3b, 0x01, 0x00, 0x00, 0x37, 0x03, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x10, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x0a, 0xc0, 0x14, 0x00,
- 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x00, 0xff, 0x01, 0x00}},
{TLS1_VERSION,
{0x16, 0x03, 0x01, 0x00, 0x5a, 0x01, 0x00, 0x00, 0x56, 0x03, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1982,14 +1979,13 @@
0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18}},
{TLS1_2_VERSION,
- {0x16, 0x03, 0x01, 0x00, 0x8e, 0x01, 0x00, 0x00, 0x8a, 0x03, 0x03, 0x00,
+ {0x16, 0x03, 0x01, 0x00, 0x82, 0x01, 0x00, 0x00, 0x7e, 0x03, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xcc, 0xa9,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xcc, 0xa9,
0xcc, 0xa8, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xc0, 0x09,
- 0xc0, 0x23, 0xc0, 0x13, 0xc0, 0x27, 0xc0, 0x0a, 0xc0, 0x24, 0xc0, 0x14,
- 0xc0, 0x28, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x35,
- 0x00, 0x3d, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x37, 0xff, 0x01, 0x00, 0x01,
+ 0xc0, 0x13, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f,
+ 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x37, 0xff, 0x01, 0x00, 0x01,
0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,
0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08,
0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x0b, 0x00,
@@ -2007,8 +2003,6 @@
// Our default cipher list varies by CPU capabilities, so manually place the
// ChaCha20 ciphers in front.
const char *cipher_list = "CHACHA20:ALL";
- // SSLv3 is off by default.
- ASSERT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), SSL3_VERSION));
ASSERT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), t.max_version));
ASSERT_TRUE(SSL_CTX_set_strict_cipher_list(ctx.get(), cipher_list));
@@ -2247,12 +2241,15 @@
}
static bool GetServerTicketTime(long *out, const SSL_SESSION *session) {
- if (session->tlsext_ticklen < 16 + 16 + SHA256_DIGEST_LENGTH) {
+ const uint8_t *ticket;
+ size_t ticket_len;
+ SSL_SESSION_get0_ticket(session, &ticket, &ticket_len);
+ if (ticket_len < 16 + 16 + SHA256_DIGEST_LENGTH) {
return false;
}
- const uint8_t *ciphertext = session->tlsext_tick + 16 + 16;
- size_t len = session->tlsext_ticklen - 16 - 16 - SHA256_DIGEST_LENGTH;
+ const uint8_t *ciphertext = ticket + 16 + 16;
+ size_t len = ticket_len - 16 - 16 - SHA256_DIGEST_LENGTH;
std::unique_ptr<uint8_t[]> plaintext(new uint8_t[len]);
#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
@@ -2260,7 +2257,7 @@
OPENSSL_memcpy(plaintext.get(), ciphertext, len);
#else
static const uint8_t kZeros[16] = {0};
- const uint8_t *iv = session->tlsext_tick + 16;
+ const uint8_t *iv = ticket + 16;
bssl::ScopedEVP_CIPHER_CTX ctx;
int len1, len2;
if (!EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_cbc(), nullptr, kZeros, iv) ||
@@ -2282,7 +2279,7 @@
return false;
}
- *out = server_session->time;
+ *out = SSL_SESSION_get_time(server_session.get());
return true;
}
@@ -2341,11 +2338,6 @@
session.get(),
false /* expect session not reused */));
- // SSL 3.0 cannot renew sessions.
- if (version() == SSL3_VERSION) {
- continue;
- }
-
// Renew the session 10 seconds before expiration.
time_t new_start_time = kStartTime + timeout - 10;
g_current_time.tv_sec = new_start_time;
@@ -2361,7 +2353,7 @@
if (server_test) {
ASSERT_TRUE(GetServerTicketTime(&session_time, new_session.get()));
} else {
- session_time = new_session->time;
+ session_time = SSL_SESSION_get_time(new_session.get());
}
ASSERT_EQ(session_time, g_current_time.tv_sec);
@@ -2428,13 +2420,8 @@
}
TEST_P(SSLVersionTest, DefaultTicketKeyRotation) {
- if (GetParam().version == SSL3_VERSION) {
- return;
- }
-
static const time_t kStartTime = 1001;
g_current_time.tv_sec = kStartTime;
- uint8_t ticket_key[kTicketKeyLen];
// We use session reuse as a proxy for ticket decryption success, hence
// disable session timeouts.
@@ -2448,7 +2435,9 @@
SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_OFF);
- // Initialize ticket_key with the current key.
+ // Initialize ticket_key with the current key and check that it was
+ // initialized to something, not all zeros.
+ uint8_t ticket_key[kTicketKeyLen] = {0};
TRACED_CALL(ExpectTicketKeyChanged(server_ctx_.get(), ticket_key,
true /* changed */));
@@ -2501,11 +2490,6 @@
}
TEST_P(SSLVersionTest, SNICallback) {
- // SSL 3.0 lacks extensions.
- if (version() == SSL3_VERSION) {
- return;
- }
-
bssl::UniquePtr<X509> cert2 = GetECDSATestCertificate();
ASSERT_TRUE(cert2);
bssl::UniquePtr<EVP_PKEY> key2 = GetECDSATestKey();
@@ -2611,12 +2595,13 @@
EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), 0));
EXPECT_EQ(TLS1_VERSION, ctx->conf_min_version);
- // SSL 3.0 and TLS 1.3 are available, but not by default.
- EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), SSL3_VERSION));
- EXPECT_EQ(SSL3_VERSION, ctx->conf_min_version);
+ // TLS 1.3 is available, but not by default.
EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_VERSION));
EXPECT_EQ(TLS1_3_VERSION, ctx->conf_max_version);
+ // SSL 3.0 is not available.
+ EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), SSL3_VERSION));
+
// TLS1_3_DRAFT_VERSION is not an API-level version.
EXPECT_FALSE(
SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_DRAFT23_VERSION));
@@ -2647,8 +2632,6 @@
static const char *GetVersionName(uint16_t version) {
switch (version) {
- case SSL3_VERSION:
- return "SSLv3";
case TLS1_VERSION:
return "TLSv1";
case TLS1_1_VERSION:
@@ -2689,11 +2672,6 @@
// Tests that that |SSL_get_pending_cipher| is available during the ALPN
// selection callback.
TEST_P(SSLVersionTest, ALPNCipherAvailable) {
- // SSL 3.0 lacks extensions.
- if (version() == SSL3_VERSION) {
- return;
- }
-
ASSERT_TRUE(UseCertAndKey(client_ctx_.get()));
static const uint8_t kALPNProtos[] = {0x03, 'f', 'o', 'o'};
@@ -2729,6 +2707,7 @@
return;
}
+ shed_handshake_config_ = false;
ASSERT_TRUE(Connect());
EXPECT_FALSE(SSL_session_reused(client_.get()));
@@ -2746,6 +2725,25 @@
EXPECT_TRUE(SSL_session_reused(server_.get()));
}
+TEST_P(SSLVersionTest, SSLClearFailsWithShedding) {
+ shed_handshake_config_ = false;
+ ASSERT_TRUE(Connect());
+ ASSERT_TRUE(CompleteHandshakes(client_.get(), server_.get()));
+
+ // Reset everything.
+ ASSERT_TRUE(SSL_clear(client_.get()));
+ ASSERT_TRUE(SSL_clear(server_.get()));
+
+ // Now enable shedding, and connect a second time.
+ shed_handshake_config_ = true;
+ ASSERT_TRUE(Connect());
+ ASSERT_TRUE(CompleteHandshakes(client_.get(), server_.get()));
+
+ // |SSL_clear| should now fail.
+ ASSERT_FALSE(SSL_clear(client_.get()));
+ ASSERT_FALSE(SSL_clear(server_.get()));
+}
+
static bool ChainsEqual(STACK_OF(X509) * chain,
const std::vector<X509 *> &expected) {
if (sk_X509_num(chain) != expected.size()) {
@@ -2990,11 +2988,6 @@
}
TEST_P(SSLVersionTest, GetServerName) {
- // No extensions in SSL 3.0.
- if (version() == SSL3_VERSION) {
- return;
- }
-
ClientConfig config;
config.servername = "host1";
@@ -3245,8 +3238,7 @@
bssl::UniquePtr<STACK_OF(CRYPTO_BUFFER)> ca_names(
sk_CRYPTO_BUFFER_new_null());
ASSERT_TRUE(ca_names);
- ASSERT_TRUE(sk_CRYPTO_BUFFER_push(ca_names.get(), ca_name.get()));
- ca_name.release();
+ ASSERT_TRUE(PushToStack(ca_names.get(), std::move(ca_name)));
SSL_CTX_set0_client_CAs(server_ctx.get(), ca_names.release());
// Configure client and server to accept all certificates.
@@ -3265,7 +3257,7 @@
SSL_CTX_set_cert_cb(
client_ctx.get(),
[](SSL *ssl, void *arg) -> int {
- STACK_OF(CRYPTO_BUFFER) *peer_names =
+ const STACK_OF(CRYPTO_BUFFER) *peer_names =
SSL_get0_server_requested_CAs(ssl);
EXPECT_EQ(1u, sk_CRYPTO_BUFFER_num(peer_names));
CRYPTO_BUFFER *peer_name = sk_CRYPTO_BUFFER_value(peer_names, 0);
@@ -3926,6 +3918,69 @@
EXPECT_TRUE(SSL_is_signature_algorithm_rsa_pss(SSL_SIGN_RSA_PSS_RSAE_SHA384));
}
+static int XORCompressFunc(SSL *ssl, CBB *out, const uint8_t *in,
+ size_t in_len) {
+ for (size_t i = 0; i < in_len; i++) {
+ if (!CBB_add_u8(out, in[i] ^ 0x55)) {
+ return 0;
+ }
+ }
+
+ SSL_set_app_data(ssl, XORCompressFunc);
+
+ return 1;
+}
+
+static int XORDecompressFunc(SSL *ssl, CRYPTO_BUFFER **out,
+ size_t uncompressed_len, const uint8_t *in,
+ size_t in_len) {
+ if (in_len != uncompressed_len) {
+ return 0;
+ }
+
+ uint8_t *data;
+ *out = CRYPTO_BUFFER_alloc(&data, uncompressed_len);
+ if (*out == nullptr) {
+ return 0;
+ }
+
+ for (size_t i = 0; i < in_len; i++) {
+ data[i] = in[i] ^ 0x55;
+ }
+
+ SSL_set_app_data(ssl, XORDecompressFunc);
+
+ return 1;
+}
+
+TEST(SSLTest, CertCompression) {
+ 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));
+ ASSERT_TRUE(SSL_CTX_add_cert_compression_alg(
+ client_ctx.get(), 0x1234, XORCompressFunc, XORDecompressFunc));
+ ASSERT_TRUE(SSL_CTX_add_cert_compression_alg(
+ server_ctx.get(), 0x1234, XORCompressFunc, XORDecompressFunc));
+
+ bssl::UniquePtr<SSL> client, server;
+ ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get()));
+
+ EXPECT_TRUE(SSL_get_app_data(client.get()) == XORDecompressFunc);
+ EXPECT_TRUE(SSL_get_app_data(server.get()) == XORCompressFunc);
+}
+
void MoveBIOs(SSL *dest, SSL *src) {
BIO *rbio = SSL_get_rbio(src);
BIO_up_ref(rbio);
@@ -4064,6 +4119,294 @@
EXPECT_EQ(43, byte);
}
+static std::string SigAlgsToString(Span<const uint16_t> sigalgs) {
+ std::string ret = "{";
+
+ for (uint16_t v : sigalgs) {
+ if (ret.size() > 1) {
+ ret += ", ";
+ }
+
+ char buf[8];
+ snprintf(buf, sizeof(buf) - 1, "0x%02x", v);
+ buf[sizeof(buf)-1] = 0;
+ ret += std::string(buf);
+ }
+
+ ret += "}";
+ return ret;
+}
+
+void ExpectSigAlgsEqual(Span<const uint16_t> expected,
+ Span<const uint16_t> actual) {
+ bool matches = false;
+ if (expected.size() == actual.size()) {
+ matches = true;
+
+ for (size_t i = 0; i < expected.size(); i++) {
+ if (expected[i] != actual[i]) {
+ matches = false;
+ break;
+ }
+ }
+ }
+
+ if (!matches) {
+ ADD_FAILURE() << "expected: " << SigAlgsToString(expected)
+ << " got: " << SigAlgsToString(actual);
+ }
+}
+
+TEST(SSLTest, SigAlgs) {
+ static const struct {
+ std::vector<int> input;
+ bool ok;
+ std::vector<uint16_t> expected;
+ } kTests[] = {
+ {{}, true, {}},
+ {{1}, false, {}},
+ {{1, 2, 3}, false, {}},
+ {{NID_sha256, EVP_PKEY_ED25519}, false, {}},
+ {{NID_sha256, EVP_PKEY_RSA, NID_sha256, EVP_PKEY_RSA}, false, {}},
+
+ {{NID_sha256, EVP_PKEY_RSA}, true, {SSL_SIGN_RSA_PKCS1_SHA256}},
+ {{NID_sha512, EVP_PKEY_RSA}, true, {SSL_SIGN_RSA_PKCS1_SHA512}},
+ {{NID_sha256, EVP_PKEY_RSA_PSS}, true, {SSL_SIGN_RSA_PSS_RSAE_SHA256}},
+ {{NID_undef, EVP_PKEY_ED25519}, true, {SSL_SIGN_ED25519}},
+ {{NID_undef, EVP_PKEY_ED25519, NID_sha384, EVP_PKEY_EC},
+ true,
+ {SSL_SIGN_ED25519, SSL_SIGN_ECDSA_SECP384R1_SHA384}},
+ };
+
+ UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+
+ unsigned n = 1;
+ for (const auto &test : kTests) {
+ SCOPED_TRACE(n++);
+
+ const bool ok =
+ SSL_CTX_set1_sigalgs(ctx.get(), test.input.data(), test.input.size());
+ EXPECT_EQ(ok, test.ok);
+
+ if (!ok) {
+ ERR_clear_error();
+ }
+
+ if (!test.ok) {
+ continue;
+ }
+
+ ExpectSigAlgsEqual(test.expected, ctx->cert->sigalgs);
+ }
+}
+
+TEST(SSLTest, SigAlgsList) {
+ static const struct {
+ const char *input;
+ bool ok;
+ std::vector<uint16_t> expected;
+ } kTests[] = {
+ {"", false, {}},
+ {":", false, {}},
+ {"+", false, {}},
+ {"RSA", false, {}},
+ {"RSA+", false, {}},
+ {"RSA+SHA256:", false, {}},
+ {":RSA+SHA256:", false, {}},
+ {":RSA+SHA256+:", false, {}},
+ {"!", false, {}},
+ {"\x01", false, {}},
+ {"RSA+SHA256:RSA+SHA384:RSA+SHA256", false, {}},
+ {"RSA-PSS+SHA256:rsa_pss_rsae_sha256", false, {}},
+
+ {"RSA+SHA256", true, {SSL_SIGN_RSA_PKCS1_SHA256}},
+ {"RSA+SHA256:ed25519",
+ true,
+ {SSL_SIGN_RSA_PKCS1_SHA256, SSL_SIGN_ED25519}},
+ {"ECDSA+SHA256:RSA+SHA512",
+ true,
+ {SSL_SIGN_ECDSA_SECP256R1_SHA256, SSL_SIGN_RSA_PKCS1_SHA512}},
+ {"ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256",
+ true,
+ {SSL_SIGN_ECDSA_SECP256R1_SHA256, SSL_SIGN_RSA_PSS_RSAE_SHA256}},
+ {"RSA-PSS+SHA256", true, {SSL_SIGN_RSA_PSS_RSAE_SHA256}},
+ {"PSS+SHA256", true, {SSL_SIGN_RSA_PSS_RSAE_SHA256}},
+ };
+
+ UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+
+ unsigned n = 1;
+ for (const auto &test : kTests) {
+ SCOPED_TRACE(n++);
+
+ const bool ok = SSL_CTX_set1_sigalgs_list(ctx.get(), test.input);
+ EXPECT_EQ(ok, test.ok);
+
+ if (!ok) {
+ if (test.ok) {
+ ERR_print_errors_fp(stderr);
+ }
+ ERR_clear_error();
+ }
+
+ if (!test.ok) {
+ continue;
+ }
+
+ ExpectSigAlgsEqual(test.expected, ctx->cert->sigalgs);
+ }
+}
+
+TEST_P(SSLVersionTest, VerifyBeforeCertRequest) {
+ // Configure the server to request client certificates.
+ SSL_CTX_set_custom_verify(
+ server_ctx_.get(), SSL_VERIFY_PEER,
+ [](SSL *ssl, uint8_t *out_alert) { return ssl_verify_ok; });
+
+ // Configure the client to reject the server certificate.
+ SSL_CTX_set_custom_verify(
+ client_ctx_.get(), SSL_VERIFY_PEER,
+ [](SSL *ssl, uint8_t *out_alert) { return ssl_verify_invalid; });
+
+ // cert_cb should not be called. Verification should fail first.
+ SSL_CTX_set_cert_cb(client_ctx_.get(),
+ [](SSL *ssl, void *arg) {
+ ADD_FAILURE() << "cert_cb unexpectedly called";
+ return 0;
+ },
+ nullptr);
+
+ bssl::UniquePtr<SSL> client, server;
+ EXPECT_FALSE(ConnectClientAndServer(&client, &server, client_ctx_.get(),
+ server_ctx_.get()));
+}
+
+// These tests test multi-threaded behavior. They are intended to run with
+// ThreadSanitizer.
+#if !defined(OPENSSL_NO_THREADS)
+TEST_P(SSLVersionTest, SessionCacheThreads) {
+ SSL_CTX_set_options(server_ctx_.get(), SSL_OP_NO_TICKET);
+ SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
+ SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH);
+
+ if (version() == TLS1_3_VERSION) {
+ // Our TLS 1.3 implementation does not support stateful resumption.
+ ASSERT_FALSE(CreateClientSession(client_ctx_.get(), server_ctx_.get()));
+ return;
+ }
+
+ // Establish two client sessions to test with.
+ bssl::UniquePtr<SSL_SESSION> session1 =
+ CreateClientSession(client_ctx_.get(), server_ctx_.get());
+ ASSERT_TRUE(session1);
+ bssl::UniquePtr<SSL_SESSION> session2 =
+ CreateClientSession(client_ctx_.get(), server_ctx_.get());
+ ASSERT_TRUE(session2);
+
+ auto connect_with_session = [&](SSL_SESSION *session) {
+ ClientConfig config;
+ config.session = session;
+ UniquePtr<SSL> client, server;
+ EXPECT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(),
+ server_ctx_.get(), config));
+ };
+
+ // Resume sessions in parallel with establishing new ones.
+ {
+ std::vector<std::thread> threads;
+ threads.emplace_back([&] { connect_with_session(nullptr); });
+ threads.emplace_back([&] { connect_with_session(nullptr); });
+ threads.emplace_back([&] { connect_with_session(session1.get()); });
+ threads.emplace_back([&] { connect_with_session(session1.get()); });
+ threads.emplace_back([&] { connect_with_session(session2.get()); });
+ threads.emplace_back([&] { connect_with_session(session2.get()); });
+ for (auto &thread : threads) {
+ thread.join();
+ }
+ }
+
+ // Hit the maximum session cache size across multiple threads
+ size_t limit = SSL_CTX_sess_number(server_ctx_.get()) + 2;
+ SSL_CTX_sess_set_cache_size(server_ctx_.get(), limit);
+ {
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 4; i++) {
+ threads.emplace_back([&]() {
+ connect_with_session(nullptr);
+ EXPECT_LE(SSL_CTX_sess_number(server_ctx_.get()), limit);
+ });
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+ EXPECT_EQ(SSL_CTX_sess_number(server_ctx_.get()), limit);
+ }
+}
+
+TEST_P(SSLVersionTest, SessionTicketThreads) {
+ for (bool renew_ticket : {false, true}) {
+ SCOPED_TRACE(renew_ticket);
+ ResetContexts();
+ SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
+ SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH);
+ if (renew_ticket) {
+ SSL_CTX_set_tlsext_ticket_key_cb(server_ctx_.get(), RenewTicketCallback);
+ }
+
+ // Establish two client sessions to test with.
+ bssl::UniquePtr<SSL_SESSION> session1 =
+ CreateClientSession(client_ctx_.get(), server_ctx_.get());
+ ASSERT_TRUE(session1);
+ bssl::UniquePtr<SSL_SESSION> session2 =
+ CreateClientSession(client_ctx_.get(), server_ctx_.get());
+ ASSERT_TRUE(session2);
+
+ auto connect_with_session = [&](SSL_SESSION *session) {
+ ClientConfig config;
+ config.session = session;
+ UniquePtr<SSL> client, server;
+ EXPECT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(),
+ server_ctx_.get(), config));
+ };
+
+ // Resume sessions in parallel with establishing new ones.
+ {
+ std::vector<std::thread> threads;
+ threads.emplace_back([&] { connect_with_session(nullptr); });
+ threads.emplace_back([&] { connect_with_session(nullptr); });
+ threads.emplace_back([&] { connect_with_session(session1.get()); });
+ threads.emplace_back([&] { connect_with_session(session1.get()); });
+ threads.emplace_back([&] { connect_with_session(session2.get()); });
+ threads.emplace_back([&] { connect_with_session(session2.get()); });
+ for (auto &thread : threads) {
+ thread.join();
+ }
+ }
+ }
+}
+
+// SSL_CTX_get0_certificate needs to lock internally. Test this works.
+TEST(SSLTest, GetCertificateThreads) {
+ bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+ ASSERT_TRUE(ctx);
+ bssl::UniquePtr<X509> cert = GetTestCertificate();
+ ASSERT_TRUE(cert);
+ ASSERT_TRUE(SSL_CTX_use_certificate(ctx.get(), cert.get()));
+
+ // Existing code expects |SSL_CTX_get0_certificate| to be callable from two
+ // threads concurrently. It originally was an immutable operation. Now we
+ // implement it with a thread-safe cache, so it is worth testing.
+ X509 *cert2_thread;
+ std::thread thread(
+ [&] { cert2_thread = SSL_CTX_get0_certificate(ctx.get()); });
+ X509 *cert2 = SSL_CTX_get0_certificate(ctx.get());
+ thread.join();
+
+ EXPECT_EQ(cert2, cert2_thread);
+ EXPECT_EQ(0, X509_cmp(cert.get(), cert2));
+}
+#endif
+
// TODO(davidben): Convert this file to GTest properly.
TEST(SSLTest, AllTests) {
if (!TestSSL_SESSIONEncoding(kOpenSSLSession) ||
@@ -4079,7 +4422,7 @@
!TestPaddingExtension(TLS1_3_VERSION, TLS1_2_VERSION) ||
// Test the padding extension at TLS 1.3 with a TLS 1.3 session, so there
// will be a PSK binder after the padding extension.
- !TestPaddingExtension(TLS1_3_VERSION, TLS1_3_DRAFT23_VERSION)) {
+ !TestPaddingExtension(TLS1_3_VERSION, TLS1_3_VERSION)) {
ADD_FAILURE() << "Tests failed";
}
}
diff --git a/src/ssl/ssl_transcript.cc b/src/ssl/ssl_transcript.cc
index 345f9d3..24b86bf 100644
--- a/src/ssl/ssl_transcript.cc
+++ b/src/ssl/ssl_transcript.cc
@@ -135,18 +135,9 @@
#include <openssl/ssl.h>
-#include <assert.h>
-#include <string.h>
-
#include <openssl/buf.h>
#include <openssl/digest.h>
-#include <openssl/err.h>
-#include <openssl/mem.h>
-#include <openssl/md5.h>
-#include <openssl/nid.h>
-#include <openssl/sha.h>
-#include "../crypto/internal.h"
#include "internal.h"
@@ -163,7 +154,6 @@
}
hash_.Reset();
- md5_.Reset();
return true;
}
@@ -180,17 +170,6 @@
bool SSLTranscript::InitHash(uint16_t version, const SSL_CIPHER *cipher) {
const EVP_MD *md = ssl_get_handshake_digest(version, cipher);
-
- // To support SSL 3.0's Finished and CertificateVerify constructions,
- // EVP_md5_sha1() is split into MD5 and SHA-1 halves. When SSL 3.0 is removed,
- // we can simplify this.
- if (md == EVP_md5_sha1()) {
- if (!InitDigestWithData(md5_.get(), EVP_md5(), buffer_.get())) {
- return false;
- }
- md = EVP_sha1();
- }
-
return InitDigestWithData(hash_.get(), md, buffer_.get());
}
@@ -203,9 +182,6 @@
}
const EVP_MD *SSLTranscript::Digest() const {
- if (EVP_MD_CTX_md(md5_.get()) != nullptr) {
- return EVP_md5_sha1();
- }
return EVP_MD_CTX_md(hash_.get());
}
@@ -244,146 +220,40 @@
if (EVP_MD_CTX_md(hash_.get()) != NULL) {
EVP_DigestUpdate(hash_.get(), in.data(), in.size());
}
- if (EVP_MD_CTX_md(md5_.get()) != NULL) {
- EVP_DigestUpdate(md5_.get(), in.data(), in.size());
- }
return true;
}
bool SSLTranscript::GetHash(uint8_t *out, size_t *out_len) {
ScopedEVP_MD_CTX ctx;
- unsigned md5_len = 0;
- if (EVP_MD_CTX_md(md5_.get()) != NULL) {
- if (!EVP_MD_CTX_copy_ex(ctx.get(), md5_.get()) ||
- !EVP_DigestFinal_ex(ctx.get(), out, &md5_len)) {
- return false;
- }
- }
-
unsigned len;
if (!EVP_MD_CTX_copy_ex(ctx.get(), hash_.get()) ||
- !EVP_DigestFinal_ex(ctx.get(), out + md5_len, &len)) {
+ !EVP_DigestFinal_ex(ctx.get(), out, &len)) {
return false;
}
-
- *out_len = md5_len + len;
- return true;
-}
-
-static bool SSL3HandshakeMAC(const SSL_SESSION *session,
- const EVP_MD_CTX *ctx_template, const char *sender,
- size_t sender_len, uint8_t *p, size_t *out_len) {
- ScopedEVP_MD_CTX ctx;
- if (!EVP_MD_CTX_copy_ex(ctx.get(), ctx_template)) {
- OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
- return false;
- }
-
- static const uint8_t kPad1[48] = {
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- };
-
- static const uint8_t kPad2[48] = {
- 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
- 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
- 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
- 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
- };
-
- size_t n = EVP_MD_CTX_size(ctx.get());
-
- size_t npad = (48 / n) * n;
- EVP_DigestUpdate(ctx.get(), sender, sender_len);
- EVP_DigestUpdate(ctx.get(), session->master_key, session->master_key_length);
- EVP_DigestUpdate(ctx.get(), kPad1, npad);
- unsigned md_buf_len;
- uint8_t md_buf[EVP_MAX_MD_SIZE];
- EVP_DigestFinal_ex(ctx.get(), md_buf, &md_buf_len);
-
- if (!EVP_DigestInit_ex(ctx.get(), EVP_MD_CTX_md(ctx.get()), NULL)) {
- OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
- return false;
- }
- EVP_DigestUpdate(ctx.get(), session->master_key, session->master_key_length);
- EVP_DigestUpdate(ctx.get(), kPad2, npad);
- EVP_DigestUpdate(ctx.get(), md_buf, md_buf_len);
- unsigned len;
- EVP_DigestFinal_ex(ctx.get(), p, &len);
-
*out_len = len;
return true;
}
-bool SSLTranscript::GetSSL3CertVerifyHash(uint8_t *out, size_t *out_len,
- const SSL_SESSION *session,
- uint16_t signature_algorithm) {
- if (Digest() != EVP_md5_sha1()) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return false;
- }
-
- if (signature_algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) {
- size_t md5_len, len;
- if (!SSL3HandshakeMAC(session, md5_.get(), NULL, 0, out, &md5_len) ||
- !SSL3HandshakeMAC(session, hash_.get(), NULL, 0, out + md5_len, &len)) {
- return false;
- }
- *out_len = md5_len + len;
- return true;
- }
-
- if (signature_algorithm == SSL_SIGN_ECDSA_SHA1) {
- return SSL3HandshakeMAC(session, hash_.get(), NULL, 0, out, out_len);
- }
-
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return false;
-}
-
bool SSLTranscript::GetFinishedMAC(uint8_t *out, size_t *out_len,
const SSL_SESSION *session,
bool from_server) {
- if (session->ssl_version == SSL3_VERSION) {
- if (Digest() != EVP_md5_sha1()) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return false;
- }
-
- const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST
- : SSL3_MD_CLIENT_FINISHED_CONST;
- const size_t sender_len = 4;
- size_t md5_len, len;
- if (!SSL3HandshakeMAC(session, md5_.get(), sender, sender_len, out,
- &md5_len) ||
- !SSL3HandshakeMAC(session, hash_.get(), sender, sender_len,
- out + md5_len, &len)) {
- return false;
- }
-
- *out_len = md5_len + len;
- return true;
- }
-
static const char kClientLabel[] = "client finished";
static const char kServerLabel[] = "server finished";
auto label = from_server
? MakeConstSpan(kServerLabel, sizeof(kServerLabel) - 1)
: MakeConstSpan(kClientLabel, sizeof(kClientLabel) - 1);
- uint8_t digests[EVP_MAX_MD_SIZE];
- size_t digests_len;
- if (!GetHash(digests, &digests_len)) {
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ size_t digest_len;
+ if (!GetHash(digest, &digest_len)) {
return false;
}
static const size_t kFinishedLen = 12;
if (!tls1_prf(Digest(), MakeSpan(out, kFinishedLen),
MakeConstSpan(session->master_key, session->master_key_length),
- label, MakeConstSpan(digests, digests_len), {})) {
+ label, MakeConstSpan(digest, digest_len), {})) {
return false;
}
diff --git a/src/ssl/ssl_versions.cc b/src/ssl/ssl_versions.cc
index 73ea26f..6a8143d 100644
--- a/src/ssl/ssl_versions.cc
+++ b/src/ssl/ssl_versions.cc
@@ -27,7 +27,6 @@
bool ssl_protocol_version_from_wire(uint16_t *out, uint16_t version) {
switch (version) {
- case SSL3_VERSION:
case TLS1_VERSION:
case TLS1_1_VERSION:
case TLS1_2_VERSION:
@@ -57,12 +56,11 @@
// decreasing preference.
static const uint16_t kTLSVersions[] = {
- TLS1_3_DRAFT23_VERSION,
TLS1_3_DRAFT28_VERSION,
+ TLS1_3_DRAFT23_VERSION,
TLS1_2_VERSION,
TLS1_1_VERSION,
TLS1_VERSION,
- SSL3_VERSION,
};
static const uint16_t kDTLSVersions[] = {
@@ -81,8 +79,8 @@
}
}
-static bool method_supports_version(const SSL_PROTOCOL_METHOD *method,
- uint16_t version) {
+bool ssl_method_supports_version(const SSL_PROTOCOL_METHOD *method,
+ uint16_t version) {
const uint16_t *versions;
size_t num_versions;
get_method_versions(method, &versions, &num_versions);
@@ -114,9 +112,6 @@
case TLS1_VERSION:
return "TLSv1";
- case SSL3_VERSION:
- return "SSLv3";
-
case DTLS1_VERSION:
return "DTLSv1";
@@ -164,7 +159,7 @@
static bool set_version_bound(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
uint16_t version) {
if (!api_version_to_wire(&version, version) ||
- !method_supports_version(method, version) ||
+ !ssl_method_supports_version(method, version) ||
!ssl_protocol_version_from_wire(out, version)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION);
return false;
@@ -177,7 +172,7 @@
uint16_t version) {
// Zero is interpreted as the default minimum version.
if (version == 0) {
- // SSL 3.0 is disabled by default and TLS 1.0 does not exist in DTLS.
+ // TLS 1.0 does not exist in DTLS.
*out = method->is_dtls ? TLS1_1_VERSION : TLS1_VERSION;
return true;
}
@@ -200,27 +195,26 @@
uint16_t version;
uint32_t flag;
} kProtocolVersions[] = {
- {SSL3_VERSION, SSL_OP_NO_SSLv3},
{TLS1_VERSION, SSL_OP_NO_TLSv1},
{TLS1_1_VERSION, SSL_OP_NO_TLSv1_1},
{TLS1_2_VERSION, SSL_OP_NO_TLSv1_2},
{TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
};
-bool ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+bool ssl_get_version_range(const SSL_HANDSHAKE *hs, uint16_t *out_min_version,
uint16_t *out_max_version) {
// For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but
// DTLS 1.0 should be mapped to TLS 1.1.
- uint32_t options = ssl->options;
- if (SSL_is_dtls(ssl)) {
+ uint32_t options = hs->ssl->options;
+ if (SSL_is_dtls(hs->ssl)) {
options &= ~SSL_OP_NO_TLSv1_1;
if (options & SSL_OP_NO_DTLSv1) {
options |= SSL_OP_NO_TLSv1_1;
}
}
- uint16_t min_version = ssl->conf_min_version;
- uint16_t max_version = ssl->conf_max_version;
+ uint16_t min_version = hs->config->conf_min_version;
+ uint16_t max_version = hs->config->conf_max_version;
// OpenSSL's API for controlling versions entails blacklisting individual
// protocols. This has two problems. First, on the client, the protocol can
@@ -292,24 +286,27 @@
bool ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) {
SSL *const ssl = hs->ssl;
uint16_t protocol_version;
- if (!method_supports_version(ssl->method, version) ||
+ if (!ssl_method_supports_version(ssl->method, version) ||
!ssl_protocol_version_from_wire(&protocol_version, version) ||
hs->min_version > protocol_version ||
protocol_version > hs->max_version) {
return false;
}
- // This logic is part of the TLS 1.3 variants mechanism used in TLS 1.3
- // experimentation. TLS 1.3 variants must match the enabled |tls13_variant|.
- if (protocol_version != TLS1_3_VERSION ||
- (ssl->tls13_variant == tls13_draft28 &&
- version == TLS1_3_DRAFT28_VERSION) ||
- (ssl->tls13_variant == tls13_default &&
- version == TLS1_3_DRAFT23_VERSION)) {
- return true;
+ // If the TLS 1.3 variant is set to |tls13_default|, all variants are enabled,
+ // otherwise only the matching version is enabled.
+ if (protocol_version == TLS1_3_VERSION) {
+ switch (ssl->tls13_variant) {
+ case tls13_draft23:
+ return version == TLS1_3_DRAFT23_VERSION;
+ case tls13_draft28:
+ return version == TLS1_3_DRAFT28_VERSION;
+ case tls13_default:
+ return true;
+ }
}
- return false;
+ return true;
}
bool ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb) {
@@ -373,11 +370,17 @@
}
int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
- return set_min_version(ssl->method, &ssl->conf_min_version, version);
+ if (!ssl->config) {
+ return 0;
+ }
+ return set_min_version(ssl->method, &ssl->config->conf_min_version, version);
}
int SSL_set_max_proto_version(SSL *ssl, uint16_t version) {
- return set_max_version(ssl->method, &ssl->conf_max_version, version);
+ if (!ssl->config) {
+ return 0;
+ }
+ return set_max_version(ssl->method, &ssl->config->conf_max_version, version);
}
int SSL_version(const SSL *ssl) {
diff --git a/src/ssl/ssl_x509.cc b/src/ssl/ssl_x509.cc
index cb35339..ef09589 100644
--- a/src/ssl/ssl_x509.cc
+++ b/src/ssl/ssl_x509.cc
@@ -209,13 +209,10 @@
return 0;
}
- CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain.get(), 0);
- if (!sk_CRYPTO_BUFFER_push(new_chain.get(), leaf)) {
- return 0;
- }
// |leaf| might be NULL if it's a “leafless” chain.
- if (leaf != nullptr) {
- CRYPTO_BUFFER_up_ref(leaf);
+ CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain.get(), 0);
+ if (!PushToStack(new_chain.get(), UpRef(leaf))) {
+ return 0;
}
}
@@ -252,11 +249,10 @@
STACK_OF(CRYPTO_BUFFER) *names) {
for (const CRYPTO_BUFFER *buffer : names) {
const uint8_t *inp = CRYPTO_BUFFER_data(buffer);
- X509_NAME *name = d2i_X509_NAME(NULL, &inp, CRYPTO_BUFFER_len(buffer));
- const int ok = name != NULL && inp == CRYPTO_BUFFER_data(buffer) +
- CRYPTO_BUFFER_len(buffer);
- X509_NAME_free(name);
- if (!ok) {
+ UniquePtr<X509_NAME> name(
+ d2i_X509_NAME(nullptr, &inp, CRYPTO_BUFFER_len(buffer)));
+ if (name == nullptr ||
+ inp != CRYPTO_BUFFER_data(buffer) + CRYPTO_BUFFER_len(buffer)) {
return 0;
}
}
@@ -286,7 +282,7 @@
static int ssl_crypto_x509_session_cache_objects(SSL_SESSION *sess) {
bssl::UniquePtr<STACK_OF(X509)> chain;
- if (sk_CRYPTO_BUFFER_num(sess->certs) > 0) {
+ if (sk_CRYPTO_BUFFER_num(sess->certs.get()) > 0) {
chain.reset(sk_X509_new_null());
if (!chain) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
@@ -295,7 +291,7 @@
}
X509 *leaf = nullptr;
- for (CRYPTO_BUFFER *cert : sess->certs) {
+ for (CRYPTO_BUFFER *cert : sess->certs.get()) {
UniquePtr<X509> x509(X509_parse_from_buffer(cert));
if (!x509) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
@@ -349,7 +345,7 @@
}
static int ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session,
- SSL *ssl,
+ SSL_HANDSHAKE *hs,
uint8_t *out_alert) {
*out_alert = SSL_AD_INTERNAL_ERROR;
STACK_OF(X509) *const cert_chain = session->x509_chain;
@@ -357,9 +353,10 @@
return 0;
}
- X509_STORE *verify_store = ssl->ctx->cert_store;
- if (ssl->cert->verify_store != NULL) {
- verify_store = ssl->cert->verify_store;
+ SSL_CTX *ssl_ctx = hs->ssl->ctx.get();
+ X509_STORE *verify_store = ssl_ctx->cert_store;
+ if (hs->config->cert->verify_store != NULL) {
+ verify_store = hs->config->cert->verify_store;
}
X509 *leaf = sk_X509_value(cert_chain, 0);
@@ -368,8 +365,8 @@
OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
return 0;
}
- if (!X509_STORE_CTX_set_ex_data(ctx.get(),
- SSL_get_ex_data_X509_STORE_CTX_idx(), ssl)) {
+ if (!X509_STORE_CTX_set_ex_data(
+ ctx.get(), SSL_get_ex_data_X509_STORE_CTX_idx(), hs->ssl)) {
return 0;
}
@@ -377,19 +374,20 @@
// context: if its a server it will verify SSL client certificates or vice
// versa.
X509_STORE_CTX_set_default(ctx.get(),
- ssl->server ? "ssl_client" : "ssl_server");
+ hs->ssl->server ? "ssl_client" : "ssl_server");
// Anything non-default in "param" should overwrite anything in the ctx.
- X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(ctx.get()), ssl->param);
+ X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(ctx.get()),
+ hs->config->param);
- if (ssl->verify_callback) {
- X509_STORE_CTX_set_verify_cb(ctx.get(), ssl->verify_callback);
+ if (hs->config->verify_callback) {
+ X509_STORE_CTX_set_verify_cb(ctx.get(), hs->config->verify_callback);
}
int verify_ret;
- if (ssl->ctx->app_verify_callback != NULL) {
+ if (ssl_ctx->app_verify_callback != NULL) {
verify_ret =
- ssl->ctx->app_verify_callback(ctx.get(), ssl->ctx->app_verify_arg);
+ ssl_ctx->app_verify_callback(ctx.get(), ssl_ctx->app_verify_arg);
} else {
verify_ret = X509_verify_cert(ctx.get());
}
@@ -397,7 +395,7 @@
session->verify_result = ctx->error;
// If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result.
- if (verify_ret <= 0 && ssl->verify_mode != SSL_VERIFY_NONE) {
+ if (verify_ret <= 0 && hs->config->verify_mode != SSL_VERIFY_NONE) {
*out_alert = SSL_alert_from_verify_result(ctx->error);
return 0;
}
@@ -411,44 +409,45 @@
hs->cached_x509_ca_names = NULL;
}
-static int ssl_crypto_x509_ssl_new(SSL *ssl) {
- ssl->param = X509_VERIFY_PARAM_new();
- if (ssl->param == NULL) {
+static int ssl_crypto_x509_ssl_new(SSL_HANDSHAKE *hs) {
+ hs->config->param = X509_VERIFY_PARAM_new();
+ if (hs->config->param == NULL) {
return 0;
}
- X509_VERIFY_PARAM_inherit(ssl->param, ssl->ctx->param);
+ X509_VERIFY_PARAM_inherit(hs->config->param, hs->ssl->ctx->param);
return 1;
}
-static void ssl_crypto_x509_ssl_flush_cached_client_CA(SSL *ssl) {
- sk_X509_NAME_pop_free(ssl->cached_x509_client_CA, X509_NAME_free);
- ssl->cached_x509_client_CA = NULL;
+static void ssl_crypto_x509_ssl_flush_cached_client_CA(SSL_CONFIG *cfg) {
+ sk_X509_NAME_pop_free(cfg->cached_x509_client_CA, X509_NAME_free);
+ cfg->cached_x509_client_CA = NULL;
}
-static void ssl_crypto_x509_ssl_free(SSL *ssl) {
- ssl_crypto_x509_ssl_flush_cached_client_CA(ssl);
- X509_VERIFY_PARAM_free(ssl->param);
+static void ssl_crypto_x509_ssl_config_free(SSL_CONFIG *cfg) {
+ sk_X509_NAME_pop_free(cfg->cached_x509_client_CA, X509_NAME_free);
+ cfg->cached_x509_client_CA = NULL;
+ X509_VERIFY_PARAM_free(cfg->param);
}
-static int ssl_crypto_x509_ssl_auto_chain_if_needed(SSL *ssl) {
+static int ssl_crypto_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) {
// Only build a chain if there are no intermediates configured and the feature
// isn't disabled.
- if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
- !ssl_has_certificate(ssl) ||
- ssl->cert->chain == nullptr ||
- sk_CRYPTO_BUFFER_num(ssl->cert->chain.get()) > 1) {
+ if ((hs->ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
+ !ssl_has_certificate(hs->config) || hs->config->cert->chain == NULL ||
+ sk_CRYPTO_BUFFER_num(hs->config->cert->chain.get()) > 1) {
return 1;
}
UniquePtr<X509> leaf(X509_parse_from_buffer(
- sk_CRYPTO_BUFFER_value(ssl->cert->chain.get(), 0)));
+ sk_CRYPTO_BUFFER_value(hs->config->cert->chain.get(), 0)));
if (!leaf) {
OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
return 0;
}
ScopedX509_STORE_CTX ctx;
- if (!X509_STORE_CTX_init(ctx.get(), ssl->ctx->cert_store, leaf.get(), NULL)) {
+ if (!X509_STORE_CTX_init(ctx.get(), hs->ssl->ctx->cert_store, leaf.get(),
+ NULL)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
return 0;
}
@@ -460,11 +459,11 @@
// Remove the leaf from the generated chain.
X509_free(sk_X509_shift(ctx->chain));
- if (!ssl_cert_set_chain(ssl->cert, ctx->chain)) {
+ if (!ssl_cert_set_chain(hs->config->cert.get(), ctx->chain)) {
return 0;
}
- ssl_crypto_x509_cert_flush_cached_chain(ssl->cert);
+ ssl_crypto_x509_cert_flush_cached_chain(hs->config->cert.get());
return 1;
}
@@ -499,7 +498,7 @@
ssl_crypto_x509_session_verify_cert_chain,
ssl_crypto_x509_hs_flush_cached_ca_names,
ssl_crypto_x509_ssl_new,
- ssl_crypto_x509_ssl_free,
+ ssl_crypto_x509_ssl_config_free,
ssl_crypto_x509_ssl_flush_cached_client_CA,
ssl_crypto_x509_ssl_auto_chain_if_needed,
ssl_crypto_x509_ssl_ctx_new,
@@ -549,12 +548,11 @@
for (size_t i = 1; i < sk_X509_num(session->x509_chain); i++) {
X509 *cert = sk_X509_value(session->x509_chain, i);
- if (!sk_X509_push(session->x509_chain_without_leaf, cert)) {
+ if (!PushToStack(session->x509_chain_without_leaf, UpRef(cert))) {
sk_X509_pop_free(session->x509_chain_without_leaf, X509_free);
session->x509_chain_without_leaf = NULL;
return NULL;
}
- X509_up_ref(cert);
}
}
@@ -578,7 +576,10 @@
int SSL_set_purpose(SSL *ssl, int purpose) {
check_ssl_x509_method(ssl);
- return X509_VERIFY_PARAM_set_purpose(ssl->param, purpose);
+ if (!ssl->config) {
+ return 0;
+ }
+ return X509_VERIFY_PARAM_set_purpose(ssl->config->param, purpose);
}
int SSL_CTX_set_trust(SSL_CTX *ctx, int trust) {
@@ -588,7 +589,10 @@
int SSL_set_trust(SSL *ssl, int trust) {
check_ssl_x509_method(ssl);
- return X509_VERIFY_PARAM_set_trust(ssl->param, trust);
+ if (!ssl->config) {
+ return 0;
+ }
+ return X509_VERIFY_PARAM_set_trust(ssl->config->param, trust);
}
int SSL_CTX_set1_param(SSL_CTX *ctx, const X509_VERIFY_PARAM *param) {
@@ -598,7 +602,10 @@
int SSL_set1_param(SSL *ssl, const X509_VERIFY_PARAM *param) {
check_ssl_x509_method(ssl);
- return X509_VERIFY_PARAM_set1(ssl->param, param);
+ if (!ssl->config) {
+ return 0;
+ }
+ return X509_VERIFY_PARAM_set1(ssl->config->param, param);
}
X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx) {
@@ -608,17 +615,29 @@
X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) {
check_ssl_x509_method(ssl);
- return ssl->param;
+ if (!ssl->config) {
+ assert(ssl->config);
+ return 0;
+ }
+ return ssl->config->param;
}
int SSL_get_verify_depth(const SSL *ssl) {
check_ssl_x509_method(ssl);
- return X509_VERIFY_PARAM_get_depth(ssl->param);
+ if (!ssl->config) {
+ assert(ssl->config);
+ return 0;
+ }
+ return X509_VERIFY_PARAM_get_depth(ssl->config->param);
}
int (*SSL_get_verify_callback(const SSL *ssl))(int, X509_STORE_CTX *) {
check_ssl_x509_method(ssl);
- return ssl->verify_callback;
+ if (!ssl->config) {
+ assert(ssl->config);
+ return 0;
+ }
+ return ssl->config->verify_callback;
}
int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) {
@@ -640,15 +659,21 @@
void SSL_set_verify(SSL *ssl, int mode,
int (*callback)(int ok, X509_STORE_CTX *store_ctx)) {
check_ssl_x509_method(ssl);
- ssl->verify_mode = mode;
+ if (!ssl->config) {
+ return;
+ }
+ ssl->config->verify_mode = mode;
if (callback != NULL) {
- ssl->verify_callback = callback;
+ ssl->config->verify_callback = callback;
}
}
void SSL_set_verify_depth(SSL *ssl, int depth) {
check_ssl_x509_method(ssl);
- X509_VERIFY_PARAM_set_depth(ssl->param, depth);
+ if (!ssl->config) {
+ return;
+ }
+ X509_VERIFY_PARAM_set_depth(ssl->config->param, depth);
}
void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx,
@@ -726,12 +751,15 @@
int SSL_use_certificate(SSL *ssl, X509 *x) {
check_ssl_x509_method(ssl);
- return ssl_use_certificate(ssl->cert, x);
+ if (!ssl->config) {
+ return 0;
+ }
+ return ssl_use_certificate(ssl->config->cert.get(), x);
}
int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) {
check_ssl_ctx_x509_method(ctx);
- return ssl_use_certificate(ctx->cert, x);
+ return ssl_use_certificate(ctx->cert.get(), x);
}
// ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
@@ -764,13 +792,17 @@
X509 *SSL_get_certificate(const SSL *ssl) {
check_ssl_x509_method(ssl);
- return ssl_cert_get0_leaf(ssl->cert);
+ if (!ssl->config) {
+ assert(ssl->config);
+ return 0;
+ }
+ return ssl_cert_get0_leaf(ssl->config->cert.get());
}
X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
check_ssl_ctx_x509_method(ctx);
MutexWriteLock lock(const_cast<CRYPTO_MUTEX*>(&ctx->lock));
- return ssl_cert_get0_leaf(ctx->cert);
+ return ssl_cert_get0_leaf(ctx->cert.get());
}
static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
@@ -836,32 +868,38 @@
int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
check_ssl_ctx_x509_method(ctx);
- return ssl_cert_set0_chain(ctx->cert, chain);
+ return ssl_cert_set0_chain(ctx->cert.get(), chain);
}
int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) {
check_ssl_ctx_x509_method(ctx);
- return ssl_cert_set1_chain(ctx->cert, chain);
+ return ssl_cert_set1_chain(ctx->cert.get(), chain);
}
int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) {
check_ssl_x509_method(ssl);
- return ssl_cert_set0_chain(ssl->cert, chain);
+ if (!ssl->config) {
+ return 0;
+ }
+ return ssl_cert_set0_chain(ssl->config->cert.get(), chain);
}
int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) {
check_ssl_x509_method(ssl);
- return ssl_cert_set1_chain(ssl->cert, chain);
+ if (!ssl->config) {
+ return 0;
+ }
+ return ssl_cert_set1_chain(ssl->config->cert.get(), chain);
}
int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) {
check_ssl_ctx_x509_method(ctx);
- return ssl_cert_add0_chain_cert(ctx->cert, x509);
+ return ssl_cert_add0_chain_cert(ctx->cert.get(), x509);
}
int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) {
check_ssl_ctx_x509_method(ctx);
- return ssl_cert_add1_chain_cert(ctx->cert, x509);
+ return ssl_cert_add1_chain_cert(ctx->cert.get(), x509);
}
int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) {
@@ -871,12 +909,18 @@
int SSL_add0_chain_cert(SSL *ssl, X509 *x509) {
check_ssl_x509_method(ssl);
- return ssl_cert_add0_chain_cert(ssl->cert, x509);
+ if (!ssl->config) {
+ return 0;
+ }
+ return ssl_cert_add0_chain_cert(ssl->config->cert.get(), x509);
}
int SSL_add1_chain_cert(SSL *ssl, X509 *x509) {
check_ssl_x509_method(ssl);
- return ssl_cert_add1_chain_cert(ssl->cert, x509);
+ if (!ssl->config) {
+ return 0;
+ }
+ return ssl_cert_add1_chain_cert(ssl->config->cert.get(), x509);
}
int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) {
@@ -926,7 +970,7 @@
int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
check_ssl_ctx_x509_method(ctx);
MutexWriteLock lock(const_cast<CRYPTO_MUTEX*>(&ctx->lock));
- if (!ssl_cert_cache_chain_certs(ctx->cert)) {
+ if (!ssl_cert_cache_chain_certs(ctx->cert.get())) {
*out_chain = NULL;
return 0;
}
@@ -942,12 +986,16 @@
int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
check_ssl_x509_method(ssl);
- if (!ssl_cert_cache_chain_certs(ssl->cert)) {
+ if (!ssl->config) {
+ assert(ssl->config);
+ return 0;
+ }
+ if (!ssl_cert_cache_chain_certs(ssl->config->cert.get())) {
*out_chain = NULL;
return 0;
}
- *out_chain = ssl->cert->x509_chain;
+ *out_chain = ssl->config->cert->x509_chain;
return 1;
}
@@ -993,7 +1041,7 @@
return sk_X509_NAME_deep_copy(list, X509_NAME_dup, X509_NAME_free);
}
-static void set_client_CA_list(STACK_OF(CRYPTO_BUFFER) **ca_list,
+static void set_client_CA_list(UniquePtr<STACK_OF(CRYPTO_BUFFER)> *ca_list,
const STACK_OF(X509_NAME) *name_list,
CRYPTO_BUFFER_POOL *pool) {
UniquePtr<STACK_OF(CRYPTO_BUFFER)> buffers(sk_CRYPTO_BUFFER_new_null());
@@ -1016,14 +1064,16 @@
}
}
- sk_CRYPTO_BUFFER_pop_free(*ca_list, CRYPTO_BUFFER_free);
- *ca_list = buffers.release();
+ *ca_list = std::move(buffers);
}
void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list) {
check_ssl_x509_method(ssl);
- ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl);
- set_client_CA_list(&ssl->client_CA, name_list, ssl->ctx->pool);
+ if (!ssl->config) {
+ return;
+ }
+ ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl->config.get());
+ set_client_CA_list(&ssl->config->client_CA, name_list, ssl->ctx->pool);
sk_X509_NAME_pop_free(name_list, X509_NAME_free);
}
@@ -1068,6 +1118,10 @@
STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl) {
check_ssl_x509_method(ssl);
+ if (!ssl->config) {
+ assert(ssl->config);
+ return NULL;
+ }
// For historical reasons, this function is used both to query configuration
// state on a server as well as handshake state on a client. However, whether
// |ssl| is a client or server is not known until explicitly configured with
@@ -1082,11 +1136,12 @@
return NULL;
}
- if (ssl->client_CA != NULL) {
+ if (ssl->config->client_CA != NULL) {
return buffer_names_to_x509(
- ssl->client_CA, (STACK_OF(X509_NAME) **)&ssl->cached_x509_client_CA);
+ ssl->config->client_CA.get(),
+ (STACK_OF(X509_NAME) **)&ssl->config->cached_x509_client_CA);
}
- return SSL_CTX_get_client_CA_list(ssl->ctx);
+ return SSL_CTX_get_client_CA_list(ssl->ctx.get());
}
STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) {
@@ -1095,11 +1150,11 @@
// so it needs to lock around updating |cached_x509_client_CA|.
MutexWriteLock lock(const_cast<CRYPTO_MUTEX *>(&ctx->lock));
return buffer_names_to_x509(
- ctx->client_CA,
+ ctx->client_CA.get(),
const_cast<STACK_OF(X509_NAME) **>(&ctx->cached_x509_client_CA));
}
-static int add_client_CA(STACK_OF(CRYPTO_BUFFER) **names, X509 *x509,
+static int add_client_CA(UniquePtr<STACK_OF(CRYPTO_BUFFER)> *names, X509 *x509,
CRYPTO_BUFFER_POOL *pool) {
if (x509 == NULL) {
return 0;
@@ -1118,8 +1173,8 @@
}
int alloced = 0;
- if (*names == NULL) {
- *names = sk_CRYPTO_BUFFER_new_null();
+ if (*names == nullptr) {
+ names->reset(sk_CRYPTO_BUFFER_new_null());
alloced = 1;
if (*names == NULL) {
@@ -1127,10 +1182,9 @@
}
}
- if (!PushToStack(*names, std::move(buffer))) {
+ if (!PushToStack(names->get(), std::move(buffer))) {
if (alloced) {
- sk_CRYPTO_BUFFER_pop_free(*names, CRYPTO_BUFFER_free);
- *names = NULL;
+ names->reset();
}
return 0;
}
@@ -1140,11 +1194,14 @@
int SSL_add_client_CA(SSL *ssl, X509 *x509) {
check_ssl_x509_method(ssl);
- if (!add_client_CA(&ssl->client_CA, x509, ssl->ctx->pool)) {
+ if (!ssl->config) {
+ return 0;
+ }
+ if (!add_client_CA(&ssl->config->client_CA, x509, ssl->ctx->pool)) {
return 0;
}
- ssl_crypto_x509_ssl_flush_cached_client_CA(ssl);
+ ssl_crypto_x509_ssl_flush_cached_client_CA(ssl->config.get());
return 1;
}
@@ -1159,7 +1216,13 @@
}
static int do_client_cert_cb(SSL *ssl, void *arg) {
- if (ssl_has_certificate(ssl) || ssl->ctx->client_cert_cb == NULL) {
+ // Should only be called during handshake, but check to be sure.
+ if (!ssl->config) {
+ assert(ssl->config);
+ return -1;
+ }
+ if (ssl_has_certificate(ssl->config.get()) ||
+ ssl->ctx->client_cert_cb == NULL) {
return 1;
}
@@ -1223,12 +1286,18 @@
int SSL_set0_verify_cert_store(SSL *ssl, X509_STORE *store) {
check_ssl_x509_method(ssl);
- return set_cert_store(&ssl->cert->verify_store, store, 0);
+ if (!ssl->config) {
+ return 0;
+ }
+ return set_cert_store(&ssl->config->cert->verify_store, store, 0);
}
int SSL_set1_verify_cert_store(SSL *ssl, X509_STORE *store) {
check_ssl_x509_method(ssl);
- return set_cert_store(&ssl->cert->verify_store, store, 1);
+ if (!ssl->config) {
+ return 0;
+ }
+ return set_cert_store(&ssl->config->cert->verify_store, store, 1);
}
int SSL_alert_from_verify_result(long result) {
diff --git a/src/ssl/t1_enc.cc b/src/ssl/t1_enc.cc
index 5947627..93170b9 100644
--- a/src/ssl/t1_enc.cc
+++ b/src/ssl/t1_enc.cc
@@ -164,56 +164,6 @@
seed2.size());
}
-static bool ssl3_prf(Span<uint8_t> out, Span<const uint8_t> secret,
- Span<const char> label, Span<const uint8_t> seed1,
- Span<const uint8_t> seed2) {
- ScopedEVP_MD_CTX md5;
- ScopedEVP_MD_CTX sha1;
- uint8_t buf[16], smd[SHA_DIGEST_LENGTH];
- uint8_t c = 'A';
- size_t k = 0;
- while (!out.empty()) {
- k++;
- if (k > sizeof(buf)) {
- // bug: 'buf' is too small for this ciphersuite
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return false;
- }
-
- for (size_t j = 0; j < k; j++) {
- buf[j] = c;
- }
- c++;
- if (!EVP_DigestInit_ex(sha1.get(), EVP_sha1(), NULL)) {
- OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
- return false;
- }
- EVP_DigestUpdate(sha1.get(), buf, k);
- EVP_DigestUpdate(sha1.get(), secret.data(), secret.size());
- // |label| is ignored for SSLv3.
- EVP_DigestUpdate(sha1.get(), seed1.data(), seed1.size());
- EVP_DigestUpdate(sha1.get(), seed2.data(), seed2.size());
- EVP_DigestFinal_ex(sha1.get(), smd, NULL);
-
- if (!EVP_DigestInit_ex(md5.get(), EVP_md5(), NULL)) {
- OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
- return false;
- }
- EVP_DigestUpdate(md5.get(), secret.data(), secret.size());
- EVP_DigestUpdate(md5.get(), smd, SHA_DIGEST_LENGTH);
- if (out.size() < MD5_DIGEST_LENGTH) {
- EVP_DigestFinal_ex(md5.get(), smd, NULL);
- OPENSSL_memcpy(out.data(), smd, out.size());
- break;
- }
- EVP_DigestFinal_ex(md5.get(), out.data(), NULL);
- out = out.subspan(MD5_DIGEST_LENGTH);
- }
-
- OPENSSL_cleanse(smd, SHA_DIGEST_LENGTH);
- return true;
-}
-
static bool get_key_block_lengths(const SSL *ssl, size_t *out_mac_secret_len,
size_t *out_key_len, size_t *out_iv_len,
const SSL_CIPHER *cipher) {
@@ -318,16 +268,9 @@
} else {
auto label =
MakeConstSpan(kMasterSecretLabel, sizeof(kMasterSecretLabel) - 1);
- if (ssl_protocol_version(ssl) == SSL3_VERSION) {
- if (!ssl3_prf(out_span, premaster, label, ssl->s3->client_random,
- ssl->s3->server_random)) {
- return 0;
- }
- } else {
- if (!tls1_prf(hs->transcript.Digest(), out_span, premaster, label,
- ssl->s3->client_random, ssl->s3->server_random)) {
- return 0;
- }
+ if (!tls1_prf(hs->transcript.Digest(), out_span, premaster, label,
+ ssl->s3->client_random, ssl->s3->server_random)) {
+ return 0;
}
}
@@ -357,11 +300,6 @@
static const char kLabel[] = "key expansion";
auto label = MakeConstSpan(kLabel, sizeof(kLabel) - 1);
- if (ssl_protocol_version(ssl) == SSL3_VERSION) {
- return ssl3_prf(out_span, master_key, label, ssl->s3->server_random,
- ssl->s3->client_random);
- }
-
const EVP_MD *digest = ssl_session_get_digest(session);
return tls1_prf(digest, out_span, master_key, label, ssl->s3->server_random,
ssl->s3->client_random);
@@ -371,11 +309,6 @@
const char *label, size_t label_len,
const uint8_t *context, size_t context_len,
int use_context) {
- if (!ssl->s3->have_version || ssl->version == SSL3_VERSION) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_HANDSHAKE_NOT_COMPLETE);
- return 0;
- }
-
// Exporters may be used in False Start and server 0-RTT, where the handshake
// has progressed enough. Otherwise, they may not be used during a handshake.
if (SSL_in_init(ssl) &&
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index a821246..dde767e 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -131,7 +131,7 @@
namespace bssl {
-static int ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs);
+static bool ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs);
static int compare_uint16_t(const void *p1, const void *p2) {
uint16_t u1 = *((const uint16_t *)p1);
@@ -149,7 +149,7 @@
// more than one extension of the same type in a ClientHello or ServerHello.
// This function does an initial scan over the extensions block to filter those
// out.
-static int tls1_check_duplicate_extensions(const CBS *cbs) {
+static bool tls1_check_duplicate_extensions(const CBS *cbs) {
// First pass: count the extensions.
size_t num_extensions = 0;
CBS extensions = *cbs;
@@ -159,19 +159,19 @@
if (!CBS_get_u16(&extensions, &type) ||
!CBS_get_u16_length_prefixed(&extensions, &extension)) {
- return 0;
+ return false;
}
num_extensions++;
}
if (num_extensions == 0) {
- return 1;
+ return true;
}
Array<uint16_t> extension_types;
if (!extension_types.Init(num_extensions)) {
- return 0;
+ return false;
}
// Second pass: gather the extension types.
@@ -182,7 +182,7 @@
if (!CBS_get_u16(&extensions, &extension_types[i]) ||
!CBS_get_u16_length_prefixed(&extensions, &extension)) {
// This should not happen.
- return 0;
+ return false;
}
}
assert(CBS_len(&extensions) == 0);
@@ -192,15 +192,15 @@
compare_uint16_t);
for (size_t i = 1; i < num_extensions; i++) {
if (extension_types[i - 1] == extension_types[i]) {
- return 0;
+ return false;
}
}
- return 1;
+ return true;
}
-int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out,
- const SSLMessage &msg) {
+bool ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out,
+ const SSLMessage &msg) {
OPENSSL_memset(out, 0, sizeof(*out));
out->ssl = ssl;
out->client_hello = CBS_data(&msg.body);
@@ -212,7 +212,7 @@
!CBS_get_bytes(&client_hello, &random, SSL3_RANDOM_SIZE) ||
!CBS_get_u8_length_prefixed(&client_hello, &session_id) ||
CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) {
- return 0;
+ return false;
}
out->random = CBS_data(&random);
@@ -225,7 +225,7 @@
CBS cookie;
if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) ||
CBS_len(&cookie) > DTLS1_COOKIE_LENGTH) {
- return 0;
+ return false;
}
}
@@ -234,7 +234,7 @@
CBS_len(&cipher_suites) < 2 || (CBS_len(&cipher_suites) & 1) != 0 ||
!CBS_get_u8_length_prefixed(&client_hello, &compression_methods) ||
CBS_len(&compression_methods) < 1) {
- return 0;
+ return false;
}
out->cipher_suites = CBS_data(&cipher_suites);
@@ -243,11 +243,11 @@
out->compression_methods_len = CBS_len(&compression_methods);
// If the ClientHello ends here then it's valid, but doesn't have any
- // extensions. (E.g. SSLv3.)
+ // extensions.
if (CBS_len(&client_hello) == 0) {
out->extensions = NULL;
out->extensions_len = 0;
- return 1;
+ return true;
}
// Extract extensions and check it is valid.
@@ -255,17 +255,17 @@
if (!CBS_get_u16_length_prefixed(&client_hello, &extensions) ||
!tls1_check_duplicate_extensions(&extensions) ||
CBS_len(&client_hello) != 0) {
- return 0;
+ return false;
}
out->extensions = CBS_data(&extensions);
out->extensions_len = CBS_len(&extensions);
- return 1;
+ return true;
}
-int ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
- CBS *out, uint16_t extension_type) {
+bool ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
+ CBS *out, uint16_t extension_type) {
CBS extensions;
CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len);
while (CBS_len(&extensions) != 0) {
@@ -274,16 +274,16 @@
CBS extension;
if (!CBS_get_u16(&extensions, &type) ||
!CBS_get_u16_length_prefixed(&extensions, &extension)) {
- return 0;
+ return false;
}
if (type == extension_type) {
*out = extension;
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
static const uint16_t kDefaultGroups[] = {
@@ -292,15 +292,14 @@
SSL_CURVE_SECP384R1,
};
-Span<const uint16_t> tls1_get_grouplist(const SSL *ssl) {
- if (ssl->supported_group_list != nullptr) {
- return MakeConstSpan(ssl->supported_group_list,
- ssl->supported_group_list_len);
+Span<const uint16_t> tls1_get_grouplist(const SSL_HANDSHAKE *hs) {
+ if (!hs->config->supported_group_list.empty()) {
+ return hs->config->supported_group_list;
}
return Span<const uint16_t>(kDefaultGroups);
}
-int tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id) {
+bool tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id) {
SSL *const ssl = hs->ssl;
assert(ssl->server);
@@ -313,7 +312,7 @@
// support our favoured group. Thus we do not special-case an emtpy
// |peer_supported_group_list|.
- Span<const uint16_t> groups = tls1_get_grouplist(ssl);
+ Span<const uint16_t> groups = tls1_get_grouplist(hs);
Span<const uint16_t> pref, supp;
if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
pref = groups;
@@ -327,86 +326,73 @@
for (uint16_t supp_group : supp) {
if (pref_group == supp_group) {
*out_group_id = pref_group;
- return 1;
+ return true;
}
}
}
- return 0;
+ return false;
}
-int tls1_set_curves(uint16_t **out_group_ids, size_t *out_group_ids_len,
- const int *curves, size_t ncurves) {
- uint16_t *group_ids = (uint16_t *)OPENSSL_malloc(ncurves * sizeof(uint16_t));
- if (group_ids == NULL) {
- return 0;
+bool tls1_set_curves(Array<uint16_t> *out_group_ids, Span<const int> curves) {
+ Array<uint16_t> group_ids;
+ if (!group_ids.Init(curves.size())) {
+ return false;
}
- for (size_t i = 0; i < ncurves; i++) {
+ for (size_t i = 0; i < curves.size(); i++) {
if (!ssl_nid_to_group_id(&group_ids[i], curves[i])) {
- OPENSSL_free(group_ids);
- return 0;
+ return false;
}
}
- OPENSSL_free(*out_group_ids);
- *out_group_ids = group_ids;
- *out_group_ids_len = ncurves;
-
- return 1;
+ *out_group_ids = std::move(group_ids);
+ return true;
}
-int tls1_set_curves_list(uint16_t **out_group_ids, size_t *out_group_ids_len,
- const char *curves) {
- uint16_t *group_ids = NULL;
- size_t ncurves = 0;
-
- const char *col;
- const char *ptr = curves;
-
+bool tls1_set_curves_list(Array<uint16_t> *out_group_ids, const char *curves) {
+ // Count the number of curves in the list.
+ size_t count = 0;
+ const char *ptr = curves, *col;
do {
col = strchr(ptr, ':');
-
- uint16_t group_id;
- if (!ssl_name_to_group_id(&group_id, ptr,
- col ? (size_t)(col - ptr) : strlen(ptr))) {
- goto err;
- }
-
- uint16_t *new_group_ids = (uint16_t *)OPENSSL_realloc(
- group_ids, (ncurves + 1) * sizeof(uint16_t));
- if (new_group_ids == NULL) {
- goto err;
- }
- group_ids = new_group_ids;
-
- group_ids[ncurves] = group_id;
- ncurves++;
-
+ count++;
if (col) {
ptr = col + 1;
}
} while (col);
- OPENSSL_free(*out_group_ids);
- *out_group_ids = group_ids;
- *out_group_ids_len = ncurves;
+ Array<uint16_t> group_ids;
+ if (!group_ids.Init(count)) {
+ return false;
+ }
- return 1;
+ size_t i = 0;
+ ptr = curves;
+ do {
+ col = strchr(ptr, ':');
+ if (!ssl_name_to_group_id(&group_ids[i++], ptr,
+ col ? (size_t)(col - ptr) : strlen(ptr))) {
+ return false;
+ }
+ if (col) {
+ ptr = col + 1;
+ }
+ } while (col);
-err:
- OPENSSL_free(group_ids);
- return 0;
+ assert(i == count);
+ *out_group_ids = std::move(group_ids);
+ return true;
}
-int tls1_check_group_id(const SSL *ssl, uint16_t group_id) {
- for (uint16_t supported : tls1_get_grouplist(ssl)) {
+bool tls1_check_group_id(const SSL_HANDSHAKE *hs, uint16_t group_id) {
+ for (uint16_t supported : tls1_get_grouplist(hs)) {
if (supported == group_id) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
// kVerifySignatureAlgorithms is the default list of accepted signature
@@ -506,9 +492,8 @@
static SSLSignatureAlgorithmList tls12_get_verify_sigalgs(const SSL *ssl,
bool for_certs) {
SSLSignatureAlgorithmList ret;
- if (ssl->ctx->num_verify_sigalgs != 0) {
- ret.list =
- MakeConstSpan(ssl->ctx->verify_sigalgs, ssl->ctx->num_verify_sigalgs);
+ if (!ssl->config->verify_sigalgs.empty()) {
+ ret.list = ssl->config->verify_sigalgs;
} else {
ret.list = kVerifySignatureAlgorithms;
ret.skip_ed25519 = !ssl->ctx->ed25519_enabled;
@@ -606,7 +591,7 @@
static bool ext_sni_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- if (ssl->tlsext_hostname == NULL) {
+ if (ssl->hostname == nullptr) {
return true;
}
@@ -616,8 +601,8 @@
!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->tlsext_hostname,
- strlen(ssl->tlsext_hostname)) ||
+ !CBB_add_bytes(&name, (const uint8_t *)ssl->hostname.get(),
+ strlen(ssl->hostname.get())) ||
!CBB_flush(out)) {
return false;
}
@@ -862,7 +847,7 @@
static bool ext_ems_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
// Extended master secret is not necessary in TLS 1.3.
- if (hs->min_version >= TLS1_3_VERSION || hs->max_version <= SSL3_VERSION) {
+ if (hs->min_version >= TLS1_3_VERSION) {
return true;
}
@@ -880,7 +865,6 @@
if (contents != NULL) {
if (ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
- ssl->version == SSL3_VERSION ||
CBS_len(contents) != 0) {
return false;
}
@@ -902,9 +886,7 @@
static bool ext_ems_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
CBS *contents) {
- uint16_t version = ssl_protocol_version(hs->ssl);
- if (version >= TLS1_3_VERSION ||
- version == SSL3_VERSION) {
+ if (ssl_protocol_version(hs->ssl) >= TLS1_3_VERSION) {
return true;
}
@@ -946,26 +928,24 @@
return true;
}
- const uint8_t *ticket_data = NULL;
- int ticket_len = 0;
+ Span<const uint8_t> ticket;
// Renegotiation does not participate in session resumption. However, still
// advertise the extension to avoid potentially breaking servers which carry
// over the state from the previous handshake, such as OpenSSL servers
// without upstream's 3c3f0259238594d77264a78944d409f2127642c4.
if (!ssl->s3->initial_handshake_complete &&
- ssl->session != NULL &&
- ssl->session->tlsext_tick != NULL &&
+ ssl->session != nullptr &&
+ !ssl->session->ticket.empty() &&
// Don't send TLS 1.3 session tickets in the ticket extension.
- ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION) {
- ticket_data = ssl->session->tlsext_tick;
- ticket_len = ssl->session->tlsext_ticklen;
+ ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION) {
+ ticket = ssl->session->ticket;
}
- CBB ticket;
+ CBB ticket_cbb;
if (!CBB_add_u16(out, TLSEXT_TYPE_session_ticket) ||
- !CBB_add_u16_length_prefixed(out, &ticket) ||
- !CBB_add_bytes(&ticket, ticket_data, ticket_len) ||
+ !CBB_add_u16_length_prefixed(out, &ticket_cbb) ||
+ !CBB_add_bytes(&ticket_cbb, ticket.data(), ticket.size()) ||
!CBB_flush(out)) {
return false;
}
@@ -1101,8 +1081,7 @@
// https://tools.ietf.org/html/rfc6066#section-8
static bool ext_ocsp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
- SSL *const ssl = hs->ssl;
- if (!ssl->ocsp_stapling_enabled) {
+ if (!hs->config->ocsp_stapling_enabled) {
return true;
}
@@ -1166,8 +1145,7 @@
static bool ext_ocsp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
if (ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
- !hs->ocsp_stapling_requested ||
- ssl->cert->ocsp_response == NULL ||
+ !hs->ocsp_stapling_requested || hs->config->cert->ocsp_response == NULL ||
ssl->s3->session_reused ||
!ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
return true;
@@ -1308,8 +1286,7 @@
// https://tools.ietf.org/html/rfc6962#section-3.3.1
static bool ext_sct_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
- SSL *const ssl = hs->ssl;
- if (!ssl->signed_cert_timestamps_enabled) {
+ if (!hs->config->signed_cert_timestamps_enabled) {
return true;
}
@@ -1336,7 +1313,7 @@
// If this is false then we should never have sent the SCT extension in the
// ClientHello and thus this function should never have been called.
- assert(ssl->signed_cert_timestamps_enabled);
+ assert(hs->config->signed_cert_timestamps_enabled);
if (!ssl_is_sct_list_valid(contents)) {
*out_alert = SSL_AD_DECODE_ERROR;
@@ -1349,9 +1326,8 @@
//
// TODO(davidben): Enforce this anyway.
if (!ssl->s3->session_reused) {
- CRYPTO_BUFFER_free(hs->new_session->signed_cert_timestamp_list);
- hs->new_session->signed_cert_timestamp_list =
- CRYPTO_BUFFER_new_from_CBS(contents, ssl->ctx->pool);
+ hs->new_session->signed_cert_timestamp_list.reset(
+ CRYPTO_BUFFER_new_from_CBS(contents, ssl->ctx->pool));
if (hs->new_session->signed_cert_timestamp_list == nullptr) {
*out_alert = SSL_AD_INTERNAL_ERROR;
return false;
@@ -1378,9 +1354,8 @@
static bool ext_sct_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
// The extension shouldn't be sent when resuming sessions.
- if (ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
- ssl->s3->session_reused ||
- ssl->cert->signed_cert_timestamp_list == NULL) {
+ if (ssl_protocol_version(ssl) >= TLS1_3_VERSION || ssl->s3->session_reused ||
+ hs->config->cert->signed_cert_timestamp_list == NULL) {
return true;
}
@@ -1389,8 +1364,10 @@
CBB_add_u16_length_prefixed(out, &contents) &&
CBB_add_bytes(
&contents,
- CRYPTO_BUFFER_data(ssl->cert->signed_cert_timestamp_list.get()),
- CRYPTO_BUFFER_len(ssl->cert->signed_cert_timestamp_list.get())) &&
+ CRYPTO_BUFFER_data(
+ hs->config->cert->signed_cert_timestamp_list.get()),
+ CRYPTO_BUFFER_len(
+ hs->config->cert->signed_cert_timestamp_list.get())) &&
CBB_flush(out);
}
@@ -1401,7 +1378,7 @@
static bool ext_alpn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- if (ssl->alpn_client_proto_list == NULL ||
+ if (hs->config->alpn_client_proto_list.empty() ||
ssl->s3->initial_handshake_complete) {
return true;
}
@@ -1410,8 +1387,8 @@
if (!CBB_add_u16(out, TLSEXT_TYPE_application_layer_protocol_negotiation) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
!CBB_add_u16_length_prefixed(&contents, &proto_list) ||
- !CBB_add_bytes(&proto_list, ssl->alpn_client_proto_list,
- ssl->alpn_client_proto_list_len) ||
+ !CBB_add_bytes(&proto_list, hs->config->alpn_client_proto_list.data(),
+ hs->config->alpn_client_proto_list.size()) ||
!CBB_flush(out)) {
return false;
}
@@ -1427,7 +1404,7 @@
}
assert(!ssl->s3->initial_handshake_complete);
- assert(ssl->alpn_client_proto_list != NULL);
+ assert(!hs->config->alpn_client_proto_list.empty());
if (hs->next_proto_neg_seen) {
// NPN and ALPN may not be negotiated in the same connection.
@@ -1448,7 +1425,7 @@
return false;
}
- if (!ssl_is_alpn_protocol_allowed(ssl, protocol_name)) {
+ if (!ssl_is_alpn_protocol_allowed(hs, protocol_name)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
*out_alert = SSL_AD_ILLEGAL_PARAMETER;
return false;
@@ -1462,20 +1439,20 @@
return true;
}
-bool ssl_is_alpn_protocol_allowed(const SSL *ssl,
+bool ssl_is_alpn_protocol_allowed(const SSL_HANDSHAKE *hs,
Span<const uint8_t> protocol) {
- if (ssl->alpn_client_proto_list == nullptr) {
+ if (hs->config->alpn_client_proto_list.empty()) {
return false;
}
- if (ssl->ctx->allow_unknown_alpn_protos) {
+ if (hs->ssl->ctx->allow_unknown_alpn_protos) {
return true;
}
// Check that the protocol name is one of the ones we advertised.
- CBS client_protocol_name_list, client_protocol_name;
- CBS_init(&client_protocol_name_list, ssl->alpn_client_proto_list,
- ssl->alpn_client_proto_list_len);
+ CBS client_protocol_name_list =
+ MakeConstSpan(hs->config->alpn_client_proto_list),
+ client_protocol_name;
while (CBS_len(&client_protocol_name_list) > 0) {
if (!CBS_get_u8_length_prefixed(&client_protocol_name_list,
&client_protocol_name)) {
@@ -1534,6 +1511,11 @@
ssl, &selected, &selected_len, CBS_data(&protocol_name_list),
CBS_len(&protocol_name_list),
ssl->ctx->alpn_select_cb_arg) == SSL_TLSEXT_ERR_OK) {
+ if (selected_len == 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ return false;
+ }
if (!ssl->s3->alpn_selected.CopyFrom(
MakeConstSpan(selected, selected_len))) {
*out_alert = SSL_AD_INTERNAL_ERROR;
@@ -1570,13 +1552,12 @@
// https://tools.ietf.org/html/draft-balfanz-tls-channelid-01
static void ext_channel_id_init(SSL_HANDSHAKE *hs) {
- hs->ssl->s3->tlsext_channel_id_valid = false;
+ hs->ssl->s3->channel_id_valid = false;
}
static bool ext_channel_id_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- if (!ssl->tlsext_channel_id_enabled ||
- SSL_is_dtls(ssl)) {
+ if (!hs->config->channel_id_enabled || SSL_is_dtls(ssl)) {
return true;
}
@@ -1597,13 +1578,13 @@
}
assert(!SSL_is_dtls(ssl));
- assert(ssl->tlsext_channel_id_enabled);
+ assert(hs->config->channel_id_enabled);
if (CBS_len(contents) != 0) {
return false;
}
- ssl->s3->tlsext_channel_id_valid = true;
+ ssl->s3->channel_id_valid = true;
return true;
}
@@ -1611,9 +1592,7 @@
uint8_t *out_alert,
CBS *contents) {
SSL *const ssl = hs->ssl;
- if (contents == NULL ||
- !ssl->tlsext_channel_id_enabled ||
- SSL_is_dtls(ssl)) {
+ if (contents == NULL || !hs->config->channel_id_enabled || SSL_is_dtls(ssl)) {
return true;
}
@@ -1621,13 +1600,13 @@
return false;
}
- ssl->s3->tlsext_channel_id_valid = true;
+ ssl->s3->channel_id_valid = true;
return true;
}
static bool ext_channel_id_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- if (!ssl->s3->tlsext_channel_id_valid) {
+ if (!ssl->s3->channel_id_valid) {
return true;
}
@@ -1871,20 +1850,20 @@
static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
- ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION) {
+ if (hs->max_version < TLS1_3_VERSION || ssl->session == nullptr ||
+ ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION) {
return 0;
}
- size_t binder_len = EVP_MD_size(ssl_session_get_digest(ssl->session));
- return 15 + ssl->session->tlsext_ticklen + binder_len;
+ size_t binder_len = EVP_MD_size(ssl_session_get_digest(ssl->session.get()));
+ return 15 + ssl->session->ticket.size() + binder_len;
}
static bool ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
hs->needs_psk_binder = false;
- if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
- ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION) {
+ if (hs->max_version < TLS1_3_VERSION || ssl->session == nullptr ||
+ ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION) {
return true;
}
@@ -1904,15 +1883,15 @@
// Fill in a placeholder zero binder of the appropriate length. It will be
// computed and filled in later after length prefixes are computed.
uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0};
- size_t binder_len = EVP_MD_size(ssl_session_get_digest(ssl->session));
+ size_t binder_len = EVP_MD_size(ssl_session_get_digest(ssl->session.get()));
CBB contents, identity, ticket, binders, binder;
if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
!CBB_add_u16_length_prefixed(&contents, &identity) ||
!CBB_add_u16_length_prefixed(&identity, &ticket) ||
- !CBB_add_bytes(&ticket, ssl->session->tlsext_tick,
- ssl->session->tlsext_ticklen) ||
+ !CBB_add_bytes(&ticket, ssl->session->ticket.data(),
+ ssl->session->ticket.size()) ||
!CBB_add_u32(&identity, obfuscated_ticket_age) ||
!CBB_add_u16_length_prefixed(&contents, &binders) ||
!CBB_add_u8_length_prefixed(&binders, &binder) ||
@@ -2069,19 +2048,17 @@
static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- if (!ssl->cert->enable_early_data ||
+ if (!ssl->enable_early_data ||
// Session must be 0-RTT capable.
- ssl->session == NULL ||
- ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION ||
+ ssl->session == nullptr ||
+ ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
ssl->session->ticket_max_early_data == 0 ||
// The second ClientHello never offers early data.
hs->received_hello_retry_request ||
// In case ALPN preferences changed since this session was established,
// avoid reporting a confusing value in |SSL_get0_alpn_selected|.
- (ssl->session->early_alpn_len != 0 &&
- !ssl_is_alpn_protocol_allowed(
- ssl, MakeConstSpan(ssl->session->early_alpn,
- ssl->session->early_alpn_len)))) {
+ (!ssl->session->early_alpn.empty() &&
+ !ssl_is_alpn_protocol_allowed(hs, ssl->session->early_alpn))) {
return true;
}
@@ -2191,7 +2168,7 @@
}
// Predict the most preferred group.
- Span<const uint16_t> groups = tls1_get_grouplist(ssl);
+ Span<const uint16_t> groups = tls1_get_grouplist(hs);
if (groups.empty()) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_GROUPS_SPECIFIED);
return false;
@@ -2422,7 +2399,7 @@
}
static bool ext_dummy_pq_padding_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
- const size_t len = hs->ssl->dummy_pq_padding_len;
+ const size_t len = hs->config->dummy_pq_padding_len;
if (len == 0) {
return true;
}
@@ -2437,7 +2414,7 @@
return true;
}
- if (CBS_len(contents) != hs->ssl->dummy_pq_padding_len) {
+ if (CBS_len(contents) != hs->config->dummy_pq_padding_len) {
return false;
}
@@ -2485,7 +2462,7 @@
return false;
}
- for (uint16_t group : tls1_get_grouplist(ssl)) {
+ for (uint16_t group : tls1_get_grouplist(hs)) {
if (!CBB_add_u16(&groups_bytes, group)) {
return false;
}
@@ -2556,7 +2533,7 @@
static bool ext_token_binding_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- if (ssl->token_binding_params == nullptr || SSL_is_dtls(ssl)) {
+ if (hs->config->token_binding_params.empty() || SSL_is_dtls(ssl)) {
return true;
}
@@ -2565,8 +2542,8 @@
!CBB_add_u16_length_prefixed(out, &contents) ||
!CBB_add_u16(&contents, kTokenBindingMaxVersion) ||
!CBB_add_u8_length_prefixed(&contents, ¶ms) ||
- !CBB_add_bytes(¶ms, ssl->token_binding_params,
- ssl->token_binding_params_len) ||
+ !CBB_add_bytes(¶ms, hs->config->token_binding_params.data(),
+ hs->config->token_binding_params.size()) ||
!CBB_flush(out)) {
return false;
}
@@ -2606,8 +2583,8 @@
return true;
}
- for (size_t i = 0; i < ssl->token_binding_params_len; ++i) {
- if (param == ssl->token_binding_params[i]) {
+ for (uint8_t config_param : hs->config->token_binding_params) {
+ if (param == config_param) {
ssl->s3->negotiated_token_binding_param = param;
ssl->s3->token_binding_negotiated = true;
return true;
@@ -2619,15 +2596,15 @@
}
// select_tb_param looks for the first token binding param in
-// |ssl->token_binding_params| that is also in |params| and puts it in
-// |ssl->negotiated_token_binding_param|. It returns true if a token binding
+// |hs->ssl->token_binding_params| that is also in |params| and puts it in
+// |hs->ssl->negotiated_token_binding_param|. It returns true if a token binding
// param is found, and false otherwise.
-static bool select_tb_param(SSL *ssl, Span<const uint8_t> peer_params) {
- for (size_t i = 0; i < ssl->token_binding_params_len; ++i) {
- uint8_t tb_param = ssl->token_binding_params[i];
+static bool select_tb_param(SSL_HANDSHAKE *hs,
+ Span<const uint8_t> peer_params) {
+ for (uint8_t tb_param : hs->config->token_binding_params) {
for (uint8_t peer_param : peer_params) {
if (tb_param == peer_param) {
- ssl->s3->negotiated_token_binding_param = tb_param;
+ hs->ssl->s3->negotiated_token_binding_param = tb_param;
return true;
}
}
@@ -2639,7 +2616,7 @@
uint8_t *out_alert,
CBS *contents) {
SSL *const ssl = hs->ssl;
- if (contents == nullptr || ssl->token_binding_params == nullptr) {
+ if (contents == nullptr || hs->config->token_binding_params.empty()) {
return true;
}
@@ -2663,7 +2640,7 @@
// version. Otherwise, use the client's version.
hs->negotiated_token_binding_version =
std::min(version, kTokenBindingMaxVersion);
- if (!select_tb_param(ssl, params)) {
+ if (!select_tb_param(hs, params)) {
return true;
}
@@ -2695,16 +2672,16 @@
static bool ext_quic_transport_params_add_clienthello(SSL_HANDSHAKE *hs,
CBB *out) {
- SSL *const ssl = hs->ssl;
- if (!ssl->quic_transport_params || hs->max_version <= TLS1_2_VERSION) {
+ if (hs->config->quic_transport_params.empty() ||
+ hs->max_version <= TLS1_2_VERSION) {
return true;
}
CBB contents;
if (!CBB_add_u16(out, TLSEXT_TYPE_quic_transport_parameters) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
- !CBB_add_bytes(&contents, ssl->quic_transport_params,
- ssl->quic_transport_params_len) ||
+ !CBB_add_bytes(&contents, hs->config->quic_transport_params.data(),
+ hs->config->quic_transport_params.size()) ||
!CBB_flush(out)) {
return false;
}
@@ -2731,7 +2708,7 @@
uint8_t *out_alert,
CBS *contents) {
SSL *const ssl = hs->ssl;
- if (!contents || !ssl->quic_transport_params) {
+ if (!contents || hs->config->quic_transport_params.empty()) {
return true;
}
// Ignore the extension before TLS 1.3.
@@ -2744,16 +2721,15 @@
static bool ext_quic_transport_params_add_serverhello(SSL_HANDSHAKE *hs,
CBB *out) {
- SSL *const ssl = hs->ssl;
- if (!ssl->quic_transport_params) {
+ if (hs->config->quic_transport_params.empty()) {
return true;
}
CBB contents;
if (!CBB_add_u16(out, TLSEXT_TYPE_quic_transport_parameters) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
- !CBB_add_bytes(&contents, ssl->quic_transport_params,
- ssl->quic_transport_params_len) ||
+ !CBB_add_bytes(&contents, hs->config->quic_transport_params.data(),
+ hs->config->quic_transport_params.size()) ||
!CBB_flush(out)) {
return false;
}
@@ -2761,6 +2737,113 @@
return true;
}
+// Certificate compression
+
+static bool cert_compression_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+ bool first = true;
+ CBB contents, algs;
+
+ for (const auto *alg : hs->ssl->ctx->cert_compression_algs.get()) {
+ if (alg->decompress == nullptr) {
+ continue;
+ }
+
+ if (first && (!CBB_add_u16(out, TLSEXT_TYPE_cert_compression) ||
+ !CBB_add_u16_length_prefixed(out, &contents) ||
+ !CBB_add_u8_length_prefixed(&contents, &algs))) {
+ return false;
+ }
+ first = false;
+ if (!CBB_add_u16(&algs, alg->alg_id)) {
+ return false;
+ }
+ }
+
+ return first || CBB_flush(out);
+}
+
+static bool cert_compression_parse_serverhello(SSL_HANDSHAKE *hs,
+ uint8_t *out_alert,
+ CBS *contents) {
+ if (contents == nullptr) {
+ return true;
+ }
+
+ // The server may not echo this extension. Any server to client negotiation is
+ // advertised in the CertificateRequest message.
+ return false;
+}
+
+static bool cert_compression_parse_clienthello(SSL_HANDSHAKE *hs,
+ uint8_t *out_alert,
+ CBS *contents) {
+ if (contents == nullptr) {
+ return true;
+ }
+
+ const size_t num_algs =
+ sk_CertCompressionAlg_num(hs->ssl->ctx->cert_compression_algs.get());
+
+ CBS alg_ids;
+ if (!CBS_get_u8_length_prefixed(contents, &alg_ids) ||
+ CBS_len(contents) != 0 ||
+ CBS_len(&alg_ids) == 0 ||
+ CBS_len(&alg_ids) % 2 == 1) {
+ return false;
+ }
+
+ const size_t num_given_alg_ids = CBS_len(&alg_ids) / 2;
+ Array<uint16_t> given_alg_ids;
+ if (!given_alg_ids.Init(num_given_alg_ids)) {
+ return false;
+ }
+
+ size_t best_index = num_algs;
+ size_t given_alg_idx = 0;
+
+ while (CBS_len(&alg_ids) > 0) {
+ uint16_t alg_id;
+ if (!CBS_get_u16(&alg_ids, &alg_id)) {
+ return false;
+ }
+
+ given_alg_ids[given_alg_idx++] = alg_id;
+
+ for (size_t i = 0; i < num_algs; i++) {
+ const auto *alg = sk_CertCompressionAlg_value(
+ hs->ssl->ctx->cert_compression_algs.get(), i);
+ if (alg->alg_id == alg_id && alg->compress != nullptr) {
+ if (i < best_index) {
+ best_index = i;
+ }
+ break;
+ }
+ }
+ }
+
+ qsort(given_alg_ids.data(), given_alg_ids.size(), sizeof(uint16_t),
+ compare_uint16_t);
+ for (size_t i = 1; i < num_given_alg_ids; i++) {
+ if (given_alg_ids[i - 1] == given_alg_ids[i]) {
+ return false;
+ }
+ }
+
+ if (best_index < num_algs &&
+ ssl_protocol_version(hs->ssl) >= TLS1_3_VERSION) {
+ hs->cert_compression_negotiated = true;
+ hs->cert_compression_alg_id =
+ sk_CertCompressionAlg_value(hs->ssl->ctx->cert_compression_algs.get(),
+ best_index)
+ ->alg_id;
+ }
+
+ return true;
+}
+
+static bool cert_compression_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+ return true;
+}
// kExtensions contains all the supported extensions.
static const struct tls_extension kExtensions[] = {
@@ -2945,6 +3028,14 @@
ext_token_binding_parse_clienthello,
ext_token_binding_add_serverhello,
},
+ {
+ TLSEXT_TYPE_cert_compression,
+ NULL,
+ cert_compression_add_clienthello,
+ cert_compression_parse_serverhello,
+ cert_compression_parse_clienthello,
+ cert_compression_add_serverhello,
+ },
};
#define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
@@ -2969,22 +3060,16 @@
return NULL;
}
-int ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, size_t header_len) {
+bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out,
+ size_t header_len) {
SSL *const ssl = hs->ssl;
- // Don't add extensions for SSLv3 unless doing secure renegotiation.
- if (hs->client_version == SSL3_VERSION &&
- !ssl->s3->send_connection_binding) {
- return 1;
- }
-
CBB extensions;
if (!CBB_add_u16_length_prefixed(out, &extensions)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
+ return false;
}
hs->extensions.sent = 0;
- hs->custom_extensions.sent = 0;
for (size_t i = 0; i < kNumExtensions; i++) {
if (kExtensions[i].init != NULL) {
@@ -2999,7 +3084,7 @@
if (!CBB_add_u16(&extensions, grease_ext1) ||
!CBB_add_u16(&extensions, 0 /* zero length */)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
+ return false;
}
}
@@ -3008,7 +3093,7 @@
if (!kExtensions[i].add_clienthello(hs, &extensions)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
- return 0;
+ return false;
}
if (CBB_len(&extensions) != len_before) {
@@ -3016,11 +3101,6 @@
}
}
- if (!custom_ext_add_clienthello(hs, &extensions)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-
if (ssl->ctx->grease_enabled) {
// Add a fake non-empty extension. See draft-davidben-tls-grease-01.
uint16_t grease_ext2 = ssl_get_grease_value(hs, ssl_grease_extension2);
@@ -3036,7 +3116,7 @@
!CBB_add_u16(&extensions, 1 /* one byte length */) ||
!CBB_add_u8(&extensions, 0 /* single zero byte as contents */)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
+ return false;
}
}
@@ -3064,7 +3144,7 @@
!CBB_add_u16(&extensions, padding_len) ||
!CBB_add_space(&extensions, &padding_bytes, padding_len)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
+ return false;
}
OPENSSL_memset(padding_bytes, 0, padding_len);
@@ -3074,7 +3154,7 @@
// The PSK extension must be last, including after the padding.
if (!ext_pre_shared_key_add_clienthello(hs, &extensions)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
+ return false;
}
// Discard empty extensions blocks.
@@ -3085,7 +3165,7 @@
return CBB_flush(out);
}
-int ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out) {
+bool ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
CBB extensions;
if (!CBB_add_u16_length_prefixed(out, &extensions)) {
@@ -3105,10 +3185,6 @@
}
}
- if (!custom_ext_add_serverhello(hs, &extensions)) {
- goto err;
- }
-
// Discard empty extensions blocks before TLS 1.3.
if (ssl_protocol_version(ssl) < TLS1_3_VERSION &&
CBB_len(&extensions) == 0) {
@@ -3119,13 +3195,12 @@
err:
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
+ return false;
}
-static int ssl_scan_clienthello_tlsext(SSL_HANDSHAKE *hs,
- const SSL_CLIENT_HELLO *client_hello,
- int *out_alert) {
- SSL *const ssl = hs->ssl;
+static bool ssl_scan_clienthello_tlsext(SSL_HANDSHAKE *hs,
+ const SSL_CLIENT_HELLO *client_hello,
+ int *out_alert) {
for (size_t i = 0; i < kNumExtensions; i++) {
if (kExtensions[i].init != NULL) {
kExtensions[i].init(hs);
@@ -3133,7 +3208,6 @@
}
hs->extensions.received = 0;
- hs->custom_extensions.received = 0;
CBS extensions;
CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len);
while (CBS_len(&extensions) != 0) {
@@ -3144,24 +3218,13 @@
if (!CBS_get_u16(&extensions, &type) ||
!CBS_get_u16_length_prefixed(&extensions, &extension)) {
*out_alert = SSL_AD_DECODE_ERROR;
- return 0;
- }
-
- // RFC 5746 made the existence of extensions in SSL 3.0 somewhat
- // ambiguous. Ignore all but the renegotiation_info extension.
- if (ssl->version == SSL3_VERSION && type != TLSEXT_TYPE_renegotiate) {
- continue;
+ return false;
}
unsigned ext_index;
const struct tls_extension *const ext =
tls_extension_find(&ext_index, type);
-
if (ext == NULL) {
- if (!custom_ext_parse_clienthello(hs, out_alert, type, &extension)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
- return 0;
- }
continue;
}
@@ -3171,7 +3234,7 @@
*out_alert = alert;
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
ERR_add_error_dataf("extension %u", (unsigned)type);
- return 0;
+ return false;
}
}
@@ -3200,36 +3263,36 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
*out_alert = alert;
- return 0;
+ return false;
}
}
- return 1;
+ return true;
}
-int ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
- const SSL_CLIENT_HELLO *client_hello) {
+bool ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
+ const SSL_CLIENT_HELLO *client_hello) {
SSL *const ssl = hs->ssl;
int alert = SSL_AD_DECODE_ERROR;
- if (ssl_scan_clienthello_tlsext(hs, client_hello, &alert) <= 0) {
+ if (!ssl_scan_clienthello_tlsext(hs, client_hello, &alert)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
- return 0;
+ return false;
}
- if (ssl_check_clienthello_tlsext(hs) <= 0) {
+ if (!ssl_check_clienthello_tlsext(hs)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_TLSEXT);
- return 0;
+ return false;
}
- return 1;
+ return true;
}
-static int ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs,
- int *out_alert) {
+static bool ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs,
+ int *out_alert) {
SSL *const ssl = hs->ssl;
// Before TLS 1.3, ServerHello extensions blocks may be omitted if empty.
if (CBS_len(cbs) == 0 && ssl_protocol_version(ssl) < TLS1_3_VERSION) {
- return 1;
+ return true;
}
// Decode the extensions block and check it is valid.
@@ -3237,7 +3300,7 @@
if (!CBS_get_u16_length_prefixed(cbs, &extensions) ||
!tls1_check_duplicate_extensions(&extensions)) {
*out_alert = SSL_AD_DECODE_ERROR;
- return 0;
+ return false;
}
uint32_t received = 0;
@@ -3249,7 +3312,7 @@
if (!CBS_get_u16(&extensions, &type) ||
!CBS_get_u16_length_prefixed(&extensions, &extension)) {
*out_alert = SSL_AD_DECODE_ERROR;
- return 0;
+ return false;
}
unsigned ext_index;
@@ -3257,24 +3320,21 @@
tls_extension_find(&ext_index, type);
if (ext == NULL) {
- hs->received_custom_extension = true;
- if (!custom_ext_parse_serverhello(hs, out_alert, type, &extension)) {
- return 0;
- }
- continue;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+ ERR_add_error_dataf("extension %u", (unsigned)type);
+ *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
+ return false;
}
static_assert(kNumExtensions <= sizeof(hs->extensions.sent) * 8,
"too many bits");
- if (!(hs->extensions.sent & (1u << ext_index)) &&
- type != TLSEXT_TYPE_renegotiate) {
- // If the extension was never sent then it is illegal, except for the
- // renegotiation extension which, in SSL 3.0, is signaled via SCSV.
+ if (!(hs->extensions.sent & (1u << ext_index))) {
+ // If the extension was never sent then it is illegal.
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
ERR_add_error_dataf("extension :%u", (unsigned)type);
*out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
- return 0;
+ return false;
}
received |= (1u << ext_index);
@@ -3284,7 +3344,7 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
ERR_add_error_dataf("extension %u", (unsigned)type);
*out_alert = alert;
- return 0;
+ return false;
}
}
@@ -3297,15 +3357,15 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
*out_alert = alert;
- return 0;
+ return false;
}
}
}
- return 1;
+ return true;
}
-static int ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs) {
+static bool ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
if (ssl->s3->token_binding_negotiated &&
@@ -3313,43 +3373,42 @@
SSL_get_extms_support(ssl))) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_TB_WITHOUT_EMS_OR_RI);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
- return -1;
+ return false;
}
int ret = SSL_TLSEXT_ERR_NOACK;
int al = SSL_AD_UNRECOGNIZED_NAME;
- if (ssl->ctx->tlsext_servername_callback != 0) {
- ret = ssl->ctx->tlsext_servername_callback(ssl, &al,
- ssl->ctx->tlsext_servername_arg);
- } else if (ssl->session_ctx->tlsext_servername_callback != 0) {
- ret = ssl->session_ctx->tlsext_servername_callback(
- ssl, &al, ssl->session_ctx->tlsext_servername_arg);
+ if (ssl->ctx->servername_callback != 0) {
+ ret = ssl->ctx->servername_callback(ssl, &al, ssl->ctx->servername_arg);
+ } else if (ssl->session_ctx->servername_callback != 0) {
+ ret = ssl->session_ctx->servername_callback(
+ ssl, &al, ssl->session_ctx->servername_arg);
}
switch (ret) {
case SSL_TLSEXT_ERR_ALERT_FATAL:
ssl_send_alert(ssl, SSL3_AL_FATAL, al);
- return -1;
+ return false;
case SSL_TLSEXT_ERR_NOACK:
hs->should_ack_sni = false;
- return 1;
+ return true;
default:
- return 1;
+ return true;
}
}
-int ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) {
+bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) {
SSL *const ssl = hs->ssl;
int alert = SSL_AD_DECODE_ERROR;
- if (ssl_scan_serverhello_tlsext(hs, cbs, &alert) <= 0) {
+ if (!ssl_scan_serverhello_tlsext(hs, cbs, &alert)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static enum ssl_ticket_aead_result_t decrypt_ticket_with_cipher_ctx(
@@ -3366,10 +3425,10 @@
}
HMAC_Update(hmac_ctx, ticket, ticket_len - mac_len);
HMAC_Final(hmac_ctx, mac, NULL);
- int mac_ok =
+ bool mac_ok =
CRYPTO_memcmp(mac, ticket + (ticket_len - mac_len), mac_len) == 0;
#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
- mac_ok = 1;
+ mac_ok = true;
#endif
if (!mac_ok) {
return ssl_ticket_aead_ignore_ticket;
@@ -3407,14 +3466,14 @@
}
static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_cb(
- SSL *ssl, uint8_t **out, size_t *out_len, bool *out_renew_ticket,
+ SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len, bool *out_renew_ticket,
const uint8_t *ticket, size_t ticket_len) {
assert(ticket_len >= SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH);
ScopedEVP_CIPHER_CTX cipher_ctx;
ScopedHMAC_CTX hmac_ctx;
const uint8_t *iv = ticket + SSL_TICKET_KEY_NAME_LEN;
- int cb_ret = ssl->session_ctx->tlsext_ticket_key_cb(
- ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, cipher_ctx.get(),
+ int cb_ret = hs->ssl->session_ctx->ticket_key_cb(
+ hs->ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, cipher_ctx.get(),
hmac_ctx.get(), 0 /* decrypt */);
if (cb_ret < 0) {
return ssl_ticket_aead_error;
@@ -3430,10 +3489,10 @@
}
static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_ticket_keys(
- SSL *ssl, uint8_t **out, size_t *out_len, const uint8_t *ticket,
+ SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len, const uint8_t *ticket,
size_t ticket_len) {
assert(ticket_len >= SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH);
- SSL_CTX *ctx = ssl->session_ctx;
+ SSL_CTX *ctx = hs->ssl->session_ctx.get();
// Rotate the ticket key if necessary.
if (!ssl_ctx_rotate_ticket_encryption_key(ctx)) {
@@ -3445,15 +3504,15 @@
ScopedHMAC_CTX hmac_ctx;
{
MutexReadLock lock(&ctx->lock);
- const tlsext_ticket_key *key;
- if (ctx->tlsext_ticket_key_current &&
- !OPENSSL_memcmp(ctx->tlsext_ticket_key_current->name, ticket,
+ const TicketKey *key;
+ if (ctx->ticket_key_current &&
+ !OPENSSL_memcmp(ctx->ticket_key_current->name, ticket,
SSL_TICKET_KEY_NAME_LEN)) {
- key = ctx->tlsext_ticket_key_current;
- } else if (ctx->tlsext_ticket_key_prev &&
- !OPENSSL_memcmp(ctx->tlsext_ticket_key_prev->name, ticket,
+ key = ctx->ticket_key_current.get();
+ } else if (ctx->ticket_key_prev &&
+ !OPENSSL_memcmp(ctx->ticket_key_prev->name, ticket,
SSL_TICKET_KEY_NAME_LEN)) {
- key = ctx->tlsext_ticket_key_prev;
+ key = ctx->ticket_key_prev.get();
} else {
return ssl_ticket_aead_ignore_ticket;
}
@@ -3470,7 +3529,7 @@
}
static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_method(
- SSL *ssl, uint8_t **out, size_t *out_len, bool *out_renew_ticket,
+ SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len, bool *out_renew_ticket,
const uint8_t *ticket, size_t ticket_len) {
uint8_t *plaintext = (uint8_t *)OPENSSL_malloc(ticket_len);
if (plaintext == NULL) {
@@ -3480,8 +3539,8 @@
size_t plaintext_len;
const enum ssl_ticket_aead_result_t result =
- ssl->session_ctx->ticket_aead_method->open(
- ssl, plaintext, &plaintext_len, ticket_len, ticket, ticket_len);
+ hs->ssl->session_ctx->ticket_aead_method->open(
+ hs->ssl, plaintext, &plaintext_len, ticket_len, ticket, ticket_len);
if (result == ssl_ticket_aead_success) {
*out = plaintext;
@@ -3494,13 +3553,13 @@
}
enum ssl_ticket_aead_result_t ssl_process_ticket(
- SSL *ssl, UniquePtr<SSL_SESSION> *out_session, bool *out_renew_ticket,
- const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id,
- size_t session_id_len) {
+ SSL_HANDSHAKE *hs, UniquePtr<SSL_SESSION> *out_session,
+ bool *out_renew_ticket, const uint8_t *ticket, size_t ticket_len,
+ const uint8_t *session_id, size_t session_id_len) {
*out_renew_ticket = false;
out_session->reset();
- if ((SSL_get_options(ssl) & SSL_OP_NO_TICKET) ||
+ if ((SSL_get_options(hs->ssl) & SSL_OP_NO_TICKET) ||
session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
return ssl_ticket_aead_ignore_ticket;
}
@@ -3508,23 +3567,23 @@
uint8_t *plaintext = NULL;
size_t plaintext_len;
enum ssl_ticket_aead_result_t result;
- if (ssl->session_ctx->ticket_aead_method != NULL) {
+ if (hs->ssl->session_ctx->ticket_aead_method != NULL) {
result = ssl_decrypt_ticket_with_method(
- ssl, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len);
+ hs, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len);
} else {
- // Ensure there is room for the key name and the largest IV
- // |tlsext_ticket_key_cb| may try to consume. The real limit may be lower,
- // but the maximum IV length should be well under the minimum size for the
- // session material and HMAC.
+ // Ensure there is room for the key name and the largest IV |ticket_key_cb|
+ // may try to consume. The real limit may be lower, but the maximum IV
+ // length should be well under the minimum size for the session material and
+ // HMAC.
if (ticket_len < SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH) {
return ssl_ticket_aead_ignore_ticket;
}
- if (ssl->session_ctx->tlsext_ticket_key_cb != NULL) {
- result = ssl_decrypt_ticket_with_cb(ssl, &plaintext, &plaintext_len,
+ if (hs->ssl->session_ctx->ticket_key_cb != NULL) {
+ result = ssl_decrypt_ticket_with_cb(hs, &plaintext, &plaintext_len,
out_renew_ticket, ticket, ticket_len);
} else {
result = ssl_decrypt_ticket_with_ticket_keys(
- ssl, &plaintext, &plaintext_len, ticket, ticket_len);
+ hs, &plaintext, &plaintext_len, ticket, ticket_len);
}
}
@@ -3534,7 +3593,7 @@
// Decode the session.
UniquePtr<SSL_SESSION> session(
- SSL_SESSION_from_bytes(plaintext, plaintext_len, ssl->ctx));
+ SSL_SESSION_from_bytes(plaintext, plaintext_len, hs->ssl->ctx.get()));
OPENSSL_free(plaintext);
if (!session) {
@@ -3575,7 +3634,7 @@
bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) {
SSL *const ssl = hs->ssl;
- CERT *cert = ssl->cert;
+ CERT *cert = hs->config->cert.get();
// Before TLS 1.2, the signature algorithm isn't negotiated as part of the
// handshake.
@@ -3622,7 +3681,7 @@
return false;
}
-int tls1_verify_channel_id(SSL_HANDSHAKE *hs, const SSLMessage &msg) {
+bool tls1_verify_channel_id(SSL_HANDSHAKE *hs, const SSLMessage &msg) {
SSL *const ssl = hs->ssl;
// A Channel ID handshake message is structured to contain multiple
// extensions, but the only one that can be present is Channel ID.
@@ -3635,19 +3694,19 @@
CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- return 0;
+ return false;
}
UniquePtr<EC_GROUP> p256(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
if (!p256) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT);
- return 0;
+ return false;
}
UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
UniquePtr<BIGNUM> x(BN_new()), y(BN_new());
if (!sig || !x || !y) {
- return 0;
+ return false;
}
const uint8_t *p = CBS_data(&extension);
@@ -3655,7 +3714,7 @@
BN_bin2bn(p + 32, 32, y.get()) == NULL ||
BN_bin2bn(p + 64, 32, sig->r) == NULL ||
BN_bin2bn(p + 96, 32, sig->s) == NULL) {
- return 0;
+ return false;
}
UniquePtr<EC_KEY> key(EC_KEY_new());
@@ -3665,40 +3724,39 @@
y.get(), nullptr) ||
!EC_KEY_set_group(key.get(), p256.get()) ||
!EC_KEY_set_public_key(key.get(), point.get())) {
- return 0;
+ return false;
}
uint8_t digest[EVP_MAX_MD_SIZE];
size_t digest_len;
if (!tls1_channel_id_hash(hs, digest, &digest_len)) {
- return 0;
- }
-
- int sig_ok = ECDSA_do_verify(digest, digest_len, sig.get(), key.get());
-#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
- sig_ok = 1;
- ERR_clear_error();
-#endif
- if (!sig_ok) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
- ssl->s3->tlsext_channel_id_valid = false;
- return 0;
- }
-
- OPENSSL_memcpy(ssl->s3->tlsext_channel_id, p, 64);
- return 1;
-}
-
-bool tls1_write_channel_id(SSL_HANDSHAKE *hs, CBB *cbb) {
- SSL *const ssl = hs->ssl;
- uint8_t digest[EVP_MAX_MD_SIZE];
- size_t digest_len;
- if (!tls1_channel_id_hash(hs, digest, &digest_len)) {
return false;
}
- EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private);
+ bool sig_ok = ECDSA_do_verify(digest, digest_len, sig.get(), key.get());
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+ sig_ok = true;
+ ERR_clear_error();
+#endif
+ if (!sig_ok) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+ ssl->s3->channel_id_valid = false;
+ return false;
+ }
+
+ OPENSSL_memcpy(ssl->s3->channel_id, p, 64);
+ return true;
+}
+
+bool tls1_write_channel_id(SSL_HANDSHAKE *hs, CBB *cbb) {
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ size_t digest_len;
+ if (!tls1_channel_id_hash(hs, digest, &digest_len)) {
+ return false;
+ }
+
+ EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(hs->config->channel_id_private.get());
if (ec_key == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return false;
@@ -3731,17 +3789,17 @@
return true;
}
-int tls1_channel_id_hash(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len) {
+bool tls1_channel_id_hash(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len) {
SSL *const ssl = hs->ssl;
if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
Array<uint8_t> msg;
if (!tls13_get_cert_verify_signature_input(hs, &msg,
ssl_cert_verify_channel_id)) {
- return 0;
+ return false;
}
SHA256(msg.data(), msg.size(), out);
*out_len = SHA256_DIGEST_LENGTH;
- return 1;
+ return true;
}
SHA256_CTX ctx;
@@ -3755,7 +3813,7 @@
SHA256_Update(&ctx, kResumptionMagic, sizeof(kResumptionMagic));
if (ssl->session->original_handshake_hash_len == 0) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
+ return false;
}
SHA256_Update(&ctx, ssl->session->original_handshake_hash,
ssl->session->original_handshake_hash_len);
@@ -3764,24 +3822,21 @@
uint8_t hs_hash[EVP_MAX_MD_SIZE];
size_t hs_hash_len;
if (!hs->transcript.GetHash(hs_hash, &hs_hash_len)) {
- return 0;
+ return false;
}
SHA256_Update(&ctx, hs_hash, (size_t)hs_hash_len);
SHA256_Final(out, &ctx);
*out_len = SHA256_DIGEST_LENGTH;
- return 1;
+ return true;
}
-// tls1_record_handshake_hashes_for_channel_id records the current handshake
-// hashes in |hs->new_session| so that Channel ID resumptions can sign that
-// data.
-int tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs) {
+bool tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
// This function should never be called for a resumed session because the
// handshake hashes that we wish to record are for the original, full
// handshake.
if (ssl->session != NULL) {
- return 0;
+ return false;
}
static_assert(
@@ -3791,35 +3846,34 @@
size_t digest_len;
if (!hs->transcript.GetHash(hs->new_session->original_handshake_hash,
&digest_len)) {
- return 0;
+ return false;
}
static_assert(EVP_MAX_MD_SIZE <= 0xff,
"EVP_MAX_MD_SIZE does not fit in uint8_t");
hs->new_session->original_handshake_hash_len = (uint8_t)digest_len;
- return 1;
+ return true;
}
-int ssl_do_channel_id_callback(SSL *ssl) {
- if (ssl->tlsext_channel_id_private != NULL ||
- ssl->ctx->channel_id_cb == NULL) {
- return 1;
+bool ssl_do_channel_id_callback(SSL_HANDSHAKE *hs) {
+ if (hs->config->channel_id_private != NULL ||
+ hs->ssl->ctx->channel_id_cb == NULL) {
+ return true;
}
EVP_PKEY *key = NULL;
- ssl->ctx->channel_id_cb(ssl, &key);
+ hs->ssl->ctx->channel_id_cb(hs->ssl, &key);
if (key == NULL) {
// The caller should try again later.
- return 1;
+ return true;
}
- int ret = SSL_set1_tls_channel_id(ssl, key);
- EVP_PKEY_free(key);
- return ret;
+ UniquePtr<EVP_PKEY> free_key(key);
+ return SSL_set1_tls_channel_id(hs->ssl, key);
}
-int ssl_is_sct_list_valid(const CBS *contents) {
+bool ssl_is_sct_list_valid(const CBS *contents) {
// Shallow parse the SCT list for sanity. By the RFC
// (https://tools.ietf.org/html/rfc6962#section-3.3) neither the list nor any
// of the SCTs may be empty.
@@ -3828,18 +3882,18 @@
if (!CBS_get_u16_length_prefixed(©, &sct_list) ||
CBS_len(©) != 0 ||
CBS_len(&sct_list) == 0) {
- return 0;
+ return false;
}
while (CBS_len(&sct_list) > 0) {
CBS sct;
if (!CBS_get_u16_length_prefixed(&sct_list, &sct) ||
CBS_len(&sct) == 0) {
- return 0;
+ return false;
}
}
- return 1;
+ return true;
}
} // namespace bssl
@@ -3867,9 +3921,3 @@
void SSL_CTX_set_rsa_pss_rsae_certs_enabled(SSL_CTX *ctx, int enabled) {
ctx->rsa_pss_rsae_certs_enabled = !!enabled;
}
-
-int SSL_extension_supported(unsigned extension_value) {
- uint32_t index;
- return extension_value == TLSEXT_TYPE_padding ||
- tls_extension_find(&index, extension_value) != NULL;
-}
diff --git a/src/ssl/test/CMakeLists.txt b/src/ssl/test/CMakeLists.txt
index 3b07903..425b43b 100644
--- a/src/ssl/test/CMakeLists.txt
+++ b/src/ssl/test/CMakeLists.txt
@@ -5,10 +5,34 @@
async_bio.cc
bssl_shim.cc
+ handshake_util.cc
packeted_bio.cc
+ settings_writer.cc
test_config.cc
+ test_state.cc
$<TARGET_OBJECTS:test_support>
)
target_link_libraries(bssl_shim ssl crypto)
+
+if(UNIX AND NOT APPLE AND NOT ANDROID)
+ add_executable(
+ handshaker
+
+ async_bio.cc
+ handshake_util.cc
+ handshaker.cc
+ packeted_bio.cc
+ settings_writer.cc
+ test_config.cc
+ test_state.cc
+
+ $<TARGET_OBJECTS:test_support>
+ )
+
+ target_link_libraries(handshaker ssl crypto)
+else()
+ # Declare a dummy target for run_tests to depend on.
+ add_custom_target(handshaker)
+endif()
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 824dc94..009bdeb 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -64,12 +64,16 @@
#include "../../crypto/internal.h"
#include "../internal.h"
#include "async_bio.h"
-#include "fuzzer_tags.h"
+#include "handshake_util.h"
#include "packeted_bio.h"
+#include "settings_writer.h"
#include "test_config.h"
+#include "test_state.h"
+#if defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
+#define HANDSHAKER_SUPPORTED
+#endif
-static CRYPTO_BUFFER_POOL *g_pool = nullptr;
#if !defined(OPENSSL_WINDOWS)
static int closesocket(int sock) {
@@ -90,350 +94,6 @@
return 1;
}
-struct TestState {
- // async_bio is async BIO which pauses reads and writes.
- BIO *async_bio = nullptr;
- // packeted_bio is the packeted BIO which simulates read timeouts.
- BIO *packeted_bio = nullptr;
- bssl::UniquePtr<EVP_PKEY> channel_id;
- bool cert_ready = false;
- bssl::UniquePtr<SSL_SESSION> session;
- bssl::UniquePtr<SSL_SESSION> pending_session;
- bool early_callback_called = false;
- bool handshake_done = false;
- // private_key is the underlying private key used when testing custom keys.
- bssl::UniquePtr<EVP_PKEY> private_key;
- std::vector<uint8_t> private_key_result;
- // private_key_retries is the number of times an asynchronous private key
- // operation has been retried.
- unsigned private_key_retries = 0;
- bool got_new_session = false;
- bssl::UniquePtr<SSL_SESSION> new_session;
- bool ticket_decrypt_done = false;
- bool alpn_select_done = false;
- bool is_resume = false;
- bool early_callback_ready = false;
- bool custom_verify_ready = false;
- std::string msg_callback_text;
- bool msg_callback_ok = true;
- // cert_verified is true if certificate verification has been driven to
- // completion. This tests that the callback is not called again after this.
- bool cert_verified = false;
-};
-
-static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
- int index, long argl, void *argp) {
- delete ((TestState *)ptr);
-}
-
-static int g_config_index = 0;
-static int g_state_index = 0;
-
-static bool SetTestConfig(SSL *ssl, const TestConfig *config) {
- return SSL_set_ex_data(ssl, g_config_index, (void *)config) == 1;
-}
-
-static const TestConfig *GetTestConfig(const SSL *ssl) {
- return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index);
-}
-
-static bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state) {
- // |SSL_set_ex_data| takes ownership of |state| only on success.
- if (SSL_set_ex_data(ssl, g_state_index, state.get()) == 1) {
- state.release();
- return true;
- }
- return false;
-}
-
-static TestState *GetTestState(const SSL *ssl) {
- return (TestState *)SSL_get_ex_data(ssl, g_state_index);
-}
-
-static bool MoveExData(SSL *dest, SSL *src) {
- TestState *state = GetTestState(src);
- const TestConfig *config = GetTestConfig(src);
- if (!SSL_set_ex_data(src, g_state_index, nullptr) ||
- !SSL_set_ex_data(dest, g_state_index, state) ||
- !SSL_set_ex_data(src, g_config_index, nullptr) ||
- !SSL_set_ex_data(dest, g_config_index, (void *) config)) {
- return false;
- }
-
- return true;
-}
-
-static void MoveBIOs(SSL *dest, SSL *src) {
- BIO *rbio = SSL_get_rbio(src);
- BIO_up_ref(rbio);
- SSL_set0_rbio(dest, rbio);
-
- BIO *wbio = SSL_get_wbio(src);
- BIO_up_ref(wbio);
- SSL_set0_wbio(dest, wbio);
-
- SSL_set0_rbio(src, nullptr);
- SSL_set0_wbio(src, nullptr);
-}
-
-static bool LoadCertificate(bssl::UniquePtr<X509> *out_x509,
- bssl::UniquePtr<STACK_OF(X509)> *out_chain,
- const std::string &file) {
- bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
- if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
- return false;
- }
-
- out_x509->reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
- if (!*out_x509) {
- return false;
- }
-
- out_chain->reset(sk_X509_new_null());
- if (!*out_chain) {
- return false;
- }
-
- // Keep reading the certificate chain.
- for (;;) {
- bssl::UniquePtr<X509> cert(
- PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
- if (!cert) {
- break;
- }
-
- if (!sk_X509_push(out_chain->get(), cert.get())) {
- return false;
- }
- cert.release(); // sk_X509_push takes ownership.
- }
-
- uint32_t err = ERR_peek_last_error();
- if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
- ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
- return false;
-}
-
- ERR_clear_error();
- return true;
-}
-
-static bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) {
- bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
- if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
- return nullptr;
- }
- return bssl::UniquePtr<EVP_PKEY>(
- PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL));
-}
-
-static bool FromHexDigit(uint8_t *out, char c) {
- if ('0' <= c && c <= '9') {
- *out = c - '0';
- return true;
- }
- if ('a' <= c && c <= 'f') {
- *out = c - 'a' + 10;
- return true;
- }
- if ('A' <= c && c <= 'F') {
- *out = c - 'A' + 10;
- return true;
- }
- return false;
-}
-
-static bool HexDecode(std::string *out, const std::string &in) {
- if ((in.size() & 1) != 0) {
- return false;
- }
-
- std::unique_ptr<uint8_t[]> buf(new uint8_t[in.size() / 2]);
- for (size_t i = 0; i < in.size() / 2; i++) {
- uint8_t high, low;
- if (!FromHexDigit(&high, in[i*2]) ||
- !FromHexDigit(&low, in[i*2+1])) {
- return false;
- }
- buf[i] = (high << 4) | low;
- }
-
- out->assign(reinterpret_cast<const char *>(buf.get()), in.size() / 2);
- return true;
-}
-
-static std::vector<std::string> SplitParts(const std::string &in,
- const char delim) {
- std::vector<std::string> ret;
- size_t start = 0;
-
- for (size_t i = 0; i < in.size(); i++) {
- if (in[i] == delim) {
- ret.push_back(in.substr(start, i - start));
- start = i + 1;
- }
- }
-
- ret.push_back(in.substr(start, std::string::npos));
- return ret;
-}
-
-static std::vector<std::string> DecodeHexStrings(
- const std::string &hex_strings) {
- std::vector<std::string> ret;
- const std::vector<std::string> parts = SplitParts(hex_strings, ',');
-
- for (const auto &part : parts) {
- std::string binary;
- if (!HexDecode(&binary, part)) {
- fprintf(stderr, "Bad hex string: %s\n", part.c_str());
- return ret;
- }
-
- ret.push_back(binary);
- }
-
- return ret;
-}
-
-static bssl::UniquePtr<STACK_OF(X509_NAME)> DecodeHexX509Names(
- const std::string &hex_names) {
- const std::vector<std::string> der_names = DecodeHexStrings(hex_names);
- bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null());
- if (!ret) {
- return nullptr;
- }
-
- for (const auto &der_name : der_names) {
- const uint8_t *const data =
- reinterpret_cast<const uint8_t *>(der_name.data());
- const uint8_t *derp = data;
- bssl::UniquePtr<X509_NAME> name(
- d2i_X509_NAME(nullptr, &derp, der_name.size()));
- if (!name || derp != data + der_name.size()) {
- fprintf(stderr, "Failed to parse X509_NAME.\n");
- return nullptr;
- }
-
- if (!sk_X509_NAME_push(ret.get(), name.get())) {
- return nullptr;
- }
- name.release();
- }
-
- return ret;
-}
-
-static ssl_private_key_result_t AsyncPrivateKeySign(
- SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
- uint16_t signature_algorithm, const uint8_t *in, size_t in_len) {
- TestState *test_state = GetTestState(ssl);
- if (!test_state->private_key_result.empty()) {
- fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n");
- abort();
- }
-
- if (EVP_PKEY_id(test_state->private_key.get()) !=
- SSL_get_signature_algorithm_key_type(signature_algorithm)) {
- fprintf(stderr, "Key type does not match signature algorithm.\n");
- abort();
- }
-
- // Determine the hash.
- const EVP_MD *md = SSL_get_signature_algorithm_digest(signature_algorithm);
- bssl::ScopedEVP_MD_CTX ctx;
- EVP_PKEY_CTX *pctx;
- if (!EVP_DigestSignInit(ctx.get(), &pctx, md, nullptr,
- test_state->private_key.get())) {
- return ssl_private_key_failure;
- }
-
- // Configure additional signature parameters.
- if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) {
- if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
- !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* salt len = hash len */)) {
- return ssl_private_key_failure;
- }
- }
-
- // Write the signature into |test_state|.
- size_t len = 0;
- if (!EVP_DigestSign(ctx.get(), nullptr, &len, in, in_len)) {
- return ssl_private_key_failure;
- }
- test_state->private_key_result.resize(len);
- if (!EVP_DigestSign(ctx.get(), test_state->private_key_result.data(), &len,
- in, in_len)) {
- return ssl_private_key_failure;
- }
- test_state->private_key_result.resize(len);
-
- // The signature will be released asynchronously in |AsyncPrivateKeyComplete|.
- return ssl_private_key_retry;
-}
-
-static ssl_private_key_result_t AsyncPrivateKeyDecrypt(
- SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
- const uint8_t *in, size_t in_len) {
- TestState *test_state = GetTestState(ssl);
- if (!test_state->private_key_result.empty()) {
- fprintf(stderr,
- "AsyncPrivateKeyDecrypt called with operation pending.\n");
- abort();
- }
-
- RSA *rsa = EVP_PKEY_get0_RSA(test_state->private_key.get());
- if (rsa == NULL) {
- fprintf(stderr,
- "AsyncPrivateKeyDecrypt called with incorrect key type.\n");
- abort();
- }
- test_state->private_key_result.resize(RSA_size(rsa));
- if (!RSA_decrypt(rsa, out_len, test_state->private_key_result.data(),
- RSA_size(rsa), in, in_len, RSA_NO_PADDING)) {
- return ssl_private_key_failure;
- }
-
- test_state->private_key_result.resize(*out_len);
-
- // The decryption will be released asynchronously in |AsyncPrivateComplete|.
- return ssl_private_key_retry;
-}
-
-static ssl_private_key_result_t AsyncPrivateKeyComplete(
- SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) {
- TestState *test_state = GetTestState(ssl);
- if (test_state->private_key_result.empty()) {
- fprintf(stderr,
- "AsyncPrivateKeyComplete called without operation pending.\n");
- abort();
- }
-
- if (test_state->private_key_retries < 2) {
- // Only return the decryption on the second attempt, to test both incomplete
- // |decrypt| and |decrypt_complete|.
- return ssl_private_key_retry;
- }
-
- if (max_out < test_state->private_key_result.size()) {
- fprintf(stderr, "Output buffer too small.\n");
- return ssl_private_key_failure;
- }
- OPENSSL_memcpy(out, test_state->private_key_result.data(),
- test_state->private_key_result.size());
- *out_len = test_state->private_key_result.size();
-
- test_state->private_key_result.clear();
- test_state->private_key_retries = 0;
- return ssl_private_key_success;
-}
-
-static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = {
- AsyncPrivateKeySign,
- AsyncPrivateKeyDecrypt,
- AsyncPrivateKeyComplete,
-};
-
template<typename T>
struct Free {
void operator()(T *buf) {
@@ -441,635 +101,6 @@
}
};
-static bool GetCertificate(SSL *ssl, bssl::UniquePtr<X509> *out_x509,
- bssl::UniquePtr<STACK_OF(X509)> *out_chain,
- bssl::UniquePtr<EVP_PKEY> *out_pkey) {
- const TestConfig *config = GetTestConfig(ssl);
-
- if (!config->signing_prefs.empty()) {
- std::vector<uint16_t> u16s(config->signing_prefs.begin(),
- config->signing_prefs.end());
- if (!SSL_set_signing_algorithm_prefs(ssl, u16s.data(), u16s.size())) {
- return false;
- }
- }
-
- if (!config->key_file.empty()) {
- *out_pkey = LoadPrivateKey(config->key_file.c_str());
- if (!*out_pkey) {
- return false;
- }
- }
- if (!config->cert_file.empty() &&
- !LoadCertificate(out_x509, out_chain, config->cert_file.c_str())) {
- return false;
- }
- if (!config->ocsp_response.empty() &&
- !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(),
- config->ocsp_response.size())) {
- return false;
- }
- return true;
-}
-
-static bool InstallCertificate(SSL *ssl) {
- bssl::UniquePtr<X509> x509;
- bssl::UniquePtr<STACK_OF(X509)> chain;
- bssl::UniquePtr<EVP_PKEY> pkey;
- if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
- return false;
- }
-
- if (pkey) {
- TestState *test_state = GetTestState(ssl);
- const TestConfig *config = GetTestConfig(ssl);
- if (config->async) {
- test_state->private_key = std::move(pkey);
- SSL_set_private_key_method(ssl, &g_async_private_key_method);
- } else if (!SSL_use_PrivateKey(ssl, pkey.get())) {
- return false;
- }
- }
-
- if (x509 && !SSL_use_certificate(ssl, x509.get())) {
- return false;
- }
-
- if (sk_X509_num(chain.get()) > 0 &&
- !SSL_set1_chain(ssl, chain.get())) {
- return false;
- }
-
- return true;
-}
-
-static enum ssl_select_cert_result_t SelectCertificateCallback(
- const SSL_CLIENT_HELLO *client_hello) {
- const TestConfig *config = GetTestConfig(client_hello->ssl);
- GetTestState(client_hello->ssl)->early_callback_called = true;
-
- if (!config->expected_server_name.empty()) {
- const uint8_t *extension_data;
- size_t extension_len;
- CBS extension, server_name_list, host_name;
- uint8_t name_type;
-
- if (!SSL_early_callback_ctx_extension_get(
- client_hello, TLSEXT_TYPE_server_name, &extension_data,
- &extension_len)) {
- fprintf(stderr, "Could not find server_name extension.\n");
- return ssl_select_cert_error;
- }
-
- CBS_init(&extension, extension_data, extension_len);
- if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) ||
- CBS_len(&extension) != 0 ||
- !CBS_get_u8(&server_name_list, &name_type) ||
- name_type != TLSEXT_NAMETYPE_host_name ||
- !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
- CBS_len(&server_name_list) != 0) {
- fprintf(stderr, "Could not decode server_name extension.\n");
- return ssl_select_cert_error;
- }
-
- if (!CBS_mem_equal(&host_name,
- (const uint8_t*)config->expected_server_name.data(),
- config->expected_server_name.size())) {
- fprintf(stderr, "Server name mismatch.\n");
- }
- }
-
- if (config->fail_early_callback) {
- return ssl_select_cert_error;
- }
-
- // Install the certificate in the early callback.
- if (config->use_early_callback) {
- bool early_callback_ready =
- GetTestState(client_hello->ssl)->early_callback_ready;
- if (config->async && !early_callback_ready) {
- // Install the certificate asynchronously.
- return ssl_select_cert_retry;
- }
- if (!InstallCertificate(client_hello->ssl)) {
- return ssl_select_cert_error;
- }
- }
- return ssl_select_cert_success;
-}
-
-static bool CheckCertificateRequest(SSL *ssl) {
- const TestConfig *config = GetTestConfig(ssl);
-
- if (!config->expected_certificate_types.empty()) {
- const uint8_t *certificate_types;
- size_t certificate_types_len =
- SSL_get0_certificate_types(ssl, &certificate_types);
- if (certificate_types_len != config->expected_certificate_types.size() ||
- OPENSSL_memcmp(certificate_types,
- config->expected_certificate_types.data(),
- certificate_types_len) != 0) {
- fprintf(stderr, "certificate types mismatch\n");
- return false;
- }
- }
-
- if (!config->expected_client_ca_list.empty()) {
- bssl::UniquePtr<STACK_OF(X509_NAME)> expected =
- DecodeHexX509Names(config->expected_client_ca_list);
- const size_t num_expected = sk_X509_NAME_num(expected.get());
-
- const STACK_OF(X509_NAME) *received = SSL_get_client_CA_list(ssl);
- const size_t num_received = sk_X509_NAME_num(received);
-
- if (num_received != num_expected) {
- fprintf(stderr, "expected %u names in CertificateRequest but got %u\n",
- static_cast<unsigned>(num_expected),
- static_cast<unsigned>(num_received));
- return false;
- }
-
- for (size_t i = 0; i < num_received; i++) {
- if (X509_NAME_cmp(sk_X509_NAME_value(received, i),
- sk_X509_NAME_value(expected.get(), i)) != 0) {
- fprintf(stderr, "names in CertificateRequest differ at index #%d\n",
- static_cast<unsigned>(i));
- return false;
- }
- }
-
- STACK_OF(CRYPTO_BUFFER) *buffers = SSL_get0_server_requested_CAs(ssl);
- if (sk_CRYPTO_BUFFER_num(buffers) != num_received) {
- fprintf(stderr,
- "Mismatch between SSL_get_server_requested_CAs and "
- "SSL_get_client_CA_list.\n");
- return false;
- }
- }
-
- return true;
-}
-
-static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) {
- if (!CheckCertificateRequest(ssl)) {
- return -1;
- }
-
- if (GetTestConfig(ssl)->async && !GetTestState(ssl)->cert_ready) {
- return -1;
- }
-
- bssl::UniquePtr<X509> x509;
- bssl::UniquePtr<STACK_OF(X509)> chain;
- bssl::UniquePtr<EVP_PKEY> pkey;
- if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
- return -1;
- }
-
- // Return zero for no certificate.
- if (!x509) {
- return 0;
- }
-
- // Chains and asynchronous private keys are not supported with client_cert_cb.
- *out_x509 = x509.release();
- *out_pkey = pkey.release();
- return 1;
-}
-
-static int CertCallback(SSL *ssl, void *arg) {
- const TestConfig *config = GetTestConfig(ssl);
-
- // Check the CertificateRequest metadata is as expected.
- if (!SSL_is_server(ssl) && !CheckCertificateRequest(ssl)) {
- return -1;
- }
-
- if (config->fail_cert_callback) {
- return 0;
- }
-
- // The certificate will be installed via other means.
- if (!config->async || config->use_early_callback) {
- return 1;
- }
-
- if (!GetTestState(ssl)->cert_ready) {
- return -1;
- }
- if (!InstallCertificate(ssl)) {
- return 0;
- }
- return 1;
-}
-
-static bool CheckVerifyCallback(SSL *ssl) {
- const TestConfig *config = GetTestConfig(ssl);
- if (!config->expected_ocsp_response.empty()) {
- const uint8_t *data;
- size_t len;
- SSL_get0_ocsp_response(ssl, &data, &len);
- if (len == 0) {
- fprintf(stderr, "OCSP response not available in verify callback\n");
- return false;
- }
- }
-
- if (GetTestState(ssl)->cert_verified) {
- fprintf(stderr, "Certificate verified twice.\n");
- return false;
- }
-
- return true;
-}
-
-static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) {
- SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx,
- SSL_get_ex_data_X509_STORE_CTX_idx());
- const TestConfig *config = GetTestConfig(ssl);
- if (!CheckVerifyCallback(ssl)) {
- return 0;
- }
-
- GetTestState(ssl)->cert_verified = true;
- if (config->verify_fail) {
- store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION;
- return 0;
- }
-
- return 1;
-}
-
-static ssl_verify_result_t CustomVerifyCallback(SSL *ssl, uint8_t *out_alert) {
- const TestConfig *config = GetTestConfig(ssl);
- if (!CheckVerifyCallback(ssl)) {
- return ssl_verify_invalid;
- }
-
- if (config->async && !GetTestState(ssl)->custom_verify_ready) {
- return ssl_verify_retry;
- }
-
- GetTestState(ssl)->cert_verified = true;
- if (config->verify_fail) {
- return ssl_verify_invalid;
- }
-
- return ssl_verify_ok;
-}
-
-static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out,
- unsigned int *out_len, void *arg) {
- const TestConfig *config = GetTestConfig(ssl);
- if (config->advertise_npn.empty()) {
- return SSL_TLSEXT_ERR_NOACK;
- }
-
- *out = (const uint8_t*)config->advertise_npn.data();
- *out_len = config->advertise_npn.size();
- return SSL_TLSEXT_ERR_OK;
-}
-
-static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen,
- const uint8_t* in, unsigned inlen, void* arg) {
- const TestConfig *config = GetTestConfig(ssl);
- if (config->select_next_proto.empty()) {
- return SSL_TLSEXT_ERR_NOACK;
- }
-
- *out = (uint8_t*)config->select_next_proto.data();
- *outlen = config->select_next_proto.size();
- return SSL_TLSEXT_ERR_OK;
-}
-
-static int AlpnSelectCallback(SSL* ssl, const uint8_t** out, uint8_t* outlen,
- const uint8_t* in, unsigned inlen, void* arg) {
- if (GetTestState(ssl)->alpn_select_done) {
- fprintf(stderr, "AlpnSelectCallback called after completion.\n");
- exit(1);
- }
-
- GetTestState(ssl)->alpn_select_done = true;
-
- const TestConfig *config = GetTestConfig(ssl);
- if (config->decline_alpn) {
- return SSL_TLSEXT_ERR_NOACK;
- }
-
- if (!config->expected_advertised_alpn.empty() &&
- (config->expected_advertised_alpn.size() != inlen ||
- OPENSSL_memcmp(config->expected_advertised_alpn.data(), in, inlen) !=
- 0)) {
- fprintf(stderr, "bad ALPN select callback inputs\n");
- exit(1);
- }
-
- *out = (const uint8_t*)config->select_alpn.data();
- *outlen = config->select_alpn.size();
- return SSL_TLSEXT_ERR_OK;
-}
-
-static unsigned PskClientCallback(SSL *ssl, const char *hint,
- char *out_identity,
- unsigned max_identity_len,
- uint8_t *out_psk, unsigned max_psk_len) {
- const TestConfig *config = GetTestConfig(ssl);
-
- if (config->psk_identity.empty()) {
- if (hint != nullptr) {
- fprintf(stderr, "Server PSK hint was non-null.\n");
- return 0;
- }
- } else if (hint == nullptr ||
- strcmp(hint, config->psk_identity.c_str()) != 0) {
- fprintf(stderr, "Server PSK hint did not match.\n");
- return 0;
- }
-
- // Account for the trailing '\0' for the identity.
- if (config->psk_identity.size() >= max_identity_len ||
- config->psk.size() > max_psk_len) {
- fprintf(stderr, "PSK buffers too small\n");
- return 0;
- }
-
- BUF_strlcpy(out_identity, config->psk_identity.c_str(),
- max_identity_len);
- OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size());
- return config->psk.size();
-}
-
-static unsigned PskServerCallback(SSL *ssl, const char *identity,
- uint8_t *out_psk, unsigned max_psk_len) {
- const TestConfig *config = GetTestConfig(ssl);
-
- if (strcmp(identity, config->psk_identity.c_str()) != 0) {
- fprintf(stderr, "Client PSK identity did not match.\n");
- return 0;
- }
-
- if (config->psk.size() > max_psk_len) {
- fprintf(stderr, "PSK buffers too small\n");
- return 0;
- }
-
- OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size());
- return config->psk.size();
-}
-
-static timeval g_clock;
-
-static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) {
- *out_clock = g_clock;
-}
-
-static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) {
- *out_pkey = GetTestState(ssl)->channel_id.release();
-}
-
-static SSL_SESSION *GetSessionCallback(SSL *ssl, const uint8_t *data, int len,
- int *copy) {
- TestState *async_state = GetTestState(ssl);
- if (async_state->session) {
- *copy = 0;
- return async_state->session.release();
- } else if (async_state->pending_session) {
- return SSL_magic_pending_session_ptr();
- } else {
- return NULL;
- }
-}
-
-static int DDoSCallback(const SSL_CLIENT_HELLO *client_hello) {
- const TestConfig *config = GetTestConfig(client_hello->ssl);
- static int callback_num = 0;
-
- callback_num++;
- if (config->fail_ddos_callback ||
- (config->fail_second_ddos_callback && callback_num == 2)) {
- return 0;
- }
- return 1;
-}
-
-static void InfoCallback(const SSL *ssl, int type, int val) {
- if (type == SSL_CB_HANDSHAKE_DONE) {
- if (GetTestConfig(ssl)->handshake_never_done) {
- fprintf(stderr, "Handshake unexpectedly completed.\n");
- // Abort before any expected error code is printed, to ensure the overall
- // test fails.
- abort();
- }
- // This callback is called when the handshake completes. |SSL_get_session|
- // must continue to work and |SSL_in_init| must return false.
- if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) {
- fprintf(stderr, "Invalid state for SSL_CB_HANDSHAKE_DONE.\n");
- abort();
- }
- GetTestState(ssl)->handshake_done = true;
-
- // Callbacks may be called again on a new handshake.
- GetTestState(ssl)->ticket_decrypt_done = false;
- GetTestState(ssl)->alpn_select_done = false;
- }
-}
-
-static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) {
- // This callback is called as the handshake completes. |SSL_get_session|
- // must continue to work and, historically, |SSL_in_init| returned false at
- // this point.
- if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) {
- fprintf(stderr, "Invalid state for NewSessionCallback.\n");
- abort();
- }
-
- GetTestState(ssl)->got_new_session = true;
- GetTestState(ssl)->new_session.reset(session);
- return 1;
-}
-
-static int TicketKeyCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv,
- EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
- int encrypt) {
- if (!encrypt) {
- if (GetTestState(ssl)->ticket_decrypt_done) {
- fprintf(stderr, "TicketKeyCallback called after completion.\n");
- return -1;
- }
-
- GetTestState(ssl)->ticket_decrypt_done = true;
- }
-
- // This is just test code, so use the all-zeros key.
- static const uint8_t kZeros[16] = {0};
-
- if (encrypt) {
- OPENSSL_memcpy(key_name, kZeros, sizeof(kZeros));
- RAND_bytes(iv, 16);
- } else if (OPENSSL_memcmp(key_name, kZeros, 16) != 0) {
- return 0;
- }
-
- if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) ||
- !EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) {
- return -1;
- }
-
- if (!encrypt) {
- return GetTestConfig(ssl)->renew_ticket ? 2 : 1;
- }
- return 1;
-}
-
-// kCustomExtensionValue is the extension value that the custom extension
-// callbacks will add.
-static const uint16_t kCustomExtensionValue = 1234;
-static void *const kCustomExtensionAddArg =
- reinterpret_cast<void *>(kCustomExtensionValue);
-static void *const kCustomExtensionParseArg =
- reinterpret_cast<void *>(kCustomExtensionValue + 1);
-static const char kCustomExtensionContents[] = "custom extension";
-
-static int CustomExtensionAddCallback(SSL *ssl, unsigned extension_value,
- const uint8_t **out, size_t *out_len,
- int *out_alert_value, void *add_arg) {
- if (extension_value != kCustomExtensionValue ||
- add_arg != kCustomExtensionAddArg) {
- abort();
- }
-
- if (GetTestConfig(ssl)->custom_extension_skip) {
- return 0;
- }
- if (GetTestConfig(ssl)->custom_extension_fail_add) {
- return -1;
- }
-
- *out = reinterpret_cast<const uint8_t*>(kCustomExtensionContents);
- *out_len = sizeof(kCustomExtensionContents) - 1;
-
- return 1;
-}
-
-static void CustomExtensionFreeCallback(SSL *ssl, unsigned extension_value,
- const uint8_t *out, void *add_arg) {
- if (extension_value != kCustomExtensionValue ||
- add_arg != kCustomExtensionAddArg ||
- out != reinterpret_cast<const uint8_t *>(kCustomExtensionContents)) {
- abort();
- }
-}
-
-static int CustomExtensionParseCallback(SSL *ssl, unsigned extension_value,
- const uint8_t *contents,
- size_t contents_len,
- int *out_alert_value, void *parse_arg) {
- if (extension_value != kCustomExtensionValue ||
- parse_arg != kCustomExtensionParseArg) {
- abort();
- }
-
- if (contents_len != sizeof(kCustomExtensionContents) - 1 ||
- OPENSSL_memcmp(contents, kCustomExtensionContents, contents_len) != 0) {
- *out_alert_value = SSL_AD_DECODE_ERROR;
- return 0;
- }
-
- return 1;
-}
-
-static int ServerNameCallback(SSL *ssl, int *out_alert, void *arg) {
- // SNI must be accessible from the SNI callback.
- const TestConfig *config = GetTestConfig(ssl);
- const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
- if (server_name == nullptr ||
- std::string(server_name) != config->expected_server_name) {
- fprintf(stderr, "servername mismatch (got %s; want %s)\n", server_name,
- config->expected_server_name.c_str());
- return SSL_TLSEXT_ERR_ALERT_FATAL;
- }
-
- return SSL_TLSEXT_ERR_OK;
-}
-
-static void MessageCallback(int is_write, int version, int content_type,
- const void *buf, size_t len, SSL *ssl, void *arg) {
- const uint8_t *buf_u8 = reinterpret_cast<const uint8_t *>(buf);
- const TestConfig *config = GetTestConfig(ssl);
- TestState *state = GetTestState(ssl);
- if (!state->msg_callback_ok) {
- return;
- }
-
- if (content_type == SSL3_RT_HEADER) {
- if (len !=
- (config->is_dtls ? DTLS1_RT_HEADER_LENGTH : SSL3_RT_HEADER_LENGTH)) {
- fprintf(stderr, "Incorrect length for record header: %zu\n", len);
- state->msg_callback_ok = false;
- }
- return;
- }
-
- state->msg_callback_text += is_write ? "write " : "read ";
- switch (content_type) {
- case 0:
- if (version != SSL2_VERSION) {
- fprintf(stderr, "Incorrect version for V2ClientHello: %x\n", version);
- state->msg_callback_ok = false;
- return;
- }
- state->msg_callback_text += "v2clienthello\n";
- return;
-
- case SSL3_RT_HANDSHAKE: {
- CBS cbs;
- CBS_init(&cbs, buf_u8, len);
- uint8_t type;
- uint32_t msg_len;
- if (!CBS_get_u8(&cbs, &type) ||
- // TODO(davidben): Reporting on entire messages would be more
- // consistent than fragments.
- (config->is_dtls &&
- !CBS_skip(&cbs, 3 /* total */ + 2 /* seq */ + 3 /* frag_off */)) ||
- !CBS_get_u24(&cbs, &msg_len) ||
- !CBS_skip(&cbs, msg_len) ||
- CBS_len(&cbs) != 0) {
- fprintf(stderr, "Could not parse handshake message.\n");
- state->msg_callback_ok = false;
- return;
- }
- char text[16];
- snprintf(text, sizeof(text), "hs %d\n", type);
- state->msg_callback_text += text;
- return;
- }
-
- case SSL3_RT_CHANGE_CIPHER_SPEC:
- if (len != 1 || buf_u8[0] != 1) {
- fprintf(stderr, "Invalid ChangeCipherSpec.\n");
- state->msg_callback_ok = false;
- return;
- }
- state->msg_callback_text += "ccs\n";
- return;
-
- case SSL3_RT_ALERT:
- if (len != 2) {
- fprintf(stderr, "Invalid alert.\n");
- state->msg_callback_ok = false;
- return;
- }
- char text[16];
- snprintf(text, sizeof(text), "alert %d %d\n", buf_u8[0], buf_u8[1]);
- state->msg_callback_text += text;
- return;
-
- default:
- fprintf(stderr, "Invalid content_type: %d\n", content_type);
- state->msg_callback_ok = false;
- }
-}
-
// Connect returns a new socket connected to localhost on |port| or -1 on
// error.
static int Connect(uint16_t port) {
@@ -1147,270 +178,6 @@
const int sock_;
};
-static void ssl_ctx_add_session(SSL_SESSION *session, void *void_param) {
- SSL_CTX *ctx = reinterpret_cast<SSL_CTX *>(void_param);
- bssl::UniquePtr<SSL_SESSION> new_session = bssl::SSL_SESSION_dup(
- session, SSL_SESSION_INCLUDE_NONAUTH | SSL_SESSION_INCLUDE_TICKET);
- if (new_session != nullptr) {
- SSL_CTX_add_session(ctx, new_session.get());
- }
-}
-
-static bssl::UniquePtr<SSL_CTX> SetupCtx(SSL_CTX *old_ctx,
- const TestConfig *config) {
- bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(
- config->is_dtls ? DTLS_method() : TLS_method()));
- if (!ssl_ctx) {
- return nullptr;
- }
-
- SSL_CTX_set0_buffer_pool(ssl_ctx.get(), g_pool);
-
- // Enable SSL 3.0 and TLS 1.3 for tests.
- if (!config->is_dtls &&
- (!SSL_CTX_set_min_proto_version(ssl_ctx.get(), SSL3_VERSION) ||
- !SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION))) {
- return nullptr;
- }
-
- std::string cipher_list = "ALL";
- if (!config->cipher.empty()) {
- cipher_list = config->cipher;
- SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);
- }
- if (!SSL_CTX_set_strict_cipher_list(ssl_ctx.get(), cipher_list.c_str())) {
- return nullptr;
- }
-
- if (config->async && config->is_server) {
- // Disable the internal session cache. To test asynchronous session lookup,
- // we use an external session cache.
- SSL_CTX_set_session_cache_mode(
- ssl_ctx.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL);
- SSL_CTX_sess_set_get_cb(ssl_ctx.get(), GetSessionCallback);
- } else {
- SSL_CTX_set_session_cache_mode(ssl_ctx.get(), SSL_SESS_CACHE_BOTH);
- }
-
- SSL_CTX_set_select_certificate_cb(ssl_ctx.get(), SelectCertificateCallback);
-
- if (config->use_old_client_cert_callback) {
- SSL_CTX_set_client_cert_cb(ssl_ctx.get(), ClientCertCallback);
- }
-
- SSL_CTX_set_next_protos_advertised_cb(
- ssl_ctx.get(), NextProtosAdvertisedCallback, NULL);
- if (!config->select_next_proto.empty()) {
- SSL_CTX_set_next_proto_select_cb(ssl_ctx.get(), NextProtoSelectCallback,
- NULL);
- }
-
- if (!config->select_alpn.empty() || config->decline_alpn) {
- SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
- }
-
- SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
-
- SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
-
- SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
- SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
-
- if (config->use_ticket_callback) {
- SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx.get(), TicketKeyCallback);
- }
-
- if (config->enable_client_custom_extension &&
- !SSL_CTX_add_client_custom_ext(
- ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback,
- CustomExtensionFreeCallback, kCustomExtensionAddArg,
- CustomExtensionParseCallback, kCustomExtensionParseArg)) {
- return nullptr;
- }
-
- if (config->enable_server_custom_extension &&
- !SSL_CTX_add_server_custom_ext(
- ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback,
- CustomExtensionFreeCallback, kCustomExtensionAddArg,
- CustomExtensionParseCallback, kCustomExtensionParseArg)) {
- return nullptr;
- }
-
- if (!config->use_custom_verify_callback) {
- SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), CertVerifyCallback, NULL);
- }
-
- if (!config->signed_cert_timestamps.empty() &&
- !SSL_CTX_set_signed_cert_timestamp_list(
- ssl_ctx.get(), (const uint8_t *)config->signed_cert_timestamps.data(),
- config->signed_cert_timestamps.size())) {
- return nullptr;
- }
-
- if (!config->use_client_ca_list.empty()) {
- if (config->use_client_ca_list == "<NULL>") {
- SSL_CTX_set_client_CA_list(ssl_ctx.get(), nullptr);
- } else if (config->use_client_ca_list == "<EMPTY>") {
- bssl::UniquePtr<STACK_OF(X509_NAME)> names;
- SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release());
- } else {
- bssl::UniquePtr<STACK_OF(X509_NAME)> names =
- DecodeHexX509Names(config->use_client_ca_list);
- SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release());
- }
- }
-
- if (config->enable_grease) {
- SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1);
- }
-
- if (!config->expected_server_name.empty()) {
- SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback);
- }
-
- if (!config->ticket_key.empty() &&
- !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), config->ticket_key.data(),
- config->ticket_key.size())) {
- return nullptr;
- }
-
- if (config->enable_early_data) {
- SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1);
- }
-
- SSL_CTX_set_tls13_variant(
- ssl_ctx.get(), static_cast<enum tls13_variant_t>(config->tls13_variant));
-
- if (config->allow_unknown_alpn_protos) {
- SSL_CTX_set_allow_unknown_alpn_protos(ssl_ctx.get(), 1);
- }
-
- if (config->enable_ed25519) {
- SSL_CTX_set_ed25519_enabled(ssl_ctx.get(), 1);
- }
- if (config->no_rsa_pss_rsae_certs) {
- SSL_CTX_set_rsa_pss_rsae_certs_enabled(ssl_ctx.get(), 0);
- }
-
- if (!config->verify_prefs.empty()) {
- std::vector<uint16_t> u16s(config->verify_prefs.begin(),
- config->verify_prefs.end());
- if (!SSL_CTX_set_verify_algorithm_prefs(ssl_ctx.get(), u16s.data(),
- u16s.size())) {
- return nullptr;
- }
- }
-
- SSL_CTX_set_msg_callback(ssl_ctx.get(), MessageCallback);
-
- if (config->allow_false_start_without_alpn) {
- SSL_CTX_set_false_start_allowed_without_alpn(ssl_ctx.get(), 1);
- }
-
- if (old_ctx) {
- uint8_t keys[48];
- if (!SSL_CTX_get_tlsext_ticket_keys(old_ctx, &keys, sizeof(keys)) ||
- !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), keys, sizeof(keys))) {
- return nullptr;
- }
- lh_SSL_SESSION_doall_arg(old_ctx->sessions, ssl_ctx_add_session,
- ssl_ctx.get());
- }
-
- return ssl_ctx;
-}
-
-// RetryAsync is called after a failed operation on |ssl| with return code
-// |ret|. If the operation should be retried, it simulates one asynchronous
-// event and returns true. Otherwise it returns false.
-static bool RetryAsync(SSL *ssl, int ret) {
- // No error; don't retry.
- if (ret >= 0) {
- return false;
- }
-
- TestState *test_state = GetTestState(ssl);
- assert(GetTestConfig(ssl)->async);
-
- if (test_state->packeted_bio != nullptr &&
- PacketedBioAdvanceClock(test_state->packeted_bio)) {
- // The DTLS retransmit logic silently ignores write failures. So the test
- // may progress, allow writes through synchronously.
- AsyncBioEnforceWriteQuota(test_state->async_bio, false);
- int timeout_ret = DTLSv1_handle_timeout(ssl);
- AsyncBioEnforceWriteQuota(test_state->async_bio, true);
-
- if (timeout_ret < 0) {
- fprintf(stderr, "Error retransmitting.\n");
- return false;
- }
- return true;
- }
-
- // See if we needed to read or write more. If so, allow one byte through on
- // the appropriate end to maximally stress the state machine.
- switch (SSL_get_error(ssl, ret)) {
- case SSL_ERROR_WANT_READ:
- AsyncBioAllowRead(test_state->async_bio, 1);
- return true;
- case SSL_ERROR_WANT_WRITE:
- AsyncBioAllowWrite(test_state->async_bio, 1);
- return true;
- case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: {
- bssl::UniquePtr<EVP_PKEY> pkey =
- LoadPrivateKey(GetTestConfig(ssl)->send_channel_id);
- if (!pkey) {
- return false;
- }
- test_state->channel_id = std::move(pkey);
- return true;
- }
- case SSL_ERROR_WANT_X509_LOOKUP:
- test_state->cert_ready = true;
- return true;
- case SSL_ERROR_PENDING_SESSION:
- test_state->session = std::move(test_state->pending_session);
- return true;
- case SSL_ERROR_PENDING_CERTIFICATE:
- test_state->early_callback_ready = true;
- return true;
- case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION:
- test_state->private_key_retries++;
- return true;
- case SSL_ERROR_WANT_CERTIFICATE_VERIFY:
- test_state->custom_verify_ready = true;
- return true;
- default:
- return false;
- }
-}
-
-// CheckIdempotentError runs |func|, an operation on |ssl|, ensuring that
-// errors are idempotent.
-static int CheckIdempotentError(const char *name, SSL *ssl,
- std::function<int()> func) {
- int ret = func();
- int ssl_err = SSL_get_error(ssl, ret);
- uint32_t err = ERR_peek_error();
- if (ssl_err == SSL_ERROR_SSL || ssl_err == SSL_ERROR_ZERO_RETURN) {
- int ret2 = func();
- int ssl_err2 = SSL_get_error(ssl, ret2);
- uint32_t err2 = ERR_peek_error();
- if (ret != ret2 || ssl_err != ssl_err2 || err != err2) {
- fprintf(stderr, "Repeating %s did not replay the error.\n", name);
- char buf[256];
- ERR_error_string_n(err, buf, sizeof(buf));
- fprintf(stderr, "Wanted: %d %d %s\n", ret, ssl_err, buf);
- ERR_error_string_n(err2, buf, sizeof(buf));
- fprintf(stderr, "Got: %d %d %s\n", ret2, ssl_err2, buf);
- // runner treats exit code 90 as always failing. Otherwise, it may
- // accidentally consider the result an expected protocol failure.
- exit(90);
- }
- }
- return ret;
-}
-
// DoRead reads from |ssl|, resolving any asynchronous operations. It returns
// the result value of the final |SSL_read| call.
static int DoRead(SSL *ssl, uint8_t *out, size_t max_out) {
@@ -1564,7 +331,7 @@
if (!sk_X509_insert(expect_chain.get(), expect_leaf.get(), 0)) {
return false;
}
- X509_up_ref(expect_leaf.get()); // sk_X509_push takes ownership.
+ X509_up_ref(expect_leaf.get()); // sk_X509_insert takes ownership.
}
bssl::UniquePtr<X509> leaf(SSL_get_peer_certificate(ssl));
@@ -1590,7 +357,7 @@
}
}
- if (SSL_get_session(ssl)->peer_sha256_valid !=
+ if (!!SSL_SESSION_has_peer_sha256(SSL_get_session(ssl)) !=
config->expect_sha256_client_cert) {
fprintf(stderr,
"Unexpected SHA-256 client cert state: expected:%d is_resume:%d.\n",
@@ -1599,12 +366,30 @@
}
if (config->expect_sha256_client_cert &&
- SSL_get_session(ssl)->certs != nullptr) {
+ SSL_SESSION_get0_peer_certificates(SSL_get_session(ssl)) != nullptr) {
fprintf(stderr, "Have both client cert and SHA-256 hash: is_resume:%d.\n",
is_resume);
return false;
}
+ const uint8_t *peer_sha256;
+ size_t peer_sha256_len;
+ SSL_SESSION_get0_peer_sha256(SSL_get_session(ssl), &peer_sha256,
+ &peer_sha256_len);
+ if (SSL_SESSION_has_peer_sha256(SSL_get_session(ssl))) {
+ if (peer_sha256_len != 32) {
+ fprintf(stderr, "Peer SHA-256 hash had length %zu instead of 32\n",
+ peer_sha256_len);
+ return false;
+ }
+ } else {
+ if (peer_sha256_len != 0) {
+ fprintf(stderr, "Unexpected peer SHA-256 hash of length %zu\n",
+ peer_sha256_len);
+ return false;
+ }
+ }
+
return true;
}
@@ -1670,7 +455,10 @@
}
}
- if (config->is_server && !GetTestState(ssl)->early_callback_called) {
+ // early_callback_called is updated in the handshaker, so we don't see it
+ // here.
+ if (!config->handoff && config->is_server &&
+ !GetTestState(ssl)->early_callback_called) {
fprintf(stderr, "early callback not called\n");
return false;
}
@@ -1849,283 +637,10 @@
return true;
}
-static bool WriteSettings(int i, const TestConfig *config,
- const SSL_SESSION *session) {
- if (config->write_settings.empty()) {
- return true;
- }
-
- // Treat write_settings as a path prefix for each connection in the run.
- char buf[DECIMAL_SIZE(int)];
- snprintf(buf, sizeof(buf), "%d", i);
- std::string path = config->write_settings + buf;
-
- bssl::ScopedCBB cbb;
- if (!CBB_init(cbb.get(), 64)) {
- return false;
- }
-
- if (session != nullptr) {
- uint8_t *data;
- size_t len;
- if (!SSL_SESSION_to_bytes(session, &data, &len)) {
- return false;
- }
- bssl::UniquePtr<uint8_t> free_data(data);
- CBB child;
- if (!CBB_add_u16(cbb.get(), kSessionTag) ||
- !CBB_add_u24_length_prefixed(cbb.get(), &child) ||
- !CBB_add_bytes(&child, data, len) ||
- !CBB_flush(cbb.get())) {
- return false;
- }
- }
-
- if (config->is_server &&
- (config->require_any_client_certificate || config->verify_peer) &&
- !CBB_add_u16(cbb.get(), kRequestClientCert)) {
- return false;
- }
-
- if (config->tls13_variant != 0 &&
- (!CBB_add_u16(cbb.get(), kTLS13Variant) ||
- !CBB_add_u8(cbb.get(), static_cast<uint8_t>(config->tls13_variant)))) {
- return false;
- }
-
- uint8_t *settings;
- size_t settings_len;
- if (!CBB_add_u16(cbb.get(), kDataTag) ||
- !CBB_finish(cbb.get(), &settings, &settings_len)) {
- return false;
- }
- bssl::UniquePtr<uint8_t> free_settings(settings);
-
- using ScopedFILE = std::unique_ptr<FILE, decltype(&fclose)>;
- ScopedFILE file(fopen(path.c_str(), "w"), fclose);
- if (!file) {
- return false;
- }
-
- return fwrite(settings, settings_len, 1, file.get()) == 1;
-}
-
-static bssl::UniquePtr<SSL> NewSSL(SSL_CTX *ssl_ctx, const TestConfig *config,
- SSL_SESSION *session, bool is_resume,
- std::unique_ptr<TestState> test_state) {
- bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx));
- if (!ssl) {
- return nullptr;
- }
-
- if (!SetTestConfig(ssl.get(), config)) {
- return nullptr;
- }
- if (test_state != nullptr) {
- if (!SetTestState(ssl.get(), std::move(test_state))) {
- return nullptr;
- }
- GetTestState(ssl.get())->is_resume = is_resume;
- }
-
- if (config->fallback_scsv &&
- !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) {
- return nullptr;
- }
- // Install the certificate synchronously if nothing else will handle it.
- if (!config->use_early_callback &&
- !config->use_old_client_cert_callback &&
- !config->async &&
- !InstallCertificate(ssl.get())) {
- return nullptr;
- }
- if (!config->use_old_client_cert_callback) {
- SSL_set_cert_cb(ssl.get(), CertCallback, nullptr);
- }
- int mode = SSL_VERIFY_NONE;
- if (config->require_any_client_certificate) {
- mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- }
- if (config->verify_peer) {
- mode = SSL_VERIFY_PEER;
- }
- if (config->verify_peer_if_no_obc) {
- // Set SSL_VERIFY_FAIL_IF_NO_PEER_CERT so testing whether client
- // certificates were requested is easy.
- mode = SSL_VERIFY_PEER | SSL_VERIFY_PEER_IF_NO_OBC |
- SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- }
- if (config->use_custom_verify_callback) {
- SSL_set_custom_verify(ssl.get(), mode, CustomVerifyCallback);
- } else if (mode != SSL_VERIFY_NONE) {
- SSL_set_verify(ssl.get(), mode, NULL);
- }
- if (config->false_start) {
- SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START);
- }
- if (config->cbc_record_splitting) {
- SSL_set_mode(ssl.get(), SSL_MODE_CBC_RECORD_SPLITTING);
- }
- if (config->partial_write) {
- SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE);
- }
- if (config->no_tls13) {
- SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_3);
- }
- if (config->no_tls12) {
- SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2);
- }
- if (config->no_tls11) {
- SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_1);
- }
- if (config->no_tls1) {
- SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1);
- }
- if (config->no_ssl3) {
- SSL_set_options(ssl.get(), SSL_OP_NO_SSLv3);
- }
- if (!config->expected_channel_id.empty() ||
- config->enable_channel_id) {
- SSL_set_tls_channel_id_enabled(ssl.get(), 1);
- }
- if (!config->send_channel_id.empty()) {
- SSL_set_tls_channel_id_enabled(ssl.get(), 1);
- if (!config->async) {
- // The async case will be supplied by |ChannelIdCallback|.
- bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(config->send_channel_id);
- if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) {
- return nullptr;
- }
- }
- }
- if (!config->send_token_binding_params.empty()) {
- SSL_set_token_binding_params(ssl.get(),
- reinterpret_cast<const uint8_t *>(
- config->send_token_binding_params.data()),
- config->send_token_binding_params.length());
- }
- if (!config->host_name.empty() &&
- !SSL_set_tlsext_host_name(ssl.get(), config->host_name.c_str())) {
- return nullptr;
- }
- if (!config->advertise_alpn.empty() &&
- SSL_set_alpn_protos(ssl.get(),
- (const uint8_t *)config->advertise_alpn.data(),
- config->advertise_alpn.size()) != 0) {
- return nullptr;
- }
- if (!config->psk.empty()) {
- SSL_set_psk_client_callback(ssl.get(), PskClientCallback);
- SSL_set_psk_server_callback(ssl.get(), PskServerCallback);
- }
- if (!config->psk_identity.empty() &&
- !SSL_use_psk_identity_hint(ssl.get(), config->psk_identity.c_str())) {
- return nullptr;
- }
- if (!config->srtp_profiles.empty() &&
- !SSL_set_srtp_profiles(ssl.get(), config->srtp_profiles.c_str())) {
- return nullptr;
- }
- if (config->enable_ocsp_stapling) {
- SSL_enable_ocsp_stapling(ssl.get());
- }
- if (config->enable_signed_cert_timestamps) {
- SSL_enable_signed_cert_timestamps(ssl.get());
- }
- if (config->min_version != 0 &&
- !SSL_set_min_proto_version(ssl.get(), (uint16_t)config->min_version)) {
- return nullptr;
- }
- if (config->max_version != 0 &&
- !SSL_set_max_proto_version(ssl.get(), (uint16_t)config->max_version)) {
- return nullptr;
- }
- if (config->mtu != 0) {
- SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU);
- SSL_set_mtu(ssl.get(), config->mtu);
- }
- if (config->install_ddos_callback) {
- SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback);
- }
- if (config->renegotiate_once) {
- SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_once);
- }
- if (config->renegotiate_freely) {
- SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely);
- }
- if (config->renegotiate_ignore) {
- SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_ignore);
- }
- if (!config->check_close_notify) {
- SSL_set_quiet_shutdown(ssl.get(), 1);
- }
- if (config->p384_only) {
- int nid = NID_secp384r1;
- if (!SSL_set1_curves(ssl.get(), &nid, 1)) {
- return nullptr;
- }
- }
- if (config->enable_all_curves) {
- static const int kAllCurves[] = {
- NID_secp224r1, NID_X9_62_prime256v1, NID_secp384r1,
- NID_secp521r1, NID_X25519,
- };
- if (!SSL_set1_curves(ssl.get(), kAllCurves,
- OPENSSL_ARRAY_SIZE(kAllCurves))) {
- return nullptr;
- }
- }
- if (config->initial_timeout_duration_ms > 0) {
- DTLSv1_set_initial_timeout_duration(ssl.get(),
- config->initial_timeout_duration_ms);
- }
- if (config->max_cert_list > 0) {
- SSL_set_max_cert_list(ssl.get(), config->max_cert_list);
- }
- if (config->retain_only_sha256_client_cert) {
- SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
- }
- if (config->max_send_fragment > 0) {
- SSL_set_max_send_fragment(ssl.get(), config->max_send_fragment);
- }
- if (config->dummy_pq_padding_len > 0 &&
- !SSL_set_dummy_pq_padding_size(ssl.get(), config->dummy_pq_padding_len)) {
- return nullptr;
- }
- if (!config->quic_transport_params.empty()) {
- if (!SSL_set_quic_transport_params(
- ssl.get(),
- reinterpret_cast<const uint8_t *>(
- config->quic_transport_params.data()),
- config->quic_transport_params.size())) {
- return nullptr;
- }
- }
-
- if (session != NULL) {
- if (!config->is_server) {
- if (SSL_set_session(ssl.get(), session) != 1) {
- return nullptr;
- }
- } else if (config->async) {
- // The internal session cache is disabled, so install the session
- // manually.
- SSL_SESSION_up_ref(session);
- GetTestState(ssl.get())->pending_session.reset(session);
- }
- }
-
- if (SSL_get_current_cipher(ssl.get()) != nullptr) {
- fprintf(stderr, "non-null cipher before handshake\n");
- return nullptr;
- }
-
- return ssl;
-}
-
static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
bssl::UniquePtr<SSL> *ssl_uniqueptr,
- const TestConfig *config, bool is_resume, bool is_retry);
+ const TestConfig *config, bool is_resume, bool is_retry,
+ SettingsWriter *writer);
// DoConnection tests an SSL connection against the peer. On success, it returns
// true and sets |*out_session| to the negotiated SSL session. If the test is a
@@ -2134,9 +649,9 @@
static bool DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session,
SSL_CTX *ssl_ctx, const TestConfig *config,
const TestConfig *retry_config, bool is_resume,
- SSL_SESSION *session) {
- bssl::UniquePtr<SSL> ssl = NewSSL(ssl_ctx, config, session, is_resume,
- std::unique_ptr<TestState>(new TestState));
+ SSL_SESSION *session, SettingsWriter *writer) {
+ bssl::UniquePtr<SSL> ssl = config->NewSSL(
+ ssl_ctx, session, is_resume, std::unique_ptr<TestState>(new TestState));
if (!ssl) {
return false;
}
@@ -2158,7 +673,7 @@
return false;
}
if (config->is_dtls) {
- bssl::UniquePtr<BIO> packeted = PacketedBioCreate(&g_clock);
+ bssl::UniquePtr<BIO> packeted = PacketedBioCreate(GetClock());
if (!packeted) {
return false;
}
@@ -2179,7 +694,7 @@
SSL_set_bio(ssl.get(), bio.get(), bio.get());
bio.release(); // SSL_set_bio takes ownership.
- bool ret = DoExchange(out_session, &ssl, config, is_resume, false);
+ bool ret = DoExchange(out_session, &ssl, config, is_resume, false, writer);
if (!config->is_server && is_resume && config->expect_reject_early_data) {
// We must have failed due to an early data rejection.
if (ret) {
@@ -2214,7 +729,7 @@
}
assert(!config->handoff);
- ret = DoExchange(out_session, &ssl, retry_config, is_resume, true);
+ ret = DoExchange(out_session, &ssl, retry_config, is_resume, true, writer);
}
if (!ret) {
@@ -2237,73 +752,25 @@
return true;
}
-static bool HandoffReady(SSL *ssl, int ret) {
- return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDOFF;
-}
-
-static bool HandbackReady(SSL *ssl, int ret) {
- return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDBACK;
-}
-
static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
bssl::UniquePtr<SSL> *ssl_uniqueptr,
- const TestConfig *config, bool is_resume,
- bool is_retry) {
+ const TestConfig *config, bool is_resume, bool is_retry,
+ SettingsWriter *writer) {
int ret;
SSL *ssl = ssl_uniqueptr->get();
- SSL_CTX *session_ctx = ssl->ctx;
+ SSL_CTX *session_ctx = SSL_get_SSL_CTX(ssl);
if (!config->implicit_handshake) {
if (config->handoff) {
- bssl::UniquePtr<SSL_CTX> ctx_handoff = SetupCtx(ssl->ctx, config);
- if (!ctx_handoff) {
+#if defined(HANDSHAKER_SUPPORTED)
+ if (!DoSplitHandshake(ssl_uniqueptr, writer, is_resume)) {
return false;
}
- SSL_CTX_set_handoff_mode(ctx_handoff.get(), 1);
-
- bssl::UniquePtr<SSL> ssl_handoff =
- NewSSL(ctx_handoff.get(), config, nullptr, false, nullptr);
- if (!ssl_handoff) {
- return false;
- }
- SSL_set_accept_state(ssl_handoff.get());
- if (!MoveExData(ssl_handoff.get(), ssl)) {
- return false;
- }
- MoveBIOs(ssl_handoff.get(), ssl);
-
- do {
- ret = CheckIdempotentError("SSL_do_handshake", ssl_handoff.get(),
- [&]() -> int {
- return SSL_do_handshake(ssl_handoff.get());
- });
- } while (!HandoffReady(ssl_handoff.get(), ret) &&
- config->async &&
- RetryAsync(ssl_handoff.get(), ret));
-
- if (!HandoffReady(ssl_handoff.get(), ret)) {
- fprintf(stderr, "Handshake failed while waiting for handoff.\n");
- return false;
- }
-
- bssl::ScopedCBB cbb;
- bssl::Array<uint8_t> handoff;
- if (!CBB_init(cbb.get(), 512) ||
- !SSL_serialize_handoff(ssl_handoff.get(), cbb.get()) ||
- !CBBFinishArray(cbb.get(), &handoff)) {
- fprintf(stderr, "Handoff serialisation failed.\n");
- return false;
- }
-
- MoveBIOs(ssl, ssl_handoff.get());
- if (!MoveExData(ssl, ssl_handoff.get())) {
- return false;
- }
-
- if (!SSL_apply_handoff(ssl, handoff)) {
- fprintf(stderr, "Handoff application failed.\n");
- return false;
- }
+ ssl = ssl_uniqueptr->get();
+#else
+ fprintf(stderr, "The external handshaker can only be used on Linux\n");
+ return false;
+#endif
}
do {
@@ -2312,56 +779,15 @@
});
} while (config->async && RetryAsync(ssl, ret));
- if (config->handoff) {
- if (!HandbackReady(ssl, ret)) {
- fprintf(stderr, "Connection failed to handback.\n");
- return false;
- }
-
- bssl::ScopedCBB cbb;
- bssl::Array<uint8_t> handback;
- if (!CBB_init(cbb.get(), 512) ||
- !SSL_serialize_handback(ssl, cbb.get()) ||
- !CBBFinishArray(cbb.get(), &handback)) {
- fprintf(stderr, "Handback serialisation failed.\n");
- return false;
- }
-
- bssl::UniquePtr<SSL_CTX> ctx_handback = SetupCtx(ssl->ctx, config);
- if (!ctx_handback) {
- return false;
- }
- bssl::UniquePtr<SSL> ssl_handback =
- NewSSL(ctx_handback.get(), config, nullptr, false, nullptr);
- if (!ssl_handback) {
- return false;
- }
- MoveBIOs(ssl_handback.get(), ssl);
- if (!MoveExData(ssl_handback.get(), ssl)) {
- return false;
- }
-
- if (!SSL_apply_handback(ssl_handback.get(), handback)) {
- fprintf(stderr, "Applying handback failed.\n");
- return false;
- }
-
- *ssl_uniqueptr = std::move(ssl_handback);
- ssl = ssl_uniqueptr->get();
-
- do {
- ret = CheckIdempotentError("SSL_do_handshake", ssl, [&]() -> int {
- return SSL_do_handshake(ssl);
- });
- } while (config->async && RetryAsync(ssl, ret));
+ if (config->forbid_renegotiation_after_handshake) {
+ SSL_set_renegotiate_mode(ssl, ssl_renegotiate_never);
}
if (ret != 1 || !CheckHandshakeProperties(ssl, is_resume, config)) {
return false;
}
- lh_SSL_SESSION_doall_arg(ssl->ctx->sessions, ssl_ctx_add_session,
- session_ctx);
+ CopySessions(session_ctx, SSL_get_SSL_CTX(ssl));
if (is_resume && !is_retry && !config->is_server &&
config->expect_no_offer_early_data && SSL_in_early_data(ssl)) {
@@ -2671,11 +1097,6 @@
#endif
CRYPTO_library_init();
- g_config_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- g_state_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, TestStateExFree);
- if (g_config_index < 0 || g_state_index < 0) {
- return 1;
- }
TestConfig initial_config, resume_config, retry_config;
if (!ParseConfig(argc - 1, argv + 1, &initial_config, &resume_config,
@@ -2683,12 +1104,14 @@
return Usage(argv[0]);
}
- g_pool = CRYPTO_BUFFER_POOL_new();
-
- // Some code treats the zero time special, so initialize the clock to a
- // non-zero time.
- g_clock.tv_sec = 1234;
- g_clock.tv_usec = 1234;
+ if (initial_config.is_handshaker_supported) {
+#if defined(HANDSHAKER_SUPPORTED)
+ printf("Yes\n");
+#else
+ printf("No\n");
+#endif
+ return 0;
+ }
bssl::UniquePtr<SSL_CTX> ssl_ctx;
@@ -2696,7 +1119,7 @@
for (int i = 0; i < initial_config.resume_count + 1; i++) {
bool is_resume = i > 0;
TestConfig *config = is_resume ? &resume_config : &initial_config;
- ssl_ctx = SetupCtx(ssl_ctx.get(), config);
+ ssl_ctx = config->SetupCtx(ssl_ctx.get());
if (!ssl_ctx) {
ERR_print_errors_fp(stderr);
return 1;
@@ -2708,19 +1131,25 @@
}
bssl::UniquePtr<SSL_SESSION> offer_session = std::move(session);
- if (!WriteSettings(i, config, offer_session.get())) {
+ SettingsWriter writer;
+ if (!writer.Init(i, config, offer_session.get())) {
fprintf(stderr, "Error writing settings.\n");
return 1;
}
- if (!DoConnection(&session, ssl_ctx.get(), config, &retry_config, is_resume,
- offer_session.get())) {
+ bool ok = DoConnection(&session, ssl_ctx.get(), config, &retry_config,
+ is_resume, offer_session.get(), &writer);
+ if (!writer.Commit()) {
+ fprintf(stderr, "Error writing settings.\n");
+ return 1;
+ }
+ if (!ok) {
fprintf(stderr, "Connection %d failed.\n", i + 1);
ERR_print_errors_fp(stderr);
return 1;
}
if (config->resumption_delay != 0) {
- g_clock.tv_sec += config->resumption_delay;
+ AdvanceClock(config->resumption_delay);
}
}
diff --git a/src/ssl/test/fuzzer.h b/src/ssl/test/fuzzer.h
index c794c4c..1ca970d 100644
--- a/src/ssl/test/fuzzer.h
+++ b/src/ssl/test/fuzzer.h
@@ -30,9 +30,9 @@
#include <openssl/ssl.h>
#include <openssl/x509.h>
+#include "../internal.h"
#include "./fuzzer_tags.h"
-
namespace {
const uint8_t kP256KeyPKCS8[] = {
@@ -276,6 +276,19 @@
}
}
+ static void MoveBIOs(SSL *dest, SSL *src) {
+ BIO *rbio = SSL_get_rbio(src);
+ BIO_up_ref(rbio);
+ SSL_set0_rbio(dest, rbio);
+
+ BIO *wbio = SSL_get_wbio(src);
+ BIO_up_ref(wbio);
+ SSL_set0_wbio(dest, wbio);
+
+ SSL_set0_rbio(src, nullptr);
+ SSL_set0_wbio(src, nullptr);
+ }
+
int TestOneInput(const uint8_t *buf, size_t len) {
RAND_reset_for_fuzzing();
@@ -294,19 +307,63 @@
SSL_set_tlsext_host_name(ssl.get(), "hostname");
}
+ // ssl_handoff may or may not be used.
+ bssl::UniquePtr<SSL> ssl_handoff(SSL_new(ctx_.get()));
+ bssl::UniquePtr<SSL> ssl_handback(SSL_new(ctx_.get()));
+ SSL_set_accept_state(ssl_handoff.get());
+
SSL_set0_rbio(ssl.get(), MakeBIO(CBS_data(&cbs), CBS_len(&cbs)).release());
SSL_set0_wbio(ssl.get(), BIO_new(BIO_s_mem()));
- if (SSL_do_handshake(ssl.get()) == 1) {
+ SSL *ssl_handshake = ssl.get();
+ bool handshake_successful = false;
+ bool handback_successful = false;
+ for (;;) {
+ int ret = SSL_do_handshake(ssl_handshake);
+ if (ret < 0 && SSL_get_error(ssl_handshake, ret) == SSL_ERROR_HANDOFF) {
+ MoveBIOs(ssl_handoff.get(), ssl.get());
+ // Ordinarily we would call SSL_serialize_handoff(ssl.get(). But for
+ // fuzzing, use the serialized handoff that's getting fuzzed.
+ if (!SSL_apply_handoff(ssl_handoff.get(), handoff_)) {
+ if (debug_) {
+ fprintf(stderr, "Handoff failed.\n");
+ }
+ break;
+ }
+ ssl_handshake = ssl_handoff.get();
+ } else if (ret < 0 &&
+ SSL_get_error(ssl_handshake, ret) == SSL_ERROR_HANDBACK) {
+ MoveBIOs(ssl_handback.get(), ssl_handoff.get());
+ if (!SSL_apply_handback(ssl_handback.get(), handback_)) {
+ if (debug_) {
+ fprintf(stderr, "Handback failed.\n");
+ }
+ break;
+ }
+ handback_successful = true;
+ ssl_handshake = ssl_handback.get();
+ } else {
+ handshake_successful = ret == 1;
+ break;
+ }
+ }
+
+ if (debug_) {
+ if (!handshake_successful) {
+ fprintf(stderr, "Handshake failed.\n");
+ } else if (handback_successful) {
+ fprintf(stderr, "Handback successful.\n");
+ }
+ }
+
+ if (handshake_successful) {
// Keep reading application data until error or EOF.
uint8_t tmp[1024];
for (;;) {
- if (SSL_read(ssl.get(), tmp, sizeof(tmp)) <= 0) {
+ if (SSL_read(ssl_handshake, tmp, sizeof(tmp)) <= 0) {
break;
}
}
- } else if (debug_) {
- fprintf(stderr, "Handshake failed.\n");
}
if (debug_) {
@@ -352,11 +409,9 @@
if (!SSL_CTX_set_strict_cipher_list(ctx_.get(), "ALL:NULL-SHA")) {
return false;
}
- if (protocol_ == kTLS) {
- if (!SSL_CTX_set_max_proto_version(ctx_.get(), TLS1_3_VERSION) ||
- !SSL_CTX_set_min_proto_version(ctx_.get(), SSL3_VERSION)) {
- return false;
- }
+ if (protocol_ == kTLS &&
+ !SSL_CTX_set_max_proto_version(ctx_.get(), TLS1_3_VERSION)) {
+ return false;
}
SSL_CTX_set_early_data_enabled(ctx_.get(), 1);
@@ -389,6 +444,8 @@
// |ctx| is shared between runs, so we must clear any modifications to it
// made later on in this function.
SSL_CTX_flush_sessions(ctx_.get(), 0);
+ handoff_ = {};
+ handback_ = {};
bssl::UniquePtr<SSL> ssl(SSL_new(ctx_.get()));
if (role_ == kServer) {
@@ -442,6 +499,26 @@
break;
}
+ case kHandoffTag: {
+ CBS handoff;
+ if (!CBS_get_u24_length_prefixed(cbs, &handoff)) {
+ return nullptr;
+ }
+ handoff_.CopyFrom(handoff);
+ bssl::SSL_set_handoff_mode(ssl.get(), 1);
+ break;
+ }
+
+ case kHandbackTag: {
+ CBS handback;
+ if (!CBS_get_u24_length_prefixed(cbs, &handback)) {
+ return nullptr;
+ }
+ handback_.CopyFrom(handback);
+ bssl::SSL_set_handoff_mode(ssl.get(), 1);
+ break;
+ }
+
default:
return nullptr;
}
@@ -497,6 +574,7 @@
Protocol protocol_;
Role role_;
bssl::UniquePtr<SSL_CTX> ctx_;
+ bssl::Array<uint8_t> handoff_, handback_;
};
const BIO_METHOD TLSFuzzer::kBIOMethod = {
diff --git a/src/ssl/test/fuzzer_tags.h b/src/ssl/test/fuzzer_tags.h
index b161d80..c21aca3 100644
--- a/src/ssl/test/fuzzer_tags.h
+++ b/src/ssl/test/fuzzer_tags.h
@@ -42,4 +42,10 @@
// kTLS13Variant is followed by a u8 denoting the TLS 1.3 variant to configure.
static const uint16_t kTLS13Variant = 3;
+// kHandoffTag is followed by the output of |SSL_serialize_handoff|.
+static const uint16_t kHandoffTag = 4;
+
+// kHandbackTag is followed by te output of |SSL_serialize_handback|.
+static const uint16_t kHandbackTag = 5;
+
#endif // HEADER_SSL_TEST_FUZZER_TAGS
diff --git a/src/ssl/test/handshake_util.cc b/src/ssl/test/handshake_util.cc
new file mode 100644
index 0000000..f839653
--- /dev/null
+++ b/src/ssl/test/handshake_util.cc
@@ -0,0 +1,473 @@
+/* Copyright (c) 2018, 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 "handshake_util.h"
+
+#include <assert.h>
+#if defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
+#include <errno.h>
+#include <fcntl.h>
+#include <spawn.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+
+#include <functional>
+
+#include "async_bio.h"
+#include "packeted_bio.h"
+#include "test_config.h"
+#include "test_state.h"
+
+#include <openssl/ssl.h>
+
+using namespace bssl;
+
+bool RetryAsync(SSL *ssl, int ret) {
+ // No error; don't retry.
+ if (ret >= 0) {
+ return false;
+ }
+
+ TestState *test_state = GetTestState(ssl);
+ assert(GetTestConfig(ssl)->async);
+
+ if (test_state->packeted_bio != nullptr &&
+ PacketedBioAdvanceClock(test_state->packeted_bio)) {
+ // The DTLS retransmit logic silently ignores write failures. So the test
+ // may progress, allow writes through synchronously.
+ AsyncBioEnforceWriteQuota(test_state->async_bio, false);
+ int timeout_ret = DTLSv1_handle_timeout(ssl);
+ AsyncBioEnforceWriteQuota(test_state->async_bio, true);
+
+ if (timeout_ret < 0) {
+ fprintf(stderr, "Error retransmitting.\n");
+ return false;
+ }
+ return true;
+ }
+
+ // See if we needed to read or write more. If so, allow one byte through on
+ // the appropriate end to maximally stress the state machine.
+ switch (SSL_get_error(ssl, ret)) {
+ case SSL_ERROR_WANT_READ:
+ AsyncBioAllowRead(test_state->async_bio, 1);
+ return true;
+ case SSL_ERROR_WANT_WRITE:
+ AsyncBioAllowWrite(test_state->async_bio, 1);
+ return true;
+ case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: {
+ UniquePtr<EVP_PKEY> pkey =
+ LoadPrivateKey(GetTestConfig(ssl)->send_channel_id);
+ if (!pkey) {
+ return false;
+ }
+ test_state->channel_id = std::move(pkey);
+ return true;
+ }
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ test_state->cert_ready = true;
+ return true;
+ case SSL_ERROR_PENDING_SESSION:
+ test_state->session = std::move(test_state->pending_session);
+ return true;
+ case SSL_ERROR_PENDING_CERTIFICATE:
+ test_state->early_callback_ready = true;
+ return true;
+ case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION:
+ test_state->private_key_retries++;
+ return true;
+ case SSL_ERROR_WANT_CERTIFICATE_VERIFY:
+ test_state->custom_verify_ready = true;
+ return true;
+ default:
+ return false;
+ }
+}
+
+int CheckIdempotentError(const char *name, SSL *ssl,
+ std::function<int()> func) {
+ int ret = func();
+ int ssl_err = SSL_get_error(ssl, ret);
+ uint32_t err = ERR_peek_error();
+ if (ssl_err == SSL_ERROR_SSL || ssl_err == SSL_ERROR_ZERO_RETURN) {
+ int ret2 = func();
+ int ssl_err2 = SSL_get_error(ssl, ret2);
+ uint32_t err2 = ERR_peek_error();
+ if (ret != ret2 || ssl_err != ssl_err2 || err != err2) {
+ fprintf(stderr, "Repeating %s did not replay the error.\n", name);
+ char buf[256];
+ ERR_error_string_n(err, buf, sizeof(buf));
+ fprintf(stderr, "Wanted: %d %d %s\n", ret, ssl_err, buf);
+ ERR_error_string_n(err2, buf, sizeof(buf));
+ fprintf(stderr, "Got: %d %d %s\n", ret2, ssl_err2, buf);
+ // runner treats exit code 90 as always failing. Otherwise, it may
+ // accidentally consider the result an expected protocol failure.
+ exit(90);
+ }
+ }
+ return ret;
+}
+
+#if defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
+
+// MoveBIOs moves the |BIO|s of |src| to |dst|. It is used for handoff.
+static void MoveBIOs(SSL *dest, SSL *src) {
+ BIO *rbio = SSL_get_rbio(src);
+ BIO_up_ref(rbio);
+ SSL_set0_rbio(dest, rbio);
+
+ BIO *wbio = SSL_get_wbio(src);
+ BIO_up_ref(wbio);
+ SSL_set0_wbio(dest, wbio);
+
+ SSL_set0_rbio(src, nullptr);
+ SSL_set0_wbio(src, nullptr);
+}
+
+static bool HandoffReady(SSL *ssl, int ret) {
+ return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDOFF;
+}
+
+static ssize_t read_eintr(int fd, void *out, size_t len) {
+ ssize_t ret;
+ do {
+ ret = read(fd, out, len);
+ } while (ret < 0 && errno == EINTR);
+ return ret;
+}
+
+static ssize_t write_eintr(int fd, const void *in, size_t len) {
+ ssize_t ret;
+ do {
+ ret = write(fd, in, len);
+ } while (ret < 0 && errno == EINTR);
+ return ret;
+}
+
+static ssize_t waitpid_eintr(pid_t pid, int *wstatus, int options) {
+ pid_t ret;
+ do {
+ ret = waitpid(pid, wstatus, options);
+ } while (ret < 0 && errno == EINTR);
+ return ret;
+}
+
+// Proxy relays data between |socket|, which is connected to the client, and the
+// handshaker, which is connected to the numerically specified file descriptors,
+// until the handshaker returns control.
+static bool Proxy(BIO *socket, bool async, int control, int rfd, int wfd) {
+ for (;;) {
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(wfd, &rfds);
+ FD_SET(control, &rfds);
+ int fd_max = wfd > control ? wfd : control;
+ if (select(fd_max + 1, &rfds, nullptr, nullptr, nullptr) == -1) {
+ perror("select");
+ return false;
+ }
+
+ char buf[64];
+ ssize_t bytes;
+ if (FD_ISSET(wfd, &rfds) &&
+ (bytes = read_eintr(wfd, buf, sizeof(buf))) > 0) {
+ char *b = buf;
+ while (bytes) {
+ int written = BIO_write(socket, b, bytes);
+ if (!written) {
+ fprintf(stderr, "BIO_write wrote nothing\n");
+ return false;
+ }
+ if (written < 0) {
+ if (async) {
+ AsyncBioAllowWrite(socket, 1);
+ continue;
+ }
+ fprintf(stderr, "BIO_write failed\n");
+ return false;
+ }
+ b += written;
+ bytes -= written;
+ }
+ // Flush all pending data from the handshaker to the client before
+ // considering control messages.
+ continue;
+ }
+
+ if (!FD_ISSET(control, &rfds)) {
+ continue;
+ }
+
+ char msg;
+ if (read_eintr(control, &msg, 1) != 1) {
+ perror("read");
+ return false;
+ }
+ switch (msg) {
+ case kControlMsgHandback:
+ return true;
+ case kControlMsgError:
+ return false;
+ case kControlMsgWantRead:
+ break;
+ default:
+ fprintf(stderr, "Unknown control message from handshaker: %c\n", msg);
+ return false;
+ }
+
+ char readbuf[64];
+ if (async) {
+ AsyncBioAllowRead(socket, 1);
+ }
+ int read = BIO_read(socket, readbuf, sizeof(readbuf));
+ if (read < 1) {
+ fprintf(stderr, "BIO_read failed\n");
+ return false;
+ }
+ ssize_t written = write_eintr(rfd, readbuf, read);
+ if (written == -1) {
+ perror("write");
+ return false;
+ }
+ if (written != read) {
+ fprintf(stderr, "short write (%zu of %d bytes)\n", written, read);
+ return false;
+ }
+ // The handshaker blocks on the control channel, so we have to signal
+ // it that the data have been written.
+ msg = kControlMsgWriteCompleted;
+ if (write_eintr(control, &msg, 1) != 1) {
+ perror("write");
+ return false;
+ }
+ }
+}
+
+class ScopedFD {
+ public:
+ explicit ScopedFD(int fd): fd_(fd) {}
+ ~ScopedFD() { close(fd_); }
+ private:
+ const int fd_;
+};
+
+// RunHandshaker forks and execs the handshaker binary, handing off |input|,
+// and, after proxying some amount of handshake traffic, handing back |out|.
+static bool RunHandshaker(BIO *bio, const TestConfig *config, bool is_resume,
+ const Array<uint8_t> &input,
+ Array<uint8_t> *out) {
+ if (config->handshaker_path.empty()) {
+ fprintf(stderr, "no -handshaker-path specified\n");
+ return false;
+ }
+ struct stat dummy;
+ if (stat(config->handshaker_path.c_str(), &dummy) == -1) {
+ perror(config->handshaker_path.c_str());
+ return false;
+ }
+
+ // A datagram socket guarantees that writes are all-or-nothing.
+ int control[2];
+ if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, control) != 0) {
+ perror("socketpair");
+ return false;
+ }
+ int rfd[2], wfd[2];
+ // We use pipes, rather than some other mechanism, for their buffers. During
+ // the handshake, this process acts as a dumb proxy until receiving the
+ // handback signal, which arrives asynchronously. The race condition means
+ // that this process could incorrectly proxy post-handshake data from the
+ // client to the handshaker.
+ //
+ // To avoid this, this process never proxies data to the handshaker that the
+ // handshaker has not explicitly requested as a result of hitting
+ // |SSL_ERROR_WANT_READ|. Pipes allow the data to sit in a buffer while the
+ // two processes synchronize over the |control| channel.
+ if (pipe(rfd) != 0 || pipe(wfd) != 0) {
+ perror("pipe2");
+ return false;
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+
+ std::vector<char *> args;
+ bssl::UniquePtr<char> handshaker_path(
+ OPENSSL_strdup(config->handshaker_path.c_str()));
+ args.push_back(handshaker_path.get());
+ char resume[] = "-handshaker-resume";
+ if (is_resume) {
+ args.push_back(resume);
+ }
+ // config->argv omits argv[0].
+ for (int j = 0; j < config->argc; ++j) {
+ args.push_back(config->argv[j]);
+ }
+ args.push_back(nullptr);
+
+ posix_spawn_file_actions_t actions;
+ if (posix_spawn_file_actions_init(&actions) != 0 ||
+ posix_spawn_file_actions_addclose(&actions, control[0]) ||
+ posix_spawn_file_actions_addclose(&actions, rfd[1]) ||
+ posix_spawn_file_actions_addclose(&actions, wfd[0])) {
+ return false;
+ }
+ assert(kFdControl != rfd[0]);
+ assert(kFdControl != wfd[1]);
+ if (control[1] != kFdControl &&
+ posix_spawn_file_actions_adddup2(&actions, control[1], kFdControl) != 0) {
+ return false;
+ }
+ assert(kFdProxyToHandshaker != wfd[1]);
+ if (rfd[0] != kFdProxyToHandshaker &&
+ posix_spawn_file_actions_adddup2(&actions, rfd[0],
+ kFdProxyToHandshaker) != 0) {
+ return false;
+ }
+ if (wfd[1] != kFdHandshakerToProxy &&
+ posix_spawn_file_actions_adddup2(&actions, wfd[1],
+ kFdHandshakerToProxy) != 0) {
+ return false;
+ }
+
+ // MSan doesn't know that |posix_spawn| initializes its output, so initialize
+ // it to -1.
+ pid_t handshaker_pid = -1;
+ int ret = posix_spawn(&handshaker_pid, args[0], &actions, nullptr,
+ args.data(), nullptr);
+ if (posix_spawn_file_actions_destroy(&actions) != 0 ||
+ ret != 0) {
+ return false;
+ }
+
+ close(control[1]);
+ close(rfd[0]);
+ close(wfd[1]);
+ ScopedFD rfd_closer(rfd[1]);
+ ScopedFD wfd_closer(wfd[0]);
+ ScopedFD control_closer(control[0]);
+
+ if (write_eintr(control[0], input.data(), input.size()) == -1) {
+ perror("write");
+ return false;
+ }
+ bool ok = Proxy(bio, config->async, control[0], rfd[1], wfd[0]);
+ int wstatus;
+ if (waitpid_eintr(handshaker_pid, &wstatus, 0) != handshaker_pid) {
+ perror("waitpid");
+ return false;
+ }
+ if (ok && wstatus) {
+ fprintf(stderr, "handshaker exited irregularly\n");
+ return false;
+ }
+ if (!ok) {
+ return false; // This is a "good", i.e. expected, error.
+ }
+
+ constexpr size_t kBufSize = 1024 * 1024;
+ bssl::UniquePtr<uint8_t> buf((uint8_t *) OPENSSL_malloc(kBufSize));
+ int len = read_eintr(control[0], buf.get(), kBufSize);
+ if (len == -1) {
+ perror("read");
+ return false;
+ }
+ out->CopyFrom({buf.get(), (size_t)len});
+ return true;
+}
+
+// PrepareHandoff accepts the |ClientHello| from |ssl| and serializes state to
+// be passed to the handshaker. The serialized state includes both the SSL
+// handoff, as well test-related state.
+static bool PrepareHandoff(SSL *ssl, SettingsWriter *writer,
+ Array<uint8_t> *out_handoff) {
+ SSL_set_handoff_mode(ssl, 1);
+
+ const TestConfig *config = GetTestConfig(ssl);
+ int ret = -1;
+ do {
+ ret = CheckIdempotentError(
+ "SSL_do_handshake", ssl,
+ [&]() -> int { return SSL_do_handshake(ssl); });
+ } while (!HandoffReady(ssl, ret) &&
+ config->async &&
+ RetryAsync(ssl, ret));
+ if (!HandoffReady(ssl, ret)) {
+ fprintf(stderr, "Handshake failed while waiting for handoff.\n");
+ return false;
+ }
+
+ ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 512) ||
+ !SSL_serialize_handoff(ssl, cbb.get()) ||
+ !writer->WriteHandoff({CBB_data(cbb.get()), CBB_len(cbb.get())}) ||
+ !SerializeContextState(ssl->ctx.get(), cbb.get()) ||
+ !GetTestState(ssl)->Serialize(cbb.get())) {
+ fprintf(stderr, "Handoff serialisation failed.\n");
+ return false;
+ }
+ return CBBFinishArray(cbb.get(), out_handoff);
+}
+
+// DoSplitHandshake delegates the SSL handshake to a separate process, called
+// the handshaker. This process proxies I/O between the handshaker and the
+// client, using the |BIO| from |ssl|. After a successful handshake, |ssl| is
+// replaced with a new |SSL| object, in a way that is intended to be invisible
+// to the caller.
+bool DoSplitHandshake(UniquePtr<SSL> *ssl, SettingsWriter *writer,
+ bool is_resume) {
+ assert(SSL_get_rbio(ssl->get()) == SSL_get_wbio(ssl->get()));
+ Array<uint8_t> handshaker_input;
+ const TestConfig *config = GetTestConfig(ssl->get());
+ // out is the response from the handshaker, which includes a serialized
+ // handback message, but also serialized updates to the |TestState|.
+ Array<uint8_t> out;
+ if (!PrepareHandoff(ssl->get(), writer, &handshaker_input) ||
+ !RunHandshaker(SSL_get_rbio(ssl->get()), config, is_resume,
+ handshaker_input, &out)) {
+ fprintf(stderr, "Handoff failed.\n");
+ return false;
+ }
+
+ UniquePtr<SSL> ssl_handback =
+ config->NewSSL((*ssl)->ctx.get(), nullptr, false, nullptr);
+ if (!ssl_handback) {
+ return false;
+ }
+ CBS output, handback;
+ CBS_init(&output, out.data(), out.size());
+ if (!CBS_get_u24_length_prefixed(&output, &handback) ||
+ !DeserializeContextState(&output, ssl_handback->ctx.get()) ||
+ !SetTestState(ssl_handback.get(), TestState::Deserialize(
+ &output, ssl_handback->ctx.get())) ||
+ !GetTestState(ssl_handback.get()) ||
+ !writer->WriteHandback(handback) ||
+ !SSL_apply_handback(ssl_handback.get(), handback)) {
+ fprintf(stderr, "Handback failed.\n");
+ return false;
+ }
+ MoveBIOs(ssl_handback.get(), ssl->get());
+ GetTestState(ssl_handback.get())->async_bio =
+ GetTestState(ssl->get())->async_bio;
+ GetTestState(ssl->get())->async_bio = nullptr;
+
+ *ssl = std::move(ssl_handback);
+ return true;
+}
+
+#endif // defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
diff --git a/src/ssl/test/handshake_util.h b/src/ssl/test/handshake_util.h
new file mode 100644
index 0000000..4fb46db
--- /dev/null
+++ b/src/ssl/test/handshake_util.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2018, 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. */
+
+#ifndef HEADER_TEST_HANDSHAKE
+#define HEADER_TEST_HANDSHAKE
+
+#include <functional>
+
+#include <openssl/base.h>
+
+#include "settings_writer.h"
+
+// RetryAsync is called after a failed operation on |ssl| with return code
+// |ret|. If the operation should be retried, it simulates one asynchronous
+// event and returns true. Otherwise it returns false.
+bool RetryAsync(SSL *ssl, int ret);
+
+// CheckIdempotentError runs |func|, an operation on |ssl|, ensuring that
+// errors are idempotent.
+int CheckIdempotentError(const char *name, SSL *ssl, std::function<int()> func);
+
+// DoSplitHandshake delegates the SSL handshake to a separate process, called
+// the handshaker. This process proxies I/O between the handshaker and the
+// client, using the |BIO| from |ssl|. After a successful handshake, |ssl| is
+// replaced with a new |SSL| object, in a way that is intended to be invisible
+// to the caller.
+bool DoSplitHandshake(bssl::UniquePtr<SSL> *ssl, SettingsWriter *writer,
+ bool is_resume);
+
+// The protocol between the proxy and the handshaker is defined by these
+// single-character prefixes.
+constexpr char kControlMsgWantRead = 'R'; // Handshaker wants data
+constexpr char kControlMsgWriteCompleted = 'W'; // Proxy has sent data
+constexpr char kControlMsgHandback = 'H'; // Proxy should resume control
+constexpr char kControlMsgError = 'E'; // Handshaker hit an error
+
+// The protocol between the proxy and handshaker uses these file descriptors.
+constexpr int kFdControl = 3; // Bi-directional dgram socket.
+constexpr int kFdProxyToHandshaker = 4; // Uni-directional pipe.
+constexpr int kFdHandshakerToProxy = 5; // Uni-directional pipe.
+
+#endif // HEADER_TEST_HANDSHAKE
diff --git a/src/ssl/test/handshaker.cc b/src/ssl/test/handshaker.cc
new file mode 100644
index 0000000..9888876
--- /dev/null
+++ b/src/ssl/test/handshaker.cc
@@ -0,0 +1,170 @@
+/* Copyright (c) 2018, 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 <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <openssl/bytestring.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#include "../internal.h"
+#include "handshake_util.h"
+#include "test_config.h"
+#include "test_state.h"
+
+using namespace bssl;
+
+namespace {
+
+bool HandbackReady(SSL *ssl, int ret) {
+ return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDBACK;
+}
+
+bool Handshaker(const TestConfig *config, int rfd, int wfd,
+ Span<const uint8_t> input, int control) {
+ UniquePtr<SSL_CTX> ctx = config->SetupCtx(/*old_ctx=*/nullptr);
+ if (!ctx) {
+ return false;
+ }
+ UniquePtr<SSL> ssl = config->NewSSL(ctx.get(), nullptr, false, nullptr);
+
+ // Set |O_NONBLOCK| in order to break out of the loop when we hit
+ // |SSL_ERROR_WANT_READ|, so that we can send |kControlMsgWantRead| to the
+ // proxy.
+ if (fcntl(rfd, F_SETFL, O_NONBLOCK) != 0) {
+ perror("fcntl");
+ return false;
+ }
+ SSL_set_rfd(ssl.get(), rfd);
+ SSL_set_wfd(ssl.get(), wfd);
+
+ CBS cbs, handoff;
+ CBS_init(&cbs, input.data(), input.size());
+ if (!CBS_get_asn1_element(&cbs, &handoff, CBS_ASN1_SEQUENCE) ||
+ !DeserializeContextState(&cbs, ctx.get()) ||
+ !SetTestState(ssl.get(), TestState::Deserialize(&cbs, ctx.get())) ||
+ !GetTestState(ssl.get()) ||
+ !SSL_apply_handoff(ssl.get(), handoff)) {
+ fprintf(stderr, "Handoff application failed.\n");
+ return false;
+ }
+
+ int ret = 0;
+ for (;;) {
+ ret = CheckIdempotentError(
+ "SSL_do_handshake", ssl.get(),
+ [&]() -> int { return SSL_do_handshake(ssl.get()); });
+ if (SSL_get_error(ssl.get(), ret) == SSL_ERROR_WANT_READ) {
+ // Synchronize with the proxy, i.e. don't let the handshake continue until
+ // the proxy has sent more data.
+ char msg = kControlMsgWantRead;
+ if (write(control, &msg, 1) != 1 ||
+ read(control, &msg, 1) != 1 ||
+ msg != kControlMsgWriteCompleted) {
+ fprintf(stderr, "read via proxy failed\n");
+ return false;
+ }
+ continue;
+ }
+ if (!config->async || !RetryAsync(ssl.get(), ret)) {
+ break;
+ }
+ }
+ if (!HandbackReady(ssl.get(), ret)) {
+ ERR_print_errors_fp(stderr);
+ return false;
+ }
+
+ ScopedCBB output;
+ CBB handback;
+ Array<uint8_t> bytes;
+ if (!CBB_init(output.get(), 1024) ||
+ !CBB_add_u24_length_prefixed(output.get(), &handback) ||
+ !SSL_serialize_handback(ssl.get(), &handback) ||
+ !SerializeContextState(ssl->ctx.get(), output.get()) ||
+ !GetTestState(ssl.get())->Serialize(output.get()) ||
+ !CBBFinishArray(output.get(), &bytes)) {
+ fprintf(stderr, "Handback serialisation failed.\n");
+ return false;
+ }
+
+ char msg = kControlMsgHandback;
+ if (write(control, &msg, 1) == -1 ||
+ write(control, bytes.data(), bytes.size()) == -1) {
+ perror("write");
+ return false;
+ }
+ return true;
+}
+
+ssize_t read_eintr(int fd, void *out, size_t len) {
+ ssize_t ret;
+ do {
+ ret = read(fd, out, len);
+ } while (ret < 0 && errno == EINTR);
+ return ret;
+}
+
+ssize_t write_eintr(int fd, const void *in, size_t len) {
+ ssize_t ret;
+ do {
+ ret = write(fd, in, len);
+ } while (ret < 0 && errno == EINTR);
+ return ret;
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ TestConfig initial_config, resume_config, retry_config;
+ if (!ParseConfig(argc - 1, argv + 1, &initial_config, &resume_config,
+ &retry_config)) {
+ return 2;
+ }
+ const TestConfig *config = initial_config.handshaker_resume
+ ? &resume_config : &initial_config;
+#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+ if (initial_config.handshaker_resume) {
+ // If the PRNG returns exactly the same values when trying to resume then a
+ // "random" session ID will happen to exactly match the session ID
+ // "randomly" generated on the initial connection. The client will thus
+ // incorrectly believe that the server is resuming.
+ uint8_t byte;
+ RAND_bytes(&byte, 1);
+ }
+#endif // BORINGSSL_UNSAFE_DETERMINISTIC_MODE
+
+ // read() will return the entire message in one go, because it's a datagram
+ // socket.
+ constexpr size_t kBufSize = 1024 * 1024;
+ bssl::UniquePtr<uint8_t> buf((uint8_t *) OPENSSL_malloc(kBufSize));
+ ssize_t len = read_eintr(kFdControl, buf.get(), kBufSize);
+ if (len == -1) {
+ perror("read");
+ return 2;
+ }
+ Span<uint8_t> handoff(buf.get(), len);
+ if (!Handshaker(config, kFdProxyToHandshaker, kFdHandshakerToProxy, handoff,
+ kFdControl)) {
+ char msg = kControlMsgError;
+ if (write_eintr(kFdControl, &msg, 1) != 1) {
+ return 3;
+ }
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/ssl/test/runner/alert.go b/src/ssl/test/runner/alert.go
index 652e9ee..c79725e 100644
--- a/src/ssl/test/runner/alert.go
+++ b/src/ssl/test/runner/alert.go
@@ -15,69 +15,71 @@
)
const (
- alertCloseNotify alert = 0
- alertEndOfEarlyData alert = 1
- alertUnexpectedMessage alert = 10
- alertBadRecordMAC alert = 20
- alertDecryptionFailed alert = 21
- alertRecordOverflow alert = 22
- alertDecompressionFailure alert = 30
- alertHandshakeFailure alert = 40
- alertNoCertificate alert = 41
- alertBadCertificate alert = 42
- alertUnsupportedCertificate alert = 43
- alertCertificateRevoked alert = 44
- alertCertificateExpired alert = 45
- alertCertificateUnknown alert = 46
- alertIllegalParameter alert = 47
- alertUnknownCA alert = 48
- alertAccessDenied alert = 49
- alertDecodeError alert = 50
- alertDecryptError alert = 51
- alertProtocolVersion alert = 70
- alertInsufficientSecurity alert = 71
- alertInternalError alert = 80
- alertInappropriateFallback alert = 86
- alertUserCanceled alert = 90
- alertNoRenegotiation alert = 100
- alertMissingExtension alert = 109
- alertUnsupportedExtension alert = 110
- alertUnrecognizedName alert = 112
- alertUnknownPSKIdentity alert = 115
- alertCertificateRequired alert = 116
+ alertCloseNotify alert = 0
+ alertEndOfEarlyData alert = 1
+ alertUnexpectedMessage alert = 10
+ alertBadRecordMAC alert = 20
+ alertDecryptionFailed alert = 21
+ alertRecordOverflow alert = 22
+ alertDecompressionFailure alert = 30
+ alertHandshakeFailure alert = 40
+ alertNoCertificate alert = 41
+ alertBadCertificate alert = 42
+ alertUnsupportedCertificate alert = 43
+ alertCertificateRevoked alert = 44
+ alertCertificateExpired alert = 45
+ alertCertificateUnknown alert = 46
+ alertIllegalParameter alert = 47
+ alertUnknownCA alert = 48
+ alertAccessDenied alert = 49
+ alertDecodeError alert = 50
+ alertDecryptError alert = 51
+ alertProtocolVersion alert = 70
+ alertInsufficientSecurity alert = 71
+ alertInternalError alert = 80
+ alertInappropriateFallback alert = 86
+ alertUserCanceled alert = 90
+ alertNoRenegotiation alert = 100
+ alertMissingExtension alert = 109
+ alertUnsupportedExtension alert = 110
+ alertUnrecognizedName alert = 112
+ alertBadCertificateStatusResponse alert = 113
+ alertUnknownPSKIdentity alert = 115
+ alertCertificateRequired alert = 116
)
var alertText = map[alert]string{
- alertCloseNotify: "close notify",
- alertEndOfEarlyData: "end of early data",
- alertUnexpectedMessage: "unexpected message",
- alertBadRecordMAC: "bad record MAC",
- alertDecryptionFailed: "decryption failed",
- alertRecordOverflow: "record overflow",
- alertDecompressionFailure: "decompression failure",
- alertHandshakeFailure: "handshake failure",
- alertNoCertificate: "no certificate",
- alertBadCertificate: "bad certificate",
- alertUnsupportedCertificate: "unsupported certificate",
- alertCertificateRevoked: "revoked certificate",
- alertCertificateExpired: "expired certificate",
- alertCertificateUnknown: "unknown certificate",
- alertIllegalParameter: "illegal parameter",
- alertUnknownCA: "unknown certificate authority",
- alertAccessDenied: "access denied",
- alertDecodeError: "error decoding message",
- alertDecryptError: "error decrypting message",
- alertProtocolVersion: "protocol version not supported",
- alertInsufficientSecurity: "insufficient security level",
- alertInternalError: "internal error",
- alertInappropriateFallback: "inappropriate fallback",
- alertUserCanceled: "user canceled",
- alertNoRenegotiation: "no renegotiation",
- alertMissingExtension: "missing extension",
- alertUnsupportedExtension: "unsupported extension",
- alertUnrecognizedName: "unrecognized name",
- alertUnknownPSKIdentity: "unknown PSK identity",
- alertCertificateRequired: "certificate required",
+ alertCloseNotify: "close notify",
+ alertEndOfEarlyData: "end of early data",
+ alertUnexpectedMessage: "unexpected message",
+ alertBadRecordMAC: "bad record MAC",
+ alertDecryptionFailed: "decryption failed",
+ alertRecordOverflow: "record overflow",
+ alertDecompressionFailure: "decompression failure",
+ alertHandshakeFailure: "handshake failure",
+ alertNoCertificate: "no certificate",
+ alertBadCertificate: "bad certificate",
+ alertUnsupportedCertificate: "unsupported certificate",
+ alertCertificateRevoked: "revoked certificate",
+ alertCertificateExpired: "expired certificate",
+ alertCertificateUnknown: "unknown certificate",
+ alertIllegalParameter: "illegal parameter",
+ alertUnknownCA: "unknown certificate authority",
+ alertAccessDenied: "access denied",
+ alertDecodeError: "error decoding message",
+ alertDecryptError: "error decrypting message",
+ alertProtocolVersion: "protocol version not supported",
+ alertInsufficientSecurity: "insufficient security level",
+ alertInternalError: "internal error",
+ alertInappropriateFallback: "inappropriate fallback",
+ alertUserCanceled: "user canceled",
+ alertNoRenegotiation: "no renegotiation",
+ alertMissingExtension: "missing extension",
+ alertUnsupportedExtension: "unsupported extension",
+ alertBadCertificateStatusResponse: "bad certificate status response",
+ alertUnrecognizedName: "unrecognized name",
+ alertUnknownPSKIdentity: "unknown PSK identity",
+ alertCertificateRequired: "certificate required",
}
func (e alert) String() string {
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 7115b4d..aa17350 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -39,8 +39,9 @@
)
const (
- TLS13Draft23 = 0
- TLS13Draft28 = 1
+ TLS13Default = 0
+ TLS13Draft23 = 1
+ TLS13Draft28 = 2
)
var allTLSWireVersions = []uint16{
@@ -81,26 +82,27 @@
// TLS handshake message types.
const (
- typeHelloRequest uint8 = 0
- typeClientHello uint8 = 1
- typeServerHello uint8 = 2
- typeHelloVerifyRequest uint8 = 3
- typeNewSessionTicket uint8 = 4
- typeEndOfEarlyData uint8 = 5 // draft-ietf-tls-tls13-21
- typeHelloRetryRequest uint8 = 6 // draft-ietf-tls-tls13-16
- typeEncryptedExtensions uint8 = 8 // draft-ietf-tls-tls13-16
- typeCertificate uint8 = 11
- typeServerKeyExchange uint8 = 12
- typeCertificateRequest uint8 = 13
- typeServerHelloDone uint8 = 14
- typeCertificateVerify uint8 = 15
- typeClientKeyExchange uint8 = 16
- typeFinished uint8 = 20
- typeCertificateStatus uint8 = 22
- typeKeyUpdate uint8 = 24 // draft-ietf-tls-tls13-16
- typeNextProtocol uint8 = 67 // Not IANA assigned
- typeChannelID uint8 = 203 // Not IANA assigned
- typeMessageHash uint8 = 254 // draft-ietf-tls-tls13-21
+ typeHelloRequest uint8 = 0
+ typeClientHello uint8 = 1
+ typeServerHello uint8 = 2
+ typeHelloVerifyRequest uint8 = 3
+ typeNewSessionTicket uint8 = 4
+ typeEndOfEarlyData uint8 = 5 // draft-ietf-tls-tls13-21
+ typeHelloRetryRequest uint8 = 6 // draft-ietf-tls-tls13-16
+ typeEncryptedExtensions uint8 = 8 // draft-ietf-tls-tls13-16
+ typeCertificate uint8 = 11
+ typeServerKeyExchange uint8 = 12
+ typeCertificateRequest uint8 = 13
+ typeServerHelloDone uint8 = 14
+ typeCertificateVerify uint8 = 15
+ typeClientKeyExchange uint8 = 16
+ typeFinished uint8 = 20
+ typeCertificateStatus uint8 = 22
+ typeKeyUpdate uint8 = 24 // draft-ietf-tls-tls13-16
+ typeCompressedCertificate uint8 = 25 // Not IANA assigned
+ typeNextProtocol uint8 = 67 // Not IANA assigned
+ typeChannelID uint8 = 203 // Not IANA assigned
+ typeMessageHash uint8 = 254 // draft-ietf-tls-tls13-21
)
// TLS compression types.
@@ -121,7 +123,7 @@
extensionPadding uint16 = 21
extensionExtendedMasterSecret uint16 = 23
extensionTokenBinding uint16 = 24
- extensionQUICTransportParams uint16 = 26
+ extensionCompressedCertAlgs uint16 = 27
extensionSessionTicket uint16 = 35
extensionPreSharedKey uint16 = 41 // draft-ietf-tls-tls13-23
extensionEarlyData uint16 = 42 // draft-ietf-tls-tls13-23
@@ -134,8 +136,9 @@
extensionCustom uint16 = 1234 // not IANA assigned
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
extensionRenegotiationInfo uint16 = 0xff01
- extensionChannelID uint16 = 30032 // not IANA assigned
- extensionDummyPQPadding uint16 = 54537 // not IANA assigned
+ extensionQUICTransportParams uint16 = 0xffa5 // draft-ietf-quic-tls-13
+ extensionChannelID uint16 = 30032 // not IANA assigned
+ extensionDummyPQPadding uint16 = 54537 // not IANA assigned
)
// TLS signaling cipher suite values
@@ -329,6 +332,16 @@
Put(sessionId string, session *sessionState)
}
+// CertCompressionAlg is a certificate compression algorithm, specified as a
+// pair of functions for compressing and decompressing certificates.
+type CertCompressionAlg struct {
+ // Compress returns a compressed representation of the input.
+ Compress func([]byte) []byte
+ // Decompress depresses the contents of in and writes the result to out, which
+ // will be the correct size. It returns true on success and false otherwise.
+ Decompress func(out, in []byte) bool
+}
+
// A Config structure is used to configure a TLS client or server.
// After one has been passed to a TLS function it must not be
// modified. A Config may be reused; the tls package will also not
@@ -499,6 +512,8 @@
// transport parameters extension.
QUICTransportParams []byte
+ CertCompressionAlgs map[uint16]CertCompressionAlg
+
// Bugs specifies optional misbehaviour to be used for testing other
// implementations.
Bugs ProtocolBugs
@@ -1153,6 +1168,10 @@
// sessions use session tickets instead of session IDs.
RequireSessionTickets bool
+ // RequireSessionIDs, if true, causes the client to require new sessions use
+ // session IDs instead of session tickets.
+ RequireSessionIDs bool
+
// NullAllCiphers, if true, causes every cipher to behave like the null
// cipher.
NullAllCiphers bool
@@ -1395,9 +1414,15 @@
// specified value in ServerHello version field.
SendServerHelloVersion uint16
- // SendServerSupportedExtensionVersion, if non-zero, causes the server to send
- // the specified value in supported_versions extension in the ServerHello.
- SendServerSupportedExtensionVersion uint16
+ // SendServerSupportedVersionExtension, if non-zero, causes the server to send
+ // the specified value in supported_versions extension in the ServerHello (but
+ // not the HelloRetryRequest).
+ SendServerSupportedVersionExtension uint16
+
+ // OmitServerSupportedVersionExtension, if true, causes the server to
+ // omit the supported_versions extension in the ServerHello (but not the
+ // HelloRetryRequest)
+ OmitServerSupportedVersionExtension bool
// SkipHelloRetryRequest, if true, causes the TLS 1.3 server to not send
// HelloRetryRequest.
@@ -1499,6 +1524,15 @@
// length accepted from the peer.
MaxReceivePlaintext int
+ // ExpectPackedEncryptedHandshake, if non-zero, requires that the peer maximally
+ // pack their encrypted handshake messages, fitting at most the
+ // specified number of plaintext bytes per record.
+ ExpectPackedEncryptedHandshake int
+
+ // ForbidHandshakePacking, if true, requires the peer place a record
+ // boundary after every handshake message.
+ ForbidHandshakePacking bool
+
// SendTicketLifetime, if non-zero, is the ticket lifetime to send in
// NewSessionTicket messages.
SendTicketLifetime time.Duration
@@ -1580,6 +1614,22 @@
// SetX25519HighBit, if true, causes X25519 key shares to set their
// high-order bit.
SetX25519HighBit bool
+
+ // DuplicateCompressedCertAlgs, if true, causes two, equal, certificate
+ // compression algorithm IDs to be sent.
+ DuplicateCompressedCertAlgs bool
+
+ // ExpectedCompressedCert specifies the compression algorithm ID that must be
+ // used on this connection, or zero if there are no special requirements.
+ ExpectedCompressedCert uint16
+
+ // SendCertCompressionAlgId, if not zero, sets the algorithm ID that will be
+ // sent in the compressed certificate message.
+ SendCertCompressionAlgId uint16
+
+ // SendCertUncompressedLength, if not zero, sets the uncompressed length that
+ // will be sent in the compressed certificate message.
+ SendCertUncompressedLength uint32
}
func (c *Config) serverInit() {
@@ -1708,8 +1758,8 @@
// it returns true and the corresponding protocol version. Otherwise, it returns
// false.
func (c *Config) isSupportedVersion(wireVers uint16, isDTLS bool) (uint16, bool) {
- if (c.TLS13Variant != TLS13Draft23 && wireVers == tls13Draft23Version) ||
- (c.TLS13Variant != TLS13Draft28 && wireVers == tls13Draft28Version) {
+ if (c.TLS13Variant == TLS13Draft23 && wireVers == tls13Draft28Version) ||
+ (c.TLS13Variant == TLS13Draft28 && wireVers == tls13Draft23Version) {
return 0, false
}
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 9cd61eb..b6b6ffa 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -111,6 +111,11 @@
expectTLS13ChangeCipherSpec bool
+ // seenHandshakePackEnd is whether the most recent handshake record was
+ // not full for ExpectPackedEncryptedHandshake. If true, no more
+ // handshake data may be received until the next flight or epoch change.
+ seenHandshakePackEnd bool
+
tmp [16]byte
}
@@ -756,6 +761,7 @@
side = clientWrite
}
c.in.useTrafficSecret(version, suite, secret, side)
+ c.seenHandshakePackEnd = false
return nil
}
@@ -975,6 +981,13 @@
return c.in.setErrorLocked(err)
}
+ if typ != recordTypeHandshake {
+ c.seenHandshakePackEnd = false
+ } else if c.seenHandshakePackEnd {
+ c.in.freeBlock(b)
+ return c.in.setErrorLocked(errors.New("tls: peer violated ExpectPackedEncryptedHandshake"))
+ }
+
switch typ {
default:
c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
@@ -1037,6 +1050,9 @@
return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation))
}
c.hand.Write(data)
+ if pack := c.config.Bugs.ExpectPackedEncryptedHandshake; pack > 0 && len(data) < pack && c.out.cipher != nil {
+ c.seenHandshakePackEnd = true
+ }
}
if b != nil {
@@ -1095,6 +1111,7 @@
// to the connection and updates the record layer state.
// c.out.Mutex <= L.
func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
+ c.seenHandshakePackEnd = false
if typ == recordTypeHandshake {
msgType := data[0]
if c.config.Bugs.SendWrongMessageType != 0 && msgType == c.config.Bugs.SendWrongMessageType {
@@ -1304,6 +1321,9 @@
return nil, err
}
}
+ if c.hand.Len() > 4+n && c.config.Bugs.ForbidHandshakePacking {
+ return nil, errors.New("tls: forbidden trailing data after a handshake message")
+ }
return c.hand.Next(4 + n), nil
}
@@ -1348,9 +1368,11 @@
m = &certificateMsg{
hasRequestContext: c.vers >= VersionTLS13,
}
+ case typeCompressedCertificate:
+ m = new(compressedCertificateMsg)
case typeCertificateRequest:
m = &certificateRequestMsg{
- vers: c.wireVersion,
+ vers: c.wireVersion,
hasSignatureAlgorithm: c.vers >= VersionTLS12,
hasRequestContext: c.vers >= VersionTLS13,
}
@@ -1786,7 +1808,7 @@
if c.isDTLS && c.config.Bugs.SendSplitAlert {
c.conn.Write([]byte{
byte(recordTypeAlert), // type
- 0xfe, 0xff, // version
+ 0xfe, 0xff, // version
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // sequence
0x0, 0x2, // length
})
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 4178dc1..3e20d87 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -155,6 +155,15 @@
}
}
+ if c.config.Bugs.DuplicateCompressedCertAlgs {
+ hello.compressedCertAlgs = []uint16{1, 1}
+ } else if len(c.config.CertCompressionAlgs) > 0 {
+ hello.compressedCertAlgs = make([]uint16, 0, len(c.config.CertCompressionAlgs))
+ for id, _ := range c.config.CertCompressionAlgs {
+ hello.compressedCertAlgs = append(hello.compressedCertAlgs, uint16(id))
+ }
+ }
+
if c.noRenegotiationInfo() {
hello.secureRenegotiation = nil
}
@@ -590,7 +599,7 @@
}
if serverWireVersion != serverHello.vers {
- c.sendAlert(alertProtocolVersion)
+ c.sendAlert(alertIllegalParameter)
return fmt.Errorf("tls: server sent non-matching version %x vs %x", serverWireVersion, serverHello.vers)
}
@@ -720,6 +729,9 @@
if c.config.Bugs.RequireSessionTickets && len(hs.session.sessionTicket) == 0 {
return errors.New("tls: new session used session IDs instead of tickets")
}
+ if c.config.Bugs.RequireSessionIDs && len(hs.session.sessionId) == 0 {
+ return errors.New("tls: new session used session tickets instead of IDs")
+ }
sessionCache.Put(cacheKey, hs.session)
}
@@ -864,12 +876,46 @@
}
}
- certMsg, ok := msg.(*certificateMsg)
- if !ok {
- c.sendAlert(alertUnexpectedMessage)
- return unexpectedMessageError(certMsg, msg)
+ var certMsg *certificateMsg
+
+ if compressedCertMsg, ok := msg.(*compressedCertificateMsg); ok {
+ hs.writeServerHash(compressedCertMsg.marshal())
+
+ alg, ok := c.config.CertCompressionAlgs[compressedCertMsg.algID]
+ if !ok {
+ c.sendAlert(alertBadCertificate)
+ return fmt.Errorf("tls: received certificate compressed with unknown algorithm %x", compressedCertMsg.algID)
+ }
+
+ decompressed := make([]byte, 4+int(compressedCertMsg.uncompressedLength))
+ if !alg.Decompress(decompressed[4:], compressedCertMsg.compressed) {
+ c.sendAlert(alertBadCertificate)
+ return fmt.Errorf("tls: failed to decompress certificate with algorithm %x", compressedCertMsg.algID)
+ }
+
+ certMsg = &certificateMsg{
+ hasRequestContext: true,
+ }
+
+ if !certMsg.unmarshal(decompressed) {
+ c.sendAlert(alertBadCertificate)
+ return errors.New("tls: failed to parse decompressed certificate")
+ }
+
+ if expected := c.config.Bugs.ExpectedCompressedCert; expected != 0 && expected != compressedCertMsg.algID {
+ return fmt.Errorf("tls: expected certificate compressed with algorithm %x, but message used %x", expected, compressedCertMsg.algID)
+ }
+ } else {
+ if certMsg, ok = msg.(*certificateMsg); !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(certMsg, msg)
+ }
+ hs.writeServerHash(certMsg.marshal())
+
+ if c.config.Bugs.ExpectedCompressedCert != 0 {
+ return errors.New("tls: uncompressed certificate received")
+ }
}
- hs.writeServerHash(certMsg.marshal())
// Check for unsolicited extensions.
for i, cert := range certMsg.certificates {
@@ -1629,6 +1675,9 @@
if c.vers == VersionSSL30 {
return errors.New("tls: negotiated session tickets in SSL 3.0")
}
+ if c.config.Bugs.ExpectNoNewSessionTicket {
+ return errors.New("tls: received unexpected NewSessionTicket")
+ }
msg, err := c.readHandshake()
if err != nil {
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index 64936b4..5324d74 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -296,6 +296,7 @@
emptyExtensions bool
pad int
dummyPQPaddingLen int
+ compressedCertAlgs []uint16
}
func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -349,7 +350,8 @@
m.omitExtensions == m1.omitExtensions &&
m.emptyExtensions == m1.emptyExtensions &&
m.pad == m1.pad &&
- m.dummyPQPaddingLen == m1.dummyPQPaddingLen
+ m.dummyPQPaddingLen == m1.dummyPQPaddingLen &&
+ eqUint16s(m.compressedCertAlgs, m1.compressedCertAlgs)
}
func (m *clientHelloMsg) marshal() []byte {
@@ -586,6 +588,14 @@
body := extensions.addU16LengthPrefixed()
body.addBytes(make([]byte, l))
}
+ if len(m.compressedCertAlgs) > 0 {
+ extensions.addU16(extensionCompressedCertAlgs)
+ body := extensions.addU16LengthPrefixed()
+ algIDs := body.addU8LengthPrefixed()
+ for _, v := range m.compressedCertAlgs {
+ algIDs.addU16(v)
+ }
+ }
// The PSK extension must be last (draft-ietf-tls-tls13-18 section 4.2.6).
if len(m.pskIdentities) > 0 && !m.pskBinderFirst {
extensions.addU16(extensionPreSharedKey)
@@ -903,6 +913,24 @@
return false
}
m.dummyPQPaddingLen = len(body)
+ case extensionCompressedCertAlgs:
+ var algIDs byteReader
+ if !body.readU8LengthPrefixed(&algIDs) {
+ return false
+ }
+
+ seen := make(map[uint16]struct{})
+ for len(algIDs) > 0 {
+ var algID uint16
+ if !algIDs.readU16(&algID) {
+ return false
+ }
+ if _, ok := seen[algID]; ok {
+ return false
+ }
+ seen[algID] = struct{}{}
+ m.compressedCertAlgs = append(m.compressedCertAlgs, algID)
+ }
}
if isGREASEValue(extension) {
@@ -919,6 +947,7 @@
vers uint16
versOverride uint16
supportedVersOverride uint16
+ omitSupportedVers bool
random []byte
sessionId []byte
cipherSuite uint16
@@ -979,12 +1008,14 @@
extensions.addU16(2) // Length
extensions.addU16(m.pskIdentity)
}
- extensions.addU16(extensionSupportedVersions)
- extensions.addU16(2) // Length
- if m.supportedVersOverride != 0 {
- extensions.addU16(m.supportedVersOverride)
- } else {
- extensions.addU16(m.vers)
+ if !m.omitSupportedVers {
+ extensions.addU16(extensionSupportedVersions)
+ extensions.addU16(2) // Length
+ if m.supportedVersOverride != 0 {
+ extensions.addU16(m.supportedVersOverride)
+ } else {
+ extensions.addU16(m.vers)
+ }
}
if len(m.customExtension) > 0 {
extensions.addU16(extensionCustom)
@@ -1668,6 +1699,48 @@
return true
}
+type compressedCertificateMsg struct {
+ raw []byte
+ algID uint16
+ uncompressedLength uint32
+ compressed []byte
+}
+
+func (m *compressedCertificateMsg) marshal() (x []byte) {
+ if m.raw != nil {
+ return m.raw
+ }
+
+ certMsg := newByteBuilder()
+ certMsg.addU8(typeCompressedCertificate)
+ certificate := certMsg.addU24LengthPrefixed()
+ certificate.addU16(m.algID)
+ certificate.addU24(int(m.uncompressedLength))
+ compressed := certificate.addU24LengthPrefixed()
+ compressed.addBytes(m.compressed)
+
+ m.raw = certMsg.finish()
+ return m.raw
+}
+
+func (m *compressedCertificateMsg) unmarshal(data []byte) bool {
+ m.raw = data
+ reader := byteReader(data[4:])
+
+ if !reader.readU16(&m.algID) ||
+ !reader.readU24(&m.uncompressedLength) ||
+ !reader.readU24LengthPrefixedBytes(&m.compressed) ||
+ len(reader) != 0 {
+ return false
+ }
+
+ if m.uncompressedLength >= 1<<17 {
+ return false
+ }
+
+ return true
+}
+
type serverKeyExchangeMsg struct {
raw []byte
key []byte
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index e5f2944..c0653b1 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -375,7 +375,8 @@
sessionId: hs.clientHello.sessionId,
compressionMethod: config.Bugs.SendCompressionMethod,
versOverride: config.Bugs.SendServerHelloVersion,
- supportedVersOverride: config.Bugs.SendServerSupportedExtensionVersion,
+ supportedVersOverride: config.Bugs.SendServerSupportedVersionExtension,
+ omitSupportedVers: config.Bugs.OmitServerSupportedVersionExtension,
customExtension: config.Bugs.CustomUnencryptedExtension,
unencryptedALPN: config.Bugs.SendUnencryptedALPN,
}
@@ -833,7 +834,7 @@
if config.ClientAuth >= RequestClientCert {
// Request a client certificate
certReq := &certificateRequestMsg{
- vers: c.wireVersion,
+ vers: c.wireVersion,
hasSignatureAlgorithm: !config.Bugs.OmitCertificateRequestAlgorithms,
hasRequestContext: true,
requestContext: config.Bugs.SendRequestContext,
@@ -884,8 +885,47 @@
}
}
certMsgBytes := certMsg.marshal()
- hs.writeServerHash(certMsgBytes)
- c.writeRecord(recordTypeHandshake, certMsgBytes)
+ sentCompressedCertMsg := false
+
+ FindCertCompressionAlg:
+ for candidate, alg := range c.config.CertCompressionAlgs {
+ for _, id := range hs.clientHello.compressedCertAlgs {
+ if id == candidate {
+ if expected := config.Bugs.ExpectedCompressedCert; expected != 0 && expected != id {
+ return fmt.Errorf("expected to send compressed cert with alg %d, but picked %d", expected, id)
+ }
+
+ if override := config.Bugs.SendCertCompressionAlgId; override != 0 {
+ id = override
+ }
+
+ uncompressed := certMsgBytes[4:]
+ uncompressedLen := uint32(len(uncompressed))
+ if override := config.Bugs.SendCertUncompressedLength; override != 0 {
+ uncompressedLen = override
+ }
+
+ compressedCertMsgBytes := (&compressedCertificateMsg{
+ algID: id,
+ uncompressedLength: uncompressedLen,
+ compressed: alg.Compress(uncompressed),
+ }).marshal()
+
+ hs.writeServerHash(compressedCertMsgBytes)
+ c.writeRecord(recordTypeHandshake, compressedCertMsgBytes)
+ sentCompressedCertMsg = true
+ break FindCertCompressionAlg
+ }
+ }
+ }
+
+ if !sentCompressedCertMsg {
+ if config.Bugs.ExpectedCompressedCert != 0 {
+ return errors.New("unexpectedly sent uncompressed certificate")
+ }
+ hs.writeServerHash(certMsgBytes)
+ c.writeRecord(recordTypeHandshake, certMsgBytes)
+ }
certVerify := &certificateVerifyMsg{
hasSignatureAlgorithm: true,
@@ -1122,7 +1162,7 @@
versOverride: config.Bugs.SendServerHelloVersion,
compressionMethod: config.Bugs.SendCompressionMethod,
extensions: serverExtensions{
- supportedVersion: config.Bugs.SendServerSupportedExtensionVersion,
+ supportedVersion: config.Bugs.SendServerSupportedVersionExtension,
},
omitExtensions: config.Bugs.OmitExtensions,
emptyExtensions: config.Bugs.EmptyExtensions,
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index ce20d22..d1748dd 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -56,6 +56,7 @@
testToRun = flag.String("test", "", "The pattern to filter tests to run, or empty to run all tests")
numWorkers = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
shimPath = flag.String("shim-path", "../../../build/ssl/test/bssl_shim", "The location of the shim binary.")
+ handshakerPath = flag.String("handshaker-path", "../../../build/ssl/test/handshaker", "The location of the handshaker binary.")
resourceDir = flag.String("resource-dir", ".", "The directory in which to find certificate and key files.")
fuzzer = flag.Bool("fuzzer", false, "If true, tests against a BoringSSL built in fuzzer mode.")
transcriptDir = flag.String("transcript-dir", "", "The directory in which to write transcripts.")
@@ -414,7 +415,8 @@
readWithUnfinishedWrite bool
// shimShutsDown, if true, runs a test where the shim shuts down the
// connection immediately after the handshake rather than echoing
- // messages from the runner.
+ // messages from the runner. The runner will default to not sending
+ // application data.
shimShutsDown bool
// renegotiate indicates the number of times the connection should be
// renegotiated during the exchange.
@@ -482,21 +484,23 @@
var testCases []testCase
-func writeTranscript(test *testCase, path string, data []byte) {
+func appendTranscript(path string, data []byte) error {
if len(data) == 0 {
- return
+ return nil
}
settings, err := ioutil.ReadFile(path)
if err != nil {
- fmt.Fprintf(os.Stderr, "Error reading %s: %s.\n", path, err)
- return
+ if !os.IsNotExist(err) {
+ return err
+ }
+ // If the shim aborted before writing a file, use a default
+ // settings block, so the transcript is still somewhat valid.
+ settings = []byte{0, 0} // kDataTag
}
settings = append(settings, data...)
- if err := ioutil.WriteFile(path, settings, 0644); err != nil {
- fmt.Fprintf(os.Stderr, "Error writing %s: %s\n", path, err)
- }
+ return ioutil.WriteFile(path, settings, 0644)
}
// A timeoutConn implements an idle timeout on each Read and Write operation.
@@ -523,7 +527,7 @@
return t.Conn.Write(b)
}
-func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, transcriptPrefix string, num int) error {
+func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, transcripts *[][]byte, num int) error {
if !test.noSessionCache {
if config.ClientSessionCache == nil {
config.ClientSessionCache = NewLRUClientSessionCache(1)
@@ -575,10 +579,13 @@
if *flagDebug {
defer connDebug.WriteTo(os.Stdout)
}
- if len(transcriptPrefix) != 0 {
+ if len(*transcriptDir) != 0 {
defer func() {
- path := transcriptPrefix + strconv.Itoa(num)
- writeTranscript(test, path, connDebug.Transcript())
+ if num == len(*transcripts) {
+ *transcripts = append(*transcripts, connDebug.Transcript())
+ } else {
+ panic("transcripts are out of sync")
+ }
}()
}
@@ -820,7 +827,8 @@
}
messageCount := test.messageCount
- if messageCount == 0 {
+ // shimShutsDown sets the default message count to zero.
+ if messageCount == 0 && !test.shimShutsDown {
messageCount = 1
}
@@ -1117,7 +1125,10 @@
flags = append(flags, "-tls13-variant", strconv.Itoa(test.tls13Variant))
}
+ flags = append(flags, "-handshaker-path", *handshakerPath)
+
var transcriptPrefix string
+ var transcripts [][]byte
if len(*transcriptDir) != 0 {
protocol := "tls"
if test.protocol == dtls {
@@ -1176,7 +1187,7 @@
conn, err := acceptOrWait(listener, waitChan)
if err == nil {
- err = doExchange(test, &config, conn, false /* not a resumption */, transcriptPrefix, 0)
+ err = doExchange(test, &config, conn, false /* not a resumption */, &transcripts, 0)
conn.Close()
}
@@ -1196,7 +1207,7 @@
var connResume net.Conn
connResume, err = acceptOrWait(listener, waitChan)
if err == nil {
- err = doExchange(test, &resumeConfig, connResume, true /* resumption */, transcriptPrefix, i+1)
+ err = doExchange(test, &resumeConfig, connResume, true /* resumption */, &transcripts, i+1)
connResume.Close()
}
}
@@ -1217,6 +1228,14 @@
waitTimeout.Stop()
}
+ // Now that the shim has exitted, all the settings files have been
+ // written. Append the saved transcripts.
+ for i, transcript := range transcripts {
+ if err := appendTranscript(transcriptPrefix+strconv.Itoa(i), transcript); err != nil {
+ return err
+ }
+ }
+
var isValgrindError, mustFail bool
if exitError, ok := childErr.(*exec.ExitError); ok {
switch exitError.Sys().(syscall.WaitStatus).ExitStatus() {
@@ -1338,11 +1357,6 @@
var tlsVersions = []tlsVersion{
{
- name: "SSL3",
- version: VersionSSL30,
- excludeFlag: "-no-ssl3",
- },
- {
name: "TLS1",
version: VersionTLS10,
excludeFlag: "-no-tls1",
@@ -1391,6 +1405,23 @@
return ret
}
+func allShimVersions(protocol protocol) []tlsVersion {
+ if protocol == dtls {
+ return allVersions(protocol)
+ }
+ tls13Default := tlsVersion{
+ name: "TLS13Default",
+ version: VersionTLS13,
+ excludeFlag: "-no-tls13",
+ versionWire: 0,
+ tls13Variant: TLS13Default,
+ }
+
+ var shimVersions []tlsVersion
+ shimVersions = append(shimVersions, allVersions(protocol)...)
+ return append(shimVersions, tls13Default)
+}
+
type testCipherSuite struct {
name string
id uint16
@@ -1400,23 +1431,17 @@
{"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
{"AES128-GCM", TLS_RSA_WITH_AES_128_GCM_SHA256},
{"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
- {"AES128-SHA256", TLS_RSA_WITH_AES_128_CBC_SHA256},
{"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384},
{"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
- {"AES256-SHA256", TLS_RSA_WITH_AES_256_CBC_SHA256},
{"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
{"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
- {"ECDHE-ECDSA-AES128-SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
{"ECDHE-ECDSA-AES256-GCM", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
{"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
- {"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
{"ECDHE-ECDSA-CHACHA20-POLY1305", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
{"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
{"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
- {"ECDHE-RSA-AES128-SHA256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
{"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
{"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
- {"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
{"ECDHE-RSA-CHACHA20-POLY1305", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
{"PSK-AES128-CBC-SHA", TLS_PSK_WITH_AES_128_CBC_SHA},
{"PSK-AES256-CBC-SHA", TLS_PSK_WITH_AES_256_CBC_SHA},
@@ -1453,6 +1478,22 @@
}
func convertToSplitHandshakeTests(tests []testCase) (splitHandshakeTests []testCase) {
+ var stdout bytes.Buffer
+ shim := exec.Command(*shimPath, "-is-handshaker-supported")
+ shim.Stdout = &stdout;
+ if err := shim.Run(); err != nil {
+ panic(err)
+ }
+
+ switch strings.TrimSpace(string(stdout.Bytes())) {
+ case "No":
+ return
+ case "Yes":
+ break
+ default:
+ panic("Unknown output from shim: 0x" + hex.EncodeToString(stdout.Bytes()))
+ }
+
NextTest:
for _, test := range tests {
if test.protocol != tls ||
@@ -1811,7 +1852,7 @@
},
{
name: "DisableEverything",
- flags: []string{"-no-tls13", "-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"},
+ flags: []string{"-no-tls13", "-no-tls12", "-no-tls11", "-no-tls1"},
shouldFail: true,
expectedError: ":NO_SUPPORTED_VERSIONS_ENABLED:",
},
@@ -2973,10 +3014,12 @@
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
- MaxReceivePlaintext: 512,
+ MaxReceivePlaintext: 512,
+ ExpectPackedEncryptedHandshake: 512,
},
},
- messageLen: 1024,
+ tls13Variant: TLS13Draft28,
+ messageLen: 1024,
flags: []string{
"-max-send-fragment", "512",
"-read-size", "1024",
@@ -3004,6 +3047,32 @@
expectedLocalError: "local error: record overflow",
},
{
+ // Test that handshake data is not packed in TLS 1.3
+ // draft-23.
+ testType: serverTest,
+ name: "ForbidHandshakePacking-TLS13Draft23",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ForbidHandshakePacking: true,
+ },
+ },
+ tls13Variant: TLS13Draft23,
+ },
+ {
+ // Test that handshake data is tightly packed in TLS 1.3
+ // draft-28.
+ testType: serverTest,
+ name: "PackedEncryptedHandshake-TLS13Draft28",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExpectPackedEncryptedHandshake: 16384,
+ },
+ },
+ tls13Variant: TLS13Draft28,
+ },
+ {
// Test that DTLS can handle multiple application data
// records in a single packet.
protocol: dtls,
@@ -3119,33 +3188,22 @@
flags = append(flags, "-cipher", "DEFAULT:NULL-SHA")
}
- var shouldServerFail, shouldClientFail bool
- if hasComponent(suite.name, "ECDHE") && ver.version == VersionSSL30 {
- // BoringSSL clients accept ECDHE on SSLv3, but
- // a BoringSSL server will never select it
- // because the extension is missing.
- shouldServerFail = true
- }
+ var shouldFail bool
if isTLS12Only(suite.name) && ver.version < VersionTLS12 {
- shouldClientFail = true
- shouldServerFail = true
+ shouldFail = true
}
if !isTLS13Suite(suite.name) && ver.version >= VersionTLS13 {
- shouldClientFail = true
- shouldServerFail = true
+ shouldFail = true
}
if isTLS13Suite(suite.name) && ver.version < VersionTLS13 {
- shouldClientFail = true
- shouldServerFail = true
+ shouldFail = true
}
var sendCipherSuite uint16
var expectedServerError, expectedClientError string
serverCipherSuites := []uint16{suite.id}
- if shouldServerFail {
+ if shouldFail {
expectedServerError = ":NO_SHARED_CIPHER:"
- }
- if shouldClientFail {
expectedClientError = ":WRONG_CIPHER_RETURNED:"
// Configure the server to select ciphers as normal but
// select an incompatible cipher in ServerHello.
@@ -3153,12 +3211,8 @@
sendCipherSuite = suite.id
}
- // For cipher suites and versions where exporters are defined, verify
- // that they interoperate.
- var exportKeyingMaterial int
- if ver.version > VersionSSL30 {
- exportKeyingMaterial = 1024
- }
+ // Verify exporters interoperate.
+ exportKeyingMaterial := 1024
testCases = append(testCases, testCase{
testType: serverTest,
@@ -3180,7 +3234,7 @@
keyFile: keyFile,
flags: flags,
resumeSession: true,
- shouldFail: shouldServerFail,
+ shouldFail: shouldFail,
expectedError: expectedServerError,
exportKeyingMaterial: exportKeyingMaterial,
})
@@ -3197,19 +3251,19 @@
PreSharedKey: []byte(psk),
PreSharedKeyIdentity: pskIdentity,
Bugs: ProtocolBugs{
- IgnorePeerCipherPreferences: shouldClientFail,
+ IgnorePeerCipherPreferences: shouldFail,
SendCipherSuite: sendCipherSuite,
},
},
tls13Variant: ver.tls13Variant,
flags: flags,
resumeSession: true,
- shouldFail: shouldClientFail,
+ shouldFail: shouldFail,
expectedError: expectedClientError,
exportKeyingMaterial: exportKeyingMaterial,
})
- if shouldClientFail {
+ if shouldFail {
return
}
@@ -3232,10 +3286,9 @@
// Test bad records for all ciphers. Bad records are fatal in TLS
// and ignored in DTLS.
- var shouldFail bool
+ shouldFail = protocol == tls
var expectedError string
- if protocol == tls {
- shouldFail = true
+ if shouldFail {
expectedError = ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"
}
@@ -3741,34 +3794,32 @@
tls13Variant: ver.tls13Variant,
flags: []string{"-require-any-client-certificate"},
})
- if ver.version != VersionSSL30 {
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: ver.name + "-Server-ClientAuth-ECDSA",
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- Certificates: []Certificate{ecdsaP256Certificate},
- },
- tls13Variant: ver.tls13Variant,
- flags: []string{"-require-any-client-certificate"},
- })
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: ver.name + "-Client-ClientAuth-ECDSA",
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- ClientAuth: RequireAnyClientCert,
- ClientCAs: certPool,
- },
- tls13Variant: ver.tls13Variant,
- flags: []string{
- "-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
- "-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
- },
- })
- }
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: ver.name + "-Server-ClientAuth-ECDSA",
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Certificates: []Certificate{ecdsaP256Certificate},
+ },
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-require-any-client-certificate"},
+ })
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: ver.name + "-Client-ClientAuth-ECDSA",
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ ClientAuth: RequireAnyClientCert,
+ ClientCAs: certPool,
+ },
+ tls13Variant: ver.tls13Variant,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
+ "-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
+ },
+ })
testCases = append(testCases, testCase{
name: "NoClientCertificate-" + ver.name,
@@ -3835,57 +3886,55 @@
expectedLocalError: certificateRequired,
})
- if ver.version != VersionSSL30 {
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SkipClientCertificate-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- SkipClientCertificate: true,
- },
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SkipClientCertificate-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ SkipClientCertificate: true,
},
- // Setting SSL_VERIFY_PEER allows anonymous clients.
- flags: []string{"-verify-peer"},
- tls13Variant: ver.tls13Variant,
- shouldFail: true,
- expectedError: ":UNEXPECTED_MESSAGE:",
- })
+ },
+ // Setting SSL_VERIFY_PEER allows anonymous clients.
+ flags: []string{"-verify-peer"},
+ tls13Variant: ver.tls13Variant,
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "VerifyPeerIfNoOBC-NoChannelID-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- },
- flags: []string{
- "-enable-channel-id",
- "-verify-peer-if-no-obc",
- },
- tls13Variant: ver.tls13Variant,
- shouldFail: true,
- expectedError: ":PEER_DID_NOT_RETURN_A_CERTIFICATE:",
- expectedLocalError: certificateRequired,
- })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "VerifyPeerIfNoOBC-NoChannelID-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ flags: []string{
+ "-enable-channel-id",
+ "-verify-peer-if-no-obc",
+ },
+ tls13Variant: ver.tls13Variant,
+ shouldFail: true,
+ expectedError: ":PEER_DID_NOT_RETURN_A_CERTIFICATE:",
+ expectedLocalError: certificateRequired,
+ })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "VerifyPeerIfNoOBC-ChannelID-" + ver.name,
- config: Config{
- MinVersion: ver.version,
- MaxVersion: ver.version,
- ChannelID: channelIDKey,
- },
- expectChannelID: true,
- tls13Variant: ver.tls13Variant,
- flags: []string{
- "-enable-channel-id",
- "-verify-peer-if-no-obc",
- },
- })
- }
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "VerifyPeerIfNoOBC-ChannelID-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ ChannelID: channelIDKey,
+ },
+ expectChannelID: true,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{
+ "-enable-channel-id",
+ "-verify-peer-if-no-obc",
+ },
+ })
testCases = append(testCases, testCase{
testType: serverTest,
@@ -4023,7 +4072,7 @@
flags = []string{expectEMSFlag}
}
- test := testCase{
+ testCases = append(testCases, testCase{
testType: testType,
name: prefix + "ExtendedMasterSecret-" + ver.name + suffix,
config: Config{
@@ -4036,12 +4085,7 @@
},
tls13Variant: ver.tls13Variant,
flags: flags,
- shouldFail: ver.version == VersionSSL30 && with,
- }
- if test.shouldFail {
- test.expectedLocalError = "extended master secret required but not supported by peer"
- }
- testCases = append(testCases, test)
+ })
}
}
}
@@ -4506,23 +4550,6 @@
if config.protocol == tls {
tests = append(tests, testCase{
testType: clientTest,
- name: "ClientAuth-NoCertificate-Client-SSL3",
- config: Config{
- MaxVersion: VersionSSL30,
- ClientAuth: RequestClientCert,
- },
- })
- tests = append(tests, testCase{
- testType: serverTest,
- name: "ClientAuth-NoCertificate-Server-SSL3",
- config: Config{
- MaxVersion: VersionSSL30,
- },
- // Setting SSL_VERIFY_PEER allows anonymous clients.
- flags: []string{"-verify-peer"},
- })
- tests = append(tests, testCase{
- testType: clientTest,
name: "ClientAuth-NoCertificate-Client-TLS13",
config: Config{
MaxVersion: VersionTLS13,
@@ -4741,60 +4768,157 @@
})
// OCSP stapling tests.
- tests = append(tests, testCase{
- testType: clientTest,
- name: "OCSPStapling-Client",
- config: Config{
- MaxVersion: VersionTLS12,
- },
- flags: []string{
- "-enable-ocsp-stapling",
- "-expect-ocsp-response",
- base64.StdEncoding.EncodeToString(testOCSPResponse),
- "-verify-peer",
- },
- resumeSession: true,
- })
- tests = append(tests, testCase{
- testType: serverTest,
- name: "OCSPStapling-Server",
- config: Config{
- MaxVersion: VersionTLS12,
- },
- expectedOCSPResponse: testOCSPResponse,
- flags: []string{
- "-ocsp-response",
- base64.StdEncoding.EncodeToString(testOCSPResponse),
- },
- resumeSession: true,
- })
- tests = append(tests, testCase{
- testType: clientTest,
- name: "OCSPStapling-Client-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- },
- flags: []string{
- "-enable-ocsp-stapling",
- "-expect-ocsp-response",
- base64.StdEncoding.EncodeToString(testOCSPResponse),
- "-verify-peer",
- },
- resumeSession: true,
- })
- tests = append(tests, testCase{
- testType: serverTest,
- name: "OCSPStapling-Server-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- },
- expectedOCSPResponse: testOCSPResponse,
- flags: []string{
- "-ocsp-response",
- base64.StdEncoding.EncodeToString(testOCSPResponse),
- },
- resumeSession: true,
- })
+ for _, vers := range tlsVersions {
+ if config.protocol == dtls && !vers.hasDTLS {
+ continue
+ }
+ tests = append(tests, testCase{
+ testType: clientTest,
+ name: "OCSPStapling-Client-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ },
+ tls13Variant: vers.tls13Variant,
+ flags: []string{
+ "-enable-ocsp-stapling",
+ "-expect-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ "-verify-peer",
+ },
+ resumeSession: true,
+ })
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "OCSPStapling-Server-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ },
+ tls13Variant: vers.tls13Variant,
+ expectedOCSPResponse: testOCSPResponse,
+ flags: []string{
+ "-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ },
+ resumeSession: true,
+ })
+
+ // The client OCSP callback is an alternate certificate
+ // verification callback.
+ tests = append(tests, testCase{
+ testType: clientTest,
+ name: "ClientOCSPCallback-Pass-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ Certificates: []Certificate{rsaCertificate},
+ },
+ tls13Variant: vers.tls13Variant,
+ flags: []string{
+ "-enable-ocsp-stapling",
+ "-use-ocsp-callback",
+ },
+ })
+ var expectedLocalError string
+ if !config.async {
+ // TODO(davidben): Asynchronous fatal alerts are never
+ // sent. https://crbug.com/boringssl/130.
+ expectedLocalError = "remote error: bad certificate status response"
+ }
+ tests = append(tests, testCase{
+ testType: clientTest,
+ name: "ClientOCSPCallback-Fail-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ Certificates: []Certificate{rsaCertificate},
+ },
+ tls13Variant: vers.tls13Variant,
+ flags: []string{
+ "-enable-ocsp-stapling",
+ "-use-ocsp-callback",
+ "-fail-ocsp-callback",
+ },
+ shouldFail: true,
+ expectedLocalError: expectedLocalError,
+ expectedError: ":OCSP_CB_ERROR:",
+ })
+ // The callback still runs if the server does not send an OCSP
+ // response.
+ certNoStaple := rsaCertificate
+ certNoStaple.OCSPStaple = nil
+ tests = append(tests, testCase{
+ testType: clientTest,
+ name: "ClientOCSPCallback-FailNoStaple-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ Certificates: []Certificate{certNoStaple},
+ },
+ tls13Variant: vers.tls13Variant,
+ flags: []string{
+ "-enable-ocsp-stapling",
+ "-use-ocsp-callback",
+ "-fail-ocsp-callback",
+ },
+ shouldFail: true,
+ expectedLocalError: expectedLocalError,
+ expectedError: ":OCSP_CB_ERROR:",
+ })
+
+ // The server OCSP callback is a legacy mechanism for
+ // configuring OCSP, used by unreliable server software.
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "ServerOCSPCallback-SetInCallback-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ },
+ tls13Variant: vers.tls13Variant,
+ expectedOCSPResponse: testOCSPResponse,
+ flags: []string{
+ "-use-ocsp-callback",
+ "-set-ocsp-in-callback",
+ "-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ },
+ resumeSession: true,
+ })
+
+ // The callback may decline OCSP, in which case we act as if
+ // the client did not support it, even if a response was
+ // configured.
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "ServerOCSPCallback-Decline-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ },
+ tls13Variant: vers.tls13Variant,
+ expectedOCSPResponse: []byte{},
+ flags: []string{
+ "-use-ocsp-callback",
+ "-decline-ocsp-callback",
+ "-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ },
+ resumeSession: true,
+ })
+
+ // The callback may also signal an internal error.
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "ServerOCSPCallback-Fail-" + vers.name,
+ config: Config{
+ MaxVersion: vers.version,
+ },
+ tls13Variant: vers.tls13Variant,
+ flags: []string{
+ "-use-ocsp-callback",
+ "-fail-ocsp-callback",
+ "-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ },
+ shouldFail: true,
+ expectedError: ":OCSP_CB_ERROR:",
+ })
+ }
// Certificate verification tests.
for _, vers := range tlsVersions {
@@ -5165,6 +5289,25 @@
flags: []string{"-check-close-notify"},
})
+ // The shim should reject unexpected application data
+ // when shutting down.
+ tests = append(tests, testCase{
+ name: "Shutdown-Shim-ApplicationData",
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ ExpectCloseNotify: true,
+ },
+ },
+ shimShutsDown: true,
+ messageCount: 1,
+ sendEmptyRecords: 1,
+ sendWarningAlerts: 1,
+ flags: []string{"-check-close-notify"},
+ shouldFail: true,
+ expectedError: ":APPLICATION_DATA_ON_SHUTDOWN:",
+ })
+
// Test that SSL_shutdown still processes KeyUpdate.
tests = append(tests, testCase{
name: "Shutdown-Shim-KeyUpdate",
@@ -5205,11 +5348,11 @@
MinVersion: VersionTLS12,
MaxVersion: VersionTLS12,
Bugs: ProtocolBugs{
- SendHelloRequestBeforeEveryAppDataRecord: true,
- ExpectCloseNotify: true,
+ ExpectCloseNotify: true,
},
},
shimShutsDown: true,
+ renegotiate: 1,
shouldFail: true,
expectedError: ":NO_RENEGOTIATION:",
flags: []string{"-check-close-notify"},
@@ -5220,11 +5363,11 @@
MinVersion: VersionTLS12,
MaxVersion: VersionTLS12,
Bugs: ProtocolBugs{
- SendHelloRequestBeforeEveryAppDataRecord: true,
- ExpectCloseNotify: true,
+ ExpectCloseNotify: true,
},
},
shimShutsDown: true,
+ renegotiate: 1,
shouldFail: true,
expectedError: ":NO_RENEGOTIATION:",
flags: []string{
@@ -5332,7 +5475,7 @@
failFlag := "-fail-ddos-callback"
if resume {
- failFlag = "-fail-second-ddos-callback"
+ failFlag = "-on-resume-fail-ddos-callback"
}
testCases = append(testCases, testCase{
testType: serverTest,
@@ -5363,7 +5506,7 @@
func addVersionNegotiationTests() {
for _, protocol := range []protocol{tls, dtls} {
- for _, shimVers := range allVersions(protocol) {
+ for _, shimVers := range allShimVersions(protocol) {
// Assemble flags to disable all newer versions on the shim.
var flags []string
for _, vers := range allVersions(protocol) {
@@ -5385,13 +5528,12 @@
if runnerVers.version < shimVers.version {
expectedVersion = runnerVers.version
}
- // When running and shim have different TLS 1.3 variants enabled,
- // shim peers are expected to fall back to TLS 1.2.
+
if expectedVersion == VersionTLS13 && runnerVers.tls13Variant != shimVers.tls13Variant {
- expectedVersion = VersionTLS12
+ if shimVers.tls13Variant != TLS13Default {
+ expectedVersion = VersionTLS12
+ }
}
- expectedClientVersion := expectedVersion
- expectedServerVersion := expectedVersion
suffix := shimVers.name + "-" + runnerVers.name
if protocol == dtls {
@@ -5404,8 +5546,8 @@
clientVers = VersionTLS10
}
clientVers = recordVersionToWire(clientVers, protocol)
- serverVers := expectedServerVersion
- if expectedServerVersion >= VersionTLS13 {
+ serverVers := expectedVersion
+ if expectedVersion >= VersionTLS13 {
serverVers = VersionTLS12
}
serverVers = recordVersionToWire(serverVers, protocol)
@@ -5422,7 +5564,7 @@
},
},
flags: flags,
- expectedVersion: expectedClientVersion,
+ expectedVersion: expectedVersion,
})
testCases = append(testCases, testCase{
protocol: protocol,
@@ -5436,7 +5578,7 @@
},
},
flags: flags2,
- expectedVersion: expectedClientVersion,
+ expectedVersion: expectedVersion,
})
testCases = append(testCases, testCase{
@@ -5451,7 +5593,7 @@
},
},
flags: flags,
- expectedVersion: expectedServerVersion,
+ expectedVersion: expectedVersion,
})
testCases = append(testCases, testCase{
protocol: protocol,
@@ -5465,7 +5607,7 @@
},
},
flags: flags2,
- expectedVersion: expectedServerVersion,
+ expectedVersion: expectedVersion,
})
}
}
@@ -5584,7 +5726,7 @@
config: Config{
MaxVersion: VersionTLS12,
Bugs: ProtocolBugs{
- SendServerSupportedExtensionVersion: VersionTLS12,
+ SendServerSupportedVersionExtension: VersionTLS12,
},
},
shouldFail: true,
@@ -5750,6 +5892,46 @@
},
},
})
+
+ // SSL 3.0 support has been removed. Test that the shim does not
+ // support it.
+ testCases = append(testCases, testCase{
+ name: "NoSSL3-Client",
+ config: Config{
+ MinVersion: VersionSSL30,
+ MaxVersion: VersionSSL30,
+ },
+ shouldFail: true,
+ expectedLocalError: "tls: client did not offer any supported protocol versions",
+ })
+ testCases = append(testCases, testCase{
+ name: "NoSSL3-Client-Unsolicited",
+ config: Config{
+ MinVersion: VersionSSL30,
+ MaxVersion: VersionSSL30,
+ Bugs: ProtocolBugs{
+ // The above test asserts the client does not
+ // offer SSL 3.0 in the supported_versions
+ // list. Additionally assert that it rejects an
+ // unsolicited SSL 3.0 ServerHello.
+ NegotiateVersion: VersionSSL30,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNSUPPORTED_PROTOCOL:",
+ expectedLocalError: "remote error: protocol version not supported",
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "NoSSL3-Server",
+ config: Config{
+ MinVersion: VersionSSL30,
+ MaxVersion: VersionSSL30,
+ },
+ shouldFail: true,
+ expectedError: ":UNSUPPORTED_PROTOCOL:",
+ expectedLocalError: "remote error: protocol version not supported",
+ })
}
func addMinimumVersionTests() {
@@ -5878,12 +6060,8 @@
// halves to EncryptedExtensions in TLS 1.3. Duplicate each of these
// tests for both. Also test interaction with 0-RTT when implemented.
- // Repeat extensions tests all versions except SSL 3.0.
+ // Repeat extensions tests at all versions.
for _, ver := range tlsVersions {
- if ver.version == VersionSSL30 {
- continue
- }
-
// Test that duplicate extensions are rejected.
testCases = append(testCases, testCase{
testType: clientTest,
@@ -6069,6 +6247,24 @@
expectNoNextProto: true,
resumeSession: true,
})
+ // Test that the server implementation catches itself if the
+ // callback tries to return an invalid empty ALPN protocol.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "ALPNServer-SelectEmpty-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ NextProtos: []string{"foo", "bar", "baz"},
+ },
+ flags: []string{
+ "-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
+ "-select-empty-alpn",
+ },
+ tls13Variant: ver.tls13Variant,
+ shouldFail: true,
+ expectedLocalError: "remote error: internal error",
+ expectedError: ":INVALID_ALPN_PROTOCOL:",
+ })
// Test ALPN in async mode as well to ensure that extensions callbacks are only
// called once.
@@ -7036,70 +7232,6 @@
flags: []string{"-host-name", "01234567890123456789012345678901234567890123456789012345678901234567890123456789.com"},
})
- // Extensions should not function in SSL 3.0.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SSLv3Extensions-NoALPN",
- config: Config{
- MaxVersion: VersionSSL30,
- NextProtos: []string{"foo", "bar", "baz"},
- },
- flags: []string{
- "-select-alpn", "foo",
- },
- expectNoNextProto: true,
- })
-
- // Test session tickets separately as they follow a different codepath.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SSLv3Extensions-NoTickets",
- config: Config{
- MaxVersion: VersionSSL30,
- Bugs: ProtocolBugs{
- // Historically, session tickets in SSL 3.0
- // failed in different ways depending on whether
- // the client supported renegotiation_info.
- NoRenegotiationInfo: true,
- },
- },
- resumeSession: true,
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SSLv3Extensions-NoTickets2",
- config: Config{
- MaxVersion: VersionSSL30,
- },
- resumeSession: true,
- })
-
- // But SSL 3.0 does send and process renegotiation_info.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SSLv3Extensions-RenegotiationInfo",
- config: Config{
- MaxVersion: VersionSSL30,
- Bugs: ProtocolBugs{
- RequireRenegotiationInfo: true,
- },
- },
- flags: []string{"-expect-secure-renegotiation"},
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "SSLv3Extensions-RenegotiationInfo-SCSV",
- config: Config{
- MaxVersion: VersionSSL30,
- Bugs: ProtocolBugs{
- NoRenegotiationInfo: true,
- SendRenegotiationSCSV: true,
- RequireRenegotiationInfo: true,
- },
- },
- flags: []string{"-expect-secure-renegotiation"},
- })
-
// Test that illegal extensions in TLS 1.3 are rejected by the client if
// in ServerHello.
testCases = append(testCases, testCase{
@@ -7378,14 +7510,6 @@
func addResumptionVersionTests() {
for _, sessionVers := range tlsVersions {
for _, resumeVers := range tlsVersions {
- // SSL 3.0 does not have tickets and TLS 1.3 does not
- // have session IDs, so skip their cross-resumption
- // tests.
- if (sessionVers.version >= VersionTLS13 && resumeVers.version == VersionSSL30) ||
- (resumeVers.version >= VersionTLS13 && sessionVers.version == VersionSSL30) {
- continue
- }
-
protocols := []protocol{tls}
if sessionVers.hasDTLS && resumeVers.hasDTLS {
protocols = append(protocols, dtls)
@@ -7504,6 +7628,27 @@
"-on-resume-tls13-variant", strconv.Itoa(resumeVers.tls13Variant),
},
})
+
+ // Repeat the test using session IDs, rather than tickets.
+ if sessionVers.version < VersionTLS13 && resumeVers.version < VersionTLS13 {
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "Resume-Server-NoTickets" + suffix,
+ resumeSession: true,
+ config: Config{
+ MaxVersion: sessionVers.version,
+ SessionTicketsDisabled: true,
+ },
+ expectedVersion: sessionVers.version,
+ expectResumeRejected: sessionVers != resumeVers,
+ resumeConfig: &Config{
+ MaxVersion: resumeVers.version,
+ SessionTicketsDisabled: true,
+ },
+ expectedResumeVersion: resumeVers.version,
+ })
+ }
}
}
}
@@ -8195,17 +8340,15 @@
},
})
- // Renegotiation is not allowed at SSL 3.0.
+ // Renegotiation may be enabled and then disabled immediately after the
+ // handshake.
testCases = append(testCases, testCase{
- name: "Renegotiate-Client-SSL3",
+ name: "Renegotiate-ForbidAfterHandshake",
config: Config{
- MaxVersion: VersionSSL30,
+ MaxVersion: VersionTLS12,
},
- renegotiate: 1,
- flags: []string{
- "-renegotiate-freely",
- "-expect-total-renegotiations", "1",
- },
+ renegotiate: 1,
+ flags: []string{"-forbid-renegotiation-after-handshake"},
shouldFail: true,
expectedError: ":NO_RENEGOTIATION:",
expectedLocalError: "remote error: no renegotiation",
@@ -8497,12 +8640,6 @@
continue
}
- // TODO(davidben): Support ECDSA in SSL 3.0 in Go for testing
- // or remove it in C.
- if ver.version == VersionSSL30 && alg.cert != testCertRSA {
- continue
- }
-
var shouldSignFail, shouldVerifyFail bool
// ecdsa_sha1 does not exist in TLS 1.3.
if ver.version >= VersionTLS13 && alg.id == signatureECDSAWithSHA1 {
@@ -8526,12 +8663,14 @@
shouldVerifyFail = true
}
- var signError, verifyError string
+ var signError, signLocalError, verifyError, verifyLocalError string
if shouldSignFail {
signError = ":NO_COMMON_SIGNATURE_ALGORITHMS:"
+ signLocalError = "remote error: handshake failure"
}
if shouldVerifyFail {
verifyError = ":WRONG_SIGNATURE_TYPE:"
+ verifyLocalError = "remote error"
}
suffix := "-" + alg.name + "-" + ver.name
@@ -8556,6 +8695,7 @@
tls13Variant: ver.tls13Variant,
shouldFail: shouldSignFail,
expectedError: signError,
+ expectedLocalError: signLocalError,
expectedPeerSignatureAlgorithm: alg.id,
})
@@ -8584,37 +8724,36 @@
},
// Resume the session to assert the peer signature
// algorithm is reported on both handshakes.
- resumeSession: !shouldVerifyFail,
- shouldFail: shouldVerifyFail,
- expectedError: verifyError,
+ resumeSession: !shouldVerifyFail,
+ shouldFail: shouldVerifyFail,
+ expectedError: verifyError,
+ expectedLocalError: verifyLocalError,
})
- // No signing cipher for SSL 3.0.
- if ver.version > VersionSSL30 {
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ServerAuth-Sign" + suffix,
- config: Config{
- MaxVersion: ver.version,
- CipherSuites: signingCiphers,
- VerifySignatureAlgorithms: []signatureAlgorithm{
- fakeSigAlg1,
- alg.id,
- fakeSigAlg2,
- },
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "ServerAuth-Sign" + suffix,
+ config: Config{
+ MaxVersion: ver.version,
+ CipherSuites: signingCiphers,
+ VerifySignatureAlgorithms: []signatureAlgorithm{
+ fakeSigAlg1,
+ alg.id,
+ fakeSigAlg2,
},
- tls13Variant: ver.tls13Variant,
- flags: []string{
- "-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
- "-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
- "-enable-all-curves",
- "-enable-ed25519",
- },
- shouldFail: shouldSignFail,
- expectedError: signError,
- expectedPeerSignatureAlgorithm: alg.id,
- })
- }
+ },
+ tls13Variant: ver.tls13Variant,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
+ "-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
+ "-enable-all-curves",
+ "-enable-ed25519",
+ },
+ shouldFail: shouldSignFail,
+ expectedError: signError,
+ expectedLocalError: signLocalError,
+ expectedPeerSignatureAlgorithm: alg.id,
+ })
testCases = append(testCases, testCase{
name: "ServerAuth-Verify" + suffix,
@@ -8640,9 +8779,10 @@
},
// Resume the session to assert the peer signature
// algorithm is reported on both handshakes.
- resumeSession: !shouldVerifyFail,
- shouldFail: shouldVerifyFail,
- expectedError: verifyError,
+ resumeSession: !shouldVerifyFail,
+ shouldFail: shouldVerifyFail,
+ expectedError: verifyError,
+ expectedLocalError: verifyLocalError,
})
if !shouldVerifyFail {
@@ -8733,6 +8873,55 @@
}
}
+ // Test the peer's verify preferences are available.
+ for _, ver := range tlsVersions {
+ if ver.version < VersionTLS12 {
+ continue
+ }
+ testCases = append(testCases, testCase{
+ name: "ClientAuth-PeerVerifyPrefs-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ ClientAuth: RequireAnyClientCert,
+ VerifySignatureAlgorithms: []signatureAlgorithm{
+ signatureRSAPSSWithSHA256,
+ signatureEd25519,
+ signatureECDSAWithP256AndSHA256,
+ },
+ },
+ tls13Variant: ver.tls13Variant,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ "-expect-peer-verify-pref", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
+ "-expect-peer-verify-pref", strconv.Itoa(int(signatureEd25519)),
+ "-expect-peer-verify-pref", strconv.Itoa(int(signatureECDSAWithP256AndSHA256)),
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "ServerAuth-PeerVerifyPrefs-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ VerifySignatureAlgorithms: []signatureAlgorithm{
+ signatureRSAPSSWithSHA256,
+ signatureEd25519,
+ signatureECDSAWithP256AndSHA256,
+ },
+ },
+ tls13Variant: ver.tls13Variant,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ "-expect-peer-verify-pref", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
+ "-expect-peer-verify-pref", strconv.Itoa(int(signatureEd25519)),
+ "-expect-peer-verify-pref", strconv.Itoa(int(signatureECDSAWithP256AndSHA256)),
+ },
+ })
+
+ }
+
// Test that algorithm selection takes the key type into account.
testCases = append(testCases, testCase{
name: "ClientAuth-SignatureType",
@@ -9772,9 +9961,6 @@
func addExportKeyingMaterialTests() {
for _, vers := range tlsVersions {
- if vers.version == VersionSSL30 {
- continue
- }
testCases = append(testCases, testCase{
name: "ExportKeyingMaterial-" + vers.name,
config: Config{
@@ -10042,19 +10228,6 @@
}
}
- testCases = append(testCases, testCase{
- name: "ExportKeyingMaterial-SSL3",
- config: Config{
- MaxVersion: VersionSSL30,
- },
- exportKeyingMaterial: 1024,
- exportLabel: "label",
- exportContext: "context",
- useExportContext: true,
- shouldFail: true,
- expectedError: "failed to export keying material",
- })
-
// Exporters work during a False Start.
testCases = append(testCases, testCase{
name: "ExportKeyingMaterial-FalseStart",
@@ -10154,278 +10327,6 @@
}
func addCustomExtensionTests() {
- expectedContents := "custom extension"
- emptyString := ""
-
- for _, isClient := range []bool{false, true} {
- suffix := "Server"
- flag := "-enable-server-custom-extension"
- testType := serverTest
- if isClient {
- suffix = "Client"
- flag = "-enable-client-custom-extension"
- testType = clientTest
- }
-
- testCases = append(testCases, testCase{
- testType: testType,
- name: "CustomExtensions-" + suffix,
- config: Config{
- MaxVersion: VersionTLS12,
- Bugs: ProtocolBugs{
- CustomExtension: expectedContents,
- ExpectedCustomExtension: &expectedContents,
- },
- },
- flags: []string{flag},
- })
- testCases = append(testCases, testCase{
- testType: testType,
- name: "CustomExtensions-" + suffix + "-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- CustomExtension: expectedContents,
- ExpectedCustomExtension: &expectedContents,
- },
- },
- flags: []string{flag},
- })
-
- // If the parse callback fails, the handshake should also fail.
- testCases = append(testCases, testCase{
- testType: testType,
- name: "CustomExtensions-ParseError-" + suffix,
- config: Config{
- MaxVersion: VersionTLS12,
- Bugs: ProtocolBugs{
- CustomExtension: expectedContents + "foo",
- ExpectedCustomExtension: &expectedContents,
- },
- },
- flags: []string{flag},
- shouldFail: true,
- expectedError: ":CUSTOM_EXTENSION_ERROR:",
- })
- testCases = append(testCases, testCase{
- testType: testType,
- name: "CustomExtensions-ParseError-" + suffix + "-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- CustomExtension: expectedContents + "foo",
- ExpectedCustomExtension: &expectedContents,
- },
- },
- flags: []string{flag},
- shouldFail: true,
- expectedError: ":CUSTOM_EXTENSION_ERROR:",
- })
-
- // If the add callback fails, the handshake should also fail.
- testCases = append(testCases, testCase{
- testType: testType,
- name: "CustomExtensions-FailAdd-" + suffix,
- config: Config{
- MaxVersion: VersionTLS12,
- Bugs: ProtocolBugs{
- CustomExtension: expectedContents,
- ExpectedCustomExtension: &expectedContents,
- },
- },
- flags: []string{flag, "-custom-extension-fail-add"},
- shouldFail: true,
- expectedError: ":CUSTOM_EXTENSION_ERROR:",
- })
- testCases = append(testCases, testCase{
- testType: testType,
- name: "CustomExtensions-FailAdd-" + suffix + "-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- CustomExtension: expectedContents,
- ExpectedCustomExtension: &expectedContents,
- },
- },
- flags: []string{flag, "-custom-extension-fail-add"},
- shouldFail: true,
- expectedError: ":CUSTOM_EXTENSION_ERROR:",
- })
-
- // If the add callback returns zero, no extension should be
- // added.
- skipCustomExtension := expectedContents
- if isClient {
- // For the case where the client skips sending the
- // custom extension, the server must not “echo” it.
- skipCustomExtension = ""
- }
- testCases = append(testCases, testCase{
- testType: testType,
- name: "CustomExtensions-Skip-" + suffix,
- config: Config{
- MaxVersion: VersionTLS12,
- Bugs: ProtocolBugs{
- CustomExtension: skipCustomExtension,
- ExpectedCustomExtension: &emptyString,
- },
- },
- flags: []string{flag, "-custom-extension-skip"},
- })
- testCases = append(testCases, testCase{
- testType: testType,
- name: "CustomExtensions-Skip-" + suffix + "-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- CustomExtension: skipCustomExtension,
- ExpectedCustomExtension: &emptyString,
- },
- },
- flags: []string{flag, "-custom-extension-skip"},
- })
- }
-
- // If the client sends both early data and custom extension, the handshake
- // should succeed as long as both the extensions aren't returned by the
- // server.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "CustomExtensions-Client-EarlyData-None",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- ExpectedCustomExtension: &expectedContents,
- AlwaysRejectEarlyData: true,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-client-custom-extension",
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-reject-early-data",
- },
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "CustomExtensions-Client-EarlyData-EarlyDataAccepted",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- ExpectedCustomExtension: &expectedContents,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-client-custom-extension",
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
- },
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "CustomExtensions-Client-EarlyData-CustomExtensionAccepted",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- AlwaysRejectEarlyData: true,
- CustomExtension: expectedContents,
- ExpectedCustomExtension: &expectedContents,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-client-custom-extension",
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-reject-early-data",
- },
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "CustomExtensions-Client-EarlyDataAndCustomExtensions",
- config: Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- CustomExtension: expectedContents,
- ExpectedCustomExtension: &expectedContents,
- },
- },
- resumeConfig: &Config{
- MaxVersion: VersionTLS13,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- CustomExtension: expectedContents,
- ExpectedCustomExtension: &expectedContents,
- SendEarlyDataExtension: true,
- },
- },
- resumeSession: true,
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:",
- flags: []string{
- "-enable-client-custom-extension",
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- },
- })
-
- // If the server receives both early data and custom extension, only the
- // custom extension should be accepted.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "CustomExtensions-Server-EarlyDataOffered",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{{1, 2, 3, 4}},
- CustomExtension: expectedContents,
- ExpectedCustomExtension: &expectedContents,
- ExpectEarlyDataAccepted: false,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-server-custom-extension",
- "-enable-early-data",
- },
- })
-
- // The custom extension add callback should not be called if the client
- // doesn't send the extension.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "CustomExtensions-NotCalled-Server",
- config: Config{
- MaxVersion: VersionTLS12,
- Bugs: ProtocolBugs{
- ExpectedCustomExtension: &emptyString,
- },
- },
- flags: []string{"-enable-server-custom-extension", "-custom-extension-fail-add"},
- })
-
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "CustomExtensions-NotCalled-Server-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- ExpectedCustomExtension: &emptyString,
- },
- },
- flags: []string{"-enable-server-custom-extension", "-custom-extension-fail-add"},
- })
-
// Test an unknown extension from the server.
testCases = append(testCases, testCase{
testType: clientTest,
@@ -10433,7 +10334,7 @@
config: Config{
MaxVersion: VersionTLS12,
Bugs: ProtocolBugs{
- CustomExtension: expectedContents,
+ CustomExtension: "custom extension",
},
},
shouldFail: true,
@@ -10446,7 +10347,7 @@
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
- CustomExtension: expectedContents,
+ CustomExtension: "custom extension",
},
},
shouldFail: true,
@@ -10459,7 +10360,7 @@
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
- CustomUnencryptedExtension: expectedContents,
+ CustomUnencryptedExtension: "custom extension",
},
},
shouldFail: true,
@@ -10567,11 +10468,6 @@
func addCurveTests() {
for _, curve := range testCurves {
for _, ver := range tlsVersions {
- // SSL 3.0 cannot reliably negotiate curves.
- if ver.version == VersionSSL30 {
- continue
- }
-
suffix := curve.name + "-" + ver.name
testCases = append(testCases, testCase{
@@ -11286,6 +11182,44 @@
resumeSession: true,
expectResumeRejected: true,
})
+
+ for _, ver := range tlsVersions {
+ // Prior to TLS 1.3, disabling session tickets enables session IDs.
+ useStatefulResumption := ver.version < VersionTLS13
+
+ // SSL_OP_NO_TICKET implies the server must not mint any tickets.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: ver.name + "-NoTicket-NoMint",
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ ExpectNoNewSessionTicket: true,
+ RequireSessionIDs: useStatefulResumption,
+ },
+ },
+ resumeSession: useStatefulResumption,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-no-ticket"},
+ })
+
+ // SSL_OP_NO_TICKET implies the server must not accept any tickets.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: ver.name + "-NoTicket-NoAccept",
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ tls13Variant: ver.tls13Variant,
+ resumeSession: true,
+ expectResumeRejected: true,
+ // Set SSL_OP_NO_TICKET on the second connection, after the first
+ // has established tickets.
+ flags: []string{"-on-resume-no-ticket"},
+ })
+ }
}
func addChangeCipherSpecTests() {
@@ -12735,6 +12669,39 @@
expectedError: ":WRONG_CURVE:",
})
+ // Test that the supported_versions extension is enforced in the
+ // second ServerHello. Note we only enforce this starting draft 28.
+ if isDraft28(version.versionWire) {
+ testCases = append(testCases, testCase{
+ name: "SecondServerHelloNoVersion-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ OmitServerSupportedVersionExtension: true,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":SECOND_SERVERHELLO_VERSION_MISMATCH:",
+ })
+ testCases = append(testCases, testCase{
+ name: "SecondServerHelloWrongVersion-" + name,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // P-384 requires HelloRetryRequest in BoringSSL.
+ CurvePreferences: []CurveID{CurveP384},
+ Bugs: ProtocolBugs{
+ SendServerSupportedVersionExtension: 0x1234,
+ },
+ },
+ tls13Variant: variant,
+ shouldFail: true,
+ expectedError: ":SECOND_SERVERHELLO_VERSION_MISMATCH:",
+ })
+ }
+
testCases = append(testCases, testCase{
name: "RequestContextInHandshake-" + name,
config: Config{
@@ -14179,6 +14146,252 @@
}
}
+func addCertCompressionTests() {
+ // shrinkingPrefix is the first two bytes of a Certificate message.
+ shrinkingPrefix := []byte{0, 0}
+ // expandingPrefix is just some arbitrary byte string. This has to match the
+ // value in the shim.
+ expandingPrefix := []byte{1, 2, 3, 4}
+
+ shrinking := CertCompressionAlg{
+ Compress: func(uncompressed []byte) []byte {
+ if !bytes.HasPrefix(uncompressed, shrinkingPrefix) {
+ panic(fmt.Sprintf("cannot compress certificate message %x", uncompressed))
+ }
+ return uncompressed[len(shrinkingPrefix):]
+ },
+ Decompress: func(out []byte, compressed []byte) bool {
+ if len(out) != len(shrinkingPrefix)+len(compressed) {
+ return false
+ }
+
+ copy(out, shrinkingPrefix)
+ copy(out[len(shrinkingPrefix):], compressed)
+ return true
+ },
+ }
+
+ expanding := CertCompressionAlg{
+ Compress: func(uncompressed []byte) []byte {
+ ret := make([]byte, 0, len(expandingPrefix)+len(uncompressed))
+ ret = append(ret, expandingPrefix...)
+ return append(ret, uncompressed...)
+ },
+ Decompress: func(out []byte, compressed []byte) bool {
+ if !bytes.HasPrefix(compressed, expandingPrefix) {
+ return false
+ }
+ copy(out, compressed[len(expandingPrefix):])
+ return true
+ },
+ }
+
+ const (
+ shrinkingAlgId = 0xff01
+ expandingAlgId = 0xff02
+ )
+
+ for _, ver := range tlsVersions {
+ if ver.version < VersionTLS12 {
+ continue
+ }
+
+ // Duplicate compression algorithms is an error, even if nothing is
+ // configured.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "DuplicateCertCompressionExt-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ DuplicateCompressedCertAlgs: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":ERROR_PARSING_EXTENSION:",
+ })
+
+ // With compression algorithms configured, an duplicate values should still
+ // be an error.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "DuplicateCertCompressionExt2-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ DuplicateCompressedCertAlgs: true,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":ERROR_PARSING_EXTENSION:",
+ })
+
+ if ver.version < VersionTLS13 {
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "CertCompressionIgnoredBefore13-" + ver.name,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CertCompressionAlgs: map[uint16]CertCompressionAlg{expandingAlgId: expanding},
+ },
+ })
+
+ continue
+ }
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "CertCompressionExpands-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CertCompressionAlgs: map[uint16]CertCompressionAlg{expandingAlgId: expanding},
+ Bugs: ProtocolBugs{
+ ExpectedCompressedCert: expandingAlgId,
+ },
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "CertCompressionShrinks-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CertCompressionAlgs: map[uint16]CertCompressionAlg{shrinkingAlgId: shrinking},
+ Bugs: ProtocolBugs{
+ ExpectedCompressedCert: shrinkingAlgId,
+ },
+ },
+ })
+
+ // With both algorithms configured, the server should pick its most
+ // preferable. (Which is expandingAlgId.)
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "CertCompressionPriority-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CertCompressionAlgs: map[uint16]CertCompressionAlg{
+ shrinkingAlgId: shrinking,
+ expandingAlgId: expanding,
+ },
+ Bugs: ProtocolBugs{
+ ExpectedCompressedCert: expandingAlgId,
+ },
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "CertCompressionExpandsClient-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CertCompressionAlgs: map[uint16]CertCompressionAlg{
+ expandingAlgId: expanding,
+ },
+ Bugs: ProtocolBugs{
+ ExpectedCompressedCert: expandingAlgId,
+ },
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "CertCompressionShrinksClient-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CertCompressionAlgs: map[uint16]CertCompressionAlg{
+ shrinkingAlgId: shrinking,
+ },
+ Bugs: ProtocolBugs{
+ ExpectedCompressedCert: shrinkingAlgId,
+ },
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "CertCompressionBadAlgIdClient-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CertCompressionAlgs: map[uint16]CertCompressionAlg{
+ shrinkingAlgId: shrinking,
+ },
+ Bugs: ProtocolBugs{
+ ExpectedCompressedCert: shrinkingAlgId,
+ SendCertCompressionAlgId: 1234,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNKNOWN_CERT_COMPRESSION_ALG:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "CertCompressionTooSmallClient-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CertCompressionAlgs: map[uint16]CertCompressionAlg{
+ shrinkingAlgId: shrinking,
+ },
+ Bugs: ProtocolBugs{
+ ExpectedCompressedCert: shrinkingAlgId,
+ SendCertUncompressedLength: 12,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":CERT_DECOMPRESSION_FAILED:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "CertCompressionTooLargeClient-" + ver.name,
+ tls13Variant: ver.tls13Variant,
+ flags: []string{"-install-cert-compression-algs"},
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CertCompressionAlgs: map[uint16]CertCompressionAlg{
+ shrinkingAlgId: shrinking,
+ },
+ Bugs: ProtocolBugs{
+ ExpectedCompressedCert: shrinkingAlgId,
+ SendCertUncompressedLength: 1 << 20,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNCOMPRESSED_CERT_TOO_LARGE:",
+ })
+ }
+}
+
func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
defer wg.Done()
@@ -14307,6 +14520,7 @@
addECDSAKeyUsageTests()
addExtraHandshakeTests()
addOmitExtensionsTests()
+ addCertCompressionTests()
testCases = append(testCases, convertToSplitHandshakeTests(testCases)...)
diff --git a/src/ssl/test/settings_writer.cc b/src/ssl/test/settings_writer.cc
new file mode 100644
index 0000000..66025f6
--- /dev/null
+++ b/src/ssl/test/settings_writer.cc
@@ -0,0 +1,121 @@
+/* Copyright (c) 2018, 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 "settings_writer.h"
+
+#include <stdio.h>
+
+#include <openssl/ssl.h>
+
+#include "fuzzer_tags.h"
+#include "test_config.h"
+
+
+SettingsWriter::SettingsWriter() {}
+
+bool SettingsWriter::Init(int i, const TestConfig *config,
+ SSL_SESSION *session) {
+ if (config->write_settings.empty()) {
+ return true;
+ }
+ // Treat write_settings as a path prefix for each connection in the run.
+ char buf[DECIMAL_SIZE(int)];
+ snprintf(buf, sizeof(buf), "%d", i);
+ path_ = config->write_settings + buf;
+
+ if (!CBB_init(cbb_.get(), 64)) {
+ return false;
+ }
+
+ if (session != nullptr) {
+ uint8_t *data;
+ size_t len;
+ if (!SSL_SESSION_to_bytes(session, &data, &len)) {
+ return false;
+ }
+ bssl::UniquePtr<uint8_t> free_data(data);
+ CBB child;
+ if (!CBB_add_u16(cbb_.get(), kSessionTag) ||
+ !CBB_add_u24_length_prefixed(cbb_.get(), &child) ||
+ !CBB_add_bytes(&child, data, len) || !CBB_flush(cbb_.get())) {
+ return false;
+ }
+ }
+
+ if (config->is_server &&
+ (config->require_any_client_certificate || config->verify_peer) &&
+ !CBB_add_u16(cbb_.get(), kRequestClientCert)) {
+ return false;
+ }
+
+ if (config->tls13_variant != 0 &&
+ (!CBB_add_u16(cbb_.get(), kTLS13Variant) ||
+ !CBB_add_u8(cbb_.get(), static_cast<uint8_t>(config->tls13_variant)))) {
+ return false;
+ }
+
+ return true;
+}
+
+bool SettingsWriter::Commit() {
+ if (path_.empty()) {
+ return true;
+ }
+
+ uint8_t *settings;
+ size_t settings_len;
+ if (!CBB_add_u16(cbb_.get(), kDataTag) ||
+ !CBB_finish(cbb_.get(), &settings, &settings_len)) {
+ return false;
+ }
+ bssl::UniquePtr<uint8_t> free_settings(settings);
+
+ using ScopedFILE = std::unique_ptr<FILE, decltype(&fclose)>;
+ ScopedFILE file(fopen(path_.c_str(), "w"), fclose);
+ if (!file) {
+ return false;
+ }
+
+ return fwrite(settings, settings_len, 1, file.get()) == 1;
+}
+
+bool SettingsWriter::WriteHandoff(bssl::Span<const uint8_t> handoff) {
+ if (path_.empty()) {
+ return true;
+ }
+
+ CBB child;
+ if (!CBB_add_u16(cbb_.get(), kHandoffTag) ||
+ !CBB_add_u24_length_prefixed(cbb_.get(), &child) ||
+ !CBB_add_bytes(&child, handoff.data(), handoff.size()) ||
+ !CBB_flush(cbb_.get())) {
+ return false;
+ }
+ return true;
+}
+
+bool SettingsWriter::WriteHandback(bssl::Span<const uint8_t> handback) {
+ if (path_.empty()) {
+ return true;
+ }
+
+ CBB child;
+ if (!CBB_add_u16(cbb_.get(), kHandbackTag) ||
+ !CBB_add_u24_length_prefixed(cbb_.get(), &child) ||
+ !CBB_add_bytes(&child, handback.data(), handback.size()) ||
+ !CBB_flush(cbb_.get())) {
+ return false;
+ }
+ return true;
+}
diff --git a/src/ssl/test/settings_writer.h b/src/ssl/test/settings_writer.h
new file mode 100644
index 0000000..322850d
--- /dev/null
+++ b/src/ssl/test/settings_writer.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2018, 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. */
+
+#ifndef HEADER_SETTINGS_WRITER
+#define HEADER_SETTINGS_WRITER
+
+#include <string>
+
+#include <openssl/bytestring.h>
+#include <openssl/ssl.h>
+
+#include "../internal.h"
+#include "test_config.h"
+
+struct SettingsWriter {
+ public:
+ SettingsWriter();
+
+ // Init initializes the writer for a new connection, given by |i|. Each
+ // connection gets a unique output file.
+ bool Init(int i, const TestConfig *config, SSL_SESSION *session);
+
+ // Commit writes the buffered data to disk.
+ bool Commit();
+
+ bool WriteHandoff(bssl::Span<const uint8_t> handoff);
+
+ bool WriteHandback(bssl::Span<const uint8_t> handback);
+
+ private:
+ std::string path_;
+ bssl::ScopedCBB cbb_;
+};
+
+#endif // HEADER_SETTINGS_WRITER
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index f74267d..d92cf72 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -14,6 +14,7 @@
#include "test_config.h"
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -21,6 +22,12 @@
#include <memory>
#include <openssl/base64.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#include "../../crypto/internal.h"
+#include "../internal.h"
+#include "test_state.h"
namespace {
@@ -59,11 +66,12 @@
{ "-no-tls12", &TestConfig::no_tls12 },
{ "-no-tls11", &TestConfig::no_tls11 },
{ "-no-tls1", &TestConfig::no_tls1 },
- { "-no-ssl3", &TestConfig::no_ssl3 },
+ { "-no-ticket", &TestConfig::no_ticket },
{ "-enable-channel-id", &TestConfig::enable_channel_id },
{ "-shim-writes-first", &TestConfig::shim_writes_first },
{ "-expect-session-miss", &TestConfig::expect_session_miss },
{ "-decline-alpn", &TestConfig::decline_alpn },
+ { "-select-empty-alpn", &TestConfig::select_empty_alpn },
{ "-expect-extended-master-secret",
&TestConfig::expect_extended_master_secret },
{ "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling },
@@ -74,7 +82,6 @@
{ "-fail-early-callback", &TestConfig::fail_early_callback },
{ "-install-ddos-callback", &TestConfig::install_ddos_callback },
{ "-fail-ddos-callback", &TestConfig::fail_ddos_callback },
- { "-fail-second-ddos-callback", &TestConfig::fail_second_ddos_callback },
{ "-fail-cert-callback", &TestConfig::fail_cert_callback },
{ "-handshake-never-done", &TestConfig::handshake_never_done },
{ "-use-export-context", &TestConfig::use_export_context },
@@ -86,12 +93,6 @@
{ "-use-ticket-callback", &TestConfig::use_ticket_callback },
{ "-renew-ticket", &TestConfig::renew_ticket },
{ "-enable-early-data", &TestConfig::enable_early_data },
- { "-enable-client-custom-extension",
- &TestConfig::enable_client_custom_extension },
- { "-enable-server-custom-extension",
- &TestConfig::enable_server_custom_extension },
- { "-custom-extension-skip", &TestConfig::custom_extension_skip },
- { "-custom-extension-fail-add", &TestConfig::custom_extension_fail_add },
{ "-check-close-notify", &TestConfig::check_close_notify },
{ "-shim-shuts-down", &TestConfig::shim_shuts_down },
{ "-verify-fail", &TestConfig::verify_fail },
@@ -101,6 +102,8 @@
{ "-renegotiate-once", &TestConfig::renegotiate_once },
{ "-renegotiate-freely", &TestConfig::renegotiate_freely },
{ "-renegotiate-ignore", &TestConfig::renegotiate_ignore },
+ { "-forbid-renegotiation-after-handshake",
+ &TestConfig::forbid_renegotiation_after_handshake },
{ "-p384-only", &TestConfig::p384_only },
{ "-enable-all-curves", &TestConfig::enable_all_curves },
{ "-use-old-client-cert-callback",
@@ -134,6 +137,14 @@
{ "-handoff", &TestConfig::handoff },
{ "-expect-dummy-pq-padding", &TestConfig::expect_dummy_pq_padding },
{ "-no-rsa-pss-rsae-certs", &TestConfig::no_rsa_pss_rsae_certs },
+ { "-use-ocsp-callback", &TestConfig::use_ocsp_callback },
+ { "-set-ocsp-in-callback", &TestConfig::set_ocsp_in_callback },
+ { "-decline-ocsp-callback", &TestConfig::decline_ocsp_callback },
+ { "-fail-ocsp-callback", &TestConfig::fail_ocsp_callback },
+ { "-install-cert-compression-algs",
+ &TestConfig::install_cert_compression_algs },
+ { "-is-handshaker-supported", &TestConfig::is_handshaker_supported },
+ { "-handshaker-resume", &TestConfig::handshaker_resume },
};
const Flag<std::string> kStringFlags[] = {
@@ -161,6 +172,7 @@
{ "-use-client-ca-list", &TestConfig::use_client_ca_list },
{ "-expect-client-ca-list", &TestConfig::expected_client_ca_list },
{ "-expect-msg-callback", &TestConfig::expect_msg_callback },
+ { "-handshaker-path", &TestConfig::handshaker_path },
};
const Flag<std::string> kBase64Flags[] = {
@@ -209,6 +221,8 @@
const Flag<std::vector<int>> kIntVectorFlags[] = {
{ "-signing-prefs", &TestConfig::signing_prefs },
{ "-verify-prefs", &TestConfig::verify_prefs },
+ { "-expect-peer-verify-pref",
+ &TestConfig::expected_peer_verify_prefs },
};
bool ParseFlag(char *flag, int argc, char **argv, int *i,
@@ -303,6 +317,8 @@
TestConfig *out_initial,
TestConfig *out_resume,
TestConfig *out_retry) {
+ out_initial->argc = out_resume->argc = out_retry->argc = argc;
+ out_initial->argv = out_resume->argv = out_retry->argv = argv;
for (int i = 0; i < argc; i++) {
bool skip = false;
char *flag = argv[i];
@@ -332,3 +348,1297 @@
return true;
}
+
+static CRYPTO_once_t once = CRYPTO_ONCE_INIT;
+static int g_config_index = 0;
+static CRYPTO_BUFFER_POOL *g_pool = nullptr;
+
+static void init_once() {
+ g_config_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ if (g_config_index < 0) {
+ abort();
+ }
+ g_pool = CRYPTO_BUFFER_POOL_new();
+ if (!g_pool) {
+ abort();
+ }
+}
+
+bool SetTestConfig(SSL *ssl, const TestConfig *config) {
+ CRYPTO_once(&once, init_once);
+ return SSL_set_ex_data(ssl, g_config_index, (void *)config) == 1;
+}
+
+const TestConfig *GetTestConfig(const SSL *ssl) {
+ CRYPTO_once(&once, init_once);
+ return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index);
+}
+
+static int LegacyOCSPCallback(SSL *ssl, void *arg) {
+ const TestConfig *config = GetTestConfig(ssl);
+ if (!SSL_is_server(ssl)) {
+ return !config->fail_ocsp_callback;
+ }
+
+ if (!config->ocsp_response.empty() && config->set_ocsp_in_callback &&
+ !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(),
+ config->ocsp_response.size())) {
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ if (config->fail_ocsp_callback) {
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ if (config->decline_ocsp_callback) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+ return SSL_TLSEXT_ERR_OK;
+}
+
+static int ServerNameCallback(SSL *ssl, int *out_alert, void *arg) {
+ // SNI must be accessible from the SNI callback.
+ const TestConfig *config = GetTestConfig(ssl);
+ const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (server_name == nullptr ||
+ std::string(server_name) != config->expected_server_name) {
+ fprintf(stderr, "servername mismatch (got %s; want %s)\n", server_name,
+ config->expected_server_name.c_str());
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+static int NextProtoSelectCallback(SSL *ssl, uint8_t **out, uint8_t *outlen,
+ const uint8_t *in, unsigned inlen,
+ void *arg) {
+ const TestConfig *config = GetTestConfig(ssl);
+ if (config->select_next_proto.empty()) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ *out = (uint8_t *)config->select_next_proto.data();
+ *outlen = config->select_next_proto.size();
+ return SSL_TLSEXT_ERR_OK;
+}
+
+static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out,
+ unsigned int *out_len, void *arg) {
+ const TestConfig *config = GetTestConfig(ssl);
+ if (config->advertise_npn.empty()) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ *out = (const uint8_t *)config->advertise_npn.data();
+ *out_len = config->advertise_npn.size();
+ return SSL_TLSEXT_ERR_OK;
+}
+
+static void MessageCallback(int is_write, int version, int content_type,
+ const void *buf, size_t len, SSL *ssl, void *arg) {
+ const uint8_t *buf_u8 = reinterpret_cast<const uint8_t *>(buf);
+ const TestConfig *config = GetTestConfig(ssl);
+ TestState *state = GetTestState(ssl);
+ if (!state->msg_callback_ok) {
+ return;
+ }
+
+ if (content_type == SSL3_RT_HEADER) {
+ if (len !=
+ (config->is_dtls ? DTLS1_RT_HEADER_LENGTH : SSL3_RT_HEADER_LENGTH)) {
+ fprintf(stderr, "Incorrect length for record header: %zu\n", len);
+ state->msg_callback_ok = false;
+ }
+ return;
+ }
+
+ state->msg_callback_text += is_write ? "write " : "read ";
+ switch (content_type) {
+ case 0:
+ if (version != SSL2_VERSION) {
+ fprintf(stderr, "Incorrect version for V2ClientHello: %x\n", version);
+ state->msg_callback_ok = false;
+ return;
+ }
+ state->msg_callback_text += "v2clienthello\n";
+ return;
+
+ case SSL3_RT_HANDSHAKE: {
+ CBS cbs;
+ CBS_init(&cbs, buf_u8, len);
+ uint8_t type;
+ uint32_t msg_len;
+ if (!CBS_get_u8(&cbs, &type) ||
+ // TODO(davidben): Reporting on entire messages would be more
+ // consistent than fragments.
+ (config->is_dtls &&
+ !CBS_skip(&cbs, 3 /* total */ + 2 /* seq */ + 3 /* frag_off */)) ||
+ !CBS_get_u24(&cbs, &msg_len) || !CBS_skip(&cbs, msg_len) ||
+ CBS_len(&cbs) != 0) {
+ fprintf(stderr, "Could not parse handshake message.\n");
+ state->msg_callback_ok = false;
+ return;
+ }
+ char text[16];
+ snprintf(text, sizeof(text), "hs %d\n", type);
+ state->msg_callback_text += text;
+ return;
+ }
+
+ case SSL3_RT_CHANGE_CIPHER_SPEC:
+ if (len != 1 || buf_u8[0] != 1) {
+ fprintf(stderr, "Invalid ChangeCipherSpec.\n");
+ state->msg_callback_ok = false;
+ return;
+ }
+ state->msg_callback_text += "ccs\n";
+ return;
+
+ case SSL3_RT_ALERT:
+ if (len != 2) {
+ fprintf(stderr, "Invalid alert.\n");
+ state->msg_callback_ok = false;
+ return;
+ }
+ char text[16];
+ snprintf(text, sizeof(text), "alert %d %d\n", buf_u8[0], buf_u8[1]);
+ state->msg_callback_text += text;
+ return;
+
+ default:
+ fprintf(stderr, "Invalid content_type: %d\n", content_type);
+ state->msg_callback_ok = false;
+ }
+}
+
+static int TicketKeyCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv,
+ EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
+ int encrypt) {
+ if (!encrypt) {
+ if (GetTestState(ssl)->ticket_decrypt_done) {
+ fprintf(stderr, "TicketKeyCallback called after completion.\n");
+ return -1;
+ }
+
+ GetTestState(ssl)->ticket_decrypt_done = true;
+ }
+
+ // This is just test code, so use the all-zeros key.
+ static const uint8_t kZeros[16] = {0};
+
+ if (encrypt) {
+ OPENSSL_memcpy(key_name, kZeros, sizeof(kZeros));
+ RAND_bytes(iv, 16);
+ } else if (OPENSSL_memcmp(key_name, kZeros, 16) != 0) {
+ return 0;
+ }
+
+ if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) ||
+ !EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) {
+ return -1;
+ }
+
+ if (!encrypt) {
+ return GetTestConfig(ssl)->renew_ticket ? 2 : 1;
+ }
+ return 1;
+}
+
+static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) {
+ // This callback is called as the handshake completes. |SSL_get_session|
+ // must continue to work and, historically, |SSL_in_init| returned false at
+ // this point.
+ if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) {
+ fprintf(stderr, "Invalid state for NewSessionCallback.\n");
+ abort();
+ }
+
+ GetTestState(ssl)->got_new_session = true;
+ GetTestState(ssl)->new_session.reset(session);
+ return 1;
+}
+
+static void InfoCallback(const SSL *ssl, int type, int val) {
+ if (type == SSL_CB_HANDSHAKE_DONE) {
+ if (GetTestConfig(ssl)->handshake_never_done) {
+ fprintf(stderr, "Handshake unexpectedly completed.\n");
+ // Abort before any expected error code is printed, to ensure the overall
+ // test fails.
+ abort();
+ }
+ // This callback is called when the handshake completes. |SSL_get_session|
+ // must continue to work and |SSL_in_init| must return false.
+ if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) {
+ fprintf(stderr, "Invalid state for SSL_CB_HANDSHAKE_DONE.\n");
+ abort();
+ }
+ GetTestState(ssl)->handshake_done = true;
+
+ // Callbacks may be called again on a new handshake.
+ GetTestState(ssl)->ticket_decrypt_done = false;
+ GetTestState(ssl)->alpn_select_done = false;
+ }
+}
+
+static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) {
+ *out_pkey = GetTestState(ssl)->channel_id.release();
+}
+
+static SSL_SESSION *GetSessionCallback(SSL *ssl, const uint8_t *data, int len,
+ int *copy) {
+ TestState *async_state = GetTestState(ssl);
+ if (async_state->session) {
+ *copy = 0;
+ return async_state->session.release();
+ } else if (async_state->pending_session) {
+ return SSL_magic_pending_session_ptr();
+ } else {
+ return NULL;
+ }
+}
+
+static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) {
+ *out_clock = *GetClock();
+}
+
+static int AlpnSelectCallback(SSL *ssl, const uint8_t **out, uint8_t *outlen,
+ const uint8_t *in, unsigned inlen, void *arg) {
+ if (GetTestState(ssl)->alpn_select_done) {
+ fprintf(stderr, "AlpnSelectCallback called after completion.\n");
+ exit(1);
+ }
+
+ GetTestState(ssl)->alpn_select_done = true;
+
+ const TestConfig *config = GetTestConfig(ssl);
+ if (config->decline_alpn) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ if (!config->expected_advertised_alpn.empty() &&
+ (config->expected_advertised_alpn.size() != inlen ||
+ OPENSSL_memcmp(config->expected_advertised_alpn.data(), in, inlen) !=
+ 0)) {
+ fprintf(stderr, "bad ALPN select callback inputs\n");
+ exit(1);
+ }
+
+ assert(config->select_alpn.empty() || !config->select_empty_alpn);
+ *out = (const uint8_t *)config->select_alpn.data();
+ *outlen = config->select_alpn.size();
+ return SSL_TLSEXT_ERR_OK;
+}
+
+static bool CheckVerifyCallback(SSL *ssl) {
+ const TestConfig *config = GetTestConfig(ssl);
+ if (!config->expected_ocsp_response.empty()) {
+ const uint8_t *data;
+ size_t len;
+ SSL_get0_ocsp_response(ssl, &data, &len);
+ if (len == 0) {
+ fprintf(stderr, "OCSP response not available in verify callback\n");
+ return false;
+ }
+ }
+
+ if (GetTestState(ssl)->cert_verified) {
+ fprintf(stderr, "Certificate verified twice.\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) {
+ SSL *ssl = (SSL *)X509_STORE_CTX_get_ex_data(
+ store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ const TestConfig *config = GetTestConfig(ssl);
+ if (!CheckVerifyCallback(ssl)) {
+ return 0;
+ }
+
+ GetTestState(ssl)->cert_verified = true;
+ if (config->verify_fail) {
+ store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION;
+ return 0;
+ }
+
+ return 1;
+}
+
+bool LoadCertificate(bssl::UniquePtr<X509> *out_x509,
+ bssl::UniquePtr<STACK_OF(X509)> *out_chain,
+ const std::string &file) {
+ bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
+ if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
+ return false;
+ }
+
+ out_x509->reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+ if (!*out_x509) {
+ return false;
+ }
+
+ out_chain->reset(sk_X509_new_null());
+ if (!*out_chain) {
+ return false;
+ }
+
+ // Keep reading the certificate chain.
+ for (;;) {
+ bssl::UniquePtr<X509> cert(
+ PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+ if (!cert) {
+ break;
+ }
+
+ if (!bssl::PushToStack(out_chain->get(), std::move(cert))) {
+ return false;
+ }
+ }
+
+ uint32_t err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
+ ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
+ return false;
+ }
+
+ ERR_clear_error();
+ return true;
+}
+
+bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) {
+ bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
+ if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
+ return nullptr;
+ }
+ return bssl::UniquePtr<EVP_PKEY>(
+ PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL));
+}
+
+static bool GetCertificate(SSL *ssl, bssl::UniquePtr<X509> *out_x509,
+ bssl::UniquePtr<STACK_OF(X509)> *out_chain,
+ bssl::UniquePtr<EVP_PKEY> *out_pkey) {
+ const TestConfig *config = GetTestConfig(ssl);
+
+ if (!config->signing_prefs.empty()) {
+ std::vector<uint16_t> u16s(config->signing_prefs.begin(),
+ config->signing_prefs.end());
+ if (!SSL_set_signing_algorithm_prefs(ssl, u16s.data(), u16s.size())) {
+ return false;
+ }
+ }
+
+ if (!config->key_file.empty()) {
+ *out_pkey = LoadPrivateKey(config->key_file.c_str());
+ if (!*out_pkey) {
+ return false;
+ }
+ }
+ if (!config->cert_file.empty() &&
+ !LoadCertificate(out_x509, out_chain, config->cert_file.c_str())) {
+ return false;
+ }
+ if (!config->ocsp_response.empty() && !config->set_ocsp_in_callback &&
+ !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(),
+ config->ocsp_response.size())) {
+ return false;
+ }
+ return true;
+}
+
+static bool FromHexDigit(uint8_t *out, char c) {
+ if ('0' <= c && c <= '9') {
+ *out = c - '0';
+ return true;
+ }
+ if ('a' <= c && c <= 'f') {
+ *out = c - 'a' + 10;
+ return true;
+ }
+ if ('A' <= c && c <= 'F') {
+ *out = c - 'A' + 10;
+ return true;
+ }
+ return false;
+}
+
+static bool HexDecode(std::string *out, const std::string &in) {
+ if ((in.size() & 1) != 0) {
+ return false;
+ }
+
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[in.size() / 2]);
+ for (size_t i = 0; i < in.size() / 2; i++) {
+ uint8_t high, low;
+ if (!FromHexDigit(&high, in[i * 2]) || !FromHexDigit(&low, in[i * 2 + 1])) {
+ return false;
+ }
+ buf[i] = (high << 4) | low;
+ }
+
+ out->assign(reinterpret_cast<const char *>(buf.get()), in.size() / 2);
+ return true;
+}
+
+static std::vector<std::string> SplitParts(const std::string &in,
+ const char delim) {
+ std::vector<std::string> ret;
+ size_t start = 0;
+
+ for (size_t i = 0; i < in.size(); i++) {
+ if (in[i] == delim) {
+ ret.push_back(in.substr(start, i - start));
+ start = i + 1;
+ }
+ }
+
+ ret.push_back(in.substr(start, std::string::npos));
+ return ret;
+}
+
+static std::vector<std::string> DecodeHexStrings(
+ const std::string &hex_strings) {
+ std::vector<std::string> ret;
+ const std::vector<std::string> parts = SplitParts(hex_strings, ',');
+
+ for (const auto &part : parts) {
+ std::string binary;
+ if (!HexDecode(&binary, part)) {
+ fprintf(stderr, "Bad hex string: %s\n", part.c_str());
+ return ret;
+ }
+
+ ret.push_back(binary);
+ }
+
+ return ret;
+}
+
+static bssl::UniquePtr<STACK_OF(X509_NAME)> DecodeHexX509Names(
+ const std::string &hex_names) {
+ const std::vector<std::string> der_names = DecodeHexStrings(hex_names);
+ bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null());
+ if (!ret) {
+ return nullptr;
+ }
+
+ for (const auto &der_name : der_names) {
+ const uint8_t *const data =
+ reinterpret_cast<const uint8_t *>(der_name.data());
+ const uint8_t *derp = data;
+ bssl::UniquePtr<X509_NAME> name(
+ d2i_X509_NAME(nullptr, &derp, der_name.size()));
+ if (!name || derp != data + der_name.size()) {
+ fprintf(stderr, "Failed to parse X509_NAME.\n");
+ return nullptr;
+ }
+
+ if (!bssl::PushToStack(ret.get(), std::move(name))) {
+ return nullptr;
+ }
+ }
+
+ return ret;
+}
+
+static bool CheckPeerVerifyPrefs(SSL *ssl) {
+ const TestConfig *config = GetTestConfig(ssl);
+ if (!config->expected_peer_verify_prefs.empty()) {
+ const uint16_t *peer_sigalgs;
+ size_t num_peer_sigalgs =
+ SSL_get0_peer_verify_algorithms(ssl, &peer_sigalgs);
+ if (config->expected_peer_verify_prefs.size() != num_peer_sigalgs) {
+ fprintf(stderr,
+ "peer verify preferences length mismatch (got %zu, wanted %zu)\n",
+ num_peer_sigalgs, config->expected_peer_verify_prefs.size());
+ return false;
+ }
+ for (size_t i = 0; i < num_peer_sigalgs; i++) {
+ if (static_cast<int>(peer_sigalgs[i]) !=
+ config->expected_peer_verify_prefs[i]) {
+ fprintf(stderr,
+ "peer verify preference %zu mismatch (got %04x, wanted %04x\n",
+ i, peer_sigalgs[i], config->expected_peer_verify_prefs[i]);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static bool CheckCertificateRequest(SSL *ssl) {
+ const TestConfig *config = GetTestConfig(ssl);
+
+ if (!CheckPeerVerifyPrefs(ssl)) {
+ return false;
+ }
+
+ if (!config->expected_certificate_types.empty()) {
+ const uint8_t *certificate_types;
+ size_t certificate_types_len =
+ SSL_get0_certificate_types(ssl, &certificate_types);
+ if (certificate_types_len != config->expected_certificate_types.size() ||
+ OPENSSL_memcmp(certificate_types,
+ config->expected_certificate_types.data(),
+ certificate_types_len) != 0) {
+ fprintf(stderr, "certificate types mismatch\n");
+ return false;
+ }
+ }
+
+ if (!config->expected_client_ca_list.empty()) {
+ bssl::UniquePtr<STACK_OF(X509_NAME)> expected =
+ DecodeHexX509Names(config->expected_client_ca_list);
+ const size_t num_expected = sk_X509_NAME_num(expected.get());
+
+ const STACK_OF(X509_NAME) *received = SSL_get_client_CA_list(ssl);
+ const size_t num_received = sk_X509_NAME_num(received);
+
+ if (num_received != num_expected) {
+ fprintf(stderr, "expected %u names in CertificateRequest but got %u\n",
+ static_cast<unsigned>(num_expected),
+ static_cast<unsigned>(num_received));
+ return false;
+ }
+
+ for (size_t i = 0; i < num_received; i++) {
+ if (X509_NAME_cmp(sk_X509_NAME_value(received, i),
+ sk_X509_NAME_value(expected.get(), i)) != 0) {
+ fprintf(stderr, "names in CertificateRequest differ at index #%d\n",
+ static_cast<unsigned>(i));
+ return false;
+ }
+ }
+
+ const STACK_OF(CRYPTO_BUFFER) *buffers = SSL_get0_server_requested_CAs(ssl);
+ if (sk_CRYPTO_BUFFER_num(buffers) != num_received) {
+ fprintf(stderr,
+ "Mismatch between SSL_get_server_requested_CAs and "
+ "SSL_get_client_CA_list.\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) {
+ if (!CheckCertificateRequest(ssl)) {
+ return -1;
+ }
+
+ if (GetTestConfig(ssl)->async && !GetTestState(ssl)->cert_ready) {
+ return -1;
+ }
+
+ bssl::UniquePtr<X509> x509;
+ bssl::UniquePtr<STACK_OF(X509)> chain;
+ bssl::UniquePtr<EVP_PKEY> pkey;
+ if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
+ return -1;
+ }
+
+ // Return zero for no certificate.
+ if (!x509) {
+ return 0;
+ }
+
+ // Chains and asynchronous private keys are not supported with client_cert_cb.
+ *out_x509 = x509.release();
+ *out_pkey = pkey.release();
+ return 1;
+}
+
+static ssl_private_key_result_t AsyncPrivateKeySign(
+ SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+ uint16_t signature_algorithm, const uint8_t *in, size_t in_len) {
+ TestState *test_state = GetTestState(ssl);
+ if (!test_state->private_key_result.empty()) {
+ fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n");
+ abort();
+ }
+
+ if (EVP_PKEY_id(test_state->private_key.get()) !=
+ SSL_get_signature_algorithm_key_type(signature_algorithm)) {
+ fprintf(stderr, "Key type does not match signature algorithm.\n");
+ abort();
+ }
+
+ // Determine the hash.
+ const EVP_MD *md = SSL_get_signature_algorithm_digest(signature_algorithm);
+ bssl::ScopedEVP_MD_CTX ctx;
+ EVP_PKEY_CTX *pctx;
+ if (!EVP_DigestSignInit(ctx.get(), &pctx, md, nullptr,
+ test_state->private_key.get())) {
+ return ssl_private_key_failure;
+ }
+
+ // Configure additional signature parameters.
+ if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) {
+ if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
+ !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* salt len = hash len */)) {
+ return ssl_private_key_failure;
+ }
+ }
+
+ // Write the signature into |test_state|.
+ size_t len = 0;
+ if (!EVP_DigestSign(ctx.get(), nullptr, &len, in, in_len)) {
+ return ssl_private_key_failure;
+ }
+ test_state->private_key_result.resize(len);
+ if (!EVP_DigestSign(ctx.get(), test_state->private_key_result.data(), &len,
+ in, in_len)) {
+ return ssl_private_key_failure;
+ }
+ test_state->private_key_result.resize(len);
+
+ // The signature will be released asynchronously in |AsyncPrivateKeyComplete|.
+ return ssl_private_key_retry;
+}
+
+static ssl_private_key_result_t AsyncPrivateKeyDecrypt(SSL *ssl, uint8_t *out,
+ size_t *out_len,
+ size_t max_out,
+ const uint8_t *in,
+ size_t in_len) {
+ TestState *test_state = GetTestState(ssl);
+ if (!test_state->private_key_result.empty()) {
+ fprintf(stderr, "AsyncPrivateKeyDecrypt called with operation pending.\n");
+ abort();
+ }
+
+ RSA *rsa = EVP_PKEY_get0_RSA(test_state->private_key.get());
+ if (rsa == NULL) {
+ fprintf(stderr, "AsyncPrivateKeyDecrypt called with incorrect key type.\n");
+ abort();
+ }
+ test_state->private_key_result.resize(RSA_size(rsa));
+ if (!RSA_decrypt(rsa, out_len, test_state->private_key_result.data(),
+ RSA_size(rsa), in, in_len, RSA_NO_PADDING)) {
+ return ssl_private_key_failure;
+ }
+
+ test_state->private_key_result.resize(*out_len);
+
+ // The decryption will be released asynchronously in |AsyncPrivateComplete|.
+ return ssl_private_key_retry;
+}
+
+static ssl_private_key_result_t AsyncPrivateKeyComplete(SSL *ssl, uint8_t *out,
+ size_t *out_len,
+ size_t max_out) {
+ TestState *test_state = GetTestState(ssl);
+ if (test_state->private_key_result.empty()) {
+ fprintf(stderr,
+ "AsyncPrivateKeyComplete called without operation pending.\n");
+ abort();
+ }
+
+ if (test_state->private_key_retries < 2) {
+ // Only return the decryption on the second attempt, to test both incomplete
+ // |decrypt| and |decrypt_complete|.
+ return ssl_private_key_retry;
+ }
+
+ if (max_out < test_state->private_key_result.size()) {
+ fprintf(stderr, "Output buffer too small.\n");
+ return ssl_private_key_failure;
+ }
+ OPENSSL_memcpy(out, test_state->private_key_result.data(),
+ test_state->private_key_result.size());
+ *out_len = test_state->private_key_result.size();
+
+ test_state->private_key_result.clear();
+ test_state->private_key_retries = 0;
+ return ssl_private_key_success;
+}
+
+static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = {
+ AsyncPrivateKeySign,
+ AsyncPrivateKeyDecrypt,
+ AsyncPrivateKeyComplete,
+};
+
+static bool InstallCertificate(SSL *ssl) {
+ bssl::UniquePtr<X509> x509;
+ bssl::UniquePtr<STACK_OF(X509)> chain;
+ bssl::UniquePtr<EVP_PKEY> pkey;
+ if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
+ return false;
+ }
+
+ if (pkey) {
+ TestState *test_state = GetTestState(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
+ if (config->async) {
+ test_state->private_key = std::move(pkey);
+ SSL_set_private_key_method(ssl, &g_async_private_key_method);
+ } else if (!SSL_use_PrivateKey(ssl, pkey.get())) {
+ return false;
+ }
+ }
+
+ if (x509 && !SSL_use_certificate(ssl, x509.get())) {
+ return false;
+ }
+
+ if (sk_X509_num(chain.get()) > 0 && !SSL_set1_chain(ssl, chain.get())) {
+ return false;
+ }
+
+ return true;
+}
+
+static enum ssl_select_cert_result_t SelectCertificateCallback(
+ const SSL_CLIENT_HELLO *client_hello) {
+ const TestConfig *config = GetTestConfig(client_hello->ssl);
+ GetTestState(client_hello->ssl)->early_callback_called = true;
+
+ if (!config->expected_server_name.empty()) {
+ const uint8_t *extension_data;
+ size_t extension_len;
+ CBS extension, server_name_list, host_name;
+ uint8_t name_type;
+
+ if (!SSL_early_callback_ctx_extension_get(
+ client_hello, TLSEXT_TYPE_server_name, &extension_data,
+ &extension_len)) {
+ fprintf(stderr, "Could not find server_name extension.\n");
+ return ssl_select_cert_error;
+ }
+
+ CBS_init(&extension, extension_data, extension_len);
+ if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) ||
+ CBS_len(&extension) != 0 ||
+ !CBS_get_u8(&server_name_list, &name_type) ||
+ name_type != TLSEXT_NAMETYPE_host_name ||
+ !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
+ CBS_len(&server_name_list) != 0) {
+ fprintf(stderr, "Could not decode server_name extension.\n");
+ return ssl_select_cert_error;
+ }
+
+ if (!CBS_mem_equal(&host_name,
+ (const uint8_t *)config->expected_server_name.data(),
+ config->expected_server_name.size())) {
+ fprintf(stderr, "Server name mismatch.\n");
+ }
+ }
+
+ if (config->fail_early_callback) {
+ return ssl_select_cert_error;
+ }
+
+ // Install the certificate in the early callback.
+ if (config->use_early_callback) {
+ bool early_callback_ready =
+ GetTestState(client_hello->ssl)->early_callback_ready;
+ if (config->async && !early_callback_ready) {
+ // Install the certificate asynchronously.
+ return ssl_select_cert_retry;
+ }
+ if (!InstallCertificate(client_hello->ssl)) {
+ return ssl_select_cert_error;
+ }
+ }
+ return ssl_select_cert_success;
+}
+
+bssl::UniquePtr<SSL_CTX> TestConfig::SetupCtx(SSL_CTX *old_ctx) const {
+ bssl::UniquePtr<SSL_CTX> ssl_ctx(
+ SSL_CTX_new(is_dtls ? DTLS_method() : TLS_method()));
+ if (!ssl_ctx) {
+ return nullptr;
+ }
+
+ CRYPTO_once(&once, init_once);
+ SSL_CTX_set0_buffer_pool(ssl_ctx.get(), g_pool);
+
+ // Enable TLS 1.3 for tests.
+ if (!is_dtls &&
+ !SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION)) {
+ return nullptr;
+ }
+
+ std::string cipher_list = "ALL";
+ if (!cipher.empty()) {
+ cipher_list = cipher;
+ SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+ if (!SSL_CTX_set_strict_cipher_list(ssl_ctx.get(), cipher_list.c_str())) {
+ return nullptr;
+ }
+
+ if (async && is_server) {
+ // Disable the internal session cache. To test asynchronous session lookup,
+ // we use an external session cache.
+ SSL_CTX_set_session_cache_mode(
+ ssl_ctx.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_get_cb(ssl_ctx.get(), GetSessionCallback);
+ } else {
+ SSL_CTX_set_session_cache_mode(ssl_ctx.get(), SSL_SESS_CACHE_BOTH);
+ }
+
+ SSL_CTX_set_select_certificate_cb(ssl_ctx.get(), SelectCertificateCallback);
+
+ if (use_old_client_cert_callback) {
+ SSL_CTX_set_client_cert_cb(ssl_ctx.get(), ClientCertCallback);
+ }
+
+ SSL_CTX_set_next_protos_advertised_cb(ssl_ctx.get(),
+ NextProtosAdvertisedCallback, NULL);
+ if (!select_next_proto.empty()) {
+ SSL_CTX_set_next_proto_select_cb(ssl_ctx.get(), NextProtoSelectCallback,
+ NULL);
+ }
+
+ if (!select_alpn.empty() || decline_alpn || select_empty_alpn) {
+ SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
+ }
+
+ SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
+
+ SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
+
+ SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
+ SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
+
+ if (use_ticket_callback) {
+ SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx.get(), TicketKeyCallback);
+ }
+
+ if (!use_custom_verify_callback) {
+ SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), CertVerifyCallback, NULL);
+ }
+
+ if (!signed_cert_timestamps.empty() &&
+ !SSL_CTX_set_signed_cert_timestamp_list(
+ ssl_ctx.get(), (const uint8_t *)signed_cert_timestamps.data(),
+ signed_cert_timestamps.size())) {
+ return nullptr;
+ }
+
+ if (!use_client_ca_list.empty()) {
+ if (use_client_ca_list == "<NULL>") {
+ SSL_CTX_set_client_CA_list(ssl_ctx.get(), nullptr);
+ } else if (use_client_ca_list == "<EMPTY>") {
+ bssl::UniquePtr<STACK_OF(X509_NAME)> names;
+ SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release());
+ } else {
+ bssl::UniquePtr<STACK_OF(X509_NAME)> names =
+ DecodeHexX509Names(use_client_ca_list);
+ SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release());
+ }
+ }
+
+ if (enable_grease) {
+ SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1);
+ }
+
+ if (!expected_server_name.empty()) {
+ SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback);
+ }
+
+ if (enable_early_data) {
+ SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1);
+ }
+
+ SSL_CTX_set_tls13_variant(ssl_ctx.get(),
+ static_cast<enum tls13_variant_t>(tls13_variant));
+
+ if (allow_unknown_alpn_protos) {
+ SSL_CTX_set_allow_unknown_alpn_protos(ssl_ctx.get(), 1);
+ }
+
+ if (enable_ed25519) {
+ SSL_CTX_set_ed25519_enabled(ssl_ctx.get(), 1);
+ }
+ if (no_rsa_pss_rsae_certs) {
+ SSL_CTX_set_rsa_pss_rsae_certs_enabled(ssl_ctx.get(), 0);
+ }
+
+ if (!verify_prefs.empty()) {
+ std::vector<uint16_t> u16s(verify_prefs.begin(), verify_prefs.end());
+ if (!SSL_CTX_set_verify_algorithm_prefs(ssl_ctx.get(), u16s.data(),
+ u16s.size())) {
+ return nullptr;
+ }
+ }
+
+ SSL_CTX_set_msg_callback(ssl_ctx.get(), MessageCallback);
+
+ if (allow_false_start_without_alpn) {
+ SSL_CTX_set_false_start_allowed_without_alpn(ssl_ctx.get(), 1);
+ }
+
+ if (use_ocsp_callback) {
+ SSL_CTX_set_tlsext_status_cb(ssl_ctx.get(), LegacyOCSPCallback);
+ }
+
+ if (old_ctx) {
+ uint8_t keys[48];
+ if (!SSL_CTX_get_tlsext_ticket_keys(old_ctx, &keys, sizeof(keys)) ||
+ !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), keys, sizeof(keys))) {
+ return nullptr;
+ }
+ CopySessions(ssl_ctx.get(), old_ctx);
+ } else if (!ticket_key.empty() &&
+ !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), ticket_key.data(),
+ ticket_key.size())) {
+ return nullptr;
+ }
+
+
+ if (install_cert_compression_algs &&
+ (!SSL_CTX_add_cert_compression_alg(
+ ssl_ctx.get(), 0xff02,
+ [](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
+ if (!CBB_add_u8(out, 1) || !CBB_add_u8(out, 2) ||
+ !CBB_add_u8(out, 3) || !CBB_add_u8(out, 4) ||
+ !CBB_add_bytes(out, in, in_len)) {
+ return 0;
+ }
+ return 1;
+ },
+ [](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
+ const uint8_t *in, size_t in_len) -> int {
+ if (in_len < 4 || in[0] != 1 || in[1] != 2 || in[2] != 3 ||
+ in[3] != 4 || uncompressed_len != in_len - 4) {
+ return 0;
+ }
+ const bssl::Span<const uint8_t> uncompressed(in + 4, in_len - 4);
+ *out = CRYPTO_BUFFER_new(uncompressed.data(), uncompressed.size(),
+ nullptr);
+ return 1;
+ }) ||
+ !SSL_CTX_add_cert_compression_alg(
+ ssl_ctx.get(), 0xff01,
+ [](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
+ if (in_len < 2 || in[0] != 0 || in[1] != 0) {
+ return 0;
+ }
+ return CBB_add_bytes(out, in + 2, in_len - 2);
+ },
+ [](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
+ const uint8_t *in, size_t in_len) -> int {
+ if (uncompressed_len != 2 + in_len) {
+ return 0;
+ }
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[2 + in_len]);
+ buf[0] = 0;
+ buf[1] = 0;
+ OPENSSL_memcpy(&buf[2], in, in_len);
+ *out = CRYPTO_BUFFER_new(buf.get(), 2 + in_len, nullptr);
+ return 1;
+ }))) {
+ fprintf(stderr, "SSL_CTX_add_cert_compression_alg failed.\n");
+ abort();
+ }
+
+ return ssl_ctx;
+}
+
+static int DDoSCallback(const SSL_CLIENT_HELLO *client_hello) {
+ const TestConfig *config = GetTestConfig(client_hello->ssl);
+ return config->fail_ddos_callback ? 0 : 1;
+}
+
+static unsigned PskClientCallback(SSL *ssl, const char *hint,
+ char *out_identity, unsigned max_identity_len,
+ uint8_t *out_psk, unsigned max_psk_len) {
+ const TestConfig *config = GetTestConfig(ssl);
+
+ if (config->psk_identity.empty()) {
+ if (hint != nullptr) {
+ fprintf(stderr, "Server PSK hint was non-null.\n");
+ return 0;
+ }
+ } else if (hint == nullptr ||
+ strcmp(hint, config->psk_identity.c_str()) != 0) {
+ fprintf(stderr, "Server PSK hint did not match.\n");
+ return 0;
+ }
+
+ // Account for the trailing '\0' for the identity.
+ if (config->psk_identity.size() >= max_identity_len ||
+ config->psk.size() > max_psk_len) {
+ fprintf(stderr, "PSK buffers too small\n");
+ return 0;
+ }
+
+ BUF_strlcpy(out_identity, config->psk_identity.c_str(), max_identity_len);
+ OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size());
+ return config->psk.size();
+}
+
+static unsigned PskServerCallback(SSL *ssl, const char *identity,
+ uint8_t *out_psk, unsigned max_psk_len) {
+ const TestConfig *config = GetTestConfig(ssl);
+
+ if (strcmp(identity, config->psk_identity.c_str()) != 0) {
+ fprintf(stderr, "Client PSK identity did not match.\n");
+ return 0;
+ }
+
+ if (config->psk.size() > max_psk_len) {
+ fprintf(stderr, "PSK buffers too small\n");
+ return 0;
+ }
+
+ OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size());
+ return config->psk.size();
+}
+
+static ssl_verify_result_t CustomVerifyCallback(SSL *ssl, uint8_t *out_alert) {
+ const TestConfig *config = GetTestConfig(ssl);
+ if (!CheckVerifyCallback(ssl)) {
+ return ssl_verify_invalid;
+ }
+
+ if (config->async && !GetTestState(ssl)->custom_verify_ready) {
+ return ssl_verify_retry;
+ }
+
+ GetTestState(ssl)->cert_verified = true;
+ if (config->verify_fail) {
+ return ssl_verify_invalid;
+ }
+
+ return ssl_verify_ok;
+}
+
+static int CertCallback(SSL *ssl, void *arg) {
+ const TestConfig *config = GetTestConfig(ssl);
+
+ // Check the peer certificate metadata is as expected.
+ if ((!SSL_is_server(ssl) && !CheckCertificateRequest(ssl)) ||
+ !CheckPeerVerifyPrefs(ssl)) {
+ return -1;
+ }
+
+ if (config->fail_cert_callback) {
+ return 0;
+ }
+
+ // The certificate will be installed via other means.
+ if (!config->async || config->use_early_callback) {
+ return 1;
+ }
+
+ if (!GetTestState(ssl)->cert_ready) {
+ return -1;
+ }
+ if (!InstallCertificate(ssl)) {
+ return 0;
+ }
+ return 1;
+}
+
+bssl::UniquePtr<SSL> TestConfig::NewSSL(
+ SSL_CTX *ssl_ctx, SSL_SESSION *session, bool is_resume,
+ std::unique_ptr<TestState> test_state) const {
+ bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx));
+ if (!ssl) {
+ return nullptr;
+ }
+
+ if (!SetTestConfig(ssl.get(), this)) {
+ return nullptr;
+ }
+ if (test_state != nullptr) {
+ if (!SetTestState(ssl.get(), std::move(test_state))) {
+ return nullptr;
+ }
+ GetTestState(ssl.get())->is_resume = is_resume;
+ }
+
+ if (fallback_scsv && !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) {
+ return nullptr;
+ }
+ // Install the certificate synchronously if nothing else will handle it.
+ if (!use_early_callback && !use_old_client_cert_callback && !async &&
+ !InstallCertificate(ssl.get())) {
+ return nullptr;
+ }
+ if (!use_old_client_cert_callback) {
+ SSL_set_cert_cb(ssl.get(), CertCallback, nullptr);
+ }
+ int mode = SSL_VERIFY_NONE;
+ if (require_any_client_certificate) {
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ }
+ if (verify_peer) {
+ mode = SSL_VERIFY_PEER;
+ }
+ if (verify_peer_if_no_obc) {
+ // Set SSL_VERIFY_FAIL_IF_NO_PEER_CERT so testing whether client
+ // certificates were requested is easy.
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_PEER_IF_NO_OBC |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ }
+ if (use_custom_verify_callback) {
+ SSL_set_custom_verify(ssl.get(), mode, CustomVerifyCallback);
+ } else if (mode != SSL_VERIFY_NONE) {
+ SSL_set_verify(ssl.get(), mode, NULL);
+ }
+ if (false_start) {
+ SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START);
+ }
+ if (cbc_record_splitting) {
+ SSL_set_mode(ssl.get(), SSL_MODE_CBC_RECORD_SPLITTING);
+ }
+ if (partial_write) {
+ SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE);
+ }
+ if (no_tls13) {
+ SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_3);
+ }
+ if (no_tls12) {
+ SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2);
+ }
+ if (no_tls11) {
+ SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_1);
+ }
+ if (no_tls1) {
+ SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1);
+ }
+ if (no_ticket) {
+ SSL_set_options(ssl.get(), SSL_OP_NO_TICKET);
+ }
+ if (!expected_channel_id.empty() || enable_channel_id) {
+ SSL_set_tls_channel_id_enabled(ssl.get(), 1);
+ }
+ if (!send_channel_id.empty()) {
+ SSL_set_tls_channel_id_enabled(ssl.get(), 1);
+ if (!async) {
+ // The async case will be supplied by |ChannelIdCallback|.
+ bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(send_channel_id);
+ if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) {
+ return nullptr;
+ }
+ }
+ }
+ if (!send_token_binding_params.empty()) {
+ SSL_set_token_binding_params(
+ ssl.get(),
+ reinterpret_cast<const uint8_t *>(send_token_binding_params.data()),
+ send_token_binding_params.length());
+ }
+ if (!host_name.empty() &&
+ !SSL_set_tlsext_host_name(ssl.get(), host_name.c_str())) {
+ return nullptr;
+ }
+ if (!advertise_alpn.empty() &&
+ SSL_set_alpn_protos(ssl.get(), (const uint8_t *)advertise_alpn.data(),
+ advertise_alpn.size()) != 0) {
+ return nullptr;
+ }
+ if (!psk.empty()) {
+ SSL_set_psk_client_callback(ssl.get(), PskClientCallback);
+ SSL_set_psk_server_callback(ssl.get(), PskServerCallback);
+ }
+ if (!psk_identity.empty() &&
+ !SSL_use_psk_identity_hint(ssl.get(), psk_identity.c_str())) {
+ return nullptr;
+ }
+ if (!srtp_profiles.empty() &&
+ !SSL_set_srtp_profiles(ssl.get(), srtp_profiles.c_str())) {
+ return nullptr;
+ }
+ if (enable_ocsp_stapling) {
+ SSL_enable_ocsp_stapling(ssl.get());
+ }
+ if (enable_signed_cert_timestamps) {
+ SSL_enable_signed_cert_timestamps(ssl.get());
+ }
+ if (min_version != 0 &&
+ !SSL_set_min_proto_version(ssl.get(), (uint16_t)min_version)) {
+ return nullptr;
+ }
+ if (max_version != 0 &&
+ !SSL_set_max_proto_version(ssl.get(), (uint16_t)max_version)) {
+ return nullptr;
+ }
+ if (mtu != 0) {
+ SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU);
+ SSL_set_mtu(ssl.get(), mtu);
+ }
+ if (install_ddos_callback) {
+ SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback);
+ }
+ SSL_set_shed_handshake_config(ssl.get(), true);
+ if (renegotiate_once) {
+ SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_once);
+ }
+ if (renegotiate_freely || forbid_renegotiation_after_handshake) {
+ // |forbid_renegotiation_after_handshake| will disable renegotiation later.
+ SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely);
+ }
+ if (renegotiate_ignore) {
+ SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_ignore);
+ }
+ if (!check_close_notify) {
+ SSL_set_quiet_shutdown(ssl.get(), 1);
+ }
+ if (p384_only) {
+ int nid = NID_secp384r1;
+ if (!SSL_set1_curves(ssl.get(), &nid, 1)) {
+ return nullptr;
+ }
+ }
+ if (enable_all_curves) {
+ static const int kAllCurves[] = {
+ NID_secp224r1, NID_X9_62_prime256v1, NID_secp384r1,
+ NID_secp521r1, NID_X25519,
+ };
+ if (!SSL_set1_curves(ssl.get(), kAllCurves,
+ OPENSSL_ARRAY_SIZE(kAllCurves))) {
+ return nullptr;
+ }
+ }
+ if (initial_timeout_duration_ms > 0) {
+ DTLSv1_set_initial_timeout_duration(ssl.get(), initial_timeout_duration_ms);
+ }
+ if (max_cert_list > 0) {
+ SSL_set_max_cert_list(ssl.get(), max_cert_list);
+ }
+ if (retain_only_sha256_client_cert) {
+ SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
+ }
+ if (max_send_fragment > 0) {
+ SSL_set_max_send_fragment(ssl.get(), max_send_fragment);
+ }
+ if (dummy_pq_padding_len > 0 &&
+ !SSL_set_dummy_pq_padding_size(ssl.get(), dummy_pq_padding_len)) {
+ return nullptr;
+ }
+ if (!quic_transport_params.empty()) {
+ if (!SSL_set_quic_transport_params(
+ ssl.get(),
+ reinterpret_cast<const uint8_t *>(quic_transport_params.data()),
+ quic_transport_params.size())) {
+ return nullptr;
+ }
+ }
+
+ if (session != NULL) {
+ if (!is_server) {
+ if (SSL_set_session(ssl.get(), session) != 1) {
+ return nullptr;
+ }
+ } else if (async) {
+ // The internal session cache is disabled, so install the session
+ // manually.
+ SSL_SESSION_up_ref(session);
+ GetTestState(ssl.get())->pending_session.reset(session);
+ }
+ }
+
+ if (SSL_get_current_cipher(ssl.get()) != nullptr) {
+ fprintf(stderr, "non-null cipher before handshake\n");
+ return nullptr;
+ }
+
+ return ssl;
+}
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 95f38e0..6c9ac3e 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -18,6 +18,10 @@
#include <string>
#include <vector>
+#include <openssl/base.h>
+#include <openssl/x509.h>
+
+#include "test_state.h"
struct TestConfig {
int port = 0;
@@ -28,6 +32,7 @@
bool fallback_scsv = false;
std::vector<int> signing_prefs;
std::vector<int> verify_prefs;
+ std::vector<int> expected_peer_verify_prefs;
std::string key_file;
std::string cert_file;
std::string expected_server_name;
@@ -45,7 +50,7 @@
bool no_tls12 = false;
bool no_tls11 = false;
bool no_tls1 = false;
- bool no_ssl3 = false;
+ bool no_ticket = false;
std::string expected_channel_id;
bool enable_channel_id = false;
std::string send_channel_id;
@@ -59,6 +64,7 @@
std::string expected_advertised_alpn;
std::string select_alpn;
bool decline_alpn = false;
+ bool select_empty_alpn = false;
std::string quic_transport_params;
std::string expected_quic_transport_params;
bool expect_session_miss = false;
@@ -79,7 +85,6 @@
bool fail_early_callback = false;
bool install_ddos_callback = false;
bool fail_ddos_callback = false;
- bool fail_second_ddos_callback = false;
bool fail_cert_callback = false;
std::string cipher;
bool handshake_never_done = false;
@@ -115,6 +120,7 @@
bool renegotiate_once = false;
bool renegotiate_freely = false;
bool renegotiate_ignore = false;
+ bool forbid_renegotiation_after_handshake = false;
int expect_peer_signature_algorithm = 0;
bool p384_only = false;
bool enable_all_curves = false;
@@ -155,10 +161,36 @@
bool handoff = false;
bool expect_dummy_pq_padding = false;
bool no_rsa_pss_rsae_certs = false;
+ bool use_ocsp_callback = false;
+ bool set_ocsp_in_callback = false;
+ bool decline_ocsp_callback = false;
+ bool fail_ocsp_callback = false;
+ bool install_cert_compression_algs = false;
+ bool is_handshaker_supported = false;
+ bool handshaker_resume = false;
+ std::string handshaker_path;
+
+ int argc;
+ char **argv;
+
+ bssl::UniquePtr<SSL_CTX> SetupCtx(SSL_CTX *old_ctx) const;
+
+ bssl::UniquePtr<SSL> NewSSL(SSL_CTX *ssl_ctx, SSL_SESSION *session,
+ bool is_resume,
+ std::unique_ptr<TestState> test_state) const;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_initial,
TestConfig *out_resume, TestConfig *out_retry);
+bool SetTestConfig(SSL *ssl, const TestConfig *config);
+
+const TestConfig *GetTestConfig(const SSL *ssl);
+
+bool LoadCertificate(bssl::UniquePtr<X509> *out_x509,
+ bssl::UniquePtr<STACK_OF(X509)> *out_chain,
+ const std::string &file);
+
+bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file);
#endif // HEADER_TEST_CONFIG
diff --git a/src/ssl/test/test_state.cc b/src/ssl/test/test_state.cc
new file mode 100644
index 0000000..b9767e0
--- /dev/null
+++ b/src/ssl/test/test_state.cc
@@ -0,0 +1,169 @@
+/* Copyright (c) 2018, 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 "test_state.h"
+
+#include <openssl/ssl.h>
+
+#include "../../crypto/internal.h"
+#include "../internal.h"
+
+using namespace bssl;
+
+static CRYPTO_once_t g_once = CRYPTO_ONCE_INIT;
+static int g_state_index = 0;
+// Some code treats the zero time special, so initialize the clock to a
+// non-zero time.
+static timeval g_clock = { 1234, 1234 };
+
+static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+ int index, long argl, void *argp) {
+ delete ((TestState *)ptr);
+}
+
+static void init_once() {
+ g_state_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, TestStateExFree);
+ if (g_state_index < 0) {
+ abort();
+ }
+}
+
+struct timeval *GetClock() {
+ CRYPTO_once(&g_once, init_once);
+ return &g_clock;
+}
+
+void AdvanceClock(unsigned seconds) {
+ CRYPTO_once(&g_once, init_once);
+ g_clock.tv_sec += seconds;
+}
+
+bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state) {
+ CRYPTO_once(&g_once, init_once);
+ // |SSL_set_ex_data| takes ownership of |state| only on success.
+ if (SSL_set_ex_data(ssl, g_state_index, state.get()) == 1) {
+ state.release();
+ return true;
+ }
+ return false;
+}
+
+TestState *GetTestState(const SSL *ssl) {
+ CRYPTO_once(&g_once, init_once);
+ return (TestState *)SSL_get_ex_data(ssl, g_state_index);
+}
+
+static void ssl_ctx_add_session(SSL_SESSION *session, void *void_param) {
+ SSL_CTX *ctx = reinterpret_cast<SSL_CTX *>(void_param);
+ UniquePtr<SSL_SESSION> new_session = SSL_SESSION_dup(
+ session, SSL_SESSION_INCLUDE_NONAUTH | SSL_SESSION_INCLUDE_TICKET);
+ if (new_session != nullptr) {
+ SSL_CTX_add_session(ctx, new_session.get());
+ }
+}
+
+void CopySessions(SSL_CTX *dst, const SSL_CTX *src) {
+ lh_SSL_SESSION_doall_arg(src->sessions, ssl_ctx_add_session, dst);
+}
+
+static void push_session(SSL_SESSION *session, void *arg) {
+ auto s = reinterpret_cast<std::vector<SSL_SESSION *> *>(arg);
+ s->push_back(session);
+}
+
+bool SerializeContextState(SSL_CTX *ctx, CBB *cbb) {
+ CBB out, ctx_sessions, ticket_keys;
+ uint8_t keys[48];
+ if (!CBB_add_u24_length_prefixed(cbb, &out) ||
+ !CBB_add_u16(&out, 0 /* version */) ||
+ !SSL_CTX_get_tlsext_ticket_keys(ctx, &keys, sizeof(keys)) ||
+ !CBB_add_u8_length_prefixed(&out, &ticket_keys) ||
+ !CBB_add_bytes(&ticket_keys, keys, sizeof(keys)) ||
+ !CBB_add_asn1(&out, &ctx_sessions, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ std::vector<SSL_SESSION *> sessions;
+ lh_SSL_SESSION_doall_arg(ctx->sessions, push_session, &sessions);
+ for (const auto &sess : sessions) {
+ if (!ssl_session_serialize(sess, &ctx_sessions)) {
+ return false;
+ }
+ }
+ return CBB_flush(cbb);
+}
+
+bool DeserializeContextState(CBS *cbs, SSL_CTX *ctx) {
+ CBS in, sessions, ticket_keys;
+ uint16_t version;
+ constexpr uint16_t kVersion = 0;
+ if (!CBS_get_u24_length_prefixed(cbs, &in) ||
+ !CBS_get_u16(&in, &version) ||
+ version > kVersion ||
+ !CBS_get_u8_length_prefixed(&in, &ticket_keys) ||
+ !SSL_CTX_set_tlsext_ticket_keys(ctx, CBS_data(&ticket_keys),
+ CBS_len(&ticket_keys)) ||
+ !CBS_get_asn1(&in, &sessions, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ while (CBS_len(&sessions)) {
+ UniquePtr<SSL_SESSION> session =
+ SSL_SESSION_parse(&sessions, ctx->x509_method, ctx->pool);
+ if (!session) {
+ return false;
+ }
+ SSL_CTX_add_session(ctx, session.get());
+ }
+ return true;
+}
+
+bool TestState::Serialize(CBB *cbb) const {
+ CBB out, pending, text;
+ if (!CBB_add_u24_length_prefixed(cbb, &out) ||
+ !CBB_add_u16(&out, 0 /* version */) ||
+ !CBB_add_u24_length_prefixed(&out, &pending) ||
+ (pending_session &&
+ !ssl_session_serialize(pending_session.get(), &pending)) ||
+ !CBB_add_u16_length_prefixed(&out, &text) ||
+ !CBB_add_bytes(
+ &text, reinterpret_cast<const uint8_t *>(msg_callback_text.data()),
+ msg_callback_text.length()) ||
+ !CBB_flush(cbb)) {
+ return false;
+ }
+ return true;
+}
+
+std::unique_ptr<TestState> TestState::Deserialize(CBS *cbs, SSL_CTX *ctx) {
+ CBS in, pending_session, text;
+ std::unique_ptr<TestState> out_state(new TestState());
+ uint16_t version;
+ constexpr uint16_t kVersion = 0;
+ if (!CBS_get_u24_length_prefixed(cbs, &in) ||
+ !CBS_get_u16(&in, &version) ||
+ version > kVersion ||
+ !CBS_get_u24_length_prefixed(&in, &pending_session) ||
+ !CBS_get_u16_length_prefixed(&in, &text)) {
+ return nullptr;
+ }
+ if (CBS_len(&pending_session)) {
+ out_state->pending_session = SSL_SESSION_parse(
+ &pending_session, ctx->x509_method, ctx->pool);
+ if (!out_state->pending_session) {
+ return nullptr;
+ }
+ }
+ out_state->msg_callback_text = std::string(
+ reinterpret_cast<const char *>(CBS_data(&text)), CBS_len(&text));
+ return out_state;
+}
diff --git a/src/ssl/test/test_state.h b/src/ssl/test/test_state.h
new file mode 100644
index 0000000..2364286
--- /dev/null
+++ b/src/ssl/test/test_state.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2018, 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. */
+
+#ifndef HEADER_TEST_STATE
+#define HEADER_TEST_STATE
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <openssl/base.h>
+
+struct TestState {
+ // Serialize writes |pending_session| and |msg_callback_text| to |out|, for
+ // use in split-handshake tests. We don't try to serialize every bit of test
+ // state, but serializing |pending_session| is necessary to exercise session
+ // resumption, and |msg_callback_text| is especially useful. In the general
+ // case, checks of state updated during the handshake can be skipped when
+ // |config->handoff|.
+ bool Serialize(CBB *out) const;
+
+ // Deserialize returns a new |TestState| from data written by |Serialize|.
+ static std::unique_ptr<TestState> Deserialize(CBS *cbs, SSL_CTX *ctx);
+
+ // async_bio is async BIO which pauses reads and writes.
+ BIO *async_bio = nullptr;
+ // packeted_bio is the packeted BIO which simulates read timeouts.
+ BIO *packeted_bio = nullptr;
+ bssl::UniquePtr<EVP_PKEY> channel_id;
+ bool cert_ready = false;
+ bssl::UniquePtr<SSL_SESSION> session;
+ bssl::UniquePtr<SSL_SESSION> pending_session;
+ bool early_callback_called = false;
+ bool handshake_done = false;
+ // private_key is the underlying private key used when testing custom keys.
+ bssl::UniquePtr<EVP_PKEY> private_key;
+ std::vector<uint8_t> private_key_result;
+ // private_key_retries is the number of times an asynchronous private key
+ // operation has been retried.
+ unsigned private_key_retries = 0;
+ bool got_new_session = false;
+ bssl::UniquePtr<SSL_SESSION> new_session;
+ bool ticket_decrypt_done = false;
+ bool alpn_select_done = false;
+ bool is_resume = false;
+ bool early_callback_ready = false;
+ bool custom_verify_ready = false;
+ std::string msg_callback_text;
+ bool msg_callback_ok = true;
+ // cert_verified is true if certificate verification has been driven to
+ // completion. This tests that the callback is not called again after this.
+ bool cert_verified = false;
+};
+
+bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state);
+
+TestState *GetTestState(const SSL *ssl);
+
+struct timeval *GetClock();
+
+void AdvanceClock(unsigned seconds);
+
+void CopySessions(SSL_CTX *dest, const SSL_CTX *src);
+
+// SerializeContextState writes session material (sessions and ticket keys) from
+// |ctx| into |cbb|.
+bool SerializeContextState(SSL_CTX *ctx, CBB *cbb);
+
+// DeserializeContextState updates |out| with material previously serialized by
+// SerializeContextState.
+bool DeserializeContextState(CBS *in, SSL_CTX *out);
+
+#endif // HEADER_TEST_STATE
diff --git a/src/ssl/tls13_both.cc b/src/ssl/tls13_both.cc
index defdac1..d6ebb4c 100644
--- a/src/ssl/tls13_both.cc
+++ b/src/ssl/tls13_both.cc
@@ -105,7 +105,73 @@
int tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
int allow_anonymous) {
SSL *const ssl = hs->ssl;
- CBS body = msg.body, context, certificate_list;
+ CBS body = msg.body;
+ bssl::UniquePtr<CRYPTO_BUFFER> decompressed;
+
+ if (msg.type == SSL3_MT_COMPRESSED_CERTIFICATE) {
+ CBS compressed;
+ uint16_t alg_id;
+ uint32_t uncompressed_len;
+
+ if (!CBS_get_u16(&body, &alg_id) ||
+ !CBS_get_u24(&body, &uncompressed_len) ||
+ !CBS_get_u24_length_prefixed(&body, &compressed) ||
+ CBS_len(&body) != 0) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ return 0;
+ }
+
+ if (uncompressed_len > ssl->max_cert_list) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNCOMPRESSED_CERT_TOO_LARGE);
+ ERR_add_error_dataf("requested=%u",
+ static_cast<unsigned>(uncompressed_len));
+ return 0;
+ }
+
+ ssl_cert_decompression_func_t decompress = nullptr;
+ for (const auto* alg : ssl->ctx->cert_compression_algs.get()) {
+ if (alg->alg_id == alg_id) {
+ decompress = alg->decompress;
+ break;
+ }
+ }
+
+ if (decompress == nullptr) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERT_COMPRESSION_ALG);
+ ERR_add_error_dataf("alg=%d", static_cast<int>(alg_id));
+ return 0;
+ }
+
+ CRYPTO_BUFFER *decompressed_ptr = nullptr;
+ if (!decompress(ssl, &decompressed_ptr, uncompressed_len,
+ CBS_data(&compressed), CBS_len(&compressed))) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_DECOMPRESSION_FAILED);
+ ERR_add_error_dataf("alg=%d", static_cast<int>(alg_id));
+ return 0;
+ }
+ decompressed.reset(decompressed_ptr);
+
+ if (CRYPTO_BUFFER_len(decompressed_ptr) != uncompressed_len) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_DECOMPRESSION_FAILED);
+ ERR_add_error_dataf(
+ "alg=%d got=%u expected=%u", static_cast<int>(alg_id),
+ static_cast<unsigned>(CRYPTO_BUFFER_len(decompressed_ptr)),
+ static_cast<unsigned>(uncompressed_len));
+ return 0;
+ }
+
+ CBS_init(&body, CRYPTO_BUFFER_data(decompressed_ptr),
+ CRYPTO_BUFFER_len(decompressed_ptr));
+ } else {
+ assert(msg.type == SSL3_MT_CERTIFICATE);
+ }
+
+ CBS context, certificate_list;
if (!CBS_get_u8_length_prefixed(&body, &context) ||
CBS_len(&context) != 0 ||
!CBS_get_u24_length_prefixed(&body, &certificate_list) ||
@@ -123,7 +189,7 @@
}
const bool retain_sha256 =
- ssl->server && ssl->retain_only_sha256_of_client_certs;
+ ssl->server && hs->config->retain_only_sha256_of_client_certs;
UniquePtr<EVP_PKEY> pkey;
while (CBS_len(&certificate_list) > 0) {
CBS certificate, extensions;
@@ -184,7 +250,7 @@
// All Certificate extensions are parsed, but only the leaf extensions are
// stored.
if (have_status_request) {
- if (ssl->server || !ssl->ocsp_stapling_enabled) {
+ if (ssl->server || !hs->config->ocsp_stapling_enabled) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
return 0;
@@ -202,9 +268,8 @@
}
if (sk_CRYPTO_BUFFER_num(certs.get()) == 1) {
- CRYPTO_BUFFER_free(hs->new_session->ocsp_response);
- hs->new_session->ocsp_response =
- CRYPTO_BUFFER_new_from_CBS(&ocsp_response, ssl->ctx->pool);
+ hs->new_session->ocsp_response.reset(
+ CRYPTO_BUFFER_new_from_CBS(&ocsp_response, ssl->ctx->pool));
if (hs->new_session->ocsp_response == nullptr) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return 0;
@@ -213,7 +278,7 @@
}
if (have_sct) {
- if (ssl->server || !ssl->signed_cert_timestamps_enabled) {
+ if (ssl->server || !hs->config->signed_cert_timestamps_enabled) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
return 0;
@@ -226,9 +291,8 @@
}
if (sk_CRYPTO_BUFFER_num(certs.get()) == 1) {
- CRYPTO_BUFFER_free(hs->new_session->signed_cert_timestamp_list);
- hs->new_session->signed_cert_timestamp_list =
- CRYPTO_BUFFER_new_from_CBS(&sct, ssl->ctx->pool);
+ hs->new_session->signed_cert_timestamp_list.reset(
+ CRYPTO_BUFFER_new_from_CBS(&sct, ssl->ctx->pool));
if (hs->new_session->signed_cert_timestamp_list == nullptr) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return 0;
@@ -244,9 +308,7 @@
}
hs->peer_pubkey = std::move(pkey);
-
- sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
- hs->new_session->certs = certs.release();
+ hs->new_session->certs = std::move(certs);
if (!ssl->ctx->x509_method->session_cache_objects(hs->new_session.get())) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
@@ -254,7 +316,7 @@
return 0;
}
- if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0) {
+ if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0) {
if (!allow_anonymous) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED);
@@ -353,21 +415,34 @@
int tls13_add_certificate(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
+ CERT *const cert = hs->config->cert.get();
+
ScopedCBB cbb;
- CBB body, certificate_list;
- if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CERTIFICATE) ||
- // The request context is always empty in the handshake.
- !CBB_add_u8(&body, 0) ||
- !CBB_add_u24_length_prefixed(&body, &certificate_list)) {
+ CBB *body, body_storage, certificate_list;
+
+ if (hs->cert_compression_negotiated) {
+ if (!CBB_init(cbb.get(), 1024)) {
+ return false;
+ }
+ body = cbb.get();
+ } else {
+ body = &body_storage;
+ if (!ssl->method->init_message(ssl, cbb.get(), body, SSL3_MT_CERTIFICATE)) {
+ return false;
+ }
+ }
+
+ if (// The request context is always empty in the handshake.
+ !CBB_add_u8(body, 0) ||
+ !CBB_add_u24_length_prefixed(body, &certificate_list)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
- if (!ssl_has_certificate(ssl)) {
+ if (!ssl_has_certificate(hs->config)) {
return ssl_add_message_cbb(ssl, cbb.get());
}
- CERT *cert = ssl->cert;
CRYPTO_BUFFER *leaf_buf = sk_CRYPTO_BUFFER_value(cert->chain.get(), 0);
CBB leaf, extensions;
if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) ||
@@ -378,30 +453,29 @@
return 0;
}
- if (hs->scts_requested && ssl->cert->signed_cert_timestamp_list != nullptr) {
+ if (hs->scts_requested && cert->signed_cert_timestamp_list != nullptr) {
CBB contents;
if (!CBB_add_u16(&extensions, TLSEXT_TYPE_certificate_timestamp) ||
!CBB_add_u16_length_prefixed(&extensions, &contents) ||
!CBB_add_bytes(
&contents,
- CRYPTO_BUFFER_data(ssl->cert->signed_cert_timestamp_list.get()),
- CRYPTO_BUFFER_len(ssl->cert->signed_cert_timestamp_list.get())) ||
+ CRYPTO_BUFFER_data(cert->signed_cert_timestamp_list.get()),
+ CRYPTO_BUFFER_len(cert->signed_cert_timestamp_list.get())) ||
!CBB_flush(&extensions)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
}
- if (hs->ocsp_stapling_requested &&
- ssl->cert->ocsp_response != NULL) {
+ if (hs->ocsp_stapling_requested && cert->ocsp_response != NULL) {
CBB contents, ocsp_response;
if (!CBB_add_u16(&extensions, TLSEXT_TYPE_status_request) ||
!CBB_add_u16_length_prefixed(&extensions, &contents) ||
!CBB_add_u8(&contents, TLSEXT_STATUSTYPE_ocsp) ||
!CBB_add_u24_length_prefixed(&contents, &ocsp_response) ||
!CBB_add_bytes(&ocsp_response,
- CRYPTO_BUFFER_data(ssl->cert->ocsp_response.get()),
- CRYPTO_BUFFER_len(ssl->cert->ocsp_response.get())) ||
+ CRYPTO_BUFFER_data(cert->ocsp_response.get()),
+ CRYPTO_BUFFER_len(cert->ocsp_response.get())) ||
!CBB_flush(&extensions)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return 0;
@@ -420,13 +494,50 @@
}
}
- return ssl_add_message_cbb(ssl, cbb.get());
+ if (!hs->cert_compression_negotiated) {
+ return ssl_add_message_cbb(ssl, cbb.get());
+ }
+
+ Array<uint8_t> msg;
+ if (!CBBFinishArray(cbb.get(), &msg)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ const CertCompressionAlg *alg = nullptr;
+ for (const auto *candidate : ssl->ctx->cert_compression_algs.get()) {
+ if (candidate->alg_id == hs->cert_compression_alg_id) {
+ alg = candidate;
+ break;
+ }
+ }
+
+ if (alg == nullptr || alg->compress == nullptr) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ CBB compressed;
+ body = &body_storage;
+ if (!ssl->method->init_message(ssl, cbb.get(), body,
+ SSL3_MT_COMPRESSED_CERTIFICATE) ||
+ !CBB_add_u16(body, hs->cert_compression_alg_id) ||
+ !CBB_add_u24(body, msg.size()) ||
+ !CBB_add_u24_length_prefixed(body, &compressed) ||
+ !alg->compress(ssl, &compressed, msg.data(), msg.size()) ||
+ !ssl_add_message_cbb(ssl, cbb.get())) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ return 1;
}
enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
uint16_t signature_algorithm;
if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
return ssl_private_key_failure;
}
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index 6e328b8..40281a0 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -157,7 +157,7 @@
}
// The group must be supported.
- if (!tls1_check_group_id(ssl, group_id)) {
+ if (!tls1_check_group_id(hs, group_id)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
return ssl_hs_error;
@@ -290,6 +290,18 @@
return ssl_hs_error;
}
+ if (ssl_is_draft28(ssl->version)) {
+ // Recheck supported_versions, in case this is the second ServerHello.
+ uint16_t version;
+ if (!have_supported_versions ||
+ !CBS_get_u16(&supported_versions, &version) ||
+ version != ssl->version) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_SECOND_SERVERHELLO_VERSION_MISMATCH);
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ return ssl_hs_error;
+ }
+ }
+
alert = SSL_AD_DECODE_ERROR;
if (have_pre_shared_key) {
if (ssl->session == NULL) {
@@ -316,7 +328,7 @@
return ssl_hs_error;
}
- if (!ssl_session_is_context_valid(ssl, ssl->session)) {
+ if (!ssl_session_is_context_valid(hs, ssl->session.get())) {
// This is actually a client application bug.
OPENSSL_PUT_ERROR(SSL,
SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
@@ -326,7 +338,8 @@
ssl->s3->session_reused = true;
// Only authentication information carries over in TLS 1.3.
- hs->new_session = SSL_SESSION_dup(ssl->session, SSL_SESSION_DUP_AUTH_ONLY);
+ hs->new_session =
+ SSL_SESSION_dup(ssl->session.get(), SSL_SESSION_DUP_AUTH_ONLY);
if (!hs->new_session) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
@@ -417,26 +430,19 @@
}
// Store the negotiated ALPN in the session.
- if (!ssl->s3->alpn_selected.empty()) {
- hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
- ssl->s3->alpn_selected.data(), ssl->s3->alpn_selected.size());
- if (hs->new_session->early_alpn == NULL) {
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
- return ssl_hs_error;
- }
- hs->new_session->early_alpn_len = ssl->s3->alpn_selected.size();
+ if (!hs->new_session->early_alpn.CopyFrom(ssl->s3->alpn_selected)) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
}
if (ssl->s3->early_data_accepted) {
if (hs->early_session->cipher != hs->new_session->cipher ||
- MakeConstSpan(hs->early_session->early_alpn,
- hs->early_session->early_alpn_len) !=
+ MakeConstSpan(hs->early_session->early_alpn) !=
ssl->s3->alpn_selected) {
OPENSSL_PUT_ERROR(SSL, SSL_R_ALPN_MISMATCH_ON_EARLY_DATA);
return ssl_hs_error;
}
- if (ssl->s3->tlsext_channel_id_valid || hs->received_custom_extension ||
- ssl->s3->token_binding_negotiated) {
+ if (ssl->s3->channel_id_valid || ssl->s3->token_binding_negotiated) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION_ON_EARLY_DATA);
return ssl_hs_error;
}
@@ -535,8 +541,13 @@
if (!ssl->method->get_message(ssl, &msg)) {
return ssl_hs_read_message;
}
- if (!ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE) ||
- !tls13_process_certificate(hs, msg, 0 /* certificate required */) ||
+
+ if (msg.type != SSL3_MT_COMPRESSED_CERTIFICATE &&
+ !ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE)) {
+ return ssl_hs_error;
+ }
+
+ if (!tls13_process_certificate(hs, msg, 0 /* certificate required */) ||
!ssl_hash_message(hs, msg)) {
return ssl_hs_error;
}
@@ -629,8 +640,8 @@
}
// Call cert_cb to update the certificate.
- if (ssl->cert->cert_cb != NULL) {
- int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+ if (hs->config->cert->cert_cb != NULL) {
+ int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg);
if (rv == 0) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
@@ -652,9 +663,8 @@
}
static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs) {
- SSL *const ssl = hs->ssl;
// Don't send CertificateVerify if there is no certificate.
- if (!ssl_has_certificate(ssl)) {
+ if (!ssl_has_certificate(hs->config)) {
hs->tls13_state = state_complete_second_flight;
return ssl_hs_ok;
}
@@ -680,13 +690,13 @@
SSL *const ssl = hs->ssl;
// Send a Channel ID assertion if necessary.
- if (ssl->s3->tlsext_channel_id_valid) {
- if (!ssl_do_channel_id_callback(ssl)) {
+ if (ssl->s3->channel_id_valid) {
+ if (!ssl_do_channel_id_callback(hs)) {
hs->tls13_state = state_complete_second_flight;
return ssl_hs_error;
}
- if (ssl->tlsext_channel_id_private == NULL) {
+ if (hs->config->channel_id_private == NULL) {
return ssl_hs_channel_id_lookup;
}
@@ -833,7 +843,7 @@
!CBS_get_u32(&body, &session->ticket_age_add) ||
!CBS_get_u8_length_prefixed(&body, &ticket_nonce) ||
!CBS_get_u16_length_prefixed(&body, &ticket) ||
- !CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
+ !session->ticket.CopyFrom(ticket) ||
!CBS_get_u16_length_prefixed(&body, &extensions) ||
CBS_len(&body) != 0) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -866,7 +876,7 @@
return 0;
}
- if (have_early_data_info && ssl->cert->enable_early_data) {
+ if (have_early_data_info && ssl->enable_early_data) {
if (!CBS_get_u32(&early_data_info, &session->ticket_max_early_data) ||
CBS_len(&early_data_info) != 0) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -875,12 +885,12 @@
}
}
- session->ticket_age_add_valid = 1;
- session->not_resumable = 0;
+ session->ticket_age_add_valid = true;
+ session->not_resumable = false;
- if ((ssl->ctx->session_cache_mode & SSL_SESS_CACHE_CLIENT) &&
- ssl->ctx->new_session_cb != NULL &&
- ssl->ctx->new_session_cb(ssl, session.get())) {
+ if ((ssl->session_ctx->session_cache_mode & SSL_SESS_CACHE_CLIENT) &&
+ ssl->session_ctx->new_session_cb != NULL &&
+ ssl->session_ctx->new_session_cb(ssl, session.get())) {
// |new_session_cb|'s return value signals that it took ownership.
session.release();
}
diff --git a/src/ssl/tls13_enc.cc b/src/ssl/tls13_enc.cc
index cc7afb8..84bc5d2 100644
--- a/src/ssl/tls13_enc.cc
+++ b/src/ssl/tls13_enc.cc
@@ -60,7 +60,7 @@
int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
size_t psk_len) {
SSL *const ssl = hs->ssl;
- return init_key_schedule(hs, ssl_session_protocol_version(ssl->session),
+ return init_key_schedule(hs, ssl_session_protocol_version(ssl->session.get()),
ssl->session->cipher) &&
HKDF_extract(hs->secret, &hs->hash_len, hs->transcript.Digest(), psk,
psk_len, hs->secret, hs->hash_len);
@@ -74,8 +74,7 @@
ScopedCBB cbb;
CBB child;
- uint8_t *hkdf_label;
- size_t hkdf_label_len;
+ Array<uint8_t> hkdf_label;
if (!CBB_init(cbb.get(), 2 + 1 + strlen(kTLS13LabelVersion) + label_len + 1 +
hash_len) ||
!CBB_add_u16(cbb.get(), len) ||
@@ -85,14 +84,12 @@
!CBB_add_bytes(&child, (const uint8_t *)label, label_len) ||
!CBB_add_u8_length_prefixed(cbb.get(), &child) ||
!CBB_add_bytes(&child, hash, hash_len) ||
- !CBB_finish(cbb.get(), &hkdf_label, &hkdf_label_len)) {
+ !CBBFinishArray(cbb.get(), &hkdf_label)) {
return 0;
}
- int ret = HKDF_expand(out, len, digest, secret, secret_len, hkdf_label,
- hkdf_label_len);
- OPENSSL_free(hkdf_label);
- return ret;
+ return HKDF_expand(out, len, digest, secret, secret_len, hkdf_label.data(),
+ hkdf_label.size());
}
static const char kTLS13LabelDerived[] = "derived";
@@ -413,7 +410,7 @@
int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len) {
SSL *const ssl = hs->ssl;
- const EVP_MD *digest = ssl_session_get_digest(ssl->session);
+ const EVP_MD *digest = ssl_session_get_digest(ssl->session.get());
size_t hash_len = EVP_MD_size(digest);
if (len < hash_len + 3) {
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index fe0449d..203e704 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -148,8 +148,18 @@
return best;
}
-static int add_new_session_tickets(SSL_HANDSHAKE *hs) {
+static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {
SSL *const ssl = hs->ssl;
+ if (// If the client doesn't accept resumption with PSK_DHE_KE, don't send a
+ // session ticket.
+ !hs->accept_psk_mode ||
+ // We only implement stateless resumption in TLS 1.3, so skip sending
+ // tickets if disabled.
+ (SSL_get_options(ssl) & SSL_OP_NO_TICKET)) {
+ *out_sent_tickets = false;
+ return true;
+ }
+
// TLS 1.3 recommends single-use tickets, so issue multiple tickets in case
// the client makes several connections before getting a renewal.
static const int kNumTickets = 2;
@@ -162,14 +172,14 @@
UniquePtr<SSL_SESSION> session(
SSL_SESSION_dup(hs->new_session.get(), SSL_SESSION_INCLUDE_NONAUTH));
if (!session) {
- return 0;
+ return false;
}
if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) {
- return 0;
+ return false;
}
- session->ticket_age_add_valid = 1;
- if (ssl->cert->enable_early_data) {
+ session->ticket_age_add_valid = true;
+ if (ssl->enable_early_data) {
session->ticket_max_early_data = kMaxEarlyDataAccepted;
}
@@ -186,18 +196,18 @@
!CBB_add_bytes(&nonce_cbb, nonce, sizeof(nonce)) ||
!CBB_add_u16_length_prefixed(&body, &ticket) ||
!tls13_derive_session_psk(session.get(), nonce) ||
- !ssl_encrypt_ticket(ssl, &ticket, session.get()) ||
+ !ssl_encrypt_ticket(hs, &ticket, session.get()) ||
!CBB_add_u16_length_prefixed(&body, &extensions)) {
- return 0;
+ return false;
}
- if (ssl->cert->enable_early_data) {
+ if (ssl->enable_early_data) {
CBB early_data_info;
if (!CBB_add_u16(&extensions, TLSEXT_TYPE_early_data) ||
!CBB_add_u16_length_prefixed(&extensions, &early_data_info) ||
!CBB_add_u32(&early_data_info, session->ticket_max_early_data) ||
!CBB_flush(&extensions)) {
- return 0;
+ return false;
}
}
@@ -205,15 +215,16 @@
if (!CBB_add_u16(&extensions,
ssl_get_grease_value(hs, ssl_grease_ticket_extension)) ||
!CBB_add_u16(&extensions, 0 /* empty */)) {
- return 0;
+ return false;
}
if (!ssl_add_message_cbb(ssl, cbb.get())) {
- return 0;
+ return false;
}
}
- return 1;
+ *out_sent_tickets = true;
+ return true;
}
static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) {
@@ -302,7 +313,7 @@
bool unused_renew;
UniquePtr<SSL_SESSION> session;
enum ssl_ticket_aead_result_t ret =
- ssl_process_ticket(ssl, &session, &unused_renew, CBS_data(&ticket),
+ ssl_process_ticket(hs, &session, &unused_renew, CBS_data(&ticket),
CBS_len(&ticket), NULL, 0);
switch (ret) {
case ssl_ticket_aead_success:
@@ -383,20 +394,17 @@
hs->new_session =
SSL_SESSION_dup(session.get(), SSL_SESSION_DUP_AUTH_ONLY);
- if (ssl->cert->enable_early_data &&
+ if (ssl->enable_early_data &&
// Early data must be acceptable for this ticket.
session->ticket_max_early_data != 0 &&
// The client must have offered early data.
hs->early_data_offered &&
// Channel ID is incompatible with 0-RTT.
- !ssl->s3->tlsext_channel_id_valid &&
+ !ssl->s3->channel_id_valid &&
// If Token Binding is negotiated, reject 0-RTT.
!ssl->s3->token_binding_negotiated &&
- // Custom extensions is incompatible with 0-RTT.
- hs->custom_extensions.received == 0 &&
// The negotiated ALPN must match the one in the ticket.
- ssl->s3->alpn_selected ==
- MakeConstSpan(session->early_alpn, session->early_alpn_len)) {
+ MakeConstSpan(ssl->s3->alpn_selected) == session->early_alpn) {
ssl->s3->early_data_accepted = true;
}
@@ -425,14 +433,9 @@
hs->new_session->cipher = hs->new_cipher;
// Store the initial negotiated ALPN in the session.
- if (!ssl->s3->alpn_selected.empty()) {
- hs->new_session->early_alpn = (uint8_t *)BUF_memdup(
- ssl->s3->alpn_selected.data(), ssl->s3->alpn_selected.size());
- if (hs->new_session->early_alpn == NULL) {
- ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
- return ssl_hs_error;
- }
- hs->new_session->early_alpn_len = ssl->s3->alpn_selected.size();
+ if (!hs->new_session->early_alpn.CopyFrom(ssl->s3->alpn_selected)) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
}
if (ssl->ctx->dos_protection_cb != NULL &&
@@ -599,10 +602,10 @@
if (!ssl->s3->session_reused) {
// Determine whether to request a client certificate.
- hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
+ hs->cert_request = !!(hs->config->verify_mode & SSL_VERIFY_PEER);
// Only request a certificate if Channel ID isn't negotiated.
- if ((ssl->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
- ssl->s3->tlsext_channel_id_valid) {
+ if ((hs->config->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
+ ssl->s3->channel_id_valid) {
hs->cert_request = false;
}
}
@@ -635,13 +638,13 @@
}
}
- if (ssl_has_client_CAs(ssl)) {
+ if (ssl_has_client_CAs(hs->config)) {
CBB ca_contents;
if (!CBB_add_u16(&cert_request_extensions,
TLSEXT_TYPE_certificate_authorities) ||
!CBB_add_u16_length_prefixed(&cert_request_extensions,
&ca_contents) ||
- !ssl_add_client_CA_list(ssl, &ca_contents) ||
+ !ssl_add_client_CA_list(hs, &ca_contents) ||
!CBB_flush(&cert_request_extensions)) {
return ssl_hs_error;
}
@@ -654,7 +657,7 @@
// Send the server Certificate message, if necessary.
if (!ssl->s3->session_reused) {
- if (!ssl_has_certificate(ssl)) {
+ if (!ssl_has_certificate(hs->config)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
return ssl_hs_error;
}
@@ -731,11 +734,12 @@
assert(hs->hash_len <= 0xff);
uint8_t header[4] = {SSL3_MT_FINISHED, 0, 0,
static_cast<uint8_t>(hs->hash_len)};
+ bool unused_sent_tickets;
if (!hs->transcript.Update(header) ||
!hs->transcript.Update(
MakeConstSpan(hs->expected_client_finished, hs->hash_len)) ||
!tls13_derive_resumption_secret(hs) ||
- !add_new_session_tickets(hs)) {
+ !add_new_session_tickets(hs, &unused_sent_tickets)) {
return ssl_hs_error;
}
}
@@ -805,7 +809,7 @@
}
const int allow_anonymous =
- (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0;
+ (hs->config->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0;
SSLMessage msg;
if (!ssl->method->get_message(ssl, &msg)) {
return ssl_hs_read_message;
@@ -824,7 +828,7 @@
static enum ssl_hs_wait_t do_read_client_certificate_verify(
SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0) {
+ if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0) {
// Skip this state.
hs->tls13_state = state_read_channel_id;
return ssl_hs_ok;
@@ -858,7 +862,7 @@
static enum ssl_hs_wait_t do_read_channel_id(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- if (!ssl->s3->tlsext_channel_id_valid) {
+ if (!ssl->s3->channel_id_valid) {
hs->tls13_state = state_read_client_finished;
return ssl_hs_ok;
}
@@ -912,19 +916,13 @@
}
static enum ssl_hs_wait_t do_send_new_session_ticket(SSL_HANDSHAKE *hs) {
- // If the client doesn't accept resumption with PSK_DHE_KE, don't send a
- // session ticket.
- if (!hs->accept_psk_mode) {
- hs->tls13_state = state_done;
- return ssl_hs_ok;
- }
-
- if (!add_new_session_tickets(hs)) {
+ bool sent_tickets;
+ if (!add_new_session_tickets(hs, &sent_tickets)) {
return ssl_hs_error;
}
hs->tls13_state = state_done;
- return ssl_hs_flush;
+ return sent_tickets ? ssl_hs_flush : ssl_hs_ok;
}
enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs) {
diff --git a/src/ssl/tls_method.cc b/src/ssl/tls_method.cc
index 2ad2817..2af5171 100644
--- a/src/ssl/tls_method.cc
+++ b/src/ssl/tls_method.cc
@@ -95,6 +95,10 @@
}
static bool ssl3_set_write_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) {
+ if (!tls_flush_pending_hs_data(ssl)) {
+ return false;
+ }
+
OPENSSL_memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence));
ssl->s3->aead_write_ctx = std::move(aead_ctx);
return true;
@@ -141,16 +145,18 @@
}
static void ssl_noop_x509_session_clear(SSL_SESSION *session) {}
static int ssl_noop_x509_session_verify_cert_chain(SSL_SESSION *session,
- SSL *ssl,
+ SSL_HANDSHAKE *hs,
uint8_t *out_alert) {
return 0;
}
static void ssl_noop_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {}
-static int ssl_noop_x509_ssl_new(SSL *ctx) { return 1; }
-static void ssl_noop_x509_ssl_free(SSL *ctx) { }
-static void ssl_noop_x509_ssl_flush_cached_client_CA(SSL *ssl) {}
-static int ssl_noop_x509_ssl_auto_chain_if_needed(SSL *ssl) { return 1; }
+static int ssl_noop_x509_ssl_new(SSL_HANDSHAKE *hs) { return 1; }
+static void ssl_noop_x509_ssl_config_free(SSL_CONFIG *cfg) {}
+static void ssl_noop_x509_ssl_flush_cached_client_CA(SSL_CONFIG *cfg) {}
+static int ssl_noop_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) {
+ return 1;
+}
static int ssl_noop_x509_ssl_ctx_new(SSL_CTX *ctx) { return 1; }
static void ssl_noop_x509_ssl_ctx_free(SSL_CTX *ctx) { }
static void ssl_noop_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {}
@@ -168,7 +174,7 @@
ssl_noop_x509_session_verify_cert_chain,
ssl_noop_x509_hs_flush_cached_ca_names,
ssl_noop_x509_ssl_new,
- ssl_noop_x509_ssl_free,
+ ssl_noop_x509_ssl_config_free,
ssl_noop_x509_ssl_flush_cached_client_CA,
ssl_noop_x509_ssl_auto_chain_if_needed,
ssl_noop_x509_ssl_ctx_new,