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

package org.bouncycastle.crypto.signers;

import org.bouncycastle.crypto.Xof;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSABlindingParameters;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.digests.Prehash;
import java.security.SecureRandom;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;

public class PSSSigner implements Signer
{
    public static final byte TRAILER_IMPLICIT = -68;
    private Digest contentDigest1;
    private Digest contentDigest2;
    private Digest mgfDigest;
    private AsymmetricBlockCipher cipher;
    private SecureRandom random;
    private int hLen;
    private int mgfhLen;
    private boolean sSet;
    private int sLen;
    private int emBits;
    private byte[] salt;
    private byte[] mDash;
    private byte[] block;
    private byte trailer;
    
    public static PSSSigner createRawSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest) {
        return new PSSSigner(asymmetricBlockCipher, Prehash.forDigest(digest), digest, digest, digest.getDigestSize(), (byte)(-68));
    }
    
    public static PSSSigner createRawSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final Digest digest2, final int n, final byte b) {
        return new PSSSigner(asymmetricBlockCipher, Prehash.forDigest(digest), digest, digest2, n, b);
    }
    
    public static PSSSigner createRawSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final Digest digest2, final byte[] array, final byte b) {
        return new PSSSigner(asymmetricBlockCipher, Prehash.forDigest(digest), digest, digest2, array, b);
    }
    
    public PSSSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final int n) {
        this(asymmetricBlockCipher, digest, n, (byte)(-68));
    }
    
    public PSSSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final Digest digest2, final int n) {
        this(asymmetricBlockCipher, digest, digest2, n, (byte)(-68));
    }
    
    public PSSSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final int n, final byte b) {
        this(asymmetricBlockCipher, digest, digest, n, b);
    }
    
    public PSSSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final Digest digest2, final int n, final byte b) {
        this(asymmetricBlockCipher, digest, digest, digest2, n, b);
    }
    
    private PSSSigner(final AsymmetricBlockCipher cipher, final Digest contentDigest1, final Digest contentDigest2, final Digest mgfDigest, final int sLen, final byte trailer) {
        this.cipher = cipher;
        this.contentDigest1 = contentDigest1;
        this.contentDigest2 = contentDigest2;
        this.mgfDigest = mgfDigest;
        this.hLen = contentDigest2.getDigestSize();
        this.mgfhLen = mgfDigest.getDigestSize();
        this.sSet = false;
        this.sLen = sLen;
        this.salt = new byte[sLen];
        this.mDash = new byte[8 + sLen + this.hLen];
        this.trailer = trailer;
    }
    
    public PSSSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final byte[] array) {
        this(asymmetricBlockCipher, digest, digest, array, (byte)(-68));
    }
    
    public PSSSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final Digest digest2, final byte[] array) {
        this(asymmetricBlockCipher, digest, digest2, array, (byte)(-68));
    }
    
    public PSSSigner(final AsymmetricBlockCipher asymmetricBlockCipher, final Digest digest, final Digest digest2, final byte[] array, final byte b) {
        this(asymmetricBlockCipher, digest, digest, digest2, array, b);
    }
    
    private PSSSigner(final AsymmetricBlockCipher cipher, final Digest contentDigest1, final Digest contentDigest2, final Digest mgfDigest, final byte[] salt, final byte trailer) {
        this.cipher = cipher;
        this.contentDigest1 = contentDigest1;
        this.contentDigest2 = contentDigest2;
        this.mgfDigest = mgfDigest;
        this.hLen = contentDigest2.getDigestSize();
        this.mgfhLen = mgfDigest.getDigestSize();
        this.sSet = true;
        this.sLen = salt.length;
        this.salt = salt;
        this.mDash = new byte[8 + this.sLen + this.hLen];
        this.trailer = trailer;
    }
    
    @Override
    public void init(final boolean b, final CipherParameters cipherParameters) {
        CipherParameters parameters;
        if (cipherParameters instanceof ParametersWithRandom) {
            final ParametersWithRandom parametersWithRandom = (ParametersWithRandom)cipherParameters;
            parameters = parametersWithRandom.getParameters();
            this.random = parametersWithRandom.getRandom();
        }
        else {
            parameters = cipherParameters;
            if (b) {
                this.random = CryptoServicesRegistrar.getSecureRandom();
            }
        }
        RSAKeyParameters publicKey;
        if (parameters instanceof RSABlindingParameters) {
            publicKey = ((RSABlindingParameters)parameters).getPublicKey();
            this.cipher.init(b, cipherParameters);
        }
        else {
            publicKey = (RSAKeyParameters)parameters;
            this.cipher.init(b, parameters);
        }
        this.emBits = publicKey.getModulus().bitLength() - 1;
        if (this.emBits < 8 * this.hLen + 8 * this.sLen + 9) {
            throw new IllegalArgumentException("key too small for specified hash and salt lengths");
        }
        this.block = new byte[(this.emBits + 7) / 8];
        this.reset();
    }
    
    private void clearBlock(final byte[] array) {
        for (int i = 0; i != array.length; ++i) {
            array[i] = 0;
        }
    }
    
    @Override
    public void update(final byte b) {
        this.contentDigest1.update(b);
    }
    
    @Override
    public void update(final byte[] array, final int n, final int n2) {
        this.contentDigest1.update(array, n, n2);
    }
    
    @Override
    public void reset() {
        this.contentDigest1.reset();
    }
    
    @Override
    public byte[] generateSignature() throws CryptoException, DataLengthException {
        if (this.contentDigest1.getDigestSize() != this.hLen) {
            throw new IllegalStateException();
        }
        this.contentDigest1.doFinal(this.mDash, this.mDash.length - this.hLen - this.sLen);
        if (this.sLen != 0) {
            if (!this.sSet) {
                this.random.nextBytes(this.salt);
            }
            System.arraycopy(this.salt, 0, this.mDash, this.mDash.length - this.sLen, this.sLen);
        }
        final byte[] array = new byte[this.hLen];
        this.contentDigest2.update(this.mDash, 0, this.mDash.length);
        this.contentDigest2.doFinal(array, 0);
        this.block[this.block.length - this.sLen - 1 - this.hLen - 1] = 1;
        System.arraycopy(this.salt, 0, this.block, this.block.length - this.sLen - this.hLen - 1, this.sLen);
        final byte[] maskGenerator = this.maskGenerator(array, 0, array.length, this.block.length - this.hLen - 1);
        for (int i = 0; i != maskGenerator.length; ++i) {
            final byte[] block = this.block;
            final int n = i;
            block[n] ^= maskGenerator[i];
        }
        System.arraycopy(array, 0, this.block, this.block.length - this.hLen - 1, this.hLen);
        final int n2 = 255 >>> this.block.length * 8 - this.emBits;
        final byte[] block2 = this.block;
        final int n3 = 0;
        block2[n3] &= (byte)n2;
        this.block[this.block.length - 1] = this.trailer;
        final byte[] processBlock = this.cipher.processBlock(this.block, 0, this.block.length);
        this.clearBlock(this.block);
        return processBlock;
    }
    
    @Override
    public boolean verifySignature(final byte[] array) {
        if (this.contentDigest1.getDigestSize() != this.hLen) {
            throw new IllegalStateException();
        }
        this.contentDigest1.doFinal(this.mDash, this.mDash.length - this.hLen - this.sLen);
        try {
            final byte[] processBlock = this.cipher.processBlock(array, 0, array.length);
            Arrays.fill(this.block, 0, this.block.length - processBlock.length, (byte)0);
            System.arraycopy(processBlock, 0, this.block, this.block.length - processBlock.length, processBlock.length);
        }
        catch (final Exception ex) {
            return false;
        }
        final int n = 255 >>> this.block.length * 8 - this.emBits;
        if ((this.block[0] & 0xFF) != (this.block[0] & n) || this.block[this.block.length - 1] != this.trailer) {
            this.clearBlock(this.block);
            return false;
        }
        final byte[] maskGenerator = this.maskGenerator(this.block, this.block.length - this.hLen - 1, this.hLen, this.block.length - this.hLen - 1);
        for (int i = 0; i != maskGenerator.length; ++i) {
            final byte[] block = this.block;
            final int n2 = i;
            block[n2] ^= maskGenerator[i];
        }
        final byte[] block2 = this.block;
        final int n3 = 0;
        block2[n3] &= (byte)n;
        for (int j = 0; j != this.block.length - this.hLen - this.sLen - 2; ++j) {
            if (this.block[j] != 0) {
                this.clearBlock(this.block);
                return false;
            }
        }
        if (this.block[this.block.length - this.hLen - this.sLen - 2] != 1) {
            this.clearBlock(this.block);
            return false;
        }
        if (this.sSet) {
            System.arraycopy(this.salt, 0, this.mDash, this.mDash.length - this.sLen, this.sLen);
        }
        else {
            System.arraycopy(this.block, this.block.length - this.sLen - this.hLen - 1, this.mDash, this.mDash.length - this.sLen, this.sLen);
        }
        this.contentDigest2.update(this.mDash, 0, this.mDash.length);
        this.contentDigest2.doFinal(this.mDash, this.mDash.length - this.hLen);
        int n4 = this.block.length - this.hLen - 1;
        for (int k = this.mDash.length - this.hLen; k != this.mDash.length; ++k) {
            if ((this.block[n4] ^ this.mDash[k]) != 0x0) {
                this.clearBlock(this.mDash);
                this.clearBlock(this.block);
                return false;
            }
            ++n4;
        }
        this.clearBlock(this.mDash);
        this.clearBlock(this.block);
        return true;
    }
    
    private void ItoOSP(final int n, final byte[] array) {
        array[0] = (byte)(n >>> 24);
        array[1] = (byte)(n >>> 16);
        array[2] = (byte)(n >>> 8);
        array[3] = (byte)(n >>> 0);
    }
    
    private byte[] maskGenerator(final byte[] array, final int n, final int n2, final int n3) {
        if (this.mgfDigest instanceof Xof) {
            final byte[] array2 = new byte[n3];
            this.mgfDigest.update(array, n, n2);
            ((Xof)this.mgfDigest).doFinal(array2, 0, array2.length);
            return array2;
        }
        return this.maskGeneratorFunction1(array, n, n2, n3);
    }
    
    private byte[] maskGeneratorFunction1(final byte[] array, final int n, final int n2, final int n3) {
        final byte[] array2 = new byte[n3];
        final byte[] array3 = new byte[this.mgfhLen];
        final byte[] array4 = new byte[4];
        int i = 0;
        this.mgfDigest.reset();
        while (i < n3 / this.mgfhLen) {
            this.ItoOSP(i, array4);
            this.mgfDigest.update(array, n, n2);
            this.mgfDigest.update(array4, 0, array4.length);
            this.mgfDigest.doFinal(array3, 0);
            System.arraycopy(array3, 0, array2, i * this.mgfhLen, this.mgfhLen);
            ++i;
        }
        if (i * this.mgfhLen < n3) {
            this.ItoOSP(i, array4);
            this.mgfDigest.update(array, n, n2);
            this.mgfDigest.update(array4, 0, array4.length);
            this.mgfDigest.doFinal(array3, 0);
            System.arraycopy(array3, 0, array2, i * this.mgfhLen, array2.length - i * this.mgfhLen);
        }
        return array2;
    }
}
