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

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.AsSpan(), hpk.AsSpan()); t = Arrays.CopyOfRange(pk, 0, IndCpaPublicKeyBytes - 32); rho = Arrays.CopyOfRange(pk, IndCpaPublicKeyBytes - 32, IndCpaPublicKeyBytes); nonce = z; seed = Arrays.Concatenate(d, z); } internal unsafe void KemDecrypt(Span<byte> secret, ReadOnlySpan<byte> encapsulation, byte[] secretKey) { Span<byte> span = new Span<byte>(stackalloc byte[64], 64); Span<byte> span2 = new Span<byte>(stackalloc byte[64], 64); int cipherTextBytes = CipherTextBytes; Span<byte> span3 = new Span<byte>(stackalloc byte[(int)(uint)cipherTextBytes], cipherTextBytes); ReadOnlySpan<byte> pk = secretKey.AsSpan(IndCpaSecretKeyBytes); cipherTextBytes = 32 + CipherTextBytes; Span<byte> span4 = new Span<byte>(stackalloc byte[(int)(uint)cipherTextBytes], cipherTextBytes); m_indCpa.Decrypt(span2, encapsulation, secretKey); Span<byte> span5 = secretKey.AsSpan(SecretKeyBytes - 64, 32); span5.CopyTo(span2.Slice(32, span2.Length - 32)); Symmetric.Hash_g(span2, span); m_indCpa.Encrypt(span3, span2.Slice(0, 32), pk, span.Slice(32, span.Length - 32)); bool b = !Arrays.FixedTimeEquals(span3, encapsulation); Symmetric.Hash_h(encapsulation, span.Slice(32, span.Length - 32)); span5 = secretKey.AsSpan(SecretKeyBytes - 32, 32); span5.CopyTo(span4); encapsulation.CopyTo(span4.Slice(32, span4.Length - 32)); Symmetric.Kdf(span4, span4); CMov(span, span4, 32, b); span5 = span.Slice(0, 32); span5.CopyTo(secret); } internal void KemEncrypt(Span<byte> encapsulation, Span<byte> secret, ReadOnlySpan<byte> pk, ReadOnlySpan<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[] array = m_indCpa.UnpackPublicKey(polyVec, pk); byte[] array2 = m_indCpa.PackPublicKey(polyVec, array); if (!pk.SequenceEqual(array2)) throw new ArgumentException("Input validation: Modulus check failed for ml-kem encapsulation"); KemEncryptInternal(encapsulation, secret, pk, randBytes); } internal unsafe void KemEncryptInternal(Span<byte> encapsulation, Span<byte> secret, ReadOnlySpan<byte> pk, ReadOnlySpan<byte> randBytes) { Span<byte> span = new Span<byte>(stackalloc byte[64], 64); Span<byte> output = new Span<byte>(stackalloc byte[64], 64); randBytes.Slice(0, 32).CopyTo(span); Symmetric.Hash_h(pk, span.Slice(32, span.Length - 32)); Symmetric.Hash_g(span, output); m_indCpa.Encrypt(encapsulation, span.Slice(0, 32), pk, output.Slice(32, output.Length - 32)); output.Slice(0, 32).CopyTo(secret); } private static void CMov(Span<byte> r, ReadOnlySpan<byte> x, int len, bool b) { if (b) x.Slice(0, len).CopyTo(r); else r.Slice(0, len).CopyTo(r); } internal void RandomBytes(byte[] buf, int len) { m_random.NextBytes(buf, 0, len); } } }