231 lines
5.0 KiB
C#
231 lines
5.0 KiB
C#
using System;
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
using Org.BouncyCastle.Utilities;
|
|
|
|
namespace Org.BouncyCastle.Crypto.Engines;
|
|
|
|
public class IdeaEngine : IBlockCipher
|
|
{
|
|
private const int BLOCK_SIZE = 8;
|
|
|
|
private int[] workingKey;
|
|
|
|
private static readonly int MASK = 65535;
|
|
|
|
private static readonly int BASE = 65537;
|
|
|
|
public virtual string AlgorithmName => "IDEA";
|
|
|
|
public virtual bool IsPartialBlockOkay => false;
|
|
|
|
public virtual void Init(bool forEncryption, ICipherParameters parameters)
|
|
{
|
|
if (!(parameters is KeyParameter))
|
|
{
|
|
throw new ArgumentException("invalid parameter passed to IDEA init - " + Platform.GetTypeName(parameters));
|
|
}
|
|
workingKey = GenerateWorkingKey(forEncryption, ((KeyParameter)parameters).GetKey());
|
|
}
|
|
|
|
public virtual int GetBlockSize()
|
|
{
|
|
return 8;
|
|
}
|
|
|
|
public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
|
|
{
|
|
if (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(workingKey, input, inOff, output, outOff);
|
|
return 8;
|
|
}
|
|
|
|
public virtual void Reset()
|
|
{
|
|
}
|
|
|
|
private int BytesToWord(byte[] input, int inOff)
|
|
{
|
|
return ((input[inOff] << 8) & 0xFF00) + (input[inOff + 1] & 0xFF);
|
|
}
|
|
|
|
private void WordToBytes(int word, byte[] outBytes, int outOff)
|
|
{
|
|
outBytes[outOff] = (byte)((uint)word >> 8);
|
|
outBytes[outOff + 1] = (byte)word;
|
|
}
|
|
|
|
private int Mul(int x, int y)
|
|
{
|
|
if (x == 0)
|
|
{
|
|
x = BASE - y;
|
|
}
|
|
else if (y == 0)
|
|
{
|
|
x = BASE - x;
|
|
}
|
|
else
|
|
{
|
|
int num = x * y;
|
|
y = num & MASK;
|
|
x = num >>> 16;
|
|
x = y - x + ((y < x) ? 1 : 0);
|
|
}
|
|
return x & MASK;
|
|
}
|
|
|
|
private void IdeaFunc(int[] workingKey, byte[] input, int inOff, byte[] outBytes, int outOff)
|
|
{
|
|
int num = 0;
|
|
int x = BytesToWord(input, inOff);
|
|
int num2 = BytesToWord(input, inOff + 2);
|
|
int num3 = BytesToWord(input, inOff + 4);
|
|
int x2 = BytesToWord(input, inOff + 6);
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
x = Mul(x, workingKey[num++]);
|
|
num2 += workingKey[num++];
|
|
num2 &= MASK;
|
|
num3 += workingKey[num++];
|
|
num3 &= MASK;
|
|
x2 = Mul(x2, workingKey[num++]);
|
|
int num4 = num2;
|
|
int num5 = num3;
|
|
num3 ^= x;
|
|
num2 ^= x2;
|
|
num3 = Mul(num3, workingKey[num++]);
|
|
num2 += num3;
|
|
num2 &= MASK;
|
|
num2 = Mul(num2, workingKey[num++]);
|
|
num3 += num2;
|
|
num3 &= MASK;
|
|
x ^= num2;
|
|
x2 ^= num3;
|
|
num2 ^= num5;
|
|
num3 ^= num4;
|
|
}
|
|
WordToBytes(Mul(x, workingKey[num++]), outBytes, outOff);
|
|
WordToBytes(num3 + workingKey[num++], outBytes, outOff + 2);
|
|
WordToBytes(num2 + workingKey[num++], outBytes, outOff + 4);
|
|
WordToBytes(Mul(x2, workingKey[num]), outBytes, outOff + 6);
|
|
}
|
|
|
|
private 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] = BytesToWord(uKey, i * 2);
|
|
}
|
|
for (int j = 8; j < 52; j++)
|
|
{
|
|
if ((j & 7) < 6)
|
|
{
|
|
array[j] = (((array[j - 7] & 0x7F) << 9) | (array[j - 6] >> 7)) & MASK;
|
|
}
|
|
else if ((j & 7) == 6)
|
|
{
|
|
array[j] = (((array[j - 7] & 0x7F) << 9) | (array[j - 14] >> 7)) & MASK;
|
|
}
|
|
else
|
|
{
|
|
array[j] = (((array[j - 15] & 0x7F) << 9) | (array[j - 14] >> 7)) & MASK;
|
|
}
|
|
}
|
|
return array;
|
|
}
|
|
|
|
private int MulInv(int x)
|
|
{
|
|
if (x < 2)
|
|
{
|
|
return x;
|
|
}
|
|
int num = 1;
|
|
int num2 = BASE / x;
|
|
int num3 = BASE % x;
|
|
while (num3 != 1)
|
|
{
|
|
int num4 = x / num3;
|
|
x %= num3;
|
|
num = (num + num2 * num4) & MASK;
|
|
if (x == 1)
|
|
{
|
|
return num;
|
|
}
|
|
num4 = num3 / x;
|
|
num3 %= x;
|
|
num2 = (num2 + num * num4) & MASK;
|
|
}
|
|
return (1 - num2) & MASK;
|
|
}
|
|
|
|
private int AddInv(int x)
|
|
{
|
|
return -x & MASK;
|
|
}
|
|
|
|
private int[] InvertKey(int[] inKey)
|
|
{
|
|
int num = 52;
|
|
int[] array = new int[52];
|
|
int num2 = 0;
|
|
int num3 = MulInv(inKey[num2++]);
|
|
int num4 = AddInv(inKey[num2++]);
|
|
int num5 = AddInv(inKey[num2++]);
|
|
int num6 = MulInv(inKey[num2++]);
|
|
array[--num] = num6;
|
|
array[--num] = num5;
|
|
array[--num] = num4;
|
|
array[--num] = num3;
|
|
for (int i = 1; i < 8; i++)
|
|
{
|
|
num3 = inKey[num2++];
|
|
num4 = inKey[num2++];
|
|
array[--num] = num4;
|
|
array[--num] = num3;
|
|
num3 = MulInv(inKey[num2++]);
|
|
num4 = AddInv(inKey[num2++]);
|
|
num5 = AddInv(inKey[num2++]);
|
|
num6 = MulInv(inKey[num2++]);
|
|
array[--num] = num6;
|
|
array[--num] = num4;
|
|
array[--num] = num5;
|
|
array[--num] = num3;
|
|
}
|
|
num3 = inKey[num2++];
|
|
num4 = inKey[num2++];
|
|
array[--num] = num4;
|
|
array[--num] = num3;
|
|
num3 = MulInv(inKey[num2++]);
|
|
num4 = AddInv(inKey[num2++]);
|
|
num5 = AddInv(inKey[num2++]);
|
|
num6 = MulInv(inKey[num2]);
|
|
array[--num] = num6;
|
|
array[--num] = num5;
|
|
array[--num] = num4;
|
|
array[--num] = num3;
|
|
return array;
|
|
}
|
|
|
|
private int[] GenerateWorkingKey(bool forEncryption, byte[] userKey)
|
|
{
|
|
if (forEncryption)
|
|
{
|
|
return ExpandKey(userKey);
|
|
}
|
|
return InvertKey(ExpandKey(userKey));
|
|
}
|
|
}
|