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

package org.bouncycastle.crypto.fpe;

import org.bouncycastle.util.Bytes;
import org.bouncycastle.util.BigIntegers;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.Integers;
import org.bouncycastle.util.Arrays;
import java.math.BigInteger;
import org.bouncycastle.crypto.util.RadixConverter;
import org.bouncycastle.crypto.BlockCipher;

class SP80038G
{
    static final String FPE_DISABLED = "org.bouncycastle.fpe.disable";
    static final String FF1_DISABLED = "org.bouncycastle.fpe.disable_ff1";
    protected static final int BLOCK_SIZE = 16;
    protected static final double LOG2;
    protected static final double TWO_TO_96;
    
    static byte[] decryptFF1(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final byte[] array2, final int n, final int n2) {
        checkArgs(blockCipher, true, radixConverter.getRadix(), array2, n, n2);
        final int n3 = n2 / 2;
        final int n4 = n2 - n3;
        return toByte(decFF1(blockCipher, radixConverter, array, n2, n3, n4, toShort(array2, n, n3), toShort(array2, n + n3, n4)));
    }
    
    static short[] decryptFF1w(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final short[] array2, final int n, final int n2) {
        checkArgs(blockCipher, true, radixConverter.getRadix(), array2, n, n2);
        final int n3 = n2 / 2;
        final int n4 = n2 - n3;
        final short[] array3 = new short[n3];
        final short[] array4 = new short[n4];
        System.arraycopy(array2, n, array3, 0, n3);
        System.arraycopy(array2, n + n3, array4, 0, n4);
        return decFF1(blockCipher, radixConverter, array, n2, n3, n4, array3, array4);
    }
    
    static short[] decFF1(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final int n, final int n2, final int n3, short[] array2, short[] array3) {
        final int radix = radixConverter.getRadix();
        final int length = array.length;
        final int calculateB_FF1 = calculateB_FF1(radix, n3);
        final int n4 = calculateB_FF1 + 7 & 0xFFFFFFFC;
        final byte[] calculateP_FF1 = calculateP_FF1(radix, (byte)n2, n, length);
        final BigInteger[] calculateModUV = calculateModUV(BigInteger.valueOf(radix), n2, n3);
        int n5 = n2;
        for (int i = 9; i >= 0; --i) {
            final BigInteger calculateY_FF1 = calculateY_FF1(blockCipher, array, calculateB_FF1, n4, i, calculateP_FF1, array2, radixConverter);
            n5 = n - n5;
            final BigInteger mod = radixConverter.fromEncoding(array3).subtract(calculateY_FF1).mod(calculateModUV[i & 0x1]);
            final short[] array4 = array3;
            array3 = array2;
            array2 = array4;
            radixConverter.toEncoding(mod, n5, array4);
        }
        return Arrays.concatenate(array2, array3);
    }
    
    static byte[] decryptFF3(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final byte[] array2, final int n, final int n2) {
        checkArgs(blockCipher, false, radixConverter.getRadix(), array2, n, n2);
        if (array.length != 8) {
            throw new IllegalArgumentException();
        }
        return implDecryptFF3(blockCipher, radixConverter, array, array2, n, n2);
    }
    
    static byte[] decryptFF3_1(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final byte[] array2, final int n, final int n2) {
        checkArgs(blockCipher, false, radixConverter.getRadix(), array2, n, n2);
        if (array.length != 7) {
            throw new IllegalArgumentException("tweak should be 56 bits");
        }
        return implDecryptFF3(blockCipher, radixConverter, calculateTweak64_FF3_1(array), array2, n, n2);
    }
    
    static short[] decryptFF3_1w(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final short[] array2, final int n, final int n2) {
        checkArgs(blockCipher, false, radixConverter.getRadix(), array2, n, n2);
        if (array.length != 7) {
            throw new IllegalArgumentException("tweak should be 56 bits");
        }
        return implDecryptFF3w(blockCipher, radixConverter, calculateTweak64_FF3_1(array), array2, n, n2);
    }
    
    static byte[] encryptFF1(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final byte[] array2, final int n, final int n2) {
        checkArgs(blockCipher, true, radixConverter.getRadix(), array2, n, n2);
        final int n3 = n2 / 2;
        final int n4 = n2 - n3;
        return toByte(encFF1(blockCipher, radixConverter, array, n2, n3, n4, toShort(array2, n, n3), toShort(array2, n + n3, n4)));
    }
    
