File Coverage

blib/lib/Net/DNS/SEC/DSA.pm
Criterion Covered Total %
statement 19 19 100.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 26 100.0


line stmt bran cond sub pod time code
1             package Net::DNS::SEC::DSA;
2              
3 10     10   7481 use strict;
  10         45  
  10         436  
4 10     10   53 use warnings;
  10         21  
  10         998  
5              
6             our $VERSION = (qw$Id: DSA.pm 2042 2025-12-24 10:23:11Z willem $)[2];
7              
8              
9             =head1 NAME
10              
11             Net::DNS::SEC::DSA - DNSSEC DSA digital signature algorithm
12              
13              
14             =head1 SYNOPSIS
15              
16             require Net::DNS::SEC::DSA;
17              
18             $signature = Net::DNS::SEC::DSA->sign( $sigdata, $private );
19              
20             $validated = Net::DNS::SEC::DSA->verify( $sigdata, $keyrr, $sigbin );
21              
22              
23             =head1 DESCRIPTION
24              
25             Implementation of DSA digital signature
26             generation and verification procedures.
27              
28             =head2 sign
29              
30             $signature = Net::DNS::SEC::DSA->sign( $sigdata, $private );
31              
32             Generates the wire-format signature from the sigdata octet string
33             and the appropriate private key object.
34              
35             =head2 verify
36              
37             $validated = Net::DNS::SEC::DSA->verify( $sigdata, $keyrr, $sigbin );
38              
39             Verifies the signature over the sigdata octet string using the specified
40             public key resource record.
41              
42             =cut
43              
44 10     10   68 use integer;
  10         28  
  10         61  
45 10     10   360 use MIME::Base64;
  10         20  
  10         1153  
46              
47 10     10   81 use constant Digest_SHA1 => Net::DNS::SEC::libcrypto->can('EVP_sha1');
  10         29  
  10         1135  
48 10     10   66 use constant DSA_configured => Digest_SHA1 && Net::DNS::SEC::libcrypto->can('EVP_PKEY_new_DSA');
  10         20  
  10         886  
49              
50 10     10   569 BEGIN { die 'DSA disabled or application has no "use Net::DNS::SEC"' unless DSA_configured }
51              
52              
53             my %parameters = (
54             3 => scalar eval { Net::DNS::SEC::libcrypto::EVP_sha1() },
55             6 => scalar eval { Net::DNS::SEC::libcrypto::EVP_sha1() },
56             );
57              
58             sub _index { return keys %parameters }
59              
60             sub _deprecate { return _index() } ## deprecate for DNSSEC sign/validate
61              
62              
63             sub sign {
64             my ( $class, $sigdata, $private ) = @_;
65              
66             my $evpmd = $parameters{$private->algorithm};
67             die 'private key not DSA' unless $evpmd;
68              
69             my @key = qw(prime subprime base private_value public_value);
70             my ( $p, $q, $g, $x, $y ) = map { decode_base64( $private->$_ ) } @key;
71             my $t = ( length($g) - 64 ) / 8;
72              
73             my $evpkey = Net::DNS::SEC::libcrypto::EVP_PKEY_new_DSA( $p, $q, $g, $y, $x );
74              
75             my $asn1 = Net::DNS::SEC::libcrypto::EVP_sign( $sigdata, $evpkey, $evpmd );
76             return _ASN1decode( $asn1, $t );
77             }
78              
79              
80             sub verify {
81             my ( $class, $sigdata, $keyrr, $sigbin ) = @_;
82              
83             my $evpmd = $parameters{$keyrr->algorithm};
84             die 'public key not DSA' unless $evpmd;
85              
86             return unless $sigbin;
87              
88             my $key = $keyrr->keybin; # public key
89             my $len = 64 + 8 * unpack( 'C', $key ); # RFC2536, section 2
90             my ( $q, $p, $g, $y ) = unpack "x a20 a$len a$len a$len", $key;
91              
92             my $evpkey = Net::DNS::SEC::libcrypto::EVP_PKEY_new_DSA( $p, $q, $g, $y );
93              
94             my $asn1 = _ASN1encode($sigbin);
95             return Net::DNS::SEC::libcrypto::EVP_verify( $sigdata, $asn1, $evpkey, $evpmd );
96             }
97              
98              
99             ########################################
100              
101             sub _ASN1encode {
102             my @part = unpack 'x a20 a20', shift; # discard "t"
103             my $length;
104             foreach (@part) {
105             s/^[\000]+//;
106             s/^$/\000/;
107             s/^(?=[\200-\377])/\000/;
108             $_ = pack 'C2 a*', 2, length, $_;
109             $length += length;
110             }
111             return pack 'C2 a* a*', 0x30, $length, @part;
112             }
113              
114             sub _ASN1decode {
115             my ( $asn1, $t ) = @_;
116             my $n = unpack 'x3 C', $asn1;
117             my $m = unpack "x5 x$n C", $asn1;
118             my @part = unpack "x4 a$n x2 a$m", $asn1;
119             return pack 'C a* a*', $t, map { substr( pack( 'x20 a*', $_ ), -20 ) } @part;
120             }
121              
122              
123             1;
124              
125             __END__