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