1/*
2 * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
3 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
4 *
5 * Licensed under the Apache License 2.0 (the "License"). You may not use
6 * this file except in compliance with the License. You can obtain a copy
7 * in the file LICENSE in the source distribution or at
8 * https://www.openssl.org/source/license.html
9 */
10
11/*
12 * Implementation of the FIPS 140-2 section 4.9.2 Conditional Tests.
13 */
14
15#include <string.h>
16#include <openssl/evp.h>
17#include "crypto/rand.h"
18#include "internal/thread_once.h"
19#include "internal/cryptlib.h"
20#include "rand_local.h"
21
22typedef struct crng_test_global_st {
23 unsigned char crngt_prev[EVP_MAX_MD_SIZE];
24 RAND_POOL *crngt_pool;
25} CRNG_TEST_GLOBAL;
26
27int (*crngt_get_entropy)(OPENSSL_CTX *, RAND_POOL *, unsigned char *,
28 unsigned char *, unsigned int *)
29 = &rand_crngt_get_entropy_cb;
30
31static void rand_crng_ossl_ctx_free(void *vcrngt_glob)
32{
33 CRNG_TEST_GLOBAL *crngt_glob = vcrngt_glob;
34
35 rand_pool_free(crngt_glob->crngt_pool);
36 OPENSSL_free(crngt_glob);
37}
38
39static void *rand_crng_ossl_ctx_new(OPENSSL_CTX *ctx)
40{
41 unsigned char buf[CRNGT_BUFSIZ];
42 CRNG_TEST_GLOBAL *crngt_glob = OPENSSL_zalloc(sizeof(*crngt_glob));
43
44 if (crngt_glob == NULL)
45 return NULL;
46
47 if ((crngt_glob->crngt_pool
48 = rand_pool_new(0, 1, CRNGT_BUFSIZ, CRNGT_BUFSIZ)) == NULL) {
49 OPENSSL_free(crngt_glob);
50 return NULL;
51 }
52 if (crngt_get_entropy(ctx, crngt_glob->crngt_pool, buf,
53 crngt_glob->crngt_prev, NULL)) {
54 OPENSSL_cleanse(buf, sizeof(buf));
55 return crngt_glob;
56 }
57 rand_pool_free(crngt_glob->crngt_pool);
58 OPENSSL_free(crngt_glob);
59 return NULL;
60}
61
62static const OPENSSL_CTX_METHOD rand_crng_ossl_ctx_method = {
63 rand_crng_ossl_ctx_new,
64 rand_crng_ossl_ctx_free,
65};
66
67int rand_crngt_get_entropy_cb(OPENSSL_CTX *ctx,
68 RAND_POOL *pool,
69 unsigned char *buf,
70 unsigned char *md,
71 unsigned int *md_size)
72{
73 int r;
74 size_t n;
75 unsigned char *p;
76
77 if (pool == NULL)
78 return 0;
79
80 n = rand_pool_acquire_entropy(pool);
81 if (n >= CRNGT_BUFSIZ) {
82 EVP_MD *fmd = EVP_MD_fetch(ctx, "SHA256", "");
83 if (fmd == NULL)
84 return 0;
85 p = rand_pool_detach(pool);
86 r = EVP_Digest(p, CRNGT_BUFSIZ, md, md_size, fmd, NULL);
87 if (r != 0)
88 memcpy(buf, p, CRNGT_BUFSIZ);
89 rand_pool_reattach(pool, p);
90 EVP_MD_free(fmd);
91 return r;
92 }
93 return 0;
94}
95
96size_t rand_crngt_get_entropy(RAND_DRBG *drbg,
97 unsigned char **pout,
98 int entropy, size_t min_len, size_t max_len,
99 int prediction_resistance)
100{
101 unsigned char buf[CRNGT_BUFSIZ], md[EVP_MAX_MD_SIZE];
102 unsigned int sz;
103 RAND_POOL *pool;
104 size_t q, r = 0, s, t = 0;
105 int attempts = 3;
106 CRNG_TEST_GLOBAL *crngt_glob
107 = openssl_ctx_get_data(drbg->libctx, OPENSSL_CTX_RAND_CRNGT_INDEX,
108 &rand_crng_ossl_ctx_method);
109
110 if (crngt_glob == NULL)
111 return 0;
112
113 if ((pool = rand_pool_new(entropy, 1, min_len, max_len)) == NULL)
114 return 0;
115
116 while ((q = rand_pool_bytes_needed(pool, 1)) > 0 && attempts-- > 0) {
117 s = q > sizeof(buf) ? sizeof(buf) : q;
118 if (!crngt_get_entropy(drbg->libctx, crngt_glob->crngt_pool, buf, md,
119 &sz)
120 || memcmp(crngt_glob->crngt_prev, md, sz) == 0
121 || !rand_pool_add(pool, buf, s, s * 8))
122 goto err;
123 memcpy(crngt_glob->crngt_prev, md, sz);
124 t += s;
125 attempts++;
126 }
127 r = t;
128 *pout = rand_pool_detach(pool);
129err:
130 OPENSSL_cleanse(buf, sizeof(buf));
131 rand_pool_free(pool);
132 return r;
133}
134
135void rand_crngt_cleanup_entropy(RAND_DRBG *drbg,
136 unsigned char *out, size_t outlen)
137{
138 OPENSSL_secure_clear_free(out, outlen);
139}
140