All of lore.kernel.org
 help / color / mirror / Atom feed
* [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng
@ 2022-03-27  5:48 Jason A. Donenfeld
  2022-03-27 18:01 ` James Hilliard
  0 siblings, 1 reply; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-03-27  5:48 UTC (permalink / raw)
  To: buildroot, Yann E. MORIN; +Cc: Jason A. Donenfeld

The RNG can't actually be seeded from a shell script, due to the
reliance on ioctls. For this reason, the seedrng project provides a
basic script meant to be copy and pasted into projects like buildroot
and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.

This commit imports it into buildroot and wires up the init scripts to
call it. This also is a significant improvement over the current init
script, which doesn't credit entropy and whose hashing in shell scripts
is sort of fragile.

As seedrng.c is a short tiny C program, we include this here in the
package, like a few other packages do. Later we'll investigate adding
this to busybox, but for now, this is a good start and a positive step
in the right direction.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
 package/urandom-scripts/Config.in          |   4 -
 package/urandom-scripts/S20urandom         |  55 +--
 package/urandom-scripts/seedrng.c          | 445 +++++++++++++++++++++
 package/urandom-scripts/urandom-scripts.mk |  10 +
 4 files changed, 464 insertions(+), 50 deletions(-)
 create mode 100644 package/urandom-scripts/seedrng.c

diff --git a/package/urandom-scripts/Config.in b/package/urandom-scripts/Config.in
index 987e442e22..070ffa5e9a 100644
--- a/package/urandom-scripts/Config.in
+++ b/package/urandom-scripts/Config.in
@@ -4,7 +4,3 @@ config BR2_PACKAGE_URANDOM_SCRIPTS
 	depends on !BR2_PACKAGE_SYSTEMD
 	help
 	  Initscript to preserve the random seed between reboots.
-
-	  WARNING: this is a poor fit to try and get high-quality
-	  entropy at boot. There are better ways, like haveged, or
-	  rng-tools.
diff --git a/package/urandom-scripts/S20urandom b/package/urandom-scripts/S20urandom
index c6b2ebd48f..f248089a0f 100644
--- a/package/urandom-scripts/S20urandom
+++ b/package/urandom-scripts/S20urandom
@@ -6,63 +6,26 @@
 # Quietly do nothing if /dev/urandom does not exist
 [ -c /dev/urandom ] || exit 0
 
-URANDOM_SEED="/var/lib/random-seed"
+# Set this to true only if you do not want seed files to actually credit the
+# RNG, for example if you plan to replicate this file system image and do not
+# have the wherewithal to first delete the contents of /var/lib/seedrng.
+#export SEEDRNG_SKIP_CREDIT=false
+# You can also place this line into /etc/default/urandom.
 
 # shellcheck source=/dev/null
 [ -r "/etc/default/urandom" ] && . "/etc/default/urandom"
 
-if pool_bits=$(cat /proc/sys/kernel/random/poolsize 2> /dev/null); then
-	pool_size=$((pool_bits/8))
-else
-	pool_size=512
-fi
-
-init_rng() {
-	printf 'Initializing random number generator: '
-	dd if="$URANDOM_SEED" bs="$pool_size" of=/dev/urandom count=1 2> /dev/null
-	status=$?
-	if [ "$status" -eq 0 ]; then
-		echo "OK"
-	else
-		echo "FAIL"
-	fi
-	return "$status"
-}
-
-save_random_seed() {
-	printf 'Saving random seed: '
-	status=1
-	if touch "$URANDOM_SEED.new" 2> /dev/null; then
-		old_umask=$(umask)
-		umask 077
-		dd if=/dev/urandom of="$URANDOM_SEED.tmp" bs="$pool_size" count=1 2> /dev/null
-		cat "$URANDOM_SEED" "$URANDOM_SEED.tmp" 2>/dev/null \
-			| sha256sum \
-			| cut -d ' ' -f 1 > "$URANDOM_SEED.new" && \
-		mv "$URANDOM_SEED.new" "$URANDOM_SEED" && status=0
-		rm -f "$URANDOM_SEED.tmp"
-		umask "$old_umask"
-		if [ "$status" -eq 0 ]; then
-			echo "OK"
-		else
-			echo "FAIL"
-		fi
-
-	else
-		echo "SKIP (read-only file system detected)"
-	fi
-	return "$status"
-}
-
 case "$1" in
 	start|restart|reload)
 		# Carry a random seed from start-up to start-up
 		# Load and then save the whole entropy pool
-		init_rng && save_random_seed;;
+		# Never fail, as this isn't worth making a fuss
+		# over if it doesn't go as planned.
+		seedrng || true;;
 	stop)
 		# Carry a random seed from shut-down to start-up
 		# Save the whole entropy pool
-		save_random_seed;;
+		seedrng;;
 	*)
 		echo "Usage: $0 {start|stop|restart|reload}"
 		exit 1
diff --git a/package/urandom-scripts/seedrng.c b/package/urandom-scripts/seedrng.c
new file mode 100644
index 0000000000..a77365d406
--- /dev/null
+++ b/package/urandom-scripts/seedrng.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT OR Apache-2.0)
+/*
+ * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is based on code from <https://git.zx2c4.com/seedrng/about/>.
+ */
+
+#define _GNU_SOURCE
+#include <linux/random.h>
+#include <sys/random.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef GRND_INSECURE
+#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */
+#endif
+
+#ifndef LOCALSTATEDIR
+#define LOCALSTATEDIR "/var/lib"
+#endif
+#ifndef RUNSTATEDIR
+#define RUNSTATEDIR "/var/run"
+#endif
+
+#define SEED_DIR LOCALSTATEDIR "/seedrng"
+#define CREDITABLE_SEED SEED_DIR "/seed.credit"
+#define NON_CREDITABLE_SEED SEED_DIR "/seed.no-credit"
+#define LOCK_FILE RUNSTATEDIR "/seedrng.lock"
+
+enum blake2s_lengths {
+	BLAKE2S_BLOCK_LEN = 64,
+	BLAKE2S_HASH_LEN = 32,
+	BLAKE2S_KEY_LEN = 32
+};
+
+enum seedrng_lengths {
+	MAX_SEED_LEN = 512,
+	MIN_SEED_LEN = BLAKE2S_HASH_LEN
+};
+
+struct blake2s_state {
+	uint32_t h[8];
+	uint32_t t[2];
+	uint32_t f[2];
+	uint8_t buf[BLAKE2S_BLOCK_LEN];
+	unsigned int buflen;
+	unsigned int outlen;
+};
+
+#define le32_to_cpup(a) le32toh(*(a))
+#define cpu_to_le32(a) htole32(a)
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#endif
+
+static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
+{
+        while (words--) {
+		*buf = cpu_to_le32(*buf);
+		++buf;
+	}
+}
+
+static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
+{
+        while (words--) {
+		*buf = le32_to_cpup(buf);
+		++buf;
+        }
+}
+
+static inline uint32_t ror32(uint32_t word, unsigned int shift)
+{
+	return (word >> (shift & 31)) | (word << ((-shift) & 31));
+}
+
+static const uint32_t blake2s_iv[8] = {
+	0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+	0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] = {
+	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+	{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+	{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+	{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+	{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+	{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+	{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+	{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+	{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+	{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+};
+
+static void blake2s_set_lastblock(struct blake2s_state *state)
+{
+	state->f[0] = -1;
+}
+
+static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc)
+{
+	state->t[0] += inc;
+	state->t[1] += (state->t[0] < inc);
+}
+
+static void blake2s_init_param(struct blake2s_state *state, const uint32_t param)
+{
+	int i;
+
+	memset(state, 0, sizeof(*state));
+	for (i = 0; i < 8; ++i)
+		state->h[i] = blake2s_iv[i];
+	state->h[0] ^= param;
+}
+
+static void blake2s_init(struct blake2s_state *state, const size_t outlen)
+{
+	blake2s_init_param(state, 0x01010000 | outlen);
+	state->outlen = outlen;
+}
+
+static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc)
+{
+	uint32_t m[16];
+	uint32_t v[16];
+	int i;
+
+	while (nblocks > 0) {
+		blake2s_increment_counter(state, inc);
+		memcpy(m, block, BLAKE2S_BLOCK_LEN);
+		le32_to_cpu_array(m, ARRAY_SIZE(m));
+		memcpy(v, state->h, 32);
+		v[ 8] = blake2s_iv[0];
+		v[ 9] = blake2s_iv[1];
+		v[10] = blake2s_iv[2];
+		v[11] = blake2s_iv[3];
+		v[12] = blake2s_iv[4] ^ state->t[0];
+		v[13] = blake2s_iv[5] ^ state->t[1];
+		v[14] = blake2s_iv[6] ^ state->f[0];
+		v[15] = blake2s_iv[7] ^ state->f[1];
+
+#define G(r, i, a, b, c, d) do { \
+	a += b + m[blake2s_sigma[r][2 * i + 0]]; \
+	d = ror32(d ^ a, 16); \
+	c += d; \
+	b = ror32(b ^ c, 12); \
+	a += b + m[blake2s_sigma[r][2 * i + 1]]; \
+	d = ror32(d ^ a, 8); \
+	c += d; \
+	b = ror32(b ^ c, 7); \
+} while (0)
+
+#define ROUND(r) do { \
+	G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
+	G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
+	G(r, 2, v[2], v[ 6], v[10], v[14]); \
+	G(r, 3, v[3], v[ 7], v[11], v[15]); \
+	G(r, 4, v[0], v[ 5], v[10], v[15]); \
+	G(r, 5, v[1], v[ 6], v[11], v[12]); \
+	G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
+	G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
+} while (0)
+		ROUND(0);
+		ROUND(1);
+		ROUND(2);
+		ROUND(3);
+		ROUND(4);
+		ROUND(5);
+		ROUND(6);
+		ROUND(7);
+		ROUND(8);
+		ROUND(9);
+
+#undef G
+#undef ROUND
+
+		for (i = 0; i < 8; ++i)
+			state->h[i] ^= v[i] ^ v[i + 8];
+
+		block += BLAKE2S_BLOCK_LEN;
+		--nblocks;
+	}
+}
+
+static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen)
+{
+	const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen;
+	const uint8_t *in = inp;
+
+	if (!inlen)
+		return;
+	if (inlen > fill) {
+		memcpy(state->buf + state->buflen, in, fill);
+		blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
+		state->buflen = 0;
+		in += fill;
+		inlen -= fill;
+	}
+	if (inlen > BLAKE2S_BLOCK_LEN) {
+		const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
+		blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
+		in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
+		inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
+	}
+	memcpy(state->buf + state->buflen, in, inlen);
+	state->buflen += inlen;
+}
+
+static void blake2s_final(struct blake2s_state *state, uint8_t *out)
+{
+	blake2s_set_lastblock(state);
+	memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
+	blake2s_compress(state, state->buf, 1, state->buflen);
+	cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
+	memcpy(out, state->h, state->outlen);
+}
+
+static size_t determine_optimal_seed_len(void)
+{
+	size_t ret = 0;
+	char poolsize_str[11] = { 0 };
+	int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
+
+	if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
+		fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno));
+		ret = MIN_SEED_LEN;
+	} else
+		ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
+	if (fd >= 0)
+		close(fd);
+	if (ret < MIN_SEED_LEN)
+		ret = MIN_SEED_LEN;
+	else if (ret > MAX_SEED_LEN)
+		ret = MAX_SEED_LEN;
+	return ret;
+}
+
+static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable)
+{
+	ssize_t ret;
+	int urandom_fd;
+
+	*is_creditable = false;
+	ret = getrandom(seed, len, GRND_NONBLOCK);
+	if (ret == (ssize_t)len) {
+		*is_creditable = true;
+		return 0;
+	} else if (ret < 0 && errno == ENOSYS) {
+		struct pollfd random_fd = {
+			.fd = open("/dev/random", O_RDONLY),
+			.events = POLLIN
+		};
+		if (random_fd.fd < 0)
+			return -errno;
+		*is_creditable = poll(&random_fd, 1, 0) == 1;
+		close(random_fd.fd);
+	} else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len)
+		return 0;
+	urandom_fd = open("/dev/urandom", O_RDONLY);
+	if (urandom_fd < 0)
+		return -errno;
+	ret = read(urandom_fd, seed, len);
+	if (ret == (ssize_t)len)
+		ret = 0;
+	else
+		ret = -errno ? -errno : -EIO;
+	close(urandom_fd);
+	return ret;
+}
+
+static int seed_rng(uint8_t *seed, size_t len, bool credit)
+{
+	struct {
+		int entropy_count;
+		int buf_size;
+		uint8_t buffer[MAX_SEED_LEN];
+	} req = {
+		.entropy_count = credit ? len * 8 : 0,
+		.buf_size = len
+	};
+	int random_fd, ret;
+
+	if (len > sizeof(req.buffer))
+		return -EFBIG;
+	memcpy(req.buffer, seed, len);
+
+	random_fd = open("/dev/random", O_RDWR);
+	if (random_fd < 0)
+		return -errno;
+	ret = ioctl(random_fd, RNDADDENTROPY, &req);
+	if (ret)
+		ret = -errno ? -errno : -EIO;
+	close(random_fd);
+	return ret;
+}
+
+static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash)
+{
+	uint8_t seed[MAX_SEED_LEN];
+	ssize_t seed_len;
+	int fd, dfd, ret = 0;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0 && errno == ENOENT)
+		return 0;
+	else if (fd < 0) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno));
+		return ret;
+	}
+	dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
+	if (dfd < 0) {
+		ret = -errno;
+		close(fd);
+		fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno));
+		return ret;
+	}
+	seed_len = read(fd, seed, sizeof(seed));
+	if (seed_len < 0) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno));
+	}
+	close(fd);
+	if (ret) {
+		close(dfd);
+		return ret;
+	}
+	if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno));
+	}
+	close(dfd);
+	if (ret)
+		return ret;
+	if (!seed_len)
+		return 0;
+
+	blake2s_update(hash, &seed_len, sizeof(seed_len));
+	blake2s_update(hash, seed, seed_len);
+
+	fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
+	ret = seed_rng(seed, seed_len, credit);
+	if (ret < 0)
+		fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret));
+	return ret;
+}
+
+static bool skip_credit(void)
+{
+	const char *skip = getenv("SEEDRNG_SKIP_CREDIT");
+	return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
+			!strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
+}
+
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+	static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix";
+	static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
+	int ret, fd, lock, program_ret = 0;
+	uint8_t new_seed[MAX_SEED_LEN];
+	size_t new_seed_len;
+	bool new_seed_creditable;
+	struct timespec realtime = { 0 }, boottime = { 0 };
+	struct blake2s_state hash;
+
+	umask(0077);
+	if (getuid()) {
+		fprintf(stderr, "ERROR: This program requires root\n");
+		return 1;
+	}
+
+	blake2s_init(&hash, BLAKE2S_HASH_LEN);
+	blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
+	clock_gettime(CLOCK_REALTIME, &realtime);
+	clock_gettime(CLOCK_BOOTTIME, &boottime);
+	blake2s_update(&hash, &realtime, sizeof(realtime));
+	blake2s_update(&hash, &boottime, sizeof(boottime));
+
+	if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
+		fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR, strerror(errno));
+		return 1;
+	}
+
+	lock = open(LOCK_FILE, O_WRONLY | O_CREAT, 0000);
+	if (lock < 0 || flock(lock, LOCK_EX) < 0) {
+		fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno));
+		return 1;
+	}
+
+	ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash);
+	if (ret < 0)
+		program_ret |= 1 << 1;
+	ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash);
+	if (ret < 0)
+		program_ret |= 1 << 2;
+
+	new_seed_len = determine_optimal_seed_len();
+	ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable);
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret));
+		new_seed_len = BLAKE2S_HASH_LEN;
+		strncpy((char *)new_seed, seedrng_failure, new_seed_len);
+		program_ret |= 1 << 3;
+	}
+	blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
+	blake2s_update(&hash, new_seed, new_seed_len);
+	blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
+
+	fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
+	fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno));
+		program_ret |= 1 << 4;
+		goto out;
+	}
+	if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
+		fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno));
+		program_ret |= 1 << 5;
+		goto out;
+	}
+	if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) {
+		fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno));
+		program_ret |= 1 << 6;
+	}
+out:
+	close(fd);
+	close(lock);
+	return program_ret;
+}
diff --git a/package/urandom-scripts/urandom-scripts.mk b/package/urandom-scripts/urandom-scripts.mk
index 2c09728c46..a5cbb95feb 100644
--- a/package/urandom-scripts/urandom-scripts.mk
+++ b/package/urandom-scripts/urandom-scripts.mk
@@ -4,7 +4,17 @@
 #
 ################################################################################
 
+define URANDOM_SCRIPTS_EXTRACT_CMDS
+	cp $(URANDOM_SCRIPTS_PKGDIR)/seedrng.c $(@D)
+endef
+
+define URANDOM_SCRIPTS_BUILD_CMDS
+	$(TARGET_CC) $(TARGET_CFLAGS) -std=gnu99 $(TARGET_LDFLAGS) \
+		$(@D)/seedrng.c -o $(@D)/seedrng
+endef
+
 define URANDOM_SCRIPTS_INSTALL_INIT_SYSV
+	$(INSTALL) -D -m 0755 $(@D)/seedrng $(TARGET_DIR)/sbin/seedrng
 	$(INSTALL) -D -m 0755 $(URANDOM_SCRIPTS_PKGDIR)/S20urandom \
 		$(TARGET_DIR)/etc/init.d/S20urandom
 endef
-- 
2.35.1

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27  5:48 [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng Jason A. Donenfeld
@ 2022-03-27 18:01 ` James Hilliard
  2022-03-27 19:36   ` Arnout Vandecappelle
  0 siblings, 1 reply; 34+ messages in thread
From: James Hilliard @ 2022-03-27 18:01 UTC (permalink / raw)
  To: Jason A. Donenfeld; +Cc: Yann E. MORIN, buildroot

On Sat, Mar 26, 2022 at 11:48 PM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
>
> The RNG can't actually be seeded from a shell script, due to the
> reliance on ioctls. For this reason, the seedrng project provides a
> basic script meant to be copy and pasted into projects like buildroot
> and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
>
> This commit imports it into buildroot and wires up the init scripts to
> call it. This also is a significant improvement over the current init
> script, which doesn't credit entropy and whose hashing in shell scripts
> is sort of fragile.
>
> As seedrng.c is a short tiny C program, we include this here in the
> package, like a few other packages do. Later we'll investigate adding
> this to busybox, but for now, this is a good start and a positive step
> in the right direction.
>
> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
> ---
>  package/urandom-scripts/Config.in          |   4 -
>  package/urandom-scripts/S20urandom         |  55 +--
>  package/urandom-scripts/seedrng.c          | 445 +++++++++++++++++++++
>  package/urandom-scripts/urandom-scripts.mk |  10 +
>  4 files changed, 464 insertions(+), 50 deletions(-)
>  create mode 100644 package/urandom-scripts/seedrng.c
>
> diff --git a/package/urandom-scripts/Config.in b/package/urandom-scripts/Config.in
> index 987e442e22..070ffa5e9a 100644
> --- a/package/urandom-scripts/Config.in
> +++ b/package/urandom-scripts/Config.in
> @@ -4,7 +4,3 @@ config BR2_PACKAGE_URANDOM_SCRIPTS
>         depends on !BR2_PACKAGE_SYSTEMD
>         help
>           Initscript to preserve the random seed between reboots.
> -
> -         WARNING: this is a poor fit to try and get high-quality
> -         entropy at boot. There are better ways, like haveged, or
> -         rng-tools.
> diff --git a/package/urandom-scripts/S20urandom b/package/urandom-scripts/S20urandom
> index c6b2ebd48f..f248089a0f 100644
> --- a/package/urandom-scripts/S20urandom
> +++ b/package/urandom-scripts/S20urandom
> @@ -6,63 +6,26 @@
>  # Quietly do nothing if /dev/urandom does not exist
>  [ -c /dev/urandom ] || exit 0
>
> -URANDOM_SEED="/var/lib/random-seed"
> +# Set this to true only if you do not want seed files to actually credit the
> +# RNG, for example if you plan to replicate this file system image and do not
> +# have the wherewithal to first delete the contents of /var/lib/seedrng.
> +#export SEEDRNG_SKIP_CREDIT=false
> +# You can also place this line into /etc/default/urandom.
>
>  # shellcheck source=/dev/null
>  [ -r "/etc/default/urandom" ] && . "/etc/default/urandom"
>
> -if pool_bits=$(cat /proc/sys/kernel/random/poolsize 2> /dev/null); then
> -       pool_size=$((pool_bits/8))
> -else
> -       pool_size=512
> -fi
> -
> -init_rng() {
> -       printf 'Initializing random number generator: '
> -       dd if="$URANDOM_SEED" bs="$pool_size" of=/dev/urandom count=1 2> /dev/null
> -       status=$?
> -       if [ "$status" -eq 0 ]; then
> -               echo "OK"
> -       else
> -               echo "FAIL"
> -       fi
> -       return "$status"
> -}
> -
> -save_random_seed() {
> -       printf 'Saving random seed: '
> -       status=1
> -       if touch "$URANDOM_SEED.new" 2> /dev/null; then
> -               old_umask=$(umask)
> -               umask 077
> -               dd if=/dev/urandom of="$URANDOM_SEED.tmp" bs="$pool_size" count=1 2> /dev/null
> -               cat "$URANDOM_SEED" "$URANDOM_SEED.tmp" 2>/dev/null \
> -                       | sha256sum \
> -                       | cut -d ' ' -f 1 > "$URANDOM_SEED.new" && \
> -               mv "$URANDOM_SEED.new" "$URANDOM_SEED" && status=0
> -               rm -f "$URANDOM_SEED.tmp"
> -               umask "$old_umask"
> -               if [ "$status" -eq 0 ]; then
> -                       echo "OK"
> -               else
> -                       echo "FAIL"
> -               fi
> -
> -       else
> -               echo "SKIP (read-only file system detected)"
> -       fi
> -       return "$status"
> -}
> -
>  case "$1" in
>         start|restart|reload)
>                 # Carry a random seed from start-up to start-up
>                 # Load and then save the whole entropy pool
> -               init_rng && save_random_seed;;
> +               # Never fail, as this isn't worth making a fuss
> +               # over if it doesn't go as planned.
> +               seedrng || true;;
>         stop)
>                 # Carry a random seed from shut-down to start-up
>                 # Save the whole entropy pool
> -               save_random_seed;;
> +               seedrng;;
>         *)
>                 echo "Usage: $0 {start|stop|restart|reload}"
>                 exit 1
> diff --git a/package/urandom-scripts/seedrng.c b/package/urandom-scripts/seedrng.c
> new file mode 100644
> index 0000000000..a77365d406
> --- /dev/null
> +++ b/package/urandom-scripts/seedrng.c
> @@ -0,0 +1,445 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT OR Apache-2.0)
> +/*
> + * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
> + *
> + * This is based on code from <https://git.zx2c4.com/seedrng/about/>.
> + */
> +
> +#define _GNU_SOURCE
> +#include <linux/random.h>
> +#include <sys/random.h>
> +#include <sys/ioctl.h>
> +#include <sys/file.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <fcntl.h>
> +#include <poll.h>
> +#include <unistd.h>
> +#include <time.h>
> +#include <errno.h>
> +#include <endian.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +#ifndef GRND_INSECURE
> +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */
> +#endif
> +
> +#ifndef LOCALSTATEDIR
> +#define LOCALSTATEDIR "/var/lib"
> +#endif
> +#ifndef RUNSTATEDIR
> +#define RUNSTATEDIR "/var/run"
> +#endif

Might be a good idea to have runtime env overrides for these to allow easier
path customization by only having to override the init script in a
rootfs overlay:
https://buildroot.org/downloads/manual/manual.html#rootfs-custom

Maybe something similar to the SEEDRNG_SKIP_CREDIT env override would
make sense for these paths.

> +
> +#define SEED_DIR LOCALSTATEDIR "/seedrng"
> +#define CREDITABLE_SEED SEED_DIR "/seed.credit"
> +#define NON_CREDITABLE_SEED SEED_DIR "/seed.no-credit"
> +#define LOCK_FILE RUNSTATEDIR "/seedrng.lock"
> +
> +enum blake2s_lengths {
> +       BLAKE2S_BLOCK_LEN = 64,
> +       BLAKE2S_HASH_LEN = 32,
> +       BLAKE2S_KEY_LEN = 32
> +};
> +
> +enum seedrng_lengths {
> +       MAX_SEED_LEN = 512,
> +       MIN_SEED_LEN = BLAKE2S_HASH_LEN
> +};
> +
> +struct blake2s_state {
> +       uint32_t h[8];
> +       uint32_t t[2];
> +       uint32_t f[2];
> +       uint8_t buf[BLAKE2S_BLOCK_LEN];
> +       unsigned int buflen;
> +       unsigned int outlen;
> +};
> +
> +#define le32_to_cpup(a) le32toh(*(a))
> +#define cpu_to_le32(a) htole32(a)
> +#ifndef ARRAY_SIZE
> +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
> +#endif
> +#ifndef DIV_ROUND_UP
> +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
> +#endif
> +
> +static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
> +{
> +        while (words--) {
> +               *buf = cpu_to_le32(*buf);
> +               ++buf;
> +       }
> +}
> +
> +static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
> +{
> +        while (words--) {
> +               *buf = le32_to_cpup(buf);
> +               ++buf;
> +        }
> +}
> +
> +static inline uint32_t ror32(uint32_t word, unsigned int shift)
> +{
> +       return (word >> (shift & 31)) | (word << ((-shift) & 31));
> +}
> +
> +static const uint32_t blake2s_iv[8] = {
> +       0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
> +       0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
> +};
> +
> +static const uint8_t blake2s_sigma[10][16] = {
> +       { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
> +       { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
> +       { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
> +       { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
> +       { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
> +       { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
> +       { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
> +       { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
> +       { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
> +       { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
> +};
> +
> +static void blake2s_set_lastblock(struct blake2s_state *state)
> +{
> +       state->f[0] = -1;
> +}
> +
> +static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc)
> +{
> +       state->t[0] += inc;
> +       state->t[1] += (state->t[0] < inc);
> +}
> +
> +static void blake2s_init_param(struct blake2s_state *state, const uint32_t param)
> +{
> +       int i;
> +
> +       memset(state, 0, sizeof(*state));
> +       for (i = 0; i < 8; ++i)
> +               state->h[i] = blake2s_iv[i];
> +       state->h[0] ^= param;
> +}
> +
> +static void blake2s_init(struct blake2s_state *state, const size_t outlen)
> +{
> +       blake2s_init_param(state, 0x01010000 | outlen);
> +       state->outlen = outlen;
> +}
> +
> +static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc)
> +{
> +       uint32_t m[16];
> +       uint32_t v[16];
> +       int i;
> +
> +       while (nblocks > 0) {
> +               blake2s_increment_counter(state, inc);
> +               memcpy(m, block, BLAKE2S_BLOCK_LEN);
> +               le32_to_cpu_array(m, ARRAY_SIZE(m));
> +               memcpy(v, state->h, 32);
> +               v[ 8] = blake2s_iv[0];
> +               v[ 9] = blake2s_iv[1];
> +               v[10] = blake2s_iv[2];
> +               v[11] = blake2s_iv[3];
> +               v[12] = blake2s_iv[4] ^ state->t[0];
> +               v[13] = blake2s_iv[5] ^ state->t[1];
> +               v[14] = blake2s_iv[6] ^ state->f[0];
> +               v[15] = blake2s_iv[7] ^ state->f[1];
> +
> +#define G(r, i, a, b, c, d) do { \
> +       a += b + m[blake2s_sigma[r][2 * i + 0]]; \
> +       d = ror32(d ^ a, 16); \
> +       c += d; \
> +       b = ror32(b ^ c, 12); \
> +       a += b + m[blake2s_sigma[r][2 * i + 1]]; \
> +       d = ror32(d ^ a, 8); \
> +       c += d; \
> +       b = ror32(b ^ c, 7); \
> +} while (0)
> +
> +#define ROUND(r) do { \
> +       G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
> +       G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
> +       G(r, 2, v[2], v[ 6], v[10], v[14]); \
> +       G(r, 3, v[3], v[ 7], v[11], v[15]); \
> +       G(r, 4, v[0], v[ 5], v[10], v[15]); \
> +       G(r, 5, v[1], v[ 6], v[11], v[12]); \
> +       G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
> +       G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
> +} while (0)
> +               ROUND(0);
> +               ROUND(1);
> +               ROUND(2);
> +               ROUND(3);
> +               ROUND(4);
> +               ROUND(5);
> +               ROUND(6);
> +               ROUND(7);
> +               ROUND(8);
> +               ROUND(9);
> +
> +#undef G
> +#undef ROUND
> +
> +               for (i = 0; i < 8; ++i)
> +                       state->h[i] ^= v[i] ^ v[i + 8];
> +
> +               block += BLAKE2S_BLOCK_LEN;
> +               --nblocks;
> +       }
> +}
> +
> +static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen)
> +{
> +       const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen;
> +       const uint8_t *in = inp;
> +
> +       if (!inlen)
> +               return;
> +       if (inlen > fill) {
> +               memcpy(state->buf + state->buflen, in, fill);
> +               blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
> +               state->buflen = 0;
> +               in += fill;
> +               inlen -= fill;
> +       }
> +       if (inlen > BLAKE2S_BLOCK_LEN) {
> +               const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
> +               blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
> +               in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
> +               inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
> +       }
> +       memcpy(state->buf + state->buflen, in, inlen);
> +       state->buflen += inlen;
> +}
> +
> +static void blake2s_final(struct blake2s_state *state, uint8_t *out)
> +{
> +       blake2s_set_lastblock(state);
> +       memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
> +       blake2s_compress(state, state->buf, 1, state->buflen);
> +       cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
> +       memcpy(out, state->h, state->outlen);
> +}
> +
> +static size_t determine_optimal_seed_len(void)
> +{
> +       size_t ret = 0;
> +       char poolsize_str[11] = { 0 };
> +       int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
> +
> +       if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
> +               fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno));
> +               ret = MIN_SEED_LEN;
> +       } else
> +               ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
> +       if (fd >= 0)
> +               close(fd);
> +       if (ret < MIN_SEED_LEN)
> +               ret = MIN_SEED_LEN;
> +       else if (ret > MAX_SEED_LEN)
> +               ret = MAX_SEED_LEN;
> +       return ret;
> +}
> +
> +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable)
> +{
> +       ssize_t ret;
> +       int urandom_fd;
> +
> +       *is_creditable = false;
> +       ret = getrandom(seed, len, GRND_NONBLOCK);
> +       if (ret == (ssize_t)len) {
> +               *is_creditable = true;
> +               return 0;
> +       } else if (ret < 0 && errno == ENOSYS) {
> +               struct pollfd random_fd = {
> +                       .fd = open("/dev/random", O_RDONLY),
> +                       .events = POLLIN
> +               };
> +               if (random_fd.fd < 0)
> +                       return -errno;
> +               *is_creditable = poll(&random_fd, 1, 0) == 1;
> +               close(random_fd.fd);
> +       } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len)
> +               return 0;
> +       urandom_fd = open("/dev/urandom", O_RDONLY);
> +       if (urandom_fd < 0)
> +               return -errno;
> +       ret = read(urandom_fd, seed, len);
> +       if (ret == (ssize_t)len)
> +               ret = 0;
> +       else
> +               ret = -errno ? -errno : -EIO;
> +       close(urandom_fd);
> +       return ret;
> +}
> +
> +static int seed_rng(uint8_t *seed, size_t len, bool credit)
> +{
> +       struct {
> +               int entropy_count;
> +               int buf_size;
> +               uint8_t buffer[MAX_SEED_LEN];
> +       } req = {
> +               .entropy_count = credit ? len * 8 : 0,
> +               .buf_size = len
> +       };
> +       int random_fd, ret;
> +
> +       if (len > sizeof(req.buffer))
> +               return -EFBIG;
> +       memcpy(req.buffer, seed, len);
> +
> +       random_fd = open("/dev/random", O_RDWR);
> +       if (random_fd < 0)
> +               return -errno;
> +       ret = ioctl(random_fd, RNDADDENTROPY, &req);
> +       if (ret)
> +               ret = -errno ? -errno : -EIO;
> +       close(random_fd);
> +       return ret;
> +}
> +
> +static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash)
> +{
> +       uint8_t seed[MAX_SEED_LEN];
> +       ssize_t seed_len;
> +       int fd, dfd, ret = 0;
> +
> +       fd = open(filename, O_RDONLY);
> +       if (fd < 0 && errno == ENOENT)
> +               return 0;
> +       else if (fd < 0) {
> +               ret = -errno;
> +               fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno));
> +               return ret;
> +       }
> +       dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
> +       if (dfd < 0) {
> +               ret = -errno;
> +               close(fd);
> +               fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno));
> +               return ret;
> +       }
> +       seed_len = read(fd, seed, sizeof(seed));
> +       if (seed_len < 0) {
> +               ret = -errno;
> +               fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno));
> +       }
> +       close(fd);
> +       if (ret) {
> +               close(dfd);
> +               return ret;
> +       }
> +       if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) {
> +               ret = -errno;
> +               fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno));
> +       }
> +       close(dfd);
> +       if (ret)
> +               return ret;
> +       if (!seed_len)
> +               return 0;
> +
> +       blake2s_update(hash, &seed_len, sizeof(seed_len));
> +       blake2s_update(hash, seed, seed_len);
> +
> +       fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
> +       ret = seed_rng(seed, seed_len, credit);
> +       if (ret < 0)
> +               fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret));
> +       return ret;
> +}
> +
> +static bool skip_credit(void)
> +{
> +       const char *skip = getenv("SEEDRNG_SKIP_CREDIT");
> +       return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
> +                       !strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
> +}
> +
> +int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
> +{
> +       static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix";
> +       static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
> +       int ret, fd, lock, program_ret = 0;
> +       uint8_t new_seed[MAX_SEED_LEN];
> +       size_t new_seed_len;
> +       bool new_seed_creditable;
> +       struct timespec realtime = { 0 }, boottime = { 0 };
> +       struct blake2s_state hash;
> +
> +       umask(0077);
> +       if (getuid()) {
> +               fprintf(stderr, "ERROR: This program requires root\n");
> +               return 1;
> +       }
> +
> +       blake2s_init(&hash, BLAKE2S_HASH_LEN);
> +       blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
> +       clock_gettime(CLOCK_REALTIME, &realtime);
> +       clock_gettime(CLOCK_BOOTTIME, &boottime);
> +       blake2s_update(&hash, &realtime, sizeof(realtime));
> +       blake2s_update(&hash, &boottime, sizeof(boottime));
> +
> +       if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
> +               fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR, strerror(errno));
> +               return 1;
> +       }
> +
> +       lock = open(LOCK_FILE, O_WRONLY | O_CREAT, 0000);
> +       if (lock < 0 || flock(lock, LOCK_EX) < 0) {
> +               fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno));
> +               return 1;
> +       }
> +
> +       ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash);
> +       if (ret < 0)
> +               program_ret |= 1 << 1;
> +       ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash);
> +       if (ret < 0)
> +               program_ret |= 1 << 2;
> +
> +       new_seed_len = determine_optimal_seed_len();
> +       ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable);
> +       if (ret < 0) {
> +               fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret));
> +               new_seed_len = BLAKE2S_HASH_LEN;
> +               strncpy((char *)new_seed, seedrng_failure, new_seed_len);
> +               program_ret |= 1 << 3;
> +       }
> +       blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
> +       blake2s_update(&hash, new_seed, new_seed_len);
> +       blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
> +
> +       fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
> +       fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
> +       if (fd < 0) {
> +               fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno));
> +               program_ret |= 1 << 4;
> +               goto out;
> +       }
> +       if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
> +               fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno));
> +               program_ret |= 1 << 5;
> +               goto out;
> +       }
> +       if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) {
> +               fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno));
> +               program_ret |= 1 << 6;
> +       }
> +out:
> +       close(fd);
> +       close(lock);
> +       return program_ret;
> +}
> diff --git a/package/urandom-scripts/urandom-scripts.mk b/package/urandom-scripts/urandom-scripts.mk
> index 2c09728c46..a5cbb95feb 100644
> --- a/package/urandom-scripts/urandom-scripts.mk
> +++ b/package/urandom-scripts/urandom-scripts.mk
> @@ -4,7 +4,17 @@
>  #
>  ################################################################################
>
> +define URANDOM_SCRIPTS_EXTRACT_CMDS
> +       cp $(URANDOM_SCRIPTS_PKGDIR)/seedrng.c $(@D)
> +endef
> +
> +define URANDOM_SCRIPTS_BUILD_CMDS
> +       $(TARGET_CC) $(TARGET_CFLAGS) -std=gnu99 $(TARGET_LDFLAGS) \
> +               $(@D)/seedrng.c -o $(@D)/seedrng
> +endef
> +
>  define URANDOM_SCRIPTS_INSTALL_INIT_SYSV
> +       $(INSTALL) -D -m 0755 $(@D)/seedrng $(TARGET_DIR)/sbin/seedrng
>         $(INSTALL) -D -m 0755 $(URANDOM_SCRIPTS_PKGDIR)/S20urandom \
>                 $(TARGET_DIR)/etc/init.d/S20urandom
>  endef
> --
> 2.35.1
>
> _______________________________________________
> buildroot mailing list
> buildroot@buildroot.org
> https://lists.buildroot.org/mailman/listinfo/buildroot
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27 18:01 ` James Hilliard
@ 2022-03-27 19:36   ` Arnout Vandecappelle
  2022-03-27 19:58     ` James Hilliard
  0 siblings, 1 reply; 34+ messages in thread
From: Arnout Vandecappelle @ 2022-03-27 19:36 UTC (permalink / raw)
  To: James Hilliard, Jason A. Donenfeld; +Cc: Yann E. MORIN, buildroot



On 27/03/2022 20:01, James Hilliard wrote:
> On Sat, Mar 26, 2022 at 11:48 PM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
>>
>> The RNG can't actually be seeded from a shell script, due to the
>> reliance on ioctls. For this reason, the seedrng project provides a
>> basic script meant to be copy and pasted into projects like buildroot
>> and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
>>
>> This commit imports it into buildroot and wires up the init scripts to
>> call it. This also is a significant improvement over the current init
>> script, which doesn't credit entropy and whose hashing in shell scripts
>> is sort of fragile.
>>
>> As seedrng.c is a short tiny C program, we include this here in the
>> package, like a few other packages do. Later we'll investigate adding
>> this to busybox, but for now, this is a good start and a positive step
>> in the right direction.
>>
>> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
[snip]
>> +#ifndef GRND_INSECURE
>> +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */
>> +#endif
>> +
>> +#ifndef LOCALSTATEDIR
>> +#define LOCALSTATEDIR "/var/lib"
>> +#endif
>> +#ifndef RUNSTATEDIR
>> +#define RUNSTATEDIR "/var/run"
>> +#endif
> 
> Might be a good idea to have runtime env overrides for these to allow easier
> path customization by only having to override the init script in a
> rootfs overlay:
> https://buildroot.org/downloads/manual/manual.html#rootfs-custom
> 
> Maybe something similar to the SEEDRNG_SKIP_CREDIT env override would
> make sense for these paths.

  I don't know if that makes sense. Dozens of buildroot packages hardocde these 
paths already.

  Regards,
  Arnout

> 
>> +
>> +#define SEED_DIR LOCALSTATEDIR "/seedrng"
>> +#define CREDITABLE_SEED SEED_DIR "/seed.credit"
>> +#define NON_CREDITABLE_SEED SEED_DIR "/seed.no-credit"
>> +#define LOCK_FILE RUNSTATEDIR "/seedrng.lock"
>> +
>> +enum blake2s_lengths {
>> +       BLAKE2S_BLOCK_LEN = 64,
>> +       BLAKE2S_HASH_LEN = 32,
>> +       BLAKE2S_KEY_LEN = 32
>> +};
>> +
>> +enum seedrng_lengths {
>> +       MAX_SEED_LEN = 512,
>> +       MIN_SEED_LEN = BLAKE2S_HASH_LEN
>> +};
>> +
>> +struct blake2s_state {
>> +       uint32_t h[8];
>> +       uint32_t t[2];
>> +       uint32_t f[2];
>> +       uint8_t buf[BLAKE2S_BLOCK_LEN];
>> +       unsigned int buflen;
>> +       unsigned int outlen;
>> +};
>> +
>> +#define le32_to_cpup(a) le32toh(*(a))
>> +#define cpu_to_le32(a) htole32(a)
>> +#ifndef ARRAY_SIZE
>> +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
>> +#endif
>> +#ifndef DIV_ROUND_UP
>> +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
>> +#endif
>> +
>> +static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
>> +{
>> +        while (words--) {
>> +               *buf = cpu_to_le32(*buf);
>> +               ++buf;
>> +       }
>> +}
>> +
>> +static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
>> +{
>> +        while (words--) {
>> +               *buf = le32_to_cpup(buf);
>> +               ++buf;
>> +        }
>> +}
>> +
>> +static inline uint32_t ror32(uint32_t word, unsigned int shift)
>> +{
>> +       return (word >> (shift & 31)) | (word << ((-shift) & 31));
>> +}
>> +
>> +static const uint32_t blake2s_iv[8] = {
>> +       0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
>> +       0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
>> +};
>> +
>> +static const uint8_t blake2s_sigma[10][16] = {
>> +       { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
>> +       { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
>> +       { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
>> +       { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
>> +       { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
>> +       { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
>> +       { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
>> +       { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
>> +       { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
>> +       { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
>> +};
>> +
>> +static void blake2s_set_lastblock(struct blake2s_state *state)
>> +{
>> +       state->f[0] = -1;
>> +}
>> +
>> +static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc)
>> +{
>> +       state->t[0] += inc;
>> +       state->t[1] += (state->t[0] < inc);
>> +}
>> +
>> +static void blake2s_init_param(struct blake2s_state *state, const uint32_t param)
>> +{
>> +       int i;
>> +
>> +       memset(state, 0, sizeof(*state));
>> +       for (i = 0; i < 8; ++i)
>> +               state->h[i] = blake2s_iv[i];
>> +       state->h[0] ^= param;
>> +}
>> +
>> +static void blake2s_init(struct blake2s_state *state, const size_t outlen)
>> +{
>> +       blake2s_init_param(state, 0x01010000 | outlen);
>> +       state->outlen = outlen;
>> +}
>> +
>> +static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc)
>> +{
>> +       uint32_t m[16];
>> +       uint32_t v[16];
>> +       int i;
>> +
>> +       while (nblocks > 0) {
>> +               blake2s_increment_counter(state, inc);
>> +               memcpy(m, block, BLAKE2S_BLOCK_LEN);
>> +               le32_to_cpu_array(m, ARRAY_SIZE(m));
>> +               memcpy(v, state->h, 32);
>> +               v[ 8] = blake2s_iv[0];
>> +               v[ 9] = blake2s_iv[1];
>> +               v[10] = blake2s_iv[2];
>> +               v[11] = blake2s_iv[3];
>> +               v[12] = blake2s_iv[4] ^ state->t[0];
>> +               v[13] = blake2s_iv[5] ^ state->t[1];
>> +               v[14] = blake2s_iv[6] ^ state->f[0];
>> +               v[15] = blake2s_iv[7] ^ state->f[1];
>> +
>> +#define G(r, i, a, b, c, d) do { \
>> +       a += b + m[blake2s_sigma[r][2 * i + 0]]; \
>> +       d = ror32(d ^ a, 16); \
>> +       c += d; \
>> +       b = ror32(b ^ c, 12); \
>> +       a += b + m[blake2s_sigma[r][2 * i + 1]]; \
>> +       d = ror32(d ^ a, 8); \
>> +       c += d; \
>> +       b = ror32(b ^ c, 7); \
>> +} while (0)
>> +
>> +#define ROUND(r) do { \
>> +       G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
>> +       G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
>> +       G(r, 2, v[2], v[ 6], v[10], v[14]); \
>> +       G(r, 3, v[3], v[ 7], v[11], v[15]); \
>> +       G(r, 4, v[0], v[ 5], v[10], v[15]); \
>> +       G(r, 5, v[1], v[ 6], v[11], v[12]); \
>> +       G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
>> +       G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
>> +} while (0)
>> +               ROUND(0);
>> +               ROUND(1);
>> +               ROUND(2);
>> +               ROUND(3);
>> +               ROUND(4);
>> +               ROUND(5);
>> +               ROUND(6);
>> +               ROUND(7);
>> +               ROUND(8);
>> +               ROUND(9);
>> +
>> +#undef G
>> +#undef ROUND
>> +
>> +               for (i = 0; i < 8; ++i)
>> +                       state->h[i] ^= v[i] ^ v[i + 8];
>> +
>> +               block += BLAKE2S_BLOCK_LEN;
>> +               --nblocks;
>> +       }
>> +}
>> +
>> +static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen)
>> +{
>> +       const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen;
>> +       const uint8_t *in = inp;
>> +
>> +       if (!inlen)
>> +               return;
>> +       if (inlen > fill) {
>> +               memcpy(state->buf + state->buflen, in, fill);
>> +               blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
>> +               state->buflen = 0;
>> +               in += fill;
>> +               inlen -= fill;
>> +       }
>> +       if (inlen > BLAKE2S_BLOCK_LEN) {
>> +               const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
>> +               blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
>> +               in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
>> +               inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
>> +       }
>> +       memcpy(state->buf + state->buflen, in, inlen);
>> +       state->buflen += inlen;
>> +}
>> +
>> +static void blake2s_final(struct blake2s_state *state, uint8_t *out)
>> +{
>> +       blake2s_set_lastblock(state);
>> +       memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
>> +       blake2s_compress(state, state->buf, 1, state->buflen);
>> +       cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
>> +       memcpy(out, state->h, state->outlen);
>> +}
>> +
>> +static size_t determine_optimal_seed_len(void)
>> +{
>> +       size_t ret = 0;
>> +       char poolsize_str[11] = { 0 };
>> +       int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
>> +
>> +       if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
>> +               fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno));
>> +               ret = MIN_SEED_LEN;
>> +       } else
>> +               ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
>> +       if (fd >= 0)
>> +               close(fd);
>> +       if (ret < MIN_SEED_LEN)
>> +               ret = MIN_SEED_LEN;
>> +       else if (ret > MAX_SEED_LEN)
>> +               ret = MAX_SEED_LEN;
>> +       return ret;
>> +}
>> +
>> +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable)
>> +{
>> +       ssize_t ret;
>> +       int urandom_fd;
>> +
>> +       *is_creditable = false;
>> +       ret = getrandom(seed, len, GRND_NONBLOCK);
>> +       if (ret == (ssize_t)len) {
>> +               *is_creditable = true;
>> +               return 0;
>> +       } else if (ret < 0 && errno == ENOSYS) {
>> +               struct pollfd random_fd = {
>> +                       .fd = open("/dev/random", O_RDONLY),
>> +                       .events = POLLIN
>> +               };
>> +               if (random_fd.fd < 0)
>> +                       return -errno;
>> +               *is_creditable = poll(&random_fd, 1, 0) == 1;
>> +               close(random_fd.fd);
>> +       } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len)
>> +               return 0;
>> +       urandom_fd = open("/dev/urandom", O_RDONLY);
>> +       if (urandom_fd < 0)
>> +               return -errno;
>> +       ret = read(urandom_fd, seed, len);
>> +       if (ret == (ssize_t)len)
>> +               ret = 0;
>> +       else
>> +               ret = -errno ? -errno : -EIO;
>> +       close(urandom_fd);
>> +       return ret;
>> +}
>> +
>> +static int seed_rng(uint8_t *seed, size_t len, bool credit)
>> +{
>> +       struct {
>> +               int entropy_count;
>> +               int buf_size;
>> +               uint8_t buffer[MAX_SEED_LEN];
>> +       } req = {
>> +               .entropy_count = credit ? len * 8 : 0,
>> +               .buf_size = len
>> +       };
>> +       int random_fd, ret;
>> +
>> +       if (len > sizeof(req.buffer))
>> +               return -EFBIG;
>> +       memcpy(req.buffer, seed, len);
>> +
>> +       random_fd = open("/dev/random", O_RDWR);
>> +       if (random_fd < 0)
>> +               return -errno;
>> +       ret = ioctl(random_fd, RNDADDENTROPY, &req);
>> +       if (ret)
>> +               ret = -errno ? -errno : -EIO;
>> +       close(random_fd);
>> +       return ret;
>> +}
>> +
>> +static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash)
>> +{
>> +       uint8_t seed[MAX_SEED_LEN];
>> +       ssize_t seed_len;
>> +       int fd, dfd, ret = 0;
>> +
>> +       fd = open(filename, O_RDONLY);
>> +       if (fd < 0 && errno == ENOENT)
>> +               return 0;
>> +       else if (fd < 0) {
>> +               ret = -errno;
>> +               fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno));
>> +               return ret;
>> +       }
>> +       dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
>> +       if (dfd < 0) {
>> +               ret = -errno;
>> +               close(fd);
>> +               fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno));
>> +               return ret;
>> +       }
>> +       seed_len = read(fd, seed, sizeof(seed));
>> +       if (seed_len < 0) {
>> +               ret = -errno;
>> +               fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno));
>> +       }
>> +       close(fd);
>> +       if (ret) {
>> +               close(dfd);
>> +               return ret;
>> +       }
>> +       if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) {
>> +               ret = -errno;
>> +               fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno));
>> +       }
>> +       close(dfd);
>> +       if (ret)
>> +               return ret;
>> +       if (!seed_len)
>> +               return 0;
>> +
>> +       blake2s_update(hash, &seed_len, sizeof(seed_len));
>> +       blake2s_update(hash, seed, seed_len);
>> +
>> +       fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
>> +       ret = seed_rng(seed, seed_len, credit);
>> +       if (ret < 0)
>> +               fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret));
>> +       return ret;
>> +}
>> +
>> +static bool skip_credit(void)
>> +{
>> +       const char *skip = getenv("SEEDRNG_SKIP_CREDIT");
>> +       return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
>> +                       !strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
>> +}
>> +
>> +int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
>> +{
>> +       static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix";
>> +       static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
>> +       int ret, fd, lock, program_ret = 0;
>> +       uint8_t new_seed[MAX_SEED_LEN];
>> +       size_t new_seed_len;
>> +       bool new_seed_creditable;
>> +       struct timespec realtime = { 0 }, boottime = { 0 };
>> +       struct blake2s_state hash;
>> +
>> +       umask(0077);
>> +       if (getuid()) {
>> +               fprintf(stderr, "ERROR: This program requires root\n");
>> +               return 1;
>> +       }
>> +
>> +       blake2s_init(&hash, BLAKE2S_HASH_LEN);
>> +       blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
>> +       clock_gettime(CLOCK_REALTIME, &realtime);
>> +       clock_gettime(CLOCK_BOOTTIME, &boottime);
>> +       blake2s_update(&hash, &realtime, sizeof(realtime));
>> +       blake2s_update(&hash, &boottime, sizeof(boottime));
>> +
>> +       if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
>> +               fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR, strerror(errno));
>> +               return 1;
>> +       }
>> +
>> +       lock = open(LOCK_FILE, O_WRONLY | O_CREAT, 0000);
>> +       if (lock < 0 || flock(lock, LOCK_EX) < 0) {
>> +               fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno));
>> +               return 1;
>> +       }
>> +
>> +       ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash);
>> +       if (ret < 0)
>> +               program_ret |= 1 << 1;
>> +       ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash);
>> +       if (ret < 0)
>> +               program_ret |= 1 << 2;
>> +
>> +       new_seed_len = determine_optimal_seed_len();
>> +       ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable);
>> +       if (ret < 0) {
>> +               fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret));
>> +               new_seed_len = BLAKE2S_HASH_LEN;
>> +               strncpy((char *)new_seed, seedrng_failure, new_seed_len);
>> +               program_ret |= 1 << 3;
>> +       }
>> +       blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
>> +       blake2s_update(&hash, new_seed, new_seed_len);
>> +       blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
>> +
>> +       fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
>> +       fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
>> +       if (fd < 0) {
>> +               fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno));
>> +               program_ret |= 1 << 4;
>> +               goto out;
>> +       }
>> +       if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
>> +               fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno));
>> +               program_ret |= 1 << 5;
>> +               goto out;
>> +       }
>> +       if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) {
>> +               fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno));
>> +               program_ret |= 1 << 6;
>> +       }
>> +out:
>> +       close(fd);
>> +       close(lock);
>> +       return program_ret;
>> +}
>> diff --git a/package/urandom-scripts/urandom-scripts.mk b/package/urandom-scripts/urandom-scripts.mk
>> index 2c09728c46..a5cbb95feb 100644
>> --- a/package/urandom-scripts/urandom-scripts.mk
>> +++ b/package/urandom-scripts/urandom-scripts.mk
>> @@ -4,7 +4,17 @@
>>   #
>>   ################################################################################
>>
>> +define URANDOM_SCRIPTS_EXTRACT_CMDS
>> +       cp $(URANDOM_SCRIPTS_PKGDIR)/seedrng.c $(@D)
>> +endef
>> +
>> +define URANDOM_SCRIPTS_BUILD_CMDS
>> +       $(TARGET_CC) $(TARGET_CFLAGS) -std=gnu99 $(TARGET_LDFLAGS) \
>> +               $(@D)/seedrng.c -o $(@D)/seedrng
>> +endef
>> +
>>   define URANDOM_SCRIPTS_INSTALL_INIT_SYSV
>> +       $(INSTALL) -D -m 0755 $(@D)/seedrng $(TARGET_DIR)/sbin/seedrng
>>          $(INSTALL) -D -m 0755 $(URANDOM_SCRIPTS_PKGDIR)/S20urandom \
>>                  $(TARGET_DIR)/etc/init.d/S20urandom
>>   endef
>> --
>> 2.35.1
>>
>> _______________________________________________
>> buildroot mailing list
>> buildroot@buildroot.org
>> https://lists.buildroot.org/mailman/listinfo/buildroot
> _______________________________________________
> buildroot mailing list
> buildroot@buildroot.org
> https://lists.buildroot.org/mailman/listinfo/buildroot
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27 19:36   ` Arnout Vandecappelle
@ 2022-03-27 19:58     ` James Hilliard
  2022-03-27 20:08       ` Jason A. Donenfeld
  0 siblings, 1 reply; 34+ messages in thread
From: James Hilliard @ 2022-03-27 19:58 UTC (permalink / raw)
  To: Arnout Vandecappelle; +Cc: Jason A. Donenfeld, Yann E. MORIN, buildroot

On Sun, Mar 27, 2022 at 1:36 PM Arnout Vandecappelle <arnout@mind.be> wrote:
>
>
>
> On 27/03/2022 20:01, James Hilliard wrote:
> > On Sat, Mar 26, 2022 at 11:48 PM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
> >>
> >> The RNG can't actually be seeded from a shell script, due to the
> >> reliance on ioctls. For this reason, the seedrng project provides a
> >> basic script meant to be copy and pasted into projects like buildroot
> >> and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
> >>
> >> This commit imports it into buildroot and wires up the init scripts to
> >> call it. This also is a significant improvement over the current init
> >> script, which doesn't credit entropy and whose hashing in shell scripts
> >> is sort of fragile.
> >>
> >> As seedrng.c is a short tiny C program, we include this here in the
> >> package, like a few other packages do. Later we'll investigate adding
> >> this to busybox, but for now, this is a good start and a positive step
> >> in the right direction.
> >>
> >> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
> [snip]
> >> +#ifndef GRND_INSECURE
> >> +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */
> >> +#endif
> >> +
> >> +#ifndef LOCALSTATEDIR
> >> +#define LOCALSTATEDIR "/var/lib"
> >> +#endif
> >> +#ifndef RUNSTATEDIR
> >> +#define RUNSTATEDIR "/var/run"
> >> +#endif
> >
> > Might be a good idea to have runtime env overrides for these to allow easier
> > path customization by only having to override the init script in a
> > rootfs overlay:
> > https://buildroot.org/downloads/manual/manual.html#rootfs-custom
> >
> > Maybe something similar to the SEEDRNG_SKIP_CREDIT env override would
> > make sense for these paths.
>
>   I don't know if that makes sense. Dozens of buildroot packages hardocde these
> paths already.

I'm mostly just thinking that due to this needing to run super early
it may be useful
to be able to override in case say the seed needs to be stored in a non-standard
mount location like the ESP along the lines of systemd:
https://www.freedesktop.org/software/systemd/man/bootctl.html#random-seed

>
>   Regards,
>   Arnout
>
> >
> >> +
> >> +#define SEED_DIR LOCALSTATEDIR "/seedrng"
> >> +#define CREDITABLE_SEED SEED_DIR "/seed.credit"
> >> +#define NON_CREDITABLE_SEED SEED_DIR "/seed.no-credit"
> >> +#define LOCK_FILE RUNSTATEDIR "/seedrng.lock"
> >> +
> >> +enum blake2s_lengths {
> >> +       BLAKE2S_BLOCK_LEN = 64,
> >> +       BLAKE2S_HASH_LEN = 32,
> >> +       BLAKE2S_KEY_LEN = 32
> >> +};
> >> +
> >> +enum seedrng_lengths {
> >> +       MAX_SEED_LEN = 512,
> >> +       MIN_SEED_LEN = BLAKE2S_HASH_LEN
> >> +};
> >> +
> >> +struct blake2s_state {
> >> +       uint32_t h[8];
> >> +       uint32_t t[2];
> >> +       uint32_t f[2];
> >> +       uint8_t buf[BLAKE2S_BLOCK_LEN];
> >> +       unsigned int buflen;
> >> +       unsigned int outlen;
> >> +};
> >> +
> >> +#define le32_to_cpup(a) le32toh(*(a))
> >> +#define cpu_to_le32(a) htole32(a)
> >> +#ifndef ARRAY_SIZE
> >> +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
> >> +#endif
> >> +#ifndef DIV_ROUND_UP
> >> +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
> >> +#endif
> >> +
> >> +static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
> >> +{
> >> +        while (words--) {
> >> +               *buf = cpu_to_le32(*buf);
> >> +               ++buf;
> >> +       }
> >> +}
> >> +
> >> +static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
> >> +{
> >> +        while (words--) {
> >> +               *buf = le32_to_cpup(buf);
> >> +               ++buf;
> >> +        }
> >> +}
> >> +
> >> +static inline uint32_t ror32(uint32_t word, unsigned int shift)
> >> +{
> >> +       return (word >> (shift & 31)) | (word << ((-shift) & 31));
> >> +}
> >> +
> >> +static const uint32_t blake2s_iv[8] = {
> >> +       0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
> >> +       0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
> >> +};
> >> +
> >> +static const uint8_t blake2s_sigma[10][16] = {
> >> +       { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
> >> +       { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
> >> +       { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
> >> +       { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
> >> +       { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
> >> +       { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
> >> +       { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
> >> +       { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
> >> +       { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
> >> +       { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
> >> +};
> >> +
> >> +static void blake2s_set_lastblock(struct blake2s_state *state)
> >> +{
> >> +       state->f[0] = -1;
> >> +}
> >> +
> >> +static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc)
> >> +{
> >> +       state->t[0] += inc;
> >> +       state->t[1] += (state->t[0] < inc);
> >> +}
> >> +
> >> +static void blake2s_init_param(struct blake2s_state *state, const uint32_t param)
> >> +{
> >> +       int i;
> >> +
> >> +       memset(state, 0, sizeof(*state));
> >> +       for (i = 0; i < 8; ++i)
> >> +               state->h[i] = blake2s_iv[i];
> >> +       state->h[0] ^= param;
> >> +}
> >> +
> >> +static void blake2s_init(struct blake2s_state *state, const size_t outlen)
> >> +{
> >> +       blake2s_init_param(state, 0x01010000 | outlen);
> >> +       state->outlen = outlen;
> >> +}
> >> +
> >> +static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc)
> >> +{
> >> +       uint32_t m[16];
> >> +       uint32_t v[16];
> >> +       int i;
> >> +
> >> +       while (nblocks > 0) {
> >> +               blake2s_increment_counter(state, inc);
> >> +               memcpy(m, block, BLAKE2S_BLOCK_LEN);
> >> +               le32_to_cpu_array(m, ARRAY_SIZE(m));
> >> +               memcpy(v, state->h, 32);
> >> +               v[ 8] = blake2s_iv[0];
> >> +               v[ 9] = blake2s_iv[1];
> >> +               v[10] = blake2s_iv[2];
> >> +               v[11] = blake2s_iv[3];
> >> +               v[12] = blake2s_iv[4] ^ state->t[0];
> >> +               v[13] = blake2s_iv[5] ^ state->t[1];
> >> +               v[14] = blake2s_iv[6] ^ state->f[0];
> >> +               v[15] = blake2s_iv[7] ^ state->f[1];
> >> +
> >> +#define G(r, i, a, b, c, d) do { \
> >> +       a += b + m[blake2s_sigma[r][2 * i + 0]]; \
> >> +       d = ror32(d ^ a, 16); \
> >> +       c += d; \
> >> +       b = ror32(b ^ c, 12); \
> >> +       a += b + m[blake2s_sigma[r][2 * i + 1]]; \
> >> +       d = ror32(d ^ a, 8); \
> >> +       c += d; \
> >> +       b = ror32(b ^ c, 7); \
> >> +} while (0)
> >> +
> >> +#define ROUND(r) do { \
> >> +       G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
> >> +       G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
> >> +       G(r, 2, v[2], v[ 6], v[10], v[14]); \
> >> +       G(r, 3, v[3], v[ 7], v[11], v[15]); \
> >> +       G(r, 4, v[0], v[ 5], v[10], v[15]); \
> >> +       G(r, 5, v[1], v[ 6], v[11], v[12]); \
> >> +       G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
> >> +       G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
> >> +} while (0)
> >> +               ROUND(0);
> >> +               ROUND(1);
> >> +               ROUND(2);
> >> +               ROUND(3);
> >> +               ROUND(4);
> >> +               ROUND(5);
> >> +               ROUND(6);
> >> +               ROUND(7);
> >> +               ROUND(8);
> >> +               ROUND(9);
> >> +
> >> +#undef G
> >> +#undef ROUND
> >> +
> >> +               for (i = 0; i < 8; ++i)
> >> +                       state->h[i] ^= v[i] ^ v[i + 8];
> >> +
> >> +               block += BLAKE2S_BLOCK_LEN;
> >> +               --nblocks;
> >> +       }
> >> +}
> >> +
> >> +static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen)
> >> +{
> >> +       const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen;
> >> +       const uint8_t *in = inp;
> >> +
> >> +       if (!inlen)
> >> +               return;
> >> +       if (inlen > fill) {
> >> +               memcpy(state->buf + state->buflen, in, fill);
> >> +               blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
> >> +               state->buflen = 0;
> >> +               in += fill;
> >> +               inlen -= fill;
> >> +       }
> >> +       if (inlen > BLAKE2S_BLOCK_LEN) {
> >> +               const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
> >> +               blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
> >> +               in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
> >> +               inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
> >> +       }
> >> +       memcpy(state->buf + state->buflen, in, inlen);
> >> +       state->buflen += inlen;
> >> +}
> >> +
> >> +static void blake2s_final(struct blake2s_state *state, uint8_t *out)
> >> +{
> >> +       blake2s_set_lastblock(state);
> >> +       memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
> >> +       blake2s_compress(state, state->buf, 1, state->buflen);
> >> +       cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
> >> +       memcpy(out, state->h, state->outlen);
> >> +}
> >> +
> >> +static size_t determine_optimal_seed_len(void)
> >> +{
> >> +       size_t ret = 0;
> >> +       char poolsize_str[11] = { 0 };
> >> +       int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
> >> +
> >> +       if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
> >> +               fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno));
> >> +               ret = MIN_SEED_LEN;
> >> +       } else
> >> +               ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
> >> +       if (fd >= 0)
> >> +               close(fd);
> >> +       if (ret < MIN_SEED_LEN)
> >> +               ret = MIN_SEED_LEN;
> >> +       else if (ret > MAX_SEED_LEN)
> >> +               ret = MAX_SEED_LEN;
> >> +       return ret;
> >> +}
> >> +
> >> +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable)
> >> +{
> >> +       ssize_t ret;
> >> +       int urandom_fd;
> >> +
> >> +       *is_creditable = false;
> >> +       ret = getrandom(seed, len, GRND_NONBLOCK);
> >> +       if (ret == (ssize_t)len) {
> >> +               *is_creditable = true;
> >> +               return 0;
> >> +       } else if (ret < 0 && errno == ENOSYS) {
> >> +               struct pollfd random_fd = {
> >> +                       .fd = open("/dev/random", O_RDONLY),
> >> +                       .events = POLLIN
> >> +               };
> >> +               if (random_fd.fd < 0)
> >> +                       return -errno;
> >> +               *is_creditable = poll(&random_fd, 1, 0) == 1;
> >> +               close(random_fd.fd);
> >> +       } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len)
> >> +               return 0;
> >> +       urandom_fd = open("/dev/urandom", O_RDONLY);
> >> +       if (urandom_fd < 0)
> >> +               return -errno;
> >> +       ret = read(urandom_fd, seed, len);
> >> +       if (ret == (ssize_t)len)
> >> +               ret = 0;
> >> +       else
> >> +               ret = -errno ? -errno : -EIO;
> >> +       close(urandom_fd);
> >> +       return ret;
> >> +}
> >> +
> >> +static int seed_rng(uint8_t *seed, size_t len, bool credit)
> >> +{
> >> +       struct {
> >> +               int entropy_count;
> >> +               int buf_size;
> >> +               uint8_t buffer[MAX_SEED_LEN];
> >> +       } req = {
> >> +               .entropy_count = credit ? len * 8 : 0,
> >> +               .buf_size = len
> >> +       };
> >> +       int random_fd, ret;
> >> +
> >> +       if (len > sizeof(req.buffer))
> >> +               return -EFBIG;
> >> +       memcpy(req.buffer, seed, len);
> >> +
> >> +       random_fd = open("/dev/random", O_RDWR);
> >> +       if (random_fd < 0)
> >> +               return -errno;
> >> +       ret = ioctl(random_fd, RNDADDENTROPY, &req);
> >> +       if (ret)
> >> +               ret = -errno ? -errno : -EIO;
> >> +       close(random_fd);
> >> +       return ret;
> >> +}
> >> +
> >> +static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash)
> >> +{
> >> +       uint8_t seed[MAX_SEED_LEN];
> >> +       ssize_t seed_len;
> >> +       int fd, dfd, ret = 0;
> >> +
> >> +       fd = open(filename, O_RDONLY);
> >> +       if (fd < 0 && errno == ENOENT)
> >> +               return 0;
> >> +       else if (fd < 0) {
> >> +               ret = -errno;
> >> +               fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno));
> >> +               return ret;
> >> +       }
> >> +       dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
> >> +       if (dfd < 0) {
> >> +               ret = -errno;
> >> +               close(fd);
> >> +               fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno));
> >> +               return ret;
> >> +       }
> >> +       seed_len = read(fd, seed, sizeof(seed));
> >> +       if (seed_len < 0) {
> >> +               ret = -errno;
> >> +               fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno));
> >> +       }
> >> +       close(fd);
> >> +       if (ret) {
> >> +               close(dfd);
> >> +               return ret;
> >> +       }
> >> +       if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) {
> >> +               ret = -errno;
> >> +               fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno));
> >> +       }
> >> +       close(dfd);
> >> +       if (ret)
> >> +               return ret;
> >> +       if (!seed_len)
> >> +               return 0;
> >> +
> >> +       blake2s_update(hash, &seed_len, sizeof(seed_len));
> >> +       blake2s_update(hash, seed, seed_len);
> >> +
> >> +       fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
> >> +       ret = seed_rng(seed, seed_len, credit);
> >> +       if (ret < 0)
> >> +               fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret));
> >> +       return ret;
> >> +}
> >> +
> >> +static bool skip_credit(void)
> >> +{
> >> +       const char *skip = getenv("SEEDRNG_SKIP_CREDIT");
> >> +       return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
> >> +                       !strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
> >> +}
> >> +
> >> +int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
> >> +{
> >> +       static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix";
> >> +       static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
> >> +       int ret, fd, lock, program_ret = 0;
> >> +       uint8_t new_seed[MAX_SEED_LEN];
> >> +       size_t new_seed_len;
> >> +       bool new_seed_creditable;
> >> +       struct timespec realtime = { 0 }, boottime = { 0 };
> >> +       struct blake2s_state hash;
> >> +
> >> +       umask(0077);
> >> +       if (getuid()) {
> >> +               fprintf(stderr, "ERROR: This program requires root\n");
> >> +               return 1;
> >> +       }
> >> +
> >> +       blake2s_init(&hash, BLAKE2S_HASH_LEN);
> >> +       blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
> >> +       clock_gettime(CLOCK_REALTIME, &realtime);
> >> +       clock_gettime(CLOCK_BOOTTIME, &boottime);
> >> +       blake2s_update(&hash, &realtime, sizeof(realtime));
> >> +       blake2s_update(&hash, &boottime, sizeof(boottime));
> >> +
> >> +       if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
> >> +               fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR, strerror(errno));
> >> +               return 1;
> >> +       }
> >> +
> >> +       lock = open(LOCK_FILE, O_WRONLY | O_CREAT, 0000);
> >> +       if (lock < 0 || flock(lock, LOCK_EX) < 0) {
> >> +               fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno));
> >> +               return 1;
> >> +       }
> >> +
> >> +       ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash);
> >> +       if (ret < 0)
> >> +               program_ret |= 1 << 1;
> >> +       ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash);
> >> +       if (ret < 0)
> >> +               program_ret |= 1 << 2;
> >> +
> >> +       new_seed_len = determine_optimal_seed_len();
> >> +       ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable);
> >> +       if (ret < 0) {
> >> +               fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret));
> >> +               new_seed_len = BLAKE2S_HASH_LEN;
> >> +               strncpy((char *)new_seed, seedrng_failure, new_seed_len);
> >> +               program_ret |= 1 << 3;
> >> +       }
> >> +       blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
> >> +       blake2s_update(&hash, new_seed, new_seed_len);
> >> +       blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
> >> +
> >> +       fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
> >> +       fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
> >> +       if (fd < 0) {
> >> +               fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno));
> >> +               program_ret |= 1 << 4;
> >> +               goto out;
> >> +       }
> >> +       if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
> >> +               fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno));
> >> +               program_ret |= 1 << 5;
> >> +               goto out;
> >> +       }
> >> +       if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) {
> >> +               fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno));
> >> +               program_ret |= 1 << 6;
> >> +       }
> >> +out:
> >> +       close(fd);
> >> +       close(lock);
> >> +       return program_ret;
> >> +}
> >> diff --git a/package/urandom-scripts/urandom-scripts.mk b/package/urandom-scripts/urandom-scripts.mk
> >> index 2c09728c46..a5cbb95feb 100644
> >> --- a/package/urandom-scripts/urandom-scripts.mk
> >> +++ b/package/urandom-scripts/urandom-scripts.mk
> >> @@ -4,7 +4,17 @@
> >>   #
> >>   ################################################################################
> >>
> >> +define URANDOM_SCRIPTS_EXTRACT_CMDS
> >> +       cp $(URANDOM_SCRIPTS_PKGDIR)/seedrng.c $(@D)
> >> +endef
> >> +
> >> +define URANDOM_SCRIPTS_BUILD_CMDS
> >> +       $(TARGET_CC) $(TARGET_CFLAGS) -std=gnu99 $(TARGET_LDFLAGS) \
> >> +               $(@D)/seedrng.c -o $(@D)/seedrng
> >> +endef
> >> +
> >>   define URANDOM_SCRIPTS_INSTALL_INIT_SYSV
> >> +       $(INSTALL) -D -m 0755 $(@D)/seedrng $(TARGET_DIR)/sbin/seedrng
> >>          $(INSTALL) -D -m 0755 $(URANDOM_SCRIPTS_PKGDIR)/S20urandom \
> >>                  $(TARGET_DIR)/etc/init.d/S20urandom
> >>   endef
> >> --
> >> 2.35.1
> >>
> >> _______________________________________________
> >> buildroot mailing list
> >> buildroot@buildroot.org
> >> https://lists.buildroot.org/mailman/listinfo/buildroot
> > _______________________________________________
> > buildroot mailing list
> > buildroot@buildroot.org
> > https://lists.buildroot.org/mailman/listinfo/buildroot
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27 19:58     ` James Hilliard
@ 2022-03-27 20:08       ` Jason A. Donenfeld
  2022-03-27 20:10         ` Jason A. Donenfeld
  0 siblings, 1 reply; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-03-27 20:08 UTC (permalink / raw)
  To: James Hilliard; +Cc: Yann E. MORIN, buildroot

