summary refs log tree commit diff
path: root/crypto/src/pqc
diff options
context:
space:
mode:
authorDavid Hook <dgh@cryptoworkshop.com>2022-09-14 05:28:35 +1000
committerDavid Hook <dgh@cryptoworkshop.com>2022-09-14 05:28:35 +1000
commit2d8f553417bcfb1b7068405292f81518a3d51a11 (patch)
tree21d75d8c8fd9d2f26821d9ab6b69827a58aabad7 /crypto/src/pqc
parentUse nullable DateTime instead of MinValue (diff)
downloadBouncyCastle.NET-ed25519-2d8f553417bcfb1b7068405292f81518a3d51a11.tar.xz
first cut of Dilithium
Diffstat (limited to 'crypto/src/pqc')
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumEngine.cs547
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyGenerationParameters.cs18
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyPairGenerator.cs35
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumKeyParameters.cs18
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumParameters.cs28
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumPrivateKeyParameters.cs22
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumPublicKeyParameters.cs24
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/DilithiumSigner.cs60
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/Ntt.cs98
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/Packing.cs177
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/Poly.cs695
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/PolyVecK.cs163
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/PolyVecL.cs111
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/PolyVecMatrix.cs47
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/Reduce.cs36
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/Rounding.cs95
-rw-r--r--crypto/src/pqc/crypto/crystals/dilithium/Symmetric.cs26
17 files changed, 2200 insertions, 0 deletions
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);
+        }
+    }
+}