SignatureVerificationFilter

From Crypto++ Wiki
Jump to navigation Jump to search
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