--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef BOTAN_TLS_H
+#define BOTAN_TLS_H
+
+#ifdef WITH_BOTAN
+
+namespace isc {
+namespace asiolink {
+
+} // namespace asiolink
+} // namespace isc
+
+#endif // WITH_BOTAN
+
+#endif // BOTAN_TLS_H
--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef CRYPTO_TLS_H
+#define CRYPTO_TLS_H
+
+#include <cryptolink/cryptolink.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+namespace isc {
+namespace asiolink {
+
+/// @file crypto_tls.h Common TLS API.
+
+/// @brief Client and server roles.
+enum TlsRole { CLIENT, SERVER };
+
+/// @brief TLS context base class.
+class TlsContextBase : private boost::noncopyable {
+public:
+ /// @brief Destructor.
+ virtual ~TlsContextBase() { }
+
+ /// @brief Create a fresh context.
+ ///
+ /// @param role The TLS role client or server.
+ explicit TlsContextBase(TlsRole role) : role_(role) { }
+
+ /// @brief Returns the role.
+ TlsRole getRole() const {
+ return (role_);
+ }
+
+ /// @note No need for a role set method.
+
+ /// @brief Set the peer certificate requirement mode.
+ ///
+ /// @param cert_required True if peer certificates are required,
+ /// false if they are optional.
+ virtual void setCertRequired(bool cert_required) = 0;
+
+ /// @brief Get the peer certificate requirement mode.
+ ///
+ /// @return True if peer certificates are required, false if they
+ /// are optional.
+ virtual bool getCertRequired() const = 0;
+
+ /// @brief Load the trust anchor aka certificate authority.
+ ///
+ /// @param ca_file The certificate file name.
+ /// @throw isc::cryptolink::LibraryError on various errors as
+ /// file not found, bad format, etc.
+ virtual void loadCaFile(const std::string& ca_file) = 0;
+
+ /// @brief Load the trust anchor aka certificate authority.
+ ///
+ /// @param ca_path The certificate directory name.
+ /// @throw isc::cryptolink::LibraryError on various errors as
+ /// file not found, bad format, etc.
+ virtual void loadCaPath(const std::string& ca_path) = 0;
+
+ /// @brief Load the certificate file.
+ ///
+ /// @param cert_file The certificate file name.
+ /// @throw isc::cryptolink::LibraryError on various errors as
+ /// file not found, bad format, etc.
+ virtual void loadCertFile(const std::string& cert_file) = 0;
+
+ /// @brief Load the private key file name.
+ ///
+ /// @param key_file The private key file name.
+ /// @throw isc::cryptolink::LibraryError on various errors as
+ /// file not found, bad format, etc.
+ virtual void loadKeyFile(const std::string& key_file) = 0;
+
+public:
+ /// @brief The role i.e. client or server.
+ TlsRole role_;
+};
+
+} // namespace asiolink
+} // namespace isc
+
+#endif // CRYPTO_TLS_H
--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#ifdef WITH_OPENSSL
+
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/crypto_tls.h>
+#include <asiolink/openssl_tls.h>
+
+#include <sys/stat.h>
+
+#include <openssl/opensslv.h>
+
+using namespace boost::asio;
+using namespace boost::asio::ssl;
+using namespace boost::system;
+using namespace isc::cryptolink;
+
+namespace isc {
+namespace asiolink {
+
+TlsContext::TlsContext(TlsRole role)
+ : TlsContextBase(role), cert_required_(true),
+ context_(context::method::tls) {
+ // Not leave the verify mode to OpenSSL default.
+ setCertRequired(true);
+}
+
+boost::asio::ssl::context&
+TlsContext::getContext() {
+ return (context_);
+}
+
+::SSL_CTX*
+TlsContext::getNativeContext() {
+ return (context_.native_handle());
+}
+
+void
+TlsContext::setCertRequired(bool cert_required) {
+ cert_required_ = cert_required;
+ error_code ec;
+ int mode = verify_peer | verify_fail_if_no_peer_cert;
+ if (!cert_required_) {
+ mode = verify_none;
+ }
+ context_.set_verify_mode(mode, ec);
+ if (ec) {
+ isc_throw(LibraryError, ec.message());
+ }
+}
+
+bool
+TlsContext::getCertRequired() const {
+ return (cert_required_);
+}
+
+void
+TlsContext::loadCaFile(const std::string& ca_file) {
+ error_code ec;
+ context_.load_verify_file(ca_file, ec);
+ if (ec) {
+ isc_throw(LibraryError, ec.message());
+ }
+}
+
+void
+TlsContext::loadCaPath(const std::string& ca_path) {
+ error_code ec;
+ context_.add_verify_path(ca_path, ec);
+ if (ec) {
+ isc_throw(LibraryError, ec.message());
+ }
+}
+
+void
+TlsContext::loadCertFile(const std::string& cert_file) {
+ error_code ec;
+ context_.use_certificate_chain_file(cert_file, ec);
+ if (ec) {
+ isc_throw(LibraryError, ec.message());
+ }
+}
+
+void
+TlsContext::loadKeyFile(const std::string& key_file) {
+ error_code ec;
+ context_.use_private_key_file(key_file, context::file_format::pem, ec);
+ if (ec) {
+ isc_throw(LibraryError, ec.message());
+ }
+}
+
+namespace { // anonymous namespace
+
+// C++17 has this function but Kea is still C++11 so provide it.
+bool
+isDir(const std::string& name) {
+ struct stat stats;
+ if (::stat(name.c_str(), &stats) < 0) {
+ return (false);
+ }
+ return ((stats.st_mode & S_IFMT) == S_IFDIR);
+}
+
+} // end of namespace
+
+void
+TlsContext::configure(TlsContextPtr& context,
+ TlsRole role,
+ const std::string& ca_file,
+ const std::string& cert_file,
+ const std::string& key_file,
+ bool cert_required) {
+ try {
+ context.reset(new TlsContext(role));
+ if (isDir(ca_file)) {
+ context->loadCaPath(ca_file);
+ } else {
+ context->loadCaFile(ca_file);
+ }
+ context->loadCertFile(cert_file);
+ context->loadKeyFile(key_file);
+ context->setCertRequired(cert_required);
+ } catch (...) {
+ context.reset();
+ throw;
+ }
+}
+
+} // namespace asiolink
+} // namespace isc
+
+#endif // WITH_OPENSSL
--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef OPENSSL_TLS_H
+#define OPENSSL_TLS_H
+
+#ifdef WITH_OPENSSL
+
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_asio_socket.h>
+#include <asiolink/io_service.h>
+
+#include <boost/asio/ssl.hpp>
+
+namespace isc {
+namespace asiolink {
+
+/// @brief Forward declaration of OpenSSL TLS context.
+class TlsContext;
+
+/// @brief The type of shared pointers to TlsContext objects.
+///
+/// @note Not clear we need shared pointers but they covers more use cases...
+typedef boost::shared_ptr<TlsContext> TlsContextPtr;
+
+/// @brief OpenSSL TLS context.
+class TlsContext : public TlsContextBase {
+public:
+
+ /// @brief Destructor.
+ virtual ~TlsContext() { }
+
+ /// @brief Create a fresh context.
+ ///
+ /// @param role The TLS role client or server.
+ explicit TlsContext(TlsRole role);
+
+ /// @brief Return a reference to the underlying context.
+ boost::asio::ssl::context& getContext();
+
+ /// @brief Return the pointer to the SSL_CTX object.
+ ///
+ /// Currently used only for tests. Please note that since OpenSSL 1.1
+ /// The SSL_CTX type is not fully publicly defined.
+ ::SSL_CTX* getNativeContext();
+
+ /// @brief Set the peer certificate requirement mode.
+ ///
+ /// @param cert_required True if peer certificates are required,
+ /// false if they are optional.
+ virtual void setCertRequired(bool cert_required);
+
+ /// @brief Get the peer certificate requirement mode.
+ ///
+ /// @return True if peer certificates are required, false if they
+ /// are optional.
+ virtual bool getCertRequired() const;
+
+ /// @brief Load the trust anchor aka certificate authority.
+ ///
+ /// @param ca_file The certificate file name.
+ virtual void loadCaFile(const std::string& ca_file);
+
+ /// @brief Load the trust anchor aka certificate authority.
+ ///
+ /// @param ca_path The certificate directory name.
+ virtual void loadCaPath(const std::string& ca_path);
+
+ /// @brief Load the certificate file.
+ ///
+ /// @param cert_file The certificate file name.
+ virtual void loadCertFile(const std::string& cert_file);
+
+ /// @brief Load the private key file name.
+ ///
+ /// @param key_file The private key file name.
+ virtual void loadKeyFile(const std::string& key_file);
+
+ /// @brief Configure.
+ ///
+ /// @param context The TLS context to configure.
+ /// @param role The TLS role client or server.
+ /// @param ca_file The certificate file or directory name.
+ /// @param cert_file The certificate file name.
+ /// @param key_file The private key file name.
+ /// @param cert_required True if peer certificates are required,
+ /// false if they are optional.
+ static void configure(TlsContextPtr& context,
+ TlsRole role,
+ const std::string& ca_file,
+ const std::string& cert_file,
+ const std::string& key_file,
+ bool cert_required);
+
+protected:
+ /// @brief Cached cert_required value.
+ bool cert_required_;
+
+ /// @brief Boost ASIO SSL object.
+ boost::asio::ssl::context context_;
+};
+
+/// @brief The type of underlying TLS streams.
+typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> TlsStreamImpl;
+
+/// @brief The type of X509 certificates.
+typedef ::X509 TlsCertificate;
+
+/// @brief OpenSSL TLS stream.
+///
+/// @param callback The callback.
+template <typename Callback>
+class TlsStream : public TlsStreamImpl {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param service I/O Service object used to manage the stream.
+ /// @param context Pointer to the TLS context.
+ /// @note The caller must not provide a null pointer to the TLS context.
+ TlsStream(IOService& service, TlsContextPtr context)
+ : TlsStreamImpl(service.get_io_service(), context->getContext()),
+ role_(context->getRole()) {
+ }
+
+ /// @brief Destructor.
+ virtual ~TlsStream() { }
+
+ /// @brief Returns the role.
+ TlsRole getRole() const {
+ return (role_);
+ }
+
+ /// @brief TLS Handshake.
+ ///
+ /// @param callback Callback object.
+ virtual void handshake(Callback& callback) {
+ using namespace boost::asio::ssl;
+ if (role_ == SERVER) {
+ async_handshake(stream_base::server, callback);
+ } else {
+ async_handshake(stream_base::client, callback);
+ }
+ }
+
+ /// @brief TLS shutdown.
+ ///
+ /// @param callback Callback object.
+ virtual void shutdown(Callback& callback) {
+ async_shutdown(callback);
+ }
+
+ /// @brief Clear the SSL object.
+ virtual void clear() {
+ static_cast<void>(::SSL_clear(this->native_handle()));
+ }
+
+ /// @brief Return the peer certificate.
+ ///
+ /// @note The native_handle() method is used so it can't be made const.
+ /// @note Do not forget to free it when no longer used.
+ virtual TlsCertificate* getPeerCert() {
+ return (::SSL_get_peer_certificate(this->native_handle()));
+ }
+
+ /// @brief The role i.e. client or server.
+ TlsRole role_;
+
+ /// @break Return the commonName part of the subjectName of
+ /// the peer certificate.
+ ///
+ /// First commonName when there are more than one, in UTF-8.
+ ///
+ /// @return The commonName part of the subjectName or the empty string.
+ std::string getSubject() {
+ TlsCertificate* cert = getPeerCert();
+ if (!cert) {
+ return ("");
+ }
+ ::X509_NAME *name = ::X509_get_subject_name(cert);
+ int loc = ::X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+ ::X509_NAME_ENTRY* ne = ::X509_NAME_get_entry(name, loc);
+ if (!ne) {
+ ::X509_free(cert);
+ return ("");
+ }
+ unsigned char* buf = 0;
+ int len = ::ASN1_STRING_to_UTF8(&buf, ::X509_NAME_ENTRY_get_data(ne));
+ if (len < 0) {
+ ::X509_free(cert);
+ return ("");
+ }
+ std::string ret(reinterpret_cast<char*>(buf), static_cast<size_t>(len));
+ ::OPENSSL_free(buf);
+ ::X509_free(cert);
+ return (ret);
+ }
+
+ /// @break Return the commonName part of the issuerName of
+ /// the peer certificate.
+ ///
+ /// First commonName when there are more than one, in UTF-8.
+ ///
+ /// @return The commonName part of the issuerName or the empty string.
+ std::string getIssuer() {
+ TlsCertificate* cert = getPeerCert();
+ if (!cert) {
+ return ("");
+ }
+ ::X509_NAME *name = ::X509_get_issuer_name(cert);
+ int loc = ::X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+ ::X509_NAME_ENTRY* ne = ::X509_NAME_get_entry(name, loc);
+ if (!ne) {
+ ::X509_free(cert);
+ return ("");
+ }
+ unsigned char* buf = 0;
+ int len = ::ASN1_STRING_to_UTF8(&buf, ::X509_NAME_ENTRY_get_data(ne));
+ if (len < 0) {
+ ::X509_free(cert);
+ return ("");
+ }
+ std::string ret(reinterpret_cast<char*>(buf), static_cast<size_t>(len));
+ ::OPENSSL_free(buf);
+ ::X509_free(cert);
+ return (ret);
+ }
+};
+
+
+} // namespace asiolink
+} // namespace isc
+
+#endif // WITH_OPENSSL
+
+#endif // OPENSSL_TLS_H
--- /dev/null
+// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tls_acceptor.h>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+#include <functional>
+#include <list>
+#include <netinet/in.h>
+#include <string>
+
+using namespace isc::asiolink;
+using namespace boost::asio;
+namespace ph = std::placeholders;
+
+namespace {
+
+/// @brief Local server address used for testing.
+const char SERVER_ADDRESS[] = "127.0.0.1";
+
+/// @brief Local server port used for testing.
+const unsigned short SERVER_PORT = 18123;
+
+/// @brief Test timeout in ms.
+const long TEST_TIMEOUT = 10000;
+
+/// @brief Simple class representing TCP socket callback.
+class SocketCallback {
+public:
+
+ /// @brief Implements callback for the asynchronous operation on the socket.
+ ///
+ /// This callback merely checks if error has occurred and reports this
+ /// error. It does nothing in case of success.
+ ///
+ /// @param ec Error code.
+ /// @param length Length of received data.
+ void operator()(boost::system::error_code ec, size_t length = 0) {
+ if (ec) {
+ ADD_FAILURE() << "error occurred for a socket: " << ec.message();
+ }
+ }
+
+};
+
+/// @brief Entity which can connect to the TLS server endpoint and close the
+/// connection.
+class TLSClient : public boost::noncopyable {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// This constructor creates new socket instance. It doesn't connect. Call
+ /// connect() to connect to the server.
+ ///
+ /// @param io_service IO service to be stopped on error.
+ explicit TLSClient(IOService& io_service)
+ : io_service_(io_service.get_io_service()), socket_(io_service_) {
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Closes the underlying socket if it is open.
+ ~TLSClient() {
+ close();
+ }
+
+ /// @brief Connect to the test server address and port.
+ ///
+ /// This method asynchronously connects to the server endpoint and uses the
+ /// connectHandler as a callback function.
+ void connect() {
+ ip::tcp::endpoint
+ endpoint(ip::address::from_string(SERVER_ADDRESS),
+ SERVER_PORT);
+ socket_.async_connect(endpoint,
+ std::bind(&TLSClient::connectHandler, this,
+ ph::_1));
+ }
+
+ /// @brief Callback function for connect().
+ ///
+ /// This function stops the IO service upon error.
+ ///
+ /// @param ec Error code.
+ void connectHandler(const boost::system::error_code& ec) {
+ if (ec) {
+ // One would expect that async_connect wouldn't return EINPROGRESS
+ // error code, but simply wait for the connection to get
+ // established before the handler is invoked. It turns out, however,
+ // that on some OSes the connect handler may receive this error code
+ // which doesn't necessarily indicate a problem. Making an attempt
+ // to write and read from this socket will typically succeed. So,
+ // we ignore this error.
+ if (ec.value() != error::in_progress) {
+ ADD_FAILURE() << "error occurred while connecting: "
+ << ec.message();
+ io_service_.stop();
+ }
+ }
+ }
+
+ /// @brief Close connection.
+ void close() {
+ socket_.close();
+ }
+
+private:
+
+ /// @brief Holds reference to the IO service.
+ io_service& io_service_;
+
+ /// @brief A socket used for the connection.
+ ip::tcp::socket socket_;
+
+};
+
+/// @brief Pointer to the TLSClient.
+typedef boost::shared_ptr<TLSClient> TLSClientPtr;
+
+/// @brief A signature of the function implementing callback for the
+/// TLSAcceptor.
+typedef std::function<void(const boost::system::error_code&)> TLSAcceptorCallback;
+
+/// @brief TLSAcceptor using TLSAcceptorCallback.
+typedef TLSAcceptor<TLSAcceptorCallback> TestTLSAcceptor;
+
+/// @brief Implements asynchronous TLS acceptor service.
+///
+/// It creates a new socket into which connection is accepted. The socket
+/// is retained until class instance exists.
+class Acceptor {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param io_service IO service.
+ /// @param context Pointer to TLS context.
+ /// @param acceptor Reference to the TLS acceptor on which asyncAccept
+ /// will be called.
+ /// @param callback Callback function for the asyncAccept.
+ explicit Acceptor(IOService& io_service,
+ TlsContextPtr context,
+ TestTLSAcceptor& acceptor,
+ const TLSAcceptorCallback& callback)
+ : socket_(io_service, context), acceptor_(acceptor),
+ callback_(callback) {
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Closes socket.
+ ~Acceptor() {
+ socket_.close();
+ }
+
+ /// @brief Asynchronous accept new connection.
+ void accept() {
+ acceptor_.asyncAccept(socket_, callback_);
+ }
+
+ /// @brief Close connection.
+ void close() {
+ socket_.close();
+ }
+
+private:
+
+ /// @brief Socket into which connection is accepted.
+ TLSSocket<SocketCallback> socket_;
+
+ /// @brief Reference to the TLSAcceptor on which asyncAccept is called.
+ TestTLSAcceptor& acceptor_;
+
+ /// @brief Instance of the callback used for asyncAccept.
+ TLSAcceptorCallback callback_;
+
+};
+
+/// @brief Pointer to the Acceptor object.
+typedef boost::shared_ptr<Acceptor> AcceptorPtr;
+
+/// @brief Test fixture class for TLSAcceptor.
+///
+/// This class provides means for creating new TLS connections, i.e. simulates
+/// clients connecting to the servers via TLSAcceptor. It is possible to create
+/// multiple simultaneous connections, which are retained by the test fixture
+/// class and closed cleanly when the test fixture is destroyed.
+class TLSAcceptorTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Besides initializing class members it also sets the test timer to guard
+ /// against endlessly running IO service when TLS connections are
+ /// unsuccessful.
+ TLSAcceptorTest()
+ : io_service_(), acceptor_(io_service_),
+ asio_endpoint_(ip::address::from_string(SERVER_ADDRESS),
+ SERVER_PORT),
+ endpoint_(asio_endpoint_), test_timer_(io_service_), connections_(),
+ clients_(), connections_num_(0), aborted_connections_num_(0),
+ max_connections_(1) {
+ test_timer_.setup(std::bind(&TLSAcceptorTest::timeoutHandler, this),
+ TEST_TIMEOUT, IntervalTimer::ONE_SHOT);
+ }
+
+ /// @brief Destructor.
+ virtual ~TLSAcceptorTest() {
+ }
+
+ /// @brief Specifies how many new connections are expected before the IO
+ /// service is stopped.
+ ///
+ /// @param max_connections Connections limit.
+ void setMaxConnections(const unsigned int max_connections) {
+ max_connections_ = max_connections;
+ }
+
+ /// @brief Create ASIO endpoint from the provided endpoint by retaining the
+ /// IP address and modifying the port.
+ ///
+ /// This convenience method is useful to create new endpoint from the
+ /// existing endpoint to test reusing IP address for multiple acceptors.
+ /// The returned endpoint has the same IP address but different port.
+ ///
+ /// @param endpoint Source endpoint.
+ ///
+ /// @return New endpoint with the port number increased by 1.
+ ip::tcp::endpoint
+ createSiblingEndpoint(const ip::tcp::endpoint& endpoint) const {
+ ip::tcp::endpoint endpoint_copy(endpoint);
+ endpoint_copy.port(endpoint.port() + 1);
+ return (endpoint_copy);
+ }
+
+ /// @brief Opens TLS acceptor and sets 'reuse address' option.
+ void acceptorOpen() {
+ acceptor_.open(endpoint_);
+ acceptor_.setOption(TestTLSAcceptor::ReuseAddress(true));
+ }
+
+ /// @brief Starts accepting TLS connections.
+ ///
+ /// This method creates new Acceptor instance and calls accept() to start
+ /// accepting new connections. The instance of the Acceptor object is
+ /// retained in the connections_ list.
+ void accept() {
+ TLSAcceptorCallback cb = std::bind(&TLSAcceptorTest::acceptHandler,
+ this, ph::_1);
+ TlsContextPtr ctx(new TlsContext(SERVER));
+ AcceptorPtr conn(new Acceptor(io_service_, ctx, acceptor_, cb));
+ connections_.push_back(conn);
+ connections_.back()->accept();
+ }
+
+ /// @brief Connect to the endpoint.
+ ///
+ /// This method creates TLSClient instance and retains it in the clients_
+ /// list.
+ void connect() {
+ TLSClientPtr client(new TLSClient(io_service_));
+ clients_.push_back(client);
+ clients_.back()->connect();
+ }
+
+ /// @brief Callback function for asynchronous accept calls.
+ ///
+ /// It stops the IO service upon error or when the number of accepted
+ /// connections reaches the max_connections_ value. Otherwise it calls
+ /// accept() to start accepting next connections.
+ ///
+ /// @param ec Error code.
+ void acceptHandler(const boost::system::error_code& ec) {
+ if (ec) {
+ if (ec.value() != error::operation_aborted) {
+ ADD_FAILURE() << "error occurred while accepting connection: "
+ << ec.message();
+ } else {
+ ++aborted_connections_num_;
+ }
+ io_service_.stop();
+ }
+
+ // We have reached the maximum number of connections - end the test.
+ if (++connections_num_ >= max_connections_) {
+ io_service_.stop();
+ return;
+ }
+
+ accept();
+ }
+
+ /// @brief Callback function invoke upon test timeout.
+ ///
+ /// It stops the IO service and reports test timeout.
+ void timeoutHandler() {
+ ADD_FAILURE() << "Timeout occurred while running the test!";
+ io_service_.stop();
+ }
+
+ /// @brief IO service.
+ IOService io_service_;
+
+ /// @brief TLSAcceptor under test.
+ TestTLSAcceptor acceptor_;
+
+ /// @brief Server endpoint.
+ ip::tcp::endpoint asio_endpoint_;
+
+ /// @brief asiolink server endpoint (uses asio_endpoint_).
+ TCPEndpoint endpoint_;
+
+ /// @brief Asynchronous timer service to detect timeouts.
+ IntervalTimer test_timer_;
+
+ /// @brief List of connections on the server side.
+ std::list<AcceptorPtr> connections_;
+
+ /// @brief List of client connections.
+ std::list<TLSClientPtr> clients_;
+
+ /// @brief Current number of established connections.
+ unsigned int connections_num_;
+
+ /// @brief Current number of aborted connections.
+ unsigned int aborted_connections_num_;
+
+ /// @brief Connections limit.
+ unsigned int max_connections_;
+};
+
+// Test TLSAcceptor::asyncAccept.
+TEST_F(TLSAcceptorTest, asyncAccept) {
+ // Establish up to 10 connections.
+ setMaxConnections(10);
+
+ // Initialize acceptor.
+ acceptorOpen();
+ acceptor_.bind(endpoint_);
+ acceptor_.listen();
+
+ // Start accepting new connections.
+ accept();
+
+ // Create 10 new TLS connections (client side).
+ for (unsigned int i = 0; i < 10; ++i) {
+ connect();
+ }
+
+ // Run the IO service until we have accepted 10 connections, an error
+ // or test timeout occurred.
+ io_service_.run();
+
+ // Make sure that all accepted connections have been recorded.
+ EXPECT_EQ(10, connections_num_);
+ EXPECT_EQ(10, connections_.size());
+}
+
+// Check that it is possible to set SO_REUSEADDR flag for the TLSAcceptor.
+TEST_F(TLSAcceptorTest, reuseAddress) {
+ // We need at least two acceptors using common address. Let's create the
+ // second endpoint which has the same address but different port.
+ ip::tcp::endpoint asio_endpoint2(createSiblingEndpoint(asio_endpoint_));
+ TCPEndpoint endpoint2(asio_endpoint2);
+
+ // Create and open two acceptors.
+ TestTLSAcceptor acceptor1(io_service_);
+ TestTLSAcceptor acceptor2(io_service_);
+ ASSERT_NO_THROW(acceptor1.open(endpoint_));
+ ASSERT_NO_THROW(acceptor2.open(endpoint2));
+
+ // Set SO_REUSEADDR socket option so as acceptors can bind to the
+ /// same address.
+ ASSERT_NO_THROW(
+ acceptor1.setOption(TestTLSAcceptor::ReuseAddress(true))
+ );
+ ASSERT_NO_THROW(
+ acceptor2.setOption(TestTLSAcceptor::ReuseAddress(true))
+ );
+ ASSERT_NO_THROW(acceptor1.bind(endpoint_));
+ ASSERT_NO_THROW(acceptor2.bind(endpoint2));
+
+ // Create third acceptor, but don't set the SO_REUSEADDR. It should
+ // refuse to bind.
+ TCPEndpoint endpoint3(createSiblingEndpoint(asio_endpoint2));
+ TestTLSAcceptor acceptor3(io_service_);
+ ASSERT_NO_THROW(acceptor3.open(endpoint3));
+ EXPECT_THROW(acceptor3.bind(endpoint_), boost::system::system_error);
+}
+
+// Test that TLSAcceptor::getProtocol returns IPPROTO_TCP.
+TEST_F(TLSAcceptorTest, getProtocol) {
+ EXPECT_EQ(IPPROTO_TCP, acceptor_.getProtocol());
+}
+
+// Test that TLSAcceptor::getNative returns valid socket descriptor.
+TEST_F(TLSAcceptorTest, getNative) {
+ // Initially the descriptor should be invalid (negative).
+ ASSERT_LT(acceptor_.getNative(), 0);
+ // Now open the socket and make sure the returned descriptor is now valid.
+ ASSERT_NO_THROW(acceptorOpen());
+ EXPECT_GE(acceptor_.getNative(), 0);
+}
+
+// macOS 10.12.3 has a bug which causes the connections to not enter
+// the TIME-WAIT state and they never get closed.
+#if !defined (OS_OSX)
+
+// Test that TLSAcceptor::close works properly.
+TEST_F(TLSAcceptorTest, close) {
+ // Initialize acceptor.
+ acceptorOpen();
+ acceptor_.bind(endpoint_);
+ acceptor_.listen();
+
+ // Start accepting new connections.
+ accept();
+
+ // Create 10 new TLS connections (client side).
+ for (unsigned int i = 0; i < 10; ++i) {
+ connect();
+ }
+
+ // Close the acceptor before connections are accepted.
+ acceptor_.close();
+
+ // Run the IO service.
+ io_service_.run();
+
+ // The connections should have been aborted.
+ EXPECT_EQ(1, connections_num_);
+ EXPECT_EQ(1, aborted_connections_num_);
+ EXPECT_EQ(1, connections_.size());
+}
+
+#endif
+
+}
--- /dev/null
+// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// \brief Test of TCPSocket
+///
+/// Tests the functionality of a TCPSocket by working through an open-send-
+/// receive-close sequence and checking that the asynchronous notifications
+/// work.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tls_socket.h>
+#include <asiolink/testutils/test_tls.h>
+#include <util/buffer.h>
+#include <util/io_utilities.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <arpa/inet.h>
+#include <cstddef>
+#include <cstdlib>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string>
+#include <vector>
+
+using namespace boost::asio;
+using namespace boost::asio::ip;
+using namespace isc::util;
+using namespace isc::asiolink;
+using namespace std;
+
+namespace {
+
+const char SERVER_ADDRESS[] = "127.0.0.1";
+const unsigned short SERVER_PORT = 5303;
+
+// TODO: Shouldn't we send something that is real message?
+const char OUTBOUND_DATA[] = "Data sent from client to server";
+const char INBOUND_DATA[] = "Returned data from server to client";
+}
+
+/// An instance of this object is passed to the asynchronous I/O functions
+/// and the operator() method is called when when an asynchronous I/O completes.
+/// The arguments to the completion callback are stored for later retrieval.
+class TLSCallback {
+public:
+ /// \brief Operations the server is doing
+ enum Operation {
+ ACCEPT = 0, ///< accept() was issued
+ OPEN = 1, ///< Client connected to server
+ HANDSHAKE = 2, ///< TLS handshake completed
+ READ = 3, ///< Asynchronous read completed
+ WRITE = 4, ///< Asynchronous write completed
+ NONE = 5 ///< "Not set" state
+ };
+
+ /// \brief Minimum size of buffers
+ enum {
+ MIN_SIZE = (64 * 1024 + 2) ///< 64kB + two bytes for a count
+ };
+
+ struct PrivateData {
+ PrivateData() :
+ error_code_(), length_(0), cumulative_(0), expected_(0), offset_(0),
+ name_(""), queued_(NONE), called_(NONE), data_(MIN_SIZE, 0)
+ {}
+
+ boost::system::error_code error_code_; ///< Completion error code
+ size_t length_; ///< Bytes transferred in this I/O
+ size_t cumulative_; ///< Cumulative bytes transferred
+ size_t expected_; ///< Expected amount of data
+ size_t offset_; ///< Where to put data in buffer
+ std::string name_; ///< Which of the objects this is
+ Operation queued_; ///< Queued operation
+ Operation called_; ///< Which callback called
+ std::vector<uint8_t> data_; ///< Receive buffer
+ };
+
+ /// \brief Constructor
+ ///
+ /// Constructs the object. It also creates the data member pointed to by
+ /// a shared pointer. When used as a callback object, this is copied as it
+ /// is passed into the asynchronous function. This means that there are two
+ /// objects and inspecting the one we passed in does not tell us anything.
+ ///
+ /// Therefore we use a boost::shared_ptr. When the object is copied, the
+ /// shared pointer is copied, which leaves both objects pointing to the same
+ /// data.
+ ///
+ /// \param which Which of the two callback objects this is
+ TLSCallback(std::string which) : ptr_(new PrivateData())
+ {
+ ptr_->name_ = which;
+ }
+
+ /// \brief Destructor
+ ///
+ /// No code needed, destroying the shared pointer destroys the private data.
+ virtual ~TLSCallback()
+ {}
+
+ /// \brief Client Callback Function
+ ///
+ /// Called when an asynchronous operation is completed by the client, this
+ /// stores the origin of the operation in the client_called_ data member.
+ ///
+ /// \param ec I/O completion error code passed to callback function.
+ /// \param length Number of bytes transferred
+ void operator()(boost::system::error_code ec = boost::system::error_code(),
+ size_t length = 0)
+ {
+ setCode(ec.value());
+ ptr_->called_ = ptr_->queued_;
+ ptr_->length_ = length;
+ }
+
+ /// \brief Get I/O completion error code
+ int getCode() {
+ return (ptr_->error_code_.value());
+ }
+
+ /// \brief Set I/O completion code
+ ///
+ /// \param code New value of completion code
+ void setCode(int code) {
+ ptr_->error_code_ = boost::system::error_code(code, boost::system::error_code().category());
+ }
+
+ /// \brief Get number of bytes transferred in I/O
+ size_t& length() {
+ return (ptr_->length_);
+ }
+
+ /// \brief Get cumulative number of bytes transferred in I/O
+ size_t& cumulative() {
+ return (ptr_->cumulative_);
+ }
+
+ /// \brief Get expected amount of data
+ size_t& expected() {
+ return (ptr_->expected_);
+ }
+
+ /// \brief Get offset into data
+ size_t& offset() {
+ return (ptr_->offset_);
+ }
+
+ /// \brief Get data member
+ uint8_t* data() {
+ return (&ptr_->data_[0]);
+ }
+
+ /// \brief Get flag to say what was queued
+ Operation& queued() {
+ return (ptr_->queued_);
+ }
+
+ /// \brief Get flag to say when callback was called
+ Operation& called() {
+ return (ptr_->called_);
+ }
+
+ /// \brief Return instance of callback name
+ std::string& name() {
+ return (ptr_->name_);
+ }
+
+private:
+ boost::shared_ptr<PrivateData> ptr_; ///< Pointer to private data
+};
+
+
+// Read Server Data
+//
+// Called in the part of the test that has the client send a message to the
+// server, this loops until all the data has been read (synchronously) by the
+// server.
+//
+// "All the data read" means that the server has received a message that is
+// preceded by a two-byte count field and that the total amount of data received
+// from the remote end is equal to the value in the count field plus two bytes
+// for the count field itself.
+//
+// \param stream Stream on which the server is reading data
+// \param server_cb Structure in which server data is held.
+void
+serverRead(TlsStreamImpl& stream, TLSCallback& server_cb) {
+
+ // As we may need to read multiple times, keep a count of the cumulative
+ // amount of data read and do successive reads into the appropriate part
+ // of the buffer.
+ //
+ // Note that there are no checks for buffer overflow - this is a test
+ // program and we have sized the buffer to be large enough for the test.
+ server_cb.cumulative() = 0;
+
+ bool complete = false;
+ while (!complete) {
+
+ // Read block of data and update cumulative amount of data received.
+ server_cb.length() = stream.read_some(
+ boost::asio::buffer(server_cb.data() + server_cb.cumulative(),
+ TLSCallback::MIN_SIZE - server_cb.cumulative()));
+ server_cb.cumulative() += server_cb.length();
+
+ // If we have read at least two bytes, we can work out how much we
+ // should be reading.
+ if (server_cb.cumulative() >= 2) {
+ server_cb.expected() = readUint16(server_cb.data(), server_cb.length());
+ if ((server_cb.expected() + 2) == server_cb.cumulative()) {
+
+ // Amount of data read from stream equals the size of the
+ // message (as indicated in the first two bytes of the message)
+ // plus the size of the count field. Therefore we have received
+ // all the data.
+ complete = true;
+ }
+ }
+ }
+}
+
+// Receive complete method should return true only if the count in the first
+// two bytes is equal to the size of the rest if the buffer.
+
+TEST(TLSSocket, processReceivedData) {
+ // Amount of "real" data in the buffer
+ const uint16_t PACKET_SIZE = 16382;
+
+ // Used to instantiate socket
+ IOService service;
+ TlsContextPtr context(new TlsContext(CLIENT));
+ // Socket under test
+ TLSSocket<TLSCallback> test(service, context);
+ // Buffer to check
+ uint8_t inbuff[PACKET_SIZE + 2];
+ // Where data is put
+ OutputBufferPtr outbuff(new OutputBuffer(16));
+ // Expected amount of data
+ size_t expected;
+ // Where to put next data
+ size_t offset;
+ // Cumulative data received
+ size_t cumulative;
+
+ // Set some dummy values in the buffer to check
+ for (size_t i = 0; i < sizeof(inbuff); ++i) {
+ inbuff[i] = i % 256;
+ }
+
+ // Check that the method will handle various receive sizes.
+ writeUint16(PACKET_SIZE, inbuff, sizeof(inbuff));
+
+ cumulative = 0;
+ offset = 0;
+ expected = 0;
+ outbuff->clear();
+ bool complete = test.processReceivedData(inbuff, 1, cumulative, offset,
+ expected, outbuff);
+ EXPECT_FALSE(complete);
+ EXPECT_EQ(1, cumulative);
+ EXPECT_EQ(1, offset);
+ EXPECT_EQ(0, expected);
+ EXPECT_EQ(0, outbuff->getLength());
+
+ // Now pretend that we've received one more byte.
+ complete = test.processReceivedData(inbuff, 1, cumulative, offset, expected,
+ outbuff);
+ EXPECT_FALSE(complete);
+ EXPECT_EQ(2, cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(PACKET_SIZE, expected);
+ EXPECT_EQ(0, outbuff->getLength());
+
+ // Add another two bytes. However, this time note that we have to offset
+ // in the input buffer because it is expected that the next chunk of data
+ // from the connection will be read into the start of the buffer.
+ complete = test.processReceivedData(inbuff + cumulative, 2, cumulative,
+ offset, expected, outbuff);
+ EXPECT_FALSE(complete);
+ EXPECT_EQ(4, cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(PACKET_SIZE, expected);
+ EXPECT_EQ(2, outbuff->getLength());
+
+ const uint8_t* dataptr = static_cast<const uint8_t*>(outbuff->getData());
+ EXPECT_TRUE(equal(inbuff + 2, inbuff + cumulative, dataptr));
+
+ // And add the remaining data. Remember that "inbuff" is "PACKET_SIZE + 2"
+ // long.
+ complete = test.processReceivedData(inbuff + cumulative,
+ PACKET_SIZE + 2 - cumulative,
+ cumulative, offset, expected, outbuff);
+ EXPECT_TRUE(complete);
+ EXPECT_EQ(PACKET_SIZE + 2, cumulative);
+ EXPECT_EQ(0, offset);
+ EXPECT_EQ(PACKET_SIZE, expected);
+ EXPECT_EQ(PACKET_SIZE, outbuff->getLength());
+ dataptr = static_cast<const uint8_t*>(outbuff->getData());
+ EXPECT_TRUE(equal(inbuff + 2, inbuff + cumulative, dataptr));
+}
+
+// TODO: Need to add a test to check the cancel() method
+
+// Tests the operation of a TLSSocket by opening it, sending an asynchronous
+// message to a server, receiving an asynchronous message from the server and
+// closing.
+TEST(TLSSocket, sequenceTest) {
+
+ // Common objects.
+ // Service object for async control
+ IOService service;
+
+ // The client - the TLSSocket being tested
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ // Socket under test
+ TLSSocket<TLSCallback> client(service, client_ctx);
+ // Async I/O callback function
+ TLSCallback client_cb("Client");
+ // Where client receives message from
+ TCPEndpoint client_remote_endpoint;
+ // Received data is put here
+ OutputBufferPtr client_buffer(new OutputBuffer(128));
+ // The server - with which the client communicates.
+ // Address of target server
+ IOAddress server_address(SERVER_ADDRESS);
+ // Server callback
+ TLSCallback server_cb("Server");
+ // Endpoint describing server
+ TCPEndpoint server_endpoint(server_address, SERVER_PORT);
+ // Address where server received message from
+ TCPEndpoint server_remote_endpoint;
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ // Stream used for server.
+ TlsStreamImpl server(service.get_io_service(), server_ctx->getContext());
+
+ // Step 1. Create the connection between the client and the server. Set
+ // up the server to accept incoming connections and have the client open
+ // a channel to it.
+
+ // Set up server - open socket and queue an accept.
+ server_cb.queued() = TLSCallback::ACCEPT;
+ server_cb.called() = TLSCallback::NONE;
+ server_cb.setCode(42); // Some error
+ tcp::acceptor acceptor(service.get_io_service(),
+ tcp::endpoint(tcp::v4(), SERVER_PORT));
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ acceptor.async_accept(server.lowest_layer(), server_cb);
+
+ // Set up client - connect to the server.
+ client_cb.queued() = TLSCallback::OPEN;
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.setCode(43); // Some error
+ EXPECT_FALSE(client.isOpenSynchronous());
+ client.open(&server_endpoint, client_cb);
+
+ // Run the open and the accept callback and check that they ran.
+ while ((server_cb.called() == TLSCallback::NONE) ||
+ (client_cb.called() == TLSCallback::NONE)) {
+ service.run_one();
+ }
+ EXPECT_EQ(TLSCallback::ACCEPT, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+
+ EXPECT_EQ(TLSCallback::OPEN, client_cb.called());
+
+ // On some operating system the async_connect may return EINPROGRESS.
+ // This doesn't necessarily indicate an error. In most cases trying
+ // to asynchronously write and read from the socket would work just
+ // fine.
+ if ((client_cb.getCode()) != 0 && (client_cb.getCode() != EINPROGRESS)) {
+ ADD_FAILURE() << "expected error code of 0 or " << EINPROGRESS
+ << " as a result of async_connect, got " << client_cb.getCode();
+ }
+
+ // Perform handshake.
+ client_cb.queued() = TLSCallback::HANDSHAKE;
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.setCode(43); // Some error
+ client.handshake(client_cb);
+
+ server_cb.queued() = TLSCallback::HANDSHAKE;
+ server_cb.called() = TLSCallback::NONE;
+ server_cb.setCode(42); // Some error
+ server.async_handshake(ssl::stream_base::server, server_cb);
+
+ while ((server_cb.called() == TLSCallback::NONE) ||
+ (client_cb.called() == TLSCallback::NONE)) {
+ service.run_one();
+ }
+ EXPECT_EQ(TLSCallback::HANDSHAKE, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+
+ EXPECT_EQ(TLSCallback::HANDSHAKE, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+
+ // Step 2. Get the client to write to the server asynchronously. The
+ // server will loop reading the data synchronously.
+
+ // Write asynchronously to the server.
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.queued() = TLSCallback::WRITE;
+ client_cb.setCode(143); // Arbitrary number
+ client_cb.length() = 0;
+ client.asyncSend(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &server_endpoint, client_cb);
+
+ // Wait for the client callback to complete. (Must do this first on
+ // Solaris: if we do the synchronous read first, the test hangs.)
+ while (client_cb.called() == TLSCallback::NONE) {
+ service.run_one();
+ }
+
+ // Synchronously read the data from the server.;
+ serverRead(server, server_cb);
+
+ // Check the client state
+ EXPECT_EQ(TLSCallback::WRITE, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+ EXPECT_EQ(sizeof(OUTBOUND_DATA) + 2, client_cb.length());
+
+ // ... and check what the server received.
+ EXPECT_EQ(sizeof(OUTBOUND_DATA) + 2, server_cb.cumulative());
+ EXPECT_TRUE(equal(OUTBOUND_DATA,
+ (OUTBOUND_DATA + (sizeof(OUTBOUND_DATA) - 1)),
+ (server_cb.data() + 2)));
+
+ // Step 3. Get the server to write all the data asynchronously and have the
+ // client loop (asynchronously) reading the data. Note that we copy the
+ // data into the server's internal buffer in order to precede it with a two-
+ // byte count field.
+
+ // Have the server write asynchronously to the client.
+ server_cb.called() = TLSCallback::NONE;
+ server_cb.queued() = TLSCallback::WRITE;
+ server_cb.length() = 0;
+ server_cb.cumulative() = 0;
+
+ writeUint16(sizeof(INBOUND_DATA), server_cb.data(), TLSCallback::MIN_SIZE);
+ copy(INBOUND_DATA, (INBOUND_DATA + sizeof(INBOUND_DATA) - 1),
+ (server_cb.data() + 2));
+ boost::asio::async_write(server,
+ boost::asio::buffer(server_cb.data(),
+ (sizeof(INBOUND_DATA) + 2)),
+ server_cb);
+
+ // Have the client read asynchronously.
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.queued() = TLSCallback::READ;
+ client_cb.length() = 0;
+ client_cb.cumulative() = 0;
+ client_cb.expected() = 0;
+ client_cb.offset() = 0;
+
+ client.asyncReceive(client_cb.data(), TLSCallback::MIN_SIZE,
+ client_cb.offset(), &client_remote_endpoint,
+ client_cb);
+
+ // Run the callbacks. Several options are possible depending on how ASIO
+ // is implemented and whether the message gets fragmented:
+ //
+ // 1) The send handler may complete immediately, regardless of whether the
+ // data has been read by the client. (This is the most likely.)
+ // 2) The send handler may only run after all the data has been read by
+ // the client. (This could happen if the client's TCP buffers were too
+ // small so the data was not transferred to the "remote" system until the
+ // remote buffer has been emptied one or more times.)
+ // 3) The client handler may be run a number of times to handle the message
+ // fragments and the server handler may run between calls of the client
+ // handler.
+ //
+ // So loop, running one handler at a time until we are certain that all the
+ // handlers have run.
+
+ bool server_complete = false;
+ bool client_complete = false;
+ while (!server_complete || !client_complete) {
+ service.run_one();
+
+ // Has the server run?
+ if (!server_complete) {
+ if (server_cb.called() != TLSCallback::NONE) {
+
+ // Yes. Check that the send completed successfully and that
+ // all the data that was expected to have been sent was in fact
+ // sent.
+ EXPECT_EQ(TLSCallback::WRITE, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+ EXPECT_EQ((sizeof(INBOUND_DATA) + 2), server_cb.length());
+ server_complete = true;
+ }
+ }
+
+ // Has the client run?
+ if (!client_complete) {
+
+ if (client_cb.called() == TLSCallback::NONE) {
+ // No. Run the service another time.
+ continue;
+ }
+
+ // Client callback must have run. Check that it ran OK.
+ EXPECT_EQ(TLSCallback::READ, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+
+ // Check if we need to queue another read, copying the data into
+ // the output buffer as we do so.
+ client_complete = client.processReceivedData(client_cb.data(),
+ client_cb.length(),
+ client_cb.cumulative(),
+ client_cb.offset(),
+ client_cb.expected(),
+ client_buffer);
+
+ // If the data is not complete, queue another read.
+ if (!client_complete) {
+ client_cb.called() = TLSCallback::NONE;
+ client_cb.queued() = TLSCallback::READ;
+ client_cb.length() = 0;
+ client.asyncReceive(client_cb.data(), TLSCallback::MIN_SIZE ,
+ client_cb.offset(), &client_remote_endpoint,
+ client_cb);
+ }
+ }
+ }
+
+ // Both the send and the receive have completed. Check that the received
+ // is what was sent.
+
+ // Check the client state
+ EXPECT_EQ(TLSCallback::READ, client_cb.called());
+ EXPECT_EQ(0, client_cb.getCode());
+ EXPECT_EQ(sizeof(INBOUND_DATA) + 2, client_cb.cumulative());
+ EXPECT_EQ(sizeof(INBOUND_DATA), client_buffer->getLength());
+
+ // ... and check what the server sent.
+ EXPECT_EQ(TLSCallback::WRITE, server_cb.called());
+ EXPECT_EQ(0, server_cb.getCode());
+ EXPECT_EQ(sizeof(INBOUND_DATA) + 2, server_cb.length());
+
+ // ... and that what was sent is what was received.
+ const uint8_t* received = static_cast<const uint8_t*>(client_buffer->getData());
+ EXPECT_TRUE(equal(INBOUND_DATA, (INBOUND_DATA + (sizeof(INBOUND_DATA) - 1)),
+ received));
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/crypto_tls.h>
+#include <asiolink/botan_tls.h>
+#include <asiolink/openssl_tls.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/testutils/test_tls.h>
+#include <testutils/gtest_utils.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#ifdef WITH_OPENSSL
+#include <openssl/opensslv.h>
+#endif
+
+using namespace boost::asio;
+using namespace boost::asio::ip;
+using namespace isc::asiolink;
+using namespace isc::cryptolink;
+using namespace std;
+
+// Test if we can get a client context.
+TEST(TLSTest, clientContext) {
+ TlsContextPtr ctx;
+ ASSERT_NO_THROW(ctx.reset(new TlsContext(TlsRole::CLIENT)));
+}
+
+// Test if we can get a server context.
+TEST(TLSTest, serverContext) {
+ TlsContextPtr ctx;
+ ASSERT_NO_THROW(ctx.reset(new TlsContext(TlsRole::SERVER)));
+}
+
+// Test if the cert required flag is handled as expected.
+TEST(TLSTest, certRequired) {
+ auto check = [] (TlsContext& ctx) -> bool {
+#ifdef WITH_BOTAN
+ // Implement it.
+ return (ctx.getCertRequired());
+#else // WITH_OPENSSL
+ ::SSL_CTX* ssl_ctx = ctx.getNativeContext();
+ if (!ssl_ctx) {
+ ADD_FAILURE() << "null SSL_CTX";
+ return (false);
+ }
+ int mode = SSL_CTX_get_verify_mode(ssl_ctx);
+ switch (mode) {
+ case SSL_VERIFY_NONE:
+ return (false);
+ case (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT):
+ return (true);
+ default:
+ ADD_FAILURE() << "unknown ssl_verify_mode: " << mode;
+ return (false);
+ }
+#endif
+ };
+
+ TlsContext ctx(TlsRole::CLIENT);
+ EXPECT_TRUE(ctx.getCertRequired());
+ EXPECT_TRUE(check(ctx));
+ ASSERT_NO_THROW(ctx.setCertRequired(false));
+ EXPECT_FALSE(ctx.getCertRequired());
+ EXPECT_FALSE(check(ctx));
+ ASSERT_NO_THROW(ctx.setCertRequired(true));
+ EXPECT_TRUE(ctx.getCertRequired());
+ EXPECT_TRUE(check(ctx));
+}
+
+// Test if the certificate authority can be loaded.
+TEST(TLSTest, loadCAFile) {
+ string ca(string(TEST_CA_DIR) + "/kea-ca.crt");
+ TlsContext ctx(TlsRole::CLIENT);
+ ASSERT_NO_THROW(ctx.loadCaFile(ca));
+}
+
+// Test that no certificate authority gives an error.
+TEST(TLSTest, loadNoCAFile) {
+ string ca("/no-such-file");
+ TlsContext ctx(TlsRole::CLIENT);
+ EXPECT_THROW_MSG(ctx.loadCaFile(ca), LibraryError,
+ "No such file or directory");
+}
+
+#ifdef WITH_BOTAH
+// Test that a directory can't be loaded with Botan.
+TEST(TLSTest, loadCAPath) {
+ string ca(TEST_CA_DIR);
+ TlsContext ctx(TlsRole::CLIENT);
+ EXPECT_THROW(ctx.loadCaPath(ca), NotImplemented);
+}
+#else // WITH_OPENSSL
+// Test that a directory can be loaded.
+TEST(TLSTest, loadCAPath) {
+ string ca(TEST_CA_DIR);
+ TlsContext ctx(TlsRole::CLIENT);
+ ASSERT_NO_THROW(ctx.loadCaPath(ca));
+}
+#endif
+
+// Test that a certificate is wanted.
+TEST(TLSTest, loadKeyCA) {
+ string ca(string(TEST_CA_DIR) + "/kea-ca.key");
+ TlsContext ctx(TlsRole::CLIENT);
+#ifdef WITH_OPENSSL
+#if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ EXPECT_THROW_MSG(ctx.loadCaFile(ca), LibraryError,
+ "no certificate or crl found");
+#endif
+#endif
+}
+
+// Test if the end entity certificate can be loaded.
+TEST(TLSTest, loadCertFile) {
+ string cert(string(TEST_CA_DIR) + "/kea-client.crt");
+ TlsContext ctx(TlsRole::CLIENT);
+ ASSERT_NO_THROW(ctx.loadCertFile(cert));
+}
+
+// Test that no end entity certificate gives an error.
+TEST(TLSTest, loadNoCertFile) {
+ string cert("/no-such-file");
+ TlsContext ctx(TlsRole::CLIENT);
+ EXPECT_THROW_MSG(ctx.loadCertFile(cert), LibraryError,
+ "No such file or directory");
+}
+
+// Test that a certificate is wanted.
+TEST(TLSTest, loadCsrCertFile) {
+ string cert(string(TEST_CA_DIR) + "/kea-client.csr");
+ TlsContext ctx(TlsRole::CLIENT);
+ EXPECT_THROW_MSG(ctx.loadCertFile(cert), LibraryError,
+ "no start line");
+}
+
+// Test if the private key can be loaded.
+TEST(TLSTest, loadKeyFile) {
+ string key(string(TEST_CA_DIR) + "/kea-client.key");
+ TlsContext ctx(TlsRole::CLIENT);
+ ASSERT_NO_THROW(ctx.loadKeyFile(key));
+}
+
+// Test that no private key gives an error.
+TEST(TLSTest, loadNoKeyFile) {
+ string key("/no-such-file");
+ TlsContext ctx(TlsRole::CLIENT);
+ EXPECT_THROW_MSG(ctx.loadKeyFile(key), LibraryError,
+ "No such file or directory");
+}
+
+// Test that a private key is wanted.
+TEST(TLSTest, loadCertKeyFile) {
+ string key(string(TEST_CA_DIR) + "/kea-client.crt");
+ TlsContext ctx(TlsRole::CLIENT);
+ EXPECT_THROW_MSG(ctx.loadKeyFile(key), LibraryError,
+ "no start line");
+}
+
+// Test that the certificate and private key must match.
+TEST(TLSTest, loadMismatch) {
+ string cert(string(TEST_CA_DIR) + "/kea-server.crt");
+ TlsContext ctx(TlsRole::SERVER);
+ EXPECT_NO_THROW(ctx.loadCertFile(cert));
+ string key(string(TEST_CA_DIR) + "/kea-client.key");
+ // In fact OpenSSL checks only RSA key values...
+ // The explicit check function is SSL_CTX_check_private_key.
+ EXPECT_THROW_MSG(ctx.loadKeyFile(key), LibraryError,
+ "key values mismatch");
+}
+
+// Test the configure class method.
+TEST(TLSTest, configure) {
+ TlsContextPtr ctx;
+ string ca(string(TEST_CA_DIR) + "/kea-ca.crt");
+ string cert(string(TEST_CA_DIR) + "/kea-client.crt");
+ string key(string(TEST_CA_DIR) + "/kea-client.key");
+ EXPECT_NO_THROW(TlsContext::configure(ctx, TlsRole::CLIENT,
+ ca, cert, key, true));
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ(TlsRole::CLIENT, ctx->getRole());
+ EXPECT_TRUE(ctx->getCertRequired());
+
+#ifdef WITH_OPENSSL
+ // Retry using the directory and the server.
+ ca = TEST_CA_DIR;
+ cert = string(TEST_CA_DIR) + "/kea-server.crt";
+ key = string(TEST_CA_DIR) + "/kea-server.key";
+ EXPECT_NO_THROW(TlsContext::configure(ctx, TlsRole::SERVER,
+ ca, cert, key, false));
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ(TlsRole::SERVER, ctx->getRole());
+ EXPECT_FALSE(ctx->getCertRequired());
+
+#endif
+
+ // The error case.
+ cert = "/no-such-file";
+ key = string(TEST_CA_DIR) + "/kea-client.key";
+ EXPECT_THROW_MSG(TlsContext::configure(ctx, TlsRole::CLIENT,
+ ca, cert, key, true),
+ LibraryError,
+ "No such file or directory");
+ // The context is reseted on errors.
+ EXPECT_FALSE(ctx);
+}
+
+// Define a callback class.
+namespace { // anonymous namespace.
+
+/// @brief Local server address used for testing.
+const char SERVER_ADDRESS[] = "127.0.0.1";
+
+/// @brief Local server port used for testing.
+const unsigned short SERVER_PORT = 18123;
+
+/// @brief Class of test callbacks.
+class Callback {
+public:
+ /// @brief State part.
+ class State {
+ public:
+ /// @brief Constructor.
+ State() : called_(false), error_code_() {
+ }
+
+ /// @brief Destructor.
+ virtual ~State() {
+ }
+
+ /// @brief Called flag.
+ ///
+ /// Initialized to false, set to true when the callback is called.
+ bool called_;
+
+ /// @brief Last error code.
+ boost::system::error_code error_code_;
+ };
+
+ /// @brief Constructor.
+ ///
+ /// Used to shared pointer to state to allow the callback object to
+ /// be copied keeping the state member values.
+ Callback() : state_(new State()) {
+ }
+
+ /// @brief Destructor.
+ virtual ~Callback() {
+ }
+
+ /// @brief Callback function (one argument).
+ ///
+ /// @parame ec Boost completion code.
+ void operator()(const boost::system::error_code& ec) {
+ state_->called_ = true;
+ state_->error_code_ = ec;
+ }
+
+ /// @brief Callback function (two arguments).
+ ///
+ /// @parame ec Boost completion code.
+ void operator()(const boost::system::error_code& ec, size_t) {
+ state_->called_ = true;
+ state_->error_code_ = ec;
+ }
+
+ /// @brief Get called value.
+ inline bool getCalled() const {
+ return (state_->called_);
+ }
+
+ /// @brief Get error code.
+ inline const boost::system::error_code& getCode() const {
+ return (state_->error_code_);
+ }
+
+protected:
+ /// @brief Pointer to state.
+ boost::shared_ptr<State> state_;
+};
+
+} // end of anonymous namespace.
+
+// Test if we can get a stream.
+TEST(TLSTest, stream) {
+ IOService service;
+ TlsContextPtr ctx;
+ ASSERT_NO_THROW(ctx.reset(new TlsContext(TlsRole::CLIENT)));
+ boost::scoped_ptr<TlsStream<Callback> > st;
+ ASSERT_NO_THROW(st.reset(new TlsStream<Callback>(service, ctx)));
+}
+
+namespace { // anonymous namespace.
+} // end of anonymous namespace.
+
+// Test what happens when handshake is forgotten.
+TEST(TLSTest, noHandshake) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<Callback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ Callback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<Callback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ Callback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Send on the client.
+ char send_buf[] = "some text...";
+ Callback send_cb;
+ async_write(client, boost::asio::buffer(send_buf), send_cb);
+ while (!send_cb.getCalled()) {
+ service.run_one();
+ }
+ EXPECT_TRUE(send_cb.getCode());
+ EXPECT_EQ("uninitialized", send_cb.getCode().message());
+
+ // Receive on the server.
+ vector<char> receive_buf(64);
+ Callback receive_cb;
+ server.async_read_some(boost::asio::buffer(receive_buf), receive_cb);
+ while (!receive_cb.getCalled()) {
+ service.run_one();
+ }
+ EXPECT_TRUE(receive_cb.getCode());
+ EXPECT_EQ("uninitialized", receive_cb.getCode().message());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the server was not configured.
+TEST(TLSTest, serverNotConfigured) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx(new TlsContext(TlsRole::SERVER));
+ // Skip config.
+ TlsStream<Callback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ Callback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx;
+ test::configClient(client_ctx);
+ TlsStream<Callback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ Callback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Perform TLS handshakes.
+ Callback server_cb;
+ server.handshake(server_cb);
+ Callback client_cb;
+ client.handshake(client_cb);
+ while (!server_cb.getCalled() || !client_cb.getCalled()) {
+ service.run_one();
+ }
+ EXPECT_TRUE(server_cb.getCode());
+#ifndef LIBRESSL_VERSION_NUMBER
+ string server_expected("no shared cipher");
+#else
+ string server_expected("sslv3 alert handshake failure");
+#endif
+ EXPECT_EQ(server_expected, server_cb.getCode().message());
+ EXPECT_TRUE(client_cb.getCode());
+ EXPECT_EQ("sslv3 alert handshake failure", client_cb.getCode().message());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client was not configured.
+TEST(TLSTest, clientNotConfigured) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<Callback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ Callback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ TlsContextPtr client_ctx(new TlsContext(TlsRole::CLIENT));
+ // Skip config.
+ TlsStream<Callback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ Callback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Perform TLS handshakes.
+ Callback server_cb;
+ server.async_handshake(ssl::stream_base::server, server_cb);
+ Callback client_cb;
+ client.async_handshake(ssl::stream_base::client, client_cb);
+ while (!server_cb.getCalled() || !client_cb.getCalled()) {
+ service.run_one();
+ }
+ EXPECT_TRUE(server_cb.getCode());
+ EXPECT_EQ("tlsv1 alert unknown ca", server_cb.getCode().message());
+ EXPECT_TRUE(client_cb.getCode());
+#ifndef LIBRESSL_VERSION_NUMBER
+ string client_expected("certificate verify failed");
+#else
+ string client_expected("tlsv1 alert unknown ca");
+#endif
+ EXPECT_EQ(client_expected, client_cb.getCode().message());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client is HTTP (vs HTTPS).
+TEST(TLSTest, clientHTTPnoS) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<Callback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ Callback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ tcp::socket client(service.get_io_service());
+
+ // Connect to.
+ client.open(tcp::v4());
+ Callback connect_cb;
+ client.async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Perform server TLS handshake.
+ Callback server_cb;
+ server.async_handshake(ssl::stream_base::server, server_cb);
+
+ // Client sending a HTTP GET.
+ char send_buf[] = "GET / HTTP/1.1\r\n";
+ Callback client_cb;
+ client.async_send(boost::asio::buffer(send_buf), client_cb);
+
+ while (!server_cb.getCalled() || !client_cb.getCalled()) {
+ service.run_one();
+ }
+ EXPECT_TRUE(server_cb.getCode());
+#ifndef LIBRESSL_VERSION_NUMBER
+ string server_expected("http request");
+#else
+ string server_expected("tlsv1 alert protocol version");
+#endif
+ EXPECT_EQ(server_expected, server_cb.getCode().message());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client does not use HTTP nor HTTP.
+TEST(TLSTest, unknownClient) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<Callback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ Callback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part.
+ tcp::socket client(service.get_io_service());
+
+ // Connect to.
+ client.open(tcp::v4());
+ Callback connect_cb;
+ client.async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Perform server TLS handshake.
+ Callback server_cb;
+ server.async_handshake(ssl::stream_base::server, server_cb);
+
+ // Client sending something which is not a TLS ClientHello.
+ char send_buf[] = "hello my server...";
+ Callback client_cb;
+ client.async_send(boost::asio::buffer(send_buf), client_cb);
+
+ while (!server_cb.getCalled() || !client_cb.getCalled()) {
+ service.run_one();
+ }
+ EXPECT_TRUE(server_cb.getCode());
+#ifdef WITH_OPENSSL
+#ifndef LIBRESSL_VERSION_NUMBER
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ string server_expected("wrong version number");
+#else
+ string server_expected("unknown protocol");
+#endif
+#else
+ string server_expected("tlsv1 alert protocol version");
+#endif
+#endif
+ EXPECT_EQ(server_expected, server_cb.getCode().message());
+ EXPECT_FALSE(client_cb.getCode());
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client uses a certificate from another CA.
+TEST(TLSTest, anotherClient) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<Callback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ Callback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part using a certificate signed by another CA.
+ TlsContextPtr client_ctx;
+ test::configOther(client_ctx);
+ TlsStream<Callback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ Callback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Perform TLS handshakes.
+ Callback server_cb;
+ server.async_handshake(ssl::stream_base::server, server_cb);
+ Callback client_cb;
+ client.async_handshake(ssl::stream_base::client, client_cb);
+ while (!server_cb.getCalled() || !client_cb.getCalled()) {
+ service.run_one();
+ }
+ EXPECT_TRUE(server_cb.getCode());
+ // Full error is:
+ // error 20 at 0 depth lookup:unable to get local issuer certificate
+#ifdef WITH_OPENSSL
+#ifndef LIBRESSL_VERSION_NUMBER
+ string server_expected("certificate verify failed");
+#else
+ string server_expected("tlsv1 alert unknown ca");
+#endif
+ EXPECT_EQ(server_expected, server_cb.getCode().message());
+#if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ EXPECT_FALSE(client_cb.getCode());
+#endif
+#endif
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+// Test what happens when the client uses a self-signed certificate.
+TEST(TLSTest, selfSigned) {
+ IOService service;
+
+ // Server part.
+ TlsContextPtr server_ctx;
+ test::configServer(server_ctx);
+ TlsStream<Callback> server(service, server_ctx);
+
+ // Accept a client.
+ tcp::endpoint server_ep(tcp::endpoint(address::from_string(SERVER_ADDRESS),
+ SERVER_PORT));
+ tcp::acceptor acceptor(service.get_io_service(), server_ep);
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
+ Callback accept_cb;
+ acceptor.async_accept(server.lowest_layer(), accept_cb);
+
+ // Client part using a self-signed certificate.
+ TlsContextPtr client_ctx;
+ test::configSelf(client_ctx);
+ TlsStream<Callback> client(service, client_ctx);
+
+ // Connect to.
+ client.lowest_layer().open(tcp::v4());
+ Callback connect_cb;
+ client.lowest_layer().async_connect(server_ep, connect_cb);
+
+ // Run accept and connect.
+ while (!accept_cb.getCalled() || !connect_cb.getCalled()) {
+ service.run_one();
+ }
+
+ // Verify the error codes.
+ if (accept_cb.getCode()) {
+ FAIL() << "accept error " << accept_cb.getCode().value()
+ << " '" << accept_cb.getCode().message() << "'";
+ }
+ // Possible EINPROGRESS for the client.
+ if (connect_cb.getCode() &&
+ (connect_cb.getCode().value() != EINPROGRESS)) {
+ FAIL() << "connect error " << connect_cb.getCode().value()
+ << " '" << connect_cb.getCode().message() << "'";
+ }
+
+ // Perform TLS handshakes.
+ Callback server_cb;
+ server.async_handshake(ssl::stream_base::server, server_cb);
+ Callback client_cb;
+ client.async_handshake(ssl::stream_base::client, client_cb);
+ while (!server_cb.getCalled() || !client_cb.getCalled()) {
+ service.run_one();
+ }
+ EXPECT_TRUE(server_cb.getCode());
+ // Full error is:
+ // error 18 at 0 depth lookup:self signed certificate
+#ifdef WITH_OPENSSL
+#ifndef LIBRESSL_VERSION_NUMBER
+ string server_expected("certificate verify failed");
+#else
+ string server_expected("tlsv1 alert unknown ca");
+#endif
+ EXPECT_EQ(server_expected, server_cb.getCode().message());
+#if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ EXPECT_FALSE(client_cb.getCode());
+#endif
+#endif
+
+ // Used when adding other error cases.
+#if 0
+ cerr << "server: '" << server_cb.getCode().message() << "'\n";
+ cerr << "client: '" << client_cb.getCode().message() << "'\n";
+#endif
+
+ // Close client and server.
+ EXPECT_NO_THROW(client.lowest_layer().close());
+ EXPECT_NO_THROW(server.lowest_layer().close());
+}
+
+
+
+
+
+
+
+
+
--- /dev/null
+kea-server.crt
\ No newline at end of file
--- /dev/null
+kea-self.crt
\ No newline at end of file
--- /dev/null
+kea-client.crt
\ No newline at end of file
--- /dev/null
+kea-other.crt
\ No newline at end of file
--- /dev/null
+kea-server-addr.crt
\ No newline at end of file
--- /dev/null
+kea-ca.crt
\ No newline at end of file
--- /dev/null
+Similar to doc/examples/https/nginx/kea-nginx.conf
+ password is keatest
+ Country Name is US
+ Organization Name is ISC Inc.
+ Common Name is the key name.
+
+1 - create a CA self signed certificate (password is keatest)
+ openssl genrsa -aes128 -out kea-ca.key 4096
+ openssl req -new -x509 -days 3650 -key kea-ca.key -out kea-ca.crt
+
+2 - create a key for the client and decipher it
+ openssl genrsa -aes128 -out kea-client-aes.key 2048
+ openssl rsa -in kea-client-aes.key -out kea-client.key
+ rm kea-client-aes.key
+
+3 - create a certificate for the client
+ openssl req -new -key kea-client.key -out kea-client.csr
+ openssl x509 -req -days 3650 -in kea-client.csr -CA kea-ca.crt \
+ -CAkey kea-ca.key -set_serial 10 -out kea-client.crt -sha256
+
+4 - create a PKCS#12 bundle on macOS (password is keatest)
+ openssl pkcs12 -in kea-client.crt -inkey kea-client.key -export \
+ -out kea-client.p12
+
+5 - create a key for the server and decipher it (same than 2)
+ openssl genrsa -aes128 -out kea-server-aes.key 2048
+ openssl rsa -in kea-server-aes.key -out kea-server.key
+ rm kea-server-aes.key
+
+6 - create a certificate with a subject alternate name set to localhost
+ for the server
+ openssl req -new -key kea-server.key -out kea-server.csr \
+ -config server-conf.cnf
+ openssl x509 -req -days 3650 -in kea-server.csr -CA kea-ca.crt \
+ -CAkey kea-ca.key -set_serial 20 -out kea-server.crt \
+ -extfile ext-conf.cnf -sha256
+
+7 - create a certificate with a subject alternate name set to 127.0.0.1
+ and ::1 for the server
+ openssl req -new -key kea-server.key -out kea-server-addr.csr \
+ -config server-addr-conf.cnf
+ openssl x509 -req -days 3650 -in kea-server-addr.csr -CA kea-ca.crt \
+ -CAkey kea-ca.key -set_serial 30 -out kea-server-addr.crt \
+ -extfile ext-addr-conf.cnf -sha256
+
+8 - use c_rehash or openssl rehash to create hashes
+
+Setup the control agent: kea-ctrl-agent.json sample.
+
+Using curl.
+Note the localhost is important: using 127.0.0.1 instead can make the
+subjectAltName check to fail. curl is also picky about http vs https.
+
+to send a command (e.g. list-commands) directly to the control agent
+listening at port 8000:
+
+curl -D - -X POST -H Content-Type:application/json \
+ -d '{ "command": "list-commands" }' http://localhost:8000
+
+With the CA only (so authenticating the server only):
+curl -D - -X POST -H Content-Type:application/json --cacert kea-ca.crt \
+ -d '{ "command": "list-commands" }' https://localhost:8443
+
+With mutual authentication using OpenSSL:
+curl -D - -X POST -H Content-Type:application/json \
+ --cacert kea-ca.crt --cert kea-client.crt --key kea-client.key \
+
+With the mutual authentication on macOS (when the OpenSSL one fails):
+curl -D - -X POST -H Content-Type:application/json \
+ --cacert kea-ca.crt --cert kea-client.p12:keatest --cert-type P12 \
+ -d '{ "command": "list-commands" }' https://localhost:8443
+
+To the control agent:
+echo | kea-shell
+
+With server authentication only:
+echo | kea-shell --ca kea-ca.crt --port 8443 --host localhost
+
+With the mutual authentication:
+echo | kea-shell --ca kea-ca.crt --port 8443 --host localhost \
+ --cert kea-client.crt --key kea-client.key
--- /dev/null
+subjectAltName=IP:127.0.0.1,IP:::1
--- /dev/null
+subjectAltName=DNS:localhost
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIE3jCCAsYCCQDVzhmZelXOXDANBgkqhkiG9w0BAQsFADAxMQswCQYDVQQGEwJV
+UzERMA8GA1UECgwISVNDIEluYy4xDzANBgNVBAMMBmtlYS1jYTAeFw0yMDA2MTEx
+MzU3MzhaFw0zMDA2MDkxMzU3MzhaMDExCzAJBgNVBAYTAlVTMREwDwYDVQQKDAhJ
+U0MgSW5jLjEPMA0GA1UEAwwGa2VhLWNhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAvKQ/vJpJnXjZ+/LxZNfPc/QYSChSEQ8qoxh8prBYvPXyDu9ORHOa
+Dtd5AWusQLCI3iNYMDaJwrazj0g91jPKcxfvFZbnzFHTAZrDnmJwcTw96UfrP4b7
+PyXpUSF1/YfDf+/M3C7Wm9IJ/e704XHln/vFCw2dR/N5VOrXXJRcCd5NOES/ICXe
+xe62Mv7OjUQS8u6ovejtaaMkvoV2hGSG2LXdgVOCv0U8ybRs03Xl8BVM4lFYVO9H
+jnQ7O9AeGMqebvuyNAyGK9Dv+ERu65M9hB+pW//d+tVv3Dkfou+d5cOXPFXjf6vI
+K+2ClxkBH4A5dhsRJ7vPI41mwXA+H0g+MzxJ8Lg0pzJuLher03RZq3pBHvEc/jek
+P4u6mPrc+5J84jQ0hFwH4XIpxaKJsUiE/r1nFDiWRV27PgXMQgEbjdotxFX4IDBN
+KPtQNrybxiQHsYoZPdKcEfh8XyVT4NHrcbqN1SNf2ZIfDkm09aeDYXDdINAD+0yZ
+E+3YMeH4oWPpOIfW4OVzEDyfBGHyo2klTZfI5zdd54Kp4dKkzSlmIPC7OubdZZGo
+SlZfUlWVcRkqMbUAsZ8H2sdz0l+4k8+VmyiA4EWAiO6SV5xmYSncPQIN5dE2PbIx
+jKosl9JGhajs2gxCqlK+ZA3zgoFHhG1mKGWW7ucMic8Jy4oEq1XsoI0CAwEAATAN
+BgkqhkiG9w0BAQsFAAOCAgEAert/+ovFSWtRWKbFZNXs/o9ElWtVp+dxbOtgUNIS
+hdfLSHt/9nXw2FuBrvonDnTtl0kPhci1Qcwd5uAErlgddE6k27kcjOesMuXtwUke
+LLb7UQG7TQy3KmB+ARnG+toNTm2d8I5420+VDLqU1oh++x9l9KpWsDENSNeTDulT
+lVTJ7fVOTK7++NTCmqrp+Ublri3a2aoTK4pkt1ymcdIGD/kyCNeZro3/CKooV4yU
+xyTMBV0Huyu9V6OldtKtfbP2sWrQn5McRY1/18wJVTMq+OV5EI73R4bn/frfwl8o
+k8x8PH/ulozK+Pk4wz3z2NdT+ckSIfFs0RtVLW2MF1+8kJyt/9u8yUNfcw7MvNor
+94Zr86Mg0ZsHlXgeFfLm7h9dB/lQ5mtotrXfH4C4zltjPz17xouBSuZjZhgbkLaJ
+s4nPxWwxM/tN1mSYuVkiwq+qOz8ooePZh7zzEwpDiHr1tgzXxKojDcNC2uvVRTw/
+DKq8htcEb9kFyvDzxTq6zbvuNIyvzmpseEnpYxuzHFqCQtbN54Q88HuyebJlbxEI
+0BNb74yxvAQj3P+KS8xY9iqPExBeMiQu85eGmpTtKSnNjP1i09bg+xOVlESOeUPE
+cEe2ZsdEBwVaoCvjl5vbt2eJfpdt3UEwg6zfyncxjMZka/315B7d7k5qIEqsD5KO
+HXQ=
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,1E4500430B45CC59A1AFA62E20D0632E
+
+L1O4pVdZnk9nHSyH4fkoEehRNfhE4xbYt28YMtVctjeOQQWCf6m89k/rtOlSb9c6
+82WMHWiACuWNGxcd3RLZl0dWTPZYE4xk6T3TzTk/GwkDbQRf/6hfzGcRnObVRGYq
+kzBq6zXtoqFbq2jAACqCSoRlZgpLOv8hUdUcnto707iT0ebmwbNgPsxCBXjvxOYO
+Pvkihpfd7QY5GD8fn14y/y/im/9sqZgpNfhEVeO//Dpo1Nvo6DasU1gTnEoOkRRK
+/IBl12N4FxdiAjg16SfDw/M3/uka6ftekdr4PwD616qiUsBdKsuslp9aN82k+5RK
+X3iuODmMc/42SUoSskbL5mkuroOZxihwbiKsejcmGOfVygYXuZ9a9tLHLsdKLoWO
+1mmTMU4fzNpwXPor4h0yEDaortX2KwBVPnSWOMCJtwreukgt0GHfePfbd08Ojf6M
+pyZZ7gVv/q573RSgQL6nipU+4Il6T+cK4Iwdui9WSFahiOKgALuhTX0eY7CmlfcR
+hgNqmJhXEuXbEiQONcDA7iEAggdha4W3bm8blCj7QEBpr45fAyDSZxP/dNrIoZWC
+BxbrTq+YqzLyhUOOE7THdR5qpCha5Tsoyv8n7K91v77wZjmL1poyqHbXqvWDIJni
++LAPJDd6/Z0lqXLyTV3U9FcE6cAz6kkl5J1aeWFzfWSPtdiSzMPFkaz1MUPPllHF
+nyoA1R8PAD1yPj2accSIi8nBMYpOUrwMZcS+MbSW4GsbPEOqkluLgLLas/H9eohp
+SdyPsSnNBmWaCAwNHGWRAyRRefeMsrjtlF2AfVMsrCIzUNiSiw0MHsZQV6zlI23i
+/xyYxMn3fDmMxqJCJ8FkEHxVx5SeyzbysYmCfBsquKnfzE8JAyjmRQzdqfXHt5H9
+MEctsLiTQ+WPwWMN/6zHjuJMpJFZTfK/y0RUgTUyf02t0C4Bobx30DOx0SM4B7Rx
+QQ7uwMlarE8Pg7tCDA0kC2aGCSaHo2u0qssmLVGhNKNkBVKkr7SpS4CM7dcIh+Yk
+30Q4UQfCzRbS17RD1LfdUg+SPCeDFoKdh4f4FVoHXrbeEOhPJVeCjPli78nnPuZ0
+kGvndf/v+4DH40Wvt5aZj90mes6q+2Hy4GlgciELEWhMcj2QSiRISNi5UFNYRsSL
+RsEhuksONQVrFnRS3n3WvQrZ8X4OLAfatlFewpR9UVvgfWXLuWLy6etDWa056wDa
+4OW715YaEedSsF8WrfhRXmU/IDJ19oiQzsQiyeiKoFW3OVRyf2ngb8psUOwLbgA6
+kjcrzt77RsYKlP7TYC2hvycqnvvDhKCe6yQmd6vS1lOdBm8VZWzJCGFfoeucx4i2
+DS5ryWhU9d4VoCxFYEEsNhC8GKkrcATikhLnB8riJgt5PrJenYMBd9EsuwAo3Xaa
++95SeiAdka2XIN2dBDOJ4qAJYKhHyZF/fJpJP/1s3zGsdBN3mkY3C1C3/dYR1fan
+7fK9Qx2fcZjeMTkdm91Ito7ui2LQDVjJoTEaZ0LyMh3Gz7hALuDfPeS3Eft3QXMB
+Do3Tki68lvtc9DadlDQfTm84WvS4BVyOhQVQqhS2Ttq+ICGrNekPg1zyMUI2N0bo
+8ulenrCKStFBqgyWq1aczcLNEDth0GWOFjLdgWUwI2pcN3tuouLHXpfKKARxxdis
+Un3Dj5nhg6G2vGhTTTRdxMQeiT0Dr6Q2tD9VUNojVZwJ1c50dgZ6hlhzU5pv+1vU
+krBjlx9szF2ikx2pUp8RHDAziKkv17zXDjvEJpE/pvYWHBfBPoQr5NPaPGYnbFIX
+qaLYtWOAFlL3BI1XSO/32nYee0+WjnKMr4IOvXJfnaa94S+wU6pJEbTGHP+1aGNS
+wsslmcfRDmmeblGd40Bo4ENCc93KxBf3V7g7/JnSUZO39TyfvMnyy1E3JC6fu/A4
+VvnlnFM+6ZjdhkiZ4RJqd2rc2AhA6HhOslJSa0kPRc6UQQqAci+7YHZBc/PELhpD
+LpFbBXbqyi1jNQNodhhJtkD8VkvYHOisqzHFTITZp5epK8mjLkBhIW2VUVZ+dDK+
+3kFrKB+CaEvE1OBAlDYeVxMAvT1rmyjT04mqPRnp0G57+5VQQFYrKfVevDddLIt2
+tQphIcgZYAHTU+2otlPAOXqgPJWRoKNTw6Rtc6dELrAOE/kDFqZ4VKRnXRNFmxj3
+NSC8zapuNmkGQTo8CHzJuRI8sfNHjcDrMELHV1Fe8XSoqdovV2X+Xa/fesCaYfrp
+6506uFGZSR7SrMdT5MoXGri1IEvGXkGI30UDq5QTEzHiyyYgC7kZFn3E/zREbA0y
+/WahS8zICLsEK2ZknSv3q6e9aONokNbYu7PqvQtW5IPGrjdZxuQDtRXEYafiDLKT
+c3h9eE8OKk5Si49TRjsYbuR4+BBw9N0R0RIfs5TIDkkGeCu0M4yFPKQVhCN98OAk
+h0L+ZhQJZfbDE7QNBuvmRBNcpJYe7JTXl2/p6JjoxeyZTgShk81BiOmMCaWavKB+
+gIqy4X39y+J+AiYMiKy/+B5gtNaZaE9hka7RH2tV5nkiTBilZ6v5N1A4V4Q0PRFT
+HZAXgnUwI0HcIRfkqxlF3gXMzhG1+K2wxS9uVn5K0E27xNeswr+ksfLJsyWz+gdT
+/ZFgGyErUY6CLmYzmW+WfQox+qd9pd1TMISNuBWXrdoKkX8iFjj8SWyPcZvqMUkx
+lo8RVzb/6ugSTcbCQGpf+6H8ZuOe9hZwD9tKBh6XZbC5KtBQ8TtSnrmsk9ufIzn8
+ACrJFTVOG4u/g/xn1j3MY4NIaLA77YSCed+TzOXBPmG+LrJM67n1tMtGWEPoOnGi
+6pzJpF5cxsF4i0QoqdYFThqMb6mHtaVPsjjIpdzEXmYyQENLQECERE6lYlz9ZVkS
+NsOR3KMOxXZQ+iWmqCptazz0hVVmEBFisg6K6WuQR3BpXcf8N9UP7xUnStlUUaQ7
+G5nf6BZl3AIxZPay/NoM87n4I4lplPaQwyK/ReMztu78OQFyx9mC1BGOHxVtF6hO
+W+POZqc7ugCXiY8A08vSv5yt8paWDnU+hHXnEo04Hw0ex2KNOOZeL0Eg+idJTZe0
+/0yl0olct0HUgSyhU3wm0uWiHwulreoa3tNL+a4Xt7k5L2e5XcvAh3T2mgxzDq5q
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIID2jCCAcICARQwDQYJKoZIhvcNAQELBQAwMTELMAkGA1UEBhMCVVMxETAPBgNV
+BAoMCElTQyBJbmMuMQ8wDQYDVQQDDAZrZWEtY2EwHhcNMjEwMjIxMTg0MDQzWhcN
+MzEwMjE5MTg0MDQzWjA1MQswCQYDVQQGEwJVUzERMA8GA1UECgwISVNDIEluYy4x
+EzARBgNVBAMMCmtlYS1jbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDKbsDkElojvFhVt234GQOEVVudEp4s8KYnDQTZpsdeidrP3yY+qWfzG1k1
+6qMB5jXF7dRhzq4FiPbZMs5cz3BfwZDlxjWMxgixPaCrVphYLGhI8AOne8PEl47e
+4Ae3Cl96dWUfQKQmGIzzHfTcJvCxUOCob5zYOCDvtjk48IxdvHi18Ab/hXyGJKXS
+uqCsaXBRK7Amn8/jxMgdhds92tNxm0BiAJtsmkQm9QW8ztcoiEEgO4ViDRJSRKaG
+9hVRrAe4GPisOjUzerADkPX/pchHIqmrTJ9YKhngOfDdiAZY1lkZc1cbM6zqqTgT
+p1MvttSv8JEN6OMhM+bpCbaiWp4DAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAElh
+o7srSKP+6qiuzXR1eWDgTXcZa13Zj1z78Ipr3GnnoKJLLa+X69nHkA8fC2nP+Y6h
+2COdlPn/JVJ20ZKMkmC+VnnBklAe7zGnd83cmiOm12kj8lGUwQ/muDW3GU402WBT
+3CZubevUGgVIZv3fYcw1l3t1Q6eNASRr/xY40a9QvGAilKQSvZKdbIuYbAoMEbpX
+yCErSCVPxHcIjVDghIx/Jsn2RXg+yehpRgtCO/DM9E7/6q7yhb6jMrUqujCE40cc
+5TuBexXZsXH1x/Ic7mcwVDgAfGMm9j5a5isyIh7+uCItNpGlTOQIIx80wZbVHVyx
+9IpUA+IInq2rK5LGp4otXGODAN9wbMBrMX0VTQlY2DZ24Vr5L6sykmHkOSELaWvW
+2M0bNU16NUPxRUoWDkG99AwqT2ZnKMnsYqwayWMiQu/s1ek9zs01Pf4YFf5w659I
+YHgAVhd5gSmxcJ3VTilgUaYE9DRAKY3GVFkliTlGYM55khyJYdASWGijHI14hs4W
+TZQWebbaoaKNtEq+5omj7HsNLrWfKe6EQrn9z7PY+96ZbSZsbt34/tmsVpmTrOFB
+BV/iU3uEJGvAucI0VXgguKN3jmw2hWstHzEWEMHm107Vp3QPWmrHzvcosAxLsKpg
+WyHjO3AiUQOsP9NPOy9Owr/XJCcSbf5k4MuFDLXi
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE REQUEST-----
+MIICejCCAWICAQAwNTELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJbmMuMRMw
+EQYDVQQDDAprZWEtY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAym7A5BJaI7xYVbdt+BkDhFVbnRKeLPCmJw0E2abHXonaz98mPqln8xtZNeqj
+AeY1xe3UYc6uBYj22TLOXM9wX8GQ5cY1jMYIsT2gq1aYWCxoSPADp3vDxJeO3uAH
+twpfenVlH0CkJhiM8x303CbwsVDgqG+c2Dgg77Y5OPCMXbx4tfAG/4V8hiSl0rqg
+rGlwUSuwJp/P48TIHYXbPdrTcZtAYgCbbJpEJvUFvM7XKIhBIDuFYg0SUkSmhvYV
+UawHuBj4rDo1M3qwA5D1/6XIRyKpq0yfWCoZ4Dnw3YgGWNZZGXNXGzOs6qk4E6dT
+L7bUr/CRDejjITPm6Qm2olqeAwIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAMgA
+4PiiHLAdo5tjjEWyPOsVCaKORAB8PqELc9XJHfZeyyCEDRptQfH//XKe7WRZmbZI
+baq1cqjZFVb8yrMjBr1mXUOuBzmofexaXwFEMOufirUawenqGeivkIW23j+Jq6vX
+xs2jlXdqE7H6ApXo5De0NhnpeNQS+88xDfQvcaqPYw5TmOrAtPrGt42vSa0x0vf6
+OnnFnOFEFh6AFfj6Sg6SWeNOn61RgUR5iqPkQsH33o/viTqKL4qITroFUHmau7Ec
+BimeigqvKOMS785BxmXeYl2qEg9Vu4zaFePAHPPpjIA7LELfXdM/B6TOP9/aCMEd
+NhQVPAUOXFxCnBHWo84=
+-----END CERTIFICATE REQUEST-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAym7A5BJaI7xYVbdt+BkDhFVbnRKeLPCmJw0E2abHXonaz98m
+Pqln8xtZNeqjAeY1xe3UYc6uBYj22TLOXM9wX8GQ5cY1jMYIsT2gq1aYWCxoSPAD
+p3vDxJeO3uAHtwpfenVlH0CkJhiM8x303CbwsVDgqG+c2Dgg77Y5OPCMXbx4tfAG
+/4V8hiSl0rqgrGlwUSuwJp/P48TIHYXbPdrTcZtAYgCbbJpEJvUFvM7XKIhBIDuF
+Yg0SUkSmhvYVUawHuBj4rDo1M3qwA5D1/6XIRyKpq0yfWCoZ4Dnw3YgGWNZZGXNX
+GzOs6qk4E6dTL7bUr/CRDejjITPm6Qm2olqeAwIDAQABAoIBAQCiT9OSC7UC2k3p
+zL38I/JR9S8T7o2tcveGcEXnTnalMtujuUUtESAsKU2KkaxKJZAQN/YGxnV6Mqva
+04XrnNh5rvbDhf4B9feaRdPCDhjw+gpUET6c1/vMcck4o2EMPuD3i5GdUXNMqHq7
+pNVkgESVqEe6RmP4amjeS7nuEdI6hSqFQa7EelC7C7HIIxz/jpiHRYk2pp5o3wx3
+EEyyhR+Ip3+U9EOlGZyqvasaGf6PYgBC2pTjitVP7+ArxokBTx1/VfNmVgOT+A+2
+kkqg4Ee3sgmBGjy0aUatr/QOSEELnJw9cHZLIapklDo+cS/ypSWiGASGUvCyjmBm
+VDg/DDDBAoGBAPFXAR1NwmVNs5mU5LA9kgs8Pz/d4LAOa3CrUEFjBSMrfAkB3Je8
+0x0Xmht2QIRVPQ2NFklM07aqToWMtxPSoLoGlovgTEAtcyLWnRrANlhd0VwG0m62
+YlRkIrRcS5m1yS+EKETCEWnsGoCrdYbBdpKJVoNd4pxAHXYgjlzKLPH5AoGBANa6
+tz0RiP6GHU7yONR0yXEYmLhniWnE9A+5UEKjEt4ZOo4rDxocBZSENPwMf7576Vv3
+kTuL4aSkBPA5DxBsjOq/CT3o3Fng2aRLNL37glYrVLAsNIPs/YVWuLJZ2fXJZMbG
+PbR2SVhXU8YvQaY3s6OlzfQ07Zd4T5TUnoMpDA7bAoGAJ5638R6d5lGeRX1bGc/R
+1QRcAdkkFOMZIlMNht6BrmdqInRqyYJXSjRguVdtegwgTMQ3v2rcauWEpIoYWCnA
+9ykzt9znx7VubG69NfIOOe2U/D2meER62g3iYKeyRZbBY4qXrcoKX9BB/ZOoZKoI
+FEB1snVMSYiBDa6EkJkkTckCgYBeU3UtAWfxjw6O4H6wbYEUCl8EGo9VhCxGP/yO
+2T3vjJuZWjbvHEIjRJRV6FOxZJNVUAJfawo7HcYBlL8WUujwMe0oYgNyBAD3WAYa
+MsLFgZFZNoH3NgMEMN0/k5LYkpiPbQQsIw4DHZFybM3k63EhQTOgxCNet34V/fSH
+318powKBgH/QdL/jSMUV4DGnPtayzTEszjgNsqt7SPkWvKtA+K+EoX2rlpZf65RI
+Mei9BawHU2H4rfCN3QTqimHt2/xNKyCowF+a4fRLPz8bDqOqiWMPZeD+PscWSrKq
+r3TDUNfttWQvdE5x1nct20T4dQ9FY1w8MgcsouBbmhFoWYDQOfuO
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIID1zCCAb8CARQwDQYJKoZIhvcNAQELBQAwMzELMAkGA1UEBhMCVVMxETAPBgNV
+BAoMCElTQyBJbmMuMREwDwYDVQQDDAhvdGhlci1jYTAeFw0yMTAyMjEyMTE4NDda
+Fw0zMTAyMTkyMTE4NDdaMDAxCzAJBgNVBAYTAlVTMREwDwYDVQQKDAhJU0MgSW5j
+LjEOMAwGA1UEAwwFb3RoZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDAoEENWQ6tl6aaRMn+yaNUKTBIIWpVoy5+uGsBdZW++fEvw4xmleGD+bwyHZFE
+sHPos/v7zWUNFaX2aWD0H+Hk4l2WTFigWO3utPoXDzDOjfQmglKG+R08p3giURrJ
+zUKWwe/RRJBs7qXdcD9yNXVOb2JWp4Cxk1iPj7zTS/LGsFr7F4/k2nlH3EuqvB3G
+BEXHa/sA55xigMyvqVnVb4rNh+PjGL8l5SZzSnrbdoIEtKw/LVbBCAVrQsgcADNq
+jR7ILbqeIqg1Td11QvQzB7f/U5dQoQPzq3j4ow1zOiaSokZE7UcUCUNfjRv5E2lW
++mmyM7nkgyE9LqUJ/3udIh1vAgMBAAEwDQYJKoZIhvcNAQELBQADggIBACK3Dl0s
+NmwPSNQuVH9d/fgL954ephn+GAsNamOLL9gFwZmHxVzHJ93GnbrVsTtvWFa2w+Tc
+jDGzRvbhMMh4bnKOJ4OUzn2ISQOyWdhBWnSKVt9kiunP7Jn8ufH4WOpkeP1FKXRg
+xMgGcK/3oOn2OV2Nj9BT5Wm8MPytVdsY1e3XCbBcRfYB1acNE0Q6Fx9/u0OxGNQ2
+ITRUQb5T1yoI6Isq2bhdW1hdl3O5DPcjkzDBQlqxXOUoZeLKuXeQlPxV+rELO52o
+z48CL/Y1jOhmplM7aUCNNxdObZZ0ym1OCEGo6yNCkGil6ErEgmVE7IrGaLMwbAHj
+eylbIcAieaAuc1w858nRbB+ryND9BWj5G0+B3xhuX3Xs4bfDKxPayytf9ixfshrn
+9TR0g3GIV+lIVOuCmQtf5H2eZ2wTxd09f6Sglh7WMm/RNEd+E53n3dnksjt9X/Lc
+27Q513x6IudXTreJMQdAstna/Aftjm4KN+zMc1JEYXPyiwN3UEFk8gfBstbg3Tb9
+ioA/yX4E1Jim7HHqB4eoTcmv4nz0kYrNtFxp4xAoy1qYc8afFOyborWdCqZFbERO
+JQJop0aVSm7tR95nhcd1O3ZgxMjzT6jOIw7P/DxCBQqz5xwXThpcYboK5z4cdWe7
+QAsCwmY3m4pbVvxtY9xl+LtEqNt7/eFXqstV
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAwKBBDVkOrZemmkTJ/smjVCkwSCFqVaMufrhrAXWVvvnxL8OM
+ZpXhg/m8Mh2RRLBz6LP7+81lDRWl9mlg9B/h5OJdlkxYoFjt7rT6Fw8wzo30JoJS
+hvkdPKd4IlEayc1ClsHv0USQbO6l3XA/cjV1Tm9iVqeAsZNYj4+800vyxrBa+xeP
+5Np5R9xLqrwdxgRFx2v7AOecYoDMr6lZ1W+KzYfj4xi/JeUmc0p623aCBLSsPy1W
+wQgFa0LIHAAzao0eyC26niKoNU3ddUL0Mwe3/1OXUKED86t4+KMNczomkqJGRO1H
+FAlDX40b+RNpVvppsjO55IMhPS6lCf97nSIdbwIDAQABAoIBAGFKT7D7MzOwbeBr
+MA71Lv5aE13LKtb4JYqxgLJq/mhH+26heO9zckTjRQ+W6YFlEhYNmg8TLZ/POFTd
+4q9KzyB4yMYZ54WNhRdU5x/wdzlMb21n02ECv6ab+0xx5HLarnBQodzzoXKzkqjm
+gdUgdRBZp7SWw6RtBIT2F2wtZwC8v9TbLVjgYanK+sU1NlVJ86kg8WuPaST7kmaR
+I+BtFXapq5SP/MI5dJki3WNlalzJUIUiRtmqkSointJIEL1Jx1RGonPWDTQtmq8V
+/86eXOFEoqMXpK5gpKACl6amxbMBks3BViUHq/p7wpCF/c4a/t2xZ+P3ZAyuBg+P
+QLcNJCkCgYEA3sP4hAyoENqG0BVVttloXk0rhN7A7AXZ6Hd2EWYCJ+1X+0Mmjas6
+DnnPEIBN6DLthHSisiw8jfP9yAQlas1CHliSzdwgregzSt+PIRCXNxXNoYm7/xGu
+E0OK2cjcYmCX1fOp3WLyjEEjJh8/ZQys64wf8dS2gQbjuKi1M4Cu78MCgYEA3V0s
+CaPTMwng76kLVMTWzFyiTnxKhbrGlvzYnW0vTJ6YSJnLEKGzH3Df4e+K9mh/sRhw
+ZOaT3nH1P6iuPHcxFp3K1qaUU3yfVXlOVWJZ5LyP5hwyiaqBGRMnEapVAkb5bvAx
+qhDh3pVu2Qo3Bg+A3JEQ5mQmng+/DsSpIsrtjOUCgYAYthiNXXIPXI5z6sn1XKyt
+OVZIiQVRqVyA4y+fwncewr9tygcu0/2+uVh09iauSWf7t4yMw0d8X8KZO4yDCn35
+K84tM+wUHpwCBEa2XkbH/40uDD9kjiuHS4jNm/CGoTx2qW8Adgd984PYqMK5jOxp
+vMOWaghMy9zbESv4qJ+/TQKBgGBLcKW841n3eScoNSqp+fqqbVyRCuYDqvHxidVp
+ssK01u/2HvTcMoyB0JJ7Xsr0CGAkjngGKdsBXbtUiH77Jpp9B8i6bBmpQ7Kt81qH
+Ty2GrV5fc8iZKFGdGEjB/Prhnw4YZLJjZ64o+TBnFiqHwfmxDVX7ySTHGsi02hKt
+jgrVAoGBAKF0Z+KdT95HbS12J0wLJ4LAyFRMxQJ2+A17ryt6MwN5lICMBh0IosHD
+fpgh5pd4ZDJ5pA0seG2pGwFLkPhCM5EuumikU713SGboxkjS8ozfUkJGiXyg2C1t
+9lpsU6MKC4eTMg6WlTjBXoFc3seXP83mNKjy7Rn/qqIDHKH7WXke
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIC6DCCAdACCQC3T0mkbypFVTANBgkqhkiG9w0BAQsFADA2MQswCQYDVQQGEwJV
+UzERMA8GA1UECgwISVNDIEluYy4xFDASBgNVBAMMC3NlbGYtc2lnbmVkMB4XDTIx
+MDIxODE4MzA0MloXDTMxMDIxNjE4MzA0MlowNjELMAkGA1UEBhMCVVMxETAPBgNV
+BAoMCElTQyBJbmMuMRQwEgYDVQQDDAtzZWxmLXNpZ25lZDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAMCgQQ1ZDq2XpppEyf7Jo1QpMEghalWjLn64awF1
+lb758S/DjGaV4YP5vDIdkUSwc+iz+/vNZQ0VpfZpYPQf4eTiXZZMWKBY7e60+hcP
+MM6N9CaCUob5HTyneCJRGsnNQpbB79FEkGzupd1wP3I1dU5vYlangLGTWI+PvNNL
+8sawWvsXj+TaeUfcS6q8HcYERcdr+wDnnGKAzK+pWdVvis2H4+MYvyXlJnNKett2
+ggS0rD8tVsEIBWtCyBwAM2qNHsgtup4iqDVN3XVC9DMHt/9Tl1ChA/OrePijDXM6
+JpKiRkTtRxQJQ1+NG/kTaVb6abIzueSDIT0upQn/e50iHW8CAwEAATANBgkqhkiG
+9w0BAQsFAAOCAQEAnhmEeDZv9IJL5Vv8K9Ltb8WzCaH7faSd/wKW5qqh+odeUJHk
+mZN8gwBaL8VSrXiCGKgTexn5Uc4PgxAbK887t3Q0BUIleOHG5mvQ7/0+uBtGEp72
+PSSsIHL7osiSMTi142ppY2/LpUfP7I65Z1lpaThdJu2YgxjVeoFZI+L3ubzVM6M0
+V/yBrK/vZMVVQv4tkCgte3jX/XH7aQ/+OK1xB9oyOqe7yShMrPS6oFLmvGjWMqQO
+/NMPxqsGWH/EZeTVmPP8+zw7/s2mnHrdqMLkHO6/sEPAdgyxrjcDLqtIdNgoMdBz
+2sH8t4L5qGTKQjDIJ8Zam6O9lJJhZ18D6Rqtwg==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAwKBBDVkOrZemmkTJ/smjVCkwSCFqVaMufrhrAXWVvvnxL8OM
+ZpXhg/m8Mh2RRLBz6LP7+81lDRWl9mlg9B/h5OJdlkxYoFjt7rT6Fw8wzo30JoJS
+hvkdPKd4IlEayc1ClsHv0USQbO6l3XA/cjV1Tm9iVqeAsZNYj4+800vyxrBa+xeP
+5Np5R9xLqrwdxgRFx2v7AOecYoDMr6lZ1W+KzYfj4xi/JeUmc0p623aCBLSsPy1W
+wQgFa0LIHAAzao0eyC26niKoNU3ddUL0Mwe3/1OXUKED86t4+KMNczomkqJGRO1H
+FAlDX40b+RNpVvppsjO55IMhPS6lCf97nSIdbwIDAQABAoIBAGFKT7D7MzOwbeBr
+MA71Lv5aE13LKtb4JYqxgLJq/mhH+26heO9zckTjRQ+W6YFlEhYNmg8TLZ/POFTd
+4q9KzyB4yMYZ54WNhRdU5x/wdzlMb21n02ECv6ab+0xx5HLarnBQodzzoXKzkqjm
+gdUgdRBZp7SWw6RtBIT2F2wtZwC8v9TbLVjgYanK+sU1NlVJ86kg8WuPaST7kmaR
+I+BtFXapq5SP/MI5dJki3WNlalzJUIUiRtmqkSointJIEL1Jx1RGonPWDTQtmq8V
+/86eXOFEoqMXpK5gpKACl6amxbMBks3BViUHq/p7wpCF/c4a/t2xZ+P3ZAyuBg+P
+QLcNJCkCgYEA3sP4hAyoENqG0BVVttloXk0rhN7A7AXZ6Hd2EWYCJ+1X+0Mmjas6
+DnnPEIBN6DLthHSisiw8jfP9yAQlas1CHliSzdwgregzSt+PIRCXNxXNoYm7/xGu
+E0OK2cjcYmCX1fOp3WLyjEEjJh8/ZQys64wf8dS2gQbjuKi1M4Cu78MCgYEA3V0s
+CaPTMwng76kLVMTWzFyiTnxKhbrGlvzYnW0vTJ6YSJnLEKGzH3Df4e+K9mh/sRhw
+ZOaT3nH1P6iuPHcxFp3K1qaUU3yfVXlOVWJZ5LyP5hwyiaqBGRMnEapVAkb5bvAx
+qhDh3pVu2Qo3Bg+A3JEQ5mQmng+/DsSpIsrtjOUCgYAYthiNXXIPXI5z6sn1XKyt
+OVZIiQVRqVyA4y+fwncewr9tygcu0/2+uVh09iauSWf7t4yMw0d8X8KZO4yDCn35
+K84tM+wUHpwCBEa2XkbH/40uDD9kjiuHS4jNm/CGoTx2qW8Adgd984PYqMK5jOxp
+vMOWaghMy9zbESv4qJ+/TQKBgGBLcKW841n3eScoNSqp+fqqbVyRCuYDqvHxidVp
+ssK01u/2HvTcMoyB0JJ7Xsr0CGAkjngGKdsBXbtUiH77Jpp9B8i6bBmpQ7Kt81qH
+Ty2GrV5fc8iZKFGdGEjB/Prhnw4YZLJjZ64o+TBnFiqHwfmxDVX7ySTHGsi02hKt
+jgrVAoGBAKF0Z+KdT95HbS12J0wLJ4LAyFRMxQJ2+A17ryt6MwN5lICMBh0IosHD
+fpgh5pd4ZDJ5pA0seG2pGwFLkPhCM5EuumikU713SGboxkjS8ozfUkJGiXyg2C1t
+9lpsU6MKC4eTMg6WlTjBXoFc3seXP83mNKjy7Rn/qqIDHKH7WXke
+-----END RSA PRIVATE KEY-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIECzCCAfOgAwIBAgIBHjANBgkqhkiG9w0BAQsFADAxMQswCQYDVQQGEwJVUzER
+MA8GA1UECgwISVNDIEluYy4xDzANBgNVBAMMBmtlYS1jYTAeFw0yMTAyMjEyMTE1
+MTdaFw0zMTAyMTkyMTE1MTdaMDoxCzAJBgNVBAYTAlVTMREwDwYDVQQKDAhJU0Mg
+SW5jLjEYMBYGA1UEAwwPa2VhLXNlcnZlci1hZGRyMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAveRRgIN0S8oeBXVaIEnsG1DKuDzKKqLoLdBQNfoZrKzD
+LIMNzlabxu20h82Y/OU02EdEzar98OstzglIWimKFVI0Omi0AuinUkv9640tjoO0
+g0oyCiWFpJLJ8WOF4j7vmZUWuSS3VthlB+MLWlOZ5zACyPyWPo4Z2noHaYjfiQxB
+H8r5GJtQiJGapgWRbeyI+m837bjimpz6V1AGebHvf+zd1Lj+zDOczp38PqIGUbmA
+vfKCj+ILMS46wYjjHTvCG5WSCG/Skker2HAJM2cNcEPmQqAOpAkmFQ2G46bXB4rB
+Xh9dNZB52U9QkyPFHKrnNn400B/xBGNKoyTSYbLQEwIDAQABoyUwIzAhBgNVHREE
+GjAYhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4ICAQAz
+qHfXZGJ7fBZPEvOhAHrPyNTrf66tqNavGG+xkSIZ8R58SaS2JHQO/YvmX1vZ+kHI
+AM1DiqEFiT2/yeuh1OrzSN58L5n6mHxfkaCDFW3y7dmm8P34G36CqRY2YxiXH20Z
+pr+rAz2rVoopWxpsIY0hzuXytBF/ZDGLNv6NsrJzK6joM+WM23ugQ9krOy79whf5
+rrvVERygbnaTP+gXOrc1KePaw5YFEdGAdbOHxR2/1j+xvyNtdO5eqZr78SX81dr3
+YE6O3NHz+aHf3zJ5f0cXABSe9ZDqCnBaBvF3vj5Hd0BGdZ5TuM+tKLEQDCivdC2K
+89cthsPpCqTzednw3Q/mEuIqyJg6F6grQR1/qapMf8OlVa6LQXhTL9RVe6M6sg3u
+DuAnrdrFDcmxP2dGL4oycxA2t82lnJ3vjfl329PQmZ3iEVQJDuitQGsymv0nnqos
+S9YCqPpTnk6ADnv9RLqHuCyKJLgERy5RO/pMM9rjB0mqtNtSQgVW55Or2byFL1qR
+IKQIQWneP7VLAXaNKUQNbyDzTCTo7QZTHivDgC03E/36vKz2wWbcl4u/oBxkEa+H
+LORk7RNnXQJRXQms7rydJQk9osBWio0hsuZddkwuetUzkverWOSdofqAtN+xZUBt
+lZYE+em29aBd6/wektxqJriVZxpaQxyrSMKF+jQtmA==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE REQUEST-----
+MIICyzCCAbMCAQAwOjELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJbmMuMRgw
+FgYDVQQDDA9rZWEtc2VydmVyLWFkZHIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQC95FGAg3RLyh4FdVogSewbUMq4PMoqougt0FA1+hmsrMMsgw3OVpvG
+7bSHzZj85TTYR0TNqv3w6y3OCUhaKYoVUjQ6aLQC6KdSS/3rjS2Og7SDSjIKJYWk
+ksnxY4XiPu+ZlRa5JLdW2GUH4wtaU5nnMALI/JY+jhnaegdpiN+JDEEfyvkYm1CI
+kZqmBZFt7Ij6bzftuOKanPpXUAZ5se9/7N3UuP7MM5zOnfw+ogZRuYC98oKP4gsx
+LjrBiOMdO8IblZIIb9KSR6vYcAkzZw1wQ+ZCoA6kCSYVDYbjptcHisFeH101kHnZ
+T1CTI8Ucquc2fjTQH/EEY0qjJNJhstATAgMBAAGgTDBKBgkqhkiG9w0BCQ4xPTA7
+MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMCEGA1UdEQQaMBiHBH8AAAGHEAAAAAAA
+AAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggEBADlAkM7Vt3acIbgx9uz/nzEU
+biTUETzQnCU/mJZU+F8nuZtIlH9TAej4oT0J1uBuneGdkgGSm3lONUNxYJ7Uz8dm
+wyudv4cpvtacAzPqZNb0aapX3qD9/lUbXfReoOUmt+asdmF2ncmn3l465ercxtUg
+zhbU5uQUEk7C7f4OZQ3b08yG+tblFhpO7Xm4JD6nJk9iQ6gB4WBUDSr7mdm7PMmV
+T8xesD7lDZVjSdXql9p/6YxJJR3360jycLXeTQbom6gfvsfQcs91yfGHRel2yoDx
+ZBcmjfkYK7mwagpB/QCsZDuC4cxZyFM7lV/ukIysviW7WzrtT9mvfTEcTqmPsPU=
+-----END CERTIFICATE REQUEST-----
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIID+TCCAeGgAwIBAgIBFDANBgkqhkiG9w0BAQsFADAxMQswCQYDVQQGEwJVUzER
+MA8GA1UECgwISVNDIEluYy4xDzANBgNVBAMMBmtlYS1jYTAeFw0yMTAyMjEyMTEz
+MzZaFw0zMTAyMTkyMTEzMzZaMDUxCzAJBgNVBAYTAlVTMREwDwYDVQQKDAhJU0Mg
+SW5jLjETMBEGA1UEAwwKa2VhLXNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAL3kUYCDdEvKHgV1WiBJ7BtQyrg8yiqi6C3QUDX6GayswyyDDc5W
+m8bttIfNmPzlNNhHRM2q/fDrLc4JSFopihVSNDpotALop1JL/euNLY6DtINKMgol
+haSSyfFjheI+75mVFrkkt1bYZQfjC1pTmecwAsj8lj6OGdp6B2mI34kMQR/K+Rib
+UIiRmqYFkW3siPpvN+244pqc+ldQBnmx73/s3dS4/swznM6d/D6iBlG5gL3ygo/i
+CzEuOsGI4x07whuVkghv0pJHq9hwCTNnDXBD5kKgDqQJJhUNhuOm1weKwV4fXTWQ
+edlPUJMjxRyq5zZ+NNAf8QRjSqMk0mGy0BMCAwEAAaMYMBYwFAYDVR0RBA0wC4IJ
+bG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4ICAQBXVoyYSkLjFHKwAutjN8E0SWdX
+tBuEPtYwB9lZrGx+B36x4dufxl5NBeUvNLvmJrJlZC4tnrdXzWQBTNX1wKjaNcbM
+nNMvcczFUo/H7mJJys3vVxQx2kCkMwjgOjA/VsFjcgGF5zK630o2+0fdLYEmoe2J
+OgOz304HccOS8j888p0Sfh4y3v4ZvvZ2uwrWXjVtehGa1Yy6iBNrrczmiVRBcOrh
+GSw4kw+p+BuLZ2VK98DWD5FzW1+9+a7pEJA5Zt/ru88wm+/FK5SpysIykq/2CY6G
+pHyBh361es+4gRobg0ApSkldqmd3TubWyQj9zXV98qghhyT0DuQN3KIAF/RMloq2
+dQHVK6a6h7hPk876/FSGILwKw3yxaXFYmkoUpv2bnEBtMvNYgrkoxp6zpxFforKa
+VVz6NhpkKg1iG4wEc7rot60IlRUfqPFX68sQfkcOXezuj6Qdkl41sfBSQIyUcPkH
+OOQ9Mi1Rn/pPMm/kHXwJthcuVcLP006eS5zFaU5ejicx+nT2L0YS/eyNygq6jubJ
+4Xm2QcX2be3LNyWwiGWPw1CqOCxpGFIgY1Z9cyORGL12KyZ5sPxFdwUWI2RTXLOn
+mjDYzyR8cByql0QZSO7neH/QSrQyfVeDxawbWJCK9VimAKqxUXWuAqjqtfF42XGM
+xbwIHFtwsd+04XA8xQ==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN CERTIFICATE REQUEST-----
+MIICuTCCAaECAQAwNTELMAkGA1UEBhMCVVMxETAPBgNVBAoMCElTQyBJbmMuMRMw
+EQYDVQQDDAprZWEtc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAveRRgIN0S8oeBXVaIEnsG1DKuDzKKqLoLdBQNfoZrKzDLIMNzlabxu20h82Y
+/OU02EdEzar98OstzglIWimKFVI0Omi0AuinUkv9640tjoO0g0oyCiWFpJLJ8WOF
+4j7vmZUWuSS3VthlB+MLWlOZ5zACyPyWPo4Z2noHaYjfiQxBH8r5GJtQiJGapgWR
+beyI+m837bjimpz6V1AGebHvf+zd1Lj+zDOczp38PqIGUbmAvfKCj+ILMS46wYjj
+HTvCG5WSCG/Skker2HAJM2cNcEPmQqAOpAkmFQ2G46bXB4rBXh9dNZB52U9QkyPF
+HKrnNn400B/xBGNKoyTSYbLQEwIDAQABoD8wPQYJKoZIhvcNAQkOMTAwLjAJBgNV
+HRMEAjAAMAsGA1UdDwQEAwIF4DAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZI
+hvcNAQELBQADggEBAECqICoEZb0XeGwoBedtG2Exb4RUeoTAfL24q5a8cOtv0+Mw
+i7y9LNihtRqP2kzhoZ7IhzSUZGVuh4BIUywpJHuWfM9b+fe+hxSGdqCeULKS3InK
+4RWRh9jr12L7hEKfAG7VtL03/+Lm5DHLr47X6RkeZ5GwP29qqLwJcrK9qeFi26Bs
+TrEafPInhF7PgyFjH2YVZVotNaOFMRvwEQwAMtuF7SAqRHr+8VHXP3yi9UjHvxRs
+BpbVD6fEWNkLLJhoSqERgjWnsFlU3O+kj9R+iKA+6arxr4d+HS+dyYitFtVJaR6C
+0+De9msTbJmn+2mu4zQ09Sdf0pN5lb/I3pgcbLU=
+-----END CERTIFICATE REQUEST-----
--- /dev/null
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAveRRgIN0S8oeBXVaIEnsG1DKuDzKKqLoLdBQNfoZrKzDLIMN
+zlabxu20h82Y/OU02EdEzar98OstzglIWimKFVI0Omi0AuinUkv9640tjoO0g0oy
+CiWFpJLJ8WOF4j7vmZUWuSS3VthlB+MLWlOZ5zACyPyWPo4Z2noHaYjfiQxBH8r5
+GJtQiJGapgWRbeyI+m837bjimpz6V1AGebHvf+zd1Lj+zDOczp38PqIGUbmAvfKC
+j+ILMS46wYjjHTvCG5WSCG/Skker2HAJM2cNcEPmQqAOpAkmFQ2G46bXB4rBXh9d
+NZB52U9QkyPFHKrnNn400B/xBGNKoyTSYbLQEwIDAQABAoIBAHYZ4nbDSzk346QR
+cJRUZXw3q10ascICv9R+kuR/QCic6mZsu9FxHEGE5ZrwzA29oDhDWw9vIbENE9fo
+/g7VdwP7tG//XrXQbQBKMwqlK4hPcZ2WL7kPzSamBOMUutgTvIEQsKlFpKFmxK87
+DnyHMlkPCyxlrCwLxRiUfLeuYRiiau/3mAHUneJiYTL+pqQVkeznSkWq1cYp9A7n
+Gy4+3lxzyj3ru///S28V4UShh53PgPD5ghWKPqLJYbS6Zc1T8c0mtg/cbwZBUyuX
+W8AkT/ifYuJEBn3oQWluI2acwV7Yy+onRgFOsfZwUiX1oQ7keNh7D5+5XJ6CpZxK
+xtIpw0ECgYEA8wW6O07AQfA0dNNyMbHijfVabpeG1FKfqZeIhAnbYLwU1DJiipHl
+fyNDo7wM0AM27puohMAjo16ZfacUseruIiUSvv8bcWQE9g3XN7RGqpfJHHPlRI2V
+WT0iHEkjkMOkq4viufWcxGz/nw4BZERBnYmAOANmbwXX7ZnVbCnrjEUCgYEAyAhD
+PPNUTQGTcME98lVPohHvtekuaqA415otCtHA330I4mG8I3XGYbYymXYnmbIzy0KH
++ZjMZdTXiWmqPYIh0P7ZOeXKXNe8ZTedCwfX+1wGpjk01KCIzpdoS8X5WeN23/1t
+hoF+HTKdhRBQte68WFD36Dtb0r1Hwe+IKC8h7HcCgYB0i0mdSY3v0UcGw6Re6qTw
+WTqOEMLLLfh9tzrzv1pikLAYdzVEqOT7TKkSa4tlcjU0xpdRWmd84FARrz/Adx7O
+ZyMPT34UqderPEX648yD4RjEOVw4vQFjc2rZT8XrlbdxwTrw3TXaCT+pQmkucYFa
+EGfZ9N953L6Jpp1wKsZYVQKBgEwaiKpZ0YryvIu7mbvnJUL+G/tT2isLBlVQ/S4O
+m5jr00N997xuBKoMTbgBMhPRrs74Yw9dSPa9QbuwDesU5ZTEQRU8Df/AvJatz/vw
+YgXp/0Wioiz7XtFq3W1mxvWiCwoxO0hfYAHvzepgSLTPPa1EMO2UF91X0kNAxMa1
+F+0FAoGAAIcCoN3PazFWSsNMO4EfZf4VUgnTg9Dh3mMH8M3hEGybISSVKz5NILBC
+OKRKNLPLuj4TwTcurelNjMOUvkd/+yQgu1B9ImNuHdSvJjS9TzWCgZ26Q16woMzv
+yKeky514sst/1LtWuwiitmGS0rpKf3vIlkqcUE9WcLd3Hy/PxKg=
+-----END RSA PRIVATE KEY-----
--- /dev/null
+#
+# OpenSSL example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+# Policies used by the TSA examples.
+tsa_policy1 = 1.2.3.4.1
+tsa_policy2 = 1.2.3.4.5.6
+tsa_policy3 = 1.2.3.4.5.7
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./demoCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several ctificates with same subject.
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Comment out the following two lines for the "traditional"
+# (and highly broken) format.
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+# Extension copying option: use with caution.
+# copy_extensions = copy
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crlnumber must also be commented out to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = sha256 # use SHA-256 by default
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 1024
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString (PKIX recommendation before 2004)
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
+string_mask = utf8only
+
+req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = AU
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+#stateOrProvinceName_default = Some-State
+
+localityName = Locality Name (eg, city)
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = Internet Widgits Pty Ltd
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+# This is required for TSA certificates.
+# extendedKeyUsage = critical,timeStamping
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = @alt_name
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ proxy_cert_ext ]
+# These extensions should be added when creating a proxy certificate
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+# This really needs to be in place for it to be a proxy certificate.
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
+
+####################################################################
+[ tsa ]
+
+default_tsa = tsa_config1 # the default TSA section
+
+[ tsa_config1 ]
+
+# These are used by the TSA reply generation only.
+dir = ./demoCA # TSA root directory
+serial = $dir/tsaserial # The current serial number (mandatory)
+crypto_device = builtin # OpenSSL engine to use for signing
+signer_cert = $dir/tsacert.pem # The TSA signing certificate
+ # (optional)
+certs = $dir/cacert.pem # Certificate chain to include in reply
+ # (optional)
+signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
+
+default_policy = tsa_policy1 # Policy if request did not specify it
+ # (optional)
+other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
+digests = md5, sha1 # Acceptable message digests (mandatory)
+accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
+clock_precision_digits = 0 # number of digits after dot. (optional)
+ordering = yes # Is ordering defined for timestamps?
+ # (optional, default: no)
+tsa_name = yes # Must the TSA name be included in the reply?
+ # (optional, default: no)
+ess_cert_id_chain = no # Must the ESS cert id chain be included?
+ # (optional, default: no)
+
+[ alt_name ]
+IP.1 = 127.0.0.1
+IP.2 = ::1
--- /dev/null
+#
+# OpenSSL example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+# Policies used by the TSA examples.
+tsa_policy1 = 1.2.3.4.1
+tsa_policy2 = 1.2.3.4.5.6
+tsa_policy3 = 1.2.3.4.5.7
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./demoCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several ctificates with same subject.
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Comment out the following two lines for the "traditional"
+# (and highly broken) format.
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+# Extension copying option: use with caution.
+# copy_extensions = copy
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crlnumber must also be commented out to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = sha256 # use SHA-256 by default
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 1024
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString (PKIX recommendation before 2004)
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
+string_mask = utf8only
+
+req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = AU
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+#stateOrProvinceName_default = Some-State
+
+localityName = Locality Name (eg, city)
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = Internet Widgits Pty Ltd
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+# This is required for TSA certificates.
+# extendedKeyUsage = critical,timeStamping
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = @alt_name
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ proxy_cert_ext ]
+# These extensions should be added when creating a proxy certificate
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+# This really needs to be in place for it to be a proxy certificate.
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
+
+####################################################################
+[ tsa ]
+
+default_tsa = tsa_config1 # the default TSA section
+
+[ tsa_config1 ]
+
+# These are used by the TSA reply generation only.
+dir = ./demoCA # TSA root directory
+serial = $dir/tsaserial # The current serial number (mandatory)
+crypto_device = builtin # OpenSSL engine to use for signing
+signer_cert = $dir/tsacert.pem # The TSA signing certificate
+ # (optional)
+certs = $dir/cacert.pem # Certificate chain to include in reply
+ # (optional)
+signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
+
+default_policy = tsa_policy1 # Policy if request did not specify it
+ # (optional)
+other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
+digests = md5, sha1 # Acceptable message digests (mandatory)
+accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
+clock_precision_digits = 0 # number of digits after dot. (optional)
+ordering = yes # Is ordering defined for timestamps?
+ # (optional, default: no)
+tsa_name = yes # Must the TSA name be included in the reply?
+ # (optional, default: no)
+ess_cert_id_chain = no # Must the ESS cert id chain be included?
+ # (optional, default: no)
+
+[ alt_name ]
+DNS.1 = localhost
--- /dev/null
+//
+// client.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#include <cstdlib>
+#include <cstring>
+#include <functional>
+#include <iostream>
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+
+using boost::asio::ip::tcp;
+using std::placeholders::_1;
+using std::placeholders::_2;
+
+inline std::string CA_(const std::string& filename) {
+ return (std::string(TEST_CA_DIR) + "/" + filename);
+}
+
+enum { max_length = 1024 };
+
+class client
+{
+public:
+ client(boost::asio::io_service& io_context,
+ boost::asio::ssl::context& context,
+ const tcp::resolver::results_type& endpoints)
+ : socket_(io_context, context)
+ {
+ socket_.set_verify_mode(boost::asio::ssl::verify_peer |
+ boost::asio::ssl::verify_fail_if_no_peer_cert);
+ socket_.set_verify_callback(
+ std::bind(&client::verify_certificate, this, _1, _2));
+
+ connect(endpoints);
+ }
+
+private:
+ bool verify_certificate(bool preverified,
+ boost::asio::ssl::verify_context& ctx)
+ {
+ // The verify callback can be used to check whether the certificate that is
+ // being presented is valid for the peer. For example, RFC 2818 describes
+ // the steps involved in doing this for HTTPS. Consult the OpenSSL
+ // documentation for more details. Note that the callback is called once
+ // for each certificate in the certificate chain, starting from the root
+ // certificate authority.
+
+ // In this example we will simply print the certificate's subject name.
+ char subject_name[256];
+ X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
+ X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
+ std::cout << "Verifying " << subject_name << "\n";
+
+ return preverified;
+ }
+
+ void connect(const tcp::resolver::results_type& endpoints)
+ {
+ boost::asio::async_connect(socket_.lowest_layer(), endpoints,
+ [this](const boost::system::error_code& error,
+ const tcp::endpoint& /*endpoint*/)
+ {
+ if (!error)
+ {
+ handshake();
+ }
+ else
+ {
+ std::cout << "Connect failed: " << error.message() << "\n";
+ }
+ });
+ }
+
+ void handshake()
+ {
+ socket_.async_handshake(boost::asio::ssl::stream_base::client,
+ [this](const boost::system::error_code& error)
+ {
+ if (!error)
+ {
+ send_request();
+ }
+ else
+ {
+ std::cout << "Handshake failed: " << error.message() << "\n";
+ }
+ });
+ }
+
+ void send_request()
+ {
+ std::cout << "Enter message: ";
+ std::cin.getline(request_, max_length);
+ size_t request_length = std::strlen(request_);
+
+ boost::asio::async_write(socket_,
+ boost::asio::buffer(request_, request_length),
+ [this](const boost::system::error_code& error, std::size_t length)
+ {
+ if (!error)
+ {
+ receive_response(length);
+ }
+ else
+ {
+ std::cout << "Write failed: " << error.message() << "\n";
+ }
+ });
+ }
+
+ void receive_response(std::size_t length)
+ {
+ boost::asio::async_read(socket_,
+ boost::asio::buffer(reply_, length),
+ [this](const boost::system::error_code& error, std::size_t length)
+ {
+ if (!error)
+ {
+ std::cout << "Reply: ";
+ std::cout.write(reply_, length);
+ std::cout << "\n";
+ }
+ else
+ {
+ std::cout << "Read failed: " << error.message() << "\n";
+ }
+ });
+ }
+
+ boost::asio::ssl::stream<tcp::socket> socket_;
+ char request_[max_length];
+ char reply_[max_length];
+};
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ if (argc != 3)
+ {
+ std::cerr << "Usage: client <host> <port>\n";
+ return 1;
+ }
+
+ boost::asio::io_service io_context;
+
+ tcp::resolver resolver(io_context);
+ auto endpoints = resolver.resolve(argv[1], argv[2]);
+
+ boost::asio::ssl::context ctx(boost::asio::ssl::context::tls);
+ ctx.load_verify_file(CA_("kea-ca.crt"));
+ ctx.use_certificate_chain_file(CA_("kea-client.crt"));
+ ctx.use_private_key_file(CA_("kea-client.key"),
+ boost::asio::ssl::context::pem);
+
+ client c(io_context, ctx, endpoints);
+
+ io_context.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
--- /dev/null
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+// Use the cpp03 version because the cpp11 version does not compile with
+// some g++ e.g. on Fedora 33.
+
+#include <cstdlib>
+#include <iostream>
+#include <boost/bind/bind.hpp>
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+
+inline std::string CA_(const std::string& filename) {
+ return (std::string(TEST_CA_DIR) + "/" + filename);
+}
+
+typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
+
+class session
+{
+public:
+ session(boost::asio::io_service& io_context,
+ boost::asio::ssl::context& context)
+ : socket_(io_context, context)
+ {
+ }
+
+ ssl_socket::lowest_layer_type& socket()
+ {
+ return socket_.lowest_layer();
+ }
+
+ void start()
+ {
+ socket_.async_handshake(boost::asio::ssl::stream_base::server,
+ boost::bind(&session::handle_handshake, this,
+ boost::asio::placeholders::error));
+ }
+
+ void handle_handshake(const boost::system::error_code& error)
+ {
+ if (!error)
+ {
+ socket_.async_read_some(boost::asio::buffer(data_, max_length),
+ boost::bind(&session::handle_read, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+ }
+ else
+ {
+ std::cerr << "handshake error '" << error.message() << "'\n";
+ delete this;
+ }
+ }
+
+ void handle_read(const boost::system::error_code& error,
+ size_t bytes_transferred)
+ {
+ if (!error)
+ {
+ boost::asio::async_write(socket_,
+ boost::asio::buffer(data_, bytes_transferred),
+ boost::bind(&session::handle_write, this,
+ boost::asio::placeholders::error));
+ }
+ else
+ {
+ delete this;
+ }
+ }
+
+ void handle_write(const boost::system::error_code& error)
+ {
+ if (!error)
+ {
+ socket_.async_read_some(boost::asio::buffer(data_, max_length),
+ boost::bind(&session::handle_read, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+ }
+ else
+ {
+ delete this;
+ }
+ }
+
+private:
+ ssl_socket socket_;
+ enum { max_length = 1024 };
+ char data_[max_length];
+};
+
+class server
+{
+public:
+ server(boost::asio::io_service& io_context, unsigned short port)
+ : io_context_(io_context),
+ acceptor_(io_context,
+ boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
+ context_(boost::asio::ssl::context::tls)
+ {
+ //context_.set_options(
+ // boost::asio::ssl::context::default_workarounds
+ // | boost::asio::ssl::context::no_sslv2
+ // | boost::asio::ssl::context::single_dh_use);
+ //context_.set_password_callback(boost::bind(&server::get_password, this));
+ context_.set_verify_mode(boost::asio::ssl::verify_peer |
+ boost::asio::ssl::verify_fail_if_no_peer_cert);
+ context_.load_verify_file(CA_("kea-ca.crt"));
+ context_.use_certificate_chain_file(CA_("kea-server.crt"));
+ context_.use_private_key_file(CA_("kea-server.key"),
+ boost::asio::ssl::context::pem);
+ //context_.use_tmp_dh_file("dh2048.pem");
+
+ start_accept();
+ }
+
+ void start_accept()
+ {
+ session* new_session = new session(io_context_, context_);
+ acceptor_.async_accept(new_session->socket(),
+ boost::bind(&server::handle_accept, this, new_session,
+ boost::asio::placeholders::error));
+ }
+
+ void handle_accept(session* new_session,
+ const boost::system::error_code& error)
+ {
+ if (!error)
+ {
+ new_session->start();
+ }
+ else
+ {
+ delete new_session;
+ }
+
+ start_accept();
+ }
+
+private:
+ boost::asio::io_service& io_context_;
+ boost::asio::ip::tcp::acceptor acceptor_;
+ boost::asio::ssl::context context_;
+};
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ if (argc != 2)
+ {
+ std::cerr << "Usage: server <port>\n";
+ return 1;
+ }
+
+ boost::asio::io_service io_context;
+
+ using namespace std; // For atoi.
+ server s(io_context, atoi(argv[1]));
+
+ io_context.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/testutils/test_tls.h>
+
+namespace isc {
+namespace asiolink {
+namespace test {
+
+/// @brief Configure the TLS server.
+void configServer(TlsContextPtr& ctx) {
+ std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt");
+ std::string cert(std::string(TEST_CA_DIR) + "/kea-server.crt");
+ std::string key(std::string(TEST_CA_DIR) + "/kea-server.key");
+ TlsContext::configure(ctx, TlsRole::SERVER, ca, cert, key, true);
+}
+
+/// @brief Configure the TLS client.
+void configClient(TlsContextPtr& ctx) {
+ std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt");
+ std::string cert(std::string(TEST_CA_DIR) + "/kea-client.crt");
+ std::string key(std::string(TEST_CA_DIR) + "/kea-client.key");
+ TlsContext::configure(ctx, TlsRole::CLIENT, ca, cert, key, true);
+}
+
+/// @brief Configure another TLS client.
+void configOther(TlsContextPtr& ctx) {
+ std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt");
+ std::string cert(std::string(TEST_CA_DIR) + "/kea-other.crt");
+ std::string key(std::string(TEST_CA_DIR) + "/kea-other.key");
+ TlsContext::configure(ctx, TlsRole::CLIENT, ca, cert, key, true);
+}
+
+/// @brief Configure self-signed TLS client.
+void configSelf(TlsContextPtr& ctx) {
+ std::string ca(std::string(TEST_CA_DIR) + "/kea-ca.crt");
+ std::string cert(std::string(TEST_CA_DIR) + "/kea-self.crt");
+ std::string key(std::string(TEST_CA_DIR) + "/kea-self.key");
+ TlsContext::configure(ctx, TlsRole::CLIENT, ca, cert, key, true);
+}
+
+} // end of namespace isc::asiolink::test
+} // end of namespace isc::asiolink
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef TEST_TLS_H
+#define TEST_TLS_H
+
+#ifndef CONFIG_H_WAS_INCLUDED
+#error config.h must be included before test_tls.h
+#endif
+
+#include <asiolink/crypto_tls.h>
+#include <asiolink/botan_tls.h>
+#include <asiolink/openssl_tls.h>
+
+#include <string>
+
+namespace isc {
+namespace asiolink {
+namespace test {
+
+/// @brief Configure the TLS server.
+void configServer(TlsContextPtr& ctx);
+
+/// @brief Configure the TLS client.
+void configClient(TlsContextPtr& ctx);
+
+/// @brief Configure another TLS client.
+void configOther(TlsContextPtr& ctx);
+
+/// @brief Configure self-signed TLS client.
+void configSelf(TlsContextPtr& ctx);
+
+} // end of namespace isc::asiolink::test
+} // end of namespace isc::asiolink
+} // end of namespace isc
+
+#endif // TEST_TLS_H
--- /dev/null
+// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef TLS_ACCEPTOR_H
+#define TLS_ACCEPTOR_H
+
+#ifndef BOOST_ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/io_acceptor.h>
+#include <asiolink/io_service.h>
+#include <asiolink/io_socket.h>
+#include <asiolink/tcp_acceptor.h>
+#include <asiolink/tcp_endpoint.h>
+#include <asiolink/tcp_socket.h>
+#include <asiolink/tls_socket.h>
+#include <boost/shared_ptr.hpp>
+#include <netinet/in.h>
+
+namespace isc {
+namespace asiolink {
+
+/// @brief Provides a service for accepting new TLS connections.
+///
+/// @tparam C Acceptor callback type.
+template<typename C>
+class TLSAcceptor : public TCPAcceptor<C> {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param io_service IO service.
+ explicit TLSAcceptor(IOService& io_service) : TCPAcceptor<C>(io_service) {
+ }
+
+ /// @brief Destructor.
+ virtual ~TLSAcceptor() { }
+
+ /// @brief Asynchronously accept new connection.
+ ///
+ /// This method accepts new connection into the specified socket. When the
+ /// new connection arrives or an error occurs the specified callback function
+ /// is invoked.
+ ///
+ /// @param socket Socket into which connection should be accepted.
+ /// @param callback Callback function to be invoked when the new connection
+ /// arrives.
+ /// @tparam SocketCallback Type of the callback for the @ref TLSSocket.
+ template<typename SocketCallback>
+ void asyncAccept(const TLSSocket<SocketCallback>& socket, C& callback) {
+ TCPAcceptor<C>::acceptor_->async_accept(socket.getASIOSocket(), callback);
+ }
+};
+
+} // namespace asiolink
+} // namespace isc
+
+#endif
--- /dev/null
+// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef TLS_SOCKET_H
+#define TLS_SOCKET_H
+
+#ifndef BOOST_ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include <asiolink/crypto_tls.h>
+#include <asiolink/botan_tls.h>
+#include <asiolink/openssl_tls.h>
+#include <asiolink/tcp_socket.h>
+
+#include <boost/noncopyable.hpp>
+
+namespace isc {
+namespace asiolink {
+
+/// @brief The @c TLSSocket class is a concrete derived class of @c IOAsioSocket
+/// that represents a TLS socket.
+///
+/// @tparam C Callback type.
+template <typename C>
+class TLSSocket : public IOAsioSocket<C>, private boost::noncopyable {
+public:
+
+ /// @brief Constructor from a TLS stream.
+ ///
+ /// It is assumed that the caller will open and close the stream,
+ /// so these operations are a no-op for that stream.
+ ///
+ /// @param stream The TLS stream.
+ TLSSocket(TlsStream<C>& stream);
+
+ /// @brief Constructor.
+ ///
+ /// Used when the TLSSocket is being asked to manage its own internal
+ /// socket. In this case, the open() and close() methods are used.
+ ///
+ /// @param service I/O Service object used to manage the socket.
+ /// @param context Pointer to TLS context.
+ TLSSocket(IOService& service, TlsContextPtr context);
+
+ /// @brief Destructor.
+ virtual ~TLSSocket() { }
+
+ /// @brief Return file descriptor of underlying socket.
+ virtual int getNative() const {
+#if BOOST_VERSION < 106600
+ return (socket_.native());
+#else
+ return (socket_.native_handle());
+#endif
+ }
+
+ /// @brief Return protocol of socket.
+ virtual int getProtocol() const {
+ return (IPPROTO_TCP);
+ }
+
+ /// @brief Is "open()" synchronous predicate.
+ ///
+ /// Indicates that the opening of a TLS socket is asynchronous.
+ virtual bool isOpenSynchronous() const {
+ return (false);
+ }
+
+ /// @brief Checks if the connection is usable.
+ ///
+ /// The connection is usable if the socket is open and the peer has not
+ /// closed its connection.
+ ///
+ /// @return true if the connection is usable.
+ bool isUsable() const {
+ // If the socket is open it doesn't mean that it is still
+ // usable. The connection could have been closed on the other
+ // end. We have to check if we can still use this socket.
+ if (socket_.is_open()) {
+ // Remember the current non blocking setting.
+ const bool non_blocking_orig = socket_.non_blocking();
+
+ // Set the socket to non blocking mode. We're going to
+ // test if the socket returns would_block status on the
+ // attempt to read from it.
+ socket_.non_blocking(true);
+
+ // Use receive with message peek flag to avoid removing
+ // the data awaiting to be read.
+ char data[2];
+ int err = 0;
+ int cc = recv(getNative(), data, sizeof(data), MSG_PEEK);
+ if (cc < 0) {
+ // Error case.
+ err = errno;
+ } else if (cc == 0) {
+ // End of file.
+ err = -1;
+ }
+
+ // Revert the original non_blocking flag on the socket.
+ socket_.non_blocking(non_blocking_orig);
+
+ // If the connection is alive we'd typically get
+ // would_block status code. If there are any data that
+ // haven't been read we may also get success status. We're
+ // guessing that try_again may also be returned by some
+ // implementations in some situations. Any other error
+ // code indicates a problem with the connection so we
+ // assume that the connection has been closed.
+ return ((err == 0) || (err == EAGAIN) || (err == EWOULDBLOCK));
+ }
+
+ return (false);
+ }
+
+ /// @brief Open Socket.
+ ///
+ /// Opens the TLS socket. This is an asynchronous operation, completion of
+ /// which will be signalled via a call to the callback function.
+ ///
+ /// @param endpoint Endpoint to which the socket will connect.
+ /// @param callback Callback object.
+ virtual void open(const IOEndpoint* endpoint, C& callback);
+
+ /// @brief Perform Handshake.
+ ///
+ /// Perform the TLS handshake. This is an asynchronous operation,
+ /// completion of which will be signalled via a call to the callback
+ /// function.
+ ///
+ /// @param callback Callback object.
+ virtual void handshake(C& callback);
+
+ /// @brief Send Asynchronously.
+ ///
+ /// Calls the underlying socket's async_send() method to send a
+ /// packet of data asynchronously to the remote endpoint. The
+ /// callback will be called on completion.
+ ///
+ /// @param data Data to send.
+ /// @param length Length of data to send.
+ /// @param endpoint Target of the send. (Unused for a TLS socket because
+ /// that was determined when the connection was opened.)
+ /// @param callback Callback object.
+ /// @throw BufferTooLarge on attempt to send a buffer larger than 64kB.
+ virtual void asyncSend(const void* data, size_t length,
+ const IOEndpoint* endpoint, C& callback);
+
+ /// @brief Send Asynchronously without count.
+ ///
+ /// This variant of the method sends data over the TLS socket without
+ /// preceding the data with a data count. Eventually, we should migrate
+ /// the virtual method to not insert the count but there are existing
+ /// classes using the count. Once this migration is done, the existing
+ /// virtual method should be replaced by this method.
+ ///
+ /// @param data Data to send.
+ /// @param length Length of data to send.
+ /// @param callback Callback object.
+ /// @throw BufferTooLarge on attempt to send a buffer larger than 64kB.
+ void asyncSend(const void* data, size_t length, C& callback);
+
+ /// @brief Receive Asynchronously.
+ ///
+ /// Calls the underlying socket's async_receive() method to read a packet
+ /// of data from a remote endpoint. Arrival of the data is signalled via a
+ /// call to the callback function.
+ ///
+ /// @param data Buffer to receive incoming message.
+ /// @param length Length of the data buffer.
+ /// @param offset Offset into buffer where data is to be put.
+ /// @param endpoint Source of the communication.
+ /// @param callback Callback object.
+ virtual void asyncReceive(void* data, size_t length, size_t offset,
+ IOEndpoint* endpoint, C& callback);
+
+ /// @brief Process received data packet.
+ ///
+ /// See the description of IOAsioSocket::receiveComplete for a complete
+ /// description of this method.
+ ///
+ /// @param staging Pointer to the start of the staging buffer.
+ /// @param length Amount of data in the staging buffer.
+ /// @param cumulative Amount of data received before the staging buffer is
+ /// processed.
+ /// @param offset Unused.
+ /// @param expected unused.
+ /// @param outbuff Output buffer. Data in the staging buffer is be copied
+ /// to this output buffer in the call.
+ ///
+ /// @return Always true.
+ virtual bool processReceivedData(const void* staging, size_t length,
+ size_t& cumulative, size_t& offset,
+ size_t& expected,
+ isc::util::OutputBufferPtr& outbuff);
+
+ /// @brief Cancel I/O On Socket.
+ virtual void cancel();
+
+ /// @brief Close socket.
+ virtual void close();
+
+ /// @brief TLS shutdown.
+ virtual void shutdown(C& callback);
+
+ /// @brief Returns reference to the underlying ASIO socket.
+ ///
+ /// @return Reference to underlying ASIO socket.
+ virtual typename TlsStream<C>::lowest_layer_type& getASIOSocket() const {
+ return (socket_);
+ }
+
+ /// @brief Returns reference to the underlying TLS stream.
+ ///
+ /// @return Reference to underlying TLS stream.
+ virtual TlsStream<C>& getTlsStream() const {
+ return (stream_);
+ }
+
+private:
+ /// Two variables to hold the stream - a stream and a pointer to it. This
+ /// handles the case where a stream is passed to the TLSSocket on
+ /// construction, or where it is asked to manage its own stream.
+
+ /// @brief Pointer to own stream.
+ std::unique_ptr<TlsStream<C>> stream_ptr_;
+
+ /// @brief TLS stream.
+ TlsStream<C>& stream_;
+
+ /// @brief Underlying TCP socket.
+ typename TlsStream<C>::lowest_layer_type& socket_;
+
+ /// TODO: Remove temporary buffer
+ /// The current implementation copies the buffer passed to asyncSend() into
+ /// a temporary buffer and precedes it with a two-byte count field. As
+ /// ASIO should really be just about sending and receiving data, the TCP
+ /// code should not do this. If the protocol using this requires a two-byte
+ /// count, it should add it before calling this code. (This may be best
+ /// achieved by altering isc::dns::buffer to have pairs of methods:
+ /// getLength()/getTCPLength(), getData()/getTCPData(), with the getTCPXxx()
+ /// methods taking into account a two-byte count field.)
+ ///
+ /// The option of sending the data in two operations, the count followed by
+ /// the data was discounted as that would lead to two callbacks which would
+ /// cause problems with the stackless coroutine code.
+
+ /// @brief Send buffer.
+ isc::util::OutputBufferPtr send_buffer_;
+};
+
+// Constructor - caller manages socket.
+
+template <typename C>
+TLSSocket<C>::TLSSocket(TlsStream<C>& stream) :
+ stream_ptr_(), stream_(stream),
+ socket_(stream_.lowest_layer()), send_buffer_() {
+}
+
+// Constructor - create socket on the fly.
+
+template <typename C>
+TLSSocket<C>::TLSSocket(IOService& service, TlsContextPtr context) :
+ stream_ptr_(new TlsStream<C>(service, context)),
+ stream_(*stream_ptr_), socket_(stream_.lowest_layer()), send_buffer_()
+{
+}
+
+// Open the socket.
+
+template <typename C> void
+TLSSocket<C>::open(const IOEndpoint* endpoint, C& callback) {
+ // If socket is open on this end but has been closed by the peer,
+ // we need to reconnect.
+ if (socket_.is_open() && !isUsable()) {
+ socket_.close();
+ }
+
+ // Ignore opens on already-open socket. Don't throw a failure because
+ // of uncertainties as to what precedes whan when using asynchronous I/O.
+ // At also allows us a treat a passed-in socket as a self-managed socket.
+ if (!socket_.is_open()) {
+ if (endpoint->getFamily() == AF_INET) {
+ socket_.open(boost::asio::ip::tcp::v4());
+ } else {
+ socket_.open(boost::asio::ip::tcp::v6());
+ }
+
+ // Set options on the socket:
+
+ // Reuse address - allow the socket to bind to a port even if the port
+ // is in the TIMED_WAIT state.
+ socket_.set_option(boost::asio::socket_base::reuse_address(true));
+ }
+
+ // Upconvert to a TCPEndpoint. We need to do this because although
+ // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it does not
+ // contain a method for getting at the underlying endpoint type - that is in
+ /// the derived class and the two classes differ on return type.
+ isc_throw_assert(endpoint->getProtocol() == IPPROTO_TCP);
+ const TCPEndpoint* tcp_endpoint =
+ static_cast<const TCPEndpoint*>(endpoint);
+
+ // Connect to the remote endpoint. On success, the handler will be
+ // called (with one argument - the length argument will default to
+ // zero).
+ socket_.async_connect(tcp_endpoint->getASIOEndpoint(), callback);
+}
+
+// Perform the handshake.
+
+template <typename C> void
+TLSSocket<C>::handshake(C& callback) {
+ if (!socket_.is_open()) {
+ isc_throw(SocketNotOpen, "attempt to perform handshake on "
+ "a TLS socket that is not open");
+ }
+ stream_.handshake(callback);
+}
+
+// Send a message. Should never do this if the socket is not open, so throw
+// an exception if this is the case.
+
+template <typename C> void
+TLSSocket<C>::asyncSend(const void* data, size_t length, C& callback)
+{
+ if (!socket_.is_open()) {
+ isc_throw(SocketNotOpen,
+ "attempt to send on a TLS socket that is not open");
+ }
+
+ try {
+ send_buffer_.reset(new isc::util::OutputBuffer(length));
+ send_buffer_->writeData(data, length);
+
+ // Send the data.
+ boost::asio::async_write(stream_,
+ boost::asio::buffer(send_buffer_->getData(),
+ send_buffer_->getLength()),
+ callback);
+ } catch (const boost::numeric::bad_numeric_cast&) {
+ isc_throw(BufferTooLarge,
+ "attempt to send buffer larger than 64kB");
+ }
+}
+
+template <typename C> void
+TLSSocket<C>::asyncSend(const void* data, size_t length,
+ const IOEndpoint*, C& callback)
+{
+ if (!socket_.is_open()) {
+ isc_throw(SocketNotOpen,
+ "attempt to send on a TLS socket that is not open");
+ }
+
+ // Need to copy the data into a temporary buffer and precede it with
+ // a two-byte count field.
+ // TODO: arrange for the buffer passed to be preceded by the count
+ try {
+ // Ensure it fits into 16 bits
+ uint16_t count = boost::numeric_cast<uint16_t>(length);
+
+ // Copy data into a buffer preceded by the count field.
+ send_buffer_.reset(new isc::util::OutputBuffer(length + 2));
+ send_buffer_->writeUint16(count);
+ send_buffer_->writeData(data, length);
+
+ // ... and send it
+ boost::asio::async_write(stream_,
+ boost::asio::buffer(send_buffer_->getData(),
+ send_buffer_->getLength()),
+ callback);
+ } catch (const boost::numeric::bad_numeric_cast&) {
+ isc_throw(BufferTooLarge,
+ "attempt to send buffer larger than 64kB");
+ }
+}
+
+// Receive a message. Note that the "offset" argument is used as an index
+// into the buffer in order to decide where to put the data. It is up to the
+// caller to initialize the data to zero
+template <typename C> void
+TLSSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
+ IOEndpoint* endpoint, C& callback)
+{
+ if (!socket_.is_open()) {
+ isc_throw(SocketNotOpen,
+ "attempt to receive from a TLS socket that is not open");
+ }
+
+ // Upconvert to a TCPEndpoint. We need to do this because although
+ // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
+ // does not contain a method for getting at the underlying endpoint
+ // type - that is in the derived class and the two classes differ on
+ // return type.
+ isc_throw_assert(endpoint->getProtocol() == IPPROTO_TCP);
+ TCPEndpoint* tcp_endpoint = static_cast<TCPEndpoint*>(endpoint);
+
+ // Write the endpoint details from the communications link. Ideally
+ // we should make IOEndpoint assignable, but this runs in to all sorts
+ // of problems concerning the management of the underlying Boost
+ // endpoint (e.g. if it is not self-managed, is the copied one
+ // self-managed?) The most pragmatic solution is to let Boost take care
+ // of everything and copy details of the underlying endpoint.
+ tcp_endpoint->getASIOEndpoint() = socket_.remote_endpoint();
+
+ // Ensure we can write into the buffer and if so, set the pointer to
+ // where the data will be written.
+ if (offset >= length) {
+ isc_throw(BufferOverflow, "attempt to read into area beyond end of "
+ "TCP receive buffer");
+ }
+ void* buffer_start =
+ static_cast<void*>(static_cast<uint8_t*>(data) + offset);
+
+ // ... and kick off the read.
+ stream_.async_read_some(boost::asio::buffer(buffer_start, length - offset),
+ callback);
+}
+
+// Is the receive complete?
+
+template <typename C> bool
+TLSSocket<C>::processReceivedData(const void* staging, size_t length,
+ size_t& cumulative, size_t& offset,
+ size_t& expected,
+ isc::util::OutputBufferPtr& outbuff)
+{
+ // Point to the data in the staging buffer and note how much there is.
+ const uint8_t* data = static_cast<const uint8_t*>(staging);
+ size_t data_length = length;
+
+ // Is the number is "expected" valid? It won't be unless we have received
+ // at least two bytes of data in total for this set of receives.
+ if (cumulative < 2) {
+
+ // "expected" is not valid. Did this read give us enough data to
+ // work it out?
+ cumulative += length;
+ if (cumulative < 2) {
+
+ // Nope, still not valid. This must have been the first packet and
+ // was only one byte long. Tell the fetch code to read the next
+ // packet into the staging buffer beyond the data that is already
+ // there so that the next time we are called we have a complete
+ // TCP count.
+ offset = cumulative;
+ return (false);
+ }
+
+ // Have enough data to interpret the packet count, so do so now.
+ expected = isc::util::readUint16(data, cumulative);
+
+ // We have two bytes less of data to process. Point to the start of the
+ // data and adjust the packet size. Note that at this point,
+ // "cumulative" is the true amount of data in the staging buffer, not
+ // "length".
+ data += 2;
+ data_length = cumulative - 2;
+ } else {
+
+ // Update total amount of data received.
+ cumulative += length;
+ }
+
+ // Regardless of anything else, the next read goes into the start of the
+ // staging buffer.
+ offset = 0;
+
+ // Work out how much data we still have to put in the output buffer. (This
+ // could be zero if we have just interpreted the TCP count and that was
+ // set to zero.)
+ if (expected >= outbuff->getLength()) {
+
+ // Still need data in the output packet. Copy what we can from the
+ // staging buffer to the output buffer.
+ size_t copy_amount = std::min(expected - outbuff->getLength(),
+ data_length);
+ outbuff->writeData(data, copy_amount);
+ }
+
+ // We can now say if we have all the data.
+ return (expected == outbuff->getLength());
+}
+
+// Cancel I/O on the socket. No-op if the socket is not open.
+
+template <typename C> void
+TLSSocket<C>::cancel() {
+ if (socket_.is_open()) {
+ socket_.cancel();
+ }
+}
+
+// TLS shutdown. Can be used for orderly close.
+
+template <typename C> void
+TLSSocket<C>::shutdown(C& callback) {
+ if (!socket_.is_open()) {
+ isc_throw(SocketNotOpen, "attempt to perform shutdown on "
+ "a TLS socket that is not open");
+ }
+ stream_.shutdown(callback);
+}
+
+// Close the socket down. Can only do this if the socket is open and we are
+// managing it ourself.
+
+template <typename C> void
+TLSSocket<C>::close() {
+ stream_.clear();
+ if (socket_.is_open() && stream_ptr_) {
+ socket_.close();
+ }
+}
+
+} // namespace asiolink
+} // namespace isc
+
+#endif // TLS_SOCKET_H