fiss

Friedel's Initialization and Service Supervision
Log | Files | Refs | LICENSE

seedrng.c (12628B)


      1 /* Based on code from <https://git.zx2c4.com/seedrng/about/>. */
      2 
      3 #include <endian.h>
      4 #include <errno.h>
      5 #include <fcntl.h>
      6 #include <fmt.h>
      7 #include <linux/random.h>
      8 #include <poll.h>
      9 #include <stdbool.h>
     10 #include <stdint.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <sys/file.h>
     15 #include <sys/ioctl.h>
     16 #include <sys/random.h>
     17 #include <sys/stat.h>
     18 #include <time.h>
     19 #include <unistd.h>
     20 
     21 
     22 const char* current_prog(void) {
     23 	return "seedrng";
     24 }
     25 
     26 #ifndef LOCALSTATEDIR
     27 #	define LOCALSTATEDIR "/var/lib"
     28 #endif
     29 
     30 #define SEED_DIR            LOCALSTATEDIR "/seedrng"
     31 #define CREDITABLE_SEED     "seed.credit"
     32 #define NON_CREDITABLE_SEED "seed.no-credit"
     33 
     34 enum blake2s_lengths {
     35 	BLAKE2S_BLOCK_LEN = 64,
     36 	BLAKE2S_HASH_LEN  = 32,
     37 	BLAKE2S_KEY_LEN   = 32
     38 };
     39 
     40 enum seedrng_lengths {
     41 	MAX_SEED_LEN = 512,
     42 	MIN_SEED_LEN = BLAKE2S_HASH_LEN
     43 };
     44 
     45 struct blake2s_state {
     46 	uint32_t     h[8];
     47 	uint32_t     t[2];
     48 	uint32_t     f[2];
     49 	uint8_t      buf[BLAKE2S_BLOCK_LEN];
     50 	unsigned int buflen;
     51 	unsigned int outlen;
     52 };
     53 
     54 #define le32_to_cpup(a) le32toh(*(a))
     55 #define cpu_to_le32(a)  htole32(a)
     56 #ifndef ARRAY_SIZE
     57 #	define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
     58 #endif
     59 #ifndef DIV_ROUND_UP
     60 #	define DIV_ROUND_UP(n, d) (((n) + (d) -1) / (d))
     61 #endif
     62 
     63 static inline void cpu_to_le32_array(uint32_t* buf, unsigned int words) {
     64 	while (words--) {
     65 		*buf = cpu_to_le32(*buf);
     66 		++buf;
     67 	}
     68 }
     69 
     70 static inline void le32_to_cpu_array(uint32_t* buf, unsigned int words) {
     71 	while (words--) {
     72 		*buf = le32_to_cpup(buf);
     73 		++buf;
     74 	}
     75 }
     76 
     77 static inline uint32_t ror32(uint32_t word, unsigned int shift) {
     78 	return (word >> (shift & 31)) | (word << ((-shift) & 31));
     79 }
     80 
     81 static const uint32_t blake2s_iv[8] = {
     82 	0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
     83 	0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
     84 };
     85 
     86 static const uint8_t blake2s_sigma[10][16] = {
     87 	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
     88 	{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
     89 	{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
     90 	{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
     91 	{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
     92 	{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
     93 	{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
     94 	{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
     95 	{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
     96 	{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
     97 };
     98 
     99 static void blake2s_set_lastblock(struct blake2s_state* state) {
    100 	state->f[0] = -1;
    101 }
    102 
    103 static void blake2s_increment_counter(struct blake2s_state* state, const uint32_t inc) {
    104 	state->t[0] += inc;
    105 	state->t[1] += (state->t[0] < inc);
    106 }
    107 
    108 static void blake2s_init_param(struct blake2s_state* state, const uint32_t param) {
    109 	int i;
    110 
    111 	memset(state, 0, sizeof(*state));
    112 	for (i = 0; i < 8; ++i)
    113 		state->h[i] = blake2s_iv[i];
    114 	state->h[0] ^= param;
    115 }
    116 
    117 static void blake2s_init(struct blake2s_state* state, const size_t outlen) {
    118 	blake2s_init_param(state, 0x01010000 | outlen);
    119 	state->outlen = outlen;
    120 }
    121 
    122 static void blake2s_compress(struct blake2s_state* state, const uint8_t* block, size_t nblocks, const uint32_t inc) {
    123 	uint32_t m[16];
    124 	uint32_t v[16];
    125 	int      i;
    126 
    127 	while (nblocks > 0) {
    128 		blake2s_increment_counter(state, inc);
    129 		memcpy(m, block, BLAKE2S_BLOCK_LEN);
    130 		le32_to_cpu_array(m, ARRAY_SIZE(m));
    131 		memcpy(v, state->h, 32);
    132 		v[8]  = blake2s_iv[0];
    133 		v[9]  = blake2s_iv[1];
    134 		v[10] = blake2s_iv[2];
    135 		v[11] = blake2s_iv[3];
    136 		v[12] = blake2s_iv[4] ^ state->t[0];
    137 		v[13] = blake2s_iv[5] ^ state->t[1];
    138 		v[14] = blake2s_iv[6] ^ state->f[0];
    139 		v[15] = blake2s_iv[7] ^ state->f[1];
    140 
    141 #define G(r, i, a, b, c, d)                      \
    142 	do {                                         \
    143 		a += b + m[blake2s_sigma[r][2 * i + 0]]; \
    144 		d = ror32(d ^ a, 16);                    \
    145 		c += d;                                  \
    146 		b = ror32(b ^ c, 12);                    \
    147 		a += b + m[blake2s_sigma[r][2 * i + 1]]; \
    148 		d = ror32(d ^ a, 8);                     \
    149 		c += d;                                  \
    150 		b = ror32(b ^ c, 7);                     \
    151 	} while (0)
    152 
    153 #define ROUND(r)                           \
    154 	do {                                   \
    155 		G(r, 0, v[0], v[4], v[8], v[12]);  \
    156 		G(r, 1, v[1], v[5], v[9], v[13]);  \
    157 		G(r, 2, v[2], v[6], v[10], v[14]); \
    158 		G(r, 3, v[3], v[7], v[11], v[15]); \
    159 		G(r, 4, v[0], v[5], v[10], v[15]); \
    160 		G(r, 5, v[1], v[6], v[11], v[12]); \
    161 		G(r, 6, v[2], v[7], v[8], v[13]);  \
    162 		G(r, 7, v[3], v[4], v[9], v[14]);  \
    163 	} while (0)
    164 		ROUND(0);
    165 		ROUND(1);
    166 		ROUND(2);
    167 		ROUND(3);
    168 		ROUND(4);
    169 		ROUND(5);
    170 		ROUND(6);
    171 		ROUND(7);
    172 		ROUND(8);
    173 		ROUND(9);
    174 
    175 #undef G
    176 #undef ROUND
    177 
    178 		for (i = 0; i < 8; ++i)
    179 			state->h[i] ^= v[i] ^ v[i + 8];
    180 
    181 		block += BLAKE2S_BLOCK_LEN;
    182 		--nblocks;
    183 	}
    184 }
    185 
    186 static void blake2s_update(struct blake2s_state* state, const void* inp, size_t inlen) {
    187 	const size_t   fill = BLAKE2S_BLOCK_LEN - state->buflen;
    188 	const uint8_t* in   = inp;
    189 
    190 	if (!inlen)
    191 		return;
    192 	if (inlen > fill) {
    193 		memcpy(state->buf + state->buflen, in, fill);
    194 		blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
    195 		state->buflen = 0;
    196 		in += fill;
    197 		inlen -= fill;
    198 	}
    199 	if (inlen > BLAKE2S_BLOCK_LEN) {
    200 		const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
    201 		blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
    202 		in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
    203 		inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
    204 	}
    205 	memcpy(state->buf + state->buflen, in, inlen);
    206 	state->buflen += inlen;
    207 }
    208 
    209 static void blake2s_final(struct blake2s_state* state, uint8_t* out) {
    210 	blake2s_set_lastblock(state);
    211 	memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
    212 	blake2s_compress(state, state->buf, 1, state->buflen);
    213 	cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
    214 	memcpy(out, state->h, state->outlen);
    215 }
    216 
    217 static ssize_t getrandom_full(void* buf, size_t count, unsigned int flags) {
    218 	ssize_t  ret, total = 0;
    219 	uint8_t* p = buf;
    220 
    221 	do {
    222 		ret = getrandom(p, count, flags);
    223 		if (ret < 0 && errno == EINTR)
    224 			continue;
    225 		else if (ret < 0)
    226 			return ret;
    227 		total += ret;
    228 		p += ret;
    229 		count -= ret;
    230 	} while (count);
    231 	return total;
    232 }
    233 
    234 static ssize_t read_full(int fd, void* buf, size_t count) {
    235 	ssize_t  ret, total = 0;
    236 	uint8_t* p = buf;
    237 
    238 	do {
    239 		ret = read(fd, p, count);
    240 		if (ret < 0 && errno == EINTR)
    241 			continue;
    242 		else if (ret < 0)
    243 			return ret;
    244 		else if (ret == 0)
    245 			break;
    246 		total += ret;
    247 		p += ret;
    248 		count -= ret;
    249 	} while (count);
    250 	return total;
    251 }
    252 
    253 static ssize_t write_full(int fd, const void* buf, size_t count) {
    254 	ssize_t        ret, total = 0;
    255 	const uint8_t* p = buf;
    256 
    257 	do {
    258 		ret = write(fd, p, count);
    259 		if (ret < 0 && errno == EINTR)
    260 			continue;
    261 		else if (ret < 0)
    262 			return ret;
    263 		total += ret;
    264 		p += ret;
    265 		count -= ret;
    266 	} while (count);
    267 	return total;
    268 }
    269 
    270 static size_t determine_optimal_seed_len(void) {
    271 	size_t ret              = 0;
    272 	char   poolsize_str[11] = { 0 };
    273 	int    fd               = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
    274 
    275 	if (fd < 0 || read_full(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
    276 		perror("Unable to determine pool size, falling back to 256 bits");
    277 		ret = MIN_SEED_LEN;
    278 	} else
    279 		ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
    280 	if (fd >= 0)
    281 		close(fd);
    282 	if (ret < MIN_SEED_LEN)
    283 		ret = MIN_SEED_LEN;
    284 	else if (ret > MAX_SEED_LEN)
    285 		ret = MAX_SEED_LEN;
    286 	return ret;
    287 }
    288 
    289 static int read_new_seed(uint8_t* seed, size_t len, bool* is_creditable) {
    290 	ssize_t ret;
    291 	int     urandom_fd;
    292 
    293 	*is_creditable = false;
    294 	ret            = getrandom_full(seed, len, GRND_NONBLOCK);
    295 	if (ret == (ssize_t) len) {
    296 		*is_creditable = true;
    297 		return 0;
    298 	} else if (ret < 0 && errno == ENOSYS) {
    299 		struct pollfd random_fd = {
    300 			.fd     = open("/dev/random", O_RDONLY),
    301 			.events = POLLIN
    302 		};
    303 		if (random_fd.fd < 0)
    304 			return -errno;
    305 		*is_creditable = poll(&random_fd, 1, 0) == 1;
    306 		close(random_fd.fd);
    307 	} else if (getrandom_full(seed, len, GRND_INSECURE) == (ssize_t) len)
    308 		return 0;
    309 	urandom_fd = open("/dev/urandom", O_RDONLY);
    310 	if (urandom_fd < 0)
    311 		return -1;
    312 	ret = read_full(urandom_fd, seed, len);
    313 	if (ret == (ssize_t) len)
    314 		ret = 0;
    315 	else
    316 		ret = -errno ? -errno : -EIO;
    317 	close(urandom_fd);
    318 	errno = -ret;
    319 	return ret ? -1 : 0;
    320 }
    321 
    322 static int seed_rng(uint8_t* seed, size_t len, bool credit) {
    323 	struct {
    324 		int     entropy_count;
    325 		int     buf_size;
    326 		uint8_t buffer[MAX_SEED_LEN];
    327 	} req = {
    328 		.entropy_count = credit ? len * 8 : 0,
    329 		.buf_size      = len
    330 	};
    331 	int random_fd, ret;
    332 
    333 	if (len > sizeof(req.buffer)) {
    334 		errno = EFBIG;
    335 		return -1;
    336 	}
    337 	memcpy(req.buffer, seed, len);
    338 
    339 	random_fd = open("/dev/urandom", O_RDONLY);
    340 	if (random_fd < 0)
    341 		return -1;
    342 	ret = ioctl(random_fd, RNDADDENTROPY, &req);
    343 	if (ret)
    344 		ret = -errno ? -errno : -EIO;
    345 	close(random_fd);
    346 	errno = -ret;
    347 	return ret ? -1 : 0;
    348 }
    349 
    350 static int seed_from_file_if_exists(const char* filename, int dfd, bool credit, struct blake2s_state* hash) {
    351 	uint8_t seed[MAX_SEED_LEN];
    352 	ssize_t seed_len;
    353 	int     fd = -1, ret = 0;
    354 
    355 	fd = openat(dfd, filename, O_RDONLY);
    356 	if (fd < 0 && errno == ENOENT)
    357 		return 0;
    358 	else if (fd < 0) {
    359 		ret = -errno;
    360 		perror("Unable to open seed file");
    361 		goto out;
    362 	}
    363 	seed_len = read_full(fd, seed, sizeof(seed));
    364 	if (seed_len < 0) {
    365 		ret = -errno;
    366 		perror("Unable to read seed file");
    367 		goto out;
    368 	}
    369 	if ((unlinkat(dfd, filename, 0) < 0 || fsync(dfd) < 0) && seed_len) {
    370 		ret = -errno;
    371 		perror("Unable to remove seed after reading, so not seeding");
    372 		goto out;
    373 	}
    374 	if (!seed_len)
    375 		goto out;
    376 
    377 	blake2s_update(hash, &seed_len, sizeof(seed_len));
    378 	blake2s_update(hash, seed, seed_len);
    379 
    380 	print("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
    381 	if (seed_rng(seed, seed_len, credit) < 0) {
    382 		ret = -errno;
    383 		perror("Unable to seed");
    384 	}
    385 
    386 out:
    387 	if (fd >= 0)
    388 		close(fd);
    389 	errno = -ret;
    390 	return ret ? -1 : 0;
    391 }
    392 
    393 static bool skip_credit(void) {
    394 	const char* skip = getenv("SEEDRNG_SKIP_CREDIT");
    395 	return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
    396 	                !strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
    397 }
    398 
    399 int main(int argc __attribute__((unused)), char* argv[] __attribute__((unused))) {
    400 	static const char    seedrng_prefix[]  = "SeedRNG v1 Old+New Prefix";
    401 	static const char    seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
    402 	int                  fd = -1, dfd = -1, program_ret = 0;
    403 	uint8_t              new_seed[MAX_SEED_LEN];
    404 	size_t               new_seed_len;
    405 	bool                 new_seed_creditable;
    406 	struct timespec      realtime = { 0 }, boottime = { 0 };
    407 	struct blake2s_state hash;
    408 
    409 	umask(0077);
    410 	if (getuid()) {
    411 		errno = EACCES;
    412 		perror("This program requires root");
    413 		return 1;
    414 	}
    415 
    416 	blake2s_init(&hash, BLAKE2S_HASH_LEN);
    417 	blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
    418 	clock_gettime(CLOCK_REALTIME, &realtime);
    419 	clock_gettime(CLOCK_BOOTTIME, &boottime);
    420 	blake2s_update(&hash, &realtime, sizeof(realtime));
    421 	blake2s_update(&hash, &boottime, sizeof(boottime));
    422 
    423 	if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
    424 		perror("Unable to create seed directory");
    425 		return 1;
    426 	}
    427 
    428 	dfd = open(SEED_DIR, O_DIRECTORY);
    429 	if (dfd < 0 || flock(dfd, LOCK_EX) < 0) {
    430 		perror("Unable to lock seed directory");
    431 		program_ret = 1;
    432 		goto out;
    433 	}
    434 
    435 	if (seed_from_file_if_exists(NON_CREDITABLE_SEED, dfd, false, &hash) < 0)
    436 		program_ret |= 1 << 1;
    437 	if (seed_from_file_if_exists(CREDITABLE_SEED, dfd, !skip_credit(), &hash) < 0)
    438 		program_ret |= 1 << 2;
    439 
    440 	new_seed_len = determine_optimal_seed_len();
    441 	if (read_new_seed(new_seed, new_seed_len, &new_seed_creditable) < 0) {
    442 		perror("Unable to read new seed");
    443 		new_seed_len = BLAKE2S_HASH_LEN;
    444 		strncpy((char*) new_seed, seedrng_failure, new_seed_len);
    445 		program_ret |= 1 << 3;
    446 	}
    447 	blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
    448 	blake2s_update(&hash, new_seed, new_seed_len);
    449 	blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
    450 
    451 	print("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
    452 	fd = openat(dfd, NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
    453 	if (fd < 0) {
    454 		perror("Unable to open seed file for writing");
    455 		program_ret |= 1 << 4;
    456 		goto out;
    457 	}
    458 	if (write_full(fd, new_seed, new_seed_len) != (ssize_t) new_seed_len || fsync(fd) < 0) {
    459 		perror("Unable to write seed file");
    460 		program_ret |= 1 << 5;
    461 		goto out;
    462 	}
    463 	if (new_seed_creditable && renameat(dfd, NON_CREDITABLE_SEED, dfd, CREDITABLE_SEED) < 0) {
    464 		perror("Unable to make new seed creditable");
    465 		program_ret |= 1 << 6;
    466 	}
    467 out:
    468 	if (fd >= 0)
    469 		close(fd);
    470 	if (dfd >= 0)
    471 		close(dfd);
    472 	return program_ret;
    473 }