Match IDisposable with 4.0 changes in System.Security.Cryptography
[mono.git] / mcs / class / corlib / System.Security.Cryptography / FromBase64Transform.cs
1 //
2 // System.Security.Cryptography.FromBase64Transform.cs
3 //
4 // Authors:
5 //      Sergey Chaban (serge@wildwestsoftware.com)
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //
8 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.Globalization;
31 using System.Runtime.InteropServices;
32
33 namespace System.Security.Cryptography {
34
35         [Serializable]
36         [ComVisible (true)]
37         public enum FromBase64TransformMode : int {
38                 IgnoreWhiteSpaces,
39                 DoNotIgnoreWhiteSpaces
40         }
41
42         [ComVisible (true)]
43         public class FromBase64Transform : ICryptoTransform {
44
45                 private FromBase64TransformMode mode;
46                 private byte[] accumulator;
47                 private int accPtr;
48                 private bool m_disposed;
49
50                 private const byte TerminatorByte = ((byte) '=');
51
52                 public FromBase64Transform ()
53                         : this (FromBase64TransformMode.IgnoreWhiteSpaces)
54                 {
55                 }
56
57                 public FromBase64Transform (FromBase64TransformMode whitespaces)
58                 {
59                         this.mode = whitespaces;
60                         accumulator = new byte [4];
61                         accPtr = 0;
62                         m_disposed = false;
63                 }
64
65                 ~FromBase64Transform () 
66                 {
67                         Dispose (false);
68                 }
69
70                 public bool CanTransformMultipleBlocks {
71                         get { return false; }
72                 }
73
74                 public virtual bool CanReuseTransform {
75                         get { return true; }
76                 }
77
78                 public int InputBlockSize {
79                         get { return 1; }
80                 }
81
82                 public int OutputBlockSize {
83                         get { return 3; }
84                 }
85
86                 public void Clear() 
87                 {
88                         Dispose (true);
89                 }
90
91 #if NET_4_0
92                 public void Dispose ()
93 #else
94                 void IDisposable.Dispose () 
95 #endif
96                 {
97                         Dispose (true);
98                         GC.SuppressFinalize (this);  // Finalization is now unnecessary
99                 }
100
101                 protected virtual void Dispose (bool disposing) 
102                 {
103                         if (!m_disposed) {
104                                 // zeroize data
105                                 if (accumulator != null)
106                                         Array.Clear (accumulator, 0, accumulator.Length);
107
108                                 // dispose unmanaged objects
109                                 if (disposing) {
110                                         // dispose managed objects
111                                         accumulator = null;
112                                 }
113                                 m_disposed = true;
114                         }
115                 }
116
117                 private byte[] lookupTable; 
118
119                 private byte lookup (byte input)
120                 {
121                         if (input >= lookupTable.Length) {
122                                 throw new FormatException (
123                                         Locale.GetText ("Invalid character in a Base-64 string."));
124                         }
125
126                         byte ret = lookupTable [input];
127                         if (ret == Byte.MaxValue) {
128                                 throw new FormatException (
129                                         Locale.GetText ("Invalid character in a Base-64 string."));
130                         }
131
132                         return ret;
133                 }
134
135                 private int ProcessBlock (byte[] output, int offset)
136                 {
137                         int rem = 0;
138                         if (accumulator [3] == TerminatorByte)
139                                 rem++;
140                         if (accumulator [2] == TerminatorByte)
141                                 rem++;
142
143                         lookupTable = Base64Constants.DecodeTable;
144                         int b0,b1,b2,b3;
145
146                         switch (rem) {
147                         case 0:
148                                 b0 = lookup (accumulator [0]);
149                                 b1 = lookup (accumulator [1]);
150                                 b2 = lookup (accumulator [2]);
151                                 b3 = lookup (accumulator [3]);
152                                 output [offset++] = (byte) ((b0 << 2) | (b1 >> 4));
153                                 output [offset++] = (byte) ((b1 << 4) | (b2 >> 2));
154                                 output [offset] = (byte) ((b2 << 6) | b3);
155                                 break;
156                         case 1:
157                                 b0 = lookup (accumulator [0]);
158                                 b1 = lookup (accumulator [1]);
159                                 b2 = lookup (accumulator [2]);
160                                 output [offset++] = (byte) ((b0 << 2) | (b1 >> 4));
161                                 output [offset] = (byte) ((b1 << 4) | (b2 >> 2));
162                                 break;
163                         case 2:
164                                 b0 = lookup (accumulator [0]);
165                                 b1 = lookup (accumulator [1]);
166                                 output [offset] = (byte) ((b0 << 2) | (b1 >> 4));
167                                 break;
168                         }
169
170                         return (3 - rem);
171                 }
172
173                 private void CheckInputParameters (byte[] inputBuffer, int inputOffset, int inputCount)
174                 {
175                         if (inputBuffer == null)
176                                 throw new ArgumentNullException ("inputBuffer");
177                         if (inputOffset < 0)
178                                 throw new ArgumentOutOfRangeException ("inputOffset", "< 0");
179                         if (inputCount > inputBuffer.Length)
180                                 throw new OutOfMemoryException ("inputCount " + Locale.GetText ("Overflow"));
181                         if (inputOffset > inputBuffer.Length - inputCount)
182                                 throw new ArgumentException ("inputOffset", Locale.GetText ("Overflow"));
183                         if (inputCount < 0)
184                                 throw new OverflowException ("inputCount < 0");
185                 }
186
187                 public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
188                 {
189                         if (m_disposed)
190                                 throw new ObjectDisposedException ("FromBase64Transform");
191                         // LAMESPEC: undocumented exceptions
192                         CheckInputParameters (inputBuffer, inputOffset, inputCount);
193                         if ((outputBuffer == null) || (outputOffset < 0))
194                                 throw new FormatException ("outputBuffer");
195
196                         int res = 0;
197
198                         while (inputCount > 0) {
199                                 if (accPtr < 4) {
200                                         byte b = inputBuffer [inputOffset++];
201                                         if (mode == FromBase64TransformMode.IgnoreWhiteSpaces) {
202                                                 if (!Char.IsWhiteSpace ((char) b))
203                                                         accumulator [accPtr++] = b;
204                                         } else {
205                                                 // don't ignore, we'll fail if bad data is provided
206                                                 accumulator [accPtr++] = b;
207                                         }
208                                 }
209                                 if (accPtr == 4) {
210                                         res += ProcessBlock (outputBuffer, outputOffset);
211                                         outputOffset += 3;
212                                         accPtr = 0;
213                                 }
214                                 inputCount--;
215                         }
216
217                         return res;
218                 }
219
220                 public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
221                 {
222                         if (m_disposed)
223                                 throw new ObjectDisposedException ("FromBase64Transform");
224                         // LAMESPEC: undocumented exceptions
225                         CheckInputParameters (inputBuffer, inputOffset, inputCount);
226
227                         int ws = 0;
228                         int terminator = 0;
229                         if (mode == FromBase64TransformMode.IgnoreWhiteSpaces) {
230                                 // count whitespace inside string
231                                 for (int i=inputOffset, j=0; j < inputCount; i++, j++) {
232                                         if (Char.IsWhiteSpace ((char)inputBuffer [i]))
233                                                 ws++;
234                                 }
235                                 // no more (useful) data
236                                 if (ws == inputCount)
237                                         return new byte [0];
238                                 // there may be whitespace after the terminator
239                                 int k = inputOffset + inputCount - 1;
240                                 int n = Math.Min (2, inputCount);
241                                 while (n > 0) {
242                                         char c = (char) inputBuffer [k--];
243                                         if (c == '=') {
244                                                 terminator++;
245                                                 n--;
246                                         } else if (Char.IsWhiteSpace (c)) {
247                                                 continue;
248                                         } else {
249                                                 break;
250                                         }
251                                 }                                               
252                         } else {
253                                 if (inputBuffer [inputOffset + inputCount - 1] == TerminatorByte)
254                                         terminator++;
255                                 if (inputBuffer [inputOffset + inputCount - 2] == TerminatorByte)
256                                         terminator++;
257                         }
258                         // some terminators could already be in the accumulator
259                         if ((inputCount < 4) && (terminator < 2)) {
260                                 if ((accPtr > 2) && (accumulator [3] == TerminatorByte))
261                                         terminator++;
262                                 if ((accPtr > 1) && (accumulator [2] == TerminatorByte))
263                                         terminator++;
264                         }
265
266                         int count = ((accPtr + inputCount - ws) >> 2) * 3 - terminator;
267                         if (count <= 0)
268                                 return new byte [0];
269
270                         // allocate the "right" ammount (to avoid multiple allocation/copy)
271                         byte[] result = new byte [count];
272                         TransformBlock (inputBuffer, inputOffset, inputCount, result, 0);
273                         return result;
274                 }
275         }
276 }