Coverage Report

Created: 2024-11-29 06:05

/builds/passgen/passgen-c/src/random/random.c
Line
Count
Source (jump to first uncovered line)
1
#include "passgen/random.h"
2
#include "passgen/assert.h"
3
#include "passgen/config.h"
4
#include "passgen/util/endian.h"
5
#include <stdlib.h>
6
#include <string.h>
7
8
29
static bool _strprefix(const char *prefix, const char *string) {
9
29
    size_t prefix_len = strlen(prefix);
10
108
    for(size_t i = 0; i < prefix_len; 
i++79
) {
11
97
        if(prefix[i] != string[i]) {
12
18
            return false;
13
18
        }
14
97
    }
15
29
    
return true11
;
16
29
}
17
18
57.2k
void passgen_random_reload(passgen_random *random) {
19
57.2k
    passgen_assert(random != NULL);
20
57.2k
21
57.2k
    // read random data.
22
57.2k
    size_t bytes =
23
57.2k
        random->read(random->context, &random->buffer, sizeof(random->buffer));
24
57.2k
    (void) bytes;
25
57.2k
26
57.2k
    // make sure we've read enough.
27
57.2k
    passgen_assert(bytes == sizeof(random->buffer));
28
57.2k
29
57.2k
    // reset position in ring buffer.
30
57.2k
    random->pos = 0;
31
57.2k
}
32
33
11.3M
void passgen_random_read(passgen_random *random, void *data, size_t bytes) {
34
11.3M
    if(bytes <= sizeof(random->buffer)) {
35
11.3M
        // maximum bytes we can have right now
36
11.3M
        size_t left = sizeof(random->buffer) - random->pos;
37
11.3M
38
11.3M
        if(bytes < left) {
39
11.3M
            // if we have enough, just copy it over.
40
11.3M
            memcpy(data, &random->buffer[random->pos], bytes);
41
11.3M
            random->pos += bytes;
42
11.3M
        } else {
43
57.2k
            // if we don't have enough, copy over whatever we have and
44
57.2k
            // reload.
45
57.2k
            memcpy(data, random->buffer, left);
46
57.2k
            passgen_random_reload(random);
47
57.2k
48
57.2k
            // if there's more to be read, recurse.
49
57.2k
            if(bytes != left) {
50
3
                passgen_random_read(random, data + left, bytes - left);
51
3
            }
52
57.2k
        }
53
11.3M
    } else {
54
1
        size_t ret = random->read(random->context, data, bytes);
55
1
        if(ret != bytes) {
56
0
            fprintf(
57
0
                stderr,
58
0
                "Error reading from randomness source: trying to read %zu "
59
0
                "bytes but got %zu",
60
0
                bytes,
61
0
                ret);
62
0
            abort();
63
0
        }
64
1
    }
65
11.3M
}
66
67
passgen_random *
68
17
passgen_random_open_parse(passgen_random *random, const char *desc) {
69
17
    if(strcmp("zero", desc) == 0) {
70
2
        return passgen_random_zero_open(random);
71
2
    }
72
15
73
15
    // check if we should read randomness from this file
74
15
    if(_strprefix("file:", desc)) {
75
5
        return passgen_random_path_open(random, &desc[5]);
76
5
    }
77
10
78
10
    // check if we should use the xorshift PRNG with the given seed
79
10
    if(_strprefix("xorshift:", desc)) {
80
6
        const char *seed_str = &desc[9];
81
6
        if(*seed_str == 0) {
82
2
            return NULL;
83
2
        }
84
4
85
4
        uint64_t seed = atoll(seed_str);
86
4
        if(seed == 0) {
87
2
            return NULL;
88
2
        }
89
2
90
2
        return passgen_random_xorshift_open(random, seed);
91
2
    }
92
4
93
4
    // check if we should use the system default
94
4
    if(0 == strcmp(desc, "system")) {
95
2
        return passgen_random_system_open(random);
96
2
    }
97
2
98
2
#ifdef PASSGEN_MONOCYPHER
99
2
    // use chacha20 with raw key and IV
100
2
    if(_strprefix("chacha20:", desc)) {
101
0
        const char *key_ptr = &desc[9];
102
0
        const char *key_end = strchr(key_ptr, ':');
103
0
        if(!key_end) {
104
0
            key_end = key_ptr + strlen(key_ptr);
105
0
        }
106
0
        char key[32] = {0};
107
0
        memcpy(key, key_ptr, key_end - key_ptr);
108
0
109
0
        char iv[] = "passgen";
110
0
111
0
        if(*key_end == ':') {
112
0
            const char *iv_ptr = key_end + 1;
113
0
            size_t iv_len = strlen(iv_ptr);
114
0
            iv_len = iv_len > 8 ? 8 : iv_len;
115
0
            memset(iv, 0, 8);
116
0
            memcpy(iv, iv_ptr, iv_len);
117
0
        }
118
0
119
0
        return passgen_random_chacha20_open(random, key, iv);
120
0
    }
121
2
122
2
    // use chacha20 with raw key and IV
123
2
    if(_strprefix("chacha20-argon2:", desc)) {
124
0
        char master_pass[128] = {0};
125
0
        char domain[128] = {0};
126
0
        char token[128] = {0};
127
0
128
0
        const char *start = &desc[16];
129
0
        const char *end = &desc[16];
130
0
131
0
#define PARSE_STRING(name) \
132
0
        if(*end == ':') end++; \
133
0
        if(*end) { \
134
0
            start = end; \
135
0
            end = strchr(start, ':'); \
136
0
            if(!end) { \
137
0
                end = start + strlen(start); \
138
0
            } \
139
0
            memcpy(name, start, end - start); \
140
0
        }
141
0
142
0
        PARSE_STRING(master_pass)
143
0
        PARSE_STRING(domain)
144
0
        PARSE_STRING(token)
145
0
146
0
        return passgen_random_chacha20_argon2_open(random, master_pass, domain, token, NULL);
147
0
    }
148
2
#endif
149
2
150
2
    return NULL;
151
2
}
152
153
68
passgen_random *passgen_random_open(passgen_random *random, const char *desc) {
154
68
    if(desc) {
155
17
        return passgen_random_open_parse(random, desc);
156
17
    }
157
51
158
51
    return passgen_random_system_open(random);
159
51
}
160
161
47
void passgen_random_close(passgen_random *random) {
162
47
    // close randomness source
163
47
    random->close(random->context);
164
47
165
47
    // reset members to prevent accidental use-after-free
166
47
    PASSGEN_CLEAR(random);
167
47
}
168
169
12
void passgen_random_free(passgen_random *random) {
170
12
    passgen_random_close(random);
171
12
    free(random);
172
12
}
173
174
646k
inline uint8_t passgen_random_u8(passgen_random *random) {
175
646k
    uint8_t data;
176
646k
    passgen_random_read(random, &data, sizeof(data));
177
646k
    return data;
178
646k
}
179
180
2.31M
inline uint16_t passgen_random_u16(passgen_random *random) {
181
2.31M
    uint16_t data;
182
2.31M
    passgen_random_read(random, &data, sizeof(data));
183
2.31M
    data = htole16(data);
184
2.31M
    return data;
185
2.31M
}
186
187
3.52M
inline uint32_t passgen_random_u32(passgen_random *random) {
188
3.52M
    uint32_t data;
189
3.52M
    passgen_random_read(random, &data, sizeof(data));
190
3.52M
    data = htole32(data);
191
3.52M
    return data;
192
3.52M
}
193
194
4.89M
inline uint64_t passgen_random_u64(passgen_random *random) {
195
4.89M
    uint64_t data;
196
4.89M
    passgen_random_read(random, &data, sizeof(data));
197
4.89M
    data = htole64(data);
198
4.89M
    return data;
199
4.89M
}
200
201
6
inline bool passgen_random_bool(passgen_random *random) {
202
6
    uint8_t data;
203
6
    passgen_random_read(random, &data, sizeof(data));
204
6
    return (data & 128) == 0;
205
6
}
206
207
463k
inline uint8_t passgen_random_u8_max(passgen_random *random, uint8_t max) {
208
463k
    passgen_assert(max);
209
463k
210
463k
    uint8_t mask = max;
211
463k
    mask |= mask >> 4;
212
463k
    mask |= mask >> 2;
213
463k
    mask |= mask >> 1;
214
463k
215
463k
    uint8_t num;
216
643k
    do {
217
643k
        num = passgen_random_u8(random) & mask;
218
643k
    } while(num >= max);
219
463k
220
463k
    return num;
221
463k
}
222
223
1.13M
inline uint16_t passgen_random_u16_max(passgen_random *random, uint16_t max) {
224
1.13M
    passgen_assert(max);
225
1.13M
226
1.13M
    uint16_t mask = max;
227
1.13M
    mask |= mask >> 8;
228
1.13M
    mask |= mask >> 4;
229
1.13M
    mask |= mask >> 2;
230
1.13M
    mask |= mask >> 1;
231
1.13M
232
1.13M
    uint16_t num;
233
1.13M
234
1.44M
    do {
235
1.44M
        num = passgen_random_u16(random) & mask;
236
1.44M
    } while(num >= max);
237
1.13M
238
1.13M
    return num;
239
1.13M
}
240
241
1.29M
inline uint32_t passgen_random_u32_max(passgen_random *random, uint32_t max) {
242
1.29M
    passgen_assert(max);
243
1.29M
244
1.29M
    uint32_t mask = max;
245
1.29M
    mask |= mask >> 16;
246
1.29M
    mask |= mask >> 8;
247
1.29M
    mask |= mask >> 4;
248
1.29M
    mask |= mask >> 2;
249
1.29M
    mask |= mask >> 1;
250
1.29M
251
1.29M
    uint32_t num;
252
1.29M
253
1.74M
    do {
254
1.74M
        num = passgen_random_u32(random) & mask;
255
1.74M
    } while(num >= max);
256
1.29M
257
1.29M
    return num;
258
1.29M
}
259
260
2.21M
inline uint64_t passgen_random_u64_max(passgen_random *random, uint64_t max) {
261
2.21M
    passgen_assert(max);
262
2.21M
263
2.21M
    uint64_t mask = max;
264
2.21M
    mask |= mask >> 32;
265
2.21M
    mask |= mask >> 16;
266
2.21M
    mask |= mask >> 8;
267
2.21M
    mask |= mask >> 4;
268
2.21M
    mask |= mask >> 2;
269
2.21M
    mask |= mask >> 1;
270
2.21M
271
2.21M
    uint64_t num;
272
2.21M
273
2.94M
    do {
274
2.94M
        num = passgen_random_u64(random) & mask;
275
2.94M
    } while(num >= max);
276
2.21M
277
2.21M
    return num;
278
2.21M
}