/src/CMake/Source/cmCryptoHash.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmCryptoHash.h" |
4 | | |
5 | | #include <cassert> |
6 | | |
7 | | #include <cm/memory> |
8 | | |
9 | | #include <cm3p/kwiml/int.h> |
10 | | #include <cm3p/rhash.h> |
11 | | |
12 | | #include "cmsys/FStream.hxx" |
13 | | |
14 | | static unsigned int const cmCryptoHashAlgoToId[] = { |
15 | | /* clang-format needs this comment to break after the opening brace */ |
16 | | RHASH_MD5, // |
17 | | RHASH_SHA1, // |
18 | | RHASH_SHA224, // |
19 | | RHASH_SHA256, // |
20 | | RHASH_SHA384, // |
21 | | RHASH_SHA512, // |
22 | | RHASH_SHA3_224, // |
23 | | RHASH_SHA3_256, // |
24 | | RHASH_SHA3_384, // |
25 | | RHASH_SHA3_512 |
26 | | }; |
27 | | |
28 | | static int cmCryptoHash_rhash_library_initialized; |
29 | | |
30 | | static rhash cmCryptoHash_rhash_init(unsigned int id) |
31 | 0 | { |
32 | 0 | if (!cmCryptoHash_rhash_library_initialized) { |
33 | 0 | cmCryptoHash_rhash_library_initialized = 1; |
34 | 0 | rhash_library_init(); |
35 | 0 | } |
36 | 0 | return rhash_init(id); |
37 | 0 | } |
38 | | |
39 | | cmCryptoHash::cmCryptoHash(Algo algo) |
40 | 0 | : Id(cmCryptoHashAlgoToId[algo]) |
41 | 0 | , CTX(cmCryptoHash_rhash_init(this->Id)) |
42 | 0 | { |
43 | 0 | } |
44 | | |
45 | | cmCryptoHash::~cmCryptoHash() |
46 | 0 | { |
47 | 0 | rhash_free(this->CTX); |
48 | 0 | } |
49 | | |
50 | | std::unique_ptr<cmCryptoHash> cmCryptoHash::New(cm::string_view algo) |
51 | 0 | { |
52 | 0 | if (algo == "MD5") { |
53 | 0 | return cm::make_unique<cmCryptoHash>(AlgoMD5); |
54 | 0 | } |
55 | 0 | if (algo == "SHA1") { |
56 | 0 | return cm::make_unique<cmCryptoHash>(AlgoSHA1); |
57 | 0 | } |
58 | 0 | if (algo == "SHA224") { |
59 | 0 | return cm::make_unique<cmCryptoHash>(AlgoSHA224); |
60 | 0 | } |
61 | 0 | if (algo == "SHA256") { |
62 | 0 | return cm::make_unique<cmCryptoHash>(AlgoSHA256); |
63 | 0 | } |
64 | 0 | if (algo == "SHA384") { |
65 | 0 | return cm::make_unique<cmCryptoHash>(AlgoSHA384); |
66 | 0 | } |
67 | 0 | if (algo == "SHA512") { |
68 | 0 | return cm::make_unique<cmCryptoHash>(AlgoSHA512); |
69 | 0 | } |
70 | 0 | if (algo == "SHA3_224") { |
71 | 0 | return cm::make_unique<cmCryptoHash>(AlgoSHA3_224); |
72 | 0 | } |
73 | 0 | if (algo == "SHA3_256") { |
74 | 0 | return cm::make_unique<cmCryptoHash>(AlgoSHA3_256); |
75 | 0 | } |
76 | 0 | if (algo == "SHA3_384") { |
77 | 0 | return cm::make_unique<cmCryptoHash>(AlgoSHA3_384); |
78 | 0 | } |
79 | 0 | if (algo == "SHA3_512") { |
80 | 0 | return cm::make_unique<cmCryptoHash>(AlgoSHA3_512); |
81 | 0 | } |
82 | 0 | return std::unique_ptr<cmCryptoHash>(); |
83 | 0 | } |
84 | | |
85 | | std::string cmCryptoHash::GetHashAlgoName() const |
86 | 0 | { |
87 | 0 | #ifndef CMAKE_USE_SYSTEM_LIBRHASH |
88 | 0 | static_assert(RHASH_HASH_COUNT == 10, "Update switch statement!"); |
89 | 0 | #endif |
90 | 0 | switch (this->Id) { |
91 | 0 | case RHASH_MD5: |
92 | 0 | return "MD5"; |
93 | 0 | case RHASH_SHA1: |
94 | 0 | return "SHA1"; |
95 | 0 | case RHASH_SHA224: |
96 | 0 | return "SHA224"; |
97 | 0 | case RHASH_SHA256: |
98 | 0 | return "SHA256"; |
99 | 0 | case RHASH_SHA384: |
100 | 0 | return "SHA384"; |
101 | 0 | case RHASH_SHA512: |
102 | 0 | return "SHA512"; |
103 | 0 | case RHASH_SHA3_224: |
104 | 0 | return "SHA3_224"; |
105 | 0 | case RHASH_SHA3_256: |
106 | 0 | return "SHA3_256"; |
107 | 0 | case RHASH_SHA3_384: |
108 | 0 | return "SHA3_384"; |
109 | 0 | case RHASH_SHA3_512: |
110 | 0 | return "SHA3_512"; |
111 | 0 | } |
112 | 0 | assert(false); |
113 | 0 | return "UNKNOWN"; |
114 | 0 | } |
115 | | |
116 | | bool cmCryptoHash::IntFromHexDigit(char input, char& output) |
117 | 0 | { |
118 | 0 | if (input >= '0' && input <= '9') { |
119 | 0 | output = static_cast<char>(input - '0'); |
120 | 0 | return true; |
121 | 0 | } |
122 | 0 | if (input >= 'a' && input <= 'f') { |
123 | 0 | output = static_cast<char>(input - 'a' + 0xA); |
124 | 0 | return true; |
125 | 0 | } |
126 | 0 | if (input >= 'A' && input <= 'F') { |
127 | 0 | output = static_cast<char>(input - 'A' + 0xA); |
128 | 0 | return true; |
129 | 0 | } |
130 | 0 | return false; |
131 | 0 | } |
132 | | |
133 | | std::string cmCryptoHash::ByteHashToString( |
134 | | std::vector<unsigned char> const& hash) |
135 | 0 | { |
136 | | // Map from 4-bit index to hexadecimal representation. |
137 | 0 | static char const hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', |
138 | 0 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; |
139 | |
|
140 | 0 | std::string res; |
141 | 0 | res.reserve(hash.size() * 2); |
142 | 0 | for (unsigned char v : hash) { |
143 | 0 | res.push_back(hex[v >> 4]); |
144 | 0 | res.push_back(hex[v & 0xF]); |
145 | 0 | } |
146 | 0 | return res; |
147 | 0 | } |
148 | | |
149 | | std::vector<unsigned char> cmCryptoHash::ByteHashString(cm::string_view input) |
150 | 0 | { |
151 | 0 | this->Initialize(); |
152 | 0 | this->Append(input); |
153 | 0 | return this->Finalize(); |
154 | 0 | } |
155 | | |
156 | | std::vector<unsigned char> cmCryptoHash::ByteHashStream(std::istream& sin) |
157 | 0 | { |
158 | 0 | if (sin) { |
159 | 0 | this->Initialize(); |
160 | 0 | { |
161 | | // Should be efficient enough on most system: |
162 | 0 | KWIML_INT_uint64_t buffer[512]; |
163 | 0 | char* buffer_c = reinterpret_cast<char*>(buffer); |
164 | 0 | unsigned char const* buffer_uc = |
165 | 0 | reinterpret_cast<unsigned char const*>(buffer); |
166 | | // This copy loop is very sensitive on certain platforms with |
167 | | // slightly broken stream libraries (like HPUX). Normally, it is |
168 | | // incorrect to not check the error condition on the fin.read() |
169 | | // before using the data, but the fin.gcount() will be zero if an |
170 | | // error occurred. Therefore, the loop should be safe everywhere. |
171 | 0 | while (sin) { |
172 | 0 | sin.read(buffer_c, sizeof(buffer)); |
173 | 0 | if (int gcount = static_cast<int>(sin.gcount())) { |
174 | 0 | this->Append(buffer_uc, gcount); |
175 | 0 | } |
176 | 0 | } |
177 | 0 | } |
178 | 0 | if (sin.eof()) { |
179 | | // Success |
180 | 0 | return this->Finalize(); |
181 | 0 | } |
182 | | // Finalize anyway |
183 | 0 | this->Finalize(); |
184 | 0 | } |
185 | | // Return without success |
186 | 0 | return std::vector<unsigned char>(); |
187 | 0 | } |
188 | | |
189 | | std::vector<unsigned char> cmCryptoHash::ByteHashFile(std::string const& file) |
190 | 0 | { |
191 | 0 | cmsys::ifstream fin(file.c_str(), std::ios::in | std::ios::binary); |
192 | 0 | return this->ByteHashStream(fin); |
193 | 0 | } |
194 | | |
195 | | std::string cmCryptoHash::HashString(cm::string_view input) |
196 | 0 | { |
197 | 0 | return ByteHashToString(this->ByteHashString(input)); |
198 | 0 | } |
199 | | |
200 | | std::string cmCryptoHash::HashStream(std::istream& sin) |
201 | 0 | { |
202 | 0 | return ByteHashToString(this->ByteHashStream(sin)); |
203 | 0 | } |
204 | | |
205 | | std::string cmCryptoHash::HashFile(std::string const& file) |
206 | 0 | { |
207 | 0 | return ByteHashToString(this->ByteHashFile(file)); |
208 | 0 | } |
209 | | |
210 | | void cmCryptoHash::Initialize() |
211 | 0 | { |
212 | 0 | rhash_reset(this->CTX); |
213 | 0 | } |
214 | | |
215 | | void cmCryptoHash::Append(void const* buf, size_t sz) |
216 | 0 | { |
217 | 0 | rhash_update(this->CTX, buf, sz); |
218 | 0 | } |
219 | | |
220 | | void cmCryptoHash::Append(cm::string_view input) |
221 | 0 | { |
222 | 0 | rhash_update(this->CTX, input.data(), input.size()); |
223 | 0 | } |
224 | | |
225 | | std::vector<unsigned char> cmCryptoHash::Finalize() |
226 | 0 | { |
227 | 0 | std::vector<unsigned char> hash(rhash_get_digest_size(this->Id), 0); |
228 | 0 | rhash_final(this->CTX, hash.data()); |
229 | 0 | return hash; |
230 | 0 | } |
231 | | |
232 | | std::string cmCryptoHash::FinalizeHex() |
233 | 0 | { |
234 | 0 | return cmCryptoHash::ByteHashToString(this->Finalize()); |
235 | 0 | } |