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