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

Poly

class Poly
using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium { internal class Poly { private const int N = 256; private readonly DilithiumEngine m_engine; private readonly int PolyUniformNBlocks; private readonly Symmetric Symmetric; internal readonly int[] Coeffs; public Poly(DilithiumEngine engine) { m_engine = engine; Symmetric = engine.Symmetric; PolyUniformNBlocks = (768 + Symmetric.Stream128BlockBytes - 1) / Symmetric.Stream128BlockBytes; Coeffs = new int[256]; } internal void CopyTo(Poly z) { Array.Copy(Coeffs, z.Coeffs, 256); } public void UniformBlocks(byte[] seed, ushort nonce) { int num = PolyUniformNBlocks * Symmetric.Stream128BlockBytes; byte[] array = new byte[num + 2]; Symmetric.Stream128Init(seed, nonce); Symmetric.Stream128SqueezeBlocks(array, 0, num); for (int i = RejectUniform(Coeffs, 0, 256, array, num); i < 256; i += RejectUniform(Coeffs, i, 256 - i, array, num)) { int num2 = num % 3; for (int j = 0; j < num2; j++) { array[j] = array[num - num2 + j]; } Symmetric.Stream128SqueezeBlocks(array, num2, Symmetric.Stream128BlockBytes); num = Symmetric.Stream128BlockBytes + num2; } } private static int RejectUniform(int[] coeffs, int off, int len, byte[] buf, int buflen) { int num = 0; int num2 = 0; while (num < len && num2 + 3 <= buflen) { uint num4 = (uint)(buf[num2++] & 255); num4 = (uint)((int)num4 | ((buf[num2++] & 255) << 8)); num4 = (uint)((int)num4 | ((buf[num2++] & 255) << 16)); num4 &= 8388607; if (num4 < 8380417) coeffs[off + num++] = (int)num4; } return num; } public void UniformEta(byte[] seed, ushort nonce) { int eta = m_engine.Eta; int num; switch (eta) { case 2: num = (136 + Symmetric.Stream256BlockBytes - 1) / Symmetric.Stream256BlockBytes; break; case 4: num = (227 + Symmetric.Stream256BlockBytes - 1) / Symmetric.Stream256BlockBytes; break; default: throw new ArgumentException("Wrong Dilithium Eta!"); } int num2 = num * Symmetric.Stream256BlockBytes; byte[] array = new byte[num2]; Symmetric.Stream256Init(seed, nonce); Symmetric.Stream256SqueezeBlocks(array, 0, num2); for (int i = RejectEta(Coeffs, 0, 256, array, num2, eta); i < 256; i += RejectEta(Coeffs, i, 256 - i, array, Symmetric.Stream256BlockBytes, eta)) { Symmetric.Stream256SqueezeBlocks(array, 0, Symmetric.Stream256BlockBytes); } } private static int RejectEta(int[] coeffs, int off, int len, byte[] buf, int buflen, int eta) { int num = 0; int num2 = 0; while (num < len && num2 < buflen) { byte num4 = buf[num2++]; uint num5 = (uint)(num4 & 15); uint num6 = (uint)num4 >> 4; switch (eta) { case 2: if (num5 < 15) { num5 -= (205 * num5 >> 10) * 5; coeffs[off + num++] = (int)(2 - num5); } if (num6 < 15 && num < len) { num6 -= (205 * num6 >> 10) * 5; coeffs[off + num++] = (int)(2 - num6); } break; case 4: if (num5 < 9) coeffs[off + num++] = (int)(4 - num5); if (num6 < 9 && num < len) coeffs[off + num++] = (int)(4 - num6); break; } } return num; } public void PointwiseMontgomery(Poly v, Poly w) { for (int i = 0; i < 256; i++) { Coeffs[i] = Reduce.MontgomeryReduce((long)v.Coeffs[i] * (long)w.Coeffs[i]); } } public void PointwiseAccountMontgomery(PolyVec u, PolyVec v) { Poly poly = new Poly(m_engine); PointwiseMontgomery(u[0], v[0]); for (int i = 1; i < m_engine.L; i++) { poly.PointwiseMontgomery(u[i], v[i]); Add(poly); } } public void Add(Poly a) { for (int i = 0; i < 256; i++) { Coeffs[i] += a.Coeffs[i]; } } public void Subtract(Poly b) { for (int i = 0; i < 256; i++) { Coeffs[i] -= b.Coeffs[i]; } } public void ReducePoly() { for (int i = 0; i < 256; i++) { Coeffs[i] = Reduce.Reduce32(Coeffs[i]); } } public void PolyNtt() { Ntt.NTT(Coeffs); } public void InverseNttToMont() { Ntt.InverseNttToMont(Coeffs); } public void ConditionalAddQ() { for (int i = 0; i < 256; i++) { Coeffs[i] = Reduce.ConditionalAddQ(Coeffs[i]); } } public void Power2Round(Poly a) { for (int i = 0; i < 256; i++) { int[] array = Rounding.Power2Round(Coeffs[i]); Coeffs[i] = array[0]; a.Coeffs[i] = array[1]; } } public void PolyT0Pack(byte[] r, int off) { int[] array = new int[8]; for (int i = 0; i < 32; i++) { array[0] = 4096 - Coeffs[8 * i]; array[1] = 4096 - Coeffs[8 * i + 1]; array[2] = 4096 - Coeffs[8 * i + 2]; array[3] = 4096 - Coeffs[8 * i + 3]; array[4] = 4096 - Coeffs[8 * i + 4]; array[5] = 4096 - Coeffs[8 * i + 5]; array[6] = 4096 - Coeffs[8 * i + 6]; array[7] = 4096 - Coeffs[8 * i + 7]; r[off + 13 * i] = (byte)array[0]; r[off + 13 * i + 1] = (byte)(array[0] >> 8); r[off + 13 * i + 1] = (byte)(r[off + 13 * i + 1] | (byte)(array[1] << 5)); r[off + 13 * i + 2] = (byte)(array[1] >> 3); r[off + 13 * i + 3] = (byte)(array[1] >> 11); r[off + 13 * i + 3] = (byte)(r[off + 13 * i + 3] | (byte)(array[2] << 2)); r[off + 13 * i + 4] = (byte)(array[2] >> 6); r[off + 13 * i + 4] = (byte)(r[off + 13 * i + 4] | (byte)(array[3] << 7)); r[off + 13 * i + 5] = (byte)(array[3] >> 1); r[off + 13 * i + 6] = (byte)(array[3] >> 9); r[off + 13 * i + 6] = (byte)(r[off + 13 * i + 6] | (byte)(array[4] << 4)); r[off + 13 * i + 7] = (byte)(array[4] >> 4); r[off + 13 * i + 8] = (byte)(array[4] >> 12); r[off + 13 * i + 8] = (byte)(r[off + 13 * i + 8] | (byte)(array[5] << 1)); r[off + 13 * i + 9] = (byte)(array[5] >> 7); r[off + 13 * i + 9] = (byte)(r[off + 13 * i + 9] | (byte)(array[6] << 6)); r[off + 13 * i + 10] = (byte)(array[6] >> 2); r[off + 13 * i + 11] = (byte)(array[6] >> 10); r[off + 13 * i + 11] = (byte)(r[off + 13 * i + 11] | (byte)(array[7] << 3)); r[off + 13 * i + 12] = (byte)(array[7] >> 5); } } public void PolyT0Unpack(byte[] a, int off) { for (int i = 0; i < 32; i++) { Coeffs[8 * i] = (((a[off + 13 * i] & 255) | ((a[off + 13 * i + 1] & 255) << 8)) & 8191); Coeffs[8 * i + 1] = ((((a[off + 13 * i + 1] & 255) >> 5) | ((a[off + 13 * i + 2] & 255) << 3) | ((a[off + 13 * i + 3] & 255) << 11)) & 8191); Coeffs[8 * i + 2] = ((((a[off + 13 * i + 3] & 255) >> 2) | ((a[off + 13 * i + 4] & 255) << 6)) & 8191); Coeffs[8 * i + 3] = ((((a[off + 13 * i + 4] & 255) >> 7) | ((a[off + 13 * i + 5] & 255) << 1) | ((a[off + 13 * i + 6] & 255) << 9)) & 8191); Coeffs[8 * i + 4] = ((((a[off + 13 * i + 6] & 255) >> 4) | ((a[off + 13 * i + 7] & 255) << 4) | ((a[off + 13 * i + 8] & 255) << 12)) & 8191); Coeffs[8 * i + 5] = ((((a[off + 13 * i + 8] & 255) >> 1) | ((a[off + 13 * i + 9] & 255) << 7)) & 8191); Coeffs[8 * i + 6] = ((((a[off + 13 * i + 9] & 255) >> 6) | ((a[off + 13 * i + 10] & 255) << 2) | ((a[off + 13 * i + 11] & 255) << 10)) & 8191); Coeffs[8 * i + 7] = ((((a[off + 13 * i + 11] & 255) >> 3) | ((a[off + 13 * i + 12] & 255) << 5)) & 8191); Coeffs[8 * i] = 4096 - Coeffs[8 * i]; Coeffs[8 * i + 1] = 4096 - Coeffs[8 * i + 1]; Coeffs[8 * i + 2] = 4096 - Coeffs[8 * i + 2]; Coeffs[8 * i + 3] = 4096 - Coeffs[8 * i + 3]; Coeffs[8 * i + 4] = 4096 - Coeffs[8 * i + 4]; Coeffs[8 * i + 5] = 4096 - Coeffs[8 * i + 5]; Coeffs[8 * i + 6] = 4096 - Coeffs[8 * i + 6]; Coeffs[8 * i + 7] = 4096 - Coeffs[8 * i + 7]; } } public void PolyT1Pack(byte[] buf, int bufOff) { for (int i = 0; i < 64; i++) { buf[bufOff] = (byte)Coeffs[4 * i]; buf[bufOff + 1] = (byte)((Coeffs[4 * i] >> 8) | (Coeffs[4 * i + 1] << 2)); buf[bufOff + 2] = (byte)((Coeffs[4 * i + 1] >> 6) | (Coeffs[4 * i + 2] << 4)); buf[bufOff + 3] = (byte)((Coeffs[4 * i + 2] >> 4) | (Coeffs[4 * i + 3] << 6)); buf[bufOff + 4] = (byte)(Coeffs[4 * i + 3] >> 2); bufOff += 5; } } public void PolyT1Unpack(byte[] a, int aOff) { for (int i = 0; i < 64; i++) { int num = a[aOff]; int num2 = a[aOff + 1]; int num3 = a[aOff + 2]; int num4 = a[aOff + 3]; int num5 = a[aOff + 4]; aOff += 5; Coeffs[4 * i] = ((num | (num2 << 8)) & 1023); Coeffs[4 * i + 1] = (((num2 >> 2) | (num3 << 6)) & 1023); Coeffs[4 * i + 2] = (((num3 >> 4) | (num4 << 4)) & 1023); Coeffs[4 * i + 3] = ((num4 >> 6) | (num5 << 2)); } } public void PolyEtaPack(byte[] r, int off) { int eta = m_engine.Eta; switch (eta) { case 2: for (int j = 0; j < 32; j++) { byte b3 = (byte)(eta - Coeffs[8 * j]); byte b4 = (byte)(eta - Coeffs[8 * j + 1]); byte b5 = (byte)(eta - Coeffs[8 * j + 2]); byte b6 = (byte)(eta - Coeffs[8 * j + 3]); byte b7 = (byte)(eta - Coeffs[8 * j + 4]); byte b8 = (byte)(eta - Coeffs[8 * j + 5]); byte b9 = (byte)(eta - Coeffs[8 * j + 6]); byte b10 = (byte)(eta - Coeffs[8 * j + 7]); r[off + 3 * j] = (byte)(b3 | (b4 << 3) | (b5 << 6)); r[off + 3 * j + 1] = (byte)((b5 >> 2) | (b6 << 1) | (b7 << 4) | (b8 << 7)); r[off + 3 * j + 2] = (byte)((b8 >> 1) | (b9 << 2) | (b10 << 5)); } break; case 4: for (int i = 0; i < 128; i++) { byte b = (byte)(eta - Coeffs[2 * i]); byte b2 = (byte)(eta - Coeffs[2 * i + 1]); r[off + i] = (byte)(b | (b2 << 4)); } break; default: throw new ArgumentException("Eta needs to be 2 or 4!"); } } public void PolyEtaUnpack(byte[] a, int off) { int eta = m_engine.Eta; switch (eta) { case 2: for (int j = 0; j < 32; j++) { Coeffs[8 * j] = (a[off + 3 * j] & 255 & 7); Coeffs[8 * j + 1] = (((a[off + 3 * j] & 255) >> 3) & 7); Coeffs[8 * j + 2] = (((a[off + 3 * j] & 255) >> 6) | (((a[off + 3 * j + 1] & 255) << 2) & 7)); Coeffs[8 * j + 3] = (((a[off + 3 * j + 1] & 255) >> 1) & 7); Coeffs[8 * j + 4] = (((a[off + 3 * j + 1] & 255) >> 4) & 7); Coeffs[8 * j + 5] = (((a[off + 3 * j + 1] & 255) >> 7) | (((a[off + 3 * j + 2] & 255) << 1) & 7)); Coeffs[8 * j + 6] = (((a[off + 3 * j + 2] & 255) >> 2) & 7); Coeffs[8 * j + 7] = (((a[off + 3 * j + 2] & 255) >> 5) & 7); Coeffs[8 * j] = eta - Coeffs[8 * j]; Coeffs[8 * j + 1] = eta - Coeffs[8 * j + 1]; Coeffs[8 * j + 2] = eta - Coeffs[8 * j + 2]; Coeffs[8 * j + 3] = eta - Coeffs[8 * j + 3]; Coeffs[8 * j + 4] = eta - Coeffs[8 * j + 4]; Coeffs[8 * j + 5] = eta - Coeffs[8 * j + 5]; Coeffs[8 * j + 6] = eta - Coeffs[8 * j + 6]; Coeffs[8 * j + 7] = eta - Coeffs[8 * j + 7]; } break; case 4: for (int i = 0; i < 128; i++) { Coeffs[2 * i] = (a[off + i] & 255 & 15); Coeffs[2 * i + 1] = (a[off + i] & 255) >> 4; Coeffs[2 * i] = eta - Coeffs[2 * i]; Coeffs[2 * i + 1] = eta - Coeffs[2 * i + 1]; } break; } } public void UniformGamma1(byte[] seed, ushort nonce) { byte[] array = new byte[m_engine.PolyUniformGamma1NBytes * Symmetric.Stream256BlockBytes]; Symmetric.Stream256Init(seed, nonce); Symmetric.Stream256SqueezeBlocks(array, 0, array.Length); UnpackZ(array); } public void PackZ(byte[] r, int offset) { int gamma = m_engine.Gamma1; switch (gamma) { case 131072: for (int j = 0; j < 64; j++) { uint num3 = (uint)(gamma - Coeffs[4 * j]); uint num4 = (uint)(gamma - Coeffs[4 * j + 1]); uint num5 = (uint)(gamma - Coeffs[4 * j + 2]); uint num6 = (uint)(gamma - Coeffs[4 * j + 3]); r[offset + 9 * j] = (byte)num3; r[offset + 9 * j + 1] = (byte)(num3 >> 8); r[offset + 9 * j + 2] = (byte)((byte)(num3 >> 16) | (num4 << 2)); r[offset + 9 * j + 3] = (byte)(num4 >> 6); r[offset + 9 * j + 4] = (byte)((byte)(num4 >> 14) | (num5 << 4)); r[offset + 9 * j + 5] = (byte)(num5 >> 4); r[offset + 9 * j + 6] = (byte)((byte)(num5 >> 12) | (num6 << 6)); r[offset + 9 * j + 7] = (byte)(num6 >> 2); r[offset + 9 * j + 8] = (byte)(num6 >> 10); } break; case 524288: for (int i = 0; i < 128; i++) { uint num = (uint)(gamma - Coeffs[2 * i]); uint num2 = (uint)(gamma - Coeffs[2 * i + 1]); r[offset + 5 * i] = (byte)num; r[offset + 5 * i + 1] = (byte)(num >> 8); r[offset + 5 * i + 2] = (byte)((byte)(num >> 16) | (num2 << 4)); r[offset + 5 * i + 3] = (byte)(num2 >> 4); r[offset + 5 * i + 4] = (byte)(num2 >> 12); } break; default: throw new ArgumentException("Wrong Dilithium Gamma1!"); } } public void UnpackZ(byte[] a) { int gamma = m_engine.Gamma1; switch (gamma) { case 131072: for (int j = 0; j < 64; j++) { Coeffs[4 * j] = (((a[9 * j] & 255) | ((a[9 * j + 1] & 255) << 8) | ((a[9 * j + 2] & 255) << 16)) & 262143); Coeffs[4 * j + 1] = ((((a[9 * j + 2] & 255) >> 2) | ((a[9 * j + 3] & 255) << 6) | ((a[9 * j + 4] & 255) << 14)) & 262143); Coeffs[4 * j + 2] = ((((a[9 * j + 4] & 255) >> 4) | ((a[9 * j + 5] & 255) << 4) | ((a[9 * j + 6] & 255) << 12)) & 262143); Coeffs[4 * j + 3] = ((((a[9 * j + 6] & 255) >> 6) | ((a[9 * j + 7] & 255) << 2) | ((a[9 * j + 8] & 255) << 10)) & 262143); Coeffs[4 * j] = gamma - Coeffs[4 * j]; Coeffs[4 * j + 1] = gamma - Coeffs[4 * j + 1]; Coeffs[4 * j + 2] = gamma - Coeffs[4 * j + 2]; Coeffs[4 * j + 3] = gamma - Coeffs[4 * j + 3]; } break; case 524288: for (int i = 0; i < 128; i++) { Coeffs[2 * i] = (((a[5 * i] & 255) | ((a[5 * i + 1] & 255) << 8) | ((a[5 * i + 2] & 255) << 16)) & 1048575); Coeffs[2 * i + 1] = ((((a[5 * i + 2] & 255) >> 4) | ((a[5 * i + 3] & 255) << 4) | ((a[5 * i + 4] & 255) << 12)) & 1048575); Coeffs[2 * i] = gamma - Coeffs[2 * i]; Coeffs[2 * i + 1] = gamma - Coeffs[2 * i + 1]; } break; default: throw new ArgumentException("Wrong Dilithiumn Gamma1!"); } } public void Decompose(Poly a) { int gamma = m_engine.Gamma2; for (int i = 0; i < 256; i++) { int[] array = Rounding.Decompose(Coeffs[i], gamma); a.Coeffs[i] = array[0]; Coeffs[i] = array[1]; } } internal void PackW1(byte[] r, int off) { switch (m_engine.Gamma2) { case 95232: for (int j = 0; j < 64; j++) { r[off + 3 * j] = (byte)((byte)Coeffs[4 * j] | (Coeffs[4 * j + 1] << 6)); r[off + 3 * j + 1] = (byte)((byte)(Coeffs[4 * j + 1] >> 2) | (Coeffs[4 * j + 2] << 4)); r[off + 3 * j + 2] = (byte)((byte)(Coeffs[4 * j + 2] >> 4) | (Coeffs[4 * j + 3] << 2)); } break; case 261888: for (int i = 0; i < 128; i++) { r[off + i] = (byte)(Coeffs[2 * i] | (Coeffs[2 * i + 1] << 4)); } break; } } internal void Challenge(byte[] seed, int seedOff, int seedLen) { byte[] array = new byte[Symmetric.Stream256BlockBytes]; ShakeDigest shakeDigest = new ShakeDigest(256); shakeDigest.BlockUpdate(seed, seedOff, seedLen); shakeDigest.Output(array, 0, Symmetric.Stream256BlockBytes); ulong num = Pack.LE_To_UInt64(array); int num2 = 8; Arrays.Fill(Coeffs, 0, 256, 0); for (int i = 256 - m_engine.Tau; i < 256; i++) { int num4; do { if (num2 >= Symmetric.Stream256BlockBytes) { shakeDigest.Output(array, 0, Symmetric.Stream256BlockBytes); num2 = 0; } num4 = array[num2++]; } while (num4 > i); Coeffs[i] = Coeffs[num4]; Coeffs[num4] = (int)(1 - 2 * (num & 1)); num >>= 1; } } public bool CheckNorm(int B) { if (B > 1047552) return true; for (int i = 0; i < 256; i++) { int num = Coeffs[i] >> 31; num = Coeffs[i] - (num & (2 * Coeffs[i])); if (num >= B) return true; } return false; } public int PolyMakeHint(Poly a0, Poly a1) { int num = 0; for (int i = 0; i < 256; i++) { Coeffs[i] = Rounding.MakeHint(a0.Coeffs[i], a1.Coeffs[i], m_engine); num += Coeffs[i]; } return num; } public void PolyUseHint(Poly a, Poly h) { int gamma = m_engine.Gamma2; for (int i = 0; i < 256; i++) { Coeffs[i] = Rounding.UseHint(a.Coeffs[i], h.Coeffs[i], gamma); } } public void ShiftLeft() { for (int i = 0; i < 256; i++) { Coeffs[i] <<= 13; } } } }