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