xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblutil/entropy.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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  */
51 int 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