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 <theil.markus@gmail.com>
  • Loading branch information
thillux committed Dec 12, 2024
1 parent 679ca89 commit f3d19f7
Show file tree
Hide file tree
Showing 14 changed files with 319 additions and 6 deletions.
2 changes: 1 addition & 1 deletion configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,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', 'tpm2']
third_party = ['boost', 'bzip2', 'esdm_rng', 'lzma', 'commoncrypto', 'sqlite3', 'zlib', 'tpm', 'tpm2']

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 @@ -128,7 +128,7 @@ Other Useful Things
* Full C++ PKCS #11 API wrapper
* Interfaces for TPM v1.2 and v2.0 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
3 changes: 3 additions & 0 deletions src/configs/repo_config.env
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ ANDROID_NDK="android-ndk-r26"
# Jitterentropy library version to be used for testing the 'jitter_rng' module
JITTERENTROPY_VERSION="3.6.0"

# Entropy Source and DRNG Manager (ESDM) bundle version used to test the ESDM adapter
ESDM_VERSION="1.2.0"

# The version of the Intel SDE tool to use for running the Intel SDE tests
INTEL_SDE_VERSION="sde-external-9.38.0-2024-04-18-lin"

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 @@ -276,6 +276,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
10 changes: 10 additions & 0 deletions src/lib/ffi/ffi_rng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#if defined(BOTAN_HAS_JITTER_RNG)
#include <botan/jitter_rng.h>
#endif
#if defined(BOTAN_HAS_ESDM_RNG)
#include <botan/esdm_rng.h>
#endif

extern "C" {

Expand Down Expand Up @@ -54,6 +57,13 @@ int botan_rng_init(botan_rng_t* rng_out, const char* rng_type) {
rng = std::make_unique<Botan::Jitter_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
97 changes: 97 additions & 0 deletions src/lib/rng/esdm_rng/esdm_rng.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* ESDM RNG
* (C) 2024, Markus Theil <theil.markus@gmail.com>
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/esdm_rng.h>

#include <esdm/esdm_rpc_client.h>

namespace Botan {

namespace {
/**
* This helper makes sure that the ESDM service is initialized and
* finalized as needed in a threadsafe fashion. Finalization happens
* as soon as all instances of ESDM_RNG are destructed. This may
* happen multiple times in the lifetime of the process.
*/
class ESDM_Context {
public:
[[nodiscard]] static std::shared_ptr<void> instance() {
static ESDM_Context g_instance;
return g_instance.acquire();
}

private:
ESDM_Context() = default;

[[nodiscard]] std::shared_ptr<void> acquire() {
std::scoped_lock lk(m_mutex);
if(m_refs++ == 0) {
if(esdm_rpcc_init_unpriv_service(nullptr) != 0) {
throw Botan::System_Error("unable to initialize ESDM unprivileged service");
}
}
return std::shared_ptr<void>{nullptr, [this](void*) { this->release(); }};
}

void release() {
std::scoped_lock lk(m_mutex);
if(m_refs-- == 1) {
esdm_rpcc_fini_unpriv_service();
}
}

private:
std::mutex m_mutex;
size_t m_refs = 0;
};
} // namespace

ESDM_RNG::ESDM_RNG(bool prediction_resistance) :
m_prediction_resistance(prediction_resistance), m_ctx(ESDM_Context::instance()) {}

void ESDM_RNG::fill_bytes_with_input(std::span<uint8_t> out, std::span<const uint8_t> in) {
/*
* This variable is implicitly set by the "esdm_invoke" macro that comes
* with the ESDM library. "esdm_invoke" implements a retry mechanism for
* the underlying RPC mechanism of ESDM.
*/
ssize_t ret = 0;

if(!in.empty()) {
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.empty()) {
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
111 changes: 111 additions & 0 deletions src/lib/rng/esdm_rng/esdm_rng.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* ESDM RNG
* (C) 2024, Markus Theil <theil.markus@gmail.com>
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef BOTAN_ESDM_RNG_H_
#define BOTAN_ESDM_RNG_H_

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

namespace Botan {

/**
* Return a shared reference to a global PRNG instance provided by ESDM
*/
BOTAN_PUBLIC_API(3, 7) 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 being
* 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.
*
* Thread safety:
* It is fine to construct, destruct and use objects of this class concurrently.
* The communication with ESDM is thread-safe, as handled by esdm_rpc_client.
* The initialization of esdm_rpc_client is not thread safe, therefore this class
* takes care of it, with its embedded ESDM_Context.
*/
class BOTAN_PUBLIC_API(3, 7) ESDM_RNG final : public Botan::RandomNumberGenerator {
public:
/**
* Default constructor for ESDM, fully seeded mode
*/
ESDM_RNG() : ESDM_RNG(false) {}

/**
* Construct ESDM instance with configurable mode
*/
explicit ESDM_RNG(bool prediction_resistance);

std::string name() const override {
if(m_prediction_resistance) {
return "esdm-pr";
} else {
return "esdm-full";
}
}

/**
* ESDM blocks, if it is not seeded,
*
* @return true
*/
bool is_seeded() const override { return true; }

/**
* ESDM can inject additional inputs
* but we do not account entropy for it
*
* @return true
*/
bool accepts_input() const override { return true; }

/**
* the ESDM RNG does not hold any state outside ESDM, that should be cleared
* here
*/
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
*/
bool m_prediction_resistance;

/**
* takes care of thread-safe esdm_rpc_client initialization
*/
std::shared_ptr<void> m_ctx;
};

} // namespace Botan

#endif /* BOTAN_ESDM_RNG_H_ */
18 changes: 18 additions & 0 deletions src/lib/rng/esdm_rng/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<defines>
ESDM_RNG -> 20241107
</defines>

<module_info>
name -> "ESDM RNG"
brief -> "RNG based on ESDM - Entropy Source and DRNG Manager"
</module_info>

load_on vendor

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

<libs>
linux -> esdm_rpc_client
</libs>
15 changes: 15 additions & 0 deletions src/scripts/ci/setup_gh_actions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ function build_and_install_jitterentropy() {
rm -rf "${jel_dir}"
}

function build_and_install_esdm() {
# build dependencies
sudo apt-get -qq install libprotobuf-c-dev meson

# download, build and install ESDM
curl -L "https://github.com/smuellerDD/esdm/archive/refs/tags/v${ESDM_VERSION}.tar.gz" | tar -xz -C .
pushd "$(realpath esdm-*)"
meson setup build -Dselinux=disabled -Dais2031=false -Dlinux-devfiles=disabled -Des_jent=disabled --prefix=/usr --libdir=lib
meson compile -C build
sudo meson install -C build
popd
}

if [ -z "$REPO_CONFIG_LOADED" ]; then
echo "Repository configuration not loaded" >&2
exit 1
Expand Down Expand Up @@ -81,6 +94,7 @@ if type -p "apt-get"; then
elif [ "$TARGET" = "shared" ]; then
sudo apt-get -qq install libboost-dev "${tpm2_specific_packages[@]}"
echo "BOTAN_TPM2_ENABLED=${ci_support_of_tpm2}" >> "$GITHUB_ENV"
build_and_install_esdm

elif [ "$TARGET" = "examples" ] || [ "$TARGET" = "amalgamation" ] || [ "$TARGET" = "tlsanvil" ] || [ "$TARGET" = "clang-tidy" ] ; then
sudo apt-get -qq install libboost-dev libtss2-dev
Expand Down Expand Up @@ -180,6 +194,7 @@ if type -p "apt-get"; then
echo "PKCS11_LIB=/usr/lib/softhsm/libsofthsm2.so" >> "$GITHUB_ENV"

build_and_install_jitterentropy
build_and_install_esdm

elif [ "$TARGET" = "docs" ]; then
sudo apt-get -qq install doxygen python3-docutils python3-sphinx
Expand Down
Loading

0 comments on commit f3d19f7

Please sign in to comment.