summaryrefslogtreecommitdiff
path: root/src/rand.c
blob: 4f656dc34582b1254216148ac55d6e95a2d87c2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
/*
 * rand - generate random bytes
 *
 * Copyright(c) 2016 Glenn Strauss gstrauss()gluelogic.com  All rights reserved
 * License: BSD 3-clause (same as lighttpd)
 */
#include "first.h"

#include "rand.h"
#include "buffer.h"
#include "fdevent.h"
#include "safe_memclear.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "sys-crypto.h" /* USE_LIB_CRYPTO */
#ifdef USE_NETTLE_CRYPTO
#undef USE_MBEDTLS_CRYPTO
#undef USE_WOLFSSL_CRYPTO
#undef USE_OPENSSL_CRYPTO
#undef USE_GNUTLS_CRYPTO
#undef USE_NSS_CRYPTO
#include <nettle/knuth-lfib.h>
#include <nettle/arcfour.h>
#include <nettle/yarrow.h>
#endif
#ifdef USE_MBEDTLS_CRYPTO
#undef USE_WOLFSSL_CRYPTO
#undef USE_OPENSSL_CRYPTO
#undef USE_GNUTLS_CRYPTO
#undef USE_NSS_CRYPTO
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#endif
#ifdef USE_OPENSSL_CRYPTO
#undef USE_WOLFSSL_CRYPTO
#undef USE_GNUTLS_CRYPTO
#undef USE_NSS_CRYPTO
#include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */
#include <openssl/rand.h>
#endif
#ifdef USE_WOLFSSL_CRYPTO
#undef USE_GNUTLS_CRYPTO
#undef USE_NSS_CRYPTO
#include <wolfssl/wolfcrypt/random.h>
#endif
#ifdef USE_GNUTLS_CRYPTO
#undef USE_NSS_CRYPTO
#include <gnutls/crypto.h>
#endif
#ifdef USE_NSS_CRYPTO
#include <nss3/nss.h>
#include <nss3/pk11pub.h>
#endif
#ifdef HAVE_GETENTROPY
#include <sys/random.h>
#endif
#ifdef HAVE_LINUX_RANDOM_H
#include <sys/syscall.h>
#include <linux/random.h>
#endif
#ifdef RNDGETENTCNT
#include <sys/ioctl.h>
#endif

/* Take some reasonable steps to attempt to *seed* random number generators with
 * cryptographically random data.  Some of these initialization routines may
 * block, and are intended to be called only at startup in lighttpd, or
 * immediately after fork() to start lighttpd workers.
 *
 * Update: li_rand_init() is now deferred until first use so that installations
 * that do not use modules which use these routines do need to potentially block
 * at startup.  Current use by core lighttpd modules is in mod_auth HTTP Digest
 * auth and in mod_usertrack.  Deferring collection of random data until first
 * use may allow sufficient entropy to be collected by kernel before first use,
 * helping reduce or avoid situations in low-entropy-generating embedded devices
 * which might otherwise block lighttpd for minutes at device startup.
 * Further discussion in https://redmine.lighttpd.net/boards/2/topics/6981
 *
 * Note: results from li_rand_pseudo_bytes() are not necessarily
 * cryptographically random and must not be used for purposes such
 * as key generation which require cryptographic randomness.
 *
 * https://wiki.openssl.org/index.php/Random_Numbers
 * https://wiki.openssl.org/index.php/Random_fork-safety
 *
 * openssl random number generators are not thread-safe by default
 * https://wiki.openssl.org/index.php/Manual:Threads(3)
 *
 * RFE: add more paranoid checks from the following to improve confidence:
 * http://insanecoding.blogspot.co.uk/2014/05/a-good-idea-with-bad-usage-devurandom.html
 * RFE: retry on EINTR
 * RFE: check RAND_status()
 */

