summaryrefslogtreecommitdiffstats
path: root/Source/cmCryptoHash.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmCryptoHash.cxx')
-rw-r--r--Source/cmCryptoHash.cxx194
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());
+}