X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=fips%2Frand%2Ffips_drbg_lib.c;h=ee162d05eb0281d42548ec0365262ae79631943b;hp=7a0a1070e2b21dd740125a1e3256a1ee4bed2ff6;hb=85a1a836a2b865cb0ff8ee15647e5c5a8c7e464f;hpb=42bd0a6b3c73c71b69967503c0ee0a3598b52655 diff --git a/fips/rand/fips_drbg_lib.c b/fips/rand/fips_drbg_lib.c index 7a0a1070e2..ee162d05eb 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,10 +135,62 @@ void FIPS_drbg_free(DRBG_CTX *dctx) { if (dctx->uninstantiate) dctx->uninstantiate(dctx); - OPENSSL_cleanse(&dctx->d, sizeof(dctx->d)); - 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->get_entropy) + return 0; + 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) { @@ -133,6 +206,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; @@ -143,6 +217,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) @@ -154,7 +234,7 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, dctx->status = DRBG_STATUS_ERROR; - entlen = dctx->get_entropy(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) @@ -163,7 +243,7 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, goto end; } - if (dctx->max_nonce > 0) + if (dctx->max_nonce > 0 && dctx->get_nonce) { noncelen = dctx->get_nonce(dctx, &nonce, dctx->strength / 2, @@ -188,12 +268,13 @@ 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: if (entropy && dctx->cleanup_entropy) - dctx->cleanup_entropy(dctx, entropy, entlen); + fips_cleanup_entropy(dctx, entropy, entlen); if (nonce && dctx->cleanup_nonce) dctx->cleanup_nonce(dctx, nonce, noncelen); @@ -201,23 +282,23 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, 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) { unsigned char *entropy = NULL; - size_t entlen; + 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) @@ -238,8 +319,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, &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) @@ -252,28 +344,59 @@ int FIPS_drbg_reseed(DRBG_CTX *dctx, goto end; dctx->status = DRBG_STATUS_READY; - dctx->reseed_counter = 1; + if (!(dctx->iflags & DRBG_CUSTOM_RESEED)) + dctx->reseed_counter = 1; end: if (entropy && dctx->cleanup_entropy) - dctx->cleanup_entropy(dctx, entropy, entlen); + 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; + } 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_selftest_failed()) + { + FIPSerr(FIPS_F_FIPS_DRBG_GENERATE, FIPS_R_SELFTEST_FAILED); + return 0; + } + + if (!fips_drbg_check(dctx)) + return 0; + if (dctx->status != DRBG_STATUS_READY && dctx->status != DRBG_STATUS_RESEED) { @@ -290,15 +413,23 @@ int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen, 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; @@ -313,15 +444,18 @@ int FIPS_drbg_generate(DRBG_CTX *dctx, unsigned char *out, size_t outlen, 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_NOERR)) + if (!(dctx->iflags & DRBG_FLAG_NOERR)) FIPSerr(FIPS_F_FIPS_DRBG_GENERATE, r); return 0; } @@ -349,12 +483,14 @@ 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), void (*cleanup_nonce)(DRBG_CTX *ctx, unsigned char *out, size_t olen)) { if (dctx->status != DRBG_STATUS_UNINITIALISED) return 0; + dctx->entropy_blocklen = entropy_blocklen; dctx->get_entropy = get_entropy; dctx->cleanup_entropy = cleanup_entropy; dctx->get_nonce = get_nonce; @@ -398,23 +534,33 @@ 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) +void FIPS_drbg_stick(int onoff) { - drbg_stick = 1; + drbg_stick = onoff; } /* Continuous DRBG utility function */ int fips_drbg_cprng_test(DRBG_CTX *dctx, const unsigned char *out) { /* No CPRNG in test mode */ - if (dctx->flags & DRBG_FLAG_TEST) + if (dctx->xflags & DRBG_FLAG_TEST) return 1; /* Check block is valid: should never happen */ if (dctx->lb_valid == 0) { - FIPSerr(FIPS_F_DRBG_CPRNG_TEST, FIPS_R_INTERNAL_ERROR); + FIPSerr(FIPS_F_FIPS_DRBG_CPRNG_TEST, FIPS_R_INTERNAL_ERROR); fips_set_selftest_fail(); return 0; } @@ -423,7 +569,7 @@ int fips_drbg_cprng_test(DRBG_CTX *dctx, const unsigned char *out) /* Check against last block: fail if match */ if (!memcmp(dctx->lb, out, dctx->blocklength)) { - FIPSerr(FIPS_F_DRBG_CPRNG_TEST, FIPS_R_DRBG_STUCK); + FIPSerr(FIPS_F_FIPS_DRBG_CPRNG_TEST, FIPS_R_DRBG_STUCK); fips_set_selftest_fail(); return 0; }