File Coverage

blib/lib/Net/DNS/SEC/ECDSA.pm
Criterion Covered Total %
statement 52 52 100.0
branch 10 10 100.0
condition n/a
subroutine 11 11 100.0
pod 2 2 100.0
total 75 75 100.0


line stmt bran cond sub pod time code
1             package Net::DNS::SEC::ECDSA;
2              
3 12     12   9414 use strict;
  12         62  
  12         359  
4 12     12   115 use warnings;
  12         40  
  12         869  
5              
6             our $VERSION = (qw$Id: ECDSA.pm 1937 2023-09-11 09:27:16Z willem $)[2];
7              
8              
9             =head1 NAME
10              
11             Net::DNS::SEC::ECDSA - DNSSEC ECDSA digital signature algorithm
12              
13              
14             =head1 SYNOPSIS
15              
16             require Net::DNS::SEC::ECDSA;
17              
18             $signature = Net::DNS::SEC::ECDSA->sign( $sigdata, $private );
19              
20             $validated = Net::DNS::SEC::ECDSA->verify( $sigdata, $keyrr, $sigbin );
21              
22              
23             =head1 DESCRIPTION
24              
25             Implementation of ECDSA elliptic curve digital signature
26             generation and verification procedures.
27              
28             =head2 sign
29              
30             $signature = Net::DNS::SEC::ECDSA->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::ECDSA->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 12     12   82 use integer;
  12         34  
  12         79  
45 12     12   332 use MIME::Base64;
  12         40  
  12         994  
46              
47 12     12   98 use constant ECDSA_configured => Net::DNS::SEC::libcrypto->can('EVP_PKEY_new_ECDSA');
  12         35  
  12         1011  
48              
49 12     12   8052 BEGIN { die 'ECDSA disabled or application has no "use Net::DNS::SEC"' unless ECDSA_configured }
50              
51              
52             my %parameters = (
53             13 => ['P-256', 32, Net::DNS::SEC::libcrypto::EVP_sha256()],
54             14 => ['P-384', 48, Net::DNS::SEC::libcrypto::EVP_sha384()],
55             );
56              
57 12     12   167 sub _index { return keys %parameters }
58              
59              
60             sub sign {
61 3     3 1 20113 my ( $class, $sigdata, $private ) = @_;
62              
63 3         11 my $algorithm = $private->algorithm;
64 3 100       6 my ( $curve, $keylen, $evpmd ) = @{$parameters{$algorithm} || []};
  3         24  
65 3 100       20 die 'private key not ECDSA' unless $curve;
66              
67 2         20 my $rawkey = pack "a$keylen", decode_base64( $private->PrivateKey );
68 2         281 my $evpkey = Net::DNS::SEC::libcrypto::EVP_PKEY_new_ECDSA( $curve, $rawkey, '' );
69              
70 2         1951 my $asn1 = Net::DNS::SEC::libcrypto::EVP_sign( $sigdata, $evpkey, $evpmd );
71 2         13 return _ASN1decode( $asn1, $keylen );
72             }
73              
74              
75             sub verify {
76 6     6 1 2396 my ( $class, $sigdata, $keyrr, $sigbin ) = @_;
77              
78 6         53 my $algorithm = $keyrr->algorithm;
79 6 100       51 my ( $curve, $keylen, $evpmd ) = @{$parameters{$algorithm} || []};
  6         30  
80 6 100       24 die 'public key not ECDSA' unless $curve;
81              
82 5 100       13 return unless $sigbin;
83              
84 4         15 my ( $x, $y ) = unpack "a$keylen a$keylen", $keyrr->keybin;
85 4         2569 my $evpkey = Net::DNS::SEC::libcrypto::EVP_PKEY_new_ECDSA( $curve, $x, $y );
86              
87 4         12 my $asn1 = _ASN1encode( $sigbin, $keylen );
88 4         3473 return Net::DNS::SEC::libcrypto::EVP_verify( $sigdata, $asn1, $evpkey, $evpmd );
89             }
90              
91              
92             ########################################
93              
94             sub _ASN1encode {
95 4     4   11 my ( $sig, $size ) = @_;
96 4         15 my @part = unpack "a$size a$size", $sig;
97 4         8 my $length;
98 4         7 foreach (@part) {
99 8         17 s/^[\000]+//;
100 8         17 s/^$/\000/;
101 8         24 s/^(?=[\200-\377])/\000/;
102 8         28 $_ = pack 'C2 a*', 2, length, $_;
103 8         18 $length += length;
104             }
105 4         14 return pack 'C2 a* a*', 0x30, $length, @part;
106             }
107              
108             sub _ASN1decode {
109 2     2   6 my ( $asn1, $size ) = @_;
110 2         13 my $n = unpack 'x3 C', $asn1;
111 2         10 my $m = unpack "x5 x$n C", $asn1;
112 2         12 my @part = unpack "x4 a$n x2 a$m", $asn1;
113 2         5 return pack 'a* a*', map { substr( pack( "x$size a*", $_ ), -$size ) } @part;
  4         39  
114             }
115              
116              
117             1;
118              
119             __END__