static int li_getentropy (void *buf, size_t buflen)
{
  #ifdef HAVE_GETENTROPY
    return getentropy(buf, buflen);
  #else
    /*(see NOTES section in 'man getrandom' on Linux)*/
   #if defined(HAVE_GETRANDOM) || defined(SYS_getrandom)
    if (buflen <= 256) {
      #ifdef HAVE_GETRANDOM /*(not implemented in glibc yet)*/
        int num = getrandom(buf, buflen, 0);
      #elif defined(SYS_getrandom)
        /* https://lwn.net/Articles/605828/ */
        /* https://bbs.archlinux.org/viewtopic.php?id=200039 */
        int num = (int)syscall(SYS_getrandom, buf, buflen, 0);
      #endif
        if (num == (int)buflen) return 0;
        if (num < 0)            return num; /* -1 */
    }
   #else
    UNUSED(buf);
    UNUSED(buflen);
   #endif
    errno = EIO;
    return -1;
  #endif
}

static int li_rand_device_bytes (unsigned char *buf, int num)
{
    /* randomness from these devices is cryptographically strong,
     * unless /dev/urandom is low on entropy */

    static const char * const devices[] = {
      #ifdef __OpenBSD__
        "/dev/arandom",
      #endif
        "/dev/urandom",
        "/dev/random"
    };

    /* device files might not be available in chroot environment,
     * so prefer syscall, if available */
    if (0 == li_getentropy(buf, (size_t)num)) return 1;

    for (unsigned int u = 0; u < sizeof(devices)/sizeof(devices[0]); ++u) {
        /*(some systems might have symlink to another device; omit O_NOFOLLOW)*/
        int fd = fdevent_open_cloexec(devices[u], 1, O_RDONLY, 0);
        if (fd >= 0) {
            ssize_t rd = 0;
          #ifdef RNDGETENTCNT
            int entropy;
            if (0 == ioctl(fd, (unsigned long)(RNDGETENTCNT), &entropy)
                && entropy >= num*8)
          #endif
                rd = read(fd, buf, (size_t)num);
            close(fd);
            if (rd == num) {
                return 1;
            }
        }
    }

    return 0;
}

static int li_rand_inited;
static unsigned short xsubi[3];
#ifdef USE_MBEDTLS_CRYPTO
#ifdef MBEDTLS_ENTROPY_C
static mbedtls_entropy_context entropy;
#ifdef MBEDTLS_CTR_DRBG_C
static mbedtls_ctr_drbg_context ctr_drbg;
#endif
#endif
#endif
#ifdef USE_WOLFSSL_CRYPTO
static WC_RNG wolf_globalRNG;
#endif
#ifdef USE_NETTLE_CRYPTO
static struct knuth_lfib_ctx knuth_lfib_ctx;
static struct arcfour_ctx    arcfour_ctx;
static struct yarrow256_ctx  yarrow256_ctx;
#endif

#ifdef USE_NETTLE_CRYPTO
/* adapted from Nettle documentation arcfour_set_key_hashed() in nettle.pdf */
/* A more robust key setup function for ARCFOUR */
static void
li_arcfour_init_random_key_hashed(struct arcfour_ctx *ctx)
{
    uint8_t key[ARCFOUR_KEY_SIZE];
    const size_t length = sizeof(key);
    if (1 != li_rand_device_bytes(key, (int)sizeof(key))) {
        log_failed_assert(__FILE__, __LINE__,
                          "gathering entropy for arcfour seed failed");
    }
    memset(ctx, 0, sizeof(*ctx));

    struct sha256_ctx hash;
    uint8_t digest[SHA256_DIGEST_SIZE];
    uint8_t buf[0x200];
    memset(buf, 0, sizeof(buf));
    sha256_init(&hash);
    sha256_update(&hash, length, key);
    sha256_digest(&hash, SHA256_DIGEST_SIZE, digest);
    nettle_arcfour_set_key(ctx, SHA256_DIGEST_SIZE, digest);
    nettle_arcfour_crypt(ctx, sizeof(buf), buf, buf);
    nettle_arcfour_crypt(ctx, sizeof(buf), buf, buf);
    nettle_arcfour_crypt(ctx, sizeof(buf), buf, buf);
}
#endif

