AsconAead128
Ascon-AEAD128 was introduced as part of the NIST Lightweight Cryptography
competition and descriLEd in the NIST Special Publication SP 800-232 (Initial
Public Draft).
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
using System.Runtime.CompilerServices;
namespace Org.BouncyCastle.Crypto.Modes
{
public sealed class AsconAead128 : IAeadCipher
{
private enum State
{
Uninitialized,
EncInit,
EncAad,
EncData,
EncFinal,
DecInit,
DecAad,
DecData,
DecFinal
}
private const ulong AsconIV = 17594342703105;
private const int BufSizeDecrypt = 32;
private const int CryptoABytes = 16;
private const int CryptoKeyBytes = 16;
private const int Rate = 16;
private readonly byte[] m_buf;
private byte[] m_initialAssociatedText;
private byte[] m_mac;
private ulong K0;
private ulong K1;
private ulong N0;
private ulong N1;
private ulong S0;
private ulong S1;
private ulong S2;
private ulong S3;
private ulong S4;
private State m_state;
private int m_bufPos;
public string AlgorithmName => "Ascon-AEAD128";
public AsconAead128()
{
m_buf = new byte[32];
}
public int GetKeyBytesSize()
{
return 16;
}
public int GetIVBytesSize()
{
return 16;
}
public void Init(bool forEncryption, ICipherParameters parameters)
{
AeadParameters aeadParameters = parameters as AeadParameters;
KeyParameter keyParameter;
byte[] array;
if (aeadParameters != null) {
keyParameter = aeadParameters.Key;
array = aeadParameters.GetNonce();
m_initialAssociatedText = aeadParameters.GetAssociatedText();
int macSize = aeadParameters.MacSize;
if (macSize != 128)
throw new ArgumentException($"""{macSize}");
} else {
ParametersWithIV parametersWithIV = parameters as ParametersWithIV;
if (parametersWithIV == null)
throw new ArgumentException("invalid parameters passed to " + AlgorithmName);
keyParameter = (parametersWithIV.Parameters as KeyParameter);
array = parametersWithIV.GetIV();
m_initialAssociatedText = null;
}
if (keyParameter == null)
throw new ArgumentException(AlgorithmName + " Init parameters must include a key");
if (array.Length != 16)
throw new ArgumentException($"{AlgorithmName}""{16}""");
byte[] key = keyParameter.GetKey();
if (key.Length != 16)
throw new ArgumentException($"{AlgorithmName}""{16}""");
K0 = Pack.LE_To_UInt64(key, 0);
K1 = Pack.LE_To_UInt64(key, 8);
N0 = Pack.LE_To_UInt64(array, 0);
N1 = Pack.LE_To_UInt64(array, 8);
m_state = (forEncryption ? State.EncInit : State.DecInit);
Reset(true);
}
public void ProcessAadByte(byte input)
{
CheckAad();
m_buf[m_bufPos] = input;
if (++m_bufPos == 16) {
ProcessBufferAad(m_buf, 0);
m_bufPos = 0;
}
}
public void ProcessAadBytes(byte[] inBytes, int inOff, int len)
{
Check.DataLength(inBytes, inOff, len, "input buffer too short");
if (len > 0) {
CheckAad();
if (m_bufPos > 0) {
int num = 16 - m_bufPos;
if (len < num) {
Array.Copy(inBytes, inOff, m_buf, m_bufPos, len);
m_bufPos += len;
return;
}
Array.Copy(inBytes, inOff, m_buf, m_bufPos, num);
inOff += num;
len -= num;
ProcessBufferAad(m_buf, 0);
}
while (len >= 16) {
ProcessBufferAad(inBytes, inOff);
inOff += 16;
len -= 16;
}
Array.Copy(inBytes, inOff, m_buf, 0, len);
m_bufPos = len;
}
}
public int ProcessByte(byte input, byte[] outBytes, int outOff)
{
return ProcessBytes(new byte[1] {
input
}, 0, 1, outBytes, outOff);
}
public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
{
Check.DataLength(inBytes, inOff, len, "input buffer too short");
bool num = CheckData();
int num2 = 0;
if (num) {
if (m_bufPos > 0) {
int num3 = 16 - m_bufPos;
if (len < num3) {
Array.Copy(inBytes, inOff, m_buf, m_bufPos, len);
m_bufPos += len;
return 0;
}
Array.Copy(inBytes, inOff, m_buf, m_bufPos, num3);
inOff += num3;
len -= num3;
ProcessBufferEncrypt(m_buf, 0, outBytes, outOff);
num2 = 16;
}
while (len >= 16) {
ProcessBufferEncrypt(inBytes, inOff, outBytes, outOff + num2);
inOff += 16;
len -= 16;
num2 += 16;
}
} else {
int num4 = 32 - m_bufPos;
if (len < num4) {
Array.Copy(inBytes, inOff, m_buf, m_bufPos, len);
m_bufPos += len;
return 0;
}
if (m_bufPos >= 16) {
ProcessBufferDecrypt(m_buf, 0, outBytes, outOff + num2);
m_bufPos -= 16;
Array.Copy(m_buf, 16, m_buf, 0, m_bufPos);
num2 += 16;
num4 += 16;
if (len < num4) {
Array.Copy(inBytes, inOff, m_buf, m_bufPos, len);
m_bufPos += len;
return num2;
}
}
num4 = 16 - m_bufPos;
Array.Copy(inBytes, inOff, m_buf, m_bufPos, num4);
inOff += num4;
len -= num4;
ProcessBufferDecrypt(m_buf, 0, outBytes, outOff + num2);
num2 += 16;
while (len >= 32) {
ProcessBufferDecrypt(inBytes, inOff, outBytes, outOff + num2);
inOff += 16;
len -= 16;
num2 += 16;
}
}
Array.Copy(inBytes, inOff, m_buf, 0, len);
m_bufPos = len;
return num2;
}
public int DoFinal(byte[] outBytes, int outOff)
{
int num;
if (CheckData()) {
num = m_bufPos + 16;
Check.OutputLength(outBytes, outOff, num, "output buffer too short");
ProcessFinalEncrypt(m_buf, 0, m_bufPos, outBytes, outOff);
m_mac = new byte[16];
Pack.UInt64_To_LE(S3, m_mac, 0);
Pack.UInt64_To_LE(S4, m_mac, 8);
Array.Copy(m_mac, 0, outBytes, outOff + m_bufPos, 16);
Reset(false);
} else {
if (m_bufPos < 16)
throw new InvalidCipherTextException("data too short");
m_bufPos -= 16;
num = m_bufPos;
Check.OutputLength(outBytes, outOff, num, "output buffer too short");
ProcessFinalDecrypt(m_buf, 0, m_bufPos, outBytes, outOff);
S3 ^= Pack.LE_To_UInt64(m_buf, m_bufPos);
S4 ^= Pack.LE_To_UInt64(m_buf, m_bufPos + 8);
if ((S3 | S4) != 0)
throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
Reset(true);
}
return num;
}
public byte[] GetMac()
{
return m_mac;
}
public int GetUpdateOutputSize(int len)
{
int num = System.Math.Max(0, len);
switch (m_state) {
case State.DecInit:
case State.DecAad:
num = System.Math.Max(0, num - 16);
break;
case State.DecData:
case State.DecFinal:
num = System.Math.Max(0, num + m_bufPos - 16);
break;
case State.EncData:
case State.EncFinal:
num += m_bufPos;
break;
}
return num - num % 16;
}
public int GetOutputSize(int len)
{
int num = System.Math.Max(0, len);
switch (m_state) {
case State.DecInit:
case State.DecAad:
return System.Math.Max(0, num - 16);
case State.DecData:
case State.DecFinal:
return System.Math.Max(0, num + m_bufPos - 16);
case State.EncData:
case State.EncFinal:
return num + m_bufPos + 16;
default:
return num + 16;
}
}
public void Reset()
{
Reset(true);
}
private void CheckAad()
{
switch (m_state) {
case State.EncAad:
case State.DecAad:
break;
case State.DecInit:
m_state = State.DecAad;
break;
case State.EncInit:
m_state = State.EncAad;
break;
case State.EncFinal:
throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption");
default:
throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
}
}
private bool CheckData()
{
switch (m_state) {
case State.DecInit:
case State.DecAad:
FinishAad(State.DecData);
return false;
case State.EncInit:
case State.EncAad:
FinishAad(State.EncData);
return true;
case State.DecData:
return false;
case State.EncData:
return true;
case State.EncFinal:
throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption");
default:
throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
}
}
private void FinishAad(State nextState)
{
State state = m_state;
if (state == State.EncAad || state == State.DecAad) {
m_buf[m_bufPos] = 1;
if (m_bufPos >= 8) {
S0 ^= Pack.LE_To_UInt64(m_buf, 0);
S1 ^= (Pack.LE_To_UInt64(m_buf, 8) & (ulong.MaxValue >> 56 - (m_bufPos - 8 << 3)));
} else
S0 ^= (Pack.LE_To_UInt64(m_buf, 0) & (ulong.MaxValue >> 56 - (m_bufPos << 3)));
P8();
}
S4 ^= 9223372036854775808;
m_bufPos = 0;
m_state = nextState;
}
private void FinishData(State nextState)
{
S2 ^= K0;
S3 ^= K1;
P12();
S3 ^= K0;
S4 ^= K1;
m_state = nextState;
}
private void InitState()
{
S0 = 17594342703105;
S1 = K0;
S2 = K1;
S3 = N0;
S4 = N1;
P12();
S3 ^= K0;
S4 ^= K1;
}
private void P8()
{
Round(180);
Round(165);
Round(150);
Round(135);
Round(120);
Round(105);
Round(90);
Round(75);
}
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 ProcessBufferAad(byte[] buffer, int bufOff)
{
S0 ^= Pack.LE_To_UInt64(buffer, bufOff);
S1 ^= Pack.LE_To_UInt64(buffer, bufOff + 8);
P8();
}
private void ProcessBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
{
Check.OutputLength(output, outOff, 16, "output buffer too short");
ulong num = Pack.LE_To_UInt64(buffer, bufOff);
Pack.UInt64_To_LE(S0 ^ num, output, outOff);
S0 = num;
ulong num2 = Pack.LE_To_UInt64(buffer, bufOff + 8);
Pack.UInt64_To_LE(S1 ^ num2, output, outOff + 8);
S1 = num2;
P8();
}
private void ProcessBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
{
Check.OutputLength(output, outOff, 16, "output buffer too short");
S0 ^= Pack.LE_To_UInt64(buffer, bufOff);
Pack.UInt64_To_LE(S0, output, outOff);
S1 ^= Pack.LE_To_UInt64(buffer, bufOff + 8);
Pack.UInt64_To_LE(S1, output, outOff + 8);
P8();
}
private void ProcessFinalDecrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff)
{
if (inLen >= 8) {
ulong num = Pack.LE_To_UInt64(input, inOff);
Pack.UInt64_To_LE(S0 ^ num, output, outOff);
S0 = num;
inLen -= 8;
if (inLen > 0)
ProcessFinalDecrypt64(input, inOff + 8, inLen, output, outOff + 8, ref S1);
S1 ^= Pad(inLen);
} else {
if (inLen > 0)
ProcessFinalDecrypt64(input, inOff, inLen, output, outOff, ref S0);
S0 ^= Pad(inLen);
}
FinishData(State.DecFinal);
}
private static void ProcessFinalDecrypt64(byte[] input, int inOff, int inLen, byte[] output, int outOff, ref ulong s)
{
ulong num = Pack.LE_To_UInt64_Low(input, inOff, inLen);
Pack.UInt64_To_LE_Low(s ^ num, output, outOff, inLen);
s &= (ulong)(-1 << (inLen << 3));
s ^= num;
}
private void ProcessFinalEncrypt(byte[] input, int inOff, int inLen, byte[] output, int outOff)
{
if (inLen >= 8) {
S0 ^= Pack.LE_To_UInt64(input, inOff);
Pack.UInt64_To_LE(S0, output, outOff);
inLen -= 8;
if (inLen > 0)
ProcessFinalEncrypt64(input, inOff + 8, inLen, output, outOff + 8, ref S1);
S1 ^= Pad(inLen);
} else {
if (inLen > 0)
ProcessFinalEncrypt64(input, inOff, inLen, output, outOff, ref S0);
S0 ^= Pad(inLen);
}
FinishData(State.EncFinal);
}
private static void ProcessFinalEncrypt64(byte[] input, int inOff, int inLen, byte[] output, int outOff, ref ulong s)
{
s ^= Pack.LE_To_UInt64_Low(input, inOff, inLen);
Pack.UInt64_To_LE_Low(s, output, outOff, inLen);
}
private void Reset(bool clearMac)
{
if (clearMac)
m_mac = null;
Array.Clear(m_buf, 0, m_buf.Length);
m_bufPos = 0;
switch (m_state) {
case State.DecAad:
case State.DecData:
case State.DecFinal:
m_state = State.DecInit;
break;
case State.EncAad:
case State.EncData:
case State.EncFinal:
m_state = State.EncFinal;
return;
default:
throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
case State.EncInit:
case State.DecInit:
break;
}
InitState();
if (m_initialAssociatedText != null)
ProcessAadBytes(m_initialAssociatedText, 0, m_initialAssociatedText.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong Pad(int i)
{
return (ulong)(1 << (i << 3));
}
}
}