From df2d5fc875586d7b7b30aa0fa5cfd0e920751cd5 Mon Sep 17 00:00:00 2001 From: Markus Theil Date: Wed, 14 Aug 2024 22:45:13 +0200 Subject: [PATCH] Add Entropy Source and DRNG Manager (ESDM) RNG support ESDM is a Linux-based user-space PRNG daemon, with configurable entropy sources. See: https://github.com/smuellerDD/esdm It currently gets used, when a high level of control over entropy sources is desirable, e.g. for VPN appliance solutions. In order to use this source, the ESDM server daemon has to be running and botan must be configured --with-esdm. ESDM currently works only on Linux. Signed-off-by: Markus Theil --- configure.py | 2 +- readme.rst | 2 +- src/cli/cli_rng.cpp | 20 +++++- src/lib/ffi/ffi.h | 2 + src/lib/ffi/ffi_rng.cpp | 11 +++ src/lib/rng/esdm_rng/esdm_rng.cpp | 88 ++++++++++++++++++++++++ src/lib/rng/esdm_rng/esdm_rng.h | 107 +++++++++++++++++++++++++++++ src/lib/rng/esdm_rng/info.txt | 16 +++++ src/lib/utils/esdm/info.txt | 13 ++++ src/scripts/config_for_oss_fuzz.py | 2 +- 10 files changed, 258 insertions(+), 5 deletions(-) create mode 100644 src/lib/rng/esdm_rng/esdm_rng.cpp create mode 100644 src/lib/rng/esdm_rng/esdm_rng.h create mode 100644 src/lib/rng/esdm_rng/info.txt create mode 100644 src/lib/utils/esdm/info.txt diff --git a/configure.py b/configure.py index 04a50fc2f1e..ab8bcfe6d95 100755 --- a/configure.py +++ b/configure.py @@ -582,7 +582,7 @@ def add_enable_disable_pair(group, what, default, msg=optparse.SUPPRESS_HELP): 'disable building of deprecated features and modules') # Should be derived from info.txt but this runs too early - third_party = ['boost', 'bzip2', 'lzma', 'commoncrypto', 'sqlite3', 'zlib', 'tpm'] + third_party = ['boost', 'bzip2', 'esdm', 'lzma', 'commoncrypto', 'sqlite3', 'zlib', 'tpm'] for mod in third_party: mods_group.add_option('--with-%s' % (mod), diff --git a/readme.rst b/readme.rst index 3ee1fe5af46..bca1ee3eb58 100644 --- a/readme.rst +++ b/readme.rst @@ -127,7 +127,7 @@ Other Useful Things * Full C++ PKCS #11 API wrapper * Interfaces for TPM v1.2 device access * Simple compression API wrapping zlib, bzip2, and lzma libraries -* RNG wrappers for system RNG and hardware RNGs +* RNG wrappers for system RNG, ESDM and hardware RNGs * HMAC_DRBG and entropy collection system for userspace RNGs * SRP-6a password authenticated key exchange * Key derivation functions including HKDF, KDF2, SP 800-108, SP 800-56A, SP 800-56C diff --git a/src/cli/cli_rng.cpp b/src/cli/cli_rng.cpp index af6a00b13a0..4d375025a21 100644 --- a/src/cli/cli_rng.cpp +++ b/src/cli/cli_rng.cpp @@ -14,6 +14,10 @@ #include #endif +#if defined(BOTAN_HAS_ESDM_RNG) + #include +#endif + #if defined(BOTAN_HAS_SYSTEM_RNG) #include #endif @@ -36,6 +40,15 @@ std::shared_ptr cli_make_rng(const std::string& rn } #endif +#if defined(BOTAN_HAS_ESDM_RNG) + if(rng_type == "esdm-full") { + return std::make_shared(false); + } + if(rng_type == "esdm-pr") { + return std::make_shared(true); + } +#endif + const std::vector drbg_seed = Botan::hex_decode(hex_drbg_seed); #if defined(BOTAN_HAS_AUTO_SEEDING_RNG) @@ -89,7 +102,10 @@ std::shared_ptr cli_make_rng(const std::string& rn class RNG final : public Command { public: - RNG() : Command("rng --format=hex --system --rdrand --auto --entropy --drbg --drbg-seed= *bytes") {} + RNG() : + Command( + "rng --format=hex --system --esdm-full --esdm-pr --rdrand --auto --entropy --drbg --drbg-seed= *bytes") { + } std::string group() const override { return "misc"; } @@ -100,7 +116,7 @@ class RNG final : public Command { std::string type = get_arg("rng-type"); if(type.empty()) { - for(std::string flag : {"system", "rdrand", "auto", "entropy", "drbg"}) { + for(std::string flag : {"system", "rdrand", "auto", "entropy", "drbg", "esdm-full", "esdm-pr"}) { if(flag_set(flag)) { type = flag; break; diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h index c0fd79f8b1a..acde50c9f90 100644 --- a/src/lib/ffi/ffi.h +++ b/src/lib/ffi/ffi.h @@ -264,6 +264,8 @@ typedef struct botan_rng_struct* botan_rng_t; * @param rng rng object * @param rng_type type of the rng, possible values: * "system": system RNG +* "esdm-full": ESDM RNG (fully seeded) +* "esdm-pr": ESDM RNG (w. prediction resistance) * "user": userspace RNG * "user-threadsafe": userspace RNG, with internal locking * "rdrand": directly read RDRAND diff --git a/src/lib/ffi/ffi_rng.cpp b/src/lib/ffi/ffi_rng.cpp index 9a23cde3672..46dd166105e 100644 --- a/src/lib/ffi/ffi_rng.cpp +++ b/src/lib/ffi/ffi_rng.cpp @@ -19,6 +19,10 @@ #include #endif +#if defined(BOTAN_HAS_ESDM_RNG) + #include +#endif + extern "C" { using namespace Botan_FFI; @@ -45,6 +49,13 @@ int botan_rng_init(botan_rng_t* rng_out, const char* rng_type) { rng = std::make_unique(); } #endif +#if defined(BOTAN_HAS_ESDM_RNG) + else if(rng_type_s == "esdm-full") { + rng = std::make_unique(false); + } else if(rng_type_s == "esdm-pr") { + rng = std::make_unique(true); + } +#endif if(!rng) { return BOTAN_FFI_ERROR_NOT_IMPLEMENTED; diff --git a/src/lib/rng/esdm_rng/esdm_rng.cpp b/src/lib/rng/esdm_rng/esdm_rng.cpp new file mode 100644 index 00000000000..0dc0eb327ab --- /dev/null +++ b/src/lib/rng/esdm_rng/esdm_rng.cpp @@ -0,0 +1,88 @@ +/* + * ESDM RNG + * (C) 2024, Markus Theil + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#include "esdm_rng.h" +#include + +namespace Botan { + +ESDM_RNG::ESDM_RNG() : ESDM_RNG(false) {} + +ESDM_RNG::ESDM_RNG(bool prediction_resistance) : m_prediction_resistance(prediction_resistance) { + std::lock_guard lg(m_init_lock); + + if(m_ref_cnt == 0) { + if(esdm_rpcc_init_unpriv_service(nullptr) != 0) { + throw Botan::System_Error("unable to initialize ESDM unprivileged service"); + } + } + ++m_ref_cnt; +} + +ESDM_RNG::~ESDM_RNG() { + std::lock_guard lg(m_init_lock); + + if(m_ref_cnt == 1) { + esdm_rpcc_fini_unpriv_service(); + } + --m_ref_cnt; +} + +std::string ESDM_RNG::name() const { + if(m_prediction_resistance) { + return "esdm-pr"; + } else { + return "esdm-full"; + } +} + +/* + * as long as we use only the _full and _pr calls, just return true here + */ +bool ESDM_RNG::is_seeded() const { + return true; +} + +bool ESDM_RNG::accepts_input() const { + return true; +} + +/* + * the ESDM RNG does not hold any state outside ESDM, that should be cleared + * here + */ +void ESDM_RNG::clear() {} + +void ESDM_RNG::fill_bytes_with_input(std::span out, std::span in) { + if(in.size() > 0) { + ssize_t ret = 0; + // take additional input, but do not account entropy for it, + // as this information is not included in the API + esdm_invoke(esdm_rpcc_write_data(in.data(), in.size())); + if(ret != 0) { + throw Botan::System_Error("Writing additional input to ESDM failed"); + } + } + if(out.size() > 0) { + ssize_t ret = 0; + if(m_prediction_resistance) { + esdm_invoke(esdm_rpcc_get_random_bytes_pr(out.data(), out.size())); + } else { + esdm_invoke(esdm_rpcc_get_random_bytes_full(out.data(), out.size())); + } + if(ret != static_cast(out.size())) { + throw Botan::System_Error("Fetching random bytes from ESDM failed"); + } + } +} + +RandomNumberGenerator& esdm_rng() { + static ESDM_RNG g_esdm_rng; + return g_esdm_rng; +} + +}; // namespace Botan diff --git a/src/lib/rng/esdm_rng/esdm_rng.h b/src/lib/rng/esdm_rng/esdm_rng.h new file mode 100644 index 00000000000..3c1e465f42d --- /dev/null +++ b/src/lib/rng/esdm_rng/esdm_rng.h @@ -0,0 +1,107 @@ +/* + * ESDM RNG + * (C) 2024, Markus Theil + * + * Botan is released under the Simplified BSD License (see license.txt) + */ + +#ifndef ESDM_RNG_H +#define ESDM_RNG_H + +#include +#include + +namespace Botan { + +/** + * Return a shared reference to a global PRNG instance provided by ESDM + */ +BOTAN_PUBLIC_API(3, 6) RandomNumberGenerator& esdm_rng(); + +/** + * Entropy Source and DRNG Manager (ESDM) is a Linux/Unix based + * PRNG manager, which can be seeded from NTG.1/SP800-90B sources. + * + * See: + * - Repository: https://github.com/smuellerDD/esdm + * - Further Docs: https://www.chronox.de/esdm/index.html + * + * Different entropy sources can be configured in respect of beeing + * active and how much entropy is accounted for each of them. + * + * ESDM tracks its seed and reseed status and blocks, when not fully seeded. + * For this functionality, the esdm_rpcc_get_random_bytes_pr (prediction resistant) or + * esdm_rpcc_get_random_bytes_full (fully seeded) calls have to be used. + * + * Configurable modes: + * - fully seeded (-> fast): provide entropy from a DRBG/PRNG after beeing fully seeded, + * block until this point is reached, reseed from after a time + * and/or invocation limit, block again if reseeding is not possible + * - prediction resistance (-> slow): reseed ESDM with fresh entropy after each invocation + * + * You typically want to use the fast fully seeded mode, which is the default. + * + * Instances of this class communicate over RPC with ESDM. The esdm_rpc_client + * library, provided by ESDM, is leveraged for this. + */ +class BOTAN_PUBLIC_API(3, 6) ESDM_RNG final : public Botan::RandomNumberGenerator { + public: + /** + * Default constructor for ESDM, fully seeded mode + */ + ESDM_RNG(); + + /** + * Construct ESDM instance with configurable mode + * + * @param prediction_resistance: use prediction resistant mode if true, + * fully seeded mode otherwise + */ + explicit ESDM_RNG(bool prediction_resistance); + + ~ESDM_RNG() override; + + std::string name() const override; + + /** + * ESDM blocks, if it is not seeded, + * + * @return true + */ + bool is_seeded() const override; + + /** + * ESDM can inject additional inputs + * but we do not account entropy for it + * + * @return true + */ + bool accepts_input() const override; + + void clear() override; + + protected: + void fill_bytes_with_input(std::span out, std::span in) override; + + private: + /** + * tracks if predicition resistant or fully seeded interface should be queried + */ + const bool m_prediction_resistance; + + /** + * ESDM rpc client locks concurrent accesses, but initialization should be + * only done once + */ + inline static std::mutex m_init_lock; + + /** + * counts how many ESDM RNG instances are active in order to perform init and + * fini on first/last one + */ + inline static size_t m_ref_cnt = 0; +}; + +}; // namespace Botan + +#endif /* ESDM_RNG_H */ diff --git a/src/lib/rng/esdm_rng/info.txt b/src/lib/rng/esdm_rng/info.txt new file mode 100644 index 00000000000..53cb50fa979 --- /dev/null +++ b/src/lib/rng/esdm_rng/info.txt @@ -0,0 +1,16 @@ + +ESDM_RNG -> 20240814 + + + +name -> "ESDM RNG" +brief -> "ESDM-based RNG" + + + +esdm_rng.h + + + +esdm + \ No newline at end of file diff --git a/src/lib/utils/esdm/info.txt b/src/lib/utils/esdm/info.txt new file mode 100644 index 00000000000..baaf37c8e3a --- /dev/null +++ b/src/lib/utils/esdm/info.txt @@ -0,0 +1,13 @@ + +ESDM -> 20240814 + + + +name -> "ESDM" + + +load_on vendor + + +linux -> esdm_rpc_client + diff --git a/src/scripts/config_for_oss_fuzz.py b/src/scripts/config_for_oss_fuzz.py index e9967a166df..3c574dd3991 100755 --- a/src/scripts/config_for_oss_fuzz.py +++ b/src/scripts/config_for_oss_fuzz.py @@ -29,7 +29,7 @@ "--build-fuzzers=libfuzzer", "--build-targets=static", "--without-os-features=getrandom,getentropy", - "--disable-modules=system_rng,processor_rng", + "--disable-modules=system_rng,processor_rng,esdm_rng", "--with-fuzzer-lib=FuzzingEngine", ]