__attribute_cold__
static void li_rand_init (void)
{
    /* (intended to be called at init and after fork() in order to re-seed PRNG
     *  so that forked children, grandchildren, etc do not share PRNG seed)
     * https://github.com/ramsey/uuid/issues/80
     * https://www.agwa.name/blog/post/libressls_prng_is_unsafe_on_linux
     *   (issue in early version of libressl has since been fixed)
     * https://github.com/libressl-portable/portable/commit/32d9eeeecf4e951e1566d5f4a42b36ea37b60f35
     */
    unsigned int u;
    li_rand_inited = 1;
    if (1 == li_rand_device_bytes((unsigned char *)xsubi, (int)sizeof(xsubi))) {
        u = ((unsigned int)xsubi[0] << 16) | xsubi[1];
    }
    else {
      #ifdef HAVE_ARC4RANDOM_BUF
        u = arc4random();
        arc4random_buf(xsubi, sizeof(xsubi));
      #elif defined(__COVERITY__)
        /* Coverity Scan ignores(?) annotation below,
         * so hide fallback path from Coverity Scan */
        u = (unsigned int)(time(NULL) ^ getpid());
      #else
        /* NOTE: not cryptographically random !!! */
        srand((unsigned int)(time(NULL) ^ getpid()));
        for (u = 0; u < sizeof(unsigned short); ++u)
            /* coverity[dont_call : FALSE] */
            xsubi[u] = (unsigned short)(rand() & 0xFFFF);
        u = ((unsigned int)xsubi[0] << 16) | xsubi[1];
      #endif
    }
    srand(u);   /*(initialize just in case rand() used elsewhere)*/
  #ifdef HAVE_SRANDOM
    srandom(u); /*(initialize just in case random() used elsewhere)*/
  #endif
  #ifdef USE_NETTLE_CRYPTO
    nettle_knuth_lfib_init(&knuth_lfib_ctx, u);
    nettle_yarrow256_init(&yarrow256_ctx, 0, NULL);
    li_arcfour_init_random_key_hashed(&arcfour_ctx);
  #endif
  #ifdef USE_WOLFSSL_CRYPTO
    /* xsubi[] is small, so use wc_InitRng() instead of wc_InitRngNonce()
     * to get default behavior of a larger internally-generated nonce */
    if (0 != wolfCrypt_Init() || 0 != wc_InitRng(&wolf_globalRNG))
        log_failed_assert(__FILE__, __LINE__,
                          "wolfCrypt_Init or wc_InitRng() failed");
  #endif
  #ifdef USE_OPENSSL_CRYPTO
    RAND_poll();
    RAND_seed(xsubi, (int)sizeof(xsubi));
  #endif
  #ifdef USE_MBEDTLS_CRYPTO
  #ifdef MBEDTLS_ENTROPY_C
    mbedtls_entropy_init(&entropy);
  #ifdef MBEDTLS_CTR_DRBG_C
    mbedtls_ctr_drbg_init(&ctr_drbg);
    int rc =
      mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
                            (unsigned char *)xsubi, sizeof(xsubi));
    if (0 != rc) /*(not expecting built-in entropy function to fail)*/
        log_failed_assert(__FILE__, __LINE__, "mbedtls_ctr_drbg_seed() failed");
  #endif
  #endif
  #endif
  #ifdef USE_NSS_CRYPTO
    if (!NSS_IsInitialized() && NSS_NoDB_Init(NULL) < 0)
        SEGFAULT();
    PK11_RandomUpdate(xsubi, sizeof(xsubi));
  #endif
}

