lib: Streamline genrand.c includes
[Samba.git] / lib / util / genrand.c
blob4473433b5f78e363137a90ea2c4556ebb7b44623
1 /*
2 Unix SMB/CIFS implementation.
4 Functions to create reasonable random numbers for crypto use.
6 Copyright (C) Jeremy Allison 2001
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "replace.h"
23 #include "system/filesys.h"
24 #include "../lib/crypto/crypto.h"
25 #include "lib/util/genrand.h"
26 #include "lib/util/blocking.h"
27 #include "lib/util/time_basic.h"
28 #include "lib/util/byteorder.h"
30 /**
31 * @file
32 * @brief Random number generation
35 static unsigned char hash[258];
36 static uint32_t counter;
38 static bool done_reseed = false;
39 static unsigned int bytes_since_reseed = 0;
41 static int urand_fd = -1;
43 static void (*reseed_callback)(void *userdata, int *newseed);
44 static void *reseed_callback_userdata = NULL;
46 /**
47 Copy any user given reseed data.
48 **/
50 _PUBLIC_ void set_rand_reseed_callback(void (*fn)(void *, int *), void *userdata)
52 reseed_callback = fn;
53 reseed_callback_userdata = userdata;
54 set_need_random_reseed();
57 /**
58 * Tell the random number generator it needs to reseed.
60 _PUBLIC_ void set_need_random_reseed(void)
62 done_reseed = false;
63 bytes_since_reseed = 0;
66 static void get_rand_reseed_data(int *reseed_data)
68 if (reseed_callback) {
69 reseed_callback(reseed_callback_userdata, reseed_data);
70 } else {
71 *reseed_data = 0;
75 /****************************************************************
76 Setup the seed.
77 *****************************************************************/
79 static void seed_random_stream(unsigned char *seedval, size_t seedlen)
81 unsigned char j = 0;
82 size_t ind;
84 for (ind = 0; ind < 256; ind++)
85 hash[ind] = (unsigned char)ind;
87 for( ind = 0; ind < 256; ind++) {
88 unsigned char tc;
90 j += (hash[ind] + seedval[ind%seedlen]);
92 tc = hash[ind];
93 hash[ind] = hash[j];
94 hash[j] = tc;
97 hash[256] = 0;
98 hash[257] = 0;
101 /****************************************************************
102 Get datasize bytes worth of random data.
103 *****************************************************************/
105 static void get_random_stream(unsigned char *data, size_t datasize)
107 unsigned char index_i = hash[256];
108 unsigned char index_j = hash[257];
109 size_t ind;
111 for( ind = 0; ind < datasize; ind++) {
112 unsigned char tc;
113 unsigned char t;
115 index_i++;
116 index_j += hash[index_i];
118 tc = hash[index_i];
119 hash[index_i] = hash[index_j];
120 hash[index_j] = tc;
122 t = hash[index_i] + hash[index_j];
123 data[ind] = hash[t];
126 hash[256] = index_i;
127 hash[257] = index_j;
130 /****************************************************************
131 Get a 16 byte hash from the contents of a file.
133 Note that the hash is initialised, because the extra entropy is not
134 worth the valgrind pain.
135 *****************************************************************/
137 static void do_filehash(const char *fname, unsigned char *the_hash)
139 unsigned char buf[1011]; /* deliberate weird size */
140 unsigned char tmp_md4[16];
141 int fd, n;
143 ZERO_STRUCT(tmp_md4);
145 fd = open(fname,O_RDONLY,0);
146 if (fd == -1)
147 return;
149 while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) {
150 mdfour(tmp_md4, buf, n);
151 for (n=0;n<16;n++)
152 the_hash[n] ^= tmp_md4[n];
154 close(fd);
157 /**************************************************************
158 Try and get a good random number seed. Try a number of
159 different factors. Firstly, try /dev/urandom - use if exists.
161 We use /dev/urandom as a read of /dev/random can block if
162 the entropy pool dries up. This leads clients to timeout
163 or be very slow on connect.
165 If we can't use /dev/urandom then seed the stream random generator
166 above...
167 **************************************************************/
169 static int do_reseed(int fd)
171 unsigned char seed_inbuf[40];
172 uint32_t v1, v2; struct timeval tval; pid_t mypid;
173 int reseed_data = 0;
175 if (fd == -1) {
176 fd = open( "/dev/urandom", O_RDONLY,0);
177 if (fd != -1) {
178 smb_set_close_on_exec(fd);
181 if (fd != -1
182 && (read(fd, seed_inbuf, sizeof(seed_inbuf)) == sizeof(seed_inbuf))) {
183 seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
184 return fd;
187 /* Add in some secret file contents */
189 do_filehash("/etc/shadow", &seed_inbuf[0]);
192 * Add the counter, time of day, and pid.
195 GetTimeOfDay(&tval);
196 mypid = getpid();
197 v1 = (counter++) + mypid + tval.tv_sec;
198 v2 = (counter++) * mypid + tval.tv_usec;
200 SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32));
201 SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36));
204 * Add any user-given reseed data.
207 get_rand_reseed_data(&reseed_data);
208 if (reseed_data) {
209 size_t i;
210 for (i = 0; i < sizeof(seed_inbuf); i++)
211 seed_inbuf[i] ^= ((char *)(&reseed_data))[i % sizeof(reseed_data)];
214 seed_random_stream(seed_inbuf, sizeof(seed_inbuf));
216 return -1;
220 Interface to the (hopefully) good crypto random number generator.
221 Will use our internal PRNG if more than 40 bytes of random generation
222 has been requested, otherwise tries to read from /dev/random
224 _PUBLIC_ void generate_random_buffer(uint8_t *out, int len)
226 unsigned char md4_buf[64];
227 unsigned char tmp_buf[16];
228 unsigned char *p;
230 if(!done_reseed) {
231 bytes_since_reseed += len;
233 /* Magic constant to try and avoid reading 40 bytes
234 * and setting up the PRNG if the app only ever wants
235 * a few bytes */
236 if (bytes_since_reseed < 40) {
237 if (urand_fd == -1) {
238 urand_fd = open( "/dev/urandom", O_RDONLY,0);
239 if (urand_fd != -1) {
240 smb_set_close_on_exec(urand_fd);
243 if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
244 return;
248 urand_fd = do_reseed(urand_fd);
249 done_reseed = true;
253 * Generate random numbers in chunks of 64 bytes,
254 * then md4 them & copy to the output buffer.
255 * This way the raw state of the stream is never externally
256 * seen.
259 p = out;
260 while(len > 0) {
261 int copy_len = len > 16 ? 16 : len;
263 get_random_stream(md4_buf, sizeof(md4_buf));
264 mdfour(tmp_buf, md4_buf, sizeof(md4_buf));
265 memcpy(p, tmp_buf, copy_len);
266 p += copy_len;
267 len -= copy_len;
272 Interface to the (hopefully) good crypto random number generator.
273 Will always use /dev/urandom if available.
275 _PUBLIC_ void generate_secret_buffer(uint8_t *out, int len)
277 if (urand_fd == -1) {
278 urand_fd = open( "/dev/urandom", O_RDONLY,0);
279 if (urand_fd != -1) {
280 smb_set_close_on_exec(urand_fd);
283 if(urand_fd != -1 && (read(urand_fd, out, len) == len)) {
284 return;
287 generate_random_buffer(out, len);