Friday, April 18, 2014

Implementing SymmetricAlgorithm

In a previous post I answered my own question about how difficult it was to formally implement HashAlgorithm. It turns out you just override a handful of members and you have a class that behaves like the standard classes (MD5, SHA1, etc).

Then I wondered how difficult it was to implement SymmetricAlgorithm and create a class that behaves just like the standard classes (Aes, DES, etc). It turns out to be more tricky and 'fiddly' because there are many interrelated properties and virtual members that are difficult to understand clearly even after reading the MSDN documentation. You also need to implement three classes: the provider and the pair of encrypt/decrypt transform classes.

I set myself the challenge of writing a simple but completely functional set of symmetric algorithm classes that behave just like the standard ones. The internal algorithm is the childish technique of encrypting and decrypting 8-byte blocks by XORing them with a sequence of pseudo-random blocks in either ECB or CBC modes (this algorithm is otherwise known as Snake Oil or Craptography)..

The resulting project, code and test data can be downloaded from the repository. The code is well documented, but there are some points worth clarifying.

CanTransformMultipleBlocks

If you set this property to true, then the transform methods may be passed long buffers which are a multiple of the block size. It is your responsibility to process the blocks sensibly. You may set this property true if you know you can process many blocks more efficiently. For the demonstration code I set this property false and only transform single blocks.

Decrypting the final block

During decryption we need to know when the final block is being transformed so the padding can be stripped from it. Unfortunately, the TransformBlock method does not distinguish the blocks and there is no way of telling which block is the final one. However, TransformFinalBlock is always the last method to run and it's always passed input length zero because there are no trailing bytes. A workaround is to delay writing decrypted blocks until the next one is read, so writing "lags" one call behind the reading. So when we hit the TransformFinalBlock call we know that the "lagged" block is the one to be padding stripped. This "lag" workaround is a nuisance as it clutters the decryption code a bit.

ADDENDUM #1

A few days after finishing the sample code I thought I'd replace the childish pseudo-random encryption with something also simple but more realistic. I searched for TEA (Tiny Encryption Algorithm) and discovered that someone had practically duplicated my code using the XTEA algorithm, except their version lacked C# style, lacked some Dispose calls, and they didn't support CBC mode. See: eTutorials 14.4 Extending the .NET Framework.

ADDENDUM #2

Rather than use one of the TEA variants as the encryption algorithm I decided to use a pseudo-random sequence of 64-bit numbers. Such sequences produce nice random looking ciphertext for my demonstration, but it is well documented that they are worthless for serious cryptography. I didn't actually have a 64-bit random number generator handy, so I took this incredibly simple and effective unsigned 32-bit one-liner from Numerical Recipes in C, Chapter 7:

uint s = seed value;
s = s * 1664525u + 1013904223u;


And I turned it into this:

ulong s = seed value;
s = s * 2770643476691ul + 4354685564936844689ul;


The new 64-bit multiplier is a prime that is near the square of the 32-bit multiplier. The new 64-bit addend is a prime near (Sqrt[5]-2) * 2^64 as suggested by Knuth. I have no theoretical proof that these numbers are good, but running 5 billion iterations through the RandPlot application shows no lattice structure or recycling. Based on this heuristic proof only, I feel that this is a great choice when speed, reliability and simplicity are desirable for random number generation. The low-orders bits of the sequence are of course highly correlated, which could be partly cured by a Bayes-Durham shuffle, but I didn't bother with it for this sample.

I found the prime numbers thanks to Mathematica's RandomPrime function.

Sunday, April 13, 2014

Implementing HashAlgorithm

See the 2022 article titled CRC vs Hash Algorithms for a news update about the CRC and XXHash classes that are now part of the .NET Core class libraries. There is no longer any need to use borrowed code for CRC or SHA3.

I often use the classes MD5 and SHA1 for creating data "fingerprints" or secure hashes. I was pleased to discover recently that authors of the SHA-3 hash algorithm have created a C# implementation and published it as a Nuget package. This means .NET developers can easily use this new NIST certified algorithm.

For years I wondered how hard it was to implement your own hash algorithm and make it look and behave like the standard ones. I vaguely guessed it was tricky due to the large numbers of virtual and abstract base class members. However, after running an experiment one morning I found it's actually quite easy. For my experiment I tried to create a HashAlgorithm implementation that produced a 32-bit output using the CRC-32 algorithm. The resulting class is just this:

public class HashCrc32 : HashAlgorithm
{
  private Crc32 crc;

  public HashCrc32()
  {
     crc = new Crc32();
  }

  protected override void HashCore(byte[] array, int ibStart, int cbSize)
  {
    crc.Update(array, ibStart, cbSize);
  }

  protected override byte[] HashFinal()
  {
    byte[] buff = BitConverter.GetBytes((uint)crc.Value);
    Array.Reverse(buff);
    return buff;
  }

  public override void Initialize()
  {
    crc.Reset();
  }

  public override int HashSize
  {
     get { return 32; }
  }
}

The code for the Crc32 class I use above can be found all over the Internet in various slightly different forms which all seem to work correctly. My own copy can be found in the Orthogonal.Common repository, look under the Basic folder.

As you can see, only a handful of members need to be overridden. The virtual HashCore and HashFinal methods are called by all of the familiar public methods that hash buffers and streams in various ways, so by implementing those virtual methods the class will work correctly in all scenarios.