-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
238 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |