C#でRC4(Arcfour)を計算する

# 内容

PDFでRC4(Arcfour)が使われる場面があり、
Arcfourの計算は、.NETの既存クラスでは対応していないようなので、Arcfourを計算するコードを書いてみた。
一応テストベクタとは一致してそうですが、ご利用は自己責任でお願いします。

Arcfourは安全性が十分でないとして、今後は使われなくなる流れのようです。
https://ja.wikipedia.org/wiki/RC4

※RC4は登録商標のため、RC4を謳うとまずいらしい
http://e-words.jp/w/RC4.html

# 参考サイト

https://tools.ietf.org/html/draft-kaukonen-cipher-arcfour-03#ref-CRYPTLIB

# ソースコード

参考サイトの内容を実装

```csharp

using System;
using System.IO;

public class Arcfour
{
    byte[] _S; // S-Box
    byte[] _key;
    byte _Index;
    byte _J;

    Arcfour(){}

    public static Arcfour CreateArcfour(byte[] key)
    {
        Arcfour ret = new Arcfour();

        if (key.Length<5){return null;}
        ret._key = new byte[key.Length];
        Array.Copy(key,0,ret._key,0,key.Length);  // _key[i] = key[i]
        ret._S = InitializeSBox(ret._key);
        ret._Index = 0;
        ret._J = 0;

        return ret;
    }

    // 3.1 Key Setup
    static byte[] InitializeSBox(byte[] key)
    {
        // step.1.
        byte[] S  = new byte[256];

        // step.2.
        for ( int i=0 ; i<256 ; i++ ) { // ループカウンタをbyteにするとoverflowして無限ループするはずなので注意
            S[i] = (byte)i;
        }

        // step.3.
        byte[] S2 = new byte[256];
        for ( int i=0 ; i<256 ; i++ ) {
            S2[i] = key[i % key.Length];
        }

        // step.4.
        int j = 0;
        for ( int i=0 ; i<256 ; i++ ) {
            j = (byte)(j + S[i] + S2[i]);
            byte temp = S[i];
            S[i] = S[j];
            S[j] = temp;
        }

        // step.5. セキュリティの観点でメモリにゴミが残らないようi,j,S2を0クリア推奨されている。
        // (iをクリアしても意味ないような)
        // 効果が不明だがクリアしておく。
        j = 0;
        for ( int i=0 ; i<256 ; i++ ) {
            S2[i] = 0;
        }

        return S;
    }

    // 3.2 Stream Generation
    // EncryptとDecryptは同一処理
    public void Encrypt(Stream src, Stream dest, int length)
    {
        for ( int count=0 ; count<length ; count++ ) {
            int sByte = src.ReadByte();
            if (sByte < 0) {return;} // End of stream

            _Index++;
            _J += _S[_Index];
            byte temp = _S[_Index];
            _S[_Index] = _S[_J];
            _S[_J] = temp;
            
            temp = (byte)(_S[_Index] + _S[_J]);
            byte K = _S[temp];

            dest.WriteByte((byte)(((byte)sByte) ^ K)); // XOR
        }
    }
}

// -------------------------------------------------------------

class ArcfourTest
{
    static void Test1()
    {
        byte[] plain = new byte[8]; // all zero
        byte[] chipher = new byte[8];
        byte[] key = new byte[]{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};  // 128bit
        Arcfour arcfour = Arcfour.CreateArcfour(key);

        MemoryStream src  = new MemoryStream(plain, false); // 第2引数=falseでreadonly
        MemoryStream dest = new MemoryStream(chipher);
        arcfour.Encrypt(src, dest, plain.Length);

        src.Close();
        dest.Close();

        DumpBytes(chipher);
    }

    static void Test1d()
    {
        byte[] plain = new byte[]{0x74, 0x94, 0xC2, 0xE7, 0x10, 0x4B, 0x08, 0x79};
        byte[] chipher = new byte[8];
        byte[] key = new byte[]{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};  // 64bit
        Arcfour arcfour = Arcfour.CreateArcfour(key);

        MemoryStream src  = new MemoryStream(plain, false); // 第2引数=falseでreadonly
        MemoryStream dest = new MemoryStream(chipher);
        arcfour.Encrypt(src, dest, plain.Length);

        src.Close();
        dest.Close();

        DumpBytes(chipher);
    }

    static void Test2()
    {
        //  Cipher Text:   0xf1, 0x38, 0x29, 0xc9, 0xde
        byte[] plain = new byte[]{0xdc, 0xee, 0x4c, 0xf9, 0x2c};
        byte[] chipher = new byte[5];
        byte[] key = new byte[]{0x61, 0x8a, 0x63, 0xd2, 0xfb};  // 40bit
        Arcfour arcfour = Arcfour.CreateArcfour(key);

        MemoryStream src  = new MemoryStream(plain, false); // 第2引数=falseでreadonly
        MemoryStream dest = new MemoryStream(chipher);
        arcfour.Encrypt(src, dest, plain.Length);
        
        src.Close();
        dest.Close();

        DumpBytes(chipher);
    }
    
    static byte[] TestVector3_PlainText = new byte[] {
            0x52, 0x75, 0x69, 0x73, 0x6c, 0x69, 0x6e, 0x6e,
            0x75, 0x6e, 0x20, 0x6c, 0x61, 0x75, 0x6c, 0x75,
            0x20, 0x6b, 0x6f, 0x72, 0x76, 0x69, 0x73, 0x73,
            0x73, 0x61, 0x6e, 0x69, 0x2c, 0x20, 0x74, 0xe4,
            0x68, 0x6b, 0xe4, 0x70, 0xe4, 0x69, 0x64, 0x65,
            0x6e, 0x20, 0x70, 0xe4, 0xe4, 0x6c, 0x6c, 0xe4,
            0x20, 0x74, 0xe4, 0x79, 0x73, 0x69, 0x6b, 0x75,
            0x75, 0x2e, 0x20, 0x4b, 0x65, 0x73, 0xe4, 0x79,
            0xf6, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x6f, 0x6e,
            0x6e, 0x69, 0x20, 0x6f, 0x6d, 0x61, 0x6e, 0x61,
            0x6e, 0x69, 0x2c, 0x20, 0x6b, 0x61, 0x73, 0x6b,
            0x69, 0x73, 0x61, 0x76, 0x75, 0x75, 0x6e, 0x20,
            0x6c, 0x61, 0x61, 0x6b, 0x73, 0x6f, 0x74, 0x20,
            0x76, 0x65, 0x72, 0x68, 0x6f, 0x75, 0x75, 0x2e,
            0x20, 0x45, 0x6e, 0x20, 0x6d, 0x61, 0x20, 0x69,
            0x6c, 0x6f, 0x69, 0x74, 0x73, 0x65, 0x2c, 0x20,
            0x73, 0x75, 0x72, 0x65, 0x20, 0x68, 0x75, 0x6f,
            0x6b, 0x61, 0x61, 0x2c, 0x20, 0x6d, 0x75, 0x74,
            0x74, 0x61, 0x20, 0x6d, 0x65, 0x74, 0x73, 0xe4,
            0x6e, 0x20, 0x74, 0x75, 0x6d, 0x6d, 0x75, 0x75,
            0x73, 0x20, 0x6d, 0x75, 0x6c, 0x6c, 0x65, 0x20,
            0x74, 0x75, 0x6f, 0x6b, 0x61, 0x61, 0x2e, 0x20,
            0x50, 0x75, 0x75, 0x6e, 0x74, 0x6f, 0x20, 0x70,
            0x69, 0x6c, 0x76, 0x65, 0x6e, 0x2c, 0x20, 0x6d,
            0x69, 0x20, 0x68, 0x75, 0x6b, 0x6b, 0x75, 0x75,
            0x2c, 0x20, 0x73, 0x69, 0x69, 0x6e, 0x74, 0x6f,
            0x20, 0x76, 0x61, 0x72, 0x61, 0x6e, 0x20, 0x74,
            0x75, 0x75, 0x6c, 0x69, 0x73, 0x65, 0x6e, 0x2c,
            0x20, 0x6d, 0x69, 0x20, 0x6e, 0x75, 0x6b, 0x6b,
            0x75, 0x75, 0x2e, 0x20, 0x54, 0x75, 0x6f, 0x6b,
            0x73, 0x75, 0x74, 0x20, 0x76, 0x61, 0x6e, 0x61,
            0x6d, 0x6f, 0x6e, 0x20, 0x6a, 0x61, 0x20, 0x76,
            0x61, 0x72, 0x6a, 0x6f, 0x74, 0x20, 0x76, 0x65,
            0x65, 0x6e, 0x2c, 0x20, 0x6e, 0x69, 0x69, 0x73,
            0x74, 0xe4, 0x20, 0x73, 0x79, 0x64, 0xe4, 0x6d,
            0x65, 0x6e, 0x69, 0x20, 0x6c, 0x61, 0x75, 0x6c,
            0x75, 0x6e, 0x20, 0x74, 0x65, 0x65, 0x6e, 0x2e,
            0x20, 0x2d, 0x20, 0x45, 0x69, 0x6e, 0x6f, 0x20,
            0x4c, 0x65, 0x69, 0x6e, 0x6f};

    static byte[] TestVector3_Key = new byte[] {
        0x29, 0x04, 0x19, 0x72, 0xfb, 0x42, 0xba, 0x5f,
        0xc7, 0x12, 0x77, 0x12, 0xf1, 0x38, 0x29, 0xc9
    };
    
    static void Test3()
    {
        byte[] plain = TestVector3_PlainText;
        //Array.Copy(TestVector3_PlainText, 0, plain, 0, plain.Length);
        byte[] chipher = new byte[plain.Length];
        byte[] key = TestVector3_Key;
        Arcfour arcfour = Arcfour.CreateArcfour(key);

        MemoryStream src  = new MemoryStream(plain, false); // 第2引数=falseでreadonly
        MemoryStream dest = new MemoryStream(chipher);
        arcfour.Encrypt(src, dest, plain.Length);
        
        src.Close();
        dest.Close();

        DumpBytes(chipher);
    }

    static void Test3_2()
    {
        byte[] plain = TestVector3_PlainText;
        //Array.Copy(TestVector3_PlainText, 0, plain, 0, plain.Length);
        byte[] chipher = new byte[plain.Length];
        byte[] key = TestVector3_Key;
        Arcfour arcfour = Arcfour.CreateArcfour(key);

        MemoryStream src  = new MemoryStream(plain, false); // 第2引数=falseでreadonly
        MemoryStream dest = new MemoryStream(chipher);
        arcfour.Encrypt(src, dest, 50);
        arcfour.Encrypt(src, dest, 50);
        arcfour.Encrypt(src, dest, 50);
        arcfour.Encrypt(src, dest, 50);
        arcfour.Encrypt(src, dest, 50);
        arcfour.Encrypt(src, dest, 50);
        arcfour.Encrypt(src, dest, 50);
        arcfour.Encrypt(src, dest, 50);
        
        src.Close();
        dest.Close();

        DumpBytes(chipher);
    }

    static void DumpBytes(byte[] a)
    {
        bool first = true;
        foreach(byte b in a) {
            if(!first){Console.Write(" ");}
            Console.Write(b.ToString("x2"));
            first=false;
        }
        Console.WriteLine();
    }

    [STAThread]
    static void Main(string[] args)
    {
        Test3_2();
    }
}

```