// 
// Decompiled by Procyon v0.6.0
// 

package org.bouncycastle.jcajce.provider.drbg;

import java.io.IOException;
import java.io.InputStream;
import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
import java.util.concurrent.atomic.AtomicReference;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.prng.SP800SecureRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicBoolean;
import java.security.Provider;
import java.security.SecureRandomSpi;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.Strings;
import org.bouncycastle.jcajce.provider.symmetric.util.ClassUtil;
import java.net.URL;
import java.security.Security;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.bouncycastle.crypto.prng.EntropySource;
import org.bouncycastle.crypto.prng.EntropySourceProvider;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder;
import java.security.SecureRandom;
import org.bouncycastle.util.Properties;

public class DRBG
{
    private static final String PREFIX;
    private static final String[][] initialEntropySourceNames;
    private static EntropyDaemon entropyDaemon;
    private static Thread entropyThread;
    
    private static int get256BitsEffectiveEntropySize() {
        return (Properties.asInteger("org.bouncycastle.drbg.effective_256bits_entropy", 282) + 7) / 8 * 8;
    }
    
    private static final Object[] findSource() {
        int i = 0;
        while (i < DRBG.initialEntropySourceNames.length) {
            final String[] array = DRBG.initialEntropySourceNames[i];
            try {
                return new Object[] { Class.forName(array[0]).newInstance(), Class.forName(array[1]).newInstance() };
            }
            catch (final Throwable t) {
                ++i;
                continue;
            }
            break;
        }
        return null;
    }
    
    private static SecureRandom createBaseRandom(final boolean b) {
        if (Properties.getPropertyValue("org.bouncycastle.drbg.entropysource") != null) {
            final EntropySourceProvider entropySource = createEntropySource();
            final EntropySource value = entropySource.get(128);
            return new SP800SecureRandomBuilder(entropySource).setPersonalizationString(b ? generateDefaultPersonalizationString(value.getEntropy()) : generateNonceIVPersonalizationString(value.getEntropy())).buildHash(new SHA512Digest(), value.getEntropy(), b);
        }
        if (Properties.isOverrideSet("org.bouncycastle.drbg.entropy_thread")) {
            synchronized (DRBG.entropyDaemon) {
                if (DRBG.entropyThread == null) {
                    (DRBG.entropyThread = new Thread(DRBG.entropyDaemon, "BC Entropy Daemon")).setDaemon(true);
                    DRBG.entropyThread.start();
                }
            }
            final HybridEntropySource hybridEntropySource = new HybridEntropySource(DRBG.entropyDaemon, 256);
            return new SP800SecureRandomBuilder(new EntropySourceProvider() {
                @Override
                public EntropySource get(final int n) {
                    return new HybridEntropySource(DRBG.entropyDaemon, n);
                }
            }).setPersonalizationString(b ? generateDefaultPersonalizationString(hybridEntropySource.getEntropy()) : generateNonceIVPersonalizationString(hybridEntropySource.getEntropy())).buildHash(new SHA512Digest(), hybridEntropySource.getEntropy(), b);
        }
        final OneShotHybridEntropySource oneShotHybridEntropySource = new OneShotHybridEntropySource(256);
        return new SP800SecureRandomBuilder(new EntropySourceProvider() {
            @Override
            public EntropySource get(final int n) {
                return new OneShotHybridEntropySource(n);
            }
        }).setPersonalizationString(b ? generateDefaultPersonalizationString(oneShotHybridEntropySource.getEntropy()) : generateNonceIVPersonalizationString(oneShotHybridEntropySource.getEntropy())).buildHash(new SHA512Digest(), oneShotHybridEntropySource.getEntropy(), b);
    }
    
    private static EntropySourceProvider createCoreEntropySourceProvider() {
        if (!AccessController.doPrivileged((PrivilegedAction<Boolean>)new PrivilegedAction<Boolean>() {
            @Override
            public Boolean run() {
                try {
                    return SecureRandom.class.getMethod("getInstanceStrong", (Class<?>[])new Class[0]) != null;
                }
                catch (final Exception ex) {
                    return false;
                }
            }
        })) {
            return createInitialEntropySource();
        }
        final SecureRandom secureRandom = AccessController.doPrivileged((PrivilegedAction<SecureRandom>)new PrivilegedAction<SecureRandom>() {
            @Override
            public SecureRandom run() {
                try {
                    return (SecureRandom)SecureRandom.class.getMethod("getInstanceStrong", (Class<?>[])new Class[0]).invoke(null, new Object[0]);
                }
                catch (final Exception ex) {
                    return null;
                }
            }
        });
        if (secureRandom == null) {
            return createInitialEntropySource();
        }
        return new IncrementalEntropySourceProvider(secureRandom, true);
    }
    
