1 /*
2  * Copyright (C) 2011-2019 Daniel Scharrer
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty.  In no event will the author(s) be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  *    claim that you wrote the original software. If you use this software
14  *    in a product, an acknowledgment in the product documentation would be
15  *    appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  *    misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  */
20 
21 /*!
22  * \file
23  *
24  * Generic hashing utilities.
25  */
26 #ifndef INNOEXTRACT_CRYPTO_ITERATEDHASH_HPP
27 #define INNOEXTRACT_CRYPTO_ITERATEDHASH_HPP
28 
29 // Taken from Crypto++ and modified to fit the project.
30 
31 #include <cstring>
32 
33 #include <boost/cstdint.hpp>
34 #include <boost/range/size.hpp>
35 
36 #include "crypto/checksum.hpp"
37 #include "util/align.hpp"
38 #include "util/endian.hpp"
39 #include "util/math.hpp"
40 #include "util/types.hpp"
41 
42 namespace crypto {
43 
44 template <class T>
45 class iterated_hash : public checksum_base< iterated_hash<T> > {
46 
47 public:
48 
49 	typedef T transform;
50 	typedef typename transform::hash_word hash_word;
51 	typedef typename transform::byte_order byte_order;
52 	static const size_t block_size = transform::block_size;
53 	static const size_t hash_size = transform::hash_size / sizeof(hash_word);
54 
init()55 	void init() { count_lo = count_hi = 0; transform::init(state); }
56 
57 	void update(const char * data, size_t length);
58 
59 	void finalize(char * result);
60 
61 private:
62 
63 	size_t hash(const char * input, size_t length);
64 	void pad(size_t last_block_size, char pad_first = '\x80');
65 
bit_count_hi() const66 	hash_word bit_count_hi() const {
67 		return (count_lo >> (8 * sizeof(hash_word) - 3)) + (count_hi << 3);
68 	}
bit_count_lo() const69 	hash_word bit_count_lo() const { return count_lo << 3; }
70 
71 	char buffer[block_size];
72 	hash_word state[hash_size];
73 
74 	hash_word count_lo, count_hi;
75 
76 };
77 
78 template <class T>
update(const char * data,size_t length)79 void iterated_hash<T>::update(const char * data, size_t length) {
80 
81 	hash_word old_count_lo = count_lo;
82 
83 	if((count_lo = old_count_lo + hash_word(length)) < old_count_lo) {
84 		count_hi++; // carry from low to high
85 	}
86 
87 	count_hi += hash_word(util::safe_right_shift<8 * sizeof(hash_word)>(length));
88 
89 	size_t num = util::mod_power_of_2(old_count_lo, size_t(block_size));
90 
91 	if(num != 0) { // process left over data
92 		if(num + length >= block_size) {
93 			std::memcpy(buffer + num, data, block_size - num);
94 			hash(buffer, block_size);
95 			data += (block_size - num);
96 			length -= (block_size - num);
97 			// drop through and do the rest
98 		} else {
99 			std::memcpy(buffer + num, data, length);
100 			return;
101 		}
102 	}
103 
104 	// now process the input data in blocks of BlockSize bytes and save the leftovers to m_data
105 	if(length >= block_size) {
106 		size_t left_over = hash(data, length);
107 		data += (length - left_over);
108 		length = left_over;
109 	}
110 
111 	if(length) {
112 		memcpy(buffer, data, length);
113 	}
114 }
115 
116 template <class T>
hash(const char * input,size_t length)117 size_t iterated_hash<T>::hash(const char * input, size_t length) {
118 
119 	if(byte_order::native() && util::is_aligned<T>(input)) {
120 
121 		do {
122 
123 			transform::transform(state, reinterpret_cast<const hash_word *>(input));
124 
125 			input += block_size;
126 			length -= block_size;
127 
128 		} while(length >= block_size);
129 
130 	} else {
131 
132 		do {
133 
134 			hash_word aligned_buffer[block_size / sizeof(hash_word)];
135 			byte_order::load(input, aligned_buffer, size_t(boost::size(aligned_buffer)));
136 
137 			transform::transform(state, aligned_buffer);
138 
139 			input += block_size;
140 			length -= block_size;
141 
142 		} while(length >= block_size);
143 
144 	}
145 
146 	return length;
147 }
148 
149 template <class T>
pad(size_t last_block_size,char pad_first)150 void iterated_hash<T>::pad(size_t last_block_size, char pad_first) {
151 
152 	size_t num = util::mod_power_of_2(count_lo, size_t(block_size));
153 
154 	buffer[num++] = pad_first;
155 
156 	if(num <= last_block_size) {
157 		memset(buffer + num, 0, last_block_size - num);
158 	} else {
159 		memset(buffer + num, 0, block_size - num);
160 		hash(buffer, block_size);
161 		memset(buffer, 0, last_block_size);
162 	}
163 }
164 
165 template <class T>
finalize(char * result)166 void iterated_hash<T>::finalize(char * result) {
167 
168 	size_t order = transform::offset * sizeof(hash_word);
169 
170 	size_t size = block_size - 2 * sizeof(hash_word);
171 	pad(size);
172 	byte_order::store(bit_count_lo(), buffer + size + order);
173 	byte_order::store(bit_count_hi(), buffer + size + sizeof(hash_word) - order);
174 
175 	hash(buffer, block_size);
176 
177 	byte_order::store(state, hash_size, result);
178 
179 }
180 
181 } // namespace crypto
182 
183 #endif // INNOEXTRACT_CRYPTO_ITERATEDHASH_HPP
184