(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / corlib / Mono.Security.Cryptography / SymmetricTransform.cs
blobe9b972b7a3db791752786cfc1a6fb287e9056b0a
1 //
2 // Mono.Security.Cryptography.SymmetricTransform implementation
3 //
4 // Authors:
5 // Thomas Neidhart (tome@sbox.tugraz.at)
6 // Sebastien Pouliot <sebastien@ximian.com>
7 //
8 // Portions (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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.
31 using System;
32 using System.Security.Cryptography;
34 namespace Mono.Security.Cryptography {
36 // This class implement most of the common code required for symmetric
37 // algorithm transforms, like:
38 // - CipherMode: Builds CBC and CFB on top of (descendant supplied) ECB
39 // - PaddingMode, transform properties, multiple blocks, reuse...
41 // Descendants MUST:
42 // - intialize themselves (like key expansion, ...)
43 // - override the ECB (Electronic Code Book) method which will only be
44 // called using BlockSize byte[] array.
45 internal abstract class SymmetricTransform : ICryptoTransform {
46 protected SymmetricAlgorithm algo;
47 protected bool encrypt;
48 private int BlockSizeByte;
49 private byte[] temp;
50 private byte[] temp2;
51 private byte[] workBuff;
52 private byte[] workout;
53 private int FeedBackByte;
54 private int FeedBackIter;
55 private bool m_disposed = false;
56 private bool lastBlock;
58 public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV)
60 algo = symmAlgo;
61 encrypt = encryption;
62 BlockSizeByte = (algo.BlockSize >> 3);
63 // mode buffers
64 temp = new byte [BlockSizeByte];
65 Buffer.BlockCopy (rgbIV, 0, temp, 0, BlockSizeByte);
66 temp2 = new byte [BlockSizeByte];
67 FeedBackByte = (algo.FeedbackSize >> 3);
68 FeedBackIter = (int) BlockSizeByte / FeedBackByte;
69 // transform buffers
70 workBuff = new byte [BlockSizeByte];
71 workout = new byte [BlockSizeByte];
74 ~SymmetricTransform ()
76 Dispose (false);
79 void IDisposable.Dispose ()
81 Dispose (true);
82 GC.SuppressFinalize (this); // Finalization is now unnecessary
85 // MUST be overriden by classes using unmanaged ressources
86 // the override method must call the base class
87 protected void Dispose (bool disposing)
89 if (!m_disposed) {
90 if (disposing) {
91 // dispose managed object: zeroize and free
92 Array.Clear (temp, 0, BlockSizeByte);
93 temp = null;
94 Array.Clear (temp2, 0, BlockSizeByte);
95 temp2 = null;
97 m_disposed = true;
101 public virtual bool CanTransformMultipleBlocks {
102 get { return true; }
105 public bool CanReuseTransform {
106 get { return false; }
109 public virtual int InputBlockSize {
110 get { return BlockSizeByte; }
113 public virtual int OutputBlockSize {
114 get { return BlockSizeByte; }
117 // note: Each block MUST be BlockSizeValue in size!!!
118 // i.e. Any padding must be done before calling this method
119 protected void Transform (byte[] input, byte[] output)
121 switch (algo.Mode) {
122 case CipherMode.ECB:
123 ECB (input, output);
124 break;
125 case CipherMode.CBC:
126 CBC (input, output);
127 break;
128 case CipherMode.CFB:
129 CFB (input, output);
130 break;
131 case CipherMode.OFB:
132 OFB (input, output);
133 break;
134 case CipherMode.CTS:
135 CTS (input, output);
136 break;
137 default:
138 throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
142 // Electronic Code Book (ECB)
143 protected abstract void ECB (byte[] input, byte[] output);
145 // Cipher-Block-Chaining (CBC)
146 protected virtual void CBC (byte[] input, byte[] output)
148 if (encrypt) {
149 for (int i = 0; i < BlockSizeByte; i++)
150 temp[i] ^= input[i];
151 ECB (temp, output);
152 Buffer.BlockCopy (output, 0, temp, 0, BlockSizeByte);
154 else {
155 Buffer.BlockCopy (input, 0, temp2, 0, BlockSizeByte);
156 ECB (input, output);
157 for (int i = 0; i < BlockSizeByte; i++)
158 output[i] ^= temp[i];
159 Buffer.BlockCopy (temp2, 0, temp, 0, BlockSizeByte);
163 // Cipher-FeedBack (CFB)
164 protected virtual void CFB (byte[] input, byte[] output)
166 if (encrypt) {
167 for (int x = 0; x < FeedBackIter; x++) {
168 // temp is first initialized with the IV
169 ECB (temp, temp2);
171 for (int i = 0; i < FeedBackByte; i++)
172 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
173 Buffer.BlockCopy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
174 Buffer.BlockCopy (output, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
177 else {
178 for (int x = 0; x < FeedBackIter; x++) {
179 // we do not really decrypt this data!
180 encrypt = true;
181 // temp is first initialized with the IV
182 ECB (temp, temp2);
183 encrypt = false;
185 Buffer.BlockCopy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
186 Buffer.BlockCopy (input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
187 for (int i = 0; i < FeedBackByte; i++)
188 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
193 // Output-FeedBack (OFB)
194 protected virtual void OFB (byte[] input, byte[] output)
196 throw new CryptographicException ("OFB isn't supported by the framework");
199 // Cipher Text Stealing (CTS)
200 protected virtual void CTS (byte[] input, byte[] output)
202 throw new CryptographicException ("CTS isn't supported by the framework");
205 private void CheckInput (byte[] inputBuffer, int inputOffset, int inputCount)
207 if (inputBuffer == null)
208 throw new ArgumentNullException ("inputBuffer");
209 if (inputOffset < 0)
210 throw new ArgumentOutOfRangeException ("inputOffset", "< 0");
211 if (inputCount < 0)
212 throw new ArgumentOutOfRangeException ("inputCount", "< 0");
213 // ordered to avoid possible integer overflow
214 if (inputOffset > inputBuffer.Length - inputCount)
215 throw new ArgumentException ("inputBuffer", Locale.GetText ("Overflow"));
218 // this method may get called MANY times so this is the one to optimize
219 public virtual int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
221 if (m_disposed)
222 throw new ObjectDisposedException ("Object is disposed");
223 CheckInput (inputBuffer, inputOffset, inputCount);
224 // check output parameters
225 if (outputBuffer == null)
226 throw new ArgumentNullException ("outputBuffer");
227 if (outputOffset < 0)
228 throw new ArgumentOutOfRangeException ("outputOffset", "< 0");
229 // ordered to avoid possible integer overflow
230 if (outputOffset > outputBuffer.Length - inputCount)
231 throw new ArgumentException ("outputBuffer", Locale.GetText ("Overflow"));
233 return InternalTransformBlock (inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
236 private bool KeepLastBlock {
237 get {
238 return ((!encrypt) && (algo.Mode != CipherMode.ECB) && (algo.Padding != PaddingMode.None));
242 private int InternalTransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
244 int offs = inputOffset;
245 int full;
247 // this way we don't do a modulo every time we're called
248 // and we may save a division
249 if (inputCount != BlockSizeByte) {
250 if ((inputCount % BlockSizeByte) != 0)
251 throw new CryptographicException ("Invalid input block size.");
253 full = inputCount / BlockSizeByte;
255 else
256 full = 1;
258 if (KeepLastBlock)
259 full--;
261 int total = 0;
263 if (lastBlock) {
264 Transform (workBuff, workout);
265 Buffer.BlockCopy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
266 outputOffset += BlockSizeByte;
267 total += BlockSizeByte;
268 lastBlock = false;
271 for (int i = 0; i < full; i++) {
272 Buffer.BlockCopy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
273 Transform (workBuff, workout);
274 Buffer.BlockCopy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
275 offs += BlockSizeByte;
276 outputOffset += BlockSizeByte;
277 total += BlockSizeByte;
280 if (KeepLastBlock) {
281 Buffer.BlockCopy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
282 lastBlock = true;
285 return total;
288 private byte[] FinalEncrypt (byte[] inputBuffer, int inputOffset, int inputCount)
290 // are there still full block to process ?
291 int full = (inputCount / BlockSizeByte) * BlockSizeByte;
292 int rem = inputCount - full;
293 int total = full;
295 if (algo.Padding != PaddingMode.PKCS7) {
296 if (inputCount == 0)
297 return new byte [0];
298 if (rem != 0) {
299 if (algo.Padding == PaddingMode.None)
300 throw new CryptographicException ("invalid block length");
301 // zero padding the input (by adding a block for the partial data)
302 byte[] paddedInput = new byte [full + BlockSizeByte];
303 Buffer.BlockCopy (inputBuffer, inputOffset, paddedInput, 0, inputCount);
304 inputBuffer = paddedInput;
305 inputOffset = 0;
306 inputCount = paddedInput.Length;
307 total = inputCount;
310 else {
311 // we need to add an extra block for padding
312 total += BlockSizeByte;
315 byte[] res = new byte [total];
316 int outputOffset = 0;
318 // process all blocks except the last (final) block
319 while (total > BlockSizeByte) {
320 InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
321 inputOffset += BlockSizeByte;
322 outputOffset += BlockSizeByte;
323 total -= BlockSizeByte;
326 // now we only have a single last block to encrypt
327 if (algo.Padding == PaddingMode.PKCS7) {
328 byte padding = (byte) (BlockSizeByte - rem);
329 for (int i = res.Length; --i >= (res.Length - padding);)
330 res [i] = padding;
331 Buffer.BlockCopy (inputBuffer, inputOffset, res, full, rem);
332 // the last padded block will be transformed in-place
333 InternalTransformBlock (res, full, BlockSizeByte, res, full);
335 else
336 InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
338 return res;
341 private byte[] FinalDecrypt (byte[] inputBuffer, int inputOffset, int inputCount)
343 if ((inputCount % BlockSizeByte) > 0)
344 throw new CryptographicException ("Invalid input block size.");
346 int total = inputCount;
347 if (lastBlock)
348 total += BlockSizeByte;
350 byte[] res = new byte [total];
351 int outputOffset = 0;
353 while (inputCount > 0) {
354 int len = InternalTransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, outputOffset);
355 inputOffset += BlockSizeByte;
356 outputOffset += len;
357 inputCount -= BlockSizeByte;
360 if (lastBlock) {
361 Transform (workBuff, workout);
362 Buffer.BlockCopy (workout, 0, res, outputOffset, BlockSizeByte);
363 outputOffset += BlockSizeByte;
364 lastBlock = false;
367 switch (algo.Padding) {
368 case PaddingMode.None: // nothing to do - it's a multiple of block size
369 case PaddingMode.Zeros: // nothing to do - user must unpad himself
370 break;
371 case PaddingMode.PKCS7:
372 total -= res [total - 1];
373 break;
376 // return output without padding
377 if (total > 0) {
378 byte[] data = new byte [total];
379 Buffer.BlockCopy (res, 0, data, 0, total);
380 // zeroize decrypted data (copy with padding)
381 Array.Clear (res, 0, res.Length);
382 return data;
384 else
385 return new byte [0];
388 public virtual byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
390 if (m_disposed)
391 throw new ObjectDisposedException ("Object is disposed");
392 CheckInput (inputBuffer, inputOffset, inputCount);
394 if (encrypt)
395 return FinalEncrypt (inputBuffer, inputOffset, inputCount);
396 else
397 return FinalDecrypt (inputBuffer, inputOffset, inputCount);