Hi James, Arnout,

On Sun, Mar 27, 2022 at 3:58 PM James Hilliard
<james.hilliard1@gmail.com> wrote:
> I'm mostly just thinking that due to this needing to run super early
> it may be useful
> to be able to override in case say the seed needs to be stored in a non-standard
> mount location like the ESP along the lines of systemd:
> https://www.freedesktop.org/software/systemd/man/bootctl.html#random-seed

If you want to reengineer other things about this, please feel free to
do so after with follow-up commits. This keeps the same path location
root as the code that it replaces. Let's do one thing at a time. If we
do all these things at once, it'll be hard to fix regressions or
understand what's happened. This commit here improves one dimension of
things. Future ones from you can add additional features or tweaks or
whatever else.

Jason
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27 20:08       ` Jason A. Donenfeld
@ 2022-03-27 20:10         ` Jason A. Donenfeld
  2022-03-27 20:24           ` [Buildroot] [PATCH v2] " Jason A. Donenfeld
  2022-03-27 20:25           ` [Buildroot] [PATCH] " James Hilliard
  0 siblings, 2 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-03-27 20:10 UTC (permalink / raw)
  To: James Hilliard; +Cc: Yann E. MORIN, buildroot

Hey again,

Oh, I didn't see the whole conversation because you failed to reply
all. Please keep me in the CC, as I have list mail turned off.

