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

package org.bouncycastle.crypto.digests;

import org.bouncycastle.util.Integers;
import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.util.Pack;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.ExtendedDigest;

public class Blake2sDigest implements ExtendedDigest
{
    private static final int[] blake2s_IV;
    private static final byte[][] blake2s_sigma;
    private static final int ROUNDS = 10;
    private static final int BLOCK_LENGTH_BYTES = 64;
    private int digestLength;
    private int keyLength;
    private byte[] salt;
    private byte[] personalization;
    private byte[] key;
    private int fanout;
    private int depth;
    private int leafLength;
    private long nodeOffset;
    private int nodeDepth;
    private int innerHashLength;
    private boolean isLastNode;
    private byte[] buffer;
    private int bufferPos;
    private int[] internalState;
    private int[] chainValue;
    private int t0;
    private int t1;
    private int f0;
    private int f1;
    private final CryptoServicePurpose purpose;
    
    public Blake2sDigest() {
        this(256, CryptoServicePurpose.ANY);
    }
    
    public Blake2sDigest(final int n) {
        this(n, CryptoServicePurpose.ANY);
    }
    
    public Blake2sDigest(final Blake2sDigest blake2sDigest) {
        this.digestLength = 32;
        this.keyLength = 0;
        this.salt = null;
        this.personalization = null;
        this.key = null;
        this.fanout = 1;
        this.depth = 1;
        this.leafLength = 0;
        this.nodeOffset = 0L;
        this.nodeDepth = 0;
        this.innerHashLength = 0;
        this.isLastNode = false;
        this.buffer = null;
        this.bufferPos = 0;
        this.internalState = new int[16];
        this.chainValue = null;
        this.t0 = 0;
        this.t1 = 0;
        this.f0 = 0;
        this.f1 = 0;
        this.bufferPos = blake2sDigest.bufferPos;
        this.buffer = Arrays.clone(blake2sDigest.buffer);
        this.keyLength = blake2sDigest.keyLength;
        this.key = Arrays.clone(blake2sDigest.key);
        this.digestLength = blake2sDigest.digestLength;
        this.internalState = Arrays.clone(blake2sDigest.internalState);
        this.chainValue = Arrays.clone(blake2sDigest.chainValue);
        this.t0 = blake2sDigest.t0;
        this.t1 = blake2sDigest.t1;
        this.f0 = blake2sDigest.f0;
        this.salt = Arrays.clone(blake2sDigest.salt);
        this.personalization = Arrays.clone(blake2sDigest.personalization);
        this.fanout = blake2sDigest.fanout;
        this.depth = blake2sDigest.depth;
        this.leafLength = blake2sDigest.leafLength;
        this.nodeOffset = blake2sDigest.nodeOffset;
        this.nodeDepth = blake2sDigest.nodeDepth;
        this.innerHashLength = blake2sDigest.innerHashLength;
        this.purpose = blake2sDigest.purpose;
    }
    
    public Blake2sDigest(final int n, final CryptoServicePurpose purpose) {
        this.digestLength = 32;
        this.keyLength = 0;
        this.salt = null;
        this.personalization = null;
        this.key = null;
        this.fanout = 1;
        this.depth = 1;
        this.leafLength = 0;
        this.nodeOffset = 0L;
        this.nodeDepth = 0;
        this.innerHashLength = 0;
        this.isLastNode = false;
        this.buffer = null;
        this.bufferPos = 0;
        this.internalState = new int[16];
        this.chainValue = null;
        this.t0 = 0;
        this.t1 = 0;
        this.f0 = 0;
        this.f1 = 0;
        if (n < 8 || n > 256 || n % 8 != 0) {
            throw new IllegalArgumentException("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256");
        }
        this.digestLength = n / 8;
        this.purpose = purpose;
        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, n, purpose));
        this.init(null, null, null);
    }
    
    public Blake2sDigest(final byte[] array) {
        this(array, CryptoServicePurpose.ANY);
    }
    
    public Blake2sDigest(final byte[] array, final CryptoServicePurpose purpose) {
        this.digestLength = 32;
        this.keyLength = 0;
        this.salt = null;
        this.personalization = null;
        this.key = null;
        this.fanout = 1;
        this.depth = 1;
        this.leafLength = 0;
        this.nodeOffset = 0L;
        this.nodeDepth = 0;
        this.innerHashLength = 0;
        this.isLastNode = false;
        this.buffer = null;
        this.bufferPos = 0;
        this.internalState = new int[16];
        this.chainValue = null;
        this.t0 = 0;
        this.t1 = 0;
        this.f0 = 0;
        this.f1 = 0;
        this.purpose = purpose;
        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, array.length * 8, purpose));
        this.init(null, null, array);
    }
    
    public Blake2sDigest(final byte[] array, final int n, final byte[] array2, final byte[] array3) {
        this(array, n, array2, array3, CryptoServicePurpose.ANY);
    }
    
    public Blake2sDigest(final byte[] array, final int digestLength, final byte[] array2, final byte[] array3, final CryptoServicePurpose purpose) {
        this.digestLength = 32;
        this.keyLength = 0;
        this.salt = null;
        this.personalization = null;
        this.key = null;
        this.fanout = 1;
        this.depth = 1;
        this.leafLength = 0;
        this.nodeOffset = 0L;
        this.nodeDepth = 0;
        this.innerHashLength = 0;
        this.isLastNode = false;
        this.buffer = null;
        this.bufferPos = 0;
        this.internalState = new int[16];
        this.chainValue = null;
        this.t0 = 0;
        this.t1 = 0;
        this.f0 = 0;
        this.f1 = 0;
        if (digestLength < 1 || digestLength > 32) {
            throw new IllegalArgumentException("Invalid digest length (required: 1 - 32)");
        }
        this.digestLength = digestLength;
        this.purpose = purpose;
        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, digestLength * 8, purpose));
        this.init(array2, array3, array);
    }
    
    Blake2sDigest(final int digestLength, final byte[] array, final byte[] array2, final byte[] array3, final long nodeOffset, final CryptoServicePurpose purpose) {
        this.digestLength = 32;
        this.keyLength = 0;
        this.salt = null;
        this.personalization = null;
        this.key = null;
        this.fanout = 1;
        this.depth = 1;
        this.leafLength = 0;
        this.nodeOffset = 0L;
        this.nodeDepth = 0;
        this.innerHashLength = 0;
        this.isLastNode = false;
        this.buffer = null;
        this.bufferPos = 0;
        this.internalState = new int[16];
        this.chainValue = null;
        this.t0 = 0;
        this.t1 = 0;
        this.f0 = 0;
        this.f1 = 0;
        this.digestLength = digestLength;
        this.nodeOffset = nodeOffset;
        this.purpose = purpose;
        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, digestLength * 8, purpose));
        this.init(array2, array3, array);
    }
    
    Blake2sDigest(final int n, final int n2, final long n3) {
        this(n, n2, n3, CryptoServicePurpose.ANY);
    }
    
    Blake2sDigest(final int digestLength, final int n, final long nodeOffset, final CryptoServicePurpose purpose) {
        this.digestLength = 32;
        this.keyLength = 0;
        this.salt = null;
        this.personalization = null;
        this.key = null;
        this.fanout = 1;
        this.depth = 1;
        this.leafLength = 0;
        this.nodeOffset = 0L;
        this.nodeDepth = 0;
        this.innerHashLength = 0;
        this.isLastNode = false;
        this.buffer = null;
        this.bufferPos = 0;
        this.internalState = new int[16];
        this.chainValue = null;
        this.t0 = 0;
        this.t1 = 0;
        this.f0 = 0;
        this.f1 = 0;
        this.digestLength = digestLength;
        this.nodeOffset = nodeOffset;
        this.fanout = 0;
        this.depth = 0;
        this.leafLength = n;
        this.innerHashLength = n;
        this.nodeDepth = 0;
        this.purpose = purpose;
        CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties(this, digestLength * 8, purpose));
        this.init(null, null, null);
    }
    
    Blake2sDigest(final byte[] array, final byte[] array2) {
        this.digestLength = 32;
        this.keyLength = 0;
        this.salt = null;
        this.personalization = null;
        this.key = null;
        this.fanout = 1;
        this.depth = 1;
        this.leafLength = 0;
        this.nodeOffset = 0L;
        this.nodeDepth = 0;
        this.innerHashLength = 0;
        this.isLastNode = false;
        this.buffer = null;
        this.bufferPos = 0;
        this.internalState = new int[16];
        this.chainValue = null;
        this.t0 = 0;
        this.t1 = 0;
        this.f0 = 0;
        this.f1 = 0;
        this.purpose = CryptoServicePurpose.ANY;
        this.digestLength = array2[0];
        this.keyLength = array2[1];
        this.fanout = array2[2];
        this.depth = array2[3];
        this.leafLength = Pack.littleEndianToInt(array2, 4);
        this.nodeOffset |= Pack.littleEndianToInt(array2, 8);
        this.nodeDepth = array2[14];
        this.innerHashLength = array2[15];
        final byte[] array3 = new byte[8];
        final byte[] array4 = new byte[8];
        System.arraycopy(array2, 16, array3, 0, 8);
        System.arraycopy(array2, 24, array4, 0, 8);
        this.init(array3, array4, array);
    }
    
    private void init(final byte[] array, final byte[] array2, final byte[] array3) {
        this.buffer = new byte[64];
        if (array3 != null && array3.length > 0) {
            this.keyLength = array3.length;
            if (this.keyLength > 32) {
                throw new IllegalArgumentException("Keys > 32 bytes are not supported");
            }
            System.arraycopy(array3, 0, this.key = new byte[this.keyLength], 0, this.keyLength);
            System.arraycopy(array3, 0, this.buffer, 0, this.keyLength);
            this.bufferPos = 64;
        }
        if (this.chainValue == null) {
            (this.chainValue = new int[8])[0] = (Blake2sDigest.blake2s_IV[0] ^ (this.digestLength | this.keyLength << 8 | (this.fanout << 16 | this.depth << 24)));
            this.chainValue[1] = (Blake2sDigest.blake2s_IV[1] ^ this.leafLength);
            final int n = (int)(this.nodeOffset >> 32);
            this.chainValue[2] = (Blake2sDigest.blake2s_IV[2] ^ (int)this.nodeOffset);
            this.chainValue[3] = (Blake2sDigest.blake2s_IV[3] ^ (n | this.nodeDepth << 16 | this.innerHashLength << 24));
            this.chainValue[4] = Blake2sDigest.blake2s_IV[4];
            this.chainValue[5] = Blake2sDigest.blake2s_IV[5];
            if (array != null) {
                if (array.length != 8) {
                    throw new IllegalArgumentException("Salt length must be exactly 8 bytes");
                }
                System.arraycopy(array, 0, this.salt = new byte[8], 0, array.length);
                final int[] chainValue = this.chainValue;
                final int n2 = 4;
                chainValue[n2] ^= Pack.littleEndianToInt(array, 0);
                final int[] chainValue2 = this.chainValue;
                final int n3 = 5;
                chainValue2[n3] ^= Pack.littleEndianToInt(array, 4);
            }
            this.chainValue[6] = Blake2sDigest.blake2s_IV[6];
            this.chainValue[7] = Blake2sDigest.blake2s_IV[7];
            if (array2 != null) {
                if (array2.length != 8) {
                    throw new IllegalArgumentException("Personalization length must be exactly 8 bytes");
                }
                System.arraycopy(array2, 0, this.personalization = new byte[8], 0, array2.length);
                final int[] chainValue3 = this.chainValue;
                final int n4 = 6;
                chainValue3[n4] ^= Pack.littleEndianToInt(array2, 0);
                final int[] chainValue4 = this.chainValue;
                final int n5 = 7;
                chainValue4[n5] ^= Pack.littleEndianToInt(array2, 4);
            }
        }
    }
    
    private void initializeInternalState() {
        System.arraycopy(this.chainValue, 0, this.internalState, 0, this.chainValue.length);
        System.arraycopy(Blake2sDigest.blake2s_IV, 0, this.internalState, this.chainValue.length, 4);
        this.internalState[12] = (this.t0 ^ Blake2sDigest.blake2s_IV[4]);
        this.internalState[13] = (this.t1 ^ Blake2sDigest.blake2s_IV[5]);
        this.internalState[14] = (this.f0 ^ Blake2sDigest.blake2s_IV[6]);
        this.internalState[15] = (this.f1 ^ Blake2sDigest.blake2s_IV[7]);
    }
    
    @Override
    public void update(final byte b) {
        if (64 - this.bufferPos == 0) {
            this.t0 += 64;
            if (this.t0 == 0) {
                ++this.t1;
            }
            this.compress(this.buffer, 0);
            Arrays.fill(this.buffer, (byte)0);
            this.buffer[0] = b;
            this.bufferPos = 1;
        }
        else {
            this.buffer[this.bufferPos] = b;
            ++this.bufferPos;
        }
    }
    
    @Override
    public void update(final byte[] array, final int n, final int n2) {
        if (array == null || n2 == 0) {
            return;
        }
        int n3 = 0;
        if (this.bufferPos != 0) {
            n3 = 64 - this.bufferPos;
            if (n3 >= n2) {
                System.arraycopy(array, n, this.buffer, this.bufferPos, n2);
                this.bufferPos += n2;
                return;
            }
            System.arraycopy(array, n, this.buffer, this.bufferPos, n3);
            this.t0 += 64;
            if (this.t0 == 0) {
                ++this.t1;
            }
            this.compress(this.buffer, 0);
            this.bufferPos = 0;
            Arrays.fill(this.buffer, (byte)0);
        }
        int n4;
        int i;
        for (n4 = n + n2 - 64, i = n + n3; i < n4; i += 64) {
            this.t0 += 64;
            if (this.t0 == 0) {
                ++this.t1;
            }
            this.compress(array, i);
        }
        System.arraycopy(array, i, this.buffer, 0, n + n2 - i);
        this.bufferPos += n + n2 - i;
    }
    
    @Override
    public int doFinal(final byte[] array, final int n) {
        if (n > array.length - this.digestLength) {
            throw new OutputLengthException("output buffer too short");
        }
        this.f0 = -1;
        if (this.isLastNode) {
            this.f1 = -1;
        }
        this.t0 += this.bufferPos;
        if (this.t0 < 0 && this.bufferPos > -this.t0) {
            ++this.t1;
        }
        this.compress(this.buffer, 0);
        Arrays.fill(this.buffer, (byte)0);
        Arrays.fill(this.internalState, 0);
        final int n2 = this.digestLength >>> 2;
        final int n3 = this.digestLength & 0x3;
        Pack.intToLittleEndian(this.chainValue, 0, n2, array, n);
        if (n3 > 0) {
            final byte[] array2 = new byte[4];
            Pack.intToLittleEndian(this.chainValue[n2], array2, 0);
            System.arraycopy(array2, 0, array, n + this.digestLength - n3, n3);
        }
        Arrays.fill(this.chainValue, 0);
        this.reset();
        return this.digestLength;
    }
    
    @Override
    public void reset() {
        this.bufferPos = 0;
        this.f0 = 0;
        this.f1 = 0;
        this.t0 = 0;
        this.t1 = 0;
        this.isLastNode = false;
        this.chainValue = null;
        Arrays.fill(this.buffer, (byte)0);
        if (this.key != null) {
            System.arraycopy(this.key, 0, this.buffer, 0, this.key.length);
            this.bufferPos = 64;
        }
        this.init(this.salt, this.personalization, this.key);
    }
    
    private void compress(final byte[] array, final int n) {
        this.initializeInternalState();
        final int[] array2 = new int[16];
        Pack.littleEndianToInt(array, n, array2);
        for (int i = 0; i < 10; ++i) {
            this.G(array2[Blake2sDigest.blake2s_sigma[i][0]], array2[Blake2sDigest.blake2s_sigma[i][1]], 0, 4, 8, 12);
            this.G(array2[Blake2sDigest.blake2s_sigma[i][2]], array2[Blake2sDigest.blake2s_sigma[i][3]], 1, 5, 9, 13);
            this.G(array2[Blake2sDigest.blake2s_sigma[i][4]], array2[Blake2sDigest.blake2s_sigma[i][5]], 2, 6, 10, 14);
            this.G(array2[Blake2sDigest.blake2s_sigma[i][6]], array2[Blake2sDigest.blake2s_sigma[i][7]], 3, 7, 11, 15);
            this.G(array2[Blake2sDigest.blake2s_sigma[i][8]], array2[Blake2sDigest.blake2s_sigma[i][9]], 0, 5, 10, 15);
            this.G(array2[Blake2sDigest.blake2s_sigma[i][10]], array2[Blake2sDigest.blake2s_sigma[i][11]], 1, 6, 11, 12);
            this.G(array2[Blake2sDigest.blake2s_sigma[i][12]], array2[Blake2sDigest.blake2s_sigma[i][13]], 2, 7, 8, 13);
            this.G(array2[Blake2sDigest.blake2s_sigma[i][14]], array2[Blake2sDigest.blake2s_sigma[i][15]], 3, 4, 9, 14);
        }
        for (int j = 0; j < this.chainValue.length; ++j) {
            this.chainValue[j] = (this.chainValue[j] ^ this.internalState[j] ^ this.internalState[j + 8]);
        }
    }
    
    private void G(final int n, final int n2, final int n3, final int n4, final int n5, final int n6) {
        this.internalState[n3] = this.internalState[n3] + this.internalState[n4] + n;
        this.internalState[n6] = Integers.rotateRight(this.internalState[n6] ^ this.internalState[n3], 16);
        this.internalState[n5] += this.internalState[n6];
        this.internalState[n4] = Integers.rotateRight(this.internalState[n4] ^ this.internalState[n5], 12);
        this.internalState[n3] = this.internalState[n3] + this.internalState[n4] + n2;
        this.internalState[n6] = Integers.rotateRight(this.internalState[n6] ^ this.internalState[n3], 8);
        this.internalState[n5] += this.internalState[n6];
        this.internalState[n4] = Integers.rotateRight(this.internalState[n4] ^ this.internalState[n5], 7);
    }
    
    protected void setAsLastNode() {
        this.isLastNode = true;
    }
    
    @Override
    public String getAlgorithmName() {
        return "BLAKE2s";
    }
    
    @Override
    public int getDigestSize() {
        return this.digestLength;
    }
    
    @Override
    public int getByteLength() {
        return 64;
    }
    
    public void clearKey() {
        if (this.key != null) {
            Arrays.fill(this.key, (byte)0);
            Arrays.fill(this.buffer, (byte)0);
        }
    }
    
    public void clearSalt() {
        if (this.salt != null) {
            Arrays.fill(this.salt, (byte)0);
        }
    }
    
    static {
        blake2s_IV = new int[] { 1779033703, -1150833019, 1013904242, -1521486534, 1359893119, -1694144372, 528734635, 1541459225 };
        blake2s_sigma = new byte[][] { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 } };
    }
}
