Skip to content

Commit

Permalink
Add PrivateKeyProvider Quic support
Browse files Browse the repository at this point in the history
Signed-off-by: He Jie Xu <hejie.xu@intel.com>
  • Loading branch information
soulxu authored and Your Name committed Jun 18, 2024
1 parent ea982dc commit 44925a6
Show file tree
Hide file tree
Showing 18 changed files with 462 additions and 39 deletions.
2 changes: 2 additions & 0 deletions bazel/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,8 @@ def _com_github_google_quiche():
name = "com_github_google_quiche",
patch_cmds = ["find quiche/ -type f -name \"*.bazel\" -delete"],
build_file = "@envoy//bazel/external:quiche.BUILD",
# patches = ["@envoy//bazel/foreign_cc:quiche_move_default_proof_source_handler.patch"],
# patch_args = ["-p1"],
)
native.bind(
name = "quiche_common_platform",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ class CryptoMbPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodPro
bool checkFips() override;
bool isAvailable() override;
Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override;

EVP_PKEY* private_key() const override { return pkey_.get(); }
static int connectionIndex();

const std::chrono::microseconds& getPollDelayForTest() const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ void QatPrivateKeyConnection::registerCallback(QatContext* ctx) {
status = CPA_STATUS_FAIL;
}
ctx->setOpStatus(status);
return absl::OkStatus();
}
this->cb_.onPrivateKeyMethodComplete();
return absl::OkStatus();
},
Event::FileTriggerType::Edge, Event::FileReadyType::Read);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class QatPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider
bool checkFips() override;
bool isAvailable() override;
Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override;
EVP_PKEY* private_key() const { return pkey_.get(); }

private:
Ssl::BoringSslPrivateKeyMethodSharedPtr method_{};
Expand Down
2 changes: 2 additions & 0 deletions envoy/ssl/private_key/private_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class PrivateKeyMethodProvider {
* configuration.
*/
virtual BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() PURE;

virtual EVP_PKEY* private_key() const PURE;
#endif
};

