WIP:afs-service-commit
[jackhill/guix/guix.git] / nix / libutil / hash.cc
1 #include "config.h"
2
3 #include <iostream>
4 #include <cstring>
5
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;
29 hashSize = gcry_md_get_algo_dlen(type);
30
31 if (hashSize == 0) throw Error("unknown hash type");
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
75
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);
83 if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
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
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
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]);
120 }
121
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
132 Hash parseHash32(HashType ht, const string & s)
133 {
134 Hash hash(ht);
135 unsigned int len = hashLength32(ht);
136 assert(s.size() == len);
137
138 for (unsigned int n = 0; n < len; ++n) {
139 char c = s[len - n - 1];
140 unsigned char digit;
141 for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
142 if (base32Chars[digit] == c) break;
143 if (digit >= 32)
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);
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
184 /* The "hash context". */
185 struct Ctx
186 {
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;
201 };
202
203
204 static void start(HashType ht, Ctx & ctx)
205 {
206 gcry_error_t err;
207
208 err = gcry_md_open (&ctx.md_handle, ht, 0);
209 assert (err == GPG_ERR_NO_ERROR);
210 }
211
212
213 static void update(HashType ht, Ctx & ctx,
214 const unsigned char * bytes, unsigned int len)
215 {
216 gcry_md_write (ctx.md_handle, bytes, len);
217 }
218
219
220 static void finish(HashType ht, Ctx & ctx, unsigned char * hash)
221 {
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;
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 }
256
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 }
268
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;
323 else if (s == "sha512") return htSHA512;
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;
327 else return htUnknown;
328 }
329
330
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";
336 else if (ht == htSHA512) return "sha512";
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";
340 else throw Error("cannot print unknown hash type");
341 }
342
343
344 }