Digital Signature Algorithm

From Crypto++ Wiki
(Redirected from DSA)
Jump to: navigation, search

DSA is the Digital Signature Algorithm. DSA is one of three signature schemes specified in FIPS 186. If interested in the elliptic curve variant, see Elliptic Curve Digital Signature Algorithm.

FIPS 186-2 specifies the use of a 1024 bit p, a 160 bit q, and SHA-1 as the hash. FIPS 186-3 uses larger hashes (SHA-2), larger values for p (up to 3072 bits), and larger values for q (up to 256 bits). FIPS 186-3 went into effect in June, 2009.

To allow a smaller DSA modulus from the earlier FIPS 186 documents, undefine DSA_1024_BIT_MODULUS_ONLY. If using a down level modulus size, keep in mind the modulus must be between DSA::MIN_PRIME_LENGTH and DSA::MAX_PRIME_LENGTH in a multiple of PRIME_LENGTH_MULTIPLE.

Constructor

Both the DSA::PublicKey and DSA::PrivateKey are typedef'd from template classes which accept no parameters. See gfpcrypt.h for details.

Sample Programs

Key Generation

Below is sample code to generate a DSA key. Details of key generation, validation, loading, and saving is available in Keys and Formats.

#include "dsa.h"
#include "osrng.h"

int main(int argc, char* argv[])
{
   AutoSeededRandomPool rng;

   // Generate Private Key
   DSA::PrivateKey PrivateKey;
   PrivateKey.GenerateRandomWithKeySize(rng, 1024);
   
   // Generate Public Key   
   DSA::PublicKey PublicKey;
   PublicKey.AssignFrom(PrivateKey);
   if (!PrivateKey.Validate(rng, 3) || !PublicKey.Validate(rng, 3))
   {
      throw runtime_error("DSA key generation failed");
   }

   return 0;
}

Loading and Saving

The program below extends the key generation example by serializing the DSA keys in PKCS#8 and X.509 format. In the code below, the encoded keys (encodedPublicKey and encodedPrivateKey) exist in memory. The keys could be persisted to disk by using a FileSink rather than a StringSink.

The code below uses a StringSink and string to hold the private key. Though convenient, the practice is not a very good idea - see Keys and Formats for details.

AutoSeededRandomPool rng;

// Generate Public and Private Keys
DSA::PrivateKey privateKey = ...; 
DSA::PublicKey publicKey = ...;
...

// DER Encoded Keys
string encodedPublicKey, encodedPrivateKey;

// Serialize in PKCS#8 and X.509 format
publicKey.Save( StringSink(encodedPublicKey).Ref() );
privateKey.Save( StringSink(encodedPrivateKey).Ref() );

// Decode DSA keys
DSA::PrivateKey decodedPrivateKey;
decodedPrivateKey.Load(
   StringStore(encodedPrivateKey).Ref()
);

DSA::PublicKey decodedPublicKey;
decodedPublicKey.Load(
  StringStore(encodedPublicKey).Ref()
);

Signature Generation and Verification

Building on the previous samples, the next sample signs and verifies a message. Signing is accomplished using a SignerFilter, while verifications are performed with a SignatureVerificationFilter. The SignatureVerificationFilter is constructed using the THROW_EXCEPTION flag, so an exception handler for SignatureVerificationFailed should be in place.

// 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( message, true, 
    new SignerFilter( rng, signer,
        new StringSink( signature )
    ) // SignerFilter
); // StringSource

DSA::Verifier verifier( PublicKey );
StringSource( 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. If the signature is inserted first, SIGNATURE_AT_BEGIN should be specified as an additional flags value.

...

DSA::Verifier verifier( PublicKey );
StringSource( signature+message, true,
    new SignatureVerificationFilter(
        verifier, NULL,
        THROW_EXCEPTION | SIGNATURE_AT_BEGIN
    )
);

The next sample verifies the signature without throwing an exception. This sample uses the PUT_RESULT flag. The SignatureVerificationFilter will place the result into its attached transformation. In the sample below, the result is piped 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 makes not 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. See the StringSink entry for details. Finally, the only flags is PUT_RESULT, so the signature must be presented last (SIGNATURE_AT_BEGIN is not specified).

...

DSA::Verifier verifier( PublicKey );

bool result = false;
StringSource( message+signature, true,
    new SignatureVerificationFilter(
        verifier,
        new ArraySink(
            (byte*)&result, sizeof(result ) ),
        PUT_RESULT | SIGNATURE_AT_END
    )
);

if( true == result ) {
    cout << "Verified signature on message" << endl;
}

The final piece of code 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 /* SIGNATURE_AT_END */
); // SignatureVerificationFilter

StringSource( message+signature, true,
    new Redirector( svf )
); // StringSource

if( true == svf.GetLastResult() ) {
    cout << "Verified signature on message" << endl;
}

Downloads

DSA-Test.zip Crypto++ DSA sample program - 5KB