| /* |
| * Copyright 2019-2021 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 <string.h> |
| #include <openssl/evp.h> |
| #include <openssl/core_dispatch.h> |
| #include <openssl/params.h> |
| #include <openssl/self_test.h> |
| #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); |
| } |