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

package org.bouncycastle.crypto.generators;

import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.crypto.params.DSAValidationParameters;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAParameterGenerationParameters;
import org.bouncycastle.crypto.util.DigestFactory;
import java.security.SecureRandom;
import org.bouncycastle.crypto.Digest;
import java.math.BigInteger;

public class DSAParametersGenerator
{
    private static final BigInteger ZERO;
    private static final BigInteger ONE;
    private static final BigInteger TWO;
    private Digest digest;
    private int L;
    private int N;
    private int certainty;
    private int iterations;
    private SecureRandom random;
    private boolean use186_3;
    private int usageIndex;
    
    public DSAParametersGenerator() {
        this(DigestFactory.createSHA1());
    }
    
    public DSAParametersGenerator(final Digest digest) {
        this.digest = digest;
    }
    
    public void init(final int l, final int certainty, final SecureRandom random) {
        this.L = l;
        this.N = getDefaultN(l);
        this.certainty = certainty;
        this.iterations = Math.max(getMinimumIterations(this.L), (certainty + 1) / 2);
        this.random = random;
        this.use186_3 = false;
        this.usageIndex = -1;
    }
    
    public void init(final DSAParameterGenerationParameters dsaParameterGenerationParameters) {
        final int l = dsaParameterGenerationParameters.getL();
        final int n = dsaParameterGenerationParameters.getN();
        if (l < 1024 || l > 3072 || l % 1024 != 0) {
            throw new IllegalArgumentException("L values must be between 1024 and 3072 and a multiple of 1024");
        }
        if (l == 1024 && n != 160) {
            throw new IllegalArgumentException("N must be 160 for L = 1024");
        }
        if (l == 2048 && n != 224 && n != 256) {
            throw new IllegalArgumentException("N must be 224 or 256 for L = 2048");
        }
        if (l == 3072 && n != 256) {
            throw new IllegalArgumentException("N must be 256 for L = 3072");
        }
        if (this.digest.getDigestSize() * 8 < n) {
            throw new IllegalStateException("Digest output size too small for value of N");
        }
        this.L = l;
        this.N = n;
        this.certainty = dsaParameterGenerationParameters.getCertainty();
        this.iterations = Math.max(getMinimumIterations(l), (this.certainty + 1) / 2);
        this.random = dsaParameterGenerationParameters.getRandom();
        this.use186_3 = true;
        this.usageIndex = dsaParameterGenerationParameters.getUsageIndex();
    }
    
    public DSAParameters generateParameters() {
        return this.use186_3 ? this.generateParameters_FIPS186_3() : this.generateParameters_FIPS186_2();
    }
    
    private DSAParameters generateParameters_FIPS186_2() {
        final byte[] bytes = new byte[20];
        final byte[] array = new byte[20];
        final byte[] array2 = new byte[20];
        final byte[] magnitude = new byte[20];
        final int n = (this.L - 1) / 160;
        final byte[] magnitude2 = new byte[this.L / 8];
        if (!(this.digest instanceof SHA1Digest)) {
            throw new IllegalStateException("can only use SHA-1 for generating FIPS 186-2 parameters");
        }
        BigInteger bigInteger = null;
        int j = 0;
        BigInteger subtract = null;
    Block_7:
        while (true) {
            this.random.nextBytes(bytes);
            hash(this.digest, bytes, array, 0);
            System.arraycopy(bytes, 0, array2, 0, bytes.length);
            inc(array2);
            hash(this.digest, array2, array2, 0);
            for (int i = 0; i != magnitude.length; ++i) {
                magnitude[i] = (byte)(array[i] ^ array2[i]);
            }
            final byte[] array3 = magnitude;
            final int n2 = 0;
            array3[n2] |= 0xFFFFFF80;
            final byte[] array4 = magnitude;
            final int n3 = 19;
            array4[n3] |= 0x1;
            bigInteger = new BigInteger(1, magnitude);
            if (!this.isProbablePrime(bigInteger)) {
                continue;
            }
            final byte[] clone = Arrays.clone(bytes);
            inc(clone);
            for (j = 0; j < 4096; ++j) {
                for (int k = 1; k <= n; ++k) {
                    inc(clone);
                    hash(this.digest, clone, magnitude2, magnitude2.length - k * array.length);
                }
                final int n4 = magnitude2.length - n * array.length;
                inc(clone);
                hash(this.digest, clone, array, 0);
                System.arraycopy(array, array.length - n4, magnitude2, 0, n4);
                final byte[] array5 = magnitude2;
                final int n5 = 0;
                array5[n5] |= 0xFFFFFF80;
                final BigInteger bigInteger2 = new BigInteger(1, magnitude2);
                subtract = bigInteger2.subtract(bigInteger2.mod(bigInteger.shiftLeft(1)).subtract(DSAParametersGenerator.ONE));
                if (subtract.bitLength() == this.L) {
                    if (this.isProbablePrime(subtract)) {
                        break Block_7;
                    }
                }
            }
        }
        return new DSAParameters(subtract, bigInteger, calculateGenerator_FIPS186_2(subtract, bigInteger, this.random), new DSAValidationParameters(bytes, j));
    }
    
