-#region
-/* -----------------------------------------------------------------------
- * <copyright file="Signature.cs" company="GRNet">
- *
- * Copyright 2011-2012 GRNET S.A. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * 1. Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and
- * documentation are those of the authors and should not be
- * interpreted as representing official policies, either expressed
- * or implied, of GRNET S.A.
- * </copyright>
- * -----------------------------------------------------------------------
- */
-#endregion
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics.Contracts;
-using System.IO;
-using System.Reflection;
-using System.Runtime.Remoting.Metadata.W3cXsd2001;
-using System.Security.Cryptography;
-using System.Threading.Tasks;
-using System.Linq;
-
-namespace Pithos.Network
-{
- public static class Signature
- {
- private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- public static string CalculateMD5(FileInfo info)
- {
- if (info==null)
- throw new ArgumentNullException("info");
- if (String.IsNullOrWhiteSpace(info.FullName))
- throw new ArgumentException("info.FullName is empty","info");
- Contract.EndContractBlock();
-
- return CalculateMD5(info.FullName);
- }
-
- public static string CalculateMD5(string path)
- {
- if (String.IsNullOrWhiteSpace(path))
- throw new ArgumentNullException("path");
- Contract.EndContractBlock();
-
- //DON'T calculate hashes for folders
- if (Directory.Exists(path))
- return "";
-
- string hash;
- using (var hasher = MD5.Create())
- using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 65536, true))
- {
- var hashBytes = hasher.ComputeHash(stream);
- hash = hashBytes.ToHashString();
- }
- return hash;
- }
-
-/*
- public static string BytesToString(byte[] hashBytes)
- {
- var shb=new SoapHexBinary(hashBytes);
- return shb.ToString();
-
- }
-
-
- public static byte[] StringToBytes(string hash)
- {
- var shb=SoapHexBinary.Parse(hash);
- return shb.Value;
- }
-*/
-
- public static byte[] ToBytes(this string hash)
- {
- var shb = SoapHexBinary.Parse(hash);
- return shb.Value;
- }
-
- public static string ToHashString(this byte[] hashBytes)
- {
- var shb = new SoapHexBinary(hashBytes);
- return shb.ToString().ToLower();
- }
-
- public static TreeHash CalculateTreeHash(FileSystemInfo fileInfo, int blockSize, string algorithm)
- {
- if (fileInfo == null)
- throw new ArgumentNullException("fileInfo");
- if (String.IsNullOrWhiteSpace(fileInfo.FullName))
- throw new ArgumentException("fileInfo.FullName is empty", "fileInfo");
- if (blockSize <= 0)
- throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
- if (String.IsNullOrWhiteSpace(algorithm))
- throw new ArgumentNullException("algorithm");
- Contract.EndContractBlock();
-
- if (fileInfo is DirectoryInfo || !fileInfo.Exists)
- return TreeHash.Empty;
-
- return CalculateTreeHash(fileInfo.FullName, blockSize, algorithm);
- }
-
- /// <summary>
- /// Calculates a file's tree hash synchronously, using the specified block size
- /// </summary>
- /// <param name="filePath">Path to an existing file</param>
- /// <param name="blockSize">Block size used to calculate leaf hashes</param>
- /// <param name="algorithm"></param>
- /// <returns>A <see cref="TreeHash"/> with the block hashes and top hash</returns>
- public static TreeHash CalculateTreeHash(string filePath, int blockSize, string algorithm)
- {
- if (String.IsNullOrWhiteSpace(filePath))
- throw new ArgumentNullException("filePath");
- if (blockSize<=0)
- throw new ArgumentOutOfRangeException("blockSize","blockSize must be a value greater than zero ");
- if (String.IsNullOrWhiteSpace(algorithm))
- throw new ArgumentNullException("algorithm");
- Contract.EndContractBlock();
-
- var hash=CalculateTreeHashAsync(filePath, blockSize, algorithm, 2);
- return hash.Result;
- }
-
- public static async Task<TreeHash> CalculateTreeHashAsync(FileInfo fileInfo, int blockSize, string algorithm, byte parallelism)
- {
- if (fileInfo == null)
- throw new ArgumentNullException("fileInfo");
- if (String.IsNullOrWhiteSpace(fileInfo.FullName))
- throw new ArgumentNullException("fileInfo.FullName is empty","fileInfo");
- if (blockSize <= 0)
- throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
- if (String.IsNullOrWhiteSpace(algorithm))
- throw new ArgumentNullException("algorithm");
- Contract.EndContractBlock();
-
- return await CalculateTreeHashAsync(fileInfo.FullName, blockSize, algorithm, parallelism);
- }
-
-
- public static async Task<TreeHash> CalculateTreeHashAsync(string filePath, int blockSize,string algorithm, int parallelism)
- {
- if (String.IsNullOrWhiteSpace(filePath))
- throw new ArgumentNullException("filePath");
- if (blockSize <= 0)
- throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
- if (String.IsNullOrWhiteSpace(algorithm))
- throw new ArgumentNullException("algorithm");
- Contract.EndContractBlock();
-
- if (Log.IsDebugEnabled)
- Log.DebugFormat("Calc Signature [{0}]",filePath);
-
- //DON'T calculate hashes for folders
- if (Directory.Exists(filePath))
- return new TreeHash(algorithm);
- //The hash of a non-existent file is the empty hash
- if (!File.Exists(filePath))
- return new TreeHash(algorithm);
-
- //Calculate the hash of all blocks using a blockhash iterator
- using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, blockSize, true))
- {
- //Calculate the blocks asyncrhonously
- var hashes = await BlockHashAlgorithms.CalculateBlockHashesInPlacePFor(stream, blockSize, algorithm, parallelism);
-
- //And then proceed with creating and returning a TreeHash
- var length = stream.Length;
- var list = hashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();
-
- var treeHash = new TreeHash(algorithm)
- {
- Bytes = length,
- BlockSize = blockSize,
- Hashes = list,
- };
-
- string fileHash;
- var hasher = HashAlgorithm.Create("MD5");
- stream.Position = 0;
- treeHash.MD5= hasher.ComputeHash(stream).ToHashString();
-
- return treeHash;
- }
- }
-
-
- public static byte[] CalculateTopHash(IList<byte[]> hashMap, string algorithm)
- {
- if (hashMap == null)
- throw new ArgumentNullException("hashMap");
- if (String.IsNullOrWhiteSpace(algorithm))
- throw new ArgumentNullException("algorithm");
- Contract.EndContractBlock();
-
- var hashCount = hashMap.Count;
- //The tophash of an empty hashmap is an empty array
- if (hashCount == 0)
- return new byte[0];
- //The tophash of a one-item hashmap is the hash itself
- if (hashCount == 1)
- return hashMap[0];
-
- //Calculate the required number of leaf nodes
- var leafs =(int)Math.Pow(2, Math.Ceiling(Math.Log(hashCount,2)));
- //The size of all nodes is the same and equal to the size of the input hashes
- var hashSize = hashMap[0].Length;
-
- //If the hashmap containes fewer nodes than the required leaf count, we need to fill
- //the rest with empty blocks
- byte[] empty=null;
- if (hashCount < leafs)
- empty = new byte[hashSize];
-
- //New hashes will be stored in a dictionary keyed by their step to preserve order
- var newHashes=new ConcurrentDictionary<int, byte[]>();
-
- Parallel.For(0, leafs/2,
- (step, state) =>
- {
- using (var hasher = HashAlgorithm.Create(algorithm))
- {
- var i = step*2;
- var block1 = i <= hashCount - 1 ? hashMap[i] : empty;
- var block2 = i <= hashCount - 2 ? hashMap[i + 1] : empty;
-
- hasher.TransformBlock(block1, 0, block1.Length, null, 0);
- hasher.TransformFinalBlock(block2, 0, block2.Length);
-
- var finalHash = hasher.Hash;
- //Store the final value in its proper place
- newHashes[step] = finalHash;
- }
- });
-
- //Extract the hashes to a list ordered by their step
- var hashes = newHashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();
- return CalculateTopHash(hashes, algorithm);
- }
-
-
- public static byte[] CalculateHash(byte[] buffer,string algorithm)
- {
- if (buffer == null)
- throw new ArgumentNullException("buffer");
- if (String.IsNullOrWhiteSpace(algorithm))
- throw new ArgumentNullException("algorithm");
- Contract.EndContractBlock();
-
- using (var hasher = HashAlgorithm.Create(algorithm))
- {
- var hash = hasher.ComputeHash(buffer, 0, buffer.Length);
- return hash;
- }
- }
- }
-}
-
-
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="Signature.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above\r
+ * copyright notice, this list of conditions and the following\r
+ * disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above\r
+ * copyright notice, this list of conditions and the following\r
+ * disclaimer in the documentation and/or other materials\r
+ * provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+using System;\r
+using System.Collections.Concurrent;\r
+using System.Collections.Generic;\r
+using System.Diagnostics.Contracts;\r
+using System.IO;\r
+using System.Reflection;\r
+using System.Runtime.Remoting.Metadata.W3cXsd2001;\r
+using System.Threading;\r
+using System.Threading.Tasks;\r
+using System.Linq;\r
+using Pithos.Interfaces;\r
+using log4net;\r
+using OpenSSL.Core;\r
+using OpenSSL.Crypto;\r
+\r
+namespace Pithos.Network\r
+{\r
+ public static class Signature\r
+ {\r
+ private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
+ public const int BufferSize = 16384;\r
+\r
+// public const string MD5_EMPTY = "d41d8cd98f00b204e9800998ecf8427e";\r
+ public const string MERKLE_EMPTY = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";\r
+\r
+\r
+ public static byte[] ToBytes(this string hash)\r
+ {\r
+ var shb = SoapHexBinary.Parse(hash);\r
+ return shb.Value;\r
+ }\r
+\r
+ public static string ToHashString(this byte[] hashBytes)\r
+ {\r
+ var shb = new SoapHexBinary(hashBytes);\r
+ return shb.ToString().ToLower();\r
+ }\r
+\r
+ public static TreeHash CalculateTreeHash(FileSystemInfo fileInfo, int blockSize, string algorithm,byte parallelism,CancellationToken token,IProgress<HashProgress> progress )\r
+ {\r
+ if (fileInfo == null)\r
+ throw new ArgumentNullException("fileInfo");\r
+ if (String.IsNullOrWhiteSpace(fileInfo.FullName))\r
+ throw new ArgumentException("fileInfo.FullName is empty", "fileInfo");\r
+ if (blockSize <= 0)\r
+ throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");\r
+ if (String.IsNullOrWhiteSpace(algorithm))\r
+ throw new ArgumentNullException("algorithm");\r
+ Contract.EndContractBlock();\r
+ fileInfo.Refresh();\r
+ if (fileInfo is DirectoryInfo || !fileInfo.Exists)\r
+ return TreeHash.Empty;\r
+\r
+ return TaskEx.Run(async ()=>await CalculateTreeHashAsync(fileInfo, blockSize, algorithm, parallelism, token, progress)).Result;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Calculates a file's tree hash synchronously, using the specified block size\r
+ /// </summary>\r
+ /// <param name="filePath">Path to an existing file</param>\r
+ /// <param name="blockSize">Block size used to calculate leaf hashes</param>\r
+ /// <param name="algorithm"></param>\r
+ /// <returns>A <see cref="TreeHash"/> with the block hashes and top hash</returns>\r
+ public static TreeHash CalculateTreeHash(string filePath, int blockSize, string algorithm,CancellationToken token,IProgress<HashProgress> progress )\r
+ {\r
+ if (String.IsNullOrWhiteSpace(filePath))\r
+ throw new ArgumentNullException("filePath");\r
+ if (blockSize<=0)\r
+ throw new ArgumentOutOfRangeException("blockSize","blockSize must be a value greater than zero ");\r
+ if (String.IsNullOrWhiteSpace(algorithm))\r
+ throw new ArgumentNullException("algorithm");\r
+ Contract.EndContractBlock();\r
+\r
+ var info = FileInfoExtensions.FromPath(filePath);\r
+ var hash=TaskEx.Run(async ()=>await CalculateTreeHashAsync(info, blockSize, algorithm, 1,token,progress).ConfigureAwait(false)).Result;\r
+ return hash;\r
+ }\r
+ \r
+\r
+ public static Task<TreeHash> CalculateTreeHashAsync(string filePath, int blockSize, string algorithm, byte parallelism,CancellationToken token,IProgress<HashProgress> progress )\r
+ {\r
+ if (filePath== null)\r
+ throw new ArgumentNullException("filePath");\r
+ if (blockSize <= 0)\r
+ throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");\r
+ if (String.IsNullOrWhiteSpace(algorithm))\r
+ throw new ArgumentNullException("algorithm");\r
+ Contract.EndContractBlock();\r
+\r
+ var info = FileInfoExtensions.FromPath(filePath);\r
+ return CalculateTreeHashAsync(info, blockSize, algorithm, parallelism,token,progress);\r
+ }\r
+\r
+\r
+\r
+ public static async Task<TreeHash> CalculateTreeHashAsync(FileSystemInfo info, int blockSize,string algorithm, byte parallelism,CancellationToken token,IProgress<HashProgress> progress )\r
+ {\r
+ if (info==null)\r
+ throw new ArgumentNullException("info");\r
+ if (blockSize <= 0)\r
+ throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");\r
+ if (String.IsNullOrWhiteSpace(algorithm))\r
+ throw new ArgumentNullException("algorithm");\r
+ Contract.EndContractBlock();\r
+\r
+ var filePath = info.FullName;\r
+\r
+ if (Log.IsDebugEnabled)\r
+ Log.DebugFormat("Calc Signature [{0}]",filePath);\r
+\r
+ if (filePath.Split('/').Contains(".pithos.cache"))\r
+ throw new ArgumentException(String.Format("Trying to hash file from the cache folder: [{0}]",filePath));\r
+\r
+ //DON'T calculate hashes for folders\r
+ if (Directory.Exists(filePath))\r
+ return new TreeHash(algorithm);\r
+ //The hash of a non-existent file is the empty hash\r
+ if (!File.Exists(filePath))\r
+ return new TreeHash(algorithm); \r
+\r
+ if (info is FileInfo && (info as FileInfo).Length==0)\r
+ return new TreeHash(algorithm);\r
+\r
+ //Calculate the hash of all blocks using a blockhash iterator\r
+ using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, BufferSize, true))\r
+ {\r
+ //var md5 = new MD5BlockCalculator();\r
+ //Action<long, byte[], int> postAction = md5.PostBlock;\r
+ //Calculate the blocks asyncrhonously\r
+ var hashes = await BlockHashAlgorithms.CalculateBlockHashesInPlacePFor(stream, blockSize, algorithm, parallelism,token, progress).ConfigureAwait(false);\r
+ //var hashes = BlockHashAlgorithms.CalculateBlockHashesInPlacePFor(stream, blockSize, algorithm, parallelism, postAction, token, progress).Result; \r
+\r
+ //And then proceed with creating and returning a TreeHash\r
+ var length = stream.Length;\r
+ var list = hashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();\r
+\r
+ var treeHash = new TreeHash(algorithm)\r
+ {\r
+ Bytes = length,\r
+ BlockSize = blockSize,\r
+ Hashes = list,\r
+ };\r
+\r
+ //string fileHash;\r
+\r
+ //var md5Hash=md5.GetHash().Result;\r
+ //treeHash.MD5= md5Hash;\r
+\r
+ return treeHash;\r
+ }\r
+ }\r
+\r
+ \r
+ public static byte[] CalculateTopHash(IList<byte[]> hashMap, string algorithm)\r
+ {\r
+ if (hashMap == null)\r
+ throw new ArgumentNullException("hashMap");\r
+ if (String.IsNullOrWhiteSpace(algorithm))\r
+ throw new ArgumentNullException("algorithm");\r
+ Contract.EndContractBlock(); \r
+\r
+ var hashCount = hashMap.Count;\r
+ //TODO: Replace this calculation with a constant\r
+ //The tophash of an empty hashmap is the hash of an empty array\r
+ if (hashCount == 0)\r
+ {\r
+ using (var hasher = new MessageDigestContext(MessageDigest.CreateByName(algorithm)))\r
+ {\r
+ hasher.Init();\r
+ var emptyHash=hasher.Digest(new byte[0]);\r
+ return emptyHash;\r
+ } \r
+ }\r
+ //The tophash of a one-item hashmap is the hash itself\r
+ if (hashCount == 1)\r
+ return hashMap[0];\r
+\r
+ //Calculate the required number of leaf nodes\r
+ var leafs =(int)Math.Pow(2, Math.Ceiling(Math.Log(hashCount,2)));\r
+ //The size of all nodes is the same and equal to the size of the input hashes\r
+ var hashSize = hashMap[0].Length;\r
+\r
+ //If the hashmap containes fewer nodes than the required leaf count, we need to fill\r
+ //the rest with empty blocks\r
+ byte[] empty=null; \r
+ if (hashCount < leafs)\r
+ empty = new byte[hashSize];\r
+\r
+ //New hashes will be stored in a dictionary keyed by their step to preserve order\r
+ var newHashes=new ConcurrentDictionary<int, byte[]>(); \r
+ \r
+ Parallel.For(0, leafs/2,\r
+ (step, state) =>\r
+ {\r
+ using (var hasher = new MessageDigestContext(MessageDigest.CreateByName(algorithm)))\r
+ {\r
+ hasher.Init();\r
+ var i = step*2;\r
+ var block1 = i <= hashCount - 1 ? hashMap[i] : empty;\r
+ var block2 = i <= hashCount - 2 ? hashMap[i + 1] : empty;\r
+\r
+ hasher.Update(block1);\r
+ hasher.Update(block2);\r
+\r
+ var finalHash = hasher.DigestFinal();\r
+ //Store the final value in its proper place\r
+ newHashes[step] = finalHash;\r
+ }\r
+ });\r
+\r
+ //Extract the hashes to a list ordered by their step \r
+ var hashes = newHashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();\r
+ return CalculateTopHash(hashes, algorithm); \r
+ } \r
+ \r
+\r
+ public static byte[] CalculateHash(byte[] buffer,string algorithm)\r
+ {\r
+ if (buffer == null)\r
+ throw new ArgumentNullException("buffer");\r
+ if (String.IsNullOrWhiteSpace(algorithm))\r
+ throw new ArgumentNullException("algorithm");\r
+ Contract.EndContractBlock();\r
+\r
+ using (var hasher = new MessageDigestContext(MessageDigest.CreateByName(algorithm)))\r
+ {\r
+ hasher.Init();\r
+ var hash = hasher.Digest(buffer);\r
+ return hash;\r
+ } \r
+ }\r
+ }\r
+\r
+ public class HashProgress\r
+ {\r
+ public HashProgress(long current,long total)\r
+ {\r
+ Current = current;\r
+ Total = total;\r
+\r
+ }\r
+ public float Percentage { get\r
+ {\r
+ if (Current > Total)\r
+ return 1;\r
+ return Current/(float)Total;\r
+ }}\r
+ public long Current { get; set; }\r
+ public long Total { get; set; }\r
+\r
+ }\r
+\r
+}\r
+\r
+\r
\ No newline at end of file