Expand Down
27 changes: 27 additions & 0 deletions source/common/quic/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,13 @@ envoy_cc_library(
external_deps = ["ssl"],
tags = ["nofips"],
deps = [
":envoy_quic_certificate_private_key_lib",
":envoy_quic_proof_source_base_lib",
":envoy_quic_utils_lib",
":quic_io_handle_wrapper_lib",
":quic_transport_socket_factory_lib",
"//envoy/ssl:tls_certificate_config_interface",
"//envoy/ssl/private_key:private_key_callbacks_interface",
"//source/common/quic:quic_server_transport_socket_factory_lib",
"//source/common/stream_info:stream_info_lib",
"//source/server:listener_stats",
Expand Down Expand Up @@ -368,6 +370,18 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "envoy_quic_certificate_private_key_lib",
srcs = ["envoy_quic_certificate_private_key.cc"],
hdrs = ["envoy_quic_certificate_private_key.h"],
tags = ["nofips"],
deps = [
":envoy_quic_proof_source_base_lib",
"//envoy/ssl/private_key:private_key_interface",
"@com_github_google_quiche//:quic_core_connection_lib",
],
)

envoy_cc_library(
name = "envoy_quic_client_connection_lib",
srcs = ["envoy_quic_client_connection.cc"],
Expand Down Expand Up @@ -498,6 +512,7 @@ envoy_cc_library(
],
tags = ["nofips"],
deps = [
":envoy_quic_certificate_private_key_lib",
":envoy_quic_proof_verifier_lib",
":quic_transport_socket_factory_lib",
"//envoy/network:transport_socket_interface",
Expand Down Expand Up @@ -665,6 +680,18 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "envoy_tls_server_handshaker_lib",
srcs = ["envoy_tls_server_handshaker.cc"],
hdrs = ["envoy_tls_server_handshaker.h"],
tags = ["nofips"],
deps = [
":envoy_quic_proof_source_lib",
"//source/common/quic:quic_server_transport_socket_factory_lib",
"@com_github_google_quiche//:quic_server_session_lib",
],
)

envoy_cc_library(
name = "quic_stats_gatherer",
srcs = ["quic_stats_gatherer.cc"],
Expand Down
77 changes: 77 additions & 0 deletions source/common/quic/envoy_quic_certificate_private_key.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include "source/common/quic/envoy_quic_certificate_private_key.h"

#include <cstddef>

#include "source/common/quic/envoy_quic_proof_source_base.h"

#include "openssl/ssl.h"

namespace Envoy {
namespace Quic {

EnvoyQuicDefaultCertificatePrivateKey::EnvoyQuicDefaultCertificatePrivateKey(
bssl::UniquePtr<EVP_PKEY> private_key)
: private_key_(std::make_unique<quic::CertificatePrivateKey>(std::move(private_key))) {}

EnvoyQuicDefaultCertificatePrivateKey::EnvoyQuicDefaultCertificatePrivateKey(
std::unique_ptr<quic::CertificatePrivateKey> private_key)
: private_key_(std::move(private_key)) {}

void EnvoyQuicDefaultCertificatePrivateKey::Sign(
SSL*, Event::Dispatcher*, absl::string_view input, uint16_t signature_algorithm,
std::unique_ptr<EnvoyQuicProofSourceDetails> details, PrivateKeySignCallbacks& cb) {
std::string signature = private_key_->Sign(input, signature_algorithm);
cb.onPrivateKeySignComplete(true, signature, std::move(details));
}

EVP_PKEY* EnvoyQuicDefaultCertificatePrivateKey::private_key() const {
return private_key_->private_key();
}

EnvoyQuicCertificatePrivateKeyByProvider::EnvoyQuicCertificatePrivateKeyByProvider(
Ssl::PrivateKeyMethodProviderSharedPtr private_key_provider)
: private_key_provider_(private_key_provider) {}

void EnvoyQuicCertificatePrivateKeyByProvider::Sign(
SSL* ssl, Event::Dispatcher* dispatcher, absl::string_view input, uint16_t signature_algorithm,
std::unique_ptr<EnvoyQuicProofSourceDetails> details, PrivateKeySignCallbacks& cb) {
ssl_ = ssl;
cb_ = cb;
private_key_provider_->registerPrivateKeyMethod(ssl, *this, *dispatcher);
EVP_PKEY* pkey = private_key_provider_->private_key();
if (pkey == nullptr) {
cb.onPrivateKeySignComplete(false, "", nullptr);
}
max_sig_size_ = EVP_PKEY_size(pkey);

signature_.resize(max_sig_size_);
size_t sig_size = 0;

auto ret = private_key_provider_->getBoringSslPrivateKeyMethod()->sign(
ssl, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(signature_.data())), &sig_size,
max_sig_size_, signature_algorithm,
const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(input.data())), input.size());
if (ret == ssl_private_key_retry) {
details_ = std::move(details);
return;
}
if (ret == ssl_private_key_failure) {
cb.onPrivateKeySignComplete(false, "", nullptr);
}
cb.onPrivateKeySignComplete(true, signature_, std::move(details));
}

void EnvoyQuicCertificatePrivateKeyByProvider::onPrivateKeyMethodComplete() {
size_t sig_len = 0;
private_key_provider_->getBoringSslPrivateKeyMethod()->complete(
ssl_, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(signature_.data())), &sig_len,
max_sig_size_);
cb_->onPrivateKeySignComplete(true, signature_, std::move(details_));
}

EVP_PKEY* EnvoyQuicCertificatePrivateKeyByProvider::private_key() const {
return private_key_provider_->private_key();
}

} // namespace Quic
} // namespace Envoy
68 changes: 68 additions & 0 deletions source/common/quic/envoy_quic_certificate_private_key.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once

#include "envoy/ssl/private_key/private_key.h"

#include "source/common/quic/envoy_quic_proof_source_base.h"

#include "absl/types/optional.h"
#include "quiche/quic/core/crypto/proof_source.h"