    static short[] encryptFF1w(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final short[] array2, final int n, final int n2) {
        checkArgs(blockCipher, true, radixConverter.getRadix(), array2, n, n2);
        final int n3 = n2 / 2;
        final int n4 = n2 - n3;
        final short[] array3 = new short[n3];
        final short[] array4 = new short[n4];
        System.arraycopy(array2, n, array3, 0, n3);
        System.arraycopy(array2, n + n3, array4, 0, n4);
        return encFF1(blockCipher, radixConverter, array, n2, n3, n4, array3, array4);
    }
    
    private static short[] encFF1(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final int n, final int n2, final int n3, short[] array2, short[] array3) {
        final int radix = radixConverter.getRadix();
        final int length = array.length;
        final int calculateB_FF1 = calculateB_FF1(radix, n3);
        final int n4 = calculateB_FF1 + 7 & 0xFFFFFFFC;
        final byte[] calculateP_FF1 = calculateP_FF1(radix, (byte)n2, n, length);
        final BigInteger[] calculateModUV = calculateModUV(BigInteger.valueOf(radix), n2, n3);
        int n5 = n3;
        for (int i = 0; i < 10; ++i) {
            final BigInteger calculateY_FF1 = calculateY_FF1(blockCipher, array, calculateB_FF1, n4, i, calculateP_FF1, array3, radixConverter);
            n5 = n - n5;
            final BigInteger mod = radixConverter.fromEncoding(array2).add(calculateY_FF1).mod(calculateModUV[i & 0x1]);
            final short[] array4 = array2;
            array2 = array3;
            array3 = array4;
            radixConverter.toEncoding(mod, n5, array4);
        }
        return Arrays.concatenate(array2, array3);
    }
    
    static byte[] encryptFF3(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final byte[] array2, final int n, final int n2) {
        checkArgs(blockCipher, false, radixConverter.getRadix(), array2, n, n2);
        if (array.length != 8) {
            throw new IllegalArgumentException();
        }
        return implEncryptFF3(blockCipher, radixConverter, array, array2, n, n2);
    }
    
    static short[] encryptFF3w(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final short[] array2, final int n, final int n2) {
        checkArgs(blockCipher, false, radixConverter.getRadix(), array2, n, n2);
        if (array.length != 8) {
            throw new IllegalArgumentException();
        }
        return implEncryptFF3w(blockCipher, radixConverter, array, array2, n, n2);
    }
    
    static short[] encryptFF3_1w(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final short[] array2, final int n, final int n2) {
        checkArgs(blockCipher, false, radixConverter.getRadix(), array2, n, n2);
        if (array.length != 7) {
            throw new IllegalArgumentException("tweak should be 56 bits");
        }
        return encryptFF3w(blockCipher, radixConverter, calculateTweak64_FF3_1(array), array2, n, n2);
    }
    
    static byte[] encryptFF3_1(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final byte[] array2, final int n, final int n2) {
        checkArgs(blockCipher, false, radixConverter.getRadix(), array2, n, n2);
        if (array.length != 7) {
            throw new IllegalArgumentException("tweak should be 56 bits");
        }
        return encryptFF3(blockCipher, radixConverter, calculateTweak64_FF3_1(array), array2, n, n2);
    }
    
    protected static int calculateB_FF1(final int n, final int exponent) {
        final int numberOfTrailingZeros = Integers.numberOfTrailingZeros(n);
        int n2 = numberOfTrailingZeros * exponent;
        final int n3 = n >>> numberOfTrailingZeros;
        if (n3 != 1) {
            n2 += BigInteger.valueOf(n3).pow(exponent).bitLength();
        }
        return (n2 + 7) / 8;
    }
    
    protected static BigInteger[] calculateModUV(final BigInteger val, final int exponent, final int n) {
        final BigInteger[] array = { val.pow(exponent), null };
        array[1] = array[0];
        if (n != exponent) {
            array[1] = array[1].multiply(val);
        }
        return array;
    }
    
    protected static byte[] calculateP_FF1(final int n, final byte b, final int n2, final int n3) {
        final byte[] array = { 1, 2, 1, 0, (byte)(n >> 8), (byte)n, 10, b, 0, 0, 0, 0, 0, 0, 0, 0 };
        Pack.intToBigEndian(n2, array, 8);
        Pack.intToBigEndian(n3, array, 12);
        return array;
    }
    
    protected static byte[] calculateTweak64_FF3_1(final byte[] array) {
        return new byte[] { array[0], array[1], array[2], (byte)(array[3] & 0xF0), array[4], array[5], array[6], (byte)(array[3] << 4) };
    }
    
