digestpp  0.01
Experimental C++11 header-only message digest library.
hasher.hpp
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>
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>
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>
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,
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  */
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>
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  {
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>
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>
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>
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>
244  {
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>
271  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 
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>
296  inline void digest(OI it) const
297  {
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>
319  inline std::string hexdigest() const
320  {
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  */
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
hasher< detail::shake_provider< 256, 24 > > shake256
SHAKE256 function.
Definition: shake.hpp:53
Empty mixin that does not have any additional fuctions.
Definition: null_mixin.hpp:18
void reset(bool resetParameters=false)
Reset the hasher state to start new digest computation.
Definition: hasher.hpp:332