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

package org.bouncycastle.cms.jcajce;

import java.security.GeneralSecurityException;
import java.io.IOException;
import org.bouncycastle.crypto.DerivationParameters;
import org.bouncycastle.crypto.params.HKDFParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
import org.bouncycastle.crypto.digests.SHA256Digest;
import java.security.Key;
import org.bouncycastle.operator.jcajce.JceGenericKey;
import org.bouncycastle.operator.GenericKey;
import org.bouncycastle.jcajce.io.CipherOutputStream;
import org.bouncycastle.asn1.cms.GCMParameters;
import org.bouncycastle.cms.CMSAlgorithm;
import java.io.OutputStream;
import org.bouncycastle.operator.MacCaptureStream;
import org.bouncycastle.operator.OutputAEADEncryptor;
import org.bouncycastle.util.Strings;
import org.bouncycastle.operator.DefaultSecretKeySizeProvider;
import java.security.AccessController;
import javax.crypto.Cipher;
import java.security.PrivilegedAction;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DERNull;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.cms.CMSException;
import javax.crypto.KeyGenerator;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.operator.OutputEncryptor;
import java.security.Provider;
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import java.security.AlgorithmParameters;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import java.security.SecureRandom;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.operator.SecretKeySizeProvider;

public class JceCMSContentEncryptorBuilder
{
    private static final SecretKeySizeProvider KEY_SIZE_PROVIDER;
    private static final byte[] hkdfSalt;
    private final ASN1ObjectIdentifier encryptionOID;
    private final int keySize;
    private EnvelopedDataHelper helper;
    private SecureRandom random;
    private AlgorithmIdentifier algorithmIdentifier;
    private AlgorithmParameters algorithmParameters;
    private ASN1ObjectIdentifier kdfAlgorithm;
    
    public JceCMSContentEncryptorBuilder(final ASN1ObjectIdentifier asn1ObjectIdentifier) {
        this(asn1ObjectIdentifier, JceCMSContentEncryptorBuilder.KEY_SIZE_PROVIDER.getKeySize(asn1ObjectIdentifier));
    }
    
    public JceCMSContentEncryptorBuilder(final ASN1ObjectIdentifier encryptionOID, final int keySize) {
        this.helper = new EnvelopedDataHelper(new DefaultJcaJceExtHelper());
        this.encryptionOID = encryptionOID;
        final int keySize2 = JceCMSContentEncryptorBuilder.KEY_SIZE_PROVIDER.getKeySize(encryptionOID);
        if (encryptionOID.equals(PKCSObjectIdentifiers.des_EDE3_CBC)) {
            if (keySize != 168 && keySize != keySize2) {
                throw new IllegalArgumentException("incorrect keySize for encryptionOID passed to builder.");
            }
            this.keySize = 168;
        }
        else if (encryptionOID.equals(OIWObjectIdentifiers.desCBC)) {
            if (keySize != 56 && keySize != keySize2) {
                throw new IllegalArgumentException("incorrect keySize for encryptionOID passed to builder.");
            }
            this.keySize = 56;
        }
        else {
            if (keySize2 > 0 && keySize2 != keySize) {
                throw new IllegalArgumentException("incorrect keySize for encryptionOID passed to builder.");
            }
            this.keySize = keySize;
        }
    }
    
    public JceCMSContentEncryptorBuilder(final AlgorithmIdentifier algorithmIdentifier) {
        this(algorithmIdentifier.getAlgorithm(), JceCMSContentEncryptorBuilder.KEY_SIZE_PROVIDER.getKeySize(algorithmIdentifier.getAlgorithm()));
        this.algorithmIdentifier = algorithmIdentifier;
    }
    
    public JceCMSContentEncryptorBuilder setEnableSha256HKdf(final boolean b) {
        if (b) {
            this.kdfAlgorithm = CMSObjectIdentifiers.id_alg_cek_hkdf_sha256;
        }
        else if (this.kdfAlgorithm != null) {
            if (!this.kdfAlgorithm.equals(CMSObjectIdentifiers.id_alg_cek_hkdf_sha256)) {
                throw new IllegalStateException("SHA256 HKDF not enabled");
            }
            this.kdfAlgorithm = null;
        }
        return this;
    }
    
    public JceCMSContentEncryptorBuilder setProvider(final Provider provider) {
        this.helper = new EnvelopedDataHelper(new ProviderJcaJceExtHelper(provider));
        return this;
    }
    
    public JceCMSContentEncryptorBuilder setProvider(final String s) {
        this.helper = new EnvelopedDataHelper(new NamedJcaJceExtHelper(s));
        return this;
    }
    