    protected static BigInteger calculateY_FF1(final BlockCipher blockCipher, final byte[] array, final int n, final int n2, final int n3, final byte[] array2, final short[] array3, final RadixConverter radixConverter) {
        final int length = array.length;
        final byte[] unsignedByteArray = BigIntegers.asUnsignedByteArray(radixConverter.fromEncoding(array3));
        final int n4 = -(length + n + 1) & 0xF;
        final byte[] array4 = new byte[length + n4 + 1 + n];
        System.arraycopy(array, 0, array4, 0, length);
        array4[length + n4] = (byte)n3;
        System.arraycopy(unsignedByteArray, 0, array4, array4.length - unsignedByteArray.length, unsignedByteArray.length);
        byte[] prf;
        final byte[] array5 = prf = prf(blockCipher, Arrays.concatenate(array2, array4));
        if (n2 > 16) {
            final int n5 = (n2 + 16 - 1) / 16;
            prf = new byte[n5 * 16];
            final int bigEndianToInt = Pack.bigEndianToInt(array5, 12);
            System.arraycopy(array5, 0, prf, 0, 16);
            for (int i = 1; i < n5; ++i) {
                final int n6 = i * 16;
                System.arraycopy(array5, 0, prf, n6, 12);
                Pack.intToBigEndian(bigEndianToInt ^ i, prf, n6 + 16 - 4);
                blockCipher.processBlock(prf, n6, prf, n6);
            }
        }
        return num(prf, 0, n2);
    }
    
    protected static BigInteger calculateY_FF3(final BlockCipher blockCipher, final byte[] array, final int n, final int n2, final short[] array2, final RadixConverter radixConverter) {
        final byte[] array3 = new byte[16];
        Pack.intToBigEndian(Pack.bigEndianToInt(array, n) ^ n2, array3, 0);
        BigIntegers.asUnsignedByteArray(radixConverter.fromEncoding(array2), array3, 4, 12);
        Arrays.reverseInPlace(array3);
        blockCipher.processBlock(array3, 0, array3, 0);
        Arrays.reverseInPlace(array3);
        final byte[] array4 = array3;
        return num(array4, 0, array4.length);
    }
    
    protected static void checkArgs(final BlockCipher blockCipher, final boolean b, final int n, final short[] array, final int n2, final int n3) {
        checkCipher(blockCipher);
        if (n < 2 || n > 65536) {
            throw new IllegalArgumentException();
        }
        checkData(b, n, array, n2, n3);
    }
    
    protected static void checkArgs(final BlockCipher blockCipher, final boolean b, final int n, final byte[] array, final int n2, final int n3) {
        checkCipher(blockCipher);
        if (n < 2 || n > 256) {
            throw new IllegalArgumentException();
        }
        checkData(b, n, array, n2, n3);
    }
    
    protected static void checkCipher(final BlockCipher blockCipher) {
        if (16 != blockCipher.getBlockSize()) {
            throw new IllegalArgumentException();
        }
    }
    
    protected static void checkData(final boolean b, final int n, final short[] array, final int n2, final int n3) {
        checkLength(b, n, n3);
        for (int i = 0; i < n3; ++i) {
            if ((array[n2 + i] & 0xFFFF) >= n) {
                throw new IllegalArgumentException("input data outside of radix");
            }
        }
    }
    
    protected static void checkData(final boolean b, final int n, final byte[] array, final int n2, final int n3) {
        checkLength(b, n, n3);
        for (int i = 0; i < n3; ++i) {
            if ((array[n2 + i] & 0xFF) >= n) {
                throw new IllegalArgumentException("input data outside of radix");
            }
        }
    }
    
    private static void checkLength(final boolean b, final int n, final int n2) {
        if (n2 < 2 || Math.pow(n, n2) < 1000000.0) {
            throw new IllegalArgumentException("input too short");
        }
        if (!b) {
            final int i = 2 * (int)Math.floor(Math.log(SP80038G.TWO_TO_96) / Math.log(n));
            if (n2 > i) {
                throw new IllegalArgumentException("maximum input length is " + i);
            }
        }
    }
    
    protected static byte[] implDecryptFF3(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final byte[] array2, final int n, final int n2) {
        final int n3 = n2 / 2;
        final int n4 = n2 - n3;
        return toByte(decFF3_1(blockCipher, radixConverter, array, n2, n3, n4, toShort(array2, n, n4), toShort(array2, n + n4, n3)));
    }
    
