2 * UCW Library -- Strong random generator
4 * (c) 2020--2022 Pavel Charvat <pchar@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
13 #include <ucw/random.h>
14 #include <ucw/threads.h>
20 #include <sys/types.h>
22 #ifdef CONFIG_UCW_GETRANDOM
23 #include <sys/random.h>
26 /*** getrandom() helpers ***/
28 #ifdef CONFIG_UCW_GETRANDOM
29 static bool strongrand_getrandom_detect(void)
31 static int static_detected;
34 detected = static_detected;
44 int n = getrandom(buf, 1, GRND_NONBLOCK);
51 else if (errno != EINTR)
58 static_detected = detected;
62 DBG("RANDOM: Kernel supports getrandom()");
65 else if (err == ENOSYS)
67 DBG("RANDOM: Kernel does not support getrandom()");
72 // We print an error also for EAGAIN -- for urandom it should be possible only during early boot.
73 msg(L_ERROR, "RANDOM: Failed to call getrandom(): %s -> assuming no support", strerror(err));
78 bool strongrand_getrandom_mem_try(void *buf, size_t size)
80 if (!strongrand_getrandom_detect())
84 ssize_t n = getrandom(buf, size, 0);
89 die("RANDOM: Failed to call getrandom(): %m");
91 buf = (byte *)buf + n;
97 bool strongrand_getrandom_mem_try(void *buf UNUSED, size_t size UNUSED)
103 /*** Generic interface ***/
105 void strongrand_close(struct strongrand *c)
111 void strongrand_reset(struct strongrand *c)
117 void strongrand_mem(struct strongrand *c, void *ptr, size_t size)
122 int n = MIN(size, 0x7fffffff);
125 ptr = (byte *)ptr + n;
129 /*** Kernel random source ***/
131 struct strongrand_std {
132 struct strongrand sr;
133 int fd; // Non-negative descriptor or special STRONGRAND_FD_x value
134 uint flags; // STRONGRAND_STD_x
135 uint buf_size; // Size of @buf or 0
136 uint buf_avail; // Remaining buffered bytes, <= @buf_size
137 pid_t last_pid; // Last known process id; maintained with STRONGRAND_STD_x_RESET
138 byte buf[]; // Buffered randomness
142 #define STRONGRAND_FD_KERNEL_GETRANDOM -1
144 // Not yet open /dev/[u]random
145 #define STRONGRAND_FD_KERNEL_DEVICE -2
147 static void strongrand_std_close(struct strongrand *sr)
149 struct strongrand_std *srs = (struct strongrand_std *)sr;
152 DBG("RANDOM[%s]: Closed", sr->name);
156 static void strongrand_std_reset(struct strongrand *sr)
158 struct strongrand_std *srs = (struct strongrand_std *)sr;
162 static void strongrand_std_open_device(struct strongrand_std *srs)
164 ASSERT(srs->fd == STRONGRAND_FD_KERNEL_DEVICE);
165 ASSERT(srs->flags & (STRONGRAND_STD_URANDOM | STRONGRAND_STD_RANDOM));
166 const char *path = (srs->flags & STRONGRAND_STD_URANDOM) ? "/dev/urandom" : "/dev/random";
167 int fd = open(path, O_RDONLY);
169 die("RANDOM[%s]: Cannot open %s: %m", srs->sr.name, path);
171 DBG("RANDOM[%s]: Opened device, path=%s, fd=%d", srs->sr.name, path, fd);
174 static void strongrand_std_read_unbuffered(struct strongrand *sr, byte *ptr, int size)
176 struct strongrand_std *srs = (struct strongrand_std *)sr;
177 #ifdef CONFIG_UCW_GETRANDOM
178 if (srs->fd == STRONGRAND_FD_KERNEL_GETRANDOM)
182 int n = getrandom(ptr, size, (srs->flags & STRONGRAND_STD_RANDOM) ? GRND_RANDOM : 0);
185 if (errno == EINTR || errno == EAGAIN)
187 die("RANDOM[%s]: Failed to read %u bytes with getrandom(): %m", srs->sr.name, size);
189 DBG("RANDOM[%s]: Read %u bytes with getrandom()", srs->sr.name, n);
199 ASSERT(srs->flags & STRONGRAND_STD_DELAYED);
200 strongrand_std_open_device(srs);
204 int n = read(srs->fd, ptr, size);
207 if (errno == EINTR || errno == EAGAIN)
209 die("RANDOM[%s]: Failed to read %u bytes: %m", srs->sr.name, size);
212 DBG("RANDOM[%s]: Read %u bytes", srs->sr.name, n);
218 static void strongrand_std_read_buffered(struct strongrand *sr, byte *ptr, int size)
220 struct strongrand_std *srs = (struct strongrand_std *)sr;
221 if (srs->flags & (STRONGRAND_STD_AUTO_RESET | STRONGRAND_STD_ASSERT_RESET))
223 pid_t pid = getpid();
224 if (pid != srs->last_pid)
226 DBG("RANDOM[%s]: Detected changed pid from %u to %u", sr->name, (uint)srs->last_pid, (uint)pid);
228 if (srs->flags & STRONGRAND_STD_ASSERT_RESET)
229 ASSERT(!srs->buf_avail);
237 DBG("RANDOM[%s]: Refilling buffer", srs->sr.name);
238 strongrand_std_read_unbuffered(sr, srs->buf, srs->buf_size);
239 srs->buf_avail = srs->buf_size;
241 uint n = MIN(srs->buf_avail, (uint)size);
242 memcpy(ptr, srs->buf + (srs->buf_size - srs->buf_avail), n);
249 static struct strongrand *strongrand_std_open_internal(int fd, const char *name, uint buf_size, uint flags)
251 struct strongrand_std *srs = xmalloc(sizeof(*srs) + buf_size);
252 bzero(srs, sizeof(*srs));
254 srs->sr.close = strongrand_std_close;
255 srs->sr.reset = buf_size ? strongrand_std_reset : NULL;
256 srs->sr.read = buf_size ? strongrand_std_read_buffered : strongrand_std_read_unbuffered;
259 srs->buf_size = buf_size;
260 if (flags & (STRONGRAND_STD_AUTO_RESET | STRONGRAND_STD_ASSERT_RESET))
261 srs->last_pid = getpid();
265 struct strongrand *strongrand_std_open(uint buf_size, uint flags)
268 if (flags & STRONGRAND_STD_URANDOM)
270 ASSERT(!(flags & STRONGRAND_STD_RANDOM));
275 ASSERT(flags & STRONGRAND_STD_RANDOM);
278 #ifdef CONFIG_UCW_GETRANDOM
279 if (strongrand_getrandom_detect())
280 return strongrand_std_open_internal(STRONGRAND_FD_KERNEL_GETRANDOM, name, buf_size, flags);
282 struct strongrand *sr = strongrand_std_open_internal(STRONGRAND_FD_KERNEL_DEVICE, name, buf_size, flags);
283 if (!(flags & STRONGRAND_STD_DELAYED))
284 strongrand_std_open_device((struct strongrand_std *)sr);