blob: 684ac0ac160af22e0442368f21c6881884a9ae0d [file] [log] [blame]
bigbiff bigbiffe60683a2013-02-22 20:55:50 -05001/*
2 * General purpose random utilities
3 *
4 * Based on libuuid code.
5 *
6 * This file may be redistributed under the terms of the
7 * GNU Lesser General Public License.
8 */
9#include <stdio.h>
10#include <unistd.h>
11#include <fcntl.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/time.h>
15
16#include <sys/syscall.h>
17
bigbiff7b4c7a62015-01-01 19:44:14 -050018#include "c.h"
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050019#include "randutils.h"
bigbiff7b4c7a62015-01-01 19:44:14 -050020#include "nls.h"
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050021
22#ifdef HAVE_TLS
23#define THREAD_LOCAL static __thread
24#else
25#define THREAD_LOCAL static
26#endif
27
28#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
29#define DO_JRAND_MIX
30THREAD_LOCAL unsigned short ul_jrand_seed[3];
31#endif
32
33int random_get_fd(void)
34{
35 int i, fd;
36 struct timeval tv;
37
38 gettimeofday(&tv, 0);
bigbiff7b4c7a62015-01-01 19:44:14 -050039 fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050040 if (fd == -1)
bigbiff7b4c7a62015-01-01 19:44:14 -050041 fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050042 if (fd >= 0) {
43 i = fcntl(fd, F_GETFD);
44 if (i >= 0)
45 fcntl(fd, F_SETFD, i | FD_CLOEXEC);
46 }
47 srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
48
49#ifdef DO_JRAND_MIX
50 ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
51 ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
52 ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
53#endif
54 /* Crank the random number generator a few times */
55 gettimeofday(&tv, 0);
56 for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
57 rand();
58 return fd;
59}
60
61
62/*
63 * Generate a stream of random nbytes into buf.
64 * Use /dev/urandom if possible, and if not,
65 * use glibc pseudo-random functions.
66 */
67void random_get_bytes(void *buf, size_t nbytes)
68{
69 size_t i, n = nbytes;
70 int fd = random_get_fd();
71 int lose_counter = 0;
72 unsigned char *cp = (unsigned char *) buf;
73
74 if (fd >= 0) {
75 while (n > 0) {
76 ssize_t x = read(fd, cp, n);
77 if (x <= 0) {
78 if (lose_counter++ > 16)
79 break;
80 continue;
81 }
82 n -= x;
83 cp += x;
84 lose_counter = 0;
85 }
86
87 close(fd);
88 }
89
90 /*
91 * We do this all the time, but this is the only source of
92 * randomness if /dev/random/urandom is out to lunch.
93 */
94 for (cp = buf, i = 0; i < nbytes; i++)
95 *cp++ ^= (rand() >> 7) & 0xFF;
96
97#ifdef DO_JRAND_MIX
98 {
99 unsigned short tmp_seed[3];
100
101 memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
102 ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
103 for (cp = buf, i = 0; i < nbytes; i++)
104 *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
105 memcpy(ul_jrand_seed, tmp_seed,
106 sizeof(ul_jrand_seed)-sizeof(unsigned short));
107 }
108#endif
109
110 return;
111}
112
bigbiff7b4c7a62015-01-01 19:44:14 -0500113
114/*
115 * Tell source of randomness.
116 */
117const char *random_tell_source(void)
118{
119 size_t i;
120 static const char *random_sources[] = {
121 "/dev/urandom",
122 "/dev/random"
123 };
124
125 for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
126 if (!access(random_sources[i], R_OK))
127 return random_sources[i];
128 }
129
130 return _("libc pseudo-random functions");
131}
132
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500133#ifdef TEST_PROGRAM
134int main(int argc __attribute__ ((__unused__)),
135 char *argv[] __attribute__ ((__unused__)))
136{
137 unsigned int v, i;
138
139 /* generate and print 10 random numbers */
140 for (i = 0; i < 10; i++) {
141 random_get_bytes(&v, sizeof(v));
142 printf("%d\n", v);
143 }
144
145 return EXIT_SUCCESS;
146}
147#endif /* TEST_PROGRAM */