root / trunk / Pithos.Network / Signature.cs @ bfc13ed8
History | View | Annotate | Download (11.9 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.Read, 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().ToLower(); |
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(IList<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 |
//The tophash of an empty hashmap is an empty array |
173 |
if (hashCount == 0) |
174 |
return new byte[0]; |
175 |
//The tophash of a one-item hashmap is the hash itself |
176 |
if (hashCount == 1) |
177 |
return hashMap[0]; |
178 |
|
179 |
//Calculate the required number of leaf nodes |
180 |
var leafs =(int)Math.Pow(2, Math.Ceiling(Math.Log(hashCount,2))); |
181 |
//The size of all nodes is the same and equal to the size of the input hashes |
182 |
var hashSize = hashMap[0].Length; |
183 |
|
184 |
//If the hashmap containes fewer nodes than the required leaf count, we need to fill |
185 |
//the rest with empty blocks |
186 |
byte[] empty=null; |
187 |
if (hashCount < leafs) |
188 |
empty = new byte[hashSize]; |
189 |
|
190 |
//New hashes will be stored in a dictionary keyed by their step to preserve order |
191 |
var newHashes=new ConcurrentDictionary<int, byte[]>(); |
192 |
|
193 |
Parallel.For(0, leafs/2, |
194 |
(step, state) => |
195 |
{ |
196 |
using (var hasher = HashAlgorithm.Create(algorithm)) |
197 |
{ |
198 |
var i = step*2; |
199 |
var block1 = i <= hashCount - 1 ? hashMap[i] : empty; |
200 |
var block2 = i <= hashCount - 2 ? hashMap[i + 1] : empty; |
201 |
|
202 |
hasher.TransformBlock(block1, 0, block1.Length, null, 0); |
203 |
hasher.TransformFinalBlock(block2, 0, block2.Length); |
204 |
|
205 |
var finalHash = hasher.Hash; |
206 |
//Store the final value in its proper place |
207 |
newHashes[step] = finalHash; |
208 |
} |
209 |
}); |
210 |
/* |
211 |
Parallel.For(0, leafs/2, |
212 |
() => HashAlgorithm.Create(algorithm), |
213 |
(step, state, hasher) => |
214 |
{ |
215 |
|
216 |
var i = step*2; |
217 |
var block1 = i <= hashCount - 1 ? hashMap[i]: empty; |
218 |
var block2 = i <= hashCount - 2 ? hashMap[i + 1] : empty; |
219 |
|
220 |
hasher.TransformBlock(block1, 0, block1.Length, null, 0); |
221 |
hasher.TransformFinalBlock(block2, 0, block2.Length); |
222 |
|
223 |
var finalHash = hasher.Hash; |
224 |
//Store the final value in its proper place |
225 |
newHashes[step] = finalHash; |
226 |
|
227 |
return hasher; |
228 |
}, |
229 |
hasher => hasher.Dispose()); |
230 |
*/ |
231 |
//Extract the hashes to a list ordered by their step |
232 |
var hashes = newHashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList(); |
233 |
return CalculateTopHash(hashes, algorithm); |
234 |
} |
235 |
|
236 |
|
237 |
|
238 |
/* public static string CalculateTopHash(string hashString, string algorithm) |
239 |
{ |
240 |
if (String.IsNullOrWhiteSpace(algorithm)) |
241 |
throw new ArgumentNullException("algorithm"); |
242 |
Contract.EndContractBlock(); |
243 |
if (String.IsNullOrWhiteSpace(hashString)) |
244 |
return String.Empty; |
245 |
|
246 |
using (var hasher = HashAlgorithm.Create(algorithm)) |
247 |
{ |
248 |
var bytes=Encoding.ASCII.GetBytes(hashString.ToLower()); |
249 |
var hash=hasher.ComputeHash(bytes); |
250 |
return hash.ToHashString(); |
251 |
} |
252 |
}*/ |
253 |
|
254 |
private static Task<ConcurrentDictionary<int, byte[]>> CalculateBlockHashesAsync(FileStream stream, int blockSize, string algorithm, ConcurrentDictionary<int, byte[]> hashes, int index = 0) |
255 |
{ |
256 |
if (stream==null) |
257 |
throw new ArgumentNullException("stream"); |
258 |
if (hashes==null) |
259 |
throw new ArgumentNullException("hashes"); |
260 |
if (String.IsNullOrWhiteSpace(algorithm)) |
261 |
throw new ArgumentNullException("algorithm"); |
262 |
if (blockSize <= 0) |
263 |
throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero "); |
264 |
if (index< 0) |
265 |
throw new ArgumentOutOfRangeException("index", "index must be a non-negative value"); |
266 |
Contract.EndContractBlock(); |
267 |
|
268 |
|
269 |
var buffer = new byte[blockSize]; |
270 |
return stream.ReadAsync(buffer, 0, blockSize).ContinueWith(t => |
271 |
{ |
272 |
var read = t.Result; |
273 |
|
274 |
var nextTask = read == blockSize |
275 |
? CalculateBlockHashesAsync(stream, blockSize, algorithm, hashes, index + 1) |
276 |
: Task.Factory.StartNew(() => hashes); |
277 |
|
278 |
using (var hasher = HashAlgorithm.Create(algorithm)) |
279 |
{ |
280 |
var hash = hasher.ComputeHash(buffer, 0, read); |
281 |
hashes[index]=hash; |
282 |
} |
283 |
return nextTask; |
284 |
}).Unwrap(); |
285 |
} |
286 |
|
287 |
|
288 |
|
289 |
public static byte[] CalculateHash(byte[] buffer,string algorithm) |
290 |
{ |
291 |
if (buffer == null) |
292 |
throw new ArgumentNullException("buffer"); |
293 |
if (String.IsNullOrWhiteSpace(algorithm)) |
294 |
throw new ArgumentNullException("algorithm"); |
295 |
Contract.EndContractBlock(); |
296 |
|
297 |
using (var hasher = HashAlgorithm.Create(algorithm)) |
298 |
{ |
299 |
var hash = hasher.ComputeHash(buffer, 0, buffer.Length); |
300 |
return hash; |
301 |
} |
302 |
} |
303 |
} |
304 |
} |
305 |
|
306 |
|
307 |
|