| // 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 |