IsaacEngine
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Engines
{
public class IsaacEngine : IStreamCipher
{
private static readonly int sizeL = 8;
private static readonly int stateArraySize = sizeL << 5;
private uint[] engineState;
private uint[] results;
private uint a;
private uint b;
private uint c;
private int index;
private byte[] keyStream = new byte[stateArraySize << 2];
private byte[] workingKey;
private bool initialised;
public virtual string AlgorithmName => "ISAAC";
public virtual void Init(bool forEncryption, ICipherParameters parameters)
{
if (!(parameters is KeyParameter))
throw new ArgumentException("invalid parameter passed to ISAAC Init - " + Platform.GetTypeName(parameters), "parameters");
KeyParameter keyParameter = (KeyParameter)parameters;
setKey(keyParameter.GetKey());
}
public virtual byte ReturnByte(byte input)
{
if (index == 0) {
isaac();
keyStream = Pack.UInt32_To_BE(results);
}
byte result = (byte)(keyStream[index] ^ input);
index = ((index + 1) & 1023);
return result;
}
public virtual void ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
{
if (!initialised)
throw new InvalidOperationException(AlgorithmName + " not initialised");
Check.DataLength(input, inOff, len, "input buffer too short");
Check.OutputLength(output, outOff, len, "output buffer too short");
for (int i = 0; i < len; i++) {
if (index == 0) {
isaac();
keyStream = Pack.UInt32_To_BE(results);
}
output[i + outOff] = (byte)(keyStream[index] ^ input[i + inOff]);
index = ((index + 1) & 1023);
}
}
public virtual void ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
{
if (!initialised)
throw new InvalidOperationException(AlgorithmName + " not initialised");
Check.OutputLength(output, input.Length, "output buffer too short");
for (int i = 0; i < input.Length; i++) {
if (index == 0) {
isaac();
keyStream = Pack.UInt32_To_BE(results);
}
output[i] = (byte)(keyStream[index++] ^ input[i]);
index &= 1023;
}
}
public virtual void Reset()
{
setKey(workingKey);
}
private void setKey(byte[] keyBytes)
{
workingKey = keyBytes;
if (engineState == null)
engineState = new uint[stateArraySize];
if (results == null)
results = new uint[stateArraySize];
for (int i = 0; i < stateArraySize; i++) {
engineState[i] = (results[i] = 0);
}
a = (b = (c = 0));
index = 0;
byte[] array = new byte[keyBytes.Length + (keyBytes.Length & 3)];
Array.Copy(keyBytes, 0, array, 0, keyBytes.Length);
for (int i = 0; i < array.Length; i += 4) {
results[i >> 2] = Pack.LE_To_UInt32(array, i);
}
uint[] array2 = new uint[sizeL];
for (int i = 0; i < sizeL; i++) {
array2[i] = 2654435769;
}
for (int i = 0; i < 4; i++) {
mix(array2);
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < stateArraySize; j += sizeL) {
for (int k = 0; k < sizeL; k++) {
array2[k] += ((i < 1) ? results[j + k] : engineState[j + k]);
}
mix(array2);
for (int k = 0; k < sizeL; k++) {
engineState[j + k] = array2[k];
}
}
}
isaac();
initialised = true;
}
private void isaac()
{
b += ++c;
for (int i = 0; i < stateArraySize; i++) {
uint num = engineState[i];
switch (i & 3) {
case 0:
a ^= a << 13;
break;
case 1:
a ^= a >> 6;
break;
case 2:
a ^= a << 2;
break;
case 3:
a ^= a >> 16;
break;
}
a += engineState[(i + 128) & 255];
uint num2 = engineState[i] = engineState[(num >> 2) & 255] + a + b;
results[i] = (b = engineState[(num2 >> 10) & 255] + num);
}
}
private void mix(uint[] x)
{
x[0] ^= x[1] << 11;
x[3] += x[0];
x[1] += x[2];
x[1] ^= x[2] >> 2;
x[4] += x[1];
x[2] += x[3];
x[2] ^= x[3] << 8;
x[5] += x[2];
x[3] += x[4];
x[3] ^= x[4] >> 16;
x[6] += x[3];
x[4] += x[5];
x[4] ^= x[5] << 10;
x[7] += x[4];
x[5] += x[6];
x[5] ^= x[6] >> 4;
x[0] += x[5];
x[6] += x[7];
x[6] ^= x[7] << 8;
x[1] += x[6];
x[7] += x[0];
x[7] ^= x[0] >> 9;
x[2] += x[7];
x[0] += x[1];
}
}
}