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-2021 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 publicly
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 offered 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  */
lutil_entropy(unsigned char * buf,ber_len_t nbytes)49 int 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