summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quiche/quic/core/tls_server_handshaker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/third_party/quiche/src/quiche/quic/core/tls_server_handshaker.cc')
-rw-r--r--chromium/net/third_party/quiche/src/quiche/quic/core/tls_server_handshaker.cc1137
1 files changed, 1137 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/tls_server_handshaker.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/tls_server_handshaker.cc
new file mode 100644
index 00000000000..ce63767bad3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/quic/core/tls_server_handshaker.cc
@@ -0,0 +1,1137 @@
+// 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_server_handshaker.h"
+
+#include <memory>
+#include <string>
+
+#include "absl/base/macros.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "openssl/pool.h"
+#include "openssl/ssl.h"
+#include "quiche/quic/core/crypto/quic_crypto_server_config.h"
+#include "quiche/quic/core/crypto/transport_parameters.h"
+#include "quiche/quic/core/http/http_encoder.h"
+#include "quiche/quic/core/http/http_frames.h"
+#include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/quic/platform/api/quic_flag_utils.h"
+#include "quiche/quic/platform/api/quic_flags.h"
+#include "quiche/quic/platform/api/quic_hostname_utils.h"
+#include "quiche/quic/platform/api/quic_logging.h"
+#include "quiche/quic/platform/api/quic_server_stats.h"
+
+#define RECORD_LATENCY_IN_US(stat_name, latency, comment) \
+ do { \
+ const int64_t latency_in_us = (latency).ToMicroseconds(); \
+ QUIC_DVLOG(1) << "Recording " stat_name ": " << latency_in_us; \
+ QUIC_SERVER_HISTOGRAM_COUNTS(stat_name, latency_in_us, 1, 10000000, 50, \
+ comment); \
+ } while (0)
+
+namespace quic {
+
+namespace {
+
+// Default port for HTTP/3.
+uint16_t kDefaultPort = 443;
+
+} // namespace
+
+TlsServerHandshaker::DefaultProofSourceHandle::DefaultProofSourceHandle(
+ TlsServerHandshaker* handshaker, ProofSource* proof_source)
+ : handshaker_(handshaker), proof_source_(proof_source) {}
+
+TlsServerHandshaker::DefaultProofSourceHandle::~DefaultProofSourceHandle() {
+ CloseHandle();
+}
+
+void TlsServerHandshaker::DefaultProofSourceHandle::CloseHandle() {
+ QUIC_DVLOG(1) << "CloseHandle. is_signature_pending="
+ << (signature_callback_ != nullptr);
+ if (signature_callback_) {
+ signature_callback_->Cancel();
+ signature_callback_ = nullptr;
+ }
+}
+
+QuicAsyncStatus
+TlsServerHandshaker::DefaultProofSourceHandle::SelectCertificate(
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address,
+ absl::string_view /*ssl_capabilities*/, const std::string& hostname,
+ absl::string_view /*client_hello*/, const std::string& /*alpn*/,
+ absl::optional<std::string> /*alps*/,
+ const std::vector<uint8_t>& /*quic_transport_params*/,
+ const absl::optional<std::vector<uint8_t>>& /*early_data_context*/,
+ const QuicSSLConfig& /*ssl_config*/) {
+ if (!handshaker_ || !proof_source_) {
+ QUIC_BUG(quic_bug_10341_1)
+ << "SelectCertificate called on a detached handle";
+ return QUIC_FAILURE;
+ }
+
+ bool cert_matched_sni;
+ quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain =
+ proof_source_->GetCertChain(server_address, client_address, hostname,
+ &cert_matched_sni);
+
+ handshaker_->OnSelectCertificateDone(
+ /*ok=*/true, /*is_sync=*/true, chain.get(),
+ /*handshake_hints=*/absl::string_view(),
+ /*ticket_encryption_key=*/absl::string_view(), cert_matched_sni,
+ QuicDelayedSSLConfig());
+ if (!handshaker_->select_cert_status().has_value()) {
+ QUIC_BUG(quic_bug_12423_1)
+ << "select_cert_status() has no value after a synchronous select cert";
+ // Return success to continue the handshake.
+ return QUIC_SUCCESS;
+ }
+ return handshaker_->select_cert_status().value();
+}
+
+QuicAsyncStatus TlsServerHandshaker::DefaultProofSourceHandle::ComputeSignature(
+ const QuicSocketAddress& server_address,
+ const QuicSocketAddress& client_address, const std::string& hostname,
+ uint16_t signature_algorithm, absl::string_view in,
+ size_t max_signature_size) {
+ if (!handshaker_ || !proof_source_) {
+ QUIC_BUG(quic_bug_10341_2)
+ << "ComputeSignature called on a detached handle";
+ return QUIC_FAILURE;
+ }
+
+ if (signature_callback_) {
+ QUIC_BUG(quic_bug_10341_3) << "ComputeSignature called while pending";
+ return QUIC_FAILURE;
+ }
+
+ signature_callback_ = new DefaultSignatureCallback(this);
+ proof_source_->ComputeTlsSignature(
+ server_address, client_address, hostname, signature_algorithm, in,
+ std::unique_ptr<DefaultSignatureCallback>(signature_callback_));
+
+ if (signature_callback_) {
+ QUIC_DVLOG(1) << "ComputeTlsSignature is pending";
+ signature_callback_->set_is_sync(false);
+ return QUIC_PENDING;
+ }
+
+ bool success = handshaker_->HasValidSignature(max_signature_size);
+ QUIC_DVLOG(1) << "ComputeTlsSignature completed synchronously. success:"
+ << success;
+ // OnComputeSignatureDone should have been called by signature_callback_->Run.
+ return success ? QUIC_SUCCESS : QUIC_FAILURE;
+}
+
+TlsServerHandshaker::DecryptCallback::DecryptCallback(
+ TlsServerHandshaker* handshaker)
+ : handshaker_(handshaker) {}
+
+void TlsServerHandshaker::DecryptCallback::Run(std::vector<uint8_t> plaintext) {
+ if (handshaker_ == nullptr) {
+ // The callback was cancelled before we could run.
+ return;
+ }
+
+ TlsServerHandshaker* handshaker = handshaker_;
+ handshaker_ = nullptr;
+
+ handshaker->decrypted_session_ticket_ = std::move(plaintext);
+ const bool is_async =
+ (handshaker->expected_ssl_error() == SSL_ERROR_PENDING_TICKET);
+
+ absl::optional<QuicConnectionContextSwitcher> context_switcher;
+
+ if (is_async) {
+ context_switcher.emplace(handshaker->connection_context());
+ }
+ QUIC_TRACESTRING(
+ absl::StrCat("TLS ticket decryption done. len(decrypted_ticket):",
+ handshaker->decrypted_session_ticket_.size()));
+
+ // DecryptCallback::Run could be called synchronously. When that happens, we
+ // are currently in the middle of a call to AdvanceHandshake.
+ // (AdvanceHandshake called SSL_do_handshake, which through some layers
+ // called SessionTicketOpen, which called TicketCrypter::Decrypt, which
+ // synchronously called this function.) In that case, the handshake will
+ // continue to be processed when this function returns.
+ //
+ // When this callback is called asynchronously (i.e. the ticket decryption
+ // is pending), TlsServerHandshaker is not actively processing handshake
+ // messages. We need to have it resume processing handshake messages by
+ // calling AdvanceHandshake.
+ if (is_async) {
+ handshaker->AdvanceHandshakeFromCallback();
+ }
+
+ handshaker->ticket_decryption_callback_ = nullptr;
+}
+
+void TlsServerHandshaker::DecryptCallback::Cancel() {
+ QUICHE_DCHECK(handshaker_);
+ handshaker_ = nullptr;
+}
+
+TlsServerHandshaker::TlsServerHandshaker(
+ QuicSession* session, const QuicCryptoServerConfig* crypto_config)
+ : TlsHandshaker(this, session),
+ QuicCryptoServerStreamBase(session),
+ proof_source_(crypto_config->proof_source()),
+ pre_shared_key_(crypto_config->pre_shared_key()),
+ crypto_negotiated_params_(new QuicCryptoNegotiatedParameters),
+ tls_connection_(crypto_config->ssl_ctx(), this, session->GetSSLConfig()),
+ crypto_config_(crypto_config) {
+ QUIC_DVLOG(1) << "TlsServerHandshaker: support_client_cert:"
+ << session->support_client_cert()
+ << ", client_cert_mode initial value: " << client_cert_mode();
+
+ QUICHE_DCHECK_EQ(PROTOCOL_TLS1_3,
+ session->connection()->version().handshake_protocol);
+
+ // Configure the SSL to be a server.
+ SSL_set_accept_state(ssl());
+
+ // Make sure we use the right TLS extension codepoint.
+ int use_legacy_extension = 0;
+ if (session->version().UsesLegacyTlsExtension()) {
+ use_legacy_extension = 1;
+ }
+ SSL_set_quic_use_legacy_codepoint(ssl(), use_legacy_extension);
+
+ if (session->connection()->context()->tracer) {
+ tls_connection_.EnableInfoCallback();
+ }
+}
+
+TlsServerHandshaker::~TlsServerHandshaker() { CancelOutstandingCallbacks(); }
+
+void TlsServerHandshaker::CancelOutstandingCallbacks() {
+ if (proof_source_handle_) {
+ proof_source_handle_->CloseHandle();
+ }
+ if (ticket_decryption_callback_) {
+ ticket_decryption_callback_->Cancel();
+ ticket_decryption_callback_ = nullptr;
+ }
+}
+
+void TlsServerHandshaker::InfoCallback(int type, int value) {
+ QuicConnectionTracer* tracer =
+ session()->connection()->context()->tracer.get();
+
+ if (tracer == nullptr) {
+ return;
+ }
+
+ if (type & SSL_CB_LOOP) {
+ tracer->PrintString(
+ absl::StrCat("SSL:ACCEPT_LOOP:", SSL_state_string_long(ssl())));
+ } else if (type & SSL_CB_ALERT) {
+ const char* prefix =
+ (type & SSL_CB_READ) ? "SSL:READ_ALERT:" : "SSL:WRITE_ALERT:";
+ tracer->PrintString(absl::StrCat(prefix, SSL_alert_type_string_long(value),
+ ":", SSL_alert_desc_string_long(value)));
+ } else if (type & SSL_CB_EXIT) {
+ const char* prefix =
+ (value == 1) ? "SSL:ACCEPT_EXIT_OK:" : "SSL:ACCEPT_EXIT_FAIL:";
+ tracer->PrintString(absl::StrCat(prefix, SSL_state_string_long(ssl())));
+ } else if (type & SSL_CB_HANDSHAKE_START) {
+ tracer->PrintString(
+ absl::StrCat("SSL:HANDSHAKE_START:", SSL_state_string_long(ssl())));
+ } else if (type & SSL_CB_HANDSHAKE_DONE) {
+ tracer->PrintString(
+ absl::StrCat("SSL:HANDSHAKE_DONE:", SSL_state_string_long(ssl())));
+ } else {
+ QUIC_DLOG(INFO) << "Unknown event type " << type << ": "
+ << SSL_state_string_long(ssl());
+ tracer->PrintString(
+ absl::StrCat("SSL:unknown:", value, ":", SSL_state_string_long(ssl())));
+ }
+}
+
+std::unique_ptr<ProofSourceHandle>
+TlsServerHandshaker::MaybeCreateProofSourceHandle() {
+ return std::make_unique<DefaultProofSourceHandle>(this, proof_source_);
+}
+
+bool TlsServerHandshaker::GetBase64SHA256ClientChannelID(
+ std::string* /*output*/) const {
+ // Channel ID is not supported when TLS is used in QUIC.
+ return false;
+}
+
+void TlsServerHandshaker::SendServerConfigUpdate(
+ const CachedNetworkParameters* /*cached_network_params*/) {
+ // SCUP messages aren't supported when using the TLS handshake.
+}
+
+bool TlsServerHandshaker::DisableResumption() {
+ if (!can_disable_resumption_ || !session()->connection()->connected()) {
+ return false;
+ }
+ tls_connection_.DisableTicketSupport();
+ return true;
+}
+
+bool TlsServerHandshaker::IsZeroRtt() const {
+ return SSL_early_data_accepted(ssl());
+}
+
+bool TlsServerHandshaker::IsResumption() const {
+ return SSL_session_reused(ssl());
+}
+
+bool TlsServerHandshaker::ResumptionAttempted() const {
+ return ticket_received_;
+}
+
+bool TlsServerHandshaker::EarlyDataAttempted() const {
+ QUIC_BUG_IF(quic_tls_early_data_attempted_too_early,
+ !select_cert_status_.has_value())
+ << "EarlyDataAttempted must be called after EarlySelectCertCallback is "
+ "started";
+ return early_data_attempted_;
+}
+
+int TlsServerHandshaker::NumServerConfigUpdateMessagesSent() const {
+ // SCUP messages aren't supported when using the TLS handshake.
+ return 0;
+}
+
+const CachedNetworkParameters*
+TlsServerHandshaker::PreviousCachedNetworkParams() const {
+ return last_received_cached_network_params_.get();
+}
+
+void TlsServerHandshaker::SetPreviousCachedNetworkParams(
+ CachedNetworkParameters cached_network_params) {
+ last_received_cached_network_params_ =
+ std::make_unique<CachedNetworkParameters>(cached_network_params);
+}
+
+void TlsServerHandshaker::OnPacketDecrypted(EncryptionLevel level) {
+ if (level == ENCRYPTION_HANDSHAKE && state_ < HANDSHAKE_PROCESSED) {
+ state_ = HANDSHAKE_PROCESSED;
+ handshaker_delegate()->DiscardOldEncryptionKey(ENCRYPTION_INITIAL);
+ handshaker_delegate()->DiscardOldDecryptionKey(ENCRYPTION_INITIAL);
+ }
+}
+
+void TlsServerHandshaker::OnHandshakeDoneReceived() { QUICHE_DCHECK(false); }
+
+void TlsServerHandshaker::OnNewTokenReceived(absl::string_view /*token*/) {
+ QUICHE_DCHECK(false);
+}
+
+std::string TlsServerHandshaker::GetAddressToken(
+ const CachedNetworkParameters* cached_network_params) const {
+ SourceAddressTokens empty_previous_tokens;
+ const QuicConnection* connection = session()->connection();
+ return crypto_config_->NewSourceAddressToken(
+ crypto_config_->source_address_token_boxer(), empty_previous_tokens,
+ connection->effective_peer_address().host(),
+ connection->random_generator(), connection->clock()->WallNow(),
+ cached_network_params);
+}
+
+bool TlsServerHandshaker::ValidateAddressToken(absl::string_view token) const {
+ SourceAddressTokens tokens;
+ HandshakeFailureReason reason = crypto_config_->ParseSourceAddressToken(
+ crypto_config_->source_address_token_boxer(), token, tokens);
+ if (reason != HANDSHAKE_OK) {
+ QUIC_DLOG(WARNING) << "Failed to parse source address token: "
+ << CryptoUtils::HandshakeFailureReasonToString(reason);
+ return false;
+ }
+ auto cached_network_params = std::make_unique<CachedNetworkParameters>();
+ reason = crypto_config_->ValidateSourceAddressTokens(
+ tokens, session()->connection()->effective_peer_address().host(),
+ session()->connection()->clock()->WallNow(), cached_network_params.get());
+ if (reason != HANDSHAKE_OK) {
+ QUIC_DLOG(WARNING) << "Failed to validate source address token: "
+ << CryptoUtils::HandshakeFailureReasonToString(reason);
+ return false;
+ }
+
+ last_received_cached_network_params_ = std::move(cached_network_params);
+ return true;
+}
+
+bool TlsServerHandshaker::ShouldSendExpectCTHeader() const { return false; }
+
+bool TlsServerHandshaker::DidCertMatchSni() const { return cert_matched_sni_; }
+
+const ProofSource::Details* TlsServerHandshaker::ProofSourceDetails() const {
+ return proof_source_details_.get();
+}
+
+bool TlsServerHandshaker::ExportKeyingMaterial(absl::string_view label,
+ absl::string_view context,
+ size_t result_len,
+ std::string* result) {
+ return ExportKeyingMaterialForLabel(label, context, result_len, result);
+}
+
+void TlsServerHandshaker::OnConnectionClosed(QuicErrorCode error,
+ ConnectionCloseSource source) {
+ TlsHandshaker::OnConnectionClosed(error, source);
+}
+
+ssl_early_data_reason_t TlsServerHandshaker::EarlyDataReason() const {
+ return TlsHandshaker::EarlyDataReason();
+}
+
+bool TlsServerHandshaker::encryption_established() const {
+ return encryption_established_;
+}
+
+bool TlsServerHandshaker::one_rtt_keys_available() const {
+ return state_ == HANDSHAKE_CONFIRMED;
+}
+
+const QuicCryptoNegotiatedParameters&
+TlsServerHandshaker::crypto_negotiated_params() const {
+ return *crypto_negotiated_params_;
+}
+
+CryptoMessageParser* TlsServerHandshaker::crypto_message_parser() {
+ return TlsHandshaker::crypto_message_parser();
+}
+
+HandshakeState TlsServerHandshaker::GetHandshakeState() const { return state_; }
+
+void TlsServerHandshaker::SetServerApplicationStateForResumption(
+ std::unique_ptr<ApplicationState> state) {
+ application_state_ = std::move(state);
+}
+
+size_t TlsServerHandshaker::BufferSizeLimitForLevel(
+ EncryptionLevel level) const {
+ return TlsHandshaker::BufferSizeLimitForLevel(level);
+}
+
+std::unique_ptr<QuicDecrypter>
+TlsServerHandshaker::AdvanceKeysAndCreateCurrentOneRttDecrypter() {
+ return TlsHandshaker::AdvanceKeysAndCreateCurrentOneRttDecrypter();
+}
+
+std::unique_ptr<QuicEncrypter>
+TlsServerHandshaker::CreateCurrentOneRttEncrypter() {
+ return TlsHandshaker::CreateCurrentOneRttEncrypter();
+}
+
+void TlsServerHandshaker::OverrideQuicConfigDefaults(QuicConfig* /*config*/) {}
+
+void TlsServerHandshaker::AdvanceHandshakeFromCallback() {
+ QuicConnection::ScopedPacketFlusher flusher(session()->connection());
+
+ AdvanceHandshake();
+ if (!is_connection_closed()) {
+ handshaker_delegate()->OnHandshakeCallbackDone();
+ }
+}
+
+bool TlsServerHandshaker::ProcessTransportParameters(
+ const SSL_CLIENT_HELLO* client_hello, std::string* error_details) {
+ TransportParameters client_params;
+ const uint8_t* client_params_bytes;
+ size_t params_bytes_len;
+
+ // Make sure we use the right TLS extension codepoint.
+ uint16_t extension_type = TLSEXT_TYPE_quic_transport_parameters_standard;
+ if (session()->version().UsesLegacyTlsExtension()) {
+ extension_type = TLSEXT_TYPE_quic_transport_parameters_legacy;
+ }
+ // When using early select cert callback, SSL_get_peer_quic_transport_params
+ // can not be used to retrieve the client's transport parameters, but we can
+ // use SSL_early_callback_ctx_extension_get to do that.
+ if (!SSL_early_callback_ctx_extension_get(client_hello, extension_type,
+ &client_params_bytes,
+ &params_bytes_len)) {
+ params_bytes_len = 0;
+ }
+
+ if (params_bytes_len == 0) {
+ *error_details = "Client's transport parameters are missing";
+ return false;
+ }
+ std::string parse_error_details;
+ if (!ParseTransportParameters(session()->connection()->version(),
+ Perspective::IS_CLIENT, client_params_bytes,
+ params_bytes_len, &client_params,
+ &parse_error_details)) {
+ QUICHE_DCHECK(!parse_error_details.empty());
+ *error_details =
+ "Unable to parse client's transport parameters: " + parse_error_details;
+ return false;
+ }
+
+ // Notify QuicConnectionDebugVisitor.
+ session()->connection()->OnTransportParametersReceived(client_params);
+
+ if (client_params.legacy_version_information.has_value() &&
+ CryptoUtils::ValidateClientHelloVersion(
+ client_params.legacy_version_information.value().version,
+ session()->connection()->version(), session()->supported_versions(),
+ error_details) != QUIC_NO_ERROR) {
+ return false;
+ }
+
+ if (client_params.version_information.has_value() &&
+ !CryptoUtils::ValidateChosenVersion(
+ client_params.version_information.value().chosen_version,
+ session()->version(), error_details)) {
+ QUICHE_DCHECK(!error_details->empty());
+ return false;
+ }
+
+ if (handshaker_delegate()->ProcessTransportParameters(
+ client_params, /* is_resumption = */ false, error_details) !=
+ QUIC_NO_ERROR) {
+ return false;
+ }
+
+ ProcessAdditionalTransportParameters(client_params);
+
+ return true;
+}
+
+TlsServerHandshaker::SetTransportParametersResult
+TlsServerHandshaker::SetTransportParameters() {
+ SetTransportParametersResult result;
+ QUICHE_DCHECK(!result.success);
+
+ TransportParameters server_params;
+ server_params.perspective = Perspective::IS_SERVER;
+ server_params.legacy_version_information =
+ TransportParameters::LegacyVersionInformation();
+ server_params.legacy_version_information.value().supported_versions =
+ CreateQuicVersionLabelVector(session()->supported_versions());
+ server_params.legacy_version_information.value().version =
+ CreateQuicVersionLabel(session()->connection()->version());
+ server_params.version_information = TransportParameters::VersionInformation();
+ server_params.version_information.value().chosen_version =
+ CreateQuicVersionLabel(session()->version());
+ server_params.version_information.value().other_versions =
+ CreateQuicVersionLabelVector(session()->supported_versions());
+
+ if (!handshaker_delegate()->FillTransportParameters(&server_params)) {
+ return result;
+ }
+
+ // Notify QuicConnectionDebugVisitor.
+ session()->connection()->OnTransportParametersSent(server_params);
+
+ { // Ensure |server_params_bytes| is not accessed out of the scope.
+ std::vector<uint8_t> server_params_bytes;
+ if (!SerializeTransportParameters(session()->connection()->version(),
+ server_params, &server_params_bytes) ||
+ SSL_set_quic_transport_params(ssl(), server_params_bytes.data(),
+ server_params_bytes.size()) != 1) {
+ return result;
+ }
+ result.quic_transport_params = std::move(server_params_bytes);
+ }
+
+ if (application_state_) {
+ std::vector<uint8_t> early_data_context;
+ if (!SerializeTransportParametersForTicket(
+ server_params, *application_state_, &early_data_context)) {
+ QUIC_BUG(quic_bug_10341_4)
+ << "Failed to serialize Transport Parameters for ticket.";
+ result.early_data_context = std::vector<uint8_t>();
+ return result;
+ }
+ SSL_set_quic_early_data_context(ssl(), early_data_context.data(),
+ early_data_context.size());
+ result.early_data_context = std::move(early_data_context);
+ application_state_.reset(nullptr);
+ }
+ result.success = true;
+ return result;
+}
+
+void TlsServerHandshaker::SetWriteSecret(
+ EncryptionLevel level, const SSL_CIPHER* cipher,
+ const std::vector<uint8_t>& write_secret) {
+ if (is_connection_closed()) {
+ return;
+ }
+ if (level == ENCRYPTION_FORWARD_SECURE) {
+ encryption_established_ = true;
+ // Fill crypto_negotiated_params_:
+ const SSL_CIPHER* cipher = SSL_get_current_cipher(ssl());
+ if (cipher) {
+ crypto_negotiated_params_->cipher_suite =
+ SSL_CIPHER_get_protocol_id(cipher);
+ }
+ crypto_negotiated_params_->key_exchange_group = SSL_get_curve_id(ssl());
+ }
+ TlsHandshaker::SetWriteSecret(level, cipher, write_secret);
+}
+
+std::string TlsServerHandshaker::GetAcceptChValueForHostname(
+ const std::string& /*hostname*/) const {
+ return {};
+}
+
+void TlsServerHandshaker::FinishHandshake() {
+ QUICHE_DCHECK(!SSL_in_early_data(ssl()));
+
+ if (!valid_alpn_received_) {
+ QUIC_DLOG(ERROR)
+ << "Server: handshake finished without receiving a known ALPN";
+ // TODO(b/130164908) this should send no_application_protocol
+ // instead of QUIC_HANDSHAKE_FAILED.
+ CloseConnection(QUIC_HANDSHAKE_FAILED,
+ "Server did not receive a known ALPN");
+ return;
+ }
+
+ ssl_early_data_reason_t reason_code = EarlyDataReason();
+ QUIC_DLOG(INFO) << "Server: handshake finished. Early data reason "
+ << reason_code << " ("
+ << CryptoUtils::EarlyDataReasonToString(reason_code) << ")";
+ state_ = HANDSHAKE_CONFIRMED;
+
+ handshaker_delegate()->OnTlsHandshakeComplete();
+ handshaker_delegate()->DiscardOldEncryptionKey(ENCRYPTION_HANDSHAKE);
+ handshaker_delegate()->DiscardOldDecryptionKey(ENCRYPTION_HANDSHAKE);
+ // ENCRYPTION_ZERO_RTT decryption key is not discarded here as "Servers MAY
+ // temporarily retain 0-RTT keys to allow decrypting reordered packets
+ // without requiring their contents to be retransmitted with 1-RTT keys."
+ // It is expected that QuicConnection will discard the key at an
+ // appropriate time.
+}
+
+QuicAsyncStatus TlsServerHandshaker::VerifyCertChain(
+ const std::vector<std::string>& /*certs*/, std::string* /*error_details*/,
+ std::unique_ptr<ProofVerifyDetails>* /*details*/, uint8_t* /*out_alert*/,
+ std::unique_ptr<ProofVerifierCallback> /*callback*/) {
+ if (!session()->support_client_cert()) {
+ QUIC_BUG(quic_bug_10341_5)
+ << "Client certificates are not yet supported on the server";
+ return QUIC_FAILURE;
+ }
+
+ QUIC_RESTART_FLAG_COUNT_N(quic_tls_server_support_client_cert, 2, 2);
+ QUIC_DVLOG(1) << "VerifyCertChain returning success";
+
+ // No real verification here. A subclass can override this function to verify
+ // the client cert if needed.
+ return QUIC_SUCCESS;
+}
+
+void TlsServerHandshaker::OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& /*verify_details*/) {}
+
+ssl_private_key_result_t TlsServerHandshaker::PrivateKeySign(
+ uint8_t* out, size_t* out_len, size_t max_out, uint16_t sig_alg,
+ absl::string_view in) {
+ QUICHE_DCHECK_EQ(expected_ssl_error(), SSL_ERROR_WANT_READ);
+
+ QuicAsyncStatus status = proof_source_handle_->ComputeSignature(
+ session()->connection()->self_address(),
+ session()->connection()->peer_address(), crypto_negotiated_params_->sni,
+ sig_alg, in, max_out);
+ if (status == QUIC_PENDING) {
+ set_expected_ssl_error(SSL_ERROR_WANT_PRIVATE_KEY_OPERATION);
+ if (async_op_timer_.has_value()) {
+ QUIC_CODE_COUNT(
+ quic_tls_server_computing_signature_while_another_op_pending);
+ }
+ async_op_timer_ = QuicTimeAccumulator();
+ async_op_timer_->Start(now());
+ }
+ return PrivateKeyComplete(out, out_len, max_out);
+}
+
+ssl_private_key_result_t TlsServerHandshaker::PrivateKeyComplete(
+ uint8_t* out, size_t* out_len, size_t max_out) {
+ if (expected_ssl_error() == SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) {
+ return ssl_private_key_retry;
+ }
+
+ const bool success = HasValidSignature(max_out);
+ QuicConnectionStats::TlsServerOperationStats compute_signature_stats;
+ compute_signature_stats.success = success;
+ if (async_op_timer_.has_value()) {
+ async_op_timer_->Stop(now());
+ compute_signature_stats.async_latency =
+ async_op_timer_->GetTotalElapsedTime();
+ async_op_timer_.reset();
+ RECORD_LATENCY_IN_US("tls_server_async_compute_signature_latency_us",
+ compute_signature_stats.async_latency,
+ "Async compute signature latency in microseconds");
+ }
+ connection_stats().tls_server_compute_signature_stats =
+ std::move(compute_signature_stats);
+
+ if (!success) {
+ return ssl_private_key_failure;
+ }
+ *out_len = cert_verify_sig_.size();
+ memcpy(out, cert_verify_sig_.data(), *out_len);
+ cert_verify_sig_.clear();
+ cert_verify_sig_.shrink_to_fit();
+ return ssl_private_key_success;
+}
+
+void TlsServerHandshaker::OnComputeSignatureDone(
+ bool ok, bool is_sync, std::string signature,
+ std::unique_ptr<ProofSource::Details> details) {
+ QUIC_DVLOG(1) << "OnComputeSignatureDone. ok:" << ok
+ << ", is_sync:" << is_sync
+ << ", len(signature):" << signature.size();
+ absl::optional<QuicConnectionContextSwitcher> context_switcher;
+
+ if (!is_sync) {
+ context_switcher.emplace(connection_context());
+ }
+
+ QUIC_TRACESTRING(absl::StrCat("TLS compute signature done. ok:", ok,
+ ", len(signature):", signature.size()));
+
+ if (ok) {
+ cert_verify_sig_ = std::move(signature);
+ proof_source_details_ = std::move(details);
+ }
+ const int last_expected_ssl_error = expected_ssl_error();
+ set_expected_ssl_error(SSL_ERROR_WANT_READ);
+ if (!is_sync) {
+ QUICHE_DCHECK_EQ(last_expected_ssl_error,
+ SSL_ERROR_WANT_PRIVATE_KEY_OPERATION);
+ AdvanceHandshakeFromCallback();
+ }
+}
+
+bool TlsServerHandshaker::HasValidSignature(size_t max_signature_size) const {
+ return !cert_verify_sig_.empty() &&
+ cert_verify_sig_.size() <= max_signature_size;
+}
+
+size_t TlsServerHandshaker::SessionTicketMaxOverhead() {
+ QUICHE_DCHECK(proof_source_->GetTicketCrypter());
+ return proof_source_->GetTicketCrypter()->MaxOverhead();
+}
+
+int TlsServerHandshaker::SessionTicketSeal(uint8_t* out, size_t* out_len,
+ size_t max_out_len,
+ absl::string_view in) {
+ QUICHE_DCHECK(proof_source_->GetTicketCrypter());
+ std::vector<uint8_t> ticket =
+ proof_source_->GetTicketCrypter()->Encrypt(in, ticket_encryption_key_);
+ if (max_out_len < ticket.size()) {
+ QUIC_BUG(quic_bug_12423_2)
+ << "TicketCrypter returned " << ticket.size()
+ << " bytes of ciphertext, which is larger than its max overhead of "
+ << max_out_len;
+ return 0; // failure
+ }
+ *out_len = ticket.size();
+ memcpy(out, ticket.data(), ticket.size());
+ QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_sealed);
+ return 1; // success
+}
+
+ssl_ticket_aead_result_t TlsServerHandshaker::SessionTicketOpen(
+ uint8_t* out, size_t* out_len, size_t max_out_len, absl::string_view in) {
+ QUICHE_DCHECK(proof_source_->GetTicketCrypter());
+
+ if (ignore_ticket_open_) {
+ // SetIgnoreTicketOpen has been called. Typically this means the caller is
+ // using handshake hints and expect the hints to contain ticket decryption
+ // results.
+ QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_ignored_1);
+ return ssl_ticket_aead_ignore_ticket;
+ }
+
+ if (!ticket_decryption_callback_) {
+ ticket_decryption_callback_ = new DecryptCallback(this);
+ proof_source_->GetTicketCrypter()->Decrypt(
+ in, std::unique_ptr<DecryptCallback>(ticket_decryption_callback_));
+
+ // Decrypt can run the callback synchronously. In that case, the callback
+ // will clear the ticket_decryption_callback_ pointer, and instead of
+ // returning ssl_ticket_aead_retry, we should continue processing to
+ // return the decrypted ticket.
+ //
+ // If the callback is not run synchronously, return ssl_ticket_aead_retry
+ // and when the callback is complete this function will be run again to
+ // return the result.
+ if (ticket_decryption_callback_) {
+ QUICHE_DCHECK(!ticket_decryption_callback_->IsDone());
+ set_expected_ssl_error(SSL_ERROR_PENDING_TICKET);
+ if (async_op_timer_.has_value()) {
+ QUIC_CODE_COUNT(
+ quic_tls_server_decrypting_ticket_while_another_op_pending);
+ }
+ async_op_timer_ = QuicTimeAccumulator();
+ async_op_timer_->Start(now());
+ }
+ }
+
+ // If the async ticket decryption is pending, either started by this
+ // SessionTicketOpen call or one that happened earlier, return
+ // ssl_ticket_aead_retry.
+ if (ticket_decryption_callback_ && !ticket_decryption_callback_->IsDone()) {
+ return ssl_ticket_aead_retry;
+ }
+
+ ssl_ticket_aead_result_t result =
+ FinalizeSessionTicketOpen(out, out_len, max_out_len);
+
+ QuicConnectionStats::TlsServerOperationStats decrypt_ticket_stats;
+ decrypt_ticket_stats.success = (result == ssl_ticket_aead_success);
+ if (async_op_timer_.has_value()) {
+ async_op_timer_->Stop(now());
+ decrypt_ticket_stats.async_latency = async_op_timer_->GetTotalElapsedTime();
+ async_op_timer_.reset();
+ RECORD_LATENCY_IN_US("tls_server_async_decrypt_ticket_latency_us",
+ decrypt_ticket_stats.async_latency,
+ "Async decrypt ticket latency in microseconds");
+ }
+ connection_stats().tls_server_decrypt_ticket_stats =
+ std::move(decrypt_ticket_stats);
+
+ return result;
+}
+
+ssl_ticket_aead_result_t TlsServerHandshaker::FinalizeSessionTicketOpen(
+ uint8_t* out, size_t* out_len, size_t max_out_len) {
+ ticket_decryption_callback_ = nullptr;
+ set_expected_ssl_error(SSL_ERROR_WANT_READ);
+ if (decrypted_session_ticket_.empty()) {
+ QUIC_DLOG(ERROR) << "Session ticket decryption failed; ignoring ticket";
+ // Ticket decryption failed. Ignore the ticket.
+ QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_ignored_2);
+ return ssl_ticket_aead_ignore_ticket;
+ }
+ if (max_out_len < decrypted_session_ticket_.size()) {
+ return ssl_ticket_aead_error;
+ }
+ memcpy(out, decrypted_session_ticket_.data(),
+ decrypted_session_ticket_.size());
+ *out_len = decrypted_session_ticket_.size();
+
+ QUIC_CODE_COUNT(quic_tls_server_handshaker_tickets_opened);
+ return ssl_ticket_aead_success;
+}
+
+ssl_select_cert_result_t TlsServerHandshaker::EarlySelectCertCallback(
+ const SSL_CLIENT_HELLO* client_hello) {
+ // EarlySelectCertCallback can be called twice from BoringSSL: If the first
+ // call returns ssl_select_cert_retry, when cert selection completes,
+ // SSL_do_handshake will call it again.
+
+ if (select_cert_status_.has_value()) {
+ // This is the second call, return the result directly.
+ QUIC_DVLOG(1) << "EarlySelectCertCallback called to continue handshake, "
+ "returning directly. success:"
+ << (select_cert_status_.value() == QUIC_SUCCESS);
+ return (select_cert_status_.value() == QUIC_SUCCESS)
+ ? ssl_select_cert_success
+ : ssl_select_cert_error;
+ }
+
+ // This is the first call.
+ select_cert_status_ = QUIC_PENDING;
+ proof_source_handle_ = MaybeCreateProofSourceHandle();
+
+ if (!pre_shared_key_.empty()) {
+ // TODO(b/154162689) add PSK support to QUIC+TLS.
+ QUIC_BUG(quic_bug_10341_6)
+ << "QUIC server pre-shared keys not yet supported with TLS";
+ return ssl_select_cert_error;
+ }
+
+ {
+ const uint8_t* unused_extension_bytes;
+ size_t unused_extension_len;
+ ticket_received_ = SSL_early_callback_ctx_extension_get(
+ client_hello, TLSEXT_TYPE_pre_shared_key, &unused_extension_bytes,
+ &unused_extension_len);
+
+ early_data_attempted_ = SSL_early_callback_ctx_extension_get(
+ client_hello, TLSEXT_TYPE_early_data, &unused_extension_bytes,
+ &unused_extension_len);
+ }
+
+ // This callback is called very early by Boring SSL, most of the SSL_get_foo
+ // function do not work at this point, but SSL_get_servername does.
+ const char* hostname = SSL_get_servername(ssl(), TLSEXT_NAMETYPE_host_name);
+ if (hostname) {
+ crypto_negotiated_params_->sni =
+ QuicHostnameUtils::NormalizeHostname(hostname);
+ if (!ValidateHostname(hostname)) {
+ return ssl_select_cert_error;
+ }
+ if (hostname != crypto_negotiated_params_->sni) {
+ QUIC_CODE_COUNT(quic_tls_server_hostname_diff);
+ QUIC_LOG_EVERY_N_SEC(WARNING, 300)
+ << "Raw and normalized hostnames differ, but both are valid SNIs. "
+ "raw hostname:"
+ << hostname << ", normalized:" << crypto_negotiated_params_->sni;
+ } else {
+ QUIC_CODE_COUNT(quic_tls_server_hostname_same);
+ }
+ } else {
+ QUIC_LOG(INFO) << "No hostname indicated in SNI";
+ }
+
+ std::string error_details;
+ if (!ProcessTransportParameters(client_hello, &error_details)) {
+ CloseConnection(QUIC_HANDSHAKE_FAILED, error_details);
+ return ssl_select_cert_error;
+ }
+ OverrideQuicConfigDefaults(session()->config());
+ session()->OnConfigNegotiated();
+
+ auto set_transport_params_result = SetTransportParameters();
+ if (!set_transport_params_result.success) {
+ QUIC_LOG(ERROR) << "Failed to set transport parameters";
+ return ssl_select_cert_error;
+ }
+
+ bssl::UniquePtr<uint8_t> ssl_capabilities;
+ size_t ssl_capabilities_len = 0;
+ absl::string_view ssl_capabilities_view;
+
+ absl::optional<std::string> alps;
+
+ if (CryptoUtils::GetSSLCapabilities(ssl(), &ssl_capabilities,
+ &ssl_capabilities_len)) {
+ ssl_capabilities_view =
+ absl::string_view(reinterpret_cast<const char*>(ssl_capabilities.get()),
+ ssl_capabilities_len);
+ }
+
+ // Enable ALPS for the session's ALPN.
+ SetApplicationSettingsResult alps_result =
+ SetApplicationSettings(AlpnForVersion(session()->version()));
+ if (!alps_result.success) {
+ return ssl_select_cert_error;
+ }
+ alps =
+ alps_result.alps_length > 0
+ ? std::string(alps_result.alps_buffer.get(), alps_result.alps_length)
+ : std::string();
+
+ if (!session()->connection()->connected()) {
+ select_cert_status_ = QUIC_FAILURE;
+ return ssl_select_cert_error;
+ }
+
+ can_disable_resumption_ = false;
+ const QuicAsyncStatus status = proof_source_handle_->SelectCertificate(
+ session()->connection()->self_address().Normalized(),
+ session()->connection()->peer_address().Normalized(),
+ ssl_capabilities_view, crypto_negotiated_params_->sni,
+ absl::string_view(
+ reinterpret_cast<const char*>(client_hello->client_hello),
+ client_hello->client_hello_len),
+ AlpnForVersion(session()->version()), std::move(alps),
+ set_transport_params_result.quic_transport_params,
+ set_transport_params_result.early_data_context,
+ tls_connection_.ssl_config());
+
+ QUICHE_DCHECK_EQ(status, select_cert_status().value());
+
+ if (status == QUIC_PENDING) {
+ set_expected_ssl_error(SSL_ERROR_PENDING_CERTIFICATE);
+ if (async_op_timer_.has_value()) {
+ QUIC_CODE_COUNT(quic_tls_server_selecting_cert_while_another_op_pending);
+ }
+ async_op_timer_ = QuicTimeAccumulator();
+ async_op_timer_->Start(now());
+ return ssl_select_cert_retry;
+ }
+
+ if (status == QUIC_FAILURE) {
+ return ssl_select_cert_error;
+ }
+
+ return ssl_select_cert_success;
+}
+
+void TlsServerHandshaker::OnSelectCertificateDone(
+ bool ok, bool is_sync, const ProofSource::Chain* chain,
+ absl::string_view handshake_hints, absl::string_view ticket_encryption_key,
+ bool cert_matched_sni, QuicDelayedSSLConfig delayed_ssl_config) {
+ QUIC_DVLOG(1) << "OnSelectCertificateDone. ok:" << ok
+ << ", is_sync:" << is_sync
+ << ", len(handshake_hints):" << handshake_hints.size()
+ << ", len(ticket_encryption_key):"
+ << ticket_encryption_key.size();
+ absl::optional<QuicConnectionContextSwitcher> context_switcher;
+ if (!is_sync) {
+ context_switcher.emplace(connection_context());
+ }
+
+ QUIC_TRACESTRING(absl::StrCat(
+ "TLS select certificate done: ok:", ok,
+ ", certs_found:", (chain != nullptr && !chain->certs.empty()),
+ ", len(handshake_hints):", handshake_hints.size(),
+ ", len(ticket_encryption_key):", ticket_encryption_key.size()));
+
+ ticket_encryption_key_ = std::string(ticket_encryption_key);
+ select_cert_status_ = QUIC_FAILURE;
+ cert_matched_sni_ = cert_matched_sni;
+ if (session()->support_client_cert()) {
+ if (delayed_ssl_config.client_cert_mode.has_value()) {
+ tls_connection_.SetClientCertMode(*delayed_ssl_config.client_cert_mode);
+ QUIC_DVLOG(1) << "client_cert_mode after cert selection: "
+ << client_cert_mode();
+ }
+ }
+ if (ok) {
+ if (chain && !chain->certs.empty()) {
+ tls_connection_.SetCertChain(chain->ToCryptoBuffers().value);
+ if (!handshake_hints.empty() &&
+ !SSL_set_handshake_hints(
+ ssl(), reinterpret_cast<const uint8_t*>(handshake_hints.data()),
+ handshake_hints.size())) {
+ // If |SSL_set_handshake_hints| fails, the ssl() object will remain
+ // intact, it is as if we didn't call it. The handshaker will
+ // continue to compute signature/decrypt ticket as normal.
+ QUIC_CODE_COUNT(quic_tls_server_set_handshake_hints_failed);
+ QUIC_DVLOG(1) << "SSL_set_handshake_hints failed";
+ }
+ select_cert_status_ = QUIC_SUCCESS;
+ } else {
+ QUIC_DLOG(ERROR) << "No certs provided for host '"
+ << crypto_negotiated_params_->sni << "', server_address:"
+ << session()->connection()->self_address()
+ << ", client_address:"
+ << session()->connection()->peer_address();
+ }
+ }
+
+ QuicConnectionStats::TlsServerOperationStats select_cert_stats;
+ select_cert_stats.success = (select_cert_status_ == QUIC_SUCCESS);
+ QUICHE_DCHECK_NE(is_sync, async_op_timer_.has_value());
+ if (async_op_timer_.has_value()) {
+ async_op_timer_->Stop(now());
+ select_cert_stats.async_latency = async_op_timer_->GetTotalElapsedTime();
+ async_op_timer_.reset();
+ RECORD_LATENCY_IN_US("tls_server_async_select_cert_latency_us",
+ select_cert_stats.async_latency,
+ "Async select cert latency in microseconds");
+ }
+ connection_stats().tls_server_select_cert_stats =
+ std::move(select_cert_stats);
+
+ const int last_expected_ssl_error = expected_ssl_error();
+ set_expected_ssl_error(SSL_ERROR_WANT_READ);
+ if (!is_sync) {
+ QUICHE_DCHECK_EQ(last_expected_ssl_error, SSL_ERROR_PENDING_CERTIFICATE);
+ AdvanceHandshakeFromCallback();
+ }
+}
+
+bool TlsServerHandshaker::WillNotCallComputeSignature() const {
+ return SSL_can_release_private_key(ssl());
+}
+
+bool TlsServerHandshaker::ValidateHostname(const std::string& hostname) const {
+ if (!QuicHostnameUtils::IsValidSNI(hostname)) {
+ // TODO(b/151676147): Include this error string in the CONNECTION_CLOSE
+ // frame.
+ QUIC_DLOG(ERROR) << "Invalid SNI provided: \"" << hostname << "\"";
+ return false;
+ }
+ return true;
+}
+
+int TlsServerHandshaker::TlsExtServernameCallback(int* /*out_alert*/) {
+ // SSL_TLSEXT_ERR_OK causes the server_name extension to be acked in
+ // ServerHello.
+ return SSL_TLSEXT_ERR_OK;
+}
+
+int TlsServerHandshaker::SelectAlpn(const uint8_t** out, uint8_t* out_len,
+ const uint8_t* in, unsigned in_len) {
+ // |in| contains a sequence of 1-byte-length-prefixed values.
+ *out_len = 0;
+ *out = nullptr;
+ if (in_len == 0) {
+ QUIC_DLOG(ERROR) << "No ALPN provided by client";
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ CBS all_alpns;
+ CBS_init(&all_alpns, in, in_len);
+
+ std::vector<absl::string_view> alpns;
+ while (CBS_len(&all_alpns) > 0) {
+ CBS alpn;
+ if (!CBS_get_u8_length_prefixed(&all_alpns, &alpn)) {
+ QUIC_DLOG(ERROR) << "Failed to parse ALPN length";
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ const size_t alpn_length = CBS_len(&alpn);
+ if (alpn_length == 0) {
+ QUIC_DLOG(ERROR) << "Received invalid zero-length ALPN";
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ alpns.emplace_back(reinterpret_cast<const char*>(CBS_data(&alpn)),
+ alpn_length);
+ }
+
+ // TODO(wub): Remove QuicSession::SelectAlpn. QuicSessions should know the
+ // ALPN on construction.
+ auto selected_alpn = session()->SelectAlpn(alpns);
+ if (selected_alpn == alpns.end()) {
+ QUIC_DLOG(ERROR) << "No known ALPN provided by client";
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ session()->OnAlpnSelected(*selected_alpn);
+ valid_alpn_received_ = true;
+ *out_len = selected_alpn->size();
+ *out = reinterpret_cast<const uint8_t*>(selected_alpn->data());
+ return SSL_TLSEXT_ERR_OK;
+}
+
+TlsServerHandshaker::SetApplicationSettingsResult
+TlsServerHandshaker::SetApplicationSettings(absl::string_view alpn) {
+ TlsServerHandshaker::SetApplicationSettingsResult result;
+ const uint8_t* alps_data = nullptr;
+
+ const std::string& hostname = crypto_negotiated_params_->sni;
+ std::string accept_ch_value = GetAcceptChValueForHostname(hostname);
+ std::string origin = absl::StrCat("https://", hostname);
+ uint16_t port = session()->self_address().port();
+ if (port != kDefaultPort) {
+ // This should be rare in production, but useful for test servers.
+ QUIC_CODE_COUNT(quic_server_alps_non_default_port);
+ absl::StrAppend(&origin, ":", port);
+ }
+
+ if (!accept_ch_value.empty()) {
+ AcceptChFrame frame{{{std::move(origin), std::move(accept_ch_value)}}};
+ result.alps_length =
+ HttpEncoder::SerializeAcceptChFrame(frame, &result.alps_buffer);
+ alps_data = reinterpret_cast<const uint8_t*>(result.alps_buffer.get());
+ }
+
+ if (SSL_add_application_settings(
+ ssl(), reinterpret_cast<const uint8_t*>(alpn.data()), alpn.size(),
+ alps_data, result.alps_length) != 1) {
+ QUIC_DLOG(ERROR) << "Failed to enable ALPS";
+ result.success = false;
+ } else {
+ result.success = true;
+ }
+ return result;
+}
+
+SSL* TlsServerHandshaker::GetSsl() const { return ssl(); }
+
+} // namespace quic