2 // System.Security.Cryptography.FromBase64Transform.cs
5 // Sergey Chaban (serge@wildwestsoftware.com)
6 // Sebastien Pouliot <sebastien@ximian.com>
8 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
30 using System.Globalization;
31 using System.Runtime.InteropServices;
33 namespace System.Security.Cryptography {
37 public enum FromBase64TransformMode : int {
39 DoNotIgnoreWhiteSpaces
43 public class FromBase64Transform : ICryptoTransform {
45 private FromBase64TransformMode mode;
46 private byte[] accumulator;
48 private bool m_disposed;
50 private const byte TerminatorByte = ((byte) '=');
52 public FromBase64Transform ()
53 : this (FromBase64TransformMode.IgnoreWhiteSpaces)
57 public FromBase64Transform (FromBase64TransformMode whitespaces)
59 this.mode = whitespaces;
60 accumulator = new byte [4];
65 ~FromBase64Transform ()
70 public bool CanTransformMultipleBlocks {
74 public virtual bool CanReuseTransform {
78 public int InputBlockSize {
82 public int OutputBlockSize {
92 public void Dispose ()
94 void IDisposable.Dispose ()
98 GC.SuppressFinalize (this); // Finalization is now unnecessary
101 protected virtual void Dispose (bool disposing)
105 if (accumulator != null)
106 Array.Clear (accumulator, 0, accumulator.Length);
108 // dispose unmanaged objects
110 // dispose managed objects
117 private byte[] lookupTable;
119 private byte lookup (byte input)
121 if (input >= lookupTable.Length) {
122 throw new FormatException (
123 Locale.GetText ("Invalid character in a Base-64 string."));
126 byte ret = lookupTable [input];
127 if (ret == Byte.MaxValue) {
128 throw new FormatException (
129 Locale.GetText ("Invalid character in a Base-64 string."));
135 private int ProcessBlock (byte[] output, int offset)
138 if (accumulator [3] == TerminatorByte)
140 if (accumulator [2] == TerminatorByte)
143 lookupTable = Base64Constants.DecodeTable;
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);
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));
164 b0 = lookup (accumulator [0]);
165 b1 = lookup (accumulator [1]);
166 output [offset] = (byte) ((b0 << 2) | (b1 >> 4));
173 private void CheckInputParameters (byte[] inputBuffer, int inputOffset, int inputCount)
175 if (inputBuffer == null)
176 throw new ArgumentNullException ("inputBuffer");
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"));
184 throw new OverflowException ("inputCount < 0");
187 public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
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");
198 while (inputCount > 0) {
200 byte b = inputBuffer [inputOffset++];
201 if (mode == FromBase64TransformMode.IgnoreWhiteSpaces) {
202 if (!Char.IsWhiteSpace ((char) b))
203 accumulator [accPtr++] = b;
205 // don't ignore, we'll fail if bad data is provided
206 accumulator [accPtr++] = b;
210 res += ProcessBlock (outputBuffer, outputOffset);
220 public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
223 throw new ObjectDisposedException ("FromBase64Transform");
224 // LAMESPEC: undocumented exceptions
225 CheckInputParameters (inputBuffer, inputOffset, inputCount);
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]))
235 // no more (useful) data
236 if (ws == inputCount)
238 // there may be whitespace after the terminator
239 int k = inputOffset + inputCount - 1;
240 int n = Math.Min (2, inputCount);
242 char c = (char) inputBuffer [k--];
246 } else if (Char.IsWhiteSpace (c)) {
253 if (inputBuffer [inputOffset + inputCount - 1] == TerminatorByte)
255 if (inputBuffer [inputOffset + inputCount - 2] == TerminatorByte)
258 // some terminators could already be in the accumulator
259 if ((inputCount < 4) && (terminator < 2)) {
260 if ((accPtr > 2) && (accumulator [3] == TerminatorByte))
262 if ((accPtr > 1) && (accumulator [2] == TerminatorByte))
266 int count = ((accPtr + inputCount - ws) >> 2) * 3 - terminator;
270 // allocate the "right" ammount (to avoid multiple allocation/copy)
271 byte[] result = new byte [count];
272 TransformBlock (inputBuffer, inputOffset, inputCount, result, 0);