/* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*
* Copyright (c) Damien Guard. All rights reserved.
* AlphaFS has written permission from the author to include the CRC code.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;
namespace Alphaleonis.Win32.Security
{
/// Implements an ISO-3309 compliant 64-bit CRC hash algorithm.
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crc")]
internal class Crc64 : HashAlgorithm
{
private static ulong[] Table;
private const ulong Iso3309Polynomial = 0xD800000000000000;
private const ulong DefaultSeed = 0x0;
private readonly ulong[] m_table;
private readonly ulong m_seed;
private ulong m_hash;
/// Initializes a new instance of
public Crc64()
: this(Iso3309Polynomial, DefaultSeed)
{
}
/// Initializes a new instance of
/// The polynomial.
/// The seed.
private Crc64(ulong polynomial, ulong seed)
{
m_table = InitializeTable(polynomial);
this.m_seed = m_hash = seed;
}
///
/// Initializes an implementation of the
/// class.
///
public override void Initialize()
{
m_hash = m_seed;
}
/// When overridden in a derived class, routes data written to the object into the hash algorithm for computing the hash.
/// The input to compute the hash code for..
/// The offset into the byte array from which to begin using data.
/// The number of bytes in the byte array to use as data.
protected override void HashCore(byte[] array, int ibStart, int cbSize)
{
m_hash = CalculateHash(m_hash, m_table, array, ibStart, cbSize);
}
///
/// Finalizes the hash computation after the last data is processed by the cryptographic stream
/// object.
///
///
/// This method finalizes any partial computation and returns the correct hash value for the data
/// stream.
///
protected override byte[] HashFinal()
{
var hashBuffer = UInt64ToBigEndianBytes(m_hash);
HashValue = hashBuffer;
return hashBuffer;
}
/// Gets the size, in bits, of the computed hash code.
/// The size, in bits, of the computed hash code.
public override int HashSize
{
get { return 64; }
}
/// Calculates the hash.
/// The seed.
/// The table.
/// The buffer.
/// The start.
/// The size.
/// The calculated hash.
private static ulong CalculateHash(ulong seed, ulong[] table, IList buffer, int start, int size)
{
var hash = seed;
for (var i = start; i < start + size; i++)
unchecked
{
hash = (hash >> 8) ^ table[(buffer[i] ^ hash) & 0xff];
}
return hash;
}
/// Int 64 to big endian bytes.
/// The value.
/// A byte[].
private static byte[] UInt64ToBigEndianBytes(ulong value)
{
var result = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
Array.Reverse(result);
return result;
}
/// Initializes the table.
/// The polynomial.
/// An ulong[].
private static ulong[] InitializeTable(ulong polynomial)
{
if (polynomial == Iso3309Polynomial && Table != null)
return Table;
var createTable = CreateTable(polynomial);
if (polynomial == Iso3309Polynomial)
Table = createTable;
return createTable;
}
/// Creates a table.
/// The polynomial.
/// A new array of ulong.
private static ulong[] CreateTable(ulong polynomial)
{
var createTable = new ulong[256];
for (var i = 0; i < 256; ++i)
{
var entry = (ulong)i;
for (var j = 0; j < 8; ++j)
if ((entry & 1) == 1)
entry = (entry >> 1) ^ polynomial;
else
entry = entry >> 1;
createTable[i] = entry;
}
return createTable;
}
}
}