Skip to content

Commit

Permalink
quiche: implement certificate verification (#12063)
Browse files Browse the repository at this point in the history
Implement quic::ProofVerifier which consists of cert verification and signature verification.

Cert verification: Share cert verification code with Extensions::TransportSockets::Tls::ClientContextImpl. And initialize ProofVerifier using Envoy::Ssl::ClientContextConfig protobuf.
Signature verification: Use quic::CertificateViewer to verify signature.

Part of #9434 #2557

Signed-off-by: Dan Zhang <danzh@google.com>
  • Loading branch information
danzh2010 authored Aug 7, 2020
1 parent e0bd398 commit 43b110a
Show file tree
Hide file tree
Showing 24 changed files with 1,072 additions and 191 deletions.
36 changes: 30 additions & 6 deletions source/extensions/quic_listeners/quiche/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,17 @@ envoy_cc_library(
)

envoy_cc_library(
name = "envoy_quic_fake_proof_source_lib",
hdrs = ["envoy_quic_fake_proof_source.h"],
name = "envoy_quic_proof_source_base_lib",
srcs = ["envoy_quic_proof_source_base.cc"],
hdrs = ["envoy_quic_proof_source_base.h"],
external_deps = ["quiche_quic_platform"],
tags = ["nofips"],
deps = [
":envoy_quic_utils_lib",
"@com_googlesource_quiche//:quic_core_crypto_certificate_view_lib",
"@com_googlesource_quiche//:quic_core_crypto_crypto_handshake_lib",
"@com_googlesource_quiche//:quic_core_crypto_proof_source_interface_lib",
"@com_googlesource_quiche//:quic_core_data_lib",
"@com_googlesource_quiche//:quic_core_versions_lib",
],
)
Expand All @@ -79,7 +84,7 @@ envoy_cc_library(
external_deps = ["ssl"],
tags = ["nofips"],
deps = [
":envoy_quic_fake_proof_source_lib",
":envoy_quic_proof_source_base_lib",
":envoy_quic_utils_lib",
":quic_io_handle_wrapper_lib",
":quic_transport_socket_factory_lib",
Expand All @@ -91,16 +96,32 @@ envoy_cc_library(
)

envoy_cc_library(
name = "envoy_quic_proof_verifier_lib",
hdrs = ["envoy_quic_fake_proof_verifier.h"],
name = "envoy_quic_proof_verifier_base_lib",
srcs = ["envoy_quic_proof_verifier_base.cc"],
hdrs = ["envoy_quic_proof_verifier_base.h"],
external_deps = ["quiche_quic_platform"],
tags = ["nofips"],
deps = [
":envoy_quic_utils_lib",
"@com_googlesource_quiche//:quic_core_crypto_certificate_view_lib",
"@com_googlesource_quiche//:quic_core_crypto_crypto_handshake_lib",
"@com_googlesource_quiche//:quic_core_versions_lib",
],
)

envoy_cc_library(
name = "envoy_quic_proof_verifier_lib",
srcs = ["envoy_quic_proof_verifier.cc"],
hdrs = ["envoy_quic_proof_verifier.h"],
external_deps = ["quiche_quic_platform"],
tags = ["nofips"],
deps = [
":envoy_quic_proof_verifier_base_lib",
":envoy_quic_utils_lib",
"//source/extensions/transport_sockets/tls:context_lib",
],
)

envoy_cc_library(
name = "spdy_server_push_utils_for_envoy_lib",
srcs = ["spdy_server_push_utils_for_envoy.cc"],
Expand Down Expand Up @@ -323,7 +344,10 @@ envoy_cc_library(
name = "envoy_quic_utils_lib",
srcs = ["envoy_quic_utils.cc"],
hdrs = ["envoy_quic_utils.h"],
external_deps = ["quiche_quic_platform"],
external_deps = [
"quiche_quic_platform",
"ssl",
],
tags = ["nofips"],
deps = [
"//include/envoy/http:codec_interface",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,13 @@ EnvoyQuicProofSource::GetCertChain(const quic::QuicSocketAddress& server_address
}
auto& cert_config = cert_config_ref.value().get();
const std::string& chain_str = cert_config.certificateChain();
std::string pem_str = std::string(const_cast<char*>(chain_str.data()), chain_str.size());
std::stringstream pem_stream(chain_str);
std::vector<std::string> chain = quic::CertificateView::LoadPemFromStream(&pem_stream);
if (chain.empty()) {
ENVOY_LOG(warn, "Failed to load certificate chain from %s", cert_config.certificateChainPath());
return quic::QuicReferenceCountedPointer<quic::ProofSource::Chain>(
new quic::ProofSource::Chain({}));
}
return quic::QuicReferenceCountedPointer<quic::ProofSource::Chain>(
new quic::ProofSource::Chain(chain));
}

void EnvoyQuicProofSource::ComputeTlsSignature(
void EnvoyQuicProofSource::signPayload(
const quic::QuicSocketAddress& server_address, const quic::QuicSocketAddress& client_address,
const std::string& hostname, uint16_t signature_algorithm, quiche::QuicheStringPiece in,
std::unique_ptr<quic::ProofSource::SignatureCallback> callback) {
Expand All @@ -59,7 +53,11 @@ void EnvoyQuicProofSource::ComputeTlsSignature(
std::stringstream pem_str(pkey);
std::unique_ptr<quic::CertificatePrivateKey> pem_key =
quic::CertificatePrivateKey::LoadPemFromStream(&pem_str);

if (pem_key == nullptr) {
ENVOY_LOG(warn, "Failed to load private key.");
callback->Run(false, "", nullptr);
return;
}
// Sign.
std::string sig = pem_key->Sign(in, signature_algorithm);

Expand All @@ -85,7 +83,6 @@ EnvoyQuicProofSource::getTlsCertConfigAndFilterChain(const quic::QuicSocketAddre
const Network::FilterChain* filter_chain =
filter_chain_manager_.findFilterChain(connection_socket);
if (filter_chain == nullptr) {
ENVOY_LOG(warn, "No matching filter chain found for handshake.");
listener_stats_.no_filter_chain_match_.inc();
return {absl::nullopt, absl::nullopt};
}
Expand Down
19 changes: 11 additions & 8 deletions source/extensions/quic_listeners/quiche/envoy_quic_proof_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

#include "server/connection_handler_impl.h"

#include "extensions/quic_listeners/quiche/envoy_quic_fake_proof_source.h"
#include "extensions/quic_listeners/quiche/envoy_quic_proof_source_base.h"
#include "extensions/quic_listeners/quiche/quic_transport_socket_factory.h"

namespace Envoy {
namespace Quic {

class EnvoyQuicProofSource : public EnvoyQuicFakeProofSource,
protected Logger::Loggable<Logger::Id::quic> {
// A ProofSource implementation which supplies a proof instance with certs from filter chain.
class EnvoyQuicProofSource : public EnvoyQuicProofSourceBase {
public:
EnvoyQuicProofSource(Network::Socket& listen_socket,
Network::FilterChainManager& filter_chain_manager,
Expand All @@ -19,14 +19,17 @@ class EnvoyQuicProofSource : public EnvoyQuicFakeProofSource,

~EnvoyQuicProofSource() override = default;

// quic::ProofSource
quic::QuicReferenceCountedPointer<quic::ProofSource::Chain>
GetCertChain(const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address, const std::string& hostname) override;
void ComputeTlsSignature(const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address,
const std::string& hostname, uint16_t signature_algorithm,
quiche::QuicheStringPiece in,
std::unique_ptr<quic::ProofSource::SignatureCallback> callback) override;

protected:
// quic::ProofSource
void signPayload(const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address, const std::string& hostname,
uint16_t signature_algorithm, quiche::QuicheStringPiece in,
std::unique_ptr<quic::ProofSource::SignatureCallback> callback) override;

private:
struct CertConfigWithFilterChain {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "extensions/quic_listeners/quiche/envoy_quic_proof_source_base.h"

#pragma GCC diagnostic push

// QUICHE allows unused parameters.
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include "quiche/quic/core/quic_data_writer.h"

#pragma GCC diagnostic pop

#include "extensions/quic_listeners/quiche/envoy_quic_utils.h"

namespace Envoy {
namespace Quic {

void EnvoyQuicProofSourceBase::GetProof(const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address,
const std::string& hostname,
const std::string& server_config,
quic::QuicTransportVersion /*transport_version*/,
quiche::QuicheStringPiece chlo_hash,
std::unique_ptr<quic::ProofSource::Callback> callback) {
quic::QuicReferenceCountedPointer<quic::ProofSource::Chain> chain =
GetCertChain(server_address, client_address, hostname);

if (chain == nullptr || chain->certs.empty()) {
quic::QuicCryptoProof proof;
callback->Run(/*ok=*/false, nullptr, proof, nullptr);
return;
}
size_t payload_size = sizeof(quic::kProofSignatureLabel) + sizeof(uint32_t) + chlo_hash.size() +
server_config.size();
auto payload = std::make_unique<char[]>(payload_size);
quic::QuicDataWriter payload_writer(payload_size, payload.get(),
quiche::Endianness::HOST_BYTE_ORDER);
bool success =
payload_writer.WriteBytes(quic::kProofSignatureLabel, sizeof(quic::kProofSignatureLabel)) &&
payload_writer.WriteUInt32(chlo_hash.size()) && payload_writer.WriteStringPiece(chlo_hash) &&
payload_writer.WriteStringPiece(server_config);
if (!success) {
quic::QuicCryptoProof proof;
callback->Run(/*ok=*/false, nullptr, proof, nullptr);
return;
}

std::string error_details;
bssl::UniquePtr<X509> cert = parseDERCertificate(chain->certs[0], &error_details);
if (cert == nullptr) {
ENVOY_LOG(warn, absl::StrCat("Invalid leaf cert: ", error_details));
quic::QuicCryptoProof proof;
callback->Run(/*ok=*/false, nullptr, proof, nullptr);
return;
}

bssl::UniquePtr<EVP_PKEY> pub_key(X509_get_pubkey(cert.get()));
int sign_alg = deduceSignatureAlgorithmFromPublicKey(pub_key.get(), &error_details);
if (sign_alg == 0) {
ENVOY_LOG(warn, absl::StrCat("Failed to deduce signature algorithm from public key: ",
error_details));
quic::QuicCryptoProof proof;
callback->Run(/*ok=*/false, nullptr, proof, nullptr);
return;
}

auto signature_callback = std::make_unique<SignatureCallback>(std::move(callback), chain);

signPayload(server_address, client_address, hostname, sign_alg,
quiche::QuicheStringPiece(payload.get(), payload_size),
std::move(signature_callback));
}

void EnvoyQuicProofSourceBase::ComputeTlsSignature(
const quic::QuicSocketAddress& server_address, const quic::QuicSocketAddress& client_address,
const std::string& hostname, uint16_t signature_algorithm, quiche::QuicheStringPiece in,
std::unique_ptr<quic::ProofSource::SignatureCallback> callback) {
signPayload(server_address, client_address, hostname, signature_algorithm, in,
std::move(callback));
}

} // namespace Quic
} // namespace Envoy
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include "quiche/quic/core/crypto/proof_source.h"
#include "quiche/quic/core/quic_versions.h"

#include "quiche/quic/core/crypto/crypto_protocol.h"
#include "quiche/quic/platform/api/quic_reference_counted.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/common/platform/api/quiche_string_piece.h"
#pragma GCC diagnostic pop

#include "openssl/ssl.h"
#include "envoy/network/filter.h"
#include "quiche/quic/platform/api/quic_reference_counted.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/common/platform/api/quiche_string_piece.h"
#include "server/backtrace.h"
#include "common/common/logger.h"

namespace Envoy {
namespace Quic {
Expand All @@ -38,31 +40,37 @@ class EnvoyQuicProofSourceDetails : public quic::ProofSource::Details {
const Network::FilterChain& filter_chain_;
};

// A fake implementation of quic::ProofSource which uses RSA cipher suite to sign in GetProof().
// TODO(danzh) Rename it to EnvoyQuicProofSource once it's fully implemented.
class EnvoyQuicFakeProofSource : public quic::ProofSource {
// A partial implementation of quic::ProofSource which chooses a cipher suite according to the leaf
// cert to sign in GetProof().
class EnvoyQuicProofSourceBase : public quic::ProofSource,
protected Logger::Loggable<Logger::Id::quic> {
public:
~EnvoyQuicFakeProofSource() override = default;
~EnvoyQuicProofSourceBase() override = default;

// quic::ProofSource
// Returns a certs chain and its fake SCT "Fake timestamp" and TLS signature wrapped
// in QuicCryptoProof.
void GetProof(const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address, const std::string& hostname,
const std::string& server_config, quic::QuicTransportVersion /*transport_version*/,
quiche::QuicheStringPiece /*chlo_hash*/,
std::unique_ptr<quic::ProofSource::Callback> callback) override {
quic::QuicReferenceCountedPointer<quic::ProofSource::Chain> chain =
GetCertChain(server_address, client_address, hostname);
quic::QuicCryptoProof proof;
// TODO(danzh) Get the signature algorithm from leaf cert.
auto signature_callback = std::make_unique<SignatureCallback>(std::move(callback), chain);
ComputeTlsSignature(server_address, client_address, hostname, SSL_SIGN_RSA_PSS_RSAE_SHA256,
server_config, std::move(signature_callback));
}
quiche::QuicheStringPiece chlo_hash,
std::unique_ptr<quic::ProofSource::Callback> callback) override;

TicketCrypter* GetTicketCrypter() override { return nullptr; }

void ComputeTlsSignature(const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address,
const std::string& hostname, uint16_t signature_algorithm,
quiche::QuicheStringPiece in,
std::unique_ptr<quic::ProofSource::SignatureCallback> callback) override;

protected:
virtual void signPayload(const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address,
const std::string& hostname, uint16_t signature_algorithm,
quiche::QuicheStringPiece in,
std::unique_ptr<quic::ProofSource::SignatureCallback> callback) PURE;

private:
// Used by GetProof() to get signature.
class SignatureCallback : public quic::ProofSource::SignatureCallback {
Expand Down
Loading

0 comments on commit 43b110a

Please sign in to comment.