Serenity Operating System
at master 203 lines 5.4 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#pragma once 9 10#include <AK/Assertions.h> 11#include <AK/ByteBuffer.h> 12#include <AK/Types.h> 13#include <Kernel/Arch/Processor.h> 14#include <Kernel/Locking/Mutex.h> 15#include <Kernel/StdLib.h> 16#include <LibCrypto/Cipher/AES.h> 17#include <LibCrypto/Cipher/Cipher.h> 18#include <LibCrypto/Hash/SHA2.h> 19 20namespace Kernel { 21 22template<typename CipherT, typename HashT, int KeySize> 23class FortunaPRNG { 24public: 25 constexpr static size_t pool_count = 32; 26 constexpr static size_t reseed_threshold = 16; 27 28 using CipherType = CipherT; 29 using BlockType = typename CipherT::BlockType; 30 using HashType = HashT; 31 using DigestType = typename HashT::DigestType; 32 33 // FIXME: Do something other than VERIFY()'ing in case of OOM. 34 FortunaPRNG() 35 : m_counter(ByteBuffer::create_zeroed(BlockType::block_size()).release_value_but_fixme_should_propagate_errors()) 36 { 37 } 38 39 bool get_random_bytes(Bytes buffer) 40 { 41 SpinlockLocker lock(m_lock); 42 if (!is_ready()) 43 return false; 44 if (m_p0_len >= reseed_threshold) { 45 this->reseed(); 46 } 47 48 VERIFY(is_seeded()); 49 50 // FIXME: More than 2^20 bytes cannot be generated without refreshing the key. 51 VERIFY(buffer.size() < (1 << 20)); 52 53 typename CipherType::CTRMode cipher(m_key, KeySize, Crypto::Cipher::Intent::Encryption); 54 55 auto counter_span = m_counter.bytes(); 56 cipher.key_stream(buffer, counter_span, &counter_span); 57 58 // Extract a new key from the prng stream. 59 Bytes key_span = m_key.bytes(); 60 cipher.key_stream(key_span, counter_span, &counter_span); 61 return true; 62 } 63 64 template<typename T> 65 void add_random_event(T const& event_data, size_t pool) 66 { 67 pool %= pool_count; 68 if (pool == 0) { 69 m_p0_len++; 70 } 71 m_pools[pool].update(reinterpret_cast<u8 const*>(&event_data), sizeof(T)); 72 } 73 74 [[nodiscard]] bool is_seeded() const 75 { 76 return m_reseed_number > 0; 77 } 78 79 [[nodiscard]] bool is_ready() const 80 { 81 VERIFY(m_lock.is_locked()); 82 return is_seeded() || m_p0_len >= reseed_threshold; 83 } 84 85 Spinlock<LockRank::None>& get_lock() { return m_lock; } 86 87private: 88 void reseed() 89 { 90 HashType new_key; 91 new_key.update(m_key); 92 for (size_t i = 0; i < pool_count; ++i) { 93 if (m_reseed_number % (1u << i) == 0) { 94 DigestType digest = m_pools[i].digest(); 95 new_key.update(digest.immutable_data(), digest.data_length()); 96 } 97 } 98 DigestType digest = new_key.digest(); 99 if (m_key.size() == digest.data_length()) { 100 // Avoid reallocating, just overwrite the key. 101 m_key.overwrite(0, digest.immutable_data(), digest.data_length()); 102 } else { 103 auto buffer_result = ByteBuffer::copy(digest.immutable_data(), digest.data_length()); 104 // If there's no memory left to copy this into, bail out. 105 if (buffer_result.is_error()) 106 return; 107 108 m_key = buffer_result.release_value(); 109 } 110 111 m_reseed_number++; 112 m_p0_len = 0; 113 } 114 115 ByteBuffer m_counter; 116 size_t m_reseed_number { 0 }; 117 size_t m_p0_len { 0 }; 118 ByteBuffer m_key; 119 HashType m_pools[pool_count]; 120 Spinlock<LockRank::None> m_lock {}; 121}; 122 123class KernelRng : public FortunaPRNG<Crypto::Cipher::AESCipher, Crypto::Hash::SHA256, 256> { 124 125public: 126 KernelRng(); 127 static KernelRng& the(); 128 129 void wait_for_entropy(); 130 131 void wake_if_ready(); 132 133private: 134 WaitQueue m_seed_queue; 135}; 136 137class EntropySource { 138 template<typename T> 139 struct Event { 140 u64 timestamp; 141 size_t source; 142 T event_data; 143 }; 144 145public: 146 enum class Static : size_t { 147 Interrupts, 148 MaxHardcodedSourceIndex, 149 }; 150 151 EntropySource() 152 : m_source(next_source++) 153 { 154 } 155 156 EntropySource(Static hardcoded_source) 157 : m_source(static_cast<size_t>(hardcoded_source)) 158 { 159 } 160 161 template<typename T> 162 void add_random_event(T const& event_data) 163 { 164 auto& kernel_rng = KernelRng::the(); 165 SpinlockLocker lock(kernel_rng.get_lock()); 166 // We don't lock this because on the off chance a pool is corrupted, entropy isn't lost. 167 Event<T> event = { Processor::read_cpu_counter(), m_source, event_data }; 168 kernel_rng.add_random_event(event, m_pool); 169 m_pool++; 170 kernel_rng.wake_if_ready(); 171 } 172 173private: 174 static size_t next_source; 175 size_t m_pool { 0 }; 176 size_t m_source; 177}; 178 179// NOTE: These API's are primarily about expressing intent/needs in the calling code. 180// The only difference is that get_fast_random is guaranteed not to block. 181 182void get_fast_random_bytes(Bytes); 183bool get_good_random_bytes(Bytes bytes, bool allow_wait = true, bool fallback_to_fast = true); 184 185template<typename T> 186inline T get_fast_random() 187{ 188 T value; 189 Bytes bytes { reinterpret_cast<u8*>(&value), sizeof(T) }; 190 get_fast_random_bytes(bytes); 191 return value; 192} 193 194template<typename T> 195inline T get_good_random() 196{ 197 T value; 198 Bytes bytes { reinterpret_cast<u8*>(&value), sizeof(T) }; 199 get_good_random_bytes(bytes); 200 return value; 201} 202 203}