]> git.proxmox.com Git - wasi-libc.git/blame - libc-top-half/sources/arc4random.c
Fix miscellaneous lint warnings.
[wasi-libc.git] / libc-top-half / sources / arc4random.c
CommitLineData
320054e8
DG
1#include <assert.h>
2#include <stdint.h>
3#include <string.h>
4#include <unistd.h>
7ba6adfc 5#include <stdlib.h>
320054e8
DG
6
7#define RNG_RESERVE_LEN 512
8
9#define CHACHA20_KEYBYTES 32
10#define CHACHA20_BLOCKBYTES 64
11
12#define ROTL32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
13
14#define CHACHA20_QUARTERROUND(A, B, C, D) \
15 A += B; \
16 D = ROTL32(D ^ A, 16); \
17 C += D; \
18 B = ROTL32(B ^ C, 12); \
19 A += B; \
20 D = ROTL32(D ^ A, 8); \
21 C += D; \
22 B = ROTL32(B ^ C, 7)
23
24static void CHACHA20_ROUNDS(uint32_t st[16])
25{
26 int i;
27
28 for (i = 0; i < 20; i += 2) {
29 CHACHA20_QUARTERROUND(st[0], st[4], st[8], st[12]);
30 CHACHA20_QUARTERROUND(st[1], st[5], st[9], st[13]);
31 CHACHA20_QUARTERROUND(st[2], st[6], st[10], st[14]);
32 CHACHA20_QUARTERROUND(st[3], st[7], st[11], st[15]);
33 CHACHA20_QUARTERROUND(st[0], st[5], st[10], st[15]);
34 CHACHA20_QUARTERROUND(st[1], st[6], st[11], st[12]);
35 CHACHA20_QUARTERROUND(st[2], st[7], st[8], st[13]);
36 CHACHA20_QUARTERROUND(st[3], st[4], st[9], st[14]);
37 }
38}
39
40static void chacha20_update(uint8_t out[CHACHA20_BLOCKBYTES], uint32_t st[16])
41{
42 uint32_t ks[16];
43 int i;
44
45 memcpy(ks, st, 4 * 16);
46 CHACHA20_ROUNDS(st);
47 for (i = 0; i < 16; i++) {
48 ks[i] += st[i];
49 }
50 memcpy(out, ks, CHACHA20_BLOCKBYTES);
51 st[12]++;
52}
53
54static void chacha20_init(uint32_t st[16], const uint8_t key[CHACHA20_KEYBYTES])
55{
56 static const uint32_t constants[4] = { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
57 memcpy(&st[0], constants, 4 * 4);
58 memcpy(&st[4], key, CHACHA20_KEYBYTES);
59 memset(&st[12], 0, 4 * 4);
60}
61
62static int chacha20_rng(uint8_t* out, size_t len, uint8_t key[CHACHA20_KEYBYTES])
63{
64 uint32_t st[16];
65 size_t off;
66
67 chacha20_init(st, key);
68 chacha20_update(&out[0], st);
69 memcpy(key, out, CHACHA20_KEYBYTES);
70 off = 0;
71 while (len >= CHACHA20_BLOCKBYTES) {
72 chacha20_update(&out[off], st);
73 len -= CHACHA20_BLOCKBYTES;
74 off += CHACHA20_BLOCKBYTES;
75 }
76 if (len > 0) {
77 uint8_t tmp[CHACHA20_BLOCKBYTES];
78 chacha20_update(tmp, st);
79 memcpy(&out[off], tmp, len);
80 }
81 return 0;
82}
83
84struct rng_state {
85 int initialized;
86 size_t off;
87 uint8_t key[CHACHA20_KEYBYTES];
88 uint8_t reserve[RNG_RESERVE_LEN];
89};
90
91void arc4random_buf(void* buffer, size_t len)
92{
93 static _Thread_local struct rng_state rng_state;
94
95 unsigned char* buffer_ = (unsigned char*)buffer;
96 size_t off;
97 size_t remaining;
98 size_t partial;
320054e8
DG
99
100 if (!rng_state.initialized) {
101 if (getentropy(rng_state.key, sizeof rng_state.key) != 0) {
102 assert(0);
103 }
104 rng_state.off = RNG_RESERVE_LEN;
105 rng_state.initialized = 1;
106 }
107 off = 0;
108 remaining = len;
109 while (remaining > 0) {
110 if (rng_state.off == RNG_RESERVE_LEN) {
111 while (remaining >= RNG_RESERVE_LEN) {
112 chacha20_rng(&buffer_[off], RNG_RESERVE_LEN, rng_state.key);
113 off += RNG_RESERVE_LEN;
114 remaining -= RNG_RESERVE_LEN;
115 }
116 if (remaining == 0) {
117 break;
118 }
119 chacha20_rng(&rng_state.reserve[0], RNG_RESERVE_LEN, rng_state.key);
120 rng_state.off = 0;
121 }
122 partial = RNG_RESERVE_LEN - rng_state.off;
123 if (remaining < partial) {
124 partial = remaining;
125 }
126 memcpy(&buffer_[off], &rng_state.reserve[rng_state.off], partial);
127 memset(&rng_state.reserve[rng_state.off], 0, partial);
128 rng_state.off += partial;
129 remaining -= partial;
130 off += partial;
131 }
132}
133
134uint32_t arc4random(void)
135{
136 uint32_t v;
137
138 arc4random_buf(&v, sizeof v);
139
140 return v;
141}
142
143uint32_t arc4random_uniform(const uint32_t upper_bound)
144{
145 uint32_t min;
146 uint32_t r;
147
148 if (upper_bound < 2U) {
149 return 0;
150 }
151 min = (1U + ~upper_bound) % upper_bound; /* = 2**32 mod upper_bound */
152 do {
153 r = arc4random();
154 } while (r < min);
155 /* r is now clamped to a set whose size mod upper_bound == 0
156 * the worst case (2**31+1) requires 2 attempts on average */
157
158 return r % upper_bound;
159}