1#include <crypto/chacha20.h>
2#include <crypto/entropy_pool.h>
3#include <stdbool.h>
4#include <stdint.h>
5#include <string.h>
6
7#include <sync/spinlock.h>
8
9static uint8_t chacha_key[32] = {0};
10static uint8_t chacha_nonce[12] = {0};
11static uint32_t chacha_counter = 0;
12static uint64_t chacha_reseed_counter = 0;
13
14void entropy_pool_init(struct entropy_pool *pool) {
15 memset(pool->buffer, 0, ENTROPY_POOL_SIZE);
16 pool->write_pos = 0;
17 pool->entropy_bits = 0;
18 memset(&pool->lock, 0, sizeof(struct spinlock));
19}
20
21void entropy_pool_add(struct entropy_pool *pool, const uint8_t *data,
22 uint64_t len, uint64_t entropy_bits) {
23 enum irql irql = spin_lock(lock: &pool->lock);
24
25 for (uint64_t i = 0; i < len; i++) {
26 pool->buffer[pool->write_pos] ^= data[i];
27 pool->write_pos = (pool->write_pos + 1) % ENTROPY_POOL_SIZE;
28 }
29
30 pool->entropy_bits += entropy_bits;
31 if (pool->entropy_bits > ENTROPY_MAX_BITS)
32 pool->entropy_bits = ENTROPY_MAX_BITS;
33
34 spin_unlock(lock: &pool->lock, old: irql);
35}
36
37uint64_t entropy_pool_extract(struct entropy_pool *pool, uint8_t *out,
38 uint64_t len) {
39 uint8_t seed[32];
40
41 enum irql irql = spin_lock(lock: &pool->lock);
42
43 // Refuse to output if there's insufficient entropy
44 if (pool->entropy_bits < len * 8) {
45 spin_unlock(lock: &pool->lock, old: irql);
46 return 0;
47 }
48
49 memcpy(seed, pool->buffer, 32);
50
51 // Reduce entropy count
52 if (pool->entropy_bits >= len * 8)
53 pool->entropy_bits -= len * 8;
54 else
55 pool->entropy_bits = 0;
56
57 spin_unlock(lock: &pool->lock, old: irql);
58
59 chacha20_encrypt(key: seed, nonce: chacha_nonce, counter: 0, in: (uint8_t[64]){0}, out, len);
60 return len;
61}
62
63uint64_t entropy_pool_bits(struct entropy_pool *pool) {
64 enum irql irql = spin_lock(lock: &pool->lock);
65 uint64_t bits = pool->entropy_bits;
66 spin_unlock(lock: &pool->lock, old: irql);
67 return bits;
68}
69
70void entropy_pool_decrease(struct entropy_pool *pool, uint64_t bits) {
71 enum irql irql = spin_lock(lock: &pool->lock);
72 if (pool->entropy_bits > bits)
73 pool->entropy_bits -= bits;
74 else
75 pool->entropy_bits = 0;
76 spin_unlock(lock: &pool->lock, old: irql);
77}
78
79void entropy_pool_seed(struct entropy_pool *pool) {
80 uint64_t bits = entropy_pool_bits(pool);
81 if (bits < 256) {
82 // Wait for more entropy
83 return;
84 }
85
86 entropy_pool_extract(pool, out: chacha_key, len: 32);
87 memset(chacha_nonce, 0, sizeof(chacha_nonce));
88 chacha_counter = 0;
89}
90
91void entropy_pool_reseed(struct entropy_pool *pool) {
92 if (entropy_pool_bits(pool) >= 128 && chacha_reseed_counter++ > 10000) {
93 uint8_t seed[32];
94 entropy_pool_extract(pool, out: seed, len: 32);
95 for (int i = 0; i < 32; i++) {
96 chacha_key[i] ^= seed[i];
97 }
98 chacha_reseed_counter = 0;
99 }
100}
101