#include <openssl/engine.h>
#include "internal/thread_once.h"
#include "rand_lcl.h"
-#ifdef OPENSSL_SYS_UNIX
-# include <sys/types.h>
-# include <unistd.h>
-# include <sys/time.h>
-#endif
#include "e_os.h"
-/* Macro to convert two thirty two bit values into a sixty four bit one */
-#define TWO32TO64(a, b) ((((uint64_t)(a)) << 32) + (b))
-
-/*
- * Check for the existence and support of POSIX timers. The standard
- * says that the _POSIX_TIMERS macro will have a positive value if they
- * are available.
- *
- * However, we want an additional constraint: that the timer support does
- * not require an extra library dependency. Early versions of glibc
- * require -lrt to be specified on the link line to access the timers,
- * so this needs to be checked for.
- *
- * It is worse because some libraries define __GLIBC__ but don't
- * support the version testing macro (e.g. uClibc). This means
- * an extra check is needed.
- *
- * The final condition is:
- * "have posix timers and either not glibc or glibc without -lrt"
- *
- * The nested #if sequences are required to avoid using a parameterised
- * macro that might be undefined.
- */
-#undef OSSL_POSIX_TIMER_OKAY
-#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
-# if defined(__GLIBC__)
-# if defined(__GLIBC_PREREQ)
-# if __GLIBC_PREREQ(2, 17)
-# define OSSL_POSIX_TIMER_OKAY
-# endif
-# endif
-# else
-# define OSSL_POSIX_TIMER_OKAY
-# endif
-#endif
-
#ifndef OPENSSL_NO_ENGINE
/* non-NULL if default_RAND_meth is ENGINE-provided */
static ENGINE *funct_ref;
int rand_fork_count;
+static CRYPTO_RWLOCK *rand_nonce_lock;
+static int rand_nonce_count;
+
+static int rand_inited = 0;
+
#ifdef OPENSSL_RAND_SEED_RDTSC
/*
* IMPORTANT NOTE: It is not currently possible to use this code
size_t bytes_needed;
unsigned char *buffer;
- bytes_needed = rand_pool_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
if (bytes_needed > 0) {
buffer = rand_pool_add_begin(pool, bytes_needed);
return 0;
}
- pool = rand_pool_new(entropy, min_len, max_len);
- if (pool == NULL)
- return 0;
-
- if (drbg->pool) {
- rand_pool_add(pool,
- rand_pool_buffer(drbg->pool),
- rand_pool_length(drbg->pool),
- rand_pool_entropy(drbg->pool));
- rand_pool_free(drbg->pool);
- drbg->pool = NULL;
+ if (drbg->seed_pool != NULL) {
+ pool = drbg->seed_pool;
+ pool->entropy_requested = entropy;
+ } else {
+ pool = rand_pool_new(entropy, min_len, max_len);
+ if (pool == NULL)
+ return 0;
}
if (drbg->parent) {
- size_t bytes_needed = rand_pool_bytes_needed(pool, 8);
+ size_t bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
unsigned char *buffer = rand_pool_add_begin(pool, bytes_needed);
if (buffer != NULL) {
if (RAND_DRBG_generate(drbg->parent,
buffer, bytes_needed,
prediction_resistance,
- (unsigned char *)drbg, sizeof(*drbg)) != 0)
+ NULL, 0) != 0)
bytes = bytes_needed;
+ drbg->reseed_next_counter
+ = tsan_load(&drbg->parent->reseed_prop_counter);
rand_drbg_unlock(drbg->parent);
rand_pool_add_end(pool, bytes, 8 * bytes);
}
err:
- rand_pool_free(pool);
+ if (drbg->seed_pool == NULL)
+ rand_pool_free(pool);
return ret;
}
/*
- * Find a suitable source of time. Start with the highest resolution source
- * and work down to the slower ones. This is added as additional data and
- * isn't counted as randomness, so any result is acceptable.
+ * Implements the cleanup_entropy() callback (see RAND_DRBG_set_callbacks())
*
- * Returns 0 when we weren't able to find any time source
*/
-static uint64_t get_timer_bits(void)
+void rand_drbg_cleanup_entropy(RAND_DRBG *drbg,
+ unsigned char *out, size_t outlen)
{
- uint64_t res = OPENSSL_rdtsc();
-
- if (res != 0)
- return res;
-#if defined(_WIN32)
- {
- LARGE_INTEGER t;
- FILETIME ft;
-
- if (QueryPerformanceCounter(&t) != 0)
- return t.QuadPart;
- GetSystemTimeAsFileTime(&ft);
- return TWO32TO64(ft.dwHighDateTime, ft.dwLowDateTime);
- }
-#elif defined(__sun) || defined(__hpux)
- return gethrtime();
-#elif defined(_AIX)
- {
- timebasestruct_t t;
-
- read_wall_time(&t, TIMEBASE_SZ);
- return TWO32TO64(t.tb_high, t.tb_low);
- }
-#else
-
-# if defined(OSSL_POSIX_TIMER_OKAY)
- {
- struct timespec ts;
- clockid_t cid;
-
-# ifdef CLOCK_BOOTTIME
- cid = CLOCK_BOOTTIME;
-# elif defined(_POSIX_MONOTONIC_CLOCK)
- cid = CLOCK_MONOTONIC;
-# else
- cid = CLOCK_REALTIME;
-# endif
-
- if (clock_gettime(cid, &ts) == 0)
- return TWO32TO64(ts.tv_sec, ts.tv_nsec);
- }
-# endif
-# if defined(__unix__) \
- || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
- {
- struct timeval tv;
-
- if (gettimeofday(&tv, NULL) == 0)
- return TWO32TO64(tv.tv_sec, tv.tv_usec);
- }
-# endif
- {
- time_t t = time(NULL);
- if (t == (time_t)-1)
- return 0;
- return t;
- }
-#endif
+ if (drbg->seed_pool == NULL)
+ OPENSSL_secure_clear_free(out, outlen);
}
+
/*
- * Generate additional data that can be used for the drbg. The data does
- * not need to contain entropy, but it's useful if it contains at least
- * some bits that are unpredictable.
- *
- * Returns 0 on failure.
+ * Implements the get_nonce() callback (see RAND_DRBG_set_callbacks())
*
- * On success it allocates a buffer at |*pout| and returns the length of
- * the data. The buffer should get freed using OPENSSL_secure_clear_free().
*/
-size_t rand_drbg_get_additional_data(unsigned char **pout, size_t max_len)
+size_t rand_drbg_get_nonce(RAND_DRBG *drbg,
+ unsigned char **pout,
+ int entropy, size_t min_len, size_t max_len)
{
+ size_t ret = 0;
RAND_POOL *pool;
- CRYPTO_THREAD_ID thread_id;
- size_t len;
-#ifdef OPENSSL_SYS_UNIX
- pid_t pid;
-#elif defined(OPENSSL_SYS_WIN32)
- DWORD pid;
-#endif
- uint64_t tbits;
- pool = rand_pool_new(0, 0, max_len);
+ struct {
+ void * instance;
+ int count;
+ } data = { 0 };
+
+ pool = rand_pool_new(0, min_len, max_len);
if (pool == NULL)
return 0;
-#ifdef OPENSSL_SYS_UNIX
- pid = getpid();
- rand_pool_add(pool, (unsigned char *)&pid, sizeof(pid), 0);
-#elif defined(OPENSSL_SYS_WIN32)
- pid = GetCurrentProcessId();
- rand_pool_add(pool, (unsigned char *)&pid, sizeof(pid), 0);
-#endif
+ if (rand_pool_add_nonce_data(pool) == 0)
+ goto err;
- thread_id = CRYPTO_THREAD_get_current_id();
- if (thread_id != 0)
- rand_pool_add(pool, (unsigned char *)&thread_id, sizeof(thread_id), 0);
+ data.instance = drbg;
+ CRYPTO_atomic_add(&rand_nonce_count, 1, &data.count, rand_nonce_lock);
- tbits = get_timer_bits();
- if (tbits != 0)
- rand_pool_add(pool, (unsigned char *)&tbits, sizeof(tbits), 0);
+ if (rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0) == 0)
+ goto err;
- /* TODO: Use RDSEED? */
+ ret = rand_pool_length(pool);
+ *pout = rand_pool_detach(pool);
- len = rand_pool_length(pool);
- if (len != 0)
- *pout = rand_pool_detach(pool);
+ err:
rand_pool_free(pool);
- return len;
+ return ret;
}
/*
- * Implements the cleanup_entropy() callback (see RAND_DRBG_set_callbacks())
+ * Implements the cleanup_nonce() callback (see RAND_DRBG_set_callbacks())
*
*/
-void rand_drbg_cleanup_entropy(RAND_DRBG *drbg,
- unsigned char *out, size_t outlen)
+void rand_drbg_cleanup_nonce(RAND_DRBG *drbg,
+ unsigned char *out, size_t outlen)
{
OPENSSL_secure_clear_free(out, outlen);
}
-void rand_fork()
+/*
+ * Generate additional data that can be used for the drbg. The data does
+ * not need to contain entropy, but it's useful if it contains at least
+ * some bits that are unpredictable.
+ *
+ * Returns 0 on failure.
+ *
+ * On success it allocates a buffer at |*pout| and returns the length of
+ * the data. The buffer should get freed using OPENSSL_secure_clear_free().
+ */
+size_t rand_drbg_get_additional_data(RAND_POOL *pool, unsigned char **pout)
+{
+ size_t ret = 0;
+
+ if (rand_pool_add_additional_data(pool) == 0)
+ goto err;
+
+ ret = rand_pool_length(pool);
+ *pout = rand_pool_detach(pool);
+
+ err:
+ return ret;
+}
+
+void rand_drbg_cleanup_additional_data(RAND_POOL *pool, unsigned char *out)
+{
+ rand_pool_reattach(pool, out);
+}
+
+void rand_fork(void)
{
rand_fork_count++;
}
DEFINE_RUN_ONCE_STATIC(do_rand_init)
{
- int ret = 1;
-
#ifndef OPENSSL_NO_ENGINE
rand_engine_lock = CRYPTO_THREAD_lock_new();
- ret &= rand_engine_lock != NULL;
+ if (rand_engine_lock == NULL)
+ return 0;
#endif
+
rand_meth_lock = CRYPTO_THREAD_lock_new();
- ret &= rand_meth_lock != NULL;
+ if (rand_meth_lock == NULL)
+ goto err1;
- return ret;
+ rand_nonce_lock = CRYPTO_THREAD_lock_new();
+ if (rand_nonce_lock == NULL)
+ goto err2;
+
+ if (!rand_pool_init())
+ goto err3;
+
+ rand_inited = 1;
+ return 1;
+
+err3:
+ CRYPTO_THREAD_lock_free(rand_nonce_lock);
+ rand_nonce_lock = NULL;
+err2:
+ CRYPTO_THREAD_lock_free(rand_meth_lock);
+ rand_meth_lock = NULL;
+err1:
+#ifndef OPENSSL_NO_ENGINE
+ CRYPTO_THREAD_lock_free(rand_engine_lock);
+ rand_engine_lock = NULL;
+#endif
+ return 0;
}
void rand_cleanup_int(void)
{
const RAND_METHOD *meth = default_RAND_meth;
+ if (!rand_inited)
+ return;
+
if (meth != NULL && meth->cleanup != NULL)
meth->cleanup();
RAND_set_rand_method(NULL);
+ rand_pool_cleanup();
#ifndef OPENSSL_NO_ENGINE
CRYPTO_THREAD_lock_free(rand_engine_lock);
+ rand_engine_lock = NULL;
#endif
CRYPTO_THREAD_lock_free(rand_meth_lock);
+ rand_meth_lock = NULL;
+ CRYPTO_THREAD_lock_free(rand_nonce_lock);
+ rand_nonce_lock = NULL;
+ rand_inited = 0;
+}
+
+/*
+ * RAND_close_seed_files() ensures that any seed file decriptors are
+ * closed after use.
+ */
+void RAND_keep_random_devices_open(int keep)
+{
+ if (RUN_ONCE(&rand_init, do_rand_init))
+ rand_pool_keep_random_devices_open(keep);
}
/*
/* fill random pool and seed the current legacy RNG */
pool = rand_pool_new(RAND_DRBG_STRENGTH,
RAND_DRBG_STRENGTH / 8,
- DRBG_MINMAX_FACTOR * (RAND_DRBG_STRENGTH / 8));
+ RAND_POOL_MAX_LENGTH);
if (pool == NULL)
return 0;
* Allocate memory and initialize a new random pool
*/
-RAND_POOL *rand_pool_new(int entropy, size_t min_len, size_t max_len)
+RAND_POOL *rand_pool_new(int entropy_requested, size_t min_len, size_t max_len)
{
RAND_POOL *pool = OPENSSL_zalloc(sizeof(*pool));
if (pool == NULL) {
RANDerr(RAND_F_RAND_POOL_NEW, ERR_R_MALLOC_FAILURE);
- goto err;
+ return NULL;
}
pool->min_len = min_len;
- pool->max_len = max_len;
+ pool->max_len = (max_len > RAND_POOL_MAX_LENGTH) ?
+ RAND_POOL_MAX_LENGTH : max_len;
pool->buffer = OPENSSL_secure_zalloc(pool->max_len);
if (pool->buffer == NULL) {
goto err;
}
- pool->requested_entropy = entropy;
+ pool->entropy_requested = entropy_requested;
return pool;
return NULL;
}
+/*
+ * Attach new random pool to the given buffer
+ *
+ * This function is intended to be used only for feeding random data
+ * provided by RAND_add() and RAND_seed() into the <master> DRBG.
+ */
+RAND_POOL *rand_pool_attach(const unsigned char *buffer, size_t len,
+ size_t entropy)
+{
+ RAND_POOL *pool = OPENSSL_zalloc(sizeof(*pool));
+
+ if (pool == NULL) {
+ RANDerr(RAND_F_RAND_POOL_ATTACH, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+
+ /*
+ * The const needs to be cast away, but attached buffers will not be
+ * modified (in contrary to allocated buffers which are zeroed and
+ * freed in the end).
+ */
+ pool->buffer = (unsigned char *) buffer;
+ pool->len = len;
+
+ pool->attached = 1;
+
+ pool->min_len = pool->max_len = pool->len;
+ pool->entropy = entropy;
+
+ return pool;
+}
+
/*
* Free |pool|, securely erasing its buffer.
*/
if (pool == NULL)
return;
- OPENSSL_secure_clear_free(pool->buffer, pool->max_len);
+ /*
+ * Although it would be advisable from a cryptographical viewpoint,
+ * we are not allowed to clear attached buffers, since they are passed
+ * to rand_pool_attach() as `const unsigned char*`.
+ * (see corresponding comment in rand_pool_attach()).
+ */
+ if (!pool->attached)
+ OPENSSL_secure_clear_free(pool->buffer, pool->max_len);
OPENSSL_free(pool);
}
/*
* Detach the |pool| buffer and return it to the caller.
* It's the responsibility of the caller to free the buffer
- * using OPENSSL_secure_clear_free().
+ * using OPENSSL_secure_clear_free() or to re-attach it
+ * again to the pool using rand_pool_reattach().
*/
unsigned char *rand_pool_detach(RAND_POOL *pool)
{
unsigned char *ret = pool->buffer;
pool->buffer = NULL;
+ pool->entropy = 0;
return ret;
}
+/*
+ * Re-attach the |pool| buffer. It is only allowed to pass
+ * the |buffer| which was previously detached from the same pool.
+ */
+void rand_pool_reattach(RAND_POOL *pool, unsigned char *buffer)
+{
+ pool->buffer = buffer;
+ OPENSSL_cleanse(pool->buffer, pool->len);
+ pool->len = 0;
+}
/*
- * If every byte of the input contains |entropy_per_bytes| bits of entropy,
- * how many bytes does one need to obtain at least |bits| bits of entropy?
+ * If |entropy_factor| bits contain 1 bit of entropy, how many bytes does one
+ * need to obtain at least |bits| bits of entropy?
*/
-#define ENTROPY_TO_BYTES(bits, entropy_per_bytes) \
- (((bits) + ((entropy_per_bytes) - 1))/(entropy_per_bytes))
+#define ENTROPY_TO_BYTES(bits, entropy_factor) \
+ (((bits) * (entropy_factor) + 7) / 8)
/*
*/
size_t rand_pool_entropy_available(RAND_POOL *pool)
{
- if (pool->entropy < pool->requested_entropy)
+ if (pool->entropy < pool->entropy_requested)
return 0;
if (pool->len < pool->min_len)
size_t rand_pool_entropy_needed(RAND_POOL *pool)
{
- if (pool->entropy < pool->requested_entropy)
- return pool->requested_entropy - pool->entropy;
+ if (pool->entropy < pool->entropy_requested)
+ return pool->entropy_requested - pool->entropy;
return 0;
}
/*
* Returns the number of bytes needed to fill the pool, assuming
- * the input has 'entropy_per_byte' entropy bits per byte.
+ * the input has 1 / |entropy_factor| entropy bits per data bit.
* In case of an error, 0 is returned.
*/
-size_t rand_pool_bytes_needed(RAND_POOL *pool, unsigned int entropy_per_byte)
+size_t rand_pool_bytes_needed(RAND_POOL *pool, unsigned int entropy_factor)
{
size_t bytes_needed;
size_t entropy_needed = rand_pool_entropy_needed(pool);
- if (entropy_per_byte < 1 || entropy_per_byte > 8) {
+ if (entropy_factor < 1) {
RANDerr(RAND_F_RAND_POOL_BYTES_NEEDED, RAND_R_ARGUMENT_OUT_OF_RANGE);
return 0;
}
- bytes_needed = ENTROPY_TO_BYTES(entropy_needed, entropy_per_byte);
+ bytes_needed = ENTROPY_TO_BYTES(entropy_needed, entropy_factor);
if (bytes_needed > pool->max_len - pool->len) {
/* not enough space left */
return 0;
}
+ if (pool->buffer == NULL) {
+ RANDerr(RAND_F_RAND_POOL_ADD, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
if (len > 0) {
memcpy(pool->buffer + pool->len, buffer, len);
pool->len += len;
return NULL;
}
+ if (pool->buffer == NULL) {
+ RANDerr(RAND_F_RAND_POOL_ADD_BEGIN, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
return pool->buffer + pool->len;
}