Introduction
This set of pure C# classes implements the cryptographic Tiger hash algorithm. It inherits from .NET's HashAlgorithm
class and so is usable as drop-in in any place where .NET's built-in hash functions are already used. Although not as fast as native machine code generated by C++ or ASM, it's still about 30% faster than the other C# implementations I found (2010).
The program has been tested with a number of different binaries, and most of the test vectors supplied by the creators of Tiger.
Background
A cryptographic hash maps an arbitrary-length data block (e.g., password, file) to a fixed-length hash value as one-way function (= irreversible).
Tiger was designed in 1995, so it had enough time to be well-analyzed, and no successful attacks on full 24-round Tiger are known to date. It is always a good idea to consult Wikipedia or The Hash Function Lounge to check if new vulnerabilities have been found since this article was written.
Its level of security is comparable to RIPEMD-160 or SHA-256. It works on whole 512-bit input data blocks, and produces 192 bits of hash value output. Input data that doesn't align to 512-bit boundaries (as usually is the case) is padded accordingly.
Tiger itself is a 64-bit-optimized algorithm, but still runs well on narrower buses. This implementation does some optimizations that do not drag down the 64-bit performance noticeably but help the 32-bit systems very much.
Some of the functionality (e.g., FileStream
processing) is provided by the .NET Framework through the abstract HashAlgorithm
base class.
Using the code
The class is being created/instantiated directly, but used/called through the abstract HashAlgorithm
class:
using System.Security.Cryptography;
using softwareunion;
HashAlgorithm myhash;
switch(AlgorithmToUse)
{ case "MD5": myhash=new MD5CryptoServiceProvider();
case "TIGER": myhash=new Tiger(); default: throw new NotImplementedException();
}
myhash.ComputeHash( File.OpenRead("myfile.bin") );
byte[] the_hash_result=myhash.HashValue;
There is a version 2 of Tiger that only differs in the padding value of 0x01 being upgraded to 0x80 (there's already a comment for it in the ProcessFinalBlock
function).
Optimizations
- Using
Array.Copy(...)
can also process arrays of different types, and runs with highly optimized native code, while pure C# loops that copy element per element are only as good as the JIT allows them to be.
- Calling functions inside a loop may be comfortable, but is rarely a good idea and is not always optimized by the JIT compiler.
- Loop unrolling often gives good results under C++, but my tests on this code (using .NET 2.0) have shown compact loops giving better performance.
- Converting to
(byte)
is slightly faster than &0xFF
.
- Arrays are fastest when indexed by an
int
.
- Functions with no local variables are called faster.
Room for Future Improvements
- Can the
BitTools.RotLeft
functions be further optimized?
- Can the
Array
type or any other part of the framework provide the functionality of TypeBlindCopy(...)
- that is pack an array of byte
s into an array of ulong
s?
- Can any more temporary gcheap allocations be removed?
History
- 2011-01-21: Tests finished, going public.