2#include "pedigree/kernel/utilities/SecureRandom.h"
12#include <sys/random.h>
16constexpr unsigned long AddEntropy = 0x40085203UL;
17constexpr char DefaultSeed[] =
"/var/lib/pedigree/random-seed";
20 unsigned char saved[32] = {};
21 unsigned char next[32] = {};
23 int entropyBits = 256;
25 unsigned char seed[32] = {};
28 pedigree_random::erase(
this,
sizeof(*
this));
32int fail(
const char* operation) {
33 fprintf(stderr,
"random-seed: %s: %s; seed not activated\n", operation, strerror(errno));
37bool privateFile(
int fd,
bool directory =
false) {
39 if (fstat(fd, &st) != 0)
41 if (st.st_uid || (st.st_mode & 0777) != (directory ? 0700 : 0600) ||
42 (directory ? !S_ISDIR(st.st_mode) : (!S_ISREG(st.st_mode) || st.st_nlink != 1))) {
49bool transfer(
int fd,
unsigned char* bytes,
size_t count,
bool writing) {
51 while (done < count) {
53 writing ? write(fd, bytes + done, count - done) : read(fd, bytes + done, count - done);
54 if (n < 0 && errno == EINTR)
61 done +=
static_cast<size_t>(n);
67int main(
int argc,
char** argv) {
68 if (argc == 2 && !strcmp(argv[1],
"--check")) {
69 unsigned char bytes[32];
70 const bool ready = getrandom(bytes,
sizeof(bytes), GRND_NONBLOCK) ==
sizeof(bytes);
71 pedigree_random::erase(bytes,
sizeof(bytes));
72 puts(ready ?
"Secure random generator is ready." :
"Secure random generator is not seeded.");
75 if (argc > 2 || (argc == 2 && argv[1][0] ==
'-')) {
76 fprintf(stderr,
"Usage: random-seed [SEED_FILE] | --check\n");
81 return fail(
"requires root");
83 const char* path = argc == 2 ? argv[1] : DefaultSeed;
84 char parent[4096], name[256], temporary[272], lockname[272];
85 const char* slash = strrchr(path,
'/');
86 if (!slash || slash == path || !slash[1] ||
static_cast<size_t>(slash - path) >=
sizeof(parent) ||
87 strlen(slash + 1) >=
sizeof(name)) {
89 return fail(
"seed path must have a private parent directory");
91 memcpy(parent, path, slash - path);
92 parent[slash - path] = 0;
93 strcpy(name, slash + 1);
94 snprintf(temporary,
sizeof(temporary),
"%s.next", name);
95 snprintf(lockname,
sizeof(lockname),
"%s.lock", name);
96 int directory = open(parent, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
97 if (directory < 0 || !privateFile(directory,
true))
98 return fail(
"open private seed directory (root:root, mode 0700)");
102 int lock = openat(directory, lockname, O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
103 if (lock < 0 || !privateFile(lock) || flock(lock, LOCK_EX | LOCK_NB))
104 return fail(
"lock seed transaction");
105 int input = openat(directory, name, O_RDONLY | O_NOFOLLOW);
106 if (input < 0 || !privateFile(input))
107 return fail(
"open private seed file (root:root, mode 0600)");
109 if (!transfer(input, secrets.saved,
sizeof(secrets.saved),
false))
110 return fail(
"read 32-byte seed");
112 if (read(input, &extra, 1) != 0) {
114 return fail(
"seed must contain exactly 32 bytes");
117 return fail(
"close seed input");
118 constexpr char KernelDomain[] =
"Pedigree kernel seed v1";
119 constexpr char SavedDomain[] =
"Pedigree saved seed v1";
120 pedigree_random::hmac_sha256(secrets.saved, KernelDomain,
sizeof(KernelDomain) - 1,
121 secrets.request.seed);
122 pedigree_random::hmac_sha256(secrets.saved, SavedDomain,
sizeof(SavedDomain) - 1, secrets.next);
123 pedigree_random::erase(secrets.saved,
sizeof(secrets.saved));
127 int output = openat(directory, temporary, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
129 return fail(
"create successor (recover a stale .next file from rescue Linux)");
130 if (!transfer(output, secrets.next,
sizeof(secrets.next),
true) || fsync(output) || close(output))
131 return fail(
"persist successor");
132 if (renameat(directory, temporary, directory, name) || fsync(directory))
133 return fail(
"persist seed replacement");
137 int random = open(
"/dev/random", O_RDONLY);
138 if (random < 0 || ioctl(random, AddEntropy, &secrets.request))
139 return fail(
"activate kernel seed");
140 puts(
"random-seed: advanced saved seed and initialized secure randomness");