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/>.
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"
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
;
47 Copy any user given reseed data.
50 _PUBLIC_
void set_rand_reseed_callback(void (*fn
)(void *, int *), void *userdata
)
53 reseed_callback_userdata
= userdata
;
54 set_need_random_reseed();
58 * Tell the random number generator it needs to reseed.
60 _PUBLIC_
void set_need_random_reseed(void)
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
);
75 /****************************************************************
77 *****************************************************************/
79 static void seed_random_stream(unsigned char *seedval
, size_t seedlen
)
84 for (ind
= 0; ind
< 256; ind
++)
85 hash
[ind
] = (unsigned char)ind
;
87 for( ind
= 0; ind
< 256; ind
++) {
90 j
+= (hash
[ind
] + seedval
[ind
%seedlen
]);
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];
111 for( ind
= 0; ind
< datasize
; ind
++) {
116 index_j
+= hash
[index_i
];
119 hash
[index_i
] = hash
[index_j
];
122 t
= hash
[index_i
] + hash
[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];
143 ZERO_STRUCT(tmp_md4
);
145 fd
= open(fname
,O_RDONLY
,0);
149 while ((n
= read(fd
, (char *)buf
, sizeof(buf
))) > 0) {
150 mdfour(tmp_md4
, buf
, n
);
152 the_hash
[n
] ^= tmp_md4
[n
];
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
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
;
176 fd
= open( "/dev/urandom", O_RDONLY
,0);
178 smb_set_close_on_exec(fd
);
182 && (read(fd
, seed_inbuf
, sizeof(seed_inbuf
)) == sizeof(seed_inbuf
))) {
183 seed_random_stream(seed_inbuf
, sizeof(seed_inbuf
));
187 /* Add in some secret file contents */
189 do_filehash("/etc/shadow", &seed_inbuf
[0]);
192 * Add the counter, time of day, and pid.
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
);
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
));
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];
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
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
)) {
248 urand_fd
= do_reseed(urand_fd
);
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
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
);
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
)) {
287 generate_random_buffer(out
, len
);