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