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, 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, 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, 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);
byte[] seed = Arrays.CopyOfRange(array, 0, 32);
byte[] array3 = Arrays.CopyOfRange(array, 32, 64);
for (int i = 0; i < k; i++) {
array2[i] = new PolyVec(m_engine);
}
GenerateMatrix(array2, seed, false);
for (int j = 0; j < k; j++) {
Poly obj = polyVec3.m_vec[j];
byte[] seed2 = array3;
byte num = b;
b = (byte)(num + 1);
obj.GetNoiseEta1(seed2, num);
}
for (int l = 0; l < k; l++) {
Poly obj2 = polyVec.m_vec[l];
byte[] seed3 = array3;
byte num2 = b;
b = (byte)(num2 + 1);
obj2.GetNoiseEta1(seed3, 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, seed);
}
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, byte[] seed)
{
pk = new byte[m_engine.IndCpaPublicKeyBytes];
pkpv.ToBytes(pk);
Array.Copy(seed, 0, pk, m_engine.PolyVecBytes, 32);
}
private void UnpackSecretKey(PolyVec skpv, byte[] sk)
{
skpv.FromBytes(sk);
}
private void UnpackPublicKey(PolyVec pkpv, byte[] seed, byte[] pk)
{
pkpv.FromBytes(pk);
Array.Copy(pk, m_engine.PolyVecBytes, seed, 0, 32);
}
public void Encrypt(byte[] cBuf, int cOff, byte[] m, byte[] pk, byte[] coins)
{
int k = m_engine.K;
byte[] seed = 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[] array = new PolyVec[k];
Poly poly = new Poly(m_engine);
Poly poly2 = new Poly(m_engine);
Poly poly3 = new Poly(m_engine);
UnpackPublicKey(polyVec2, seed, pk);
poly2.FromMsg(m);
for (int i = 0; i < k; i++) {
array[i] = new PolyVec(m_engine);
}
GenerateMatrix(array, seed, 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], array[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(cBuf, cOff, polyVec4, poly);
}
private void PackCipherText(byte[] rBuf, int rOff, PolyVec b, Poly v)
{
b.CompressPolyVec(rBuf, rOff);
v.CompressPoly(rBuf, rOff + m_engine.PolyVecCompressedBytes);
}
private void UnpackCipherText(PolyVec b, Poly v, byte[] cBuf, int cOff)
{
b.DecompressPolyVec(cBuf, cOff);
v.DecompressPoly(cBuf, cOff + m_engine.PolyVecCompressedBytes);
}
internal void Decrypt(byte[] m, byte[] cBuf, int cOff, 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, cBuf, cOff);
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, byte[] seed)
{
byte[] array = new byte[m_engine.IndCpaPublicKeyBytes];
polyVec.ToBytes(array);
Array.Copy(seed, 0, array, m_engine.PolyVecBytes, 32);
return array;
}
internal byte[] UnpackPublicKey(PolyVec polyVec, byte[] pk)
{
byte[] array = new byte[32];
polyVec.FromBytes(pk);
Array.Copy(pk, m_engine.PolyVecBytes, array, 0, 32);
return array;
}
}
}