/builds/xfbs/passgen/src/util/random.c
Line | Count | Source (jump to first uncovered line) |
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++79 ) { |
9 | 93 | if(prefix[i] != string[i]) { |
10 | 14 | return false; |
11 | 14 | } |
12 | 93 | } |
13 | 25 | return true11 ; |
14 | 25 | } |
15 | | |
16 | | #ifdef __linux__ |
17 | | #define PASSGEN_RANDOM_HAVE_SYSTEM |
18 | | #include <sys/random.h> |
19 | | |
20 | 55.1k | size_t passgen_random_read_system(void *dest, size_t size, void *data) { |
21 | 55.1k | (void) data; |
22 | 55.1k | return getrandom(dest, size, 0); |
23 | 55.1k | } |
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 | 5 | } |
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 | 17 | (void) data; |
50 | 17 | } |
51 | | |
52 | 39.4k | static uint64_t xorshift64(uint64_t *state) { |
53 | 39.4k | uint64_t x = *state; |
54 | 39.4k | x ^= x << 13; |
55 | 39.4k | x ^= x >> 7; |
56 | 39.4k | x ^= x << 17; |
57 | 39.4k | return *state = x; |
58 | 39.4k | } |
59 | | |
60 | 308 | size_t passgen_random_read_xorshift(void *dest, size_t size, void *data) { |
61 | 308 | size_t written = 0; |
62 | 308 | uint64_t result; |
63 | 308 | |
64 | 308 | // fill all whole uint64 blocks |
65 | 39.7k | while((size - written) >= sizeof(result)) { |
66 | 39.4k | result = xorshift64(data); |
67 | 39.4k | memcpy(dest + written, &result, sizeof(result)); |
68 | 39.4k | written += sizeof(result); |
69 | 39.4k | } |
70 | 308 | |
71 | 308 | // 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 | 1 | } |
77 | 308 | |
78 | 308 | return written; |
79 | 308 | } |
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 NULL0 ; |
88 | 1 | |
89 | 1 | return passgen_random_open_xorshift(random, seed); |
90 | 1 | } |
91 | | |
92 | 55.4k | void passgen_random_reload(passgen_random *random) { |
93 | 55.4k | passgen_assert(random != NULL); |
94 | 55.4k | |
95 | 55.4k | // read random data. |
96 | 55.4k | size_t bytes = |
97 | 55.4k | random->read(&random->buffer, sizeof(random->buffer), random->data); |
98 | 55.4k | (void) bytes; |
99 | 55.4k | |
100 | 55.4k | // make sure we've read enough. |
101 | 55.4k | passgen_assert(bytes == sizeof(random->buffer)); |
102 | 55.4k | |
103 | 55.4k | // reset position in ring buffer. |
104 | 55.4k | random->pos = 0; |
105 | 55.4k | } |
106 | | |
107 | | passgen_random * |
108 | 17 | passgen_random_open_xorshift(passgen_random *random, uint64_t seed) { |
109 | 17 | // create state |
110 | 17 | uint64_t *state = malloc(sizeof(uint64_t)); |
111 | 17 | if(!state) return NULL0 ; |
112 | 17 | |
113 | 17 | // initialise state |
114 | 17 | *state = seed; |
115 | 17 | |
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 | 17 | |
121 | 17 | return random; |
122 | 17 | } |
123 | | |
124 | 3 | size_t passgen_random_read_zero(void *dest, size_t size, void *data) { |
125 | 3 | (void) data; |
126 | 3 | memset(dest, 0, size); |
127 | 3 | return size; |
128 | 3 | } |
129 | | |
130 | 3 | void passgen_random_close_zero(void *data) { |
131 | 3 | (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 | 3 | } |
141 | | |
142 | 1 | passgen_random *passgen_random_new_zero() { |
143 | 1 | passgen_random *random = malloc(sizeof(passgen_random)); |
144 | 1 | if(!random) return NULL0 ; |
145 | 1 | return passgen_random_open_zero(random); |
146 | 1 | } |
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 | 53 | } |
156 | | #else |
157 | | passgen_random *passgen_random_open_system(passgen_random *random) { |
158 | | (void) random; |
159 | | |
160 | | return NULL; |
161 | | } |
162 | | #endif |
163 | | |
164 | 11.1M | void passgen_random_read(passgen_random *random, void *data, size_t bytes) { |
165 | 11.1M | if(bytes <= sizeof(random->buffer)) { |
166 | 11.1M | // maximum bytes we can have right now |
167 | 11.1M | size_t left = sizeof(random->buffer) - random->pos; |
168 | 11.1M | |
169 | 11.1M | if(bytes < left) { |
170 | 11.0M | // if we have enough, just copy it over. |
171 | 11.0M | memcpy(data, &random->buffer[random->pos], bytes); |
172 | 11.0M | random->pos += bytes; |
173 | 11.0M | } else { |
174 | 55.3k | // if we don't have enough, copy over whatever we have and |
175 | 55.3k | // reload. |
176 | 55.3k | memcpy(data, random->buffer, left); |
177 | 55.3k | passgen_random_reload(random); |
178 | 55.3k | |
179 | 55.3k | // if there's more to be read, recurse. |
180 | 55.3k | if(bytes != left) { |
181 | 20 | passgen_random_read(random, data + left, bytes - left); |
182 | 20 | } |
183 | 55.3k | } |
184 | 11.1M | } else { |
185 | 1 | size_t ret = random->read(data, bytes, random->data); |
186 | 1 | if(ret != bytes) { |
187 | 0 | fprintf( |
188 | 0 | stderr, |
189 | 0 | "Error reading from randomness source: trying to read %zu " |
190 | 0 | "bytes but got %zu", |
191 | 0 | bytes, |
192 | 0 | ret); |
193 | 0 | abort(); |
194 | 0 | } |
195 | 1 | } |
196 | 11.1M | } |
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 | 13 | |
202 | 13 | passgen_random *ret = passgen_random_open(random, desc); |
203 | 13 | if(!ret) { |
204 | 5 | free(random); |
205 | 5 | } |
206 | 13 | |
207 | 13 | return ret; |
208 | 13 | } |
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 NULL0 ; |
213 | 2 | |
214 | 2 | if(!passgen_random_open_path(random, path)) { |
215 | 1 | free(random); |
216 | 1 | return NULL; |
217 | 1 | } |
218 | 1 | |
219 | 1 | return random; |
220 | 1 | } |
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 NULL0 ; |
225 | 1 | |
226 | 1 | passgen_random_open_file(random, file); |
227 | 1 | |
228 | 1 | return random; |
229 | 1 | } |
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 | 2 | } |
236 | 15 | |
237 | 15 | // 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 | 5 | } |
241 | 10 | |
242 | 10 | // 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 | 2 | } |
248 | 4 | |
249 | 4 | uint64_t seed = atoll(seed_str); |
250 | 4 | if(seed == 0) { |
251 | 2 | return NULL; |
252 | 2 | } |
253 | 2 | |
254 | 2 | return passgen_random_open_xorshift(random, seed); |
255 | 2 | } |
256 | 4 | |
257 | 4 | // check if we should use the system default |
258 | 4 | if(0 == strcmp(desc, "system")) { |
259 | 2 | return passgen_random_open_system(random); |
260 | 2 | } |
261 | 2 | |
262 | 2 | return NULL; |
263 | 2 | } |
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 | 17 | } |
269 | 51 | |
270 | 51 | #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 | 51 | } |
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 | 5 | } |
291 | 4 | |
292 | 4 | return passgen_random_open_file(random, device); |
293 | 4 | |
294 | 4 | passgen_random_reload(random); |
295 | 0 |
|
296 | 0 | return random; |
297 | 4 | } |
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 | 5 | } |
306 | | |
307 | 42 | void passgen_random_close(passgen_random *random) { |
308 | 42 | // close randomness source |
309 | 42 | random->close(random->data); |
310 | 42 | |
311 | 42 | // reset members to prevent accidental use-after-free |
312 | 42 | random->data = NULL; |
313 | 42 | random->read = NULL; |
314 | 42 | random->close = NULL; |
315 | 42 | |
316 | 42 | // 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 | 638k | inline uint8_t passgen_random_u8(passgen_random *random) { |
326 | 638k | uint8_t data; |
327 | 638k | passgen_random_read(random, &data, sizeof(data)); |
328 | 638k | return data; |
329 | 638k | } |
330 | | |
331 | 2.36M | inline uint16_t passgen_random_u16(passgen_random *random) { |
332 | 2.36M | uint16_t data; |
333 | 2.36M | passgen_random_read(random, &data, sizeof(data)); |
334 | 2.36M | return data; |
335 | 2.36M | } |
336 | | |
337 | 3.43M | inline uint32_t passgen_random_u32(passgen_random *random) { |
338 | 3.43M | uint32_t data; |
339 | 3.43M | passgen_random_read(random, &data, sizeof(data)); |
340 | 3.43M | return data; |
341 | 3.43M | } |
342 | | |
343 | 4.70M | inline uint64_t passgen_random_u64(passgen_random *random) { |
344 | 4.70M | uint64_t data; |
345 | 4.70M | passgen_random_read(random, &data, sizeof(data)); |
346 | 4.70M | return data; |
347 | 4.70M | } |
348 | | |
349 | 6 | inline bool passgen_random_bool(passgen_random *random) { |
350 | 6 | uint8_t data; |
351 | 6 | passgen_random_read(random, &data, sizeof(data)); |
352 | 6 | return (data & 128) == 0; |
353 | 6 | } |
354 | | |
355 | 456k | inline uint8_t passgen_random_u8_max(passgen_random *random, uint8_t max) { |
356 | 456k | passgen_assert(max); |
357 | 456k | |
358 | 456k | uint8_t mask = max; |
359 | 456k | mask |= mask >> 4; |
360 | 456k | mask |= mask >> 2; |
361 | 456k | mask |= mask >> 1; |
362 | 456k | |
363 | 456k | uint8_t num; |
364 | 637k | do { |
365 | 637k | num = passgen_random_u8(random) & mask; |
366 | 637k | } while(num >= max); |
367 | 456k | |
368 | 456k | return num; |
369 | 456k | } |
370 | | |
371 | 1.14M | inline uint16_t passgen_random_u16_max(passgen_random *random, uint16_t max) { |
372 | 1.14M | passgen_assert(max); |
373 | 1.14M | |
374 | 1.14M | uint16_t mask = max; |
375 | 1.14M | mask |= mask >> 8; |
376 | 1.14M | mask |= mask >> 4; |
377 | 1.14M | mask |= mask >> 2; |
378 | 1.14M | mask |= mask >> 1; |
379 | 1.14M | |
380 | 1.14M | uint16_t num; |
381 | 1.14M | |
382 | 1.45M | do { |
383 | 1.45M | num = passgen_random_u16(random) & mask; |
384 | 1.45M | } while(num >= max); |
385 | 1.14M | |
386 | 1.14M | return num; |
387 | 1.14M | } |
388 | | |
389 | 1.36M | inline uint32_t passgen_random_u32_max(passgen_random *random, uint32_t max) { |
390 | 1.36M | passgen_assert(max); |
391 | 1.36M | |
392 | 1.36M | uint32_t mask = max; |
393 | 1.36M | mask |= mask >> 16; |
394 | 1.36M | mask |= mask >> 8; |
395 | 1.36M | mask |= mask >> 4; |
396 | 1.36M | mask |= mask >> 2; |
397 | 1.36M | mask |= mask >> 1; |
398 | 1.36M | |
399 | 1.36M | uint32_t num; |
400 | 1.36M | |
401 | 1.83M | do { |
402 | 1.83M | num = passgen_random_u32(random) & mask; |
403 | 1.83M | } while(num >= max); |
404 | 1.36M | |
405 | 1.36M | return num; |
406 | 1.36M | } |
407 | | |
408 | 2.07M | inline uint64_t passgen_random_u64_max(passgen_random *random, uint64_t max) { |
409 | 2.07M | passgen_assert(max); |
410 | 2.07M | |
411 | 2.07M | uint64_t mask = max; |
412 | 2.07M | mask |= mask >> 32; |
413 | 2.07M | mask |= mask >> 16; |
414 | 2.07M | mask |= mask >> 8; |
415 | 2.07M | mask |= mask >> 4; |
416 | 2.07M | mask |= mask >> 2; |
417 | 2.07M | mask |= mask >> 1; |
418 | 2.07M | |
419 | 2.07M | uint64_t num; |
420 | 2.07M | |
421 | 2.75M | do { |
422 | 2.75M | num = passgen_random_u64(random) & mask; |
423 | 2.75M | } while(num >= max); |
424 | 2.07M | |
425 | 2.07M | return num; |
426 | 2.07M | } |