GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/crypto/hash.h
Date: 2026-08-30 02:40:36
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 2436648 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 69668 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 28104620 explicit Hex(const Digest<digest_size_, algorithm_> *digest)
130 28104620 : digest_(*digest)
131 28104620 , hash_length_(2 * kDigestSizes[digest_.algorithm])
132 28104620 , algo_id_length_(kAlgorithmIdSizes[digest_.algorithm]) { }
133
134 2297682250 unsigned int length() const { return hash_length_ + algo_id_length_; }
135
136 1120267815 char operator[](const unsigned int position) const {
137
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1120264616 times.
1120267815 assert(position < length());
138
2/2
✓ Branch 0 taken 1120238921 times.
✓ Branch 1 taken 25695 times.
1120264616 return (position < hash_length_) ? GetHashChar(position)
139 1120420734 : GetAlgorithmIdentifierChar(position);
140 }
141
142 protected:
143 1120257670 char GetHashChar(const unsigned int position) const {
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1120257670 times.
1120257670 assert(position < hash_length_);
145
2/2
✓ Branch 0 taken 560472999 times.
✓ Branch 1 taken 559784671 times.
1120257670 const char digit = (position % 2 == 0)
146 560472999 ? digest_.digest[position / 2] / 16
147 559784671 : digest_.digest[position / 2] % 16;
148 1120257670 return ToHex(digit);
149 }
150
151 25695 char GetAlgorithmIdentifierChar(const unsigned int position) const {
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25695 times.
25695 assert(position >= hash_length_);
153 25695 return kAlgorithmIds[digest_.algorithm][position - hash_length_];
154 }
155
156 1120341419 static char ToHex(const char c) {
157
2/2
✓ Branch 0 taken 889562010 times.
✓ Branch 1 taken 230779409 times.
1120341419 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 120319024 unsigned GetDigestSize() const { return kDigestSizes[algorithm]; }
167 2592 unsigned GetHexSize() const {
168 2592 return 2 * kDigestSizes[algorithm] + kAlgorithmIdSizes[algorithm];
169 }
170
171 1246311836 Digest() : algorithm(algorithm_), suffix(kSuffixNone) { SetNull(); }
172
173 319344 explicit Digest(const Algorithms a, const HexPtr hex, const char s = 0)
174 319344 : algorithm(a), suffix(s) {
175
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1428 times.
1428 assert((algorithm_ == kAny) || (a == algorithm_));
176 319344 const unsigned char_size = 2 * kDigestSizes[a];
177
178 319344 const std::string *str = hex.str;
179 319344 const unsigned length = str->length();
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 319344 times.
319344 assert(length >= char_size); // A suffix won't hurt
181
182
2/2
✓ Branch 0 taken 6199428 times.
✓ Branch 1 taken 319344 times.
6518772 for (unsigned i = 0; i < char_size; i += 2) {
183 11269832 this->digest[i / 2] = ((*str)[i] <= '9' ? (*str)[i] - '0'
184 1129024 : (*str)[i] - 'a' + 10)
185 * 16
186
4/4
✓ Branch 0 taken 5070404 times.
✓ Branch 1 taken 1129024 times.
✓ Branch 3 taken 4949685 times.
✓ Branch 4 taken 1249743 times.
13648599 + ((*str)[i + 1] <= '9' ? (*str)[i + 1] - '0'
187 1249743 : (*str)[i + 1] - 'a' + 10);
188 }
189 319344 }
190
191 9666 Digest(const Algorithms a,
192 const unsigned char *digest_buffer,
193 const Suffix s = kSuffixNone)
194 9666 : algorithm(a), suffix(s) {
195 9666 memcpy(digest, digest_buffer, kDigestSizes[a]);
196 9666 }
197
198 /**
199 * Generates a purely random hash
200 * Only used for testing purposes
201 */
202 2873 void Randomize() {
203 2873 Prng prng;
204 2873 prng.InitLocaltime();
205
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
2873 Randomize(&prng);
206 2873 }
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 60017226 void Randomize(const uint64_t seed) {
215 60017226 Prng prng;
216 60017226 prng.InitSeed(seed);
217
1/2
✓ Branch 1 taken 60008613 times.
✗ Branch 2 not taken.
60017226 Randomize(&prng);
218 60017226 }
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 108845631 void Randomize(Prng *prng) {
227 108845631 const unsigned bytes = GetDigestSize();
228
2/2
✓ Branch 0 taken 1875448804 times.
✓ Branch 1 taken 108845587 times.
1984295315 for (unsigned i = 0; i < bytes; ++i) {
229 1875449684 digest[i] = prng->Next(256);
230 }
231 108845631 }
232
233 328008 bool HasSuffix() const { return suffix != kSuffixNone; }
234 2645384 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 26153369 std::string ToString(const bool with_suffix = false) const {
244 26153369 Hex hex(this);
245
4/4
✓ Branch 0 taken 317948 times.
✓ Branch 1 taken 25835025 times.
✓ Branch 3 taken 3663 times.
✓ Branch 4 taken 313805 times.
26152973 const bool use_suffix = with_suffix && HasSuffix();
246 26152493 const unsigned string_length = hex.length() + use_suffix;
247
1/2
✓ Branch 2 taken 26153054 times.
✗ Branch 3 not taken.
26152350 std::string result(string_length, 0);
248
249
2/2
✓ Branch 1 taken 1042385741 times.
✓ Branch 2 taken 26021341 times.
1068515093 for (unsigned int i = 0; i < hex.length(); ++i) {
250
2/4
✓ Branch 1 taken 1042420049 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1042362296 times.
✗ Branch 5 not taken.
1042385741 result[i] = hex[i];
251 }
252
253
2/2
✓ Branch 0 taken 3663 times.
✓ Branch 1 taken 26017678 times.
26021341 if (use_suffix) {
254
1/2
✓ Branch 1 taken 3663 times.
✗ Branch 2 not taken.
3663 result[string_length - 1] = suffix;
255 }
256
257
2/2
✓ Branch 1 taken 88 times.
✓ Branch 2 taken 26153493 times.
26021341 assert(result.length() == string_length);
258 52306986 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 352 std::string ToFingerprint(const bool with_suffix = false) const {
270 352 Hex hex(this);
271
3/4
✓ Branch 0 taken 176 times.
✓ Branch 1 taken 176 times.
✓ Branch 3 taken 176 times.
✗ Branch 4 not taken.
352 const bool use_suffix = with_suffix && HasSuffix();
272 352 const unsigned string_length = hex.length() + kDigestSizes[algorithm] - 1
273 352 + use_suffix;
274
1/2
✓ Branch 2 taken 352 times.
✗ Branch 3 not taken.
352 std::string result(string_length, 0);
275
276 352 unsigned l = hex.length();
277
2/2
✓ Branch 0 taken 14784 times.
✓ Branch 1 taken 352 times.
15136 for (unsigned int hex_i = 0, result_i = 0; hex_i < l; ++hex_i, ++result_i) {
278
2/4
✓ Branch 1 taken 14784 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14784 times.
✗ Branch 5 not taken.
14784 result[result_i] = toupper(hex[hex_i]);
279
4/4
✓ Branch 0 taken 13024 times.
✓ Branch 1 taken 1760 times.
✓ Branch 2 taken 6336 times.
✓ Branch 3 taken 6688 times.
14784 if ((hex_i < 2 * kDigestSizes[algorithm] - 1) && (hex_i % 2 == 1)) {
280
1/2
✓ Branch 1 taken 6336 times.
✗ Branch 2 not taken.
6336 result[++result_i] = ':';
281 }
282 }
283
284
2/2
✓ Branch 0 taken 176 times.
✓ Branch 1 taken 176 times.
352 if (use_suffix) {
285
1/2
✓ Branch 1 taken 176 times.
✗ Branch 2 not taken.
176 result[string_length - 1] = suffix;
286 }
287
288
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 352 times.
352 assert(result.length() == string_length);
289 704 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 6362 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 43663 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 446 std::string MakeAlternativePath() const {
316
1/2
✓ Branch 2 taken 446 times.
✗ Branch 3 not taken.
446 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 1906878 std::string MakePathWithoutSuffix() const {
326 1906878 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 1951245 std::string MakePathExplicit(const unsigned dir_levels,
342 const unsigned digits_per_level,
343 const Suffix hash_suffix = kSuffixNone) const {
344 1951245 Hex hex(this);
345
346 // figure out how big the output string needs to be
347 1951245 const bool use_suffix = (hash_suffix != kSuffixNone);
348 1951245 const unsigned string_length = hex.length() + dir_levels + use_suffix;
349 1951245 std::string result;
350
1/2
✓ Branch 1 taken 1951245 times.
✗ Branch 2 not taken.
1951245 result.resize(string_length);
351
352 // build hexified hash and path delimiters
353 1951245 unsigned i = 0;
354 1951245 unsigned pos = 0;
355
2/2
✓ Branch 1 taken 77929161 times.
✓ Branch 2 taken 1951245 times.
79880406 for (; i < hex.length(); ++i) {
356
4/4
✓ Branch 0 taken 75977916 times.
✓ Branch 1 taken 1951245 times.
✓ Branch 2 taken 37015012 times.
✓ Branch 3 taken 38962904 times.
77929161 if (i > 0 && (i % digits_per_level == 0)
357
2/2
✓ Branch 0 taken 1951597 times.
✓ Branch 1 taken 35063415 times.
37015012 && (i / digits_per_level <= dir_levels)) {
358
1/2
✓ Branch 1 taken 1951597 times.
✗ Branch 2 not taken.
1951597 result[pos++] = '/';
359 }
360
2/4
✓ Branch 1 taken 77929161 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 77929161 times.
✗ Branch 5 not taken.
77929161 result[pos++] = hex[i];
361 }
362
363 // (optionally) add hash hint suffix
364
2/2
✓ Branch 0 taken 21154 times.
✓ Branch 1 taken 1930091 times.
1951245 if (use_suffix) {
365
1/2
✓ Branch 1 taken 21154 times.
✗ Branch 2 not taken.
21154 result[pos++] = hash_suffix;
366 }
367
368
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1951245 times.
1951245 assert(i == hex.length());
369
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1951245 times.
1951245 assert(pos == string_length);
370 3902490 return result;
371 }
372
373 21690666 bool IsNull() const {
374
2/2
✓ Branch 0 taken 73288330 times.
✓ Branch 1 taken 2707864 times.
75996194 for (unsigned i = 0; i < kDigestSizes[algorithm]; ++i) {
375
2/2
✓ Branch 0 taken 18982802 times.
✓ Branch 1 taken 54305528 times.
73288330 if (digest[i] != 0)
376 18982802 return false;
377 }
378 2707864 return true;
379 }
380
381 /**
382 * Get a partial digest for use when only 32 bits are required
383 */
384 1428 uint32_t Partial32() const {
385 1428 const uint32_t *partial = (const uint32_t *)digest;
386 1428 return ntohl(*partial);
387 }
388
389
390 1246320098 void SetNull() { memset(digest, 0, digest_size_); }
391
392
393 1167762726 bool operator==(const Digest<digest_size_, algorithm_> &other) const {
394
2/2
✓ Branch 0 taken 30371515 times.
✓ Branch 1 taken 553510814 times.
1167762726 if (this->algorithm != other.algorithm)
395 60741456 return false;
396
2/2
✓ Branch 0 taken 4370762286 times.
✓ Branch 1 taken 248303997 times.
9238125048 for (unsigned i = 0; i < kDigestSizes[algorithm]; ++i) {
397
2/2
✓ Branch 0 taken 305206817 times.
✓ Branch 1 taken 4065555469 times.
8741517412 if (this->digest[i] != other.digest[i])
398 610413634 return false;
399 }
400 496607636 return true;
401 }
402
403 176340196 bool operator!=(const Digest<digest_size_, algorithm_> &other) const {
404 176340196 return !(*this == other);
405 }
406
407 2021533315 bool operator<(const Digest<digest_size_, algorithm_> &other) const {
408
2/2
✓ Branch 0 taken 105262298 times.
✓ Branch 1 taken 1916271017 times.
2021533315 if (this->algorithm != other.algorithm)
409 105262298 return (this->algorithm < other.algorithm);
410
2/2
✓ Branch 0 taken 4930526893 times.
✓ Branch 1 taken 56901498 times.
4987428391 for (unsigned i = 0; i < kDigestSizes[algorithm]; ++i) {
411
2/2
✓ Branch 0 taken 877962651 times.
✓ Branch 1 taken 4052564242 times.
4930526893 if (this->digest[i] > other.digest[i])
412 877962651 return false;
413
2/2
✓ Branch 0 taken 981406868 times.
✓ Branch 1 taken 3071157374 times.
4052564242 if (this->digest[i] < other.digest[i])
414 981406868 return true;
415 }
416 56901498 return false;
417 }
418
419 1047974 bool operator>(const Digest<digest_size_, algorithm_> &other) const {
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1047974 times.
1047974 if (this->algorithm != other.algorithm)
421 return (this->algorithm > other.algorithm);
422
2/2
✓ Branch 0 taken 1234534 times.
✓ Branch 1 taken 1584 times.
1236118 for (unsigned i = 0; i < kDigestSizes[algorithm]; ++i) {
423
2/2
✓ Branch 0 taken 395478 times.
✓ Branch 1 taken 839056 times.
1234534 if (this->digest[i] < other.digest[i])
424 395478 return false;
425
2/2
✓ Branch 0 taken 650912 times.
✓ Branch 1 taken 188144 times.
839056 if (this->digest[i] > other.digest[i])
426 650912 return true;
427 }
428 1584 return false;
429 }
430 };
431
432
433 struct CVMFS_EXPORT Md5 : public Digest<16, kMd5> {
434 357951791 Md5() : Digest<16, kMd5>() { }
435 explicit Md5(const AsciiPtr ascii);
436 1428 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 149081453 Any() : Digest<kMaxDigestSize, kAny>() { }
457
458 114592281 explicit Any(const Algorithms a, const char s = kSuffixNone)
459 114592281 : Digest<kMaxDigestSize, kAny>() {
460 114592281 algorithm = a;
461 114592281 suffix = s;
462 114592281 }
463
464 9666 Any(const Algorithms a,
465 const unsigned char *digest_buffer,
466 const Suffix suffix = kSuffixNone)
467 9666 : Digest<kMaxDigestSize, kAny>(a, digest_buffer, suffix) { }
468
469 317916 explicit Any(const Algorithms a,
470 const HexPtr hex,
471 const char suffix = kSuffixNone)
472 317916 : 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 44 explicit Short(const Any &full) : Digest<kShortDigestSize, kAny>() {
480 44 algorithm = full.algorithm;
481 44 suffix = full.suffix;
482 44 digest_size_ = kShortDigestSize / 4;
483 44 hex_size_ = 2 * digest_size_ + kAlgorithmIdSizes[algorithm];
484 44 memcpy(digest, full.digest, kShortDigestSize);
485 44 }
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 88 bool Collide(const Any &other) const {
520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 88 times.
88 if (this->algorithm != other.algorithm) {
521 return false;
522 }
523
2/2
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 44 times.
308 for (unsigned i = 0; i < digest_size_; ++i) {
524
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 220 times.
264 if (this->digest[i] != other.digest[i])
525 44 return false;
526 }
527 44 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 11051699 ContextPtr() : algorithm(kAny), buffer(NULL), size(0) { }
551
552 1233763 explicit ContextPtr(const Algorithms a)
553 1233763 : 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 44 inline void HmacString(const std::string &key, const std::string &content,
574 Any *any_digest) {
575 44 Hmac(key,
576 44 reinterpret_cast<const unsigned char *>(content.data()),
577 44 content.size(),
578 any_digest);
579 44 }
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