I see now you just want an option to do this via environment. This is
what I did on OpenRC. I'll send a v2 with that for here.

Jason
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [Buildroot] [PATCH v2] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27 20:10         ` Jason A. Donenfeld
@ 2022-03-27 20:24           ` Jason A. Donenfeld
  2022-03-27 20:29             ` James Hilliard
  2022-03-29  5:04             ` [Buildroot] [PATCH v3] " Jason A. Donenfeld
  2022-03-27 20:25           ` [Buildroot] [PATCH] " James Hilliard
  1 sibling, 2 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-03-27 20:24 UTC (permalink / raw)
  To: James Hilliard, Arnout Vandecappelle, Yann E. MORIN, buildroot
  Cc: Jason A. Donenfeld

The RNG can't actually be seeded from a shell script, due to the
reliance on ioctls. For this reason, the seedrng project provides a
basic script meant to be copy and pasted into projects like buildroot
and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.

This commit imports it into buildroot and wires up the init scripts to
call it. This also is a significant improvement over the current init
script, which doesn't credit entropy and whose hashing in shell scripts
is sort of fragile.

As seedrng.c is a short tiny C program, we include this here in the
package, like a few other packages do. Later we'll investigate adding
this to busybox, but for now, this is a good start and a positive step
in the right direction.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
 package/urandom-scripts/Config.in          |   4 -
 package/urandom-scripts/S20urandom         |  64 +--
 package/urandom-scripts/seedrng.c          | 455 +++++++++++++++++++++
 package/urandom-scripts/urandom-scripts.mk |   6 +
 4 files changed, 479 insertions(+), 50 deletions(-)
 create mode 100644 package/urandom-scripts/seedrng.c

diff --git a/package/urandom-scripts/Config.in b/package/urandom-scripts/Config.in
index 987e442e22..070ffa5e9a 100644
--- a/package/urandom-scripts/Config.in
+++ b/package/urandom-scripts/Config.in
@@ -4,7 +4,3 @@ config BR2_PACKAGE_URANDOM_SCRIPTS
 	depends on !BR2_PACKAGE_SYSTEMD
 	help
 	  Initscript to preserve the random seed between reboots.
-
-	  WARNING: this is a poor fit to try and get high-quality
-	  entropy at boot. There are better ways, like haveged, or
-	  rng-tools.
diff --git a/package/urandom-scripts/S20urandom b/package/urandom-scripts/S20urandom
index c6b2ebd48f..1959fad93b 100644
--- a/package/urandom-scripts/S20urandom
+++ b/package/urandom-scripts/S20urandom
@@ -6,63 +6,35 @@
 # Quietly do nothing if /dev/urandom does not exist
 [ -c /dev/urandom ] || exit 0
 
-URANDOM_SEED="/var/lib/random-seed"
+# The following knobs can be adjusted by placing uncommented modified lines
+# into /etc/default/urandom:
+#
+# Set these to change where the seed and runtime lock file are stored:
+#
+#     export SEEDRNG_SEED_DIR=/var/lib/seedrng
+#     export SEEDRNG_LOCK_FILE=/var/run/seedrng.lock
+#
+# Set this to true only if you do not want seed files to actually credit the
+# RNG, for example if you plan to replicate this file system image and do not
+# have the wherewithal to first delete the contents of /var/lib/seedrng:
+#
+#     export SEEDRNG_SKIP_CREDIT=false
+#
 
 # shellcheck source=/dev/null
 [ -r "/etc/default/urandom" ] && . "/etc/default/urandom"
 
-if pool_bits=$(cat /proc/sys/kernel/random/poolsize 2> /dev/null); then
-	pool_size=$((pool_bits/8))
-else
-	pool_size=512
-fi
-
-init_rng() {
-	printf 'Initializing random number generator: '
-	dd if="$URANDOM_SEED" bs="$pool_size" of=/dev/urandom count=1 2> /dev/null
-	status=$?
-	if [ "$status" -eq 0 ]; then
-		echo "OK"
-	else
-		echo "FAIL"
-	fi
-	return "$status"
-}
-
-save_random_seed() {
-	printf 'Saving random seed: '
-	status=1
-	if touch "$URANDOM_SEED.new" 2> /dev/null; then
-		old_umask=$(umask)
-		umask 077
-		dd if=/dev/urandom of="$URANDOM_SEED.tmp" bs="$pool_size" count=1 2> /dev/null
-		cat "$URANDOM_SEED" "$URANDOM_SEED.tmp" 2>/dev/null \
-			| sha256sum \
-			| cut -d ' ' -f 1 > "$URANDOM_SEED.new" && \
-		mv "$URANDOM_SEED.new" "$URANDOM_SEED" && status=0
-		rm -f "$URANDOM_SEED.tmp"
-		umask "$old_umask"
-		if [ "$status" -eq 0 ]; then
-			echo "OK"
-		else
-			echo "FAIL"
-		fi
-
-	else
-		echo "SKIP (read-only file system detected)"
-	fi
-	return "$status"
-}
-
 case "$1" in
 	start|restart|reload)
 		# Carry a random seed from start-up to start-up
 		# Load and then save the whole entropy pool
-		init_rng && save_random_seed;;
+		# Never fail, as this isn't worth making a fuss
+		# over if it doesn't go as planned.
+		seedrng || true;;
 	stop)
 		# Carry a random seed from shut-down to start-up
 		# Save the whole entropy pool
-		save_random_seed;;
+		seedrng;;
 	*)
 		echo "Usage: $0 {start|stop|restart|reload}"
 		exit 1
diff --git a/package/urandom-scripts/seedrng.c b/package/urandom-scripts/seedrng.c
new file mode 100644
index 0000000000..b3f6381bd8
--- /dev/null
+++ b/package/urandom-scripts/seedrng.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT OR Apache-2.0)
+/*
+ * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is based on code from <https://git.zx2c4.com/seedrng/about/>.
+ */
+
+#define _GNU_SOURCE
+#include <linux/random.h>
+#include <sys/random.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef GRND_INSECURE
+#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */
+#endif
+
+
+static const char *SEED_DIR;
+static const char *LOCK_FILE;
+static char *CREDITABLE_SEED;
+static char *NON_CREDITABLE_SEED;
+
+enum blake2s_lengths {
+	BLAKE2S_BLOCK_LEN = 64,
+	BLAKE2S_HASH_LEN = 32,
+	BLAKE2S_KEY_LEN = 32
+};
+
+enum seedrng_lengths {
+	MAX_SEED_LEN = 512,
+	MIN_SEED_LEN = BLAKE2S_HASH_LEN
+};
+
+struct blake2s_state {
+	uint32_t h[8];
+	uint32_t t[2];
+	uint32_t f[2];
+	uint8_t buf[BLAKE2S_BLOCK_LEN];
+	unsigned int buflen;
+	unsigned int outlen;
+};
+
+#define le32_to_cpup(a) le32toh(*(a))
+#define cpu_to_le32(a) htole32(a)
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#endif
+
+static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
+{
+        while (words--) {
+		*buf = cpu_to_le32(*buf);
+		++buf;
+	}
+}
+
+static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
+{
+        while (words--) {
+		*buf = le32_to_cpup(buf);
+		++buf;
+        }
+}
+
+static inline uint32_t ror32(uint32_t word, unsigned int shift)
+{
+	return (word >> (shift & 31)) | (word << ((-shift) & 31));
+}
+
+static const uint32_t blake2s_iv[8] = {
+	0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+	0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] = {
+	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+	{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+	{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+	{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+	{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+	{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+	{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+	{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+	{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+	{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+};
+
+static void blake2s_set_lastblock(struct blake2s_state *state)
+{
+	state->f[0] = -1;
+}
+
+static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc)
+{
+	state->t[0] += inc;
+	state->t[1] += (state->t[0] < inc);
+}
+
+static void blake2s_init_param(struct blake2s_state *state, const uint32_t param)
+{
+	int i;
+
+	memset(state, 0, sizeof(*state));
+	for (i = 0; i < 8; ++i)
+		state->h[i] = blake2s_iv[i];
+	state->h[0] ^= param;
+}
+
+static void blake2s_init(struct blake2s_state *state, const size_t outlen)
+{
+	blake2s_init_param(state, 0x01010000 | outlen);
+	state->outlen = outlen;
+}
+
+static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc)
+{
+	uint32_t m[16];
+	uint32_t v[16];
+	int i;
+
+	while (nblocks > 0) {
+		blake2s_increment_counter(state, inc);
+		memcpy(m, block, BLAKE2S_BLOCK_LEN);
+		le32_to_cpu_array(m, ARRAY_SIZE(m));
+		memcpy(v, state->h, 32);
+		v[ 8] = blake2s_iv[0];
+		v[ 9] = blake2s_iv[1];
+		v[10] = blake2s_iv[2];
+		v[11] = blake2s_iv[3];
+		v[12] = blake2s_iv[4] ^ state->t[0];
+		v[13] = blake2s_iv[5] ^ state->t[1];
+		v[14] = blake2s_iv[6] ^ state->f[0];
+		v[15] = blake2s_iv[7] ^ state->f[1];
+
+#define G(r, i, a, b, c, d) do { \
+	a += b + m[blake2s_sigma[r][2 * i + 0]]; \
+	d = ror32(d ^ a, 16); \
+	c += d; \
+	b = ror32(b ^ c, 12); \
+	a += b + m[blake2s_sigma[r][2 * i + 1]]; \
+	d = ror32(d ^ a, 8); \
+	c += d; \
+	b = ror32(b ^ c, 7); \
+} while (0)
+
+#define ROUND(r) do { \
+	G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
+	G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
+	G(r, 2, v[2], v[ 6], v[10], v[14]); \
+	G(r, 3, v[3], v[ 7], v[11], v[15]); \
+	G(r, 4, v[0], v[ 5], v[10], v[15]); \
+	G(r, 5, v[1], v[ 6], v[11], v[12]); \
+	G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
+	G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
+} while (0)
+		ROUND(0);
+		ROUND(1);
+		ROUND(2);
+		ROUND(3);
+		ROUND(4);
+		ROUND(5);
+		ROUND(6);
+		ROUND(7);
+		ROUND(8);
+		ROUND(9);
+
+#undef G
+#undef ROUND
+
+		for (i = 0; i < 8; ++i)
+			state->h[i] ^= v[i] ^ v[i + 8];
+
+		block += BLAKE2S_BLOCK_LEN;
+		--nblocks;
+	}
+}
+
+static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen)
+{
+	const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen;
+	const uint8_t *in = inp;
+
+	if (!inlen)
+		return;
+	if (inlen > fill) {
+		memcpy(state->buf + state->buflen, in, fill);
+		blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
+		state->buflen = 0;
+		in += fill;
+		inlen -= fill;
+	}
+	if (inlen > BLAKE2S_BLOCK_LEN) {
+		const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
+		blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
+		in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
+		inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
+	}
+	memcpy(state->buf + state->buflen, in, inlen);
+	state->buflen += inlen;
+}
+
+static void blake2s_final(struct blake2s_state *state, uint8_t *out)
+{
+	blake2s_set_lastblock(state);
+	memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
+	blake2s_compress(state, state->buf, 1, state->buflen);
+	cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
+	memcpy(out, state->h, state->outlen);
+}
+
+static size_t determine_optimal_seed_len(void)
+{
+	size_t ret = 0;
+	char poolsize_str[11] = { 0 };
+	int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
+
+	if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
+		fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno));
+		ret = MIN_SEED_LEN;
+	} else
+		ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
+	if (fd >= 0)
+		close(fd);
+	if (ret < MIN_SEED_LEN)
+		ret = MIN_SEED_LEN;
+	else if (ret > MAX_SEED_LEN)
+		ret = MAX_SEED_LEN;
+	return ret;
+}
+
+static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable)
+{
+	ssize_t ret;
+	int urandom_fd;
+
+	*is_creditable = false;
+	ret = getrandom(seed, len, GRND_NONBLOCK);
+	if (ret == (ssize_t)len) {
+		*is_creditable = true;
+		return 0;
+	} else if (ret < 0 && errno == ENOSYS) {
+		struct pollfd random_fd = {
+			.fd = open("/dev/random", O_RDONLY),
+			.events = POLLIN
+		};
+		if (random_fd.fd < 0)
+			return -errno;
+		*is_creditable = poll(&random_fd, 1, 0) == 1;
+		close(random_fd.fd);
+	} else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len)
+		return 0;
+	urandom_fd = open("/dev/urandom", O_RDONLY);
+	if (urandom_fd < 0)
+		return -errno;
+	ret = read(urandom_fd, seed, len);
+	if (ret == (ssize_t)len)
+		ret = 0;
+	else
+		ret = -errno ? -errno : -EIO;
+	close(urandom_fd);
+	return ret;
+}
+
+static int seed_rng(uint8_t *seed, size_t len, bool credit)
+{
+	struct {
+		int entropy_count;
+		int buf_size;
+		uint8_t buffer[MAX_SEED_LEN];
+	} req = {
+		.entropy_count = credit ? len * 8 : 0,
+		.buf_size = len
+	};
+	int random_fd, ret;
+
+	if (len > sizeof(req.buffer))
+		return -EFBIG;
+	memcpy(req.buffer, seed, len);
+
+	random_fd = open("/dev/random", O_RDWR);
+	if (random_fd < 0)
+		return -errno;
+	ret = ioctl(random_fd, RNDADDENTROPY, &req);
+	if (ret)
+		ret = -errno ? -errno : -EIO;
+	close(random_fd);
+	return ret;
+}
+
+static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash)
+{
+	uint8_t seed[MAX_SEED_LEN];
+	ssize_t seed_len;
+	int fd, dfd, ret = 0;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0 && errno == ENOENT)
+		return 0;
+	else if (fd < 0) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno));
+		return ret;
+	}
+	dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
+	if (dfd < 0) {
+		ret = -errno;
+		close(fd);
+		fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno));
+		return ret;
+	}
+	seed_len = read(fd, seed, sizeof(seed));
+	if (seed_len < 0) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno));
+	}
+	close(fd);
+	if (ret) {
+		close(dfd);
+		return ret;
+	}
+	if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno));
+	}
+	close(dfd);
+	if (ret)
+		return ret;
+	if (!seed_len)
+		return 0;
+
+	blake2s_update(hash, &seed_len, sizeof(seed_len));
+	blake2s_update(hash, seed, seed_len);
+
+	fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
+	ret = seed_rng(seed, seed_len, credit);
+	if (ret < 0)
+		fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret));
+	return ret;
+}
+
+static bool skip_credit(void)
+{
+	const char *skip = getenv("SEEDRNG_SKIP_CREDIT");
+	return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
+			!strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
+}
+
+static void populate_global_paths(void)
+{
+	SEED_DIR = getenv("SEEDRNG_SEED_DIR");
+	if (!SEED_DIR || !*SEED_DIR)
+		SEED_DIR = "/var/lib/seedrng";
+	LOCK_FILE = getenv("SEEDRNG_LOCK_FILE");
+	if (!LOCK_FILE || !*LOCK_FILE)
+		LOCK_FILE = "/var/run/seedrng.lock";
+	if (asprintf(&CREDITABLE_SEED, "%s/seed.credit", SEED_DIR) < 0 ||
+	    asprintf(&NON_CREDITABLE_SEED, "%s/seed.no-credit", SEED_DIR) < 0) {
+		fprintf(stderr, "ERROR: Unable to allocate paths: %s\n", strerror(errno));
+		exit(1);
+	}
+}
+
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+	static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix";
+	static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
+	int ret, fd, lock, program_ret = 0;
+	uint8_t new_seed[MAX_SEED_LEN];
+	size_t new_seed_len;
+	bool new_seed_creditable;
+	struct timespec realtime = { 0 }, boottime = { 0 };
+	struct blake2s_state hash;
+
+	umask(0077);
+	if (getuid()) {
+		fprintf(stderr, "ERROR: This program requires root\n");
+		return 1;
+	}
+
+	populate_global_paths();
+	blake2s_init(&hash, BLAKE2S_HASH_LEN);
+	blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
+	clock_gettime(CLOCK_REALTIME, &realtime);
+	clock_gettime(CLOCK_BOOTTIME, &boottime);
+	blake2s_update(&hash, &realtime, sizeof(realtime));
+	blake2s_update(&hash, &boottime, sizeof(boottime));
+
+	if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
+		fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR, strerror(errno));
+		return 1;
+	}
+
+	lock = open(LOCK_FILE, O_WRONLY | O_CREAT, 0000);
+	if (lock < 0 || flock(lock, LOCK_EX) < 0) {
+		fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno));
+		return 1;
+	}
+
+	ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash);
+	if (ret < 0)
+		program_ret |= 1 << 1;
+	ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash);
+	if (ret < 0)
+		program_ret |= 1 << 2;
+
+	new_seed_len = determine_optimal_seed_len();
+	ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable);
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret));
+		new_seed_len = BLAKE2S_HASH_LEN;
+		strncpy((char *)new_seed, seedrng_failure, new_seed_len);
+		program_ret |= 1 << 3;
+	}
+	blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
+	blake2s_update(&hash, new_seed, new_seed_len);
+	blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
+
+	fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
+	fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno));
+		program_ret |= 1 << 4;
+		goto out;
+	}
+	if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
+		fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno));
+		program_ret |= 1 << 5;
+		goto out;
+	}
+	if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) {
+		fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno));
+		program_ret |= 1 << 6;
+	}
+out:
+	close(fd);
+	close(lock);
+	return program_ret;
+}
diff --git a/package/urandom-scripts/urandom-scripts.mk b/package/urandom-scripts/urandom-scripts.mk
index 2c09728c46..e8526e248a 100644
--- a/package/urandom-scripts/urandom-scripts.mk
+++ b/package/urandom-scripts/urandom-scripts.mk
@@ -4,7 +4,13 @@
 #
 ################################################################################
 
+define URANDOM_SCRIPTS_BUILD_CMDS
+	$(TARGET_CC) $(TARGET_CFLAGS) -std=gnu99 $(TARGET_LDFLAGS) \
+		$(URANDOM_SCRIPTS_PKGDIR)/seedrng.c -o $(@D)/seedrng
+endef
+
 define URANDOM_SCRIPTS_INSTALL_INIT_SYSV
+	$(INSTALL) -D -m 0755 $(@D)/seedrng $(TARGET_DIR)/sbin/seedrng
 	$(INSTALL) -D -m 0755 $(URANDOM_SCRIPTS_PKGDIR)/S20urandom \
 		$(TARGET_DIR)/etc/init.d/S20urandom
 endef
-- 
2.35.1

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27 20:10         ` Jason A. Donenfeld
  2022-03-27 20:24           ` [Buildroot] [PATCH v2] " Jason A. Donenfeld
@ 2022-03-27 20:25           ` James Hilliard
  2022-03-27 20:26             ` Jason A. Donenfeld
  1 sibling, 1 reply; 34+ messages in thread
From: James Hilliard @ 2022-03-27 20:25 UTC (permalink / raw)
  To: Jason A. Donenfeld; +Cc: Yann E. MORIN, buildroot

On Sun, Mar 27, 2022 at 2:10 PM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
>
> Hey again,
>
> Oh, I didn't see the whole conversation because you failed to reply
> all. Please keep me in the CC, as I have list mail turned off.

Strange, I did use reply all, maybe it ended up in spam?

It appears to have been sent correctly from what I can tell:
https://lore.kernel.org/buildroot/CADvTj4rJcC8NQnG4A70VgF6uqzj-xGDxg2nOyRB=jxj4wSLpqg@mail.gmail.com/

>
> I see now you just want an option to do this via environment. This is
> what I did on OpenRC. I'll send a v2 with that for here.

Yeah, I'm just suggesting a path override since that's trivial to tweak with the
existing script via a rootfs overlay override but slightly more complex to do if
one has to modify a binary.

>
> Jason
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27 20:25           ` [Buildroot] [PATCH] " James Hilliard
@ 2022-03-27 20:26             ` Jason A. Donenfeld
  0 siblings, 0 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-03-27 20:26 UTC (permalink / raw)
  To: James Hilliard; +Cc: Yann E. MORIN, buildroot

Hey James,

On Sun, Mar 27, 2022 at 4:25 PM James Hilliard
<james.hilliard1@gmail.com> wrote:
> Strange, I did use reply all, maybe it ended up in spam?
>
> It appears to have been sent correctly from what I can tell:
> https://lore.kernel.org/buildroot/CADvTj4rJcC8NQnG4A70VgF6uqzj-xGDxg2nOyRB=jxj4wSLpqg@mail.gmail.com/

Indeed I think you're right. MTA issues? I'll investigate.

>
> >
> > I see now you just want an option to do this via environment. This is
> > what I did on OpenRC. I'll send a v2 with that for here.
>
> Yeah, I'm just suggesting a path override since that's trivial to tweak with the
> existing script via a rootfs overlay override but slightly more complex to do if
> one has to modify a binary.

Yep! Already done for the v2:
https://lore.kernel.org/buildroot/20220327202415.1248312-1-Jason@zx2c4.com/

Jason
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v2] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27 20:24           ` [Buildroot] [PATCH v2] " Jason A. Donenfeld
@ 2022-03-27 20:29             ` James Hilliard
  2022-03-29  5:04             ` [Buildroot] [PATCH v3] " Jason A. Donenfeld
  1 sibling, 0 replies; 34+ messages in thread
