WIP:afs-service-commit
[jackhill/guix/guix.git] / nix / libutil / hash.cc
CommitLineData
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
15namespace nix {
16
17
18Hash::Hash()
19{
20 type = htUnknown;
21 hashSize = 0;
22 memset(hash, 0, maxHashSize);
23}
24
25
26Hash::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
37bool 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
46bool Hash::operator != (const Hash & h2) const
47{
48 return !(*this == h2);
49}
50
51
52bool 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
62const string base16Chars = "0123456789abcdef";
63
64
65string 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
76Hash 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
94unsigned int hashLength32(const Hash & hash)
95{
96 return (hash.hashSize * 8 - 1) / 5 + 1;
97}
98
99
100// omitted: E O U T
101const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
102
103
104string 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
126string printHash16or32(const Hash & hash)
127{
128 return hash.type == htMD5 ? printHash(hash) : printHash32(hash);
129}
130
131
36457566
LC
132Hash 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
156Hash 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
172bool 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
185struct 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
204static 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
213static 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
220static 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
229Hash 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
240Hash 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
262HashSink::HashSink(HashType ht) : ht(ht)
263{
264 ctx = new Ctx;
265 bytes = 0;
266 start(ht, *ctx);
267}
54c260e6 268
36457566
LC
269HashSink::~HashSink()
270{
271 bufPos = 0;
272 delete ctx;
273}
274
275void HashSink::write(const unsigned char * data, size_t len)
276{
277 bytes += len;
278 update(ht, *ctx, data, len);
279}
280
281HashResult HashSink::finish()
282{
283 flush();
284 Hash hash(ht);
285 nix::finish(ht, *ctx, hash.hash);
286 return HashResult(hash, bytes);
287}
288
289HashResult 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
299HashResult 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
308Hash 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
318HashType 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
331string 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}