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

package org.bouncycastle.crypto.encodings;

import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.util.Properties;
import java.security.SecureRandom;
import org.bouncycastle.crypto.AsymmetricBlockCipher;

public class PKCS1Encoding implements AsymmetricBlockCipher
{
    @Deprecated
    public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.strict";
    public static final String NOT_STRICT_LENGTH_ENABLED_PROPERTY = "org.bouncycastle.pkcs1.not_strict";
    private static final int HEADER_LENGTH = 10;
    private SecureRandom random;
    private AsymmetricBlockCipher engine;
    private boolean forEncryption;
    private boolean forPrivateKey;
    private boolean useStrictLength;
    private int pLen;
    private byte[] fallback;
    private byte[] blockBuffer;
    
    public PKCS1Encoding(final AsymmetricBlockCipher engine) {
        this.pLen = -1;
        this.fallback = null;
        this.engine = engine;
        this.useStrictLength = this.useStrict();
    }
    
    public PKCS1Encoding(final AsymmetricBlockCipher engine, final int pLen) {
        this.pLen = -1;
        this.fallback = null;
        this.engine = engine;
        this.useStrictLength = this.useStrict();
        this.pLen = pLen;
    }
    
    public PKCS1Encoding(final AsymmetricBlockCipher engine, final byte[] fallback) {
        this.pLen = -1;
        this.fallback = null;
        this.engine = engine;
        this.useStrictLength = this.useStrict();
        this.fallback = fallback;
        this.pLen = fallback.length;
    }
    
    private boolean useStrict() {
        return !Properties.isOverrideSetTo("org.bouncycastle.pkcs1.not_strict", true) && !Properties.isOverrideSetTo("org.bouncycastle.pkcs1.strict", false);
    }
    
    public AsymmetricBlockCipher getUnderlyingCipher() {
        return this.engine;
    }
    
    @Override
    public void init(final boolean forEncryption, final CipherParameters cipherParameters) {
        AsymmetricKeyParameter asymmetricKeyParameter;
        if (cipherParameters instanceof ParametersWithRandom) {
            final ParametersWithRandom parametersWithRandom = (ParametersWithRandom)cipherParameters;
            this.random = parametersWithRandom.getRandom();
            asymmetricKeyParameter = (AsymmetricKeyParameter)parametersWithRandom.getParameters();
        }
        else {
            asymmetricKeyParameter = (AsymmetricKeyParameter)cipherParameters;
            if (!asymmetricKeyParameter.isPrivate() && forEncryption) {
                this.random = CryptoServicesRegistrar.getSecureRandom();
            }
        }
        this.engine.init(forEncryption, cipherParameters);
        this.forPrivateKey = asymmetricKeyParameter.isPrivate();
        this.forEncryption = forEncryption;
        this.blockBuffer = new byte[this.engine.getOutputBlockSize()];
        if (this.pLen > 0 && this.fallback == null && this.random == null) {
            throw new IllegalArgumentException("encoder requires random");
        }
    }
    
    @Override
    public int getInputBlockSize() {
        final int inputBlockSize = this.engine.getInputBlockSize();
        if (this.forEncryption) {
            return inputBlockSize - 10;
        }
        return inputBlockSize;
    }
    
    @Override
    public int getOutputBlockSize() {
        final int outputBlockSize = this.engine.getOutputBlockSize();
        if (this.forEncryption) {
            return outputBlockSize;
        }
        return outputBlockSize - 10;
    }
    
    @Override
    public byte[] processBlock(final byte[] array, final int n, final int n2) throws InvalidCipherTextException {
        if (this.forEncryption) {
            return this.encodeBlock(array, n, n2);
        }
        return this.decodeBlock(array, n, n2);
    }
    
    private byte[] encodeBlock(final byte[] array, final int n, final int n2) throws InvalidCipherTextException {
        if (n2 > this.getInputBlockSize()) {
            throw new IllegalArgumentException("input data too large");
        }
        final byte[] bytes = new byte[this.engine.getInputBlockSize()];
        if (this.forPrivateKey) {
            bytes[0] = 1;
            for (int i = 1; i != bytes.length - n2 - 1; ++i) {
                bytes[i] = -1;
            }
        }
        else {
            this.random.nextBytes(bytes);
            bytes[0] = 2;
            for (int j = 1; j != bytes.length - n2 - 1; ++j) {
                while (bytes[j] == 0) {
                    bytes[j] = (byte)this.random.nextInt();
                }
            }
        }
        bytes[bytes.length - n2 - 1] = 0;
        System.arraycopy(array, n, bytes, bytes.length - n2, n2);
        return this.engine.processBlock(bytes, 0, bytes.length);
    }
    
