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

package org.bouncycastle.crypto.encodings;

import org.bouncycastle.util.Bytes;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.util.DigestFactory;
import org.bouncycastle.crypto.ExtendedDigest;
import org.bouncycastle.util.Memoable;
import java.security.SecureRandom;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.AsymmetricBlockCipher;

public class OAEPEncoding implements AsymmetricBlockCipher
{
    private final AsymmetricBlockCipher engine;
    private final Digest mgf1Hash;
    private final int mgf1NoMemoLimit;
    private final byte[] defHash;
    private SecureRandom random;
    private boolean forEncryption;
    
    private static int getMGF1NoMemoLimit(final Digest digest) {
        if (digest instanceof Memoable && digest instanceof ExtendedDigest) {
            return ((ExtendedDigest)digest).getByteLength() - 1;
        }
        return Integer.MAX_VALUE;
    }
    
    public OAEPEncoding(final AsymmetricBlockCipher asymmetricBlockCipher) {
        this(asymmetricBlockCipher, DigestFactory.createSHA1(), null);
    }
    
    public OAEPEncoding(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest) {
        this(asymmetricBlockCipher, digest, null);
    }
    
    public OAEPEncoding(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final byte[] array) {
        this(asymmetricBlockCipher, digest, digest, array);
    }
    
    public OAEPEncoding(final AsymmetricBlockCipher engine, final Digest digest, final Digest mgf1Hash, final byte[] array) {
        this.engine = engine;
        this.mgf1Hash = mgf1Hash;
        this.mgf1NoMemoLimit = getMGF1NoMemoLimit(mgf1Hash);
        this.defHash = new byte[digest.getDigestSize()];
        digest.reset();
        if (array != null) {
            digest.update(array, 0, array.length);
        }
        digest.doFinal(this.defHash, 0);
    }
    
    public AsymmetricBlockCipher getUnderlyingCipher() {
        return this.engine;
    }
    
    @Override
    public void init(final boolean forEncryption, final CipherParameters cipherParameters) {
        SecureRandom random = null;
        if (cipherParameters instanceof ParametersWithRandom) {
            random = ((ParametersWithRandom)cipherParameters).getRandom();
        }
        this.random = (forEncryption ? CryptoServicesRegistrar.getSecureRandom(random) : null);
        this.forEncryption = forEncryption;
        this.engine.init(forEncryption, cipherParameters);
    }
    
    @Override
    public int getInputBlockSize() {
        final int inputBlockSize = this.engine.getInputBlockSize();
        if (this.forEncryption) {
            return inputBlockSize - 1 - 2 * this.defHash.length;
        }
        return inputBlockSize;
    }
    
    @Override
    public int getOutputBlockSize() {
        final int outputBlockSize = this.engine.getOutputBlockSize();
        if (this.forEncryption) {
            return outputBlockSize;
        }
        return outputBlockSize - 1 - 2 * this.defHash.length;
    }
    
    @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);
    }
    
    public byte[] encodeBlock(final byte[] array, final int n, final int n2) throws InvalidCipherTextException {
        final int inputBlockSize = this.getInputBlockSize();
        if (n2 > inputBlockSize) {
            throw new DataLengthException("input data too long");
        }
        final byte[] array2 = new byte[inputBlockSize + 1 + 2 * this.defHash.length];
        System.arraycopy(array, n, array2, array2.length - n2, n2);
        array2[array2.length - n2 - 1] = 1;
        System.arraycopy(this.defHash, 0, array2, this.defHash.length, this.defHash.length);
        final byte[] bytes = new byte[this.defHash.length];
        this.random.nextBytes(bytes);
        System.arraycopy(bytes, 0, array2, 0, this.defHash.length);
        this.mgf1Hash.reset();
        this.maskGeneratorFunction1(bytes, 0, bytes.length, array2, this.defHash.length, array2.length - this.defHash.length);
        this.maskGeneratorFunction1(array2, this.defHash.length, array2.length - this.defHash.length, array2, 0, this.defHash.length);
        return this.engine.processBlock(array2, 0, array2.length);
    }
    
    public byte[] decodeBlock(final byte[] array, final int n, final int n2) throws InvalidCipherTextException {
        final int n3 = this.getOutputBlockSize() >> 31;
        final byte[] array2 = new byte[this.engine.getOutputBlockSize()];
        final byte[] processBlock = this.engine.processBlock(array, n, n2);
        int n4 = n3 | array2.length - processBlock.length >> 31;
        final int min = Math.min(array2.length, processBlock.length);
        System.arraycopy(processBlock, 0, array2, array2.length - min, min);
        Arrays.fill(processBlock, (byte)0);
        this.mgf1Hash.reset();
        this.maskGeneratorFunction1(array2, this.defHash.length, array2.length - this.defHash.length, array2, 0, this.defHash.length);
        this.maskGeneratorFunction1(array2, 0, this.defHash.length, array2, this.defHash.length, array2.length - this.defHash.length);
        for (int i = 0; i != this.defHash.length; ++i) {
            n4 |= (this.defHash[i] ^ array2[this.defHash.length + i]);
        }
        int n5 = -1;
        for (int j = 2 * this.defHash.length; j != array2.length; ++j) {
            n5 += (j & (-(array2[j] & 0xFF) & n5) >> 31);
        }
        final int n6 = n4 | n5 >> 31;
        ++n5;
        if ((n6 | (array2[n5] ^ 0x1)) != 0x0) {
            Arrays.fill(array2, (byte)0);
            throw new InvalidCipherTextException("data wrong");
        }
        ++n5;
        final byte[] array3 = new byte[array2.length - n5];
        System.arraycopy(array2, n5, array3, 0, array3.length);
        Arrays.fill(array2, (byte)0);
        return array3;
    }
    
    private void maskGeneratorFunction1(final byte[] array, final int n, final int n2, final byte[] array2, final int n3, final int n4) {
        final int digestSize = this.mgf1Hash.getDigestSize();
        final byte[] array3 = new byte[digestSize];
        final byte[] array4 = new byte[4];
        int n5 = 0;
        final int n6 = n3 + n4;
        final int n7 = n6 - digestSize;
        int i = n3;
        this.mgf1Hash.update(array, n, n2);
        if (n2 > this.mgf1NoMemoLimit) {
            final Memoable memoable = (Memoable)this.mgf1Hash;
            final Memoable copy = memoable.copy();
            while (i < n7) {
                Pack.intToBigEndian(n5++, array4, 0);
                this.mgf1Hash.update(array4, 0, array4.length);
                this.mgf1Hash.doFinal(array3, 0);
                memoable.reset(copy);
                Bytes.xorTo(digestSize, array3, 0, array2, i);
                i += digestSize;
            }
        }
        else {
            while (i < n7) {
                Pack.intToBigEndian(n5++, array4, 0);
                this.mgf1Hash.update(array4, 0, array4.length);
                this.mgf1Hash.doFinal(array3, 0);
                this.mgf1Hash.update(array, n, n2);
                Bytes.xorTo(digestSize, array3, 0, array2, i);
                i += digestSize;
            }
        }
        Pack.intToBigEndian(n5, array4, 0);
        this.mgf1Hash.update(array4, 0, array4.length);
        this.mgf1Hash.doFinal(array3, 0);
        Bytes.xorTo(n6 - i, array3, 0, array2, i);
    }
}
