Welcome to Atalasoft Community Sign in | Help

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; }
Published Friday, December 11, 2009 4:59 PM by Steve Hawley

Comments

No Comments
Anonymous comments are disabled