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