]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/randutils.c
2 * General purpose random utilities
4 * Based on libuuid code.
6 * This file may be redistributed under the terms of the
7 * GNU Lesser General Public License.
16 #include <sys/syscall.h>
19 #include "randutils.h"
23 #define THREAD_LOCAL static __thread
25 #define THREAD_LOCAL static
29 # include <sys/random.h>
30 #elif defined (__linux__)
31 # if !defined(SYS_getrandom) && defined(__NR_getrandom)
32 /* usable kernel-headers, but old glibc-headers */
33 # define SYS_getrandom __NR_getrandom
37 #if !defined(HAVE_GETRANDOM) && defined(SYS_getrandom)
38 /* libc without function, but we have syscal */
39 #define GRND_NONBLOCK 0x01
40 #define GRND_RANDOM 0x02
41 static int getrandom(void *buf
, size_t buflen
, unsigned int flags
)
43 return (syscall(SYS_getrandom
, buf
, buflen
, flags
));
45 # define HAVE_GETRANDOM
48 #if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
50 THREAD_LOCAL
unsigned short ul_jrand_seed
[3];
53 int rand_get_number(int low_n
, int high_n
)
55 return rand() % (high_n
- low_n
+ 1) + low_n
;
58 static void crank_random(void)
63 gettimeofday(&tv
, NULL
);
64 srand((getpid() << 16) ^ getuid() ^ tv
.tv_sec
^ tv
.tv_usec
);
67 ul_jrand_seed
[0] = getpid() ^ (tv
.tv_sec
& 0xFFFF);
68 ul_jrand_seed
[1] = getppid() ^ (tv
.tv_usec
& 0xFFFF);
69 ul_jrand_seed
[2] = (tv
.tv_sec
^ tv
.tv_usec
) >> 16;
71 /* Crank the random number generator a few times */
72 gettimeofday(&tv
, NULL
);
73 for (i
= (tv
.tv_sec
^ tv
.tv_usec
) & 0x1F; i
> 0; i
--)
77 int random_get_fd(void)
81 fd
= open("/dev/urandom", O_RDONLY
| O_CLOEXEC
);
83 fd
= open("/dev/random", O_RDONLY
| O_NONBLOCK
| O_CLOEXEC
);
85 i
= fcntl(fd
, F_GETFD
);
87 fcntl(fd
, F_SETFD
, i
| FD_CLOEXEC
);
94 * Generate a stream of random nbytes into buf.
95 * Use /dev/urandom if possible, and if not,
96 * use glibc pseudo-random functions.
98 #define UL_RAND_READ_ATTEMPTS 8
99 #define UL_RAND_READ_DELAY 125000 /* microseconds */
101 void random_get_bytes(void *buf
, size_t nbytes
)
103 unsigned char *cp
= (unsigned char *)buf
;
104 size_t i
, n
= nbytes
;
105 int lose_counter
= 0;
107 #ifdef HAVE_GETRANDOM
112 x
= getrandom(cp
, n
, GRND_NONBLOCK
);
113 if (x
> 0) { /* success */
118 } else if (errno
== ENOSYS
) { /* kernel without getrandom() */
121 } else if (errno
== EAGAIN
&& lose_counter
< UL_RAND_READ_ATTEMPTS
) {
122 xusleep(UL_RAND_READ_DELAY
); /* no etropy, wait and try again */
131 * We've been built against headers that support getrandom, but the
132 * running kernel does not. Fallback to reading from /dev/{u,}random
136 int fd
= random_get_fd();
141 ssize_t x
= read(fd
, cp
, n
);
143 if (lose_counter
++ > UL_RAND_READ_ATTEMPTS
)
145 xusleep(UL_RAND_READ_DELAY
);
157 * We do this all the time, but this is the only source of
158 * randomness if /dev/random/urandom is out to lunch.
161 for (cp
= buf
, i
= 0; i
< nbytes
; i
++)
162 *cp
++ ^= (rand() >> 7) & 0xFF;
166 unsigned short tmp_seed
[3];
168 memcpy(tmp_seed
, ul_jrand_seed
, sizeof(tmp_seed
));
169 ul_jrand_seed
[2] = ul_jrand_seed
[2] ^ syscall(__NR_gettid
);
170 for (cp
= buf
, i
= 0; i
< nbytes
; i
++)
171 *cp
++ ^= (jrand48(tmp_seed
) >> 7) & 0xFF;
172 memcpy(ul_jrand_seed
, tmp_seed
,
173 sizeof(ul_jrand_seed
)-sizeof(unsigned short));
181 * Tell source of randomness.
183 const char *random_tell_source(void)
185 #ifdef HAVE_GETRANDOM
186 return _("getrandom() function");
189 static const char *random_sources
[] = {
194 for (i
= 0; i
< ARRAY_SIZE(random_sources
); i
++) {
195 if (!access(random_sources
[i
], R_OK
))
196 return random_sources
[i
];
199 return _("libc pseudo-random functions");
202 #ifdef TEST_PROGRAM_RANDUTILS
203 #include <inttypes.h>
205 int main(int argc
, char *argv
[])
212 n
= argc
== 1 ? 16 : atoi(argv
[1]);
214 printf("Multiple random calls:\n");
215 for (i
= 0; i
< n
; i
++) {
216 random_get_bytes(&v
, sizeof(v
));
217 printf("#%02zu: %25"PRIu64
"\n", i
, v
);
221 printf("One random call:\n");
222 bufsz
= n
* sizeof(*vp
);
225 err(EXIT_FAILURE
, "failed to allocate buffer");
227 random_get_bytes(buf
, bufsz
);
228 for (i
= 0; i
< n
; i
++) {
229 vp
= (int64_t *) (buf
+ (i
* sizeof(*vp
)));
230 printf("#%02zu: %25"PRIu64
"\n", i
, *vp
);
235 #endif /* TEST_PROGRAM_RANDUTILS */