void li_rand_reseed (void)
{
  #ifdef USE_GNUTLS_CRYPTO
    gnutls_rnd_refresh();
    return;
  #endif
  #ifdef USE_WOLFSSL_CRYPTO
    if (li_rand_inited) {
      #if 0 /*(wc_RNG_DRBG_Reseed() is not part of public API)*/
        /*(XXX: might use stack to procure larger seed;
         * xsubi[] is short (6 bytes)) */
        if (1 == li_rand_device_bytes((unsigned char *)xsubi,
                                      (int)sizeof(xsubi))) {
            if (0 != wc_RNG_DRBG_Reseed(&wolf_globalRNG,
                                        (const byte *)xsubi,
                                        (word32)sizeof(xsubi)))
                /*(not expecting this to fail)*/
                log_failed_assert(__FILE__, __LINE__,
                                  "wc_RNG_DRBG_Reseed() failed");
        }
      #else
        wc_FreeRng(&wolf_globalRNG);
        if (0 != wc_InitRng(&wolf_globalRNG))
            log_failed_assert(__FILE__, __LINE__, "wc_InitRng() failed");
      #endif
        return;
    }
  #endif
  #ifdef USE_MBEDTLS_CRYPTO
    if (li_rand_inited) {
      #ifdef MBEDTLS_ENTROPY_C
      #ifdef MBEDTLS_CTR_DRBG_C
        mbedtls_ctr_drbg_free(&ctr_drbg);
      #endif
        mbedtls_entropy_free(&entropy);
      #endif
    }
  #endif
    if (li_rand_inited) li_rand_init();
}

int li_rand_pseudo (void)
{
  #ifdef USE_GNUTLS_CRYPTO
    int i;
    if (0 == gnutls_rnd(GNUTLS_RND_NONCE, &i, sizeof(i))) return i;
  #endif
    if (!li_rand_inited) li_rand_init();
    /* randomness *is not* cryptographically strong */
    /* (attempt to use better mechanisms to replace the more portable rand()) */
  #ifdef USE_OPENSSL_CRYPTO /* (openssl 1.1.0 deprecates RAND_pseudo_bytes()) */
  #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
    int i;
    if (-1 != RAND_pseudo_bytes((unsigned char *)&i, sizeof(i))) return i;
  #endif
  #endif
  #ifdef USE_WOLFSSL_CRYPTO
    /* RAND_pseudo_bytes() in WolfSSL is equivalent to RAND_bytes() */
    int i;
    if (0 == wc_RNG_GenerateBlock(&wolf_globalRNG,(byte *)&i,(word32)sizeof(i)))
        return i;
  #endif
  #ifdef USE_NETTLE_CRYPTO
    int i = (int)nettle_knuth_lfib_get(&knuth_lfib_ctx);
    nettle_arcfour_crypt(&arcfour_ctx, sizeof(i), (uint8_t *)&i, (uint8_t *)&i);
    if (i) return i; /*(cond to avoid compiler warning for code after return)*/
  #endif
  #ifdef USE_MBEDTLS_CRYPTO
  #ifdef MBEDTLS_CTR_DRBG_C
    int i;
    if (0 == mbedtls_ctr_drbg_random(&ctr_drbg, (unsigned char *)&i, sizeof(i)))
        return i;
  #endif
  #endif
  #ifdef USE_NSS_CRYPTO
    if (SECSuccess == PK11_GenerateRandom((unsigned char *)&i, sizeof(i)))
        return i;
  #endif
  #ifdef HAVE_ARC4RANDOM_BUF
    return (int)arc4random();
  #elif defined(__COVERITY__)
    /* li_rand_pseudo() is not intended for cryptographic use */
    /* Coverity Scan ignores(?) annotation below,
     * so hide fallback paths from Coverity Scan */
    return (int)(time(NULL) ^ getpid());
  #elif defined(HAVE_SRANDOM)
    /* coverity[dont_call : FALSE] */
    return (int)random();
  #elif defined(HAVE_JRAND48)
    /*(FYI: jrand48() reentrant, but use of file-scoped static xsubi[] is not)*/
    /* coverity[dont_call : FALSE] */
    return (int)jrand48(xsubi);
  #else
    /* coverity[dont_call : FALSE] */
    return rand();
  #endif
}

