X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=fips%2Frand%2Ffips_drbg_lib.c;h=32e4b83c5e9f7cdcb83fbc44436b3412dd7e0633;hp=a015da1e41cf5c26fa81aaccd84ca358942d75be;hb=af4bfa151c27d70c94272e3ae53b8a50d648b81d;hpb=beb895083c4794c6134e05dc0fbf0a1a6b919b8e diff --git a/fips/rand/fips_drbg_lib.c b/fips/rand/fips_drbg_lib.c index a015da1e41..32e4b83c5e 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,8 +54,6 @@ #include #include -#include -#include #include #include #include "fips_rand_lcl.h" @@ -68,13 +65,22 @@ 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) { @@ -84,12 +90,22 @@ int FIPS_drbg_init(DRBG_CTX *dctx, int type, unsigned int flags) 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) @@ -97,9 +113,14 @@ DRBG_CTX *FIPS_drbg_new(int type, unsigned int flags) FIPSerr(FIPS_F_FIPS_DRBG_NEW, ERR_R_MALLOC_FAILURE); return NULL; } + if (type == 0) + { + memset(dctx, 0, sizeof(DRBG_CTX)); + dctx->type = 0; + dctx->status = DRBG_STATUS_UNINITIALISED; return dctx; - rv = FIPS_drbg_init(dctx, type, flags); + } if (FIPS_drbg_init(dctx, type, flags) <= 0) { @@ -114,14 +135,65 @@ void FIPS_drbg_free(DRBG_CTX *dctx) { if (dctx->uninstantiate) dctx->uninstantiate(dctx); - OPENSSL_cleanse(dctx, sizeof(DRBG_CTX)); - OPENSSL_free(dctx); + /* Don't free up default DRBG */ + if (dctx == FIPS_get_default_drbg()) + { + memset(dctx, 0, sizeof(DRBG_CTX)); + dctx->type = 0; + dctx->status = DRBG_STATUS_UNINITIALISED; + } + else + { + 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, 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 */ @@ -132,6 +204,7 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, FIPS_R_ERROR_RETRIEVING_ENTROPY); FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, FIPS_R_ERROR_RETRIEVING_NONCE); FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, FIPS_R_INSTANTIATE_ERROR); + FIPSerr(FIPS_F_FIPS_DRBG_INSTANTIATE, FIPS_R_DRBG_NOT_INITIALISED); #endif int r = 0; @@ -142,6 +215,12 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, goto end; } + if (!dctx->instantiate) + { + r = FIPS_R_DRBG_NOT_INITIALISED; + goto end; + } + if (dctx->status != DRBG_STATUS_UNINITIALISED) { if (dctx->status == DRBG_STATUS_ERROR) @@ -153,7 +232,7 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, 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 +243,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 +254,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,32 +266,37 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, 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)); - 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_NOERR)) + 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 - FIPSerr(FIPS_F_FIPS_DRBG_RESEED, FIPS_R_NOT_INSTANTIATED); - FIPSerr(FIPS_F_FIPS_DRBG_RESEED, FIPS_R_ADDITIONAL_INPUT_TOO_LONG); + FIPSerr(FIPS_F_DRBG_RESEED, FIPS_R_NOT_INSTANTIATED); + FIPSerr(FIPS_F_DRBG_RESEED, FIPS_R_ADDITIONAL_INPUT_TOO_LONG); #endif if (dctx->status != DRBG_STATUS_READY && dctx->status != DRBG_STATUS_RESEED) @@ -236,8 +317,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) @@ -246,45 +338,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_NOERR)) - FIPSerr(FIPS_F_FIPS_DRBG_RESEED, r); + if (r && !(dctx->iflags & DRBG_FLAG_NOERR)) + FIPSerr(FIPS_F_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; + } -static int fips_drbg_generate_internal(DRBG_CTX *dctx, - unsigned char *out, size_t outlen, - int strength, int prediction_resistance, +int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen, + 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; @@ -292,97 +429,30 @@ static int fips_drbg_generate_internal(DRBG_CTX *dctx, 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++; - - end: - if (r) - { - if (!(dctx->flags & DRBG_FLAG_NOERR)) - FIPSerr(FIPS_F_FIPS_DRBG_GENERATE_INTERNAL, r); - return 0; - } - - return 1; - } - -/* external generate function: incorporates continuous RNG test if not - * in test mode. - */ - -int FIPS_drbg_generate(DRBG_CTX *dctx, - unsigned char *out, size_t outlen, - int strength, int prediction_resistance, - const unsigned char *adin, size_t adinlen) - { - unsigned char tmp[16], *pout; - size_t poutlen; - /* If test mode don't run continuous RNG test */ - if (dctx->flags & DRBG_FLAG_TEST) - { - return fips_drbg_generate_internal(dctx, out, outlen, - strength, - prediction_resistance, - adin, adinlen); - } - /* If this is the first call generate block and save buffer */ - if (!dctx->lb_valid) + if (!(dctx->iflags & DRBG_CUSTOM_RESEED)) { - if (!fips_drbg_generate_internal(dctx, dctx->lb, 16, - strength, prediction_resistance, - adin, adinlen)) - return 0; - dctx->lb_valid = 1; - } - - /* If request less that 16 bytes request 16 in temp buffer */ - - if (outlen < 16) - { - pout = tmp; - poutlen = 16; - } - else - { - pout = out; - poutlen = outlen; + if (dctx->reseed_counter >= dctx->reseed_interval) + dctx->status = DRBG_STATUS_RESEED; + else + dctx->reseed_counter++; } - /* Generate data */ - if (!fips_drbg_generate_internal(dctx, pout, poutlen, - strength, prediction_resistance, - adin, adinlen)) - return 0; - /* Compare to last block for continuous PRNG test */ - if (!memcmp(pout, dctx->lb, 16)) + end: + if (r) { - FIPSerr(FIPS_F_FIPS_DRBG_GENERATE, FIPS_R_DRBG_STUCK); + if (!(dctx->iflags & DRBG_FLAG_NOERR)) + FIPSerr(FIPS_F_FIPS_DRBG_GENERATE, r); return 0; } - /* Update last block */ - memcpy(dctx->lb, pout, 16); - /* Copy to output buffer if needed */ - if (outlen < 16) - memcpy(out, pout, outlen); return 1; - } int FIPS_drbg_uninstantiate(DRBG_CTX *dctx) @@ -395,22 +465,44 @@ int FIPS_drbg_uninstantiate(DRBG_CTX *dctx) /* Although we'd like to cleanse here we can't because we have to * test the uninstantiate really zeroes the data. */ - memset(dctx, 0, sizeof(DRBG_CTX)); + memset(&dctx->d, 0, sizeof(dctx->d)); + dctx->status = DRBG_STATUS_UNINITIALISED; /* If method has problems uninstantiating, return error */ 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; } @@ -433,3 +525,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; + }