From 3ce1c27b56fa9856693e5c98331cebaa2a3accfa Mon Sep 17 00:00:00 2001 From: "Dr. Matthias St. Pierre" Date: Thu, 8 Feb 2018 16:40:32 +0100 Subject: [PATCH] DRBG: add locking api This commit adds three new accessors to the internal DRBG lock int RAND_DRBG_lock(RAND_DRBG *drbg) int RAND_DRBG_unlock(RAND_DRBG *drbg) int RAND_DRBG_enable_locking(RAND_DRBG *drbg) The three shared DRBGs are intended to be used concurrently, so they have locking enabled by default. It is the callers responsibility to guard access to the shared DRBGs by calls to RAND_DRBG_lock() and RAND_DRBG_unlock(). All other DRBG instances don't have locking enabled by default, because they are intendended to be used by a single thread. If it is desired, locking can be enabled by using RAND_DRBG_enable_locking(). Reviewed-by: Rich Salz (Merged from https://github.com/openssl/openssl/pull/5294) --- crypto/err/openssl.txt | 3 ++ crypto/rand/drbg_lib.c | 90 ++++++++++++++++++++++++++++++++++++--- crypto/rand/rand_err.c | 6 +++ crypto/rand/rand_lib.c | 17 ++++---- include/internal/rand.h | 4 ++ include/openssl/randerr.h | 3 ++ util/libcrypto.num | 3 ++ 7 files changed, 111 insertions(+), 15 deletions(-) diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 3ed71fe53a..8d00463d85 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -890,6 +890,7 @@ 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_ENABLE_LOCKING:119:RAND_DRBG_enable_locking 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 @@ -2256,6 +2257,7 @@ 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_ALREADY_INITIALIZED:129:drbg already initialized 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 @@ -2274,6 +2276,7 @@ RAND_R_IN_ERROR_STATE:114:in error state RAND_R_NOT_A_REGULAR_FILE:122:Not a regular file RAND_R_NOT_INSTANTIATED:115:not instantiated RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED:128:no drbg implementation selected +RAND_R_PARENT_LOCKING_NOT_ENABLED:130:parent locking not enabled 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 diff --git a/crypto/rand/drbg_lib.c b/crypto/rand/drbg_lib.c index 4404e4f720..13b640bb9b 100644 --- a/crypto/rand/drbg_lib.c +++ b/crypto/rand/drbg_lib.c @@ -90,6 +90,21 @@ static RAND_DRBG *drbg_private; * |randomness| argument). This will immediately reseed the DRBG. * The and DRBG will detect this on their next generate * call and reseed, pulling randomness from . + * + * LOCKING + * + * The three shared DRBGs are intended to be used concurrently, so they + * support locking by default. It is the callers responsibility to wrap + * calls to functions like RAND_DRBG_generate() which modify the DRBGs + * internal state with calls to RAND_DRBG_lock() and RAND_DRBG_unlock(). + * The functions RAND_bytes() and RAND_priv_bytes() take the locks + * automatically, so using the RAND api is thread safe as before. + * + * All other DRBG instances don't have locking enabled by default, because + * they are intendended to be used by a single thread. If it is desired, + * locking can be enabled using RAND_DRBG_enable_locking(). However, instead + * of accessing a single DRBG instance concurrently from different threads, + * it is recommended to instantiate a separate DRBG instance per thread. */ @@ -656,6 +671,69 @@ int RAND_DRBG_set_reseed_time_interval(RAND_DRBG *drbg, time_t interval) return 1; } + +/* + * Locks the given drbg. Locking a drbg which does not have locking + * enabled is considered a successful no-op. + * + * Returns 1 on success, 0 on failure. + */ +int RAND_DRBG_lock(RAND_DRBG *drbg) +{ + if (drbg->lock != NULL) + return CRYPTO_THREAD_write_lock(drbg->lock); + + return 1; +} + +/* + * Unlocks the given drbg. Unlocking a drbg which does not have locking + * enabled is considered a successful no-op. + * + * Returns 1 on success, 0 on failure. + */ +int RAND_DRBG_unlock(RAND_DRBG *drbg) +{ + if (drbg->lock != NULL) + return CRYPTO_THREAD_unlock(drbg->lock); + + return 1; +} + +/* + * Enables locking for the given drbg + * + * Locking can only be enabled if the random generator + * is in the uninitialized state. + * + * Returns 1 on success, 0 on failure. + */ +int RAND_DRBG_enable_locking(RAND_DRBG *drbg) +{ + if (drbg->state != DRBG_UNINITIALISED) { + RANDerr(RAND_F_RAND_DRBG_ENABLE_LOCKING, + RAND_R_DRBG_ALREADY_INITIALIZED); + return 0; + } + + if (drbg->lock == NULL) { + if (drbg->parent != NULL && drbg->lock == NULL) { + RANDerr(RAND_F_RAND_DRBG_ENABLE_LOCKING, + RAND_R_PARENT_LOCKING_NOT_ENABLED); + return 0; + } + + drbg->lock = CRYPTO_THREAD_lock_new(); + if (drbg->lock == NULL) { + RANDerr(RAND_F_RAND_DRBG_ENABLE_LOCKING, + RAND_R_FAILED_TO_CREATE_LOCK); + return 0; + } + } + + return 1; +} + /* * Get and set the EXDATA */ @@ -782,9 +860,9 @@ static int drbg_bytes(unsigned char *out, int count) if (drbg == NULL) return 0; - CRYPTO_THREAD_write_lock(drbg->lock); + RAND_DRBG_lock(drbg); ret = RAND_DRBG_bytes(drbg, out, count); - CRYPTO_THREAD_unlock(drbg->lock); + RAND_DRBG_unlock(drbg); return ret; } @@ -811,11 +889,11 @@ static int drbg_add(const void *buf, int num, double randomness) return 0; } - CRYPTO_THREAD_write_lock(drbg->lock); + RAND_DRBG_lock(drbg); ret = rand_drbg_restart(drbg, buf, (size_t)(unsigned int)num, (size_t)(8*randomness)); - CRYPTO_THREAD_unlock(drbg->lock); + RAND_DRBG_unlock(drbg); return ret; } @@ -835,9 +913,9 @@ static int drbg_status(void) if (drbg == NULL) return 0; - CRYPTO_THREAD_write_lock(drbg->lock); + RAND_DRBG_lock(drbg); ret = drbg->state == DRBG_READY ? 1 : 0; - CRYPTO_THREAD_unlock(drbg->lock); + RAND_DRBG_unlock(drbg); return ret; } diff --git a/crypto/rand/rand_err.c b/crypto/rand/rand_err.c index 9eadbf9a67..e8ec44ae12 100644 --- a/crypto/rand/rand_err.c +++ b/crypto/rand/rand_err.c @@ -19,6 +19,8 @@ static const ERR_STRING_DATA RAND_str_functs[] = { {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_ENABLE_LOCKING, 0), + "RAND_DRBG_enable_locking"}, {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_GENERATE, 0), "RAND_DRBG_generate"}, {ERR_PACK(ERR_LIB_RAND, RAND_F_RAND_DRBG_INSTANTIATE, 0), @@ -49,6 +51,8 @@ static const ERR_STRING_DATA RAND_str_reasons[] = { {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_ALREADY_INITIALIZED), + "drbg already initialized"}, {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), @@ -80,6 +84,8 @@ static const ERR_STRING_DATA RAND_str_reasons[] = { {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_NOT_INSTANTIATED), "not instantiated"}, {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED), "no drbg implementation selected"}, + {ERR_PACK(ERR_LIB_RAND, 0, RAND_R_PARENT_LOCKING_NOT_ENABLED), + "parent locking not enabled"}, {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"}, diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c index faec18dd99..289acf3039 100644 --- a/crypto/rand/rand_lib.c +++ b/crypto/rand/rand_lib.c @@ -200,17 +200,16 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg, /* * Get random from parent, include our state as additional input. * Our lock is already held, but we need to lock our parent before - * generating bits from it. + * generating bits from it. (Note: taking the lock will be a no-op + * if locking if drbg->parent->lock == NULL.) */ - if (drbg->parent->lock) - CRYPTO_THREAD_write_lock(drbg->parent->lock); + RAND_DRBG_lock(drbg->parent); if (RAND_DRBG_generate(drbg->parent, buffer, bytes_needed, 0, (unsigned char *)drbg, sizeof(*drbg)) != 0) bytes = bytes_needed; - if (drbg->parent->lock) - CRYPTO_THREAD_unlock(drbg->parent->lock); + RAND_DRBG_unlock(drbg->parent); entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes); } @@ -406,9 +405,9 @@ int RAND_poll(void) if (drbg == NULL) return 0; - CRYPTO_THREAD_write_lock(drbg->lock); + RAND_DRBG_lock(drbg); ret = rand_drbg_restart(drbg, NULL, 0, 0); - CRYPTO_THREAD_unlock(drbg->lock); + RAND_DRBG_unlock(drbg); return ret; @@ -798,9 +797,9 @@ int RAND_priv_bytes(unsigned char *buf, int num) return 0; /* We have to lock the DRBG before generating bits from it. */ - CRYPTO_THREAD_write_lock(drbg->lock); + RAND_DRBG_lock(drbg); ret = RAND_DRBG_bytes(drbg, buf, num); - CRYPTO_THREAD_unlock(drbg->lock); + RAND_DRBG_unlock(drbg); return ret; } diff --git a/include/internal/rand.h b/include/internal/rand.h index 575e6cadeb..a7d2912069 100644 --- a/include/internal/rand.h +++ b/include/internal/rand.h @@ -47,6 +47,10 @@ int RAND_DRBG_bytes(RAND_DRBG *drbg, unsigned char *out, size_t outlen); int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg, unsigned int interval); int RAND_DRBG_set_reseed_time_interval(RAND_DRBG *drbg, time_t interval); +int RAND_DRBG_lock(RAND_DRBG *drbg); +int RAND_DRBG_unlock(RAND_DRBG *drbg); +int RAND_DRBG_enable_locking(RAND_DRBG *drbg); + RAND_DRBG *RAND_DRBG_get0_master(void); RAND_DRBG *RAND_DRBG_get0_public(void); RAND_DRBG *RAND_DRBG_get0_private(void); diff --git a/include/openssl/randerr.h b/include/openssl/randerr.h index ae5a2ea992..4cfc06d9c4 100644 --- a/include/openssl/randerr.h +++ b/include/openssl/randerr.h @@ -24,6 +24,7 @@ int ERR_load_RAND_strings(void); # define RAND_F_DRBG_SETUP 117 # define RAND_F_GET_ENTROPY 106 # define RAND_F_RAND_BYTES 100 +# define RAND_F_RAND_DRBG_ENABLE_LOCKING 119 # define RAND_F_RAND_DRBG_GENERATE 107 # define RAND_F_RAND_DRBG_INSTANTIATE 108 # define RAND_F_RAND_DRBG_NEW 109 @@ -46,6 +47,7 @@ int ERR_load_RAND_strings(void); # define RAND_R_ALREADY_INSTANTIATED 103 # define RAND_R_ARGUMENT_OUT_OF_RANGE 105 # define RAND_R_CANNOT_OPEN_FILE 121 +# define RAND_R_DRBG_ALREADY_INITIALIZED 129 # define RAND_R_DRBG_NOT_INITIALISED 104 # define RAND_R_ENTROPY_INPUT_TOO_LONG 106 # define RAND_R_ENTROPY_OUT_OF_RANGE 124 @@ -64,6 +66,7 @@ int ERR_load_RAND_strings(void); # define RAND_R_NOT_A_REGULAR_FILE 122 # define RAND_R_NOT_INSTANTIATED 115 # define RAND_R_NO_DRBG_IMPLEMENTATION_SELECTED 128 +# define RAND_R_PARENT_LOCKING_NOT_ENABLED 130 # define RAND_R_PERSONALISATION_STRING_TOO_LONG 116 # define RAND_R_PRNG_NOT_SEEDED 100 # define RAND_R_RANDOM_POOL_OVERFLOW 125 diff --git a/util/libcrypto.num b/util/libcrypto.num index 5005d9a628..b133c66546 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -4501,3 +4501,6 @@ EVP_sha512_256 4442 1_1_1 EXIST::FUNCTION: EVP_sha512_224 4443 1_1_1 EXIST::FUNCTION: OCSP_basic_sign_ctx 4444 1_1_1 EXIST::FUNCTION:OCSP RAND_DRBG_bytes 4445 1_1_1 EXIST::FUNCTION: +RAND_DRBG_lock 4446 1_1_1 EXIST::FUNCTION: +RAND_DRBG_unlock 4447 1_1_1 EXIST::FUNCTION: +RAND_DRBG_enable_locking 4448 1_1_1 EXIST::FUNCTION: -- 2.34.1