CernVM-FS  2.12.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
hash.cc
Go to the documentation of this file.
1 
5 #include "cvmfs_config.h"
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 "crypto/openssl_version.h"
21 #include "util/exception.h"
22 #include "KeccakHash.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[] =
34  {"", "", "-rmd160", "-shake128", ""};
35 
36 
37 bool HexPtr::IsValid() const {
38  const unsigned l = str->length();
39  if (l == 0)
40  return false;
41  const char *c = str->data(); // Walks through the string
42  unsigned i = 0; // String position of *c
43 
44  for ( ; i < l; ++i, ++c) {
45  if (*c == '-')
46  break;
47  if ((*c < '0') || (*c > 'f') || ((*c > '9') && (*c < 'a')))
48  return false;
49  }
50 
51  // Walk through all algorithms
52  for (unsigned j = 0; j < kAny; ++j) {
53  const unsigned hex_length = 2*kDigestSizes[j];
54  const unsigned algo_id_length = kAlgorithmIdSizes[j];
55  if (i == hex_length) {
56  // Right suffix?
57  for ( ; (i < l) && (i-hex_length < algo_id_length); ++i, ++c) {
58  if (*c != kAlgorithmIds[j][i-hex_length])
59  break;
60  }
61  if ((i == l) && (l == hex_length + algo_id_length))
62  return true;
63  i = hex_length;
64  c = str->data() + i;
65  }
66  }
67 
68  return false;
69 }
70 
71 
72 Algorithms ParseHashAlgorithm(const string &algorithm_option) {
73  if (algorithm_option == "sha1")
74  return kSha1;
75  if (algorithm_option == "rmd160")
76  return kRmd160;
77  if (algorithm_option == "shake128")
78  return kShake128;
79  return kAny;
80 }
81 
82 
83 Any MkFromHexPtr(const HexPtr hex, const char suffix) {
84  Any result;
85 
86  const unsigned length = hex.str->length();
87  if (length == 2*kDigestSizes[kMd5])
88  result = Any(kMd5, hex);
89  if (length == 2*kDigestSizes[kSha1])
90  result = Any(kSha1, hex);
91  // TODO(jblomer) compare -rmd160, -shake128
92  if ((length == 2*kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160]))
93  result = Any(kRmd160, hex);
94  if ((length == 2*kDigestSizes[kShake128] + kAlgorithmIdSizes[kShake128]))
95  result = Any(kShake128, hex);
96 
97  result.suffix = suffix;
98  return result;
99 }
100 
101 
106  Any result;
107 
108  const unsigned length = hex.str->length();
109  if ((length == 2*kDigestSizes[kMd5]) || (length == 2*kDigestSizes[kMd5] + 1))
110  {
111  Suffix suffix = (length == 2*kDigestSizes[kMd5] + 1) ?
112  *(hex.str->rbegin()) : kSuffixNone;
113  result = Any(kMd5, hex, suffix);
114  }
115  if ((length == 2*kDigestSizes[kSha1]) ||
116  (length == 2*kDigestSizes[kSha1] + 1))
117  {
118  Suffix suffix = (length == 2*kDigestSizes[kSha1] + 1) ?
119  *(hex.str->rbegin()) : kSuffixNone;
120  result = Any(kSha1, hex, suffix);
121  }
122  if ((length == 2*kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160]) ||
123  (length == 2*kDigestSizes[kRmd160] + kAlgorithmIdSizes[kRmd160] + 1))
124  {
125  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 == 2*kDigestSizes[kShake128] + kAlgorithmIdSizes[kShake128] + 1))
133  {
134  Suffix suffix =
135  (length == 2*kDigestSizes[kShake128] + kAlgorithmIdSizes[kShake128] + 1)
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 {
193  HashReturn keccak_result;
194  switch (context.algorithm) {
195  case kMd5:
196  assert(context.size == sizeof(MD5_CTX));
197  MD5_Update(reinterpret_cast<MD5_CTX *>(context.buffer),
198  buffer, buffer_length);
199  break;
200  case kSha1:
201  assert(context.size == sizeof(SHA_CTX));
202  SHA1_Update(reinterpret_cast<SHA_CTX *>(context.buffer),
203  buffer, buffer_length);
204  break;
205  case kRmd160:
206  assert(context.size == sizeof(RIPEMD160_CTX));
207  RIPEMD160_Update(reinterpret_cast<RIPEMD160_CTX *>(context.buffer),
208  buffer, buffer_length);
209  break;
210  case kShake128:
211  assert(context.size == sizeof(Keccak_HashInstance));
212  keccak_result = Keccak_HashUpdate(reinterpret_cast<Keccak_HashInstance *>(
213  context.buffer), buffer, 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(reinterpret_cast<Keccak_HashInstance *>(
242  context.buffer), NULL);
243  assert(keccak_result == SUCCESS);
244  keccak_result =
245  Keccak_HashSqueeze(reinterpret_cast<Keccak_HashInstance *>(
246  context.buffer), 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 {
258  Algorithms algorithm = any_digest->algorithm;
259  ContextPtr context(algorithm);
260  context.buffer = alloca(context.size);
261 
262  Init(context);
263  Update(buffer, buffer_size, context);
264  Final(context, any_digest);
265 }
266 
267 
268 void HashString(const std::string &content, Any *any_digest) {
269  HashMem(reinterpret_cast<const unsigned char *>(content.data()),
270  content.length(), any_digest);
271 }
272 
273 
274 void Hmac(
275  const string &key,
276  const unsigned char *buffer,
277  const unsigned buffer_size,
278  Any *any_digest
279 ) {
280  Algorithms algorithm = any_digest->algorithm;
281  assert(algorithm != kAny);
282 
283  const unsigned block_size = kBlockSizes[algorithm];
284  unsigned char key_block[block_size];
285  memset(key_block, 0, block_size);
286  if (key.length() > block_size) {
287  Any hash_key(algorithm);
288  HashMem(reinterpret_cast<const unsigned char *>(key.data()),
289  key.length(), &hash_key);
290  memcpy(key_block, hash_key.digest, kDigestSizes[algorithm]);
291  } else {
292  if (key.length() > 0)
293  memcpy(key_block, key.data(), key.length());
294  }
295 
296  unsigned char pad_block[block_size];
297  // Inner hash
298  Any hash_inner(algorithm);
299  ContextPtr context_inner(algorithm);
300  context_inner.buffer = alloca(context_inner.size);
301  Init(context_inner);
302  for (unsigned i = 0; i < block_size; ++i)
303  pad_block[i] = key_block[i] ^ 0x36;
304  Update(pad_block, block_size, context_inner);
305  Update(buffer, buffer_size, context_inner);
306  Final(context_inner, &hash_inner);
307 
308  // Outer hash
309  ContextPtr context_outer(algorithm);
310  context_outer.buffer = alloca(context_outer.size);
311  Init(context_outer);
312  for (unsigned i = 0; i < block_size; ++i)
313  pad_block[i] = key_block[i] ^ 0x5c;
314  Update(pad_block, block_size, context_outer);
315  Update(hash_inner.digest, kDigestSizes[algorithm], context_outer);
316 
317  Final(context_outer, any_digest);
318 }
319 
320 
321 bool HashFd(int fd, Any *any_digest) {
322  Algorithms algorithm = any_digest->algorithm;
323  ContextPtr context(algorithm);
324  context.buffer = alloca(context.size);
325 
326  Init(context);
327  unsigned char io_buffer[4096];
328  int actual_bytes;
329  while ((actual_bytes = read(fd, io_buffer, 4096)) != 0) {
330  if (actual_bytes == -1) {
331  if (errno == EINTR)
332  continue;
333  return false;
334  }
335  Update(io_buffer, actual_bytes, context);
336  }
337  Final(context, any_digest);
338  return true;
339 }
340 
341 
342 bool HashFile(const std::string &filename, Any *any_digest) {
343  int fd = open(filename.c_str(), O_RDONLY);
344  if (fd == -1)
345  return false;
346 
347  bool result = HashFd(fd, any_digest);
348  close(fd);
349  return result;
350 }
351 
352 
356 Md5::Md5(const AsciiPtr ascii) {
357  algorithm = kMd5;
358  const string *str = ascii.str;
359 
360  MD5_CTX md5_state;
361  MD5_Init(&md5_state);
362  MD5_Update(&md5_state, reinterpret_cast<const unsigned char *>(&(*str)[0]),
363  str->length());
364  MD5_Final(digest, &md5_state);
365 }
366 
367 
368 Md5::Md5(const char *chars, const unsigned length) {
369  algorithm = kMd5;
370 
371  MD5_CTX md5_state;
372  MD5_Init(&md5_state);
373  MD5_Update(&md5_state, reinterpret_cast<const unsigned char *>(chars),
374  length);
375  MD5_Final(digest, &md5_state);
376 }
377 
378 
379 Md5::Md5(const uint64_t lo, const uint64_t hi) {
380  algorithm = kMd5;
381  memcpy(digest, &lo, 8);
382  memcpy(digest+8, &hi, 8);
383 }
384 
385 void Md5::ToIntPair(uint64_t *lo, uint64_t *hi) const {
386  memcpy(lo, digest, 8);
387  memcpy(hi, digest+8, 8);
388 }
389 
390 
391 Md5 Any::CastToMd5() {
392  assert(algorithm == kMd5);
393  Md5 result;
394  memcpy(result.digest, digest, kDigestSizes[kMd5]);
395  return result;
396 }
397 
398 #ifndef OPENSSL_API_INTERFACE_V09
399 static string HexFromSha256(unsigned char digest[SHA256_DIGEST_LENGTH]) {
400  string result;
401  result.reserve(2 * SHA256_DIGEST_LENGTH);
402  for (unsigned i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
403  const char d1 = digest[i] / 16;
404  const char d2 = digest[i] % 16;
405  result.push_back(d1 + ((d1 <= 9) ? '0' : 'a' - 10));
406  result.push_back(d2 + ((d2 <= 9) ? '0' : 'a' - 10));
407  }
408  return result;
409 }
410 #endif
411 
412 string Sha256File(const string &filename) {
413 #ifdef OPENSSL_API_INTERFACE_V09
414  PANIC(NULL);
415 #else
416  int fd = open(filename.c_str(), O_RDONLY);
417  if (fd < 0)
418  return "";
419 
420  SHA256_CTX ctx;
421  SHA256_Init(&ctx);
422 
423  unsigned char io_buffer[4096];
424  int actual_bytes;
425  while ((actual_bytes = read(fd, io_buffer, 4096)) != 0) {
426  if (actual_bytes == -1) {
427  if (errno == EINTR)
428  continue;
429  close(fd);
430  return "";
431  }
432  SHA256_Update(&ctx, io_buffer, actual_bytes);
433  }
434  close(fd);
435 
436  unsigned char digest[SHA256_DIGEST_LENGTH];
437  SHA256_Final(digest, &ctx);
438  return HexFromSha256(digest);
439 #endif
440 }
441 
442 string Sha256Mem(const unsigned char *buffer, const unsigned buffer_size) {
443 #ifdef OPENSSL_API_INTERFACE_V09
444  PANIC(NULL);
445 #else
446  unsigned char digest[SHA256_DIGEST_LENGTH];
447  SHA256(buffer, buffer_size, digest);
448  return HexFromSha256(digest);
449 #endif
450 }
451 
452 string Sha256String(const string &content) {
453  return Sha256Mem(reinterpret_cast<const unsigned char *>(content.data()),
454  content.length());
455 }
456 
457 
458 std::string Hmac256(
459  const std::string &key,
460  const std::string &content,
461  bool raw_output)
462 {
463 #ifdef OPENSSL_API_INTERFACE_V09
464  PANIC(NULL);
465 #else
466  unsigned char digest[SHA256_DIGEST_LENGTH];
467  const unsigned block_size = 64;
468  const unsigned key_length = key.length();
469  unsigned char key_block[block_size];
470  memset(key_block, 0, block_size);
471  if (key_length > block_size) {
472  SHA256(reinterpret_cast<const unsigned char *>(key.data()), key_length,
473  key_block);
474  } else {
475  if (key.length() > 0)
476  memcpy(key_block, key.data(), key_length);
477  }
478 
479  unsigned char pad_block[block_size];
480  // Inner hash
481  SHA256_CTX ctx_inner;
482  unsigned char digest_inner[SHA256_DIGEST_LENGTH];
483  SHA256_Init(&ctx_inner);
484  for (unsigned i = 0; i < block_size; ++i)
485  pad_block[i] = key_block[i] ^ 0x36;
486  SHA256_Update(&ctx_inner, pad_block, block_size);
487  SHA256_Update(&ctx_inner, content.data(), content.length());
488  SHA256_Final(digest_inner, &ctx_inner);
489 
490  // Outer hash
491  SHA256_CTX ctx_outer;
492  SHA256_Init(&ctx_outer);
493  for (unsigned i = 0; i < block_size; ++i)
494  pad_block[i] = key_block[i] ^ 0x5c;
495  SHA256_Update(&ctx_outer, pad_block, block_size);
496  SHA256_Update(&ctx_outer, digest_inner, SHA256_DIGEST_LENGTH);
497 
498  SHA256_Final(digest, &ctx_outer);
499  if (raw_output)
500  return string(reinterpret_cast<const char *>(digest), SHA256_DIGEST_LENGTH);
501  return HexFromSha256(digest);
502 #endif
503 }
504 
505 } // namespace shash
506 
507 #ifdef CVMFS_NAMESPACE_GUARD
508 } // namespace CVMFS_NAMESPACE_GUARD
509 #endif
void HashString(const std::string &content, Any *any_digest)
Definition: hash.cc:268
const char kSuffixNone
Definition: compat.h:163
struct cvmcache_context * ctx
const std::string * str
Definition: hash.h:104
bool HashFile(const std::string &filename, Any *any_digest)
Definition: hash.cc:342
string Sha256String(const string &content)
Definition: hash.cc:452
#define PANIC(...)
Definition: exception.h:29
const unsigned kAlgorithmIdSizes[]
Definition: compat.h:160
void Hmac(const string &key, const unsigned char *buffer, const unsigned buffer_size, Any *any_digest)
Definition: hash.cc:274
assert((mem||(size==0))&&"Out Of Memory")
string Sha256File(const string &filename)
Definition: hash.cc:412
static string HexFromSha256(unsigned char digest[SHA256_DIGEST_LENGTH])
Definition: hash.cc:399
Algorithms algorithm
Definition: hash.h:125
unsigned char digest[digest_size_]
Definition: hash.h:124
char algorithm
Algorithms
Definition: hash.h:41
const unsigned kBlockSizes[]
Definition: hash.h:95
const unsigned kDigestSizes[]
Definition: compat.h:37
const std::string * str
Definition: hash.h:110
string Sha256Mem(const unsigned char *buffer, const unsigned buffer_size)
Definition: hash.cc:442
Algorithms algorithm
Definition: hash.h:500
unsigned char digest[20]
static int Init(const loader::LoaderExports *loader_exports)
Definition: cvmfs.cc:2296
void Final(ContextPtr context, Any *any_digest)
Definition: hash.cc:221
void * buffer
Definition: hash.h:501
const char * kAlgorithmIds[]
Definition: hash.cc:33
char Suffix
Definition: hash.h:114
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:105
Algorithms ParseHashAlgorithm(const string &algorithm_option)
Definition: hash.cc:72
bool HashFd(int fd, Any *any_digest)
Definition: hash.cc:321
Any MkFromHexPtr(const HexPtr hex, const char suffix)
Definition: hash.cc:83
std::string Hmac256(const std::string &key, const std::string &content, bool raw_output)
Definition: hash.cc:458
Suffix suffix
Definition: hash.h:126
unsigned size
Definition: hash.h:502