iTextSharp-LGPL/src/core/srcbc/crypto/engines/IdeaEngine.cs

334 lines
10 KiB
C#

using System;
using Org.BouncyCastle.Crypto.Parameters;
namespace Org.BouncyCastle.Crypto.Engines
{
/**
* A class that provides a basic International Data Encryption Algorithm (IDEA) engine.
* <p>
* This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM"
* implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (baring 1 typo at the
* end of the mulinv function!).
* </p>
* <p>
* It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/
* </p>
* <p>
* Note: This algorithm is patented in the USA, Japan, and Europe including
* at least Austria, France, Germany, Italy, Netherlands, Spain, Sweden, Switzerland
* and the United Kingdom. Non-commercial use is free, however any commercial
* products are liable for royalties. Please see
* <a href="http://www.mediacrypt.com">www.mediacrypt.com</a> for
* further details. This announcement has been included at the request of
* the patent holders.
* </p>
*/
public class IdeaEngine
: IBlockCipher
{
private const int BLOCK_SIZE = 8;
private int[] workingKey;
/**
* standard constructor.
*/
public IdeaEngine()
{
}
/**
* initialise an IDEA cipher.
*
* @param forEncryption whether or not we are for encryption.
* @param parameters the parameters required to set up the cipher.
* @exception ArgumentException if the parameters argument is
* inappropriate.
*/
public void Init(
bool forEncryption,
ICipherParameters parameters)
{
if (!(parameters is KeyParameter))
throw new ArgumentException("invalid parameter passed to IDEA init - " + parameters.GetType().ToString());
workingKey = GenerateWorkingKey(forEncryption,
((KeyParameter)parameters).GetKey());
}
public string AlgorithmName
{
get { return "IDEA"; }
}
public bool IsPartialBlockOkay
{
get { return false; }
}
public int GetBlockSize()
{
return BLOCK_SIZE;
}
public int ProcessBlock(
byte[] input,
int inOff,
byte[] output,
int outOff)
{
if (workingKey == null)
{
throw new InvalidOperationException("IDEA engine not initialised");
}
if ((inOff + BLOCK_SIZE) > input.Length)
{
throw new DataLengthException("input buffer too short");
}
if ((outOff + BLOCK_SIZE) > output.Length)
{
throw new DataLengthException("output buffer too short");
}
IdeaFunc(workingKey, input, inOff, output, outOff);
return BLOCK_SIZE;
}
public void Reset()
{
}
private static readonly int MASK = 0xffff;
private static readonly int BASE = 0x10001;
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;
}
/**
* return x = x * y where the multiplication is done modulo
* 65537 (0x10001) (as defined in the IDEA specification) and
* a zero input is taken to be 65536 (0x10000).
*
* @param x the x value
* @param y the y value
* @return x = x * y
*/
private int Mul(
int x,
int y)
{
if (x == 0)
{
x = (BASE - y);
}
else if (y == 0)
{
x = (BASE - x);
}
else
{
int p = x * y;
y = p & MASK;
x = (int) ((uint) p >> 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 x0, x1, x2, x3, t0, t1;
int keyOff = 0;
x0 = BytesToWord(input, inOff);
x1 = BytesToWord(input, inOff + 2);
x2 = BytesToWord(input, inOff + 4);
x3 = BytesToWord(input, inOff + 6);
for (int round = 0; round < 8; round++)
{
x0 = Mul(x0, workingKey[keyOff++]);
x1 += workingKey[keyOff++];
x1 &= MASK;
x2 += workingKey[keyOff++];
x2 &= MASK;
x3 = Mul(x3, workingKey[keyOff++]);
t0 = x1;
t1 = x2;
x2 ^= x0;
x1 ^= x3;
x2 = Mul(x2, workingKey[keyOff++]);
x1 += x2;
x1 &= MASK;
x1 = Mul(x1, workingKey[keyOff++]);
x2 += x1;
x2 &= MASK;
x0 ^= x1;
x3 ^= x2;
x1 ^= t1;
x2 ^= t0;
}
WordToBytes(Mul(x0, workingKey[keyOff++]), outBytes, outOff);
WordToBytes(x2 + workingKey[keyOff++], outBytes, outOff + 2); /* NB: Order */
WordToBytes(x1 + workingKey[keyOff++], outBytes, outOff + 4);
WordToBytes(Mul(x3, workingKey[keyOff]), outBytes, outOff + 6);
}
/**
* The following function is used to expand the user key to the encryption
* subkey. The first 16 bytes are the user key, and the rest of the subkey
* is calculated by rotating the previous 16 bytes by 25 bits to the left,
* and so on until the subkey is completed.
*/
private int[] ExpandKey(
byte[] uKey)
{
int[] key = new int[52];
if (uKey.Length < 16)
{
byte[] tmp = new byte[16];
Array.Copy(uKey, 0, tmp, tmp.Length - uKey.Length, uKey.Length);
uKey = tmp;
}
for (int i = 0; i < 8; i++)
{
key[i] = BytesToWord(uKey, i * 2);
}
for (int i = 8; i < 52; i++)
{
if ((i & 7) < 6)
{
key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7) & MASK;
}
else if ((i & 7) == 6)
{
key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7) & MASK;
}
else
{
key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7) & MASK;
}
}
return key;
}
/**
* This function computes multiplicative inverse using Euclid's Greatest
* Common Divisor algorithm. Zero and one are self inverse.
* <p>
* i.e. x * MulInv(x) == 1 (modulo BASE)
* </p>
*/
private int MulInv(
int x)
{
int t0, t1, q, y;
if (x < 2)
{
return x;
}
t0 = 1;
t1 = BASE / x;
y = BASE % x;
while (y != 1)
{
q = x / y;
x = x % y;
t0 = (t0 + (t1 * q)) & MASK;
if (x == 1)
{
return t0;
}
q = y / x;
y = y % x;
t1 = (t1 + (t0 * q)) & MASK;
}
return (1 - t1) & MASK;
}
/**
* Return the additive inverse of x.
* <p>
* i.e. x + AddInv(x) == 0
* </p>
*/
int AddInv(
int x)
{
return (0 - x) & MASK;
}
/**
* The function to invert the encryption subkey to the decryption subkey.
* It also involves the multiplicative inverse and the additive inverse functions.
*/
private int[] InvertKey(
int[] inKey)
{
int t1, t2, t3, t4;
int p = 52; /* We work backwards */
int[] key = new int[52];
int inOff = 0;
t1 = MulInv(inKey[inOff++]);
t2 = AddInv(inKey[inOff++]);
t3 = AddInv(inKey[inOff++]);
t4 = MulInv(inKey[inOff++]);
key[--p] = t4;
key[--p] = t3;
key[--p] = t2;
key[--p] = t1;
for (int round = 1; round < 8; round++)
{
t1 = inKey[inOff++];
t2 = inKey[inOff++];
key[--p] = t2;
key[--p] = t1;
t1 = MulInv(inKey[inOff++]);
t2 = AddInv(inKey[inOff++]);
t3 = AddInv(inKey[inOff++]);
t4 = MulInv(inKey[inOff++]);
key[--p] = t4;
key[--p] = t2; /* NB: Order */
key[--p] = t3;
key[--p] = t1;
}
t1 = inKey[inOff++];
t2 = inKey[inOff++];
key[--p] = t2;
key[--p] = t1;
t1 = MulInv(inKey[inOff++]);
t2 = AddInv(inKey[inOff++]);
t3 = AddInv(inKey[inOff++]);
t4 = MulInv(inKey[inOff]);
key[--p] = t4;
key[--p] = t3;
key[--p] = t2;
key[--p] = t1;
return key;
}
private int[] GenerateWorkingKey(
bool forEncryption,
byte[] userKey)
{
if (forEncryption)
{
return ExpandKey(userKey);
}
else
{
return InvertKey(ExpandKey(userKey));
}
}
}
}