]>
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)
63 unsigned int n_pid
, n_uid
;
65 gettimeofday(&tv
, NULL
);
68 srand((n_pid
<< 16) ^ n_uid
^ tv
.tv_sec
^ tv
.tv_usec
);
71 ul_jrand_seed
[0] = getpid() ^ (tv
.tv_sec
& 0xFFFF);
72 ul_jrand_seed
[1] = getppid() ^ (tv
.tv_usec
& 0xFFFF);
73 ul_jrand_seed
[2] = (tv
.tv_sec
^ tv
.tv_usec
) >> 16;
75 /* Crank the random number generator a few times */
76 gettimeofday(&tv
, NULL
);
77 for (i
= (tv
.tv_sec
^ tv
.tv_usec
) & 0x1F; i
> 0; i
--)
81 int random_get_fd(void)
85 fd
= open("/dev/urandom", O_RDONLY
| O_CLOEXEC
);
87 fd
= open("/dev/random", O_RDONLY
| O_NONBLOCK
| O_CLOEXEC
);
89 i
= fcntl(fd
, F_GETFD
);
91 fcntl(fd
, F_SETFD
, i
| FD_CLOEXEC
);
98 * Generate a stream of random nbytes into buf.
99 * Use /dev/urandom if possible, and if not,
100 * use glibc pseudo-random functions.
102 #define UL_RAND_READ_ATTEMPTS 8
103 #define UL_RAND_READ_DELAY 125000 /* microseconds */
105 void random_get_bytes(void *buf
, size_t nbytes
)
107 unsigned char *cp
= (unsigned char *)buf
;
108 size_t i
, n
= nbytes
;
109 int lose_counter
= 0;
111 #ifdef HAVE_GETRANDOM
116 x
= getrandom(cp
, n
, GRND_NONBLOCK
);
117 if (x
> 0) { /* success */
122 } else if (errno
== ENOSYS
) { /* kernel without getrandom() */
125 } else if (errno
== EAGAIN
&& lose_counter
< UL_RAND_READ_ATTEMPTS
) {
126 xusleep(UL_RAND_READ_DELAY
); /* no etropy, wait and try again */
135 * We've been built against headers that support getrandom, but the
136 * running kernel does not. Fallback to reading from /dev/{u,}random
140 int fd
= random_get_fd();
145 ssize_t x
= read(fd
, cp
, n
);
147 if (lose_counter
++ > UL_RAND_READ_ATTEMPTS
)
149 xusleep(UL_RAND_READ_DELAY
);
161 * We do this all the time, but this is the only source of
162 * randomness if /dev/random/urandom is out to lunch.
165 for (cp
= buf
, i
= 0; i
< nbytes
; i
++)
166 *cp
++ ^= (rand() >> 7) & 0xFF;
170 unsigned short tmp_seed
[3];
172 memcpy(tmp_seed
, ul_jrand_seed
, sizeof(tmp_seed
));
173 ul_jrand_seed
[2] = ul_jrand_seed
[2] ^ syscall(__NR_gettid
);
174 for (cp
= buf
, i
= 0; i
< nbytes
; i
++)
175 *cp
++ ^= (jrand48(tmp_seed
) >> 7) & 0xFF;
176 memcpy(ul_jrand_seed
, tmp_seed
,
177 sizeof(ul_jrand_seed
)-sizeof(unsigned short));
184 * Tell source of randomness.
186 const char *random_tell_source(void)
188 #ifdef HAVE_GETRANDOM
189 return _("getrandom() function");
192 static const char *random_sources
[] = {
197 for (i
= 0; i
< ARRAY_SIZE(random_sources
); i
++) {
198 if (!access(random_sources
[i
], R_OK
))
199 return random_sources
[i
];
202 return _("libc pseudo-random functions");
205 #ifdef TEST_PROGRAM_RANDUTILS
206 #include <inttypes.h>
208 int main(int argc
, char *argv
[])
215 n
= argc
== 1 ? 16 : atoi(argv
[1]);
217 printf("Multiple random calls:\n");
218 for (i
= 0; i
< n
; i
++) {
219 random_get_bytes(&v
, sizeof(v
));
220 printf("#%02zu: %25"PRIu64
"\n", i
, v
);
224 printf("One random call:\n");
225 bufsz
= n
* sizeof(*vp
);
228 err(EXIT_FAILURE
, "failed to allocate buffer");
230 random_get_bytes(buf
, bufsz
);
231 for (i
= 0; i
< n
; i
++) {
232 vp
= (int64_t *) (buf
+ (i
* sizeof(*vp
)));
233 printf("#%02zu: %25"PRIu64
"\n", i
, *vp
);
238 #endif /* TEST_PROGRAM_RANDUTILS */