Click here to Skip to main content
15,885,875 members
Articles / Programming Languages / C#

XCrypt - Encryption and decryption class wrapper

Rate me:
Please Sign up or sign in to vote.
4.86/5 (19 votes)
24 Jun 2013CPOL2 min read 56K   2K   77  
This is an alternative for "XCrypt - Encryption and decryption class wrapper".
/* 
  Copright 2001-2003 by Markus Hahn <markus_hahn@gmx.net>
  All rights reserved. 
  See Documentation for license details.
*/


using System;
using System.Security;
using System.Security.Cryptography;


namespace Blowfish_NET
{
	public class BlowfishAlgorithm : SymmetricAlgorithm, ICryptoTransform
	{
		// in factory mode the Blowfish instances are always null,
		// they get only initialized in transformation mode

		Blowfish m_bf;
		BlowfishCBC m_bfc;
		bool m_blIsEncryptor;


		// factory settings

		RNGCryptoServiceProvider m_rng;

		/// <summary>
		/// constructor
		/// </summary>
		public BlowfishAlgorithm() : base()
		{
			m_bf = null;
			m_bfc = null;

			// FIXME: are we supposed to create a default key and IV?
			IVValue = null;
			KeyValue = null;
			KeySizeValue = Blowfish.MAXKEYLENGTH * 8;

			LegalBlockSizesValue = new KeySizes[1];
			LegalBlockSizesValue[0] = new KeySizes(BlockSize, BlockSize, 8);

			LegalKeySizesValue = new KeySizes[1];
			LegalKeySizesValue[0] = new KeySizes(0, Blowfish.MAXKEYLENGTH * 8, 8);

			ModeValue = CipherMode.ECB;

			m_rng = null;
		}

		BlowfishAlgorithm
			(
			byte[] key,
			byte[] iv,
			bool blCBC,
			bool blIsEncryptor
			)
		{
			if (null == key) GenerateKey(); else Key = key;
 
			if (blCBC)
			{
			    if (null == iv) GenerateIV(); else IV = iv;

				m_bf = null;
				m_bfc = new BlowfishCBC(KeyValue, IVValue);
			}
			else
			{
				m_bf = new Blowfish(KeyValue);
				m_bfc = null;
			}
			
			m_blIsEncryptor = blIsEncryptor;
		}


		/// <summary>
		/// checks if a key is weak (and perhaps shouldn't be used)
		/// </summary>
		/// <param name="key">
		/// the key material to test against
		/// </param>
		/// <returns>
		/// true: key is "weak" / false: key passed the test
		/// </returns>
		public static bool IsWeakKey
			(byte[] key)
		{
			BlowfishAlgorithm bfAlg = new BlowfishAlgorithm(key, null, false, true);
			
			return bfAlg.m_bf.IsWeakKey;
		}


		// SymmetricAlgorithm ...


		/// <summary>
		/// the Blowfish block size, can only be set to the same value
		/// </summary>
		public override int BlockSize
		{
			get
			{
				return Blowfish.BLOCKSIZE * 8;
			}
			set
			{
				// (we only support 64bit block sizes, although Blowfish is
				//  also thinkable with 128bit blocks or even more)
				if (value != Blowfish.BLOCKSIZE * 8)
				{
					throw new CryptographicException("illegal blocksize");
				}
			}
		}

		/// <summary>
		/// the initialization vector, used in the factory
		/// </summary>
		public override byte[] IV
		{
			get
			{
				return IVValue;
			}
			set
			{
				if (null == value) 
				{
					throw new ArgumentNullException();
				}
				if (value.Length != Blowfish.BLOCKSIZE) 
				{
					throw new CryptographicException("illegal IV length");
				}
				IVValue = value;
			}
		}

		/// <summary>
		/// the key, used in the factory
		/// </summary>
		public override byte[] Key
		{
			get
			{
				return KeyValue;
			}
			set
			{
				if (null == value) 
				{
					throw new ArgumentNullException("key cannot be null");
				}
				// FIXME: according to the documentation we don't validate the
				//        key, can this be correct? additionally it is confusing
				//        because the key size can actually be changed, so what
				//        happens then to the key material already set?
				KeyValue = value;
			}
		}

		/// <summary>
		/// the key length (for auto generation only)
		/// </summary>
		public override int KeySize
		{
			get
			{
				return KeySizeValue;
			}
			set
			{
				KeySizes ks = LegalKeySizes[0];
				if ((0 != (value % ks.SkipSize)) ||
					(value > ks.MaxSize) ||
					(value < ks.MinSize))
				{
					throw new CryptographicException("invalid key size");
				}
				KeySizeValue = value;
			}
		}

		public override KeySizes[] LegalBlockSizes
		{
			get
			{
				return LegalBlockSizesValue;
			}
		}
	
		public override KeySizes[] LegalKeySizes
		{
			get
			{
				return LegalKeySizesValue;
			}
		}

		public override CipherMode Mode
		{
			get
			{
				return ModeValue;
			}
			set
			{
				// FIXME: we only support ECB and CBC, so is it ok
				//        to raise an exception even if the requested
				//        mode itself is a valid one?
				if (value != CipherMode.CBC &&
					value != CipherMode.ECB)
				{
					throw new CryptographicException("only ECB and CBC are supported");
				}
				ModeValue = value;
			}
		}