From: James Hilliard @ 2022-03-27 20:29 UTC (permalink / raw)
  To: Jason A. Donenfeld; +Cc: Yann E. MORIN, buildroot

On Sun, Mar 27, 2022 at 2:24 PM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
>
> The RNG can't actually be seeded from a shell script, due to the
> reliance on ioctls. For this reason, the seedrng project provides a
> basic script meant to be copy and pasted into projects like buildroot
> and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
>
> This commit imports it into buildroot and wires up the init scripts to
> call it. This also is a significant improvement over the current init
> script, which doesn't credit entropy and whose hashing in shell scripts
> is sort of fragile.
>
> As seedrng.c is a short tiny C program, we include this here in the
> package, like a few other packages do. Later we'll investigate adding
> this to busybox, but for now, this is a good start and a positive step
> in the right direction.
>
> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Reviewed-by: James Hilliard <james.hilliard1@gmail.com>
> ---
>  package/urandom-scripts/Config.in          |   4 -
>  package/urandom-scripts/S20urandom         |  64 +--
>  package/urandom-scripts/seedrng.c          | 455 +++++++++++++++++++++
>  package/urandom-scripts/urandom-scripts.mk |   6 +
>  4 files changed, 479 insertions(+), 50 deletions(-)
>  create mode 100644 package/urandom-scripts/seedrng.c
>
> diff --git a/package/urandom-scripts/Config.in b/package/urandom-scripts/Config.in
> index 987e442e22..070ffa5e9a 100644
> --- a/package/urandom-scripts/Config.in
> +++ b/package/urandom-scripts/Config.in
> @@ -4,7 +4,3 @@ config BR2_PACKAGE_URANDOM_SCRIPTS
>         depends on !BR2_PACKAGE_SYSTEMD
>         help
>           Initscript to preserve the random seed between reboots.
> -
> -         WARNING: this is a poor fit to try and get high-quality
> -         entropy at boot. There are better ways, like haveged, or
> -         rng-tools.
> diff --git a/package/urandom-scripts/S20urandom b/package/urandom-scripts/S20urandom
> index c6b2ebd48f..1959fad93b 100644
> --- a/package/urandom-scripts/S20urandom
> +++ b/package/urandom-scripts/S20urandom
> @@ -6,63 +6,35 @@
>  # Quietly do nothing if /dev/urandom does not exist
>  [ -c /dev/urandom ] || exit 0
>
> -URANDOM_SEED="/var/lib/random-seed"
> +# The following knobs can be adjusted by placing uncommented modified lines
> +# into /etc/default/urandom:
> +#
> +# Set these to change where the seed and runtime lock file are stored:
> +#
> +#     export SEEDRNG_SEED_DIR=/var/lib/seedrng
> +#     export SEEDRNG_LOCK_FILE=/var/run/seedrng.lock
> +#
> +# Set this to true only if you do not want seed files to actually credit the
> +# RNG, for example if you plan to replicate this file system image and do not
> +# have the wherewithal to first delete the contents of /var/lib/seedrng:
> +#
> +#     export SEEDRNG_SKIP_CREDIT=false
> +#
>
>  # shellcheck source=/dev/null
>  [ -r "/etc/default/urandom" ] && . "/etc/default/urandom"
>
> -if pool_bits=$(cat /proc/sys/kernel/random/poolsize 2> /dev/null); then
> -       pool_size=$((pool_bits/8))
> -else
> -       pool_size=512
> -fi
> -
> -init_rng() {
> -       printf 'Initializing random number generator: '
> -       dd if="$URANDOM_SEED" bs="$pool_size" of=/dev/urandom count=1 2> /dev/null
> -       status=$?
> -       if [ "$status" -eq 0 ]; then
> -               echo "OK"
> -       else
> -               echo "FAIL"
> -       fi
> -       return "$status"
> -}
> -
> -save_random_seed() {
> -       printf 'Saving random seed: '
> -       status=1
> -       if touch "$URANDOM_SEED.new" 2> /dev/null; then
> -               old_umask=$(umask)
> -               umask 077
> -               dd if=/dev/urandom of="$URANDOM_SEED.tmp" bs="$pool_size" count=1 2> /dev/null
> -               cat "$URANDOM_SEED" "$URANDOM_SEED.tmp" 2>/dev/null \
> -                       | sha256sum \
> -                       | cut -d ' ' -f 1 > "$URANDOM_SEED.new" && \
> -               mv "$URANDOM_SEED.new" "$URANDOM_SEED" && status=0
> -               rm -f "$URANDOM_SEED.tmp"
> -               umask "$old_umask"
> -               if [ "$status" -eq 0 ]; then
> -                       echo "OK"
> -               else
> -                       echo "FAIL"
> -               fi
> -
> -       else
> -               echo "SKIP (read-only file system detected)"
> -       fi
> -       return "$status"
> -}
> -
>  case "$1" in
>         start|restart|reload)
>                 # Carry a random seed from start-up to start-up
>                 # Load and then save the whole entropy pool
> -               init_rng && save_random_seed;;
> +               # Never fail, as this isn't worth making a fuss
> +               # over if it doesn't go as planned.
> +               seedrng || true;;
>         stop)
>                 # Carry a random seed from shut-down to start-up
>                 # Save the whole entropy pool
> -               save_random_seed;;
> +               seedrng;;
>         *)
>                 echo "Usage: $0 {start|stop|restart|reload}"
>                 exit 1
> diff --git a/package/urandom-scripts/seedrng.c b/package/urandom-scripts/seedrng.c
> new file mode 100644
> index 0000000000..b3f6381bd8
> --- /dev/null
> +++ b/package/urandom-scripts/seedrng.c
> @@ -0,0 +1,455 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT OR Apache-2.0)
> +/*
> + * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
> + *
> + * This is based on code from <https://git.zx2c4.com/seedrng/about/>.
> + */
> +
> +#define _GNU_SOURCE
> +#include <linux/random.h>
> +#include <sys/random.h>
> +#include <sys/ioctl.h>
> +#include <sys/file.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#include <fcntl.h>
> +#include <poll.h>
> +#include <unistd.h>
> +#include <time.h>
> +#include <errno.h>
> +#include <endian.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +#ifndef GRND_INSECURE
> +#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */
> +#endif
> +
> +
> +static const char *SEED_DIR;
> +static const char *LOCK_FILE;
> +static char *CREDITABLE_SEED;
> +static char *NON_CREDITABLE_SEED;
> +
> +enum blake2s_lengths {
> +       BLAKE2S_BLOCK_LEN = 64,
> +       BLAKE2S_HASH_LEN = 32,
> +       BLAKE2S_KEY_LEN = 32
> +};
> +
> +enum seedrng_lengths {
> +       MAX_SEED_LEN = 512,
> +       MIN_SEED_LEN = BLAKE2S_HASH_LEN
> +};
> +
> +struct blake2s_state {
> +       uint32_t h[8];
> +       uint32_t t[2];
> +       uint32_t f[2];
> +       uint8_t buf[BLAKE2S_BLOCK_LEN];
> +       unsigned int buflen;
> +       unsigned int outlen;
> +};
> +
> +#define le32_to_cpup(a) le32toh(*(a))
> +#define cpu_to_le32(a) htole32(a)
> +#ifndef ARRAY_SIZE
> +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
> +#endif
> +#ifndef DIV_ROUND_UP
> +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
> +#endif
> +
> +static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
> +{
> +        while (words--) {
> +               *buf = cpu_to_le32(*buf);
> +               ++buf;
> +       }
> +}
> +
> +static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
> +{
> +        while (words--) {
> +               *buf = le32_to_cpup(buf);
> +               ++buf;
> +        }
> +}
> +
> +static inline uint32_t ror32(uint32_t word, unsigned int shift)
> +{
> +       return (word >> (shift & 31)) | (word << ((-shift) & 31));
> +}
> +
> +static const uint32_t blake2s_iv[8] = {
> +       0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
> +       0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
> +};
> +
> +static const uint8_t blake2s_sigma[10][16] = {
> +       { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
> +       { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
> +       { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
> +       { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
> +       { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
> +       { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
> +       { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
> +       { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
> +       { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
> +       { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
> +};
> +
> +static void blake2s_set_lastblock(struct blake2s_state *state)
> +{
> +       state->f[0] = -1;
> +}
> +
> +static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc)
> +{
> +       state->t[0] += inc;
> +       state->t[1] += (state->t[0] < inc);
> +}
> +
> +static void blake2s_init_param(struct blake2s_state *state, const uint32_t param)
> +{
> +       int i;
> +
> +       memset(state, 0, sizeof(*state));
> +       for (i = 0; i < 8; ++i)
> +               state->h[i] = blake2s_iv[i];
> +       state->h[0] ^= param;
> +}
> +
> +static void blake2s_init(struct blake2s_state *state, const size_t outlen)
> +{
> +       blake2s_init_param(state, 0x01010000 | outlen);
> +       state->outlen = outlen;
> +}
> +
> +static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc)
> +{
> +       uint32_t m[16];
> +       uint32_t v[16];
> +       int i;
> +
> +       while (nblocks > 0) {
> +               blake2s_increment_counter(state, inc);
> +               memcpy(m, block, BLAKE2S_BLOCK_LEN);
> +               le32_to_cpu_array(m, ARRAY_SIZE(m));
> +               memcpy(v, state->h, 32);
> +               v[ 8] = blake2s_iv[0];
> +               v[ 9] = blake2s_iv[1];
> +               v[10] = blake2s_iv[2];
> +               v[11] = blake2s_iv[3];
> +               v[12] = blake2s_iv[4] ^ state->t[0];
> +               v[13] = blake2s_iv[5] ^ state->t[1];
> +               v[14] = blake2s_iv[6] ^ state->f[0];
> +               v[15] = blake2s_iv[7] ^ state->f[1];
> +
> +#define G(r, i, a, b, c, d) do { \
> +       a += b + m[blake2s_sigma[r][2 * i + 0]]; \
> +       d = ror32(d ^ a, 16); \
> +       c += d; \
> +       b = ror32(b ^ c, 12); \
> +       a += b + m[blake2s_sigma[r][2 * i + 1]]; \
> +       d = ror32(d ^ a, 8); \
> +       c += d; \
> +       b = ror32(b ^ c, 7); \
> +} while (0)
> +
> +#define ROUND(r) do { \
> +       G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
> +       G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
> +       G(r, 2, v[2], v[ 6], v[10], v[14]); \
> +       G(r, 3, v[3], v[ 7], v[11], v[15]); \
> +       G(r, 4, v[0], v[ 5], v[10], v[15]); \
> +       G(r, 5, v[1], v[ 6], v[11], v[12]); \
> +       G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
> +       G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
> +} while (0)
> +               ROUND(0);
> +               ROUND(1);
> +               ROUND(2);
> +               ROUND(3);
> +               ROUND(4);
> +               ROUND(5);
> +               ROUND(6);
> +               ROUND(7);
> +               ROUND(8);
> +               ROUND(9);
> +
> +#undef G
> +#undef ROUND
> +
> +               for (i = 0; i < 8; ++i)
> +                       state->h[i] ^= v[i] ^ v[i + 8];
> +
> +               block += BLAKE2S_BLOCK_LEN;
> +               --nblocks;
> +       }
> +}
> +
> +static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen)
> +{
> +       const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen;
> +       const uint8_t *in = inp;
> +
> +       if (!inlen)
> +               return;
> +       if (inlen > fill) {
> +               memcpy(state->buf + state->buflen, in, fill);
> +               blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
> +               state->buflen = 0;
> +               in += fill;
> +               inlen -= fill;
> +       }
> +       if (inlen > BLAKE2S_BLOCK_LEN) {
> +               const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
> +               blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
> +               in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
> +               inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
> +       }
> +       memcpy(state->buf + state->buflen, in, inlen);
> +       state->buflen += inlen;
> +}
> +
> +static void blake2s_final(struct blake2s_state *state, uint8_t *out)
> +{
> +       blake2s_set_lastblock(state);
> +       memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
> +       blake2s_compress(state, state->buf, 1, state->buflen);
> +       cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
> +       memcpy(out, state->h, state->outlen);
> +}
> +
> +static size_t determine_optimal_seed_len(void)
> +{
> +       size_t ret = 0;
> +       char poolsize_str[11] = { 0 };
> +       int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
> +
> +       if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
> +               fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno));
> +               ret = MIN_SEED_LEN;
> +       } else
> +               ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
> +       if (fd >= 0)
> +               close(fd);
> +       if (ret < MIN_SEED_LEN)
> +               ret = MIN_SEED_LEN;
> +       else if (ret > MAX_SEED_LEN)
> +               ret = MAX_SEED_LEN;
> +       return ret;
> +}
> +
> +static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable)
> +{
> +       ssize_t ret;
> +       int urandom_fd;
> +
> +       *is_creditable = false;
> +       ret = getrandom(seed, len, GRND_NONBLOCK);
> +       if (ret == (ssize_t)len) {
> +               *is_creditable = true;
> +               return 0;
> +       } else if (ret < 0 && errno == ENOSYS) {
> +               struct pollfd random_fd = {
> +                       .fd = open("/dev/random", O_RDONLY),
> +                       .events = POLLIN
> +               };
> +               if (random_fd.fd < 0)
> +                       return -errno;
> +               *is_creditable = poll(&random_fd, 1, 0) == 1;
> +               close(random_fd.fd);
> +       } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len)
> +               return 0;
> +       urandom_fd = open("/dev/urandom", O_RDONLY);
> +       if (urandom_fd < 0)
> +               return -errno;
> +       ret = read(urandom_fd, seed, len);
> +       if (ret == (ssize_t)len)
> +               ret = 0;
> +       else
> +               ret = -errno ? -errno : -EIO;
> +       close(urandom_fd);
> +       return ret;
> +}
> +
> +static int seed_rng(uint8_t *seed, size_t len, bool credit)
> +{
> +       struct {
> +               int entropy_count;
> +               int buf_size;
> +               uint8_t buffer[MAX_SEED_LEN];
> +       } req = {
> +               .entropy_count = credit ? len * 8 : 0,
> +               .buf_size = len
> +       };
> +       int random_fd, ret;
> +
> +       if (len > sizeof(req.buffer))
> +               return -EFBIG;
> +       memcpy(req.buffer, seed, len);
> +
> +       random_fd = open("/dev/random", O_RDWR);
> +       if (random_fd < 0)
> +               return -errno;
> +       ret = ioctl(random_fd, RNDADDENTROPY, &req);
> +       if (ret)
> +               ret = -errno ? -errno : -EIO;
> +       close(random_fd);
> +       return ret;
> +}
> +
> +static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash)
> +{
> +       uint8_t seed[MAX_SEED_LEN];
> +       ssize_t seed_len;
> +       int fd, dfd, ret = 0;
> +
> +       fd = open(filename, O_RDONLY);
> +       if (fd < 0 && errno == ENOENT)
> +               return 0;
> +       else if (fd < 0) {
> +               ret = -errno;
> +               fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno));
> +               return ret;
> +       }
> +       dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
> +       if (dfd < 0) {
> +               ret = -errno;
> +               close(fd);
> +               fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno));
> +               return ret;
> +       }
> +       seed_len = read(fd, seed, sizeof(seed));
> +       if (seed_len < 0) {
> +               ret = -errno;
> +               fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno));
> +       }
> +       close(fd);
> +       if (ret) {
> +               close(dfd);
> +               return ret;
> +       }
> +       if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) {
> +               ret = -errno;
> +               fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno));
> +       }
> +       close(dfd);
> +       if (ret)
> +               return ret;
> +       if (!seed_len)
> +               return 0;
> +
> +       blake2s_update(hash, &seed_len, sizeof(seed_len));
> +       blake2s_update(hash, seed, seed_len);
> +
> +       fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
> +       ret = seed_rng(seed, seed_len, credit);
> +       if (ret < 0)
> +               fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret));
> +       return ret;
> +}
> +
> +static bool skip_credit(void)
> +{
> +       const char *skip = getenv("SEEDRNG_SKIP_CREDIT");
> +       return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
> +                       !strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
> +}
> +
> +static void populate_global_paths(void)
> +{
> +       SEED_DIR = getenv("SEEDRNG_SEED_DIR");
> +       if (!SEED_DIR || !*SEED_DIR)
> +               SEED_DIR = "/var/lib/seedrng";
> +       LOCK_FILE = getenv("SEEDRNG_LOCK_FILE");
> +       if (!LOCK_FILE || !*LOCK_FILE)
> +               LOCK_FILE = "/var/run/seedrng.lock";
> +       if (asprintf(&CREDITABLE_SEED, "%s/seed.credit", SEED_DIR) < 0 ||
> +           asprintf(&NON_CREDITABLE_SEED, "%s/seed.no-credit", SEED_DIR) < 0) {
> +               fprintf(stderr, "ERROR: Unable to allocate paths: %s\n", strerror(errno));
> +               exit(1);
> +       }
> +}
> +
> +int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
> +{
> +       static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix";
> +       static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
> +       int ret, fd, lock, program_ret = 0;
> +       uint8_t new_seed[MAX_SEED_LEN];
> +       size_t new_seed_len;
> +       bool new_seed_creditable;
> +       struct timespec realtime = { 0 }, boottime = { 0 };
> +       struct blake2s_state hash;
> +
> +       umask(0077);
> +       if (getuid()) {
> +               fprintf(stderr, "ERROR: This program requires root\n");
> +               return 1;
> +       }
> +
> +       populate_global_paths();
> +       blake2s_init(&hash, BLAKE2S_HASH_LEN);
> +       blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
> +       clock_gettime(CLOCK_REALTIME, &realtime);
> +       clock_gettime(CLOCK_BOOTTIME, &boottime);
> +       blake2s_update(&hash, &realtime, sizeof(realtime));
> +       blake2s_update(&hash, &boottime, sizeof(boottime));
> +
> +       if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
> +               fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR, strerror(errno));
> +               return 1;
> +       }
> +
> +       lock = open(LOCK_FILE, O_WRONLY | O_CREAT, 0000);
> +       if (lock < 0 || flock(lock, LOCK_EX) < 0) {
> +               fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno));
> +               return 1;
> +       }
> +
> +       ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash);
> +       if (ret < 0)
> +               program_ret |= 1 << 1;
> +       ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash);
> +       if (ret < 0)
> +               program_ret |= 1 << 2;
> +
> +       new_seed_len = determine_optimal_seed_len();
> +       ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable);
> +       if (ret < 0) {
> +               fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret));
> +               new_seed_len = BLAKE2S_HASH_LEN;
> +               strncpy((char *)new_seed, seedrng_failure, new_seed_len);
> +               program_ret |= 1 << 3;
> +       }
> +       blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
> +       blake2s_update(&hash, new_seed, new_seed_len);
> +       blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
> +
> +       fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
> +       fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
> +       if (fd < 0) {
> +               fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno));
> +               program_ret |= 1 << 4;
> +               goto out;
> +       }
> +       if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
> +               fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno));
> +               program_ret |= 1 << 5;
> +               goto out;
> +       }
> +       if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) {
> +               fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno));
> +               program_ret |= 1 << 6;
> +       }
> +out:
> +       close(fd);
> +       close(lock);
> +       return program_ret;
> +}
> diff --git a/package/urandom-scripts/urandom-scripts.mk b/package/urandom-scripts/urandom-scripts.mk
> index 2c09728c46..e8526e248a 100644
> --- a/package/urandom-scripts/urandom-scripts.mk
> +++ b/package/urandom-scripts/urandom-scripts.mk
> @@ -4,7 +4,13 @@
>  #
>  ################################################################################
>
> +define URANDOM_SCRIPTS_BUILD_CMDS
> +       $(TARGET_CC) $(TARGET_CFLAGS) -std=gnu99 $(TARGET_LDFLAGS) \
> +               $(URANDOM_SCRIPTS_PKGDIR)/seedrng.c -o $(@D)/seedrng
> +endef
> +
>  define URANDOM_SCRIPTS_INSTALL_INIT_SYSV
> +       $(INSTALL) -D -m 0755 $(@D)/seedrng $(TARGET_DIR)/sbin/seedrng
>         $(INSTALL) -D -m 0755 $(URANDOM_SCRIPTS_PKGDIR)/S20urandom \
>                 $(TARGET_DIR)/etc/init.d/S20urandom
>  endef
> --
> 2.35.1
>
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-27 20:24           ` [Buildroot] [PATCH v2] " Jason A. Donenfeld
  2022-03-27 20:29             ` James Hilliard
