The Pedigree Project 0.1
random-contract-test/main.c
1/* Copyright (c) 2026, Pedigree Developers. See LICENSE. */
2#include <errno.h>
3#include <fcntl.h>
4#include <stdio.h>
5#include <string.h>
6#include <unistd.h>
7
8#include <sys/auxv.h>
9#include <sys/ioctl.h>
10#include <sys/random.h>
11#include <sys/uio.h>
12#include <sys/wait.h>
13
14#define CHECK(test) \
15 do { \
16 if (!(test)) { \
17 fprintf(stderr, "RNG FAIL line %d errno %d\n", __LINE__, errno); \
18 return 1; \
19 } \
20 } while (0)
21
22static int test_device_writes(void) {
23 const char* paths[] = {"/dev/random", "/dev/urandom"};
24 unsigned char payload[513];
25 memset(payload, 0xa5, sizeof(payload));
26 for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
27 int fd = open(paths[i], O_WRONLY);
28 CHECK(fd >= 0);
29 CHECK(write(fd, payload, 0) == 0);
30 CHECK(write(fd, payload, sizeof(payload)) == sizeof(payload));
31 struct iovec vectors[] = {{payload, 7}, {payload, 0}, {payload + 7, sizeof(payload) - 7}};
32 CHECK(writev(fd, vectors, 3) == sizeof(payload));
33
34 // Dropbear feeds bytes back through stdio while initializing its PRNG.
35 FILE* stream = fdopen(fd, "w");
36 CHECK(stream != NULL);
37 CHECK(fwrite(payload, 1, sizeof(payload), stream) == sizeof(payload));
38 CHECK(fflush(stream) == 0);
39 CHECK(fclose(stream) == 0);
40 }
41 puts("RNG-WRITE-PASS");
42 return 0;
43}
44
45int main(int argc, char** argv) {
46 unsigned char first[513], second[513];
47#if defined(__x86_64__)
48 unsigned a = 1, b, c, d;
49 __asm__ volatile("cpuid" : "+a"(a), "=b"(b), "=c"(c), "=d"(d));
50 unsigned rdrand = (c >> 30) & 1;
51 a = 7;
52 c = 0;
53 __asm__ volatile("cpuid" : "+a"(a), "=b"(b), "+c"(c), "=d"(d));
54 printf("RNG-CPU rdrand=%u rdseed=%u\n", rdrand, (b >> 18) & 1);
55#endif
56 if (argc == 2 && !strcmp(argv[1], "--unseeded")) {
57 errno = 0;
58 CHECK(getrandom(first, sizeof(first), GRND_NONBLOCK) == -1 && errno == EAGAIN);
59 CHECK(test_device_writes() == 0);
60 errno = 0;
61 CHECK(getrandom(first, sizeof(first), GRND_NONBLOCK) == -1 && errno == EAGAIN);
62 puts("RNG-UNSEEDED-PASS");
63 return 0;
64 }
65 CHECK(test_device_writes() == 0);
66 CHECK(getauxval(AT_RANDOM) != 0);
67 CHECK(getrandom(first, 0, 0) == 0);
68 CHECK(getrandom(first, 256, 0) == 256);
69 CHECK(getrandom(second, 256, GRND_NONBLOCK) == 256);
70 CHECK(memcmp(first, second, 256) != 0);
71 errno = 0;
72 CHECK(getrandom(first, 32, 0x40000000) == -1 && errno == EINVAL);
73 int random = open("/dev/urandom", O_RDONLY);
74 CHECK(random >= 0);
75 CHECK(read(random, first, sizeof(first)) == sizeof(first));
76 CHECK(read(random, second, sizeof(second)) == sizeof(second));
77 CHECK(memcmp(first, second, sizeof(first)) != 0);
78
79 struct {
80 int bits;
81 int size;
82 unsigned char seed[32];
83 } request = {256, 31, {0}};
84 errno = 0;
85 CHECK(ioctl(random, 0x40085203UL, &request) == -1 && errno == EINVAL);
86 errno = 0;
87 CHECK(ioctl(random, 0x40085203UL, (void*)1) == -1 && errno == EFAULT);
88 pid_t child = fork();
89 CHECK(child >= 0);
90 if (!child) {
91 CHECK(setuid(65534) == 0);
92 request.size = 32;
93 errno = 0;
94 CHECK(ioctl(random, 0x40085203UL, &request) == -1 && (errno == EPERM || errno == EACCES));
95 _exit(0);
96 }
97 int status;
98 CHECK(waitpid(child, &status, 0) == child && WIFEXITED(status) && !WEXITSTATUS(status));
99 close(random);
100
101 int readers[4];
102 pid_t children[4];
103 for (int i = 0; i < 4; ++i) {
104 int stream[2];
105 CHECK(pipe(stream) == 0);
106 children[i] = fork();
107 CHECK(children[i] >= 0);
108 if (!children[i]) {
109 close(stream[0]);
110 unsigned char sample[64];
111 CHECK(getrandom(sample, sizeof(sample), 0) == sizeof(sample));
112 CHECK(write(stream[1], sample, sizeof(sample)) == sizeof(sample));
113 _exit(0);
114 }
115 close(stream[1]);
116 readers[i] = stream[0];
117 }
118 unsigned char samples[4][64];
119 for (int i = 0; i < 4; ++i) {
120 CHECK(read(readers[i], samples[i], sizeof(samples[i])) == sizeof(samples[i]));
121 close(readers[i]);
122 CHECK(waitpid(children[i], &status, 0) == children[i] && WIFEXITED(status) &&
123 !WEXITSTATUS(status));
124 for (int j = 0; j < i; ++j)
125 CHECK(memcmp(samples[i], samples[j], sizeof(samples[i])) != 0);
126 }
127 puts("RNG-SEEDED-PASS");
128 return 0;
129}