CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
hash.cc
Go to the documentation of this file.
1 
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 bool HexPtr::IsValid() const {
37  const unsigned l = str->length();
38  if (l == 0)
39  return false;
40  const char *c = str->data(); // Walks through the string
41  unsigned i = 0; // String position of *c
42 
43  for (; i < l; ++i, ++c) {
44  if (*c == '-')
45  break;
46  if ((*c < '0') || (*c > 'f') || ((*c > '9') && (*c < 'a')))
47  return false;
48  }
49 
50  // Walk through all algorithms
51  for (unsigned j = 0; j < kAny; ++j) {
52  const unsigned hex_length = 2 * kDigestSizes[j];
53  const unsigned algo_id_length = kAlgorithmIdSizes[j];
54  if (i == hex_length) {
55  // Right suffix?
56  for (; (i < l) && (i - hex_length < algo_id_length); ++i, ++c) {
57  if (*c != kAlgorithmIds[j][i - hex_length])
58  break;
59  }
60  if ((i == l) && (l == hex_length + algo_id_length))
61  return true;
62  i = hex_length;
63  c = str->data() + i;
64  }
65  }
66 
67  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 Any MkFromHexPtr(const HexPtr hex, const char suffix) {
83  Any result;
84 
85  const unsigned length = hex.str->length();
86  if (length == 2 * kDigestSizes[kMd5])
87  result = Any(kMd5, hex);
88  if (length == 2 * kDigestSizes[kSha1])
89  result = Any(kSha1, hex);
90  // TODO(jblomer) compare -rmd160, -shake128
91  if ((length == 2 * kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160]))
92  result = Any(kRmd160, hex);
93  if ((length == 2 * kDigestSizes[kShake128] + kAlgorithmIdSizes[kShake128]))
94  result = Any(kShake128, hex);
95 
96  result.suffix = suffix;
97  return result;
98 }
99 
100 
105  Any result;
106 
107  const unsigned length = hex.str->length();
108  if ((length == 2 * kDigestSizes[kMd5])
109  || (length == 2 * kDigestSizes[kMd5] + 1)) {
110  const Suffix suffix = (length == 2 * kDigestSizes[kMd5] + 1)
111  ? *(hex.str->rbegin())
112  : kSuffixNone;
113  result = Any(kMd5, hex, suffix);
114  }
115  if ((length == 2 * kDigestSizes[kSha1])
116  || (length == 2 * kDigestSizes[kSha1] + 1)) {
117  const Suffix suffix = (length == 2 * kDigestSizes[kSha1] + 1)
118  ? *(hex.str->rbegin())
119  : kSuffixNone;
120  result = Any(kSha1, hex, suffix);
121  }
122  if ((length == 2 * kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160])
123  || (length
124  == 2 * kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160] + 1)) {
125  const Suffix suffix =
126  (length == 2 * kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160] + 1)
127  ? *(hex.str->rbegin())
128  : kSuffixNone;
129  result = Any(kRmd160, hex, suffix);
130  }
131  if ((length == 2 * kDigestSizes[kShake128] + kAlgorithmIdSizes[kShake128])
132  || (length
133  == 2 * kDigestSizes[kShake128] + kAlgorithmIdSizes[kShake128] + 1)) {
134  const Suffix suffix = (length == 2 * kDigestSizes[kShake128] +
136  ? *(hex.str->rbegin())
137  : kSuffixNone;
138  result = Any(kShake128, hex, suffix);
139  }
140 
141  return result;
142 }
143 
144 
149  switch (algorithm) {
150  case kMd5:
151  return sizeof(MD5_CTX);
152  case kSha1:
153  return sizeof(SHA_CTX);
154  case kRmd160:
155  return sizeof(RIPEMD160_CTX);
156  case kShake128:
157  return sizeof(Keccak_HashInstance);
158  default:
160  "tried to generate hash context for unspecified hash. Aborting...");
161  }
162 }
163 
164 void Init(ContextPtr context) {
165  HashReturn keccak_result;
166  switch (context.algorithm) {
167  case kMd5:
168  assert(context.size == sizeof(MD5_CTX));
169  MD5_Init(reinterpret_cast<MD5_CTX *>(context.buffer));
170  break;
171  case kSha1:
172  assert(context.size == sizeof(SHA_CTX));
173  SHA1_Init(reinterpret_cast<SHA_CTX *>(context.buffer));
174  break;
175  case kRmd160:
176  assert(context.size == sizeof(RIPEMD160_CTX));
177  RIPEMD160_Init(reinterpret_cast<RIPEMD160_CTX *>(context.buffer));
178  break;
179  case kShake128:
180  assert(context.size == sizeof(Keccak_HashInstance));
181  keccak_result = Keccak_HashInitialize_SHAKE128(
182  reinterpret_cast<Keccak_HashInstance *>(context.buffer));
183  assert(keccak_result == SUCCESS);
184  break;
185  default:
186  PANIC(NULL); // Undefined hash
187  }
188 }
189 
190 void Update(const unsigned char *buffer, const unsigned buffer_length,
191  ContextPtr context) {
192  HashReturn keccak_result;
193  switch (context.algorithm) {
194  case kMd5:
195  assert(context.size == sizeof(MD5_CTX));
196  MD5_Update(reinterpret_cast<MD5_CTX *>(context.buffer), buffer,
197  buffer_length);
198  break;
199  case kSha1:
200  assert(context.size == sizeof(SHA_CTX));
201  SHA1_Update(reinterpret_cast<SHA_CTX *>(context.buffer), buffer,
202  buffer_length);
203  break;
204  case kRmd160:
205  assert(context.size == sizeof(RIPEMD160_CTX));
206  RIPEMD160_Update(reinterpret_cast<RIPEMD160_CTX *>(context.buffer),
207  buffer, buffer_length);
208  break;
209  case kShake128:
210  assert(context.size == sizeof(Keccak_HashInstance));
211  keccak_result = Keccak_HashUpdate(
212  reinterpret_cast<Keccak_HashInstance *>(context.buffer), buffer,
213  buffer_length * 8);
214  assert(keccak_result == SUCCESS);
215  break;
216  default:
217  PANIC(NULL); // Undefined hash
218  }
219 }
220 
221 void Final(ContextPtr context, Any *any_digest) {
222  HashReturn keccak_result;
223  switch (context.algorithm) {
224  case kMd5:
225  assert(context.size == sizeof(MD5_CTX));
226  MD5_Final(any_digest->digest,
227  reinterpret_cast<MD5_CTX *>(context.buffer));
228  break;
229  case kSha1:
230  assert(context.size == sizeof(SHA_CTX));
231  SHA1_Final(any_digest->digest,
232  reinterpret_cast<SHA_CTX *>(context.buffer));
233  break;
234  case kRmd160:
235  assert(context.size == sizeof(RIPEMD160_CTX));
236  RIPEMD160_Final(any_digest->digest,
237  reinterpret_cast<RIPEMD160_CTX *>(context.buffer));
238  break;
239  case kShake128:
240  assert(context.size == sizeof(Keccak_HashInstance));
241  keccak_result = Keccak_HashFinal(
242  reinterpret_cast<Keccak_HashInstance *>(context.buffer), NULL);
243  assert(keccak_result == SUCCESS);
244  keccak_result = Keccak_HashSqueeze(
245  reinterpret_cast<Keccak_HashInstance *>(context.buffer),
246  any_digest->digest, kDigestSizes[kShake128] * 8);
247  break;
248  default:
249  PANIC(NULL); // Undefined hash
250  }
251  any_digest->algorithm = context.algorithm;
252 }
253 
254 
255 void HashMem(const unsigned char *buffer, const unsigned buffer_size,
256  Any *any_digest) {
257  const Algorithms algorithm = any_digest->algorithm;
258  ContextPtr context(algorithm);
259  context.buffer = alloca(context.size);
260 
261  Init(context);
262  Update(buffer, buffer_size, context);
263  Final(context, any_digest);
264 }
265 
266 
267 void HashString(const std::string &content, Any *any_digest) {
268  HashMem(reinterpret_cast<const unsigned char *>(content.data()),
269  content.length(), any_digest);
270 }
271 
272 
273 void Hmac(const string &key,
274  const unsigned char *buffer,
275  const unsigned buffer_size,
276  Any *any_digest) {
277  const Algorithms algorithm = any_digest->algorithm;
278  assert(algorithm != kAny);
279 
280  const unsigned block_size = kBlockSizes[algorithm];
281  unsigned char key_block[block_size];
282  memset(key_block, 0, block_size);
283  if (key.length() > block_size) {
284  Any hash_key(algorithm);
285  HashMem(reinterpret_cast<const unsigned char *>(key.data()), key.length(),
286  &hash_key);
287  memcpy(key_block, hash_key.digest, kDigestSizes[algorithm]);
288  } else {
289  if (key.length() > 0)
290  memcpy(key_block, key.data(), key.length());
291  }
292 
293  unsigned char pad_block[block_size];
294  // Inner hash
295  Any hash_inner(algorithm);
296  ContextPtr context_inner(algorithm);
297  context_inner.buffer = alloca(context_inner.size);
298  Init(context_inner);
299  for (unsigned i = 0; i < block_size; ++i)
300  pad_block[i] = key_block[i] ^ 0x36;
301  Update(pad_block, block_size, context_inner);
302  Update(buffer, buffer_size, context_inner);
303  Final(context_inner, &hash_inner);
304 
305  // Outer hash
306  ContextPtr context_outer(algorithm);
307  context_outer.buffer = alloca(context_outer.size);
308  Init(context_outer);
309  for (unsigned i = 0; i < block_size; ++i)
310  pad_block[i] = key_block[i] ^ 0x5c;
311  Update(pad_block, block_size, context_outer);
312  Update(hash_inner.digest, kDigestSizes[algorithm], context_outer);
313 
314  Final(context_outer, any_digest);
315 }
316 
317 
318 bool HashFd(int fd, Any *any_digest) {
319  const Algorithms algorithm = any_digest->algorithm;
320  ContextPtr context(algorithm);
321  context.buffer = alloca(context.size);
322 
323  Init(context);
324  unsigned char io_buffer[4096];
325  int actual_bytes;
326  while ((actual_bytes = read(fd, io_buffer, 4096)) != 0) {
327  if (actual_bytes == -1) {
328  if (errno == EINTR)
329  continue;
330  return false;
331  }
332  Update(io_buffer, actual_bytes, context);
333  }
334  Final(context, any_digest);
335  return true;
336 }
337 
338 
339 bool HashFile(const std::string &filename, Any *any_digest) {
340  const int fd = open(filename.c_str(), O_RDONLY);
341  if (fd == -1)
342  return false;
343 
344  const bool result = HashFd(fd, any_digest);
345  close(fd);
346  return result;
347 }
348 
349 
353 Md5::Md5(const AsciiPtr ascii) {
354  algorithm = kMd5;
355  const string *str = ascii.str;
356 
357  MD5_CTX md5_state;
358  MD5_Init(&md5_state);
359  MD5_Update(&md5_state, reinterpret_cast<const unsigned char *>(&(*str)[0]),
360  str->length());
361  MD5_Final(digest, &md5_state);
362 }
363 
364 
365 Md5::Md5(const char *chars, const unsigned length) {
366  algorithm = kMd5;
367 
368  MD5_CTX md5_state;
369  MD5_Init(&md5_state);
370  MD5_Update(&md5_state, reinterpret_cast<const unsigned char *>(chars),
371  length);
372  MD5_Final(digest, &md5_state);
373 }
374 
375 
376 Md5::Md5(const uint64_t lo, const uint64_t hi) {
377  algorithm = kMd5;
378  memcpy(digest, &lo, 8);
379  memcpy(digest + 8, &hi, 8);
380 }
381 
382 void Md5::ToIntPair(uint64_t *lo, uint64_t *hi) const {
383  memcpy(lo, digest, 8);
384  memcpy(hi, digest + 8, 8);
385 }
386 
387 
388 Md5 Any::CastToMd5() {
389  assert(algorithm == kMd5);
390  Md5 result;
391  memcpy(result.digest, digest, kDigestSizes[kMd5]);
392  return result;
393 }
394 
395 #ifndef OPENSSL_API_INTERFACE_V09
396 static string HexFromSha256(unsigned char digest[SHA256_DIGEST_LENGTH]) {
397  string result;
398  result.reserve(2 * SHA256_DIGEST_LENGTH);
399  for (unsigned i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
400  const char d1 = digest[i] / 16;
401  const char d2 = digest[i] % 16;
402  result.push_back(d1 + ((d1 <= 9) ? '0' : 'a' - 10));
403  result.push_back(d2 + ((d2 <= 9) ? '0' : 'a' - 10));
404  }
405  return result;
406 }
407 #endif
408 
409 string Sha256File(const string &filename) {
410 #ifdef OPENSSL_API_INTERFACE_V09
411  PANIC(NULL);
412 #else
413  const int fd = open(filename.c_str(), O_RDONLY);
414  if (fd < 0)
415  return "";
416 
417  SHA256_CTX ctx;
418  SHA256_Init(&ctx);
419 
420  unsigned char io_buffer[4096];
421  int actual_bytes;
422  while ((actual_bytes = read(fd, io_buffer, 4096)) != 0) {
423  if (actual_bytes == -1) {
424  if (errno == EINTR)
425  continue;
426  close(fd);
427  return "";
428  }
429  SHA256_Update(&ctx, io_buffer, actual_bytes);
430  }
431  close(fd);
432 
433  unsigned char digest[SHA256_DIGEST_LENGTH];
434  SHA256_Final(digest, &ctx);
435  return HexFromSha256(digest);
436 #endif
437 }
438 
439 string Sha256Mem(const unsigned char *buffer, const unsigned buffer_size) {
440 #ifdef OPENSSL_API_INTERFACE_V09
441  PANIC(NULL);
442 #else
443  unsigned char digest[SHA256_DIGEST_LENGTH];
444  SHA256(buffer, buffer_size, digest);
445  return HexFromSha256(digest);
446 #endif
447 }
448 
449 string Sha256String(const string &content) {
450  return Sha256Mem(reinterpret_cast<const unsigned char *>(content.data()),
451  content.length());
452 }
453 
454 
455 std::string Hmac256(const std::string &key,
456  const std::string &content,
457  bool raw_output) {
458 #ifdef OPENSSL_API_INTERFACE_V09
459  PANIC(NULL);
460 #else
461  unsigned char digest[SHA256_DIGEST_LENGTH];
462  const unsigned block_size = 64;
463  const unsigned key_length = key.length();
464  unsigned char key_block[block_size];
465  memset(key_block, 0, block_size);
466  if (key_length > block_size) {
467  SHA256(reinterpret_cast<const unsigned char *>(key.data()), key_length,
468  key_block);
469  } else {
470  if (key.length() > 0)
471  memcpy(key_block, key.data(), key_length);
472  }
473 
474  unsigned char pad_block[block_size];
475  // Inner hash
476  SHA256_CTX ctx_inner;
477  unsigned char digest_inner[SHA256_DIGEST_LENGTH];
478  SHA256_Init(&ctx_inner);
479  for (unsigned i = 0; i < block_size; ++i)
480  pad_block[i] = key_block[i] ^ 0x36;
481  SHA256_Update(&ctx_inner, pad_block, block_size);
482  SHA256_Update(&ctx_inner, content.data(), content.length());
483  SHA256_Final(digest_inner, &ctx_inner);
484 
485  // Outer hash
486  SHA256_CTX ctx_outer;
487  SHA256_Init(&ctx_outer);
488  for (unsigned i = 0; i < block_size; ++i)
489  pad_block[i] = key_block[i] ^ 0x5c;
490  SHA256_Update(&ctx_outer, pad_block, block_size);
491  SHA256_Update(&ctx_outer, digest_inner, SHA256_DIGEST_LENGTH);
492 
493  SHA256_Final(digest, &ctx_outer);
494  if (raw_output)
495  return string(reinterpret_cast<const char *>(digest), SHA256_DIGEST_LENGTH);
496  return HexFromSha256(digest);
497 #endif
498 }
499 
500 } // namespace shash
501 
502 #ifdef CVMFS_NAMESPACE_GUARD
503 } // namespace CVMFS_NAMESPACE_GUARD
504 #endif
void HashString(const std::string &content, Any *any_digest)
Definition: hash.cc:267
const char kSuffixNone
Definition: compat.h:162
struct cvmcache_context * ctx
const std::string * str
Definition: hash.h:101
bool HashFile(const std::string &filename, Any *any_digest)
Definition: hash.cc:339
string Sha256String(const string &content)
Definition: hash.cc:449
#define PANIC(...)
Definition: exception.h:29
const unsigned kAlgorithmIdSizes[]
Definition: compat.h:159
void Hmac(const string &key, const unsigned char *buffer, const unsigned buffer_size, Any *any_digest)
Definition: hash.cc:273
assert((mem||(size==0))&&"Out Of Memory")
string Sha256File(const string &filename)
Definition: hash.cc:409
static string HexFromSha256(unsigned char digest[SHA256_DIGEST_LENGTH])
Definition: hash.cc:396
Algorithms algorithm
Definition: hash.h:122
unsigned char digest[digest_size_]
Definition: hash.h:121
char algorithm
Algorithms
Definition: hash.h:41
const unsigned kBlockSizes[]
Definition: hash.h:93
const unsigned kDigestSizes[]
Definition: compat.h:37
const std::string * str
Definition: hash.h:107
string Sha256Mem(const unsigned char *buffer, const unsigned buffer_size)
Definition: hash.cc:439
Algorithms algorithm
Definition: hash.h:488
unsigned char digest[20]
static int Init(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:2320
void Final(ContextPtr context, Any *any_digest)
Definition: hash.cc:221
void * buffer
Definition: hash.h:489
const char * kAlgorithmIds[]
Definition: hash.cc:33
char Suffix
Definition: hash.h:111
void HashMem(const unsigned char *buffer, const unsigned buffer_size, Any *any_digest)
Definition: hash.cc:255
unsigned GetContextSize(const Algorithms algorithm)
Definition: hash.cc:148
void Update(const unsigned char *buffer, const unsigned buffer_length, ContextPtr context)
Definition: hash.cc:190
Any MkFromSuffixedHexPtr(const HexPtr hex)
Definition: hash.cc:104
Algorithms ParseHashAlgorithm(const string &algorithm_option)
Definition: hash.cc:71
bool HashFd(int fd, Any *any_digest)
Definition: hash.cc:318
Any MkFromHexPtr(const HexPtr hex, const char suffix)
Definition: hash.cc:82
std::string Hmac256(const std::string &key, const std::string &content, bool raw_output)
Definition: hash.cc:455
Suffix suffix
Definition: hash.h:123
unsigned size
Definition: hash.h:490