namespace Envoy {
namespace Quic {

class PrivateKeySignCallbacks {
public:
virtual ~PrivateKeySignCallbacks() = default;

virtual void onPrivateKeySignComplete(bool success, const std::string& output,
std::unique_ptr<EnvoyQuicProofSourceDetails> details) PURE;
};

class EnvoyQuicCertificatePrivateKey {
public:
virtual ~EnvoyQuicCertificatePrivateKey() = default;
virtual void Sign(SSL* ssl, Event::Dispatcher* dispatcher, absl::string_view input,
uint16_t signature_algorithm,
std::unique_ptr<EnvoyQuicProofSourceDetails> details,
PrivateKeySignCallbacks& cb) PURE;
virtual EVP_PKEY* private_key() const PURE;
};

class EnvoyQuicDefaultCertificatePrivateKey : public EnvoyQuicCertificatePrivateKey {
public:
EnvoyQuicDefaultCertificatePrivateKey(bssl::UniquePtr<EVP_PKEY> private_key);
EnvoyQuicDefaultCertificatePrivateKey(std::unique_ptr<quic::CertificatePrivateKey>);

void Sign(SSL* ssl, Event::Dispatcher* dispatcher, absl::string_view input,
uint16_t signature_algorithm, std::unique_ptr<EnvoyQuicProofSourceDetails> details,
PrivateKeySignCallbacks& cb) override;
EVP_PKEY* private_key() const override;

private:
std::unique_ptr<quic::CertificatePrivateKey> private_key_;
};

class EnvoyQuicCertificatePrivateKeyByProvider : public EnvoyQuicCertificatePrivateKey,
public Ssl::PrivateKeyConnectionCallbacks {
public:
EnvoyQuicCertificatePrivateKeyByProvider(
Ssl::PrivateKeyMethodProviderSharedPtr provider_key_provider);

void Sign(SSL* ssl, Event::Dispatcher* dispatcher, absl::string_view input,
uint16_t signature_algorithm, std::unique_ptr<EnvoyQuicProofSourceDetails> details,
PrivateKeySignCallbacks& cb) override;
EVP_PKEY* private_key() const override;

void onPrivateKeyMethodComplete() override;

private:
Ssl::PrivateKeyMethodProviderSharedPtr private_key_provider_;
SSL* ssl_{nullptr};
std::string signature_;
size_t max_sig_size_{0};
OptRef<PrivateKeySignCallbacks> cb_{absl::nullopt};
std::unique_ptr<EnvoyQuicProofSourceDetails> details_{nullptr};
};

} // namespace Quic
} // namespace Envoy
50 changes: 31 additions & 19 deletions source/common/quic/envoy_quic_proof_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "source/common/runtime/runtime_features.h"
#include "source/common/stream_info/stream_info_impl.h"

#include "envoy_quic_certificate_private_key.h"
#include "openssl/bytestring.h"
#include "quiche/quic/core/crypto/certificate_view.h"

Expand Down Expand Up @@ -71,22 +72,23 @@ void EnvoyQuicProofSource::signPayload(
const quic::QuicSocketAddress& server_address, const quic::QuicSocketAddress& client_address,
const std::string& hostname, uint16_t signature_algorithm, absl::string_view in,
std::unique_ptr<quic::ProofSource::SignatureCallback> callback) {
callback_ = std::move(callback);
auto data = getTransportSocketAndFilterChain(server_address, client_address, hostname);
if (!data.has_value()) {
ENVOY_LOG(warn, "No matching filter chain found for handshake.");
callback->Run(false, "", nullptr);
callback_->Run(false, "", nullptr);
return;
}

if (!data->transport_socket_factory_.handleCertsWithSharedTlsCode()) {
return legacySignPayload(*data, signature_algorithm, in, std::move(callback));
return legacySignPayload(*data, signature_algorithm, in);
}

CertWithFilterChain res =
getTlsCertAndFilterChain(*data, hostname, nullptr /* cert_matched_sni */);
if (res.private_key_ == nullptr) {
ENVOY_LOG(warn, "No matching filter chain found for handshake.");
callback->Run(false, "", nullptr);
callback_->Run(false, "", nullptr);
return;
}

Expand All @@ -98,27 +100,27 @@ void EnvoyQuicProofSource::signPayload(
ENVOY_LOG(warn,
fmt::format("The signature algorithm {} from the private key is not expected: {}",
sign_alg, error_details));
callback->Run(false, "", nullptr);
callback_->Run(false, "", nullptr);
return;
}

// Sign.
std::string sig = res.private_key_->Sign(in, signature_algorithm);
bool success = !sig.empty();
ASSERT(res.filter_chain_.has_value());
callback->Run(success, sig,
std::make_unique<EnvoyQuicProofSourceDetails>(res.filter_chain_.value().get()));
ASSERT(ssl_ != nullptr);
res.private_key_->Sign(
ssl_, dispatcher_, in, signature_algorithm,
std::make_unique<EnvoyQuicProofSourceDetails>(res.filter_chain_.value().get()), *this);
}

