+int RAND_DRBG_set_ex_data(RAND_DRBG *drbg, int idx, void *arg)
+{
+ return CRYPTO_set_ex_data(&drbg->ex_data, idx, arg);
+}
+
+void *RAND_DRBG_get_ex_data(const RAND_DRBG *drbg, int idx)
+{
+ return CRYPTO_get_ex_data(&drbg->ex_data, idx);
+}
+
+
+/*
+ * The following functions provide a RAND_METHOD that works on the
+ * global DRBG. They lock.
+ */
+
+/*
+ * Allocates a new global DRBG on the secure heap (if enabled) and
+ * initializes it with default settings.
+ * A global lock for the DRBG is created with the given name.
+ *
+ * Returns a pointer to the new DRBG instance on success, NULL on failure.
+ */
+static RAND_DRBG *drbg_setup(const char *name, RAND_DRBG *parent)
+{
+ RAND_DRBG *drbg;
+
+ if (name == NULL) {
+ RANDerr(RAND_F_DRBG_SETUP, ERR_R_INTERNAL_ERROR);
+ return NULL;
+ }
+
+ drbg = OPENSSL_secure_zalloc(sizeof(RAND_DRBG));
+ if (drbg == NULL)
+ return NULL;
+
+ drbg->lock = CRYPTO_THREAD_glock_new(name);
+ if (drbg->lock == NULL) {
+ RANDerr(RAND_F_DRBG_SETUP, RAND_R_FAILED_TO_CREATE_LOCK);
+ goto err;
+ }
+
+ if (RAND_DRBG_set(drbg,
+ RAND_DRBG_NID, RAND_DRBG_FLAG_CTR_USE_DF) != 1)
+ goto err;
+ if (RAND_DRBG_set_callbacks(drbg, rand_drbg_get_entropy,
+ rand_drbg_cleanup_entropy, NULL, NULL) != 1)
+ goto err;
+
+ if (parent == NULL) {
+ drbg->reseed_interval = MASTER_RESEED_INTERVAL;
+ drbg->reseed_time_interval = MASTER_RESEED_TIME_INTERVAL;
+ } else {
+ drbg->parent = parent;
+ drbg->reseed_interval = SLAVE_RESEED_INTERVAL;
+ drbg->reseed_time_interval = SLAVE_RESEED_TIME_INTERVAL;
+ }
+
+ /* enable seed propagation */
+ drbg->reseed_counter = 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 drbg;
+
+err:
+ drbg_cleanup(drbg);
+ return NULL;
+}
+
+/*
+ * Initialize the global DRBGs on first use.
+ * Returns 1 on success, 0 on failure.
+ */
+DEFINE_RUN_ONCE_STATIC(do_rand_drbg_init)
+{
+ /*
+ * ensure that libcrypto is initialized, otherwise the
+ * DRBG locks are not cleaned up properly
+ */
+ if (!OPENSSL_init_crypto(0, NULL))
+ return 0;
+
+ drbg_master = drbg_setup("drbg_master", NULL);
+ drbg_public = drbg_setup("drbg_public", drbg_master);
+ drbg_private = drbg_setup("drbg_private", drbg_master);
+
+ if (drbg_master == NULL || drbg_public == NULL || drbg_private == NULL)
+ return 0;
+
+ return 1;
+}
+
+/* Cleans up the given global DRBG */
+static void drbg_cleanup(RAND_DRBG *drbg)
+{
+ if (drbg != NULL) {
+ RAND_DRBG_uninstantiate(drbg);
+ CRYPTO_THREAD_lock_free(drbg->lock);
+ OPENSSL_secure_clear_free(drbg, sizeof(RAND_DRBG));
+ }
+}
+
+/* Clean up the global DRBGs before exit */
+void rand_drbg_cleanup_int(void)
+{
+ drbg_cleanup(drbg_private);
+ drbg_cleanup(drbg_public);
+ drbg_cleanup(drbg_master);
+
+ drbg_private = drbg_public = drbg_master = NULL;
+}
+
+/* Implements the default OpenSSL RAND_bytes() method */
+static int drbg_bytes(unsigned char *out, int count)
+{
+ int ret = 0;
+ size_t chunk;
+ RAND_DRBG *drbg = RAND_DRBG_get0_public();
+
+ if (drbg == NULL)
+ return 0;
+
+ CRYPTO_THREAD_write_lock(drbg->lock);
+ if (drbg->state == DRBG_UNINITIALISED)
+ goto err;
+
+ for ( ; count > 0; count -= chunk, out += chunk) {
+ chunk = count;
+ if (chunk > drbg->max_request)
+ chunk = drbg->max_request;
+ ret = RAND_DRBG_generate(drbg, out, chunk, 0, NULL, 0);
+ if (!ret)
+ goto err;
+ }
+ ret = 1;
+
+err:
+ CRYPTO_THREAD_unlock(drbg->lock);
+ return ret;
+}
+
+/* Implements the default OpenSSL RAND_add() method */
+static int drbg_add(const void *buf, int num, double randomness)
+{
+ int ret = 0;
+ RAND_DRBG *drbg = RAND_DRBG_get0_master();
+
+ if (drbg == NULL)
+ return 0;
+
+ if (num < 0 || randomness < 0.0)
+ return 0;
+
+ 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_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;
+ RAND_DRBG *drbg = RAND_DRBG_get0_master();
+
+ if (drbg == NULL)
+ return 0;
+
+ CRYPTO_THREAD_write_lock(drbg->lock);
+ ret = drbg->state == DRBG_READY ? 1 : 0;
+ CRYPTO_THREAD_unlock(drbg->lock);
+ return ret;
+}
+
+/*
+ * Get the master DRBG.
+ * Returns pointer to the DRBG on success, NULL on failure.
+ *
+ */
+RAND_DRBG *RAND_DRBG_get0_master(void)