prov: add extra params argument to KDF implementations
[openssl.git] / providers / implementations / rands / crngt.c
index 538de37468badd28d33224cab2dcabb0be82eba2..f1b31df1014aea91da31b45216e00b479fd9add1 100644 (file)
 #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 "prov/rand_pool.h"
+#include "crypto/rand_pool.h"
 #include "drbg_local.h"
 #include "prov/seeding.h"
 
 typedef struct crng_test_global_st {
     unsigned char crngt_prev[EVP_MAX_MD_SIZE];
-    RAND_POOL *crngt_pool;
+    EVP_MD *md;
+    int preloaded;
+    CRYPTO_RWLOCK *lock;
 } CRNG_TEST_GLOBAL;
 
-static int crngt_get_entropy(OPENSSL_CTX *ctx, RAND_POOL *pool,
+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;
-    EVP_MD *fmd;
 
-    if (pool == NULL)
-        return 0;
-
-    n = prov_pool_acquire_entropy(pool);
-    if (n >= CRNGT_BUFSIZ) {
-        fmd = EVP_MD_fetch(ctx, "SHA256", "");
-        if (fmd == NULL)
-            return 0;
-        p = rand_pool_detach(pool);
-        r = EVP_Digest(p, CRNGT_BUFSIZ, md, md_size, fmd, NULL);
+    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);
-        rand_pool_reattach(pool, p);
-        EVP_MD_free(fmd);
-        return r;
+        ossl_prov_cleanup_entropy(provctx, p, n);
+        return r != 0;
     }
+    if (n != 0)
+        ossl_prov_cleanup_entropy(provctx, p, n);
     return 0;
 }
 
@@ -60,79 +56,141 @@ static void rand_crng_ossl_ctx_free(void *vcrngt_glob)
 {
     CRNG_TEST_GLOBAL *crngt_glob = vcrngt_glob;
 
-    rand_pool_free(crngt_glob->crngt_pool);
+    CRYPTO_THREAD_lock_free(crngt_glob->lock);
+    EVP_MD_free(crngt_glob->md);
     OPENSSL_free(crngt_glob);
 }
 
-static void *rand_crng_ossl_ctx_new(OPENSSL_CTX *ctx)
+static void *rand_crng_ossl_ctx_new(OSSL_LIB_CTX *ctx)
 {
-    unsigned char buf[CRNGT_BUFSIZ];
     CRNG_TEST_GLOBAL *crngt_glob = OPENSSL_zalloc(sizeof(*crngt_glob));
 
     if (crngt_glob == NULL)
         return NULL;
 
-    if ((crngt_glob->crngt_pool
-         = rand_pool_new(0, 1, CRNGT_BUFSIZ, CRNGT_BUFSIZ)) == NULL) {
+    if ((crngt_glob->md = EVP_MD_fetch(ctx, "SHA256", "")) == NULL) {
         OPENSSL_free(crngt_glob);
         return NULL;
     }
-    if (crngt_get_entropy(ctx, crngt_glob->crngt_pool, buf,
-                          crngt_glob->crngt_prev, NULL)) {
-        OPENSSL_cleanse(buf, sizeof(buf));
-        return crngt_glob;
+
+    if ((crngt_glob->lock = CRYPTO_THREAD_lock_new()) == NULL) {
+        EVP_MD_free(crngt_glob->md);
+        OPENSSL_free(crngt_glob);
+        return NULL;
     }
-    rand_pool_free(crngt_glob->crngt_pool);
-    OPENSSL_free(crngt_glob);
-    return NULL;
+
+    return crngt_glob;
 }
 
-static const OPENSSL_CTX_METHOD rand_crng_ossl_ctx_method = {
+static const OSSL_LIB_CTX_METHOD rand_crng_ossl_ctx_method = {
     rand_crng_ossl_ctx_new,
     rand_crng_ossl_ctx_free,
 };
 
-size_t prov_crngt_get_entropy(PROV_DRBG *drbg,
+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 buf[CRNGT_BUFSIZ], md[EVP_MAX_MD_SIZE];
+    unsigned char md[EVP_MAX_MD_SIZE];
+    unsigned char buf[CRNGT_BUFSIZ];
+    unsigned char *ent, *entp, *entbuf;
     unsigned int sz;
-    RAND_POOL *pool;
-    size_t q, r = 0, s, t = 0;
-    int attempts = 3;
-    OPENSSL_CTX *libctx = PROV_LIBRARY_CONTEXT_OF(drbg->provctx);
+    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
-        = openssl_ctx_get_data(libctx, OPENSSL_CTX_RAND_CRNGT_INDEX,
-                               &rand_crng_ossl_ctx_method);
+        = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_RAND_CRNGT_INDEX,
+                                &rand_crng_ossl_ctx_method);
+    OSSL_CALLBACK *stcb = NULL;
+    void *stcbarg = NULL;
+    OSSL_SELF_TEST *st = NULL;
 
     if (crngt_glob == NULL)
         return 0;
 
-    if ((pool = rand_pool_new(entropy, 1, min_len, max_len)) == NULL)
+    if (!CRYPTO_THREAD_write_lock(crngt_glob->lock))
         return 0;
 
-    while ((q = rand_pool_bytes_needed(pool, 1)) > 0 && attempts-- > 0) {
-        s = q > sizeof(buf) ? sizeof(buf) : q;
-        if (!crngt_get_entropy(libctx, crngt_glob->crngt_pool, buf, md,
-                               &sz)
-            || memcmp(crngt_glob->crngt_prev, md, sz) == 0
-            || !rand_pool_add(pool, buf, s, s * 8))
+    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);
-        t += s;
-        attempts++;
+        entp += s;
+        t -= s;
     }
-    r = t;
-    *pout = rand_pool_detach(pool);
-err:
-    OPENSSL_cleanse(buf, sizeof(buf));
-    rand_pool_free(pool);
+    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 prov_crngt_cleanup_entropy(PROV_DRBG *drbg,
+void ossl_crngt_cleanup_entropy(ossl_unused PROV_DRBG *drbg,
                                 unsigned char *out, size_t outlen)
 {
     OPENSSL_secure_clear_free(out, outlen);