aboutsummaryrefslogtreecommitdiffstats
path: root/crypto/rand
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/rand')
-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
8 files changed, 864 insertions, 303 deletions
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 *pool);
/* DRBG entropy callbacks. */
-void drbg_release_entropy(RAND_DRBG *drbg, unsigned char *out, size_t outlen);
-size_t drbg_entropy_from_parent(RAND_DRBG *drbg,
- unsigned char **pout,
- int entropy, size_t min_len, size_t max_len);
-size_t drbg_entropy_from_system(RAND_DRBG *drbg,
- unsigned char **pout,
- int entropy, size_t min_len, size_t max_len);
+size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
+ unsigned char **pout,
+ int entropy, size_t min_len, size_t max_len);
+void rand_drbg_cleanup_entropy(RAND_DRBG *drbg,
+ unsigned char *out, size_t outlen);
+
+/* DRBG helpers */
+int rand_drbg_restart(RAND_DRBG *drbg,
+ const unsigned char *buffer, size_t len, size_t entropy);
/* DRBG functions implementing AES-CTR */
int ctr_init(RAND_DRBG *drbg);
diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c
index 2f2ab6a86d..6f8deca1f9 100644
--- a/crypto/rand/rand_lib.c
+++ b/crypto/rand/rand_lib.c
@@ -24,7 +24,7 @@ static CRYPTO_RWLOCK *rand_engine_lock;
static CRYPTO_RWLOCK *rand_meth_lock;
static const RAND_METHOD *default_RAND_meth;
static CRYPTO_ONCE rand_init = CRYPTO_ONCE_STATIC_INIT;
-RAND_BYTES_BUFFER rand_bytes;
+
int rand_fork_count;
#ifdef OPENSSL_RAND_SEED_RDTSC
@@ -37,12 +37,15 @@ int rand_fork_count;
# error "RDTSC enabled? Should not be possible!"
/*
+ * Acquire entropy from high-speed clock
+ *
* Since we get some randomness from the low-order bits of the
- * high-speec clock, it can help. But don't return a status since
- * it's not sufficient to indicate whether or not the seeding was
- * done.
+ * high-speed clock, it can help.
+ *
+ * Returns the total entropy count, if it exceeds the requested
+ * entropy count. Otherwise, returns an entropy count of 0.
*/
-void rand_read_tsc(RAND_poll_cb rand_add, void *arg)
+size_t rand_acquire_entropy_from_tsc(RAND_POOL *pool)
{
unsigned char c;
int i;
@@ -50,126 +53,141 @@ void rand_read_tsc(RAND_poll_cb rand_add, void *arg)
if ((OPENSSL_ia32cap_P[0] & (1 << 4)) != 0) {
for (i = 0; i < TSC_READ_COUNT; i++) {
c = (unsigned char)(OPENSSL_rdtsc() & 0xFF);
- rand_add(arg, &c, 1, 0.5);
+ RAND_POOL_add(pool, &c, 1, 4);
}
}
+ return RAND_POOL_entropy_available(pool);
}
#endif
#ifdef OPENSSL_RAND_SEED_RDCPU
-size_t OPENSSL_ia32_rdseed_bytes(char *buf, size_t len);
-size_t OPENSSL_ia32_rdrand_bytes(char *buf, size_t len);
+size_t OPENSSL_ia32_rdseed_bytes(unsigned char *buf, size_t len);
+size_t OPENSSL_ia32_rdrand_bytes(unsigned char *buf, size_t len);
extern unsigned int OPENSSL_ia32cap_P[];
-int rand_read_cpu(RAND_poll_cb rand_add, void *arg)
+/*
+ * Acquire entropy using Intel-specific cpu instructions
+ *
+ * Uses the RDSEED instruction if available, otherwise uses
+ * RDRAND if available.
+ *
+ * For the differences between RDSEED and RDRAND, and why RDSEED
+ * is the preferred choice, see https://goo.gl/oK3KcN
+ *
+ * Returns the total entropy count, if it exceeds the requested
+ * entropy count. Otherwise, returns an entropy count of 0.
+ */
+size_t rand_acquire_entropy_from_cpu(RAND_POOL *pool)
{
- char buff[RANDOMNESS_NEEDED];
-
- /* If RDSEED is available, use that. */
- if ((OPENSSL_ia32cap_P[2] & (1 << 18)) != 0) {
- if (OPENSSL_ia32_rdseed_bytes(buff, sizeof(buff)) == sizeof(buff)) {
- rand_add(arg, buff, (int)sizeof(buff), sizeof(buff));
- return 1;
- }
- }
-
- /* Second choice is RDRAND. */
- if ((OPENSSL_ia32cap_P[1] & (1 << (62 - 32))) != 0) {
- if (OPENSSL_ia32_rdrand_bytes(buff, sizeof(buff)) == sizeof(buff)) {
- rand_add(arg, buff, (int)sizeof(buff), sizeof(buff));
- return 1;
+ size_t bytes_needed;
+ unsigned char *buffer;
+
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ if (bytes_needed > 0) {
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+
+ if (buffer != NULL) {
+
+ /* If RDSEED is available, use that. */
+ if ((OPENSSL_ia32cap_P[2] & (1 << 18)) != 0) {
+ if (OPENSSL_ia32_rdseed_bytes(buffer, bytes_needed)
+ == bytes_needed)
+ return RAND_POOL_add_end(pool,
+ bytes_needed,
+ 8 * bytes_needed);
+ }
+
+ /* Second choice is RDRAND. */
+ if ((OPENSSL_ia32cap_P[1] & (1 << (62 - 32))) != 0) {
+ if (OPENSSL_ia32_rdrand_bytes(buffer, bytes_needed)
+ == bytes_needed)
+ return RAND_POOL_add_end(pool,
+ bytes_needed,
+ 8 * bytes_needed);
+ }
+
+ return RAND_POOL_add_end(pool, 0, 0);
}
}
- return 0;
+ return RAND_POOL_entropy_available(pool);
}
#endif
/*
- * DRBG has two sets of callbacks; we only discuss the "entropy" one
- * here. When the DRBG needs additional randomness bits (called entropy
- * in the NIST document), it calls the get_entropy callback which fills in
- * a pointer and returns the number of bytes. When the DRBG is finished with
- * the buffer, it calls the cleanup_entropy callback, with the value of
- * the buffer that the get_entropy callback filled in.
+ * Implements the get_entropy() callback (see RAND_DRBG_set_callbacks())
+ *
+ * If the DRBG has a parent, then the required amount of entropy input
+ * is fetched using the parent's RAND_DRBG_generate().
*
- * Get entropy from the system, via RAND_poll if needed. The |entropy|
- * is the bits of randomness required, and is expected to fit into a buffer
- * of |min_len|..|max__len| size. We assume we're getting high-quality
- * randomness from the system, and that |min_len| bytes will do.
+ * Otherwise, the entropy is polled from the system entropy sources
+ * using RAND_POOL_acquire_entropy().
+ *
+ * If a random pool has been added to the DRBG using RAND_add(), then
+ * its entropy will be used up first.
*/
-size_t drbg_entropy_from_system(RAND_DRBG *drbg,
- unsigned char **pout,
- int entropy, size_t min_len, size_t max_len)
+size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
+ unsigned char **pout,
+ int entropy, size_t min_len, size_t max_len)
{
- int i;
- unsigned char *randomness;
+ size_t ret = 0;
+ size_t entropy_available = 0;
+ RAND_POOL *pool = RAND_POOL_new(entropy, min_len, max_len);
- if (min_len > (size_t)drbg->size) {
- /* Should not happen. See comment near RANDOMNESS_NEEDED. */
- min_len = drbg->size;
+ 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;
}
- randomness = drbg->secure ? OPENSSL_secure_malloc(drbg->size)
- : OPENSSL_malloc(drbg->size);
+ if (drbg->parent) {
+ size_t bytes_needed = RAND_POOL_bytes_needed(pool, 8);
+ unsigned char *buffer = RAND_POOL_add_begin(pool, bytes_needed);
- /* If we don't have enough, try to get more. */
- CRYPTO_THREAD_write_lock(rand_bytes.lock);
- for (i = RAND_POLL_RETRIES; rand_bytes.curr < min_len && --i >= 0; ) {
- CRYPTO_THREAD_unlock(rand_bytes.lock);
- RAND_poll();
- CRYPTO_THREAD_write_lock(rand_bytes.lock);
- }
+ if (buffer != NULL) {
+ size_t bytes = 0;
- /* Get desired amount, but no more than we have. */
- if (min_len > rand_bytes.curr)
- min_len = rand_bytes.curr;
- if (min_len != 0) {
- memcpy(randomness, rand_bytes.buff, min_len);
- /* Update amount left and shift it down. */
- rand_bytes.curr -= min_len;
- if (rand_bytes.curr != 0)
- memmove(rand_bytes.buff, &rand_bytes.buff[min_len], rand_bytes.curr);
- }
- CRYPTO_THREAD_unlock(rand_bytes.lock);
- *pout = randomness;
- return min_len;
-}
+ /* Get entropy from parent, include our state as additional input */
+ if (RAND_DRBG_generate(drbg->parent,
+ buffer, bytes_needed,
+ 0,
+ (unsigned char *)drbg, sizeof(*drbg)) != 0)
+ bytes = bytes_needed;
-size_t drbg_entropy_from_parent(RAND_DRBG *drbg,
- unsigned char **pout,
- int entropy, size_t min_len, size_t max_len)
-{
- int st;
- unsigned char *randomness;
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
+ }
- if (min_len > (size_t)drbg->size) {
- /* Should not happen. See comment near RANDOMNESS_NEEDED. */
- min_len = drbg->size;
+ } else {
+ /* Get entropy by polling system entropy sources. */
+ entropy_available = RAND_POOL_acquire_entropy(pool);
}
- randomness = drbg->secure ? OPENSSL_secure_malloc(drbg->size)
- : OPENSSL_malloc(drbg->size);
-
- /* Get random from parent, include our state as additional input. */
- st = RAND_DRBG_generate(drbg->parent, randomness, min_len, 0,
- (unsigned char *)drbg, sizeof(*drbg));
- if (st == 0) {
- drbg_release_entropy(drbg, randomness, min_len);
- return 0;
+ if (entropy_available > 0) {
+ ret = RAND_POOL_length(pool);
+ *pout = RAND_POOL_detach(pool);
}
- *pout = randomness;
- return min_len;
+
+ RAND_POOL_free(pool);
+ return ret;
}
-void drbg_release_entropy(RAND_DRBG *drbg, unsigned char *out, size_t outlen)
+
+/*
+ * Implements the cleanup_entropy() callback (see RAND_DRBG_set_callbacks())
+ *
+ */
+void rand_drbg_cleanup_entropy(RAND_DRBG *drbg,
+ unsigned char *out, size_t outlen)
{
- if (drbg->secure)
- OPENSSL_secure_clear_free(out, outlen);
- else
- OPENSSL_clear_free(out, outlen);
+ OPENSSL_secure_clear_free(out, outlen);
}
void rand_fork()
@@ -188,15 +206,6 @@ DEFINE_RUN_ONCE_STATIC(do_rand_init)
rand_meth_lock = CRYPTO_THREAD_glock_new("rand_meth");
ret &= rand_meth_lock != NULL;
- rand_bytes.lock = CRYPTO_THREAD_glock_new("rand_bytes");
- ret &= rand_bytes.lock != NULL;
- rand_bytes.curr = 0;
- rand_bytes.size = MAX_RANDOMNESS_HELD;
- rand_bytes.secure = CRYPTO_secure_malloc_initialized();
- rand_bytes.buff = rand_bytes.secure
- ? OPENSSL_secure_malloc(rand_bytes.size)
- : OPENSSL_malloc(rand_bytes.size);
- ret &= rand_bytes.buff != NULL;
return ret;
}
@@ -211,25 +220,314 @@ void rand_cleanup_int(void)
CRYPTO_THREAD_lock_free(rand_engine_lock);
#endif
CRYPTO_THREAD_lock_free(rand_meth_lock);
- CRYPTO_THREAD_lock_free(rand_bytes.lock);
- if (rand_bytes.secure)
- OPENSSL_secure_clear_free(rand_bytes.buff, rand_bytes.size);
- else
- OPENSSL_clear_free(rand_bytes.buff, rand_bytes.size);
}
/*
- * RAND_poll_ex() gets a function pointer to call when it has random bytes.
- * RAND_poll() sets the function pointer to be a wrapper that calls RAND_add().
+ * RAND_poll() reseeds the default RNG using random input
+ *
+ * The random input is obtained from polling various entropy
+ * sources which depend on the operating system and are
+ * configurable via the --with-rand-seed configure option.
*/
-static void call_rand_add(void* arg, const void *buf, int num, double r)
+int RAND_poll(void)
{
- RAND_add(buf, num, r);
+ int ret = 0;
+
+ RAND_POOL *pool = NULL;
+
+ const RAND_METHOD *meth = RAND_get_rand_method();
+
+ if (meth == RAND_OpenSSL()) {
+ /* fill random pool and seed the default DRBG */
+ RAND_DRBG *drbg = RAND_DRBG_get0_global();
+
+ if (drbg == NULL)
+ return 0;
+
+ CRYPTO_THREAD_write_lock(drbg->lock);
+ ret = rand_drbg_restart(drbg, NULL, 0, 0);
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+
+ } else {
+ /* 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));
+ if (pool == NULL)
+ return 0;
+
+ if (RAND_POOL_acquire_entropy(pool) == 0)
+ goto err;
+
+ if (meth->add == NULL
+ || meth->add(RAND_POOL_buffer(pool),
+ RAND_POOL_length(pool),
+ (RAND_POOL_entropy(pool) / 8.0)) == 0)
+ goto err;
+
+ ret = 1;
+ }
+
+err:
+ RAND_POOL_free(pool);
+ return ret;
}
-int RAND_poll(void)
+/*
+ * The 'random pool' acts as a dumb container for collecting random
+ * input from various entropy sources. The pool has no knowledge about
+ * whether its randomness is fed into a legacy RAND_METHOD via RAND_add()
+ * or into a new style RAND_DRBG. It is the callers duty to 1) initialize the
+ * random pool, 2) pass it to the polling callbacks, 3) seed the RNG, and
+ * 4) cleanup the random pool again.
+ *
+ * The random pool contains no locking mechanism because its scope and
+ * lifetime is intended to be restricted to a single stack frame.
+ */
+struct rand_pool_st {
+ unsigned char *buffer; /* points to the beginning of the random pool */
+ size_t len; /* current number of random bytes contained in the pool */
+
+ size_t min_len; /* minimum number of random bytes requested */
+ size_t max_len; /* maximum number of random bytes (allocated buffer size) */
+ size_t entropy; /* current entropy count in bits */
+ size_t requested_entropy; /* requested entropy count in bits */
+};
+
+/*
+ * 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 *pool = OPENSSL_zalloc(sizeof(*pool));
+
+ if (pool == NULL) {
+ RANDerr(RAND_F_RAND_POOL_NEW, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ pool->min_len = min_len;
+ pool->max_len = max_len;
+
+ pool->buffer = OPENSSL_secure_zalloc(pool->max_len);
+ if (pool->buffer == NULL) {
+ RANDerr(RAND_F_RAND_POOL_NEW, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ pool->requested_entropy = entropy;
+
+ return pool;
+
+err:
+ OPENSSL_free(pool);
+ return NULL;
+}
+
+/*
+ * Free |pool|, securely erasing its buffer.
+ */
+void RAND_POOL_free(RAND_POOL *pool)
{
- return RAND_poll_ex(call_rand_add, NULL);
+ if (pool == NULL)
+ return;
+
+ OPENSSL_secure_clear_free(pool->buffer, pool->max_len);
+ OPENSSL_free(pool);
+}
+
+/*
+ * Return the |pool|'s buffer to the caller (readonly).
+ */
+const unsigned char *RAND_POOL_buffer(RAND_POOL *pool)
+{
+ return pool->buffer;
+}
+
+/*
+ * Return the |pool|'s entropy to the caller.
+ */
+size_t RAND_POOL_entropy(RAND_POOL *pool)
+{
+ return pool->entropy;
+}
+
+/*
+ * Return the |pool|'s buffer length to the caller.
+ */
+size_t RAND_POOL_length(RAND_POOL *pool)
+{
+ return pool->len;
+}
+
+/*
+ * 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().
+ */
+unsigned char *RAND_POOL_detach(RAND_POOL *pool)
+{
+ unsigned char *ret = pool->buffer;
+ pool->buffer = NULL;
+ return ret;
+}
+
+
+/*
+ * 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?
+ */
+#define ENTROPY_TO_BYTES(bits, entropy_per_bytes) \
+ (((bits) + ((entropy_per_bytes) - 1))/(entropy_per_bytes))
+
+
+/*
+ * Checks whether the |pool|'s entropy is available to the caller.
+ * This is the case when entropy count and buffer length are high enough.
+ * Returns
+ *
+ * |entropy| if the entropy count and buffer size is large enough
+ * 0 otherwise
+ */
+size_t RAND_POOL_entropy_available(RAND_POOL *pool)
+{
+ if (pool->entropy < pool->requested_entropy)
+ return 0;
+
+ if (pool->len < pool->min_len)
+ return 0;
+
+ return pool->entropy;
+}
+
+/*
+ * Returns the (remaining) amount of entropy needed to fill
+ * the random pool.
+ */
+
+size_t RAND_POOL_entropy_needed(RAND_POOL *pool)
+{
+ if (pool->entropy < pool->requested_entropy)
+ return pool->requested_entropy - 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.
+ * In case of an error, 0 is returned.
+ */
+
+size_t RAND_POOL_bytes_needed(RAND_POOL *pool, unsigned int entropy_per_byte)
+{
+ size_t bytes_needed;
+ size_t entropy_needed = RAND_POOL_entropy_needed(pool);
+
+ if (entropy_per_byte < 1 || entropy_per_byte > 8) {
+ 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);
+
+ if (bytes_needed > pool->max_len - pool->len) {
+ /* not enough space left */
+ RANDerr(RAND_F_RAND_POOL_BYTES_NEEDED, RAND_R_RANDOM_POOL_OVERFLOW);
+ return 0;
+ }
+
+ if (pool->len < pool->min_len &&
+ bytes_needed < pool->min_len - pool->len)
+ /* to meet the min_len requirement */
+ bytes_needed = pool->min_len - pool->len;
+
+ return bytes_needed;
+}
+
+/* Returns the remaining number of bytes available */
+size_t RAND_POOL_bytes_remaining(RAND_POOL *pool)
+{
+ return pool->max_len - pool->len;
+}
+
+/*
+ * Add random bytes to the random pool.
+ *
+ * It is expected that the |buffer| contains |len| bytes of
+ * random input which contains at least |entropy| bits of
+ * randomness.
+ *
+ * Return available amount of entropy after this operation.
+ * (see RAND_POOL_entropy_available(pool))
+ */
+size_t RAND_POOL_add(RAND_POOL *pool,
+ const unsigned char *buffer, size_t len, size_t entropy)
+{
+ if (len > pool->max_len - pool->len) {
+ RANDerr(RAND_F_RAND_POOL_ADD, RAND_R_ENTROPY_INPUT_TOO_LONG);
+ return 0;
+ }
+
+ if (len > 0) {
+ memcpy(pool->buffer + pool->len, buffer, len);
+ pool->len += len;
+ pool->entropy += entropy;
+ }
+
+ return RAND_POOL_entropy_available(pool);
+}
+
+/*
+ * Start to add random bytes to the random pool in-place.
+ *
+ * Reserves the next |len| bytes for adding random bytes in-place
+ * and returns a pointer to the buffer.
+ * The caller is allowed to copy up to |len| bytes into the buffer.
+ * If |len| == 0 this is considered a no-op and a NULL pointer
+ * is returned without producing an error message.
+ *
+ * After updating the buffer, RAND_POOL_add_end() needs to be called
+ * to finish the udpate operation (see next comment).
+ */
+unsigned char *RAND_POOL_add_begin(RAND_POOL *pool, size_t len)
+{
+ if (len == 0)
+ return NULL;
+
+ if (len > pool->max_len - pool->len) {
+ RANDerr(RAND_F_RAND_POOL_ADD_BEGIN, RAND_R_RANDOM_POOL_OVERFLOW);
+ return NULL;
+ }
+
+ return pool->buffer + pool->len;
+}
+
+/*
+ * Finish to add random bytes to the random pool in-place.
+ *
+ * Finishes an in-place update of the random pool started by
+ * RAND_POOL_add_begin() (see previous comment).
+ * It is expected that |len| bytes of random input have been added
+ * to the buffer which contain at least |entropy| bits of randomness.
+ * It is allowed to add less bytes than originally reserved.
+ */
+size_t RAND_POOL_add_end(RAND_POOL *pool, size_t len, size_t entropy)
+{
+ if (len > pool->max_len - pool->len) {
+ RANDerr(RAND_F_RAND_POOL_ADD_END, RAND_R_RANDOM_POOL_OVERFLOW);
+ return 0;
+ }
+
+ if (len > 0) {
+ pool->len += len;
+ pool->entropy += entropy;
+ }
+
+ return RAND_POOL_entropy_available(pool);
}
int RAND_set_rand_method(const RAND_METHOD *meth)
diff --git a/crypto/rand/rand_unix.c b/crypto/rand/rand_unix.c
index 08ea55fbb9..f5a59cb28a 100644
--- a/crypto/rand/rand_unix.c
+++ b/crypto/rand/rand_unix.c
@@ -14,12 +14,18 @@
#include "rand_lcl.h"
#include <stdio.h>
-#if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI))
+#ifdef OPENSSL_RAND_SEED_GETRANDOM
+# include <linux/random.h>
+#endif
-# if (defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)) && \
+#if (defined(OPENSSL_SYS_VXWORKS) || defined(OPENSSL_SYS_UEFI)) && \
!defined(OPENSSL_RAND_SEED_NONE)
-# error "UEFI and VXWorks only support seeding NONE"
-# endif
+# error "UEFI and VXWorks only support seeding NONE"
+#endif
+
+#if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) \
+ || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) \
+ || defined(OPENSSL_SYS_UEFI))
# if defined(OPENSSL_SYS_VOS)
@@ -46,16 +52,16 @@
* would be far more predictable. This should only be used for legacy
* platforms.
*
- * As a precaution, we generate four times the required amount of seed
- * data.
+ * As a precaution, we assume only 2 bits of entropy per byte.
*/
-int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
+size_t RAND_POOL_acquire_entropy(RAND_POOL *pool)
{
short int code;
gid_t curr_gid;
pid_t curr_pid;
uid_t curr_uid;
int i, k;
+ size_t bytes_needed;
struct timespec ts;
unsigned char v;
# ifdef OPENSSL_SYS_VOS_HPPA
@@ -71,13 +77,15 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
* different processes.
*/
curr_gid = getgid();
- rand_add(arg, &curr_gid, sizeof curr_gid, 0);
+ RAND_POOL_add(pool, &curr_gid, sizeof(curr_gid), 0);
curr_pid = getpid();
- rand_add(arg, &curr_pid, sizeof curr_pid, 0);
+ RAND_POOL_add(pool, &curr_pid, sizeof(curr_pid), 0);
curr_uid = getuid();
- rand_add(arg, &curr_uid, sizeof curr_uid, 0);
+ RAND_POOL_add(pool, &curr_uid, sizeof(curr_uid), 0);
- for (i = 0; i < (RANDOMNESS_NEEDED * 4); i++) {
+ bytes_needed = RAND_POOL_bytes_needed(pool, 2 /*entropy_per_byte*/);
+
+ for (i = 0; i < bytes_needed; i++) {
/*
* burn some cpu; hope for interrupts, cache collisions, bus
* interference, etc.
@@ -98,9 +106,9 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
/* Get wall clock time, take 8 bits. */
clock_gettime(CLOCK_REALTIME, &ts);
v = (unsigned char)(ts.tv_nsec & 0xFF);
- rand_add(arg, &v, sizeof v, 1);
+ RAND_POOL_add(pool, arg, &v, sizeof(v) , 2);
}
- return 1;
+ return RAND_POOL_entropy_available(pool);
}
# else
@@ -127,27 +135,44 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
# endif
/*
- * Try the various seeding methods in turn, exit when succesful.
+ * Try the various seeding methods in turn, exit when successful.
+ *
+ * TODO(DRBG): If more than one entropy source is available, is it
+ * preferable to stop as soon as enough entropy has been collected
+ * (as favored by @rsalz) or should one rather be defensive and add
+ * more entropy than requested and/or from different sources?
+ *
+ * Currently, the user can select multiple entropy sources in the
+ * configure step, yet in practice only the first available source
+ * will be used. A more flexible solution has been requested, but
+ * currently it is not clear how this can be achieved without
+ * overengineering the problem. There are many parameters which
+ * could be taken into account when selecting the order and amount
+ * of input from the different entropy sources (trust, quality,
+ * possibility of blocking).
*/
-int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
+size_t RAND_POOL_acquire_entropy(RAND_POOL *pool)
{
# ifdef OPENSSL_RAND_SEED_NONE
- return 0;
+ return RAND_POOL_entropy_available(pool);
# else
- int ok = 1;
- char temp[RANDOMNESS_NEEDED];
-# define TEMPSIZE (int)sizeof(temp)
+ size_t bytes_needed;
+ size_t entropy_available = 0;
+ unsigned char *buffer;
# ifdef OPENSSL_RAND_SEED_GETRANDOM
- {
- int i = getrandom(temp, TEMPSIZE, 0);
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
- if (i >= 0) {
- rand_add(arg, temp, i, i);
- if (i == TEMPSIZE)
- goto done;
- }
+ if (getrandom(buffer, bytes_needed, 0) == (int)bytes_needed)
+ bytes = bytes_needed;
+
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
}
+ if (entropy_available > 0)
+ return entropy_available;
# endif
# if defined(OPENSSL_RAND_SEED_LIBRANDOM)
@@ -157,7 +182,8 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
# endif
# ifdef OPENSSL_RAND_SEED_DEVRANDOM
- {
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ if (bytes_needed > 0) {
static const char *paths[] = { DEVRANDOM, NULL };
FILE *fp;
int i;
@@ -166,44 +192,59 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
if ((fp = fopen(paths[i], "rb")) == NULL)
continue;
setbuf(fp, NULL);
- if (fread(temp, 1, TEMPSIZE, fp) == TEMPSIZE) {
- rand_add(arg, temp, TEMPSIZE, TEMPSIZE);
- fclose(fp);
- goto done;
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ if (fread(buffer, 1, bytes_needed, fp) == bytes_needed)
+ bytes = bytes_needed;
+
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
}
fclose(fp);
+ if (entropy_available > 0)
+ return entropy_available;
+
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
}
}
# endif
# ifdef OPENSSL_RAND_SEED_RDTSC
- rand_read_tsc(rand_add, arg);
+ entropy_available = rand_acquire_entropy_from_tsc(pool);
+ if (entropy_available > 0)
+ return entropy_available;
# endif
# ifdef OPENSSL_RAND_SEED_RDCPU
- if (rand_read_cpu(rand_add, arg))
- goto done;
+ entropy_available = rand_acquire_entropy_from_cpu(pool);
+ if (entropy_available > 0)
+ return entropy_available;
# endif
# ifdef OPENSSL_RAND_SEED_EGD
- {
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ if (bytes_needed > 0) {
static const char *paths[] = { DEVRANDOM_EGD, NULL };
int i;
for (i = 0; paths[i] != NULL; i++) {
- if (RAND_query_egd_bytes(paths[i], temp, TEMPSIZE) == TEMPSIZE) {
- rand_add(arg, temp, TEMPSIZE, TEMPSIZE);
- goto done;
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ int num = RAND_query_egd_bytes(paths[i],
+ buffer, (int)bytes_needed);
+ if (num == (int)bytes_needed)
+ bytes = bytes_needed;
+
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
}
+ if (entropy_available > 0)
+ return entropy_available;
}
}
# endif
- ok = 0;
-
-done:
- OPENSSL_cleanse(temp, TEMPSIZE);
- return ok;
+ return RAND_POOL_entropy_available(pool);
# endif
}
# endif
diff --git a/crypto/rand/rand_vms.c b/crypto/rand/rand_vms.c
index 773373d1a6..4ec4b35bd4 100644
--- a/crypto/rand/rand_vms.c
+++ b/crypto/rand/rand_vms.c
@@ -54,7 +54,7 @@ static struct items_data_st {
{0, 0}
};
-int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
+size_t RAND_POOL_acquire_entropy(RAND_POOL *pool)
{
/* determine the number of items in the JPI array */
struct items_data_st item_entry;
@@ -112,9 +112,14 @@ int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
total_length += (tmp_length - 1);
- /* size of seed is total_length*4 bytes (64bytes) */
- rand_add(arg, (PTR_T)data_buffer, total_length * 4, total_length * 2);
- return 1;
+ /*
+ * Size of seed is total_length*4 bytes (64bytes). The original assumption
+ * was that it contains 4 bits of entropy per byte. This makes a total
+ * amount of total_length*16 bits (256bits).
+ */
+ return RAND_POOL_add(pool,
+ (PTR_T)data_buffer, total_length * 4,
+ total_length * 16);
}
#endif
diff --git a/crypto/rand/rand_win.c b/crypto/rand/rand_win.c
index 8637ca4185..9eff319bc8 100644
--- a/crypto/rand/rand_win.c
+++ b/crypto/rand/rand_win.c
@@ -10,7 +10,6 @@
#include "internal/cryptlib.h"
#include <openssl/rand.h>
#include "rand_lcl.h"
-
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
# ifndef OPENSSL_RAND_SEED_OS
@@ -39,55 +38,80 @@
# define INTEL_DEF_PROV L"Intel Hardware Cryptographic Service Provider"
# endif
-int RAND_poll_ex(RAND_poll_cb rand_add, void *arg)
+size_t RAND_POOL_acquire_entropy(RAND_POOL *pool)
{
# ifndef USE_BCRYPTGENRANDOM
HCRYPTPROV hProvider;
- int ok = 0;
# endif
- BYTE buf[RANDOMNESS_NEEDED];
+ unsigned char *buffer;
+ size_t bytes_needed;
+ size_t entropy_available = 0;
+
# ifdef OPENSSL_RAND_SEED_RDTSC
- rand_read_tsc(cb, arg);
+ entropy_available = rand_acquire_entropy_from_tsc(pool);
+ if (entropy_available > 0)
+ return entropy_available;
# endif
+
# ifdef OPENSSL_RAND_SEED_RDCPU
- if (rand_read_cpu(cb, arg))
- return 1;
+ entropy_available = rand_acquire_entropy_from_cpu(pool);
+ if (entropy_available > 0)
+ return entropy_available;
# endif
# ifdef USE_BCRYPTGENRANDOM
- if (BCryptGenRandom(NULL, buf, (ULONG)sizeof(buf),
- BCRYPT_USE_SYSTEM_PREFERRED_RNG) == STATUS_SUCCESS) {
- rand_add(arg, buf, sizeof(buf), sizeof(buf));
- return 1;
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ if (BCryptGenRandom(NULL, buffer, bytes_needed,
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG) == STATUS_SUCCESS)
+ bytes = bytes_needed;
+
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
}
+ if (entropy_available > 0)
+ return entropy_available;
# else
- /* poll the CryptoAPI PRNG */
- if (CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL,
- CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) {
- if (CryptGenRandom(hProvider, (DWORD)sizeof(buf), buf) != 0) {
- rand_add(arg, buf, sizeof(buf), sizeof(buf));
- ok = 1;
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ /* poll the CryptoAPI PRNG */
+ if (CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) {
+ if (CryptGenRandom(hProvider, bytes_needed, buffer) != 0)
+ bytes = bytes_needed;
+
+ CryptReleaseContext(hProvider, 0);
}
- CryptReleaseContext(hProvider, 0);
- if (ok)
- return 1;
+
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
}
+ if (entropy_available > 0)
+ return entropy_available;
+
+ bytes_needed = RAND_POOL_bytes_needed(pool, 8 /*entropy_per_byte*/);
+ buffer = RAND_POOL_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ /* poll the Pentium PRG with CryptoAPI */
+ if (CryptAcquireContextW(&hProvider, NULL,
+ INTEL_DEF_PROV, PROV_INTEL_SEC,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) {
+ if (CryptGenRandom(hProvider, bytes_needed, buffer) != 0)
+ bytes = bytes_needed;
- /* poll the Pentium PRG with CryptoAPI */
- if (CryptAcquireContextW(&hProvider, NULL, INTEL_DEF_PROV, PROV_INTEL_SEC,
- CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) {
- if (CryptGenRandom(hProvider, (DWORD)sizeof(buf), buf) != 0) {
- rand_add(arg, buf, sizeof(buf), sizeof(buf));
- ok = 1;
+ CryptReleaseContext(hProvider, 0);
}
- CryptReleaseContext(hProvider, 0);
- if (ok)
- return 1;
+ entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
}
+ if (entropy_available > 0)
+ return entropy_available;
# endif
- return 0;
+ return RAND_POOL_entropy_available(pool);
}
# if OPENSSL_API_COMPAT < 0x10100000L