comparison librandom.c @ 0:9ee92e7a1cb5 default tip

*: initial commit this is a very simple, bare-bones randomness API. if the system can't provide it, so be it -- init will fail.
author Paper <paper@tflc.us>
date Tue, 10 Jun 2025 16:18:50 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:9ee92e7a1cb5
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4
5 #include "librandom.h"
6
7 #ifdef _WIN32
8 # define RANDOM_WIN32
9 #elif defined(__MACH__) && defined(__APPLE__)
10 # define RANDOM_ARC4RANDOM
11 #else
12 # define RANDOM_UNIX
13 #endif
14
15 #ifdef RANDOM_WIN32
16 # include <windows.h>
17 # include <wincrypt.h>
18 #endif
19
20 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(*(x)))
21
22 /* NOTE: I am not sure if other platforms provide real APIs for good
23 * randomness. Macintosh provides SetRandomSeed and RandomBits, but I
24 * am skeptical of its use as a good random number generator. */
25
26 #ifdef RANDOM_UNIX
27
28 struct random_unix {
29 FILE *f;
30 };
31
32 static void random_unix_close(struct random *r)
33 {
34 struct random_unix *ru = r->userdata;
35
36 if (ru->f)
37 fclose(ru->f);
38
39 free(ru);
40 }
41
42 static size_t random_unix_read(struct random *r, void *ptr, size_t s)
43 {
44 struct random_unix *ru = r->userdata;
45
46 return fread(ptr, s, 1, ru->f);
47 }
48
49 static int random_unix_init(struct random *r)
50 {
51 /* hmmm. */
52 static const char *names[] = {
53 "/dev/random",
54 "/dev/urandom",
55 };
56 struct random_unix *ru = malloc(sizeof(*ru));
57 size_t i;
58
59 for (i = 0; i < ARRAY_SIZE(names); i++) {
60 ru->f = fopen(names[i], "rb");
61 if (ru->f)
62 goto gotdevice;
63 }
64
65 free(ru);
66 return -1;
67
68 gotdevice:
69
70 r->userdata = ru;
71 r->close = random_unix_close;
72 r->read = random_unix_read;
73
74 return 0;
75 }
76
77 #endif
78
79 /* ------------------------------------------------------------------------ */
80
81 #ifdef RANDOM_ARC4RANDOM
82
83 static size_t random_arc4random_read(struct random *r, void *ptr, size_t s)
84 {
85 size_t l = s;
86
87 while (l > 0) {
88 size_t amt;
89 uint32_t x;
90
91 amt = MIN(sizeof(x), l);
92 x = arc4random();
93
94 memcpy(ptr, &x, amt);
95
96 ptr += amt;
97 l -= amt;
98 }
99
100 return s;
101 }
102
103 static void random_arc4random_close(struct random *r)
104 {
105 /* nothing */
106 }
107
108 static int random_arc4random_init(struct random *r)
109 {
110 /* always successful */
111 r->read = random_arc4random_read;
112 r->close = random_arc4random_close;
113 return 0;
114 }
115
116 #endif
117
118 /* ------------------------------------------------------------------------ */
119
120 #ifdef RANDOM_WIN32
121
122 typedef BOOL (*pADVAPI32_CryptReleaseContext)(HCRYPTPROV hProv, DWORD dwFlags);
123 typedef BOOL (*pADVAPI32_CryptGenRandom)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
124 typedef BOOL (*pADVAPI32_CryptAcquireContextA)(HCRYPTPROV *phProv, LPCSTR szContainer, LPCSTR szProvider, DWORD dwProvType,DWORD dwFlags);
125
126 struct random_win32 {
127 HCRYPTPROV c;
128
129 HMODULE advapi32;
130 pADVAPI32_CryptReleaseContext ADVAPI32_CryptReleaseContext;
131 pADVAPI32_CryptGenRandom ADVAPI32_CryptGenRandom;
132 pADVAPI32_CryptAcquireContextA ADVAPI32_CryptAcquireContextA;
133 };
134
135 static void random_win32_close(struct random *r)
136 {
137 struct random_win32 *rw = r->userdata;
138
139 rw->ADVAPI32_CryptReleaseContext(rw->c, 0);
140 FreeLibrary(rw->advapi32);
141
142 free(rw);
143 }
144
145 static size_t random_win32_read(struct random *r, void *ptr, size_t s)
146 {
147 struct random_win32 *rw = r->userdata;
148
149 if (!rw->ADVAPI32_CryptGenRandom(rw->c, s, ptr))
150 return 0;
151
152 return s;
153 }
154
155 static int random_win32_init(struct random *r)
156 {
157 HCRYPTPROV c;
158 struct random_win32 *rw;
159
160 rw = malloc(sizeof(*rw));
161 if (!rw)
162 return -2;
163
164 rw->advapi32 = LoadLibraryA("ADVAPI32.DLL");
165 if (!rw->advapi32)
166 return -3;
167
168 #define LOADFUNC(x) \
169 rw->ADVAPI32_##x = (pADVAPI32_##x)GetProcAddress(rw->advapi32, #x); \
170 if (!rw->ADVAPI32_##x) \
171 return -4
172
173 LOADFUNC(CryptAcquireContextA);
174 LOADFUNC(CryptGenRandom);
175 LOADFUNC(CryptReleaseContext);
176
177 if (!rw->ADVAPI32_CryptAcquireContextA(&c, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
178 return -1;
179
180
181 rw->c = c;
182
183 r->userdata = rw;
184 r->close = random_win32_close;
185 r->read = random_win32_read;
186 return 0;
187 }
188
189 #endif
190
191 /* ------------------------------------------------------------------------ */
192 /* platform-independent routines */
193
194 int random_init(struct random *r)
195 {
196 int i;
197
198 int (*funcs[])(struct random *r) = {
199 #ifdef RANDOM_ARC4RANDOM
200 random_arc4random_init,
201 #endif
202 #ifdef RANDOM_UNIX
203 random_unix_init,
204 #endif
205 #ifdef RANDOM_WIN32
206 random_win32_init,
207 #endif
208 NULL,
209 };
210
211 for (i = 0; funcs[i]; i++)
212 if (!funcs[i](r))
213 return 0;
214
215 return -1;
216 }
217
218 void random_close(struct random *r)
219 {
220 r->close(r);
221 }
222
223 size_t random_read(struct random *r, void *ptr, size_t s)
224 {
225 return r->read(r, ptr, s);
226 }