blob: eeaef17afcf33771fe65d9883d85382515fbe42b [file] [log] [blame]
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/third_party/quic/core/tls_handshaker.h"
#include "net/third_party/quic/core/quic_crypto_stream.h"
#include "net/third_party/quic/core/tls_client_handshaker.h"
#include "net/third_party/quic/platform/api/quic_arraysize.h"
#include "net/third_party/quic/platform/api/quic_bug_tracker.h"
#include "starboard/memory.h"
#include "third_party/boringssl/src/include/openssl/crypto.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
namespace quic {
namespace {
class SslIndexSingleton {
public:
static SslIndexSingleton* GetInstance() {
static SslIndexSingleton* instance = new SslIndexSingleton();
return instance;
}
int HandshakerIndex() const { return ssl_ex_data_index_handshaker_; }
private:
SslIndexSingleton() {
CRYPTO_library_init();
ssl_ex_data_index_handshaker_ =
SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
CHECK_LE(0, ssl_ex_data_index_handshaker_);
}
SslIndexSingleton(const SslIndexSingleton&) = delete;
SslIndexSingleton& operator=(const SslIndexSingleton&) = delete;
int ssl_ex_data_index_handshaker_;
};
} // namespace
TlsHandshaker::TlsHandshaker(QuicCryptoStream* stream,
QuicSession* session,
SSL_CTX* ssl_ctx)
: stream_(stream), session_(session) {
ssl_.reset(SSL_new(ssl_ctx));
SSL_set_ex_data(ssl(), SslIndexSingleton::GetInstance()->HandshakerIndex(),
this);
}
TlsHandshaker::~TlsHandshaker() {}
bool TlsHandshaker::ProcessInput(QuicStringPiece input, EncryptionLevel level) {
if (parser_error_ != QUIC_NO_ERROR) {
return false;
}
#if defined(COBALT_QUIC46)
NOTREACHED();
#else
TODO(nharper): Call SSL_quic_read_level(ssl()) and check whether the
encryption level BoringSSL expects matches the encryption level that we
just received input at. If they mismatch, should ProcessInput return true
or false? If data is for a future encryption level, it should be queued for
later?
NOTREACHED();
if (SSL_provide_quic_data(ssl(), BoringEncryptionLevel(level),
reinterpret_cast<const uint8_t*>(input.data()),
input.size()) != 1) {
// SSL_provide_quic_data can fail for 3 reasons:
// - API misuse (calling it before SSL_set_custom_quic_method, which we
// call in the TlsHandshaker c'tor)
// - Memory exhaustion when appending data to its buffer
// - Data provided at the wrong encryption level
//
// Of these, the only sensible error to handle is data provided at the wrong
// encryption level.
//
// Note: the error provided below has a good-sounding enum value, although
// it doesn't match the description as it's a QUIC Crypto specific error.
parser_error_ = QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
parser_error_detail_ = "TLS stack failed to receive data";
return false;
}
AdvanceHandshake();
#endif
return true;
}
// static
bssl::UniquePtr<SSL_CTX> TlsHandshaker::CreateSslCtx() {
CRYPTO_library_init();
bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_with_buffers_method()));
SSL_CTX_set_min_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION);
#if !defined(COBALT_QUIC46)
SSL_CTX_set_quic_method(ssl_ctx.get(), &kSslQuicMethod);
#endif
return ssl_ctx;
}
// static
TlsHandshaker* TlsHandshaker::HandshakerFromSsl(const SSL* ssl) {
return reinterpret_cast<TlsHandshaker*>(SSL_get_ex_data(
ssl, SslIndexSingleton::GetInstance()->HandshakerIndex()));
}
// static
EncryptionLevel TlsHandshaker::QuicEncryptionLevel(
enum ssl_encryption_level_t level) {
switch (level) {
case ssl_encryption_initial:
return ENCRYPTION_NONE;
case ssl_encryption_early_data:
case ssl_encryption_handshake:
return ENCRYPTION_ZERO_RTT;
case ssl_encryption_application:
return ENCRYPTION_FORWARD_SECURE;
#if defined(STARBOARD)
default:
NOTREACHED();
return ENCRYPTION_NONE;
#endif
}
}
// static
enum ssl_encryption_level_t TlsHandshaker::BoringEncryptionLevel(
EncryptionLevel level) {
switch (level) {
case ENCRYPTION_NONE:
return ssl_encryption_initial;
case ENCRYPTION_ZERO_RTT:
return ssl_encryption_handshake;
case ENCRYPTION_FORWARD_SECURE:
return ssl_encryption_application;
default:
QUIC_BUG << "Invalid encryption level " << level;
return ssl_encryption_initial;
}
}
const EVP_MD* TlsHandshaker::Prf() {
return EVP_get_digestbynid(
SSL_CIPHER_get_prf_nid(SSL_get_pending_cipher(ssl())));
}
std::unique_ptr<QuicEncrypter> TlsHandshaker::CreateEncrypter(
const std::vector<uint8_t>& pp_secret) {
std::unique_ptr<QuicEncrypter> encrypter =
QuicEncrypter::CreateFromCipherSuite(
SSL_CIPHER_get_id(SSL_get_pending_cipher(ssl())));
CryptoUtils::SetKeyAndIV(Prf(), pp_secret, encrypter.get());
return encrypter;
}
std::unique_ptr<QuicDecrypter> TlsHandshaker::CreateDecrypter(
const std::vector<uint8_t>& pp_secret) {
std::unique_ptr<QuicDecrypter> decrypter =
QuicDecrypter::CreateFromCipherSuite(
SSL_CIPHER_get_id(SSL_get_pending_cipher(ssl())));
CryptoUtils::SetKeyAndIV(Prf(), pp_secret, decrypter.get());
return decrypter;
}
#if !defined(COBALT_QUIC46)
const SSL_QUIC_METHOD TlsHandshaker::kSslQuicMethod{
TlsHandshaker::SetEncryptionSecretCallback,
TlsHandshaker::WriteMessageCallback, TlsHandshaker::FlushFlightCallback,
TlsHandshaker::SendAlertCallback};
#endif
// static
int TlsHandshaker::SetEncryptionSecretCallback(
SSL* ssl,
enum ssl_encryption_level_t level,
const uint8_t* read_key,
const uint8_t* write_key,
size_t secret_len) {
// TODO(nharper): replace these vectors and memcpys with spans (which
// unfortunately doesn't yet exist in quic/platform/api).
std::vector<uint8_t> read_secret(secret_len), write_secret(secret_len);
memcpy(read_secret.data(), read_key, secret_len);
memcpy(write_secret.data(), write_key, secret_len);
HandshakerFromSsl(ssl)->SetEncryptionSecret(QuicEncryptionLevel(level),
read_secret, write_secret);
return 1;
}
// static
int TlsHandshaker::WriteMessageCallback(SSL* ssl,
enum ssl_encryption_level_t level,
const uint8_t* data,
size_t len) {
HandshakerFromSsl(ssl)->WriteMessage(
QuicEncryptionLevel(level),
QuicStringPiece(reinterpret_cast<const char*>(data), len));
return 1;
}
// static
int TlsHandshaker::FlushFlightCallback(SSL* ssl) {
HandshakerFromSsl(ssl)->FlushFlight();
return 1;
}
// static
int TlsHandshaker::SendAlertCallback(SSL* ssl,
enum ssl_encryption_level_t level,
uint8_t desc) {
HandshakerFromSsl(ssl)->SendAlert(QuicEncryptionLevel(level), desc);
return 1;
}
void TlsHandshaker::SetEncryptionSecret(
EncryptionLevel level,
const std::vector<uint8_t>& read_secret,
const std::vector<uint8_t>& write_secret) {
std::unique_ptr<QuicEncrypter> encrypter = CreateEncrypter(write_secret);
session()->connection()->SetEncrypter(level, std::move(encrypter));
if (level != ENCRYPTION_FORWARD_SECURE) {
std::unique_ptr<QuicDecrypter> decrypter = CreateDecrypter(read_secret);
session()->connection()->SetDecrypter(level, std::move(decrypter));
} else {
// When forward-secure read keys are available, they get set as the
// alternative decrypter instead of the primary decrypter. One reason for
// this is that after the forward secure keys become available, the server
// still has crypto handshake messages to read at the handshake encryption
// level, meaning that both the ENCRYPTION_ZERO_RTT and
// ENCRYPTION_FORWARD_SECURE decrypters need to be available. (Tests also
// assume that an alternative decrypter gets set, so at some point we need
// to call SetAlternativeDecrypter.)
std::unique_ptr<QuicDecrypter> decrypter = CreateDecrypter(read_secret);
session()->connection()->SetAlternativeDecrypter(
level, std::move(decrypter), /*latch_once_used*/ true);
}
}
void TlsHandshaker::WriteMessage(EncryptionLevel level, QuicStringPiece data) {
stream_->WriteCryptoData(level, data);
}
void TlsHandshaker::FlushFlight() {}
void TlsHandshaker::SendAlert(EncryptionLevel level, uint8_t desc) {
// TODO(nharper): Alerts should be sent on the wire as a 16-bit QUIC error
// code computed to be 0x100 | desc (draft-ietf-quic-tls-14, section 4.8).
// This puts it in the range reserved for CRYPTO_ERROR
// (draft-ietf-quic-transport-14, section 11.3). However, according to
// quic_error_codes.h, this QUIC implementation only sends 1-byte error codes
// right now.
CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failure");
}
} // namespace quic