    private static BigInteger calculateGenerator_FIPS186_2(final BigInteger m, final BigInteger val, final SecureRandom secureRandom) {
        final BigInteger divide = m.subtract(DSAParametersGenerator.ONE).divide(val);
        final BigInteger subtract = m.subtract(DSAParametersGenerator.TWO);
        BigInteger modPow;
        do {
            modPow = BigIntegers.createRandomInRange(DSAParametersGenerator.TWO, subtract, secureRandom).modPow(divide, m);
        } while (modPow.bitLength() <= 1);
        return modPow;
    }
    
    private DSAParameters generateParameters_FIPS186_3() {
        final Digest digest = this.digest;
        final int n = digest.getDigestSize() * 8;
        final byte[] bytes = new byte[this.N / 8];
        final int n2 = (this.L - 1) / n;
        final int n3 = (this.L - 1) % n;
        final byte[] magnitude = new byte[this.L / 8];
        final byte[] magnitude2 = new byte[digest.getDigestSize()];
        BigInteger setBit = null;
        int i = 0;
        BigInteger subtract = null;
    Block_5:
        while (true) {
            this.random.nextBytes(bytes);
            hash(digest, bytes, magnitude2, 0);
            setBit = new BigInteger(1, magnitude2).mod(DSAParametersGenerator.ONE.shiftLeft(this.N - 1)).setBit(0).setBit(this.N - 1);
            if (!this.isProbablePrime(setBit)) {
                continue;
            }
            final byte[] clone = Arrays.clone(bytes);
            for (final int n4 = 4 * this.L, i = 0; i < n4; ++i) {
                for (int j = 1; j <= n2; ++j) {
                    inc(clone);
                    hash(digest, clone, magnitude, magnitude.length - j * magnitude2.length);
                }
                final int n5 = magnitude.length - n2 * magnitude2.length;
                inc(clone);
                hash(digest, clone, magnitude2, 0);
                System.arraycopy(magnitude2, magnitude2.length - n5, magnitude, 0, n5);
                final byte[] array = magnitude;
                final int n6 = 0;
                array[n6] |= 0xFFFFFF80;
                final BigInteger bigInteger = new BigInteger(1, magnitude);
                subtract = bigInteger.subtract(bigInteger.mod(setBit.shiftLeft(1)).subtract(DSAParametersGenerator.ONE));
                if (subtract.bitLength() == this.L) {
                    if (this.isProbablePrime(subtract)) {
                        break Block_5;
                    }
                }
            }
        }
        if (this.usageIndex >= 0) {
            final BigInteger calculateGenerator_FIPS186_3_Verifiable = calculateGenerator_FIPS186_3_Verifiable(digest, subtract, setBit, bytes, this.usageIndex);
            if (calculateGenerator_FIPS186_3_Verifiable != null) {
                return new DSAParameters(subtract, setBit, calculateGenerator_FIPS186_3_Verifiable, new DSAValidationParameters(bytes, i, this.usageIndex));
            }
        }
        return new DSAParameters(subtract, setBit, calculateGenerator_FIPS186_3_Unverifiable(subtract, setBit, this.random), new DSAValidationParameters(bytes, i));
    }
    
    private boolean isProbablePrime(final BigInteger bigInteger) {
        return bigInteger.isProbablePrime(this.certainty);
    }
    
    private static BigInteger calculateGenerator_FIPS186_3_Unverifiable(final BigInteger bigInteger, final BigInteger bigInteger2, final SecureRandom secureRandom) {
        return calculateGenerator_FIPS186_2(bigInteger, bigInteger2, secureRandom);
    }
    
    private static BigInteger calculateGenerator_FIPS186_3_Verifiable(final Digest digest, final BigInteger m, final BigInteger val, final byte[] array, final int n) {
        final BigInteger divide = m.subtract(DSAParametersGenerator.ONE).divide(val);
        final byte[] decodeStrict = Hex.decodeStrict("6767656E");
        final byte[] array2 = new byte[array.length + decodeStrict.length + 1 + 2];
        System.arraycopy(array, 0, array2, 0, array.length);
        System.arraycopy(decodeStrict, 0, array2, array.length, decodeStrict.length);
        array2[array2.length - 3] = (byte)n;
        final byte[] magnitude = new byte[digest.getDigestSize()];
        for (int i = 1; i < 65536; ++i) {
            inc(array2);
            hash(digest, array2, magnitude, 0);
            final BigInteger modPow = new BigInteger(1, magnitude).modPow(divide, m);
            if (modPow.compareTo(DSAParametersGenerator.TWO) >= 0) {
                return modPow;
            }
        }
        return null;
    }
    
    private static void hash(final Digest digest, final byte[] array, final byte[] array2, final int n) {
        digest.update(array, 0, array.length);
        digest.doFinal(array2, n);
    }
    
    private static int getDefaultN(final int n) {
        return (n > 1024) ? 256 : 160;
    }
    
    private static int getMinimumIterations(final int n) {
        return (n <= 1024) ? 40 : (48 + 8 * ((n - 1) / 1024));
    }
    
    private static void inc(final byte[] array) {
        for (int n = array.length - 1; n >= 0 && (array[n] = (byte)(array[n] + 1 & 0xFF)) == 0; --n) {}
    }
    
    static {
        ZERO = BigInteger.valueOf(0L);
        ONE = BigInteger.valueOf(1L);
        TWO = BigInteger.valueOf(2L);
    }
}