@ 2022-03-29  5:04             ` Jason A. Donenfeld
  2022-03-29  6:12               ` David Laight
  2022-03-30 16:32               ` Peter Korsgaard
  1 sibling, 2 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-03-29  5:04 UTC (permalink / raw)
  To: James Hilliard, Arnout Vandecappelle, Yann E. MORIN, buildroot
  Cc: Jason A. Donenfeld

The RNG can't actually be seeded from a shell script, due to the
reliance on ioctls. For this reason, the seedrng project provides a
basic script meant to be copy and pasted into projects like buildroot
and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.

This commit imports it into buildroot and wires up the init scripts to
call it. This also is a significant improvement over the current init
script, which doesn't credit entropy and whose hashing in shell scripts
is sort of fragile.

As seedrng.c is a short tiny C program, we include this here in the
package, like a few other packages do. Later we'll investigate adding
this to busybox, but for now, this is a good start and a positive step
in the right direction.

Reviewed-by: James Hilliard <james.hilliard1@gmail.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
Changes v2->v3:
- Some small fixes on the exit path to be a bit cleaner.

 package/urandom-scripts/Config.in          |   4 -
 package/urandom-scripts/S20urandom         |  64 +--
 package/urandom-scripts/seedrng.c          | 458 +++++++++++++++++++++
 package/urandom-scripts/urandom-scripts.mk |   6 +
 4 files changed, 482 insertions(+), 50 deletions(-)
 create mode 100644 package/urandom-scripts/seedrng.c

diff --git a/package/urandom-scripts/Config.in b/package/urandom-scripts/Config.in
index 987e442e22..070ffa5e9a 100644
--- a/package/urandom-scripts/Config.in
+++ b/package/urandom-scripts/Config.in
@@ -4,7 +4,3 @@ config BR2_PACKAGE_URANDOM_SCRIPTS
 	depends on !BR2_PACKAGE_SYSTEMD
 	help
 	  Initscript to preserve the random seed between reboots.
-
-	  WARNING: this is a poor fit to try and get high-quality
-	  entropy at boot. There are better ways, like haveged, or
-	  rng-tools.
diff --git a/package/urandom-scripts/S20urandom b/package/urandom-scripts/S20urandom
index c6b2ebd48f..1959fad93b 100644
--- a/package/urandom-scripts/S20urandom
+++ b/package/urandom-scripts/S20urandom
@@ -6,63 +6,35 @@
 # Quietly do nothing if /dev/urandom does not exist
 [ -c /dev/urandom ] || exit 0
 
-URANDOM_SEED="/var/lib/random-seed"
+# The following knobs can be adjusted by placing uncommented modified lines
+# into /etc/default/urandom:
+#
+# Set these to change where the seed and runtime lock file are stored:
+#
+#     export SEEDRNG_SEED_DIR=/var/lib/seedrng
+#     export SEEDRNG_LOCK_FILE=/var/run/seedrng.lock
+#
+# Set this to true only if you do not want seed files to actually credit the
+# RNG, for example if you plan to replicate this file system image and do not
+# have the wherewithal to first delete the contents of /var/lib/seedrng:
+#
+#     export SEEDRNG_SKIP_CREDIT=false
+#
 
 # shellcheck source=/dev/null
 [ -r "/etc/default/urandom" ] && . "/etc/default/urandom"
 
-if pool_bits=$(cat /proc/sys/kernel/random/poolsize 2> /dev/null); then
-	pool_size=$((pool_bits/8))
-else
-	pool_size=512
-fi
-
-init_rng() {
-	printf 'Initializing random number generator: '
-	dd if="$URANDOM_SEED" bs="$pool_size" of=/dev/urandom count=1 2> /dev/null
-	status=$?
-	if [ "$status" -eq 0 ]; then
-		echo "OK"
-	else
-		echo "FAIL"
-	fi
-	return "$status"
-}
-
-save_random_seed() {
-	printf 'Saving random seed: '
-	status=1
-	if touch "$URANDOM_SEED.new" 2> /dev/null; then
-		old_umask=$(umask)
-		umask 077
-		dd if=/dev/urandom of="$URANDOM_SEED.tmp" bs="$pool_size" count=1 2> /dev/null
-		cat "$URANDOM_SEED" "$URANDOM_SEED.tmp" 2>/dev/null \
-			| sha256sum \
-			| cut -d ' ' -f 1 > "$URANDOM_SEED.new" && \
-		mv "$URANDOM_SEED.new" "$URANDOM_SEED" && status=0
-		rm -f "$URANDOM_SEED.tmp"
-		umask "$old_umask"
-		if [ "$status" -eq 0 ]; then
-			echo "OK"
-		else
-			echo "FAIL"
-		fi
-
-	else
-		echo "SKIP (read-only file system detected)"
-	fi
-	return "$status"
-}
-
 case "$1" in
 	start|restart|reload)
 		# Carry a random seed from start-up to start-up
 		# Load and then save the whole entropy pool
-		init_rng && save_random_seed;;
+		# Never fail, as this isn't worth making a fuss
+		# over if it doesn't go as planned.
+		seedrng || true;;
 	stop)
 		# Carry a random seed from shut-down to start-up
 		# Save the whole entropy pool
-		save_random_seed;;
+		seedrng;;
 	*)
 		echo "Usage: $0 {start|stop|restart|reload}"
 		exit 1
diff --git a/package/urandom-scripts/seedrng.c b/package/urandom-scripts/seedrng.c
new file mode 100644
index 0000000000..2a9df3c914
--- /dev/null
+++ b/package/urandom-scripts/seedrng.c
@@ -0,0 +1,458 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT OR Apache-2.0)
+/*
+ * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is based on code from <https://git.zx2c4.com/seedrng/about/>.
+ */
+
+#define _GNU_SOURCE
+#include <linux/random.h>
+#include <sys/random.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef GRND_INSECURE
+#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */
+#endif
+
+
+static const char *SEED_DIR;
+static const char *LOCK_FILE;
+static char *CREDITABLE_SEED;
+static char *NON_CREDITABLE_SEED;
+
+enum blake2s_lengths {
+	BLAKE2S_BLOCK_LEN = 64,
+	BLAKE2S_HASH_LEN = 32,
+	BLAKE2S_KEY_LEN = 32
+};
+
+enum seedrng_lengths {
+	MAX_SEED_LEN = 512,
+	MIN_SEED_LEN = BLAKE2S_HASH_LEN
+};
+
+struct blake2s_state {
+	uint32_t h[8];
+	uint32_t t[2];
+	uint32_t f[2];
+	uint8_t buf[BLAKE2S_BLOCK_LEN];
+	unsigned int buflen;
+	unsigned int outlen;
+};
+
+#define le32_to_cpup(a) le32toh(*(a))
+#define cpu_to_le32(a) htole32(a)
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#endif
+
+static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
+{
+        while (words--) {
+		*buf = cpu_to_le32(*buf);
+		++buf;
+	}
+}
+
+static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
+{
+        while (words--) {
+		*buf = le32_to_cpup(buf);
+		++buf;
+        }
+}
+
+static inline uint32_t ror32(uint32_t word, unsigned int shift)
+{
+	return (word >> (shift & 31)) | (word << ((-shift) & 31));
+}
+
+static const uint32_t blake2s_iv[8] = {
+	0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+	0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] = {
+	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+	{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+	{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+	{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+	{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+	{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+	{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+	{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+	{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+	{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+};
+
+static void blake2s_set_lastblock(struct blake2s_state *state)
+{
+	state->f[0] = -1;
+}
+
+static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc)
+{
+	state->t[0] += inc;
+	state->t[1] += (state->t[0] < inc);
+}
+
+static void blake2s_init_param(struct blake2s_state *state, const uint32_t param)
+{
+	int i;
+
+	memset(state, 0, sizeof(*state));
+	for (i = 0; i < 8; ++i)
+		state->h[i] = blake2s_iv[i];
+	state->h[0] ^= param;
+}
+
+static void blake2s_init(struct blake2s_state *state, const size_t outlen)
+{
+	blake2s_init_param(state, 0x01010000 | outlen);
+	state->outlen = outlen;
+}
+
+static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc)
+{
+	uint32_t m[16];
+	uint32_t v[16];
+	int i;
+
+	while (nblocks > 0) {
+		blake2s_increment_counter(state, inc);
+		memcpy(m, block, BLAKE2S_BLOCK_LEN);
+		le32_to_cpu_array(m, ARRAY_SIZE(m));
+		memcpy(v, state->h, 32);
+		v[ 8] = blake2s_iv[0];
+		v[ 9] = blake2s_iv[1];
+		v[10] = blake2s_iv[2];
+		v[11] = blake2s_iv[3];
+		v[12] = blake2s_iv[4] ^ state->t[0];
+		v[13] = blake2s_iv[5] ^ state->t[1];
+		v[14] = blake2s_iv[6] ^ state->f[0];
+		v[15] = blake2s_iv[7] ^ state->f[1];
+
+#define G(r, i, a, b, c, d) do { \
+	a += b + m[blake2s_sigma[r][2 * i + 0]]; \
+	d = ror32(d ^ a, 16); \
+	c += d; \
+	b = ror32(b ^ c, 12); \
+	a += b + m[blake2s_sigma[r][2 * i + 1]]; \
+	d = ror32(d ^ a, 8); \
+	c += d; \
+	b = ror32(b ^ c, 7); \
+} while (0)
+
+#define ROUND(r) do { \
+	G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
+	G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
+	G(r, 2, v[2], v[ 6], v[10], v[14]); \
+	G(r, 3, v[3], v[ 7], v[11], v[15]); \
+	G(r, 4, v[0], v[ 5], v[10], v[15]); \
+	G(r, 5, v[1], v[ 6], v[11], v[12]); \
+	G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
+	G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
+} while (0)
+		ROUND(0);
+		ROUND(1);
+		ROUND(2);
+		ROUND(3);
+		ROUND(4);
+		ROUND(5);
+		ROUND(6);
+		ROUND(7);
+		ROUND(8);
+		ROUND(9);
+
+#undef G
+#undef ROUND
+
+		for (i = 0; i < 8; ++i)
+			state->h[i] ^= v[i] ^ v[i + 8];
+
+		block += BLAKE2S_BLOCK_LEN;
+		--nblocks;
+	}
+}
+
+static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen)
+{
+	const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen;
+	const uint8_t *in = inp;
+
+	if (!inlen)
+		return;
+	if (inlen > fill) {
+		memcpy(state->buf + state->buflen, in, fill);
+		blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
+		state->buflen = 0;
+		in += fill;
+		inlen -= fill;
+	}
+	if (inlen > BLAKE2S_BLOCK_LEN) {
+		const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
+		blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
+		in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
+		inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
+	}
+	memcpy(state->buf + state->buflen, in, inlen);
+	state->buflen += inlen;
+}
+
+static void blake2s_final(struct blake2s_state *state, uint8_t *out)
+{
+	blake2s_set_lastblock(state);
+	memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
+	blake2s_compress(state, state->buf, 1, state->buflen);
+	cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
+	memcpy(out, state->h, state->outlen);
+}
+
+static size_t determine_optimal_seed_len(void)
+{
+	size_t ret = 0;
+	char poolsize_str[11] = { 0 };
+	int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
+
+	if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
+		fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno));
+		ret = MIN_SEED_LEN;
+	} else
+		ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
+	if (fd >= 0)
+		close(fd);
+	if (ret < MIN_SEED_LEN)
+		ret = MIN_SEED_LEN;
+	else if (ret > MAX_SEED_LEN)
+		ret = MAX_SEED_LEN;
+	return ret;
+}
+
+static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable)
+{
+	ssize_t ret;
+	int urandom_fd;
+
+	*is_creditable = false;
+	ret = getrandom(seed, len, GRND_NONBLOCK);
+	if (ret == (ssize_t)len) {
+		*is_creditable = true;
+		return 0;
+	} else if (ret < 0 && errno == ENOSYS) {
+		struct pollfd random_fd = {
+			.fd = open("/dev/random", O_RDONLY),
+			.events = POLLIN
+		};
+		if (random_fd.fd < 0)
+			return -errno;
+		*is_creditable = poll(&random_fd, 1, 0) == 1;
+		close(random_fd.fd);
+	} else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len)
+		return 0;
+	urandom_fd = open("/dev/urandom", O_RDONLY);
+	if (urandom_fd < 0)
+		return -errno;
+	ret = read(urandom_fd, seed, len);
+	if (ret == (ssize_t)len)
+		ret = 0;
+	else
+		ret = -errno ? -errno : -EIO;
+	close(urandom_fd);
+	return ret;
+}
+
+static int seed_rng(uint8_t *seed, size_t len, bool credit)
+{
+	struct {
+		int entropy_count;
+		int buf_size;
+		uint8_t buffer[MAX_SEED_LEN];
+	} req = {
+		.entropy_count = credit ? len * 8 : 0,
+		.buf_size = len
+	};
+	int random_fd, ret;
+
+	if (len > sizeof(req.buffer))
+		return -EFBIG;
+	memcpy(req.buffer, seed, len);
+
+	random_fd = open("/dev/random", O_RDWR);
+	if (random_fd < 0)
+		return -errno;
+	ret = ioctl(random_fd, RNDADDENTROPY, &req);
+	if (ret)
+		ret = -errno ? -errno : -EIO;
+	close(random_fd);
+	return ret;
+}
+
+static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash)
+{
+	uint8_t seed[MAX_SEED_LEN];
+	ssize_t seed_len;
+	int fd, dfd, ret = 0;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0 && errno == ENOENT)
+		return 0;
+	else if (fd < 0) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno));
+		return ret;
+	}
+	dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
+	if (dfd < 0) {
+		ret = -errno;
+		close(fd);
+		fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno));
+		return ret;
+	}
+	seed_len = read(fd, seed, sizeof(seed));
+	if (seed_len < 0) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno));
+	}
+	close(fd);
+	if (ret) {
+		close(dfd);
+		return ret;
+	}
+	if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno));
+	}
+	close(dfd);
+	if (ret)
+		return ret;
+	if (!seed_len)
+		return 0;
+
+	blake2s_update(hash, &seed_len, sizeof(seed_len));
+	blake2s_update(hash, seed, seed_len);
+
+	fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
+	ret = seed_rng(seed, seed_len, credit);
+	if (ret < 0)
+		fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret));
+	return ret;
+}
+
+static bool skip_credit(void)
+{
+	const char *skip = getenv("SEEDRNG_SKIP_CREDIT");
+	return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
+			!strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
+}
+
+static void populate_global_paths(void)
+{
+	SEED_DIR = getenv("SEEDRNG_SEED_DIR");
+	if (!SEED_DIR || !*SEED_DIR)
+		SEED_DIR = "/var/lib/seedrng";
+	LOCK_FILE = getenv("SEEDRNG_LOCK_FILE");
+	if (!LOCK_FILE || !*LOCK_FILE)
+		LOCK_FILE = "/var/run/seedrng.lock";
+	if (asprintf(&CREDITABLE_SEED, "%s/seed.credit", SEED_DIR) < 0 ||
+	    asprintf(&NON_CREDITABLE_SEED, "%s/seed.no-credit", SEED_DIR) < 0) {
+		fprintf(stderr, "ERROR: Unable to allocate paths: %s\n", strerror(errno));
+		exit(1);
+	}
+}
+
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+	static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix";
+	static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
+	int ret, fd = -1, lock, program_ret = 0;
+	uint8_t new_seed[MAX_SEED_LEN];
+	size_t new_seed_len;
+	bool new_seed_creditable;
+	struct timespec realtime = { 0 }, boottime = { 0 };
+	struct blake2s_state hash;
+
+	umask(0077);
+	if (getuid()) {
+		fprintf(stderr, "ERROR: This program requires root\n");
+		return 1;
+	}
+
+	populate_global_paths();
+	blake2s_init(&hash, BLAKE2S_HASH_LEN);
+	blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
+	clock_gettime(CLOCK_REALTIME, &realtime);
+	clock_gettime(CLOCK_BOOTTIME, &boottime);
+	blake2s_update(&hash, &realtime, sizeof(realtime));
+	blake2s_update(&hash, &boottime, sizeof(boottime));
+
+	if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
+		fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR, strerror(errno));
+		return 1;
+	}
+
+	lock = open(LOCK_FILE, O_WRONLY | O_CREAT, 0000);
+	if (lock < 0 || flock(lock, LOCK_EX) < 0) {
+		fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno));
+		program_ret = 1;
+		goto out;
+	}
+
+	ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash);
+	if (ret < 0)
+		program_ret |= 1 << 1;
+	ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash);
+	if (ret < 0)
+		program_ret |= 1 << 2;
+
+	new_seed_len = determine_optimal_seed_len();
+	ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable);
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret));
+		new_seed_len = BLAKE2S_HASH_LEN;
+		strncpy((char *)new_seed, seedrng_failure, new_seed_len);
+		program_ret |= 1 << 3;
+	}
+	blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
+	blake2s_update(&hash, new_seed, new_seed_len);
+	blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
+
+	fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
+	fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno));
+		program_ret |= 1 << 4;
+		goto out;
+	}
+	if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
+		fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno));
+		program_ret |= 1 << 5;
+		goto out;
+	}
+	if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) {
+		fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno));
+		program_ret |= 1 << 6;
+	}
+out:
+	if (fd >= 0)
+		close(fd);
+	if (lock >= 0)
+		close(lock);
+	return program_ret;
+}
diff --git a/package/urandom-scripts/urandom-scripts.mk b/package/urandom-scripts/urandom-scripts.mk
index 2c09728c46..e8526e248a 100644
--- a/package/urandom-scripts/urandom-scripts.mk
+++ b/package/urandom-scripts/urandom-scripts.mk
@@ -4,7 +4,13 @@
 #
 ################################################################################
 
+define URANDOM_SCRIPTS_BUILD_CMDS
+	$(TARGET_CC) $(TARGET_CFLAGS) -std=gnu99 $(TARGET_LDFLAGS) \
+		$(URANDOM_SCRIPTS_PKGDIR)/seedrng.c -o $(@D)/seedrng
+endef
+
 define URANDOM_SCRIPTS_INSTALL_INIT_SYSV
+	$(INSTALL) -D -m 0755 $(@D)/seedrng $(TARGET_DIR)/sbin/seedrng
 	$(INSTALL) -D -m 0755 $(URANDOM_SCRIPTS_PKGDIR)/S20urandom \
 		$(TARGET_DIR)/etc/init.d/S20urandom
 endef
-- 
2.35.1

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-29  5:04             ` [Buildroot] [PATCH v3] " Jason A. Donenfeld
@ 2022-03-29  6:12               ` David Laight
  2022-03-30 16:32               ` Peter Korsgaard
  1 sibling, 0 replies; 34+ messages in thread
