]>
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.
16 #ifdef HAVE_SYS_SYSCALL_H
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 syscall */
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
);
93 * Generate a stream of random nbytes into buf.
94 * Use /dev/urandom if possible, and if not,
95 * use glibc pseudo-random functions.
97 #define UL_RAND_READ_ATTEMPTS 8
98 #define UL_RAND_READ_DELAY 125000 /* microseconds */
101 * Write @nbytes random bytes into @buf.
103 * Returns 0 for good quality of random bytes or 1 for weak quality.
105 int ul_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 entropy, 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));
186 * Tell source of randomness.
188 const char *random_tell_source(void)
190 #ifdef HAVE_GETRANDOM
191 return _("getrandom() function");
194 static const char *random_sources
[] = {
199 for (i
= 0; i
< ARRAY_SIZE(random_sources
); i
++) {
200 if (!access(random_sources
[i
], R_OK
))
201 return random_sources
[i
];
204 return _("libc pseudo-random functions");
207 #ifdef TEST_PROGRAM_RANDUTILS
208 #include <inttypes.h>
210 int main(int argc
, char *argv
[])
217 n
= argc
== 1 ? 16 : atoi(argv
[1]);
219 printf("Multiple random calls:\n");
220 for (i
= 0; i
< n
; i
++) {
221 ul_random_get_bytes(&v
, sizeof(v
));
222 printf("#%02zu: %25"PRIu64
"\n", i
, v
);
226 printf("One random call:\n");
227 bufsz
= n
* sizeof(*vp
);
230 err(EXIT_FAILURE
, "failed to allocate buffer");
232 ul_random_get_bytes(buf
, bufsz
);
233 for (i
= 0; i
< n
; i
++) {
234 vp
= (int64_t *) (buf
+ (i
* sizeof(*vp
)));
235 printf("#%02zu: %25"PRIu64
"\n", i
, *vp
);
240 #endif /* TEST_PROGRAM_RANDUTILS */