Merge branch 'master' into staging
[jackhill/guix/guix.git] / nix / libutil / hash.cc
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"
14 #include "sha512.h"
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;
44 else if (type == htSHA512) hashSize = sha512HashSize;
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
89
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);
97 if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
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
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
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]);
134 }
135
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
146 Hash parseHash32(HashType ht, const string & s)
147 {
148 Hash hash(ht);
149 unsigned int len = hashLength32(ht);
150 assert(s.size() == len);
151
152 for (unsigned int n = 0; n < len; ++n) {
153 char c = s[len - n - 1];
154 unsigned char digit;
155 for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
156 if (base32Chars[digit] == c) break;
157 if (digit >= 32)
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);
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;
204 SHA512_CTX sha512;
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);
213 else if (ht == htSHA512) SHA512_Init(&ctx.sha512);
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);
223 else if (ht == htSHA512) SHA512_Update(&ctx.sha512, bytes, len);
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);
232 else if (ht == htSHA512) SHA512_Final(hash, &ctx.sha512);
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 }
263
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 }
275
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;
330 else if (s == "sha512") return htSHA512;
331 else return htUnknown;
332 }
333
334
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";
340 else if (ht == htSHA512) return "sha512";
341 else throw Error("cannot print unknown hash type");
342 }
343
344
345 }