2004-03-10 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / corlib / System.Security.Cryptography / FromBase64Transform.cs
1 //
2 // System.Security.Cryptography.FromBase64Transform
3 //
4 // Author:
5 //   Sergey Chaban (serge@wildwestsoftware.com)
6 //
7
8 using System;
9
10 namespace System.Security.Cryptography {
11
12         [Serializable]
13         public enum FromBase64TransformMode : int {
14                 IgnoreWhiteSpaces,
15                 DoNotIgnoreWhiteSpaces
16         }
17
18         public class FromBase64Transform : ICryptoTransform {
19
20                 private FromBase64TransformMode mode;
21                 private byte [] accumulator;
22                 private byte [] filterBuffer;
23                 private int accPtr;
24                 private bool m_disposed;
25
26
27                 /// <summary>
28                 ///  Creates a new instance of the decoder
29                 ///  with the default transformation mode (IgnoreWhiteSpaces).
30                 /// </summary>
31                 public FromBase64Transform ()
32                         : this (FromBase64TransformMode.IgnoreWhiteSpaces)
33                 {
34                 }
35
36
37                 /// <summary>
38                 ///  Creates a new instance of the decoder
39                 ///  with the specified transformation mode.
40                 /// </summary>
41                 public FromBase64Transform (FromBase64TransformMode mode)
42                 {
43                         this.mode = mode;
44                         accumulator = new byte [4];
45                         filterBuffer = new byte [4];
46                         accPtr = 0;
47                         m_disposed = false;
48                 }
49
50                 ~FromBase64Transform () 
51                 {
52                         Dispose (false);
53                 }
54
55
56                 public bool CanTransformMultipleBlocks {
57                         get { return false; }
58                 }
59
60                 public virtual bool CanReuseTransform {
61                         get { return true; }
62                 }
63
64                 /// <summary>
65                 ///  Returns the input block size for the Base64 decoder.
66                 /// </summary>
67                 /// <remarks>
68                 ///  The input block size for Base64 decoder is always 1 byte.
69                 /// </remarks>
70                 public int InputBlockSize {
71                         get {
72                                 return 1;
73                         }
74                 }
75
76
77                 /// <summary>
78                 ///  Returns the output block size for the Base64 decoder.
79                 /// </summary>
80                 /// <remarks>
81                 ///  The value returned by this property is always 3.
82                 /// </remarks>
83                 public int OutputBlockSize {
84                         get {
85                                 return 3;
86                         }
87                 }
88
89                 public void Clear() 
90                 {
91                         Dispose (true);
92                 }
93
94                 void IDisposable.Dispose () 
95                 {
96                         Dispose (true);
97                         GC.SuppressFinalize (this);  // Finalization is now unnecessary
98                 }
99
100                 protected virtual void Dispose (bool disposing) 
101                 {
102                         if (!m_disposed) {
103                                 // dispose unmanaged objects
104                                 if (disposing) {
105                                         // dispose managed objects
106                                 }
107                                 m_disposed = true;
108                         }
109                 }
110
111                 private int Filter (byte [] buffer, int offset, int count)
112                 {
113                         int end = offset + count;
114                         int len = filterBuffer.Length;
115                         int ptr = 0;
116                         byte [] filter = this.filterBuffer;
117
118                         for (int i = offset; i < end; i++) {
119                                 byte b = buffer [i];
120                                 if (!Char.IsWhiteSpace ((char) b)) {
121                                         if (ptr >= len) {
122                                                 len <<= 1;
123                                                 this.filterBuffer = new byte [len];
124                                                 Array.Copy(filter, 0, this.filterBuffer, 0, len >> 1);
125                                                 filter = this.filterBuffer;
126                                         }
127                                         filter [ptr++] = b;
128                                 }
129                         }
130
131                         return ptr;
132                 }
133
134                 private byte [] lookupTable; 
135
136                 private byte lookup(byte input) {
137                         byte ret;
138                         if ((input >= lookupTable.Length) || 
139                             ((ret = lookupTable[input]) == Byte.MaxValue))
140                                 throw new System.FormatException("Invalid character in a Base-64 string.");
141                         return ret;
142                 }
143
144                 private int DoTransform (byte [] inputBuffer,
145                                          int inputOffset,
146                                          int inputCount,
147                                          byte [] outputBuffer,
148                                          int outputOffset)
149                 {
150                         int full = inputCount >> 2;
151                         if (full == 0) return 0;
152
153                         int rem = 0;
154
155                         if (inputBuffer[inputCount - 1] == (byte)'=') {
156                                 ++rem;
157                                 --full;
158                         }
159
160                         if (inputBuffer[inputCount - 2] == (byte)'=') ++rem;
161
162                         lookupTable = Base64Table.DecodeTable;
163                         int b0,b1,b2,b3;
164
165                         for (int i = 0; i < full; i++) {
166                                 b0 = lookup (inputBuffer [inputOffset++]);
167                                 b1 = lookup (inputBuffer [inputOffset++]);
168                                 b2 = lookup (inputBuffer [inputOffset++]);
169                                 b3 = lookup (inputBuffer [inputOffset++]);
170
171                                 outputBuffer [outputOffset++] = (byte) ((b0 << 2) | (b1 >> 4));
172                                 outputBuffer [outputOffset++] = (byte) ((b1 << 4) | (b2 >> 2));
173                                 outputBuffer [outputOffset++] = (byte) ((b2 << 6) | b3);
174                         }
175
176                         int res = full * 3;
177
178                         switch (rem) {
179                         case 0:
180                                 break;
181                         case 1:
182                                 b0 = lookup (inputBuffer [inputOffset++]);
183                                 b1 = lookup (inputBuffer [inputOffset++]);
184                                 b2 = lookup (inputBuffer [inputOffset++]);
185                                 outputBuffer [outputOffset++] = (byte) ((b0 << 2) | (b1 >> 4));
186                                 outputBuffer [outputOffset++] = (byte) ((b1 << 4) | (b2 >> 2));
187                                 res += 2;
188                                 break;
189                         case 2:
190                                 b0 = lookup (inputBuffer [inputOffset++]);
191                                 b1 = lookup (inputBuffer [inputOffset++]);
192                                 outputBuffer [outputOffset++] = (byte) ((b0 << 2) | (b1 >> 4));
193                                 ++res;
194                                 break;
195                         default:
196                                 break;
197                         }
198
199                         return res;
200                 }
201
202
203                 /// <summary>
204                 /// </summary>
205                 public int TransformBlock (byte [] inputBuffer,
206                                                    int inputOffset,
207                                                    int inputCount,
208                                                    byte [] outputBuffer,
209                                                    int outputOffset)
210                 {
211                         if (m_disposed)
212                                 throw new ObjectDisposedException ("FromBase64Transform");
213
214                         int n;
215                         byte [] src;
216                         int srcOff;
217                         int res = 0;
218
219                         if (mode == FromBase64TransformMode.IgnoreWhiteSpaces) {
220                                 n = Filter (inputBuffer, inputOffset, inputCount);
221                                 src = filterBuffer;
222                                 srcOff = 0;
223                         } else {
224                                 n = inputCount;
225                                 src = inputBuffer;
226                                 srcOff = inputOffset;
227                         }
228
229
230                         int count = accPtr + n;
231
232                         if (count < 4) {
233                                 Array.Copy (src, srcOff, accumulator, accPtr, n);
234                                 accPtr = count;
235                         } else {
236                                 byte [] tmpBuff = new byte [count];
237                                 Array.Copy (accumulator, 0, tmpBuff, 0, accPtr);
238                                 Array.Copy (src, srcOff, tmpBuff, accPtr, n);
239                                 accPtr = count & 3;
240                                 Array.Copy (src, srcOff + (n - accPtr), accumulator, 0, accPtr);
241                                 res = DoTransform (tmpBuff, 0, count & (~3), outputBuffer, outputOffset) ;
242                         }
243
244                         return res;
245                 }
246
247
248                 /// <summary>
249                 /// </summary>
250                 public byte [] TransformFinalBlock (byte [] inputBuffer,
251                                                             int inputOffset,
252                                                             int inputCount)
253                 {
254                         if (m_disposed)
255                                 throw new ObjectDisposedException ("FromBase64Transform");
256
257                         byte [] src;
258                         int srcOff;
259                         int n;
260
261                         if (mode == FromBase64TransformMode.IgnoreWhiteSpaces) {
262                                 n = Filter (inputBuffer, inputOffset, inputCount);
263                                 src = filterBuffer;
264                                 srcOff = 0;
265                         } else {
266                                 n = inputCount;
267                                 src = inputBuffer;
268                                 srcOff = inputOffset;
269                         }
270
271                         int dataLen = accPtr + n;
272                         byte [] tmpBuf = new byte [dataLen];
273
274                         int resLen = ((dataLen) >> 2) * 3;
275                         byte [] res = new byte [resLen];
276
277                         Array.Copy (accumulator, 0, tmpBuf, 0, accPtr);
278                         Array.Copy (src, srcOff, tmpBuf, accPtr, n);
279                         
280                         int actLen;
281                         try {
282                                 actLen = DoTransform (tmpBuf, 0, dataLen, res, 0);
283                         }
284                         catch (System.FormatException e) {
285                                 throw e; 
286                         }
287
288                         accPtr = 0;
289
290                         if (actLen < resLen) {
291                                 byte [] newres = new byte [actLen];
292
293                                 Array.Copy (res, newres, actLen);
294                                 return newres;
295                         } 
296                         else
297                                 return res;
298                 }
299
300         } // FromBase64Transform
301
302 } // System.Security.Cryptography