10 #include <sys/types.h>
22 memset(hash
, 0, maxHashSize
);
26 Hash::Hash(HashType type
)
29 hashSize
= gcry_md_get_algo_dlen(type
);
31 if (hashSize
== 0) throw Error("unknown hash type");
32 assert(hashSize
<= maxHashSize
);
33 memset(hash
, 0, maxHashSize
);
37 bool Hash::operator == (const Hash
& h2
) const
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;
46 bool Hash::operator != (const Hash
& h2
) const
48 return !(*this == h2
);
52 bool Hash::operator < (const Hash
& h
) const
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;
62 const string base16Chars
= "0123456789abcdef";
65 string
printHash(const Hash
& hash
)
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];
72 return string(buf
, hash
.hashSize
* 2);
76 Hash
parseHash(HashType ht
, const string
& s
)
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
);
94 unsigned int hashLength32(const Hash
& hash
)
96 return (hash
.hashSize
* 8 - 1) / 5 + 1;
101 const string base32Chars
= "0123456789abcdfghijklmnpqrsvwxyz";
104 string
printHash32(const Hash
& hash
)
107 unsigned int len
= hashLength32(hash
);
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;
118 | (i
>= hash
.hashSize
- 1 ? 0 : hash
.hash
[i
+ 1] << (8 - j
));
119 s
.push_back(base32Chars
[c
& 0x1f]);
126 string
printHash16or32(const Hash
& hash
)
128 return hash
.type
== htMD5
? printHash(hash
) : printHash32(hash
);
132 Hash
parseHash32(HashType ht
, const string
& s
)
135 unsigned int len
= hashLength32(ht
);
136 assert(s
.size() == len
);
138 for (unsigned int n
= 0; n
< len
; ++n
) {
139 char c
= s
[len
- n
- 1];
141 for (digit
= 0; digit
< base32Chars
.size(); ++digit
) /* !!! slow */
142 if (base32Chars
[digit
] == c
) break;
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
);
156 Hash
parseHash16or32(HashType ht
, const string
& s
)
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
);
166 throw Error(format("hash `%1%' has wrong length for hash type `%2%'")
167 % s
% printHashType(ht
));
172 bool isHash(const string
& s
)
174 if (s
.length() != 32) return false;
175 for (int i
= 0; i
< 32; i
++) {
177 if (!((c
>= '0' && c
<= '9') ||
178 (c
>= 'a' && c
<= 'f')))
184 /* The "hash context". */
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. */
191 if (ref
.md_handle
== NULL
)
194 gcry_md_copy (&md_handle
, ref
.md_handle
);
197 /* Make sure 'md_handle' is always initialized. */
198 Ctx(): md_handle (NULL
) { };
200 gcry_md_hd_t md_handle
;
204 static void start(HashType ht
, Ctx
& ctx
)
208 err
= gcry_md_open (&ctx
.md_handle
, ht
, 0);
209 assert (err
== GPG_ERR_NO_ERROR
);
213 static void update(HashType ht
, Ctx
& ctx
,
214 const unsigned char * bytes
, unsigned int len
)
216 gcry_md_write (ctx
.md_handle
, bytes
, len
);
220 static void finish(HashType ht
, Ctx
& ctx
, unsigned char * hash
)
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
;
229 Hash
hashString(HashType ht
, const string
& s
)
234 update(ht
, ctx
, (const unsigned char *) s
.data(), s
.length());
235 finish(ht
, ctx
, hash
.hash
);
240 Hash
hashFile(HashType ht
, const Path
& path
)
246 AutoCloseFD fd
= open(path
.c_str(), O_RDONLY
);
247 if (fd
== -1) throw SysError(format("opening file `%1%'") % path
);
249 unsigned char buf
[8192];
251 while ((n
= read(fd
, buf
, sizeof(buf
)))) {
253 if (n
== -1) throw SysError(format("reading file `%1%'") % path
);
254 update(ht
, ctx
, buf
, n
);
257 finish(ht
, ctx
, hash
.hash
);
262 HashSink::HashSink(HashType ht
) : ht(ht
)
269 HashSink::~HashSink()
275 void HashSink::write(const unsigned char * data
, size_t len
)
278 update(ht
, *ctx
, data
, len
);
281 HashResult
HashSink::finish()
285 nix::finish(ht
, *ctx
, hash
.hash
);
286 return HashResult(hash
, bytes
);
289 HashResult
HashSink::currentHash()
294 nix::finish(ht
, ctx2
, hash
.hash
);
295 return HashResult(hash
, bytes
);
300 HashType ht
, const Path
& path
, PathFilter
& filter
)
303 dumpPath(path
, sink
, filter
);
304 return sink
.finish();
308 Hash
compressHash(const Hash
& hash
, unsigned int newSize
)
311 h
.hashSize
= newSize
;
312 for (unsigned int i
= 0; i
< hash
.hashSize
; ++i
)
313 h
.hash
[i
% newSize
] ^= hash
.hash
[i
];
318 HashType
parseHashType(const string
& s
)
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
;
331 string
printHashType(HashType ht
)
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");