fips selftest: avoid relying on a real RNG for self tests
authorPauli <pauli@openssl.org>
Tue, 5 Sep 2023 00:16:49 +0000 (10:16 +1000)
committerPauli <pauli@openssl.org>
Tue, 3 Oct 2023 07:54:03 +0000 (18:54 +1100)
Rather than instantiate the private and primary DRBGs during the
selftest, instead use a test RNG.  This leaves the DRBG setup
pristine and permits later replacement of the seed source despite
the very early running power up self tests.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22210)

crypto/rand/rand_lib.c
include/crypto/rand.h
include/openssl/core_names.h
providers/fips/self_test.c
providers/fips/self_test_kats.c
providers/implementations/rands/test_rng.c

index 7c1c89ab70135fd97b9472b34b61b6da382e31fa..4d10ca8db9dd7a224a25b7a5896fb0c8fe02e461 100644 (file)
@@ -338,6 +338,8 @@ int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
     }
 #endif
 
+    if (num < 0)
+        return 0;
     rand = RAND_get0_private(ctx);
     if (rand != NULL)
         return EVP_RAND_generate(rand, buf, num, strength, 0, NULL, 0);
@@ -347,8 +349,6 @@ int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
 
 int RAND_priv_bytes(unsigned char *buf, int num)
 {
-    if (num < 0)
-        return 0;
     return RAND_priv_bytes_ex(NULL, buf, (size_t)num, 0);
 }
 
@@ -367,6 +367,8 @@ int RAND_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
     }
 #endif
 
+    if (num < 0)
+        return 0;
     rand = RAND_get0_public(ctx);
     if (rand != NULL)
         return EVP_RAND_generate(rand, buf, num, strength, 0, NULL, 0);
@@ -376,8 +378,6 @@ int RAND_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
 
 int RAND_bytes(unsigned char *buf, int num)
 {
-    if (num < 0)
-        return 0;
     return RAND_bytes_ex(NULL, buf, (size_t)num, 0);
 }
 
@@ -727,6 +727,18 @@ EVP_RAND_CTX *RAND_get0_private(OSSL_LIB_CTX *ctx)
     return rand;
 }
 
