Statistics
| Branch: | Revision:

root / trunk / Pithos.Network / Signature.cs @ 9d2c0fc0

History | View | Annotate | Download (11.2 kB)

1
#region
2
/* -----------------------------------------------------------------------
3
 * <copyright file="Signature.cs" company="GRNet">
4
 * 
5
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or
8
 * without modification, are permitted provided that the following
9
 * conditions are met:
10
 *
11
 *   1. Redistributions of source code must retain the above
12
 *      copyright notice, this list of conditions and the following
13
 *      disclaimer.
14
 *
15
 *   2. Redistributions in binary form must reproduce the above
16
 *      copyright notice, this list of conditions and the following
17
 *      disclaimer in the documentation and/or other materials
18
 *      provided with the distribution.
19
 *
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
 * POSSIBILITY OF SUCH DAMAGE.
33
 *
34
 * The views and conclusions contained in the software and
35
 * documentation are those of the authors and should not be
36
 * interpreted as representing official policies, either expressed
37
 * or implied, of GRNET S.A.
38
 * </copyright>
39
 * -----------------------------------------------------------------------
40
 */
41
#endregion
42
using System;
43
using System.Collections.Concurrent;
44
using System.Collections.Generic;
45
using System.Diagnostics.Contracts;
46
using System.IO;
47
using System.Reflection;
48
using System.Runtime.Remoting.Metadata.W3cXsd2001;
49
using System.Security.Cryptography;
50
using System.Threading.Tasks;
51
using System.Linq;
52

    
53
namespace Pithos.Network
54
{
55
    public static class Signature
56
    {
57
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
58

    
59
        public static string CalculateMD5(FileInfo info)
60
        {
61
            if (info==null)
62
                throw new ArgumentNullException("info");
63
            if (String.IsNullOrWhiteSpace(info.FullName))
64
                throw new ArgumentException("info.FullName is empty","info");
65
            Contract.EndContractBlock();
66

    
67
            return CalculateMD5(info.FullName);
68
        }
69

    
70
        public static string CalculateMD5(string path)
71
        {
72
            if (String.IsNullOrWhiteSpace(path))
73
                throw new ArgumentNullException("path");
74
            Contract.EndContractBlock();
75

    
76
            //DON'T calculate hashes for folders
77
            if (Directory.Exists(path))
78
                return "";
79

    
80
            string hash;
81
            using (var hasher = MD5.Create())
82
            using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
83
            {
84
                var hashBytes = hasher.ComputeHash(stream);
85
                hash = hashBytes.ToHashString();
86
            }
87
            return hash;
88
        }
89

    
90
/*
91
        public static string BytesToString(byte[] hashBytes)
92
        {
93
            var shb=new SoapHexBinary(hashBytes);
94
            return shb.ToString();
95
            
96
        }
97

    
98

    
99
        public static byte[] StringToBytes(string hash)
100
        {
101
            var shb=SoapHexBinary.Parse(hash);
102
            return shb.Value;
103
        }
104
*/
105

    
106
        public static byte[] ToBytes(this string hash)
107
        {
108
            var shb = SoapHexBinary.Parse(hash);
109
            return shb.Value;
110
        }
111

    
112
        public static string ToHashString(this byte[] hashBytes)
113
        {
114
            var shb = new SoapHexBinary(hashBytes);
115
            return shb.ToString().ToLower();
116
        }
117

    
118
        public static TreeHash CalculateTreeHash(FileInfo fileInfo, int blockSize, string algorithm)
119
        {
120
            if (fileInfo==null)
121
                throw new ArgumentNullException("fileInfo");
122
            if (String.IsNullOrWhiteSpace(fileInfo.FullName))
123
                throw new ArgumentException("fileInfo.FullName is empty","fileInfo");
124
            if (blockSize <= 0)
125
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
126
            if (String.IsNullOrWhiteSpace(algorithm))
127
                throw new ArgumentNullException("algorithm");
128
            Contract.EndContractBlock();
129

    
130
            return CalculateTreeHash(fileInfo.FullName, blockSize, algorithm);
131
        }
132

    
133
        /// <summary>
134
        /// Calculates a file's tree hash synchronously, using the specified block size
135
        /// </summary>
136
        /// <param name="filePath">Path to an existing file</param>
137
        /// <param name="blockSize">Block size used to calculate leaf hashes</param>
138
        /// <param name="algorithm"></param>
139
        /// <returns>A <see cref="TreeHash"/> with the block hashes and top hash</returns>
140
        public static TreeHash CalculateTreeHash(string filePath, int blockSize, string algorithm)
141
        {
142
            if (String.IsNullOrWhiteSpace(filePath))
143
                throw new ArgumentNullException("filePath");
144
            if (blockSize<=0)
145
                throw new ArgumentOutOfRangeException("blockSize","blockSize must be a value greater than zero ");
146
            if (String.IsNullOrWhiteSpace(algorithm))
147
                throw new ArgumentNullException("algorithm");
148
            Contract.EndContractBlock();
149

    
150
            var hash=CalculateTreeHashAsync(filePath, blockSize, algorithm, 2);
151
            return hash.Result;
152
        }
153
        
154
        public static async Task<TreeHash> CalculateTreeHashAsync(FileInfo fileInfo, int blockSize, string algorithm, byte parallelism)
155
        {
156
            if (fileInfo == null)
157
                throw new ArgumentNullException("fileInfo");
158
            if (String.IsNullOrWhiteSpace(fileInfo.FullName))
159
                throw new ArgumentNullException("fileInfo.FullName is empty","fileInfo");
160
            if (blockSize <= 0)
161
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
162
            if (String.IsNullOrWhiteSpace(algorithm))
163
                throw new ArgumentNullException("algorithm");
164
            Contract.EndContractBlock();
165
            
166
            return await CalculateTreeHashAsync(fileInfo.FullName, blockSize, algorithm, parallelism);
167
        }
168

    
169

    
170
        public static async Task<TreeHash> CalculateTreeHashAsync(string filePath, int blockSize,string algorithm, int parallelism)
171
        {
172
            if (String.IsNullOrWhiteSpace(filePath))
173
                throw new ArgumentNullException("filePath");
174
            if (blockSize <= 0)
175
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
176
            if (String.IsNullOrWhiteSpace(algorithm))
177
                throw new ArgumentNullException("algorithm");
178
            Contract.EndContractBlock();
179

    
180
            //DON'T calculate hashes for folders
181
            if (Directory.Exists(filePath))
182
                return new TreeHash(algorithm);
183
            //The hash of a non-existent file is the empty hash
184
            if (!File.Exists(filePath))
185
                return new TreeHash(algorithm);
186

    
187
            //Calculate the hash of all blocks using a blockhash iterator
188
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, blockSize, true))
189
            {
190
                //Calculate the blocks asyncrhonously
191
                var hashes = await BlockHashAlgorithms.CalculateBlockHashesInPlacePFor(stream, blockSize, algorithm, parallelism);                
192

    
193
                //And then proceed with creating and returning a TreeHash
194
                var length = stream.Length;
195
                var list = hashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();
196

    
197
                var treeHash = new TreeHash(algorithm)
198
                {
199
                    Bytes = length,
200
                    BlockSize = blockSize,
201
                    Hashes = list
202
                };
203

    
204
                return treeHash;
205
            }
206
        }
