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

package org.bouncycastle.pqc.crypto.snova;

import org.bouncycastle.util.GF16;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.digests.SHAKEDigest;
import java.security.SecureRandom;
import org.bouncycastle.pqc.crypto.MessageSigner;

public class SnovaSigner implements MessageSigner
{
    private SnovaParameters params;
    private SnovaEngine engine;
    private SecureRandom random;
    private final SHAKEDigest shake;
    private SnovaPublicKeyParameters pubKey;
    private SnovaPrivateKeyParameters privKey;
    
    public SnovaSigner() {
        this.shake = new SHAKEDigest(256);
    }
    
    @Override
    public void init(final boolean b, final CipherParameters cipherParameters) {
        if (b) {
            this.pubKey = null;
            if (cipherParameters instanceof ParametersWithRandom) {
                final ParametersWithRandom parametersWithRandom = (ParametersWithRandom)cipherParameters;
                this.privKey = (SnovaPrivateKeyParameters)parametersWithRandom.getParameters();
                this.random = parametersWithRandom.getRandom();
            }
            else {
                this.privKey = (SnovaPrivateKeyParameters)cipherParameters;
                this.random = CryptoServicesRegistrar.getSecureRandom();
            }
            this.params = this.privKey.getParameters();
        }
        else {
            this.pubKey = (SnovaPublicKeyParameters)cipherParameters;
            this.params = this.pubKey.getParameters();
            this.privKey = null;
            this.random = null;
        }
        this.engine = new SnovaEngine(this.params);
    }
    
    @Override
    public byte[] generateSignature(final byte[] array) {
        final byte[] messageHash = this.getMessageHash(array);
        final byte[] bytes = new byte[this.params.getSaltLength()];
        this.random.nextBytes(bytes);
        final byte[] array2 = new byte[(this.params.getN() * this.params.getLsq() + 1 >>> 1) + this.params.getSaltLength()];
        final SnovaKeyElements snovaKeyElements = new SnovaKeyElements(this.params);
        byte[] array3;
        byte[] array4;
        if (this.params.isSkIsSeed()) {
            final byte[] privateKey = this.privKey.getPrivateKey();
            array3 = Arrays.copyOfRange(privateKey, 0, 16);
            array4 = Arrays.copyOfRange(privateKey, 16, privateKey.length);
            this.engine.genMap1T12Map2(snovaKeyElements, array3, array4);
        }
        else {
            final byte[] privateKey2 = this.privKey.getPrivateKey();
            final byte[] array5 = new byte[privateKey2.length - 16 - 32 << 1];
            GF16Utils.decodeMergeInHalf(privateKey2, array5, array5.length);
            SnovaKeyElements.copy4d(array5, SnovaKeyElements.copy4d(array5, SnovaKeyElements.copy4d(array5, SnovaKeyElements.copy3d(array5, SnovaKeyElements.copy3d(array5, SnovaKeyElements.copy3d(array5, SnovaKeyElements.copy3d(array5, SnovaKeyElements.copy3d(array5, 0, snovaKeyElements.map1.aAlpha), snovaKeyElements.map1.bAlpha), snovaKeyElements.map1.qAlpha1), snovaKeyElements.map1.qAlpha2), snovaKeyElements.T12), snovaKeyElements.map2.f11), snovaKeyElements.map2.f12), snovaKeyElements.map2.f21);
            array3 = Arrays.copyOfRange(privateKey2, privateKey2.length - 16 - 32, privateKey2.length - 32);
            array4 = Arrays.copyOfRange(privateKey2, privateKey2.length - 32, privateKey2.length);
        }
        this.signDigestCore(array2, messageHash, bytes, snovaKeyElements.map1.aAlpha, snovaKeyElements.map1.bAlpha, snovaKeyElements.map1.qAlpha1, snovaKeyElements.map1.qAlpha2, snovaKeyElements.T12, snovaKeyElements.map2.f11, snovaKeyElements.map2.f12, snovaKeyElements.map2.f21, array3, array4);
        return Arrays.concatenate(array2, array);
    }
    
