<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />

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 H_FCT_DOMAIN = 4; private byte K_FCT_DOMAIN = 5; 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]; HqcKeccakRandomGenerator hqcKeccakRandomGenerator = new HqcKeccakRandomGenerator(256); hqcKeccakRandomGenerator.RandomGeneratorInit(seed, null, seed.Length, 0); hqcKeccakRandomGenerator.Squeeze(array, 40); HqcKeccakRandomGenerator hqcKeccakRandomGenerator2 = new HqcKeccakRandomGenerator(256); hqcKeccakRandomGenerator2.SeedExpanderInit(array, array.Length); long[] array2 = new long[N_BYTE_64]; long[] array3 = new long[N_BYTE_64]; GenerateRandomFixedWeight(array2, hqcKeccakRandomGenerator2, w); GenerateRandomFixedWeight(array3, hqcKeccakRandomGenerator2, w); byte[] array4 = new byte[SEED_SIZE]; hqcKeccakRandomGenerator.Squeeze(array4, 40); HqcKeccakRandomGenerator hqcKeccakRandomGenerator3 = new HqcKeccakRandomGenerator(256); hqcKeccakRandomGenerator3.SeedExpanderInit(array4, array4.Length); long[] array5 = new long[N_BYTE_64]; GeneratePublicKeyH(array5, hqcKeccakRandomGenerator3); long[] array6 = new long[N_BYTE_64]; gfCalculator.MultLongs(array6, array3, array5); GF2PolynomialCalculator.AddLongs(array6, array6, array2); byte[] array7 = new byte[N_BYTE]; Utils.FromLongArrayToByteArray(array7, array6); byte[] array8 = Arrays.Concatenate(array4, array7); byte[] array9 = Arrays.Concatenate(array, array8); Array.Copy(array8, 0, pk, 0, array8.Length); Array.Copy(array9, 0, sk, 0, array9.Length); } public void Encaps(byte[] u, byte[] v, byte[] K, byte[] d, 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[SEED_SIZE]; hqcKeccakRandomGenerator.Squeeze(output2, 40); hqcKeccakRandomGenerator.Squeeze(array, K_BYTE); byte[] array2 = new byte[SHA512_BYTES]; byte[] array3 = new byte[K_BYTE + SEED_SIZE + SALT_SIZE_BYTES]; hqcKeccakRandomGenerator.Squeeze(salt, SALT_SIZE_BYTES); Array.Copy(array, 0, array3, 0, array.Length); Array.Copy(pk, 0, array3, K_BYTE, SEED_SIZE); Array.Copy(salt, 0, array3, K_BYTE + SEED_SIZE, 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); hqcKeccakRandomGenerator2.SHAKE256_512_ds(d, array, array.Length, new byte[1] { H_FCT_DOMAIN }); byte[] array5 = new byte[K_BYTE + N_BYTE + N1N2_BYTE]; array5 = Arrays.Concatenate(array, u); array5 = Arrays.Concatenate(array5, v); hqcKeccakRandomGenerator2.SHAKE256_512_ds(K, array5, array5.Length, new byte[1] { K_FCT_DOMAIN }); } public void Decaps(byte[] ss, byte[] ct, byte[] sk) { long[] x = new long[N_BYTE_64]; long[] y = new long[N_BYTE_64]; byte[] array = new byte[40 + N_BYTE]; ExtractKeysFromSecretKeys(x, y, array, sk); byte[] array2 = new byte[N_BYTE]; byte[] array3 = new byte[N1N2_BYTE]; byte[] array4 = new byte[SHA512_BYTES]; byte[] array5 = new byte[SALT_SIZE_BYTES]; ExtractCiphertexts(array2, array3, array4, array5, ct); byte[] array6 = new byte[k]; Decrypt(array6, array6, array2, array3, y); byte[] array7 = new byte[SHA512_BYTES]; byte[] array8 = new byte[K_BYTE + SALT_SIZE_BYTES + SEED_SIZE]; Array.Copy(array6, 0, array8, 0, array6.Length); Array.Copy(array, 0, array8, K_BYTE, SEED_SIZE); Array.Copy(array5, 0, array8, K_BYTE + SEED_SIZE, 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[SHA512_BYTES]; hqcKeccakRandomGenerator.SHAKE256_512_ds(array12, array6, array6.Length, new byte[1] { H_FCT_DOMAIN }); byte[] array13 = new byte[K_BYTE + N_BYTE + N1N2_BYTE]; array13 = Arrays.Concatenate(array6, array2); array13 = Arrays.Concatenate(array13, array3); hqcKeccakRandomGenerator.SHAKE256_512_ds(ss, array13, array13.Length, new byte[1] { K_FCT_DOMAIN }); int num = 1; if (!Arrays.AreEqual(array2, array9)) num = 0; if (!Arrays.AreEqual(array3, array10)) num = 0; if (!Arrays.AreEqual(array4, array12)) num = 0; if (num == 0) { for (int i = 0; i < GetSessionKeySize(); i++) { ss[i] = 0; } } } 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(array2, hqcKeccakRandomGenerator, wr); GenerateRandomFixedWeight(array3, hqcKeccakRandomGenerator, wr); GenerateRandomFixedWeight(array, hqcKeccakRandomGenerator, we); 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 void Decrypt(byte[] output, byte[] m, 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); } 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[] x, long[] y, byte[] pk, byte[] sk) { byte[] array = new byte[SEED_SIZE]; Array.Copy(sk, 0, array, 0, array.Length); HqcKeccakRandomGenerator hqcKeccakRandomGenerator = new HqcKeccakRandomGenerator(256); hqcKeccakRandomGenerator.SeedExpanderInit(array, array.Length); GenerateRandomFixedWeight(x, hqcKeccakRandomGenerator, w); GenerateRandomFixedWeight(y, hqcKeccakRandomGenerator, w); Array.Copy(sk, SEED_SIZE, pk, 0, pk.Length); } private static void ExtractCiphertexts(byte[] u, byte[] v, byte[] d, 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, d, 0, d.Length); Array.Copy(ct, u.Length + v.Length + d.Length, salt, 0, salt.Length); } } }