| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Net::DNS::SEC::ECDSA; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 12 |  |  | 12 |  | 9220 | use strict; | 
|  | 12 |  |  |  |  | 32 |  | 
|  | 12 |  |  |  |  | 358 |  | 
| 4 | 12 |  |  | 12 |  | 65 | use warnings; | 
|  | 12 |  |  |  |  | 25 |  | 
|  | 12 |  |  |  |  | 758 |  | 
| 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 |  | 85 | use integer; | 
|  | 12 |  |  |  |  | 30 |  | 
|  | 12 |  |  |  |  | 70 |  | 
| 45 | 12 |  |  | 12 |  | 324 | use MIME::Base64; | 
|  | 12 |  |  |  |  | 43 |  | 
|  | 12 |  |  |  |  | 886 |  | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 12 |  |  | 12 |  | 83 | use constant ECDSA_configured => Net::DNS::SEC::libcrypto->can('EVP_PKEY_new_ECDSA'); | 
|  | 12 |  |  |  |  | 28 |  | 
|  | 12 |  |  |  |  | 891 |  | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 12 |  |  | 12 |  | 7891 | 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 |  | 184 | sub _index { return keys %parameters } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | sub sign { | 
| 61 | 3 |  |  | 3 | 1 | 20073 | my ( $class, $sigdata, $private ) = @_; | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 3 |  |  |  |  | 12 | my $algorithm = $private->algorithm; | 
| 64 | 3 | 100 |  |  |  | 7 | my ( $curve, $keylen, $evpmd ) = @{$parameters{$algorithm} || []}; | 
|  | 3 |  |  |  |  | 27 |  | 
| 65 | 3 | 100 |  |  |  | 22 | die 'private key not ECDSA' unless $curve; | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 2 |  |  |  |  | 3786 | my $rawkey = pack "a$keylen", decode_base64( $private->PrivateKey ); | 
| 68 | 2 |  |  |  |  | 289 | my $evpkey = Net::DNS::SEC::libcrypto::EVP_PKEY_new_ECDSA( $curve, $rawkey, '' ); | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 2 |  |  |  |  | 1867 | my $asn1 = Net::DNS::SEC::libcrypto::EVP_sign( $sigdata, $evpkey, $evpmd ); | 
| 71 | 2 |  |  |  |  | 12 | return _ASN1decode( $asn1, $keylen ); | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | sub verify { | 
| 76 | 6 |  |  | 6 | 1 | 2329 | my ( $class, $sigdata, $keyrr, $sigbin ) = @_; | 
| 77 |  |  |  |  |  |  |  | 
| 78 | 6 |  |  |  |  | 20 | my $algorithm = $keyrr->algorithm; | 
| 79 | 6 | 100 |  |  |  | 44 | my ( $curve, $keylen, $evpmd ) = @{$parameters{$algorithm} || []}; | 
|  | 6 |  |  |  |  | 29 |  | 
| 80 | 6 | 100 |  |  |  | 34 | die 'public key not ECDSA' unless $curve; | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 5 | 100 |  |  |  | 15 | return unless $sigbin; | 
| 83 |  |  |  |  |  |  |  | 
| 84 | 4 |  |  |  |  | 14 | my ( $x, $y ) = unpack "a$keylen a$keylen", $keyrr->keybin; | 
| 85 | 4 |  |  |  |  | 2583 | my $evpkey = Net::DNS::SEC::libcrypto::EVP_PKEY_new_ECDSA( $curve, $x, $y ); | 
| 86 |  |  |  |  |  |  |  | 
| 87 | 4 |  |  |  |  | 13 | my $asn1 = _ASN1encode( $sigbin, $keylen ); | 
| 88 | 4 |  |  |  |  | 3445 | 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 |  |  |  |  | 14 | my @part = unpack "a$size a$size", $sig; | 
| 97 | 4 |  |  |  |  | 7 | my $length; | 
| 98 | 4 |  |  |  |  | 8 | foreach (@part) { | 
| 99 | 8 |  |  |  |  | 18 | s/^[\000]+//; | 
| 100 | 8 |  |  |  |  | 16 | s/^$/\000/; | 
| 101 | 8 |  |  |  |  | 27 | s/^(?=[\200-\377])/\000/; | 
| 102 | 8 |  |  |  |  | 24 | $_ = pack 'C2 a*', 2, length, $_; | 
| 103 | 8 |  |  |  |  | 16 | $length += length; | 
| 104 |  |  |  |  |  |  | } | 
| 105 | 4 |  |  |  |  | 14 | return pack 'C2 a* a*', 0x30, $length, @part; | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | sub _ASN1decode { | 
| 109 | 2 |  |  | 2 |  | 7 | my ( $asn1, $size ) = @_; | 
| 110 | 2 |  |  |  |  | 9 | my $n	 = unpack 'x3 C',	   $asn1; | 
| 111 | 2 |  |  |  |  | 10 | my $m	 = unpack "x5 x$n C",	   $asn1; | 
| 112 | 2 |  |  |  |  | 11 | my @part = unpack "x4 a$n x2 a$m", $asn1; | 
| 113 | 2 |  |  |  |  | 7 | return pack 'a* a*', map { substr( pack( "x$size a*", $_ ), -$size ) } @part; | 
|  | 4 |  |  |  |  | 41 |  | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | 1; | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | __END__ |