/* * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.provider; import java.io.IOException; import java.security.MessageDigest; import java.security.SecureRandomSpi; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; /** *
This class provides a crytpographically strong pseudo-random number * generator based on the SHA-1 hash algorithm. * *
Note that if a seed is not provided, we attempt to provide sufficient * seed bytes to completely randomize the internal state of the generator * (20 bytes). However, our seed generation algorithm has not been thoroughly * studied or widely deployed. * *
Also note that when a random object is deserialized, * engineNextBytes invoked on the * restored random object will yield the exact same (random) bytes as the * original object. If this behaviour is not desired, the restored random * object should be seeded, using * engineSetSeed. * * @author Benjamin Renaud * @author Josh Bloch * @author Gadi Guy */ public final class SecureRandom extends SecureRandomSpi implements java.io.Serializable { private static final long serialVersionUID = 3581829991155417889L; private static final int DIGEST_SIZE = 20; private transient MessageDigest digest; private byte[] state; private byte[] remainder; private int remCount; /** * This empty constructor automatically seeds the generator. We attempt * to provide sufficient seed bytes to completely randomize the internal * state of the generator (20 bytes). Note, however, that our seed * generation algorithm has not been thoroughly studied or widely deployed. * *
The first time this constructor is called in a given Virtual Machine, * it may take several seconds of CPU time to seed the generator, depending * on the underlying hardware. Successive calls run quickly because they * rely on the same (internal) pseudo-random number generator for their * seed bits. */ public SecureRandom() { init(null); } /** * This constructor is used to instantiate the private seeder object * with a given seed from the SeedGenerator. * * @param seed the seed. */ private SecureRandom(byte[] seed) { init(seed); } /** * This call, used by the constructors, instantiates the SHA digest * and sets the seed, if given. */ private void init(byte[] seed) { try { /* * Use the local SUN implementation to avoid native * performance overhead. */ digest = MessageDigest.getInstance("SHA", "SUN"); } catch (NoSuchProviderException | NoSuchAlgorithmException e) { // Fallback to any available. try { digest = MessageDigest.getInstance("SHA"); } catch (NoSuchAlgorithmException exc) { throw new InternalError( "internal error: SHA-1 not available.", exc); } } if (seed != null) { engineSetSeed(seed); } } /** * Returns the given number of seed bytes, computed using the seed * generation algorithm that this class uses to seed itself. This * call may be used to seed other random number generators. While * we attempt to return a "truly random" sequence of bytes, we do not * know exactly how random the bytes returned by this call are. (See * the empty constructor SecureRandom * for a brief description of the underlying algorithm.) * The prudent user will err on the side of caution and get extra * seed bytes, although it should be noted that seed generation is * somewhat costly. * * @param numBytes the number of seed bytes to generate. * * @return the seed bytes. */ @Override public byte[] engineGenerateSeed(int numBytes) { // Neither of the SeedGenerator implementations require // locking, so no sync needed here. byte[] b = new byte[numBytes]; SeedGenerator.generateSeed(b); return b; } /** * Reseeds this random object. The given seed supplements, rather than * replaces, the existing seed. Thus, repeated calls are guaranteed * never to reduce randomness. * * @param seed the seed. */ @Override public synchronized void engineSetSeed(byte[] seed) { if (state != null) { digest.update(state); for (int i = 0; i < state.length; i++) { state[i] = 0; } } state = digest.digest(seed); } private static void updateState(byte[] state, byte[] output) { int last = 1; int v; byte t; boolean zf = false; // state(n + 1) = (state(n) + output(n) + 1) % 2^160; for (int i = 0; i < state.length; i++) { // Add two bytes v = (int)state[i] + (int)output[i] + last; // Result is lower 8 bits t = (byte)v; // Store result. Check for state collision. zf = zf | (state[i] != t); state[i] = t; // High 8 bits are carry. Store for next iteration. last = v >> 8; } // Make sure at least one bit changes! if (!zf) { state[0]++; } } /** * This static object will be seeded by SeedGenerator, and used * to seed future instances of SHA1PRNG SecureRandoms. * * Bloch, Effective Java Second Edition: Item 71 */ private static class SeederHolder { private static final SecureRandom seeder; static { /* * Call to SeedGenerator.generateSeed() to add additional * seed material (likely from the Native implementation). */ seeder = new SecureRandom(SeedGenerator.getSystemEntropy()); byte [] b = new byte[DIGEST_SIZE]; SeedGenerator.generateSeed(b); seeder.engineSetSeed(b); } } /** * Generates a user-specified number of random bytes. * * @param result the array to be filled in with random bytes. */ @Override public synchronized void engineNextBytes(byte[] result) { int index = 0; int todo; byte[] output = remainder; if (state == null) { byte[] seed = new byte[DIGEST_SIZE]; SeederHolder.seeder.engineNextBytes(seed); state = digest.digest(seed); } // Use remainder from last time int r = remCount; if (r > 0) { // How many bytes? todo = (result.length - index) < (DIGEST_SIZE - r) ? (result.length - index) : (DIGEST_SIZE - r); // Copy the bytes, zero the buffer for (int i = 0; i < todo; i++) { result[i] = output[r]; output[r++] = 0; } remCount += todo; index += todo; } // If we need more bytes, make them. while (index < result.length) { // Step the state digest.update(state); output = digest.digest(); updateState(state, output); // How many bytes? todo = (result.length - index) > DIGEST_SIZE ? DIGEST_SIZE : result.length - index; // Copy the bytes, zero the buffer for (int i = 0; i < todo; i++) { result[index++] = output[i]; output[i] = 0; } remCount += todo; } // Store remainder for next time remainder = output; remCount %= DIGEST_SIZE; } /* * readObject is called to restore the state of the random object from * a stream. We have to create a new instance of MessageDigest, because * it is not included in the stream (it is marked "transient"). * * Note that the engineNextBytes() method invoked on the restored random * object will yield the exact same (random) bytes as the original. * If you do not want this behaviour, you should re-seed the restored * random object, using engineSetSeed(). */ private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject (); try { /* * Use the local SUN implementation to avoid native * performance overhead. */ digest = MessageDigest.getInstance("SHA", "SUN"); } catch (NoSuchProviderException | NoSuchAlgorithmException e) { // Fallback to any available. try { digest = MessageDigest.getInstance("SHA"); } catch (NoSuchAlgorithmException exc) { throw new InternalError( "internal error: SHA-1 not available.", exc); } } } }