1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4 
5     hash.cpp
6 
7     Function to handle hash functions (checksums)
8 
9     Based on original idea by Farfetch'd
10 
11 ***************************************************************************/
12 
13 #include "hash.h"
14 #include "hashing.h"
15 #include <cctype>
16 
17 
18 namespace util {
19 //**************************************************************************
20 //  GLOBAL VARIABLES
21 //**************************************************************************
22 
23 char const hash_collection::HASH_CRC;
24 char const hash_collection::HASH_SHA1;
25 
26 char const *const hash_collection::HASH_TYPES_CRC = "R";
27 char const *const hash_collection::HASH_TYPES_CRC_SHA1 = "RS";
28 char const *const hash_collection::HASH_TYPES_ALL = "RS";
29 
30 char const hash_collection::FLAG_NO_DUMP;
31 char const hash_collection::FLAG_BAD_DUMP;
32 
33 
34 
35 //**************************************************************************
36 //  HASH COLLECTION
37 //**************************************************************************
38 
39 //-------------------------------------------------
40 //  hash_collection - constructor
41 //-------------------------------------------------
42 
hash_collection()43 hash_collection::hash_collection()
44 	: m_has_crc32(false),
45 		m_has_sha1(false),
46 		m_creator(nullptr)
47 {
48 }
49 
50 
hash_collection(const char * string)51 hash_collection::hash_collection(const char *string)
52 	: m_has_crc32(false),
53 		m_has_sha1(false),
54 		m_creator(nullptr)
55 {
56 	from_internal_string(string);
57 }
58 
59 
hash_collection(const hash_collection & src)60 hash_collection::hash_collection(const hash_collection &src)
61 	: m_has_crc32(false),
62 		m_has_sha1(false),
63 		m_creator(nullptr)
64 {
65 	copyfrom(src);
66 }
67 
68 
69 //-------------------------------------------------
70 //  ~hash_collection - destructor
71 //-------------------------------------------------
72 
~hash_collection()73 hash_collection::~hash_collection()
74 {
75 	delete m_creator;
76 }
77 
78 
79 //-------------------------------------------------
80 //  operator= - assignment operator
81 //-------------------------------------------------
82 
operator =(const hash_collection & src)83 hash_collection &hash_collection::operator=(const hash_collection &src)
84 {
85 	// ignore self-assignment
86 	if (this != &src)
87 		copyfrom(src);
88 	return *this;
89 }
90 
91 
92 //-------------------------------------------------
93 //  operator== - test for equality
94 //-------------------------------------------------
95 
operator ==(const hash_collection & rhs) const96 bool hash_collection::operator==(const hash_collection &rhs) const
97 {
98 	// match CRCs
99 	int matches = 0;
100 	if (m_has_crc32 && rhs.m_has_crc32)
101 	{
102 		if (m_crc32 != rhs.m_crc32)
103 			return false;
104 		matches++;
105 	}
106 
107 	// match SHA1s
108 	if (m_has_sha1 && rhs.m_has_sha1)
109 	{
110 		if (m_sha1 != rhs.m_sha1)
111 			return false;
112 		matches++;
113 	}
114 
115 	// if all shared hashes match, return true
116 	return (matches > 0);
117 }
118 
119 
120 //-------------------------------------------------
121 //  hash_types - return a list of hash types as
122 //  a string
123 //-------------------------------------------------
124 
hash_types() const125 std::string hash_collection::hash_types() const
126 {
127 	std::string buffer;
128 	if (m_has_crc32)
129 		buffer.push_back(HASH_CRC);
130 	if (m_has_sha1)
131 		buffer.push_back(HASH_SHA1);
132 	return buffer;
133 }
134 
135 
136 //-------------------------------------------------
137 //  reset - reset the hash collection to an empty
138 //  set of hashes and flags
139 //-------------------------------------------------
140 
reset()141 void hash_collection::reset()
142 {
143 	m_flags.clear();
144 	m_has_crc32 = m_has_sha1 = false;
145 	delete m_creator;
146 	m_creator = nullptr;
147 }
148 
149 
150 //-------------------------------------------------
151 //  add_from_string - add a new hash, importing
152 //  from a string
153 //-------------------------------------------------
154 
add_from_string(char type,const char * buffer,int length)155 bool hash_collection::add_from_string(char type, const char *buffer, int length)
156 {
157 	// handle CRCs
158 	if (type == HASH_CRC)
159 		return m_has_crc32 = m_crc32.from_string(buffer, length);
160 
161 	// handle SHA1s
162 	else if (type == HASH_SHA1)
163 		return m_has_sha1 = m_sha1.from_string(buffer, length);
164 
165 	return false;
166 }
167 
168 
169 //-------------------------------------------------
170 //  remove - remove a hash of the given type
171 //-------------------------------------------------
172 
remove(char type)173 bool hash_collection::remove(char type)
174 {
175 	bool result = false;
176 
177 	// handle CRCs
178 	if (type == HASH_CRC)
179 	{
180 		result = m_has_crc32;
181 		m_has_crc32 = false;
182 	}
183 
184 	// handle SHA1s
185 	else if (type == HASH_SHA1)
186 	{
187 		result = m_has_sha1;
188 		m_has_sha1 = false;
189 	}
190 	return result;
191 }
192 
193 
194 //-------------------------------------------------
195 //  internal_string - convert set of hashes and
196 //  flags to a string in our internal compact
197 //  format
198 //-------------------------------------------------
199 
internal_string() const200 std::string hash_collection::internal_string() const
201 {
202 	std::string buffer;
203 
204 	// handle CRCs
205 	if (m_has_crc32) {
206 		buffer.push_back(HASH_CRC);
207 		buffer.append(m_crc32.as_string());
208 	}
209 	// handle SHA1s
210 	if (m_has_sha1) {
211 		buffer.push_back(HASH_SHA1);
212 		buffer.append(m_sha1.as_string());
213 	}
214 
215 	// append flags
216 	buffer.append(m_flags);
217 	return buffer;
218 }
219 
220 
221 //-------------------------------------------------
222 //  macro_string - convert set of hashes and
223 //  flags to a string in the macroized format
224 //-------------------------------------------------
225 
macro_string() const226 std::string hash_collection::macro_string() const
227 {
228 	std::string buffer;
229 
230 	// handle CRCs
231 	if (m_has_crc32)
232 		buffer.append("CRC(").append(m_crc32.as_string()).append(") ");
233 
234 	// handle SHA1s
235 	if (m_has_sha1)
236 		buffer.append("SHA1(").append(m_sha1.as_string()).append(") ");
237 
238 	// append flags
239 	if (flag(FLAG_NO_DUMP))
240 		buffer.append("NO_DUMP ");
241 	if (flag(FLAG_BAD_DUMP))
242 		buffer.append("BAD_DUMP ");
243 	strtrimspace(buffer);
244 	return buffer;
245 }
246 
247 
248 //-------------------------------------------------
249 //  attribute_string - convert set of hashes and
250 //  flags to a string in XML attribute format
251 //-------------------------------------------------
252 
attribute_string() const253 std::string hash_collection::attribute_string() const
254 {
255 	std::string buffer;
256 
257 	// handle CRCs
258 	if (m_has_crc32)
259 		buffer.append("crc=\"").append(m_crc32.as_string()).append("\" ");
260 
261 	// handle SHA1s
262 	if (m_has_sha1)
263 		buffer.append("sha1=\"").append(m_sha1.as_string()).append("\" ");
264 
265 	// append flags
266 	if (flag(FLAG_NO_DUMP))
267 		buffer.append("status=\"nodump\"");
268 	if (flag(FLAG_BAD_DUMP))
269 		buffer.append("status=\"baddump\"");
270 	strtrimspace(buffer);
271 	return buffer;
272 }
273 
274 
275 //-------------------------------------------------
276 //  from_internal_string - convert an internal
277 //  compact string to set of hashes and flags
278 //-------------------------------------------------
279 
from_internal_string(const char * string)280 bool hash_collection::from_internal_string(const char *string)
281 {
282 	assert(string != nullptr);
283 
284 	// start fresh
285 	reset();
286 
287 	// determine the end of the string
288 	const char *stringend = string + strlen(string);
289 	const char *ptr = string;
290 
291 	// loop until we hit it
292 	bool errors = false;
293 	int skip_digits = 0;
294 	while (ptr < stringend)
295 	{
296 		char c = *ptr++;
297 		char uc = toupper(c);
298 
299 		// non-hex alpha values specify a hash type
300 		if (uc >= 'G' && uc <= 'Z')
301 		{
302 			skip_digits = 0;
303 			if (uc == HASH_CRC)
304 			{
305 				m_has_crc32 = true;
306 				errors = !m_crc32.from_string(ptr, stringend - ptr);
307 				skip_digits = 2 * sizeof(crc32_t);
308 			}
309 			else if (uc == HASH_SHA1)
310 			{
311 				m_has_sha1 = true;
312 				errors = !m_sha1.from_string(ptr, stringend - ptr);
313 				skip_digits = 2 * sizeof(sha1_t);
314 			}
315 			else
316 				errors = true;
317 		}
318 
319 		// hex values are ignored, though unexpected
320 		else if ((uc >= '0' && uc <= '9') || (uc >= 'A' && uc <= 'F'))
321 		{
322 			if (skip_digits != 0)
323 				skip_digits--;
324 			else
325 				errors = true;
326 		}
327 
328 		// anything else is a flag
329 		else if (skip_digits != 0)
330 			errors = true;
331 		else
332 			m_flags.push_back(c);
333 	}
334 	return !errors;
335 }
336 
337 
338 //-------------------------------------------------
339 //  begin - begin hashing
340 //-------------------------------------------------
341 
begin(const char * types)342 void hash_collection::begin(const char *types)
343 {
344 	// nuke previous creator and make a new one
345 	delete m_creator;
346 	m_creator = new hash_creator;
347 
348 	// by default use all types
349 	if (types == nullptr)
350 		m_creator->m_doing_crc32 = m_creator->m_doing_sha1 = true;
351 
352 	// otherwise, just allocate the ones that are specified
353 	else
354 	{
355 		m_creator->m_doing_crc32 = (strchr(types, HASH_CRC) != nullptr);
356 		m_creator->m_doing_sha1 = (strchr(types, HASH_SHA1) != nullptr);
357 	}
358 }
359 
360 
361 //-------------------------------------------------
362 //  buffer - add the given buffer to the hash
363 //-------------------------------------------------
364 
buffer(const uint8_t * data,uint32_t length)365 void hash_collection::buffer(const uint8_t *data, uint32_t length)
366 {
367 	assert(m_creator != nullptr);
368 
369 	// append to each active hash
370 	if (m_creator->m_doing_crc32)
371 		m_creator->m_crc32_creator.append(data, length);
372 	if (m_creator->m_doing_sha1)
373 		m_creator->m_sha1_creator.append(data, length);
374 }
375 
376 
377 //-------------------------------------------------
378 //  end - stop hashing
379 //-------------------------------------------------
380 
end()381 void hash_collection::end()
382 {
383 	assert(m_creator != nullptr);
384 
385 	// finish up the CRC32
386 	if (m_creator->m_doing_crc32)
387 	{
388 		m_has_crc32 = true;
389 		m_crc32 = m_creator->m_crc32_creator.finish();
390 	}
391 
392 	// finish up the SHA1
393 	if (m_creator->m_doing_sha1)
394 	{
395 		m_has_sha1 = true;
396 		m_sha1 = m_creator->m_sha1_creator.finish();
397 	}
398 
399 	// nuke the creator
400 	delete m_creator;
401 	m_creator = nullptr;
402 }
403 
404 
405 //-------------------------------------------------
406 //  copyfrom - copy everything from another
407 //  collection
408 //-------------------------------------------------
409 
copyfrom(const hash_collection & src)410 void hash_collection::copyfrom(const hash_collection &src)
411 {
412 	// copy flags directly
413 	m_flags = src.m_flags;
414 
415 	// copy hashes
416 	m_has_crc32 = src.m_has_crc32;
417 	m_crc32 = src.m_crc32;
418 	m_has_sha1 = src.m_has_sha1;
419 	m_sha1 = src.m_sha1;
420 
421 	// don't copy creators
422 	m_creator = nullptr;
423 }
424 
425 } // namespace util
426