Merge pull request #799 from kebby/master
[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-2005 Novell, Inc (http://www.novell.com)
9 // Copyright 2013 Xamarin Inc. (http://www.xamarin.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Globalization;
32 using System.Runtime.InteropServices;
33
34 namespace System.Security.Cryptography {
35
36         static class Base64Helper {
37
38                 private const int inputBlockSize = 3;
39                 private const int outputBlockSize = 4;
40
41                 internal static void TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
42                 {
43                         byte[] lookup = Base64Constants.EncodeTable;
44                         
45                         int b1 = inputBuffer [inputOffset];
46                         int b2 = inputBuffer [inputOffset + 1];
47                         int b3 = inputBuffer [inputOffset + 2];
48                         
49                         outputBuffer [outputOffset] = lookup [b1 >> 2];
50                         outputBuffer [outputOffset+1] = lookup [((b1 << 4) & 0x30) | (b2 >> 4)];
51                         outputBuffer [outputOffset+2] = lookup [((b2 << 2) & 0x3c) | (b3 >> 6)];
52                         outputBuffer [outputOffset+3] = lookup [b3 & 0x3f];
53                 }
54
55                 // Mono System.Convert depends on the ability to process multiple blocks                
56                 internal static byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
57                 {
58                         int blockLen = inputBlockSize;
59                         int outLen = outputBlockSize;
60                         int fullBlocks = inputCount / blockLen;
61                         int tail = inputCount % blockLen;
62                         
63                         byte[] res = new byte [(inputCount != 0)
64                                                ? ((inputCount + 2) / blockLen) * outLen
65                                                : 0];
66                         
67                         int outputOffset = 0;
68                         
69                         for (int i = 0; i < fullBlocks; i++) {
70                                 TransformBlock (inputBuffer, inputOffset, blockLen, res, outputOffset);
71                                 inputOffset += blockLen;
72                                 outputOffset += outLen;
73                         }
74                         
75                         byte[] lookup = Base64Constants.EncodeTable;
76                         int b1,b2;
77                         
78                         // When fewer than 24 input bits are available
79                         // in an input group, zero bits are added
80                         // (on the right) to form an integral number of
81                         // 6-bit groups.
82                         switch (tail) {
83                         case 0:
84                                 break;
85                         case 1:
86                                 b1 = inputBuffer [inputOffset];
87                                 res [outputOffset] = lookup [b1 >> 2];
88                                 res [outputOffset+1] = lookup [(b1 << 4) & 0x30];
89                                 
90                                 // padding
91                                 res [outputOffset+2] = (byte)'=';
92                                 res [outputOffset+3] = (byte)'=';
93                                 break;
94                                 
95                         case 2:
96                                 b1 = inputBuffer [inputOffset];
97                                 b2 = inputBuffer [inputOffset + 1];
98                                 res [outputOffset] = lookup [b1 >> 2];
99                                 res [outputOffset+1] = lookup [((b1 << 4) & 0x30) | (b2 >> 4)];
100                                 res [outputOffset+2] = lookup [(b2 << 2) & 0x3c];
101                                 
102                                 // one-byte padding
103                                 res [outputOffset+3] = (byte)'=';
104                                 break;
105                         }
106                         
107                         return res;
108                 }
109         }
110
111         [ComVisible (true)]
112         public class ToBase64Transform : ICryptoTransform {
113
114                 private const int inputBlockSize = 3;
115                 private const int outputBlockSize = 4;
116                 private bool m_disposed;
117
118                 public ToBase64Transform ()
119                 {
120                 }
121
122                 ~ToBase64Transform ()
123                 {
124                         Dispose (false);
125                 }
126
127                 public bool CanTransformMultipleBlocks {
128                         get { return false; }
129                 }
130
131                 public virtual bool CanReuseTransform {
132                         get { return true; }
133                 }
134
135                 public int InputBlockSize {
136                         get { return inputBlockSize; }
137                 }
138
139                 public int OutputBlockSize {
140                         get { return outputBlockSize; }
141                 }
142
143                 public void Clear() 
144                 {
145                         Dispose (true);
146                 }
147
148 #if NET_4_0
149                 public void Dispose ()
150 #else
151                 void IDisposable.Dispose () 
152 #endif
153                 {
154                         Dispose (true);
155                         GC.SuppressFinalize (this);  // Finalization is now unnecessary
156                 }
157
158                 protected virtual void Dispose (bool disposing) 
159                 {
160                         if (!m_disposed) {
161                                 // dispose unmanaged objects
162                                 if (disposing) {
163                                         // dispose managed objects
164                                 }
165                                 m_disposed = true;
166                         }
167                 }
168
169                 // LAMESPEC: It's not clear from docs what should be happening 
170                 // here if inputCount > InputBlockSize. It just "Converts the 
171                 // specified region of the specified byte array" and that's all.
172                 public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
173                 {
174                         if (m_disposed)
175                                 throw new ObjectDisposedException ("TransformBlock");
176                         if (inputBuffer == null)
177                                 throw new ArgumentNullException ("inputBuffer");
178                         if (outputBuffer == null)
179                                 throw new ArgumentNullException ("outputBuffer");
180                         if (inputCount < 0)
181                                 throw new ArgumentException ("inputCount", "< 0");
182                         if (inputCount > inputBuffer.Length)
183                                 throw new ArgumentException ("inputCount", Locale.GetText ("Overflow"));
184                         if (inputOffset < 0)
185                                 throw new ArgumentOutOfRangeException ("inputOffset", "< 0");
186                         // ordered to avoid possible integer overflow
187                         if (inputOffset > inputBuffer.Length - inputCount)
188                                 throw new ArgumentException ("inputOffset", Locale.GetText ("Overflow"));
189                         // ordered to avoid possible integer overflow
190                         if (outputOffset < 0)
191                                 throw new ArgumentOutOfRangeException ("outputOffset", "< 0");
192                         if (outputOffset > outputBuffer.Length - inputCount)
193                                 throw new ArgumentException ("outputOffset", Locale.GetText ("Overflow"));
194 /// To match MS implementation
195 //                      if (inputCount != this.InputBlockSize)
196 //                              throw new CryptographicException (Locale.GetText ("Invalid input length"));
197
198                         Base64Helper.TransformBlock (inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
199                         return this.OutputBlockSize;
200                 }
201
202                 public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
203                 {
204                         if (m_disposed)
205                                 throw new ObjectDisposedException ("TransformFinalBlock");
206                         if (inputBuffer == null)
207                                 throw new ArgumentNullException ("inputBuffer");
208                         if (inputCount < 0)
209                                 throw new ArgumentException ("inputCount", "< 0");
210                         if (inputOffset > inputBuffer.Length - inputCount)
211                                 throw new ArgumentException ("inputCount", Locale.GetText ("Overflow"));
212                         if (inputCount > this.InputBlockSize)
213                                 throw new ArgumentOutOfRangeException (Locale.GetText ("Invalid input length"));
214                         
215                         return Base64Helper.TransformFinalBlock (inputBuffer, inputOffset, inputCount);
216                 }
217         }
218 }