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