1 // SPDX-License-Identifier: (GPL-2.0 OR MIT OR Apache-2.0)
3 * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
6 #include <linux/random.h>
7 #include <sys/random.h>
11 #include <sys/types.h>
24 #define SEED_DIR "/etc/seedrng"
25 #define CREDITABLE_SEED SEED_DIR "/seed.credit"
26 #define NON_CREDITABLE_SEED SEED_DIR "/seed.no-credit"
27 #define LOCK_FILE "/tmp/run/seedrng.lock"
29 enum blake2s_lengths
{
30 BLAKE2S_BLOCK_LEN
= 64,
31 BLAKE2S_HASH_LEN
= 32,
35 enum seedrng_lengths
{
37 MIN_SEED_LEN
= BLAKE2S_HASH_LEN
40 struct blake2s_state
{
44 uint8_t buf
[BLAKE2S_BLOCK_LEN
];
49 #define le32_to_cpup(a) le32toh(*(a))
50 #define cpu_to_le32(a) htole32(a)
52 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
55 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
58 static inline void cpu_to_le32_array(uint32_t *buf
, unsigned int words
)
61 *buf
= cpu_to_le32(*buf
);
66 static inline void le32_to_cpu_array(uint32_t *buf
, unsigned int words
)
69 *buf
= le32_to_cpup(buf
);
74 static inline uint32_t ror32(uint32_t word
, unsigned int shift
)
76 return (word
>> (shift
& 31)) | (word
<< ((-shift
) & 31));
79 static const uint32_t blake2s_iv
[8] = {
80 0x6A09E667UL
, 0xBB67AE85UL
, 0x3C6EF372UL
, 0xA54FF53AUL
,
81 0x510E527FUL
, 0x9B05688CUL
, 0x1F83D9ABUL
, 0x5BE0CD19UL
84 static const uint8_t blake2s_sigma
[10][16] = {
85 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
86 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
87 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
88 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
89 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
90 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
91 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
92 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
93 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
94 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
97 static void blake2s_set_lastblock(struct blake2s_state
*state
)
102 static void blake2s_increment_counter(struct blake2s_state
*state
, const uint32_t inc
)
105 state
->t
[1] += (state
->t
[0] < inc
);
108 static void blake2s_init_param(struct blake2s_state
*state
, const uint32_t param
)
112 memset(state
, 0, sizeof(*state
));
113 for (i
= 0; i
< 8; ++i
)
114 state
->h
[i
] = blake2s_iv
[i
];
115 state
->h
[0] ^= param
;
118 static void blake2s_init(struct blake2s_state
*state
, const size_t outlen
)
120 blake2s_init_param(state
, 0x01010000 | outlen
);
121 state
->outlen
= outlen
;
124 static void blake2s_compress(struct blake2s_state
*state
, const uint8_t *block
, size_t nblocks
, const uint32_t inc
)
130 while (nblocks
> 0) {
131 blake2s_increment_counter(state
, inc
);
132 memcpy(m
, block
, BLAKE2S_BLOCK_LEN
);
133 le32_to_cpu_array(m
, ARRAY_SIZE(m
));
134 memcpy(v
, state
->h
, 32);
135 v
[ 8] = blake2s_iv
[0];
136 v
[ 9] = blake2s_iv
[1];
137 v
[10] = blake2s_iv
[2];
138 v
[11] = blake2s_iv
[3];
139 v
[12] = blake2s_iv
[4] ^ state
->t
[0];
140 v
[13] = blake2s_iv
[5] ^ state
->t
[1];
141 v
[14] = blake2s_iv
[6] ^ state
->f
[0];
142 v
[15] = blake2s_iv
[7] ^ state
->f
[1];
144 #define G(r, i, a, b, c, d) do { \
145 a += b + m[blake2s_sigma[r][2 * i + 0]]; \
146 d = ror32(d ^ a, 16); \
148 b = ror32(b ^ c, 12); \
149 a += b + m[blake2s_sigma[r][2 * i + 1]]; \
150 d = ror32(d ^ a, 8); \
152 b = ror32(b ^ c, 7); \
155 #define ROUND(r) do { \
156 G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
157 G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
158 G(r, 2, v[2], v[ 6], v[10], v[14]); \
159 G(r, 3, v[3], v[ 7], v[11], v[15]); \
160 G(r, 4, v[0], v[ 5], v[10], v[15]); \
161 G(r, 5, v[1], v[ 6], v[11], v[12]); \
162 G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
163 G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
179 for (i
= 0; i
< 8; ++i
)
180 state
->h
[i
] ^= v
[i
] ^ v
[i
+ 8];
182 block
+= BLAKE2S_BLOCK_LEN
;
187 static void blake2s_update(struct blake2s_state
*state
, const void *inp
, size_t inlen
)
189 const size_t fill
= BLAKE2S_BLOCK_LEN
- state
->buflen
;
190 const uint8_t *in
= inp
;
195 memcpy(state
->buf
+ state
->buflen
, in
, fill
);
196 blake2s_compress(state
, state
->buf
, 1, BLAKE2S_BLOCK_LEN
);
201 if (inlen
> BLAKE2S_BLOCK_LEN
) {
202 const size_t nblocks
= DIV_ROUND_UP(inlen
, BLAKE2S_BLOCK_LEN
);
203 blake2s_compress(state
, in
, nblocks
- 1, BLAKE2S_BLOCK_LEN
);
204 in
+= BLAKE2S_BLOCK_LEN
* (nblocks
- 1);
205 inlen
-= BLAKE2S_BLOCK_LEN
* (nblocks
- 1);
207 memcpy(state
->buf
+ state
->buflen
, in
, inlen
);
208 state
->buflen
+= inlen
;
211 static void blake2s_final(struct blake2s_state
*state
, uint8_t *out
)
213 blake2s_set_lastblock(state
);
214 memset(state
->buf
+ state
->buflen
, 0, BLAKE2S_BLOCK_LEN
- state
->buflen
);
215 blake2s_compress(state
, state
->buf
, 1, state
->buflen
);
216 cpu_to_le32_array(state
->h
, ARRAY_SIZE(state
->h
));
217 memcpy(out
, state
->h
, state
->outlen
);
220 static size_t determine_optimal_seed_len(void)
223 char poolsize_str
[11] = { 0 };
224 int fd
= open("/proc/sys/kernel/random/poolsize", O_RDONLY
);
226 if (fd
< 0 || read(fd
, poolsize_str
, sizeof(poolsize_str
) - 1) < 0) {
227 fprintf(stderr
, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN
* 8, strerror(errno
));
230 ret
= DIV_ROUND_UP(strtoul(poolsize_str
, NULL
, 10), 8);
233 if (ret
< MIN_SEED_LEN
)
235 else if (ret
> MAX_SEED_LEN
)
240 static int read_new_seed(uint8_t *seed
, size_t len
, bool *is_creditable
)
245 *is_creditable
= false;
246 ret
= getrandom(seed
, len
, GRND_NONBLOCK
);
247 if (ret
== (ssize_t
)len
) {
248 *is_creditable
= true;
250 } else if (ret
< 0 && errno
== ENOSYS
) {
251 struct pollfd random_fd
= {
252 .fd
= open("/dev/random", O_RDONLY
),
255 if (random_fd
.fd
< 0)
257 *is_creditable
= poll(&random_fd
, 1, 0) == 1;
259 } else if (getrandom(seed
, len
, GRND_INSECURE
) == (ssize_t
)len
)
261 urandom_fd
= open("/dev/urandom", O_RDONLY
);
264 ret
= read(urandom_fd
, seed
, len
);
265 if (ret
== (ssize_t
)len
)
268 ret
= -errno
? -errno
: -EIO
;
273 static int seed_rng(uint8_t *seed
, size_t len
, bool credit
)
278 uint8_t buffer
[MAX_SEED_LEN
];
280 .entropy_count
= credit
? len
* 8 : 0,
285 if (len
> sizeof(req
.buffer
))
287 memcpy(req
.buffer
, seed
, len
);
289 random_fd
= open("/dev/random", O_RDWR
);
292 ret
= ioctl(random_fd
, RNDADDENTROPY
, &req
);
294 ret
= -errno
? -errno
: -EIO
;
299 static int seed_from_file_if_exists(const char *filename
, bool credit
, struct blake2s_state
*hash
)
301 uint8_t seed
[MAX_SEED_LEN
];
303 int fd
, dfd
, ret
= 0;
305 fd
= open(filename
, O_RDONLY
);
306 if (fd
< 0 && errno
== ENOENT
)
310 fprintf(stderr
, "ERROR: Unable to open seed file: %s\n", strerror(errno
));
313 dfd
= open(SEED_DIR
, O_DIRECTORY
| O_RDONLY
);
317 fprintf(stderr
, "ERROR: Unable to open seed directory: %s\n", strerror(errno
));
320 seed_len
= read(fd
, seed
, sizeof(seed
));
323 fprintf(stderr
, "ERROR: Unable to read seed file: %s\n", strerror(errno
));
330 if ((unlink(filename
) < 0 || fsync(dfd
) < 0) && seed_len
) {
332 fprintf(stderr
, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno
));
340 blake2s_update(hash
, &seed_len
, sizeof(seed_len
));
341 blake2s_update(hash
, seed
, seed_len
);
343 fprintf(stdout
, "Seeding %zd bits %s crediting\n", seed_len
* 8, credit
? "and" : "without");
344 ret
= seed_rng(seed
, seed_len
, credit
);
346 fprintf(stderr
, "ERROR: Unable to seed: %s\n", strerror(-ret
));
350 static bool skip_credit(void)
352 const char *skip
= getenv("SEEDRNG_SKIP_CREDIT");
353 return skip
&& (!strcmp(skip
, "1") || !strcasecmp(skip
, "true") ||
354 !strcasecmp(skip
, "yes") || !strcasecmp(skip
, "y"));
357 int main(int argc
__attribute__((unused
)), char *argv
[] __attribute__((unused
)))
359 static const char seedrng_prefix
[] = "SeedRNG v1 Old+New Prefix";
360 static const char seedrng_failure
[] = "SeedRNG v1 No New Seed Failure";
361 int ret
, fd
= -1, lock
, program_ret
= 0;
362 uint8_t new_seed
[MAX_SEED_LEN
];
364 bool new_seed_creditable
;
365 struct timespec realtime
= { 0 }, boottime
= { 0 };
366 struct blake2s_state hash
;
370 fprintf(stderr
, "ERROR: This program requires root\n");
374 blake2s_init(&hash
, BLAKE2S_HASH_LEN
);
375 blake2s_update(&hash
, seedrng_prefix
, strlen(seedrng_prefix
));
376 clock_gettime(CLOCK_REALTIME
, &realtime
);
377 clock_gettime(CLOCK_BOOTTIME
, &boottime
);
378 blake2s_update(&hash
, &realtime
, sizeof(realtime
));
379 blake2s_update(&hash
, &boottime
, sizeof(boottime
));
381 if (mkdir(SEED_DIR
, 0700) < 0 && errno
!= EEXIST
) {
382 fprintf(stderr
, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR
, strerror(errno
));
386 lock
= open(LOCK_FILE
, O_WRONLY
| O_CREAT
, 0000);
387 if (lock
< 0 || flock(lock
, LOCK_EX
) < 0) {
388 fprintf(stderr
, "ERROR: Unable to open lock file: %s\n", strerror(errno
));
393 ret
= seed_from_file_if_exists(NON_CREDITABLE_SEED
, false, &hash
);
395 program_ret
|= 1 << 1;
396 ret
= seed_from_file_if_exists(CREDITABLE_SEED
, !skip_credit(), &hash
);
398 program_ret
|= 1 << 2;
400 new_seed_len
= determine_optimal_seed_len();
401 ret
= read_new_seed(new_seed
, new_seed_len
, &new_seed_creditable
);
403 fprintf(stderr
, "ERROR: Unable to read new seed: %s\n", strerror(-ret
));
404 new_seed_len
= BLAKE2S_HASH_LEN
;
405 strncpy((char *)new_seed
, seedrng_failure
, new_seed_len
);
406 program_ret
|= 1 << 3;
408 blake2s_update(&hash
, &new_seed_len
, sizeof(new_seed_len
));
409 blake2s_update(&hash
, new_seed
, new_seed_len
);
410 blake2s_final(&hash
, new_seed
+ new_seed_len
- BLAKE2S_HASH_LEN
);
412 fprintf(stdout
, "Saving %zu bits of %s seed for next boot\n", new_seed_len
* 8, new_seed_creditable
? "creditable" : "non-creditable");
413 fd
= open(NON_CREDITABLE_SEED
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0400);
415 fprintf(stderr
, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno
));
416 program_ret
|= 1 << 4;
419 if (write(fd
, new_seed
, new_seed_len
) != (ssize_t
)new_seed_len
|| fsync(fd
) < 0) {
420 fprintf(stderr
, "ERROR: Unable to write seed file: %s\n", strerror(errno
));
421 program_ret
|= 1 << 5;
424 if (new_seed_creditable
&& rename(NON_CREDITABLE_SEED
, CREDITABLE_SEED
) < 0) {
425 fprintf(stderr
, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno
));
426 program_ret
|= 1 << 6;