cmCryptoHash.cxx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCryptoHash.h"
  4. #include <cm/memory>
  5. #include <cm3p/kwiml/int.h>
  6. #include <cm3p/rhash.h>
  7. #include "cmsys/FStream.hxx"
  8. static unsigned int const cmCryptoHashAlgoToId[] = {
  9. /* clang-format needs this comment to break after the opening brace */
  10. RHASH_MD5, //
  11. RHASH_SHA1, //
  12. RHASH_SHA224, //
  13. RHASH_SHA256, //
  14. RHASH_SHA384, //
  15. RHASH_SHA512, //
  16. RHASH_SHA3_224, //
  17. RHASH_SHA3_256, //
  18. RHASH_SHA3_384, //
  19. RHASH_SHA3_512
  20. };
  21. static int cmCryptoHash_rhash_library_initialized;
  22. static rhash cmCryptoHash_rhash_init(unsigned int id)
  23. {
  24. if (!cmCryptoHash_rhash_library_initialized) {
  25. cmCryptoHash_rhash_library_initialized = 1;
  26. rhash_library_init();
  27. }
  28. return rhash_init(id);
  29. }
  30. cmCryptoHash::cmCryptoHash(Algo algo)
  31. : Id(cmCryptoHashAlgoToId[algo])
  32. , CTX(cmCryptoHash_rhash_init(this->Id))
  33. {
  34. }
  35. cmCryptoHash::~cmCryptoHash()
  36. {
  37. rhash_free(this->CTX);
  38. }
  39. std::unique_ptr<cmCryptoHash> cmCryptoHash::New(cm::string_view algo)
  40. {
  41. if (algo == "MD5") {
  42. return cm::make_unique<cmCryptoHash>(AlgoMD5);
  43. }
  44. if (algo == "SHA1") {
  45. return cm::make_unique<cmCryptoHash>(AlgoSHA1);
  46. }
  47. if (algo == "SHA224") {
  48. return cm::make_unique<cmCryptoHash>(AlgoSHA224);
  49. }
  50. if (algo == "SHA256") {
  51. return cm::make_unique<cmCryptoHash>(AlgoSHA256);
  52. }
  53. if (algo == "SHA384") {
  54. return cm::make_unique<cmCryptoHash>(AlgoSHA384);
  55. }
  56. if (algo == "SHA512") {
  57. return cm::make_unique<cmCryptoHash>(AlgoSHA512);
  58. }
  59. if (algo == "SHA3_224") {
  60. return cm::make_unique<cmCryptoHash>(AlgoSHA3_224);
  61. }
  62. if (algo == "SHA3_256") {
  63. return cm::make_unique<cmCryptoHash>(AlgoSHA3_256);
  64. }
  65. if (algo == "SHA3_384") {
  66. return cm::make_unique<cmCryptoHash>(AlgoSHA3_384);
  67. }
  68. if (algo == "SHA3_512") {
  69. return cm::make_unique<cmCryptoHash>(AlgoSHA3_512);
  70. }
  71. return std::unique_ptr<cmCryptoHash>(nullptr);
  72. }
  73. bool cmCryptoHash::IntFromHexDigit(char input, char& output)
  74. {
  75. if (input >= '0' && input <= '9') {
  76. output = char(input - '0');
  77. return true;
  78. }
  79. if (input >= 'a' && input <= 'f') {
  80. output = char(input - 'a' + 0xA);
  81. return true;
  82. }
  83. if (input >= 'A' && input <= 'F') {
  84. output = char(input - 'A' + 0xA);
  85. return true;
  86. }
  87. return false;
  88. }
  89. std::string cmCryptoHash::ByteHashToString(
  90. const std::vector<unsigned char>& hash)
  91. {
  92. // Map from 4-bit index to hexadecimal representation.
  93. static char const hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
  94. '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
  95. std::string res;
  96. res.reserve(hash.size() * 2);
  97. for (unsigned char v : hash) {
  98. res.push_back(hex[v >> 4]);
  99. res.push_back(hex[v & 0xF]);
  100. }
  101. return res;
  102. }
  103. std::vector<unsigned char> cmCryptoHash::ByteHashString(cm::string_view input)
  104. {
  105. this->Initialize();
  106. this->Append(input);
  107. return this->Finalize();
  108. }
  109. std::vector<unsigned char> cmCryptoHash::ByteHashFile(const std::string& file)
  110. {
  111. cmsys::ifstream fin(file.c_str(), std::ios::in | std::ios::binary);
  112. if (fin) {
  113. this->Initialize();
  114. {
  115. // Should be efficient enough on most system:
  116. KWIML_INT_uint64_t buffer[512];
  117. char* buffer_c = reinterpret_cast<char*>(buffer);
  118. unsigned char const* buffer_uc =
  119. reinterpret_cast<unsigned char const*>(buffer);
  120. // This copy loop is very sensitive on certain platforms with
  121. // slightly broken stream libraries (like HPUX). Normally, it is
  122. // incorrect to not check the error condition on the fin.read()
  123. // before using the data, but the fin.gcount() will be zero if an
  124. // error occurred. Therefore, the loop should be safe everywhere.
  125. while (fin) {
  126. fin.read(buffer_c, sizeof(buffer));
  127. if (int gcount = static_cast<int>(fin.gcount())) {
  128. this->Append(buffer_uc, gcount);
  129. }
  130. }
  131. }
  132. if (fin.eof()) {
  133. // Success
  134. return this->Finalize();
  135. }
  136. // Finalize anyway
  137. this->Finalize();
  138. }
  139. // Return without success
  140. return std::vector<unsigned char>();
  141. }
  142. std::string cmCryptoHash::HashString(cm::string_view input)
  143. {
  144. return ByteHashToString(this->ByteHashString(input));
  145. }
  146. std::string cmCryptoHash::HashFile(const std::string& file)
  147. {
  148. return ByteHashToString(this->ByteHashFile(file));
  149. }
  150. void cmCryptoHash::Initialize()
  151. {
  152. rhash_reset(this->CTX);
  153. }
  154. void cmCryptoHash::Append(void const* buf, size_t sz)
  155. {
  156. rhash_update(this->CTX, buf, sz);
  157. }
  158. void cmCryptoHash::Append(cm::string_view input)
  159. {
  160. rhash_update(this->CTX, input.data(), input.size());
  161. }
  162. std::vector<unsigned char> cmCryptoHash::Finalize()
  163. {
  164. std::vector<unsigned char> hash(rhash_get_digest_size(this->Id), 0);
  165. rhash_final(this->CTX, &hash[0]);
  166. return hash;
  167. }
  168. std::string cmCryptoHash::FinalizeHex()
  169. {
  170. return cmCryptoHash::ByteHashToString(this->Finalize());
  171. }