1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.conscrypt;
18
19import java.io.Serializable;
20import java.security.SecureRandomSpi;
21
22public class OpenSSLRandom extends SecureRandomSpi implements Serializable {
23    private static final long serialVersionUID = 8506210602917522860L;
24
25    private boolean mSeeded;
26
27    @Override
28    protected void engineSetSeed(byte[] seed) {
29        if (seed == null) {
30            throw new NullPointerException("seed == null");
31        }
32
33        // NOTE: The contract of the SecureRandomSpi does not appear to prohibit self-seeding here
34        // (in addition to using the provided seed).
35        selfSeedIfNotSeeded();
36        NativeCrypto.RAND_seed(seed);
37    }
38
39    @Override
40    protected void engineNextBytes(byte[] bytes) {
41        selfSeedIfNotSeeded();
42        NativeCrypto.RAND_bytes(bytes);
43    }
44
45    @Override
46    protected byte[] engineGenerateSeed(int numBytes) {
47        selfSeedIfNotSeeded();
48        byte[] output = new byte[numBytes];
49        NativeCrypto.RAND_bytes(output);
50        return output;
51    }
52
53    /**
54     * Self-seeds this instance from the Linux RNG. Does nothing if this instance has already been
55     * seeded.
56     */
57    private void selfSeedIfNotSeeded() {
58        // For BoringSSL, RAND_seed and RAND_load_file are no-ops since the RNG
59        // reads directly from the random device node.
60        if (NativeCrypto.isBoringSSL) {
61            return;
62        }
63
64        // NOTE: No need to worry about concurrent access to this field because the worst case is
65        // that the code below is executed multiple times (by different threads), which may only
66        // increase the entropy of the OpenSSL PRNG.
67        if (mSeeded) {
68            return;
69        }
70
71        seedOpenSSLPRNGFromLinuxRNG();
72        mSeeded = true;
73    }
74
75    /**
76     * Obtains a seed from the Linux RNG and mixes it into the OpenSSL PRNG (default RAND engine).
77     *
78     * <p>NOTE: This modifies the OpenSSL PRNG shared by all instances of OpenSSLRandom and other
79     * crypto primitives offered by or built on top of OpenSSL.
80     */
81    public static void seedOpenSSLPRNGFromLinuxRNG() {
82        int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES;
83        int bytesRead = NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes);
84        if (bytesRead != seedLengthInBytes) {
85            throw new SecurityException("Failed to read sufficient bytes from /dev/urandom."
86                    + " Expected: " + seedLengthInBytes + ", actual: " + bytesRead);
87        }
88    }
89}
90