From: David Laight @ 2022-03-29  6:12 UTC (permalink / raw)
  To: 'Jason A. Donenfeld',
	James Hilliard, Arnout Vandecappelle, Yann E. MORIN, buildroot

From: Jason A. Donenfeld
> Sent: 29 March 2022 06:04
> 
> The RNG can't actually be seeded from a shell script, due to the
> reliance on ioctls. For this reason, the seedrng project provides a
> basic script meant to be copy and pasted into projects like buildroot
> and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
> 
> This commit imports it into buildroot and wires up the init scripts to
> call it. This also is a significant improvement over the current init
> script, which doesn't credit entropy and whose hashing in shell scripts
> is sort of fragile.
> 
> As seedrng.c is a short tiny C program, we include this here in the
> package, like a few other packages do. Later we'll investigate adding
> this to busybox, but for now, this is a good start and a positive step
> in the right direction.

It isn't that tiny, this bloats out to quite a lot of code.

> +#define G(r, i, a, b, c, d) do { \
> +	a += b + m[blake2s_sigma[r][2 * i + 0]]; \
> +	d = ror32(d ^ a, 16); \
> +	c += d; \
> +	b = ror32(b ^ c, 12); \
> +	a += b + m[blake2s_sigma[r][2 * i + 1]]; \
> +	d = ror32(d ^ a, 8); \
> +	c += d; \
> +	b = ror32(b ^ c, 7); \
> +} while (0)
> +
> +#define ROUND(r) do { \
> +	G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
> +	G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
> +	G(r, 2, v[2], v[ 6], v[10], v[14]); \
> +	G(r, 3, v[3], v[ 7], v[11], v[15]); \
> +	G(r, 4, v[0], v[ 5], v[10], v[15]); \
> +	G(r, 5, v[1], v[ 6], v[11], v[12]); \
> +	G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
> +	G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
> +} while (0)
> +		ROUND(0);
> +		ROUND(1);
> +		ROUND(2);
> +		ROUND(3);
> +		ROUND(4);
> +		ROUND(5);
> +		ROUND(6);
> +		ROUND(7);
> +		ROUND(8);
> +		ROUND(9);

I've not looked at why the code is doing this.
If you are feeding data that has come from the RNG back in
as state/entropy why does it contain a copy of blake2?

I was expecting to see a program that copied stdin to /dev/urandom
in a manner that actually credited entropy.
About 10 lines of code.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-29  5:04             ` [Buildroot] [PATCH v3] " Jason A. Donenfeld
  2022-03-29  6:12               ` David Laight
@ 2022-03-30 16:32               ` Peter Korsgaard
  2022-03-30 16:57                 ` David Laight
  2022-03-30 17:13                 ` Jason A. Donenfeld
  1 sibling, 2 replies; 34+ messages in thread
From: Peter Korsgaard @ 2022-03-30 16:32 UTC (permalink / raw)
  To: Jason A. Donenfeld; +Cc: James Hilliard, Yann E. MORIN, buildroot

>>>>> "Jason" == Jason A Donenfeld <Jason@zx2c4.com> writes:

Hi,

 > The RNG can't actually be seeded from a shell script, due to the
 > reliance on ioctls. For this reason, the seedrng project provides a
 > basic script meant to be copy and pasted into projects like buildroot
 > and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.

 > This commit imports it into buildroot and wires up the init scripts to
 > call it. This also is a significant improvement over the current init
 > script, which doesn't credit entropy and whose hashing in shell scripts
 > is sort of fragile.

 > As seedrng.c is a short tiny C program, we include this here in the
 > package, like a few other packages do. Later we'll investigate adding
 > this to busybox, but for now, this is a good start and a positive step
 > in the right direction.

As discussed on IRC, I think it would be nicer to just add a normal
seedrng package and depend on that from urandom-scripts, so the standard
version/license info/.. stuff works. I can do that change if needed.

You mentioned something on IRC about this containing differences from
what it is seedrng.git. Those changes/fixes are presumably not specific
to Buildroot, will you add those changes to the git repo as well?


 > +# The following knobs can be adjusted by placing uncommented modified lines
 > +# into /etc/default/urandom:
 > +#
 > +# Set these to change where the seed and runtime lock file are stored:
 > +#
 > +#     export SEEDRNG_SEED_DIR=/var/lib/seedrng
 > +#     export SEEDRNG_LOCK_FILE=/var/run/seedrng.lock
 > +#

Will you add the logic for these features to seedrng.git? If not, then I
don't think we should have them here either. A bunch of other code already
expects "normal" locations, so people generally have to handle such
customizations by symlinks in their rootfs(-overlay).

If this customization is left in, then it should ideally use the
existing (directory part of) URANDOM_SEED, which used to be how the
location could be customized for backwards compatibility.


 >  case "$1" in
 >  	start|restart|reload)
 >  		# Carry a random seed from start-up to start-up
 >  		# Load and then save the whole entropy pool
 > -		init_rng && save_random_seed;;
 > +		# Never fail, as this isn't worth making a fuss
 > +		# over if it doesn't go as planned.
 > +		seedrng || true;;
 >  	stop)
 >  		# Carry a random seed from shut-down to start-up
 >  		# Save the whole entropy pool
 > -		save_random_seed;;
 > +		seedrng;;

Nice and short! The comments above are not really correct any more, as
the stop logic also loads.


 > +++ b/package/urandom-scripts/seedrng.c
..

 > +#define le32_to_cpup(a) le32toh(*(a))
 > +#define cpu_to_le32(a) htole32(a)

What is the purpose of the to/from le32 stuff in this use case?

 > +
 > +	fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
 > +	fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);

The 0400 confused me a bit, as that would normally cause the open to
fail when the file exists, but it happens to work as you unlink above /
use the lock file.


> +	if (fd < 0) {
 > +		fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno));
 > +		program_ret |= 1 << 4;
 > +		goto out;
 > +	}
 > +	if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
 > +		fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno));
 > +		program_ret |= 1 << 5;
 > +		goto out;
 > +	}
 > +	if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) {
 > +		fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno));
 > +		program_ret |= 1 << 6;
 > +	}
 > +out:
 > +	if (fd >= 0)
 > +		close(fd);
 > +	if (lock >= 0)
 > +		close(lock);

No fsync of the directory like you do in seed_from_file_if_exists()?

-- 
Bye, Peter Korsgaard
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-30 16:32               ` Peter Korsgaard
@ 2022-03-30 16:57                 ` David Laight
  2022-03-30 17:13                 ` Jason A. Donenfeld
  1 sibling, 0 replies; 34+ messages in thread
From: David Laight @ 2022-03-30 16:57 UTC (permalink / raw)
  To: 'Peter Korsgaard', Jason A. Donenfeld
  Cc: James Hilliard, Yann E. MORIN, buildroot

From: Peter Korsgaard
> Sent: 30 March 2022 17:33
> 
> Hi,
> 
>  > The RNG can't actually be seeded from a shell script, due to the
>  > reliance on ioctls. For this reason, the seedrng project provides a
>  > basic script meant to be copy and pasted into projects like buildroot
>  > and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
> 
>  > This commit imports it into buildroot and wires up the init scripts to
>  > call it. This also is a significant improvement over the current init
>  > script, which doesn't credit entropy and whose hashing in shell scripts
>  > is sort of fragile.
> 
>  > As seedrng.c is a short tiny C program, we include this here in the
>  > package, like a few other packages do. Later we'll investigate adding
>  > this to busybox, but for now, this is a good start and a positive step
>  > in the right direction.
> 
> As discussed on IRC, I think it would be nicer to just add a normal
> seedrng package and depend on that from urandom-scripts, so the standard
> version/license info/.. stuff works. I can do that change if needed.
> 
> You mentioned something on IRC about this containing differences from
> what it is seedrng.git. Those changes/fixes are presumably not specific
> to Buildroot, will you add those changes to the git repo as well?

I remember this 'not so little' program limiting the amount of data
saved and restored to a fairly small amount.
I think it referred to a limit in the kernel.

I just wrote a 10 line program to copy a file to /dev/urandom
using the ioctl so that the 'entropy' is credited.
AFAICT the kernel copied all the data into the pool and would
credit quite a lot of entropy.
There is a loop that uses an 128 byte on-stack buffer.
But it is a loop.

Another thing I noticed is that the script is S20urandom.
This runs rather late, things like udev have already tried
to read random numbers.
The script really needs to run much sooner.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-30 16:32               ` Peter Korsgaard
  2022-03-30 16:57                 ` David Laight
@ 2022-03-30 17:13                 ` Jason A. Donenfeld
  2022-03-31 14:50                   ` Jason A. Donenfeld
                                     ` (3 more replies)
  1 sibling, 4 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-03-30 17:13 UTC (permalink / raw)
  To: Peter Korsgaard; +Cc: James Hilliard, Yann E. MORIN, buildroot

Hi Peter,

On 3/30/22, Peter Korsgaard <peter@korsgaard.com> wrote:
>>>>>> "Jason" == Jason A Donenfeld <Jason@zx2c4.com> writes:
>
> Hi,
>
>  > The RNG can't actually be seeded from a shell script, due to the
>  > reliance on ioctls. For this reason, the seedrng project provides a
>  > basic script meant to be copy and pasted into projects like buildroot
>  > and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
>
>  > This commit imports it into buildroot and wires up the init scripts to
>  > call it. This also is a significant improvement over the current init
>  > script, which doesn't credit entropy and whose hashing in shell scripts
>  > is sort of fragile.
>
>  > As seedrng.c is a short tiny C program, we include this here in the
>  > package, like a few other packages do. Later we'll investigate adding
>  > this to busybox, but for now, this is a good start and a positive step
>  > in the right direction.
>
> As discussed on IRC, I think it would be nicer to just add a normal
> seedrng package and depend on that from urandom-scripts, so the standard
> version/license info/.. stuff works. I can do that change if needed.
>
> You mentioned something on IRC about this containing differences from
> what it is seedrng.git. Those changes/fixes are presumably not specific
> to Buildroot, will you add those changes to the git repo as well?

Absolutely not. This 100% does not work for me. I have made it
abundantly clear to you that seedrng is code that's meant to be copied
into individual distros and tweaked as needed, which I've done for
Buildroot. Do not attempt to package it from any repos I may have. If
you try to do that, I will intentionally interfere with those
operations server side, and take whatever other adversarial steps to
ensure my server is not misused for that purpose. I also will not
incorporate buildroot-specific tweaks into the sample code repo.

That repo supplies sample code. After Yann's absence, I took the
necessary steps that a maintainer like him would have done to
customize it to Buildroot's needs, incorporating it into the project,
and taking into account James' comments. You now have the result of
that work here, and I think it'd be silly not to take it.

I am offering to maintain this code inside of Buildroot for you. I'm
generally very much on top of things in that regard. I'm not offering
to start and maintain an external project at your request, no matter
how much pressure and conditionalization you apply on this mailing
list or IRC. That's not work I'll do for you. However, again, I am
offering to maintain this for you inside the buildroot repo as this
patch does.

That's my offer; take it or leave it.


>
>
>  > +# The following knobs can be adjusted by placing uncommented modified
> lines
>  > +# into /etc/default/urandom:
>  > +#
>  > +# Set these to change where the seed and runtime lock file are stored:
>  > +#
>  > +#     export SEEDRNG_SEED_DIR=/var/lib/seedrng
>  > +#     export SEEDRNG_LOCK_FILE=/var/run/seedrng.lock
>  > +#
>
> Will you add the logic for these features to seedrng.git? If not, then I
> don't think we should have them here either. A bunch of other code already
> expects "normal" locations, so people generally have to handle such
> customizations by symlinks in their rootfs(-overlay).
>
> If this customization is left in, then it should ideally use the
> existing (directory part of) URANDOM_SEED, which used to be how the
> location could be customized for backwards compatibility.

As mentioned, no tweaks go up to seedrng.git. That's sample code.

I'm inclined to agree with James' assessment about this, that
Buildroot needs it to be customizable. Happy to send a v4 of this
patch adjusting the defaults to have paths similar to what was there
before.

>
>
>  >  case "$1" in
>  >  	start|restart|reload)
>  >  		# Carry a random seed from start-up to start-up
>  >  		# Load and then save the whole entropy pool
>  > -		init_rng && save_random_seed;;
>  > +		# Never fail, as this isn't worth making a fuss
>  > +		# over if it doesn't go as planned.
>  > +		seedrng || true;;
>  >  	stop)
>  >  		# Carry a random seed from shut-down to start-up
>  >  		# Save the whole entropy pool
>  > -		save_random_seed;;
>  > +		seedrng;;
>
> Nice and short! The comments above are not really correct any more, as
> the stop logic also loads.

Ack, will get rid of those comments for v4.

>
>
>  > +++ b/package/urandom-scripts/seedrng.c
> ..
>
>  > +#define le32_to_cpup(a) le32toh(*(a))
>  > +#define cpu_to_le32(a) htole32(a)
>
> What is the purpose of the to/from le32 stuff in this use case?

So that blake2s passes its test vectors. We're not about to go messing
around with the internal encoding of a hash function. B2s specifies
little endian, so that's what we use.

>
>  > +
>  > +	fprintf(stdout, "Saving %zu bits of %s seed for next boot\n",
> new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
>  > +	fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
>
> The 0400 confused me a bit, as that would normally cause the open to
> fail when the file exists, but it happens to work as you unlink above /
> use the lock file.
>
>
>> +	if (fd < 0) {
>  > +		fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n",
> strerror(errno));
>  > +		program_ret |= 1 << 4;
>  > +		goto out;
>  > +	}
>  > +	if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len ||
> fsync(fd) < 0) {
>  > +		fprintf(stderr, "ERROR: Unable to write seed file: %s\n",
> strerror(errno));
>  > +		program_ret |= 1 << 5;
>  > +		goto out;
>  > +	}
>  > +	if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED)
> < 0) {
>  > +		fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n",
> strerror(errno));
>  > +		program_ret |= 1 << 6;
>  > +	}
>  > +out:
>  > +	if (fd >= 0)
>  > +		close(fd);
>  > +	if (lock >= 0)
>  > +		close(lock);
>
> No fsync of the directory like you do in seed_from_file_if_exists()?

No, it's not necessary here, because this is a transition from
no-credit to credit, so if the power clicks off at the bad moment, it
fails closed instead of open.
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-30 17:13                 ` Jason A. Donenfeld
@ 2022-03-31 14:50                   ` Jason A. Donenfeld
  2022-03-31 14:57                   ` [Buildroot] [PATCH v4] " Jason A. Donenfeld
                                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-03-31 14:50 UTC (permalink / raw)
  To: Peter Korsgaard; +Cc: James Hilliard, Yann E. MORIN, buildroot

On Wed, Mar 30, 2022 at 1:13 PM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
> > If this customization is left in, then it should ideally use the
> > existing (directory part of) URANDOM_SEED, which used to be how the
> > location could be customized for backwards compatibility.
>
> As mentioned, no tweaks go up to seedrng.git. That's sample code.
>
> I'm inclined to agree with James' assessment about this, that
> Buildroot needs it to be customizable. Happy to send a v4 of this
> patch adjusting the defaults to have paths similar to what was there
> before.

