| package org.bouncycastle.jcajce.provider.drbg; |
| |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.security.Provider; |
| import java.security.SecureRandom; |
| import java.security.SecureRandomSpi; |
| |
| import org.bouncycastle.crypto.digests.SHA512Digest; |
| import org.bouncycastle.crypto.prng.EntropySource; |
| import org.bouncycastle.crypto.prng.EntropySourceProvider; |
| import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder; |
| import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; |
| import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; |
| import org.bouncycastle.util.Arrays; |
| import org.bouncycastle.util.Pack; |
| import org.bouncycastle.util.Strings; |
| |
| public class DRBG |
| { |
| private static final String PREFIX = DRBG.class.getName(); |
| |
| // {"Provider class name","SecureRandomSpi class name"} |
| private static final String[][] initialEntropySourceNames = new String[][] |
| { |
| // Normal JVM |
| {"sun.security.provider.Sun", "sun.security.provider.SecureRandom"}, |
| // Apache harmony |
| {"org.apache.harmony.security.provider.crypto.CryptoProvider", "org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl"}, |
| // Android. |
| {"com.android.org.conscrypt.OpenSSLProvider", "com.android.org.conscrypt.OpenSSLRandom"}, |
| {"org.conscrypt.OpenSSLProvider", "org.conscrypt.OpenSSLRandom"}, |
| }; |
| |
| private static final Object[] initialEntropySourceAndSpi = findSource(); |
| |
| // Cascade through providers looking for match. |
| private final static Object[] findSource() |
| { |
| for (int t = 0; t < initialEntropySourceNames.length; t++) |
| { |
| String[] pair = initialEntropySourceNames[t]; |
| try |
| { |
| Object[] r = new Object[]{Class.forName(pair[0]).newInstance(), Class.forName(pair[1]).newInstance()}; |
| |
| return r; |
| } |
| catch (Throwable ex) |
| { |
| continue; |
| } |
| } |
| |
| return null; |
| } |
| |
| private static class CoreSecureRandom |
| extends SecureRandom |
| { |
| CoreSecureRandom() |
| { |
| super((SecureRandomSpi)initialEntropySourceAndSpi[1], (Provider)initialEntropySourceAndSpi[0]); |
| } |
| } |
| |
| // unfortunately new SecureRandom() can cause a regress and it's the only reliable way of getting access |
| // to the JVM's seed generator. |
| private static SecureRandom createInitialEntropySource() |
| { |
| if (initialEntropySourceAndSpi != null) |
| { |
| return new CoreSecureRandom(); |
| } |
| else |
| { |
| return new SecureRandom(); // we're desperate, it's worth a try. |
| } |
| } |
| |
| private static EntropySourceProvider createEntropySource() |
| { |
| final String sourceClass = System.getProperty("org.bouncycastle.drbg.entropysource"); |
| |
| return AccessController.doPrivileged(new PrivilegedAction<EntropySourceProvider>() |
| { |
| public EntropySourceProvider run() |
| { |
| try |
| { |
| Class clazz = DRBG.class.getClassLoader().loadClass(sourceClass); |
| |
| return (EntropySourceProvider)clazz.newInstance(); |
| } |
| catch (Exception e) |
| { |
| throw new IllegalStateException("entropy source " + sourceClass + " not created: " + e.getMessage(), e); |
| } |
| } |
| }); |
| } |
| |
| private static SecureRandom createBaseRandom(boolean isPredictionResistant) |
| { |
| if (System.getProperty("org.bouncycastle.drbg.entropysource") != null) |
| { |
| EntropySourceProvider entropyProvider = createEntropySource(); |
| |
| EntropySource initSource = entropyProvider.get(16 * 8); |
| |
| return new SP800SecureRandomBuilder(entropyProvider) |
| .setPersonalizationString(generateDefaultPersonalizationString(initSource.getEntropy())) |
| .buildHash(new SHA512Digest(), Arrays.concatenate(initSource.getEntropy(), initSource.getEntropy()), isPredictionResistant); |
| } |
| else |
| { |
| SecureRandom randomSource = createInitialEntropySource(); // needs to be done late, can't use static |
| return new SP800SecureRandomBuilder(randomSource, true) |
| .setPersonalizationString(generateDefaultPersonalizationString(randomSource.generateSeed(16))) |
| .buildHash(new SHA512Digest(), randomSource.generateSeed(32), isPredictionResistant); |
| } |
| } |
| |
| public static class Default |
| extends SecureRandomSpi |
| { |
| private static final SecureRandom random = createBaseRandom(true); |
| |
| public Default() |
| { |
| } |
| |
| protected void engineSetSeed(byte[] bytes) |
| { |
| random.setSeed(bytes); |
| } |
| |
| protected void engineNextBytes(byte[] bytes) |
| { |
| random.nextBytes(bytes); |
| } |
| |
| protected byte[] engineGenerateSeed(int numBytes) |
| { |
| return random.generateSeed(numBytes); |
| } |
| } |
| |
| public static class NonceAndIV |
| extends SecureRandomSpi |
| { |
| private static final SecureRandom random = createBaseRandom(false); |
| |
| public NonceAndIV() |
| { |
| } |
| |
| protected void engineSetSeed(byte[] bytes) |
| { |
| random.setSeed(bytes); |
| } |
| |
| protected void engineNextBytes(byte[] bytes) |
| { |
| random.nextBytes(bytes); |
| } |
| |
| protected byte[] engineGenerateSeed(int numBytes) |
| { |
| return random.generateSeed(numBytes); |
| } |
| } |
| |
| public static class Mappings |
| extends AsymmetricAlgorithmProvider |
| { |
| public Mappings() |
| { |
| } |
| |
| public void configure(ConfigurableProvider provider) |
| { |
| provider.addAlgorithm("SecureRandom.DEFAULT", PREFIX + "$Default"); |
| provider.addAlgorithm("SecureRandom.NONCEANDIV", PREFIX + "$NonceAndIV"); |
| } |
| } |
| |
| private static byte[] generateDefaultPersonalizationString(byte[] seed) |
| { |
| return Arrays.concatenate(Strings.toByteArray("Default"), seed, |
| Pack.longToBigEndian(Thread.currentThread().getId()), Pack.longToBigEndian(System.currentTimeMillis())); |
| } |
| |
| private static byte[] generateNonceIVPersonalizationString(byte[] seed) |
| { |
| return Arrays.concatenate(Strings.toByteArray("Nonce"), seed, |
| Pack.longToLittleEndian(Thread.currentThread().getId()), Pack.longToLittleEndian(System.currentTimeMillis())); |
| } |
| } |