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 |
|