OpenSSLRandom.java revision 49336618a762eff280621cf7474021e06e8521fa
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        // NOTE: The contract of the SecureRandomSpi does not appear to prohibit self-seeding here
30        // (in addition to using the provided seed).
31        selfSeedIfNotSeeded();
32        NativeCrypto.RAND_seed(seed);
33    }
34
35    @Override
36    protected void engineNextBytes(byte[] bytes) {
37        selfSeedIfNotSeeded();
38        NativeCrypto.RAND_bytes(bytes);
39    }
40
41    @Override
42    protected byte[] engineGenerateSeed(int numBytes) {
43        selfSeedIfNotSeeded();
44        byte[] output = new byte[numBytes];
45        NativeCrypto.RAND_bytes(output);
46        return output;
47    }
48
49    /**
50     * Self-seeds this instance from the Linux RNG. Does nothing if this instance has already been
51     * seeded.
52     */
53    private void selfSeedIfNotSeeded() {
54        // NOTE: No need to worry about concurrent access to this field because the worst case is
55        // that the code below is executed multiple times (by different threads), which may only
56        // increase the entropy of the OpenSSL PRNG.
57        if (mSeeded) {
58            return;
59        }
60
61        seedOpenSSLPRNGFromLinuxRNG();
62        mSeeded = true;
63    }
64
65    /**
66     * Obtains a seed from the Linux RNG and mixes it into the OpenSSL PRNG (default RAND engine).
67     *
68     * <p>NOTE: This modifies the OpenSSL PRNG shared by all instances of OpenSSLRandom and other
69     * crypto primitives offered by or built on top of OpenSSL.
70     */
71    public static void seedOpenSSLPRNGFromLinuxRNG() {
72        int seedLengthInBytes = NativeCrypto.RAND_SEED_LENGTH_IN_BYTES;
73        int bytesRead = NativeCrypto.RAND_load_file("/dev/urandom", seedLengthInBytes);
74        if (bytesRead != seedLengthInBytes) {
75            throw new SecurityException("Failed to read sufficient bytes from /dev/urandom."
76                    + " Expected: " + seedLengthInBytes + ", actual: " + bytesRead);
77        }
78    }
79}
80