digestpp  0.01
Experimental C++11 header-only message digest library.
digestpp

Experimental C++11 header-only message digest library.

Derived from cppcrypto in an attempt to devise a more modern yet flexible and universal C++ API for cryptographic hash functions.

Tested with g++ 6.4.0, clang 4.0.1 and Visual C++ 2017.

Examples

Calculate BLAKE2b digest from a double quoted string and output it in hex format:

cout << blake2b().absorb("The quick brown fox jumps over the lazy dog").hexdigest();

Calculate BLAKE2b-256 digest from an std::string and output it in hex format:

string str = "The quick brown fox jumps over the lazy dog";
cout << blake2b(256).absorb(str).hexdigest();

Calculate SHA-512 digest of a vector<unsigned char> and output it in hex format:

vector<unsigned char> v;
// ...fill the vector
cout << sha512().absorb(v.begin(), v.end()).hexdigest();

Calculate SHA-512/256 digest of a C array and output it in hex format:

unsigned char c[32];
// ...fill the array
cout << sha512(256).absorb(c, sizeof(c)).hexdigest();

Calculate SHA-256 digest of a file and output it in hex format:

ifstream file("filename", ios_base::in|ios_base::binary);
cout << sha256().absorb(file).hexdigest();

Generate SHA3-224 digest using multiple calls to absorb():

cout << sha3(224).absorb("The quick brown fox ").absorb("jumps over the lazy dog").hexdigest();

Output binary digest to a vector<unsigned char>:

vector<unsigned char> v;
sha3(256).absorb("The quick brown fox jumps over the lazy dog").digest(back_inserter(v));

Output binary digest to a raw C array:

unsigned char buf[32];
sha3(256).absorb("The quick brown fox jumps over the lazy dog").digest(buf, sizeof(buf));

Output binary digest to a stream:

string str = "The quick brown fox jumps over the lazy dog";
string output;
ostringstream os(output);
sha3(256).absorb(str).digest(ostream_iterator<char>(os, ""));

Generate long output using SHAKE-256 extendable output function using multiple calls to squeeze():

vector<unsigned char> v;
xof.absorb("The quick brown fox jumps over the lazy dog");
xof.squeeze(1000, back_inserter(v));
xof.squeeze(1000, back_inserter(v));
xof.squeeze(1000, back_inserter(v));
cout << "Squeezed " << v.size() << " bytes." << endl;

Generate 64-byte digest using customizable cSHAKE-256 algorithm and print it in hex format:

xof.set_customization("Customization");
cout << xof.absorb("The quick brown fox jumps over the lazy dog").hexsqueeze(64);

Hasher class

hasher is a main class template implementing the public API for hashing.

It has two template parameters:

See hasher reference documentation for the description of all public functions.

Individual hash algorithms are defined by typedefs, e.g.

typedef hasher<detail::sha3_provider> sha3;
typedef hasher<detail::blake_provider, detail::blake_mixin> blake;
// ...

Supported algorithms

Hash functions

TypedefDescriptionSupported output sizesOptional parameters
blakeOriginal BLAKE algorithm224, 256, 384, 512salt
blake2bBLAKE2b8-512salt, personalization, key
blake2sBLAKE2s8-256salt, personalization, key
blake2xbBLAKE2xbarbitrarysalt, personalization, key
blake2xsBLAKE2xsarbitrarysalt, personalization, key
groestlGrøstl8-512-
jhJH8-512-
kmac128KMAC128arbitrarykey, customization
kmac256KMAC256arbitrarykey, customization
kupynaKupyna256, 512-
md5MD5128-
sha1SHA-1160-
sha224SHA-224224-
sha256SHA-256256-
sha384SHA-384384-
sha512SHA-5128-512-
sha3SHA-3224, 256, 384, 512-
skein256Skein256arbitrarypersonalization, key, nonce
skein512Skein512arbitrarypersonalization, key, nonce
skein1024Skein1024arbitrarypersonalization, key, nonce
sm3SM3256-
streebogStreebog256, 512-
whirlpoolWhirlpool512-

Extendable output functions

