1 /**
2  * Copyright: Copyright (C) Thomas Dixon 2009. All rights reserved.
3  * License:   BSD style: $(LICENSE)
4  * Authors:   Thomas Dixon
5  */
6 
7 module tango.util.cipher.RC4;
8 
9 private import tango.util.cipher.Cipher;
10 
11 /** Implementation of RC4 designed by Ron Rivest of RSA Security. */
12 class RC4 : StreamCipher
13 {
14     private
15     {
16         ubyte[] state;
17         const(ubyte)[] workingKey;
18         ubyte x, y;
19     }
20     
21     this()
22     {
23         state = new ubyte[256];
24     }
25 
26     this(bool encrypt, ubyte[] key) {
27         this();
28         init(encrypt, key);
29     }
30     
31     final void init(bool encrypt, ubyte[] key)
32     {
33         if (key.length < 0 || key.length > 256)
34             invalid(name()~": Invalid key length (requires 1-256 bytes)");
35                 
36         workingKey = key;
37         setup(workingKey);
38         
39         _encrypt = _initialized = true;
40     }
41     
42     @property final override const(char)[] name()
43     {
44         return "RC4";
45     }
46     
47     override ubyte returnByte(ubyte input)
48     {
49         if (!_initialized)
50             invalid(name()~": Cipher not initialized");
51             
52         y += state[++x];
53         ubyte t = state[x];
54         state[x] = state[y];
55         state[y] = t;
56         
57         return (input^state[cast(ubyte)(state[x]+state[y])]);
58     }
59     
60     final override uint update(const(void[]) input_, void[] output_)
61     {
62         if (!_initialized)
63             invalid(name()~": Cipher not initialized");
64             
65         const(ubyte[]) input = cast(const(ubyte[])) input_;
66         ubyte[] output = cast(ubyte[]) output_;
67             
68         if (input.length > output.length)
69             invalid(name()~": Output buffer too short");
70             
71         for (int i = 0; i < input.length; i++)
72         {
73             y += state[++x];
74             ubyte t = state[x];
75             state[x] = state[y];
76             state[y] = t;
77             output[i] = input[i] ^ state[cast(ubyte)(state[x]+state[y])];
78         }
79         
80         return cast(uint)input.length;
81     }
82     
83     final override void reset()
84     { 
85         setup(workingKey);
86     }
87     
88     // Do RC4's key setup in a separate method to ease resetting
89     private void setup(const(ubyte)[] key)
90     {
91         for (int i = 0; i < 256; i++)
92             state[i] = cast(ubyte)i;
93             
94         x = 0;
95         for (int i = 0; i < 256; i++)
96         {
97             x += key[i % key.length] + state[i];
98             ubyte t = state[i];
99             state[i] = state[x];
100             state[x] = t;
101         }
102         
103         x = y = 0;
104     }
105     
106     /** Some RC4 test vectors. */
107     debug (UnitTest)
108     {
109         unittest
110         {
111             __gshared immutable immutable(char)[][] test_keys = [
112                 "0123456789abcdef",
113                 "0123456789abcdef",
114                 "0000000000000000",
115                 "ef012345",
116                 "0123456789abcdef"
117             ];
118                  
119             __gshared immutable immutable(char)[][] test_plaintexts = [
120                 "0123456789abcdef",
121                 "0000000000000000",
122                 "0000000000000000",
123                 "00000000000000000000",
124                 "01010101010101010101010101010101"~
125                 "01010101010101010101010101010101"~
126                 "01010101010101010101010101010101"~
127                 "01010101010101010101010101010101"~
128                 "01010101010101010101010101010101"~
129                 "01010101010101010101010101010101"~
130                 "01010101010101010101010101010101"~
131                 "01010101010101010101010101010101"~
132                 "01010101010101010101010101010101"~
133                 "01010101010101010101010101010101"~
134                 "01010101010101010101010101010101"~
135                 "01010101010101010101010101010101"~
136                 "01010101010101010101010101010101"~
137                 "01010101010101010101010101010101"~
138                 "01010101010101010101010101010101"~
139                 "01010101010101010101010101010101"~
140                 "01010101010101010101010101010101"~
141                 "01010101010101010101010101010101"~
142                 "01010101010101010101010101010101"~
143                 "01010101010101010101010101010101"~
144                 "01010101010101010101010101010101"~
145                 "01010101010101010101010101010101"~
146                 "01010101010101010101010101010101"~
147                 "01010101010101010101010101010101"~
148                 "01010101010101010101010101010101"~
149                 "01010101010101010101010101010101"~
150                 "01010101010101010101010101010101"~
151                 "01010101010101010101010101010101"~
152                 "01010101010101010101010101010101"~
153                 "01010101010101010101010101010101"~
154                 "01010101010101010101010101010101"~
155                 "01010101010101010101010101010101"
156             ];
157                  
158             __gshared immutable immutable(char)[][] test_ciphertexts = [
159                 "75b7878099e0c596",
160                 "7494c2e7104b0879",
161                 "de188941a3375d3a",
162                 "d6a141a7ec3c38dfbd61",
163                 "7595c3e6114a09780c4ad452338e1ffd"~
164                 "9a1be9498f813d76533449b6778dcad8"~
165                 "c78a8d2ba9ac66085d0e53d59c26c2d1"~
166                 "c490c1ebbe0ce66d1b6b1b13b6b919b8"~
167                 "47c25a91447a95e75e4ef16779cde8bf"~
168                 "0a95850e32af9689444fd377108f98fd"~
169                 "cbd4e726567500990bcc7e0ca3c4aaa3"~
170                 "04a387d20f3b8fbbcd42a1bd311d7a43"~
171                 "03dda5ab078896ae80c18b0af66dff31"~
172                 "9616eb784e495ad2ce90d7f772a81747"~
173                 "b65f62093b1e0db9e5ba532fafec4750"~
174                 "8323e671327df9444432cb7367cec82f"~
175                 "5d44c0d00b67d650a075cd4b70dedd77"~
176                 "eb9b10231b6b5b741347396d62897421"~
177                 "d43df9b42e446e358e9c11a9b2184ecb"~
178                 "ef0cd8e7a877ef968f1390ec9b3d35a5"~
179                 "585cb009290e2fcde7b5ec66d9084be4"~
180                 "4055a619d9dd7fc3166f9487f7cb2729"~ 
181                 "12426445998514c15d53a18c864ce3a2"~ 
182                 "b7555793988126520eacf2e3066e230c"~  
183                 "91bee4dd5304f5fd0405b35bd99c7313"~
184                 "5d3d9bc335ee049ef69b3867bf2d7bd1"~
185                 "eaa595d8bfc0066ff8d31509eb0c6caa"~
186                 "006c807a623ef84c3d33c195d23ee320"~
187                 "c40de0558157c822d4b8c569d849aed5"~
188                 "9d4e0fd7f379586b4b7ff684ed6a189f"~
189                 "7486d49b9c4bad9ba24b96abf924372c"~
190                 "8a8fffb10d55354900a77a3db5f205e1"~
191                 "b99fcd8660863a159ad4abe40fa48934"~
192                 "163ddde542a6585540fd683cbfd8c00f"~
193                 "12129a284deacc4cdefe58be7137541c"~
194                 "047126c8d49e2755ab181ab7e940b0c0"
195             ];
196     
197             RC4 r = new RC4();
198             foreach (uint i, immutable(char)[] test_key; test_keys)
199             {
200                 ubyte[] buffer = new ubyte[test_plaintexts[i].length>>1];
201                 char[] result;
202                 
203                 r.init(true, ByteConverter.hexDecode(test_key));
204                 
205                 // Encryption
206                 r.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer);
207                 result = ByteConverter.hexEncode(buffer);
208                 assert(result == test_ciphertexts[i],
209                         r.name~": ("~result~") != ("~test_ciphertexts[i]~")");
210                         
211                 r.reset();
212                 
213                 // Decryption
214                 r.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer);
215                 result = ByteConverter.hexEncode(buffer);
216                 assert(result == test_plaintexts[i],
217                         r.name~": ("~result~") != ("~test_plaintexts[i]~")");
218             }
219         }
220     }
221 }