| /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying | 
 |    file LICENSE.rst or https://cmake.org/licensing for details.  */ | 
 | #include "cmCryptoHash.h" | 
 |  | 
 | #include <cassert> | 
 |  | 
 | #include <cm/memory> | 
 |  | 
 | #include <cm3p/kwiml/int.h> | 
 | #include <cm3p/rhash.h> | 
 |  | 
 | #include "cmsys/FStream.hxx" | 
 |  | 
 | static unsigned int const cmCryptoHashAlgoToId[] = { | 
 |   /* clang-format needs this comment to break after the opening brace */ | 
 |   RHASH_MD5,      // | 
 |   RHASH_SHA1,     // | 
 |   RHASH_SHA224,   // | 
 |   RHASH_SHA256,   // | 
 |   RHASH_SHA384,   // | 
 |   RHASH_SHA512,   // | 
 |   RHASH_SHA3_224, // | 
 |   RHASH_SHA3_256, // | 
 |   RHASH_SHA3_384, // | 
 |   RHASH_SHA3_512 | 
 | }; | 
 |  | 
 | static int cmCryptoHash_rhash_library_initialized; | 
 |  | 
 | static rhash cmCryptoHash_rhash_init(unsigned int id) | 
 | { | 
 |   if (!cmCryptoHash_rhash_library_initialized) { | 
 |     cmCryptoHash_rhash_library_initialized = 1; | 
 |     rhash_library_init(); | 
 |   } | 
 |   return rhash_init(id); | 
 | } | 
 |  | 
 | cmCryptoHash::cmCryptoHash(Algo algo) | 
 |   : Id(cmCryptoHashAlgoToId[algo]) | 
 |   , CTX(cmCryptoHash_rhash_init(this->Id)) | 
 | { | 
 | } | 
 |  | 
 | cmCryptoHash::~cmCryptoHash() | 
 | { | 
 |   rhash_free(this->CTX); | 
 | } | 
 |  | 
 | std::unique_ptr<cmCryptoHash> cmCryptoHash::New(cm::string_view algo) | 
 | { | 
 |   if (algo == "MD5") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoMD5); | 
 |   } | 
 |   if (algo == "SHA1") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoSHA1); | 
 |   } | 
 |   if (algo == "SHA224") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoSHA224); | 
 |   } | 
 |   if (algo == "SHA256") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoSHA256); | 
 |   } | 
 |   if (algo == "SHA384") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoSHA384); | 
 |   } | 
 |   if (algo == "SHA512") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoSHA512); | 
 |   } | 
 |   if (algo == "SHA3_224") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoSHA3_224); | 
 |   } | 
 |   if (algo == "SHA3_256") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoSHA3_256); | 
 |   } | 
 |   if (algo == "SHA3_384") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoSHA3_384); | 
 |   } | 
 |   if (algo == "SHA3_512") { | 
 |     return cm::make_unique<cmCryptoHash>(AlgoSHA3_512); | 
 |   } | 
 |   return std::unique_ptr<cmCryptoHash>(); | 
 | } | 
 |  | 
 | std::string cmCryptoHash::GetHashAlgoName() const | 
 | { | 
 | #ifndef CMAKE_USE_SYSTEM_LIBRHASH | 
 |   static_assert(RHASH_HASH_COUNT == 10, "Update switch statement!"); | 
 | #endif | 
 |   switch (this->Id) { | 
 |     case RHASH_MD5: | 
 |       return "MD5"; | 
 |     case RHASH_SHA1: | 
 |       return "SHA1"; | 
 |     case RHASH_SHA224: | 
 |       return "SHA224"; | 
 |     case RHASH_SHA256: | 
 |       return "SHA256"; | 
 |     case RHASH_SHA384: | 
 |       return "SHA384"; | 
 |     case RHASH_SHA512: | 
 |       return "SHA512"; | 
 |     case RHASH_SHA3_224: | 
 |       return "SHA3_224"; | 
 |     case RHASH_SHA3_256: | 
 |       return "SHA3_256"; | 
 |     case RHASH_SHA3_384: | 
 |       return "SHA3_384"; | 
 |     case RHASH_SHA3_512: | 
 |       return "SHA3_512"; | 
 |   } | 
 |   assert(false); | 
 |   return "UNKNOWN"; | 
 | } | 
 |  | 
 | bool cmCryptoHash::IntFromHexDigit(char input, char& output) | 
 | { | 
 |   if (input >= '0' && input <= '9') { | 
 |     output = static_cast<char>(input - '0'); | 
 |     return true; | 
 |   } | 
 |   if (input >= 'a' && input <= 'f') { | 
 |     output = static_cast<char>(input - 'a' + 0xA); | 
 |     return true; | 
 |   } | 
 |   if (input >= 'A' && input <= 'F') { | 
 |     output = static_cast<char>(input - 'A' + 0xA); | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | std::string cmCryptoHash::ByteHashToString( | 
 |   std::vector<unsigned char> const& hash) | 
 | { | 
 |   // Map from 4-bit index to hexadecimal representation. | 
 |   static char const hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', | 
 |                                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; | 
 |  | 
 |   std::string res; | 
 |   res.reserve(hash.size() * 2); | 
 |   for (unsigned char v : hash) { | 
 |     res.push_back(hex[v >> 4]); | 
 |     res.push_back(hex[v & 0xF]); | 
 |   } | 
 |   return res; | 
 | } | 
 |  | 
 | std::vector<unsigned char> cmCryptoHash::ByteHashString(cm::string_view input) | 
 | { | 
 |   this->Initialize(); | 
 |   this->Append(input); | 
 |   return this->Finalize(); | 
 | } | 
 |  | 
 | std::vector<unsigned char> cmCryptoHash::ByteHashFile(std::string const& file) | 
 | { | 
 |   cmsys::ifstream fin(file.c_str(), std::ios::in | std::ios::binary); | 
 |   if (fin) { | 
 |     this->Initialize(); | 
 |     { | 
 |       // Should be efficient enough on most system: | 
 |       KWIML_INT_uint64_t buffer[512]; | 
 |       char* buffer_c = reinterpret_cast<char*>(buffer); | 
 |       unsigned char const* buffer_uc = | 
 |         reinterpret_cast<unsigned char const*>(buffer); | 
 |       // This copy loop is very sensitive on certain platforms with | 
 |       // slightly broken stream libraries (like HPUX).  Normally, it is | 
 |       // incorrect to not check the error condition on the fin.read() | 
 |       // before using the data, but the fin.gcount() will be zero if an | 
 |       // error occurred.  Therefore, the loop should be safe everywhere. | 
 |       while (fin) { | 
 |         fin.read(buffer_c, sizeof(buffer)); | 
 |         if (int gcount = static_cast<int>(fin.gcount())) { | 
 |           this->Append(buffer_uc, gcount); | 
 |         } | 
 |       } | 
 |     } | 
 |     if (fin.eof()) { | 
 |       // Success | 
 |       return this->Finalize(); | 
 |     } | 
 |     // Finalize anyway | 
 |     this->Finalize(); | 
 |   } | 
 |   // Return without success | 
 |   return std::vector<unsigned char>(); | 
 | } | 
 |  | 
 | std::string cmCryptoHash::HashString(cm::string_view input) | 
 | { | 
 |   return ByteHashToString(this->ByteHashString(input)); | 
 | } | 
 |  | 
 | std::string cmCryptoHash::HashFile(std::string const& file) | 
 | { | 
 |   return ByteHashToString(this->ByteHashFile(file)); | 
 | } | 
 |  | 
 | void cmCryptoHash::Initialize() | 
 | { | 
 |   rhash_reset(this->CTX); | 
 | } | 
 |  | 
 | void cmCryptoHash::Append(void const* buf, size_t sz) | 
 | { | 
 |   rhash_update(this->CTX, buf, sz); | 
 | } | 
 |  | 
 | void cmCryptoHash::Append(cm::string_view input) | 
 | { | 
 |   rhash_update(this->CTX, input.data(), input.size()); | 
 | } | 
 |  | 
 | std::vector<unsigned char> cmCryptoHash::Finalize() | 
 | { | 
 |   std::vector<unsigned char> hash(rhash_get_digest_size(this->Id), 0); | 
 |   rhash_final(this->CTX, hash.data()); | 
 |   return hash; | 
 | } | 
 |  | 
 | std::string cmCryptoHash::FinalizeHex() | 
 | { | 
 |   return cmCryptoHash::ByteHashToString(this->Finalize()); | 
 | } |