1/* entropy.c -- routines for providing pseudo-random data */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 1999-2011 The OpenLDAP Foundation.
6 * Portions Copyright 1999-2003 Kurt D. Zeilenga.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17/* This work was initially developed by Kurt D. Zeilenga for
18 * inclusion in OpenLDAP Software based, in part, on publically
19 * available works (as noted below).
20 */
21
22#include "portable.h"
23
24#include <ac/string.h>
25#include <ac/time.h>
26#include <ac/unistd.h>
27
28#ifdef HAVE_PROCESS_H
29#include <process.h>
30#endif
31
32#include <fcntl.h>
33
34#include <lutil.h>
35#include <lutil_md5.h>
36
37/*
38 * lutil_entropy() provides nbytes of entropy in buf.
39 * Quality offerred is suitable for one-time uses, such as "once" keys.
40 * Values may not be suitable for multi-time uses.
41 *
42 * Note:  Callers are encouraged to provide additional bytes of
43 * of entropy in the buf argument.  This information is used in
44 * fallback mode to improve the quality of bytes returned.
45 *
46 * This routinue should be extended to support additional sources
47 * of entropy.
48 */
49int lutil_entropy( unsigned char *buf, ber_len_t nbytes )
50{
51	if( nbytes == 0 ) return 0;
52
53#ifdef URANDOM_DEVICE
54#define URANDOM_NREADS 4
55	/* Linux and *BSD offer a urandom device */
56	{
57		int rc, fd, n=0;
58
59		fd = open( URANDOM_DEVICE, O_RDONLY );
60
61		if( fd < 0 ) return -1;
62
63		do {
64			rc = read( fd, buf, nbytes );
65			if( rc <= 0 ) break;
66
67			buf+=rc;
68			nbytes-=rc;
69
70			if( ++n >= URANDOM_NREADS ) break;
71		} while( nbytes > 0 );
72
73		close(fd);
74		return nbytes > 0 ? -1 : 0;
75	}
76#elif defined(PROV_RSA_FULL)
77	{
78		/* Not used since _WIN32_WINNT not set... */
79		HCRYPTPROV hProv = 0;
80
81		/* Get handle to user default provider */
82		if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
83		   return -1;
84		}
85
86		/* Generate random initialization vector */
87		if(!CryptGenRandom(hProv, (DWORD) nbytes, (BYTE *) buf)) {
88		   return -1;
89		}
90
91		/* Release provider handle */
92		if(hProv != 0) CryptReleaseContext(hProv, 0);
93
94		return 0;
95	}
96#else
97	{
98		/* based upon Phil Karn's "practical randomness" idea
99		 * but implementation 100% OpenLDAP.  So don't blame Phil.
100		 *
101		 * Worse case is that this is a MD5 hash of a counter, if
102		 * MD5 is a strong cryptographic hash, this should be fairly
103		 * resistant to attack
104		 */
105
106		/*
107		 * the caller may need to provide external synchronization OR
108		 * provide entropy (in buf) to ensure quality results as
109		 * access to this counter may not be atomic.
110		 */
111		static int counter = 0;
112		ber_len_t n;
113
114		struct rdata_s {
115			int counter;
116
117			unsigned char *buf;
118			struct rdata_s *stack;
119
120			pid_t	pid;
121
122#ifdef HAVE_GETTIMEOFDAY
123			struct timeval tv;
124#else
125			time_t	time;
126#endif
127
128			unsigned long	junk;	/* purposely not initialized */
129		} rdata;
130
131		/* make sure rdata differs for each process */
132		rdata.pid = getpid();
133
134		/* make sure rdata differs for each program */
135		rdata.buf = buf;
136		rdata.stack = &rdata;
137
138		for( n = 0; n < nbytes; n += 16 ) {
139			struct lutil_MD5Context ctx;
140			unsigned char digest[16];
141
142			/* poor resolution */
143#ifdef HAVE_GETTIMEOFDAY
144			(void) gettimeofday( &rdata.tv, NULL );
145#else
146			(void) time( &rdata.time );
147#endif
148
149			/* make sure rdata differs */
150			rdata.counter = ++counter;
151			rdata.pid++;
152			rdata.junk++;
153
154			lutil_MD5Init( &ctx );
155			lutil_MD5Update( &ctx, (unsigned char *) &rdata, sizeof( rdata ) );
156
157			/* allow caller to provided additional entropy */
158			lutil_MD5Update( &ctx, buf, nbytes );
159
160			lutil_MD5Final( digest, &ctx );
161
162			AC_MEMCPY( &buf[n], digest,
163				nbytes - n >= 16 ? 16 : nbytes - n );
164		}
165
166		return 0;
167	}
168#endif
169	return -1;
170}
171