RC4 in C#
I was working on a project this week that required some RC4 (SARC, ARC4, ARCFOUR) encryption. In looking around, I didn’t find a public version that I liked, so working from Wikipedia, I created my own. This version is in C# and I tried to build it so that it is efficient enough, but maximized usability. Therefore, there are interfaces for byte arrays (immutable and not), Streams, and IEnumerable<byte>. The code is not thread safe, so use a different instance in any thread that needs it.
Feel free to use the code as your needs and jurisdiction allow.
// This source code is property of Atalasoft, Inc. (www.atalasoft.com)
// No warrantees expressed or implied.
// You are free to use this in your own code, provided that you
// leave this notice in place.
// Change History:
// December 11, 2009 - Initial version, Steve Hawley
using System;
using System.Collections.Generic;
using System.IO;
namespace Atalasoft
{
public class Rc4
{
private byte[] _S;
int _x, _y;
public Rc4()
{
}
public Rc4(byte[] key)
: this(key, key.Length)
{
}
public Rc4(byte[] key, int length)
{
SetKey(key, length);
}
public void SetKey(byte[] key)
{
SetKey(key, key.Length);
}
public void SetKey(byte[] key, int length)
{
if (_S == null)
_S = new byte[256];
if (key == null)
throw new ArgumentNullException("key");
if (length < 1 || length > key.Length)
throw new ArgumentOutOfRangeException("length");
for (int i = 0; i < 256; i++)
_S[i] = (byte)i;
for (int i = 0, j = 0; i < 256; i++)
{
j = (j + key[i % length] + _S[i]) & 0xff;
byte temp = _S[i];
_S[i] = _S[j];
_S[j] = temp;
}
Reset();
}
public void Reset()
{
_x = _y = 0;
}
private byte GetNextMask()
{
_x = (_x + 1) & 0xff;
_y = (_y + _S[_x]) & 0xff;
byte temp = _S[_x];
_S[_x] = _S[_y];
_S[_y] = temp;
return _S[(_S[_x] + _S[_y]) & 0xff];
}
public IEnumerable<byte> Process(IEnumerable<byte> data)
{
if (data == null)
throw new ArgumentNullException("data");
if (_S == null)
throw new Exception("Need to call SetKey before calling Process");
foreach (byte b in data)
yield return (byte)(b ^ GetNextMask());
}
public void ProcessInPlace(byte[] data)
{
ProcessInPlace(data, 0, data.Length);
}
public void ProcessInPlace(byte[] data, int index, int length)
{
if (data == null)
throw new ArgumentNullException("data");
if (_S == null)
throw new Exception("Need to call SetKey before calling ProcessInPlace");
if (index < 0 || index >= data.Length)
throw new ArgumentOutOfRangeException("index");
if (length <= 0 || index + length > data.Length)
throw new ArgumentOutOfRangeException("length");
for (int i = 0; i < length; i++)
data[i + index] = (byte)(data[i + index] ^ GetNextMask());
}
public byte[] Process(byte[] data)
{
return Process(data, 0, data.Length);
}
public byte[] Process(byte[] data, int index, int length)
{
if (data == null)
throw new ArgumentNullException("data");
if (_S == null)
throw new Exception("Need to call SetKey before calling Process");
if (index < 0 || index >= data.Length)
throw new ArgumentOutOfRangeException("index");
if (length <= 0 || index + length > data.Length)
throw new ArgumentOutOfRangeException("length");
byte[] output = new byte[length];
for (int i = 0; i < length; i++)
output[i] = (byte)(data[i + index] ^ GetNextMask());
return output;
}
public void Process(Stream data, Stream output)
{
if (_S == null)
throw new Exception("Need to call SetKey before calling Process");
int databyte;
while ((databyte = data.ReadByte()) >= 0)
output.WriteByte((byte)(databyte ^ GetNextMask()));
}
public void Process(Stream data, Stream output, long length)
{
if (length <= 0 || data.Position + length > data.Length)
throw new ArgumentOutOfRangeException("length");
if (_S == null)
throw new Exception("Need to call SetKey before calling Process");
for (long i = 0; i < length; i++)
{
int databyte = data.ReadByte();
if (databyte < 0)
throw new EndOfStreamException("unexpected end of stream processing RC4");
output.WriteByte((byte)(databyte ^ GetNextMask()));
}
}
}
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }