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

package org.bouncycastle.crypto.engines;

import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.CryptoServiceProperties;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
import org.bouncycastle.crypto.CryptoServicePurpose;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.util.Memoable;
import org.bouncycastle.crypto.StreamCipher;

public class Zuc128CoreEngine implements StreamCipher, Memoable
{
    private static final byte[] S0;
    private static final byte[] S1;
    private static final short[] EK_d;
    private final int[] LFSR;
    private final int[] F;
    private final int[] BRC;
    private int theIndex;
    private final byte[] keyStream;
    private int theIterations;
    private Zuc128CoreEngine theResetState;
    
    protected Zuc128CoreEngine() {
        this.LFSR = new int[16];
        this.F = new int[2];
        this.BRC = new int[4];
        this.keyStream = new byte[4];
    }
    
    protected Zuc128CoreEngine(final Zuc128CoreEngine zuc128CoreEngine) {
        this.LFSR = new int[16];
        this.F = new int[2];
        this.BRC = new int[4];
        this.keyStream = new byte[4];
        this.reset(zuc128CoreEngine);
    }
    
    @Override
    public void init(final boolean b, final CipherParameters cipherParameters) {
        CipherParameters parameters = cipherParameters;
        byte[] key = null;
        byte[] iv = null;
        if (parameters instanceof ParametersWithIV) {
            final ParametersWithIV parametersWithIV = (ParametersWithIV)parameters;
            iv = parametersWithIV.getIV();
            parameters = parametersWithIV.getParameters();
        }
        if (parameters instanceof KeyParameter) {
            key = ((KeyParameter)parameters).getKey();
        }
        this.theIndex = 0;
        this.theIterations = 0;
        this.setKeyAndIV(key, iv);
        CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(this.getAlgorithmName(), key.length * 8, cipherParameters, b ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION));
        this.theResetState = (Zuc128CoreEngine)this.copy();
    }
    
    protected int getMaxIterations() {
        return 2047;
    }
    
    @Override
    public String getAlgorithmName() {
        return "Zuc-128";
    }
    
    @Override
    public int processBytes(final byte[] array, final int n, final int n2, final byte[] array2, final int n3) {
        if (this.theResetState == null) {
            throw new IllegalStateException(this.getAlgorithmName() + " not initialised");
        }
        if (n + n2 > array.length) {
            throw new DataLengthException("input buffer too short");
        }
        if (n3 + n2 > array2.length) {
            throw new OutputLengthException("output buffer too short");
        }
        for (int i = 0; i < n2; ++i) {
            array2[i + n3] = this.returnByte(array[i + n]);
        }
        return n2;
    }
    
    @Override
    public void reset() {
        if (this.theResetState != null) {
            this.reset(this.theResetState);
        }
    }
    
    @Override
    public byte returnByte(final byte b) {
        if (this.theIndex == 0) {
            this.makeKeyStream();
        }
        final byte b2 = (byte)(this.keyStream[this.theIndex] ^ b);
        this.theIndex = (this.theIndex + 1) % 4;
        return b2;
    }
    
    public static void encode32be(final int n, final byte[] array, final int n2) {
        array[n2] = (byte)(n >> 24);
        array[n2 + 1] = (byte)(n >> 16);
        array[n2 + 2] = (byte)(n >> 8);
        array[n2 + 3] = (byte)n;
    }
    
    private int AddM(final int n, final int n2) {
        final int n3 = n + n2;
        return (n3 & Integer.MAX_VALUE) + (n3 >>> 31);
    }
    
    private static int MulByPow2(final int n, final int n2) {
        return (n << n2 | n >>> 31 - n2) & Integer.MAX_VALUE;
    }
    
    private void LFSRWithInitialisationMode(final int n) {
        final int addM = this.AddM(this.AddM(this.AddM(this.AddM(this.AddM(this.AddM(this.LFSR[0], MulByPow2(this.LFSR[0], 8)), MulByPow2(this.LFSR[4], 20)), MulByPow2(this.LFSR[10], 21)), MulByPow2(this.LFSR[13], 17)), MulByPow2(this.LFSR[15], 15)), n);
        this.LFSR[0] = this.LFSR[1];
        this.LFSR[1] = this.LFSR[2];
        this.LFSR[2] = this.LFSR[3];
        this.LFSR[3] = this.LFSR[4];
        this.LFSR[4] = this.LFSR[5];
        this.LFSR[5] = this.LFSR[6];
        this.LFSR[6] = this.LFSR[7];
        this.LFSR[7] = this.LFSR[8];
        this.LFSR[8] = this.LFSR[9];
        this.LFSR[9] = this.LFSR[10];
        this.LFSR[10] = this.LFSR[11];
        this.LFSR[11] = this.LFSR[12];
        this.LFSR[12] = this.LFSR[13];
        this.LFSR[13] = this.LFSR[14];
        this.LFSR[14] = this.LFSR[15];
        this.LFSR[15] = addM;
    }
    
    private void LFSRWithWorkMode() {
        final int addM = this.AddM(this.AddM(this.AddM(this.AddM(this.AddM(this.LFSR[0], MulByPow2(this.LFSR[0], 8)), MulByPow2(this.LFSR[4], 20)), MulByPow2(this.LFSR[10], 21)), MulByPow2(this.LFSR[13], 17)), MulByPow2(this.LFSR[15], 15));
        this.LFSR[0] = this.LFSR[1];
        this.LFSR[1] = this.LFSR[2];
        this.LFSR[2] = this.LFSR[3];
        this.LFSR[3] = this.LFSR[4];
        this.LFSR[4] = this.LFSR[5];
        this.LFSR[5] = this.LFSR[6];
        this.LFSR[6] = this.LFSR[7];
        this.LFSR[7] = this.LFSR[8];
        this.LFSR[8] = this.LFSR[9];
        this.LFSR[9] = this.LFSR[10];
        this.LFSR[10] = this.LFSR[11];
        this.LFSR[11] = this.LFSR[12];
        this.LFSR[12] = this.LFSR[13];
        this.LFSR[13] = this.LFSR[14];
        this.LFSR[14] = this.LFSR[15];
        this.LFSR[15] = addM;
    }
    
    private void BitReorganization() {
        this.BRC[0] = ((this.LFSR[15] & 0x7FFF8000) << 1 | (this.LFSR[14] & 0xFFFF));
        this.BRC[1] = ((this.LFSR[11] & 0xFFFF) << 16 | this.LFSR[9] >>> 15);
        this.BRC[2] = ((this.LFSR[7] & 0xFFFF) << 16 | this.LFSR[5] >>> 15);
        this.BRC[3] = ((this.LFSR[2] & 0xFFFF) << 16 | this.LFSR[0] >>> 15);
    }
    
    static int ROT(final int n, final int n2) {
        return n << n2 | n >>> 32 - n2;
    }
    
    private static int L1(final int n) {
        return n ^ ROT(n, 2) ^ ROT(n, 10) ^ ROT(n, 18) ^ ROT(n, 24);
    }
    
    private static int L2(final int n) {
        return n ^ ROT(n, 8) ^ ROT(n, 14) ^ ROT(n, 22) ^ ROT(n, 30);
    }
    
    private static int MAKEU32(final byte b, final byte b2, final byte b3, final byte b4) {
        return (b & 0xFF) << 24 | (b2 & 0xFF) << 16 | (b3 & 0xFF) << 8 | (b4 & 0xFF);
    }
    
    int F() {
        final int n = (this.BRC[0] ^ this.F[0]) + this.F[1];
        final int n2 = this.F[0] + this.BRC[1];
        final int n3 = this.F[1] ^ this.BRC[2];
        final int l1 = L1(n2 << 16 | n3 >>> 16);
        final int l2 = L2(n3 << 16 | n2 >>> 16);
        this.F[0] = MAKEU32(Zuc128CoreEngine.S0[l1 >>> 24], Zuc128CoreEngine.S1[l1 >>> 16 & 0xFF], Zuc128CoreEngine.S0[l1 >>> 8 & 0xFF], Zuc128CoreEngine.S1[l1 & 0xFF]);
        this.F[1] = MAKEU32(Zuc128CoreEngine.S0[l2 >>> 24], Zuc128CoreEngine.S1[l2 >>> 16 & 0xFF], Zuc128CoreEngine.S0[l2 >>> 8 & 0xFF], Zuc128CoreEngine.S1[l2 & 0xFF]);
        return n;
    }
    
    private static int MAKEU31(final byte b, final short n, final byte b2) {
        return (b & 0xFF) << 23 | (n & 0xFFFF) << 8 | (b2 & 0xFF);
    }
    
    protected void setKeyAndIV(final int[] array, final byte[] array2, final byte[] array3) {
        if (array2 == null || array2.length != 16) {
            throw new IllegalArgumentException("A key of 16 bytes is needed");
        }
        if (array3 == null || array3.length != 16) {
            throw new IllegalArgumentException("An IV of 16 bytes is needed");
        }
        this.LFSR[0] = MAKEU31(array2[0], Zuc128CoreEngine.EK_d[0], array3[0]);
        this.LFSR[1] = MAKEU31(array2[1], Zuc128CoreEngine.EK_d[1], array3[1]);
        this.LFSR[2] = MAKEU31(array2[2], Zuc128CoreEngine.EK_d[2], array3[2]);
        this.LFSR[3] = MAKEU31(array2[3], Zuc128CoreEngine.EK_d[3], array3[3]);
        this.LFSR[4] = MAKEU31(array2[4], Zuc128CoreEngine.EK_d[4], array3[4]);
        this.LFSR[5] = MAKEU31(array2[5], Zuc128CoreEngine.EK_d[5], array3[5]);
        this.LFSR[6] = MAKEU31(array2[6], Zuc128CoreEngine.EK_d[6], array3[6]);
        this.LFSR[7] = MAKEU31(array2[7], Zuc128CoreEngine.EK_d[7], array3[7]);
        this.LFSR[8] = MAKEU31(array2[8], Zuc128CoreEngine.EK_d[8], array3[8]);
        this.LFSR[9] = MAKEU31(array2[9], Zuc128CoreEngine.EK_d[9], array3[9]);
        this.LFSR[10] = MAKEU31(array2[10], Zuc128CoreEngine.EK_d[10], array3[10]);
        this.LFSR[11] = MAKEU31(array2[11], Zuc128CoreEngine.EK_d[11], array3[11]);
        this.LFSR[12] = MAKEU31(array2[12], Zuc128CoreEngine.EK_d[12], array3[12]);
        this.LFSR[13] = MAKEU31(array2[13], Zuc128CoreEngine.EK_d[13], array3[13]);
        this.LFSR[14] = MAKEU31(array2[14], Zuc128CoreEngine.EK_d[14], array3[14]);
        this.LFSR[15] = MAKEU31(array2[15], Zuc128CoreEngine.EK_d[15], array3[15]);
    }
    
    private void setKeyAndIV(final byte[] array, final byte[] array2) {
        this.setKeyAndIV(this.LFSR, array, array2);
        this.F[0] = 0;
        this.F[1] = 0;
        for (int i = 32; i > 0; --i) {
            this.BitReorganization();
            this.LFSRWithInitialisationMode(this.F() >>> 1);
        }
        this.BitReorganization();
        this.F();
        this.LFSRWithWorkMode();
    }
    
    private void makeKeyStream() {
        encode32be(this.makeKeyStreamWord(), this.keyStream, 0);
    }
    
    protected int makeKeyStreamWord() {
        if (this.theIterations++ >= this.getMaxIterations()) {
            throw new IllegalStateException("Too much data processed by singleKey/IV");
        }
        this.BitReorganization();
        final int n = this.F() ^ this.BRC[3];
        this.LFSRWithWorkMode();
        return n;
    }
    
    @Override
    public Memoable copy() {
        return new Zuc128CoreEngine(this);
    }
    
    @Override
    public void reset(final Memoable memoable) {
        final Zuc128CoreEngine theResetState = (Zuc128CoreEngine)memoable;
        System.arraycopy(theResetState.LFSR, 0, this.LFSR, 0, this.LFSR.length);
        System.arraycopy(theResetState.F, 0, this.F, 0, this.F.length);
        System.arraycopy(theResetState.BRC, 0, this.BRC, 0, this.BRC.length);
        System.arraycopy(theResetState.keyStream, 0, this.keyStream, 0, this.keyStream.length);
        this.theIndex = theResetState.theIndex;
        this.theIterations = theResetState.theIterations;
        this.theResetState = theResetState;
    }
    
    static {
        S0 = new byte[] { 62, 114, 91, 71, -54, -32, 0, 51, 4, -47, 84, -104, 9, -71, 109, -53, 123, 27, -7, 50, -81, -99, 106, -91, -72, 45, -4, 29, 8, 83, 3, -112, 77, 78, -124, -103, -28, -50, -39, -111, -35, -74, -123, 72, -117, 41, 110, -84, -51, -63, -8, 30, 115, 67, 105, -58, -75, -67, -3, 57, 99, 32, -44, 56, 118, 125, -78, -89, -49, -19, 87, -59, -13, 44, -69, 20, 33, 6, 85, -101, -29, -17, 94, 49, 79, 127, 90, -92, 13, -126, 81, 73, 95, -70, 88, 28, 74, 22, -43, 23, -88, -110, 36, 31, -116, -1, -40, -82, 46, 1, -45, -83, 59, 75, -38, 70, -21, -55, -34, -102, -113, -121, -41, 58, -128, 111, 47, -56, -79, -76, 55, -9, 10, 34, 19, 40, 124, -52, 60, -119, -57, -61, -106, 86, 7, -65, 126, -16, 11, 43, -105, 82, 53, 65, 121, 97, -90, 76, 16, -2, -68, 38, -107, -120, -118, -80, -93, -5, -64, 24, -108, -14, -31, -27, -23, 93, -48, -36, 17, 102, 100, 92, -20, 89, 66, 117, 18, -11, 116, -100, -86, 35, 14, -122, -85, -66, 42, 2, -25, 103, -26, 68, -94, 108, -62, -109, -97, -15, -10, -6, 54, -46, 80, 104, -98, 98, 113, 21, 61, -42, 64, -60, -30, 15, -114, -125, 119, 107, 37, 5, 63, 12, 48, -22, 112, -73, -95, -24, -87, 101, -115, 39, 26, -37, -127, -77, -96, -12, 69, 122, 25, -33, -18, 120, 52, 96 };
        S1 = new byte[] { 85, -62, 99, 113, 59, -56, 71, -122, -97, 60, -38, 91, 41, -86, -3, 119, -116, -59, -108, 12, -90, 26, 19, 0, -29, -88, 22, 114, 64, -7, -8, 66, 68, 38, 104, -106, -127, -39, 69, 62, 16, 118, -58, -89, -117, 57, 67, -31, 58, -75, 86, 42, -64, 109, -77, 5, 34, 102, -65, -36, 11, -6, 98, 72, -35, 32, 17, 6, 54, -55, -63, -49, -10, 39, 82, -69, 105, -11, -44, -121, 127, -124, 76, -46, -100, 87, -92, -68, 79, -102, -33, -2, -42, -115, 122, -21, 43, 83, -40, 92, -95, 20, 23, -5, 35, -43, 125, 48, 103, 115, 8, 9, -18, -73, 112, 63, 97, -78, 25, -114, 78, -27, 75, -109, -113, 93, -37, -87, -83, -15, -82, 46, -53, 13, -4, -12, 45, 70, 110, 29, -105, -24, -47, -23, 77, 55, -91, 117, 94, -125, -98, -85, -126, -99, -71, 28, -32, -51, 73, -119, 1, -74, -67, 88, 36, -94, 95, 56, 120, -103, 21, -112, 80, -72, -107, -28, -48, -111, -57, -50, -19, 15, -76, 111, -96, -52, -16, 2, 74, 121, -61, -34, -93, -17, -22, 81, -26, 107, 24, -20, 27, 44, -128, -9, 116, -25, -1, 33, 90, 106, 84, 30, 65, 49, -110, 53, -60, 51, 7, 10, -70, 126, 14, 52, -120, -79, -104, 124, -13, 61, 96, 108, 123, -54, -45, 31, 50, 101, 4, 40, 100, -66, -123, -101, 47, 89, -118, -41, -80, 37, -84, -81, 18, 3, -30, -14 };
        EK_d = new short[] { 17623, 9916, 25195, 4958, 22409, 13794, 28981, 2479, 19832, 12051, 27588, 6897, 24102, 15437, 30874, 18348 };
    }
}
