From 9e742d3034280a840481b7ad6f321f8e95e6029b Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Thu, 23 Aug 2018 18:10:10 +0200 Subject: [29-cryptolink-random-generator] Added RNG support --- src/lib/cryptolink/Makefile.am | 1 + src/lib/cryptolink/botan1_link.cc | 40 ++++++++++++++++- src/lib/cryptolink/botan_link.cc | 41 +++++++++++++++++- src/lib/cryptolink/crypto_rng.cc | 33 ++++++++++++++ src/lib/cryptolink/crypto_rng.h | 64 ++++++++++++++++++++++++++++ src/lib/cryptolink/cryptolink.cc | 5 +++ src/lib/cryptolink/cryptolink.h | 19 ++++++++- src/lib/cryptolink/openssl_link.cc | 42 ++++++++++++++++-- src/lib/cryptolink/tests/crypto_unittests.cc | 28 ++++++++++++ 9 files changed, 265 insertions(+), 8 deletions(-) create mode 100644 src/lib/cryptolink/crypto_rng.cc create mode 100644 src/lib/cryptolink/crypto_rng.h (limited to 'src') diff --git a/src/lib/cryptolink/Makefile.am b/src/lib/cryptolink/Makefile.am index b7e14ca55d..9b3c9fde01 100644 --- a/src/lib/cryptolink/Makefile.am +++ b/src/lib/cryptolink/Makefile.am @@ -11,6 +11,7 @@ lib_LTLIBRARIES = libkea-cryptolink.la libkea_cryptolink_la_SOURCES = cryptolink.h cryptolink.cc libkea_cryptolink_la_SOURCES += crypto_hash.h crypto_hash.cc libkea_cryptolink_la_SOURCES += crypto_hmac.h crypto_hmac.cc +libkea_cryptolink_la_SOURCES += crypto_rng.h crypto_rng.cc if HAVE_BOTAN1 libkea_cryptolink_la_SOURCES += botan1_link.cc libkea_cryptolink_la_SOURCES += botan_common.h diff --git a/src/lib/cryptolink/botan1_link.cc b/src/lib/cryptolink/botan1_link.cc index a367c4ea6e..ce6bd8d6fa 100644 --- a/src/lib/cryptolink/botan1_link.cc +++ b/src/lib/cryptolink/botan1_link.cc @@ -9,9 +9,11 @@ #include #include #include +#include #include #include +#include namespace isc { namespace cryptolink { @@ -23,19 +25,55 @@ private: }; CryptoLink::~CryptoLink() { + rng_.reset(); delete impl_; } +/// \brief Botan implementation of RNG. +class RNGImpl : public RNG { +public: + RNGImpl() { + rng.reset(new Botan::AutoSeeded_RNG()); + } + + ~RNGImpl() { + } + +private: + std::vector random(size_t len) { + std::vector data; + if (len > 0) { + data.resize(len); + try { + rng->randomize(&data[0], len); + } catch (const Botan::Exception& ex) { + isc_throw(isc::cryptolink::LibraryError, + "Botan error: " << ex.what()); + } + } + return (data); + } + + boost::shared_ptr rng; +}; + void CryptoLink::initialize() { CryptoLink& c = getCryptoLinkInternal(); - if (c.impl_ == NULL) { + if (!c.impl_) { try { c.impl_ = new CryptoLinkImpl(); } catch (const Botan::Exception& ex) { isc_throw(InitializationError, "Botan error: " << ex.what()); } } + if (!c.rng_) { + try { + c.rng_.reset(new RNGImpl()); + } catch (const Botan::Exception& ex) { + isc_throw(InitializationError, "Botan error: " << ex.what()); + } + } } std::string diff --git a/src/lib/cryptolink/botan_link.cc b/src/lib/cryptolink/botan_link.cc index c7f40cb182..714663d507 100644 --- a/src/lib/cryptolink/botan_link.cc +++ b/src/lib/cryptolink/botan_link.cc @@ -9,9 +9,11 @@ #include #include #include +#include #include #include +#include namespace isc { namespace cryptolink { @@ -25,16 +27,53 @@ CryptoLink::~CryptoLink() { delete impl_; } +/// \brief Botan implementation of RNG. +class RNGImpl : public RNG { +public: + RNGImpl() { + rng.reset(new Botan::AutoSeeded_RNG()); + } + + ~RNGImpl() { + } + +private: + std::vector random(size_t len) { + std::vector data; + if (len > 0) { + data.resize(len); + try { + rng->randomize(&data[0], len); + } catch (const Botan::Exception& ex) { + isc_throw(isc::cryptolink::LibraryError, + "Botan error: " << ex.what()); + } + } + return (data); + } + + boost::shared_ptr rng; +}; + void CryptoLink::initialize() { CryptoLink& c = getCryptoLinkInternal(); - if (c.impl_ == NULL) { + if (!c.impl_) { try { c.impl_ = new CryptoLinkImpl(); } catch (const Botan::Exception& ex) { isc_throw(InitializationError, "Botan error: " << ex.what()); } } + if (!c.rng_) { + try { + c.rng_.reset(new RNGImpl()); + } catch (const Botan::Exception& ex) { + isc_throw(InitializationError, "Botan error: " << ex.what()); + } + } + // A not yet fixed bug makes RNG to be destroyed after memory pool... + atexit([]{ getCryptoLink().getRNG().reset(); }); } std::string diff --git a/src/lib/cryptolink/crypto_rng.cc b/src/lib/cryptolink/crypto_rng.cc new file mode 100644 index 0000000000..54dacce408 --- /dev/null +++ b/src/lib/cryptolink/crypto_rng.cc @@ -0,0 +1,33 @@ +// Copyright (C) 2018 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 + +#include +#include + +#include + +#include + +namespace isc { +namespace cryptolink { + +RNG::RNG() { +} + +RNG::~RNG() { +} + +std::vector +random(size_t len) +{ + RNGPtr rng(CryptoLink::getCryptoLink().getRNG()); + return (rng->random(len)); +} + +} // namespace cryptolink +} // namespace isc diff --git a/src/lib/cryptolink/crypto_rng.h b/src/lib/cryptolink/crypto_rng.h new file mode 100644 index 0000000000..916321e7ca --- /dev/null +++ b/src/lib/cryptolink/crypto_rng.h @@ -0,0 +1,64 @@ +// Copyright (C) 2018 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 + +#include + +#ifndef ISC_CRYPTO_RNG_H +#define ISC_CRYPTO_RNG_H + +namespace isc { +namespace cryptolink { + +/// \brief RNG support +/// +/// This class is used to get the RNG. +/// The global instance can be get with CryptoLink::getRNG() +/// +class RNG : private boost::noncopyable { +public: + /// \brief Constructor from a Random Number Generator + /// + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + RNG(); + + /// \brief Destructor + virtual ~RNG(); + + /// \brief Generate random value. + /// + /// The result will be returned as a std::vector + /// + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + /// + /// \param len The number of bytes from the result to generate. + /// \return a vector containing random value. + virtual std::vector random(size_t len) = 0; + +private: + friend RNGPtr& CryptoLink::getRNG(); +}; + +/// \brief Generate random value. +/// +/// This is a convenience function that generate random data +/// given a fixed amount of data. Internally it does the same as +/// creating an RNG object and generating the resulting value. +/// +/// \exception LibraryError if there was any unexpected exception +/// in the underlying library +/// +/// \param len The length of the data +std::vector random(size_t len); + +} // namespace cryptolink +} // namespace isc + +#endif // ISC_CRYPTO_RNG_H + diff --git a/src/lib/cryptolink/cryptolink.cc b/src/lib/cryptolink/cryptolink.cc index 3bae155ce5..a768ee206b 100644 --- a/src/lib/cryptolink/cryptolink.cc +++ b/src/lib/cryptolink/cryptolink.cc @@ -41,6 +41,11 @@ CryptoLink::createHMAC(const void* secret, size_t secret_len, return (new HMAC(secret, secret_len, hash_algorithm)); } +RNGPtr& +CryptoLink::getRNG() { + return (rng_); +} + } // namespace cryptolink } // namespace isc diff --git a/src/lib/cryptolink/cryptolink.h b/src/lib/cryptolink/cryptolink.h index 1856b34137..b3e357835d 100644 --- a/src/lib/cryptolink/cryptolink.h +++ b/src/lib/cryptolink/cryptolink.h @@ -12,7 +12,7 @@ #include #include -#include +#include #include @@ -42,6 +42,10 @@ class Hash; // Forward declaration for createHMAC() class HMAC; +// Forward declaration for getRNG() +class RNG; +typedef boost::shared_ptr RNGPtr; + /// General exception class that is the base for all crypto-related /// exceptions class CryptoLinkError : public Exception { @@ -84,8 +88,9 @@ public: CryptoLinkError(file, line, what) {} }; -/// Forward declaration for pimpl +/// Forward declarations for pimpl class CryptoLinkImpl; +class RNGImpl; /// \brief Singleton entry point and factory class /// @@ -209,6 +214,14 @@ public: HMAC* createHMAC(const void* secret, size_t secret_len, const HashAlgorithm hash_algorithm); + /// \brief Get the global RNG + /// + /// \exception NotImplemented if the method was not implemented + /// in a derived class + /// \exception LibraryError if there was any unexpected exception + /// in the underlying library + virtual RNGPtr& getRNG(); + private: // To enable us to use an optional explicit initialization call, // the 'real' instance getter is private @@ -220,6 +233,8 @@ private: ~CryptoLink(); CryptoLinkImpl* impl_; + + RNGPtr rng_; }; } // namespace cryptolink diff --git a/src/lib/cryptolink/openssl_link.cc b/src/lib/cryptolink/openssl_link.cc index dac6ee4fd1..1a573f7c41 100644 --- a/src/lib/cryptolink/openssl_link.cc +++ b/src/lib/cryptolink/openssl_link.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2018 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 @@ -7,27 +7,49 @@ #include #include +#include #include #include #include +#include namespace isc { namespace cryptolink { // For OpenSSL, we use the CryptoLink class object in RAII style class CryptoLinkImpl { - // empty class }; CryptoLink::~CryptoLink() { delete impl_; } +/// \brief OpenSSL implementation of RNG. +class RNGImpl : public RNG { +public: + RNGImpl() { } + + ~RNGImpl() { } + +private: + std::vector random(size_t len) { + std::vector data; + if (len > 0) { + data.resize(len); + if (RAND_bytes(&data[0], len) != 1) { + isc_throw(isc::cryptolink::LibraryError, + "OpenSSL RAND_bytes() failed"); + } + } + return (data); + } +}; + void CryptoLink::initialize() { CryptoLink& c = getCryptoLinkInternal(); - if (c.impl_ == NULL) { + if (!c.impl_) { try { c.impl_ = new CryptoLinkImpl(); } catch (const std::exception &ex) { @@ -40,6 +62,19 @@ CryptoLink::initialize() { "Error during OpenSSL initialization"); } } + if (!c.rng_) { + try { + c.rng_.reset(new RNGImpl()); + } catch (const std::exception &ex) { + // Should never happen + isc_throw(InitializationError, + "Error during OpenSSL RNG initialization:" << ex.what()); + } catch (...) { + // Should never happen + isc_throw(InitializationError, + "Error during OpenSSL RNG initialization"); + } + } } std::string @@ -49,4 +84,3 @@ CryptoLink::getVersion() { } // namespace cryptolink } // namespace isc - diff --git a/src/lib/cryptolink/tests/crypto_unittests.cc b/src/lib/cryptolink/tests/crypto_unittests.cc index d512ecaa5a..39dc207400 100644 --- a/src/lib/cryptolink/tests/crypto_unittests.cc +++ b/src/lib/cryptolink/tests/crypto_unittests.cc @@ -11,7 +11,9 @@ #include #include +#include +using namespace std; using namespace isc::cryptolink; // Test get version @@ -25,3 +27,29 @@ TEST(CryptoLinkTest, Singleton) { const CryptoLink& c2 = CryptoLink::getCryptoLink(); ASSERT_EQ(&c1, &c2); } + +// Tests whether getRNG() returns a global value +TEST(CryptoLinkTest, GlobalRNG) { + CryptoLink& c = CryptoLink::getCryptoLink(); + RNGPtr rng1 = c.getRNG(); + RNGPtr rng2 = c.getRNG(); + ASSERT_EQ(rng1, rng2); +} + +// Tests whether RNG works +TEST(CryptoLinkTest, RNG) { + RNGPtr rng = CryptoLink::getCryptoLink().getRNG(); + vector data; + ASSERT_NO_THROW(data = rng->random(16)); + ASSERT_EQ(16, data.size()); + vector zero; + zero.resize(16); + EXPECT_NE(0, memcmp(&zero[0], &data[0], zero.size())); + + // Retry with the function (vs method) + vector dataf; + ASSERT_NO_THROW(dataf = random(16)); + ASSERT_EQ(16, dataf.size()); + EXPECT_NE(0, memcmp(&zero[0], &dataf[0], zero.size())); + EXPECT_NE(0, memcmp(&data[0], &dataf[0], zero.size())); +} -- cgit v1.2.3