X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=fips%2Frand%2Ffips_drbg_lib.c;h=98bd10bce374024434ac37d875a8f0374575c98a;hp=a015da1e41cf5c26fa81aaccd84ca358942d75be;hb=de2132de937217353d0a4a0aafa087d263a23ba0;hpb=beb895083c4794c6134e05dc0fbf0a1a6b919b8e diff --git a/fips/rand/fips_drbg_lib.c b/fips/rand/fips_drbg_lib.c index a015da1e41..98bd10bce3 100644 --- a/fips/rand/fips_drbg_lib.c +++ b/fips/rand/fips_drbg_lib.c @@ -55,8 +55,6 @@ #include #include -#include -#include #include #include #include "fips_rand_lcl.h" @@ -71,10 +69,18 @@ int FIPS_drbg_init(DRBG_CTX *dctx, int type, unsigned int flags) dctx->flags = flags; dctx->type = type; + 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,24 @@ 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->flags & DRBG_FLAG_TEST)) + { + DRBG_CTX tctx; + if (!fips_drbg_kat(&tctx, type, flags | DRBG_FLAG_TEST)) + { + FIPSerr(FIPS_F_FIPS_DRBG_INIT, FIPS_R_SELFTEST_FAILURE); + dctx->status = DRBG_STATUS_ERROR; + 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 +115,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 +137,55 @@ void FIPS_drbg_free(DRBG_CTX *dctx) { if (dctx->uninstantiate) dctx->uninstantiate(dctx); - OPENSSL_cleanse(dctx, sizeof(DRBG_CTX)); + 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->flags & 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->flags & 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 */ @@ -153,7 +217,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 +228,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 +239,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,12 +251,16 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, dctx->status = DRBG_STATUS_READY; - dctx->reseed_counter = 1; + if (!(dctx->flags & 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; @@ -210,6 +275,7 @@ int FIPS_drbg_instantiate(DRBG_CTX *dctx, int FIPS_drbg_reseed(DRBG_CTX *dctx, const unsigned char *adin, size_t adinlen) { + unsigned char *entropy = NULL; size_t entlen; int r = 0; @@ -237,7 +303,7 @@ int FIPS_drbg_reseed(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) @@ -246,13 +312,16 @@ 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->flags & 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; @@ -263,24 +332,55 @@ int FIPS_drbg_reseed(DRBG_CTX *dctx, return 0; } +static int fips_drbg_check(DRBG_CTX *dctx) + { + if (dctx->flags & DRBG_FLAG_TEST) + return 1; + dctx->health_check_cnt++; + if (dctx->health_check_cnt >= dctx->health_check_interval) + { + DRBG_CTX tctx; + if (!fips_drbg_kat(&tctx, dctx->type, + dctx->flags | DRBG_FLAG_TEST)) + { + FIPSerr(FIPS_F_FIPS_DRBG_CHECK, FIPS_R_SELFTEST_FAILURE); + dctx->status = DRBG_STATUS_ERROR; + return 0; + } + dctx->health_check_cnt = 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) - { - r = FIPS_R_INSUFFICIENT_SECURITY_STRENGTH; - goto end; - } + if (dctx->flags & 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) { @@ -292,97 +392,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++; + if (!(dctx->flags & 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)) - 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 (!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; - } - - /* 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)) - { - FIPSerr(FIPS_F_FIPS_DRBG_GENERATE, FIPS_R_DRBG_STUCK); + 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 +428,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), - size_t (*get_nonce)(DRBG_CTX *ctx, unsigned char *out, - 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->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 +488,42 @@ 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; + } + +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->flags & 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; + }