1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCryptoHash.h"
7 #include <cm3p/kwiml/int.h>
8 #include <cm3p/rhash.h>
10 #include "cmsys/FStream.hxx"
12 static unsigned int const cmCryptoHashAlgoToId[] = {
13 /* clang-format needs this comment to break after the opening brace */
26 static int cmCryptoHash_rhash_library_initialized;
28 static rhash cmCryptoHash_rhash_init(unsigned int id)
30 if (!cmCryptoHash_rhash_library_initialized) {
31 cmCryptoHash_rhash_library_initialized = 1;
34 return rhash_init(id);
37 cmCryptoHash::cmCryptoHash(Algo algo)
38 : Id(cmCryptoHashAlgoToId[algo])
39 , CTX(cmCryptoHash_rhash_init(this->Id))
43 cmCryptoHash::~cmCryptoHash()
45 rhash_free(this->CTX);
48 std::unique_ptr<cmCryptoHash> cmCryptoHash::New(cm::string_view algo)
51 return cm::make_unique<cmCryptoHash>(AlgoMD5);
54 return cm::make_unique<cmCryptoHash>(AlgoSHA1);
56 if (algo == "SHA224") {
57 return cm::make_unique<cmCryptoHash>(AlgoSHA224);
59 if (algo == "SHA256") {
60 return cm::make_unique<cmCryptoHash>(AlgoSHA256);
62 if (algo == "SHA384") {
63 return cm::make_unique<cmCryptoHash>(AlgoSHA384);
65 if (algo == "SHA512") {
66 return cm::make_unique<cmCryptoHash>(AlgoSHA512);
68 if (algo == "SHA3_224") {
69 return cm::make_unique<cmCryptoHash>(AlgoSHA3_224);
71 if (algo == "SHA3_256") {
72 return cm::make_unique<cmCryptoHash>(AlgoSHA3_256);
74 if (algo == "SHA3_384") {
75 return cm::make_unique<cmCryptoHash>(AlgoSHA3_384);
77 if (algo == "SHA3_512") {
78 return cm::make_unique<cmCryptoHash>(AlgoSHA3_512);
80 return std::unique_ptr<cmCryptoHash>(nullptr);
83 bool cmCryptoHash::IntFromHexDigit(char input, char& output)
85 if (input >= '0' && input <= '9') {
86 output = static_cast<char>(input - '0');
89 if (input >= 'a' && input <= 'f') {
90 output = static_cast<char>(input - 'a' + 0xA);
93 if (input >= 'A' && input <= 'F') {
94 output = static_cast<char>(input - 'A' + 0xA);
100 std::string cmCryptoHash::ByteHashToString(
101 const std::vector<unsigned char>& hash)
103 // Map from 4-bit index to hexadecimal representation.
104 static char const hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
105 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
108 res.reserve(hash.size() * 2);
109 for (unsigned char v : hash) {
110 res.push_back(hex[v >> 4]);
111 res.push_back(hex[v & 0xF]);
116 std::vector<unsigned char> cmCryptoHash::ByteHashString(cm::string_view input)
120 return this->Finalize();
123 std::vector<unsigned char> cmCryptoHash::ByteHashFile(const std::string& file)
125 cmsys::ifstream fin(file.c_str(), std::ios::in | std::ios::binary);
129 // Should be efficient enough on most system:
130 KWIML_INT_uint64_t buffer[512];
131 char* buffer_c = reinterpret_cast<char*>(buffer);
132 unsigned char const* buffer_uc =
133 reinterpret_cast<unsigned char const*>(buffer);
134 // This copy loop is very sensitive on certain platforms with
135 // slightly broken stream libraries (like HPUX). Normally, it is
136 // incorrect to not check the error condition on the fin.read()
137 // before using the data, but the fin.gcount() will be zero if an
138 // error occurred. Therefore, the loop should be safe everywhere.
140 fin.read(buffer_c, sizeof(buffer));
141 if (int gcount = static_cast<int>(fin.gcount())) {
142 this->Append(buffer_uc, gcount);
148 return this->Finalize();
153 // Return without success
154 return std::vector<unsigned char>();
157 std::string cmCryptoHash::HashString(cm::string_view input)
159 return ByteHashToString(this->ByteHashString(input));
162 std::string cmCryptoHash::HashFile(const std::string& file)
164 return ByteHashToString(this->ByteHashFile(file));
167 void cmCryptoHash::Initialize()
169 rhash_reset(this->CTX);
172 void cmCryptoHash::Append(void const* buf, size_t sz)
174 rhash_update(this->CTX, buf, sz);
177 void cmCryptoHash::Append(cm::string_view input)
179 rhash_update(this->CTX, input.data(), input.size());
182 std::vector<unsigned char> cmCryptoHash::Finalize()
184 std::vector<unsigned char> hash(rhash_get_digest_size(this->Id), 0);
185 rhash_final(this->CTX, hash.data());
189 std::string cmCryptoHash::FinalizeHex()
191 return cmCryptoHash::ByteHashToString(this->Finalize());