diff options
-rw-r--r-- | crypto/err/openssl.txt | 13 | ||||
-rw-r--r-- | crypto/include/internal/rand_int.h | 2 | ||||
-rw-r--r-- | crypto/init.c | 2 | ||||
-rw-r--r-- | crypto/rand/drbg_lib.c | 311 | ||||
-rw-r--r-- | crypto/rand/drbg_rand.c | 4 | ||||
-rw-r--r-- | crypto/rand/rand_err.c | 21 | ||||
-rw-r--r-- | crypto/rand/rand_lcl.h | 87 | ||||
-rw-r--r-- | crypto/rand/rand_lib.c | 520 | ||||
-rw-r--r-- | crypto/rand/rand_unix.c | 127 | ||||
-rw-r--r-- | crypto/rand/rand_vms.c | 13 | ||||
-rw-r--r-- | crypto/rand/rand_win.c | 84 | ||||
-rw-r--r-- | doc/man3/RAND_add.pod | 89 | ||||
-rw-r--r-- | include/internal/rand.h | 45 | ||||
-rw-r--r-- | include/openssl/ossl_typ.h | 1 | ||||
-rw-r--r-- | include/openssl/rand.h | 1 | ||||
-rw-r--r-- | include/openssl/randerr.h | 13 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 9 | ||||
-rw-r--r-- | test/drbgtest.c | 2 | ||||
-rw-r--r-- | util/libcrypto.num | 15 |
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 *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 cloc |