using System;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
namespace Org.BouncyCastle.Crypto.Engines{
/**
* HC-256 is a software-efficient stream cipher created by Hongjun Wu. It
* generates keystream from a 256-bit secret key and a 256-bit initialization
* vector.
* <p>
* http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
* </p><p>
* Its brother, HC-128, is a third phase candidate in the eStream contest.
* The algorithm is patent-free. No attacks are known as of today (April 2007).
* See
*
* http://www.ecrypt.eu.org/stream/hcp3.html
* </p>
*/
public class HC256Engine
: IStreamCipher
{
private uint[] p = new uint[1024];
private uint[] q = new uint[1024];
private uint cnt = 0;
private uint Step()
{
uint j = cnt & 0x3FF;
uint ret;
if (cnt < 1024)
{
uint x = p[(j - 3 & 0x3FF)];
uint y = p[(j - 1023 & 0x3FF)];
p[j] += p[(j - 10 & 0x3FF)]
+ (RotateRight(x, 10) ^ RotateRight(y, 23))
+ q[((x ^ y) & 0x3FF)];
x = p[(j - 12 & 0x3FF)];
ret = (q[x & 0xFF] + q[((x >> 8) & 0xFF) + 256]
+ q[((x >> 16) & 0xFF) + 512] + q[((x >> 24) & 0xFF) + 768])
^ p[j];
}
else
{
uint x = q[(j - 3 & 0x3FF)];
uint y = q[(j - 1023 & 0x3FF)];
q[j] += q[(j - 10 & 0x3FF)]
+ (RotateRight(x, 10) ^ RotateRight(y, 23))
+ p[((x ^ y) & 0x3FF)];
x = q[(j - 12 & 0x3FF)];
ret = (p[x & 0xFF] + p[((x >> 8) & 0xFF) + 256]
+ p[((x >> 16) & 0xFF) + 512] + p[((x >> 24) & 0xFF) + 768])
^ q[j];
}
cnt = cnt + 1 & 0x7FF;
return ret;
}
private byte[] key, iv;
private bool initialised;
private void Init()
{
if (key.Length != 32 && key.Length != 16)
throw new ArgumentException("The key must be 128/256 bits long");
if (iv.Length < 16)
throw new ArgumentException("The IV must be at least 128 bits long");
if (key.Length != 32)
{
byte[] k = new byte[32];
Array.Copy(key, 0, k, 0, key.Length);
Array.Copy(key, 0, k, 16, key.Length);
key = k;
}
if (iv.Length < 32)
{
byte[] newIV = new byte[32];
Array.Copy(iv, 0, newIV, 0, iv.Length);
Array.Copy(iv, 0, newIV, iv.Length, newIV.Length - iv.Length);
iv = newIV;
}
cnt = 0;
uint[] w = new uint[2560];
for (int i = 0; i < 32; i++)
{
w[i >> 2] |= ((uint)key[i] << (8 * (i & 0x3)));
}
for (int i = 0; i < 32; i++)
{
w[(i >> 2) + 8] |= ((uint)iv[i] << (8 * (i & 0x3)));
}
for (uint i = 16; i < 2560; i++)
{
uint x = w[i - 2];
uint y = w[i - 15];
w[i] = (RotateRight(x, 17) ^ RotateRight(x, 19) ^ (x >> 10))
+ w[i - 7]
+ (RotateRight(y, 7) ^ RotateRight(y, 18) ^ (y >> 3))
+ w[i - 16] + i;
}
Array.Copy(w, 512, p, 0, 1024);
Array.Copy(w, 1536, q, 0, 1024);
for (int i = 0; i < 4096; i++)
{
Step();
}
cnt = 0;
}
public string AlgorithmName
{
get { return "HC-256"; }
}
/**
* Initialise a HC-256 cipher.
*
* @param forEncryption whether or not we are for encryption. Irrelevant, as
* encryption and decryption are the same.
* @param params the parameters required to set up the cipher.
* @throws ArgumentException if the params argument is
* inappropriate (ie. the key is not 256 bit long).
*/
public void Init(
bool forEncryption,
ICipherParameters parameters)
{
ICipherParameters keyParam = parameters;
if (parameters is ParametersWithIV)
{
iv = ((ParametersWithIV)parameters).GetIV();
keyParam = ((ParametersWithIV)parameters).Parameters;
}
else
{
iv = new byte[0];
}
if (keyParam is KeyParameter)
{
key = ((KeyParameter)keyParam).GetKey();
Init();
}
else
{
throw new ArgumentException(
"Invalid parameter passed to HC256 init - " + parameters.GetType().Name,
"parameters");
}
initialised = true;
}
private byte[] buf = new byte[4];
private int idx = 0;
private byte GetByte()
{
if (idx == 0)
{
Pack.UInt32_To_LE(Step(), buf);
}
byte ret = buf[idx];
idx = idx + 1 & 0x3;
return ret;
}
public void ProcessBytes(
byte[] input,
int inOff,
int len,
byte[] output,
int outOff)
{
if (!initialised)
throw new InvalidOperationException(AlgorithmName + " not initialised");
if ((inOff + len) > input.Length)
throw new DataLengthException("input buffer too short");
if ((outOff + len) > output.Length)
throw new DataLengthException("output buffer too short");
for (int i = 0; i < len; i++)
{
output[outOff + i] = (byte)(input[inOff + i] ^ GetByte());
}
}
public void Reset()
{
idx = 0;
Init();
}
public byte ReturnByte(byte input)
{
return (byte)(input ^ GetByte());
}
private static uint RotateRight(uint x, int bits)
{
return (x >> bits) | (x << -bits);
}
}
}
|