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)
46extern "C"
47# endif
48BOOLEAN 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
81typedef 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
91typedef 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
99static Salsa20RandomGlobal global = {
100    SODIUM_C99(.initialized =) 0,
101    SODIUM_C99(.random_data_source_fd =) -1
102};
103
104static 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
115static uint64_t
116sodium_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
128static uint64_t
129sodium_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
146static void
147randombytes_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
156static ssize_t
157safe_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)
181static int
182randombytes_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
208static int
209randombytes_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))
250static int
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
267static int
268randombytes_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
289static void
290randombytes_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
329static void
330randombytes_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
377static void
378randombytes_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
398static int
399randombytes_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
412static int
413randombytes_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
447static void
448randombytes_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
468static inline void
469randombytes_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
483static void
484randombytes_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
515static uint32_t
516randombytes_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
545static const char *
546randombytes_salsa20_implementation_name(void)
547{
548    return "salsa20";
549}
550
551struct 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