xref: /freebsd/sys/contrib/libsodium/src/libsodium/randombytes/salsa20/randombytes_salsa20_random.c (revision 3611ec604864a7d4dcc9a3ea898c80eb35eef8a0)
1 
2 #include <assert.h>
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #if !defined(_MSC_VER) && !defined(__BORLANDC__)
11 # include <unistd.h>
12 #endif
13 
14 #include <sys/types.h>
15 #ifndef _WIN32
16 # include <sys/stat.h>
17 # include <sys/time.h>
18 #endif
19 #ifdef __linux__
20 # ifdef __dietlibc__
21 #  define _LINUX_SOURCE
22 # else
23 #  include <sys/syscall.h>
24 # endif
25 # include <poll.h>
26 #endif
27 #ifdef HAVE_RDRAND
28 # pragma GCC target("rdrnd")
29 # include <immintrin.h>
30 #endif
31 
32 #include "core.h"
33 #include "crypto_core_salsa20.h"
34 #include "crypto_stream_salsa20.h"
35 #include "private/common.h"
36 #include "randombytes.h"
37 #include "randombytes_salsa20_random.h"
38 #include "runtime.h"
39 #include "utils.h"
40 
41 #ifdef _WIN32
42 # include <windows.h>
43 # include <sys/timeb.h>
44 # define RtlGenRandom SystemFunction036
45 # if defined(__cplusplus)
46 extern "C"
47 # endif
48 BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
49 # pragma comment(lib, "advapi32.lib")
50 # ifdef __BORLANDC__
51 #  define _ftime ftime
52 #  define _timeb timeb
53 # endif
54 #endif
55 
56 #define SALSA20_RANDOM_BLOCK_SIZE crypto_core_salsa20_OUTPUTBYTES
57 
58 #if defined(__OpenBSD__) || defined(__CloudABI__)
59 # define HAVE_SAFE_ARC4RANDOM 1
60 #endif
61 
62 #ifndef SSIZE_MAX
63 # define SSIZE_MAX (SIZE_MAX / 2 - 1)
64 #endif
65 #ifndef S_ISNAM
66 # ifdef __COMPCERT__
67 #  define S_ISNAM(X) 1
68 # else
69 #  define S_ISNAM(X) 0
70 # endif
71 #endif
72 
73 #ifndef TLS
74 # ifdef _WIN32
75 #  define TLS __declspec(thread)
76 # else
77 #  define TLS
78 # endif
79 #endif
80 
81 typedef struct Salsa20RandomGlobal_ {
82     int           initialized;
83     int           random_data_source_fd;
84     int           getrandom_available;
85     int           rdrand_available;
86 #ifdef HAVE_GETPID
87     pid_t         pid;
88 #endif
89 } Salsa20RandomGlobal;
90 
91 typedef struct Salsa20Random_ {
92     int           initialized;
93     size_t        rnd32_outleft;
94     unsigned char key[crypto_stream_salsa20_KEYBYTES];
95     unsigned char rnd32[16U * SALSA20_RANDOM_BLOCK_SIZE];
96     uint64_t      nonce;
97 } Salsa20Random;
98 
99 static Salsa20RandomGlobal global = {
100     SODIUM_C99(.initialized =) 0,
101     SODIUM_C99(.random_data_source_fd =) -1
102 };
103 
104 static TLS Salsa20Random stream = {
105     SODIUM_C99(.initialized =) 0,
106     SODIUM_C99(.rnd32_outleft =) (size_t) 0U
107 };
108 
109 
110 /*
111  * Get a high-resolution timestamp, as a uint64_t value
112  */
113 
114 #ifdef _WIN32
115 static uint64_t
sodium_hrtime(void)116 sodium_hrtime(void)
117 {
118     struct _timeb tb;
119 # pragma warning(push)
120 # pragma warning(disable: 4996)
121     _ftime(&tb);
122 # pragma warning(pop)
123     return ((uint64_t) tb.time) * 1000000U + ((uint64_t) tb.millitm) * 1000U;
124 }
125 
126 #else /* _WIN32 */
127 
128 static uint64_t
sodium_hrtime(void)129 sodium_hrtime(void)
130 {
131     struct   timeval tv;
132 
133     if (gettimeofday(&tv, NULL) != 0) {
134         sodium_misuse(); /* LCOV_EXCL_LINE */
135     }
136     return ((uint64_t) tv.tv_sec) * 1000000U + (uint64_t) tv.tv_usec;
137 }
138 #endif
139 
140 /*
141  * Initialize the entropy source
142  */
143 
144 #ifdef _WIN32
145 
146 static void
randombytes_salsa20_random_init(void)147 randombytes_salsa20_random_init(void)
148 {
149     stream.nonce = sodium_hrtime();
150     assert(stream.nonce != (uint64_t) 0U);
151     global.rdrand_available = sodium_runtime_has_rdrand();
152 }
153 
154 #else /* _WIN32 */
155 
156 static ssize_t
safe_read(const int fd,void * const buf_,size_t size)157 safe_read(const int fd, void * const buf_, size_t size)
158 {
159     unsigned char *buf = (unsigned char *) buf_;
160     ssize_t        readnb;
161 
162     assert(size > (size_t) 0U);
163     assert(size <= SSIZE_MAX);
164     do {
165         while ((readnb = read(fd, buf, size)) < (ssize_t) 0 &&
166                (errno == EINTR || errno == EAGAIN)); /* LCOV_EXCL_LINE */
167         if (readnb < (ssize_t) 0) {
168             return readnb; /* LCOV_EXCL_LINE */
169         }
170         if (readnb == (ssize_t) 0) {
171             break; /* LCOV_EXCL_LINE */
172         }
173         size -= (size_t) readnb;
174         buf += readnb;
175     } while (size > (ssize_t) 0);
176 
177     return (ssize_t) (buf - (unsigned char *) buf_);
178 }
179 
180 # if defined(__linux__) && !defined(USE_BLOCKING_RANDOM) && !defined(NO_BLOCKING_RANDOM_POLL)
181 static int
randombytes_block_on_dev_random(void)182 randombytes_block_on_dev_random(void)
183 {
184     struct pollfd pfd;
185     int           fd;
186     int           pret;
187 
188     fd = open("/dev/random", O_RDONLY);
189     if (fd == -1) {
190         return 0;
191     }
192     pfd.fd = fd;
193     pfd.events = POLLIN;
194     pfd.revents = 0;
195     do {
196         pret = poll(&pfd, 1, -1);
197     } while (pret < 0 && (errno == EINTR || errno == EAGAIN));
198     if (pret != 1) {
199         (void) close(fd);
200         errno = EIO;
201         return -1;
202     }
203     return close(fd);
204 }
205 # endif
206 
207 # ifndef HAVE_SAFE_ARC4RANDOM
208 static int
randombytes_salsa20_random_random_dev_open(void)209 randombytes_salsa20_random_random_dev_open(void)
210 {
211 /* LCOV_EXCL_START */
212     struct stat       st;
213     static const char *devices[] = {
214 #  ifndef USE_BLOCKING_RANDOM
215         "/dev/urandom",
216 #  endif
217         "/dev/random", NULL
218     };
219     const char      **device = devices;
220     int               fd;
221 
222 # if defined(__linux__) && !defined(USE_BLOCKING_RANDOM) && !defined(NO_BLOCKING_RANDOM_POLL)
223     if (randombytes_block_on_dev_random() != 0) {
224         return -1;
225     }
226 # endif
227     do {
228         fd = open(*device, O_RDONLY);
229         if (fd != -1) {
230             if (fstat(fd, &st) == 0 && (S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode))) {
231 #  if defined(F_SETFD) && defined(FD_CLOEXEC)
232                 (void) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
233 #  endif
234                 return fd;
235             }
236             (void) close(fd);
237         } else if (errno == EINTR) {
238             continue;
239         }
240         device++;
241     } while (*device != NULL);
242 
243     errno = EIO;
244     return -1;
245 /* LCOV_EXCL_STOP */
246 }
247 # endif
248 
249 # if defined(__dietlibc__) || (defined(SYS_getrandom) && defined(__NR_getrandom))
250 static int
_randombytes_linux_getrandom(void * const buf,const size_t size)251 _randombytes_linux_getrandom(void * const buf, const size_t size)
252 {
253     int readnb;
254 
255     assert(size <= 256U);
256     do {
257 #  ifdef __dietlibc__
258         readnb = getrandom(buf, size, 0);
259 #  else
260         readnb = syscall(SYS_getrandom, buf, (int) size, 0);
261 #  endif
262     } while (readnb < 0 && (errno == EINTR || errno == EAGAIN));
263 
264     return (readnb == (int) size) - 1;
265 }
266 
267 static int
randombytes_linux_getrandom(void * const buf_,size_t size)268 randombytes_linux_getrandom(void * const buf_, size_t size)
269 {
270     unsigned char *buf = (unsigned char *) buf_;
271     size_t         chunk_size = 256U;
272 
273     do {
274         if (size < chunk_size) {
275             chunk_size = size;
276             assert(chunk_size > (size_t) 0U);
277         }
278         if (_randombytes_linux_getrandom(buf, chunk_size) != 0) {
279             return -1;
280         }
281         size -= chunk_size;
282         buf += chunk_size;
283     } while (size > (size_t) 0U);
284 
285     return 0;
286 }
287 # endif
288 
289 static void
randombytes_salsa20_random_init(void)290 randombytes_salsa20_random_init(void)
291 {
292     const int errno_save = errno;
293 
294     stream.nonce = sodium_hrtime();
295     global.rdrand_available = sodium_runtime_has_rdrand();
296     assert(stream.nonce != (uint64_t) 0U);
297 
298 # ifdef HAVE_SAFE_ARC4RANDOM
299     errno = errno_save;
300 # else
301 
302 #  if defined(SYS_getrandom) && defined(__NR_getrandom)
303     {
304         unsigned char fodder[16];
305 
306         if (randombytes_linux_getrandom(fodder, sizeof fodder) == 0) {
307             global.getrandom_available = 1;
308             errno = errno_save;
309             return;
310         }
311         global.getrandom_available = 0;
312     }
313 #  endif /* SYS_getrandom */
314 
315     if ((global.random_data_source_fd =
316          randombytes_salsa20_random_random_dev_open()) == -1) {
317         sodium_misuse(); /* LCOV_EXCL_LINE */
318     }
319     errno = errno_save;
320 # endif /* HAVE_SAFE_ARC4RANDOM */
321 }
322 
323 #endif /* _WIN32 */
324 
325 /*
326  * (Re)seed the generator using the entropy source
327  */
328 
329 static void
randombytes_salsa20_random_stir(void)330 randombytes_salsa20_random_stir(void)
331 {
332     memset(stream.rnd32, 0, sizeof stream.rnd32);
333     stream.rnd32_outleft = (size_t) 0U;
334     if (global.initialized == 0) {
335         randombytes_salsa20_random_init();
336         global.initialized = 1;
337     }
338 #ifdef HAVE_GETPID
339     global.pid = getpid();
340 #endif
341 
342 #ifndef _WIN32
343 
344 # ifdef HAVE_SAFE_ARC4RANDOM
345     arc4random_buf(stream.key, sizeof stream.key);
346 # elif defined(SYS_getrandom) && defined(__NR_getrandom)
347     if (global.getrandom_available != 0) {
348         if (randombytes_linux_getrandom(stream.key, sizeof stream.key) != 0) {
349             sodium_misuse(); /* LCOV_EXCL_LINE */
350         }
351     } else if (global.random_data_source_fd == -1 ||
352                safe_read(global.random_data_source_fd, stream.key,
353                          sizeof stream.key) != (ssize_t) sizeof stream.key) {
354         sodium_misuse(); /* LCOV_EXCL_LINE */
355     }
356 # else
357     if (global.random_data_source_fd == -1 ||
358         safe_read(global.random_data_source_fd, stream.key,
359                   sizeof stream.key) != (ssize_t) sizeof stream.key) {
360         sodium_misuse(); /* LCOV_EXCL_LINE */
361     }
362 # endif
363 
364 #else /* _WIN32 */
365     if (! RtlGenRandom((PVOID) stream.key, (ULONG) sizeof stream.key)) {
366         sodium_misuse(); /* LCOV_EXCL_LINE */
367     }
368 #endif
369 
370     stream.initialized = 1;
371 }
372 
373 /*
374  * Reseed the generator if it hasn't been initialized yet
375  */
376 
377 static void
randombytes_salsa20_random_stir_if_needed(void)378 randombytes_salsa20_random_stir_if_needed(void)
379 {
380 #ifdef HAVE_GETPID
381     if (stream.initialized == 0) {
382         randombytes_salsa20_random_stir();
383     } else if (global.pid != getpid()) {
384         sodium_misuse(); /* LCOV_EXCL_LINE */
385     }
386 #else
387     if (stream.initialized == 0) {
388         randombytes_salsa20_random_stir();
389     }
390 #endif
391 }
392 
393 /*
394  * Close the stream, free global resources
395  */
396 
397 #ifdef _WIN32
398 static int
randombytes_salsa20_random_close(void)399 randombytes_salsa20_random_close(void)
400 {
401     int ret = -1;
402 
403     if (global.initialized != 0) {
404         global.initialized = 0;
405         ret = 0;
406     }
407     sodium_memzero(&stream, sizeof stream);
408 
409     return ret;
410 }
411 #else
412 static int
randombytes_salsa20_random_close(void)413 randombytes_salsa20_random_close(void)
414 {
415     int ret = -1;
416 
417     if (global.random_data_source_fd != -1 &&
418         close(global.random_data_source_fd) == 0) {
419         global.random_data_source_fd = -1;
420         global.initialized = 0;
421 # ifdef HAVE_GETPID
422         global.pid = (pid_t) 0;
423 # endif
424         ret = 0;
425     }
426 
427 # ifdef HAVE_SAFE_ARC4RANDOM
428     ret = 0;
429 # endif
430 
431 # if defined(SYS_getrandom) && defined(__NR_getrandom)
432     if (global.getrandom_available != 0) {
433         ret = 0;
434     }
435 # endif
436 
437     sodium_memzero(&stream, sizeof stream);
438 
439     return ret;
440 }
441 #endif
442 
443 /*
444  * RDRAND is only used to mitigate prediction if a key is compromised
445  */
446 
447 static void
randombytes_salsa20_random_xorhwrand(void)448 randombytes_salsa20_random_xorhwrand(void)
449 {
450 /* LCOV_EXCL_START */
451 #ifdef HAVE_RDRAND
452     unsigned int r;
453 
454     if (global.rdrand_available == 0) {
455         return;
456     }
457     (void) _rdrand32_step(&r);
458     * (uint32_t *) (void *)
459         &stream.key[crypto_stream_salsa20_KEYBYTES - 4] ^= (uint32_t) r;
460 #endif
461 /* LCOV_EXCL_STOP */
462 }
463 
464 /*
465  * XOR the key with another same-length secret
466  */
467 
468 static inline void
randombytes_salsa20_random_xorkey(const unsigned char * const mix)469 randombytes_salsa20_random_xorkey(const unsigned char * const mix)
470 {
471     unsigned char *key = stream.key;
472     size_t         i;
473 
474     for (i = (size_t) 0U; i < sizeof stream.key; i++) {
475         key[i] ^= mix[i];
476     }
477 }
478 
479 /*
480  * Put `size` random bytes into `buf` and overwrite the key
481  */
482 
483 static void
randombytes_salsa20_random_buf(void * const buf,const size_t size)484 randombytes_salsa20_random_buf(void * const buf, const size_t size)
485 {
486     size_t i;
487     int    ret;
488 
489     randombytes_salsa20_random_stir_if_needed();
490     COMPILER_ASSERT(sizeof stream.nonce == crypto_stream_salsa20_NONCEBYTES);
491 #if defined(ULONG_LONG_MAX) && defined(SIZE_MAX)
492 # if SIZE_MAX > ULONG_LONG_MAX
493     /* coverity[result_independent_of_operands] */
494     assert(size <= ULONG_LONG_MAX);
495 # endif
496 #endif
497     ret = crypto_stream_salsa20((unsigned char *) buf, (unsigned long long) size,
498                                 (unsigned char *) &stream.nonce, stream.key);
499     assert(ret == 0);
500     for (i = 0U; i < sizeof size; i++) {
501         stream.key[i] ^= ((const unsigned char *) (const void *) &size)[i];
502     }
503     randombytes_salsa20_random_xorhwrand();
504     stream.nonce++;
505     crypto_stream_salsa20_xor(stream.key, stream.key, sizeof stream.key,
506                               (unsigned char *) &stream.nonce, stream.key);
507 }
508 
509 /*
510  * Pop a 32-bit value from the random pool
511  *
512  * Overwrite the key after the pool gets refilled.
513  */
514 
515 static uint32_t
randombytes_salsa20_random(void)516 randombytes_salsa20_random(void)
517 {
518     uint32_t val;
519     int      ret;
520 
521     COMPILER_ASSERT(sizeof stream.rnd32 >= (sizeof stream.key) + (sizeof val));
522     COMPILER_ASSERT(((sizeof stream.rnd32) - (sizeof stream.key))
523                     % sizeof val == (size_t) 0U);
524     if (stream.rnd32_outleft <= (size_t) 0U) {
525         randombytes_salsa20_random_stir_if_needed();
526         COMPILER_ASSERT(sizeof stream.nonce == crypto_stream_salsa20_NONCEBYTES);
527         ret = crypto_stream_salsa20((unsigned char *) stream.rnd32,
528                                     (unsigned long long) sizeof stream.rnd32,
529                                     (unsigned char *) &stream.nonce,
530                                     stream.key);
531         assert(ret == 0);
532         stream.rnd32_outleft = (sizeof stream.rnd32) - (sizeof stream.key);
533         randombytes_salsa20_random_xorhwrand();
534         randombytes_salsa20_random_xorkey(&stream.rnd32[stream.rnd32_outleft]);
535         memset(&stream.rnd32[stream.rnd32_outleft], 0, sizeof stream.key);
536         stream.nonce++;
537     }
538     stream.rnd32_outleft -= sizeof val;
539     memcpy(&val, &stream.rnd32[stream.rnd32_outleft], sizeof val);
540     memset(&stream.rnd32[stream.rnd32_outleft], 0, sizeof val);
541 
542     return val;
543 }
544 
545 static const char *
randombytes_salsa20_implementation_name(void)546 randombytes_salsa20_implementation_name(void)
547 {
548     return "salsa20";
549 }
550 
551 struct randombytes_implementation randombytes_salsa20_implementation = {
552     SODIUM_C99(.implementation_name =) randombytes_salsa20_implementation_name,
553     SODIUM_C99(.random =) randombytes_salsa20_random,
554     SODIUM_C99(.stir =) randombytes_salsa20_random_stir,
555     SODIUM_C99(.uniform =) NULL,
556     SODIUM_C99(.buf =) randombytes_salsa20_random_buf,
557     SODIUM_C99(.close =) randombytes_salsa20_random_close
558 };
559