Statistics
| Branch: | Revision:

root / trunk / Pithos.Network / Signature.cs @ 1caef52e

History | View | Annotate | Download (10.1 kB)

1
using System;
2
using System.Collections.Concurrent;
3
using System.Collections.Generic;
4
using System.Diagnostics.Contracts;
5
using System.IO;
6
using System.Runtime.Remoting.Metadata.W3cXsd2001;
7
using System.Security.Cryptography;
8
using System.Text;
9
using System.Threading.Tasks;
10
using System.Linq;
11

    
12
namespace Pithos.Network
13
{
14
    public static class Signature
15
    {
16
        public static string CalculateMD5(string path)
17
        {
18
            if (String.IsNullOrWhiteSpace(path))
19
                throw new ArgumentNullException("path");
20
            Contract.EndContractBlock();
21

    
22
            //DON'T calculate hashes for folders
23
            if (Directory.Exists(path))
24
                return "";
25

    
26
            string hash;
27
            using (var hasher = MD5.Create())
28
            using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true))
29
            {
30
                var hashBytes = hasher.ComputeHash(stream);
31
                hash = hashBytes.ToHashString();
32
            }
33
            return hash;
34
        }
35

    
36
/*
37
        public static string BytesToString(byte[] hashBytes)
38
        {
39
            var shb=new SoapHexBinary(hashBytes);
40
            return shb.ToString();
41
            
42
        }
43

    
44

    
45
        public static byte[] StringToBytes(string hash)
46
        {
47
            var shb=SoapHexBinary.Parse(hash);
48
            return shb.Value;
49
        }
50
*/
51

    
52
        public static byte[] ToBytes(this string hash)
53
        {
54
            var shb = SoapHexBinary.Parse(hash);
55
            return shb.Value;
56
        }
57

    
58
        public static string ToHashString(this byte[] hashBytes)
59
        {
60
            var shb = new SoapHexBinary(hashBytes);
61
            return shb.ToString();
62
        }
63

    
64

    
65
        /// <summary>
66
        /// Calculates a file's tree hash synchronously, using the specified block size
67
        /// </summary>
68
        /// <param name="filePath">Path to an existing file</param>
69
        /// <param name="blockSize">Block size used to calculate leaf hashes</param>
70
        /// <param name="algorithm"></param>
71
        /// <returns>A <see cref="TreeHash"/> with the block hashes and top hash</returns>
72
        public static TreeHash CalculateTreeHash(string filePath, int blockSize, string algorithm)
73
        {
74
            if (String.IsNullOrWhiteSpace(filePath))
75
                throw new ArgumentNullException("filePath");
76
            if (blockSize<=0)
77
                throw new ArgumentOutOfRangeException("blockSize","blockSize must be a value greater than zero ");
78
            if (String.IsNullOrWhiteSpace(algorithm))
79
                throw new ArgumentNullException("algorithm");
80
            Contract.EndContractBlock();
81

    
82
            //DON'T calculate hashes for folders
83
            if (Directory.Exists(filePath))
84
                return null;
85

    
86

    
87
            var list = new List<byte[]>();
88
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, blockSize, false))
89
            using (var hasher = HashAlgorithm.Create(algorithm))
90
            {
91
                int read;
92
                var buffer = new byte[blockSize];            
93
                while ((read = stream.Read(buffer, 0, blockSize)) > 0)
94
                {
95
                    var hash = hasher.ComputeHash(buffer, 0, read);
96
                    list.Add(hash);
97
                }
98
                return new TreeHash(algorithm) { Hashes = list,                    
99
                    BlockSize = blockSize, 
100
                    Bytes = stream.Length};
101
            }            
102
        }
103

    
104

    
105
        public static Task<TreeHash> CalculateTreeHashAsync(string filePath, int blockSize,string algorithm)
106
        {
107
            if (String.IsNullOrWhiteSpace(filePath))
108
                throw new ArgumentNullException("filePath");
109
            if (blockSize <= 0)
110
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
111
            if (String.IsNullOrWhiteSpace(algorithm))
112
                throw new ArgumentNullException("algorithm");
113
            Contract.EndContractBlock();
114

    
115
            //DON'T calculate hashes for folders
116
            if (Directory.Exists(filePath))
117
                return Task.Factory.StartNew(()=>new TreeHash(algorithm));
118

    
119

    
120
            var hashes = new ConcurrentDictionary<int, byte[]>();
121
            var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, blockSize, true);
122

    
123
            return CalculateBlockHashesAsync(stream, blockSize, algorithm,hashes)
124
                .ContinueWith(t => {
125
                                        var length = stream.Length;
126
                                       stream.Close();
127
                                       var list= t.Result.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();                                       
128
                                       return new TreeHash(algorithm) { Hashes = list,                                           
129
                                           BlockSize = blockSize, 
130
                                           Bytes = length };
131
                });
132
        }
