IdeaEngine
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Engines
{
public class IdeaEngine : IBlockCipher
{
private const int Base = 65537;
private const int BlockSize = 8;
private const int Mask = 65535;
private int[] m_workingKey;
public virtual string AlgorithmName => "IDEA";
public virtual void Init(bool forEncryption, ICipherParameters parameters)
{
KeyParameter keyParameter = parameters as KeyParameter;
if (keyParameter == null)
throw new ArgumentException("invalid parameter passed to IDEA Init - " + Platform.GetTypeName(parameters));
m_workingKey = GenerateWorkingKey(forEncryption, keyParameter.GetKey());
}
public virtual int GetBlockSize()
{
return 8;
}
public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
{
if (m_workingKey == null)
throw new InvalidOperationException("IDEA engine not initialised");
Check.DataLength(input, inOff, 8, "input buffer too short");
Check.OutputLength(output, outOff, 8, "output buffer too short");
IdeaFunc(m_workingKey, input.AsSpan(inOff), output.AsSpan(outOff));
return 8;
}
public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
{
if (m_workingKey == null)
throw new InvalidOperationException("IDEA engine not initialised");
Check.DataLength(input, 8, "input buffer too short");
Check.OutputLength(output, 8, "output buffer too short");
IdeaFunc(m_workingKey, input, output);
return 8;
}
private int Mul(int x, int y)
{
if (x == 0)
x = 65537 - y;
else if (y == 0) {
x = 65537 - x;
} else {
int num = x * y;
y = (num & 65535);
x = (int)((uint)num >> 16);
x = y - x + ((y < x) ? 1 : 0);
}
return x & 65535;
}
private void IdeaFunc(int[] workingKey, ReadOnlySpan<byte> input, Span<byte> output)
{
int x = Pack.BE_To_UInt16(input);
int num = Pack.BE_To_UInt16(input.Slice(2, input.Length - 2));
int num2 = Pack.BE_To_UInt16(input.Slice(4, input.Length - 4));
int x2 = Pack.BE_To_UInt16(input.Slice(6, input.Length - 6));
int num3 = 0;
for (int i = 0; i < 8; i++) {
x = Mul(x, workingKey[num3++]);
num += workingKey[num3++];
num &= 65535;
num2 += workingKey[num3++];
num2 &= 65535;
x2 = Mul(x2, workingKey[num3++]);
int num8 = num;
int num9 = num2;
num2 ^= x;
num ^= x2;
num2 = Mul(num2, workingKey[num3++]);
num += num2;
num &= 65535;
num = Mul(num, workingKey[num3++]);
num2 += num;
num2 &= 65535;
x ^= num;
x2 ^= num2;
num ^= num9;
num2 ^= num8;
}
Pack.UInt16_To_BE((ushort)Mul(x, workingKey[num3++]), output);
Pack.UInt16_To_BE((ushort)(num2 + workingKey[num3++]), output.Slice(2, output.Length - 2));
Pack.UInt16_To_BE((ushort)(num + workingKey[num3++]), output.Slice(4, output.Length - 4));
Pack.UInt16_To_BE((ushort)Mul(x2, workingKey[num3]), output.Slice(6, output.Length - 6));
}
private static int[] ExpandKey(byte[] uKey)
{
int[] array = new int[52];
if (uKey.Length < 16) {
byte[] array2 = new byte[16];
Array.Copy(uKey, 0, array2, array2.Length - uKey.Length, uKey.Length);
uKey = array2;
}
for (int i = 0; i < 8; i++) {
array[i] = Pack.BE_To_UInt16(uKey.AsSpan(i * 2));
}
for (int j = 8; j < 52; j++) {
if ((j & 7) < 6)
array[j] = ((((array[j - 7] & 127) << 9) | (array[j - 6] >> 7)) & 65535);
else if ((j & 7) == 6) {
array[j] = ((((array[j - 7] & 127) << 9) | (array[j - 14] >> 7)) & 65535);
} else {
array[j] = ((((array[j - 15] & 127) << 9) | (array[j - 14] >> 7)) & 65535);
}
}
return array;
}
private static int MulInv(int x)
{
if (x < 2)
return x;
int num = 1;
int num2 = 65537 / x;
int num3 = 65537 % x;
while (num3 != 1) {
int num4 = x / num3;
x %= num3;
num = ((num + num2 * num4) & 65535);
if (x == 1)
return num;
num4 = num3 / x;
num3 %= x;
num2 = ((num2 + num * num4) & 65535);
}
return (1 - num2) & 65535;
}
private static int AddInv(int x)
{
return -x & 65535;
}
private static int[] InvertKey(int[] inKey)
{
int[] array = new int[52];
int num = 0;
int num2 = 52;
int num4 = MulInv(inKey[num++]);
int num6 = AddInv(inKey[num++]);
int num8 = AddInv(inKey[num++]);
int num10 = MulInv(inKey[num++]);
array[--num2] = num10;
array[--num2] = num8;
array[--num2] = num6;
array[--num2] = num4;
for (int i = 1; i < 8; i++) {
num4 = inKey[num++];
num6 = inKey[num++];
array[--num2] = num6;
array[--num2] = num4;
num4 = MulInv(inKey[num++]);
num6 = AddInv(inKey[num++]);
num8 = AddInv(inKey[num++]);
num10 = MulInv(inKey[num++]);
array[--num2] = num10;
array[--num2] = num6;
array[--num2] = num8;
array[--num2] = num4;
}
num4 = inKey[num++];
num6 = inKey[num++];
array[--num2] = num6;
array[--num2] = num4;
num4 = MulInv(inKey[num++]);
num6 = AddInv(inKey[num++]);
num8 = AddInv(inKey[num++]);
num10 = MulInv(inKey[num]);
array[--num2] = num10;
array[--num2] = num8;
array[--num2] = num6;
array[--num2] = num4;
return array;
}
private static int[] GenerateWorkingKey(bool forEncryption, byte[] userKey)
{
int[] array = ExpandKey(userKey);
if (!forEncryption)
return InvertKey(array);
return array;
}
}
}