<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />

IndCpa

class IndCpa
using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Crypto.Kems.MLKem { internal class IndCpa { private readonly MLKemEngine m_engine; private readonly Symmetric m_symmetric; private int GenerateMatrixNBlocks => (472 + m_symmetric.XofBlockBytes) / m_symmetric.XofBlockBytes; internal IndCpa(MLKemEngine engine) { m_engine = engine; m_symmetric = engine.Symmetric; } private void GenerateMatrix(PolyVec[] a, ReadOnlySpan<byte> seed, bool transposed) { int k = m_engine.K; byte[] array = new byte[GenerateMatrixNBlocks * m_symmetric.XofBlockBytes + 2]; for (int i = 0; i < k; i++) { for (int j = 0; j < k; j++) { if (transposed) m_symmetric.XofAbsorb(seed, (byte)i, (byte)j); else m_symmetric.XofAbsorb(seed, (byte)j, (byte)i); m_symmetric.XofSqueezeBlocks(array.AsSpan(0, GenerateMatrixNBlocks * m_symmetric.XofBlockBytes)); int num = GenerateMatrixNBlocks * m_symmetric.XofBlockBytes; for (int l = RejectionSampling(a[i].m_vec[j].m_coeffs, 0, 256, array, num); l < 256; l += RejectionSampling(a[i].m_vec[j].m_coeffs, l, 256 - l, array, num)) { int num2 = num % 3; for (int m = 0; m < num2; m++) { array[m] = array[num - num2 + m]; } m_symmetric.XofSqueezeBlocks(array.AsSpan(num2, m_symmetric.XofBlockBytes * 2)); num = num2 + m_symmetric.XofBlockBytes; } } } } private int RejectionSampling(short[] r, int off, int len, byte[] buf, int buflen) { int num = 0; int num2 = 0; while (num < len && num2 + 3 <= buflen) { ushort num3 = (ushort)(((ushort)(buf[num2] & 255) | ((ushort)(buf[num2 + 1] & 255) << 8)) & 4095); ushort num4 = (ushort)((((ushort)(buf[num2 + 1] & 255) >> 4) | ((ushort)(buf[num2 + 2] & 255) << 4)) & 4095); num2 += 3; if (num3 < 3329) r[off + num++] = (short)num3; if (num < len && num4 < 3329) r[off + num++] = (short)num4; } return num; } internal void GenerateKeyPair(byte[] d, out byte[] pk, out byte[] sk) { int k = m_engine.K; byte[] array = new byte[64]; byte b = 0; PolyVec[] array2 = new PolyVec[k]; PolyVec polyVec = new PolyVec(m_engine); PolyVec polyVec2 = new PolyVec(m_engine); PolyVec polyVec3 = new PolyVec(m_engine); m_symmetric.Hash_g(Arrays.Append(d, (byte)k), array); Span<byte> span = array.AsSpan(0, 32); Span<byte> span2 = array.AsSpan(32, 32); for (int i = 0; i < k; i++) { array2[i] = new PolyVec(m_engine); } GenerateMatrix(array2, span, false); for (int j = 0; j < k; j++) { Poly obj = polyVec3.m_vec[j]; ReadOnlySpan<byte> seed = span2; byte num = b; b = (byte)(num + 1); obj.GetNoiseEta1(seed, num); } for (int l = 0; l < k; l++) { Poly obj2 = polyVec.m_vec[l]; ReadOnlySpan<byte> seed2 = span2; byte num2 = b; b = (byte)(num2 + 1); obj2.GetNoiseEta1(seed2, num2); } polyVec3.Ntt(); polyVec.Ntt(); for (int m = 0; m < k; m++) { PolyVec.PointwiseAccountMontgomery(polyVec2.m_vec[m], array2[m], polyVec3, m_engine); polyVec2.m_vec[m].ToMont(); } polyVec2.Add(polyVec); polyVec2.Reduce(); PackSecretKey(out sk, polyVec3); PackPublicKey(out pk, polyVec2, span); } private void PackSecretKey(out byte[] sk, PolyVec skpv) { sk = new byte[m_engine.PolyVecBytes]; skpv.ToBytes(sk); } private void PackPublicKey(out byte[] pk, PolyVec pkpv, ReadOnlySpan<byte> seed) { pk = new byte[m_engine.IndCpaPublicKeyBytes]; pkpv.ToBytes(pk); seed.Slice(0, 32).CopyTo(pk.AsSpan(m_engine.PolyVecBytes)); } private void UnpackSecretKey(PolyVec skpv, ReadOnlySpan<byte> sk) { skpv.FromBytes(sk); } private void UnpackPublicKey(PolyVec pkpv, Span<byte> seed, ReadOnlySpan<byte> pk) { pkpv.FromBytes(pk); pk.Slice(m_engine.PolyVecBytes, 32).CopyTo(seed); } public void Encrypt(Span<byte> encapsulation, ReadOnlySpan<byte> m, ReadOnlySpan<byte> pk, ReadOnlySpan<byte> coins) { int k = m_engine.K; byte[] array = new byte[32]; byte b = 0; PolyVec polyVec = new PolyVec(m_engine); PolyVec polyVec2 = new PolyVec(m_engine); PolyVec polyVec3 = new PolyVec(m_engine); PolyVec polyVec4 = new PolyVec(m_engine); PolyVec[] array2 = new PolyVec[k]; Poly poly = new Poly(m_engine); Poly poly2 = new Poly(m_engine); Poly poly3 = new Poly(m_engine); UnpackPublicKey(polyVec2, array, pk); poly2.FromMsg(m); for (int i = 0; i < k; i++) { array2[i] = new PolyVec(m_engine); } GenerateMatrix(array2, array, true); for (int j = 0; j < k; j++) { Poly obj = polyVec.m_vec[j]; byte num = b; b = (byte)(num + 1); obj.GetNoiseEta1(coins, num); } for (int l = 0; l < k; l++) { Poly obj2 = polyVec3.m_vec[l]; byte num2 = b; b = (byte)(num2 + 1); obj2.GetNoiseEta2(coins, num2); } Poly poly4 = poly3; byte num3 = b; b = (byte)(num3 + 1); poly4.GetNoiseEta2(coins, num3); polyVec.Ntt(); for (int n = 0; n < k; n++) { PolyVec.PointwiseAccountMontgomery(polyVec4.m_vec[n], array2[n], polyVec, m_engine); } PolyVec.PointwiseAccountMontgomery(poly, polyVec2, polyVec, m_engine); polyVec4.InverseNttToMont(); poly.PolyInverseNttToMont(); polyVec4.Add(polyVec3); poly.Add(poly3); poly.Add(poly2); polyVec4.Reduce(); poly.PolyReduce(); PackCipherText(encapsulation, polyVec4, poly); } private void PackCipherText(Span<byte> r, PolyVec b, Poly v) { b.CompressPolyVec(r); int polyVecCompressedBytes = m_engine.PolyVecCompressedBytes; v.CompressPoly(r.Slice(polyVecCompressedBytes, r.Length - polyVecCompressedBytes)); } private void UnpackCipherText(PolyVec b, Poly v, ReadOnlySpan<byte> c) { b.DecompressPolyVec(c); int polyVecCompressedBytes = m_engine.PolyVecCompressedBytes; v.DecompressPoly(c.Slice(polyVecCompressedBytes, c.Length - polyVecCompressedBytes)); } internal void Decrypt(Span<byte> m, ReadOnlySpan<byte> encapsulation, ReadOnlySpan<byte> sk) { PolyVec polyVec = new PolyVec(m_engine); PolyVec polyVec2 = new PolyVec(m_engine); Poly poly = new Poly(m_engine); Poly poly2 = new Poly(m_engine); UnpackCipherText(polyVec, poly, encapsulation); UnpackSecretKey(polyVec2, sk); polyVec.Ntt(); PolyVec.PointwiseAccountMontgomery(poly2, polyVec2, polyVec, m_engine); poly2.PolyInverseNttToMont(); poly2.Subtract(poly); poly2.PolyReduce(); poly2.ToMsg(m); } internal byte[] PackPublicKey(PolyVec polyVec, ReadOnlySpan<byte> seed) { byte[] array = new byte[m_engine.IndCpaPublicKeyBytes]; polyVec.ToBytes(array); seed.Slice(0, 32).CopyTo(array.AsSpan(m_engine.PolyVecBytes)); return array; } internal byte[] UnpackPublicKey(PolyVec polyVec, ReadOnlySpan<byte> pk) { byte[] array = new byte[32]; polyVec.FromBytes(pk); pk.Slice(m_engine.PolyVecBytes, 32).CopyTo(array); return array; } } }