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

package org.bouncycastle.crypto.hpke;

import org.bouncycastle.crypto.CryptoServicesRegistrar;
import java.security.SecureRandom;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.Strings;
import org.bouncycastle.math.ec.WNafUtil;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import java.math.BigInteger;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.math.ec.rfc7748.X25519;
import org.bouncycastle.crypto.params.X25519PrivateKeyParameters;
import org.bouncycastle.math.ec.rfc7748.X448;
import org.bouncycastle.crypto.params.X448PrivateKeyParameters;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.X25519PublicKeyParameters;
import org.bouncycastle.crypto.params.X448PublicKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.X448KeyGenerationParameters;
import org.bouncycastle.crypto.generators.X448KeyPairGenerator;
import org.bouncycastle.crypto.agreement.X448Agreement;
import org.bouncycastle.crypto.params.X25519KeyGenerationParameters;
import org.bouncycastle.crypto.generators.X25519KeyPairGenerator;
import org.bouncycastle.crypto.agreement.X25519Agreement;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.BasicAgreement;
import org.bouncycastle.crypto.agreement.BasicRawAgreement;
import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.RawAgreement;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;

class DHKEM extends KEM
{
    private AsymmetricCipherKeyPairGenerator kpGen;
    private RawAgreement rawAgreement;
    private final short kemId;
    private HKDF hkdf;
    private byte bitmask;
    private int Nsk;
    private int Nsecret;
    private int Nenc;
    ECDomainParameters domainParams;
    
    protected DHKEM(final short kemId) {
        switch (this.kemId = kemId) {
            case 16: {
                this.hkdf = new HKDF((short)1);
                this.domainParams = getDomainParameters("P-256");
                this.rawAgreement = new BasicRawAgreement(new ECDHCBasicAgreement());
                this.bitmask = -1;
                this.Nsk = 32;
                this.Nsecret = 32;
                this.Nenc = 65;
                (this.kpGen = new ECKeyPairGenerator()).init(new ECKeyGenerationParameters(this.domainParams, getSecureRandom()));
                break;
            }
            case 17: {
                this.hkdf = new HKDF((short)2);
                this.domainParams = getDomainParameters("P-384");
                this.rawAgreement = new BasicRawAgreement(new ECDHCBasicAgreement());
                this.bitmask = -1;
                this.Nsk = 48;
                this.Nsecret = 48;
                this.Nenc = 97;
                (this.kpGen = new ECKeyPairGenerator()).init(new ECKeyGenerationParameters(this.domainParams, getSecureRandom()));
                break;
            }
            case 18: {
                this.hkdf = new HKDF((short)3);
                this.domainParams = getDomainParameters("P-521");
                this.rawAgreement = new BasicRawAgreement(new ECDHCBasicAgreement());
                this.bitmask = 1;
                this.Nsk = 66;
                this.Nsecret = 64;
                this.Nenc = 133;
                (this.kpGen = new ECKeyPairGenerator()).init(new ECKeyGenerationParameters(this.domainParams, getSecureRandom()));
                break;
            }
            case 32: {
                this.hkdf = new HKDF((short)1);
                this.rawAgreement = new X25519Agreement();
                this.Nsecret = 32;
                this.Nsk = 32;
                this.Nenc = 32;
                (this.kpGen = new X25519KeyPairGenerator()).init(new X25519KeyGenerationParameters(getSecureRandom()));
                break;
            }
            case 33: {
                this.hkdf = new HKDF((short)3);
                this.rawAgreement = new X448Agreement();
                this.Nsecret = 64;
                this.Nsk = 56;
                this.Nenc = 56;
                (this.kpGen = new X448KeyPairGenerator()).init(new X448KeyGenerationParameters(getSecureRandom()));
                break;
            }
            default: {
                throw new IllegalArgumentException("invalid kem id");
            }
        }
    }
    
