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

Poly

class Poly
namespace Org.BouncyCastle.Pqc.Crypto.Saber { internal class Poly { private const int KARATSUBA_N = 64; private readonly int N_SB; private readonly int N_SB_RES; private readonly int SABER_N; private readonly int SABER_L; private readonly SaberEngine engine; private readonly SaberUtilities utils; public Poly(SaberEngine engine) { this.engine = engine; SABER_L = engine.L; SABER_N = engine.N; N_SB = SABER_N >> 2; N_SB_RES = 2 * N_SB - 1; utils = engine.Utilities; } public void GenMatrix(short[][][] A, byte[] seed) { byte[] array = new byte[SABER_L * engine.PolyVecBytes]; engine.Symmetric.Prf(array, seed, engine.SeedBytes, array.Length); for (int i = 0; i < SABER_L; i++) { utils.BS2POLVECq(array, i * engine.PolyVecBytes, A[i]); } } public void GenSecret(short[][] s, byte[] seed) { byte[] array = new byte[SABER_L * engine.PolyCoinBytes]; engine.Symmetric.Prf(array, seed, engine.NoiseSeedBytes, array.Length); for (int i = 0; i < SABER_L; i++) { if (!engine.UsingEffectiveMasking) Cbd(s[i], array, i * engine.PolyCoinBytes); else { for (int j = 0; j < SABER_N / 4; j++) { s[i][4 * j] = (short)(((array[j + i * engine.PolyCoinBytes] & 3) ^ 2) - 2); s[i][4 * j + 1] = (short)((((array[j + i * engine.PolyCoinBytes] >> 2) & 3) ^ 2) - 2); s[i][4 * j + 2] = (short)((((array[j + i * engine.PolyCoinBytes] >> 4) & 3) ^ 2) - 2); s[i][4 * j + 3] = (short)((((array[j + i * engine.PolyCoinBytes] >> 6) & 3) ^ 2) - 2); } } } } private long LoadLittleEndian(byte[] x, int offset, int bytes) { long num = x[offset] & 255; for (int i = 1; i < bytes; i++) { num |= (long)(x[offset + i] & 255) << 8 * i; } return num; } private void Cbd(short[] s, byte[] buf, int offset) { int[] array = new int[4]; int[] array2 = new int[4]; if (engine.MU == 6) { for (int i = 0; i < SABER_N / 4; i++) { int num = (int)LoadLittleEndian(buf, offset + 3 * i, 3); int num2 = 0; for (int j = 0; j < 3; j++) { num2 += ((num >> j) & 2396745); } array[0] = (num2 & 7); array2[0] = ((num2 >> 3) & 7); array[1] = ((num2 >> 6) & 7); array2[1] = ((num2 >> 9) & 7); array[2] = ((num2 >> 12) & 7); array2[2] = ((num2 >> 15) & 7); array[3] = ((num2 >> 18) & 7); array2[3] = num2 >> 21; s[4 * i] = (short)(array[0] - array2[0]); s[4 * i + 1] = (short)(array[1] - array2[1]); s[4 * i + 2] = (short)(array[2] - array2[2]); s[4 * i + 3] = (short)(array[3] - array2[3]); } } else if (engine.MU == 8) { for (int i = 0; i < SABER_N / 4; i++) { int num3 = (int)LoadLittleEndian(buf, offset + 4 * i, 4); int num4 = 0; for (int j = 0; j < 4; j++) { num4 += ((num3 >> j) & 286331153); } array[0] = (num4 & 15); array2[0] = ((num4 >> 4) & 15); array[1] = ((num4 >> 8) & 15); array2[1] = ((num4 >> 12) & 15); array[2] = ((num4 >> 16) & 15); array2[2] = ((num4 >> 20) & 15); array[3] = ((num4 >> 24) & 15); array2[3] = num4 >> 28; s[4 * i] = (short)(array[0] - array2[0]); s[4 * i + 1] = (short)(array[1] - array2[1]); s[4 * i + 2] = (short)(array[2] - array2[2]); s[4 * i + 3] = (short)(array[3] - array2[3]); } } else if (engine.MU == 10) { for (int i = 0; i < SABER_N / 4; i++) { long num5 = LoadLittleEndian(buf, offset + 5 * i, 5); long num6 = 0; for (int j = 0; j < 5; j++) { num6 += ((num5 >> j) & 35468117025); } array[0] = (int)(num6 & 31); array2[0] = (int)((num6 >> 5) & 31); array[1] = (int)((num6 >> 10) & 31); array2[1] = (int)((num6 >> 15) & 31); array[2] = (int)((num6 >> 20) & 31); array2[2] = (int)((num6 >> 25) & 31); array[3] = (int)((num6 >> 30) & 31); array2[3] = (int)(num6 >> 35); s[4 * i] = (short)(array[0] - array2[0]); s[4 * i + 1] = (short)(array[1] - array2[1]); s[4 * i + 2] = (short)(array[2] - array2[2]); s[4 * i + 3] = (short)(array[3] - array2[3]); } } } private short OVERFLOWING_MUL(int x, int y) { return (short)(x * y); } private void karatsuba_simple(int[] a_1, int[] b_1, int[] result_final) { int[] array = new int[31]; int[] array2 = new int[31]; int[] array3 = new int[31]; int[] array4 = new int[63]; for (int i = 0; i < 16; i++) { int num = a_1[i]; int num2 = a_1[i + 16]; int num3 = a_1[i + 32]; int num4 = a_1[i + 48]; for (int j = 0; j < 16; j++) { int num5 = b_1[j]; int num6 = b_1[j + 16]; result_final[i + j] += OVERFLOWING_MUL(num, num5); result_final[i + j + 32] += OVERFLOWING_MUL(num2, num6); int num7 = num5 + num6; int num8 = num + num2; array[i + j] = (int)(array[i + j] + (long)num7 * (long)num8); num7 = b_1[j + 32]; num8 = b_1[j + 48]; result_final[i + j + 64] += OVERFLOWING_MUL(num7, num3); result_final[i + j + 96] += OVERFLOWING_MUL(num8, num4); int x = num3 + num4; int y = num7 + num8; array3[i + j] += OVERFLOWING_MUL(x, y); num5 += num7; num7 = num + num3; array4[i + j] += OVERFLOWING_MUL(num5, num7); num6 += num8; num8 = num2 + num4; array4[i + j + 32] += OVERFLOWING_MUL(num6, num8); num5 += num6; num7 += num8; array2[i + j] += OVERFLOWING_MUL(num5, num7); } } for (int i = 0; i < 31; i++) { array2[i] = array2[i] - array4[i] - array4[i + 32]; array[i] = array[i] - result_final[i] - result_final[i + 32]; array3[i] = array3[i] - result_final[i + 64] - result_final[i + 96]; } for (int i = 0; i < 31; i++) { array4[i + 16] += array2[i]; result_final[i + 16] += array[i]; result_final[i + 80] += array3[i]; } for (int i = 0; i < 63; i++) { array4[i] = array4[i] - result_final[i] - result_final[i + 64]; } for (int i = 0; i < 63; i++) { result_final[i + 32] += array4[i]; } } private void toom_cook_4way(short[] a1, short[] b1, short[] result) { int num = 43691; int num2 = 36409; int num3 = 61167; int[] array = new int[N_SB]; int[] array2 = new int[N_SB]; int[] array3 = new int[N_SB]; int[] array4 = new int[N_SB]; int[] array5 = new int[N_SB]; int[] array6 = new int[N_SB]; int[] array7 = new int[N_SB]; int[] array8 = new int[N_SB]; int[] array9 = new int[N_SB]; int[] array10 = new int[N_SB]; int[] array11 = new int[N_SB]; int[] array12 = new int[N_SB]; int[] array13 = new int[N_SB]; int[] array14 = new int[N_SB]; int[] array15 = new int[N_SB_RES]; int[] array16 = new int[N_SB_RES]; int[] array17 = new int[N_SB_RES]; int[] array18 = new int[N_SB_RES]; int[] array19 = new int[N_SB_RES]; int[] array20 = new int[N_SB_RES]; int[] array21 = new int[N_SB_RES]; for (int i = 0; i < N_SB; i++) { int num4 = a1[i]; int num5 = a1[i + N_SB]; int num6 = a1[i + N_SB * 2]; int num7 = a1[i + N_SB * 3]; int num8 = (short)(num4 + num6); int num9 = (short)(num5 + num7); int num10 = (short)(num8 + num9); int num11 = (short)(num8 - num9); array3[i] = num10; array4[i] = num11; num8 = (short)((num4 << 2) + num6 << 1); num9 = (short)((num5 << 2) + num7); num10 = (short)(num8 + num9); num11 = (short)(num8 - num9); array5[i] = num10; array6[i] = num11; num8 = (array2[i] = (short)((num7 << 3) + (num6 << 2) + (num5 << 1) + num4)); array7[i] = num4; array[i] = num7; } for (int i = 0; i < N_SB; i++) { int num4 = b1[i]; int num5 = b1[i + N_SB]; int num6 = b1[i + N_SB * 2]; int num7 = b1[i + N_SB * 3]; int num8 = num4 + num6; int num9 = num5 + num7; int num10 = num8 + num9; int num11 = num8 - num9; array10[i] = num10; array11[i] = num11; num8 = (num4 << 2) + num6 << 1; num9 = (num5 << 2) + num7; num10 = num8 + num9; num11 = num8 - num9; array12[i] = num10; array13[i] = num11; num8 = (array9[i] = (num7 << 3) + (num6 << 2) + (num5 << 1) + num4); array14[i] = num4; array8[i] = num7; } karatsuba_simple(array, array8, array15); karatsuba_simple(array2, array9, array16); karatsuba_simple(array3, array10, array17); karatsuba_simple(array4, array11, array18); karatsuba_simple(array5, array12, array19); karatsuba_simple(array6, array13, array20); karatsuba_simple(array7, array14, array21); for (int j = 0; j < N_SB_RES; j++) { int num4 = array15[j]; int num5 = array16[j]; int num6 = array17[j]; int num7 = array18[j]; int num8 = array19[j]; int num9 = array20[j]; int num10 = array21[j]; num5 += num8; num9 -= num8; num7 = (num7 & 65535) - (num6 & 65535) >> 1; num8 -= num4; num8 -= num10 << 6; num8 = (num8 << 1) + num9; num6 += num7; num5 = num5 - (num6 << 6) - num6; num6 -= num10; num6 -= num4; num5 += 45 * num6; num8 = ((num8 & 65535) - (num6 << 3)) * num >> 3; num9 += num5; num5 = ((num5 & 65535) + ((num7 & 65535) << 4)) * num2 >> 1; num7 = -(num7 + num5); num9 = (30 * (num5 & 65535) - (num9 & 65535)) * num3 >> 2; num6 -= num8; num5 -= num9; result[j] += (short)(num10 & 65535); result[j + 64] += (short)(num9 & 65535); result[j + 128] += (short)(num8 & 65535); result[j + 192] += (short)(num7 & 65535); result[j + 256] += (short)(num6 & 65535); result[j + 320] += (short)(num5 & 65535); result[j + 384] += (short)(num4 & 65535); } } private void poly_mul_acc(short[] a, short[] b, short[] res) { short[] array = new short[2 * SABER_N]; toom_cook_4way(a, b, array); for (int i = SABER_N; i < 2 * SABER_N; i++) { res[i - SABER_N] += (short)(array[i - SABER_N] - array[i]); } } public void MatrixVectorMul(short[][][] A, short[][] s, short[][] res, int transpose) { for (int i = 0; i < SABER_L; i++) { for (int j = 0; j < SABER_L; j++) { if (transpose == 1) poly_mul_acc(A[j][i], s[j], res[i]); else poly_mul_acc(A[i][j], s[j], res[i]); } } } public void InnerProd(short[][] b, short[][] s, short[] res) { for (int i = 0; i < SABER_L; i++) { poly_mul_acc(b[i], s[i], res); } } } }