X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=fips%2Frand%2Ffips_drbg_lib.c;h=95c73e191d32fc468d620df0f4a0bada8a62f67a;hp=1c8712e7a5255d3d36f94b4a4ad8dc0d670d4ee6;hb=cf61940534836ebd8d8a13bae0dd2cbd273a3d49;hpb=1b76fac5ae55d2d307f635af4775a7c9149c8551 diff --git a/fips/rand/fips_drbg_lib.c b/fips/rand/fips_drbg_lib.c index 1c8712e7a5..95c73e191d 100644 --- a/fips/rand/fips_drbg_lib.c +++ b/fips/rand/fips_drbg_lib.c @@ -1,4 +1,3 @@ -/* fips/rand/fips_drbg_lib.c */ /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL * project. */ @@ -55,33 +54,58 @@ #include #include -#include -#include #include #include #include "fips_rand_lcl.h" /* Support framework for SP800-90 DRBGs */ -static int fips_drbg_init(DRBG_CTX *dctx, int type, unsigned int flags) +int FIPS_drbg_init(DRBG_CTX *dctx, int type, unsigned int flags) { int rv; memset(dctx, 0, sizeof(DRBG_CTX)); dctx->status = DRBG_STATUS_UNINITIALISED; - dctx->flags = flags; + dctx->xflags = flags; dctx->type = type; + dctx->iflags = 0; + dctx->entropy_blocklen = 0; + dctx->health_check_cnt = 0; + dctx->health_check_interval = DRBG_HEALTH_INTERVAL; + rv = fips_drbg_hash_init(dctx); if (rv == -2) rv = fips_drbg_ctr_init(dctx); + if (rv == -2) + rv = fips_drbg_hmac_init(dctx); + if (rv == -2) + rv = fips_drbg_ec_init(dctx); + + if (rv <= 0) + { + if (rv == -2) + FIPSerr(FIPS_F_FIPS_DRBG_INIT, FIPS_R_UNSUPPORTED_DRBG_TYPE); + else + FIPSerr(FIPS_F_FIPS_DRBG_INIT, FIPS_R_ERROR_INITIALISING_DRBG); + } + + /* If not in test mode run selftests on DRBG of the same type */ + + if (!(dctx->xflags & DRBG_FLAG_TEST)) + { + if (!FIPS_drbg_health_check(dctx)) + { + FIPSerr(FIPS_F_FIPS_DRBG_INIT, FIPS_R_SELFTEST_FAILURE); + return 0; + } + } return rv; } DRBG_CTX *FIPS_drbg_new(int type, unsigned int flags) { - int rv; DRBG_CTX *dctx; dctx = OPENSSL_malloc(sizeof(DRBG_CTX)); if (!dctx) @@ -89,33 +113,77 @@ DRBG_CTX *FIPS_drbg_new(int type, unsigned int flags) FIPSerr(FIPS_F_FIPS_DRBG_NEW, ERR_R_MALLOC_FAILURE); return NULL; } - rv = fips_drbg_init(dctx, type, flags); - if (rv <= 0) + if (type == 0) { - if (rv == -2) - FIPSerr(FIPS_F_FIPS_DRBG_NEW, FIPS_R_UNSUPPORTED_DRBG_TYPE); - else - FIPSerr(FIPS_F_FIPS_DRBG_NEW, FIPS_R_ERROR_INITIALISING_DRBG); + memset(dctx, 0, sizeof(DRBG_CTX)); + dctx->type = 0; + dctx->status = DRBG_STATUS_UNINITIALISED; + return dctx; + } + if (FIPS_drbg_init(dctx, type, flags) <= 0) + { OPENSSL_free(dctx); return NULL; } + return dctx; } void FIPS_drbg_free(DRBG_CTX *dctx) { - dctx->uninstantiate(dctx); - OPENSSL_cleanse(dctx, sizeof(DRBG_CTX)); + if (dctx->uninstantiate) + dctx->uninstantiate(dctx); + OPENSSL_cleanse(&dctx->d, sizeof(dctx->d)); OPENSSL_free(dctx); } +static size_t fips_get_entropy(DRBG_CTX *dctx, unsigned char **pout, + int entropy, size_t min_len, size_t max_len) + { + unsigned char *tout, *p; + size_t bl = dctx->entropy_blocklen, rv; + if (dctx->xflags & DRBG_FLAG_TEST || !bl) + return dctx->get_entropy(dctx, pout, entropy, min_len, max_len); + rv = dctx->get_entropy(dctx, &tout, entropy + bl, + min_len + bl, max_len + bl); + *pout = tout + bl; + if (rv < (min_len + bl) || (rv % bl)) + return 0; + /* Compare consecutive blocks for continuous PRNG test */ + for (p = tout; p < tout + rv - bl; p += bl) + { + if (!memcmp(p, p + bl, bl)) + { + FIPSerr(FIPS_F_FIPS_GET_ENTROPY, FIPS_R_ENTROPY_SOURCE_STUCK); + return 0; + } + } + rv -= bl; + if (rv > max_len) + return max_len; + return rv; + } + +static void fips_cleanup_entropy(DRBG_CTX *dctx, + unsigned char *out, size_t olen) + { + size_t bl; + if (dctx->xflags & DRBG_FLAG_TEST) + bl = 0; + else + bl = dctx->entropy_blocklen; + /* Call cleanup with original arguments */ + dctx->cleanup_entropy(dctx, out - bl, olen + bl); + } + + int FIPS_drbg_instantiate(DRBG_CTX *dctx, - int strength, const unsigned char *pers, size_t perslen) { - size_t entlen, noncelen; + size_t entlen = 0, noncelen = 0; + unsigned char *nonce = NULL, *entropy = NULL; #if 0 /* Put here so error script picks them up */ @@ -145,15 +213,9 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, goto end; } - if (strength > dctx->strength) - { - r = FIPS_R_INSUFFICIENT_SECURITY_STRENGTH; - goto end; - } - dctx->status = DRBG_STATUS_ERROR; - entlen = dctx->get_entropy(dctx, dctx->entropy, dctx->strength, + entlen = fips_get_entropy(dctx, &entropy, dctx->strength, dctx->min_entropy, dctx->max_entropy); if (entlen < dctx->min_entropy || entlen > dctx->max_entropy) @@ -164,8 +226,7 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, if (dctx->max_nonce > 0) { - - noncelen = dctx->get_nonce(dctx, dctx->nonce, + noncelen = dctx->get_nonce(dctx, &nonce, dctx->strength / 2, dctx->min_nonce, dctx->max_nonce); @@ -176,12 +237,10 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, } } - else - noncelen = 0; if (!dctx->instantiate(dctx, - dctx->entropy, entlen, - dctx->nonce, noncelen, + entropy, entlen, + nonce, noncelen, pers, perslen)) { r = FIPS_R_ERROR_INSTANTIATING_DRBG; @@ -190,29 +249,32 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, dctx->status = DRBG_STATUS_READY; - dctx->reseed_counter = 1; - /* Initial test value for reseed interval */ - dctx->reseed_interval = 1<<24; + if (!(dctx->iflags & DRBG_CUSTOM_RESEED)) + dctx->reseed_counter = 1; end: - OPENSSL_cleanse(dctx->entropy, sizeof(dctx->entropy)); - OPENSSL_cleanse(dctx->nonce, sizeof(dctx->nonce)); + if (entropy && dctx->cleanup_entropy) + fips_cleanup_entropy(dctx, entropy, entlen); + + if (nonce && dctx->cleanup_nonce) + dctx->cleanup_nonce(dctx, nonce, noncelen); if (dctx->status == DRBG_STATUS_READY) return 1; - if (r && !(dctx->flags & DRBG_FLAG_TEST)) + if (r && !(dctx->iflags & DRBG_FLAG_NOERR)) FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, r); return 0; } -int FIPS_drbg_reseed(DRBG_CTX *dctx, - const unsigned char *adin, size_t adinlen) +static int drbg_reseed(DRBG_CTX *dctx, + const unsigned char *adin, size_t adinlen, int hcheck) { - size_t entlen; + unsigned char *entropy = NULL; + size_t entlen = 0; int r = 0; #if 0 @@ -238,8 +300,19 @@ int FIPS_drbg_reseed(DRBG_CTX *dctx, } dctx->status = DRBG_STATUS_ERROR; + /* Peform health check on all reseed operations if not a prediction + * resistance request and not in test mode. + */ + if (hcheck && !(dctx->xflags & DRBG_FLAG_TEST)) + { + if (!FIPS_drbg_health_check(dctx)) + { + r = FIPS_R_SELFTEST_FAILURE; + goto end; + } + } - entlen = dctx->get_entropy(dctx, dctx->entropy, dctx->strength, + entlen = fips_get_entropy(dctx, &entropy, dctx->strength, dctx->min_entropy, dctx->max_entropy); if (entlen < dctx->min_entropy || entlen > dctx->max_entropy) @@ -248,44 +321,90 @@ int FIPS_drbg_reseed(DRBG_CTX *dctx, goto end; } - if (!dctx->reseed(dctx, dctx->entropy, entlen, adin, adinlen)) + if (!dctx->reseed(dctx, entropy, entlen, adin, adinlen)) goto end; dctx->status = DRBG_STATUS_READY; - dctx->reseed_counter = 1; + if (!(dctx->iflags & DRBG_CUSTOM_RESEED)) + dctx->reseed_counter = 1; end: - OPENSSL_cleanse(dctx->entropy, sizeof(dctx->entropy)); + + if (entropy && dctx->cleanup_entropy) + fips_cleanup_entropy(dctx, entropy, entlen); if (dctx->status == DRBG_STATUS_READY) return 1; - if (r && !(dctx->flags & DRBG_FLAG_TEST)) + if (r && !(dctx->iflags & DRBG_FLAG_NOERR)) FIPSerr(FIPS_F_FIPS_DRBG_RESEED, r); return 0; } +int FIPS_drbg_reseed(DRBG_CTX *dctx, + const unsigned char *adin, size_t adinlen) + { + return drbg_reseed(dctx, adin, adinlen, 1); + } + +static int fips_drbg_check(DRBG_CTX *dctx) + { + if (dctx->xflags & DRBG_FLAG_TEST) + return 1; + dctx->health_check_cnt++; + if (dctx->health_check_cnt >= dctx->health_check_interval) + { + if (!FIPS_drbg_health_check(dctx)) + { + FIPSerr(FIPS_F_FIPS_DRBG_CHECK, FIPS_R_SELFTEST_FAILURE); + return 0; + } + } + return 1; + } int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen, - int strength, int prediction_resistance, + int prediction_resistance, const unsigned char *adin, size_t adinlen) { int r = 0; + + if (!fips_drbg_check(dctx)) + return 0; + + if (dctx->status != DRBG_STATUS_READY + && dctx->status != DRBG_STATUS_RESEED) + { + if (dctx->status == DRBG_STATUS_ERROR) + r = FIPS_R_IN_ERROR_STATE; + else if(dctx->status == DRBG_STATUS_UNINITIALISED) + r = FIPS_R_NOT_INSTANTIATED; + goto end; + } + if (outlen > dctx->max_request) { r = FIPS_R_REQUEST_TOO_LARGE_FOR_DRBG; return 0; } - if (strength > dctx->strength) + if (adinlen > dctx->max_adin) { - r = FIPS_R_INSUFFICIENT_SECURITY_STRENGTH; + r = FIPS_R_ADDITIONAL_INPUT_TOO_LONG; goto end; } + if (dctx->iflags & DRBG_CUSTOM_RESEED) + dctx->generate(dctx, NULL, outlen, NULL, 0); + else if (dctx->reseed_counter >= dctx->reseed_interval) + dctx->status = DRBG_STATUS_RESEED; + if (dctx->status == DRBG_STATUS_RESEED || prediction_resistance) { - if (!FIPS_drbg_reseed(dctx, adin, adinlen)) + /* If prediction resistance request don't do health check */ + int hcheck = prediction_resistance ? 0 : 1; + + if (!drbg_reseed(dctx, adin, adinlen, hcheck)) { r = FIPS_R_RESEED_ERROR; goto end; @@ -293,29 +412,25 @@ int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen, adin = NULL; adinlen = 0; } - if (dctx->status != DRBG_STATUS_READY) - { - if (dctx->status == DRBG_STATUS_ERROR) - r = FIPS_R_IN_ERROR_STATE; - else if(dctx->status == DRBG_STATUS_UNINITIALISED) - r = FIPS_R_NOT_INSTANTIATED; - goto end; - } + if (!dctx->generate(dctx, out, outlen, adin, adinlen)) { r = FIPS_R_GENERATE_ERROR; dctx->status = DRBG_STATUS_ERROR; goto end; } - if (dctx->reseed_counter > dctx->reseed_interval) - dctx->status = DRBG_STATUS_RESEED; - else - dctx->reseed_counter++; + if (!(dctx->iflags & DRBG_CUSTOM_RESEED)) + { + if (dctx->reseed_counter >= dctx->reseed_interval) + dctx->status = DRBG_STATUS_RESEED; + else + dctx->reseed_counter++; + } end: if (r) { - if (!(dctx->flags & DRBG_FLAG_TEST)) + if (!(dctx->iflags & DRBG_FLAG_NOERR)) FIPSerr(FIPS_F_FIPS_DRBG_GENERATE, r); return 0; } @@ -325,28 +440,52 @@ int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen, int FIPS_drbg_uninstantiate(DRBG_CTX *dctx) { - int save_type, save_flags, rv; - save_type = dctx->type; - save_flags = dctx->flags; - rv = dctx->uninstantiate(dctx); - OPENSSL_cleanse(dctx, sizeof(DRBG_CTX)); + int rv; + if (!dctx->uninstantiate) + rv = 1; + else + rv = dctx->uninstantiate(dctx); + /* Although we'd like to cleanse here we can't because we have to + * test the uninstantiate really zeroes the data. + */ + memset(&dctx->d, 0, sizeof(dctx->d)); + dctx->status = DRBG_STATUS_UNINITIALISED; /* If method has problems uninstantiating, return error */ - if (rv <= 0) - return rv; - return fips_drbg_init(dctx, save_type, save_flags); + return rv; } -int FIPS_drbg_set_test_mode(DRBG_CTX *dctx, - size_t (*get_entropy)(DRBG_CTX *ctx, unsigned char *out, +int FIPS_drbg_set_callbacks(DRBG_CTX *dctx, + size_t (*get_entropy)(DRBG_CTX *ctx, unsigned char **pout, + int entropy, size_t min_len, size_t max_len), + void (*cleanup_entropy)(DRBG_CTX *ctx, unsigned char *out, size_t olen), + size_t entropy_blocklen, + size_t (*get_nonce)(DRBG_CTX *ctx, unsigned char **pout, int entropy, size_t min_len, size_t max_len), - size_t (*get_nonce)(DRBG_CTX *ctx, unsigned char *out, - int entropy, size_t min_len, size_t max_len)) + void (*cleanup_nonce)(DRBG_CTX *ctx, unsigned char *out, size_t olen)) { if (dctx->status != DRBG_STATUS_UNINITIALISED) return 0; - dctx->flags |= DRBG_FLAG_TEST; + dctx->entropy_blocklen = entropy_blocklen; dctx->get_entropy = get_entropy; + dctx->cleanup_entropy = cleanup_entropy; dctx->get_nonce = get_nonce; + dctx->cleanup_nonce = cleanup_nonce; + return 1; + } + +int FIPS_drbg_set_rand_callbacks(DRBG_CTX *dctx, + size_t (*get_adin)(DRBG_CTX *ctx, unsigned char **pout), + void (*cleanup_adin)(DRBG_CTX *ctx, unsigned char *out, size_t olen), + int (*rand_seed_cb)(DRBG_CTX *ctx, const void *buf, int num), + int (*rand_add_cb)(DRBG_CTX *ctx, + const void *buf, int num, double entropy)) + { + if (dctx->status != DRBG_STATUS_UNINITIALISED) + return 0; + dctx->get_adin = get_adin; + dctx->cleanup_adin = cleanup_adin; + dctx->rand_seed_cb = rand_seed_cb; + dctx->rand_add_cb = rand_add_cb; return 1; } @@ -369,3 +508,47 @@ int FIPS_drbg_get_strength(DRBG_CTX *dctx) { return dctx->strength; } + +void FIPS_drbg_set_check_interval(DRBG_CTX *dctx, int interval) + { + dctx->health_check_interval = interval; + } + +void FIPS_drbg_set_reseed_interval(DRBG_CTX *dctx, int interval) + { + dctx->reseed_interval = interval; + } + +static int drbg_stick = 0; + +void FIPS_drbg_stick(void) + { + drbg_stick = 1; + } + +/* Continuous DRBG utility function */ +int fips_drbg_cprng_test(DRBG_CTX *dctx, const unsigned char *out) + { + /* No CPRNG in test mode */ + if (dctx->xflags & DRBG_FLAG_TEST) + return 1; + /* Check block is valid: should never happen */ + if (dctx->lb_valid == 0) + { + FIPSerr(FIPS_F_FIPS_DRBG_CPRNG_TEST, FIPS_R_INTERNAL_ERROR); + fips_set_selftest_fail(); + return 0; + } + if (drbg_stick) + memcpy(dctx->lb, out, dctx->blocklength); + /* Check against last block: fail if match */ + if (!memcmp(dctx->lb, out, dctx->blocklength)) + { + FIPSerr(FIPS_F_FIPS_DRBG_CPRNG_TEST, FIPS_R_DRBG_STUCK); + fips_set_selftest_fail(); + return 0; + } + /* Save last block for next comparison */ + memcpy(dctx->lb, out, dctx->blocklength); + return 1; + }