blob: dee634ba2cce9f2ba443c5968dbf2eb635d68340 [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 "quic/core/tls_handshaker.h"
#include "absl/base/macros.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "third_party/boringssl/src/include/openssl/crypto.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
#include "quic/core/quic_crypto_stream.h"
#include "quic/core/tls_client_handshaker.h"
#include "quic/platform/api/quic_bug_tracker.h"
#include "quic/platform/api/quic_stack_trace.h"
namespace quic {
#define ENDPOINT (SSL_is_server(ssl()) ? "TlsServer: " : "TlsClient: ")
TlsHandshaker::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
TlsHandshaker* parent)
: parent_(parent) {}
TlsHandshaker::ProofVerifierCallbackImpl::~ProofVerifierCallbackImpl() {}
void TlsHandshaker::ProofVerifierCallbackImpl::Run(
bool ok,
const std::string& /*error_details*/,
std::unique_ptr<ProofVerifyDetails>* details) {
if (parent_ == nullptr) {
return;
}
parent_->verify_details_ = std::move(*details);
parent_->verify_result_ = ok ? ssl_verify_ok : ssl_verify_invalid;
parent_->set_expected_ssl_error(SSL_ERROR_WANT_READ);
parent_->proof_verify_callback_ = nullptr;
if (parent_->verify_details_) {
parent_->OnProofVerifyDetailsAvailable(*parent_->verify_details_);
}
parent_->AdvanceHandshake();
}
void TlsHandshaker::ProofVerifierCallbackImpl::Cancel() {
parent_ = nullptr;
}
TlsHandshaker::TlsHandshaker(QuicCryptoStream* stream, QuicSession* session)
: stream_(stream), handshaker_delegate_(session) {}
TlsHandshaker::~TlsHandshaker() {
if (proof_verify_callback_) {
proof_verify_callback_->Cancel();
}
}
bool TlsHandshaker::ProcessInput(absl::string_view input,
EncryptionLevel level) {
if (parser_error_ != QUIC_NO_ERROR) {
return false;
}
// 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?
if (SSL_provide_quic_data(ssl(), TlsConnection::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();
return true;
}
void TlsHandshaker::AdvanceHandshake() {
if (is_connection_closed_) {
return;
}
if (GetHandshakeState() >= HANDSHAKE_COMPLETE) {
ProcessPostHandshakeMessage();
return;
}
QUICHE_BUG_IF(
quic_tls_server_async_done_no_flusher,
SSL_is_server(ssl()) && !handshaker_delegate_->PacketFlusherAttached())
<< "is_server:" << SSL_is_server(ssl());
QUIC_VLOG(1) << ENDPOINT << "Continuing handshake";
int rv = SSL_do_handshake(ssl());
// If SSL_do_handshake return success(1) and we are in early data, it is
// possible that we have provided ServerHello to BoringSSL but it hasn't been
// processed. Retry SSL_do_handshake once will advance the handshake more in
// that case. If there are no unprocessed ServerHello, the retry will return a
// non-positive number.
if (rv == 1 && SSL_in_early_data(ssl())) {
OnEnterEarlyData();
rv = SSL_do_handshake(ssl());
QUIC_VLOG(1) << ENDPOINT
<< "SSL_do_handshake returned when entering early data. After "
<< "retry, rv=" << rv
<< ", SSL_in_early_data=" << SSL_in_early_data(ssl());
// The retry should either
// - Return <= 0 if the handshake is still pending, likely still in early
// data.
// - Return 1 if the handshake has _actually_ finished. i.e.
// SSL_in_early_data should be false.
//
// In either case, it should not both return 1 and stay in early data.
if (rv == 1 && SSL_in_early_data(ssl()) && !is_connection_closed_) {
QUIC_BUG(quic_handshaker_stay_in_early_data)
<< "The original and the retry of SSL_do_handshake both returned "
"success and in early data";
CloseConnection(QUIC_HANDSHAKE_FAILED,
"TLS handshake failed: Still in early data after retry");
return;
}
}
if (rv == 1) {
FinishHandshake();
return;
}
int ssl_error = SSL_get_error(ssl(), rv);
if (ssl_error == expected_ssl_error_) {
return;
}
if (ShouldCloseConnectionOnUnexpectedError(ssl_error) &&
!is_connection_closed_) {
QUIC_VLOG(1) << "SSL_do_handshake failed; SSL_get_error returns "
<< ssl_error;
ERR_print_errors_fp(stderr);
CloseConnection(QUIC_HANDSHAKE_FAILED, "TLS handshake failed");
}
}
void TlsHandshaker::CloseConnection(QuicErrorCode error,
const std::string& reason_phrase) {
QUICHE_DCHECK(!reason_phrase.empty());
stream()->OnUnrecoverableError(error, reason_phrase);
is_connection_closed_ = true;
}
void TlsHandshaker::CloseConnection(QuicErrorCode error,
QuicIetfTransportErrorCodes ietf_error,
const std::string& reason_phrase) {
QUICHE_DCHECK(!reason_phrase.empty());
stream()->OnUnrecoverableError(error, ietf_error, reason_phrase);
is_connection_closed_ = true;
}
void TlsHandshaker::OnConnectionClosed(QuicErrorCode /*error*/,
ConnectionCloseSource /*source*/) {
is_connection_closed_ = true;
}
bool TlsHandshaker::ShouldCloseConnectionOnUnexpectedError(int /*ssl_error*/) {
return true;
}
size_t TlsHandshaker::BufferSizeLimitForLevel(EncryptionLevel level) const {
return SSL_quic_max_handshake_flight_len(
ssl(), TlsConnection::BoringEncryptionLevel(level));
}
ssl_early_data_reason_t TlsHandshaker::EarlyDataReason() const {
return SSL_get_early_data_reason(ssl());
}
const EVP_MD* TlsHandshaker::Prf(const SSL_CIPHER* cipher) {
return EVP_get_digestbynid(SSL_CIPHER_get_prf_nid(cipher));
}
enum ssl_verify_result_t TlsHandshaker::VerifyCert(uint8_t* out_alert) {
if (verify_result_ != ssl_verify_retry ||
expected_ssl_error() == SSL_ERROR_WANT_CERTIFICATE_VERIFY) {
enum ssl_verify_result_t result = verify_result_;
verify_result_ = ssl_verify_retry;
*out_alert = cert_verify_tls_alert_;
return result;
}
const STACK_OF(CRYPTO_BUFFER)* cert_chain = SSL_get0_peer_certificates(ssl());
if (cert_chain == nullptr) {
*out_alert = SSL_AD_INTERNAL_ERROR;
return ssl_verify_invalid;
}
// TODO(nharper): Pass the CRYPTO_BUFFERs into the QUIC stack to avoid copies.
std::vector<std::string> certs;
for (CRYPTO_BUFFER* cert : cert_chain) {
certs.push_back(
std::string(reinterpret_cast<const char*>(CRYPTO_BUFFER_data(cert)),
CRYPTO_BUFFER_len(cert)));
}
ProofVerifierCallbackImpl* proof_verify_callback =
new ProofVerifierCallbackImpl(this);
cert_verify_tls_alert_ = *out_alert;
QuicAsyncStatus verify_result = VerifyCertChain(
certs, &cert_verify_error_details_, &verify_details_,
&cert_verify_tls_alert_,
std::unique_ptr<ProofVerifierCallback>(proof_verify_callback));
switch (verify_result) {
case QUIC_SUCCESS:
if (verify_details_) {
OnProofVerifyDetailsAvailable(*verify_details_);
}
return ssl_verify_ok;
case QUIC_PENDING:
proof_verify_callback_ = proof_verify_callback;
set_expected_ssl_error(SSL_ERROR_WANT_CERTIFICATE_VERIFY);
return ssl_verify_retry;
case QUIC_FAILURE:
default:
*out_alert = cert_verify_tls_alert_;
QUIC_LOG(INFO) << "Cert chain verification failed: "
<< cert_verify_error_details_;
return ssl_verify_invalid;
}
}
void TlsHandshaker::SetWriteSecret(EncryptionLevel level,
const SSL_CIPHER* cipher,
const std::vector<uint8_t>& write_secret) {
QUIC_DVLOG(1) << ENDPOINT << "SetWriteSecret level=" << level;
std::unique_ptr<QuicEncrypter> encrypter =
QuicEncrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher));
const EVP_MD* prf = Prf(cipher);
CryptoUtils::SetKeyAndIV(prf, write_secret, encrypter.get());
std::vector<uint8_t> header_protection_key =
CryptoUtils::GenerateHeaderProtectionKey(prf, write_secret,
encrypter->GetKeySize());
encrypter->SetHeaderProtectionKey(
absl::string_view(reinterpret_cast<char*>(header_protection_key.data()),
header_protection_key.size()));
if (level == ENCRYPTION_FORWARD_SECURE) {
QUICHE_DCHECK(latest_write_secret_.empty());
latest_write_secret_ = write_secret;
one_rtt_write_header_protection_key_ = header_protection_key;
}
handshaker_delegate_->OnNewEncryptionKeyAvailable(level,
std::move(encrypter));
}
bool TlsHandshaker::SetReadSecret(EncryptionLevel level,
const SSL_CIPHER* cipher,
const std::vector<uint8_t>& read_secret) {
QUIC_DVLOG(1) << ENDPOINT << "SetReadSecret level=" << level;
std::unique_ptr<QuicDecrypter> decrypter =
QuicDecrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher));
const EVP_MD* prf = Prf(cipher);
CryptoUtils::SetKeyAndIV(prf, read_secret, decrypter.get());
std::vector<uint8_t> header_protection_key =
CryptoUtils::GenerateHeaderProtectionKey(prf, read_secret,
decrypter->GetKeySize());
decrypter->SetHeaderProtectionKey(
absl::string_view(reinterpret_cast<char*>(header_protection_key.data()),
header_protection_key.size()));
if (level == ENCRYPTION_FORWARD_SECURE) {
QUICHE_DCHECK(latest_read_secret_.empty());
latest_read_secret_ = read_secret;
one_rtt_read_header_protection_key_ = header_protection_key;
}
return handshaker_delegate_->OnNewDecryptionKeyAvailable(
level, std::move(decrypter),
/*set_alternative_decrypter=*/false,
/*latch_once_used=*/false);
}
std::unique_ptr<QuicDecrypter>
TlsHandshaker::AdvanceKeysAndCreateCurrentOneRttDecrypter() {
if (latest_read_secret_.empty() || latest_write_secret_.empty() ||
one_rtt_read_header_protection_key_.empty() ||
one_rtt_write_header_protection_key_.empty()) {
std::string error_details = "1-RTT secret(s) not set yet.";
QUIC_BUG(quic_bug_10312_1) << error_details;
CloseConnection(QUIC_INTERNAL_ERROR, error_details);
return nullptr;
}
const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl());
const EVP_MD* prf = Prf(cipher);
latest_read_secret_ =
CryptoUtils::GenerateNextKeyPhaseSecret(prf, latest_read_secret_);
latest_write_secret_ =
CryptoUtils::GenerateNextKeyPhaseSecret(prf, latest_write_secret_);
std::unique_ptr<QuicDecrypter> decrypter =
QuicDecrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher));
CryptoUtils::SetKeyAndIV(prf, latest_read_secret_, decrypter.get());
decrypter->SetHeaderProtectionKey(absl::string_view(
reinterpret_cast<char*>(one_rtt_read_header_protection_key_.data()),
one_rtt_read_header_protection_key_.size()));
return decrypter;
}
std::unique_ptr<QuicEncrypter> TlsHandshaker::CreateCurrentOneRttEncrypter() {
if (latest_write_secret_.empty() ||
one_rtt_write_header_protection_key_.empty()) {
std::string error_details = "1-RTT write secret not set yet.";
QUIC_BUG(quic_bug_10312_2) << error_details;
CloseConnection(QUIC_INTERNAL_ERROR, error_details);
return nullptr;
}
const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl());
std::unique_ptr<QuicEncrypter> encrypter =
QuicEncrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher));
CryptoUtils::SetKeyAndIV(Prf(cipher), latest_write_secret_, encrypter.get());
encrypter->SetHeaderProtectionKey(absl::string_view(
reinterpret_cast<char*>(one_rtt_write_header_protection_key_.data()),
one_rtt_write_header_protection_key_.size()));
return encrypter;
}
bool TlsHandshaker::ExportKeyingMaterialForLabel(absl::string_view label,
absl::string_view context,
size_t result_len,
std::string* result) {
// TODO(haoyuewang) Adding support of keying material export when 0-RTT is
// accepted.
if (SSL_in_init(ssl())) {
return false;
}
result->resize(result_len);
return SSL_export_keying_material(
ssl(), reinterpret_cast<uint8_t*>(&*result->begin()), result_len,
label.data(), label.size(),
reinterpret_cast<const uint8_t*>(context.data()), context.size(),
!context.empty()) == 1;
}
void TlsHandshaker::WriteMessage(EncryptionLevel level,
absl::string_view data) {
stream_->WriteCryptoData(level, data);
}
void TlsHandshaker::FlushFlight() {}
void TlsHandshaker::SendAlert(EncryptionLevel level, uint8_t desc) {
std::string error_details = absl::StrCat(
"TLS handshake failure (", EncryptionLevelToString(level), ") ",
static_cast<int>(desc), ": ", SSL_alert_desc_string_long(desc));
QUIC_DLOG(ERROR) << error_details;
CloseConnection(
TlsAlertToQuicErrorCode(desc),
static_cast<QuicIetfTransportErrorCodes>(CRYPTO_ERROR_FIRST + desc),
error_details);
}
} // namespace quic