blob: 80893d3db941b4038e770f7b70bd368e8be4efba [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
18#include "randutils.h"
19
20#ifdef HAVE_TLS
21#define THREAD_LOCAL static __thread
22#else
23#define THREAD_LOCAL static
24#endif
25
26#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
27#define DO_JRAND_MIX
28THREAD_LOCAL unsigned short ul_jrand_seed[3];
29#endif
30
31int random_get_fd(void)
32{
33 int i, fd;
34 struct timeval tv;
35
36 gettimeofday(&tv, 0);
37 fd = open("/dev/urandom", O_RDONLY);
38 if (fd == -1)
39 fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
40 if (fd >= 0) {
41 i = fcntl(fd, F_GETFD);
42 if (i >= 0)
43 fcntl(fd, F_SETFD, i | FD_CLOEXEC);
44 }
45 srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
46
47#ifdef DO_JRAND_MIX
48 ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
49 ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
50 ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
51#endif
52 /* Crank the random number generator a few times */
53 gettimeofday(&tv, 0);
54 for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
55 rand();
56 return fd;
57}
58
59
60/*
61 * Generate a stream of random nbytes into buf.
62 * Use /dev/urandom if possible, and if not,
63 * use glibc pseudo-random functions.
64 */
65void random_get_bytes(void *buf, size_t nbytes)
66{
67 size_t i, n = nbytes;
68 int fd = random_get_fd();
69 int lose_counter = 0;
70 unsigned char *cp = (unsigned char *) buf;
71
72 if (fd >= 0) {
73 while (n > 0) {
74 ssize_t x = read(fd, cp, n);
75 if (x <= 0) {
76 if (lose_counter++ > 16)
77 break;
78 continue;
79 }
80 n -= x;
81 cp += x;
82 lose_counter = 0;
83 }
84
85 close(fd);
86 }
87
88 /*
89 * We do this all the time, but this is the only source of
90 * randomness if /dev/random/urandom is out to lunch.
91 */
92 for (cp = buf, i = 0; i < nbytes; i++)
93 *cp++ ^= (rand() >> 7) & 0xFF;
94
95#ifdef DO_JRAND_MIX
96 {
97 unsigned short tmp_seed[3];
98
99 memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
100 ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
101 for (cp = buf, i = 0; i < nbytes; i++)
102 *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
103 memcpy(ul_jrand_seed, tmp_seed,
104 sizeof(ul_jrand_seed)-sizeof(unsigned short));
105 }
106#endif
107
108 return;
109}
110
111#ifdef TEST_PROGRAM
112int main(int argc __attribute__ ((__unused__)),
113 char *argv[] __attribute__ ((__unused__)))
114{
115 unsigned int v, i;
116
117 /* generate and print 10 random numbers */
118 for (i = 0; i < 10; i++) {
119 random_get_bytes(&v, sizeof(v));
120 printf("%d\n", v);
121 }
122
123 return EXIT_SUCCESS;
124}
125#endif /* TEST_PROGRAM */