]> mj.ucw.cz Git - libucw.git/blob - ucw/random-strong.c
Random: Detect and use getrandom() to seed fastrand.
[libucw.git] / ucw / random-strong.c
1 /*
2  *      UCW Library -- Strong random generator
3  *
4  *      (c) 2020--2022 Pavel Charvat <pchar@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include <ucw/lib.h>
13 #include <ucw/random.h>
14 #include <ucw/threads.h>
15
16 #include <errno.h>
17 #include <string.h>
18
19 #ifdef CONFIG_UCW_GETRANDOM
20 #include <sys/random.h>
21 #endif
22
23 /*** getrandom() helpers ***/
24
25 #ifdef CONFIG_UCW_GETRANDOM
26 static bool strongrand_getrandom_detect(void)
27 {
28   static int static_detected;
29   int detected;
30   ucwlib_lock();
31   detected = static_detected;
32   if (detected)
33     {
34       ucwlib_unlock();
35       return detected > 0;
36     }
37   byte buf[1];
38   int err;
39   while (1)
40     {
41       int n = getrandom(buf, 1, GRND_NONBLOCK);
42       if (n >= 0)
43         {
44           ASSERT(n == 1);
45           detected = 1;
46           break;
47         }
48       else if (errno != EINTR)
49         {
50           err = errno;
51           detected = -1;
52           break;
53         }
54     }
55   static_detected = detected;
56   ucwlib_unlock();
57   if (detected > 0)
58     {
59       DBG("RANDOM: Kernel supports getrandom()");
60       return true;
61     }
62   else if (err == ENOSYS)
63     {
64       DBG("RANDOM: Kernel does not support getrandom()");
65       return false;
66     }
67   else
68     {
69       // We print an error also for EAGAIN -- for urandom it should be possible only during early boot.
70       msg(L_ERROR, "RANDOM: Failed to call getrandom(): %s -> assuming no support", strerror(err));
71       return false;
72     }
73 }
74
75 bool strongrand_getrandom_mem_try(void *buf, size_t size)
76 {
77   if (!strongrand_getrandom_detect())
78     return false;
79   while (size)
80     {
81       ssize_t n = getrandom(buf, size, 0);
82       if (n < 0)
83         {
84           if (errno == EINTR)
85             continue;
86           die("RANDOM: Failed to call getrandom(): %m");
87         }
88       buf = (byte *)buf + n;
89       size -= n;
90     }
91   return true;
92 }
93 #else
94 bool strongrand_getrandom_mem_try(void *buf UNUSED, size_t size UNUSED)
95 {
96   return false;
97 }
98 #endif