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

package org.bouncycastle.pqc.jcajce.provider.ntru;

import org.bouncycastle.crypto.InvalidCipherTextException;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.pqc.crypto.ntru.NTRUKEMExtractor;
import org.bouncycastle.crypto.Wrapper;
import org.bouncycastle.crypto.SecretWithEncapsulation;
import javax.security.auth.DestroyFailedException;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.pqc.jcajce.provider.util.WrapUtil;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.ShortBufferException;
import java.security.InvalidParameterException;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.jcajce.spec.KEMParameterSpec;
import java.security.InvalidKeyException;
import java.security.InvalidAlgorithmParameterException;
import java.security.SecureRandom;
import org.bouncycastle.util.Exceptions;
import java.security.spec.AlgorithmParameterSpec;
import java.security.Key;
import javax.crypto.NoSuchPaddingException;
import java.security.NoSuchAlgorithmException;
import java.security.AlgorithmParameters;
import org.bouncycastle.jcajce.spec.KTSParameterSpec;
import org.bouncycastle.pqc.crypto.ntru.NTRUKEMGenerator;
import javax.crypto.CipherSpi;

class NTRUCipherSpi extends CipherSpi
{
    private final String algorithmName;
    private NTRUKEMGenerator kemGen;
    private KTSParameterSpec kemParameterSpec;
    private BCNTRUPublicKey wrapKey;
    private BCNTRUPrivateKey unwrapKey;
    private AlgorithmParameters engineParams;
    
    NTRUCipherSpi(final String algorithmName) throws NoSuchAlgorithmException {
        this.algorithmName = algorithmName;
    }
    
    @Override
    protected void engineSetMode(final String str) throws NoSuchAlgorithmException {
        throw new NoSuchAlgorithmException("Cannot support mode " + str);
    }
    
    @Override
    protected void engineSetPadding(final String str) throws NoSuchPaddingException {
        throw new NoSuchPaddingException("Padding " + str + " unknown");
    }
    
    @Override
    protected int engineGetKeySize(final Key key) {
        return 2048;
    }
    
    @Override
    protected int engineGetBlockSize() {
        return 0;
    }
    
    @Override
    protected int engineGetOutputSize(final int n) {
        return -1;
    }
    
    @Override
    protected byte[] engineGetIV() {
        return null;
    }
    
    @Override
    protected AlgorithmParameters engineGetParameters() {
        if (this.engineParams == null) {
            try {
                (this.engineParams = AlgorithmParameters.getInstance(this.algorithmName, "BCPQC")).init(this.kemParameterSpec);
            }
            catch (final Exception ex) {
                throw Exceptions.illegalStateException(ex.toString(), ex);
            }
        }
        return this.engineParams;
    }
    
    @Override
    protected void engineInit(final int n, final Key key, final SecureRandom secureRandom) throws InvalidKeyException {
        try {
            this.engineInit(n, key, (AlgorithmParameterSpec)null, secureRandom);
        }
        catch (final InvalidAlgorithmParameterException ex) {
            throw Exceptions.illegalArgumentException(ex.getMessage(), ex);
        }
    }
    
    @Override
    protected void engineInit(final int n, final Key key, final AlgorithmParameterSpec algorithmParameterSpec, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        if (algorithmParameterSpec == null) {
            this.kemParameterSpec = new KEMParameterSpec("AES-KWP");
        }
        else {
            if (!(algorithmParameterSpec instanceof KTSParameterSpec)) {
                throw new InvalidAlgorithmParameterException(this.algorithmName + " can only accept KTSParameterSpec");
            }
            this.kemParameterSpec = (KTSParameterSpec)algorithmParameterSpec;
        }
        if (n == 3) {
            if (!(key instanceof BCNTRUPublicKey)) {
                throw new InvalidKeyException("Only a " + this.algorithmName + " public key can be used for wrapping");
            }
            this.wrapKey = (BCNTRUPublicKey)key;
            this.kemGen = new NTRUKEMGenerator(CryptoServicesRegistrar.getSecureRandom(secureRandom));
        }
        else {
            if (n != 4) {
                throw new InvalidParameterException("Cipher only valid for wrapping/unwrapping");
            }
            if (!(key instanceof BCNTRUPrivateKey)) {
                throw new InvalidKeyException("Only a " + this.algorithmName + " private key can be used for unwrapping");
            }
            this.unwrapKey = (BCNTRUPrivateKey)key;
        }
    }
    
