Fix reseeding issues of the public RAND_DRBG
[openssl.git] / crypto / rand / rand_unix.c
1 /*
2  * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 #include "e_os.h"
11 #include <stdio.h>
12 #include "internal/cryptlib.h"
13 #include <openssl/rand.h>
14 #include "rand_lcl.h"
15 #include <stdio.h>
16
17 #ifdef OPENSSL_RAND_SEED_GETRANDOM
18 # include <linux/random.h>
19 #endif
20
21 #if (defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)) && \
22         !defined(OPENSSL_RAND_SEED_NONE)
23 # error "UEFI and VXWorks only support seeding NONE"
24 #endif
25
26 #if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) \
27     || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) \
28     || defined(OPENSSL_SYS_UEFI))
29
30 # if defined(OPENSSL_SYS_VOS)
31
32 #  ifndef OPENSSL_RAND_SEED_OS
33 #   error "Unsupported seeding method configured; must be os"
34 #  endif
35
36 #  if defined(OPENSSL_SYS_VOS_HPPA) && defined(OPENSSL_SYS_VOS_IA32)
37 #   error "Unsupported HP-PA and IA32 at the same time."
38 #  endif
39 #  if !defined(OPENSSL_SYS_VOS_HPPA) && !defined(OPENSSL_SYS_VOS_IA32)
40 #   error "Must have one of HP-PA or IA32"
41 #  endif
42
43 /*
44  * The following algorithm repeatedly samples the real-time clock (RTC) to
45  * generate a sequence of unpredictable data.  The algorithm relies upon the
46  * uneven execution speed of the code (due to factors such as cache misses,
47  * interrupts, bus activity, and scheduling) and upon the rather large
48  * relative difference between the speed of the clock and the rate at which
49  * it can be read.  If it is ported to an environment where execution speed
50  * is more constant or where the RTC ticks at a much slower rate, or the
51  * clock can be read with fewer instructions, it is likely that the results
52  * would be far more predictable.  This should only be used for legacy
53  * platforms.
54  *
55  * As a precaution, we assume only 2 bits of entropy per byte.
56  */
57 size_t RAND_POOL_acquire_entropy(RAND_POOL *pool)
58 {
59     short int code;
60     gid_t curr_gid;
61     pid_t curr_pid;
62     uid_t curr_uid;
63     int i, k;
64     size_t bytes_needed;
65     struct timespec ts;
66     unsigned char v;
67 #  ifdef OPENSSL_SYS_VOS_HPPA
68     long duration;
69     extern void s$sleep(long *_duration, short int *_code);
70 #  else
71     long long duration;
72     extern void s$sleep2(long long *_duration, short int *_code);
73 #  endif
74
75     /*
76      * Seed with the gid, pid, and uid, to ensure *some* variation between
77      * different processes.
78      */
79     curr_gid = getgid();
80     RAND_POOL_add(pool, &curr_gid, sizeof(curr_gid), 0);
81     curr_pid = getpid();
82     RAND_POOL_add(pool, &curr_pid, sizeof(curr_pid), 0);
83     curr_uid = getuid();
84     RAND_POOL_add(pool, &curr_uid, sizeof(curr_uid), 0);
85
86     bytes_needed = RAND_POOL_bytes_needed(pool, 2 /*entropy_per_byte*/);
87
88     for (i = 0; i < bytes_needed; i++) {
89         /*
90          * burn some cpu; hope for interrupts, cache collisions, bus
91          * interference, etc.
92          */
93         for (k = 0; k < 99; k++)
94             ts.tv_nsec = random();
95
96 #  ifdef OPENSSL_SYS_VOS_HPPA
97         /* sleep for 1/1024 of a second (976 us).  */
98         duration = 1;
99         s$sleep(&duration, &code);
100 #  else
101         /* sleep for 1/65536 of a second (15 us).  */
102         duration = 1;
103         s$sleep2(&duration, &code);
104 #  endif
105
106         /* Get wall clock time, take 8 bits. */
107         clock_gettime(CLOCK_REALTIME, &ts);
108         v = (unsigned char)(ts.tv_nsec & 0xFF);
109         RAND_POOL_add(pool, arg, &v, sizeof(v) , 2);
110     }
111     return RAND_POOL_entropy_available(pool);
112 }
113
114 # else
115
116 #  if defined(OPENSSL_RAND_SEED_EGD) && \
117         (defined(OPENSSL_NO_EGD) || !defined(DEVRANDOM_EGD))
118 #   error "Seeding uses EGD but EGD is turned off or no device given"
119 #  endif
120
121 #  if defined(OPENSSL_RAND_SEED_DEVRANDOM) && !defined(DEVRANDOM)
122 #   error "Seeding uses urandom but DEVRANDOM is not configured"
123 #  endif
124
125 #  if defined(OPENSSL_RAND_SEED_OS)
126 #   if defined(DEVRANDOM)
127 #    define OPENSSL_RAND_SEED_DEVRANDOM
128 #   else
129 #    error "OS seeding requires DEVRANDOM to be configured"
130 #   endif
131 #  endif
132
133 #  if defined(OPENSSL_RAND_SEED_LIBRANDOM)
134 #   error "librandom not (yet) supported"
135 #  endif
136
137 /*
138  * Try the various seeding methods in turn, exit when successful.
139  *
140  * TODO(DRBG): If more than one entropy source is available, is it
141  * preferable to stop as soon as enough entropy has been collected
142  * (as favored by @rsalz) or should one rather be defensive and add
143  * more entropy than requested and/or from different sources?
144  *
145  * Currently, the user can select multiple entropy sources in the
146  * configure step, yet in practice only the first available source
147  * will be used. A more flexible solution has been requested, but
148  * currently it is not clear how this can be achieved without
149  * overengineering the problem. There are many parameters which
150  * could be taken into account when selecting the order and amount
151  * of input from the different entropy sources (trust, quality,
152  * possibility of blocking).
153  */
154 size_t RAND_POOL_acquire_entropy(RAND_POOL *pool)
155 {
156 #  ifdef OPENSSL_RAND_SEED_NONE
157     return RAND_POOL_entropy_available(pool);
158 #  else
159     size_t bytes_needed;
160     size_t entropy_available = 0;
161     unsigned char *buffer;
162
163 #   ifdef OPENSSL_RAND_SEED_GETRANDOM
164     bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
165     buffer = RAND_POOL_add_begin(pool, bytes_needed);
166     if (buffer != NULL) {
167         size_t bytes = 0;
168
169         if (getrandom(buffer, bytes_needed, 0) == (int)bytes_needed)
170             bytes = bytes_needed;
171
172         entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
173     }
174     if (entropy_available > 0)
175         return entropy_available;
176 #   endif
177
178 #   if defined(OPENSSL_RAND_SEED_LIBRANDOM)
179     {
180         /* Not yet implemented. */
181     }
182 #   endif
183
184 #   ifdef OPENSSL_RAND_SEED_DEVRANDOM
185     bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
186     if (bytes_needed > 0) {
187         static const char *paths[] = { DEVRANDOM, NULL };
188         FILE *fp;
189         int i;
190
191         for (i = 0; paths[i] != NULL; i++) {
192             if ((fp = fopen(paths[i], "rb")) == NULL)
193                 continue;
194             setbuf(fp, NULL);
195             buffer = RAND_POOL_add_begin(pool, bytes_needed);
196             if (buffer != NULL) {
197                 size_t bytes = 0;
198                 if (fread(buffer, 1, bytes_needed, fp) == bytes_needed)
199                     bytes = bytes_needed;
200
201                 entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
202             }
203             fclose(fp);
204             if (entropy_available > 0)
205                 return entropy_available;
206
207             bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
208         }
209     }
210 #   endif
211
212 #   ifdef OPENSSL_RAND_SEED_RDTSC
213     entropy_available = rand_acquire_entropy_from_tsc(pool);
214     if (entropy_available > 0)
215         return entropy_available;
216 #   endif
217
218 #   ifdef OPENSSL_RAND_SEED_RDCPU
219     entropy_available = rand_acquire_entropy_from_cpu(pool);
220     if (entropy_available > 0)
221         return entropy_available;
222 #   endif
223
224 #   ifdef OPENSSL_RAND_SEED_EGD
225     bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
226     if (bytes_needed > 0) {
227         static const char *paths[] = { DEVRANDOM_EGD, NULL };
228         int i;
229
230         for (i = 0; paths[i] != NULL; i++) {
231             buffer = RAND_POOL_add_begin(pool, bytes_needed);
232             if (buffer != NULL) {
233                 size_t bytes = 0;
234                 int num = RAND_query_egd_bytes(paths[i],
235                                                buffer, (int)bytes_needed);
236                 if (num == (int)bytes_needed)
237                     bytes = bytes_needed;
238
239                 entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
240             }
241             if (entropy_available > 0)
242                 return entropy_available;
243         }
244     }
245 #   endif
246
247     return RAND_POOL_entropy_available(pool);
248 #  endif
249 }
250 # endif
251
252 #endif