diff options
author | David Hook <dgh@cryptoworkshop.com> | 2022-08-25 12:56:49 +1000 |
---|---|---|
committer | David Hook <dgh@cryptoworkshop.com> | 2022-08-25 12:56:49 +1000 |
commit | c50de2843354b736ee5dcd4482f91d4155c7f6fb (patch) | |
tree | 910e2252be5d3f6d932494a7a3de107965b9fd81 /crypto/src/pqc | |
parent | minor refactoring, added sampling (diff) | |
download | BouncyCastle.NET-ed25519-c50de2843354b736ee5dcd4482f91d4155c7f6fb.tar.xz |
initial CRYSTALS-Kyber implementation
Diffstat (limited to 'crypto/src/pqc')
16 files changed, 1513 insertions, 0 deletions
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; + } + + } +} |