summaryrefslogtreecommitdiffstats
path: root/crypto/rand/rand_lib.c
blob: 2f2ab6a86dc63934b6bb918e11d9dc43248347d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
/*
 * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the OpenSSL license (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
 */

#include <stdio.h>
#include <time.h>
#include "internal/cryptlib.h"
#include <openssl/opensslconf.h>
#include "internal/rand_int.h"
#include <openssl/engine.h>
#include "internal/thread_once.h"
#include "rand_lcl.h"

#ifndef OPENSSL_NO_ENGINE
/* non-NULL if default_RAND_meth is ENGINE-provided */
static ENGINE *funct_ref;
static CRYPTO_RWLOCK *rand_engine_lock;
#endif
static CRYPTO_RWLOCK *rand_meth_lock;
static const RAND_METHOD *default_RAND_meth;
static CRYPTO_ONCE rand_init = CRYPTO_ONCE_STATIC_INIT;
RAND_BYTES_BUFFER rand_bytes;
int rand_fork_count;

#ifdef OPENSSL_RAND_SEED_RDTSC
/*
 * IMPORTANT NOTE:  It is not currently possible to use this code
 * because we are not sure about the amount of randomness it provides.
 * Some SP900 tests have been run, but there is internal skepticism.
 * So for now this code is not used.
 */
# error "RDTSC enabled?  Should not be possible!"

/*
 * Since we get some randomness from the low-order bits of the
 * high-speec clock, it can help.  But don't return a status since
 * it's not sufficient to indicate whether or not the seeding was
 * done.
 */
void rand_read_tsc(RAND_poll_cb rand_add, void *arg)
{
    unsigned char c;
    int i;

    if ((OPENSSL_ia32cap_P[0] & (1 << 4)) != 0) {
        for (i = 0; i < TSC_READ_COUNT; i++) {
            c = (unsigned char)(OPENSSL_rdtsc() & 0xFF);
            rand_add(arg, &c, 1, 0.5);
        }
    }
}
#endif

#ifdef OPENSSL_RAND_SEED_RDCPU
size_t OPENSSL_ia32_rdseed_bytes(char *buf, size_t len);
size_t OPENSSL_ia32_rdrand_bytes(char *buf, size_t len);

extern unsigned int OPENSSL_ia32cap_P[];

int rand_read_cpu(RAND_poll_cb rand_add, void *arg)
{
    char buff[RANDOMNESS_NEEDED];

    /* If RDSEED is available, use that. */
    if ((OPENSSL_ia32cap_P[2] & (1 << 18)) != 0) {
        if (OPENSSL_ia32_rdseed_bytes(buff, sizeof(buff)) == sizeof(buff)) {
            rand_add(arg, buff, (int)sizeof(buff), sizeof(buff));
            return 1;
        }
    }

    /* Second choice is RDRAND. */
    if ((OPENSSL_ia32cap_P[1] & (1 << (62 - 32))) != 0) {
        if (OPENSSL_ia32_rdrand_bytes(buff, sizeof(buff)) == sizeof(buff)) {
            rand_add(arg, buff, (int)sizeof(buff), sizeof(buff));
            return 1;
        }
    }

    return 0;
}
#endif


/*
 * DRBG has two sets of callbacks; we only discuss the "entropy" one
 * here.  When the DRBG needs additional randomness bits (called entropy
 * in the NIST document), it calls the get_entropy callback which fills in
 * a pointer and returns the number of bytes. When the DRBG is finished with
 * the buffer, it calls the cleanup_entropy callback, with the value of
 * the buffer that the get_entropy callback filled in.
 *
 * Get entropy from the system, via RAND_poll if needed.  The |entropy|
 * is the bits of randomness required, and is expected to fit into a buffer
 * of |min_len|..|max__len| size.  We assume we're getting high-quality
 * randomness from the system, and that |min_len| bytes will do.
 */
size_t drbg_entropy_from_system(RAND_DRBG *drbg,
                                unsigned char **pout,
                                int entropy, size_t min_len, size_t max_len)
{
    int i;
    unsigned char *randomness;

    if (min_len > (size_t)drbg->size) {
        /* Should not happen.  See comment near RANDOMNESS_NEEDED. */
        min_len = drbg->size;
    }

    randomness = drbg->secure ? OPENSSL_secure_malloc(drbg->size)
                                    : OPENSSL_malloc(drbg->size);

    /* If we don't have enough, try to get more. */
    CRYPTO_THREAD_write_lock(rand_bytes.lock);
    for (i = RAND_POLL_RETRIES; rand_bytes.curr < min_len && --i >= 0; ) {
        CRYPTO_THREAD_unlock(rand_bytes.lock);
        RAND_poll();
        CRYPTO_THREAD_write_lock(rand_bytes.lock);
    }

    /* Get desired amount, but no more than we have. */
    if (min_len > rand_bytes.curr)
        min_len = rand_bytes.curr;
    if (min_len != 0) {
        memcpy(randomness, rand_bytes.buff, min_len);
        /* Update amount left and shift it down. */
        rand_bytes.curr -= min_len;
        if (rand_bytes.curr != 0)
            memmove(rand_bytes.buff, &rand_bytes.buff[min_len], rand_bytes.curr);
    }
    CRYPTO_THREAD_unlock(rand_bytes.lock);
    *pout = randomness;
    return min_len;
}

