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);
}
}
}