DRBG: add locking api
authorDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Thu, 8 Feb 2018 15:40:32 +0000 (16:40 +0100)
committerDr. Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
Tue, 13 Feb 2018 16:32:54 +0000 (17:32 +0100)
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 <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/5294)

crypto/err/openssl.txt
crypto/rand/drbg_lib.c
crypto/rand/rand_err.c
crypto/rand/rand_lib.c
include/internal/rand.h
include/openssl/randerr.h
util/libcrypto.num

index 3ed71fe53ac2ba83607312d2fa55cb33a28ae13c..8d00463d8522b17c14e385b6d382409c10937ce0 100644 (file)
@@ -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
index 4404e4f7205e2c92f4f0c7bc78d0f264a1cf6b43..13b640bb9b0e3e98864bf1336f8dcd40614368e7 100644 (file)
@@ -90,6 +90,21 @@ static RAND_DRBG *drbg_private;
  * |randomness| argument). This will immediately reseed the <master> DRBG.
  * The <public> and <private> DRBG will detect this on their next generate
  * call and reseed, pulling randomness from <master>.
+ *
+ * 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;
 }
 
index 9eadbf9a678c84e9c9fa4b4788ae59f480a27dd1..e8ec44ae12a6e0502c9ec8cc6ec8841c71d8056a 100644 (file)
@@ -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"},
index faec18dd99251ff937501ce007ed4229e4a86063..289acf30393664b7972a087d8d624082efc4afd6 100644 (file)
@@ -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;
 }
 
index 575e6cadeb95ef0ca454ca25561747f6e1468bcd..a7d291206936e0002a87611067d9b8a903e4b3e2 100644 (file)
@@ -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);
index ae5a2ea9929f74a344154fd807fbdb90c900a89f..4cfc06d9c474037430fc3ae885c79b89cbceaa36 100644 (file)
@@ -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
index 5005d9a62833c994db93b71bb1244105820bc99f..b133c66546eddc720fe39e07cc2b3f499ca4db31 100644 (file)
@@ -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: