2005-06-05 Peter Bartok <pbartok@novell.com>
[mono.git] / mcs / class / corlib / System.Security.Cryptography / ToBase64Transform.cs
1 //
2 // System.Security.Cryptography.ToBase64Transform
3 //
4 // Author:
5 //   Sergey Chaban (serge@wildwestsoftware.com)
6 //
7 // (C) 2004 Novell (http://www.novell.com)
8 // Copyright (C) 2004 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
32 namespace System.Security.Cryptography {
33
34         public class ToBase64Transform : ICryptoTransform {
35
36                 private bool m_disposed;
37
38                 public ToBase64Transform ()
39                 {
40                 }
41
42                 ~ToBase64Transform ()
43                 {
44                         Dispose (false);
45                 }
46
47                 public bool CanTransformMultipleBlocks {
48                         get { return false; }
49                 }
50
51                 public virtual bool CanReuseTransform {
52                         get { return true; }
53                 }
54
55                 public int InputBlockSize {
56                         get { return 3; }
57                 }
58
59                 public int OutputBlockSize {
60                         get { return 4; }
61                 }
62
63                 public void Clear() 
64                 {
65                         Dispose (true);
66                 }
67
68                 void IDisposable.Dispose () 
69                 {
70                         Dispose (true);
71                         GC.SuppressFinalize (this);  // Finalization is now unnecessary
72                 }
73
74                 protected virtual void Dispose (bool disposing) 
75                 {
76                         if (!m_disposed) {
77                                 // dispose unmanaged objects
78                                 if (disposing) {
79                                         // dispose managed objects
80                                 }
81                                 m_disposed = true;
82                         }
83                 }
84
85                 // LAMESPEC: It's not clear from docs what should be happening 
86                 // here if inputCount > InputBlockSize. It just "Converts the 
87                 // specified region of the specified byte array" and that's all.
88                 public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
89                 {
90                         if (m_disposed)
91                                 throw new ObjectDisposedException ("TransformBlock");
92                         if (inputBuffer == null)
93                                 throw new ArgumentNullException ("inputBuffer");
94                         if (outputBuffer == null)
95                                 throw new ArgumentNullException ("outputBuffer");
96                         if (inputCount < 0)
97                                 throw new ArgumentException ("inputCount", "< 0");
98                         if (inputCount > inputBuffer.Length)
99                                 throw new ArgumentException ("inputCount", Locale.GetText ("Overflow"));
100                         if (inputOffset < 0)
101                                 throw new ArgumentOutOfRangeException ("inputOffset", "< 0");
102                         // ordered to avoid possible integer overflow
103                         if (inputOffset > inputBuffer.Length - inputCount)
104                                 throw new ArgumentException ("inputOffset", Locale.GetText ("Overflow"));
105                         // ordered to avoid possible integer overflow
106 #if NET_2_0
107                         if (outputOffset < 0)
108                                 throw new ArgumentOutOfRangeException ("outputOffset", "< 0");
109                         if (outputOffset > outputBuffer.Length - inputCount)
110                                 throw new ArgumentException ("outputOffset", Locale.GetText ("Overflow"));
111 #else
112                         if ((outputOffset < 0) || (outputOffset > outputBuffer.Length - inputCount))
113                                 throw new IndexOutOfRangeException ("outputOffset");
114 #endif
115 /// To match MS implementation
116 //                      if (inputCount != this.InputBlockSize)
117 //                              throw new CryptographicException (Locale.GetText ("Invalid input length"));
118
119                         byte[] lookup = Base64Constants.EncodeTable;
120
121                         int b1 = inputBuffer [inputOffset];
122                         int b2 = inputBuffer [inputOffset + 1];
123                         int b3 = inputBuffer [inputOffset + 2];
124
125                         outputBuffer [outputOffset] = lookup [b1 >> 2];
126                         outputBuffer [outputOffset+1] = lookup [((b1 << 4) & 0x30) | (b2 >> 4)];
127                         outputBuffer [outputOffset+2] = lookup [((b2 << 2) & 0x3c) | (b3 >> 6)];
128                         outputBuffer [outputOffset+3] = lookup [b3 & 0x3f];
129
130                         return this.OutputBlockSize;
131                 }
132
133                 public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
134                 {
135                         if (m_disposed)
136                                 throw new ObjectDisposedException ("TransformFinalBlock");
137                         if (inputBuffer == null)
138                                 throw new ArgumentNullException ("inputBuffer");
139                         if (inputCount < 0)
140                                 throw new ArgumentException ("inputCount", "< 0");
141                         if (inputOffset > inputBuffer.Length - inputCount)
142                                 throw new ArgumentException ("inputCount", Locale.GetText ("Overflow"));
143                         if (inputCount > this.InputBlockSize)
144                                 throw new ArgumentOutOfRangeException (Locale.GetText ("Invalid input length"));
145                         
146                         return InternalTransformFinalBlock (inputBuffer, inputOffset, inputCount);
147                 }
148                 
149                 // Mono System.Convert depends on the ability to process multiple blocks                
150                 internal byte[] InternalTransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
151                 {
152                         int blockLen = this.InputBlockSize;
153                         int outLen = this.OutputBlockSize;
154                         int fullBlocks = inputCount / blockLen;
155                         int tail = inputCount % blockLen;
156
157                         byte[] res = new byte [(inputCount != 0)
158                                                 ? ((inputCount + 2) / blockLen) * outLen
159                                                 : 0];
160
161                         int outputOffset = 0;
162
163                         for (int i = 0; i < fullBlocks; i++) {
164
165                                 TransformBlock (inputBuffer, inputOffset,
166                                                 blockLen, res, outputOffset);
167
168                                 inputOffset += blockLen;
169                                 outputOffset += outLen;
170                         }
171
172                         byte[] lookup = Base64Constants.EncodeTable;
173                         int b1,b2;
174
175                         // When fewer than 24 input bits are available
176                         // in an input group, zero bits are added
177                         // (on the right) to form an integral number of
178                         // 6-bit groups.
179                         switch (tail) {
180                         case 0:
181                                 break;
182                         case 1:
183                                 b1 = inputBuffer [inputOffset];
184                                 res [outputOffset] = lookup [b1 >> 2];
185                                 res [outputOffset+1] = lookup [(b1 << 4) & 0x30];
186
187                                 // padding
188                                 res [outputOffset+2] = (byte)'=';
189                                 res [outputOffset+3] = (byte)'=';
190                                 break;
191
192                         case 2:
193                                 b1 = inputBuffer [inputOffset];
194                                 b2 = inputBuffer [inputOffset + 1];
195                                 res [outputOffset] = lookup [b1 >> 2];
196                                 res [outputOffset+1] = lookup [((b1 << 4) & 0x30) | (b2 >> 4)];
197                                 res [outputOffset+2] = lookup [(b2 << 2) & 0x3c];
198
199                                 // one-byte padding
200                                 res [outputOffset+3] = (byte)'=';
201                                 break;
202                         }
203
204                         return res;
205                 }
206         }
207 }