FYI, I just noticed the old code writes to the file
/var/lib/random-seed. The directory part is /var/lib. Therefore
/var/lib/seedrng/* shares that same directory part. So I think what's
there now fits what you want already? I'll leave that change out of
v4.
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [Buildroot] [PATCH v4] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-30 17:13                 ` Jason A. Donenfeld
  2022-03-31 14:50                   ` Jason A. Donenfeld
@ 2022-03-31 14:57                   ` Jason A. Donenfeld
  2022-03-31 15:16                     ` David Laight
  2022-03-31 17:11                   ` [Buildroot] [PATCH v3] " Peter Korsgaard
  2022-04-01 10:57                   ` James Hilliard
  3 siblings, 1 reply; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-03-31 14:57 UTC (permalink / raw)
  To: Peter Korsgaard, buildroot; +Cc: Jason A. Donenfeld, James Hilliard

The RNG can't actually be seeded from a shell script, due to the
reliance on ioctls. For this reason, the seedrng project provides a
basic script meant to be copy and pasted into projects like buildroot
and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.

This commit imports it into buildroot and wires up the init scripts to
call it. This also is a significant improvement over the current init
script, which doesn't credit entropy and whose hashing in shell scripts
is sort of fragile.

As seedrng.c is a short tiny C program, we include this here in the
package, like a few other packages do. Later we'll investigate adding
this to busybox, but for now, this is a good start and a positive step
in the right direction.

Reviewed-by: James Hilliard <james.hilliard1@gmail.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
Changes v3->v4:
- Remove unneeded whitespace.
- Remove check for /dev/urandom in script file.
- Remove outdated comments and group start and stop commands.
- Additionally license under public domain (CC0) and 1-clause BSD,
  to make bringing this into buildroot's tree as easy as possible.

 package/urandom-scripts/Config.in          |   4 -
 package/urandom-scripts/S20urandom         |  73 +---
 package/urandom-scripts/seedrng.c          | 457 +++++++++++++++++++++
 package/urandom-scripts/urandom-scripts.mk |   6 +
 4 files changed, 481 insertions(+), 59 deletions(-)
 create mode 100644 package/urandom-scripts/seedrng.c

diff --git a/package/urandom-scripts/Config.in b/package/urandom-scripts/Config.in
index 987e442e22..070ffa5e9a 100644
--- a/package/urandom-scripts/Config.in
+++ b/package/urandom-scripts/Config.in
@@ -4,7 +4,3 @@ config BR2_PACKAGE_URANDOM_SCRIPTS
 	depends on !BR2_PACKAGE_SYSTEMD
 	help
 	  Initscript to preserve the random seed between reboots.
-
-	  WARNING: this is a poor fit to try and get high-quality
-	  entropy at boot. There are better ways, like haveged, or
-	  rng-tools.
diff --git a/package/urandom-scripts/S20urandom b/package/urandom-scripts/S20urandom
index c6b2ebd48f..1bdb19979a 100644
--- a/package/urandom-scripts/S20urandom
+++ b/package/urandom-scripts/S20urandom
@@ -3,66 +3,29 @@
 # Preserve the random seed between reboots. See urandom(4).
 #
 
-# Quietly do nothing if /dev/urandom does not exist
-[ -c /dev/urandom ] || exit 0
-
-URANDOM_SEED="/var/lib/random-seed"
+# The following knobs can be adjusted by placing uncommented modified lines
+# into /etc/default/urandom:
+#
+# Set these to change where the seed and runtime lock file are stored:
+#
+#     export SEEDRNG_SEED_DIR=/var/lib/seedrng
+#     export SEEDRNG_LOCK_FILE=/var/run/seedrng.lock
+#
+# Set this to true only if you do not want seed files to actually credit the
+# RNG, for example if you plan to replicate this file system image and do not
+# have the wherewithal to first delete the contents of /var/lib/seedrng:
+#
+#     export SEEDRNG_SKIP_CREDIT=false
+#
 
 # shellcheck source=/dev/null
 [ -r "/etc/default/urandom" ] && . "/etc/default/urandom"
 
-if pool_bits=$(cat /proc/sys/kernel/random/poolsize 2> /dev/null); then
-	pool_size=$((pool_bits/8))
-else
-	pool_size=512
-fi
-
-init_rng() {
-	printf 'Initializing random number generator: '
-	dd if="$URANDOM_SEED" bs="$pool_size" of=/dev/urandom count=1 2> /dev/null
-	status=$?
-	if [ "$status" -eq 0 ]; then
-		echo "OK"
-	else
-		echo "FAIL"
-	fi
-	return "$status"
-}
-
-save_random_seed() {
-	printf 'Saving random seed: '
-	status=1
-	if touch "$URANDOM_SEED.new" 2> /dev/null; then
-		old_umask=$(umask)
-		umask 077
-		dd if=/dev/urandom of="$URANDOM_SEED.tmp" bs="$pool_size" count=1 2> /dev/null
-		cat "$URANDOM_SEED" "$URANDOM_SEED.tmp" 2>/dev/null \
-			| sha256sum \
-			| cut -d ' ' -f 1 > "$URANDOM_SEED.new" && \
-		mv "$URANDOM_SEED.new" "$URANDOM_SEED" && status=0
-		rm -f "$URANDOM_SEED.tmp"
-		umask "$old_umask"
-		if [ "$status" -eq 0 ]; then
-			echo "OK"
-		else
-			echo "FAIL"
-		fi
-
-	else
-		echo "SKIP (read-only file system detected)"
-	fi
-	return "$status"
-}
-
 case "$1" in
-	start|restart|reload)
-		# Carry a random seed from start-up to start-up
-		# Load and then save the whole entropy pool
-		init_rng && save_random_seed;;
-	stop)
-		# Carry a random seed from shut-down to start-up
-		# Save the whole entropy pool
-		save_random_seed;;
+	start|stop|restart|reload)
+		# Never fail, as this isn't worth making a fuss
+		# over if it doesn't go as planned.
+		seedrng || true;;
 	*)
 		echo "Usage: $0 {start|stop|restart|reload}"
 		exit 1
diff --git a/package/urandom-scripts/seedrng.c b/package/urandom-scripts/seedrng.c
new file mode 100644
index 0000000000..7793c7338e
--- /dev/null
+++ b/package/urandom-scripts/seedrng.c
@@ -0,0 +1,457 @@
+// SPDX-License-Identifier: (GPL-2.0 OR Apache-2.0 OR MIT OR BSD-1-Clause OR CC0-1.0)
+/*
+ * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is based on code from <https://git.zx2c4.com/seedrng/about/>.
+ */
+
+#define _GNU_SOURCE
+#include <linux/random.h>
+#include <sys/random.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <endian.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef GRND_INSECURE
+#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */
+#endif
+
+static const char *SEED_DIR;
+static const char *LOCK_FILE;
+static char *CREDITABLE_SEED;
+static char *NON_CREDITABLE_SEED;
+
+enum blake2s_lengths {
+	BLAKE2S_BLOCK_LEN = 64,
+	BLAKE2S_HASH_LEN = 32,
+	BLAKE2S_KEY_LEN = 32
+};
+
+enum seedrng_lengths {
+	MAX_SEED_LEN = 512,
+	MIN_SEED_LEN = BLAKE2S_HASH_LEN
+};
+
+struct blake2s_state {
+	uint32_t h[8];
+	uint32_t t[2];
+	uint32_t f[2];
+	uint8_t buf[BLAKE2S_BLOCK_LEN];
+	unsigned int buflen;
+	unsigned int outlen;
+};
+
+#define le32_to_cpup(a) le32toh(*(a))
+#define cpu_to_le32(a) htole32(a)
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+#ifndef DIV_ROUND_UP
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#endif
+
+static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
+{
+        while (words--) {
+		*buf = cpu_to_le32(*buf);
+		++buf;
+	}
+}
+
+static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
+{
+        while (words--) {
+		*buf = le32_to_cpup(buf);
+		++buf;
+        }
+}
+
+static inline uint32_t ror32(uint32_t word, unsigned int shift)
+{
+	return (word >> (shift & 31)) | (word << ((-shift) & 31));
+}
+
+static const uint32_t blake2s_iv[8] = {
+	0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+	0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] = {
+	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+	{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
+	{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
+	{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
+	{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
+	{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
+	{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
+	{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
+	{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
+	{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
+};
+
+static void blake2s_set_lastblock(struct blake2s_state *state)
+{
+	state->f[0] = -1;
+}
+
+static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc)
+{
+	state->t[0] += inc;
+	state->t[1] += (state->t[0] < inc);
+}
+
+static void blake2s_init_param(struct blake2s_state *state, const uint32_t param)
+{
+	int i;
+
+	memset(state, 0, sizeof(*state));
+	for (i = 0; i < 8; ++i)
+		state->h[i] = blake2s_iv[i];
+	state->h[0] ^= param;
+}
+
+static void blake2s_init(struct blake2s_state *state, const size_t outlen)
+{
+	blake2s_init_param(state, 0x01010000 | outlen);
+	state->outlen = outlen;
+}
+
+static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc)
+{
+	uint32_t m[16];
+	uint32_t v[16];
+	int i;
+
+	while (nblocks > 0) {
+		blake2s_increment_counter(state, inc);
+		memcpy(m, block, BLAKE2S_BLOCK_LEN);
+		le32_to_cpu_array(m, ARRAY_SIZE(m));
+		memcpy(v, state->h, 32);
+		v[ 8] = blake2s_iv[0];
+		v[ 9] = blake2s_iv[1];
+		v[10] = blake2s_iv[2];
+		v[11] = blake2s_iv[3];
+		v[12] = blake2s_iv[4] ^ state->t[0];
+		v[13] = blake2s_iv[5] ^ state->t[1];
+		v[14] = blake2s_iv[6] ^ state->f[0];
+		v[15] = blake2s_iv[7] ^ state->f[1];
+
+#define G(r, i, a, b, c, d) do { \
+	a += b + m[blake2s_sigma[r][2 * i + 0]]; \
+	d = ror32(d ^ a, 16); \
+	c += d; \
+	b = ror32(b ^ c, 12); \
+	a += b + m[blake2s_sigma[r][2 * i + 1]]; \
+	d = ror32(d ^ a, 8); \
+	c += d; \
+	b = ror32(b ^ c, 7); \
+} while (0)
+
+#define ROUND(r) do { \
+	G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
+	G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
+	G(r, 2, v[2], v[ 6], v[10], v[14]); \
+	G(r, 3, v[3], v[ 7], v[11], v[15]); \
+	G(r, 4, v[0], v[ 5], v[10], v[15]); \
+	G(r, 5, v[1], v[ 6], v[11], v[12]); \
+	G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
+	G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
+} while (0)
+		ROUND(0);
+		ROUND(1);
+		ROUND(2);
+		ROUND(3);
+		ROUND(4);
+		ROUND(5);
+		ROUND(6);
+		ROUND(7);
+		ROUND(8);
+		ROUND(9);
+
+#undef G
+#undef ROUND
+
+		for (i = 0; i < 8; ++i)
+			state->h[i] ^= v[i] ^ v[i + 8];
+
+		block += BLAKE2S_BLOCK_LEN;
+		--nblocks;
+	}
+}
+
+static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen)
+{
+	const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen;
+	const uint8_t *in = inp;
+
+	if (!inlen)
+		return;
+	if (inlen > fill) {
+		memcpy(state->buf + state->buflen, in, fill);
+		blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
+		state->buflen = 0;
+		in += fill;
+		inlen -= fill;
+	}
+	if (inlen > BLAKE2S_BLOCK_LEN) {
+		const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
+		blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
+		in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
+		inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
+	}
+	memcpy(state->buf + state->buflen, in, inlen);
+	state->buflen += inlen;
+}
+
+static void blake2s_final(struct blake2s_state *state, uint8_t *out)
+{
+	blake2s_set_lastblock(state);
+	memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
+	blake2s_compress(state, state->buf, 1, state->buflen);
+	cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
+	memcpy(out, state->h, state->outlen);
+}
+
+static size_t determine_optimal_seed_len(void)
+{
+	size_t ret = 0;
+	char poolsize_str[11] = { 0 };
+	int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
+
+	if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
+		fprintf(stderr, "WARNING: Unable to determine pool size, falling back to %u bits: %s\n", MIN_SEED_LEN * 8, strerror(errno));
+		ret = MIN_SEED_LEN;
+	} else
+		ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
+	if (fd >= 0)
+		close(fd);
+	if (ret < MIN_SEED_LEN)
+		ret = MIN_SEED_LEN;
+	else if (ret > MAX_SEED_LEN)
+		ret = MAX_SEED_LEN;
+	return ret;
+}
+
+static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable)
+{
+	ssize_t ret;
+	int urandom_fd;
+
+	*is_creditable = false;
+	ret = getrandom(seed, len, GRND_NONBLOCK);
+	if (ret == (ssize_t)len) {
+		*is_creditable = true;
+		return 0;
+	} else if (ret < 0 && errno == ENOSYS) {
+		struct pollfd random_fd = {
+			.fd = open("/dev/random", O_RDONLY),
+			.events = POLLIN
+		};
+		if (random_fd.fd < 0)
+			return -errno;
+		*is_creditable = poll(&random_fd, 1, 0) == 1;
+		close(random_fd.fd);
+	} else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len)
+		return 0;
+	urandom_fd = open("/dev/urandom", O_RDONLY);
+	if (urandom_fd < 0)
+		return -errno;
+	ret = read(urandom_fd, seed, len);
+	if (ret == (ssize_t)len)
+		ret = 0;
+	else
+		ret = -errno ? -errno : -EIO;
+	close(urandom_fd);
+	return ret;
+}
+
+static int seed_rng(uint8_t *seed, size_t len, bool credit)
+{
+	struct {
+		int entropy_count;
+		int buf_size;
+		uint8_t buffer[MAX_SEED_LEN];
+	} req = {
+		.entropy_count = credit ? len * 8 : 0,
+		.buf_size = len
+	};
+	int random_fd, ret;
+
+	if (len > sizeof(req.buffer))
+		return -EFBIG;
+	memcpy(req.buffer, seed, len);
+
+	random_fd = open("/dev/random", O_RDWR);
+	if (random_fd < 0)
+		return -errno;
+	ret = ioctl(random_fd, RNDADDENTROPY, &req);
+	if (ret)
+		ret = -errno ? -errno : -EIO;
+	close(random_fd);
+	return ret;
+}
+
+static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash)
+{
+	uint8_t seed[MAX_SEED_LEN];
+	ssize_t seed_len;
+	int fd, dfd, ret = 0;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0 && errno == ENOENT)
+		return 0;
+	else if (fd < 0) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to open seed file: %s\n", strerror(errno));
+		return ret;
+	}
+	dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
+	if (dfd < 0) {
+		ret = -errno;
+		close(fd);
+		fprintf(stderr, "ERROR: Unable to open seed directory: %s\n", strerror(errno));
+		return ret;
+	}
+	seed_len = read(fd, seed, sizeof(seed));
+	if (seed_len < 0) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to read seed file: %s\n", strerror(errno));
+	}
+	close(fd);
+	if (ret) {
+		close(dfd);
+		return ret;
+	}
+	if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) {
+		ret = -errno;
+		fprintf(stderr, "ERROR: Unable to remove seed after reading, so not seeding: %s\n", strerror(errno));
+	}
+	close(dfd);
+	if (ret)
+		return ret;
+	if (!seed_len)
+		return 0;
+
+	blake2s_update(hash, &seed_len, sizeof(seed_len));
+	blake2s_update(hash, seed, seed_len);
+
+	fprintf(stdout, "Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
+	ret = seed_rng(seed, seed_len, credit);
+	if (ret < 0)
+		fprintf(stderr, "ERROR: Unable to seed: %s\n", strerror(-ret));
+	return ret;
+}
+
+static bool skip_credit(void)
+{
+	const char *skip = getenv("SEEDRNG_SKIP_CREDIT");
+	return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
+			!strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
+}
+
+static void populate_global_paths(void)
+{
+	SEED_DIR = getenv("SEEDRNG_SEED_DIR");
+	if (!SEED_DIR || !*SEED_DIR)
+		SEED_DIR = "/var/lib/seedrng";
+	LOCK_FILE = getenv("SEEDRNG_LOCK_FILE");
+	if (!LOCK_FILE || !*LOCK_FILE)
+		LOCK_FILE = "/var/run/seedrng.lock";
+	if (asprintf(&CREDITABLE_SEED, "%s/seed.credit", SEED_DIR) < 0 ||
+	    asprintf(&NON_CREDITABLE_SEED, "%s/seed.no-credit", SEED_DIR) < 0) {
+		fprintf(stderr, "ERROR: Unable to allocate paths: %s\n", strerror(errno));
+		exit(1);
+	}
+}
+
+int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+	static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix";
+	static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
+	int ret, fd = -1, lock, program_ret = 0;
+	uint8_t new_seed[MAX_SEED_LEN];
+	size_t new_seed_len;
+	bool new_seed_creditable;
+	struct timespec realtime = { 0 }, boottime = { 0 };
+	struct blake2s_state hash;
+
+	umask(0077);
+	if (getuid()) {
+		fprintf(stderr, "ERROR: This program requires root\n");
+		return 1;
+	}
+
+	populate_global_paths();
+	blake2s_init(&hash, BLAKE2S_HASH_LEN);
+	blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
+	clock_gettime(CLOCK_REALTIME, &realtime);
+	clock_gettime(CLOCK_BOOTTIME, &boottime);
+	blake2s_update(&hash, &realtime, sizeof(realtime));
+	blake2s_update(&hash, &boottime, sizeof(boottime));
+
+	if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
+		fprintf(stderr, "ERROR: Unable to create \"%s\" directory: %s\n", SEED_DIR, strerror(errno));
+		return 1;
+	}
+
+	lock = open(LOCK_FILE, O_WRONLY | O_CREAT, 0000);
+	if (lock < 0 || flock(lock, LOCK_EX) < 0) {
+		fprintf(stderr, "ERROR: Unable to open lock file: %s\n", strerror(errno));
+		program_ret = 1;
+		goto out;
+	}
+
+	ret = seed_from_file_if_exists(NON_CREDITABLE_SEED, false, &hash);
+	if (ret < 0)
+		program_ret |= 1 << 1;
+	ret = seed_from_file_if_exists(CREDITABLE_SEED, !skip_credit(), &hash);
+	if (ret < 0)
+		program_ret |= 1 << 2;
+
+	new_seed_len = determine_optimal_seed_len();
+	ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable);
+	if (ret < 0) {
+		fprintf(stderr, "ERROR: Unable to read new seed: %s\n", strerror(-ret));
+		new_seed_len = BLAKE2S_HASH_LEN;
+		strncpy((char *)new_seed, seedrng_failure, new_seed_len);
+		program_ret |= 1 << 3;
+	}
+	blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
+	blake2s_update(&hash, new_seed, new_seed_len);
+	blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
+
+	fprintf(stdout, "Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
+	fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
+	if (fd < 0) {
+		fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n", strerror(errno));
+		program_ret |= 1 << 4;
+		goto out;
+	}
+	if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
+		fprintf(stderr, "ERROR: Unable to write seed file: %s\n", strerror(errno));
+		program_ret |= 1 << 5;
+		goto out;
+	}
+	if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED) < 0) {
+		fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n", strerror(errno));
+		program_ret |= 1 << 6;
+	}
+out:
+	if (fd >= 0)
+		close(fd);
+	if (lock >= 0)
+		close(lock);
+	return program_ret;
+}
diff --git a/package/urandom-scripts/urandom-scripts.mk b/package/urandom-scripts/urandom-scripts.mk
index 2c09728c46..e8526e248a 100644
--- a/package/urandom-scripts/urandom-scripts.mk
+++ b/package/urandom-scripts/urandom-scripts.mk
@@ -4,7 +4,13 @@
 #
 ################################################################################
 
+define URANDOM_SCRIPTS_BUILD_CMDS
+	$(TARGET_CC) $(TARGET_CFLAGS) -std=gnu99 $(TARGET_LDFLAGS) \
+		$(URANDOM_SCRIPTS_PKGDIR)/seedrng.c -o $(@D)/seedrng
+endef
+
 define URANDOM_SCRIPTS_INSTALL_INIT_SYSV
+	$(INSTALL) -D -m 0755 $(@D)/seedrng $(TARGET_DIR)/sbin/seedrng
 	$(INSTALL) -D -m 0755 $(URANDOM_SCRIPTS_PKGDIR)/S20urandom \
 		$(TARGET_DIR)/etc/init.d/S20urandom
 endef
-- 
2.35.1

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v4] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-31 14:57                   ` [Buildroot] [PATCH v4] " Jason A. Donenfeld
@ 2022-03-31 15:16                     ` David Laight
  2022-03-31 15:46                       ` David Laight
  0 siblings, 1 reply; 34+ messages in thread
From: David Laight @ 2022-03-31 15:16 UTC (permalink / raw)
  To: 'Jason A. Donenfeld', Peter Korsgaard, buildroot; +Cc: James Hilliard

From: Jason A. Donenfeld
> Sent: 31 March 2022 15:58
> 
> The RNG can't actually be seeded from a shell script, due to the
> reliance on ioctls. For this reason, the seedrng project provides a
> basic script meant to be copy and pasted into projects like buildroot
> and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
> 
> This commit imports it into buildroot and wires up the init scripts to
> call it. This also is a significant improvement over the current init
> script, which doesn't credit entropy and whose hashing in shell scripts
> is sort of fragile.
> 
> As seedrng.c is a short tiny C program, we include this here in the
> package, like a few other packages do. Later we'll investigate adding
> this to busybox, but for now, this is a good start and a positive step
> in the right direction.
> 
...
> -# Quietly do nothing if /dev/urandom does not exist
> -[ -c /dev/urandom ] || exit 0
> -
> -URANDOM_SEED="/var/lib/random-seed"
> +# The following knobs can be adjusted by placing uncommented modified lines
> +# into /etc/default/urandom:
> +#
> +# Set these to change where the seed and runtime lock file are stored:
> +#
> +#     export SEEDRNG_SEED_DIR=/var/lib/seedrng
> +#     export SEEDRNG_LOCK_FILE=/var/run/seedrng.lock
> +#
> +# Set this to true only if you do not want seed files to actually credit the
> +# RNG, for example if you plan to replicate this file system image and do not
> +# have the wherewithal to first delete the contents of /var/lib/seedrng:
> +#
> +#     export SEEDRNG_SKIP_CREDIT=false
> +#

That quietly breaks anyone who just updates buildroot without
noticing that the 'magic' shell variables have changed.

I'm also sure you can manage to pass the arguments into the program
through argv[] instead of grovelling through the environment.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v4] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-31 15:16                     ` David Laight
@ 2022-03-31 15:46                       ` David Laight
  0 siblings, 0 replies; 34+ messages in thread
From: David Laight @ 2022-03-31 15:46 UTC (permalink / raw)
  To: 'buildroot'

Resend to just the list - I think Jason's gmail account
is rejecting direct mail from me.

Which probably means he hasn't seen my earlier messages either.

	David

> -----Original Message-----
> From: David Laight
> Sent: 31 March 2022 16:16
> To: 'Jason A. Donenfeld' <Jason@zx2c4.com>; Peter Korsgaard <peter@korsgaard.com>; buildroot
> <buildroot@buildroot.org>
> Cc: James Hilliard <james.hilliard1@gmail.com>
> Subject: RE: [Buildroot] [PATCH v4] package/urandom-scripts: actually credit seed files via seedrng
> 
> From: Jason A. Donenfeld
> > Sent: 31 March 2022 15:58
> >
> > The RNG can't actually be seeded from a shell script, due to the
> > reliance on ioctls. For this reason, the seedrng project provides a
> > basic script meant to be copy and pasted into projects like buildroot
> > and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
> >
> > This commit imports it into buildroot and wires up the init scripts to
> > call it. This also is a significant improvement over the current init
> > script, which doesn't credit entropy and whose hashing in shell scripts
> > is sort of fragile.
> >
> > As seedrng.c is a short tiny C program, we include this here in the
> > package, like a few other packages do. Later we'll investigate adding
> > this to busybox, but for now, this is a good start and a positive step
> > in the right direction.
> >
> ...
> > -# Quietly do nothing if /dev/urandom does not exist
> > -[ -c /dev/urandom ] || exit 0
> > -
> > -URANDOM_SEED="/var/lib/random-seed"
> > +# The following knobs can be adjusted by placing uncommented modified lines
> > +# into /etc/default/urandom:
> > +#
> > +# Set these to change where the seed and runtime lock file are stored:
> > +#
> > +#     export SEEDRNG_SEED_DIR=/var/lib/seedrng
> > +#     export SEEDRNG_LOCK_FILE=/var/run/seedrng.lock
> > +#
> > +# Set this to true only if you do not want seed files to actually credit the
> > +# RNG, for example if you plan to replicate this file system image and do not
> > +# have the wherewithal to first delete the contents of /var/lib/seedrng:
> > +#
> > +#     export SEEDRNG_SKIP_CREDIT=false
> > +#
> 
> That quietly breaks anyone who just updates buildroot without
> noticing that the 'magic' shell variables have changed.
> 
> I'm also sure you can manage to pass the arguments into the program
> through argv[] instead of grovelling through the environment.
> 
> 	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-30 17:13                 ` Jason A. Donenfeld
  2022-03-31 14:50                   ` Jason A. Donenfeld
  2022-03-31 14:57                   ` [Buildroot] [PATCH v4] " Jason A. Donenfeld
@ 2022-03-31 17:11                   ` Peter Korsgaard
  2022-04-01  8:12                     ` David Laight
  2022-04-01 10:57                   ` James Hilliard
  3 siblings, 1 reply; 34+ messages in thread
From: Peter Korsgaard @ 2022-03-31 17:11 UTC (permalink / raw)
  To: Jason A. Donenfeld; +Cc: James Hilliard, Yann E. MORIN, buildroot

>>>>> "Jason" == Jason A Donenfeld <Jason@zx2c4.com> writes:

Hi,

 >> As discussed on IRC, I think it would be nicer to just add a normal
 >> seedrng package and depend on that from urandom-scripts, so the standard
 >> version/license info/.. stuff works. I can do that change if needed.
 >> 
 >> You mentioned something on IRC about this containing differences from
 >> what it is seedrng.git. Those changes/fixes are presumably not specific
 >> to Buildroot, will you add those changes to the git repo as well?

 > Absolutely not. This 100% does not work for me. I have made it
 > abundantly clear to you that seedrng is code that's meant to be copied
 > into individual distros and tweaked as needed, which I've done for
 > Buildroot. Do not attempt to package it from any repos I may have. If
 > you try to do that, I will intentionally interfere with those
 > operations server side, and take whatever other adversarial steps to
 > ensure my server is not misused for that purpose. I also will not
 > incorporate buildroot-specific tweaks into the sample code repo.

 > That repo supplies sample code. After Yann's absence, I took the
 > necessary steps that a maintainer like him would have done to
 > customize it to Buildroot's needs, incorporating it into the project,
 > and taking into account James' comments. You now have the result of
 > that work here, and I think it'd be silly not to take it.

 > I am offering to maintain this code inside of Buildroot for you. I'm
 > generally very much on top of things in that regard. I'm not offering
 > to start and maintain an external project at your request, no matter
 > how much pressure and conditionalization you apply on this mailing
 > list or IRC. That's not work I'll do for you. However, again, I am
 > offering to maintain this for you inside the buildroot repo as this
 > patch does.

 > That's my offer; take it or leave it.

Then I'm afraid we'll have to leave it. Sorry about that.

I do have a lot of respect for your various other open source work, but
your aggressive behaviour here is not OK.

Not having the entrophy credited by this script is not great, but it has
been like this for 20 years already, and it only affects a small subset
of users, E.G. people using a combination of writable and persistent
/var/lib, the classic initscripts and on a platform without a suitable
entrophy source. In the 15+ years that I have been using Buildroot, I
have so far never used it. In fact, perhaps we should drop the default y
from the package.

This can be revisited if something like this ends up in Busybox and/or
something similar gets packaged,
E.G. https://github.com/jumpnow/rndaddtoentcnt

-- 
Bye, Peter Korsgaard
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-31 17:11                   ` [Buildroot] [PATCH v3] " Peter Korsgaard
@ 2022-04-01  8:12                     ` David Laight
  2022-04-01  9:22                       ` Jason A. Donenfeld
  0 siblings, 1 reply; 34+ messages in thread
From: David Laight @ 2022-04-01  8:12 UTC (permalink / raw)
  To: 'Peter Korsgaard', Jason A. Donenfeld
  Cc: James Hilliard, Yann E. MORIN, buildroot

I used the following small program instead of dd to credit the entropy.

+#include <linux/random.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/* Just writing to /dev/urandom doesn't credit any entropy
+ * so the crng remains uninitialised.
+ * So use the relevant ioctl instead. */
+
+int main(int argc, char **argv)
+{
+	int fd;
+	int len;
+	struct {
+		struct rand_pool_info info;
+		int buf[4096];
+	} rnd_info;
+
+	if (!argv[1])
+		return 1;
+
+	fd = open(argv[1], O_RDONLY);
+	if (fd < 0)
+		return 1;
+	len = read(fd, rnd_info.buf, sizeof rnd_info.buf);
+	close(fd);
+	if (len <= 0)
+		return 1;
+
+	fd = open("/dev/urandom", O_RDWR);
+	if (fd < 0)
+		return 1;
+	rnd_info.info.entropy_count = len * 8;
+	rnd_info.info.buf_size = len;
+
+	len = ioctl(fd, RNDADDENTROPY, &rnd_info);
+
+	return len ? 1 : 0;
+}

Seems to do the trick.
Something that size is probably a candidate for busybox.
Although it does really need to run before udev is initialised.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-01  8:12                     ` David Laight
@ 2022-04-01  9:22                       ` Jason A. Donenfeld
  2022-04-01 10:11                         ` David Laight
  0 siblings, 1 reply; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-04-01  9:22 UTC (permalink / raw)
  To: David Laight; +Cc: buildroot, James Hilliard, Yann E. MORIN

On 4/1/22, David Laight <David.Laight@aculab.com> wrote:
> I used the following small program instead of dd to credit the entropy.

Not a safe idea; will lead to problems and footguns. Doesn't handle
safe fsync'd deletion of used seeds and safe sequencing. Doesn't
handle hashing old seed with new seed. Ignores much of the discussion
that lead to seedrng's design.
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-01  9:22                       ` Jason A. Donenfeld
@ 2022-04-01 10:11                         ` David Laight
  2022-04-01 10:17                           ` Jason A. Donenfeld
  0 siblings, 1 reply; 34+ messages in thread
From: David Laight @ 2022-04-01 10:11 UTC (permalink / raw)
  To: 'Jason A. Donenfeld'; +Cc: James Hilliard, Yann E. MORIN, buildroot

From: Jason A. Donenfeld
> Sent: 01 April 2022 10:23
> 
> On 4/1/22, David Laight <David.Laight@aculab.com> wrote:
> > I used the following small program instead of dd to credit the entropy.
> 
> Not a safe idea; will lead to problems and footguns. Doesn't handle
> safe fsync'd deletion of used seeds and safe sequencing. Doesn't
> handle hashing old seed with new seed. Ignores much of the discussion
> that lead to seedrng's design.

But it does exactly what the old scripts thought they were doing
and is far better that just using dd.

I'm not even sure that trying to save a 'new seed' in the
startup script is a good idea at all.
The new seed is very likely to just be a permutation of the
old seed - since little extra 'entropy' can have been added.
So while it may stop complete reproducibility I'm not at
all sure it is cryptographically sound at all.

If you can't rely on saving entropy at shutdown, then using
(the equivalent of) a cron job to save every hour (or so)
is probably useful.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-01 10:11                         ` David Laight
@ 2022-04-01 10:17                           ` Jason A. Donenfeld
  0 siblings, 0 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-04-01 10:17 UTC (permalink / raw)
  To: David Laight; +Cc: James Hilliard, Yann E. MORIN, buildroot

On 4/1/22, David Laight <David.Laight@aculab.com> wrote:
> But it does exactly what the old scripts thought they were doing
> and is far better that just using dd.

On the contrary, it's strictly worse, since the crediting becomes
dangerous here.

> I'm not even sure that trying to save a 'new seed' in the
> startup script is a good idea at all.
> The new seed is very likely to just be a permutation of the
> old seed - since little extra 'entropy' can have been added.
> So while it may stop complete reproducibility I'm not at
> all sure it is cryptographically sound at all.

?? You could get away with hashing the old seed together with some
constant public value and it would be sufficient here. What are you
talking about?

> If you can't rely on saving entropy at shutdown, then using
> (the equivalent of) a cron job to save every hour (or so)
> is probably useful.

