Click here to Skip to main content
Click here to Skip to main content

A C# implementation of the Twofish cipher

By , 17 Jul 2002
 

Preface

This article is about using the .NET framework to create an encryption provider and integrate it using the same mechanism provided by the .NET platform. This article is not about the Twofish cipher itself but is used as an example cipher that can can be integrated in such a manner.

Introduction

The .NET framework supports various encryption providers such as the AES winner Rijndael. But it is possible to use the same framework to add custom encryption providers and use them in the same manner as the .NET provided ones. It is not necessary to just use it for encryption as the same framework can also be used for any form of encoding mechanism such as compression or MIME encoding. Also these transformations can be connected together via the streams so that it is possible to cascade these transformations i.e. memory -> compress -> encrypt -> encode, in a very simple manner. This technique will be familiar to people who have used Crypto++. For this purpose the .NET framework provides a base class SymmetricAlgorithm and an interface ICryptoTransform.

Investigation

To investigate how the .NET framework used the SymmetricAlgorithm class and ICryptoTransform interface I created a simple class XOR which does a byte by byte eXclusiveOR on a block of data. A very basic and very poor encryption system but it at least lets one work out if we are using the supplied classes and interfaces correctly. I have also included this in the install but it is just a bunch of methods/properties and lots of trace statements.

Discovery

byte[] ICryptoTransform.TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount); - This method does not like when you return null, throws an exception, when there is no data to return. You will usually have this case when this method is called but inputCount is 0, instead you have to return new byte[0]. This is not documented in the help files (yet).

public virtual SymmetricAlgorithm.CipherMode Mode {get; set;} - The framework does not use this property itself to implement the various cipher modes - you must read this property when you are transforming data and act accordingly.

Twofish

Now armed with the new found knowledge I proceeded to implement the Twofish cipher in C#. I based my implementation on the reference C implementation of the Twofish cipher which can be found at Counterpane Internet Security as I do not think the optimised C implementation would port as well. I have tested the code so that it works in EBC mode and I have also implemented CBC mode as well.

Cascade

As I mentioned before it is possible to cascade these transforms such that with one call you can compress -> encrypt -> encode. In the install I have shown how one may cascade the Twofish cipher and the .NET provided Base64 transforms FromBase64Transform and ToBase64Transform. I haven't shown the compression step as I have yet to implement that transform.

Twofish fish = new Twofish();
System.IO.MemoryStream ms = new System.IO.MemoryStream();

// create an encoder
ICryptoTransform encode = new ToBase64Transform();

//create Twofish Encryptor from this instance
ICryptoTransform encrypt = fish.CreateEncryptor( Key, IV);
// both Key and IV are byte[] types 

// we have to work backwards defining the last link in the chain first
CryptoStream cryptostreamEncode = new CryptoStream( ms, encode, 
                                                    CryptoStreamMode.Write);
CryptoStream cryptostream = new CryptoStream( cryptostreamEncode, encrypt, 
                                              CryptoStreamMode.Write);
// or we could do this as we don't need to use cryptostreamEncode
CryptoStream cryptostream = new 
  CryptoStream(new CryptoStream( ms,encode, CryptoStreamMode.Write), 
  encrypt, CryptoStreamMode.Write);

Outstanding Issues

  • I have not created any random key or IV mechanism that would normally be implemented in the GenerateIV() and GenerateKey() overrides.
  • Need further testing to test the CBC mode and to add other cipher modes.
  • Integrate a compression algorithm into a class that supports ICryptoTransform interface
  • Optimise the code. As I mentioned before I am not too sure how to go about optimising C# code so any tips appreciated.
  • The uninstall does not remove any produced files due to compilation.

History

First revision - 17 July 2002

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Shaun Wilde
Software Developer (Senior) MYOB
Australia Australia
Member
All articles are supplied as-is, as a howto on a particular task that worked for me in the past. None of the articles are supposed to be out-of-the-box freeware controls and nor should they be treated as such. Caveat emptor.
 
Now living and working in Australia, trying to be involved in the local .NET and Agile communities when I can.
 
I spend a good chunk of my spare time building OpenCover and maintaining PartCover both of which are Code Coverage utilities for .NET.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralBug in MonomemberMember 194556810 Oct '09 - 7:37 
In method "TransformFinalBlock" you expect that the inputBuffer is already padded with '\0'.
 
This is NOT correct in Mono's CryptoStream. It contains the bytes of the last block.
 
I added a manual zeropadding of the inputBuffer.
 
[....]
outputBuffer = new byte[16]; // blocksize
uint[] x = new uint[4];
 
if (inputCount < 16)
{
for (var n = inputCount; n < 16; n++)
{
inputBuffer[n] = 0;
}
}
 
// load it up
for (int i = 0; i < 4; i++) // should be okay as we have already said to pad with zeros
[...]
 
Not a very clean way [changing input variables], but it works for me.
GeneralRe: Bug in MonomemberShaun Wilde26 Oct '10 - 10:46 
Bearing in mind this article was written in 2002 and Mono was then used as a shortened variation of the word monochrome - I am not surprised. Smile | :)
 
Thanks for the update
I'll be more enthusiastic about encouraging thinking outside the box when there's evidence of any thinking going on inside it. - pTerry
BizSquawk

