Skip to content

Commit

Permalink
CPU jitter RNG and entropy source
Browse files Browse the repository at this point in the history
  • Loading branch information
dirkz committed Oct 8, 2024
1 parent 7f256a0 commit 57ccd78
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 0 deletions.
9 changes: 9 additions & 0 deletions doc/api_ref/rng.rst
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,15 @@ PKCS11_RNG

This RNG type allows using the RNG exported from a hardware token accessed via PKCS11.

Jitter_RNG
^^^^^^^^^^^^^^^^^

This is an RNG based on low-level CPU timing jitter, using the
`jitterentropy library <https://github.com/smuellerDD/jitterentropy-library>`_.

Can be enabled with ``configure.py`` via ``--enable-module="jitter_rng"``, provided
you have the library installed and made available to the build, including headers.

Entropy Sources
---------------------------------

Expand Down
28 changes: 28 additions & 0 deletions src/lib/entropy/entropy_srcs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
#include <botan/internal/getentropy.h>
#endif

#if defined(BOTAN_HAS_JITTER_RNG)
#include <botan/jitter_rng.h>
#endif

namespace Botan {

namespace {
Expand Down Expand Up @@ -83,6 +87,24 @@ class Processor_RNG_EntropySource final : public Entropy_Source {

#endif

#if defined(BOTAN_HAS_JITTER_RNG)

class Jitter_RNG_EntropySource final : public Entropy_Source {
public:
size_t poll(RandomNumberGenerator& rng) override {
const size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS;
rng.reseed_from_rng(m_rng, poll_bits);
return poll_bits;
}

std::string name() const override { return m_rng.name(); }

private:
Jitter_RNG m_rng;
};

#endif

} // namespace

std::unique_ptr<Entropy_Source> Entropy_Source::create(std::string_view name) {
Expand Down Expand Up @@ -118,6 +140,12 @@ std::unique_ptr<Entropy_Source> Entropy_Source::create(std::string_view name) {
}
#endif

#if defined(BOTAN_HAS_JITTER_RNG)
if(name == "jitter_rng") {
return std::make_unique<Jitter_RNG_EntropySource>();
}
#endif

BOTAN_UNUSED(name);
return nullptr;
}
Expand Down
9 changes: 9 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_JITTER_RNG)
#include <botan/jitter_rng.h>
#endif

extern "C" {

using namespace Botan_FFI;
Expand All @@ -45,6 +49,11 @@ 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_JITTER_RNG)
else if(rng_type_s == "jitter") {
rng = std::make_unique<Botan::Jitter_RNG>();
}
#endif

if(!rng) {
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
Expand Down
17 changes: 17 additions & 0 deletions src/lib/rng/jitter_rng/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
load_on dep

<module_info>
name -> "CPU Jitter Random Number Generator"
</module_info>

<defines>
JITTER_RNG -> 20240901
</defines>

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

<libs>
all -> jitterentropy
</libs>
96 changes: 96 additions & 0 deletions src/lib/rng/jitter_rng/jitter_rng.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* CPU Jitter Random Number Generator
* (C) 2024 Planck Security S.A.
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/jitter_rng.h>

#include <jitterentropy.h>

namespace Botan {

struct Jitter_RNG_Internal {
Jitter_RNG_Internal();
~Jitter_RNG_Internal();
void collect_into_buffer(std::span<uint8_t> buf);

private:
rand_data* m_rand_data;
};

Jitter_RNG_Internal::Jitter_RNG_Internal() {
static int result = jent_entropy_init();

// no further details documented regarding the return value
BOTAN_ASSERT(result == 0, "JitterRNG: initialization successful");

constexpr unsigned int oversampling_rate = 0; // use default oversampling
constexpr unsigned int flags = 0;

m_rand_data = jent_entropy_collector_alloc(oversampling_rate, flags);
BOTAN_ASSERT_NONNULL(m_rand_data);
}

Jitter_RNG_Internal::~Jitter_RNG_Internal() {
if(m_rand_data) {
jent_entropy_collector_free(m_rand_data);
m_rand_data = nullptr;
}
}

void Jitter_RNG_Internal::collect_into_buffer(std::span<uint8_t> buf) {
if(buf.empty()) {
return;
}

BOTAN_STATE_CHECK(m_rand_data != nullptr);

ssize_t num_bytes = jent_read_entropy_safe(&m_rand_data, reinterpret_cast<char*>(buf.data()), buf.size());
if(num_bytes < 0) {
const auto error_msg = [&]() -> std::string_view {
switch(num_bytes) {
case -1: // should never happen because of the check above
return "JitterRNG: Uninitilialized";
case -2:
return "JitterRNG: SP800-90B repetition count online health test failed";
case -3:
return "JitterRNG: SP800-90B adaptive proportion online health test failed";
case -4:
return "JitterRNG: Internal timer generator could not be initialized";
case -5:
return "JitterRNG: LAG predictor health test failed";
case -6:
return "JitterRNG: Repetitive count test (RCT) failed permanently";
case -7:
return "JitterRNG: Adaptive proportion test (APT) failed permanently";
case -8:
return "JitterRNG: LAG prediction test failed permanently";
default:
return "JitterRNG: Error reading entropy";
}
}();
throw Internal_Error(error_msg);
}

// According to the docs, `jent_read_entropy` itself runs its logic as often
// as necessary to gather the requested number of bytes,
// so this should actually never happen.
BOTAN_ASSERT(static_cast<size_t>(num_bytes) == buf.size(), "JitterRNG produced the expected number of bytes");
}

Jitter_RNG::Jitter_RNG() : m_jitter{std::make_unique<Jitter_RNG_Internal>()} {}

Jitter_RNG::~Jitter_RNG() = default;

void Jitter_RNG::clear() {
m_jitter = std::make_unique<Jitter_RNG_Internal>();
}

void Jitter_RNG::fill_bytes_with_input(std::span<uint8_t> out, std::span<const uint8_t> in) {
BOTAN_UNUSED(in);

m_jitter->collect_into_buffer(out);
}
}; // namespace Botan
40 changes: 40 additions & 0 deletions src/lib/rng/jitter_rng/jitter_rng.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* CPU Jitter Random Number Generator
* (C) 2024 Planck Security S.A.
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#ifndef BOTAN_JITTER_RNG_H_
#define BOTAN_JITTER_RNG_H_

#include <botan/rng.h>

namespace Botan {

struct Jitter_RNG_Internal;

/*
* RNG using libjitterentropy (https://github.com/smuellerDD/jitterentropy-library).
*/
class BOTAN_PUBLIC_API(3, 6) Jitter_RNG final : public RandomNumberGenerator {
public:
Jitter_RNG();
~Jitter_RNG();

std::string name() const override { return "JitterRNG"; }

bool is_seeded() const override { return true; }

bool accepts_input() const override { return false; }

void clear() override;

private:
void fill_bytes_with_input(std::span<uint8_t> out, std::span<const uint8_t> in) override;

std::unique_ptr<Jitter_RNG_Internal> m_jitter;
};
} // namespace Botan

#endif
9 changes: 9 additions & 0 deletions src/tests/test_ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,15 @@ class FFI_RNG_Test final : public FFI_Test {
result.test_eq("custom_destroy_cb called", cb_counter, 5);
}

#ifdef BOTAN_HAS_JITTER_RNG
botan_rng_t jitter_rng;
if(TEST_FFI_OK(botan_rng_init, (&jitter_rng, "jitter"))) {
std::vector<uint8_t> buf(256);
TEST_FFI_OK(botan_rng_get, (jitter_rng, outbuf.data(), buf.size()));
TEST_FFI_OK(botan_rng_destroy, (jitter_rng));
}
#endif

TEST_FFI_OK(botan_rng_destroy, (rng));
TEST_FFI_OK(botan_rng_destroy, (null_rng));
TEST_FFI_OK(botan_rng_destroy, (system_rng));
Expand Down
60 changes: 60 additions & 0 deletions src/tests/test_jitter_rng.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* (C) 2024 Planck Security S.A.
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <vector>

#include <botan/build.h>

#ifdef BOTAN_HAS_JITTER_RNG

#include <botan/auto_rng.h>
#include <botan/entropy_src.h>
#include <botan/jitter_rng.h>
#include <botan/system_rng.h>

#include "tests.h"

namespace Botan_Tests {

std::vector<Test::Result> test_jitter_rng() {
return {
CHECK("Jitter_RNG basic usage",
[](Test::Result&) {
const std::vector<size_t> sample_counts{0, 1, 2, 4, 64, 128, 512};

Botan::Jitter_RNG rng;
for(auto sample_count : sample_counts) {
[[maybe_unused]] auto buf = rng.random_vec(sample_count);
}
}),

CHECK("Jitter_RNG clear",
[](Test::Result&) {
const std::vector<size_t> sample_counts{64, 128};

Botan::Jitter_RNG rng;
for(auto sample_count : sample_counts) {
[[maybe_unused]] auto buf = rng.random_vec(sample_count);
rng.clear();
}
}),

CHECK("JitterRNG as entropy source",
[](Test::Result&) {
Botan::Entropy_Sources entropy_sources;
entropy_sources.add_source(Botan::Entropy_Source::create("jitter_rng"));
Botan::AutoSeeded_RNG rng{entropy_sources};

[[maybe_unused]] auto buf = rng.random_vec(512);
}),
};
}

BOTAN_REGISTER_TEST_FN("rng", "jitter_rng", test_jitter_rng);

} // namespace Botan_Tests

#endif

0 comments on commit 57ccd78

Please sign in to comment.