entropy.c revision 1.3
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 */
54int 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