diff --git a/crypto/rand/drbg_lib.c b/crypto/rand/drbg_lib.c index c471b6eda4..67a79d4efe 100644 --- a/crypto/rand/drbg_lib.c +++ b/crypto/rand/drbg_lib.c @@ -15,12 +15,6 @@ #include "internal/thread_once.h" #include "internal/rand_int.h" -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. @@ -33,9 +27,72 @@ static const char ossl_pers_string[] = "OpenSSL NIST SP 800-90A DRBG"; * a much bigger deal than just re-setting an allocated resource.) */ +/* + * THE THREE SHARED DRBGs + * + * There are three shared DRBGs (master, public and private), which are + * accessed concurrently by all threads. + * + * THE MASTER DRBG + * + * Not used directly by the application, only for reseeding the two other + * DRBGs. It reseeds itself by pulling either randomness from os entropy + * sources or by consuming randomnes which was added by RAND_add() + */ +static RAND_DRBG drbg_master; +/* + * THE PUBLIC DRBG + * + * Used by default for generating random bytes using RAND_bytes(). + */ +static RAND_DRBG drbg_public; +/* + * THE PRIVATE DRBG + * + * Used by default for generating private keys using RAND_priv_bytes() + */ +static RAND_DRBG drbg_private; +/*+ + * DRBG HIERARCHY + * + * In addition there are DRBGs, which are not shared, but used only by a + * single thread at every time, for example the DRBGs which are owned by + * an SSL context. All DRBGs are organized in a hierarchical fashion + * with the DRBG as root. + * + * This gives the following overall picture: + * + * + * | + * RAND_add() ==> \ + * / \ | shared DRBGs (with locking) + * / + * | + * owned by an SSL context + * + * AUTOMATIC RESEEDING + * + * Before satisfying a generate request, a DRBG reseeds itself automatically + * if the number of generate requests since the last reseeding exceeds a + * certain threshold, the so called |reseed_interval|. This automatic + * reseeding can be disabled by setting the |reseed_interval| to 0. + * + * MANUAL RESEEDING + * + * For the three shared DRBGs (and only for these) there is a way to reseed + * them manually by calling RAND_seed() (or RAND_add() with a positive + * |randomness| argument). This will immediately reseed the DRBG. + * Its immediate children ( and DRBG) will detect this + * on their next generate call and reseed, pulling randomness from . + */ + + +/* NIST SP 800-90A DRBG recommends the use of a personalization string. */ +static const char ossl_pers_string[] = "OpenSSL NIST SP 800-90A DRBG"; + static CRYPTO_ONCE rand_drbg_init = CRYPTO_ONCE_STATIC_INIT; -static int drbg_setup(RAND_DRBG *drbg, const char *name); +static int drbg_setup(RAND_DRBG *drbg, const char *name, RAND_DRBG *parent); /* * Set/initialize |drbg| to be of type |nid|, with optional |flags|. @@ -72,6 +129,8 @@ int RAND_DRBG_set(RAND_DRBG *drbg, int nid, unsigned int flags) /* * Allocate memory and initialize a new DRBG. The |parent|, if not * NULL, will be used to auto-seed this RAND_DRBG as needed. + * + * Returns a pointer to the new DRBG instance on success, NULL on failure. */ RAND_DRBG *RAND_DRBG_new(int type, unsigned int flags, RAND_DRBG *parent) { @@ -86,12 +145,10 @@ RAND_DRBG *RAND_DRBG_new(int type, unsigned int flags, RAND_DRBG *parent) if (RAND_DRBG_set(drbg, type, flags) < 0) goto err; - if (parent != NULL) { - if (!RAND_DRBG_set_callbacks(drbg, rand_drbg_get_entropy, - rand_drbg_cleanup_entropy, - NULL, NULL)) - goto err; - } + if (!RAND_DRBG_set_callbacks(drbg, rand_drbg_get_entropy, + rand_drbg_cleanup_entropy, + NULL, NULL)) + goto err; return drbg; @@ -164,7 +221,14 @@ int RAND_DRBG_instantiate(RAND_DRBG *drbg, } drbg->state = DRBG_READY; - drbg->reseed_counter = 1; + drbg->generate_counter = 0; + + if (drbg->reseed_counter > 0) { + if (drbg->parent == NULL) + drbg->reseed_counter++; + else + drbg->reseed_counter = drbg->parent->reseed_counter; + } end: if (entropy != NULL && drbg->cleanup_entropy != NULL) @@ -194,7 +258,6 @@ int RAND_DRBG_uninstantiate(RAND_DRBG *drbg) { int ret = ctr_uninstantiate(drbg); - OPENSSL_cleanse(&drbg->ctr, sizeof(drbg->ctr)); drbg->state = DRBG_UNINITIALISED; return ret; } @@ -238,8 +301,16 @@ int RAND_DRBG_reseed(RAND_DRBG *drbg, if (!ctr_reseed(drbg, entropy, entropylen, adin, adinlen)) goto end; + drbg->state = DRBG_READY; - drbg->reseed_counter = 1; + drbg->generate_counter = 0; + + if (drbg->reseed_counter > 0) { + if (drbg->parent == NULL) + drbg->reseed_counter++; + else + drbg->reseed_counter = drbg->parent->reseed_counter; + } end: if (entropy != NULL && drbg->cleanup_entropy != NULL) @@ -310,12 +381,18 @@ int rand_drbg_restart(RAND_DRBG *drbg, } /* repair error state */ - if (drbg->state == DRBG_ERROR) + if (drbg->state == DRBG_ERROR) { RAND_DRBG_uninstantiate(drbg); + /* The drbg->ctr member needs to be reinitialized before reinstantiation */ + RAND_DRBG_set(drbg, drbg->nid, drbg->flags); + } /* repair uninitialized state */ if (drbg->state == DRBG_UNINITIALISED) { - drbg_setup(drbg, NULL); + /* reinstantiate drbg */ + RAND_DRBG_instantiate(drbg, + (const unsigned char *) ossl_pers_string, + sizeof(ossl_pers_string) - 1); /* already reseeded. prevent second reseeding below */ reseeded = (drbg->state == DRBG_READY); } @@ -394,8 +471,15 @@ int RAND_DRBG_generate(RAND_DRBG *drbg, unsigned char *out, size_t outlen, reseed_required = 1; } - if (drbg->reseed_counter >= drbg->reseed_interval) - reseed_required = 1; + if (drbg->reseed_interval > 0) { + if (drbg->generate_counter >= drbg->reseed_interval) + reseed_required = 1; + } + + if (drbg->reseed_counter > 0 && drbg->parent != NULL) { + if (drbg->reseed_counter != drbg->parent->reseed_counter) + reseed_required = 1; + } if (reseed_required || prediction_resistance) { if (!RAND_DRBG_reseed(drbg, adin, adinlen)) { @@ -412,7 +496,7 @@ int RAND_DRBG_generate(RAND_DRBG *drbg, unsigned char *out, size_t outlen, return 0; } - drbg->reseed_counter++; + drbg->generate_counter++; return 1; } @@ -472,10 +556,16 @@ int RAND_DRBG_set_callbacks(RAND_DRBG *drbg, /* * Set the reseed interval. + * + * The drbg will reseed automatically whenever the number of generate + * requests exceeds the given reseed interval. If the reseed interval + * is 0, then this automatic reseeding is disabled. + * + * Returns 1 on success, 0 on failure. */ -int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg, int interval) +int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg, unsigned int interval) { - if (interval < 0 || interval > MAX_RESEED) + if (interval > MAX_RESEED_INTERVAL) return 0; drbg->reseed_interval = interval; return 1; @@ -501,31 +591,41 @@ void *RAND_DRBG_get_ex_data(const RAND_DRBG *drbg, int idx) */ /* - * 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 + * Initializes the given global DRBG 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 int drbg_setup(RAND_DRBG *drbg, const char *name) +static int drbg_setup(RAND_DRBG *drbg, const char *name, RAND_DRBG *parent) { int ret = 1; - if (name != NULL) { - if (drbg->lock != NULL) { - RANDerr(RAND_F_DRBG_SETUP, ERR_R_INTERNAL_ERROR); - return 0; - } + if (name == NULL || 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; - } + 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, 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; + + if (parent == NULL) + drbg->reseed_interval = MASTER_RESEED_INTERVAL; + else { + drbg->parent = parent; + drbg->reseed_interval = SLAVE_RESEED_INTERVAL; + } + + /* enable seed propagation */ + drbg->reseed_counter = 1; + /* * Ignore instantiation error so support just-in-time instantiation. * @@ -546,8 +646,9 @@ DEFINE_RUN_ONCE_STATIC(do_rand_drbg_init) { int ret = 1; - ret &= drbg_setup(&rand_drbg, "rand_drbg"); - ret &= drbg_setup(&priv_drbg, "priv_drbg"); + ret &= drbg_setup(&drbg_master, "drbg_master", NULL); + ret &= drbg_setup(&drbg_public, "drbg_public", &drbg_master); + ret &= drbg_setup(&drbg_private, "drbg_private", &drbg_master); return ret; } @@ -562,8 +663,9 @@ static void drbg_cleanup(RAND_DRBG *drbg) /* Clean up the global DRBGs before exit */ void rand_drbg_cleanup_int(void) { - drbg_cleanup(&rand_drbg); - drbg_cleanup(&priv_drbg); + drbg_cleanup(&drbg_private); + drbg_cleanup(&drbg_public); + drbg_cleanup(&drbg_master); } /* Implements the default OpenSSL RAND_bytes() method */ @@ -571,7 +673,7 @@ static int drbg_bytes(unsigned char *out, int count) { int ret = 0; size_t chunk; - RAND_DRBG *drbg = RAND_DRBG_get0_global(); + RAND_DRBG *drbg = RAND_DRBG_get0_public(); if (drbg == NULL) return 0; @@ -599,7 +701,7 @@ err: static int drbg_add(const void *buf, int num, double randomness) { int ret = 0; - RAND_DRBG *drbg = RAND_DRBG_get0_global(); + RAND_DRBG *drbg = RAND_DRBG_get0_master(); if (drbg == NULL) return 0; @@ -636,7 +738,7 @@ static int drbg_seed(const void *buf, int num) static int drbg_status(void) { int ret; - RAND_DRBG *drbg = RAND_DRBG_get0_global(); + RAND_DRBG *drbg = RAND_DRBG_get0_master(); if (drbg == NULL) return 0; @@ -648,27 +750,40 @@ static int drbg_status(void) } /* - * Get the global public DRBG. + * Get the master DRBG. * Returns pointer to the DRBG on success, NULL on failure. + * */ -RAND_DRBG *RAND_DRBG_get0_global(void) +RAND_DRBG *RAND_DRBG_get0_master(void) { if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init)) return NULL; - return &rand_drbg; + return &drbg_master; } /* - * Get the global private DRBG. + * Get the public DRBG. * Returns pointer to the DRBG on success, NULL on failure. */ -RAND_DRBG *RAND_DRBG_get0_priv_global(void) +RAND_DRBG *RAND_DRBG_get0_public(void) { if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init)) return NULL; - return &priv_drbg; + return &drbg_public; +} + +/* + * Get the private DRBG. + * Returns pointer to the DRBG on success, NULL on failure. + */ +RAND_DRBG *RAND_DRBG_get0_private(void) +{ + if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init)) + return NULL; + + return &drbg_private; } RAND_METHOD rand_meth = { diff --git a/crypto/rand/drbg_rand.c b/crypto/rand/drbg_rand.c index f45ef1424c..9d10aaa8be 100644 --- a/crypto/rand/drbg_rand.c +++ b/crypto/rand/drbg_rand.c @@ -301,7 +301,7 @@ int ctr_generate(RAND_DRBG *drbg, int ctr_uninstantiate(RAND_DRBG *drbg) { - memset(&drbg->ctr, 0, sizeof(drbg->ctr)); + OPENSSL_cleanse(&drbg->ctr, sizeof(drbg->ctr)); return 1; } @@ -357,6 +357,6 @@ int ctr_init(RAND_DRBG *drbg) } drbg->max_request = 1 << 16; - drbg->reseed_interval = MAX_RESEED; + drbg->reseed_interval = MAX_RESEED_INTERVAL; return 1; } diff --git a/crypto/rand/rand_lcl.h b/crypto/rand/rand_lcl.h index 5e319d8c99..ad79434ec4 100644 --- a/crypto/rand/rand_lcl.h +++ b/crypto/rand/rand_lcl.h @@ -20,8 +20,12 @@ /* How many times to read the TSC as a randomness source. */ # define TSC_READ_COUNT 4 -/* Maximum count allowed in reseeding */ -# define MAX_RESEED (1 << 24) +/* Maximum reseed interval */ +# define MAX_RESEED_INTERVAL (1 << 24) + +/* Default reseed intervals */ +# define MASTER_RESEED_INTERVAL (1 << 8) +# define SLAVE_RESEED_INTERVAL (1 << 16) /* Max size of additional input and personalization string. */ # define DRBG_MAX_LENGTH 4096 @@ -106,8 +110,26 @@ struct rand_drbg_st { size_t min_entropylen, max_entropylen; size_t min_noncelen, max_noncelen; size_t max_perslen, max_adinlen; - unsigned int reseed_counter; + + /* Counts the number of generate requests since the last reseed. */ + unsigned int generate_counter; + /* + * Maximum number of generate requests until a reseed is required. + * This value is ignored if it is zero. + */ unsigned int reseed_interval; + /* + * Counts the number of reseeds since instantiation. + * This value is ignored if it is zero. + * + * This counter is used only for seed propagation from the DRBG + * to its two children, the and DRBG. This feature is + * very special and its sole purpose is to ensure that any randomness which + * is added by RAND_add() or RAND_seed() will have an immediate effect on + * the output of RAND_bytes() resp. RAND_priv_bytes(). + */ + unsigned int reseed_counter; + size_t seedlen; DRBG_STATUS state; diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c index a290e5c1a2..07f54276d3 100644 --- a/crypto/rand/rand_lib.c +++ b/crypto/rand/rand_lib.c @@ -246,8 +246,8 @@ int RAND_poll(void) 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(); + /* fill random pool and seed the master DRBG */ + RAND_DRBG *drbg = RAND_DRBG_get0_master(); if (drbg == NULL) return 0; @@ -639,7 +639,7 @@ int RAND_priv_bytes(unsigned char *buf, int num) if (meth != RAND_OpenSSL()) return RAND_bytes(buf, num); - drbg = RAND_DRBG_get0_priv_global(); + drbg = RAND_DRBG_get0_private(); if (drbg == NULL) return 0; diff --git a/include/internal/rand.h b/include/internal/rand.h index feda9beaa1..26d6c38f45 100644 --- a/include/internal/rand.h +++ b/include/internal/rand.h @@ -42,9 +42,11 @@ int RAND_DRBG_reseed(RAND_DRBG *drbg, int RAND_DRBG_generate(RAND_DRBG *drbg, unsigned char *out, size_t outlen, int prediction_resistance, const unsigned char *adin, size_t adinlen); -int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg, int interval); -RAND_DRBG *RAND_DRBG_get0_global(void); -RAND_DRBG *RAND_DRBG_get0_priv_global(void); +int RAND_DRBG_set_reseed_interval(RAND_DRBG *drbg, unsigned int interval); + +RAND_DRBG *RAND_DRBG_get0_master(void); +RAND_DRBG *RAND_DRBG_get0_public(void); +RAND_DRBG *RAND_DRBG_get0_private(void); /* * EXDATA diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 37e83bddaf..fb113bab6d 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -691,7 +691,7 @@ SSL *SSL_new(SSL_CTX *ctx) if (RAND_get_rand_method() == RAND_OpenSSL()) { s->drbg = RAND_DRBG_new(RAND_DRBG_NID, RAND_DRBG_FLAG_CTR_USE_DF, - RAND_DRBG_get0_global()); + RAND_DRBG_get0_public()); if (s->drbg == NULL || RAND_DRBG_instantiate(s->drbg, (const unsigned char *) SSL_version_str, diff --git a/test/drbgtest.c b/test/drbgtest.c index 25920d6e40..b1fc751cd9 100644 --- a/test/drbgtest.c +++ b/test/drbgtest.c @@ -274,7 +274,7 @@ static int error_check(DRBG_SELFTEST_DATA *td) RAND_DRBG *drbg = NULL; TEST_CTX t; unsigned char buff[1024]; - unsigned int reseed_counter_tmp; + unsigned int generate_counter_tmp; int ret = 0; if (!TEST_ptr(drbg = RAND_DRBG_new(0, 0, NULL))) @@ -369,15 +369,15 @@ static int error_check(DRBG_SELFTEST_DATA *td) /* Instantiate again with valid data */ if (!instantiate(drbg, td, &t)) goto err; - reseed_counter_tmp = drbg->reseed_counter; - drbg->reseed_counter = drbg->reseed_interval; + generate_counter_tmp = drbg->generate_counter; + drbg->generate_counter = drbg->reseed_interval; /* Generate output and check entropy has been requested for reseed */ t.entropycnt = 0; if (!TEST_true(RAND_DRBG_generate(drbg, buff, td->exlen, 0, td->adin, td->adinlen)) || !TEST_int_eq(t.entropycnt, 1) - || !TEST_int_eq(drbg->reseed_counter, reseed_counter_tmp + 1) + || !TEST_int_eq(drbg->generate_counter, generate_counter_tmp + 1) || !uninstantiate(drbg)) goto err; @@ -394,15 +394,15 @@ static int error_check(DRBG_SELFTEST_DATA *td) /* Test reseed counter works */ if (!instantiate(drbg, td, &t)) goto err; - reseed_counter_tmp = drbg->reseed_counter; - drbg->reseed_counter = drbg->reseed_interval; + generate_counter_tmp = drbg->generate_counter; + drbg->generate_counter = drbg->reseed_interval; /* Generate output and check entropy has been requested for reseed */ t.entropycnt = 0; if (!TEST_true(RAND_DRBG_generate(drbg, buff, td->exlen, 0, td->adin, td->adinlen)) || !TEST_int_eq(t.entropycnt, 1) - || !TEST_int_eq(drbg->reseed_counter, reseed_counter_tmp + 1) + || !TEST_int_eq(drbg->generate_counter, generate_counter_tmp + 1) || !uninstantiate(drbg)) goto err; @@ -475,19 +475,275 @@ err: return rv; } -#define RAND_ADD_SIZE 500 +/* + * Hook context data, attached as EXDATA to the RAND_DRBG + */ +typedef struct hook_ctx_st { + RAND_DRBG *drbg; + /* + * Currently, all DRBGs use the same get_entropy() callback. + * The tests however, don't assume this and store + * the original callback for every DRBG separately. + */ + RAND_DRBG_get_entropy_fn get_entropy; + /* forces a failure of the get_entropy() call if nonzero */ + int fail; + /* counts successful reseeds */ + int reseed_count; +} HOOK_CTX; -static int test_rand_add(void) +static HOOK_CTX master_ctx, public_ctx, private_ctx; + +static HOOK_CTX *get_hook_ctx(RAND_DRBG *drbg) { - char *p; + return (HOOK_CTX *)RAND_DRBG_get_ex_data(drbg, app_data_index); +} - if (!TEST_ptr(p = calloc(RAND_ADD_SIZE, 1))) +/* Intercepts and counts calls to the get_entropy() callback */ +static size_t get_entropy_hook(RAND_DRBG *drbg, unsigned char **pout, + int entropy, size_t min_len, size_t max_len) +{ + size_t ret; + HOOK_CTX *ctx = get_hook_ctx(drbg); + + if (ctx->fail != 0) return 0; - RAND_add(p, RAND_ADD_SIZE, RAND_ADD_SIZE); - free(p); + + ret = ctx->get_entropy( + drbg, pout, entropy, min_len, max_len); + + if (ret != 0) + ctx->reseed_count++; + return ret; +} + +/* Installs a hook for the get_entropy() callback of the given drbg */ +static void hook_drbg(RAND_DRBG *drbg, HOOK_CTX *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->drbg = drbg; + ctx->get_entropy = drbg->get_entropy; + drbg->get_entropy = get_entropy_hook; + RAND_DRBG_set_ex_data(drbg, app_data_index, ctx); +} + +/* Installs the hook for the get_entropy() callback of the given drbg */ +static void unhook_drbg(RAND_DRBG *drbg) +{ + HOOK_CTX *ctx = get_hook_ctx(drbg); + + drbg->get_entropy = ctx->get_entropy; + CRYPTO_free_ex_data(CRYPTO_EX_INDEX_DRBG, drbg, &drbg->ex_data); +} + +/* Resets the given hook context */ +static void reset_hook_ctx(HOOK_CTX *ctx) +{ + ctx->fail = 0; + ctx->reseed_count = 0; +} + +/* Resets all drbg hook contexts */ +static void reset_drbg_hook_ctx() +{ + reset_hook_ctx(&master_ctx); + reset_hook_ctx(&public_ctx); + reset_hook_ctx(&private_ctx); +} + +/* + * Generates random output using RAND_bytes() and RAND_priv_bytes() + * and checks whether the three shared DRBGs were reseeded as + * expected. + * + * |expect_success|: expected outcome (as reported by RAND_status()) + * |master|, |public|, |private|: pointers to the three shared DRBGs + * |expect_xxx_reseed| = + * 1: it is expected that the specified DRBG is reseeded + * 0: it is expected that the specified DRBG is not reseeded + * -1: don't check whether the specified DRBG was reseeded or not + */ +static int test_drbg_reseed(int expect_success, + RAND_DRBG *master, + RAND_DRBG *public, + RAND_DRBG *private, + int expect_master_reseed, + int expect_public_reseed, + int expect_private_reseed + ) +{ + unsigned char buf[32]; + int expected_state = (expect_success ? DRBG_READY : DRBG_ERROR); + + /* + * step 1: check preconditions + */ + + /* Test whether seed propagation is enabled */ + if (!TEST_int_ne(master->reseed_counter, 0) + || !TEST_int_ne(public->reseed_counter, 0) + || !TEST_int_ne(private->reseed_counter, 0)) + return 0; + + /* Check whether the master DRBG's reseed counter is the largest one */ + if (!TEST_int_le(public->reseed_counter, master->reseed_counter) + || !TEST_int_le(private->reseed_counter, master->reseed_counter)) + return 0; + + /* + * step 2: generate random output + */ + + /* Generate random output from the public and private DRBG */ + if (!TEST_int_eq(RAND_bytes(buf, sizeof(buf)), expect_success) + || !TEST_int_eq(RAND_priv_bytes(buf, sizeof(buf)), expect_success)) + return 0; + + + /* + * step 3: check postconditions + */ + + /* Test whether reseeding succeeded as expected */ + if (!TEST_int_eq(master->state, expected_state) + || !TEST_int_eq(public->state, expected_state) + || !TEST_int_eq(private->state, expected_state)) + return 0; + + if (expect_master_reseed >= 0) { + /* Test whether master DRBG was reseeded as expected */ + if (!TEST_int_eq(master_ctx.reseed_count, expect_master_reseed)) + return 0; + } + + if (expect_public_reseed >= 0) { + /* Test whether public DRBG was reseeded as expected */ + if (!TEST_int_eq(public_ctx.reseed_count, expect_public_reseed)) + return 0; + } + + if (expect_private_reseed >= 0) { + /* Test whether public DRBG was reseeded as expected */ + if (!TEST_int_eq(private_ctx.reseed_count, expect_private_reseed)) + return 0; + } + + if (expect_success == 1) { + /* Test whether all three reseed counters are synchronized */ + if (!TEST_int_eq(public->reseed_counter, master->reseed_counter) + || !TEST_int_eq(private->reseed_counter, master->reseed_counter)) + return 0; + } else { + ERR_clear_error(); + } + return 1; } +/* + * Test whether the default rand_method (RAND_OpenSSL()) is + * setup correctly, in particular whether reseeding works + * as designed. + */ +static int test_rand_reseed(void) +{ + RAND_DRBG *master, *public, *private; + unsigned char rand_add_buf[256]; + int rv=0; + + /* Check whether RAND_OpenSSL() is the default method */ + if (!TEST_ptr_eq(RAND_get_rand_method(), RAND_OpenSSL())) + return 0; + + /* All three DRBGs should be non-null */ + if (!TEST_ptr(master = RAND_DRBG_get0_master()) + || !TEST_ptr(public = RAND_DRBG_get0_public()) + || !TEST_ptr(private = RAND_DRBG_get0_private())) + return 0; + + /* There should be three distinct DRBGs, two of them chained to master */ + if (!TEST_ptr_ne(public, private) + || !TEST_ptr_ne(public, master) + || !TEST_ptr_ne(private, master) + || !TEST_ptr_eq(public->parent, master) + || !TEST_ptr_eq(private->parent, master)) + return 0; + + /* Install hooks for the following tests */ + hook_drbg(master, &master_ctx); + hook_drbg(public, &public_ctx); + hook_drbg(private, &private_ctx); + + /* + * Test initial state of shared DRBs + */ + if (!TEST_true(test_drbg_reseed(1, master, public, private, 0, 0, 0))) + goto error; + reset_drbg_hook_ctx(); + + /* + * Test whether the public and private DRBG are both reseeded when their + * reseed counters differ from the master's reseed counter. + */ + master->reseed_counter++; + if (!TEST_true(test_drbg_reseed(1, master, public, private, 0, 1, 1))) + goto error; + reset_drbg_hook_ctx(); + + /* + * Test whether the public DRBG is reseeded when its reseed counter differs + * from the master's reseed counter. + */ + master->reseed_counter++; + private->reseed_counter++; + if (!TEST_true(test_drbg_reseed(1, master, public, private, 0, 1, 0))) + goto error; + reset_drbg_hook_ctx(); + + /* + * Test whether the private DRBG is reseeded when its reseed counter differs + * from the master's reseed counter. + */ + master->reseed_counter++; + public->reseed_counter++; + if (!TEST_true(test_drbg_reseed(1, master, public, private, 0, 0, 1))) + goto error; + reset_drbg_hook_ctx(); + + + /* fill 'randomness' buffer with some arbitrary data */ + memset(rand_add_buf, 'r', sizeof(rand_add_buf)); + + /* + * Test whether all three DRBGs are reseeded by RAND_add() + */ + RAND_add(rand_add_buf, sizeof(rand_add_buf), sizeof(rand_add_buf)); + if (!TEST_true(test_drbg_reseed(1, master, public, private, 1, 1, 1))) + goto error; + reset_drbg_hook_ctx(); + + + /* + * Test whether none of the DRBGs is reseed if the master fails to reseed + */ + master_ctx.fail = 1; + master->reseed_counter++; + RAND_add(rand_add_buf, sizeof(rand_add_buf), sizeof(rand_add_buf)); + if (!TEST_true(test_drbg_reseed(0, master, public, private, 0, 0, 0))) + goto error; + reset_drbg_hook_ctx(); + + rv = 1; + +error: + /* Remove hooks */ + unhook_drbg(master); + unhook_drbg(public); + unhook_drbg(private); + + return rv; +} + int setup_tests(void) { @@ -495,6 +751,6 @@ int setup_tests(void) ADD_ALL_TESTS(test_kats, OSSL_NELEM(drbg_test)); ADD_ALL_TESTS(test_error_checks, OSSL_NELEM(drbg_test)); - ADD_TEST(test_rand_add); + ADD_TEST(test_rand_reseed); return 1; } diff --git a/util/libcrypto.num b/util/libcrypto.num index 10ffa2c3d5..7bfa6015d0 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -4371,7 +4371,7 @@ SCRYPT_PARAMS_it 4314 1_1_1 EXIST:EXPORT_VAR_AS_FUNCTION: CRYPTO_secure_clear_free 4315 1_1_0g EXIST::FUNCTION: EVP_PKEY_meth_get0 4316 1_1_1 EXIST::FUNCTION: EVP_PKEY_meth_get_count 4317 1_1_1 EXIST::FUNCTION: -RAND_DRBG_get0_global 4319 1_1_1 EXIST::FUNCTION: +RAND_DRBG_get0_public 4319 1_1_1 EXIST::FUNCTION: RAND_priv_bytes 4320 1_1_1 EXIST::FUNCTION: BN_priv_rand 4321 1_1_1 EXIST::FUNCTION: BN_priv_rand_range 4322 1_1_1 EXIST::FUNCTION: @@ -4381,7 +4381,7 @@ ASN1_TIME_compare 4325 1_1_1 EXIST::FUNCTION: EVP_PKEY_CTX_ctrl_uint64 4326 1_1_1 EXIST::FUNCTION: EVP_DigestFinalXOF 4327 1_1_1 EXIST::FUNCTION: ERR_clear_last_mark 4328 1_1_1 EXIST::FUNCTION: -RAND_DRBG_get0_priv_global 4329 1_1_1 EXIST::FUNCTION: +RAND_DRBG_get0_private 4329 1_1_1 EXIST::FUNCTION: EVP_aria_192_ccm 4330 1_1_1 EXIST::FUNCTION:ARIA EVP_aria_256_gcm 4331 1_1_1 EXIST::FUNCTION:ARIA EVP_aria_256_ccm 4332 1_1_1 EXIST::FUNCTION:ARIA @@ -4446,3 +4446,4 @@ RSA_set0_multi_prime_params 4390 1_1_1 EXIST::FUNCTION:RSA RSA_get_version 4391 1_1_1 EXIST::FUNCTION:RSA RSA_meth_get_multi_prime_keygen 4392 1_1_1 EXIST::FUNCTION:RSA RSA_meth_set_multi_prime_keygen 4393 1_1_1 EXIST::FUNCTION:RSA +RAND_DRBG_get0_master 4394 1_1_1 EXIST::FUNCTION: