summaryrefslogtreecommitdiffstats
path: root/contrib/bind/lib/dst/prandom.c
blob: 031da2e8894e317ae5e1000139a8d3c751bffc7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
#ifndef LINT
static const char rcsid[] = "$Header: /proj/cvs/isc/bind8/src/lib/dst/prandom.c,v 1.10 2001/02/12 23:13:46 marka Exp $";
#endif
/*
 * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
 *
 * Permission to use, copy modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL
 * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THE SOFTWARE.
 */

#include "port_before.h"

#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <dirent.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>

#include "dst_internal.h"
#include "prand_conf.h"

#include "port_after.h"

#ifndef DST_NUM_HASHES
#define DST_NUM_HASHES 4
#endif
#ifndef DST_NUMBER_OF_COUNTERS
#define DST_NUMBER_OF_COUNTERS 5	/* 32 * 5 == 160 == SHA(1) > MD5 */
#endif

/* 
 * the constant below is a prime number to make fixed data structues like 
 * stat and time wrap over blocks. This adds certain uncertanty to what is 
 * in each digested block. 
 * The prime number 2879 has the special property that when 
 * divided by 2,4 and 6 the result is also a prime numbers
 */

#ifndef DST_RANDOM_BLOCK_SIZE
#define DST_RANDOM_BLOCK_SIZE 2879
#endif

/* 
 * This constant dictatates how many bits we shift to the right before using a 
 */
#ifndef DST_SHIFT
#define DST_SHIFT 9
#endif

/*
 * An initalizer that is as bad as any other with half the bits set 
 */
#ifndef DST_RANDOM_PATTERN
#define DST_RANDOM_PATTERN 0x8765CA93
#endif
/* 
 * things must have changed in the last 3600 seconds to be used 
 */
#define MAX_OLD 3600


/*  
 *  these two data structure are used to process input data into digests, 
 *
 *  The first structure is containts a pointer to a DST HMAC key 
 *  the variables accompanying are used for 
 *	step : select every step byte from input data for the hash
 *	block: number of data elements going into each hash
 *	digested: number of data elements digested so far
 *	curr: offset into the next input data for the first byte. 
 */
typedef struct hash {
	DST_KEY *key;
	void *ctx;
	int digested, block, step, curr;
} prand_hash;

/*
 *  This data structure controlls number of hashes and keeps track of 
 *  overall progress in generating correct number of bytes of output.
 *	output  : array to store the output data in
 *	needed  : how many bytes of output are needed
 *	filled  : number of bytes in output so far. 
 *	bytes   : total number of bytes processed by this structure
 *	file_digest : the HMAC key used to digest files.
 */
typedef struct work {
	int needed, filled, bytes;
	u_char *output;
	prand_hash *hash[DST_NUM_HASHES];
	DST_KEY *file_digest;
} dst_work;


/* 
 * forward function declarations 
 */
static int get_dev_random(u_char *output, int size);
static int do_time(dst_work *work);
static int do_ls(dst_work *work);
static int unix_cmd(dst_work *work);
static int digest_file(dst_work *work);

static void force_hash(dst_work *work, prand_hash *hash);
static int do_hash(dst_work *work, prand_hash *hash, u_char *input,
		   int size);
static int my_digest(dst_work *tmp, u_char *input, int size);
static prand_hash *get_hmac_key(int step, int block);

static int own_random(dst_work *work);


/* 
 * variables used in the quick random number generator 
 */
static u_int32_t ran_val = DST_RANDOM_PATTERN;
static u_int32_t ran_cnt = (DST_RANDOM_PATTERN >> 10);

/* 
 * setting the quick_random generator to particular values or if both 
 * input parameters are 0 then set it to initial vlaues
 */

void
dst_s_quick_random_set(u_int32_t val, u_int32_t cnt)
{
	ran_val = (val == 0) ? DST_RANDOM_PATTERN : val;
	ran_cnt = (cnt == 0) ? (DST_RANDOM_PATTERN >> 10) : cnt;
}

