diff --git a/Kernel/Random.h b/Kernel/Random.h index 1e112ca5d5..513fc7cfa7 100644 --- a/Kernel/Random.h +++ b/Kernel/Random.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2020, Peter Elliott +#include #include +#include +#include namespace Kernel { +template +class FortunaPRNG { + constexpr static size_t pool_count = 32; + constexpr static size_t reseed_threshold = 16; + + using CipherType = CipherT; + using BlockType = CipherT::BlockType; + using HashType = HashT; + using DigestType = HashT::DigestType; + +public: + void get_random_bytes(u8* buffer, size_t n) + { + if (m_p0_len >= reseed_threshold) { + this->reseed(); + } + + ASSERT(m_counter != 0); + + // FIXME: More than 2^20 bytes cannot be generated without refreshing the key. + ASSERT(n < (1 << 20)); + + CipherType cipher(m_key, m_key.size()); + + size_t block_size = CipherType::BlockSizeInBits / 8; + + for (size_t i = 0; i < n; i += block_size) { + this->generate_block(cipher, &buffer[i], min(block_size, n - i)); + } + + // Extract a new key from the prng stream. + + for (size_t i = 0; i < KeySize; i += block_size) { + this->generate_block(cipher, &(m_key[i]), min(block_size, KeySize - i)); + } + } + + template + void add_random_event(const T& event_data, size_t pool) + { + pool %= pool_count; + if (pool == 0) { + m_p0_len++; + } + m_pools[pool].update(reinterpret_cast(&event_data), sizeof(T)); + } + +private: + void generate_block(CipherType cipher, u8* buffer, size_t size) + { + BlockType input((u8*)m_counter, sizeof(m_counter)); + BlockType output; + cipher.encrypt_block(input, output); + m_counter++; + + memcpy(buffer, output.get().data(), size); + } + + void reseed() + { + HashType new_key; + new_key.update(m_key); + for (size_t i = 0; i < pool_count; ++i) { + if (m_reseed_number % (1 << i) == 0) { + DigestType digest = m_pools[i].digest(); + new_key.update(digest.immutable_data(), digest.data_length()); + } + } + DigestType digest = new_key.digest(); + m_key = ByteBuffer::copy(digest.immutable_data(), + digest.data_length()); + + m_counter++; + m_reseed_number++; + m_p0_len = 0; + } + + size_t m_counter { 0 }; + size_t m_reseed_number { 0 }; + size_t m_p0_len { 0 }; + ByteBuffer m_key; + HashType m_pools[pool_count]; +}; + // NOTE: These API's are primarily about expressing intent/needs in the calling code. // We don't make any guarantees about actual fastness or goodness yet.