Line data Source code
1 : #include "passgen/util/random.h"
2 : #include "passgen/assert.h"
3 : #include <stdlib.h>
4 : #include <string.h>
5 :
6 25 : static bool _strprefix(const char *prefix, const char *string) {
7 25 : size_t prefix_len = strlen(prefix);
8 104 : for(size_t i = 0; i < prefix_len; i++) {
9 93 : if(prefix[i] != string[i]) {
10 14 : return false;
11 : }
12 : }
13 11 : return true;
14 : }
15 :
16 : #ifdef __linux__
17 : #define PASSGEN_RANDOM_HAVE_SYSTEM
18 : #include <sys/random.h>
19 :
20 57096 : size_t passgen_random_read_system(void *dest, size_t size, void *data) {
21 : (void) data;
22 57096 : return getrandom(dest, size, 0);
23 : }
24 : #endif
25 :
26 : #ifdef __APPLE__
27 : #define PASSGEN_RANDOM_HAVE_SYSTEM
28 :
29 : size_t passgen_random_read_system(void *dest, size_t size, void *data) {
30 : (void) data;
31 : arc4random_buf(dest, size);
32 : return size;
33 : }
34 : #endif
35 :
36 : #ifndef PASSGEN_RANDOM_HAVE_SYSTEM
37 : static const char *passgen_random_default_device = "/dev/urandom";
38 : #endif
39 :
40 5 : size_t passgen_random_read_file(void *dest, size_t size, void *data) {
41 5 : return fread(dest, 1, size, data);
42 : }
43 :
44 5 : void passgen_random_close_file(void *data) {
45 5 : fclose(data);
46 5 : }
47 :
48 17 : void passgen_random_close_system(void *data) {
49 : (void) data;
50 17 : }
51 :
52 39425 : static uint64_t xorshift64(uint64_t *state) {
53 39425 : uint64_t x = *state;
54 39425 : x ^= x << 13;
55 39425 : x ^= x >> 7;
56 39425 : x ^= x << 17;
57 39425 : return *state = x;
58 : }
59 :
60 308 : size_t passgen_random_read_xorshift(void *dest, size_t size, void *data) {
61 308 : size_t written = 0;
62 : uint64_t result;
63 :
64 : // fill all whole uint64 blocks
65 39732 : while((size - written) >= sizeof(result)) {
66 39424 : result = xorshift64(data);
67 39424 : memcpy(dest + written, &result, sizeof(result));
68 39424 : written += sizeof(result);
69 : }
70 :
71 : // maybe fill the last incomplete block
72 308 : if(size != written) {
73 1 : result = xorshift64(data);
74 1 : memcpy(dest + written, &result, size - written);
75 1 : written += size - written;
76 : }
77 :
78 308 : return written;
79 : }
80 :
81 17 : void passgen_random_close_xorshift(void *data) {
82 17 : free(data);
83 17 : }
84 :
85 1 : passgen_random *passgen_random_new_xorshift(uint64_t seed) {
86 1 : passgen_random *random = malloc(sizeof(passgen_random));
87 1 : if(!random) return NULL;
88 :
89 1 : return passgen_random_open_xorshift(random, seed);
90 : }
91 :
92 57411 : void passgen_random_reload(passgen_random *random) {
93 57411 : passgen_assert(random != NULL);
94 :
95 : // read random data.
96 : size_t bytes =
97 57411 : random->read(&random->buffer, sizeof(random->buffer), random->data);
98 : (void) bytes;
99 :
100 : // make sure we've read enough.
101 57411 : passgen_assert(bytes == sizeof(random->buffer));
102 :
103 : // reset position in ring buffer.
104 57411 : random->pos = 0;
105 57411 : }
106 :
107 : passgen_random *
108 17 : passgen_random_open_xorshift(passgen_random *random, uint64_t seed) {
109 : // create state
110 17 : uint64_t *state = malloc(sizeof(uint64_t));
111 17 : if(!state) return NULL;
112 :
113 : // initialise state
114 17 : *state = seed;
115 :
116 17 : random->data = state;
117 17 : random->read = passgen_random_read_xorshift;
118 17 : random->close = passgen_random_close_xorshift;
119 17 : passgen_random_reload(random);
120 :
121 17 : return random;
122 : }
123 :
124 3 : size_t passgen_random_read_zero(void *dest, size_t size, void *data) {
125 : (void) data;
126 3 : memset(dest, 0, size);
127 3 : return size;
128 : }
129 :
130 3 : void passgen_random_close_zero(void *data) {
131 : (void) data;
132 3 : }
133 :
134 3 : passgen_random *passgen_random_open_zero(passgen_random *random) {
135 3 : random->data = NULL;
136 3 : random->read = passgen_random_read_zero;
137 3 : random->close = passgen_random_close_zero;
138 3 : passgen_random_reload(random);
139 3 : return random;
140 : }
141 :
142 1 : passgen_random *passgen_random_new_zero() {
143 1 : passgen_random *random = malloc(sizeof(passgen_random));
144 1 : if(!random) return NULL;
145 1 : return passgen_random_open_zero(random);
146 : }
147 :
148 : #ifdef PASSGEN_RANDOM_HAVE_SYSTEM
149 53 : passgen_random *passgen_random_open_system(passgen_random *random) {
150 53 : random->data = NULL;
151 53 : random->read = passgen_random_read_system;
152 53 : random->close = passgen_random_close_system;
153 53 : passgen_random_reload(random);
154 53 : return random;
155 : }
156 : #else
157 : passgen_random *passgen_random_open_system(passgen_random *random) {
158 : (void) random;
159 :
160 : return NULL;
161 : }
162 : #endif
163 :
164 11645391 : void passgen_random_read(passgen_random *random, void *data, size_t bytes) {
165 11645391 : if(bytes <= sizeof(random->buffer)) {
166 : // maximum bytes we can have right now
167 11645390 : size_t left = sizeof(random->buffer) - random->pos;
168 :
169 11645390 : if(bytes < left) {
170 : // if we have enough, just copy it over.
171 11588057 : memcpy(data, &random->buffer[random->pos], bytes);
172 11588057 : random->pos += bytes;
173 : } else {
174 : // if we don't have enough, copy over whatever we have and
175 : // reload.
176 57333 : memcpy(data, random->buffer, left);
177 57333 : passgen_random_reload(random);
178 :
179 : // if there's more to be read, recurse.
180 57333 : if(bytes != left) {
181 20 : passgen_random_read(random, data + left, bytes - left);
182 : }
183 : }
184 : } else {
185 1 : size_t ret = random->read(data, bytes, random->data);
186 1 : if(ret != bytes) {
187 0 : fprintf(
188 : stderr,
189 : "Error reading from randomness source: trying to read %zu "
190 : "bytes but got %zu",
191 : bytes,
192 : ret);
193 0 : abort();
194 : }
195 : }
196 11645391 : }
197 :
198 13 : passgen_random *passgen_random_new(const char *desc) {
199 13 : passgen_random *random = malloc(sizeof(passgen_random));
200 13 : passgen_assert(random);
201 :
202 13 : passgen_random *ret = passgen_random_open(random, desc);
203 13 : if(!ret) {
204 5 : free(random);
205 : }
206 :
207 13 : return ret;
208 : }
209 :
210 2 : passgen_random *passgen_random_new_path(const char *path) {
211 2 : passgen_random *random = malloc(sizeof(passgen_random));
212 2 : if(!random) return NULL;
213 :
214 2 : if(!passgen_random_open_path(random, path)) {
215 1 : free(random);
216 1 : return NULL;
217 : }
218 :
219 1 : return random;
220 : }
221 :
222 1 : passgen_random *passgen_random_new_file(FILE *file) {
223 1 : passgen_random *random = malloc(sizeof(passgen_random));
224 1 : if(!random) return NULL;
225 :
226 1 : passgen_random_open_file(random, file);
227 :
228 1 : return random;
229 : }
230 :
231 : passgen_random *
232 17 : passgen_random_open_parse(passgen_random *random, const char *desc) {
233 17 : if(strcmp("zero", desc) == 0) {
234 2 : return passgen_random_open_zero(random);
235 : }
236 :
237 : // check if we should read randomness from this file
238 15 : if(_strprefix("file:", desc)) {
239 5 : return passgen_random_open_path(random, &desc[5]);
240 : }
241 :
242 : // check if we should use the xorshift PRNG with the given seed
243 10 : if(_strprefix("xorshift:", desc)) {
244 6 : const char *seed_str = &desc[9];
245 6 : if(*seed_str == 0) {
246 2 : return NULL;
247 : }
248 :
249 4 : uint64_t seed = atoll(seed_str);
250 4 : if(seed == 0) {
251 2 : return NULL;
252 : }
253 :
254 2 : return passgen_random_open_xorshift(random, seed);
255 : }
256 :
257 : // check if we should use the system default
258 4 : if(0 == strcmp(desc, "system")) {
259 2 : return passgen_random_open_system(random);
260 : }
261 :
262 2 : return NULL;
263 : }
264 :
265 68 : passgen_random *passgen_random_open(passgen_random *random, const char *desc) {
266 68 : if(desc) {
267 17 : return passgen_random_open_parse(random, desc);
268 : }
269 :
270 : #ifdef PASSGEN_RANDOM_HAVE_SYSTEM
271 51 : return passgen_random_open_system(random);
272 : #else
273 : passgen_random *rand =
274 : passgen_random_open_path(random, passgen_random_default_device);
275 : if(!rand) {
276 : fprintf(
277 : stderr,
278 : "error: cannot open system randomness device '%s'\n",
279 : passgen_random_default_device);
280 : }
281 : return rand;
282 : #endif
283 : }
284 :
285 : passgen_random *
286 9 : passgen_random_open_path(passgen_random *random, const char *path) {
287 9 : FILE *device = fopen(path, "r");
288 9 : if(!device) {
289 5 : return NULL;
290 : }
291 :
292 4 : return passgen_random_open_file(random, device);
293 :
294 : passgen_random_reload(random);
295 :
296 : return random;
297 : }
298 :
299 5 : passgen_random *passgen_random_open_file(passgen_random *random, FILE *file) {
300 5 : random->data = file;
301 5 : random->read = passgen_random_read_file;
302 5 : random->close = passgen_random_close_file;
303 5 : passgen_random_reload(random);
304 5 : return random;
305 : }
306 :
307 42 : void passgen_random_close(passgen_random *random) {
308 : // close randomness source
309 42 : random->close(random->data);
310 :
311 : // reset members to prevent accidental use-after-free
312 42 : random->data = NULL;
313 42 : random->read = NULL;
314 42 : random->close = NULL;
315 :
316 : // overwrite random buffer with zeroes to remove sensitive data
317 42 : memset(random->buffer, 0, PASSGEN_RANDOM_BUFFER_LENGTH);
318 42 : }
319 :
320 12 : void passgen_random_free(passgen_random *random) {
321 12 : passgen_random_close(random);
322 12 : free(random);
323 12 : }
324 :
325 651623 : inline uint8_t passgen_random_u8(passgen_random *random) {
326 : uint8_t data;
327 651623 : passgen_random_read(random, &data, sizeof(data));
328 651623 : return data;
329 : }
330 :
331 2540288 : inline uint16_t passgen_random_u16(passgen_random *random) {
332 : uint16_t data;
333 2540288 : passgen_random_read(random, &data, sizeof(data));
334 2540288 : return data;
335 : }
336 :
337 3656815 : inline uint32_t passgen_random_u32(passgen_random *random) {
338 : uint32_t data;
339 3656815 : passgen_random_read(random, &data, sizeof(data));
340 3656815 : return data;
341 : }
342 :
343 4794601 : inline uint64_t passgen_random_u64(passgen_random *random) {
344 : uint64_t data;
345 4794601 : passgen_random_read(random, &data, sizeof(data));
346 4794601 : return data;
347 : }
348 :
349 6 : inline bool passgen_random_bool(passgen_random *random) {
350 : uint8_t data;
351 6 : passgen_random_read(random, &data, sizeof(data));
352 6 : return (data & 128) == 0;
353 : }
354 :
355 466230 : inline uint8_t passgen_random_u8_max(passgen_random *random, uint8_t max) {
356 466230 : passgen_assert(max);
357 :
358 466230 : uint8_t mask = max;
359 466230 : mask |= mask >> 4;
360 466230 : mask |= mask >> 2;
361 466230 : mask |= mask >> 1;
362 :
363 : uint8_t num;
364 : do {
365 649822 : num = passgen_random_u8(random) & mask;
366 649822 : } while(num >= max);
367 :
368 466230 : return num;
369 : }
370 :
371 1216512 : inline uint16_t passgen_random_u16_max(passgen_random *random, uint16_t max) {
372 1216512 : passgen_assert(max);
373 :
374 1216512 : uint16_t mask = max;
375 1216512 : mask |= mask >> 8;
376 1216512 : mask |= mask >> 4;
377 1216512 : mask |= mask >> 2;
378 1216512 : mask |= mask >> 1;
379 :
380 : uint16_t num;
381 :
382 : do {
383 1549127 : num = passgen_random_u16(random) & mask;
384 1549127 : } while(num >= max);
385 :
386 1216512 : return num;
387 : }
388 :
389 1434624 : inline uint32_t passgen_random_u32_max(passgen_random *random, uint32_t max) {
390 1434624 : passgen_assert(max);
391 :
392 1434624 : uint32_t mask = max;
393 1434624 : mask |= mask >> 16;
394 1434624 : mask |= mask >> 8;
395 1434624 : mask |= mask >> 4;
396 1434624 : mask |= mask >> 2;
397 1434624 : mask |= mask >> 1;
398 :
399 : uint32_t num;
400 :
401 : do {
402 1903225 : num = passgen_random_u32(random) & mask;
403 1903225 : } while(num >= max);
404 :
405 1434624 : return num;
406 : }
407 :
408 2172348 : inline uint64_t passgen_random_u64_max(passgen_random *random, uint64_t max) {
409 2172348 : passgen_assert(max);
410 :
411 2172348 : uint64_t mask = max;
412 2172348 : mask |= mask >> 32;
413 2172348 : mask |= mask >> 16;
414 2172348 : mask |= mask >> 8;
415 2172348 : mask |= mask >> 4;
416 2172348 : mask |= mask >> 2;
417 2172348 : mask |= mask >> 1;
418 :
419 : uint64_t num;
420 :
421 : do {
422 2866874 : num = passgen_random_u64(random) & mask;
423 2866874 : } while(num >= max);
424 :
425 2172348 : return num;
426 : }
|