TypedefDescriptionOptional parameters
blake2xb_xofBLAKE2xb in XOF modesalt, personalization, key
blake2xs_xofBLAKE2xs in XOF modesalt, personalization, key
k12KangarooTwelvecustomization
m14MarsupilamiFourteencustomization
shake128SHAKE-128-
shake256SHAKE-256-
cshake128cSHAKE-128function name, customization
cshake256cSHAKE-256function name, customization
kmac128_xofKMAC128 in XOF modekey, customization
kmac256_xofKMAC256 in XOF modekey, customization
skein256_xofSkein256 in XOF modepersonalization, key, nonce
skein512_xofSkein512 in XOF modepersonalization, key, nonce
skein1024_xofSkein1024 in XOF modepersonalization, key, nonce

Design rationale in questions and answers

Q: What is the difference between a hash function with variable output size and an extendable output function (XOF)?

A: Hash functions require the digest size to be known at the moment of initialization and normally produce unrelated outputs for different digest sizes. For example, blake2b(256) and blake2b(512) produce completely different digests. XOFs are functions that do not need to know the output size in advance and can produce outputs of unrestricted size. Bytes generated by XOFs depend only on the input data, but not on the digest size. It is generally recommended to use hash functions instead of XOFs when the output size is known in advance.

Q: What is the difference between digest() and squeeze()?

A. digest() is used with hash functions; it retrieves a digest of a certain length (defined by the algorithm or specified in the constructor). Calling digest() or hexdigest() does not change the internal state, so that these functions can be called more than once and will produce the same output. squeeze() is used with XOF functions; it can be called multiple times to squeeze an arbitrary number of output bytes. After each invocation of squeeze() the internal state changes so that the next call to squeeze() will generate different (additional) output bytes.

Q: For hash functions with variable output size, why the output size is not a template parameter, e.g. sha3<256>?

A: While it may seem cool to make the output size a template parameter, in some usage scenarios the required digest size is not known at compile time. One simple example is Argon2 password hashing algorithm, which requires us to hash its state using BLAKE2b with dynamically calculated digest size. We can't just use the largest digest size and truncate the result, because most hash functions (unlike XOFs) produce completely different digests depending on the requested output size. Using a template parameter for the digest size would encumber implementation of such algorithms. Additionally, some hash functions support arbitrary output sizes which are not limited by the security level (examples of such functions are Skein, BLAKE2x, ParallelHash). Some functions are specifically designed to be usable both in hashing and in XOF modes, where the required output size is not known in advance even at runtime. Taking all this factors in consideration, specifying the output size at compile time does not seem like a good design.

Q: Why hasher does not support hashing non-byte types?

A: Cryptographic hash functions are always defined for a sequence of bytes. We support only those data types that can be unambiguosly converted to bytes (sequences of char, signed char, or unsigned char). Other data types should be converted to a sequence of bytes in non-ambiguous way before they can be hashed (eg wide strings could be encoded using UTF-8 or another encoding), which is beyond the scope of the library.

Q: Since the output size has to be provided to the constructor, why there are separate typedefs for sha256 and sha512 instead of one hasher with output size parameter: sha2(256) / sha2(512)?

A: SHA-2 family of hash functions is special because SHA-512 can produce output of any size up to 512 bits (SHA-512/t), e.g. sha512(256) will calculate SHA-512/256. The resulting hash is different from SHA-256, but has the same length. Thus SHA-512 is an independent hash function supporting variable output sizes. On the other hand, the 32-bit version of SHA-2 is only defined for 224-bit and 256-bit outputs, and they are widely known as SHA-224 and SHA-256. We decided to use different typedefs for SHA-224 and SHA-256 because requiring users to use sha256(224) for getting SHA-224 digests would be confusing. Internally all SHA-2 functions are implemented using one template class.

Q: Why there are separate typedefs for skein256, skein512 and skein1024 instead of one hasher with output size parameter: skein(256) / skein(512) / skein(1024)?

A: Skein256, Skein512 and Skein1024 are different algorithms. Each of them can produce digests of any size. The outputs are unrelated, e.g. skein256(256) != skein512(256) != skein1024(256). Internally all Skein variants are implemented using one template class.

Q: Why there are so many typedefs for BLAKE2 hash function?

A: BLAKE2 has many variants that produce incompatible digests for the same output sizes. We support different variants via different typedef. For the 512-bit version, blake2b is the oldest algorithm which can produce digests of any size up to 512 bits. blake2xb can be used to produce larger digests but requires the output size to be known in advance; it can't be merged with blake2b because their output are different for the same digest sizes. blake2xb_xof can be used in XOF mode when the output size is not known in advance. Then there is a 256-bit version blake2s which supports all this variants as well. Internally all BLAKE2 variants are implemented using one template class.

Known limitations