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

MLKemEngine

class MLKemEngine
using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Crypto.Kems.MLKem { internal class MLKemEngine { private readonly IndCpa m_indCpa; private readonly SecureRandom m_random; internal const int N = 256; internal const int Q = 3329; internal const int QInv = 62209; internal const int SymBytes = 32; private const int SharedSecretBytes = 32; internal const int PolyBytes = 384; internal const int Eta2 = 2; internal int IndCpaMsgBytes = 32; internal Symmetric Symmetric { get; set; } internal int K { get; set; } internal int PolyVecBytes { get; set; } internal int PolyCompressedBytes { get; set; } internal int PolyVecCompressedBytes { get; set; } internal int Eta1 { get; set; } internal int IndCpaPublicKeyBytes { get; set; } internal int IndCpaSecretKeyBytes { get; set; } internal int IndCpaBytes { get; set; } internal int PublicKeyBytes { get; set; } internal int SecretKeyBytes { get; set; } internal int CipherTextBytes { get; set; } internal int CryptoBytes { get; set; } internal int CryptoSecretKeyBytes { get; set; } internal int CryptoPublicKeyBytes { get; set; } internal int CryptoCipherTextBytes { get; set; } internal MLKemEngine(int k, SecureRandom random) { 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; } PolyVecBytes = k * 384; IndCpaPublicKeyBytes = PolyVecBytes + 32; IndCpaSecretKeyBytes = PolyVecBytes; IndCpaBytes = PolyVecCompressedBytes + PolyCompressedBytes; PublicKeyBytes = IndCpaPublicKeyBytes; SecretKeyBytes = IndCpaSecretKeyBytes + IndCpaPublicKeyBytes + 64; CipherTextBytes = IndCpaBytes; CryptoBytes = 32; CryptoSecretKeyBytes = SecretKeyBytes; CryptoPublicKeyBytes = PublicKeyBytes; CryptoCipherTextBytes = CipherTextBytes; Symmetric = new Symmetric.ShakeSymmetric(); m_indCpa = new IndCpa(this); m_random = random; } internal void GenerateKemKeyPair(out byte[] t, out byte[] rho, out byte[] s, out byte[] hpk, out byte[] nonce, out byte[] seed) { byte[] array = new byte[32]; byte[] array2 = new byte[32]; m_random.NextBytes(array); m_random.NextBytes(array2); GenerateKemKeyPairInternal(array, array2, out t, out rho, out s, out hpk, out nonce, out seed); } internal void GenerateKemKeyPairInternal(byte[] d, byte[] z, out byte[] t, out byte[] rho, out byte[] s, out byte[] hpk, out byte[] nonce, out byte[] seed) { m_indCpa.GenerateKeyPair(d, out byte[] pk, out s); hpk = new byte[32]; Symmetric.Hash_h(pk, 0, pk.Length, hpk, 0); t = Arrays.CopyOfRange(pk, 0, IndCpaPublicKeyBytes - 32); rho = Arrays.CopyOfRange(pk, IndCpaPublicKeyBytes - 32, IndCpaPublicKeyBytes); nonce = z; seed = Arrays.Concatenate(d, z); } internal void KemDecrypt(byte[] secBuf, int secOff, byte[] encBuf, int encOff, byte[] secretKey) { byte[] array = new byte[64]; byte[] array2 = new byte[64]; byte[] array3 = new byte[CipherTextBytes]; byte[] pk = Arrays.CopyOfRange(secretKey, IndCpaSecretKeyBytes, secretKey.Length); byte[] array4 = new byte[32 + CipherTextBytes]; m_indCpa.Decrypt(array, encBuf, encOff, secretKey); Array.Copy(secretKey, SecretKeyBytes - 64, array, 32, 32); Symmetric.Hash_g(array, array2); m_indCpa.Encrypt(array3, 0, Arrays.CopyOf(array, 32), pk, Arrays.CopyOfRange(array2, 32, array2.Length)); bool b = !Arrays.FixedTimeEquals(array3.Length, array3, 0, encBuf, encOff); Symmetric.Hash_h(encBuf, encOff, CipherTextBytes, array2, 32); Array.Copy(secretKey, SecretKeyBytes - 32, array4, 0, 32); Array.Copy(encBuf, encOff, array4, 32, CipherTextBytes); Symmetric.Kdf(array4, array4); CMov(array2, array4, 32, b); Array.Copy(array2, 0, secBuf, secOff, 32); } internal void KemEncrypt(byte[] encBuf, int encOff, byte[] secBuf, int secOff, byte[] pk, byte[] randBytes) { if (pk.Length != IndCpaPublicKeyBytes) throw new ArgumentException("Input validation Error: Type check failed for ml-kem encapsulation"); PolyVec polyVec = new PolyVec(this); byte[] seed = m_indCpa.UnpackPublicKey(polyVec, pk); if (!Arrays.AreEqual(m_indCpa.PackPublicKey(polyVec, seed), pk)) throw new ArgumentException("Input validation: Modulus check failed for ml-kem encapsulation"); KemEncryptInternal(encBuf, encOff, secBuf, secOff, pk, randBytes); } internal void KemEncryptInternal(byte[] encBuf, int encOff, byte[] secBuf, int secOff, byte[] pk, byte[] randBytes) { byte[] array = new byte[64]; byte[] array2 = new byte[64]; Array.Copy(randBytes, 0, array, 0, 32); Symmetric.Hash_h(pk, 0, pk.Length, array, 32); Symmetric.Hash_g(array, array2); m_indCpa.Encrypt(encBuf, encOff, Arrays.CopyOfRange(array, 0, 32), pk, Arrays.CopyOfRange(array2, 32, 64)); Array.Copy(array2, 0, secBuf, secOff, 32); } private static void CMov(byte[] r, byte[] x, int len, bool b) { if (b) Array.Copy(x, 0, r, 0, len); else Array.Copy(r, 0, r, 0, len); } internal void RandomBytes(byte[] buf, int len) { m_random.NextBytes(buf, 0, len); } } }