/* 
 * this is a quick and random number generator that seems to generate quite 
 * good distribution of data 
 */
u_int32_t
dst_s_quick_random(int inc)
{
	ran_val = ((ran_val >> 13) ^ (ran_val << 19)) ^
		((ran_val >> 7) ^ (ran_val << 25));
	if (inc > 0)		/* only increasing values accepted */
		ran_cnt += inc;
	ran_val += ran_cnt++;
	return (ran_val);
}

/* 
 * get_dev_random: Function to read /dev/random reliably
 * this function returns how many bytes where read from the device.
 * port_after.h should set the control variable HAVE_DEV_RANDOM 
 */
static int
get_dev_random(u_char *output, int size)
{
#ifdef HAVE_DEV_RANDOM
	struct stat st;
	int n = 0, fd = -1, s;

	s = stat("/dev/random", &st);
	if (s == 0 && S_ISCHR(st.st_mode)) {
		if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) != -1) {
			if ((n = read(fd, output, size)) < 0)
				n = 0;
			close(fd);
		}
		return (n);
	}
#endif
	return (0);
}

/*
 * Portable way of getting the time values if gettimeofday is missing 
 * then compile with -DMISSING_GETTIMEOFDAY  time() is POSIX compliant but
 * gettimeofday() is not.
 * Time of day is predictable, we are looking for the randomness that comes 
 * the last few bits in the microseconds in the timer are hard to predict when 
 * this is invoked at the end of other operations
 */
struct timeval *mtime;
static int
do_time(dst_work *work)
{
	int cnt = 0;
	static u_char tmp[sizeof(struct timeval) + sizeof(struct timezone)];
	struct timezone *zone;

	zone = (struct timezone *) tmp;
	mtime = (struct timeval *)(tmp + sizeof(struct timezone));
	gettimeofday(mtime, zone);
	cnt = sizeof(tmp);
	my_digest(work, tmp, sizeof(tmp));

	return (cnt);
}

/*
 * this function simulates the ls command, but it uses stat which gives more
 * information and is harder to guess 
 * Each call to this function will visit the next directory on the list of 
 * directories, in a circular manner. 
 * return value is the number of bytes added to the temp buffer
 *
 * do_ls() does not visit subdirectories
 * if attacker has access to machine it can guess most of the values seen
 * thus it is important to only visit directories that are freqently updated
 * Attacker that has access to the network can see network traffic 
 * when NFS mounted directories are accessed and know exactly the data used
 * but may not know exactly in what order data is used. 
 * Returns the number of bytes that where returned in stat structures
 */
static int
do_ls(dst_work *work)
{
	struct dir_info { 
		uid_t  uid;
		gid_t  gid;
		off_t size;
		time_t atime, mtime, ctime;
	};
	static struct dir_info dir_info;
	struct stat buf;
	struct dirent *entry;
	static int i = 0;
	static unsigned long d_round = 0;
	struct timeval tv;
	int n = 0, dir_len, tb_i = 0, out = 0;

	char file_name[1024];
	u_char tmp_buff[1024]; 
	DIR *dir = NULL;

	if (dirs[i] == NULL) 	/* if at the end of the list start over */
		i = 0;
	if (stat(dirs[i++], &buf))  /* directory does not exist */
		return (0);

	gettimeofday(&tv, NULL);
	if (d_round == 0) 
		d_round = tv.tv_sec - MAX_OLD;
	else if (i==1) /* if starting a new round cut what we accept */
		d_round += (tv.tv_sec - d_round)/2;

	if (buf.st_atime < d_round) 
		return (0);

	EREPORT(("do_ls i %d filled %4d in_temp %4d\n",
		 i-1, work->filled, work->in_temp));
	memcpy(tmp_buff, &buf, sizeof(buf)); 
	tb_i += sizeof(buf);


	if ((dir = opendir(dirs[i-1])) == NULL)/* open it for read */
		return (0);
	strcpy(file_name, dirs[i-1]);
	dir_len = strlen(file_name);
	file_name[dir_len++] = '/';
	while ((entry = readdir(dir))) {
		int len = strlen(entry->d_name);
		out += len;
		if (my_digest(work, (u_char *)entry->d_name, len))
			break;
	
		memcpy(&file_name[dir_len], entry->d_name, len);
		file_name[dir_len + len] = 0x0;
		/* for all entries in dir get the stats */
		if (stat(file_name, &buf) == 0) {
			n++;	/* count successfull stat calls */
			/* copy non static fields */
			dir_info.uid   += buf.st_uid;
			dir_info.gid   += buf.st_gid;
			dir_info.size  += buf.st_size;
			dir_info.atime += buf.st_atime;
			dir_info.mtime += buf.st_mtime;
			dir_info.ctime += buf.st_ctime;
			out += sizeof(dir_info);
			if(my_digest(work, (u_char *)&dir_info, 
				     sizeof(dir_info)))
				break; 
		}
	}
	closedir(dir);	/* done */
	out += do_time(work);	/* add a time stamp */
	return (out);
}


/* 
 * unix_cmd() 
 * this function executes the a command from the cmds[] list of unix commands 
 * configured in the prand_conf.h file
 * return value is the number of bytes added to the randomness temp buffer
 * 
 * it returns the number of bytes that where read in
 * if more data is needed at the end time is added to the data.
 * This function maintains a state to selects the next command to run
 * returns the number of bytes read in from the command 
 */
static int
unix_cmd(dst_work *work)
{
	static int cmd_index = 0;
	int cnt = 0, n;
	FILE *pipe;
	u_char buffer[4096];

	if (cmds[cmd_index] == NULL)
		cmd_index = 0;
	EREPORT(("unix_cmd() i %d filled %4d in_temp %4d\n",
		 cmd_index, work->filled, work->in_temp));
	pipe = popen(cmds[cmd_index++], "r");	/* execute the command */

	while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0) {
		cnt += n;	/* process the output */
		if (my_digest(work, buffer, n))
			break;
		/* this adds some randomness to the output */
		cnt += do_time(work);
	}
	while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0)
		NULL; /* drain the pipe */
	pclose(pipe);
	return (cnt);		/* read how many bytes where read in */
}

/* 
 * digest_file() This function will read a file and run hash over it
 * input is a file name 
 */ 
static int 
digest_file(dst_work *work) 
{
	static int f_cnt = 0;
	static unsigned long f_round = 0;
	FILE *fp; 
	void *ctx;
	const char *name;
	int no, i; 
	struct stat st;
	struct timeval tv;
	u_char buf[1024];

	if (f_round == 0 || files[f_cnt] == NULL || work->file_digest == NULL) 
		if (gettimeofday(&tv, NULL)) /* only do this if needed */
			return (0);
	if (f_round == 0)   /* first time called set to one hour ago */
		f_round = (tv.tv_sec - MAX_OLD); 
	name = files[f_cnt++]; 
	if (files[f_cnt] == NULL) {  /* end of list of files */
		if(f_cnt <= 1)       /* list is too short */
			return (0);
		f_cnt = 0;           /* start again on list */
		f_round += (tv.tv_sec - f_round)/2; /* set new cutoff */
		work->file_digest = dst_free_key(work->file_digest);
	}
	if (work->file_digest == NULL) {
		work->file_digest  = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0, 
					    (u_char *)&tv, sizeof(tv));
		if (work->file_digest == NULL)
			return (0);
	}
	if (access(name, R_OK) || stat(name, &st))
		return (0); /* no such file or not allowed to read it */
	if (strncmp(name, "/proc/", 6) && st.st_mtime < f_round)  
		return(0); /* file has not changed recently enough */
	if (dst_sign_data(SIG_MODE_INIT, work->file_digest, &ctx, 
			  NULL, 0, NULL, 0)) {
		work->file_digest = dst_free_key(work->file_digest);
		return (0);
	}
	if ((fp = fopen(name, "r")) == NULL) 
		return (0);
	for (no = 0; (i = fread(buf, sizeof(*buf), sizeof(buf), fp)) > 0; 
	     no += i) 
		dst_sign_data(SIG_MODE_UPDATE, work->file_digest, &ctx, 
			      buf, i, NULL, 0);

	fclose(fp);
	if (no >= 64) {
		i = dst_sign_data(SIG_MODE_FINAL, work->file_digest, &ctx, 
				  NULL, 0, &work->output[work->filled], 
				  DST_HASH_SIZE);	  
		if (i > 0) 
			work->filled += i;
	}
	else if (i > 0)
		my_digest(work, buf, i);
	my_digest(work, (u_char *)name, strlen(name));
	return (no + strlen(name));
}

