diff options
author | Francis Dupont <fdupont@isc.org> | 2024-08-30 11:34:23 +0200 |
---|---|---|
committer | Francis Dupont <fdupont@isc.org> | 2024-10-11 15:20:20 +0200 |
commit | 78737d8786060304081e38c6e22460eb9828aff6 (patch) | |
tree | 99b9f43b642f071980a1497145daff368a29b7ed | |
parent | [#3588] Improved no test logs (diff) | |
download | kea-78737d8786060304081e38c6e22460eb9828aff6.tar.xz kea-78737d8786060304081e38c6e22460eb9828aff6.zip |
[#3552] Updated Botan support
21 files changed, 460 insertions, 726 deletions
diff --git a/configure.ac b/configure.ac index fcf4bebe1b..0e44735fdf 100644 --- a/configure.ac +++ b/configure.ac @@ -1270,22 +1270,6 @@ AC_SUBST(PKGPYTHONDIR) AC_SUBST(PYTHON_PREFIX) AC_SUBST(PYTHON_EXEC_PREFIX) -# Decide if TLS is supported. -tls_support=no -if test "x${CRYPTO_NAME}" = "xOpenSSL"; then - tls_support=yes -fi -if test "x${CRYPTO_NAME}" = "xBotan" && test "x$BOTAN_BOOST" = "xyes"; then - tls_support=yes -fi - -# Decide if the shell TLS test can work. -ca_tls_test=no -if test "x$enable_shell" != "xno"; then - ca_tls_test="$tls_support" -fi -AM_CONDITIONAL(CA_TLS_TEST, test x$ca_tls_test != xno) - AC_ARG_WITH([sphinx], [AS_HELP_STRING([--with-sphinx=PATH],[path to sphinx-build tool])], [sphinx_path="$withval"]) @@ -1988,7 +1972,6 @@ ${CRYPTO_NAME}: CRYPTO_INCLUDES: ${CRYPTO_INCLUDES} CRYPTO_LDFLAGS: ${CRYPTO_LDFLAGS} CRYPTO_LIBS: ${CRYPTO_LIBS} - TLS support: $tls_support ${DISABLED_CRYPTO}: no diff --git a/doc/sphinx/arm/security.rst b/doc/sphinx/arm/security.rst index 3063fb0667..d729455fd7 100644 --- a/doc/sphinx/arm/security.rst +++ b/doc/sphinx/arm/security.rst @@ -59,8 +59,7 @@ that must be used: - LibreSSL 3.2.4 has been tested. LibreSSL shares the OpenSSL 1.0.2 API, so it should work, but is not supported. -- Botan 1.x versions are obsolete and should not be used. - Kea TLS support has not been tested and is not supported with these versions. +- Botan 1.x versions are obsolete and must not be used. - Botan versions 2.14.0 and later have been tested and are supported. Kea TLS support requires the four Asio header files which are included in Botan @@ -76,8 +75,8 @@ that must be used: directory, but this should be a last-resort procedure. Without these header files, or with a Botan version prior - to 2.14.0, Kea can still build, but the TLS/HTTPS support is disabled; - any attempt to use it will fail with a fatal error. + to 2.14.0, Kea cannot build as the TLS/HTTPS support is considered + as essential for security. - Very old Boost versions provide SSL support (based on OpenSSL) without offering a choice of the TLS version; Kea can still use them, diff --git a/m4macros/ax_crypto.m4 b/m4macros/ax_crypto.m4 index c075aba840..389582c9d5 100644 --- a/m4macros/ax_crypto.m4 +++ b/m4macros/ax_crypto.m4 @@ -40,9 +40,9 @@ AC_DEFUN([ACX_TRY_BOTAN_TOOL], [ CPPFLAGS="$CRYPTO_INCLUDES $CPPFLAGS" #AC_MSG_RESULT([found]) AC_LINK_IFELSE( - [AC_LANG_PROGRAM([#include <botan/lookup.h>], + [AC_LANG_PROGRAM([#include <botan/hash.h>], [using namespace Botan; - HashFunction *h = HashFunction::create("MD5").release(); + auto h = HashFunction::create("MD5"); ])], [ AC_MSG_RESULT([ok]) $3 @@ -199,13 +199,13 @@ EOF # failure handler we can detect the difference between a header not existing # (or not even passing the pre-processor phase) and a header file resulting # in compilation failures. - AC_CHECK_HEADERS([botan/botan.h],,[ + AC_CHECK_HEADERS([botan/build.h],,[ CRYPTO_INCLUDES="" CRYPTO_LIBS="" CRYPTO_LDFLAGS="" CRYPTO_RPATH="" if test "x$ac_header_preproc" = "xyes"; then - AC_MSG_RESULT([botan/botan.h + AC_MSG_RESULT([botan/build.h was found but is unusable. The most common cause of this problem is attempting to use an updated C++ compiler with older C++ libraries, such as the version of Botan that comes with your distribution. If you have updated @@ -226,10 +226,9 @@ then LIBS_SAVED="$LIBS" LIBS="$LIBS $CRYPTO_LIBS" AC_LINK_IFELSE( - [AC_LANG_PROGRAM([#include <botan/lookup.h>], + [AC_LANG_PROGRAM([#include <botan/hash.h>], [using namespace Botan; - HashFunction *h = HashFunction::create("MD5") -.release(); + auto h = HashFunction::create("MD5"); ])], [AC_MSG_RESULT([checking for Botan library... yes])], [AC_MSG_RESULT([checking for Botan library... no]) @@ -391,24 +390,17 @@ then dnl Check Botan boost ASIO TLS CPPFLAGS_SAVED=$CPPFLAGS CPPFLAGS="$CRYPTO_INCLUDES $CPPFLAGS $BOOST_INCLUDES" - BOTAN_BOOST="" AC_CHECK_HEADERS([botan/asio_stream.h], - [BOTAN_BOOST="maybe" - AC_MSG_CHECKING([Botan boost TLS support]) + [AC_MSG_CHECKING([Botan boost TLS support]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([#include <botan/asio_stream.h>], [#ifndef BOTAN_TLS_SERVER_H_ #error botan/tls_server.h is not included by botan/asio_stream.h #endif])], - [AC_MSG_RESULT(yes) - BOTAN_BOOST="yes" - AC_DEFINE([WITH_BOTAN_BOOST], [1], - [Define to 1 if Botan boost TLS is available])], + [AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no) - BOTAN_BOOST="no" - AC_MSG_WARN([Botan is configured with boost support but is too old: only Botan >= 2.14.0 can be used for TLS support.])])], - [BOTAN_BOOST="no" - AC_MSG_WARN([Botan cannot be used for TLS support, because it was compiled without boost support, so required headers are missing.])]) + AC_MSG_ERROR([Botan is configured with boost support but is too old: only Botan >= 2.14.0 can be used for TLS support.])])], + [AC_MSG_ERROR([Botan cannot be used for TLS support, because it was installed without boost support, so required headers are missing.])]) CPPFLAGS=${CPPFLAGS_SAVED} fi if test "x${CRYPTO_NAME}" = "xOpenSSL" @@ -454,7 +446,5 @@ then [AC_MSG_ERROR([Can not find a definition for stream_truncated (SSL short read) error: sorry, your boost library is too old])])]) CPPFLAGS=${CPPFLAGS_SAVED} fi -AM_CONDITIONAL(HAVE_BOTAN_BOOST, - test "$CRYPTO_NAME" = "Botan" && test "$BOTAN_BOOST" = "yes") ]) # End of AX_TLS diff --git a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc index 62951d4eed..1c17ea4a4c 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc @@ -1419,7 +1419,6 @@ TEST_F(HAConfigTest, passiveBackupDelayedUpdatesLimit) { "'delayed-updates-limit' must be set to 0 in the passive backup configuration"); } -#if (defined(WITH_OPENSSL) || defined(WITH_BOTAN_BOOST)) /// Test that TLS parameters are correctly inherited. TEST_F(HAConfigTest, tlsParameterInheritance) { const std::string ha_config = @@ -1743,7 +1742,6 @@ TEST_F(HAConfigTest, badKeyFile) { #endif testInvalidConfig(patched, expected); } -#endif // WITH_OPENSSL || WITH_BOTAN_BOOST // Test that conversion of the role names works correctly. TEST_F(HAConfigTest, stringToRole) { diff --git a/src/lib/asiolink/Makefile.am b/src/lib/asiolink/Makefile.am index 9654c931ac..21fbee30b3 100644 --- a/src/lib/asiolink/Makefile.am +++ b/src/lib/asiolink/Makefile.am @@ -17,8 +17,6 @@ libkea_asiolink_la_LDFLAGS += $(CRYPTO_LDFLAGS) libkea_asiolink_la_SOURCES = asiolink.h libkea_asiolink_la_SOURCES += asio_wrapper.h libkea_asiolink_la_SOURCES += addr_utilities.cc addr_utilities.h -libkea_asiolink_la_SOURCES += botan_boost_tls.h botan_boost_wrapper.h -libkea_asiolink_la_SOURCES += botan_tls.h libkea_asiolink_la_SOURCES += common_tls.cc common_tls.h libkea_asiolink_la_SOURCES += crypto_tls.h libkea_asiolink_la_SOURCES += dummy_io_cb.h @@ -33,7 +31,6 @@ libkea_asiolink_la_SOURCES += io_service_mgr.h io_service_mgr.cc libkea_asiolink_la_SOURCES += io_service_signal.cc io_service_signal.h libkea_asiolink_la_SOURCES += io_service_thread_pool.cc io_service_thread_pool.h libkea_asiolink_la_SOURCES += io_socket.h io_socket.cc -libkea_asiolink_la_SOURCES += openssl_tls.h libkea_asiolink_la_SOURCES += process_spawn.h process_spawn.cc libkea_asiolink_la_SOURCES += tcp_acceptor.h libkea_asiolink_la_SOURCES += tcp_endpoint.h @@ -47,14 +44,10 @@ libkea_asiolink_la_SOURCES += unix_domain_socket_acceptor.h libkea_asiolink_la_SOURCES += unix_domain_socket_endpoint.h if HAVE_BOTAN -if HAVE_BOTAN_BOOST -libkea_asiolink_la_SOURCES += botan_boost_tls.cc -else -libkea_asiolink_la_SOURCES += botan_tls.cc -endif +libkea_asiolink_la_SOURCES += botan_tls.cc botan_tls.h botan_wrapper.h endif if HAVE_OPENSSL -libkea_asiolink_la_SOURCES += openssl_tls.cc +libkea_asiolink_la_SOURCES += openssl_tls.cc openssl_tls.h endif # Note: the ordering matters: -Wno-... must follow -Wextra (defined in KEA_CXXFLAGS) @@ -70,9 +63,8 @@ libkea_asiolink_include_HEADERS = \ addr_utilities.h \ asio_wrapper.h \ asiolink.h \ - botan_boost_tls.h \ - botan_boost_wrapper.h \ botan_tls.h \ + botan_wrapper.h \ common_tls.h \ crypto_tls.h \ dummy_io_cb.h \ diff --git a/src/lib/asiolink/botan_boost_tls.cc b/src/lib/asiolink/botan_boost_tls.cc deleted file mode 100644 index 12db353b04..0000000000 --- a/src/lib/asiolink/botan_boost_tls.cc +++ /dev/null @@ -1,339 +0,0 @@ -// 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> - -/// @file botan_boost_tls.cc Botan boost ASIO implementation of the TLS API. - -#if defined(WITH_BOTAN) && defined(WITH_BOTAN_BOOST) - -#include <asiolink/asio_wrapper.h> -#include <asiolink/crypto_tls.h> - -#include <botan/auto_rng.h> -#include <botan/certstor_flatfile.h> -#include <botan/data_src.h> -#include <botan/pem.h> -#include <botan/pkcs8.h> - -using namespace isc::cryptolink; - -namespace isc { -namespace asiolink { - -// Classes of Kea certificate stores. -using KeaCertificateStorePath = Botan::Certificate_Store_In_Memory; -using KeaCertificateStoreFile = Botan::Flatfile_Certificate_Store; - -// Class of Kea credential managers. -class KeaCredentialsManager : public Botan::Credentials_Manager { -public: - // Constructor. - KeaCredentialsManager() : store_(), use_stores_(true), certs_(), key_() { - } - - // Destructor. - virtual ~KeaCredentialsManager() { - } - - // CA certificate stores. - // nullptr means do not require or check peer certificate. - std::vector<Botan::Certificate_Store*> - trusted_certificate_authorities(const std::string&, - const std::string&) override { - std::vector<Botan::Certificate_Store*> result; - if (use_stores_ && store_) { - result.push_back(store_.get()); - } - return (result); - } - - // Certificate chain. - std::vector<Botan::X509_Certificate> - cert_chain(const std::vector<std::string>&, - const std::string&, - const std::string&) override { - return (certs_); - } - - // Private key. - Botan::Private_Key* - private_key_for(const Botan::X509_Certificate&, - const std::string&, - const std::string&) override { - return (key_.get()); - } - - // Set the store from a path. - void setStorePath(const std::string& path) { - store_.reset(new KeaCertificateStorePath(path)); - } - - // Set the store from a file. - void setStoreFile(const std::string& file) { - store_.reset(new KeaCertificateStoreFile(file)); - } - - // Get the use of CA certificate stores flag. - bool getUseStores() const { - return (use_stores_); - } - - // Set the use of CA certificate stores flag. - void setUseStores(bool use_stores) { - use_stores_ = use_stores; - } - - // Set the certificate chain. - void setCertChain(const std::string& file) { - Botan::DataSource_Stream source(file); - certs_.clear(); - while (!source.end_of_data()) { - std::string label; - std::vector<uint8_t> cert; - try { - cert = unlock(Botan::PEM_Code::decode(source, label)); - if ((label != "CERTIFICATE") && - (label != "X509 CERTIFICATE") && - (label != "TRUSTED CERTIFICATE")) { - isc_throw(LibraryError, "Expected a certificate, got '" - << label << "'"); - } - certs_.push_back(Botan::X509_Certificate(cert)); - } catch (const std::exception& ex) { - if (certs_.empty()) { - throw; - } - // Got one certificate so skipping garbage. - continue; - } - } - if (certs_.empty()) { - isc_throw(LibraryError, "Found no certificate?"); - } - } - - // Set the private key. - void setPrivateKey(const std::string& file, - Botan::RandomNumberGenerator& rng, - bool& is_rsa) { - key_.reset(Botan::PKCS8::load_key(file, rng)); - if (!key_) { - isc_throw(Unexpected, - "Botan::PKCS8::load_key failed but not threw?"); - } - is_rsa = (key_->algo_name() == "RSA"); - } - - // Pointer to the CA certificate store. - std::unique_ptr<Botan::Certificate_Store> store_; - - // Use the CA certificate store flag. - bool use_stores_; - - // The certificate chain. - std::vector<Botan::X509_Certificate> certs_; - - // Pointer to the private key. - std::unique_ptr<Botan::Private_Key> key_; -}; - -// Class of Kea policy. -// Use Strict_Policy? -class KeaPolicy : public Botan::TLS::Default_Policy { -public: - // Constructor. - KeaPolicy() : prefer_rsa_(true) { - } - - // Destructor. - virtual ~KeaPolicy() { - } - - // Allowed signature methods in preference order. - std::vector<std::string> allowed_signature_methods() const override { - if (prefer_rsa_) { - return (AllowedSignatureMethodsRSA); - } else { - return (AllowedSignatureMethodsECDSA); - } - } - - // Disable OSCP. - bool require_cert_revocation_info() const override { - return false; - } - - // Set the RSA preferred flag. - void setPrefRSA(bool prefer_rsa) { - prefer_rsa_ = prefer_rsa; - } - - // Prefer RSA preferred flag. - bool prefer_rsa_; - - // Allowed signature methods which prefers RSA. - static const std::vector<std::string> AllowedSignatureMethodsRSA; - - // Allowed signature methods which prefers ECDSA. - static const std::vector<std::string> AllowedSignatureMethodsECDSA; -}; - - -// Kea session manager. -using KeaSessionManager = Botan::TLS::Session_Manager_Noop; - -// Allowed signature methods which prefers RSA. -const std::vector<std::string> -KeaPolicy::AllowedSignatureMethodsRSA = { "RSA", "DSA", "ECDSA" }; - -// Allowed signature methods which prefers ECDSA. -const std::vector<std::string> -KeaPolicy::AllowedSignatureMethodsECDSA = { "ECDSA", "RSA", "DSA" }; - -// Class of Botan TLS context implementations. -class TlsContextImpl { -public: - // Constructor. - TlsContextImpl() : cred_mgr_(), rng_(), sess_mgr_(), policy_() { - } - - // Destructor. - virtual ~TlsContextImpl() { - } - - // Get the peer certificate requirement mode. - virtual bool getCertRequired() const { - return (cred_mgr_.getUseStores()); - } - - // Set the peer certificate requirement mode. - // - // With Botan this means to provide or not the CA certificate stores. - virtual void setCertRequired(bool cert_required) { - cred_mgr_.setUseStores(cert_required); - } - - // Load the trust anchor aka certificate authority (path). - virtual void loadCaPath(const std::string& ca_path) { - try { - cred_mgr_.setStorePath(ca_path); - } catch (const std::exception& ex) { - isc_throw(LibraryError, ex.what()); - } - } - - // Load the trust anchor aka certificate authority (file). - virtual void loadCaFile(const std::string& ca_file) { - try { - cred_mgr_.setStoreFile(ca_file); - } catch (const std::exception& ex) { - isc_throw(LibraryError, ex.what()); - } - } - - /// @brief Load the certificate file. - virtual void loadCertFile(const std::string& cert_file) { - try { - cred_mgr_.setCertChain(cert_file); - } catch (const std::exception& ex) { - isc_throw(LibraryError, ex.what()); - } - } - - /// @brief Load the private key file. - /// - /// As a side effect set the preference for RSA in the policy. - virtual void loadKeyFile(const std::string& key_file) { - try { - bool is_rsa = true; - cred_mgr_.setPrivateKey(key_file, rng_, is_rsa); - policy_.setPrefRSA(is_rsa); - } catch (const std::exception& ex) { - isc_throw(LibraryError, ex.what()); - } - } - - // Build the context if not yet done. - virtual void build() { - if (context_) { - return; - } - context_.reset(new Botan::TLS::Context(cred_mgr_, - rng_, - sess_mgr_, - policy_)); - } - - virtual Botan::TLS::Context& get() { - return (*context_); - } - - // Credentials Manager. - KeaCredentialsManager cred_mgr_; - - // Random Number Generator. - Botan::AutoSeeded_RNG rng_; - - // Session Manager. - KeaSessionManager sess_mgr_; - - KeaPolicy policy_; - - std::unique_ptr<Botan::TLS::Context> context_; -}; - -TlsContext::~TlsContext() { -} - -TlsContext::TlsContext(TlsRole role) - : TlsContextBase(role), impl_(new TlsContextImpl()) { -} - -Botan::TLS::Context& -TlsContext::getContext() { - impl_->build(); - return (impl_->get()); -} - -void -TlsContext::setCertRequired(bool cert_required) { - if (!cert_required && (getRole() == TlsRole::CLIENT)) { - isc_throw(BadValue, - "'cert-required' parameter must be true for a TLS client"); - } - impl_->setCertRequired(cert_required); -} - -bool -TlsContext::getCertRequired() const { - return (impl_->getCertRequired()); -} - -void -TlsContext::loadCaFile(const std::string& ca_file) { - impl_->loadCaFile(ca_file); -} - -void -TlsContext::loadCaPath(const std::string& ca_path) { - impl_->loadCaPath(ca_path); -} - -void -TlsContext::loadCertFile(const std::string& cert_file) { - impl_->loadCertFile(cert_file); -} - -void -TlsContext::loadKeyFile(const std::string& key_file) { - impl_->loadKeyFile(key_file); -} - -} // namespace asiolink -} // namespace isc - -#endif // WITH_BOTAN && WITH_BOTAN_BOOST diff --git a/src/lib/asiolink/botan_boost_tls.h b/src/lib/asiolink/botan_boost_tls.h deleted file mode 100644 index 32b4d06597..0000000000 --- a/src/lib/asiolink/botan_boost_tls.h +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (C) 2021-2024 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/. - -// Do not include this header directly: use crypto_tls.h instead. - -#ifndef BOTAN_BOOST_TLS_H -#define BOTAN_BOOST_TLS_H - -/// @file botan_boost_tls.h Botan boost ASIO implementation of the TLS API. - -#if defined(WITH_BOTAN) && defined(WITH_BOTAN_BOOST) - -#include <asiolink/asio_wrapper.h> -#include <asiolink/io_asio_socket.h> -#include <asiolink/io_service.h> -#include <asiolink/common_tls.h> -#include <exceptions/exceptions.h> - -#include <asiolink/botan_boost_wrapper.h> -#include <botan/asio_stream.h> - -namespace isc { -namespace asiolink { - -/// @brief Translate TLS role into implementation. -inline Botan::TLS::Connection_Side roleToImpl(TlsRole role) { - if (role == TlsRole::SERVER) { - return (Botan::TLS::Connection_Side::SERVER); - } else { - return (Botan::TLS::Connection_Side::CLIENT); - } -} - -/// @brief Forward declaration of Botan TLS context. -class TlsContextImpl; - -/// @brief Botan boost ASIO TLS context. -class TlsContext : public TlsContextBase { -public: - - /// @brief Destructor. - /// - /// @note The destructor can't be defined here because a unique - /// pointer to an incomplete type is used. - virtual ~TlsContext(); - - /// @brief Create a fresh context. - /// - /// @param role The TLS role client or server. - explicit TlsContext(TlsRole role); - - /// @brief Return the underlying context. - Botan::TLS::Context& getContext(); - - /// @brief Get the peer certificate requirement mode. - /// - /// @return True if peer certificates are required, false if they - /// are optional. - virtual bool getCertRequired() const; - -protected: - /// @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 Load the trust anchor aka certification authority. - /// - /// @param ca_file The certificate file name. - virtual void loadCaFile(const std::string& ca_file); - - /// @brief Load the trust anchor aka certification 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 from a file. - /// - /// @param key_file The private key file name. - virtual void loadKeyFile(const std::string& key_file); - - /// @brief Botan TLS context. - std::unique_ptr<TlsContextImpl> impl_; - - /// @brief Allow access to protected methods by the base class. - friend class TlsContextBase; -}; - -/// @brief The type of underlying TLS streams. -typedef Botan::TLS::Stream<boost::asio::ip::tcp::socket> TlsStreamImpl; - -/// @brief TlsStreamBase constructor. -/// -/// @tparam Callback The type of callbacks. -/// @tparam TlsStreamImpl The type of underlying TLS streams. -/// @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. -template <typename Callback, typename TlsStreamImpl> -TlsStreamBase<Callback, TlsStreamImpl>:: -TlsStreamBase(const IOServicePtr& io_service, TlsContextPtr context) - : StreamService(io_service, context), - TlsStreamImpl(io_service->getInternalIOService(), - context->getContext()), role_(context->getRole()) { -} - -/// @brief Botan boost ASIO TLS stream. -/// -/// @tparam callback The callback. -template <typename Callback> -class TlsStream : public TlsStreamBase<Callback, TlsStreamImpl> -{ -public: - - /// @brief Type of the base. - typedef TlsStreamBase<Callback, TlsStreamImpl> Base; - - /// @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(const IOServicePtr& service, TlsContextPtr context) - : Base(service, context) { - } - - /// @brief Destructor. - virtual ~TlsStream() { } - - /// @brief TLS Handshake. - /// - /// @param callback Callback object. - virtual void handshake(Callback& callback) { - Base::async_handshake(roleToImpl(Base::getRole()), callback); - } - - /// @brief TLS shutdown. - /// - /// @param callback Callback object. - virtual void shutdown(Callback& callback) { - Base::async_shutdown(callback); - } - - /// @brief Clear the TLS object. - /// - /// @note The idea to reuse a TCP connection for a fresh TLS is at - /// least arguable. Currently it does nothing so the socket is - /// **not** reusable. - virtual void clear() { - } - - /// @brief Return the commonName part of the subjectName of - /// the peer certificate. - /// - /// First commonName when there are more than one, in UTF-8. - /// RFC 3280 provides as a commonName example "Susan Housley", - /// to idea to give access to this come from the Role Based - /// Access Control experiment. - /// - /// @return The commonName part of the subjectName or the empty string. - virtual std::string getSubject() { - const std::vector<Botan::X509_Certificate>& cert_chain = - Base::native_handle()->peer_cert_chain(); - if (cert_chain.empty()) { - return (""); - } - const Botan::X509_DN& subject = cert_chain[0].subject_dn(); - return (subject.get_first_attribute("CommonName")); - } - - /// @brief Return the commonName part of the issuerName of - /// the peer certificate. - /// - /// First commonName when there are more than one, in UTF-8. - /// The issuerName is the subjectName of the signing certificate - /// (the issue in PKIX terms). The idea is to encode a group as - /// members of an intermediate certification authority. - /// - /// @return The commonName part of the issuerName or the empty string. - virtual std::string getIssuer() { - const std::vector<Botan::X509_Certificate>& cert_chain = - Base::native_handle()->peer_cert_chain(); - if (cert_chain.empty()) { - return (""); - } - const Botan::X509_DN& issuer = cert_chain[0].issuer_dn(); - return (issuer.get_first_attribute("CommonName")); - } -}; - -// Stream truncated error code. -const int STREAM_TRUNCATED = Botan::TLS::StreamError::StreamTruncated; - -} // namespace asiolink -} // namespace isc - -#endif // WITH_BOTAN && WITH_BOTAN_BOOST - -#endif // BOTAN_BOOST_TLS_H diff --git a/src/lib/asiolink/botan_tls.cc b/src/lib/asiolink/botan_tls.cc index 3cd18189d0..b95485ccc3 100644 --- a/src/lib/asiolink/botan_tls.cc +++ b/src/lib/asiolink/botan_tls.cc @@ -1,23 +1,302 @@ -// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2021-2024 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/. -/// @file botan_tls.cc Botan fake implementation of the TLS API. - #include <config.h> -#if defined(WITH_BOTAN) && !defined(WITH_BOTAN_BOOST) +/// @file botan_tls.cc Botan ASIO implementation of the TLS API. + +#ifdef WITH_BOTAN #include <asiolink/asio_wrapper.h> #include <asiolink/crypto_tls.h> +#include <botan/auto_rng.h> +#include <botan/certstor_flatfile.h> +#include <botan/data_src.h> +#include <botan/pem.h> +#include <botan/pkcs8.h> + +using namespace isc::cryptolink; + namespace isc { namespace asiolink { +// Classes of Kea certificate stores. +using KeaCertificateStorePath = Botan::Certificate_Store_In_Memory; +using KeaCertificateStoreFile = Botan::Flatfile_Certificate_Store; + +// Class of Kea credential managers. +class KeaCredentialsManager : public Botan::Credentials_Manager { +public: + // Constructor. + KeaCredentialsManager() : store_(), use_stores_(true), certs_(), key_() { + } + + // Destructor. + virtual ~KeaCredentialsManager() { + } + + // CA certificate stores. + // nullptr means do not require or check peer certificate. + std::vector<Botan::Certificate_Store*> + trusted_certificate_authorities(const std::string&, + const std::string&) override { + std::vector<Botan::Certificate_Store*> result; + if (use_stores_ && store_) { + result.push_back(store_.get()); + } + return (result); + } + + // Certificate chain. + std::vector<Botan::X509_Certificate> + cert_chain(const std::vector<std::string>&, + const std::string&, + const std::string&) override { + return (certs_); + } + + // Private key. + Botan::Private_Key* + private_key_for(const Botan::X509_Certificate&, + const std::string&, + const std::string&) override { + return (key_.get()); + } + + // Set the store from a path. + void setStorePath(const std::string& path) { + store_.reset(new KeaCertificateStorePath(path)); + } + + // Set the store from a file. + void setStoreFile(const std::string& file) { + store_.reset(new KeaCertificateStoreFile(file)); + } + + // Get the use of CA certificate stores flag. + bool getUseStores() const { + return (use_stores_); + } + + // Set the use of CA certificate stores flag. + void setUseStores(bool use_stores) { + use_stores_ = use_stores; + } + + // Set the certificate chain. + void setCertChain(const std::string& file) { + Botan::DataSource_Stream source(file); + certs_.clear(); + while (!source.end_of_data()) { + std::string label; + std::vector<uint8_t> cert; + try { + cert = unlock(Botan::PEM_Code::decode(source, label)); + if ((label != "CERTIFICATE") && + (label != "X509 CERTIFICATE") && + (label != "TRUSTED CERTIFICATE")) { + isc_throw(LibraryError, "Expected a certificate, got '" + << label << "'"); + } + certs_.push_back(Botan::X509_Certificate(cert)); + } catch (const std::exception& ex) { + if (certs_.empty()) { + throw; + } + // Got one certificate so skipping garbage. + continue; + } + } + if (certs_.empty()) { + isc_throw(LibraryError, "Found no certificate?"); + } + } + + // Set the private key. + void setPrivateKey(const std::string& file, + Botan::RandomNumberGenerator& rng, + bool& is_rsa) { + key_.reset(Botan::PKCS8::load_key(file, rng)); + if (!key_) { + isc_throw(Unexpected, + "Botan::PKCS8::load_key failed but not threw?"); + } + is_rsa = (key_->algo_name() == "RSA"); + } + + // Pointer to the CA certificate store. + std::unique_ptr<Botan::Certificate_Store> store_; + + // Use the CA certificate store flag. + bool use_stores_; + + // The certificate chain. + std::vector<Botan::X509_Certificate> certs_; + + // Pointer to the private key. + std::unique_ptr<Botan::Private_Key> key_; +}; + +// Class of Kea policy. +// Use Strict_Policy? +class KeaPolicy : public Botan::TLS::Default_Policy { +public: + // Constructor. + KeaPolicy() : prefer_rsa_(true) { + } + + // Destructor. + virtual ~KeaPolicy() { + } + + // Allowed signature methods in preference order. + std::vector<std::string> allowed_signature_methods() const override { + if (prefer_rsa_) { + return (AllowedSignatureMethodsRSA); + } else { + return (AllowedSignatureMethodsECDSA); + } + } + + // Disable OSCP. + bool require_cert_revocation_info() const override { + return false; + } + + // Set the RSA preferred flag. + void setPrefRSA(bool prefer_rsa) { + prefer_rsa_ = prefer_rsa; + } + + // Prefer RSA preferred flag. + bool prefer_rsa_; + + // Allowed signature methods which prefers RSA. + static const std::vector<std::string> AllowedSignatureMethodsRSA; + + // Allowed signature methods which prefers ECDSA. + static const std::vector<std::string> AllowedSignatureMethodsECDSA; +}; + + +// Kea session manager. +using KeaSessionManager = Botan::TLS::Session_Manager_Noop; + +// Allowed signature methods which prefers RSA. +const std::vector<std::string> +KeaPolicy::AllowedSignatureMethodsRSA = { "RSA", "DSA", "ECDSA" }; + +// Allowed signature methods which prefers ECDSA. +const std::vector<std::string> +KeaPolicy::AllowedSignatureMethodsECDSA = { "ECDSA", "RSA", "DSA" }; + +// Class of Botan TLS context implementations. +class TlsContextImpl { +public: + // Constructor. + TlsContextImpl() : cred_mgr_(), rng_(), sess_mgr_(), policy_() { + } + + // Destructor. + virtual ~TlsContextImpl() { + } + + // Get the peer certificate requirement mode. + virtual bool getCertRequired() const { + return (cred_mgr_.getUseStores()); + } + + // Set the peer certificate requirement mode. + // + // With Botan this means to provide or not the CA certificate stores. + virtual void setCertRequired(bool cert_required) { + cred_mgr_.setUseStores(cert_required); + } + + // Load the trust anchor aka certificate authority (path). + virtual void loadCaPath(const std::string& ca_path) { + try { + cred_mgr_.setStorePath(ca_path); + } catch (const std::exception& ex) { + isc_throw(LibraryError, ex.what()); + } + } + + // Load the trust anchor aka certificate authority (file). + virtual void loadCaFile(const std::string& ca_file) { + try { + cred_mgr_.setStoreFile(ca_file); + } catch (const std::exception& ex) { + isc_throw(LibraryError, ex.what()); + } + } + + /// @brief Load the certificate file. + virtual void loadCertFile(const std::string& cert_file) { + try { + cred_mgr_.setCertChain(cert_file); + } catch (const std::exception& ex) { + isc_throw(LibraryError, ex.what()); + } + } + + /// @brief Load the private key file. + /// + /// As a side effect set the preference for RSA in the policy. + virtual void loadKeyFile(const std::string& key_file) { + try { + bool is_rsa = true; + cred_mgr_.setPrivateKey(key_file, rng_, is_rsa); + policy_.setPrefRSA(is_rsa); + } catch (const std::exception& ex) { + isc_throw(LibraryError, ex.what()); + } + } + + // Build the context if not yet done. + virtual void build() { + if (context_) { + return; + } + context_.reset(new Botan::TLS::Context(cred_mgr_, + rng_, + sess_mgr_, + policy_)); + } + + virtual Botan::TLS::Context& get() { + return (*context_); + } + + // Credentials Manager. + KeaCredentialsManager cred_mgr_; + + // Random Number Generator. + Botan::AutoSeeded_RNG rng_; + + // Session Manager. + KeaSessionManager sess_mgr_; + + KeaPolicy policy_; + + std::unique_ptr<Botan::TLS::Context> context_; +}; + +TlsContext::~TlsContext() { +} + TlsContext::TlsContext(TlsRole role) - : TlsContextBase(role), cert_required_(true) { + : TlsContextBase(role), impl_(new TlsContextImpl()) { +} + +Botan::TLS::Context& +TlsContext::getContext() { + impl_->build(); + return (impl_->get()); } void @@ -26,35 +305,35 @@ TlsContext::setCertRequired(bool cert_required) { isc_throw(BadValue, "'cert-required' parameter must be true for a TLS client"); } - cert_required_ = cert_required; + impl_->setCertRequired(cert_required); } bool TlsContext::getCertRequired() const { - return (cert_required_); + return (impl_->getCertRequired()); } void -TlsContext::loadCaFile(const std::string&) { - isc_throw(NotImplemented, "Botan TLS is not yet supported"); +TlsContext::loadCaFile(const std::string& ca_file) { + impl_->loadCaFile(ca_file); } void -TlsContext::loadCaPath(const std::string&) { - isc_throw(NotImplemented, "loadCaPath is not implemented by Botan"); +TlsContext::loadCaPath(const std::string& ca_path) { + impl_->loadCaPath(ca_path); } void -TlsContext::loadCertFile(const std::string&) { - isc_throw(NotImplemented, "Botan TLS is not yet supported"); +TlsContext::loadCertFile(const std::string& cert_file) { + impl_->loadCertFile(cert_file); } void -TlsContext::loadKeyFile(const std::string&) { - isc_throw(NotImplemented, "Botan TLS is not yet supported"); +TlsContext::loadKeyFile(const std::string& key_file) { + impl_->loadKeyFile(key_file); } } // namespace asiolink } // namespace isc -#endif // WITH_BOTAN && !WITH_BOTAN_BOOST +#endif // WITH_BOTAN diff --git a/src/lib/asiolink/botan_tls.h b/src/lib/asiolink/botan_tls.h index 1dbed1e1fd..6004672336 100644 --- a/src/lib/asiolink/botan_tls.h +++ b/src/lib/asiolink/botan_tls.h @@ -9,32 +9,52 @@ #ifndef BOTAN_TLS_H #define BOTAN_TLS_H -/// @file botan_tls.h Botan fake implementation of the TLS API. +/// @file botan_tls.h Botan ASIO implementation of the TLS API. -#if defined(WITH_BOTAN) && !defined(WITH_BOTAN_BOOST) +#ifdef WITH_BOTAN #include <asiolink/asio_wrapper.h> #include <asiolink/io_asio_socket.h> #include <asiolink/io_service.h> #include <asiolink/common_tls.h> - #include <exceptions/exceptions.h> +#include <asiolink/botan_wrapper.h> +#include <botan/asio_stream.h> + namespace isc { namespace asiolink { -/// @brief Botan TLS context. +/// @brief Translate TLS role into implementation. +inline Botan::TLS::Connection_Side roleToImpl(TlsRole role) { + if (role == TlsRole::SERVER) { + return (Botan::TLS::Connection_Side::SERVER); + } else { + return (Botan::TLS::Connection_Side::CLIENT); + } +} + +/// @brief Forward declaration of Botan TLS context. +class TlsContextImpl; + +/// @brief Botan ASIO TLS context. class TlsContext : public TlsContextBase { public: /// @brief Destructor. - virtual ~TlsContext() { } + /// + /// @note The destructor can't be defined here because a unique + /// pointer to an incomplete type is used. + virtual ~TlsContext(); /// @brief Create a fresh context. /// /// @param role The TLS role client or server. explicit TlsContext(TlsRole role); + /// @brief Return the underlying context. + Botan::TLS::Context& getContext(); + /// @brief Get the peer certificate requirement mode. /// /// @return True if peer certificates are required, false if they @@ -51,40 +71,32 @@ protected: /// @brief Load the trust anchor aka certification 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); /// @brief Load the trust anchor aka certification 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); /// @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); /// @brief Load the private key from a file. /// /// @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); - /// @brief Cached cert_required value. - bool cert_required_; + /// @brief Botan TLS context. + std::unique_ptr<TlsContextImpl> impl_; /// @brief Allow access to protected methods by the base class. friend class TlsContextBase; }; -/// @brief The type of Botan TLS streams (in fact pure TCP streams). -typedef boost::asio::ip::tcp::socket TlsStreamImpl; +/// @brief The type of underlying TLS streams. +typedef Botan::TLS::Stream<boost::asio::ip::tcp::socket> TlsStreamImpl; /// @brief TlsStreamBase constructor. /// @@ -97,15 +109,16 @@ template <typename Callback, typename TlsStreamImpl> TlsStreamBase<Callback, TlsStreamImpl>:: TlsStreamBase(const IOServicePtr& io_service, TlsContextPtr context) : StreamService(io_service, context), - TlsStreamImpl(io_service->getInternalIOService()), - role_(context->getRole()) { + TlsStreamImpl(io_service->getInternalIOService(), + context->getContext()), role_(context->getRole()) { } -/// @brief Botan fake TLS stream. +/// @brief Botan ASIO TLS stream. /// /// @tparam callback The callback. template <typename Callback> -class TlsStream : public TlsStreamBase<Callback, TlsStreamImpl> { +class TlsStream : public TlsStreamBase<Callback, TlsStreamImpl> +{ public: /// @brief Type of the base. @@ -124,13 +137,25 @@ public: virtual ~TlsStream() { } /// @brief TLS Handshake. - virtual void handshake(Callback&) { - isc_throw(NotImplemented, "Botan TLS is not yet supported"); + /// + /// @param callback Callback object. + virtual void handshake(Callback& callback) { + Base::async_handshake(roleToImpl(Base::getRole()), callback); } /// @brief TLS shutdown. - virtual void shutdown(Callback&) { - isc_throw(NotImplemented, "Botan TLS is not yet supported"); + /// + /// @param callback Callback object. + virtual void shutdown(Callback& callback) { + Base::async_shutdown(callback); + } + + /// @brief Clear the TLS object. + /// + /// @note The idea to reuse a TCP connection for a fresh TLS is at + /// least arguable. Currently it does nothing so the socket is + /// **not** reusable. + virtual void clear() { } /// @brief Return the commonName part of the subjectName of @@ -141,10 +166,15 @@ public: /// to idea to give access to this come from the Role Based /// Access Control experiment. /// - /// /// @return The commonName part of the subjectName or the empty string. - std::string getSubject() { - return (""); + virtual std::string getSubject() { + const std::vector<Botan::X509_Certificate>& cert_chain = + Base::native_handle()->peer_cert_chain(); + if (cert_chain.empty()) { + return (""); + } + const Botan::X509_DN& subject = cert_chain[0].subject_dn(); + return (subject.get_first_attribute("CommonName")); } /// @brief Return the commonName part of the issuerName of @@ -155,16 +185,24 @@ public: /// (the issue in PKIX terms). The idea is to encode a group as /// members of an intermediate certification authority. /// - /// /// @return The commonName part of the issuerName or the empty string. - std::string getIssuer() { - return (""); + virtual std::string getIssuer() { + const std::vector<Botan::X509_Certificate>& cert_chain = + Base::native_handle()->peer_cert_chain(); + if (cert_chain.empty()) { + return (""); + } + const Botan::X509_DN& issuer = cert_chain[0].issuer_dn(); + return (issuer.get_first_attribute("CommonName")); } }; +// Stream truncated error code. +const int STREAM_TRUNCATED = Botan::TLS::StreamError::StreamTruncated; + } // namespace asiolink } // namespace isc -#endif // WITH_BOTAN && !WITH_BOTAN_BOOST +#endif // WITH_BOTAN #endif // BOTAN_TLS_H diff --git a/src/lib/asiolink/botan_boost_wrapper.h b/src/lib/asiolink/botan_wrapper.h index e244bbb9cd..09d14838de 100644 --- a/src/lib/asiolink/botan_boost_wrapper.h +++ b/src/lib/asiolink/botan_wrapper.h @@ -1,4 +1,4 @@ -// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2021-2024 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 @@ -6,12 +6,12 @@ // Do not include this header directly: use crypto_tls.h instead. -#ifndef BOTAN_BOOST_WRAPPER_H -#define BOTAN_BOOST_WRAPPER_H +#ifndef BOTAN_WRAPPER_H +#define BOTAN_WRAPPER_H -/// @file botan_boost_wrapper.h Botan boost ASIO wrapper. +/// @file botan_wrapper.h Botan ASIO wrapper. -#if defined(WITH_BOTAN) && defined(WITH_BOTAN_BOOST) +#ifdef WITH_BOTAN /// The error classes do not define virtual destructors. /// This workaround is taken from the boost header. @@ -27,6 +27,6 @@ #pragma GCC diagnostic pop #endif -#endif // WITH_BOTAN && WITH_BOTAN_BOOST +#endif // WITH_BOTAN -#endif // BOTAN_BOOST_WRAPPER_H +#endif // BOTAN_WRAPPER_H diff --git a/src/lib/asiolink/crypto_tls.h b/src/lib/asiolink/crypto_tls.h index c3e899febb..256c476707 100644 --- a/src/lib/asiolink/crypto_tls.h +++ b/src/lib/asiolink/crypto_tls.h @@ -15,7 +15,6 @@ #endif // Include different versions. -#include <asiolink/botan_boost_tls.h> #include <asiolink/botan_tls.h> #include <asiolink/openssl_tls.h> diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am index e9e13d57aa..1ae8d05869 100644 --- a/src/lib/asiolink/tests/Makefile.am +++ b/src/lib/asiolink/tests/Makefile.am @@ -46,7 +46,7 @@ run_unittests_SOURCES += tls_unittest.cc run_unittests_SOURCES += tls_acceptor_unittest.cc run_unittests_SOURCES += tls_socket_unittest.cc endif -if HAVE_BOTAN_BOOST +if HAVE_BOTAN run_unittests_SOURCES += tls_unittest.cc run_unittests_SOURCES += tls_acceptor_unittest.cc run_unittests_SOURCES += tls_socket_unittest.cc diff --git a/src/lib/asiolink/testutils/.gitignore b/src/lib/asiolink/testutils/.gitignore index cc30b7cbc2..34750c0b49 100644 --- a/src/lib/asiolink/testutils/.gitignore +++ b/src/lib/asiolink/testutils/.gitignore @@ -1,4 +1,4 @@ -/botan_boost_sample_client -/botan_boost_sample_server +/botan_sample_client +/botan_sample_server /openssl_sample_client /openssl_sample_server diff --git a/src/lib/asiolink/testutils/Makefile.am b/src/lib/asiolink/testutils/Makefile.am index cb06448a70..3bd402c8b4 100644 --- a/src/lib/asiolink/testutils/Makefile.am +++ b/src/lib/asiolink/testutils/Makefile.am @@ -75,19 +75,19 @@ openssl_sample_server_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) openssl_sample_server_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) endif -if HAVE_BOTAN_BOOST -# Same samples ported to Botan boost ASIO. +if HAVE_BOTAN +# Same samples ported to Botan ASIO. -noinst_PROGRAMS = botan_boost_sample_client botan_boost_sample_server +noinst_PROGRAMS = botan_sample_client botan_sample_server -botan_boost_sample_client_SOURCES = botan_boost_sample_client.cc -botan_boost_sample_client_CPPFLAGS = $(AM_CPPFLAGS) -botan_boost_sample_client_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) -botan_boost_sample_client_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) +botan_sample_client_SOURCES = botan_sample_client.cc +botan_sample_client_CPPFLAGS = $(AM_CPPFLAGS) +botan_sample_client_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +botan_sample_client_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) -botan_boost_sample_server_SOURCES = botan_boost_sample_server.cc -botan_boost_sample_server_CPPFLAGS = $(AM_CPPFLAGS) -botan_boost_sample_server_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) -botan_boost_sample_server_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) +botan_sample_server_SOURCES = botan_sample_server.cc +botan_sample_server_CPPFLAGS = $(AM_CPPFLAGS) +botan_sample_server_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +botan_sample_server_LDADD = $(BOOST_LIBS) $(CRYPTO_LIBS) endif endif diff --git a/src/lib/asiolink/testutils/botan_boost_sample_client.cc b/src/lib/asiolink/testutils/botan_sample_client.cc index 16a83e8d2b..9e322fb543 100644 --- a/src/lib/asiolink/testutils/botan_boost_sample_client.cc +++ b/src/lib/asiolink/testutils/botan_sample_client.cc @@ -16,7 +16,7 @@ #include <iostream> #include <asiolink/asio_wrapper.h> -#include <asiolink/botan_boost_wrapper.h> +#include <asiolink/botan_wrapper.h> #include <botan/asio_stream.h> #include <botan/certstor_flatfile.h> #include <botan/pkcs8.h> diff --git a/src/lib/asiolink/testutils/botan_boost_sample_server.cc b/src/lib/asiolink/testutils/botan_sample_server.cc index 9d7e8c55b8..2d6be475ce 100644 --- a/src/lib/asiolink/testutils/botan_boost_sample_server.cc +++ b/src/lib/asiolink/testutils/botan_sample_server.cc @@ -15,7 +15,7 @@ #include <iostream> #include <asiolink/asio_wrapper.h> -#include <asiolink/botan_boost_wrapper.h> +#include <asiolink/botan_wrapper.h> #include <botan/asio_stream.h> #include <botan/certstor_flatfile.h> #include <botan/pkcs8.h> diff --git a/src/lib/cryptolink/botan_common.h b/src/lib/cryptolink/botan_common.h index 05cae30fbb..b617e78238 100644 --- a/src/lib/cryptolink/botan_common.h +++ b/src/lib/cryptolink/botan_common.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2024 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 @@ -15,6 +15,13 @@ namespace btn { const std::string getHashAlgorithmName(isc::cryptolink::HashAlgorithm algorithm); +/// @brief Decode the HashAlgorithm enum into a name usable by Botan +/// +/// @param algorithm algorithm to be converted +/// @return static text representation of the algorithm name +const std::string +getHmacAlgorithmName(isc::cryptolink::HashAlgorithm algorithm); + } // namespace btn } // namespace cryptolink } // namespace isc diff --git a/src/lib/cryptolink/botan_hash.cc b/src/lib/cryptolink/botan_hash.cc index 06dca6d05d..8859379a26 100644 --- a/src/lib/cryptolink/botan_hash.cc +++ b/src/lib/cryptolink/botan_hash.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2024 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 @@ -9,9 +9,8 @@ #include <cryptolink.h> #include <cryptolink/crypto_hash.h> -#include <boost/scoped_ptr.hpp> - -#include <botan/lookup.h> +#include <botan/hash.h> +#include <botan/exceptn.h> #include <cryptolink/botan_common.h> @@ -51,12 +50,11 @@ public: /// @param hash_algorithm The hash algorithm explicit HashImpl(const HashAlgorithm hash_algorithm) : hash_algorithm_(hash_algorithm), hash_() { - Botan::HashFunction* hash; try { const std::string& name = btn::getHashAlgorithmName(hash_algorithm); - hash = Botan::HashFunction::create(name).release(); - } catch (const Botan::Algorithm_Not_Found&) { + hash_ = Botan::HashFunction::create_or_throw(name); + } catch (const Botan::Lookup_Error&) { isc_throw(isc::cryptolink::UnsupportedAlgorithm, "Unknown hash algorithm: " << static_cast<int>(hash_algorithm)); @@ -64,12 +62,10 @@ public: isc_throw(isc::cryptolink::LibraryError, "Botan error: " << exc.what()); } - - hash_.reset(hash); } /// @brief Destructor - ~HashImpl() { } + ~HashImpl() = default; /// @brief Returns the HashAlgorithm of the object HashAlgorithm getHashAlgorithm() const { @@ -153,7 +149,7 @@ private: HashAlgorithm hash_algorithm_; /// @brief The protected pointer to the Botan HashFunction object - boost::scoped_ptr<Botan::HashFunction> hash_; + std::unique_ptr<Botan::HashFunction> hash_; }; Hash::Hash(const HashAlgorithm hash_algorithm) diff --git a/src/lib/cryptolink/botan_hmac.cc b/src/lib/cryptolink/botan_hmac.cc index 88efb2efc0..14cb00f7e2 100644 --- a/src/lib/cryptolink/botan_hmac.cc +++ b/src/lib/cryptolink/botan_hmac.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2011-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011-2024 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 @@ -9,16 +9,37 @@ #include <cryptolink.h> #include <cryptolink/crypto_hmac.h> -#include <boost/scoped_ptr.hpp> - -#include <botan/hmac.h> -#include <botan/lookup.h> +#include <botan/mac.h> +#include <botan/exceptn.h> #include <cryptolink/botan_common.h> namespace isc { namespace cryptolink { +const std::string +btn::getHmacAlgorithmName(HashAlgorithm algorithm) { + switch (algorithm) { + case isc::cryptolink::MD5: + return ("HMAC(MD5)"); + case isc::cryptolink::SHA1: + return ("HMAC(SHA-1)"); + case isc::cryptolink::SHA256: + return ("HMAC(SHA-256)"); + case isc::cryptolink::SHA224: + return ("HMAC(SHA-224)"); + case isc::cryptolink::SHA384: + return ("HMAC(SHA-384)"); + case isc::cryptolink::SHA512: + return ("HMAC(SHA-512)"); + case isc::cryptolink::UNKNOWN_HASH: + return ("HMAC(Unknown)"); + } + // compiler should have prevented us to reach this, since we have + // no default. But we need a return value anyway + return ("Unknown"); +} + /// @brief Botan implementation of HMAC. Each method is the counterpart /// of the HMAC corresponding method. class HMACImpl { @@ -33,18 +54,11 @@ public: explicit HMACImpl(const void* secret, size_t secret_len, const HashAlgorithm hash_algorithm) : hash_algorithm_(hash_algorithm), hmac_() { - Botan::HashFunction* hash; try { const std::string& name = - btn::getHashAlgorithmName(hash_algorithm); - std::unique_ptr<Botan::HashFunction> hash_ptr = - Botan::HashFunction::create(name); - if (hash_ptr) { - hash = hash_ptr.release(); - } else { - throw Botan::Algorithm_Not_Found(name); - } - } catch (const Botan::Algorithm_Not_Found&) { + btn::getHmacAlgorithmName(hash_algorithm); + hmac_ = Botan::MessageAuthenticationCode::create_or_throw(name); + } catch (const Botan::Lookup_Error&) { isc_throw(UnsupportedAlgorithm, "Unknown hash algorithm: " << static_cast<int>(hash_algorithm)); @@ -52,28 +66,16 @@ public: isc_throw(LibraryError, "Botan error: " << exc.what()); } - hmac_.reset(new Botan::HMAC(hash)); - // If the key length is larger than the block size, we hash the // key itself first. try { - // use a temp var so we don't have blocks spanning - // preprocessor directives - size_t block_length = hash->hash_block_size(); - if (secret_len > block_length) { - Botan::secure_vector<Botan::byte> hashed_key = - hash->process(static_cast<const Botan::byte*>(secret), - secret_len); - hmac_->set_key(&hashed_key[0], hashed_key.size()); - } else { - // Botan 1.8 considers len 0 a bad key. 1.9 does not, - // but we won't accept it anyway, and fail early - if (secret_len == 0) { - isc_throw(BadKey, "Bad HMAC secret length: 0"); - } - hmac_->set_key(static_cast<const Botan::byte*>(secret), - secret_len); + // Botan 1.8 considers len 0 a bad key. 1.9 does not, + // but we won't accept it anyway, and fail early + if (secret_len == 0) { + isc_throw(BadKey, "Bad HMAC secret length: 0"); } + hmac_->set_key(static_cast<const Botan::byte*>(secret), + secret_len); } catch (const Botan::Invalid_Key_Length& ikl) { isc_throw(BadKey, ikl.what()); } catch (const Botan::Exception& exc) { @@ -82,8 +84,7 @@ public: } /// @brief Destructor - ~HMACImpl() { - } + ~HMACImpl() = default; /// @brief Returns the HashAlgorithm of the object HashAlgorithm getHashAlgorithm() const { @@ -177,9 +178,8 @@ public: if (digest_.size() == 0) { digest_ = hmac_->final(); } - return (Botan::same_mem(&digest_[0], - static_cast<const unsigned char*>(sig), - len)); + const uint8_t* sig8 = static_cast<const uint8_t*>(sig); + return (Botan::constant_time_compare(&digest_[0], sig8, len)); } catch (const Botan::Exception& exc) { isc_throw(LibraryError, "Botan error: " << exc.what()); } @@ -190,7 +190,7 @@ private: HashAlgorithm hash_algorithm_; /// @brief The protected pointer to the Botan HMAC object - boost::scoped_ptr<Botan::HMAC> hmac_; + std::unique_ptr<Botan::MessageAuthenticationCode> hmac_; /// @brief The digest cache for multiple verify Botan::secure_vector<Botan::byte> digest_; diff --git a/src/lib/http/tests/Makefile.am b/src/lib/http/tests/Makefile.am index 232d6e2b0c..b2f629d671 100644 --- a/src/lib/http/tests/Makefile.am +++ b/src/lib/http/tests/Makefile.am @@ -54,7 +54,7 @@ libhttp_unittests_SOURCES += tls_response_creator_test.h libhttp_unittests_SOURCES += tls_server_unittests.cc libhttp_unittests_SOURCES += tls_client_unittests.cc endif -if HAVE_BOTAN_BOOST +if HAVE_BOTAN libhttp_unittests_SOURCES += tls_response_creator_test.h libhttp_unittests_SOURCES += tls_server_unittests.cc libhttp_unittests_SOURCES += tls_client_unittests.cc diff --git a/src/lib/tcp/tests/Makefile.am b/src/lib/tcp/tests/Makefile.am index 492c6272c7..422cf49c38 100644 --- a/src/lib/tcp/tests/Makefile.am +++ b/src/lib/tcp/tests/Makefile.am @@ -27,7 +27,7 @@ run_unittests_SOURCES += mt_tcp_listener_mgr_unittests.cc if HAVE_OPENSSL run_unittests_SOURCES += tls_listener_unittests.cc endif -if HAVE_BOTAN_BOOST +if HAVE_BOTAN run_unittests_SOURCES += tls_listener_unittests.cc endif |