summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Hook <dgh@cryptoworkshop.com>2022-07-22 14:39:25 +1000
committerDavid Hook <dgh@cryptoworkshop.com>2022-07-22 14:39:25 +1000
commit4602a2852a5b8543d0886b759dc1b43c70628e86 (patch)
tree1d66ab84db79948703f2f7f926428023913c84fe
parentadded NTRU Prime ack (diff)
downloadBouncyCastle.NET-ed25519-4602a2852a5b8543d0886b759dc1b43c70628e86.tar.xz
initial NTRU Prime
-rw-r--r--crypto/src/pqc/crypto/ntrup/NtruPEngine.cs1284
-rw-r--r--crypto/src/pqc/crypto/ntrup/NtruPKemExtractor.cs35
-rw-r--r--crypto/src/pqc/crypto/ntrup/NtruPKemGenerator.cs77
-rw-r--r--crypto/src/pqc/crypto/ntrup/NtruPKeyGenerationParameters.cs18
-rw-r--r--crypto/src/pqc/crypto/ntrup/NtruPKeyPairGenerator.cs69
-rw-r--r--crypto/src/pqc/crypto/ntrup/NtruPKeyParameters.cs17
-rw-r--r--crypto/src/pqc/crypto/ntrup/NtruPParameters.cs72
-rw-r--r--crypto/src/pqc/crypto/ntrup/NtruPPrivateKeyParameters.cs21
-rw-r--r--crypto/src/pqc/crypto/ntrup/NtruPPublicKeyParameters.cs21
-rw-r--r--crypto/test/src/pqc/crypto/test/NtruPVectorTest.cs150
10 files changed, 1764 insertions, 0 deletions
diff --git a/crypto/src/pqc/crypto/ntrup/NtruPEngine.cs b/crypto/src/pqc/crypto/ntrup/NtruPEngine.cs
new file mode 100644
index 000000000..f78abbf7c
--- /dev/null
+++ b/crypto/src/pqc/crypto/ntrup/NtruPEngine.cs
@@ -0,0 +1,1284 @@
+using System;
+using System.Collections.Generic;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.pqc.crypto.NtruP
+{
+    internal class NtruPEngine
+    {
+        // Key Sizes
+        private readonly int _skBytes; // [KEM] Size of secret key
+        private readonly int _pkBytes; // [KEM] Size of public key
+        private readonly int _ctBytes; // [KEM] Size of ciphertext
+
+        private readonly int _secretKeyBytes; // C Reference: SecretKey_bytes
+        private readonly int _publicKeyBytes; // C Reference: PublicKey_bytes
+        private readonly int _ciphertextsBytes; // C Reference: Ciphertext_bytes
+        
+        private readonly int _confirmBytes;
+        private readonly int _inputsBytes; // For encoding _I-bit inputs
+        private readonly int _topBytes;
+        private readonly int _seedBytes;
+        private readonly int _smallBytes;
+        private readonly int _hashBytes;
+        
+        private const int SessionKeyBytes = 32;
+
+        // Parameters for NTRU
+        private readonly int _p;
+        private readonly int _q;
+
+        private readonly int _roundedBytes; // Defined in params.h
+
+        // private readonly int _rqBytes; // Defined in params.h - Public key bytes for streamlined nTRU
+        private readonly bool _lpr;
+        private readonly int _w;
+        private readonly int _tau0;
+        private readonly int _tau1;
+        private readonly int _tau2;
+        private readonly int _tau3;
+
+        private readonly int _I;
+
+        private readonly int _q12;
+
+        public int PrivateKeySize => _skBytes;
+        public int PublicKeySize => _pkBytes;
+        public int CipherTextSize => _ctBytes;
+        public int SessionKeySize => SessionKeyBytes;
+        
+        public NtruPEngine(int p, int q, bool lpr, int w, int tau0,
+            int tau1, int tau2, int tau3, int skBytes, int pkBytes, int ctBytes, int roundedBytes, int rqBytes)
+        {
+            this._p = p;
+            this._q = q;
+            this._w = w;
+            this._tau0 = tau0;
+            this._tau1 = tau1;
+            this._tau2 = tau2;
+            this._tau3 = tau3;
+
+            // KEM parameters
+            this._roundedBytes = roundedBytes;
+            this._skBytes = skBytes;
+            this._pkBytes = pkBytes;
+            this._ctBytes = ctBytes;
+            
+            this._lpr = lpr;
+
+            this._confirmBytes = 32;
+
+            _smallBytes = ((p + 3) / 4);
+            _q12 = ((q - 1) / 2);
+            _hashBytes = 32;
+
+            if (lpr)
+            {
+                _seedBytes = 32;
+                _I = 256;
+                _inputsBytes = _I / 8;
+                _topBytes = _I / 2;
+                _ciphertextsBytes = roundedBytes + _topBytes;
+                _secretKeyBytes = _smallBytes;
+                _publicKeyBytes = _seedBytes + roundedBytes;
+            }
+            else
+            {
+                _inputsBytes = _smallBytes;
+                _ciphertextsBytes = _roundedBytes;
+                _secretKeyBytes = (2*_smallBytes);
+                _publicKeyBytes = rqBytes;
+            }
+        }
+
+        
+        public void kem_keypair(byte[] pk, byte[] sk, SecureRandom random) //KEM_KeyGen
+        {
+            
+            KeyGen(random, ref pk, ref sk);
+            Array.Copy(pk, 0, sk, _secretKeyBytes, _publicKeyBytes);
+            byte[] rand = new byte[_inputsBytes];
+            random.NextBytes(rand);
+            Array.Copy(rand, 0, sk, _secretKeyBytes+_publicKeyBytes, _inputsBytes);
+            HashPrefix(ref sk, 4, ref pk, _publicKeyBytes);
+        }
+
+      
+        public void kem_enc(byte[] ct, byte[] ss, byte[] pk, SecureRandom random)
+        {
+            sbyte[] inputs;
+
+            if (_lpr)
+            {
+                inputs = new sbyte[_I];
+            }
+            else
+            {
+                inputs = new sbyte[_p];
+            }
+            
+            byte[] r_enc = new byte[_inputsBytes];
+            byte[] cache = new byte[_hashBytes];
+            
+            HashPrefix(ref cache, 4, ref pk, _publicKeyBytes);
+            
+            if (_lpr)
+            {
+                InputsRandom(ref inputs, random);
+            }
+            else
+            {
+                ShortRandom(ref inputs, random);
+            }
+            
+            Hide(ref ct, r_enc, inputs, pk, cache);
+            HashSession(ref ss, 1, r_enc, ct);
+        }
+
+        public void kem_dec(byte[] ss, byte[] ct, byte[] sk)
+        {
+            byte[] pk = new byte[sk.Length - _secretKeyBytes];
+
+            // Shift sk by _secretKeyBytes and copy to pk
+
+            Array.Copy(sk, _secretKeyBytes, pk, 0, pk.Length);
+
+            byte[] rho = new byte[pk.Length - _publicKeyBytes];
+
+            // Shift pk by _publicKeyBytes and fill rho
+            Array.Copy(pk, _publicKeyBytes, rho, 0, rho.Length);
+
+            byte[] cache = new byte[rho.Length - _inputsBytes];
+
+            // Shift rho by _inputsBytes and fill cache
+
+            Array.Copy(rho, _inputsBytes, cache, 0, cache.Length);
+            
+            sbyte[] r;
+
+            if (_lpr)
+            {
+                r = new sbyte[_I];
+            }
+            else
+            {
+                r = new sbyte[_p];
+            }
+            
+            byte[] r_enc = new byte[_inputsBytes];
+            byte[] cnew = new byte[_ciphertextsBytes + _confirmBytes];
+
+            byte[] ct_dec = Arrays.Clone(ct); // ct somehow gets modified in Decrypt
+            
+            Decrypt(ref r, ct_dec, sk);
+
+            Hide(ref cnew, r_enc, r, pk, cache);
+            
+            int mask = ctDiffMask(ct, cnew);
+            
+            for (int i = 0; i < _inputsBytes; ++i)
+            {
+                r_enc[i] ^= (byte)(mask & (r_enc[i] ^ rho[i]));
+            }
+            
+            HashSession(ref ss, 1 + mask, r_enc, ct);
+        }
+
+        // ---------------------------------------------------------------------    
+
+        private void KeyGen(SecureRandom random, ref byte[] pk, ref byte[] sk) // ZKeyGen
+        {
+            if (_lpr)
+            {
+                short[] A = new short[_p];
+                sbyte[] a = new sbyte[_p];
+                
+                // BEGIN: XKeyGen
+                byte[] seedOut = new byte[_seedBytes];
+                random.NextBytes(seedOut);
+                Array.Copy(seedOut, 0, pk, 0, _seedBytes);
+                short[] genOut = new short[_p];
+                Generator(ref genOut, seedOut);
+            
+                // BEGIN: XKeyGen > KeyGen
+                ShortRandom(ref a, random);
+                short[] aG = new short[_p];
+                RqMult(ref aG,  genOut, ref a);
+                Round(ref A, aG);
+                // END XKeyGen > KeyGen
+                // END: XKeyGen
+
+                byte[] roundedEncOut = new byte[pk.Length];
+                RoundedEncode(ref roundedEncOut, A);
+
+                Array.Copy(roundedEncOut, 0, pk, _seedBytes, pk.Length - _seedBytes);
+
+                ByteEncode(ref sk, a);
+            }
+            else
+            {
+                // KeyGen
+                short[] h = new short[_p];
+                sbyte[] f = new sbyte[_p];
+                
+                sbyte[] ginv = new sbyte[_p];
+                sbyte[] g = new sbyte[_p];
+                short[] finv = new short[_p];
+
+                while (true)
+                {
+                    ByteRandom(ref g, random);
+
+                    if (R3Recip(ref ginv, g) == 0)
+                    {
+                        break;
+                    }
+                    
+                }
+                
+                ShortRandom(ref f, random);
+                RqRecip3(ref finv, f);
+                RqMult(ref h, finv, g);
+                // END KeyGen
+                
+                RqEncode(ref pk, h);
+                
+                ByteEncode(ref sk, f);
+                
+                byte[] smallEncOut = new byte[sk.Length];
+                ByteEncode(ref smallEncOut, ginv);
+                
+                Array.Copy(smallEncOut, 0, sk, _smallBytes, sk.Length - _smallBytes);
+                
+            }
+            
+        }
+        
+        //----------------------- Streamlined --------------------------------
+        
+        private void ByteRandom(ref sbyte[] output, SecureRandom random)
+        {
+            for (int i = 0;i < _p;++i)
+            {
+                byte[] smallRandom = new byte[4];
+                random.NextBytes(smallRandom);
+                output[i] = (sbyte)((((BitConverter.ToUInt32(smallRandom, 0) & 0x3fffffff) * 3) >> 30)-1);
+            };
+        }
+
+        private int R3Recip(ref sbyte[] output, sbyte[] input)
+        {
+            sbyte[] f = new sbyte[_p+1];
+            sbyte[] g = new sbyte[_p+1];
+            sbyte[] v = new sbyte[_p+1];
+            sbyte[] r = new sbyte[_p+1];
+
+            for (int i = 0; i < _p+1; ++i)
+            {
+                v[i] = 0;
+                r[i] = 0;
+            }
+
+            r[0] = 1;
+
+            for (int i = 0; i < _p; ++i)
+            {
+                f[i] = 0;
+            }
+            
+            f[0] = 1;
+            f[_p - 1] = f[_p] = -1;
+
+            for (int i = 0; i < _p; ++i)
+            {
+                g[_p-1-i] = input[i];
+            }
+            
+            g[_p] = 0;
+
+            int delta = 1;
+
+            int sign, swap, t;
+
+            for (int i = 0; i < 2*_p-1; ++i)
+            {
+                for (int j = _p; j > 0; --j)
+                {
+                    v[j] = v[j - 1];
+                }
+
+                v[0] = 0;
+
+                sign = -g[0] * f[0];
+                
+                
+                swap = NegativeMask((short)-delta) & ((g[0] != 0) ? -1 : 0);
+                
+                
+                delta ^= swap & (delta ^ -delta);
+                delta +=1;
+
+                for (int j = 0; j < _p + 1; ++j)
+                {
+                    t = swap&(f[j]^g[j]);
+                    f[j] ^= (sbyte)t; 
+                    g[j] ^= (sbyte)t;
+                    t = swap&(v[j]^r[j]); 
+                    v[j] ^= (sbyte)t; 
+                    r[j] ^= (sbyte)t;
+
+                }
+                for (int j = 0; j < _p+1; ++j)
+                {
+                    g[j] =(sbyte)(mod(((g[j] + sign * f[j]) + 1), 3) - 1);
+                }
+
+                for (int j = 0; j < _p+1; ++j)
+                {
+                    r[j] =(sbyte)(mod(((r[j] + sign * v[j]) + 1), 3) - 1);
+                }
+
+                for (int j = 0; j < _p; ++j)
+                {
+                    g[j] = g[j + 1];
+                }
+                g[_p] = 0;
+            }
+
+            sign = f[0];
+
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = (sbyte)(sign * v[_p - 1 - i]);
+            }
+            
+            return (delta != 0) ? -1 : 0;
+
+        }
+
+        private int RqRecip3(ref short[] output, sbyte[] input)
+        {
+            short[] f = new short[_p + 1];
+            short[] g = new short[_p + 1];
+            short[] v = new short[_p + 1];
+            short[] r = new short[_p + 1];
+            int swap, t;
+            short scale;
+            int f0, g0;
+
+            for (int i = 0; i < _p + 1; ++i)
+            {
+                v[i] = 0;
+                r[i] = 0;
+            }
+
+            r[0] = FqRecip(3);
+
+            for (int i = 0; i < _p; ++i)
+            {
+                f[i] = 0;
+            }
+
+            f[0] = 1;
+            f[_p - 1] = f[_p] = -1;
+
+            for (int i = 0; i < _p; ++i)
+            {
+                g[_p - 1 - i] = input[i];
+            }
+
+            g[_p] = 0;
+
+            int delta = 1;
+
+            for (int i = 0; i < 2 * _p - 1; ++i)
+            {
+
+                for (int j = _p; j > 0; --j)
+                {
+                    v[j] = v[j - 1];
+                }
+
+                v[0] = 0;
+                
+
+                swap = NegativeMask((short)-delta) & ((g[0] != 0) ? -1 : 0);
+                
+                delta ^= swap & (delta ^ -delta);
+                delta += 1;
+
+                for (int j = 0; j < _p + 1; ++j)
+                {
+                    t = swap & (f[j] ^ g[j]);
+                    f[j] ^= (short)t;
+                    g[j] ^= (short)t;
+                    t = swap & (v[j] ^ r[j]);
+                    v[j] ^= (short)t;
+                    r[j] ^= (short)t;
+                }
+
+                f0 = f[0];
+                g0 = g[0];
+
+                for (int j = 0; j < _p + 1; ++j)
+                {
+                    g[j] = ArithmeticMod_q(f0 * g[j] - g0 * f[j]);
+                }
+
+                for (int j = 0; j < _p + 1; ++j)
+                {
+                    r[j] = ArithmeticMod_q(f0 * r[j] - g0 * v[j]);
+                }
+
+                for (int j = 0; j < _p; ++j)
+                {
+                    g[j] = g[j + 1];
+                }
+
+                g[_p] = 0;
+            }
+
+            scale = FqRecip(f[0]);
+
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = ArithmeticMod_q(scale * v[_p - 1 - i]);
+            }
+
+            return (delta != 0) ? -1 : 0;
+        }
+
+        private short FqRecip(short a1)
+        {
+            int i = 1;
+            short ai = a1;
+
+            while (i < _q - 2)
+            {
+                ai = ArithmeticMod_q(a1 * ai);
+                i++;
+            }
+
+            return ai;
+        }
+
+        private void RqMult(ref short[] output, short[] f, sbyte[] g)
+        {
+            // h = f * g in the ring Rq
+            
+            short[] fg = new short[_p + _p + 1]; // Can directly modify h
+            short result;
+
+            for (int i = 0; i < _p; ++i)
+            {
+                result = 0;
+                for (int j = 0; j <= i; ++j)
+                {
+                    result = ArithmeticMod_q(result + f[j] * g[i - j]);
+                }
+                fg[i] = result;
+            }
+
+            for (int i = _p;i < _p+_p-1;++i)
+            {
+                result = 0;
+                for (int j = i-_p+1;j < _p;++j)
+                {
+                    result = ArithmeticMod_q(result + f[j] * g[i - j]);
+                }
+                fg[i] = result;
+            }
+
+            for (int i = _p+_p-2; i >= _p; --i)
+            {
+                fg[i - _p] = ArithmeticMod_q(fg[i - _p] + fg[i]);
+                fg[i-_p+1] = ArithmeticMod_q(fg[i-_p+1] + fg[i]);
+            }
+            
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = fg[i];
+            }
+        }
+
+        private void RqEncode(ref byte[] output, short[] r)
+        {
+            ushort[] R = new ushort[_p];
+            ushort[] M = new ushort[_p];
+
+            for (int i = 0; i < _p; ++i)
+            {
+                R[i] = (ushort)(r[i] + _q12);
+                M[i] = (ushort)_q;
+            }
+            
+            List<byte> sList = new List<byte>();
+
+            Encode(ref sList, R, M, _p);
+
+            Array.Copy(sList.ToArray(), 0, output, 0, sList.Count);
+        }
+
+        private void RqDecode(ref short[] output, byte[] s)
+        {
+            ushort[] R = new ushort[_p];
+            ushort[] M = new ushort[_p];
+
+            for (int i = 0; i < _p; ++i)
+            {
+                M[i] = (ushort)_q;
+            }
+            
+            List<byte> rList = new List<byte>(s);
+            List<ushort> mList = new List<ushort>(M);
+            
+            List<ushort> decoded = Decode(rList, mList);
+
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = (short)(decoded[i] - _q12);
+            }
+        }
+
+        private void RqMult3(ref short[] output, short[] f)
+        {
+            for (int i = 0; i < _p; ++i)
+            { 
+                output[i] = ArithmeticMod_q(f[i] * 3);
+            }
+        }
+
+        private void R3FromRq(ref sbyte[] output, short[] r)
+        {
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = (sbyte)ArithmeticMod_3(r[i]);
+            }
+        }
+
+        private void R3Mult(ref sbyte[] output, sbyte[] f, sbyte[] g)
+        {
+            sbyte[] fg = new sbyte[_p + _p + 1];
+            sbyte result;
+
+            for (int i = 0; i < _p; ++i)
+            {
+                result = 0;
+                for (int j = 0; j <= i; ++j)
+                {
+                    result = (sbyte)(ArithmeticMod_3(result + f[j] * g[i - j]));
+                }
+                fg[i] = result;
+            }
+
+            for (int i = _p;i < _p+_p-1;++i)
+            {
+                result = 0;
+
+                for (int j = i-_p+1;j < _p;++j)
+                {
+                    result = (sbyte)(ArithmeticMod_3(result+f[j]*g[i-j]));
+                }
+                fg[i] = result;
+            }
+
+            for (int i = _p+_p-2;i >= _p;--i)
+            {
+                fg[i - _p] = (sbyte)(ArithmeticMod_3(fg[i - _p] + fg[i]));
+                fg[i - _p + 1] = (sbyte)(ArithmeticMod_3(fg[i - _p + 1] + fg[i]));
+            }
+
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = fg[i];
+            }
+        }
+
+        private int WeightMask(sbyte[] r)
+        {
+            int weight = 0;
+
+            for (int i = 0; i < _p; ++i)
+            {
+                weight += r[i]&1;
+            }
+
+            return NonZeroMask((short)(weight - _w));
+
+        }
+
+        private int NonZeroMask(short x)
+        {
+            /* return -1 if x!=0; else return 0 */
+            return (x == 0) ? 0 : -1;
+        }
+
+        //---------------------------------------------------------------------
+        
+        private List<ushort> Decode(List<byte> S, List<ushort> M)
+        {
+            int limit = 16384;
+
+            if (M.Count == 0)
+            {
+                return new List<ushort>();
+            }
+
+            if (M.Count == 1)
+            {
+                if (M[0] == 1)
+                {
+                    return new List<ushort>() { 0 };
+                }
+                if (M[0] <= 256)
+                {
+                    return new List<ushort>() { (ushort)mod(S[0], M[0]) };
+                } else
+                {
+                    return new List<ushort>() { (ushort)mod(S[0] + (((uint)S[1]) << 8), M[0]) };
+                }
+            }
+
+            int k = 0;
+            List<ushort> bottomr = new List<ushort>();
+            List<uint> bottomt = new List<uint>();
+            List<ushort> M2 = new List<ushort>();
+
+            for (int i = 0; i < M.Count - 1; i += 2)
+            {
+                uint m = (uint)(M[i] * M[i + 1]);
+                ushort r = 0;
+                uint t = 1;
+
+                while (m >= limit)
+                {
+                    r = (ushort)(r + S[k] * t);
+                    t = t * 256;
+                    k = k + 1;
+                    m = (uint)System.Math.Floor((double)((m + 255) / 256));
+                }
+                bottomr.Add(r);
+                bottomt.Add(t);
+                M2.Add((ushort)m);
+            }
+
+            if (M.Count % 2 != 0)
+            {
+                M2.Add(M[M.Count - 1]);
+            }
+
+            List<byte> S2 = new List<byte>();
+
+            S2 = S.GetRange(k, S.Count - k);
+
+            List<ushort> R2 = Decode(S2, M2);
+
+            List<ushort> R = new List<ushort>();
+
+            for (int i = 0; i < M.Count - 1; i += 2)
+            {
+                uint r = bottomr[i / 2];
+                uint t = bottomt[i / 2];
+                r += (uint)(t * R2[i / 2]);
+                R.Add((ushort)mod(r, M[i]));
+                R.Add((ushort)(mod(System.Math.Floor((double)r / M[i]), M[i + 1])));
+            }
+            if (M.Count % 2 != 0)
+            {
+                R.Add(R2[M2.Count - 1]);
+            }
+            return R;
+        }
+
+        private void Encode(ref List<byte> output, ushort[] R, ushort[] M, long len)
+        {
+            int limit = 16384;
+            if (len == 1)
+            {
+                ushort r = R[0];
+                ushort m = M[0];
+
+                while (m > 1)
+                {
+                    output.Add(Decimal.ToByte(r % 256));
+                    r >>= 8;
+                    m = (ushort)((m + 255) >> 8);
+                }
+            }
+
+            if (len > 1)
+            {
+                ushort[] R2 = new ushort[(len + 1) / 2];
+                ushort[] M2 = new ushort[(len + 1) / 2];
+
+                int i;
+                for (i = 0; i < len - 1; i += 2)
+                {
+                    uint m0 = M[i];
+                    uint r = R[i] + R[i + 1] * m0;
+                    uint m = M[i + 1] * m0;
+
+                    while (m >= limit)
+                    {
+                        output.Add(Decimal.ToByte(r % 256));
+                        r >>= 8;
+                        m = (m + 255) >> 8;
+                    }
+
+                    R2[i / 2] = (ushort)r;
+                    M2[i / 2] = (ushort)m;
+                }
+
+                if (i < len)
+                {
+                    R2[i / 2] = R[i];
+                    M2[i / 2] = M[i];
+                }
+
+                Encode(ref output, R2, M2, (len + 1) / 2);
+            }
+        }
+
+        private void Encrypt(ref byte[] output, sbyte[] r, byte[] pk) // ZEncrypt
+        {
+            if (_lpr)
+            {
+                short[] A = new short[_p];
+                short[] B = new short[_p];
+                sbyte[] T = new sbyte[_I];
+
+                byte[] pkMinusSeed = new byte[pk.Length - _seedBytes];
+                Array.Copy(pk, _seedBytes, pkMinusSeed, 0, pkMinusSeed.Length);
+
+                RoundedDecode(ref A, pkMinusSeed);
+
+                // BEGIN: XEncrypt
+                short[] G = new short[_p];
+                sbyte[] b = new sbyte[_p];
+                byte[] seedOut = new byte[_seedBytes];
+
+                Array.Copy(pk, 0, seedOut, 0, _seedBytes);
+
+                Generator(ref G, seedOut);
+                HashShort(ref b, r);
+
+                // BEGIN: Encrypt
+                short[] bG = new short[_p];
+                short[] bA = new short[_p];
+
+                RqMult(ref bG, G, ref b);
+
+                Round(ref B, bG);
+
+                RqMult(ref bA, A, ref b);
+
+                for (int i = 0; i < _I; ++i)
+                {
+                    T[i] = Top(ArithmeticMod_q(bA[i] + r[i] * _q12));
+                }
+                // END: Encrypt
+                // END: XEncrypt
+            
+                RoundedEncode(ref output, B);
+                byte[] topEncOut = new byte[output.Length];
+                TopEncode(ref topEncOut, T);
+                Array.Copy(topEncOut, 0, output, _roundedBytes, output.Length - _roundedBytes);
+            }
+            else
+            {
+                short[] h = new short[_p];
+                short[] c = new short[_p];
+                
+                RqDecode(ref h, pk);
+        
+                // Encrypt
+                short[] hr = new short[_p];
+                RqMult(ref hr, h, r);
+                Round(ref c, hr);
+                // END Encrypt
+                
+                RoundedEncode(ref output, c);
+
+            }
+        }
+        
+        private void Decrypt(ref sbyte[] output, byte[] c, byte[] sk) // ZDecrypt
+        {
+            if (_lpr)
+            {
+                sbyte[] a = new sbyte[_p];
+                short[] B = new short[_p];
+                sbyte[] T = new sbyte[_I];
+
+                ByteDecode(ref a, sk);
+                RoundedDecode(ref B, c);
+
+                Array.Copy(c, _roundedBytes, c, 0, c.Length - _roundedBytes);
+
+                TopDecode(ref T, c);
+                
+                // BEGIN: XDecrypt
+                short[] aB = new short[_p];
+
+                RqMult(ref aB, B, ref a);
+
+                for (int i = 0; i < _I; ++i)
+                {
+                    int freeze = Right(T[i]) - aB[i] + 4 * _w + 1;
+                    output[i] = (sbyte)(-NegativeMask((short)(mod(freeze + _q12, _q) - _q12)));
+                }
+                // END: XDecrypt
+            }
+            else
+            {
+                sbyte[] f = new sbyte[_p];
+                sbyte[] v = new sbyte[_p];
+                
+                short[] c2 = new short[_p];
+                
+                ByteDecode(ref f, sk);
+                
+                byte[] skShift = new byte[sk.Length];
+                
+                Array.Copy(sk, _smallBytes, skShift, 0, skShift.Length - _smallBytes);
+                
+                ByteDecode(ref v, skShift);
+                
+                RoundedDecode(ref c2, c);
+                
+                short[] cf = new short[_p];
+                short[] cf3 = new short[_p];
+                sbyte[] e = new sbyte[_p];
+                sbyte[] ev = new sbyte[_p];
+                
+                RqMult(ref cf, c2, f);
+                RqMult3(ref cf3, cf);
+                R3FromRq(ref e, cf3);
+                R3Mult(ref ev, e, v);
+
+                int mask = WeightMask(ev);
+                
+                for (int i = 0; i < _w; ++i)
+                {
+                    output[i] = (sbyte)(((ev[i] ^ 1) & ~mask) ^ 1);
+                }
+                
+                for (int i = _w; i < _p; ++i)
+                {
+                    output[i] = (sbyte)(ev[i] & ~mask);
+                }
+            }
+            
+        }
+        
+        private void Hide(ref byte[] output, byte[] r_enc, sbyte[] r, byte[] pk, byte[] cache)
+        {
+            /* c,r_enc = Hide(r,pk,cache); cache is Hash4(pk) */
+
+            if (_lpr)
+            {
+                InputsEncode(ref r_enc, r);
+            }
+            else
+            {
+                ByteEncode(ref r_enc, r);
+            }
+            
+            Encrypt(ref output, r, pk);
+            
+            Array.Copy(output, 0, output, _ctBytes, output.Length - _ctBytes);
+            
+            HashConfirm(ref output, ref r_enc, ref pk, ref cache);
+
+        }
+
+        private void Generator(ref short[] output, byte[] seed)
+        {
+            uint[] L = Expand(seed);
+            
+            for (int i = 0; i < _p; i++)
+            {
+                output[i] = (short)((L[i] % _q) - _q12);
+            }
+        }
+
+        private uint[] Expand(byte[] k)
+        {
+            byte[] L = new byte[_p * 4];
+            byte[] cipherInput = new byte[_p * 4];
+            uint[] L_uint = new uint[_p];
+
+            // AES256 CTR
+            BufferedBlockCipher cipher = new BufferedBlockCipher(new SicBlockCipher(new AesEngine()));
+            KeyParameter kp = new KeyParameter(k);
+            cipher.Init(true, new ParametersWithIV(kp, new byte[16]));
+            int len = cipher.ProcessBytes(cipherInput, 0, 4 * _p, L, 0);
+            len += cipher.DoFinal(L, len);
+            
+            for (int i = 0; i < _p; ++i)
+            {
+                uint L0 = L[4 * i];
+                uint L1 = L[4 * i + 1];
+                uint L2 = L[4 * i + 2];
+                uint L3 = L[4 * i + 3];
+
+                L_uint[i] = L0 + (L1 << 8) + (L2 << 16) + (L3 << 24);
+            }
+            return L_uint;
+        }
+        
+        private void ShortRandom(ref sbyte[] output, SecureRandom random)
+        {
+            uint[] L = new uint[_p];
+
+            for (int i = 0; i < _p; ++i)
+            {
+                byte[] shortRandom = new byte[4];
+                random.NextBytes(shortRandom);
+                L[i] = BitConverter.ToUInt32(shortRandom, 0);
+            }
+            ShortFromList(ref output, L);
+        }
+
+        private void ShortFromList(ref sbyte[] output, uint[] L_in)
+        {
+            uint[] L = new uint[_p];
+
+            for (int i = 0; i < _w; ++i)
+            {
+                L[i] = (uint)(L_in[i] & -2);
+            }
+            
+            for (int i = _w; i < _p; ++i)
+            {
+                L[i] = (uint)(L_in[i] & -3) | 1;
+            }
+
+            Array.Sort(L);
+
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = (sbyte)((L[i] & 3) - 1);
+            }
+        }
+        
+        private void RqMult(ref short[] output, short[] G, ref sbyte[] a) // aG, G, a -> h, f, g
+        {
+            short[] fg = new short[_p + _p - 1];
+            short result;
+
+            for (int i = 0; i < _p; ++i)
+            {
+                result = 0;
+                for (int j = 0; j <= i; ++j)
+                {
+                    result = ArithmeticMod_q(result + (G[j] * a[i - j]));
+                }
+
+                fg[i] = result;
+            }
+
+            for (int i = _p; i < _p + _p - 1; ++i)
+            {
+                result = 0;
+                for (int j = i - _p + 1; j < _p; ++j)
+                {
+                    result = ArithmeticMod_q(result + (G[j] * a[i - j]));
+                }
+
+                fg[i] = result;
+            }
+
+            for (int i = _p + _p - 2; i >= _p; --i)
+            {
+                fg[i - _p] = ArithmeticMod_q(fg[i - _p] + fg[i]);
+                fg[i - _p + 1] = ArithmeticMod_q(fg[i - _p + 1] + fg[i]);
+            }
+
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = fg[i];
+            }
+        }
+
+        private void Round(ref short[] output, short[] aG)
+        {
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = (short)(aG[i] - ArithmeticMod_3(aG[i]));
+            }
+        }
+        
+        private void InputsRandom(ref sbyte[] output, SecureRandom random)
+        {
+            byte[] s = new byte[_inputsBytes];
+            random.NextBytes(s);
+            for (int i = 0; i < _I; ++i)
+            {
+                output[i] = (sbyte)(1 & (s[i >> 3] >> (i & 7)));
+            }
+        }
+        
+        private void InputsEncode(ref byte[] output, sbyte[] r)
+        {
+            for (int i = 0; i < _inputsBytes; ++i)
+            {
+                output[i] = 0;
+            }
+
+            for (int i = 0; i < _I; ++i)
+            {
+                output[i >> 3] |= (byte)(r[i] << (i & 7));
+            }
+        }
+        
+        private void RoundedEncode(ref byte[] output, short[] A)
+        {
+            ushort[] R = new ushort[_p];
+            ushort[] M = new ushort[_p];
+            
+            for (int i = 0; i < _p; ++i)
+            {
+                R[i] = (ushort)(((A[i] + _q12) * 10923) >> 15);
+            }
+
+            for (int i = 0; i < _p; ++i)
+            {
+                M[i] = (ushort)((_q + 2) / 3);
+            }
+            
+            List<byte> outputList = new List<byte>();
+
+            Encode(ref outputList, R, M, _p);
+
+            Array.Copy(outputList.ToArray(), 0, output, 0, outputList.Count);
+        }
+        
+        private void RoundedDecode(ref short[] output, byte[] s)
+        {
+            List<ushort> M = new List<ushort>(_p);
+            List<byte> S = new List<byte>(s);
+
+            for (int i = 0; i < _p; ++i)
+            {
+                M.Add((ushort)((_q + 2) / 3));
+            }
+
+            List<ushort> decoded = Decode(S, M);
+
+            for (int i = 0; i < _p; ++i)
+            {
+                output[i] = (short)((decoded[i] * 3) - _q12);
+            }
+        }
+        
+        private void ByteEncode(ref byte[] output, sbyte[] a)
+        {
+            sbyte x;
+            for (int i = 0; i < _p / 4; ++i)
+            {
+                int x0 = a[4 * i] + 1;
+                int x1 = (a[4 * i + 1] + 1) << 2;
+                int x2 = (a[4 * i + 2] + 1) << 4;
+                int x3 = (a[4 * i + 3] + 1) << 6;
+                x = (sbyte)(x0 + x1 + x2 + x3);
+                output[i] = (byte)x;
+            }
+            output[_p / 4] = (byte)(a[_p - 1] + 1);
+        }
+        
+        private void ByteDecode(ref sbyte[] output, byte[] s)
+        {
+            byte x;
+            for (int i = 0; i < _p / 4; ++i)
+            {
+                x = s[i];
+                output[i * 4] = (sbyte)((x & 3) - 1);
+                x >>= 2;
+                output[i * 4 + 1] = (sbyte)((x & 3) - 1);
+                x >>= 2;
+                output[i * 4 + 2] = (sbyte)((x & 3) - 1);
+                x >>= 2;
+                output[i * 4 + 3] = (sbyte)((x & 3) - 1);
+            }
+
+            x = s[_p / 4];
+            output[_p / 4 * 4] = (sbyte)((x & 3) - 1);
+        }
+
+        private void TopEncode(ref byte[] output, sbyte[] T)
+        {
+            for (int i = 0; i < _topBytes; ++i)
+            {
+                output[i] = (byte)(T[2 * i] + (T[2 * i + 1] << 4));
+            }
+        }
+
+        private void TopDecode(ref sbyte[] output, byte[] s)
+        {
+            for (int i = 0; i < _topBytes; ++i)
+            {
+                output[2 * i] = (sbyte)(s[i] & 15);
+                output[2 * i + 1] = (sbyte)(s[i] >> 4);
+            }
+        }
+        
+        private void HashShort(ref sbyte[] output, sbyte[] r)
+        {
+            byte[] s = new byte[_inputsBytes];
+            byte[] h = new byte[_hashBytes];
+            uint[] L = new uint[_p];
+
+            InputsEncode(ref s, r);
+            HashPrefix(ref h, 5, ref s, s.Length);
+            L = Expand(h);
+            ShortFromList(ref output, L);
+        }
+
+        private void HashPrefix(ref byte[] output, int b, ref byte[] input, int inlen)
+        {
+            byte[] x = new byte[inlen + 1];
+            byte[] h = new byte[64];
+
+            x[0] = (byte)b;
+
+            for (int i = 0; i < inlen; ++i)
+            {
+                x[i + 1] = input[i];
+            }
+
+            Sha512Digest sha512 = new Sha512Digest();
+            sha512.BlockUpdate(x, 0, x.Length);
+            sha512.DoFinal(h, 0);
+
+            Array.Copy(h, 0, output, output.Length - 32, 32);
+        }
+        
+        private void HashConfirm(ref byte[] output, ref byte[] r, ref byte[] pk, ref byte[] cache)
+        {
+
+            byte[] x;
+            
+            if (_lpr)
+            {
+                
+                x = new byte[_inputsBytes + _hashBytes];
+
+
+                for (int i = 0; i < _inputsBytes; ++i)
+                {
+                    x[i] = r[i];
+                }
+
+                for (int i = 0; i < _hashBytes; ++i)
+                {
+                    x[_inputsBytes + i] = cache[i];
+                }
+                
+            }
+            else
+            {
+                x = new byte[_hashBytes*2];
+
+                byte[] prefix = new byte[_hashBytes];
+                
+                HashPrefix(ref prefix, 3, ref r, _inputsBytes);
+                
+                Array.Copy(prefix, 0, x, 0, _hashBytes);
+                
+                for (int i = 0; i < _hashBytes; ++i)
+                {
+                    x[_hashBytes + i] = cache[i];
+                }
+            }
+            HashPrefix(ref output, 2, ref x, x.Length);
+
+        }
+        
+        private void HashSession(ref byte[] output, int b, byte[] y, byte[] z)
+        {
+            byte[] x;
+            
+            if (_lpr)
+            {
+                x = new byte[_inputsBytes + _ciphertextsBytes + _confirmBytes];
+
+                for (int i = 0; i < _inputsBytes; ++i)
+                {
+                    x[i] = y[i];
+                }
+
+                for (int i = 0; i < _ciphertextsBytes + _confirmBytes; ++i)
+                {
+                    x[_inputsBytes + i] = z[i];
+                }
+            }
+            else
+            {
+                x = new byte[_hashBytes + _ciphertextsBytes + _confirmBytes];
+                
+                byte[] prefix = new byte[_hashBytes];
+                HashPrefix(ref prefix, 3, ref y, _inputsBytes);
+                Array.Copy(prefix, 0, x, 0, _hashBytes);
+                
+                for (int i = 0; i < _ciphertextsBytes + _confirmBytes; ++i)
+                {
+                    x[_hashBytes + i] = z[i];
+                }
+            }
+
+            HashPrefix(ref output, b, ref x, x.Length);
+        }
+        
+        private int NegativeMask(short x)
+        {
+            return (x < 0) ? -1 : 0;
+        }
+        
+        private int ctDiffMask(byte[] c, byte[] c2)
+        {
+            int x = c.Length ^ c2.Length;
+            for (int i = 0; i < c.Length && i < c2.Length; ++i)
+            {
+                x |= c[i] ^ c2[i];
+            }
+
+            // Return 0 if matching, else -1
+            return x == 0 ? 0 : -1;
+        }
+        
+        // Arithmetics
+        double mod(double a, double b)
+        {
+            return a - b * System.Math.Floor(a / b);
+        }
+        
+        private short ArithmeticMod_q(int x)  // Fq_freeze
+        {
+            return (short)((mod(x + _q12, _q)) - _q12);
+        }
+        
+        private short ArithmeticMod_3(int x) // F3_freeze
+        {
+            return (short)(mod(x + 1, 3) - 1);
+        }
+        
+        private sbyte Top(int C)
+        {
+            return (sbyte)((_tau1 * (C + _tau0) + 16384) >> 15);
+        }
+        
+        private short Right(sbyte T)
+        {
+            int freeze = _tau3 * T - _tau2;
+            return (short)(mod(freeze + _q12, _q) - _q12);
+        }
+        
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/ntrup/NtruPKemExtractor.cs b/crypto/src/pqc/crypto/ntrup/NtruPKemExtractor.cs
new file mode 100644
index 000000000..6149e1112
--- /dev/null
+++ b/crypto/src/pqc/crypto/ntrup/NtruPKemExtractor.cs
@@ -0,0 +1,35 @@
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Frodo;
+
+namespace Org.BouncyCastle.pqc.crypto.NtruP
+{
+    public class NtruPKEMExtractor : IEncapsulatedSecretExtractor
+    {
+        private NtruPEngine _pEngine;
+        private NtruPKeyParameters _pKey;
+
+        public NtruPKEMExtractor(NtruPKeyParameters privParams)
+        {
+            this._pKey = privParams;
+            InitCipher(_pKey.PParameters);
+        }
+
+        private void InitCipher(NtruPParameters param)
+        {
+            _pEngine = param.PEngine;
+        }
+
+        public byte[] ExtractSecret(byte[] encapsulation)
+        {
+            byte[] session_key = new byte[_pEngine.SessionKeySize];
+            _pEngine.kem_dec(session_key, encapsulation, ((NtruPPrivateKeyParameters)_pKey).PrivateKey);
+            return session_key;
+        }
+
+        public int GetInputSize()
+        {
+            return _pEngine.CipherTextSize;
+        }
+        
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/ntrup/NtruPKemGenerator.cs b/crypto/src/pqc/crypto/ntrup/NtruPKemGenerator.cs
new file mode 100644
index 000000000..0fb2633e8
--- /dev/null
+++ b/crypto/src/pqc/crypto/ntrup/NtruPKemGenerator.cs
@@ -0,0 +1,77 @@
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.pqc.crypto.NtruP
+{
+    public class NtruPKemGenerator : IEncapsulatedSecretGenerator
+    {
+        private SecureRandom sr;
+        
+        public NtruPKemGenerator(SecureRandom sr)
+        {
+            this.sr = sr;
+        }
+
+        public ISecretWithEncapsulation GenerateEncapsulated(AsymmetricKeyParameter recipientKey)
+        {
+            NtruPPublicKeyParameters key = (NtruPPublicKeyParameters)recipientKey;
+            NtruPEngine pEngine = key.PParameters.PEngine;
+            byte[] cipherText = new byte[pEngine.CipherTextSize];
+            byte[] sessionKey = new byte[pEngine.SessionKeySize];
+            pEngine.kem_enc(cipherText, sessionKey,key.PublicKey, sr);
+            return new NtruPKemGenerator.SecretWithEncapsulationImpl(sessionKey, cipherText);
+        }
+
+        public class SecretWithEncapsulationImpl : ISecretWithEncapsulation
+        {
+            private volatile bool hasBeenDestroyed = false;
+            
+            private byte[] sessionKey;
+            private byte[] cipherText;
+            
+            public SecretWithEncapsulationImpl(byte[] sessionKey, byte[] cipherText)
+            {
+                this.sessionKey = sessionKey;
+                this.cipherText = cipherText;
+            }
+
+            public byte[] GetSecret()
+            {
+                CheckDestroyed();
+                return Arrays.Clone(sessionKey);
+            }
+
+            public byte[] GetEncapsulation()
+            {
+                return Arrays.Clone(cipherText);
+            }
+
+            public void Dispose()
+            {
+                if (!hasBeenDestroyed)
+                {
+                    hasBeenDestroyed = true;
+                    Arrays.Clear(sessionKey);
+                    Arrays.Clear(cipherText);
+                }
+            }
+            
+            public bool IsDestroyed()
+            {
+                return hasBeenDestroyed;
+            }
+            
+            void CheckDestroyed()
+            {
+                if (IsDestroyed())
+                {
+                    throw new Exception("data has been destroyed");
+                }
+            }
+            
+        }
+        
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/ntrup/NtruPKeyGenerationParameters.cs b/crypto/src/pqc/crypto/ntrup/NtruPKeyGenerationParameters.cs
new file mode 100644
index 000000000..7f96dfce6
--- /dev/null
+++ b/crypto/src/pqc/crypto/ntrup/NtruPKeyGenerationParameters.cs
@@ -0,0 +1,18 @@
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.pqc.crypto.NtruP
+{
+    public class NtruKeyGenerationParameters : KeyGenerationParameters
+    {
+        private NtruPParameters _pParameters;
+        
+        public NtruKeyGenerationParameters(SecureRandom random, NtruPParameters ntruPParameters) : base(random,256)
+        {
+            this._pParameters = ntruPParameters;
+        }
+
+        public NtruPParameters PParameters => _pParameters;
+
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/ntrup/NtruPKeyPairGenerator.cs b/crypto/src/pqc/crypto/ntrup/NtruPKeyPairGenerator.cs
new file mode 100644
index 000000000..fbbd66281
--- /dev/null
+++ b/crypto/src/pqc/crypto/ntrup/NtruPKeyPairGenerator.cs
@@ -0,0 +1,69 @@
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.pqc.crypto.NtruP
+{
+    public class NtruKeyPairGenerator
+    {
+        private NtruKeyGenerationParameters ntruParams;
+
+        private int p;
+        private int q;
+
+        private SecureRandom random;
+
+        private void Initialize(KeyGenerationParameters param)
+        {
+            ntruParams = (NtruKeyGenerationParameters) param;
+            random = param.Random;
+
+            // n = ntruParams.Parameters.N;
+
+            p = ntruParams.PParameters.P;
+            q = ntruParams.PParameters.Q;
+
+        }
+
+        private AsymmetricCipherKeyPair GenKeyPair()
+        {
+            NtruPEngine pEngine = ntruParams.PParameters.PEngine;
+            byte[] sk = new byte[pEngine.PrivateKeySize];
+            byte[] pk = new byte[pEngine.PublicKeySize];
+            pEngine.kem_keypair( pk,sk,random);
+
+            NtruPPublicKeyParameters pubKey = new NtruPPublicKeyParameters(ntruParams.PParameters, pk);
+            NtruPPrivateKeyParameters privKey = new NtruPPrivateKeyParameters(ntruParams.PParameters, sk);
+            return new AsymmetricCipherKeyPair(pubKey, privKey);
+        }
+        
+        public void Init(KeyGenerationParameters param)
+        {
+            this.Initialize(param);
+        }
+        
+        public AsymmetricCipherKeyPair GenerateKeyPair()
+        {
+            return GenKeyPair();
+        }
+        
+        // private AsymmetricCipherKeyPair GenKeyPair()
+        // {
+        //     NtruEngine engine = ntruParams.Parameters.Engine;
+        //     byte[] sk = new byte[engine.PrivateKeySize];
+        //     byte[] pk = new byte[engine.PublicKeySize];
+        //     
+        //     
+        // }
+        //
+        // public void Init(KeyGenerationParameters param)
+        // {
+        //     this.Initialize(param);
+        // }
+        //
+        // public AsymmetricCipherKeyPair GenerateKeyPair()
+        // {
+        //     return GenKeyPair();
+        // }
+
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/ntrup/NtruPKeyParameters.cs b/crypto/src/pqc/crypto/ntrup/NtruPKeyParameters.cs
new file mode 100644
index 000000000..96bd4ca8e
--- /dev/null
+++ b/crypto/src/pqc/crypto/ntrup/NtruPKeyParameters.cs
@@ -0,0 +1,17 @@
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.pqc.crypto.NtruP
+{
+    public class NtruPKeyParameters : AsymmetricKeyParameter
+    {
+        private NtruPParameters _pParameters;
+        
+        public NtruPKeyParameters(bool isPrivate, NtruPParameters pParameters) : base(isPrivate)
+        {
+            this._pParameters = pParameters;
+        }
+
+        public NtruPParameters PParameters => _pParameters;
+
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/ntrup/NtruPParameters.cs b/crypto/src/pqc/crypto/ntrup/NtruPParameters.cs
new file mode 100644
index 000000000..cba3b9d77
--- /dev/null
+++ b/crypto/src/pqc/crypto/ntrup/NtruPParameters.cs
@@ -0,0 +1,72 @@
+using System;
+using System.ComponentModel;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Modes;
+
+namespace Org.BouncyCastle.pqc.crypto.NtruP
+{
+    public class NtruPParameters : ICipherParameters
+    {
+
+        private String name;
+        private int p;
+        private int q;
+        private int _roundedBytes;
+        private bool LPR;
+        private int _w;
+        private int _rqBytes;
+        private int _tau0;
+        private int _tau1;
+        private int _tau2;
+        private int _tau3;
+        private int _skBytes;
+        private int _pkBytes;
+        private int _ctBytes;
+        private NtruPEngine _pEngine;
+        
+        public NtruPParameters(String name, int p, int q, bool LPR, int w, int tau0,
+            int tau1, int tau2, int tau3, int skBytes, int pkBytes, int ctBytes, int roundedBytes, int rqBytes)
+        {
+            this.name = name;
+            this.p = p;
+            this.q = q;
+            this.LPR = LPR;
+            this._w = w;
+            this._tau0 = tau0;
+            this._tau1 = tau1;
+            this._tau2 = tau2;
+            this._tau3 = tau3;
+            
+            // KEM Parameters
+            this._roundedBytes = roundedBytes;
+            this._rqBytes = rqBytes;
+            this._skBytes = skBytes;
+            this._pkBytes = pkBytes;
+            this._ctBytes = ctBytes;
+            this._pEngine = new NtruPEngine(p,q, LPR, w, tau0, tau1, tau2, tau3, skBytes, pkBytes, ctBytes, roundedBytes, rqBytes);
+        }
+
+        public static NtruPParameters ntrulpr653 = new NtruPParameters("NTRU_LPRime_653", 653, 4621, true, 252, 2175,113,2031,290,1125,897,1025, 865, -1);
+        public static NtruPParameters ntrulpr761 = new NtruPParameters("NTRU_LPRime_761", 761, 4591, true, 250, 2156,114,2007,287,1294,1039,1167, 1007, -1);
+        public static NtruPParameters ntrulpr857 = new NtruPParameters("NTRU_LPRime_857", 857, 5167, true, 281, 2433,101,2265,324,1463,1184,1312, 1152, -1);
+        public static NtruPParameters ntrulpr953 = new NtruPParameters("NTRU_LPRime_953", 953, 6343, true, 345, 2997,82,2798,400,1652,1349,1477, 1317, -1);
+        public static NtruPParameters ntrulpr1013 = new NtruPParameters("NTRU_LPRime_1013", 1013, 7177, true, 392, 3367,73,3143,449,1773,1455,1583, 1423, -1);
+        public static NtruPParameters ntrulpr1277 = new NtruPParameters("NTRU_LPRime_1277", 1277, 7879, true, 429, 3724,66,3469,496,2231,1847,1975, 1815, -1);
+        
+        public static NtruPParameters sntrup653 = new NtruPParameters("SNTRU_Prime_653", 653, 4621, false, 288, -1,-1,-1,-1,1518,994,897, 865, 994);
+        public static NtruPParameters sntrup761 = new NtruPParameters("SNTRU_Prime_761", 761, 4591, false, 286, -1,-1,-1,-1,1763,1158,1039, 1007, 1158);
+        public static NtruPParameters sntrup857 = new NtruPParameters("SNTRU_Prime_857", 857, 5167, false, 322, -1,-1,-1,-1,1999,1322,1184, 1152, 1322);
+        public static NtruPParameters sntrup953 = new NtruPParameters("SNTRU_Prime_953", 953, 6343, false, 396, -1,-1,-1,-1,2254,1505,1349, 1317, 1505);
+        public static NtruPParameters sntrup1013 = new NtruPParameters("SNTRU_Prime_1013", 1013, 7177, false, 448, -1,-1,-1,-1,2417,1623,1455, 1423, 1623);
+        public static NtruPParameters sntrup1277 = new NtruPParameters("SNTRU_Prime_1277", 1277, 7879, false, 492, -1,-1,-1,-1,3059,2067,1847, 1815, 2067);
+        
+        public int P => p;
+        public bool lpr => LPR;
+        
+        public int Q => q;
+
+        public NtruPEngine PEngine => _pEngine;
+
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/ntrup/NtruPPrivateKeyParameters.cs b/crypto/src/pqc/crypto/ntrup/NtruPPrivateKeyParameters.cs
new file mode 100644
index 000000000..ff70aa241
--- /dev/null
+++ b/crypto/src/pqc/crypto/ntrup/NtruPPrivateKeyParameters.cs
@@ -0,0 +1,21 @@
+using System;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.pqc.crypto.NtruP
+{
+    public class NtruPPrivateKeyParameters : NtruPKeyParameters
+    {
+        private byte[] privKey;
+        public byte[] PrivateKey => Arrays.Clone(privKey);
+
+        public NtruPPrivateKeyParameters(NtruPParameters pParameters, byte[] privKey) : base(true, pParameters)
+        {
+            this.privKey = Arrays.Clone(privKey);
+        }
+        
+        public byte[] GetEncoded()
+        {
+            return PrivateKey;
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/ntrup/NtruPPublicKeyParameters.cs b/crypto/src/pqc/crypto/ntrup/NtruPPublicKeyParameters.cs
new file mode 100644
index 000000000..c7a00d9f3
--- /dev/null
+++ b/crypto/src/pqc/crypto/ntrup/NtruPPublicKeyParameters.cs
@@ -0,0 +1,21 @@
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.pqc.crypto.NtruP
+{
+    public class NtruPPublicKeyParameters : NtruPKeyParameters
+    {
+        public byte[] pubKey;
+
+        public byte[] PublicKey => Arrays.Clone(pubKey);
+
+        public byte[] GetEncoded()
+        {
+            return PublicKey;
+        }
+
+        public NtruPPublicKeyParameters(NtruPParameters pParameters, byte[] pubKey) : base(false,pParameters)
+        {
+            this.pubKey = Arrays.Clone(pubKey);
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/test/src/pqc/crypto/test/NtruPVectorTest.cs b/crypto/test/src/pqc/crypto/test/NtruPVectorTest.cs
new file mode 100644
index 000000000..0e8557dcc
--- /dev/null
+++ b/crypto/test/src/pqc/crypto/test/NtruPVectorTest.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using NUnit.Framework;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Frodo;
+using Org.BouncyCastle.pqc.crypto.NtruP;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Tests
+{
+    [TestFixture]
+    public class NtruPVectorTest
+    {
+        [Test]
+        public void TestParameters()
+        {
+            // Console.WriteLine("Testing");
+            // Console.WriteLine(NtruPParameters.ntrulpr653.P);
+            // Console.WriteLine(NtruPParameters.ntrulpr653.Q);
+            // Console.WriteLine(NtruPParameters.ntrulpr653.lpr);
+        }
+
+        [Test]
+        public void TestVectors()
+        {
+            string[] files =
+            {
+                "kat_kem_ntrulp_653.rsp",
+                "kat_kem_ntrulp_761.rsp",
+                "kat_kem_ntrulp_857.rsp",
+                "kat_kem_ntrulp_953.rsp",
+                "kat_kem_ntrulp_1013.rsp",
+                "kat_kem_ntrulp_1277.rsp",
+                "kat_kem_sntrup_653.rsp",
+                "kat_kem_sntrup_761.rsp",
+                "kat_kem_sntrup_857.rsp",
+                "kat_kem_sntrup_953.rsp",
+                "kat_kem_sntrup_1013.rsp",
+                "kat_kem_sntrup_1277.rsp",
+            };
+
+            NtruPParameters[] parameters =
+            {
+                NtruPParameters.ntrulpr653,
+                NtruPParameters.ntrulpr761,
+                NtruPParameters.ntrulpr857,
+                NtruPParameters.ntrulpr953,
+                NtruPParameters.ntrulpr1013,
+                NtruPParameters.ntrulpr1277,
+                NtruPParameters.sntrup653, 
+                NtruPParameters.sntrup761,
+                NtruPParameters.sntrup857,
+                NtruPParameters.sntrup953,
+                NtruPParameters.sntrup1013,
+                NtruPParameters.sntrup1277,
+            };
+
+            for (int fileIndex = 0; fileIndex != files.Length; fileIndex++)
+            {
+                String name = files[fileIndex];
+                Console.Write("Testing " + name + "...");
+                Console.WriteLine("pqc.ntru."+ name);
+                StreamReader src = new StreamReader(SimpleTest.GetTestDataAsStream("pqc.ntru." + name));
+                String line = null;
+                Dictionary<String, String> buf = new Dictionary<string, string>();
+                
+                while ((line = src.ReadLine()) != null)
+                {
+                    line = line.Trim();
+                    if(line.StartsWith("#"))
+                    {
+                        continue;
+                    }
+
+                    if (line.Length == 0)
+                    {
+                        if (buf.Count > 0)
+                        {
+                            String count = buf["count"];
+                            
+                            if (!"0".Equals(count))
+                            {
+                                // Console.WriteLine("Zero");
+                            }
+                    
+                            byte[] seed = Hex.Decode(buf["seed"]);
+                            byte[] pk = Hex.Decode(buf["pk"]);
+                            byte[] ct = Hex.Decode(buf["ct"]);
+                            byte[] sk = Hex.Decode(buf["sk"]);
+                            byte[] ss = Hex.Decode(buf["ss"]);
+
+                            
+                            NistSecureRandom random = new NistSecureRandom(seed, null);
+                            NtruPParameters ntruPParameters = parameters[fileIndex];
+                            
+                            NtruKeyPairGenerator kpGen = new NtruKeyPairGenerator();
+                            NtruKeyGenerationParameters genParams = new NtruKeyGenerationParameters(random,ntruPParameters);
+                            
+                            // Generate the key pair
+                            kpGen.Init(genParams);
+                            AsymmetricCipherKeyPair kp = kpGen.GenerateKeyPair();
+                            
+                            NtruPPublicKeyParameters pubParams = (NtruPPublicKeyParameters) kp.Public;
+                            NtruPPrivateKeyParameters privParams = (NtruPPrivateKeyParameters) kp.Private;
+                            
+                            // Check public and private key
+                            Assert.True(Arrays.AreEqual(pk,pubParams.PublicKey), $"{name} {count} : public key");
+                            Assert.True(Arrays.AreEqual(sk,privParams.PrivateKey), $"{name} {count} : private key");
+                            
+                            // Encapsulation
+                            NtruPKemGenerator ntruPEncCipher = new NtruPKemGenerator(random);
+                            ISecretWithEncapsulation secWenc = ntruPEncCipher.GenerateEncapsulated(pubParams);
+                            byte[] generatedCT = secWenc.GetEncapsulation();
+                            
+                            // Check ciphertext
+                            Assert.True(Arrays.AreEqual(ct, generatedCT), name + " " + count + ": kem_enc cipher text");
+                            
+                            // Check secret
+                            byte[] secret = secWenc.GetSecret();
+                            Assert.True(Arrays.AreEqual(ss, secret), name + " " + count + ": kem_enc secret");
+                            
+                            // Decapsulation
+                            NtruPKEMExtractor ntruDecCipher = new NtruPKEMExtractor(privParams);
+                            byte[] dec_key = ntruDecCipher.ExtractSecret(generatedCT);
+                            
+                            // Check decapsulation secret
+                            Assert.True(Arrays.AreEqual(dec_key, ss), $"{name} {count}: kem_dec ss");
+                            Assert.True(Arrays.AreEqual(dec_key, secret),$"{name} {count}: kem_dec key");
+                        }
+                        buf.Clear();
+
+                        continue;
+                    }
+                    
+                    int a = line.IndexOf("=");
+                    if (a > -1)
+                    {
+                        buf[line.Substring(0, a).Trim()] = line.Substring(a + 1).Trim();
+                    }
+                }
+                Console.WriteLine("OK");
+            }
+
+        }
+        
+    }
+}