Merge pull request #637 from LogosBible/enetdown
[mono.git] / mcs / class / Mono.Security / Mono.Security.Cryptography / ARC4Managed.cs
1 //
2 // ARC4Managed.cs: Alleged RC4(tm) compatible symmetric stream cipher
3 //      RC4 is a trademark of RSA Security
4 //
5
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 // 
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 // 
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 //
26
27 using System;
28 using System.Globalization;
29 using System.Security.Cryptography;
30
31 namespace Mono.Security.Cryptography {
32
33         // References:
34         // a.   Usenet 1994 - RC4 Algorithm revealed
35         //      http://www.qrst.de/html/dsds/rc4.htm
36
37 #if INSIDE_SYSTEM
38         internal
39 #else
40         public
41 #endif
42         class ARC4Managed : RC4, ICryptoTransform {
43
44                 private byte[] key;
45                 private byte[] state;
46                 private byte x;
47                 private byte y;
48                 private bool m_disposed;
49
50                 public ARC4Managed () : base () 
51                 {
52                         state = new byte [256];
53                         m_disposed = false;
54                 }
55
56                 ~ARC4Managed () 
57                 {
58                         Dispose (true);
59                 }
60                 
61                 protected override void Dispose (bool disposing) 
62                 {
63                         if (!m_disposed) {
64                                 x = 0;
65                                 y = 0;
66                                 if (key != null) {
67                                         Array.Clear (key, 0, key.Length);
68                                         key = null;
69                                 }
70                                 Array.Clear (state, 0, state.Length);
71                                 state = null;
72                                 GC.SuppressFinalize (this);
73                                 m_disposed = true;
74                         }
75                 }
76
77                 public override byte[] Key {
78                         get {
79                                 if (KeyValue == null)
80                                         GenerateKey ();
81                                 return (byte[]) KeyValue.Clone (); 
82                         }
83                         set { 
84                                 if (value == null)
85                                         throw new ArgumentNullException ("Key");
86                                 KeyValue = key = (byte[]) value.Clone ();
87                                 KeySetup (key);
88                         }
89                 }
90
91                 public bool CanReuseTransform {
92                         get { return false; }
93                 }
94
95                 public override ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgvIV)
96                 {
97                         Key = rgbKey;
98                         return (ICryptoTransform) this;
99                 }
100
101                 public override ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgvIV) 
102                 {
103                         Key = rgbKey;
104                         return CreateEncryptor ();
105                 }
106
107                 public override void GenerateIV () 
108                 {
109                         // not used for a stream cipher
110                         IV = new byte [0];
111                 }
112
113                 public override void GenerateKey () 
114                 {
115                         KeyValue = KeyBuilder.Key (KeySizeValue >> 3);
116                 }
117
118                 public bool CanTransformMultipleBlocks {
119                         get { return true; }
120                 }
121
122                 public int InputBlockSize {
123                         get { return 1; }
124                 }
125
126                 public int OutputBlockSize {
127                         get { return 1; }
128                 }
129
130                 private void KeySetup (byte[] key) 
131                 {
132                         byte index1 = 0;
133                         byte index2 = 0;
134
135                         for (int counter = 0; counter < 256; counter++)
136                                 state [counter] = (byte) counter;    
137                         x = 0;
138                         y = 0;
139                         for (int counter = 0; counter < 256; counter++) {
140                                 index2 = (byte) (key [index1] + state [counter] + index2);
141                                 // swap byte
142                                 byte tmp = state [counter];
143                                 state [counter] = state [index2];
144                                 state [index2] = tmp;
145                                 index1 = (byte) ((index1 + 1) % key.Length);
146                         }
147                 }
148
149                 private void CheckInput (byte[] inputBuffer, int inputOffset, int inputCount)
150                 {
151                         if (inputBuffer == null)
152                                 throw new ArgumentNullException ("inputBuffer");
153                         if (inputOffset < 0)
154                                 throw new ArgumentOutOfRangeException ("inputOffset", "< 0");
155                         if (inputCount < 0)
156                                 throw new ArgumentOutOfRangeException ("inputCount", "< 0");
157                         // ordered to avoid possible integer overflow
158                         if (inputOffset > inputBuffer.Length - inputCount)
159                                 throw new ArgumentException ("inputBuffer", Locale.GetText ("Overflow"));
160                 }
161
162                 public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) 
163                 {
164                         CheckInput (inputBuffer, inputOffset, inputCount);
165                         // check output parameters
166                         if (outputBuffer == null)
167                                 throw new ArgumentNullException ("outputBuffer");
168                         if (outputOffset < 0)
169                                 throw new ArgumentOutOfRangeException ("outputOffset", "< 0");
170                         // ordered to avoid possible integer overflow
171                         if (outputOffset > outputBuffer.Length - inputCount)
172                                 throw new ArgumentException ("outputBuffer", Locale.GetText ("Overflow"));
173
174                         return InternalTransformBlock (inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
175                 }
176
177                 private int InternalTransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) 
178                 {
179                         byte xorIndex;
180                         for (int counter = 0; counter < inputCount; counter ++) {               
181                                 x = (byte) (x + 1);
182                                 y = (byte) (state [x] + y);
183                                 // swap byte
184                                 byte tmp = state [x];
185                                 state [x] = state [y];
186                                 state [y] = tmp;
187
188                                 xorIndex = (byte) (state [x] + state [y]);
189                                 outputBuffer [outputOffset + counter] = (byte) (inputBuffer [inputOffset + counter] ^ state [xorIndex]);
190                         }
191                         return inputCount;
192                 }
193
194                 public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) 
195                 {
196                         CheckInput (inputBuffer, inputOffset, inputCount);
197
198                         byte[] output = new byte [inputCount];
199                         InternalTransformBlock (inputBuffer, inputOffset, inputCount, output, 0);
200                         return output;
201                 }
202         }
203 }