/* 
 * function to perform the FINAL and INIT operation on a hash if allowed
 */
static void
force_hash(dst_work *work, prand_hash *hash)
{
	int i = 0;

	/* 
	 * if more than half a block then add data to output 
	 * otherwise adde the digest to the next hash 
	 */
	if ((hash->digested * 2) > hash->block) {
		i = dst_sign_data(SIG_MODE_FINAL, hash->key, &hash->ctx,
				  NULL, 0, &work->output[work->filled],
				  DST_HASH_SIZE);

		hash->digested = 0;
		dst_sign_data(SIG_MODE_INIT, hash->key, &hash->ctx, 
			      NULL, 0, NULL, 0);
		if (i > 0)
			work->filled += i;
	}
	return;
}

/* 
 * This function takes the input data does the selection of data specified
 * by the hash control block.
 * The step varialbe in the work sturcture determines which 1/step bytes
 * are used, 
 *
 */
static int
do_hash(dst_work *work, prand_hash *hash, u_char *input, int size)
{
	u_char *tmp = input, *tp;
	int i, cnt = size, n, needed, avail, dig, tmp_size = 0;

	if (cnt <= 0 || input == NULL)
		return (0);

	if (hash->step > 1) {	/* if using subset of input data */
		tmp_size = size / hash->step + 2;
		tp = tmp = malloc(tmp_size);
		for (cnt = 0, i = hash->curr; i < size; i += hash->step, cnt++)
			*(tp++) = input[i];
		/* calcutate the starting point in the next input set */
		hash->curr = (hash->step - (i - size)) % hash->step;
	}
	/* digest the data in block sizes */
	for (n = 0; n < cnt; n += needed) {
		avail = (cnt - n);
		needed = hash->block - hash->digested;
		dig = (avail < needed) ? avail : needed;
		dst_sign_data(SIG_MODE_UPDATE, hash->key, &hash->ctx, 
			      &tmp[n], dig, NULL, 0);
		hash->digested += dig;
		if (hash->digested >= hash->block)
			force_hash(work, hash);
		if (work->needed < work->filled) {
			if (tmp != input) 
				SAFE_FREE2(tmp, tmp_size);
			return (1);
		}
	}
	if (tmp_size > 0)
		SAFE_FREE2(tmp, tmp_size);
	return (0);
}

/*
 * Copy data from INPUT for length SIZE into the work-block TMP.
 * If we fill the work-block, digest it; then,
 * if work-block needs more data, keep filling with the rest of the input.
 */
static int
my_digest(dst_work *work, u_char *input, int size)
{

	int i, full = 0;
	static unsigned counter;
	
	counter += size;
	/* first do each one of the hashes */
	for (i = 0; i < DST_NUM_HASHES && full == 0; i++) 
		full = do_hash(work, work->hash[i], input, size) +
		       do_hash(work, work->hash[i], (u_char *) &counter, 
				sizeof(counter));
/* 
 * if enough data has be generated do final operation on all hashes 
 *  that have enough date for that 
 */
	for (i = 0; full && (i < DST_NUM_HASHES); i++)
		force_hash(work, work->hash[i]);

	return (full);
}