		// the factory methods are just simple mappings to the private constructors

		public override ICryptoTransform CreateEncryptor
			(
			byte[] key,
			byte[] iv
			)
		{
			BlowfishAlgorithm result = new BlowfishAlgorithm(
				key, 
				iv, 
				(CipherMode.CBC == ModeValue),
				true);

			result.Padding = Padding;
			return result;
		}

		public override ICryptoTransform CreateDecryptor
			(
			byte[] key,
			byte[] iv
			)
		{
			BlowfishAlgorithm result = new BlowfishAlgorithm(
				key, 
				iv, 
				(CipherMode.CBC == ModeValue),
				false);

			result.Padding = Padding;
			return result;
		}

		// FIXME: is our generator strategy here good enough?
		//        (and why does the base class actually not offer a decent default?)

		public override void GenerateKey()
		{
			if (null == m_rng) m_rng = new RNGCryptoServiceProvider();
			
			KeyValue = new byte[KeySizeValue / 8];
			m_rng.GetBytes(KeyValue);		
		}

		public override void GenerateIV()
		{
			if (null == m_rng) m_rng = new RNGCryptoServiceProvider();
			
			IVValue = new byte[Blowfish.BLOCKSIZE];
			m_rng.GetBytes(IVValue);		
		}

		// ICryptoTransform ...

		public bool CanReuseTransform
		{
			get
			{
				return true;
			}
		}

		public bool CanTransformMultipleBlocks
		{
			get
			{
				return true;
			}
		}

		public int InputBlockSize
		{
			get
			{
				return Blowfish.BLOCKSIZE;
			}
		}

		public int OutputBlockSize
		{
			get
			{
				return Blowfish.BLOCKSIZE;
			}
		}

		public int TransformBlock
			(
			byte[] bufIn,
			int nOfsIn,
			int nCount,
			byte[] bufOut,
			int nOfsOut
			)
		{
			// NOTE: we assume that the caller understands the meaning of
			//	     this method and that only even byte boundaries are
			//       given, thus we do not cache any data left internally

			int nResult = 0;

			if (null != m_bfc)
			{
				if (m_blIsEncryptor)
				{
					nResult = m_bfc.Encrypt(bufIn, bufOut, nOfsIn, nOfsOut, nCount);
				}
				else
				{
					nResult = m_bfc.Decrypt(bufIn, bufOut, nOfsIn, nOfsOut, nCount);
				}
			}
			else if (null != m_bf)
			{
				if (m_blIsEncryptor)
				{
					nResult = m_bf.Encrypt(bufIn, bufOut, nOfsIn, nOfsOut, nCount);
				}
				else
				{
					nResult = m_bf.Decrypt(bufIn, bufOut, nOfsIn, nOfsOut, nCount);
				}
			}
			else
			{
				nResult = 0;
			}

			return nResult * Blowfish.BLOCKSIZE;
		}

		public byte[] TransformFinalBlock
			(
			byte[] inBuf,
			int nOfs,
			int nCount
			)
		{
			byte[] result;

			if (m_blIsEncryptor)
			{
				// we need to take over whatever we got, pad it with the right
				// scheme and then encrypt or decrypt it

				int nRest = nCount % Blowfish.BLOCKSIZE;

				// NOTE: the padding schemes may result into different data length,
				//       while zero padding just fills up the last block PKCS7 might
				//       need an extra block to store its length information

				int nBufSize = nCount - nRest;
				int nFill;  

				if (PaddingMode.PKCS7 == PaddingValue)
				{
					nBufSize += Blowfish.BLOCKSIZE;
					nFill = Blowfish.BLOCKSIZE - nRest;
				}
				else
				{
					if (0 < nRest) nBufSize += Blowfish.BLOCKSIZE;
					nFill = 0;
				}

				result = new byte[nBufSize];
				Array.Copy(inBuf, nOfs, result, 0, nCount);
            
				for (int nI = nCount; nI < nBufSize; nI++)
				{
					result[nI] = (byte)nFill;
				}

				TransformBlock(result, 0, nBufSize, result, 0);
			}
			else
			{
				byte[] lastBlocks = new byte[nCount];
				if (0 < nCount)
				{
					TransformBlock(inBuf, nOfs, nCount, lastBlocks, 0);
					
					if (PaddingMode.PKCS7 == PaddingValue)
					{
					  nCount -= lastBlocks[nCount - 1];
					}
					
					result = new byte[nCount];
					Array.Copy(lastBlocks, 0, result, 0, nCount);
				}
				else
				{
					// (that will be an empty array)
					result = lastBlocks;
				}			
			}

			return result;
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Software Developer
India India
Vasudevan Deepak Kumar is from Chennai, India who has been in the programming career since 1994, when he was 15 years old. He has his Bachelors of Engineering (in Computer Science and Engineering) from Vellore Engineering College. He also has a MBA in Systems from Alagappa University, Karaikudi, India.
He started his programming career with GWBasic and then in his college was involved in developing programs in Fortran, Cobol, C++. He has been developing in Microsoft technologies like ASP, SQLServer 2000.
His current focus is ASP.NET, C#, VB.NET, PHP, SQL Server and MySQL. In his past-time, he listens to polite Carnatic Music. But the big question is that with his current Todolist backlog, does he get some past time?

Comments and Discussions