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

CmceEngine<GFImpl>

class CmceEngine<GFImpl> : ICmceEngine where GFImpl : struct, GF
using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Runtime.Intrinsics.X86; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using System; using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; namespace Org.BouncyCastle.Pqc.Crypto.Cmce { internal class CmceEngine<GFImpl> : ICmceEngine where GFImpl : struct, GF { private int SYS_N; private int SYS_T; private int GFBITS; private int IRR_BYTES; private int COND_BYTES; private int PK_NROWS; private int PK_NCOLS; private int PK_ROW_BYTES; private int SYND_BYTES; private int GFMASK; private int[] poly; private int defaultKeySize; private readonly GFImpl gf; private readonly Benes benes; private bool usePadding; private bool countErrorIndices; private bool usePivots; public int IrrBytes => IRR_BYTES; public int CondBytes => COND_BYTES; public int PrivateKeySize => COND_BYTES + IRR_BYTES + SYS_N / 8 + 40; public int PublicKeySize { get { if (!usePadding) return PK_NROWS * PK_NCOLS / 8; return PK_NROWS * (SYS_N / 8 - (PK_NROWS - 1) / 8); } } public int CipherTextSize => SYND_BYTES; public int DefaultSessionKeySize => defaultKeySize; internal CmceEngine(int m, int n, int t, int[] p, bool usePivots, int defaultKeySize) { this.usePivots = usePivots; SYS_N = n; SYS_T = t; GFBITS = m; poly = p; this.defaultKeySize = defaultKeySize; IRR_BYTES = SYS_T * 2; COND_BYTES = (1 << GFBITS - 4) * (2 * GFBITS - 1); PK_NROWS = SYS_T * GFBITS; PK_NCOLS = SYS_N - PK_NROWS; PK_ROW_BYTES = (PK_NCOLS + 7) / 8; SYND_BYTES = (PK_NROWS + 7) / 8; GFMASK = (1 << GFBITS) - 1; gf = default(GFImpl); if (GFBITS == 12) benes = new Benes12(SYS_N, SYS_T, GFBITS); else benes = new Benes13(SYS_N, SYS_T, GFBITS); usePadding = (SYS_T % 8 != 0); countErrorIndices = (1 << GFBITS > SYS_N); } public byte[] GeneratePublicKeyFromPrivateKey(byte[] sk) { byte[] array = new byte[PublicKeySize]; ushort[] pi = new ushort[1 << GFBITS]; ulong[] pivots = new ulong[1]; uint[] array2 = new uint[1 << GFBITS]; byte[] array3 = new byte[SYS_N / 8 + (1 << GFBITS) * 4]; int num = array3.Length - 32 - IRR_BYTES - (1 << GFBITS) * 4; IDigest digest = DigestUtilities.GetDigest(NistObjectIdentifiers.IdShake256); digest.Update(64); digest.BlockUpdate(sk, 0, 32); ((IXof)digest).OutputFinal(array3, 0, array3.Length); for (int i = 0; i < 1 << GFBITS; i++) { array2[i] = Utils.Load4(array3, num + i * 4); } PKGen(array, sk, array2, pi, pivots); return array; } public byte[] DecompressPrivateKey(byte[] sk) { byte[] array = new byte[PrivateKeySize]; Array.Copy(sk, 0, array, 0, sk.Length); byte[] array2 = new byte[SYS_N / 8 + (1 << GFBITS) * 4 + IRR_BYTES + 32]; IDigest digest = DigestUtilities.GetDigest(NistObjectIdentifiers.IdShake256); digest.Update(64); digest.BlockUpdate(sk, 0, 32); ((IXof)digest).OutputFinal(array2, 0, array2.Length); if (sk.Length <= 40) { ushort[] array3 = new ushort[SYS_T]; byte[] array4 = new byte[IRR_BYTES]; int num = array2.Length - 32 - IRR_BYTES; for (int i = 0; i < SYS_T; i++) { array3[i] = Utils.LoadGF(array2, num + i * 2, GFMASK); } GenerateIrrPoly(array3); for (int j = 0; j < SYS_T; j++) { Utils.StoreGF(array4, j * 2, array3[j]); } Array.Copy(array4, 0, array, 40, IRR_BYTES); } if (sk.Length <= 40 + IRR_BYTES) { uint[] array5 = new uint[1 << GFBITS]; ushort[] array6 = new ushort[1 << GFBITS]; int num2 = array2.Length - 32 - IRR_BYTES - (1 << GFBITS) * 4; for (int k = 0; k < 1 << GFBITS; k++) { array5[k] = Utils.Load4(array2, num2 + k * 4); } if (usePivots) { ulong[] pivots = new ulong[1]; PKGen(null, array, array5, array6, pivots); } else { long[] array7 = new long[1 << GFBITS]; for (int l = 0; l < 1 << GFBITS; l++) { array7[l] = (long)(((ulong)array5[l] << 31) | (uint)l); } Sort64(array7, 0, array7.Length); for (int m = 0; m < 1 << GFBITS; m++) { array6[m] = (ushort)(array7[m] & GFMASK); } } byte[] array8 = new byte[COND_BYTES]; ControlBitsFromPermutation(array8, array6, GFBITS, 1 << GFBITS); Array.Copy(array8, 0, array, IRR_BYTES + 40, array8.Length); } Array.Copy(array2, 0, array, PrivateKeySize - SYS_N / 8, SYS_N / 8); return array; } public void KemKeypair(byte[] pk, byte[] sk, SecureRandom random) { byte[] array = new byte[1]; byte[] array2 = new byte[32]; array[0] = 64; random.NextBytes(array2); byte[] array3 = new byte[SYS_N / 8 + (1 << GFBITS) * 4 + SYS_T * 2 + 32]; int num = 0; byte[] sourceArray = array2; ulong[] array4 = new ulong[1]; IDigest digest = DigestUtilities.GetDigest(NistObjectIdentifiers.IdShake256); ushort[] pi; int num2; while (true) { digest.BlockUpdate(array, 0, array.Length); digest.BlockUpdate(array2, 0, array2.Length); ((IXof)digest).OutputFinal(array3, 0, array3.Length); num2 = array3.Length - 32; array2 = Arrays.CopyOfRange(array3, num2, num2 + 32); Array.Copy(sourceArray, 0, sk, 0, 32); sourceArray = Arrays.CopyOfRange(array2, 0, 32); ushort[] array5 = new ushort[SYS_T]; int num3 = array3.Length - 32 - 2 * SYS_T; num2 = num3; for (int i = 0; i < SYS_T; i++) { array5[i] = Utils.LoadGF(array3, num3 + i * 2, GFMASK); } if (GenerateIrrPoly(array5) != -1) { num = 40; for (int j = 0; j < SYS_T; j++) { Utils.StoreGF(sk, num + j * 2, array5[j]); } uint[] array6 = new uint[1 << GFBITS]; num2 -= (1 << GFBITS) * 4; for (int k = 0; k < 1 << GFBITS; k++) { array6[k] = Utils.Load4(array3, num2 + k * 4); } pi = new ushort[1 << GFBITS]; if (PKGen(pk, sk, array6, pi, array4) != -1) break; } } byte[] array7 = new byte[COND_BYTES]; ControlBitsFromPermutation(array7, pi, GFBITS, 1 << GFBITS); Array.Copy(array7, 0, sk, IRR_BYTES + 40, array7.Length); num2 -= SYS_N / 8; Array.Copy(array3, num2, sk, sk.Length - SYS_N / 8, SYS_N / 8); if (!usePivots) Utils.Store8(sk, 32, 4294967295); else Utils.Store8(sk, 32, array4[0]); } private void Syndrome(byte[] cipher_text, byte[] pk, byte[] error_vector) { short[] array = new short[SYS_N / 8]; int num = 0; int num2 = PK_NROWS % 8; for (int i = 0; i < SYND_BYTES; i++) { cipher_text[i] = 0; } for (int i = 0; i < PK_NROWS; i++) { for (int j = 0; j < SYS_N / 8; j++) { array[j] = 0; } for (int j = 0; j < PK_ROW_BYTES; j++) { array[SYS_N / 8 - PK_ROW_BYTES + j] = pk[num + j]; } if (usePadding) { for (int j = SYS_N / 8 - 1; j >= SYS_N / 8 - PK_ROW_BYTES; j--) { array[j] = (short)((((array[j] & 255) << num2) | ((array[j - 1] & 255) >> 8 - num2)) & 255); } } array[i / 8] |= (short)(1 << i % 8); byte b = 0; for (int j = 0; j < SYS_N / 8; j++) { b = (byte)(b ^ (byte)(array[j] & error_vector[j])); } b = (byte)(b ^ (byte)(b >> 4)); b = (byte)(b ^ (byte)(b >> 2)); b = (byte)(b ^ (byte)(b >> 1)); b = (byte)(b & 1); cipher_text[i / 8] |= (byte)(b << i % 8); num += PK_ROW_BYTES; } } private void GenerateErrorVector(byte[] error_vector, SecureRandom random) { ushort[] array = new ushort[SYS_T * 2]; ushort[] array2 = new ushort[SYS_T]; byte[] array3 = new byte[SYS_T]; int num3; do { if (countErrorIndices) { byte[] array4 = new byte[SYS_T * 4]; random.NextBytes(array4); for (int i = 0; i < SYS_T * 2; i++) { array[i] = Utils.LoadGF(array4, i * 2, GFMASK); } int num = 0; for (int j = 0; j < SYS_T * 2; j++) { if (num >= SYS_T) break; if (array[j] < SYS_N) array2[num++] = array[j]; } if (num < SYS_T) goto IL_0026; } else { byte[] array4 = new byte[SYS_T * 2]; random.NextBytes(array4); for (int k = 0; k < SYS_T; k++) { array2[k] = Utils.LoadGF(array4, k * 2, GFMASK); } } num3 = 0; for (int l = 1; l < SYS_T; l++) { if (num3 == 1) break; for (int m = 0; m < l; m++) { if (array2[l] == array2[m]) { num3 = 1; break; } } } } while (num3 != 0); for (int n = 0; n < SYS_T; n++) { array3[n] = (byte)(1 << (array2[n] & 7)); } for (short num4 = 0; num4 < SYS_N / 8; num4 = (short)(num4 + 1)) { error_vector[num4] = 0; for (int num5 = 0; num5 < SYS_T; num5++) { short num6 = SameMask32(num4, (short)(array2[num5] >> 3)); num6 = (short)(num6 & 255); error_vector[num4] |= (byte)(array3[num5] & num6); } } } private void Encrypt(byte[] cipher_text, byte[] pk, byte[] error_vector, SecureRandom random) { GenerateErrorVector(error_vector, random); Syndrome(cipher_text, pk, error_vector); } public int KemEnc(byte[] cipher_text, byte[] key, byte[] pk, SecureRandom random) { byte[] array = new byte[SYS_N / 8]; int num = 0; if (usePadding) num = CheckPKPadding(pk); Encrypt(cipher_text, pk, array, random); IDigest digest = DigestUtilities.GetDigest(NistObjectIdentifiers.IdShake256); digest.Update(1); digest.BlockUpdate(array, 0, array.Length); digest.BlockUpdate(cipher_text, 0, cipher_text.Length); ((IXof)digest).OutputFinal(key, 0, key.Length); if (usePadding) { byte b = (byte)num; b = (byte)(b ^ 255); for (int i = 0; i < SYND_BYTES; i++) { cipher_text[i] &= b; } for (int i = 0; i < 32; i++) { key[i] &= b; } return num; } return 0; } public int KemDec(byte[] key, byte[] cipher_text, byte[] sk) { byte[] array = new byte[SYS_N / 8]; byte[] array2 = new byte[1 + SYS_N / 8 + SYND_BYTES]; int num = 0; if (usePadding) num = CheckCPadding(cipher_text); short num2 = (byte)Decrypt(array, sk, cipher_text); num2 = (short)(num2 - 1); num2 = (short)(num2 >> 8); num2 = (short)(num2 & 255); array2[0] = (byte)(num2 & 1); for (int i = 0; i < SYS_N / 8; i++) { array2[1 + i] = (byte)((~num2 & sk[i + 40 + IRR_BYTES + COND_BYTES]) | (num2 & array[i])); } for (int i = 0; i < SYND_BYTES; i++) { array2[1 + SYS_N / 8 + i] = cipher_text[i]; } DigestUtilities.GetDigest(NistObjectIdentifiers.IdShake256); IDigest digest = DigestUtilities.GetDigest(NistObjectIdentifiers.IdShake256); digest.BlockUpdate(array2, 0, array2.Length); ((IXof)digest).OutputFinal(key, 0, key.Length); if (usePadding) { byte b = (byte)num; for (int i = 0; i < key.Length; i++) { key[i] |= b; } return num; } return 0; } private int Decrypt(byte[] error_vector, byte[] sk, byte[] cipher_text) { ushort[] array = new ushort[SYS_T + 1]; ushort[] array2 = new ushort[SYS_N]; ushort[] array3 = new ushort[SYS_T * 2]; ushort[] array4 = new ushort[SYS_T * 2]; ushort[] array5 = new ushort[SYS_T + 1]; ushort[] array6 = new ushort[SYS_N]; byte[] array7 = new byte[SYS_N / 8]; for (int i = 0; i < SYND_BYTES; i++) { array7[i] = cipher_text[i]; } for (int j = SYND_BYTES; j < SYS_N / 8; j++) { array7[j] = 0; } for (int k = 0; k < SYS_T; k++) { array[k] = Utils.LoadGF(sk, 40 + k * 2, GFMASK); } array[SYS_T] = 1; benes.SupportGen(array2, sk); Synd(array3, array, array2, array7); BM(array5, array3); Root(array6, array5, array2); for (int l = 0; l < SYS_N / 8; l++) { error_vector[l] = 0; } int num = 0; for (int m = 0; m < SYS_N; m++) { ushort num2 = (ushort)(gf.GFIsZero(array6[m]) & 1); error_vector[m / 8] |= (byte)(num2 << m % 8); num += num2; } Synd(array4, array, array2, error_vector); int num3 = num; num3 ^= SYS_T; for (int n = 0; n < SYS_T * 2; n++) { num3 |= (array3[n] ^ array4[n]); } num3--; num3 >>= 15; num3 &= 1; return num3 ^ 1; } private static int Min(ushort a, int b) { if (a < b) return a; return b; } private void BM(ushort[] output, ushort[] s) { ushort num = 0; ushort num2 = 0; ushort[] array = new ushort[SYS_T + 1]; ushort[] array2 = new ushort[SYS_T + 1]; ushort[] array3 = new ushort[SYS_T + 1]; ushort num3 = 1; for (int i = 0; i < SYS_T + 1; i++) { array2[i] = (array3[i] = 0); } array3[1] = (array2[0] = 1); for (num = 0; num < 2 * SYS_T; num = (ushort)(num + 1)) { uint num4 = 0; GFImpl val; for (int j = 0; j <= Min(num, SYS_T); j++) { uint num5 = num4; val = gf; num4 = (num5 ^ val.GFMulExt(array2[j], s[num - j])); } val = gf; ushort num6 = val.GFReduce(num4); ushort num7 = num6; num7 = (ushort)(num7 - 1); num7 = (ushort)(num7 >> 15); num7 = (ushort)(num7 & 1); num7 = (ushort)(num7 - 1); ushort num8 = num; num8 = (ushort)(num8 - (ushort)(2 * num2)); num8 = (ushort)(num8 >> 15); num8 = (ushort)(num8 & 1); num8 = (ushort)(num8 - 1); num8 = (ushort)(num8 & num7); for (int k = 0; k <= SYS_T; k++) { array[k] = array2[k]; } val = gf; ushort left = val.GFFrac(num3, num6); for (int l = 0; l <= SYS_T; l++) { ref ushort reference = ref array2[l]; ushort num9 = reference; val = gf; reference = (ushort)(num9 ^ (ushort)(val.GFMul(left, array3[l]) & num7)); } num2 = (ushort)((num2 & ~num8) | ((num + 1 - num2) & num8)); for (int num10 = SYS_T - 1; num10 >= 0; num10--) { array3[num10 + 1] = (ushort)((array3[num10] & ~num8) | (array[num10] & num8)); } array3[0] = 0; num3 = (ushort)((num3 & ~num8) | (num6 & num8)); } for (int m = 0; m <= SYS_T; m++) { output[m] = array2[SYS_T - m]; } } private void Synd(ushort[] output, ushort[] f, ushort[] L, byte[] r) { ushort num = (ushort)(r[0] & 1); ushort num2 = L[0]; ushort input = Eval(f, num2); GFImpl val = gf; GFImpl val2 = gf; ushort left = output[0] = (ushort)(val.GFInv(val2.GFSq(input)) & -num); for (int i = 1; i < 2 * SYS_T; i++) { val = gf; left = (output[i] = val.GFMul(left, num2)); } for (int j = 1; j < SYS_N; j++) { ushort right = (ushort)((r[j / 8] >> j % 8) & 1); ushort num3 = L[j]; ushort input2 = Eval(f, num3); val = gf; val2 = gf; ushort left2 = val.GFInv(val2.GFSq(input2)); val = gf; ushort num4 = val.GFMul(left2, right); output[0] ^= num4; for (int k = 1; k < 2 * SYS_T; k++) { val = gf; num4 = val.GFMul(num4, num3); output[k] ^= num4; } } } private int MovColumns(byte[][] mat, ushort[] pi, ulong[] pivots) { ulong[] array = new ulong[64]; int[] array2 = new int[32]; ulong num = 1; byte[] array3 = new byte[9]; int num2 = PK_NROWS - 32; int num3 = num2 / 8; int num4 = num2 % 8; if (usePadding) { for (int i = 0; i < 32; i++) { for (int j = 0; j < 9; j++) { array3[j] = mat[num2 + i][num3 + j]; } for (int j = 0; j < 8; j++) { array3[j] = (byte)(((array3[j] & 255) >> num4) | (array3[j + 1] << 8 - num4)); } array[i] = Utils.Load8(array3, 0); } } else { for (int i = 0; i < 32; i++) { array[i] = Utils.Load8(mat[num2 + i], num3); } } pivots[0] = 0; for (int i = 0; i < 32; i++) { ulong num5 = array[i]; for (int j = i + 1; j < 32; j++) { num5 |= array[j]; } if (num5 == 0) return -1; int num6 = array2[i] = Ctz(num5); pivots[0] |= num << num6; for (int j = i + 1; j < 32; j++) { ulong num7 = (array[i] >> num6) & 1; num7--; array[i] ^= (array[j] & num7); } for (int j = i + 1; j < 32; j++) { ulong num7 = (array[j] >> num6) & 1; num7 = 0 - num7; array[j] ^= (array[i] & num7); } } for (int j = 0; j < 32; j++) { for (int k = j + 1; k < 64; k++) { ulong num8 = (ulong)(pi[num2 + j] ^ pi[num2 + k]); num8 &= SameMask64((ushort)k, (ushort)array2[j]); pi[num2 + j] ^= (ushort)num8; pi[num2 + k] ^= (ushort)num8; } } for (int i = 0; i < PK_NROWS; i++) { ulong num5; if (usePadding) { for (int k = 0; k < 9; k++) { array3[k] = mat[i][num3 + k]; } for (int k = 0; k < 8; k++) { array3[k] = (byte)(((array3[k] & 255) >> num4) | (array3[k + 1] << 8 - num4)); } num5 = Utils.Load8(array3, 0); } else num5 = Utils.Load8(mat[i], num3); for (int j = 0; j < 32; j++) { ulong num8 = num5 >> j; num8 ^= num5 >> array2[j]; num8 &= 1; num5 ^= num8 << array2[j]; num5 ^= num8 << j; } if (usePadding) { Utils.Store8(array3, 0, num5); mat[i][num3 + 8] = (byte)(((mat[i][num3 + 8] & 255) >> num4 << num4) | ((array3[7] & 255) >> 8 - num4)); mat[i][num3] = (byte)(((array3[0] & 255) << num4) | ((mat[i][num3] & 255) << 8 - num4 >> 8 - num4)); for (int k = 7; k >= 1; k--) { mat[i][num3 + k] = (byte)(((array3[k] & 255) << num4) | ((array3[k - 1] & 255) >> 8 - num4)); } } else Utils.Store8(mat[i], num3, num5); } return 0; } private static int Ctz(ulong input) { if (Org.BouncyCastle.Runtime.Intrinsics.X86.Bmi1.X64.IsEnabled) return (int)System.Runtime.Intrinsics.X86.Bmi1.X64.TrailingZeroCount(input); ulong num = 72340172838076673; ulong num2 = 0; ulong num3 = ~input; for (int i = 0; i < 8; i++) { num &= num3 >> i; num2 += num; } ulong num4 = num2 & 578721382704613384; num4 |= num4 >> 1; num4 |= num4 >> 2; ulong num5 = num2; num2 >>= 8; num5 += (num2 & num4); for (int j = 2; j < 8; j++) { num4 &= num4 >> 8; num2 >>= 8; num5 += (num2 & num4); } return (int)num5 & 255; } private static ulong SameMask64(ushort x, ushort y) { return (ulong)((long)(x ^ y) - 1 >> 63); } private static byte SameMask32(short x, short y) { return (byte)((x ^ y) - 1 >> 31); } private static void Layer(ushort[] p, byte[] output, int ptrIndex, int s, int n) { int num = 1 << s; int num2 = 0; for (int i = 0; i < n; i += num * 2) { for (int j = 0; j < num; j++) { int num3 = p[i + j] ^ p[i + j + num]; int num4 = (output[ptrIndex + (num2 >> 3)] >> (num2 & 7)) & 1; num4 = -num4; num3 &= num4; p[i + j] ^= (ushort)num3; p[i + j + num] ^= (ushort)num3; num2++; } } } private static void ControlBitsFromPermutation(byte[] output, ushort[] pi, long w, long n) { int[] temp = new int[(int)(2 * n)]; ushort[] array = new ushort[(int)n]; ushort num2; do { for (int i = 0; i < ((2 * w - 1) * n / 2 + 7) / 8; i++) { output[i] = 0; } CBRecursion(output, 0, 1, pi, 0, w, n, temp); for (int i = 0; i < n; i++) { array[i] = (ushort)i; } int num = 0; for (int i = 0; i < w; i++) { Layer(array, output, num, i, (int)n); num += (int)n >> 4; } for (int i = (int)(w - 2); i >= 0; i--) { Layer(array, output, num, i, (int)n); num += (int)n >> 4; } num2 = 0; for (int i = 0; i < n; i++) { num2 = (ushort)(num2 | (ushort)(pi[i] ^ array[i])); } } while (num2 != 0); } private static short GetQShort(int[] temp, int q_index) { int num = q_index / 2; if (q_index % 2 == 0) return (short)temp[num]; return (short)((temp[num] & 4294901760) >> 16); } private static void CBRecursion(byte[] output, long pos, long step, ushort[] pi, int qIndex, long w, long n, int[] temp) { if (w == 1) output[(int)(pos >> 3)] ^= (byte)(GetQShort(temp, qIndex) << (int)(pos & 7)); else { if (pi != null) { for (long num = 0; num < n; num++) { temp[(int)num] = (((pi[(int)num] ^ 1) << 16) | pi[(int)(num ^ 1)]); } } else { for (long num = 0; num < n; num++) { ushort num2 = (ushort)GetQShort(temp, (int)(qIndex + num)); ushort num3 = (ushort)GetQShort(temp, (int)(qIndex + (num ^ 1))); temp[(int)num] = (((num2 ^ 1) << 16) | num3); } } Sort32(temp, 0, (int)n); for (long num = 0; num < n; num++) { int num4 = temp[(int)num] & 65535; int num5 = num4; if (num < num5) num5 = (int)num; temp[(int)(n + num)] = ((num4 << 16) | num5); } for (long num = 0; num < n; num++) { temp[(int)num] = (int)((uint)(temp[(int)num] << 16) | num); } Sort32(temp, 0, (int)n); for (long num = 0; num < n; num++) { temp[(int)num] = (temp[(int)num] << 16) + (temp[(int)(n + num)] >> 16); } Sort32(temp, 0, (int)n); if (w <= 10) { for (long num = 0; num < n; num++) { temp[(int)(n + num)] = (((temp[(int)num] & 65535) << 10) | (temp[(int)(n + num)] & 1023)); } for (long num6 = 1; num6 < w - 1; num6++) { for (long num = 0; num < n; num++) { temp[(int)num] = (int)((uint)((temp[(int)(n + num)] & -1024) << 6) | num); } Sort32(temp, 0, (int)n); for (long num = 0; num < n; num++) { temp[(int)num] = ((temp[(int)num] << 20) | temp[(int)(n + num)]); } Sort32(temp, 0, (int)n); for (long num = 0; num < n; num++) { int num7 = temp[(int)num] & 1048575; int num8 = (temp[(int)num] & 1047552) | (temp[(int)(n + num)] & 1023); if (num7 < num8) num8 = num7; temp[(int)(n + num)] = num8; } } for (long num = 0; num < n; num++) { temp[(int)(n + num)] &= 1023; } } else { for (long num = 0; num < n; num++) { temp[(int)(n + num)] = ((temp[(int)num] << 16) | (temp[(int)(n + num)] & 65535)); } for (long num6 = 1; num6 < w - 1; num6++) { for (long num = 0; num < n; num++) { temp[(int)num] = (int)((uint)(temp[(int)(n + num)] & -65536) | num); } Sort32(temp, 0, (int)n); for (long num = 0; num < n; num++) { temp[(int)num] = ((temp[(int)num] << 16) | (temp[(int)(n + num)] & 65535)); } if (num6 < w - 2) { for (long num = 0; num < n; num++) { temp[(int)(n + num)] = ((temp[(int)num] & -65536) | (temp[(int)(n + num)] >> 16)); } Sort32(temp, (int)n, (int)(n * 2)); for (long num = 0; num < n; num++) { temp[(int)(n + num)] = ((temp[(int)(n + num)] << 16) | (temp[(int)num] & 65535)); } } Sort32(temp, 0, (int)n); for (long num = 0; num < n; num++) { int num9 = (temp[(int)(n + num)] & -65536) | (temp[(int)num] & 65535); if (num9 < temp[(int)(n + num)]) temp[(int)(n + num)] = num9; } } for (long num = 0; num < n; num++) { temp[(int)(n + num)] &= 65535; } } if (pi != null) { for (long num = 0; num < n; num++) { temp[(int)num] = (int)((pi[(int)num] << 16) + num); } } else { for (long num = 0; num < n; num++) { temp[(int)num] = (int)((GetQShort(temp, (int)(qIndex + num)) << 16) + num); } } Sort32(temp, 0, (int)n); for (long num10 = 0; num10 < n / 2; num10++) { long num11 = 2 * num10; int num12 = temp[(int)(n + num11)] & 1; int num13 = (int)(num11 + num12); int num14 = num13 ^ 1; output[(int)(pos >> 3)] ^= (byte)(num12 << (int)(pos & 7)); pos += step; temp[(int)(n + num11)] = ((temp[(int)num11] << 16) | num13); temp[(int)(n + num11 + 1)] = ((temp[(int)(num11 + 1)] << 16) | num14); } Sort32(temp, (int)n, (int)(n * 2)); pos += (2 * w - 3) * step * (n / 2); for (long num15 = 0; num15 < n / 2; num15++) { long num16 = 2 * num15; int num17 = temp[(int)(n + num16)] & 1; int num18 = (int)(num16 + num17); int num19 = num18 ^ 1; output[(int)(pos >> 3)] ^= (byte)(num17 << (int)(pos & 7)); pos += step; temp[(int)num16] = ((num18 << 16) | (temp[(int)(n + num16)] & 65535)); temp[(int)(num16 + 1)] = ((num19 << 16) | (temp[(int)(n + num16 + 1)] & 65535)); } Sort32(temp, 0, (int)n); pos -= (2 * w - 2) * step * (n / 2); short[] array = new short[(int)n * 4]; for (long num6 = 0; num6 < n * 2; num6++) { array[(int)(num6 * 2)] = (short)temp[(int)num6]; array[(int)(num6 * 2 + 1)] = (short)((temp[(int)num6] & 4294901760) >> 16); } for (long num10 = 0; num10 < n / 2; num10++) { array[(int)num10] = (short)((temp[(int)(2 * num10)] & 65535) >> 1); array[(int)(num10 + n / 2)] = (short)((temp[(int)(2 * num10 + 1)] & 65535) >> 1); } for (long num6 = 0; num6 < n / 2; num6++) { temp[(int)(n + n / 4 + num6)] = ((array[(int)(num6 * 2 + 1)] << 16) | array[(int)(num6 * 2)]); } CBRecursion(output, pos, step * 2, null, (int)(n + n / 4) * 2, w - 1, n / 2, temp); CBRecursion(output, pos + step, step * 2, null, (int)((n + n / 4) * 2 + n / 2), w - 1, n / 2, temp); } } private int PKGen(byte[] pk, byte[] sk, uint[] perm, ushort[] pi, ulong[] pivots) { ushort[] array = new ushort[SYS_T + 1]; array[SYS_T] = 1; for (int i = 0; i < SYS_T; i++) { array[i] = Utils.LoadGF(sk, 40 + i * 2, GFMASK); } long[] array2 = new long[1 << GFBITS]; for (int i = 0; i < 1 << GFBITS; i++) { array2[i] = (long)(((ulong)perm[i] << 31) | (uint)i); } Sort64(array2, 0, array2.Length); for (int i = 1; i < 1 << GFBITS; i++) { if (array2[i - 1] >> 31 == array2[i] >> 31) return -1; } ushort[] array3 = new ushort[SYS_N]; for (int i = 0; i < 1 << GFBITS; i++) { pi[i] = (ushort)(array2[i] & GFMASK); } for (int i = 0; i < SYS_N; i++) { array3[i] = Utils.Bitrev(pi[i], GFBITS); } ushort[] array4 = new ushort[SYS_N]; Root(array4, array, array3); GFImpl val; for (int i = 0; i < SYS_N; i++) { ushort[] array5 = array4; int num = i; val = gf; array5[num] = val.GFInv(array4[i]); } byte[][] array6 = new byte[PK_NROWS][]; for (int i = 0; i < PK_NROWS; i++) { array6[i] = new byte[SYS_N / 8]; } for (int i = 0; i < SYS_T; i++) { for (int j = 0; j < SYS_N; j += 8) { ulong lo = array4[j] | ((ulong)array4[j + 2] << 16) | ((ulong)array4[j + 4] << 32) | ((ulong)array4[j + 6] << 48); ulong hi = array4[j + 1] | ((ulong)array4[j + 3] << 16) | ((ulong)array4[j + 5] << 32) | ((ulong)array4[j + 7] << 48); Bits.BitPermuteStep2(ref hi, ref lo, 71777214294589695, 8); lo = Interleave.Transpose(lo); hi = Interleave.Transpose(hi); array6[i * GFBITS][j / 8] = (byte)lo; array6[i * GFBITS + 1][j / 8] = (byte)(lo >> 8); array6[i * GFBITS + 2][j / 8] = (byte)(lo >> 16); array6[i * GFBITS + 3][j / 8] = (byte)(lo >> 24); array6[i * GFBITS + 4][j / 8] = (byte)(lo >> 32); array6[i * GFBITS + 5][j / 8] = (byte)(lo >> 40); array6[i * GFBITS + 6][j / 8] = (byte)(lo >> 48); array6[i * GFBITS + 7][j / 8] = (byte)(lo >> 56); array6[i * GFBITS + 8][j / 8] = (byte)hi; array6[i * GFBITS + 9][j / 8] = (byte)(hi >> 8); array6[i * GFBITS + 10][j / 8] = (byte)(hi >> 16); array6[i * GFBITS + 11][j / 8] = (byte)(hi >> 24); if (GFBITS > 12) array6[i * GFBITS + 12][j / 8] = (byte)(hi >> 32); } for (int j = 0; j < SYS_N; j++) { ushort[] array7 = array4; int num2 = j; val = gf; array7[num2] = val.GFMul(array4[j], array3[j]); } } for (int k = 0; k < PK_NROWS; k++) { int i = k >> 3; int j = k & 7; if (usePivots && k == PK_NROWS - 32 && MovColumns(array6, pi, pivots) != 0) return -1; byte[] array8 = array6[k]; Vector<byte> vector; for (int l = k + 1; l < PK_NROWS; l++) { byte[] array9 = array6[l]; byte b = (byte)(array8[i] ^ array9[i]); b = (byte)(b >> j); b = (byte)(b & 1); int m = 0; if (Vector.IsHardwareAccelerated) { Vector<byte> right = new Vector<byte>((byte)(-b)); for (int num3 = SYS_N / 8 - Vector<byte>.Count; m <= num3; m += Vector<byte>.Count) { Vector<byte> left = new Vector<byte>(array9, m); Vector<byte> right2 = new Vector<byte>(array8, m); vector = ((left & right) ^ right2); vector.CopyTo(array8, m); } } ulong num4 = (ulong)(-b); for (int num5 = SYS_N / 8 - 8; m <= num5; m += 8) { ulong num6 = MemoryMarshal.Read<ulong>((ReadOnlySpan<byte>)MemoryExtensions.AsSpan<byte>(array9, m)); ulong value = MemoryMarshal.Read<ulong>((ReadOnlySpan<byte>)MemoryExtensions.AsSpan<byte>(array8, m)); value ^= (num6 & num4); MemoryMarshal.Write<ulong>(MemoryExtensions.AsSpan<byte>(array8, m), ref value); } byte b2 = (byte)(-b); for (; m < SYS_N / 8; m++) { array8[m] ^= (byte)(array9[m] & b2); } } if (((array8[i] >> j) & 1) == 0) return -1; for (int l = 0; l < PK_NROWS; l++) { if (l != k) { byte[] array10 = array6[l]; byte b3 = (byte)(array10[i] >> j); b3 = (byte)(b3 & 1); int n = 0; if (Vector.IsHardwareAccelerated) { Vector<byte> right3 = new Vector<byte>((byte)(-b3)); for (int num7 = SYS_N / 8 - Vector<byte>.Count; n <= num7; n += Vector<byte>.Count) { Vector<byte> left2 = new Vector<byte>(array8, n); Vector<byte> right4 = new Vector<byte>(array10, n); vector = ((left2 & right3) ^ right4); vector.CopyTo(array10, n); } } ulong num8 = (ulong)(-b3); for (int num9 = SYS_N / 8 - 8; n <= num9; n += 8) { ulong num10 = MemoryMarshal.Read<ulong>((ReadOnlySpan<byte>)MemoryExtensions.AsSpan<byte>(array8, n)); ulong value2 = MemoryMarshal.Read<ulong>((ReadOnlySpan<byte>)MemoryExtensions.AsSpan<byte>(array10, n)); value2 ^= (num10 & num8); MemoryMarshal.Write<ulong>(MemoryExtensions.AsSpan<byte>(array10, n), ref value2); } byte b4 = (byte)(-b3); for (; n < SYS_N / 8; n++) { array10[n] ^= (byte)(array8[n] & b4); } } } } if (pk != null) { if (usePadding) { int num11 = (PK_NROWS - 1) / 8; int num12 = SYS_N / 8; int num13 = 0; int num14 = PK_NROWS % 8; if (num14 == 0) { int num15 = num12 - num11; for (int i = 0; i < PK_NROWS; i++) { Array.Copy(array6[i], num11, pk, num13, num15); num13 += num15; } } else { for (int i = 0; i < PK_NROWS; i++) { byte[] bs = array6[i]; ulong num16 = Pack.LE_To_UInt64(bs, num11); int j; for (j = num11 + 8; j < num12 - 8; j += 8) { ulong num17 = Pack.LE_To_UInt64(bs, j); Pack.UInt64_To_LE((num16 >> num14) | (num17 << 64 - num14), pk, num13); num13 += 8; num16 = num17; } int num18 = num12 - j; ulong num19 = Pack.LE_To_UInt64_Low(bs, j, num18); Pack.UInt64_To_LE((num16 >> num14) | (num19 << 64 - num14), pk, num13); num13 += 8; Pack.UInt64_To_LE_Low(num19 >> num14, pk, num13, num18); num13 += num18; } } } else { int num20 = (SYS_N - PK_NROWS + 7) / 8; for (int i = 0; i < PK_NROWS; i++) { Array.Copy(array6[i], PK_NROWS / 8, pk, num20 * i, num20); } } } return 0; } private ushort Eval(ushort[] f, ushort a) { ushort num = f[SYS_T]; for (int num2 = SYS_T - 1; num2 >= 0; num2--) { num = gf.GFMul(num, a); num = (ushort)(num ^ f[num2]); } return num; } private void Root(ushort[] output, ushort[] f, ushort[] L) { for (int i = 0; i < SYS_N; i++) { output[i] = Eval(f, L[i]); } } private int GenerateIrrPoly(ushort[] field) { ushort[][] array = new ushort[SYS_T + 1][]; array[0] = new ushort[SYS_T]; array[0][0] = 1; array[1] = new ushort[SYS_T]; Array.Copy(field, 0, array[1], 0, SYS_T); uint[] temp = new uint[SYS_T * 2 - 1]; int i; GFImpl val; for (i = 2; i < SYS_T; i += 2) { array[i] = new ushort[SYS_T]; val = gf; val.GFSqrPoly(SYS_T, poly, array[i], array[i >> 1], temp); array[i + 1] = new ushort[SYS_T]; val = gf; val.GFMulPoly(SYS_T, poly, array[i + 1], array[i], field, temp); } if (i == SYS_T) { array[i] = new ushort[SYS_T]; val = gf; val.GFSqrPoly(SYS_T, poly, array[i], array[i >> 1], temp); } for (int j = 0; j < SYS_T; j++) { for (int k = j + 1; k < SYS_T; k++) { val = gf; ushort num = val.GFIsZero(array[j][j]); for (int l = j; l < SYS_T + 1; l++) { array[l][j] ^= (ushort)(array[l][k] & num); } } if (array[j][j] == 0) return -1; val = gf; ushort right = val.GFInv(array[j][j]); for (int m = j; m < SYS_T + 1; m++) { ushort[] obj = array[m]; int num2 = j; val = gf; obj[num2] = val.GFMul(array[m][j], right); } for (int n = 0; n < SYS_T; n++) { if (n != j) { ushort right2 = array[j][n]; for (int num3 = j; num3 <= SYS_T; num3++) { ref ushort reference = ref array[num3][n]; ushort num4 = reference; val = gf; reference = (ushort)(num4 ^ val.GFMul(array[num3][j], right2)); } } } } Array.Copy(array[SYS_T], field, SYS_T); return 0; } private int CheckPKPadding(byte[] pk) { byte b = 0; for (int i = 0; i < PK_NROWS; i++) { b = (byte)(b | pk[i * PK_ROW_BYTES + PK_ROW_BYTES - 1]); } b = (byte)((b & 255) >> PK_NCOLS % 8); b = (byte)(b - 1); b = (byte)((b & 255) >> 7); return b - 1; } private int CheckCPadding(byte[] c) { return (byte)(((byte)((byte)((c[SYND_BYTES - 1] & 255) >> PK_NROWS % 8) - 1) & 255) >> 7) - 1; } private static void Sort32(int[] temp, int from, int to) { int num = to - from; if (num >= 2) { int i; for (i = 1; i < num - i; i += i) { } for (int num2 = i; num2 > 0; num2 >>= 1) { for (int j = 0; j < num - num2; j++) { if ((j & num2) == 0) { int num3 = temp[from + j + num2] ^ temp[from + j]; int num4 = temp[from + j + num2] - temp[from + j]; num4 ^= (num3 & (num4 ^ temp[from + j + num2])); num4 >>= 31; num4 &= num3; temp[from + j] ^= num4; temp[from + j + num2] ^= num4; } } for (int num5 = i; num5 > num2; num5 >>= 1) { for (int k = 0; k < num - num5; k++) { if ((k & num2) == 0) { int num6 = temp[from + k + num2]; for (int num7 = num5; num7 > num2; num7 >>= 1) { int num8 = temp[from + k + num7] ^ num6; int num9 = temp[from + k + num7] - num6; num9 ^= (num8 & (num9 ^ temp[from + k + num7])); num9 >>= 31; num9 &= num8; num6 ^= num9; temp[from + k + num7] ^= num9; } temp[from + k + num2] = num6; } } } } } } private static void Sort64(long[] temp, int from, int to) { int num = to - from; if (num >= 2) { int i; for (i = 1; i < num - i; i += i) { } for (int num2 = i; num2 > 0; num2 >>= 1) { for (int j = 0; j < num - num2; j++) { if ((j & num2) == 0) { long num3 = temp[from + j + num2] - temp[from + j]; num3 >>= 63; num3 &= (temp[from + j] ^ temp[from + j + num2]); temp[from + j] ^= num3; temp[from + j + num2] ^= num3; } } for (int num4 = i; num4 > num2; num4 >>= 1) { for (int k = 0; k < num - num4; k++) { if ((k & num2) == 0) { long num5 = temp[from + k + num2]; for (int num6 = num4; num6 > num2; num6 >>= 1) { long num7 = temp[from + k + num6] - num5; num7 >>= 63; num7 &= (num5 ^ temp[from + k + num6]); num5 ^= num7; temp[from + k + num6] ^= num7; } temp[from + k + num2] = num5; } } } } } } } }