Coverage Report

Created: 2024-05-03 06:05

/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
}