Commit | Line | Data |
---|---|---|
36457566 LC |
1 | #include "config.h" |
2 | ||
3 | #include <iostream> | |
4 | #include <cstring> | |
5 | ||
6 | #ifdef HAVE_OPENSSL | |
7 | #include <openssl/md5.h> | |
8 | #include <openssl/sha.h> | |
9 | #else | |
10 | extern "C" { | |
11 | #include "md5.h" | |
12 | #include "sha1.h" | |
13 | #include "sha256.h" | |
29d3242e | 14 | #include "sha512.h" |
36457566 LC |
15 | } |
16 | #endif | |
17 | ||
18 | #include "hash.hh" | |
19 | #include "archive.hh" | |
20 | #include "util.hh" | |
21 | ||
22 | #include <sys/types.h> | |
23 | #include <sys/stat.h> | |
24 | #include <fcntl.h> | |
25 | ||
26 | ||
27 | namespace nix { | |
28 | ||
29 | ||
30 | Hash::Hash() | |
31 | { | |
32 | type = htUnknown; | |
33 | hashSize = 0; | |
34 | memset(hash, 0, maxHashSize); | |
35 | } | |
36 | ||
37 | ||
38 | Hash::Hash(HashType type) | |
39 | { | |
40 | this->type = type; | |
41 | if (type == htMD5) hashSize = md5HashSize; | |
42 | else if (type == htSHA1) hashSize = sha1HashSize; | |
43 | else if (type == htSHA256) hashSize = sha256HashSize; | |
29d3242e | 44 | else if (type == htSHA512) hashSize = sha512HashSize; |
36457566 LC |
45 | else throw Error("unknown hash type"); |
46 | assert(hashSize <= maxHashSize); | |
47 | memset(hash, 0, maxHashSize); | |
48 | } | |
49 | ||
50 | ||
51 | bool Hash::operator == (const Hash & h2) const | |
52 | { | |
53 | if (hashSize != h2.hashSize) return false; | |
54 | for (unsigned int i = 0; i < hashSize; i++) | |
55 | if (hash[i] != h2.hash[i]) return false; | |
56 | return true; | |
57 | } | |
58 | ||
59 | ||
60 | bool Hash::operator != (const Hash & h2) const | |
61 | { | |
62 | return !(*this == h2); | |
63 | } | |
64 | ||
65 | ||
66 | bool Hash::operator < (const Hash & h) const | |
67 | { | |
68 | for (unsigned int i = 0; i < hashSize; i++) { | |
69 | if (hash[i] < h.hash[i]) return true; | |
70 | if (hash[i] > h.hash[i]) return false; | |
71 | } | |
72 | return false; | |
73 | } | |
74 | ||
75 | ||
76 | const string base16Chars = "0123456789abcdef"; | |
77 | ||
78 | ||
79 | string printHash(const Hash & hash) | |
80 | { | |
81 | char buf[hash.hashSize * 2]; | |
82 | for (unsigned int i = 0; i < hash.hashSize; i++) { | |
83 | buf[i * 2] = base16Chars[hash.hash[i] >> 4]; | |
84 | buf[i * 2 + 1] = base16Chars[hash.hash[i] & 0x0f]; | |
85 | } | |
86 | return string(buf, hash.hashSize * 2); | |
87 | } | |
88 | ||
54c260e6 | 89 | |
36457566 LC |
90 | Hash parseHash(HashType ht, const string & s) |
91 | { | |
92 | Hash hash(ht); | |
93 | if (s.length() != hash.hashSize * 2) | |
94 | throw Error(format("invalid hash `%1%'") % s); | |
95 | for (unsigned int i = 0; i < hash.hashSize; i++) { | |
96 | string s2(s, i * 2, 2); | |
54c260e6 | 97 | if (!isxdigit(s2[0]) || !isxdigit(s2[1])) |
36457566 LC |
98 | throw Error(format("invalid hash `%1%'") % s); |
99 | std::istringstream str(s2); | |
100 | int n; | |
101 | str >> std::hex >> n; | |
102 | hash.hash[i] = n; | |
103 | } | |
104 | return hash; | |
105 | } | |
106 | ||
107 | ||
36457566 LC |
108 | unsigned int hashLength32(const Hash & hash) |
109 | { | |
110 | return (hash.hashSize * 8 - 1) / 5 + 1; | |
111 | } | |
112 | ||
113 | ||
114 | // omitted: E O U T | |
115 | const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"; | |
116 | ||
117 | ||
118 | string printHash32(const Hash & hash) | |
119 | { | |
120 | Hash hash2(hash); | |
121 | unsigned int len = hashLength32(hash); | |
122 | ||
54c260e6 LC |
123 | string s; |
124 | s.reserve(len); | |
125 | ||
126 | for (int n = len - 1; n >= 0; n--) { | |
127 | unsigned int b = n * 5; | |
128 | unsigned int i = b / 8; | |
129 | unsigned int j = b % 8; | |
130 | unsigned char c = | |
131 | (hash.hash[i] >> j) | |
132 | | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j)); | |
133 | s.push_back(base32Chars[c & 0x1f]); | |
36457566 LC |
134 | } |
135 | ||
36457566 LC |
136 | return s; |
137 | } | |
138 | ||
139 | ||
140 | string printHash16or32(const Hash & hash) | |
141 | { | |
142 | return hash.type == htMD5 ? printHash(hash) : printHash32(hash); | |
143 | } | |
144 | ||
145 | ||
36457566 LC |
146 | Hash parseHash32(HashType ht, const string & s) |
147 | { | |
148 | Hash hash(ht); | |
54c260e6 LC |
149 | unsigned int len = hashLength32(ht); |
150 | assert(s.size() == len); | |
36457566 | 151 | |
54c260e6 LC |
152 | for (unsigned int n = 0; n < len; ++n) { |
153 | char c = s[len - n - 1]; | |
36457566 LC |
154 | unsigned char digit; |
155 | for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ | |
54c260e6 | 156 | if (base32Chars[digit] == c) break; |
36457566 | 157 | if (digit >= 32) |
54c260e6 LC |
158 | throw Error(format("invalid base-32 hash '%1%'") % s); |
159 | unsigned int b = n * 5; | |
160 | unsigned int i = b / 8; | |
161 | unsigned int j = b % 8; | |
162 | hash.hash[i] |= digit << j; | |
163 | if (i < hash.hashSize - 1) hash.hash[i + 1] |= digit >> (8 - j); | |
36457566 LC |
164 | } |
165 | ||
166 | return hash; | |
167 | } | |
168 | ||
169 | ||
170 | Hash parseHash16or32(HashType ht, const string & s) | |
171 | { | |
172 | Hash hash(ht); | |
173 | if (s.size() == hash.hashSize * 2) | |
174 | /* hexadecimal representation */ | |
175 | hash = parseHash(ht, s); | |
176 | else if (s.size() == hashLength32(hash)) | |
177 | /* base-32 representation */ | |
178 | hash = parseHash32(ht, s); | |
179 | else | |
180 | throw Error(format("hash `%1%' has wrong length for hash type `%2%'") | |
181 | % s % printHashType(ht)); | |
182 | return hash; | |
183 | } | |
184 | ||
185 | ||
186 | bool isHash(const string & s) | |
187 | { | |
188 | if (s.length() != 32) return false; | |
189 | for (int i = 0; i < 32; i++) { | |
190 | char c = s[i]; | |
191 | if (!((c >= '0' && c <= '9') || | |
192 | (c >= 'a' && c <= 'f'))) | |
193 | return false; | |
194 | } | |
195 | return true; | |
196 | } | |
197 | ||
198 | ||
199 | struct Ctx | |
200 | { | |
201 | MD5_CTX md5; | |
202 | SHA_CTX sha1; | |
203 | SHA256_CTX sha256; | |
29d3242e | 204 | SHA512_CTX sha512; |
36457566 LC |
205 | }; |
206 | ||
207 | ||
208 | static void start(HashType ht, Ctx & ctx) | |
209 | { | |
210 | if (ht == htMD5) MD5_Init(&ctx.md5); | |
211 | else if (ht == htSHA1) SHA1_Init(&ctx.sha1); | |
212 | else if (ht == htSHA256) SHA256_Init(&ctx.sha256); | |
29d3242e | 213 | else if (ht == htSHA512) SHA512_Init(&ctx.sha512); |
36457566 LC |
214 | } |
215 | ||
216 | ||
217 | static void update(HashType ht, Ctx & ctx, | |
218 | const unsigned char * bytes, unsigned int len) | |
219 | { | |
220 | if (ht == htMD5) MD5_Update(&ctx.md5, bytes, len); | |
221 | else if (ht == htSHA1) SHA1_Update(&ctx.sha1, bytes, len); | |
222 | else if (ht == htSHA256) SHA256_Update(&ctx.sha256, bytes, len); | |
29d3242e | 223 | else if (ht == htSHA512) SHA512_Update(&ctx.sha512, bytes, len); |
36457566 LC |
224 | } |
225 | ||
226 | ||
227 | static void finish(HashType ht, Ctx & ctx, unsigned char * hash) | |
228 | { | |
229 | if (ht == htMD5) MD5_Final(hash, &ctx.md5); | |
230 | else if (ht == htSHA1) SHA1_Final(hash, &ctx.sha1); | |
231 | else if (ht == htSHA256) SHA256_Final(hash, &ctx.sha256); | |
29d3242e | 232 | else if (ht == htSHA512) SHA512_Final(hash, &ctx.sha512); |
36457566 LC |
233 | } |
234 | ||
235 | ||
236 | Hash hashString(HashType ht, const string & s) | |
237 | { | |
238 | Ctx ctx; | |
239 | Hash hash(ht); | |
240 | start(ht, ctx); | |
241 | update(ht, ctx, (const unsigned char *) s.data(), s.length()); | |
242 | finish(ht, ctx, hash.hash); | |
243 | return hash; | |
244 | } | |
245 | ||
246 | ||
247 | Hash hashFile(HashType ht, const Path & path) | |
248 | { | |
249 | Ctx ctx; | |
250 | Hash hash(ht); | |
251 | start(ht, ctx); | |
252 | ||
253 | AutoCloseFD fd = open(path.c_str(), O_RDONLY); | |
254 | if (fd == -1) throw SysError(format("opening file `%1%'") % path); | |
255 | ||
256 | unsigned char buf[8192]; | |
257 | ssize_t n; | |
258 | while ((n = read(fd, buf, sizeof(buf)))) { | |
259 | checkInterrupt(); | |
260 | if (n == -1) throw SysError(format("reading file `%1%'") % path); | |
261 | update(ht, ctx, buf, n); | |
262 | } | |
54c260e6 | 263 | |
36457566 LC |
264 | finish(ht, ctx, hash.hash); |
265 | return hash; | |
266 | } | |
267 | ||
268 | ||
269 | HashSink::HashSink(HashType ht) : ht(ht) | |
270 | { | |
271 | ctx = new Ctx; | |
272 | bytes = 0; | |
273 | start(ht, *ctx); | |
274 | } | |
54c260e6 | 275 | |
36457566 LC |
276 | HashSink::~HashSink() |
277 | { | |
278 | bufPos = 0; | |
279 | delete ctx; | |
280 | } | |
281 | ||
282 | void HashSink::write(const unsigned char * data, size_t len) | |
283 | { | |
284 | bytes += len; | |
285 | update(ht, *ctx, data, len); | |
286 | } | |
287 | ||
288 | HashResult HashSink::finish() | |
289 | { | |
290 | flush(); | |
291 | Hash hash(ht); | |
292 | nix::finish(ht, *ctx, hash.hash); | |
293 | return HashResult(hash, bytes); | |
294 | } | |
295 | ||
296 | HashResult HashSink::currentHash() | |
297 | { | |
298 | flush(); | |
299 | Ctx ctx2 = *ctx; | |
300 | Hash hash(ht); | |
301 | nix::finish(ht, ctx2, hash.hash); | |
302 | return HashResult(hash, bytes); | |
303 | } | |
304 | ||
305 | ||
306 | HashResult hashPath( | |
307 | HashType ht, const Path & path, PathFilter & filter) | |
308 | { | |
309 | HashSink sink(ht); | |
310 | dumpPath(path, sink, filter); | |
311 | return sink.finish(); | |
312 | } | |
313 | ||
314 | ||
315 | Hash compressHash(const Hash & hash, unsigned int newSize) | |
316 | { | |
317 | Hash h; | |
318 | h.hashSize = newSize; | |
319 | for (unsigned int i = 0; i < hash.hashSize; ++i) | |
320 | h.hash[i % newSize] ^= hash.hash[i]; | |
321 | return h; | |
322 | } | |
323 | ||
324 | ||
325 | HashType parseHashType(const string & s) | |
326 | { | |
327 | if (s == "md5") return htMD5; | |
328 | else if (s == "sha1") return htSHA1; | |
329 | else if (s == "sha256") return htSHA256; | |
29d3242e | 330 | else if (s == "sha512") return htSHA512; |
36457566 LC |
331 | else return htUnknown; |
332 | } | |
333 | ||
54c260e6 | 334 | |
36457566 LC |
335 | string printHashType(HashType ht) |
336 | { | |
337 | if (ht == htMD5) return "md5"; | |
338 | else if (ht == htSHA1) return "sha1"; | |
339 | else if (ht == htSHA256) return "sha256"; | |
29d3242e | 340 | else if (ht == htSHA512) return "sha512"; |
36457566 LC |
341 | else throw Error("cannot print unknown hash type"); |
342 | } | |
343 | ||
54c260e6 | 344 | |
36457566 | 345 | } |