AsconEngine
ASCON v1.2 AEAD, https://ascon.iaik.tugraz.at/ .
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
using System.Runtime.CompilerServices;
namespace Org.BouncyCastle.Crypto.Engines
{
[Obsolete("This class is deprecated. For the latest Ascon version, use AsconAead128 instead.")]
public sealed class AsconEngine : IAeadCipher
{
public enum AsconParameters
{
ascon80pq,
ascon128a,
ascon128
}
private enum State
{
Uninitialized,
EncInit,
EncAad,
EncData,
EncFinal,
DecInit,
DecAad,
DecData,
DecFinal
}
private readonly AsconParameters asconParameters;
private readonly int CRYPTO_KEYBYTES;
private readonly int CRYPTO_ABYTES;
private readonly int ASCON_AEAD_RATE;
private readonly int nr;
private byte[] mac;
private ulong K0;
private ulong K1;
private ulong K2;
private ulong N0;
private ulong N1;
private readonly ulong ASCON_IV;
private ulong x0;
private ulong x1;
private ulong x2;
private ulong x3;
private ulong x4;
private string algorithmName;
private State m_state;
private byte[] initialAssociatedText;
private readonly int m_bufferSizeDecrypt;
private readonly byte[] m_buf;
private int m_bufPos;
public string AlgorithmName => algorithmName;
public AsconEngine(AsconParameters asconParameters)
{
this.asconParameters = asconParameters;
switch (asconParameters) {
case AsconParameters.ascon80pq:
CRYPTO_KEYBYTES = 20;
CRYPTO_ABYTES = 16;
ASCON_AEAD_RATE = 8;
ASCON_IV = 11547242664487288832;
algorithmName = "Ascon-80pq AEAD";
break;
case AsconParameters.ascon128a:
CRYPTO_KEYBYTES = 16;
CRYPTO_ABYTES = 16;
ASCON_AEAD_RATE = 16;
ASCON_IV = 9259414062373011456;
algorithmName = "Ascon-128a AEAD";
break;
case AsconParameters.ascon128:
CRYPTO_KEYBYTES = 16;
CRYPTO_ABYTES = 16;
ASCON_AEAD_RATE = 8;
ASCON_IV = 9241399655273594880;
algorithmName = "Ascon-128 AEAD";
break;
default:
throw new ArgumentException("invalid parameter setting for ASCON AEAD");
}
nr = ((ASCON_AEAD_RATE == 8) ? 6 : 8);
m_bufferSizeDecrypt = ASCON_AEAD_RATE + CRYPTO_ABYTES;
m_buf = new byte[m_bufferSizeDecrypt];
}
public int GetKeyBytesSize()
{
return CRYPTO_KEYBYTES;
}
public int GetIVBytesSize()
{
return CRYPTO_ABYTES;
}
public void Init(bool forEncryption, ICipherParameters parameters)
{
AeadParameters aeadParameters = parameters as AeadParameters;
KeyParameter keyParameter;
ReadOnlySpan<byte> bs;
if (aeadParameters != null) {
keyParameter = aeadParameters.Key;
bs = aeadParameters.Nonce;
initialAssociatedText = aeadParameters.GetAssociatedText();
int macSize = aeadParameters.MacSize;
if (macSize != CRYPTO_ABYTES * 8)
throw new ArgumentException("Invalid value for MAC size: " + macSize.ToString());
} else {
ParametersWithIV parametersWithIV = parameters as ParametersWithIV;
if (parametersWithIV == null)
throw new ArgumentException("invalid parameters passed to Ascon");
keyParameter = (parametersWithIV.Parameters as KeyParameter);
bs = parametersWithIV.IV;
initialAssociatedText = null;
}
if (keyParameter == null)
throw new ArgumentException("Ascon Init parameters must include a key");
AsconParameters asconParameters;
if (bs.Length != CRYPTO_ABYTES) {
asconParameters = this.asconParameters;
throw new ArgumentException(asconParameters.ToString() + " requires exactly " + CRYPTO_ABYTES.ToString() + " bytes of IV");
}
ReadOnlySpan<byte> key = keyParameter.Key;
if (key.Length != CRYPTO_KEYBYTES) {
asconParameters = this.asconParameters;
throw new ArgumentException(asconParameters.ToString() + " key must be " + CRYPTO_KEYBYTES.ToString() + " bytes long");
}
N0 = Pack.BE_To_UInt64(bs, 0);
N1 = Pack.BE_To_UInt64(bs, 8);
if (CRYPTO_KEYBYTES == 16) {
K1 = Pack.BE_To_UInt64(key, 0);
K2 = Pack.BE_To_UInt64(key, 8);
} else {
if (CRYPTO_KEYBYTES != 20)
throw new InvalidOperationException();
K0 = Pack.BE_To_UInt32(key, 0);
K1 = Pack.BE_To_UInt64(key, 4);
K2 = Pack.BE_To_UInt64(key, 12);
}
m_state = (forEncryption ? State.EncInit : State.DecInit);
Reset(true);
}
public void ProcessAadByte(byte input)
{
CheckAad();
m_buf[m_bufPos] = input;
if (++m_bufPos == ASCON_AEAD_RATE) {
ProcessBufferAad(m_buf);
m_bufPos = 0;
}
}
public void ProcessAadBytes(byte[] inBytes, int inOff, int len)
{
Check.DataLength(inBytes, inOff, len, "input buffer too short");
ProcessAadBytes(inBytes.AsSpan(inOff, len));
}
public void ProcessAadBytes(ReadOnlySpan<byte> input)
{
if (!input.IsEmpty) {
CheckAad();
if (m_bufPos > 0) {
int num = ASCON_AEAD_RATE - m_bufPos;
if (input.Length < num) {
input.CopyTo(m_buf.AsSpan(m_bufPos));
m_bufPos += input.Length;
return;
}
input.Slice(0, num).CopyTo(m_buf.AsSpan(m_bufPos));
int num2 = num;
input = input.Slice(num2, input.Length - num2);
ProcessBufferAad(m_buf);
}
while (input.Length >= ASCON_AEAD_RATE) {
ProcessBufferAad(input);
int num2 = ASCON_AEAD_RATE;
input = input.Slice(num2, input.Length - num2);
}
input.CopyTo(m_buf);
m_bufPos = input.Length;
}
}
public int ProcessByte(byte input, byte[] outBytes, int outOff)
{
return ProcessByte(input, Spans.FromNullable(outBytes, outOff));
}
public unsafe int ProcessByte(byte input, Span<byte> output)
{
byte* intPtr = stackalloc byte[1];
*intPtr = input;
Span<byte> span = new Span<byte>(intPtr, 1);
return ProcessBytes(span, output);
}
public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
{
Check.DataLength(inBytes, inOff, len, "input buffer too short");
return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff));
}
public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
bool num = CheckData();
int num2 = 0;
ReadOnlySpan<byte> readOnlySpan;
if (num) {
if (m_bufPos > 0) {
int num3 = ASCON_AEAD_RATE - m_bufPos;
if (input.Length < num3) {
input.CopyTo(m_buf.AsSpan(m_bufPos));
m_bufPos += input.Length;
return 0;
}
readOnlySpan = input.Slice(0, num3);
readOnlySpan.CopyTo(m_buf.AsSpan(m_bufPos));
int num4 = num3;
input = input.Slice(num4, input.Length - num4);
ProcessBufferEncrypt(m_buf, output);
num2 = ASCON_AEAD_RATE;
}
while (input.Length >= ASCON_AEAD_RATE) {
ReadOnlySpan<byte> buffer = input;
int num4 = num2;
ProcessBufferEncrypt(buffer, output.Slice(num4, output.Length - num4));
num4 = ASCON_AEAD_RATE;
input = input.Slice(num4, input.Length - num4);
num2 += ASCON_AEAD_RATE;
}
} else {
int num5 = m_bufferSizeDecrypt - m_bufPos;
if (input.Length < num5) {
input.CopyTo(m_buf.AsSpan(m_bufPos));
m_bufPos += input.Length;
return 0;
}
int num4;
while (m_bufPos >= ASCON_AEAD_RATE) {
ReadOnlySpan<byte> buffer2 = m_buf;
num4 = num2;
ProcessBufferDecrypt(buffer2, output.Slice(num4, output.Length - num4));
m_bufPos -= ASCON_AEAD_RATE;
m_buf.AsSpan(0, m_bufPos).CopyFrom(m_buf.AsSpan(ASCON_AEAD_RATE));
num2 += ASCON_AEAD_RATE;
num5 += ASCON_AEAD_RATE;
if (input.Length < num5) {
input.CopyTo(m_buf.AsSpan(m_bufPos));
m_bufPos += input.Length;
return num2;
}
}
num5 = ASCON_AEAD_RATE - m_bufPos;
readOnlySpan = input.Slice(0, num5);
readOnlySpan.CopyTo(m_buf.AsSpan(m_bufPos));
num4 = num5;
input = input.Slice(num4, input.Length - num4);
ReadOnlySpan<byte> buffer3 = m_buf;
num4 = num2;
ProcessBufferDecrypt(buffer3, output.Slice(num4, output.Length - num4));
num2 += ASCON_AEAD_RATE;
while (input.Length >= m_bufferSizeDecrypt) {
ReadOnlySpan<byte> buffer4 = input;
num4 = num2;
ProcessBufferDecrypt(buffer4, output.Slice(num4, output.Length - num4));
num4 = ASCON_AEAD_RATE;
input = input.Slice(num4, input.Length - num4);
num2 += ASCON_AEAD_RATE;
}
}
input.CopyTo(m_buf);
m_bufPos = input.Length;
return num2;
}
public int DoFinal(byte[] outBytes, int outOff)
{
return DoFinal(outBytes.AsSpan(outOff));
}
public int DoFinal(Span<byte> output)
{
int num;
if (CheckData()) {
num = m_bufPos + CRYPTO_ABYTES;
Check.OutputLength(output, num, "output buffer too short");
ProcessFinalEncrypt(m_buf.AsSpan(0, m_bufPos), output);
mac = new byte[CRYPTO_ABYTES];
Pack.UInt64_To_BE(x3, mac.AsSpan());
Pack.UInt64_To_BE(x4, mac.AsSpan(8));
byte[] source = mac;
int bufPos = m_bufPos;
source.CopyTo(output.Slice(bufPos, output.Length - bufPos));
Reset(false);
} else {
if (m_bufPos < CRYPTO_ABYTES)
throw new InvalidCipherTextException("data too short");
m_bufPos -= CRYPTO_ABYTES;
num = m_bufPos;
Check.OutputLength(output, num, "output buffer too short");
ProcessFinalDecrypt(m_buf.AsSpan(0, m_bufPos), output);
x3 ^= Pack.BE_To_UInt64(m_buf.AsSpan(m_bufPos));
x4 ^= Pack.BE_To_UInt64(m_buf.AsSpan(m_bufPos + 8));
if ((x3 | x4) != 0)
throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
Reset(true);
}
return num;
}
public byte[] GetMac()
{
return 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 - CRYPTO_ABYTES);
break;
case State.DecData:
case State.DecFinal:
num = System.Math.Max(0, num + m_bufPos - CRYPTO_ABYTES);
break;
case State.EncData:
case State.EncFinal:
num += m_bufPos;
break;
}
return num - num % ASCON_AEAD_RATE;
}
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 - CRYPTO_ABYTES);
case State.DecData:
case State.DecFinal:
return System.Math.Max(0, num + m_bufPos - CRYPTO_ABYTES);
case State.EncData:
case State.EncFinal:
return num + m_bufPos + CRYPTO_ABYTES;
default:
return num + CRYPTO_ABYTES;
}
}
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] = 128;
if (m_bufPos >= 8) {
x0 ^= Pack.BE_To_UInt64(m_buf, 0);
x1 ^= (ulong)((long)Pack.BE_To_UInt64(m_buf, 8) & (-1 << 56 - (m_bufPos - 8 << 3)));
} else
x0 ^= (ulong)((long)Pack.BE_To_UInt64(m_buf, 0) & (-1 << 56 - (m_bufPos << 3)));
P(nr);
}
x4 ^= 1;
m_bufPos = 0;
m_state = nextState;
}
private void FinishData(State nextState)
{
switch (asconParameters) {
case AsconParameters.ascon128:
x1 ^= K1;
x2 ^= K2;
break;
case AsconParameters.ascon128a:
x2 ^= K1;
x3 ^= K2;
break;
case AsconParameters.ascon80pq:
x1 ^= ((K0 << 32) | (K1 >> 32));
x2 ^= ((K1 << 32) | (K2 >> 32));
x3 ^= K2 << 32;
break;
default:
throw new InvalidOperationException();
}
P(12);
x3 ^= K1;
x4 ^= K2;
m_state = nextState;
}
private void P(int nr)
{
if (nr >= 8) {
if (nr == 12) {
ROUND(240);
ROUND(225);
ROUND(210);
ROUND(195);
}
ROUND(180);
ROUND(165);
}
ROUND(150);
ROUND(135);
ROUND(120);
ROUND(105);
ROUND(90);
ROUND(75);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ROUND(ulong c)
{
ulong num = x0 ^ x1 ^ x2 ^ x3 ^ c ^ (x1 & (x0 ^ x2 ^ x4 ^ c));
ulong num2 = x0 ^ x2 ^ x3 ^ x4 ^ c ^ ((x1 ^ x2 ^ c) & (x1 ^ x3));
ulong num3 = x1 ^ x2 ^ x4 ^ c ^ (x3 & x4);
ulong num4 = x0 ^ x1 ^ x2 ^ c ^ (~x0 & (x3 ^ x4));
ulong num5 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
x0 = (num ^ Longs.RotateRight(num, 19) ^ Longs.RotateRight(num, 28));
x1 = (num2 ^ Longs.RotateRight(num2, 39) ^ Longs.RotateRight(num2, 61));
x2 = ~(num3 ^ Longs.RotateRight(num3, 1) ^ Longs.RotateRight(num3, 6));
x3 = (num4 ^ Longs.RotateRight(num4, 10) ^ Longs.RotateRight(num4, 17));
x4 = (num5 ^ Longs.RotateRight(num5, 7) ^ Longs.RotateRight(num5, 41));
}
private void ascon_aeadinit()
{
x0 = ASCON_IV;
if (CRYPTO_KEYBYTES == 20)
x0 ^= K0;
x1 = K1;
x2 = K2;
x3 = N0;
x4 = N1;
P(12);
if (CRYPTO_KEYBYTES == 20)
x2 ^= K0;
x3 ^= K1;
x4 ^= K2;
}
private void ProcessBufferAad(ReadOnlySpan<byte> buffer)
{
x0 ^= Pack.BE_To_UInt64(buffer);
if (ASCON_AEAD_RATE == 16)
x1 ^= Pack.BE_To_UInt64(buffer.Slice(8, buffer.Length - 8));
P(nr);
}
private void ProcessBufferDecrypt(ReadOnlySpan<byte> buffer, Span<byte> output)
{
Check.OutputLength(output, ASCON_AEAD_RATE, "output buffer too short");
ulong num = Pack.BE_To_UInt64(buffer);
Pack.UInt64_To_BE(x0 ^ num, output);
x0 = num;
if (ASCON_AEAD_RATE == 16) {
ulong num2 = Pack.BE_To_UInt64(buffer.Slice(8, buffer.Length - 8));
Pack.UInt64_To_BE(x1 ^ num2, output.Slice(8, output.Length - 8));
x1 = num2;
}
P(nr);
}
private void ProcessBufferEncrypt(ReadOnlySpan<byte> buffer, Span<byte> output)
{
Check.OutputLength(output, ASCON_AEAD_RATE, "output buffer too short");
x0 ^= Pack.BE_To_UInt64(buffer);
Pack.UInt64_To_BE(x0, output);
if (ASCON_AEAD_RATE == 16) {
x1 ^= Pack.BE_To_UInt64(buffer.Slice(8, buffer.Length - 8));
Pack.UInt64_To_BE(x1, output.Slice(8, output.Length - 8));
}
P(nr);
}
private void ProcessFinalDecrypt(ReadOnlySpan<byte> input, Span<byte> output)
{
if (input.Length >= 8) {
ulong num = Pack.BE_To_UInt64(input);
x0 ^= num;
Pack.UInt64_To_BE(x0, output);
x0 = num;
input = input.Slice(8, input.Length - 8);
output = output.Slice(8, output.Length - 8);
x1 ^= PAD(input.Length);
if (!input.IsEmpty) {
num = Pack.BE_To_UInt64_High(input);
x1 ^= num;
Pack.UInt64_To_BE_High(x1, output.Slice(0, input.Length));
x1 &= ulong.MaxValue >> (input.Length << 3);
x1 ^= num;
}
} else {
x0 ^= PAD(input.Length);
if (!input.IsEmpty) {
ulong num2 = Pack.BE_To_UInt64_High(input);
x0 ^= num2;
Pack.UInt64_To_BE_High(x0, output.Slice(0, input.Length));
x0 &= ulong.MaxValue >> (input.Length << 3);
x0 ^= num2;
}
}
FinishData(State.DecFinal);
}
private void ProcessFinalEncrypt(ReadOnlySpan<byte> input, Span<byte> output)
{
if (input.Length >= 8) {
x0 ^= Pack.BE_To_UInt64(input);
Pack.UInt64_To_BE(x0, output);
input = input.Slice(8, input.Length - 8);
output = output.Slice(8, output.Length - 8);
x1 ^= PAD(input.Length);
if (!input.IsEmpty) {
x1 ^= Pack.BE_To_UInt64_High(input);
Pack.UInt64_To_BE_High(x1, output.Slice(0, input.Length));
}
} else {
x0 ^= PAD(input.Length);
if (!input.IsEmpty) {
x0 ^= Pack.BE_To_UInt64_High(input);
Pack.UInt64_To_BE_High(x0, output.Slice(0, input.Length));
}
}
FinishData(State.EncFinal);
}
private void Reset(bool clearMac)
{
if (clearMac)
mac = null;
Arrays.Clear(m_buf);
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;
}
ascon_aeadinit();
if (initialAssociatedText != null)
ProcessAadBytes(initialAssociatedText);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong PAD(int i)
{
return 9223372036854775808 >> (i << 3);
}
}
}