    @Override
    public boolean verifySignature(final byte[] array, final byte[] array2) {
        final byte[] messageHash = this.getMessageHash(array);
        final MapGroup1 mapGroup1 = new MapGroup1(this.params);
        final byte[] encoded = this.pubKey.getEncoded();
        final byte[] copy = Arrays.copyOf(encoded, 16);
        final byte[] copyOfRange = Arrays.copyOfRange(encoded, 16, encoded.length);
        this.engine.genABQP(mapGroup1, copy);
        final byte[][][][] array3 = new byte[this.params.getM()][this.params.getO()][this.params.getO()][this.params.getLsq()];
        if ((this.params.getLsq() & 0x1) == 0x0) {
            MapGroup1.decodeP(copyOfRange, 0, array3, copyOfRange.length << 1);
        }
        else {
            final byte[] array4 = new byte[copyOfRange.length << 1];
            GF16.decode(copyOfRange, array4, array4.length);
            MapGroup1.fillP(array4, 0, array3, array4.length);
        }
        return this.verifySignatureCore(messageHash, array2, copy, mapGroup1, array3);
    }
    
    void createSignedHash(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final int n3, final int n4, final byte[] array4, final int n5) {
        this.shake.update(array, 0, n);
        this.shake.update(array2, 0, n2);
        this.shake.update(array3, n3, n4);
        this.shake.doFinal(array4, 0, n5);
    }
    
