/* * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved. * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ /* * Implementation of the FIPS 140-2 section 4.9.2 Conditional Tests. */ #include #include #include #include #include #include "prov/providercommon.h" #include "prov/provider_ctx.h" #include "internal/cryptlib.h" #include "crypto/rand_pool.h" #include "drbg_local.h" #include "prov/seeding.h" #include "crypto/context.h" typedef struct crng_test_global_st { unsigned char crngt_prev[EVP_MAX_MD_SIZE]; EVP_MD *md; int preloaded; CRYPTO_RWLOCK *lock; } CRNG_TEST_GLOBAL; static int crngt_get_entropy(PROV_CTX *provctx, const EVP_MD *digest, unsigned char *buf, unsigned char *md, unsigned int *md_size) { int r; size_t n; unsigned char *p; n = ossl_prov_get_entropy(provctx, &p, 0, CRNGT_BUFSIZ, CRNGT_BUFSIZ); if (n == CRNGT_BUFSIZ) { r = EVP_Digest(p, CRNGT_BUFSIZ, md, md_size, digest, NULL); if (r != 0) memcpy(buf, p, CRNGT_BUFSIZ); ossl_prov_cleanup_entropy(provctx, p, n); return r != 0; } if (n != 0) ossl_prov_cleanup_entropy(provctx, p, n); return 0; } void ossl_rand_crng_ctx_free(void *vcrngt_glob) { CRNG_TEST_GLOBAL *crngt_glob = vcrngt_glob; CRYPTO_THREAD_lock_free(crngt_glob->lock); EVP_MD_free(crngt_glob->md); OPENSSL_free(crngt_glob); } void *ossl_rand_crng_ctx_new(OSSL_LIB_CTX *ctx) { CRNG_TEST_GLOBAL *crngt_glob = OPENSSL_zalloc(sizeof(*crngt_glob)); if (crngt_glob == NULL) return NULL; if ((crngt_glob->md = EVP_MD_fetch(ctx, "SHA256", "")) == NULL) { OPENSSL_free(crngt_glob); return NULL; } if ((crngt_glob->lock = CRYPTO_THREAD_lock_new()) == NULL) { EVP_MD_free(crngt_glob->md); OPENSSL_free(crngt_glob); return NULL; } return crngt_glob; } static int prov_crngt_compare_previous(const unsigned char *prev, const unsigned char *cur, size_t sz) { const int res = memcmp(prev, cur, sz) != 0; if (!res) ossl_set_error_state(OSSL_SELF_TEST_TYPE_CRNG); return res; } size_t ossl_crngt_get_entropy(PROV_DRBG *drbg, unsigned char **pout, int entropy, size_t min_len, size_t max_len, int prediction_resistance) { unsigned char md[EVP_MAX_MD_SIZE]; unsigned char buf[CRNGT_BUFSIZ]; unsigned char *ent, *entp, *entbuf; unsigned int sz; size_t bytes_needed; size_t r = 0, s, t; int crng_test_pass = 1; OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(drbg->provctx); CRNG_TEST_GLOBAL *crngt_glob = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_RAND_CRNGT_INDEX); OSSL_CALLBACK *stcb = NULL; void *stcbarg = NULL; OSSL_SELF_TEST *st = NULL; if (crngt_glob == NULL) return 0; if (!CRYPTO_THREAD_write_lock(crngt_glob->lock)) return 0; if (!crngt_glob->preloaded) { if (!crngt_get_entropy(drbg->provctx, crngt_glob->md, buf, crngt_glob->crngt_prev, NULL)) { OPENSSL_cleanse(buf, sizeof(buf)); goto unlock_return; } crngt_glob->preloaded = 1; } /* * Calculate how many bytes of seed material we require, rounded up * to the nearest byte. If the entropy is of less than full quality, * the amount required should be scaled up appropriately here. */ bytes_needed = (entropy + 7) / 8; if (bytes_needed < min_len) bytes_needed = min_len; if (bytes_needed > max_len) goto unlock_return; entp = ent = OPENSSL_secure_malloc(bytes_needed); if (ent == NULL) goto unlock_return; OSSL_SELF_TEST_get_callback(libctx, &stcb, &stcbarg); if (stcb != NULL) { st = OSSL_SELF_TEST_new(stcb, stcbarg); if (st == NULL) goto err; OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_CRNG, OSSL_SELF_TEST_DESC_RNG); } for (t = bytes_needed; t > 0;) { /* Care needs to be taken to avoid overrunning the buffer */ s = t >= CRNGT_BUFSIZ ? CRNGT_BUFSIZ : t; entbuf = t >= CRNGT_BUFSIZ ? entp : buf; if (!crngt_get_entropy(drbg->provctx, crngt_glob->md, entbuf, md, &sz)) goto err; if (t < CRNGT_BUFSIZ) memcpy(entp, buf, t); /* Force a failure here if the callback returns 1 */ if (OSSL_SELF_TEST_oncorrupt_byte(st, md)) memcpy(md, crngt_glob->crngt_prev, sz); if (!prov_crngt_compare_previous(crngt_glob->crngt_prev, md, sz)) { crng_test_pass = 0; goto err; } /* Update for next block */ memcpy(crngt_glob->crngt_prev, md, sz); entp += s; t -= s; } r = bytes_needed; *pout = ent; ent = NULL; err: OSSL_SELF_TEST_onend(st, crng_test_pass); OSSL_SELF_TEST_free(st); OPENSSL_secure_clear_free(ent, bytes_needed); unlock_return: CRYPTO_THREAD_unlock(crngt_glob->lock); return r; } void ossl_crngt_cleanup_entropy(ossl_unused PROV_DRBG *drbg, unsigned char *out, size_t outlen) { OPENSSL_secure_clear_free(out, outlen); }