size_t drbg_entropy_from_parent(RAND_DRBG *drbg,
                                unsigned char **pout,
                                int entropy, size_t min_len, size_t max_len)
{
    int st;
    unsigned char *randomness;

    if (min_len > (size_t)drbg->size) {
        /* Should not happen.  See comment near RANDOMNESS_NEEDED. */
        min_len = drbg->size;
    }

    randomness = drbg->secure ? OPENSSL_secure_malloc(drbg->size)
                                    : OPENSSL_malloc(drbg->size);

    /* Get random from parent, include our state as additional input. */
    st = RAND_DRBG_generate(drbg->parent, randomness, min_len, 0,
                            (unsigned char *)drbg, sizeof(*drbg));
    if (st == 0) {
        drbg_release_entropy(drbg, randomness, min_len);
        return 0;
    }
    *pout = randomness;
    return min_len;
}

void drbg_release_entropy(RAND_DRBG *drbg, unsigned char *out, size_t outlen)
{
    if (drbg->secure)
        OPENSSL_secure_clear_free(out, outlen);
    else
        OPENSSL_clear_free(out, outlen);
}

void rand_fork()
{
    rand_fork_count++;
}

DEFINE_RUN_ONCE_STATIC(do_rand_init)
{
    int ret = 1;

#ifndef OPENSSL_NO_ENGINE
    rand_engine_lock = CRYPTO_THREAD_glock_new("rand_engine");
    ret &= rand_engine_lock != NULL;
#endif
    rand_meth_lock = CRYPTO_THREAD_glock_new("rand_meth");
    ret &= rand_meth_lock != NULL;

    rand_bytes.lock = CRYPTO_THREAD_glock_new("rand_bytes");
    ret &= rand_bytes.lock != NULL;
    rand_bytes.curr = 0;
    rand_bytes.size = MAX_RANDOMNESS_HELD;
    rand_bytes.secure = CRYPTO_secure_malloc_initialized();
    rand_bytes.buff = rand_bytes.secure
        ? OPENSSL_secure_malloc(rand_bytes.size)
        : OPENSSL_malloc(rand_bytes.size);
    ret &= rand_bytes.buff != NULL;
    return ret;
}

void rand_cleanup_int(void)
{
    const RAND_METHOD *meth = default_RAND_meth;

    if (meth != NULL && meth->cleanup != NULL)
        meth->cleanup();
    RAND_set_rand_method(NULL);
#ifndef OPENSSL_NO_ENGINE
    CRYPTO_THREAD_lock_free(rand_engine_lock);
#endif
    CRYPTO_THREAD_lock_free(rand_meth_lock);
    CRYPTO_THREAD_lock_free(rand_bytes.lock);
    if (rand_bytes.secure)
        OPENSSL_secure_clear_free(rand_bytes.buff, rand_bytes.size);
    else
        OPENSSL_clear_free(rand_bytes.buff, rand_bytes.size);
}

/*
 * RAND_poll_ex() gets a function pointer to call when it has random bytes.
 * RAND_poll() sets the function pointer to be a wrapper that calls RAND_add().
 */
static void call_rand_add(void* arg, const void *buf, int num, double r)
{
    RAND_add(buf, num, r);
}

int RAND_poll(void)
{
    return RAND_poll_ex(call_rand_add, NULL);
}

int RAND_set_rand_method(const RAND_METHOD *meth)
{
    if (!RUN_ONCE(&rand_init, do_rand_init))
        return 0;

    CRYPTO_THREAD_write_lock(rand_meth_lock);
#ifndef OPENSSL_NO_ENGINE
    ENGINE_finish(funct_ref);
    funct_ref = NULL;
#endif
    default_RAND_meth = meth;
    CRYPTO_THREAD_unlock(rand_meth_lock);
    return 1;
}

const RAND_METHOD *RAND_get_rand_method(void)
{
    const RAND_METHOD *tmp_meth = NULL;

    if (!RUN_ONCE(&rand_init, do_rand_init))
        return NULL;

    CRYPTO_THREAD_write_lock(rand_meth_lock);
    if (default_RAND_meth == NULL) {
#ifndef OPENSSL_NO_ENGINE
        ENGINE *e;

        /* If we have an engine that can do RAND, use it. */
        if ((e = ENGINE_get_default_RAND()) != NULL
                && (tmp_meth = ENGINE_get_RAND(e)) != NULL) {
            funct_ref = e;
            default_RAND_meth =