AsconCXof128
Ascon-CXOF128 was introduced in NIST Special Publication (SP) 800-232 (Initial Public Draft).
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Digests
{
public sealed class AsconCXof128 : IXof, IDigest
{
private const int Rate = 8;
private readonly byte[] m_buf = new byte[8];
private readonly ulong Z0;
private readonly ulong Z1;
private readonly ulong Z2;
private readonly ulong Z3;
private readonly ulong Z4;
private ulong S0;
private ulong S1;
private ulong S2;
private ulong S3;
private ulong S4;
private int m_bufPos;
private bool m_squeezing;
public string AlgorithmName => "Ascon-CXOF128";
public AsconCXof128()
: this(Array.Empty<byte>())
{
}
public AsconCXof128(byte[] z)
: this(z, 0, z.Length)
{
}
public AsconCXof128(byte[] z, int zOff, int zLen)
{
Arrays.ValidateSegment(z, zOff, zLen);
if (zLen > 256)
throw new ArgumentOutOfRangeException("customization string too long");
InitState(z, zOff, zLen);
Z0 = S0;
Z1 = S1;
Z2 = S2;
Z3 = S3;
Z4 = S4;
}
public int GetDigestSize()
{
return 32;
}
public int GetByteLength()
{
return 8;
}
public void Update(byte input)
{
if (m_squeezing)
throw new InvalidOperationException("attempt to absorb while squeezing");
m_buf[m_bufPos] = input;
if (++m_bufPos == 8) {
S0 ^= Pack.LE_To_UInt64(m_buf, 0);
m_bufPos = 0;
P12();
}
}
public void BlockUpdate(byte[] input, int inOff, int inLen)
{
Check.DataLength(input, inOff, inLen, "input buffer too short");
if (m_squeezing)
throw new InvalidOperationException("attempt to absorb while squeezing");
if (inLen >= 1) {
int num = 8 - m_bufPos;
if (inLen < num) {
Array.Copy(input, inOff, m_buf, m_bufPos, inLen);
m_bufPos += inLen;
} else {
int num2 = 0;
if (m_bufPos > 0) {
Array.Copy(input, inOff, m_buf, m_bufPos, num);
S0 ^= Pack.LE_To_UInt64(m_buf, 0);
num2 = num;
P12();
}
int num3;
while ((num3 = inLen - num2) >= 8) {
S0 ^= Pack.LE_To_UInt64(input, inOff + num2);
num2 += 8;
P12();
}
Array.Copy(input, inOff + num2, m_buf, 0, num3);
m_bufPos = num3;
}
}
}
public int DoFinal(byte[] output, int outOff)
{
return OutputFinal(output, outOff, GetDigestSize());
}
public void Reset()
{
S0 = Z0;
S1 = Z1;
S2 = Z2;
S3 = Z3;
S4 = Z4;
Array.Clear(m_buf, 0, m_buf.Length);
m_bufPos = 0;
m_squeezing = false;
}
public int OutputFinal(byte[] output, int outOff, int outLen)
{
Check.OutputLength(output, outOff, outLen, "output buffer is too short");
int result = Output(output, outOff, outLen);
Reset();
return result;
}
public int Output(byte[] output, int outOff, int outLen)
{
Check.OutputLength(output, outOff, outLen, "output buffer is too short");
int result = outLen;
if (!m_squeezing) {
PadAndAbsorb();
m_squeezing = true;
m_bufPos = 8;
} else if (m_bufPos < 8) {
int num = 8 - m_bufPos;
if (outLen <= num) {
Array.Copy(m_buf, m_bufPos, output, outOff, outLen);
m_bufPos += outLen;
return result;
}
Array.Copy(m_buf, m_bufPos, output, outOff, num);
outOff += num;
outLen -= num;
m_bufPos = 8;
}
while (outLen >= 8) {
P12();
Pack.UInt64_To_LE(S0, output, outOff);
outOff += 8;
outLen -= 8;
}
if (outLen > 0) {
P12();
Pack.UInt64_To_LE(S0, m_buf);
Array.Copy(m_buf, 0, output, outOff, outLen);
m_bufPos = outLen;
}
return result;
}
private void InitState(byte[] z, int zOff, int zLen)
{
if (zLen == 0) {
S0 = 5768210384618244584;
S1 = 6623958265790276749;
S2 = 4252419465292010770;
S3 = 1238191464582506891;
S4 = 56353695744608240;
} else {
S0 = 7445901275803737603;
S1 = 4886737088792722364;
S2 = 16829984708047569333;
S3 = 3076320316797452470;
S4 = 10322000768943701062;
ulong num = Convert.ToUInt64(zLen) << 3;
S0 ^= num;
P12();
BlockUpdate(z, zOff, zLen);
PadAndAbsorb();
P12();
}
m_bufPos = 0;
}
private void PadAndAbsorb()
{
int num = m_bufPos << 3;
S0 ^= (Pack.LE_To_UInt64(m_buf, 0) & (72057594037927935 >> 56 - num));
S0 ^= (ulong)(1 << num);
}
private void P12()
{
Round(240);
Round(225);
Round(210);
Round(195);
Round(180);
Round(165);
Round(150);
Round(135);
Round(120);
Round(105);
Round(90);
Round(75);
}
private void Round(ulong c)
{
ulong num = S2 ^ c;
ulong num2 = S0 ^ S1 ^ num ^ S3 ^ (S1 & (S0 ^ num ^ S4));
ulong num3 = S0 ^ num ^ S3 ^ S4 ^ ((S1 ^ num) & (S1 ^ S3));
ulong num4 = S1 ^ num ^ S4 ^ (S3 & S4);
ulong num5 = S0 ^ S1 ^ num ^ (~S0 & (S3 ^ S4));
ulong num6 = S1 ^ S3 ^ S4 ^ ((S0 ^ S4) & S1);
S0 = (num2 ^ Longs.RotateRight(num2, 19) ^ Longs.RotateRight(num2, 28));
S1 = (num3 ^ Longs.RotateRight(num3, 39) ^ Longs.RotateRight(num3, 61));
S2 = ~(num4 ^ Longs.RotateRight(num4, 1) ^ Longs.RotateRight(num4, 6));
S3 = (num5 ^ Longs.RotateRight(num5, 10) ^ Longs.RotateRight(num5, 17));
S4 = (num6 ^ Longs.RotateRight(num6, 7) ^ Longs.RotateRight(num6, 41));
}
}
}