Skip to content

Commit

Permalink
quic: add private key provider support (envoyproxy#65) (envoyproxy#75)
Browse files Browse the repository at this point in the history
* quic: add private key provider support (envoyproxy#65)

* add integration test

Signed-off-by: He Jie Xu <[email protected]>
  • Loading branch information
soulxu committed Feb 27, 2024
1 parent f741e79 commit 002323d
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 24 deletions.
12 changes: 10 additions & 2 deletions source/extensions/quic/crypto_stream/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ envoy_extension_package()

envoy_cc_library(
name = "envoy_quic_crypto_server_stream_lib",
srcs = ["envoy_quic_crypto_server_stream.cc"],
hdrs = ["envoy_quic_crypto_server_stream.h"],
srcs = [
"envoy_quic_crypto_server_stream.cc",
"envoy_tls_server_handshaker.cc",
],
hdrs = [
"envoy_quic_crypto_server_stream.h",
"envoy_tls_server_handshaker.h",
],
tags = ["nofips"],
visibility = [
"//source/common/quic:__subpackages__",
Expand All @@ -27,6 +33,8 @@ envoy_cc_library(
deps = [
"//envoy/registry",
"//source/common/quic:envoy_quic_server_crypto_stream_factory_lib",
"//source/common/quic:quic_server_transport_socket_factory_lib",
"@com_github_google_quiche//:quic_server_session_lib",
"@envoy_api//envoy/extensions/quic/crypto_stream/v3:pkg_cc_proto",
],
alwayslink = LEGACY_ALWAYSLINK,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "source/extensions/quic/crypto_stream/envoy_quic_crypto_server_stream.h"

#include "envoy_tls_server_handshaker.h"
#include "quiche/quic/core/tls_server_handshaker.h"

namespace Envoy {
namespace Quic {

Expand All @@ -8,10 +11,12 @@ EnvoyQuicCryptoServerStreamFactoryImpl::createEnvoyQuicCryptoServerStream(
const quic::QuicCryptoServerConfig* crypto_config,
quic::QuicCompressedCertsCache* compressed_certs_cache, quic::QuicSession* session,
quic::QuicCryptoServerStreamBase::Helper* helper,
// Though this extension doesn't use the two parameters below, they might be used by
// downstreams. Do not remove them.
OptRef<const Network::DownstreamTransportSocketFactory> /*transport_socket_factory*/,
Envoy::Event::Dispatcher& /*dispatcher*/) {
OptRef<const Network::DownstreamTransportSocketFactory> transport_socket_factory,
Envoy::Event::Dispatcher& dispatcher) {
if (session->connection()->version().handshake_protocol == quic::PROTOCOL_TLS1_3) {
return std::unique_ptr<EnvoyTlsServerHandshaker>(
new EnvoyTlsServerHandshaker(session, crypto_config, transport_socket_factory, dispatcher));
}
return quic::CreateCryptoServerStream(crypto_config, compressed_certs_cache, session, helper);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "envoy_tls_server_handshaker.h"

namespace Envoy {
namespace Quic {

EnvoyTlsServerHandshaker::EnvoyTlsServerHandshaker(
quic::QuicSession* session, const quic::QuicCryptoServerConfig* crypto_config,
OptRef<const Network::DownstreamTransportSocketFactory> transport_socket_factory,
Envoy::Event::Dispatcher& dispatcher)
: quic::TlsServerHandshaker(session, crypto_config) {
if (transport_socket_factory != absl::nullopt) {
transport_socket_factory_.emplace(
dynamic_cast<const QuicServerTransportSocketFactory&>(transport_socket_factory.ref()));
for (auto cert_config : transport_socket_factory_->getTlsCertificates()) {
if (cert_config.get().privateKeyMethod()) {
cert_config.get().privateKeyMethod()->registerPrivateKeyMethod(GetSsl(), *this, dispatcher);
}
}
}
}

ssl_private_key_result_t EnvoyTlsServerHandshaker::PrivateKeySign(uint8_t* out, size_t* out_len,
size_t max_out, uint16_t sig_alg,
absl::string_view in) {
if (transport_socket_factory_->getTlsCertificates().size() > 0) {
// TODO(soulxu): Currently the QUIC transport socket only support one certificate. After
// QUIC transport socket with multiple certificates, we will figure out how to support
// multiple certificates for private key provider also.
auto private_key_method =
transport_socket_factory_->getTlsCertificates()[0].get().privateKeyMethod();
if (private_key_method != nullptr) {
auto ret = private_key_method->getBoringSslPrivateKeyMethod()->sign(
GetSsl(), out, out_len, max_out, sig_alg, reinterpret_cast<const uint8_t*>(in.data()),
in.size());
if (ret == ssl_private_key_retry) {
set_expected_ssl_error(SSL_ERROR_WANT_PRIVATE_KEY_OPERATION);
}
return ret;
}
}
return quic::TlsServerHandshaker::PrivateKeySign(out, out_len, max_out, sig_alg, in);
}

ssl_private_key_result_t EnvoyTlsServerHandshaker::PrivateKeyComplete(uint8_t* out, size_t* out_len,
size_t max_out) {
if (transport_socket_factory_->getTlsCertificates().size() > 0) {
auto private_key_method =
transport_socket_factory_->getTlsCertificates()[0].get().privateKeyMethod();
if (private_key_method != nullptr) {
auto ret = private_key_method->getBoringSslPrivateKeyMethod()->complete(GetSsl(), out,
out_len, max_out);
if (ret == ssl_private_key_success) {
set_expected_ssl_error(SSL_ERROR_WANT_READ);
}
return ret;
}
}
return quic::TlsServerHandshaker::PrivateKeyComplete(out, out_len, max_out);
}

void EnvoyTlsServerHandshaker::onPrivateKeyMethodComplete() { AdvanceHandshakeFromCallback(); }

void EnvoyTlsServerHandshaker::FinishHandshake() {
quic::TlsServerHandshaker::FinishHandshake();
if (transport_socket_factory_ != absl::nullopt) {
for (auto cert_config : transport_socket_factory_->getTlsCertificates()) {
if (cert_config.get().privateKeyMethod()) {
cert_config.get().privateKeyMethod()->unregisterPrivateKeyMethod(GetSsl());
}
}
}
}

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

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

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

#include "quiche/quic/core/tls_server_handshaker.h"

namespace Envoy {
namespace Quic {

class EnvoyTlsServerHandshaker : public quic::TlsServerHandshaker,
public Envoy::Ssl::PrivateKeyConnectionCallbacks {
public:
EnvoyTlsServerHandshaker(
quic::QuicSession* session, const quic::QuicCryptoServerConfig* crypto_config,
OptRef<const Network::DownstreamTransportSocketFactory> transport_socket_factory,
Envoy::Event::Dispatcher& dispatcher);

ssl_private_key_result_t PrivateKeySign(uint8_t* out, size_t* out_len, size_t max_out,
uint16_t sig_alg, absl::string_view in) override;
ssl_private_key_result_t PrivateKeyComplete(uint8_t* out, size_t* out_len,
size_t max_out) override;
void onPrivateKeyMethodComplete() override;

protected:
void FinishHandshake() override;

private:
OptRef<const QuicServerTransportSocketFactory> transport_socket_factory_;
};

} // namespace Quic
} // namespace Envoy
1 change: 1 addition & 0 deletions test/common/quic/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ envoy_cc_test(
"//source/common/quic:envoy_quic_server_connection_lib",
"//source/common/quic:envoy_quic_server_session_lib",
"//source/common/quic:server_codec_lib",
"//source/extensions/quic/crypto_stream:envoy_quic_crypto_server_stream_lib",
"//source/server:configuration_lib",
"//test/mocks/event:event_mocks",
"//test/mocks/http:http_mocks",
Expand Down
18 changes: 11 additions & 7 deletions test/common/quic/envoy_quic_server_session_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "source/common/quic/envoy_quic_server_stream.h"
#include "source/common/quic/envoy_quic_utils.h"
#include "source/common/quic/server_codec_impl.h"
#include "source/extensions/quic/crypto_stream/envoy_tls_server_handshaker.h"
#include "source/server/configuration_impl.h"

#include "test/common/quic/test_proof_source.h"
Expand Down Expand Up @@ -88,14 +89,16 @@ class TestQuicCryptoServerStream : public quic::QuicCryptoServerStream,
std::unique_ptr<EnvoyQuicProofSourceDetails> details_;
};

class TestEnvoyQuicTlsServerHandshaker : public quic::TlsServerHandshaker,
class TestEnvoyQuicTlsServerHandshaker : public EnvoyTlsServerHandshaker,
public ProofSourceDetailsSetter {
public:
~TestEnvoyQuicTlsServerHandshaker() override = default;

TestEnvoyQuicTlsServerHandshaker(quic::QuicSession* session,
const quic::QuicCryptoServerConfig& crypto_config)
: quic::TlsServerHandshaker(session, &crypto_config),
TestEnvoyQuicTlsServerHandshaker(
quic::QuicSession* session, const quic::QuicCryptoServerConfig& crypto_config,
OptRef<const Network::DownstreamTransportSocketFactory> transport_socket_factory,
Envoy::Event::Dispatcher& dispatcher)
: EnvoyTlsServerHandshaker(session, &crypto_config, transport_socket_factory, dispatcher),
params_(new quic::QuicCryptoNegotiatedParameters) {
params_->cipher_suite = 1;
}
Expand Down Expand Up @@ -123,14 +126,15 @@ class EnvoyQuicTestCryptoServerStreamFactory : public EnvoyQuicCryptoServerStrea
const quic::QuicCryptoServerConfig* crypto_config,
quic::QuicCompressedCertsCache* compressed_certs_cache, quic::QuicSession* session,
quic::QuicCryptoServerStreamBase::Helper* helper,
OptRef<const Network::DownstreamTransportSocketFactory> /*transport_socket_factory*/,
Event::Dispatcher& /*dispatcher*/) override {
OptRef<const Network::DownstreamTransportSocketFactory> transport_socket_factory,
Event::Dispatcher& dispatcher) override {
switch (session->connection()->version().handshake_protocol) {
case quic::PROTOCOL_QUIC_CRYPTO:
return std::make_unique<TestQuicCryptoServerStream>(crypto_config, compressed_certs_cache,
session, helper);
case quic::PROTOCOL_TLS1_3:
return std::make_unique<TestEnvoyQuicTlsServerHandshaker>(session, *crypto_config);
return std::make_unique<TestEnvoyQuicTlsServerHandshaker>(
session, *crypto_config, transport_socket_factory, dispatcher);
case quic::PROTOCOL_UNSUPPORTED:
ASSERT(false, "Unknown handshake protocol");
}
Expand Down
34 changes: 27 additions & 7 deletions test/config/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1320,7 +1320,8 @@ void ConfigHelper::addSslConfig(const ServerSslOptions& options) {
}

void ConfigHelper::addQuicDownstreamTransportSocketConfig(
bool enable_early_data, std::vector<absl::string_view> custom_alpns) {
bool enable_early_data, std::vector<absl::string_view> custom_alpns,
bool with_test_private_key_provider, bool test_private_key_provider_sync_mode) {
for (auto& listener : *bootstrap_.mutable_static_resources()->mutable_listeners()) {
if (listener.udp_listener_config().has_quic_options()) {
// Disable SO_REUSEPORT, because it undesirably allows parallel test jobs to use the same
Expand All @@ -1331,8 +1332,8 @@ void ConfigHelper::addQuicDownstreamTransportSocketConfig(
configDownstreamTransportSocketWithTls(
bootstrap_,
[&](envoy::extensions::transport_sockets::tls::v3::CommonTlsContext& common_tls_context) {
initializeTls(ServerSslOptions().setRsaCert(true).setTlsV13(true), common_tls_context,
true);
initializeTls(ServerSslOptions().setRsaCert(true).setTlsV13(true), common_tls_context, true,
with_test_private_key_provider, test_private_key_provider_sync_mode);
for (absl::string_view alpn : custom_alpns) {
common_tls_context.add_alpn_protocols(alpn);
}
Expand Down Expand Up @@ -1452,8 +1453,8 @@ void ConfigHelper::initializeTlsKeyLog(

void ConfigHelper::initializeTls(
const ServerSslOptions& options,
envoy::extensions::transport_sockets::tls::v3::CommonTlsContext& common_tls_context,
bool http3) {
envoy::extensions::transport_sockets::tls::v3::CommonTlsContext& common_tls_context, bool http3,
bool with_test_private_key_provider, bool test_private_key_provider_sync_mode) {
if (!http3) {
// If it's HTTP/3, leave it empty as QUIC will derive the supported ALPNs from QUIC version by
// default.
Expand Down Expand Up @@ -1510,8 +1511,27 @@ void ConfigHelper::initializeTls(
auto* tls_certificate = common_tls_context.add_tls_certificates();
tls_certificate->mutable_certificate_chain()->set_filename(
TestEnvironment::runfilesPath("test/config/integration/certs/servercert.pem"));
tls_certificate->mutable_private_key()->set_filename(
TestEnvironment::runfilesPath("test/config/integration/certs/serverkey.pem"));
if (!with_test_private_key_provider) {
tls_certificate->mutable_private_key()->set_filename(
TestEnvironment::runfilesPath("test/config/integration/certs/serverkey.pem"));
} else {
tls_certificate->mutable_private_key_provider()->set_provider_name("test");
ProtobufWkt::Struct message;
std::string sync_mode = test_private_key_provider_sync_mode ? "true" : "false";
#ifdef ENVOY_ENABLE_YAML
TestUtility::loadFromJson(
"{\"private_key_file\":\"" +
TestEnvironment::runfilesPath("test/config/integration/certs/serverkey.pem") +
"\", \"expected_operation\":\"sign\", \"mode\":\"rsa\", \"sync_mode\":" + sync_mode +
"}",
message);
#else
UNREFERENCED_PARAMETER(message);
UNREFERENCED_PARAMETER(sync_mode);
PANIC("YAML support compiled out");
#endif
tls_certificate->mutable_private_key_provider()->mutable_typed_config()->PackFrom(message);
}
if (options.rsa_cert_ocsp_staple_) {
tls_certificate->mutable_ocsp_staple()->set_filename(
TestEnvironment::runfilesPath("test/config/integration/certs/server_ocsp_resp.der"));
Expand Down
7 changes: 5 additions & 2 deletions test/config/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ class ConfigHelper {
static void
initializeTls(const ServerSslOptions& options,
envoy::extensions::transport_sockets::tls::v3::CommonTlsContext& common_context,
bool http3);
bool http3, bool with_test_private_key_provider = false,
bool test_private_key_provider_sync_mode = false);

static void initializeTlsKeyLog(
envoy::extensions::transport_sockets::tls::v3::CommonTlsContext& common_tls_context,
Expand Down Expand Up @@ -338,7 +339,9 @@ class ConfigHelper {

// Add the default SSL configuration for QUIC downstream.
void addQuicDownstreamTransportSocketConfig(bool enable_early_data,
std::vector<absl::string_view> custom_alpns);
std::vector<absl::string_view> custom_alpns,
bool with_test_private_key_provider = false,
bool test_private_key_provider_sync_mode = false);

// Set the HTTP access log for the first HCM (if present) to a given file. The default is
// the platform's null device.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ TestPrivateKeyMethodProvider::TestPrivateKeyMethodProvider(
pkey_ = std::move(pkey);
}

REGISTER_FACTORY(TestPrivateKeyMethodFactory, Ssl::PrivateKeyMethodProviderInstanceFactory);

} // namespace PrivateKeyMethodProvider
} // namespace Extensions
} // namespace Envoy
1 change: 1 addition & 0 deletions test/integration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2435,6 +2435,7 @@ envoy_cc_test(
"//source/extensions/quic/server_preferred_address:fixed_server_preferred_address_config_factory_config",
"//test/common/config:dummy_config_proto_cc_proto",
"//test/extensions/transport_sockets/tls/cert_validator:timed_cert_validator",
"//test/extensions/transport_sockets/tls:test_private_key_method_provider_test_lib",
":http_integration_lib",
":socket_interface_swap_lib",
"//source/common/quic:client_connection_factory_lib",
Expand Down
3 changes: 2 additions & 1 deletion test/integration/http_integration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ void HttpIntegrationTest::initialize() {

// Needed to config QUIC transport socket factory, and needs to be added before base class calls
// initialize().
config_helper_.addQuicDownstreamTransportSocketConfig(enable_quic_early_data_, custom_alpns_);
config_helper_.addQuicDownstreamTransportSocketConfig(enable_quic_early_data_, custom_alpns_,
enable_test_private_key_provider_);

BaseIntegrationTest::initialize();
registerTestServerPorts({"http"}, test_server_);
Expand Down
2 changes: 2 additions & 0 deletions test/integration/http_integration.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,8 @@ class HttpIntegrationTest : public BaseIntegrationTest {
quic::DeterministicConnectionIdGenerator connection_id_generator_{
quic::kQuicDefaultConnectionIdLength};
#endif
bool enable_test_private_key_provider_{false};
bool test_private_key_provider_sync_mode_{false};
};

// Helper class for integration tests using raw HTTP/2 frames
Expand Down
Loading

0 comments on commit 002323d

Please sign in to comment.