Archive for CRC tag

Calculating CRC-32 in C# and .NET

August 2006 – May 2010 .NET (, , , ) • 79,088 views • 20 responses

Just a few days ago I found myself needing to calculate a CRC-32 in .NET. With so many facilities available I was a little shocked that there was nothing built-in to do it so knocked up something myself.

Because unsigned ints aren’t CLS compliant it won’t play well with VB.Net and implementing the HashAlgorithm might lead people to believe it’s suitable for signing – it isn’t. CRC-32′s are only any good for checksums along the lines of WinZIP, RAR etc. and certainly shouldn’t come near a password and instead consider SHA-512 or similar.

As well as using it as a HashAlgorithm with block processing you can also access the static method Compute although there is an overhead with every call building the table with that. If neither option suits you needs cut ‘n splice it to something that does.

Updated: Fix for repeated calls, cache look-up table for the default polynomial and additional static Compute methods with defaults.

using System;
using System.Security.Cryptography;

public class Crc32 : HashAlgorithm {
    public const UInt32 DefaultPolynomial = 0xedb88320;
    public const UInt32 DefaultSeed = 0xffffffff;

    private UInt32 hash;
    private UInt32 seed;
    private UInt32[] table;
    private static UInt32[] defaultTable;

    public Crc32() {
        table = InitializeTable(DefaultPolynomial);
        seed = DefaultSeed;
        Initialize();
    }

    public Crc32(UInt32 polynomial, UInt32 seed) {
        table = InitializeTable(polynomial);
        this.seed = seed;
        Initialize();
    }

    public override void Initialize() {
        hash = seed;
    }

    protected override void HashCore(byte[] buffer, int start, int length) {
        hash = CalculateHash(table, hash, buffer, start, length);
    }

    protected override byte[] HashFinal() {
        byte[] hashBuffer = UInt32ToBigEndianBytes(~hash);
        this.HashValue = hashBuffer;
        return hashBuffer;
    }

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

    public static UInt32 Compute(byte[] buffer) {
        return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
    }

    public static UInt32 Compute(UInt32 seed, byte[] buffer) {
        return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
    }

    public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer) {
        return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
    }

    private static UInt32[] InitializeTable(UInt32 polynomial) {
        if (polynomial == DefaultPolynomial && defaultTable != null)
            return defaultTable;

        UInt32[] createTable = new UInt32[256];
        for (int i = 0; i < 256; i++) {
            UInt32 entry = (UInt32)i;
            for (int j = 0; j < 8; j++)
                if ((entry & 1) == 1)
                    entry = (entry >> 1) ^ polynomial;
                else
                    entry = entry >> 1;
            createTable[i] = entry;
        }

        if (polynomial == DefaultPolynomial)
            defaultTable = createTable;

        return createTable;
    }

    private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size) {
        UInt32 crc = seed;
        for (int i = start; i < size; i++)
            unchecked {
                crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
            }
        return crc;
    }

    private byte[] UInt32ToBigEndianBytes(UInt32 x) {
        return new byte[] {
			(byte)((x >> 24) & 0xff),
			(byte)((x >> 16) & 0xff),
			(byte)((x >> 8) & 0xff),
			(byte)(x & 0xff)
		};
    }
}

To compute the hash for a file simply:

Crc32 crc32 = new Crc32();
String hash = String.Empty;

using (FileStream fs = File.Open("c:\\myfile.txt", FileMode.Open))
	foreach (byte b in crc32.ComputeHash(fs)) hash += b.ToString("x2").ToLower();

Console.WriteLine("CRC-32 is {0}", hash);

[)amien