    private static EntropySourceProvider createInitialEntropySource() {
        final String spec = AccessController.doPrivileged((PrivilegedAction<String>)new PrivilegedAction<String>() {
            @Override
            public String run() {
                return Security.getProperty("securerandom.source");
            }
        });
        if (spec == null) {
            return new IncrementalEntropySourceProvider(new CoreSecureRandom(findSource()), true);
        }
        try {
            return new URLSeededEntropySourceProvider(new URL(spec));
        }
        catch (final Exception ex) {
            return new IncrementalEntropySourceProvider(new CoreSecureRandom(findSource()), true);
        }
    }
    
    private static EntropySourceProvider createEntropySource() {
        return AccessController.doPrivileged((PrivilegedAction<EntropySourceProvider>)new PrivilegedAction<EntropySourceProvider>() {
            final /* synthetic */ String val$sourceClass = Properties.getPropertyValue("org.bouncycastle.drbg.entropysource");
            
            @Override
            public EntropySourceProvider run() {
                try {
                    return ClassUtil.loadClass(DRBG.class, this.val$sourceClass).newInstance();
                }
                catch (final Exception cause) {
                    throw new IllegalStateException("entropy source " + this.val$sourceClass + " not created: " + cause.getMessage(), cause);
                }
            }
        });
    }
    
    private static byte[] generateDefaultPersonalizationString(final byte[] array) {
        return Arrays.concatenate(Strings.toByteArray("Default"), array, Pack.longToBigEndian(Thread.currentThread().getId()), Pack.longToBigEndian(System.currentTimeMillis()));
    }
    
    private static byte[] generateNonceIVPersonalizationString(final byte[] array) {
        return Arrays.concatenate(Strings.toByteArray("Nonce"), array, Pack.longToLittleEndian(Thread.currentThread().getId()), Pack.longToLittleEndian(System.currentTimeMillis()));
    }
    
    private static void sleep(final long millis) throws InterruptedException {
        if (millis != 0L) {
            Thread.sleep(millis);
        }
    }
    
    static {
        PREFIX = DRBG.class.getName();
        initialEntropySourceNames = new String[][] { { "sun.security.provider.Sun", "sun.security.provider.SecureRandom" }, { "org.apache.harmony.security.provider.crypto.CryptoProvider", "org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl" }, { "com.android.org.conscrypt.OpenSSLProvider", "com.android.org.conscrypt.OpenSSLRandom" }, { "org.conscrypt.OpenSSLProvider", "org.conscrypt.OpenSSLRandom" } };
        DRBG.entropyDaemon = null;
        DRBG.entropyThread = null;
        DRBG.entropyDaemon = new EntropyDaemon();
    }
    
    private static class CoreSecureRandom extends SecureRandom
    {
        CoreSecureRandom(final Object[] array) {
            super((SecureRandomSpi)array[1], (Provider)array[0]);
        }
    }
    
    public static class Default extends SecureRandomSpi
    {
        private static final SecureRandom random;
        
        @Override
        protected void engineSetSeed(final byte[] seed) {
            Default.random.setSeed(seed);
        }
        
        @Override
        protected void engineNextBytes(final byte[] bytes) {
            Default.random.nextBytes(bytes);
        }
        
        @Override
        protected byte[] engineGenerateSeed(final int numBytes) {
            return Default.random.generateSeed(numBytes);
        }
        
        static {
            random = createBaseRandom(true);
        }
    }
    
    private static class HybridEntropySource implements EntropySource
    {
        private final AtomicBoolean seedAvailable;
        private final AtomicInteger samples;
        private final SP800SecureRandom drbg;
        private final SignallingEntropySource entropySource;
        private final int bytesRequired;
        private final byte[] additionalInput;
        
        HybridEntropySource(final EntropyDaemon entropyDaemon, final int n) {
            this.seedAvailable = new AtomicBoolean(false);
            this.samples = new AtomicInteger(0);
            this.additionalInput = Pack.longToBigEndian(System.currentTimeMillis());
            final EntropySourceProvider access$600 = createCoreEntropySourceProvider();
            this.bytesRequired = (n + 7) / 8;
            this.entropySource = new SignallingEntropySource(entropyDaemon, this.seedAvailable, access$600, get256BitsEffectiveEntropySize());
            this.drbg = new SP800SecureRandomBuilder(new EntropySourceProvider() {
                @Override
                public EntropySource get(final int n) {
                    return HybridEntropySource.this.entropySource;
                }
            }).setPersonalizationString(Strings.toByteArray("Bouncy Castle Hybrid Entropy Source")).buildHMAC(new HMac(new SHA512Digest()), this.entropySource.getEntropy(), false);
        }
        