    void signDigestCore(final byte[] array, final byte[] array2, final byte[] array3, final byte[][][] array4, final byte[][][] array5, final byte[][][] array6, final byte[][][] array7, final byte[][][] array8, final byte[][][][] array9, final byte[][][][] array10, final byte[][][][] array11, final byte[] array12, final byte[] array13) {
        final int m = this.params.getM();
        final int l = this.params.getL();
        final int lsq = this.params.getLsq();
        final int alpha = this.params.getAlpha();
        final int v = this.params.getV();
        final int o = this.params.getO();
        final int n = this.params.getN();
        final int n2 = m * lsq;
        final int n3 = o * lsq;
        final int n4 = v * lsq;
        final int n5 = n3 + 1 >>> 1;
        final byte[][] array14 = new byte[n2][n2 + 1];
        final byte[][] array15 = new byte[lsq][lsq];
        final byte[] array16 = new byte[n2];
        final byte[][][] array17 = new byte[alpha][v][lsq];
        final byte[][][] array18 = new byte[alpha][v][lsq];
        final byte[] array19 = new byte[lsq];
        final byte[] array20 = new byte[lsq];
        final byte[] array21 = new byte[lsq];
        final byte[] array22 = new byte[n2];
        final byte[] array23 = new byte[n * lsq];
        final byte[] array24 = new byte[n5];
        final byte[] array25 = new byte[n4 + 1 >>> 1];
        final byte[] array26 = new byte[l];
        byte b = 0;
        this.createSignedHash(array12, array12.length, array2, array2.length, array3, 0, array3.length, array24, n5);
        GF16.decode(array24, 0, array22, 0, array22.length);
        do {
            for (int i = 0; i < array14.length; ++i) {
                Arrays.fill(array14[i], (byte)0);
            }
            ++b;
            for (int j = 0; j < n2; ++j) {
                array14[j][n2] = array22[j];
            }
            this.shake.update(array13, 0, array13.length);
            this.shake.update(array2, 0, array2.length);
            this.shake.update(array3, 0, array3.length);
            this.shake.update(b);
            this.shake.doFinal(array25, 0, array25.length);
            GF16.decode(array25, array23, array25.length << 1);
            for (int k = 0, n6 = 0; k < m; ++k, n6 += lsq) {
                Arrays.fill(array21, (byte)0);
                for (int n7 = 0, n8 = k; n7 < alpha; ++n7, ++n8) {
                    if (n8 >= o) {
                        n8 -= o;
                    }
                    for (int n9 = 0, n10 = 0; n9 < v; ++n9, n10 += lsq) {
                        GF16Utils.gf16mTranMulMul(array23, n10, array4[k][n7], array5[k][n7], array6[k][n7], array7[k][n7], array26, array17[n7][n9], array18[n7][n9], l);
                    }
                    for (int n11 = 0; n11 < v; ++n11) {
                        for (int n12 = 0; n12 < v; ++n12) {
                            GF16Utils.gf16mMulMulTo(array17[n7][n11], array9[n8][n11][n12], array18[n7][n12], array26, array21, l);
                        }
                    }
                }
                int n13 = 0;
                int n14 = 0;
                while (n13 < l) {
                    for (int n15 = 0; n15 < l; ++n15) {
                        final byte[] array27 = array14[n6 + n14];
                        final int n16 = n2;
                        array27[n16] ^= array21[n14++];
                    }
                    ++n13;
                }
                for (int n17 = 0, n18 = 0; n17 < o; ++n17, n18 += lsq) {
                    for (int n19 = 0, n20 = k; n19 < alpha; ++n19, ++n20) {
                        if (n20 >= o) {
                            n20 -= o;
                        }
                        for (int n21 = 0; n21 < lsq; ++n21) {
                            Arrays.fill(array15[n21], (byte)0);
                        }
                        for (int n22 = 0; n22 < v; ++n22) {
                            GF16Utils.gf16mMulMul(array17[n19][n22], array10[n20][n22][n17], array7[k][n19], array26, array19, l);
                            GF16Utils.gf16mMulMul(array6[k][n19], array11[n20][n17][n22], array18[n19][n22], array26, array20, l);
                            int n23 = 0;
                            int n24 = 0;
                            int n25 = 0;
                            while (n23 < lsq) {
                                if (n24 == l) {
                                    n24 = 0;
                                    n25 += l;
                                }
                                byte b2 = array19[n25];
                                byte b3 = array20[n24];
                                int n26 = 0;
                                int n27 = 0;
                                int n28 = 0;
                                int n29 = 0;
                                for (int n30 = 0; n26 < lsq; ++n26, ++n27, n30 += l) {
                                    if (n27 == l) {
                                        n27 = 0;
                                        n30 = 0;
                                        ++n28;
                                        n29 += l;
                                        b2 = array19[n25 + n28];
                                        b3 = array20[n29 + n24];
                                    }
                                    final byte b4 = array5[k][n19][n30 + n24];
                                    final byte b5 = array4[k][n19][n25 + n27];
                                    final byte[] array28 = array15[n23];
                                    final int n31 = n26;
                                    array28[n31] ^= (byte)(GF16.mul(b2, b4) ^ GF16.mul(b5, b3));
                                }
                                ++n23;
                                ++n24;
                            }
                        }
                        for (int n32 = 0; n32 < lsq; ++n32) {
                            for (int n33 = 0; n33 < lsq; ++n33) {
                                final byte[] array29 = array14[n6 + n32];
                                final int n34 = n18 + n33;
                                array29[n34] ^= array15[n32][n33];
                            }
                        }
                    }
                }
            }
        } while (this.performGaussianElimination(array14, array16, n2) != 0);
        for (int n35 = 0, n36 = 0; n35 < v; ++n35, n36 += lsq) {
            for (int n37 = 0, n38 = 0; n37 < o; ++n37, n38 += lsq) {
                GF16Utils.gf16mMulTo(array8[n35][n37], array16, n38, array23, n36, l);
            }
        }
        System.arraycopy(array16, 0, array23, n4, n3);
        GF16.encode(array23, array, array23.length);
        System.arraycopy(array3, 0, array, array.length - 16, 16);
    }
    
    boolean verifySignatureCore(final byte[] array, final byte[] array2, final byte[] array3, final MapGroup1 mapGroup1, final byte[][][][] array4) {
        final int lsq = this.params.getLsq();
        final int n = this.params.getO() * lsq;
        final int n2 = n + 1 >>> 1;
        final int saltLength = this.params.getSaltLength();
        final int m = this.params.getM();
        final int n3 = this.params.getN() * lsq;
        final int n4 = n3 + 1 >>> 1;
        final byte[] array5 = new byte[n2];
        this.createSignedHash(array3, array3.length, array, array.length, array2, n4, saltLength, array5, n2);
        if ((n & 0x1) != 0x0) {
            final byte[] array6 = array5;
            final int n5 = n2 - 1;
            array6[n5] &= 0xF;
        }
        final byte[] array7 = new byte[n3];
        GF16.decode(array2, 0, array7, 0, array7.length);
        final byte[] array8 = new byte[m * lsq];
        this.evaluation(array8, mapGroup1, array4, array7);
        final byte[] array9 = new byte[n2];
        GF16.encode(array8, array9, array8.length);
        return Arrays.areEqual(array5, array9);
    }
    