    public byte[] SerializePublicKey(final AsymmetricKeyParameter asymmetricKeyParameter) {
        switch (this.kemId) {
            case 16:
            case 17:
            case 18: {
                return ((ECPublicKeyParameters)asymmetricKeyParameter).getQ().getEncoded(false);
            }
            case 33: {
                return ((X448PublicKeyParameters)asymmetricKeyParameter).getEncoded();
            }
            case 32: {
                return ((X25519PublicKeyParameters)asymmetricKeyParameter).getEncoded();
            }
            default: {
                throw new IllegalStateException("invalid kem id");
            }
        }
    }
    
    public byte[] SerializePrivateKey(final AsymmetricKeyParameter asymmetricKeyParameter) {
        switch (this.kemId) {
            case 16:
            case 17:
            case 18: {
                return BigIntegers.asUnsignedByteArray(this.Nsk, ((ECPrivateKeyParameters)asymmetricKeyParameter).getD());
            }
            case 33: {
                final byte[] encoded = ((X448PrivateKeyParameters)asymmetricKeyParameter).getEncoded();
                X448.clampPrivateKey(encoded);
                return encoded;
            }
            case 32: {
                final byte[] encoded2 = ((X25519PrivateKeyParameters)asymmetricKeyParameter).getEncoded();
                X25519.clampPrivateKey(encoded2);
                return encoded2;
            }
            default: {
                throw new IllegalStateException("invalid kem id");
            }
        }
    }
    
    public AsymmetricKeyParameter DeserializePublicKey(final byte[] array) {
        if (array == null) {
            throw new NullPointerException("'pkEncoded' cannot be null");
        }
        if (array.length != this.Nenc) {
            throw new IllegalArgumentException("'pkEncoded' has invalid length");
        }
        switch (this.kemId) {
            case 16:
            case 17:
            case 18: {
                if (array[0] != 4) {
                    throw new IllegalArgumentException("'pkEncoded' has invalid format");
                }
                return new ECPublicKeyParameters(this.domainParams.getCurve().decodePoint(array), this.domainParams);
            }
            case 33: {
                return new X448PublicKeyParameters(array);
            }
            case 32: {
                return new X25519PublicKeyParameters(array);
            }
            default: {
                throw new IllegalStateException("invalid kem id");
            }
        }
    }
    
