RC4 Stream Cipher

I recently started to look at some other cryptography ciphers outside what is included in my development platform of choice, .NET, and started reading up on RC4. RC4 is a stream cipher.

Stream Ciphers

A stream cipher is a symmetric key cipher where plain-text digits are combined with a pseudo-random cipher digit stream (key-stream). In a stream cipher each plain-text digit is encrypted one at a time with the corresponding digit of the key-stream, to give a digit of the cipher-text stream. With a stream cipher a digit is typically a bit and the combining operation an exclusive-or (XOR).

RC4 Stream Cipher

RC4 Stream Cipher

The pseudo-random key-stream is typically generated serially from a random seed value using digital shift registers. The seed value serves as the cryptographic key for decrypting the cipher-text stream.

Stream ciphers represent a different approach to symmetric encryption from block ciphers. Block ciphers operate on large blocks of data n a fixed block size. Stream ciphers typically execute at a higher speed than block ciphers and have lower hardware complexity.

RC4 Stream Cipher

In cryptography, RC4 (also known as ARC4 or ARCFOUR meaning Alleged RC4) is the most widely used software stream cipher and is used in popular protocols such as Transport Layer Security (TLS) (to protect Internet traffic) and WEP (to secure wireless networks). While remarkable for its simplicity and speed in software, RC4 has weaknesses that argue against its use in new systems.

RC4 was designed by Ron Rivest of RSA Security in 1987. RC4 was initially a trade secret, but in September 1994 a description of it was anonymously posted to a mailing list. The leaked code was confirmed to be genuine as its output was found to match that of proprietary software using licensed RC4. Because the algorithm is known, it is no longer a trade secret. The name RC4 is trademarked, so RC4 is often referred to as ARCFOUR or ARC4 (meaning alleged RC4) to avoid trademark problems.

RC4 Key Generation

RC4 generates a pseudorandom stream of bits (a keystream). As with any stream cipher, these can be used for encryption by combining it with the plaintext using bit-wise exclusive-or. Decryption is performed the same way as applying the same exclusive-or operation a 2nd time reverses the operation. To generate the keystream, the cipher makes use of a secret internal state which consists of two parts:

  • A permutation of all 256 possible bytes. (_sbox in the example code below)
  • Two 8-bit index-pointers. (_i and _j in the example code below)

The permutation is initialized with a variable length key, typically between 40 and 256 bits, using the key-scheduling algorithm. Once this has been completed, the stream of bits is generated using the pseudo-random generation algorithm.

There have been a number of attacks against RC4 but one of the prevalent ones is that the key can be determined by analysing statistics for the first few bytes of output keystream as they are non-random, leaking information about the key. This can be guarded against by discarding an initial portion of the keystream. This modified version of RC4 is called RC4-drop[n] where n is the number of initial keystream bytes that are dropped. A typical default for n could be 768 bytes, but a better value would be 3072 bytes.

A good rule to remember with RC4 is to regenerate your seed key as often as you can and don’t re-use parts of the generated key-stream as this will aid attacks. The more frequently you change your initial key, the more secure you will be.

RC4 Implementation in C#

I have put an implementation of RC4 together below. As a word of warning though don’t use this in production code as it hasn’t been tested and verified against other implementations of RC4. This is here for educational value only.

The first example of usage here generates a 256byte seed key to initialize the RC4 algorithm and then encrypts and decrypts a string. The mechanism for generating the seed key is done using the .NET random number generator provided by RNGCryptoServiceProvider.

public static byte[] GenerateKey()
{
    using (var randomNumberGenerator = new RNGCryptoServiceProvider())
    {
        var randomNumber = new byte[256];
        randomNumberGenerator.GetBytes(randomNumber);

        return randomNumber;
    }
}

Once the key has been generated we use this to initialize the RC4 instance where we can the encrypt and decrypt our data.

private static void EncryptDecryptData()
{
    var rc4 = new Rc4(GenerateKey(), "Mary had a little lamb.");

    var encrypted = rc4.EnDeCrypt();
    rc4.Text = encrypted;
    var decrypted = rc4.EnDeCrypt();
}

This 2nd example uses the RC4 algorithm as a key generator. So in this case no encryption is taking place, but the GetNextKeyByte method returns a new derived random byte each time it is called.

private static void GenerateKeyStream()
{
    var rc4 = new Rc4(GenerateKey());
    rc4.Rc4Initialize(768);

    for (var i = 0; i < 300000; i++)
    {
        Console.Write(rc4.GetNextKeyByte() + ", ");
    }
}

The code below shows the full implementation of the RC4 stream cipher.

using System;
using System.Text;

namespace CryptographyInDotNet
{
    public class Rc4
    {
        private const int N = 256;
        private int[] _sbox;
        private readonly byte[] _seedKey;
        private string _text;
        private int _i, _j;

        public Rc4(byte[] seedKey, string text)
        {
            _seedKey = seedKey;
            _text = text;
        }

        public Rc4(byte[] seedKey)
        {
            _seedKey = seedKey;
        }

        public string Text
        {
            get
            {
                return _text;
            }
            set
            {
                _text = value;
            }
        }

        public string EnDeCrypt()
        {
            Rc4Initialize();

            var cipher = new StringBuilder();

            foreach (var t in _text)
            {
                var k = GetNextKeyByte();

                var cipherBy = (t) ^ k;  

                cipher.Append(Convert.ToChar(cipherBy));
            }

            return cipher.ToString();
        }        

        public byte GetNextKeyByte()
        {
            _i = (_i + 1) % N;
            _j = (_j + _sbox[_i]) % N;

            var tempSwap = _sbox[_i];

            _sbox[_i] = _sbox[_j];
            _sbox[_j] = tempSwap;

            var k = _sbox[(_sbox[_i] + _sbox[_j]) % N];            

            return (byte)k;
        }

        public void Rc4Initialize()
        {
            Initialize();
        }

        public void Rc4Initialize(int drop)
        {
            Initialize();

            for (var i = 0; i < drop; i++)
            {
                GetNextKeyByte();
            }
        }

        private void Initialize()
        {
            _i = 0;
            _j = 0;

            _sbox = new int[N];
            var key = new int[N];

            for (var a = 0; a < N; a++)
            {
                key[a] = _seedKey[a % _seedKey.Length];
                _sbox[a] = a;
            }

            var b = 0;
            for (var a = 0; a < N; a++)
            {
                b = (b + _sbox[a] + key[a]) % N;
                var tempSwap = _sbox[a];

                _sbox[a] = _sbox[b];
                _sbox[b] = tempSwap;
            }
        }
    }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s