xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblutil/entropy.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: entropy.c,v 1.3 2021/08/14 16:14:58 christos 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-2021 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 publicly
21  * available works (as noted below).
22  */
23 
24 #include <sys/cdefs.h>
25 __RCSID("$NetBSD: entropy.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
26 
27 #include "portable.h"
28 
29 #include <ac/string.h>
30 #include <ac/time.h>
31 #include <ac/unistd.h>
32 
33 #ifdef HAVE_PROCESS_H
34 #include <process.h>
35 #endif
36 
37 #include <fcntl.h>
38 
39 #include <lutil.h>
40 #include <lutil_md5.h>
41 
42 /*
43  * lutil_entropy() provides nbytes of entropy in buf.
44  * Quality offered is suitable for one-time uses, such as "once" keys.
45  * Values may not be suitable for multi-time uses.
46  *
47  * Note:  Callers are encouraged to provide additional bytes of
48  * of entropy in the buf argument.  This information is used in
49  * fallback mode to improve the quality of bytes returned.
50  *
51  * This routinue should be extended to support additional sources
52  * of entropy.
53  */
lutil_entropy(unsigned char * buf,ber_len_t nbytes)54 int lutil_entropy( unsigned char *buf, ber_len_t nbytes )
55 {
56 	if( nbytes == 0 ) return 0;
57 
58 #ifdef URANDOM_DEVICE
59 #define URANDOM_NREADS 4
60 	/* Linux and *BSD offer a urandom device */
61 	{
62 		int rc, fd, n=0;
63 
64 		fd = open( URANDOM_DEVICE, O_RDONLY );
65 
66 		if( fd < 0 ) return -1;
67 
68 		do {
69 			rc = read( fd, buf, nbytes );
70 			if( rc <= 0 ) break;
71 
72 			buf+=rc;
73 			nbytes-=rc;
74 
75 			if( ++n >= URANDOM_NREADS ) break;
76 		} while( nbytes > 0 );
77 
78 		close(fd);
79 		return nbytes > 0 ? -1 : 0;
80 	}
81 #elif defined(PROV_RSA_FULL)
82 	{
83 		/* Not used since _WIN32_WINNT not set... */
84 		HCRYPTPROV hProv = 0;
85 
86 		/* Get handle to user default provider */
87 		if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
88 		   return -1;
89 		}
90 
91 		/* Generate random initialization vector */
92 		if(!CryptGenRandom(hProv, (DWORD) nbytes, (BYTE *) buf)) {
93 		   return -1;
94 		}
95 
96 		/* Release provider handle */
97 		if(hProv != 0) CryptReleaseContext(hProv, 0);
98 
99 		return 0;
100 	}
101 #else
102 	{
103 		/* based upon Phil Karn's "practical randomness" idea
104 		 * but implementation 100% OpenLDAP.  So don't blame Phil.
105 		 *
106 		 * Worse case is that this is a MD5 hash of a counter, if
107 		 * MD5 is a strong cryptographic hash, this should be fairly
108 		 * resistant to attack
109 		 */
110 
111 		/*
112 		 * the caller may need to provide external synchronization OR
113 		 * provide entropy (in buf) to ensure quality results as
114 		 * access to this counter may not be atomic.
115 		 */
116 		static int counter = 0;
117 		ber_len_t n;
118 
119 		struct rdata_s {
120 			int counter;
121 
122 			unsigned char *buf;
123 			struct rdata_s *stack;
124 
125 			pid_t	pid;
126 
127 #ifdef HAVE_GETTIMEOFDAY
128 			struct timeval tv;
129 #else
130 			time_t	time;
131 #endif
132 
133 			unsigned long	junk;	/* purposely not initialized */
134 		} rdata;
135 
136 		/* make sure rdata differs for each process */
137 		rdata.pid = getpid();
138 
139 		/* make sure rdata differs for each program */
140 		rdata.buf = buf;
141 		rdata.stack = &rdata;
142 
143 		for( n = 0; n < nbytes; n += 16 ) {
144 			struct lutil_MD5Context ctx;
145 			unsigned char digest[16];
146 
147 			/* poor resolution */
148 #ifdef HAVE_GETTIMEOFDAY
149 			(void) gettimeofday( &rdata.tv, NULL );
150 #else
151 			(void) time( &rdata.time );
152 #endif
153 
154 			/* make sure rdata differs */
155 			rdata.counter = ++counter;
156 			rdata.pid++;
157 			rdata.junk++;
158 
159 			lutil_MD5Init( &ctx );
160 			lutil_MD5Update( &ctx, (unsigned char *) &rdata, sizeof( rdata ) );
161 
162 			/* allow caller to provided additional entropy */
163 			lutil_MD5Update( &ctx, buf, nbytes );
164 
165 			lutil_MD5Final( digest, &ctx );
166 
167 			AC_MEMCPY( &buf[n], digest,
168 				nbytes - n >= 16 ? 16 : nbytes - n );
169 		}
170 
171 		return 0;
172 	}
173 #endif
174 	return -1;
175 }
176