Android does it every 3. And as of http://r.android.com/2050961 they
now use the SeedRNG construction.
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-03-30 17:13                 ` Jason A. Donenfeld
                                     ` (2 preceding siblings ...)
  2022-03-31 17:11                   ` [Buildroot] [PATCH v3] " Peter Korsgaard
@ 2022-04-01 10:57                   ` James Hilliard
  2022-04-01 11:04                     ` Jason A. Donenfeld
  3 siblings, 1 reply; 34+ messages in thread
From: James Hilliard @ 2022-04-01 10:57 UTC (permalink / raw)
  To: Jason A. Donenfeld; +Cc: Yann E. MORIN, buildroot

On Wed, Mar 30, 2022 at 11:13 AM Jason A. Donenfeld <Jason@zx2c4.com> wrote:
>
> Hi Peter,
>
> On 3/30/22, Peter Korsgaard <peter@korsgaard.com> wrote:
> >>>>>> "Jason" == Jason A Donenfeld <Jason@zx2c4.com> writes:
> >
> > Hi,
> >
> >  > The RNG can't actually be seeded from a shell script, due to the
> >  > reliance on ioctls. For this reason, the seedrng project provides a
> >  > basic script meant to be copy and pasted into projects like buildroot
> >  > and tweaked as needed: <https://git.zx2c4.com/seedrng/about/>.
> >
> >  > This commit imports it into buildroot and wires up the init scripts to
> >  > call it. This also is a significant improvement over the current init
> >  > script, which doesn't credit entropy and whose hashing in shell scripts
> >  > is sort of fragile.
> >
> >  > As seedrng.c is a short tiny C program, we include this here in the
> >  > package, like a few other packages do. Later we'll investigate adding
> >  > this to busybox, but for now, this is a good start and a positive step
> >  > in the right direction.
> >
> > As discussed on IRC, I think it would be nicer to just add a normal
> > seedrng package and depend on that from urandom-scripts, so the standard
> > version/license info/.. stuff works. I can do that change if needed.
> >
> > You mentioned something on IRC about this containing differences from
> > what it is seedrng.git. Those changes/fixes are presumably not specific
> > to Buildroot, will you add those changes to the git repo as well?
>
> Absolutely not. This 100% does not work for me. I have made it
> abundantly clear to you that seedrng is code that's meant to be copied
> into individual distros and tweaked as needed, which I've done for
> Buildroot. Do not attempt to package it from any repos I may have. If
> you try to do that, I will intentionally interfere with those
> operations server side, and take whatever other adversarial steps to
> ensure my server is not misused for that purpose. I also will not
> incorporate buildroot-specific tweaks into the sample code repo.
>
> That repo supplies sample code. After Yann's absence, I took the
> necessary steps that a maintainer like him would have done to
> customize it to Buildroot's needs, incorporating it into the project,
> and taking into account James' comments. You now have the result of
> that work here, and I think it'd be silly not to take it.

I should add that I do also think this should be upstreamed to busybox, which
will also reduce the amount of duplicate work as busybox is commonly used
across many distros.

Buildroot isn't a normal distro, we don't normally maintain application code
in-tree like this at all.

>
> I am offering to maintain this code inside of Buildroot for you. I'm
> generally very much on top of things in that regard. I'm not offering
> to start and maintain an external project at your request, no matter
> how much pressure and conditionalization you apply on this mailing
> list or IRC. That's not work I'll do for you. However, again, I am
> offering to maintain this for you inside the buildroot repo as this
> patch does.
>
> That's my offer; take it or leave it.
>
>
> >
> >
> >  > +# The following knobs can be adjusted by placing uncommented modified
> > lines
> >  > +# into /etc/default/urandom:
> >  > +#
> >  > +# Set these to change where the seed and runtime lock file are stored:
> >  > +#
> >  > +#     export SEEDRNG_SEED_DIR=/var/lib/seedrng
> >  > +#     export SEEDRNG_LOCK_FILE=/var/run/seedrng.lock
> >  > +#
> >
> > Will you add the logic for these features to seedrng.git? If not, then I
> > don't think we should have them here either. A bunch of other code already
> > expects "normal" locations, so people generally have to handle such
> > customizations by symlinks in their rootfs(-overlay).
> >
> > If this customization is left in, then it should ideally use the
> > existing (directory part of) URANDOM_SEED, which used to be how the
> > location could be customized for backwards compatibility.
>
> As mentioned, no tweaks go up to seedrng.git. That's sample code.
>
> I'm inclined to agree with James' assessment about this, that
> Buildroot needs it to be customizable. Happy to send a v4 of this
> patch adjusting the defaults to have paths similar to what was there
> before.
>
> >
> >
> >  >  case "$1" in
> >  >    start|restart|reload)
> >  >            # Carry a random seed from start-up to start-up
> >  >            # Load and then save the whole entropy pool
> >  > -          init_rng && save_random_seed;;
> >  > +          # Never fail, as this isn't worth making a fuss
> >  > +          # over if it doesn't go as planned.
> >  > +          seedrng || true;;
> >  >    stop)
> >  >            # Carry a random seed from shut-down to start-up
> >  >            # Save the whole entropy pool
> >  > -          save_random_seed;;
> >  > +          seedrng;;
> >
> > Nice and short! The comments above are not really correct any more, as
> > the stop logic also loads.
>
> Ack, will get rid of those comments for v4.
>
> >
> >
> >  > +++ b/package/urandom-scripts/seedrng.c
> > ..
> >
> >  > +#define le32_to_cpup(a) le32toh(*(a))
> >  > +#define cpu_to_le32(a) htole32(a)
> >
> > What is the purpose of the to/from le32 stuff in this use case?
>
> So that blake2s passes its test vectors. We're not about to go messing
> around with the internal encoding of a hash function. B2s specifies
> little endian, so that's what we use.
>
> >
> >  > +
> >  > +  fprintf(stdout, "Saving %zu bits of %s seed for next boot\n",
> > new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
> >  > +  fd = open(NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
> >
> > The 0400 confused me a bit, as that would normally cause the open to
> > fail when the file exists, but it happens to work as you unlink above /
> > use the lock file.
> >
> >
> >> +    if (fd < 0) {
> >  > +          fprintf(stderr, "ERROR: Unable to open seed file for writing: %s\n",
> > strerror(errno));
> >  > +          program_ret |= 1 << 4;
> >  > +          goto out;
> >  > +  }
> >  > +  if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len ||
> > fsync(fd) < 0) {
> >  > +          fprintf(stderr, "ERROR: Unable to write seed file: %s\n",
> > strerror(errno));
> >  > +          program_ret |= 1 << 5;
> >  > +          goto out;
> >  > +  }
> >  > +  if (new_seed_creditable && rename(NON_CREDITABLE_SEED, CREDITABLE_SEED)
> > < 0) {
> >  > +          fprintf(stderr, "WARNING: Unable to make new seed creditable: %s\n",
> > strerror(errno));
> >  > +          program_ret |= 1 << 6;
> >  > +  }
> >  > +out:
> >  > +  if (fd >= 0)
> >  > +          close(fd);
> >  > +  if (lock >= 0)
> >  > +          close(lock);
> >
> > No fsync of the directory like you do in seed_from_file_if_exists()?
>
> No, it's not necessary here, because this is a transition from
> no-credit to credit, so if the power clicks off at the bad moment, it
> fails closed instead of open.
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-01 10:57                   ` James Hilliard
@ 2022-04-01 11:04                     ` Jason A. Donenfeld
  2022-04-01 11:34                       ` David Laight
  0 siblings, 1 reply; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-04-01 11:04 UTC (permalink / raw)
  To: James Hilliard; +Cc: Yann E. MORIN, buildroot

Hi James,

On 4/1/22, James Hilliard <james.hilliard1@gmail.com> wrote:
> I should add that I do also think this should be upstreamed to busybox,
> which
> will also reduce the amount of duplicate work as busybox is commonly used
> across many distros.

I'll work on that. And hopefully David won't sabotage it with his
"it's only 10 lines!" stuff.

Jason
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-01 11:04                     ` Jason A. Donenfeld
@ 2022-04-01 11:34                       ` David Laight
  2022-04-02 17:08                         ` Arnout Vandecappelle
  0 siblings, 1 reply; 34+ messages in thread
From: David Laight @ 2022-04-01 11:34 UTC (permalink / raw)
  To: 'Jason A. Donenfeld', James Hilliard; +Cc: Yann E. MORIN, buildroot

From: Jason A. Donenfeld
> Sent: 01 April 2022 12:05
> 
> On 4/1/22, James Hilliard <james.hilliard1@gmail.com> wrote:
> > I should add that I do also think this should be upstreamed to busybox,
> > which
> > will also reduce the amount of duplicate work as busybox is commonly used
> > across many distros.
> 
> I'll work on that. And hopefully David won't sabotage it with his
> "it's only 10 lines!" stuff.

:-)

Busybox tends to care about code size.
You really want to roll-up the unrolled loop in blakes2.
Performance really doesn't matter here.

In any case, don't new kernels just 'stir' the new entropy into
input pool?
So feeding in non-random data doesn't really make the output
any worse?
So you could add the saved entropy, force a reseed of the output
generator, and then save data from /dev/urandom - which now
contains any entropy collected since boot and the saved seed.
No need for any cryprographic code in userspace??

The old kernel code is just too horrid to think about.
IIRC the output generator is just and xor of several LFSR
and so is completely and trivially reversable.
(One of the random number generators did that.)
And the input pool tries to count bits in and out instead
of 'stirring' input data into some data structure.
So feed it non-random data and it discards the old random
data it had.

How much state does the blakes2 input pool have?
I'd have thought you'd want the input pool to have
much more state than the output generator?

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-01 11:34                       ` David Laight
@ 2022-04-02 17:08                         ` Arnout Vandecappelle
  2022-04-03  7:30                           ` David Laight
  2022-04-03  9:42                           ` Yann E. MORIN
  0 siblings, 2 replies; 34+ messages in thread
From: Arnout Vandecappelle @ 2022-04-02 17:08 UTC (permalink / raw)
  To: David Laight, 'Jason A. Donenfeld', James Hilliard
  Cc: Yann E. MORIN, buildroot



On 01/04/2022 13:34, David Laight wrote:
> From: Jason A. Donenfeld
>> Sent: 01 April 2022 12:05
>>
>> On 4/1/22, James Hilliard <james.hilliard1@gmail.com> wrote:
>>> I should add that I do also think this should be upstreamed to busybox,
>>> which
>>> will also reduce the amount of duplicate work as busybox is commonly used
>>> across many distros.
>>
>> I'll work on that.

  +1 to have it in busybox.

>> And hopefully David won't sabotage it with his
>> "it's only 10 lines!" stuff.
> 
> :-)
> 
> Busybox tends to care about code size.

  Yes, but if it's not overly bloated, it's not going to be blocker for initial 
inclusion.

> You really want to roll-up the unrolled loop in blakes2.
> Performance really doesn't matter here.

  AFAIU (but neither the commit message nor the seedrng about page explain it) 
the Blake2 algorithm was simply chosen because it's small. Any hash function 
should be fine. There's sha1, sha2 and sha3 in libbb, so I guess one of them 
should have all the desired properties.

> In any case, don't new kernels just 'stir' the new entropy into
> input pool?
> So feeding in non-random data doesn't really make the output
> any worse?
> So you could add the saved entropy, force a reseed of the output
> generator, and then save data from /dev/urandom - which now
> contains any entropy collected since boot and the saved seed.
> No need for any cryprographic code in userspace??

  This is explained in commit f0986de551f46e72268857fd817986e9be697cd0.
TL;DR: the kernel actually does make the output worse if there's not much 
entropy available.

  Also, seedrng makes sure that it's powerfail safe. If power is interrupted at 
any point in time, we have not lost entropy.

  Regards,
  Arnout


> The old kernel code is just too horrid to think about.
> IIRC the output generator is just and xor of several LFSR
> and so is completely and trivially reversable.
> (One of the random number generators did that.)
> And the input pool tries to count bits in and out instead
> of 'stirring' input data into some data structure.
> So feed it non-random data and it discards the old random
> data it had.
> 
> How much state does the blakes2 input pool have?
> I'd have thought you'd want the input pool to have
> much more state than the output generator?
> 
> 	David
> 
> -
> Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
> Registration No: 1397386 (Wales)
> 
> _______________________________________________
> buildroot mailing list
> buildroot@buildroot.org
> https://lists.buildroot.org/mailman/listinfo/buildroot
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-02 17:08                         ` Arnout Vandecappelle
@ 2022-04-03  7:30                           ` David Laight
  2022-04-04 14:32                             ` Jason A. Donenfeld
  2022-04-03  9:42                           ` Yann E. MORIN
  1 sibling, 1 reply; 34+ messages in thread
From: David Laight @ 2022-04-03  7:30 UTC (permalink / raw)
  To: 'Arnout Vandecappelle', 'Jason A. Donenfeld',
	James Hilliard
  Cc: Yann E. MORIN, buildroot

From: Arnout Vandecappelle
> Sent: 02 April 2022 18:09
...
> > Busybox tends to care about code size.
> 
>   Yes, but if it's not overly bloated, it's not going to be blocker for initial
> inclusion.
> 
> > You really want to roll-up the unrolled loop in blakes2.
> > Performance really doesn't matter here.
> 
>   AFAIU (but neither the commit message nor the seedrng about page explain it)
> the Blake2 algorithm was simply chosen because it's small. Any hash function
> should be fine. There's sha1, sha2 and sha3 in libbb, so I guess one of them
> should have all the desired properties.

The unrolled blakes2 is typically 5kb of object code.
The unrolled code is better for large buffers,
but just fetching the code to the I-cache will
significantly affect performance for small buffers.
I really ought to sit and measure the cutoff.

The code is:
+#define G(r, i, a, b, c, d) do { \
+	a += b + m[blake2s_sigma[r][2 * i + 0]]; \
+	d = ror32(d ^ a, 16); \
+	c += d; \
+	b = ror32(b ^ c, 12); \
+	a += b + m[blake2s_sigma[r][2 * i + 1]]; \
+	d = ror32(d ^ a, 8); \
+	c += d; \
+	b = ror32(b ^ c, 7); \
+} while (0)
+
+#define ROUND(r) do { \
+	G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
+	G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
+	G(r, 2, v[2], v[ 6], v[10], v[14]); \
+	G(r, 3, v[3], v[ 7], v[11], v[15]); \
+	G(r, 4, v[0], v[ 5], v[10], v[15]); \
+	G(r, 5, v[1], v[ 6], v[11], v[12]); \
+	G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
+	G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
+} while (0)
+		ROUND(0);
+		ROUND(1);
+		ROUND(2);
+		ROUND(3);
+		ROUND(4);
+		ROUND(5);
+		ROUND(6);
+		ROUND(7);
+		ROUND(8);
+		ROUND(9);
+
+#undef G
+#undef ROUND

Now G() is a 14 instruction register dependency chain.
But since each batch of 4 G() is independent the compiler can
interleave them - significant on modern cpu.

OTOH the 10 ROUND() just advance through blake2s_sigma[].
Unrolling these that is probably not that significant.
Especially if the compiler manages to increment the pointer.
While modern gcc manage to optimise the array away completely
the code isn't constrained by memory accesses so loading
from an actual array won't make much difference.
On some architectures the constants either take multiple
instructions to load, or load from memory anyway.
But these bloat that code block by a factor of 10.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-02 17:08                         ` Arnout Vandecappelle
  2022-04-03  7:30                           ` David Laight
@ 2022-04-03  9:42                           ` Yann E. MORIN
  2022-04-03 23:13                             ` James Hilliard
  2022-04-04 14:38                             ` Jason A. Donenfeld
  1 sibling, 2 replies; 34+ messages in thread
From: Yann E. MORIN @ 2022-04-03  9:42 UTC (permalink / raw)
  To: Arnout Vandecappelle
  Cc: 'Jason A. Donenfeld', David Laight, James Hilliard, buildroot

All,

On 2022-04-02 19:08 +0200, Arnout Vandecappelle spake thusly:
> On 01/04/2022 13:34, David Laight wrote:
> >From: Jason A. Donenfeld
> >>Sent: 01 April 2022 12:05
> >>On 4/1/22, James Hilliard <james.hilliard1@gmail.com> wrote:
> >>>I should add that I do also think this should be upstreamed to busybox,
> >>>which
> >>>will also reduce the amount of duplicate work as busybox is commonly used
> >>>across many distros.
> >>I'll work on that.
>  +1 to have it in busybox.

I still fail to understand why this can't be a standalone project.

The reasoning offered by Jason is that the code should be included
and duplicated into all and each init systems out there.

However, this increases the maintenance burden, as each implementation
has to be actively tracked (FTR: Jason said he would actively maintain
the implementation in Buildroot, which is very nice of him, so I
understand that he would also do so for all other projects where he'd
have seedrng included).

Instead of having one implementation suitable for every init systems
that use shell scripts, we'd end up with many different and diverging
implementations that each have their own warts and fixes (or worse,
counter-productive fixes that actually decrease the robustness of that
implementation).

On the other hand, having a common project would alow to centralise the
fixes. It would also allow to ensure that changes do not actually break
security. Finally, any evolution, be it fixes or features, would be
easily available to every init systems using the common project.

Furthermore, the level of customisation is very low. All that we can
expect to be customisable is the location where the seeds are stored.
This is already accounted for in the existing seedrng git tree. Using
another hash implementation could be another thing, but there's not
much point here, Blake2 being already pretty strong and known. So there
is probably not much more customisation left to do.

Moving it into busybox might seem a good idea at first, but this would
still make for an n-th implementation to track, and since busybox has a
focus on code size, the implementation there would probably diverge
substantially from the canonical code we saw so far, further increasing
the maintenance burden.

That would also not address distributions that do not use busybox (and
do not use systemd either). Buildroot can even be configured in such a
way, using a sys-v init system with coreutils et al,. and no busybox, in
which case having seedrng only in busybox would still not solve the
problem in such a case. And init systems are not limited to what we can
see publicly; there are maybe hundreds or thousands of such custom init
systems behind private doors. Buildroot even has an option to configure
for such an init system (BR2_INIT_NONE, which really means 'custom').

On a final note: systemd has native support for this feature, and thus
one may argue that the feature is indeed already duplicated there.
However, this is different in two ways: first, systemd needs random
numbers for itself already, very early in the boot, possibly in an
initramfs, so it can't easily rely on an external tool to do that;
second, systemd is already C, so it does not make sense for the feature
to be implemented as an external tool either.

Regards,
Yann E. MORIN.

-- 
.-----------------.--------------------.------------------.--------------------.
|  Yann E. MORIN  | Real-Time Embedded | /"\ ASCII RIBBON | Erics' conspiracy: |
| +33 662 376 056 | Software  Designer | \ / CAMPAIGN     |  ___               |
| +33 561 099 427 `------------.-------:  X  AGAINST      |  \e/  There is no  |
| http://ymorin.is-a-geek.org/ | _/*\_ | / \ HTML MAIL    |   v   conspiracy.  |
'------------------------------^-------^------------------^--------------------'
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-03  9:42                           ` Yann E. MORIN
@ 2022-04-03 23:13                             ` James Hilliard
  2022-04-04 14:40                               ` Jason A. Donenfeld
  2022-04-04 14:38                             ` Jason A. Donenfeld
  1 sibling, 1 reply; 34+ messages in thread
From: James Hilliard @ 2022-04-03 23:13 UTC (permalink / raw)
  To: Yann E. MORIN; +Cc: Jason A. Donenfeld, David Laight, buildroot

On Sun, Apr 3, 2022 at 3:42 AM Yann E. MORIN <yann.morin.1998@free.fr> wrote:
>
> All,
>
> On 2022-04-02 19:08 +0200, Arnout Vandecappelle spake thusly:
> > On 01/04/2022 13:34, David Laight wrote:
> > >From: Jason A. Donenfeld
> > >>Sent: 01 April 2022 12:05
> > >>On 4/1/22, James Hilliard <james.hilliard1@gmail.com> wrote:
> > >>>I should add that I do also think this should be upstreamed to busybox,
> > >>>which
> > >>>will also reduce the amount of duplicate work as busybox is commonly used
> > >>>across many distros.
> > >>I'll work on that.
> >  +1 to have it in busybox.
>
> I still fail to understand why this can't be a standalone project.
>
> The reasoning offered by Jason is that the code should be included
> and duplicated into all and each init systems out there.
>
> However, this increases the maintenance burden, as each implementation
> has to be actively tracked (FTR: Jason said he would actively maintain
> the implementation in Buildroot, which is very nice of him, so I
> understand that he would also do so for all other projects where he'd
> have seedrng included).
>
> Instead of having one implementation suitable for every init systems
> that use shell scripts, we'd end up with many different and diverging
> implementations that each have their own warts and fixes (or worse,
> counter-productive fixes that actually decrease the robustness of that
> implementation).
>
> On the other hand, having a common project would alow to centralise the
> fixes. It would also allow to ensure that changes do not actually break
> security. Finally, any evolution, be it fixes or features, would be
> easily available to every init systems using the common project.
>
> Furthermore, the level of customisation is very low. All that we can
> expect to be customisable is the location where the seeds are stored.
> This is already accounted for in the existing seedrng git tree. Using
> another hash implementation could be another thing, but there's not
> much point here, Blake2 being already pretty strong and known. So there
> is probably not much more customisation left to do.
>
> Moving it into busybox might seem a good idea at first, but this would
> still make for an n-th implementation to track, and since busybox has a
> focus on code size, the implementation there would probably diverge
> substantially from the canonical code we saw so far, further increasing
> the maintenance burden.
>
> That would also not address distributions that do not use busybox (and
> do not use systemd either). Buildroot can even be configured in such a
> way, using a sys-v init system with coreutils et al,. and no busybox, in
> which case having seedrng only in busybox would still not solve the
> problem in such a case. And init systems are not limited to what we can
> see publicly; there are maybe hundreds or thousands of such custom init
> systems behind private doors. Buildroot even has an option to configure
> for such an init system (BR2_INIT_NONE, which really means 'custom').

Maybe it should also be upstreamed to util-linux?

>
> On a final note: systemd has native support for this feature, and thus
> one may argue that the feature is indeed already duplicated there.
> However, this is different in two ways: first, systemd needs random
> numbers for itself already, very early in the boot, possibly in an
> initramfs, so it can't easily rely on an external tool to do that;
> second, systemd is already C, so it does not make sense for the feature
> to be implemented as an external tool either.
>
> Regards,
> Yann E. MORIN.
>
> --
> .-----------------.--------------------.------------------.--------------------.
> |  Yann E. MORIN  | Real-Time Embedded | /"\ ASCII RIBBON | Erics' conspiracy: |
> | +33 662 376 056 | Software  Designer | \ / CAMPAIGN     |  ___               |
> | +33 561 099 427 `------------.-------:  X  AGAINST      |  \e/  There is no  |
> | http://ymorin.is-a-geek.org/ | _/*\_ | / \ HTML MAIL    |   v   conspiracy.  |
> '------------------------------^-------^------------------^--------------------'
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-03  7:30                           ` David Laight
@ 2022-04-04 14:32                             ` Jason A. Donenfeld
  0 siblings, 0 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-04-04 14:32 UTC (permalink / raw)
  To: David Laight; +Cc: James Hilliard, Yann E. MORIN, buildroot

Hi David,

On Sun, Apr 03, 2022 at 07:30:23AM +0000, David Laight wrote:
> >   AFAIU (but neither the commit message nor the seedrng about page explain it)
> > the Blake2 algorithm was simply chosen because it's small. Any hash function
> > should be fine. There's sha1, sha2 and sha3 in libbb, so I guess one of them
> > should have all the desired properties.
> 
> The unrolled blakes2 is typically 5kb of object code.
> The unrolled code is better for large buffers,
> but just fetching the code to the I-cache will
> significantly affect performance for small buffers.
> I really ought to sit and measure the cutoff.
> 
> The code is:
> [...]

Yes, yes, I know: you've been peppering my inbox about BLAKE2s for a few
months now. :-) By the way, for the kernel, you still haven't sent
me any real patches yet. Happy to look at whatever code with benchmarks
you have available.

Anyway, for busybox, I'll likely be using something already in libbb, so
no worries here.

Jason
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-03  9:42                           ` Yann E. MORIN
  2022-04-03 23:13                             ` James Hilliard
@ 2022-04-04 14:38                             ` Jason A. Donenfeld
  1 sibling, 0 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-04-04 14:38 UTC (permalink / raw)
  To: Yann E. MORIN; +Cc: James Hilliard, David Laight, buildroot

Hi Yann,

On Sun, Apr 03, 2022 at 11:42:36AM +0200, Yann E. MORIN wrote:
> I still fail to understand why this can't be a standalone project.

Please stop whining about this. I'm offering to maintain and support
this as part of projects where the code is relevant to have adhering to
the various customs and conventions of each project. Unless you want to
open your checkbook, I am not offering to start and maintain an external
project. Stop badgering me about this; at this point, I find it quite
obnoxious. I'm not going to spend my time doing that work. I will,
however, as stated numerous times, maintain it diligently inside
projects where it makes sense. Peter made the case that Buildroot isn't
one such project where it makes sense, a decision which I'll respect,
and so I'll look into it elsewhere, and maybe that'll circle back around
to a Buildroot package again. Either way, for the time being and at
least until that happens, your involvement here is done, and I'd
appreciate it if you would cease your pressure campaign on me to take on
volunteer work that I do not agree to do.

Thank you,
Jason
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [Buildroot] [PATCH v3] package/urandom-scripts: actually credit seed files via seedrng
  2022-04-03 23:13                             ` James Hilliard
@ 2022-04-04 14:40                               ` Jason A. Donenfeld
  0 siblings, 0 replies; 34+ messages in thread
From: Jason A. Donenfeld @ 2022-04-04 14:40 UTC (permalink / raw)
  To: James Hilliard; +Cc: David Laight, Yann E. MORIN, buildroot

Hi James,

On Sun, Apr 03, 2022 at 05:13:06PM -0600, James Hilliard wrote:
> Maybe it should also be upstreamed to util-linux?

Yea it might make sense there indeed; it's something I'm looking into.
I'll let this list know the results of all this in ${TIME_INTERVAL} when
things land various places, and I'll CC you on submissions I make.

Jason
_______________________________________________
buildroot mailing list
buildroot@buildroot.org
https://lists.buildroot.org/mailman/listinfo/buildroot

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2022-04-04 14:40 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-27  5:48 [Buildroot] [PATCH] package/urandom-scripts: actually credit seed files via seedrng Jason A. Donenfeld
2022-03-27 18:01 ` James Hilliard
2022-03-27 19:36   ` Arnout Vandecappelle
2022-03-27 19:58     ` James Hilliard
2022-03-27 20:08       ` Jason A. Donenfeld
2022-03-27 20:10         ` Jason A. Donenfeld
2022-03-27 20:24           ` [Buildroot] [PATCH v2] " Jason A. Donenfeld
2022-03-27 20:29             ` James Hilliard
2022-03-29  5:04             ` [Buildroot] [PATCH v3] " Jason A. Donenfeld
2022-03-29  6:12               ` David Laight
2022-03-30 16:32               ` Peter Korsgaard
2022-03-30 16:57                 ` David Laight
2022-03-30 17:13                 ` Jason A. Donenfeld
2022-03-31 14:50                   ` Jason A. Donenfeld
2022-03-31 14:57                   ` [Buildroot] [PATCH v4] " Jason A. Donenfeld
2022-03-31 15:16                     ` David Laight
2022-03-31 15:46                       ` David Laight
2022-03-31 17:11                   ` [Buildroot] [PATCH v3] " Peter Korsgaard
2022-04-01  8:12                     ` David Laight
2022-04-01  9:22                       ` Jason A. Donenfeld
2022-04-01 10:11                         ` David Laight
2022-04-01 10:17                           ` Jason A. Donenfeld
2022-04-01 10:57                   ` James Hilliard
2022-04-01 11:04                     ` Jason A. Donenfeld
2022-04-01 11:34                       ` David Laight
2022-04-02 17:08                         ` Arnout Vandecappelle
2022-04-03  7:30                           ` David Laight
2022-04-04 14:32                             ` Jason A. Donenfeld
2022-04-03  9:42                           ` Yann E. MORIN
2022-04-03 23:13                             ` James Hilliard
2022-04-04 14:40                               ` Jason A. Donenfeld
2022-04-04 14:38                             ` Jason A. Donenfeld
2022-03-27 20:25           ` [Buildroot] [PATCH] " James Hilliard
2022-03-27 20:26             ` Jason A. Donenfeld

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.