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