// // System.Security.Cryptography.ToBase64Transform // // Author: // Sergey Chaban (serge@wildwestsoftware.com) // // (C) 2004 Novell (http://www.novell.com) // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.Globalization; using System.Runtime.InteropServices; namespace System.Security.Cryptography { #if NET_2_0 [ComVisible (true)] #endif public class ToBase64Transform : ICryptoTransform { private bool m_disposed; public ToBase64Transform () { } ~ToBase64Transform () { Dispose (false); } public bool CanTransformMultipleBlocks { get { return false; } } public virtual bool CanReuseTransform { get { return true; } } public int InputBlockSize { get { return 3; } } public int OutputBlockSize { get { return 4; } } public void Clear() { Dispose (true); } void IDisposable.Dispose () { Dispose (true); GC.SuppressFinalize (this); // Finalization is now unnecessary } protected virtual void Dispose (bool disposing) { if (!m_disposed) { // dispose unmanaged objects if (disposing) { // dispose managed objects } m_disposed = true; } } // LAMESPEC: It's not clear from docs what should be happening // here if inputCount > InputBlockSize. It just "Converts the // specified region of the specified byte array" and that's all. public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { if (m_disposed) throw new ObjectDisposedException ("TransformBlock"); if (inputBuffer == null) throw new ArgumentNullException ("inputBuffer"); if (outputBuffer == null) throw new ArgumentNullException ("outputBuffer"); if (inputCount < 0) throw new ArgumentException ("inputCount", "< 0"); if (inputCount > inputBuffer.Length) throw new ArgumentException ("inputCount", Locale.GetText ("Overflow")); if (inputOffset < 0) throw new ArgumentOutOfRangeException ("inputOffset", "< 0"); // ordered to avoid possible integer overflow if (inputOffset > inputBuffer.Length - inputCount) throw new ArgumentException ("inputOffset", Locale.GetText ("Overflow")); // ordered to avoid possible integer overflow #if NET_2_0 if (outputOffset < 0) throw new ArgumentOutOfRangeException ("outputOffset", "< 0"); if (outputOffset > outputBuffer.Length - inputCount) throw new ArgumentException ("outputOffset", Locale.GetText ("Overflow")); #else if ((outputOffset < 0) || (outputOffset > outputBuffer.Length - inputCount)) throw new IndexOutOfRangeException ("outputOffset"); #endif /// To match MS implementation // if (inputCount != this.InputBlockSize) // throw new CryptographicException (Locale.GetText ("Invalid input length")); byte[] lookup = Base64Constants.EncodeTable; int b1 = inputBuffer [inputOffset]; int b2 = inputBuffer [inputOffset + 1]; int b3 = inputBuffer [inputOffset + 2]; outputBuffer [outputOffset] = lookup [b1 >> 2]; outputBuffer [outputOffset+1] = lookup [((b1 << 4) & 0x30) | (b2 >> 4)]; outputBuffer [outputOffset+2] = lookup [((b2 << 2) & 0x3c) | (b3 >> 6)]; outputBuffer [outputOffset+3] = lookup [b3 & 0x3f]; return this.OutputBlockSize; } public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) { if (m_disposed) throw new ObjectDisposedException ("TransformFinalBlock"); if (inputBuffer == null) throw new ArgumentNullException ("inputBuffer"); if (inputCount < 0) throw new ArgumentException ("inputCount", "< 0"); if (inputOffset > inputBuffer.Length - inputCount) throw new ArgumentException ("inputCount", Locale.GetText ("Overflow")); if (inputCount > this.InputBlockSize) throw new ArgumentOutOfRangeException (Locale.GetText ("Invalid input length")); return InternalTransformFinalBlock (inputBuffer, inputOffset, inputCount); } // Mono System.Convert depends on the ability to process multiple blocks internal byte[] InternalTransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount) { int blockLen = this.InputBlockSize; int outLen = this.OutputBlockSize; int fullBlocks = inputCount / blockLen; int tail = inputCount % blockLen; byte[] res = new byte [(inputCount != 0) ? ((inputCount + 2) / blockLen) * outLen : 0]; int outputOffset = 0; for (int i = 0; i < fullBlocks; i++) { TransformBlock (inputBuffer, inputOffset, blockLen, res, outputOffset); inputOffset += blockLen; outputOffset += outLen; } byte[] lookup = Base64Constants.EncodeTable; int b1,b2; // When fewer than 24 input bits are available // in an input group, zero bits are added // (on the right) to form an integral number of // 6-bit groups. switch (tail) { case 0: break; case 1: b1 = inputBuffer [inputOffset]; res [outputOffset] = lookup [b1 >> 2]; res [outputOffset+1] = lookup [(b1 << 4) & 0x30]; // padding res [outputOffset+2] = (byte)'='; res [outputOffset+3] = (byte)'='; break; case 2: b1 = inputBuffer [inputOffset]; b2 = inputBuffer [inputOffset + 1]; res [outputOffset] = lookup [b1 >> 2]; res [outputOffset+1] = lookup [((b1 << 4) & 0x30) | (b2 >> 4)]; res [outputOffset+2] = lookup [(b2 << 2) & 0x3c]; // one-byte padding res [outputOffset+3] = (byte)'='; break; } return res; } } }