File Coverage

/usr/local/lib/perl5/site_perl/5.42.0/x86_64-linux/Horus/include/horus_random.h
Criterion Covered Total %
statement 0 21 0.0
branch 0 16 0.0
condition n/a
subroutine n/a
pod n/a
total 0 37 0.0


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(_WIN32) || defined(_WIN64)
16             # define HORUS_HAVE_WIN32_CRYPT 1
17             # ifndef WIN32_LEAN_AND_MEAN
18             # define WIN32_LEAN_AND_MEAN
19             # endif
20             # include
21             /* RtlGenRandom (SystemFunction036) — available since Windows XP */
22             # ifdef __cplusplus
23             extern "C"
24             # endif
25             BOOLEAN NTAPI SystemFunction036(PVOID, ULONG);
26             # define RtlGenRandom SystemFunction036
27             #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
28             # define HORUS_HAVE_ARC4RANDOM 1
29             # include
30             #elif defined(__linux__)
31             # define HORUS_HAVE_GETRANDOM 1
32             # include
33             # include
34             # include
35             # ifdef SYS_getrandom
36             # define horus_getrandom(buf, len) syscall(SYS_getrandom, (buf), (len), 0)
37             # else
38             # undef HORUS_HAVE_GETRANDOM
39             # define HORUS_HAVE_DEVURANDOM 1
40             # endif
41             #else
42             # define HORUS_HAVE_DEVURANDOM 1
43             #endif
44              
45             #if defined(HORUS_HAVE_DEVURANDOM)
46             # include
47             # include
48             #endif
49              
50             /* ── Error handling ─────────────────────────────────────────────── */
51             /* HORUS_FATAL can be defined before including this header to override.
52             * Perl XS modules define it as croak(); standalone C uses abort(). */
53             #ifndef HORUS_FATAL
54             # include
55             # include
56             # define HORUS_FATAL(msg) do { fprintf(stderr, "%s\n", (msg)); abort(); } while(0)
57             #endif
58              
59             /* ── Low-level fill ─────────────────────────────────────────────── */
60              
61 0           static void horus_fill_raw(unsigned char *buf, size_t len) {
62             #if defined(HORUS_HAVE_WIN32_CRYPT)
63             /* RtlGenRandom takes ULONG, so fill in chunks if len > ULONG_MAX */
64             while (len > 0) {
65             ULONG chunk = (len > (size_t)0xFFFFFFFFUL) ? 0xFFFFFFFFUL : (ULONG)len;
66             if (!RtlGenRandom(buf, chunk))
67             HORUS_FATAL("Horus: RtlGenRandom failed");
68             buf += chunk;
69             len -= chunk;
70             }
71             #elif defined(HORUS_HAVE_ARC4RANDOM)
72             arc4random_buf(buf, len);
73             #elif defined(HORUS_HAVE_GETRANDOM)
74 0           size_t done = 0;
75 0 0         while (done < len) {
76 0           long ret = horus_getrandom(buf + done, len - done);
77 0 0         if (ret < 0) {
78 0 0         if (errno == EINTR) continue;
79             /* fallback to /dev/urandom on unexpected error */
80 0           goto urandom_fallback;
81             }
82 0           done += (size_t)ret;
83             }
84 0           return;
85 0           urandom_fallback:
86             {
87             #else
88             {
89             #endif
90             #if defined(HORUS_HAVE_GETRANDOM) || defined(HORUS_HAVE_DEVURANDOM)
91             static int fd = -1;
92 0 0         if (fd < 0) {
93 0           fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
94 0 0         if (fd < 0) {
95             /* This should never happen on any modern Unix */
96 0           HORUS_FATAL("Horus: cannot open /dev/urandom");
97             }
98             }
99             {
100 0           size_t done = 0;
101 0 0         while (done < len) {
102 0           ssize_t ret = read(fd, buf + done, len - done);
103 0 0         if (ret < 0) {
104 0 0         if (errno == EINTR) continue;
105 0           HORUS_FATAL("Horus: read from /dev/urandom failed");
106             }
107 0           done += (size_t)ret;
108             }
109             }
110             }
111             #endif
112             }
113              
114             /* ── Random pool ────────────────────────────────────────────────── */
115              
116             #define HORUS_POOL_SIZE 4096
117              
118             static unsigned char horus_random_pool[HORUS_POOL_SIZE];
119             static int horus_pool_pos = HORUS_POOL_SIZE; /* start exhausted */
120              
121             static inline void horus_pool_refill(void) {
122             horus_fill_raw(horus_random_pool, HORUS_POOL_SIZE);
123             horus_pool_pos = 0;
124             }
125              
126             static inline void horus_random_bytes(unsigned char *buf, size_t len) {
127             if (len >= HORUS_POOL_SIZE) {
128             /* Large request: bypass pool */
129             horus_fill_raw(buf, len);
130             return;
131             }
132             if (horus_pool_pos + (int)len > HORUS_POOL_SIZE) {
133             horus_pool_refill();
134             }
135             memcpy(buf, horus_random_pool + horus_pool_pos, len);
136             horus_pool_pos += (int)len;
137             }
138              
139             /* Fill a bulk buffer for batch UUID generation */
140             static inline void horus_random_bulk(unsigned char *buf, size_t len) {
141             horus_fill_raw(buf, len);
142             }
143              
144             #endif /* HORUS_RANDOM_H */