projects/guix·blob python-team · refs

366 lines · 8.1 KiB

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