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 = 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);
}
}
}