/*
 * this function gets some semi random data and sets that as an HMAC key
 * If we get a valid key this function returns that key initalized
 * otherwise it returns NULL;
 */
static prand_hash *
get_hmac_key(int step, int block)
{

	u_char *buff;
	int temp = 0, n = 0, size = 70;
	DST_KEY *new_key = NULL;
	prand_hash *new = NULL;

	/* use key that is larger than  digest algorithms (64) for key size */
	buff = malloc(size);
	if (buff == NULL)
		return (NULL);
	/* do not memset the allocated memory to get random bytes there */
	/* time of day is somewhat random  expecialy in the last bytes */
	gettimeofday((struct timeval *) &buff[n], NULL);
	n += sizeof(struct timeval);

/* get some semi random stuff in here stir it with micro seconds */
	if (n < size) {
		temp = dst_s_quick_random((int) buff[n - 1]);
		memcpy(&buff[n], &temp, sizeof(temp));
		n += sizeof(temp);
	}
/* get the pid of this process and its parent */
	if (n < size) {
		temp = (int) getpid();
		memcpy(&buff[n], &temp, sizeof(temp));
		n += sizeof(temp);
	}
	if (n < size) {
		temp = (int) getppid();
		memcpy(&buff[n], &temp, sizeof(temp));
		n += sizeof(temp);
	}
/* get the user ID */
	if (n < size) {
		temp = (int) getuid();
		memcpy(&buff[n], &temp, sizeof(temp));
		n += sizeof(temp);
	}
#ifndef GET_HOST_ID_MISSING
	if (n < size) {
		temp = (int) gethostid();
		memcpy(&buff[n], &temp, sizeof(temp));
		n += sizeof(temp);
	}
#endif
/* get some more random data */
	if (n < size) {
		temp = dst_s_quick_random((int) buff[n - 1]);
		memcpy(&buff[n], &temp, sizeof(temp));
		n += sizeof(temp);
	}
/* covert this into a HMAC key */
	new_key = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0, buff, size);
	SAFE_FREE(buff);

/* get the control structure */
	if ((new = malloc(sizeof(prand_hash))) == NULL)
		return (NULL);
	new->digested = new->curr = 0;
	new->step = step;
	new->block = block;
	new->key = new_key;
	if (dst_sign_data(SIG_MODE_INIT, new_key, &new->ctx, NULL, 0, NULL, 0))
		return (NULL);

	return (new);
}

/* 
 * own_random() 
 * This function goes out and from various sources tries to generate enough
 * semi random data that a hash function can generate a random data. 
 * This function will iterate between the two main random source sources, 
 *  information from programs and directores in random order. 
 * This function return the number of bytes added to the random output buffer. 
 */
static int
own_random(dst_work *work)
{
	int dir = 0, b;
	int bytes, n, cmd = 0, dig = 0;
	int start =0;
/* 
 * now get the initial seed to put into the quick random function from 
 * the address of the work structure 
 */
	bytes = (int) getpid();
/*
 * proceed while needed 
 */
	while (work->filled < work->needed) {
		EREPORT(("own_random r %08x b %6d t %6d f %6d\n",
			 ran_val, bytes, work->in_temp, work->filled));
/* pick a random number in the range of 0..7 based on that random number
 * perform some operations that yield random data
 */
		start = work->filled;
		n = (dst_s_quick_random(bytes) >> DST_SHIFT) & 0x07;
		switch (n) {
		    case 0:
		    case 3:
			if (sizeof(cmds) > 2 *sizeof(*cmds)) {
				b = unix_cmd(work);
				cmd += b;
			}
			break;

		    case 1:
		    case 7:
			if (sizeof(dirs) > 2 *sizeof(*dirs)) {
				b = do_ls(work);
				dir += b;
			}
			break;

		    case 4:
		    case 5:
			/* retry getting data from /dev/random */
			b = get_dev_random(&work->output[work->filled], 
					   work->needed - work->filled);
			if (b > 0)
				work->filled += b;
			break;

		    case 6:
			if (sizeof(files) > 2 * sizeof(*files)) {
				b = digest_file(work);
				dig += b;
			}
			break;

		    case 2:
		    default:	/* to make sure we make some progress */
			work->output[work->filled++] = 0xff &
				dst_s_quick_random(bytes);
			b = 1;
			break;
		}
		if (b > 0) 
			bytes += b;
	}
	return (work->filled);
}


