| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/crypto/hash.cc |
| Date: | 2026-02-22 02:35:58 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 275 | 306 | 89.9% |
| Branches: | 169 | 268 | 63.1% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * This file is part of the CernVM File System. | ||
| 3 | */ | ||
| 4 | |||
| 5 | |||
| 6 | #include "crypto/hash.h" | ||
| 7 | |||
| 8 | #include <alloca.h> | ||
| 9 | #include <errno.h> | ||
| 10 | #include <fcntl.h> | ||
| 11 | #include <openssl/hmac.h> | ||
| 12 | #include <openssl/md5.h> | ||
| 13 | #include <openssl/ripemd.h> | ||
| 14 | #include <openssl/sha.h> | ||
| 15 | #include <unistd.h> | ||
| 16 | |||
| 17 | #include <cstdio> | ||
| 18 | #include <cstring> | ||
| 19 | |||
| 20 | #include "KeccakHash.h" | ||
| 21 | #include "crypto/openssl_version.h" | ||
| 22 | #include "util/exception.h" | ||
| 23 | |||
| 24 | |||
| 25 | using namespace std; // NOLINT | ||
| 26 | |||
| 27 | #ifdef CVMFS_NAMESPACE_GUARD | ||
| 28 | namespace CVMFS_NAMESPACE_GUARD { | ||
| 29 | #endif | ||
| 30 | |||
| 31 | namespace shash { | ||
| 32 | |||
| 33 | const char *kAlgorithmIds[] = {"", "", "-rmd160", "-shake128", ""}; | ||
| 34 | |||
| 35 | |||
| 36 | 44 | bool HexPtr::IsValid() const { | |
| 37 | 44 | const unsigned l = str->length(); | |
| 38 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 42 times.
|
44 | if (l == 0) |
| 39 | 2 | return false; | |
| 40 | 42 | const char *c = str->data(); // Walks through the string | |
| 41 | 42 | unsigned i = 0; // String position of *c | |
| 42 | |||
| 43 |
2/2✓ Branch 0 taken 1640 times.
✓ Branch 1 taken 10 times.
|
1650 | for (; i < l; ++i, ++c) { |
| 44 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1612 times.
|
1640 | if (*c == '-') |
| 45 | 28 | break; | |
| 46 |
6/8✓ Branch 0 taken 1612 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1612 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 654 times.
✓ Branch 5 taken 958 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 650 times.
|
1612 | if ((*c < '0') || (*c > 'f') || ((*c > '9') && (*c < 'a'))) |
| 47 | 4 | return false; | |
| 48 | } | ||
| 49 | |||
| 50 | // Walk through all algorithms | ||
| 51 |
2/2✓ Branch 0 taken 140 times.
✓ Branch 1 taken 30 times.
|
170 | for (unsigned j = 0; j < kAny; ++j) { |
| 52 | 140 | const unsigned hex_length = 2 * kDigestSizes[j]; | |
| 53 | 140 | const unsigned algo_id_length = kAlgorithmIdSizes[j]; | |
| 54 |
2/2✓ Branch 0 taken 44 times.
✓ Branch 1 taken 96 times.
|
140 | if (i == hex_length) { |
| 55 | // Right suffix? | ||
| 56 |
4/4✓ Branch 0 taken 122 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 106 times.
✓ Branch 3 taken 16 times.
|
130 | for (; (i < l) && (i - hex_length < algo_id_length); ++i, ++c) { |
| 57 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 86 times.
|
106 | if (*c != kAlgorithmIds[j][i - hex_length]) |
| 58 | 20 | break; | |
| 59 | } | ||
| 60 |
3/4✓ Branch 0 taken 8 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
44 | if ((i == l) && (l == hex_length + algo_id_length)) |
| 61 | 8 | return true; | |
| 62 | 36 | i = hex_length; | |
| 63 | 36 | c = str->data() + i; | |
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | 30 | return false; | |
| 68 | } | ||
| 69 | |||
| 70 | |||
| 71 | ✗ | Algorithms ParseHashAlgorithm(const string &algorithm_option) { | |
| 72 | ✗ | if (algorithm_option == "sha1") | |
| 73 | ✗ | return kSha1; | |
| 74 | ✗ | if (algorithm_option == "rmd160") | |
| 75 | ✗ | return kRmd160; | |
| 76 | ✗ | if (algorithm_option == "shake128") | |
| 77 | ✗ | return kShake128; | |
| 78 | ✗ | return kAny; | |
| 79 | } | ||
| 80 | |||
| 81 | |||
| 82 | 112292 | Any MkFromHexPtr(const HexPtr hex, const char suffix) { | |
| 83 | 112292 | Any result; | |
| 84 | |||
| 85 | 112292 | const unsigned length = hex.str->length(); | |
| 86 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 112290 times.
|
112292 | if (length == 2 * kDigestSizes[kMd5]) |
| 87 | 2 | result = Any(kMd5, hex); | |
| 88 |
2/2✓ Branch 0 taken 112096 times.
✓ Branch 1 taken 196 times.
|
112292 | if (length == 2 * kDigestSizes[kSha1]) |
| 89 | 112096 | result = Any(kSha1, hex); | |
| 90 | // TODO(jblomer) compare -rmd160, -shake128 | ||
| 91 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 112272 times.
|
112292 | if ((length == 2 * kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160])) |
| 92 | 20 | result = Any(kRmd160, hex); | |
| 93 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 112266 times.
|
112292 | if ((length == 2 * kDigestSizes[kShake128] + kAlgorithmIdSizes[kShake128])) |
| 94 | 26 | result = Any(kShake128, hex); | |
| 95 | |||
| 96 | 112292 | result.suffix = suffix; | |
| 97 | 112292 | return result; | |
| 98 | } | ||
| 99 | |||
| 100 | |||
| 101 | /** | ||
| 102 | * Similar to MkFromHexPtr but the suffix is deducted from the HexPtr string. | ||
| 103 | */ | ||
| 104 | 14888 | Any MkFromSuffixedHexPtr(const HexPtr hex) { | |
| 105 | 14888 | Any result; | |
| 106 | |||
| 107 | 14888 | const unsigned length = hex.str->length(); | |
| 108 |
2/2✓ Branch 0 taken 762 times.
✓ Branch 1 taken 14126 times.
|
14888 | if ((length == 2 * kDigestSizes[kMd5]) |
| 109 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 760 times.
|
762 | || (length == 2 * kDigestSizes[kMd5] + 1)) { |
| 110 | 14128 | const Suffix suffix = (length == 2 * kDigestSizes[kMd5] + 1) | |
| 111 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14126 times.
|
14128 | ? *(hex.str->rbegin()) |
| 112 | 14128 | : kSuffixNone; | |
| 113 | 14128 | result = Any(kMd5, hex, suffix); | |
| 114 | } | ||
| 115 |
2/2✓ Branch 0 taken 14480 times.
✓ Branch 1 taken 408 times.
|
14888 | if ((length == 2 * kDigestSizes[kSha1]) |
| 116 |
2/2✓ Branch 0 taken 338 times.
✓ Branch 1 taken 14142 times.
|
14480 | || (length == 2 * kDigestSizes[kSha1] + 1)) { |
| 117 | 746 | const Suffix suffix = (length == 2 * kDigestSizes[kSha1] + 1) | |
| 118 |
2/2✓ Branch 0 taken 338 times.
✓ Branch 1 taken 408 times.
|
746 | ? *(hex.str->rbegin()) |
| 119 | 746 | : kSuffixNone; | |
| 120 | 746 | result = Any(kSha1, hex, suffix); | |
| 121 | } | ||
| 122 |
2/2✓ Branch 0 taken 14886 times.
✓ Branch 1 taken 2 times.
|
14888 | if ((length == 2 * kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160]) |
| 123 | 14886 | || (length | |
| 124 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 14880 times.
|
14886 | == 2 * kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160] + 1)) { |
| 125 | const Suffix suffix = (length | ||
| 126 | 8 | == 2 * kDigestSizes[kRmd160] | |
| 127 | 8 | + kAlgorithmIdSizes[kRmd160] + 1) | |
| 128 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | ? *(hex.str->rbegin()) |
| 129 | 8 | : kSuffixNone; | |
| 130 | 8 | result = Any(kRmd160, hex, suffix); | |
| 131 | } | ||
| 132 |
2/2✓ Branch 0 taken 14886 times.
✓ Branch 1 taken 2 times.
|
14888 | if ((length == 2 * kDigestSizes[kShake128] + kAlgorithmIdSizes[kShake128]) |
| 133 | 14886 | || (length | |
| 134 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14884 times.
|
14886 | == 2 * kDigestSizes[kShake128] + kAlgorithmIdSizes[kShake128] + 1)) { |
| 135 | const Suffix suffix = (length | ||
| 136 | 4 | == 2 * kDigestSizes[kShake128] | |
| 137 | 4 | + kAlgorithmIdSizes[kShake128] + 1) | |
| 138 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | ? *(hex.str->rbegin()) |
| 139 | 4 | : kSuffixNone; | |
| 140 | 4 | result = Any(kShake128, hex, suffix); | |
| 141 | } | ||
| 142 | |||
| 143 | 14888 | return result; | |
| 144 | } | ||
| 145 | |||
| 146 | |||
| 147 | /** | ||
| 148 | * Allows the caller to create the context on the stack. | ||
| 149 | */ | ||
| 150 | 1749598 | unsigned GetContextSize(const Algorithms algorithm) { | |
| 151 |
4/5✓ Branch 0 taken 1206772 times.
✓ Branch 1 taken 542415 times.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 401 times.
✗ Branch 4 not taken.
|
1749598 | switch (algorithm) { |
| 152 | 1206772 | case kMd5: | |
| 153 | 1206772 | return sizeof(MD5_CTX); | |
| 154 | 542415 | case kSha1: | |
| 155 | 542415 | return sizeof(SHA_CTX); | |
| 156 | 26 | case kRmd160: | |
| 157 | 26 | return sizeof(RIPEMD160_CTX); | |
| 158 | 401 | case kShake128: | |
| 159 | 401 | return sizeof(Keccak_HashInstance); | |
| 160 | ✗ | default: | |
| 161 | ✗ | PANIC(kLogDebug | kLogSyslogErr, | |
| 162 | "tried to generate hash context for unspecified hash. Aborting..."); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | 1749834 | void Init(ContextPtr context) { | |
| 167 | HashReturn keccak_result; | ||
| 168 |
4/5✓ Branch 0 taken 1206778 times.
✓ Branch 1 taken 542633 times.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 401 times.
✗ Branch 4 not taken.
|
1749834 | switch (context.algorithm) { |
| 169 | 1206778 | case kMd5: | |
| 170 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1206778 times.
|
1206778 | assert(context.size == sizeof(MD5_CTX)); |
| 171 | 1206778 | MD5_Init(reinterpret_cast<MD5_CTX *>(context.buffer)); | |
| 172 | 1206778 | break; | |
| 173 | 542633 | case kSha1: | |
| 174 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 542633 times.
|
542633 | assert(context.size == sizeof(SHA_CTX)); |
| 175 | 542633 | SHA1_Init(reinterpret_cast<SHA_CTX *>(context.buffer)); | |
| 176 | 542627 | break; | |
| 177 | 26 | case kRmd160: | |
| 178 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | assert(context.size == sizeof(RIPEMD160_CTX)); |
| 179 | 26 | RIPEMD160_Init(reinterpret_cast<RIPEMD160_CTX *>(context.buffer)); | |
| 180 | 26 | break; | |
| 181 | 401 | case kShake128: | |
| 182 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 401 times.
|
401 | assert(context.size == sizeof(Keccak_HashInstance)); |
| 183 | 401 | keccak_result = Keccak_HashInitialize_SHAKE128( | |
| 184 | reinterpret_cast<Keccak_HashInstance *>(context.buffer)); | ||
| 185 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 401 times.
|
401 | assert(keccak_result == SUCCESS); |
| 186 | 401 | break; | |
| 187 | ✗ | default: | |
| 188 | ✗ | PANIC(NULL); // Undefined hash | |
| 189 | } | ||
| 190 | 1749832 | } | |
| 191 | |||
| 192 | 189651687 | void Update(const unsigned char *buffer, const unsigned buffer_length, | |
| 193 | ContextPtr context) { | ||
| 194 | HashReturn keccak_result; | ||
| 195 |
4/5✓ Branch 0 taken 50343732 times.
✓ Branch 1 taken 47032856 times.
✓ Branch 2 taken 46137374 times.
✓ Branch 3 taken 46137735 times.
✗ Branch 4 not taken.
|
189651687 | switch (context.algorithm) { |
| 196 | 50343732 | case kMd5: | |
| 197 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50343732 times.
|
50343732 | assert(context.size == sizeof(MD5_CTX)); |
| 198 | 50343732 | MD5_Update(reinterpret_cast<MD5_CTX *>(context.buffer), buffer, | |
| 199 | buffer_length); | ||
| 200 | 50343732 | break; | |
| 201 | 47032856 | case kSha1: | |
| 202 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 47032856 times.
|
47032856 | assert(context.size == sizeof(SHA_CTX)); |
| 203 | 47032856 | SHA1_Update(reinterpret_cast<SHA_CTX *>(context.buffer), buffer, | |
| 204 | buffer_length); | ||
| 205 | 47034950 | break; | |
| 206 | 46137374 | case kRmd160: | |
| 207 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46137374 times.
|
46137374 | assert(context.size == sizeof(RIPEMD160_CTX)); |
| 208 | 46137374 | RIPEMD160_Update(reinterpret_cast<RIPEMD160_CTX *>(context.buffer), | |
| 209 | buffer, buffer_length); | ||
| 210 | 46137374 | break; | |
| 211 | 46137735 | case kShake128: | |
| 212 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46137735 times.
|
46137735 | assert(context.size == sizeof(Keccak_HashInstance)); |
| 213 | 92275470 | keccak_result = Keccak_HashUpdate( | |
| 214 | 46137735 | reinterpret_cast<Keccak_HashInstance *>(context.buffer), buffer, | |
| 215 | 46137735 | buffer_length * 8); | |
| 216 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46137735 times.
|
46137735 | assert(keccak_result == SUCCESS); |
| 217 | 46137735 | break; | |
| 218 | ✗ | default: | |
| 219 | ✗ | PANIC(NULL); // Undefined hash | |
| 220 | } | ||
| 221 | 189653791 | } | |
| 222 | |||
| 223 | 1717448 | void Final(ContextPtr context, Any *any_digest) { | |
| 224 | HashReturn keccak_result; | ||
| 225 |
4/5✓ Branch 0 taken 1206770 times.
✓ Branch 1 taken 510277 times.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 401 times.
✗ Branch 4 not taken.
|
1717448 | switch (context.algorithm) { |
| 226 | 1206770 | case kMd5: | |
| 227 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1206770 times.
|
1206770 | assert(context.size == sizeof(MD5_CTX)); |
| 228 | 1206770 | MD5_Final(any_digest->digest, | |
| 229 | 1206770 | reinterpret_cast<MD5_CTX *>(context.buffer)); | |
| 230 | 1206770 | break; | |
| 231 | 510277 | case kSha1: | |
| 232 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 510277 times.
|
510277 | assert(context.size == sizeof(SHA_CTX)); |
| 233 | 510277 | SHA1_Final(any_digest->digest, | |
| 234 | 510277 | reinterpret_cast<SHA_CTX *>(context.buffer)); | |
| 235 | 513047 | break; | |
| 236 | 26 | case kRmd160: | |
| 237 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | assert(context.size == sizeof(RIPEMD160_CTX)); |
| 238 | 26 | RIPEMD160_Final(any_digest->digest, | |
| 239 | 26 | reinterpret_cast<RIPEMD160_CTX *>(context.buffer)); | |
| 240 | 26 | break; | |
| 241 | 401 | case kShake128: | |
| 242 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 401 times.
|
401 | assert(context.size == sizeof(Keccak_HashInstance)); |
| 243 | 802 | keccak_result = Keccak_HashFinal( | |
| 244 | 401 | reinterpret_cast<Keccak_HashInstance *>(context.buffer), NULL); | |
| 245 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 401 times.
|
401 | assert(keccak_result == SUCCESS); |
| 246 | 802 | keccak_result = Keccak_HashSqueeze( | |
| 247 | 401 | reinterpret_cast<Keccak_HashInstance *>(context.buffer), | |
| 248 | 401 | any_digest->digest, kDigestSizes[kShake128] * 8); | |
| 249 | 401 | break; | |
| 250 | ✗ | default: | |
| 251 | ✗ | PANIC(NULL); // Undefined hash | |
| 252 | } | ||
| 253 | 1720244 | any_digest->algorithm = context.algorithm; | |
| 254 | 1720244 | } | |
| 255 | |||
| 256 | |||
| 257 | 8469 | void HashMem(const unsigned char *buffer, const unsigned buffer_size, | |
| 258 | Any *any_digest) { | ||
| 259 | 8469 | const Algorithms algorithm = any_digest->algorithm; | |
| 260 |
1/2✓ Branch 1 taken 8469 times.
✗ Branch 2 not taken.
|
8469 | ContextPtr context(algorithm); |
| 261 | 8469 | context.buffer = alloca(context.size); | |
| 262 | |||
| 263 |
1/2✓ Branch 1 taken 8469 times.
✗ Branch 2 not taken.
|
8469 | Init(context); |
| 264 |
1/2✓ Branch 1 taken 8469 times.
✗ Branch 2 not taken.
|
8469 | Update(buffer, buffer_size, context); |
| 265 |
1/2✓ Branch 1 taken 8469 times.
✗ Branch 2 not taken.
|
8469 | Final(context, any_digest); |
| 266 | 8469 | } | |
| 267 | |||
| 268 | |||
| 269 | 438 | void HashString(const std::string &content, Any *any_digest) { | |
| 270 | 438 | HashMem(reinterpret_cast<const unsigned char *>(content.data()), | |
| 271 | 438 | content.length(), any_digest); | |
| 272 | 438 | } | |
| 273 | |||
| 274 | |||
| 275 | 602494 | void Hmac(const string &key, | |
| 276 | const unsigned char *buffer, | ||
| 277 | const unsigned buffer_size, | ||
| 278 | Any *any_digest) { | ||
| 279 | 602494 | const Algorithms algorithm = any_digest->algorithm; | |
| 280 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 602494 times.
|
602494 | assert(algorithm != kAny); |
| 281 | |||
| 282 | 602494 | const unsigned block_size = kBlockSizes[algorithm]; | |
| 283 | 602494 | unsigned char key_block[block_size]; | |
| 284 | 602494 | memset(key_block, 0, block_size); | |
| 285 |
2/2✓ Branch 1 taken 2444 times.
✓ Branch 2 taken 600050 times.
|
602494 | if (key.length() > block_size) { |
| 286 |
1/2✓ Branch 1 taken 2444 times.
✗ Branch 2 not taken.
|
2444 | Any hash_key(algorithm); |
| 287 |
1/2✓ Branch 3 taken 2444 times.
✗ Branch 4 not taken.
|
2444 | HashMem(reinterpret_cast<const unsigned char *>(key.data()), key.length(), |
| 288 | &hash_key); | ||
| 289 | 2444 | memcpy(key_block, hash_key.digest, kDigestSizes[algorithm]); | |
| 290 | } else { | ||
| 291 |
2/2✓ Branch 1 taken 600044 times.
✓ Branch 2 taken 6 times.
|
600050 | if (key.length() > 0) |
| 292 | 600044 | memcpy(key_block, key.data(), key.length()); | |
| 293 | } | ||
| 294 | |||
| 295 | 602494 | unsigned char pad_block[block_size]; | |
| 296 | // Inner hash | ||
| 297 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | Any hash_inner(algorithm); |
| 298 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | ContextPtr context_inner(algorithm); |
| 299 | 602494 | context_inner.buffer = alloca(context_inner.size); | |
| 300 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | Init(context_inner); |
| 301 |
2/2✓ Branch 0 taken 38559616 times.
✓ Branch 1 taken 602494 times.
|
39162110 | for (unsigned i = 0; i < block_size; ++i) |
| 302 | 38559616 | pad_block[i] = key_block[i] ^ 0x36; | |
| 303 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | Update(pad_block, block_size, context_inner); |
| 304 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | Update(buffer, buffer_size, context_inner); |
| 305 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | Final(context_inner, &hash_inner); |
| 306 | |||
| 307 | // Outer hash | ||
| 308 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | ContextPtr context_outer(algorithm); |
| 309 | 602494 | context_outer.buffer = alloca(context_outer.size); | |
| 310 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | Init(context_outer); |
| 311 |
2/2✓ Branch 0 taken 38559616 times.
✓ Branch 1 taken 602494 times.
|
39162110 | for (unsigned i = 0; i < block_size; ++i) |
| 312 | 38559616 | pad_block[i] = key_block[i] ^ 0x5c; | |
| 313 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | Update(pad_block, block_size, context_outer); |
| 314 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | Update(hash_inner.digest, kDigestSizes[algorithm], context_outer); |
| 315 | |||
| 316 |
1/2✓ Branch 1 taken 602494 times.
✗ Branch 2 not taken.
|
602494 | Final(context_outer, any_digest); |
| 317 | 602494 | } | |
| 318 | |||
| 319 | |||
| 320 | 4602 | bool HashFd(int fd, Any *any_digest) { | |
| 321 | 4602 | const Algorithms algorithm = any_digest->algorithm; | |
| 322 |
1/2✓ Branch 1 taken 4602 times.
✗ Branch 2 not taken.
|
4602 | ContextPtr context(algorithm); |
| 323 | 4602 | context.buffer = alloca(context.size); | |
| 324 | |||
| 325 |
1/2✓ Branch 1 taken 4602 times.
✗ Branch 2 not taken.
|
4602 | Init(context); |
| 326 | unsigned char io_buffer[4096]; | ||
| 327 | int actual_bytes; | ||
| 328 |
3/4✓ Branch 1 taken 1914606 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1910004 times.
✓ Branch 4 taken 4602 times.
|
1914606 | while ((actual_bytes = read(fd, io_buffer, 4096)) != 0) { |
| 329 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1910004 times.
|
1910004 | if (actual_bytes == -1) { |
| 330 | ✗ | if (errno == EINTR) | |
| 331 | ✗ | continue; | |
| 332 | ✗ | return false; | |
| 333 | } | ||
| 334 |
1/2✓ Branch 1 taken 1910004 times.
✗ Branch 2 not taken.
|
1910004 | Update(io_buffer, actual_bytes, context); |
| 335 | } | ||
| 336 |
1/2✓ Branch 1 taken 4602 times.
✗ Branch 2 not taken.
|
4602 | Final(context, any_digest); |
| 337 | 4602 | return true; | |
| 338 | } | ||
| 339 | |||
| 340 | |||
| 341 | 4602 | bool HashFile(const std::string &filename, Any *any_digest) { | |
| 342 | 4602 | const int fd = open(filename.c_str(), O_RDONLY); | |
| 343 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4602 times.
|
4602 | if (fd == -1) |
| 344 | ✗ | return false; | |
| 345 | |||
| 346 | 4602 | const bool result = HashFd(fd, any_digest); | |
| 347 | 4602 | close(fd); | |
| 348 | 4602 | return result; | |
| 349 | } | ||
| 350 | |||
| 351 | |||
| 352 | /** | ||
| 353 | * Fast constructor for hashing path names. | ||
| 354 | */ | ||
| 355 | 4999 | Md5::Md5(const AsciiPtr ascii) { | |
| 356 | 4999 | algorithm = kMd5; | |
| 357 | 4999 | const string *str = ascii.str; | |
| 358 | |||
| 359 | MD5_CTX md5_state; | ||
| 360 |
1/2✓ Branch 1 taken 4999 times.
✗ Branch 2 not taken.
|
4999 | MD5_Init(&md5_state); |
| 361 |
1/2✓ Branch 3 taken 4999 times.
✗ Branch 4 not taken.
|
4999 | MD5_Update(&md5_state, reinterpret_cast<const unsigned char *>(&(*str)[0]), |
| 362 | str->length()); | ||
| 363 |
1/2✓ Branch 1 taken 4999 times.
✗ Branch 2 not taken.
|
4999 | MD5_Final(digest, &md5_state); |
| 364 | 4999 | } | |
| 365 | |||
| 366 | |||
| 367 | 2970945 | Md5::Md5(const char *chars, const unsigned length) { | |
| 368 | 2970945 | algorithm = kMd5; | |
| 369 | |||
| 370 | MD5_CTX md5_state; | ||
| 371 |
1/2✓ Branch 1 taken 2970945 times.
✗ Branch 2 not taken.
|
2970945 | MD5_Init(&md5_state); |
| 372 |
1/2✓ Branch 1 taken 2970945 times.
✗ Branch 2 not taken.
|
2970945 | MD5_Update(&md5_state, reinterpret_cast<const unsigned char *>(chars), |
| 373 | length); | ||
| 374 |
1/2✓ Branch 1 taken 2970945 times.
✗ Branch 2 not taken.
|
2970945 | MD5_Final(digest, &md5_state); |
| 375 | 2970945 | } | |
| 376 | |||
| 377 | |||
| 378 | ✗ | Md5::Md5(const uint64_t lo, const uint64_t hi) { | |
| 379 | ✗ | algorithm = kMd5; | |
| 380 | ✗ | memcpy(digest, &lo, 8); | |
| 381 | ✗ | memcpy(digest + 8, &hi, 8); | |
| 382 | } | ||
| 383 | |||
| 384 | 6307 | void Md5::ToIntPair(uint64_t *lo, uint64_t *hi) const { | |
| 385 | 6307 | memcpy(lo, digest, 8); | |
| 386 | 6307 | memcpy(hi, digest + 8, 8); | |
| 387 | 6307 | } | |
| 388 | |||
| 389 | |||
| 390 | 600052 | Md5 Any::CastToMd5() { | |
| 391 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 600052 times.
|
600052 | assert(algorithm == kMd5); |
| 392 | 600052 | Md5 result; | |
| 393 | 600052 | memcpy(result.digest, digest, kDigestSizes[kMd5]); | |
| 394 | 600052 | return result; | |
| 395 | } | ||
| 396 | |||
| 397 | #ifndef OPENSSL_API_INTERFACE_V09 | ||
| 398 | 12 | static string HexFromSha256(unsigned char digest[SHA256_DIGEST_LENGTH]) { | |
| 399 | 12 | string result; | |
| 400 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | result.reserve(2 * SHA256_DIGEST_LENGTH); |
| 401 |
2/2✓ Branch 0 taken 384 times.
✓ Branch 1 taken 12 times.
|
396 | for (unsigned i = 0; i < SHA256_DIGEST_LENGTH; ++i) { |
| 402 | 384 | const char d1 = digest[i] / 16; | |
| 403 | 384 | const char d2 = digest[i] % 16; | |
| 404 |
3/4✓ Branch 0 taken 222 times.
✓ Branch 1 taken 162 times.
✓ Branch 3 taken 384 times.
✗ Branch 4 not taken.
|
384 | result.push_back(d1 + ((d1 <= 9) ? '0' : 'a' - 10)); |
| 405 |
3/4✓ Branch 0 taken 266 times.
✓ Branch 1 taken 118 times.
✓ Branch 3 taken 384 times.
✗ Branch 4 not taken.
|
384 | result.push_back(d2 + ((d2 <= 9) ? '0' : 'a' - 10)); |
| 406 | } | ||
| 407 | 12 | return result; | |
| 408 | } | ||
| 409 | #endif | ||
| 410 | |||
| 411 | 2 | string Sha256File(const string &filename) { | |
| 412 | #ifdef OPENSSL_API_INTERFACE_V09 | ||
| 413 | PANIC(NULL); | ||
| 414 | #else | ||
| 415 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | const int fd = open(filename.c_str(), O_RDONLY); |
| 416 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (fd < 0) |
| 417 | ✗ | return ""; | |
| 418 | |||
| 419 | SHA256_CTX ctx; | ||
| 420 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | SHA256_Init(&ctx); |
| 421 | |||
| 422 | unsigned char io_buffer[4096]; | ||
| 423 | int actual_bytes; | ||
| 424 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
|
2 | while ((actual_bytes = read(fd, io_buffer, 4096)) != 0) { |
| 425 | ✗ | if (actual_bytes == -1) { | |
| 426 | ✗ | if (errno == EINTR) | |
| 427 | ✗ | continue; | |
| 428 | ✗ | close(fd); | |
| 429 | ✗ | return ""; | |
| 430 | } | ||
| 431 | ✗ | SHA256_Update(&ctx, io_buffer, actual_bytes); | |
| 432 | } | ||
| 433 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | close(fd); |
| 434 | |||
| 435 | unsigned char digest[SHA256_DIGEST_LENGTH]; | ||
| 436 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | SHA256_Final(digest, &ctx); |
| 437 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | return HexFromSha256(digest); |
| 438 | #endif | ||
| 439 | } | ||
| 440 | |||
| 441 | 4 | string Sha256Mem(const unsigned char *buffer, const unsigned buffer_size) { | |
| 442 | #ifdef OPENSSL_API_INTERFACE_V09 | ||
| 443 | PANIC(NULL); | ||
| 444 | #else | ||
| 445 | unsigned char digest[SHA256_DIGEST_LENGTH]; | ||
| 446 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | SHA256(buffer, buffer_size, digest); |
| 447 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | return HexFromSha256(digest); |
| 448 | #endif | ||
| 449 | } | ||
| 450 | |||
| 451 | 2 | string Sha256String(const string &content) { | |
| 452 | 2 | return Sha256Mem(reinterpret_cast<const unsigned char *>(content.data()), | |
| 453 | 2 | content.length()); | |
| 454 | } | ||
| 455 | |||
| 456 | |||
| 457 | 14 | std::string Hmac256(const std::string &key, | |
| 458 | const std::string &content, | ||
| 459 | bool raw_output) { | ||
| 460 | #ifdef OPENSSL_API_INTERFACE_V09 | ||
| 461 | PANIC(NULL); | ||
| 462 | #else | ||
| 463 | unsigned char digest[SHA256_DIGEST_LENGTH]; | ||
| 464 | 14 | const unsigned block_size = 64; | |
| 465 | 14 | const unsigned key_length = key.length(); | |
| 466 | unsigned char key_block[block_size]; | ||
| 467 | 14 | memset(key_block, 0, block_size); | |
| 468 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
|
14 | if (key_length > block_size) { |
| 469 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | SHA256(reinterpret_cast<const unsigned char *>(key.data()), key_length, |
| 470 | key_block); | ||
| 471 | } else { | ||
| 472 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | if (key.length() > 0) |
| 473 | 12 | memcpy(key_block, key.data(), key_length); | |
| 474 | } | ||
| 475 | |||
| 476 | unsigned char pad_block[block_size]; | ||
| 477 | // Inner hash | ||
| 478 | SHA256_CTX ctx_inner; | ||
| 479 | unsigned char digest_inner[SHA256_DIGEST_LENGTH]; | ||
| 480 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | SHA256_Init(&ctx_inner); |
| 481 |
2/2✓ Branch 0 taken 896 times.
✓ Branch 1 taken 14 times.
|
910 | for (unsigned i = 0; i < block_size; ++i) |
| 482 | 896 | pad_block[i] = key_block[i] ^ 0x36; | |
| 483 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | SHA256_Update(&ctx_inner, pad_block, block_size); |
| 484 |
1/2✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
|
14 | SHA256_Update(&ctx_inner, content.data(), content.length()); |
| 485 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | SHA256_Final(digest_inner, &ctx_inner); |
| 486 | |||
| 487 | // Outer hash | ||
| 488 | SHA256_CTX ctx_outer; | ||
| 489 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | SHA256_Init(&ctx_outer); |
| 490 |
2/2✓ Branch 0 taken 896 times.
✓ Branch 1 taken 14 times.
|
910 | for (unsigned i = 0; i < block_size; ++i) |
| 491 | 896 | pad_block[i] = key_block[i] ^ 0x5c; | |
| 492 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | SHA256_Update(&ctx_outer, pad_block, block_size); |
| 493 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | SHA256_Update(&ctx_outer, digest_inner, SHA256_DIGEST_LENGTH); |
| 494 | |||
| 495 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | SHA256_Final(digest, &ctx_outer); |
| 496 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
|
14 | if (raw_output) |
| 497 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | return string(reinterpret_cast<const char *>(digest), SHA256_DIGEST_LENGTH); |
| 498 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | return HexFromSha256(digest); |
| 499 | #endif | ||
| 500 | } | ||
| 501 | |||
| 502 | } // namespace shash | ||
| 503 | |||
| 504 | #ifdef CVMFS_NAMESPACE_GUARD | ||
| 505 | } // namespace CVMFS_NAMESPACE_GUARD | ||
| 506 | #endif | ||
| 507 |