+#ifdef FIPS_MODULE
+EVP_RAND_CTX *ossl_rand_get0_private_noncreating(OSSL_LIB_CTX *ctx)
+{
+    RAND_GLOBAL *dgbl = rand_get_global(ctx);
+
+    if (dgbl == NULL)
+        return NULL;
+
+    return CRYPTO_THREAD_get_local(&dgbl->private);
+}
+#endif
+
 int RAND_set0_public(OSSL_LIB_CTX *ctx, EVP_RAND_CTX *rand)
 {
     RAND_GLOBAL *dgbl = rand_get_global(ctx);
index 6a71a339c81228c072e10292a983c0a6cb81dd82..3b6ccecc065217eda0a6a18b2a9d261a543ee5be 100644 (file)
@@ -125,4 +125,8 @@ void ossl_rand_cleanup_nonce(ossl_unused const OSSL_CORE_HANDLE *handle,
 size_t ossl_pool_acquire_entropy(RAND_POOL *pool);
 int ossl_pool_add_nonce_data(RAND_POOL *pool);
 
+# ifdef FIPS_MODULE
+EVP_RAND_CTX *ossl_rand_get0_private_noncreating(OSSL_LIB_CTX *ctx);
+# endif
+
 #endif
index 5e3c132f5b3659410b983107ca9d12296152be65..0a6ec1bbbad0508093bfbb28c2b47956b088bffe 100644 (file)
@@ -248,6 +248,7 @@ extern "C" {
 #define OSSL_RAND_PARAM_MAX_REQUEST             "max_request"
 #define OSSL_RAND_PARAM_TEST_ENTROPY            "test_entropy"
 #define OSSL_RAND_PARAM_TEST_NONCE              "test_nonce"
+#define OSSL_RAND_PARAM_GENERATE                "generate"
 
 /* RAND/DRBG names */
 #define OSSL_DRBG_PARAM_RESEED_REQUESTS         "reseed_requests"
index 0be314692e8872a53a7d4924cbdaa942ea6b27f3..b8dc9817b283cde10a539df2ec0e51e795e8bd8e 100644 (file)
@@ -19,6 +19,7 @@
 #include "internal/e_os.h"
 #include "internal/tsan_assist.h"
 #include "prov/providercommon.h"
+#include "crypto/rand.h"
 
 /*
  * We're cheating here. Normally we don't allow RUN_ONCE usage inside the FIPS
@@ -406,14 +407,14 @@ int SELF_TEST_post(SELF_TEST_POST_PARAMS *st, int on_demand_test)
     }
 
     /* Verify that the RNG has been restored properly */
-    testrand = EVP_RAND_fetch(st->libctx, "TEST-RAND", NULL);
-    if (testrand == NULL
-            || (rng = RAND_get0_private(st->libctx)) == NULL
-            || strcmp(EVP_RAND_get0_name(EVP_RAND_CTX_get0_rand(rng)),
-                      EVP_RAND_get0_name(testrand)) == 0) {
-        ERR_raise(ERR_LIB_PROV, PROV_R_SELF_TEST_KAT_FAILURE);
-        goto end;
-    }
+    rng = ossl_rand_get0_private_noncreating(st->libctx);
+    if (rng != NULL)
+        if ((testrand = EVP_RAND_fetch(st->libctx, "TEST-RAND", NULL)) == NULL
+                || strcmp(EVP_RAND_get0_name(EVP_RAND_CTX_get0_rand(rng)),
+                          EVP_RAND_get0_name(testrand)) == 0) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_SELF_TEST_KAT_FAILURE);
+            goto end;
+        }
 
     ok = 1;
 end:
index 74ee25dcb6ae2b081f44e5a515af0aa35bf60a15..b2060c500c8ced035fb49582016794f37c183a70 100644 (file)
@@ -13,6 +13,7 @@
 #include <openssl/core_names.h>
 #include <openssl/param_build.h>
 #include <openssl/rand.h>
+#include "crypto/rand.h"
 #include "internal/cryptlib.h"
 #include "internal/nelem.h"
 #include "self_test.h"
@@ -22,7 +23,7 @@ static int set_kat_drbg(OSSL_LIB_CTX *ctx,
                         const unsigned char *entropy, size_t entropy_len,
                         const unsigned char *nonce, size_t nonce_len,
                         const unsigned char *persstr, size_t persstr_len);
-static int reset_original_drbg(OSSL_LIB_CTX *ctx);
+static int reset_main_drbg(OSSL_LIB_CTX *ctx);
 
 static int self_test_digest(const ST_KAT_DIGEST *t, OSSL_SELF_TEST *st,
                             OSSL_LIB_CTX *libctx)
@@ -701,39 +702,12 @@ static int self_test_signatures(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
             return 0;
         if (!self_test_sign(t, st, libctx))
             ret = 0;
-        if (!reset_original_drbg(libctx))
+        if (!reset_main_drbg(libctx))
             ret = 0;
     }
     return ret;
 }
 
