SignatureVerificationFilter
Documentation |
#include <cryptopp/filters.h>
|
SignatureVerificationFilter is used to verify a signature on a message using Crypto++ pipelines. The counterpart to a SignatureVerificationFilter
is a SignerFilter
.
The SignatureVerificationFilter
takes a pointer to a BufferedTransformation
. Because a pointer is taken, the SignatureVerificationFilter
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.
Constructor
SignatureVerificationFilter(const PK_Verifier &verifier, BufferedTransformation *attachment = NULL, word32 flags = DEFAULT_FLAGS)
verifier
is a reference to a PK_Verifier
object, which is usually a public key.
attachment
is a BufferedTransformation, such as another filter or sink. If attachment
is NULL
, then the SignatureVerificationFilter
object will internally accumulate the output byte stream.
flags
specify the verifier's behavior. Note that SIGNATURE_AT_BEGIN
and SIGNATURE_AT_END
are mutually exclusive.
DEFAULT_FLAGS
=SIGNATURE_AT_BEGIN
|PUT_RESULT
.SIGNATURE_AT_END
(0)- specifies that the signature value will be located at the end of message
SIGNATURE_AT_BEGIN
(1)- specifies that the signature value will be located at the beginning of message
PUT_MESSAGE
(2)- specifies whether the message should be forwarded to the attached transformation
PUT_SIGNATURE
(4)- specifies whether the the signature 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 signature verification fails
Sample Programs
In the following examples, it is presumed that PublicKey
and PrivateKey
have been created. For details of key generation, verification, formats, loading, and saving, see Keys and Formats.
Most samples are written in the context of DSA, so please refer to the Digital Signature Algorithm for details of key generation. One sample uses RSA and its message recovery mechanism. Details of RSA can be found at RSA Cryptography.
With THROW_EXCEPTION
The first three samples are based on Crypto++ throwing an exception when verification fails, so an exception handler for SignatureVerificationFailed
should be in place. Since the filter is throwing, the SignatureVerificationFilter
is constructed using the THROW_EXCEPTION
flag.
// Generate or Load the Public and Private Keys DSA::PrivateKey privateKey; DSA::PublicKey publicKey; ... string message = "DSA Signature"; string signature; DSA::Signer signer(privateKey); StringSource ss1(message, true, new SignerFilter(rng, signer, new StringSink(signature) ) // SignerFilter ); // StringSource DSA::Verifier verifier(publicKey); StringSource ss2(message+signature, true, new SignatureVerificationFilter( verifier, NULL, THROW_EXCEPTION /* SIGNATURE_AT_END */ ) ); cout << "Verified signature on message" << endl;
In the example above, the filter receives a concatenation of message+signature
. When SIGNATURE_AT_BEGIN
is not specified in the constructor, SIGNATURE_AT_END
is implied and the signature to be verified must be presented after the message. If the signature is inserted first, SIGNATURE_AT_BEGIN
must be be specified as an additional flags
value as shown below.
... DSA::Verifier verifier(publicKey); StringSource ss(signature+message, true, new SignatureVerificationFilter( verifier, NULL, THROW_EXCEPTION | SIGNATURE_AT_BEGIN ) );
The final example uses RSA and its Signature Scheme with Recovery to exercise PUT_MESSAGE
. In a recovery scheme, the message is interleaved with the signature through use of a Redundancy Function. Because the message is present in the signature, a StringSink
was added to recover the message.
In the example below, note that the SignerFilter
constructor specifies all arguments, including putMessage=true
. See SignerFilter Class Reference.
// Generate or Load keys RSA::PrivateKey privateKey = ...; RSA::PublicKey publicKey = ...; //////////////////////////////////////////////// // Setup string message = "RSA-PSSR Test"; string signature, recovered; //////////////////////////////////////////////// // Sign and Encode RSASS<PSSR, SHA1>::Signer signer(privateKey); StringSource ss1(message, true, new SignerFilter(rng, signer, new StringSink(signature), true // putMessage for recovery ) // SignerFilter ); // StringSource //////////////////////////////////////////////// // Verify and Recover RSASS<PSSR, SHA1>::Verifier verifier(publicKey); StringSource ss2(signature, true, new SignatureVerificationFilter( verifier, new StringSink(recovered), THROW_EXCEPTION | PUT_MESSAGE ) // SignatureVerificationFilter ); // StringSource cout << "Verified signature on message" << endl;
Without THROW_EXCEPTION
The remaining two samples verify the signature without throwing an exception. This first sample uses the PUT_RESULT
flag. Using this flag will instruct the SignatureVerificationFilter
to forward the result of the verification to its attached transformation. In the sample below, the result is pipelined into the bool
value result
. To facilitate the pipeline, the variable is wrapped in an ArraySink.
There are three points to observe below. First, it does not make sense to specify both PUT_RESULT
and THROW_EXCEPTION
as a flag. Second, a StringSink cannot be used since the boolean variable does not derive from std::basic_string
. Finally, because PUT_RESULT
is being used, message recovery (using PUT_MESSAGE
) is not available.
... DSA::Verifier verifier(publicKey); byte result = false; StringSource ss(message+signature, true, new SignatureVerificationFilter( verifier, new ArraySink( &result, sizeof(result)), PUT_RESULT | SIGNATURE_AT_END ) ); if(result) { cout << "Verified signature on message" << endl; }
The final example uses a little known sink called a Redirector. The Redirector does not own its attached BufferedTransformation
, so the attached object is not deleted (as a consequence of the behavior, the Redirector takes a reference and not a pointer). It is useful when an intermediate result is required from an object that would otherwise be participating in pipelining.
... DSA::Verifier verifier(publicKey); SignatureVerificationFilter svf( verifier ); // SignatureVerificationFilter StringSource ss(signature+message, true, new Redirector(svf) ); // StringSource if(true == svf.GetLastResult()) { cout << "Verified signature on message" << endl; }
Verification of a File
The code above uses a couple of small strings, and it allows you to use StringSource ss(plain + signature, 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 signature) 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 "osrng.h" #include "files.h" #include "dsa.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 // Generate private key AutoSeededRandomPool prng; DSA::PrivateKey privateKey; privateKey.GenerateRandomWithKeySize(prng, 2048); // Create public key DSA::PublicKey publicKey; publicKey.AssignFrom(privateKey); // Create the signature on the file std::string signature; DSA::Signer signer(privateKey); FileSource("zero.dat", true, new SignerFilter(prng, signer, new StringSink(signature))); // Print the signature std::cout << "Signature: "; StringSource(signature, true, new HexEncoder(new FileSink(std::cout))); std::cout << std::endl; // Create a verifier using DEFAULT_FLAGS byte result = 0; DSA::Verifier verifier(publicKey); SignatureVerificationFilter filter(verifier, new ArraySink(&result, sizeof(result))); // Wrap the data in sources StringSource ss(signature, true); FileSource fs("zero.dat", true); // Add the data to the filter. Add the signature first due to SIGNATURE_AT_BEGIN ss.TransferTo(filter); fs.TransferTo(filter); // Signal end of data. The filter will verify the signature on the file // and place write to result due to PUT_RESULT filter.MessageEnd(); if (result) std::cout << "Verified signature on file" << std::endl; else std::cout << "Failed to signature hash on file" << std::endl; return 0; }
Downloads
DSA-Test.zip Crypto++ DSA sample program
RSA-SSA-Filter-Test.zip - Demonstrates RSA-SSA (Appendix) using SignatureVerificationFilter
RSA-PSSR-Filter-Test.zip - Demonstrates RSA-PSSR (Recovery) using SignatureVerificationFilter