        @Override
        public boolean isPredictionResistant() {
            return true;
        }
        
        @Override
        public byte[] getEntropy() {
            final byte[] array = new byte[this.bytesRequired];
            if (this.samples.getAndIncrement() > 128) {
                if (this.seedAvailable.getAndSet(false)) {
                    this.samples.set(0);
                    this.drbg.reseed(this.additionalInput);
                }
                else {
                    this.entropySource.schedule();
                }
            }
            this.drbg.nextBytes(array);
            return array;
        }
        
        @Override
        public int entropySize() {
            return this.bytesRequired * 8;
        }
        
        private static class SignallingEntropySource implements IncrementalEntropySource
        {
            private final EntropyDaemon entropyDaemon;
            private final AtomicBoolean seedAvailable;
            private final IncrementalEntropySource entropySource;
            private final int byteLength;
            private final AtomicReference entropy;
            private final AtomicBoolean scheduled;
            
            SignallingEntropySource(final EntropyDaemon entropyDaemon, final AtomicBoolean seedAvailable, final EntropySourceProvider entropySourceProvider, final int n) {
                this.entropy = new AtomicReference();
                this.scheduled = new AtomicBoolean(false);
                this.entropyDaemon = entropyDaemon;
                this.seedAvailable = seedAvailable;
                this.entropySource = (IncrementalEntropySource)entropySourceProvider.get(n);
                this.byteLength = (n + 7) / 8;
            }
            
            @Override
            public boolean isPredictionResistant() {
                return true;
            }
            
            @Override
            public byte[] getEntropy() {
                try {
                    return this.getEntropy(0L);
                }
                catch (final InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    throw new IllegalStateException("initial entropy fetch interrupted");
                }
            }
            
            @Override
            public byte[] getEntropy(final long n) throws InterruptedException {
                byte[] entropy = this.entropy.getAndSet(null);
                if (entropy == null || entropy.length != this.byteLength) {
                    entropy = this.entropySource.getEntropy(n);
                }
                else {
                    this.scheduled.set(false);
                }
                return entropy;
            }
            
            void schedule() {
                if (!this.scheduled.getAndSet(true)) {
                    this.entropyDaemon.addTask(new EntropyGatherer(this.entropySource, this.seedAvailable, this.entropy));
                }
            }
            
            @Override
            public int entropySize() {
                return this.byteLength * 8;
            }
        }
    }
    
    public static class Mappings extends AsymmetricAlgorithmProvider
    {
        @Override
        public void configure(final ConfigurableProvider configurableProvider) {
            configurableProvider.addAlgorithm("SecureRandom.DEFAULT", DRBG.PREFIX + "$Default");
            configurableProvider.addAlgorithm("SecureRandom.NONCEANDIV", DRBG.PREFIX + "$NonceAndIV");
        }
    }
    
    public static class NonceAndIV extends SecureRandomSpi
    {
        private static final SecureRandom random;
        
        @Override
        protected void engineSetSeed(final byte[] seed) {
            NonceAndIV.random.setSeed(seed);
        }
        
        @Override
        protected void engineNextBytes(final byte[] bytes) {
            NonceAndIV.random.nextBytes(bytes);
        }
        
        @Override
        protected byte[] engineGenerateSeed(final int numBytes) {
            return NonceAndIV.random.generateSeed(numBytes);
        }
        
        static {
            random = createBaseRandom(false);
        }
    }
    
    private static class OneShotHybridEntropySource implements EntropySource
    {
        private final AtomicBoolean seedAvailable;
        private final AtomicInteger samples;
        private final SP800SecureRandom drbg;
        private final OneShotSignallingEntropySource entropySource;
        private final int bytesRequired;
        private final byte[] additionalInput;
        
        OneShotHybridEntropySource(final int n) {
            this.seedAvailable = new AtomicBoolean(false);
            this.samples = new AtomicInteger(0);
            this.additionalInput = Pack.longToBigEndian(System.currentTimeMillis());
            final EntropySourceProvider access$600 = createCoreEntropySourceProvider();
            this.bytesRequired = (n + 7) / 8;
            this.entropySource = new OneShotSignallingEntropySource(this.seedAvailable, access$600, get256BitsEffectiveEntropySize());
            this.drbg = new SP800SecureRandomBuilder(new EntropySourceProvider() {
                @Override
                public EntropySource get(final int n) {
                    return OneShotHybridEntropySource.this.entropySource;
                }
            }).setPersonalizationString(Strings.toByteArray("Bouncy Castle Hybrid Entropy Source")).buildHMAC(new HMac(new SHA512Digest()), this.entropySource.getEntropy(), false);
        }
        
