diff options
Diffstat (limited to 'Source/cmCryptoHash.cxx')
-rw-r--r-- | Source/cmCryptoHash.cxx | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/Source/cmCryptoHash.cxx b/Source/cmCryptoHash.cxx new file mode 100644 index 0000000..d914eb1 --- /dev/null +++ b/Source/cmCryptoHash.cxx @@ -0,0 +1,194 @@ +/* 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 "cmAlgorithms.h" +#include "cm_kwiml.h" +#include "cm_rhash.h" +#include "cmsys/FStream.hxx" +#include <string.h> + +#include <memory> // IWYU pragma: keep + +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> cmCryptoHash::New(const char* algo) +{ + if (strcmp(algo, "MD5") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoMD5); + } + if (strcmp(algo, "SHA1") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoSHA1); + } + if (strcmp(algo, "SHA224") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoSHA224); + } + if (strcmp(algo, "SHA256") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoSHA256); + } + if (strcmp(algo, "SHA384") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoSHA384); + } + if (strcmp(algo, "SHA512") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoSHA512); + } + if (strcmp(algo, "SHA3_224") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoSHA3_224); + } + if (strcmp(algo, "SHA3_256") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoSHA3_256); + } + if (strcmp(algo, "SHA3_384") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoSHA3_384); + } + if (strcmp(algo, "SHA3_512") == 0) { + return cm::make_unique<cmCryptoHash>(AlgoSHA3_512); + } + return std::unique_ptr<cmCryptoHash>(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<unsigned char>& 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<unsigned char> cmCryptoHash::ByteHashString( + const std::string& input) +{ + this->Initialize(); + this->Append(reinterpret_cast<unsigned char const*>(input.c_str()), + static_cast<int>(input.size())); + return this->Finalize(); +} + +std::vector<unsigned char> 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<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(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<unsigned char> cmCryptoHash::Finalize() +{ + std::vector<unsigned char> 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()); +} |