-/*
- * Run the algorithm KAT's.
- * Return 1 is successful, otherwise return 0.
- * This runs all the tests regardless of if any fail.
- */
-int SELF_TEST_kats(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
-{
-    int ret = 1;
-
-    if (!self_test_digests(st, libctx))
-        ret = 0;
-    if (!self_test_ciphers(st, libctx))
-        ret = 0;
-    if (!self_test_signatures(st, libctx))
-        ret = 0;
-    if (!self_test_kdfs(st, libctx))
-        ret = 0;
-    if (!self_test_drbgs(st, libctx))
-        ret = 0;
-    if (!self_test_kas(st, libctx))
-        ret = 0;
-    if (!self_test_asym_ciphers(st, libctx))
-        ret = 0;
-
-    return ret;
-}
-
 /*
  * Swap the library context DRBG for KAT testing
  *
@@ -745,13 +719,12 @@ int SELF_TEST_kats(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
  */
 
 /*
- * The default private DRBG of the library context, saved for the duration
- * of KAT testing.
+ * Replacement "random" sources
+ * main_rand is used for most tests and it's set to generate mode.
+ * kat_rand is used for KATs where specific input is mandated.
  */
-static EVP_RAND_CTX *saved_rand = NULL;
-
-/* Replacement "random" source */
 static EVP_RAND_CTX *kat_rand = NULL;
+static EVP_RAND_CTX *main_rand = NULL;
 
 static int set_kat_drbg(OSSL_LIB_CTX *ctx,
                         const unsigned char *entropy, size_t entropy_len,
@@ -765,7 +738,7 @@ static int set_kat_drbg(OSSL_LIB_CTX *ctx,
     };
 
     /* If not NULL, we didn't cleanup from last call: BAD */
-    if (kat_rand != NULL || saved_rand != NULL)
+    if (kat_rand != NULL)
         return 0;
 
     rand = EVP_RAND_fetch(ctx, "TEST-RAND", NULL);
@@ -777,7 +750,8 @@ static int set_kat_drbg(OSSL_LIB_CTX *ctx,
     if (parent_rand == NULL)
         goto err;
 
-    drbg_params[0] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH, &strength);
+    drbg_params[0] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH,
+                                               &strength);
     if (!EVP_RAND_CTX_set_params(parent_rand, drbg_params))
         goto err;
 
@@ -810,37 +784,30 @@ static int set_kat_drbg(OSSL_LIB_CTX *ctx,
     if (!EVP_RAND_instantiate(kat_rand, strength, 0, persstr, persstr_len, NULL))
         goto err;
 
+    /* When we set the new private generator this one is freed, so upref it */
+    if (!EVP_RAND_CTX_up_ref(main_rand))
+        goto err;
+
     /* Update the library context DRBG */
-    if ((saved_rand = RAND_get0_private(ctx)) != NULL)
-        /* Avoid freeing this since we replace it */
-        if (!EVP_RAND_CTX_up_ref(saved_rand)) {
-            saved_rand = NULL;
-            goto err;
-        }
     if (RAND_set0_private(ctx, kat_rand) > 0) {
         /* Keeping a copy to verify zeroization */
         if (EVP_RAND_CTX_up_ref(kat_rand))
             return 1;
-        if (saved_rand != NULL)
-            RAND_set0_private(ctx, saved_rand);
+        RAND_set0_private(ctx, main_rand);
     }
 
  err:
     EVP_RAND_CTX_free(parent_rand);
-    EVP_RAND_CTX_free(saved_rand);
     EVP_RAND_CTX_free(kat_rand);
-    kat_rand = saved_rand = NULL;
+    kat_rand = NULL;
     return 0;
 }
 
-static int reset_original_drbg(OSSL_LIB_CTX *ctx) {
+static int reset_main_drbg(OSSL_LIB_CTX *ctx) {
     int ret = 1;
 
-    if (saved_rand != NULL) {
-        if (!RAND_set0_private(ctx, saved_rand))
-            ret = 0;
-        saved_rand = NULL;
-    }
+    if (!RAND_set0_private(ctx, main_rand))
+        ret = 0;
     if (kat_rand != NULL) {
         if (!EVP_RAND_uninstantiate(kat_rand)
                 || !EVP_RAND_verify_zeroization(kat_rand))
@@ -851,3 +818,68 @@ static int reset_original_drbg(OSSL_LIB_CTX *ctx) {
     return ret;
 }
 
+static int setup_main_random(OSSL_LIB_CTX *libctx)
+{
+    OSSL_PARAM drbg_params[3] = {
+        OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END
+    };
+    unsigned int strength = 256, generate = 1;
+    EVP_RAND *rand;
+
+    rand = EVP_RAND_fetch(libctx, "TEST-RAND", NULL);
+    if (rand == NULL)
+        return 0;
+
+    main_rand = EVP_RAND_CTX_new(rand, NULL);
+    EVP_RAND_free(rand);
+    if (main_rand == NULL)
+        goto err;
+
+    drbg_params[0] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_GENERATE,
+                                               &generate);
+    drbg_params[1] = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH,
+                                               &strength);
+
+    if (!EVP_RAND_instantiate(main_rand, strength, 0, NULL, 0, drbg_params))
+        goto err;
+    return 1;
+ err:
+    EVP_RAND_CTX_free(main_rand);
+    return 0;
+}
+
+/*
+ * Run the algorithm KAT's.
+ * Return 1 is successful, otherwise return 0.
+ * This runs all the tests regardless of if any fail.
+ */
+int SELF_TEST_kats(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
+{
+    EVP_RAND_CTX *saved_rand = ossl_rand_get0_private_noncreating(libctx);
+    int ret = 1;
+
+    if (!setup_main_random(libctx)
+            || !RAND_set0_private(libctx, main_rand)) {
+        EVP_RAND_CTX_free(main_rand);
+        return 0;
+    }
+
+    if (!self_test_digests(st, libctx))
+        ret = 0;
+    if (!self_test_ciphers(st, libctx))
+        ret = 0;
+    if (!self_test_signatures(st, libctx))
+        ret = 0;
+    if (!self_test_kdfs(st, libctx))
+        ret = 0;
+    if (!self_test_drbgs(st, libctx))
+        ret = 0;
+    if (!self_test_kas(st, libctx))
+        ret = 0;
+    if (!self_test_asym_ciphers(st, libctx))
+        ret = 0;
+
+    RAND_set0_private(libctx, saved_rand);
+    return ret;
+}
+
index 4e7fed0fc7b1f8894e3abb5c7eefd037eb170848..5c05e349888d01cb2701b0e8349a5e8e2a324cf7 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <string.h>
+#include <stdlib.h>
 #include <openssl/core_dispatch.h>
 #include <openssl/e_os2.h>
 #include <openssl/params.h>
@@ -39,6 +40,7 @@ static OSSL_FUNC_rand_get_seed_fn test_rng_get_seed;
 
 typedef struct {
     void *provctx;
+    unsigned int generate;
     int state;
     unsigned int strength;
     size_t max_request;
@@ -99,16 +101,30 @@ static int test_rng_uninstantiate(void *vtest)
     return 1;
 }
 
+static unsigned char gen_byte(PROV_TEST_RNG *t)
+{
+    return rand() & 0xff;
+}
+
 static int test_rng_generate(void *vtest, unsigned char *out, size_t outlen,
                              unsigned int strength, int prediction_resistance,
                              const unsigned char *adin, size_t adin_len)
 {
     PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+    size_t i;
 
-    if (strength > t->strength || t->entropy_len - t->entropy_pos < outlen)
+    if (strength > t->strength)
         return 0;
-    memcpy(out, t->entropy + t->entropy_pos, outlen);
-    t->entropy_pos += outlen;
+    if (t->generate) {
+        for (i = 0; i < outlen; i++)
+            out[i] = gen_byte(t);
+    } else {
+        if (t->entropy_len - t->entropy_pos < outlen)
+            return 0;
+
+        memcpy(out, t->entropy + t->entropy_pos, outlen);
+        t->entropy_pos += outlen;
+    }
     return 1;
 }
 
@@ -123,15 +139,23 @@ static int test_rng_reseed(ossl_unused void *vtest,
 }
 
 static size_t test_rng_nonce(void *vtest, unsigned char *out,
-                             unsigned int strength,
-                             ossl_unused size_t min_noncelen,
+                             unsigned int strength, size_t min_noncelen,
                              ossl_unused size_t max_noncelen)
 {
     PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+    size_t i;
 
-    if (t->nonce == NULL || strength > t->strength)
+    if (strength > t->strength)
         return 0;
 
+    if (t->generate) {
+        for (i = 0; i < min_noncelen; i++)
+            out[i] = gen_byte(t);
+        return min_noncelen;
+    }
+
+    if (t->nonce == NULL)
+        return 0;
     if (out != NULL)
         memcpy(out, t->nonce, t->nonce_len);
     return t->nonce_len;
@@ -153,6 +177,10 @@ static int test_rng_get_ctx_params(void *vtest, OSSL_PARAM params[])
     p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST);
     if (p != NULL && !OSSL_PARAM_set_size_t(p, t->max_request))
         return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_GENERATE);
+    if (p != NULL && OSSL_PARAM_set_uint(p, t->generate))
+        return 0;
     return 1;
 }
 
@@ -163,6 +191,7 @@ static const OSSL_PARAM *test_rng_gettable_ctx_params(ossl_unused void *vtest,
         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_uint(OSSL_RAND_PARAM_GENERATE, NULL),
         OSSL_PARAM_END
     };
     return known_gettable_ctx_params;
@@ -203,9 +232,12 @@ static int test_rng_set_ctx_params(void *vtest, const OSSL_PARAM params[])
     }
 
     p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_MAX_REQUEST);
-    if (p != NULL  && !OSSL_PARAM_get_size_t(p, &t->max_request))
+    if (p != NULL && !OSSL_PARAM_get_size_t(p, &t->max_request))
         return 0;
 
+    p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_GENERATE);
+    if (p != NULL && !OSSL_PARAM_get_uint(p, &t->generate))
+        return 0;
     return 1;
 }
 
@@ -217,6 +249,7 @@ static const OSSL_PARAM *test_rng_settable_ctx_params(ossl_unused void *vtest,
         OSSL_PARAM_octet_string(OSSL_RAND_PARAM_TEST_NONCE, NULL, 0),
         OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL),
         OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL),
+        OSSL_PARAM_uint(OSSL_RAND_PARAM_GENERATE, NULL),
         OSSL_PARAM_END
     };
     return known_settable_ctx_params;