BLAKE2
BLAKE2b and BLAKE2s are cryptographic hash functions by Aumasson, Neves, Wilcox-O'Hearn and Winnerlein. The hashes were added at Crypto++ 5.6.4. Also see BLAKE2: simpler, smaller, fast as MD5.
BLAKE2s
is a message digest using 32-bit words, and BLAKE2b
is a message digest using 64-bit words. The classes also serve as a keyed hash when using constructors that take a key
and keyLength
pair.
Crypto++ 8.2 and earlier failed to use the salt and personalization string when producing a digest. Also see Issue 921.
All Crypto++ hashes derive from HashTransformation
. The base class provides functions like Update
, Final
and Verify
. You can swap-in any hash for any other hash in your program. You can also use ChannelSwitch
to send data to multiple hashes at the same time.
Constructors
BLAKE2s
and BLAKE2b
provide the same constructors. The constructors for BLAKE2b
are shown below, but apply to BLAKE2s
as well. The first and third constructors were added at Crypto++ 5.6.4; and the second constructor was added at Crypto++ 8.2.
// Crypto++ 5.6.4 BLAKE2b (bool treeMode=false, unsigned int digestSize=DIGESTSIZE)
// Crypto++ 8.2 BLAKE2b (unsigned int digestSize)
// Crypto++ 5.6.4 BLAKE2b (const byte *key, size_t keyLength, const byte *salt=NULL, size_t saltLength=0, const byte *personalization=NULL, size_t personalizationLength=0, bool treeMode=false, unsigned int digestSize=DIGESTSIZE)
treeMode
- flag indicating tree mode. Default is false
.
digestSize
- digest size, in bytes. Default is DIGESTSIZE
.
key
- pointer to the key. Default is unkeyed.
keyLength
- key size, in bytes. Default is unkeyed.
salt
- pointer to the salt. Default is unused.
saltLength
- salt size, in bytes. Default is unused.
personalization
- pointer to the personalization string. Default is unused.
personalizationLength
- personalization string size, in bytes. Default is unused.
BLAKE2s
and BLAKE2b
each have a parameter block that is hashed in the course of calculating a message digest. The parameter block includes all of the parameters named above. Though not readily apparent, all of the constructors set all of the parameters when using BLAKE2 classes.
BlockSize
BLAKE2 classes prior to Crypto++ 8.2 were missing the BlockSize
function. BLAKE2s
and BLAKE2b
would function normally until used with an HMAC. When used with a HMAC an InvalidParameter
exception would happen.
BLAKE2 is a keyed hash and does not need to be paired with a HMAC. All you should need to do is use the constructor that takes key
and keyLength
. However, classes like HKDF internally use a HMAC so the the new member functions were added to ensure interop.
Commit 758939ab2e1b added BlockSize
so BLAKE2 classes could be used with an HMAC.
Digest Size
Crypto++ 5.6.4 and above provided two consturctors. The first was BLAKE2b(bool treeMode, unsigned int digestSize)
and the second was the constructor that took all the parameters BLAKE2b(const byte *key, size_t keyLength, ... )
.
The two constructors made it inconvenient to set the digest size alone because you also had to set tree mode. Crypto++ 8.2 added a constructor that accepts just the digest size:
BLAKE2b (unsigned int digestSize)
The new constructor allows you to write code like the following for BLAKE2b-384:
byte digest[48]; BLAKE2b(48).CalculateTruncatedDigest(digest, 48, password, passwordLength);
If you are using the original constructors then you need to write:
byte digest[48]; BLAKE2b(false, 48).CalculateTruncatedDigest(digest, 48, password, passwordLength);
Also see Commit 31839703005e.
Sample Programs
There are five sample programs. The first prints information about the hash. The second creates a hash using BLAKE2b class. The third creates a hash using a pipeline. The fourth and fifth examples show how to verify an existing digest.
The examples below use BLAKE2b, but you can swap-in any hash function, like PanamaHash, SM3 or BLAKE2s.
The first example dumps the name, digest size and internal block size of the hash.
#include "cryptlib.h" #include "blake2.h" #include <iostream> int main (int argc, char* argv[]) { using namespace CryptoPP; BLAKE2b hash; std::cout << "Name: " << hash.AlgorithmName() << std::endl; std::cout << "Digest size: " << hash.DigestSize() << std::endl; std::cout << "Block size: " << hash.BlockSize() << std::endl; return 0; }
Running the program results in the following. In general you should use DigestSize
and avoid BlockSize
. BlockSize
is usually not required by a program. In the case of SHAKE the block size is the rate r
.
$ ./test.exe Name: BLAKE2b-512 Digest size: 64 Block size: 128
The second example creates a hash using the hash object and member functions. You add data using Update
and you calculate the hash using Final
. Calling Final
resets the hash so you don't need to do it manually.
using namespace CryptoPP; HexEncoder encoder(new FileSink(std::cout)); std::string msg = "Yoda said, Do or do not. There is not try."; std::string digest; BLAKE2b hash; hash.Update((const byte*)msg.data(), msg.size()); digest.resize(hash.DigestSize()); hash.Final((byte*)&digest[0]); std::cout << "Message: " << msg << std::endl; std::cout << "Digest: "; StringSource(digest, true, new Redirector(encoder)); std::cout << std::endl;
Running the program results in the following.
$ ./test.exe Message: Yoda said, Do or do not. There is not try. Digest: 7A693CE57F747AB434B67CC99D36FA3EE11FE69DBB2C8F6BDA52086AFF0FBE5CA4DE1D68E1D90DEE1402A840A82663564FCB2945FB115A8AE3B379535669192D
You can also obtain a truncated hash rather than the full hash using TruncatedFinal
.
std::cout << "Message: " << msg << std::endl; hash.Update((const byte*)msg.data(), msg.size()); digest.resize(hash.DigestSize()/2); hash.TruncatedFinal((byte*)&digest[0], digest.size()); std::cout << "Digest: "; StringSource(digest, true, new Redirector(encoder)); std::cout << std::endl;
The program produces the following result.
$ ./test.exe Message: Yoda said, Do or do not. There is not try. Digest: 7A693CE57F747AB434B67CC99D36FA3EE11FE69DBB2C8F6BDA52086AFF0FBE5C
Using a pipeline produces the same result. It relieves you of calling Update
and Final manually. The code also uses a HashFilter
, which has its own wiki page at HashFilter.
std::string msg = "Yoda said, Do or do not. There is not try."; std::string digest; StringSource(msg, true, new HashFilter(hash, new StringSink(digest))); std::cout << "Message: " << msg << std::endl; std::cout << "Digest: "; StringSource(digest, true, new Redirector(encoder)); std::cout << std::endl;
Running the program results in the following.
$ ./test.exe Message: Yoda said, Do or do not. There is not try. Digest: 7A693CE57F747AB434B67CC99D36FA3EE11FE69DBB2C8F6BDA52086AFF0FBE5CA4DE1D68E1D90DEE1402A840A82663564FCB2945FB115A8AE3B379535669192D
The fourth program verifies an existing hash using the hash object. Notice the program proceeds as if the hash is going to be calculated. But rather than calling Final
to retrieve the hash, Verify
is called to verify the existing hash.
BLAKE2b hash; hash.Update((const byte*)msg.data(), msg.size()); bool verified = hash.Verify((const byte*)digest.data()); if (verified == true) std::cout << "Verified hash over message" << std::endl; else std::cout << "Failed to verify hash over message" << std::endl;
The final program verifies an existing hash using a pipeline. The code uses a HashVerificationFilter
, which has its own wiki page at HashVerificationFilter.
bool result; StringSource(digest+msg, true, new HashVerificationFilter(hash, new ArraySink((byte*)&result, sizeof(result)))); if (result == true) std::cout << "Verified hash over message" << std::endl; else std::cout << "Failed to verify hash over message" << std::endl;
Running the program results in the following output.
$ ./test.exe Message: Yoda said, Do or do not. There is not try. Digest: 7A693CE57F747AB434B67CC99D36FA3EE11FE69DBB2C8F6BDA52086AFF0FBE5CA4DE1D68E1D90DEE1402A840A82663564FCB2945FB115A8AE3B379535669192D Verified hash over message
Downloads
No downloads available.