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

package org.bouncycastle.crypto.generators;

import org.bouncycastle.math.Primes;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.math.ec.WNafUtil;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CryptoServiceProperties;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.constraints.ConstraintUtils;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import java.math.BigInteger;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;

public class RSAKeyPairGenerator implements AsymmetricCipherKeyPairGenerator
{
    private static final BigInteger ONE;
    private RSAKeyGenerationParameters param;
    
    @Override
    public void init(final KeyGenerationParameters keyGenerationParameters) {
        this.param = (RSAKeyGenerationParameters)keyGenerationParameters;
        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("RSAKeyGen", ConstraintUtils.bitsOfSecurityForFF(keyGenerationParameters.getStrength()), null, CryptoServicePurpose.KEYGEN));
    }
    
    @Override
    public AsymmetricCipherKeyPair generateKeyPair() {
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = null;
        int i = 0;
        final int strength = this.param.getStrength();
        final int n = (strength + 1) / 2;
        final int n2 = strength - n;
        int n3 = strength / 2 - 100;
        if (n3 < strength / 3) {
            n3 = strength / 3;
        }
        final int n4 = strength >> 2;
        final BigInteger pow = BigInteger.valueOf(2L).pow(strength / 2);
        final BigInteger shiftLeft = RSAKeyPairGenerator.ONE.shiftLeft(strength - 1);
        final BigInteger shiftLeft2 = RSAKeyPairGenerator.ONE.shiftLeft(n3);
        while (i == 0) {
            final BigInteger publicExponent = this.param.getPublicExponent();
            BigInteger val = this.chooseRandomPrime(n, publicExponent, shiftLeft);
            BigInteger chooseRandomPrime;
            BigInteger multiply;
            while (true) {
                chooseRandomPrime = this.chooseRandomPrime(n2, publicExponent, shiftLeft);
                final BigInteger abs = chooseRandomPrime.subtract(val).abs();
                if (abs.bitLength() >= n3) {
                    if (abs.compareTo(shiftLeft2) <= 0) {
                        continue;
                    }
                    multiply = val.multiply(chooseRandomPrime);
                    if (multiply.bitLength() != strength) {
                        val = val.max(chooseRandomPrime);
                    }
                    else {
                        if (WNafUtil.getNafWeight(multiply) >= n4) {
                            break;
                        }
                        val = this.chooseRandomPrime(n, publicExponent, shiftLeft);
                    }
                }
            }
            if (val.compareTo(chooseRandomPrime) < 0) {
                final BigInteger bigInteger = val;
                val = chooseRandomPrime;
                chooseRandomPrime = bigInteger;
            }
            final BigInteger subtract = val.subtract(RSAKeyPairGenerator.ONE);
            final BigInteger subtract2 = chooseRandomPrime.subtract(RSAKeyPairGenerator.ONE);
            final BigInteger modInverse = publicExponent.modInverse(subtract.divide(subtract.gcd(subtract2)).multiply(subtract2));
            if (modInverse.compareTo(pow) <= 0) {
                continue;
            }
            i = 1;
            asymmetricCipherKeyPair = new AsymmetricCipherKeyPair(new RSAKeyParameters(false, multiply, publicExponent, true), new RSAPrivateCrtKeyParameters(multiply, publicExponent, modInverse, val, chooseRandomPrime, modInverse.remainder(subtract), modInverse.remainder(subtract2), BigIntegers.modOddInverse(val, chooseRandomPrime), true));
        }
        return asymmetricCipherKeyPair;
    }
    
    protected BigInteger chooseRandomPrime(final int n, final BigInteger m, final BigInteger val) {
        for (int i = 0; i != 5 * n; ++i) {
            final BigInteger randomPrime = BigIntegers.createRandomPrime(n, 1, this.param.getRandom());
            if (!randomPrime.mod(m).equals(RSAKeyPairGenerator.ONE)) {
                if (randomPrime.multiply(randomPrime).compareTo(val) >= 0) {
                    if (this.isProbablePrime(randomPrime)) {
                        if (m.gcd(randomPrime.subtract(RSAKeyPairGenerator.ONE)).equals(RSAKeyPairGenerator.ONE)) {
                            return randomPrime;
                        }
                    }
                }
            }
        }
        throw new IllegalStateException("unable to generate prime number for RSA key");
    }
    
    protected boolean isProbablePrime(final BigInteger bigInteger) {
        final int numberOfIterations = getNumberOfIterations(bigInteger.bitLength(), this.param.getCertainty());
        return !Primes.hasAnySmallFactors(bigInteger) && Primes.isMRProbablePrime(bigInteger, this.param.getRandom(), numberOfIterations);
    }
    
    private static int getNumberOfIterations(final int n, final int n2) {
        if (n >= 1536) {
            return (n2 <= 100) ? 3 : ((n2 <= 128) ? 4 : (4 + (n2 - 128 + 1) / 2));
        }
        if (n >= 1024) {
            return (n2 <= 100) ? 4 : ((n2 <= 112) ? 5 : (5 + (n2 - 112 + 1) / 2));
        }
        if (n >= 512) {
            return (n2 <= 80) ? 5 : ((n2 <= 100) ? 7 : (7 + (n2 - 100 + 1) / 2));
        }
        return (n2 <= 80) ? 40 : (40 + (n2 - 80 + 1) / 2);
    }
    
    static {
        ONE = BigInteger.valueOf(1L);
    }
}
