|  | // 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))); | 
|  | } | 
|  | QUIC_DVLOG(1) << "VerifyCert: peer cert_chain length: " << certs.size(); | 
|  |  | 
|  | 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 |