Poly
class Poly
using Org.BouncyCastle.Crypto.Digests;
using System;
namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium
{
internal class Poly
{
private const int N = 256;
private readonly DilithiumEngine Engine;
private readonly int PolyUniformNBlocks;
private readonly Symmetric Symmetric;
internal readonly int[] Coeffs;
public Poly(DilithiumEngine engine)
{
Engine = engine;
Symmetric = engine.Symmetric;
PolyUniformNBlocks = (768 + Symmetric.Stream128BlockBytes - 1) / Symmetric.Stream128BlockBytes;
Coeffs = new int[256];
}
public void UniformBlocks(byte[] seed, ushort nonce)
{
int num = PolyUniformNBlocks * Symmetric.Stream128BlockBytes;
byte[] array = new byte[num + 2];
Symmetric.Stream128Init(seed, nonce);
Symmetric.Stream128SqueezeBlocks(array, 0, num);
for (int i = RejectUniform(Coeffs, 0, 256, array, num); i < 256; i += RejectUniform(Coeffs, i, 256 - i, array, num)) {
int num2 = num % 3;
for (int j = 0; j < num2; j++) {
array[j] = array[num - num2 + j];
}
Symmetric.Stream128SqueezeBlocks(array, num2, Symmetric.Stream128BlockBytes);
num = Symmetric.Stream128BlockBytes + num2;
}
}
private static int RejectUniform(int[] coeffs, int off, int len, byte[] buf, int buflen)
{
int num = 0;
int num2 = 0;
while (num < len && num2 + 3 <= buflen) {
uint num4 = (uint)(buf[num2++] & 255);
num4 = (uint)((int)num4 | ((buf[num2++] & 255) << 8));
num4 = (uint)((int)num4 | ((buf[num2++] & 255) << 16));
num4 &= 8388607;
if (num4 < 8380417)
coeffs[off + num++] = (int)num4;
}
return num;
}
public void UniformEta(byte[] seed, ushort nonce)
{
int eta = Engine.Eta;
int num;
switch (eta) {
case 2:
num = (136 + Symmetric.Stream256BlockBytes - 1) / Symmetric.Stream256BlockBytes;
break;
case 4:
num = (227 + Symmetric.Stream256BlockBytes - 1) / Symmetric.Stream256BlockBytes;
break;
default:
throw new ArgumentException("Wrong Dilithium Eta!");
}
int num2 = num * Symmetric.Stream256BlockBytes;
byte[] array = new byte[num2];
Symmetric.Stream256Init(seed, nonce);
Symmetric.Stream256SqueezeBlocks(array, 0, num2);
for (int i = RejectEta(Coeffs, 0, 256, array, num2, eta); i < 256; i += RejectEta(Coeffs, i, 256 - i, array, Symmetric.Stream256BlockBytes, eta)) {
Symmetric.Stream256SqueezeBlocks(array, 0, Symmetric.Stream256BlockBytes);
}
}
private static int RejectEta(int[] coeffs, int off, int len, byte[] buf, int buflen, int eta)
{
int num = 0;
int num2 = 0;
while (num < len && num2 < buflen) {
byte num4 = buf[num2++];
uint num5 = (uint)(num4 & 15);
uint num6 = (uint)num4 >> 4;
switch (eta) {
case 2:
if (num5 < 15) {
num5 -= (205 * num5 >> 10) * 5;
coeffs[off + num++] = (int)(2 - num5);
}
if (num6 < 15 && num < len) {
num6 -= (205 * num6 >> 10) * 5;
coeffs[off + num++] = (int)(2 - num6);
}
break;
case 4:
if (num5 < 9)
coeffs[off + num++] = (int)(4 - num5);
if (num6 < 9 && num < len)
coeffs[off + num++] = (int)(4 - num6);
break;
}
}
return num;
}
public void PointwiseMontgomery(Poly v, Poly w)
{
for (int i = 0; i < 256; i++) {
Coeffs[i] = Reduce.MontgomeryReduce((long)v.Coeffs[i] * (long)w.Coeffs[i]);
}
}
public void PointwiseAccountMontgomery(PolyVec u, PolyVec v)
{
Poly poly = new Poly(Engine);
PointwiseMontgomery(u.Vec[0], v.Vec[0]);
for (int i = 1; i < Engine.L; i++) {
poly.PointwiseMontgomery(u.Vec[i], v.Vec[i]);
Add(poly);
}
}
public void Add(Poly a)
{
for (int i = 0; i < 256; i++) {
Coeffs[i] += a.Coeffs[i];
}
}
public void Subtract(Poly b)
{
for (int i = 0; i < 256; i++) {
Coeffs[i] -= b.Coeffs[i];
}
}
public void ReducePoly()
{
for (int i = 0; i < 256; i++) {
Coeffs[i] = Reduce.Reduce32(Coeffs[i]);
}
}
public void PolyNtt()
{
Ntt.NTT(Coeffs);
}
public void InverseNttToMont()
{
Ntt.InverseNttToMont(Coeffs);
}
public void ConditionalAddQ()
{
for (int i = 0; i < 256; i++) {
Coeffs[i] = Reduce.ConditionalAddQ(Coeffs[i]);
}
}
public void Power2Round(Poly a)
{
for (int i = 0; i < 256; i++) {
int[] array = Rounding.Power2Round(Coeffs[i]);
Coeffs[i] = array[0];
a.Coeffs[i] = array[1];
}
}
public void PolyT0Pack(byte[] r, int off)
{
int[] array = new int[8];
for (int i = 0; i < 32; i++) {
array[0] = 4096 - Coeffs[8 * i];
array[1] = 4096 - Coeffs[8 * i + 1];
array[2] = 4096 - Coeffs[8 * i + 2];
array[3] = 4096 - Coeffs[8 * i + 3];
array[4] = 4096 - Coeffs[8 * i + 4];
array[5] = 4096 - Coeffs[8 * i + 5];
array[6] = 4096 - Coeffs[8 * i + 6];
array[7] = 4096 - Coeffs[8 * i + 7];
r[off + 13 * i] = (byte)array[0];
r[off + 13 * i + 1] = (byte)(array[0] >> 8);
r[off + 13 * i + 1] = (byte)(r[off + 13 * i + 1] | (byte)(array[1] << 5));
r[off + 13 * i + 2] = (byte)(array[1] >> 3);
r[off + 13 * i + 3] = (byte)(array[1] >> 11);
r[off + 13 * i + 3] = (byte)(r[off + 13 * i + 3] | (byte)(array[2] << 2));
r[off + 13 * i + 4] = (byte)(array[2] >> 6);
r[off + 13 * i + 4] = (byte)(r[off + 13 * i + 4] | (byte)(array[3] << 7));
r[off + 13 * i + 5] = (byte)(array[3] >> 1);
r[off + 13 * i + 6] = (byte)(array[3] >> 9);
r[off + 13 * i + 6] = (byte)(r[off + 13 * i + 6] | (byte)(array[4] << 4));
r[off + 13 * i + 7] = (byte)(array[4] >> 4);
r[off + 13 * i + 8] = (byte)(array[4] >> 12);
r[off + 13 * i + 8] = (byte)(r[off + 13 * i + 8] | (byte)(array[5] << 1));
r[off + 13 * i + 9] = (byte)(array[5] >> 7);
r[off + 13 * i + 9] = (byte)(r[off + 13 * i + 9] | (byte)(array[6] << 6));
r[off + 13 * i + 10] = (byte)(array[6] >> 2);
r[off + 13 * i + 11] = (byte)(array[6] >> 10);
r[off + 13 * i + 11] = (byte)(r[off + 13 * i + 11] | (byte)(array[7] << 3));
r[off + 13 * i + 12] = (byte)(array[7] >> 5);
}
}
public void PolyT0Unpack(byte[] a, int off)
{
for (int i = 0; i < 32; i++) {
Coeffs[8 * i] = (((a[off + 13 * i] & 255) | ((a[off + 13 * i + 1] & 255) << 8)) & 8191);
Coeffs[8 * i + 1] = ((((a[off + 13 * i + 1] & 255) >> 5) | ((a[off + 13 * i + 2] & 255) << 3) | ((a[off + 13 * i + 3] & 255) << 11)) & 8191);
Coeffs[8 * i + 2] = ((((a[off + 13 * i + 3] & 255) >> 2) | ((a[off + 13 * i + 4] & 255) << 6)) & 8191);
Coeffs[8 * i + 3] = ((((a[off + 13 * i + 4] & 255) >> 7) | ((a[off + 13 * i + 5] & 255) << 1) | ((a[off + 13 * i + 6] & 255) << 9)) & 8191);
Coeffs[8 * i + 4] = ((((a[off + 13 * i + 6] & 255) >> 4) | ((a[off + 13 * i + 7] & 255) << 4) | ((a[off + 13 * i + 8] & 255) << 12)) & 8191);
Coeffs[8 * i + 5] = ((((a[off + 13 * i + 8] & 255) >> 1) | ((a[off + 13 * i + 9] & 255) << 7)) & 8191);
Coeffs[8 * i + 6] = ((((a[off + 13 * i + 9] & 255) >> 6) | ((a[off + 13 * i + 10] & 255) << 2) | ((a[off + 13 * i + 11] & 255) << 10)) & 8191);
Coeffs[8 * i + 7] = ((((a[off + 13 * i + 11] & 255) >> 3) | ((a[off + 13 * i + 12] & 255) << 5)) & 8191);
Coeffs[8 * i] = 4096 - Coeffs[8 * i];
Coeffs[8 * i + 1] = 4096 - Coeffs[8 * i + 1];
Coeffs[8 * i + 2] = 4096 - Coeffs[8 * i + 2];
Coeffs[8 * i + 3] = 4096 - Coeffs[8 * i + 3];
Coeffs[8 * i + 4] = 4096 - Coeffs[8 * i + 4];
Coeffs[8 * i + 5] = 4096 - Coeffs[8 * i + 5];
Coeffs[8 * i + 6] = 4096 - Coeffs[8 * i + 6];
Coeffs[8 * i + 7] = 4096 - Coeffs[8 * i + 7];
}
}
public void PolyT1Pack(byte[] buf, int bufOff)
{
for (int i = 0; i < 64; i++) {
buf[bufOff] = (byte)Coeffs[4 * i];
buf[bufOff + 1] = (byte)((Coeffs[4 * i] >> 8) | (Coeffs[4 * i + 1] << 2));
buf[bufOff + 2] = (byte)((Coeffs[4 * i + 1] >> 6) | (Coeffs[4 * i + 2] << 4));
buf[bufOff + 3] = (byte)((Coeffs[4 * i + 2] >> 4) | (Coeffs[4 * i + 3] << 6));
buf[bufOff + 4] = (byte)(Coeffs[4 * i + 3] >> 2);
bufOff += 5;
}
}
public void PolyT1Unpack(byte[] a, int aOff)
{
for (int i = 0; i < 64; i++) {
int num = a[aOff];
int num2 = a[aOff + 1];
int num3 = a[aOff + 2];
int num4 = a[aOff + 3];
int num5 = a[aOff + 4];
aOff += 5;
Coeffs[4 * i] = ((num | (num2 << 8)) & 1023);
Coeffs[4 * i + 1] = (((num2 >> 2) | (num3 << 6)) & 1023);
Coeffs[4 * i + 2] = (((num3 >> 4) | (num4 << 4)) & 1023);
Coeffs[4 * i + 3] = ((num4 >> 6) | (num5 << 2));
}
}
public void PolyEtaPack(byte[] r, int off)
{
int eta = Engine.Eta;
switch (eta) {
case 2:
for (int j = 0; j < 32; j++) {
byte b3 = (byte)(eta - Coeffs[8 * j]);
byte b4 = (byte)(eta - Coeffs[8 * j + 1]);
byte b5 = (byte)(eta - Coeffs[8 * j + 2]);
byte b6 = (byte)(eta - Coeffs[8 * j + 3]);
byte b7 = (byte)(eta - Coeffs[8 * j + 4]);
byte b8 = (byte)(eta - Coeffs[8 * j + 5]);
byte b9 = (byte)(eta - Coeffs[8 * j + 6]);
byte b10 = (byte)(eta - Coeffs[8 * j + 7]);
r[off + 3 * j] = (byte)(b3 | (b4 << 3) | (b5 << 6));
r[off + 3 * j + 1] = (byte)((b5 >> 2) | (b6 << 1) | (b7 << 4) | (b8 << 7));
r[off + 3 * j + 2] = (byte)((b8 >> 1) | (b9 << 2) | (b10 << 5));
}
break;
case 4:
for (int i = 0; i < 128; i++) {
byte b = (byte)(eta - Coeffs[2 * i]);
byte b2 = (byte)(eta - Coeffs[2 * i + 1]);
r[off + i] = (byte)(b | (b2 << 4));
}
break;
default:
throw new ArgumentException("Eta needs to be 2 or 4!");
}
}
public void PolyEtaUnpack(byte[] a, int off)
{
int eta = Engine.Eta;
switch (eta) {
case 2:
for (int j = 0; j < 32; j++) {
Coeffs[8 * j] = (a[off + 3 * j] & 255 & 7);
Coeffs[8 * j + 1] = (((a[off + 3 * j] & 255) >> 3) & 7);
Coeffs[8 * j + 2] = (((a[off + 3 * j] & 255) >> 6) | (((a[off + 3 * j + 1] & 255) << 2) & 7));
Coeffs[8 * j + 3] = (((a[off + 3 * j + 1] & 255) >> 1) & 7);
Coeffs[8 * j + 4] = (((a[off + 3 * j + 1] & 255) >> 4) & 7);
Coeffs[8 * j + 5] = (((a[off + 3 * j + 1] & 255) >> 7) | (((a[off + 3 * j + 2] & 255) << 1) & 7));
Coeffs[8 * j + 6] = (((a[off + 3 * j + 2] & 255) >> 2) & 7);
Coeffs[8 * j + 7] = (((a[off + 3 * j + 2] & 255) >> 5) & 7);
Coeffs[8 * j] = eta - Coeffs[8 * j];
Coeffs[8 * j + 1] = eta - Coeffs[8 * j + 1];
Coeffs[8 * j + 2] = eta - Coeffs[8 * j + 2];
Coeffs[8 * j + 3] = eta - Coeffs[8 * j + 3];
Coeffs[8 * j + 4] = eta - Coeffs[8 * j + 4];
Coeffs[8 * j + 5] = eta - Coeffs[8 * j + 5];
Coeffs[8 * j + 6] = eta - Coeffs[8 * j + 6];
Coeffs[8 * j + 7] = eta - Coeffs[8 * j + 7];
}
break;
case 4:
for (int i = 0; i < 128; i++) {
Coeffs[2 * i] = (a[off + i] & 255 & 15);
Coeffs[2 * i + 1] = (a[off + i] & 255) >> 4;
Coeffs[2 * i] = eta - Coeffs[2 * i];
Coeffs[2 * i + 1] = eta - Coeffs[2 * i + 1];
}
break;
}
}
public void UniformGamma1(byte[] seed, ushort nonce)
{
byte[] array = new byte[Engine.PolyUniformGamma1NBytes * Symmetric.Stream256BlockBytes];
Symmetric.Stream256Init(seed, nonce);
Symmetric.Stream256SqueezeBlocks(array, 0, array.Length);
UnpackZ(array);
}
public void PackZ(byte[] r, int offset)
{
if (Engine.Gamma1 == 131072) {
for (int i = 0; i < 64; i++) {
uint num = (uint)(Engine.Gamma1 - Coeffs[4 * i]);
uint num2 = (uint)(Engine.Gamma1 - Coeffs[4 * i + 1]);
uint num3 = (uint)(Engine.Gamma1 - Coeffs[4 * i + 2]);
uint num4 = (uint)(Engine.Gamma1 - Coeffs[4 * i + 3]);
r[offset + 9 * i] = (byte)num;
r[offset + 9 * i + 1] = (byte)(num >> 8);
r[offset + 9 * i + 2] = (byte)((byte)(num >> 16) | (num2 << 2));
r[offset + 9 * i + 3] = (byte)(num2 >> 6);
r[offset + 9 * i + 4] = (byte)((byte)(num2 >> 14) | (num3 << 4));
r[offset + 9 * i + 5] = (byte)(num3 >> 4);
r[offset + 9 * i + 6] = (byte)((byte)(num3 >> 12) | (num4 << 6));
r[offset + 9 * i + 7] = (byte)(num4 >> 2);
r[offset + 9 * i + 8] = (byte)(num4 >> 10);
}
} else {
if (Engine.Gamma1 != 524288)
throw new ArgumentException("Wrong Dilithium Gamma1!");
for (int j = 0; j < 128; j++) {
uint num5 = (uint)(Engine.Gamma1 - Coeffs[2 * j]);
uint num6 = (uint)(Engine.Gamma1 - Coeffs[2 * j + 1]);
r[offset + 5 * j] = (byte)num5;
r[offset + 5 * j + 1] = (byte)(num5 >> 8);
r[offset + 5 * j + 2] = (byte)((byte)(num5 >> 16) | (num6 << 4));
r[offset + 5 * j + 3] = (byte)(num6 >> 4);
r[offset + 5 * j + 4] = (byte)(num6 >> 12);
}
}
}
public void UnpackZ(byte[] a)
{
if (Engine.Gamma1 == 131072) {
for (int i = 0; i < 64; i++) {
Coeffs[4 * i] = (((a[9 * i] & 255) | ((a[9 * i + 1] & 255) << 8) | ((a[9 * i + 2] & 255) << 16)) & 262143);
Coeffs[4 * i + 1] = ((((a[9 * i + 2] & 255) >> 2) | ((a[9 * i + 3] & 255) << 6) | ((a[9 * i + 4] & 255) << 14)) & 262143);
Coeffs[4 * i + 2] = ((((a[9 * i + 4] & 255) >> 4) | ((a[9 * i + 5] & 255) << 4) | ((a[9 * i + 6] & 255) << 12)) & 262143);
Coeffs[4 * i + 3] = ((((a[9 * i + 6] & 255) >> 6) | ((a[9 * i + 7] & 255) << 2) | ((a[9 * i + 8] & 255) << 10)) & 262143);
Coeffs[4 * i] = Engine.Gamma1 - Coeffs[4 * i];
Coeffs[4 * i + 1] = Engine.Gamma1 - Coeffs[4 * i + 1];
Coeffs[4 * i + 2] = Engine.Gamma1 - Coeffs[4 * i + 2];
Coeffs[4 * i + 3] = Engine.Gamma1 - Coeffs[4 * i + 3];
}
} else {
if (Engine.Gamma1 != 524288)
throw new ArgumentException("Wrong Dilithiumn Gamma1!");
for (int j = 0; j < 128; j++) {
Coeffs[2 * j] = (((a[5 * j] & 255) | ((a[5 * j + 1] & 255) << 8) | ((a[5 * j + 2] & 255) << 16)) & 1048575);
Coeffs[2 * j + 1] = ((((a[5 * j + 2] & 255) >> 4) | ((a[5 * j + 3] & 255) << 4) | ((a[5 * j + 4] & 255) << 12)) & 1048575);
Coeffs[2 * j] = Engine.Gamma1 - Coeffs[2 * j];
Coeffs[2 * j + 1] = Engine.Gamma1 - Coeffs[2 * j + 1];
}
}
}
public void Decompose(Poly a)
{
for (int i = 0; i < 256; i++) {
int[] array = Rounding.Decompose(Coeffs[i], Engine.Gamma2);
a.Coeffs[i] = array[0];
Coeffs[i] = array[1];
}
}
public void PackW1(byte[] r, int off)
{
if (Engine.Gamma2 == 95232) {
for (int i = 0; i < 64; i++) {
r[off + 3 * i] = (byte)((byte)Coeffs[4 * i] | (Coeffs[4 * i + 1] << 6));
r[off + 3 * i + 1] = (byte)((byte)(Coeffs[4 * i + 1] >> 2) | (Coeffs[4 * i + 2] << 4));
r[off + 3 * i + 2] = (byte)((byte)(Coeffs[4 * i + 2] >> 4) | (Coeffs[4 * i + 3] << 2));
}
} else if (Engine.Gamma2 == 261888) {
for (int j = 0; j < 128; j++) {
r[off + j] = (byte)(Coeffs[2 * j] | (Coeffs[2 * j + 1] << 4));
}
}
}
public void Challenge(byte[] seed)
{
byte[] array = new byte[Symmetric.Stream256BlockBytes];
ShakeDigest shakeDigest = new ShakeDigest(256);
shakeDigest.BlockUpdate(seed, 0, Engine.CTilde);
shakeDigest.Output(array, 0, Symmetric.Stream256BlockBytes);
ulong num = 0;
for (int i = 0; i < 8; i++) {
num = (ulong)((long)num | ((long)(array[i] & 255) << 8 * i));
}
int num2 = 8;
for (int i = 0; i < 256; i++) {
Coeffs[i] = 0;
}
for (int i = 256 - Engine.Tau; i < 256; i++) {
int num4;
do {
if (num2 >= Symmetric.Stream256BlockBytes) {
shakeDigest.Output(array, 0, Symmetric.Stream256BlockBytes);
num2 = 0;
}
num4 = (array[num2++] & 255);
} while (num4 > i);
Coeffs[i] = Coeffs[num4];
Coeffs[num4] = (int)(1 - 2 * (num & 1));
num >>= 1;
}
}
public bool CheckNorm(int B)
{
if (B > 1047552)
return true;
for (int i = 0; i < 256; i++) {
int num = Coeffs[i] >> 31;
num = Coeffs[i] - (num & (2 * Coeffs[i]));
if (num >= B)
return true;
}
return false;
}
public int PolyMakeHint(Poly a0, Poly a1)
{
int num = 0;
for (int i = 0; i < 256; i++) {
Coeffs[i] = Rounding.MakeHint(a0.Coeffs[i], a1.Coeffs[i], Engine);
num += Coeffs[i];
}
return num;
}
public void PolyUseHint(Poly a, Poly h)
{
for (int i = 0; i < 256; i++) {
Coeffs[i] = Rounding.UseHint(a.Coeffs[i], h.Coeffs[i], Engine.Gamma2);
}
}
public void ShiftLeft()
{
for (int i = 0; i < 256; i++) {
Coeffs[i] <<= 13;
}
}
}
}