1#include <crypto/prng.h>
2#include <smp/core.h>
3#include <smp/percpu.h>
4#include <stdint.h>
5#include <string.h>
6
7struct prng_core {
8 uint32_t state[16];
9 uint8_t buffer[64]; // keystream buffer
10 size_t pos;
11};
12
13PERCPU_DEFINE(struct prng_core, pcs);
14#define prng_core_state PERCPU_READ(TOPC_IRQL, pcs)
15
16static void prng_seed_core(uint64_t seed) {
17 const char *sigma = "expand 32-byte k";
18 uint8_t key[32];
19
20 uint64_t tsc = smp_core(cond: TOPC_IRQL)->last_tsc;
21 uint32_t core_id = smp_id(cond: TOPC_IRQL);
22
23 for (int i = 0; i < 4; i++) {
24 key[i * 8 + 0] = (seed >> (i * 8 + 0)) & 0xff;
25 key[i * 8 + 1] = (seed >> (i * 8 + 8)) & 0xff;
26
27 key[i * 8 + 2] = (tsc >> (i * 8 + 32)) & 0xff;
28 key[i * 8 + 3] = (tsc >> (i * 8 + 32)) & 0xff;
29
30 key[i * 8 + 4] = (core_id >> (i * 8 + 0)) & 0xff;
31 key[i * 8 + 5] = (core_id >> (i * 8 + 4)) & 0xff;
32
33 key[i * 8 + 6] = (tsc >> (i * 8 + 16)) & 0xff;
34 key[i * 8 + 7] = (tsc >> (i * 8 + 24)) & 0xff;
35 }
36
37 for (int i = 0; i < 4; i++)
38 prng_core_state.state[i] = ((uint32_t *) sigma)[i];
39 for (int i = 0; i < 8; i++)
40 prng_core_state.state[4 + i] = ((uint32_t *) key)[i];
41
42 prng_core_state.state[12] = 0;
43 prng_core_state.state[13] = 0;
44 prng_core_state.state[14] = 0;
45 prng_core_state.state[15] = 0;
46
47 prng_core_state.pos = 64;
48}
49
50#define QR(a, b, c, d) \
51 a += b; \
52 d ^= a; \
53 d = (d << 16) | (d >> 16); \
54 c += d; \
55 b ^= c; \
56 b = (b << 12) | (b >> 20); \
57 a += b; \
58 d ^= a; \
59 d = (d << 8) | (d >> 24); \
60 c += d; \
61 b ^= c; \
62 b = (b << 7) | (b >> 25)
63
64static void chacha20_generate_block(uint8_t out[64], uint32_t state[16]) {
65 uint32_t x[16];
66 memcpy(x, state, sizeof(x));
67
68 for (int i = 0; i < 10; i++) {
69 QR(x[0], x[4], x[8], x[12]);
70 QR(x[1], x[5], x[9], x[13]);
71 QR(x[2], x[6], x[10], x[14]);
72 QR(x[3], x[7], x[11], x[15]);
73 QR(x[0], x[5], x[10], x[15]);
74 QR(x[1], x[6], x[11], x[12]);
75 QR(x[2], x[7], x[8], x[13]);
76 QR(x[3], x[4], x[9], x[14]);
77 }
78
79 for (int i = 0; i < 16; i++) {
80 x[i] += state[i];
81 out[i * 4 + 0] = x[i] & 0xff;
82 out[i * 4 + 1] = (x[i] >> 8) & 0xff;
83 out[i * 4 + 2] = (x[i] >> 16) & 0xff;
84 out[i * 4 + 3] = (x[i] >> 24) & 0xff;
85 }
86}
87
88uint64_t prng_next(void) {
89 enum irql irql = irql_raise(new_level: IRQL_HIGH_LEVEL);
90 if (prng_core_state.pos >= 64) {
91 chacha20_generate_block(prng_core_state.buffer, prng_core_state.state);
92 prng_core_state.state[12]++;
93 prng_core_state.pos = 0;
94 }
95
96 uint64_t val;
97 memcpy(&val, prng_core_state.buffer + prng_core_state.pos,
98 sizeof(uint64_t));
99 prng_core_state.pos += 8;
100 irql_lower(old_level: irql);
101 return val;
102}
103
104void prng_seed(uint64_t seed) {
105 enum irql irql = irql_raise(new_level: IRQL_HIGH_LEVEL);
106 if (seed == 0)
107 seed = smp_core(cond: TOPC_IRQL)->last_tsc;
108 prng_seed_core(seed);
109 irql_lower(old_level: irql);
110}
111