Skip to content

Commit

Permalink
Add RandomPool
Browse files Browse the repository at this point in the history
  • Loading branch information
john0312 committed Jul 25, 2024
1 parent f229c0d commit 647f098
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 1 deletion.
95 changes: 95 additions & 0 deletions fw/Core/Hitcon/Logic/RandomPool.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include <Logic/RandomPool.h>
#include <Service/Sched/PeriodicTask.h>
#include <Service/Sched/Scheduler.h>

using hitcon::service::sched::task_callback_t;

namespace hitcon {

SecureRandomPool::SecureRandomPool()
: init_finished(false), seed_count(0),
routine_task(950, (task_callback_t)&SecureRandomPool::Routine, this, 20),
seed_queue_head(0), seed_queue_tail(0), random_queue_head(0),
random_queue_tail(0) {}

void SecureRandomPool::Init() {
sha3_Init256(&keccak_context);
hitcon::service::sched::scheduler.Queue(&routine_task, nullptr);
hitcon::service::sched::scheduler.EnablePeriodic(&routine_task);
init_finished = true;
}

bool SecureRandomPool::Seed(uint64_t seed_val) {
if (seed_queue_head == (seed_queue_tail + 1) % kSeedQueueSize) {
// Queue is full
return false;
}
seed_queue[seed_queue_tail] = seed_val;
seed_queue_tail = (seed_queue_tail + 1) % kSeedQueueSize;
return true;
}

bool SecureRandomPool::GetRandom(uint64_t* res) {
if (!init_finished || seed_count < kMinSeedCountBeforeReady ||
random_queue_head == random_queue_tail) {
// Not ready or queue is empty
return false;
}
*res = random_queue[random_queue_head];
random_queue_head = (random_queue_head + 1) % kRandomQueueSize;
return true;
}

void SecureRandomPool::Routine(void* unused) {
if (TrySeed()) {
return;
}
if (TryRandom()) {
return;
}
}

bool SecureRandomPool::TrySeed() {
if (seed_queue_head == seed_queue_tail) {
// Queue is empty
return false;
}
uint64_t seed_val = seed_queue[seed_queue_head];
seed_queue_head = (seed_queue_head + 1) % kSeedQueueSize;
for (size_t i = 0; i < sizeof(uint64_t); i++) {
keccak_context.u.sb[i] ^= static_cast<uint8_t>(seed_val >> (8 * i));
}
keccakf(keccak_context.u.s);
seed_count++;
return true;
}

bool SecureRandomPool::TryRandom() {
if (seed_count < kMinSeedCountBeforeReady ||
random_queue_head == (random_queue_tail + 1) % kRandomQueueSize) {
// Not ready or queue is full
return false;
}
uint64_t random_val = 0;
for (size_t i = 0; i < sizeof(uint64_t); i++) {
random_val |= static_cast<uint64_t>(keccak_context.u.sb[i]) << (8 * i);
}
keccakf(keccak_context.u.s);
random_queue[random_queue_tail] = random_val;
random_queue_tail = (random_queue_tail + 1) % kRandomQueueSize;
return true;
}

FastRandomPool::FastRandomPool() : prng(0) {}

void FastRandomPool::Init() {
// Left blank as it was not specified what should be done here,
// it might be used to set the seed to a certain value or initialize other
// resources.
}

uint32_t FastRandomPool::GetRandom() { return prng.GetRandom(); }

void FastRandomPool::Seed(uint64_t seed) { prng.MixState(seed); }

} // namespace hitcon
107 changes: 107 additions & 0 deletions fw/Core/Hitcon/Logic/RandomPool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#ifndef LOGIC_RANDOM_POOL_DOT_H_
#define LOGIC_RANDOM_POOL_DOT_H_

#include <Logic/keccak.h>
#include <Logic/pcg32.h>
#include <Service/Sched/Scheduler.h>
#include <stddef.h>
#include <stdint.h>