133

    
134
      /*  public static byte[] CalculateTopHash(IEnumerable<byte[]> hashMap, string algorithm)
135
        {
136
            if (hashMap == null)
137
                throw new ArgumentNullException("hashMap");
138
            if (String.IsNullOrWhiteSpace(algorithm))
139
                throw new ArgumentNullException("algorithm");
140
            Contract.EndContractBlock();
141

    
142
            var hashCount = hashMap.Count();
143
            if (hashCount == 0)
144
                return null;
145
            using (var hasher = HashAlgorithm.Create(algorithm))
146
            {
147
                var i = 0;
148
                var count = hashCount;
149
                foreach (var block in hashMap)
150
                {
151
                    if (i++ != count - 1)
152
                        hasher.TransformBlock(block, 0, block.Length, null, 0);
153
                    else
154
                        hasher.TransformFinalBlock(block, 0, block.Length);
155
                }
156

    
157
                var finalHash = hasher.Hash;
158

    
159
                return finalHash;
160
            }
161
        }*/
162
        
163
        public static byte[] CalculateTopHash(IEnumerable<byte[]> hashMap, string algorithm)
164
        {
165
            if (hashMap == null)
166
                throw new ArgumentNullException("hashMap");
167
            if (String.IsNullOrWhiteSpace(algorithm))
168
                throw new ArgumentNullException("algorithm");
169
            Contract.EndContractBlock();
170

    
171
            var hashCount = hashMap.Count();
172
            if (hashCount == 0)
173
                return null;
174
            if (hashCount == 1)
175
                return hashMap.First();
176

    
177
            var newHashes=new List<byte[]>();
178
            var leafs =Math.Pow(2, Math.Log(hashCount,2)+1);
179
            for (int i = 0; i < leafs;i+=2 )
180
            {
181
                using (var hasher = HashAlgorithm.Create(algorithm))
182
                {
183
                    var block1 = i >hashCount - 1 ? new byte[hashMap.First().Length] : hashMap.ElementAt(i);
184
                    var block2 = i>hashCount-2 ? new byte[block1.Length] : hashMap.ElementAt(i+1);
185
                    
186
                    hasher.TransformBlock(block1, 0, block1.Length, null, 0);
187
                    hasher.TransformFinalBlock(block2, 0, block2.Length);                        
188
                    var finalHash = hasher.Hash;
189
                    newHashes.Add(finalHash);
190
                }                    
191
            }
192
            return CalculateTopHash(newHashes, algorithm);                   
193
        }
194

    
195
        
196

    
197
        public static string CalculateTopHash(string hashString, string algorithm)
198
        {
199
            if (String.IsNullOrWhiteSpace(algorithm))
200
                throw new ArgumentNullException("algorithm");
201
            Contract.EndContractBlock();
202
            if (String.IsNullOrWhiteSpace(hashString))
203
                return String.Empty;
204

    
205
            using (var hasher = HashAlgorithm.Create(algorithm))
206
            {
207
                var bytes=Encoding.ASCII.GetBytes(hashString.ToLower());
208
                var hash=hasher.ComputeHash(bytes);
209
                return hash.ToHashString();
210
            }
211
        }
212

    
213
        private static Task<ConcurrentDictionary<int, byte[]>> CalculateBlockHashesAsync(FileStream stream, int blockSize, string algorithm, ConcurrentDictionary<int, byte[]> hashes, int index = 0)
214
        {
215
            if (stream==null)
216
                throw new ArgumentNullException("stream");
217
            if (hashes==null)
218
                throw new ArgumentNullException("hashes");
219
            if (String.IsNullOrWhiteSpace(algorithm))
220
                throw new ArgumentNullException("algorithm");
221
            if (blockSize <= 0)
222
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
223
            if (index< 0)
224
                throw new ArgumentOutOfRangeException("index", "index must be a non-negative value");
225
            Contract.EndContractBlock();
226

    
227

    
228
            var buffer = new byte[blockSize];
229
            return stream.ReadAsync(buffer, 0, blockSize).ContinueWith(t =>
230
            {
231
                var read = t.Result;
232

    
233
                var nextTask = read == blockSize
234
                                    ? CalculateBlockHashesAsync(stream, blockSize, algorithm, hashes, index + 1) 
235
                                    : Task.Factory.StartNew(() => hashes);
236

    
237
                using (var hasher = HashAlgorithm.Create(algorithm))
238
                {
239
                    var hash = hasher.ComputeHash(buffer, 0, read);
240
                    hashes[index]=hash;
241
                }
242
                return nextTask;
243
            }).Unwrap();
244
        }
245

    
246
    
247

    
248
        public static byte[] CalculateHash(byte[] buffer,string algorithm)
249
        {
250
            if (buffer == null)
251
                throw new ArgumentNullException("buffer");
252
            if (String.IsNullOrWhiteSpace(algorithm))
253
                throw new ArgumentNullException("algorithm");
254
            Contract.EndContractBlock();
255

    
256
            using (var hasher = HashAlgorithm.Create(algorithm))
257
            {
258
                var hash = hasher.ComputeHash(buffer, 0, buffer.Length);
259
                return hash;
260
            }        
261
        }
262
    }
263
}
264

    
265

    
266