From abff1b6edea5a5a1e0e79d70f74179439ec54e11 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Mon, 5 Oct 2015 18:58:13 +0100 Subject: [PATCH] * DiscImageChef/Checksums/MD5Context.cs: * DiscImageChef.Checksums/MD5Context.cs: * DiscImageChef/Checksums/CDChecksums.cs: * DiscImageChef.Checksums/ReedSolomon.cs: * DiscImageChef.Checksums/SHA1Context.cs: * DiscImageChef/Checksums/ReedSolomon.cs: * DiscImageChef/Checksums/SHA1Context.cs: * DiscImageChef.Checksums/CDChecksums.cs: * DiscImageChef/Checksums/CRC64Context.cs: * DiscImageChef/Checksums/CRC32Context.cs: * DiscImageChef/Checksums/CRC16Context.cs: * DiscImageChef.Checksums/CRC64Context.cs: * DiscImageChef.Checksums/CRC32Context.cs: * DiscImageChef.Checksums/CRC16Context.cs: * DiscImageChef/Checksums/SHA256Context.cs: * DiscImageChef.Checksums/SHA512Context.cs: * DiscImageChef.Checksums/SHA384Context.cs: * DiscImageChef/Checksums/SHA384Context.cs: * DiscImageChef/Checksums/SHA512Context.cs: * DiscImageChef.Checksums/SHA256Context.cs: * DiscImageChef.Checksums/Adler32Context.cs: * DiscImageChef/Checksums/SpamSumContext.cs: * DiscImageChef/Checksums/Adler32Context.cs: * DiscImageChef.Checksums/SpamSumContext.cs: * DiscImageChef.Checksums/FletcherContext.cs: * DiscImageChef/Checksums/FletcherContext.cs: * DiscImageChef.Checksums/RIPEMD160Context.cs: * DiscImageChef/Checksums/RIPEMD160Context.cs: * DiscImageChef.Checksums/Properties/AssemblyInfo.cs: * DiscImageChef.Checksums/DiscImageChef.Checksums.csproj: Move checksums to a separate library. * DiscImageChef/Swapping.cs: * DiscImageChef/PrintHex.cs: * DiscImageChef/ArrayFill.cs: * DiscImageChef/DateHandlers.cs: * DiscImageChef/StringHandlers.cs: * DiscImageChef.Helpers/Swapping.cs: * DiscImageChef.Helpers/PrintHex.cs: * DiscImageChef/DiscImageChef.csproj: * DiscImageChef.Helpers/ArrayFill.cs: * DiscImageChef.Helpers/DateHandlers.cs: * DiscImageChef/BigEndianBitConverter.cs: * DiscImageChef.Helpers/StringHandlers.cs: * DiscImageChef/EndianAwareBinaryReader.cs: * DiscImageChef.Helpers/BigEndianBitConverter.cs: * DiscImageChef.Helpers/EndianAwareBinaryReader.cs: * DiscImageChef.Helpers/Properties/AssemblyInfo.cs: * DiscImageChef.Helpers/DiscImageChef.Helpers.csproj: Move helpers to a separate library. * DiscImageChef.sln: Move helpers to a separate library. Move checksums to a separate library. --- Adler32Context.cs | 193 ++++++++++ CDChecksums.cs | 615 +++++++++++++++++++++++++++++++ CRC16Context.cs | 239 ++++++++++++ CRC32Context.cs | 240 ++++++++++++ CRC64Context.cs | 239 ++++++++++++ ChangeLog | 20 + DiscImageChef.Checksums.csproj | 60 +++ FletcherContext.cs | 458 +++++++++++++++++++++++ MD5Context.cs | 162 ++++++++ Properties/AssemblyInfo.cs | 27 ++ RIPEMD160Context.cs | 162 ++++++++ ReedSolomon.cs | 650 +++++++++++++++++++++++++++++++++ SHA1Context.cs | 162 ++++++++ SHA256Context.cs | 162 ++++++++ SHA384Context.cs | 162 ++++++++ SHA512Context.cs | 162 ++++++++ SpamSumContext.cs | 534 +++++++++++++++++++++++++++ 17 files changed, 4247 insertions(+) create mode 100644 Adler32Context.cs create mode 100644 CDChecksums.cs create mode 100644 CRC16Context.cs create mode 100644 CRC32Context.cs create mode 100644 CRC64Context.cs create mode 100644 ChangeLog create mode 100644 DiscImageChef.Checksums.csproj create mode 100644 FletcherContext.cs create mode 100644 MD5Context.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 RIPEMD160Context.cs create mode 100644 ReedSolomon.cs create mode 100644 SHA1Context.cs create mode 100644 SHA256Context.cs create mode 100644 SHA384Context.cs create mode 100644 SHA512Context.cs create mode 100644 SpamSumContext.cs diff --git a/Adler32Context.cs b/Adler32Context.cs new file mode 100644 index 000000000..b12323457 --- /dev/null +++ b/Adler32Context.cs @@ -0,0 +1,193 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : Adler32Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Implements an Adler-32 algorithm. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2015 Claunia.com +****************************************************************************/ +//$Id$ +using System; +using System.Text; +using System.IO; + +namespace DiscImageChef.Checksums +{ + public class Adler32Context + { + UInt16 sum1, sum2; + const UInt16 AdlerModule = 65521; + + /// + /// Initializes the Adler-32 sums + /// + public void Init() + { + sum1 = 1; + sum2 = 0; + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + for (int i = 0; i < len; i++) + { + sum1 = (ushort)((sum1 + data[i]) % AdlerModule); + sum2 = (ushort)((sum2 + sum1) % AdlerModule); + } + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + UInt32 finalSum = (uint)((sum2 << 16) | sum1); + return BigEndianBitConverter.GetBytes(finalSum); + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + UInt32 finalSum = (uint)((sum2 << 16) | sum1); + StringBuilder adlerOutput = new StringBuilder(); + + for (int i = 0; i < BigEndianBitConverter.GetBytes(finalSum).Length; i++) + { + adlerOutput.Append(BigEndianBitConverter.GetBytes(finalSum)[i].ToString("x2")); + } + + return adlerOutput.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public static byte[] File(string filename) + { + byte[] hash; + File(filename, out hash); + return hash; + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + UInt16 localSum1, localSum2; + UInt32 finalSum; + + localSum1 = 1; + localSum2 = 0; + + localSum1 = (ushort)((localSum1 + fileStream.ReadByte()) % AdlerModule); + localSum2 = (ushort)((localSum2 + localSum1) % AdlerModule); + + finalSum = (uint)((localSum2 << 16) | localSum1); + + hash = BitConverter.GetBytes(finalSum); + + StringBuilder adlerOutput = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + adlerOutput.Append(hash[i].ToString("x2")); + } + + return adlerOutput.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + UInt16 localSum1, localSum2; + UInt32 finalSum; + + localSum1 = 1; + localSum2 = 0; + + for (int i = 0; i < len; i++) + { + localSum1 = (ushort)((localSum1 + data[i]) % AdlerModule); + localSum2 = (ushort)((localSum2 + localSum1) % AdlerModule); + } + + finalSum = (uint)((localSum2 << 16) | localSum1); + + hash = BitConverter.GetBytes(finalSum); + + StringBuilder adlerOutput = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + adlerOutput.Append(hash[i].ToString("x2")); + } + + return adlerOutput.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + diff --git a/CDChecksums.cs b/CDChecksums.cs new file mode 100644 index 000000000..110e7bf1b --- /dev/null +++ b/CDChecksums.cs @@ -0,0 +1,615 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : CDChecksums.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Checks a CD checksum + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +ECC algorithm from ECM (C) 2002-2011 Neill Corlett +****************************************************************************/ +//$Id$ +using System; + +namespace DiscImageChef.Checksums +{ + public static class CDChecksums + { + static byte[] ECC_F_Table; + static byte[] ECC_B_Table; + const UInt32 CDCRC32Poly = 0xD8018001; + const UInt32 CDCRC32Seed = 0x00000000; + + public static bool? CheckCDSector(byte[] buffer) + { + switch (buffer.Length) + { + case 2448: + { + byte[] subchannel = new byte[96]; + byte[] channel = new byte[2352]; + + Array.Copy(buffer, 0, channel, 0, 2352); + Array.Copy(buffer, 2352, subchannel, 0, 96); + + bool? channelStatus = CheckCDSectorChannel(channel); + bool? subchannelStatus = CheckCDSectorSubChannel(subchannel); + bool? status = null; + + if (channelStatus == null && subchannelStatus == null) + status = null; + if (channelStatus == false || subchannelStatus == false) + status = false; + if (channelStatus == null && subchannelStatus == true) + status = true; + if (channelStatus == true && subchannelStatus == null) + status = true; + if (channelStatus == true && subchannelStatus == true) + status = true; + + return status; + } + case 2352: + return CheckCDSectorChannel(buffer); + default: + return null; + } + } + + static void ECCInit() + { + ECC_F_Table = new byte[256]; + ECC_B_Table = new byte[256]; + + for (UInt32 i = 0; i < 256; i++) + { + UInt32 j = (uint)((i << 1) ^ ((i & 0x80) == 0x80 ? 0x11D : 0)); + ECC_F_Table[i] = (byte)j; + ECC_B_Table[i ^ j] = (byte)i; + } + } + + static bool CheckECC( + byte[] address, + byte[] data, + UInt32 major_count, + UInt32 minor_count, + UInt32 major_mult, + UInt32 minor_inc, + byte[] ecc + ) + { + UInt32 size = major_count * minor_count; + UInt32 major; + for (major = 0; major < major_count; major++) + { + UInt32 index = (major >> 1) * major_mult + (major & 1); + byte ecc_a = 0; + byte ecc_b = 0; + UInt32 minor; + for (minor = 0; minor < minor_count; minor++) + { + byte temp; + if (index < 4) + { + temp = address[index]; + } + else + { + temp = data[index - 4]; + } + index += minor_inc; + if (index >= size) + { + index -= size; + } + ecc_a ^= temp; + ecc_b ^= temp; + ecc_a = ECC_F_Table[ecc_a]; + } + ecc_a = ECC_B_Table[ECC_F_Table[ecc_a] ^ ecc_b]; + if ( + ecc[major] != (ecc_a) || + ecc[major + major_count] != (ecc_a ^ ecc_b)) + { + return false; + } + } + return true; + } + + static bool? CheckCDSectorChannel(byte[] channel) + { + ECCInit(); + + if ( + channel[0x000] == 0x00 && // sync (12 bytes) + channel[0x001] == 0xFF && + channel[0x002] == 0xFF && + channel[0x003] == 0xFF && + channel[0x004] == 0xFF && + channel[0x005] == 0xFF && + channel[0x006] == 0xFF && + channel[0x007] == 0xFF && + channel[0x008] == 0xFF && + channel[0x009] == 0xFF && + channel[0x00A] == 0xFF && + channel[0x00B] == 0x00) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Data sector, address {0:X2}:{1:X2}:{2:X2}", channel[0x00C], channel[0x00D], channel[0x00E]); + + if (channel[0x00F] == 0x00) // mode (1 byte) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 0 sector at address {0:X2}:{1:X2}:{2:X2}", channel[0x00C], channel[0x00D], channel[0x00E]); + for (int i = 0x010; i < 0x930; i++) + { + if (channel[i] != 0x00) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 0 sector with error at address: {0:X2}:{1:X2}:{2:X2}", channel[0x00C], channel[0x00D], channel[0x00E]); + return false; + } + } + return true; + } + else if (channel[0x00F] == 0x01) // mode (1 byte) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 1 sector at address {0:X2}:{1:X2}:{2:X2}", channel[0x00C], channel[0x00D], channel[0x00E]); + + if (channel[0x814] != 0x00 || // reserved (8 bytes) + channel[0x815] != 0x00 || + channel[0x816] != 0x00 || + channel[0x817] != 0x00 || + channel[0x818] != 0x00 || + channel[0x819] != 0x00 || + channel[0x81A] != 0x00 || + channel[0x81B] != 0x00) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 1 sector with data in reserved bytes at address: {0:X2}:{1:X2}:{2:X2}", channel[0x00C], channel[0x00D], channel[0x00E]); + return false; + } + + byte[] address = new byte[4]; + byte[] data = new byte[2060]; + byte[] data2 = new byte[2232]; + byte[] ecc_p = new byte[172]; + byte[] ecc_q = new byte[104]; + + Array.Copy(channel, 0x0C, address, 0, 4); + Array.Copy(channel, 0x0C, data, 0, 2060); + Array.Copy(channel, 0x0C, data2, 0, 2232); + Array.Copy(channel, 0x81C, ecc_p, 0, 172); + Array.Copy(channel, 0x8C8, ecc_q, 0, 104); + + bool FailedECC_P = CheckECC(address, data, 86, 24, 2, 86, ecc_p); + bool FailedECC_Q = CheckECC(address, data2, 52, 43, 86, 88, ecc_q); + + if (FailedECC_P) + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 1 sector at address: {0:X2}:{1:X2}:{2:X2}, fails ECC P check", channel[0x00C], channel[0x00D], channel[0x00E]); + if (FailedECC_Q) + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 1 sector at address: {0:X2}:{1:X2}:{2:X2}, fails ECC Q check", channel[0x00C], channel[0x00D], channel[0x00E]); + + if (FailedECC_P || FailedECC_Q) + return false; + + byte[] SectorForCheck = new byte[0x810]; + UInt32 StoredEDC = BitConverter.ToUInt32(channel, 0x810); + byte[] CalculatedEDCBytes; + Array.Copy(channel, 0, SectorForCheck, 0, 0x810); + CRC32Context.Data(SectorForCheck, 0x810, out CalculatedEDCBytes, CDCRC32Poly, CDCRC32Seed); + UInt32 CalculatedEDC = BitConverter.ToUInt32(CalculatedEDCBytes, 0); + + if (CalculatedEDC != StoredEDC) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 1 sector at address: {0:X2}:{1:X2}:{2:X2}, got CRC 0x{3:X8} expected 0x{4:X8}", channel[0x00C], channel[0x00D], channel[0x00E], CalculatedEDC, StoredEDC); + return false; + } + + return true; + } + else if (channel[0x00F] == 0x02) // mode (1 byte) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 2 sector at address {0:X2}:{1:X2}:{2:X2}", channel[0x00C], channel[0x00D], channel[0x00E]); + + if ((channel[0x012] & 0x20) == 0x20) // mode 2 form 2 + { + if (channel[0x010] != channel[0x014] || channel[0x011] != channel[0x015] || channel[0x012] != channel[0x016] || channel[0x013] != channel[0x017]) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Subheader copies differ in mode 2 form 2 sector at address: {0:X2}:{1:X2}:{2:X2}", channel[0x00C], channel[0x00D], channel[0x00E]); + } + + byte[] SectorForCheck = new byte[0x91C]; + UInt32 StoredEDC = BitConverter.ToUInt32(channel, 0x92C); + byte[] CalculatedEDCBytes; + Array.Copy(channel, 0x10, SectorForCheck, 0, 0x91C); + CRC32Context.Data(SectorForCheck, 0x91C, out CalculatedEDCBytes, CDCRC32Poly, CDCRC32Seed); + UInt32 CalculatedEDC = BitConverter.ToUInt32(CalculatedEDCBytes, 0); + + if (CalculatedEDC != StoredEDC && StoredEDC != 0x00000000) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 2 form 2 sector at address: {0:X2}:{1:X2}:{2:X2}, got CRC 0x{3:X8} expected 0x{4:X8}", channel[0x00C], channel[0x00D], channel[0x00E], CalculatedEDC, StoredEDC); + return false; + } + } + else + { + if (channel[0x010] != channel[0x014] || channel[0x011] != channel[0x015] || channel[0x012] != channel[0x016] || channel[0x013] != channel[0x017]) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Subheader copies differ in mode 2 form 1 sector at address: {0:X2}:{1:X2}:{2:X2}", channel[0x00C], channel[0x00D], channel[0x00E]); + } + + byte[] address = new byte[4]; + byte[] data = new byte[2060]; + byte[] data2 = new byte[2232]; + byte[] ecc_p = new byte[172]; + byte[] ecc_q = new byte[104]; + + address[0] = 0; + address[1] = 0; + address[2] = 0; + address[3] = 0; + Array.Copy(channel, 0x0C, data, 0, 2060); + Array.Copy(channel, 0x0C, data2, 0, 2232); + Array.Copy(channel, 0x80C, ecc_p, 0, 172); + Array.Copy(channel, 0x8B8, ecc_q, 0, 104); + + bool FailedECC_P = CheckECC(address, data, 86, 24, 2, 86, ecc_p); + bool FailedECC_Q = CheckECC(address, data2, 52, 43, 86, 88, ecc_q); + + if (FailedECC_P) + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 2 form 1 sector at address: {0:X2}:{1:X2}:{2:X2}, fails ECC P check", channel[0x00C], channel[0x00D], channel[0x00E]); + if (FailedECC_Q) + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 2 form 1 sector at address: {0:X2}:{1:X2}:{2:X2}, fails ECC Q check", channel[0x00F], channel[0x00C], channel[0x00D], channel[0x00E]); + + if (FailedECC_P || FailedECC_Q) + return false; + + byte[] SectorForCheck = new byte[0x808]; + UInt32 StoredEDC = BitConverter.ToUInt32(channel, 0x818); + byte[] CalculatedEDCBytes; + Array.Copy(channel, 0x10, SectorForCheck, 0, 0x808); + CRC32Context.Data(SectorForCheck, 0x808, out CalculatedEDCBytes, CDCRC32Poly, CDCRC32Seed); + UInt32 CalculatedEDC = BitConverter.ToUInt32(CalculatedEDCBytes, 0); + + if (CalculatedEDC != StoredEDC) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Mode 2 form 1 sector at address: {0:X2}:{1:X2}:{2:X2}, got CRC 0x{3:X8} expected 0x{4:X8}", channel[0x00C], channel[0x00D], channel[0x00E], CalculatedEDC, StoredEDC); + return false; + } + } + + return true; + } + else + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Unknown mode {0} sector at address: {1:X2}:{2:X2}:{3:X2}", channel[0x00F], channel[0x00C], channel[0x00D], channel[0x00E]); + return null; + } + } + return null; + } + + static bool? CheckCDSectorSubChannel(byte[] subchannel) + { + bool? status = true; + byte[] QSubChannel = new byte[12]; + byte[] CDTextPack1 = new byte[18]; + byte[] CDTextPack2 = new byte[18]; + byte[] CDTextPack3 = new byte[18]; + byte[] CDTextPack4 = new byte[18]; + byte[] CDSubRWPack1 = new byte[24]; + byte[] CDSubRWPack2 = new byte[24]; + byte[] CDSubRWPack3 = new byte[24]; + byte[] CDSubRWPack4 = new byte[24]; + + int i = 0; + for (int j = 0; j < 12; j++) + QSubChannel[j] = 0; + for (int j = 0; j < 18; j++) + { + CDTextPack1[j] = 0; + CDTextPack2[j] = 0; + CDTextPack3[j] = 0; + CDTextPack4[j] = 0; + } + for (int j = 0; j < 24; j++) + { + CDSubRWPack1[j] = 0; + CDSubRWPack2[j] = 0; + CDSubRWPack3[j] = 0; + CDSubRWPack4[j] = 0; + } + + for (int j = 0; j < 12; j++) + { + QSubChannel[j] = (byte)(QSubChannel[j] | ((subchannel[i++] & 0x40) << 1)); + QSubChannel[j] = (byte)(QSubChannel[j] | (subchannel[i++] & 0x40)); + QSubChannel[j] = (byte)(QSubChannel[j] | ((subchannel[i++] & 0x40) >> 1)); + QSubChannel[j] = (byte)(QSubChannel[j] | ((subchannel[i++] & 0x40) >> 2)); + QSubChannel[j] = (byte)(QSubChannel[j] | ((subchannel[i++] & 0x40) >> 3)); + QSubChannel[j] = (byte)(QSubChannel[j] | ((subchannel[i++] & 0x40) >> 4)); + QSubChannel[j] = (byte)(QSubChannel[j] | ((subchannel[i++] & 0x40) >> 5)); + QSubChannel[j] = (byte)(QSubChannel[j] | ((subchannel[i++] & 0x40) >> 6)); + } + + i = 0; + for (int j = 0; j < 18; j++) + { + if (j < 18) + CDTextPack1[j] = (byte)(CDTextPack1[j] | ((subchannel[i++] & 0x3F) << 2)); + if (j < 18) + CDTextPack1[j] = (byte)(CDTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4)); + if (j < 18) + CDTextPack1[j] = (byte)(CDTextPack1[j] | ((subchannel[i++] & 0x0F) << 4)); + if (j < 18) + CDTextPack1[j] = (byte)(CDTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2)); + if (j < 18) + CDTextPack1[j] = (byte)(CDTextPack1[j] | ((subchannel[i++] & 0x03) << 6)); + if (j < 18) + CDTextPack1[j] = (byte)(CDTextPack1[j] | (subchannel[i++] & 0x3F)); + } + for (int j = 0; j < 18; j++) + { + if (j < 18) + CDTextPack2[j] = (byte)(CDTextPack2[j] | ((subchannel[i++] & 0x3F) << 2)); + if (j < 18) + CDTextPack2[j] = (byte)(CDTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4)); + if (j < 18) + CDTextPack2[j] = (byte)(CDTextPack2[j] | ((subchannel[i++] & 0x0F) << 4)); + if (j < 18) + CDTextPack2[j] = (byte)(CDTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2)); + if (j < 18) + CDTextPack2[j] = (byte)(CDTextPack2[j] | ((subchannel[i++] & 0x03) << 6)); + if (j < 18) + CDTextPack2[j] = (byte)(CDTextPack2[j] | (subchannel[i++] & 0x3F)); + } + for (int j = 0; j < 18; j++) + { + if (j < 18) + CDTextPack3[j] = (byte)(CDTextPack3[j] | ((subchannel[i++] & 0x3F) << 2)); + if (j < 18) + CDTextPack3[j] = (byte)(CDTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4)); + if (j < 18) + CDTextPack3[j] = (byte)(CDTextPack3[j] | ((subchannel[i++] & 0x0F) << 4)); + if (j < 18) + CDTextPack3[j] = (byte)(CDTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2)); + if (j < 18) + CDTextPack3[j] = (byte)(CDTextPack3[j] | ((subchannel[i++] & 0x03) << 6)); + if (j < 18) + CDTextPack3[j] = (byte)(CDTextPack3[j] | (subchannel[i++] & 0x3F)); + } + for (int j = 0; j < 18; j++) + { + if (j < 18) + CDTextPack4[j] = (byte)(CDTextPack4[j] | ((subchannel[i++] & 0x3F) << 2)); + if (j < 18) + CDTextPack4[j] = (byte)(CDTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4)); + if (j < 18) + CDTextPack4[j] = (byte)(CDTextPack4[j] | ((subchannel[i++] & 0x0F) << 4)); + if (j < 18) + CDTextPack4[j] = (byte)(CDTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2)); + if (j < 18) + CDTextPack4[j] = (byte)(CDTextPack4[j] | ((subchannel[i++] & 0x03) << 6)); + if (j < 18) + CDTextPack4[j] = (byte)(CDTextPack4[j] | (subchannel[i++] & 0x3F)); + } + + i = 0; + for (int j = 0; j < 24; j++) + { + CDSubRWPack1[j] = (byte)(subchannel[i++] & 0x3F); + } + for (int j = 0; j < 24; j++) + { + CDSubRWPack2[j] = (byte)(subchannel[i++] & 0x3F); + } + for (int j = 0; j < 24; j++) + { + CDSubRWPack3[j] = (byte)(subchannel[i++] & 0x3F); + } + for (int j = 0; j < 24; j++) + { + CDSubRWPack4[j] = (byte)(subchannel[i++] & 0x3F); + } + + //if(MainClass.isDebug) + //{ + switch(CDSubRWPack1[0]) + { + case 0x00: + Console.WriteLine("Detected Zero Pack in subchannel"); + break; + case 0x08: + Console.WriteLine("Detected Line Graphics Pack in subchannel"); + break; + case 0x09: + Console.WriteLine("Detected CD+G Pack in subchannel"); + break; + case 0x0A: + Console.WriteLine("Detected CD+EG Pack in subchannel"); + break; + case 0x14: + Console.WriteLine("Detected CD-TEXT Pack in subchannel"); + break; + case 0x18: + Console.WriteLine("Detected CD+MIDI Pack in subchannel"); + break; + case 0x38: + Console.WriteLine("Detected User Pack in subchannel"); + break; + default: + Console.WriteLine("Detected unknown Pack type in subchannel: mode {0}, item {1}", Convert.ToString(CDSubRWPack1[0] & 0x38, 2), Convert.ToString(CDSubRWPack1[0] & 0x07, 2)); + break; + } + //} + + BigEndianBitConverter.IsLittleEndian = true; + + UInt16 QSubChannelCRC = BigEndianBitConverter.ToUInt16(QSubChannel, 10); + byte[] QSubChannelForCRC = new byte[10]; + Array.Copy(QSubChannel, 0, QSubChannelForCRC, 0, 10); + UInt16 CalculatedQCRC = CalculateCCITT_CRC16(QSubChannelForCRC); + + if (QSubChannelCRC != CalculatedQCRC) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): Q subchannel CRC 0x{0:X4}, expected 0x{1:X4}", CalculatedQCRC, QSubChannelCRC); + status = false; + } + + if ((CDTextPack1[0] & 0x80) == 0x80) + { + UInt16 CDTextPack1CRC = BigEndianBitConverter.ToUInt16(CDTextPack1, 16); + byte[] CDTextPack1ForCRC = new byte[16]; + Array.Copy(CDTextPack1, 0, CDTextPack1ForCRC, 0, 16); + UInt16 CalculatedCDTP1CRC = CalculateCCITT_CRC16(CDTextPack1ForCRC); + + if (CDTextPack1CRC != CalculatedCDTP1CRC && CDTextPack1CRC != 0) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): CD-Text Pack 1 CRC 0x{0:X4}, expected 0x{1:X4}", CDTextPack1CRC, CalculatedCDTP1CRC); + status = false; + } + } + + if ((CDTextPack2[0] & 0x80) == 0x80) + { + UInt16 CDTextPack2CRC = BigEndianBitConverter.ToUInt16(CDTextPack2, 16); + byte[] CDTextPack2ForCRC = new byte[16]; + Array.Copy(CDTextPack2, 0, CDTextPack2ForCRC, 0, 16); + UInt16 CalculatedCDTP2CRC = CalculateCCITT_CRC16(CDTextPack2ForCRC); + Console.WriteLine("Cyclic CDTP2 0x{0:X4}, Calc CDTP2 0x{1:X4}", CDTextPack2CRC, CalculatedCDTP2CRC); + + if (CDTextPack2CRC != CalculatedCDTP2CRC && CDTextPack2CRC != 0) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): CD-Text Pack 2 CRC 0x{0:X4}, expected 0x{1:X4}", CDTextPack2CRC, CalculatedCDTP2CRC); + status = false; + } + } + + if ((CDTextPack3[0] & 0x80) == 0x80) + { + UInt16 CDTextPack3CRC = BigEndianBitConverter.ToUInt16(CDTextPack3, 16); + byte[] CDTextPack3ForCRC = new byte[16]; + Array.Copy(CDTextPack3, 0, CDTextPack3ForCRC, 0, 16); + UInt16 CalculatedCDTP3CRC = CalculateCCITT_CRC16(CDTextPack3ForCRC); + Console.WriteLine("Cyclic CDTP3 0x{0:X4}, Calc CDTP3 0x{1:X4}", CDTextPack3CRC, CalculatedCDTP3CRC); + + if (CDTextPack3CRC != CalculatedCDTP3CRC && CDTextPack3CRC != 0) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): CD-Text Pack 3 CRC 0x{0:X4}, expected 0x{1:X4}", CDTextPack3CRC, CalculatedCDTP3CRC); + status = false; + } + } + + if ((CDTextPack4[0] & 0x80) == 0x80) + { + UInt16 CDTextPack4CRC = BigEndianBitConverter.ToUInt16(CDTextPack4, 16); + byte[] CDTextPack4ForCRC = new byte[16]; + Array.Copy(CDTextPack4, 0, CDTextPack4ForCRC, 0, 16); + UInt16 CalculatedCDTP4CRC = CalculateCCITT_CRC16(CDTextPack4ForCRC); + Console.WriteLine("Cyclic CDTP4 0x{0:X4}, Calc CDTP4 0x{1:X4}", CDTextPack4CRC, CalculatedCDTP4CRC); + + if (CDTextPack4CRC != CalculatedCDTP4CRC && CDTextPack4CRC != 0) + { + //if (MainClass.isDebug) + Console.WriteLine("DEBUG (CDChecksums): CD-Text Pack 4 CRC 0x{0:X4}, expected 0x{1:X4}", CDTextPack4CRC, CalculatedCDTP4CRC); + status = false; + } + } + + return status; + } + + static readonly ushort[] CCITT_CRC16Table = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, + }; + + static ushort CalculateCCITT_CRC16(byte[] buffer) + { + UInt16 CRC16=0; + for (int i = 0; i < buffer.Length; i++) { + CRC16 = (ushort)(CCITT_CRC16Table[(CRC16 >> 8) ^ buffer[i]] ^ (CRC16 << 8)); + } + CRC16 = (ushort)~CRC16; + + return CRC16; + } + } +} + diff --git a/CRC16Context.cs b/CRC16Context.cs new file mode 100644 index 000000000..d59eb18b4 --- /dev/null +++ b/CRC16Context.cs @@ -0,0 +1,239 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : CRC16Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Implements a CRC16-CCITT algorithm. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +****************************************************************************/ +//$Id$ +using System.Text; +using System.IO; +using System; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to calculate CRC16. + /// + public class CRC16Context + { + const UInt16 crc16Poly = 0xA001; + const UInt16 crc16Seed = 0x0000; + + UInt16[] table; + UInt16 hashInt; + + /// + /// Initializes the CRC16 table and seed + /// + public void Init() + { + hashInt = crc16Seed; + + table = new UInt16[256]; + for (int i = 0; i < 256; i++) + { + UInt16 entry = (UInt16)i; + for (int j = 0; j < 8; j++) + if ((entry & 1) == 1) + entry = (ushort)((entry >> 1) ^ crc16Poly); + else + entry = (ushort)(entry >> 1); + table[i] = entry; + } + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + for (int i = 0; i < len; i++) + hashInt = (ushort)((hashInt >> 8) ^ table[data[i] ^ (hashInt & 0xFF)]); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + hashInt ^= crc16Seed; + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + return BigEndianBitConverter.GetBytes(hashInt); + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + hashInt ^= crc16Seed; + StringBuilder crc16Output = new StringBuilder(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + for (int i = 0; i < BigEndianBitConverter.GetBytes(hashInt).Length; i++) + { + crc16Output.Append(BigEndianBitConverter.GetBytes(hashInt)[i].ToString("x2")); + } + + return crc16Output.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public static byte[] File(string filename) + { + byte[] hash; + File(filename, out hash); + return hash; + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + UInt16[] localTable; + UInt16 localhashInt; + + localhashInt = crc16Seed; + + localTable = new UInt16[256]; + for (int i = 0; i < 256; i++) + { + UInt16 entry = (UInt16)i; + for (int j = 0; j < 8; j++) + if ((entry & 1) == 1) + entry = (ushort)((entry >> 1) ^ crc16Poly); + else + entry = (ushort)(entry >> 1); + localTable[i] = entry; + } + + for (int i = 0; i < fileStream.Length; i++) + localhashInt = (ushort)((localhashInt >> 8) ^ localTable[fileStream.ReadByte() ^ localhashInt & 0xff]); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + hash = BigEndianBitConverter.GetBytes(localhashInt); + + StringBuilder crc16Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + crc16Output.Append(hash[i].ToString("x2")); + } + + return crc16Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + return Data(data, len, out hash, crc16Poly, crc16Seed); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + /// CRC polynomial + /// CRC seed + public static string Data(byte[] data, uint len, out byte[] hash, UInt16 polynomial, UInt16 seed) + { + UInt16[] localTable; + UInt16 localhashInt; + + localhashInt = seed; + + localTable = new UInt16[256]; + for (int i = 0; i < 256; i++) + { + UInt16 entry = (UInt16)i; + for (int j = 0; j < 8; j++) + if ((entry & 1) == 1) + entry = (ushort)((entry >> 1) ^ polynomial); + else + entry = (ushort)(entry >> 1); + localTable[i] = entry; + } + + for (int i = 0; i < len; i++) + localhashInt = (ushort)((localhashInt >> 8) ^ localTable[data[i] ^ localhashInt & 0xff]); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + hash = BigEndianBitConverter.GetBytes(localhashInt); + + StringBuilder crc16Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + crc16Output.Append(hash[i].ToString("x2")); + } + + return crc16Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + diff --git a/CRC32Context.cs b/CRC32Context.cs new file mode 100644 index 000000000..355b4588d --- /dev/null +++ b/CRC32Context.cs @@ -0,0 +1,240 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : CRC32Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Implements a CRC32 algorithm. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +****************************************************************************/ +//$Id$ +using System.Text; +using System.IO; +using System; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to calculate CRC32. + /// + public class CRC32Context + { + const UInt32 crc32Poly = 0xEDB88320; + const UInt32 crc32Seed = 0xFFFFFFFF; + + UInt32[] table; + UInt32 hashInt; + + /// + /// Initializes the CRC32 table and seed + /// + public void Init() + { + hashInt = crc32Seed; + + table = 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) ^ crc32Poly; + else + entry = entry >> 1; + table[i] = entry; + } + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + for (int i = 0; i < len; i++) + hashInt = (hashInt >> 8) ^ table[data[i] ^ hashInt & 0xff]; + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + hashInt ^= crc32Seed; + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + return BigEndianBitConverter.GetBytes(hashInt); + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + hashInt ^= crc32Seed; + StringBuilder crc32Output = new StringBuilder(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + for (int i = 0; i < BigEndianBitConverter.GetBytes(hashInt).Length; i++) + { + crc32Output.Append(BigEndianBitConverter.GetBytes(hashInt)[i].ToString("x2")); + } + + return crc32Output.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public static byte[] File(string filename) + { + byte[] hash; + File(filename, out hash); + return hash; + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + UInt32[] localTable; + UInt32 localhashInt; + + localhashInt = crc32Seed; + + localTable = 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) ^ crc32Poly; + else + entry = entry >> 1; + localTable[i] = entry; + } + + for (int i = 0; i < fileStream.Length; i++) + localhashInt = (localhashInt >> 8) ^ localTable[fileStream.ReadByte() ^ localhashInt & 0xff]; + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + hash = BitConverter.GetBytes(localhashInt); + + StringBuilder crc32Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + crc32Output.Append(hash[i].ToString("x2")); + } + + return crc32Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + return Data(data, len, out hash, crc32Poly, crc32Seed); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + /// CRC polynomial + /// CRC seed + public static string Data(byte[] data, uint len, out byte[] hash, UInt32 polynomial, UInt32 seed) + { + UInt32[] localTable; + UInt32 localhashInt; + + localhashInt = seed; + + localTable = 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; + localTable[i] = entry; + } + + for (int i = 0; i < len; i++) + localhashInt = (localhashInt >> 8) ^ localTable[data[i] ^ localhashInt & 0xff]; + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + hash = BitConverter.GetBytes(localhashInt); + + StringBuilder crc32Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + crc32Output.Append(hash[i].ToString("x2")); + } + + return crc32Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + + diff --git a/CRC64Context.cs b/CRC64Context.cs new file mode 100644 index 000000000..ca965d50d --- /dev/null +++ b/CRC64Context.cs @@ -0,0 +1,239 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : CRC64Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Checksums +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Implements a CRC64 algorithm. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +****************************************************************************/ +//$Id$ +using System.Text; +using System.IO; +using System; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to calculate CRC64 (ECMA). + /// + public class CRC64Context + { + const UInt64 crc64Poly = 0xC96C5795D7870F42; + const UInt64 crc64Seed = 0xFFFFFFFFFFFFFFFF; + + UInt64[] table; + UInt64 hashInt; + + /// + /// Initializes the CRC64 table and seed + /// + public void Init() + { + hashInt = crc64Seed; + + table = new UInt64[256]; + for (int i = 0; i < 256; i++) + { + UInt64 entry = (UInt64)i; + for (int j = 0; j < 8; j++) + if ((entry & 1) == 1) + entry = (entry >> 1) ^ crc64Poly; + else + entry = entry >> 1; + table[i] = entry; + } + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + for (int i = 0; i < len; i++) + hashInt = (hashInt >> 8) ^ table[data[i] ^ hashInt & 0xff]; + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + hashInt ^= crc64Seed; + BigEndianBitConverter.IsLittleEndian = BigEndianBitConverter.IsLittleEndian; + return BigEndianBitConverter.GetBytes(hashInt); + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + hashInt ^= crc64Seed; + StringBuilder crc64Output = new StringBuilder(); + + BigEndianBitConverter.IsLittleEndian = BigEndianBitConverter.IsLittleEndian; + for (int i = 0; i < BigEndianBitConverter.GetBytes(hashInt).Length; i++) + { + crc64Output.Append(BigEndianBitConverter.GetBytes(hashInt)[i].ToString("x2")); + } + + return crc64Output.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public static byte[] File(string filename) + { + byte[] localHash; + File(filename, out localHash); + return localHash; + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + UInt64[] localTable; + UInt64 localhashInt; + + localhashInt = crc64Seed; + + localTable = new UInt64[256]; + for (int i = 0; i < 256; i++) + { + UInt64 entry = (UInt64)i; + for (int j = 0; j < 8; j++) + if ((entry & 1) == 1) + entry = (entry >> 1) ^ crc64Poly; + else + entry = entry >> 1; + localTable[i] = entry; + } + + for (int i = 0; i < fileStream.Length; i++) + localhashInt = (localhashInt >> 8) ^ localTable[(ulong)fileStream.ReadByte() ^ localhashInt & (ulong)0xff]; + + BigEndianBitConverter.IsLittleEndian = BigEndianBitConverter.IsLittleEndian; + hash = BitConverter.GetBytes(localhashInt); + + StringBuilder crc64Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + crc64Output.Append(hash[i].ToString("x2")); + } + + return crc64Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + return Data(data, len, out hash, crc64Poly, crc64Seed); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + /// CRC polynomial + /// CRC seed + public static string Data(byte[] data, uint len, out byte[] hash, UInt64 polynomial, UInt64 seed) + { + UInt64[] localTable; + UInt64 localhashInt; + + localhashInt = seed; + + localTable = new UInt64[256]; + for (int i = 0; i < 256; i++) + { + UInt64 entry = (UInt64)i; + for (int j = 0; j < 8; j++) + if ((entry & 1) == 1) + entry = (entry >> 1) ^ polynomial; + else + entry = entry >> 1; + localTable[i] = entry; + } + + for (int i = 0; i < len; i++) + localhashInt = (localhashInt >> 8) ^ localTable[data[i] ^ localhashInt & 0xff]; + + BigEndianBitConverter.IsLittleEndian = BigEndianBitConverter.IsLittleEndian; + hash = BitConverter.GetBytes(localhashInt); + + StringBuilder crc64Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + crc64Output.Append(hash[i].ToString("x2")); + } + + return crc64Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 000000000..40ed9581e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,20 @@ +2015-10-05 Natalia Portillo + + * MD5Context.cs: + * SHA1Context.cs: + * ReedSolomon.cs: + * CDChecksums.cs: + * CRC64Context.cs: + * CRC16Context.cs: + * CRC32Context.cs: + * SHA256Context.cs: + * SHA512Context.cs: + * SHA384Context.cs: + * SpamSumContext.cs: + * Adler32Context.cs: + * FletcherContext.cs: + * RIPEMD160Context.cs: + * Properties/AssemblyInfo.cs: + * DiscImageChef.Checksums.csproj: + Move checksums to a separate library. + diff --git a/DiscImageChef.Checksums.csproj b/DiscImageChef.Checksums.csproj new file mode 100644 index 000000000..61ed63bba --- /dev/null +++ b/DiscImageChef.Checksums.csproj @@ -0,0 +1,60 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {CC48B324-A532-4A45-87A6-6F91F7141E8D} + Library + DiscImageChef.Checksums + DiscImageChef.Checksums + 2.2 + v3.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + {F8BDF57B-1571-4CD0-84B3-B422088D359A} + DiscImageChef.Helpers + + + \ No newline at end of file diff --git a/FletcherContext.cs b/FletcherContext.cs new file mode 100644 index 000000000..3c60d6349 --- /dev/null +++ b/FletcherContext.cs @@ -0,0 +1,458 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : FletcherContext.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Implements Fletcher-32 and Fletcher-16 algorithms. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2015 Claunia.com +****************************************************************************/ +//$Id$ +using System; +using System.Text; +using System.IO; + +namespace DiscImageChef.Checksums +{ + public class Fletcher32Context + { + UInt16 sum1, sum2; + byte oddValue; + bool inodd; + + /// + /// Initializes the Fletcher32 sums + /// + public void Init() + { + sum1 = 0xFFFF; + sum2 = 0xFFFF; + oddValue = 0; + inodd = false; + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + UInt16 block; + if (!inodd) + { + // Odd size + if (len % 2 != 0) + { + oddValue = data[len - 1]; + inodd = true; + + for (int i = 0; i < len - 1; i += 2) + { + block = BigEndianBitConverter.ToUInt16(data, i); + sum1 = (UInt16)((sum1 + block) % 0xFFFF); + sum2 = (UInt16)((sum2 + sum1) % 0xFFFF); + } + } + else + { + inodd = false; + for (int i = 0; i < len; i += 2) + { + block = BigEndianBitConverter.ToUInt16(data, i); + sum1 = (UInt16)((sum1 + block) % 0xFFFF); + sum2 = (UInt16)((sum2 + sum1) % 0xFFFF); + } + } + } + // Carrying odd + else + { + byte[] oddData = new byte[2]; + oddData[0] = oddValue; + oddData[1] = data[0]; + + block = BigEndianBitConverter.ToUInt16(oddData, 0); + sum1 = (UInt16)((sum1 + block) % 0xFFFF); + sum2 = (UInt16)((sum2 + sum1) % 0xFFFF); + + // Even size, carrying odd + if (len % 2 == 0) + { + oddValue = data[len - 1]; + inodd = true; + + for (int i = 1; i < len - 1; i += 2) + { + block = BigEndianBitConverter.ToUInt16(data, i); + sum1 = (UInt16)((sum1 + block) % 0xFFFF); + sum2 = (UInt16)((sum2 + sum1) % 0xFFFF); + } + } + else + { + inodd = false; + for (int i = 1; i < len; i += 2) + { + block = BigEndianBitConverter.ToUInt16(data, i); + sum1 = (UInt16)((sum1 + block) % 0xFFFF); + sum2 = (UInt16)((sum2 + sum1) % 0xFFFF); + } + } + } + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + UInt32 finalSum = (UInt32)(sum1 + (sum2 << 16)); + return BigEndianBitConverter.GetBytes(finalSum); + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + UInt32 finalSum = (UInt32)(sum1 + (sum2 << 16)); + StringBuilder fletcherOutput = new StringBuilder(); + + for (int i = 0; i < BigEndianBitConverter.GetBytes(finalSum).Length; i++) + { + fletcherOutput.Append(BigEndianBitConverter.GetBytes(finalSum)[i].ToString("x2")); + } + + return fletcherOutput.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public static byte[] File(string filename) + { + byte[] hash; + File(filename, out hash); + return hash; + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + UInt16 localSum1, localSum2, block; + UInt32 finalSum; + byte[] blockBytes; + + localSum1 = 0xFFFF; + localSum2 = 0xFFFF; + block = 0; + + if (fileStream.Length % 2 == 0) + { + for (int i = 0; i < fileStream.Length; i += 2) + { + blockBytes = new byte[2]; + fileStream.Read(blockBytes, 0, 2); + block = BigEndianBitConverter.ToUInt16(blockBytes, 0); + localSum1 = (UInt16)((localSum1 + block) % 0xFFFF); + localSum2 = (UInt16)((localSum2 + localSum1) % 0xFFFF); + } + } + else + { + for (int i = 0; i < fileStream.Length - 1; i += 2) + { + blockBytes = new byte[2]; + fileStream.Read(blockBytes, 0, 2); + block = BigEndianBitConverter.ToUInt16(blockBytes, 0); + localSum1 = (UInt16)((localSum1 + block) % 0xFFFF); + localSum2 = (UInt16)((localSum2 + localSum1) % 0xFFFF); + } + + byte[] oddData = new byte[2]; + oddData[0] = (byte)fileStream.ReadByte(); + oddData[1] = 0; + + block = BigEndianBitConverter.ToUInt16(oddData, 0); + localSum1 = (UInt16)((localSum1 + block) % 0xFFFF); + localSum2 = (UInt16)((localSum2 + localSum1) % 0xFFFF); + } + + finalSum = (UInt32)(localSum1 + (localSum2 << 16)); + + hash = BitConverter.GetBytes(finalSum); + + StringBuilder fletcherOutput = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + fletcherOutput.Append(hash[i].ToString("x2")); + } + + return fletcherOutput.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + UInt16 localSum1, localSum2, block; + UInt32 finalSum; + + localSum1 = 0xFFFF; + localSum2 = 0xFFFF; + block = 0; + + if (len % 2 == 0) + { + for (int i = 0; i < len; i += 2) + { + block = BigEndianBitConverter.ToUInt16(data, i); + localSum1 = (UInt16)((localSum1 + block) % 0xFFFF); + localSum2 = (UInt16)((localSum2 + localSum1) % 0xFFFF); + } + } + else + { + for (int i = 0; i < len - 1; i += 2) + { + block = BigEndianBitConverter.ToUInt16(data, i); + localSum1 = (UInt16)((localSum1 + block) % 0xFFFF); + localSum2 = (UInt16)((localSum2 + localSum1) % 0xFFFF); + } + + byte[] oddData = new byte[2]; + oddData[0] = data[len - 1]; + oddData[1] = 0; + + block = BigEndianBitConverter.ToUInt16(oddData, 0); + localSum1 = (UInt16)((localSum1 + block) % 0xFFFF); + localSum2 = (UInt16)((localSum2 + localSum1) % 0xFFFF); + } + + finalSum = (UInt32)(localSum1 + (localSum2 << 16)); + + hash = BitConverter.GetBytes(finalSum); + + StringBuilder fletcherOutput = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + fletcherOutput.Append(hash[i].ToString("x2")); + } + + return fletcherOutput.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } + + public class Fletcher16Context + { + byte sum1, sum2; + + /// + /// Initializes the Fletcher16 sums + /// + public void Init() + { + sum1 = 0xFF; + sum2 = 0xFF; + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + for (int i = 0; i < len; i++) + { + sum1 = (byte)((sum1 + data[i]) % 0xFF); + sum2 = (byte)((sum2 + sum1) % 0xFF); + } + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + UInt16 finalSum = (UInt16)(sum1 + (sum2 << 8)); + return BigEndianBitConverter.GetBytes(finalSum); + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + UInt16 finalSum = (UInt16)(sum1 + (sum2 << 8)); + StringBuilder fletcherOutput = new StringBuilder(); + + for (int i = 0; i < BigEndianBitConverter.GetBytes(finalSum).Length; i++) + { + fletcherOutput.Append(BigEndianBitConverter.GetBytes(finalSum)[i].ToString("x2")); + } + + return fletcherOutput.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public static byte[] File(string filename) + { + byte[] hash; + File(filename, out hash); + return hash; + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + byte localSum1, localSum2, block; + UInt16 finalSum; + + localSum1 = 0xFF; + localSum2 = 0xFF; + block = 0; + + for (int i = 0; i < fileStream.Length; i += 2) + { + block = (byte)fileStream.ReadByte(); + localSum1 = (byte)((localSum1 + block) % 0xFF); + localSum2 = (byte)((localSum2 + localSum1) % 0xFF); + } + + finalSum = (UInt16)(localSum1 + (localSum2 << 8)); + + hash = BitConverter.GetBytes(finalSum); + + StringBuilder fletcherOutput = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + fletcherOutput.Append(hash[i].ToString("x2")); + } + + return fletcherOutput.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public static string Data(byte[] data, uint len, out byte[] hash) + { + byte localSum1, localSum2; + UInt16 finalSum; + + localSum1 = 0xFF; + localSum2 = 0xFF; + + for (int i = 0; i < len; i++) + { + localSum1 = (byte)((localSum1 + data[i]) % 0xFF); + localSum2 = (byte)((localSum2 + localSum1) % 0xFF); + } + + finalSum = (UInt16)(localSum1 + (localSum2 << 8)); + + hash = BitConverter.GetBytes(finalSum); + + StringBuilder fletcherOutput = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + fletcherOutput.Append(hash[i].ToString("x2")); + } + + return fletcherOutput.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public static string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + diff --git a/MD5Context.cs b/MD5Context.cs new file mode 100644 index 000000000..d7cad56cb --- /dev/null +++ b/MD5Context.cs @@ -0,0 +1,162 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : MD5Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Wraps up .NET MD5 implementation to a Init(), Update(), Final() context. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +****************************************************************************/ +//$Id$ +using System.Security.Cryptography; +using System.Text; +using System.IO; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to .NET MD5. + /// + public class MD5Context + { + MD5 _md5Provider; + + /// + /// Initializes the MD5 hash provider + /// + public void Init() + { + _md5Provider = MD5.Create(); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + _md5Provider.TransformBlock(data, 0, (int)len, data, 0); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + _md5Provider.TransformFinalBlock(new byte[0], 0, 0); + return _md5Provider.Hash; + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + _md5Provider.TransformFinalBlock(new byte[0], 0, 0); + StringBuilder md5Output = new StringBuilder(); + + for (int i = 0; i < _md5Provider.Hash.Length; i++) + { + md5Output.Append(_md5Provider.Hash[i].ToString("x2")); + } + + return md5Output.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public byte[] File(string filename) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + return _md5Provider.ComputeHash(fileStream); + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + hash = _md5Provider.ComputeHash(fileStream); + StringBuilder md5Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + md5Output.Append(hash[i].ToString("x2")); + } + + return md5Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public string Data(byte[] data, uint len, out byte[] hash) + { + hash = _md5Provider.ComputeHash(data, 0, (int)len); + StringBuilder md5Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + md5Output.Append(hash[i].ToString("x2")); + } + + return md5Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..20fb93d6d --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("DiscImageChef.Checksums")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Claunia.com")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("© Claunia.com")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/RIPEMD160Context.cs b/RIPEMD160Context.cs new file mode 100644 index 000000000..47b4a6a86 --- /dev/null +++ b/RIPEMD160Context.cs @@ -0,0 +1,162 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : RIPEMD160Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Wraps up .NET RIPEMD160 implementation to a Init(), Update(), Final() context. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +****************************************************************************/ +//$Id$ +using System.Security.Cryptography; +using System.Text; +using System.IO; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to .NET RIPEMD160. + /// + public class RIPEMD160Context + { + RIPEMD160 _ripemd160Provider; + + /// + /// Initializes the RIPEMD160 hash provider + /// + public void Init() + { + _ripemd160Provider = RIPEMD160.Create(); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + _ripemd160Provider.TransformBlock(data, 0, (int)len, data, 0); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + _ripemd160Provider.TransformFinalBlock(new byte[0], 0, 0); + return _ripemd160Provider.Hash; + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + _ripemd160Provider.TransformFinalBlock(new byte[0], 0, 0); + StringBuilder ripemd160Output = new StringBuilder(); + + for (int i = 0; i < _ripemd160Provider.Hash.Length; i++) + { + ripemd160Output.Append(_ripemd160Provider.Hash[i].ToString("x2")); + } + + return ripemd160Output.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public byte[] File(string filename) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + return _ripemd160Provider.ComputeHash(fileStream); + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + hash = _ripemd160Provider.ComputeHash(fileStream); + StringBuilder ripemd160Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + ripemd160Output.Append(hash[i].ToString("x2")); + } + + return ripemd160Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public string Data(byte[] data, uint len, out byte[] hash) + { + hash = _ripemd160Provider.ComputeHash(data, 0, (int)len); + StringBuilder ripemd160Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + ripemd160Output.Append(hash[i].ToString("x2")); + } + + return ripemd160Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + diff --git a/ReedSolomon.cs b/ReedSolomon.cs new file mode 100644 index 000000000..f10e4346a --- /dev/null +++ b/ReedSolomon.cs @@ -0,0 +1,650 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : ReedSolomon.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Calculates a Reed-Solomon + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +Copyright (C) 1996 Phil Karn +Copyright (C) 1995 Robert Morelos-Zaragoza +Copyright (C) 1995 Hari Thirumoorthy +****************************************************************************/ +//$Id$ + +/* + * Reed-Solomon coding and decoding + * Phil Karn (karn at ka9q.ampr.org) September 1996 + * + * This file is derived from the program "new_rs_erasures.c" by Robert + * Morelos-Zaragoza (robert at spectra.eng.hawaii.edu) and Hari Thirumoorthy + * (harit at spectra.eng.hawaii.edu), Aug 1995 + * + * I've made changes to improve performance, clean up the code and make it + * easier to follow. Data is now passed to the encoding and decoding functions + * through arguments rather than in global arrays. The decode function returns + * the number of corrected symbols, or -1 if the word is uncorrectable. + * + * This code supports a symbol size from 2 bits up to 16 bits, + * implying a block size of 3 2-bit symbols (6 bits) up to 65535 + * 16-bit symbols (1,048,560 bits). The code parameters are set in rs.h. + * + * Note that if symbols larger than 8 bits are used, the type of each + * data array element switches from unsigned char to unsigned int. The + * caller must ensure that elements larger than the symbol range are + * not passed to the encoder or decoder. + * + */ + +using System; + +namespace DiscImageChef.Checksums +{ + public class ReedSolomon + { + /* Primitive polynomials - see Lin & Costello, Error Control Coding Appendix A, + * and Lee & Messerschmitt, Digital Communication p. 453. + */ + int[] Pp; + /* index->polynomial form conversion table */ + int[] Alpha_to; + /* Polynomial->index form conversion table */ + int[] Index_of; + /* Generator polynomial g(x) + * Degree of g(x) = 2*TT + * has roots @**B0, @**(B0+1), ... ,@^(B0+2*TT-1) + */ + int[] Gg; + int MM, KK, NN; + /* No legal value in index form represents zero, so + * we need a special value for this purpose + */ + int A0; + bool initialized; + /* Alpha exponent for the first root of the generator polynomial */ + const int B0 = 1; + + /// + /// Initializes the Reed-Solomon with RS(n,k) with GF(2^m) + /// + public void InitRS(int n, int k, int m) + { + switch (m) + { + case 2: + Pp = new []{ 1, 1, 1 }; + break; + case 3: + Pp = new []{ 1, 1, 0, 1 }; + break; + case 4: + Pp = new []{ 1, 1, 0, 0, 1 }; + break; + case 5: + Pp = new []{ 1, 0, 1, 0, 0, 1 }; + break; + case 6: + Pp = new []{ 1, 1, 0, 0, 0, 0, 1 }; + break; + case 7: + Pp = new []{ 1, 0, 0, 1, 0, 0, 0, 1 }; + break; + case 8: + Pp = new []{ 1, 0, 1, 1, 1, 0, 0, 0, 1 }; + break; + case 9: + Pp = new []{ 1, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; + break; + case 10: + Pp = new []{ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 }; + break; + case 11: + Pp = new []{ 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + break; + case 12: + Pp = new []{ 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1 }; + break; + case 13: + Pp = new []{ 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + break; + case 14: + Pp = new []{ 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1 }; + break; + case 15: + Pp = new []{ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + break; + case 16: + Pp = new []{ 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 }; + break; + default: + throw new ArgumentOutOfRangeException("m", "m must be between 2 and 16 inclusive"); + } + + MM = m; + KK = k; + NN = n; + A0 = n; + Alpha_to = new int[n + 1]; + Index_of = new int[n + 1]; + + + Gg = new int[NN - KK + 1]; + + generate_gf(); + gen_poly(); + + initialized = true; + } + + int modnn(int x) + { + while (x >= NN) + { + x -= NN; + x = (x >> MM) + (x & NN); + } + return x; + } + + static int min(int a, int b) + { + return ((a) < (b) ? (a) : (b)); + } + + static void CLEAR(ref int[] a, int n) + { + int ci; + for (ci = (n) - 1; ci >= 0; ci--) + (a)[ci] = 0; + } + + static void COPY(ref int[] a, ref int[] b, int n) + { + int ci; + for (ci = (n) - 1; ci >= 0; ci--) + (a)[ci] = (b)[ci]; + } + + static void COPYDOWN(ref int[] a, ref int[] b, int n) + { + int ci; + for (ci = (n) - 1; ci >= 0; ci--) + (a)[ci] = (b)[ci]; + } + + /* generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m] + lookup tables: index->polynomial form alpha_to[] contains j=alpha**i; + polynomial form -> index form index_of[j=alpha**i] = i + alpha=2 is the primitive element of GF(2**m) + HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows: + Let @ represent the primitive element commonly called "alpha" that + is the root of the primitive polynomial p(x). Then in GF(2^m), for any + 0 <= i <= 2^m-2, + @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1) + where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation + of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for + example the polynomial representation of @^5 would be given by the binary + representation of the integer "alpha_to[5]". + Similarily, index_of[] can be used as follows: + As above, let @ represent the primitive element of GF(2^m) that is + the root of the primitive polynomial p(x). In order to find the power + of @ (alpha) that has the polynomial representation + a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1) + we consider the integer "i" whose binary representation with a(0) being LSB + and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry + "index_of[i]". Now, @^index_of[i] is that element whose polynomial + representation is (a(0),a(1),a(2),...,a(m-1)). + NOTE: + The element alpha_to[2^m-1] = 0 always signifying that the + representation of "@^infinity" = 0 is (0,0,0,...,0). + Similarily, the element index_of[0] = A0 always signifying + that the power of alpha which has the polynomial representation + (0,0,...,0) is "infinity". + + */ + void generate_gf() + { + int i, mask; + + mask = 1; + Alpha_to[MM] = 0; + for (i = 0; i < MM; i++) + { + Alpha_to[i] = mask; + Index_of[Alpha_to[i]] = i; + /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */ + if (Pp[i] != 0) + Alpha_to[MM] ^= mask; /* Bit-wise EXOR operation */ + mask <<= 1; /* single left-shift */ + } + Index_of[Alpha_to[MM]] = MM; + /* + * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by + * poly-repr of @^i shifted left one-bit and accounting for any @^MM + * term that may occur when poly-repr of @^i is shifted. + */ + mask >>= 1; + for (i = MM + 1; i < NN; i++) + { + if (Alpha_to[i - 1] >= mask) + Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1); + else + Alpha_to[i] = Alpha_to[i - 1] << 1; + Index_of[Alpha_to[i]] = i; + } + Index_of[0] = A0; + Alpha_to[NN] = 0; + } + + /* + * Obtain the generator polynomial of the TT-error correcting, length + * NN=(2**MM -1) Reed Solomon code from the product of (X+@**(B0+i)), i = 0, + * ... ,(2*TT-1) + * + * Examples: + * + * If B0 = 1, TT = 1. deg(g(x)) = 2*TT = 2. + * g(x) = (x+@) (x+@**2) + * + * If B0 = 0, TT = 2. deg(g(x)) = 2*TT = 4. + * g(x) = (x+1) (x+@) (x+@**2) (x+@**3) + */ + void gen_poly() + { + int i, j; + + Gg[0] = Alpha_to[B0]; + Gg[1] = 1; /* g(x) = (X+@**B0) initially */ + for (i = 2; i <= NN - KK; i++) + { + Gg[i] = 1; + /* + * Below multiply (Gg[0]+Gg[1]*x + ... +Gg[i]x^i) by + * (@**(B0+i-1) + x) + */ + for (j = i - 1; j > 0; j--) + if (Gg[j] != 0) + Gg[j] = Gg[j - 1] ^ Alpha_to[modnn((Index_of[Gg[j]]) + B0 + i - 1)]; + else + Gg[j] = Gg[j - 1]; + /* Gg[0] can never be zero */ + Gg[0] = Alpha_to[modnn((Index_of[Gg[0]]) + B0 + i - 1)]; + } + /* convert Gg[] to index form for quicker encoding */ + for (i = 0; i <= NN - KK; i++) + Gg[i] = Index_of[Gg[i]]; + } + + /* + * take the string of symbols in data[i], i=0..(k-1) and encode + * systematically to produce NN-KK parity symbols in bb[0]..bb[NN-KK-1] data[] + * is input and bb[] is output in polynomial form. Encoding is done by using + * a feedback shift register with appropriate connections specified by the + * elements of Gg[], which was generated above. Codeword is c(X) = + * data(X)*X**(NN-KK)+ b(X) + */ + /// + /// Takes the symbols in data to output parity in bb. + /// + /// Returns -1 if an illegal symbol is found. + /// Data symbols. + /// Outs parity symbols. + public int encode_rs(int[] data, out int[] bb) + { + if (initialized) + { + int i, j; + int feedback; + bb = new int[NN - KK]; + + CLEAR(ref bb, NN - KK); + for (i = KK - 1; i >= 0; i--) + { + if (MM != 8) + { + if (data[i] > NN) + return -1; /* Illegal symbol */ + } + feedback = Index_of[data[i] ^ bb[NN - KK - 1]]; + if (feedback != A0) + { /* feedback term is non-zero */ + for (j = NN - KK - 1; j > 0; j--) + if (Gg[j] != A0) + bb[j] = bb[j - 1] ^ Alpha_to[modnn(Gg[j] + feedback)]; + else + bb[j] = bb[j - 1]; + bb[0] = Alpha_to[modnn(Gg[0] + feedback)]; + } + else + { /* feedback term is zero. encoder becomes a + * single-byte shifter */ + for (j = NN - KK - 1; j > 0; j--) + bb[j] = bb[j - 1]; + bb[0] = 0; + } + } + return 0; + } + throw new UnauthorizedAccessException("Trying to calculate RS without initializing!"); + } + + /* + * Performs ERRORS+ERASURES decoding of RS codes. If decoding is successful, + * writes the codeword into data[] itself. Otherwise data[] is unaltered. + * + * Return number of symbols corrected, or -1 if codeword is illegal + * or uncorrectable. + * + * First "no_eras" erasures are declared by the calling program. Then, the + * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2). + * If the number of channel errors is not greater than "t_after_eras" the + * transmitted codeword will be recovered. Details of algorithm can be found + * in R. Blahut's "Theory ... of Error-Correcting Codes". + */ + /// + /// Decodes the RS. If decoding is successful outputs corrected data symbols. + /// + /// Returns corrected symbols, -1 if illegal or uncorrectable + /// Data symbols. + /// Position of erasures. + /// Number of erasures. + public int eras_dec_rs(ref int[] data, out int[] eras_pos, int no_eras) + { + if (initialized) + { + eras_pos = new int[NN - KK]; + int deg_lambda, el, deg_omega; + int i, j, r; + int u, q, tmp, num1, num2, den, discr_r; + int[] recd = new int[NN]; + int[] lambda = new int[NN - KK + 1]; /* Err+Eras Locator poly */ + int[] s = new int[NN - KK + 1]; /* syndrome poly */ + int[] b = new int[NN - KK + 1]; + int[] t = new int[NN - KK + 1]; + int[] omega = new int[NN - KK + 1]; + int[] root = new int[NN - KK]; + int[] reg = new int[NN - KK + 1]; + int[] loc = new int[NN - KK]; + int syn_error, count; + + /* data[] is in polynomial form, copy and convert to index form */ + for (i = NN - 1; i >= 0; i--) + { + if (MM != 8) + { + if (data[i] > NN) + return -1; /* Illegal symbol */ + } + recd[i] = Index_of[data[i]]; + } + /* first form the syndromes; i.e., evaluate recd(x) at roots of g(x) + * namely @**(B0+i), i = 0, ... ,(NN-KK-1) + */ + syn_error = 0; + for (i = 1; i <= NN - KK; i++) + { + tmp = 0; + for (j = 0; j < NN; j++) + if (recd[j] != A0) /* recd[j] in index form */ + tmp ^= Alpha_to[modnn(recd[j] + (B0 + i - 1) * j)]; + syn_error |= tmp; /* set flag if non-zero syndrome => + * error */ + /* store syndrome in index form */ + s[i] = Index_of[tmp]; + } + if (syn_error == 0) + { + /* + * if syndrome is zero, data[] is a codeword and there are no + * errors to correct. So return data[] unmodified + */ + return 0; + } + CLEAR(ref lambda, NN - KK); + lambda[0] = 1; + if (no_eras > 0) + { + /* Init lambda to be the erasure locator polynomial */ + lambda[1] = Alpha_to[eras_pos[0]]; + for (i = 1; i < no_eras; i++) + { + u = eras_pos[i]; + for (j = i + 1; j > 0; j--) + { + tmp = Index_of[lambda[j - 1]]; + if (tmp != A0) + lambda[j] ^= Alpha_to[modnn(u + tmp)]; + } + } + //if (MainClass.isDebug) + { + /* find roots of the erasure location polynomial */ + for (i = 1; i <= no_eras; i++) + reg[i] = Index_of[lambda[i]]; + count = 0; + for (i = 1; i <= NN; i++) + { + q = 1; + for (j = 1; j <= no_eras; j++) + if (reg[j] != A0) + { + reg[j] = modnn(reg[j] + j); + q ^= Alpha_to[reg[j]]; + } + if (q == 0) + { + /* store root and error location + * number indices + */ + root[count] = i; + loc[count] = NN - i; + count++; + } + } + if (count != no_eras) + { + Console.WriteLine("\n lambda(x) is WRONG\n"); + return -1; + } + + Console.WriteLine("\n Erasure positions as determined by roots of Eras Loc Poly:\n"); + for (i = 0; i < count; i++) + Console.WriteLine("{0} ", loc[i]); + Console.WriteLine("\n"); + } + } + for (i = 0; i < NN - KK + 1; i++) + b[i] = Index_of[lambda[i]]; + + /* + * Begin Berlekamp-Massey algorithm to determine error+erasure + * locator polynomial + */ + r = no_eras; + el = no_eras; + while (++r <= NN - KK) + { /* r is the step number */ + /* Compute discrepancy at the r-th step in poly-form */ + discr_r = 0; + for (i = 0; i < r; i++) + { + if ((lambda[i] != 0) && (s[r - i] != A0)) + { + discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])]; + } + } + discr_r = Index_of[discr_r]; /* Index form */ + if (discr_r == A0) + { + /* 2 lines below: B(x) <-- x*B(x) */ + COPYDOWN(ref b, ref b, NN - KK); + b[0] = A0; + } + else + { + /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */ + t[0] = lambda[0]; + for (i = 0; i < NN - KK; i++) + { + if (b[i] != A0) + t[i + 1] = lambda[i + 1] ^ Alpha_to[modnn(discr_r + b[i])]; + else + t[i + 1] = lambda[i + 1]; + } + if (2 * el <= r + no_eras - 1) + { + el = r + no_eras - el; + /* + * 2 lines below: B(x) <-- inv(discr_r) * + * lambda(x) + */ + for (i = 0; i <= NN - KK; i++) + b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN); + } + else + { + /* 2 lines below: B(x) <-- x*B(x) */ + COPYDOWN(ref b, ref b, NN - KK); + b[0] = A0; + } + COPY(ref lambda, ref t, NN - KK + 1); + } + } + + /* Convert lambda to index form and compute deg(lambda(x)) */ + deg_lambda = 0; + for (i = 0; i < NN - KK + 1; i++) + { + lambda[i] = Index_of[lambda[i]]; + if (lambda[i] != A0) + deg_lambda = i; + } + /* + * Find roots of the error+erasure locator polynomial. By Chien + * Search + */ + int temp = reg[0]; + COPY(ref reg, ref lambda, NN - KK); + reg[0] = temp; + count = 0; /* Number of roots of lambda(x) */ + for (i = 1; i <= NN; i++) + { + q = 1; + for (j = deg_lambda; j > 0; j--) + if (reg[j] != A0) + { + reg[j] = modnn(reg[j] + j); + q ^= Alpha_to[reg[j]]; + } + if (q == 0) + { + /* store root (index-form) and error location number */ + root[count] = i; + loc[count] = NN - i; + count++; + } + } + + //if (MainClass.isDebug) + { + Console.WriteLine("\n Final error positions:\t"); + for (i = 0; i < count; i++) + Console.WriteLine("{0} ", loc[i]); + Console.WriteLine("\n"); + } + if (deg_lambda != count) + { + /* + * deg(lambda) unequal to number of roots => uncorrectable + * error detected + */ + return -1; + } + /* + * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo + * x**(NN-KK)). in index form. Also find deg(omega). + */ + deg_omega = 0; + for (i = 0; i < NN - KK; i++) + { + tmp = 0; + j = (deg_lambda < i) ? deg_lambda : i; + for (; j >= 0; j--) + { + if ((s[i + 1 - j] != A0) && (lambda[j] != A0)) + tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])]; + } + if (tmp != 0) + deg_omega = i; + omega[i] = Index_of[tmp]; + } + omega[NN - KK] = A0; + + /* + * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form + */ + for (j = count - 1; j >= 0; j--) + { + num1 = 0; + for (i = deg_omega; i >= 0; i--) + { + if (omega[i] != A0) + num1 ^= Alpha_to[modnn(omega[i] + i * root[j])]; + } + num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ + for (i = min(deg_lambda, NN - KK - 1) & ~1; i >= 0; i -= 2) + { + if (lambda[i + 1] != A0) + den ^= Alpha_to[modnn(lambda[i + 1] + i * root[j])]; + } + if (den == 0) + { + //if (MainClass.isDebug) + { + Console.WriteLine("\n ERROR: denominator = 0\n"); + } + return -1; + } + /* Apply error to data */ + if (num1 != 0) + { + data[loc[j]] ^= Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])]; + } + } + return count; + } + throw new UnauthorizedAccessException("Trying to calculate RS without initializing!"); + } + } +} diff --git a/SHA1Context.cs b/SHA1Context.cs new file mode 100644 index 000000000..22873d5c6 --- /dev/null +++ b/SHA1Context.cs @@ -0,0 +1,162 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : SHA1Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Wraps up .NET SHA1 implementation to a Init(), Update(), Final() context. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +****************************************************************************/ +//$Id$ +using System.Security.Cryptography; +using System.Text; +using System.IO; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to .NET SHA1. + /// + public class SHA1Context + { + SHA1 _sha1Provider; + + /// + /// Initializes the SHA1 hash provider + /// + public void Init() + { + _sha1Provider = SHA1.Create(); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + _sha1Provider.TransformBlock(data, 0, (int)len, data, 0); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + _sha1Provider.TransformFinalBlock(new byte[0], 0, 0); + return _sha1Provider.Hash; + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + _sha1Provider.TransformFinalBlock(new byte[0], 0, 0); + StringBuilder sha1Output = new StringBuilder(); + + for (int i = 0; i < _sha1Provider.Hash.Length; i++) + { + sha1Output.Append(_sha1Provider.Hash[i].ToString("x2")); + } + + return sha1Output.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public byte[] File(string filename) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + return _sha1Provider.ComputeHash(fileStream); + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + hash = _sha1Provider.ComputeHash(fileStream); + StringBuilder sha1Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + sha1Output.Append(hash[i].ToString("x2")); + } + + return sha1Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public string Data(byte[] data, uint len, out byte[] hash) + { + hash = _sha1Provider.ComputeHash(data, 0, (int)len); + StringBuilder sha1Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + sha1Output.Append(hash[i].ToString("x2")); + } + + return sha1Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + diff --git a/SHA256Context.cs b/SHA256Context.cs new file mode 100644 index 000000000..6da3dbbeb --- /dev/null +++ b/SHA256Context.cs @@ -0,0 +1,162 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : SHA256Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Wraps up .NET SHA256 implementation to a Init(), Update(), Final() context. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +****************************************************************************/ +//$Id$ +using System.Security.Cryptography; +using System.Text; +using System.IO; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to .NET SHA256. + /// + public class SHA256Context + { + SHA256 _sha256Provider; + + /// + /// Initializes the SHA256 hash provider + /// + public void Init() + { + _sha256Provider = SHA256.Create(); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + _sha256Provider.TransformBlock(data, 0, (int)len, data, 0); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + _sha256Provider.TransformFinalBlock(new byte[0], 0, 0); + return _sha256Provider.Hash; + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + _sha256Provider.TransformFinalBlock(new byte[0], 0, 0); + StringBuilder sha256Output = new StringBuilder(); + + for (int i = 0; i < _sha256Provider.Hash.Length; i++) + { + sha256Output.Append(_sha256Provider.Hash[i].ToString("x2")); + } + + return sha256Output.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public byte[] File(string filename) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + return _sha256Provider.ComputeHash(fileStream); + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + hash = _sha256Provider.ComputeHash(fileStream); + StringBuilder sha256Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + sha256Output.Append(hash[i].ToString("x2")); + } + + return sha256Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public string Data(byte[] data, uint len, out byte[] hash) + { + hash = _sha256Provider.ComputeHash(data, 0, (int)len); + StringBuilder sha256Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + sha256Output.Append(hash[i].ToString("x2")); + } + + return sha256Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + diff --git a/SHA384Context.cs b/SHA384Context.cs new file mode 100644 index 000000000..c8f31c925 --- /dev/null +++ b/SHA384Context.cs @@ -0,0 +1,162 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : SHA384Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Wraps up .NET SHA384 implementation to a Init(), Update(), Final() context. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +****************************************************************************/ +//$Id$ +using System.Security.Cryptography; +using System.Text; +using System.IO; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to .NET SHA384. + /// + public class SHA384Context + { + SHA384 _sha384Provider; + + /// + /// Initializes the SHA384 hash provider + /// + public void Init() + { + _sha384Provider = SHA384.Create(); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + _sha384Provider.TransformBlock(data, 0, (int)len, data, 0); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + _sha384Provider.TransformFinalBlock(new byte[0], 0, 0); + return _sha384Provider.Hash; + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + _sha384Provider.TransformFinalBlock(new byte[0], 0, 0); + StringBuilder sha384Output = new StringBuilder(); + + for (int i = 0; i < _sha384Provider.Hash.Length; i++) + { + sha384Output.Append(_sha384Provider.Hash[i].ToString("x2")); + } + + return sha384Output.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public byte[] File(string filename) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + return _sha384Provider.ComputeHash(fileStream); + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + hash = _sha384Provider.ComputeHash(fileStream); + StringBuilder sha384Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + sha384Output.Append(hash[i].ToString("x2")); + } + + return sha384Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public string Data(byte[] data, uint len, out byte[] hash) + { + hash = _sha384Provider.ComputeHash(data, 0, (int)len); + StringBuilder sha384Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + sha384Output.Append(hash[i].ToString("x2")); + } + + return sha384Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + diff --git a/SHA512Context.cs b/SHA512Context.cs new file mode 100644 index 000000000..741a8f943 --- /dev/null +++ b/SHA512Context.cs @@ -0,0 +1,162 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : SHA512Context.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Wraps up .NET SHA512 implementation to a Init(), Update(), Final() context. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2014 Claunia.com +****************************************************************************/ +//$Id$ +using System.Security.Cryptography; +using System.Text; +using System.IO; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to .NET SHA512. + /// + public class SHA512Context + { + SHA512 _sha512Provider; + + /// + /// Initializes the SHA512 hash provider + /// + public void Init() + { + _sha512Provider = SHA512.Create(); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + _sha512Provider.TransformBlock(data, 0, (int)len, data, 0); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + _sha512Provider.TransformFinalBlock(new byte[0], 0, 0); + return _sha512Provider.Hash; + } + + /// + /// Returns a hexadecimal representation of the hash value. + /// + public string End() + { + _sha512Provider.TransformFinalBlock(new byte[0], 0, 0); + StringBuilder sha512Output = new StringBuilder(); + + for (int i = 0; i < _sha512Provider.Hash.Length; i++) + { + sha512Output.Append(_sha512Provider.Hash[i].ToString("x2")); + } + + return sha512Output.ToString(); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public byte[] File(string filename) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + return _sha512Provider.ComputeHash(fileStream); + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public string File(string filename, out byte[] hash) + { + FileStream fileStream = new FileStream(filename, FileMode.Open); + hash = _sha512Provider.ComputeHash(fileStream); + StringBuilder sha512Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + sha512Output.Append(hash[i].ToString("x2")); + } + + return sha512Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// Byte array of the hash value. + public string Data(byte[] data, uint len, out byte[] hash) + { + hash = _sha512Provider.ComputeHash(data, 0, (int)len); + StringBuilder sha512Output = new StringBuilder(); + + for (int i = 0; i < hash.Length; i++) + { + sha512Output.Append(hash[i].ToString("x2")); + } + + return sha512Output.ToString(); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Byte array of the hash value. + public string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + } +} + diff --git a/SpamSumContext.cs b/SpamSumContext.cs new file mode 100644 index 000000000..4a6ff6eaf --- /dev/null +++ b/SpamSumContext.cs @@ -0,0 +1,534 @@ +/*************************************************************************** +The Disc Image Chef +---------------------------------------------------------------------------- + +Filename : SpamSumContext.cs +Version : 1.0 +Author(s) : Natalia Portillo + +Component : Checksums. + +Revision : $Revision$ +Last change by : $Author$ +Date : $Date$ + +--[ Description ] ---------------------------------------------------------- + +Implements the SpamSum fuzzy hashing algorithm. + +--[ License ] -------------------------------------------------------------- + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +---------------------------------------------------------------------------- +Copyright (C) 2011-2015 Claunia.com +****************************************************************************/ +// Based on ssdeep +// Copyright (C) 2002 Andrew Tridgell +// Copyright (C) 2006 ManTech International Corporation +// Copyright (C) 2013 Helmut Grohne +// +// Earlier versions of this code were named fuzzy.c and can be found at: +// http://www.samba.org/ftp/unpacked/junkcode/spamsum/ +// http://ssdeep.sf.net/ + +using System; +using System.Text; + +namespace DiscImageChef.Checksums +{ + /// + /// Provides a UNIX similar API to calculate Fuzzy Hash (SpamSum). + /// + public class SpamSumContext + { + const UInt32 ROLLING_WINDOW = 7; + const UInt32 MIN_BLOCKSIZE = 3; + const UInt32 HASH_PRIME = 0x01000193; + const UInt32 HASH_INIT = 0x28021967; + const UInt32 NUM_BLOCKHASHES = 31; + const UInt32 SPAMSUM_LENGTH = 64; + const UInt32 FUZZY_MAX_RESULT = (2 * SPAMSUM_LENGTH + 20); + //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + readonly byte[] b64 = + {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, + 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F + }; + + struct roll_state + { + public byte[] window; + // ROLLING_WINDOW + public UInt32 h1; + public UInt32 h2; + public UInt32 h3; + public UInt32 n; + } + + /* A blockhash contains a signature state for a specific (implicit) blocksize. + * The blocksize is given by SSDEEP_BS(index). The h and halfh members are the + * FNV hashes, where halfh stops to be reset after digest is SPAMSUM_LENGTH/2 + * long. The halfh hash is needed be able to truncate digest for the second + * output hash to stay compatible with ssdeep output. */ + struct blockhash_context + { + public UInt32 h; + public UInt32 halfh; + public byte[] digest; + // SPAMSUM_LENGTH + public byte halfdigest; + public UInt32 dlen; + } + + struct fuzzy_state + { + public UInt32 bhstart; + public UInt32 bhend; + public blockhash_context[] bh; + //NUM_BLOCKHASHES + public UInt64 total_size; + public roll_state roll; + } + + fuzzy_state self; + + void roll_init() + { + self.roll = new roll_state(); + self.roll.window = new byte[ROLLING_WINDOW]; + } + + /// + /// Initializes the SpamSum structures + /// + public void Init() + { + self = new fuzzy_state(); + self.bh = new blockhash_context[NUM_BLOCKHASHES]; + for (int i = 0; i < NUM_BLOCKHASHES; i++) + self.bh[i].digest = new byte[SPAMSUM_LENGTH]; + + self.bhstart = 0; + self.bhend = 1; + self.bh[0].h = HASH_INIT; + self.bh[0].halfh = HASH_INIT; + self.bh[0].digest[0] = 0; + self.bh[0].halfdigest = 0; + self.bh[0].dlen = 0; + self.total_size = 0; + roll_init(); + } + + /* + * a rolling hash, based on the Adler checksum. By using a rolling hash + * we can perform auto resynchronisation after inserts/deletes + + * internally, h1 is the sum of the bytes in the window and h2 + * is the sum of the bytes times the index + + * h3 is a shift/xor based rolling hash, and is mostly needed to ensure that + * we can cope with large blocksize values + */ + void roll_hash(byte c) + { + self.roll.h2 -= self.roll.h1; + self.roll.h2 += ROLLING_WINDOW * (UInt32)c; + + self.roll.h1 += (UInt32)c; + self.roll.h1 -= (UInt32)self.roll.window[self.roll.n % ROLLING_WINDOW]; + + self.roll.window[self.roll.n % ROLLING_WINDOW] = c; + self.roll.n++; + + /* The original spamsum AND'ed this value with 0xFFFFFFFF which + * in theory should have no effect. This AND has been removed + * for performance (jk) */ + self.roll.h3 <<= 5; + self.roll.h3 ^= c; + } + + UInt32 roll_sum() + { + return self.roll.h1 + self.roll.h2 + self.roll.h3; + } + + /* A simple non-rolling hash, based on the FNV hash. */ + static UInt32 sum_hash(byte c, UInt32 h) + { + return (h * HASH_PRIME) ^ c; + } + + static UInt32 SSDEEP_BS(UInt32 index) + { + return (MIN_BLOCKSIZE << (int)index); + } + + void fuzzy_try_fork_blockhash() + { + uint obh, nbh; + + if (self.bhend >= NUM_BLOCKHASHES) + return; + + if (self.bhend == 0) // assert + throw new Exception("Assertion failed"); + + obh = self.bhend - 1; + nbh = self.bhend; + self.bh[nbh].h = self.bh[obh].h; + self.bh[nbh].halfh = self.bh[obh].halfh; + self.bh[nbh].digest[0] = 0; + self.bh[nbh].halfdigest = 0; + self.bh[nbh].dlen = 0; + ++self.bhend; + } + + void fuzzy_try_reduce_blockhash() + { + if (self.bhstart >= self.bhend) + throw new Exception("Assertion failed"); + + if (self.bhend - self.bhstart < 2) + /* Need at least two working hashes. */ + return; + if ((UInt64)SSDEEP_BS(self.bhstart) * SPAMSUM_LENGTH >= + self.total_size) + /* Initial blocksize estimate would select this or a smaller + * blocksize. */ + return; + if (self.bh[self.bhstart + 1].dlen < SPAMSUM_LENGTH / 2) + /* Estimate adjustment would select this blocksize. */ + return; + /* At this point we are clearly no longer interested in the + * start_blocksize. Get rid of it. */ + ++self.bhstart; + } + + void fuzzy_engine_step(byte c) + { + UInt64 h; + UInt32 i; + /* At each character we update the rolling hash and the normal hashes. + * When the rolling hash hits a reset value then we emit a normal hash + * as a element of the signature and reset the normal hash. */ + roll_hash(c); + h = roll_sum(); + + for (i = self.bhstart; i < self.bhend; ++i) + { + self.bh[i].h = sum_hash(c, self.bh[i].h); + self.bh[i].halfh = sum_hash(c, self.bh[i].halfh); + } + + for (i = self.bhstart; i < self.bhend; ++i) + { + /* With growing blocksize almost no runs fail the next test. */ + if (h % SSDEEP_BS(i) != SSDEEP_BS(i) - 1) + /* Once this condition is false for one bs, it is + * automatically false for all further bs. I.e. if + * h === -1 (mod 2*bs) then h === -1 (mod bs). */ + break; + /* We have hit a reset point. We now emit hashes which are + * based on all characters in the piece of the message between + * the last reset point and this one */ + if (0 == self.bh[i].dlen) + { + /* Can only happen 30 times. */ + /* First step for this blocksize. Clone next. */ + fuzzy_try_fork_blockhash(); + } + self.bh[i].digest[self.bh[i].dlen] = b64[self.bh[i].h % 64]; + self.bh[i].halfdigest = b64[self.bh[i].halfh % 64]; + if (self.bh[i].dlen < SPAMSUM_LENGTH - 1) + { + /* We can have a problem with the tail overflowing. The + * easiest way to cope with this is to only reset the + * normal hash if we have room for more characters in + * our signature. This has the effect of combining the + * last few pieces of the message into a single piece + * */ + self.bh[i].digest[++(self.bh[i].dlen)] = 0; + self.bh[i].h = HASH_INIT; + if (self.bh[i].dlen < SPAMSUM_LENGTH / 2) + { + self.bh[i].halfh = HASH_INIT; + self.bh[i].halfdigest = 0; + } + } + else + fuzzy_try_reduce_blockhash(); + } + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + /// Length of buffer to hash. + public void Update(byte[] data, uint len) + { + self.total_size += len; + for (int i = 0; i < len; i++) + fuzzy_engine_step(data[i]); + } + + /// + /// Updates the hash with data. + /// + /// Data buffer. + public void Update(byte[] data) + { + Update(data, (uint)data.Length); + } + + // CLAUNIA: Flags seems to never be used in ssdeep, so I just removed it for code simplicity + UInt32 fuzzy_digest(out byte[] result) + { + StringBuilder sb = new StringBuilder(); + UInt32 bi = self.bhstart; + UInt32 h = roll_sum(); + int i, result_off; + int remain = (int)(FUZZY_MAX_RESULT - 1); /* Exclude terminating '\0'. */ + result = new byte[FUZZY_MAX_RESULT]; + /* Verify that our elimination was not overeager. */ + if (!(bi == 0 || (UInt64)SSDEEP_BS(bi) / 2 * SPAMSUM_LENGTH < self.total_size)) + throw new Exception("Assertion failed"); + + result_off = 0; + + /* Initial blocksize guess. */ + while ((UInt64)SSDEEP_BS(bi) * SPAMSUM_LENGTH < self.total_size) + { + ++bi; + if (bi >= NUM_BLOCKHASHES) + { + throw new OverflowException("The input exceeds data types."); + } + } + /* Adapt blocksize guess to actual digest length. */ + while (bi >= self.bhend) + --bi; + while (bi > self.bhstart && self.bh[bi].dlen < SPAMSUM_LENGTH / 2) + --bi; + if ((bi > 0 && self.bh[bi].dlen < SPAMSUM_LENGTH / 2)) + throw new Exception("Assertion failed"); + + sb.AppendFormat("{0}:", SSDEEP_BS(bi)); + i = Encoding.ASCII.GetBytes(sb.ToString()).Length; + if (i <= 0) + /* Maybe snprintf has set errno here? */ + throw new OverflowException("The input exceeds data types."); + if (i >= remain) + throw new Exception("Assertion failed"); + remain -= i; + + Array.Copy(Encoding.ASCII.GetBytes(sb.ToString()), 0, result, 0, i); + + result_off += i; + + i = (int)self.bh[bi].dlen; + if (i > remain) + throw new Exception("Assertion failed"); + + Array.Copy(self.bh[bi].digest, 0, result, result_off, i); + result_off += i; + remain -= i; + if (h != 0) + { + if (remain <= 0) + throw new Exception("Assertion failed"); + result[result_off] = b64[self.bh[bi].h % 64]; + if (i < 3 || + result[result_off] != result[result_off - 1] || + result[result_off] != result[result_off - 2] || + result[result_off] != result[result_off - 3]) + { + ++result_off; + --remain; + } + } + else if (self.bh[bi].digest[i] != 0) + { + if (remain <= 0) + throw new Exception("Assertion failed"); + result[result_off] = self.bh[bi].digest[i]; + if (i < 3 || + result[result_off] != result[result_off - 1] || + result[result_off] != result[result_off - 2] || + result[result_off] != result[result_off - 3]) + { + ++result_off; + --remain; + } + } + if (remain <= 0) + throw new Exception("Assertion failed"); + result[result_off++] = 0x3A; // ':' + --remain; + if (bi < self.bhend - 1) + { + ++bi; + i = (int)self.bh[bi].dlen; + if (i > remain) + throw new Exception("Assertion failed"); + Array.Copy(self.bh[bi].digest, 0, result, result_off, i); + result_off += i; + remain -= i; + + if (h != 0) + { + if (remain <= 0) + throw new Exception("Assertion failed"); + h = self.bh[bi].halfh; + result[result_off] = b64[h % 64]; + if (i < 3 || + result[result_off] != result[result_off - 1] || + result[result_off] != result[result_off - 2] || + result[result_off] != result[result_off - 3]) + { + ++result_off; + --remain; + } + } + else + { + i = self.bh[bi].halfdigest; + if (i != 0) + { + if (remain <= 0) + throw new Exception("Assertion failed"); + result[result_off] = (byte)i; + if (i < 3 || + result[result_off] != result[result_off - 1] || + result[result_off] != result[result_off - 2] || + result[result_off] != result[result_off - 3]) + { + ++result_off; + --remain; + } + } + } + } + else if (h != 0) + { + if (self.bh[bi].dlen != 0) + throw new Exception("Assertion failed"); + if (remain <= 0) + throw new Exception("Assertion failed"); + result[result_off++] = b64[self.bh[bi].h % 64]; + /* No need to bother with FUZZY_FLAG_ELIMSEQ, because this + * digest has length 1. */ + --remain; + } + result[result_off] = 0; + return 0; + } + + /// + /// Returns a byte array of the hash value. + /// + public byte[] Final() + { + // SpamSum does not have a binary representation, or so it seems + throw new NotImplementedException("SpamSum does not have a binary representation."); + } + + /// + /// Returns a base64 representation of the hash value. + /// + public string End() + { + byte[] result; + fuzzy_digest(out result); + + return CToString(result); + } + + /// + /// Gets the hash of a file + /// + /// File path. + public static byte[] File(string filename) + { + // SpamSum does not have a binary representation, or so it seems + throw new NotImplementedException("SpamSum does not have a binary representation."); + } + + /// + /// Gets the hash of a file in hexadecimal and as a byte array. + /// + /// File path. + /// Byte array of the hash value. + public static string File(string filename, out byte[] hash) + { + // SpamSum does not have a binary representation, or so it seems + throw new NotImplementedException("Not yet implemented."); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// Length of the data buffer to hash. + /// null + /// Base64 representation of SpamSum $blocksize:$hash:$hash + public string Data(byte[] data, uint len, out byte[] hash) + { + SpamSumContext fuzzyContext = new SpamSumContext(); + fuzzyContext.Init(); + + fuzzyContext.Update(data, len); + hash = null; + + byte[] result; + fuzzy_digest(out result); + + return CToString(result); + } + + /// + /// Gets the hash of the specified data buffer. + /// + /// Data buffer. + /// null + /// Base64 representation of SpamSum $blocksize:$hash:$hash + public string Data(byte[] data, out byte[] hash) + { + return Data(data, (uint)data.Length, out hash); + } + + // Converts an ASCII null-terminated string to .NET string + private string CToString(byte[] CString) + { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < CString.Length; i++) + { + if (CString[i] == 0) + break; + + sb.Append(Encoding.ASCII.GetString(CString, i, 1)); + } + + return sb.ToString(); + } + } +} +