summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>2017-08-31 23:16:22 +0200
committerBen Kaduk <kaduk@mit.edu>2017-10-18 08:39:20 -0500
commitc16de9d8329d41a2433d0f273c080d9d06ad7a87 (patch)
tree3bdcf5e354efd8991aa1f7d83921655e81db726b
parentaf1d638730bdfad85a7fa8c3f157b2828eda7c1d (diff)
Fix reseeding issues of the public RAND_DRBG
Reseeding is handled very differently by the classic RAND_METHOD API and the new RAND_DRBG api. These differences led to some problems when the new RAND_DRBG was made the default OpenSSL RNG. In particular, RAND_add() did not work as expected anymore. These issues are discussed on the thread '[openssl-dev] Plea for a new public OpenSSL RNG API' and in Pull Request #4328. This commit fixes the mentioned issues, introducing the following changes: - Replace the fixed size RAND_BYTES_BUFFER by a new RAND_POOL API which facilitates collecting entropy by the get_entropy() callback. - Don't use RAND_poll()/RAND_add() for collecting entropy from the get_entropy() callback anymore. Instead, replace RAND_poll() by RAND_POOL_acquire_entropy(). - Add a new function rand_drbg_restart() which tries to get the DRBG in an instantiated state by all means, regardless of the current state (uninstantiated, error, ...) the DRBG is in. If the caller provides entropy or additional input, it will be used for reseeding. - Restore the original documented behaviour of RAND_add() and RAND_poll() (namely to reseed the DRBG immediately) by a new implementation based on rand_drbg_restart(). - Add automatic error recovery from temporary failures of the entropy source to RAND_DRBG_generate() using the rand_drbg_restart() function. Reviewed-by: Paul Dale <paul.dale@oracle.com> Reviewed-by: Kurt Roeckx <kurt@roeckx.be> Reviewed-by: Rich Salz <rsalz@openssl.org> Reviewed-by: Ben Kaduk <kaduk@mit.edu> (Merged from https://github.com/openssl/openssl/pull/4328)
-rw-r--r--crypto/err/openssl.txt13
-rw-r--r--crypto/include/internal/rand_int.h2
-rw-r--r--crypto/init.c2
-rw-r--r--crypto/rand/drbg_lib.c311
-rw-r--r--crypto/rand/drbg_rand.c4
-rw-r--r--crypto/rand/rand_err.c21
-rw-r--r--crypto/rand/rand_lcl.h87
-rw-r--r--crypto/rand/rand_lib.c520
-rw-r--r--crypto/rand/rand_unix.c127
-rw-r--r--crypto/rand/rand_vms.c13
-rw-r--r--crypto/rand/rand_win.c84
-rw-r--r--doc/man3/RAND_add.pod89
-rw-r--r--include/internal/rand.h45
-rw-r--r--include/openssl/ossl_typ.h1
-rw-r--r--include/openssl/rand.h1
-rw-r--r--include/openssl/randerr.h13
-rw-r--r--ssl/ssl_lib.c9
-rw-r--r--test/drbgtest.c2
-rw-r--r--util/libcrypto.num15
19 files changed, 1032 insertions, 327 deletions
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 67f708602b..1749a7037f 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -876,14 +876,21 @@ PKCS7_F_PKCS7_SIMPLE_SMIMECAP:119:PKCS7_simple_smimecap
PKCS7_F_PKCS7_VERIFY:117:PKCS7_verify
RAND_F_DRBG_BYTES:101:drbg_bytes
RAND_F_DRBG_GET_ENTROPY:105:drbg_get_entropy
+RAND_F_DRBG_SETUP:117:drbg_setup
RAND_F_GET_ENTROPY:106:get_entropy
RAND_F_RAND_BYTES:100:RAND_bytes
RAND_F_RAND_DRBG_GENERATE:107:RAND_DRBG_generate
RAND_F_RAND_DRBG_INSTANTIATE:108:RAND_DRBG_instantiate
RAND_F_RAND_DRBG_NEW:109:RAND_DRBG_new
RAND_F_RAND_DRBG_RESEED:110:RAND_DRBG_reseed
+RAND_F_RAND_DRBG_RESTART:102:rand_drbg_restart
RAND_F_RAND_DRBG_SET:104:RAND_DRBG_set
RAND_F_RAND_LOAD_FILE:111:RAND_load_file
+RAND_F_RAND_POOL_ADD:103:RAND_POOL_add
+RAND_F_RAND_POOL_ADD_BEGIN:113:RAND_POOL_add_begin
+RAND_F_RAND_POOL_ADD_END:114:RAND_POOL_add_end
+RAND_F_RAND_POOL_BYTES_NEEDED:115:RAND_POOL_bytes_needed
+RAND_F_RAND_POOL_NEW:116:RAND_POOL_new
RAND_F_RAND_WRITE_FILE:112:RAND_write_file
RSA_F_CHECK_PADDING_MD:140:check_padding_md
RSA_F_ENCODE_PKCS1:146:encode_pkcs1
@@ -2137,13 +2144,18 @@ PKCS7_R_WRONG_CONTENT_TYPE:113:wrong content type
PKCS7_R_WRONG_PKCS7_TYPE:114:wrong pkcs7 type
RAND_R_ADDITIONAL_INPUT_TOO_LONG:102:additional input too long
RAND_R_ALREADY_INSTANTIATED:103:already instantiated
+RAND_R_ARGUMENT_OUT_OF_RANGE:105:argument out of range
RAND_R_CANNOT_OPEN_FILE:121:Cannot open file
RAND_R_DRBG_NOT_INITIALISED:104:drbg not initialised
+RAND_R_ENTROPY_INPUT_TOO_LONG:106:entropy input too long
+RAND_R_ENTROPY_OUT_OF_RANGE:124:entropy out of range
+RAND_R_ERROR_ENTROPY_POOL_WAS_IGNORED:127:error entropy pool was ignored
RAND_R_ERROR_INITIALISING_DRBG:107:error initialising drbg
RAND_R_ERROR_INSTANTIATING_DRBG:108:error instantiating drbg
RAND_R_ERROR_RETRIEVING_ADDITIONAL_INPUT:109:error retrieving additional input
RAND_R_ERROR_RETRIEVING_ENTROPY:110:error retrieving entropy
RAND_R_ERROR_RETRIEVING_NONCE:111:error retrieving nonce
+RAND_R_FAILED_TO_CREATE_LOCK:126:failed to create lock
RAND_R_FUNC_NOT_IMPLEMENTED:101:Function not implemented
RAND_R_FWRITE_ERROR:123:Error writing file
RAND_R_GENERATE_ERROR:112:generate error
@@ -2153,6 +2165,7 @@ RAND_R_NOT_A_REGULAR_FILE:122:Not a regular file
RAND_R_NOT_INSTANTIATED:115:not instantiated
RAND_R_PERSONALISATION_STRING_TOO_LONG:116:personalisation string too long
RAND_R_PRNG_NOT_SEEDED:100:PRNG not seeded
+RAND_R_RANDOM_POOL_OVERFLOW:125:random pool overflow
RAND_R_REQUEST_TOO_LARGE_FOR_DRBG:117:request too large for drbg
RAND_R_RESEED_ERROR:118:reseed error
RAND_R_SELFTEST_FAILURE:119:selftest failure
diff --git a/crypto/include/internal/rand_int.h b/crypto/include/internal/rand_int.h
index d0999f28ad..fc1abd97bc 100644
--- a/crypto/include/internal/rand_int.h
+++ b/crypto/include/internal/rand_int.h
@@ -18,5 +18,5 @@
#include <openssl/rand.h>
void rand_cleanup_int(void);
-void rand_cleanup_drbg_int(void);
+void rand_drbg_cleanup_int(void);
void rand_fork(void);
diff --git a/crypto/init.c b/crypto/init.c
index 3eda1c987d..8daf4e31cf 100644
--- a/crypto/init.c
+++ b/crypto/init.c
@@ -503,7 +503,7 @@ void OPENSSL_cleanup(void)
* obj_cleanup_int() must be called last
*/
rand_cleanup_int();
- rand_cleanup_drbg_int();
+ rand_drbg_cleanup_int();
conf_modules_free_int();
#ifndef OPENSSL_NO_ENGINE
engine_cleanup_int();
diff --git a/crypto/rand/drbg_lib.c b/crypto/rand/drbg_lib.c
index b7f7e4c341..eef5e11cc5 100644
--- a/crypto/rand/drbg_lib.c
+++ b/crypto/rand/drbg_lib.c
@@ -18,6 +18,9 @@
static RAND_DRBG rand_drbg; /* The default global DRBG. */
static RAND_DRBG priv_drbg; /* The global private-key DRBG. */
+/* NIST SP 800-90A DRBG recommends the use of a personalization string. */
+static const char ossl_pers_string[] = "OpenSSL NIST SP 800-90A DRBG";
+
/*
* Support framework for NIST SP 800-90A DRBG, AES-CTR mode.
* The RAND_DRBG is OpenSSL's pointer to an instance of the DRBG.
@@ -30,7 +33,9 @@ static RAND_DRBG priv_drbg; /* The global private-key DRBG. */
* a much bigger deal than just re-setting an allocated resource.)
*/
-static CRYPTO_ONCE rand_init_drbg = CRYPTO_ONCE_STATIC_INIT;
+static CRYPTO_ONCE rand_drbg_init = CRYPTO_ONCE_STATIC_INIT;
+
+static int drbg_setup(RAND_DRBG *drbg, const char *name);
/*
* Set/initialize |drbg| to be of type |nid|, with optional |flags|.
@@ -76,15 +81,14 @@ RAND_DRBG *RAND_DRBG_new(int type, unsigned int flags, RAND_DRBG *parent)
RANDerr(RAND_F_RAND_DRBG_NEW, ERR_R_MALLOC_FAILURE);
goto err;
}
- drbg->size = RANDOMNESS_NEEDED;
drbg->fork_count = rand_fork_count;
drbg->parent = parent;
if (RAND_DRBG_set(drbg, type, flags) < 0)
goto err;
if (parent != NULL) {
- if (!RAND_DRBG_set_callbacks(drbg, drbg_entropy_from_parent,
- drbg_release_entropy,
+ if (!RAND_DRBG_set_callbacks(drbg, rand_drbg_get_entropy,
+ rand_drbg_cleanup_entropy,
NULL, NULL))
goto err;
}
@@ -101,8 +105,7 @@ err:
*/
void RAND_DRBG_free(RAND_DRBG *drbg)
{
- /* The global DRBG is free'd by rand_cleanup_drbg_int() */
- if (drbg == NULL || drbg == &rand_drbg)
+ if (drbg == NULL)
return;
ctr_uninstantiate(drbg);
@@ -136,7 +139,8 @@ int RAND_DRBG_instantiate(RAND_DRBG *drbg,
if (drbg->get_entropy != NULL)
entropylen = drbg->get_entropy(drbg, &entropy, drbg->strength,
drbg->min_entropylen, drbg->max_entropylen);
- if (entropylen < drbg->min_entropylen || entropylen > drbg->max_entropylen) {
+ if (entropylen < drbg->min_entropylen
+ || entropylen > drbg->max_entropylen) {
RANDerr(RAND_F_RAND_DRBG_INSTANTIATE, RAND_R_ERROR_RETRIEVING_ENTROPY);
goto end;
}
@@ -145,7 +149,8 @@ int RAND_DRBG_instantiate(RAND_DRBG *drbg,
noncelen = drbg->get_nonce(drbg, &nonce, drbg->strength / 2,
drbg->min_noncelen, drbg->max_noncelen);
if (noncelen < drbg->min_noncelen || noncelen > drbg->max_noncelen) {
- RANDerr(RAND_F_RAND_DRBG_INSTANTIATE, RAND_R_ERROR_RETRIEVING_NONCE);
+ RANDerr(RAND_F_RAND_DRBG_INSTANTIATE,
+ RAND_R_ERROR_RETRIEVING_NONCE);
goto end;
}
}
@@ -164,6 +169,15 @@ end:
drbg->cleanup_entropy(drbg, entropy, entropylen);
if (nonce != NULL && drbg->cleanup_nonce!= NULL )
drbg->cleanup_nonce(drbg, nonce, noncelen);
+ if (drbg->pool != NULL) {
+ if (drbg->state == DRBG_READY) {
+ RANDerr(RAND_F_RAND_DRBG_INSTANTIATE,
+ RAND_R_ERROR_ENTROPY_POOL_WAS_IGNORED);
+ drbg->state = DRBG_ERROR;
+ }
+ RAND_POOL_free(drbg->pool);
+ drbg->pool = NULL;
+ }
if (drbg->state == DRBG_READY)
return 1;
return 0;
@@ -182,7 +196,7 @@ int RAND_DRBG_uninstantiate(RAND_DRBG *drbg)
}
/*
- * Mix in the specified data to reseed |drbg|.
+ * Reseed |drbg|, mixing in the specified data
*/
int RAND_DRBG_reseed(RAND_DRBG *drbg,
const unsigned char *adin, size_t adinlen)
@@ -210,7 +224,8 @@ int RAND_DRBG_reseed(RAND_DRBG *drbg,
if (drbg->get_entropy != NULL)
entropylen = drbg->get_entropy(drbg, &entropy, drbg->strength,
drbg->min_entropylen, drbg->max_entropylen);
- if (entropylen < drbg->min_entropylen || entropylen > drbg->max_entropylen) {
+ if (entropylen < drbg->min_entropylen
+ || entropylen > drbg->max_entropylen) {
RANDerr(RAND_F_RAND_DRBG_RESEED, RAND_R_ERROR_RETRIEVING_ENTROPY);
goto end;
}
@@ -229,22 +244,132 @@ end:
}
/*
+ * Restart |drbg|, using the specified entropy or additional input
+ *
+ * Tries its best to get the drbg instantiated by all means,
+ * regardless of its current state.
+ *
+ * Optionally, a |buffer| of |len| random bytes can be passed,
+ * which is assumed to contain at least |entropy| bits of entropy.
+ *
+ * If |entropy| > 0, the buffer content is used as entropy input.
+ *
+ * If |entropy| == 0, the buffer content is used as additional input
+ *
+ * Returns 1 on success, 0 on failure.
+ *
+ * This function is used internally only.
+ */
+int rand_drbg_restart(RAND_DRBG *drbg,
+ const unsigned char *buffer, size_t len, size_t entropy)
+{
+ int reseeded = 0;
+ const unsigned char *adin = NULL;
+ size_t adinlen = 0;
+
+ if (drbg->pool != NULL) {
+ RANDerr(RAND_F_RAND_DRBG_RESTART, ERR_R_INTERNAL_ERROR);
+ RAND_POOL_free(drbg->pool);
+ drbg->pool = NULL;
+ }
+
+ if (buffer != NULL) {
+ if (entropy > 0) {
+ if (drbg->max_entropylen < len) {
+ RANDerr(RAND_F_RAND_DRBG_RESTART,
+ RAND_R_ENTROPY_INPUT_TOO_LONG);
+ return 0;
+ }
+
+ if (entropy > 8 * len) {
+ RANDerr(RAND_F_RAND_DRBG_RESTART, RAND_R_ENTROPY_OUT_OF_RANGE);
+ return 0;
+ }
+
+ /* will be picked up by the rand_drbg_get_entropy() callback */
+ drbg->pool = RAND_POOL_new(entropy, len, len);
+ if (drbg->pool == NULL)
+ return 0;
+
+ RAND_POOL_add(drbg->pool, buffer, len, entropy);
+ } else {
+ if (drbg->max_adinlen < len) {
+ RANDerr(RAND_F_RAND_DRBG_RESTART,
+ RAND_R_ADDITIONAL_INPUT_TOO_LONG);
+ return 0;
+ }
+ adin = buffer;
+ adinlen = len;
+ }
+ }
+
+ /* repair error state */
+ if (drbg->state == DRBG_ERROR)
+ RAND_DRBG_uninstantiate(drbg);
+
+ /* repair uninitialized state */
+ if (drbg->state == DRBG_UNINITIALISED) {
+ drbg_setup(drbg, NULL);
+ /* already reseeded. prevent second reseeding below */
+ reseeded = (drbg->state == DRBG_READY);
+ }
+
+ /* refresh current state if entropy or additional input has been provided */
+ if (drbg->state == DRBG_READY) {
+ if (adin != NULL) {
+ /*
+ * mix in additional input without reseeding
+ *
+ * Similar to RAND_DRBG_reseed(), but the provided additional
+ * data |adin| is mixed into the current state without pulling
+ * entropy from the trusted entropy source using get_entropy().
+ * This is not a reseeding in the strict sense of NIST SP 800-90A.
+ */
+ ctr_reseed(drbg, adin, adinlen, NULL, 0);
+ } else if (reseeded == 0) {
+ /* do a full reseeding if it has not been done yet above */
+ RAND_DRBG_reseed(drbg, NULL, 0);
+ }
+ }
+
+ /* check whether a given entropy pool was cleared properly during reseed */
+ if (drbg->pool != NULL) {
+ drbg->state = DRBG_ERROR;
+ RANDerr(RAND_F_RAND_DRBG_RESTART, ERR_R_INTERNAL_ERROR);
+ RAND_POOL_free(drbg->pool);
+ drbg->pool = NULL;
+ return 0;
+ }
+
+ return drbg->state == DRBG_READY;
+}
+
+/*
* Generate |outlen| bytes into the buffer at |out|. Reseed if we need
* to or if |prediction_resistance| is set. Additional input can be
* sent in |adin| and |adinlen|.
+ *
+ * Returns 1 on success, 0 on failure.
+ *
*/
int RAND_DRBG_generate(RAND_DRBG *drbg, unsigned char *out, size_t outlen,
int prediction_resistance,
const unsigned char *adin, size_t adinlen)
{
- if (drbg->state == DRBG_ERROR) {
- RANDerr(RAND_F_RAND_DRBG_GENERATE, RAND_R_IN_ERROR_STATE);
- return 0;
- }
- if (drbg->state == DRBG_UNINITIALISED) {
- RANDerr(RAND_F_RAND_DRBG_GENERATE, RAND_R_NOT_INSTANTIATED);
- return 0;
+ if (drbg->state != DRBG_READY) {
+ /* try to recover from previous errors */
+ rand_drbg_restart(drbg, NULL, 0, 0);
+
+ if (drbg->state == DRBG_ERROR) {
+ RANDerr(RAND_F_RAND_DRBG_GENERATE, RAND_R_IN_ERROR_STATE);
+ return 0;
+ }
+ if (drbg->state == DRBG_UNINITIALISED) {
+ RANDerr(RAND_F_RAND_DRBG_GENERATE, RAND_R_NOT_INSTANTIATED);
+ return 0;
+ }
}
+
if (outlen > drbg->max_request) {
RANDerr(RAND_F_RAND_DRBG_GENERATE, RAND_R_REQUEST_TOO_LARGE_FOR_DRBG);
return 0;
@@ -285,21 +410,55 @@ int RAND_DRBG_generate(RAND_DRBG *drbg, unsigned char *out, size_t outlen,
}
/*
- * Set the callbacks for entropy and nonce. We currently don't use
- * the nonce; that's mainly for the KATs
+ * Set the RAND_DRBG callbacks for obtaining entropy and nonce.
+ *
+ * In the following, the signature and the semantics of the
+ * get_entropy() and cleanup_entropy() callbacks are explained.
+ *
+ * GET_ENTROPY
+ *
+ * size_t get_entropy(RAND_DRBG *ctx,
+ * unsigned char **pout,
+ * int entropy,
+ * size_t min_len, size_t max_len);
+ *
+ * This is a request to allocate and fill a buffer of size
+ * |min_len| <= size <= |max_len| (in bytes) which contains
+ * at least |entropy| bits of randomness. The buffer's address is
+ * to be returned in |*pout| and the number of collected
+ * randomness bytes (which may be less than the allocated size
+ * of the buffer) as return value.
+ *
+ * If the callback fails to acquire at least |entropy| bits of
+ * randomness, it shall return a buffer length of 0.
+ *
+ * CLEANUP_ENTROPY
+ *
+ * void cleanup_entropy(RAND_DRBG *ctx,
+ * unsigned char *out, size_t outlen);
+ *
+ * A request to clear and free the buffer allocated by get_entropy().
+ * The values |out| and |outlen| are expected to be the random buffer's
+ * address and length, as returned by the get_entropy() callback.
+ *
+ * GET_NONCE, CLEANUP_NONCE
+ *
+ * Signature and semantics of the get_nonce() and cleanup_nonce()
+ * callbacks are analogous to get_entropy() and cleanup_entropy().
+ * Currently, the nonce is used only for the known answer tests.
*/
int RAND_DRBG_set_callbacks(RAND_DRBG *drbg,
- RAND_DRBG_get_entropy_fn cb_get_entropy,
- RAND_DRBG_cleanup_entropy_fn cb_cleanup_entropy,
- RAND_DRBG_get_nonce_fn cb_get_nonce,
- RAND_DRBG_cleanup_nonce_fn cb_cleanup_nonce)
+ RAND_DRBG_get_entropy_fn get_entropy,
+ RAND_DRBG_cleanup_entropy_fn cleanup_entropy,
+ RAND_DRBG_get_nonce_fn get_nonce,
+ RAND_DRBG_cleanup_nonce_fn cleanup_nonce)
{
if (drbg->state != DRBG_UNINITIALISED)
return 0;
- drbg->get_entropy = cb_get_entropy;
- drbg->cleanup_entropy = cb_cleanup_entropy;
- drbg->get_nonce = cb_get_nonce;
- drbg->cleanup_nonce = cb_cleanup_nonce;
+ drbg->get_entropy = get_entropy;
+ drbg->cleanup_entropy = cleanup_entropy;
+ drbg->get_nonce = get_nonce;
+ drbg->cleanup_nonce = cleanup_nonce;
return 1;
}
@@ -334,23 +493,40 @@ void *RAND_DRBG_get_ex_data(const RAND_DRBG *drbg, int idx)
*/
/*
- * Creates a global DRBG with default settings.
+ * Initializes the DRBG with default settings.
+ * For global DRBGs a global lock is created with the given name
* Returns 1 on success, 0 on failure
*/
-static int setup_drbg(RAND_DRBG *drbg, const char *name)
+static int drbg_setup(RAND_DRBG *drbg, const char *name)
{
int ret = 1;
- drbg->lock = CRYPTO_THREAD_glock_new(name);
- ret &= drbg->lock != NULL;
- drbg->size = RANDOMNESS_NEEDED;
- drbg->secure = CRYPTO_secure_malloc_initialized();
- /* If you change these parameters, see RANDOMNESS_NEEDED */
+ if (name != NULL) {
+ if (drbg->lock != NULL) {
+ RANDerr(RAND_F_DRBG_SETUP, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ drbg->lock = CRYPTO_THREAD_glock_new(name);
+ if (drbg->lock == NULL) {
+ RANDerr(RAND_F_DRBG_SETUP, RAND_R_FAILED_TO_CREATE_LOCK);
+ return 0;
+ }
+ }
+
ret &= RAND_DRBG_set(drbg,
- NID_aes_128_ctr, RAND_DRBG_FLAG_CTR_USE_DF) == 1;
- ret &= RAND_DRBG_set_callbacks(drbg, drbg_entropy_from_system,
- drbg_release_entropy, NULL, NULL) == 1;
- ret &= RAND_DRBG_instantiate(drbg, NULL, 0) == 1;
+ RAND_DRBG_NID, RAND_DRBG_FLAG_CTR_USE_DF) == 1;
+ ret &= RAND_DRBG_set_callbacks(drbg, rand_drbg_get_entropy,
+ rand_drbg_cleanup_entropy, NULL, NULL) == 1;
+ /*
+ * Ignore instantiation error so support just-in-time instantiation.
+ *
+ * The state of the drbg will be checked in RAND_DRBG_generate() and
+ * an automatic recovery is attempted.
+ */
+ RAND_DRBG_instantiate(drbg,
+ (const unsigned char *) ossl_pers_string,
+ sizeof(ossl_pers_string) - 1);
return ret;
}
@@ -358,30 +534,31 @@ static int setup_drbg(RAND_DRBG *drbg, const char *name)
* Initialize the global DRBGs on first use.
* Returns 1 on success, 0 on failure.
*/
-DEFINE_RUN_ONCE_STATIC(do_rand_init_drbg)
+DEFINE_RUN_ONCE_STATIC(do_rand_drbg_init)
{
int ret = 1;
- ret &= setup_drbg(&rand_drbg, "rand_drbg");
- ret &= setup_drbg(&priv_drbg, "priv_drbg");
+ ret &= drbg_setup(&rand_drbg, "rand_drbg");
+ ret &= drbg_setup(&priv_drbg, "priv_drbg");
return ret;
}
-/* Clean up a DRBG and free it */
-static void free_drbg(RAND_DRBG *drbg)
+/* Cleans up the given global DRBG */
+static void drbg_cleanup(RAND_DRBG *drbg)
{
CRYPTO_THREAD_lock_free(drbg->lock);
RAND_DRBG_uninstantiate(drbg);
}
/* Clean up the global DRBGs before exit */
-void rand_cleanup_drbg_int(void)
+void rand_drbg_cleanup_int(void)
{
- free_drbg(&rand_drbg);
- free_drbg(&priv_drbg);
+ drbg_cleanup(&rand_drbg);
+ drbg_cleanup(&priv_drbg);
}
+/* Implements the default OpenSSL RAND_bytes() method */
static int drbg_bytes(unsigned char *out, int count)
{
int ret = 0;
@@ -410,34 +587,44 @@ err:
return ret;
}
+/* Implements the default OpenSSL RAND_add() method */
static int drbg_add(const void *buf, int num, double randomness)
{
- unsigned char *in = (unsigned char *)buf;
- unsigned char *out, *end;
+ int ret = 0;
+ RAND_DRBG *drbg = RAND_DRBG_get0_global();
- CRYPTO_THREAD_write_lock(rand_bytes.lock);
- out = &rand_bytes.buff[rand_bytes.curr];
- end = &rand_bytes.buff[rand_bytes.size];
+ if (drbg == NULL)
+ return 0;
- /* Copy whatever fits into the end of the buffer. */
- for ( ; --num >= 0 && out < end; rand_bytes.curr++)
- *out++ = *in++;
+ if (num < 0 || randomness < 0.0)
+ return 0;
- /* XOR any the leftover. */
- while (num > 0) {
- for (out = rand_bytes.buff; --num >= 0 && out < end; )
- *out++ ^= *in++;
+ if (randomness > (double)drbg->max_entropylen) {
+ /*
+ * The purpose of this check is to bound |randomness| by a
+ * relatively small value in order to prevent an integer
+ * overflow when multiplying by 8 in the rand_drbg_restart()
+ * call below.
+ */
+ return 0;
}
- CRYPTO_THREAD_unlock(rand_bytes.lock);
- return 1;
+ CRYPTO_THREAD_write_lock(drbg->lock);
+ ret = rand_drbg_restart(drbg, buf,
+ (size_t)(unsigned int)num,
+ (size_t)(8*randomness));
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
}
+/* Implements the default OpenSSL RAND_seed() method */
static int drbg_seed(const void *buf, int num)
{
return drbg_add(buf, num, num);
}
+/* Implements the default OpenSSL RAND_status() method */
static int drbg_status(void)
{
int ret;
@@ -458,7 +645,7 @@ static int drbg_status(void)
*/
RAND_DRBG *RAND_DRBG_get0_global(void)
{
- if (!RUN_ONCE(&rand_init_drbg, do_rand_init_drbg))
+ if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init))
return NULL;
return &rand_drbg;
@@ -470,7 +657,7 @@ RAND_DRBG *RAND_DRBG_get0_global(void)
*/
RAND_DRBG *RAND_DRBG_get0_priv_global(void)
{
- if (!RUN_ONCE(&rand_init_drbg, do_rand_init_drbg))
+ if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init))
return NULL;
return &priv_drbg;
diff --git a/crypto/rand/drbg_rand.c b/crypto/rand/drbg_rand.c
index 83f1ad876f..f45ef1424c 100644
--- a/crypto/rand/drbg_rand.c
+++ b/crypto/rand/drbg_rand.c
@@ -341,9 +341,9 @@ int ctr_init(RAND_DRBG *drbg)
AES_set_encrypt_key(df_key, drbg->strength, &ctr->df_ks);
drbg->min_entropylen = ctr->keylen;
- drbg->max_entropylen = DRBG_MAX_LENGTH;
+ drbg->max_entropylen = DRBG_MINMAX_FACTOR * drbg->min_entropylen;
drbg->min_noncelen = drbg->min_entropylen / 2;
- drbg->max_noncelen = DRBG_MAX_LENGTH;
+ drbg->max_noncelen = DRBG_MINMAX_FACTOR * drbg->min_noncelen;
drbg->max_perslen = DRBG_MAX_LENGTH;
drbg->max_adinlen = DRBG_MAX_LENGTH;
} else {
diff --git a/crypto/rand/rand_err.c b/crypto/rand/rand_err.c
index 707f010e4c..dc6140c075 100644
--- a/crypto/rand/rand_err.c
+++ b/crypto/rand/rand_err.c
@@ -16,6 +16,7 @@
static const ERR_STRING_DATA RAND_str_functs[] = {
{ERR_PACK(ERR_LIB_RAND, RAND_F_DRBG_BYTES, 0), "drbg_bytes"},
{ERR_PACK(ERR_LIB_RAND, RAND_F_DRBG_GET_ENTROPY, 0), "drbg_get_entropy"},
+ {ERR_PACK(ERR_LIB_RAND, RAND_F_DRBG_SETUP, 0), "drbg_setup"},
{ERR_PACK(ERR_LIB_RAND, RAND_F_GET_ENTROPY, 0), "get_entropy"},
{ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_BYTES, 0), "RAND_bytes"},
{ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_GENERATE, 0),
@@ -24,8 +25,16 @@ static const ERR_STRING_DATA RAND_str_functs[] = {
"RAND_DRBG_instantiate"},
{ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_NEW, 0), "RAND_DRBG_new"},
{ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_RESEED, 0), "RAND_DRBG_reseed"},
+ {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_RESTART, 0), "rand_drbg_restart"},
{ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_SET, 0), "RAND_DRBG_set"},
{ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_LOAD_FILE, 0), "RAND_load_file"},
+ {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_ADD, 0), "RAND_POOL_add"},
+ {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_ADD_BEGIN, 0),
+ "RAND_POOL_add_begin"},
+ {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_ADD_END, 0), "RAND_POOL_add_end"},
+ {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_BYTES_NEEDED, 0),
+ "RAND_POOL_bytes_needed"},
+ {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_POOL_NEW, 0), "RAND_POOL_new"},
{ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_WRITE_FILE, 0), "RAND_write_file"},
{0, NULL}
};
@@ -35,9 +44,17 @@ static const ERR_STRING_DATA RAND_str_reasons[] = {
"additional input too long"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_ALREADY_INSTANTIATED),
"already instantiated"},
+ {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_ARGUMENT_OUT_OF_RANGE),
+ "argument out of range"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_CANNOT_OPEN_FILE), "Cannot open file"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_DRBG_NOT_INITIALISED),
"drbg not initialised"},
+ {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_ENTROPY_INPUT_TOO_LONG),
+ "entropy input too long"},
+ {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_ENTROPY_OUT_OF_RANGE),
+ "entropy out of range"},
+ {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_ERROR_ENTROPY_POOL_WAS_IGNORED),
+ "error entropy pool was ignored"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_ERROR_INITIALISING_DRBG),
"error initialising drbg"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_ERROR_INSTANTIATING_DRBG),
@@ -48,6 +65,8 @@ static const ERR_STRING_DATA RAND_str_reasons[] = {
"error retrieving entropy"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_ERROR_RETRIEVING_NONCE),
"error retrieving nonce"},
+ {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_FAILED_TO_CREATE_LOCK),
+ "failed to create lock"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_FUNC_NOT_IMPLEMENTED),
"Function not implemented"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_FWRITE_ERROR), "Error writing file"},
@@ -60,6 +79,8 @@ static const ERR_STRING_DATA RAND_str_reasons[] = {
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_PERSONALISATION_STRING_TOO_LONG),
"personalisation string too long"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_PRNG_NOT_SEEDED), "PRNG not seeded"},
+ {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_RANDOM_POOL_OVERFLOW),
+ "random pool overflow"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_REQUEST_TOO_LARGE_FOR_DRBG),
"request too large for drbg"},
{ERR_PACK(ERR_LIB_RAND, 0, RAND_R_RESEED_ERROR), "reseed error"},
diff --git a/crypto/rand/rand_lcl.h b/crypto/rand/rand_lcl.h
index 498b7e687e..10a6f00a2d 100644
--- a/crypto/rand/rand_lcl.h
+++ b/crypto/rand/rand_lcl.h
@@ -17,28 +17,24 @@
# include <openssl/ec.h>
# include "internal/rand.h"
-/*
- * Amount of randomness (in bytes) we want for initial seeding.
- * This is based on the fact that we use AES-128 as the CRBG, and
- * that we use the derivation function. If either of those changes,
- * (see rand_init() in rand_lib.c), change this.
- */
-# define RANDOMNESS_NEEDED 16
-
/* How many times to read the TSC as a randomness source. */
# define TSC_READ_COUNT 4
-/* Maximum amount of randomness to hold in RAND_BYTES_BUFFER. */
-# define MAX_RANDOMNESS_HELD (4 * RANDOMNESS_NEEDED)
-
/* Maximum count allowed in reseeding */
# define MAX_RESEED (1 << 24)
-/* How often we call RAND_poll() in drbg_entropy_from_system */
-# define RAND_POLL_RETRIES 8
+/* Max size of additional input and personalization string. */
+# define DRBG_MAX_LENGTH 4096
-/* Max size of entropy, addin, etc. Larger than any reasonable value */
-# define DRBG_MAX_LENGTH 0x7ffffff0
+/*
+ * The quotient between max_{entropy,nonce}len and min_{entropy,nonce}len
+ *
+ * The current factor is large enough that the RAND_POOL can store a
+ * random input which has a lousy entropy rate of 0.0625 bits per byte.
+ * This input will be sent through the derivation function which 'compresses'
+ * the low quality input into a high quality output.
+ */
+# define DRBG_MINMAX_FACTOR 128
/* DRBG status values */
@@ -51,22 +47,6 @@ typedef enum drbg_status_e {
/*
- * A buffer of random bytes to be fed as "entropy" into the DRBG. RAND_add()
- * adds data to the buffer, and the drbg_entropy_from_system() pulls data from
- * the buffer. We have a separate data structure because of the way the
- * API is defined; otherwise we'd run into deadlocks (RAND_bytes ->
- * RAND_DRBG_generate* -> drbg_entropy_from_system -> RAND_poll -> RAND_add ->
- * drbg_add*; the functions with an asterisk lock).
- */
-typedef struct rand_bytes_buffer_st {
- CRYPTO_RWLOCK *lock;
- unsigned char *buff;
- size_t size;
- size_t curr;
- int secure;
-} RAND_BYTES_BUFFER;
-
-/*
* The state of a DRBG AES-CTR.
*/
typedef struct rand_drbg_ctr_st {
@@ -94,30 +74,34 @@ struct rand_drbg_st {
int nid; /* the underlying algorithm */
int fork_count;
unsigned short flags; /* various external flags */
- char secure;
+
/*
- * This is a fixed-size buffer, but we malloc to make it a little
- * harder to find; a classic security/performance trade-off.
+ * The random pool is used by RAND_add()/drbg_add() to attach random
+ * data to the global drbg, such that the rand_drbg_get_entropy() callback
+ * can pull it during instantiation and reseeding. This is necessary to
+ * reconcile the different philosophies of the RAND and the RAND_DRBG
+ * with respect to how randomness is added to the RNG during reseeding
+ * (see PR #4328).
*/
- int size;
+ RAND_POOL *pool;
- /*
+ /*
* The following parameters are setup by the per-type "init" function.
*
* Currently the only type is CTR_DRBG, its init function is ctr_init().
*
- * The parameters are closely related to the ones described in
+ * The parameters are closely related to the ones described in
* section '10.2.1 CTR_DRBG' of [NIST SP 800-90Ar1], with one
* crucial difference: In the NIST standard, all counts are given
- * in bits, whereas in OpenSSL entropy counts are given in bits
+ * in bits, whereas in OpenSSL entropy counts are given in bits
* and buffer lengths are given in bytes.
- *
+ *
* Since this difference has lead to some confusion in the past,
* (see [GitHub Issue #2443], formerly [rt.openssl.org #4055])
- * the 'len' suffix has been added to all buffer sizes for
+ * the 'len' suffix has been added to all buffer sizes for
* clarification.
*/
-
+
int strength;
size_t max_request;
size_t min_entropylen, max_entropylen;
@@ -143,23 +127,24 @@ struct rand_drbg_st {
/* The global RAND method, and the global buffer and DRBG instance. */
extern RAND_METHOD rand_meth;
-extern RAND_BYTES_BUFFER rand_bytes;
/* How often we've forked (only incremented in child). */
extern int rand_fork_count;
/* Hardware-based seeding functions. */
-void rand_read_tsc(RAND_poll_cb rand_add, void *arg);
-int rand_read_cpu(RAND_poll_cb rand_add, void *arg);
+size_t rand_acquire_entropy_from_tsc(RAND_POOL *pool);
+size_t rand_acquire_entropy_from_cpu(RAND_POOL *po