1/*
2 * Crypto-quality random number functions
3 *
4 * Author: Harlan Stenn, 2014
5 *
6 * This file is Copyright (c) 2014 by Network Time Foundation.
7 * BSD terms apply: see the file COPYRIGHT in the distribution root for details.
8 */
9
10#include "config.h"
11#include <sys/types.h>
12#ifdef HAVE_UNISTD_H
13# include <unistd.h>
14#endif
15#include <stdio.h>
16
17#include <ntp_stdlib.h>
18#include <ntp_random.h>
19#include "safecast.h"
20
21#ifdef USE_OPENSSL_CRYPTO_RAND
22#include <openssl/err.h>
23#include <openssl/rand.h>
24
25int crypto_rand_init = 0;
26#elif !defined(HAVE_ARC4RANDOM_BUF)
27#include <event2/util.h>
28#endif
29
30int crypto_rand_ok = 0;
31
32/*
33 * As of late 2014, here's how we plan to provide cryptographic-quality
34 * random numbers:
35 *
36 * - If we are building with OpenSSL, use RAND_poll() and RAND_bytes().
37 * - Otherwise, use arc4random().
38 *
39 * Use of arc4random() can be forced using configure options
40 * --disable-openssl-random or --without-crypto.
41 *
42 * We can count on arc4random existing, thru the OS or thru libevent.
43 * The quality of arc4random depends on the implementor.
44 *
45 * RAND_poll() doesn't show up until XXX.  If it's not present, we
46 * need to either provide our own or use arc4random().
47 */
48
49 /*
50  * ntp_crypto_srandom:
51  *
52  * Initialize the random number generator, if needed by the underlying
53  * crypto random number generation mechanism.
54  */
55
56void
57ntp_crypto_srandom(
58	void
59)
60{
61#ifdef USE_OPENSSL_CRYPTO_RAND
62	if (!crypto_rand_init) {
63		if (RAND_poll())
64			crypto_rand_ok = 1;
65		crypto_rand_init = 1;
66	}
67#elif HAVE_ARC4RANDOM_BUF
68	/*
69	 * arc4random_buf has no error return and needs no seeding nor reseeding.
70	 */
71	crypto_rand_ok = 1;
72#else
73	/*
74	 * Explicitly init libevent secure RNG to make sure it seeds.
75	 * This is the only way we can tell if it can successfully get
76	 * entropy from the system.
77	 */
78	if (!evutil_secure_rng_init())
79		crypto_rand_ok = 1;
80#endif
81}
82
83
84/*
85 * ntp_crypto_random_buf:  Used by ntp-keygen
86 *
87 * Returns 0 on success, -1 on error.
88 */
89int
90ntp_crypto_random_buf(
91	void *buf,
92	size_t nbytes
93	)
94{
95	if (!crypto_rand_ok)
96		return -1;
97
98#if defined(USE_OPENSSL_CRYPTO_RAND)
99	if (1 != RAND_bytes(buf, size2int_chk(nbytes))) {
100		unsigned long err;
101		char *err_str;
102
103		err = ERR_get_error();
104		err_str = ERR_error_string(err, NULL);
105		msyslog(LOG_ERR, "RAND_bytes failed: %s", err_str);
106
107		return -1;
108	}
109#elif defined(HAVE_ARC4RANDOM_BUF)
110	arc4random_buf(buf, nbytes);
111#else
112	evutil_secure_rng_get_bytes(buf, nbytes);
113#endif
114	return 0;
115}
116