GeneralMore or less bug in TransformBlockmemberSvante Seleborg12 Oct '07 - 4:21 
The code works fine as such, but there's a little bug in TransformBlock.
 
This implementation will only transform exactly one block.
 
It is mitigated by the fact that CanTransformMultipleBlocks is false - but then it should throw when an attempt is made to transform multiple blocks. The comment is also misleading there, as it implies that it would work if set to true. It won't.
GeneralRe: More or less bug in TransformBlockmemberShaun Wilde12 Oct '07 - 5:06 
What is the bug?
 
I'll be more enthusiastic about encouraging thinking outside the box when there's evidence of any thinking going on inside it. - pTerry
www.many-monkeys.com

GeneralRe: More or less bug in TransformBlockmemberSvante Seleborg12 Oct '07 - 5:15 
I'm sorry, I thought that was clear.
 
You should throw an ArgumentException in TransformBlock if an attempt is made to transform more than one block, since it does not support it. The current behavior is to transform exactly one block and ignore any that may also be specified in the call. That's a bug.
 
Also, not a bug, but the comment on CanTransformMultipleBlocks is misleading, when seen with the code. It implies that all one has to do is change the value of canTransformMultipleBlocks to 'true' to get that functionality.
GeneralRe: More or less bug in TransformBlockmemberShaun Wilde13 Oct '07 - 2:58 
I see maybe the Property does not work as one would expect but the API is not well documented even after all these years and I made a guess as to what it does; maybe it means that the algorithm can handle all the data and does not need to be repeatably called by the API. Who knows?
 
The following - keeping the Property untouched (i.e. false) does encrypt the whole array (in this case 64K of data - and can also be decrypted)
 
Twofish fish = new Twofish();
 
fish.Mode = CipherMode.CBC;
 
System.IO.MemoryStream ms = new System.IO.MemoryStream();
 
//create Twofish Encryptor from this instance
ICryptoTransform encrypt = fish.CreateEncryptor(Key, iv);
 
//Create Crypto Stream that transforms file stream using twofish encryption
CryptoStream cryptostream = new CryptoStream(ms, encrypt, CryptoStreamMode.Write);
 
byte[] data = File.ReadAllBytes("Soap Bubbles.bmp");
 
//write out Twofish encrypted stream
cryptostream.Write(data, 0, data.Length);
 
cryptostream.Close();
 
byte[] bytOut = ms.ToArray();

 
Maybe you have an example of how it doesn't work for you - or on what way is it wrong.
 
I'll be more enthusiastic about encouraging thinking outside the box when there's evidence of any thinking going on inside it. - pTerry
www.many-monkeys.com

GeneralRe: More or less bug in TransformBlockmemberSvante Seleborg13 Oct '07 - 9:57 
The following should throw an ArgumentException, or possibly an ArgumentOutOfRangeException:
 
byte[] sessionKeyBytes = new byte[32];
keyDecryptor.TransformBlock(encryptedSessionKeyK, 0, 32, sessionKeyBytes, 0);
 
Most implementations of ICryptoTransform will support multiple blocks at once, so it's easy to assume that it does - and when this happens your implementation should throw. Or better yet - support multiple blocks (and then you change the result of CanTransformMultipleBlocks.
GeneralRe: More or less bug in TransformBlockmemberShaun Wilde13 Oct '07 - 11:35 
Ahh - I see you are trying to use the code outside the scope provided - If support for multiple blocks is needed I suggest this can be an exercise for the reader. If you need multiple blocks why not use the stream technique as demonstrated?
 
Since I have no need for using the code in the manner suggested, I have no need to rework it at this moment in time.
 
In the end this was an investigation into how the API works using Twofish as an example. The comments about multiple blocks was a hangover from another algorithm I implemented for a customer that did support multiple blocks but couldn't use in the example (I checked in my source control - another example of why developers should rework their comments as much as their code - Smile | :) ). So the value of CanTransfromMultipleBlocks is actually correct.
 
Sorry for the confusion.

 
I'll be more enthusiastic about encouraging thinking outside the box when there's evidence of any thinking going on inside it. - pTerry
www.many-monkeys.com

GeneralRe: More or less bug in TransformBlockmemberSvante Seleborg14 Oct '07 - 0:53 
I think we have a difference of viewpoint on writing software. This is a bug. Either you support multiple blocks, or you don't. Either is fine. If you don't, you don't - and then an attempt to use that functionality is an error and must be reported as an error, not silently ignored.
 
Of course I can live with it as it is, iterate external to your code, fix your code or do any number of things.
 
I just wanted to give you feed-back on a bug, since one of the points of open source software is that you get such things for free.
 
There's nothing wrong with having bugs found in your software. All software has bugs, two kinds in fact. Those you know about, and those you don't. What is not so good, is to not fix bugs that are known. But it's entirely up to you, and I meant no criticism.
 
Thank you for posting your code, I've had use of it, and the core algorithm appears to work as advertised.
GeneralNice topicmemberVahe Karamian13 Feb '07 - 14:20 
Hi, I like the article you have written. Do you have more documentation for the code?
 
I have little patience with scientists who take a
board of wood, look for its thinnest part, and drill a
great number of holes where drilling is easy.
 
-Albert Einstein

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 18 Jul 2002
Article Copyright 2002 by Shaun Wilde
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid