Add an interface for QUIC integration.

0-RTT support and APIs to consume NewSessionTicket will be added in a
follow-up.

Change-Id: Ib2b2c6b618b3e33a74355fb53fdbd2ffafcc5c56
Reviewed-on: https://boringssl-review.googlesource.com/c/31744
Commit-Queue: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
This commit is contained in:
Steven Valdez 2018-07-14 11:23:01 -04:00 committed by CQ bot account: commit-bot@chromium.org
parent dc33220532
commit c8e0f90f83
16 changed files with 1094 additions and 156 deletions

View File

@ -128,6 +128,7 @@ SSL,271,PSK_IDENTITY_BINDER_COUNT_MISMATCH
SSL,195,PSK_IDENTITY_NOT_FOUND
SSL,196,PSK_NO_CLIENT_CB
SSL,197,PSK_NO_SERVER_CB
SSL,298,QUIC_INTERNAL_ERROR
SSL,198,READ_TIMEOUT_EXPIRED
SSL,199,RECORD_LENGTH_MISMATCH
SSL,200,RECORD_TOO_LARGE
@ -221,6 +222,7 @@ SSL,252,UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY
SSL,241,WRONG_CERTIFICATE_TYPE
SSL,242,WRONG_CIPHER_RETURNED
SSL,243,WRONG_CURVE
SSL,299,WRONG_ENCRYPTION_LEVEL_RECEIVED
SSL,244,WRONG_MESSAGE_TYPE
SSL,245,WRONG_SIGNATURE_TYPE
SSL,246,WRONG_SSL_VERSION

View File

@ -394,6 +394,7 @@ typedef struct ssl_cipher_st SSL_CIPHER;
typedef struct ssl_ctx_st SSL_CTX;
typedef struct ssl_method_st SSL_METHOD;
typedef struct ssl_private_key_method_st SSL_PRIVATE_KEY_METHOD;
typedef struct ssl_quic_method_st SSL_QUIC_METHOD;
typedef struct ssl_session_st SSL_SESSION;
typedef struct ssl_st SSL;
typedef struct ssl_ticket_aead_method_st SSL_TICKET_AEAD_METHOD;

View File

@ -3036,6 +3036,113 @@ OPENSSL_EXPORT void SSL_get_peer_quic_transport_params(const SSL *ssl,
size_t *out_params_len);
// QUIC integration.
//
// QUIC acts as an underlying transport for the TLS 1.3 handshake. The following
// functions allow a QUIC implementation to serve as the underlying transport as
// described in draft-ietf-quic-tls.
//
// When configured for QUIC, |SSL_do_handshake| will drive the handshake as
// before, but it will not use the configured |BIO|. It will call functions on
// |SSL_QUIC_METHOD| to configure secrets and send data. If data is needed from
// the peer, it will return |SSL_ERROR_WANT_READ|. When received, the caller
// should call |SSL_provide_quic_data| and then |SSL_do_handshake| to continue
// the handshake. It is an error to call |SSL_read| and |SSL_write| in QUIC.
//
// Note that secrets for an encryption level may be available to QUIC before the
// level is active in TLS. Callers should use |SSL_quic_read_level| to determine
// the active read level for |SSL_provide_quic_data|. |SSL_do_handshake| will
// pass the active write level to |SSL_QUIC_METHOD| when writing data. Callers
// can use |SSL_quic_write_level| to query the active write level when
// generating their own errors.
//
// See https://tools.ietf.org/html/draft-ietf-quic-tls-15#section-4.1 for more
// details.
//
// To avoid DoS attacks, the QUIC implementation must limit the amount of data
// being queued up. The implementation can call
// |SSL_quic_max_handshake_flight_len| to get the maximum buffer length at each
// encryption level.
//
// Note: 0-RTT and post-handshake tickets are not currently supported via this
// API.
// ssl_encryption_level_t represents a specific QUIC encryption level used to
// transmit handshake messages.
enum ssl_encryption_level_t {
ssl_encryption_initial = 0,
ssl_encryption_early_data,
ssl_encryption_handshake,
ssl_encryption_application,
};
// ssl_quic_method_st (aka |SSL_QUIC_METHOD|) describes custom QUIC hooks.
struct ssl_quic_method_st {
// set_encryption_secrets configures the read and write secrets for the given
// encryption level. This function will always be called before an encryption
// level other than |ssl_encryption_initial| is used. Note, however, that
// secrets for a level may be configured before TLS is ready to send or accept
// data at that level.
//
// When reading packets at a given level, the QUIC implementation must send
// ACKs at the same level, so this function provides read and write secrets
// together. The exception is |ssl_encryption_early_data|, where secrets are
// only available in the client to server direction. The other secret will be
// NULL. The server acknowledges such data at |ssl_encryption_application|,
// which will be configured in the same |SSL_do_handshake| call.
//
// This function should use |SSL_get_current_cipher| to determine the TLS
// cipher suite.
//
// It returns one on success and zero on error.
int (*set_encryption_secrets)(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *read_secret,
const uint8_t *write_secret, size_t secret_len);
// add_message adds a message to the current flight at the given encryption
// level. A single handshake flight may include multiple encryption levels.
// Callers can defer writing data to the network until |flush_flight| for
// optimal packing. It returns one on success and zero on error.
int (*add_message)(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len);
// flush_flight is called when the current flight is complete and should be
// written to the transport. Note a flight may contain data at several
// encryption levels. It returns one on success and zero on error.
int (*flush_flight)(SSL *ssl);
// send_alert sends a fatal alert at the specified encryption level. It
// returns one on success and zero on error.
int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert);
};
// SSL_quic_max_handshake_flight_len returns returns the maximum number of bytes
// that may be received at the given encryption level. This function should be
// used to limit buffering in the QUIC implementation.
//
// See https://tools.ietf.org/html/draft-ietf-quic-transport-16#section-4.4.
OPENSSL_EXPORT size_t SSL_quic_max_handshake_flight_len(
const SSL *ssl, enum ssl_encryption_level_t level);
// SSL_quic_read_level returns the current read encryption level.
OPENSSL_EXPORT enum ssl_encryption_level_t SSL_quic_read_level(const SSL *ssl);
// SSL_quic_write_level returns the current write encryption level.
OPENSSL_EXPORT enum ssl_encryption_level_t SSL_quic_write_level(const SSL *ssl);
// SSL_provide_quic_data provides data from QUIC at a particular encryption
// level |level|. It is an error to call this function outside of the handshake
// or with an encryption level other than the current read level. It returns one
// on success and zero on error.
OPENSSL_EXPORT int SSL_provide_quic_data(SSL *ssl,
enum ssl_encryption_level_t level,
const uint8_t *data, size_t len);
// SSL_CTX_set_quic_method configures the QUIC hooks. This should only be
// configured with a minimum version of TLS 1.3. |quic_method| must remain valid
// for the lifetime of |ctx|. It returns one on success and zero on error.
OPENSSL_EXPORT int SSL_CTX_set_quic_method(SSL_CTX *ctx,
const SSL_QUIC_METHOD *quic_method);
// Early data.
//
// WARNING: 0-RTT support in BoringSSL is currently experimental and not fully
@ -4795,6 +4902,8 @@ BSSL_NAMESPACE_END
#define SSL_R_INVALID_SIGNATURE_ALGORITHM 295
#define SSL_R_DUPLICATE_SIGNATURE_ALGORITHM 296
#define SSL_R_TLS13_DOWNGRADE 297
#define SSL_R_QUIC_INTERNAL_ERROR 298
#define SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED 299
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020

View File

