/* 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 #include #include #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::New(cm::string_view algo) { if (algo == "MD5") { return cm::make_unique(AlgoMD5); } if (algo == "SHA1") { return cm::make_unique(AlgoSHA1); } if (algo == "SHA224") { return cm::make_unique(AlgoSHA224); } if (algo == "SHA256") { return cm::make_unique(AlgoSHA256); } if (algo == "SHA384") { return cm::make_unique(AlgoSHA384); } if (algo == "SHA512") { return cm::make_unique(AlgoSHA512); } if (algo == "SHA3_224") { return cm::make_unique(AlgoSHA3_224); } if (algo == "SHA3_256") { return cm::make_unique(AlgoSHA3_256); } if (algo == "SHA3_384") { return cm::make_unique(AlgoSHA3_384); } if (algo == "SHA3_512") { return cm::make_unique(AlgoSHA3_512); } return std::unique_ptr(nullptr); } bool cmCryptoHash::IntFromHexDigit(char input, char& output) { if (input >= '0' && input <= '9') { output = static_cast(input - '0'); return true; } if (input >= 'a' && input <= 'f') { output = static_cast(input - 'a' + 0xA); return true; } if (input >= 'A' && input <= 'F') { output = static_cast(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; 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 cmCryptoHash::ByteHashString(cm::string_view input) { this->Initialize(); this->Append(input); 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(cm::string_view 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(cm::string_view input) { rhash_update(this->CTX, input.data(), input.size()); } std::vector cmCryptoHash::Finalize() { std::vector 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()); }