]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/randutils.c
2 * SPDX-License-Identifier: BSD-3-Clause
4 * General purpose random utilities. Based on libuuid code.
6 * This code is free software; you can redistribute it and/or modify it under
7 * the terms of the Modified BSD License. The complete text of the license is
8 * available in the Documentation/licenses/COPYING.BSD-3-Clause file.
17 #include <sys/syscall.h>
20 #include "randutils.h"
24 #define THREAD_LOCAL static __thread
26 #define THREAD_LOCAL static
30 # include <sys/random.h>
31 #elif defined (__linux__)
32 # if !defined(SYS_getrandom) && defined(__NR_getrandom)
33 /* usable kernel-headers, but old glibc-headers */
34 # define SYS_getrandom __NR_getrandom
38 #if !defined(HAVE_GETRANDOM) && defined(SYS_getrandom)
39 /* libc without function, but we have syscal */
40 #define GRND_NONBLOCK 0x01
41 #define GRND_RANDOM 0x02
42 static int getrandom(void *buf
, size_t buflen
, unsigned int flags
)
44 return (syscall(SYS_getrandom
, buf
, buflen
, flags
));
46 # define HAVE_GETRANDOM
49 #if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
51 THREAD_LOCAL
unsigned short ul_jrand_seed
[3];
54 int rand_get_number(int low_n
, int high_n
)
56 return rand() % (high_n
- low_n
+ 1) + low_n
;
59 static void crank_random(void)
64 gettimeofday(&tv
, NULL
);
65 srand((getpid() << 16) ^ getuid() ^ tv
.tv_sec
^ tv
.tv_usec
);
68 ul_jrand_seed
[0] = getpid() ^ (tv
.tv_sec
& 0xFFFF);
69 ul_jrand_seed
[1] = getppid() ^ (tv
.tv_usec
& 0xFFFF);
70 ul_jrand_seed
[2] = (tv
.tv_sec
^ tv
.tv_usec
) >> 16;
72 /* Crank the random number generator a few times */
73 gettimeofday(&tv
, NULL
);
74 for (i
= (tv
.tv_sec
^ tv
.tv_usec
) & 0x1F; i
> 0; i
--)
78 int random_get_fd(void)
82 fd
= open("/dev/urandom", O_RDONLY
| O_CLOEXEC
);
84 fd
= open("/dev/random", O_RDONLY
| O_NONBLOCK
| O_CLOEXEC
);
86 i
= fcntl(fd
, F_GETFD
);
88 fcntl(fd
, F_SETFD
, i
| FD_CLOEXEC
);
95 * Generate a stream of random nbytes into buf.
96 * Use /dev/urandom if possible, and if not,
97 * use glibc pseudo-random functions.
99 #define UL_RAND_READ_ATTEMPTS 8
100 #define UL_RAND_READ_DELAY 125000 /* microseconds */
102 void random_get_bytes(void *buf
, size_t nbytes
)
104 unsigned char *cp
= (unsigned char *)buf
;
105 size_t i
, n
= nbytes
;
106 int lose_counter
= 0;
108 #ifdef HAVE_GETRANDOM
113 x
= getrandom(cp
, n
, GRND_NONBLOCK
);
114 if (x
> 0) { /* success */
119 } else if (errno
== ENOSYS
) { /* kernel without getrandom() */
122 } else if (errno
== EAGAIN
&& lose_counter
< UL_RAND_READ_ATTEMPTS
) {
123 xusleep(UL_RAND_READ_DELAY
); /* no etropy, wait and try again */
132 * We've been built against headers that support getrandom, but the
133 * running kernel does not. Fallback to reading from /dev/{u,}random
137 int fd
= random_get_fd();
142 ssize_t x
= read(fd
, cp
, n
);
144 if (lose_counter
++ > UL_RAND_READ_ATTEMPTS
)
146 xusleep(UL_RAND_READ_DELAY
);
158 * We do this all the time, but this is the only source of
159 * randomness if /dev/random/urandom is out to lunch.
162 for (cp
= buf
, i
= 0; i
< nbytes
; i
++)
163 *cp
++ ^= (rand() >> 7) & 0xFF;
167 unsigned short tmp_seed
[3];
169 memcpy(tmp_seed
, ul_jrand_seed
, sizeof(tmp_seed
));
170 ul_jrand_seed
[2] = ul_jrand_seed
[2] ^ syscall(__NR_gettid
);
171 for (cp
= buf
, i
= 0; i
< nbytes
; i
++)
172 *cp
++ ^= (jrand48(tmp_seed
) >> 7) & 0xFF;
173 memcpy(ul_jrand_seed
, tmp_seed
,
174 sizeof(ul_jrand_seed
)-sizeof(unsigned short));
182 * Tell source of randomness.
184 const char *random_tell_source(void)
186 #ifdef HAVE_GETRANDOM
187 return _("getrandom() function");
190 static const char *random_sources
[] = {
195 for (i
= 0; i
< ARRAY_SIZE(random_sources
); i
++) {
196 if (!access(random_sources
[i
], R_OK
))
197 return random_sources
[i
];
200 return _("libc pseudo-random functions");
203 #ifdef TEST_PROGRAM_RANDUTILS
204 #include <inttypes.h>
206 int main(int argc
, char *argv
[])
213 n
= argc
== 1 ? 16 : atoi(argv
[1]);
215 printf("Multiple random calls:\n");
216 for (i
= 0; i
< n
; i
++) {
217 random_get_bytes(&v
, sizeof(v
));
218 printf("#%02zu: %25"PRIu64
"\n", i
, v
);
222 printf("One random call:\n");
223 bufsz
= n
* sizeof(*vp
);
226 err(EXIT_FAILURE
, "failed to allocate buffer");
228 random_get_bytes(buf
, bufsz
);
229 for (i
= 0; i
< n
; i
++) {
230 vp
= (int64_t *) (buf
+ (i
* sizeof(*vp
)));
231 printf("#%02zu: %25"PRIu64
"\n", i
, *vp
);
236 #endif /* TEST_PROGRAM_RANDUTILS */