Serenity Operating System
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}