    @Override
    protected void engineInit(final int n, final Key key, final AlgorithmParameters algorithmParameters, final SecureRandom secureRandom) throws InvalidKeyException, InvalidAlgorithmParameterException {
        AlgorithmParameterSpec parameterSpec = null;
        if (algorithmParameters != null) {
            try {
                parameterSpec = algorithmParameters.getParameterSpec(KEMParameterSpec.class);
            }
            catch (final Exception ex) {
                throw new InvalidAlgorithmParameterException("can't handle parameter " + algorithmParameters.toString());
            }
        }
        this.engineInit(n, key, parameterSpec, secureRandom);
    }
    
    @Override
    protected byte[] engineUpdate(final byte[] array, final int n, final int n2) {
        throw new IllegalStateException("Not supported in a wrapping mode");
    }
    
    @Override
    protected int engineUpdate(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) throws ShortBufferException {
        throw new IllegalStateException("Not supported in a wrapping mode");
    }
    
    @Override
    protected byte[] engineDoFinal(final byte[] array, final int n, final int n2) throws IllegalBlockSizeException, BadPaddingException {
        throw new IllegalStateException("Not supported in a wrapping mode");
    }
    
    @Override
    protected int engineDoFinal(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
        throw new IllegalStateException("Not supported in a wrapping mode");
    }
    
    @Override
    protected byte[] engineWrap(final Key key) throws IllegalBlockSizeException, InvalidKeyException {
        if (key.getEncoded() == null) {
            throw new InvalidKeyException("Cannot wrap key, null encoding.");
        }
        SecretWithEncapsulation generateEncapsulated = null;
        try {
            generateEncapsulated = this.kemGen.generateEncapsulated(this.wrapKey.getKeyParams());
            final Wrapper keyWrapper = WrapUtil.getKeyWrapper(this.kemParameterSpec, generateEncapsulated.getSecret());
            final byte[] encapsulation = generateEncapsulated.getEncapsulation();
            final byte[] encoded = key.getEncoded();
            final byte[] concatenate = Arrays.concatenate(encapsulation, keyWrapper.wrap(encoded, 0, encoded.length));
            Arrays.clear(encoded);
            return concatenate;
        }
        catch (final IllegalArgumentException ex) {
            throw new IllegalBlockSizeException("unable to generate KTS secret: " + ex.getMessage());
        }
        finally {
            try {
                if (generateEncapsulated != null) {
                    generateEncapsulated.destroy();
                }
            }
            catch (final DestroyFailedException ex2) {
                throw new IllegalBlockSizeException("unable to destroy interim values: " + ex2.getMessage());
            }
        }
    }
    
    @Override
    protected Key engineUnwrap(final byte[] array, final String algorithm, final int n) throws InvalidKeyException, NoSuchAlgorithmException {
        if (n != 3) {
            throw new InvalidKeyException("only SECRET_KEY supported");
        }
        byte[] secret = null;
        try {
            final NTRUKEMExtractor ntrukemExtractor = new NTRUKEMExtractor(this.unwrapKey.getKeyParams());
            secret = ntrukemExtractor.extractSecret(Arrays.copyOfRange(array, 0, ntrukemExtractor.getEncapsulationLength()));
            final Wrapper keyUnwrapper = WrapUtil.getKeyUnwrapper(this.kemParameterSpec, secret);
            final byte[] copyOfRange = Arrays.copyOfRange(array, ntrukemExtractor.getEncapsulationLength(), array.length);
            return new SecretKeySpec(keyUnwrapper.unwrap(copyOfRange, 0, copyOfRange.length), algorithm);
        }
        catch (final IllegalArgumentException ex) {
            throw new NoSuchAlgorithmException("unable to extract KTS secret: " + ex.getMessage());
        }
        catch (final InvalidCipherTextException ex2) {
            throw new InvalidKeyException("unable to extract KTS secret: " + ex2.getMessage());
        }
        finally {
            if (secret != null) {
                Arrays.clear(secret);
            }
        }
    }
    
    public static class Base extends NTRUCipherSpi
    {
        public Base() throws NoSuchAlgorithmException {
            super("NTRU");
        }
    }
}
