PolyVec
class PolyVec
using System;
namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber
{
internal class PolyVec
{
private KyberEngine m_engine;
internal Poly[] m_vec;
internal PolyVec(KyberEngine engine)
{
m_engine = engine;
m_vec = new Poly[engine.K];
for (int i = 0; i < engine.K; i++) {
m_vec[i] = new Poly(engine);
}
}
internal void Ntt()
{
for (int i = 0; i < m_engine.K; i++) {
m_vec[i].PolyNtt();
}
}
internal void InverseNttToMont()
{
for (int i = 0; i < m_engine.K; i++) {
m_vec[i].PolyInverseNttToMont();
}
}
internal static void PointwiseAccountMontgomery(Poly r, PolyVec a, PolyVec b, KyberEngine engine)
{
Poly poly = new Poly(engine);
Poly.BaseMultMontgomery(r, a.m_vec[0], b.m_vec[0]);
for (int i = 1; i < engine.K; i++) {
Poly.BaseMultMontgomery(poly, a.m_vec[i], b.m_vec[i]);
r.Add(poly);
}
r.PolyReduce();
}
internal void Add(PolyVec a)
{
for (int i = 0; i < m_engine.K; i++) {
m_vec[i].Add(a.m_vec[i]);
}
}
internal void Reduce()
{
for (int i = 0; i < m_engine.K; i++) {
m_vec[i].PolyReduce();
}
}
internal unsafe void CompressPolyVec(byte[] r)
{
ConditionalSubQ();
int num = 0;
if (m_engine.PolyVecCompressedBytes == m_engine.K * 320) {
Span<short> span = new Span<short>(stackalloc byte[8], 4);
for (int i = 0; i < m_engine.K; i++) {
for (int j = 0; j < 64; j++) {
for (int k = 0; k < 4; k++) {
span[k] = (short)(((uint)((m_vec[i].m_coeffs[4 * j + k] << 10) + 1664) / 3329) & 1023);
}
r[num] = (byte)span[0];
r[num + 1] = (byte)((span[0] >> 8) | (span[1] << 2));
r[num + 2] = (byte)((span[1] >> 6) | (span[2] << 4));
r[num + 3] = (byte)((span[2] >> 4) | (span[3] << 6));
r[num + 4] = (byte)(span[3] >> 2);
num += 5;
}
}
} else {
if (m_engine.PolyVecCompressedBytes != m_engine.K * 352)
throw new ArgumentException("Kyber PolyVecCompressedBytes neither 320 * KyberK or 352 * KyberK!");
Span<short> span2 = new Span<short>(stackalloc byte[16], 8);
for (int l = 0; l < m_engine.K; l++) {
for (int m = 0; m < 32; m++) {
for (int n = 0; n < 8; n++) {
span2[n] = (short)(((uint)((m_vec[l].m_coeffs[8 * m + n] << 11) + 1664) / 3329) & 2047);
}
r[num] = (byte)span2[0];
r[num + 1] = (byte)((span2[0] >> 8) | (span2[1] << 3));
r[num + 2] = (byte)((span2[1] >> 5) | (span2[2] << 6));
r[num + 3] = (byte)(span2[2] >> 2);
r[num + 4] = (byte)((span2[2] >> 10) | (span2[3] << 1));
r[num + 5] = (byte)((span2[3] >> 7) | (span2[4] << 4));
r[num + 6] = (byte)((span2[4] >> 4) | (span2[5] << 7));
r[num + 7] = (byte)(span2[5] >> 1);
r[num + 8] = (byte)((span2[5] >> 9) | (span2[6] << 2));
r[num + 9] = (byte)((span2[6] >> 6) | (span2[7] << 5));
r[num + 10] = (byte)(span2[7] >> 3);
num += 11;
}
}
}
}
internal unsafe void DecompressPolyVec(byte[] compressedCipherText)
{
int num = 0;
if (m_engine.PolyVecCompressedBytes == m_engine.K * 320) {
Span<short> span = new Span<short>(stackalloc byte[8], 4);
for (int i = 0; i < m_engine.K; i++) {
for (int j = 0; j < 64; j++) {
span[0] = (short)((compressedCipherText[num] & 255) | ((ushort)(compressedCipherText[num + 1] & 255) << 8));
span[1] = (short)(((compressedCipherText[num + 1] & 255) >> 2) | ((ushort)(compressedCipherText[num + 2] & 255) << 6));
span[2] = (short)(((compressedCipherText[num + 2] & 255) >> 4) | ((ushort)(compressedCipherText[num + 3] & 255) << 4));
span[3] = (short)(((compressedCipherText[num + 3] & 255) >> 6) | ((ushort)(compressedCipherText[num + 4] & 255) << 2));
num += 5;
for (int k = 0; k < 4; k++) {
m_vec[i].m_coeffs[4 * j + k] = (short)((span[k] & 1023) * 3329 + 512 >> 10);
}
}
}
} else {
if (m_engine.PolyVecCompressedBytes != m_engine.K * 352)
throw new ArgumentException("Kyber PolyVecCompressedBytes neither 320 * KyberK or 352 * KyberK!");
Span<short> span2 = new Span<short>(stackalloc byte[16], 8);
for (int l = 0; l < m_engine.K; l++) {
for (int m = 0; m < 32; m++) {
span2[0] = (short)((compressedCipherText[num] & 255) | ((ushort)(compressedCipherText[num + 1] & 255) << 8));
span2[1] = (short)(((compressedCipherText[num + 1] & 255) >> 3) | ((ushort)(compressedCipherText[num + 2] & 255) << 5));
span2[2] = (short)(((compressedCipherText[num + 2] & 255) >> 6) | ((ushort)(compressedCipherText[num + 3] & 255) << 2) | (ushort)((compressedCipherText[num + 4] & 255) << 10));
span2[3] = (short)(((compressedCipherText[num + 4] & 255) >> 1) | ((ushort)(compressedCipherText[num + 5] & 255) << 7));
span2[4] = (short)(((compressedCipherText[num + 5] & 255) >> 4) | ((ushort)(compressedCipherText[num + 6] & 255) << 4));
span2[5] = (short)(((compressedCipherText[num + 6] & 255) >> 7) | ((ushort)(compressedCipherText[num + 7] & 255) << 1) | (ushort)((compressedCipherText[num + 8] & 255) << 9));
span2[6] = (short)(((compressedCipherText[num + 8] & 255) >> 2) | ((ushort)(compressedCipherText[num + 9] & 255) << 6));
span2[7] = (short)(((compressedCipherText[num + 9] & 255) >> 5) | ((ushort)(compressedCipherText[num + 10] & 255) << 3));
num += 11;
for (int n = 0; n < 8; n++) {
m_vec[l].m_coeffs[8 * m + n] = (short)((span2[n] & 2047) * 3329 + 1024 >> 11);
}
}
}
}
}
internal void ToBytes(byte[] r)
{
for (int i = 0; i < m_engine.K; i++) {
m_vec[i].ToBytes(r, i * KyberEngine.PolyBytes);
}
}
internal void FromBytes(byte[] pk)
{
for (int i = 0; i < m_engine.K; i++) {
m_vec[i].FromBytes(pk, i * KyberEngine.PolyBytes);
}
}
private void ConditionalSubQ()
{
for (int i = 0; i < m_engine.K; i++) {
m_vec[i].CondSubQ();
}
}
}
}