blob: 28f4d6e3955f69070daaa41cecf9fae02da272e2 [file] [log] [blame]
// Copyright 2020 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/crypto/proof_source_x509.h"
#include <memory>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "openssl/ssl.h"
#include "quiche/quic/core/crypto/certificate_view.h"
#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/core/crypto/crypto_utils.h"
#include "quiche/quic/core/quic_data_writer.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/common/quiche_endian.h"
namespace quic {
ProofSourceX509::ProofSourceX509(
quiche::QuicheReferenceCountedPointer<Chain> default_chain,
CertificatePrivateKey default_key) {
if (!AddCertificateChain(default_chain, std::move(default_key))) {
return;
}
default_certificate_ = &certificates_.front();
}
std::unique_ptr<ProofSourceX509> ProofSourceX509::Create(
quiche::QuicheReferenceCountedPointer<Chain> default_chain,
CertificatePrivateKey default_key) {
std::unique_ptr<ProofSourceX509> result(
new ProofSourceX509(default_chain, std::move(default_key)));
if (!result->valid()) {
return nullptr;
}
return result;
}
void ProofSourceX509::GetProof(
const QuicSocketAddress& /*server_address*/,
const QuicSocketAddress& /*client_address*/, const std::string& hostname,
const std::string& server_config,
QuicTransportVersion /*transport_version*/, absl::string_view chlo_hash,
std::unique_ptr<ProofSource::Callback> callback) {
QuicCryptoProof proof;
if (!valid()) {
QUIC_BUG(ProofSourceX509::GetProof called in invalid state)
<< "ProofSourceX509::GetProof called while the object is not valid";
callback->Run(/*ok=*/false, nullptr, proof, nullptr);
return;
}
absl::optional<std::string> payload =
CryptoUtils::GenerateProofPayloadToBeSigned(chlo_hash, server_config);
if (!payload.has_value()) {
callback->Run(/*ok=*/false, nullptr, proof, nullptr);
return;
}
Certificate* certificate = GetCertificate(hostname, &proof.cert_matched_sni);
proof.signature =
certificate->key.Sign(*payload, SSL_SIGN_RSA_PSS_RSAE_SHA256);
MaybeAddSctsForHostname(hostname, proof.leaf_cert_scts);
callback->Run(/*ok=*/!proof.signature.empty(), certificate->chain, proof,
nullptr);
}
quiche::QuicheReferenceCountedPointer<ProofSource::Chain>
ProofSourceX509::GetCertChain(const QuicSocketAddress& /*server_address*/,
const QuicSocketAddress& /*client_address*/,
const std::string& hostname,
bool* cert_matched_sni) {
if (!valid()) {
QUIC_BUG(ProofSourceX509::GetCertChain called in invalid state)
<< "ProofSourceX509::GetCertChain called while the object is not "
"valid";
return nullptr;
}
return GetCertificate(hostname, cert_matched_sni)->chain;
}
void ProofSourceX509::ComputeTlsSignature(
const QuicSocketAddress& /*server_address*/,
const QuicSocketAddress& /*client_address*/, const std::string& hostname,
uint16_t signature_algorithm, absl::string_view in,
std::unique_ptr<ProofSource::SignatureCallback> callback) {
if (!valid()) {
QUIC_BUG(ProofSourceX509::ComputeTlsSignature called in invalid state)
<< "ProofSourceX509::ComputeTlsSignature called while the object is "
"not valid";
callback->Run(/*ok=*/false, "", nullptr);
return;
}
bool cert_matched_sni;
std::string signature = GetCertificate(hostname, &cert_matched_sni)
->key.Sign(in, signature_algorithm);
callback->Run(/*ok=*/!signature.empty(), signature, nullptr);
}
absl::InlinedVector<uint16_t, 8>
ProofSourceX509::SupportedTlsSignatureAlgorithms() const {
// Let ComputeTlsSignature() report an error if a bad signature algorithm is
// requested.
return {};
}
ProofSource::TicketCrypter* ProofSourceX509::GetTicketCrypter() {
return nullptr;
}
bool ProofSourceX509::AddCertificateChain(
quiche::QuicheReferenceCountedPointer<Chain> chain,
CertificatePrivateKey key) {
if (chain->certs.empty()) {
QUIC_BUG(quic_bug_10644_1) << "Empty certificate chain supplied.";
return false;
}
std::unique_ptr<CertificateView> leaf =
CertificateView::ParseSingleCertificate(chain->certs[0]);
if (leaf == nullptr) {
QUIC_BUG(quic_bug_10644_2)
<< "Unable to parse X.509 leaf certificate in the supplied chain.";
return false;
}
if (!key.MatchesPublicKey(*leaf)) {
QUIC_BUG(quic_bug_10644_3)
<< "Private key does not match the leaf certificate.";
return false;
}
certificates_.push_front(Certificate{
chain,
std::move(key),
});
Certificate* certificate = &certificates_.front();
for (absl::string_view host : leaf->subject_alt_name_domains()) {
certificate_map_[std::string(host)] = certificate;
}
return true;
}
ProofSourceX509::Certificate* ProofSourceX509::GetCertificate(
const std::string& hostname, bool* cert_matched_sni) const {
QUICHE_DCHECK(valid());
auto it = certificate_map_.find(hostname);
if (it != certificate_map_.end()) {
*cert_matched_sni = true;
return it->second;
}
auto dot_pos = hostname.find('.');
if (dot_pos != std::string::npos) {
std::string wildcard = absl::StrCat("*", hostname.substr(dot_pos));
it = certificate_map_.find(wildcard);
if (it != certificate_map_.end()) {
*cert_matched_sni = true;
return it->second;
}
}
*cert_matched_sni = false;
return default_certificate_;
}
} // namespace quic