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

package org.bouncycastle.crypto.kems;

import org.bouncycastle.crypto.DerivationParameters;
import org.bouncycastle.crypto.params.KDFParameters;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.BigIntegers;
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.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.SecretWithEncapsulation;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.math.ec.ECMultiplier;
import java.security.SecureRandom;
import org.bouncycastle.crypto.DerivationFunction;
import java.math.BigInteger;
import org.bouncycastle.crypto.EncapsulatedSecretGenerator;

public class ECIESKEMGenerator implements EncapsulatedSecretGenerator
{
    private static final BigInteger ONE;
    private DerivationFunction kdf;
    private SecureRandom rnd;
    private final int keySize;
    private boolean CofactorMode;
    private boolean OldCofactorMode;
    private boolean SingleHashMode;
    
    public ECIESKEMGenerator(final int keySize, final DerivationFunction kdf, final SecureRandom rnd) {
        this.keySize = keySize;
        this.kdf = kdf;
        this.rnd = rnd;
        this.CofactorMode = false;
        this.OldCofactorMode = false;
        this.SingleHashMode = false;
    }
    
    public ECIESKEMGenerator(final int keySize, final DerivationFunction kdf, final SecureRandom rnd, final boolean cofactorMode, final boolean oldCofactorMode, final boolean singleHashMode) {
        this.kdf = kdf;
        this.rnd = rnd;
        this.keySize = keySize;
        this.CofactorMode = cofactorMode;
        if (cofactorMode) {
            this.OldCofactorMode = false;
        }
        else {
            this.OldCofactorMode = oldCofactorMode;
        }
        this.SingleHashMode = singleHashMode;
    }
    
    private ECMultiplier createBasePointMultiplier() {
        return new FixedPointCombMultiplier();
    }
    
    @Override
    public SecretWithEncapsulation generateEncapsulated(final AsymmetricKeyParameter asymmetricKeyParameter) {
        if (!(asymmetricKeyParameter instanceof ECPublicKeyParameters)) {
            throw new IllegalArgumentException("EC public key required");
        }
        final ECPublicKeyParameters ecPublicKeyParameters = (ECPublicKeyParameters)asymmetricKeyParameter;
        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("ECIESKem", ConstraintUtils.bitsOfSecurityFor(ecPublicKeyParameters.getParameters().getCurve()), asymmetricKeyParameter, CryptoServicePurpose.ENCRYPTION));
        final ECDomainParameters parameters = ecPublicKeyParameters.getParameters();
        final ECCurve curve = parameters.getCurve();
        final BigInteger n = parameters.getN();
        final BigInteger h = parameters.getH();
        final BigInteger randomInRange = BigIntegers.createRandomInRange(ECIESKEMGenerator.ONE, n, this.rnd);
        final ECPoint[] array = { this.createBasePointMultiplier().multiply(parameters.getG(), randomInRange), ecPublicKeyParameters.getQ().multiply(this.OldCofactorMode ? randomInRange.multiply(h).mod(n) : randomInRange) };
        curve.normalizeAll(array);
        final ECPoint ecPoint = array[0];
        final ECPoint ecPoint2 = array[1];
        final byte[] encoded = ecPoint.getEncoded(false);
        final byte[] array2 = new byte[encoded.length];
        System.arraycopy(encoded, 0, array2, 0, encoded.length);
        return new SecretWithEncapsulationImpl(deriveKey(this.SingleHashMode, this.kdf, this.keySize, encoded, ecPoint2.getAffineXCoord().getEncoded()), array2);
    }
    
    static byte[] deriveKey(final boolean b, final DerivationFunction derivationFunction, final int n, final byte[] array, final byte[] array2) {
        byte[] concatenate = array2;
        if (!b) {
            concatenate = Arrays.concatenate(array, array2);
            Arrays.fill(array2, (byte)0);
        }
        try {
            derivationFunction.init(new KDFParameters(concatenate, null));
            final byte[] array3 = new byte[n];
            derivationFunction.generateBytes(array3, 0, array3.length);
            return array3;
        }
        finally {
            Arrays.fill(concatenate, (byte)0);
        }
    }
    
    static {
        ONE = BigInteger.valueOf(1L);
    }
}
