diff options
Diffstat (limited to 'util/libreboot-utils/lib/rand.c')
| -rw-r--r-- | util/libreboot-utils/lib/rand.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/util/libreboot-utils/lib/rand.c b/util/libreboot-utils/lib/rand.c new file mode 100644 index 00000000..7b53b6a4 --- /dev/null +++ b/util/libreboot-utils/lib/rand.c @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: MIT + * Copyright (c) 2026 Leah Rowe <leah@libreboot.org> + * + * Random number generation + */ + +#ifndef RAND_H +#define RAND_H + +#ifdef __OpenBSD__ +#include <sys/param.h> +#endif +#include <sys/types.h> + +#ifndef USE_URANDOM +#define USE_URANDOM 0 +#endif + +#include <errno.h> +#if defined(USE_URANDOM) && \ + ((USE_URANDOM) > 0) +#include <fcntl.h> /* if not arc4random: /dev/urandom */ +#endif + +#include <fcntl.h> +#include <limits.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include "../include/common.h" + +/* Regarding Linux getrandom/urandom: + * + * For maximum security guarantee, we *only* + * use getrandom via syscall, or /dev/urandom; + * use of urandom is ill advised. This is why + * we use the syscall, in case the libc version + * of getrandom() might defer to /dev/urandom + * + * We *abort* on error, for both /dev/urandom + * and getrandom(), because the BSD arc4random + * never returns with error; therefore, for the + * most parity in terms of behaviour, we abort, + * because otherwise the function would have two + * return modes: always successful (BSD), or only + * sometimes (Linux). The BSD arc4random could + * theoretically abort; it is extremely unlikely + * there, and just so on Linux, hence this design. + * + * This is important, because cryptographic code + * for example must not rely on weak randomness. + * We must therefore treat broken randomness as + * though the world is broken, and burn accordingly. + * + * Similarly, any invalid input (NULL, zero bytes + * requested) are treated as fatal errors; again, + * cryptographic code must be reliable. If your + * code erroneously requested zero bytes, you might + * then end up with a non-randomised buffer, where + * you likely intended otherwise. + * + * In other words: call rset() correctly, or your + * program dies, and rset will behave correctly, + * or your program dies. + */ + +void +rset(void *buf, size_t n) +{ + int saved_errno = errno; + + if (if_err(buf == NULL, EFAULT)) + goto err; + + if (n == 0) + err_no_cleanup(0, EPERM, + "rset: zero-byte length request"); + +#if (defined(__OpenBSD__) || defined(__FreeBSD__) || \ + defined(__NetBSD__) || defined(__APPLE__) || \ + defined(__DragonFly__)) && !(defined(USE_URANDOM) && \ + ((USE_URANDOM) > 0)) + + arc4random_buf(buf, n); + goto out; +#else + size_t off = 0; + ssize_t rc = 0; + +#if defined(USE_URANDOM) && \ + ((USE_URANDOM) > 0) + int fd = -1; + + if ((fd = open("/dev/urandom", O_RDONLY)) < 0) + goto err; +retry_rand: + if ((rc = read(fd, (unsigned char *)buf + off, n - off)) < 0) { +#elif defined(__linux__) +retry_rand: + if ((rc = (ssize_t)syscall(SYS_getrandom, + (unsigned char *)buf + off, n - off, 0)) < 0) { +#else +#error Unsupported operating system (possibly unsecure randomisation) +#endif + if (errno == EINTR || + errno == EAGAIN) + goto retry_rand; + + goto err; /* possibly unsupported by kernel */ + } + + if (rc == 0) + goto err; /* prevent infinite loop on fatal err */ + + if ((off += (size_t)rc) < n) + goto retry_rand; + +#if defined(USE_URANDOM) && \ + ((USE_URANDOM) > 0) + close_no_err(&fd); +#endif + goto out; +#endif +out: + errno = saved_errno; + return; +err: + err_no_cleanup(0, ECANCELED, + "Randomisation failure, possibly unsupported in your kernel"); + exit(EXIT_FAILURE); +} +#endif |