namespace hitcon {

class SecureRandomPool;
class FastRandomPool;

// SecureRandomPool maintains a pool of entropy in a keccak state, and everytime
// randomness is requested it'll run the keccakf() and output the bytes. This
// should be used for anything that needs cryptography randomness and doesn't
// mind being slow.
class SecureRandomPool {
public:
SecureRandomPool();

void Init();

// Add entropy to the secure random pool.
// Returns true if successful, otherwise the pool is busy (or not ready yet)
// and the caller should retry later on. Generally this adds the seed to
// seeding queue and Routine() will take care of the rest.
bool Seed(uint64_t seed_val);

// Pull entropy from the secure random pool.
// Return true if successful and the randomness will be in res. Otherwise the
// pool is busy (or not ready yet) and the caller should retry later on.
// Generally this involves pulling from the random queue and Routine() is in
// charge of filling the queue.
bool GetRandom(uint64_t* res);

// Will be called routinely by the scheduler, and will try to empty the seed
// queue first then fill the random queue. Each invocation is limited to 1
// keccakf() run due to scheduling.
void Routine(void* unused);

private:
// Try to seed once into the state if there's seed in the queue.
// Seeding involves xor'ing the corresponding bytes in the state then run
// keccakf(). Return true if one such operation has been done and that signals
// to the caller that we can't run another keccakf() in the same Routine()
// run.
bool TrySeed();

// Try to pull one random out if we've enough entropy considering seed_count
// and there's room in the random queue. This involves getting the
// corresponding bytes in the state. After getting the bytes, keccakf() is
// invoked. Return true if one such operation has been done and that signals
// to the caller that we can't run another keccakf() in the same Routine()
// run.
bool TryRandom();

// Set to true after Init.
bool init_finished;

// The internal keccak state.
sha3_context keccak_context;

// Number of times we've been seeded.
int seed_count;

// Scheduler task for running Routine(), runs every 20ms with low priority
// (950).
hitcon::service::sched::PeriodicTask routine_task;

// A circular queue for holding the to be seeded values.
static constexpr size_t kSeedQueueSize = 8;
uint64_t seed_queue[kSeedQueueSize];
size_t seed_queue_head;
size_t seed_queue_tail;

// A circular queue for holding the newly minted random values.
static constexpr size_t kRandomQueueSize = 8;
uint64_t random_queue[kRandomQueueSize];
size_t random_queue_head;
size_t random_queue_tail;

// Must be seeded this amount of times before GetRandom() will be ready.
static constexpr int kMinSeedCountBeforeReady = 64;
};

// Fast random pool uses the PCG32 for faster but non-secure random generation.
class FastRandomPool {
public:
FastRandomPool();

void Init();

// Never blocks, simply retrieves the random.
uint32_t GetRandom();

// Add to the PRNG state, aka MixState().
void Seed(uint64_t seed);

private:
PCG32 prng;
};

} // namespace hitcon

#endif // LOGIC_RANDOM_POOL_DOT_H_
2 changes: 1 addition & 1 deletion fw/Core/Hitcon/Logic/keccak.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ static const unsigned keccakf_piln[24] = {10, 7, 11, 17, 18, 3, 5, 16,
/* generally called after SHA3_KECCAK_SPONGE_WORDS-ctx->capacityWords words
* are XORed into the state s
*/
static void keccakf(uint64_t s[25]) {
void keccakf(uint64_t s[25]) {
int i, j, round;
uint64_t t, bc[5];

Expand Down
3 changes: 3 additions & 0 deletions fw/Core/Hitcon/Logic/keccak.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Slightly modified for this project.
// See license below.

#include <stddef.h>
#include <stdint.h>

/* -------------------------------------------------------------------------
Expand Down Expand Up @@ -57,6 +58,8 @@ typedef enum SHA3_RETURN sha3_return_t;
/* For Init or Reset call these: */
sha3_return_t sha3_Init(void *priv, unsigned bitSize);

void keccakf(uint64_t s[25]);

void sha3_Init256(void *priv);
void sha3_Init384(void *priv);
void sha3_Init512(void *priv);
Expand Down
32 changes: 32 additions & 0 deletions fw/Core/Hitcon/Logic/pcg32.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef LOGIC_PCG32_DOT_H_
#define LOGIC_PCG32_DOT_H_

#include <stdint.h>

class PCG32 {
private:
uint64_t state;
static constexpr uint64_t multiplier = 6364136223846793005u;
static constexpr uint64_t increment = 1442695040888963407u;

uint32_t rotr32(uint32_t x, unsigned r) { return x >> r | x << (-r & 31); }

public:
PCG32(uint64_t seed) : state(seed + increment) { GetRandom(); }

void MixState(uint64_t input) {
state = state * multiplier + input;
GetRandom();
}

uint32_t GetRandom() {
uint64_t x = state;
unsigned count = (unsigned)(x >> 59); // 59 = 64 - 5

state = x * multiplier + increment;
x ^= x >> 18; // 18 = (64 - 27)/2
return rotr32((uint32_t)(x >> 27), count); // 27 = 32 - 5
}
};

#endif // LOGIC_PCG32_DOT_H_

0 comments on commit 647f098

Please sign in to comment.