    public JceCMSContentEncryptorBuilder setSecureRandom(final SecureRandom random) {
        this.random = random;
        return this;
    }
    
    public JceCMSContentEncryptorBuilder setAlgorithmParameters(final AlgorithmParameters algorithmParameters) {
        this.algorithmParameters = algorithmParameters;
        return this;
    }
    
    public OutputEncryptor build() throws CMSException {
        final KeyGenerator keyGenerator = this.helper.createKeyGenerator(this.encryptionOID);
        this.random = CryptoServicesRegistrar.getSecureRandom(this.random);
        if (this.keySize < 0) {
            keyGenerator.init(this.random);
        }
        else {
            keyGenerator.init(this.keySize, this.random);
        }
        return this.build(keyGenerator.generateKey());
    }
    
    public OutputEncryptor build(final byte[] key) throws CMSException {
        return this.build(new SecretKeySpec(key, this.helper.getBaseCipherName(this.encryptionOID)));
    }
    
    public OutputEncryptor build(final SecretKey secretKey) throws CMSException {
        if (this.algorithmParameters != null) {
            if (this.helper.isAuthEnveloped(this.encryptionOID)) {
                return new CMSAuthOutputEncryptor(this.kdfAlgorithm, this.encryptionOID, secretKey, this.algorithmParameters, this.random);
            }
            return new CMSOutputEncryptor(this.kdfAlgorithm, this.encryptionOID, secretKey, this.algorithmParameters, this.random);
        }
        else {
            if (this.algorithmIdentifier != null) {
                final ASN1Encodable parameters = this.algorithmIdentifier.getParameters();
                if (parameters != null && !parameters.equals(DERNull.INSTANCE)) {
                    try {
                        (this.algorithmParameters = this.helper.createAlgorithmParameters(this.algorithmIdentifier.getAlgorithm())).init(parameters.toASN1Primitive().getEncoded());
                    }
                    catch (final Exception ex) {
                        throw new CMSException("unable to process provided algorithmIdentifier: " + ex.toString(), ex);
                    }
                }
            }
            if (this.helper.isAuthEnveloped(this.encryptionOID)) {
                return new CMSAuthOutputEncryptor(this.kdfAlgorithm, this.encryptionOID, secretKey, this.algorithmParameters, this.random);
            }
            return new CMSOutputEncryptor(this.kdfAlgorithm, this.encryptionOID, secretKey, this.algorithmParameters, this.random);
        }
    }
    
    private static boolean checkForAEAD() {
        return AccessController.doPrivileged((PrivilegedAction<Boolean>)new PrivilegedAction() {
            @Override
            public Object run() {
                try {
                    return Cipher.class.getMethod("updateAAD", byte[].class) != null;
                }
                catch (final Exception ex) {
                    return Boolean.FALSE;
                }
            }
        });
    }
    
    static {
        KEY_SIZE_PROVIDER = DefaultSecretKeySizeProvider.INSTANCE;
        hkdfSalt = Strings.toByteArray("The Cryptographic Message Syntax");
    }
    
    private class CMSAuthOutputEncryptor extends CMSOutEncryptor implements OutputAEADEncryptor
    {
        private MacCaptureStream macOut;
        
        CMSAuthOutputEncryptor(final ASN1ObjectIdentifier asn1ObjectIdentifier, final ASN1ObjectIdentifier asn1ObjectIdentifier2, final SecretKey secretKey, final AlgorithmParameters algorithmParameters, final SecureRandom secureRandom) throws CMSException {
            this.init(asn1ObjectIdentifier, asn1ObjectIdentifier2, secretKey, algorithmParameters, secureRandom);
        }
        
        @Override
        public AlgorithmIdentifier getAlgorithmIdentifier() {
            return this.algorithmIdentifier;
        }
        
        @Override
        public OutputStream getOutputStream(final OutputStream outputStream) {
            AlgorithmIdentifier algorithmIdentifier;
            if (JceCMSContentEncryptorBuilder.this.kdfAlgorithm != null) {
                algorithmIdentifier = AlgorithmIdentifier.getInstance(this.algorithmIdentifier.getParameters());
            }
            else {
                algorithmIdentifier = this.algorithmIdentifier;
            }
            if (CMSAlgorithm.ChaCha20Poly1305.equals(this.algorithmIdentifier.getAlgorithm())) {
                this.macOut = new MacCaptureStream(outputStream, 16);
            }
            else {
                this.macOut = new MacCaptureStream(outputStream, GCMParameters.getInstance(algorithmIdentifier.getParameters()).getIcvLen());
            }
            return new CipherOutputStream(this.macOut, this.cipher);
        }
        
