diff --git a/crypto/src/pqc/crypto/crystals/kyber/CBD.cs b/crypto/src/pqc/crypto/crystals/kyber/CBD.cs
new file mode 100644
index 000000000..843b0d8f6
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/CBD.cs
@@ -0,0 +1,76 @@
+
+using System;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ internal static class CBD
+ {
+ public static void Eta(Poly r, byte[] bytes, int eta)
+ {
+ int i, j;
+ uint t, d;
+ short a, b;
+
+ switch (eta)
+ {
+ case 2:
+ for (i = 0; i < KyberEngine.N / 8; i++)
+ {
+ t = LittleEndianToUInt32(bytes, 4 * i);
+ d = t & 0x55555555;
+ d += (t >> 1) & 0x55555555;
+ for (j = 0; j < 8; j++)
+ {
+ a = (short)((d >> (4 * j + 0)) & 0x3);
+ b = (short)((d >> (4 * j + eta)) & 0x3);
+ r.Coeffs[8 * i + j] = (short) (a - b);
+ }
+ }
+ break;
+ case 3:
+ for (i = 0; i < KyberEngine.N / 4; i++)
+ {
+ t = LittleEndianToUInt24(bytes, 3 * i);
+ d = t & 0x00249249;
+ d += (t >> 1) & 0x00249249;
+ d += (t >> 2) & 0x00249249;
+
+ for (j = 0; j < 4; j++)
+ {
+ a = (short)((d >> (6 * j + 0)) & 0x7);
+ b = (short)((d >> (6 * j + 3)) & 0x7);
+ r.Coeffs[4 * i + j] = (short)(a - b);
+ }
+ }
+ break;
+ default:
+ throw new ArgumentException("Wrong Eta");
+ }
+ }
+
+ private static uint LittleEndianToUInt24(byte[] x, int offset)
+ {
+ // Refer to convertByteTo32-BitUnsignedInt for explanation
+ uint r = (uint) (x[offset] & 0xFF);
+ r = r | ((uint)(x[offset + 1] & 0xFF) << 8);
+ r = r | ((uint)(x[offset + 2] & 0xFF) << 16);
+ return r;
+ }
+
+ private static uint LittleEndianToUInt32(byte[] x, int offset)
+ {
+ // Convert first byte to an unsigned integer
+ // byte x & 0xFF allows us to grab the last 8 bits
+ uint r = (uint)(x[offset] & 0xFF);
+
+ // Perform the same operation then left bit shift to store the next 8 bits without
+ // altering the previous bits
+ r = r | ((uint)(x[offset + 1] & 0xFF) << 8);
+ r = r | ((uint)(x[offset + 2] & 0xFF) << 16);
+ r = r | ((uint)(x[offset + 3] & 0xFF) << 24);
+ return r;
+ }
+
+
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberEngine.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberEngine.cs
new file mode 100644
index 000000000..0cbd56e7f
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberEngine.cs
@@ -0,0 +1,189 @@
+using System;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ internal class KyberEngine
+ {
+ private SecureRandom _random;
+ private KyberIndCpa IndCpa;
+ private byte[] Seed;
+
+ // Constant Parameters
+ public const int N = 256;
+ public const int Q = 3329;
+ public const int QInv = 62209;
+
+ public static int SymBytes = 32;
+ private const int SharedSecretBytes = 32;
+
+ public static int PolyBytes = 384;
+
+ public const int Eta2 = 2;
+
+ public int IndCpaMsgBytes = SymBytes;
+
+
+ // Parameters
+ public int K { get; private set; }
+ public int PolyVecBytes { get; private set; }
+ public int PolyCompressedBytes { get; private set; }
+ public int PolyVecCompressedBytes { get; private set; }
+ public int Eta1 { get; private set; }
+ public int IndCpaPublicKeyBytes { get; private set; }
+ public int IndCpaSecretKeyBytes { get; private set; }
+ public int IndCpaBytes { get; private set; }
+ public int PublicKeyBytes { get; private set; }
+ public int SecretKeyBytes { get; private set; }
+ public int CipherTextBytes { get; private set; }
+
+ // Crypto
+ public int CryptoBytes { get; private set; }
+ public int CryptoSecretKeyBytes { get; private set; }
+ public int CryptoPublicKeyBytes { get; private set; }
+ public int CryptoCipherTextBytes { get; private set; }
+
+ public KyberEngine(int k)
+ {
+ K = k;
+ switch (k)
+ {
+ case 2:
+ Eta1 = 3;
+ PolyCompressedBytes = 128;
+ PolyVecCompressedBytes = K * 320;
+ break;
+ case 3:
+ Eta1 = 2;
+ PolyCompressedBytes = 128;
+ PolyVecCompressedBytes = K * 320;
+ break;
+ case 4:
+ Eta1 = 2;
+ PolyCompressedBytes = 160;
+ PolyVecCompressedBytes = K * 352;
+ break;
+ default:
+ break;
+ }
+
+ PolyVecBytes = k * PolyBytes;
+ IndCpaPublicKeyBytes = PolyVecBytes + SymBytes;
+ IndCpaSecretKeyBytes = PolyVecBytes;
+ IndCpaBytes = PolyVecCompressedBytes + PolyCompressedBytes;
+ PublicKeyBytes = IndCpaPublicKeyBytes;
+ SecretKeyBytes = IndCpaSecretKeyBytes + IndCpaPublicKeyBytes + 2 * SymBytes;
+ CipherTextBytes = IndCpaBytes;
+
+ // Define Crypto Params
+ CryptoBytes = SharedSecretBytes;
+ CryptoSecretKeyBytes = SecretKeyBytes;
+ CryptoPublicKeyBytes = PublicKeyBytes;
+ CryptoCipherTextBytes = CipherTextBytes;
+
+ IndCpa = new KyberIndCpa(this);
+ }
+
+ public void Init(SecureRandom random)
+ {
+ this._random = random;
+ }
+
+ public void UpdateSeed(byte[] seed)
+ {
+ this.Seed = seed;
+ _random.SetSeed(seed);
+ }
+
+ public void GenerateKemKeyPair(byte[] pk, byte[] sk)
+ {
+ Sha3Digest Sha3Digest256 = new Sha3Digest(256);
+ IndCpa.GenerateKeyPair(pk, sk);
+ Array.Copy(pk, 0, sk, IndCpaSecretKeyBytes, IndCpaPublicKeyBytes);
+ Sha3Digest256.BlockUpdate(pk, 0, PublicKeyBytes);
+ Sha3Digest256.DoFinal(sk, SecretKeyBytes - 2 * SymBytes);
+ _random.NextBytes(sk, SecretKeyBytes - SymBytes, SymBytes);
+ }
+
+ public void KemEncrypt(byte[] cipherText, byte[] sharedSecret, byte[] pk)
+ {
+ byte[] buf = new byte[2 * SymBytes];
+ byte[] kr = new byte[2 * SymBytes];
+
+ Sha3Digest Sha3Digest256 = new Sha3Digest(256);
+
+ _random.NextBytes(buf, 0, SymBytes);
+
+ Sha3Digest256.BlockUpdate(buf, 0, SymBytes);
+ Sha3Digest256.DoFinal(buf, 0);
+
+ Sha3Digest256.BlockUpdate(pk, 0, PublicKeyBytes);
+ Sha3Digest256.DoFinal(buf, SymBytes);
+
+ Sha3Digest Sha3Digest512 = new Sha3Digest(512);
+ Sha3Digest512.BlockUpdate(buf, 0, 2 * SymBytes);
+ Sha3Digest512.DoFinal(kr, 0);
+
+ IndCpa.Encrypt(cipherText, Arrays.CopyOfRange(buf, 0, SymBytes), pk, Arrays.CopyOfRange(kr, SymBytes, 2 * SymBytes));
+ Sha3Digest256.BlockUpdate(cipherText, 0, CipherTextBytes);
+ Sha3Digest256.DoFinal(kr, SymBytes);
+
+ ShakeDigest ShakeDigest128 = new ShakeDigest(256);
+
+ ShakeDigest128.BlockUpdate(kr, 0, 2 * SymBytes);
+ ShakeDigest128.DoFinal(sharedSecret, 0, SymBytes);
+ }
+
+ public void KemDecrypt(byte[] SharedSecret, byte[] CipherText, byte[] SecretKey)
+ {
+ int i;
+ bool fail;
+ byte[] buf = new byte[2 * SymBytes],
+ kr = new byte[2 * SymBytes],
+ cmp = new byte[CipherTextBytes];
+ byte[] pk = Arrays.CopyOfRange(SecretKey, IndCpaSecretKeyBytes, SecretKey.Length);
+ IndCpa.Decrypt(buf, CipherText, SecretKey);
+ Array.Copy(SecretKey, SecretKeyBytes - 2 * SymBytes, buf, SymBytes, SymBytes);
+
+ Sha3Digest Sha3Digest512 = new Sha3Digest(512);
+ Sha3Digest512.BlockUpdate(buf, 0, 2 * SymBytes);
+ Sha3Digest512.DoFinal(kr, 0);
+
+ IndCpa.Encrypt(cmp, Arrays.CopyOf(buf, SymBytes), pk, Arrays.CopyOfRange(kr, SymBytes, kr.Length));
+
+ fail = !(Arrays.AreEqual(CipherText, cmp));
+
+ Sha3Digest Sha3Digest256 = new Sha3Digest(256);
+ Sha3Digest256.BlockUpdate(CipherText, 0, CipherTextBytes);
+ Sha3Digest256.DoFinal(kr, SymBytes);
+
+ Cmov(kr, Arrays.CopyOfRange(SecretKey, SecretKeyBytes - SymBytes, SecretKeyBytes), SymBytes, fail);
+
+ ShakeDigest ShakeDigest256 = new ShakeDigest(256);
+ ShakeDigest256.BlockUpdate(kr, 0, 2 * SymBytes);
+ ShakeDigest256.DoFinal(SharedSecret, 0, SymBytes);
+ }
+
+ private void Cmov(byte[] r, byte[] x, int len, bool b)
+ {
+ int i;
+ if (b)
+ {
+ Array.Copy(x, 0, r, 0, len);
+ }
+ else
+ {
+ Array.Copy(r, 0, r, 0, len);
+ }
+ }
+
+ public void RandomBytes(byte[] buf, int len)
+ {
+ _random.NextBytes(buf,0,len);
+ }
+ }
+}
+
+
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberIndCpa.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberIndCpa.cs
new file mode 100644
index 000000000..da4f94b88
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberIndCpa.cs
@@ -0,0 +1,293 @@
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Utilities;
+using System;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ internal class KyberIndCpa
+ {
+ KyberEngine Engine;
+ int K;
+ public KyberIndCpa(KyberEngine engine)
+ {
+ Engine = engine;
+ this.K = engine.K;
+ return;
+ }
+
+ private int XOFBlockBytes => Symmetric.Shake128Rate;
+
+ private int GenerateMatrixNBlocks => ((12 * KyberEngine.N / 8 * (1 << 12) / KyberEngine.Q + XOFBlockBytes) / XOFBlockBytes);
+
+ private void GenerateMatrix(PolyVec[] a, byte[] seed, bool transposed)
+ {
+ int ctr, i, j, k;
+ int buflen, off;
+ ShakeDigest XOF = new ShakeDigest(128);
+ byte[] buf = new byte[GenerateMatrixNBlocks * XOFBlockBytes + 2];
+ for (i = 0; i < K; i++)
+ {
+ for (j = 0; j < K; j++)
+ {
+ if (transposed)
+ {
+ XOF = Symmetric.XOF(seed, (byte) i, (byte) j);
+ }
+ else
+ {
+ XOF = Symmetric.XOF(seed, (byte) j, (byte) i);
+ }
+ XOF.DoOutput(buf, 0, GenerateMatrixNBlocks * XOFBlockBytes);
+ buflen = GenerateMatrixNBlocks * XOFBlockBytes;
+ ctr = RejectionSampling(a[i].Vec[j].Coeffs, 0, KyberEngine.N, buf, buflen);
+ while (ctr < KyberEngine.N)
+ {
+ off = buflen % 3;
+ for (k = 0; k < off; k++)
+ {
+ buf[k] = buf[buflen - off + k];
+ }
+ XOF.DoOutput(buf, off, XOFBlockBytes * 2);
+ buflen = off + XOFBlockBytes;
+ ctr += RejectionSampling(a[i].Vec[j].Coeffs, ctr, KyberEngine.N - ctr, buf, buflen);
+ }
+
+ }
+ }
+ return;
+ }
+
+ private int RejectionSampling(short[] r, int off, int len, byte[] buf, int buflen)
+ {
+ int ctr, pos;
+ ushort val0, val1;
+ ctr = pos = 0;
+ while (ctr < len && pos + 3 <= buflen)
+ {
+ val0 = (ushort) ((((ushort) (buf[pos + 0] & 0xFF) >> 0) | ((ushort)(buf[pos + 1] & 0xFF) << 8)) & 0xFFF);
+ val1 = (ushort) ((((ushort) (buf[pos + 1] & 0xFF) >> 4) | ((ushort)(buf[pos + 2] & 0xFF) << 4)) & 0xFFF);
+ pos += 3;
+
+ if (val0 < KyberEngine.Q)
+ {
+ r[off + ctr++] = (short) val0;
+ }
+ if (ctr < len && val1 < KyberEngine.Q)
+ {
+ r[off + ctr++] = (short) val1;
+ }
+ }
+
+ return ctr;
+ }
+
+ public void GenerateKeyPair(byte[] pk, byte[] sk)
+ {
+ uint i;
+ byte[] buf = new byte[2 * KyberEngine.SymBytes];
+ byte nonce = 0;
+ PolyVec[] Matrix = new PolyVec[K];
+ PolyVec e = new PolyVec(Engine), pkpv = new PolyVec(Engine), skpv = new PolyVec(Engine);
+ Sha3Digest Sha3Digest512 = new Sha3Digest(512);
+
+ Engine.RandomBytes(buf, KyberEngine.SymBytes);
+
+ Sha3Digest512.BlockUpdate(buf, 0, KyberEngine.SymBytes);
+ Sha3Digest512.DoFinal(buf, 0);
+
+ //Console.WriteLine(string.Format("buf = {0}", Convert.ToHexString(buf)));
+ byte[] PublicSeed = Arrays.CopyOfRange(buf, 0, KyberEngine.SymBytes);
+ byte[] NoiseSeed = Arrays.CopyOfRange(buf, KyberEngine.SymBytes, 2 * KyberEngine.SymBytes);
+
+ for (i = 0; i < K; i++)
+ {
+ Matrix[i] = new PolyVec(Engine);
+ }
+
+ GenerateMatrix(Matrix, PublicSeed, false);
+
+ for (i = 0; i < K; i++)
+ {
+ skpv.Vec[i].GetNoiseEta1(NoiseSeed, nonce++);
+ }
+
+ for (i = 0; i < K; i++)
+ {
+ e.Vec[i].GetNoiseEta1(NoiseSeed, nonce++);
+ }
+
+
+
+ skpv.Ntt();
+ e.Ntt();
+
+ //Console.WriteLine("skpv = ");
+ //for (i = 0; i < K; i++)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (short coeff in skpv.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+
+ //for (i = 0; i < K; i++)
+ //{
+ // Console.Write("[");
+ // for (int j = 0; j < K; j++)
+ // {
+ // Console.Write("[");
+ // for (int k = 0; k < KyberEngine.N; k++)
+ // {
+ // Console.Write(String.Format("{0:G}, ", Matrix[i].Vec[j].Coeffs[k]));
+ // }
+ // Console.Write("], \n");
+ // }
+ // Console.Write("] \n");
+ //}
+
+ for (i = 0; i < K; i++)
+ {
+ PolyVec.PointwiseAccountMontgomery(pkpv.Vec[i], Matrix[i], skpv, Engine);
+ pkpv.Vec[i].ToMont();
+ }
+
+ //Console.WriteLine("pkpv = ");
+ //for (i = 0; i < K; i++)
+ //{
+ // Console.Write(String.Format("{0} [", i));
+ // foreach (short coeff in pkpv.Vec[i].Coeffs)
+ // {
+ // Console.Write(String.Format("{0}, ", coeff));
+ // }
+ // Console.Write("]\n");
+ //}
+ pkpv.Add(e);
+ pkpv.Reduce();
+
+
+
+
+ PackSecretKey(sk, skpv);
+ PackPublicKey(pk, pkpv, PublicSeed);
+
+
+ return;
+ }
+
+ private void PackSecretKey(byte[] sk, PolyVec skpv)
+ {
+ skpv.ToBytes(sk);
+ }
+
+ private void UnpackSecretKey(PolyVec skpv, byte[] sk)
+ {
+ skpv.FromBytes(sk);
+ }
+
+ private void PackPublicKey(byte[] pk, PolyVec pkpv, byte[] seed)
+ {
+ int i;
+ pkpv.ToBytes(pk);
+ Array.Copy(seed, 0, pk, Engine.PolyVecBytes, KyberEngine.SymBytes);
+ }
+
+ private void UnpackPublicKey(PolyVec pkpv, byte[] seed, byte[] pk)
+ {
+ int i;
+ pkpv.FromBytes(pk);
+ Array.Copy(pk, Engine.PolyVecBytes, seed, 0, KyberEngine.SymBytes);
+ }
+
+ public void Encrypt(byte[] c, byte[] m, byte[] pk, byte[] coins)
+ {
+ int i;
+ byte[] seed = new byte[KyberEngine.SymBytes];
+ byte nonce = (byte)0;
+ PolyVec sp = new PolyVec(Engine), pkpv = new PolyVec(Engine), ep = new PolyVec(Engine), bp = new PolyVec(Engine);
+ PolyVec[] MatrixTransposed = new PolyVec[K];
+ Poly v = new Poly(Engine), k = new Poly(Engine), epp = new Poly(Engine);
+
+ UnpackPublicKey(pkpv, seed, pk);
+
+ k.FromMsg(m);
+
+ for (i = 0; i < K; i++)
+ {
+ MatrixTransposed[i] = new PolyVec(Engine);
+ }
+
+ GenerateMatrix(MatrixTransposed, seed, true);
+
+ for (i = 0; i < K; i++)
+ {
+ sp.Vec[i].GetNoiseEta1(coins, nonce++);
+ }
+
+ for (i = 0; i < K; i++)
+ {
+ ep.Vec[i].GetNoiseEta2(coins, nonce++);
+ }
+ epp.GetNoiseEta2(coins, nonce++);
+
+ sp.Ntt();
+
+ for (i = 0; i < K; i++)
+ {
+ PolyVec.PointwiseAccountMontgomery(bp.Vec[i], MatrixTransposed[i], sp, Engine);
+ }
+
+ PolyVec.PointwiseAccountMontgomery(v, pkpv, sp, Engine);
+
+ bp.InverseNttToMont();
+
+ v.PolyInverseNttToMont();
+
+ bp.Add(ep);
+
+ v.Add(epp);
+ v.Add(k);
+
+ bp.Reduce();
+ v.PolyReduce();
+
+ PackCipherText(c, bp, v);
+ }
+
+ private void PackCipherText(byte[] r, PolyVec b, Poly v)
+ {
+ b.CompressPolyVec(r);
+ v.CompressPoly(r, Engine.PolyVecCompressedBytes);
+ }
+
+ private void UnpackCipherText(PolyVec b, Poly v, byte[] c)
+ {
+ b.DecompressPolyVec(c);
+ v.DecompressPoly(c, Engine.PolyVecCompressedBytes);
+ }
+
+ public void Decrypt(byte[] m, byte[] c, byte[] sk)
+ {
+ PolyVec bp = new PolyVec(Engine),
+ skpv = new PolyVec(Engine);
+ Poly v = new Poly(Engine),
+ mp = new Poly(Engine);
+ int i;
+
+ UnpackCipherText(bp, v, c);
+
+ UnpackSecretKey(skpv, sk);
+
+ bp.Ntt();
+
+ PolyVec.PointwiseAccountMontgomery(mp, skpv, bp, Engine);
+
+ mp.PolyInverseNttToMont();
+ mp.Subtract(v);
+ mp.PolyReduce();
+ mp.ToMsg(m);
+
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberKEMExtractor.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberKEMExtractor.cs
new file mode 100644
index 000000000..e916e5575
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberKEMExtractor.cs
@@ -0,0 +1,36 @@
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ public class KyberKEMExtractor
+ : IEncapsulatedSecretExtractor
+ {
+ private KyberEngine engine;
+
+ private KyberKeyParameters key;
+
+ public KyberKEMExtractor(KyberKeyParameters privParams)
+ {
+ this.key = privParams;
+ InitCipher(key.Parameters);
+ }
+
+ private void InitCipher(KyberParameters param)
+ {
+ engine = param.GetEngine();
+ }
+
+ public byte[] ExtractSecret(byte[] encapsulation)
+ {
+ byte[] sessionKey = new byte[engine.CryptoBytes];
+ engine.KemDecrypt(sessionKey, encapsulation, ((KyberPrivateKeyParameters) key).privateKey);
+ byte[] rv = Arrays.CopyOfRange(sessionKey, 0, key.Parameters.DefaultKeySize / 8);
+ Arrays.Clear(sessionKey);
+ return rv;
+ }
+
+ public int InputSize => engine.CryptoCipherTextBytes;
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberKEMGenerator.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberKEMGenerator.cs
new file mode 100644
index 000000000..c43f10ff1
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberKEMGenerator.cs
@@ -0,0 +1,85 @@
+
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ public class KyberKEMGenerator
+ : IEncapsulatedSecretGenerator
+ {
+ // the source of randomness
+ private SecureRandom sr;
+
+ public KyberKEMGenerator(SecureRandom random)
+ {
+ this.sr = random;
+ }
+
+ public ISecretWithEncapsulation GenerateEncapsulated(AsymmetricKeyParameter recipientKey)
+ {
+ KyberPublicKeyParameters key = (KyberPublicKeyParameters) recipientKey;
+ KyberEngine engine = key.Parameters.GetEngine();
+ engine.Init(sr);
+ byte[] CipherText = new byte[engine.CryptoCipherTextBytes];
+ byte[] SessionKey = new byte[engine.CryptoBytes];
+ engine.KemEncrypt(CipherText, SessionKey, key.publicKey);
+ byte[] rv = Arrays.CopyOfRange(SessionKey, 0, key.Parameters.DefaultKeySize / 8);
+ Arrays.Clear(SessionKey);
+ return new SecretWithEncapsulationImpl(rv, CipherText);
+ }
+
+ private class SecretWithEncapsulationImpl
+ : ISecretWithEncapsulation
+ {
+ private volatile bool hasBeenDestroyed = false;
+
+ private byte[] SessionKey;
+ private byte[] CipherText;
+
+ public SecretWithEncapsulationImpl(byte[] sessionKey, byte[] cipher_text)
+ {
+ this.SessionKey = sessionKey;
+ this.CipherText = cipher_text;
+ }
+
+ public byte[] GetSecret()
+ {
+ CheckDestroyed();
+
+ return Arrays.Clone(SessionKey);
+ }
+
+ public byte[] GetEncapsulation()
+ {
+ CheckDestroyed();
+
+ 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 ArgumentException("data has been destroyed");
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberKeyGenerationParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberKeyGenerationParameters.cs
new file mode 100644
index 000000000..f6adb0071
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberKeyGenerationParameters.cs
@@ -0,0 +1,22 @@
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ public class KyberKeyGenerationParameters
+ : KeyGenerationParameters
+ {
+ private KyberParameters parameters;
+
+ public KyberKeyGenerationParameters(
+ SecureRandom random,
+ KyberParameters KyberParameters)
+ : base(random, 256)
+ {
+ this.parameters = KyberParameters;
+ }
+
+ public KyberParameters Parameters => parameters;
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberKeyPairGenerator.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberKeyPairGenerator.cs
new file mode 100644
index 000000000..39ee496f2
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberKeyPairGenerator.cs
@@ -0,0 +1,49 @@
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ public class KyberKeyPairGenerator
+ : IAsymmetricCipherKeyPairGenerator
+ {
+ private KyberKeyGenerationParameters KyberParams;
+
+ private int k;
+
+ private SecureRandom random;
+
+ private void Initialize(
+ KeyGenerationParameters param)
+ {
+ this.KyberParams = (KyberKeyGenerationParameters) param;
+ this.random = param.Random;
+
+ this.k = this.KyberParams.Parameters.K;
+ }
+
+ private AsymmetricCipherKeyPair GenKeyPair()
+ {
+ KyberEngine engine = KyberParams.Parameters.GetEngine();
+ engine.Init(random);
+ byte[] sk = new byte[engine.CryptoSecretKeyBytes];
+ byte[] pk = new byte[engine.CryptoPublicKeyBytes];
+ engine.GenerateKemKeyPair(pk, sk);
+
+ KyberPublicKeyParameters pubKey = new KyberPublicKeyParameters(KyberParams.Parameters, pk);
+ KyberPrivateKeyParameters privKey = new KyberPrivateKeyParameters(KyberParams.Parameters, sk);
+ return new AsymmetricCipherKeyPair(pubKey, privKey);
+ }
+
+ 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/crystals/kyber/KyberKeyParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberKeyParameters.cs
new file mode 100644
index 000000000..37650cb4d
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberKeyParameters.cs
@@ -0,0 +1,21 @@
+
+using Org.BouncyCastle.Crypto;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ public class KyberKeyParameters
+ : AsymmetricKeyParameter
+ {
+ private KyberParameters parameters;
+
+ public KyberKeyParameters(
+ bool isPrivate,
+ KyberParameters parameters)
+ : base(isPrivate)
+ {
+ this.parameters = parameters;
+ }
+
+ public KyberParameters Parameters => parameters;
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberParameters.cs
new file mode 100644
index 000000000..d0fd8a631
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberParameters.cs
@@ -0,0 +1,38 @@
+
+using System;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ public class KyberParameters
+ : ICipherParameters
+ {
+
+ public static KyberParameters kyber512 = new KyberParameters("kyber512", 2);
+ public static KyberParameters kyber768 = new KyberParameters("kyber768", 3);
+ public static KyberParameters kyber1024 = new KyberParameters("kyber1024", 4);
+
+ private String name;
+ private int k;
+ private KyberEngine engine;
+
+ public KyberParameters(String name, int k)
+ {
+ this.name = name;
+ this.k = k;
+ this.engine = new KyberEngine(k);
+ }
+
+ public String Name => name;
+
+ public int K => k;
+
+ public int DefaultKeySize => 64 * k;
+
+ internal KyberEngine GetEngine()
+ {
+ return engine;
+ }
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs
new file mode 100644
index 000000000..955b44673
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberPrivateKeyParameters.cs
@@ -0,0 +1,21 @@
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ public class KyberPrivateKeyParameters
+ : KyberKeyParameters
+ {
+ internal byte[] privateKey;
+
+ public KyberPrivateKeyParameters(KyberParameters parameters, byte[] privateKey)
+ : base(true, parameters)
+ {
+ this.privateKey = Arrays.Clone(privateKey);
+ }
+
+ public byte[] GetEncoded()
+ {
+ return Arrays.Clone(privateKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs b/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs
new file mode 100644
index 000000000..1092c7e96
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/KyberPublicKeyParameters.cs
@@ -0,0 +1,21 @@
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ public class KyberPublicKeyParameters
+ : KyberKeyParameters
+ {
+ internal byte[] publicKey;
+
+ public byte[] GetEncoded()
+ {
+ return Arrays.Clone(publicKey);
+ }
+
+ public KyberPublicKeyParameters(KyberParameters parameters, byte[] publicKey)
+ : base(false, parameters)
+ {
+ this.publicKey = Arrays.Clone(publicKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/crypto/src/pqc/crypto/crystals/kyber/Ntt.cs b/crypto/src/pqc/crypto/crystals/kyber/Ntt.cs
new file mode 100644
index 000000000..f07e8da26
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/Ntt.cs
@@ -0,0 +1,97 @@
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ internal static class Ntt
+ {
+ public static readonly short[] Zetas = {
+ 2285, 2571, 2970, 1812, 1493, 1422, 287, 202, 3158, 622, 1577, 182, 962,
+ 2127, 1855, 1468, 573, 2004, 264, 383, 2500, 1458, 1727, 3199, 2648, 1017,
+ 732, 608, 1787, 411, 3124, 1758, 1223, 652, 2777, 1015, 2036, 1491, 3047,
+ 1785, 516, 3321, 3009, 2663, 1711, 2167, 126, 1469, 2476, 3239, 3058, 830,
+ 107, 1908, 3082, 2378, 2931, 961, 1821, 2604, 448, 2264, 677, 2054, 2226,
+ 430, 555, 843, 2078, 871, 1550, 105, 422, 587, 177, 3094, 3038, 2869, 1574,
+ 1653, 3083, 778, 1159, 3182, 2552, 1483, 2727, 1119, 1739, 644, 2457, 349,
+ 418, 329, 3173, 3254, 817, 1097, 603, 610, 1322, 2044, 1864, 384, 2114, 3193,
+ 1218, 1994, 2455, 220, 2142, 1670, 2144, 1799, 2051, 794, 1819, 2475, 2459,
+ 478, 3221, 3021, 996, 991, 958, 1869, 1522, 1628
+ };
+
+ public static readonly short[] ZetasInv = {
+ 1701, 1807, 1460, 2371, 2338, 2333, 308, 108, 2851, 870, 854, 1510, 2535,
+ 1278, 1530, 1185, 1659, 1187, 3109, 874, 1335, 2111, 136, 1215, 2945, 1465,
+ 1285, 2007, 2719, 2726, 2232, 2512, 75, 156, 3000, 2911, 2980, 872, 2685,
+ 1590, 2210, 602, 1846, 777, 147, 2170, 2551, 246, 1676, 1755, 460, 291, 235,
+ 3152, 2742, 2907, 3224, 1779, 2458, 1251, 2486, 2774, 2899, 1103, 1275, 2652,
+ 1065, 2881, 725, 1508, 2368, 398, 951, 247, 1421, 3222, 2499, 271, 90, 853,
+ 1860, 3203, 1162, 1618, 666, 320, 8, 2813, 1544, 282, 1838, 1293, 2314, 552,
+ 2677, 2106, 1571, 205, 2918, 1542, 2721, 2597, 2312, 681, 130, 1602, 1871,
+ 829, 2946, 3065, 1325, 2756, 1861, 1474, 1202, 2367, 3147, 1752, 2707, 171,
+ 3127, 3042, 1907, 1836, 1517, 359, 758, 1441
+ };
+
+ private static short FactorQMulMont(short a, short b)
+ {
+ return Reduce.MontgomeryReduce(a * b);
+ }
+
+ public static void NTT(short[] r)
+ {
+ int len, start, j, k;
+ short t, zeta;
+
+ k = 1;
+ for (len = 128; len >= 2; len >>= 1)
+ {
+ for (start = 0; start < 256; start = j + len)
+ {
+ zeta = Zetas[k++];
+ for (j = start; j < start + len; ++j)
+ {
+ t = FactorQMulMont(zeta, r[j + len]);
+ r[j + len] = (short)(r[j] - t);
+ r[j] = (short)(r[j] + t);
+ }
+ }
+ }
+ }
+
+ public static void InvNTT(short[] r)
+ {
+ int len, start, j, k;
+ short t, zeta;
+
+ k = 0;
+ for (len = 2; len <= 128; len <<= 1)
+ {
+ for (start = 0; start < 256; start = j + len)
+ {
+ zeta = ZetasInv[k++];
+ for (j = start; j < start + len; ++j)
+ {
+ t = r[j];
+ r[j] = Reduce.BarrettReduce((short)(t + r[j + len]));
+ r[j + len] = (short)(t - r[j + len]);
+ r[j + len] = FactorQMulMont(zeta, r[j + len]);
+
+ }
+ }
+ }
+ for (j = 0; j < 256; ++j)
+ {
+ r[j] = FactorQMulMont(r[j], Ntt.ZetasInv[127]);
+ }
+ }
+
+ public static void BaseMult(short[] r, int off, short a0, short a1, short b0, short b1, short zeta)
+ {
+ short OutVal0 = FactorQMulMont(a1, b1);
+ OutVal0 = FactorQMulMont(OutVal0, zeta);
+ OutVal0 += FactorQMulMont(a0, b0);
+ r[off] = OutVal0;
+
+ short OutVal1 = FactorQMulMont(a0, b1);
+ OutVal1 += FactorQMulMont(a1, b0);
+ r[off + 1] = OutVal1;
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/kyber/Poly.cs b/crypto/src/pqc/crypto/crystals/kyber/Poly.cs
new file mode 100644
index 000000000..0b226bca1
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/Poly.cs
@@ -0,0 +1,269 @@
+
+using System;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ class Poly
+ {
+ private KyberEngine Engine;
+ public short[] Coeffs { get; set; }
+ private int PolyCompressedBytes;
+ private int Eta1;
+ private int Eta2;
+ private int N;
+ public Poly(KyberEngine engine)
+ {
+ Engine = engine;
+ Coeffs = new short[KyberEngine.N];
+ PolyCompressedBytes = engine.PolyCompressedBytes;
+ Eta1 = engine.Eta1;
+ N = KyberEngine.N;
+ }
+
+ public void GetNoiseEta1(byte[] seed, byte nonce)
+ {
+ byte[] buf = new byte[Engine.Eta1 * KyberEngine.N / 4];
+ Symmetric.PRF(buf, buf.Length, seed, nonce);
+ CBD.Eta(this, buf, Engine.Eta1);
+ }
+
+ public void GetNoiseEta2(byte[] seed, byte nonce)
+ {
+ byte[] buf = new byte[KyberEngine.Eta2 * KyberEngine.N / 4];
+ Symmetric.PRF(buf, buf.Length, seed, nonce);
+ CBD.Eta(this, buf, KyberEngine.Eta2);
+ }
+
+ public void PolyNtt()
+ {
+ Ntt.NTT(Coeffs);
+ PolyReduce();
+ }
+
+ public void PolyInverseNttToMont()
+ {
+ Ntt.InvNTT(Coeffs);
+ }
+
+ public static void BaseMultMontgomery(Poly r, Poly a, Poly b)
+ {
+ int i;
+ for (i = 0; i < KyberEngine.N/4; i++)
+ {
+ Ntt.BaseMult(r.Coeffs, 4 * i,
+ a.Coeffs[4 * i], a.Coeffs[4 * i + 1],
+ b.Coeffs[4 * i], b.Coeffs[4 * i + 1],
+ Ntt.Zetas[64 + i]);
+ Ntt.BaseMult(r.Coeffs, 4 * i + 2,
+ a.Coeffs[4 * i + 2], a.Coeffs[4 * i + 3],
+ b.Coeffs[4 * i + 2], b.Coeffs[4 * i + 3],
+ (short) (-1 * Ntt.Zetas[64 + i]));
+ }
+ }
+
+ public void ToMont()
+ {
+ int i;
+ const short f = (short) ((1UL << 32) % KyberEngine.Q);
+ for (i = 0; i < KyberEngine.N; i++)
+ {
+ Coeffs[i] = Reduce.MontgomeryReduce(Coeffs[i] * f);
+ }
+ }
+
+ public void Add(Poly a)
+ {
+ int i;
+ for (i = 0; i < N; i++)
+ {
+ Coeffs[i] += a.Coeffs[i];
+ }
+ }
+
+ public void Subtract(Poly a)
+ {
+ int i;
+ for (i = 0; i < N; i++)
+ {
+ Coeffs[i] = (short) (a.Coeffs[i] - Coeffs[i]);
+ }
+ }
+
+ public void PolyReduce()
+ {
+ int i;
+ for (i = 0; i < KyberEngine.N; i++)
+ {
+ Coeffs[i] = Reduce.BarrettReduce(Coeffs[i]);
+ }
+ }
+
+ public void CompressPoly(byte[] r, int off)
+ {
+ int i, j;
+ byte[] t = new byte[8];
+ int count = 0;
+ CondSubQ();
+
+ if (Engine.PolyCompressedBytes == 128)
+ {
+ for (i = 0; i < KyberEngine.N / 8; i++)
+ {
+ for (j = 0; j < 8; j++)
+ {
+ t[j] =
+ (byte)(((((Coeffs[8 * i + j]) << 4)
+ +
+ (KyberEngine.Q / 2)
+ ) / KyberEngine.Q)
+ & 15);
+ }
+
+ r[off + count + 0] = (byte)(t[0] | (t[1] << 4));
+ r[off + count + 1] = (byte)(t[2] | (t[3] << 4));
+ r[off + count + 2] = (byte)(t[4] | (t[5] << 4));
+ r[off + count + 3] = (byte)(t[6] | (t[7] << 4));
+ count += 4;
+ }
+ }
+ else if (Engine.PolyCompressedBytes == 160)
+ {
+ for (i = 0; i < KyberEngine.N / 8; i++)
+ {
+ for (j = 0; j < 8; j++)
+ {
+ t[j] =
+ (byte)((((Coeffs[8 * i + j] << 5)
+ +
+ (KyberEngine.Q / 2)
+ ) / KyberEngine.Q
+ ) & 31
+ );
+ }
+ r[off + count + 0] = (byte)((t[0] >> 0) | (t[1] << 5));
+ r[off + count + 1] = (byte)((t[1] >> 3) | (t[2] << 2) | (t[3] << 7));
+ r[off + count + 2] = (byte)((t[3] >> 1) | (t[4] << 4));
+ r[off + count + 3] = (byte)((t[4] >> 4) | (t[5] << 1) | (t[6] << 6));
+ r[off + count + 4] = (byte)((t[6] >> 2) | (t[7] << 3));
+ count += 5;
+ }
+ }
+ else
+ {
+ throw new ArgumentException("PolyCompressedBytes is neither 128 or 160!");
+ }
+ }
+
+ public void DecompressPoly(byte[] CompressedCipherText, int off)
+ {
+ int i, count = off;
+
+ if (Engine.PolyCompressedBytes == 128)
+ {
+ for (i = 0; i < KyberEngine.N / 2; i++)
+ {
+ Coeffs[2 * i + 0] = (short)((((short)((CompressedCipherText[count] & 0xFF) & 15) * KyberEngine.Q) + 8) >> 4);
+ Coeffs[2 * i + 1] = (short)((((short)((CompressedCipherText[count] & 0xFF) >> 4) * KyberEngine.Q) + 8) >> 4);
+ count += 1;
+ }
+ }
+ else if (Engine.PolyCompressedBytes == 160)
+ {
+ int j;
+ byte[] t = new byte[8];
+ for (i = 0; i < KyberEngine.N / 8; i++)
+ {
+ t[0] = (byte)((CompressedCipherText[count + 0] & 0xFF) >> 0);
+ t[1] = (byte)(((CompressedCipherText[count + 0] & 0xFF) >> 5) | ((CompressedCipherText[count + 1] & 0xFF) << 3));
+ t[2] = (byte)((CompressedCipherText[count + 1] & 0xFF) >> 2);
+ t[3] = (byte)(((CompressedCipherText[count + 1] & 0xFF) >> 7) | ((CompressedCipherText[count + 2] & 0xFF) << 1));
+ t[4] = (byte)(((CompressedCipherText[count + 2] & 0xFF) >> 4) | ((CompressedCipherText[count + 3] & 0xFF) << 4));
+ t[5] = (byte)((CompressedCipherText[count + 3] & 0xFF) >> 1);
+ t[6] = (byte)(((CompressedCipherText[count + 3] & 0xFF) >> 6) | ((CompressedCipherText[count + 4] & 0xFF) << 2));
+ t[7] = (byte)((CompressedCipherText[count + 4] & 0xFF) >> 3);
+ count += 5;
+ for (j = 0; j < 8; j++)
+ {
+ Coeffs[8 * i + j] = (short)(((t[j] & 31) * KyberEngine.Q + 16) >> 5);
+ }
+ }
+ }
+ else
+ {
+ throw new ArgumentException("PolyCompressedBytes is neither 128 or 160!");
+ }
+ }
+
+ public void ToBytes(byte[] r, int off)
+ {
+ int i;
+ ushort t0, t1;
+
+ CondSubQ();
+
+ for (i = 0; i < KyberEngine.N/2; i++)
+ {
+ t0 = (ushort) Coeffs[2 * i];
+ t1 = (ushort) Coeffs[2 * i + 1];
+ r[off + 3 * i + 0] = (byte) (ushort) (t0 >> 0);
+ r[off + 3 * i + 1] = (byte)((t0 >> 8) | (ushort) (t1 << 4));
+ r[off + 3 * i + 2] = (byte) (ushort) (t1 >> 4);
+ }
+ }
+
+ public void FromBytes(byte[] a, int off)
+ {
+ int i;
+ for (i = 0; i < KyberEngine.N / 2; i++)
+ {
+ Coeffs[2 * i] = (short) ((((a[off + 3 * i + 0] & 0xFF) >> 0) | (ushort)((a[off + 3 * i + 1] & 0xFF) << 8)) & 0xFFF);
+ Coeffs[2 * i + 1] = (short) ((((a[off + 3 * i + 1] & 0xFF) >> 4) | (ushort)((a[off + 3 * i + 2] & 0xFF) << 4)) & 0xFFF);
+ }
+ }
+
+ public void ToMsg(byte[] msg)
+ {
+ int i, j;
+ short t;
+
+ CondSubQ();
+
+ for (i = 0; i < KyberEngine.N / 8; i++)
+ {
+ msg[i] = 0;
+ for (j = 0; j < 8; j++)
+ {
+ t = (short)(((((short)(Coeffs[8 * i + j] << 1) + KyberEngine.Q / 2) / KyberEngine.Q) & 1));
+ msg[i] |= (byte)(t << j);
+ }
+ }
+ }
+
+ public void FromMsg(byte[] m)
+ {
+ int i, j;
+ short mask;
+ if (m.Length != KyberEngine.N / 8)
+ {
+ throw new ArgumentException("KYBER_INDCPA_MSGBYTES must be equal to KYBER_N/8 bytes!");
+ }
+ for (i = 0; i < KyberEngine.N / 8; i++)
+ {
+ for (j = 0; j < 8; j++)
+ {
+ mask = (short)((-1) * (short)(((m[i] & 0xFF) >> j) & 1));
+ Coeffs[8 * i + j] = (short)(mask & ((KyberEngine.Q + 1) / 2));
+ }
+ }
+ }
+
+ public void CondSubQ()
+ {
+ int i;
+ for (i = 0; i < KyberEngine.N; i++)
+ {
+ Coeffs[i] = Reduce.CondSubQ(Coeffs[i]);
+ }
+ }
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/kyber/PolyVec.cs b/crypto/src/pqc/crypto/crystals/kyber/PolyVec.cs
new file mode 100644
index 000000000..7c915dcce
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/PolyVec.cs
@@ -0,0 +1,229 @@
+using Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber;
+using System;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ class PolyVec
+ {
+ private KyberEngine Engine;
+ private int K;
+ private int PolyVecBytes;
+ public Poly[] Vec;
+
+
+
+
+ public PolyVec(KyberEngine engine)
+ {
+ Engine = engine;
+ K = engine.K;
+ PolyVecBytes = engine.PolyVecBytes;
+ Vec = new Poly[engine.K];
+ for (int i = 0; i < K; i++)
+ {
+ Vec[i] = new Poly(engine);
+ }
+ }
+
+ public void Ntt()
+ {
+ int i;
+ for (i = 0; i < K; i++)
+ {
+ Vec[i].PolyNtt();
+ }
+ }
+
+ public void InverseNttToMont()
+ {
+ int i;
+ for (i = 0; i < K; i++)
+ {
+ Vec[i].PolyInverseNttToMont();
+ }
+ }
+
+ public static void PointwiseAccountMontgomery(Poly r, PolyVec a, PolyVec b, KyberEngine engine)
+ {
+ int i;
+ Poly t = new Poly(engine);
+ Poly.BaseMultMontgomery(r, a.Vec[0], b.Vec[0]);
+ for (i = 1; i < engine.K; i++)
+ {
+ Poly.BaseMultMontgomery(t, a.Vec[i], b.Vec[i]);
+ r.Add(t);
+ }
+ r.PolyReduce();
+ }
+
+ public void Add(PolyVec a)
+ {
+ uint i;
+ for (i = 0; i < K; i++)
+ {
+ Vec[i].Add(a.Vec[i]);
+ }
+ }
+
+ public void Reduce()
+ {
+ int i;
+ for (i = 0; i < K; i++)
+ {
+ Vec[i].PolyReduce();
+ }
+ }
+
+ public void CompressPolyVec(byte[] r)
+ {
+ int i, j, k;
+ ConditionalSubQ();
+ short[] t;
+ int count = 0;
+ if (Engine.PolyVecCompressedBytes == K * 320)
+ {
+ t = new short[4];
+ for (i = 0; i < K; i++)
+ {
+ for (j = 0; j < KyberEngine.N / 4; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ t[k] = (short)
+ (
+ (
+ (((uint) Vec[i].Coeffs[4 * j + k] << 10)
+ + (KyberEngine.Q / 2))
+ / KyberEngine.Q)
+ & 0x3ff);
+ }
+ r[count + 0] = (byte)(t[0] >> 0);
+ r[count + 1] = (byte)((t[0] >> 8) | (t[1] << 2));
+ r[count + 2] = (byte)((t[1] >> 6) | (t[2] << 4));
+ r[count + 3] = (byte)((t[2] >> 4) | (t[3] << 6));
+ r[count + 4] = (byte)((t[3] >> 2));
+ count += 5;
+ }
+ }
+ }
+ else if (Engine.PolyVecCompressedBytes == K * 352)
+ {
+ t = new short[8];
+ for (i = 0; i < K; i++)
+ {
+ for (j = 0; j < KyberEngine.N / 8; j++)
+ {
+ for (k = 0; k < 8; k++)
+ {
+ t[k] = (short)
+ (
+ (
+ (((uint) Vec[i].Coeffs[8 * j + k] << 11)
+ + (KyberEngine.Q / 2))
+ / KyberEngine.Q)
+ & 0x7ff);
+ }
+ r[count + 0] = (byte)((t[0] >> 0));
+ r[count + 1] = (byte)((t[0] >> 8) | (t[1] << 3));
+ r[count + 2] = (byte)((t[1] >> 5) | (t[2] << 6));
+ r[count + 3] = (byte)((t[2] >> 2));
+ r[count + 4] = (byte)((t[2] >> 10) | (t[3] << 1));
+ r[count + 5] = (byte)((t[3] >> 7) | (t[4] << 4));
+ r[count + 6] = (byte)((t[4] >> 4) | (t[5] << 7));
+ r[count + 7] = (byte)((t[5] >> 1));
+ r[count + 8] = (byte)((t[5] >> 9) | (t[6] << 2));
+ r[count + 9] = (byte)((t[6] >> 6) | (t[7] << 5));
+ r[count + 10] = (byte)((t[7] >> 3));
+ count += 11;
+ }
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Kyber PolyVecCompressedBytes neither 320 * KyberK or 352 * KyberK!");
+ }
+ }
+
+ public void DecompressPolyVec(byte[] CompressedCipherText)
+ {
+ int i, j, k, count = 0;
+
+ if (Engine.PolyVecCompressedBytes == (K * 320))
+ {
+ short[] t = new short[4];
+ for (i = 0; i < K; i++)
+ {
+ for (j = 0; j < KyberEngine.N / 4; j++)
+ {
+ t[0] = (short)(((CompressedCipherText[count] & 0xFF) >> 0) | ((ushort)(CompressedCipherText[count + 1] & 0xFF) << 8));
+ t[1] = (short)(((CompressedCipherText[count + 1] & 0xFF) >> 2) | ((ushort)(CompressedCipherText[count + 2] & 0xFF) << 6));
+ t[2] = (short)(((CompressedCipherText[count + 2] & 0xFF) >> 4) | ((ushort)(CompressedCipherText[count + 3] & 0xFF) << 4));
+ t[3] = (short)(((CompressedCipherText[count + 3] & 0xFF) >> 6) | ((ushort)(CompressedCipherText[count + 4] & 0xFF) << 2));
+ count += 5;
+ for (k = 0; k < 4; k++)
+ {
+ Vec[i].Coeffs[4 * j + k] = (short)(((t[k] & 0x3FF) * KyberEngine.Q + 512) >> 10);
+ }
+ }
+
+ }
+
+ }
+ else if (Engine.PolyVecCompressedBytes == (K * 352))
+ {
+ short[] t = new short[8];
+ for (i = 0; i < K; i++)
+ {
+ for (j = 0; j < KyberEngine.N / 8; j++)
+ {
+ t[0] = (short)(((CompressedCipherText[count] & 0xFF) >> 0) | ((ushort)(CompressedCipherText[count + 1] & 0xFF) << 8));
+ t[1] = (short)(((CompressedCipherText[count + 1] & 0xFF) >> 3) | ((ushort)(CompressedCipherText[count + 2] & 0xFF) << 5));
+ t[2] = (short)(((CompressedCipherText[count + 2] & 0xFF) >> 6) | ((ushort)(CompressedCipherText[count + 3] & 0xFF) << 2) | ((ushort)((CompressedCipherText[count + 4] & 0xFF) << 10)));
+ t[3] = (short)(((CompressedCipherText[count + 4] & 0xFF) >> 1) | ((ushort)(CompressedCipherText[count + 5] & 0xFF) << 7));
+ t[4] = (short)(((CompressedCipherText[count + 5] & 0xFF) >> 4) | ((ushort)(CompressedCipherText[count + 6] & 0xFF) << 4));
+ t[5] = (short)(((CompressedCipherText[count + 6] & 0xFF) >> 7) | ((ushort)(CompressedCipherText[count + 7] & 0xFF) << 1) | ((ushort)((CompressedCipherText[count + 8] & 0xFF) << 9)));
+ t[6] = (short)(((CompressedCipherText[count + 8] & 0xFF) >> 2) | ((ushort)(CompressedCipherText[count + 9] & 0xFF) << 6));
+ t[7] = (short)(((CompressedCipherText[count + 9] & 0xFF) >> 5) | ((ushort)(CompressedCipherText[count + 10] & 0xFF) << 3));
+ count += 11;
+ for (k = 0; k < 8; k++)
+ {
+ Vec[i].Coeffs[8 * j + k] = (short)(((t[k] & 0x7FF) * KyberEngine.Q + 1024) >> 11);
+ }
+ }
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Kyber PolyVecCompressedBytes neither 320 * KyberK or 352 * KyberK!");
+ }
+ }
+
+ public void ToBytes(byte[] r)
+ {
+ int i;
+ for (i = 0; i < K; i++)
+ {
+ Vec[i].ToBytes(r, i * KyberEngine.PolyBytes);
+ }
+ }
+
+ public void FromBytes(byte[] pk)
+ {
+ int i;
+ for (i = 0; i < K; i++)
+ {
+ Vec[i].FromBytes(pk, i * KyberEngine.PolyBytes);
+ }
+ }
+
+ private void ConditionalSubQ()
+ {
+ int i;
+ for (i = 0; i < K; i++)
+ {
+ Vec[i].CondSubQ();
+ }
+ }
+
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/kyber/Reduce.cs b/crypto/src/pqc/crypto/crystals/kyber/Reduce.cs
new file mode 100644
index 000000000..b222cdc17
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/Reduce.cs
@@ -0,0 +1,35 @@
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ internal static class Reduce
+ {
+ public static short MontgomeryReduce(int a)
+ {
+ int t;
+ short u;
+
+ u = (short)(a * KyberEngine.QInv);
+ t = (int)(u * KyberEngine.Q);
+ t = a - t;
+ t >>= 16;
+ return (short)t;
+ }
+
+ public static short BarrettReduce(short a)
+ {
+ short t;
+ short v = (short)(((1U << 26) + (KyberEngine.Q / 2)) / KyberEngine.Q);
+ t = (short)((v * a) >> 26);
+ t = (short)(t * KyberEngine.Q);
+ return (short)(a - t);
+ }
+
+ public static short CondSubQ(short a)
+ {
+ a -= KyberEngine.Q;
+ a += (short) ((a >> 15) & KyberEngine.Q);
+ return a;
+ }
+
+ }
+}
diff --git a/crypto/src/pqc/crypto/crystals/kyber/Symmetric.cs b/crypto/src/pqc/crypto/crystals/kyber/Symmetric.cs
new file mode 100644
index 000000000..711209e2b
--- /dev/null
+++ b/crypto/src/pqc/crypto/crystals/kyber/Symmetric.cs
@@ -0,0 +1,32 @@
+using Org.BouncyCastle.Crypto.Digests;
+using System;
+
+namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
+{
+ internal static class Symmetric
+ {
+ public static readonly int Shake128Rate = 168;
+ public static void PRF(byte[] outbuf, int outlen, byte[] key, byte nonce)
+ {
+ uint i;
+ byte[] ExtraKey = new byte[KyberEngine.SymBytes + 1];
+ Array.Copy(key, ExtraKey, KyberEngine.SymBytes);
+ ExtraKey[KyberEngine.SymBytes] = nonce;
+ ShakeDigest Shake256 = new ShakeDigest(256);
+ Shake256.BlockUpdate(ExtraKey, 0, KyberEngine.SymBytes + 1);
+ Shake256.DoFinal(outbuf, 0, outlen);
+ }
+
+ public static ShakeDigest XOF(byte[] seed, byte a, byte b)
+ {
+ ShakeDigest OutDigest = new ShakeDigest(128);
+ byte[] buf = new byte[seed.Length + 2];
+ Array.Copy(seed, buf, seed.Length);
+ buf[seed.Length] = a;
+ buf[seed.Length + 1] = b;
+ OutDigest.BlockUpdate(buf, 0, buf.Length);
+ return OutDigest;
+ }
+
+ }
+}
|