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

package org.bouncycastle.crypto.kems;

import org.bouncycastle.crypto.Digest;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters;
import org.bouncycastle.crypto.SecretWithEncapsulation;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import java.security.SecureRandom;
import org.bouncycastle.crypto.EncapsulatedSecretGenerator;

public class SAKKEKEMSGenerator implements EncapsulatedSecretGenerator
{
    private final SecureRandom random;
    
    public SAKKEKEMSGenerator(final SecureRandom random) {
        this.random = random;
    }
    
    @Override
    public SecretWithEncapsulation generateEncapsulated(final AsymmetricKeyParameter asymmetricKeyParameter) {
        final SAKKEPublicKeyParameters sakkePublicKeyParameters = (SAKKEPublicKeyParameters)asymmetricKeyParameter;
        final ECPoint z = sakkePublicKeyParameters.getZ();
        final BigInteger identifier = sakkePublicKeyParameters.getIdentifier();
        final BigInteger prime = sakkePublicKeyParameters.getPrime();
        final BigInteger q = sakkePublicKeyParameters.getQ();
        final BigInteger g = sakkePublicKeyParameters.getG();
        final int n = sakkePublicKeyParameters.getN();
        final ECCurve curve = sakkePublicKeyParameters.getCurve();
        final ECPoint point = sakkePublicKeyParameters.getPoint();
        final Digest digest = sakkePublicKeyParameters.getDigest();
        final BigInteger randomBigInteger = BigIntegers.createRandomBigInteger(n, this.random);
        final BigInteger hashToIntegerRange = hashToIntegerRange(Arrays.concatenate(randomBigInteger.toByteArray(), identifier.toByteArray()), q, digest);
        final BigInteger order = curve.getOrder();
        ECPoint ecPoint;
        if (order == null) {
            ecPoint = point.multiply(identifier).add(z).multiply(hashToIntegerRange).normalize();
        }
        else {
            ecPoint = ECAlgorithms.sumOfTwoMultiplies(point, identifier.multiply(hashToIntegerRange).mod(order), z, hashToIntegerRange).normalize();
        }
        final BigInteger one = BigInteger.ONE;
        final BigInteger bigInteger = g;
        BigInteger one2 = BigInteger.ONE;
        BigInteger bigInteger2 = g;
        ECPoint ecPoint2 = curve.createPoint(one2, bigInteger2);
        for (int i = hashToIntegerRange.bitLength() - 2; i >= 0; --i) {
            final BigInteger[] fp2PointSquare = SAKKEKEMExtractor.fp2PointSquare(one2, bigInteger2, prime);
            ecPoint2 = ecPoint2.timesPow2(2);
            one2 = fp2PointSquare[0];
            bigInteger2 = fp2PointSquare[1];
            if (hashToIntegerRange.testBit(i)) {
                final BigInteger[] fp2Multiply = SAKKEKEMExtractor.fp2Multiply(one2, bigInteger2, one, bigInteger, prime);
                one2 = fp2Multiply[0];
                bigInteger2 = fp2Multiply[1];
            }
        }
        return new SecretWithEncapsulationImpl(BigIntegers.asUnsignedByteArray(n / 8, randomBigInteger), Arrays.concatenate(ecPoint.getEncoded(false), BigIntegers.asUnsignedByteArray(16, randomBigInteger.xor(hashToIntegerRange(bigInteger2.multiply(BigIntegers.modOddInverse(prime, one2)).mod(prime).toByteArray(), BigInteger.ONE.shiftLeft(n), digest)))));
    }
    
    static BigInteger hashToIntegerRange(final byte[] array, final BigInteger m, final Digest digest) {
        final byte[] array2 = new byte[digest.getDigestSize()];
        digest.update(array, 0, array.length);
        digest.doFinal(array2, 0);
        final byte[] array3 = new byte[digest.getDigestSize()];
        final int n = m.bitLength() >> 8;
        BigInteger bigInteger = BigInteger.ZERO;
        final byte[] magnitude = new byte[digest.getDigestSize()];
        for (int i = 0; i <= n; ++i) {
            digest.update(array3, 0, array3.length);
            digest.doFinal(array3, 0);
            digest.update(array3, 0, array3.length);
            digest.update(array2, 0, array2.length);
            digest.doFinal(magnitude, 0);
            bigInteger = bigInteger.shiftLeft(magnitude.length * 8).add(new BigInteger(1, magnitude));
        }
        return bigInteger.mod(m);
    }
}
