HashVerificationFilter
Documentation |
#include <cryptopp/filters.h>
|
A HashVerificationFilter is a filter that verifies a digest on a message. The companion class is the HashFilter
.
The HashVerificationFilter
takes a pointer to a BufferedTransformation
. Because a pointer is taken, the HashVerificationFilter
owns the attached transformation, and therefore will destroy it. See ownership for more details.
Sources, filters and sinks are discussed at Pipelining. The pipeline article explains the design and shows you how to use them.
Construction
HashVerificationFilter (HashTransformation &hm, BufferedTransformation *attachment=NULL, word32 flags=DEFAULT_FLAGS)
hm
is a hash, such as Whirlpool or SHA.
attachment
is a BufferedTransformation, such as another filter or sink. If attachment
is NULL
, then the HashVerificationFilter
object will internally accumulate the output byte stream.
flags
is most interesting. The values and their meanings are as follows. Note that HASH_AT_BEGIN
and HASH_AT_END
are mutually exclusive. If you set putMessage=true
with a HashFilter
, then be sure to specify HASH_AT_END
.
DEFAULT_FLAGS
=HASH_AT_BEGIN
|PUT_RESULT
HASH_AT_END
(0)- specifies the hash value will be located at the end of the message
HASH_AT_BEGIN
(1)- specifies the hash value will be located at the beginning of the message
PUT_MESSAGE
(2)- specifies whether the message should be forwarded to the attached transformation
PUT_HASH
(4)- specifies whether the the hash should be forwarded to the attached transformation
PUT_RESULT
(8)- specifies whether the result of the verification (a boolean) should be forwarded to the attached transformation
THROW_EXCEPTION
(16)- specifies whether an exception should be thrown if digest verification fails
With THROW_EXCEPTION
The sample program below demonstrates a HMAC with SHA256 using filters (see pipelining). The HashVerificationFilter
uses the THROW_EXCEPTION
flag, which causes Crypto++ to throw an exception on verification failure.
AutoSeededRandomPool prng; SecByteBlock key(16); // SHA256::BLOCKSIZE will also work prng.GenerateBlock(key, key.size()); string plain = "HMAC Test"; string mac, encoded; /*********************************\ \*********************************/ try { HMAC< SHA256 > hmac(key, key.size()); StringSource ss(plain, true, new HashFilter(hmac, new StringSink(mac) ) // HashFilter ); // StringSource } catch(const CryptoPP::Exception& e) { cerr << e.what() << endl; exit(1); } /*********************************\ \*********************************/ try { HMAC< SHA256 > hmac(key, key.size()); const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END; StringSource ss(plain + mac, true, new HashVerificationFilter(hmac, NULL, flags) ); // StringSource cout << "Verified message" << endl; } catch(const CryptoPP::Exception& e) { cerr << e.what() << endl; exit(1); }
Testing the try/catch block is easily accomplished by tampering with the message or the MAC:
HMAC< SHA256 > hmac(key, key.size()); // Tamper with message plain[0] ^= 0x01; // Tamper with MAC mac[0] ^= 0x01; StringSource ss(plain + mac, true, new HashVerificationFilter(hmac, NULL, flags) ); // StringSource
Without THROW_EXCEPTION
The sample program below demonstrates verification using PUT_RESULT
. When using the flag, the result is forwarded to the attached buffered transformation (attachment
), which is a bool
wrapped using an ArraySink.
AutoSeededRandomPool prng; SecByteBlock key(16); prng.GenerateBlock(key, key.size()); string plain = "HMAC Test"; string mac, encoded; /*********************************\ \*********************************/ HMAC< SHA256 > hmac1(key, key.size()); StringSource ss(plain, true, new HashFilter(hmac1, new StringSink(mac) ) // HashFilter ); // StringSource /*********************************\ \*********************************/ HMAC< SHA256 > hmac2(key, key.size()); bool result = false; StringSource ss(plain + mac, true, new HashVerificationFilter(hmac2, new ArraySink((byte*)&result, sizeof(result)), PUT_RESULT | HASH_AT_END ) // HashVerificationFilter ); // StringSource if(result) cout << "Verified message" << endl; else cerr << "Failed to verify message" << endl;
String and File Sources
The code above uses a couple of small strings, and it allows you to use StringSource ss(plain + mac, true, ...)
. Sometimes you want to use a large file, but loading a large file into memory is not economical. In this case you can use both a StringSource
(for the digest) and a FileSource
(for the file data). The code below shows you how to do it.
#include <iostream> #include <string> #include "cryptlib.h" #include "filters.h" #include "files.h" #include "sha.h" #include "hex.h" int main(int argc, char* argv[]) { using namespace CryptoPP; // Create a file of all 0's with: // dd if=/dev/zero of=./zero.dat bs=4096 count=1 std::string digest; SHA256 sha256; // Create the digest on the file FileSource("zero.dat", true, new HashFilter(sha256, new StringSink(digest))); // Print the digest std::cout << "Digest: "; StringSource(digest, true, new HexEncoder(new FileSink(std::cout))); std::cout << std::endl; // Create a verifier with DEFAULT_FLAGS byte result = 0; HashVerificationFilter verifier(sha256, new ArraySink(&result, sizeof(result))); // Wrap the data in sources StringSource ss(digest, true); FileSource fs("zero.dat", true); // Add the data to the filter. The digest is added first due to HASH_AT_BEGIN ss.TransferTo(verifier); fs.TransferTo(verifier); // Signal end of data. The verifier will finish calculating // the digest, and compare the expected and calculated digests. verifier.MessageEnd(); if (result) std::cout << "Verified hash on file" << std::endl; else std::cout << "Failed to verify hash on file" << std::endl; return 0; }
Downloads
CMAC-AES-Filter.zip - Demonstrates an AES based CMAC and verification with filters
HMAC-SHA-Filter.zip - Demonstrates a SHA256 based HMAC and verification with filters
SHA-File-Filter.zip - Demonstrates a SHA256 based verification of a file with filters