<PackageReference Include="BouncyCastle.Cryptography" Version="2.7.0-beta.98" />

BikeEngine

sealed class BikeEngine
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using System; using System.Numerics; namespace Org.BouncyCastle.Pqc.Crypto.Bike { internal sealed class BikeEngine { private readonly int r; private readonly int w; private readonly int hw; private readonly int t; private readonly int nbIter; private readonly int tau; private readonly BikeRing bikeRing; private readonly int L_BYTE; private readonly int R_BYTE; private readonly int R2_UINT; private readonly int R_ULONG; private readonly int R2_ULONG; internal int SessionKeySize => L_BYTE; internal BikeEngine(int r, int w, int t, int l, int nbIter, int tau) { this.r = r; this.w = w; this.t = t; this.nbIter = nbIter; this.tau = tau; hw = this.w / 2; L_BYTE = l / 8; R_BYTE = r + 7 >> 3; R2_UINT = 2 * r + 31 >> 5; R_ULONG = r + 63 >> 6; R2_ULONG = 2 * r + 63 >> 6; bikeRing = new BikeRing(r); } private ulong[] FunctionH(byte[] seed) { IXof xof = new ShakeDigest(256); xof.BlockUpdate(seed, 0, seed.Length); ulong[] obj = new ulong[2 * R_ULONG]; BikeUtilities.GenerateRandomUlongs(obj, 2 * r, t, xof); return obj; } private unsafe void FunctionL(ulong[] e01, byte[] c1, int c1Off) { Span<byte> output = new Span<byte>(stackalloc byte[48], 48); Sha3Digest.CalculateDigest(e01, 16 * R_BYTE, output, 384); output.Slice(0, L_BYTE).CopyTo(c1.AsSpan(c1Off)); } private unsafe void FunctionK(byte[] m, byte[] c01, byte[] result) { Span<byte> output = new Span<byte>(stackalloc byte[48], 48); Sha3Digest sha3Digest = new Sha3Digest(384); sha3Digest.BlockUpdate(m); sha3Digest.BlockUpdate(c01); sha3Digest.DoFinal(output); output.Slice(0, L_BYTE).CopyTo(result); } private unsafe void FunctionK(byte[] m, byte[] c0, byte[] c1, byte[] result) { Span<byte> output = new Span<byte>(stackalloc byte[48], 48); Sha3Digest sha3Digest = new Sha3Digest(384); sha3Digest.BlockUpdate(m); sha3Digest.BlockUpdate(c0); sha3Digest.BlockUpdate(c1); sha3Digest.DoFinal(output); output.Slice(0, L_BYTE).CopyTo(result); } internal unsafe void GenKeyPair(byte[] h0, byte[] h1, byte[] sigma, byte[] h, SecureRandom random) { Span<byte> buffer = new Span<byte>(stackalloc byte[64], 64); random.NextBytes(buffer); IXof xof = new ShakeDigest(256); xof.BlockUpdate(buffer.Slice(0, L_BYTE)); ulong[] array = bikeRing.Create(); ulong[] array2 = bikeRing.Create(); BikeUtilities.GenerateRandomUlongs(array, r, hw, xof); BikeUtilities.GenerateRandomUlongs(array2, r, hw, xof); bikeRing.EncodeBytes(array, h0); bikeRing.EncodeBytes(array2, h1); bikeRing.Inv(array, array); bikeRing.Multiply(array, array2, array); bikeRing.EncodeBytes(array, h); Span<byte> output = sigma.AsSpan(); int l_BYTE = L_BYTE; output.CopyFrom(buffer.Slice(l_BYTE, buffer.Length - l_BYTE)); } internal void Encaps(byte[] c01, byte[] k, byte[] h, SecureRandom random) { byte[] array = new byte[L_BYTE]; random.NextBytes(array); ulong[] array2 = FunctionH(array); AlignE01From1To64(array2); ulong[] array3 = bikeRing.Create(); bikeRing.DecodeBytes(h, array3); bikeRing.Multiply(array3, 0, array2, R_ULONG, array3); bikeRing.Add(array3, array2, array3); bikeRing.EncodeBytes(array3, c01); AlignE01From64To8(array2); FunctionL(array2, c01, R_BYTE); Bytes.XorTo(L_BYTE, array, 0, c01, R_BYTE); FunctionK(array, c01, k); } internal void Decaps(byte[] k, byte[] h0, byte[] h1, byte[] sigma, byte[] c0, byte[] c1) { int[] array = new int[hw]; int[] array2 = new int[hw]; ConvertToCompact(array, h0); ConvertToCompact(array2, h1); byte[] s = ComputeSyndrome(c0, h0); byte[] input = BGFDecoder(s, array, array2); ulong[] array3 = new ulong[2 * R_ULONG]; BikeUtilities.FromBitsToUlongs(array3, input, 0, 2 * r); AlignE01From1To64(array3); AlignE01From64To8(array3); byte[] array4 = new byte[L_BYTE]; FunctionL(array3, array4, 0); Bytes.XorTo(L_BYTE, c1, array4); AlignE01From8To1(array3); ulong[] b = FunctionH(array4); if (Arrays.AreEqual(array3, 0, R2_ULONG, b, 0, R2_ULONG)) FunctionK(array4, c0, c1, k); else FunctionK(sigma, c0, c1, k); } private byte[] ComputeSyndrome(byte[] c0, byte[] h0) { ulong[] array = bikeRing.Create(); ulong[] array2 = bikeRing.Create(); bikeRing.DecodeBytes(c0, array); bikeRing.DecodeBytes(h0, array2); bikeRing.Multiply(array, array2, array); return bikeRing.EncodeBitsTransposed(array); } private byte[] BGFDecoder(byte[] s, int[] h0Compact, int[] h1Compact) { byte[] array = new byte[2 * r]; int[] columnFromCompactVersion = GetColumnFromCompactVersion(h0Compact); int[] columnFromCompactVersion2 = GetColumnFromCompactVersion(h1Compact); uint[] array2 = new uint[R2_UINT]; byte[] ctrs = new byte[r]; uint[] array3 = new uint[R2_UINT]; int num = Threshold(BikeUtilities.GetHammingWeight(s), r); BFIter(s, array, num, h0Compact, h1Compact, columnFromCompactVersion, columnFromCompactVersion2, array2, array3, ctrs); BFMaskedIter(s, array, array2, (hw + 3) / 2, h0Compact, h1Compact, columnFromCompactVersion, columnFromCompactVersion2); BFMaskedIter(s, array, array3, (hw + 3) / 2, h0Compact, h1Compact, columnFromCompactVersion, columnFromCompactVersion2); for (int i = 1; i < nbIter; i++) { Array.Clear(array2, 0, array2.Length); int num2 = Threshold(BikeUtilities.GetHammingWeight(s), r); BFIter2(s, array, num2, h0Compact, h1Compact, columnFromCompactVersion, columnFromCompactVersion2, array2, ctrs); } if (BikeUtilities.GetHammingWeight(s) == 0) return array; return null; } private void BFIter(byte[] s, byte[] e, int T, int[] h0Compact, int[] h1Compact, int[] h0CompactCol, int[] h1CompactCol, uint[] black, uint[] gray, byte[] ctrs) { CtrAll(h0CompactCol, s, ctrs); int num = (ctrs[0] - T >> 31) + 1; int num2 = (ctrs[0] - (T - tau) >> 31) + 1; e[0] ^= (byte)num; black[0] |= (uint)num; gray[0] |= (uint)num2; for (int i = 1; i < r; i++) { int num3 = (ctrs[i] - T >> 31) + 1; int num4 = (ctrs[i] - (T - tau) >> 31) + 1; e[r - i] ^= (byte)num3; black[i >> 5] |= (uint)(num3 << i); gray[i >> 5] |= (uint)(num4 << i); } CtrAll(h1CompactCol, s, ctrs); int num5 = (ctrs[0] - T >> 31) + 1; int num6 = (ctrs[0] - (T - tau) >> 31) + 1; e[r] ^= (byte)num5; black[r >> 5] |= (uint)(num5 << r); gray[r >> 5] |= (uint)(num6 << r); for (int j = 1; j < r; j++) { int num7 = (ctrs[j] - T >> 31) + 1; int num8 = (ctrs[j] - (T - tau) >> 31) + 1; e[r + r - j] ^= (byte)num7; black[r + j >> 5] |= (uint)(num7 << r + j); gray[r + j >> 5] |= (uint)(num8 << r + j); } for (int k = 0; k < black.Length; k++) { int num10; for (uint num9 = black[k]; num9 != 0; num9 = (uint)((int)num9 ^ (1 << num10))) { num10 = Integers.NumberOfTrailingZeros((int)num9); RecomputeSyndrome(s, (k << 5) + num10, h0Compact, h1Compact); } } } private void BFIter2(byte[] s, byte[] e, int T, int[] h0Compact, int[] h1Compact, int[] h0CompactCol, int[] h1CompactCol, uint[] black, byte[] ctrs) { CtrAll(h0CompactCol, s, ctrs); int num = (ctrs[0] - T >> 31) + 1; e[0] ^= (byte)num; black[0] |= (uint)num; for (int i = 1; i < r; i++) { int num2 = (ctrs[i] - T >> 31) + 1; e[r - i] ^= (byte)num2; black[i >> 5] |= (uint)(num2 << i); } CtrAll(h1CompactCol, s, ctrs); int num3 = (ctrs[0] - T >> 31) + 1; e[r] ^= (byte)num3; black[r >> 5] |= (uint)(num3 << r); for (int j = 1; j < r; j++) { int num4 = (ctrs[j] - T >> 31) + 1; e[r + r - j] ^= (byte)num4; black[r + j >> 5] |= (uint)(num4 << r + j); } for (int k = 0; k < black.Length; k++) { int num6; for (uint num5 = black[k]; num5 != 0; num5 = (uint)((int)num5 ^ (1 << num6))) { num6 = Integers.NumberOfTrailingZeros((int)num5); RecomputeSyndrome(s, (k << 5) + num6, h0Compact, h1Compact); } } } private void BFMaskedIter(byte[] s, byte[] e, uint[] mask, int T, int[] h0Compact, int[] h1Compact, int[] h0CompactCol, int[] h1CompactCol) { uint[] array = new uint[R2_UINT]; for (int i = 0; i < r; i++) { if (((int)mask[i >> 5] & (1 << i)) != 0) { int num = (Ctr(h0CompactCol, s, i) - T >> 31) + 1; int num2 = -i; num2 += ((num2 >> 31) & r); e[num2] ^= (byte)num; array[i >> 5] |= (uint)(num << i); } } for (int j = 0; j < r; j++) { if (((int)mask[r + j >> 5] & (1 << r + j)) != 0) { int num3 = (Ctr(h1CompactCol, s, j) - T >> 31) + 1; int num4 = -j; num4 += ((num4 >> 31) & r); e[r + num4] ^= (byte)num3; array[r + j >> 5] |= (uint)(num3 << r + j); } } for (int k = 0; k < array.Length; k++) { int num6; for (uint num5 = array[k]; num5 != 0; num5 = (uint)((int)num5 ^ (1 << num6))) { num6 = Integers.NumberOfTrailingZeros((int)num5); RecomputeSyndrome(s, (k << 5) + num6, h0Compact, h1Compact); } } } private static int Threshold(int hammingWeight, int r) { switch (r) { case 12323: return ThresholdFromParameters(hammingWeight, 0.0069722, 13.53, 36); case 24659: return ThresholdFromParameters(hammingWeight, 0.005265, 15.2588, 52); case 40973: return ThresholdFromParameters(hammingWeight, 0.00402312, 17.8785, 69); default: throw new ArgumentException(); } } private static int ThresholdFromParameters(int hammingWeight, double dm, double da, int min) { return System.Math.Max(min, Convert.ToInt32(System.Math.Floor(dm * (double)hammingWeight + da))); } private int Ctr(int[] hCompactCol, byte[] s, int j) { int num = 0; int i = 0; for (int num2 = hw - 4; i <= num2; i += 4) { int num3 = hCompactCol[i] + j - r; int num4 = hCompactCol[i + 1] + j - r; int num5 = hCompactCol[i + 2] + j - r; int num6 = hCompactCol[i + 3] + j - r; num3 += ((num3 >> 31) & r); num4 += ((num4 >> 31) & r); num5 += ((num5 >> 31) & r); num6 += ((num6 >> 31) & r); num += s[num3]; num += s[num4]; num += s[num5]; num += s[num6]; } for (; i < hw; i++) { int num7 = hCompactCol[i] + j - r; num7 += ((num7 >> 31) & r); num += s[num7]; } return num; } private void CtrAll(int[] hCompactCol, byte[] s, byte[] ctrs) { int num = hCompactCol[0]; int num2 = r - num; Array.Copy(s, num, ctrs, 0, num2); Array.Copy(s, 0, ctrs, num2, num); for (int i = 1; i < hw; i++) { int num3 = hCompactCol[i]; int num4 = r - num3; int j = 0; Vector<byte> vector; if (Vector.IsHardwareAccelerated) { for (int num5 = num4 - Vector<byte>.Count; j <= num5; j += Vector<byte>.Count) { Vector<byte> left = new Vector<byte>(ctrs, j); Vector<byte> right = new Vector<byte>(s, num3 + j); vector = left + right; vector.CopyTo(ctrs, j); } } for (int num6 = num4 - 4; j <= num6; j += 4) { ctrs[j] += s[num3 + j]; ctrs[j + 1] += s[num3 + j + 1]; ctrs[j + 2] += s[num3 + j + 2]; ctrs[j + 3] += s[num3 + j + 3]; } for (; j < num4; j++) { ctrs[j] += s[num3 + j]; } int k = num4; if (Vector.IsHardwareAccelerated) { for (int num7 = r - Vector<byte>.Count; k <= num7; k += Vector<byte>.Count) { Vector<byte> left2 = new Vector<byte>(ctrs, k); Vector<byte> right2 = new Vector<byte>(s, k - num4); vector = left2 + right2; vector.CopyTo(ctrs, k); } } for (int num8 = r - 4; k <= num8; k += 4) { ctrs[k] += s[k - num4]; ctrs[k + 1] += s[k + 1 - num4]; ctrs[k + 2] += s[k + 2 - num4]; ctrs[k + 3] += s[k + 3 - num4]; } for (; k < r; k++) { ctrs[k] += s[k - num4]; } } } private void ConvertToCompact(int[] compactVersion, byte[] h) { int num = 0; for (int i = 0; i < R_BYTE; i++) { for (int j = 0; j < 8 && i * 8 + j != r; j++) { int num2 = (h[i] >> j) & 1; compactVersion[num] = (((i * 8 + j) & -num2) | (compactVersion[num] & ~(-num2))); num += num2 - hw; num += ((num >> 31) & hw); } } } private int[] GetColumnFromCompactVersion(int[] hCompact) { int[] array = new int[hw]; if (hCompact[0] == 0) { array[0] = 0; for (int i = 1; i < hw; i++) { array[i] = r - hCompact[hw - i]; } } else { for (int j = 0; j < hw; j++) { array[j] = r - hCompact[hw - 1 - j]; } } return array; } private void RecomputeSyndrome(byte[] syndrome, int index, int[] h0Compact, int[] h1Compact) { if (index < r) { for (int i = 0; i < hw; i++) { if (h0Compact[i] <= index) syndrome[index - h0Compact[i]] ^= 1; else syndrome[r + index - h0Compact[i]] ^= 1; } } else { for (int j = 0; j < hw; j++) { if (h1Compact[j] <= index - r) syndrome[index - r - h1Compact[j]] ^= 1; else syndrome[r - h1Compact[j] + (index - r)] ^= 1; } } } private void AlignE01From1To64(ulong[] e01) { int num = r & 63; int bits = 64 - num; ulong num2 = (ulong)(-1 << num); ulong num3 = e01[R_ULONG - 1]; ulong c = num3 & num2; Nat.ShiftUpBits64(R_ULONG, e01, R_ULONG, bits, c); e01[R_ULONG - 1] = (num3 & ~num2); } private void AlignE01From64To8(ulong[] e01) { int num = (8 * R_BYTE) & 63; int bits = 64 - num; ulong num2 = Nat.ShiftDownBits64(R_ULONG, e01, R_ULONG, bits, 0); e01[R_ULONG - 1] |= num2; } private void AlignE01From8To1(ulong[] e01) { int num = r & 63; int num2 = 8 * R_BYTE - r; ulong num3 = (ulong)(-1 << num); ulong num4 = e01[R_ULONG - 1]; ulong num5 = Nat.ShiftDownBits64(R_ULONG, e01, R_ULONG, num2, 0); e01[R_ULONG - 1] = ((num4 & ~num3) | ((num4 >> num2) & num3) | num5); } } }