7 #include <openssl/md5.h>
8 #include <openssl/sha.h>
21 #include <sys/types.h>
33 memset(hash
, 0, maxHashSize
);
37 Hash::Hash(HashType type
)
40 if (type
== htMD5
) hashSize
= md5HashSize
;
41 else if (type
== htSHA1
) hashSize
= sha1HashSize
;
42 else if (type
== htSHA256
) hashSize
= sha256HashSize
;
43 else throw Error("unknown hash type");
44 assert(hashSize
<= maxHashSize
);
45 memset(hash
, 0, maxHashSize
);
49 bool Hash::operator == (const Hash
& h2
) const
51 if (hashSize
!= h2
.hashSize
) return false;
52 for (unsigned int i
= 0; i
< hashSize
; i
++)
53 if (hash
[i
] != h2
.hash
[i
]) return false;
58 bool Hash::operator != (const Hash
& h2
) const
60 return !(*this == h2
);
64 bool Hash::operator < (const Hash
& h
) const
66 for (unsigned int i
= 0; i
< hashSize
; i
++) {
67 if (hash
[i
] < h
.hash
[i
]) return true;
68 if (hash
[i
] > h
.hash
[i
]) return false;
74 const string base16Chars
= "0123456789abcdef";
77 string
printHash(const Hash
& hash
)
79 char buf
[hash
.hashSize
* 2];
80 for (unsigned int i
= 0; i
< hash
.hashSize
; i
++) {
81 buf
[i
* 2] = base16Chars
[hash
.hash
[i
] >> 4];
82 buf
[i
* 2 + 1] = base16Chars
[hash
.hash
[i
] & 0x0f];
84 return string(buf
, hash
.hashSize
* 2);
88 Hash
parseHash(HashType ht
, const string
& s
)
91 if (s
.length() != hash
.hashSize
* 2)
92 throw Error(format("invalid hash `%1%'") % s
);
93 for (unsigned int i
= 0; i
< hash
.hashSize
; i
++) {
94 string
s2(s
, i
* 2, 2);
95 if (!isxdigit(s2
[0]) || !isxdigit(s2
[1]))
96 throw Error(format("invalid hash `%1%'") % s
);
97 std::istringstream
str(s2
);
106 static unsigned char divMod(unsigned char * bytes
, unsigned char y
)
108 unsigned int borrow
= 0;
110 int pos
= Hash::maxHashSize
- 1;
111 while (pos
>= 0 && !bytes
[pos
]) --pos
;
113 for ( ; pos
>= 0; --pos
) {
114 unsigned int s
= bytes
[pos
] + (borrow
<< 8);
115 unsigned int d
= s
/ y
;
124 unsigned int hashLength32(const Hash
& hash
)
126 return (hash
.hashSize
* 8 - 1) / 5 + 1;
131 const string base32Chars
= "0123456789abcdfghijklmnpqrsvwxyz";
134 string
printHash32(const Hash
& hash
)
137 unsigned int len
= hashLength32(hash
);
139 const char * chars
= base32Chars
.data();
145 unsigned char digit
= divMod(hash2
.hash
, 32);
146 s
[pos
--] = chars
[digit
];
149 for (unsigned int i
= 0; i
< hash2
.maxHashSize
; ++i
)
150 assert(hash2
.hash
[i
] == 0);
156 string
printHash16or32(const Hash
& hash
)
158 return hash
.type
== htMD5
? printHash(hash
) : printHash32(hash
);
162 static bool mul(unsigned char * bytes
, unsigned char y
, int maxSize
)
164 unsigned char carry
= 0;
166 for (int pos
= 0; pos
< maxSize
; ++pos
) {
167 unsigned int m
= bytes
[pos
] * y
+ carry
;
168 bytes
[pos
] = m
& 0xff;
176 static bool add(unsigned char * bytes
, unsigned char y
, int maxSize
)
178 unsigned char carry
= y
;
180 for (int pos
= 0; pos
< maxSize
; ++pos
) {
181 unsigned int m
= bytes
[pos
] + carry
;
182 bytes
[pos
] = m
& 0xff;
184 if (carry
== 0) break;
191 Hash
parseHash32(HashType ht
, const string
& s
)
195 const char * chars
= base32Chars
.data();
197 for (unsigned int i
= 0; i
< s
.length(); ++i
) {
200 for (digit
= 0; digit
< base32Chars
.size(); ++digit
) /* !!! slow */
201 if (chars
[digit
] == c
) break;
203 throw Error(format("invalid base-32 hash `%1%'") % s
);
204 if (mul(hash
.hash
, 32, hash
.hashSize
) ||
205 add(hash
.hash
, digit
, hash
.hashSize
))
206 throw Error(format("base-32 hash `%1%' is too large") % s
);
213 Hash
parseHash16or32(HashType ht
, const string
& s
)
216 if (s
.size() == hash
.hashSize
* 2)
217 /* hexadecimal representation */
218 hash
= parseHash(ht
, s
);
219 else if (s
.size() == hashLength32(hash
))
220 /* base-32 representation */
221 hash
= parseHash32(ht
, s
);
223 throw Error(format("hash `%1%' has wrong length for hash type `%2%'")
224 % s
% printHashType(ht
));
229 bool isHash(const string
& s
)
231 if (s
.length() != 32) return false;
232 for (int i
= 0; i
< 32; i
++) {
234 if (!((c
>= '0' && c
<= '9') ||
235 (c
>= 'a' && c
<= 'f')))
250 static void start(HashType ht
, Ctx
& ctx
)
252 if (ht
== htMD5
) MD5_Init(&ctx
.md5
);
253 else if (ht
== htSHA1
) SHA1_Init(&ctx
.sha1
);
254 else if (ht
== htSHA256
) SHA256_Init(&ctx
.sha256
);
258 static void update(HashType ht
, Ctx
& ctx
,
259 const unsigned char * bytes
, unsigned int len
)
261 if (ht
== htMD5
) MD5_Update(&ctx
.md5
, bytes
, len
);
262 else if (ht
== htSHA1
) SHA1_Update(&ctx
.sha1
, bytes
, len
);
263 else if (ht
== htSHA256
) SHA256_Update(&ctx
.sha256
, bytes
, len
);
267 static void finish(HashType ht
, Ctx
& ctx
, unsigned char * hash
)
269 if (ht
== htMD5
) MD5_Final(hash
, &ctx
.md5
);
270 else if (ht
== htSHA1
) SHA1_Final(hash
, &ctx
.sha1
);
271 else if (ht
== htSHA256
) SHA256_Final(hash
, &ctx
.sha256
);
275 Hash
hashString(HashType ht
, const string
& s
)
280 update(ht
, ctx
, (const unsigned char *) s
.data(), s
.length());
281 finish(ht
, ctx
, hash
.hash
);
286 Hash
hashFile(HashType ht
, const Path
& path
)
292 AutoCloseFD fd
= open(path
.c_str(), O_RDONLY
);
293 if (fd
== -1) throw SysError(format("opening file `%1%'") % path
);
295 unsigned char buf
[8192];
297 while ((n
= read(fd
, buf
, sizeof(buf
)))) {
299 if (n
== -1) throw SysError(format("reading file `%1%'") % path
);
300 update(ht
, ctx
, buf
, n
);
303 finish(ht
, ctx
, hash
.hash
);
308 HashSink::HashSink(HashType ht
) : ht(ht
)
315 HashSink::~HashSink()
321 void HashSink::write(const unsigned char * data
, size_t len
)
324 update(ht
, *ctx
, data
, len
);
327 HashResult
HashSink::finish()
331 nix::finish(ht
, *ctx
, hash
.hash
);
332 return HashResult(hash
, bytes
);
335 HashResult
HashSink::currentHash()
340 nix::finish(ht
, ctx2
, hash
.hash
);
341 return HashResult(hash
, bytes
);
346 HashType ht
, const Path
& path
, PathFilter
& filter
)
349 dumpPath(path
, sink
, filter
);
350 return sink
.finish();
354 Hash
compressHash(const Hash
& hash
, unsigned int newSize
)
357 h
.hashSize
= newSize
;
358 for (unsigned int i
= 0; i
< hash
.hashSize
; ++i
)
359 h
.hash
[i
% newSize
] ^= hash
.hash
[i
];
364 HashType
parseHashType(const string
& s
)
366 if (s
== "md5") return htMD5
;
367 else if (s
== "sha1") return htSHA1
;
368 else if (s
== "sha256") return htSHA256
;
369 else return htUnknown
;
373 string
printHashType(HashType ht
)
375 if (ht
== htMD5
) return "md5";
376 else if (ht
== htSHA1
) return "sha1";
377 else if (ht
== htSHA256
) return "sha256";
378 else throw Error("cannot print unknown hash type");