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

package org.bouncycastle.crypto.engines;

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.util.Pack;

public class AsconEngine extends AsconBaseEngine
{
    private final AsconParameters asconParameters;
    private long K2;
    
    public AsconEngine(final AsconParameters asconParameters) {
        this.asconParameters = asconParameters;
        final int n = 16;
        this.MAC_SIZE = n;
        this.IV_SIZE = n;
        switch (asconParameters.ordinal()) {
            case 0: {
                this.KEY_SIZE = 20;
                this.BlockSize = 8;
                this.ASCON_IV = -6899501409222262784L;
                this.algorithmName = "Ascon-80pq AEAD";
                break;
            }
            case 1: {
                this.KEY_SIZE = 16;
                this.BlockSize = 16;
                this.ASCON_IV = -9187330011336540160L;
                this.algorithmName = "Ascon-128a AEAD";
                break;
            }
            case 2: {
                this.KEY_SIZE = 16;
                this.BlockSize = 8;
                this.ASCON_IV = -9205344418435956736L;
                this.algorithmName = "Ascon-128 AEAD";
                break;
            }
            default: {
                throw new IllegalArgumentException("invalid parameter setting for ASCON AEAD");
            }
        }
        this.nr = ((this.BlockSize == 8) ? 6 : 8);
        this.AADBufferSize = this.BlockSize;
        this.dsep = 1L;
        this.setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Default, DataOperatorType.Default);
    }
    
    @Override
    protected long pad(final int n) {
        return 128L << 56 - (n << 3);
    }
    
    @Override
    protected long loadBytes(final byte[] array, final int n) {
        return Pack.bigEndianToLong(array, n);
    }
    
    @Override
    protected void setBytes(final long n, final byte[] array, final int n2) {
        Pack.longToBigEndian(n, array, n2);
    }
    
    @Override
    protected void ascon_aeadinit() {
        this.p.set(this.ASCON_IV, this.K1, this.K2, this.N0, this.N1);
        if (this.KEY_SIZE == 20) {
            final AsconPermutationFriend.AsconPermutation p = this.p;
            p.x0 ^= this.K0;
        }
        this.p.p(12);
        if (this.KEY_SIZE == 20) {
            final AsconPermutationFriend.AsconPermutation p2 = this.p;
            p2.x2 ^= this.K0;
        }
        final AsconPermutationFriend.AsconPermutation p3 = this.p;
        p3.x3 ^= this.K1;
        final AsconPermutationFriend.AsconPermutation p4 = this.p;
        p4.x4 ^= this.K2;
    }
    
    @Override
    protected void processFinalAAD() {
        this.m_aad[this.m_aadPos] = -128;
        if (this.m_aadPos >= 8) {
            final AsconPermutationFriend.AsconPermutation p = this.p;
            p.x0 ^= Pack.bigEndianToLong(this.m_aad, 0);
            final AsconPermutationFriend.AsconPermutation p2 = this.p;
            p2.x1 ^= (Pack.bigEndianToLong(this.m_aad, 8) & -1L << 56 - (this.m_aadPos - 8 << 3));
        }
        else {
            final AsconPermutationFriend.AsconPermutation p3 = this.p;
            p3.x0 ^= (Pack.bigEndianToLong(this.m_aad, 0) & -1L << 56 - (this.m_aadPos << 3));
        }
    }
    
    @Override
    protected void processFinalDecrypt(final byte[] array, int n, final byte[] array2, int n2) {
        if (n >= 8) {
            final long bigEndianToLong = Pack.bigEndianToLong(array, 0);
            final AsconPermutationFriend.AsconPermutation p4 = this.p;
            p4.x0 ^= bigEndianToLong;
            Pack.longToBigEndian(this.p.x0, array2, n2);
            this.p.x0 = bigEndianToLong;
            n2 += 8;
            n -= 8;
            final AsconPermutationFriend.AsconPermutation p5 = this.p;
            p5.x1 ^= this.pad(n);
            if (n != 0) {
                final long littleEndianToLong_High = Pack.littleEndianToLong_High(array, 8, n);
                final AsconPermutationFriend.AsconPermutation p6 = this.p;
                p6.x1 ^= littleEndianToLong_High;
                Pack.longToLittleEndian_High(this.p.x1, array2, n2, n);
                final AsconPermutationFriend.AsconPermutation p7 = this.p;
                p7.x1 &= -1L >>> (n << 3);
                final AsconPermutationFriend.AsconPermutation p8 = this.p;
                p8.x1 ^= littleEndianToLong_High;
            }
        }
        else {
            final AsconPermutationFriend.AsconPermutation p9 = this.p;
            p9.x0 ^= this.pad(n);
            if (n != 0) {
                final long littleEndianToLong_High2 = Pack.littleEndianToLong_High(array, 0, n);
                final AsconPermutationFriend.AsconPermutation p10 = this.p;
                p10.x0 ^= littleEndianToLong_High2;
                Pack.longToLittleEndian_High(this.p.x0, array2, n2, n);
                final AsconPermutationFriend.AsconPermutation p11 = this.p;
                p11.x0 &= -1L >>> (n << 3);
                final AsconPermutationFriend.AsconPermutation p12 = this.p;
                p12.x0 ^= littleEndianToLong_High2;
            }
        }
        this.finishData(State.DecFinal);
    }
    
    @Override
    protected void processFinalEncrypt(final byte[] array, int n, final byte[] array2, int n2) {
        if (n >= 8) {
            final AsconPermutationFriend.AsconPermutation p4 = this.p;
            p4.x0 ^= Pack.bigEndianToLong(array, 0);
            Pack.longToBigEndian(this.p.x0, array2, n2);
            n2 += 8;
            n -= 8;
            final AsconPermutationFriend.AsconPermutation p5 = this.p;
            p5.x1 ^= this.pad(n);
            if (n != 0) {
                final AsconPermutationFriend.AsconPermutation p6 = this.p;
                p6.x1 ^= Pack.littleEndianToLong_High(array, 8, n);
                Pack.longToLittleEndian_High(this.p.x1, array2, n2, n);
            }
        }
        else {
            final AsconPermutationFriend.AsconPermutation p7 = this.p;
            p7.x0 ^= this.pad(n);
            if (n != 0) {
                final AsconPermutationFriend.AsconPermutation p8 = this.p;
                p8.x0 ^= Pack.littleEndianToLong_High(array, 0, n);
                Pack.longToLittleEndian_High(this.p.x0, array2, n2, n);
            }
        }
        this.finishData(State.EncFinal);
    }
    
    protected void finishData(final State state) {
        switch (this.asconParameters.ordinal()) {
            case 2: {
                final AsconPermutationFriend.AsconPermutation p = this.p;
                p.x1 ^= this.K1;
                final AsconPermutationFriend.AsconPermutation p2 = this.p;
                p2.x2 ^= this.K2;
                break;
            }
            case 1: {
                final AsconPermutationFriend.AsconPermutation p3 = this.p;
                p3.x2 ^= this.K1;
                final AsconPermutationFriend.AsconPermutation p4 = this.p;
                p4.x3 ^= this.K2;
                break;
            }
            case 0: {
                final AsconPermutationFriend.AsconPermutation p5 = this.p;
                p5.x1 ^= (this.K0 << 32 | this.K1 >> 32);
                final AsconPermutationFriend.AsconPermutation p6 = this.p;
                p6.x2 ^= (this.K1 << 32 | this.K2 >> 32);
                final AsconPermutationFriend.AsconPermutation p7 = this.p;
                p7.x3 ^= this.K2 << 32;
                break;
            }
            default: {
                throw new IllegalStateException();
            }
        }
        this.p.p(12);
        final AsconPermutationFriend.AsconPermutation p8 = this.p;
        p8.x3 ^= this.K1;
        final AsconPermutationFriend.AsconPermutation p9 = this.p;
        p9.x4 ^= this.K2;
        this.m_state = state;
    }
    
    @Override
    protected void init(final byte[] array, final byte[] array2) throws IllegalArgumentException {
        this.N0 = Pack.bigEndianToLong(array2, 0);
        this.N1 = Pack.bigEndianToLong(array2, 8);
        if (this.KEY_SIZE == 16) {
            this.K1 = Pack.bigEndianToLong(array, 0);
            this.K2 = Pack.bigEndianToLong(array, 8);
        }
        else {
            if (this.KEY_SIZE != 20) {
                throw new IllegalStateException();
            }
            this.K0 = Pack.bigEndianToInt(array, 0);
            this.K1 = Pack.bigEndianToLong(array, 4);
            this.K2 = Pack.bigEndianToLong(array, 12);
        }
    }
    
    @Override
    public String getAlgorithmVersion() {
        return "v1.2";
    }
    
    public enum AsconParameters
    {
        ascon80pq, 
        ascon128a, 
        ascon128;
    }
}