void li_rand_pseudo_bytes (unsigned char *buf, int num)
{
  #ifdef USE_GNUTLS_CRYPTO
    if (0 == gnutls_rnd(GNUTLS_RND_NONCE, buf, (size_t)num)) return;
  #endif
    if (!li_rand_inited) li_rand_init();
  #ifdef USE_NSS_CRYPTO
    if (SECSuccess == PK11_GenerateRandom(buf, num)) return;
  #endif
  #ifdef USE_MBEDTLS_CRYPTO
  #ifdef MBEDTLS_CTR_DRBG_C
    if (0 == mbedtls_ctr_drbg_random(&ctr_drbg, buf, (size_t)num)) return;
  #endif
  #endif
  #ifdef USE_WOLFSSL_CRYPTO
    /* RAND_pseudo_bytes() in WolfSSL is equivalent to RAND_bytes() */
    if (0 == wc_RNG_GenerateBlock(&wolf_globalRNG, (byte *)buf, (word32)num))
        return;
  #endif
    for (int i = 0; i < num; ++i)
        buf[i] = li_rand_pseudo() & 0xFF;
}

int li_rand_bytes (unsigned char *buf, int num)
{
  #ifdef USE_GNUTLS_CRYPTO /* should use GNUTLS_RND_KEY for long-term keys */
    if (0 == gnutls_rnd(GNUTLS_RND_RANDOM, buf, (size_t)num)) return 1;
  #endif
  #ifdef USE_NSS_CRYPTO
    if (!li_rand_inited) li_rand_init();
    if (SECSuccess == PK11_GenerateRandom(buf, num)) return 1;
  #endif
  #ifdef USE_NETTLE_CRYPTO
  #if 0 /* not implemented: periodic nettle_yarrow256_update() and reseed */
    if (!nettle_yarrow256_is_seeded(&yarrow256_ctx)) {
        uint8_t seed_file[YARROW256_SEED_FILE_SIZE];
        if (1 == li_rand_device_bytes((unsigned char *)seed_file,
                                      (int)sizeof(seed_file))) {
            nettle_yarrow256_seed(&yarrow256_ctx, sizeof(seed_file), seed_file);
        }
    }
    if (nettle_yarrow256_is_seeded(&yarrow256_ctx)) {
        nettle_yarrow256_random(&yarrow256_ctx, (size_t)num, (uint8_t *)buf);
        return 1;
    }
  #endif
  #endif
  #ifdef USE_OPENSSL_CRYPTO
    int rc = RAND_bytes(buf, num);
    if (-1 != rc) {
        return rc;
    }
  #endif
  #ifdef USE_WOLFSSL_CRYPTO
    if (0 == wc_RNG_GenerateBlock(&wolf_globalRNG, (byte *)buf, (word32)num)) {
        return 1;
    }
  #endif
  #ifdef USE_MBEDTLS_CRYPTO
  #ifdef MBEDTLS_ENTROPY_C
    /*(each call <= MBEDTLS_ENTROPY_BLOCK_SIZE; could implement loop here)*/
    if (num <= MBEDTLS_ENTROPY_BLOCK_SIZE
        && 0 == mbedtls_entropy_func(&entropy, buf, (size_t)num)) {
        return 1;
    }
  #endif
  #endif
    if (1 == li_rand_device_bytes(buf, num)) {
        return 1;
    }
    else {
        /* NOTE: not cryptographically random !!! */
        li_rand_pseudo_bytes(buf, num);
        /*(openssl RAND_pseudo_bytes rc for non-cryptographically random data)*/
        return 0;
    }
}

void li_rand_cleanup (void)
{
  #ifdef USE_WOLFSSL_CRYPTO
    if (li_rand_inited) {
        wc_FreeRng(&wolf_globalRNG);
        wolfCrypt_Cleanup();
        li_rand_inited = 0;
    }
  #endif
  #ifdef USE_OPENSSL_CRYPTO
  #if OPENSSL_VERSION_NUMBER < 0x10100000L
    RAND_cleanup();
  #endif
  #endif
  #ifdef USE_MBEDTLS_CRYPTO
  #ifdef MBEDTLS_ENTROPY_C
  #ifdef MBEDTLS_CTR_DRBG_C
    mbedtls_ctr_drbg_free(&ctr_drbg);
  #endif
    mbedtls_entropy_free(&entropy);
  #endif
  #endif
    safe_memclear(xsubi, sizeof(xsubi));
}