/* 
 * dst_s_random() This function will return the requested number of bytes 
 * of randomness to the caller it will use the best available sources of 
 * randomness.
 * The current order is to use /dev/random, precalculated randomness, and 
 * finaly use some system calls and programs to generate semi random data that 
 * is then digested to generate randomness. 
 * This function is thread safe as each thread uses its own context, but
 * concurrent treads will affect each other as they update shared state 
 * information.
 * It is strongly recommended that this function be called requesting a size 
 * that is not a multiple of the output of the hash function used. 
 * 
 * If /dev/random is not available this function is not suitable to generate 
 * large ammounts of data, rather it is suitable to seed a pseudo-random 
 * generator 
 * Returns the number of bytes put in the output buffer 
 */
int
dst_s_random(u_char *output, int size)
{
	int n = 0, s, i;
	static u_char old_unused[DST_HASH_SIZE * DST_NUM_HASHES];
	static int unused = 0;

	if (size <= 0 || output == NULL)
		return (0);

	if (size >= 2048)
		return (-1);
	/* 
	 * Read from /dev/random 
	 */
	n = get_dev_random(output, size);
	/* 
	 *  If old data is available and needed use it 
	 */
	if (n < size && unused > 0) {
		int need = size - n;
		if (unused <= need) {
			memcpy(output, old_unused, unused);
			n += unused;
			unused = 0;
		} else {
			memcpy(output, old_unused, need);
			n += need;
			unused -= need;
			memcpy(old_unused, &old_unused[need], unused);
		}
	}
	/*
	 * If we need more use the simulated randomness here.
	 */
	if (n < size) {
		dst_work *my_work = (dst_work *) malloc(sizeof(dst_work));
		if (my_work == NULL)
			return (n);
		my_work->needed = size - n;
		my_work->filled = 0;
		my_work->output = (u_char *) malloc(my_work->needed +
						    DST_HASH_SIZE *
						    DST_NUM_HASHES);
		my_work->file_digest = NULL;
		if (my_work->output == NULL)
			return (n);
		memset(my_work->output, 0x0, my_work->needed);
/* allocate upto 4 different HMAC hash functions out of order */
#if DST_NUM_HASHES >= 3
		my_work->hash[2] = get_hmac_key(3, DST_RANDOM_BLOCK_SIZE / 2);
#endif
#if DST_NUM_HASHES >= 2
		my_work->hash[1] = get_hmac_key(7, DST_RANDOM_BLOCK_SIZE / 6);
#endif
#if DST_NUM_HASHES >= 4
		my_work->hash[3] = get_hmac_key(5, DST_RANDOM_BLOCK_SIZE / 4);
#endif
		my_work->hash[0] = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE);
		if (my_work->hash[0] == NULL)	/* if failure bail out */
			return (n);
		s = own_random(my_work);
/* if more generated than needed store it for future use */
		if (s >= my_work->needed) {
			EREPORT(("dst_s_random(): More than needed %d >= %d\n",
				 s, my_work->needed));
			memcpy(&output[n], my_work->output, my_work->needed);
			n += my_work->needed;
			/* saving unused data for next time */
			unused = s - my_work->needed;
			memcpy(old_unused, &my_work->output[my_work->needed],
			       unused);
		} else {
			/* XXXX This should not happen */
			EREPORT(("Not enough %d >= %d\n", s, my_work->needed));
			memcpy(&output[n], my_work->output, s);
			n += my_work->needed;
		}

/* delete the allocated work area */
		for (i = 0; i < DST_NUM_HASHES; i++) {
			dst_free_key(my_work->hash[i]->key);
			SAFE_FREE(my_work->hash[i]);
		}
		SAFE_FREE(my_work->output);
		SAFE_FREE(my_work);
	}
	return (n);
}