    private void evaluation(final byte[] array, final MapGroup1 mapGroup1, final byte[][][][] array2, final byte[] array3) {
        final int m = this.params.getM();
        final int alpha = this.params.getAlpha();
        final int n = this.params.getN();
        final int l = this.params.getL();
        final int lsq = this.params.getLsq();
        final int o = this.params.getO();
        final byte[][][] array4 = new byte[alpha][n][lsq];
        final byte[][][] array5 = new byte[alpha][n][lsq];
        final byte[] array6 = new byte[lsq];
        for (int i = 0, n2 = 0; i < m; ++i, n2 += lsq) {
            for (int j = 0, n3 = 0; j < n; ++j, n3 += lsq) {
                for (int k = 0; k < alpha; ++k) {
                    GF16Utils.gf16mTranMulMul(array3, n3, mapGroup1.aAlpha[i][k], mapGroup1.bAlpha[i][k], mapGroup1.qAlpha1[i][k], mapGroup1.qAlpha2[i][k], array6, array4[k][j], array5[k][j], l);
                }
            }
            for (int n4 = 0, n5 = i; n4 < alpha; ++n4, ++n5) {
                if (n5 >= o) {
                    n5 -= o;
                }
                for (int n6 = 0; n6 < n; ++n6) {
                    GF16Utils.gf16mMul(this.getPMatrix(mapGroup1, array2, n5, n6, 0), array5[n4][0], array6, l);
                    for (int n7 = 1; n7 < n; ++n7) {
                        GF16Utils.gf16mMulTo(this.getPMatrix(mapGroup1, array2, n5, n6, n7), array5[n4][n7], array6, l);
                    }
                    GF16Utils.gf16mMulTo(array4[n4][n6], array6, array, n2, l);
                }
            }
        }
    }
    
    private byte[] getPMatrix(final MapGroup1 mapGroup1, final byte[][][][] array, final int n, final int n2, final int n3) {
        final int v = this.params.getV();
        if (n2 < v) {
            if (n3 < v) {
                return mapGroup1.p11[n][n2][n3];
            }
            return mapGroup1.p12[n][n2][n3 - v];
        }
        else {
            if (n3 < v) {
                return mapGroup1.p21[n][n2 - v][n3];
            }
            return array[n][n2 - v][n3 - v];
        }
    }
    
    private int performGaussianElimination(final byte[][] array, final byte[] array2, final int n) {
        final int n2 = n + 1;
        for (int i = 0; i < n; ++i) {
            int n3;
            for (n3 = i; n3 < n && array[n3][i] == 0; ++n3) {}
            if (n3 >= n) {
                return 1;
            }
            if (n3 != i) {
                final byte[] array3 = array[i];
                array[i] = array[n3];
                array[n3] = array3;
            }
            final byte inv = GF16.inv(array[i][i]);
            for (int j = i; j < n2; ++j) {
                array[i][j] = GF16.mul(array[i][j], inv);
            }
            for (int k = i + 1; k < n; ++k) {
                final byte b = array[k][i];
                if (b != 0) {
                    for (int l = i; l < n2; ++l) {
                        final byte[] array4 = array[k];
                        final int n4 = l;
                        array4[n4] ^= GF16.mul(array[i][l], b);
                    }
                }
            }
        }
        for (int n5 = n - 1; n5 >= 0; --n5) {
            byte b2 = array[n5][n];
            for (int n6 = n5 + 1; n6 < n; ++n6) {
                b2 ^= GF16.mul(array[n5][n6], array2[n6]);
            }
            array2[n5] = b2;
        }
        return 0;
    }
    
    private byte[] getMessageHash(final byte[] array) {
        final byte[] array2 = new byte[this.shake.getDigestSize()];
        this.shake.update(array, 0, array.length);
        this.shake.doFinal(array2, 0);
        return array2;
    }
}
