commit abff1b6edea5a5a1e0e79d70f74179439ec54e11 Author: Natalia Portillo Date: Mon Oct 5 18:58:13 2015 +0100 * 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. 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(); + } + } +} +