void EnvoyQuicProofSource::legacySignPayload(
const TransportSocketFactoryWithFilterChain& data, uint16_t signature_algorithm,
absl::string_view in, std::unique_ptr<quic::ProofSource::SignatureCallback> callback) {
void EnvoyQuicProofSource::legacySignPayload(const TransportSocketFactoryWithFilterChain& data,
uint16_t signature_algorithm, absl::string_view in) {
ASSERT(callback_ != nullptr);
LegacyCertConfigWithFilterChain res = legacyGetTlsCertConfigAndFilterChain(data);
absl::optional<std::reference_wrapper<const Envoy::Ssl::TlsCertificateConfig>> cert_config_ref =
res.cert_config_;
if (!cert_config_ref.has_value()) {
ENVOY_LOG(warn, "No matching filter chain found for handshake.");
callback->Run(false, "", nullptr);
callback_->Run(false, "", nullptr);
return;
}
auto& cert_config = cert_config_ref.value().get();
Expand All @@ -129,26 +131,30 @@ void EnvoyQuicProofSource::legacySignPayload(
quic::CertificatePrivateKey::LoadPemFromStream(&pem_str);
if (pem_key == nullptr) {
ENVOY_LOG(warn, "Failed to load private key.");
callback->Run(false, "", nullptr);
callback_->Run(false, "", nullptr);
return;
}

// TODO(soulxu): keep this as member, since the provider key is async.
std::unique_ptr<EnvoyQuicCertificatePrivateKey> private_key =
std::make_unique<EnvoyQuicDefaultCertificatePrivateKey>(std::move(pem_key));
// Verify the signature algorithm is as expected.
std::string error_details;
int sign_alg = deduceSignatureAlgorithmFromPublicKey(pem_key->private_key(), &error_details);
int sign_alg = deduceSignatureAlgorithmFromPublicKey(private_key->private_key(), &error_details);
if (sign_alg != signature_algorithm) {
ENVOY_LOG(warn,
fmt::format("The signature algorithm {} from the private key is not expected: {}",
sign_alg, error_details));
callback->Run(false, "", nullptr);
callback_->Run(false, "", nullptr);
return;
}

// Sign.
std::string sig = pem_key->Sign(in, signature_algorithm);
bool success = !sig.empty();
ASSERT(res.filter_chain_.has_value());
callback->Run(success, sig,
std::make_unique<EnvoyQuicProofSourceDetails>(res.filter_chain_.value().get()));
ASSERT(ssl_ != nullptr && dispatcher_ != nullptr);
private_key->Sign(ssl_, dispatcher_, in, signature_algorithm,
std::make_unique<EnvoyQuicProofSourceDetails>(res.filter_chain_.value().get()),
*this);
}

EnvoyQuicProofSource::CertWithFilterChain
Expand Down Expand Up @@ -211,5 +217,11 @@ void EnvoyQuicProofSource::updateFilterChainManager(
filter_chain_manager_ = &filter_chain_manager;
}

void EnvoyQuicProofSource::onPrivateKeySignComplete(
bool success, const std::string& output, std::unique_ptr<EnvoyQuicProofSourceDetails> details) {
ASSERT(callback_ != nullptr);
callback_->Run(success, output, std::move(details));
}

} // namespace Quic
} // namespace Envoy
Loading

0 comments on commit 44925a6

Please sign in to comment.