File Coverage

include/horus_random.h
Criterion Covered Total %
statement 20 36 55.5
branch 6 20 30.0
condition n/a
subroutine n/a
pod n/a
total 26 56 46.4


line stmt bran cond sub pod time code
1             #ifndef HORUS_RANDOM_H
2             #define HORUS_RANDOM_H
3              
4             /*
5             * horus_random.h - Platform-native CSPRNG with pool buffering
6             *
7             * Strategy: arc4random_buf (macOS/BSD) > getrandom (Linux 3.17+) > /dev/urandom
8             * A 4096-byte pool amortises syscall cost across ~256 UUID generations.
9             */
10              
11             #include
12              
13             /* ── Platform detection ─────────────────────────────────────────── */
14              
15             #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
16             # define HORUS_HAVE_ARC4RANDOM 1
17             # include
18             #elif defined(__linux__)
19             # define HORUS_HAVE_GETRANDOM 1
20             # include
21             # include
22             # include
23             # ifdef SYS_getrandom
24             # define horus_getrandom(buf, len) syscall(SYS_getrandom, (buf), (len), 0)
25             # else
26             # undef HORUS_HAVE_GETRANDOM
27             # define HORUS_HAVE_DEVURANDOM 1
28             # endif
29             #else
30             # define HORUS_HAVE_DEVURANDOM 1
31             #endif
32              
33             #if defined(HORUS_HAVE_DEVURANDOM)
34             # include
35             # include
36             #endif
37              
38             /* ── Error handling ─────────────────────────────────────────────── */
39             /* HORUS_FATAL can be defined before including this header to override.
40             * Perl XS modules define it as croak(); standalone C uses abort(). */
41             #ifndef HORUS_FATAL
42             # include
43             # include
44             # define HORUS_FATAL(msg) do { fprintf(stderr, "%s\n", (msg)); abort(); } while(0)
45             #endif
46              
47             /* ── Low-level fill ─────────────────────────────────────────────── */
48              
49 24           static void horus_fill_raw(unsigned char *buf, size_t len) {
50             #if defined(HORUS_HAVE_ARC4RANDOM)
51             arc4random_buf(buf, len);
52             #elif defined(HORUS_HAVE_GETRANDOM)
53 24           size_t done = 0;
54 48 100         while (done < len) {
55 24           long ret = horus_getrandom(buf + done, len - done);
56 24 50         if (ret < 0) {
57 0 0         if (errno == EINTR) continue;
58             /* fallback to /dev/urandom on unexpected error */
59 0           goto urandom_fallback;
60             }
61 24           done += (size_t)ret;
62             }
63 24           return;
64 0           urandom_fallback:
65             {
66             #else
67             {
68             #endif
69             #if defined(HORUS_HAVE_GETRANDOM) || defined(HORUS_HAVE_DEVURANDOM)
70             static int fd = -1;
71 0 0         if (fd < 0) {
72 0           fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
73 0 0         if (fd < 0) {
74             /* This should never happen on any modern Unix */
75 0           HORUS_FATAL("Horus: cannot open /dev/urandom");
76             }
77             }
78             {
79 0           size_t done = 0;
80 0 0         while (done < len) {
81 0           ssize_t ret = read(fd, buf + done, len - done);
82 0 0         if (ret < 0) {
83 0 0         if (errno == EINTR) continue;
84 0           HORUS_FATAL("Horus: read from /dev/urandom failed");
85             }
86 0           done += (size_t)ret;
87             }
88             }
89             }
90             #endif
91             }
92              
93             /* ── Random pool ────────────────────────────────────────────────── */
94              
95             #define HORUS_POOL_SIZE 4096
96              
97             static unsigned char horus_random_pool[HORUS_POOL_SIZE];
98             static int horus_pool_pos = HORUS_POOL_SIZE; /* start exhausted */
99              
100 22           static inline void horus_pool_refill(void) {
101 22           horus_fill_raw(horus_random_pool, HORUS_POOL_SIZE);
102 22           horus_pool_pos = 0;
103 22           }
104              
105 1160           static inline void horus_random_bytes(unsigned char *buf, size_t len) {
106 1160 50         if (len >= HORUS_POOL_SIZE) {
107             /* Large request: bypass pool */
108 0           horus_fill_raw(buf, len);
109 0           return;
110             }
111 1160 100         if (horus_pool_pos + (int)len > HORUS_POOL_SIZE) {
112 3           horus_pool_refill();
113             }
114 1160           memcpy(buf, horus_random_pool + horus_pool_pos, len);
115 1160           horus_pool_pos += (int)len;
116             }
117              
118             /* Fill a bulk buffer for batch UUID generation */
119 2           static inline void horus_random_bulk(unsigned char *buf, size_t len) {
120 2           horus_fill_raw(buf, len);
121 2           }
122              
123             #endif /* HORUS_RANDOM_H */