@ -543,6 +543,16 @@ int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) {
case ssl_hs_read_server_hello:
case ssl_hs_read_message:
case ssl_hs_read_change_cipher_spec: {
if (ssl->ctx->quic_method) {
hs->wait = ssl_hs_ok;
// The change cipher spec is omitted in QUIC.
if (hs->wait != ssl_hs_read_change_cipher_spec) {
ssl->s3->rwstate = SSL_READING;
return -1;
}
break;
}
uint8_t alert = SSL_AD_DECODE_ERROR;
size_t consumed = 0;
ssl_open_record_t ret;

View File

@ -459,8 +459,8 @@ static enum ssl_hs_wait_t do_enter_early_data(SSL_HANDSHAKE *hs) {
if (!tls13_init_early_key_schedule(hs, ssl->session->master_key,
ssl->session->master_key_length) ||
!tls13_derive_early_secrets(hs) ||
!tls13_set_traffic_key(ssl, evp_aead_seal, hs->early_traffic_secret,
hs->hash_len)) {
!tls13_set_traffic_key(ssl, ssl_encryption_early_data, evp_aead_seal,
hs->early_traffic_secret, hs->hash_len)) {
return ssl_hs_error;
}

View File

@ -665,6 +665,12 @@ class SSLAEADContext {
Span<const uint8_t> mac_key,
Span<const uint8_t> fixed_iv);
// CreatePlaceholderForQUIC creates a placeholder |SSLAEADContext| for the
// given cipher and version. The resulting object can be queried for various
// properties but cannot encrypt or decrypt data.
static UniquePtr<SSLAEADContext> CreatePlaceholderForQUIC(
uint16_t version, const SSL_CIPHER *cipher);
// SetVersionIfNullCipher sets the version the SSLAEADContext for the null
// cipher, to make version-specific determinations in the record layer prior
// to a cipher being selected.
@ -1231,7 +1237,8 @@ bool tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
// tls13_set_traffic_key sets the read or write traffic keys to
// |traffic_secret|. It returns true on success and false on error.
bool tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
bool tls13_set_traffic_key(SSL *ssl, enum ssl_encryption_level_t level,
enum evp_aead_direction_t direction,
const uint8_t *traffic_secret,
size_t traffic_secret_len);
@ -1272,7 +1279,8 @@ bool tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
// tls13_derive_session_psk calculates the PSK for this session based on the
// resumption master secret and |nonce|. It returns true on success, and false
// on failure.
bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce);
bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce,
bool use_quic);
// tls13_write_psk_binder calculates the PSK binder value and replaces the last
// bytes of |msg| with the resulting value. It returns true on success, and
@ -2074,6 +2082,9 @@ struct SSL3_STATE {
// needs re-doing when in SSL_accept or SSL_connect
int rwstate = SSL_NOTHING;
enum ssl_encryption_level_t read_level = ssl_encryption_initial;
enum ssl_encryption_level_t write_level = ssl_encryption_initial;
// early_data_skipped is the amount of early data that has been skipped by the
// record layer.
uint16_t early_data_skipped = 0;
@ -2790,6 +2801,9 @@ struct ssl_ctx_st {
// and is further constrainted by |SSL_OP_NO_*|.
uint16_t conf_min_version = 0;
// quic_method is the method table corresponding to the QUIC hooks.
const SSL_QUIC_METHOD *quic_method = nullptr;
// tls13_variant is the variant of TLS 1.3 we are using for this
// configuration.
tls13_variant_t tls13_variant = tls13_rfc;

View File

@ -184,48 +184,56 @@ bool ssl3_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg) {
}
bool ssl3_add_message(SSL *ssl, Array<uint8_t> msg) {
// 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;
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 (ssl->ctx->quic_method) {
if (!ssl->ctx->quic_method->add_message(ssl, ssl->s3->write_level,
msg.data(), msg.size())) {
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
return false;
}
} 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;
}
// 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;
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());
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 (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, chunk)) {
return false;
}
}
if (!ssl->s3->pending_hs_data ||
!BUF_MEM_append(ssl->s3->pending_hs_data.get(), chunk.data(),
chunk.size())) {
return false;
} 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;
}
}
}
}
@ -241,7 +249,8 @@ bool ssl3_add_message(SSL *ssl, Array<uint8_t> msg) {
}
bool tls_flush_pending_hs_data(SSL *ssl) {
if (!ssl->s3->pending_hs_data || ssl->s3->pending_hs_data->length == 0) {
if (!ssl->s3->pending_hs_data || ssl->s3->pending_hs_data->length == 0 ||
ssl->ctx->quic_method) {
return true;
}
@ -255,7 +264,11 @@ bool tls_flush_pending_hs_data(SSL *ssl) {
bool ssl3_add_change_cipher_spec(SSL *ssl) {
static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
if (!tls_flush_pending_hs_data(ssl) ||
if (!tls_flush_pending_hs_data(ssl)) {
return false;
}
if (!ssl->ctx->quic_method &&
!add_record_to_flight(ssl, SSL3_RT_CHANGE_CIPHER_SPEC,
kChangeCipherSpec)) {
return false;
@ -267,6 +280,18 @@ bool ssl3_add_change_cipher_spec(SSL *ssl) {
}
int ssl3_flush_flight(SSL *ssl) {
if (ssl->ctx->quic_method) {
if (ssl->s3->write_shutdown != ssl_shutdown_none) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
return -1;
}
if (!ssl->ctx->quic_method->flush_flight(ssl)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
return -1;
}
}
if (!tls_flush_pending_hs_data(ssl)) {
return -1;
}

View File

@ -163,9 +163,11 @@ int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *in,
for (;;) {
// max contains the maximum number of bytes that we can put into a record.
unsigned max = ssl->max_send_fragment;
if (is_early_data_write && max > ssl->session->ticket_max_early_data -
ssl->s3->hs->early_data_written) {
max = ssl->session->ticket_max_early_data - ssl->s3->hs->early_data_written;
if (is_early_data_write &&
max > ssl->session->ticket_max_early_data -
ssl->s3->hs->early_data_written) {
max =
ssl->session->ticket_max_early_data - ssl->s3->hs->early_data_written;
if (max == 0) {
ssl->s3->wnum = tot;
ssl->s3->hs->can_early_write = false;
@ -406,10 +408,19 @@ int ssl_send_alert(SSL *ssl, int level, int desc) {
}
int ssl3_dispatch_alert(SSL *ssl) {
int ret = do_ssl3_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2);
if (ret <= 0) {
return ret;
if (ssl->ctx->quic_method) {
if (!ssl->ctx->quic_method->send_alert(ssl, ssl->s3->write_level,
ssl->s3->send_alert[1])) {
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
return 0;
}
} else {
int ret = do_ssl3_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2);
if (ret <= 0) {
return ret;
}
}
ssl->s3->alert_dispatch = 0;
// If the alert is fatal, flush the BIO now.

View File

@ -151,6 +151,11 @@ UniquePtr<SSLAEADContext> SSLAEADContext::Create(
return aead_ctx;
}
UniquePtr<SSLAEADContext> SSLAEADContext::CreatePlaceholderForQUIC(
uint16_t version, const SSL_CIPHER *cipher) {
return MakeUnique<SSLAEADContext>(version, false, cipher);
}
void SSLAEADContext::SetVersionIfNullCipher(uint16_t version) {
if (is_null_cipher()) {
version_ = version;

View File

@ -781,6 +781,82 @@ BIO *SSL_get_rbio(const SSL *ssl) { return ssl->rbio.get(); }
BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio.get(); }
size_t SSL_quic_max_handshake_flight_len(const SSL *ssl,
enum ssl_encryption_level_t level) {
// Limits flights to 16K by default when there are no large
// (certificate-carrying) messages.
static const size_t kDefaultLimit = 16384;
switch (level) {
case ssl_encryption_initial:
return kDefaultLimit;
case ssl_encryption_early_data:
// QUIC does not send EndOfEarlyData.
return 0;
case ssl_encryption_handshake:
if (ssl->server) {
// Servers may receive Certificate message if configured to request
// client certificates.
if (!!(ssl->config->verify_mode & SSL_VERIFY_PEER) &&
ssl->max_cert_list > kDefaultLimit) {
return ssl->max_cert_list;
}
} else {
// Clients may receive both Certificate message and a CertificateRequest
// message.
if (2*ssl->max_cert_list > kDefaultLimit) {
return 2*ssl->max_cert_list;
}
}
return kDefaultLimit;
case ssl_encryption_application:
// Note there is not actually a bound on the number of NewSessionTickets
// one may send in a row. This level may need more involved flow
// control. See https://github.com/quicwg/base-drafts/issues/1834.
return kDefaultLimit;
}
return 0;
}
enum ssl_encryption_level_t SSL_quic_read_level(const SSL *ssl) {
return ssl->s3->read_level;
}
enum ssl_encryption_level_t SSL_quic_write_level(const SSL *ssl) {
return ssl->s3->write_level;
}
int SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len) {
if (ssl->ctx->quic_method == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
if (level != ssl->s3->read_level) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED);
return 0;
}
size_t new_len = (ssl->s3->hs_buf ? ssl->s3->hs_buf->length : 0) + len;
if (new_len < len ||
new_len > SSL_quic_max_handshake_flight_len(ssl, level)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
return 0;
}
// Re-create the handshake buffer if needed.
if (!ssl->s3->hs_buf) {
ssl->s3->hs_buf.reset(BUF_MEM_new());
if (!ssl->s3->hs_buf) {
return 0;
}
}
return BUF_MEM_append(ssl->s3->hs_buf.get(), data, len);
}
int SSL_do_handshake(SSL *ssl) {
ssl_reset_error_state(ssl);
@ -961,6 +1037,11 @@ int SSL_read(SSL *ssl, void *buf, int num) {
}
int SSL_peek(SSL *ssl, void *buf, int num) {
if (ssl->ctx->quic_method != nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
int ret = ssl_read_impl(ssl);
if (ret <= 0) {
return ret;
@ -977,6 +1058,11 @@ int SSL_peek(SSL *ssl, void *buf, int num) {
int SSL_write(SSL *ssl, const void *buf, int num) {
ssl_reset_error_state(ssl);
if (ssl->ctx->quic_method != nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
if (ssl->do_handshake == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED);
return -1;
@ -1193,6 +1279,9 @@ int SSL_get_error(const SSL *ssl, int ret_code) {
return SSL_ERROR_HANDBACK;
case SSL_READING: {
if (ssl->ctx->quic_method) {
return SSL_ERROR_WANT_READ;
}
BIO *bio = SSL_get_rbio(ssl);
if (BIO_should_read(bio)) {
return SSL_ERROR_WANT_READ;
@ -2298,6 +2387,14 @@ char *SSL_get_shared_ciphers(const SSL *ssl, char *buf, int len) {
return buf;
}
int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method) {
if (ctx->method->is_dtls) {
return 0;
}
ctx->quic_method = quic_method;
return 1;
}
int SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_unused *unused,
CRYPTO_EX_dup *dup_unused, CRYPTO_EX_free *free_func) {
int index;

View File

@ -17,6 +17,7 @@
#include <time.h>
#include <algorithm>
#include <limits>
#include <string>
#include <utility>
#include <vector>
@ -104,6 +105,26 @@ struct CurveTest {
std::vector<uint16_t> expected;
};
template <typename T>
class UnownedSSLExData {
public:
UnownedSSLExData() {
index_ = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
}
T *Get(const SSL *ssl) {
return index_ < 0 ? nullptr
: static_cast<T *>(SSL_get_ex_data(ssl, index_));
}
bool Set(SSL *ssl, T *t) {
return index_ >= 0 && SSL_set_ex_data(ssl, index_, t);
}
private:
int index_;
};
static const CipherTest kCipherTests[] = {
// Selecting individual ciphers should work.
{
@ -4422,6 +4443,546 @@ TEST(SSLTest, GetCertificateThreads) {
}
#endif
constexpr size_t kNumQUICLevels = 4;
static_assert(ssl_encryption_initial < kNumQUICLevels,
"kNumQUICLevels is wrong");
static_assert(ssl_encryption_early_data < kNumQUICLevels,
"kNumQUICLevels is wrong");
static_assert(ssl_encryption_handshake < kNumQUICLevels,
"kNumQUICLevels is wrong");
static_assert(ssl_encryption_application < kNumQUICLevels,
"kNumQUICLevels is wrong");
class MockQUICTransport {
public:
MockQUICTransport() {
// The caller is expected to configure initial secrets.
levels_[ssl_encryption_initial].write_secret = {1};
levels_[ssl_encryption_initial].read_secret = {1};
}
void set_peer(MockQUICTransport *peer) { peer_ = peer; }
bool has_alert() const { return has_alert_; }
ssl_encryption_level_t alert_level() const { return alert_level_; }
uint8_t alert() const { return alert_; }
bool PeerSecretsMatch(ssl_encryption_level_t level) const {
return levels_[level].write_secret == peer_->levels_[level].read_secret &&
levels_[level].read_secret == peer_->levels_[level].write_secret;
}
bool HasSecrets(ssl_encryption_level_t level) const {
return !levels_[level].write_secret.empty() ||
!levels_[level].read_secret.empty();
}
bool SetEncryptionSecrets(ssl_encryption_level_t level,
const uint8_t *read_secret,
const uint8_t *write_secret, size_t secret_len) {
if (HasSecrets(level)) {
ADD_FAILURE() << "duplicate keys configured";
return false;
}
if (level != ssl_encryption_early_data &&
(read_secret == nullptr || write_secret == nullptr)) {
ADD_FAILURE() << "key was unexpectedly null";
return false;
}
if (read_secret != nullptr) {
levels_[level].read_secret.assign(read_secret, read_secret + secret_len);
}
if (write_secret != nullptr) {
levels_[level].write_secret.assign(write_secret,
write_secret + secret_len);
}
return true;
}
bool WriteHandshakeData(ssl_encryption_level_t level,
Span<const uint8_t> data) {
if (levels_[level].write_secret.empty()) {
ADD_FAILURE() << "data written before keys configured";
return false;
}
levels_[level].write_data.insert(levels_[level].write_data.end(),
data.begin(), data.end());
return true;
}
bool SendAlert(ssl_encryption_level_t level, uint8_t alert_value) {
if (has_alert_) {
ADD_FAILURE() << "duplicate alert sent";
return false;
}
if (levels_[level].write_secret.empty()) {
ADD_FAILURE() << "alert sent before keys configured";
return false;
}
has_alert_ = true;
alert_level_ = level;
alert_ = alert_value;
return true;
}
bool ReadHandshakeData(std::vector<uint8_t> *out,
ssl_encryption_level_t level,
size_t num = std::numeric_limits<size_t>::max()) {
if (levels_[level].read_secret.empty()) {
ADD_FAILURE() << "data read before keys configured";
return false;
}
// The peer may not have configured any keys yet.
if (peer_->levels_[level].write_secret.empty()) {
return true;
}
// Check the peer computed the same key.
if (peer_->levels_[level].write_secret != levels_[level].read_secret) {
ADD_FAILURE() << "peer write key does not match read key";
return false;
}
std::vector<uint8_t> *peer_data = &peer_->levels_[level].write_data;
num = std::min(num, peer_data->size());
out->assign(peer_data->begin(), peer_data->begin() + num);
peer_data->erase(peer_data->begin(), peer_data->begin() + num);
return true;
}
private:
MockQUICTransport *peer_ = nullptr;
bool has_alert_ = false;
ssl_encryption_level_t alert_level_ = ssl_encryption_initial;
uint8_t alert_ = 0;
struct Level {
std::vector<uint8_t> write_data;
std::vector<uint8_t> write_secret;
std::vector<uint8_t> read_secret;
};
Level levels_[kNumQUICLevels];
};
class MockQUICTransportPair {
public:
MockQUICTransportPair() {
server_.set_peer(&client_);
client_.set_peer(&server_);
}
~MockQUICTransportPair() {
server_.set_peer(nullptr);
client_.set_peer(nullptr);
}
MockQUICTransport *client() { return &client_; }
MockQUICTransport *server() { return &server_; }
bool SecretsMatch(ssl_encryption_level_t level) const {
return client_.PeerSecretsMatch(level);
}
private:
MockQUICTransport client_;
MockQUICTransport server_;
};
class QUICMethodTest : public testing::Test {
protected:
void SetUp() override {
client_ctx_.reset(SSL_CTX_new(TLS_method()));
server_ctx_.reset(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()));
SSL_CTX_set_min_proto_version(server_ctx_.get(), TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(server_ctx_.get(), TLS1_3_VERSION);
SSL_CTX_set_min_proto_version(client_ctx_.get(), TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(client_ctx_.get(), TLS1_3_VERSION);
}
static MockQUICTransport *TransportFromSSL(const SSL *ssl) {
return ex_data_.Get(ssl);
}
static bool ProvideHandshakeData(
SSL *ssl, size_t num = std::numeric_limits<size_t>::max()) {
MockQUICTransport *transport = TransportFromSSL(ssl);
ssl_encryption_level_t level = SSL_quic_read_level(ssl);
std::vector<uint8_t> data;
return transport->ReadHandshakeData(&data, level, num) &&
SSL_provide_quic_data(ssl, level, data.data(), data.size());
}
bool CreateClientAndServer() {
client_.reset(SSL_new(client_ctx_.get()));
server_.reset(SSL_new(server_ctx_.get()));
if (!client_ || !server_) {
return false;
}
SSL_set_connect_state(client_.get());
SSL_set_accept_state(server_.get());
ex_data_.Set(client_.get(), transport_.client());
ex_data_.Set(server_.get(), transport_.server());
return true;
}
// The following functions may be configured on an |SSL_QUIC_METHOD| as
// default implementations.
static int SetEncryptionSecretsCallback(SSL *ssl,
ssl_encryption_level_t level,
const uint8_t *read_key,
const uint8_t *write_key,
size_t key_len) {
return TransportFromSSL(ssl)->SetEncryptionSecrets(level, read_key,
write_key, key_len);
}
static int AddMessageCallback(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len) {
EXPECT_EQ(level, SSL_quic_write_level(ssl));
return TransportFromSSL(ssl)->WriteHandshakeData(level,
MakeConstSpan(data, len));
}
static int FlushFlightCallback(SSL *ssl) { return 1; }
static int SendAlertCallback(SSL *ssl, ssl_encryption_level_t level,
uint8_t alert) {
EXPECT_EQ(level, SSL_quic_write_level(ssl));
return TransportFromSSL(ssl)->SendAlert(level, alert);
}
bssl::UniquePtr<SSL_CTX> client_ctx_;
bssl::UniquePtr<SSL_CTX> server_ctx_;
static UnownedSSLExData<MockQUICTransport> ex_data_;
MockQUICTransportPair transport_;
bssl::UniquePtr<SSL> client_;
bssl::UniquePtr<SSL> server_;
};
UnownedSSLExData<MockQUICTransport> QUICMethodTest::ex_data_;
// Test a full handshake works.
TEST_F(QUICMethodTest, Basic) {
const SSL_QUIC_METHOD quic_method = {
SetEncryptionSecretsCallback,
AddMessageCallback,
FlushFlightCallback,
SendAlertCallback,
};
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
for (;;) {
ASSERT_TRUE(ProvideHandshakeData(client_.get()));
int client_ret = SSL_do_handshake(client_.get());
if (client_ret != 1) {
ASSERT_EQ(client_ret, -1);
ASSERT_EQ(SSL_get_error(client_.get(), client_ret), SSL_ERROR_WANT_READ);
}
ASSERT_TRUE(ProvideHandshakeData(server_.get()));
int server_ret = SSL_do_handshake(server_.get());
if (server_ret != 1) {
ASSERT_EQ(server_ret, -1);
ASSERT_EQ(SSL_get_error(server_.get(), server_ret), SSL_ERROR_WANT_READ);
}
if (client_ret == 1 && server_ret == 1) {
break;
}
}
EXPECT_EQ(SSL_do_handshake(client_.get()), 1);
EXPECT_EQ(SSL_do_handshake(server_.get()), 1);
EXPECT_TRUE(transport_.SecretsMatch(ssl_encryption_application));
EXPECT_FALSE(transport_.client()->has_alert());
EXPECT_FALSE(transport_.server()->has_alert());
// The server sent NewSessionTicket messages in the handshake.
//
// TODO(davidben,svaldez): Add an API for the client to consume post-handshake
// messages and update these tests.
std::vector<uint8_t> new_session_ticket;
ASSERT_TRUE(transport_.client()->ReadHandshakeData(
&new_session_ticket, ssl_encryption_application));
EXPECT_FALSE(new_session_ticket.empty());
}
// Test only releasing data to QUIC one byte at a time on request, to maximize
// state machine pauses. Additionally, test that existing asynchronous callbacks
// still work.
TEST_F(QUICMethodTest, Async) {
const SSL_QUIC_METHOD quic_method = {
SetEncryptionSecretsCallback,
AddMessageCallback,
FlushFlightCallback,
SendAlertCallback,
};
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
// Install an asynchronous certificate callback.
bool cert_cb_ok = false;
SSL_set_cert_cb(server_.get(),
[](SSL *, void *arg) -> int {
return *static_cast<bool *>(arg) ? 1 : -1;
},
&cert_cb_ok);
for (;;) {
int client_ret = SSL_do_handshake(client_.get());
if (client_ret != 1) {
ASSERT_EQ(client_ret, -1);
ASSERT_EQ(SSL_get_error(client_.get(), client_ret), SSL_ERROR_WANT_READ);
ASSERT_TRUE(ProvideHandshakeData(client_.get(), 1));
}
int server_ret = SSL_do_handshake(server_.get());
if (server_ret != 1) {
ASSERT_EQ(server_ret, -1);
int ssl_err = SSL_get_error(server_.get(), server_ret);
switch (ssl_err) {
case SSL_ERROR_WANT_READ:
ASSERT_TRUE(ProvideHandshakeData(server_.get(), 1));
break;
case SSL_ERROR_WANT_X509_LOOKUP:
ASSERT_FALSE(cert_cb_ok);
cert_cb_ok = true;
break;
default:
FAIL() << "Unexpected SSL_get_error result: " << ssl_err;
}
}
if (client_ret == 1 && server_ret == 1) {
break;
}
}
EXPECT_EQ(SSL_do_handshake(client_.get()), 1);
EXPECT_EQ(SSL_do_handshake(server_.get()), 1);
EXPECT_TRUE(transport_.SecretsMatch(ssl_encryption_application));
EXPECT_FALSE(transport_.client()->has_alert());
EXPECT_FALSE(transport_.server()->has_alert());
}
// Test buffering write data until explicit flushes.
TEST_F(QUICMethodTest, Buffered) {
struct BufferedFlight {
std::vector<uint8_t> data[kNumQUICLevels];
};
static UnownedSSLExData<BufferedFlight> buffered_flights;
auto add_message = [](SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len) -> int {
BufferedFlight *flight = buffered_flights.Get(ssl);
flight->data[level].insert(flight->data[level].end(), data, data + len);
return 1;
};
auto flush_flight = [](SSL *ssl) -> int {
BufferedFlight *flight = buffered_flights.Get(ssl);
for (size_t level = 0; level < kNumQUICLevels; level++) {
if (!flight->data[level].empty()) {
if (!TransportFromSSL(ssl)->WriteHandshakeData(
static_cast<ssl_encryption_level_t>(level),
flight->data[level])) {
return 0;
}
flight->data[level].clear();
}
}
return 1;
};
const SSL_QUIC_METHOD quic_method = {
SetEncryptionSecretsCallback,
add_message,
flush_flight,
SendAlertCallback,
};
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
BufferedFlight client_flight, server_flight;
buffered_flights.Set(client_.get(), &client_flight);
buffered_flights.Set(server_.get(), &server_flight);
for (;;) {
ASSERT_TRUE(ProvideHandshakeData(client_.get()));
int client_ret = SSL_do_handshake(client_.get());
if (client_ret != 1) {
ASSERT_EQ(client_ret, -1);
ASSERT_EQ(SSL_get_error(client_.get(), client_ret), SSL_ERROR_WANT_READ);
}
ASSERT_TRUE(ProvideHandshakeData(server_.get()));
int server_ret = SSL_do_handshake(server_.get());
if (server_ret != 1) {
ASSERT_EQ(server_ret, -1);
ASSERT_EQ(SSL_get_error(server_.get(), server_ret), SSL_ERROR_WANT_READ);
}
if (client_ret == 1 && server_ret == 1) {
break;
}
}
EXPECT_EQ(SSL_do_handshake(client_.get()), 1);
EXPECT_EQ(SSL_do_handshake(server_.get()), 1);
EXPECT_TRUE(transport_.SecretsMatch(ssl_encryption_application));
EXPECT_FALSE(transport_.client()->has_alert());
EXPECT_FALSE(transport_.server()->has_alert());
}
// Test that excess data at one level is rejected. That is, if a single
// |SSL_provide_quic_data| call included both ServerHello and
// EncryptedExtensions in a single chunk, BoringSSL notices and rejects this on
// key change.
TEST_F(QUICMethodTest, ExcessProvidedData) {
auto add_message = [](SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len) -> int {
// Switch everything to the initial level.
return TransportFromSSL(ssl)->WriteHandshakeData(ssl_encryption_initial,
MakeConstSpan(data, len));
};
const SSL_QUIC_METHOD quic_method = {
SetEncryptionSecretsCallback,
add_message,
FlushFlightCallback,
SendAlertCallback,
};
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
// Send the ClientHello and ServerHello through Finished.
ASSERT_EQ(SSL_do_handshake(client_.get()), -1);
ASSERT_EQ(SSL_get_error(client_.get(), -1), SSL_ERROR_WANT_READ);
ASSERT_TRUE(ProvideHandshakeData(server_.get()));
ASSERT_EQ(SSL_do_handshake(server_.get()), -1);
ASSERT_EQ(SSL_get_error(server_.get(), -1), SSL_ERROR_WANT_READ);
// The client is still waiting for the ServerHello at initial
// encryption.
ASSERT_EQ(ssl_encryption_initial, SSL_quic_read_level(client_.get()));
// |add_message| incorrectly wrote everything at the initial level, so this
// queues up ServerHello through Finished in one chunk.
ASSERT_TRUE(ProvideHandshakeData(client_.get()));
// The client reads ServerHello successfully, but then rejects the buffered
// EncryptedExtensions on key change.
ASSERT_EQ(SSL_do_handshake(client_.get()), -1);
ASSERT_EQ(SSL_get_error(client_.get(), -1), SSL_ERROR_SSL);
uint32_t err = ERR_get_error();
EXPECT_EQ(ERR_GET_LIB(err), ERR_LIB_SSL);
EXPECT_EQ(ERR_GET_REASON(err), SSL_R_BUFFERED_MESSAGES_ON_CIPHER_CHANGE);
// The client sends an alert in response to this.
ASSERT_TRUE(transport_.client()->has_alert());
EXPECT_EQ(transport_.client()->alert_level(), ssl_encryption_initial);
EXPECT_EQ(transport_.client()->alert(), SSL_AD_UNEXPECTED_MESSAGE);
// Sanity-check client did get far enough to process the ServerHello and
// install keys.
EXPECT_TRUE(transport_.client()->HasSecrets(ssl_encryption_handshake));
}
// Test that |SSL_provide_quic_data| will reject data at the wrong level.
TEST_F(QUICMethodTest, ProvideWrongLevel) {
const SSL_QUIC_METHOD quic_method = {
SetEncryptionSecretsCallback,
AddMessageCallback,
FlushFlightCallback,
SendAlertCallback,
};
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
// Send the ClientHello and ServerHello through Finished.
ASSERT_EQ(SSL_do_handshake(client_.get()), -1);
ASSERT_EQ(SSL_get_error(client_.get(), -1), SSL_ERROR_WANT_READ);
ASSERT_TRUE(ProvideHandshakeData(server_.get()));
ASSERT_EQ(SSL_do_handshake(server_.get()), -1);
ASSERT_EQ(SSL_get_error(server_.get(), -1), SSL_ERROR_WANT_READ);
// The client is still waiting for the ServerHello at initial
// encryption.
ASSERT_EQ(ssl_encryption_initial, SSL_quic_read_level(client_.get()));
// Data cannot be provided at the next level.
std::vector<uint8_t> data;
ASSERT_TRUE(
transport_.client()->ReadHandshakeData(&data, ssl_encryption_initial));
ASSERT_FALSE(SSL_provide_quic_data(client_.get(), ssl_encryption_handshake,
data.data(), data.size()));
ERR_clear_error();
// Progress to EncryptedExtensions.
ASSERT_TRUE(SSL_provide_quic_data(client_.get(), ssl_encryption_initial,
data.data(), data.size()));
ASSERT_EQ(SSL_do_handshake(client_.get()), -1);
ASSERT_EQ(SSL_get_error(client_.get(), -1), SSL_ERROR_WANT_READ);
ASSERT_EQ(ssl_encryption_handshake, SSL_quic_read_level(client_.get()));
// Data cannot be provided at the previous level.
ASSERT_TRUE(
transport_.client()->ReadHandshakeData(&data, ssl_encryption_handshake));
ASSERT_FALSE(SSL_provide_quic_data(client_.get(), ssl_encryption_initial,
data.data(), data.size()));
}
TEST_F(QUICMethodTest, TooMuchData) {
const SSL_QUIC_METHOD quic_method = {
SetEncryptionSecretsCallback,
AddMessageCallback,
FlushFlightCallback,
SendAlertCallback,
};
ASSERT_TRUE(SSL_CTX_set_quic_method(client_ctx_.get(), &quic_method));
ASSERT_TRUE(SSL_CTX_set_quic_method(server_ctx_.get(), &quic_method));
ASSERT_TRUE(CreateClientAndServer());
size_t limit =
SSL_quic_max_handshake_flight_len(client_.get(), ssl_encryption_initial);
uint8_t b = 0;
for (size_t i = 0; i < limit; i++) {
ASSERT_TRUE(
SSL_provide_quic_data(client_.get(), ssl_encryption_initial, &b, 1));
}
EXPECT_FALSE(
SSL_provide_quic_data(client_.get(), ssl_encryption_initial, &b, 1));
}
// TODO(davidben): Convert this file to GTest properly.
TEST(SSLTest, AllTests) {
if (!TestSSL_SESSIONEncoding(kOpenSSLSession) ||

View File

@ -217,6 +217,11 @@ bool ssl_get_version_range(const SSL_HANDSHAKE *hs, uint16_t *out_min_version,
uint16_t min_version = hs->config->conf_min_version;
uint16_t max_version = hs->config->conf_max_version;
// QUIC requires TLS 1.3.
if (hs->ssl->ctx->quic_method && min_version < TLS1_3_VERSION) {
min_version = TLS1_3_VERSION;
}
// OpenSSL's API for controlling versions entails blacklisting individual
// protocols. This has two problems. First, on the client, the protocol can
// only express a contiguous range of versions. Second, a library consumer

View File

@ -645,7 +645,8 @@ static bool tls13_receive_key_update(SSL *ssl, const SSLMessage &msg) {
bool tls13_post_handshake(SSL *ssl, const SSLMessage &msg) {
if (msg.type == SSL3_MT_KEY_UPDATE) {
ssl->s3->key_update_count++;
if (ssl->s3->key_update_count > kMaxKeyUpdates) {
if (ssl->ctx->quic_method != nullptr ||
ssl->s3->key_update_count > kMaxKeyUpdates) {
OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_KEY_UPDATES);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
return false;

View File

@ -389,18 +389,17 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
}
if (!tls13_advance_key_schedule(hs, dhe_secret.data(), dhe_secret.size()) ||
!ssl_hash_message(hs, msg) ||
!tls13_derive_handshake_secrets(hs) ||
!tls13_set_traffic_key(ssl, evp_aead_open, hs->server_handshake_secret,
hs->hash_len)) {
!ssl_hash_message(hs, msg) || !tls13_derive_handshake_secrets(hs) ||
!tls13_set_traffic_key(ssl, ssl_encryption_handshake, evp_aead_open,
hs->server_handshake_secret, hs->hash_len)) {
return ssl_hs_error;
}
if (!hs->early_data_offered) {
// If not sending early data, set client traffic keys now so that alerts are
// encrypted.
if (!tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
hs->hash_len)) {
if (!tls13_set_traffic_key(ssl, ssl_encryption_handshake, evp_aead_seal,
hs->client_handshake_secret, hs->hash_len)) {
return ssl_hs_error;
}
}
@ -641,8 +640,8 @@ static enum ssl_hs_wait_t do_send_end_of_early_data(SSL_HANDSHAKE *hs) {
}
if (hs->early_data_offered) {
if (!tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
hs->hash_len)) {
if (!tls13_set_traffic_key(ssl, ssl_encryption_handshake, evp_aead_seal,
hs->client_handshake_secret, hs->hash_len)) {
return ssl_hs_error;
}
}
@ -736,10 +735,10 @@ static enum ssl_hs_wait_t do_complete_second_flight(SSL_HANDSHAKE *hs) {
}
// Derive the final keys and enable them.
if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->server_traffic_secret_0,
hs->hash_len) ||
!tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_traffic_secret_0,
hs->hash_len) ||
if (!tls13_set_traffic_key(ssl, ssl_encryption_application, evp_aead_open,
hs->server_traffic_secret_0, hs->hash_len) ||
!tls13_set_traffic_key(ssl, ssl_encryption_application, evp_aead_seal,
hs->client_traffic_secret_0, hs->hash_len) ||
!tls13_derive_resumption_secret(hs)) {
return ssl_hs_error;
}
@ -883,7 +882,8 @@ bool tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg) {
session->timeout = server_timeout;
}
if (!tls13_derive_session_psk(session.get(), ticket_nonce)) {
if (!tls13_derive_session_psk(session.get(), ticket_nonce,
ssl->ctx->quic_method != nullptr)) {
return false;
}

View File

@ -69,19 +69,27 @@ bool tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
static bool hkdf_expand_label(uint8_t *out, const EVP_MD *digest,
const uint8_t *secret, size_t secret_len,
const char *label, size_t label_len,
const uint8_t *hash, size_t hash_len,
size_t len) {
static const char kTLS13LabelVersion[] = "tls13 ";
const uint8_t *hash, size_t hash_len, size_t len,
bool use_quic_label) {
static const char kTLS13ProtocolLabel[] = "tls13 ";
static const char kQUICProtocolLabel[] = "quic ";
const char *protocol_label;
if (use_quic_label) {
protocol_label = kQUICProtocolLabel;
} else {
protocol_label = kTLS13ProtocolLabel;
}
ScopedCBB cbb;
CBB child;
Array<uint8_t> hkdf_label;
if (!CBB_init(cbb.get(), 2 + 1 + strlen(kTLS13LabelVersion) + label_len + 1 +
hash_len) ||
if (!CBB_init(cbb.get(),
2 + 1 + strlen(protocol_label) + label_len + 1 + hash_len) ||
!CBB_add_u16(cbb.get(), len) ||
!CBB_add_u8_length_prefixed(cbb.get(), &child) ||
!CBB_add_bytes(&child, (const uint8_t *)kTLS13LabelVersion,
strlen(kTLS13LabelVersion)) ||
!CBB_add_bytes(&child, (const uint8_t *)protocol_label,
strlen(protocol_label)) ||
!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) ||
@ -107,7 +115,8 @@ bool tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
if (!hkdf_expand_label(hs->secret, hs->transcript.Digest(), hs->secret,
hs->hash_len, kTLS13LabelDerived,
strlen(kTLS13LabelDerived), derive_context,
derive_context_len, hs->hash_len)) {
derive_context_len, hs->hash_len,
hs->ssl->ctx->quic_method != nullptr)) {
return false;
}
@ -128,10 +137,12 @@ static bool derive_secret(SSL_HANDSHAKE *hs, uint8_t *out, size_t len,
return hkdf_expand_label(out, hs->transcript.Digest(), hs->secret,
hs->hash_len, label, label_len, context_hash,
context_hash_len, len);
context_hash_len, len,
hs->ssl->ctx->quic_method != nullptr);
}
bool tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
bool tls13_set_traffic_key(SSL *ssl, enum ssl_encryption_level_t level,
enum evp_aead_direction_t direction,
const uint8_t *traffic_secret,
size_t traffic_secret_len) {
const SSL_SESSION *session = SSL_get_session(ssl);
@ -142,36 +153,48 @@ bool tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
return false;
}
// Look up cipher suite properties.
const EVP_AEAD *aead;
size_t discard;
if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard, session->cipher,
version, SSL_is_dtls(ssl))) {
return false;
UniquePtr<SSLAEADContext> traffic_aead;
if (ssl->ctx->quic_method == nullptr) {
// Look up cipher suite properties.
const EVP_AEAD *aead;
size_t discard;
if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard, session->cipher,
version, SSL_is_dtls(ssl))) {
return false;
}
const EVP_MD *digest = ssl_session_get_digest(session);
// Derive the key.
size_t key_len = EVP_AEAD_key_length(aead);
uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len,
"key", 3, NULL, 0, key_len,
ssl->ctx->quic_method != nullptr)) {
return false;
}
// Derive the IV.
size_t iv_len = EVP_AEAD_nonce_length(aead);
uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH];
if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len, "iv",
2, NULL, 0, iv_len,
ssl->ctx->quic_method != nullptr)) {
return false;
}
traffic_aead = SSLAEADContext::Create(
direction, session->ssl_version, SSL_is_dtls(ssl), session->cipher,
MakeConstSpan(key, key_len), Span<const uint8_t>(),
MakeConstSpan(iv, iv_len));
} else {
// Install a placeholder SSLAEADContext so that SSL accessors work. The
// encryption itself will be handled by the SSL_QUIC_METHOD.
traffic_aead =
SSLAEADContext::CreatePlaceholderForQUIC(version, session->cipher);
}
const EVP_MD *digest = ssl_session_get_digest(session);
// Derive the key.
size_t key_len = EVP_AEAD_key_length(aead);
uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len, "key",
3, NULL, 0, key_len)) {
return false;
}
// Derive the IV.
size_t iv_len = EVP_AEAD_nonce_length(aead);
uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH];
if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len, "iv",
2, NULL, 0, iv_len)) {
return false;
}
UniquePtr<SSLAEADContext> traffic_aead =
SSLAEADContext::Create(direction, session->ssl_version, SSL_is_dtls(ssl),
session->cipher, MakeConstSpan(key, key_len),
Span<const uint8_t>(), MakeConstSpan(iv, iv_len));
if (!traffic_aead) {
return false;
}
@ -191,10 +214,12 @@ bool tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
OPENSSL_memmove(ssl->s3->read_traffic_secret, traffic_secret,
traffic_secret_len);
ssl->s3->read_traffic_secret_len = traffic_secret_len;
ssl->s3->read_level = level;
} else {
OPENSSL_memmove(ssl->s3->write_traffic_secret, traffic_secret,
traffic_secret_len);
ssl->s3->write_traffic_secret_len = traffic_secret_len;
ssl->s3->write_level = level;
}
return true;
@ -223,40 +248,103 @@ bool tls13_derive_early_secrets(SSL_HANDSHAKE *hs) {
return false;
}
ssl->s3->early_exporter_secret_len = hs->hash_len;
if (ssl->ctx->quic_method != nullptr) {
if (ssl->server) {
if (!ssl->ctx->quic_method->set_encryption_secrets(
ssl, ssl_encryption_early_data, nullptr, hs->early_traffic_secret,
hs->hash_len)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
return false;
}
} else {
if (!ssl->ctx->quic_method->set_encryption_secrets(
ssl, ssl_encryption_early_data, hs->early_traffic_secret, nullptr,
hs->hash_len)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
return false;
}
}
}
return true;
}
bool tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
return derive_secret(hs, hs->client_handshake_secret, hs->hash_len,
kTLS13LabelClientHandshakeTraffic,
strlen(kTLS13LabelClientHandshakeTraffic)) &&
ssl_log_secret(ssl, "CLIENT_HANDSHAKE_TRAFFIC_SECRET",
hs->client_handshake_secret, hs->hash_len) &&
derive_secret(hs, hs->server_handshake_secret, hs->hash_len,
kTLS13LabelServerHandshakeTraffic,
strlen(kTLS13LabelServerHandshakeTraffic)) &&
ssl_log_secret(ssl, "SERVER_HANDSHAKE_TRAFFIC_SECRET",
hs->server_handshake_secret, hs->hash_len);
if (!derive_secret(hs, hs->client_handshake_secret, hs->hash_len,
kTLS13LabelClientHandshakeTraffic,
strlen(kTLS13LabelClientHandshakeTraffic)) ||
!ssl_log_secret(ssl, "CLIENT_HANDSHAKE_TRAFFIC_SECRET",
hs->client_handshake_secret, hs->hash_len) ||
!derive_secret(hs, hs->server_handshake_secret, hs->hash_len,
kTLS13LabelServerHandshakeTraffic,
strlen(kTLS13LabelServerHandshakeTraffic)) ||
!ssl_log_secret(ssl, "SERVER_HANDSHAKE_TRAFFIC_SECRET",
hs->server_handshake_secret, hs->hash_len)) {
return false;
}
if (ssl->ctx->quic_method != nullptr) {
if (ssl->server) {
if (!ssl->ctx->quic_method->set_encryption_secrets(
ssl, ssl_encryption_handshake, hs->client_handshake_secret,
hs->server_handshake_secret, hs->hash_len)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
return false;
}
} else {
if (!ssl->ctx->quic_method->set_encryption_secrets(
ssl, ssl_encryption_handshake, hs->server_handshake_secret,
hs->client_handshake_secret, hs->hash_len)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
return false;
}
}
}
return true;
}
bool tls13_derive_application_secrets(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
ssl->s3->exporter_secret_len = hs->hash_len;
return derive_secret(hs, hs->client_traffic_secret_0, hs->hash_len,
kTLS13LabelClientApplicationTraffic,
strlen(kTLS13LabelClientApplicationTraffic)) &&
ssl_log_secret(ssl, "CLIENT_TRAFFIC_SECRET_0",
hs->client_traffic_secret_0, hs->hash_len) &&
derive_secret(hs, hs->server_traffic_secret_0, hs->hash_len,
kTLS13LabelServerApplicationTraffic,
strlen(kTLS13LabelServerApplicationTraffic)) &&
ssl_log_secret(ssl, "SERVER_TRAFFIC_SECRET_0",
hs->server_traffic_secret_0, hs->hash_len) &&
derive_secret(hs, ssl->s3->exporter_secret, hs->hash_len,
kTLS13LabelExporter, strlen(kTLS13LabelExporter)) &&
ssl_log_secret(ssl, "EXPORTER_SECRET", ssl->s3->exporter_secret,
hs->hash_len);
if (!derive_secret(hs, hs->client_traffic_secret_0, hs->hash_len,
kTLS13LabelClientApplicationTraffic,
strlen(kTLS13LabelClientApplicationTraffic)) ||
!ssl_log_secret(ssl, "CLIENT_TRAFFIC_SECRET_0",
hs->client_traffic_secret_0, hs->hash_len) ||
!derive_secret(hs, hs->server_traffic_secret_0, hs->hash_len,
kTLS13LabelServerApplicationTraffic,
strlen(kTLS13LabelServerApplicationTraffic)) ||
!ssl_log_secret(ssl, "SERVER_TRAFFIC_SECRET_0",
hs->server_traffic_secret_0, hs->hash_len) ||
!derive_secret(hs, ssl->s3->exporter_secret, hs->hash_len,
kTLS13LabelExporter, strlen(kTLS13LabelExporter)) ||
!ssl_log_secret(ssl, "EXPORTER_SECRET", ssl->s3->exporter_secret,
hs->hash_len)) {
return false;
}
if (ssl->ctx->quic_method != nullptr) {
if (ssl->server) {
if (!ssl->ctx->quic_method->set_encryption_secrets(
ssl, ssl_encryption_application, hs->client_traffic_secret_0,
hs->server_traffic_secret_0, hs->hash_len)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
return false;
}
} else {
if (!ssl->ctx->quic_method->set_encryption_secrets(
ssl, ssl_encryption_application, hs->server_traffic_secret_0,
hs->client_traffic_secret_0, hs->hash_len)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
return false;
}
}
}
return true;
}
static const char kTLS13LabelApplicationTraffic[] = "traffic upd";
@ -273,13 +361,15 @@ bool tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) {
}
const EVP_MD *digest = ssl_session_get_digest(SSL_get_session(ssl));
if (!hkdf_expand_label(
secret, digest, secret, secret_len, kTLS13LabelApplicationTraffic,
strlen(kTLS13LabelApplicationTraffic), NULL, 0, secret_len)) {
if (!hkdf_expand_label(secret, digest, secret, secret_len,
kTLS13LabelApplicationTraffic,
strlen(kTLS13LabelApplicationTraffic), NULL, 0,
secret_len, ssl->ctx->quic_method != nullptr)) {
return false;
}
return tls13_set_traffic_key(ssl, direction, secret, secret_len);
return tls13_set_traffic_key(ssl, ssl_encryption_application, direction,
secret, secret_len);
}
static const char kTLS13LabelResumption[] = "res master";
@ -302,11 +392,13 @@ static const char kTLS13LabelFinished[] = "finished";
static bool tls13_verify_data(const EVP_MD *digest, uint16_t version,
uint8_t *out, size_t *out_len,
const uint8_t *secret, size_t hash_len,
uint8_t *context, size_t context_len) {
uint8_t *context, size_t context_len,
bool use_quic) {
uint8_t key[EVP_MAX_MD_SIZE];
unsigned len;
if (!hkdf_expand_label(key, digest, secret, hash_len, kTLS13LabelFinished,
strlen(kTLS13LabelFinished), NULL, 0, hash_len) ||
strlen(kTLS13LabelFinished), NULL, 0, hash_len,
use_quic) ||
HMAC(digest, key, hash_len, context, context_len, out, &len) == NULL) {
return false;
}
@ -328,7 +420,8 @@ bool tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
if (!hs->transcript.GetHash(context_hash, &context_hash_len) ||
!tls13_verify_data(hs->transcript.Digest(), hs->ssl->version, out,
out_len, traffic_secret, hs->hash_len, context_hash,
context_hash_len)) {
context_hash_len,
hs->ssl->ctx->quic_method != nullptr)) {
return 0;
}
return 1;
@ -336,12 +429,13 @@ bool tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
static const char kTLS13LabelResumptionPSK[] = "resumption";
bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce) {
bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce,
bool use_quic) {
const EVP_MD *digest = ssl_session_get_digest(session);
return hkdf_expand_label(session->master_key, digest, session->master_key,
session->master_key_length, kTLS13LabelResumptionPSK,
strlen(kTLS13LabelResumptionPSK), nonce.data(),
nonce.size(), session->master_key_length);
nonce.size(), session->master_key_length, use_quic);
}
static const char kTLS13LabelExportKeying[] = "exporter";
@ -370,11 +464,12 @@ bool tls13_export_keying_material(SSL *ssl, Span<uint8_t> out,
nullptr) &&
hkdf_expand_label(derived_secret, digest, secret.data(), secret.size(),
label.data(), label.size(), export_context,
export_context_len, derived_secret_len) &&
export_context_len, derived_secret_len,
ssl->ctx->quic_method != nullptr) &&
hkdf_expand_label(out.data(), digest, derived_secret,
derived_secret_len, kTLS13LabelExportKeying,
strlen(kTLS13LabelExportKeying), hash, hash_len,
out.size());
out.size(), ssl->ctx->quic_method != nullptr);
}
static const char kTLS13LabelPSKBinder[] = "res binder";
@ -382,7 +477,7 @@ static const char kTLS13LabelPSKBinder[] = "res binder";
static bool tls13_psk_binder(uint8_t *out, uint16_t version,
const EVP_MD *digest, uint8_t *psk, size_t psk_len,
uint8_t *context, size_t context_len,
size_t hash_len) {
size_t hash_len, bool use_quic) {
uint8_t binder_context[EVP_MAX_MD_SIZE];
unsigned binder_context_len;
if (!EVP_Digest(NULL, 0, binder_context, &binder_context_len, digest, NULL)) {
@ -400,9 +495,10 @@ static bool tls13_psk_binder(uint8_t *out, uint16_t version,
size_t len;
if (!hkdf_expand_label(binder_key, digest, early_secret, hash_len,
kTLS13LabelPSKBinder, strlen(kTLS13LabelPSKBinder),
binder_context, binder_context_len, hash_len) ||
binder_context, binder_context_len, hash_len,
use_quic) ||
!tls13_verify_data(digest, version, out, &len, binder_key, hash_len,
context, context_len)) {
context, context_len, use_quic)) {
return false;
}
@ -435,7 +531,7 @@ bool tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len) {
if (!tls13_psk_binder(verify_data, ssl->session->ssl_version, digest,
ssl->session->master_key,
ssl->session->master_key_length, context, context_len,
hash_len)) {
hash_len, ssl->ctx->quic_method != nullptr)) {
return false;
}
@ -467,16 +563,16 @@ bool tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
CBS binder;
if (!tls13_psk_binder(verify_data, hs->ssl->version, hs->transcript.Digest(),
session->master_key, session->master_key_length,
context, context_len, hash_len) ||
context, context_len, hash_len,
hs->ssl->ctx->quic_method != nullptr) ||
// We only consider the first PSK, so compare against the first binder.
!CBS_get_u8_length_prefixed(binders, &binder)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return false;
}
bool binder_ok =
CBS_len(&binder) == hash_len &&
CRYPTO_memcmp(CBS_data(&binder), verify_data, hash_len) == 0;
bool binder_ok = CBS_len(&binder) == hash_len &&
CRYPTO_memcmp(CBS_data(&binder), verify_data, hash_len) == 0;
#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
binder_ok = true;
#endif

View File

@ -195,7 +195,8 @@ static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {
!CBB_add_u8_length_prefixed(&body, &nonce_cbb) ||
!CBB_add_bytes(&nonce_cbb, nonce, sizeof(nonce)) ||
!CBB_add_u16_length_prefixed(&body, &ticket) ||
!tls13_derive_session_psk(session.get(), nonce) ||
!tls13_derive_session_psk(session.get(), nonce,
ssl->ctx->quic_method != nullptr) ||
!ssl_encrypt_ticket(hs, &ticket, session.get()) ||
!CBB_add_u16_length_prefixed(&body, &extensions)) {
return false;
@ -586,8 +587,8 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
// Derive and enable the handshake traffic secrets.
if (!tls13_derive_handshake_secrets(hs) ||
!tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_handshake_secret,
hs->hash_len)) {
!tls13_set_traffic_key(ssl, ssl_encryption_handshake, evp_aead_seal,
hs->server_handshake_secret, hs->hash_len)) {
return ssl_hs_error;
}
@ -697,8 +698,8 @@ static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) {
// Update the secret to the master secret and derive traffic keys.
!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
!tls13_derive_application_secrets(hs) ||
!tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_traffic_secret_0,
hs->hash_len)) {
!tls13_set_traffic_key(ssl, ssl_encryption_application, evp_aead_seal,
hs->server_traffic_secret_0, hs->hash_len)) {
return ssl_hs_error;
}
@ -750,8 +751,8 @@ static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) {
static enum ssl_hs_wait_t do_read_second_client_flight(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
if (ssl->s3->early_data_accepted) {
if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->early_traffic_secret,
hs->hash_len)) {
if (!tls13_set_traffic_key(ssl, ssl_encryption_early_data, evp_aead_open,
hs->early_traffic_secret, hs->hash_len)) {
return ssl_hs_error;
}
hs->can_early_write = true;
@ -785,8 +786,8 @@ static enum ssl_hs_wait_t do_process_end_of_early_data(SSL_HANDSHAKE *hs) {
ssl->method->next_message(ssl);
}
}
if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->client_handshake_secret,
hs->hash_len)) {
if (!tls13_set_traffic_key(ssl, ssl_encryption_handshake, evp_aead_open,
hs->client_handshake_secret, hs->hash_len)) {
return ssl_hs_error;
}
hs->tls13_state = ssl->s3->early_data_accepted
@ -892,8 +893,8 @@ static enum ssl_hs_wait_t do_read_client_finished(SSL_HANDSHAKE *hs) {
// and derived the resumption secret.
!tls13_process_finished(hs, msg, ssl->s3->early_data_accepted) ||
// evp_aead_seal keys have already been switched.
!tls13_set_traffic_key(ssl, evp_aead_open, hs->client_traffic_secret_0,
hs->hash_len)) {
!tls13_set_traffic_key(ssl, ssl_encryption_application, evp_aead_open,
hs->client_traffic_secret_0, hs->hash_len)) {
return ssl_hs_error;
}