| // 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 "quiche/quic/core/tls_handshaker.h" | |
| #include "absl/base/macros.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "openssl/crypto.h" | |
| #include "openssl/ssl.h" | |
| #include "quiche/quic/core/quic_crypto_stream.h" | |
| #include "quiche/quic/core/tls_client_handshaker.h" | |
| #include "quiche/quic/platform/api/quic_bug_tracker.h" | |
| #include "quiche/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 (is_connection_closed()) { | |
| return; | |
| } | |
| // 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()); | |
| if (is_connection_closed()) { | |
| return; | |
| } | |
| 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, | |
| absl::Span<const 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, | |
| handshaker_delegate_->parsed_version(), | |
| encrypter.get()); | |
| std::vector<uint8_t> header_protection_key = | |
| CryptoUtils::GenerateHeaderProtectionKey( | |
| prf, write_secret, handshaker_delegate_->parsed_version(), | |
| 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_.assign(write_secret.begin(), write_secret.end()); | |
| 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, | |
| absl::Span<const 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, | |
| handshaker_delegate_->parsed_version(), | |
| decrypter.get()); | |
| std::vector<uint8_t> header_protection_key = | |
| CryptoUtils::GenerateHeaderProtectionKey( | |
| prf, read_secret, handshaker_delegate_->parsed_version(), | |
| 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_.assign(read_secret.begin(), read_secret.end()); | |
| 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, handshaker_delegate_->parsed_version(), latest_read_secret_); | |
| latest_write_secret_ = CryptoUtils::GenerateNextKeyPhaseSecret( | |
| prf, handshaker_delegate_->parsed_version(), latest_write_secret_); | |
| std::unique_ptr<QuicDecrypter> decrypter = | |
| QuicDecrypter::CreateFromCipherSuite(SSL_CIPHER_get_id(cipher)); | |
| CryptoUtils::SetKeyAndIV(prf, latest_read_secret_, | |
| handshaker_delegate_->parsed_version(), | |
| 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_, | |
| handshaker_delegate_->parsed_version(), | |
| 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) { | |
| if (result == nullptr) { | |
| 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 |