From a93ba405b0327db9106f8f224112a2b64fb264e7 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias St. Pierre" Date: Fri, 24 Nov 2017 14:59:58 +0100 Subject: [PATCH] Add master DRBG for reseeding A third shared DRBG is added, the so called master DRBG. Its sole purpose is to reseed the two other shared DRBGs, the public and the private DRBG. The randomness for the master DRBG is either pulled from the os entropy sources, or added by the application using the RAND_add() call. The master DRBG reseeds itself automatically after a given number of generate requests, but can also be reseeded using RAND_seed() or RAND_add(). A reseeding of the master DRBG is automatically propagated to the public and private DRBG. This construction fixes the problem, that up to now the randomness provided by RAND_add() was added only to the public and not to the private DRBG. Signed-off-by: Dr. Matthias St. Pierre Reviewed-by: Paul Dale Reviewed-by: Kurt Roeckx (Merged from https://github.com/openssl/openssl/pull/4402) --- crypto/rand/drbg_lib.c | 215 +++++++++++++++++++++++------- crypto/rand/drbg_rand.c | 4 +- crypto/rand/rand_lcl.h | 28 +++- crypto/rand/rand_lib.c | 6 +- include/internal/rand.h | 8 +- ssl/ssl_lib.c | 2 +- test/drbgtest.c | 284 ++++++++++++++++++++++++++++++++++++++-- util/libcrypto.num | 5 +- 8 files changed, 474 insertions(+), 78 deletions(-) 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_master(void) +{ + if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init)) + return NULL; + + return &drbg_master; +} + +/* + * Get the public DRBG. * Returns pointer to the DRBG on success, NULL on failure. */ -RAND_DRBG *RAND_DRBG_get0_global(void) +RAND_DRBG *RAND_DRBG_get0_public(void) { if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init)) return NULL; - return &rand_drbg; + return &drbg_public; } /* - * Get the global private DRBG. + * Get the private DRBG. * Returns pointer to the DRBG on success, NULL on failure. */ -RAND_DRBG *RAND_DRBG_get0_priv_global(void) +RAND_DRBG *RAND_DRBG_get0_private(void) { if (!RUN_ONCE(&rand_drbg_init, do_rand_drbg_init)) return NULL; - return &priv_drbg; + 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 HOOK_CTX master_ctx, public_ctx, private_ctx; + +static HOOK_CTX *get_hook_ctx(RAND_DRBG *drbg) +{ + return (HOOK_CTX *)RAND_DRBG_get_ex_data(drbg, app_data_index); +} + +/* 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; + + 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); +} -static int test_rand_add(void) +/* Resets the given hook context */ +static void reset_hook_ctx(HOOK_CTX *ctx) { - char *p; + 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 + */ - if (!TEST_ptr(p = calloc(RAND_ADD_SIZE, 1))) + /* 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; - RAND_add(p, RAND_ADD_SIZE, RAND_ADD_SIZE); - free(p); + + 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: -- 2.34.1