    public AsymmetricCipherKeyPair DeserializePrivateKey(final byte[] magnitude, final byte[] array) {
        if (magnitude == null) {
            throw new NullPointerException("'skEncoded' cannot be null");
        }
        if (magnitude.length != this.Nsk) {
            throw new IllegalArgumentException("'skEncoded' has invalid length");
        }
        AsymmetricKeyParameter asymmetricKeyParameter = null;
        if (array != null) {
            asymmetricKeyParameter = this.DeserializePublicKey(array);
        }
        switch (this.kemId) {
            case 16:
            case 17:
            case 18: {
                final ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, magnitude), this.domainParams);
                if (asymmetricKeyParameter == null) {
                    asymmetricKeyParameter = new ECPublicKeyParameters(new FixedPointCombMultiplier().multiply(this.domainParams.getG(), ecPrivateKeyParameters.getD()), this.domainParams);
                }
                return new AsymmetricCipherKeyPair(asymmetricKeyParameter, ecPrivateKeyParameters);
            }
            case 33: {
                final X448PrivateKeyParameters x448PrivateKeyParameters = new X448PrivateKeyParameters(magnitude);
                if (asymmetricKeyParameter == null) {
                    asymmetricKeyParameter = x448PrivateKeyParameters.generatePublicKey();
                }
                return new AsymmetricCipherKeyPair(asymmetricKeyParameter, x448PrivateKeyParameters);
            }
            case 32: {
                final X25519PrivateKeyParameters x25519PrivateKeyParameters = new X25519PrivateKeyParameters(magnitude);
                if (asymmetricKeyParameter == null) {
                    asymmetricKeyParameter = x25519PrivateKeyParameters.generatePublicKey();
                }
                return new AsymmetricCipherKeyPair(asymmetricKeyParameter, x25519PrivateKeyParameters);
            }
            default: {
                throw new IllegalStateException("invalid kem id");
            }
        }
    }
    
    @Override
    int getEncryptionSize() {
        return this.Nenc;
    }
    
    private boolean validateSk(final BigInteger bigInteger) {
        final BigInteger n = this.domainParams.getN();
        final int n2 = n.bitLength() >>> 2;
        return bigInteger.compareTo(BigInteger.valueOf(1L)) >= 0 && bigInteger.compareTo(n) < 0 && WNafUtil.getNafWeight(bigInteger) >= n2;
    }
    
    public AsymmetricCipherKeyPair GeneratePrivateKey() {
        return this.kpGen.generateKeyPair();
    }
    
    public AsymmetricCipherKeyPair DeriveKeyPair(final byte[] array) {
        final byte[] concatenate = Arrays.concatenate(Strings.toByteArray("KEM"), Pack.shortToBigEndian(this.kemId));
        switch (this.kemId) {
            case 16:
            case 17:
            case 18: {
                final byte[] labeledExtract = this.hkdf.LabeledExtract(null, concatenate, "dkp_prk", array);
                final byte[] array2 = { 0 };
                for (int i = 0; i < 256; ++i) {
                    array2[0] = (byte)i;
                    final byte[] labeledExpand = this.hkdf.LabeledExpand(labeledExtract, concatenate, "candidate", array2, this.Nsk);
                    labeledExpand[0] &= this.bitmask;
                    final BigInteger bigInteger = new BigInteger(1, labeledExpand);
                    if (this.validateSk(bigInteger)) {
                        return new AsymmetricCipherKeyPair(new ECPublicKeyParameters(new FixedPointCombMultiplier().multiply(this.domainParams.getG(), bigInteger), this.domainParams), new ECPrivateKeyParameters(bigInteger, this.domainParams));
                    }
                }
                throw new IllegalStateException("DeriveKeyPairError");
            }
            case 33: {
                final X448PrivateKeyParameters x448PrivateKeyParameters = new X448PrivateKeyParameters(this.hkdf.LabeledExpand(this.hkdf.LabeledExtract(null, concatenate, "dkp_prk", array), concatenate, "sk", null, this.Nsk));
                return new AsymmetricCipherKeyPair(x448PrivateKeyParameters.generatePublicKey(), x448PrivateKeyParameters);
            }
            case 32: {
                final X25519PrivateKeyParameters x25519PrivateKeyParameters = new X25519PrivateKeyParameters(this.hkdf.LabeledExpand(this.hkdf.LabeledExtract(null, concatenate, "dkp_prk", array), concatenate, "sk", null, this.Nsk));
                return new AsymmetricCipherKeyPair(x25519PrivateKeyParameters.generatePublicKey(), x25519PrivateKeyParameters);
            }
            default: {
                throw new IllegalStateException("invalid kem id");
            }
        }
    }
    
    protected byte[][] Encap(final AsymmetricKeyParameter asymmetricKeyParameter) {
        return this.Encap(asymmetricKeyParameter, this.kpGen.generateKeyPair());
    }
    
    protected byte[][] Encap(final AsymmetricKeyParameter asymmetricKeyParameter, final AsymmetricCipherKeyPair asymmetricCipherKeyPair) {
        final byte[][] array = new byte[2][];
        final byte[] calculateRawAgreement = calculateRawAgreement(this.rawAgreement, asymmetricCipherKeyPair.getPrivate(), asymmetricKeyParameter);
        final byte[] serializePublicKey = this.SerializePublicKey(asymmetricCipherKeyPair.getPublic());
        array[0] = this.ExtractAndExpand(calculateRawAgreement, Arrays.concatenate(serializePublicKey, this.SerializePublicKey(asymmetricKeyParameter)));
        array[1] = serializePublicKey;
        return array;
    }
    
    protected byte[] Decap(final byte[] array, final AsymmetricCipherKeyPair asymmetricCipherKeyPair) {
        return this.ExtractAndExpand(calculateRawAgreement(this.rawAgreement, asymmetricCipherKeyPair.getPrivate(), this.DeserializePublicKey(array)), Arrays.concatenate(array, this.SerializePublicKey(asymmetricCipherKeyPair.getPublic())));
    }
    
    protected byte[][] AuthEncap(final AsymmetricKeyParameter asymmetricKeyParameter, final AsymmetricCipherKeyPair asymmetricCipherKeyPair) {
        final byte[][] array = new byte[2][];
        final AsymmetricCipherKeyPair generateKeyPair = this.kpGen.generateKeyPair();
        this.rawAgreement.init(generateKeyPair.getPrivate());
        final int agreementSize = this.rawAgreement.getAgreementSize();
        final byte[] array2 = new byte[agreementSize * 2];
        this.rawAgreement.calculateAgreement(asymmetricKeyParameter, array2, 0);
        this.rawAgreement.init(asymmetricCipherKeyPair.getPrivate());
        if (agreementSize != this.rawAgreement.getAgreementSize()) {
            throw new IllegalStateException();
        }
        this.rawAgreement.calculateAgreement(asymmetricKeyParameter, array2, agreementSize);
        final byte[] serializePublicKey = this.SerializePublicKey(generateKeyPair.getPublic());
        array[0] = this.ExtractAndExpand(array2, Arrays.concatenate(serializePublicKey, this.SerializePublicKey(asymmetricKeyParameter), this.SerializePublicKey(asymmetricCipherKeyPair.getPublic())));
        array[1] = serializePublicKey;
        return array;
    }
    
    protected byte[] AuthDecap(final byte[] array, final AsymmetricCipherKeyPair asymmetricCipherKeyPair, final AsymmetricKeyParameter asymmetricKeyParameter) {
        final AsymmetricKeyParameter deserializePublicKey = this.DeserializePublicKey(array);
        this.rawAgreement.init(asymmetricCipherKeyPair.getPrivate());
        final int agreementSize = this.rawAgreement.getAgreementSize();
        final byte[] array2 = new byte[agreementSize * 2];
        this.rawAgreement.calculateAgreement(deserializePublicKey, array2, 0);
        this.rawAgreement.calculateAgreement(asymmetricKeyParameter, array2, agreementSize);
        return this.ExtractAndExpand(array2, Arrays.concatenate(array, this.SerializePublicKey(asymmetricCipherKeyPair.getPublic()), this.SerializePublicKey(asymmetricKeyParameter)));
    }
    
    private byte[] ExtractAndExpand(final byte[] array, final byte[] array2) {
        final byte[] concatenate = Arrays.concatenate(Strings.toByteArray("KEM"), Pack.shortToBigEndian(this.kemId));
        return this.hkdf.LabeledExpand(this.hkdf.LabeledExtract(null, concatenate, "eae_prk", array), concatenate, "shared_secret", array2, this.Nsecret);
    }
    
    private static byte[] calculateRawAgreement(final RawAgreement rawAgreement, final AsymmetricKeyParameter asymmetricKeyParameter, final AsymmetricKeyParameter asymmetricKeyParameter2) {
        rawAgreement.init(asymmetricKeyParameter);
        final byte[] array = new byte[rawAgreement.getAgreementSize()];
        rawAgreement.calculateAgreement(asymmetricKeyParameter2, array, 0);
        return array;
    }
    
    private static ECDomainParameters getDomainParameters(final String s) {
        return new ECDomainParameters(CustomNamedCurves.getByName(s));
    }
    
    private static SecureRandom getSecureRandom() {
        return CryptoServicesRegistrar.getSecureRandom();
    }
}
