Index: openssl-1.1.1g/include/crypto/rand.h =================================================================== --- openssl-1.1.1g.orig/include/crypto/rand.h 2020-04-21 15:59:25.552654754 +0200 +++ openssl-1.1.1g/include/crypto/rand.h 2020-04-21 15:59:27.208663772 +0200 @@ -49,6 +49,14 @@ size_t rand_drbg_get_additional_data(RAN void rand_drbg_cleanup_additional_data(RAND_POOL *pool, unsigned char *out); +/* CRNG test entropy filter callbacks. */ +size_t rand_crngt_get_entropy(RAND_DRBG *drbg, + unsigned char **pout, + int entropy, size_t min_len, size_t max_len, + int prediction_resistance); +void rand_crngt_cleanup_entropy(RAND_DRBG *drbg, + unsigned char *out, size_t outlen); + /* * RAND_POOL functions */ Index: openssl-1.1.1g/crypto/rand/build.info =================================================================== --- openssl-1.1.1g.orig/crypto/rand/build.info 2020-04-21 15:59:27.208663772 +0200 +++ openssl-1.1.1g/crypto/rand/build.info 2020-04-21 16:00:32.869021309 +0200 @@ -1,6 +1,6 @@ LIBS=../../libcrypto SOURCE[../../libcrypto]=\ - randfile.c rand_lib.c rand_err.c rand_egd.c \ + randfile.c rand_lib.c rand_err.c rand_crng_test.c rand_egd.c \ rand_win.c rand_unix.c rand_vms.c drbg_lib.c drbg_ctr.c INCLUDE[drbg_ctr.o]=../modes Index: openssl-1.1.1g/crypto/rand/drbg_lib.c =================================================================== --- openssl-1.1.1g.orig/crypto/rand/drbg_lib.c 2020-04-21 15:59:25.552654754 +0200 +++ openssl-1.1.1g/crypto/rand/drbg_lib.c 2020-04-21 15:59:27.208663772 +0200 @@ -67,7 +67,7 @@ static CRYPTO_THREAD_LOCAL private_drbg; /* NIST SP 800-90A DRBG recommends the use of a personalization string. */ -static const char ossl_pers_string[] = "OpenSSL NIST SP 800-90A DRBG"; +static const char ossl_pers_string[] = DRBG_DEFAULT_PERS_STRING; static CRYPTO_ONCE rand_drbg_init = CRYPTO_ONCE_STATIC_INIT; @@ -201,8 +201,13 @@ static RAND_DRBG *rand_drbg_new(int secu drbg->parent = parent; if (parent == NULL) { +#ifdef OPENSSL_FIPS + drbg->get_entropy = rand_crngt_get_entropy; + drbg->cleanup_entropy = rand_crngt_cleanup_entropy; +#else drbg->get_entropy = rand_drbg_get_entropy; drbg->cleanup_entropy = rand_drbg_cleanup_entropy; +#endif #ifndef RAND_DRBG_GET_RANDOM_NONCE drbg->get_nonce = rand_drbg_get_nonce; drbg->cleanup_nonce = rand_drbg_cleanup_nonce; Index: openssl-1.1.1g/crypto/rand/rand_crng_test.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ openssl-1.1.1g/crypto/rand/rand_crng_test.c 2020-04-21 15:59:27.208663772 +0200 @@ -0,0 +1,118 @@ +/* + * Copyright 2019 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 "crypto/rand.h" +#include "internal/thread_once.h" +#include "rand_local.h" + +static RAND_POOL *crngt_pool; +static unsigned char crngt_prev[EVP_MAX_MD_SIZE]; + +int (*crngt_get_entropy)(unsigned char *, unsigned char *, unsigned int *) + = &rand_crngt_get_entropy_cb; + +int rand_crngt_get_entropy_cb(unsigned char *buf, unsigned char *md, + unsigned int *md_size) +{ + int r; + size_t n; + unsigned char *p; + + n = rand_pool_acquire_entropy(crngt_pool); + if (n >= CRNGT_BUFSIZ) { + p = rand_pool_detach(crngt_pool); + r = EVP_Digest(p, CRNGT_BUFSIZ, md, md_size, EVP_sha256(), NULL); + if (r != 0) + memcpy(buf, p, CRNGT_BUFSIZ); + rand_pool_reattach(crngt_pool, p); + return r; + } + return 0; +} + +void rand_crngt_cleanup(void) +{ + rand_pool_free(crngt_pool); + crngt_pool = NULL; +} + +int rand_crngt_init(void) +{ + unsigned char buf[CRNGT_BUFSIZ]; + + if ((crngt_pool = rand_pool_new(0, 1, CRNGT_BUFSIZ, CRNGT_BUFSIZ)) == NULL) + return 0; + if (crngt_get_entropy(buf, crngt_prev, NULL)) { + OPENSSL_cleanse(buf, sizeof(buf)); + return 1; + } + rand_crngt_cleanup(); + return 0; +} + +static CRYPTO_ONCE rand_crngt_init_flag = CRYPTO_ONCE_STATIC_INIT; +DEFINE_RUN_ONCE_STATIC(do_rand_crngt_init) +{ + return OPENSSL_init_crypto(0, NULL) + && rand_crngt_init() + && OPENSSL_atexit(&rand_crngt_cleanup); +} + +int rand_crngt_single_init(void) +{ + return RUN_ONCE(&rand_crngt_init_flag, do_rand_crngt_init); +} + +size_t rand_crngt_get_entropy(RAND_DRBG *drbg, + unsigned char **pout, + int entropy, size_t min_len, size_t max_len, + int prediction_resistance) +{ + unsigned char buf[CRNGT_BUFSIZ], md[EVP_MAX_MD_SIZE]; + unsigned int sz; + RAND_POOL *pool; + size_t q, r = 0, s, t = 0; + int attempts = 3; + + if (!RUN_ONCE(&rand_crngt_init_flag, do_rand_crngt_init)) + return 0; + + if ((pool = rand_pool_new(entropy, 1, min_len, max_len)) == NULL) + return 0; + + while ((q = rand_pool_bytes_needed(pool, 1)) > 0 && attempts-- > 0) { + s = q > sizeof(buf) ? sizeof(buf) : q; + if (!crngt_get_entropy(buf, md, &sz) + || memcmp(crngt_prev, md, sz) == 0 + || !rand_pool_add(pool, buf, s, s * 8)) + goto err; + memcpy(crngt_prev, md, sz); + t += s; + attempts++; + } + r = t; + *pout = rand_pool_detach(pool); +err: + OPENSSL_cleanse(buf, sizeof(buf)); + rand_pool_free(pool); + return r; +} + +void rand_crngt_cleanup_entropy(RAND_DRBG *drbg, + unsigned char *out, size_t outlen) +{ + OPENSSL_secure_clear_free(out, outlen); +} Index: openssl-1.1.1g/crypto/rand/rand_local.h =================================================================== --- openssl-1.1.1g.orig/crypto/rand/rand_local.h 2020-04-21 15:59:25.552654754 +0200 +++ openssl-1.1.1g/crypto/rand/rand_local.h 2020-04-21 15:59:27.208663772 +0200 @@ -33,7 +33,15 @@ # define MASTER_RESEED_TIME_INTERVAL (60*60) /* 1 hour */ # define SLAVE_RESEED_TIME_INTERVAL (7*60) /* 7 minutes */ - +/* + * The number of bytes that constitutes an atomic lump of entropy with respect + * to the FIPS 140-2 section 4.9.2 Conditional Tests. The size is somewhat + * arbitrary, the smaller the value, the less entropy is consumed on first + * read but the higher the probability of the test failing by accident. + * + * The value is in bytes. + */ +#define CRNGT_BUFSIZ 16 /* * Maximum input size for the DRBG (entropy, nonce, personalization string) @@ -44,6 +52,8 @@ */ # define DRBG_MAX_LENGTH INT32_MAX +/* The default nonce */ +# define DRBG_DEFAULT_PERS_STRING "OpenSSL NIST SP 800-90A DRBG" /* * Maximum allocation size for RANDOM_POOL buffers @@ -296,4 +306,22 @@ int rand_drbg_enable_locking(RAND_DRBG * /* initializes the AES-CTR DRBG implementation */ int drbg_ctr_init(RAND_DRBG *drbg); +/* + * Entropy call back for the FIPS 140-2 section 4.9.2 Conditional Tests. + * These need to be exposed for the unit tests. + */ +int rand_crngt_get_entropy_cb(unsigned char *buf, unsigned char *md, + unsigned int *md_size); +extern int (*crngt_get_entropy)(unsigned char *buf, unsigned char *md, + unsigned int *md_size); +int rand_crngt_init(void); +void rand_crngt_cleanup(void); + +/* + * Expose the run once initialisation function for the unit tests because. + * they need to restart from scratch to validate the first block is skipped + * properly. + */ +int rand_crngt_single_init(void); + #endif Index: openssl-1.1.1g/test/drbgtest.c =================================================================== --- openssl-1.1.1g.orig/test/drbgtest.c 2020-04-21 15:59:25.552654754 +0200 +++ openssl-1.1.1g/test/drbgtest.c 2020-04-21 15:59:27.208663772 +0200 @@ -150,6 +150,31 @@ static size_t kat_nonce(RAND_DRBG *drbg, return t->noncelen; } + /* + * Disable CRNG testing if it is enabled. + * If the DRBG is ready or in an error state, this means an instantiate cycle + * for which the default personalisation string is used. + */ +static int disable_crngt(RAND_DRBG *drbg) +{ + static const char pers[] = DRBG_DEFAULT_PERS_STRING; + const int instantiate = drbg->state != DRBG_UNINITIALISED; + + if (drbg->get_entropy != rand_crngt_get_entropy) + return 1; + + if ((instantiate && !RAND_DRBG_uninstantiate(drbg)) + || !TEST_true(RAND_DRBG_set_callbacks(drbg, &rand_drbg_get_entropy, + &rand_drbg_cleanup_entropy, + &rand_drbg_get_nonce, + &rand_drbg_cleanup_nonce)) + || (instantiate + && !RAND_DRBG_instantiate(drbg, (const unsigned char *)pers, + sizeof(pers) - 1))) + return 0; + return 1; +} + static int uninstantiate(RAND_DRBG *drbg) { int ret = drbg == NULL ? 1 : RAND_DRBG_uninstantiate(drbg); @@ -175,7 +200,8 @@ static int single_kat(DRBG_SELFTEST_DATA if (!TEST_ptr(drbg = RAND_DRBG_new(td->nid, td->flags, NULL))) return 0; if (!TEST_true(RAND_DRBG_set_callbacks(drbg, kat_entropy, NULL, - kat_nonce, NULL))) { + kat_nonce, NULL)) + || !TEST_true(disable_crngt(drbg))) { failures++; goto err; } @@ -293,7 +319,8 @@ static int error_check(DRBG_SELFTEST_DAT unsigned int reseed_counter_tmp; int ret = 0; - if (!TEST_ptr(drbg = RAND_DRBG_new(0, 0, NULL))) + if (!TEST_ptr(drbg = RAND_DRBG_new(0, 0, NULL)) + || !TEST_true(disable_crngt(drbg))) goto err; /* @@ -740,6 +767,10 @@ static int test_rand_drbg_reseed(void) || !TEST_ptr_eq(private->parent, master)) return 0; + /* Disable CRNG testing for the master DRBG */ + if (!TEST_true(disable_crngt(master))) + return 0; + /* uninstantiate the three global DRBGs */ RAND_DRBG_uninstantiate(private); RAND_DRBG_uninstantiate(public); @@ -964,7 +995,8 @@ static int test_rand_seed(void) size_t rand_buflen; size_t required_seed_buflen = 0; - if (!TEST_ptr(master = RAND_DRBG_get0_master())) + if (!TEST_ptr(master = RAND_DRBG_get0_master()) + || !TEST_true(disable_crngt(master))) return 0; #ifdef OPENSSL_RAND_SEED_NONE @@ -1013,6 +1045,95 @@ static int test_rand_add(void) return 1; } +/* + * A list of the FIPS DRGB types. + */ +static const struct s_drgb_types { + int nid; + int flags; +} drgb_types[] = { + { NID_aes_128_ctr, 0 }, + { NID_aes_192_ctr, 0 }, + { NID_aes_256_ctr, 0 }, +}; + +/* Six cases for each covers seed sizes up to 32 bytes */ +static const size_t crngt_num_cases = 6; + +static size_t crngt_case, crngt_idx; + +static int crngt_entropy_cb(unsigned char *buf, unsigned char *md, + unsigned int *md_size) +{ + size_t i, z; + + if (!TEST_int_lt(crngt_idx, crngt_num_cases)) + return 0; + /* Generate a block of unique data unless this is the duplication point */ + z = crngt_idx++; + if (z > 0 && crngt_case == z) + z--; + for (i = 0; i < CRNGT_BUFSIZ; i++) + buf[i] = (unsigned char)(i + 'A' + z); + return EVP_Digest(buf, CRNGT_BUFSIZ, md, md_size, EVP_sha256(), NULL); +} + +static int test_crngt(int n) +{ + const struct s_drgb_types *dt = drgb_types + n / crngt_num_cases; + RAND_DRBG *drbg = NULL; + unsigned char buff[100]; + size_t ent; + int res = 0; + int expect; + + if (!TEST_true(rand_crngt_single_init())) + return 0; + rand_crngt_cleanup(); + + if (!TEST_ptr(drbg = RAND_DRBG_new(dt->nid, dt->flags, NULL))) + return 0; + ent = (drbg->min_entropylen + CRNGT_BUFSIZ - 1) / CRNGT_BUFSIZ; + crngt_case = n % crngt_num_cases; + crngt_idx = 0; + crngt_get_entropy = &crngt_entropy_cb; + if (!TEST_true(rand_crngt_init())) + goto err; +#ifndef OPENSSL_FIPS + if (!TEST_true(RAND_DRBG_set_callbacks(drbg, &rand_crngt_get_entropy, + &rand_crngt_cleanup_entropy, + &rand_drbg_get_nonce, + &rand_drbg_cleanup_nonce))) + goto err; +#endif + expect = crngt_case == 0 || crngt_case > ent; + if (!TEST_int_eq(RAND_DRBG_instantiate(drbg, NULL, 0), expect)) + goto err; + if (!expect) + goto fin; + if (!TEST_true(RAND_DRBG_generate(drbg, buff, sizeof(buff), 0, NULL, 0))) + goto err; + + expect = crngt_case == 0 || crngt_case > 2 * ent; + if (!TEST_int_eq(RAND_DRBG_reseed(drbg, NULL, 0, 0), expect)) + goto err; + if (!expect) + goto fin; + if (!TEST_true(RAND_DRBG_generate(drbg, buff, sizeof(buff), 0, NULL, 0))) + goto err; + +fin: + res = 1; +err: + if (!res) + TEST_note("DRBG %zd case %zd block %zd", n / crngt_num_cases, + crngt_case, crngt_idx); + uninstantiate(drbg); + RAND_DRBG_free(drbg); + crngt_get_entropy = &rand_crngt_get_entropy_cb; + return res; +} + int setup_tests(void) { app_data_index = RAND_DRBG_get_ex_new_index(0L, NULL, NULL, NULL, NULL); @@ -1025,5 +1146,6 @@ int setup_tests(void) #if defined(OPENSSL_THREADS) ADD_TEST(test_multi_thread); #endif + ADD_ALL_TESTS(test_crngt, crngt_num_cases * OSSL_NELEM(drgb_types)); return 1; }