diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumEngine.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumEngine.cs
new file mode 100644
index 000000000..6cc3bca53
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumEngine.cs
@@ -0,0 +1,547 @@
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Emit;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using static Org.BouncyCastle.Tls.DtlsReliableHandshake;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ internal class DilithiumEngine
+ {
+ private SecureRandom _random;
+
+ public const int N = 256;
+ public const int Q = 8380417;
+ public const int QInv = 58728449; // Q ^ (-1) mod 2 ^32
+ public const int D = 13;
+ public const int RootOfUnity = 1753;
+ public const int SeedBytes = 32;
+ public const int CrhBytes = 64;
+ public const bool RandomizedSigning = false;
+
+ public const int PolyT1PackedBytes = 320;
+ public const int PolyT0PackedBytes = 416;
+
+ public int Mode { get; private set; }
+
+ public int K { get; private set; }
+ public int L { get; private set; }
+ public int Eta { get; private set; }
+ public int Tau { get; private set; }
+ public int Beta { get; private set; }
+ public int Gamma1 { get; private set; }
+ public int Gamma2 { get; private set; }
+ public int Omega { get; private set; }
+
+ public int PolyVecHPackedBytes { get; private set; }
+
+ public int PolyZPackedBytes { get; private set; }
+ public int PolyW1PackedBytes { get; private set; }
+ public int PolyEtaPackedBytes { get; private set; }
+
+ public int CryptoPublicKeyBytes { get; private set; }
+ public int CryptoSecretKeyBytes { get; private set; }
+ public int CryptoBytes { get; private set; }
+ public int PolyUniformGamma1NBytes { get; private set; }
+
+ public DilithiumEngine(int mode, SecureRandom random)
+ {
+ Mode = mode;
+ switch (Mode)
+ {
+ case 2:
+ K = 4;
+ L = 4;
+ Eta = 2;
+ Tau = 39;
+ Beta = 78;
+ Gamma1 = (1 << 17);
+ Gamma2 = ((Q - 1) / 88);
+ Omega = 80;
+ PolyZPackedBytes = 576;
+ PolyW1PackedBytes = 192;
+ PolyEtaPackedBytes = 96;
+ break;
+ case 3:
+ K = 6;
+ L = 5;
+ Eta = 4;
+ Tau = 49;
+ Beta = 196;
+ Gamma1 = (1 << 19);
+ Gamma2 = ((Q - 1) / 32);
+ Omega = 55;
+ PolyZPackedBytes = 640;
+ PolyW1PackedBytes = 128;
+ PolyEtaPackedBytes = 128;
+ break;
+ case 5:
+ K = 8;
+ L = 7;
+ Eta = 2;
+ Tau = 60;
+ Beta = 120;
+ Gamma1 = (1 << 19);
+ Gamma2 = ((Q - 1) / 32);
+ Omega = 75;
+ PolyZPackedBytes = 640;
+ PolyW1PackedBytes = 128;
+ PolyEtaPackedBytes = 96;
+ break;
+ default:
+ throw new ArgumentException("The mode " + mode + "is not supported by Crystals Dilithium!");
+ }
+
+ _random = random;
+ PolyVecHPackedBytes = Omega + K;
+ CryptoPublicKeyBytes = SeedBytes + K * PolyT1PackedBytes;
+ CryptoSecretKeyBytes = 3 * SeedBytes + L * PolyEtaPackedBytes + K * PolyEtaPackedBytes + K * PolyT0PackedBytes;
+ CryptoBytes = SeedBytes + L * PolyZPackedBytes + PolyVecHPackedBytes;
+
+ if (Gamma1 == (1 << 17))
+ {
+ PolyUniformGamma1NBytes = ((576 + Symmetric.Shake256Rate - 1) / Symmetric.Shake256Rate);
+ }
+ else if (Gamma1 == (1 << 19))
+ {
+ PolyUniformGamma1NBytes = ((640 + Symmetric.Shake256Rate - 1) / Symmetric.Shake256Rate);
+ }
+ else
+ {
+ throw new ArgumentException("Wrong Dilithium Gamma1!");
+ }
+ }
+
+ public void GenerateKeyPair(byte[] pk, byte[] sk)
+ {
+ byte[] SeedBuf = new byte[SeedBytes];
+ byte[] buf = new byte[2 * SeedBytes + CrhBytes];
+ byte[] tr = new byte[SeedBytes];
+
+ byte[] rho, rhoPrime, key;
+
+ PolyVecMatrix Matrix = new PolyVecMatrix(this);
+
+ PolyVecL s1 = new PolyVecL(this), s1Hat;
+ PolyVecK s2 = new PolyVecK(this), t1 = new PolyVecK(this), t0 = new PolyVecK(this);
+
+ _random.NextBytes(SeedBuf);
+
+ //Console.WriteLine("SeedBuf = {0}", Convert.ToHexString(SeedBuf));
+
+
+ ShakeDigest Shake256Digest = new ShakeDigest(256);
+ Shake256Digest.BlockUpdate(SeedBuf, 0, SeedBytes);
+ Shake256Digest.DoFinal(buf, 0, 2 * SeedBytes + CrhBytes);
+
+ rho = Arrays.CopyOfRange(buf, 0, SeedBytes);
+ rhoPrime = Arrays.CopyOfRange(buf, SeedBytes, SeedBytes + CrhBytes);
+ key = Arrays.CopyOfRange(buf, SeedBytes + CrhBytes, 2 * SeedBytes + CrhBytes);
+
+ Matrix.ExpandMatrix(rho);
+
+ s1.UniformEta(rhoPrime, (ushort)0);
+
+ s2.UniformEta(rhoPrime, (ushort)L);
+
+ s1Hat = new PolyVecL(this);
+
+ s1.CopyPolyVecL(s1Hat);
+ s1Hat.Ntt();
+
+ Matrix.PointwiseMontgomery(t1, s1Hat);
+
+ t1.Reduce();
+ t1.InverseNttToMont();
+
+ t1.AddPolyVecK(s2);
+ t1.ConditionalAddQ();
+ t1.Power2Round(t0);
+
+ //Console.WriteLine("t1 = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (short coeff in t1.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ //Console.WriteLine("rho = {0}", Convert.ToHexString(rho));
+
+ Packing.PackPublicKey(pk, rho, t1, this);
+
+ Shake256Digest.BlockUpdate(pk, 0, CryptoPublicKeyBytes);
+ Shake256Digest.DoFinal(tr, 0, SeedBytes);
+
+ Packing.PackSecretKey(sk, rho, tr, key, t0, s1, s2, this);
+ }
+
+ public void SignSignature(byte[] sig, int siglen, byte[] msg, int msglen, byte[] sk)
+ {
+ int n;
+ byte[] SeedBuf = new byte[3 * SeedBytes + 2 * CrhBytes];
+ byte[] rho = new byte[SeedBytes], tr = new byte[SeedBytes], key = new byte[SeedBytes], mu = new byte[CrhBytes], rhoPrime = new byte[CrhBytes];
+ ushort nonce = 0;
+ PolyVecMatrix Matrix = new PolyVecMatrix(this);
+ PolyVecL s1 = new PolyVecL(this), y = new PolyVecL(this), z = new PolyVecL(this);
+ PolyVecK t0 = new PolyVecK(this), s2 = new PolyVecK(this), w1 = new PolyVecK(this), w0 = new PolyVecK(this), h = new PolyVecK(this);
+ Poly cp = new Poly(this);
+
+ Packing.UnpackSecretKey(rho, tr, key, t0, s1, s2, sk, this);
+
+ ShakeDigest ShakeDigest256 = new ShakeDigest(256);
+ ShakeDigest256.BlockUpdate(tr, 0, SeedBytes);
+ ShakeDigest256.BlockUpdate(msg, 0, msglen);
+ ShakeDigest256.DoFinal(mu, 0, CrhBytes);
+
+ if (RandomizedSigning)
+ {
+ _random.NextBytes(rhoPrime);
+ }
+ else
+ {
+ byte[] KeyMu = Arrays.CopyOf(key, SeedBytes + CrhBytes);
+ Array.Copy(mu, 0, KeyMu, SeedBytes, CrhBytes);
+ ShakeDigest256.BlockUpdate(KeyMu, 0, SeedBytes + CrhBytes);
+ ShakeDigest256.DoFinal(rhoPrime, 0, CrhBytes);
+ }
+
+ Matrix.ExpandMatrix(rho);
+
+ //Console.WriteLine("Matrix = [");
+ //for (int i = 0; i < K; i++)
+ //{
+ // Console.Write("[", i);
+ // for (int j = 0; j < L; j++)
+ // {
+ // Console.Write("{0} {1} [", i, j);
+ // for (int co = 0; co < N; co++)
+ // {
+ // Console.Write("{0}, ", Matrix.Matrix[i].Vec[j].Coeffs[co]);
+ // }
+ // Console.Write("]\n");
+ // }
+ // Console.Write("]\n");
+ //}
+ //Console.Write("]\n");
+
+ s1.Ntt();
+ s2.Ntt();
+ t0.Ntt();
+
+ rej:
+ y.UniformGamma1(rhoPrime, nonce++);
+ y.CopyPolyVecL(z);
+ z.Ntt();
+
+ Matrix.PointwiseMontgomery(w1, z);
+
+ //Console.WriteLine("w1 = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in w1.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+
+ w1.Reduce();
+ w1.InverseNttToMont();
+
+ w1.ConditionalAddQ();
+ w1.Decompose(w0);
+
+ //Console.WriteLine("w0 = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in w0.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ w1.PackW1(sig);
+
+ ShakeDigest256.BlockUpdate(mu, 0, CrhBytes);
+ ShakeDigest256.BlockUpdate(sig, 0, K * PolyW1PackedBytes);
+ ShakeDigest256.DoFinal(sig, 0, SeedBytes);
+
+ cp.Challenge(sig);
+
+ cp.PolyNtt();
+
+ z.PointwisePolyMontgomery(cp, s1);
+ z.InverseNttToMont();
+ z.AddPolyVecL(y);
+ z.Reduce();
+ if (z.CheckNorm(Gamma1 - Beta))
+ {
+ goto rej;
+ }
+
+ //Console.WriteLine("cp = ");
+ //Console.Write("[");
+ //foreach (int coeff in cp.Coeffs)
+ //{
+ // Console.Write(String.Format("{0}, ", coeff));
+ //}
+ //Console.Write("]\n");
+
+ //Console.WriteLine("s2 = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in s2.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ h.PointwisePolyMontgomery(cp, s2);
+ h.InverseNttToMont();
+
+ //Console.WriteLine("h = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in h.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ w0.Subtract(h);
+ w0.Reduce();
+ if (w0.CheckNorm(Gamma2 - Beta))
+ {
+ goto rej;
+ }
+
+ h.PointwisePolyMontgomery(cp, t0);
+ h.InverseNttToMont();
+ h.Reduce();
+ if (h.CheckNorm(Gamma2))
+ {
+ goto rej;
+ }
+
+ w0.AddPolyVecK(h);
+
+ //Console.WriteLine("w0 = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in w0.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ w0.ConditionalAddQ();
+
+ //Console.WriteLine("w0 = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in w0.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ //Console.WriteLine("w1 = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in w1.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ n = h.MakeHint(w0, w1);
+ if (n > Omega)
+ {
+ goto rej;
+ }
+
+
+ //Console.WriteLine("z = ");
+ //for (int i = 0; i < L; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in z.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ //Console.WriteLine("h = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in h.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ Packing.PackSignature(sig, sig, z, h, this);
+ }
+
+ public void Sign(byte[] sig, int siglen, byte[] msg, int msglen, byte[] sk)
+ {
+ SignSignature(sig, siglen, msg, msglen, sk);
+ }
+
+ public bool SignVerify(byte[] msg, int msglen, byte[] sig, int siglen, byte[] pk)
+ {
+ byte[] buf = new byte[K * PolyW1PackedBytes], rho = new byte[SeedBytes], mu = new byte[CrhBytes], c = new byte[SeedBytes], c2 = new byte[SeedBytes];
+ Poly cp = new Poly(this);
+ PolyVecMatrix Matrix = new PolyVecMatrix(this);
+ PolyVecL z = new PolyVecL(this);
+ PolyVecK t1 = new PolyVecK(this), w1 = new PolyVecK(this), h = new PolyVecK(this);
+
+ if (sig.Length != CryptoBytes + msg.Length)
+ {
+ return false;
+ }
+
+ Packing.UnpackPublicKey(rho, t1, pk, this);
+
+ //Console.WriteLine("rho = "+Convert.ToHexString(rho));
+
+ //Console.WriteLine("t1 = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in t1.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+
+ if (!Packing.UnpackSignature(c, z, h, sig, this))
+ {
+ return false;
+ }
+
+ if (z.CheckNorm(Gamma1 - Beta))
+ {
+ return false;
+ }
+
+ //Console.WriteLine("z = ");
+ //for (int i = 0; i < L; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in z.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ //Console.WriteLine("h = ");
+ //for (int i = 0; i < K; ++i)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (int coeff in h.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+
+
+ ShakeDigest Shake256Digest = new ShakeDigest(256);
+ Shake256Digest.BlockUpdate(pk, 0, CryptoPublicKeyBytes);
+ Shake256Digest.DoFinal(mu, 0, SeedBytes);
+
+ Shake256Digest.BlockUpdate(mu, 0, SeedBytes);
+ Shake256Digest.BlockUpdate(msg, 0, msglen);
+ Shake256Digest.DoFinal(mu, 0);
+
+ cp.Challenge(c);
+
+ Matrix.ExpandMatrix(rho);
+
+ z.Ntt();
+ Matrix.PointwiseMontgomery(w1, z);
+
+ cp.PolyNtt();
+
+ t1.ShiftLeft();
+ t1.Ntt();
+ t1.PointwisePolyMontgomery(cp, t1);
+
+ w1.Subtract(t1);
+ w1.Reduce();
+ w1.InverseNttToMont();
+
+ w1.ConditionalAddQ();
+ w1.UseHint(w1, h);
+
+ w1.PackW1(buf);
+
+ Shake256Digest.BlockUpdate(mu, 0, CrhBytes);
+ Shake256Digest.BlockUpdate(buf, 0, K * PolyW1PackedBytes);
+ Shake256Digest.DoFinal(c2, 0, SeedBytes);
+
+ for (int i = 0; i < SeedBytes; ++i)
+ {
+ if (c[i] != c2[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public bool SignOpen(byte[] msg, int msglen, byte[] sig, int siglen, byte[] pk)
+ {
+ int i;
+ if (siglen < CryptoBytes)
+ {
+ goto badsig;
+ }
+
+ Array.Copy(sig, CryptoBytes, msg, 0, siglen - CryptoBytes);
+ if (SignVerify(msg, siglen - CryptoBytes, sig, siglen, pk))
+ {
+ return true;
+ }
+
+ badsig:
+ for (i = 0; i < msg.Length; i++)
+ {
+ msg[i] = 0;
+ }
+ return false;
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyGenerationParameters.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyGenerationParameters.cs
new file mode 100644
index 000000000..a5883bf78
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyGenerationParameters.cs
@@ -0,0 +1,18 @@
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ public class DilithiumKeyGenerationParameters
+ : KeyGenerationParameters
+ {
+ private DilithiumParameters parameters;
+
+ public DilithiumKeyGenerationParameters(SecureRandom random, DilithiumParameters parameters) : base(random, 255)
+ {
+ this.parameters = parameters;
+ }
+
+ public DilithiumParameters Parameters => parameters;
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyPairGenerator.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyPairGenerator.cs
new file mode 100644
index 000000000..57e2e3629
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyPairGenerator.cs
@@ -0,0 +1,35 @@
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ public class DilithiumKeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+
+ {
+ private SecureRandom random;
+ private DilithiumParameters parameters;
+
+ public void Init(KeyGenerationParameters param)
+ {
+ random = param.Random;
+ parameters = ((DilithiumKeyGenerationParameters)param).Parameters;
+ }
+
+ public AsymmetricCipherKeyPair GenerateKeyPair()
+ {
+ DilithiumEngine engine = parameters.GetEngine(random);
+ byte[] sk = new byte[engine.CryptoSecretKeyBytes];
+ byte[] pk = new byte[engine.CryptoPublicKeyBytes];
+ engine.GenerateKeyPair(pk, sk);
+
+ DilithiumPublicKeyParameters pubKey = new DilithiumPublicKeyParameters(parameters, pk);
+ DilithiumPrivateKeyParameters privKey = new DilithiumPrivateKeyParameters(parameters, sk);
+
+
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyParameters.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyParameters.cs
new file mode 100644
index 000000000..7e0054747
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyParameters.cs
@@ -0,0 +1,18 @@
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ public class DilithiumKeyParameters
+ : AsymmetricKeyParameter
+ {
+ DilithiumParameters parameters;
+
+ public DilithiumKeyParameters(bool isPrivate, DilithiumParameters parameters) : base(isPrivate)
+ {
+ this.parameters = parameters;
+ }
+
+ public DilithiumParameters Parameters => parameters;
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumParameters.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumParameters.cs
new file mode 100644
index 000000000..48c697b55
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumParameters.cs
@@ -0,0 +1,28 @@
+
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ public class DilithiumParameters
+ : ICipherParameters
+ {
+ public static DilithiumParameters Dilithium2 = new DilithiumParameters(2);
+ public static DilithiumParameters Dilithium3 = new DilithiumParameters(3);
+ public static DilithiumParameters Dilithium5 = new DilithiumParameters(5);
+
+ private int k;
+
+ private DilithiumParameters(int param)
+ {
+ k = param;
+ }
+
+ public DilithiumEngine GetEngine(SecureRandom Random)
+ {
+ return new DilithiumEngine(k, Random);
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs
new file mode 100644
index 000000000..510e46eea
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs
@@ -0,0 +1,22 @@
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ public class DilithiumPrivateKeyParameters
+ : DilithiumKeyParameters
+ {
+ private byte[] privateKey;
+
+ public DilithiumPrivateKeyParameters(DilithiumParameters parameters, byte[] skEncoded)
+ : base(true, parameters)
+ {
+ privateKey = Arrays.Clone(skEncoded);
+ }
+
+ public byte[] GetEncoded()
+ {
+ return Arrays.Clone(privateKey);
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs
new file mode 100644
index 000000000..c2891ba3d
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs
@@ -0,0 +1,24 @@
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ public class DilithiumPublicKeyParameters
+ : DilithiumKeyParameters
+ {
+
+ private byte[] publicKey;
+
+ public DilithiumPublicKeyParameters(DilithiumParameters parameters, byte[] pkEncoded)
+ : base(false, parameters)
+ {
+ publicKey = Arrays.Clone(pkEncoded);
+ }
+
+ public byte[] GetEncoded()
+ {
+ return Arrays.Clone(publicKey);
+ }
+
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/DilithiumSigner.cs b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumSigner.cs
new file mode 100644
index 000000000..3c7ad7d60
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/DilithiumSigner.cs
@@ -0,0 +1,60 @@
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using System;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ public class DilithiumSigner
+ : IMessageSigner
+ {
+ private DilithiumPrivateKeyParameters privKey;
+ private DilithiumPublicKeyParameters pubKey;
+
+ private SecureRandom random;
+
+ public DilithiumSigner(SecureRandom random)
+ {
+ this.random = random;
+ }
+
+ public void Init(bool forSigning, ICipherParameters param)
+ {
+ if (forSigning)
+ {
+ if (param is ParametersWithRandom)
+ {
+ privKey = (DilithiumPrivateKeyParameters)((ParametersWithRandom)param).Parameters;
+ random = ((ParametersWithRandom)param).Random;
+ }
+ else
+ {
+ privKey = (DilithiumPrivateKeyParameters)param;
+ random = new SecureRandom();
+ }
+ }
+ else
+ {
+ pubKey = (DilithiumPublicKeyParameters) param;
+ }
+
+ }
+
+ public byte[] GenerateSignature(byte[] message)
+ {
+ DilithiumEngine engine = privKey.Parameters.GetEngine(random);
+ byte[] sig = new byte[engine.CryptoBytes];
+ engine.Sign(sig, sig.Length, message, message.Length, privKey.GetEncoded());
+ return sig;
+ }
+
+ public bool VerifySignature(byte[] message, byte[] signature)
+ {
+ DilithiumEngine engine = pubKey.Parameters.GetEngine(random);
+ return engine.SignOpen(message, message.Length, signature, signature.Length, pubKey.GetEncoded());
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/Ntt.cs b/crypto/src/pqc/crypto/crystals/dilithium/Ntt.cs
new file mode 100644
index 000000000..5ca7f8b6b
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/Ntt.cs
@@ -0,0 +1,98 @@
+using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium;
+using Org.BouncyCastle.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ internal class Ntt
+ {
+ private static int[] Zetas = {
+ 0, 25847, -2608894, -518909, 237124, -777960, -876248, 466468,
+ 1826347, 2353451, -359251, -2091905, 3119733, -2884855, 3111497, 2680103,
+ 2725464, 1024112, -1079900, 3585928, -549488, -1119584, 2619752, -2108549,
+ -2118186, -3859737, -1399561, -3277672, 1757237, -19422, 4010497, 280005,
+ 2706023, 95776, 3077325, 3530437, -1661693, -3592148, -2537516, 3915439,
+ -3861115, -3043716, 3574422, -2867647, 3539968, -300467, 2348700, -539299,
+ -1699267, -1643818, 3505694, -3821735, 3507263, -2140649, -1600420, 3699596,
+ 811944, 531354, 954230, 3881043, 3900724, -2556880, 2071892, -2797779,
+ -3930395, -1528703, -3677745, -3041255, -1452451, 3475950, 2176455, -1585221,
+ -1257611, 1939314, -4083598, -1000202, -3190144, -3157330, -3632928, 126922,
+ 3412210, -983419, 2147896, 2715295, -2967645, -3693493, -411027, -2477047,
+ -671102, -1228525, -22981, -1308169, -381987, 1349076, 1852771, -1430430,
+ -3343383, 264944, 508951, 3097992, 44288, -1100098, 904516, 3958618,
+ -3724342, -8578, 1653064, -3249728, 2389356, -210977, 759969, -1316856,
+ 189548, -3553272, 3159746, -1851402, -2409325, -177440, 1315589, 1341330,
+ 1285669, -1584928, -812732, -1439742, -3019102, -3881060, -3628969, 3839961,
+ 2091667, 3407706, 2316500, 3817976, -3342478, 2244091, -2446433, -3562462,
+ 266997, 2434439, -1235728, 3513181, -3520352, -3759364, -1197226, -3193378,
+ 900702, 1859098, 909542, 819034, 495491, -1613174, -43260, -522500,
+ -655327, -3122442, 2031748, 3207046, -3556995, -525098, -768622, -3595838,
+ 342297, 286988, -2437823, 4108315, 3437287, -3342277, 1735879, 203044,
+ 2842341, 2691481, -2590150, 1265009, 4055324, 1247620, 2486353, 1595974,
+ -3767016, 1250494, 2635921, -3548272, -2994039, 1869119, 1903435, -1050970,
+ -1333058, 1237275, -3318210, -1430225, -451100, 1312455, 3306115, -1962642,
+ -1279661, 1917081, -2546312, -1374803, 1500165, 777191, 2235880, 3406031,
+ -542412, -2831860, -1671176, -1846953, -2584293, -3724270, 594136, -3776993,
+ -2013608, 2432395, 2454455, -164721, 1957272, 3369112, 185531, -1207385,
+ -3183426, 162844, 1616392, 3014001, 810149, 1652634, -3694233, -1799107,
+ -3038916, 3523897, 3866901, 269760, 2213111, -975884, 1717735, 472078,
+ -426683, 1723600, -1803090, 1910376, -1667432, -1104333, -260646, -3833893,
+ -2939036, -2235985, -420899, -2286327, 183443, -976891, 1612842, -3545687,
+ -554416, 3919660, -48306, -1362209, 3937738, 1400424, -846154, 1976782
+ };
+ public static void NTT(int[] r)
+ {
+ uint len, start, j, k;
+ int zeta, t;
+
+ k = 0;
+ for (len = 128; len > 0; len >>= 1)
+ {
+ for (start = 0; start < DilithiumEngine.N; start = j + len)
+ {
+ zeta = Ntt.Zetas[++k];
+ for (j = start; j < start + len; ++j)
+ {
+ t = Reduce.MontgomeryReduce((long)((long)zeta * (long)r[j + len]));
+ r[j + len] = r[j] - t;
+ r[j] = r[j] + t;
+ }
+ }
+ }
+ }
+
+ public static void InverseNttToMont(int[] a)
+ {
+ uint start, len, j, k;
+ int t, zeta;
+ const int f = 41978; // (mont^2)/256
+
+
+ k = 256;
+ for (len = 1; len < DilithiumEngine.N; len <<= 1)
+ {
+ for (start = 0; start < DilithiumEngine.N; start = j + len)
+ {
+ zeta = (-1) * Ntt.Zetas[--k];
+ for (j = start; j < start + len; ++j)
+ {
+ t = a[j];
+ a[j] = t + a[j + len];
+ a[j + len] = t - a[j + len];
+ a[j + len] = Reduce.MontgomeryReduce((long)((long)zeta * (long)a[j + len]));
+ }
+
+ }
+ }
+
+ for (j = 0; j < DilithiumEngine.N; ++j)
+ {
+ a[j] = Reduce.MontgomeryReduce((long)((long)f * (long)a[j]));
+ }
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/Packing.cs b/crypto/src/pqc/crypto/crystals/dilithium/Packing.cs
new file mode 100644
index 000000000..caed99a30
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/Packing.cs
@@ -0,0 +1,177 @@
+using Org.BouncyCastle.Pqc.Crypto.SphincsPlus;
+using Org.BouncyCastle.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ internal class Packing
+ {
+ public static void PackPublicKey(byte[] pk, byte[] rho, PolyVecK t1, DilithiumEngine Engine)
+ {
+ Array.Copy(rho, 0, pk, 0, DilithiumEngine.SeedBytes);
+ for (int i = 0; i < Engine.K; i++)
+ {
+ t1.Vec[i].PolyT1Pack(pk, DilithiumEngine.SeedBytes + i * DilithiumEngine.PolyT1PackedBytes);
+ }
+ }
+
+ public static void UnpackPublicKey(byte[] rho, PolyVecK t1, byte[] pk, DilithiumEngine Engine)
+ {
+ int i;
+
+ Array.Copy(pk, 0, rho, 0, DilithiumEngine.SeedBytes);
+
+ for (i = 0; i < Engine.K; ++i)
+ {
+ t1.Vec[i].PolyT1Unpack(pk, DilithiumEngine.SeedBytes + i * DilithiumEngine.PolyT1PackedBytes);
+ }
+ }
+
+ public static void PackSecretKey(byte[] sk, byte[] rho, byte[] tr, byte[] key, PolyVecK t0, PolyVecL s1, PolyVecK s2, DilithiumEngine Engine)
+ {
+ int i, end = 0;
+ Array.Copy(rho, sk, DilithiumEngine.SeedBytes);
+ end += DilithiumEngine.SeedBytes;
+
+ Array.Copy(key, 0, sk, end, DilithiumEngine.SeedBytes);
+ end += DilithiumEngine.SeedBytes;
+
+ Array.Copy(tr, 0, sk, end, DilithiumEngine.SeedBytes);
+ end += DilithiumEngine.SeedBytes;
+
+ for (i = 0; i < Engine.L; ++i)
+ {
+ s1.Vec[i].PolyEtaPack(sk, end + i * Engine.PolyEtaPackedBytes);
+ }
+ end += Engine.L * Engine.PolyEtaPackedBytes;
+
+ for (i = 0; i < Engine.K; ++i)
+ {
+ s2.Vec[i].PolyEtaPack(sk, end + i * Engine.PolyEtaPackedBytes);
+ }
+ end += Engine.K * Engine.PolyEtaPackedBytes;
+
+ for (i = 0; i < Engine.K; ++i)
+ {
+ t0.Vec[i].PolyT0Pack(sk, end + i * DilithiumEngine.PolyT0PackedBytes);
+ }
+ }
+
+ public static void UnpackSecretKey(byte[] rho, byte[] tr, byte[] key, PolyVecK t0, PolyVecL s1, PolyVecK s2, byte[] sk, DilithiumEngine Engine)
+ {
+ int i, end = 0;
+ Array.Copy(sk, 0, rho, 0, DilithiumEngine.SeedBytes);
+ end += DilithiumEngine.SeedBytes;
+
+ Array.Copy(sk, end, key, 0, DilithiumEngine.SeedBytes);
+ end += DilithiumEngine.SeedBytes;
+
+ Array.Copy(sk, end, tr, 0, DilithiumEngine.SeedBytes);
+ end += DilithiumEngine.SeedBytes;
+
+ for (i = 0; i < Engine.L; ++i)
+ {
+ s1.Vec[i].PolyEtaUnpack(sk, end + i * Engine.PolyEtaPackedBytes);
+ }
+ end += Engine.L * Engine.PolyEtaPackedBytes;
+
+ for (i = 0; i < Engine.K; ++i)
+ {
+ s2.Vec[i].PolyEtaUnpack(sk, end + i * Engine.PolyEtaPackedBytes);
+ }
+ end += Engine.K * Engine.PolyEtaPackedBytes;
+
+ for (i = 0; i < Engine.K; ++i)
+ {
+ t0.Vec[i].PolyT0Unpack(sk, end + i * DilithiumEngine.PolyT0PackedBytes);
+ }
+ }
+
+ public static void PackSignature(byte[] sig, byte[] c, PolyVecL z, PolyVecK h, DilithiumEngine engine)
+ {
+ int i, j, k, end = 0;
+
+ Array.Copy(c, 0, sig, 0, DilithiumEngine.SeedBytes);
+ end += DilithiumEngine.SeedBytes;
+
+ for (i = 0; i < engine.L; ++i)
+ {
+ z.Vec[i].PackZ(sig, end + i * engine.PolyZPackedBytes);
+ }
+ end += engine.L * engine.PolyZPackedBytes;
+
+ for (i = 0; i < engine.Omega + engine.K; ++i)
+ {
+ sig[end + i] = 0;
+ }
+
+
+ k = 0;
+ for (i = 0; i < engine.K; ++i)
+ {
+ for (j = 0; j < DilithiumEngine.N; ++j)
+ {
+ if (h.Vec[i].Coeffs[j] != 0)
+ {
+ sig[end + k++] = (byte)j;
+ }
+ }
+ sig[end + engine.Omega + i] = (byte)k;
+ }
+ //Console.WriteLine("sig = " + Convert.ToHexString(sig));
+
+ }
+
+ public static bool UnpackSignature(byte[] c, PolyVecL z, PolyVecK h, byte[] sig, DilithiumEngine Engine)
+ {
+ int i, j, k;
+
+ Array.Copy(sig, c, DilithiumEngine.SeedBytes);
+
+ int end = DilithiumEngine.SeedBytes;
+ for (i = 0; i < Engine.L; ++i)
+ {
+ z.Vec[i].UnpackZ(sig, end + i * Engine.PolyZPackedBytes);
+ }
+ end += Engine.L * Engine.PolyZPackedBytes;
+
+ k = 0;
+ for (i = 0; i < Engine.K; ++i)
+ {
+ for (j = 0; j < DilithiumEngine.N; ++j)
+ {
+ h.Vec[i].Coeffs[j] = 0;
+ }
+
+ if ((sig[end + Engine.Omega + i] & 0xFF) < k || (sig[end + Engine.Omega + i] & 0xFF) > Engine.Omega)
+ {
+ return false;
+ }
+
+ for (j = k; j < (sig[end + Engine.Omega + i] & 0xFF); ++j)
+ {
+ if (j > k && (sig[end + j] & 0xFF) <= (sig[end + j - 1] & 0xFF))
+ {
+ return false;
+ }
+ h.Vec[i].Coeffs[sig[end + j] & 0xFF] = 1;
+ }
+
+ k = (int)(sig[end + Engine.Omega + i]);
+ }
+ for (j = k; j < Engine.Omega; ++j)
+ {
+ if ((sig[end + j] & 0xFF) != 0)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/Poly.cs b/crypto/src/pqc/crypto/crystals/dilithium/Poly.cs
new file mode 100644
index 000000000..8ee9676e3
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/Poly.cs
@@ -0,0 +1,695 @@
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ internal class Poly
+ {
+ public int[] Coeffs { get; set; }
+
+ private int N;
+ private DilithiumEngine Engine;
+ private int PolyUniformNBlocks = (768 + Symmetric.Shake128Rate - 1) / Symmetric.Shake128Rate;
+
+ public Poly(DilithiumEngine engine)
+ {
+ N = DilithiumEngine.N;
+ Coeffs = new int[N];
+ Engine = engine;
+ }
+
+ public void UniformBlocks(byte[] seed, ushort nonce)
+ {
+ int i, ctr, off,
+ buflen = PolyUniformNBlocks * Symmetric.Shake128Rate;
+ byte[] buf = new byte[buflen + 2];
+ ShakeDigest Shake128Digest = new ShakeDigest(128);
+
+ Symmetric.ShakeStreamInit(Shake128Digest, seed, nonce);
+
+ Shake128Digest.DoOutput(buf, 0, buflen + 2);
+
+ ctr = RejectUniform(Coeffs, 0, N, buf, buflen);
+
+ while (ctr < N)
+ {
+ off = buflen % 3;
+ for (i = 0; i < off; ++i)
+ {
+ buf[i] = buf[buflen - off + i];
+ }
+ Shake128Digest.DoOutput(buf, buflen + off, 1);
+ buflen = Symmetric.Shake128Rate + off;
+ ctr += RejectUniform(Coeffs, ctr, N, buf, buflen);
+ }
+
+
+ }
+
+ private static int RejectUniform(int[] coeffs, int off, int len, byte[] buf, int buflen)
+ {
+ int ctr, pos;
+ uint t;
+
+
+ ctr = pos = 0;
+ while (ctr < len && pos + 3 <= buflen)
+ {
+ t = (uint)(buf[pos++] & 0xFF);
+ t |= (uint)(buf[pos++] & 0xFF) << 8;
+ t |= (uint)(buf[pos++] & 0xFF) << 16;
+ t &= 0x7FFFFF;
+
+ if (t < DilithiumEngine.Q)
+ {
+ coeffs[off + ctr++] = (int)t;
+ }
+ }
+ return ctr;
+
+ }
+
+ public void UniformEta(byte[] seed, ushort nonce)
+ {
+ int ctr, PolyUniformEtaNBlocks, eta = Engine.Eta;
+
+
+ if (Engine.Eta == 2)
+ {
+ PolyUniformEtaNBlocks = ((136 + Symmetric.Shake128Rate - 1) / Symmetric.Shake256Rate);
+ }
+ else if (Engine.Eta == 4)
+ {
+ PolyUniformEtaNBlocks = ((227 + Symmetric.Shake128Rate - 1) / Symmetric.Shake256Rate);
+ }
+ else
+ {
+ throw new ArgumentException("Wrong Dilithium Eta!");
+ }
+
+ int buflen = PolyUniformEtaNBlocks * Symmetric.Shake128Rate;
+
+ byte[] buf = new byte[buflen];
+ ShakeDigest Shake256Digest = new ShakeDigest(256);
+
+ Symmetric.ShakeStreamInit(Shake256Digest, seed, nonce);
+ Shake256Digest.DoOutput(buf, 0, buflen);
+ ctr = RejectEta(Coeffs, 0, N, buf, buflen, eta);
+
+ while (ctr < DilithiumEngine.N)
+ {
+ Shake256Digest.DoOutput(buf, buflen, Symmetric.Shake128Rate);
+ ctr += RejectEta(Coeffs, ctr, N - ctr, buf, Symmetric.Shake128Rate, eta);
+ }
+ }
+
+ private static int RejectEta(int[] coeffs, int off, int len, byte[] buf, int buflen, int eta)
+ {
+ int ctr, pos;
+ uint t0, t1;
+
+ ctr = pos = 0;
+
+ while (ctr < len && pos < buflen)
+ {
+ t0 = (uint)(buf[pos] & 0xFF) & 0x0F;
+ t1 = (uint)(buf[pos++] & 0xFF) >> 4;
+ if (eta == 2)
+ {
+ if (t0 < 15)
+ {
+ t0 = t0 - (205 * t0 >> 10) * 5;
+ coeffs[off + ctr++] = (int)(2 - t0);
+ }
+ if (t1 < 15 && ctr < len)
+ {
+ t1 = t1 - (205 * t1 >> 10) * 5;
+ coeffs[off + ctr++] = (int)(2 - t1);
+ }
+ }
+ else if (eta == 4)
+ {
+ if (t0 < 9)
+ {
+ coeffs[off + ctr++] = (int)(4 - t0);
+ }
+ if (t1 < 9 && ctr < len)
+ {
+ coeffs[off + ctr++] = (int)(4 - t1);
+ }
+ }
+ }
+ return ctr;
+ }
+
+ public void PointwiseMontgomery(Poly v, Poly w)
+ {
+ int i;
+ for (i = 0; i < N; ++i)
+ {
+ Coeffs[i] = Reduce.MontgomeryReduce((long)((long)v.Coeffs[i] * (long)w.Coeffs[i]));
+ }
+ }
+
+ public void PointwiseAccountMontgomery(PolyVecL u, PolyVecL v)
+ {
+ int i;
+ Poly t = new Poly(Engine);
+
+ PointwiseMontgomery(u.Vec[0], v.Vec[0]);
+ //Console.Write("temp = [");
+ //for (int j = 0; j < N; ++j)
+ //{
+ // Console.Write("{0}, ", Coeffs[j]);
+ //}
+ //Console.Write("]\n");
+
+
+
+ for (i = 1; i < Engine.L; ++i)
+ {
+ t.PointwiseMontgomery(u.Vec[i], v.Vec[i]);
+ AddPoly(t);
+ }
+ //Console.Write("temp = [");
+ //for (int j = 0; j < N; ++j)
+ //{
+ // Console.Write("{0}, ", Coeffs[j]);
+ //}
+ //Console.Write("]\n");
+ }
+
+ public void AddPoly(Poly a)
+ {
+ int i;
+ for (i = 0; i < N; i++)
+ {
+ Coeffs[i] += a.Coeffs[i];
+ }
+ }
+
+ public void Subtract(Poly b)
+ {
+ for (int i = 0; i < N; ++i)
+ {
+ Coeffs[i] -= b.Coeffs[i];
+ }
+ }
+
+ public void ReducePoly()
+ {
+ for (int i = 0; i < N; ++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 < N; ++i)
+ {
+ Coeffs[i] = Reduce.ConditionalAddQ(Coeffs[i]);
+ }
+ }
+
+ public void Power2Round(Poly a)
+ {
+ for (int i = 0; i < N; ++i)
+ {
+ int[] Power2Round = Rounding.Power2Round(Coeffs[i]);
+ Coeffs[i] = Power2Round[0];
+ a.Coeffs[i] = Power2Round[1];
+ }
+ }
+
+ public void PolyT0Pack(byte[] r, int off)
+ {
+ int i;
+ int[] t = new int[8];
+ for (i = 0; i < N / 8; ++i)
+ {
+ t[0] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 0];
+ t[1] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 1];
+ t[2] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 2];
+ t[3] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 3];
+ t[4] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 4];
+ t[5] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 5];
+ t[6] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 6];
+ t[7] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 7];
+
+ r[off + 13 * i + 0] = (byte)(t[0]);
+
+ r[off + 13 * i + 1] = (byte)(t[0] >> 8);
+ r[off + 13 * i + 1] = (byte)(r[off + 13 * i + 1] | (byte)(t[1] << 5));
+ r[off + 13 * i + 2] = (byte)(t[1] >> 3);
+ r[off + 13 * i + 3] = (byte)(t[1] >> 11);
+ r[off + 13 * i + 3] = (byte)(r[off + 13 * i + 3] | (byte)(t[2] << 2));
+ r[off + 13 * i + 4] = (byte)(t[2] >> 6);
+ r[off + 13 * i + 4] = (byte)(r[off + 13 * i + 4] | (byte)(t[3] << 7));
+ r[off + 13 * i + 5] = (byte)(t[3] >> 1);
+ r[off + 13 * i + 6] = (byte)(t[3] >> 9);
+ r[off + 13 * i + 6] = (byte)(r[off + 13 * i + 6] | (byte)(t[4] << 4));
+ r[off + 13 * i + 7] = (byte)(t[4] >> 4);
+ r[off + 13 * i + 8] = (byte)(t[4] >> 12);
+ r[off + 13 * i + 8] = (byte)(r[off + 13 * i + 8] | (byte)(t[5] << 1));
+ r[off + 13 * i + 9] = (byte)(t[5] >> 7);
+ r[off + 13 * i + 9] = (byte)(r[off + 13 * i + 9] | (byte)(t[6] << 6));
+ r[off + 13 * i + 10] = (byte)(t[6] >> 2);
+ r[off + 13 * i + 11] = (byte)(t[6] >> 10);
+ r[off + 13 * i + 11] = (byte)(r[off + 13 * i + 11] | (byte)(t[7] << 3));
+ r[off + 13 * i + 12] = (byte)(t[7] >> 5);
+ }
+ }
+
+ public void PolyT0Unpack(byte[] a, int off)
+ {
+ int i;
+ for (i = 0; i < N / 8; ++i)
+ {
+ Coeffs[8 * i + 0] =
+ (
+ (a[off + 13 * i + 0] & 0xFF) |
+ ((a[off + 13 * i + 1] & 0xFF) << 8)
+ ) & 0x1FFF;
+ Coeffs[8 * i + 1] =
+ (
+ (((a[off + 13 * i + 1] & 0xFF) >> 5) |
+ ((a[off + 13 * i + 2] & 0xFF) << 3)) |
+ ((a[off + 13 * i + 3] & 0xFF) << 11)
+ ) & 0x1FFF;
+
+ Coeffs[8 * i + 2] =
+ (
+ (((a[off + 13 * i + 3] & 0xFF) >> 2) |
+ ((a[off + 13 * i + 4] & 0xFF) << 6))
+ ) & 0x1FFF;
+
+ Coeffs[8 * i + 3] =
+ (
+ (((a[off + 13 * i + 4] & 0xFF) >> 7) |
+ ((a[off + 13 * i + 5] & 0xFF) << 1)) |
+ ((a[off + 13 * i + 6] & 0xFF) << 9)
+ ) & 0x1FFF;
+
+ Coeffs[8 * i + 4] =
+ (
+ (((a[off + 13 * i + 6] & 0xFF) >> 4) |
+ ((a[off + 13 * i + 7] & 0xFF) << 4)) |
+ ((a[off + 13 * i + 8] & 0xFF) << 12)
+ ) & 0x1FFF;
+
+ Coeffs[8 * i + 5] =
+ (
+ (((a[off + 13 * i + 8] & 0xFF) >> 1) |
+ ((a[off + 13 * i + 9] & 0xFF) << 7))
+ ) & 0x1FFF;
+
+ Coeffs[8 * i + 6] =
+ (
+ (((a[off + 13 * i + 9] & 0xFF) >> 6) |
+ ((a[off + 13 * i + 10] & 0xFF) << 2)) |
+ ((a[off + 13 * i + 11] & 0xFF) << 10)
+ ) & 0x1FFF;
+
+ Coeffs[8 * i + 7] =
+ (
+ ((a[off + 13 * i + 11] & 0xFF) >> 3 |
+ ((a[off + 13 * i + 12] & 0xFF) << 5))
+ ) & 0x1FFF;
+
+
+ Coeffs[8 * i + 0] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 0];
+ Coeffs[8 * i + 1] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 1];
+ Coeffs[8 * i + 2] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 2];
+ Coeffs[8 * i + 3] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 3];
+ Coeffs[8 * i + 4] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 4];
+ Coeffs[8 * i + 5] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 5];
+ Coeffs[8 * i + 6] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 6];
+ Coeffs[8 * i + 7] = (1 << (DilithiumEngine.D - 1)) - Coeffs[8 * i + 7];
+ }
+ }
+
+ public void PolyT1Pack(byte[] r, int off)
+ {
+ for (int i = 0; i < N / 4; ++i)
+ {
+ r[off + 5 * i + 0] = (byte)(Coeffs[4 * i + 0] >> 0);
+ r[off + 5 * i + 1] = (byte)((Coeffs[4 * i + 0] >> 8) | (Coeffs[4 * i + 1] << 2));
+ r[off + 5 * i + 2] = (byte)((Coeffs[4 * i + 1] >> 6) | (Coeffs[4 * i + 2] << 4));
+ r[off + 5 * i + 3] = (byte)((Coeffs[4 * i + 2] >> 4) | (Coeffs[4 * i + 3] << 6));
+ r[off + 5 * i + 4] = (byte)(Coeffs[4 * i + 3] >> 2);
+ }
+ }
+
+ public void PolyT1Unpack(byte[] a, int off)
+ {
+ int i;
+
+ for (i = 0; i < N / 4; ++i)
+ {
+ Coeffs[4 * i + 0] = (((a[off + 5 * i + 0] & 0xFF) >> 0) | ((int)(a[off + 5 * i + 1] & 0xFF) << 8)) & 0x3FF;
+ Coeffs[4 * i + 1] = (((a[off + 5 * i + 1] & 0xFF) >> 2) | ((int)(a[off + 5 * i + 2] & 0xFF) << 6)) & 0x3FF;
+ Coeffs[4 * i + 2] = (((a[off + 5 * i + 2] & 0xFF) >> 4) | ((int)(a[off + 5 * i + 3] & 0xFF) << 4)) & 0x3FF;
+ Coeffs[4 * i + 3] = (((a[off + 5 * i + 3] & 0xFF) >> 6) | ((int)(a[off + 5 * i + 4] & 0xFF) << 2)) & 0x3FF;
+ }
+ }
+
+ public void PolyEtaPack(byte[] r, int off)
+ {
+ int i;
+ byte[] t = new byte[8];
+
+ if (Engine.Eta == 2)
+ {
+ for (i = 0; i < N / 8; ++i)
+ {
+ t[0] = (byte)(Engine.Eta - Coeffs[8 * i + 0]);
+ t[1] = (byte)(Engine.Eta - Coeffs[8 * i + 1]);
+ t[2] = (byte)(Engine.Eta - Coeffs[8 * i + 2]);
+ t[3] = (byte)(Engine.Eta - Coeffs[8 * i + 3]);
+ t[4] = (byte)(Engine.Eta - Coeffs[8 * i + 4]);
+ t[5] = (byte)(Engine.Eta - Coeffs[8 * i + 5]);
+ t[6] = (byte)(Engine.Eta - Coeffs[8 * i + 6]);
+ t[7] = (byte)(Engine.Eta - Coeffs[8 * i + 7]);
+
+ r[off + 3 * i + 0] = (byte)((t[0] >> 0) | (t[1] << 3) | (t[2] << 6));
+ r[off + 3 * i + 1] = (byte)((t[2] >> 2) | (t[3] << 1) | (t[4] << 4) | (t[5] << 7));
+ r[off + 3 * i + 2] = (byte)((t[5] >> 1) | (t[6] << 2) | (t[7] << 5));
+ }
+ }
+ else if (Engine.Eta == 4)
+ {
+ for (i = 0; i < N / 2; ++i)
+ {
+ t[0] = (byte)(Engine.Eta - Coeffs[2 * i + 0]);
+ t[1] = (byte)(Engine.Eta - Coeffs[2 * i + 1]);
+ r[off + i] = (byte)(t[0] | t[1] << 4);
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Eta needs to be 2 or 4!");
+ }
+ }
+
+ public void PolyEtaUnpack(byte[] a, int off)
+ {
+ int i, eta = Engine.Eta;
+
+ if (eta == 2)
+ {
+ for (i = 0; i < N / 8; ++i)
+ {
+ Coeffs[8 * i + 0] = (((a[off + 3 * i + 0] & 0xFF) >> 0) & 7);
+ Coeffs[8 * i + 1] = ((((a[off + 3 * i + 0] & 0xFF) >> 3)) & 7);
+ Coeffs[8 * i + 2] = (((a[off + 3 * i + 0] & 0xFF) >> 6) | ((a[off + 3 * i + 1] & 0xFF) << 2) & 7);
+ Coeffs[8 * i + 3] = ((((a[off + 3 * i + 1] & 0xFF) >> 1)) & 7);
+ Coeffs[8 * i + 4] = ((((a[off + 3 * i + 1] & 0xFF) >> 4)) & 7);
+ Coeffs[8 * i + 5] = (((a[off + 3 * i + 1] & 0xFF) >> 7) | ((a[off + 3 * i + 2] & 0xFF) << 1) & 7);
+ Coeffs[8 * i + 6] = ((((a[off + 3 * i + 2] & 0xFF) >> 2)) & 7);
+ Coeffs[8 * i + 7] = ((((a[off + 3 * i + 2] & 0xFF) >> 5)) & 7);
+
+ Coeffs[8 * i + 0] = eta - Coeffs[8 * i + 0];
+ Coeffs[8 * i + 1] = eta - Coeffs[8 * i + 1];
+ Coeffs[8 * i + 2] = eta - Coeffs[8 * i + 2];
+ Coeffs[8 * i + 3] = eta - Coeffs[8 * i + 3];
+ Coeffs[8 * i + 4] = eta - Coeffs[8 * i + 4];
+ Coeffs[8 * i + 5] = eta - Coeffs[8 * i + 5];
+ Coeffs[8 * i + 6] = eta - Coeffs[8 * i + 6];
+ Coeffs[8 * i + 7] = eta - Coeffs[8 * i + 7];
+ }
+ }
+ else if (eta == 4)
+ {
+ for (i = 0; i < N / 2; ++i)
+ {
+ Coeffs[2 * i + 0] = ((a[off + i] & 0xFF) & 0x0F);
+ Coeffs[2 * i + 1] = ((a[off + i] & 0xFF) >> 4);
+ Coeffs[2 * i + 0] = eta - Coeffs[2 * i + 0];
+ Coeffs[2 * i + 1] = eta - Coeffs[2 * i + 1];
+ }
+ }
+ }
+
+ public void UniformGamma1(byte[] seed, ushort nonce)
+ {
+ byte[] buf = new byte[Engine.PolyUniformGamma1NBytes * Symmetric.Shake256Rate];
+ ShakeDigest ShakeDigest256 = new ShakeDigest(256);
+ Symmetric.ShakeStreamInit(ShakeDigest256, seed, nonce);
+ ShakeDigest256.DoFinal(buf, 0, buf.Length);
+ UnpackZ(buf, 0);
+
+ }
+
+ public void PackZ(byte[] r, int offset)
+ {
+ int i;
+ uint[] t = new uint[4];
+ if (Engine.Gamma1 == (1 << 17))
+ {
+ for (i = 0; i < N / 4; ++i)
+ {
+ t[0] = (uint)(Engine.Gamma1 - Coeffs[4 * i + 0]);
+ t[1] = (uint)(Engine.Gamma1 - Coeffs[4 * i + 1]);
+ t[2] = (uint)(Engine.Gamma1 - Coeffs[4 * i + 2]);
+ t[3] = (uint)(Engine.Gamma1 - Coeffs[4 * i + 3]);
+
+ r[offset + 9 * i + 0] = (byte)t[0];
+ r[offset + 9 * i + 1] = (byte)(t[0] >> 8);
+ r[offset + 9 * i + 2] = (byte)((byte)(t[0] >> 16) | (t[1] << 2));
+ r[offset + 9 * i + 3] = (byte)(t[1] >> 6);
+ r[offset + 9 * i + 4] = (byte)((byte)(t[1] >> 14) | (t[2] << 4));
+ r[offset + 9 * i + 5] = (byte)(t[2] >> 4);
+ r[offset + 9 * i + 6] = (byte)((byte)(t[2] >> 12) | (t[3] << 6));
+ r[offset + 9 * i + 7] = (byte)(t[3] >> 2);
+ r[offset + 9 * i + 8] = (byte)(t[3] >> 10);
+ }
+ }
+ else if (Engine.Gamma1 == (1 << 19))
+ {
+ for (i = 0; i < N / 2; ++i)
+ {
+ t[0] = (uint)(Engine.Gamma1 - Coeffs[2 * i + 0]);
+ t[1] = (uint)(Engine.Gamma1 - Coeffs[2 * i + 1]);
+
+ r[offset + 5 * i + 0] = (byte)t[0];
+ r[offset + 5 * i + 1] = (byte)(t[0] >> 8);
+ r[offset + 5 * i + 2] = (byte)((byte)(t[0] >> 16) | (t[1] << 4));
+ r[offset + 5 * i + 3] = (byte)(t[1] >> 4);
+ r[offset + 5 * i + 4] = (byte)(t[1] >> 12);
+
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Wrong Dilithium Gamma1!");
+ }
+ }
+
+ public void UnpackZ(byte[] a, int off)
+ {
+ int i;
+ if (Engine.Gamma1 == (1 << 17))
+ {
+ for (i = 0; i < N / 4; ++i)
+ {
+ Coeffs[4 * i + 0] =
+ (
+ (((a[off + 9 * i + 0] & 0xFF)) |
+ ((a[off + 9 * i + 1] & 0xFF) << 8)) |
+ ((a[off + 9 * i + 2] & 0xFF) << 16)
+ ) & 0x3FFFF;
+ Coeffs[4 * i + 1] =
+ (
+ (((a[off + 9 * i + 2] & 0xFF) >> 2) |
+ ((a[off + 9 * i + 3] & 0xFF) << 6)) |
+ ((a[off + 9 * i + 4] & 0xFF) << 14)
+ ) & 0x3FFFF;
+ Coeffs[4 * i + 2] =
+ (
+ (((a[off + 9 * i + 4] & 0xFF) >> 4) |
+ ((a[off + 9 * i + 5] & 0xFF) << 4)) |
+ ((a[off + 9 * i + 6] & 0xFF) << 12)
+ ) & 0x3FFFF;
+ Coeffs[4 * i + 3] =
+ (
+ (((a[off + 9 * i + 6] & 0xFF) >> 6) |
+ ((a[off + 9 * i + 7] & 0xFF) << 2)) |
+ ((a[off + 9 * i + 8] & 0xFF) << 10)
+ ) & 0x3FFFF;
+
+
+ Coeffs[4 * i + 0] = Engine.Gamma1 - Coeffs[4 * i + 0];
+ Coeffs[4 * i + 1] = Engine.Gamma1 - Coeffs[4 * i + 1];
+ Coeffs[4 * i + 2] = Engine.Gamma1 - Coeffs[4 * i + 2];
+ Coeffs[4 * i + 3] = Engine.Gamma1 - Coeffs[4 * i + 3];
+ }
+ }
+ else if (Engine.Gamma1 == (1 << 19))
+ {
+ for (i = 0; i < N / 2; ++i)
+ {
+ Coeffs[2 * i + 0] =
+ (
+ (((a[off + 5 * i + 0] & 0xFF)) |
+ ((a[off + 5 * i + 1] & 0xFF) << 8)) |
+ ((a[off + 5 * i + 2] & 0xFF) << 16)
+ ) & 0xFFFFF;
+ Coeffs[2 * i + 1] =
+ (
+ (((a[off + 5 * i + 2] & 0xFF) >> 4) |
+ ((a[off + 5 * i + 3] & 0xFF) << 4)) |
+ ((a[off + 5 * i + 4] & 0xFF) << 12)
+ ) & 0xFFFFF;
+
+ Coeffs[2 * i + 0] = Engine.Gamma1 - Coeffs[2 * i + 0];
+ Coeffs[2 * i + 1] = Engine.Gamma1 - Coeffs[2 * i + 1];
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Wrong Dilithiumn Gamma1!");
+ }
+ }
+
+ public void Decompose(Poly a)
+ {
+ int i;
+ for (i = 0; i < N; ++i)
+ {
+ int[] decomp = Rounding.Decompose(Coeffs[i], Engine.Gamma2);
+ a.Coeffs[i] = decomp[0];
+ Coeffs[i] = decomp[1];
+ }
+ }
+
+ public void PackW1(byte[] r, int off)
+ {
+ int i;
+
+ if (Engine.Gamma2 == (DilithiumEngine.Q - 1) / 88)
+ {
+ for (i = 0; i < N / 4; ++i)
+ {
+ r[off + 3 * i + 0] = (byte)(((byte)Coeffs[4 * i + 0]) | (Coeffs[4 * i + 1] << 6));
+ r[off + 3 * i + 1] = (byte)((byte)(Coeffs[4 * i + 1] >> 2) | (Coeffs[4 * i + 2] << 4));
+ r[off + 3 * i + 2] = (byte)((byte)(Coeffs[4 * i + 2] >> 4) | (Coeffs[4 * i + 3] << 2));
+ }
+ }
+ else if (Engine.Gamma2 == (DilithiumEngine.Q - 1) / 32)
+ {
+ for (i = 0; i < N / 2; ++i)
+ {
+ r[off + i] = (byte)(Coeffs[2 * i + 0] | (Coeffs[2 * i + 1] << 4));
+ }
+ }
+ }
+
+ public void Challenge(byte[] seed)
+ {
+ int i, b, pos;
+ ulong signs;
+ byte[] buf = new byte[Symmetric.Shake256Rate];
+
+ ShakeDigest ShakeDigest256 = new ShakeDigest(256);
+ ShakeDigest256.BlockUpdate(seed, 0, DilithiumEngine.SeedBytes);
+ ShakeDigest256.DoOutput(buf, 0, Symmetric.Shake256Rate);
+
+ signs = 0;
+ for (i = 0; i < 8; ++i)
+ {
+ signs |= (ulong)(buf[i] & 0xFF) << 8 * i;
+ }
+
+ pos = 8;
+
+ for (i = 0; i < N; ++i)
+ {
+ Coeffs[i] = 0;
+ }
+
+ for (i = N - Engine.Tau; i < N; ++i)
+ {
+ do
+ {
+ if (pos >= Symmetric.Shake256Rate)
+ {
+ ShakeDigest256.DoOutput(buf, 0, Symmetric.Shake256Rate);
+ pos = 0;
+ }
+ b = (buf[pos++] & 0xFF);
+ }
+ while (b > i);
+
+ Coeffs[i] = Coeffs[b];
+ Coeffs[b] = (int)(1 - 2 * (signs & 1));
+ signs = signs >> 1;
+ }
+ }
+
+ public bool CheckNorm(int B)
+ {
+ int i, t;
+
+ if (B > (DilithiumEngine.Q - 1) / 8)
+ {
+ return true;
+ }
+
+ for (i = 0; i < N; ++i)
+ {
+ t = Coeffs[i] >> 31;
+ t = Coeffs[i] - (t & 2 * Coeffs[i]);
+
+ if (t >= B)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ public int PolyMakeHint(Poly a0, Poly a1)
+ {
+ int i, s = 0;
+
+ for (i = 0; i < N; ++i)
+ {
+ Coeffs[i] = Rounding.MakeHint(a0.Coeffs[i], a1.Coeffs[i], Engine);
+ s += Coeffs[i];
+ }
+ return s;
+ }
+
+ public void PolyUseHint(Poly a, Poly h)
+ {
+ for (int i = 0; i < DilithiumEngine.N; ++i)
+ {
+ Coeffs[i] = Rounding.UseHint(a.Coeffs[i], h.Coeffs[i], Engine.Gamma2);
+ }
+ }
+
+ public void ShiftLeft()
+ {
+ for (int i = 0; i < N; ++i)
+ {
+ Coeffs[i] <<= DilithiumEngine.D;
+ }
+ }
+
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/PolyVecK.cs b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecK.cs
new file mode 100644
index 000000000..506200936
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecK.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ internal class PolyVecK
+ {
+ public Poly[] Vec;
+ private DilithiumEngine Engine;
+ private int Mode;
+ private int PolyVecBytes;
+ private int K;
+ private int L;
+
+ public PolyVecK(DilithiumEngine Engine)
+ {
+ this.Engine = Engine;
+ Mode = Engine.Mode;
+ K = Engine.K;
+ L = Engine.L;
+ Vec = new Poly[K];
+
+ for (int i = 0; i < K; i++)
+ {
+ Vec[i] = new Poly(Engine);
+ }
+ }
+
+ public void UniformEta(byte[] seed, ushort nonce)
+ {
+ int i;
+ ushort n = nonce;
+ for (i = 0; i < K; i++)
+ {
+ Vec[i].UniformEta(seed, n++);
+ }
+ }
+
+ public void Reduce()
+ {
+ for (int i = 0; i < K; i++)
+ {
+ Vec[i].ReducePoly();
+ }
+ }
+
+ public void Ntt()
+ {
+ for (int i= 0; i < K; ++i)
+ {
+ Vec[i].PolyNtt();
+ }
+ }
+
+ public void InverseNttToMont()
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ Vec[i].InverseNttToMont();
+ }
+ }
+
+ public void AddPolyVecK(PolyVecK b)
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ Vec[i].AddPoly(b.Vec[i]);
+ }
+ }
+
+ public void Subtract(PolyVecK v)
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ Vec[i].Subtract(v.Vec[i]);
+ }
+ }
+
+ public void ConditionalAddQ()
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ Vec[i].ConditionalAddQ();
+ }
+ }
+
+ public void Power2Round(PolyVecK v)
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ Vec[i].Power2Round(v.Vec[i]);
+ }
+ }
+
+ public void Decompose(PolyVecK v)
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ Vec[i].Decompose(v.Vec[i]);
+ }
+ }
+
+ public void PackW1(byte[] r)
+ {
+ int i;
+ for (i = 0; i < K; i++)
+ {
+ Vec[i].PackW1(r, i * Engine.PolyW1PackedBytes);
+ }
+ }
+
+ public void PointwisePolyMontgomery(Poly a, PolyVecK v)
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ Vec[i].PointwiseMontgomery(a, v.Vec[i]);
+ }
+ }
+
+ public bool CheckNorm(int bound)
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ if (Vec[i].CheckNorm(bound))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int MakeHint(PolyVecK v0, PolyVecK v1)
+ {
+ int i, s = 0;
+ for (i = 0; i < K; ++i)
+ {
+ s += Vec[i].PolyMakeHint(v0.Vec[i], v1.Vec[i]);
+ }
+
+ return s;
+ }
+
+ public void UseHint(PolyVecK a, PolyVecK h)
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ Vec[i].PolyUseHint(a.Vec[i], h.Vec[i]);
+ }
+ }
+
+ public void ShiftLeft()
+ {
+ for (int i = 0; i < K; ++i)
+ {
+ Vec[i].ShiftLeft();
+ }
+ }
+
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/PolyVecL.cs b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecL.cs
new file mode 100644
index 000000000..75309b374
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecL.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ internal class PolyVecL
+ {
+ public Poly[] Vec;
+ private DilithiumEngine Engine;
+ private int Mode;
+ private int PolyVecBytes;
+ private int L;
+ private int K;
+
+ public PolyVecL(DilithiumEngine Engine)
+ {
+ this.Engine = Engine;
+ Mode = Engine.Mode;
+ L = Engine.L;
+ K = Engine.K;
+ Vec = new Poly[L];
+ for (int i = 0; i < L; i++)
+ {
+ Vec[i] = new Poly(Engine);
+ }
+ }
+
+ public void UniformEta(byte[] seed, ushort nonce)
+ {
+ int i;
+ for (i = 0; i < L; i++)
+ {
+ Vec[i].UniformEta(seed, nonce++);
+ }
+ }
+
+ public void CopyPolyVecL(PolyVecL OutPoly)
+ {
+ for (int i = 0; i < L; i++)
+ {
+ for (int j = 0; j < DilithiumEngine.N; j++)
+ {
+ OutPoly.Vec[i].Coeffs[j] = Vec[i].Coeffs[j];
+ }
+ }
+ }
+
+ public void InverseNttToMont()
+ {
+ for (int i = 0; i < L; i++)
+ {
+ Vec[i].InverseNttToMont();
+ }
+ }
+
+ public void Ntt()
+ {
+ for (int i = 0; i < L; i++)
+ {
+ Vec[i].PolyNtt();
+ }
+ }
+
+ public void UniformGamma1(byte[] seed, ushort nonce)
+ {
+ for (int i = 0; i < L; i++)
+ {
+ Vec[i].UniformGamma1(seed, (ushort)(L * nonce + i));
+ }
+ }
+
+ public void PointwisePolyMontgomery(Poly a, PolyVecL v)
+ {
+ for (int i = 0; i < L; ++i)
+ {
+ Vec[i].PointwiseMontgomery(a, v.Vec[i]);
+ }
+ }
+
+ public void AddPolyVecL(PolyVecL b)
+ {
+ for (int i = 0; i < L; i++)
+ {
+ Vec[i].AddPoly(b.Vec[i]);
+ }
+ }
+
+ public void Reduce()
+ {
+ for (int i = 0; i < L; i++)
+ {
+ Vec[i].ReducePoly();
+ }
+ }
+
+ public bool CheckNorm(int bound)
+ {
+ for (int i = 0; i < L; ++i)
+ {
+ if (Vec[i].CheckNorm(bound))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/PolyVecMatrix.cs b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecMatrix.cs
new file mode 100644
index 000000000..4f3868239
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/PolyVecMatrix.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ internal class PolyVecMatrix
+ {
+ private int K, L;
+ public PolyVecL[] Matrix;
+
+ public PolyVecMatrix(DilithiumEngine Engine)
+ {
+ K = Engine.K;
+ L = Engine.L;
+ Matrix = new PolyVecL[K];
+
+ for (int i = 0; i < K; i++)
+ {
+ Matrix[i] = new PolyVecL(Engine);
+ }
+ }
+
+ public void ExpandMatrix(byte[] rho)
+ {
+ int i, j;
+ for (i = 0; i < K; ++i)
+ {
+ for (j = 0; j < L; ++j)
+ {
+ Matrix[i].Vec[j].UniformBlocks(rho, (ushort)((ushort) (i << 8) + j));
+ }
+ }
+ }
+
+ public void PointwiseMontgomery(PolyVecK t, PolyVecL v)
+ {
+ int i;
+ for (i = 0; i < K; ++i)
+ {
+ t.Vec[i].PointwiseAccountMontgomery(Matrix[i], v);
+ }
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/Reduce.cs b/crypto/src/pqc/crypto/crystals/dilithium/Reduce.cs
new file mode 100644
index 000000000..4efcdb49e
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/Reduce.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ internal class Reduce
+ {
+ public static int MontgomeryReduce(long a)
+ {
+ int t;
+ t = (int)(a * DilithiumEngine.QInv);
+ t = (int)((a - (long)((long)t * (long)DilithiumEngine.Q)) >> 32);
+ //Console.Write("{0}, ", t);
+ return t;
+ }
+
+ public static int Reduce32(int a)
+ {
+ int t;
+ t = (a + (1 << 22)) >> 23;
+ t = a - t * DilithiumEngine.Q;
+ return t;
+ }
+
+ public static int ConditionalAddQ(int a)
+ {
+ a += (a >> 31) & DilithiumEngine.Q;
+ return a;
+ }
+
+
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/Rounding.cs b/crypto/src/pqc/crypto/crystals/dilithium/Rounding.cs
new file mode 100644
index 000000000..625b60cf5
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/Rounding.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ internal class Rounding
+ {
+ public static int[] Power2Round(int a)
+ {
+ int[] r = new int[2];
+
+ r[0] = (a + (1 << (DilithiumEngine.D - 1)) - 1) >> DilithiumEngine.D;
+ r[1] = a - (r[0] << DilithiumEngine.D);
+ return r;
+ }
+
+ public static int[] Decompose(int a, int gamma2)
+ {
+ int a0, a1;
+ a1 = (a + 127) >> 7;
+ if (gamma2 == (DilithiumEngine.Q - 1) / 32)
+ {
+ a1 = (a1 * 1025 + (1 << 21)) >> 22;
+ a1 &= 15;
+ }
+ else if (gamma2 == (DilithiumEngine.Q - 1) / 88)
+ {
+ a1 = (a1 * 11275 + (1 << 23)) >> 24;
+ a1 ^= ((43 - a1) >> 31) & a1;
+ }
+ else
+ {
+ throw new ArgumentException("Wrong Gamma2!");
+ }
+
+ a0 = a - a1 * 2 * gamma2;
+ a0 -= (((DilithiumEngine.Q - 1) / 2 - a0) >> 31) & DilithiumEngine.Q;
+ return new int[] { a0, a1 };
+ }
+
+ public static int MakeHint(int a0, int a1, DilithiumEngine engine)
+ {
+ int g2 = engine.Gamma2, q = DilithiumEngine.Q;
+ if (a0 <= g2 || a0 > q - g2 || (a0 == q - g2 && a1 == 0))
+ {
+ return 0;
+ }
+ return 1;
+ }
+
+ public static int UseHint(int a, int hint, int gamma2)
+ {
+ int a0, a1;
+
+ int[] intArray = Decompose(a, gamma2);
+ a0 = intArray[0];
+ a1 = intArray[1];
+
+ if (hint == 0)
+ {
+ return a1;
+ }
+
+ if (gamma2 == (DilithiumEngine.Q - 1) / 32)
+ {
+ if (a0 > 0)
+ {
+ return (a1 + 1) & 15;
+ }
+ else
+ {
+ return (a1 - 1) & 15;
+ }
+ }
+ else if (gamma2 == (DilithiumEngine.Q - 1) / 88)
+ {
+ if (a0 > 0)
+ {
+ return (a1 == 43) ? 0 : a1 + 1;
+ }
+ else
+ {
+ return (a1 == 0) ? 43 : a1 - 1;
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Wrong Gamma2!");
+ }
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/dilithium/Symmetric.cs b/crypto/src/pqc/crypto/crystals/dilithium/Symmetric.cs
new file mode 100644
index 000000000..46d59a667
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/dilithium/Symmetric.cs
@@ -0,0 +1,26 @@
+using Org.BouncyCastle.Crypto.Digests;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
+{
+ static class Symmetric
+ {
+ public const int Shake128Rate = 168;
+ public const int Shake256Rate = 136;
+ public const int Sha3Rate256 = 136;
+ public const int Sha3Rate512 = 72;
+
+ public static void ShakeStreamInit(ShakeDigest Digest, byte[] seed, ushort nonce)
+ {
+ byte[] temp = new byte[2];
+ temp[0] = (byte)nonce;
+ temp[1] = (byte)(nonce >> 8);
+ Digest.BlockUpdate(seed, 0, seed.Length);
+ Digest.BlockUpdate(temp, 0, temp.Length);
+ }
+ }
+}
|