        @Override
        public boolean isPredictionResistant() {
            return true;
        }
        
        @Override
        public byte[] getEntropy() {
            final byte[] array = new byte[this.bytesRequired];
            if (this.samples.getAndIncrement() > 1024) {
                if (this.seedAvailable.getAndSet(false)) {
                    this.samples.set(0);
                    this.drbg.reseed(this.additionalInput);
                }
                else {
                    this.entropySource.schedule();
                }
            }
            this.drbg.nextBytes(array);
            return array;
        }
        
        @Override
        public int entropySize() {
            return this.bytesRequired * 8;
        }
        
        private static class OneShotSignallingEntropySource implements IncrementalEntropySource
        {
            private final AtomicBoolean seedAvailable;
            private final IncrementalEntropySource entropySource;
            private final int byteLength;
            private final AtomicReference entropy;
            private final AtomicBoolean scheduled;
            
            OneShotSignallingEntropySource(final AtomicBoolean seedAvailable, final EntropySourceProvider entropySourceProvider, final int n) {
                this.entropy = new AtomicReference();
                this.scheduled = new AtomicBoolean(false);
                this.seedAvailable = seedAvailable;
                this.entropySource = (IncrementalEntropySource)entropySourceProvider.get(n);
                this.byteLength = (n + 7) / 8;
            }
            
            @Override
            public boolean isPredictionResistant() {
                return true;
            }
            
            @Override
            public byte[] getEntropy() {
                try {
                    return this.getEntropy(0L);
                }
                catch (final InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    throw new IllegalStateException("initial entropy fetch interrupted");
                }
            }
            
            @Override
            public byte[] getEntropy(final long n) throws InterruptedException {
                byte[] entropy = this.entropy.getAndSet(null);
                if (entropy == null || entropy.length != this.byteLength) {
                    entropy = this.entropySource.getEntropy(n);
                }
                else {
                    this.scheduled.set(false);
                }
                return entropy;
            }
            
            void schedule() {
                if (!this.scheduled.getAndSet(true)) {
                    final Thread thread = new Thread(new EntropyGatherer(this.entropySource, this.seedAvailable, this.entropy));
                    thread.setDaemon(true);
                    thread.start();
                }
            }
            
            @Override
            public int entropySize() {
                return this.byteLength * 8;
            }
        }
    }
    
    private static class URLSeededEntropySourceProvider implements EntropySourceProvider
    {
        private final InputStream seedStream;
        
        URLSeededEntropySourceProvider(final URL url) {
            this.seedStream = AccessController.doPrivileged((PrivilegedAction<InputStream>)new PrivilegedAction<InputStream>() {
                @Override
                public InputStream run() {
                    try {
                        return url.openStream();
                    }
                    catch (final IOException ex) {
                        throw new IllegalStateException("unable to open random source");
                    }
                }
            });
        }
        
        private int privilegedRead(final byte[] array, final int n, final int n2) {
            return AccessController.doPrivileged((PrivilegedAction<Integer>)new PrivilegedAction<Integer>() {
                @Override
                public Integer run() {
                    try {
                        return URLSeededEntropySourceProvider.this.seedStream.read(array, n, n2);
                    }
                    catch (final IOException ex) {
                        throw new InternalError("unable to read random source");
                    }
                }
            });
        }
        
        @Override
        public EntropySource get(final int n) {
            return new IncrementalEntropySource() {
                private final int numBytes = (n + 7) / 8;
                
                @Override
                public boolean isPredictionResistant() {
                    return true;
                }
                
                @Override
                public byte[] getEntropy() {
                    try {
                        return this.getEntropy(0L);
                    }
                    catch (final InterruptedException ex) {
                        Thread.currentThread().interrupt();
                        throw new IllegalStateException("initial entropy fetch interrupted");
                    }
                }
                
                @Override
                public byte[] getEntropy(final long n) throws InterruptedException {
                    final byte[] array = new byte[this.numBytes];
                    int n2 = 0;
                    int access$400;
                    while (n2 != array.length && (access$400 = URLSeededEntropySourceProvider.this.privilegedRead(array, n2, array.length - n2)) > -1) {
                        n2 += access$400;
                        sleep(n);
                    }
                    if (n2 != array.length) {
                        throw new InternalError("unable to fully read random source");
                    }
                    return array;
                }
                
                @Override
                public int entropySize() {
                    return n;
                }
            };
        }
    }
}
