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 // 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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
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...
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
;
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
)
62 BlockSizeByte
= (algo
.BlockSize
>> 3);
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
;
70 workBuff
= new byte [BlockSizeByte
];
71 workout
= new byte [BlockSizeByte
];
74 ~
SymmetricTransform ()
79 void IDisposable
.Dispose ()
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
)
91 // dispose managed object: zeroize and free
92 Array
.Clear (temp
, 0, BlockSizeByte
);
94 Array
.Clear (temp2
, 0, BlockSizeByte
);
101 public virtual bool CanTransformMultipleBlocks
{
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
)
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
)
149 for (int i
= 0; i
< BlockSizeByte
; i
++)
152 Buffer
.BlockCopy (output
, 0, temp
, 0, BlockSizeByte
);
155 Buffer
.BlockCopy (input
, 0, temp2
, 0, BlockSizeByte
);
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
)
167 for (int x
= 0; x
< FeedBackIter
; x
++) {
168 // temp is first initialized with the IV
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
);
178 for (int x
= 0; x
< FeedBackIter
; x
++) {
179 // we do not really decrypt this data!
181 // temp is first initialized with the IV
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");
210 throw new ArgumentOutOfRangeException ("inputOffset", "< 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
)
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
{
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
;
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
;
264 Transform (workBuff
, workout
);
265 Buffer
.BlockCopy (workout
, 0, outputBuffer
, outputOffset
, BlockSizeByte
);
266 outputOffset
+= BlockSizeByte
;
267 total
+= BlockSizeByte
;
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
;
281 Buffer
.BlockCopy (inputBuffer
, offs
, workBuff
, 0, BlockSizeByte
);
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
;
295 if (algo
.Padding
!= PaddingMode
.PKCS7
) {
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
;
306 inputCount
= paddedInput
.Length
;
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
);)
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
);
336 InternalTransformBlock (inputBuffer
, inputOffset
, BlockSizeByte
, res
, outputOffset
);
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
;
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
;
357 inputCount
-= BlockSizeByte
;
361 Transform (workBuff
, workout
);
362 Buffer
.BlockCopy (workout
, 0, res
, outputOffset
, BlockSizeByte
);
363 outputOffset
+= BlockSizeByte
;
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
371 case PaddingMode
.PKCS7
:
372 total
-= res
[total
- 1];
376 // return output without padding
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
);
388 public virtual byte[] TransformFinalBlock (byte[] inputBuffer
, int inputOffset
, int inputCount
)
391 throw new ObjectDisposedException ("Object is disposed");
392 CheckInput (inputBuffer
, inputOffset
, inputCount
);
395 return FinalEncrypt (inputBuffer
, inputOffset
, inputCount
);
397 return FinalDecrypt (inputBuffer
, inputOffset
, inputCount
);