1 /*
2 This code is written by kerukuro and released into public domain.
3 */
4 
5 #ifndef DIGESTPP_HASHER_HPP
6 #define DIGESTPP_HASHER_HPP
7 
8 #include <string>
9 #include <array>
10 #include <algorithm>
11 #include <vector>
12 #include <iterator>
13 #include <sstream>
14 #include <cstring>
15 #include <iomanip>
16 
17 #include "detail/traits.hpp"
18 #include "detail/stream_width_fixer.hpp"
19 #include "algorithm/mixin/null_mixin.hpp"
20 
21 namespace digestpp
22 {
23 
24 /**
25  * \brief Main class template implementing the public API for hashing
26  *
27  * Individual hash functions are defined by typedefs.
28  * See \ref digestpp namespace description for description of supported hash functions with usage examples.
29  *
30  * \param HashProvider A class implementing the algorithm via traditional init/update/final interface.
31  * \param Mixin A class template which can be used to inject additional functions to the public API of the hasher.
32  *
33  * \sa digestpp
34  */
35 template<class HashProvider, template <class> class Mixin = mixin::null_mixin>
36 class hasher : public Mixin<HashProvider>
37 {
38 	public:
39 
40 	/**
41 	 * \brief Default constructor
42 	 *
43 	 * \available_if * HashProvider is a hash function with fixed output size, OR
44 	 * * HashProvider is a hash function with sensible default output size, OR
45 	 * * HashProvider is an extendable output function (XOF)
46 	 */
47 	template<typename H=HashProvider, typename std::enable_if<std::is_default_constructible<H>::value>::type* = nullptr>
hasher()48 	hasher()
49 	{
50 		provider.init();
51 	}
52 
53 	/**
54 	 * \brief Constructor with hash size parameter
55 	 *
56 	 * Supported output sizes for each algorithm are listed in the description of corresponding typedef.
57 	 *
58 	 * \available_if * HashProvider supports multiple output sizes, AND
59 	 * * HashProvider is not an extendable output function (XOF)
60 	 *
61 	 * \param[in] hashsize Desired output digest size (in bits).
62 	 * \throw std::runtime_error if the requested output size is not supported by the algorithm.
63 	 */
64 	template<typename H=HashProvider, typename std::enable_if<!detail::is_xof<H>::value>::type* = nullptr>
hasher(size_t hashsize)65 	hasher(size_t hashsize) : provider(hashsize)
66 	{
67 		provider.init();
68 	}
69 
70 	/**
71 	 * \brief Absorbs bytes from a C-style pointer to character buffer
72 	 * \param[in] data Pointer to data to absorb
73 	 * \param[in] len Size of data to absorb (in bytes)
74 	 * \return Reference to *this
75 	 *
76 	 * @par Example:\n
77 	 * @code // Calculate SHA-512/256 digest of a C array and output it in hex format
78 	 * unsigned char c[32];
79 	 * std::iota(c, c + sizeof(c), 0);
80 	 * cout << digestpp::sha512(256).absorb(c, sizeof(c)).hexdigest() << std::endl;
81 	 * @endcode
82 	 */
83 	template<typename T, typename std::enable_if<detail::is_byte<T>::value>::type* = nullptr>
absorb(const T * data,size_t len)84 	inline hasher& absorb(const T* data, size_t len)
85 	{
86 		provider.update(reinterpret_cast<const unsigned char*>(data), len);
87 		return *this;
88 	}
89 
90 	/**
91 	 * \brief Absorbs bytes from std::basic_string
92 	 * \param[in] str String to absorb
93 	 * \return Reference to *this
94 	 */
95 	template<typename T,
96 		typename std::enable_if<detail::is_byte<T>::value && !std::is_same<T, std::string::value_type>::value>::type* = nullptr>
absorb(const std::basic_string<T> & str)97 	inline hasher& absorb(const std::basic_string<T>& str)
98 	{
99 		if (!str.empty())
100 			provider.update(reinterpret_cast<const unsigned char*>(&str[0]), str.size());
101 		return *this;
102 	}
103 
104 	/**
105 	 * \brief Absorbs bytes from std::string
106 	 * \param[in] str String to absorb
107 	 * \return Reference to *this
108 	 * @par Example:\n
109 	 * @code // Calculate BLAKE2b-256 digest from an std::string and output it in hex format
110 	 * std::string str = "The quick brown fox jumps over the lazy dog";
111 	 * std::cout << digestpp::blake2b(256).absorb(str).hexdigest() << std::endl;
112 	 * @endcode
113 	 */
absorb(const std::string & str)114 	inline hasher& absorb(const std::string& str)
115 	{
116 		if (!str.empty())
117 			provider.update(reinterpret_cast<const unsigned char*>(&str[0]), str.size());
118 		return *this;
119 	}
120 
121 	/**
122 	 * \brief Absorbs bytes from std::istream
123 	 * \param[in] istr Stream to absorb
124 	 * \return Reference to *this
125 	 * @par Example:\n
126 	 * @code // Calculate SHA-256 digest of a file and output it in hex format
127 	 * std::ifstream file("filename", std::ios_base::in|std::ios_base::binary);
128 	 * std::cout << digestpp::sha256().absorb(file).hexdigest() << std::endl;
129 	 * @endcode
130 	 */
131 	template<typename T, typename std::enable_if<detail::is_byte<T>::value>::type* = nullptr>
absorb(std::basic_istream<T> & istr)132 	inline hasher& absorb(std::basic_istream<T>& istr)
133 	{
134 		const int tmp_buffer_size = 10000;
135 		unsigned char buffer[tmp_buffer_size];
136 		size_t len = 0;
137 		while (istr.read(reinterpret_cast<T*>(buffer), sizeof(buffer)))
138 		{
139 			provider.update(buffer, sizeof(buffer));
140 			len += sizeof(buffer);
141 		}
142 		size_t gcount = istr.gcount();
143 		if (gcount)
144 		{
145 			provider.update(buffer, gcount);
146 			len += gcount;
147 		}
148 		return *this;
149 	}
150 
151 	/**
152 	 * \brief Absorbs bytes from an iterator sequence
153 	 * \param[in] begin Begin iterator
154 	 * \param[in] end End iterator
155 	 * \return Reference to *this
156 	 *
157 	 * @par Example:\n
158 	 * @code // Calculate SHA-512 digest of a vector and output it in hex format
159      * std::vector<unsigned char> v(100);
160 	 * std::iota(v.begin(), v.end(), 0);
161 	 * std::cout << digestpp::sha512().absorb(v.begin(), v.end()).hexdigest() << std::endl;
162 	 * @endcode
163 	 */
164 	template<typename IT>
absorb(IT begin,IT end)165 	inline hasher& absorb(IT begin, IT end)
166 	{
167 		while (begin != end)
168 		{
169 			unsigned char byte = *begin++;
170 			provider.update(&byte, 1);
171 		}
172 		return *this;
173 	}
174 
175 	/**
176 	 * \brief Squeeze bytes into user-provided preallocated buffer.
177 	 *
178 	 * After each invocation of this function the internal state of the hasher changes
179 	 * so that the next call will generate different (additional) output bytes.
180 	 * To reset the state and start new digest calculation, use \ref reset function.
181 	 *
182 	 * \available_if HashProvider is an extendable output function (XOF)
183 	 *
184 	 * \param[out] buf Buffer to squeeze data to; must be of byte type (char, unsigned char or signed char)
185 	 * \param[in] len Size of data to squeeze (in bytes)
186 	 */
187 	template<typename T, typename H=HashProvider,
188 		typename std::enable_if<detail::is_byte<T>::value && detail::is_xof<H>::value>::type* = nullptr>
squeeze(T * buf,size_t len)189 	inline void squeeze(T* buf, size_t len)
190 	{
191 		provider.squeeze(reinterpret_cast<unsigned char*>(buf), len);
192 	}
193 
194 	/**
195 	 * \brief Squeeze bytes into an output iterator.
196 	 *
197 	 * After each invocation of this function the internal state of the hasher changes
198 	 * so that the next call will generate different (additional) output bytes.
199 	 * To reset the state and start new digest calculation, use \ref reset function.
200 	 *
201 	 * \available_if HashProvider is an extendable output function (XOF)
202 	 *
203 	 * \param[in] len Size of data to squeeze (in bytes)
204 	 * \param[out] it output iterator to a byte container
205 	 * @par Example:\n
206 	 * @code // Generate long output using SHAKE-256 extendable output function using multiple calls to squeeze()
207 	 * std::vector<unsigned char> v;
208 	 * digestpp::shake256 xof;
209 	 * xof.absorb("The quick brown fox jumps over the lazy dog");
210 	 * xof.squeeze(1000, back_inserter(v));
211 	 * xof.squeeze(1000, back_inserter(v));
212 	 * xof.squeeze(1000, back_inserter(v));
213 	 * std::cout << "Squeezed " << v.size() << " bytes." << std::endl;
214 	 * @endcode
215 	 */
216 	template<typename OI, typename H=HashProvider, typename std::enable_if<detail::is_xof<H>::value>::type* = nullptr>
squeeze(size_t len,OI it)217 	inline void squeeze(size_t len, OI it)
218 	{
219 		std::vector<unsigned char> hash(len);
220 		provider.squeeze(&hash[0], len);
221 		std::copy(hash.begin(), hash.end(), it);
222 	}
223 
224 	/**
225 	 * \brief Squeeze bytes and return them as a hex string.
226 	 *
227 	 * After each invocation of this function the internal state of the hasher changes
228 	 * so that the next call will generate different (additional) output bytes.
229 	 * To reset the state and start new digest calculation, use \ref reset function.
230 	 *
231 	 * \available_if HashProvider is an extendable output function (XOF)
232 	 *
233 	 * \param[in] len Size of data to squeeze (in bytes)
234 	 * \return Calculated digest as a hexademical string
235 	 * @par Example:\n
236 	 * @code // Generate 64-byte digest using customizable cSHAKE-256 algorithm and print it in hex format
237 	 * digestpp::cshake256 xof;
238 	 * xof.set_customization("My Customization");
239 	 * std::cout << xof.absorb("The quick brown fox jumps over the lazy dog").hexsqueeze(64) << std::endl;
240 	 * @endcode
241 	 */
242 	template<typename H=HashProvider, typename std::enable_if<detail::is_xof<H>::value>::type* = nullptr>
hexsqueeze(size_t len)243 	inline std::string hexsqueeze(size_t len)
244 	{
245 		std::ostringstream res;
246 		res << std::setfill('0') << std::hex;
247 		squeeze(len, std::ostream_iterator<detail::stream_width_fixer<unsigned int, 2>>(res, ""));
248 		return res.str();
249 	}
250 
251 	/**
252 	 * \brief Output binary digest into user-provided preallocated buffer.
253 	 *
254 	 * This function does not change the state of the hasher and can be called multiple times, producing the same result.
255 	 * To reset the state and start new digest calculation, use \ref reset function.
256 	 *
257 	 * \available_if HashProvider is a hash function (not XOF)
258 	 *
259 	 * \param[out] buf Buffer to squeeze data to; must be of byte type (char, unsigned char or signed char)
260 	 * \param[in] len Size of the buffer
261 	 * \throw std::runtime_error if the buffer size is not enough to fit the calculated digest
262 	 * (fixed by the algorithm or specified in the hasher constructor).
263 	 * @par Example:\n
264 	 * @code // Output binary digest to a raw C array
265 	 * unsigned char buf[32];
266 	 * digestpp::sha3(256).absorb("The quick brown fox jumps over the lazy dog").digest(buf, sizeof(buf));
267 	 * @endcode
268 	 */
269 	template<typename T, typename H=HashProvider,
270 		typename std::enable_if<detail::is_byte<T>::value && !detail::is_xof<H>::value>::type* = nullptr>
digest(T * buf,size_t len) const271 	inline void digest(T* buf, size_t len) const
272 	{
273 		if (len < provider.hash_size() / 8)
274 			throw std::runtime_error("Invalid buffer size");
275 
276 		HashProvider copy(provider);
277 		copy.final(buf);
278 	}
279 
280 	/**
281 	 * \brief Write binary digest into an output iterator.
282 	 *
283 	 * This function does not change the state of the hasher and can be called multiple times, producing the same result.
284 	 * To reset the state and start new digest calculation, use \ref reset function.
285 	 *
286 	 * \available_if HashProvider is a hash function (not XOF)
287 	 *
288 	 * \param[out] it Output iterator to a byte container.
289 	 * @par Example:\n
290 	 * @code // Output binary SHA3-256 digest to a vector
291 	 * vector<unsigned char> v;
292 	 * digestpp::sha3(256).absorb("The quick brown fox jumps over the lazy dog").digest(back_inserter(v));
293 	 * @endcode
294 	 */
295 	template<typename OI, typename H=HashProvider, typename std::enable_if<!detail::is_xof<H>::value>::type* = nullptr>
digest(OI it) const296 	inline void digest(OI it) const
297 	{
298 		HashProvider copy(provider);
299 		std::vector<unsigned char> hash(provider.hash_size() / 8);
300 		copy.final(&hash[0]);
301 		std::copy(hash.begin(), hash.end(), it);
302 	}
303 
304 	/**
305 	 * \brief Return hex digest of absorbed data.
306 	 *
307 	 * This function does not change the state of the hasher and can be called multiple times, producing the same result.
308 	 * To reset the state and start new digest calculation, use \ref reset function.
309 	 *
310 	 * \available_if HashProvider is a hash function (not XOF)
311 	 *
312 	 * \return Calculated digest as a hexademical string
313 	 * @par Example:\n
314 	 * @code // Calculate BLAKE2b digest from a double quoted string and output it in hex format
315 	 * std::cout << digestpp::blake2b().absorb("The quick brown fox jumps over the lazy dog").hexdigest() << std::endl;
316 	 * @endcode
317 	 */
318 	template<typename H=HashProvider, typename std::enable_if<!detail::is_xof<H>::value>::type* = nullptr>
hexdigest() const319 	inline std::string hexdigest() const
320 	{
321 		std::ostringstream res;
322 		res << std::setfill('0') << std::hex;
323 		digest(std::ostream_iterator<detail::stream_width_fixer<unsigned int, 2>>(res, ""));
324 		return res.str();
325 	}
326 
327 	/**
328 	 * \brief Reset the hasher state to start new digest computation.
329 	 *
330 	 * \param[in] resetParameters if true, also clear optional parameters (personalization, salt, etc)
331 	 */
reset(bool resetParameters=false)332 	inline void reset(bool resetParameters = false)
333 	{
334 		if (resetParameters)
335 			provider.clear();
336 		provider.init();
337 	}
338 
339 private:
340 	friend Mixin<HashProvider>;
341 	HashProvider provider;
342 };
343 
344 
345 } // namespace digestpp
346 
347 #endif // DIGESTPP_HASHER_HPP