GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/crypto/hash.h
Date: 2026-09-20 02:39:58
Exec Total Coverage
Lines: 178 193 92.2%
Branches: 100 146 68.5%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * Provides a bit syntactic sugar around the hash algorithms.
5 * In particular, hashes can easily be created by constructors.
6 * Also, we have a little to-string-from-string conversion.
7 *
8 * The complexity is due to the need to avoid dynamically allocated memory
9 * for the hashes. Almost everything happens on the stack.
10 */
11
12 #ifndef CVMFS_CRYPTO_HASH_H_
13 #define CVMFS_CRYPTO_HASH_H_
14
15 #include <arpa/inet.h>
16 #include <stdint.h>
17
18 #include <cassert>
19 #include <cctype>
20 #include <cstdlib>
21 #include <cstring>
22 #include <string>
23
24 #include "util/export.h"
25 #include "util/logging.h"
26 #include "util/prng.h"
27 #include "util/smalloc.h"
28
29 #ifdef CVMFS_NAMESPACE_GUARD
30 namespace CVMFS_NAMESPACE_GUARD {
31 #endif
32
33 namespace shash {
34
35 /**
36 * Don't change order! The integer value of the enum constants is used
37 * as file catalog flags and as flags in communication with the cache manager.
38 * If algorithms are added, the protocol definition for external cache managers
39 * needs to be updated, too.
40 */
41 enum Algorithms {
42 kMd5 = 0,
43 kSha1,
44 kRmd160,
45 kShake128, // with 160 output bits
46 kAny,
47 };
48
49 /**
50 * NOTE: when adding a suffix here, one must edit `cvmfs_swissknife scrub`
51 * accordingly, that checks for invalid hash suffixes
52 */
53 const char kSuffixNone = 0;
54 const char kSuffixCatalog = 'C';
55 const char kSuffixHistory = 'H';
56 const char kSuffixMicroCatalog = 'L'; // currently unused
57 const char kSuffixPartial = 'P';
58 const char kSuffixTemporary = 'T';
59 const char kSuffixCertificate = 'X';
60 const char kSuffixMetainfo = 'M';
61
62
63 /**
64 * Corresponds to Algorithms. "Any" is the maximum of all the other
65 * digest sizes.
66 * When the maximum digest size changes, the memory layout of DirectoryEntry and
67 * PosixQuotaManager::LruCommand changes, too!
68 */
69 const unsigned kDigestSizes[] = {16, 20, 20, 20, 20};
70 // Md5 Sha1 Rmd160 Shake128 Any
71 const unsigned kMaxDigestSize = 20;
72
73 /**
74 * The maximum of GetContextSize()
75 * Nettle v3.x has a larger context size for sha3.
76 * When using v4 exclusively, this could be reset to 256 bytes.
77 */
78 const unsigned kMaxContextSize = 384;
79
80 /**
81 * Hex representations of hashes with the same length need a suffix
82 * to be distinguished from each other. They should all have one but
83 * for backwards compatibility MD5 and SHA-1 have none. Initialized in hash.cc
84 * like const char *kAlgorithmIds[] = {"", "", "-rmd160", ...
85 */
86 CVMFS_EXPORT extern const char *kAlgorithmIds[];
87 const unsigned kAlgorithmIdSizes[] = {0, 0, 7, 9, 0};
88 // Md5 Sha1 -rmd160 -shake128 Any
89 const unsigned kMaxAlgorithmIdentifierSize = 9;
90
91 /**
92 * Corresponds to Algorithms. There is no block size for Any.
93 * Is an HMAC for SHAKE well-defined?
94 */
95 const unsigned kBlockSizes[] = {64, 64, 64, 168};
96 // Md5 Sha1 Rmd160 Shake128
97
98 /**
99 * Distinguishes between interpreting a string as hex hash and hashing over
100 * the contents of a string.
101 */
102 struct CVMFS_EXPORT HexPtr {
103 const std::string *str;
104 2060004 explicit HexPtr(const std::string &s) { str = &s; }
105 bool IsValid() const;
106 };
107
108 struct CVMFS_EXPORT AsciiPtr {
109 const std::string *str;
110 67771 explicit AsciiPtr(const std::string &s) { str = &s; }
111 };
112
113 typedef char Suffix;
114
115 /**
116 * Holds a hash digest and provides from string / to string conversion and
117 * comparison. The kAny algorithm may not be used in functions! The algorithm
118 * has to be changed beforehand.
119 * This class is not used directly, but used as base clase of Md5, Sha1, ...
120 */
121 template<unsigned digest_size_, Algorithms algorithm_>
122 struct CVMFS_EXPORT Digest {
123 unsigned char digest[digest_size_];
124 Algorithms algorithm;
125 Suffix suffix;
126
127 class Hex {
128 public:
129 23689859 explicit Hex(const Digest<digest_size_, algorithm_> *digest)
130 23689859 : digest_(*digest)
131 23689859 , hash_length_(2 * kDigestSizes[digest_.algorithm])
132 23689859 , algo_id_length_(kAlgorithmIdSizes[digest_.algorithm]) { }
133
134 1935628178 unsigned int length() const { return hash_length_ + algo_id_length_; }
135
136 943809575 char operator[](const unsigned int position) const {
137
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 943809554 times.
943809575 assert(position < length());
138
2/2
✓ Branch 0 taken 943780916 times.
✓ Branch 1 taken 28638 times.
943809554 return (position < hash_length_) ? GetHashChar(position)
139 943933538 : GetAlgorithmIdentifierChar(position);
140 }
141
142 protected:
143 943797777 char GetHashChar(const unsigned int position) const {
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 943797777 times.
943797777 assert(position < hash_length_);
145
2/2
✓ Branch 0 taken 472196166 times.
✓ Branch 1 taken 471601611 times.
943797777 const char digit = (position % 2 == 0)
146 472196166 ? digest_.digest[position / 2] / 16
147 471601611 : digest_.digest[position / 2] % 16;
148 943797777 return ToHex(digit);
149 }
150
151 28638 char GetAlgorithmIdentifierChar(const unsigned int position) const {
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28638 times.
28638 assert(position >= hash_length_);
153 28638 return kAlgorithmIds[digest_.algorithm][position - hash_length_];
154 }
155
156 943879103 static char ToHex(const char c) {
157
2/2
✓ Branch 0 taken 736055031 times.
✓ Branch 1 taken 207824072 times.
943879103 return static_cast<char>(c + ((c <= 9) ? '0' : 'a' - 10));
158 }
159
160 private:
161 const Digest<digest_size_, algorithm_> &digest_;
162 const unsigned int hash_length_;
163 const unsigned int algo_id_length_;
164 };
165
166 155232296 unsigned GetDigestSize() const { return kDigestSizes[algorithm]; }
167 2882 unsigned GetHexSize() const {
168 2882 return 2 * kDigestSizes[algorithm] + kAlgorithmIdSizes[algorithm];
169 }
170
171 2484935484 Digest() : algorithm(algorithm_), suffix(kSuffixNone) { SetNull(); }
172
173 265811 explicit Digest(const Algorithms a, const HexPtr hex, const char s = 0)
174 265811 : algorithm(a), suffix(s) {
175
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1446 times.
1446 assert((algorithm_ == kAny) || (a == algorithm_));
176 265811 const unsigned char_size = 2 * kDigestSizes[a];
177
178 265811 const std::string *str = hex.str;
179 265811 const unsigned length = str->length();
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 265811 times.
265811 assert(length >= char_size); // A suffix won't hurt
181
182
2/2
✓ Branch 0 taken 5172956 times.
✓ Branch 1 taken 265811 times.
5438767 for (unsigned i = 0; i < char_size; i += 2) {
183 9227493 this->digest[i / 2] = ((*str)[i] <= '9' ? (*str)[i] - '0'
184 1118419 : (*str)[i] - 'a' + 10)
185 * 16
186
4/4
✓ Branch 0 taken 4054537 times.
✓ Branch 1 taken 1118419 times.
✓ Branch 3 taken 3956574 times.
✓ Branch 4 taken 1216382 times.
11562294 + ((*str)[i + 1] <= '9' ? (*str)[i + 1] - '0'
187 1216382 : (*str)[i + 1] - 'a' + 10);
188 }
189 265811 }
190
191 11267 Digest(const Algorithms a,
192 const unsigned char *digest_buffer,
193 const Suffix s = kSuffixNone)
194 11267 : algorithm(a), suffix(s) {
195 11267 memcpy(digest, digest_buffer, kDigestSizes[a]);
196 11267 }
197
198 /**
199 * Generates a purely random hash
200 * Only used for testing purposes
201 */
202 5028 void Randomize() {
203 5028 Prng prng;
204 5028 prng.InitLocaltime();
205
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
5028 Randomize(&prng);
206 5028 }
207
208 /**
209 * Generates a purely random hash
210 * Only used for testing purposes
211 *
212 * @param seed random number generator seed (for reproducibility)
213 */
214 72014764 void Randomize(const uint64_t seed) {
215 72014764 Prng prng;
216 72014764 prng.InitSeed(seed);
217
1/2
✓ Branch 1 taken 72007382 times.
✗ Branch 2 not taken.
72014764 Randomize(&prng);
218 72014764 }
219
220 /**
221 * Generates a purely random hash
222 * Only used for testing purposes
223 *
224 * @param prng random number generator object (for external reproducibility)
225 */
226 146771437 void Randomize(Prng *prng) {
227 146771437 const unsigned bytes = GetDigestSize();
228
2/2
✓ Branch 0 taken 2548578048 times.
✓ Branch 1 taken 146771394 times.
2695350345 for (unsigned i = 0; i < bytes; ++i) {
229 2548578908 digest[i] = prng->Next(256);
230 }
231 146771437 }
232
233 341547 bool HasSuffix() const { return suffix != kSuffixNone; }
234 437132 void set_suffix(const Suffix s) { suffix = s; }
235
236 /**
237 * Generates a hexified representation of the digest including the identifier
238 * string for newly added hashes.
239 *
240 * @param with_suffix append the hash suffix (C,H,X, ...) to the result
241 * @return a string representation of the digest
242 */
243 22233373 std::string ToString(const bool with_suffix = false) const {
244 22233373 Hex hex(this);
245
4/4
✓ Branch 0 taken 331685 times.
✓ Branch 1 taken 21901644 times.
✓ Branch 3 taken 3299 times.
✓ Branch 4 taken 328181 times.
22233329 const bool use_suffix = with_suffix && HasSuffix();
246 22233124 const unsigned string_length = hex.length() + use_suffix;
247
1/2
✓ Branch 2 taken 22234072 times.
✗ Branch 3 not taken.
22233029 std::string result(string_length, 0);
248
249
2/2
✓ Branch 1 taken 885688515 times.
✓ Branch 2 taken 22120997 times.
907907011 for (unsigned int i = 0; i < hex.length(); ++i) {
250
2/4
✓ Branch 1 taken 885714026 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 885672986 times.
✗ Branch 5 not taken.
885688515 result[i] = hex[i];
251 }
252
253
2/2
✓ Branch 0 taken 3299 times.
✓ Branch 1 taken 22117698 times.
22120997 if (use_suffix) {
254
1/2
✓ Branch 1 taken 3299 times.
✗ Branch 2 not taken.
3299 result[string_length - 1] = suffix;
255 }
256
257
2/2
✓ Branch 1 taken 258 times.
✓ Branch 2 taken 22234869 times.
22120997 assert(result.length() == string_length);
258 44469738 return result;
259 }
260
261 /**
262 * Generates a hexified representation of the digest including the identifier
263 * string for newly added hashes. Output is in the form of
264 * 'openssl x509 fingerprint', e.g. 00:AA:BB:...-SHAKE128
265 *
266 * @param with_suffix append the hash suffix (C,H,X, ...) to the result
267 * @return a string representation of the digest
268 */
269 344 std::string ToFingerprint(const bool with_suffix = false) const {
270 344 Hex hex(this);
271
3/4
✓ Branch 0 taken 172 times.
✓ Branch 1 taken 172 times.
✓ Branch 3 taken 172 times.
✗ Branch 4 not taken.
344 const bool use_suffix = with_suffix && HasSuffix();
272 344 const unsigned string_length = hex.length() + kDigestSizes[algorithm] - 1
273 344 + use_suffix;
274
1/2
✓ Branch 2 taken 344 times.
✗ Branch 3 not taken.
344 std::string result(string_length, 0);
275
276 344 unsigned l = hex.length();
277
2/2
✓ Branch 0 taken 14448 times.
✓ Branch 1 taken 344 times.
14792 for (unsigned int hex_i = 0, result_i = 0; hex_i < l; ++hex_i, ++result_i) {
278
2/4
✓ Branch 1 taken 14448 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14448 times.
✗ Branch 5 not taken.
14448 result[result_i] = toupper(hex[hex_i]);
279
4/4
✓ Branch 0 taken 12728 times.
✓ Branch 1 taken 1720 times.
✓ Branch 2 taken 6192 times.
✓ Branch 3 taken 6536 times.
14448 if ((hex_i < 2 * kDigestSizes[algorithm] - 1) && (hex_i % 2 == 1)) {
280
1/2
✓ Branch 1 taken 6192 times.
✗ Branch 2 not taken.
6192 result[++result_i] = ':';
281 }
282 }
283
284
2/2
✓ Branch 0 taken 172 times.
✓ Branch 1 taken 172 times.
344 if (use_suffix) {
285
1/2
✓ Branch 1 taken 172 times.
✗ Branch 2 not taken.
172 result[string_length - 1] = suffix;
286 }
287
288
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 344 times.
344 assert(result.length() == string_length);
289 688 return result;
290 }
291
292 /**
293 * Convenience method to generate a string representation of the digest.
294 * See Digest<>::ToString() for details
295 *
296 * @return a string representation including the hash suffix of the digest
297 */
298 5935 std::string ToStringWithSuffix() const { return ToString(true); }
299
300 /**
301 * Generate the standard relative path from the hexified digest to be used in
302 * CAS areas or cache directories. Throughout the entire system we use one
303 * directory level (first to hex digest characters) for namespace splitting.
304 * Note: This method appends the internal hash suffix to the path.
305 *
306 * @return a relative path representation of the digest including the suffix
307 */
308 42545 std::string MakePath() const { return MakePathExplicit(1, 2, suffix); }
309
310 /**
311 * The alternative path is used to symlink the root catalog from the webserver
312 * root to the data directory. This way, the data directory can be protected
313 * while the root catalog remains accessible.
314 */
315 426 std::string MakeAlternativePath() const {
316
1/2
✓ Branch 2 taken 426 times.
✗ Branch 3 not taken.
426 return ".cvmfsalt-" + ToStringWithSuffix();
317 }
318
319 /**
320 * Produces a relative path representation of the digest without appending the
321 * hash suffix. See Digest<>::MakePath() for more details.
322 *
323 * @return a relative path representation of the digest without the suffix
324 */
325 1413132 std::string MakePathWithoutSuffix() const {
326 1413132 return MakePathExplicit(1, 2, kSuffixNone);
327 }
328
329 /**
330 * Generates an arbitrary path representation of the digest. Both number of
331 * directory levels and the hash-digits per level can be customized. Further-
332 * more an arbitrary hash suffix can be provided.
333 * Note: This method is mainly meant for internal usage but stays public for
334 * historical reasons.
335 *
336 * @param dir_levels the number of namespace splitting directory levels
337 * @param digits_per_level each directory level's number of hex-digits
338 * @param hash_suffix the hash suffix character to be appended
339 * @return a relative path representation of the digest
340 */
341 1456365 std::string MakePathExplicit(const unsigned dir_levels,
342 const unsigned digits_per_level,
343 const Suffix hash_suffix = kSuffixNone) const {
344 1456365 Hex hex(this);
345
346 // figure out how big the output string needs to be
347 1456365 const bool use_suffix = (hash_suffix != kSuffixNone);
348 1456365 const unsigned string_length = hex.length() + dir_levels + use_suffix;
349 1456365 std::string result;
350
1/2
✓ Branch 1 taken 1456365 times.
✗ Branch 2 not taken.
1456365 result.resize(string_length);
351
352 // build hexified hash and path delimiters
353 1456365 unsigned i = 0;
354 1456365 unsigned pos = 0;
355
2/2
✓ Branch 1 taken 58156133 times.
✓ Branch 2 taken 1456365 times.
59612498 for (; i < hex.length(); ++i) {
356
4/4
✓ Branch 0 taken 56699768 times.
✓ Branch 1 taken 1456365 times.
✓ Branch 2 taken 27623529 times.
✓ Branch 3 taken 29076239 times.
58156133 if (i > 0 && (i % digits_per_level == 0)
357
2/2
✓ Branch 0 taken 1456709 times.
✓ Branch 1 taken 26166820 times.
27623529 && (i / digits_per_level <= dir_levels)) {
358
1/2
✓ Branch 1 taken 1456709 times.
✗ Branch 2 not taken.
1456709 result[pos++] = '/';
359 }
360
2/4
✓ Branch 1 taken 58156133 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 58156133 times.
✗ Branch 5 not taken.
58156133 result[pos++] = hex[i];
361 }
362
363 // (optionally) add hash hint suffix
364
2/2
✓ Branch 0 taken 21918 times.
✓ Branch 1 taken 1434447 times.
1456365 if (use_suffix) {
365
1/2
✓ Branch 1 taken 21918 times.
✗ Branch 2 not taken.
21918 result[pos++] = hash_suffix;
366 }
367
368
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1456365 times.
1456365 assert(i == hex.length());
369
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1456365 times.
1456365 assert(pos == string_length);
370 2912730 return result;
371 }
372
373 13588013 bool IsNull() const {
374
2/2
✓ Branch 0 taken 23178132 times.
✓ Branch 1 taken 497158 times.
23675290 for (unsigned i = 0; i < kDigestSizes[algorithm]; ++i) {
375
2/2
✓ Branch 0 taken 13090855 times.
✓ Branch 1 taken 10087277 times.
23178132 if (digest[i] != 0)
376 13090855 return false;
377 }
378 497158 return true;
379 }
380
381 /**
382 * Get a partial digest for use when only 32 bits are required
383 */
384 1534 uint32_t Partial32() const {
385 1534 const uint32_t *partial = (const uint32_t *)digest;
386 1534 return ntohl(*partial);
387 }
388
389
390 2484929426 void SetNull() { memset(digest, 0, digest_size_); }
391
392
393 1997974716 bool operator==(const Digest<digest_size_, algorithm_> &other) const {
394
2/2
✓ Branch 0 taken 191920192 times.
✓ Branch 1 taken 807068165 times.
1997974716 if (this->algorithm != other.algorithm)
395 383838806 return false;
396
2/2
✓ Branch 0 taken 6505368307 times.
✓ Branch 1 taken 369127587 times.
13748982968 for (unsigned i = 0; i < kDigestSizes[algorithm]; ++i) {
397
2/2
✓ Branch 0 taken 437940578 times.
✓ Branch 1 taken 6067427729 times.
13010728214 if (this->digest[i] != other.digest[i])
398 875881156 return false;
399 }
400 738254754 return true;
401 }
402
403 205289421 bool operator!=(const Digest<digest_size_, algorithm_> &other) const {
404 205289421 return !(*this == other);
405 }
406
407 1211581027 bool operator<(const Digest<digest_size_, algorithm_> &other) const {
408
2/2
✓ Branch 0 taken 67002366 times.
✓ Branch 1 taken 1144578661 times.
1211581027 if (this->algorithm != other.algorithm)
409 67002366 return (this->algorithm < other.algorithm);
410
2/2
✓ Branch 0 taken 2933035933 times.
✓ Branch 1 taken 52025017 times.
2985060950 for (unsigned i = 0; i < kDigestSizes[algorithm]; ++i) {
411
2/2
✓ Branch 0 taken 516384352 times.
✓ Branch 1 taken 2416651581 times.
2933035933 if (this->digest[i] > other.digest[i])
412 516384352 return false;
413
2/2
✓ Branch 0 taken 576169292 times.
✓ Branch 1 taken 1840482289 times.
2416651581 if (this->digest[i] < other.digest[i])
414 576169292 return true;
415 }
416 52025017 return false;
417 }
418
419 857888 bool operator>(const Digest<digest_size_, algorithm_> &other) const {
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 857888 times.
857888 if (this->algorithm != other.algorithm)
421 return (this->algorithm > other.algorithm);
422
2/2
✓ Branch 0 taken 1010528 times.
✓ Branch 1 taken 1296 times.
1011824 for (unsigned i = 0; i < kDigestSizes[algorithm]; ++i) {
423
2/2
✓ Branch 0 taken 323832 times.
✓ Branch 1 taken 686696 times.
1010528 if (this->digest[i] < other.digest[i])
424 323832 return false;
425
2/2
✓ Branch 0 taken 532760 times.
✓ Branch 1 taken 153936 times.
686696 if (this->digest[i] > other.digest[i])
426 532760 return true;
427 }
428 1296 return false;
429 }
430 };
431
432
433 struct CVMFS_EXPORT Md5 : public Digest<16, kMd5> {
434 417997293 Md5() : Digest<16, kMd5>() { }
435 explicit Md5(const AsciiPtr ascii);
436 1446 explicit Md5(const HexPtr hex) : Digest<16, kMd5>(kMd5, hex) { }
437 Md5(const char *chars, const unsigned length);
438
439 /**
440 * An MD5 hash can be seen as two 64bit integers.
441 */
442 Md5(const uint64_t lo, const uint64_t hi);
443 void ToIntPair(uint64_t *lo, uint64_t *hi) const;
444 };
445
446 struct CVMFS_EXPORT Sha1 : public Digest<20, kSha1> { };
447 struct CVMFS_EXPORT Rmd160 : public Digest<20, kRmd160> { };
448 struct CVMFS_EXPORT Shake128 : public Digest<20, kShake128> { };
449
450 /**
451 * Any as such must not be used except for digest storage.
452 * To do real work, the class has to be "blessed" to be a real hash by
453 * setting the algorithm field accordingly.
454 */
455 struct CVMFS_EXPORT Any : public Digest<kMaxDigestSize, kAny> {
456 696097532 Any() : Digest<kMaxDigestSize, kAny>() { }
457
458 126855212 explicit Any(const Algorithms a, const char s = kSuffixNone)
459 126855212 : Digest<kMaxDigestSize, kAny>() {
460 126855212 algorithm = a;
461 126855212 suffix = s;
462 126855212 }
463
464 11267 Any(const Algorithms a,
465 const unsigned char *digest_buffer,
466 const Suffix suffix = kSuffixNone)
467 11267 : Digest<kMaxDigestSize, kAny>(a, digest_buffer, suffix) { }
468
469 264365 explicit Any(const Algorithms a,
470 const HexPtr hex,
471 const char suffix = kSuffixNone)
472 264365 : Digest<kMaxDigestSize, kAny>(a, hex, suffix) { }
473
474 Md5 CastToMd5();
475 };
476
477 const size_t kShortDigestSize = kMaxDigestSize;
478 struct CVMFS_EXPORT Short : public Digest<kShortDigestSize, kAny> {
479 43 explicit Short(const Any &full) : Digest<kShortDigestSize, kAny>() {
480 43 algorithm = full.algorithm;
481 43 suffix = full.suffix;
482 43 digest_size_ = kShortDigestSize / 4;
483 43 hex_size_ = 2 * digest_size_ + kAlgorithmIdSizes[algorithm];
484 43 memcpy(digest, full.digest, kShortDigestSize);
485 43 }
486
487 bool operator==(const Short &other) const {
488 if (this->algorithm != other.algorithm) {
489 return false;
490 }
491 if (this->digest_size_ != other.digest_size_) {
492 return false;
493 }
494 for (unsigned i = 0; i < digest_size_; ++i) {
495 if (this->digest[i] != other.digest[i])
496 return false;
497 }
498 return true;
499 }
500
501 std::string ToString(const bool with_suffix = false) {
502 const Hex hex(this);
503 const bool use_suffix = with_suffix && HasSuffix();
504 const unsigned string_length = hex_size_ + use_suffix;
505 std::string result(string_length, 0);
506
507 for (unsigned int i = 0; i < hex_size_; ++i) {
508 result[i] = hex[i];
509 }
510
511 if (use_suffix) {
512 result[string_length - 1] = suffix;
513 }
514
515 assert(result.length() == string_length);
516 return result;
517 }
518
519 86 bool Collide(const Any &other) const {
520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 86 times.
86 if (this->algorithm != other.algorithm) {
521 return false;
522 }
523
2/2
✓ Branch 0 taken 258 times.
✓ Branch 1 taken 43 times.
301 for (unsigned i = 0; i < digest_size_; ++i) {
524
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 215 times.
258 if (this->digest[i] != other.digest[i])
525 43 return false;
526 }
527 43 return true;
528 }
529
530 private:
531 size_t digest_size_;
532 size_t hex_size_;
533 };
534 /**
535 * Actual operations on digests, like "hash a file", "hash a buffer", or
536 * iterative operations.
537 */
538 CVMFS_EXPORT unsigned GetContextSize(const Algorithms algorithm);
539
540 /**
541 * Holds an OpenSSL context, only required for hash operations. Allows to
542 * deferr the storage allocation for the context to alloca.
543 */
544 class CVMFS_EXPORT ContextPtr {
545 public:
546 Algorithms algorithm;
547 void *buffer;
548 unsigned size;
549
550 11989365 ContextPtr() : algorithm(kAny), buffer(NULL), size(0) { }
551
552 831542 explicit ContextPtr(const Algorithms a)
553 831542 : algorithm(a), buffer(NULL), size(GetContextSize(a)) { }
554 ContextPtr(const Algorithms a, void *b)
555 : algorithm(a), buffer(b), size(GetContextSize(a)) { }
556 };
557
558 CVMFS_EXPORT void Init(ContextPtr context);
559 CVMFS_EXPORT void Update(const unsigned char *buffer,
560 const unsigned buffer_size,
561 ContextPtr context);
562 CVMFS_EXPORT void Final(ContextPtr context, Any *any_digest);
563 CVMFS_EXPORT bool HashFile(const std::string &filename, Any *any_digest);
564 CVMFS_EXPORT bool HashFd(int fd, Any *any_digest);
565 CVMFS_EXPORT void HashMem(const unsigned char *buffer,
566 const unsigned buffer_size,
567 Any *any_digest);
568 CVMFS_EXPORT void HashString(const std::string &content, Any *any_digest);
569 CVMFS_EXPORT void Hmac(const std::string &key,
570 const unsigned char *buffer,
571 const unsigned buffer_size,
572 Any *any_digest);
573 43 inline void HmacString(const std::string &key, const std::string &content,
574 Any *any_digest) {
575 43 Hmac(key,
576 43 reinterpret_cast<const unsigned char *>(content.data()),
577 43 content.size(),
578 any_digest);
579 43 }
580
581 /**
582 * Only used for AWS4 signature.
583 *
584 * Adding SHA-256 to the standard hash infrastructure would generally bloat the
585 * digets size to 32 bytes and require client data structure transformation
586 * during hotpatch.
587 */
588 CVMFS_EXPORT std::string Hmac256(const std::string &key,
589 const std::string &content,
590 bool raw_output = false);
591 CVMFS_EXPORT std::string Sha256File(const std::string &filename);
592 CVMFS_EXPORT std::string Sha256Mem(const unsigned char *buffer,
593 const unsigned buffer_size);
594 CVMFS_EXPORT std::string Sha256String(const std::string &content);
595
596 CVMFS_EXPORT
597 Algorithms ParseHashAlgorithm(const std::string &algorithm_option);
598 CVMFS_EXPORT
599 Any MkFromHexPtr(const HexPtr hex, const Suffix suffix = kSuffixNone);
600 CVMFS_EXPORT Any MkFromSuffixedHexPtr(const HexPtr hex);
601
602 } // namespace shash
603
604 #ifdef CVMFS_NAMESPACE_GUARD
605 } // namespace CVMFS_NAMESPACE_GUARD
606 #endif
607
608 #endif // CVMFS_CRYPTO_HASH_H_
609
610