207

    
208
        
209
        public static byte[] CalculateTopHash(IList<byte[]> hashMap, string algorithm)
210
        {
211
            if (hashMap == null)
212
                throw new ArgumentNullException("hashMap");
213
            if (String.IsNullOrWhiteSpace(algorithm))
214
                throw new ArgumentNullException("algorithm");
215
            Contract.EndContractBlock();            
216

    
217
            var hashCount = hashMap.Count;
218
            //The tophash of an empty hashmap is an empty array
219
            if (hashCount == 0)
220
                return new byte[0];
221
            //The tophash of a one-item hashmap is the hash itself
222
            if (hashCount == 1)
223
                return hashMap[0];
224

    
225
            //Calculate the required number of leaf nodes
226
            var leafs =(int)Math.Pow(2, Math.Ceiling(Math.Log(hashCount,2)));
227
            //The size of all nodes is the same and equal to the size of the input hashes
228
            var hashSize = hashMap[0].Length;
229

    
230
            //If the hashmap containes fewer nodes than the required leaf count, we need to fill
231
            //the rest with empty blocks
232
            byte[] empty=null;            
233
            if (hashCount < leafs)
234
                empty = new byte[hashSize];
235

    
236
            //New hashes will be stored in a dictionary keyed by their step to preserve order
237
            var newHashes=new ConcurrentDictionary<int, byte[]>();            
238
            
239
            Parallel.For(0, leafs/2,
240
                (step, state) =>
241
                {
242
                    using (var hasher = HashAlgorithm.Create(algorithm))
243
                    {
244
                        var i = step*2;
245
                        var block1 = i <= hashCount - 1 ? hashMap[i] : empty;
246
                        var block2 = i <= hashCount - 2 ? hashMap[i + 1] : empty;
247

    
248
                        hasher.TransformBlock(block1, 0, block1.Length, null, 0);
249
                        hasher.TransformFinalBlock(block2, 0, block2.Length);
250

    
251
                        var finalHash = hasher.Hash;
252
                        //Store the final value in its proper place
253
                        newHashes[step] = finalHash;
254
                    }
255
                });
256

    
257
            //Extract the hashes to a list ordered by their step 
258
            var hashes = newHashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();
259
            return CalculateTopHash(hashes, algorithm);                   
260
        }        
261
    
262

    
263
        public static byte[] CalculateHash(byte[] buffer,string algorithm)
264
        {
265
            if (buffer == null)
266
                throw new ArgumentNullException("buffer");
267
            if (String.IsNullOrWhiteSpace(algorithm))
268
                throw new ArgumentNullException("algorithm");
269
            Contract.EndContractBlock();
270

    
271
            using (var hasher = HashAlgorithm.Create(algorithm))
272
            {
273
                var hash = hasher.ComputeHash(buffer, 0, buffer.Length);
274
                return hash;
275
            }        
276
        }
277
    }
278
}
279

    
280

    
281