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..faf453e8174 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,7 @@ 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 +113,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..6a4f69529c2 --- /dev/null +++ b/src/lib/rng/esdm_rng/esdm_rng.cpp @@ -0,0 +1,87 @@ +/* + * 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..d787d3e3df4 --- /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); + + virtual ~ESDM_RNG(); + + 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", ]