]> git.sur5r.net Git - openldap/blob - libraries/liblutil/entropy.c
Happy new year (belated)
[openldap] / libraries / liblutil / entropy.c
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-2014 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 publically
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 offerred 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  */
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 }