/*
 * A random number generator that is fast and strong 
 * this random number generator is based on HASHing data,
 * the input to the digest function is a collection of <NUMBER_OF_COUNTERS>
 * counters that is incremented between digest operations
 * each increment operation amortizes to 2 bits changed in that value
 * for 5 counters thus the input will amortize to have 10 bits changed 
 * The counters are initaly set using the strong random function above
 * the HMAC key is selected by the same methold as the HMAC keys for the 
 * strong random function. 
 * Each set of counters is used for 2^25 operations 
 * 
 * returns the number of bytes written to the output buffer 
 * or       negative number in case of error 
 */
int
dst_s_semi_random(u_char *output, int size)
{
	static u_int32_t counter[DST_NUMBER_OF_COUNTERS];
	static u_char semi_old[DST_HASH_SIZE];
	static int semi_loc = 0, cnt = 0, hb_size = 0;
	static DST_KEY *my_key = NULL;
	prand_hash *hash;
	int out = 0, i, n;

	if (output == NULL || size <= 0)
		return (-2);

/* check if we need a new key */
	if (my_key == NULL || cnt > (1 << 25)) {	/* get HMAC KEY */
		if (my_key)
			my_key->dk_func->destroy(my_key);
		if ((hash = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE)) == NULL)
			return (0);
		my_key = hash->key;
/* check if the key works stir the new key using some old random data */
		hb_size = dst_sign_data(SIG_MODE_ALL, my_key, NULL, 
				        (u_char *) counter, sizeof(counter),
					semi_old, sizeof(semi_old));
		if (hb_size <= 0) {
			EREPORT(("dst_s_semi_random() Sign of alg %d failed %d\n",
				 my_key->dk_alg, hb_size));
			return (-1);
		}
/* new set the counters to random values */
		dst_s_random((u_char *) counter, sizeof(counter));
		cnt = 0;
	}
/* if old data around use it first */
	if (semi_loc < hb_size) {
		if (size <= hb_size - semi_loc) {	/* need less */
			memcpy(output, &semi_old[semi_loc], size);
			semi_loc += size;
			return (size);	/* DONE */
		} else {
			out = hb_size - semi_loc;
			memcpy(output, &semi_old[semi_loc], out);
			semi_loc += out;
		}
	}
/* generate more randome stuff */
	while (out < size) {
		/* 
		 * modify at least one bit by incrementing at least one counter
		 * based on the last bit of the last counter updated update
		 * the next one.
		 * minimaly this  operation will modify at least 1 bit, 
		 * amortized 2 bits
		 */
		for (n = 0; n < DST_NUMBER_OF_COUNTERS; n++)
			i = (int) counter[n]++;

		i = dst_sign_data(SIG_MODE_ALL, my_key, NULL, 
				  (u_char *) counter, hb_size,
				  semi_old, sizeof(semi_old));
		if (i != hb_size)
			EREPORT(("HMAC SIGNATURE FAILURE %d\n", i));
		cnt++;
		if (size - out < i)	/* Not all data is needed */
			semi_loc = i = size - out;
		memcpy(&output[out], semi_old, i);
		out += i;
	}
	return (out);
}
OpenPOWER on IntegriCloud