Statistics
| Branch: | Revision:

root / trunk / Pithos.Network / Signature.cs @ 34bdb91d

History | View | Annotate | Download (9.3 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.Threading.Tasks;
9
using System.Linq;
10

    
11
namespace Pithos.Network
12
{
13
    public static class Signature
14
    {
15
        public static string CalculateMD5(FileInfo info)
16
        {
17
            if (info==null)
18
                throw new ArgumentNullException("info");
19
            if (String.IsNullOrWhiteSpace(info.FullName))
20
                throw new ArgumentException("info.FullName is empty","info");
21
            Contract.EndContractBlock();
22

    
23
            return CalculateMD5(info.FullName);
24
        }
25

    
26
        public static string CalculateMD5(string path)
27
        {
28
            if (String.IsNullOrWhiteSpace(path))
29
                throw new ArgumentNullException("path");
30
            Contract.EndContractBlock();
31

    
32
            //DON'T calculate hashes for folders
33
            if (Directory.Exists(path))
34
                return "";
35

    
36
            string hash;
37
            using (var hasher = MD5.Create())
38
            using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
39
            {
40
                var hashBytes = hasher.ComputeHash(stream);
41
                hash = hashBytes.ToHashString();
42
            }
43
            return hash;
44
        }
45

    
46
/*
47
        public static string BytesToString(byte[] hashBytes)
48
        {
49
            var shb=new SoapHexBinary(hashBytes);
50
            return shb.ToString();
51
            
52
        }
53

    
54

    
55
        public static byte[] StringToBytes(string hash)
56
        {
57
            var shb=SoapHexBinary.Parse(hash);
58
            return shb.Value;
59
        }
60
*/
61

    
62
        public static byte[] ToBytes(this string hash)
63
        {
64
            var shb = SoapHexBinary.Parse(hash);
65
            return shb.Value;
66
        }
67

    
68
        public static string ToHashString(this byte[] hashBytes)
69
        {
70
            var shb = new SoapHexBinary(hashBytes);
71
            return shb.ToString().ToLower();
72
        }
73

    
74
        public static TreeHash CalculateTreeHash(FileInfo fileInfo, int blockSize, string algorithm)
75
        {
76
            if (fileInfo==null)
77
                throw new ArgumentNullException("fileInfo");
78
            if (String.IsNullOrWhiteSpace(fileInfo.FullName))
79
                throw new ArgumentException("fileInfo.FullName is empty","fileInfo");
80
            if (blockSize <= 0)
81
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
82
            if (String.IsNullOrWhiteSpace(algorithm))
83
                throw new ArgumentNullException("algorithm");
84
            Contract.EndContractBlock();
85

    
86
            return CalculateTreeHash(fileInfo.FullName, blockSize, algorithm);
87
        }
88

    
89
        /// <summary>
90
        /// Calculates a file's tree hash synchronously, using the specified block size
91
        /// </summary>
92
        /// <param name="filePath">Path to an existing file</param>
93
        /// <param name="blockSize">Block size used to calculate leaf hashes</param>
94
        /// <param name="algorithm"></param>
95
        /// <returns>A <see cref="TreeHash"/> with the block hashes and top hash</returns>
96
        public static TreeHash CalculateTreeHash(string filePath, int blockSize, string algorithm)
97
        {
98
            if (String.IsNullOrWhiteSpace(filePath))
99
                throw new ArgumentNullException("filePath");
100
            if (blockSize<=0)
101
                throw new ArgumentOutOfRangeException("blockSize","blockSize must be a value greater than zero ");
102
            if (String.IsNullOrWhiteSpace(algorithm))
103
                throw new ArgumentNullException("algorithm");
104
            Contract.EndContractBlock();
105

    
106
            var hash=CalculateTreeHashAsync(filePath, blockSize, algorithm, 2);
107
            return hash.Result;
108
        }
109
        
110
        public static async Task<TreeHash> CalculateTreeHashAsync(FileInfo fileInfo, int blockSize, string algorithm, byte parallelism)
111
        {
112
            if (fileInfo == null)
113
                throw new ArgumentNullException("fileInfo");
114
            if (String.IsNullOrWhiteSpace(fileInfo.FullName))
115
                throw new ArgumentNullException("fileInfo.FullName is empty","fileInfo");
116
            if (blockSize <= 0)
117
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
118
            if (String.IsNullOrWhiteSpace(algorithm))
119
                throw new ArgumentNullException("algorithm");
120
            Contract.EndContractBlock();
121
            
122
            return await CalculateTreeHashAsync(fileInfo.FullName, blockSize, algorithm, parallelism);
123
        }
124

    
125

    
126
        public static async Task<TreeHash> CalculateTreeHashAsync(string filePath, int blockSize,string algorithm, int parallelism)
127
        {
128
            if (String.IsNullOrWhiteSpace(filePath))
129
                throw new ArgumentNullException("filePath");
130
            if (blockSize <= 0)
131
                throw new ArgumentOutOfRangeException("blockSize", "blockSize must be a value greater than zero ");
132
            if (String.IsNullOrWhiteSpace(algorithm))
133
                throw new ArgumentNullException("algorithm");
134
            Contract.EndContractBlock();
135

    
136
            //DON'T calculate hashes for folders
137
            if (Directory.Exists(filePath))
138
                return new TreeHash(algorithm);
139
            //The hash of a non-existent file is the empty hash
140
            if (!File.Exists(filePath))
141
                return new TreeHash(algorithm);
142

    
143
            //Calculate the hash of all blocks using a blockhash iterator
144
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, blockSize, true))
145
            {
146
                //Calculate the blocks asyncrhonously
147
                var hashes = await BlockHashAlgorithms.CalculateBlockHashesAgentAsync(stream, blockSize, algorithm, parallelism);                
148

    
149
                //And then proceed with creating and returning a TreeHash
150
                var length = stream.Length;
151
                var list = hashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();
152

    
153
                var treeHash = new TreeHash(algorithm)
154
                {
155
                    Bytes = length,
156
                    BlockSize = blockSize,
157
                    Hashes = list
158
                };
159

    
160
                return treeHash;
161
            }
162
        }
163

    
164
        
165
        public static byte[] CalculateTopHash(IList<byte[]> hashMap, string algorithm)
166
        {
167
            if (hashMap == null)
168
                throw new ArgumentNullException("hashMap");
169
            if (String.IsNullOrWhiteSpace(algorithm))
170
                throw new ArgumentNullException("algorithm");
171
            Contract.EndContractBlock();            
172

    
173
            var hashCount = hashMap.Count;
174
            //The tophash of an empty hashmap is an empty array
175
            if (hashCount == 0)
176
                return new byte[0];
177
            //The tophash of a one-item hashmap is the hash itself
178
            if (hashCount == 1)
179
                return hashMap[0];
180

    
181
            //Calculate the required number of leaf nodes
182
            var leafs =(int)Math.Pow(2, Math.Ceiling(Math.Log(hashCount,2)));
183
            //The size of all nodes is the same and equal to the size of the input hashes
184
            var hashSize = hashMap[0].Length;
185

    
186
            //If the hashmap containes fewer nodes than the required leaf count, we need to fill
187
            //the rest with empty blocks
188
            byte[] empty=null;            
189
            if (hashCount < leafs)
190
                empty = new byte[hashSize];
191

    
192
            //New hashes will be stored in a dictionary keyed by their step to preserve order
193
            var newHashes=new ConcurrentDictionary<int, byte[]>();            
194
            
195
            Parallel.For(0, leafs/2,
196
                (step, state) =>
197
                {
198
                    using (var hasher = HashAlgorithm.Create(algorithm))
199
                    {
200
                        var i = step*2;
201
                        var block1 = i <= hashCount - 1 ? hashMap[i] : empty;
202
                        var block2 = i <= hashCount - 2 ? hashMap[i + 1] : empty;
203

    
204
                        hasher.TransformBlock(block1, 0, block1.Length, null, 0);
205
                        hasher.TransformFinalBlock(block2, 0, block2.Length);
206

    
207
                        var finalHash = hasher.Hash;
208
                        //Store the final value in its proper place
209
                        newHashes[step] = finalHash;
210
                    }
211
                });
212

    
213
            //Extract the hashes to a list ordered by their step 
214
            var hashes = newHashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();
215
            return CalculateTopHash(hashes, algorithm);                   
216
        }        
217
    
218

    
219
        public static byte[] CalculateHash(byte[] buffer,string algorithm)
220
        {
221
            if (buffer == null)
222
                throw new ArgumentNullException("buffer");
223
            if (String.IsNullOrWhiteSpace(algorithm))
224
                throw new ArgumentNullException("algorithm");
225
            Contract.EndContractBlock();
226

    
227
            using (var hasher = HashAlgorithm.Create(algorithm))
228
            {
229
                var hash = hasher.ComputeHash(buffer, 0, buffer.Length);
230
                return hash;
231
            }        
232
        }
233
    }
234
}
235

    
236

    
237