[Mono-dev] Question about TransformFinalBlock with SymmetricAlgorithms and CBC

Sebastien Pouliot sebastien.pouliot at gmail.com
Fri Jan 30 15:07:09 EST 2009


On Fri, 2009-01-30 at 14:51 -0500, David Wolinsky wrote:
> Sebastien Pouliot wrote:
> > On Fri, 2009-01-30 at 13:59 -0500, David Wolinsky wrote:
> >   
> >> Sebastien Pouliot wrote:
> >>     
> >>> On Fri, 2009-01-30 at 12:27 -0500, David Wolinsky wrote:
> >>>   
> >>>       
> >>>> Hey guys,
> >>>>
> >>>> Just wanted to let you know a behavioral difference between .Net and 
> >>>> Mono and potentially get advice from you.  The method 
> >>>> SA.CreateEncryptor.TransformFinalBlock() differs on the two platforms.  
> >>>> Specifically, Mono appears to continue from where the last one ended, 
> >>>> whereas .Net repeats itself (i.e. the IV is the same IV you inserted 
> >>>> when the ICryptoTransform was created).
> >>>>
> >>>> Below is a sample.
> >>>>
> >>>> using System;
> >>>> using System.Security.Cryptography;
> >>>>
> >>>> public class tdes_test {
> >>>>   public static void Main() {
> >>>>     RijndaelManaged rm = new RijndaelManaged();
> >>>>     byte[] key = new byte[rm.KeySize / 8];
> >>>>     for(int i = 0; i < key.Length; i++) {
> >>>>       key[i] = (byte) i;
> >>>>     }
> >>>>     byte[] iv = new byte[rm.BlockSize / 8];
> >>>>     for(int i = 0; i < iv.Length; i++) {
> >>>>       iv[i] = (byte) i;
> >>>>     }
> >>>>     ICryptoTransform encryptor = rm.CreateEncryptor(key, iv);
> >>>>     byte[] data = new byte[111];
> >>>>     for(int i = 0; i < data.Length; i++) {
> >>>>       data[i] = (byte) i;
> >>>>     }
> >>>>
> >>>>     byte[] encrypted_data = encryptor.TransformFinalBlock(data, 0, 
> >>>> data.Length);
> >>>>     for(int i = 0; i < encrypted_data.Length; i++) {
> >>>>       Console.Write(encrypted_data[i]);
> >>>>     }
> >>>>     Console.WriteLine("\n");
> >>>>     encrypted_data = encryptor.TransformFinalBlock(data, 0, data.Length);
> >>>>     for(int i = 0; i < encrypted_data.Length; i++) {
> >>>>       Console.Write(encrypted_data[i]);
> >>>>     }
> >>>>   }
> >>>> }
> >>>>
> >>>>
> >>>> We're currently using this on a datagram security system and on Mono 
> >>>> (not sure if .Net is the same) creation of Encryptors and Decryptors is 
> >>>> expensive.  Any thoughts or suggestions?
> >>>>     
> >>>>         
> >>> Your code should always* look at ICryptoTransform.CanReuseTransform
> >>> before reusing a transform. If false then it cannot be reused.
> >>>
> >>> * since you should be using the factory methods to create ciphers
> >>> (e.g. Rijndael.Create) and you can't be sure what exact class the
> >>> runtime will instantiate (nor it's behavior).
> >>>
> >>> Sebastien
> >>>
> >>>
> >>>   
> >>>       
> >> Thanks for the reply.
> >>
> >> In our deployed code, I actually use SymmetricAlgorithm.Create(string), 
> >> and it is a cleaner API, this was just a test.
> >>     
> >
> > Great :)
> >
> >   
> >> Now on to the "ReuseTransform" part.  We'd expect the same result in 
> >> both .Net and Mono, but it is different.  .Net returns true, while Mono 
> >> returns false.  (This is both with and without Rijndael.Create).  Am I 
> >> wrong in expecting the same behavior from both platforms?
> >>     
> >
> > Yes and no. We generally tend to do the exact same (good or bad) thing -
> > but this one is an exception because you should not expect any specific
> > value, either true or false *even* on MS platforms alone. That's
> > because:
> >
> > (a) it could change in the future (e.g. the default Rijndael could be
> > provided by a CSP or CNG provider in future frameworks). This is less
> > likely for RijndaelManaged but you can't be sure this is what you'll
> > receive when using the factory methods; 
> >
> > 	or more likely (i.e. it could occur today)
> >
> > (b) someone could have an alternative implementation (e.g. native or
> > hardware acceleration) and have its machine.config redirect the
> > "default" Rijndael implementation to some custom code.
> >
> > In both case you cannot assume if you can, or can't, reuse a transform
> > without checking the property. So yes Mono has a different default value
> > - but this is not the real (or the major) issue.
> >
> > Sebastien
> >
> >
> >   
> Thanks for the wealth of information and making it clear.  I agree this 
> is not the issue, obviously we cannot use the TransformFinalBlock 
> without a) creating decryptors and encryptors each time or b) force 
> users to use a frozen mono version.  So back to the original question...
> 
> "We're currently using this on a datagram security system and on Mono 
> (not sure if .Net is the same) creation of Encryptors and Decryptors is 
> expensive. Any thoughts or suggestions?"

First make sure if this is the creation of transforms that is costly
(something that you could save by resetting the transform) or if it is
the cipher initialization (that you can't save anyway by resetting the
transform). It's likely a bit of both... but that will tell you what
options is best.

> Could we yank out the guts of Mono's FinalEncrypt/FinalDecrypt and just 
> use the TransformBlock instead of the internal calls without worries?  

Yes you can. All the code is available, MIT licensed. The existing API
has limitations (it's not very memory friendly) so you could adapt the
code to use an API without any memory allocations (beside the initial
buffers) which could help performance - at least if this fits your
application.

> Is there another approach that I am blind to?

You could also use machine.config support to use a native implementation
of AES (I assume this is the Rijndael mode you're using anyway).
You'll find hand-optimized assembly implementation of most existing CPU.

In this case you'll need to write a C# wrapper around it and ensure it's
registered in machine.config (if you want to replace the default).
Alternatively you could have your C# code do a fallback to the existing
managed code if the native library is unavailable (and show a warning to
the end-users).

Sebastien



More information about the Mono-devel-list mailing list