        @Override
        public GenericKey getKey() {
            return new JceGenericKey(this.algorithmIdentifier, this.encKey);
        }
        
        @Override
        public OutputStream getAADStream() {
            if (checkForAEAD()) {
                return new JceAADStream(this.cipher);
            }
            return null;
        }
        
        @Override
        public byte[] getMAC() {
            return this.macOut.getMac();
        }
    }
    
    private class CMSOutEncryptor
    {
        protected SecretKey encKey;
        protected AlgorithmIdentifier algorithmIdentifier;
        protected Cipher cipher;
        
        private void applyKdf(final ASN1ObjectIdentifier asn1ObjectIdentifier, final AlgorithmParameters params, final SecureRandom random) throws CMSException {
            final HKDFBytesGenerator hkdfBytesGenerator = new HKDFBytesGenerator(new SHA256Digest());
            final byte[] encoded = this.encKey.getEncoded();
            try {
                hkdfBytesGenerator.init(new HKDFParameters(encoded, JceCMSContentEncryptorBuilder.hkdfSalt, this.algorithmIdentifier.getEncoded("DER")));
            }
            catch (final IOException ex) {
                throw new CMSException("unable to encode enc algorithm parameters", ex);
            }
            hkdfBytesGenerator.generateBytes(encoded, 0, encoded.length);
            final SecretKeySpec key = new SecretKeySpec(encoded, this.encKey.getAlgorithm());
            try {
                this.cipher.init(1, key, params, random);
            }
            catch (final GeneralSecurityException ex2) {
                throw new CMSException("unable to initialize cipher: " + ex2.getMessage(), ex2);
            }
            this.algorithmIdentifier = new AlgorithmIdentifier(asn1ObjectIdentifier, this.algorithmIdentifier);
        }
        
        protected void init(final ASN1ObjectIdentifier asn1ObjectIdentifier, final ASN1ObjectIdentifier asn1ObjectIdentifier2, final SecretKey key, AlgorithmParameters algorithmParameters, SecureRandom secureRandom) throws CMSException {
            this.encKey = key;
            secureRandom = CryptoServicesRegistrar.getSecureRandom(secureRandom);
            this.cipher = JceCMSContentEncryptorBuilder.this.helper.createCipher(asn1ObjectIdentifier2);
            if (algorithmParameters == null) {
                algorithmParameters = JceCMSContentEncryptorBuilder.this.helper.generateParameters(asn1ObjectIdentifier2, key, secureRandom);
            }
            if (algorithmParameters != null) {
                this.algorithmIdentifier = JceCMSContentEncryptorBuilder.this.helper.getAlgorithmIdentifier(asn1ObjectIdentifier2, algorithmParameters);
                if (asn1ObjectIdentifier != null) {
                    this.applyKdf(asn1ObjectIdentifier, algorithmParameters, secureRandom);
                    return;
                }
                try {
                    this.cipher.init(1, key, algorithmParameters, secureRandom);
                    return;
                }
                catch (final GeneralSecurityException ex) {
                    throw new CMSException("unable to initialize cipher: " + ex.getMessage(), ex);
                }
            }
            try {
                this.cipher.init(1, key, algorithmParameters, secureRandom);
            }
            catch (final GeneralSecurityException ex2) {
                throw new CMSException("unable to initialize cipher: " + ex2.getMessage(), ex2);
            }
            algorithmParameters = this.cipher.getParameters();
            this.algorithmIdentifier = JceCMSContentEncryptorBuilder.this.helper.getAlgorithmIdentifier(asn1ObjectIdentifier2, algorithmParameters);
            if (asn1ObjectIdentifier != null) {
                this.applyKdf(asn1ObjectIdentifier, algorithmParameters, secureRandom);
            }
        }
    }
    
    private class CMSOutputEncryptor extends CMSOutEncryptor implements OutputEncryptor
    {
        CMSOutputEncryptor(final ASN1ObjectIdentifier asn1ObjectIdentifier, final ASN1ObjectIdentifier asn1ObjectIdentifier2, final SecretKey secretKey, final AlgorithmParameters algorithmParameters, final SecureRandom secureRandom) throws CMSException {
            this.init(asn1ObjectIdentifier, asn1ObjectIdentifier2, secretKey, algorithmParameters, secureRandom);
        }
        
        @Override
        public AlgorithmIdentifier getAlgorithmIdentifier() {
            return this.algorithmIdentifier;
        }
        
        @Override
        public OutputStream getOutputStream(final OutputStream outputStream) {
            return new CipherOutputStream(outputStream, this.cipher);
        }
        
        @Override
        public GenericKey getKey() {
            return new JceGenericKey(this.algorithmIdentifier, this.encKey);
        }
    }
}