    protected static short[] implDecryptFF3w(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final short[] array2, final int n, final int n2) {
        final int n3 = n2 / 2;
        final int n4 = n2 - n3;
        final short[] array3 = new short[n4];
        final short[] array4 = new short[n3];
        System.arraycopy(array2, n, array3, 0, n4);
        System.arraycopy(array2, n + n4, array4, 0, n3);
        return decFF3_1(blockCipher, radixConverter, array, n2, n3, n4, array3, array4);
    }
    
    private static short[] decFF3_1(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final int n, final int n2, final int n3, short[] array2, short[] array3) {
        final BigInteger[] calculateModUV = calculateModUV(BigInteger.valueOf(radixConverter.getRadix()), n2, n3);
        int n4 = n3;
        Arrays.reverseInPlace(array2);
        Arrays.reverseInPlace(array3);
        for (int i = 7; i >= 0; --i) {
            n4 = n - n4;
            final BigInteger mod = radixConverter.fromEncoding(array3).subtract(calculateY_FF3(blockCipher, array, 4 - (i & 0x1) * 4, i, array2, radixConverter)).mod(calculateModUV[1 - (i & 0x1)]);
            final short[] array4 = array3;
            array3 = array2;
            array2 = array4;
            radixConverter.toEncoding(mod, n4, array4);
        }
        Arrays.reverseInPlace(array2);
        Arrays.reverseInPlace(array3);
        return Arrays.concatenate(array2, array3);
    }
    
    protected static byte[] implEncryptFF3(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final byte[] array2, final int n, final int n2) {
        final int n3 = n2 / 2;
        final int n4 = n2 - n3;
        return toByte(encFF3_1(blockCipher, radixConverter, array, n2, n3, n4, toShort(array2, n, n4), toShort(array2, n + n4, n3)));
    }
    
    protected static short[] implEncryptFF3w(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final short[] array2, final int n, final int n2) {
        final int n3 = n2 / 2;
        final int n4 = n2 - n3;
        final short[] array3 = new short[n4];
        final short[] array4 = new short[n3];
        System.arraycopy(array2, n, array3, 0, n4);
        System.arraycopy(array2, n + n4, array4, 0, n3);
        return encFF3_1(blockCipher, radixConverter, array, n2, n3, n4, array3, array4);
    }
    
    private static short[] encFF3_1(final BlockCipher blockCipher, final RadixConverter radixConverter, final byte[] array, final int n, final int n2, final int n3, short[] array2, short[] array3) {
        final BigInteger[] calculateModUV = calculateModUV(BigInteger.valueOf(radixConverter.getRadix()), n2, n3);
        int n4 = n2;
        Arrays.reverseInPlace(array2);
        Arrays.reverseInPlace(array3);
        for (int i = 0; i < 8; ++i) {
            n4 = n - n4;
            final BigInteger mod = radixConverter.fromEncoding(array2).add(calculateY_FF3(blockCipher, array, 4 - (i & 0x1) * 4, i, array3, radixConverter)).mod(calculateModUV[1 - (i & 0x1)]);
            final short[] array4 = array2;
            array2 = array3;
            array3 = array4;
            radixConverter.toEncoding(mod, n4, array4);
        }
        Arrays.reverseInPlace(array2);
        Arrays.reverseInPlace(array3);
        return Arrays.concatenate(array2, array3);
    }
    
    protected static BigInteger num(final byte[] array, final int n, final int n2) {
        return new BigInteger(1, Arrays.copyOfRange(array, n, n + n2));
    }
    
    protected static byte[] prf(final BlockCipher blockCipher, final byte[] array) {
        if (array.length % 16 != 0) {
            throw new IllegalArgumentException();
        }
        final int n = array.length / 16;
        final byte[] array2 = new byte[16];
        for (int i = 0; i < n; ++i) {
            Bytes.xorTo(16, array, i * 16, array2, 0);
            blockCipher.processBlock(array2, 0, array2, 0);
        }
        return array2;
    }
    
    private static byte[] toByte(final short[] array) {
        final byte[] array2 = new byte[array.length];
        for (int i = 0; i != array2.length; ++i) {
            array2[i] = (byte)array[i];
        }
        return array2;
    }
    
    private static short[] toShort(final byte[] array, final int n, final int n2) {
        final short[] array2 = new short[n2];
        for (int i = 0; i != array2.length; ++i) {
            array2[i] = (short)(array[n + i] & 0xFF);
        }
        return array2;
    }
    
    static {
        LOG2 = Math.log(2.0);
        TWO_TO_96 = Math.pow(2.0, 96.0);
    }
}
