1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.security;
19
20import java.nio.ByteOrder;
21import java.util.Random;
22import libcore.io.Memory;
23import libcore.io.SizeOf;
24import org.apache.harmony.security.fortress.Engine;
25import org.apache.harmony.security.fortress.Services;
26import org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl;
27
28/**
29 * This class generates cryptographically secure pseudo-random numbers.
30 *
31 * <h3>Supported Algorithms</h3>
32 * <ul>
33 *   <li><strong>SHA1PRNG</strong>: Based on <a
34 *     href="http://en.wikipedia.org/wiki/SHA-1">SHA-1</a>. Not guaranteed to be
35 *     compatible with the SHA1PRNG algorithm on the reference
36 *     implementation.</li>
37 * </ul>
38 *
39 * <p>The default algorithm is defined by the first {@code SecureRandomSpi}
40 * provider found in the VM's installed security providers. Use {@link
41 * Security} to install custom {@link SecureRandomSpi} providers.
42 *
43 * <a name="insecure_seed"><h3>Seeding {@code SecureRandom} may be
44 * insecure</h3></a>
45 * A seed is an array of bytes used to bootstrap random number generation.
46 * To produce cryptographically secure random numbers, both the seed and the
47 * algorithm must be secure.
48 *
49 * <p>By default, instances of this class will generate an initial seed using
50 * an internal entropy source, such as {@code /dev/urandom}. This seed is
51 * unpredictable and appropriate for secure use.
52 *
53 * <p>You may alternatively specify the initial seed explicitly with the
54 * {@link #SecureRandom(byte[]) seeded constructor} or by calling {@link
55 * #setSeed} before any random numbers have been generated. Specifying a fixed
56 * seed will cause the instance to return a predictable sequence of numbers.
57 * This may be useful for testing but it is not appropriate for secure use.
58 *
59 * <p>It is dangerous to seed {@code SecureRandom} with the current time because
60 * that value is more predictable to an attacker than the default seed.
61 *
62 * <p>Calling {@link #setSeed} on a {@code SecureRandom} <i>after</i> it has
63 * been used to generate random numbers (ie. calling {@link #nextBytes}) will
64 * supplement the existing seed. This does not cause the instance to return a
65 * predictable numbers, nor does it harm the security of the numbers generated.
66 */
67public class SecureRandom extends Random {
68
69    private static final long serialVersionUID = 4940670005562187L;
70
71    // The service name.
72    private static final String SERVICE = "SecureRandom";
73
74    // Used to access common engine functionality
75    private static final Engine ENGINE = new Engine(SERVICE);
76
77    private final Provider provider;
78
79    private final SecureRandomSpi secureRandomSpi;
80
81    private final String algorithm;
82
83    // Internal SecureRandom used for getSeed(int)
84    private static volatile SecureRandom internalSecureRandom;
85
86    /**
87     * Constructs a new {@code SecureRandom} that uses the default algorithm.
88     */
89    public SecureRandom() {
90        super(0);
91        Provider.Service service = Services.getSecureRandomService();
92        if (service == null) {
93            this.provider = null;
94            this.secureRandomSpi = new SHA1PRNG_SecureRandomImpl();
95            this.algorithm = "SHA1PRNG";
96        } else {
97            try {
98                this.provider = service.getProvider();
99                this.secureRandomSpi = (SecureRandomSpi)service.newInstance(null);
100                this.algorithm = service.getAlgorithm();
101            } catch (Exception e) {
102                throw new RuntimeException(e);
103            }
104        }
105    }
106
107    /**
108     * Constructs a new seeded {@code SecureRandom} that uses the default
109     * algorithm. <a href="#insecure_seed">Seeding {@code SecureRandom} may be
110     * insecure</a>.
111     */
112    public SecureRandom(byte[] seed) {
113        this();
114        setSeed(seed);
115    }
116
117    /**
118     * Constructs a new instance of {@code SecureRandom} using the given
119     * implementation from the specified provider.
120     *
121     * @param secureRandomSpi
122     *            the implementation.
123     * @param provider
124     *            the security provider.
125     */
126    protected SecureRandom(SecureRandomSpi secureRandomSpi,
127                           Provider provider) {
128        this(secureRandomSpi, provider, "unknown");
129    }
130
131    // Constructor
132    private SecureRandom(SecureRandomSpi secureRandomSpi,
133                         Provider provider,
134                         String algorithm) {
135        super(0);
136        this.provider = provider;
137        this.algorithm = algorithm;
138        this.secureRandomSpi = secureRandomSpi;
139    }
140
141    /**
142     * Returns a new instance of {@code SecureRandom} that utilizes the
143     * specified algorithm.
144     *
145     * @param algorithm
146     *            the name of the algorithm to use.
147     * @return a new instance of {@code SecureRandom} that utilizes the
148     *         specified algorithm.
149     * @throws NoSuchAlgorithmException
150     *             if the specified algorithm is not available.
151     * @throws NullPointerException
152     *             if {@code algorithm} is {@code null}.
153     */
154    public static SecureRandom getInstance(String algorithm) throws NoSuchAlgorithmException {
155        if (algorithm == null) {
156            throw new NullPointerException("algorithm == null");
157        }
158        Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
159        return new SecureRandom((SecureRandomSpi) sap.spi, sap.provider,
160                                algorithm);
161    }
162
163    /**
164     * Returns a new instance of {@code SecureRandom} that utilizes the
165     * specified algorithm from the specified provider.
166     *
167     * @param algorithm
168     *            the name of the algorithm to use.
169     * @param provider
170     *            the name of the provider.
171     * @return a new instance of {@code SecureRandom} that utilizes the
172     *         specified algorithm from the specified provider.
173     * @throws NoSuchAlgorithmException
174     *             if the specified algorithm is not available.
175     * @throws NoSuchProviderException
176     *             if the specified provider is not available.
177     * @throws NullPointerException
178     *             if {@code algorithm} is {@code null}.
179     * @throws IllegalArgumentException if {@code provider == null || provider.isEmpty()}
180     */
181    public static SecureRandom getInstance(String algorithm, String provider)
182                                throws NoSuchAlgorithmException, NoSuchProviderException {
183        if (provider == null || provider.isEmpty()) {
184            throw new IllegalArgumentException();
185        }
186        Provider p = Security.getProvider(provider);
187        if (p == null) {
188            throw new NoSuchProviderException(provider);
189        }
190        return getInstance(algorithm, p);
191    }
192
193    /**
194     * Returns a new instance of {@code SecureRandom} that utilizes the
195     * specified algorithm from the specified provider.
196     *
197     * @param algorithm
198     *            the name of the algorithm to use.
199     * @param provider
200     *            the security provider.
201     * @return a new instance of {@code SecureRandom} that utilizes the
202     *         specified algorithm from the specified provider.
203     * @throws NoSuchAlgorithmException
204     *             if the specified algorithm is not available.
205     * @throws NullPointerException
206     *             if {@code algorithm} is {@code null}.
207     * @throws IllegalArgumentException if {@code provider == null}
208     */
209    public static SecureRandom getInstance(String algorithm, Provider provider)
210            throws NoSuchAlgorithmException {
211        if (provider == null) {
212            throw new IllegalArgumentException();
213        }
214        if (algorithm == null) {
215            throw new NullPointerException("algorithm == null");
216        }
217        Object spi = ENGINE.getInstance(algorithm, provider, null);
218        return new SecureRandom((SecureRandomSpi) spi, provider, algorithm);
219    }
220
221    /**
222     * Returns the provider associated with this {@code SecureRandom}.
223     *
224     * @return the provider associated with this {@code SecureRandom}.
225     */
226    public final Provider getProvider() {
227        return provider;
228    }
229
230    /**
231     * Returns the name of the algorithm of this {@code SecureRandom}.
232     *
233     * @return the name of the algorithm of this {@code SecureRandom}.
234     */
235    public String getAlgorithm() {
236        return algorithm;
237    }
238
239    /**
240     * Seeds this {@code SecureRandom} instance with the specified {@code
241     * seed}. <a href="#insecure_seed">Seeding {@code SecureRandom} may be
242     * insecure</a>.
243     */
244    public synchronized void setSeed(byte[] seed) {
245        secureRandomSpi.engineSetSeed(seed);
246    }
247
248    /**
249     * Seeds this {@code SecureRandom} instance with the specified eight-byte
250     * {@code seed}. <a href="#insecure_seed">Seeding {@code SecureRandom} may
251     * be insecure</a>.
252     */
253    @Override
254    public void setSeed(long seed) {
255        if (seed == 0) {    // skip call from Random
256            return;
257        }
258        byte[] byteSeed = new byte[SizeOf.LONG];
259        Memory.pokeLong(byteSeed, 0, seed, ByteOrder.BIG_ENDIAN);
260        setSeed(byteSeed);
261    }
262
263    /**
264     * Generates and stores random bytes in the given {@code byte[]} for each
265     * array element.
266     *
267     * @param bytes
268     *            the {@code byte[]} to be filled with random bytes.
269     */
270    @Override
271    public synchronized void nextBytes(byte[] bytes) {
272        secureRandomSpi.engineNextBytes(bytes);
273    }
274
275    /**
276     * Generates and returns an {@code int} containing the specified number of
277     * random bits (right justified, with leading zeros).
278     *
279     * @param numBits
280     *            number of bits to be generated. An input value should be in
281     *            the range [0, 32].
282     * @return an {@code int} containing the specified number of random bits.
283     */
284    @Override
285    protected final int next(int numBits) {
286        if (numBits < 0) {
287            numBits = 0;
288        } else {
289            if (numBits > 32) {
290                numBits = 32;
291            }
292        }
293        int bytes = (numBits+7)/8;
294        byte[] next = new byte[bytes];
295        int ret = 0;
296
297        nextBytes(next);
298        for (int i = 0; i < bytes; i++) {
299            ret = (next[i] & 0xFF) | (ret << 8);
300        }
301        ret = ret >>> (bytes*8 - numBits);
302        return ret;
303    }
304
305    /**
306     * Generates and returns the specified number of seed bytes, computed using
307     * the seed generation algorithm used by this {@code SecureRandom}.
308     *
309     * @param numBytes
310     *            the number of seed bytes.
311     * @return the seed bytes
312     */
313    public static byte[] getSeed(int numBytes) {
314        SecureRandom result = internalSecureRandom;
315        if (result == null) {
316            // single-check idiom
317            internalSecureRandom = result = new SecureRandom();
318        }
319        return result.generateSeed(numBytes);
320    }
321
322    /**
323     * Generates and returns the specified number of seed bytes, computed using
324     * the seed generation algorithm used by this {@code SecureRandom}.
325     *
326     * @param numBytes
327     *            the number of seed bytes.
328     * @return the seed bytes.
329     */
330    public byte[] generateSeed(int numBytes) {
331        return secureRandomSpi.engineGenerateSeed(numBytes);
332    }
333
334}
335