Skip to content

Commit

Permalink
Add Entropy Source and DRNG Manager (ESDM) RNG support
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
thillux committed Aug 15, 2024
1 parent 06061a9 commit df2d5fc
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 5 deletions.
2 changes: 1 addition & 1 deletion configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 18 additions & 2 deletions src/cli/cli_rng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
#include <botan/auto_rng.h>
#endif

#if defined(BOTAN_HAS_ESDM_RNG)
#include <botan/esdm_rng.h>
#endif

#if defined(BOTAN_HAS_SYSTEM_RNG)
#include <botan/system_rng.h>
#endif
Expand All @@ -36,6 +40,15 @@ std::shared_ptr<Botan::RandomNumberGenerator> cli_make_rng(const std::string& rn
}
#endif

#if defined(BOTAN_HAS_ESDM_RNG)
if(rng_type == "esdm-full") {
return std::make_shared<Botan::ESDM_RNG>(false);
}
if(rng_type == "esdm-pr") {
return std::make_shared<Botan::ESDM_RNG>(true);
}
#endif

const std::vector<uint8_t> drbg_seed = Botan::hex_decode(hex_drbg_seed);

#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
Expand Down Expand Up @@ -89,7 +102,10 @@ std::shared_ptr<Botan::RandomNumberGenerator> 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"; }

Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/lib/ffi/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions src/lib/ffi/ffi_rng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
#include <botan/processor_rng.h>
#endif

#if defined(BOTAN_HAS_ESDM_RNG)
#include <botan/esdm_rng.h>
#endif

extern "C" {

using namespace Botan_FFI;
Expand All @@ -45,6 +49,13 @@ int botan_rng_init(botan_rng_t* rng_out, const char* rng_type) {
rng = std::make_unique<Botan::Processor_RNG>();
}
#endif
#if defined(BOTAN_HAS_ESDM_RNG)
else if(rng_type_s == "esdm-full") {
rng = std::make_unique<Botan::ESDM_RNG>(false);
} else if(rng_type_s == "esdm-pr") {
rng = std::make_unique<Botan::ESDM_RNG>(true);
}
#endif

if(!rng) {
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
Expand Down
88 changes: 88 additions & 0 deletions src/lib/rng/esdm_rng/esdm_rng.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* ESDM RNG
* (C) 2024, Markus Theil <[email protected]>
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "esdm_rng.h"
#include <esdm/esdm_rpc_client.h>

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<uint8_t> out, std::span<const uint8_t> 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<ssize_t>(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
107 changes: 107 additions & 0 deletions src/lib/rng/esdm_rng/esdm_rng.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* ESDM RNG
* (C) 2024, Markus Theil <[email protected]>
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef ESDM_RNG_H
#define ESDM_RNG_H

#include <botan/rng.h>
#include <mutex>

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<uint8_t> out, std::span<const uint8_t> 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 */
16 changes: 16 additions & 0 deletions src/lib/rng/esdm_rng/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<defines>
ESDM_RNG -> 20240814
</defines>

<module_info>
name -> "ESDM RNG"
brief -> "ESDM-based RNG"
</module_info>

<header:public>
esdm_rng.h
</header:public>

<requires>
esdm
</requires>
13 changes: 13 additions & 0 deletions src/lib/utils/esdm/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<defines>
ESDM -> 20240814
</defines>

<module_info>
name -> "ESDM"
</module_info>

load_on vendor

<libs>
linux -> esdm_rpc_client
</libs>
2 changes: 1 addition & 1 deletion src/scripts/config_for_oss_fuzz.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand Down

0 comments on commit df2d5fc

Please sign in to comment.