```
package Crypt::DSA::GMP;
use 5.006;
use strict;
use warnings;
BEGIN {
$Crypt::DSA::GMP::AUTHORITY = 'cpan:DANAJ';
$Crypt::DSA::GMP::VERSION = '0.02';
}
use Carp qw( croak );
use Math::BigInt lib => "GMP";
use Digest::SHA qw( sha1 sha256 sha512 );
use Crypt::DSA::GMP::KeyChain;
use Crypt::DSA::GMP::Key;
use Crypt::DSA::GMP::Signature;
use Crypt::DSA::GMP::Util qw( bitsize bin2mp mod_inverse mod_exp makerandomrange );
sub new {
my $class = shift;
my $dsa = bless { @_ }, $class;
$dsa->{_keychain} = Crypt::DSA::GMP::KeyChain->new(@_);
$dsa;
}
sub keygen {
my ($dsa, %params) = @_;
my $key = $dsa->{_keychain}->generate_params(%params);
my $nonblock = $params{NonBlockingKeyGeneration};
$dsa->{_keychain}->generate_keys($key, $nonblock);
croak "Invalid key" unless $key->validate();
$key;
}
sub keyset {
my ($dsa, %param) = @_;
my $key = Crypt::DSA::GMP::Key->new;
croak "Key missing p" unless defined $param{p}; $key->p($param{p});
croak "Key missing q" unless defined $param{q}; $key->q($param{q});
croak "Key missing g" unless defined $param{g}; $key->g($param{g});
$key->priv_key($param{priv_key}) if defined $param{priv_key};
$key->priv_key($param{x} ) if defined $param{x};
$key->pub_key($param{pub_key}) if defined $param{pub_key};
$key->pub_key($param{y} ) if defined $param{y};
$key->pub_key(mod_exp($key->g, $key->priv_key, $key->p))
if !defined $key->pub_key && defined $key->priv_key;
croak "Key missing both private and public keys"
unless defined $key->pub_key || defined $key->priv_key;
croak "Invalid key" unless $key->validate();
$key;
}
sub sign {
my ($dsa, %param) = @_;
my ($key, $dgst) = ($param{Key}, $param{Digest});
croak __PACKAGE__, "->sign: Need a Key" unless defined $key && ref($key);
croak __PACKAGE__, "->sign: Invalid key" unless $key->validate();
my ($p, $q, $g) = ($key->p, $key->q, $key->g);
my $N = bitsize($q);
if (!defined $dgst) {
my $message = $param{Message};
croak __PACKAGE__, "->sign: Need either Message or Digest"
unless defined $message;
# Determine which standard we're following.
$param{Standard} = $dsa->{Standard}
if defined $dsa->{Standard} && !defined $param{Standard};
if (defined $param{Standard} && $param{Standard} =~ /186-[34]/) {
# See NIST SP 800-57 revision 3, section 5.6.1
$dgst = ($N > 256) ? sha512($message) : sha256($message);
} else {
$dgst = sha1($message);
}
}
# FIPS 186-4, section 4.6 "DSA Signature Generation"
# compute z as the leftmost MIN(N, outlen) bits of the digest
my $z = bin2mp($dgst);
$z->brsft(8*length($dgst) - $N) if $N < 8*length($dgst);
# Generate r and s, ensuring neither are zero.
my ($r, $s);
do {
my ($k, $kinv);
do {
# Using FIPS 186-4 B.2.2 approved method
# k is per-message random number 0 < k < q
$k = makerandomrange( Max => $q-2 ) + 1;
$r = mod_exp($g, $k, $p)->bmod($q);
} while $r == 0;
$kinv = mod_inverse($k, $q);
$s = ($kinv * ($z + $key->priv_key * $r)) % $q;
} while $s == 0;
croak "Internal error in signing" if $r == 0 || $s == 0;
my $sig = Crypt::DSA::GMP::Signature->new;
$sig->r($r);
$sig->s($s);
$sig;
}
sub verify {
my ($dsa, %param) = @_;
my ($key, $dgst, $sig) = ($param{Key}, $param{Digest}, $param{Signature});
croak __PACKAGE__, "->verify: Need a Key"
unless defined $key && ref($key);
croak __PACKAGE__, "->verify: Need a Signature"
unless defined $sig && ref($sig);
croak __PACKAGE__, "->verify: Invalid key" unless $key->validate();
my ($p, $q, $g, $r, $s) = ($key->p, $key->q, $key->g, $sig->r, $sig->s);
return 0 unless $r > 0 && $r < $q && $s > 0 && $s < $q;
my $N = bitsize($q);
if (!defined $dgst) {
my $message = $param{Message};
croak __PACKAGE__, "->verify: Need either Message or Digest"
unless defined $message;
# Determine which standard we're following.
$param{Standard} = $dsa->{Standard}
if defined $dsa->{Standard} && !defined $param{Standard};
if (defined $param{Standard} && $param{Standard} =~ /186-[34]/) {
# See NIST SP 800-57 revision 3, section 5.6.1
$dgst = ($N > 256) ? sha512($message) : sha256($message);
} else {
$dgst = sha1($message);
}
}
my $w = mod_inverse($s, $q);
my $z = bin2mp($dgst);
$z->brsft(8*length($dgst) - $N) if $N < 8*length($dgst);
my $u1 = $w->copy->bmul($z)->bmod($q);
my $u2 = $w->copy->bmul($r)->bmod($q);
my $v = mod_exp($g, $u1, $p)
->bmul(mod_exp($key->pub_key, $u2, $p))
->bmod($p)
->bmod($q);
$v == $r;
}
1;
__END__
=pod
=head1 NAME
Crypt::DSA::GMP - DSA Signatures and Key Generation
=head1 SYNOPSIS
use Crypt::DSA::GMP;
my $dsa = Crypt::DSA::GMP->new;
my $key = $dsa->keygen(
Size => 512,
Seed => $seed,
Verbosity => 1
);
my $sig = $dsa->sign(
Message => "foo bar",
Key => $key
);
my $verified = $dsa->verify(
Message => "foo bar",
Signature => $sig,
Key => $key,
);
=head1 DESCRIPTION
L<Crypt::DSA::GMP> is an implementation of the DSA (Digital Signature
Algorithm) signature verification system. The implementation
itself is pure Perl, with mathematics support from
L<Math::BigInt::GMP> and L<Math::Prime::Util::GMP>.
This package provides DSA signing, signature verification, and key
generation.
This module is backwards compatible with L<Crypt::DSA>. It removes
a number of dependencies that were portability concerns.
Importantly, it follows FIPS 186-4 wherever possible, and has
support for the new hash methods.
See L</RECOMMENDED KEY GENERATION PARAMETERS> for recommendations
of key generation parameters.
=head1 USAGE
The public interface is a superset of L<Crypt::DSA>, and is
intentionally very similar to L<Crypt::RSA>.
=head2 new
my $dsa_2 = Crypt::DSA::GMP->new;
my $dsa_4 = Crypt::DSA::GMP->new( Standard => "FIPS 186-4" );
Constructs and returns a new L<Crypt::DSA::GMP> object. This
is the object used to perform other useful actions.
The standard to follow may be given in this call, where it
will be used in all methods unless overridden. Currently
only two standards exist:
FIPS 186-2 (includes FIPS 186-1)
FIPS 186-4 (includes FIPS 186-3)
FIPS 186-2 is used as the default to preserve backwards
compatibility. The primary differences:
- FIPS 186-2:
- Up to 80 bits of security (less with default SHA-1).
- NIST deprecated in 2009.
- Completely backward compatible with Crypt::DSA.
(barring differences caused by Crypt::DSA calling openssl)
- Key generation:
- SHA-1 is used for the CSPRNG.
- QSize (the size of q) must be 160 bits.
- Signing and verification:
- SHA-1 is used to hash Message:
less than 80 bits of security regardless of key sizes.
- No difference if Digest is given directly.
- FIPS 186-4:
- Up to 256 bits of security.
- Key generation:
- SHA-2 256/384/512 is used for the CSPRNG.
- QSize (the size of q) may be any integer from 1 to 512.
- The default QSize is 160 when Size < 2048.
- The default QSize is 256 when Size >= 2048.
- Signing and verification:
- SHA2-256 or SHA2-512 is used to hash Message.
- No difference if Digest is given directly.
=head2 keygen
$key = $dsa->keygen(%arg);
Generates a new of DSA key, including both the public and
private portions of the key.
I<%arg> can contain:
=over 4
=item * Standard
If not provided or contains C<186-1> or C<186-2> then the
backward compatible implementation is used, using SHA-1. If it
is provided and contains C<186-3> or C<186-4> then the newer
and recommended FIPS 186-4 standard is used.
For key generation this means different default and allowed
sizes for I<q>, the use of SHA-256 or SHA-512 during random
prime generation, and the FIPS 186-4 updated prime generation
method.
The FIPS 186-4 recommended primality tests are always used as
they are more stringent than FIPS 186-2.
=item * Size
The size in bits of the I<p> value to generate.
This argument is mandatory, and must be at least 256.
=item * QSize
The size in bits of the I<q> value to generate. This is optional.
If FIPS 186-2 is being used or I<Size> is less than 2048, then
the default value will be 160. If FIPS 186-4 is being used and
I<Size> is 2048 or larger, then the default value is 256.
NIST SP 800-57 describes the cryptographic strengths of different
I<Size> and I<QSize> selections. Their table 2 includes:
Bits L N
----- ----- -----
80 1024 160
112 2048 224 Bits = Bits of security
128 3072 256 L = Size = bit length of p
192 7680 384 N = QSize = bit length of q
256 15360 512
In addition, if SHA-1 is used (the default without FIPS 186-4)
then the bits of security provided is strictly less than 80 bits.
=item * Seed
A seed with which I<q> generation will begin. If this seed does
not lead to a suitable prime, it will be discarded, and a new
random seed chosen in its place, until a suitable prime can be
found.
A seed that is shorter than the size of I<q> will be
immediately discarded.
This is entirely optional, and if not provided a random seed will
be generated automatically.
=item * Verbosity
Should be either 0 or 1. A value of 1 will give you a progress
meter during I<p> and I<q> generation--this can be useful, since
the process can be relatively long.
The default is 0.
=item * Prove
Should be 0, 1, I<P>, or I<Q>. If defined and true, then both
the primes for I<p> and I<q> will have a primality proof
constructed and verified. Setting to I<P> or I<Q> will result
in just that prime being proven. The time for proving I<q>
should be minimal, but proving I<p> when Size is larger than
1024 can be B<very> time consuming.
The default is 0, which means the standard FIPS 186-4 probable
prime tests are done.
=back
=head3 RECOMMENDED KEY GENERATION PARAMETERS
These are recommended parameters for the L</keygen> method.
For strict interoperability with all other DSA software, use:
Size => 1024
For better security and interoperability with anything but the
most pedantic software (FIPS 186-2 had a maximum size of 1024;
FIPS 186-4 strict compliance doesn't support this I<(L,N)> pair):
Size => 2048, QSize => 160, Prove => "Q", Standard => "186-4"
For better security and good interoperability with modern code
(including OpenSSL):
Size => 3072, QSize => 256, Prove => "Q", Standard => "186-4"
Note that signatures should a strong hash (either use the
C<Standard =E<gt> "FIPS 186-4"> option when signing, or hash
the message yourself with something like I<sha256>). Without
this, the FIPS 186-2 default of SHA-1 will be used, and
security strength will be less than 80 bits regardless of the
sizes of I<p> and I<q>.
Using Size larger than 3072 and QSize larger than 256 is possible
and most software will support this. NIST SP 800-57 indicates
the two pairs I<(7680,384)> and I<(15360,512)> as examples of
higher cryptographic strength options with 192 and 256 bits of
security respectively. With either pair, an appropriately strong
hash should be used, e.g. I<sha512>, I<sha3_512>, I<skein_512>,
or I<whirlpool>. The main bottleneck is the time required to
generate the keys, which could be several minutes.
=head2 keyset
my $key = $dsa->keyset(%arg);
Creates a key with given elements, typically read from another
source or via another module. I<p>, I<q>, and I<g> are all
required. One or both of I<priv_key> and I<pub_key> are
required. I<pub_key> will be constructed if it is not supplied
but I<priv_key> is not.
=head2 sign
my $sig = $dsa->sign(Key => $key, Message => $msg);
my $sig = $dsa->sign(Key => $key, Digest => $hash_of_msg);
my $sig = $dsa->sign(%arg);
Signs a message (or the digest of a message) using the private
portion of the DSA key and returns the signature.
The return value (the signature) is a
L<Crypt::DSA::GMP::Signature> object.
I<%arg> can include:
=over 4
=item * Standard
If not provided or contains C<186-1> or C<186-2> then the
backward compatible implementation is used, using SHA-1. If it
is provided and contains C<186-3> or C<186-4> then the newer
and recommended FIPS 186-4 standard is used.
For message signing this means FIPS 186-2 uses SHA-1 for digest
construction and at most 160 bits of the digest is used. With
FIPS 186-4, SHA-256 is used if the bit length of I<q> is 256 or
less and SHA-512 is used otherwise. If the input is a Digest
rather than a Message, then there will be no difference.
=item * Digest
A digest to be signed. If the digest length is larger than
I<N>, the bit length of I<q>, then only the leftmost I<N> bits
will be used (as specified in FIPS 186-4).
You must provide either this argument or I<Message> (see below).
=item * Key
The L<Crypt::DSA::GMP::Key> object with which the signature will be
generated. Should contain a private key attribute (I<priv_key>).
This argument is required.
=item * Message
A plaintext message to be signed. If you provide this argument,
I<sign> will first produce a digest of the plaintext, then
use that as the digest to sign. Thus writing
my $sign = $dsa->sign(Message => $message, ... );
is a shorter way of writing
# FIPS 186-2:
use Digest::SHA qw( sha1 );
my $sig = $dsa->sign(Digest => sha1( $message ), ... );
# FIPS 186-4 with QSize <= 256:
use Digest::SHA qw( sha256 );
my $sig = $dsa->sign(Digest => sha256( $message ), ... );
=back
=head2 verify
my $v = $dsa->verify(Key=>$key, Signature=>$sig, Message=>$msg);
my $v = $dsa->verify(Key=>$key, Signature=>$sig, Digest=>$hash);
my $v = $dsa->verify(%arg);
Verifies a signature generated with L</sign>. Returns a true
value on success and false on failure.
I<%arg> can contain:
=over 4
=item * Standard
If not provided or contains C<186-1> or C<186-2> then the
backward compatible implementation is used, using SHA-1. If it
is provided and contains C<186-3> or C<186-4> then the newer
and recommended FIPS 186-4 standard is used.
For message verification this means FIPS 186-2 uses SHA-1
for digest construction and at most 160 bits of the digest is
used. With FIPS 186-4, SHA-256 is used if the bit length
of I<q> is 256 or less and SHA-512 is used otherwise. If
the input is a Digest rather than a Message, then there will
be no difference.
=item * Key
Key of the signer of the message; a L<Crypt::DSA::GMP::Key> object.
The public portion of the key is used to verify the signature.
This argument is required.
=item * Signature
The signature itself. Should be in the same format as returned
from L</sign>, a L<Crypt::DSA::GMP::Signature> object.
This argument is required.
=item * Digest
The original signed digest. This must be computed using the
same hash that was used to sign the message.
Either this argument or I<Message> (see below) must be present.
=item * Message
As above in I<sign>, the plaintext message that was signed, a
string of arbitrary length. A digest of this message will
be created and used in the verification process.
=back
=head1 SUPPORT
Bugs should be reported via the CPAN bug tracker at
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Crypt-DSA-GMP>
For other issues, contact the author.
=head1 AUTHORS
Dana Jacobsen E<lt>dana@acm.orgE<gt> wrote the new internals.
Benjamin Trott E<lt>ben@sixapart.comE<gt> wrote L<Crypt::DSA>
which was the basis for this module. The PEM module remains
almost entirely his code.
=head1 COPYRIGHT
Copyright 2013 by Dana Jacobsen E<lt>dana@acm.orgE<gt>.
Portions Copyright 2006-2011 by Benjamin Trott.
This program is free software; you can redistribute it
and/or modify it under the same terms as Perl itself.
=cut
```