/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCryptoHash.h" #include "cm_kwiml.h" #include "cm_rhash.h" #include "cmsys/FStream.hxx" #include #include "cm_memory.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(Id)) { } cmCryptoHash::~cmCryptoHash() { rhash_free(this->CTX); } std::unique_ptr cmCryptoHash::New(const char* algo) { if (strcmp(algo, "MD5") == 0) { return cm::make_unique(AlgoMD5); } if (strcmp(algo, "SHA1") == 0) { return cm::make_unique(AlgoSHA1); } if (strcmp(algo, "SHA224") == 0) { return cm::make_unique(AlgoSHA224); } if (strcmp(algo, "SHA256") == 0) { return cm::make_unique(AlgoSHA256); } if (strcmp(algo, "SHA384") == 0) { return cm::make_unique(AlgoSHA384); } if (strcmp(algo, "SHA512") == 0) { return cm::make_unique(AlgoSHA512); } if (strcmp(algo, "SHA3_224") == 0) { return cm::make_unique(AlgoSHA3_224); } if (strcmp(algo, "SHA3_256") == 0) { return cm::make_unique(AlgoSHA3_256); } if (strcmp(algo, "SHA3_384") == 0) { return cm::make_unique(AlgoSHA3_384); } if (strcmp(algo, "SHA3_512") == 0) { return cm::make_unique(AlgoSHA3_512); } return std::unique_ptr(nullptr); } bool cmCryptoHash::IntFromHexDigit(char input, char& output) { if (input >= '0' && input <= '9') { output = char(input - '0'); return true; } if (input >= 'a' && input <= 'f') { output = char(input - 'a' + 0xA); return true; } if (input >= 'A' && input <= 'F') { output = char(input - 'A' + 0xA); return true; } return false; } std::string cmCryptoHash::ByteHashToString( const std::vector& 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; for (unsigned char v : hash) { res.push_back(hex[v >> 4]); res.push_back(hex[v & 0xF]); } return res; } std::vector cmCryptoHash::ByteHashString( const std::string& input) { this->Initialize(); this->Append(reinterpret_cast(input.c_str()), static_cast(input.size())); return this->Finalize(); } std::vector cmCryptoHash::ByteHashFile(const std::string& 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(buffer); unsigned char const* buffer_uc = reinterpret_cast(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(fin.gcount())) { this->Append(buffer_uc, gcount); } } } if (fin.eof()) { // Success return this->Finalize(); } // Finalize anyway this->Finalize(); } // Return without success return std::vector(); } std::string cmCryptoHash::HashString(const std::string& input) { return ByteHashToString(this->ByteHashString(input)); } std::string cmCryptoHash::HashFile(const std::string& 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(std::string const& str) { this->Append(str.c_str(), str.size()); } std::vector cmCryptoHash::Finalize() { std::vector hash(rhash_get_digest_size(this->Id), 0); rhash_final(this->CTX, &hash[0]); return hash; } std::string cmCryptoHash::FinalizeHex() { return cmCryptoHash::ByteHashToString(this->Finalize()); }