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 using System.Security.Cryptography;
15 namespace Mono.Security.Cryptography {
17 // This class implement most of the common code required for symmetric
18 // algorithm transforms, like:
19 // - CipherMode: Builds CBC and CFB on top of (descendant supplied) ECB
20 // - PaddingMode, transform properties, multiple blocks, reuse...
23 // - intialize themselves (like key expansion, ...)
24 // - override the ECB (Electronic Code Book) method which will only be
25 // called using BlockSize byte[] array.
26 internal abstract class SymmetricTransform : ICryptoTransform {
27 protected SymmetricAlgorithm algo;
28 protected bool encrypt;
29 private int BlockSizeByte;
32 private byte[] workBuff;
33 private byte[] workout;
34 private int FeedBackByte;
35 private int FeedBackIter;
36 private bool m_disposed = false;
38 public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV)
42 BlockSizeByte = (algo.BlockSize >> 3);
44 temp = new byte [BlockSizeByte];
45 Array.Copy (rgbIV, 0, temp, 0, BlockSizeByte);
46 temp2 = new byte [BlockSizeByte];
47 FeedBackByte = (algo.FeedbackSize >> 3);
48 FeedBackIter = (int) BlockSizeByte / FeedBackByte;
50 workBuff = new byte [BlockSizeByte];
51 workout = new byte [BlockSizeByte];
54 ~SymmetricTransform ()
59 void IDisposable.Dispose ()
62 GC.SuppressFinalize (this); // Finalization is now unnecessary
65 // MUST be overriden by classes using unmanaged ressources
66 // the override method must call the base class
67 protected void Dispose (bool disposing)
71 // dispose managed object: zeroize and free
72 Array.Clear (temp, 0, BlockSizeByte);
74 Array.Clear (temp2, 0, BlockSizeByte);
81 public virtual bool CanTransformMultipleBlocks {
85 public bool CanReuseTransform {
89 public virtual int InputBlockSize {
90 get { return BlockSizeByte; }
93 public virtual int OutputBlockSize {
94 get { return BlockSizeByte; }
97 // note: Each block MUST be BlockSizeValue in size!!!
98 // i.e. Any padding must be done before calling this method
99 protected void Transform (byte[] input, byte[] output)
118 throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
122 // Electronic Code Book (ECB)
123 protected abstract void ECB (byte[] input, byte[] output);
125 // Cipher-Block-Chaining (CBC)
126 protected virtual void CBC (byte[] input, byte[] output)
129 for (int i = 0; i < BlockSizeByte; i++)
132 Array.Copy (output, 0, temp, 0, BlockSizeByte);
135 Array.Copy (input, 0, temp2, 0, BlockSizeByte);
137 for (int i = 0; i < BlockSizeByte; i++)
138 output[i] ^= temp[i];
139 Array.Copy (temp2, 0, temp, 0, BlockSizeByte);
143 // Cipher-FeedBack (CFB)
144 protected virtual void CFB (byte[] input, byte[] output)
147 for (int x = 0; x < FeedBackIter; x++) {
148 // temp is first initialized with the IV
151 for (int i = 0; i < FeedBackByte; i++)
152 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
153 Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
154 Array.Copy (output, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
158 for (int x = 0; x < FeedBackIter; x++) {
159 // we do not really decrypt this data!
161 // temp is first initialized with the IV
165 Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
166 Array.Copy (input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
167 for (int i = 0; i < FeedBackByte; i++)
168 output[i + x] = (byte)(temp2[i] ^ input[i + x]);
173 // Output-FeedBack (OFB)
174 protected virtual void OFB (byte[] input, byte[] output)
176 throw new NotImplementedException ("OFB not yet supported");
179 // Cipher Text Stealing (CTS)
180 protected virtual void CTS (byte[] input, byte[] output)
182 throw new NotImplementedException ("CTS not yet supported");
185 // this method may get called MANY times so this is the one to optimize
186 public virtual int TransformBlock (byte [] inputBuffer, int inputOffset, int inputCount, byte [] outputBuffer, int outputOffset)
189 throw new ObjectDisposedException ("Object is disposed");
191 if (outputOffset + inputCount > outputBuffer.Length)
192 throw new CryptographicException ("Insufficient output buffer size.");
194 int offs = inputOffset;
197 // this way we don't do a modulo every time we're called
198 // and we may save a division
199 if (inputCount != BlockSizeByte) {
200 if ((inputCount % BlockSizeByte) != 0)
201 throw new CryptographicException ("Invalid input block size.");
203 full = inputCount / BlockSizeByte;
209 for (int i = 0; i < full; i++) {
210 Array.Copy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
211 Transform (workBuff, workout);
212 Array.Copy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
213 offs += BlockSizeByte;
214 outputOffset += BlockSizeByte;
215 total += BlockSizeByte;
221 private byte[] FinalEncrypt (byte[] inputBuffer, int inputOffset, int inputCount)
223 // are there still full block to process ?
224 int full = (inputCount / BlockSizeByte) * BlockSizeByte;
225 int rem = inputCount - full;
228 if (algo.Padding != PaddingMode.PKCS7) {
232 if (algo.Padding == PaddingMode.None)
233 throw new CryptographicException ("invalid block length");
234 // zero padding the input (by adding a block for the partial data)
235 byte[] paddedInput = new byte [full + BlockSizeByte];
236 Buffer.BlockCopy (inputBuffer, inputOffset, paddedInput, 0, inputCount);
237 inputBuffer = paddedInput;
239 inputCount = paddedInput.Length;
244 // we need to add an extra block for padding
245 total += BlockSizeByte;
248 byte[] res = new byte [total];
250 // process all blocks except the last (final) block
251 while (total > BlockSizeByte) {
252 TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
253 inputOffset += BlockSizeByte;
254 total -= BlockSizeByte;
257 // now we only have a single last block to encrypt
258 if (algo.Padding == PaddingMode.PKCS7) {
259 byte padding = (byte) (BlockSizeByte - rem);
260 for (int i = res.Length; --i >= (res.Length - padding);)
262 Array.Copy (inputBuffer, inputOffset, res, full, rem);
263 // the last padded block will be transformed in-place
264 TransformBlock (res, full, BlockSizeByte, res, full);
267 TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
272 private byte[] FinalDecrypt (byte [] inputBuffer, int inputOffset, int inputCount)
274 if ((inputCount % BlockSizeByte) > 0)
275 throw new CryptographicException ("Invalid input block size.");
277 int total = inputCount;
278 byte[] res = new byte [total];
279 while (inputCount > 0) {
280 TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
281 inputOffset += BlockSizeByte;
282 inputCount -= BlockSizeByte;
285 switch (algo.Padding) {
286 case PaddingMode.None: // nothing to do - it's a multiple of block size
287 case PaddingMode.Zeros: // nothing to do - user must unpad himself
289 case PaddingMode.PKCS7:
290 total -= res [total - 1];
294 // return output without padding
296 byte[] data = new byte [total];
297 Array.Copy (res, 0, data, 0, total);
298 // zeroize decrypted data (copy with padding)
299 Array.Clear (res, 0, res.Length);
306 public virtual byte [] TransformFinalBlock (byte [] inputBuffer, int inputOffset, int inputCount)
309 throw new ObjectDisposedException ("Object is disposed");
312 return FinalEncrypt (inputBuffer, inputOffset, inputCount);
314 return FinalDecrypt (inputBuffer, inputOffset, inputCount);