* [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 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-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-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 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
* 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] 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
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.