diff options
author | Pauli <ppzgs1@gmail.com> | 2024-09-05 02:24:07 +0200 |
---|---|---|
committer | Pauli <ppzgs1@gmail.com> | 2024-09-19 00:44:14 +0200 |
commit | 6f20c6804e639230a2228810be0f682d452743e0 (patch) | |
tree | 62f7f0aa03be08ef768dc1b3eb9b5a27e807f434 | |
parent | rand: remove unused field in DRBG structure (diff) | |
download | openssl-6f20c6804e639230a2228810be0f682d452743e0.tar.xz openssl-6f20c6804e639230a2228810be0f682d452743e0.zip |
fips: continuous random bit generator tests
For FIPS 140-3 the continuous tests specified in SP 800-90B need to be
included on the output of any entropy source.
They are implemented here as a replacement for the primary DRBG in the FIPS
provider. This results in a setup that looks like this:
+-------------+
| |
| Seed Source |
| |
+------+------+
|
|
v
+-------------+
| |
| CRNG Test |
| |
++----------+-+
| |
| |
v v
+--------------+ +--------------+
| | | |
| Public DRBG | | Private DRBG |
| | | |
+--------------+ +--------------+
An additional benefit, that of avoiding DRBG chains, is also gained.
The current standards do not permit the output of one DRBG to be used
as the input for a second (i.e. a chain).
This also leaves open the future possibility of incorporating a seed
source inside the FIPS boundary.
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25415)
-rw-r--r-- | crypto/context.c | 47 | ||||
-rw-r--r-- | crypto/rand/rand_lib.c | 43 | ||||
-rw-r--r-- | include/internal/cryptlib.h | 2 | ||||
-rw-r--r-- | providers/fips/fipsprov.c | 1 | ||||
-rw-r--r-- | providers/implementations/include/prov/implementations.h | 1 | ||||
-rw-r--r-- | providers/implementations/include/prov/names.h | 1 | ||||
-rw-r--r-- | providers/implementations/rands/build.info | 3 | ||||
-rw-r--r-- | providers/implementations/rands/crngt.c | 192 | ||||
-rw-r--r-- | providers/implementations/rands/drbg.c | 12 | ||||
-rw-r--r-- | providers/implementations/rands/fips_crng_test.c | 434 |
10 files changed, 478 insertions, 258 deletions
diff --git a/crypto/context.c b/crypto/context.c index f3d9f384fc..96216abcda 100644 --- a/crypto/context.c +++ b/crypto/context.c @@ -19,7 +19,7 @@ #include "crypto/context.h" struct ossl_lib_ctx_st { - CRYPTO_RWLOCK *lock, *rand_crngt_lock; + CRYPTO_RWLOCK *lock; OSSL_EX_DATA_GLOBAL global; void *property_string_data; @@ -45,7 +45,6 @@ struct ossl_lib_ctx_st { #if defined(OPENSSL_THREADS) void *threads; #endif - void *rand_crngt; #ifdef FIPS_MODULE void *thread_event_handler; void *fips_prov; @@ -93,10 +92,6 @@ static int context_init(OSSL_LIB_CTX *ctx) if (ctx->lock == NULL) goto err; - ctx->rand_crngt_lock = CRYPTO_THREAD_lock_new(); - if (ctx->rand_crngt_lock == NULL) - goto err; - /* Initialize ex_data. */ if (!ossl_do_ex_data_init(ctx)) goto err; @@ -222,7 +217,6 @@ static int context_init(OSSL_LIB_CTX *ctx) if (exdata_done) ossl_crypto_cleanup_all_ex_data_int(ctx); - CRYPTO_THREAD_lock_free(ctx->rand_crngt_lock); CRYPTO_THREAD_lock_free(ctx->lock); CRYPTO_THREAD_cleanup_local(&ctx->rcu_local_key); memset(ctx, '\0', sizeof(*ctx)); @@ -328,11 +322,6 @@ static void context_deinit_objs(OSSL_LIB_CTX *ctx) } #endif - if (ctx->rand_crngt != NULL) { - ossl_rand_crng_ctx_free(ctx->rand_crngt); - ctx->rand_crngt = NULL; - } - #ifdef FIPS_MODULE if (ctx->thread_event_handler != NULL) { ossl_thread_event_ctx_free(ctx->thread_event_handler); @@ -380,9 +369,7 @@ static int context_deinit(OSSL_LIB_CTX *ctx) ossl_crypto_cleanup_all_ex_data_int(ctx); - CRYPTO_THREAD_lock_free(ctx->rand_crngt_lock); CRYPTO_THREAD_lock_free(ctx->lock); - ctx->rand_crngt_lock = NULL; ctx->lock = NULL; CRYPTO_THREAD_cleanup_local(&ctx->rcu_local_key); return 1; @@ -573,8 +560,6 @@ int ossl_lib_ctx_is_global_default(OSSL_LIB_CTX *ctx) void *ossl_lib_ctx_get_data(OSSL_LIB_CTX *ctx, int index) { - void *p; - ctx = ossl_lib_ctx_get_concrete(ctx); if (ctx == NULL) return NULL; @@ -621,36 +606,6 @@ void *ossl_lib_ctx_get_data(OSSL_LIB_CTX *ctx, int index) return ctx->threads; #endif - case OSSL_LIB_CTX_RAND_CRNGT_INDEX: { - - /* - * rand_crngt must be lazily initialized because it calls into - * libctx, so must not be called from context_init, else a deadlock - * will occur. - * - * We use a separate lock because code called by the instantiation - * of rand_crngt is liable to try and take the libctx lock. - */ - if (CRYPTO_THREAD_read_lock(ctx->rand_crngt_lock) != 1) - return NULL; - - if (ctx->rand_crngt == NULL) { - CRYPTO_THREAD_unlock(ctx->rand_crngt_lock); - - if (CRYPTO_THREAD_write_lock(ctx->rand_crngt_lock) != 1) - return NULL; - - if (ctx->rand_crngt == NULL) - ctx->rand_crngt = ossl_rand_crng_ctx_new(ctx); - } - - p = ctx->rand_crngt; - - CRYPTO_THREAD_unlock(ctx->rand_crngt_lock); - - return p; - } - #ifdef FIPS_MODULE case OSSL_LIB_CTX_THREAD_EVENT_HANDLER_INDEX: return ctx->thread_event_handler; diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c index b117a67f91..9e147f27bb 100644 --- a/crypto/rand/rand_lib.c +++ b/crypto/rand/rand_lib.c @@ -696,6 +696,33 @@ static EVP_RAND_CTX *rand_new_drbg(OSSL_LIB_CTX *libctx, EVP_RAND_CTX *parent, return ctx; } +#ifdef FIPS_MODULE +static EVP_RAND_CTX *rand_new_crngt(OSSL_LIB_CTX *libctx, EVP_RAND_CTX *parent) +{ + EVP_RAND *rand; + EVP_RAND_CTX *ctx; + + rand = EVP_RAND_fetch(libctx, "CRNG-TEST", "fips=no"); + if (rand == NULL) { + ERR_raise(ERR_LIB_RAND, RAND_R_UNABLE_TO_FETCH_DRBG); + return NULL; + } + ctx = EVP_RAND_CTX_new(rand, parent); + EVP_RAND_free(rand); + if (ctx == NULL) { + ERR_raise(ERR_LIB_RAND, RAND_R_UNABLE_TO_CREATE_DRBG); + return NULL; + } + + if (!EVP_RAND_instantiate(ctx, 0, 0, NULL, 0, NULL)) { + ERR_raise(ERR_LIB_RAND, RAND_R_ERROR_INSTANTIATING_DRBG); + EVP_RAND_CTX_free(ctx); + return NULL; + } + return ctx; +} +#endif + /* * Get the primary random generator. * Returns pointer to its EVP_RAND_CTX on success, NULL on failure. @@ -727,21 +754,23 @@ EVP_RAND_CTX *RAND_get0_primary(OSSL_LIB_CTX *ctx) return ret; } -#ifndef FIPS_MODULE +#ifdef FIPS_MODULE + ret = rand_new_crngt(ctx, dgbl->seed); +#else if (dgbl->seed == NULL) { ERR_set_mark(); dgbl->seed = rand_new_seed(ctx); ERR_pop_to_mark(); } + ret = rand_new_drbg(ctx, dgbl->seed, PRIMARY_RESEED_INTERVAL, + PRIMARY_RESEED_TIME_INTERVAL, 1); #endif - ret = dgbl->primary = rand_new_drbg(ctx, dgbl->seed, - PRIMARY_RESEED_INTERVAL, - PRIMARY_RESEED_TIME_INTERVAL, 1); /* - * The primary DRBG may be shared between multiple threads so we must - * enable locking. - */ + * The primary DRBG may be shared between multiple threads so we must + * enable locking. + */ + dgbl->primary = ret; if (ret != NULL && !EVP_RAND_enable_locking(ret)) { ERR_raise(ERR_LIB_EVP, EVP_R_UNABLE_TO_ENABLE_LOCKING); EVP_RAND_CTX_free(ret); diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h index 6b90b729e3..3227f9fcf9 100644 --- a/include/internal/cryptlib.h +++ b/include/internal/cryptlib.h @@ -100,7 +100,7 @@ typedef struct ossl_ex_data_global_st { # define OSSL_LIB_CTX_NAMEMAP_INDEX 4 # define OSSL_LIB_CTX_DRBG_INDEX 5 # define OSSL_LIB_CTX_DRBG_NONCE_INDEX 6 -# define OSSL_LIB_CTX_RAND_CRNGT_INDEX 7 +/* slot 7 unused, was CRNG test data and can be reused */ # ifdef FIPS_MODULE # define OSSL_LIB_CTX_THREAD_EVENT_HANDLER_INDEX 8 # endif diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c index c446367dea..561f7123ad 100644 --- a/providers/fips/fipsprov.c +++ b/providers/fips/fipsprov.c @@ -386,6 +386,7 @@ static const OSSL_ALGORITHM fips_kdfs[] = { }; static const OSSL_ALGORITHM fips_rands[] = { + { PROV_NAMES_CRNG_TEST, FIPS_UNAPPROVED_PROPERTIES, ossl_crng_test_functions }, { PROV_NAMES_CTR_DRBG, FIPS_DEFAULT_PROPERTIES, ossl_drbg_ctr_functions }, { PROV_NAMES_HASH_DRBG, FIPS_DEFAULT_PROPERTIES, ossl_drbg_hash_functions }, { PROV_NAMES_HMAC_DRBG, FIPS_DEFAULT_PROPERTIES, ossl_drbg_ossl_hmac_functions }, diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index 66f42e0a14..5b91905657 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -296,6 +296,7 @@ extern const OSSL_DISPATCH ossl_kdf_argon2id_functions[]; extern const OSSL_DISPATCH ossl_test_rng_functions[]; extern const OSSL_DISPATCH ossl_seed_src_functions[]; extern const OSSL_DISPATCH ossl_jitter_functions[]; +extern const OSSL_DISPATCH ossl_crng_test_functions[]; extern const OSSL_DISPATCH ossl_drbg_hash_functions[]; extern const OSSL_DISPATCH ossl_drbg_ossl_hmac_functions[]; extern const OSSL_DISPATCH ossl_drbg_ctr_functions[]; diff --git a/providers/implementations/include/prov/names.h b/providers/implementations/include/prov/names.h index 05df498d0e..d422dbac29 100644 --- a/providers/implementations/include/prov/names.h +++ b/providers/implementations/include/prov/names.h @@ -306,6 +306,7 @@ * RANDs * ----- */ +#define PROV_NAMES_CRNG_TEST "CRNG-TEST" #define PROV_NAMES_CTR_DRBG "CTR-DRBG" #define PROV_NAMES_HASH_DRBG "HASH-DRBG" #define PROV_NAMES_HMAC_DRBG "HMAC-DRBG" diff --git a/providers/implementations/rands/build.info b/providers/implementations/rands/build.info index 70f8454193..8e0894faa6 100644 --- a/providers/implementations/rands/build.info +++ b/providers/implementations/rands/build.info @@ -2,5 +2,6 @@ SUBDIRS=seeding $RANDS_GOAL=../../libdefault.a ../../libfips.a -SOURCE[$RANDS_GOAL]=drbg.c test_rng.c drbg_ctr.c drbg_hash.c drbg_hmac.c crngt.c +SOURCE[$RANDS_GOAL]=drbg.c test_rng.c drbg_ctr.c drbg_hash.c drbg_hmac.c SOURCE[../../libdefault.a]=seed_src.c seed_src_jitter.c +SOURCE[../../libfips.a]=fips_crng_test.c diff --git a/providers/implementations/rands/crngt.c b/providers/implementations/rands/crngt.c deleted file mode 100644 index fa4a2db14a..0000000000 --- a/providers/implementations/rands/crngt.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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 <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); -} diff --git a/providers/implementations/rands/drbg.c b/providers/implementations/rands/drbg.c index 255bf9b507..f9aa5e2fae 100644 --- a/providers/implementations/rands/drbg.c +++ b/providers/implementations/rands/drbg.c @@ -197,18 +197,12 @@ static size_t get_entropy(PROV_DRBG *drbg, unsigned char **pout, int entropy, unsigned int p_str; if (drbg->parent == NULL) -#ifdef FIPS_MODULE - return ossl_crngt_get_entropy(drbg, pout, entropy, min_len, max_len, - prediction_resistance); -#else /* * In normal use (i.e. OpenSSL's own uses), this is never called. - * Outside of the FIPS provider, OpenSSL sets its DRBGs up so that - * they always have a parent. This remains purely for legacy reasons. + * This remains purely for legacy reasons. */ return ossl_prov_get_entropy(drbg->provctx, pout, entropy, min_len, max_len); -#endif if (drbg->parent_get_seed == NULL) { ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED); @@ -251,11 +245,7 @@ static size_t get_entropy(PROV_DRBG *drbg, unsigned char **pout, int entropy, static void cleanup_entropy(PROV_DRBG *drbg, unsigned char *out, size_t outlen) { if (drbg->parent == NULL) { -#ifdef FIPS_MODULE - ossl_crngt_cleanup_entropy(drbg, out, outlen); -#else ossl_prov_cleanup_entropy(drbg->provctx, out, outlen); -#endif } else if (drbg->parent_clear_seed != NULL) { if (!ossl_drbg_lock_parent(drbg)) return; diff --git a/providers/implementations/rands/fips_crng_test.c b/providers/implementations/rands/fips_crng_test.c new file mode 100644 index 0000000000..057ae6e35e --- /dev/null +++ b/providers/implementations/rands/fips_crng_test.c @@ -0,0 +1,434 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. 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 SP 800-90B section 4.4 Approved Continuous Health Tests. + */ + +#include <string.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/params.h> +#include <openssl/self_test.h> +#include <openssl/proverr.h> +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/implementations.h" +#include "internal/cryptlib.h" +#include "crypto/rand_pool.h" +#include "drbg_local.h" +#include "prov/seeding.h" +#include "crypto/context.h" + +static OSSL_FUNC_rand_newctx_fn crng_test_new; +static OSSL_FUNC_rand_freectx_fn crng_test_free; +static OSSL_FUNC_rand_instantiate_fn crng_test_instantiate; +static OSSL_FUNC_rand_uninstantiate_fn crng_test_uninstantiate; +static OSSL_FUNC_rand_generate_fn crng_test_generate; +static OSSL_FUNC_rand_reseed_fn crng_test_reseed; +static OSSL_FUNC_rand_gettable_ctx_params_fn crng_test_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn crng_test_get_ctx_params; +static OSSL_FUNC_rand_verify_zeroization_fn crng_test_verify_zeroization; +static OSSL_FUNC_rand_enable_locking_fn crng_test_enable_locking; +static OSSL_FUNC_rand_lock_fn crng_test_lock; +static OSSL_FUNC_rand_unlock_fn crng_test_unlock; +static OSSL_FUNC_rand_get_seed_fn crng_test_get_seed; +static OSSL_FUNC_rand_clear_seed_fn crng_test_clear_seed; + +#ifndef ENTROPY_H +# define ENTROPY_H 6 /* default to six bits per byte of entropy */ +#endif +#ifndef ENTROPY_APT_W +# define ENTROPY_APT_W 512 +#endif + +typedef struct crng_testal_st { + void *provctx; + CRYPTO_RWLOCK *lock; + int state; + + /* State for SP 800-90B 4.4.1 Repetition Count Test */ + struct { + unsigned int b; + uint8_t a; + } rct; + + /* State for SP 800-90B 4.4.2 Adaptive Proportion Test */ + struct { + unsigned int b; + unsigned int i; + uint8_t a; + } apt; + + /* Parent PROV_RAND and its dispatch table functions */ + void *parent; + OSSL_FUNC_rand_enable_locking_fn *parent_enable_locking; + OSSL_FUNC_rand_lock_fn *parent_lock; + OSSL_FUNC_rand_unlock_fn *parent_unlock; + OSSL_FUNC_rand_get_ctx_params_fn *parent_get_ctx_params; + OSSL_FUNC_rand_gettable_ctx_params_fn *parent_gettable_ctx_params; + OSSL_FUNC_rand_get_seed_fn *parent_get_seed; + OSSL_FUNC_rand_clear_seed_fn *parent_clear_seed; +} CRNG_TEST; + +/* + * Some helper functions + */ +static int lock_parent(CRNG_TEST *crngt) +{ + void *parent = crngt->parent; + + if (parent != NULL + && crngt->parent_lock != NULL + && !crngt->parent_lock(parent)) { + ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED); + return 0; + } + return 1; +} + +static void unlock_parent(CRNG_TEST *crngt) +{ + void *parent = crngt->parent; + + if (parent != NULL && crngt->parent_unlock != NULL) + crngt->parent_unlock(parent); +} + +/* + * Implementation of SP 800-90B section 4.4.1: Repetition Count Test + */ +static int RCT_test(CRNG_TEST *crngt, uint8_t next) +{ + /* + * Critical values for this test are computed using: + * + * C = 1 + \left\lceil\frac{-log_2 \alpha}H\right\rceil + * + * where alpha = 2^-20 and H is the expected entropy per sample. + */ + static const unsigned int rct_c[9] = { + 41, /* H = 0.5 */ + 21, 11, 8, 6, 5, 5, 4, 4 /* H = 1, ..., 8 */ + }; + + if (ossl_likely(crngt->rct.b != 0) + && ossl_unlikely(next == crngt->rct.a)) + return ossl_likely(++crngt->rct.b < rct_c[ENTROPY_H]); + crngt->rct.a = next; + crngt->rct.b = 1; + return 1; +} + +/* + * Implementation of SP 800-90B section 4.4.2: Adaptive Proportion Test + */ +static int APT_test(CRNG_TEST *crngt, uint8_t next) +{ + /* + * Critical values for this test are drawn from a binomial + * distribution with n = 512, p = 2^-H at a critical threshold of + * 2^-20. H being the expected entropy per sample. Refer SP 800-90B + * section 4.4.2, table 2. + */ + static const unsigned int apt_c[9] = { + 410, /* H = 0.5 */ + 311, 177, 103, 62, 39, 25, 18, 13 /* H = 1, ..., 8 */ + }; + + if (ossl_likely(crngt->apt.b != 0)) { + if (ossl_unlikely(crngt->apt.a == next) + && ossl_unlikely(++crngt->apt.b >= apt_c[ENTROPY_H])) { + crngt->apt.b = 0; + return 0; + } + if (ossl_unlikely(++crngt->apt.i >= ENTROPY_APT_W)) + crngt->apt.b = 0; + return 1; + } + crngt->apt.a = next; + crngt->apt.b = 1; + crngt->apt.i = 1; + return 1; +} + +static int crng_test(CRNG_TEST *crngt, const unsigned char *buf, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) + if (!RCT_test(crngt, buf[i]) || !APT_test(crngt, buf[i])) { + crngt->state = EVP_RAND_STATE_ERROR; + ERR_raise(ERR_LIB_PROV, + PROV_R_ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS); + return 0; + } + return 1; +} + +static const OSSL_DISPATCH *find_call(const OSSL_DISPATCH *dispatch, + int function) +{ + if (dispatch != NULL) + while (dispatch->function_id != 0) { + if (dispatch->function_id == function) + return dispatch; + dispatch++; + } + return NULL; +} + +static void *crng_test_new(void *provctx, void *parent, + const OSSL_DISPATCH *p_dispatch) +{ + CRNG_TEST *crngt = OPENSSL_zalloc(sizeof(*crngt)); + const OSSL_DISPATCH *pfunc; + + if (crngt == NULL) + return NULL; + + crngt->provctx = provctx; + crngt->state = EVP_RAND_STATE_UNINITIALISED; + + /* Extract parent's functions */ + if (parent != NULL) { + crngt->parent = parent; + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_ENABLE_LOCKING)) != NULL) + crngt->parent_enable_locking = OSSL_FUNC_rand_enable_locking(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_LOCK)) != NULL) + crngt->parent_lock = OSSL_FUNC_rand_lock(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_UNLOCK)) != NULL) + crngt->parent_unlock = OSSL_FUNC_rand_unlock(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS)) != NULL) + crngt->parent_gettable_ctx_params = OSSL_FUNC_rand_gettable_ctx_params(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_CTX_PARAMS)) != NULL) + crngt->parent_get_ctx_params = OSSL_FUNC_rand_get_ctx_params(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_SEED)) != NULL) + crngt->parent_get_seed = OSSL_FUNC_rand_get_seed(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_CLEAR_SEED)) != NULL) + crngt->parent_clear_seed = OSSL_FUNC_rand_clear_seed(pfunc); + } + + return crngt; +} + +static void crng_test_free(void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (crngt != NULL) { + CRYPTO_THREAD_lock_free(crngt->lock); + OPENSSL_free(crngt); + } +} + +static int crng_test_instantiate(void *vcrngt, unsigned int strength, + int prediction_resistance, + const unsigned char *pstr, + size_t pstr_len, + ossl_unused const OSSL_PARAM params[]) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + /* Start up health tests should go here */ + crngt->state = EVP_RAND_STATE_READY; + return 1; +} + +static int crng_test_uninstantiate(void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + crngt->state = EVP_RAND_STATE_UNINITIALISED; + return 1; +} + +static int crng_test_generate(void *vcrngt, unsigned char *out, size_t outlen, + unsigned int strength, int prediction_resistance, + const unsigned char *adin, size_t adin_len) +{ + unsigned char *p; + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (!crng_test_get_seed(crngt, &p, 0, outlen, outlen, prediction_resistance, + adin, adin_len)) + return 0; + memcpy(out, p, outlen); + crng_test_clear_seed(crngt, p, outlen); + return 1; +} + +static int crng_test_reseed(ossl_unused void *vcrngt, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *ent, + ossl_unused size_t ent_len, + ossl_unused const unsigned char *adin, + ossl_unused size_t adin_len) +{ + return 1; +} + +static int crng_test_verify_zeroization(ossl_unused void *vcrngt) +{ + return 1; +} + +static size_t crng_test_get_seed(void *vcrngt, unsigned char **pout, + int entropy, size_t min_len, + size_t max_len, + int prediction_resistance, + const unsigned char *adin, + size_t adin_len) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + size_t n; + int r = 0; + + /* Without a parent, we rely on the up calls */ + if (crngt->parent == NULL + || crngt->parent_get_seed == NULL) { + n = ossl_prov_get_entropy(crngt->provctx, pout, entropy, + min_len, max_len); + if (n == 0) + return 0; + r = crng_test(crngt, *pout, n); + return r > 0 ? n : 0; + } + + /* Grab seed from our parent */ + if (!crng_test_lock(crngt)) + return 0; + if (!lock_parent(crngt)) { + crng_test_unlock(crngt); + return 0; + } + n = crngt->parent_get_seed(crngt->parent, pout, entropy, + min_len, max_len, prediction_resistance, + adin, adin_len); + unlock_parent(crngt); + if (n > 0) + r = crng_test(crngt, *pout, n); + crng_test_unlock(crngt); + if (n > 0 && r > 0) + return n; + if (crngt->parent_clear_seed != NULL) + crngt->parent_clear_seed(crngt->parent, *pout, n); + return 0; +} + +static void crng_test_clear_seed(void *vcrngt, + unsigned char *out, size_t outlen) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (crngt->parent == NULL || crngt->parent_get_seed == NULL) + ossl_prov_cleanup_entropy(crngt->provctx, out, outlen); + else if (crngt->parent_clear_seed != NULL) + crngt->parent_clear_seed(crngt->parent, out, outlen); +} + +static int crng_test_enable_locking(void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (crngt != NULL && crngt->lock == NULL) { + if (crngt->parent_enable_locking != NULL) + if (!crngt->parent_enable_locking(crngt->parent)) { + ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED); + return 0; + } + crngt->lock = CRYPTO_THREAD_lock_new(); + if (crngt->lock == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_CREATE_LOCK); + return 0; + } + } + return 1; +} + +static int crng_test_lock(ossl_unused void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + return crngt->lock == NULL || CRYPTO_THREAD_write_lock(crngt->lock); +} + +static void crng_test_unlock(ossl_unused void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (crngt->lock != NULL) + CRYPTO_THREAD_unlock(crngt->lock); +} + +static int crng_test_get_ctx_params(void *vcrngt, OSSL_PARAM params[]) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + OSSL_PARAM *p; + + if (crngt->parent != NULL && crngt->parent_get_ctx_params != NULL) + return crngt->parent_get_ctx_params(crngt->parent, params); + + /* No parent means we are using call backs for entropy */ + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE); + if (p != NULL && !OSSL_PARAM_set_int(p, crngt->state)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH); + if (p != NULL && !OSSL_PARAM_set_int(p, 1024)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); + if (p != NULL && !OSSL_PARAM_set_size_t(p, 128)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_FIPS_APPROVED_INDICATOR); + if (p != NULL && !OSSL_PARAM_set_int(p, 0)) + return 0; + return 1; +} + +static const OSSL_PARAM *crng_test_gettable_ctx_params(void *vcrngt, + void *provctx) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), + OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), + OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), + OSSL_PARAM_int(OSSL_RAND_PARAM_FIPS_APPROVED_INDICATOR, NULL), + OSSL_PARAM_END + }; + + if (crngt->parent != NULL && crngt->parent_gettable_ctx_params != NULL) + return crngt->parent_gettable_ctx_params(crngt->parent, provctx); + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_crng_test_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))crng_test_new }, + { OSSL_FUNC_RAND_FREECTX, (void(*)(void))crng_test_free }, + { OSSL_FUNC_RAND_INSTANTIATE, + (void(*)(void))crng_test_instantiate }, + { OSSL_FUNC_RAND_UNINSTANTIATE, + (void(*)(void))crng_test_uninstantiate }, + { OSSL_FUNC_RAND_GENERATE, (void(*)(void))crng_test_generate }, + { OSSL_FUNC_RAND_RESEED, (void(*)(void))crng_test_reseed }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))crng_test_enable_locking }, + { OSSL_FUNC_RAND_LOCK, (void(*)(void))crng_test_lock }, + { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))crng_test_unlock }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))crng_test_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))crng_test_get_ctx_params }, + { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, + (void(*)(void))crng_test_verify_zeroization }, + { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))crng_test_get_seed }, + { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))crng_test_clear_seed }, + OSSL_DISPATCH_END +}; |