    private static int checkPkcs1Encoding1(final byte[] array) {
        int n = 0;
        int n2 = 0;
        int n3 = -((array[0] & 0xFF) ^ 0x1);
        for (int i = 1; i < array.length; ++i) {
            final int n4 = array[i] & 0xFF;
            final int n5 = (n4 ^ 0x0) - 1 >> 31;
            final int n6 = (n4 ^ 0xFF) - 1 >> 31;
            n2 ^= (i & ~n & n5);
            n |= n5;
            n3 |= ~(n | n6);
        }
        return array.length - 1 - n2 | (n3 | n2 - 9) >> 31;
    }
    
    private static int checkPkcs1Encoding2(final byte[] array) {
        int n = 0;
        int n2 = 0;
        final int n3 = -((array[0] & 0xFF) ^ 0x2);
        for (int i = 1; i < array.length; ++i) {
            final int n4 = ((array[i] & 0xFF) ^ 0x0) - 1 >> 31;
            n2 ^= (i & ~n & n4);
            n |= n4;
        }
        return array.length - 1 - n2 | (n3 | n2 - 9) >> 31;
    }
    
    private static int checkPkcs1Encoding2(final byte[] array, final int n) {
        final int n2 = -((array[0] & 0xFF) ^ 0x2);
        final int n3 = array.length - 1 - n;
        int n4 = n2 | n3 - 9;
        for (int i = 1; i < n3; ++i) {
            n4 |= (array[i] & 0xFF) - 1;
        }
        return (n4 | -(array[n3] & 0xFF)) >> 31;
    }
    
    private byte[] decodeBlockOrRandom(final byte[] array, final int n, final int n2) throws InvalidCipherTextException {
        if (!this.forPrivateKey) {
            throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing");
        }
        final int pLen = this.pLen;
        byte[] fallback = this.fallback;
        if (this.fallback == null) {
            fallback = new byte[pLen];
            this.random.nextBytes(fallback);
        }
        final int n3 = 0;
        final int outputBlockSize = this.engine.getOutputBlockSize();
        byte[] array3;
        final byte[] array2 = array3 = this.engine.processBlock(array, n, n2);
        if (array2.length != outputBlockSize && (this.useStrictLength || array2.length < outputBlockSize)) {
            array3 = this.blockBuffer;
        }
        final int n4 = n3 | checkPkcs1Encoding2(array3, pLen);
        final int n5 = array3.length - pLen;
        final byte[] array4 = new byte[pLen];
        for (int i = 0; i < pLen; ++i) {
            array4[i] = (byte)((array3[n5 + i] & ~n4) | (fallback[i] & n4));
        }
        Arrays.fill(array2, (byte)0);
        Arrays.fill(this.blockBuffer, 0, Math.max(0, this.blockBuffer.length - array2.length), (byte)0);
        return array4;
    }
    
    private byte[] decodeBlock(final byte[] array, final int n, final int n2) throws InvalidCipherTextException {
        if (this.forPrivateKey && this.pLen != -1) {
            return this.decodeBlockOrRandom(array, n, n2);
        }
        final int outputBlockSize = this.engine.getOutputBlockSize();
        final byte[] processBlock = this.engine.processBlock(array, n, n2);
        final boolean b = this.useStrictLength & processBlock.length != outputBlockSize;
        byte[] blockBuffer = processBlock;
        if (processBlock.length < outputBlockSize) {
            blockBuffer = this.blockBuffer;
        }
        final int n3 = this.forPrivateKey ? checkPkcs1Encoding2(blockBuffer) : checkPkcs1Encoding1(blockBuffer);
        try {
            if (n3 < 0) {
                throw new InvalidCipherTextException("block incorrect");
            }
            if (b) {
                throw new InvalidCipherTextException("block incorrect size");
            }
            final byte[] array2 = new byte[n3];
            System.arraycopy(blockBuffer, blockBuffer.length - n3, array2, 0, n3);
            return array2;
        }
        finally {
            Arrays.fill(processBlock, (byte)0);
            Arrays.fill(this.blockBuffer, 0, Math.max(0, this.blockBuffer.length - processBlock.length), (byte)0);
        }
    }
}
