HqcEngine
                    class HqcEngine
                
                using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Pqc.Crypto.Hqc
{
    internal class HqcEngine
    {
        private int n;
        private int n1;
        private int n2;
        private int k;
        private int delta;
        private int w;
        private int wr;
        private int we;
        private int g;
        private int rejectionThreshold;
        private int fft;
        private int mulParam;
        private int SEED_SIZE = 40;
        private byte G_FCT_DOMAIN = 3;
        private byte K_FCT_DOMAIN = 4;
        private int N_BYTE;
        private int n1n2;
        private int N_BYTE_64;
        private int K_BYTE;
        private int K_BYTE_64;
        private int N1_BYTE_64;
        private int N1N2_BYTE_64;
        private int N1N2_BYTE;
        private int N1_BYTE;
        private int SALT_SIZE_BYTES = 16;
        private int[] generatorPoly;
        private int SHA512_BYTES = 64;
        private ulong RED_MASK;
        private GF2PolynomialCalculator gfCalculator;
        public HqcEngine(int n, int n1, int n2, int k, int g, int delta, int w, int wr, int we, int rejectionThreshold, int fft, int[] generatorPoly)
        {
            this.n = n;
            this.k = k;
            this.delta = delta;
            this.w = w;
            this.wr = wr;
            this.we = we;
            this.n1 = n1;
            this.n2 = n2;
            n1n2 = n1 * n2;
            this.generatorPoly = generatorPoly;
            this.g = g;
            this.rejectionThreshold = rejectionThreshold;
            this.fft = fft;
            mulParam = (n2 + 127) / 128;
            N_BYTE = Utils.GetByteSizeFromBitSize(n);
            K_BYTE = k;
            N_BYTE_64 = Utils.GetByte64SizeFromBitSize(n);
            K_BYTE_64 = Utils.GetByteSizeFromBitSize(k);
            N1_BYTE_64 = Utils.GetByteSizeFromBitSize(n1);
            N1N2_BYTE_64 = Utils.GetByte64SizeFromBitSize(n1 * n2);
            N1N2_BYTE = Utils.GetByteSizeFromBitSize(n1 * n2);
            N1_BYTE = Utils.GetByteSizeFromBitSize(n1);
            RED_MASK = (ulong)((1 << n % 64) - 1);
            gfCalculator = new GF2PolynomialCalculator(N_BYTE_64, n, RED_MASK);
        }
        public void GenKeyPair(byte[] pk, byte[] sk, byte[] seed)
        {
            byte[] array = new byte[SEED_SIZE];
            byte[] array2 = new byte[K_BYTE];
            HqcKeccakRandomGenerator hqcKeccakRandomGenerator = new HqcKeccakRandomGenerator(256);
            hqcKeccakRandomGenerator.RandomGeneratorInit(seed, null, seed.Length, 0);
            hqcKeccakRandomGenerator.Squeeze(array, 40);
            hqcKeccakRandomGenerator.Squeeze(array2, K_BYTE);
            HqcKeccakRandomGenerator hqcKeccakRandomGenerator2 = new HqcKeccakRandomGenerator(256);
            hqcKeccakRandomGenerator2.SeedExpanderInit(array, array.Length);
            long[] array3 = new long[N_BYTE_64];
            long[] array4 = new long[N_BYTE_64];
            GenerateRandomFixedWeight(array4, hqcKeccakRandomGenerator2, w);
            GenerateRandomFixedWeight(array3, hqcKeccakRandomGenerator2, w);
            byte[] array5 = new byte[SEED_SIZE];
            hqcKeccakRandomGenerator.Squeeze(array5, 40);
            HqcKeccakRandomGenerator hqcKeccakRandomGenerator3 = new HqcKeccakRandomGenerator(256);
            hqcKeccakRandomGenerator3.SeedExpanderInit(array5, array5.Length);
            long[] array6 = new long[N_BYTE_64];
            GeneratePublicKeyH(array6, hqcKeccakRandomGenerator3);
            long[] array7 = new long[N_BYTE_64];
            gfCalculator.MultLongs(array7, array4, array6);
            GF2PolynomialCalculator.AddLongs(array7, array7, array3);
            byte[] array8 = new byte[N_BYTE];
            Utils.FromLongArrayToByteArray(array8, array7);
            byte[] array9 = Arrays.Concatenate(array5, array8);
            byte[] array10 = Arrays.ConcatenateAll(array, array2, array9);
            Array.Copy(array9, 0, pk, 0, array9.Length);
            Array.Copy(array10, 0, sk, 0, array10.Length);
        }
        public void Encaps(byte[] u, byte[] v, byte[] K, byte[] pk, byte[] seed, byte[] salt)
        {
            byte[] array = new byte[K_BYTE];
            byte[] output = new byte[SEED_SIZE];
            HqcKeccakRandomGenerator hqcKeccakRandomGenerator = new HqcKeccakRandomGenerator(256);
            hqcKeccakRandomGenerator.RandomGeneratorInit(seed, null, seed.Length, 0);
            hqcKeccakRandomGenerator.Squeeze(output, 40);
            byte[] output2 = new byte[K_BYTE];
            hqcKeccakRandomGenerator.Squeeze(output2, K_BYTE);
            byte[] output3 = new byte[SEED_SIZE];
            hqcKeccakRandomGenerator.Squeeze(output3, 40);
            hqcKeccakRandomGenerator.Squeeze(array, K_BYTE);
            byte[] array2 = new byte[SHA512_BYTES];
            byte[] array3 = new byte[K_BYTE + SALT_SIZE_BYTES * 2 + SALT_SIZE_BYTES];
            hqcKeccakRandomGenerator.Squeeze(salt, SALT_SIZE_BYTES);
            Array.Copy(array, 0, array3, 0, array.Length);
            Array.Copy(pk, 0, array3, K_BYTE, SALT_SIZE_BYTES * 2);
            Array.Copy(salt, 0, array3, K_BYTE + SALT_SIZE_BYTES * 2, SALT_SIZE_BYTES);
            HqcKeccakRandomGenerator hqcKeccakRandomGenerator2 = new HqcKeccakRandomGenerator(256);
            hqcKeccakRandomGenerator2.SHAKE256_512_ds(array2, array3, array3.Length, new byte[1] {
                G_FCT_DOMAIN
            });
            long[] h = new long[N_BYTE_64];
            byte[] s = new byte[N_BYTE];
            ExtractPublicKeys(h, s, pk);
            long[] array4 = new long[N1N2_BYTE_64];
            Encrypt(u, array4, h, s, array, array2);
            Utils.FromLongArrayToByteArray(v, array4);
            byte[] array5 = Arrays.ConcatenateAll(array, u, v);
            hqcKeccakRandomGenerator2.SHAKE256_512_ds(K, array5, array5.Length, new byte[1] {
                K_FCT_DOMAIN
            });
        }
        public int Decaps(byte[] ss, byte[] ct, byte[] sk)
        {
            long[] array13 = new long[N_BYTE_64];
            long[] y = new long[N_BYTE_64];
            byte[] array = new byte[40 + N_BYTE];
            byte[] array2 = new byte[K_BYTE];
            ExtractKeysFromSecretKeys(y, array2, array, sk);
            byte[] array3 = new byte[N_BYTE];
            byte[] array4 = new byte[N1N2_BYTE];
            byte[] array5 = new byte[SALT_SIZE_BYTES];
            ExtractCiphertexts(array3, array4, array5, ct);
            byte[] array6 = new byte[k];
            int num = Decrypt(array6, array6, array2, array3, array4, y);
            byte[] array7 = new byte[SHA512_BYTES];
            byte[] array8 = new byte[K_BYTE + SALT_SIZE_BYTES * 2 + SALT_SIZE_BYTES];
            Array.Copy(array6, 0, array8, 0, array6.Length);
            Array.Copy(array, 0, array8, K_BYTE, SALT_SIZE_BYTES * 2);
            Array.Copy(array5, 0, array8, K_BYTE + SALT_SIZE_BYTES * 2, SALT_SIZE_BYTES);
            HqcKeccakRandomGenerator hqcKeccakRandomGenerator = new HqcKeccakRandomGenerator(256);
            hqcKeccakRandomGenerator.SHAKE256_512_ds(array7, array8, array8.Length, new byte[1] {
                G_FCT_DOMAIN
            });
            long[] h = new long[N_BYTE_64];
            byte[] s = new byte[N_BYTE];
            ExtractPublicKeys(h, s, array);
            byte[] array9 = new byte[N_BYTE];
            byte[] array10 = new byte[N1N2_BYTE];
            long[] array11 = new long[N1N2_BYTE_64];
            Encrypt(array9, array11, h, s, array6, array7);
            Utils.FromLongArrayToByteArray(array10, array11);
            byte[] array12 = new byte[K_BYTE + N_BYTE + N1N2_BYTE];
            if (!Arrays.FixedTimeEquals(array3, array9))
                num = 1;
            if (!Arrays.FixedTimeEquals(array4, array10))
                num = 1;
            num--;
            for (int i = 0; i < K_BYTE; i++) {
                array12[i] = (byte)(((array6[i] & num) ^ (array2[i] & ~num)) & 255);
            }
            Array.Copy(array3, 0, array12, K_BYTE, N_BYTE);
            Array.Copy(array4, 0, array12, K_BYTE + N_BYTE, N1N2_BYTE);
            hqcKeccakRandomGenerator.SHAKE256_512_ds(ss, array12, array12.Length, new byte[1] {
                K_FCT_DOMAIN
            });
            return -num;
        }
        internal int GetSessionKeySize()
        {
            return SHA512_BYTES;
        }
        private void Encrypt(byte[] u, long[] v, long[] h, byte[] s, byte[] m, byte[] theta)
        {
            HqcKeccakRandomGenerator hqcKeccakRandomGenerator = new HqcKeccakRandomGenerator(256);
            hqcKeccakRandomGenerator.SeedExpanderInit(theta, SEED_SIZE);
            long[] array = new long[N_BYTE_64];
            long[] array2 = new long[N_BYTE_64];
            long[] array3 = new long[N_BYTE_64];
            GenerateRandomFixedWeight(array3, hqcKeccakRandomGenerator, wr);
            GenerateRandomFixedWeight(array, hqcKeccakRandomGenerator, we);
            GenerateRandomFixedWeight(array2, hqcKeccakRandomGenerator, wr);
            long[] array4 = new long[N_BYTE_64];
            gfCalculator.MultLongs(array4, array3, h);
            GF2PolynomialCalculator.AddLongs(array4, array4, array2);
            Utils.FromLongArrayToByteArray(u, array4);
            byte[] array5 = new byte[n1];
            long[] array6 = new long[N1N2_BYTE_64];
            long[] array7 = new long[N_BYTE_64];
            ReedSolomon.Encode(array5, m, K_BYTE * 8, n1, k, g, generatorPoly);
            ReedMuller.Encode(array6, array5, n1, mulParam);
            Array.Copy(array6, 0, array7, 0, array6.Length);
            long[] array8 = new long[N_BYTE_64];
            Utils.FromByteArrayToLongArray(array8, s);
            long[] array9 = new long[N_BYTE_64];
            gfCalculator.MultLongs(array9, array3, array8);
            GF2PolynomialCalculator.AddLongs(array9, array9, array7);
            GF2PolynomialCalculator.AddLongs(array9, array9, array);
            Utils.ResizeArray(v, n1n2, array9, n, N1N2_BYTE_64, N1N2_BYTE_64);
        }
        private int Decrypt(byte[] output, byte[] m, byte[] sigma, byte[] u, byte[] v, long[] y)
        {
            long[] array = new long[N_BYTE_64];
            Utils.FromByteArrayToLongArray(array, u);
            long[] array2 = new long[N1N2_BYTE_64];
            Utils.FromByteArrayToLongArray(array2, v);
            long[] array3 = new long[N_BYTE_64];
            Array.Copy(array2, 0, array3, 0, array2.Length);
            long[] array4 = new long[N_BYTE_64];
            gfCalculator.MultLongs(array4, y, array);
            GF2PolynomialCalculator.AddLongs(array4, array4, array3);
            byte[] array5 = new byte[n1];
            ReedMuller.Decode(array5, array4, n1, mulParam);
            ReedSolomon.Decode(m, array5, n1, fft, delta, k, g);
            Array.Copy(m, 0, output, 0, output.Length);
            return 0;
        }
        private void GenerateRandomFixedWeight(long[] output, HqcKeccakRandomGenerator random, int weight)
        {
            uint[] array = new uint[wr];
            byte[] array2 = new byte[wr * 4];
            int[] array3 = new int[wr];
            int[] array4 = new int[wr];
            long[] array5 = new long[wr];
            random.ExpandSeed(array2, 4 * weight);
            Pack.LE_To_UInt32(array2, 0, array, 0, array.Length);
            for (int i = 0; i < weight; i++) {
                array3[i] = (int)(i + ((long)array[i] & 4294967295) % (n - i));
            }
            for (int num = weight - 1; num >= 0; num--) {
                int num2 = 0;
                for (int j = num + 1; j < weight; j++) {
                    if (array3[j] == array3[num])
                        num2 |= 1;
                }
                int num3 = -num2;
                array3[num] = ((num3 & num) ^ (~num3 & array3[num]));
            }
            for (int k = 0; k < weight; k++) {
                array4[k] = (int)((uint)array3[k] >> 6);
                int num4 = array3[k] & 63;
                array5[k] = 1 << num4;
            }
            long num5 = 0;
            for (int l = 0; l < N_BYTE_64; l++) {
                num5 = 0;
                for (int m = 0; m < weight; m++) {
                    int num6 = l - array4[m];
                    long num7 = (int)(0 - (1 ^ ((uint)(num6 | -num6) >> 31)));
                    num5 |= (array5[m] & num7);
                }
                output[l] |= num5;
            }
        }
        private void GeneratePublicKeyH(long[] output, HqcKeccakRandomGenerator random)
        {
            byte[] array = new byte[N_BYTE];
            random.ExpandSeed(array, N_BYTE);
            long[] obj = new long[N_BYTE_64];
            Utils.FromByteArrayToLongArray(obj, array);
            obj[N_BYTE_64 - 1] &= Utils.BitMask((ulong)n, 64);
            Array.Copy(obj, 0, output, 0, output.Length);
        }
        private void ExtractPublicKeys(long[] h, byte[] s, byte[] pk)
        {
            byte[] array = new byte[SEED_SIZE];
            Array.Copy(pk, 0, array, 0, array.Length);
            HqcKeccakRandomGenerator hqcKeccakRandomGenerator = new HqcKeccakRandomGenerator(256);
            hqcKeccakRandomGenerator.SeedExpanderInit(array, array.Length);
            long[] array2 = new long[N_BYTE_64];
            GeneratePublicKeyH(array2, hqcKeccakRandomGenerator);
            Array.Copy(array2, 0, h, 0, h.Length);
            Array.Copy(pk, 40, s, 0, s.Length);
        }
        private void ExtractKeysFromSecretKeys(long[] y, byte[] sigma, byte[] pk, byte[] sk)
        {
            byte[] array = new byte[SEED_SIZE];
            Array.Copy(sk, 0, array, 0, array.Length);
            Array.Copy(sk, SEED_SIZE, sigma, 0, K_BYTE);
            HqcKeccakRandomGenerator hqcKeccakRandomGenerator = new HqcKeccakRandomGenerator(256);
            hqcKeccakRandomGenerator.SeedExpanderInit(array, array.Length);
            GenerateRandomFixedWeight(y, hqcKeccakRandomGenerator, w);
            Array.Copy(sk, SEED_SIZE + K_BYTE, pk, 0, pk.Length);
        }
        private static void ExtractCiphertexts(byte[] u, byte[] v, byte[] salt, byte[] ct)
        {
            Array.Copy(ct, 0, u, 0, u.Length);
            Array.Copy(ct, u.Length, v, 0, v.Length);
            Array.Copy(ct, u.Length + v.Length, salt, 0, salt.Length);
        }
    }
}