From 3ab2e1d3a75d33870539f61161f5ab27200ff759 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 13 Mar 2019 15:43:34 +0200 Subject: [PATCH 01/56] tls: support BoringSSL private key async functionality. Opens: 1. Validate the thread model. Now there is an unique PrivateKeyOperations instance for each connection, but the PrivateKeyOperationsProvider is shared. This makes the dispatcher model easier and the PrivateKeyOperations lifecycle can be tied with the caller. 2. How to get the private key to the provider? Just let the provider use SSL_get_privatekey()? 3. Does SDS require any special handling? 4. Should we expose BoringSSL primitives (such as 'SSL *ssl') in the API? 5. Automatic registration of PrivateKeyOperationsProvider extensions to the PrivateKeyOperationsManager. We need a NamedPrivateKeyOperationsProviderConfigFactory? 6. Is the API sufficient for all private key users? Signed-off-by: Ismo Puustinen --- include/envoy/ssl/BUILD | 1 + include/envoy/ssl/context_config.h | 7 ++ include/envoy/ssl/private_key/BUILD | 26 +++++++ include/envoy/ssl/private_key/private_key.h | 74 +++++++++++++++++++ .../ssl/private_key/private_key_callbacks.h | 30 ++++++++ source/extensions/transport_sockets/tls/BUILD | 4 + .../tls/context_config_impl.cc | 12 +++ .../tls/context_config_impl.h | 6 ++ .../transport_sockets/tls/context_impl.cc | 3 +- .../transport_sockets/tls/context_impl.h | 6 ++ .../transport_sockets/tls/private_key/BUILD | 24 ++++++ .../private_key/private_key_manager_impl.cc | 23 ++++++ .../private_key/private_key_manager_impl.h | 21 ++++++ .../transport_sockets/tls/ssl_socket.cc | 34 ++++++++- .../transport_sockets/tls/ssl_socket.h | 8 ++ 15 files changed, 276 insertions(+), 3 deletions(-) create mode 100644 include/envoy/ssl/private_key/BUILD create mode 100644 include/envoy/ssl/private_key/private_key.h create mode 100644 include/envoy/ssl/private_key/private_key_callbacks.h create mode 100644 source/extensions/transport_sockets/tls/private_key/BUILD create mode 100644 source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc create mode 100644 source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h diff --git a/include/envoy/ssl/BUILD b/include/envoy/ssl/BUILD index d5537579a042..8861e8b4c748 100644 --- a/include/envoy/ssl/BUILD +++ b/include/envoy/ssl/BUILD @@ -31,6 +31,7 @@ envoy_cc_library( deps = [ ":certificate_validation_context_config_interface", ":tls_certificate_config_interface", + "//include/envoy/ssl/private_key:private_key_interface", ], ) diff --git a/include/envoy/ssl/context_config.h b/include/envoy/ssl/context_config.h index 5f9063e85d0c..1a385c14096b 100644 --- a/include/envoy/ssl/context_config.h +++ b/include/envoy/ssl/context_config.h @@ -6,6 +6,7 @@ #include "envoy/common/pure.h" #include "envoy/ssl/certificate_validation_context_config.h" +#include "envoy/ssl/private_key/private_key.h" #include "envoy/ssl/tls_certificate_config.h" namespace Envoy { @@ -69,6 +70,12 @@ class ContextConfig { * @param callback callback that is executed by context config. */ virtual void setSecretUpdateCallback(std::function callback) PURE; + + /** + * @return the configured BoringSSL private key operations provider. + */ + virtual const Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr + privateKeyOperationsProvider() const PURE; }; class ClientContextConfig : public virtual ContextConfig { diff --git a/include/envoy/ssl/private_key/BUILD b/include/envoy/ssl/private_key/BUILD new file mode 100644 index 000000000000..d65bdf6ee1c5 --- /dev/null +++ b/include/envoy/ssl/private_key/BUILD @@ -0,0 +1,26 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "private_key_interface", + hdrs = ["private_key.h"], + external_deps = ["ssl"], + deps = [ + ":private_key_callbacks_interface", + "//include/envoy/event:dispatcher_interface", + "@envoy_api//envoy/api/v2/core:config_source_cc", + ], +) + +envoy_cc_library( + name = "private_key_callbacks_interface", + hdrs = ["private_key_callbacks.h"], + external_deps = ["ssl"], +) diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h new file mode 100644 index 000000000000..9e992bb0cfad --- /dev/null +++ b/include/envoy/ssl/private_key/private_key.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +#include "envoy/api/v2/core/config_source.pb.h" +#include "envoy/common/pure.h" +#include "envoy/event/dispatcher.h" +#include "envoy/ssl/private_key/private_key_callbacks.h" + +#include "openssl/ssl.h" + +namespace Envoy { +namespace Ssl { + +typedef std::shared_ptr PrivateKeyMethodSharedPtr; + +class PrivateKeyOperations { +public: + virtual ~PrivateKeyOperations() {} + + /** + * Get the private key asynchronous methods from the provider, if the context can be handled by + * the provider. + * @param ssl a SSL connection object. + * @return private key methods, or nullptr if regular TLS processing should happen. + */ + virtual PrivateKeyMethodSharedPtr getPrivateKeyMethods(SSL* ssl) PURE; +}; + +typedef std::unique_ptr PrivateKeyOperationsPtr; + +class PrivateKeyOperationsProvider { +public: + virtual ~PrivateKeyOperationsProvider() {} + + /** + * Get a private key operations instance from the provider. + * @param cb a callbacks object, whose "complete" method will be invoked when the asynchronous + * processing is complete. + * @param dispatcher supplies the owning thread's dispatcher. + * @return the private key operations. + */ + virtual PrivateKeyOperationsPtr getPrivateKeyOperations(PrivateKeyOperationsCallbacks& cb, + Event::Dispatcher& dispatcher) PURE; +}; + +typedef std::shared_ptr PrivateKeyOperationsProviderSharedPtr; + +/** + * A manager for finding correct user-provided functions for handling BoringSSL private key + * operations. + */ +class PrivateKeyOperationsManager { +public: + virtual ~PrivateKeyOperationsManager() {} + + /** + * Finds and returns a private key operations provider for BoringSSL. + * + * @param config_source a protobuf message object containing a TLS config source. + * @param config_name a name that uniquely refers to the private key operations provider. + * @return TlsPrivateKeyOperationsProvider the private key operations provider, or nullptr if + * no provider can be used with the context configuration. + */ + virtual PrivateKeyOperationsProviderSharedPtr + findPrivateKeyOperationsProvider(const envoy::api::v2::core::ConfigSource& config_source, + const std::string& config_name) PURE; +}; + +typedef std::shared_ptr PrivateKeyOperationsManagerSharedPtr; + +} // namespace Ssl +} // namespace Envoy diff --git a/include/envoy/ssl/private_key/private_key_callbacks.h b/include/envoy/ssl/private_key/private_key_callbacks.h new file mode 100644 index 000000000000..276feac885e9 --- /dev/null +++ b/include/envoy/ssl/private_key/private_key_callbacks.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#include "envoy/common/pure.h" + +namespace Envoy { +namespace Ssl { + +enum class PrivateKeyOperationStatus { + Success, + Failure, +}; + +class PrivateKeyOperationsCallbacks { +public: + virtual ~PrivateKeyOperationsCallbacks() {} + + /** + * Callback function which is called when the asynchronous private key + * operation has been completed. + * @param status is "Success" or "Failure" depending on whether the private key operation was + * successful or not. + */ + virtual void complete(PrivateKeyOperationStatus status) PURE; +}; + +} // namespace Ssl +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/BUILD b/source/extensions/transport_sockets/tls/BUILD index 4f07d1e5dea0..ee8f2fabc7d7 100644 --- a/source/extensions/transport_sockets/tls/BUILD +++ b/source/extensions/transport_sockets/tls/BUILD @@ -38,6 +38,8 @@ envoy_cc_library( ":utility_lib", "//include/envoy/network:connection_interface", "//include/envoy/network:transport_socket_interface", + "//include/envoy/ssl/private_key:private_key_callbacks_interface", + "//include/envoy/ssl/private_key:private_key_interface", "//include/envoy/stats:stats_macros", "//source/common/common:assert_lib", "//source/common/common:empty_string", @@ -59,6 +61,7 @@ envoy_cc_library( "//include/envoy/secret:secret_provider_interface", "//include/envoy/server:transport_socket_config_interface", "//include/envoy/ssl:context_config_interface", + "//include/envoy/ssl/private_key:private_key_interface", "//source/common/common:assert_lib", "//source/common/common:empty_string", "//source/common/config:datasource_lib", @@ -91,6 +94,7 @@ envoy_cc_library( "//include/envoy/ssl:context_config_interface", "//include/envoy/ssl:context_interface", "//include/envoy/ssl:context_manager_interface", + "//include/envoy/ssl/private_key:private_key_interface", "//include/envoy/stats:stats_interface", "//include/envoy/stats:stats_macros", "//source/common/common:assert_lib", diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index bb19a2c791e3..b058ab3608dc 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -104,6 +104,17 @@ getCertificateValidationContextConfigProvider( } } +Ssl::PrivateKeyOperationsProviderSharedPtr getPrivateKeyOperationsProvider( + const envoy::api::v2::auth::CommonTlsContext& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) { + (void)config; + (void)factory_context; + + // TODO: get PrivateKeyOperationsManager from the factory_context and + // get a provider based on the provider name in the TLS context. + return nullptr; +} + } // namespace ContextConfigImpl::ContextConfigImpl( @@ -120,6 +131,7 @@ ContextConfigImpl::ContextConfigImpl( tls_certificate_providers_(getTlsCertificateConfigProviders(config, factory_context)), certificate_validation_context_provider_( getCertificateValidationContextConfigProvider(config, factory_context, &default_cvc_)), + private_key_provider_(getPrivateKeyOperationsProvider(config, factory_context)), min_protocol_version_(tlsVersionFromProto(config.tls_params().tls_minimum_protocol_version(), default_min_protocol_version)), max_protocol_version_(tlsVersionFromProto(config.tls_params().tls_maximum_protocol_version(), diff --git a/source/extensions/transport_sockets/tls/context_config_impl.h b/source/extensions/transport_sockets/tls/context_config_impl.h index 5efbc0680b67..24acd6533356 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.h +++ b/source/extensions/transport_sockets/tls/context_config_impl.h @@ -8,6 +8,7 @@ #include "envoy/secret/secret_provider.h" #include "envoy/server/transport_socket_config.h" #include "envoy/ssl/context_config.h" +#include "envoy/ssl/private_key/private_key.h" #include "common/common/empty_string.h" #include "common/json/json_loader.h" @@ -41,6 +42,10 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { certificateValidationContext() const override { return validation_context_config_.get(); } + const Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr + privateKeyOperationsProvider() const override { + return private_key_provider_; + } unsigned minProtocolVersion() const override { return min_protocol_version_; }; unsigned maxProtocolVersion() const override { return max_protocol_version_; }; @@ -87,6 +92,7 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { Common::CallbackHandle* tc_update_callback_handle_{}; Secret::CertificateValidationContextConfigProviderSharedPtr certificate_validation_context_provider_; + Ssl::PrivateKeyOperationsProviderSharedPtr private_key_provider_; // Handle for certificate validation context dynamic secret callback. Common::CallbackHandle* cvc_update_callback_handle_{}; Common::CallbackHandle* cvc_validation_callback_handle_{}; diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 61c14856a58a..97a94c5ceaae 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -48,7 +48,8 @@ bool cbsContainsU16(CBS& cbs, uint16_t n) { ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, TimeSource& time_source) : scope_(scope), stats_(generateStats(scope)), time_source_(time_source), - tls_max_version_(config.maxProtocolVersion()) { + tls_max_version_(config.maxProtocolVersion()), + private_key_provider_(config.privateKeyOperationsProvider()) { const auto tls_certificates = config.tlsCertificates(); tls_contexts_.resize(std::max(1UL, tls_certificates.size())); diff --git a/source/extensions/transport_sockets/tls/context_impl.h b/source/extensions/transport_sockets/tls/context_impl.h index b8f074a4b880..290c3a582b89 100644 --- a/source/extensions/transport_sockets/tls/context_impl.h +++ b/source/extensions/transport_sockets/tls/context_impl.h @@ -7,6 +7,7 @@ #include "envoy/ssl/context.h" #include "envoy/ssl/context_config.h" +#include "envoy/ssl/private_key/private_key.h" #include "envoy/stats/scope.h" #include "envoy/stats/stats_macros.h" @@ -73,6 +74,10 @@ class ContextImpl : public virtual Envoy::Ssl::Context { SslStats& stats() { return stats_; } + Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr getPrivateKeyOperationsProvider() { + return private_key_provider_; + } + // Ssl::Context size_t daysUntilFirstCertExpires() const override; Envoy::Ssl::CertificateDetailsPtr getCaCertInformation() const override; @@ -159,6 +164,7 @@ class ContextImpl : public virtual Envoy::Ssl::Context { std::string cert_chain_file_path_; TimeSource& time_source_; const unsigned tls_max_version_; + Ssl::PrivateKeyOperationsProviderSharedPtr private_key_provider_; }; typedef std::shared_ptr ContextImplSharedPtr; diff --git a/source/extensions/transport_sockets/tls/private_key/BUILD b/source/extensions/transport_sockets/tls/private_key/BUILD new file mode 100644 index 000000000000..abf0d022089b --- /dev/null +++ b/source/extensions/transport_sockets/tls/private_key/BUILD @@ -0,0 +1,24 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "private_key_manager_lib", + srcs = [ + "private_key_manager_impl.cc", + ], + hdrs = [ + "private_key_manager_impl.h", + ], + deps = [ + "//include/envoy/event:dispatcher_interface", + "//include/envoy/ssl/private_key:private_key_interface", + "@envoy_api//envoy/api/v2/core:config_source_cc", + ], +) diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc new file mode 100644 index 000000000000..1e10a081880f --- /dev/null +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc @@ -0,0 +1,23 @@ +#include "extensions/transport_sockets/tls/private_key/private_key_manager_impl.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr +PrivateKeyOperationsManagerImpl::findPrivateKeyOperationsProvider( + const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name) { + + (void)config_name; + (void)config_source; + + // TODO(ipuustin): implement this. + + return nullptr; +} + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h new file mode 100644 index 000000000000..11de0949a4a3 --- /dev/null +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h @@ -0,0 +1,21 @@ +#pragma once + +#include "envoy/api/v2/core/config_source.pb.h" +#include "envoy/ssl/private_key/private_key.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +class PrivateKeyOperationsManagerImpl : public virtual Ssl::PrivateKeyOperationsManager { +public: + Ssl::PrivateKeyOperationsProviderSharedPtr + findPrivateKeyOperationsProvider(const envoy::api::v2::core::ConfigSource& config_source, + const std::string& config_name) override; +}; + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 2918c8afff4d..66a4411f0a9f 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -60,6 +60,15 @@ void SslSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& c ASSERT(!callbacks_); callbacks_ = &callbacks; + provider_ = ctx_->getPrivateKeyOperationsProvider(); + if (provider_) { + ops_ = provider_->getPrivateKeyOperations(*this, callbacks_->connection().dispatcher()); + Ssl::PrivateKeyMethodSharedPtr private_key_methods = ops_->getPrivateKeyMethods(ssl_.get()); + if (private_key_methods) { + SSL_set_private_key_method(ssl_.get(), private_key_methods.get()); + } + } + BIO* bio = BIO_new_socket(callbacks_->ioHandle().fd(), 0); SSL_set_bio(ssl_.get(), bio, bio); } @@ -124,6 +133,21 @@ Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { return {action, bytes_read, end_stream}; } +void SslSocket::complete(Envoy::Ssl::PrivateKeyOperationStatus status) { + if (status == Envoy::Ssl::PrivateKeyOperationStatus::Success) { + ENVOY_CONN_LOG(debug, "async handshake complete", callbacks_->connection()); + async_handshake_in_progress_ = false; + if (!handshake_complete_) { + // It's possible that the async call comes in later, but the handshare has been retried from + // doWrite or similar. */ + doHandshake(); + } + } else { + ENVOY_CONN_LOG(debug, "async handshake failed", callbacks_->connection()); + callbacks_->raiseEvent(Network::ConnectionEvent::LocalClose); + } +} + PostIoAction SslSocket::doHandshake() { ASSERT(!handshake_complete_); int rc = SSL_do_handshake(ssl_.get()); @@ -139,12 +163,18 @@ PostIoAction SslSocket::doHandshake() { : PostIoAction::Close; } else { int err = SSL_get_error(ssl_.get(), rc); - ENVOY_CONN_LOG(debug, "handshake error: {}", callbacks_->connection(), err); switch (err) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: + ENVOY_CONN_LOG(debug, "handshake expecting {}", callbacks_->connection(), + err == SSL_ERROR_WANT_READ ? "read" : "write"); + return PostIoAction::KeepOpen; + case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION: + ENVOY_CONN_LOG(debug, "handshake continued asynchronously", callbacks_->connection()); + async_handshake_in_progress_ = true; return PostIoAction::KeepOpen; default: + ENVOY_CONN_LOG(debug, "handshake error: {}", callbacks_->connection(), err); drainErrorQueue(); return PostIoAction::Close; } @@ -238,7 +268,7 @@ Network::IoResult SslSocket::doWrite(Buffer::Instance& write_buffer, bool end_st void SslSocket::onConnected() { ASSERT(!handshake_complete_); } void SslSocket::shutdownSsl() { - ASSERT(handshake_complete_); + ASSERT(handshake_complete_ || async_handshake_in_progress_); if (!shutdown_sent_ && callbacks_->connection().state() != Network::Connection::State::Closed) { int rc = SSL_shutdown(ssl_.get()); ENVOY_CONN_LOG(debug, "SSL shutdown: rc={}", callbacks_->connection(), rc); diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index ccae3a144e0b..22c513765481 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -6,6 +6,7 @@ #include "envoy/network/connection.h" #include "envoy/network/transport_socket.h" #include "envoy/secret/secret_callbacks.h" +#include "envoy/ssl/private_key/private_key_callbacks.h" #include "envoy/stats/scope.h" #include "envoy/stats/stats_macros.h" @@ -41,6 +42,7 @@ enum class InitialState { Client, Server }; class SslSocket : public Network::TransportSocket, public Envoy::Ssl::ConnectionInfo, + public Envoy::Ssl::PrivateKeyOperationsCallbacks, protected Logger::Loggable { public: SslSocket(Envoy::Ssl::ContextSharedPtr ctx, InitialState state, @@ -72,6 +74,9 @@ class SslSocket : public Network::TransportSocket, void onConnected() override; const Ssl::ConnectionInfo* ssl() const override { return this; } + // Ssl::PrivateKeyOperationsCallbacks + void complete(Envoy::Ssl::PrivateKeyOperationStatus status) override; + SSL* rawSslForTest() const { return ssl_.get(); } private: @@ -89,6 +94,9 @@ class SslSocket : public Network::TransportSocket, mutable std::string cached_sha_256_peer_certificate_digest_; mutable std::string cached_url_encoded_pem_encoded_peer_certificate_; mutable std::string cached_url_encoded_pem_encoded_peer_cert_chain_; + Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr provider_; + Envoy::Ssl::PrivateKeyOperationsPtr ops_; + bool async_handshake_in_progress_{}; }; class ClientSslSocketFactory : public Network::TransportSocketFactory, From 84b6fe1895547494b6eeddcb6041a51732c6577f Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 27 Mar 2019 15:59:33 +0200 Subject: [PATCH 02/56] tls: add BoringSSL private key operations provider manager support. The private key operations manager allows extensions to register private key operations provider factories. These factories in turn create providers for individual SSL contexts. Signed-off-by: Ismo Puustinen --- api/envoy/api/v2/auth/cert.proto | 7 ++++++ include/envoy/ssl/context_manager.h | 7 ++++++ include/envoy/ssl/private_key/BUILD | 9 ++++++++ include/envoy/ssl/private_key/private_key.h | 16 ++++++++++---- .../ssl/private_key/private_key_config.h | 22 +++++++++++++++++++ source/extensions/transport_sockets/tls/BUILD | 1 + .../tls/context_config_impl.cc | 13 +++++++---- .../tls/context_manager_impl.h | 7 ++++++ .../transport_sockets/tls/private_key/BUILD | 2 ++ .../private_key/private_key_manager_impl.cc | 18 ++++++++++----- .../private_key/private_key_manager_impl.h | 7 +++--- 11 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 include/envoy/ssl/private_key/private_key_config.h diff --git a/api/envoy/api/v2/auth/cert.proto b/api/envoy/api/v2/auth/cert.proto index 526caf292829..6caaadc568ee 100644 --- a/api/envoy/api/v2/auth/cert.proto +++ b/api/envoy/api/v2/auth/cert.proto @@ -147,6 +147,11 @@ message TlsSessionTicketKeys { repeated core.DataSource keys = 1 [(validate.rules).repeated .min_items = 1]; } +message PrivateKeyOperations { + string private_key_provider = 1; + core.ConfigSource private_key_provider_config = 2; +} + message CertificateValidationContext { // TLS certificate data containing certificate authority certificates to use in verifying // a presented peer certificate (e.g. server certificate for clusters or client certificate @@ -315,6 +320,8 @@ message CommonTlsContext { repeated string alpn_protocols = 4; reserved 5; + + PrivateKeyOperations private_key_operations = 9; } message UpstreamTlsContext { diff --git a/include/envoy/ssl/context_manager.h b/include/envoy/ssl/context_manager.h index 6f55d3477d96..36e66aef96b2 100644 --- a/include/envoy/ssl/context_manager.h +++ b/include/envoy/ssl/context_manager.h @@ -4,6 +4,7 @@ #include "envoy/ssl/context.h" #include "envoy/ssl/context_config.h" +#include "envoy/ssl/private_key/private_key.h" #include "envoy/stats/scope.h" namespace Envoy { @@ -38,6 +39,12 @@ class ContextManager { * Iterate through all currently allocated contexts. */ virtual void iterateContexts(std::function callback) PURE; + + /** + * Access the private key operations manager, which is part of SSL + * context manager. + */ + virtual PrivateKeyOperationsManager& privateKeyOperationsManager() PURE; }; } // namespace Ssl diff --git a/include/envoy/ssl/private_key/BUILD b/include/envoy/ssl/private_key/BUILD index d65bdf6ee1c5..5edb662d6514 100644 --- a/include/envoy/ssl/private_key/BUILD +++ b/include/envoy/ssl/private_key/BUILD @@ -19,6 +19,15 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "private_key_config_interface", + hdrs = ["private_key_config.h"], + deps = [ + ":private_key_interface", + "//include/envoy/registry", + ], +) + envoy_cc_library( name = "private_key_callbacks_interface", hdrs = ["private_key_callbacks.h"], diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index 9e992bb0cfad..c5f1da7cfa8f 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -11,6 +11,12 @@ #include "openssl/ssl.h" namespace Envoy { +namespace Server { +namespace Configuration { +class TransportSocketFactoryContext; +} // namespace Configuration +} // namespace Server + namespace Ssl { typedef std::shared_ptr PrivateKeyMethodSharedPtr; @@ -60,15 +66,17 @@ class PrivateKeyOperationsManager { * * @param config_source a protobuf message object containing a TLS config source. * @param config_name a name that uniquely refers to the private key operations provider. + * @param private_key_provider_context context that provides components for creating and + * initializing connections for keyless TLS etc. * @return TlsPrivateKeyOperationsProvider the private key operations provider, or nullptr if * no provider can be used with the context configuration. */ - virtual PrivateKeyOperationsProviderSharedPtr - findPrivateKeyOperationsProvider(const envoy::api::v2::core::ConfigSource& config_source, - const std::string& config_name) PURE; + virtual PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProvider( + const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name, + Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) PURE; }; -typedef std::shared_ptr PrivateKeyOperationsManagerSharedPtr; +// typedef std::shared_ptr PrivateKeyOperationsManagerSharedPtr; } // namespace Ssl } // namespace Envoy diff --git a/include/envoy/ssl/private_key/private_key_config.h b/include/envoy/ssl/private_key/private_key_config.h new file mode 100644 index 000000000000..4b15fd9f2c9a --- /dev/null +++ b/include/envoy/ssl/private_key/private_key_config.h @@ -0,0 +1,22 @@ +#pragma once + +#include "envoy/api/v2/core/config_source.pb.h" +#include "envoy/registry/registry.h" +#include "envoy/ssl/private_key/private_key.h" + +namespace Envoy { +namespace Ssl { + +// Base class which the private key operation provider implementations can register. + +class PrivateKeyOperationsProviderInstanceFactory { +public: + virtual ~PrivateKeyOperationsProviderInstanceFactory() {} + virtual PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProviderInstance( + const std::string name, const envoy::api::v2::core::ConfigSource& config_source, + Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) PURE; + virtual std::string name() const PURE; +}; + +} // namespace Ssl +} // namespace Envoy \ No newline at end of file diff --git a/source/extensions/transport_sockets/tls/BUILD b/source/extensions/transport_sockets/tls/BUILD index ee8f2fabc7d7..030dfe95de85 100644 --- a/source/extensions/transport_sockets/tls/BUILD +++ b/source/extensions/transport_sockets/tls/BUILD @@ -102,6 +102,7 @@ envoy_cc_library( "//source/common/common:hex_lib", "//source/common/common:utility_lib", "//source/common/protobuf:utility_lib", + "//source/extensions/transport_sockets/tls/private_key:private_key_manager_lib", "@envoy_api//envoy/admin/v2alpha:certs_cc", ], ) diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index b058ab3608dc..f31e42b77e9b 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -107,11 +107,16 @@ getCertificateValidationContextConfigProvider( Ssl::PrivateKeyOperationsProviderSharedPtr getPrivateKeyOperationsProvider( const envoy::api::v2::auth::CommonTlsContext& config, Server::Configuration::TransportSocketFactoryContext& factory_context) { - (void)config; - (void)factory_context; - // TODO: get PrivateKeyOperationsManager from the factory_context and - // get a provider based on the provider name in the TLS context. + const auto private_key_operations_config = config.private_key_operations(); + + if (private_key_operations_config.private_key_provider() != "") { + return factory_context.sslContextManager() + .privateKeyOperationsManager() + .createPrivateKeyOperationsProvider( + private_key_operations_config.private_key_provider_config(), + private_key_operations_config.private_key_provider(), factory_context); + } return nullptr; } diff --git a/source/extensions/transport_sockets/tls/context_manager_impl.h b/source/extensions/transport_sockets/tls/context_manager_impl.h index 2bb82342940c..f67884434169 100644 --- a/source/extensions/transport_sockets/tls/context_manager_impl.h +++ b/source/extensions/transport_sockets/tls/context_manager_impl.h @@ -5,8 +5,11 @@ #include "envoy/common/time.h" #include "envoy/ssl/context_manager.h" +#include "envoy/ssl/private_key/private_key.h" #include "envoy/stats/scope.h" +#include "extensions/transport_sockets/tls/private_key/private_key_manager_impl.h" + namespace Envoy { namespace Extensions { namespace TransportSockets { @@ -33,11 +36,15 @@ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { const std::vector& server_names) override; size_t daysUntilFirstCertExpires() const override; void iterateContexts(std::function callback) override; + Ssl::PrivateKeyOperationsManager& privateKeyOperationsManager() override { + return private_key_operations_manager_; + }; private: void removeEmptyContexts(); TimeSource& time_source_; std::list> contexts_; + PrivateKeyOperationsManagerImpl private_key_operations_manager_{}; }; } // namespace Tls diff --git a/source/extensions/transport_sockets/tls/private_key/BUILD b/source/extensions/transport_sockets/tls/private_key/BUILD index abf0d022089b..2593478ec0d4 100644 --- a/source/extensions/transport_sockets/tls/private_key/BUILD +++ b/source/extensions/transport_sockets/tls/private_key/BUILD @@ -18,6 +18,8 @@ envoy_cc_library( ], deps = [ "//include/envoy/event:dispatcher_interface", + "//include/envoy/registry", + "//include/envoy/ssl/private_key:private_key_config_interface", "//include/envoy/ssl/private_key:private_key_interface", "@envoy_api//envoy/api/v2/core:config_source_cc", ], diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc index 1e10a081880f..f04db266c94e 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc @@ -1,18 +1,26 @@ #include "extensions/transport_sockets/tls/private_key/private_key_manager_impl.h" +#include "envoy/registry/registry.h" + namespace Envoy { namespace Extensions { namespace TransportSockets { namespace Tls { Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr -PrivateKeyOperationsManagerImpl::findPrivateKeyOperationsProvider( - const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name) { +PrivateKeyOperationsManagerImpl::createPrivateKeyOperationsProvider( + const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name, + Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) { - (void)config_name; - (void)config_source; + Ssl::PrivateKeyOperationsProviderInstanceFactory* factory = + Registry::FactoryRegistry::getFactory( + config_name); - // TODO(ipuustin): implement this. + // Create a new provider instance with the configuration. + if (factory) { + return factory->createPrivateKeyOperationsProviderInstance(config_name, config_source, + private_key_provider_context); + } return nullptr; } diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h index 11de0949a4a3..f32bc58ff746 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h @@ -2,6 +2,7 @@ #include "envoy/api/v2/core/config_source.pb.h" #include "envoy/ssl/private_key/private_key.h" +#include "envoy/ssl/private_key/private_key_config.h" namespace Envoy { namespace Extensions { @@ -10,9 +11,9 @@ namespace Tls { class PrivateKeyOperationsManagerImpl : public virtual Ssl::PrivateKeyOperationsManager { public: - Ssl::PrivateKeyOperationsProviderSharedPtr - findPrivateKeyOperationsProvider(const envoy::api::v2::core::ConfigSource& config_source, - const std::string& config_name) override; + Ssl::PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProvider( + const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name, + Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) override; }; } // namespace Tls From 127bff524e3c978ddbdb6f9d57ad605c1987bb53 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 27 Mar 2019 17:14:43 +0200 Subject: [PATCH 03/56] tests: add mock privateKeyOperationsManager() method. Signed-off-by: Ismo Puustinen --- test/mocks/ssl/mocks.h | 1 + 1 file changed, 1 insertion(+) diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index e6dc9b4d1315..593b0b6b0b3c 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -28,6 +28,7 @@ class MockContextManager : public ContextManager { const std::vector& server_names)); MOCK_CONST_METHOD0(daysUntilFirstCertExpires, size_t()); MOCK_METHOD1(iterateContexts, void(std::function callback)); + MOCK_METHOD0(privateKeyOperationsManager, Ssl::PrivateKeyOperationsManager&()); }; class MockConnectionInfo : public ConnectionInfo { From e58ddfd8702bd8c593d36dd1ead39bc174b16c93 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Thu, 28 Mar 2019 17:02:51 +0200 Subject: [PATCH 04/56] tls: use typed config for private key provider configuration. Signed-off-by: Ismo Puustinen --- api/envoy/api/v2/auth/cert.proto | 9 +++++++-- include/envoy/ssl/private_key/BUILD | 2 +- include/envoy/ssl/private_key/private_key.h | 4 ++-- include/envoy/ssl/private_key/private_key_config.h | 6 +++--- .../transport_sockets/tls/context_config_impl.cc | 6 ++---- .../extensions/transport_sockets/tls/private_key/BUILD | 2 +- .../tls/private_key/private_key_manager_impl.cc | 6 +++--- .../tls/private_key/private_key_manager_impl.h | 5 +++-- 8 files changed, 22 insertions(+), 18 deletions(-) diff --git a/api/envoy/api/v2/auth/cert.proto b/api/envoy/api/v2/auth/cert.proto index 6caaadc568ee..5af820876f04 100644 --- a/api/envoy/api/v2/auth/cert.proto +++ b/api/envoy/api/v2/auth/cert.proto @@ -10,6 +10,7 @@ option go_package = "auth"; import "envoy/api/v2/core/base.proto"; import "envoy/api/v2/core/config_source.proto"; +import "google/protobuf/any.proto"; import "google/protobuf/wrappers.proto"; import "validate/validate.proto"; @@ -148,8 +149,12 @@ message TlsSessionTicketKeys { } message PrivateKeyOperations { - string private_key_provider = 1; - core.ConfigSource private_key_provider_config = 2; + // Private key operations provider name. The name must match a + // supported private key operations provider type. + string provider_name = 1 [(validate.rules).string.min_bytes = 1]; + + // Private key operations provider specific configuration. + google.protobuf.Any typed_config = 2; } message CertificateValidationContext { diff --git a/include/envoy/ssl/private_key/BUILD b/include/envoy/ssl/private_key/BUILD index 5edb662d6514..4bb651d1f8d3 100644 --- a/include/envoy/ssl/private_key/BUILD +++ b/include/envoy/ssl/private_key/BUILD @@ -15,7 +15,7 @@ envoy_cc_library( deps = [ ":private_key_callbacks_interface", "//include/envoy/event:dispatcher_interface", - "@envoy_api//envoy/api/v2/core:config_source_cc", + "@envoy_api//envoy/api/v2/auth:cert_cc", ], ) diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index c5f1da7cfa8f..7f383f0a8d3c 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -3,7 +3,7 @@ #include #include -#include "envoy/api/v2/core/config_source.pb.h" +#include "envoy/api/v2/auth/cert.pb.h" #include "envoy/common/pure.h" #include "envoy/event/dispatcher.h" #include "envoy/ssl/private_key/private_key_callbacks.h" @@ -72,7 +72,7 @@ class PrivateKeyOperationsManager { * no provider can be used with the context configuration. */ virtual PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProvider( - const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name, + const envoy::api::v2::auth::PrivateKeyOperations& message, Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) PURE; }; diff --git a/include/envoy/ssl/private_key/private_key_config.h b/include/envoy/ssl/private_key/private_key_config.h index 4b15fd9f2c9a..f14a10d13b17 100644 --- a/include/envoy/ssl/private_key/private_key_config.h +++ b/include/envoy/ssl/private_key/private_key_config.h @@ -1,6 +1,6 @@ #pragma once -#include "envoy/api/v2/core/config_source.pb.h" +#include "envoy/api/v2/auth/cert.pb.h" #include "envoy/registry/registry.h" #include "envoy/ssl/private_key/private_key.h" @@ -13,10 +13,10 @@ class PrivateKeyOperationsProviderInstanceFactory { public: virtual ~PrivateKeyOperationsProviderInstanceFactory() {} virtual PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProviderInstance( - const std::string name, const envoy::api::v2::core::ConfigSource& config_source, + const envoy::api::v2::auth::PrivateKeyOperations& message, Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) PURE; virtual std::string name() const PURE; }; } // namespace Ssl -} // namespace Envoy \ No newline at end of file +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index f31e42b77e9b..329b1f0056fb 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -110,12 +110,10 @@ Ssl::PrivateKeyOperationsProviderSharedPtr getPrivateKeyOperationsProvider( const auto private_key_operations_config = config.private_key_operations(); - if (private_key_operations_config.private_key_provider() != "") { + if (private_key_operations_config.provider_name() != "") { return factory_context.sslContextManager() .privateKeyOperationsManager() - .createPrivateKeyOperationsProvider( - private_key_operations_config.private_key_provider_config(), - private_key_operations_config.private_key_provider(), factory_context); + .createPrivateKeyOperationsProvider(private_key_operations_config, factory_context); } return nullptr; } diff --git a/source/extensions/transport_sockets/tls/private_key/BUILD b/source/extensions/transport_sockets/tls/private_key/BUILD index 2593478ec0d4..2c181249b5d8 100644 --- a/source/extensions/transport_sockets/tls/private_key/BUILD +++ b/source/extensions/transport_sockets/tls/private_key/BUILD @@ -21,6 +21,6 @@ envoy_cc_library( "//include/envoy/registry", "//include/envoy/ssl/private_key:private_key_config_interface", "//include/envoy/ssl/private_key:private_key_interface", - "@envoy_api//envoy/api/v2/core:config_source_cc", + "@envoy_api//envoy/api/v2/auth:cert_cc", ], ) diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc index f04db266c94e..2297a1529f77 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc @@ -9,16 +9,16 @@ namespace Tls { Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr PrivateKeyOperationsManagerImpl::createPrivateKeyOperationsProvider( - const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name, + const envoy::api::v2::auth::PrivateKeyOperations& message, Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) { Ssl::PrivateKeyOperationsProviderInstanceFactory* factory = Registry::FactoryRegistry::getFactory( - config_name); + message.provider_name()); // Create a new provider instance with the configuration. if (factory) { - return factory->createPrivateKeyOperationsProviderInstance(config_name, config_source, + return factory->createPrivateKeyOperationsProviderInstance(message, private_key_provider_context); } diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h index f32bc58ff746..dddb2ba00a32 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h @@ -1,6 +1,6 @@ #pragma once -#include "envoy/api/v2/core/config_source.pb.h" +#include "envoy/api/v2/auth/cert.pb.h" #include "envoy/ssl/private_key/private_key.h" #include "envoy/ssl/private_key/private_key_config.h" @@ -11,8 +11,9 @@ namespace Tls { class PrivateKeyOperationsManagerImpl : public virtual Ssl::PrivateKeyOperationsManager { public: + // Ssl::PrivateKeyOperationsManager Ssl::PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProvider( - const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name, + const envoy::api::v2::auth::PrivateKeyOperations& message, Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) override; }; From 8d91d594433501707cc45e217a3a30231669d1dc Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 3 Apr 2019 13:24:55 +0300 Subject: [PATCH 05/56] tls: fixed handling of getPrivateKeyMethods() failure. Also nicer handling of Protobuf message existence. Signed-off-by: Ismo Puustinen --- .../transport_sockets/tls/context_config_impl.cc | 4 ++-- .../extensions/transport_sockets/tls/ssl_socket.cc | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index 329b1f0056fb..3cdcb4ae3211 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -108,9 +108,9 @@ Ssl::PrivateKeyOperationsProviderSharedPtr getPrivateKeyOperationsProvider( const envoy::api::v2::auth::CommonTlsContext& config, Server::Configuration::TransportSocketFactoryContext& factory_context) { - const auto private_key_operations_config = config.private_key_operations(); + if (config.has_private_key_operations()) { + const auto private_key_operations_config = config.private_key_operations(); - if (private_key_operations_config.provider_name() != "") { return factory_context.sslContextManager() .privateKeyOperationsManager() .createPrivateKeyOperationsProvider(private_key_operations_config, factory_context); diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 66a4411f0a9f..e73dd9a8a3fb 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -63,9 +63,11 @@ void SslSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& c provider_ = ctx_->getPrivateKeyOperationsProvider(); if (provider_) { ops_ = provider_->getPrivateKeyOperations(*this, callbacks_->connection().dispatcher()); - Ssl::PrivateKeyMethodSharedPtr private_key_methods = ops_->getPrivateKeyMethods(ssl_.get()); - if (private_key_methods) { - SSL_set_private_key_method(ssl_.get(), private_key_methods.get()); + if (ops_) { + Ssl::PrivateKeyMethodSharedPtr private_key_methods = ops_->getPrivateKeyMethods(ssl_.get()); + if (private_key_methods) { + SSL_set_private_key_method(ssl_.get(), private_key_methods.get()); + } } } @@ -134,9 +136,11 @@ Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { } void SslSocket::complete(Envoy::Ssl::PrivateKeyOperationStatus status) { + ASSERT(async_handshake_in_progress_); + async_handshake_in_progress_ = false; + if (status == Envoy::Ssl::PrivateKeyOperationStatus::Success) { ENVOY_CONN_LOG(debug, "async handshake complete", callbacks_->connection()); - async_handshake_in_progress_ = false; if (!handshake_complete_) { // It's possible that the async call comes in later, but the handshare has been retried from // doWrite or similar. */ From 0bd6f25000de020bf9485023ef6afe02665f750c Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 15 Apr 2019 16:43:10 +0300 Subject: [PATCH 06/56] tls: change BoringSSL private key support to be certificate-based. Signed-off-by: Ismo Puustinen --- api/envoy/api/v2/auth/cert.proto | 29 +++--- include/envoy/ssl/BUILD | 4 +- include/envoy/ssl/context_config.h | 7 -- include/envoy/ssl/private_key/private_key.h | 45 +++++---- .../ssl/private_key/private_key_config.h | 2 +- include/envoy/ssl/tls_certificate_config.h | 6 ++ source/common/ssl/BUILD | 2 + .../common/ssl/tls_certificate_config_impl.cc | 29 +++++- .../common/ssl/tls_certificate_config_impl.h | 12 ++- source/extensions/transport_sockets/tls/BUILD | 1 - .../tls/context_config_impl.cc | 37 +++----- .../tls/context_config_impl.h | 6 -- .../transport_sockets/tls/context_impl.cc | 95 ++++++++++++------- .../transport_sockets/tls/context_impl.h | 11 ++- .../private_key/private_key_manager_impl.cc | 2 +- .../private_key/private_key_manager_impl.h | 2 +- .../transport_sockets/tls/ssl_socket.cc | 17 ++-- .../transport_sockets/tls/ssl_socket.h | 3 +- test/common/secret/sds_api_test.cc | 2 +- .../common/secret/secret_manager_impl_test.cc | 4 +- 20 files changed, 191 insertions(+), 125 deletions(-) diff --git a/api/envoy/api/v2/auth/cert.proto b/api/envoy/api/v2/auth/cert.proto index 5af820876f04..953239b6ff6c 100644 --- a/api/envoy/api/v2/auth/cert.proto +++ b/api/envoy/api/v2/auth/cert.proto @@ -11,6 +11,7 @@ import "envoy/api/v2/core/base.proto"; import "envoy/api/v2/core/config_source.proto"; import "google/protobuf/any.proto"; +import "google/protobuf/struct.proto"; import "google/protobuf/wrappers.proto"; import "validate/validate.proto"; @@ -103,6 +104,19 @@ message TlsParameters { repeated string ecdh_curves = 4; } +message PrivateKeyMethod { + // Private key operations provider name. The name must match a + // supported private key operations provider type. + string provider_name = 1 [(validate.rules).string.min_bytes = 1]; + + // Private key operations provider specific configuration. + oneof config_type { + google.protobuf.Struct config = 2; + + google.protobuf.Any typed_config = 4; + } +} + message TlsCertificate { // The TLS certificate chain. core.DataSource certificate_chain = 1; @@ -119,6 +133,10 @@ message TlsCertificate { // [#not-implemented-hide:] repeated core.DataSource signed_certificate_timestamp = 5; + + // BoringSSL private key operations. This is an alternative to private_key field. This can't be + // marked as "oneof" due to API compatibility reasons. + PrivateKeyMethod private_key_method = 6; } message TlsSessionTicketKeys { @@ -148,15 +166,6 @@ message TlsSessionTicketKeys { repeated core.DataSource keys = 1 [(validate.rules).repeated .min_items = 1]; } -message PrivateKeyOperations { - // Private key operations provider name. The name must match a - // supported private key operations provider type. - string provider_name = 1 [(validate.rules).string.min_bytes = 1]; - - // Private key operations provider specific configuration. - google.protobuf.Any typed_config = 2; -} - message CertificateValidationContext { // TLS certificate data containing certificate authority certificates to use in verifying // a presented peer certificate (e.g. server certificate for clusters or client certificate @@ -325,8 +334,6 @@ message CommonTlsContext { repeated string alpn_protocols = 4; reserved 5; - - PrivateKeyOperations private_key_operations = 9; } message UpstreamTlsContext { diff --git a/include/envoy/ssl/BUILD b/include/envoy/ssl/BUILD index 8861e8b4c748..b09a6d09235e 100644 --- a/include/envoy/ssl/BUILD +++ b/include/envoy/ssl/BUILD @@ -31,7 +31,6 @@ envoy_cc_library( deps = [ ":certificate_validation_context_config_interface", ":tls_certificate_config_interface", - "//include/envoy/ssl/private_key:private_key_interface", ], ) @@ -48,6 +47,9 @@ envoy_cc_library( envoy_cc_library( name = "tls_certificate_config_interface", hdrs = ["tls_certificate_config.h"], + deps = [ + "//include/envoy/ssl/private_key:private_key_interface", + ], ) envoy_cc_library( diff --git a/include/envoy/ssl/context_config.h b/include/envoy/ssl/context_config.h index 1a385c14096b..5f9063e85d0c 100644 --- a/include/envoy/ssl/context_config.h +++ b/include/envoy/ssl/context_config.h @@ -6,7 +6,6 @@ #include "envoy/common/pure.h" #include "envoy/ssl/certificate_validation_context_config.h" -#include "envoy/ssl/private_key/private_key.h" #include "envoy/ssl/tls_certificate_config.h" namespace Envoy { @@ -70,12 +69,6 @@ class ContextConfig { * @param callback callback that is executed by context config. */ virtual void setSecretUpdateCallback(std::function callback) PURE; - - /** - * @return the configured BoringSSL private key operations provider. - */ - virtual const Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr - privateKeyOperationsProvider() const PURE; }; class ClientContextConfig : public virtual ContextConfig { diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index 7f383f0a8d3c..169ccecf0509 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -13,25 +13,27 @@ namespace Envoy { namespace Server { namespace Configuration { +// Prevent a dependency loop with the forward declaration. class TransportSocketFactoryContext; } // namespace Configuration } // namespace Server namespace Ssl { -typedef std::shared_ptr PrivateKeyMethodSharedPtr; +typedef std::shared_ptr BoringSslPrivateKeyMethodSharedPtr; class PrivateKeyOperations { public: virtual ~PrivateKeyOperations() {} /** - * Get the private key asynchronous methods from the provider, if the context can be handled by - * the provider. - * @param ssl a SSL connection object. - * @return private key methods, or nullptr if regular TLS processing should happen. + * Associate the private key operations instance with a SSL connection. + * @param ssl a SSL connection object. The BoringSSL private key API + * doesn't allow passing user data to the asynchronous functions as a + * function parameter, so this enables the private key method provider + * to use SSL connection custom data fields instead. */ - virtual PrivateKeyMethodSharedPtr getPrivateKeyMethods(SSL* ssl) PURE; + virtual bool associateWithSsl(SSL* ssl) PURE; }; typedef std::unique_ptr PrivateKeyOperationsPtr; @@ -42,13 +44,21 @@ class PrivateKeyOperationsProvider { /** * Get a private key operations instance from the provider. - * @param cb a callbacks object, whose "complete" method will be invoked when the asynchronous - * processing is complete. + * @param cb a callbacks object, whose "complete" method will be invoked + * when the asynchronous processing is complete. * @param dispatcher supplies the owning thread's dispatcher. - * @return the private key operations. + * @return the private key operations instance. */ - virtual PrivateKeyOperationsPtr getPrivateKeyOperations(PrivateKeyOperationsCallbacks& cb, + virtual PrivateKeyOperationsPtr getPrivateKeyOperations(SSL* ssl, + PrivateKeyOperationsCallbacks& cb, Event::Dispatcher& dispatcher) PURE; + + /** + * Get the private key methods from the provider. + * @return the private key methods associated with this provider and + * configuration. + */ + virtual BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() PURE; }; typedef std::shared_ptr PrivateKeyOperationsProviderSharedPtr; @@ -64,19 +74,18 @@ class PrivateKeyOperationsManager { /** * Finds and returns a private key operations provider for BoringSSL. * - * @param config_source a protobuf message object containing a TLS config source. - * @param config_name a name that uniquely refers to the private key operations provider. + * @param message a protobuf message object containing a + * PrivateKeyMethod message. * @param private_key_provider_context context that provides components for creating and * initializing connections for keyless TLS etc. - * @return TlsPrivateKeyOperationsProvider the private key operations provider, or nullptr if + * @return PrivateKeyOperationsProvider the private key operations provider, or nullptr if * no provider can be used with the context configuration. */ - virtual PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProvider( - const envoy::api::v2::auth::PrivateKeyOperations& message, - Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) PURE; + virtual PrivateKeyOperationsProviderSharedPtr + createPrivateKeyOperationsProvider(const envoy::api::v2::auth::PrivateKeyMethod& message, + Envoy::Server::Configuration::TransportSocketFactoryContext& + private_key_provider_context) PURE; }; -// typedef std::shared_ptr PrivateKeyOperationsManagerSharedPtr; - } // namespace Ssl } // namespace Envoy diff --git a/include/envoy/ssl/private_key/private_key_config.h b/include/envoy/ssl/private_key/private_key_config.h index f14a10d13b17..7013148818e0 100644 --- a/include/envoy/ssl/private_key/private_key_config.h +++ b/include/envoy/ssl/private_key/private_key_config.h @@ -13,7 +13,7 @@ class PrivateKeyOperationsProviderInstanceFactory { public: virtual ~PrivateKeyOperationsProviderInstanceFactory() {} virtual PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProviderInstance( - const envoy::api::v2::auth::PrivateKeyOperations& message, + const envoy::api::v2::auth::PrivateKeyMethod& message, Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) PURE; virtual std::string name() const PURE; }; diff --git a/include/envoy/ssl/tls_certificate_config.h b/include/envoy/ssl/tls_certificate_config.h index d72a92c31f82..f98041033eed 100644 --- a/include/envoy/ssl/tls_certificate_config.h +++ b/include/envoy/ssl/tls_certificate_config.h @@ -4,6 +4,7 @@ #include #include "envoy/common/pure.h" +#include "envoy/ssl/private_key/private_key.h" namespace Envoy { namespace Ssl { @@ -34,6 +35,11 @@ class TlsCertificateConfig { */ virtual const std::string& privateKeyPath() const PURE; + /** + * @return private key method provider. + */ + virtual Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr privateKeyMethod() const PURE; + /** * @return a string of password. */ diff --git a/source/common/ssl/BUILD b/source/common/ssl/BUILD index 37dcef2ba8ea..ebbe62647302 100644 --- a/source/common/ssl/BUILD +++ b/source/common/ssl/BUILD @@ -13,7 +13,9 @@ envoy_cc_library( srcs = ["tls_certificate_config_impl.cc"], hdrs = ["tls_certificate_config_impl.h"], deps = [ + "//include/envoy/server:transport_socket_config_interface", "//include/envoy/ssl:tls_certificate_config_interface", + "//include/envoy/ssl/private_key:private_key_interface", "//source/common/common:empty_string", "//source/common/config:datasource_lib", "@envoy_api//envoy/api/v2/auth:cert_cc", diff --git a/source/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index 25f993c38da6..886a28e8933a 100644 --- a/source/common/ssl/tls_certificate_config_impl.cc +++ b/source/common/ssl/tls_certificate_config_impl.cc @@ -1,6 +1,7 @@ #include "common/ssl/tls_certificate_config_impl.h" #include "envoy/common/exception.h" +#include "envoy/server/transport_socket_config.h" #include "common/common/empty_string.h" #include "common/common/fmt.h" @@ -22,9 +23,33 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl( .value_or(private_key_.empty() ? EMPTY_STRING : INLINE_STRING)), password_(Config::DataSource::read(config.password(), true, api)), password_path_(Config::DataSource::getPath(config.password()) - .value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)) { + .value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)) {} - if (certificate_chain_.empty() || private_key_.empty()) { +TlsCertificateConfigImpl::TlsCertificateConfigImpl( + const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api, + bool expect_private_key_method) + : TlsCertificateConfigImpl(config, api) { + { + if (!expect_private_key_method) { + if (certificate_chain_.empty() || private_key_.empty()) { + throw EnvoyException(fmt::format("Failed to load incomplete certificate from {}, {}", + certificate_chain_path_, private_key_path_)); + } + } + } +} + +TlsCertificateConfigImpl::TlsCertificateConfigImpl( + const envoy::api::v2::auth::TlsCertificate& config, + Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) + : TlsCertificateConfigImpl(config, api, true) { + if (config.has_private_key_method()) { + private_key_method_ = + factory_context.sslContextManager() + .privateKeyOperationsManager() + .createPrivateKeyOperationsProvider(config.private_key_method(), factory_context); + } + if (certificate_chain_.empty() || (private_key_.empty() && private_key_method_ == nullptr)) { throw EnvoyException(fmt::format("Failed to load incomplete certificate from {}, {}", certificate_chain_path_, private_key_path_)); } diff --git a/source/common/ssl/tls_certificate_config_impl.h b/source/common/ssl/tls_certificate_config_impl.h index ed664521b187..bd4fdee81e56 100644 --- a/source/common/ssl/tls_certificate_config_impl.h +++ b/source/common/ssl/tls_certificate_config_impl.h @@ -4,6 +4,7 @@ #include "envoy/api/api.h" #include "envoy/api/v2/auth/cert.pb.h" +#include "envoy/ssl/private_key/private_key.h" #include "envoy/ssl/tls_certificate_config.h" namespace Envoy { @@ -11,20 +12,29 @@ namespace Ssl { class TlsCertificateConfigImpl : public TlsCertificateConfig { public: - TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api); + TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api, + bool expect_private_key_method); + TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, + Server::Configuration::TransportSocketFactoryContext& factory_context, + Api::Api& api); const std::string& certificateChain() const override { return certificate_chain_; } const std::string& certificateChainPath() const override { return certificate_chain_path_; } const std::string& privateKey() const override { return private_key_; } const std::string& privateKeyPath() const override { return private_key_path_; } + Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr privateKeyMethod() const override { + return private_key_method_; + } const std::string& password() const override { return password_; } const std::string& passwordPath() const override { return password_path_; } private: + TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api); const std::string certificate_chain_; const std::string certificate_chain_path_; const std::string private_key_; const std::string private_key_path_; + Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr private_key_method_{}; const std::string password_; const std::string password_path_; }; diff --git a/source/extensions/transport_sockets/tls/BUILD b/source/extensions/transport_sockets/tls/BUILD index 030dfe95de85..dd156f04cb1a 100644 --- a/source/extensions/transport_sockets/tls/BUILD +++ b/source/extensions/transport_sockets/tls/BUILD @@ -61,7 +61,6 @@ envoy_cc_library( "//include/envoy/secret:secret_provider_interface", "//include/envoy/server:transport_socket_config_interface", "//include/envoy/ssl:context_config_interface", - "//include/envoy/ssl/private_key:private_key_interface", "//source/common/common:assert_lib", "//source/common/common:empty_string", "//source/common/config:datasource_lib", diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index 3cdcb4ae3211..c3bc8d60a70f 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -26,7 +26,9 @@ std::vector getTlsCertificateConf if (!config.tls_certificates().empty()) { std::vector providers; for (const auto& tls_certificate : config.tls_certificates()) { - if (!tls_certificate.has_certificate_chain() && !tls_certificate.has_private_key()) { + Ssl::PrivateKeyOperationsProviderSharedPtr private_key_ops_provider = nullptr; + if (!tls_certificate.has_private_key_method() && !tls_certificate.has_certificate_chain() && + !tls_certificate.has_private_key()) { continue; } providers.push_back( @@ -104,20 +106,6 @@ getCertificateValidationContextConfigProvider( } } -Ssl::PrivateKeyOperationsProviderSharedPtr getPrivateKeyOperationsProvider( - const envoy::api::v2::auth::CommonTlsContext& config, - Server::Configuration::TransportSocketFactoryContext& factory_context) { - - if (config.has_private_key_operations()) { - const auto private_key_operations_config = config.private_key_operations(); - - return factory_context.sslContextManager() - .privateKeyOperationsManager() - .createPrivateKeyOperationsProvider(private_key_operations_config, factory_context); - } - return nullptr; -} - } // namespace ContextConfigImpl::ContextConfigImpl( @@ -134,7 +122,6 @@ ContextConfigImpl::ContextConfigImpl( tls_certificate_providers_(getTlsCertificateConfigProviders(config, factory_context)), certificate_validation_context_provider_( getCertificateValidationContextConfigProvider(config, factory_context, &default_cvc_)), - private_key_provider_(getPrivateKeyOperationsProvider(config, factory_context)), min_protocol_version_(tlsVersionFromProto(config.tls_params().tls_minimum_protocol_version(), default_min_protocol_version)), max_protocol_version_(tlsVersionFromProto(config.tls_params().tls_maximum_protocol_version(), @@ -159,7 +146,7 @@ ContextConfigImpl::ContextConfigImpl( if (!tls_certificate_providers_.empty()) { for (auto& provider : tls_certificate_providers_) { if (provider->secret() != nullptr) { - tls_certificate_configs_.emplace_back(*provider->secret(), api_); + tls_certificate_configs_.emplace_back(*provider->secret(), factory_context, api_); } } } @@ -185,14 +172,14 @@ void ContextConfigImpl::setSecretUpdateCallback(std::function callback) } // Once tls_certificate_config_ receives new secret, this callback updates // ContextConfigImpl::tls_certificate_config_ with new secret. - tc_update_callback_handle_ = - tls_certificate_providers_[0]->addUpdateCallback([this, callback]() { - // This breaks multiple certificate support, but today SDS is only single cert. - // TODO(htuch): Fix this when SDS goes multi-cert. - tls_certificate_configs_.clear(); - tls_certificate_configs_.emplace_back(*tls_certificate_providers_[0]->secret(), api_); - callback(); - }); + tc_update_callback_handle_ = tls_certificate_providers_[0]->addUpdateCallback([this, + callback]() { + // This breaks multiple certificate support, but today SDS is only single cert. + // TODO(htuch): Fix this when SDS goes multi-cert. + tls_certificate_configs_.clear(); + tls_certificate_configs_.emplace_back(*tls_certificate_providers_[0]->secret(), api_, false); + callback(); + }); } if (certificate_validation_context_provider_) { if (cvc_update_callback_handle_) { diff --git a/source/extensions/transport_sockets/tls/context_config_impl.h b/source/extensions/transport_sockets/tls/context_config_impl.h index 24acd6533356..5efbc0680b67 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.h +++ b/source/extensions/transport_sockets/tls/context_config_impl.h @@ -8,7 +8,6 @@ #include "envoy/secret/secret_provider.h" #include "envoy/server/transport_socket_config.h" #include "envoy/ssl/context_config.h" -#include "envoy/ssl/private_key/private_key.h" #include "common/common/empty_string.h" #include "common/json/json_loader.h" @@ -42,10 +41,6 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { certificateValidationContext() const override { return validation_context_config_.get(); } - const Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr - privateKeyOperationsProvider() const override { - return private_key_provider_; - } unsigned minProtocolVersion() const override { return min_protocol_version_; }; unsigned maxProtocolVersion() const override { return max_protocol_version_; }; @@ -92,7 +87,6 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { Common::CallbackHandle* tc_update_callback_handle_{}; Secret::CertificateValidationContextConfigProviderSharedPtr certificate_validation_context_provider_; - Ssl::PrivateKeyOperationsProviderSharedPtr private_key_provider_; // Handle for certificate validation context dynamic secret callback. Common::CallbackHandle* cvc_update_callback_handle_{}; Common::CallbackHandle* cvc_validation_callback_handle_{}; diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 97a94c5ceaae..92d33dbeba27 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -48,8 +48,7 @@ bool cbsContainsU16(CBS& cbs, uint16_t n) { ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, TimeSource& time_source) : scope_(scope), stats_(generateStats(scope)), time_source_(time_source), - tls_max_version_(config.maxProtocolVersion()), - private_key_provider_(config.privateKeyOperationsProvider()) { + tls_max_version_(config.maxProtocolVersion()) { const auto tls_certificates = config.tlsCertificates(); tls_contexts_.resize(std::max(1UL, tls_certificates.size())); @@ -303,40 +302,58 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c #endif } - // Load private key. - bio.reset(BIO_new_mem_buf(const_cast(tls_certificate.privateKey().data()), - tls_certificate.privateKey().size())); - RELEASE_ASSERT(bio != nullptr, ""); - bssl::UniquePtr pkey(PEM_read_bio_PrivateKey( - bio.get(), nullptr, nullptr, - !tls_certificate.password().empty() ? const_cast(tls_certificate.password().c_str()) - : nullptr)); - if (pkey == nullptr || !SSL_CTX_use_PrivateKey(ctx.ssl_ctx_.get(), pkey.get())) { - throw EnvoyException( - fmt::format("Failed to load private key from {}", tls_certificate.privateKeyPath())); - } + Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr private_key_ops_provider = + tls_certificate.privateKeyMethod(); + // We either have a private key or a BoringSSL private key method provider. + if (private_key_ops_provider) { + ctx.private_key_provider_ = private_key_ops_provider; + // Set the ops provider to this CTX user data, so that we can get it later in ssl_socket.cc. + // The provider has a reference to the private key methods for the context lifetime. + + Ssl::BoringSslPrivateKeyMethodSharedPtr private_key_method = + private_key_ops_provider->getBoringSslPrivateKeyMethod(); + if (private_key_method == nullptr) { + throw EnvoyException( + fmt::format("Failed to get BoringSsl private key method from provider")); + } + SSL_CTX_set_private_key_method(ctx.ssl_ctx_.get(), private_key_method.get()); + } else { + // Load private key. + bio.reset(BIO_new_mem_buf(const_cast(tls_certificate.privateKey().data()), + tls_certificate.privateKey().size())); + RELEASE_ASSERT(bio != nullptr, ""); + bssl::UniquePtr pkey( + PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, + !tls_certificate.password().empty() + ? const_cast(tls_certificate.password().c_str()) + : nullptr)); + if (pkey == nullptr || !SSL_CTX_use_PrivateKey(ctx.ssl_ctx_.get(), pkey.get())) { + throw EnvoyException( + fmt::format("Failed to load private key from {}", tls_certificate.privateKeyPath())); + } #ifdef BORINGSSL_FIPS - // Verify that private keys are passing FIPS pairwise consistency tests. - switch (pkey_id) { - case EVP_PKEY_EC: { - const EC_KEY* ecdsa_private_key = EVP_PKEY_get0_EC_KEY(pkey.get()); - if (!EC_KEY_check_fips(ecdsa_private_key)) { - throw EnvoyException(fmt::format("Failed to load private key from {}, ECDSA key failed " - "pairwise consistency test required in FIPS mode", - tls_certificate.privateKeyPath())); - } - } break; - case EVP_PKEY_RSA: { - RSA* rsa_private_key = EVP_PKEY_get0_RSA(pkey.get()); - if (!RSA_check_fips(rsa_private_key)) { - throw EnvoyException(fmt::format("Failed to load private key from {}, RSA key failed " - "pairwise consistency test required in FIPS mode", - tls_certificate.privateKeyPath())); + // Verify that private keys are passing FIPS pairwise consistency tests. + switch (pkey_id) { + case EVP_PKEY_EC: { + const EC_KEY* ecdsa_private_key = EVP_PKEY_get0_EC_KEY(pkey.get()); + if (!EC_KEY_check_fips(ecdsa_private_key)) { + throw EnvoyException(fmt::format("Failed to load private key from {}, ECDSA key failed " + "pairwise consistency test required in FIPS mode", + tls_certificate.privateKeyPath())); + } + } break; + case EVP_PKEY_RSA: { + RSA* rsa_private_key = EVP_PKEY_get0_RSA(pkey.get()); + if (!RSA_check_fips(rsa_private_key)) { + throw EnvoyException(fmt::format("Failed to load private key from {}, RSA key failed " + "pairwise consistency test required in FIPS mode", + tls_certificate.privateKeyPath())); + } + } break; } - } break; - } #endif + } } // use the server's cipher list preferences @@ -478,6 +495,20 @@ void ContextImpl::logHandshake(SSL* ssl) const { } } +std::vector +ContextImpl::getPrivateKeyMethodProviders() { + std::vector providers; + + for (uint i = 0; i < tls_contexts_.size(); i++) { + Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr provider = + tls_contexts_[i].getPrivateKeyOperationsProvider(); + if (provider) { + providers.push_back(provider); + } + } + return providers; +} + bool ContextImpl::verifySubjectAltName(X509* cert, const std::vector& subject_alt_names) { bssl::UniquePtr san_names( diff --git a/source/extensions/transport_sockets/tls/context_impl.h b/source/extensions/transport_sockets/tls/context_impl.h index 290c3a582b89..4f02167c0cb9 100644 --- a/source/extensions/transport_sockets/tls/context_impl.h +++ b/source/extensions/transport_sockets/tls/context_impl.h @@ -74,15 +74,13 @@ class ContextImpl : public virtual Envoy::Ssl::Context { SslStats& stats() { return stats_; } - Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr getPrivateKeyOperationsProvider() { - return private_key_provider_; - } - // Ssl::Context size_t daysUntilFirstCertExpires() const override; Envoy::Ssl::CertificateDetailsPtr getCaCertInformation() const override; std::vector getCertChainInformation() const override; + std::vector getPrivateKeyMethodProviders(); + protected: ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, TimeSource& time_source); @@ -139,11 +137,15 @@ class ContextImpl : public virtual Envoy::Ssl::Context { bssl::UniquePtr cert_chain_; std::string cert_chain_file_path_; bool is_ecdsa_{}; + Ssl::PrivateKeyOperationsProviderSharedPtr private_key_provider_{}; std::string getCertChainFileName() const { return cert_chain_file_path_; }; void addClientValidationContext(const Envoy::Ssl::CertificateValidationContextConfig& config, bool require_client_cert); bool isCipherEnabled(uint16_t cipher_id, uint16_t client_version); + Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr getPrivateKeyOperationsProvider() { + return private_key_provider_; + } }; // This is always non-empty, with the first context used for all new SSL @@ -164,7 +166,6 @@ class ContextImpl : public virtual Envoy::Ssl::Context { std::string cert_chain_file_path_; TimeSource& time_source_; const unsigned tls_max_version_; - Ssl::PrivateKeyOperationsProviderSharedPtr private_key_provider_; }; typedef std::shared_ptr ContextImplSharedPtr; diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc index 2297a1529f77..d2ce83249c79 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc @@ -9,7 +9,7 @@ namespace Tls { Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr PrivateKeyOperationsManagerImpl::createPrivateKeyOperationsProvider( - const envoy::api::v2::auth::PrivateKeyOperations& message, + const envoy::api::v2::auth::PrivateKeyMethod& message, Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) { Ssl::PrivateKeyOperationsProviderInstanceFactory* factory = diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h index dddb2ba00a32..e679e754e191 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h @@ -13,7 +13,7 @@ class PrivateKeyOperationsManagerImpl : public virtual Ssl::PrivateKeyOperations public: // Ssl::PrivateKeyOperationsManager Ssl::PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProvider( - const envoy::api::v2::auth::PrivateKeyOperations& message, + const envoy::api::v2::auth::PrivateKeyMethod& message, Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) override; }; diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index e73dd9a8a3fb..08792b85d1a2 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -60,14 +60,15 @@ void SslSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& c ASSERT(!callbacks_); callbacks_ = &callbacks; - provider_ = ctx_->getPrivateKeyOperationsProvider(); - if (provider_) { - ops_ = provider_->getPrivateKeyOperations(*this, callbacks_->connection().dispatcher()); - if (ops_) { - Ssl::PrivateKeyMethodSharedPtr private_key_methods = ops_->getPrivateKeyMethods(ssl_.get()); - if (private_key_methods) { - SSL_set_private_key_method(ssl_.get(), private_key_methods.get()); - } + // Associate this SSL connection with all the certificates (with their potentially different + // private key methods). + std::vector providers = + ctx_->getPrivateKeyMethodProviders(); + for (auto const& provider : providers) { + Ssl::PrivateKeyOperationsPtr op = + provider->getPrivateKeyOperations(ssl_.get(), *this, callbacks_->connection().dispatcher()); + if (op && op->associateWithSsl(ssl_.get())) { + ops_.emplace_back(std::move(op)); } } diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index 22c513765481..0662d9b97cc1 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -94,8 +94,7 @@ class SslSocket : public Network::TransportSocket, mutable std::string cached_sha_256_peer_certificate_digest_; mutable std::string cached_url_encoded_pem_encoded_peer_certificate_; mutable std::string cached_url_encoded_pem_encoded_peer_cert_chain_; - Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr provider_; - Envoy::Ssl::PrivateKeyOperationsPtr ops_; + std::vector ops_; bool async_handshake_in_progress_{}; }; diff --git a/test/common/secret/sds_api_test.cc b/test/common/secret/sds_api_test.cc index 91108c6c07c0..8c072e2696c2 100644 --- a/test/common/secret/sds_api_test.cc +++ b/test/common/secret/sds_api_test.cc @@ -100,7 +100,7 @@ TEST_F(SdsApiTest, DynamicTlsCertificateUpdateSuccess) { EXPECT_CALL(secret_callback, onAddOrUpdateSecret()); sds_api.onConfigUpdate(secret_resources, ""); - Ssl::TlsCertificateConfigImpl tls_config(*sds_api.secret(), *api_); + Ssl::TlsCertificateConfigImpl tls_config(*sds_api.secret(), *api_, false); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), diff --git a/test/common/secret/secret_manager_impl_test.cc b/test/common/secret/secret_manager_impl_test.cc index 18204d0f085d..664e1eb4081c 100644 --- a/test/common/secret/secret_manager_impl_test.cc +++ b/test/common/secret/secret_manager_impl_test.cc @@ -49,7 +49,7 @@ name: "abc.com" ASSERT_NE(secret_manager->findStaticTlsCertificateProvider("abc.com"), nullptr); Ssl::TlsCertificateConfigImpl tls_config( - *secret_manager->findStaticTlsCertificateProvider("abc.com")->secret(), *api_); + *secret_manager->findStaticTlsCertificateProvider("abc.com")->secret(), *api_, false); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), @@ -183,7 +183,7 @@ name: "abc.com" auto secret_config = secret_resources.Add(); MessageUtil::loadFromYaml(TestEnvironment::substitute(yaml), *secret_config); dynamic_cast(*secret_provider).onConfigUpdate(secret_resources, ""); - Ssl::TlsCertificateConfigImpl tls_config(*secret_provider->secret(), *api_); + Ssl::TlsCertificateConfigImpl tls_config(*secret_provider->secret(), *api_, false); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), From 5e01b306cfd6d8e745dae12035f3124786f68849 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Fri, 26 Apr 2019 15:38:09 +0300 Subject: [PATCH 07/56] tls: remove associateWithSsl(), fix tests. Signed-off-by: Ismo Puustinen --- include/envoy/ssl/private_key/private_key.h | 10 +--------- source/extensions/transport_sockets/tls/ssl_socket.cc | 6 +++++- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index 169ccecf0509..f84c509cf2f3 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -25,15 +25,6 @@ typedef std::shared_ptr BoringSslPrivateKeyMethodSharedP class PrivateKeyOperations { public: virtual ~PrivateKeyOperations() {} - - /** - * Associate the private key operations instance with a SSL connection. - * @param ssl a SSL connection object. The BoringSSL private key API - * doesn't allow passing user data to the asynchronous functions as a - * function parameter, so this enables the private key method provider - * to use SSL connection custom data fields instead. - */ - virtual bool associateWithSsl(SSL* ssl) PURE; }; typedef std::unique_ptr PrivateKeyOperationsPtr; @@ -44,6 +35,7 @@ class PrivateKeyOperationsProvider { /** * Get a private key operations instance from the provider. + * @param ssl a SSL connection object. * @param cb a callbacks object, whose "complete" method will be invoked * when the asynchronous processing is complete. * @param dispatcher supplies the owning thread's dispatcher. diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 08792b85d1a2..9d21c89fa913 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -67,7 +67,11 @@ void SslSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& c for (auto const& provider : providers) { Ssl::PrivateKeyOperationsPtr op = provider->getPrivateKeyOperations(ssl_.get(), *this, callbacks_->connection().dispatcher()); - if (op && op->associateWithSsl(ssl_.get())) { + if (op) { + // We keep track of the private key operations for memory management purposes (the operations + // object if destroyed when the SslSocket is destroyed). The operations objects are unique + // because they need to associated with the SSL objects so that user data can be passed to the + // BoringSSL private key methods. ops_.emplace_back(std::move(op)); } } From 8717cb25a1daf74749a952dc471ce56d99b4c70b Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Fri, 26 Apr 2019 17:40:13 +0300 Subject: [PATCH 08/56] tls: rename the private key API to be more in line with BoringSSL. Signed-off-by: Ismo Puustinen --- include/envoy/ssl/context_manager.h | 2 +- include/envoy/ssl/private_key/private_key.h | 32 +++++++++---------- .../ssl/private_key/private_key_callbacks.h | 8 ++--- .../ssl/private_key/private_key_config.h | 11 ++++--- include/envoy/ssl/tls_certificate_config.h | 2 +- .../common/ssl/tls_certificate_config_impl.cc | 4 +-- .../common/ssl/tls_certificate_config_impl.h | 4 +-- .../tls/context_config_impl.cc | 2 +- .../transport_sockets/tls/context_impl.cc | 21 ++++++------ .../transport_sockets/tls/context_impl.h | 8 ++--- .../tls/context_manager_impl.h | 4 +-- .../private_key/private_key_manager_impl.cc | 14 ++++---- .../private_key/private_key_manager_impl.h | 11 ++++--- .../transport_sockets/tls/ssl_socket.cc | 14 ++++---- .../transport_sockets/tls/ssl_socket.h | 8 ++--- test/mocks/ssl/mocks.h | 2 +- 16 files changed, 73 insertions(+), 74 deletions(-) diff --git a/include/envoy/ssl/context_manager.h b/include/envoy/ssl/context_manager.h index 36e66aef96b2..35a2dddf1cfd 100644 --- a/include/envoy/ssl/context_manager.h +++ b/include/envoy/ssl/context_manager.h @@ -44,7 +44,7 @@ class ContextManager { * Access the private key operations manager, which is part of SSL * context manager. */ - virtual PrivateKeyOperationsManager& privateKeyOperationsManager() PURE; + virtual PrivateKeyMethodManager& privateKeyMethodManager() PURE; }; } // namespace Ssl diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index f84c509cf2f3..4cb15f199b28 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -22,16 +22,16 @@ namespace Ssl { typedef std::shared_ptr BoringSslPrivateKeyMethodSharedPtr; -class PrivateKeyOperations { +class PrivateKeyConnection { public: - virtual ~PrivateKeyOperations() {} + virtual ~PrivateKeyConnection() {} }; -typedef std::unique_ptr PrivateKeyOperationsPtr; +typedef std::unique_ptr PrivateKeyConnectionPtr; -class PrivateKeyOperationsProvider { +class PrivateKeyMethodProvider { public: - virtual ~PrivateKeyOperationsProvider() {} + virtual ~PrivateKeyMethodProvider() {} /** * Get a private key operations instance from the provider. @@ -41,8 +41,8 @@ class PrivateKeyOperationsProvider { * @param dispatcher supplies the owning thread's dispatcher. * @return the private key operations instance. */ - virtual PrivateKeyOperationsPtr getPrivateKeyOperations(SSL* ssl, - PrivateKeyOperationsCallbacks& cb, + virtual PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, + PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) PURE; /** @@ -53,30 +53,30 @@ class PrivateKeyOperationsProvider { virtual BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() PURE; }; -typedef std::shared_ptr PrivateKeyOperationsProviderSharedPtr; +typedef std::shared_ptr PrivateKeyMethodProviderSharedPtr; /** * A manager for finding correct user-provided functions for handling BoringSSL private key * operations. */ -class PrivateKeyOperationsManager { +class PrivateKeyMethodManager { public: - virtual ~PrivateKeyOperationsManager() {} + virtual ~PrivateKeyMethodManager() {} /** * Finds and returns a private key operations provider for BoringSSL. * * @param message a protobuf message object containing a * PrivateKeyMethod message. - * @param private_key_provider_context context that provides components for creating and + * @param private_key_method_provider_context context that provides components for creating and * initializing connections for keyless TLS etc. - * @return PrivateKeyOperationsProvider the private key operations provider, or nullptr if + * @return PrivateKeyMethodProvider the private key operations provider, or nullptr if * no provider can be used with the context configuration. */ - virtual PrivateKeyOperationsProviderSharedPtr - createPrivateKeyOperationsProvider(const envoy::api::v2::auth::PrivateKeyMethod& message, - Envoy::Server::Configuration::TransportSocketFactoryContext& - private_key_provider_context) PURE; + virtual PrivateKeyMethodProviderSharedPtr + createPrivateKeyMethodProvider(const envoy::api::v2::auth::PrivateKeyMethod& message, + Envoy::Server::Configuration::TransportSocketFactoryContext& + private_key_method_provider_context) PURE; }; } // namespace Ssl diff --git a/include/envoy/ssl/private_key/private_key_callbacks.h b/include/envoy/ssl/private_key/private_key_callbacks.h index 276feac885e9..f3fa6ba75cbb 100644 --- a/include/envoy/ssl/private_key/private_key_callbacks.h +++ b/include/envoy/ssl/private_key/private_key_callbacks.h @@ -8,14 +8,14 @@ namespace Envoy { namespace Ssl { -enum class PrivateKeyOperationStatus { +enum class PrivateKeyMethodStatus { Success, Failure, }; -class PrivateKeyOperationsCallbacks { +class PrivateKeyConnectionCallbacks { public: - virtual ~PrivateKeyOperationsCallbacks() {} + virtual ~PrivateKeyConnectionCallbacks() {} /** * Callback function which is called when the asynchronous private key @@ -23,7 +23,7 @@ class PrivateKeyOperationsCallbacks { * @param status is "Success" or "Failure" depending on whether the private key operation was * successful or not. */ - virtual void complete(PrivateKeyOperationStatus status) PURE; + virtual void complete(PrivateKeyMethodStatus status) PURE; }; } // namespace Ssl diff --git a/include/envoy/ssl/private_key/private_key_config.h b/include/envoy/ssl/private_key/private_key_config.h index 7013148818e0..5ee12dad6913 100644 --- a/include/envoy/ssl/private_key/private_key_config.h +++ b/include/envoy/ssl/private_key/private_key_config.h @@ -9,12 +9,13 @@ namespace Ssl { // Base class which the private key operation provider implementations can register. -class PrivateKeyOperationsProviderInstanceFactory { +class PrivateKeyMethodProviderInstanceFactory { public: - virtual ~PrivateKeyOperationsProviderInstanceFactory() {} - virtual PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProviderInstance( - const envoy::api::v2::auth::PrivateKeyMethod& message, - Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) PURE; + virtual ~PrivateKeyMethodProviderInstanceFactory() {} + virtual PrivateKeyMethodProviderSharedPtr + createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyMethod& message, + Server::Configuration::TransportSocketFactoryContext& + private_key_method_provider_context) PURE; virtual std::string name() const PURE; }; diff --git a/include/envoy/ssl/tls_certificate_config.h b/include/envoy/ssl/tls_certificate_config.h index f98041033eed..66ea33616999 100644 --- a/include/envoy/ssl/tls_certificate_config.h +++ b/include/envoy/ssl/tls_certificate_config.h @@ -38,7 +38,7 @@ class TlsCertificateConfig { /** * @return private key method provider. */ - virtual Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr privateKeyMethod() const PURE; + virtual Envoy::Ssl::PrivateKeyMethodProviderSharedPtr privateKeyMethod() const PURE; /** * @return a string of password. diff --git a/source/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index 886a28e8933a..68865462f88f 100644 --- a/source/common/ssl/tls_certificate_config_impl.cc +++ b/source/common/ssl/tls_certificate_config_impl.cc @@ -46,8 +46,8 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl( if (config.has_private_key_method()) { private_key_method_ = factory_context.sslContextManager() - .privateKeyOperationsManager() - .createPrivateKeyOperationsProvider(config.private_key_method(), factory_context); + .privateKeyMethodManager() + .createPrivateKeyMethodProvider(config.private_key_method(), factory_context); } if (certificate_chain_.empty() || (private_key_.empty() && private_key_method_ == nullptr)) { throw EnvoyException(fmt::format("Failed to load incomplete certificate from {}, {}", diff --git a/source/common/ssl/tls_certificate_config_impl.h b/source/common/ssl/tls_certificate_config_impl.h index bd4fdee81e56..b5844226bace 100644 --- a/source/common/ssl/tls_certificate_config_impl.h +++ b/source/common/ssl/tls_certificate_config_impl.h @@ -22,7 +22,7 @@ class TlsCertificateConfigImpl : public TlsCertificateConfig { const std::string& certificateChainPath() const override { return certificate_chain_path_; } const std::string& privateKey() const override { return private_key_; } const std::string& privateKeyPath() const override { return private_key_path_; } - Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr privateKeyMethod() const override { + Envoy::Ssl::PrivateKeyMethodProviderSharedPtr privateKeyMethod() const override { return private_key_method_; } const std::string& password() const override { return password_; } @@ -34,7 +34,7 @@ class TlsCertificateConfigImpl : public TlsCertificateConfig { const std::string certificate_chain_path_; const std::string private_key_; const std::string private_key_path_; - Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr private_key_method_{}; + Envoy::Ssl::PrivateKeyMethodProviderSharedPtr private_key_method_{}; const std::string password_; const std::string password_path_; }; diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index c3bc8d60a70f..4a779f523b5a 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -26,7 +26,7 @@ std::vector getTlsCertificateConf if (!config.tls_certificates().empty()) { std::vector providers; for (const auto& tls_certificate : config.tls_certificates()) { - Ssl::PrivateKeyOperationsProviderSharedPtr private_key_ops_provider = nullptr; + Ssl::PrivateKeyMethodProviderSharedPtr private_key_method_provider = nullptr; if (!tls_certificate.has_private_key_method() && !tls_certificate.has_certificate_chain() && !tls_certificate.has_private_key()) { continue; diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 92d33dbeba27..0665a31d89fb 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -302,16 +302,14 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c #endif } - Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr private_key_ops_provider = + Envoy::Ssl::PrivateKeyMethodProviderSharedPtr private_key_method_provider = tls_certificate.privateKeyMethod(); // We either have a private key or a BoringSSL private key method provider. - if (private_key_ops_provider) { - ctx.private_key_provider_ = private_key_ops_provider; - // Set the ops provider to this CTX user data, so that we can get it later in ssl_socket.cc. - // The provider has a reference to the private key methods for the context lifetime. - + if (private_key_method_provider) { + ctx.private_key_method_provider_ = private_key_method_provider; + // The provider has a reference to the private key method for the context lifetime. Ssl::BoringSslPrivateKeyMethodSharedPtr private_key_method = - private_key_ops_provider->getBoringSslPrivateKeyMethod(); + private_key_method_provider->getBoringSslPrivateKeyMethod(); if (private_key_method == nullptr) { throw EnvoyException( fmt::format("Failed to get BoringSsl private key method from provider")); @@ -495,13 +493,12 @@ void ContextImpl::logHandshake(SSL* ssl) const { } } -std::vector -ContextImpl::getPrivateKeyMethodProviders() { - std::vector providers; +std::vector ContextImpl::getPrivateKeyMethodProviders() { + std::vector providers; for (uint i = 0; i < tls_contexts_.size(); i++) { - Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr provider = - tls_contexts_[i].getPrivateKeyOperationsProvider(); + Envoy::Ssl::PrivateKeyMethodProviderSharedPtr provider = + tls_contexts_[i].getPrivateKeyMethodProvider(); if (provider) { providers.push_back(provider); } diff --git a/source/extensions/transport_sockets/tls/context_impl.h b/source/extensions/transport_sockets/tls/context_impl.h index 4f02167c0cb9..1818afdd7b5f 100644 --- a/source/extensions/transport_sockets/tls/context_impl.h +++ b/source/extensions/transport_sockets/tls/context_impl.h @@ -79,7 +79,7 @@ class ContextImpl : public virtual Envoy::Ssl::Context { Envoy::Ssl::CertificateDetailsPtr getCaCertInformation() const override; std::vector getCertChainInformation() const override; - std::vector getPrivateKeyMethodProviders(); + std::vector getPrivateKeyMethodProviders(); protected: ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& config, @@ -137,14 +137,14 @@ class ContextImpl : public virtual Envoy::Ssl::Context { bssl::UniquePtr cert_chain_; std::string cert_chain_file_path_; bool is_ecdsa_{}; - Ssl::PrivateKeyOperationsProviderSharedPtr private_key_provider_{}; + Ssl::PrivateKeyMethodProviderSharedPtr private_key_method_provider_{}; std::string getCertChainFileName() const { return cert_chain_file_path_; }; void addClientValidationContext(const Envoy::Ssl::CertificateValidationContextConfig& config, bool require_client_cert); bool isCipherEnabled(uint16_t cipher_id, uint16_t client_version); - Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr getPrivateKeyOperationsProvider() { - return private_key_provider_; + Envoy::Ssl::PrivateKeyMethodProviderSharedPtr getPrivateKeyMethodProvider() { + return private_key_method_provider_; } }; diff --git a/source/extensions/transport_sockets/tls/context_manager_impl.h b/source/extensions/transport_sockets/tls/context_manager_impl.h index f67884434169..807236ec2c9e 100644 --- a/source/extensions/transport_sockets/tls/context_manager_impl.h +++ b/source/extensions/transport_sockets/tls/context_manager_impl.h @@ -36,7 +36,7 @@ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { const std::vector& server_names) override; size_t daysUntilFirstCertExpires() const override; void iterateContexts(std::function callback) override; - Ssl::PrivateKeyOperationsManager& privateKeyOperationsManager() override { + Ssl::PrivateKeyMethodManager& privateKeyMethodManager() override { return private_key_operations_manager_; }; @@ -44,7 +44,7 @@ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { void removeEmptyContexts(); TimeSource& time_source_; std::list> contexts_; - PrivateKeyOperationsManagerImpl private_key_operations_manager_{}; + PrivateKeyMethodManagerImpl private_key_operations_manager_{}; }; } // namespace Tls diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc index d2ce83249c79..fbabaa9dc14e 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc @@ -7,19 +7,19 @@ namespace Extensions { namespace TransportSockets { namespace Tls { -Envoy::Ssl::PrivateKeyOperationsProviderSharedPtr -PrivateKeyOperationsManagerImpl::createPrivateKeyOperationsProvider( +Envoy::Ssl::PrivateKeyMethodProviderSharedPtr +PrivateKeyMethodManagerImpl::createPrivateKeyMethodProvider( const envoy::api::v2::auth::PrivateKeyMethod& message, - Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) { + Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) { - Ssl::PrivateKeyOperationsProviderInstanceFactory* factory = - Registry::FactoryRegistry::getFactory( + Ssl::PrivateKeyMethodProviderInstanceFactory* factory = + Registry::FactoryRegistry::getFactory( message.provider_name()); // Create a new provider instance with the configuration. if (factory) { - return factory->createPrivateKeyOperationsProviderInstance(message, - private_key_provider_context); + return factory->createPrivateKeyMethodProviderInstance(message, + private_key_method_provider_context); } return nullptr; diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h index e679e754e191..38af3d936813 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h @@ -9,12 +9,13 @@ namespace Extensions { namespace TransportSockets { namespace Tls { -class PrivateKeyOperationsManagerImpl : public virtual Ssl::PrivateKeyOperationsManager { +class PrivateKeyMethodManagerImpl : public virtual Ssl::PrivateKeyMethodManager { public: - // Ssl::PrivateKeyOperationsManager - Ssl::PrivateKeyOperationsProviderSharedPtr createPrivateKeyOperationsProvider( - const envoy::api::v2::auth::PrivateKeyMethod& message, - Server::Configuration::TransportSocketFactoryContext& private_key_provider_context) override; + // Ssl::PrivateKeyMethodManager + Ssl::PrivateKeyMethodProviderSharedPtr + createPrivateKeyMethodProvider(const envoy::api::v2::auth::PrivateKeyMethod& message, + Server::Configuration::TransportSocketFactoryContext& + private_key_method_provider_context) override; }; } // namespace Tls diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 9d21c89fa913..850e5531ff9a 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -62,17 +62,17 @@ void SslSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& c // Associate this SSL connection with all the certificates (with their potentially different // private key methods). - std::vector providers = + std::vector providers = ctx_->getPrivateKeyMethodProviders(); for (auto const& provider : providers) { - Ssl::PrivateKeyOperationsPtr op = - provider->getPrivateKeyOperations(ssl_.get(), *this, callbacks_->connection().dispatcher()); - if (op) { + Ssl::PrivateKeyConnectionPtr pk_connection = + provider->getPrivateKeyConnection(ssl_.get(), *this, callbacks_->connection().dispatcher()); + if (pk_connection) { // We keep track of the private key operations for memory management purposes (the operations // object if destroyed when the SslSocket is destroyed). The operations objects are unique // because they need to associated with the SSL objects so that user data can be passed to the // BoringSSL private key methods. - ops_.emplace_back(std::move(op)); + pk_connections_.emplace_back(std::move(pk_connection)); } } @@ -140,11 +140,11 @@ Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { return {action, bytes_read, end_stream}; } -void SslSocket::complete(Envoy::Ssl::PrivateKeyOperationStatus status) { +void SslSocket::complete(Envoy::Ssl::PrivateKeyMethodStatus status) { ASSERT(async_handshake_in_progress_); async_handshake_in_progress_ = false; - if (status == Envoy::Ssl::PrivateKeyOperationStatus::Success) { + if (status == Envoy::Ssl::PrivateKeyMethodStatus::Success) { ENVOY_CONN_LOG(debug, "async handshake complete", callbacks_->connection()); if (!handshake_complete_) { // It's possible that the async call comes in later, but the handshare has been retried from diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index 0662d9b97cc1..ffeba75817d3 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -42,7 +42,7 @@ enum class InitialState { Client, Server }; class SslSocket : public Network::TransportSocket, public Envoy::Ssl::ConnectionInfo, - public Envoy::Ssl::PrivateKeyOperationsCallbacks, + public Envoy::Ssl::PrivateKeyConnectionCallbacks, protected Logger::Loggable { public: SslSocket(Envoy::Ssl::ContextSharedPtr ctx, InitialState state, @@ -74,8 +74,8 @@ class SslSocket : public Network::TransportSocket, void onConnected() override; const Ssl::ConnectionInfo* ssl() const override { return this; } - // Ssl::PrivateKeyOperationsCallbacks - void complete(Envoy::Ssl::PrivateKeyOperationStatus status) override; + // Ssl::PrivateKeyConnectionCallbacks + void complete(Envoy::Ssl::PrivateKeyMethodStatus status) override; SSL* rawSslForTest() const { return ssl_.get(); } @@ -94,7 +94,7 @@ class SslSocket : public Network::TransportSocket, mutable std::string cached_sha_256_peer_certificate_digest_; mutable std::string cached_url_encoded_pem_encoded_peer_certificate_; mutable std::string cached_url_encoded_pem_encoded_peer_cert_chain_; - std::vector ops_; + std::vector pk_connections_; bool async_handshake_in_progress_{}; }; diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 593b0b6b0b3c..f7d0d679a87e 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -28,7 +28,7 @@ class MockContextManager : public ContextManager { const std::vector& server_names)); MOCK_CONST_METHOD0(daysUntilFirstCertExpires, size_t()); MOCK_METHOD1(iterateContexts, void(std::function callback)); - MOCK_METHOD0(privateKeyOperationsManager, Ssl::PrivateKeyOperationsManager&()); + MOCK_METHOD0(privateKeyMethodManager, Ssl::PrivateKeyMethodManager&()); }; class MockConnectionInfo : public ConnectionInfo { From 7f0e0bc7843bb507b5ff213747eb64da43306b59 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 30 Apr 2019 14:44:05 +0300 Subject: [PATCH 09/56] tls: make a failed private key method association fatal. Signed-off-by: Ismo Puustinen --- .../transport_sockets/tls/ssl_socket.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 850e5531ff9a..17e5bef98cc5 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -67,13 +67,14 @@ void SslSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& c for (auto const& provider : providers) { Ssl::PrivateKeyConnectionPtr pk_connection = provider->getPrivateKeyConnection(ssl_.get(), *this, callbacks_->connection().dispatcher()); - if (pk_connection) { - // We keep track of the private key operations for memory management purposes (the operations - // object if destroyed when the SslSocket is destroyed). The operations objects are unique - // because they need to associated with the SSL objects so that user data can be passed to the - // BoringSSL private key methods. - pk_connections_.emplace_back(std::move(pk_connection)); - } + // If a private key method provider has been assigned for this certificate, it's a fatal + // error if the connection can't be associated with the method. + ASSERT(pk_connection); + // We keep track of the private key operations for memory management purposes (the operations + // object if destroyed when the SslSocket is destroyed). The operations objects are unique + // because they need to associated with the SSL objects so that user data can be passed to the + // BoringSSL private key methods. + pk_connections_.emplace_back(std::move(pk_connection)); } BIO* bio = BIO_new_socket(callbacks_->ioHandle().fd(), 0); From 4855c9e454c7eaa97c1b1eb0f0ecd38aebca8c11 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 6 May 2019 14:28:06 +0300 Subject: [PATCH 10/56] tls: typo fix. Signed-off-by: Ismo Puustinen --- source/extensions/transport_sockets/tls/ssl_socket.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 17e5bef98cc5..5a0eb37a75ee 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -148,7 +148,7 @@ void SslSocket::complete(Envoy::Ssl::PrivateKeyMethodStatus status) { if (status == Envoy::Ssl::PrivateKeyMethodStatus::Success) { ENVOY_CONN_LOG(debug, "async handshake complete", callbacks_->connection()); if (!handshake_complete_) { - // It's possible that the async call comes in later, but the handshare has been retried from + // It's possible that the async call comes in later, but the handshake has been retried from // doWrite or similar. */ doHandshake(); } From b25f6a627d4a9a29c7b410e4596563af2a388e82 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 6 May 2019 17:08:19 +0300 Subject: [PATCH 11/56] test: start testing private key methods (context and ssl_socket). Add a RSA private key method provider. Use that for validating the SSL socket interaction. Signed-off-by: Ismo Puustinen --- test/extensions/transport_sockets/tls/BUILD | 25 ++ .../tls/context_impl_test.cc | 54 ++++ .../tls/rsa_private_key_method_provider.cc | 271 ++++++++++++++++++ .../tls/rsa_private_key_method_provider.h | 81 ++++++ .../transport_sockets/tls/ssl_socket_test.cc | 224 +++++++++++++++ test/mocks/ssl/mocks.cc | 6 + test/mocks/ssl/mocks.h | 23 ++ 7 files changed, 684 insertions(+) create mode 100644 test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc create mode 100644 test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h diff --git a/test/extensions/transport_sockets/tls/BUILD b/test/extensions/transport_sockets/tls/BUILD index 0d5e5f67735b..0c0144e8c258 100644 --- a/test/extensions/transport_sockets/tls/BUILD +++ b/test/extensions/transport_sockets/tls/BUILD @@ -21,6 +21,7 @@ envoy_cc_test( ], external_deps = ["ssl"], deps = [ + ":rsa_private_key_method_provider_test_lib", "//include/envoy/network:transport_socket_interface", "//source/common/buffer:buffer_lib", "//source/common/common:empty_string", @@ -37,14 +38,17 @@ envoy_cc_test( "//source/extensions/transport_sockets/tls:context_lib", "//source/extensions/transport_sockets/tls:ssl_socket_lib", "//source/extensions/transport_sockets/tls:utility_lib", + "//source/extensions/transport_sockets/tls/private_key:private_key_manager_lib", "//test/extensions/transport_sockets/tls/test_data:cert_infos", "//test/mocks/buffer:buffer_mocks", "//test/mocks/network:network_mocks", "//test/mocks/runtime:runtime_mocks", "//test/mocks/server:server_mocks", + "//test/mocks/ssl:ssl_mocks", "//test/mocks/stats:stats_mocks", "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", + "//test/test_common:registry_lib", "//test/test_common:simulated_time_system_lib", "//test/test_common:utility_lib", ], @@ -71,6 +75,7 @@ envoy_cc_test( "//test/mocks/runtime:runtime_mocks", "//test/mocks/secret:secret_mocks", "//test/mocks/server:server_mocks", + "//test/mocks/ssl:ssl_mocks", "//test/test_common:environment_lib", "//test/test_common:simulated_time_system_lib", ], @@ -105,3 +110,23 @@ envoy_cc_test_library( "//test/test_common:environment_lib", ], ) + +envoy_cc_test_library( + name = "rsa_private_key_method_provider_test_lib", + srcs = [ + "rsa_private_key_method_provider.cc", + ], + hdrs = [ + "rsa_private_key_method_provider.h", + ], + external_deps = ["ssl"], + deps = [ + "//include/envoy/api:api_interface", + "//include/envoy/event:dispatcher_interface", + "//include/envoy/server:transport_socket_config_interface", + "//include/envoy/ssl/private_key:private_key_config_interface", + "//include/envoy/ssl/private_key:private_key_interface", + "//source/common/config:utility_lib", + "//source/common/protobuf:utility_lib", + ], +) diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index 274f4d0a9add..310f22bbe757 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -17,6 +17,7 @@ #include "test/extensions/transport_sockets/tls/test_data/san_dns3_cert_info.h" #include "test/mocks/secret/mocks.h" #include "test/mocks/server/mocks.h" +#include "test/mocks/ssl/mocks.h" #include "test/test_common/environment.h" #include "test/test_common/simulated_time_system.h" #include "test/test_common/utility.h" @@ -1201,6 +1202,59 @@ TEST_F(ServerContextConfigImplTest, InvalidIgnoreCertsNoCA) { EXPECT_NO_THROW(ServerContextConfigImpl server_context_config(tls_context, factory_context_)); } +TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoProvider) { + envoy::api::v2::auth::DownstreamTlsContext tls_context; + NiceMock context_manager; + NiceMock private_key_method_manager; + EXPECT_CALL(factory_context_, sslContextManager()).WillOnce(ReturnRef(context_manager)); + EXPECT_CALL(context_manager, privateKeyMethodManager()) + .WillOnce(ReturnRef(private_key_method_manager)); + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" + private_key_method: + provider_name: mock_provider + typed_config: + "@type": type.googleapis.com/google.protobuf.Struct + value: + test_value: 100 + )EOF"; + MessageUtil::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + EXPECT_THROW_WITH_REGEX( + ServerContextConfigImpl server_context_config(tls_context, factory_context_), EnvoyException, + "Failed to load incomplete certificate from "); +} + +TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadSuccess) { + envoy::api::v2::auth::DownstreamTlsContext tls_context; + NiceMock context_manager; + NiceMock private_key_method_manager; + NiceMock private_key_method_provider; + auto private_key_method_provider_ptr = + std::make_shared>(); + EXPECT_CALL(factory_context_, sslContextManager()).WillOnce(ReturnRef(context_manager)); + EXPECT_CALL(context_manager, privateKeyMethodManager()) + .WillOnce(ReturnRef(private_key_method_manager)); + EXPECT_CALL(private_key_method_manager, createPrivateKeyMethodProvider(_, _)) + .WillOnce(Return(private_key_method_provider_ptr)); + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" + private_key_method: + provider_name: mock_provider + typed_config: + "@type": type.googleapis.com/google.protobuf.Struct + value: + test_value: 100 + )EOF"; + MessageUtil::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + ServerContextConfigImpl server_context_config(tls_context, factory_context_); +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc new file mode 100644 index 000000000000..32ebc4748221 --- /dev/null +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -0,0 +1,271 @@ +/* Copyright (c) 2014, Google Inc. + * Copyright (c) 2019, Intel Corp. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +// The RSA signing and decrypting code has been adapted from the +// reference implementation in BoringSSL tests +// (https://github.com/google/boringssl/blob/master/ssl/test/test_config.cc). +// The license for this file is thus the same as the license for the +// source file. + +#include "test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h" + +#include + +#include "envoy/api/api.h" + +#include "openssl/ssl.h" + +namespace Envoy { +namespace Extensions { +namespace PrivateKeyMethodProvider { + +int RsaPrivateKeyMethodProvider::ssl_rsa_connection_index = -1; + +void RsaPrivateKeyConnection::delayed_op() { + const std::chrono::milliseconds timeout_0ms{0}; + + timer_ = dispatcher_.createTimer([this]() -> void { + finished_ = true; + this->cb_.complete(Envoy::Ssl::PrivateKeyMethodStatus::Success); + return; + }); + timer_->enableTimer(timeout_0ms); +} + +static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out, uint16_t signature_algorithm, + const uint8_t* in, size_t in_len) { + (void)out; + (void)out_len; + size_t len = 0; + EVP_PKEY_CTX* pkey_ctx; + const EVP_MD* digest; + bssl::ScopedEVP_MD_CTX md_ctx; + RsaPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); + + if (!ops) { + return ssl_private_key_failure; + } + + if (!ops->test_options_.sign_expected_) { + return ssl_private_key_failure; + } + + EVP_PKEY* rsa_pkey = ops->getPrivateKey(); + if (!rsa_pkey) { + return ssl_private_key_failure; + } + + // Get the digest algorithm. + digest = SSL_get_signature_algorithm_digest(signature_algorithm); + if (digest == NULL) { + return ssl_private_key_failure; + } + + // Initialize the signing context. + if (!EVP_DigestSignInit(md_ctx.get(), &pkey_ctx, digest, nullptr, rsa_pkey)) { + return ssl_private_key_failure; + } + + // Set options for PSS. + if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) { + if (!EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) { + return ssl_private_key_failure; + } + if (!EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1)) { + return ssl_private_key_failure; + } + } + + // Get the signature length for memory allocation. + if (!EVP_DigestSign(md_ctx.get(), nullptr, &len, in, in_len)) { + return ssl_private_key_failure; + } + + if (len == 0 || len > max_out) { + return ssl_private_key_failure; + } + + ops->out_len_ = len; + ops->out_ = static_cast(OPENSSL_malloc(len)); + if (ops->out_ == nullptr) { + return ssl_private_key_failure; + } + + // Run the signing operation. + if (!EVP_DigestSign(md_ctx.get(), ops->out_, &len, in, in_len)) { + OPENSSL_free(ops->out_); + return ssl_private_key_failure; + } + + if (ops->test_options_.crypto_error_) { + // Flip the bits in the first byte to cause the handshake to fail. + ops->out_[0] ^= ops->out_[0]; + } + + if (ops->test_options_.sync_mode_) { + // Return immediately with the results. + memcpy(out, ops->out_, ops->out_len_); + *out_len = ops->out_len_; + OPENSSL_free(ops->out_); + return ssl_private_key_success; + } + + // Tell SSL socket that the operation is ready to be called again. + ops->delayed_op(); + + return ssl_private_key_retry; +} + +static ssl_private_key_result_t privateKeyDecrypt(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out, const uint8_t* in, + size_t in_len) { + RSA* rsa; + RsaPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); + + if (!ops) { + return ssl_private_key_failure; + } + + if (!ops->test_options_.decrypt_expected_) { + return ssl_private_key_failure; + } + + EVP_PKEY* rsa_pkey = ops->getPrivateKey(); + if (!rsa_pkey) { + return ssl_private_key_failure; + } + + rsa = EVP_PKEY_get0_RSA(rsa_pkey); + if (rsa == NULL) { + return ssl_private_key_failure; + } + + ops->out_ = static_cast(OPENSSL_malloc(max_out)); + if (ops->out_ == nullptr) { + return ssl_private_key_failure; + } + + if (!RSA_decrypt(rsa, &ops->out_len_, ops->out_, max_out, in, in_len, RSA_NO_PADDING)) { + OPENSSL_free(ops->out_); + return ssl_private_key_failure; + } + + if (ops->test_options_.sync_mode_) { + // Return immediately with the results. + memcpy(out, ops->out_, ops->out_len_); + *out_len = ops->out_len_; + OPENSSL_free(ops->out_); + return ssl_private_key_success; + } + + ops->delayed_op(); + + return ssl_private_key_retry; +} + +static ssl_private_key_result_t privateKeyComplete(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out) { + RsaPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); + + if (!ops->finished_) { + // The operation didn't finish yet, retry. + return ssl_private_key_retry; + } + + if (ops->out_len_ > max_out) { + return ssl_private_key_failure; + } + + memcpy(out, ops->out_, ops->out_len_); + *out_len = ops->out_len_; + + OPENSSL_free(ops->out_); + + return ssl_private_key_success; +} + +Ssl::BoringSslPrivateKeyMethodSharedPtr +RsaPrivateKeyMethodProvider::getBoringSslPrivateKeyMethod() { + return method_; +} + +RsaPrivateKeyConnection::RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher, + bssl::UniquePtr pkey, + RsaPrivateKeyConnectionTestOptions& test_options) + : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(move(pkey)) { + SSL_set_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index, this); +} + +Ssl::PrivateKeyConnectionPtr RsaPrivateKeyMethodProvider::getPrivateKeyConnection( + SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) { + bssl::UniquePtr bio( + BIO_new_mem_buf(const_cast(private_key_.data()), private_key_.size())); + bssl::UniquePtr pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); + if (pkey == nullptr) { + return nullptr; + } + + return std::make_unique(ssl, cb, dispatcher, move(pkey), test_options_); +} + +RsaPrivateKeyMethodProvider::RsaPrivateKeyMethodProvider( + const ProtobufWkt::Struct& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) { + + std::string private_key_path; + + if (RsaPrivateKeyMethodProvider::ssl_rsa_connection_index == -1) { + RsaPrivateKeyMethodProvider::ssl_rsa_connection_index = + SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); + } + + for (auto& value_it : config.fields()) { + auto& value = value_it.second; + if (value_it.first == "private_key_file" && + value.kind_case() == ProtobufWkt::Value::kStringValue) { + private_key_path = value.string_value(); + } + if (value_it.first == "sync_mode" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { + test_options_.sync_mode_ = value.bool_value(); + } + if (value_it.first == "crypto_error" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { + test_options_.crypto_error_ = value.bool_value(); + } + if (value_it.first == "expected_operation" && + value.kind_case() == ProtobufWkt::Value::kStringValue) { + if (value.string_value() == "decrypt") { + test_options_.decrypt_expected_ = true; + } else if (value.string_value() == "sign") { + test_options_.sign_expected_ = true; + } + } + } + + private_key_ = factory_context.api().fileSystem().fileReadToEnd(private_key_path); + + method_ = std::make_shared(); + method_->sign = privateKeySign; + method_->decrypt = privateKeyDecrypt; + method_->complete = privateKeyComplete; +} + +} // namespace PrivateKeyMethodProvider +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h new file mode 100644 index 000000000000..dbbfb1eadee6 --- /dev/null +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h @@ -0,0 +1,81 @@ +#pragma once + +#include "envoy/event/dispatcher.h" +#include "envoy/server/transport_socket_config.h" +#include "envoy/ssl/private_key/private_key.h" +#include "envoy/ssl/private_key/private_key_config.h" + +#include "common/config/utility.h" +#include "common/protobuf/utility.h" + +namespace Envoy { +namespace Extensions { +namespace PrivateKeyMethodProvider { + +struct RsaPrivateKeyConnectionTestOptions { + bool sync_mode_{}; + bool decrypt_expected_{}; + bool sign_expected_{}; + bool crypto_error_{}; +}; + +// An example RSA private key method provider here for testing the decrypt() and sign() +// functionality. +class RsaPrivateKeyConnection : public virtual Ssl::PrivateKeyConnection { +public: + RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, + RsaPrivateKeyConnectionTestOptions& test_options); + EVP_PKEY* getPrivateKey() { return pkey_.get(); }; + void delayed_op(); + + // Store the output data temporarily. + uint8_t* out_; + size_t out_len_; + + // Is the operation finished? + bool finished_{}; + RsaPrivateKeyConnectionTestOptions& test_options_; + +private: + Ssl::PrivateKeyConnectionCallbacks& cb_; + Event::Dispatcher& dispatcher_; + bssl::UniquePtr pkey_; + Event::TimerPtr timer_; +}; + +class RsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider { +public: + RsaPrivateKeyMethodProvider( + const ProtobufWkt::Struct& config, + Server::Configuration::TransportSocketFactoryContext& factory_context); + // Ssl::PrivateKeyMethodProvider + Ssl::PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, + Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher) override; + Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; + + static int ssl_rsa_connection_index; + +private: + Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; + std::string private_key_; + RsaPrivateKeyConnectionTestOptions test_options_; +}; + +class RsaPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { +public: + // Ssl::PrivateKeyMethodProviderInstanceFactory + Ssl::PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProviderInstance( + const envoy::api::v2::auth::PrivateKeyMethod& message, + Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) { + return std::make_shared(message.config(), + private_key_method_provider_context); + } + + std::string name() const override { return std::string("rsa_test"); }; +}; + +} // namespace PrivateKeyMethodProvider +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 3851df81a492..a6d338fe16b8 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -16,8 +16,10 @@ #include "extensions/filters/listener/tls_inspector/tls_inspector.h" #include "extensions/transport_sockets/tls/context_config_impl.h" #include "extensions/transport_sockets/tls/context_impl.h" +#include "extensions/transport_sockets/tls/private_key/private_key_manager_impl.h" #include "extensions/transport_sockets/tls/ssl_socket.h" +#include "test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h" #include "test/extensions/transport_sockets/tls/ssl_certs_test.h" #include "test/extensions/transport_sockets/tls/test_data/no_san_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/password_protected_cert_info.h" @@ -29,9 +31,11 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/secret/mocks.h" #include "test/mocks/server/mocks.h" +#include "test/mocks/ssl/mocks.h" #include "test/mocks/stats/mocks.h" #include "test/test_common/environment.h" #include "test/test_common/network_utility.h" +#include "test/test_common/registry.h" #include "test/test_common/utility.h" #include "absl/strings/str_replace.h" @@ -197,12 +201,20 @@ class TestUtilOptions : public TestUtilOptionsBase { return expected_expiration_peer_cert_; } + TestUtilOptions& setPrivateKeyMethodExpected(bool expected_method) { + expect_private_key_method_ = expected_method; + return *this; + } + + bool expectedPrivateKeyMethod() const { return expect_private_key_method_; } + private: const std::string client_ctx_yaml_; const std::string server_ctx_yaml_; bool expect_no_cert_; bool expect_no_cert_chain_; + bool expect_private_key_method_{}; std::string expected_digest_; std::vector expected_local_uri_; std::string expected_serial_number_; @@ -210,6 +222,7 @@ class TestUtilOptions : public TestUtilOptionsBase { std::string expected_local_subject_; std::string expected_peer_cert_; std::string expected_peer_cert_chain_; + std::string expected_private_key_path_; std::string expected_valid_from_peer_cert_; std::string expected_expiration_peer_cert_; }; @@ -223,6 +236,18 @@ void testUtil(const TestUtilOptions& options) { server_factory_context; ON_CALL(server_factory_context, api()).WillByDefault(ReturnRef(*server_api)); + // For private key method testing. + NiceMock context_manager; + Extensions::PrivateKeyMethodProvider::RsaPrivateKeyMethodFactory rsa_factory; + Registry::InjectFactory private_key_method_factory( + rsa_factory); + PrivateKeyMethodManagerImpl private_key_method_manager; + if (options.expectedPrivateKeyMethod()) { + EXPECT_CALL(server_factory_context, sslContextManager()).WillOnce(ReturnRef(context_manager)); + EXPECT_CALL(context_manager, privateKeyMethodManager()) + .WillOnce(ReturnRef(private_key_method_manager)); + } + envoy::api::v2::auth::DownstreamTlsContext server_tls_context; MessageUtil::loadFromYaml(TestEnvironment::substitute(options.serverCtxYaml()), server_tls_context); @@ -4005,6 +4030,205 @@ TEST_P(SslReadBufferLimitTest, TestBind) { disconnect(); } +// TODO(ipuustin): The following tests are needed: +// success cases: +// * test multi-cert (does this require a ECDSA provider?) +// failure cases: +// * test private key method error return in +// * sign +// * decrypt +// * complete +// * test incorrect decryption +// * test returning failure in complete() function + +// Test asynchronous signing (ECDHE) +TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignSuccess) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + sync_mode: false + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string successful_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - ECDHE-RSA-AES128-GCM-SHA256 +)EOF"; + + TestUtilOptions successful_test_options(successful_client_ctx_yaml, server_ctx_yaml, true, + GetParam()); + testUtil(successful_test_options.setPrivateKeyMethodExpected(true)); +} + +// Test asynchronous decryption (RSA) +TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptSuccess) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: decrypt + sync_mode: false + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string successful_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - TLS_RSA_WITH_AES_128_GCM_SHA256 +)EOF"; + + TestUtilOptions successful_test_options(successful_client_ctx_yaml, server_ctx_yaml, true, + GetParam()); + testUtil(successful_test_options.setPrivateKeyMethodExpected(true)); +} + +// Test synchronous signing (ECDHE) +TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignSuccess) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + sync_mode: true + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string successful_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - ECDHE-RSA-AES128-GCM-SHA256 +)EOF"; + + TestUtilOptions successful_test_options(successful_client_ctx_yaml, server_ctx_yaml, true, + GetParam()); + testUtil(successful_test_options.setPrivateKeyMethodExpected(true)); +} + +// Test synchronous decryption (RSA) +TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncDecryptSuccess) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: decrypt + sync_mode: true + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string successful_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - TLS_RSA_WITH_AES_128_GCM_SHA256 +)EOF"; + + TestUtilOptions successful_test_options(successful_client_ctx_yaml, server_ctx_yaml, true, + GetParam()); + testUtil(successful_test_options.setPrivateKeyMethodExpected(true)); +} + +// Test asynchronous signing (ECDHE) failure (invalid signature) +TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignFailure) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + sync_mode: false + crypto_error: true + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string failing_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - ECDHE-RSA-AES128-GCM-SHA256 +)EOF"; + + TestUtilOptions failing_test_options(failing_client_ctx_yaml, server_ctx_yaml, false, GetParam()); + testUtil(failing_test_options.setPrivateKeyMethodExpected(true).setExpectedServerStats( + "ssl.connection_error")); +} + +// Test synchronous signing (ECDHE) failure (invalid signature) +TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignFailure) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + sync_mode: true + crypto_error: true + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string failing_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - ECDHE-RSA-AES128-GCM-SHA256 +)EOF"; + + TestUtilOptions failing_test_options(failing_client_ctx_yaml, server_ctx_yaml, false, GetParam()); + testUtil(failing_test_options.setPrivateKeyMethodExpected(true).setExpectedServerStats( + "ssl.connection_error")); +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/test/mocks/ssl/mocks.cc b/test/mocks/ssl/mocks.cc index f19c75217d63..82155e521e22 100644 --- a/test/mocks/ssl/mocks.cc +++ b/test/mocks/ssl/mocks.cc @@ -12,5 +12,11 @@ MockConnectionInfo::~MockConnectionInfo() {} MockClientContext::MockClientContext() {} MockClientContext::~MockClientContext() {} +MockPrivateKeyMethodManager::MockPrivateKeyMethodManager() {} +MockPrivateKeyMethodManager::~MockPrivateKeyMethodManager() {} + +MockPrivateKeyMethodProvider::MockPrivateKeyMethodProvider() {} +MockPrivateKeyMethodProvider::~MockPrivateKeyMethodProvider() {} + } // namespace Ssl } // namespace Envoy diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index f7d0d679a87e..f2d3c624e02a 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -61,5 +61,28 @@ class MockClientContext : public ClientContext { MOCK_CONST_METHOD0(getCertChainInformation, std::vector()); }; +class MockPrivateKeyMethodManager : public PrivateKeyMethodManager { +public: + MockPrivateKeyMethodManager(); + ~MockPrivateKeyMethodManager(); + + MOCK_METHOD2( + createPrivateKeyMethodProvider, + PrivateKeyMethodProviderSharedPtr(const envoy::api::v2::auth::PrivateKeyMethod& message, + Envoy::Server::Configuration::TransportSocketFactoryContext& + private_key_method_provider_context)); +}; + +class MockPrivateKeyMethodProvider : public PrivateKeyMethodProvider { +public: + MockPrivateKeyMethodProvider(); + ~MockPrivateKeyMethodProvider(); + + MOCK_METHOD3(getPrivateKeyConnection, + PrivateKeyConnectionPtr(SSL* ssl, PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher)); + MOCK_METHOD0(getBoringSslPrivateKeyMethod, BoringSslPrivateKeyMethodSharedPtr()); +}; + } // namespace Ssl } // namespace Envoy From b7f4fa39c531e42c5105ca744a15cdde4122b3b6 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Thu, 9 May 2019 23:16:41 +0300 Subject: [PATCH 12/56] dictionary: added words. Signed-off-by: Ismo Puustinen --- tools/spelling_dictionary.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index b94e12951480..b7e3b0ec3932 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -177,6 +177,7 @@ POSTs PREBIND PRNG PROT +PSS QUIC RAII RANLUX @@ -236,6 +237,7 @@ TLSv TLV TMPDIR TODO +TORTIOUS TPROXY TSAN TSI From f67c1a1238930c098910f4eaf08ddb23800ddb1f Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Fri, 10 May 2019 10:22:00 +0300 Subject: [PATCH 13/56] test: add a missing "override". Signed-off-by: Ismo Puustinen --- .../tls/rsa_private_key_method_provider.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h index dbbfb1eadee6..280127763311 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h @@ -66,9 +66,10 @@ class RsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider class RsaPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { public: // Ssl::PrivateKeyMethodProviderInstanceFactory - Ssl::PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProviderInstance( - const envoy::api::v2::auth::PrivateKeyMethod& message, - Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) { + Ssl::PrivateKeyMethodProviderSharedPtr + createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyMethod& message, + Server::Configuration::TransportSocketFactoryContext& + private_key_method_provider_context) override { return std::make_shared(message.config(), private_key_method_provider_context); } From ce312ecbc161a0f2c75e437c666318bb9467526a Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 14 May 2019 16:07:14 +0300 Subject: [PATCH 14/56] tls: close connection if async handshake fails. if the second half of asynchronous private key method fails (meaning that the operation itself failed or the second call to SSL_do_handshake() failed), just call the connection to be closed. Signed-off-by: Ismo Puustinen --- .../transport_sockets/tls/ssl_socket.cc | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 5a0eb37a75ee..7978b2479f7c 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -150,11 +150,26 @@ void SslSocket::complete(Envoy::Ssl::PrivateKeyMethodStatus status) { if (!handshake_complete_) { // It's possible that the async call comes in later, but the handshake has been retried from // doWrite or similar. */ - doHandshake(); + PostIoAction result = doHandshake(); + // TODO(ipuustin): should there be a special event type for asynchronous close? + if (result != PostIoAction::KeepOpen) { + if (callbacks_->connection().state() == Network::Connection::State::Open) { + // The connection state machine thinks we are still connecting, but the second part of the + // private key method handshake failed. + ENVOY_CONN_LOG(debug, "async handshake completion error", callbacks_->connection()); + drainErrorQueue(); + // There's nobody to handle the SSL error for us, because this event is coming in + // asynchronously -- just close the connection. This will lead to + // Network::ConnectionEvent::LocalClose event. + callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + } + } } } else { + // The private key method operation failed. ENVOY_CONN_LOG(debug, "async handshake failed", callbacks_->connection()); - callbacks_->raiseEvent(Network::ConnectionEvent::LocalClose); + drainErrorQueue(); + callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); } } From 3eacd75ac01aab8fad2e4abed4c726ee77ad5516 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 14 May 2019 14:42:23 +0300 Subject: [PATCH 15/56] tests: add more tests for private key method error cases. Signed-off-by: Ismo Puustinen --- .../tls/rsa_private_key_method_provider.cc | 25 +++ .../tls/rsa_private_key_method_provider.h | 13 ++ .../transport_sockets/tls/ssl_socket_test.cc | 148 +++++++++++++++++- 3 files changed, 178 insertions(+), 8 deletions(-) diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index 32ebc4748221..b351f682679d 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -60,7 +60,13 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o return ssl_private_key_failure; } + if (ops->test_options_.method_error_) { + // Have an artificial test failure. + return ssl_private_key_failure; + } + if (!ops->test_options_.sign_expected_) { + // TODO(ipuustin): throw exception, because a failure can be an expected result in some tests? return ssl_private_key_failure; } @@ -141,7 +147,13 @@ static ssl_private_key_result_t privateKeyDecrypt(SSL* ssl, uint8_t* out, size_t return ssl_private_key_failure; } + if (ops->test_options_.method_error_) { + // Have an artificial test failure. + return ssl_private_key_failure; + } + if (!ops->test_options_.decrypt_expected_) { + // TODO(ipuustin): throw exception, because a failure can be an expected result in some tests? return ssl_private_key_failure; } @@ -188,7 +200,13 @@ static ssl_private_key_result_t privateKeyComplete(SSL* ssl, uint8_t* out, size_ return ssl_private_key_retry; } + if (ops->test_options_.async_method_error_) { + OPENSSL_free(ops->out_); + return ssl_private_key_failure; + } + if (ops->out_len_ > max_out) { + OPENSSL_free(ops->out_); return ssl_private_key_failure; } @@ -248,6 +266,13 @@ RsaPrivateKeyMethodProvider::RsaPrivateKeyMethodProvider( if (value_it.first == "crypto_error" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { test_options_.crypto_error_ = value.bool_value(); } + if (value_it.first == "method_error" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { + test_options_.method_error_ = value.bool_value(); + } + if (value_it.first == "async_method_error" && + value.kind_case() == ProtobufWkt::Value::kBoolValue) { + test_options_.async_method_error_ = value.bool_value(); + } if (value_it.first == "expected_operation" && value.kind_case() == ProtobufWkt::Value::kStringValue) { if (value.string_value() == "decrypt") { diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h index 280127763311..730dd730b862 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h @@ -13,10 +13,23 @@ namespace Extensions { namespace PrivateKeyMethodProvider { struct RsaPrivateKeyConnectionTestOptions { + // Return private key method value directly without asynchronous operation. bool sync_mode_{}; + + // The "decrypt" private key method is expected to he called. bool decrypt_expected_{}; + + // The "sign" private key method is expected to he called. bool sign_expected_{}; + + // Add a cryptographic error (invalid signature, incorrect decryption). bool crypto_error_{}; + + // Return an error from the private key method. + bool method_error_{}; + + // Return an error from the private key method completion function. + bool async_method_error_{}; }; // An example RSA private key method provider here for testing the decrypt() and sign() diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 4e793f567cc5..e4686018f7bb 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -99,7 +99,9 @@ class TestUtilOptions : public TestUtilOptionsBase { TestUtilOptions(const std::string& client_ctx_yaml, const std::string& server_ctx_yaml, bool expect_success, Network::Address::IpVersion version) : TestUtilOptionsBase(expect_success, version), client_ctx_yaml_(client_ctx_yaml), - server_ctx_yaml_(server_ctx_yaml), expect_no_cert_(false), expect_no_cert_chain_(false) { + server_ctx_yaml_(server_ctx_yaml), expect_no_cert_(false), expect_no_cert_chain_(false), + expect_private_key_method_(false), + expected_server_close_event_(Network::ConnectionEvent::RemoteClose) { if (expect_success) { setExpectedServerStats("ssl.handshake"); } else { @@ -208,13 +210,21 @@ class TestUtilOptions : public TestUtilOptionsBase { bool expectedPrivateKeyMethod() const { return expect_private_key_method_; } + TestUtilOptions& setExpectedServerCloseEvent(Network::ConnectionEvent expected_event) { + expected_server_close_event_ = expected_event; + return *this; + } + + Network::ConnectionEvent expectedServerCloseEvent() const { return expected_server_close_event_; } + private: const std::string client_ctx_yaml_; const std::string server_ctx_yaml_; bool expect_no_cert_; bool expect_no_cert_chain_; - bool expect_private_key_method_{}; + bool expect_private_key_method_; + Network::ConnectionEvent expected_server_close_event_; std::string expected_digest_; std::vector expected_local_uri_; std::string expected_serial_number_; @@ -385,7 +395,7 @@ void testUtil(const TestUtilOptions& options) { } else { EXPECT_CALL(client_connection_callbacks, onEvent(Network::ConnectionEvent::RemoteClose)) .WillOnce(Invoke([&](Network::ConnectionEvent) -> void { close_second_time(); })); - EXPECT_CALL(server_connection_callbacks, onEvent(Network::ConnectionEvent::RemoteClose)) + EXPECT_CALL(server_connection_callbacks, onEvent(options.expectedServerCloseEvent())) .WillOnce(Invoke([&](Network::ConnectionEvent) -> void { close_second_time(); })); } @@ -4032,12 +4042,8 @@ TEST_P(SslReadBufferLimitTest, TestBind) { // success cases: // * test multi-cert (does this require a ECDSA provider?) // failure cases: -// * test private key method error return in -// * sign -// * decrypt -// * complete +// * test error return in complete() callback // * test incorrect decryption -// * test returning failure in complete() function // Test asynchronous signing (ECDHE) TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignSuccess) { @@ -4227,6 +4233,132 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignFailure) { "ssl.connection_error")); } +// Tet the sign operation return with an error. +TEST_P(SslSocketTest, RsaPrivateKeyProviderSignFailure) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + method_error: true + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string failing_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - ECDHE-RSA-AES128-GCM-SHA256 +)EOF"; + + TestUtilOptions failing_test_options(failing_client_ctx_yaml, server_ctx_yaml, false, GetParam()); + testUtil(failing_test_options.setPrivateKeyMethodExpected(true).setExpectedServerStats( + "ssl.connection_error")); +} + +// Tet the decrypt operation return with an error. +TEST_P(SslSocketTest, RsaPrivateKeyProviderDecryptFailure) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: decrypt + method_error: true + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string failing_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - TLS_RSA_WITH_AES_128_GCM_SHA256 +)EOF"; + + TestUtilOptions failing_test_options(failing_client_ctx_yaml, server_ctx_yaml, false, GetParam()); + testUtil(failing_test_options.setPrivateKeyMethodExpected(true).setExpectedServerStats( + "ssl.connection_error")); +} + +// Tet the sign operation return with an error in complete. +TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignCompleteFailure) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + async_method_error: true + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string failing_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - ECDHE-RSA-AES128-GCM-SHA256 +)EOF"; + + TestUtilOptions failing_test_options(failing_client_ctx_yaml, server_ctx_yaml, false, GetParam()); + testUtil(failing_test_options.setPrivateKeyMethodExpected(true) + .setExpectedServerCloseEvent(Network::ConnectionEvent::LocalClose) + .setExpectedServerStats("ssl.connection_error")); +} + +// Tet the decrypt operation return with an error in complete. +TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptCompleteFailure) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_tmpdir }}/unittestcert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: decrypt + async_method_error: true + validation_context: + trusted_ca: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + crl: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.crl" +)EOF"; + const std::string failing_client_ctx_yaml = R"EOF( + common_tls_context: + tls_params: + cipher_suites: + - TLS_RSA_WITH_AES_128_GCM_SHA256 +)EOF"; + + TestUtilOptions failing_test_options(failing_client_ctx_yaml, server_ctx_yaml, false, GetParam()); + testUtil(failing_test_options.setPrivateKeyMethodExpected(true) + .setExpectedServerCloseEvent(Network::ConnectionEvent::LocalClose) + .setExpectedServerStats("ssl.connection_error")); +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions From 81afc58994cfab76df79bc35bcb5882351e07641 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 15 May 2019 11:09:48 +0300 Subject: [PATCH 16/56] tls: remove status from private key method complete() callback. BoringSSL pattern seems to be that no-one takes a fast path without calling the SSL_do_hanshake() again. This means that the operators need to communicate errors in the private key complete method by returning `ssl_private_key_failure`. Signed-off-by: Ismo Puustinen --- .../ssl/private_key/private_key_callbacks.h | 13 ++---- .../transport_sockets/tls/ssl_socket.cc | 46 +++++++++---------- .../transport_sockets/tls/ssl_socket.h | 2 +- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/include/envoy/ssl/private_key/private_key_callbacks.h b/include/envoy/ssl/private_key/private_key_callbacks.h index f3fa6ba75cbb..b7f2048fb2a6 100644 --- a/include/envoy/ssl/private_key/private_key_callbacks.h +++ b/include/envoy/ssl/private_key/private_key_callbacks.h @@ -8,22 +8,17 @@ namespace Envoy { namespace Ssl { -enum class PrivateKeyMethodStatus { - Success, - Failure, -}; - class PrivateKeyConnectionCallbacks { public: virtual ~PrivateKeyConnectionCallbacks() {} /** * Callback function which is called when the asynchronous private key - * operation has been completed. - * @param status is "Success" or "Failure" depending on whether the private key operation was - * successful or not. + * operation has been completed (with either success or failure). The + * provider will communicate the success status when SSL_do_handshake() + * is called the next time. */ - virtual void complete(PrivateKeyMethodStatus status) PURE; + virtual void complete() PURE; }; } // namespace Ssl diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 7978b2479f7c..abeb1a90bfb6 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -141,35 +141,33 @@ Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { return {action, bytes_read, end_stream}; } -void SslSocket::complete(Envoy::Ssl::PrivateKeyMethodStatus status) { +void SslSocket::complete() { ASSERT(async_handshake_in_progress_); async_handshake_in_progress_ = false; - if (status == Envoy::Ssl::PrivateKeyMethodStatus::Success) { - ENVOY_CONN_LOG(debug, "async handshake complete", callbacks_->connection()); - if (!handshake_complete_) { - // It's possible that the async call comes in later, but the handshake has been retried from - // doWrite or similar. */ - PostIoAction result = doHandshake(); - // TODO(ipuustin): should there be a special event type for asynchronous close? - if (result != PostIoAction::KeepOpen) { - if (callbacks_->connection().state() == Network::Connection::State::Open) { - // The connection state machine thinks we are still connecting, but the second part of the - // private key method handshake failed. - ENVOY_CONN_LOG(debug, "async handshake completion error", callbacks_->connection()); - drainErrorQueue(); - // There's nobody to handle the SSL error for us, because this event is coming in - // asynchronously -- just close the connection. This will lead to - // Network::ConnectionEvent::LocalClose event. - callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); - } + ENVOY_CONN_LOG(debug, "async handshake complete", callbacks_->connection()); + if (!handshake_complete_) { + // It's possible that the async call comes in later, but the handshake has been retried from + // doWrite or similar. In that case the error handling has been done synchronously. */ + PostIoAction result = doHandshake(); + if (async_handshake_in_progress_ || result != PostIoAction::KeepOpen) { + // It may be semantically possible that the handshake is expecting read() or write(), so + // we shouldn't require that the handshake is complete. However, there can't be a second + // private key operation requested. + if (callbacks_->connection().state() == Network::Connection::State::Open) { + // The connection state machine thinks we are still connecting, but the second part of the + // private key method handshake failed. If the connection state would be something else, + // the socket was already closed while waiting for async handshake. + ENVOY_CONN_LOG(debug, "async handshake completion error", callbacks_->connection()); + drainErrorQueue(); + // There's nobody to handle the SSL error for us, because this event is coming in + // asynchronously -- just close the connection. This will lead to + // Network::ConnectionEvent::LocalClose event. + // TODO(ipuustin): should there be a special event type for asynchronous close? + // Such as Network::ConnectionEvent::ConnectionFailed? + callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); } } - } else { - // The private key method operation failed. - ENVOY_CONN_LOG(debug, "async handshake failed", callbacks_->connection()); - drainErrorQueue(); - callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); } } diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index ffeba75817d3..fd973a1151d1 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -75,7 +75,7 @@ class SslSocket : public Network::TransportSocket, const Ssl::ConnectionInfo* ssl() const override { return this; } // Ssl::PrivateKeyConnectionCallbacks - void complete(Envoy::Ssl::PrivateKeyMethodStatus status) override; + void complete() override; SSL* rawSslForTest() const { return ssl_.get(); } From ccc8fbefc9fc7e3f4d703123559281611b32bdb3 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 15 May 2019 11:10:11 +0300 Subject: [PATCH 17/56] tests: update tests to the new API + cleanups. Signed-off-by: Ismo Puustinen --- .../transport_sockets/tls/rsa_private_key_method_provider.cc | 2 +- test/extensions/transport_sockets/tls/ssl_socket_test.cc | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index b351f682679d..238fd6e0dd66 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -38,7 +38,7 @@ void RsaPrivateKeyConnection::delayed_op() { timer_ = dispatcher_.createTimer([this]() -> void { finished_ = true; - this->cb_.complete(Envoy::Ssl::PrivateKeyMethodStatus::Success); + this->cb_.complete(); return; }); timer_->enableTimer(timeout_0ms); diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index e4686018f7bb..0dfe02779932 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -232,7 +232,6 @@ class TestUtilOptions : public TestUtilOptionsBase { std::string expected_local_subject_; std::string expected_peer_cert_; std::string expected_peer_cert_chain_; - std::string expected_private_key_path_; std::string expected_valid_from_peer_cert_; std::string expected_expiration_peer_cert_; }; @@ -4042,7 +4041,6 @@ TEST_P(SslReadBufferLimitTest, TestBind) { // success cases: // * test multi-cert (does this require a ECDSA provider?) // failure cases: -// * test error return in complete() callback // * test incorrect decryption // Test asynchronous signing (ECDHE) From 3e8a3c3f36a828d7f2a4165fa02789e406f037a0 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 15 May 2019 11:45:56 +0300 Subject: [PATCH 18/56] tls: add a check for having both private_key and private_key_method. Signed-off-by: Ismo Puustinen --- source/common/ssl/tls_certificate_config_impl.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index 68865462f88f..6913f8d77a4a 100644 --- a/source/common/ssl/tls_certificate_config_impl.cc +++ b/source/common/ssl/tls_certificate_config_impl.cc @@ -44,6 +44,10 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl( Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) : TlsCertificateConfigImpl(config, api, true) { if (config.has_private_key_method()) { + if (config.has_private_key()) { + throw EnvoyException(fmt::format( + "Certificate configuration can't have both private_key and private_key_method")); + } private_key_method_ = factory_context.sslContextManager() .privateKeyMethodManager() From 59d93f6f55486ea989542d51dfbff0a8911d0b51 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 15 May 2019 11:46:40 +0300 Subject: [PATCH 19/56] tests: test having both private_key and private_key_method in certificate config. Signed-off-by: Ismo Puustinen --- .../tls/context_impl_test.cc | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index 310f22bbe757..6f5ea2378261 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -1255,6 +1255,28 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadSuccess) { ServerContextConfigImpl server_context_config(tls_context, factory_context_); } +TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureBothKeyAndMethod) { + envoy::api::v2::auth::DownstreamTlsContext tls_context; + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_key.pem" + private_key_method: + provider_name: mock_provider + typed_config: + "@type": type.googleapis.com/google.protobuf.Struct + value: + test_value: 100 + )EOF"; + MessageUtil::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + EXPECT_THROW_WITH_MESSAGE( + ServerContextConfigImpl server_context_config(tls_context, factory_context_), EnvoyException, + "Certificate configuration can't have both private_key and private_key_method"); +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions From eb1feaa55004b3f40c5285952aff8708f4efd605 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 15 May 2019 12:57:42 +0300 Subject: [PATCH 20/56] cert.proto: fix numbering to be contiguous. Signed-off-by: Ismo Puustinen --- api/envoy/api/v2/auth/cert.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/envoy/api/v2/auth/cert.proto b/api/envoy/api/v2/auth/cert.proto index 953239b6ff6c..d1b41048fb2a 100644 --- a/api/envoy/api/v2/auth/cert.proto +++ b/api/envoy/api/v2/auth/cert.proto @@ -113,7 +113,7 @@ message PrivateKeyMethod { oneof config_type { google.protobuf.Struct config = 2; - google.protobuf.Any typed_config = 4; + google.protobuf.Any typed_config = 3; } } From f7ccf0b84ef8c893ed869126582a3b62cad2d476 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 27 May 2019 15:50:21 +0300 Subject: [PATCH 21/56] test: fail if provider doesn't return private key methods. Signed-off-by: Ismo Puustinen --- .../tls/context_impl_test.cc | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index 6f5ea2378261..5008beaeba87 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -1227,11 +1227,45 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoProvider) { "Failed to load incomplete certificate from "); } +TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoMethod) { + envoy::api::v2::auth::DownstreamTlsContext tls_context; + tls_context.mutable_common_tls_context()->add_tls_certificates(); + Stats::IsolatedStoreImpl store; + NiceMock context_manager; + NiceMock private_key_method_manager; + auto private_key_method_provider_ptr = + std::make_shared>(); + Event::SimulatedTimeSystem time_system; + ContextManagerImpl manager(time_system); + EXPECT_CALL(factory_context_, sslContextManager()).WillOnce(ReturnRef(context_manager)); + EXPECT_CALL(context_manager, privateKeyMethodManager()) + .WillOnce(ReturnRef(private_key_method_manager)); + EXPECT_CALL(private_key_method_manager, createPrivateKeyMethodProvider(_, _)) + .WillOnce(Return(private_key_method_provider_ptr)); + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" + private_key_method: + provider_name: mock_provider + typed_config: + "@type": type.googleapis.com/google.protobuf.Struct + value: + test_value: 100 + )EOF"; + MessageUtil::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + ServerContextConfigImpl server_context_config(tls_context, factory_context_); + EXPECT_THROW_WITH_MESSAGE( + Envoy::Ssl::ServerContextSharedPtr server_ctx( + manager.createSslServerContext(store, server_context_config, std::vector{})), + EnvoyException, "Failed to get BoringSsl private key method from provider"); +} + TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadSuccess) { envoy::api::v2::auth::DownstreamTlsContext tls_context; NiceMock context_manager; NiceMock private_key_method_manager; - NiceMock private_key_method_provider; auto private_key_method_provider_ptr = std::make_shared>(); EXPECT_CALL(factory_context_, sslContextManager()).WillOnce(ReturnRef(context_manager)); From dace6bbec48dc88ba46e6d50d993e084019a3c7c Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 27 May 2019 14:03:42 +0300 Subject: [PATCH 22/56] test: rewrote rsa private key method logic. Signed-off-by: Ismo Puustinen --- .../tls/rsa_private_key_method_provider.cc | 97 ++++++++----------- .../tls/rsa_private_key_method_provider.h | 2 +- tools/spelling_dictionary.txt | 1 - 3 files changed, 41 insertions(+), 59 deletions(-) diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index 238fd6e0dd66..ffaff32d1f80 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -1,24 +1,3 @@ -/* Copyright (c) 2014, Google Inc. - * Copyright (c) 2019, Intel Corp. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -// The RSA signing and decrypting code has been adapted from the -// reference implementation in BoringSSL tests -// (https://github.com/google/boringssl/blob/master/ssl/test/test_config.cc). -// The license for this file is thus the same as the license for the -// source file. - #include "test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h" #include @@ -47,14 +26,17 @@ void RsaPrivateKeyConnection::delayed_op() { static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* out_len, size_t max_out, uint16_t signature_algorithm, const uint8_t* in, size_t in_len) { - (void)out; - (void)out_len; - size_t len = 0; - EVP_PKEY_CTX* pkey_ctx; - const EVP_MD* digest; - bssl::ScopedEVP_MD_CTX md_ctx; + RSA* rsa; + bssl::ScopedEVP_MD_CTX ctx; + const EVP_MD* md; RsaPrivateKeyConnection* ops = static_cast( SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int hash_len; + uint8_t* msg; + size_t msg_len; + int prefix_allocated = 0; + int padding = RSA_NO_PADDING; if (!ops) { return ssl_private_key_failure; @@ -70,53 +52,59 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o return ssl_private_key_failure; } - EVP_PKEY* rsa_pkey = ops->getPrivateKey(); - if (!rsa_pkey) { + rsa = ops->getPrivateKey(); + if (rsa == nullptr) { return ssl_private_key_failure; } - // Get the digest algorithm. - digest = SSL_get_signature_algorithm_digest(signature_algorithm); - if (digest == NULL) { + md = SSL_get_signature_algorithm_digest(signature_algorithm); + if (!md) { return ssl_private_key_failure; } - // Initialize the signing context. - if (!EVP_DigestSignInit(md_ctx.get(), &pkey_ctx, digest, nullptr, rsa_pkey)) { + // Calculate the digest for signing. + if (!EVP_DigestInit_ex(ctx.get(), md, nullptr) || !EVP_DigestUpdate(ctx.get(), in, in_len) || + !EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) { return ssl_private_key_failure; } - // Set options for PSS. + // Addd RSA padding to the the hash. Supported types are PSS and PKCS1. if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) { - if (!EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) { + msg_len = RSA_size(rsa); + msg = static_cast(OPENSSL_malloc(msg_len)); + if (!msg) { return ssl_private_key_failure; } - if (!EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1)) { + prefix_allocated = 1; + if (!RSA_padding_add_PKCS1_PSS_mgf1(rsa, msg, hash, md, nullptr, -1)) { + OPENSSL_free(msg); return ssl_private_key_failure; } + padding = RSA_NO_PADDING; + } else { + if (!RSA_add_pkcs1_prefix(&msg, &msg_len, &prefix_allocated, EVP_MD_type(md), hash, hash_len)) { + return ssl_private_key_failure; + } + padding = RSA_PKCS1_PADDING; } - // Get the signature length for memory allocation. - if (!EVP_DigestSign(md_ctx.get(), nullptr, &len, in, in_len)) { - return ssl_private_key_failure; - } - - if (len == 0 || len > max_out) { - return ssl_private_key_failure; - } - - ops->out_len_ = len; - ops->out_ = static_cast(OPENSSL_malloc(len)); + ops->out_ = static_cast(OPENSSL_malloc(max_out)); if (ops->out_ == nullptr) { return ssl_private_key_failure; } - // Run the signing operation. - if (!EVP_DigestSign(md_ctx.get(), ops->out_, &len, in, in_len)) { + if (!RSA_sign_raw(rsa, &ops->out_len_, ops->out_, max_out, msg, msg_len, padding)) { + if (prefix_allocated) { + OPENSSL_free(msg); + } OPENSSL_free(ops->out_); return ssl_private_key_failure; } + if (prefix_allocated) { + OPENSSL_free(msg); + } + if (ops->test_options_.crypto_error_) { // Flip the bits in the first byte to cause the handshake to fail. ops->out_[0] ^= ops->out_[0]; @@ -157,13 +145,8 @@ static ssl_private_key_result_t privateKeyDecrypt(SSL* ssl, uint8_t* out, size_t return ssl_private_key_failure; } - EVP_PKEY* rsa_pkey = ops->getPrivateKey(); - if (!rsa_pkey) { - return ssl_private_key_failure; - } - - rsa = EVP_PKEY_get0_RSA(rsa_pkey); - if (rsa == NULL) { + rsa = ops->getPrivateKey(); + if (rsa == nullptr) { return ssl_private_key_failure; } diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h index 730dd730b862..f6b3afd52b8c 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h @@ -39,7 +39,7 @@ class RsaPrivateKeyConnection : public virtual Ssl::PrivateKeyConnection { RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, RsaPrivateKeyConnectionTestOptions& test_options); - EVP_PKEY* getPrivateKey() { return pkey_.get(); }; + RSA* getPrivateKey() { return EVP_PKEY_get0_RSA(pkey_.get()); } void delayed_op(); // Store the output data temporarily. diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index e13601630baa..a81e1669b4ad 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -253,7 +253,6 @@ TLSv TLV TMPDIR TODO -TORTIOUS TPROXY TSAN TSI From 1c5630d32fa1358b9dca1a565c4b7b834e1d0eb3 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 27 May 2019 16:09:47 +0300 Subject: [PATCH 23/56] tests: fix a typo. Signed-off-by: Ismo Puustinen --- .../transport_sockets/tls/rsa_private_key_method_provider.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index ffaff32d1f80..7fb5876509f4 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -68,7 +68,7 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o return ssl_private_key_failure; } - // Addd RSA padding to the the hash. Supported types are PSS and PKCS1. + // Add RSA padding to the the hash. if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) { msg_len = RSA_size(rsa); msg = static_cast(OPENSSL_malloc(msg_len)); From c92dfd672e216a7a98f0e3dcad6a1c071971067a Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 27 May 2019 18:00:05 +0300 Subject: [PATCH 24/56] tests: private key method multi-cert tests. Add a very basic ECDSA private key method provider. Some tests to verify that the provider works, and then to see that the correct private key method is used in a case where there are several private key methods added for a TLS context. Signed-off-by: Ismo Puustinen --- test/extensions/transport_sockets/tls/BUILD | 21 ++ .../tls/ecdsa_private_key_method_provider.cc | 180 +++++++++++++++ .../tls/ecdsa_private_key_method_provider.h | 79 +++++++ .../transport_sockets/tls/ssl_socket_test.cc | 213 ++++++++++++++++-- 4 files changed, 479 insertions(+), 14 deletions(-) create mode 100644 test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc create mode 100644 test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h diff --git a/test/extensions/transport_sockets/tls/BUILD b/test/extensions/transport_sockets/tls/BUILD index 0c0144e8c258..3c61133e4a99 100644 --- a/test/extensions/transport_sockets/tls/BUILD +++ b/test/extensions/transport_sockets/tls/BUILD @@ -21,6 +21,7 @@ envoy_cc_test( ], external_deps = ["ssl"], deps = [ + ":ecdsa_private_key_method_provider_test_lib", ":rsa_private_key_method_provider_test_lib", "//include/envoy/network:transport_socket_interface", "//source/common/buffer:buffer_lib", @@ -130,3 +131,23 @@ envoy_cc_test_library( "//source/common/protobuf:utility_lib", ], ) + +envoy_cc_test_library( + name = "ecdsa_private_key_method_provider_test_lib", + srcs = [ + "ecdsa_private_key_method_provider.cc", + ], + hdrs = [ + "ecdsa_private_key_method_provider.h", + ], + external_deps = ["ssl"], + deps = [ + "//include/envoy/api:api_interface", + "//include/envoy/event:dispatcher_interface", + "//include/envoy/server:transport_socket_config_interface", + "//include/envoy/ssl/private_key:private_key_config_interface", + "//include/envoy/ssl/private_key:private_key_interface", + "//source/common/config:utility_lib", + "//source/common/protobuf:utility_lib", + ], +) diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc new file mode 100644 index 000000000000..6cf386a9be5e --- /dev/null +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc @@ -0,0 +1,180 @@ +#include "test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h" + +#include + +#include "envoy/api/api.h" + +#include "openssl/ssl.h" + +namespace Envoy { +namespace Extensions { +namespace PrivateKeyMethodProvider { + +int EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index = -1; + +void EcdsaPrivateKeyConnection::delayed_op() { + const std::chrono::milliseconds timeout_0ms{0}; + + timer_ = dispatcher_.createTimer([this]() -> void { + finished_ = true; + this->cb_.complete(); + return; + }); + timer_->enableTimer(timeout_0ms); +} + +static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out, uint16_t signature_algorithm, + const uint8_t* in, size_t in_len) { + (void)out; + (void)out_len; + (void)signature_algorithm; + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int hash_len; + EC_KEY* ec_key; + const EVP_MD* md; + bssl::ScopedEVP_MD_CTX ctx; + EcdsaPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index)); + + if (!ops) { + return ssl_private_key_failure; + } + + ec_key = ops->getPrivateKey(); + if (!ec_key) { + return ssl_private_key_failure; + } + + md = SSL_get_signature_algorithm_digest(signature_algorithm); + if (!md) { + EC_KEY_free(ec_key); + return ssl_private_key_failure; + } + + // Calculate the digest for signing. + if (!EVP_DigestInit_ex(ctx.get(), md, nullptr) || !EVP_DigestUpdate(ctx.get(), in, in_len) || + !EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) { + EC_KEY_free(ec_key); + return ssl_private_key_failure; + } + + ops->out_ = static_cast(OPENSSL_malloc(max_out)); + if (ops->out_ == nullptr) { + EC_KEY_free(ec_key); + return ssl_private_key_failure; + } + + if (!ECDSA_sign(0, hash, hash_len, ops->out_, &ops->out_len_, ec_key)) { + EC_KEY_free(ec_key); + OPENSSL_free(ops->out_); + return ssl_private_key_failure; + } + + EC_KEY_free(ec_key); + // Tell SSL socket that the operation is ready to be called again. + ops->delayed_op(); + + return ssl_private_key_retry; +} + +static ssl_private_key_result_t privateKeyDecrypt(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out, const uint8_t* in, + size_t in_len) { + (void)ssl; + (void)out; + (void)out_len; + (void)max_out; + (void)in; + (void)in_len; + + return ssl_private_key_failure; +} + +static ssl_private_key_result_t privateKeyComplete(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out) { + EcdsaPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index)); + + if (!ops->finished_) { + // The operation didn't finish yet, retry. + return ssl_private_key_retry; + } + + if (ops->test_options_.async_method_error_) { + OPENSSL_free(ops->out_); + return ssl_private_key_failure; + } + + if (ops->out_len_ > max_out) { + OPENSSL_free(ops->out_); + return ssl_private_key_failure; + } + + memcpy(out, ops->out_, ops->out_len_); + *out_len = ops->out_len_; + + OPENSSL_free(ops->out_); + + return ssl_private_key_success; +} + +Ssl::BoringSslPrivateKeyMethodSharedPtr +EcdsaPrivateKeyMethodProvider::getBoringSslPrivateKeyMethod() { + return method_; +} + +EcdsaPrivateKeyConnection::EcdsaPrivateKeyConnection( + SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, + bssl::UniquePtr pkey, EcdsaPrivateKeyConnectionTestOptions& test_options) + : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(move(pkey)) { + SSL_set_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index, this); +} + +Ssl::PrivateKeyConnectionPtr EcdsaPrivateKeyMethodProvider::getPrivateKeyConnection( + SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) { + bssl::UniquePtr bio( + BIO_new_mem_buf(const_cast(private_key_.data()), private_key_.size())); + bssl::UniquePtr pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); + if (pkey == nullptr) { + return nullptr; + } + + return std::make_unique(ssl, cb, dispatcher, move(pkey), + test_options_); +} + +EcdsaPrivateKeyMethodProvider::EcdsaPrivateKeyMethodProvider( + const ProtobufWkt::Struct& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) { + + std::string private_key_path; + + if (EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index == -1) { + EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index = + SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); + } + + for (auto& value_it : config.fields()) { + auto& value = value_it.second; + if (value_it.first == "private_key_file" && + value.kind_case() == ProtobufWkt::Value::kStringValue) { + private_key_path = value.string_value(); + } + if (value_it.first == "async_method_error" && + value.kind_case() == ProtobufWkt::Value::kBoolValue) { + test_options_.async_method_error_ = value.bool_value(); + } + } + + private_key_ = factory_context.api().fileSystem().fileReadToEnd(private_key_path); + + method_ = std::make_shared(); + method_->sign = privateKeySign; + method_->decrypt = privateKeyDecrypt; + method_->complete = privateKeyComplete; +} + +} // namespace PrivateKeyMethodProvider +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h new file mode 100644 index 000000000000..3ead45ceb5ff --- /dev/null +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h @@ -0,0 +1,79 @@ +#pragma once + +#include "envoy/event/dispatcher.h" +#include "envoy/server/transport_socket_config.h" +#include "envoy/ssl/private_key/private_key.h" +#include "envoy/ssl/private_key/private_key_config.h" + +#include "common/config/utility.h" +#include "common/protobuf/utility.h" + +namespace Envoy { +namespace Extensions { +namespace PrivateKeyMethodProvider { + +struct EcdsaPrivateKeyConnectionTestOptions { + // Return an error from the private key method completion function. + bool async_method_error_{}; +}; + +// An example ECDSA private key method provider here for testing the decrypt() and sign() +// functionality. +class EcdsaPrivateKeyConnection : public virtual Ssl::PrivateKeyConnection { +public: + EcdsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, + EcdsaPrivateKeyConnectionTestOptions& test_options); + EC_KEY* getPrivateKey() { return EVP_PKEY_get1_EC_KEY(pkey_.get()); } + void delayed_op(); + + // Store the output data temporarily. + uint8_t* out_; + unsigned int out_len_; + // Is the operation finished? + bool finished_{}; + EcdsaPrivateKeyConnectionTestOptions& test_options_; + +private: + Ssl::PrivateKeyConnectionCallbacks& cb_; + Event::Dispatcher& dispatcher_; + bssl::UniquePtr pkey_; + Event::TimerPtr timer_; +}; + +class EcdsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider { +public: + EcdsaPrivateKeyMethodProvider( + const ProtobufWkt::Struct& config, + Server::Configuration::TransportSocketFactoryContext& factory_context); + // Ssl::PrivateKeyMethodProvider + Ssl::PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, + Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher) override; + Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; + + static int ssl_ecdsa_connection_index; + +private: + Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; + std::string private_key_; + EcdsaPrivateKeyConnectionTestOptions test_options_; +}; + +class EcdsaPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { +public: + // Ssl::PrivateKeyMethodProviderInstanceFactory + Ssl::PrivateKeyMethodProviderSharedPtr + createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyMethod& message, + Server::Configuration::TransportSocketFactoryContext& + private_key_method_provider_context) override { + return std::make_shared(message.config(), + private_key_method_provider_context); + } + + std::string name() const override { return std::string("ecdsa_test"); }; +}; + +} // namespace PrivateKeyMethodProvider +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 0dfe02779932..5bf7a5e1b85d 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -19,6 +19,7 @@ #include "extensions/transport_sockets/tls/private_key/private_key_manager_impl.h" #include "extensions/transport_sockets/tls/ssl_socket.h" +#include "test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h" #include "test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h" #include "test/extensions/transport_sockets/tls/ssl_certs_test.h" #include "test/extensions/transport_sockets/tls/test_data/no_san_cert_info.h" @@ -248,13 +249,19 @@ void testUtil(const TestUtilOptions& options) { // For private key method testing. NiceMock context_manager; Extensions::PrivateKeyMethodProvider::RsaPrivateKeyMethodFactory rsa_factory; - Registry::InjectFactory private_key_method_factory( - rsa_factory); + Extensions::PrivateKeyMethodProvider::EcdsaPrivateKeyMethodFactory ecdsa_factory; + Registry::InjectFactory + rsa_private_key_method_factory(rsa_factory); + Registry::InjectFactory + ecdsa_private_key_method_factory(ecdsa_factory); PrivateKeyMethodManagerImpl private_key_method_manager; if (options.expectedPrivateKeyMethod()) { - EXPECT_CALL(server_factory_context, sslContextManager()).WillOnce(ReturnRef(context_manager)); + EXPECT_CALL(server_factory_context, sslContextManager()) + .WillOnce(ReturnRef(context_manager)) + .WillRepeatedly(ReturnRef(context_manager)); EXPECT_CALL(context_manager, privateKeyMethodManager()) - .WillOnce(ReturnRef(private_key_method_manager)); + .WillOnce(ReturnRef(private_key_method_manager)) + .WillRepeatedly(ReturnRef(private_key_method_manager)); } envoy::api::v2::auth::DownstreamTlsContext server_tls_context; @@ -4037,12 +4044,6 @@ TEST_P(SslReadBufferLimitTest, TestBind) { disconnect(); } -// TODO(ipuustin): The following tests are needed: -// success cases: -// * test multi-cert (does this require a ECDSA provider?) -// failure cases: -// * test incorrect decryption - // Test asynchronous signing (ECDHE) TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignSuccess) { const std::string server_ctx_yaml = R"EOF( @@ -4231,7 +4232,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignFailure) { "ssl.connection_error")); } -// Tet the sign operation return with an error. +// Test the sign operation return with an error. TEST_P(SslSocketTest, RsaPrivateKeyProviderSignFailure) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4262,7 +4263,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSignFailure) { "ssl.connection_error")); } -// Tet the decrypt operation return with an error. +// Test the decrypt operation return with an error. TEST_P(SslSocketTest, RsaPrivateKeyProviderDecryptFailure) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4293,7 +4294,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderDecryptFailure) { "ssl.connection_error")); } -// Tet the sign operation return with an error in complete. +// Test the sign operation return with an error in complete. TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignCompleteFailure) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4325,7 +4326,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignCompleteFailure) { .setExpectedServerStats("ssl.connection_error")); } -// Tet the decrypt operation return with an error in complete. +// Test the decrypt operation return with an error in complete. TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptCompleteFailure) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4357,6 +4358,190 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptCompleteFailure) { .setExpectedServerStats("ssl.connection_error")); } +// Test having one cert with private key method and another with just +// private key. +TEST_P(SslSocketTest, RsaPrivateKeyProviderMultiCertSuccess) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SELFSIGNED_ECDSA_P256_CERT_HASH); + + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + sync_mode: false + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, GetParam()); + testUtil(test_options.setPrivateKeyMethodExpected(true)); +} + +// Test having two certs with private key methods. This will +// synchronously fail because the second certificate is a ECDSA one and +// the RSA method can't handle it. +TEST_P(SslSocketTest, RsaPrivateKeyProviderMultiCertFail) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SELFSIGNED_ECDSA_P256_CERT_HASH); + + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + sync_mode: false + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" + expected_operation: sign + sync_mode: false +)EOF"; + + TestUtilOptions failing_test_options(client_ctx_yaml, server_ctx_yaml, false, GetParam()); + testUtil(failing_test_options.setPrivateKeyMethodExpected(true) + .setExpectedServerCloseEvent(Network::ConnectionEvent::RemoteClose) + .setExpectedServerStats("ssl.connection_error")); +} + +// Test ECDSA private key method provider. +TEST_P(SslSocketTest, EcdsaPrivateKeyProviderSuccess) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SELFSIGNED_ECDSA_P256_CERT_HASH); + + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" + private_key_method: + provider_name: ecdsa_test + config: + private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, GetParam()); + testUtil(test_options.setPrivateKeyMethodExpected(true)); +} + +// Test having two certs with different private key methods. It's expected that the ECDSA provider +// is being used. RSA provider is set to fail with "async_method_error", but that's not happening. +TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertSuccess) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SELFSIGNED_ECDSA_P256_CERT_HASH); + + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + sync_mode: false + async_method_error: true + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" + private_key_method: + provider_name: ecdsa_test + config: + private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" +)EOF"; + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, GetParam()); + testUtil(test_options.setPrivateKeyMethodExpected(true)); +} + +// Test having two certs with different private key methods. ECDSA provider is set to fail. +TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertFail) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SELFSIGNED_ECDSA_P256_CERT_HASH); + + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" + private_key_method: + provider_name: rsa_test + config: + private_key_file: "{{ test_tmpdir }}/unittestkey.pem" + expected_operation: sign + sync_mode: false + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" + private_key_method: + provider_name: ecdsa_test + config: + private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" + async_method_error: true +)EOF"; + TestUtilOptions failing_test_options(client_ctx_yaml, server_ctx_yaml, false, GetParam()); + testUtil(failing_test_options.setPrivateKeyMethodExpected(true) + .setExpectedServerCloseEvent(Network::ConnectionEvent::LocalClose) + .setExpectedServerStats("ssl.connection_error")); +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions From 86ecba914deea6367e088238bd045e287b74877a Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 28 May 2019 14:48:04 +0300 Subject: [PATCH 25/56] dispatcher: add a function for returning current thread id. Signed-off-by: Ismo Puustinen --- include/envoy/event/dispatcher.h | 5 +++++ source/common/event/dispatcher_impl.cc | 4 ++++ source/common/event/dispatcher_impl.h | 1 + test/mocks/event/mocks.h | 1 + 4 files changed, 11 insertions(+) diff --git a/include/envoy/event/dispatcher.h b/include/envoy/event/dispatcher.h index 0024b3a2e779..90a172435e8a 100644 --- a/include/envoy/event/dispatcher.h +++ b/include/envoy/event/dispatcher.h @@ -199,6 +199,11 @@ class Dispatcher { * @return the watermark buffer factory for this dispatcher. */ virtual Buffer::WatermarkFactory& getWatermarkFactory() PURE; + + /** + * @return the current thread id. + */ + virtual Thread::ThreadIdPtr getCurrentThreadId() PURE; }; typedef std::unique_ptr DispatcherPtr; diff --git a/source/common/event/dispatcher_impl.cc b/source/common/event/dispatcher_impl.cc index c7d2c0365396..7cb1780eb080 100644 --- a/source/common/event/dispatcher_impl.cc +++ b/source/common/event/dispatcher_impl.cc @@ -199,5 +199,9 @@ void DispatcherImpl::runPostCallbacks() { } } +Thread::ThreadIdPtr DispatcherImpl::getCurrentThreadId() { + return api_.threadFactory().currentThreadId(); +} + } // namespace Event } // namespace Envoy diff --git a/source/common/event/dispatcher_impl.h b/source/common/event/dispatcher_impl.h index b712f22d879e..2e6a73319876 100644 --- a/source/common/event/dispatcher_impl.h +++ b/source/common/event/dispatcher_impl.h @@ -65,6 +65,7 @@ class DispatcherImpl : Logger::Loggable, public Dispatcher { void post(std::function callback) override; void run(RunType type) override; Buffer::WatermarkFactory& getWatermarkFactory() override { return *buffer_factory_; } + Thread::ThreadIdPtr getCurrentThreadId() override; private: void runPostCallbacks(); diff --git a/test/mocks/event/mocks.h b/test/mocks/event/mocks.h index 4237a3b57ca0..38e8431dd042 100644 --- a/test/mocks/event/mocks.h +++ b/test/mocks/event/mocks.h @@ -114,6 +114,7 @@ class MockDispatcher : public Dispatcher { MOCK_METHOD1(post, void(std::function callback)); MOCK_METHOD1(run, void(RunType type)); Buffer::WatermarkFactory& getWatermarkFactory() override { return buffer_factory_; } + MOCK_METHOD0(getCurrentThreadId, Thread::ThreadIdPtr(void)); GlobalTimeSystem time_system_; std::list to_delete_; From 3cbe3a533541369e01a251aa2180ab1d3a6a144e Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 28 May 2019 14:48:45 +0300 Subject: [PATCH 26/56] ssl_socket: check the private key method callback thread id. Signed-off-by: Ismo Puustinen --- source/extensions/transport_sockets/tls/ssl_socket.cc | 8 ++++++++ source/extensions/transport_sockets/tls/ssl_socket.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index abeb1a90bfb6..fe5148413aa9 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -64,6 +64,7 @@ void SslSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& c // private key methods). std::vector providers = ctx_->getPrivateKeyMethodProviders(); + for (auto const& provider : providers) { Ssl::PrivateKeyConnectionPtr pk_connection = provider->getPrivateKeyConnection(ssl_.get(), *this, callbacks_->connection().dispatcher()); @@ -75,6 +76,12 @@ void SslSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& c // because they need to associated with the SSL objects so that user data can be passed to the // BoringSSL private key methods. pk_connections_.emplace_back(std::move(pk_connection)); + + if (run_tid_ == nullptr) { + // Store the dispatcher thread ID. We will check that the caller is the same when the private + // key method callback is received. + run_tid_ = callbacks_->connection().dispatcher().getCurrentThreadId(); + } } BIO* bio = BIO_new_socket(callbacks_->ioHandle().fd(), 0); @@ -142,6 +149,7 @@ Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { } void SslSocket::complete() { + ASSERT(isThreadSafe()); ASSERT(async_handshake_in_progress_); async_handshake_in_progress_ = false; diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index fd973a1151d1..57d535ff7b21 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -83,6 +83,7 @@ class SslSocket : public Network::TransportSocket, Network::PostIoAction doHandshake(); void drainErrorQueue(); void shutdownSsl(); + bool isThreadSafe() const { return run_tid_->isCurrentThreadId(); } Network::TransportSocketCallbacks* callbacks_{}; ContextImplSharedPtr ctx_; @@ -96,6 +97,7 @@ class SslSocket : public Network::TransportSocket, mutable std::string cached_url_encoded_pem_encoded_peer_cert_chain_; std::vector pk_connections_; bool async_handshake_in_progress_{}; + Thread::ThreadIdPtr run_tid_; }; class ClientSslSocketFactory : public Network::TransportSocketFactory, From 976a469fe0ce96d37943e0fcbcaedde1125fc112 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 3 Jun 2019 13:00:32 +0300 Subject: [PATCH 27/56] tests: unique pointers, vectors, and cleanups. Signed-off-by: Ismo Puustinen --- .../tls/ecdsa_private_key_method_provider.cc | 30 +++++---------- .../tls/ecdsa_private_key_method_provider.h | 3 +- .../tls/rsa_private_key_method_provider.cc | 38 +++++-------------- .../tls/rsa_private_key_method_provider.h | 3 +- 4 files changed, 21 insertions(+), 53 deletions(-) diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc index 6cf386a9be5e..b9ef2d446c39 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc @@ -26,52 +26,44 @@ void EcdsaPrivateKeyConnection::delayed_op() { static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* out_len, size_t max_out, uint16_t signature_algorithm, const uint8_t* in, size_t in_len) { - (void)out; (void)out_len; + (void)max_out; (void)signature_algorithm; unsigned char hash[EVP_MAX_MD_SIZE]; unsigned int hash_len; - EC_KEY* ec_key; const EVP_MD* md; bssl::ScopedEVP_MD_CTX ctx; EcdsaPrivateKeyConnection* ops = static_cast( SSL_get_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index)); + unsigned int result_len; if (!ops) { return ssl_private_key_failure; } - ec_key = ops->getPrivateKey(); + bssl::UniquePtr ec_key(ops->getPrivateKey()); if (!ec_key) { return ssl_private_key_failure; } md = SSL_get_signature_algorithm_digest(signature_algorithm); if (!md) { - EC_KEY_free(ec_key); return ssl_private_key_failure; } // Calculate the digest for signing. if (!EVP_DigestInit_ex(ctx.get(), md, nullptr) || !EVP_DigestUpdate(ctx.get(), in, in_len) || !EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) { - EC_KEY_free(ec_key); return ssl_private_key_failure; } - ops->out_ = static_cast(OPENSSL_malloc(max_out)); - if (ops->out_ == nullptr) { - EC_KEY_free(ec_key); + // Borrow "out" because it has been already initialized to the max_out size. + if (!ECDSA_sign(0, hash, hash_len, out, &result_len, ec_key.get())) { return ssl_private_key_failure; } - if (!ECDSA_sign(0, hash, hash_len, ops->out_, &ops->out_len_, ec_key)) { - EC_KEY_free(ec_key); - OPENSSL_free(ops->out_); - return ssl_private_key_failure; - } + ops->output_.assign(out, out + result_len); - EC_KEY_free(ec_key); // Tell SSL socket that the operation is ready to be called again. ops->delayed_op(); @@ -102,19 +94,15 @@ static ssl_private_key_result_t privateKeyComplete(SSL* ssl, uint8_t* out, size_ } if (ops->test_options_.async_method_error_) { - OPENSSL_free(ops->out_); return ssl_private_key_failure; } - if (ops->out_len_ > max_out) { - OPENSSL_free(ops->out_); + if (ops->output_.size() > max_out) { return ssl_private_key_failure; } - memcpy(out, ops->out_, ops->out_len_); - *out_len = ops->out_len_; - - OPENSSL_free(ops->out_); + std::copy(ops->output_.begin(), ops->output_.end(), out); + *out_len = ops->output_.size(); return ssl_private_key_success; } diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h index 3ead45ceb5ff..fa811ab4cb2d 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h @@ -28,8 +28,7 @@ class EcdsaPrivateKeyConnection : public virtual Ssl::PrivateKeyConnection { void delayed_op(); // Store the output data temporarily. - uint8_t* out_; - unsigned int out_len_; + std::vector output_; // Is the operation finished? bool finished_{}; EcdsaPrivateKeyConnectionTestOptions& test_options_; diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index 7fb5876509f4..50d2e2e37842 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -88,16 +88,10 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o padding = RSA_PKCS1_PADDING; } - ops->out_ = static_cast(OPENSSL_malloc(max_out)); - if (ops->out_ == nullptr) { - return ssl_private_key_failure; - } - - if (!RSA_sign_raw(rsa, &ops->out_len_, ops->out_, max_out, msg, msg_len, padding)) { + if (!RSA_sign_raw(rsa, out_len, out, max_out, msg, msg_len, padding)) { if (prefix_allocated) { OPENSSL_free(msg); } - OPENSSL_free(ops->out_); return ssl_private_key_failure; } @@ -107,17 +101,16 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o if (ops->test_options_.crypto_error_) { // Flip the bits in the first byte to cause the handshake to fail. - ops->out_[0] ^= ops->out_[0]; + out[0] ^= out[0]; } if (ops->test_options_.sync_mode_) { // Return immediately with the results. - memcpy(out, ops->out_, ops->out_len_); - *out_len = ops->out_len_; - OPENSSL_free(ops->out_); return ssl_private_key_success; } + ops->output_.assign(out, out + *out_len); + // Tell SSL socket that the operation is ready to be called again. ops->delayed_op(); @@ -150,24 +143,17 @@ static ssl_private_key_result_t privateKeyDecrypt(SSL* ssl, uint8_t* out, size_t return ssl_private_key_failure; } - ops->out_ = static_cast(OPENSSL_malloc(max_out)); - if (ops->out_ == nullptr) { - return ssl_private_key_failure; - } - - if (!RSA_decrypt(rsa, &ops->out_len_, ops->out_, max_out, in, in_len, RSA_NO_PADDING)) { - OPENSSL_free(ops->out_); + if (!RSA_decrypt(rsa, out_len, out, max_out, in, in_len, RSA_NO_PADDING)) { return ssl_private_key_failure; } if (ops->test_options_.sync_mode_) { // Return immediately with the results. - memcpy(out, ops->out_, ops->out_len_); - *out_len = ops->out_len_; - OPENSSL_free(ops->out_); return ssl_private_key_success; } + ops->output_.assign(out, out + *out_len); + ops->delayed_op(); return ssl_private_key_retry; @@ -184,19 +170,15 @@ static ssl_private_key_result_t privateKeyComplete(SSL* ssl, uint8_t* out, size_ } if (ops->test_options_.async_method_error_) { - OPENSSL_free(ops->out_); return ssl_private_key_failure; } - if (ops->out_len_ > max_out) { - OPENSSL_free(ops->out_); + if (ops->output_.size() > max_out) { return ssl_private_key_failure; } - memcpy(out, ops->out_, ops->out_len_); - *out_len = ops->out_len_; - - OPENSSL_free(ops->out_); + std::copy(ops->output_.begin(), ops->output_.end(), out); + *out_len = ops->output_.size(); return ssl_private_key_success; } diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h index f6b3afd52b8c..587539f174d2 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h @@ -43,8 +43,7 @@ class RsaPrivateKeyConnection : public virtual Ssl::PrivateKeyConnection { void delayed_op(); // Store the output data temporarily. - uint8_t* out_; - size_t out_len_; + std::vector output_; // Is the operation finished? bool finished_{}; From 789368aed9d07959b90ff912f94b6a3e4d3dbb33 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 3 Jun 2019 18:12:53 +0300 Subject: [PATCH 28/56] tls: cleanups. Signed-off-by: Ismo Puustinen --- api/envoy/api/v2/auth/cert.proto | 8 ++++---- .../transport_sockets/tls/context_config_impl.cc | 1 - source/extensions/transport_sockets/tls/context_impl.cc | 2 +- .../extensions/transport_sockets/tls/context_impl_test.cc | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/api/envoy/api/v2/auth/cert.proto b/api/envoy/api/v2/auth/cert.proto index d1b41048fb2a..0e17f3c6586e 100644 --- a/api/envoy/api/v2/auth/cert.proto +++ b/api/envoy/api/v2/auth/cert.proto @@ -124,6 +124,10 @@ message TlsCertificate { // The TLS private key. core.DataSource private_key = 2; + // BoringSSL private key operations. This is an alternative to private_key field. This can't be + // marked as "oneof" due to API compatibility reasons. + PrivateKeyMethod private_key_method = 6; + // The password to decrypt the TLS private key. If this field is not set, it is assumed that the // TLS private key is not password encrypted. core.DataSource password = 3; @@ -133,10 +137,6 @@ message TlsCertificate { // [#not-implemented-hide:] repeated core.DataSource signed_certificate_timestamp = 5; - - // BoringSSL private key operations. This is an alternative to private_key field. This can't be - // marked as "oneof" due to API compatibility reasons. - PrivateKeyMethod private_key_method = 6; } message TlsSessionTicketKeys { diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index 4a779f523b5a..1bd9fc6ef4e4 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -26,7 +26,6 @@ std::vector getTlsCertificateConf if (!config.tls_certificates().empty()) { std::vector providers; for (const auto& tls_certificate : config.tls_certificates()) { - Ssl::PrivateKeyMethodProviderSharedPtr private_key_method_provider = nullptr; if (!tls_certificate.has_private_key_method() && !tls_certificate.has_certificate_chain() && !tls_certificate.has_private_key()) { continue; diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index ca98245d0c08..8230057004f7 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -312,7 +312,7 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c private_key_method_provider->getBoringSslPrivateKeyMethod(); if (private_key_method == nullptr) { throw EnvoyException( - fmt::format("Failed to get BoringSsl private key method from provider")); + fmt::format("Failed to get BoringSSL private key method from provider")); } SSL_CTX_set_private_key_method(ctx.ssl_ctx_.get(), private_key_method.get()); } else { diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index 5008beaeba87..299111784e0b 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -1259,7 +1259,7 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoMethod) { EXPECT_THROW_WITH_MESSAGE( Envoy::Ssl::ServerContextSharedPtr server_ctx( manager.createSslServerContext(store, server_context_config, std::vector{})), - EnvoyException, "Failed to get BoringSsl private key method from provider"); + EnvoyException, "Failed to get BoringSSL private key method from provider"); } TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadSuccess) { From 50d6450e3eaabee3e15aaed2bf5b4a0422772dff Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 4 Jun 2019 11:31:13 +0300 Subject: [PATCH 29/56] tls: complete() -> onPrivateKeyMethodComplete() Signed-off-by: Ismo Puustinen --- include/envoy/ssl/private_key/private_key_callbacks.h | 2 +- source/extensions/transport_sockets/tls/ssl_socket.cc | 2 +- source/extensions/transport_sockets/tls/ssl_socket.h | 2 +- .../transport_sockets/tls/ecdsa_private_key_method_provider.cc | 2 +- .../transport_sockets/tls/rsa_private_key_method_provider.cc | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/envoy/ssl/private_key/private_key_callbacks.h b/include/envoy/ssl/private_key/private_key_callbacks.h index b7f2048fb2a6..2390fc7ff29e 100644 --- a/include/envoy/ssl/private_key/private_key_callbacks.h +++ b/include/envoy/ssl/private_key/private_key_callbacks.h @@ -18,7 +18,7 @@ class PrivateKeyConnectionCallbacks { * provider will communicate the success status when SSL_do_handshake() * is called the next time. */ - virtual void complete() PURE; + virtual void onPrivateKeyMethodComplete() PURE; }; } // namespace Ssl diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index fe5148413aa9..f6397a7ef01e 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -148,7 +148,7 @@ Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { return {action, bytes_read, end_stream}; } -void SslSocket::complete() { +void SslSocket::onPrivateKeyMethodComplete() { ASSERT(isThreadSafe()); ASSERT(async_handshake_in_progress_); async_handshake_in_progress_ = false; diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index 57d535ff7b21..b839de2090b5 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -75,7 +75,7 @@ class SslSocket : public Network::TransportSocket, const Ssl::ConnectionInfo* ssl() const override { return this; } // Ssl::PrivateKeyConnectionCallbacks - void complete() override; + void onPrivateKeyMethodComplete() override; SSL* rawSslForTest() const { return ssl_.get(); } diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc index b9ef2d446c39..52440ffbe5f0 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc @@ -17,7 +17,7 @@ void EcdsaPrivateKeyConnection::delayed_op() { timer_ = dispatcher_.createTimer([this]() -> void { finished_ = true; - this->cb_.complete(); + this->cb_.onPrivateKeyMethodComplete(); return; }); timer_->enableTimer(timeout_0ms); diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index 50d2e2e37842..e6407b3f57c4 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -17,7 +17,7 @@ void RsaPrivateKeyConnection::delayed_op() { timer_ = dispatcher_.createTimer([this]() -> void { finished_ = true; - this->cb_.complete(); + this->cb_.onPrivateKeyMethodComplete(); return; }); timer_->enableTimer(timeout_0ms); From 9146b400518c1ea4d35e3889dbdafeb824110524 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 4 Jun 2019 15:06:36 +0300 Subject: [PATCH 30/56] tests: FIPS check, private key read from disk only once. Signed-off-by: Ismo Puustinen --- include/envoy/ssl/private_key/private_key.h | 6 ++++++ .../transport_sockets/tls/context_impl.cc | 5 +++++ .../tls/ecdsa_private_key_method_provider.h | 1 + .../tls/rsa_private_key_method_provider.cc | 20 ++++++++++--------- .../tls/rsa_private_key_method_provider.h | 3 ++- test/mocks/ssl/mocks.h | 1 + 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index 4cb15f199b28..341babf6e0ca 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -45,6 +45,12 @@ class PrivateKeyMethodProvider { PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) PURE; + /** + * Check whether the private key method satisfies FIPS requirements. + * @return true if FIPS key requirements are satisfied, false if not. + */ + virtual bool checkFips() PURE; + /** * Get the private key methods from the provider. * @return the private key methods associated with this provider and diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 8230057004f7..535022719953 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -314,6 +314,11 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c throw EnvoyException( fmt::format("Failed to get BoringSSL private key method from provider")); } +#ifdef BORINGSSL_FIPS + if (!ctx.private_key_method_provider_.checkFips()) { + throw EnvoyException(fmt::format("Private key method doesn't support FIPS mode with current parameters"); + } +#endif SSL_CTX_set_private_key_method(ctx.ssl_ctx_.get(), private_key_method.get()); } else { // Load private key. diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h index fa811ab4cb2d..7d6e26c274a8 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h @@ -49,6 +49,7 @@ class EcdsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvid Ssl::PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) override; + bool checkFips() override { return false; }; Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; static int ssl_ecdsa_connection_index; diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index e6407b3f57c4..a42781a713bf 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -192,20 +192,15 @@ RsaPrivateKeyConnection::RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnec Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, RsaPrivateKeyConnectionTestOptions& test_options) - : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(move(pkey)) { + : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(std::move(pkey)) { SSL_set_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index, this); } Ssl::PrivateKeyConnectionPtr RsaPrivateKeyMethodProvider::getPrivateKeyConnection( SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) { - bssl::UniquePtr bio( - BIO_new_mem_buf(const_cast(private_key_.data()), private_key_.size())); - bssl::UniquePtr pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); - if (pkey == nullptr) { - return nullptr; - } - return std::make_unique(ssl, cb, dispatcher, move(pkey), test_options_); + return std::make_unique(ssl, cb, dispatcher, bssl::UpRef(pkey_), + test_options_); } RsaPrivateKeyMethodProvider::RsaPrivateKeyMethodProvider( @@ -248,7 +243,14 @@ RsaPrivateKeyMethodProvider::RsaPrivateKeyMethodProvider( } } - private_key_ = factory_context.api().fileSystem().fileReadToEnd(private_key_path); + std::string private_key = factory_context.api().fileSystem().fileReadToEnd(private_key_path); + bssl::UniquePtr bio( + BIO_new_mem_buf(const_cast(private_key.data()), private_key.size())); + bssl::UniquePtr pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); + if (pkey == nullptr) { + throw EnvoyException("Failed to read private key from disk."); + } + pkey_ = std::move(pkey); method_ = std::make_shared(); method_->sign = privateKeySign; diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h index 587539f174d2..9c07b0a269fd 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h @@ -65,13 +65,14 @@ class RsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider Ssl::PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) override; + bool checkFips() override { return false; }; Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; static int ssl_rsa_connection_index; private: Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; - std::string private_key_; + bssl::UniquePtr pkey_; RsaPrivateKeyConnectionTestOptions test_options_; }; diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index f2d3c624e02a..6b553c16b914 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -81,6 +81,7 @@ class MockPrivateKeyMethodProvider : public PrivateKeyMethodProvider { MOCK_METHOD3(getPrivateKeyConnection, PrivateKeyConnectionPtr(SSL* ssl, PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher)); + MOCK_METHOD0(checkFips, bool()); MOCK_METHOD0(getBoringSslPrivateKeyMethod, BoringSslPrivateKeyMethodSharedPtr()); }; From 8a688f6b7808a414486968950aca8449b4028617 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 4 Jun 2019 16:15:23 +0300 Subject: [PATCH 31/56] tls: fix typos. Signed-off-by: Ismo Puustinen --- source/extensions/transport_sockets/tls/context_impl.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 535022719953..325d6502eb09 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -315,8 +315,9 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c fmt::format("Failed to get BoringSSL private key method from provider")); } #ifdef BORINGSSL_FIPS - if (!ctx.private_key_method_provider_.checkFips()) { - throw EnvoyException(fmt::format("Private key method doesn't support FIPS mode with current parameters"); + if (!ctx.private_key_method_provider_->checkFips()) { + throw EnvoyException( + fmt::format("Private key method doesn't support FIPS mode with current parameters")); } #endif SSL_CTX_set_private_key_method(ctx.ssl_ctx_.get(), private_key_method.get()); From 455be7363bc79ea3308c14b04e9ed58afac873fc Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 5 Jun 2019 10:50:52 +0300 Subject: [PATCH 32/56] tests: checkFips() implementation. Also add one-time key read to ECDSA provider. Signed-off-by: Ismo Puustinen --- .../tls/ecdsa_private_key_method_provider.cc | 31 +++++++++++++------ .../tls/ecdsa_private_key_method_provider.h | 4 +-- .../tls/rsa_private_key_method_provider.cc | 14 ++++++++- .../tls/rsa_private_key_method_provider.h | 2 +- .../transport_sockets/tls/ssl_socket_test.cc | 5 ++- 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc index 52440ffbe5f0..d60ae5290ac1 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc @@ -112,6 +112,14 @@ EcdsaPrivateKeyMethodProvider::getBoringSslPrivateKeyMethod() { return method_; } +bool EcdsaPrivateKeyMethodProvider::checkFips() { + const EC_KEY* ecdsa_private_key = EVP_PKEY_get0_EC_KEY(pkey_.get()); + if (ecdsa_private_key == nullptr || !EC_KEY_check_fips(ecdsa_private_key)) { + return false; + } + return true; +} + EcdsaPrivateKeyConnection::EcdsaPrivateKeyConnection( SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, EcdsaPrivateKeyConnectionTestOptions& test_options) @@ -121,14 +129,7 @@ EcdsaPrivateKeyConnection::EcdsaPrivateKeyConnection( Ssl::PrivateKeyConnectionPtr EcdsaPrivateKeyMethodProvider::getPrivateKeyConnection( SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) { - bssl::UniquePtr bio( - BIO_new_mem_buf(const_cast(private_key_.data()), private_key_.size())); - bssl::UniquePtr pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); - if (pkey == nullptr) { - return nullptr; - } - - return std::make_unique(ssl, cb, dispatcher, move(pkey), + return std::make_unique(ssl, cb, dispatcher, bssl::UpRef(pkey_), test_options_); } @@ -155,7 +156,19 @@ EcdsaPrivateKeyMethodProvider::EcdsaPrivateKeyMethodProvider( } } - private_key_ = factory_context.api().fileSystem().fileReadToEnd(private_key_path); + std::string private_key = factory_context.api().fileSystem().fileReadToEnd(private_key_path); + bssl::UniquePtr bio( + BIO_new_mem_buf(const_cast(private_key.data()), private_key.size())); + bssl::UniquePtr pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); + if (pkey == nullptr) { + throw EnvoyException("Failed to read private key from disk."); + } + + if (EVP_PKEY_id(pkey.get()) != EVP_PKEY_EC) { + throw EnvoyException("Private key is of wrong type."); + } + + pkey_ = std::move(pkey); method_ = std::make_shared(); method_->sign = privateKeySign; diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h index 7d6e26c274a8..90e1b6e5867b 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h @@ -49,14 +49,14 @@ class EcdsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvid Ssl::PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) override; - bool checkFips() override { return false; }; + bool checkFips() override; Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; static int ssl_ecdsa_connection_index; private: Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; - std::string private_key_; + bssl::UniquePtr pkey_; EcdsaPrivateKeyConnectionTestOptions test_options_; }; diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index a42781a713bf..49d668d7a163 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -188,6 +188,14 @@ RsaPrivateKeyMethodProvider::getBoringSslPrivateKeyMethod() { return method_; } +bool RsaPrivateKeyMethodProvider::checkFips() { + RSA* rsa_private_key = EVP_PKEY_get0_RSA(pkey_.get()); + if (rsa_private_key == nullptr || !RSA_check_fips(rsa_private_key)) { + return false; + } + return true; +} + RsaPrivateKeyConnection::RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, @@ -198,7 +206,6 @@ RsaPrivateKeyConnection::RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnec Ssl::PrivateKeyConnectionPtr RsaPrivateKeyMethodProvider::getPrivateKeyConnection( SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) { - return std::make_unique(ssl, cb, dispatcher, bssl::UpRef(pkey_), test_options_); } @@ -250,6 +257,11 @@ RsaPrivateKeyMethodProvider::RsaPrivateKeyMethodProvider( if (pkey == nullptr) { throw EnvoyException("Failed to read private key from disk."); } + + if (EVP_PKEY_id(pkey.get()) != EVP_PKEY_RSA) { + throw EnvoyException("Private key is of wrong type."); + } + pkey_ = std::move(pkey); method_ = std::make_shared(); diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h index 9c07b0a269fd..8e48bc13da37 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h @@ -65,7 +65,7 @@ class RsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider Ssl::PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) override; - bool checkFips() override { return false; }; + bool checkFips() override; Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; static int ssl_rsa_connection_index; diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 5bf7a5e1b85d..af4f8c2004ba 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -4432,9 +4432,8 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderMultiCertFail) { )EOF"; TestUtilOptions failing_test_options(client_ctx_yaml, server_ctx_yaml, false, GetParam()); - testUtil(failing_test_options.setPrivateKeyMethodExpected(true) - .setExpectedServerCloseEvent(Network::ConnectionEvent::RemoteClose) - .setExpectedServerStats("ssl.connection_error")); + EXPECT_THROW_WITH_MESSAGE(testUtil(failing_test_options.setPrivateKeyMethodExpected(true)), + EnvoyException, "Private key is of wrong type.") } // Test ECDSA private key method provider. From 14b6d336b7e79349bd187af706fbb002f0b3a15a Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 5 Jun 2019 13:18:42 +0300 Subject: [PATCH 33/56] tls: remove one of three TlsCertificateConfig constructors. Signed-off-by: Ismo Puustinen --- .../common/ssl/tls_certificate_config_impl.cc | 45 ++++++++++--------- .../common/ssl/tls_certificate_config_impl.h | 12 ++--- .../tls/context_config_impl.cc | 16 +++---- .../tls/context_impl_test.cc | 9 ++++ 4 files changed, 46 insertions(+), 36 deletions(-) diff --git a/source/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index 6913f8d77a4a..ac666027ca20 100644 --- a/source/common/ssl/tls_certificate_config_impl.cc +++ b/source/common/ssl/tls_certificate_config_impl.cc @@ -23,35 +23,36 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl( .value_or(private_key_.empty() ? EMPTY_STRING : INLINE_STRING)), password_(Config::DataSource::read(config.password(), true, api)), password_path_(Config::DataSource::getPath(config.password()) - .value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)) {} + .value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)) { -TlsCertificateConfigImpl::TlsCertificateConfigImpl( - const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api, - bool expect_private_key_method) - : TlsCertificateConfigImpl(config, api) { - { - if (!expect_private_key_method) { - if (certificate_chain_.empty() || private_key_.empty()) { - throw EnvoyException(fmt::format("Failed to load incomplete certificate from {}, {}", - certificate_chain_path_, private_key_path_)); - } - } + if (certificate_chain_.empty() || private_key_.empty()) { + throw EnvoyException(fmt::format("Failed to load incomplete certificate from {}, {}", + certificate_chain_path_, private_key_path_)); } } TlsCertificateConfigImpl::TlsCertificateConfigImpl( const envoy::api::v2::auth::TlsCertificate& config, Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) - : TlsCertificateConfigImpl(config, api, true) { - if (config.has_private_key_method()) { - if (config.has_private_key()) { - throw EnvoyException(fmt::format( - "Certificate configuration can't have both private_key and private_key_method")); - } - private_key_method_ = - factory_context.sslContextManager() - .privateKeyMethodManager() - .createPrivateKeyMethodProvider(config.private_key_method(), factory_context); + : certificate_chain_(Config::DataSource::read(config.certificate_chain(), true, api)), + certificate_chain_path_( + Config::DataSource::getPath(config.certificate_chain()) + .value_or(certificate_chain_.empty() ? EMPTY_STRING : INLINE_STRING)), + private_key_(Config::DataSource::read(config.private_key(), true, api)), + private_key_path_(Config::DataSource::getPath(config.private_key()) + .value_or(private_key_.empty() ? EMPTY_STRING : INLINE_STRING)), + password_(Config::DataSource::read(config.password(), true, api)), + password_path_(Config::DataSource::getPath(config.password()) + .value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)), + private_key_method_( + config.has_private_key_method() + ? factory_context.sslContextManager() + .privateKeyMethodManager() + .createPrivateKeyMethodProvider(config.private_key_method(), factory_context) + : nullptr) { + if (config.has_private_key_method() && config.has_private_key()) { + throw EnvoyException(fmt::format( + "Certificate configuration can't have both private_key and private_key_method")); } if (certificate_chain_.empty() || (private_key_.empty() && private_key_method_ == nullptr)) { throw EnvoyException(fmt::format("Failed to load incomplete certificate from {}, {}", diff --git a/source/common/ssl/tls_certificate_config_impl.h b/source/common/ssl/tls_certificate_config_impl.h index b5844226bace..05c4b282044f 100644 --- a/source/common/ssl/tls_certificate_config_impl.h +++ b/source/common/ssl/tls_certificate_config_impl.h @@ -12,8 +12,9 @@ namespace Ssl { class TlsCertificateConfigImpl : public TlsCertificateConfig { public: - TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api, - bool expect_private_key_method); + // SDS uses this constructor. + TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api); + // Regular constructor, works also for private key methods. TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api); @@ -22,21 +23,20 @@ class TlsCertificateConfigImpl : public TlsCertificateConfig { const std::string& certificateChainPath() const override { return certificate_chain_path_; } const std::string& privateKey() const override { return private_key_; } const std::string& privateKeyPath() const override { return private_key_path_; } + const std::string& password() const override { return password_; } + const std::string& passwordPath() const override { return password_path_; } Envoy::Ssl::PrivateKeyMethodProviderSharedPtr privateKeyMethod() const override { return private_key_method_; } - const std::string& password() const override { return password_; } - const std::string& passwordPath() const override { return password_path_; } private: - TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api); const std::string certificate_chain_; const std::string certificate_chain_path_; const std::string private_key_; const std::string private_key_path_; - Envoy::Ssl::PrivateKeyMethodProviderSharedPtr private_key_method_{}; const std::string password_; const std::string password_path_; + Envoy::Ssl::PrivateKeyMethodProviderSharedPtr private_key_method_{}; }; } // namespace Ssl diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index 1bd9fc6ef4e4..0764dab54545 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -171,14 +171,14 @@ void ContextConfigImpl::setSecretUpdateCallback(std::function callback) } // Once tls_certificate_config_ receives new secret, this callback updates // ContextConfigImpl::tls_certificate_config_ with new secret. - tc_update_callback_handle_ = tls_certificate_providers_[0]->addUpdateCallback([this, - callback]() { - // This breaks multiple certificate support, but today SDS is only single cert. - // TODO(htuch): Fix this when SDS goes multi-cert. - tls_certificate_configs_.clear(); - tls_certificate_configs_.emplace_back(*tls_certificate_providers_[0]->secret(), api_, false); - callback(); - }); + tc_update_callback_handle_ = + tls_certificate_providers_[0]->addUpdateCallback([this, callback]() { + // This breaks multiple certificate support, but today SDS is only single cert. + // TODO(htuch): Fix this when SDS goes multi-cert. + tls_certificate_configs_.clear(); + tls_certificate_configs_.emplace_back(*tls_certificate_providers_[0]->secret(), api_); + callback(); + }); } if (certificate_validation_context_provider_) { if (cvc_update_callback_handle_) { diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index 299111784e0b..e9655cac307e 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -1291,6 +1291,15 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadSuccess) { TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureBothKeyAndMethod) { envoy::api::v2::auth::DownstreamTlsContext tls_context; + NiceMock context_manager; + NiceMock private_key_method_manager; + auto private_key_method_provider_ptr = + std::make_shared>(); + EXPECT_CALL(factory_context_, sslContextManager()).WillOnce(ReturnRef(context_manager)); + EXPECT_CALL(context_manager, privateKeyMethodManager()) + .WillOnce(ReturnRef(private_key_method_manager)); + EXPECT_CALL(private_key_method_manager, createPrivateKeyMethodProvider(_, _)) + .WillOnce(Return(private_key_method_provider_ptr)); const std::string tls_context_yaml = R"EOF( common_tls_context: tls_certificates: From cc54803a1387eb132915e1584064e10d1bb065da Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 5 Jun 2019 14:39:48 +0300 Subject: [PATCH 34/56] tests: remove use of old TlsCertificateConfigImpl constructor. Signed-off-by: Ismo Puustinen --- test/common/secret/sds_api_test.cc | 2 +- test/common/secret/secret_manager_impl_test.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/common/secret/sds_api_test.cc b/test/common/secret/sds_api_test.cc index bfdedca2f3bb..163ad7b549f5 100644 --- a/test/common/secret/sds_api_test.cc +++ b/test/common/secret/sds_api_test.cc @@ -101,7 +101,7 @@ TEST_F(SdsApiTest, DynamicTlsCertificateUpdateSuccess) { EXPECT_CALL(secret_callback, onAddOrUpdateSecret()); sds_api.onConfigUpdate(secret_resources, ""); - Ssl::TlsCertificateConfigImpl tls_config(*sds_api.secret(), *api_, false); + Ssl::TlsCertificateConfigImpl tls_config(*sds_api.secret(), *api_); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), diff --git a/test/common/secret/secret_manager_impl_test.cc b/test/common/secret/secret_manager_impl_test.cc index c7610dd99667..33ce6095967b 100644 --- a/test/common/secret/secret_manager_impl_test.cc +++ b/test/common/secret/secret_manager_impl_test.cc @@ -49,7 +49,7 @@ name: "abc.com" ASSERT_NE(secret_manager->findStaticTlsCertificateProvider("abc.com"), nullptr); Ssl::TlsCertificateConfigImpl tls_config( - *secret_manager->findStaticTlsCertificateProvider("abc.com")->secret(), *api_, false); + *secret_manager->findStaticTlsCertificateProvider("abc.com")->secret(), *api_); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), @@ -184,7 +184,7 @@ name: "abc.com" Protobuf::RepeatedPtrField secret_resources; secret_resources.Add()->PackFrom(typed_secret); dynamic_cast(*secret_provider).onConfigUpdate(secret_resources, ""); - Ssl::TlsCertificateConfigImpl tls_config(*secret_provider->secret(), *api_, false); + Ssl::TlsCertificateConfigImpl tls_config(*secret_provider->secret(), *api_); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), From 12680f63d252c7ad5d86d66b941a794bbb0c42e8 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 5 Jun 2019 15:38:56 +0300 Subject: [PATCH 35/56] tls: remove PrivateKeyMethodConnection class. Signed-off-by: Ismo Puustinen --- include/envoy/ssl/private_key/private_key.h | 22 ++++++++--------- .../transport_sockets/tls/ssl_socket.cc | 24 +++++++------------ .../transport_sockets/tls/ssl_socket.h | 1 - test/extensions/transport_sockets/tls/BUILD | 2 ++ .../tls/ecdsa_private_key_method_provider.cc | 15 ++++++++---- .../tls/ecdsa_private_key_method_provider.h | 14 +++++++---- .../tls/rsa_private_key_method_provider.cc | 15 ++++++++---- .../tls/rsa_private_key_method_provider.h | 15 +++++++----- test/mocks/ssl/mocks.h | 6 ++--- 9 files changed, 63 insertions(+), 51 deletions(-) diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index 341babf6e0ca..5040f2dc068d 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -22,28 +22,26 @@ namespace Ssl { typedef std::shared_ptr BoringSslPrivateKeyMethodSharedPtr; -class PrivateKeyConnection { -public: - virtual ~PrivateKeyConnection() {} -}; - -typedef std::unique_ptr PrivateKeyConnectionPtr; - class PrivateKeyMethodProvider { public: virtual ~PrivateKeyMethodProvider() {} /** - * Get a private key operations instance from the provider. + * Register an SSL connection to private key operations by the provider. * @param ssl a SSL connection object. * @param cb a callbacks object, whose "complete" method will be invoked * when the asynchronous processing is complete. * @param dispatcher supplies the owning thread's dispatcher. - * @return the private key operations instance. */ - virtual PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, - PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher) PURE; + virtual void registerPrivateKeyMethod(SSL* ssl, PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher) PURE; + + /** + * Unregister an SSL connection from private key operations by the provider. + * @param ssl a SSL connection object. + * @throw EnvoyException if registration fails. + */ + virtual void unregisterPrivateKeyMethod(SSL* ssl) PURE; /** * Check whether the private key method satisfies FIPS requirements. diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index f6397a7ef01e..02050e15e0d7 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -62,21 +62,8 @@ void SslSocket::setTransportSocketCallbacks(Network::TransportSocketCallbacks& c // Associate this SSL connection with all the certificates (with their potentially different // private key methods). - std::vector providers = - ctx_->getPrivateKeyMethodProviders(); - - for (auto const& provider : providers) { - Ssl::PrivateKeyConnectionPtr pk_connection = - provider->getPrivateKeyConnection(ssl_.get(), *this, callbacks_->connection().dispatcher()); - // If a private key method provider has been assigned for this certificate, it's a fatal - // error if the connection can't be associated with the method. - ASSERT(pk_connection); - // We keep track of the private key operations for memory management purposes (the operations - // object if destroyed when the SslSocket is destroyed). The operations objects are unique - // because they need to associated with the SSL objects so that user data can be passed to the - // BoringSSL private key methods. - pk_connections_.emplace_back(std::move(pk_connection)); - + for (auto const& provider : ctx_->getPrivateKeyMethodProviders()) { + provider->registerPrivateKeyMethod(ssl_.get(), *this, callbacks_->connection().dispatcher()); if (run_tid_ == nullptr) { // Store the dispatcher thread ID. We will check that the caller is the same when the private // key method callback is received. @@ -417,10 +404,15 @@ std::vector SslSocket::dnsSansPeerCertificate() const { } void SslSocket::closeSocket(Network::ConnectionEvent) { + // Unregister the SSL connection object from private key method providers. + for (auto const& provider : ctx_->getPrivateKeyMethodProviders()) { + provider->unregisterPrivateKeyMethod(ssl_.get()); + } + // Attempt to send a shutdown before closing the socket. It's possible this won't go out if // there is no room on the socket. We can extend the state machine to handle this at some point // if needed. - if (handshake_complete_) { + if (handshake_complete_ || async_handshake_in_progress_) { shutdownSsl(); } } diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index b839de2090b5..fea08437a494 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -95,7 +95,6 @@ class SslSocket : public Network::TransportSocket, mutable std::string cached_sha_256_peer_certificate_digest_; mutable std::string cached_url_encoded_pem_encoded_peer_certificate_; mutable std::string cached_url_encoded_pem_encoded_peer_cert_chain_; - std::vector pk_connections_; bool async_handshake_in_progress_{}; Thread::ThreadIdPtr run_tid_; }; diff --git a/test/extensions/transport_sockets/tls/BUILD b/test/extensions/transport_sockets/tls/BUILD index 3c61133e4a99..c14d1a06eaca 100644 --- a/test/extensions/transport_sockets/tls/BUILD +++ b/test/extensions/transport_sockets/tls/BUILD @@ -127,6 +127,7 @@ envoy_cc_test_library( "//include/envoy/server:transport_socket_config_interface", "//include/envoy/ssl/private_key:private_key_config_interface", "//include/envoy/ssl/private_key:private_key_interface", + "//source/common/common:thread_lib", "//source/common/config:utility_lib", "//source/common/protobuf:utility_lib", ], @@ -147,6 +148,7 @@ envoy_cc_test_library( "//include/envoy/server:transport_socket_config_interface", "//include/envoy/ssl/private_key:private_key_config_interface", "//include/envoy/ssl/private_key:private_key_interface", + "//source/common/common:thread_lib", "//source/common/config:utility_lib", "//source/common/protobuf:utility_lib", ], diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc index d60ae5290ac1..3b2a8cfb26a1 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc @@ -127,10 +127,17 @@ EcdsaPrivateKeyConnection::EcdsaPrivateKeyConnection( SSL_set_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index, this); } -Ssl::PrivateKeyConnectionPtr EcdsaPrivateKeyMethodProvider::getPrivateKeyConnection( - SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) { - return std::make_unique(ssl, cb, dispatcher, bssl::UpRef(pkey_), - test_options_); +void EcdsaPrivateKeyMethodProvider::registerPrivateKeyMethod(SSL* ssl, + Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher) { + Thread::LockGuard map_lock(map_lock_); + connections_.emplace( + ssl, new EcdsaPrivateKeyConnection(ssl, cb, dispatcher, bssl::UpRef(pkey_), test_options_)); +} + +void EcdsaPrivateKeyMethodProvider::unregisterPrivateKeyMethod(SSL* ssl) { + Thread::LockGuard map_lock(map_lock_); + connections_.erase(ssl); } EcdsaPrivateKeyMethodProvider::EcdsaPrivateKeyMethodProvider( diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h index 90e1b6e5867b..114b258f3aac 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h @@ -5,6 +5,8 @@ #include "envoy/ssl/private_key/private_key.h" #include "envoy/ssl/private_key/private_key_config.h" +#include "common/common/lock_guard.h" +#include "common/common/thread.h" #include "common/config/utility.h" #include "common/protobuf/utility.h" @@ -17,9 +19,9 @@ struct EcdsaPrivateKeyConnectionTestOptions { bool async_method_error_{}; }; -// An example ECDSA private key method provider here for testing the decrypt() and sign() +// An example ECDSA private key method provider for testing the decrypt() and sign() // functionality. -class EcdsaPrivateKeyConnection : public virtual Ssl::PrivateKeyConnection { +class EcdsaPrivateKeyConnection { public: EcdsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, @@ -46,18 +48,20 @@ class EcdsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvid const ProtobufWkt::Struct& config, Server::Configuration::TransportSocketFactoryContext& factory_context); // Ssl::PrivateKeyMethodProvider - Ssl::PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, - Ssl::PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher) override; + void registerPrivateKeyMethod(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher) override; + void unregisterPrivateKeyMethod(SSL* ssl) override; bool checkFips() override; Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; static int ssl_ecdsa_connection_index; private: + Thread::MutexBasicLockable map_lock_{}; Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; bssl::UniquePtr pkey_; EcdsaPrivateKeyConnectionTestOptions test_options_; + std::map> connections_; }; class EcdsaPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index 49d668d7a163..5afdb4e878d8 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -204,10 +204,17 @@ RsaPrivateKeyConnection::RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnec SSL_set_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index, this); } -Ssl::PrivateKeyConnectionPtr RsaPrivateKeyMethodProvider::getPrivateKeyConnection( - SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) { - return std::make_unique(ssl, cb, dispatcher, bssl::UpRef(pkey_), - test_options_); +void RsaPrivateKeyMethodProvider::registerPrivateKeyMethod(SSL* ssl, + Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher) { + Thread::LockGuard map_lock(map_lock_); + connections_.emplace( + ssl, new RsaPrivateKeyConnection(ssl, cb, dispatcher, bssl::UpRef(pkey_), test_options_)); +} + +void RsaPrivateKeyMethodProvider::unregisterPrivateKeyMethod(SSL* ssl) { + Thread::LockGuard map_lock(map_lock_); + connections_.erase(ssl); } RsaPrivateKeyMethodProvider::RsaPrivateKeyMethodProvider( diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h index 8e48bc13da37..c9200e765b2f 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h @@ -5,6 +5,8 @@ #include "envoy/ssl/private_key/private_key.h" #include "envoy/ssl/private_key/private_key_config.h" +#include "common/common/lock_guard.h" +#include "common/common/thread.h" #include "common/config/utility.h" #include "common/protobuf/utility.h" @@ -32,9 +34,9 @@ struct RsaPrivateKeyConnectionTestOptions { bool async_method_error_{}; }; -// An example RSA private key method provider here for testing the decrypt() and sign() +// An example RSA private key method provider for testing the decrypt() and sign() // functionality. -class RsaPrivateKeyConnection : public virtual Ssl::PrivateKeyConnection { +class RsaPrivateKeyConnection { public: RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, @@ -44,7 +46,6 @@ class RsaPrivateKeyConnection : public virtual Ssl::PrivateKeyConnection { // Store the output data temporarily. std::vector output_; - // Is the operation finished? bool finished_{}; RsaPrivateKeyConnectionTestOptions& test_options_; @@ -62,18 +63,20 @@ class RsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider const ProtobufWkt::Struct& config, Server::Configuration::TransportSocketFactoryContext& factory_context); // Ssl::PrivateKeyMethodProvider - Ssl::PrivateKeyConnectionPtr getPrivateKeyConnection(SSL* ssl, - Ssl::PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher) override; + void registerPrivateKeyMethod(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher) override; + void unregisterPrivateKeyMethod(SSL* ssl) override; bool checkFips() override; Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; static int ssl_rsa_connection_index; private: + Thread::MutexBasicLockable map_lock_{}; Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; bssl::UniquePtr pkey_; RsaPrivateKeyConnectionTestOptions test_options_; + std::map> connections_; }; class RsaPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 6b553c16b914..089317ea6ae7 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -78,9 +78,9 @@ class MockPrivateKeyMethodProvider : public PrivateKeyMethodProvider { MockPrivateKeyMethodProvider(); ~MockPrivateKeyMethodProvider(); - MOCK_METHOD3(getPrivateKeyConnection, - PrivateKeyConnectionPtr(SSL* ssl, PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher)); + MOCK_METHOD3(registerPrivateKeyMethod, + void(SSL* ssl, PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher)); + MOCK_METHOD1(unregisterPrivateKeyMethod, void(SSL* ssl)); MOCK_METHOD0(checkFips, bool()); MOCK_METHOD0(getBoringSslPrivateKeyMethod, BoringSslPrivateKeyMethodSharedPtr()); }; From 16ca2a14892e86c72217c77444f991cbcfba2d21 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 5 Jun 2019 17:33:44 +0300 Subject: [PATCH 36/56] tests: use higher-level RSA signing functions. Signed-off-by: Ismo Puustinen --- .../tls/rsa_private_key_method_provider.cc | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index 5afdb4e878d8..ae337c0acc60 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -33,10 +33,6 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); unsigned char hash[EVP_MAX_MD_SIZE]; unsigned int hash_len; - uint8_t* msg; - size_t msg_len; - int prefix_allocated = 0; - int padding = RSA_NO_PADDING; if (!ops) { return ssl_private_key_failure; @@ -68,35 +64,15 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o return ssl_private_key_failure; } - // Add RSA padding to the the hash. + // Perform signing. if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) { - msg_len = RSA_size(rsa); - msg = static_cast(OPENSSL_malloc(msg_len)); - if (!msg) { - return ssl_private_key_failure; - } - prefix_allocated = 1; - if (!RSA_padding_add_PKCS1_PSS_mgf1(rsa, msg, hash, md, nullptr, -1)) { - OPENSSL_free(msg); - return ssl_private_key_failure; - } - padding = RSA_NO_PADDING; + RSA_sign_pss_mgf1(rsa, out_len, out, max_out, hash, hash_len, md, nullptr, -1); } else { - if (!RSA_add_pkcs1_prefix(&msg, &msg_len, &prefix_allocated, EVP_MD_type(md), hash, hash_len)) { + unsigned int out_len_unsigned; + if (!RSA_sign(EVP_MD_type(md), hash, hash_len, out, &out_len_unsigned, rsa)) { return ssl_private_key_failure; } - padding = RSA_PKCS1_PADDING; - } - - if (!RSA_sign_raw(rsa, out_len, out, max_out, msg, msg_len, padding)) { - if (prefix_allocated) { - OPENSSL_free(msg); - } - return ssl_private_key_failure; - } - - if (prefix_allocated) { - OPENSSL_free(msg); + *out_len = out_len_unsigned; } if (ops->test_options_.crypto_error_) { From 6d5cf6f9e1b3d683add03f48247a5c8d1a5c81a5 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 5 Jun 2019 18:07:43 +0300 Subject: [PATCH 37/56] tests: do not use locking in test providers. SSL_set_ex_data() doesn't seem to support smart pointers though. Signed-off-by: Ismo Puustinen --- test/extensions/transport_sockets/tls/BUILD | 2 -- .../tls/ecdsa_private_key_method_provider.cc | 18 +++++++++--------- .../tls/ecdsa_private_key_method_provider.h | 8 ++------ .../tls/rsa_private_key_method_provider.cc | 18 +++++++++--------- .../tls/rsa_private_key_method_provider.h | 8 ++------ 5 files changed, 22 insertions(+), 32 deletions(-) diff --git a/test/extensions/transport_sockets/tls/BUILD b/test/extensions/transport_sockets/tls/BUILD index c14d1a06eaca..3c61133e4a99 100644 --- a/test/extensions/transport_sockets/tls/BUILD +++ b/test/extensions/transport_sockets/tls/BUILD @@ -127,7 +127,6 @@ envoy_cc_test_library( "//include/envoy/server:transport_socket_config_interface", "//include/envoy/ssl/private_key:private_key_config_interface", "//include/envoy/ssl/private_key:private_key_interface", - "//source/common/common:thread_lib", "//source/common/config:utility_lib", "//source/common/protobuf:utility_lib", ], @@ -148,7 +147,6 @@ envoy_cc_test_library( "//include/envoy/server:transport_socket_config_interface", "//include/envoy/ssl/private_key:private_key_config_interface", "//include/envoy/ssl/private_key:private_key_interface", - "//source/common/common:thread_lib", "//source/common/config:utility_lib", "//source/common/protobuf:utility_lib", ], diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc index 3b2a8cfb26a1..d6ff783c2cf2 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc @@ -121,23 +121,23 @@ bool EcdsaPrivateKeyMethodProvider::checkFips() { } EcdsaPrivateKeyConnection::EcdsaPrivateKeyConnection( - SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, + Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, EcdsaPrivateKeyConnectionTestOptions& test_options) - : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(move(pkey)) { - SSL_set_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index, this); -} + : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(move(pkey)) {} void EcdsaPrivateKeyMethodProvider::registerPrivateKeyMethod(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) { - Thread::LockGuard map_lock(map_lock_); - connections_.emplace( - ssl, new EcdsaPrivateKeyConnection(ssl, cb, dispatcher, bssl::UpRef(pkey_), test_options_)); + EcdsaPrivateKeyConnection* ops = + new EcdsaPrivateKeyConnection(cb, dispatcher, bssl::UpRef(pkey_), test_options_); + SSL_set_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index, ops); } void EcdsaPrivateKeyMethodProvider::unregisterPrivateKeyMethod(SSL* ssl) { - Thread::LockGuard map_lock(map_lock_); - connections_.erase(ssl); + EcdsaPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index)); + SSL_set_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index, nullptr); + delete ops; } EcdsaPrivateKeyMethodProvider::EcdsaPrivateKeyMethodProvider( diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h index 114b258f3aac..107e43da3b9b 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h @@ -5,8 +5,6 @@ #include "envoy/ssl/private_key/private_key.h" #include "envoy/ssl/private_key/private_key_config.h" -#include "common/common/lock_guard.h" -#include "common/common/thread.h" #include "common/config/utility.h" #include "common/protobuf/utility.h" @@ -23,8 +21,8 @@ struct EcdsaPrivateKeyConnectionTestOptions { // functionality. class EcdsaPrivateKeyConnection { public: - EcdsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, + EcdsaPrivateKeyConnection(Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, + bssl::UniquePtr pkey, EcdsaPrivateKeyConnectionTestOptions& test_options); EC_KEY* getPrivateKey() { return EVP_PKEY_get1_EC_KEY(pkey_.get()); } void delayed_op(); @@ -57,11 +55,9 @@ class EcdsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvid static int ssl_ecdsa_connection_index; private: - Thread::MutexBasicLockable map_lock_{}; Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; bssl::UniquePtr pkey_; EcdsaPrivateKeyConnectionTestOptions test_options_; - std::map> connections_; }; class EcdsaPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index ae337c0acc60..f041b9f8b54d 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -172,25 +172,25 @@ bool RsaPrivateKeyMethodProvider::checkFips() { return true; } -RsaPrivateKeyConnection::RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, +RsaPrivateKeyConnection::RsaPrivateKeyConnection(Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, RsaPrivateKeyConnectionTestOptions& test_options) - : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(std::move(pkey)) { - SSL_set_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index, this); -} + : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(std::move(pkey)) {} void RsaPrivateKeyMethodProvider::registerPrivateKeyMethod(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher) { - Thread::LockGuard map_lock(map_lock_); - connections_.emplace( - ssl, new RsaPrivateKeyConnection(ssl, cb, dispatcher, bssl::UpRef(pkey_), test_options_)); + RsaPrivateKeyConnection* ops = + new RsaPrivateKeyConnection(cb, dispatcher, bssl::UpRef(pkey_), test_options_); + SSL_set_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index, ops); } void RsaPrivateKeyMethodProvider::unregisterPrivateKeyMethod(SSL* ssl) { - Thread::LockGuard map_lock(map_lock_); - connections_.erase(ssl); + RsaPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); + SSL_set_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index, nullptr); + delete ops; } RsaPrivateKeyMethodProvider::RsaPrivateKeyMethodProvider( diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h index c9200e765b2f..5e63233cc73d 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h @@ -5,8 +5,6 @@ #include "envoy/ssl/private_key/private_key.h" #include "envoy/ssl/private_key/private_key_config.h" -#include "common/common/lock_guard.h" -#include "common/common/thread.h" #include "common/config/utility.h" #include "common/protobuf/utility.h" @@ -38,8 +36,8 @@ struct RsaPrivateKeyConnectionTestOptions { // functionality. class RsaPrivateKeyConnection { public: - RsaPrivateKeyConnection(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher, bssl::UniquePtr pkey, + RsaPrivateKeyConnection(Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, + bssl::UniquePtr pkey, RsaPrivateKeyConnectionTestOptions& test_options); RSA* getPrivateKey() { return EVP_PKEY_get0_RSA(pkey_.get()); } void delayed_op(); @@ -72,11 +70,9 @@ class RsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider static int ssl_rsa_connection_index; private: - Thread::MutexBasicLockable map_lock_{}; Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; bssl::UniquePtr pkey_; RsaPrivateKeyConnectionTestOptions test_options_; - std::map> connections_; }; class RsaPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { From e3a66fe725f5e587d2926afc193f245b9dbdac16 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Thu, 6 Jun 2019 13:20:12 +0300 Subject: [PATCH 38/56] docs: add an overview and some API documentation. Signed-off-by: Ismo Puustinen --- api/envoy/api/v2/auth/cert.proto | 17 ++++++++++++----- docs/root/extending/extending.rst | 1 + docs/root/intro/arch_overview/ssl.rst | 4 ++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/api/envoy/api/v2/auth/cert.proto b/api/envoy/api/v2/auth/cert.proto index 0e17f3c6586e..3d5818a75327 100644 --- a/api/envoy/api/v2/auth/cert.proto +++ b/api/envoy/api/v2/auth/cert.proto @@ -104,12 +104,15 @@ message TlsParameters { repeated string ecdh_curves = 4; } +// BoringSSL private key method configuration. The private key methods are used for external +// (potentially asynchronous) signing and decryption operations. Some use cases for private key +// methods would be TPM support and TLS acceleration. message PrivateKeyMethod { - // Private key operations provider name. The name must match a - // supported private key operations provider type. + // Private key method provider name. The name must match a + // supported private key method provider type. string provider_name = 1 [(validate.rules).string.min_bytes = 1]; - // Private key operations provider specific configuration. + // Private key method provider specific configuration. oneof config_type { google.protobuf.Struct config = 2; @@ -124,8 +127,12 @@ message TlsCertificate { // The TLS private key. core.DataSource private_key = 2; - // BoringSSL private key operations. This is an alternative to private_key field. This can't be - // marked as "oneof" due to API compatibility reasons. + // BoringSSL private key method. This is an alternative to :ref:`private_key + // ` field. This can't be + // marked as ``oneof`` due to API compatibility reasons. Setting both :ref:`private_key + // ` and :ref:`private_key_method + // ` fields will result in an + // error. PrivateKeyMethod private_key_method = 6; // The password to decrypt the TLS private key. If this field is not set, it is assumed that the diff --git a/docs/root/extending/extending.rst b/docs/root/extending/extending.rst index a44e36785149..01381c7e0902 100644 --- a/docs/root/extending/extending.rst +++ b/docs/root/extending/extending.rst @@ -18,6 +18,7 @@ types including: * :ref:`Stat sinks ` * :ref:`Tracers ` * Transport sockets +* BoringSSL private key methods As of this writing there is no high level extension developer documentation. The :repo:`existing extensions ` are a good way to learn what is possible. diff --git a/docs/root/intro/arch_overview/ssl.rst b/docs/root/intro/arch_overview/ssl.rst index e73d14dd3ef3..a44a8f531872 100644 --- a/docs/root/intro/arch_overview/ssl.rst +++ b/docs/root/intro/arch_overview/ssl.rst @@ -23,6 +23,10 @@ requirements (TLS1.2, SNI, etc.). Envoy supports the following TLS features: tickets (see `RFC 5077 `_). Resumption can be performed across hot restarts and between parallel Envoy instances (typically useful in a front proxy configuration). +* **BoringSSL private key methods**: TLS private key operations (signing and decrypting) can be + performed asynchronously from an extension. This allows extending Envoy to support various key + management schemes (such as TPM) and TLS acceleration. This mechanism uses + `BoringSSL private key method interface `_. Underlying implementation ------------------------- From 6ac9b30bb2c8bd86c3e6dc817834d60032b8555e Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Thu, 6 Jun 2019 13:42:27 +0300 Subject: [PATCH 39/56] dictionary: add "TPM". Signed-off-by: Ismo Puustinen --- tools/spelling_dictionary.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/spelling_dictionary.txt b/tools/spelling_dictionary.txt index a81e1669b4ad..1fe25828eb74 100644 --- a/tools/spelling_dictionary.txt +++ b/tools/spelling_dictionary.txt @@ -253,6 +253,7 @@ TLSv TLV TMPDIR TODO +TPM TPROXY TSAN TSI From 2ef535fb6335d487782fa67b8532459b4ea28238 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 10 Jun 2019 14:34:19 +0300 Subject: [PATCH 40/56] tls: style cleanups. Signed-off-by: Ismo Puustinen --- include/envoy/ssl/private_key/private_key.h | 4 ++-- .../ssl/private_key/private_key_callbacks.h | 2 +- .../ssl/private_key/private_key_config.h | 2 +- .../tls/ecdsa_private_key_method_provider.cc | 24 +++++-------------- .../tls/rsa_private_key_method_provider.cc | 9 +++---- 5 files changed, 13 insertions(+), 28 deletions(-) diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index 5040f2dc068d..f96af97cdbbc 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -24,7 +24,7 @@ typedef std::shared_ptr BoringSslPrivateKeyMethodSharedP class PrivateKeyMethodProvider { public: - virtual ~PrivateKeyMethodProvider() {} + virtual ~PrivateKeyMethodProvider() = default; /** * Register an SSL connection to private key operations by the provider. @@ -65,7 +65,7 @@ typedef std::shared_ptr PrivateKeyMethodProviderShared */ class PrivateKeyMethodManager { public: - virtual ~PrivateKeyMethodManager() {} + virtual ~PrivateKeyMethodManager() = default; /** * Finds and returns a private key operations provider for BoringSSL. diff --git a/include/envoy/ssl/private_key/private_key_callbacks.h b/include/envoy/ssl/private_key/private_key_callbacks.h index 2390fc7ff29e..1f370fda947b 100644 --- a/include/envoy/ssl/private_key/private_key_callbacks.h +++ b/include/envoy/ssl/private_key/private_key_callbacks.h @@ -10,7 +10,7 @@ namespace Ssl { class PrivateKeyConnectionCallbacks { public: - virtual ~PrivateKeyConnectionCallbacks() {} + virtual ~PrivateKeyConnectionCallbacks() = default; /** * Callback function which is called when the asynchronous private key diff --git a/include/envoy/ssl/private_key/private_key_config.h b/include/envoy/ssl/private_key/private_key_config.h index 5ee12dad6913..dbe3a2159d8d 100644 --- a/include/envoy/ssl/private_key/private_key_config.h +++ b/include/envoy/ssl/private_key/private_key_config.h @@ -11,7 +11,7 @@ namespace Ssl { class PrivateKeyMethodProviderInstanceFactory { public: - virtual ~PrivateKeyMethodProviderInstanceFactory() {} + virtual ~PrivateKeyMethodProviderInstanceFactory() = default; virtual PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyMethod& message, Server::Configuration::TransportSocketFactoryContext& diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc index d6ff783c2cf2..5aad26e2170a 100644 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc @@ -23,15 +23,11 @@ void EcdsaPrivateKeyConnection::delayed_op() { timer_->enableTimer(timeout_0ms); } -static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* out_len, - size_t max_out, uint16_t signature_algorithm, - const uint8_t* in, size_t in_len) { - (void)out_len; - (void)max_out; - (void)signature_algorithm; +static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t*, size_t, + uint16_t signature_algorithm, const uint8_t* in, + size_t in_len) { unsigned char hash[EVP_MAX_MD_SIZE]; unsigned int hash_len; - const EVP_MD* md; bssl::ScopedEVP_MD_CTX ctx; EcdsaPrivateKeyConnection* ops = static_cast( SSL_get_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index)); @@ -46,7 +42,7 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o return ssl_private_key_failure; } - md = SSL_get_signature_algorithm_digest(signature_algorithm); + const EVP_MD* md = SSL_get_signature_algorithm_digest(signature_algorithm); if (!md) { return ssl_private_key_failure; } @@ -70,16 +66,8 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o return ssl_private_key_retry; } -static ssl_private_key_result_t privateKeyDecrypt(SSL* ssl, uint8_t* out, size_t* out_len, - size_t max_out, const uint8_t* in, - size_t in_len) { - (void)ssl; - (void)out; - (void)out_len; - (void)max_out; - (void)in; - (void)in_len; - +static ssl_private_key_result_t privateKeyDecrypt(SSL*, uint8_t*, size_t*, size_t, const uint8_t*, + size_t) { return ssl_private_key_failure; } diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc index f041b9f8b54d..b9f070a8de9c 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc @@ -26,9 +26,7 @@ void RsaPrivateKeyConnection::delayed_op() { static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* out_len, size_t max_out, uint16_t signature_algorithm, const uint8_t* in, size_t in_len) { - RSA* rsa; bssl::ScopedEVP_MD_CTX ctx; - const EVP_MD* md; RsaPrivateKeyConnection* ops = static_cast( SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); unsigned char hash[EVP_MAX_MD_SIZE]; @@ -48,12 +46,12 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o return ssl_private_key_failure; } - rsa = ops->getPrivateKey(); + RSA* rsa = ops->getPrivateKey(); if (rsa == nullptr) { return ssl_private_key_failure; } - md = SSL_get_signature_algorithm_digest(signature_algorithm); + const EVP_MD* md = SSL_get_signature_algorithm_digest(signature_algorithm); if (!md) { return ssl_private_key_failure; } @@ -96,7 +94,6 @@ static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* o static ssl_private_key_result_t privateKeyDecrypt(SSL* ssl, uint8_t* out, size_t* out_len, size_t max_out, const uint8_t* in, size_t in_len) { - RSA* rsa; RsaPrivateKeyConnection* ops = static_cast( SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); @@ -114,7 +111,7 @@ static ssl_private_key_result_t privateKeyDecrypt(SSL* ssl, uint8_t* out, size_t return ssl_private_key_failure; } - rsa = ops->getPrivateKey(); + RSA* rsa = ops->getPrivateKey(); if (rsa == nullptr) { return ssl_private_key_failure; } From 2498b0f7c965bcb421191d12100c08bb4999d5e4 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 18 Jun 2019 22:59:51 +0300 Subject: [PATCH 41/56] tls: reduce TlsCertificateConfigImpl constructors to one. Signed-off-by: Ismo Puustinen --- .../common/ssl/tls_certificate_config_impl.cc | 27 +++---------------- .../common/ssl/tls_certificate_config_impl.h | 5 +--- .../tls/context_config_impl.cc | 5 ++-- test/common/secret/sds_api_test.cc | 4 +-- .../common/secret/secret_manager_impl_test.cc | 4 +-- 5 files changed, 12 insertions(+), 33 deletions(-) diff --git a/source/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index ac666027ca20..77be58fd74cf 100644 --- a/source/common/ssl/tls_certificate_config_impl.cc +++ b/source/common/ssl/tls_certificate_config_impl.cc @@ -12,28 +12,9 @@ namespace Ssl { static const std::string INLINE_STRING = ""; -TlsCertificateConfigImpl::TlsCertificateConfigImpl( - const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api) - : certificate_chain_(Config::DataSource::read(config.certificate_chain(), true, api)), - certificate_chain_path_( - Config::DataSource::getPath(config.certificate_chain()) - .value_or(certificate_chain_.empty() ? EMPTY_STRING : INLINE_STRING)), - private_key_(Config::DataSource::read(config.private_key(), true, api)), - private_key_path_(Config::DataSource::getPath(config.private_key()) - .value_or(private_key_.empty() ? EMPTY_STRING : INLINE_STRING)), - password_(Config::DataSource::read(config.password(), true, api)), - password_path_(Config::DataSource::getPath(config.password()) - .value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)) { - - if (certificate_chain_.empty() || private_key_.empty()) { - throw EnvoyException(fmt::format("Failed to load incomplete certificate from {}, {}", - certificate_chain_path_, private_key_path_)); - } -} - TlsCertificateConfigImpl::TlsCertificateConfigImpl( const envoy::api::v2::auth::TlsCertificate& config, - Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) + Server::Configuration::TransportSocketFactoryContext* factory_context, Api::Api& api) : certificate_chain_(Config::DataSource::read(config.certificate_chain(), true, api)), certificate_chain_path_( Config::DataSource::getPath(config.certificate_chain()) @@ -45,10 +26,10 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl( password_path_(Config::DataSource::getPath(config.password()) .value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)), private_key_method_( - config.has_private_key_method() - ? factory_context.sslContextManager() + factory_context != nullptr && config.has_private_key_method() + ? factory_context->sslContextManager() .privateKeyMethodManager() - .createPrivateKeyMethodProvider(config.private_key_method(), factory_context) + .createPrivateKeyMethodProvider(config.private_key_method(), *factory_context) : nullptr) { if (config.has_private_key_method() && config.has_private_key()) { throw EnvoyException(fmt::format( diff --git a/source/common/ssl/tls_certificate_config_impl.h b/source/common/ssl/tls_certificate_config_impl.h index 05c4b282044f..1db9046e925a 100644 --- a/source/common/ssl/tls_certificate_config_impl.h +++ b/source/common/ssl/tls_certificate_config_impl.h @@ -12,11 +12,8 @@ namespace Ssl { class TlsCertificateConfigImpl : public TlsCertificateConfig { public: - // SDS uses this constructor. - TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api); - // Regular constructor, works also for private key methods. TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, - Server::Configuration::TransportSocketFactoryContext& factory_context, + Server::Configuration::TransportSocketFactoryContext* factory_context, Api::Api& api); const std::string& certificateChain() const override { return certificate_chain_; } diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index 0764dab54545..e398e53add69 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -145,7 +145,7 @@ ContextConfigImpl::ContextConfigImpl( if (!tls_certificate_providers_.empty()) { for (auto& provider : tls_certificate_providers_) { if (provider->secret() != nullptr) { - tls_certificate_configs_.emplace_back(*provider->secret(), factory_context, api_); + tls_certificate_configs_.emplace_back(*provider->secret(), &factory_context, api_); } } } @@ -176,7 +176,8 @@ void ContextConfigImpl::setSecretUpdateCallback(std::function callback) // This breaks multiple certificate support, but today SDS is only single cert. // TODO(htuch): Fix this when SDS goes multi-cert. tls_certificate_configs_.clear(); - tls_certificate_configs_.emplace_back(*tls_certificate_providers_[0]->secret(), api_); + tls_certificate_configs_.emplace_back(*tls_certificate_providers_[0]->secret(), nullptr, + api_); callback(); }); } diff --git a/test/common/secret/sds_api_test.cc b/test/common/secret/sds_api_test.cc index 7a2d0abe50d9..92780acc5c74 100644 --- a/test/common/secret/sds_api_test.cc +++ b/test/common/secret/sds_api_test.cc @@ -86,7 +86,7 @@ TEST_F(SdsApiTest, DynamicTlsCertificateUpdateSuccess) { EXPECT_CALL(secret_callback, onAddOrUpdateSecret()); subscription_factory_.callbacks_->onConfigUpdate(secret_resources, ""); - Ssl::TlsCertificateConfigImpl tls_config(*sds_api.secret(), *api_); + Ssl::TlsCertificateConfigImpl tls_config(*sds_api.secret(), nullptr, *api_); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), @@ -180,7 +180,7 @@ TEST_F(SdsApiTest, DeltaUpdateSuccess) { initialize(); subscription_factory_.callbacks_->onConfigUpdate(secret_resources, {}, ""); - Ssl::TlsCertificateConfigImpl tls_config(*sds_api.secret(), *api_); + Ssl::TlsCertificateConfigImpl tls_config(*sds_api.secret(), nullptr, *api_); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), diff --git a/test/common/secret/secret_manager_impl_test.cc b/test/common/secret/secret_manager_impl_test.cc index 95daa82d0d78..03cad0aba13f 100644 --- a/test/common/secret/secret_manager_impl_test.cc +++ b/test/common/secret/secret_manager_impl_test.cc @@ -49,7 +49,7 @@ name: "abc.com" ASSERT_NE(secret_manager->findStaticTlsCertificateProvider("abc.com"), nullptr); Ssl::TlsCertificateConfigImpl tls_config( - *secret_manager->findStaticTlsCertificateProvider("abc.com")->secret(), *api_); + *secret_manager->findStaticTlsCertificateProvider("abc.com")->secret(), nullptr, *api_); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), @@ -188,7 +188,7 @@ name: "abc.com" init_target_handle->initialize(init_watcher); secret_context.cluster_manager_.subscription_factory_.callbacks_->onConfigUpdate(secret_resources, ""); - Ssl::TlsCertificateConfigImpl tls_config(*secret_provider->secret(), *api_); + Ssl::TlsCertificateConfigImpl tls_config(*secret_provider->secret(), nullptr, *api_); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), From cd28e08f8b38567acc0f8a064072474997c87d9d Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 18 Jun 2019 15:59:36 +0300 Subject: [PATCH 42/56] tls: simplify private key method callback. Add some assumptions to the private key method providers: 1. After calling the completition callback, the providers are not allowed to return ssl_private_key_retry from the BoringSSL complete function. 2. The providers are not allowed to return any other value than ssl_private_key_retry from the BoringSSL complete function before calling the completition callback. Signed-off-by: Ismo Puustinen --- .../tls/context_manager_impl.h | 4 +-- .../transport_sockets/tls/ssl_socket.cc | 30 +++++-------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/source/extensions/transport_sockets/tls/context_manager_impl.h b/source/extensions/transport_sockets/tls/context_manager_impl.h index 807236ec2c9e..c297cce11ef1 100644 --- a/source/extensions/transport_sockets/tls/context_manager_impl.h +++ b/source/extensions/transport_sockets/tls/context_manager_impl.h @@ -37,14 +37,14 @@ class ContextManagerImpl final : public Envoy::Ssl::ContextManager { size_t daysUntilFirstCertExpires() const override; void iterateContexts(std::function callback) override; Ssl::PrivateKeyMethodManager& privateKeyMethodManager() override { - return private_key_operations_manager_; + return private_key_method_manager_; }; private: void removeEmptyContexts(); TimeSource& time_source_; std::list> contexts_; - PrivateKeyMethodManagerImpl private_key_operations_manager_{}; + PrivateKeyMethodManagerImpl private_key_method_manager_{}; }; } // namespace Tls diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index 54887892d3e0..acefc6fb41e0 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -163,32 +163,16 @@ Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { void SslSocket::onPrivateKeyMethodComplete() { ASSERT(isThreadSafe()); + ASSERT(!handshake_complete_); ASSERT(async_handshake_in_progress_); + async_handshake_in_progress_ = false; - ENVOY_CONN_LOG(debug, "async handshake complete", callbacks_->connection()); - if (!handshake_complete_) { - // It's possible that the async call comes in later, but the handshake has been retried from - // doWrite or similar. In that case the error handling has been done synchronously. */ - PostIoAction result = doHandshake(); - if (async_handshake_in_progress_ || result != PostIoAction::KeepOpen) { - // It may be semantically possible that the handshake is expecting read() or write(), so - // we shouldn't require that the handshake is complete. However, there can't be a second - // private key operation requested. - if (callbacks_->connection().state() == Network::Connection::State::Open) { - // The connection state machine thinks we are still connecting, but the second part of the - // private key method handshake failed. If the connection state would be something else, - // the socket was already closed while waiting for async handshake. - ENVOY_CONN_LOG(debug, "async handshake completion error", callbacks_->connection()); - drainErrorQueue(); - // There's nobody to handle the SSL error for us, because this event is coming in - // asynchronously -- just close the connection. This will lead to - // Network::ConnectionEvent::LocalClose event. - // TODO(ipuustin): should there be a special event type for asynchronous close? - // Such as Network::ConnectionEvent::ConnectionFailed? - callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); - } - } + // Resume handshake. + PostIoAction action = doHandshake(); + if (action == PostIoAction::Close) { + ENVOY_CONN_LOG(debug, "async handshake completion error", callbacks_->connection()); + callbacks_->connection().close(Network::ConnectionCloseType::FlushWrite); } } From d7e9e5a3497d4ed3bcd8ce01577525d8f18cd4aa Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Wed, 19 Jun 2019 16:24:20 +0300 Subject: [PATCH 43/56] Add transport_sockets/tls/private_key to unowned directories. Signed-off-by: Ismo Puustinen --- tools/check_format.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/check_format.py b/tools/check_format.py index 94446a829471..a2fe4e57b2c8 100755 --- a/tools/check_format.py +++ b/tools/check_format.py @@ -125,6 +125,7 @@ "extensions/transport_sockets/raw_buffer", "extensions/transport_sockets/tap", "extensions/transport_sockets/tls", + "extensions/transport_sockets/tls/private_key", "extensions/tracers/zipkin", "extensions/tracers/dynamic_ot", "extensions/tracers/opencensus", From 9b31b1aed2ab3d3b8cd78e84bfe4975f2dd4f617 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Thu, 20 Jun 2019 16:02:54 +0300 Subject: [PATCH 44/56] CODEOWNERS: add @PiotrSikora and @lizan to transport_sockets/tls. Signed-off-by: Ismo Puustinen --- CODEOWNERS | 2 ++ tools/check_format.py | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 767bd14d6bb0..ed80d05c63cc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -24,6 +24,8 @@ extensions/filters/common/original_src @snowp @klarose /*/extensions/filters/http/header_to_metadata @rgs1 @zuercher # alts transport socket extension /*/extensions/transport_sockets/alts @htuch @yangminzhu +# tls transport socket extension +/*/extensions/transport_sockets/tls @PiotrSikora @lizan # sni_cluster extension /*/extensions/filters/network/sni_cluster @rshriram @lizan # tracers.datadog extension diff --git a/tools/check_format.py b/tools/check_format.py index a2fe4e57b2c8..23422797b67c 100755 --- a/tools/check_format.py +++ b/tools/check_format.py @@ -124,8 +124,6 @@ "extensions/common/tap", "extensions/transport_sockets/raw_buffer", "extensions/transport_sockets/tap", - "extensions/transport_sockets/tls", - "extensions/transport_sockets/tls/private_key", "extensions/tracers/zipkin", "extensions/tracers/dynamic_ot", "extensions/tracers/opencensus", From c9ec2bc7f07aa5f0669b47af1f8ab0aa7ab22e80 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 24 Jun 2019 15:49:49 +0300 Subject: [PATCH 45/56] tests: merged RSA and ECDSA providers. Signed-off-by: Ismo Puustinen --- test/extensions/transport_sockets/tls/BUILD | 29 +- .../tls/ecdsa_private_key_method_provider.cc | 176 -------- .../tls/ecdsa_private_key_method_provider.h | 79 ---- .../tls/rsa_private_key_method_provider.cc | 255 ------------ .../transport_sockets/tls/ssl_socket_test.cc | 90 +++-- .../tls/test_private_key_method_provider.cc | 378 ++++++++++++++++++ ...r.h => test_private_key_method_provider.h} | 40 +- 7 files changed, 458 insertions(+), 589 deletions(-) delete mode 100644 test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc delete mode 100644 test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h delete mode 100644 test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc create mode 100644 test/extensions/transport_sockets/tls/test_private_key_method_provider.cc rename test/extensions/transport_sockets/tls/{rsa_private_key_method_provider.h => test_private_key_method_provider.h} (63%) diff --git a/test/extensions/transport_sockets/tls/BUILD b/test/extensions/transport_sockets/tls/BUILD index 3c61133e4a99..f68a38c682d4 100644 --- a/test/extensions/transport_sockets/tls/BUILD +++ b/test/extensions/transport_sockets/tls/BUILD @@ -21,8 +21,7 @@ envoy_cc_test( ], external_deps = ["ssl"], deps = [ - ":ecdsa_private_key_method_provider_test_lib", - ":rsa_private_key_method_provider_test_lib", + ":test_private_key_method_provider_test_lib", "//include/envoy/network:transport_socket_interface", "//source/common/buffer:buffer_lib", "//source/common/common:empty_string", @@ -113,32 +112,12 @@ envoy_cc_test_library( ) envoy_cc_test_library( - name = "rsa_private_key_method_provider_test_lib", + name = "test_private_key_method_provider_test_lib", srcs = [ - "rsa_private_key_method_provider.cc", + "test_private_key_method_provider.cc", ], hdrs = [ - "rsa_private_key_method_provider.h", - ], - external_deps = ["ssl"], - deps = [ - "//include/envoy/api:api_interface", - "//include/envoy/event:dispatcher_interface", - "//include/envoy/server:transport_socket_config_interface", - "//include/envoy/ssl/private_key:private_key_config_interface", - "//include/envoy/ssl/private_key:private_key_interface", - "//source/common/config:utility_lib", - "//source/common/protobuf:utility_lib", - ], -) - -envoy_cc_test_library( - name = "ecdsa_private_key_method_provider_test_lib", - srcs = [ - "ecdsa_private_key_method_provider.cc", - ], - hdrs = [ - "ecdsa_private_key_method_provider.h", + "test_private_key_method_provider.h", ], external_deps = ["ssl"], deps = [ diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc deleted file mode 100644 index 5aad26e2170a..000000000000 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.cc +++ /dev/null @@ -1,176 +0,0 @@ -#include "test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h" - -#include - -#include "envoy/api/api.h" - -#include "openssl/ssl.h" - -namespace Envoy { -namespace Extensions { -namespace PrivateKeyMethodProvider { - -int EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index = -1; - -void EcdsaPrivateKeyConnection::delayed_op() { - const std::chrono::milliseconds timeout_0ms{0}; - - timer_ = dispatcher_.createTimer([this]() -> void { - finished_ = true; - this->cb_.onPrivateKeyMethodComplete(); - return; - }); - timer_->enableTimer(timeout_0ms); -} - -static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t*, size_t, - uint16_t signature_algorithm, const uint8_t* in, - size_t in_len) { - unsigned char hash[EVP_MAX_MD_SIZE]; - unsigned int hash_len; - bssl::ScopedEVP_MD_CTX ctx; - EcdsaPrivateKeyConnection* ops = static_cast( - SSL_get_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index)); - unsigned int result_len; - - if (!ops) { - return ssl_private_key_failure; - } - - bssl::UniquePtr ec_key(ops->getPrivateKey()); - if (!ec_key) { - return ssl_private_key_failure; - } - - const EVP_MD* md = SSL_get_signature_algorithm_digest(signature_algorithm); - if (!md) { - return ssl_private_key_failure; - } - - // Calculate the digest for signing. - if (!EVP_DigestInit_ex(ctx.get(), md, nullptr) || !EVP_DigestUpdate(ctx.get(), in, in_len) || - !EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) { - return ssl_private_key_failure; - } - - // Borrow "out" because it has been already initialized to the max_out size. - if (!ECDSA_sign(0, hash, hash_len, out, &result_len, ec_key.get())) { - return ssl_private_key_failure; - } - - ops->output_.assign(out, out + result_len); - - // Tell SSL socket that the operation is ready to be called again. - ops->delayed_op(); - - return ssl_private_key_retry; -} - -static ssl_private_key_result_t privateKeyDecrypt(SSL*, uint8_t*, size_t*, size_t, const uint8_t*, - size_t) { - return ssl_private_key_failure; -} - -static ssl_private_key_result_t privateKeyComplete(SSL* ssl, uint8_t* out, size_t* out_len, - size_t max_out) { - EcdsaPrivateKeyConnection* ops = static_cast( - SSL_get_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index)); - - if (!ops->finished_) { - // The operation didn't finish yet, retry. - return ssl_private_key_retry; - } - - if (ops->test_options_.async_method_error_) { - return ssl_private_key_failure; - } - - if (ops->output_.size() > max_out) { - return ssl_private_key_failure; - } - - std::copy(ops->output_.begin(), ops->output_.end(), out); - *out_len = ops->output_.size(); - - return ssl_private_key_success; -} - -Ssl::BoringSslPrivateKeyMethodSharedPtr -EcdsaPrivateKeyMethodProvider::getBoringSslPrivateKeyMethod() { - return method_; -} - -bool EcdsaPrivateKeyMethodProvider::checkFips() { - const EC_KEY* ecdsa_private_key = EVP_PKEY_get0_EC_KEY(pkey_.get()); - if (ecdsa_private_key == nullptr || !EC_KEY_check_fips(ecdsa_private_key)) { - return false; - } - return true; -} - -EcdsaPrivateKeyConnection::EcdsaPrivateKeyConnection( - Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, - bssl::UniquePtr pkey, EcdsaPrivateKeyConnectionTestOptions& test_options) - : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(move(pkey)) {} - -void EcdsaPrivateKeyMethodProvider::registerPrivateKeyMethod(SSL* ssl, - Ssl::PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher) { - EcdsaPrivateKeyConnection* ops = - new EcdsaPrivateKeyConnection(cb, dispatcher, bssl::UpRef(pkey_), test_options_); - SSL_set_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index, ops); -} - -void EcdsaPrivateKeyMethodProvider::unregisterPrivateKeyMethod(SSL* ssl) { - EcdsaPrivateKeyConnection* ops = static_cast( - SSL_get_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index)); - SSL_set_ex_data(ssl, EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index, nullptr); - delete ops; -} - -EcdsaPrivateKeyMethodProvider::EcdsaPrivateKeyMethodProvider( - const ProtobufWkt::Struct& config, - Server::Configuration::TransportSocketFactoryContext& factory_context) { - - std::string private_key_path; - - if (EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index == -1) { - EcdsaPrivateKeyMethodProvider::ssl_ecdsa_connection_index = - SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); - } - - for (auto& value_it : config.fields()) { - auto& value = value_it.second; - if (value_it.first == "private_key_file" && - value.kind_case() == ProtobufWkt::Value::kStringValue) { - private_key_path = value.string_value(); - } - if (value_it.first == "async_method_error" && - value.kind_case() == ProtobufWkt::Value::kBoolValue) { - test_options_.async_method_error_ = value.bool_value(); - } - } - - std::string private_key = factory_context.api().fileSystem().fileReadToEnd(private_key_path); - bssl::UniquePtr bio( - BIO_new_mem_buf(const_cast(private_key.data()), private_key.size())); - bssl::UniquePtr pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); - if (pkey == nullptr) { - throw EnvoyException("Failed to read private key from disk."); - } - - if (EVP_PKEY_id(pkey.get()) != EVP_PKEY_EC) { - throw EnvoyException("Private key is of wrong type."); - } - - pkey_ = std::move(pkey); - - method_ = std::make_shared(); - method_->sign = privateKeySign; - method_->decrypt = privateKeyDecrypt; - method_->complete = privateKeyComplete; -} - -} // namespace PrivateKeyMethodProvider -} // namespace Extensions -} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h deleted file mode 100644 index 107e43da3b9b..000000000000 --- a/test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "envoy/event/dispatcher.h" -#include "envoy/server/transport_socket_config.h" -#include "envoy/ssl/private_key/private_key.h" -#include "envoy/ssl/private_key/private_key_config.h" - -#include "common/config/utility.h" -#include "common/protobuf/utility.h" - -namespace Envoy { -namespace Extensions { -namespace PrivateKeyMethodProvider { - -struct EcdsaPrivateKeyConnectionTestOptions { - // Return an error from the private key method completion function. - bool async_method_error_{}; -}; - -// An example ECDSA private key method provider for testing the decrypt() and sign() -// functionality. -class EcdsaPrivateKeyConnection { -public: - EcdsaPrivateKeyConnection(Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, - bssl::UniquePtr pkey, - EcdsaPrivateKeyConnectionTestOptions& test_options); - EC_KEY* getPrivateKey() { return EVP_PKEY_get1_EC_KEY(pkey_.get()); } - void delayed_op(); - - // Store the output data temporarily. - std::vector output_; - // Is the operation finished? - bool finished_{}; - EcdsaPrivateKeyConnectionTestOptions& test_options_; - -private: - Ssl::PrivateKeyConnectionCallbacks& cb_; - Event::Dispatcher& dispatcher_; - bssl::UniquePtr pkey_; - Event::TimerPtr timer_; -}; - -class EcdsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider { -public: - EcdsaPrivateKeyMethodProvider( - const ProtobufWkt::Struct& config, - Server::Configuration::TransportSocketFactoryContext& factory_context); - // Ssl::PrivateKeyMethodProvider - void registerPrivateKeyMethod(SSL* ssl, Ssl::PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher) override; - void unregisterPrivateKeyMethod(SSL* ssl) override; - bool checkFips() override; - Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; - - static int ssl_ecdsa_connection_index; - -private: - Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; - bssl::UniquePtr pkey_; - EcdsaPrivateKeyConnectionTestOptions test_options_; -}; - -class EcdsaPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { -public: - // Ssl::PrivateKeyMethodProviderInstanceFactory - Ssl::PrivateKeyMethodProviderSharedPtr - createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyMethod& message, - Server::Configuration::TransportSocketFactoryContext& - private_key_method_provider_context) override { - return std::make_shared(message.config(), - private_key_method_provider_context); - } - - std::string name() const override { return std::string("ecdsa_test"); }; -}; - -} // namespace PrivateKeyMethodProvider -} // namespace Extensions -} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc deleted file mode 100644 index b9f070a8de9c..000000000000 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.cc +++ /dev/null @@ -1,255 +0,0 @@ -#include "test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h" - -#include - -#include "envoy/api/api.h" - -#include "openssl/ssl.h" - -namespace Envoy { -namespace Extensions { -namespace PrivateKeyMethodProvider { - -int RsaPrivateKeyMethodProvider::ssl_rsa_connection_index = -1; - -void RsaPrivateKeyConnection::delayed_op() { - const std::chrono::milliseconds timeout_0ms{0}; - - timer_ = dispatcher_.createTimer([this]() -> void { - finished_ = true; - this->cb_.onPrivateKeyMethodComplete(); - return; - }); - timer_->enableTimer(timeout_0ms); -} - -static ssl_private_key_result_t privateKeySign(SSL* ssl, uint8_t* out, size_t* out_len, - size_t max_out, uint16_t signature_algorithm, - const uint8_t* in, size_t in_len) { - bssl::ScopedEVP_MD_CTX ctx; - RsaPrivateKeyConnection* ops = static_cast( - SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); - unsigned char hash[EVP_MAX_MD_SIZE]; - unsigned int hash_len; - - if (!ops) { - return ssl_private_key_failure; - } - - if (ops->test_options_.method_error_) { - // Have an artificial test failure. - return ssl_private_key_failure; - } - - if (!ops->test_options_.sign_expected_) { - // TODO(ipuustin): throw exception, because a failure can be an expected result in some tests? - return ssl_private_key_failure; - } - - RSA* rsa = ops->getPrivateKey(); - if (rsa == nullptr) { - return ssl_private_key_failure; - } - - const EVP_MD* md = SSL_get_signature_algorithm_digest(signature_algorithm); - if (!md) { - return ssl_private_key_failure; - } - - // Calculate the digest for signing. - if (!EVP_DigestInit_ex(ctx.get(), md, nullptr) || !EVP_DigestUpdate(ctx.get(), in, in_len) || - !EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) { - return ssl_private_key_failure; - } - - // Perform signing. - if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) { - RSA_sign_pss_mgf1(rsa, out_len, out, max_out, hash, hash_len, md, nullptr, -1); - } else { - unsigned int out_len_unsigned; - if (!RSA_sign(EVP_MD_type(md), hash, hash_len, out, &out_len_unsigned, rsa)) { - return ssl_private_key_failure; - } - *out_len = out_len_unsigned; - } - - if (ops->test_options_.crypto_error_) { - // Flip the bits in the first byte to cause the handshake to fail. - out[0] ^= out[0]; - } - - if (ops->test_options_.sync_mode_) { - // Return immediately with the results. - return ssl_private_key_success; - } - - ops->output_.assign(out, out + *out_len); - - // Tell SSL socket that the operation is ready to be called again. - ops->delayed_op(); - - return ssl_private_key_retry; -} - -static ssl_private_key_result_t privateKeyDecrypt(SSL* ssl, uint8_t* out, size_t* out_len, - size_t max_out, const uint8_t* in, - size_t in_len) { - RsaPrivateKeyConnection* ops = static_cast( - SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); - - if (!ops) { - return ssl_private_key_failure; - } - - if (ops->test_options_.method_error_) { - // Have an artificial test failure. - return ssl_private_key_failure; - } - - if (!ops->test_options_.decrypt_expected_) { - // TODO(ipuustin): throw exception, because a failure can be an expected result in some tests? - return ssl_private_key_failure; - } - - RSA* rsa = ops->getPrivateKey(); - if (rsa == nullptr) { - return ssl_private_key_failure; - } - - if (!RSA_decrypt(rsa, out_len, out, max_out, in, in_len, RSA_NO_PADDING)) { - return ssl_private_key_failure; - } - - if (ops->test_options_.sync_mode_) { - // Return immediately with the results. - return ssl_private_key_success; - } - - ops->output_.assign(out, out + *out_len); - - ops->delayed_op(); - - return ssl_private_key_retry; -} - -static ssl_private_key_result_t privateKeyComplete(SSL* ssl, uint8_t* out, size_t* out_len, - size_t max_out) { - RsaPrivateKeyConnection* ops = static_cast( - SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); - - if (!ops->finished_) { - // The operation didn't finish yet, retry. - return ssl_private_key_retry; - } - - if (ops->test_options_.async_method_error_) { - return ssl_private_key_failure; - } - - if (ops->output_.size() > max_out) { - return ssl_private_key_failure; - } - - std::copy(ops->output_.begin(), ops->output_.end(), out); - *out_len = ops->output_.size(); - - return ssl_private_key_success; -} - -Ssl::BoringSslPrivateKeyMethodSharedPtr -RsaPrivateKeyMethodProvider::getBoringSslPrivateKeyMethod() { - return method_; -} - -bool RsaPrivateKeyMethodProvider::checkFips() { - RSA* rsa_private_key = EVP_PKEY_get0_RSA(pkey_.get()); - if (rsa_private_key == nullptr || !RSA_check_fips(rsa_private_key)) { - return false; - } - return true; -} - -RsaPrivateKeyConnection::RsaPrivateKeyConnection(Ssl::PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher, - bssl::UniquePtr pkey, - RsaPrivateKeyConnectionTestOptions& test_options) - : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(std::move(pkey)) {} - -void RsaPrivateKeyMethodProvider::registerPrivateKeyMethod(SSL* ssl, - Ssl::PrivateKeyConnectionCallbacks& cb, - Event::Dispatcher& dispatcher) { - RsaPrivateKeyConnection* ops = - new RsaPrivateKeyConnection(cb, dispatcher, bssl::UpRef(pkey_), test_options_); - SSL_set_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index, ops); -} - -void RsaPrivateKeyMethodProvider::unregisterPrivateKeyMethod(SSL* ssl) { - RsaPrivateKeyConnection* ops = static_cast( - SSL_get_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index)); - SSL_set_ex_data(ssl, RsaPrivateKeyMethodProvider::ssl_rsa_connection_index, nullptr); - delete ops; -} - -RsaPrivateKeyMethodProvider::RsaPrivateKeyMethodProvider( - const ProtobufWkt::Struct& config, - Server::Configuration::TransportSocketFactoryContext& factory_context) { - - std::string private_key_path; - - if (RsaPrivateKeyMethodProvider::ssl_rsa_connection_index == -1) { - RsaPrivateKeyMethodProvider::ssl_rsa_connection_index = - SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); - } - - for (auto& value_it : config.fields()) { - auto& value = value_it.second; - if (value_it.first == "private_key_file" && - value.kind_case() == ProtobufWkt::Value::kStringValue) { - private_key_path = value.string_value(); - } - if (value_it.first == "sync_mode" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { - test_options_.sync_mode_ = value.bool_value(); - } - if (value_it.first == "crypto_error" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { - test_options_.crypto_error_ = value.bool_value(); - } - if (value_it.first == "method_error" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { - test_options_.method_error_ = value.bool_value(); - } - if (value_it.first == "async_method_error" && - value.kind_case() == ProtobufWkt::Value::kBoolValue) { - test_options_.async_method_error_ = value.bool_value(); - } - if (value_it.first == "expected_operation" && - value.kind_case() == ProtobufWkt::Value::kStringValue) { - if (value.string_value() == "decrypt") { - test_options_.decrypt_expected_ = true; - } else if (value.string_value() == "sign") { - test_options_.sign_expected_ = true; - } - } - } - - std::string private_key = factory_context.api().fileSystem().fileReadToEnd(private_key_path); - bssl::UniquePtr bio( - BIO_new_mem_buf(const_cast(private_key.data()), private_key.size())); - bssl::UniquePtr pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); - if (pkey == nullptr) { - throw EnvoyException("Failed to read private key from disk."); - } - - if (EVP_PKEY_id(pkey.get()) != EVP_PKEY_RSA) { - throw EnvoyException("Private key is of wrong type."); - } - - pkey_ = std::move(pkey); - - method_ = std::make_shared(); - method_->sign = privateKeySign; - method_->decrypt = privateKeyDecrypt; - method_->complete = privateKeyComplete; -} - -} // namespace PrivateKeyMethodProvider -} // namespace Extensions -} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 9a71770b0a6d..8ae3a254c559 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -19,8 +19,6 @@ #include "extensions/transport_sockets/tls/private_key/private_key_manager_impl.h" #include "extensions/transport_sockets/tls/ssl_socket.h" -#include "test/extensions/transport_sockets/tls/ecdsa_private_key_method_provider.h" -#include "test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h" #include "test/extensions/transport_sockets/tls/ssl_certs_test.h" #include "test/extensions/transport_sockets/tls/test_data/no_san_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/password_protected_cert_info.h" @@ -28,6 +26,7 @@ #include "test/extensions/transport_sockets/tls/test_data/san_dns_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/san_uri_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert_info.h" +#include "test/extensions/transport_sockets/tls/test_private_key_method_provider.h" #include "test/mocks/buffer/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/secret/mocks.h" @@ -256,12 +255,9 @@ void testUtil(const TestUtilOptions& options) { // For private key method testing. NiceMock context_manager; - Extensions::PrivateKeyMethodProvider::RsaPrivateKeyMethodFactory rsa_factory; - Extensions::PrivateKeyMethodProvider::EcdsaPrivateKeyMethodFactory ecdsa_factory; + Extensions::PrivateKeyMethodProvider::TestPrivateKeyMethodFactory test_factory; Registry::InjectFactory - rsa_private_key_method_factory(rsa_factory); - Registry::InjectFactory - ecdsa_private_key_method_factory(ecdsa_factory); + test_private_key_method_factory(test_factory); PrivateKeyMethodManagerImpl private_key_method_manager; if (options.expectedPrivateKeyMethod()) { EXPECT_CALL(server_factory_context, sslContextManager()) @@ -4136,7 +4132,7 @@ TEST_P(SslReadBufferLimitTest, SmallReadsIntoSameSlice) { dispatcher_->run(Event::Dispatcher::RunType::Block); } -// Test asynchronous signing (ECDHE) +// Test asynchronous signing (ECDHE) using a private key provider. TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignSuccess) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4144,11 +4140,12 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignSuccess) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign sync_mode: false + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4167,7 +4164,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignSuccess) { testUtil(successful_test_options.setPrivateKeyMethodExpected(true)); } -// Test asynchronous decryption (RSA) +// Test asynchronous decryption (RSA). TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptSuccess) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4175,11 +4172,12 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptSuccess) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: decrypt sync_mode: false + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4198,7 +4196,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptSuccess) { testUtil(successful_test_options.setPrivateKeyMethodExpected(true)); } -// Test synchronous signing (ECDHE) +// Test synchronous signing (ECDHE). TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignSuccess) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4206,11 +4204,12 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignSuccess) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign sync_mode: true + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4229,7 +4228,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignSuccess) { testUtil(successful_test_options.setPrivateKeyMethodExpected(true)); } -// Test synchronous decryption (RSA) +// Test synchronous decryption (RSA). TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncDecryptSuccess) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4237,11 +4236,12 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncDecryptSuccess) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: decrypt sync_mode: true + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4260,7 +4260,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncDecryptSuccess) { testUtil(successful_test_options.setPrivateKeyMethodExpected(true)); } -// Test asynchronous signing (ECDHE) failure (invalid signature) +// Test asynchronous signing (ECDHE) failure (invalid signature). TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignFailure) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4268,12 +4268,13 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignFailure) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign sync_mode: false crypto_error: true + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4292,7 +4293,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignFailure) { "ssl.connection_error")); } -// Test synchronous signing (ECDHE) failure (invalid signature) +// Test synchronous signing (ECDHE) failure (invalid signature). TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignFailure) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4300,12 +4301,13 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignFailure) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign sync_mode: true crypto_error: true + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4332,11 +4334,12 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSignFailure) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign method_error: true + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4363,11 +4366,12 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderDecryptFailure) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: decrypt method_error: true + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4394,11 +4398,12 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignCompleteFailure) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign async_method_error: true + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4426,11 +4431,12 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptCompleteFailure) { certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: decrypt async_method_error: true + mode: rsa validation_context: trusted_ca: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" @@ -4471,11 +4477,12 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderMultiCertSuccess) { - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign sync_mode: false + mode: rsa - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" private_key: @@ -4508,27 +4515,29 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderMultiCertFail) { - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign sync_mode: false + mode: rsa - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" expected_operation: sign sync_mode: false + mode: rsa )EOF"; TestUtilOptions failing_test_options(client_ctx_yaml, server_ctx_yaml, false, GetParam()); EXPECT_THROW_WITH_MESSAGE(testUtil(failing_test_options.setPrivateKeyMethodExpected(true)), - EnvoyException, "Private key is of wrong type.") + EnvoyException, "Private key is not RSA.") } -// Test ECDSA private key method provider. +// Test ECDSA private key method provider mode. TEST_P(SslSocketTest, EcdsaPrivateKeyProviderSuccess) { const std::string client_ctx_yaml = absl::StrCat(R"EOF( common_tls_context: @@ -4547,17 +4556,20 @@ TEST_P(SslSocketTest, EcdsaPrivateKeyProviderSuccess) { - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" private_key_method: - provider_name: ecdsa_test + provider_name: test config: private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" + expected_operation: sign + mode: ecdsa )EOF"; TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, GetParam()); testUtil(test_options.setPrivateKeyMethodExpected(true)); } -// Test having two certs with different private key methods. It's expected that the ECDSA provider -// is being used. RSA provider is set to fail with "async_method_error", but that's not happening. +// Test having two certs with different private key method modes. It's expected that the ECDSA +// provider mode is being used. RSA provider mode is set to fail with "async_method_error", but +// that's not happening. TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertSuccess) { const std::string client_ctx_yaml = absl::StrCat(R"EOF( common_tls_context: @@ -4577,24 +4589,27 @@ TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertSuccess) { - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign sync_mode: false async_method_error: true + mode: rsa - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" private_key_method: - provider_name: ecdsa_test + provider_name: test config: private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" + expected_operation: sign + mode: ecdsa )EOF"; TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, GetParam()); testUtil(test_options.setPrivateKeyMethodExpected(true)); } -// Test having two certs with different private key methods. ECDSA provider is set to fail. +// Test having two certs with different private key method modes. ECDSA provider is set to fail. TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertFail) { const std::string client_ctx_yaml = absl::StrCat(R"EOF( common_tls_context: @@ -4614,18 +4629,21 @@ TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertFail) { - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" private_key_method: - provider_name: rsa_test + provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" expected_operation: sign sync_mode: false + mode: rsa - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" private_key_method: - provider_name: ecdsa_test + provider_name: test config: private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" + expected_operation: sign async_method_error: true + mode: ecdsa )EOF"; TestUtilOptions failing_test_options(client_ctx_yaml, server_ctx_yaml, false, GetParam()); testUtil(failing_test_options.setPrivateKeyMethodExpected(true) diff --git a/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc new file mode 100644 index 000000000000..04b1ef033eff --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc @@ -0,0 +1,378 @@ +#include "test/extensions/transport_sockets/tls/test_private_key_method_provider.h" + +#include + +#include "envoy/api/api.h" + +#include "openssl/ssl.h" + +namespace Envoy { +namespace Extensions { +namespace PrivateKeyMethodProvider { + +void TestPrivateKeyConnection::delayed_op() { + const std::chrono::milliseconds timeout_0ms{0}; + + timer_ = dispatcher_.createTimer([this]() -> void { + finished_ = true; + this->cb_.onPrivateKeyMethodComplete(); + return; + }); + timer_->enableTimer(timeout_0ms); +} + +static int calculateDigest(const EVP_MD* md, const uint8_t* in, size_t in_len, unsigned char* hash, + unsigned int* hash_len) { + bssl::ScopedEVP_MD_CTX ctx; + + // Calculate the message digest for signing. + if (!EVP_DigestInit_ex(ctx.get(), md, nullptr) || !EVP_DigestUpdate(ctx.get(), in, in_len) || + !EVP_DigestFinal_ex(ctx.get(), hash, hash_len)) { + return 0; + } + return 1; +} + +static ssl_private_key_result_t ecdsaPrivateKeySign(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out, uint16_t signature_algorithm, + const uint8_t* in, size_t in_len) { + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int hash_len; + TestPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, TestPrivateKeyMethodProvider::ecdsaConnectionIndex())); + unsigned int out_len_unsigned; + + if (!ops) { + return ssl_private_key_failure; + } + + if (ops->test_options_.method_error_) { + // Have an artificial test failure. + return ssl_private_key_failure; + } + + if (!ops->test_options_.sign_expected_) { + return ssl_private_key_failure; + } + + const EVP_MD* md = SSL_get_signature_algorithm_digest(signature_algorithm); + if (!md) { + return ssl_private_key_failure; + } + + if (!calculateDigest(md, in, in_len, hash, &hash_len)) { + return ssl_private_key_failure; + } + + bssl::UniquePtr ec_key(EVP_PKEY_get1_EC_KEY(ops->getPrivateKey())); + if (!ec_key) { + return ssl_private_key_failure; + } + + // Borrow "out" because it has been already initialized to the max_out size. + if (!ECDSA_sign(0, hash, hash_len, out, &out_len_unsigned, ec_key.get())) { + return ssl_private_key_failure; + } + + if (ops->test_options_.sync_mode_) { + // Return immediately with the results. + if (out_len_unsigned > max_out) { + return ssl_private_key_failure; + } + *out_len = out_len_unsigned; + return ssl_private_key_success; + } + + ops->output_.assign(out, out + out_len_unsigned); + // Tell SSL socket that the operation is ready to be called again. + ops->delayed_op(); + + return ssl_private_key_retry; +} + +static ssl_private_key_result_t ecdsaPrivateKeyDecrypt(SSL*, uint8_t*, size_t*, size_t, + const uint8_t*, size_t) { + return ssl_private_key_failure; +} + +static ssl_private_key_result_t rsaPrivateKeySign(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out, uint16_t signature_algorithm, + const uint8_t* in, size_t in_len) { + TestPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, TestPrivateKeyMethodProvider::rsaConnectionIndex())); + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int hash_len; + + if (!ops) { + return ssl_private_key_failure; + } + + if (ops->test_options_.method_error_) { + return ssl_private_key_failure; + } + + if (!ops->test_options_.sign_expected_) { + return ssl_private_key_failure; + } + + const EVP_MD* md = SSL_get_signature_algorithm_digest(signature_algorithm); + if (!md) { + return ssl_private_key_failure; + } + + if (!calculateDigest(md, in, in_len, hash, &hash_len)) { + return ssl_private_key_failure; + } + + RSA* rsa = EVP_PKEY_get0_RSA(ops->getPrivateKey()); + if (rsa == nullptr) { + return ssl_private_key_failure; + } + + // Perform RSA signing. + if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) { + RSA_sign_pss_mgf1(rsa, out_len, out, max_out, hash, hash_len, md, nullptr, -1); + } else { + unsigned int out_len_unsigned; + if (!RSA_sign(EVP_MD_type(md), hash, hash_len, out, &out_len_unsigned, rsa)) { + return ssl_private_key_failure; + } + if (out_len_unsigned > max_out) { + return ssl_private_key_failure; + } + *out_len = out_len_unsigned; + } + + if (ops->test_options_.crypto_error_) { + // Flip the bits in the first byte to cause the handshake to fail. + out[0] ^= out[0]; + } + + if (ops->test_options_.sync_mode_) { + return ssl_private_key_success; + } + + ops->output_.assign(out, out + *out_len); + ops->delayed_op(); + + return ssl_private_key_retry; +} + +static ssl_private_key_result_t rsaPrivateKeyDecrypt(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out, const uint8_t* in, + size_t in_len) { + TestPrivateKeyConnection* ops = static_cast( + SSL_get_ex_data(ssl, TestPrivateKeyMethodProvider::rsaConnectionIndex())); + + if (!ops) { + return ssl_private_key_failure; + } + + if (ops->test_options_.method_error_) { + return ssl_private_key_failure; + } + + if (!ops->test_options_.decrypt_expected_) { + return ssl_private_key_failure; + } + + RSA* rsa = EVP_PKEY_get0_RSA(ops->getPrivateKey()); + if (rsa == nullptr) { + return ssl_private_key_failure; + } + + if (!RSA_decrypt(rsa, out_len, out, max_out, in, in_len, RSA_NO_PADDING)) { + return ssl_private_key_failure; + } + + if (ops->test_options_.sync_mode_) { + return ssl_private_key_success; + } + + ops->output_.assign(out, out + *out_len); + ops->delayed_op(); + + return ssl_private_key_retry; +} + +static ssl_private_key_result_t privateKeyComplete(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out, int id) { + TestPrivateKeyConnection* ops = static_cast(SSL_get_ex_data(ssl, id)); + + if (!ops->finished_) { + // The operation didn't finish yet, retry. + return ssl_private_key_retry; + } + + if (ops->test_options_.async_method_error_) { + return ssl_private_key_failure; + } + + if (ops->output_.size() > max_out) { + return ssl_private_key_failure; + } + + std::copy(ops->output_.begin(), ops->output_.end(), out); + *out_len = ops->output_.size(); + + return ssl_private_key_success; +} + +static ssl_private_key_result_t rsaPrivateKeyComplete(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out) { + return privateKeyComplete(ssl, out, out_len, max_out, + TestPrivateKeyMethodProvider::rsaConnectionIndex()); +} + +static ssl_private_key_result_t ecdsaPrivateKeyComplete(SSL* ssl, uint8_t* out, size_t* out_len, + size_t max_out) { + return privateKeyComplete(ssl, out, out_len, max_out, + TestPrivateKeyMethodProvider::ecdsaConnectionIndex()); +} + +Ssl::BoringSslPrivateKeyMethodSharedPtr +TestPrivateKeyMethodProvider::getBoringSslPrivateKeyMethod() { + return method_; +} + +bool TestPrivateKeyMethodProvider::checkFips() { + if (mode_ == "rsa") { + RSA* rsa_private_key = EVP_PKEY_get0_RSA(pkey_.get()); + if (rsa_private_key == nullptr || !RSA_check_fips(rsa_private_key)) { + return false; + } + } else { // if (mode_ == "ecdsa") + const EC_KEY* ecdsa_private_key = EVP_PKEY_get0_EC_KEY(pkey_.get()); + if (ecdsa_private_key == nullptr || !EC_KEY_check_fips(ecdsa_private_key)) { + return false; + } + } + return true; +} + +TestPrivateKeyConnection::TestPrivateKeyConnection( + Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, + bssl::UniquePtr pkey, TestPrivateKeyConnectionTestOptions& test_options) + : test_options_(test_options), cb_(cb), dispatcher_(dispatcher), pkey_(std::move(pkey)) {} + +void TestPrivateKeyMethodProvider::registerPrivateKeyMethod(SSL* ssl, + Ssl::PrivateKeyConnectionCallbacks& cb, + Event::Dispatcher& dispatcher) { + TestPrivateKeyConnection* ops; + // In multi-cert case, when the same provider is used in different modes with the same SSL object, + // we need to keep both rsa and ecdsa connection objects in store because the test options for the + // two certificates may be different. We need to be able to deduct in the signing, decryption, and + // completion functions which options to use, so we associate the connection objects to the same + // SSL object using different user data indexes. + // + // Another way to do this would be to store both test options in one connection object. + int index = mode_ == "rsa" ? TestPrivateKeyMethodProvider::rsaConnectionIndex() + : TestPrivateKeyMethodProvider::ecdsaConnectionIndex(); + + // Check if there is another certificate of the same mode associated with the context. This would + // be an error. + ops = static_cast(SSL_get_ex_data(ssl, index)); + if (ops != nullptr) { + throw EnvoyException( + "Can't distinguish between two registered providers for the same SSL object."); + } + + ops = new TestPrivateKeyConnection(cb, dispatcher, bssl::UpRef(pkey_), test_options_); + SSL_set_ex_data(ssl, index, ops); +} + +void TestPrivateKeyMethodProvider::unregisterPrivateKeyMethod(SSL* ssl) { + int index = mode_ == "rsa" ? TestPrivateKeyMethodProvider::rsaConnectionIndex() + : TestPrivateKeyMethodProvider::ecdsaConnectionIndex(); + TestPrivateKeyConnection* ops = + static_cast(SSL_get_ex_data(ssl, index)); + SSL_set_ex_data(ssl, index, nullptr); + delete ops; +} + +static int createIndex() { + int index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); + RELEASE_ASSERT(index >= 0, "Failed to get SSL user data index."); + return index; +} + +int TestPrivateKeyMethodProvider::rsaConnectionIndex() { + CONSTRUCT_ON_FIRST_USE(int, createIndex()); +} + +int TestPrivateKeyMethodProvider::ecdsaConnectionIndex() { + CONSTRUCT_ON_FIRST_USE(int, createIndex()); +} + +TestPrivateKeyMethodProvider::TestPrivateKeyMethodProvider( + const ProtobufWkt::Struct& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) { + std::string private_key_path; + + for (auto& value_it : config.fields()) { + auto& value = value_it.second; + if (value_it.first == "private_key_file" && + value.kind_case() == ProtobufWkt::Value::kStringValue) { + private_key_path = value.string_value(); + } + if (value_it.first == "sync_mode" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { + test_options_.sync_mode_ = value.bool_value(); + } + if (value_it.first == "crypto_error" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { + test_options_.crypto_error_ = value.bool_value(); + } + if (value_it.first == "method_error" && value.kind_case() == ProtobufWkt::Value::kBoolValue) { + test_options_.method_error_ = value.bool_value(); + } + if (value_it.first == "async_method_error" && + value.kind_case() == ProtobufWkt::Value::kBoolValue) { + test_options_.async_method_error_ = value.bool_value(); + } + if (value_it.first == "expected_operation" && + value.kind_case() == ProtobufWkt::Value::kStringValue) { + if (value.string_value() == "decrypt") { + test_options_.decrypt_expected_ = true; + } else if (value.string_value() == "sign") { + test_options_.sign_expected_ = true; + } + } + if (value_it.first == "mode" && value.kind_case() == ProtobufWkt::Value::kStringValue) { + mode_ = value.string_value(); + } + } + + std::string private_key = factory_context.api().fileSystem().fileReadToEnd(private_key_path); + bssl::UniquePtr bio( + BIO_new_mem_buf(const_cast(private_key.data()), private_key.size())); + bssl::UniquePtr pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); + if (pkey == nullptr) { + throw EnvoyException("Failed to read private key from disk."); + } + + method_ = std::make_shared(); + + // Have two modes, "rsa" and "ecdsa", for testing multi-cert use cases. + if (mode_ == "rsa") { + if (EVP_PKEY_id(pkey.get()) != EVP_PKEY_RSA) { + throw EnvoyException("Private key is not RSA."); + } + method_->sign = rsaPrivateKeySign; + method_->decrypt = rsaPrivateKeyDecrypt; + method_->complete = rsaPrivateKeyComplete; + } else if (mode_ == "ecdsa") { + if (EVP_PKEY_id(pkey.get()) != EVP_PKEY_EC) { + throw EnvoyException("Private key is not ECDSA."); + } + method_->sign = ecdsaPrivateKeySign; + method_->decrypt = ecdsaPrivateKeyDecrypt; + method_->complete = ecdsaPrivateKeyComplete; + } else { + throw EnvoyException("Unknown test provider mode."); + } + + pkey_ = std::move(pkey); +} + +} // namespace PrivateKeyMethodProvider +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h b/test/extensions/transport_sockets/tls/test_private_key_method_provider.h similarity index 63% rename from test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h rename to test/extensions/transport_sockets/tls/test_private_key_method_provider.h index 5e63233cc73d..ca95f6a23373 100644 --- a/test/extensions/transport_sockets/tls/rsa_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/test_private_key_method_provider.h @@ -12,7 +12,7 @@ namespace Envoy { namespace Extensions { namespace PrivateKeyMethodProvider { -struct RsaPrivateKeyConnectionTestOptions { +struct TestPrivateKeyConnectionTestOptions { // Return private key method value directly without asynchronous operation. bool sync_mode_{}; @@ -32,32 +32,34 @@ struct RsaPrivateKeyConnectionTestOptions { bool async_method_error_{}; }; -// An example RSA private key method provider for testing the decrypt() and sign() +// An example private key method provider for testing the decrypt() and sign() // functionality. -class RsaPrivateKeyConnection { +class TestPrivateKeyConnection { public: - RsaPrivateKeyConnection(Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, - bssl::UniquePtr pkey, - RsaPrivateKeyConnectionTestOptions& test_options); - RSA* getPrivateKey() { return EVP_PKEY_get0_RSA(pkey_.get()); } + TestPrivateKeyConnection(Ssl::PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher, + bssl::UniquePtr pkey, + TestPrivateKeyConnectionTestOptions& test_options); + EVP_PKEY* getPrivateKey() { return pkey_.get(); } void delayed_op(); - // Store the output data temporarily. std::vector output_; - // Is the operation finished? + // The complete callback can return other value than "retry" only after + // onPrivateKeyMethodComplete() function has been called. This is controlled by "finished" + // variable. bool finished_{}; - RsaPrivateKeyConnectionTestOptions& test_options_; + TestPrivateKeyConnectionTestOptions& test_options_; private: Ssl::PrivateKeyConnectionCallbacks& cb_; Event::Dispatcher& dispatcher_; bssl::UniquePtr pkey_; + // A zero-length timer controls the callback. Event::TimerPtr timer_; }; -class RsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider { +class TestPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider { public: - RsaPrivateKeyMethodProvider( + TestPrivateKeyMethodProvider( const ProtobufWkt::Struct& config, Server::Configuration::TransportSocketFactoryContext& factory_context); // Ssl::PrivateKeyMethodProvider @@ -67,26 +69,28 @@ class RsaPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvider bool checkFips() override; Ssl::BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() override; - static int ssl_rsa_connection_index; + static int rsaConnectionIndex(); + static int ecdsaConnectionIndex(); private: Ssl::BoringSslPrivateKeyMethodSharedPtr method_{}; bssl::UniquePtr pkey_; - RsaPrivateKeyConnectionTestOptions test_options_; + TestPrivateKeyConnectionTestOptions test_options_; + std::string mode_; }; -class RsaPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { +class TestPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { public: // Ssl::PrivateKeyMethodProviderInstanceFactory Ssl::PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyMethod& message, Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) override { - return std::make_shared(message.config(), - private_key_method_provider_context); + return std::make_shared(message.config(), + private_key_method_provider_context); } - std::string name() const override { return std::string("rsa_test"); }; + std::string name() const override { return std::string("test"); }; }; } // namespace PrivateKeyMethodProvider From 9a321eb9ed978a8403dd452d20865397a21e9304 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 25 Jun 2019 15:31:03 +0300 Subject: [PATCH 46/56] tls: change from PrivateKeyMethod to PrivateKeyProvider. Signed-off-by: Ismo Puustinen --- api/envoy/api/v2/auth/cert.proto | 11 +++--- include/envoy/ssl/private_key/private_key.h | 4 +-- .../ssl/private_key/private_key_config.h | 2 +- .../common/ssl/tls_certificate_config_impl.cc | 8 ++--- .../tls/context_config_impl.cc | 2 +- .../private_key/private_key_manager_impl.cc | 2 +- .../private_key/private_key_manager_impl.h | 2 +- .../tls/context_impl_test.cc | 10 +++--- .../transport_sockets/tls/ssl_socket_test.cc | 36 +++++++++---------- .../tls/test_private_key_method_provider.h | 2 +- test/mocks/ssl/mocks.h | 2 +- 11 files changed, 41 insertions(+), 40 deletions(-) diff --git a/api/envoy/api/v2/auth/cert.proto b/api/envoy/api/v2/auth/cert.proto index 3d5818a75327..30db22c6d7a6 100644 --- a/api/envoy/api/v2/auth/cert.proto +++ b/api/envoy/api/v2/auth/cert.proto @@ -107,7 +107,7 @@ message TlsParameters { // BoringSSL private key method configuration. The private key methods are used for external // (potentially asynchronous) signing and decryption operations. Some use cases for private key // methods would be TPM support and TLS acceleration. -message PrivateKeyMethod { +message PrivateKeyProvider { // Private key method provider name. The name must match a // supported private key method provider type. string provider_name = 1 [(validate.rules).string.min_bytes = 1]; @@ -127,13 +127,14 @@ message TlsCertificate { // The TLS private key. core.DataSource private_key = 2; - // BoringSSL private key method. This is an alternative to :ref:`private_key + // BoringSSL private key method provider. This is an alternative to :ref:`private_key // ` field. This can't be // marked as ``oneof`` due to API compatibility reasons. Setting both :ref:`private_key - // ` and :ref:`private_key_method - // ` fields will result in an + // ` and + // :ref:`private_key_provider + // ` fields will result in an // error. - PrivateKeyMethod private_key_method = 6; + PrivateKeyProvider private_key_provider = 6; // The password to decrypt the TLS private key. If this field is not set, it is assumed that the // TLS private key is not password encrypted. diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index f96af97cdbbc..d32565ebb568 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -71,14 +71,14 @@ class PrivateKeyMethodManager { * Finds and returns a private key operations provider for BoringSSL. * * @param message a protobuf message object containing a - * PrivateKeyMethod message. + * PrivateKeyProvider message. * @param private_key_method_provider_context context that provides components for creating and * initializing connections for keyless TLS etc. * @return PrivateKeyMethodProvider the private key operations provider, or nullptr if * no provider can be used with the context configuration. */ virtual PrivateKeyMethodProviderSharedPtr - createPrivateKeyMethodProvider(const envoy::api::v2::auth::PrivateKeyMethod& message, + createPrivateKeyMethodProvider(const envoy::api::v2::auth::PrivateKeyProvider& message, Envoy::Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) PURE; }; diff --git a/include/envoy/ssl/private_key/private_key_config.h b/include/envoy/ssl/private_key/private_key_config.h index dbe3a2159d8d..fcf670491ce3 100644 --- a/include/envoy/ssl/private_key/private_key_config.h +++ b/include/envoy/ssl/private_key/private_key_config.h @@ -13,7 +13,7 @@ class PrivateKeyMethodProviderInstanceFactory { public: virtual ~PrivateKeyMethodProviderInstanceFactory() = default; virtual PrivateKeyMethodProviderSharedPtr - createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyMethod& message, + createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyProvider& message, Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) PURE; virtual std::string name() const PURE; diff --git a/source/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index 77be58fd74cf..5b28c3568e09 100644 --- a/source/common/ssl/tls_certificate_config_impl.cc +++ b/source/common/ssl/tls_certificate_config_impl.cc @@ -26,14 +26,14 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl( password_path_(Config::DataSource::getPath(config.password()) .value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)), private_key_method_( - factory_context != nullptr && config.has_private_key_method() + factory_context != nullptr && config.has_private_key_provider() ? factory_context->sslContextManager() .privateKeyMethodManager() - .createPrivateKeyMethodProvider(config.private_key_method(), *factory_context) + .createPrivateKeyMethodProvider(config.private_key_provider(), *factory_context) : nullptr) { - if (config.has_private_key_method() && config.has_private_key()) { + if (config.has_private_key_provider() && config.has_private_key()) { throw EnvoyException(fmt::format( - "Certificate configuration can't have both private_key and private_key_method")); + "Certificate configuration can't have both private_key and private_key_provider")); } if (certificate_chain_.empty() || (private_key_.empty() && private_key_method_ == nullptr)) { throw EnvoyException(fmt::format("Failed to load incomplete certificate from {}, {}", diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index e398e53add69..5f61453a5162 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -26,7 +26,7 @@ std::vector getTlsCertificateConf if (!config.tls_certificates().empty()) { std::vector providers; for (const auto& tls_certificate : config.tls_certificates()) { - if (!tls_certificate.has_private_key_method() && !tls_certificate.has_certificate_chain() && + if (!tls_certificate.has_private_key_provider() && !tls_certificate.has_certificate_chain() && !tls_certificate.has_private_key()) { continue; } diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc index fbabaa9dc14e..b88724723a35 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc @@ -9,7 +9,7 @@ namespace Tls { Envoy::Ssl::PrivateKeyMethodProviderSharedPtr PrivateKeyMethodManagerImpl::createPrivateKeyMethodProvider( - const envoy::api::v2::auth::PrivateKeyMethod& message, + const envoy::api::v2::auth::PrivateKeyProvider& message, Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) { Ssl::PrivateKeyMethodProviderInstanceFactory* factory = diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h index 38af3d936813..d04ce12ae9ff 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h @@ -13,7 +13,7 @@ class PrivateKeyMethodManagerImpl : public virtual Ssl::PrivateKeyMethodManager public: // Ssl::PrivateKeyMethodManager Ssl::PrivateKeyMethodProviderSharedPtr - createPrivateKeyMethodProvider(const envoy::api::v2::auth::PrivateKeyMethod& message, + createPrivateKeyMethodProvider(const envoy::api::v2::auth::PrivateKeyProvider& message, Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) override; }; diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index a27667a1648f..9873b2275019 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -1190,7 +1190,7 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoProvider) { tls_certificates: - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" - private_key_method: + private_key_provider: provider_name: mock_provider typed_config: "@type": type.googleapis.com/google.protobuf.Struct @@ -1223,7 +1223,7 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureNoMethod) { tls_certificates: - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" - private_key_method: + private_key_provider: provider_name: mock_provider typed_config: "@type": type.googleapis.com/google.protobuf.Struct @@ -1254,7 +1254,7 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadSuccess) { tls_certificates: - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" - private_key_method: + private_key_provider: provider_name: mock_provider typed_config: "@type": type.googleapis.com/google.protobuf.Struct @@ -1283,7 +1283,7 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureBothKeyAndMethod) filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" private_key: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_key.pem" - private_key_method: + private_key_provider: provider_name: mock_provider typed_config: "@type": type.googleapis.com/google.protobuf.Struct @@ -1293,7 +1293,7 @@ TEST_F(ServerContextConfigImplTest, PrivateKeyMethodLoadFailureBothKeyAndMethod) TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); EXPECT_THROW_WITH_MESSAGE( ServerContextConfigImpl server_context_config(tls_context, factory_context_), EnvoyException, - "Certificate configuration can't have both private_key and private_key_method"); + "Certificate configuration can't have both private_key and private_key_provider"); } } // namespace Tls diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 8ae3a254c559..02caf2eb3339 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -4139,7 +4139,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignSuccess) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4171,7 +4171,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptSuccess) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4203,7 +4203,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignSuccess) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4235,7 +4235,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncDecryptSuccess) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4267,7 +4267,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignFailure) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4300,7 +4300,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSyncSignFailure) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4333,7 +4333,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderSignFailure) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4365,7 +4365,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderDecryptFailure) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4397,7 +4397,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncSignCompleteFailure) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4430,7 +4430,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderAsyncDecryptCompleteFailure) { tls_certificates: certificate_chain: filename: "{{ test_tmpdir }}/unittestcert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4476,7 +4476,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderMultiCertSuccess) { tls_certificates: - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4514,7 +4514,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderMultiCertFail) { tls_certificates: - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4523,7 +4523,7 @@ TEST_P(SslSocketTest, RsaPrivateKeyProviderMultiCertFail) { mode: rsa - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" @@ -4555,7 +4555,7 @@ TEST_P(SslSocketTest, EcdsaPrivateKeyProviderSuccess) { tls_certificates: - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" @@ -4588,7 +4588,7 @@ TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertSuccess) { tls_certificates: - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4598,7 +4598,7 @@ TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertSuccess) { mode: rsa - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" @@ -4628,7 +4628,7 @@ TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertFail) { tls_certificates: - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_tmpdir }}/unittestkey.pem" @@ -4637,7 +4637,7 @@ TEST_P(SslSocketTest, RsaAndEcdsaPrivateKeyProviderMultiCertFail) { mode: rsa - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert.pem" - private_key_method: + private_key_provider: provider_name: test config: private_key_file: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_key.pem" diff --git a/test/extensions/transport_sockets/tls/test_private_key_method_provider.h b/test/extensions/transport_sockets/tls/test_private_key_method_provider.h index ca95f6a23373..6d0711d1307c 100644 --- a/test/extensions/transport_sockets/tls/test_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/test_private_key_method_provider.h @@ -83,7 +83,7 @@ class TestPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstance public: // Ssl::PrivateKeyMethodProviderInstanceFactory Ssl::PrivateKeyMethodProviderSharedPtr - createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyMethod& message, + createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyProvider& message, Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) override { return std::make_shared(message.config(), diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index e6b434ce8c0b..7b6c35aba913 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -73,7 +73,7 @@ class MockPrivateKeyMethodManager : public PrivateKeyMethodManager { MOCK_METHOD2( createPrivateKeyMethodProvider, - PrivateKeyMethodProviderSharedPtr(const envoy::api::v2::auth::PrivateKeyMethod& message, + PrivateKeyMethodProviderSharedPtr(const envoy::api::v2::auth::PrivateKeyProvider& message, Envoy::Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context)); }; From 7ec959ec7095d095201553ae7a923816d2d6dc8e Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 25 Jun 2019 23:59:40 +0300 Subject: [PATCH 47/56] tests: change how the crypto error is done in private key method test. It seems that flipping the bits in the output data first byte may sometimes cause the connection to succeed anyway: it may be that it only touches the crypto header. Changing the digest which is signed should cause the handshake to reliably fail however. Signed-off-by: Ismo Puustinen --- .../tls/test_private_key_method_provider.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc index 04b1ef033eff..0f5ae4c0b5c2 100644 --- a/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc @@ -129,6 +129,11 @@ static ssl_private_key_result_t rsaPrivateKeySign(SSL* ssl, uint8_t* out, size_t return ssl_private_key_failure; } + if (ops->test_options_.crypto_error_) { + // Flip the bits in the first byte of the digest the handshake to fail. + hash[0] ^= hash[0]; + } + // Perform RSA signing. if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) { RSA_sign_pss_mgf1(rsa, out_len, out, max_out, hash, hash_len, md, nullptr, -1); @@ -143,11 +148,6 @@ static ssl_private_key_result_t rsaPrivateKeySign(SSL* ssl, uint8_t* out, size_t *out_len = out_len_unsigned; } - if (ops->test_options_.crypto_error_) { - // Flip the bits in the first byte to cause the handshake to fail. - out[0] ^= out[0]; - } - if (ops->test_options_.sync_mode_) { return ssl_private_key_success; } From f35cca514fa6ac60ef909a1213f400a24cbc61cc Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Thu, 27 Jun 2019 13:16:03 +0300 Subject: [PATCH 48/56] test: fix typo in comment. Signed-off-by: Ismo Puustinen --- .../transport_sockets/tls/test_private_key_method_provider.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc index 0f5ae4c0b5c2..b80f2d164e10 100644 --- a/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc @@ -130,7 +130,7 @@ static ssl_private_key_result_t rsaPrivateKeySign(SSL* ssl, uint8_t* out, size_t } if (ops->test_options_.crypto_error_) { - // Flip the bits in the first byte of the digest the handshake to fail. + // Flip the bits in the first byte of the digest so that the handshake will fail. hash[0] ^= hash[0]; } From 0a51246cc4616d45474d610972f820a456ad18b9 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Thu, 27 Jun 2019 23:26:30 +0300 Subject: [PATCH 49/56] tests: improved an error message. Signed-off-by: Ismo Puustinen --- .../transport_sockets/tls/test_private_key_method_provider.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc index b80f2d164e10..170be01c067a 100644 --- a/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc @@ -367,7 +367,7 @@ TestPrivateKeyMethodProvider::TestPrivateKeyMethodProvider( method_->decrypt = ecdsaPrivateKeyDecrypt; method_->complete = ecdsaPrivateKeyComplete; } else { - throw EnvoyException("Unknown test provider mode."); + throw EnvoyException("Unknown test provider mode, supported modes are \"rsa\" and \"ecdsa\"."); } pkey_ = std::move(pkey); From 3f57152473ed07648a24af52146b5afe505eaef8 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 15 Jul 2019 15:12:54 +0300 Subject: [PATCH 50/56] tls: change "typedef" to "using". Signed-off-by: Ismo Puustinen --- include/envoy/ssl/private_key/private_key.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index d32565ebb568..8934b24f908a 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -20,7 +20,7 @@ class TransportSocketFactoryContext; namespace Ssl { -typedef std::shared_ptr BoringSslPrivateKeyMethodSharedPtr; +using BoringSslPrivateKeyMethodSharedPtr = std::shared_ptr; class PrivateKeyMethodProvider { public: @@ -57,7 +57,7 @@ class PrivateKeyMethodProvider { virtual BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() PURE; }; -typedef std::shared_ptr PrivateKeyMethodProviderSharedPtr; +using PrivateKeyMethodProviderSharedPtr = std::shared_ptr; /** * A manager for finding correct user-provided functions for handling BoringSSL private key From ed99c598811b6af42d988dd473bdd2c141da727c Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 15 Jul 2019 16:33:17 +0300 Subject: [PATCH 51/56] test: remove redundant return. Signed-off-by: Ismo Puustinen --- .../transport_sockets/tls/test_private_key_method_provider.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc index 170be01c067a..cf78fbf3a304 100644 --- a/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc +++ b/test/extensions/transport_sockets/tls/test_private_key_method_provider.cc @@ -16,7 +16,6 @@ void TestPrivateKeyConnection::delayed_op() { timer_ = dispatcher_.createTimer([this]() -> void { finished_ = true; this->cb_.onPrivateKeyMethodComplete(); - return; }); timer_->enableTimer(timeout_0ms); } From e4cab89e306dfcd16dd514c6b09375ed2006b294 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Tue, 23 Jul 2019 14:01:45 +0300 Subject: [PATCH 52/56] tests: annotated destructors with override. Signed-off-by: Ismo Puustinen --- test/mocks/ssl/mocks.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index b58a8eb0cc68..1c7b008a11af 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -112,7 +112,7 @@ class MockServerContextConfig : public ServerContextConfig { class MockPrivateKeyMethodManager : public PrivateKeyMethodManager { public: MockPrivateKeyMethodManager(); - ~MockPrivateKeyMethodManager(); + ~MockPrivateKeyMethodManager() override; MOCK_METHOD2( createPrivateKeyMethodProvider, @@ -124,7 +124,7 @@ class MockPrivateKeyMethodManager : public PrivateKeyMethodManager { class MockPrivateKeyMethodProvider : public PrivateKeyMethodProvider { public: MockPrivateKeyMethodProvider(); - ~MockPrivateKeyMethodProvider(); + ~MockPrivateKeyMethodProvider() override; MOCK_METHOD3(registerPrivateKeyMethod, void(SSL* ssl, PrivateKeyConnectionCallbacks& cb, Event::Dispatcher& dispatcher)); From 5db871ccb8bfc4486ff478dd86d4d6c379ea0cfe Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 12 Aug 2019 15:19:43 +0300 Subject: [PATCH 53/56] tls: gather bools into an enum. Signed-off-by: Ismo Puustinen --- .../transport_sockets/tls/ssl_socket.cc | 34 +++++++++---------- .../transport_sockets/tls/ssl_socket.h | 7 ++-- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index ba315b796cef..ff0740da2ca6 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -46,7 +46,7 @@ SslSocket::SslSocket(Envoy::Ssl::ContextSharedPtr ctx, InitialState state, const Network::TransportSocketOptionsSharedPtr& transport_socket_options) : transport_socket_options_(transport_socket_options), ctx_(std::dynamic_pointer_cast(ctx)), - ssl_(ctx_->newSsl(transport_socket_options_.get())) { + ssl_(ctx_->newSsl(transport_socket_options_.get())), state_(SocketState::PreHandshake) { if (state == InitialState::Client) { SSL_set_connect_state(ssl_.get()); } else { @@ -94,9 +94,9 @@ SslSocket::ReadResult SslSocket::sslReadIntoSlice(Buffer::RawSlice& slice) { } Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { - if (!handshake_complete_) { + if (state_ != SocketState::HandShakeComplete && state_ != SocketState::ShutdownSent) { PostIoAction action = doHandshake(); - if (action == PostIoAction::Close || !handshake_complete_) { + if (action == PostIoAction::Close || state_ != SocketState::HandShakeComplete) { // end_stream is false because either a hard error occurred (action == Close) or // the handshake isn't complete, so a half-close cannot occur yet. return {action, 0, false}; @@ -157,10 +157,7 @@ Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { void SslSocket::onPrivateKeyMethodComplete() { ASSERT(isThreadSafe()); - ASSERT(!handshake_complete_); - ASSERT(async_handshake_in_progress_); - - async_handshake_in_progress_ = false; + ASSERT(state_ == SocketState::HandshakeInProgress); // Resume handshake. PostIoAction action = doHandshake(); @@ -171,11 +168,11 @@ void SslSocket::onPrivateKeyMethodComplete() { } PostIoAction SslSocket::doHandshake() { - ASSERT(!handshake_complete_); + ASSERT(state_ != SocketState::HandShakeComplete && state_ != SocketState::ShutdownSent); int rc = SSL_do_handshake(ssl_.get()); if (rc == 1) { ENVOY_CONN_LOG(debug, "handshake complete", callbacks_->connection()); - handshake_complete_ = true; + state_ = SocketState::HandShakeComplete; ctx_->logHandshake(ssl_.get()); callbacks_->raiseEvent(Network::ConnectionEvent::Connected); @@ -193,7 +190,7 @@ PostIoAction SslSocket::doHandshake() { return PostIoAction::KeepOpen; case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION: ENVOY_CONN_LOG(debug, "handshake continued asynchronously", callbacks_->connection()); - async_handshake_in_progress_ = true; + state_ = SocketState::HandshakeInProgress; return PostIoAction::KeepOpen; default: ENVOY_CONN_LOG(debug, "handshake error: {}", callbacks_->connection(), err); @@ -231,10 +228,10 @@ void SslSocket::drainErrorQueue() { } Network::IoResult SslSocket::doWrite(Buffer::Instance& write_buffer, bool end_stream) { - ASSERT(!shutdown_sent_ || write_buffer.length() == 0); - if (!handshake_complete_) { + ASSERT(state_ != SocketState::ShutdownSent || write_buffer.length() == 0); + if (state_ != SocketState::HandShakeComplete && state_ != SocketState::ShutdownSent) { PostIoAction action = doHandshake(); - if (action == PostIoAction::Close || !handshake_complete_) { + if (action == PostIoAction::Close || state_ != SocketState::HandShakeComplete) { return {action, 0, false}; } } @@ -287,15 +284,16 @@ Network::IoResult SslSocket::doWrite(Buffer::Instance& write_buffer, bool end_st return {PostIoAction::KeepOpen, total_bytes_written, false}; } -void SslSocket::onConnected() { ASSERT(!handshake_complete_); } +void SslSocket::onConnected() { ASSERT(state_ == SocketState::PreHandshake); } void SslSocket::shutdownSsl() { - ASSERT(handshake_complete_ || async_handshake_in_progress_); - if (!shutdown_sent_ && callbacks_->connection().state() != Network::Connection::State::Closed) { + ASSERT(state_ != SocketState::PreHandshake); + if (state_ != SocketState::ShutdownSent && + callbacks_->connection().state() != Network::Connection::State::Closed) { int rc = SSL_shutdown(ssl_.get()); ENVOY_CONN_LOG(debug, "SSL shutdown: rc={}", callbacks_->connection(), rc); drainErrorQueue(); - shutdown_sent_ = true; + state_ = SocketState::ShutdownSent; } } @@ -416,7 +414,7 @@ void SslSocket::closeSocket(Network::ConnectionEvent) { // Attempt to send a shutdown before closing the socket. It's possible this won't go out if // there is no room on the socket. We can extend the state machine to handle this at some point // if needed. - if (handshake_complete_ || async_handshake_in_progress_) { + if (state_ == SocketState::HandshakeInProgress || state_ == SocketState::HandShakeComplete) { shutdownSsl(); } } diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index 1a2e7478e54e..80a8e835a22e 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -39,6 +39,7 @@ struct SslSocketFactoryStats { }; enum class InitialState { Client, Server }; +enum class SocketState { PreHandshake, HandshakeInProgress, HandShakeComplete, ShutdownSent }; class SslSocket : public Network::TransportSocket, public Envoy::Ssl::ConnectionInfo, @@ -72,7 +73,7 @@ class SslSocket : public Network::TransportSocket, void setTransportSocketCallbacks(Network::TransportSocketCallbacks& callbacks) override; std::string protocol() const override; absl::string_view failureReason() const override; - bool canFlushClose() override { return handshake_complete_; } + bool canFlushClose() override { return state_ == SocketState::HandShakeComplete; } void closeSocket(Network::ConnectionEvent close_type) override; Network::IoResult doRead(Buffer::Instance& read_buffer) override; Network::IoResult doWrite(Buffer::Instance& write_buffer, bool end_stream) override; @@ -102,14 +103,12 @@ class SslSocket : public Network::TransportSocket, Network::TransportSocketCallbacks* callbacks_{}; ContextImplSharedPtr ctx_; bssl::UniquePtr ssl_; - bool handshake_complete_{}; - bool shutdown_sent_{}; uint64_t bytes_to_retry_{}; std::string failure_reason_; mutable std::string cached_sha_256_peer_certificate_digest_; mutable std::string cached_url_encoded_pem_encoded_peer_certificate_; mutable std::string cached_url_encoded_pem_encoded_peer_cert_chain_; - bool async_handshake_in_progress_{}; + SocketState state_; }; class ClientSslSocketFactory : public Network::TransportSocketFactory, From d28d44bc88d71f3fdbd6311af4658f1e4f10a45b Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 12 Aug 2019 19:50:08 +0300 Subject: [PATCH 54/56] tests: update one new TlsCertificateConfigImpl constructor. Signed-off-by: Ismo Puustinen --- test/common/secret/secret_manager_impl_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/secret/secret_manager_impl_test.cc b/test/common/secret/secret_manager_impl_test.cc index 41998b37e9c2..69e3051ce880 100644 --- a/test/common/secret/secret_manager_impl_test.cc +++ b/test/common/secret/secret_manager_impl_test.cc @@ -261,7 +261,7 @@ name: "abc.com" init_target_handle->initialize(init_watcher); secret_context.cluster_manager_.subscription_factory_.callbacks_->onConfigUpdate(secret_resources, "keycert-v1"); - Ssl::TlsCertificateConfigImpl tls_config(*secret_provider->secret(), *api_); + Ssl::TlsCertificateConfigImpl tls_config(*secret_provider->secret(), nullptr, *api_); EXPECT_EQ("DUMMY_INLINE_BYTES_FOR_CERT_CHAIN", tls_config.certificateChain()); EXPECT_EQ("DUMMY_INLINE_BYTES_FOR_PRIVATE_KEY", tls_config.privateKey()); EXPECT_EQ("DUMMY_PASSWORD", tls_config.password()); From b79e5329b1e2efe70d36f476fe75200270efdb2a Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Mon, 12 Aug 2019 21:51:13 +0300 Subject: [PATCH 55/56] tls: modernize a for loop. Signed-off-by: Ismo Puustinen --- source/extensions/transport_sockets/tls/context_impl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index a261b89eee7e..f4795c0db2b1 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -511,9 +511,9 @@ void ContextImpl::logHandshake(SSL* ssl) const { std::vector ContextImpl::getPrivateKeyMethodProviders() { std::vector providers; - for (uint i = 0; i < tls_contexts_.size(); i++) { + for (auto& tls_context : tls_contexts_) { Envoy::Ssl::PrivateKeyMethodProviderSharedPtr provider = - tls_contexts_[i].getPrivateKeyMethodProvider(); + tls_context.getPrivateKeyMethodProvider(); if (provider) { providers.push_back(provider); } From abf87584ea08d48a30abda05198ff6f84efaa6d4 Mon Sep 17 00:00:00 2001 From: Ismo Puustinen Date: Thu, 22 Aug 2019 14:47:14 +0300 Subject: [PATCH 56/56] tls: various variable name and comment cleanups. Signed-off-by: Ismo Puustinen --- include/envoy/ssl/private_key/private_key.h | 14 ++++++-------- include/envoy/ssl/private_key/private_key_config.h | 7 +++---- .../tls/private_key/private_key_manager_impl.cc | 9 ++++----- .../tls/private_key/private_key_manager_impl.h | 7 +++---- .../extensions/transport_sockets/tls/ssl_socket.cc | 14 +++++++------- .../extensions/transport_sockets/tls/ssl_socket.h | 4 ++-- .../tls/test_private_key_method_provider.h | 10 ++++------ test/mocks/ssl/mocks.h | 9 ++++----- 8 files changed, 33 insertions(+), 41 deletions(-) diff --git a/include/envoy/ssl/private_key/private_key.h b/include/envoy/ssl/private_key/private_key.h index 8934b24f908a..e972d608cd02 100644 --- a/include/envoy/ssl/private_key/private_key.h +++ b/include/envoy/ssl/private_key/private_key.h @@ -70,17 +70,15 @@ class PrivateKeyMethodManager { /** * Finds and returns a private key operations provider for BoringSSL. * - * @param message a protobuf message object containing a - * PrivateKeyProvider message. - * @param private_key_method_provider_context context that provides components for creating and - * initializing connections for keyless TLS etc. + * @param config a protobuf message object containing a PrivateKeyProvider message. + * @param factory_context context that provides components for creating and + * initializing connections using asynchronous private key operations. * @return PrivateKeyMethodProvider the private key operations provider, or nullptr if * no provider can be used with the context configuration. */ - virtual PrivateKeyMethodProviderSharedPtr - createPrivateKeyMethodProvider(const envoy::api::v2::auth::PrivateKeyProvider& message, - Envoy::Server::Configuration::TransportSocketFactoryContext& - private_key_method_provider_context) PURE; + virtual PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProvider( + const envoy::api::v2::auth::PrivateKeyProvider& config, + Envoy::Server::Configuration::TransportSocketFactoryContext& factory_context) PURE; }; } // namespace Ssl diff --git a/include/envoy/ssl/private_key/private_key_config.h b/include/envoy/ssl/private_key/private_key_config.h index fcf670491ce3..8a5da737cac4 100644 --- a/include/envoy/ssl/private_key/private_key_config.h +++ b/include/envoy/ssl/private_key/private_key_config.h @@ -12,10 +12,9 @@ namespace Ssl { class PrivateKeyMethodProviderInstanceFactory { public: virtual ~PrivateKeyMethodProviderInstanceFactory() = default; - virtual PrivateKeyMethodProviderSharedPtr - createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyProvider& message, - Server::Configuration::TransportSocketFactoryContext& - private_key_method_provider_context) PURE; + virtual PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProviderInstance( + const envoy::api::v2::auth::PrivateKeyProvider& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) PURE; virtual std::string name() const PURE; }; diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc index b88724723a35..817b9d362616 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.cc @@ -9,17 +9,16 @@ namespace Tls { Envoy::Ssl::PrivateKeyMethodProviderSharedPtr PrivateKeyMethodManagerImpl::createPrivateKeyMethodProvider( - const envoy::api::v2::auth::PrivateKeyProvider& message, - Server::Configuration::TransportSocketFactoryContext& private_key_method_provider_context) { + const envoy::api::v2::auth::PrivateKeyProvider& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) { Ssl::PrivateKeyMethodProviderInstanceFactory* factory = Registry::FactoryRegistry::getFactory( - message.provider_name()); + config.provider_name()); // Create a new provider instance with the configuration. if (factory) { - return factory->createPrivateKeyMethodProviderInstance(message, - private_key_method_provider_context); + return factory->createPrivateKeyMethodProviderInstance(config, factory_context); } return nullptr; diff --git a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h index d04ce12ae9ff..1ae42d1916ec 100644 --- a/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h +++ b/source/extensions/transport_sockets/tls/private_key/private_key_manager_impl.h @@ -12,10 +12,9 @@ namespace Tls { class PrivateKeyMethodManagerImpl : public virtual Ssl::PrivateKeyMethodManager { public: // Ssl::PrivateKeyMethodManager - Ssl::PrivateKeyMethodProviderSharedPtr - createPrivateKeyMethodProvider(const envoy::api::v2::auth::PrivateKeyProvider& message, - Server::Configuration::TransportSocketFactoryContext& - private_key_method_provider_context) override; + Ssl::PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProvider( + const envoy::api::v2::auth::PrivateKeyProvider& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) override; }; } // namespace Tls diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index ff0740da2ca6..c079bb989fdc 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -94,9 +94,9 @@ SslSocket::ReadResult SslSocket::sslReadIntoSlice(Buffer::RawSlice& slice) { } Network::IoResult SslSocket::doRead(Buffer::Instance& read_buffer) { - if (state_ != SocketState::HandShakeComplete && state_ != SocketState::ShutdownSent) { + if (state_ != SocketState::HandshakeComplete && state_ != SocketState::ShutdownSent) { PostIoAction action = doHandshake(); - if (action == PostIoAction::Close || state_ != SocketState::HandShakeComplete) { + if (action == PostIoAction::Close || state_ != SocketState::HandshakeComplete) { // end_stream is false because either a hard error occurred (action == Close) or // the handshake isn't complete, so a half-close cannot occur yet. return {action, 0, false}; @@ -168,11 +168,11 @@ void SslSocket::onPrivateKeyMethodComplete() { } PostIoAction SslSocket::doHandshake() { - ASSERT(state_ != SocketState::HandShakeComplete && state_ != SocketState::ShutdownSent); + ASSERT(state_ != SocketState::HandshakeComplete && state_ != SocketState::ShutdownSent); int rc = SSL_do_handshake(ssl_.get()); if (rc == 1) { ENVOY_CONN_LOG(debug, "handshake complete", callbacks_->connection()); - state_ = SocketState::HandShakeComplete; + state_ = SocketState::HandshakeComplete; ctx_->logHandshake(ssl_.get()); callbacks_->raiseEvent(Network::ConnectionEvent::Connected); @@ -229,9 +229,9 @@ void SslSocket::drainErrorQueue() { Network::IoResult SslSocket::doWrite(Buffer::Instance& write_buffer, bool end_stream) { ASSERT(state_ != SocketState::ShutdownSent || write_buffer.length() == 0); - if (state_ != SocketState::HandShakeComplete && state_ != SocketState::ShutdownSent) { + if (state_ != SocketState::HandshakeComplete && state_ != SocketState::ShutdownSent) { PostIoAction action = doHandshake(); - if (action == PostIoAction::Close || state_ != SocketState::HandShakeComplete) { + if (action == PostIoAction::Close || state_ != SocketState::HandshakeComplete) { return {action, 0, false}; } } @@ -414,7 +414,7 @@ void SslSocket::closeSocket(Network::ConnectionEvent) { // Attempt to send a shutdown before closing the socket. It's possible this won't go out if // there is no room on the socket. We can extend the state machine to handle this at some point // if needed. - if (state_ == SocketState::HandshakeInProgress || state_ == SocketState::HandShakeComplete) { + if (state_ == SocketState::HandshakeInProgress || state_ == SocketState::HandshakeComplete) { shutdownSsl(); } } diff --git a/source/extensions/transport_sockets/tls/ssl_socket.h b/source/extensions/transport_sockets/tls/ssl_socket.h index 80a8e835a22e..8e0eb6601058 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.h +++ b/source/extensions/transport_sockets/tls/ssl_socket.h @@ -39,7 +39,7 @@ struct SslSocketFactoryStats { }; enum class InitialState { Client, Server }; -enum class SocketState { PreHandshake, HandshakeInProgress, HandShakeComplete, ShutdownSent }; +enum class SocketState { PreHandshake, HandshakeInProgress, HandshakeComplete, ShutdownSent }; class SslSocket : public Network::TransportSocket, public Envoy::Ssl::ConnectionInfo, @@ -73,7 +73,7 @@ class SslSocket : public Network::TransportSocket, void setTransportSocketCallbacks(Network::TransportSocketCallbacks& callbacks) override; std::string protocol() const override; absl::string_view failureReason() const override; - bool canFlushClose() override { return state_ == SocketState::HandShakeComplete; } + bool canFlushClose() override { return state_ == SocketState::HandshakeComplete; } void closeSocket(Network::ConnectionEvent close_type) override; Network::IoResult doRead(Buffer::Instance& read_buffer) override; Network::IoResult doWrite(Buffer::Instance& write_buffer, bool end_stream) override; diff --git a/test/extensions/transport_sockets/tls/test_private_key_method_provider.h b/test/extensions/transport_sockets/tls/test_private_key_method_provider.h index 6d0711d1307c..6aadf9301077 100644 --- a/test/extensions/transport_sockets/tls/test_private_key_method_provider.h +++ b/test/extensions/transport_sockets/tls/test_private_key_method_provider.h @@ -82,12 +82,10 @@ class TestPrivateKeyMethodProvider : public virtual Ssl::PrivateKeyMethodProvide class TestPrivateKeyMethodFactory : public Ssl::PrivateKeyMethodProviderInstanceFactory { public: // Ssl::PrivateKeyMethodProviderInstanceFactory - Ssl::PrivateKeyMethodProviderSharedPtr - createPrivateKeyMethodProviderInstance(const envoy::api::v2::auth::PrivateKeyProvider& message, - Server::Configuration::TransportSocketFactoryContext& - private_key_method_provider_context) override { - return std::make_shared(message.config(), - private_key_method_provider_context); + Ssl::PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProviderInstance( + const envoy::api::v2::auth::PrivateKeyProvider& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) override { + return std::make_shared(config.config(), factory_context); } std::string name() const override { return std::string("test"); }; diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 1c7b008a11af..957cbf05c87c 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -114,11 +114,10 @@ class MockPrivateKeyMethodManager : public PrivateKeyMethodManager { MockPrivateKeyMethodManager(); ~MockPrivateKeyMethodManager() override; - MOCK_METHOD2( - createPrivateKeyMethodProvider, - PrivateKeyMethodProviderSharedPtr(const envoy::api::v2::auth::PrivateKeyProvider& message, - Envoy::Server::Configuration::TransportSocketFactoryContext& - private_key_method_provider_context)); + MOCK_METHOD2(createPrivateKeyMethodProvider, + PrivateKeyMethodProviderSharedPtr( + const envoy::api::v2::auth::PrivateKeyProvider& config, + Envoy::Server::Configuration::TransportSocketFactoryContext& factory_context)); }; class MockPrivateKeyMethodProvider : public PrivateKeyMethodProvider {