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

package org.bouncycastle.crypto.macs;

import org.bouncycastle.util.Memoable;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.Zuc128CoreEngine;
import org.bouncycastle.crypto.Mac;

public final class Zuc128Mac implements Mac
{
    private static final int TOPBIT = 128;
    private final InternalZuc128Engine theEngine;
    private int theMac;
    private final int[] theKeyStream;
    private Zuc128CoreEngine theState;
    private int theWordIndex;
    private int theByteIndex;
    
    public Zuc128Mac() {
        this.theEngine = new InternalZuc128Engine();
        this.theKeyStream = new int[2];
    }
    
    @Override
    public String getAlgorithmName() {
        return "Zuc128Mac";
    }
    
    @Override
    public int getMacSize() {
        return 4;
    }
    
    @Override
    public void init(final CipherParameters cipherParameters) {
        this.theEngine.init(true, cipherParameters);
        this.theState = (Zuc128CoreEngine)this.theEngine.copy();
        this.initKeyStream();
    }
    
    private void initKeyStream() {
        this.theMac = 0;
        for (int i = 0; i < this.theKeyStream.length - 1; ++i) {
            this.theKeyStream[i] = this.theEngine.createKeyStreamWord();
        }
        this.theWordIndex = this.theKeyStream.length - 1;
        this.theByteIndex = 3;
    }
    
    @Override
    public void update(final byte b) {
        this.shift4NextByte();
        final int n = this.theByteIndex * 8;
        for (int i = 128, n2 = 0; i > 0; i >>= 1, ++n2) {
            if ((b & i) != 0x0) {
                this.updateMac(n + n2);
            }
        }
    }
    
    private void shift4NextByte() {
        this.theByteIndex = (this.theByteIndex + 1) % 4;
        if (this.theByteIndex == 0) {
            this.theKeyStream[this.theWordIndex] = this.theEngine.createKeyStreamWord();
            this.theWordIndex = (this.theWordIndex + 1) % this.theKeyStream.length;
        }
    }
    
    private void updateMac(final int n) {
        this.theMac ^= this.getKeyStreamWord(n);
    }
    
    private int getKeyStreamWord(final int n) {
        final int n2 = this.theKeyStream[this.theWordIndex];
        if (n == 0) {
            return n2;
        }
        return n2 << n | this.theKeyStream[(this.theWordIndex + 1) % this.theKeyStream.length] >>> 32 - n;
    }
    
    @Override
    public void update(final byte[] array, final int n, final int n2) {
        for (int i = 0; i < n2; ++i) {
            this.update(array[n + i]);
        }
    }
    
    private int getFinalWord() {
        if (this.theByteIndex != 0) {
            return this.theEngine.createKeyStreamWord();
        }
        this.theWordIndex = (this.theWordIndex + 1) % this.theKeyStream.length;
        return this.theKeyStream[this.theWordIndex];
    }
    
    @Override
    public int doFinal(final byte[] array, final int n) {
        this.shift4NextByte();
        this.theMac ^= this.getKeyStreamWord(this.theByteIndex * 8);
        Zuc128CoreEngine.encode32be(this.theMac ^= this.getFinalWord(), array, n);
        this.reset();
        return this.getMacSize();
    }
    
    @Override
    public void reset() {
        if (this.theState != null) {
            this.theEngine.reset(this.theState);
        }
        this.initKeyStream();
    }
    
    private static class InternalZuc128Engine extends Zuc128CoreEngine
    {
        int createKeyStreamWord() {
            return super.makeKeyStreamWord();
        }
    }
}
