2 // Mono.Security.Cryptography.SymmetricTransform implementation
5 // Thomas Neidhart (tome@sbox.tugraz.at)
6 // Sebastien Pouliot <sebastien@ximian.com>
8 // Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 // (C) 2004 Novell (http://www.novell.com)
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Security.Cryptography;
38 namespace Mono.Security.Cryptography {
40 // This class implement most of the common code required for symmetric
41 // algorithm transforms, like:
42 // - CipherMode: Builds CBC and CFB on top of (descendant supplied) ECB
43 // - PaddingMode, transform properties, multiple blocks, reuse...
46 // - intialize themselves (like key expansion, ...)
47 // - override the ECB (Electronic Code Book) method which will only be
48 // called using BlockSize byte[] array.
49 internal abstract class SymmetricTransform : ICryptoTransform {
50 protected SymmetricAlgorithm algo;
51 protected bool encrypt;
52 private int BlockSizeByte;
55 private byte[] workBuff;
56 private byte[] workout;
57 private int FeedBackByte;
58 private int FeedBackIter;
59 private bool m_disposed = false;
60 private bool lastBlock;
62 public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV)
66 BlockSizeByte = (algo.BlockSize >> 3);
68 temp = new byte [BlockSizeByte];
69 Buffer.BlockCopy (rgbIV, 0, temp, 0, BlockSizeByte);
70 temp2 = new byte [BlockSizeByte];
71 FeedBackByte = (algo.FeedbackSize >> 3);
72 FeedBackIter = (int) BlockSizeByte / FeedBackByte;
74 workBuff = new byte [BlockSizeByte];
75 workout = new byte [BlockSizeByte];
78 ~SymmetricTransform ()
83 void IDisposable.Dispose ()
86 GC.SuppressFinalize (this); // Finalization is now unnecessary
89 // MUST be overriden by classes using unmanaged ressources
90 // the override method must call the base class
91 protected void Dispose (bool disposing)
95 // dispose managed object: zeroize and free
96 Array.Clear (temp, 0, BlockSizeByte);
98 Array.Clear (temp2, 0, BlockSizeByte);
105 public virtual bool CanTransformMultipleBlocks {
109 public bool CanReuseTransform {
110 get { return false; }
113 public virtual int InputBlockSize {
114 get { return BlockSizeByte; }
117 public virtual int OutputBlockSize {
118 get { return BlockSizeByte; }
121 // note: Each block MUST be BlockSizeValue in size!!!
122 // i.e. Any padding must be done before calling this method
123 protected void Transform (byte[] input, byte[] output)
142 throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
146 // Electronic Code Book (ECB)
147 protected abstract void ECB (byte[] input, byte[] output);
149 // Cipher-Block-Chaining (CBC)
150 protected virtual void CBC (byte[] input, byte[] output)
153 for (int i = 0; i < BlockSizeByte; i++)
156 Buffer.BlockCopy (output, 0, temp, 0, BlockSizeByte);
159 Buffer.BlockCopy (input, 0, temp2, 0, BlockSizeByte);
161 for (int i = 0; i < BlockSizeByte; i++)
162 output[i] ^= temp[i];
163 Buffer.BlockCopy (temp2, 0, temp, 0, BlockSizeByte);
167 // Cipher-FeedBack (CFB)
168 protected virtual void CFB (byte[] input, byte[] output)
171 for (int x = 0; x < FeedBackIter; x++) {
172 // temp is first initialized with the IV
175 for (int i = 0; i < FeedBackByte; i++)
176 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
177 Buffer.BlockCopy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
178 Buffer.BlockCopy (output, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
182 for (int x = 0; x < FeedBackIter; x++) {
183 // we do not really decrypt this data!
185 // temp is first initialized with the IV
189 Buffer.BlockCopy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
190 Buffer.BlockCopy (input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
191 for (int i = 0; i < FeedBackByte; i++)
192 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
197 // Output-FeedBack (OFB)
198 protected virtual void OFB (byte[] input, byte[] output)
200 throw new NotImplementedException ("OFB not yet supported");
203 // Cipher Text Stealing (CTS)
204 protected virtual void CTS (byte[] input, byte[] output)
206 throw new NotImplementedException ("CTS not yet supported");
209 private void CheckInput (byte[] inputBuffer, int inputOffset, int inputCount)
211 if (inputBuffer == null)
212 throw new ArgumentNullException ("inputBuffer");
214 throw new ArgumentOutOfRangeException ("inputOffset", "< 0");
216 throw new ArgumentOutOfRangeException ("inputCount", "< 0");
217 // ordered to avoid possible integer overflow
218 if (inputOffset > inputBuffer.Length - inputCount)
219 throw new ArgumentException ("inputBuffer", Locale.GetText ("Overflow"));
222 // this method may get called MANY times so this is the one to optimize
223 public virtual int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
226 throw new ObjectDisposedException ("Object is disposed");
227 CheckInput (inputBuffer, inputOffset, inputCount);
228 // check output parameters
229 if (outputBuffer == null)
230 throw new ArgumentNullException ("outputBuffer");
231 if (outputOffset < 0)
232 throw new ArgumentOutOfRangeException ("outputOffset", "< 0");
233 // ordered to avoid possible integer overflow
234 if (outputOffset > outputBuffer.Length - inputCount)
235 throw new ArgumentException ("outputBuffer", Locale.GetText ("Overflow"));
237 return InternalTransformBlock (inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
240 private bool KeepLastBlock {
242 return ((!encrypt) && (algo.Mode != CipherMode.ECB) && (algo.Padding != PaddingMode.None));
246 private int InternalTransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
248 int offs = inputOffset;
251 // this way we don't do a modulo every time we're called
252 // and we may save a division
253 if (inputCount != BlockSizeByte) {
254 if ((inputCount % BlockSizeByte) != 0)
255 throw new CryptographicException ("Invalid input block size.");
257 full = inputCount / BlockSizeByte;
268 Transform (workBuff, workout);
269 Buffer.BlockCopy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
270 outputOffset += BlockSizeByte;
271 total += BlockSizeByte;
275 for (int i = 0; i < full; i++) {
276 Buffer.BlockCopy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
277 Transform (workBuff, workout);
278 Buffer.BlockCopy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
279 offs += BlockSizeByte;
280 outputOffset += BlockSizeByte;
281 total += BlockSizeByte;
285 Buffer.BlockCopy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
292 private byte[] FinalEncrypt (byte[] inputBuffer, int inputOffset, int inputCount)
294 // are there still full block to process ?
295 int full = (inputCount / BlockSizeByte) * BlockSizeByte;
296 int rem = inputCount - full;
299 if (algo.Padding != PaddingMode.PKCS7) {
303 if (algo.Padding == PaddingMode.None)
304 throw new CryptographicException ("invalid block length");
305 // zero padding the input (by adding a block for the partial data)
306 byte[] paddedInput = new byte [full + BlockSizeByte];
307 Buffer.BlockCopy (inputBuffer, inputOffset, paddedInput, 0, inputCount);
308 inputBuffer = paddedInput;
310 inputCount = paddedInput.Length;
315 // we need to add an extra block for padding
316 total += BlockSizeByte;
319 byte[] res = new byte [total];
320 int outputOffset = 0;
322 // process all blocks except the last (final) block
323 while (total > BlockSizeByte) {
324 InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
325 inputOffset += BlockSizeByte;
326 outputOffset += BlockSizeByte;
327 total -= BlockSizeByte;
330 // now we only have a single last block to encrypt
331 if (algo.Padding == PaddingMode.PKCS7) {
332 byte padding = (byte) (BlockSizeByte - rem);
333 for (int i = res.Length; --i >= (res.Length - padding);)
335 Buffer.BlockCopy (inputBuffer, inputOffset, res, full, rem);
336 // the last padded block will be transformed in-place
337 InternalTransformBlock (res, full, BlockSizeByte, res, full);
340 InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
345 private byte[] FinalDecrypt (byte[] inputBuffer, int inputOffset, int inputCount)
347 if ((inputCount % BlockSizeByte) > 0)
348 throw new CryptographicException ("Invalid input block size.");
350 int total = inputCount;
352 total += BlockSizeByte;
354 byte[] res = new byte [total];
355 int outputOffset = 0;
357 while (inputCount > 0) {
358 int len = InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
359 inputOffset += BlockSizeByte;
361 inputCount -= BlockSizeByte;
365 Transform (workBuff, workout);
366 Buffer.BlockCopy (workout, 0, res, outputOffset, BlockSizeByte);
367 outputOffset += BlockSizeByte;
371 switch (algo.Padding) {
372 case PaddingMode.None: // nothing to do - it's a multiple of block size
373 case PaddingMode.Zeros: // nothing to do - user must unpad himself
375 case PaddingMode.PKCS7:
376 total -= res [total - 1];
380 // return output without padding
382 byte[] data = new byte [total];
383 Buffer.BlockCopy (res, 0, data, 0, total);
384 // zeroize decrypted data (copy with padding)
385 Array.Clear (res, 0, res.Length);
392 public virtual byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
395 throw new ObjectDisposedException ("Object is disposed");
396 CheckInput (inputBuffer, inputOffset, inputCount);
399 return FinalEncrypt (inputBuffer, inputOffset, inputCount);
401 return FinalDecrypt (inputBuffer, inputOffset, inputCount);